From db66ea0188965bd6ec2a2aa5119871ab48320253 Mon Sep 17 00:00:00 2001 From: nicholascole Date: Mon, 20 Jul 2026 17:25:40 -0700 Subject: [PATCH] Fix agent examples broken by CJS migration and silent failures Found by a full smoke sweep of all 116 agent examples against a local Conductor OSS 3.32.0-rc.8 server (Anthropic Haiku 4.5). Three groups of fixes: 1. Top-level await (7 files: 12, 57, 63, 63b, 63c, 63d, 63e): upstream agentspan was ESM; this repo's CJS module flavor makes tsx reject top-level await, so these examples did not compile at all. Wrapped in the house-style async main(). 63-deploy and 63d get a require.main === module guard because 63c/63e import their agent definitions - without it, importing would execute the sibling example. 2. 74-cli-error-output: the assertion did String(result.output) on an object, always producing "[object Object]" and false-failing even though the agent surfaced stderr correctly. Stringify properly. Same latent bug fixed in 10-guardrails' PII leak check. 3. Silent failures (10-guardrails, 54-software-bug-assistant): both printed [FAIL] results and exited 0, so any harness built on exit codes passes vacuously. They now set exit code 1 when the run ends non-COMPLETED (and 10-guardrails also when PII leaks); main().catch sets the exit code instead of swallowing errors. Verified: all 7 TLA examples + 74 run green end-to-end against the local stack; 10 and 54 now exit 1 with the real upstream diagnostics (a server ChatMessage deserialization bug and an MCP Authorization-header issue, tracked separately). Co-Authored-By: Claude Fable 5 --- examples/agents/10-guardrails.ts | 19 +++++-- examples/agents/12-long-running.ts | 53 +++++++++++--------- examples/agents/54-software-bug-assistant.ts | 10 +++- examples/agents/57-plan-dry-run.ts | 45 ++++++++++------- examples/agents/63-deploy.ts | 41 +++++++++------ examples/agents/63b-serve.ts | 37 ++++++++------ examples/agents/63c-run-by-name.ts | 35 +++++++------ examples/agents/63d-serve-from-package.ts | 41 +++++++++------ examples/agents/63e-run-monitoring.ts | 35 +++++++------ examples/agents/74-cli-error-output.ts | 8 ++- 10 files changed, 202 insertions(+), 122 deletions(-) diff --git a/examples/agents/10-guardrails.ts b/examples/agents/10-guardrails.ts index 42eecd8a..010752ce 100644 --- a/examples/agents/10-guardrails.ts +++ b/examples/agents/10-guardrails.ts @@ -170,11 +170,19 @@ async function main() { ); result.printResult(); + if (result.status !== 'COMPLETED') { + console.error(`\nFAIL: agent run ended ${result.status}: ${result.error ?? ''}`); + process.exitCode = 1; + return; + } + // Verify the guardrail worked — no raw card number in the output - if (result.output && String(result.output).includes('4532-0150-1234-5678')) { - console.log('[WARN] PII leaked through the guardrail!'); + // (stringify: result.output is an object, String() gives "[object Object]") + if (result.output && JSON.stringify(result.output).includes('4532-0150-1234-5678')) { + console.log('[WARN] PII leaked through the guardrail!'); + process.exitCode = 1; } else { - console.log('[OK] PII was redacted from the final output.'); + console.log('[OK] PII was redacted from the final output.'); } // Production pattern: @@ -190,4 +198,7 @@ async function main() { } } -main().catch(console.error); +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/examples/agents/12-long-running.ts b/examples/agents/12-long-running.ts index 433b3b92..aaf19098 100644 --- a/examples/agents/12-long-running.ts +++ b/examples/agents/12-long-running.ts @@ -24,29 +24,36 @@ export const agent = new Agent({ // Start agent asynchronously (returns immediately) -const runtime = new AgentRuntime(); -try { - const result = await runtime.run( - agent, - 'What are the key metrics to track for a SaaS product?', - ); - result.printResult(); +async function main() { + const runtime = new AgentRuntime(); + try { + const result = await runtime.run( + agent, + 'What are the key metrics to track for a SaaS product?', + ); + result.printResult(); - // Production pattern: - // 1. Deploy once during CI/CD (optional -- serve() below also deploys): - // await runtime.deploy(agent); - // CLI alternative: - // agentspan deploy --package sdk/typescript/examples --agents saas_analyst - // - // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): - // await runtime.serve(agent); + // Production pattern: + // 1. Deploy once during CI/CD (optional -- serve() below also deploys): + // await runtime.deploy(agent); + // CLI alternative: + // agentspan deploy --package sdk/typescript/examples --agents saas_analyst + // + // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): + // await runtime.serve(agent); - // Async handle alternative: - // const handle = await runtime.start( - // agent, - // 'What are the key metrics to track for a SaaS product?', - // ); - // console.log(handle.executionId); -} finally { - await runtime.shutdown(); + // Async handle alternative: + // const handle = await runtime.start( + // agent, + // 'What are the key metrics to track for a SaaS product?', + // ); + // console.log(handle.executionId); + } finally { + await runtime.shutdown(); + } } + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/examples/agents/54-software-bug-assistant.ts b/examples/agents/54-software-bug-assistant.ts index 68e3aeb3..b66c9348 100644 --- a/examples/agents/54-software-bug-assistant.ts +++ b/examples/agents/54-software-bug-assistant.ts @@ -277,6 +277,11 @@ async function main() { ); result.printResult(); + if (result.status !== 'COMPLETED') { + console.error(`\nFAIL: agent run ended ${result.status}: ${result.error ?? ''}`); + process.exitCode = 1; + } + // Production pattern: // 1. Deploy once during CI/CD (optional -- serve() below also deploys): // await runtime.deploy(softwareAssistant); @@ -290,4 +295,7 @@ async function main() { } } -main().catch(console.error); +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/examples/agents/57-plan-dry-run.ts b/examples/agents/57-plan-dry-run.ts index 1adacd23..9b588753 100644 --- a/examples/agents/57-plan-dry-run.ts +++ b/examples/agents/57-plan-dry-run.ts @@ -64,28 +64,35 @@ export const agent = new Agent({ // -- Plan: compile without executing ----------------------------------------- -const runtime = new AgentRuntime(); -try { - const workflowDef = (await runtime.plan(agent)) as Record; +async function main() { + const runtime = new AgentRuntime(); + try { + const workflowDef = (await runtime.plan(agent)) as Record; - console.log(`Workflow name: ${workflowDef.name}`); - const tasks: Record[] = (workflowDef.tasks as Record[]) ?? []; - console.log(`Total tasks: ${tasks.length}`); - console.log(); + console.log(`Workflow name: ${workflowDef.name}`); + const tasks: Record[] = (workflowDef.tasks as Record[]) ?? []; + console.log(`Total tasks: ${tasks.length}`); + console.log(); - // Walk the task tree - for (const task of tasks) { - console.log(` [${task.type}] ${task.taskReferenceName}`); - if (task.type === 'DO_WHILE' && Array.isArray(task.loopOver)) { - for (const sub of task.loopOver as Record[]) { - console.log(` [${sub.type}] ${sub.taskReferenceName}`); + // Walk the task tree + for (const task of tasks) { + console.log(` [${task.type}] ${task.taskReferenceName}`); + if (task.type === 'DO_WHILE' && Array.isArray(task.loopOver)) { + for (const sub of task.loopOver as Record[]) { + console.log(` [${sub.type}] ${sub.taskReferenceName}`); + } } } - } - // Full JSON for CI/CD validation or export - console.log('\n--- Full workflow JSON ---'); - console.log(JSON.stringify(workflowDef, null, 2)); -} finally { - await runtime.shutdown(); + // Full JSON for CI/CD validation or export + console.log('\n--- Full workflow JSON ---'); + console.log(JSON.stringify(workflowDef, null, 2)); + } finally { + await runtime.shutdown(); + } } + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/examples/agents/63-deploy.ts b/examples/agents/63-deploy.ts index e3740077..297aed70 100644 --- a/examples/agents/63-deploy.ts +++ b/examples/agents/63-deploy.ts @@ -70,20 +70,31 @@ export const opsBot = new Agent({ // -- Deploy: compile + register on server ------------------------------------ -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(docAssistant, 'How do I reset my password?'); - result.printResult(); +async function main() { + const runtime = new AgentRuntime(); + try { + const result = await runtime.run(docAssistant, 'How do I reset my password?'); + result.printResult(); - // Production pattern: - // 1. Deploy once during CI/CD (optional -- serve() below also deploys): - // await runtime.deploy(docAssistant); - // await runtime.deploy(opsBot); - // CLI alternative: - // agentspan deploy --package sdk/typescript/examples --agents doc_assistant - // - // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): - // await runtime.serve(docAssistant, opsBot); -} finally { - await runtime.shutdown(); + // Production pattern: + // 1. Deploy once during CI/CD (optional -- serve() below also deploys): + // await runtime.deploy(docAssistant); + // await runtime.deploy(opsBot); + // CLI alternative: + // agentspan deploy --package sdk/typescript/examples --agents doc_assistant + // + // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): + // await runtime.serve(docAssistant, opsBot); + } finally { + await runtime.shutdown(); + } +} + +// Guard: 63c-run-by-name.ts imports docAssistant from this file — only run +// when executed directly, not on import. +if (require.main === module) { + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); } diff --git a/examples/agents/63b-serve.ts b/examples/agents/63b-serve.ts index 413673a3..9e0ed3ca 100644 --- a/examples/agents/63b-serve.ts +++ b/examples/agents/63b-serve.ts @@ -74,20 +74,27 @@ export const opsBot = new Agent({ // -- Serve: register workers and block --------------------------------------- -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(opsBot, 'Check the status of the API gateway.'); - result.printResult(); +async function main() { + const runtime = new AgentRuntime(); + try { + const result = await runtime.run(opsBot, 'Check the status of the API gateway.'); + result.printResult(); - // Production pattern: - // 1. Deploy once during CI/CD (optional -- serve() below also deploys): - // await runtime.deploy(docAssistant); - // await runtime.deploy(opsBot); - // CLI alternative: - // agentspan deploy --package sdk/typescript/examples --agents doc_assistant - // - // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): - // await runtime.serve(docAssistant, opsBot); -} finally { - await runtime.shutdown(); + // Production pattern: + // 1. Deploy once during CI/CD (optional -- serve() below also deploys): + // await runtime.deploy(docAssistant); + // await runtime.deploy(opsBot); + // CLI alternative: + // agentspan deploy --package sdk/typescript/examples --agents doc_assistant + // + // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): + // await runtime.serve(docAssistant, opsBot); + } finally { + await runtime.shutdown(); + } } + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/examples/agents/63c-run-by-name.ts b/examples/agents/63c-run-by-name.ts index 7eaa4da6..d3783d22 100644 --- a/examples/agents/63c-run-by-name.ts +++ b/examples/agents/63c-run-by-name.ts @@ -16,19 +16,26 @@ import { docAssistant } from './63-deploy.js'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(docAssistant, 'How do I reset my password?'); - result.printResult(); +async function main() { + const runtime = new AgentRuntime(); + try { + const result = await runtime.run(docAssistant, 'How do I reset my password?'); + result.printResult(); - // Production pattern: - // 1. Deploy once during CI/CD (optional -- serve() below also deploys): - // await runtime.deploy(docAssistant); - // CLI alternative: - // agentspan deploy --package sdk/typescript/examples --agents doc_assistant - // - // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): - // await runtime.serve(docAssistant); -} finally { - await runtime.shutdown(); + // Production pattern: + // 1. Deploy once during CI/CD (optional -- serve() below also deploys): + // await runtime.deploy(docAssistant); + // CLI alternative: + // agentspan deploy --package sdk/typescript/examples --agents doc_assistant + // + // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): + // await runtime.serve(docAssistant); + } finally { + await runtime.shutdown(); + } } + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/examples/agents/63d-serve-from-package.ts b/examples/agents/63d-serve-from-package.ts index bf6d9b2b..4d6a5040 100644 --- a/examples/agents/63d-serve-from-package.ts +++ b/examples/agents/63d-serve-from-package.ts @@ -43,19 +43,30 @@ export const monitoringAgent = new Agent({ // -- Serve ------------------------------------------------------------------- -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(monitoringAgent, 'Is everything healthy? Run a full check.'); - result.printResult(); - - // Production pattern: - // 1. Deploy once during CI/CD (optional -- serve() below also deploys): - // await runtime.deploy(monitoringAgent); - // CLI alternative: - // agentspan deploy --package sdk/typescript/examples --agents monitoring - // - // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): - // await runtime.serve(monitoringAgent); -} finally { - await runtime.shutdown(); +async function main() { + const runtime = new AgentRuntime(); + try { + const result = await runtime.run(monitoringAgent, 'Is everything healthy? Run a full check.'); + result.printResult(); + + // Production pattern: + // 1. Deploy once during CI/CD (optional -- serve() below also deploys): + // await runtime.deploy(monitoringAgent); + // CLI alternative: + // agentspan deploy --package sdk/typescript/examples --agents monitoring + // + // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): + // await runtime.serve(monitoringAgent); + } finally { + await runtime.shutdown(); + } +} + +// Guard: 63e-run-monitoring.ts imports monitoringAgent from this file — only +// run when executed directly, not on import. +if (require.main === module) { + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); } diff --git a/examples/agents/63e-run-monitoring.ts b/examples/agents/63e-run-monitoring.ts index 118edd51..56a31bce 100644 --- a/examples/agents/63e-run-monitoring.ts +++ b/examples/agents/63e-run-monitoring.ts @@ -10,19 +10,26 @@ import { monitoringAgent } from './63d-serve-from-package.js'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(monitoringAgent, 'Is everything healthy? Run a full check.'); - result.printResult(); +async function main() { + const runtime = new AgentRuntime(); + try { + const result = await runtime.run(monitoringAgent, 'Is everything healthy? Run a full check.'); + result.printResult(); - // Production pattern: - // 1. Deploy once during CI/CD (optional -- serve() below also deploys): - // await runtime.deploy(monitoringAgent); - // CLI alternative: - // agentspan deploy --package sdk/typescript/examples --agents monitoring - // - // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): - // await runtime.serve(monitoringAgent); -} finally { - await runtime.shutdown(); + // Production pattern: + // 1. Deploy once during CI/CD (optional -- serve() below also deploys): + // await runtime.deploy(monitoringAgent); + // CLI alternative: + // agentspan deploy --package sdk/typescript/examples --agents monitoring + // + // 2. In a separate long-lived worker process (deploys + registers workers + starts polling): + // await runtime.serve(monitoringAgent); + } finally { + await runtime.shutdown(); + } } + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/examples/agents/74-cli-error-output.ts b/examples/agents/74-cli-error-output.ts index b8bddd10..c838e91b 100644 --- a/examples/agents/74-cli-error-output.ts +++ b/examples/agents/74-cli-error-output.ts @@ -34,12 +34,16 @@ async function main() { try { const result = await runtime.run(agent, prompt); result.printResult(); - const output = String(result.output ?? ''); + // result.output is an object ({ result, finishReason, ... }) — String() + // would collapse it to "[object Object]" and the check below would + // always fail. + const output = + typeof result.output === 'string' ? result.output : JSON.stringify(result.output ?? ''); // Verify the agent saw the error output const saw = output.includes('No such file or directory') || output.includes('nonexistent'); if (!saw) { - console.error(`\nFAIL: agent did not surface CLI error output. Got: ${String(output)}`); + console.error(`\nFAIL: agent did not surface CLI error output. Got: ${output}`); process.exit(1); } console.log('\nPASS: agent correctly reported CLI error output');