code editor: Agent Studio improvement iteration 1 for customer-integration-engineer

This commit is contained in:
a2a-code-editor
2026-07-13 12:11:07 +00:00
parent a1a6efba43
commit 8fb2855e4b
3 changed files with 23 additions and 7 deletions

View File

@@ -410,7 +410,7 @@ class CustomerIntegrationEngineer(A2AAgent[CustomerIntegrationEngineerConfig, No
"Guides and validates customer integrations from requirements through a tested handoff while "
"keeping credentials and production changes under customer control."
)
version = "0.1.5"
version = "0.1.6"
config_model = CustomerIntegrationEngineerConfig
auth_model = NoAuth
@@ -980,24 +980,40 @@ def test_idempotency_key_stable():
def _typescript_connector(req: IntegrationRequirements, allowlist: list[str], digest: str) -> str:
hosts = json.dumps(allowlist)
return f'''// Minimal connector scaffold for {req.integration_id}. Plan digest: {digest}
import {{ lookup }} from "node:dns/promises";
import net from "node:net";
const ALLOWED_HOSTS = new Set<string>({hosts});
const MAX_BYTES = {MAX_JSON_BYTES};
export function validateUrl(raw: string): URL {{
function isPublicAddress(address: string): boolean {{
if (net.isIPv4(address)) {{
const [a, b] = address.split(".").map(Number);
return !(a === 0 || a === 10 || a === 127 || a >= 224 || (a === 100 && b >= 64 && b <= 127) || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168));
}}
const lower = address.toLowerCase();
return net.isIPv6(address) === 6 && !(lower === "::" || lower === "::1" || lower.startsWith("fe80:") || lower.startsWith("fc") || lower.startsWith("fd"));
}}
export async function validateUrl(raw: string): Promise<URL> {{
const url = new URL(raw);
if (url.protocol !== "https:") throw new Error("connector only permits https URLs");
if (!ALLOWED_HOSTS.has(url.hostname.toLowerCase())) throw new Error("host is not allowlisted");
const host = url.hostname.toLowerCase();
if (!ALLOWED_HOSTS.has(host)) throw new Error("host is not allowlisted");
const records = await lookup(host, {{ all: true }});
if (records.length === 0 || records.some(record => !isPublicAddress(record.address))) throw new Error("host resolved to unsafe address");
return url;
}}
export async function sendJson(rawUrl: string, payload: unknown, options: {{ dryRun?: boolean, secretRef?: string }} = {{}}) {{
const url = validateUrl(rawUrl);
const url = await validateUrl(rawUrl);
const body = JSON.stringify(payload);
if (new TextEncoder().encode(body).length > MAX_BYTES) throw new Error("payload too large");
const idem = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(body));
const idempotencyKey = Array.from(new Uint8Array(idem)).map(b => b.toString(16).padStart(2, "0")).join("");
if (options.dryRun !== false) return {{ status: "skipped", reason: "dry_run", idempotencyKey, url: url.toString() }};
const response = await fetch(url, {{ method: "POST", body, headers: {{ "Content-Type": "application/json", "Idempotency-Key": idempotencyKey }} }});
const response = await fetch(url, {{ method: "POST", redirect: "manual", body, headers: {{ "Content-Type": "application/json", "Idempotency-Key": idempotencyKey }} }});
if (response.status >= 300 && response.status < 400) throw new Error("redirects are blocked");
return {{ statusCode: response.status, idempotencyKey }};
}}
'''