diff --git a/.github/workflows/agent-e2e.yml b/.github/workflows/agent-e2e.yml index d0cc4ad0..8d26127d 100644 --- a/.github/workflows/agent-e2e.yml +++ b/.github/workflows/agent-e2e.yml @@ -5,7 +5,7 @@ name: Agent E2E # /agent/* control plane + TaskDef.runtimeMetadata persistence (conductor-oss # PR #1255) and the agent runtime on by default from 3.32.0-rc.8 onward. # The Agentspan server JAR is no longer used as a server flavor here; the -# Agentspan CLI binary is still downloaded (its own separate pin) since the +# external `agentspan` CLI binary is still downloaded (its own separate pin) since the # CLI/skill e2e suites still drive it against this same server. # Port of the Python SDK's proven agent-e2e workflow; JS deltas only # (Node toolchain, jest runner; Python kept solely for mcp-testkit). @@ -21,7 +21,7 @@ concurrency: cancel-in-progress: true env: - AGENTSPAN_CLI_VERSION: "0.4.4" # pinned CLI release — independent of the server + CONDUCTOR_AGENT_CLI_VERSION: "0.4.4" # pinned CLI release — independent of the server CONDUCTOR_OSS_VERSION: "3.32.0-rc.8" # pinned conductor-oss release — bump deliberately jobs: @@ -31,8 +31,8 @@ jobs: env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - AGENTSPAN_SERVER_URL: http://localhost:8080/api - AGENTSPAN_CLI_PATH: ${{ github.workspace }}/agentspan + CONDUCTOR_AGENT_SERVER_URL: http://localhost:8080/api + CONDUCTOR_AGENT_CLI_PATH: ${{ github.workspace }}/agentspan steps: - name: Checkout code uses: actions/checkout@v4 @@ -69,13 +69,13 @@ jobs: curl -sfL "https://repo1.maven.org/maven2/org/conductoross/conductor-server/${CONDUCTOR_OSS_VERSION}/conductor-server-${CONDUCTOR_OSS_VERSION}-boot.jar" \ --output conductor-server-boot.jar - # Agentspan CLI (CLI/skill suites) — kept on its own separate pin, + # External `agentspan` CLI (CLI/skill suites) — kept on its own separate pin, # independent of which server JAR is under test. - name: Download CLI binary from release env: GH_TOKEN: ${{ github.token }} run: | - gh release download "v${AGENTSPAN_CLI_VERSION}" --repo agentspan-ai/agentspan \ + gh release download "v${CONDUCTOR_AGENT_CLI_VERSION}" --repo agentspan-ai/agentspan \ --pattern "agentspan_linux_amd64" --output agentspan chmod +x agentspan diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 79ff70e9..be2aa4ee 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -12,6 +12,58 @@ concurrency: cancel-in-progress: true jobs: + # Documentation validation. Deliberately one fast job rather than folded into + # the unit-test matrix, where every check would run three times identically. + docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Validate documentation links + uses: lycheeverse/lychee-action@v2 + with: + args: --offline --no-progress README.md docs + fail: true + + - name: Reject retired documentation references + run: | + # Retired paths and constructs that must not come back. + # + # Deliberately does NOT reject docs/agents/api-reference.md or + # docs/agents/index.md: java-sdk retires those, but we keep + # api-reference.md as a redirect stub (as python-sdk does) so inbound + # links keep resolving. See docs/documentation-parity.md. + if grep -REn 'docs/agents/agent-(runtime-api|client-api|structure|schema)\.md|docs/agents/generated|docs/testing_framework\.md|conductor\.netflix\.com' README.md docs; then + echo "Documentation references a retired path or renderer-specific construct." + exit 1 + fi + + - name: Reject pre-rebrand configuration names outside the migration guide + run: | + # AGENTSPAN_* must not be presented as the way to configure the SDK. + # docs/upgrading.md and docs/compatibility.md document the deprecation + # itself, and the reference pages name the deprecated export aliases, + # so those are allowed to mention them. + if grep -REn 'AGENTSPAN_[A-Z]' README.md docs \ + --exclude=upgrading.md --exclude=compatibility.md; then + echo "AGENTSPAN_* appears outside the migration guide." + echo "Use CONDUCTOR_AGENT_* — see docs/upgrading.md." + exit 1 + fi + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: "npm" + + - name: Install Dependencies + run: npm ci + + - name: Verify agent configuration schema + run: npm run verify:agent-schema + linter: runs-on: ubuntu-latest steps: diff --git a/AGENTS.md b/AGENTS.md index 7680b987..b957476d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,14 +40,14 @@ src/open-api/ # OpenAPI layer types.ts # Extended types - add custom fields here src/integration-tests/ # E2E tests against real Conductor server utils/ # waitForWorkflowStatus, executeWorkflowWithRetry, etc. -src/agents/ # Durable agent layer (merged Agentspan TS SDK) +src/agents/ # Durable agent layer (merged Conductor TS SDK) index.ts # Agent, AgentRuntime, tool, guardrails, handoffs, ... frameworks/ # LangGraph/LangChain/generic serializers + detection testing/ # Agent testing toolkit (/agents/testing subpath) wrappers/ # Vercel AI / LangGraph / LangChain drop-in wrappers __tests__/ # Colocated jest unit tests (picked up by test:unit) -e2e/ # Agent e2e suites vs live agentspan server (jest.e2e.config.mjs) -cli-bin/ # agentspan CLI helper scripts (Go CLI walk-up probe target) +e2e/ # Agent e2e suites vs live Conductor server (jest.e2e.config.mjs) +cli-bin/ # Conductor CLI helper scripts (Go CLI walk-up probe target) examples/agents/ # Agent examples (own tsconfig; run via npx tsx) docs/agents/ # Agent layer documentation ``` @@ -76,9 +76,28 @@ docs/agents/ # Agent layer documentation Framework subdirs (adk/, langgraph/, openai/, vercel-ai/) install their own deps (`scripts/install-example-deps.sh`); `examples/agents` is excluded from the root tsconfig. -- `AGENTSPAN_*` env vars (`AGENTSPAN_SERVER_URL`, default - `http://localhost:8080/api`) are the agent layer's config surface — kept - working as-is; `CONDUCTOR_*` aliases are a possible follow-up. +- `CONDUCTOR_AGENT_*` env vars (`CONDUCTOR_AGENT_SERVER_URL`, default + `http://localhost:8080/api`) are the agent layer's config surface. The + previous `AGENTSPAN_*` spelling still resolves as a deprecated fallback and + warns once per name per process — see `src/agents/legacy-env.ts`, the + duplicated helper in `resolveOrkesConfig.ts`, and `e2e/helpers.ts`. + +### Names that are NOT ours to rename + +The Agentspan → Conductor rebrand deliberately left these alone. They are +externally owned, and renaming them points users at things that don't exist or +breaks interop. Do not "clean these up": + +| Name | Owner | +|---|---| +| `__agentspan_ctx__`, `__agentspan_sdk__.cjs`, `_agentspan.llm`/`.model`/`.framework`/`.tools`, `_agentspan_human_task`, `_agentspan_human_prompt` | cross-SDK wire keys / Go CLI probe target — same server compile path as the Python SDK | +| `agentspan.embedded`, `agentspan.default-context-window` | orkes-conductor server boot properties | +| `agentspan-ai/agentspan`, `agentspan/codingexamples`, `agentspan-server` | external GitHub repos and container image | +| `agentspan_linux_amd64`, the `agentspan` CLI binary, `agentspan deploy`/`credentials`/`login`/`import` | the external CLI's release asset and command surface | +| `agentspan <= 0.4.2`, `agentspan server > 0.4.2` | version-qualified references to the upstream product — renaming makes them factually false | + +Every cross-SDK wire key is underscore-prefixed; that invariant is the quickest +way to tell a wire contract from our own prose. ## Commands @@ -94,9 +113,9 @@ CONDUCTOR_AUTH_KEY=key CONDUCTOR_AUTH_SECRET=secret \ ORKES_BACKEND_VERSION=5 \ npm run test:integration:orkes-v5 -# Agent e2e (requires a running agentspan server + LLM keys; CI does this +# Agent e2e (requires a running Conductor server + LLM keys; CI does this # against the pinned release JAR — see .github/workflows/agent-e2e.yml) -AGENTSPAN_SERVER_URL=http://localhost:8080/api npm run test:agent-e2e +CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api npm run test:agent-e2e ``` ## Post-Change Verification (Required) @@ -251,14 +270,23 @@ public async someMethod(args): Promise { ## Documentation Maintenance -### Metrics Documentation (METRICS.md) +### Metrics Documentation (docs/observability.md) When adding, removing, or renaming metrics in `src/sdk/worker/metrics/`: 1. Update both `LegacyMetricsCollector.ts` and `CanonicalMetricsCollector.ts` (or add a no-op stub in the collector that does not emit the metric) 2. Ensure `toPrometheusText()` and the corresponding `PrometheusRegistry` / `CanonicalPrometheusRegistry` are updated in sync — missing a metric in either causes silent data loss -3. Update `METRICS.md` to reflect the change in both the legacy and canonical catalog tables +3. Update `docs/observability.md` to reflect the change in both the legacy and canonical catalog tables (the "Metric reference" section). Root `METRICS.md` is now a redirect stub — don't add content there. 4. Add or update the corresponding direct recording method documentation if applicable +### Documentation structure + +Docs follow the canonical layout shared with the Java and Python SDKs: +`docs/*.md` plus `docs/agents/{concepts,frameworks,reference}/`. See +`docs/documentation-standard.md` for the page shape and terminology, and +`docs/documentation-parity.md` for the cross-SDK mapping and CI checks. +`docs/agents/*.md` and `docs/api-reference/*.md` are redirect stubs kept for +inbound links — don't add content to them, and don't delete them. + ### SDK_NEW_LANGUAGE_GUIDE.md When adding new client methods, builders, worker features, or examples: diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index fdbf41da..cc982ec6 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -1,5 +1,66 @@ # Breaking Changes +## Unreleased (Agentspan → Conductor rebrand) + +### `AGENTSPAN_*` environment variables renamed to `CONDUCTOR_AGENT_*` + +**Change:** every `AGENTSPAN_` env var is now `CONDUCTOR_AGENT_` — +`AGENTSPAN_SERVER_URL` → `CONDUCTOR_AGENT_SERVER_URL`, and likewise for +`AUTH_KEY`, `AUTH_SECRET`, `LLM_MODEL`, `LOG_LEVEL`, `CLI_PATH`, +`WORKER_POLL_INTERVAL`, `WORKER_THREADS`, `AUTO_START_WORKERS`, +`STREAMING_ENABLED`, and the three `LIVENESS_*` knobs. + +**Why:** the agent layer was merged in from the Agentspan TS SDK, which has +since been rebranded to Conductor. This aligns the config surface with the +Python and Java SDKs. + +**Impact:** + +| Scenario | Before | After | Breaks? | +|----------|--------|-------|---------| +| `CONDUCTOR_AGENT_SERVER_URL` set | Ignored | Used | **No** | +| `AGENTSPAN_SERVER_URL` set | Used | Used, warns once per process | **No** | +| Both set | — | `CONDUCTOR_AGENT_*` wins | **No** | +| Custom `ConductorLogger` without `warn` | — | Deprecation goes to `info` | **No** | + +**Migration:** rename the variables. The old names keep working as deprecated +fallbacks and will be removed in a future release; each one warns once per +process the first time it actually supplies a value. `AGENTSPAN_LOG_LEVEL` +falls back silently — warning there would mean logging through the logger whose +level is still being resolved. + +The precedence chain (spec R3) is unchanged apart from the new tier: +`CONDUCTOR_*` env → explicit config → `CONDUCTOR_AGENT_*` env → +`AGENTSPAN_*` env (deprecated) → `http://localhost:8080`. + +### `AgentspanError` renamed to `ConductorAgentError` + +**Change:** the base class of the agent error hierarchy is now +`ConductorAgentError`. `AgentspanError` remains exported as a deprecated alias. +`AgentspanMetadata` (from `./agents/langchain` and `./agents/langgraph`) is +likewise aliased to `ConductorAgentMetadata`. + +**Impact:** + +| Scenario | Before | After | Breaks? | +|----------|--------|-------|---------| +| `import { AgentspanError }` | Works | Works, marked `@deprecated` | **No** | +| `catch (e) { e instanceof AgentspanError }` | Works | Works — alias is the same class object, not a subclass | **No** | +| `const e: AgentspanError` (type position) | Works | Works — value and type alias both exported | **No** | +| `err.name` on a base-class instance | `"AgentspanError"` | `"ConductorAgentError"` | **Yes** if asserted on | + +**Migration:** switch to `ConductorAgentError`. The only observable change for +existing code is `error.name` on a base-class instance, which now reports the +canonical name — assertions on that string need updating. + +### Names deliberately NOT renamed + +Cross-SDK wire keys (`__agentspan_ctx__`, `_agentspan.llm`, …), orkes-conductor +server boot properties (`agentspan.embedded`), the external `agentspan` CLI and +its subcommands, external repos/npm scopes (`agentspan-ai/agentspan`, +`@agentspan-ai`), and version-qualified references to the upstream product +(`agentspan <= 0.4.2`) are unchanged. See the table in `AGENTS.md`. + ## v3.x (Worker Architecture Parity Release) ### `TaskHandler.startWorkers()` is now `async` diff --git a/LEASE_EXTENSION.md b/LEASE_EXTENSION.md index 3fd6eb4d..60cd3b3d 100644 --- a/LEASE_EXTENSION.md +++ b/LEASE_EXTENSION.md @@ -1,224 +1,12 @@ # Lease Extension (Heartbeat) -Long-running workers need to keep their task **lease** alive on the Conductor server. When a task is polled, the server starts a `responseTimeoutSeconds` timer. If no update arrives before the timer expires, the server re-queues the task — potentially causing **duplicate execution** by a second worker. - -Lease extension sends a periodic heartbeat (`extendLease: true`) to the server that resets this timer, allowing the worker to safely run for minutes or hours. - ---- - -## Quick Start - -Enable via the `@worker` decorator: - -```typescript -import { worker } from "@io-orkes/conductor-javascript"; - -@worker({ - taskDefName: "process_video", - leaseExtendEnabled: true, // ← heartbeat at 80% of responseTimeoutSeconds -}) -async function processVideo(task: Task): Promise { - // Takes 5 minutes — server lease stays alive automatically - await encodeVideo(task.inputData.videoUrl); - return { status: "COMPLETED", outputData: { done: true } }; -} -``` - -Or on a manually constructed `ConductorWorker`: - -```typescript -const runner = new TaskRunner({ - worker: { - taskDefName: "process_video", - execute: processVideo, - leaseExtendEnabled: true, - }, - client, - options: { workerID: "worker-1", domain: undefined }, -}); -runner.startPolling(); -``` - ---- - -## How It Works - -When `leaseExtendEnabled: true` is set and a task is polled: - -1. The `LeaseTracker` records the task and computes a heartbeat interval: - ``` - intervalMs = responseTimeoutSeconds × 0.8 × 1000 - ``` -2. A `setInterval` (100 ms tick) runs **independently of the polling loop** — it fires even when all concurrency slots are occupied with executing tasks. -3. When `intervalMs` elapses since the last heartbeat (or task start), a `extendLease: true` update is sent to the server via the v1 endpoint, resetting the `responseTimeoutSeconds` timer. -4. The task is untracked as soon as `worker.execute()` resolves (before the final result is submitted). - -``` -t=0s task polled → lease tracked -t=8s heartbeat #1 → server timer reset to 10s -t=16s heartbeat #2 → server timer reset to 10s -t=20s execute() → COMPLETED, lease untracked -``` -*(Example: `responseTimeoutSeconds=10s`, execution takes 20s)* - -### Why independent of the poll loop? - -The heartbeat timer is a `setInterval` that runs on the Node.js event loop separate from the `Poller`. If all concurrency slots are full (workers are busy), no new tasks are polled — but heartbeats still fire for the tasks currently executing. - ---- - -## Configuration - -### `@worker` decorator option - -```typescript -@worker({ - taskDefName: "my_task", - leaseExtendEnabled: true, // default: false -}) -``` - -### Environment variable override - -Per-worker or global overrides follow the same hierarchy as all other worker config: - -```bash -# Worker-specific (highest priority) -CONDUCTOR_WORKER_MY_TASK_LEASE_EXTEND_ENABLED=true - -# Global (applies to all workers) -CONDUCTOR_WORKER_ALL_LEASE_EXTEND_ENABLED=true -``` - -### Task definition requirement - -The task must have `responseTimeoutSeconds >= 1.25` for lease extension to activate. Tasks with shorter response timeouts produce a computed interval < 1000 ms, which is skipped (matches Python SDK behaviour). - -```typescript -await metadataClient.registerTask({ - name: "process_video", - responseTimeoutSeconds: 60, // heartbeat fires every 48s - timeoutSeconds: 3600, // hard ceiling (unchanged by heartbeats) - retryCount: 0, -}); -``` - -> **Note:** `leaseExtendEnabled` resets the `responseTimeoutSeconds` window on each heartbeat. It does **not** extend the task's `timeoutSeconds` (total execution ceiling). - ---- - -## Constants - -| Constant | Value | Description | -|----------|-------|-------------| -| `LEASE_EXTEND_DURATION_FACTOR` | `0.8` | Heartbeat fires at 80% of `responseTimeoutSeconds` | -| `LEASE_EXTEND_RETRY_COUNT` | `3` | Retry attempts per heartbeat on failure | -| `HEARTBEAT_CHECK_INTERVAL_MS` | `100` | How often to check if a heartbeat is due | -| `HEARTBEAT_RETRY_DELAY_MS` | `500` | Delay between heartbeat retry attempts | - -```typescript -import { LEASE_EXTEND_DURATION_FACTOR, LEASE_EXTEND_RETRY_COUNT } from "@io-orkes/conductor-javascript"; -``` - ---- - -## Retry Behaviour - -If a heartbeat fails, it is retried up to `LEASE_EXTEND_RETRY_COUNT` (3) times with a 500 ms delay between attempts. If all retries fail: - -- The error is logged. -- The task remains tracked — the next check interval will attempt another heartbeat. -- The task itself is **not failed** due to heartbeat errors. - ---- - -## Direct `LeaseTracker` Usage - -For custom worker implementations that bypass `TaskRunner`, `LeaseTracker` is exported from the public API: - -```typescript -import { LeaseTracker, TaskResource, orkesConductorClient } from "@io-orkes/conductor-javascript"; -import type { LeaseInfo } from "@io-orkes/conductor-javascript"; - -const client = await orkesConductorClient(); - -const tracker = new LeaseTracker( - // sendHeartbeatFn — called by LeaseTracker on each heartbeat - async (taskId, workflowInstanceId) => { - await TaskResource.updateTask({ - client, - body: { taskId, workflowInstanceId, status: "IN_PROGRESS", extendLease: true }, - throwOnError: true, - }); - }, - logger -); - -tracker.start(); // start the 100ms check interval -tracker.track(task); // track a polled task -// ... worker executes task ... -tracker.untrack(task.taskId!); // untrack immediately after execute() resolves -tracker.stop(); // stop the interval (on worker shutdown) -``` - -`LeaseInfo` describes the tracked state for a single task: - -```typescript -interface LeaseInfo { - readonly taskId: string; - readonly workflowInstanceId: string; - readonly responseTimeoutSeconds: number; - readonly lastHeartbeatTime: number; // Date.now() of last successful heartbeat - readonly intervalMs: number; // responseTimeoutSeconds × 0.8 × 1000 - readonly isHeartbeating: boolean; // true while a heartbeat chain is in-flight -} -``` - ---- - -## Python SDK Parity - -| Behaviour | Python SDK | JS SDK | -|-----------|-----------|--------| -| Heartbeat interval | `responseTimeoutSeconds × 0.8` | ✓ same | -| Minimum interval | `< 1s → skip` | ✓ `< 1000ms → skip` | -| Retry count | 3 | ✓ same | -| Retry delay | ~500ms | ✓ same | -| Heartbeat endpoint | v1 `updateTask` | ✓ same | -| Independent of poll loop | ✓ (Python `run_once()` pre-poll) | ✓ (JS `setInterval`) | -| `leaseExtendEnabled` flag | ✓ | ✓ | - ---- - -## Related - -- [`pullWorkflowMessages`](#pullworkflowmessages) — task builder for consuming workflow message queue messages, which uses the `IN_PROGRESS` / re-queue pattern that lease extension is designed to protect. -- [METRICS.md](./METRICS.md) — monitoring worker health, poll latency, and execution duration. - ---- - -## `pullWorkflowMessages` Task Builder - -A task that consumes messages from the workflow's message queue (WMQ). When messages are available the task completes; when the queue is empty it returns `IN_PROGRESS` and is re-evaluated after ~1 second. - -```typescript -import { pullWorkflowMessages } from "@io-orkes/conductor-javascript"; - -const wf = new ConductorWorkflow(executor, "order_processor") - .add(pullWorkflowMessages("read_messages", /* batchSize */ 5)) - .add(simpleTask("process_ref", "process_order", { messages: "${read_messages.output.messages}" })); -``` - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `taskReferenceName` | `string` | — | Unique reference name within the workflow | -| `batchSize` | `number` | `1` | Max messages to dequeue per execution (server cap ~100) | -| `optional` | `boolean` | `undefined` | Whether the task is optional | - -**Output shape:** -```json -{ - "messages": [ /* WorkflowMessage objects */ ], - "count": 3 -} -``` +This reference has moved to +[docs/reliability.md](docs/reliability.md#lease-extension), which carries the +heartbeat mechanics, configuration, constants, retry behavior, direct +`LeaseTracker` usage, and Python SDK parity. + +- [Reliability — lease extension](docs/reliability.md#lease-extension) +- [Direct LeaseTracker usage](docs/reliability.md#direct-leasetracker-usage) +- [Workers — long-running tasks](docs/workers.md#long-running-tasks) +- [Observability — monitoring worker health](docs/observability.md) +- [Documentation index](docs/README.md) diff --git a/METRICS.md b/METRICS.md index 813753fb..b7276dc0 100644 --- a/METRICS.md +++ b/METRICS.md @@ -1,512 +1,11 @@ # JavaScript SDK Metrics -The Conductor JavaScript SDK can expose Prometheus metrics for worker polling, -task execution, task updates, workflow starts, external payload usage, and -API-client HTTP calls. - -The SDK currently has two mutually exclusive metric surfaces: - -- **Legacy metrics** are the default. They preserve the original JavaScript SDK - names and shapes, including a `conductor_worker_` prefix, `task_type` labels, - millisecond time units, and Summary type for distributions. -- **Canonical metrics** are opt-in with `WORKER_CANONICAL_METRICS=true`. They - use the cross-SDK canonical names, labels, units, and Prometheus histogram - shapes. - -Only one collector is active at a time. The SDK does not emit legacy and -canonical metrics at the same time. - -Metrics are created lazily. A metric appears in `/metrics` only after the -corresponding worker event or collector method records it. Some low-level -surface metrics, such as ack, queue-full, paused, and uncaught-exception -counters, may not appear in normal worker runs unless that path is exercised. - -## Usage - -Create a metrics collector, start a scrape server, and wire the collector into -`TaskHandler` as an event listener: - -```typescript -import { - createMetricsCollector, - MetricsServer, - TaskHandler, -} from "@io-orkes/conductor-javascript"; - -const metrics = createMetricsCollector(); -const server = new MetricsServer(metrics, 9090); -await server.start(); - -const handler = new TaskHandler({ - client, - eventListeners: [metrics], - scanForDecorated: true, -}); - -await handler.startWorkers(); -// GET http://localhost:9090/metrics — Prometheus text format -// GET http://localhost:9090/health — {"status":"UP"} -``` - -`createMetricsCollector()` reads `WORKER_CANONICAL_METRICS` and returns either -a `LegacyMetricsCollector` or a `CanonicalMetricsCollector`. Both implement -`MetricsCollectorInterface`, so call sites never need to know which variant is -active. - -You can also construct a collector directly if you need to pass configuration: - -```typescript -import { LegacyMetricsCollector } from "@io-orkes/conductor-javascript"; - -const metrics = new LegacyMetricsCollector({ - httpPort: 9090, - filePath: "/tmp/conductor_metrics.prom", - fileWriteIntervalMs: 10000, - usePromClient: true, -}); -``` - -### File Output - -```typescript -const metrics = createMetricsCollector({ - filePath: "/tmp/conductor_metrics.prom", - fileWriteIntervalMs: 10000, -}); -``` - -The file writer performs an immediate first write, then writes periodically at -the configured interval. The timer is unreferenced so it does not prevent -Node.js process exit. - -### prom-client Integration - -```typescript -const metrics = createMetricsCollector({ usePromClient: true }); -// Metrics are registered in prom-client's default registry. -// Use prom-client's register.metrics() for native scraping. -``` - -Requires `npm install prom-client`. Falls back to built-in text format if -prom-client is not installed. - -## Selecting Canonical Metrics - -Set `WORKER_CANONICAL_METRICS` before the worker starts: - -```shell -WORKER_CANONICAL_METRICS=true node my_worker.js -``` - -Accepted true values are `true`, `1`, and `yes`, case-insensitive. Any other -value, or an unset variable, selects legacy metrics. The variable is read when -the metrics collector is created, so changing it requires a worker restart. - -## Configuration - -| Option | Type | Default | Description | -|---|---|---|---| -| `prefix` | `string` | `"conductor_worker"` | Prometheus metric name prefix. Legacy only; canonical metrics are unprefixed. | -| `httpPort` | `number` | — | Start built-in HTTP server on this port. | -| `filePath` | `string` | — | Periodically write metrics to this file path. | -| `fileWriteIntervalMs` | `number` | `5000` | File write interval in milliseconds. | -| `slidingWindowSize` | `number` | `1000` | Max observations for quantile calculation. Legacy only; canonical uses histogram buckets. | -| `usePromClient` | `boolean` | `false` | Use `prom-client` for native Prometheus integration. | - -## Canonical Metrics - -Canonical timing values are seconds. Canonical size values are bytes. Label -names use camelCase. - -### Canonical Counters - -| Metric | Labels | Description | -|---|---|---| -| `task_poll_total` | `taskType` | Incremented each time the worker issues a poll request. | -| `task_execution_started_total` | `taskType` | Incremented when a polled task is dispatched to the worker function. | -| `task_poll_error_total` | `taskType`, `exception` | Incremented when a poll request fails client-side. | -| `task_execute_error_total` | `taskType`, `exception` | Incremented when the worker function throws. | -| `task_update_error_total` | `taskType`, `exception` | Incremented when updating the task result fails. | -| `task_ack_error_total` | `taskType`, `exception` | Collector surface for task ack errors. The internal runner uses batch poll responses as ack and may not emit this during normal polling. | -| `task_ack_failed_total` | `taskType` | Collector surface for failed task ack responses. The internal runner uses batch poll responses as ack and may not emit this during normal polling. | -| `task_execution_queue_full_total` | `taskType` | Incremented when the worker execution queue is saturated. | -| `task_paused_total` | `taskType` | Incremented when a worker is paused and skips acting on a poll. | -| `thread_uncaught_exceptions_total` | `exception` | Incremented on uncaught exceptions in the worker process. | -| `external_payload_used_total` | `entityName`, `operation`, `payloadType` | Incremented when external payload storage is used for task or workflow payloads. | -| `workflow_start_error_total` | `workflowType`, `exception` | Incremented when starting a workflow fails client-side. | - -### Canonical Time Histograms - -All canonical time histograms use buckets: -`0.001`, `0.005`, `0.01`, `0.025`, `0.05`, `0.1`, `0.25`, `0.5`, `1`, `2.5`, -`5`, `10`. - -| Metric | Labels | Description | -|---|---|---| -| `task_poll_time_seconds` | `taskType`, `status` | Poll request latency. `status` is `SUCCESS` or `FAILURE`. | -| `task_execute_time_seconds` | `taskType`, `status` | Worker function execution duration. `status` is `SUCCESS` or `FAILURE`. | -| `task_update_time_seconds` | `taskType`, `status` | Task-result update latency. `status` is `SUCCESS` or `FAILURE`. | -| `http_api_client_request_seconds` | `method`, `uri`, `status` | API-client HTTP request latency. `status` is the HTTP status code as a string, or `"0"` on network failure. | - -Each histogram exposes Prometheus series such as: - -```prometheus -task_execute_time_seconds_bucket{taskType="my_task",status="SUCCESS",le="0.1"} 42 -task_execute_time_seconds_count{taskType="my_task",status="SUCCESS"} 50 -task_execute_time_seconds_sum{taskType="my_task",status="SUCCESS"} 2.3 -``` - -### Canonical Size Histograms - -All canonical size histograms use buckets: -`100`, `1000`, `10000`, `100000`, `1000000`, `10000000`. - -| Metric | Labels | Description | -|---|---|---| -| `task_result_size_bytes` | `taskType` | Serialized task result output size. | -| `workflow_input_size_bytes` | `workflowType`, `version` | Serialized workflow input size. `version` is an empty string when not provided. | - -### Canonical Gauges - -| Metric | Labels | Description | -|---|---|---| -| `active_workers` | `taskType` | Current number of workers actively executing tasks. | - -## Legacy Metrics - -Legacy mode is the default so existing dashboards and alerts continue to work. -The default metric name prefix is `conductor_worker`. The prefix is configurable -via the `prefix` option on `MetricsCollectorConfig`. - -Distribution metrics are sliding-window summaries over the latest 1,000 -observations (configurable via `slidingWindowSize`), exposing quantiles at -p50, p75, p90, p95, and p99. Legacy distribution metrics also expose `_count` -and `_sum` series. - -### Legacy Counters - -| Metric | Labels | Description | -|---|---|---| -| `conductor_worker_task_poll_total` | `task_type` | Incremented each time polling is done. | -| `conductor_worker_task_poll_error_total` | `task_type` | Incremented when a poll request fails. | -| `conductor_worker_task_execute_total` | `task_type` | Incremented when a task execution completes. | -| `conductor_worker_task_execute_error_total` | `task_type` | Task execution errors. Label format: `taskType:ExceptionName`. | -| `conductor_worker_task_update_error_total` | `task_type` | Incremented when updating the task result fails. | -| `conductor_worker_task_ack_error_total` | `task_type` | Collector surface for task ack errors. | -| `conductor_worker_task_execution_queue_full_total` | `task_type` | Incremented when the execution queue is saturated. | -| `conductor_worker_task_paused_total` | `task_type` | Incremented when a worker is paused and skips a poll. | -| `conductor_worker_external_payload_used_total` | `payload_type` | External payload storage usage. | -| `conductor_worker_thread_uncaught_exceptions_total` | none | Uncaught exceptions in the worker process. | -| `conductor_worker_worker_restart_total` | none | Worker restart events. | -| `conductor_worker_workflow_start_error_total` | none | Workflow start errors. | - -Legacy mode does not emit `task_execution_started_total`, -`task_ack_failed_total`, or `active_workers`. - -### Legacy Time Metrics - -Time values are milliseconds. Type is Summary. - -| Metric | Labels | Description | -|---|---|---| -| `conductor_worker_task_poll_time` | `task_type` | Poll round-trip duration. | -| `conductor_worker_task_execute_time` | `task_type` | Worker function execution duration. | -| `conductor_worker_task_update_time` | `task_type` | Task result update duration. | - -Each summary exposes quantile, count, and sum series: - -```prometheus -conductor_worker_task_execute_time{task_type="my_task",quantile="0.5"} 102 -conductor_worker_task_execute_time{task_type="my_task",quantile="0.95"} 250 -conductor_worker_task_execute_time_count{task_type="my_task"} 1000 -conductor_worker_task_execute_time_sum{task_type="my_task"} 120345 -``` - -### Legacy Size Metrics - -Type is Summary. Values are bytes. - -| Metric | Labels | Description | -|---|---|---| -| `conductor_worker_task_result_size_bytes` | `task_type` | Task result output payload size. | -| `conductor_worker_workflow_input_size_bytes` | `workflow_type` | Workflow input payload size. | - -### Legacy HTTP Metrics - -| Metric | Labels | Description | -|---|---|---| -| `conductor_worker_http_api_client_request` | `endpoint` | API request duration in milliseconds. The `endpoint` label is a compound `"METHOD:/api/path:STATUS"` string. | - -## Labels - -| Label | Used by | Values | -|---|---|---| -| `task_type` | Legacy worker metrics | Task definition name. | -| `taskType` | Canonical worker metrics | Task definition name. | -| `workflowType` | Canonical workflow metrics | Workflow definition name. | -| `workflow_type` | Legacy `conductor_worker_workflow_input_size_bytes` | Workflow definition name. | -| `version` | Canonical `workflow_input_size_bytes` | Workflow version as a string. Empty string when not provided. | -| `status` | Canonical task time histograms | `SUCCESS` or `FAILURE`. For `http_api_client_request_seconds`, the HTTP status code as a string, or `"0"` on network failure. | -| `exception` | Canonical error counters | Exception type name, such as `TypeError`. Derived from `error.name` or `error.constructor.name`. | -| `entityName` | Canonical `external_payload_used_total` | Task type or workflow name associated with the external payload. | -| `operation` | Canonical `external_payload_used_total` | External payload operation, such as `READ` or `WRITE`. | -| `payload_type` | Legacy `conductor_worker_external_payload_used_total` | Payload type, such as `workflow_input` or `task_output`. | -| `payloadType` | Canonical `external_payload_used_total` | Payload type, such as `TASK_INPUT`, `TASK_OUTPUT`, `WORKFLOW_INPUT`, or `WORKFLOW_OUTPUT`. | -| `method` | Canonical HTTP metrics | HTTP verb. | -| `uri` | Canonical HTTP metrics | Request path template (e.g. `/workflow/{workflowId}`). Uses bounded-cardinality templates, not interpolated paths. | -| `endpoint` | Legacy HTTP metrics | Compound `"METHOD:/api/path:STATUS"` string. | -| `quantile` | Legacy time and size metrics | `0.5`, `0.75`, `0.9`, `0.95`, or `0.99`. | - -## Migrating From Legacy to Canonical - -Canonical mode is opt-in during the deprecation period. Before switching a -production worker, update dashboards and alerts against a staging worker with -`WORKER_CANONICAL_METRICS=true`. - -Key changes: - -- The `conductor_worker_` prefix is removed. Canonical metric names are - unprefixed. -- Legacy task labels use `task_type`; canonical task labels use `taskType`. -- Legacy time metrics are millisecond summaries with quantiles. Canonical time - metrics are second-based histograms with bucket boundaries. Query `_bucket` - series with `histogram_quantile()` instead of reading `{quantile="..."}` - gauges. -- Legacy size metrics are summaries. Canonical size metrics are histograms. -- Canonical error counters add an `exception` label containing the exception - type name. -- Canonical time histograms add a `status` label (`SUCCESS` or `FAILURE`). -- Canonical mode adds metrics that legacy mode never emits: - `task_execution_started_total`, `task_ack_failed_total`, and - `active_workers`. -- Legacy `conductor_worker_worker_restart_total` is not emitted in canonical - mode (Node.js single-process model). -- Legacy uses `payload_type`; canonical uses `payloadType`. -- Legacy HTTP metrics use a compound `endpoint` label; canonical uses separate - `method`, `uri`, and `status` labels. -- Canonical and legacy collectors are mutually exclusive. During a migration, - compare scrape output by running separate worker instances or environments - with and without `WORKER_CANONICAL_METRICS=true`. - -Legacy-to-canonical replacements: - -| Legacy metric | Canonical replacement | -|---|---| -| `conductor_worker_task_poll_total{task_type}` | `task_poll_total{taskType}` | -| `conductor_worker_task_poll_error_total{task_type}` | `task_poll_error_total{taskType,exception}` | -| `conductor_worker_task_execute_total{task_type}` | `task_execute_time_seconds{taskType,status}` (count from histogram) and `task_execution_started_total{taskType}` | -| `conductor_worker_task_execute_error_total{task_type}` | `task_execute_error_total{taskType,exception}` | -| `conductor_worker_task_update_error_total{task_type}` | `task_update_error_total{taskType,exception}` | -| `conductor_worker_task_poll_time{task_type}` (summary, ms) | `task_poll_time_seconds{taskType,status}` (histogram, seconds) | -| `conductor_worker_task_execute_time{task_type}` (summary, ms) | `task_execute_time_seconds{taskType,status}` (histogram, seconds) | -| `conductor_worker_task_update_time{task_type}` (summary, ms) | `task_update_time_seconds{taskType,status}` (histogram, seconds) | -| `conductor_worker_task_result_size_bytes{task_type}` (summary) | `task_result_size_bytes{taskType}` (histogram) | -| `conductor_worker_workflow_input_size_bytes{workflow_type}` (summary) | `workflow_input_size_bytes{workflowType,version}` (histogram) | -| `conductor_worker_http_api_client_request{endpoint}` (summary, ms) | `http_api_client_request_seconds{method,uri,status}` (histogram, seconds) | -| `conductor_worker_external_payload_used_total{payload_type}` | `external_payload_used_total{entityName,operation,payloadType}` | -| `conductor_worker_workflow_start_error_total` (no labels) | `workflow_start_error_total{workflowType,exception}` | -| `conductor_worker_worker_restart_total` | — (not emitted in canonical mode) | - -Common PromQL replacements: - -| Legacy | Canonical | -|---|---| -| `conductor_worker_task_execute_time{quantile="0.95"}` | `histogram_quantile(0.95, sum by (le, taskType, status) (rate(task_execute_time_seconds_bucket[5m])))` | -| `conductor_worker_task_poll_time{quantile="0.95"}` | `histogram_quantile(0.95, sum by (le, taskType, status) (rate(task_poll_time_seconds_bucket[5m])))` | -| `conductor_worker_http_api_client_request{quantile="0.95"}` | `histogram_quantile(0.95, sum by (le, method, uri, status) (rate(http_api_client_request_seconds_bucket[5m])))` | -| `conductor_worker_task_result_size_bytes{quantile="0.95"}` | `histogram_quantile(0.95, sum by (le, taskType) (rate(task_result_size_bytes_bucket[5m])))` | - -Average latency queries continue to use `_sum` divided by `_count`, but the -canonical series are cumulative histogram counters: - -```promql -sum(rate(task_execute_time_seconds_sum[5m])) by (taskType) -/ -sum(rate(task_execute_time_seconds_count[5m])) by (taskType) -``` - -## Troubleshooting - -### Metrics Are Empty - -- Verify that `createMetricsCollector()` or a collector constructor is called - and the collector is passed to `TaskHandler` via `eventListeners`. -- Verify workers have polled or executed tasks. Metrics are created lazily when - the relevant event occurs. -- Confirm the scrape endpoint is reachable at the expected host and port. - -### Missing HTTP or Workflow Metrics - -- `http_api_client_request_seconds` (canonical) or - `conductor_worker_http_api_client_request` (legacy) is recorded by the - `wrapFetchWithRetry` wrapper. Verify the collector is constructed before HTTP - calls begin. -- `workflow_input_size_bytes` and `workflow_start_error_total` are recorded in - `WorkflowExecutor`. Verify the collector is active before starting workflows. - -### High Cardinality - -- In canonical mode the `uri` label on `http_api_client_request_seconds` uses - path templates (e.g. `/workflow/{workflowId}`), giving bounded cardinality. - Legacy mode's `endpoint` label still contains fully interpolated paths. -- Prefer canonical mode for bounded `exception` labels. Legacy error counters - encode exception names in the Map key, not as a proper Prometheus label. -- Avoid embedding user identifiers or unbounded values in task type, workflow - type, or external payload labels. - -### Recording Uncaught Exceptions - -The `thread_uncaught_exceptions_total` metric is not wired automatically. In -Node.js, registering a `process.on("uncaughtException")` handler overrides the -default crash behavior, which can leave the process running in a corrupted -state. Instead, wire it yourself so you control the exit policy: - -```typescript -const metrics = createMetricsCollector(); - -process.on("uncaughtException", (err) => { - metrics.recordUncaughtException(err.name || "Error"); - console.error(err); - process.exit(1); -}); - -process.on("unhandledRejection", (reason) => { - const name = reason instanceof Error ? reason.name || "Error" : "Error"; - metrics.recordUncaughtException(name); - console.error(reason); - process.exit(1); -}); -``` - -### prom-client Issues - -- `MetricsCollector` uses `await import("./MetricsServer.js")` internally. The - `.js` extension does not resolve under Jest's TypeScript transform. Test - `MetricsServer` by importing it directly, not via the `httpPort` config - option. -- When `usePromClient: true` is set but `prom-client` is not installed, the - collector falls back to the built-in text format silently. - -## Implementation Notes - -### One Collector Per Process - -Only one metrics collector can be active at a time. -`CanonicalMetricsCollector` registers itself as the global HTTP metrics -observer on construction. `LegacyMetricsCollector` does not self-register; -use `createMetricsCollector()` or call `setHttpMetricsObserver()` explicitly -to enable HTTP metrics in legacy mode. Creating a second collector replaces -the first. Calling `stop()` clears the global observer. - -### Payload Size Measurement - -`workflow_input_size_bytes` is recorded by calling `JSON.stringify` on the -workflow input and measuring the resulting UTF-8 byte length with -`Buffer.byteLength`. This cost is controlled by the `measurePayloadSize` -config option: - -- **Canonical** (default `true`): measured on every `startWorkflow` call. - Set `measurePayloadSize: false` to disable. -- **Legacy** (default `false`): not measured unless you set - `measurePayloadSize: true`. - -### Legacy `task_paused_total` - -Legacy mode does not emit `task_paused_total`. This metric was defined but -never recorded in any prior release. It remains unrecorded in legacy mode to -preserve byte-for-byte output compatibility. Switch to canonical mode -(`WORKER_CANONICAL_METRICS=true`) to get paused metrics. - -### `WORKER_LEGACY_METRICS` (Reserved) - -`WORKER_LEGACY_METRICS` is reserved for future use. Once canonical metrics -become the default, setting `WORKER_LEGACY_METRICS=true` will re-activate -the legacy metric surface. It is not read by the current implementation. - -## Detailed Technical Notes -- Unreleased - -This section documents internal implementation details for developers reviewing -the metrics harmonization changes. It is not end-user-facing and will be -removed or folded into the relevant sections once the release is published. - -### Architecture - -The `MetricsCollectorInterface` (`src/sdk/worker/metrics/MetricsCollectorInterface.ts`) -defines the contract for recording SDK metrics. Two implementations exist: - -- `LegacyMetricsCollector` (`src/sdk/worker/metrics/LegacyMetricsCollector.ts`) - -- emits the original metric names and types (sliding-window Summaries for - timing, compound `endpoint` labels, `conductor_worker_` prefix). -- `CanonicalMetricsCollector` (`src/sdk/worker/metrics/CanonicalMetricsCollector.ts`) - -- emits the harmonized cross-SDK catalog (Histograms in seconds/bytes, - `_total` counters, bounded `exception` labels from `error.name`). - -`createMetricsCollector()` reads `WORKER_CANONICAL_METRICS` once and returns the -appropriate implementation. - -### HTTP request timing via interceptors - -A **request interceptor** (`createMetricsInterceptors()` in -`src/sdk/createConductorClient/helpers/metricsInterceptors.ts`) stashes the -OpenAPI path template (from `opts.url`) in a `WeakMap` keyed on the `Request` -object. The template is stripped of the `/api/` prefix baked in by the OpenAPI -spec so the canonical `uri` label matches the cross-SDK convention (e.g. -`/workflow/{workflowId}`). - -`wrapFetchWithRetry` reads the template from the `WeakMap` before calling -`retryFetch` and records HTTP metrics for both successful responses and -network errors (status `"0"`). All timing and metric recording is centralised -in the fetch wrapper; the interceptor's only job is to bridge the path -template from `opts` (available to interceptors) to the fetch layer (which -only receives the `Request` object). - -### Canonical vs. legacy URI label - -`recordApiRequestTime` accepts an optional `metricUri` parameter (the path -template). The canonical collector uses `metricUri ?? uri`; the legacy collector -ignores `metricUri` and uses the interpolated `uri`, preserving legacy output -byte-for-byte. - -The JavaScript SDK's OpenAPI spec bakes `/api/` into every path template -(`opts.url = "/api/workflow/{workflowId}"`). Other SDKs keep `/api` in the base -URL and use clean paths. The request interceptor strips this prefix so the -canonical `uri` label matches the cross-SDK convention (e.g. -`/workflow/{workflowId}`). - -### Payload size metrics - -`workflow_input_size_bytes` and `task_result_size_bytes` measure specific -sub-fields of the request/response payloads (`workflowRequest.input` and -`merged.outputData` respectively), not the full HTTP body. This requires a -separate `JSON.stringify` call on the sub-field, independent of the HTTP body -serialization performed by the OpenAPI client. - -This cost is controlled by the `measurePayloadSize` config option (canonical -default `true`, legacy default `false`). The Go SDK provides -`WORKER_METRICS_PAYLOAD_SIZE` for the same purpose. - -### Metrics collector event surface - -`CanonicalMetricsCollector` and `LegacyMetricsCollector` both implement -`TaskRunnerEventsListener`, so they receive lifecycle events from the worker -infrastructure: - -- Event listener methods (from `TaskRunnerEventsListener`): `onPollStarted` / - `onPollCompleted` / `onPollFailure` / `onTaskExecutionStarted` / - `onTaskExecutionCompleted` / `onTaskExecutionFailure` / - `onTaskUpdateCompleted` / `onTaskUpdateFailure` / `onTaskPaused` -- Direct recording methods (called outside the event system): - `recordTaskExecutionQueueFull` / `recordUncaughtException` / - `recordTaskAckError` / `recordTaskAckFailed` / `recordTaskPaused` / - `recordExternalPayloadUsed` / `recordWorkflowStartError` / - `recordWorkflowInputSize` / `recordApiRequestTime` -- `Poller`, `TaskRunner`, and `EventDispatcher` emit a `taskPaused` event when a - poll cycle is skipped because the worker is paused. - -### Harness changes - -- Harness deployment manifest sets `WORKER_CANONICAL_METRICS=true`. -- `harness/main.ts` logs which collector is active. -- `WorkflowStatusProbe` (opt-in via `HARNESS_PROBE_RATE_PER_SEC`) exercises - UUID-bearing endpoints (`/api/workflow/{workflowId}`) to validate that the - `uri` label uses bounded templates. +This reference has moved to [docs/observability.md](docs/observability.md), which +carries the full canonical and legacy metric catalogs, labels, configuration, +migration guidance, and implementation notes. + +- [Observability — metrics, health checks, logging](docs/observability.md) +- [Metric reference — canonical and legacy catalogs](docs/observability.md#metric-reference) +- [Implementation notes](docs/observability.md#implementation-notes) +- [Reliability — lease extension and retries](docs/reliability.md) +- [Documentation index](docs/README.md) diff --git a/README.md b/README.md index 8650a82f..cc985e79 100644 --- a/README.md +++ b/README.md @@ -406,7 +406,7 @@ await handler.startWorkers(); // GET http://localhost:9090/health — {"status":"UP"} ``` -The SDK has two metric surfaces: **legacy** (default, prefixed `conductor_worker_` names, Summary type) and **canonical** (opt-in via `WORKER_CANONICAL_METRICS=true`, unprefixed names, Histogram type). See [METRICS.md](METRICS.md) for the full reference. +The SDK has two metric surfaces: **legacy** (default, prefixed `conductor_worker_` names, Summary type) and **canonical** (opt-in via `WORKER_CANONICAL_METRICS=true`, unprefixed names, Histogram type). See [docs/observability.md](docs/observability.md) for the full reference. ## Managing Workflow Executions @@ -556,7 +556,7 @@ const agent = new Agent({ instructions: "You are a friendly assistant. Keep responses brief.", }); -const runtime = new AgentRuntime(); // AGENTSPAN_SERVER_URL, default http://localhost:8080/api +const runtime = new AgentRuntime(); // CONDUCTOR_AGENT_SERVER_URL, default http://localhost:8080/api try { const result = await runtime.run(agent, "Hello! What can you do?"); result.printResult(); @@ -584,23 +584,28 @@ testing toolkit (`/agents/testing`). | Document | Description | |----------|-------------| -| [SDK Development Guide](SDK_DEVELOPMENT.md) | Architecture, patterns, pitfalls, testing | +**Start at [docs/README.md](docs/README.md)** — the full documentation index. + +| Document | Description | +|----------|-------------| +| [Documentation index](docs/README.md) | Everything below, organized by goal | +| [Core quickstart](docs/core-quickstart.md) | First workflow, first worker, first execution | +| [Connection & authentication](docs/connection-authentication.md) | Env vars, key/secret auth, TLS, proxies, precedence | +| [Workflows](docs/workflows.md) | `ConductorWorkflow` DSL, task builders, control flow | +| [Workflow lifecycle](docs/workflow-lifecycle.md) | Start, pause, resume, terminate, retry, search, signal | +| [Workers](docs/workers.md) | `@worker`, `TaskHandler`, concurrency, failure semantics | +| [Reliability](docs/reliability.md) | Lease extension, retries, idempotency | +| [Observability](docs/observability.md) | Legacy and canonical Prometheus metrics with migration guide | +| [Schedules & events](docs/schedules-events.md) | CRON schedules, event handlers | +| [Schemas](docs/schema-client.md) | JSON schema registration and enforcement | +| [Security](docs/security.md) | Credentials, secrets, untrusted input, code execution | +| [Debugging](docs/debugging.md) | Stuck workflows, failed tasks, silent workers | +| [Deployment & scaling](docs/deployment-scaling.md) | Concurrency, domains, containers | +| [API map](docs/api-map.md) | Which of the 16 clients owns which operation | | [Durable AI Agents](docs/agents/README.md) | Agent authoring layer: tools, guardrails, multi-agent, HITL, framework wrappers | -| [Metrics Reference](METRICS.md) | Legacy and canonical Prometheus metrics with migration guide | -| [Breaking Changes](BREAKING_CHANGES.md) | v3.x migration guide | -| [Workflow Management](docs/api-reference/workflow-executor.md) | Start, pause, resume, terminate, retry, search, signal | -| [Task Management](docs/api-reference/task-client.md) | Task operations, logs, queue management | -| [Metadata](docs/api-reference/metadata-client.md) | Task & workflow definitions, tags, rate limits | -| [Scheduling](docs/api-reference/scheduler-client.md) | Workflow scheduling with CRON expressions | -| [Authorization](docs/api-reference/authorization-client.md) | Users, groups, permissions | -| [Applications](docs/api-reference/application-client.md) | Application management, access keys, roles | -| [Events](docs/api-reference/event-client.md) | Event handlers, event-driven workflows | -| [Human Tasks](docs/api-reference/human-executor.md) | Human-in-the-loop workflows, form templates | -| [Service Registry](docs/api-reference/service-registry-client.md) | Service discovery, circuit breakers | -| [Secrets](docs/api-reference/secret-client.md) | Secret storage and management | -| [Prompts](docs/api-reference/prompt-client.md) | AI/LLM prompt templates | -| [Integrations](docs/api-reference/integration-client.md) | AI/LLM provider integrations | -| [Schemas](docs/api-reference/schema-client.md) | JSON/Avro/Protobuf schema management | +| [SDK Development Guide](SDK_DEVELOPMENT.md) | Architecture, patterns, pitfalls, testing | +| [Upgrading](docs/upgrading.md) | Migration guide, including the Agentspan → Conductor rename | +| [Breaking Changes](BREAKING_CHANGES.md) | Per-change impact tables | ## Support @@ -619,7 +624,7 @@ Yes. Conductor OSS is the continuation of the original [Netflix Conductor](https Yes. [Orkes](https://orkes.io) is the primary maintainer and offers an enterprise SaaS platform for Conductor across all major cloud providers. -**Can Conductor scale to handle my workload?** +**Ca Conductor scale to handle my workload?** Conductor was built at Netflix to handle massive scale and has been battle-tested in production environments processing millions of workflows. It scales horizontally to meet virtually any demand. diff --git a/SDK_COMPARISON.md b/SDK_COMPARISON.md index a2fdbfd6..1e622329 100644 --- a/SDK_COMPARISON.md +++ b/SDK_COMPARISON.md @@ -580,7 +580,7 @@ Features: #### Metrics / Observability -Both SDKs support legacy and canonical metric surfaces via `WORKER_CANONICAL_METRICS`. See [METRICS.md](METRICS.md) for the JavaScript SDK catalog. +Both SDKs support legacy and canonical metric surfaces via `WORKER_CANONICAL_METRICS`. See [docs/observability.md](docs/observability.md) for the JavaScript SDK catalog. | # | Feature | Python | JavaScript | Status | |---|---------|--------|-----------|--------| diff --git a/SDK_DEVELOPMENT.md b/SDK_DEVELOPMENT.md index 5322deb1..8a57901a 100644 --- a/SDK_DEVELOPMENT.md +++ b/SDK_DEVELOPMENT.md @@ -274,7 +274,7 @@ Returns `undefined` outside task execution. All 16 methods: `getTaskId()`, `getW ### 7. Metrics -`createMetricsCollector()` reads `WORKER_CANONICAL_METRICS` and returns a legacy or canonical collector. See [METRICS.md](METRICS.md) for the full catalog and migration guide. +`createMetricsCollector()` reads `WORKER_CANONICAL_METRICS` and returns a legacy or canonical collector. See [docs/observability.md](docs/observability.md) for the full catalog and migration guide. ```typescript const metrics = createMetricsCollector(); @@ -620,7 +620,7 @@ examples/ - **Self-contained**: Every file imports from `../src/sdk`, connects via `OrkesClients.from()`, and is runnable with `npx ts-node examples/.ts` - **Cleanup**: Examples that create resources clean up after themselves in try/finally blocks - **Env vars**: All examples read `CONDUCTOR_SERVER_URL` (required) and optionally `CONDUCTOR_AUTH_KEY`/`CONDUCTOR_AUTH_SECRET` -- **Agent examples**: `examples/agents/` uses `AGENTSPAN_SERVER_URL`/`AGENTSPAN_LLM_MODEL` plus a provider API key — see [examples/agents/README.md](examples/agents/README.md) +- **Agent examples**: `examples/agents/` uses `CONDUCTOR_AGENT_SERVER_URL`/`CONDUCTOR_AGENT_LLM_MODEL` plus a provider API key — see [examples/agents/README.md](examples/agents/README.md) - **Naming**: Kebab-case file names matching Python SDK's snake_case pattern (e.g., `fork-join.ts` ↔ `fork_join_script.py`) - **Imports**: Use relative paths from the example file (e.g., `from "../../src/sdk"` for subdirectory examples) @@ -688,7 +688,7 @@ Node 18+ required. Dual ESM/CJS via `tsup` with `exports` field in `package.json | Workflow DSL | `ConductorWorkflow` | `ConductorWorkflow` | Fluent builder | | Worker decorator | `@worker_task` | `@worker` | TS decorator syntax | | Task context | `get_task_context()` | `getTaskContext()` | AsyncLocalStorage vs contextvars | -| Metrics | `MetricsCollector` | `createMetricsCollector()` | Legacy + canonical modes, see [METRICS.md](METRICS.md) | +| Metrics | `MetricsCollector` | `createMetricsCollector()` | Legacy + canonical modes, see [docs/observability.md](docs/observability.md) | | Metrics server | HTTP endpoint | `MetricsServer` | `/metrics` + `/health` | | Non-retryable | `NonRetryableError` | `NonRetryableException` | `FAILED_WITH_TERMINAL_ERROR` | | LLM builders | 13 builders | 13 builders | Full parity | diff --git a/SDK_NEW_LANGUAGE_GUIDE.md b/SDK_NEW_LANGUAGE_GUIDE.md index dd2dc2db..bae69ce4 100644 --- a/SDK_NEW_LANGUAGE_GUIDE.md +++ b/SDK_NEW_LANGUAGE_GUIDE.md @@ -847,7 +847,7 @@ Implements `TaskRunnerEventsListener`. Records 18 metric types. **File output:** When writing metrics to a file periodically, perform an immediate first write on initialization (before the first interval fires), then use a periodic timer for subsequent writes. This avoids a startup delay where the metrics file is empty. -**Documentation:** Each SDK should maintain a `METRICS.md` file documenting all metrics with their Prometheus names, types, labels, and descriptions. See the TypeScript SDK's [METRICS.md](./METRICS.md) for the reference format. +**Documentation:** Each SDK should maintain a `METRICS.md` file documenting all metrics with their Prometheus names, types, labels, and descriptions. See the TypeScript SDK's [docs/observability.md](./docs/observability.md) for the reference format. #### 5.7 Worker Configuration via Environment Variables @@ -1604,7 +1604,7 @@ afterAll: | Document | Purpose | |----------|---------| | `README.md` | Quick start, installation, basic usage examples | -| `METRICS.md` | All Prometheus metrics with names, types, labels, and descriptions | +| `docs/observability.md` | All Prometheus metrics with names, types, labels, and descriptions | | `CHANGELOG.md` | Version history with breaking changes highlighted | | API reference | Auto-generated from source (TypeDoc, Sphinx, Javadoc, etc.) | diff --git a/WORKER_ARCHITECTURE_COMPARISON.md b/WORKER_ARCHITECTURE_COMPARISON.md index 528921ee..143d0ae5 100644 --- a/WORKER_ARCHITECTURE_COMPARISON.md +++ b/WORKER_ARCHITECTURE_COMPARISON.md @@ -545,7 +545,7 @@ handler = TaskHandler( ### JavaScript: Built-in MetricsCollector -The JavaScript SDK provides `createMetricsCollector()` which returns a legacy or canonical collector based on `WORKER_CANONICAL_METRICS`. See [METRICS.md](METRICS.md) for the full catalog. +The JavaScript SDK provides `createMetricsCollector()` which returns a legacy or canonical collector based on `WORKER_CANONICAL_METRICS`. See [docs/observability.md](docs/observability.md) for the full catalog. ```typescript import { createMetricsCollector, MetricsServer, TaskHandler } from "@io-orkes/conductor-javascript"; diff --git a/cli-bin/shared.ts b/cli-bin/shared.ts index 26f51656..359f0bbe 100644 --- a/cli-bin/shared.ts +++ b/cli-bin/shared.ts @@ -39,7 +39,7 @@ function collectFiles(dir: string): string[] { /** * Scan a directory recursively for .ts/.js files and discover all agent-like exports, - * including native AgentSpan agents and framework agents (OpenAI, ADK, + * including native Conductor agents and framework agents (OpenAI, ADK, * LangChain, LangGraph). */ export async function discoverAllAgents(scanPath: string): Promise { diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..158882f6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,55 @@ +# JavaScript SDK documentation + +Build durable workflow workers and Conductor agents with TypeScript or +JavaScript. These guides cover OSS and Orkes; pages call out capabilities that +require Orkes. + +- **Package:** `@io-orkes/conductor-javascript` +- **Runtime:** Node.js >= 18 (CI covers 20, 22, 24) +- **Modules:** ESM and CommonJS — `import` and `require` both work + +## Start here + +| Goal | Guide | Expected result | +|---|---|---| +| Connect to a server | [Server setup](server-setup.md) and [connection/authentication](connection-authentication.md) | The SDK can reach an OSS or Orkes API endpoint. | +| Build a workflow and worker | [Core quickstart](core-quickstart.md) | The hello-world workflow prints its result. | +| Build a Conductor agent | [Agent quickstart](agents/README.md) | An LLM-backed agent completes through Conductor. | +| Find the right client method | [API map](api-map.md) | You know which of the 14 clients owns the call. | +| Run the repository examples | [Examples](examples.md) | A curated example runs end to end. | + +## Core SDK + +| Guide | Covers | +|---|---| +| [server-setup.md](server-setup.md) | Running a server locally, OSS vs Orkes, version support. | +| [connection-authentication.md](connection-authentication.md) | `createConductorClient`, env vars, key/secret auth, TLS, proxies. | +| [core-quickstart.md](core-quickstart.md) | First workflow, first worker, first execution. | +| [workflows.md](workflows.md) | Authoring workflows with the `ConductorWorkflow` DSL and task builders. | +| [workflow-lifecycle.md](workflow-lifecycle.md) | Register, start, pause, resume, terminate, retry, rerun. | +| [workers.md](workers.md) | The worker framework, `@worker`, `TaskHandler`, polling, lease extension. | +| [schedules-events.md](schedules-events.md) | Cron schedules and event handlers. | +| [schema-client.md](schema-client.md) | Schema registration and task-def schema enforcement. | +| [observability.md](observability.md) | Metrics, the Prometheus surface, logging. | +| [reliability.md](reliability.md) | Lease extension, retries, idempotency, failure semantics. | +| [deployment-scaling.md](deployment-scaling.md) | Worker sizing, concurrency, containerization. | +| [security.md](security.md) | Credential handling, secrets, least privilege. | +| [debugging.md](debugging.md) | Diagnosing stuck workflows, failed tasks, worker silence. | +| [workflow-testing.md](workflow-testing.md) | Unit and integration testing strategies. | +| [compatibility.md](compatibility.md) | Server version matrix, OSS vs Orkes feature gates. | +| [upgrading.md](upgrading.md) | Migrating across SDK major versions. | + +## Conductor agents + +Durable, LLM-backed agents. Start at [agents/README.md](agents/README.md). + +| Area | Pages | +|---|---| +| Concepts | [agents](agents/concepts/agents.md), [tools](agents/concepts/tools.md), [multi-agent](agents/concepts/multi-agent.md), [guardrails](agents/concepts/guardrails.md), [termination](agents/concepts/termination.md), [callbacks](agents/concepts/callbacks.md), [streaming & HITL](agents/concepts/streaming-hitl.md), [structured output](agents/concepts/structured-output.md), [scheduling](agents/concepts/scheduling.md), [stateful](agents/concepts/stateful.md), [deploy · serve · run · plan](agents/concepts/deploy-serve-run.md) | +| Frameworks | [OpenAI](agents/frameworks/openai.md), [Google ADK](agents/frameworks/google-adk.md), [LangChain](agents/frameworks/langchain.md), [LangGraph](agents/frameworks/langgraph.md), [Vercel AI SDK](agents/frameworks/vercel-ai.md) | +| Reference | [API map](agents/reference/api.md), [AgentRuntime](agents/reference/runtime.md), [AgentClient](agents/reference/client.md), [agent definition](agents/reference/agent-definition.md), [configuration contract](agents/reference/agent-schema.md) | + +## Meta + +- [documentation-standard.md](documentation-standard.md) — how these pages are written. +- [documentation-parity.md](documentation-parity.md) — how this set maps to the Python and Java SDKs. diff --git a/docs/agents/README.md b/docs/agents/README.md index e7537cb6..f0ec7426 100644 --- a/docs/agents/README.md +++ b/docs/agents/README.md @@ -1,22 +1,33 @@ -# Durable AI Agents — Documentation +# Conductor agents -The agent layer of the Conductor JavaScript SDK — long-running, dynamic plan-execute, and event-driven AI agents. +**Audience:** TypeScript and JavaScript developers authoring durable, +LLM-backed Conductor agents. -- **Package:** `@io-orkes/conductor-javascript` — agent layer imported from the `/agents` subpath +Long-running, dynamic plan-execute, and event-driven agents that survive process +restarts because their state lives on the Conductor server, not in your process. + +- **Package:** `@io-orkes/conductor-javascript` — agent symbols import from the + `/agents` subpath - **Runtime:** Node.js >= 18 -- **Module:** ESM and CommonJS (`import` / `require`) +- **Modules:** ESM and CommonJS + +## Prerequisites -## Contents +Install the SDK, have a reachable Conductor server with the agent runtime +enabled, and configure your LLM provider on that server. Keep provider +credentials out of source and workflow input — see [security.md](../security.md). + +```bash +npm install @io-orkes/conductor-javascript +export CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini +``` -| Doc | Covers | -|---|---| -| [getting-started.md](getting-started.md) | Install, env vars, and a running agent in under 30 seconds. | -| [writing-agents.md](writing-agents.md) | Authoring agents: instructions, tools, multi-agent strategies, handoffs, guardrails, termination, callbacks, streaming, HITL, schedules, agent-from-method, stateful agents. | -| [framework-agents.md](framework-agents.md) | Running agents authored with OpenAI, Google ADK, LangChain, LangGraph, and the Vercel AI SDK. | -| [advanced.md](advanced.md) | Runtime config, the `AgentClient` control plane, the `WorkflowClient`, deploy/serve/run/plan, structured output, credentials, plans / PLAN_EXECUTE, skills. | -| [api-reference.md](api-reference.md) | The public surface, one section per type. | +The server needs the agent runtime: conductor-oss `>= 3.32.0-rc.8`, or +orkes-conductor booted with `agentspan.embedded=true`. LLM provider API keys go +to the **server** process, not your application. -## At a glance +## Quickstart ```ts import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; @@ -36,4 +47,57 @@ try { } ``` -You need a running Agentspan server (default `http://localhost:8080/api`). See [getting-started.md](getting-started.md). +```bash +npx tsx my-agent.ts +``` + +**Expected result:** a formatted summary on stdout with `status=COMPLETED` and +the model's greeting. That is the whole loop — define an `Agent`, create an +`AgentRuntime`, `await runtime.run(...)`, read the `AgentResult`. +`shutdown()` stops local tool-worker polling so the process can exit. + +**Common failure modes:** a connection error means `CONDUCTOR_AGENT_SERVER_URL` +is wrong or the server is down. A completed run with an error in the output +usually means the model isn't configured on the server. A process that hangs on +exit means `shutdown()` wasn't reached — always use `try`/`finally`. + +## Concepts + +| Goal | Guide | Expected result | +|---|---|---| +| Define an agent and its instructions | [agents](concepts/agents.md) | An agent that answers with your system prompt applied. | +| Give an agent tools | [tools](concepts/tools.md) | The model calls your function and uses the result. | +| Coordinate several agents | [multi-agent](concepts/multi-agent.md) | A team runs sequentially, in parallel, or by delegation. | +| Validate input and output | [guardrails](concepts/guardrails.md) | Unsafe content is blocked, retried, or fixed. | +| Stop a loop deliberately | [termination](concepts/termination.md) | A multi-turn run ends on your condition. | +| Observe the lifecycle | [callbacks](concepts/callbacks.md) | Lifecycle hooks fire as the agent progresses. | +| Stream tokens, pause for a human | [streaming & HITL](concepts/streaming-hitl.md) | Events arrive incrementally; approvals gate tools. | +| Get typed data back | [structured output](concepts/structured-output.md) | `result.output.result` conforms to your schema. | +| Run on a cron | [scheduling](concepts/scheduling.md) | The agent runs unattended on a schedule. | +| Share state across tool calls | [stateful](concepts/stateful.md) | Tools in one run see each other's mutations. | +| Ship it | [deploy · serve · run · plan](concepts/deploy-serve-run.md) | Registration and execution are separated correctly. | + +## Frameworks + +Already have an agent written for another framework? Pass it to the same +`runtime.run(...)` — the runtime detects and serializes it. + +[OpenAI Agents SDK](frameworks/openai.md) · +[Google ADK](frameworks/google-adk.md) · +[LangChain](frameworks/langchain.md) · +[LangGraph](frameworks/langgraph.md) · +[Vercel AI SDK](frameworks/vercel-ai.md) + +## Reference + +[API map](reference/api.md) · +[AgentRuntime](reference/runtime.md) · +[AgentClient](reference/client.md) · +[Agent definition fields](reference/agent-definition.md) · +[Agent configuration contract](reference/agent-schema.md) + +## Next steps + +Read [concepts/agents.md](concepts/agents.md), then +[concepts/tools.md](concepts/tools.md). For the non-agent SDK — workflows, +workers, schedules — start at [../README.md](../README.md). diff --git a/docs/agents/advanced.md b/docs/agents/advanced.md index 1b360b02..ffc4cb6a 100644 --- a/docs/agents/advanced.md +++ b/docs/agents/advanced.md @@ -1,275 +1,11 @@ -# Advanced +# Advanced Conductor-agent usage -Runtime configuration, the control-plane and workflow clients, the deploy/serve/run/plan lifecycle, structured output, credentials, plans (PLAN_EXECUTE), and skills. +This page has been split: -## Runtime configuration - -`new AgentRuntime(configuration?, settings?)` takes two independent, optional arguments: - -- `configuration` — connection/auth, the same shape every other Conductor client takes (`OrkesApiConfig`, or a pre-built `ConductorClient` from `createConductorClient()`/`OrkesClients` to share one client — and one token mint — across control-plane and worker-plane calls). Falls back to `CONDUCTOR_SERVER_URL`/`CONDUCTOR_AUTH_KEY`/`CONDUCTOR_AUTH_SECRET`, then `AGENTSPAN_SERVER_URL`/`AGENTSPAN_AUTH_KEY`/`AGENTSPAN_AUTH_SECRET` (agent-layer fallback), then `http://localhost:8080`. -- `settings` — `AgentConfigOptions`, purely behavioral (no connection fields — those live on `configuration` now). Every field falls back to an env var, then a default; explicit values take precedence. - -```ts -import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; - -const runtime = new AgentRuntime( - { serverUrl: 'http://localhost:8080/api', keyId: '…', keySecret: '…' }, // connection (OrkesApiConfig) - { - workerPollIntervalMs: 100, // AGENTSPAN_WORKER_POLL_INTERVAL - workerThreadCount: 1, // AGENTSPAN_WORKER_THREADS - streamingEnabled: true, // AGENTSPAN_STREAMING_ENABLED - livenessEnabled: true, // AGENTSPAN_LIVENESS_ENABLED - livenessStallSeconds: 30, // AGENTSPAN_LIVENESS_STALL_SECONDS - livenessCheckIntervalSeconds: 10, // AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS - }, -); - -// Both args are optional -- this reads connection + behavior entirely from env: -const defaultRuntime = new AgentRuntime(); -``` - -Full `AgentConfigOptions`: `workerPollIntervalMs`, `workerThreadCount`, `autoStartWorkers`, `streamingEnabled`, `livenessEnabled`, `livenessStallSeconds`, `livenessCheckIntervalSeconds` (see [Liveness monitoring](writing-agents.md#liveness-monitoring)). - -There is also a module-level singleton API for convenience — `configure(configuration?, settings?)`, `run`, `start`, `stream`, `deploy`, `plan`, `serve`, `shutdown` — that operate on a shared runtime: - -```ts -import { configure, run, shutdown } from '@io-orkes/conductor-javascript/agents'; -configure({ serverUrl: 'http://localhost:8080/api' }); -const result = await run(agent, 'hi'); -await shutdown(); -``` - -## deploy vs serve vs run vs plan - -| Method | What it does | Local workers? | -|---|---|---| -| `runtime.run(agent, prompt, opts?)` | Compile + start + stream + return an `AgentResult`. | Yes — registers and polls local `tool()` workers for the run. | -| `runtime.start(agent, prompt, opts?)` | Same as `run` but returns an `AgentHandle` for async interaction (stream, approve, pause, ...). | Yes. | -| `runtime.stream(agent, prompt, opts?)` | `start` + return its `AgentStream`. | Yes. | -| `runtime.deploy(agent, { schedules? })` | Compile + register the workflow definition on the server. No execution, no workers. CI/CD step. Returns `DeploymentInfo`. | No. | -| `runtime.deploy(...agents)` | Variadic form: compile + register multiple agents in one call, no schedules reconciliation. Returns `DeploymentInfo[]`. | No. | -| `runtime.serve(...agents, { blocking? })` | Deploys the given agents (same registration as `deploy`), registers their local tool workers, and starts polling. Blocks until SIGINT/SIGTERM by default; pass a trailing `{ blocking: false }` to return once deploy + registration + polling have started. With no agents, just (re)starts polling for workers already registered. | Yes (and keeps them alive when blocking). | -| `runtime.plan(agent)` | Compile to a workflow definition and return it, without executing. | No. | -| `runtime.shutdown()` | Stop worker polling. | — | - -`serve()` already deploys, so a standalone `deploy()` call beforehand is optional — only worth doing when you want registration decoupled from worker start-up (e.g. a dedicated CI/CD step). The typical production split: run a `serve` process for the tool workers (it registers on the server for you), and trigger executions via the control plane (`runtime.client.run(...)`) or schedules. - -```ts -// Long-lived worker process -- deploys + registers workers + starts polling -await runtime.serve(myAgent); // blocks - -// Trigger (control plane, no local workers needed for LLM-only / remote-tool agents) -const result = await runtime.client.run(myAgent, 'do the thing'); -``` - -## `AgentClient` — control plane - -`runtime.client` is an [`AgentClient`](api-reference.md#agentclient): the control-plane client for the `/agent/*` HTTP surface. `OrkesAgentClient` is the Conductor/Orkes implementation; obtain one directly via `OrkesClients.getAgentClient()`, or use `runtime.client`, which shares the same underlying Conductor client (and its single token mint) as the runtime's worker plane — no bespoke auth/transport lives behind this interface. - -**Control-plane only:** `AgentClient.run/start` compile + start an agent and poll, but do **not** register or poll local tool workers. Use it for LLM-only agents, agents whose tools are remote (HTTP/MCP), or pre-deployed workflows. For agents with local `tool()` functions, use `runtime.run()` instead. - -```ts -const client = runtime.client; // or: orkesClients.getAgentClient() - -// Compile + start + poll to result -const result = await client.run(agent, 'summarize this', { timeoutSeconds: 120 }); - -// Start and interact via a ClientHandle -const handle = await client.start(agent, 'do work'); -const status = await handle.getStatus(); -const final = await handle.wait(); // deadline: timeoutSeconds + 30s, or 10 min default -await handle.approve(); // / reject(reason) / send(message) / respond(body) -await handle.stop(); // stop the execution - -// Compile + register one or more agents (no execution) -const infos = await client.deploy(agentA, agentB); // DeploymentInfo[] - -// Deploy + reconcile cron schedules in one call -import { Schedule } from '@io-orkes/conductor-javascript/agents'; -await client.schedule(agent, [new Schedule({ name: 'nightly', cron: '0 0 0 * * *' })]); -``` - -Low-level endpoints are available too: `startAgent`, `deployAgent`, `compile`, `status`, `respond`, `getExecution`, `stream`. The `client.schedules` accessor is the SDK `SchedulerClient` (shared with `OrkesClients.getSchedulerClient()`); `client.workflows` is a `WorkflowClient` (below). - -## `WorkflowClient` — execution reads - -`runtime.workflows` (also `runtime.client.workflows`) is a read-only [`WorkflowClient`](api-reference.md#workflowclient) over the underlying Conductor workflow API. - -```ts -const wf = await runtime.workflows.getWorkflow(executionId); // full execution (with tasks) -const status = await runtime.workflows.getStatus(executionId); // 'RUNNING' | 'COMPLETED' | ... -const usage = await runtime.workflows.extractTokenUsage(executionId);// aggregated across sub-workflows -// usage -> { promptTokens, completionTokens, totalTokens } | null -``` - -`extractTokenUsage` walks the execution tree (recursing into `SUB_WORKFLOW` tasks) and sums token usage — useful for multi-agent runs where tokens are spread across sub-workflows. Note: `result.tokenUsage` is already populated for you on a normal `run()`; this is for inspecting an execution by id after the fact. - -## Structured output - -Set `outputType` to a JSON Schema object (or a Zod schema — it is converted to JSON Schema). The model returns data conforming to the schema; the structured object lands under `result.output.result`. - -```ts -const ArticleAnalysis = { - type: 'object', - properties: { - title: { type: 'string' }, - category: { type: 'string', enum: ['tech', 'business', 'science'] }, - sentiment: { type: 'string', enum: ['positive', 'neutral', 'negative'] }, - keyTopics: { type: 'array', items: { type: 'string' } }, - }, - required: ['title', 'category', 'sentiment', 'keyTopics'], -}; - -const analyzer = new Agent({ - name: 'analyzer', - model: 'openai/gpt-4o', - instructions: 'Analyze the article and return structured data.', - outputType: ArticleAnalysis, -}); - -const result = await runtime.run(analyzer, 'Analyze: "Quantum Error Correction Hits 99.9% Fidelity"'); -const structured = result.output['result'] as Record; -console.log(structured.category, structured.sentiment); -``` - -## Credentials and secrets - -Pass credential names with `credentials: [...]` at the agent level and/or per tool. The server resolves secrets when it polls each task and delivers them **wire-only**, on that task's `runtimeMetadata` — never persisted, never fetched by the worker separately. The SDK injects them into the worker's `process.env` for the duration of the call (mutate-invoke-restore, serialized so concurrent calls don't clobber each other's env). For HTTP/MCP tools, reference them inline in headers with `${NAME}` substitution. - -**Fail-closed, no fallback:** if a tool declares `credentials: [...]` and the server didn't deliver one of them on `runtimeMetadata` (e.g. an older server that predates `TaskDef.runtimeMetadata` support — conductor-oss PR #1255 / agentspan server > 0.4.2), the task fails with a non-retryable error naming the missing credential. There is no ambient-env fallback to silently read a locally-set variable instead. - -```ts -import { Agent, tool, httpTool, getCredential } from '@io-orkes/conductor-javascript/agents'; - -// A worker tool: the secret is injected into the worker's process.env for the call -const dbLookup = tool( - async (args: { query: string }) => { - const key = process.env.DB_API_KEY ?? ''; - return { ok: key !== '' }; - }, - { - name: 'db_lookup', - description: 'Look up data.', - inputSchema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] }, - credentials: ['DB_API_KEY'], - }, -); - -// Or fetch a credential explicitly inside a tool -const analytics = tool( - async (args: { topic: string }) => { - const key = await getCredential('ANALYTICS_KEY'); - return { topic: args.topic, ok: !!key }; - }, - { name: 'analytics', description: 'Query analytics.', inputSchema: { - type: 'object', properties: { topic: { type: 'string' } }, required: ['topic'], - }, credentials: ['ANALYTICS_KEY'] }, -); - -// HTTP tool with ${CRED} header substitution -const searchApi = httpTool({ - name: 'search_api', - description: 'Search.', - url: 'https://api.example.com/search', - headers: { Authorization: 'Bearer ${SEARCH_API_KEY}' }, - credentials: ['SEARCH_API_KEY'], -}); - -const agent = new Agent({ - name: 'credentialed_agent', - model: 'anthropic/claude-sonnet-4-6', - instructions: '…', - tools: [dbLookup, analytics, searchApi], - credentials: ['DB_API_KEY', 'ANALYTICS_KEY', 'SEARCH_API_KEY'], -}); -``` - -You can also pass `credentials` at call time: `runtime.run(agent, prompt, { credentials: ['X'] })`. - -## RunSettings — per-run LLM overrides - -`RunOptions.runSettings` overrides the LLM call for a single `run`/`start`/`stream`, without touching the agent's own config. Only set fields override; unset fields keep the agent's own serialized values, and the override doesn't cascade to sub-agents (each keeps its own settings). - -```ts -import type { RunSettings } from '@io-orkes/conductor-javascript/agents'; - -const result = await runtime.run(agent, prompt, { - runSettings: { - model: 'anthropic/claude-sonnet-4-6', // overrides agent.model for this run - temperature: 0.2, - maxTokens: 4096, - reasoningEffort: 'high', - thinkingBudgetTokens: 8000, // maps to the wire thinkingConfig shape - }, -}); - -// RunOptions.model is sugar for runSettings.model -- an explicit runSettings.model wins: -await runtime.run(agent, prompt, { model: 'openai/gpt-4o-mini' }); -``` - -## Plans / PLAN_EXECUTE - -`strategy: 'plan_execute'` runs a planner sub-agent to produce a JSON plan, then executes it deterministically as a sub-workflow. You **must** provide a `planner` agent (and may provide a `fallback`): - -```ts -const harness = new Agent({ - name: 'plan_harness', - model: 'openai/gpt-4o', - strategy: 'plan_execute', - planner: plannerAgent, // required — produces the JSON plan - fallback: agenticAgent, // optional — runs agentically if the plan can't compile/run - tools: [/* tools the plan steps call */], -}); -const result = await runtime.run(harness, 'Build a release report.'); -``` - -You can also supply a **deterministic static plan** with the typed builders and pass it via `RunOptions.plan` — it wins over the planner's output (the planner still runs, but its output is discarded): - -```ts -import { Plan, Step, Op, Generate, Ref } from '@io-orkes/conductor-javascript/agents'; - -const plan = new Plan({ - steps: [ - new Step('fetch', { operations: [new Op('fetch_data', { args: { source: 'db' } })] }), - new Step('summarize', { - dependsOn: ['fetch'], - operations: [new Op('summarize', { - generate: new Generate({ - instructions: 'Summarize the fetched data.', - outputSchema: '{"type":"object","properties":{"summary":{"type":"string"}}}', - context: new Ref('fetch'), // reference a prior step's output - }), - })], - }), - ], -}); - -const result = await runtime.run(harness, 'Run the pipeline.', { plan }); -``` - -Builders: `Plan({ steps, validation?, onSuccess?, onFailure? })`, `Step(id, { operations?, dependsOn?, parallel? })`, `Op(tool, { args? | generate? })`, `Generate({ instructions, outputSchema, maxTokens?, context? })`, `Validation(tool, { args?, successCondition? })`, `Action(tool, { args? })`, `Ref(stepId)`, `Context({ text? | url?, headers?, required?, maxBytes? })`. - -For planner reference docs, set `plannerContext: [...]` on the agent (strings or `Context` instances; URLs are fetched at runtime, no recompile). - -## Skills - -`skill(path, options?)` loads a `SKILL.md` skill directory as an `Agent`; `loadSkills(dir)` loads every skill subdirectory keyed by name. Skills are framework agents (`_framework: "skill"`) and run via the same `run()` path; they can be wrapped with `agentTool` and used inside other agents. - -```ts -import { skill, loadSkills, agentTool, Agent } from '@io-orkes/conductor-javascript/agents'; - -const reviewer = skill('./skills/code-review', { model: 'openai/gpt-4o' }); -const all = loadSkills('./skills'); // Record - -const orchestrator = new Agent({ - name: 'lead', - model: 'anthropic/claude-sonnet-4-6', - instructions: 'Delegate reviews to the code-review skill.', - tools: [agentTool(reviewer)], -}); -``` - -## See also - -- [api-reference.md](api-reference.md) — the full public surface. -- [writing-agents.md](writing-agents.md) — schedules, HITL, guardrails, callbacks. +- [Deploy · serve · run · plan](concepts/deploy-serve-run.md) +- [AgentRuntime reference](reference/runtime.md) +- [AgentClient control plane](reference/client.md) +- [Structured output](concepts/structured-output.md) +- [Plan-execute and skills](concepts/multi-agent.md) +- [Credentials](concepts/tools.md) +- [Agent configuration contract](reference/agent-schema.md) diff --git a/docs/agents/api-reference.md b/docs/agents/api-reference.md index c9b54f9f..13376321 100644 --- a/docs/agents/api-reference.md +++ b/docs/agents/api-reference.md @@ -1,341 +1,9 @@ -# API Reference +# Conductor-agent API reference -The public surface of `@io-orkes/conductor-javascript/agents`. One section per type. Everything here is exported from the package root unless noted. +The reference is now split for easier navigation: -## AgentRuntime - -Core execution runtime. Manages agent lifecycle and local tool workers. - -```ts -new AgentRuntime(configuration?: OrkesApiConfig | ConductorClient, settings?: AgentConfigOptions) -``` - -`configuration` is connection/auth (the same shape as every other Conductor client, or a pre-built `ConductorClient` to share one client and one token mint across control- and worker-plane calls); `settings` is behavior-only (see [AgentConfig / AgentConfigOptions](#agentconfig--agentconfigoptions)). Both are optional. - -| Member | Signature | Notes | -|---|---|---| -| `config` | `AgentConfig` | Resolved behavior config (readonly). | -| `client` | `AgentClient` | Control-plane client (`/agent/*`) — shares the runtime's underlying Conductor client. | -| `workflows` | `WorkflowClient` | Read-only workflow executions. | -| `run` | `(agent, prompt, options?) => Promise` | Compile + start + stream + return result. Registers local workers. | -| `start` | `(agent, prompt, options?) => Promise` | Async interaction handle. | -| `stream` | `(agent, prompt, options?) => Promise` | Event stream. | -| `deploy` | `(agent, { schedules? }?) => Promise` \| `(...agents) => Promise` | Register workflow def(s) (+ reconcile schedules in the single-agent form). No execution, no workers. | -| `plan` | `(agent) => Promise` | Compile to workflow def without executing. | -| `serve` | `(...agents, options?: ServeOptions) => Promise` | Deploys the given agents, registers workers, starts polling. Blocks until SIGINT/SIGTERM by default; `{ blocking: false }` returns once deploy + registration + polling have started. | -| `getStatus` | `(executionId, signal?) => Promise` | Current execution status. | -| `schedulesClient` | `() => SchedulerClient` | Schedule lifecycle client (the SDK scheduler client). | -| `shutdown` | `() => Promise` | Stop worker polling. | - -`agent` is an `Agent` or a detected framework object. `options?: RunOptions` on `run`/`start`/`stream` includes `runSettings` (see [RunSettings](#runsettings)). Module-level helpers `configure(configuration?, settings?)`, `run`, `start`, `stream`, `deploy`, `plan`, `serve`, `shutdown` operate on a shared singleton runtime. - -## AgentClient - -`AgentClient` is the interface for the `/agent/*` control-plane HTTP surface (11 ops + `close()`); `OrkesAgentClient` is the Conductor/Orkes implementation, which also carries the agent-level convenience methods below (`run`, `start`, `deploy`, `schedule`) and the `workflows`/`schedules` accessors. **Does not run local tool workers.** Every op rides the shared `ConductorClient`'s authenticated call path — no bespoke auth/transport logic lives behind this interface, so it never mints a token independently. - -```ts -new OrkesAgentClient(configuration?: OrkesApiConfig | ConductorClient) -``` - -Obtain one via `runtime.client` (shares the runtime's client) or `OrkesClients.getAgentClient()` (shares whatever `Client` the `OrkesClients` instance was built with). - -| Member | Signature | Notes | -|---|---|---| -| `workflows` | `WorkflowClient` | Read-only workflow client. | -| `schedules` | `SchedulerClient` | Cron lifecycle client (SDK scheduler client over the shared Conductor client). | -| `run` | `(agent, prompt, opts?) => Promise` | Compile + start + poll to result. | -| `start` | `(agent, prompt, opts?) => Promise` | Compile + start; returns a handle. | -| `deploy` | `(agent, { schedules? }?) => Promise` \| `(...agents) => Promise` | Compile + register agent(s) (+ reconcile schedules in the single-agent form). | -| `schedule` | `(agent, schedules) => Promise` | Deploy + reconcile schedules. | -| `startAgent` / `deployAgent` / `compile` | `(payload, signal?) => Promise` | Low-level POST endpoints (spec R1 surface). | -| `status` | `(executionId, signal?) => Promise` | GET status. | -| `getExecution` | `(executionId, signal?) => Promise` | Full execution data (tasks, output, tokens). | -| `listExecutions` | `(params?, signal?) => Promise` | List executions, optionally filtered. | -| `respond` | `(executionId, body, signal?) => Promise` | Complete a pending human task. | -| `stop` | `(executionId, signal?) => Promise` | Stop a running execution. | -| `signal` | `(executionId, message, signal?) => Promise` | Inject persistent context into a running execution. | -| `stream` | `(executionId, lastEventId?, signal?) => Promise` | SSE stream for an execution. | -| `close` | `() => Promise` | Release this client's open `AgentStream`s. | - -### ClientHandle - -Returned by `AgentClient.start`. `{ executionId, getStatus(), wait(pollIntervalMs?), respond(output), approve(output?), reject(reason?), send(message), stop(), stream() }`. `wait()` rejects once its deadline passes (`timeoutSeconds`-derived, or 10 min default) with an `AgentAPIError` naming the last known status. - -## WorkflowClient - -Read-only client for Conductor workflow executions. Available as `runtime.workflows`. - -| Method | Signature | Notes | -|---|---|---| -| `getWorkflow` | `(executionId, includeTasks = true) => Promise` | Full execution (with tasks). | -| `getStatus` | `(executionId) => Promise` | `'RUNNING'` / `'COMPLETED'` / ... or `''`. | -| `extractTokenUsage` | `(executionId) => Promise` | Aggregated across sub-workflows. | - -`WorkflowTokenUsage` = `{ promptTokens, completionTokens, totalTokens }`. - -## AgentConfig / AgentConfigOptions - -Behavior-only — no connection/auth fields (those live on `OrkesApiConfig`, the `AgentRuntime`/`OrkesAgentClient` constructors' first argument). - -```ts -interface AgentConfigOptions { - workerPollIntervalMs?: number; // AGENTSPAN_WORKER_POLL_INTERVAL (100) - workerThreadCount?: number; // AGENTSPAN_WORKER_THREADS (1) - autoStartWorkers?: boolean; // AGENTSPAN_AUTO_START_WORKERS (true) - streamingEnabled?: boolean; // AGENTSPAN_STREAMING_ENABLED (true) - livenessEnabled?: boolean; // AGENTSPAN_LIVENESS_ENABLED (true) - livenessStallSeconds?: number; // AGENTSPAN_LIVENESS_STALL_SECONDS (30) - livenessCheckIntervalSeconds?: number; // AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS (10) -} -``` - -`AgentConfig.fromEnv()` is an exported helper (equivalent to `new AgentConfig()`). See [Liveness monitoring](writing-agents.md#liveness-monitoring) for the liveness fields. - -## Agent / agent() - -```ts -new Agent(options: AgentOptions) -``` - -Key `AgentOptions` fields: - -| Field | Type | Notes | -|---|---|---| -| `name` | `string` | Required. `/^[a-zA-Z][a-zA-Z0-9_-]*$/`. | -| `model` | `string \| ClaudeCode` | e.g. `'anthropic/claude-sonnet-4-6'`. | -| `baseUrl` | `string` | Override LLM provider base URL. | -| `instructions` | `string \| PromptTemplate \| (() => string)` | Static / template / dynamic. | -| `tools` | `unknown[]` | `tool()` wrappers, built-in tool defs, framework tools. | -| `agents` | `Agent[]` | Sub-agents (multi-agent). | -| `strategy` | `Strategy` | `'sequential' \| 'parallel' \| 'handoff' \| 'router' \| 'round_robin' \| 'random' \| 'swarm' \| 'manual' \| 'plan_execute'`. | -| `router` | `Agent \| (() => string)` | Required for `strategy: 'router'`. | -| `outputType` | Zod schema or JSON Schema | Structured output. | -| `guardrails` | `unknown[]` | Guardrail defs / instances. | -| `handoffs` | `HandoffCondition[]` | `OnTextMention` / `OnToolResult` / `OnCondition`. | -| `allowedTransitions` | `Record` | Constrain agent transitions. | -| `termination` | `TerminationCondition` | Stop condition. | -| `gate` | `GateCondition` | `TextGate` / `gate()`. | -| `callbacks` | `CallbackHandler[]` | Lifecycle hooks. | -| `memory` | `ConversationMemory` | Conversation history. | -| `maxTurns` | `number` | Default 25. | -| `maxTokens` / `temperature` / `timeoutSeconds` | `number` | LLM + execution tuning. | -| `credentials` | `string[]` | Secret names to resolve. | -| `stateful` | `boolean` | Per-execution worker isolation + shared state. | -| `planner` / `fallback` | `Agent` | PLAN_EXECUTE named slots. | -| `plannerContext` | `(string \| Context \| object)[]` | PLAN_EXECUTE reference docs. | -| `enablePlanning` | `boolean` | Plan-first preamble. | -| `prefillTools` | `PrefillToolCall[]` | Tools run before the first LLM turn. | -| `cliCommands` / `cliAllowedCommands` / `cliConfig` | — | Enable CLI command execution. | -| `codeExecutionConfig` | `CodeExecutionConfig` | Code execution. | -| `introduction` / `metadata` | — | Agent metadata. | - -Methods: `agent.pipe(other)` builds a sequential pipeline (flattens chains). Getters: `isClaudeCode`, `claudeCodeConfig`. - -Helpers: -- `agent(fn, options)` — functional form; `fn` is the dynamic-instructions callable. -- `scatterGather({ name, workers, model?, instructions?, retryCount?, retryDelaySeconds?, failFast?, timeoutSeconds? })` — coordinator that fans out to worker agents in parallel. -- `AgentDec(options)` + `agentsFrom(instance)` — define agents as decorated class methods. -- `PromptTemplate(name, variables?, version?)` — server-managed prompt reference. - -## tool() and built-in tools - -```ts -tool(fn: (args, ctx?: ToolContext) => Promise, options: ToolOptions): ToolFunction -``` - -`ToolOptions`: `{ name?, description, inputSchema, outputSchema?, approvalRequired?, timeoutSeconds?, external?, credentials?, guardrails?, maxCalls?, retryCount?, retryDelaySeconds?, retryPolicy? }`. `inputSchema`/`outputSchema` accept a Zod schema or a JSON Schema object. - -Built-in tool builders (all return a `ToolDef`): - -| Builder | Required options | toolType | -|---|---|---| -| `httpTool` | `name, description, url` (`method?, headers?, inputSchema?, credentials?`) | `http` | -| `mcpTool` | `serverUrl` (`name?, headers?, toolNames?, maxTools?, credentials?`) | `mcp` | -| `apiTool` | `url` (`name?, headers?, toolNames?, maxTools?, credentials?`) | `api` | -| `agentTool` | `agent` (`name?, description?, retryCount?, retryDelaySeconds?, optional?`) | `agent_tool` | -| `humanTool` | `name, description` (`inputSchema?`) | `human` | -| `imageTool` | `name, description, llmProvider, model` (`style?, size?`) | `generate_image` | -| `audioTool` | `name, description, llmProvider, model` (`voice?, speed?, format?`) | `generate_audio` | -| `videoTool` | `name, description, llmProvider, model` (`duration?, resolution?, fps?, ...`) | `generate_video` | -| `pdfTool` | — (`name?, description?, pageSize?, theme?, fontSize?`) | `generate_pdf` | -| `waitForMessageTool` | `name, description` (`batchSize?` def 1, `blocking?` def true) | `pull_workflow_messages` | -| `searchTool` | `name, description, vectorDb, index, embeddingModelProvider, embeddingModel` (`namespace?, maxResults?, dimensions?`) | `rag_search` | -| `indexTool` | `name, description, vectorDb, index, embeddingModelProvider, embeddingModel` (`namespace?, chunkSize?, chunkOverlap?, dimensions?`) | `rag_index` | - -Discovery / helpers: `Tool(options?)` decorator + `toolsFrom(instance)`; `getToolDef(obj)` / `normalizeToolInput(obj)` (extract a `ToolDef` from a `tool()` wrapper, Vercel AI tool, or raw def); `isZodSchema(obj)`. - -### ToolContext - -Passed as the second arg to a `tool()` function: - -```ts -interface ToolContext { - sessionId: string; - executionId: string; - agentName: string; - metadata: Record; - dependencies: Record; - state: Record; // mutable; mutations propagate between tool calls -} -``` - -## Guardrails - -- `guardrail(fn, { name, position?, onFail?, maxRetries? })` — custom guardrail from a function returning `{ passed, message?, fixedOutput? }`. `guardrail.external({ name, position?, onFail? })` for remote-worker guardrails. -- `new RegexGuardrail({ name, patterns, mode, position?, onFail?, message?, maxRetries? })` — `mode: 'block' | 'allow'`. `.toGuardrailDef()`. -- `new LLMGuardrail({ name, model, policy, position?, onFail?, maxRetries?, maxTokens? })` — server-side LLM judge. `.toGuardrailDef()`. -- `Guardrail(options?)` decorator + `guardrailsFrom(instance)`. - -`position`: `'input' | 'output'` (default `'output'`). `onFail`: `'raise' | 'retry' | 'fix' | 'human'` (default `'raise'`). Attach via `agent.guardrails` or `tool(fn, { guardrails })`. - -## Termination - -All extend `TerminationCondition` and compose via `.and(other)` / `.or(other)` (or variadic `AndCondition(...)` / `OrCondition(...)`). - -| Class | Constructor | -|---|---| -| `TextMention` | `(text, caseSensitive = false)` | -| `StopMessage` | `(stopMessage)` | -| `MaxMessage` | `(maxMessages)` | -| `TokenUsageCondition` | `({ maxTotalTokens?, maxPromptTokens?, maxCompletionTokens? })` | -| `AndCondition` / `OrCondition` | `(...conditions)` | - -## Handoffs - -- `new OnTextMention({ target, text })` — hand off when output mentions `text` (case-insensitive). -- `new OnToolResult({ target, toolName, resultContains? })` — hand off after a tool returns. -- `new OnCondition({ target, condition, agentName? })` — hand off when a predicate (runs as a worker) returns true. -- `new TextGate({ text, caseSensitive? })` — gate on text containment (`gate:` option). -- `gate(fn, { agentName? })` — custom gate from a function. - -`HandoffContext` (passed to conditions): `{ result, toolName?, toolResult?, messages? }`. - -## Callbacks - -Subclass `CallbackHandler` and override hooks (each runs as a server worker): - -```ts -abstract class CallbackHandler { - onAgentStart?(agentName, prompt): Promise; - onAgentEnd?(agentName, result): Promise; - onModelStart?(agentName, messages): Promise; - onModelEnd?(agentName, response): Promise; - onToolStart?(agentName, toolName, args): Promise; - onToolEnd?(agentName, toolName, result): Promise; -} -``` - -`CALLBACK_POSITIONS` maps hook names to wire positions; `getCallbackWorkerNames(agentName, handler)` lists registered worker names. - -## Schedules / SchedulerClient - -```ts -new Schedule({ name, cron, timezone?, input?, catchup?, paused?, startAt?, endAt?, description? }) -``` - -Agent schedules ride the SDK `SchedulerClient` (also available via `OrkesClients.getSchedulerClient()`). Its typed lifecycle methods: `save(schedule, agentName)`, `get(wireName, agentName?)`, `listForAgent(agentName)`, `pause(wireName, reason?)`, `resume(wireName)`, `delete(wireName)`, `runNow(info)`, `previewNext(cron, { n?, startAt?, endAt? })`, `reconcile(agentName, desired)` — plus the endpoint-level wrappers (`saveSchedule`, `getSchedule`, `pauseSchedule`, ...). Pause/resume issue PUT first and fall back to GET on HTTP 405 (per-schedule verbs differ by Conductor server family), so one client works against both OSS/embedded and Orkes servers. - -The `schedules` namespace is a convenience layer over the singleton runtime: `schedules.list({ agent })`, `.get(name, { runtime? })`, `.pause(name, { reason?, runtime? })`, `.resume`, `.delete`, `.runNow`, `.previewNext(cron, { n? })`, `.save(schedule, agent)`. Lifecycle calls key on the **wire name** (the prefixed `name` in `ScheduleInfo`). - -Errors: `ScheduleError`, `ScheduleNameConflict`, `ScheduleNotFound`, `InvalidCronExpression`. `ScheduleInfo` includes `name`, `shortName`, `agent`, `cron`, `timezone`, `paused`, `pausedReason`, `nextRun`, ... - -## AgentResult - -Returned by `run()` / `wait()`. - -```ts -interface AgentResult { - output: Record; // text answer -> { result: "..." } - executionId: string; - correlationId?: string; - messages: unknown[]; - toolCalls: unknown[]; - status: 'COMPLETED' | 'FAILED' | 'TERMINATED' | 'TIMED_OUT'; - finishReason: 'stop' | 'length' | 'tool_calls' | 'error' | 'cancelled' | 'timeout' | 'guardrail' | 'rejected'; - error?: string; - tokenUsage?: { promptTokens; completionTokens; totalTokens }; - metadata?: Record; - events: AgentEvent[]; - subResults?: Record; - readonly isSuccess: boolean; // status === 'COMPLETED' - readonly isFailed: boolean; // FAILED | TIMED_OUT - readonly isRejected: boolean; // finishReason === 'rejected' - printResult(): void; -} -``` - -## AgentHandle - -Returned by `runtime.start()`. - -```ts -interface AgentHandle { - executionId: string; - correlationId: string; - getStatus(): Promise; - wait(pollIntervalMs?): Promise; - respond(output): Promise; - approve(output?): Promise; - reject(reason?): Promise; - send(message): Promise; - pause(): Promise; - resume(): Promise; - cancel(): Promise; - stop(): Promise; - stream(): AgentStream; -} -``` - -`approve()` sends `{ approved: true, ...output }`; `reject(reason)` sends `{ approved: false, reason }`; `send(message)` sends `{ message }`. For a custom human-task response (shaped by `pendingTool.response_schema`), use `respond(body)`. `wait(pollIntervalMs?)` rejects once its deadline passes (`timeoutSeconds`-derived, or 10 min default) with an `AgentAPIError` naming the last known status — and, for a stateful (domain-routed) run with liveness enabled, rejects earlier with `WorkerStallError` if the local worker appears to have died (see [Liveness monitoring](writing-agents.md#liveness-monitoring)). - -## AgentStream / AgentEvent - -`AgentStream` implements `AsyncIterable` — iterate with `for await`. Methods: `respond(output)`, `approve(output?)`, `reject(reason?)`, `send(message)`, and `getResult(): Promise` (drains the stream, polls for the terminal status, returns the result). Fields: `executionId`, `events` (accumulates). - -```ts -interface AgentEvent { - type: 'thinking' | 'tool_call' | 'tool_result' | 'guardrail_pass' | 'guardrail_fail' - | 'waiting' | 'handoff' | 'message' | 'error' | 'done' | string; - content?: string; - toolName?: string; - args?: Record; - result?: unknown; - target?: string; // handoff target - output?: unknown; // on 'done' - pendingTool?: PendingTool;// on 'waiting' - guardrailName?: string; -} -``` - -`AgentStatus`: `{ executionId, isComplete, isRunning, isWaiting, output?, status, reason?, currentTask?, messages, pendingTool? }`. `PendingTool`: `{ taskRefName, toolCalls?: { name, args }[], response_schema?, ... }`. `EventTypes`, `Statuses`, `FinishReasons`, `TERMINAL_STATUSES` enums are exported. - -## Errors - -`AgentspanError` (base), `AgentAPIError`, `AgentNotFoundError`, `ConfigurationError`, `CredentialNotFoundError`, `CredentialAuthError`, `CredentialRateLimitError`, `CredentialServiceError`, `SSETimeoutError`, `TerminalToolError`, `WorkerStallError`, `GuardrailFailedError`. - -## RunSettings - -Per-run LLM overrides, passed as `RunOptions.runSettings` to `run`/`start`/`stream`. See [Advanced: RunSettings](advanced.md#runsettings--per-run-llm-overrides). - -```ts -interface RunSettings { - model?: string; - temperature?: number; - maxTokens?: number; - reasoningEffort?: string; - thinkingBudgetTokens?: number; -} -``` - -## Other exports - -- **Memory:** `ConversationMemory`, `SemanticMemory`, `InMemoryStore`. -- **Plans:** `Plan`, `Step`, `Op`, `Generate`, `Validation`, `Action`, `Ref`, `Context`, `coercePlan`. -- **Skills:** `skill(path, options?)`, `loadSkills(dir, options?)`, `SkillLoadError`. -- **Credentials:** `getCredential`, `runWithCredentialContext`, `setCredentialContext`, `clearCredentialContext`. Values arrive wire-only on the polled task's `runtimeMetadata` (spec R6) — never fetched separately; see [Credentials and secrets](advanced.md#credentials-and-secrets). -- **Liveness:** `LivenessMonitor`, `LivenessMonitorOptions` — see [Liveness monitoring](writing-agents.md#liveness-monitoring). -- **Code execution:** `LocalCodeExecutor`, `DockerCodeExecutor`, `JupyterCodeExecutor`, `ServerlessCodeExecutor`, `CodeExecutor`, `CommandValidator`. -- **Claude Code:** `ClaudeCode(modelName?, permissionMode?)`, `PermissionMode`, `resolveClaudeCodeModel`. -- **Extended agents:** `GPTAssistantAgent({ name, assistantId, model?, instructions? })`. -- **Framework integration:** `detectFramework`, `serializeFrameworkAgent`, `serializeLangGraph`, `serializeLangChain`. -- **Subpath exports:** `@io-orkes/conductor-javascript/agents/vercel-ai`, `@io-orkes/conductor-javascript/agents/langgraph`, `@io-orkes/conductor-javascript/agents/langchain`, `@io-orkes/conductor-javascript/agents/testing`. +- [API map](reference/api.md) +- [AgentRuntime](reference/runtime.md) +- [AgentClient](reference/client.md) +- [Agent definition fields](reference/agent-definition.md) +- [Agent configuration contract](reference/agent-schema.md) diff --git a/docs/agents/concepts/agents.md b/docs/agents/concepts/agents.md new file mode 100644 index 00000000..639f3182 --- /dev/null +++ b/docs/agents/concepts/agents.md @@ -0,0 +1,123 @@ +# Conductor agents + +**Audience:** TypeScript developers defining agents and their instructions. + +Everything you author is an `Agent`. A simple LLM agent, a tool-using agent, and +a multi-agent orchestration are all the same class with different options. + +## Prerequisites + +The SDK installed, a reachable Conductor server with the agent runtime, and a +model configured on that server. See [../README.md](../README.md). + +All snippets assume: + +```ts +import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; +const runtime = new AgentRuntime(); +``` + +## Define an agent + +```ts +const agent = new Agent({ + name: 'greeter', // required; must match /^[a-zA-Z][a-zA-Z0-9_-]*$/ + model: 'anthropic/claude-sonnet-4-6', // provider/model string + instructions: 'Keep answers short.', + temperature: 0.7, + maxTurns: 25, // default 25 + maxTokens: 2048, + timeoutSeconds: 0, // 0 = server default +}); +``` + +**Expected result:** `runtime.run(agent, 'hi')` completes with the model's reply +under `result.output.result`. + +**Common failure mode:** a name that doesn't match the pattern is rejected at +compile time. Names become workflow definition names on the server, so they must +be identifier-shaped. + +There is also a functional form, `agent(fn, options)`, where `fn` is the +dynamic-instructions callable: + +```ts +import { agent } from '@io-orkes/conductor-javascript/agents'; + +const a = agent(() => 'You are a helpful assistant.', { + name: 'helper', + model: 'anthropic/claude-sonnet-4-6', +}); +``` + +## Instructions + +Instructions can be a plain string, a callable, or a server-managed prompt +template. + +```ts +// Static +new Agent({ name: 'a', model, instructions: 'You are concise.' }); + +// Dynamic (callable) — evaluated to a string when the agent is serialized +new Agent({ name: 'a', model, instructions: () => `Today is ${new Date().toDateString()}.` }); + +// Server-managed prompt template (referenced by name + version) +import { PromptTemplate } from '@io-orkes/conductor-javascript/agents'; +new Agent({ + name: 'a', + model, + instructions: new PromptTemplate('support_greeting', { brand: 'Acme' }, 1), +}); +``` + +A callable is evaluated **once, at serialization time** — not per turn. If you +need per-turn values, pass them in the prompt or a tool result instead. + +## Agents from decorated methods + +Define agents as methods on a class and extract them: + +```ts +import { AgentDec, agentsFrom } from '@io-orkes/conductor-javascript/agents'; + +class MyAgents { + @AgentDec({ name: 'summarizer', model: 'anthropic/claude-sonnet-4-6', instructions: 'Summarize text.' }) + summarize() {} + + @AgentDec({ name: 'classifier', model: 'anthropic/claude-sonnet-4-6', instructions: 'Classify text.' }) + classify() {} +} + +const [summarizer, classifier] = agentsFrom(new MyAgents()); // Agent[] +``` + +`@AgentDec` and `@Tool` are TypeScript experimental decorators — set +`"experimentalDecorators": true` in your `tsconfig.json`. + +## Reading the result + +`run()` returns an `AgentResult`: + +```ts +result.printResult(); // formatted summary to stdout +const ok = result.isSuccess; // status === 'COMPLETED' +const output = result.output; // Record; text is usually output.result +const tokens = result.tokenUsage; // { promptTokens, completionTokens, totalTokens } | undefined +const finish = result.finishReason; // 'stop' | 'length' | 'guardrail' | 'rejected' | ... +const execId = result.executionId; // durable execution id on the server +``` + +`output` is always a `Record`. Plain text arrives as `{ result: "..." }`; +[structured output](structured-output.md) arrives under `output.result` as an +object. + +## Cleanup + +Always `await runtime.shutdown()` in a `finally`. It stops worker polling; without +it a process with local tools will not exit. + +## Next steps + +[tools](tools.md) · [multi-agent](multi-agent.md) · +[agent definition reference](../reference/agent-definition.md) diff --git a/docs/agents/concepts/callbacks.md b/docs/agents/concepts/callbacks.md new file mode 100644 index 00000000..88ac9004 --- /dev/null +++ b/docs/agents/concepts/callbacks.md @@ -0,0 +1,50 @@ +# Callbacks + +**Audience:** developers observing the Conductor-agent lifecycle. + +## Prerequisites + +A working agent and a process that polls workers — each callback hook runs as a +server-registered worker, so callbacks require `runtime.run()` or a live +`serve()`, not the control-plane client. + +**Security note:** callbacks receive prompts, tool arguments, and model output. +Anything you log from a hook inherits the sensitivity of that data; don't ship +raw hook payloads to an external sink without redaction. + +## Handlers + +Subclass `CallbackHandler` and override the hooks you care about. + +```ts +import { CallbackHandler } from '@io-orkes/conductor-javascript/agents'; + +class Logger extends CallbackHandler { + async onAgentStart(agentName: string, prompt: string) { console.log('[start]', agentName, prompt); } + async onToolStart(agentName: string, toolName: string, args: unknown) { console.log('[tool]', toolName, args); } + async onAgentEnd(agentName: string, result: unknown) { console.log('[end]', agentName); } +} + +const agent = new Agent({ name: 'a', model, instructions: '…', callbacks: [new Logger()] }); +``` + +Hooks: `onAgentStart`, `onAgentEnd`, `onModelStart`, `onModelEnd`, `onToolStart`, +`onToolEnd`. + +**Expected result:** hook output appears on stdout as the run progresses, before +the final `AgentResult`. + +**Common failure modes:** silent hooks mean no worker is polling. A hook that +throws fails its task — treat hooks as observation, keep them side-effect-light, +and don't let a logging failure take down a run. + +## Callbacks vs streaming + +Callbacks are push-based server-side hooks; streaming is a pull-based event feed +in your process. Use callbacks for durable side effects that should happen +wherever the agent runs; use [streaming](streaming-hitl.md) to drive a UI or CLI +in the calling process. + +## Next steps + +[streaming & HITL](streaming-hitl.md) · [../../observability.md](../../observability.md) diff --git a/docs/agents/concepts/deploy-serve-run.md b/docs/agents/concepts/deploy-serve-run.md new file mode 100644 index 00000000..b1d6fa4f --- /dev/null +++ b/docs/agents/concepts/deploy-serve-run.md @@ -0,0 +1,71 @@ +# Deploy · serve · run · plan + +**Audience:** developers deciding how a Conductor agent reaches production. + +## Prerequisites + +An agent that runs locally. This page is about the split between *registering* a +definition and *executing* it — the distinction that decides your deployment +topology. + +## The four verbs + +| Method | What it does | Local workers? | +|---|---|---| +| `runtime.run(agent, prompt, opts?)` | Compile + start + stream + return an `AgentResult`. | Yes — registers and polls local `tool()` workers for the run. | +| `runtime.start(agent, prompt, opts?)` | Same as `run`, but returns an `AgentHandle` for async interaction. | Yes. | +| `runtime.stream(agent, prompt, opts?)` | `start` + return its `AgentStream`. | Yes. | +| `runtime.deploy(agent, { schedules? })` | Compile + register the workflow definition. No execution, no workers. Returns `DeploymentInfo`. | No. | +| `runtime.deploy(...agents)` | Variadic: register multiple agents, no schedule reconciliation. Returns `DeploymentInfo[]`. | No. | +| `runtime.serve(...agents, { blocking? })` | Deploy the agents, register their local tool workers, start polling. Blocks until SIGINT/SIGTERM by default. | Yes, and keeps them alive when blocking. | +| `runtime.plan(agent)` | Compile to a workflow definition and return it, without executing. | No. | +| `runtime.shutdown()` | Stop worker polling. | — | + +**Expected result:** `deploy` returns a `DeploymentInfo` and the definition is +visible on the server with no execution started. `serve` blocks with workers +polling. + +## Choosing a topology + +`serve()` already deploys, so a standalone `deploy()` beforehand is optional — +worth it only when you want registration decoupled from worker startup, such as a +dedicated CI/CD step. + +The typical production split is a long-lived `serve` process for the tool workers, +with executions triggered through the control plane or on a schedule: + +```ts +// Long-lived worker process — deploys, registers workers, starts polling +await runtime.serve(myAgent); // blocks + +// Trigger from elsewhere (no local workers needed for LLM-only or remote-tool agents) +const result = await runtime.client.run(myAgent, 'do the thing'); +``` + +**Common failure mode — the one that bites hardest:** triggering an agent with +local `tool()` functions via `runtime.client.run()` when no `serve` process is +running. The execution starts, reaches the tool call, and waits forever, because +the control plane does not poll workers. Either use `runtime.run()` (which polls +for the duration of the call) or keep a `serve` process alive. + +Pass `{ blocking: false }` to `serve()` to return once deploy, registration, and +polling have started — useful inside a larger process that has its own lifecycle. +With no agents, `serve()` just restarts polling for already-registered workers. + +## Inspecting the compiled definition + +`runtime.plan(agent)` returns the workflow definition without executing it — +useful in tests and for diffing what a change to an agent actually alters on the +server. + +## Cleanup + +A `serve()` process exits on SIGINT/SIGTERM. In non-blocking mode, call +`runtime.shutdown()` yourself. Deployed definitions and schedules persist on the +server until removed — see [scheduling.md](scheduling.md#cleanup). + +## Next steps + +[../reference/runtime.md](../reference/runtime.md) · +[../reference/client.md](../reference/client.md) · +[../../deployment-scaling.md](../../deployment-scaling.md) diff --git a/docs/agents/concepts/guardrails.md b/docs/agents/concepts/guardrails.md new file mode 100644 index 00000000..f613daf6 --- /dev/null +++ b/docs/agents/concepts/guardrails.md @@ -0,0 +1,90 @@ +# Guardrails + +**Audience:** developers validating what goes into and comes out of a Conductor +agent. + +## Prerequisites + +A working agent. Custom guardrails run as local workers, so they need a polling +process; regex and LLM guardrails run server-side and do not. + +**Security note:** guardrails are a mitigation, not a boundary. A regex that +blocks a secret pattern reduces accidental disclosure; it does not make an agent +safe to hand untrusted input. Pair them with scoped credentials +([tools.md](tools.md#credentials)) and approval gates +([streaming-hitl.md](streaming-hitl.md)). + +## Attaching guardrails + +Attach at the agent level (`guardrails: [...]`) or per tool +(`tool(fn, { guardrails: [...] })`). Each has a `position` (`'input'` or +`'output'`, default `'output'`) and an `onFail` policy (`'raise'`, `'retry'`, +`'fix'`, or `'human'`, default `'raise'`). + +```ts +import { guardrail, RegexGuardrail, LLMGuardrail } from '@io-orkes/conductor-javascript/agents'; + +// Regex — runs on the server, no worker +const noSecrets = new RegexGuardrail({ + name: 'no_api_keys', + patterns: ['sk-[A-Za-z0-9]{20,}'], + mode: 'block', // 'block' fails if any pattern matches; 'allow' fails if none match + onFail: 'raise', + message: 'Output contained a secret.', +}); + +// LLM — server-side judge +const policy = new LLMGuardrail({ + name: 'safety', + model: 'anthropic/claude-sonnet-4-6', + policy: 'Reject any content that gives medical dosage advice.', + position: 'output', + onFail: 'retry', + maxRetries: 2, +}); + +// Custom — your function, runs locally as a worker +const minLength = guardrail( + (content: string) => ({ passed: content.length >= 10, message: 'Too short' }), + { name: 'min_length', position: 'output', onFail: 'fix' }, +); + +const agent = new Agent({ + name: 'safe_agent', + model, + instructions: '…', + guardrails: [noSecrets, policy, minLength], +}); +``` + +`RegexGuardrail` and `LLMGuardrail` are class instances; the serializer accepts +them directly. + +**Expected result:** a run whose output trips a `'raise'` guardrail completes with +`result.finishReason === 'guardrail'` rather than throwing — check +`finishReason`, not just `isSuccess`. + +**Common failure mode:** a custom `guardrail()` never firing usually means no +worker is polling. Like local tools, it needs `runtime.run()` or a live +`serve()`, not the control-plane client. + +## onFail policies + +| Policy | Behavior | +|---|---| +| `'raise'` | Fail the run. `finishReason` becomes `'guardrail'`. | +| `'retry'` | Re-run the model up to `maxRetries`, then raise. | +| `'fix'` | Ask the model to repair the output to satisfy the guardrail. | +| `'human'` | Pause and wait for a human verdict — see [streaming-hitl.md](streaming-hitl.md). | + +## Other forms + +- `guardrail.external({ name, position?, onFail? })` — handled by a remote worker + you run elsewhere. +- The `@Guardrail` decorator with `guardrailsFrom(instance)`, mirroring + `@Tool`/`toolsFrom`. + +## Next steps + +[termination](termination.md) · [tools](tools.md) · +[streaming & HITL](streaming-hitl.md) diff --git a/docs/agents/concepts/multi-agent.md b/docs/agents/concepts/multi-agent.md new file mode 100644 index 00000000..6ed936fb --- /dev/null +++ b/docs/agents/concepts/multi-agent.md @@ -0,0 +1,179 @@ +# Multi-agent orchestration + +**Audience:** developers coordinating several Conductor agents in one execution. + +## Prerequisites + +Two or more agents that work individually (see [agents.md](agents.md)). Each +sub-agent runs as its own sub-workflow on the server, so multi-agent runs are +durable across restarts but spread token usage across sub-workflows — see +[../reference/client.md](../reference/client.md) for aggregating it. + +## Strategies + +Set `agents: [...]` and a `strategy`. Available: `'sequential'`, `'parallel'`, +`'handoff'`, `'router'`, `'round_robin'`, `'random'`, `'swarm'`, `'manual'`, +`'plan_execute'`. + +```ts +// Sequential — agents run in order. .pipe() is sugar for strategy: 'sequential'. +const pipeline = writer.pipe(editor); +// equivalent to: +// new Agent({ name: 'writer_editor', agents: [writer, editor], strategy: 'sequential' }); + +// Parallel — agents run concurrently, results gathered +const team = new Agent({ + name: 'research_team', + agents: [webResearcher, dataAnalyst], + strategy: 'parallel', +}); + +// Handoff — the parent LLM delegates to sub-agents, which appear as callable tools +const support = new Agent({ + name: 'support', + model, + instructions: 'Route to the right specialist.', + agents: [billingAgent, technicalAgent, salesAgent], + strategy: 'handoff', +}); + +// Router — a router agent (or function) picks the sub-agent +const routed = new Agent({ + name: 'router', + agents: [a, b], + strategy: 'router', + router: routerAgent, // an Agent, or (…) => string returning a sub-agent name +}); +``` + +**Expected result:** `result.output.result` holds the orchestration's final +output. For `parallel`, results are gathered rather than reduced — the parent's +instructions decide how they're combined. + +**Common failure mode:** `strategy: 'router'` without a `router`, or +`strategy: 'plan_execute'` without a `planner`, fails at compile time. + +`scatterGather` is a convenience builder returning a coordinator that fans a +problem out to workers in parallel and synthesizes the results: + +```ts +import { scatterGather } from '@io-orkes/conductor-javascript/agents'; +const coordinator = scatterGather({ name: 'fanout', workers: [worker], retryCount: 2 }); +``` + +## Handoffs + +For `swarm` and `handoff` strategies, declare explicit transitions with +`handoffs: [...]`. Each condition has a `target` naming a sub-agent. + +```ts +import { OnTextMention, OnToolResult, OnCondition } from '@io-orkes/conductor-javascript/agents'; + +const team = new Agent({ + name: 'coding_team', + model, + agents: [pythonExpert, jsExpert], + strategy: 'swarm', + handoffs: [ + // Hand off when the output mentions text (case-insensitive) + new OnTextMention({ target: 'python_expert', text: 'Python' }), + + // Hand off when a specific tool returns, optionally only if the result contains text + new OnToolResult({ target: 'escalation', toolName: 'detect_severity', resultContains: 'critical' }), + + // Hand off when a custom predicate returns true (runs as a worker task) + new OnCondition({ target: 'fallback', condition: (ctx) => ctx.result.length > 1000 }), + ], +}); +``` + +`OnCondition` runs as a **local worker task**, so it needs a polling process — +the same constraint as any local tool. `OnTextMention` and `OnToolResult` are +evaluated server-side. + +Constrain which transitions are legal with +`allowedTransitions: { agentName: ['otherAgent', ...] }`. + +## Plan-execute + +`strategy: 'plan_execute'` runs a planner sub-agent to produce a JSON plan, then +executes it deterministically as a sub-workflow. A `planner` is required; a +`fallback` is optional. + +```ts +const harness = new Agent({ + name: 'plan_harness', + model: 'openai/gpt-4o', + strategy: 'plan_execute', + planner: plannerAgent, // required — produces the JSON plan + fallback: agenticAgent, // optional — runs agentically if the plan can't compile or run + tools: [/* tools the plan steps call */], +}); +const result = await runtime.run(harness, 'Build a release report.'); +``` + +You can supply a **deterministic static plan** with the typed builders and pass it +via `RunOptions.plan`. It wins over the planner's output — the planner still runs, +but its result is discarded. + +```ts +import { Plan, Step, Op, Generate, Ref } from '@io-orkes/conductor-javascript/agents'; + +const plan = new Plan({ + steps: [ + new Step('fetch', { operations: [new Op('fetch_data', { args: { source: 'db' } })] }), + new Step('summarize', { + dependsOn: ['fetch'], + operations: [new Op('summarize', { + generate: new Generate({ + instructions: 'Summarize the fetched data.', + outputSchema: '{"type":"object","properties":{"summary":{"type":"string"}}}', + context: new Ref('fetch'), // reference a prior step's output + }), + })], + }), + ], +}); + +const result = await runtime.run(harness, 'Run the pipeline.', { plan }); +``` + +Builders: `Plan({ steps, validation?, onSuccess?, onFailure? })`, +`Step(id, { operations?, dependsOn?, parallel? })`, +`Op(tool, { args? | generate? })`, +`Generate({ instructions, outputSchema, maxTokens?, context? })`, +`Validation(tool, { args?, successCondition? })`, `Action(tool, { args? })`, +`Ref(stepId)`, `Context({ text? | url?, headers?, required?, maxBytes? })`. + +The wire format is identical to the Python SDK's `conductor.ai.agents.plans` +dataclasses — same JSON shape, same field names, same `Ref` marker +(`{"$ref": "step_id"}`). The server compiler is the same path for both SDKs. + +For planner reference material, set `plannerContext: [...]` on the agent (strings +or `Context` instances; URLs are fetched at runtime, with no recompile). + +## Skills + +`skill(path, options?)` loads a `SKILL.md` directory as an `Agent`; +`loadSkills(dir)` loads every skill subdirectory keyed by name. Skills run via +the same `run()` path and compose with `agentTool`. + +```ts +import { skill, loadSkills, agentTool, Agent } from '@io-orkes/conductor-javascript/agents'; + +const reviewer = skill('./skills/code-review', { model: 'openai/gpt-4o' }); +const all = loadSkills('./skills'); // Record + +const orchestrator = new Agent({ + name: 'lead', + model: 'anthropic/claude-sonnet-4-6', + instructions: 'Delegate reviews to the code-review skill.', + tools: [agentTool(reviewer)], +}); +``` + +## Next steps + +[termination](termination.md) — stop a multi-agent loop · +[deploy · serve · run · plan](deploy-serve-run.md) · +[client reference](../reference/client.md) — aggregate token usage diff --git a/docs/agents/concepts/scheduling.md b/docs/agents/concepts/scheduling.md new file mode 100644 index 00000000..3cbb75e8 --- /dev/null +++ b/docs/agents/concepts/scheduling.md @@ -0,0 +1,75 @@ +# Scheduling + +**Audience:** developers running Conductor agents unattended on a cron. + +## Prerequisites + +An agent that runs, and the scheduler module enabled on the server. Schedules are +attached at **deploy** time, not run time — see +[deploy-serve-run.md](deploy-serve-run.md). + +**Capability:** scheduling requires a server with the scheduler module. On a +standalone OSS server without it, `schedules.*` calls fail rather than silently +no-op. + +## Attaching schedules + +Reconciliation is declarative: a list upserts those schedules and prunes the +rest, `[]` purges all, and omitting `schedules` leaves them untouched. + +```ts +import { Agent, AgentRuntime, Schedule, schedules } from '@io-orkes/conductor-javascript/agents'; + +const digest = new Agent({ name: 'eng_digest', model, instructions: 'Write a digest.' }); + +await runtime.deploy(digest, { + schedules: [ + new Schedule({ + name: 'weekday-9am', + cron: '0 0 9 * * MON-FRI', + timezone: 'America/Los_Angeles', + input: { channel: '#eng' }, + description: 'Weekday morning digest', + }), + ], +}); +``` + +**Expected result:** `schedules.list({ agent: digest.name })` returns one +`ScheduleInfo`, and the agent runs at the next matching time. + +**Common failure mode:** omitting `schedules` when you meant to clear them. Only +`schedules: []` purges; leaving the key out preserves whatever is already on the +server. + +Note the cron format has a **seconds field** — `0 0 9 * * MON-FRI` is 09:00, not +`0 9 * * MON-FRI`. + +## Lifecycle + +```ts +const infos = await schedules.list({ agent: digest.name }); +await schedules.pause(infos[0].name, { reason: 'cooldown' }); +await schedules.resume(infos[0].name); +const execId = await schedules.runNow(infos[0].name); +const next = await schedules.previewNext('0 0 9 * * MON-FRI', { n: 5 }); + +await runtime.deploy(digest, { schedules: [] }); // purge all +``` + +**Lifecycle calls key on the wire name** — the prefixed `name` returned in +`ScheduleInfo` — not the short name you supplied. Passing `'weekday-9am'` to +`pause()` will not find the schedule; pass `infos[0].name`. + +`AgentClient` also has `schedule(agent, schedules)` — see +[../reference/client.md](../reference/client.md). + +## Cleanup + +`runtime.deploy(agent, { schedules: [] })` removes every schedule for that agent. +A deleted agent's schedules are not cleaned up automatically. + +## Next steps + +[deploy · serve · run · plan](deploy-serve-run.md) · +[../../schedules-events.md](../../schedules-events.md) — the non-agent scheduler diff --git a/docs/agents/concepts/stateful.md b/docs/agents/concepts/stateful.md new file mode 100644 index 00000000..29c9ea4a --- /dev/null +++ b/docs/agents/concepts/stateful.md @@ -0,0 +1,75 @@ +# Stateful agents + +**Audience:** developers whose tools need to share mutable state within a single +Conductor-agent run. + +## Prerequisites + +An agent with local `tool()` functions. Stateful runs isolate tool workers per +execution via a unique domain UUID, so they require a polling process for the +life of the run. + +## Sharing state across tool calls + +Set `stateful: true` on the agent (or on a tool def). Within one run, tools share +a mutable `context.state` object; mutations are captured and propagated between +tool calls. + +```ts +import type { ToolContext } from '@io-orkes/conductor-javascript/agents'; + +const addItem = tool( + async (args: { item: string }, ctx?: ToolContext) => { + const items: string[] = (ctx?.state?.list as string[]) ?? []; + items.push(args.item); + if (ctx?.state) ctx.state.list = items; + return { total: items.length }; + }, + { name: 'add_item', description: 'Add an item.', inputSchema: { + type: 'object', properties: { item: { type: 'string' } }, required: ['item'], + }}, +); + +const agent = new Agent({ name: 'list_agent', model, tools: [addItem], stateful: true }); +``` + +**Expected result:** across three `add_item` calls in one run, `total` returns 1, +2, 3. Two concurrent runs each start from an empty list. + +**Common failure mode:** mutating a closure variable instead of `ctx.state`. It +appears to work for a single sequential run and corrupts silently under +concurrency — see [tools.md](tools.md#no-per-run-mutable-capture). `ctx.state` is +the only per-run store that is actually isolated. + +## Liveness monitoring + +A stateful run has a domain-isolated worker, so `runtime.start()`, `run()`, and +`stream()` also start a liveness monitor. It polls the execution's workflow every +`livenessCheckIntervalSeconds`; if a `SCHEDULED` or `IN_PROGRESS` task in that +run's domain sits unpolled (`pollCount === 0`) for longer than +`livenessStallSeconds`, a blocking `wait()` rejects with `WorkerStallError` +instead of hanging forever. That is the signal the local worker process for the +run's domain died. + +The monitor stops on terminal status or handle disposal, and never keeps the +process alive on its own. + +| Option | Env var | Default | +|---|---|---| +| `livenessEnabled` | `CONDUCTOR_AGENT_LIVENESS_ENABLED` | `true` | +| `livenessStallSeconds` | `CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS` | `30` | +| `livenessCheckIntervalSeconds` | `CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS` | `10` | + +Framework-spawned agents (LangGraph, LangChain, Vercel AI wrappers) never route +through a per-run domain, so liveness monitoring does not apply to them. + +## Cleanup + +`runtime.shutdown()` stops the domain worker and the monitor. Because each +stateful run creates a domain-scoped worker, leaking runtimes leaks workers — +always shut down in a `finally`. + +## Next steps + +[tools](tools.md) · [../reference/runtime.md](../reference/runtime.md) · +[../../reliability.md](../../reliability.md) diff --git a/docs/agents/concepts/streaming-hitl.md b/docs/agents/concepts/streaming-hitl.md new file mode 100644 index 00000000..96dc9809 --- /dev/null +++ b/docs/agents/concepts/streaming-hitl.md @@ -0,0 +1,102 @@ +# Streaming and human-in-the-loop + +**Audience:** developers streaming Conductor-agent progress and gating actions on +human approval. + +## Prerequisites + +A working agent. Streaming needs `streamingEnabled` (default `true`, env +`CONDUCTOR_AGENT_STREAMING_ENABLED`) and a server that supports SSE for the +route; the SDK falls back to polling when it doesn't. + +**Security note:** approval gates are the control that keeps model output from +taking destructive action unreviewed. Set `approvalRequired: true` on any tool +that deletes, pays, sends, or escalates privilege — and make sure whoever calls +`approve()` can actually see what they're approving. + +## Streaming + +`runtime.stream(agent, prompt)` returns an `AgentStream` you can `for await` +over. Events carry a `type`: `'thinking'`, `'tool_call'`, `'tool_result'`, +`'waiting'`, `'handoff'`, `'message'`, `'done'`, and others. + +```ts +const stream = await runtime.stream(agent, 'Plan a 3-day trip to Tokyo.'); +for await (const event of stream) { + if (event.type === 'thinking') console.log('[thinking]', event.content); + else if (event.type === 'tool_call') console.log('[tool]', event.toolName, event.args); + else if (event.type === 'tool_result') console.log('[result]', event.toolName, event.result); + else if (event.type === 'done') console.log('[done]', event.output); +} +const result = await stream.getResult(); // terminal AgentResult after the stream ends +``` + +**Expected result:** events arrive incrementally, ending with `'done'`. +`getResult()` then resolves immediately with the terminal `AgentResult`. + +**Common failure mode:** no events, then a single `'done'`. That means SSE was +unavailable and the run fell back to polling — functionally correct, but you lose +incremental output. `SSEUnavailableError` and `SSETimeoutError` surface the +explicit cases. + +You can also `runtime.start(...)` and call `handle.stream()`. + +## Human-in-the-loop + +A tool with `approvalRequired: true`, or a `humanTool`, pauses execution and emits +a `'waiting'` event. Resolve it via the handle or stream: `approve(output?)`, +`reject(reason?)`, `send(message)`, or `respond(body)`. + +```ts +const deleteData = tool( + async (args: { table: string }) => ({ deleted: args.table }), + { + name: 'delete_data', + description: 'Delete a table. Destructive — requires approval.', + inputSchema: { type: 'object', properties: { table: { type: 'string' } }, required: ['table'] }, + approvalRequired: true, + }, +); + +const agent = new Agent({ name: 'ops', model, tools: [deleteData], instructions: '…' }); + +const handle = await runtime.start(agent, 'Delete the stale_cache table.'); +for await (const event of handle.stream()) { + if (event.type === 'waiting') { + // The waiting event carries the pending tool batch on event.pendingTool, + // or fetch the full status: + const status = await handle.getStatus(); + console.log('Approval needed for:', status.pendingTool?.toolCalls); + + await handle.approve(); // approve, or: + // await handle.reject('Not allowed'); + // await handle.respond({ approved: true, note: 'go ahead' }); + } else if (event.type === 'done') { + console.log('done', event.output); + } +} +``` + +**One HUMAN task gates the whole batch** of pending tool calls with a single +`{ approved, reason }` verdict. Iterate `pendingTool.toolCalls` to see every tool +that verdict covers — approving one approves all of them. The `pendingTool` is +mirrored onto the `waiting` event so you can read it without a `getStatus()` +round-trip. + +`humanTool` works the same way but lets the model ask a structured question; the +response schema is on `pendingTool.response_schema`. + +**Common failure mode:** a run that waits forever. Nothing resolved the pending +approval — a `'waiting'` event with no corresponding `approve()`/`reject()` will +sit until the task times out. + +## Cleanup + +Break out of the `for await` when you're done, then `runtime.shutdown()`. +Abandoning a handle without resolving a pending approval leaves the execution +waiting server-side; `handle.stop()` terminates it. + +## Next steps + +[tools](tools.md) · [callbacks](callbacks.md) · +[client reference](../reference/client.md) diff --git a/docs/agents/concepts/structured-output.md b/docs/agents/concepts/structured-output.md new file mode 100644 index 00000000..d492a400 --- /dev/null +++ b/docs/agents/concepts/structured-output.md @@ -0,0 +1,76 @@ +# Structured output + +**Audience:** developers who need typed data back from a Conductor agent rather +than prose. + +## Prerequisites + +A working agent and a model that supports structured output. `zod` is optional — +`outputType` accepts a Zod schema (converted to JSON Schema for you) or a plain +JSON Schema object. + +## Declaring a schema + +Set `outputType`. The model returns data conforming to it, and the structured +object lands under `result.output.result`. + +```ts +const ArticleAnalysis = { + type: 'object', + properties: { + title: { type: 'string' }, + category: { type: 'string', enum: ['tech', 'business', 'science'] }, + sentiment: { type: 'string', enum: ['positive', 'neutral', 'negative'] }, + keyTopics: { type: 'array', items: { type: 'string' } }, + }, + required: ['title', 'category', 'sentiment', 'keyTopics'], +}; + +const analyzer = new Agent({ + name: 'analyzer', + model: 'openai/gpt-4o', + instructions: 'Analyze the article and return structured data.', + outputType: ArticleAnalysis, +}); + +const result = await runtime.run(analyzer, 'Analyze: "Quantum Error Correction Hits 99.9% Fidelity"'); +const structured = result.output['result'] as Record; +console.log(structured.category, structured.sentiment); +``` + +**Expected result:** `result.output.result` is an object matching the schema, not +a JSON string. `console.log` prints e.g. `science positive`. + +**Common failure modes:** + +- `output.result` is a string containing JSON. The model ignored the schema — + usually a model that doesn't support structured output, or a schema too loose + to constrain it. Tighten `required` and prefer `enum` over free strings. +- A missing property despite being listed in `required`. Schema adherence is + enforced by the model, not the SDK; validate before relying on it. + +The same schema shape works with Zod: + +```ts +import { z } from 'zod'; + +const analyzer = new Agent({ + name: 'analyzer', + model: 'openai/gpt-4o', + instructions: 'Analyze the article.', + outputType: z.object({ + title: z.string(), + category: z.enum(['tech', 'business', 'science']), + }), +}); +``` + +## Structured output vs tools + +`outputType` shapes the agent's **final answer**. Tool `inputSchema` shapes what +the model passes **into** a tool. They're independent — an agent can have both. + +## Next steps + +[tools](tools.md) · [agent definition reference](../reference/agent-definition.md) · +[configuration contract](../reference/agent-schema.md) diff --git a/docs/agents/concepts/termination.md b/docs/agents/concepts/termination.md new file mode 100644 index 00000000..8fb32fd1 --- /dev/null +++ b/docs/agents/concepts/termination.md @@ -0,0 +1,58 @@ +# Termination + +**Audience:** developers bounding multi-turn and multi-agent Conductor agent +loops. + +## Prerequisites + +A multi-turn or multi-agent agent (see [multi-agent.md](multi-agent.md)). Every +agent already has `maxTurns` (default 25) as a backstop; termination conditions +are for stopping on *meaning* rather than on count alone. + +## Conditions + +Pass one to `termination:`. They compose with `.and()` / `.or()`, or the variadic +`AndCondition` / `OrCondition`. + +```ts +import { TextMention, MaxMessage, TokenUsageCondition, StopMessage } from '@io-orkes/conductor-javascript/agents'; + +const agent = new Agent({ + name: 'debate', + model, + agents: [a, b], + strategy: 'round_robin', + termination: new TextMention('TERMINATE') // stop when output mentions text + .or(new MaxMessage(10)) // …or after 10 messages + .or(new TokenUsageCondition({ maxTotalTokens: 50000 })), +}); +``` + +| Condition | Stops when | +|---|---| +| `TextMention(text, caseSensitive?)` | Output mentions `text`. | +| `StopMessage(stopMessage)` | Output equals the stop message. | +| `MaxMessage(maxMessages)` | The conversation reaches N messages. | +| `TokenUsageCondition({ maxTotalTokens?, maxPromptTokens?, maxCompletionTokens? })` | A token budget is exhausted. | +| `AndCondition(...)` / `OrCondition(...)` | Composites. | + +**Expected result:** the loop ends and `result.finishReason` reflects the cause. + +**Common failure mode:** a `TextMention` that never matches, leaving `maxTurns` +to end the run — which reads as a successful completion with a truncated answer. +Always pair a semantic condition with a `MaxMessage` or token bound, as above. + +## Gates + +`TextGate` and `gate()` gate transitions rather than ending the run: + +```ts +import { TextGate } from '@io-orkes/conductor-javascript/agents'; + +new Agent({ name: 'a', model, gate: new TextGate({ text: 'APPROVED', caseSensitive: false }) }); +``` + +## Next steps + +[multi-agent](multi-agent.md) · [callbacks](callbacks.md) · +[guardrails](guardrails.md) diff --git a/docs/agents/concepts/tools.md b/docs/agents/concepts/tools.md new file mode 100644 index 00000000..83ae1186 --- /dev/null +++ b/docs/agents/concepts/tools.md @@ -0,0 +1,219 @@ +# Tools + +**Audience:** developers giving Conductor agents the ability to call code, HTTP +endpoints, MCP servers, and other agents. + +## Prerequisites + +An agent that runs (see [agents.md](agents.md)). Local tools additionally need +the process to stay alive while the run is in flight, because they execute as +Conductor workers this process polls. `zod` is optional — `inputSchema` accepts +a Zod schema or a plain JSON Schema object. + +**Security note:** tools are arbitrary code reachable by model output. Treat tool +arguments as untrusted input, scope credentials per tool rather than per agent, +and set `approvalRequired: true` on anything destructive. See +[../../security.md](../../security.md). + +## Local tools — `tool()` + +`tool()` wraps an async function. It runs locally as a Conductor worker the +runtime polls; `run()` and `serve()` register and poll it for you. + +```ts +const getWeather = tool( + async (args: { city: string }) => { + return { city: args.city, tempC: 21, conditions: 'sunny' }; + }, + { + name: 'get_weather', + description: 'Get the current weather for a city.', + inputSchema: { + type: 'object', + properties: { city: { type: 'string', description: 'City name' } }, + required: ['city'], + }, + }, +); + +const agent = new Agent({ + name: 'weather_agent', + model: 'anthropic/claude-sonnet-4-6', + instructions: 'Answer weather questions using the tool.', + tools: [getWeather], +}); +``` + +**Expected result:** the model calls `get_weather`, your function runs in this +process, and the result feeds back into the conversation. + +**Common failure mode:** the run hangs at the tool call. That means no worker is +polling — you used `AgentClient.run()` (control plane only) instead of +`runtime.run()`, or the process exited early. + +Options: `name`, `description`, `inputSchema`, `outputSchema?`, +`approvalRequired?`, `timeoutSeconds?`, `external?`, `credentials?`, +`guardrails?`, `maxCalls?`, `retryCount?`, `retryDelaySeconds?`, `retryPolicy?`. + +The function receives an optional second argument, the `ToolContext` +(`sessionId`, `executionId`, `agentName`, `metadata`, `dependencies`, and a +mutable `state`). See [stateful.md](stateful.md). + +### No per-run mutable capture + +A `tool()` handler is registered **once** and its worker is reused across +concurrent runs — never re-created per run. Don't close over per-run mutable +state: a module-level counter, an array pushed to across calls, a captured `let` +reassigned mid-run. Two runs executing the same tool concurrently would corrupt +each other's state. + +Anything that varies per run belongs in `ToolContext` — `state` for durable +per-execution data, `dependencies` for injected collaborators — or in the +function's own arguments. Never in a closure variable mutated across invocations. + +## Tool discovery — `@Tool` / `toolsFrom` + +```ts +import { Tool, toolsFrom } from '@io-orkes/conductor-javascript/agents'; + +class MathTools { + @Tool({ description: 'Add two numbers.', inputSchema: { + type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a', 'b'], + }}) + async add(args: { a: number; b: number }) { return { sum: args.a + args.b }; } +} + +const tools = toolsFrom(new MathTools()); // ToolFunction[], bound to the instance +new Agent({ name: 'calc', model, tools }); +``` + +Requires `"experimentalDecorators": true`. + +## Built-in tools + +These return a `ToolDef` that runs **server-side** — no local worker, so they +work with the control-plane client too. Add them to `tools: [...]`. + +| Builder | Tool type | Purpose | +|---|---|---| +| `httpTool({ name, description, url, method?, headers?, inputSchema?, credentials? })` | `http` | Call an HTTP endpoint. | +| `mcpTool({ serverUrl, name?, description?, headers?, toolNames?, maxTools?, credentials? })` | `mcp` | Expose an MCP server's tools. | +| `apiTool({ url, name?, description?, headers?, toolNames?, maxTools?, credentials? })` | `api` | Expose an OpenAPI/API as tools. | +| `agentTool(agent, { name?, description?, retryCount?, retryDelaySeconds?, optional? })` | `agent_tool` | Call another `Agent` as a tool. | +| `humanTool({ name, description, inputSchema? })` | `human` | Pause for human input. | +| `imageTool({ name, description, llmProvider, model, style?, size? })` | `generate_image` | Generate images. | +| `audioTool({ name, description, llmProvider, model, voice?, speed?, format? })` | `generate_audio` | Text-to-speech. | +| `videoTool({ name, description, llmProvider, model, duration?, resolution?, fps?, ... })` | `generate_video` | Generate video. | +| `pdfTool({ name?, description?, pageSize?, theme?, fontSize? })` | `generate_pdf` | Render markdown to PDF. | +| `waitForMessageTool({ name, description, batchSize?, blocking? })` | `pull_workflow_messages` | Dequeue workflow messages. | +| `searchTool({ name, description, vectorDb, index, embeddingModelProvider, embeddingModel, namespace?, maxResults? })` | `rag_search` | RAG vector search. | +| `indexTool({ name, description, vectorDb, index, embeddingModelProvider, embeddingModel, namespace?, chunkSize?, chunkOverlap? })` | `rag_index` | RAG index/ingest. | + +```ts +import { httpTool, mcpTool } from '@io-orkes/conductor-javascript/agents'; + +const agent = new Agent({ + name: 'researcher', + model: 'anthropic/claude-sonnet-4-6', + tools: [ + httpTool({ + name: 'get_user', + description: 'Fetch a user by id.', + url: 'https://api.example.com/users/{id}', + method: 'GET', + }), + mcpTool({ serverUrl: 'https://mcp.example.com/sse', toolNames: ['search'] }), + ], +}); +``` + +### `waitForMessageTool` — workflow message queue + +Lets a running agent dequeue messages pushed into its workflow message queue +(Conductor `PULL_WORKFLOW_MESSAGES`). No worker needed. In blocking mode +(default) the task stays in progress until a message arrives. + +```ts +import { waitForMessageTool } from '@io-orkes/conductor-javascript/agents'; + +const agent = new Agent({ + name: 'inbox_agent', + model: 'anthropic/claude-sonnet-4-6', + instructions: 'When asked to wait, call wait_for_message and process what arrives.', + tools: [waitForMessageTool({ + name: 'wait_for_message', + description: 'Wait for the next inbound message.', + batchSize: 1, // up to 100; default 1 + blocking: true, // default true + })], +}); +``` + +### `agentTool` — agent as a tool + +```ts +import { agentTool } from '@io-orkes/conductor-javascript/agents'; + +const translator = new Agent({ name: 'translator', model, instructions: 'Translate to French.' }); + +const orchestrator = new Agent({ + name: 'orchestrator', + model, + instructions: 'Use the translator tool when asked to translate.', + tools: [agentTool(translator, { description: 'Translate text to French.' })], +}); +``` + +## Credentials + +Declare credential names per tool. The server resolves them when it polls the +task and delivers them wire-only on that task's `runtimeMetadata`; the SDK +injects them into `process.env` for the duration of the call +(mutate-invoke-restore, serialized so concurrent calls don't clobber each other). + +```ts +const dbLookup = tool( + async () => ({ ok: (process.env.DB_API_KEY ?? '') !== '' }), + { + name: 'db_lookup', + description: 'Look up data.', + inputSchema: { type: 'object', properties: {}, required: [] }, + credentials: ['DB_API_KEY'], + }, +); +``` + +For HTTP and MCP tools, reference them inline with `${NAME}` substitution: + +```ts +httpTool({ + name: 'search_api', + description: 'Search.', + url: 'https://api.example.com/search', + headers: { Authorization: 'Bearer ${SEARCH_API_KEY}' }, + credentials: ['SEARCH_API_KEY'], +}); +``` + +`getCredential('NAME')` fetches one explicitly inside a tool. + +**Fail-closed, no fallback.** If a tool declares `credentials: [...]` and the +server didn't deliver one, the task fails non-retryably naming the missing +credential. There is deliberately no ambient-env fallback that would silently +read a locally-set variable instead. Servers that predate +`TaskDef.runtimeMetadata` support (conductor-oss without PR #1255, +agentspan server > 0.4.2) can't deliver credentials at all. + +You can also pass credentials at call time: +`runtime.run(agent, prompt, { credentials: ['X'] })`. + +## Cleanup + +`runtime.shutdown()` stops the tool workers. A process with registered tools will +not exit without it. + +## Next steps + +[guardrails](guardrails.md) — validate tool output · +[streaming & HITL](streaming-hitl.md) — gate tools on approval · +[stateful](stateful.md) — share state across calls diff --git a/docs/agents/framework-agents.md b/docs/agents/framework-agents.md index 0bd187f7..bc24a38a 100644 --- a/docs/agents/framework-agents.md +++ b/docs/agents/framework-agents.md @@ -1,173 +1,9 @@ -# Framework Agents +# Conductor-agent framework bridges -You don't have to rewrite agents authored with another framework to run them on Agentspan. The runtime **detects** the framework object you pass to `run()` / `deploy()` / `stream()`, serializes it to an agent config, and runs it on the server — same call you'd make with a native `Agent`. +This page has moved to individual framework guides: -```ts -const runtime = new AgentRuntime(); -const result = await runtime.run(frameworkAgent, prompt); // <-- same entry point -``` - -Supported frameworks: **OpenAI Agents SDK**, **Google ADK**, **LangChain**, **LangGraph**, and the **Vercel AI SDK**. Detection is pure duck-typing — no framework is imported by the SDK. The framework packages are optional peer dependencies; install whichever you use. - -## How detection works - -`runtime.run(agent, ...)` calls `detectFramework(agent)`. It returns the first match: - -| Framework | Detected when the object has… | -|---|---| -| native `Agent` | is an instance of `Agent` (runs natively, not as a framework) | -| `langgraph` | `.invoke()` plus a graph shape (`.getGraph()`, a `.nodes` Map, or `.nodes` + `.builder`) | -| `langchain` | `.invoke()` plus an `lc_namespace` array (e.g. an `AgentExecutor`) | -| `openai` | `name` + string/function `instructions` + string `model` + `tools[]` + an OpenAI marker (`handoffs[]`, `inputGuardrails[]`, `asTool()`, `toolUseBehavior`, ...) | -| `google_adk` | `subAgents[]` (orchestration agents), or string `model` + ADK markers (`instruction`, `outputKey`, `generateContentConfig`, `beforeModelCallback`, ...) | - -If nothing matches and the object isn't a native `Agent`, you get a clear error. - -## OpenAI Agents SDK - -Pass an `@openai/agents` `Agent` straight to the runtime. - -```ts -import { Agent, setTracingDisabled } from '@openai/agents'; -import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; - -setTracingDisabled(true); - -const agent = new Agent({ - name: 'greeter', - instructions: 'You are a friendly assistant. Keep your responses concise and helpful.', - model: 'gpt-4o-mini', -}); - -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(agent, 'Say hello and tell me a fun fact about TypeScript.'); - result.printResult(); -} finally { - await runtime.shutdown(); -} -``` - -## Google ADK - -Pass a `@google/adk` agent (`LlmAgent`, or the `Sequential`/`Parallel`/`Loop` orchestration agents). - -```ts -import { LlmAgent } from '@google/adk'; -import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; - -const agent = new LlmAgent({ - name: 'greeter', - model: 'gemini-2.5-flash', - instruction: 'You are a friendly assistant. Keep your responses concise and helpful.', -}); - -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(agent, 'Say hello and tell me a fun fact about ML.'); - result.printResult(); -} finally { - await runtime.shutdown(); -} -``` - -## LangGraph - -Pass a prebuilt `createReactAgent` graph directly — detection handles it via `.invoke()` + graph shape. - -```ts -import { createReactAgent } from '@langchain/langgraph/prebuilt'; -import { ChatOpenAI } from '@langchain/openai'; -import { DynamicStructuredTool } from '@langchain/core/tools'; -import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; - -const llm = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 }); -const graph = createReactAgent({ llm, tools, name: 'math_agent' }); - -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(graph, 'What is 12 * 9?'); - result.printResult(); -} finally { - await runtime.shutdown(); -} -``` - -For a complex graph where automatic introspection of the model/tools could fail, import `createReactAgent` from the SDK wrapper instead. It stamps `._agentspan` metadata onto the graph so the serializer skips introspection: - -```ts -import { createReactAgent } from '@io-orkes/conductor-javascript/agents/langgraph'; -``` - -You can also pass a model hint at call time when detection can't infer it: `runtime.run(graph, prompt, { model: 'anthropic/claude-sonnet-4-6' })`. - -## LangChain - -A real `langchain` `AgentExecutor` is detected via `.invoke()` + `lc_namespace`. To make the model/tools unambiguous, use the SDK's drop-in builder, which attaches `._agentspan` metadata: - -```ts -import { createAgentExecutor } from '@io-orkes/conductor-javascript/agents/langchain'; -import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; - -const executor = createAgentExecutor({ agent, tools, llm }); - -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(executor, 'Summarize the latest release notes.'); - result.printResult(); -} finally { - await runtime.shutdown(); -} -``` - -The `@io-orkes/conductor-javascript/agents/langchain` subpath also exports `createRunnableWithMetadata(...)` (a runnable-like object with `invoke` + `lc_namespace` + metadata) and `getLangChainModule()`. - -## Vercel AI SDK - -Two ways to use the AI SDK: - -**1. AI SDK tools on a native Agent (recommended).** The tool system is a superset — it auto-detects AI SDK `tool()` objects (Zod `parameters` + `execute`) and converts them to native tool defs. No wrapper needed. - -```ts -import { tool as aiTool } from 'ai'; -import { z } from 'zod'; -import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; - -const weatherTool = aiTool({ - description: 'Get current weather for a city', - parameters: z.object({ city: z.string().describe('City name') }), - execute: async ({ city }) => ({ city, tempF: 62, condition: 'Foggy' }), -}); - -const agent = new Agent({ - name: 'weather_agent', - model: 'anthropic/claude-sonnet-4-6', - instructions: 'Use available tools to answer questions.', - tools: [weatherTool], -}); - -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(agent, 'What is the weather in San Francisco?'); - result.printResult(); -} finally { - await runtime.shutdown(); -} -``` - -**2. Drop-in `generateText` / `streamText`.** The `@io-orkes/conductor-javascript/agents/vercel-ai` subpath exports AI-SDK-shaped `generateText` and `streamText` that internally build an `Agent` + `AgentRuntime` and map the result back into the AI SDK response shape: - -```ts -import { generateText } from '@io-orkes/conductor-javascript/agents/vercel-ai'; - -const { text } = await generateText({ - model: 'anthropic/claude-sonnet-4-6', - prompt: 'Write a haiku about durable execution.', -}); -``` - -## Notes - -- All five frameworks use the identical `runtime.run(agentOrGraph, prompt)` entry point — there is no per-framework runtime API. -- Framework peer deps (`@openai/agents`, `@google/adk`, `@langchain/*`, `ai`, `zod`) are optional; install only what you use. The wrappers lazy-load their dependency and throw an install hint if it's missing. -- Framework agents can be deployed too: `runtime.deploy(frameworkAgent)`. See [advanced.md](advanced.md). +- [OpenAI Agents SDK](frameworks/openai.md) +- [Google ADK](frameworks/google-adk.md) +- [LangChain](frameworks/langchain.md) +- [LangGraph](frameworks/langgraph.md) +- [Vercel AI SDK](frameworks/vercel-ai.md) diff --git a/docs/agents/frameworks/google-adk.md b/docs/agents/frameworks/google-adk.md new file mode 100644 index 00000000..2c2fc2e0 --- /dev/null +++ b/docs/agents/frameworks/google-adk.md @@ -0,0 +1,58 @@ +# Google ADK + +**Audience:** developers running `@google/adk` agents on Conductor. + +## Prerequisites + +```bash +npm install @io-orkes/conductor-javascript @google/adk +``` + +A reachable Conductor server with the agent runtime, and the Gemini provider +configured on that **server**. `@google/adk` is an optional peer dependency. + +## Running one + +Pass an `LlmAgent`, or one of the `Sequential` / `Parallel` / `Loop` +orchestration agents. + +```ts +import { LlmAgent } from '@google/adk'; +import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; + +const agent = new LlmAgent({ + name: 'greeter', + model: 'gemini-2.5-flash', + instruction: 'You are a friendly assistant. Keep your responses concise and helpful.', +}); + +const runtime = new AgentRuntime(); +try { + const result = await runtime.run(agent, 'Say hello and tell me a fun fact about ML.'); + result.printResult(); +} finally { + await runtime.shutdown(); +} +``` + +**Expected result:** `status=COMPLETED` with the model's reply. + +**Common failure mode:** ADK uses `instruction` (singular); the native `Agent` and +the OpenAI SDK use `instructions`. Passing the wrong one leaves the agent with no +system prompt rather than erroring. + +## How detection works + +An object is treated as `google_adk` when it has `subAgents[]` (the orchestration +agents), or a string `model` plus an ADK marker such as `instruction`, +`outputKey`, `generateContentConfig`, or `beforeModelCallback`. + +## Deploying + +`runtime.deploy(agent)` works the same as for native agents. See +[../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md). + +## Next steps + +[openai.md](openai.md) · [langgraph.md](langgraph.md) · +[../concepts/multi-agent.md](../concepts/multi-agent.md) diff --git a/docs/agents/frameworks/langchain.md b/docs/agents/frameworks/langchain.md new file mode 100644 index 00000000..3f2a128f --- /dev/null +++ b/docs/agents/frameworks/langchain.md @@ -0,0 +1,68 @@ +# LangChain + +**Audience:** developers running LangChain agent executors on Conductor. + +## Prerequisites + +```bash +npm install @io-orkes/conductor-javascript langchain @langchain/core +``` + +A reachable Conductor server with the agent runtime, and the provider configured +on that **server**. The LangChain packages are optional peer dependencies. + +## Running an executor + +A real `AgentExecutor` is detected via `.invoke()` plus an `lc_namespace` array. +To make the model and tools unambiguous, use the SDK's drop-in builder, which +attaches `._agentspan` metadata: + +```ts +import { createAgentExecutor } from '@io-orkes/conductor-javascript/agents/langchain'; +import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; + +const executor = createAgentExecutor({ agent, tools, llm }); + +const runtime = new AgentRuntime(); +try { + const result = await runtime.run(executor, 'Summarize the latest release notes.'); + result.printResult(); +} finally { + await runtime.shutdown(); +} +``` + +**Expected result:** `status=COMPLETED` with the summary. + +**Common failure mode:** passing a bare `AgentExecutor` whose model isn't +introspectable, which yields an agent with no model. Prefer +`createAgentExecutor` — or pass `{ model: '…' }` in the run options. + +That metadata key is a cross-SDK wire contract shared with the Python SDK. Don't +set it by hand. + +## Also exported + +The `@io-orkes/conductor-javascript/agents/langchain` subpath also exports: + +- `createRunnableWithMetadata(...)` — a runnable-like object with `invoke`, + `lc_namespace`, and metadata. +- `getLangChainModule()` — lazy access to the underlying module. +- `ConductorAgentMetadata` — the metadata type. (`AgentspanMetadata` remains + exported as a deprecated alias.) + +## Detection rules + +`langchain` matches on `.invoke()` plus `lc_namespace`. LangGraph is checked +first, so a graph is never misread as an executor — see +[langgraph.md](langgraph.md). + +## Limitations + +Framework-spawned agents never route through a per-run domain, so +[liveness monitoring](../concepts/stateful.md#liveness-monitoring) does not apply. + +## Next steps + +[langgraph.md](langgraph.md) · [vercel-ai.md](vercel-ai.md) · +[../concepts/tools.md](../concepts/tools.md) diff --git a/docs/agents/frameworks/langgraph.md b/docs/agents/frameworks/langgraph.md new file mode 100644 index 00000000..a497a175 --- /dev/null +++ b/docs/agents/frameworks/langgraph.md @@ -0,0 +1,76 @@ +# LangGraph + +**Audience:** developers running LangGraph graphs on Conductor. + +## Prerequisites + +```bash +npm install @io-orkes/conductor-javascript @langchain/langgraph @langchain/core @langchain/openai +``` + +A reachable Conductor server with the agent runtime, and the provider configured +on that **server**. The `@langchain/*` packages are optional peer dependencies. + +## Running a prebuilt graph + +Pass a `createReactAgent` graph directly — detection handles it via `.invoke()` +plus graph shape. + +```ts +import { createReactAgent } from '@langchain/langgraph/prebuilt'; +import { ChatOpenAI } from '@langchain/openai'; +import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; + +const llm = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 }); +const graph = createReactAgent({ llm, tools, name: 'math_agent' }); + +const runtime = new AgentRuntime(); +try { + const result = await runtime.run(graph, 'What is 12 * 9?'); + result.printResult(); +} finally { + await runtime.shutdown(); +} +``` + +**Expected result:** `status=COMPLETED` with the computed answer. + +**Common failure mode:** the serializer introspects the graph to find the model +and tools. On a complex or custom graph that introspection can fail or guess +wrong, producing an agent with no model or a missing tool. + +## When introspection isn't enough + +Import `createReactAgent` from the SDK wrapper instead. It stamps `._agentspan` +metadata onto the graph so the serializer skips introspection and reads the +metadata directly. + +```ts +import { createReactAgent } from '@io-orkes/conductor-javascript/agents/langgraph'; +``` + +That metadata key is a cross-SDK wire contract shared with the Python SDK — it is +deliberately not renamed, and you should not set it by hand. + +You can also pass a model hint at call time when detection can't infer one: + +```ts +await runtime.run(graph, prompt, { model: 'anthropic/claude-sonnet-4-6' }); +``` + +## Detection rules + +An object is treated as `langgraph` when it has `.invoke()` plus a graph shape: +`.getGraph()`, a `.nodes` Map, or `.nodes` together with `.builder`. LangGraph is +checked before LangChain, so a graph is never misread as an executor. + +## Limitations + +Framework-spawned agents never route through a per-run domain, so +[liveness monitoring](../concepts/stateful.md#liveness-monitoring) does not apply +to them. + +## Next steps + +[langchain.md](langchain.md) · [../concepts/tools.md](../concepts/tools.md) · +[../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md) diff --git a/docs/agents/frameworks/openai.md b/docs/agents/frameworks/openai.md new file mode 100644 index 00000000..1ed14264 --- /dev/null +++ b/docs/agents/frameworks/openai.md @@ -0,0 +1,68 @@ +# OpenAI Agents SDK + +**Audience:** developers running `@openai/agents` agents on Conductor without +rewriting them. + +## Prerequisites + +```bash +npm install @io-orkes/conductor-javascript @openai/agents +``` + +A reachable Conductor server with the agent runtime, and the OpenAI provider +configured on that **server** — the SDK never reads your provider key. +`@openai/agents` is an optional peer dependency. + +## Running one + +Pass the agent straight to the runtime. Same entry point as a native `Agent`. + +```ts +import { Agent, setTracingDisabled } from '@openai/agents'; +import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; + +setTracingDisabled(true); + +const agent = new Agent({ + name: 'greeter', + instructions: 'You are a friendly assistant. Keep your responses concise and helpful.', + model: 'gpt-4o-mini', +}); + +const runtime = new AgentRuntime(); +try { + const result = await runtime.run(agent, 'Say hello and tell me a fun fact about TypeScript.'); + result.printResult(); +} finally { + await runtime.shutdown(); +} +``` + +**Expected result:** `status=COMPLETED` with the model's reply — the agent ran as +a durable Conductor execution rather than in-process. + +**Common failure modes:** an "unrecognized agent" error means detection didn't +match; check the table below. Leaving OpenAI's own tracing enabled will try to +reach OpenAI's tracing endpoint from your process — call `setTracingDisabled(true)`. + +## How detection works + +`runtime.run(agent, ...)` calls `detectFramework(agent)`. Detection is pure +duck-typing; the SDK imports no framework. + +An object is treated as `openai` when it has `name`, string-or-function +`instructions`, a string `model`, `tools[]`, **and** an OpenAI marker such as +`handoffs[]`, `inputGuardrails[]`, `asTool()`, or `toolUseBehavior`. + +If nothing matches and the object isn't a native `Agent`, you get an explicit +error rather than a silent fallback. + +## Deploying + +Framework agents deploy like native ones: `runtime.deploy(agent)`. See +[../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md). + +## Next steps + +[../concepts/tools.md](../concepts/tools.md) — native tools are a superset · +[google-adk.md](google-adk.md) · [langgraph.md](langgraph.md) diff --git a/docs/agents/frameworks/vercel-ai.md b/docs/agents/frameworks/vercel-ai.md new file mode 100644 index 00000000..ac9dd67d --- /dev/null +++ b/docs/agents/frameworks/vercel-ai.md @@ -0,0 +1,92 @@ +# Vercel AI SDK + +**Audience:** developers using the Vercel AI SDK who want durable execution on +Conductor. + +This bridge is specific to the JavaScript SDK — the Python and Java SDKs have no +equivalent. + +## Prerequisites + +```bash +npm install @io-orkes/conductor-javascript ai zod +``` + +A reachable Conductor server with the agent runtime, and the provider configured +on that **server**. `ai` and `zod` are optional peer dependencies. + +## Recommended: AI SDK tools on a native agent + +The native tool system is a superset. It auto-detects AI SDK `tool()` objects — a +Zod `parameters` schema plus `execute` — and converts them to native tool defs. No +wrapper needed. + +```ts +import { tool as aiTool } from 'ai'; +import { z } from 'zod'; +import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; + +const weatherTool = aiTool({ + description: 'Get current weather for a city', + parameters: z.object({ city: z.string().describe('City name') }), + execute: async ({ city }) => ({ city, tempF: 62, condition: 'Foggy' }), +}); + +const agent = new Agent({ + name: 'weather_agent', + model: 'anthropic/claude-sonnet-4-6', + instructions: 'Use available tools to answer questions.', + tools: [weatherTool], +}); + +const runtime = new AgentRuntime(); +try { + const result = await runtime.run(agent, 'What is the weather in San Francisco?'); + result.printResult(); +} finally { + await runtime.shutdown(); +} +``` + +**Expected result:** the model calls the tool, `execute` runs in this process as a +Conductor worker, and the answer includes the weather. + +**Common failure mode:** the run hangs at the tool call — `execute` runs locally, +so it needs a polling process. Use `runtime.run()` or keep a `serve()` alive; the +control-plane client won't poll it. + +This path gets you the full native feature set — guardrails, approval gates, +stateful runs — on top of AI SDK tools. + +## Drop-in `generateText` / `streamText` + +The `/agents/vercel-ai` subpath exports AI-SDK-shaped `generateText` and +`streamText` that internally build an `Agent` and `AgentRuntime`, then map the +result back into the AI SDK response shape. + +```ts +import { generateText } from '@io-orkes/conductor-javascript/agents/vercel-ai'; + +const { text } = await generateText({ + model: 'anthropic/claude-sonnet-4-6', + prompt: 'Write a haiku about durable execution.', +}); +``` + +**Expected result:** `text` holds the haiku, having executed durably on Conductor. + +Use this to retrofit durability into existing AI SDK call sites with a one-line +import change. Prefer the native-agent path above for anything new — the drop-in +surface intentionally mirrors the AI SDK rather than exposing Conductor features. + +## Limitations + +Framework-spawned agents never route through a per-run domain, so +[liveness monitoring](../concepts/stateful.md#liveness-monitoring) does not apply +to the drop-in path. + +## Next steps + +[../concepts/tools.md](../concepts/tools.md) · +[../concepts/streaming-hitl.md](../concepts/streaming-hitl.md) · +[langchain.md](langchain.md) diff --git a/docs/agents/getting-started.md b/docs/agents/getting-started.md index a7a756f9..108e421c 100644 --- a/docs/agents/getting-started.md +++ b/docs/agents/getting-started.md @@ -1,90 +1,7 @@ -# Getting Started +# Agent quickstart -Get an agent running in under 30 seconds. +This page has moved. Install, configure, and run your first agent: -## 1. Install - -The SDK ships as the `@io-orkes/conductor-javascript/agents` npm package (Node.js >= 18). - -```bash -npm install @io-orkes/conductor-javascript -``` - -It is published as both ESM and CommonJS, so `import` and `require` both work. The examples in these docs use ESM (`import`). You will also want `zod` if you plan to define tool/output schemas with it: - -```bash -npm install zod -``` - -## 2. Point at a server - -You need a running Agentspan server. The defaults assume a local one at `http://localhost:8080/api` (the SDK auto-appends `/api` if you omit it). - -| Variable | Default | Description | -|---|---|---| -| `AGENTSPAN_SERVER_URL` | `http://localhost:8080/api` | Agentspan server URL. | -| `AGENTSPAN_AUTH_KEY` | — | Auth key. Unset = no-auth mode (local / OSS). | -| `AGENTSPAN_AUTH_SECRET` | — | Auth secret. Set together with the key for Orkes Cloud. | -| `AGENTSPAN_API_KEY` | — | Pre-minted bearer token (alternative to key/secret). | - -```bash -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -export OPENAI_API_KEY= -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini -# Orkes Cloud only: -# export AGENTSPAN_AUTH_KEY=... -# export AGENTSPAN_AUTH_SECRET=... -``` - -`AGENTSPAN_AUTH_KEY` / `AGENTSPAN_AUTH_SECRET` are minted into a short-lived JWT and sent as the `X-Authorization` header on every server call. The SDK handles that for you — you only set the env vars. The SDK loads a `.env` file automatically (via `dotenv`). - -A handful of other env vars tune workers and logging (`AGENTSPAN_WORKER_POLL_INTERVAL`, `AGENTSPAN_WORKER_THREADS`, `AGENTSPAN_LOG_LEVEL`, ...); see [advanced.md](advanced.md#runtime-configuration). - -## 3. Run an agent - -```ts -import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; - -const agent = new Agent({ - name: 'greeter', - model: 'anthropic/claude-sonnet-4-6', - instructions: 'You are a friendly assistant. Keep responses brief.', -}); - -const runtime = new AgentRuntime(); -try { - const result = await runtime.run(agent, 'Say hello and tell me a fun fact about TypeScript.'); - result.printResult(); -} finally { - await runtime.shutdown(); -} -``` - -Run it with `tsx` (or compile + `node`): - -```bash -npx tsx my-agent.ts -``` - -That is the whole loop: define an `Agent`, create an `AgentRuntime`, `await runtime.run(agent, prompt)`, and read the `AgentResult`. `runtime.shutdown()` stops any local tool-worker polling so the process can exit. - -## Reading the result - -`run()` returns an [`AgentResult`](api-reference.md#agentresult). Common members: - -```ts -result.printResult(); // formatted summary to stdout -const ok = result.isSuccess; // status === 'COMPLETED' -const output = result.output; // Record; final text is usually output.result -const tokens = result.tokenUsage; // { promptTokens, completionTokens, totalTokens } | undefined -const finish = result.finishReason; // 'stop' | 'length' | 'guardrail' | 'rejected' | ... -const execId = result.executionId; // durable execution id on the server -``` - -`output` is always a `Record`. A plain text answer arrives as `{ result: "..." }`; structured output (see [advanced.md](advanced.md#structured-output)) arrives under `output.result` as an object. - -## Next - -- [writing-agents.md](writing-agents.md) — tools, multi-agent orchestration, guardrails, streaming, HITL, schedules. -- [framework-agents.md](framework-agents.md) — run OpenAI / ADK / LangChain / LangGraph / Vercel AI agents as-is. -- [advanced.md](advanced.md) — deploy/serve, the control-plane `AgentClient`, structured output, credentials. +- [Conductor agents — quickstart and index](README.md) +- [Defining an agent](concepts/agents.md) +- [Connection and authentication](../connection-authentication.md) diff --git a/docs/agents/reference/agent-definition.md b/docs/agents/reference/agent-definition.md new file mode 100644 index 00000000..30ae2254 --- /dev/null +++ b/docs/agents/reference/agent-definition.md @@ -0,0 +1,281 @@ +# Agent definition fields + +**Audience:** developers looking up an `AgentOptions` field, a tool builder +signature, or a result type. + +## Prerequisites + +None. This is a lookup page; the narrative lives in +[../concepts/agents.md](../concepts/agents.md). + +## Agent + +```ts +new Agent(options: AgentOptions) +``` + +| Field | Type | Notes | +|---|---|---| +| `name` | `string` | Required. `/^[a-zA-Z][a-zA-Z0-9_-]*$/`. | +| `model` | `string \| ClaudeCode` | e.g. `'anthropic/claude-sonnet-4-6'`. | +| `baseUrl` | `string` | Override the LLM provider base URL. | +| `instructions` | `string \| PromptTemplate \| (() => string)` | Static, template, or dynamic. Callables evaluate at serialization time. | +| `tools` | `unknown[]` | `tool()` wrappers, built-in tool defs, framework tools. | +| `agents` | `Agent[]` | Sub-agents. | +| `strategy` | `Strategy` | `'sequential' \| 'parallel' \| 'handoff' \| 'router' \| 'round_robin' \| 'random' \| 'swarm' \| 'manual' \| 'plan_execute'`. | +| `router` | `Agent \| (() => string)` | Required for `strategy: 'router'`. | +| `outputType` | Zod or JSON Schema | Structured output. | +| `guardrails` | `unknown[]` | Guardrail defs or instances. | +| `handoffs` | `HandoffCondition[]` | `OnTextMention` / `OnToolResult` / `OnCondition`. | +| `allowedTransitions` | `Record` | Constrain transitions. | +| `termination` | `TerminationCondition` | Stop condition. | +| `gate` | `GateCondition` | `TextGate` / `gate()`. | +| `callbacks` | `CallbackHandler[]` | Lifecycle hooks. | +| `memory` | `ConversationMemory` | Conversation history. | +| `maxTurns` | `number` | Default 25. | +| `maxTokens` / `temperature` / `timeoutSeconds` | `number` | Tuning. `timeoutSeconds: 0` = server default. | +| `credentials` | `string[]` | Secret names to resolve. | +| `stateful` | `boolean` | Per-execution worker isolation and shared state. | +| `planner` / `fallback` | `Agent` | `plan_execute` slots. `planner` required. | +| `plannerContext` | `(string \| Context \| object)[]` | Planner reference docs. | +| `enablePlanning` | `boolean` | Plan-first preamble. | +| `prefillTools` | `PrefillToolCall[]` | Tools run before the first LLM turn. | +| `cliCommands` / `cliAllowedCommands` / `cliConfig` | — | CLI command execution. | +| `codeExecutionConfig` | `CodeExecutionConfig` | Code execution. | +| `introduction` / `metadata` | — | Agent metadata. | + +Methods: `agent.pipe(other)` builds a sequential pipeline, flattening chains. +Getters: `isClaudeCode`, `claudeCodeConfig`. + +Helpers: `agent(fn, options)`, `scatterGather({ name, workers, model?, instructions?, retryCount?, retryDelaySeconds?, failFast?, timeoutSeconds? })`, +`AgentDec(options)` + `agentsFrom(instance)`, +`PromptTemplate(name, variables?, version?)`. + +## tool() + +```ts +tool(fn: (args, ctx?: ToolContext) => Promise, options: ToolOptions): ToolFunction +``` + +`ToolOptions`: `{ name?, description, inputSchema, outputSchema?, approvalRequired?, timeoutSeconds?, external?, credentials?, guardrails?, maxCalls?, retryCount?, retryDelaySeconds?, retryPolicy? }`. +Schemas accept Zod or JSON Schema. + +| Builder | Required options | `toolType` | +|---|---|---| +| `httpTool` | `name, description, url` (`method?, headers?, inputSchema?, credentials?`) | `http` | +| `mcpTool` | `serverUrl` (`name?, headers?, toolNames?, maxTools?, credentials?`) | `mcp` | +| `apiTool` | `url` (`name?, headers?, toolNames?, maxTools?, credentials?`) | `api` | +| `agentTool` | `agent` (`name?, description?, retryCount?, retryDelaySeconds?, optional?`) | `agent_tool` | +| `humanTool` | `name, description` (`inputSchema?`) | `human` | +| `imageTool` | `name, description, llmProvider, model` (`style?, size?`) | `generate_image` | +| `audioTool` | `name, description, llmProvider, model` (`voice?, speed?, format?`) | `generate_audio` | +| `videoTool` | `name, description, llmProvider, model` (`duration?, resolution?, fps?, …`) | `generate_video` | +| `pdfTool` | — (`name?, description?, pageSize?, theme?, fontSize?`) | `generate_pdf` | +| `waitForMessageTool` | `name, description` (`batchSize?` = 1, `blocking?` = true) | `pull_workflow_messages` | +| `searchTool` | `name, description, vectorDb, index, embeddingModelProvider, embeddingModel` (`namespace?, maxResults?, dimensions?`) | `rag_search` | +| `indexTool` | `name, description, vectorDb, index, embeddingModelProvider, embeddingModel` (`namespace?, chunkSize?, chunkOverlap?, dimensions?`) | `rag_index` | + +Discovery and helpers: `Tool(options?)` + `toolsFrom(instance)`; +`getToolDef(obj)` / `normalizeToolInput(obj)`; `isZodSchema(obj)`. + +### ToolContext + +```ts +interface ToolContext { + sessionId: string; + executionId: string; + agentName: string; + metadata: Record; + dependencies: Record; + state: Record; // mutable; propagates between tool calls +} +``` + +## Guardrails + +- `guardrail(fn, { name, position?, onFail?, maxRetries? })` — `fn` returns + `{ passed, message?, fixedOutput? }`. `guardrail.external({ … })` for remote workers. +- `new RegexGuardrail({ name, patterns, mode, position?, onFail?, message?, maxRetries? })` — `mode: 'block' | 'allow'`. +- `new LLMGuardrail({ name, model, policy, position?, onFail?, maxRetries?, maxTokens? })`. +- `Guardrail(options?)` + `guardrailsFrom(instance)`. + +`position`: `'input' | 'output'` (default `'output'`). `onFail`: +`'raise' | 'retry' | 'fix' | 'human'` (default `'raise'`). + +## Termination and handoffs + +| Class | Constructor | +|---|---| +| `TextMention` | `(text, caseSensitive = false)` | +| `StopMessage` | `(stopMessage)` | +| `MaxMessage` | `(maxMessages)` | +| `TokenUsageCondition` | `({ maxTotalTokens?, maxPromptTokens?, maxCompletionTokens? })` | +| `AndCondition` / `OrCondition` | `(...conditions)` | + +Compose with `.and(other)` / `.or(other)`. + +- `new OnTextMention({ target, text })` +- `new OnToolResult({ target, toolName, resultContains? })` +- `new OnCondition({ target, condition, agentName? })` — runs as a worker +- `new TextGate({ text, caseSensitive? })`, `gate(fn, { agentName? })` + +`HandoffContext`: `{ result, toolName?, toolResult?, messages? }`. + +## Callbacks + +```ts +abstract class CallbackHandler { + onAgentStart?(agentName, prompt): Promise; + onAgentEnd?(agentName, result): Promise; + onModelStart?(agentName, messages): Promise; + onModelEnd?(agentName, response): Promise; + onToolStart?(agentName, toolName, args): Promise; + onToolEnd?(agentName, toolName, result): Promise; +} +``` + +`CALLBACK_POSITIONS` maps hooks to wire positions; +`getCallbackWorkerNames(agentName, handler)` lists registered worker names. + +## Schedules + +```ts +new Schedule({ name, cron, timezone?, input?, catchup?, paused?, startAt?, endAt?, description? }) +``` + +`SchedulerClient` methods: `save(schedule, agentName)`, `get(wireName, agentName?)`, +`listForAgent(agentName)`, `pause(wireName, reason?)`, `resume(wireName)`, +`delete(wireName)`, `runNow(info)`, `previewNext(cron, { n?, startAt?, endAt? })`, +`reconcile(agentName, desired)`. + +Pause and resume issue PUT first and fall back to GET on HTTP 405, because +per-schedule verbs differ by Conductor server family — one client works against +both OSS/embedded and Orkes. + +The `schedules` namespace is a convenience layer over the singleton runtime. +**Lifecycle calls key on the wire name** (the prefixed `name` in `ScheduleInfo`), +not the short name you supplied. + +Errors: `ScheduleError`, `ScheduleNameConflict`, `ScheduleNotFound`, +`InvalidCronExpression`. + +## AgentResult + +```ts +interface AgentResult { + output: Record; // text answer -> { result: "..." } + executionId: string; + correlationId?: string; + messages: unknown[]; + toolCalls: unknown[]; + status: 'COMPLETED' | 'FAILED' | 'TERMINATED' | 'TIMED_OUT'; + finishReason: 'stop' | 'length' | 'tool_calls' | 'error' | 'cancelled' | 'timeout' | 'guardrail' | 'rejected'; + error?: string; + tokenUsage?: { promptTokens; completionTokens; totalTokens }; + metadata?: Record; + events: AgentEvent[]; + subResults?: Record; + readonly isSuccess: boolean; // status === 'COMPLETED' + readonly isFailed: boolean; // FAILED | TIMED_OUT + readonly isRejected: boolean; // finishReason === 'rejected' + printResult(): void; +} +``` + +A guardrail block yields `status: 'COMPLETED'` with +`finishReason: 'guardrail'` — check `finishReason`, not only `isSuccess`. + +## AgentHandle + +```ts +interface AgentHandle { + executionId: string; + correlationId: string; + getStatus(): Promise; + wait(pollIntervalMs?): Promise; + respond(output): Promise; + approve(output?): Promise; + reject(reason?): Promise; + send(message): Promise; + pause(): Promise; + resume(): Promise; + cancel(): Promise; + stop(): Promise; + stream(): AgentStream; +} +``` + +`approve()` sends `{ approved: true, ...output }`; `reject(reason)` sends +`{ approved: false, reason }`; `send(message)` sends `{ message }`. For a custom +human-task response shaped by `pendingTool.response_schema`, use `respond(body)`. + +`wait(pollIntervalMs?)` rejects on deadline with an `AgentAPIError` naming the last +known status — and for a stateful, domain-routed run with liveness enabled, rejects +earlier with `WorkerStallError` if the local worker appears to have died. See +[../concepts/stateful.md](../concepts/stateful.md#liveness-monitoring). + +## AgentStream and AgentEvent + +`AgentStream` implements `AsyncIterable`. Methods: `respond`, +`approve`, `reject`, `send`, and `getResult()` (drains, polls for terminal status, +returns the result). Fields: `executionId`, `events`. + +```ts +interface AgentEvent { + type: 'thinking' | 'tool_call' | 'tool_result' | 'guardrail_pass' | 'guardrail_fail' + | 'waiting' | 'handoff' | 'message' | 'error' | 'done' | string; + content?: string; + toolName?: string; + args?: Record; + result?: unknown; + target?: string; // handoff target + output?: unknown; // on 'done' + pendingTool?: PendingTool; // on 'waiting' + guardrailName?: string; +} +``` + +`AgentStatus`: `{ executionId, isComplete, isRunning, isWaiting, output?, status, reason?, currentTask?, messages, pendingTool? }`. +`PendingTool`: `{ taskRefName, toolCalls?: { name, args }[], response_schema?, … }`. +`EventTypes`, `Statuses`, `FinishReasons`, `TERMINAL_STATUSES` are exported. + +## Errors + +`ConductorAgentError` (base), `AgentAPIError`, `AgentNotFoundError`, +`ConfigurationError`, `CredentialNotFoundError`, `CredentialAuthError`, +`CredentialRateLimitError`, `CredentialServiceError`, `SSETimeoutError`, +`SSEUnavailableError`, `TerminalToolError`, `WorkerStallError`, +`GuardrailFailedError`. + +`AgentspanError` remains exported as a deprecated alias of `ConductorAgentError` — +the same class object, so `instanceof` works in both directions. See +[../../upgrading.md](../../upgrading.md). + +## RunSettings + +```ts +interface RunSettings { + model?: string; + temperature?: number; + maxTokens?: number; + reasoningEffort?: string; + thinkingBudgetTokens?: number; +} +``` + +## Other exports + +- **Memory:** `ConversationMemory`, `SemanticMemory`, `InMemoryStore`. +- **Plans:** `Plan`, `Step`, `Op`, `Generate`, `Validation`, `Action`, `Ref`, `Context`, `coercePlan`. +- **Skills:** `skill(path, options?)`, `loadSkills(dir, options?)`, `SkillLoadError`. +- **Credentials:** `getCredential`, `runWithCredentialContext`, `setCredentialContext`, `clearCredentialContext`. +- **Liveness:** `LivenessMonitor`, `LivenessMonitorOptions`. +- **Code execution:** `LocalCodeExecutor`, `DockerCodeExecutor`, `JupyterCodeExecutor`, `ServerlessCodeExecutor`, `CodeExecutor`, `CommandValidator`. +- **Claude Code:** `ClaudeCode(modelName?, permissionMode?)`, `PermissionMode`, `resolveClaudeCodeModel`. +- **Extended agents:** `GPTAssistantAgent({ name, assistantId, model?, instructions? })`. +- **Framework integration:** `detectFramework`, `serializeFrameworkAgent`, `serializeLangGraph`, `serializeLangChain`. +- **Subpaths:** `/agents/vercel-ai`, `/agents/langgraph`, `/agents/langchain`, `/agents/testing`. + +## Next steps + +[api.md](api.md) · [runtime.md](runtime.md) · [agent-schema.md](agent-schema.md) diff --git a/docs/agents/reference/agent-schema.json b/docs/agents/reference/agent-schema.json new file mode 100644 index 00000000..b49f51f6 --- /dev/null +++ b/docs/agents/reference/agent-schema.json @@ -0,0 +1,208 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://github.com/conductor-oss/javascript-sdk/docs/agents/reference/agent-schema.json", + "title": "Conductor agent configuration", + "description": "The serialized agent configuration the JavaScript SDK sends to the Conductor server. Produced by AgentConfigSerializer from an Agent (or a detected framework agent), and consumed by the server's agent compiler. The wire shape is shared with the Python and Java SDKs.", + "type": "object", + "required": ["name"], + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", + "description": "Agent name. Becomes the workflow definition name on the server." + }, + "model": { + "type": "string", + "description": "Provider-qualified model, e.g. 'openai/gpt-4o-mini'." + }, + "baseUrl": { + "type": "string", + "description": "Override for the LLM provider base URL." + }, + "instructions": { + "description": "System prompt. A callable is evaluated to a string at serialization time; a PromptTemplate serializes to a server-managed reference.", + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" }, + "variables": { "type": "object" }, + "version": { "type": "integer" } + } + } + ] + }, + "introduction": { "type": "string" }, + "temperature": { "type": "number" }, + "maxTokens": { "type": "integer", "minimum": 1 }, + "maxTurns": { "type": "integer", "minimum": 1, "description": "Default 25." }, + "timeoutSeconds": { "type": "integer", "minimum": 0, "description": "0 means the server default." }, + "stateful": { + "type": "boolean", + "description": "Isolate tool workers per execution via a domain UUID and share mutable state across tool calls." + }, + "credentials": { + "type": "array", + "items": { "type": "string" }, + "description": "Secret names the server resolves at poll time and delivers wire-only on the task's runtimeMetadata." + }, + "outputType": { + "type": "object", + "description": "JSON Schema for structured output. A Zod schema is converted before serialization." + }, + "strategy": { + "type": "string", + "enum": [ + "sequential", + "parallel", + "handoff", + "router", + "round_robin", + "random", + "swarm", + "manual", + "plan_execute" + ] + }, + "agents": { + "type": "array", + "items": { "$ref": "#" }, + "description": "Sub-agents. Each runs as its own sub-workflow." + }, + "router": { + "description": "Required when strategy is 'router'. An agent config, or the name of one.", + "oneOf": [{ "type": "string" }, { "$ref": "#" }] + }, + "planner": { + "$ref": "#", + "description": "Required when strategy is 'plan_execute'." + }, + "fallback": { + "$ref": "#", + "description": "Optional agentic fallback when a plan cannot compile or run." + }, + "plannerContext": { + "type": "array", + "description": "Reference material for the planner. Strings, or Context objects whose URLs are fetched at runtime." + }, + "enablePlanning": { "type": "boolean" }, + "allowedTransitions": { + "type": "object", + "additionalProperties": { "type": "array", "items": { "type": "string" } } + }, + "tools": { + "type": "array", + "items": { "$ref": "#/definitions/tool" } + }, + "guardrails": { + "type": "array", + "items": { "$ref": "#/definitions/guardrail" } + }, + "handoffs": { + "type": "array", + "items": { "$ref": "#/definitions/handoff" } + }, + "termination": { + "type": "object", + "description": "Termination condition tree. Composites carry nested conditions.", + "additionalProperties": true + }, + "gate": { "type": "object", "additionalProperties": true }, + "callbacks": { + "type": "array", + "description": "Registered callback worker references, one per implemented hook.", + "items": { "type": ["object", "string"] } + }, + "memory": { "type": "object", "additionalProperties": true }, + "metadata": { "type": "object", "additionalProperties": true }, + "prefillTools": { "type": "array" }, + "cliCommands": { "type": "array", "items": { "type": "string" } }, + "cliAllowedCommands": { "type": "array", "items": { "type": "string" } }, + "cliConfig": { "type": "object", "additionalProperties": true }, + "codeExecutionConfig": { "type": "object", "additionalProperties": true }, + "_framework": { + "type": "string", + "description": "Set when the agent was serialized from a detected framework object rather than a native Agent.", + "enum": ["openai", "google_adk", "langchain", "langgraph", "vercel-ai", "skill"] + } + }, + "definitions": { + "tool": { + "type": "object", + "required": ["name", "toolType"], + "additionalProperties": true, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "toolType": { + "type": "string", + "enum": [ + "worker", + "http", + "mcp", + "api", + "agent_tool", + "human", + "generate_image", + "generate_audio", + "generate_video", + "generate_pdf", + "pull_workflow_messages", + "rag_search", + "rag_index" + ] + }, + "inputSchema": { "type": "object" }, + "outputSchema": { "type": "object" }, + "approvalRequired": { + "type": "boolean", + "description": "Gate the tool behind a HUMAN task. One verdict covers the whole pending batch." + }, + "timeoutSeconds": { "type": "integer", "minimum": 0 }, + "external": { "type": "boolean" }, + "credentials": { "type": "array", "items": { "type": "string" } }, + "guardrails": { "type": "array", "items": { "$ref": "#/definitions/guardrail" } }, + "maxCalls": { "type": "integer", "minimum": 1 }, + "retryCount": { "type": "integer", "minimum": 0 }, + "retryDelaySeconds": { "type": "integer", "minimum": 0 }, + "retryPolicy": { "type": "string" }, + "config": { + "type": "object", + "description": "Tool-type-specific settings, e.g. url and headers for an http tool.", + "additionalProperties": true + } + } + }, + "guardrail": { + "type": "object", + "required": ["name"], + "additionalProperties": true, + "properties": { + "name": { "type": "string" }, + "position": { "type": "string", "enum": ["input", "output"] }, + "onFail": { "type": "string", "enum": ["raise", "retry", "fix", "human"] }, + "maxRetries": { "type": "integer", "minimum": 0 }, + "message": { "type": "string" }, + "patterns": { "type": "array", "items": { "type": "string" } }, + "mode": { "type": "string", "enum": ["block", "allow"] }, + "model": { "type": "string" }, + "policy": { "type": "string" } + } + }, + "handoff": { + "type": "object", + "required": ["target"], + "additionalProperties": true, + "properties": { + "target": { "type": "string", "description": "Name of the sub-agent to hand off to." }, + "text": { "type": "string" }, + "caseSensitive": { "type": "boolean" }, + "toolName": { "type": "string" }, + "resultContains": { "type": "string" } + } + } + } +} diff --git a/docs/agents/reference/agent-schema.md b/docs/agents/reference/agent-schema.md new file mode 100644 index 00000000..e1d3dc4f --- /dev/null +++ b/docs/agents/reference/agent-schema.md @@ -0,0 +1,98 @@ +# Agent configuration contract + +**Audience:** developers who need to know exactly what the SDK sends to the +server, or who generate agent configs from something other than the `Agent` +class. + +The machine-readable contract is [agent-schema.json](agent-schema.json), a +draft-07 JSON Schema. CI validates it against every fixture in `e2e/_configs/` +(see `scripts/verify-agent-schema.mjs`). + +## Prerequisites + +None to read. To regenerate or check it: + +```bash +npm run verify:agent-schema +``` + +## What it describes + +`AgentConfigSerializer` turns an `Agent` — or a detected framework object — into a +plain JSON object, which the server's agent compiler turns into a workflow +definition. That JSON is the contract. It is shared with the Python and Java SDKs: +same field names, same nesting, same compiler on the server side. + +The simplest valid config is a name and a model: + +```json +{ + "name": "greeter", + "model": "openai/gpt-4o-mini" +} +``` + +`name` is the only required field, and it must match +`^[a-zA-Z][a-zA-Z0-9_-]*$` because it becomes a workflow definition name. + +## Shape + +| Field | Notes | +|---|---| +| `name` | Required. Identifier-shaped. | +| `model`, `baseUrl`, `temperature`, `maxTokens`, `maxTurns`, `timeoutSeconds` | LLM and execution tuning. `maxTurns` defaults to 25; `timeoutSeconds: 0` means the server default. | +| `instructions` | A string, or a prompt-template reference `{ name, variables?, version? }`. A callable is evaluated to a string **at serialization time**. | +| `tools[]` | Each has `name` + `toolType`. See below. | +| `agents[]` | Sub-agent configs — the schema is recursive (`$ref: "#"`). | +| `strategy` | One of the nine orchestration strategies. | +| `router`, `planner`, `fallback` | Named agent slots. `router` is required for `strategy: 'router'`, `planner` for `'plan_execute'`. | +| `guardrails[]`, `handoffs[]`, `termination`, `gate` | Validation and control flow. | +| `outputType` | JSON Schema for structured output. A Zod schema is converted before serialization. | +| `credentials[]` | Secret names, resolved server-side at poll time. | +| `stateful` | Domain-isolated workers plus shared tool state. | +| `callbacks[]` | One entry per implemented lifecycle hook. | +| `_framework` | Present only when serialized from a framework object: `openai`, `google_adk`, `langchain`, `langgraph`, `vercel-ai`, or `skill`. | + +`additionalProperties` is `true` throughout. The server accepts fields this SDK +version doesn't know about, so a newer server feature doesn't break an older SDK. + +## Tool types + +`toolType` discriminates how the server executes a tool: + +| `toolType` | Runs where | Builder | +|---|---|---| +| `worker` | Your process, polled as a Conductor worker | `tool()` | +| `http` | Server | `httpTool` | +| `mcp` | Server | `mcpTool` | +| `api` | Server | `apiTool` | +| `agent_tool` | Server, as a sub-agent | `agentTool` | +| `human` | Pauses for a person | `humanTool` | +| `generate_image` / `generate_audio` / `generate_video` / `generate_pdf` | Server | `imageTool` / `audioTool` / `videoTool` / `pdfTool` | +| `pull_workflow_messages` | Server | `waitForMessageTool` | +| `rag_search` / `rag_index` | Server | `searchTool` / `indexTool` | + +Only `worker` needs a polling process. That single distinction explains most +"my run hangs at the tool call" reports — see +[../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md). + +Tool-type-specific settings (a URL, headers, a vector DB) live under `config`. + +## Inspecting a real config + +`runtime.plan(agent)` returns the compiled workflow definition; +`AgentConfigSerializer` gives you the config that produced it: + +```ts +import { AgentConfigSerializer } from '@io-orkes/conductor-javascript/agents'; + +console.log(JSON.stringify(new AgentConfigSerializer().serialize(agent), null, 2)); +``` + +**Expected result:** JSON matching this schema. Useful for diffing what an agent +change actually alters, and for filing a bug against the compiler. + +## Next steps + +[agent-definition.md](agent-definition.md) — the authoring-side fields · +[api.md](api.md) · [../concepts/structured-output.md](../concepts/structured-output.md) diff --git a/docs/agents/reference/api.md b/docs/agents/reference/api.md new file mode 100644 index 00000000..343132d8 --- /dev/null +++ b/docs/agents/reference/api.md @@ -0,0 +1,73 @@ +# Conductor-agent API map + +**Audience:** developers looking for which type owns a given operation. + +## Prerequisites + +None. Everything here is exported from +`@io-orkes/conductor-javascript/agents` unless a subpath is noted. + +Import agent symbols from the `/agents` subpath, never the package root — the root +re-exports the generated OpenAPI surface, which contains a colliding `Action` +type. + +## By goal + +| I want to… | Use | Reference | +|---|---|---| +| Define an agent | `Agent`, `agent()` | [agent-definition.md](agent-definition.md#agent) | +| Run one and wait | `runtime.run()` | [runtime.md](runtime.md) | +| Run one and interact | `runtime.start()` → `AgentHandle` | [agent-definition.md](agent-definition.md#agenthandle) | +| Stream events | `runtime.stream()` → `AgentStream` | [../concepts/streaming-hitl.md](../concepts/streaming-hitl.md) | +| Register without running | `runtime.deploy()` | [../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md) | +| Run a long-lived worker process | `runtime.serve()` | [../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md) | +| Inspect the compiled definition | `runtime.plan()` | [agent-schema.md](agent-schema.md) | +| Trigger from another process | `runtime.client.run()` | [client.md](client.md) | +| Read an execution after the fact | `runtime.workflows` | [client.md](client.md#workflowclient) | +| Sum tokens across sub-workflows | `workflows.extractTokenUsage()` | [client.md](client.md#workflowclient) | +| Give the agent a local function | `tool()` | [../concepts/tools.md](../concepts/tools.md) | +| Call an HTTP endpoint / MCP server | `httpTool`, `mcpTool`, `apiTool` | [../concepts/tools.md](../concepts/tools.md#built-in-tools) | +| Use another agent as a tool | `agentTool()` | [../concepts/tools.md](../concepts/tools.md#agenttool--agent-as-a-tool) | +| Require human approval | `approvalRequired`, `humanTool` | [../concepts/streaming-hitl.md](../concepts/streaming-hitl.md) | +| Validate input or output | `guardrail`, `RegexGuardrail`, `LLMGuardrail` | [../concepts/guardrails.md](../concepts/guardrails.md) | +| Get typed output | `outputType` | [../concepts/structured-output.md](../concepts/structured-output.md) | +| Coordinate several agents | `agents` + `strategy` | [../concepts/multi-agent.md](../concepts/multi-agent.md) | +| Stop a loop | `termination` | [../concepts/termination.md](../concepts/termination.md) | +| Observe the lifecycle | `CallbackHandler` | [../concepts/callbacks.md](../concepts/callbacks.md) | +| Run on a cron | `Schedule`, `schedules` | [../concepts/scheduling.md](../concepts/scheduling.md) | +| Share state across tool calls | `stateful: true`, `ToolContext.state` | [../concepts/stateful.md](../concepts/stateful.md) | +| Run a plan deterministically | `strategy: 'plan_execute'`, `Plan` | [../concepts/multi-agent.md](../concepts/multi-agent.md#plan-execute) | +| Load a `SKILL.md` directory | `skill()`, `loadSkills()` | [../concepts/multi-agent.md](../concepts/multi-agent.md#skills) | +| Run a framework agent | same `runtime.run()` | [../frameworks/openai.md](../frameworks/openai.md) | + +## By type + +| Type | Role | Reference | +|---|---|---| +| `Agent` | The unit of authoring. | [agent-definition.md](agent-definition.md) | +| `AgentRuntime` | Execution + local tool workers. | [runtime.md](runtime.md) | +| `AgentConfig` / `AgentConfigOptions` | Behavior-only settings. | [runtime.md](runtime.md#agentconfigoptions) | +| `AgentClient` / `OrkesAgentClient` | Control plane (`/agent/*`). No local workers. | [client.md](client.md) | +| `WorkflowClient` | Read-only execution reads. | [client.md](client.md#workflowclient) | +| `SchedulerClient` | Cron lifecycle. | [agent-definition.md](agent-definition.md#schedules) | +| `AgentResult` | Terminal result. | [agent-definition.md](agent-definition.md#agentresult) | +| `AgentHandle` / `ClientHandle` | In-flight interaction. | [agent-definition.md](agent-definition.md#agenthandle) | +| `AgentStream` / `AgentEvent` | Incremental events. | [agent-definition.md](agent-definition.md#agentstream-and-agentevent) | +| `ToolContext` | Per-call context and per-run state. | [agent-definition.md](agent-definition.md#toolcontext) | +| `ConductorAgentError` | Base of the error hierarchy. | [agent-definition.md](agent-definition.md#errors) | + +## Runtime vs client + +The distinction that matters most: + +| | `AgentRuntime` | `AgentClient` | +|---|---|---| +| Polls local `tool()` workers | **Yes** | **No** | +| Compiles and starts executions | Yes | Yes | +| Right for agents with local tools | Yes | Only with a separate `serve()` process | +| Right for LLM-only / server-side-tool agents | Yes | Yes | + +## Next steps + +[runtime.md](runtime.md) · [client.md](client.md) · +[agent-definition.md](agent-definition.md) · [agent-schema.md](agent-schema.md) diff --git a/docs/agents/reference/client.md b/docs/agents/reference/client.md new file mode 100644 index 00000000..9c2ed5bd --- /dev/null +++ b/docs/agents/reference/client.md @@ -0,0 +1,122 @@ +# AgentClient reference + +**Audience:** applications triggering and inspecting Conductor-agent executions +without owning local tool workers. + +## Prerequisites + +A configured connection. `AgentClient` is the interface for the `/agent/*` +control-plane HTTP surface; `OrkesAgentClient` is the Conductor/Orkes +implementation. + +```ts +new OrkesAgentClient(configuration?: OrkesApiConfig | ConductorClient) +``` + +Obtain one via `runtime.client` (shares the runtime's client and token mint) or +`OrkesClients.getAgentClient()`. Every operation rides the shared +`ConductorClient`'s authenticated call path — no bespoke auth or transport lives +behind this interface, and it never mints a token independently. + +## Control plane only + +`AgentClient.run` and `start` compile, start, and poll an agent, but do **not** +register or poll local tool workers. + +Use it for LLM-only agents, agents whose tools are all server-side (HTTP, MCP, +API, RAG), or pre-deployed workflows. For agents with local `tool()` functions, +use `runtime.run()` or keep a `serve()` process alive — otherwise the execution +reaches the tool call and waits indefinitely. This is the single most common +misconfiguration; see +[../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md). + +## Usage + +```ts +const client = runtime.client; // or: orkesClients.getAgentClient() + +// Compile + start + poll to result +const result = await client.run(agent, 'summarize this', { timeoutSeconds: 120 }); + +// Start and interact via a ClientHandle +const handle = await client.start(agent, 'do work'); +const status = await handle.getStatus(); +const final = await handle.wait(); +await handle.approve(); // or reject(reason) / send(message) / respond(body) +await handle.stop(); + +// Compile + register one or more agents (no execution) +const infos = await client.deploy(agentA, agentB); // DeploymentInfo[] + +// Deploy + reconcile cron schedules in one call +import { Schedule } from '@io-orkes/conductor-javascript/agents'; +await client.schedule(agent, [new Schedule({ name: 'nightly', cron: '0 0 0 * * *' })]); +``` + +## Members + +| Member | Signature | Notes | +|---|---|---| +| `workflows` | `WorkflowClient` | Read-only workflow client. | +| `schedules` | `SchedulerClient` | Cron lifecycle over the shared Conductor client. | +| `run` | `(agent, prompt, opts?) => Promise` | Compile, start, poll to result. | +| `start` | `(agent, prompt, opts?) => Promise` | Compile, start, return a handle. | +| `deploy` | `(agent, { schedules? }?) => Promise` or `(...agents) => Promise` | Register agent(s). | +| `schedule` | `(agent, schedules) => Promise` | Deploy and reconcile schedules. | +| `startAgent` / `deployAgent` / `compile` | `(payload, signal?) => Promise` | Low-level POST endpoints. | +| `status` | `(executionId, signal?) => Promise` | GET status. | +| `getExecution` | `(executionId, signal?) => Promise` | Full execution data. | +| `listExecutions` | `(params?, signal?) => Promise` | List, optionally filtered. | +| `respond` | `(executionId, body, signal?) => Promise` | Complete a pending human task. | +| `stop` | `(executionId, signal?) => Promise` | Stop a running execution. | +| `signal` | `(executionId, message, signal?) => Promise` | Inject persistent context into a running execution. | +| `stream` | `(executionId, lastEventId?, signal?) => Promise` | SSE stream. | +| `close` | `() => Promise` | Release this client's open streams. | + +## ClientHandle + +Returned by `AgentClient.start`: + +```ts +{ executionId, getStatus(), wait(pollIntervalMs?), respond(output), + approve(output?), reject(reason?), send(message), stop(), stream() } +``` + +`wait()` rejects once its deadline passes — derived from `timeoutSeconds`, or 10 +minutes by default — with an `AgentAPIError` naming the last known status. A +rejection from `wait()` does **not** stop the execution; call `stop()` if that's +what you want. + +## WorkflowClient + +Read-only client for Conductor workflow executions. Available as +`runtime.workflows` or `client.workflows`. + +| Method | Signature | Notes | +|---|---|---| +| `getWorkflow` | `(executionId, includeTasks = true) => Promise` | Full execution. | +| `getStatus` | `(executionId) => Promise` | `'RUNNING'`, `'COMPLETED'`, … or `''`. | +| `extractTokenUsage` | `(executionId) => Promise` | Aggregated across sub-workflows. | + +```ts +const wf = await runtime.workflows.getWorkflow(executionId); +const status = await runtime.workflows.getStatus(executionId); +const usage = await runtime.workflows.extractTokenUsage(executionId); +``` + +`extractTokenUsage` walks the execution tree, recursing into `SUB_WORKFLOW` tasks, +and sums usage — the reason it exists is that multi-agent runs spread tokens across +sub-workflows, so reading the parent alone undercounts. + +`result.tokenUsage` is already populated on a normal `run()`; use this to inspect +an execution by id after the fact. + +## Cleanup + +`client.close()` releases open `AgentStream`s. A client obtained from +`runtime.client` is closed by `runtime.shutdown()`. + +## Next steps + +[runtime.md](runtime.md) · [api.md](api.md) · +[../concepts/streaming-hitl.md](../concepts/streaming-hitl.md) diff --git a/docs/agents/reference/runtime.md b/docs/agents/reference/runtime.md new file mode 100644 index 00000000..0e021ede --- /dev/null +++ b/docs/agents/reference/runtime.md @@ -0,0 +1,153 @@ +# AgentRuntime reference + +**Audience:** applications owning local tool workers and Conductor-agent +lifecycle. + +## Prerequisites + +Create one runtime per application lifetime with a configured server connection, +and shut it down on exit. A runtime owns connection-facing clients, local tool +workers, and execution lifecycle. + +```ts +new AgentRuntime(configuration?: OrkesApiConfig | ConductorClient, settings?: AgentConfigOptions) +``` + +Both arguments are optional and independent: + +- **`configuration`** — connection and auth, the same shape every other Conductor + client takes. Pass a pre-built `ConductorClient` (from `createConductorClient()` + or `OrkesClients`) to share one client, and one token mint, across control-plane + and worker-plane calls. +- **`settings`** — `AgentConfigOptions`, purely behavioral. No connection fields. + +```ts +import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; + +const runtime = new AgentRuntime( + { serverUrl: 'http://localhost:8080/api', keyId: '…', keySecret: '…' }, + { + workerPollIntervalMs: 100, + workerThreadCount: 1, + streamingEnabled: true, + livenessEnabled: true, + livenessStallSeconds: 30, + livenessCheckIntervalSeconds: 10, + }, +); + +// Both optional — reads connection and behavior entirely from env: +const defaultRuntime = new AgentRuntime(); +``` + +## Members + +| Member | Signature | Notes | +|---|---|---| +| `config` | `AgentConfig` | Resolved behavior config (readonly). | +| `client` | `AgentClient` | Control plane (`/agent/*`), sharing the runtime's Conductor client. | +| `workflows` | `WorkflowClient` | Read-only execution reads. | +| `run` | `(agent, prompt, options?) => Promise` | Compile, start, stream, return. Registers local workers. | +| `start` | `(agent, prompt, options?) => Promise` | Async interaction handle. | +| `stream` | `(agent, prompt, options?) => Promise` | Event stream. | +| `deploy` | `(agent, { schedules? }?) => Promise` or `(...agents) => Promise` | Register definitions. No execution, no workers. | +| `plan` | `(agent) => Promise` | Compile to a workflow definition without executing. | +| `serve` | `(...agents, options?: ServeOptions) => Promise` | Deploy, register workers, poll. Blocks until SIGINT/SIGTERM unless `{ blocking: false }`. | +| `getStatus` | `(executionId, signal?) => Promise` | Current status. | +| `schedulesClient` | `() => SchedulerClient` | Schedule lifecycle. | +| `shutdown` | `() => Promise` | Stop worker polling. | + +`agent` is an `Agent` or a detected framework object. See +[../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md) for which verb to +use. + +## Connection resolution + +Precedence (spec R3): + +1. `CONDUCTOR_SERVER_URL` / `CONDUCTOR_AUTH_KEY` / `CONDUCTOR_AUTH_SECRET` +2. explicit `configuration` values +3. `CONDUCTOR_AGENT_SERVER_URL` / `CONDUCTOR_AGENT_AUTH_KEY` / `CONDUCTOR_AGENT_AUTH_SECRET` +4. the deprecated `AGENTSPAN_*` spelling of (3), which warns once per name +5. `http://localhost:8080` + +Note that the core `CONDUCTOR_*` env vars outrank an explicit `configuration` — +that is deliberate, so an operator can redirect a deployed application without a +code change. + +## AgentConfigOptions + +Behavior only. Every field falls back to an env var, then a default; explicit +values win. + +```ts +interface AgentConfigOptions { + workerPollIntervalMs?: number; // CONDUCTOR_AGENT_WORKER_POLL_INTERVAL (100) + workerThreadCount?: number; // CONDUCTOR_AGENT_WORKER_THREADS (1) + autoStartWorkers?: boolean; // CONDUCTOR_AGENT_AUTO_START_WORKERS (true) + streamingEnabled?: boolean; // CONDUCTOR_AGENT_STREAMING_ENABLED (true) + livenessEnabled?: boolean; // CONDUCTOR_AGENT_LIVENESS_ENABLED (true) + livenessStallSeconds?: number; // CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS (30) + livenessCheckIntervalSeconds?: number; // CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS (10) +} +``` + +`AgentConfig.fromEnv()` is equivalent to `new AgentConfig()`. The `AGENTSPAN_*` +spelling of each var still resolves as a deprecated fallback — see +[../../upgrading.md](../../upgrading.md). + +## Module-level singleton + +For scripts, a shared runtime is exposed as module functions: `configure`, `run`, +`start`, `stream`, `deploy`, `plan`, `serve`, `shutdown`. + +```ts +import { configure, run, shutdown } from '@io-orkes/conductor-javascript/agents'; + +configure({ serverUrl: 'http://localhost:8080/api' }); +const result = await run(agent, 'hi'); +await shutdown(); +``` + +Convenient for one-file scripts. Prefer an explicit `AgentRuntime` in a service, +where two components sharing hidden global state is a liability. + +## RunOptions + +Passed to `run` / `start` / `stream`: + +| Field | Notes | +|---|---| +| `runSettings` | Per-run LLM overrides — see below. | +| `model` | Sugar for `runSettings.model`. An explicit `runSettings.model` wins. | +| `plan` | A deterministic static `Plan`, which overrides the planner's output. | +| `credentials` | Extra secret names for this run. | +| `timeoutSeconds` | Execution deadline. | + +`RunSettings` overrides the LLM call for a single run without touching the agent's +config. Only set fields override, and the override does **not** cascade to +sub-agents — each keeps its own settings. + +```ts +const result = await runtime.run(agent, prompt, { + runSettings: { + model: 'anthropic/claude-sonnet-4-6', + temperature: 0.2, + maxTokens: 4096, + reasoningEffort: 'high', + thinkingBudgetTokens: 8000, // maps to the wire thinkingConfig shape + }, +}); +``` + +## Cleanup + +`await runtime.shutdown()` in a `finally`. It stops worker polling; a process with +registered tools will not exit without it. Stateful runs create domain-scoped +workers, so leaking runtimes leaks workers. + +## Next steps + +[client.md](client.md) — the control plane · +[agent-definition.md](agent-definition.md) · +[../concepts/deploy-serve-run.md](../concepts/deploy-serve-run.md) diff --git a/docs/agents/writing-agents.md b/docs/agents/writing-agents.md index 383d6afe..dda2850d 100644 --- a/docs/agents/writing-agents.md +++ b/docs/agents/writing-agents.md @@ -1,471 +1,13 @@ -# Writing Agents - -Everything you author is an `Agent`. A simple LLM agent, a tool-using agent, and a multi-agent orchestration are all the same `Agent` class with different options. This page walks the authoring surface. - -All snippets import from `@io-orkes/conductor-javascript/agents` and assume a runtime: - -```ts -import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; -const runtime = new AgentRuntime(); -``` - -## Defining an agent - -```ts -const agent = new Agent({ - name: 'greeter', // required; must match /^[a-zA-Z][a-zA-Z0-9_-]*$/ - model: 'anthropic/claude-sonnet-4-6', // provider/model string - instructions: 'Keep answers short.', - temperature: 0.7, - maxTurns: 25, // default 25 - maxTokens: 2048, - timeoutSeconds: 0, // 0 = server default -}); -``` - -There is also a functional form, `agent(fn, options)`, where `fn` is the dynamic-instructions callable (see below): - -```ts -import { agent } from '@io-orkes/conductor-javascript/agents'; - -const a = agent(() => 'You are a helpful assistant.', { - name: 'helper', - model: 'anthropic/claude-sonnet-4-6', -}); -``` - -### Instructions - -Instructions can be a plain string, a callable, or a server-managed prompt template. - -```ts -// Static -new Agent({ name: 'a', model, instructions: 'You are concise.' }); - -// Dynamic (callable) — evaluated to a string when the agent is serialized -new Agent({ name: 'a', model, instructions: () => `Today is ${new Date().toDateString()}.` }); - -// Server-managed prompt template (referenced by name + version) -import { PromptTemplate } from '@io-orkes/conductor-javascript/agents'; -new Agent({ - name: 'a', - model, - instructions: new PromptTemplate('support_greeting', { brand: 'Acme' }, 1), -}); -``` - -## Tools - -### Local tools — `tool()` - -`tool()` wraps an async function. Pass a Zod schema **or** a plain JSON Schema object for `inputSchema`. The function runs locally as a Conductor worker that the runtime polls; the runtime registers and polls it automatically on `run()` / `serve()`. - -```ts -const getWeather = tool( - async (args: { city: string }) => { - return { city: args.city, tempC: 21, conditions: 'sunny' }; - }, - { - name: 'get_weather', - description: 'Get the current weather for a city.', - inputSchema: { - type: 'object', - properties: { city: { type: 'string', description: 'City name' } }, - required: ['city'], - }, - }, -); - -const agent = new Agent({ - name: 'weather_agent', - model: 'anthropic/claude-sonnet-4-6', - instructions: 'Answer weather questions using the tool.', - tools: [getWeather], -}); -``` - -`tool()` options: `name`, `description`, `inputSchema`, `outputSchema?`, `approvalRequired?`, `timeoutSeconds?`, `external?`, `credentials?`, `guardrails?`, `maxCalls?`, `retryCount?`, `retryDelaySeconds?`, `retryPolicy?`. - -The tool function receives an optional second argument, the [`ToolContext`](api-reference.md#toolcontext) (`sessionId`, `executionId`, `agentName`, `metadata`, `dependencies`, and a mutable `state`). See [Stateful agents](#stateful-agents). - -**No per-run mutable capture.** A `tool()` handler is registered once and its Conductor worker is reused across concurrent runs and (for framework-spawned agents) concurrent process-local executors — never re-created per run. Don't close over per-run mutable state in the handler itself (a module-level counter, an array pushed to across calls, a captured `let` reassigned mid-run); two runs executing the same tool concurrently would corrupt each other's state. Everything a handler needs that varies per run belongs in `ToolContext` (`state` for durable per-execution data, `dependencies` for injected collaborators) or in the function's own arguments — never in a closure variable mutated across invocations. Tool and agent factories otherwise take plain data (JSON-serializable configs), so building one is always safe to repeat. - -### Tool discovery — `@Tool` / `toolsFrom` - -Decorate methods on a class and extract them, bound to the instance: - -```ts -import { Tool, toolsFrom } from '@io-orkes/conductor-javascript/agents'; - -class MathTools { - @Tool({ description: 'Add two numbers.', inputSchema: { - type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a', 'b'], - }}) - async add(args: { a: number; b: number }) { return { sum: args.a + args.b }; } -} - -const tools = toolsFrom(new MathTools()); // ToolFunction[] -new Agent({ name: 'calc', model, tools }); -``` - -> `@Tool`/`@AgentDec` are TypeScript experimental decorators — set `"experimentalDecorators": true` in your `tsconfig.json`. - -### Built-in tools - -These return a `ToolDef` that runs server-side (no local worker). Add them to `tools: [...]`. - -| Builder | Tool type | Purpose | -|---|---|---| -| `httpTool({ name, description, url, method?, headers?, inputSchema?, credentials? })` | `http` | Call an HTTP endpoint. | -| `mcpTool({ serverUrl, name?, description?, headers?, toolNames?, maxTools?, credentials? })` | `mcp` | Expose an MCP server's tools. | -| `apiTool({ url, name?, description?, headers?, toolNames?, maxTools?, credentials? })` | `api` | Expose an OpenAPI/API as tools. | -| `agentTool(agent, { name?, description?, retryCount?, retryDelaySeconds?, optional? })` | `agent_tool` | Call another `Agent` as a tool (sub-agent). | -| `humanTool({ name, description, inputSchema? })` | `human` | Pause for human input (HITL). | -| `imageTool({ name, description, llmProvider, model, style?, size? })` | `generate_image` | Generate images. | -| `audioTool({ name, description, llmProvider, model, voice?, speed?, format? })` | `generate_audio` | Text-to-speech. | -| `videoTool({ name, description, llmProvider, model, duration?, resolution?, fps?, ... })` | `generate_video` | Generate video. | -| `pdfTool({ name?, description?, pageSize?, theme?, fontSize? })` | `generate_pdf` | Render markdown to PDF. | -| `waitForMessageTool({ name, description, batchSize?, blocking? })` | `pull_workflow_messages` | Dequeue messages from the workflow message queue. | -| `searchTool({ name, description, vectorDb, index, embeddingModelProvider, embeddingModel, namespace?, maxResults? })` | `rag_search` | RAG vector search. | -| `indexTool({ name, description, vectorDb, index, embeddingModelProvider, embeddingModel, namespace?, chunkSize?, chunkOverlap? })` | `rag_index` | RAG index/ingest. | - -```ts -import { httpTool, mcpTool } from '@io-orkes/conductor-javascript/agents'; - -const agent = new Agent({ - name: 'researcher', - model: 'anthropic/claude-sonnet-4-6', - tools: [ - httpTool({ - name: 'get_user', - description: 'Fetch a user by id.', - url: 'https://api.example.com/users/{id}', - method: 'GET', - }), - mcpTool({ serverUrl: 'https://mcp.example.com/sse', toolNames: ['search'] }), - ], -}); -``` - -#### `waitForMessageTool` — workflow message queue - -`waitForMessageTool` lets a running agent dequeue messages pushed into its workflow message queue (Conductor `PULL_WORKFLOW_MESSAGES`). No worker is needed — the server handles it. In blocking mode (default) the task stays in progress until a message arrives. - -```ts -import { waitForMessageTool } from '@io-orkes/conductor-javascript/agents'; - -const agent = new Agent({ - name: 'inbox_agent', - model: 'anthropic/claude-sonnet-4-6', - instructions: 'When asked to wait, call wait_for_message and process what arrives.', - tools: [waitForMessageTool({ - name: 'wait_for_message', - description: 'Wait for the next inbound message.', - batchSize: 1, // up to 100; default 1 - blocking: true, // default true - })], -}); -``` - -#### `agentTool` — agent as a tool - -```ts -import { agentTool } from '@io-orkes/conductor-javascript/agents'; - -const translator = new Agent({ name: 'translator', model, instructions: 'Translate to French.' }); - -const orchestrator = new Agent({ - name: 'orchestrator', - model, - instructions: 'Use the translator tool when asked to translate.', - tools: [agentTool(translator, { description: 'Translate text to French.' })], -}); -``` - -## Multi-agent strategies - -Set `agents: [...]` and a `strategy`. Strategies: `'sequential'`, `'parallel'`, `'handoff'`, `'router'`, `'round_robin'`, `'random'`, `'swarm'`, `'manual'`, `'plan_execute'`. - -```ts -// Sequential — agents run in order. .pipe() is sugar for strategy: 'sequential'. -const pipeline = writer.pipe(editor); -// equivalent to: -// new Agent({ name: 'writer_editor', agents: [writer, editor], strategy: 'sequential' }); - -// Parallel — agents run concurrently, results gathered -const team = new Agent({ name: 'research_team', agents: [webResearcher, dataAnalyst], strategy: 'parallel' }); - -// Handoff — the parent LLM delegates to sub-agents (they appear as callable tools) -const support = new Agent({ - name: 'support', - model, - instructions: 'Route to the right specialist.', - agents: [billingAgent, technicalAgent, salesAgent], - strategy: 'handoff', -}); - -// Router — a router agent (or function) picks the sub-agent -const routed = new Agent({ - name: 'router', - agents: [a, b], - strategy: 'router', - router: routerAgent, // an Agent or (…) => string returning a sub-agent name -}); -``` - -`scatterGather({ name, workers, ... })` is a convenience builder that returns a coordinator agent which fans a problem out to worker agents in parallel and synthesizes the results: - -```ts -import { scatterGather } from '@io-orkes/conductor-javascript/agents'; -const coordinator = scatterGather({ name: 'fanout', workers: [worker], retryCount: 2 }); -``` - -## Handoffs - -For `swarm`/`handoff` strategies you can declare explicit handoff transitions with `handoffs: [...]`. Each condition has a `target` (a sub-agent name). - -```ts -import { OnTextMention, OnToolResult, OnCondition } from '@io-orkes/conductor-javascript/agents'; - -const team = new Agent({ - name: 'coding_team', - model, - agents: [pythonExpert, jsExpert], - strategy: 'swarm', - handoffs: [ - // Hand off when the output mentions text (case-insensitive) - new OnTextMention({ target: 'python_expert', text: 'Python' }), - - // Hand off when a specific tool returns (optionally only if result contains text) - new OnToolResult({ target: 'escalation', toolName: 'detect_severity', resultContains: 'critical' }), - - // Hand off when a custom predicate returns true (runs as a worker task) - new OnCondition({ target: 'fallback', condition: (ctx) => ctx.result.length > 1000 }), - ], -}); -``` - -You can also constrain which transitions are allowed with `allowedTransitions: { agentName: ['otherAgent', ...] }`. - -## Guardrails - -Guardrails validate input or output. Attach them at the agent level (`guardrails: [...]`) or per-tool (`tool(fn, { guardrails: [...] })`). Each has a `position` (`'input'` | `'output'`, default `'output'`) and an `onFail` policy (`'raise'` | `'retry'` | `'fix'` | `'human'`, default `'raise'`). - -```ts -import { guardrail, RegexGuardrail, LLMGuardrail } from '@io-orkes/conductor-javascript/agents'; - -// Regex (runs on the server, no worker) -const noSecrets = new RegexGuardrail({ - name: 'no_api_keys', - patterns: ['sk-[A-Za-z0-9]{20,}'], - mode: 'block', // 'block' fails if any pattern matches; 'allow' fails if none match - onFail: 'raise', - message: 'Output contained a secret.', -}); - -// LLM (server-side LLM judge) -const policy = new LLMGuardrail({ - name: 'safety', - model: 'anthropic/claude-sonnet-4-6', - policy: 'Reject any content that gives medical dosage advice.', - position: 'output', - onFail: 'retry', - maxRetries: 2, -}); - -// Custom (your function, runs locally as a worker) -const minLength = guardrail( - (content: string) => ({ passed: content.length >= 10, message: 'Too short' }), - { name: 'min_length', position: 'output', onFail: 'fix' }, -); - -const agent = new Agent({ - name: 'safe_agent', - model, - instructions: '…', - guardrails: [noSecrets.toGuardrailDef?.() ?? noSecrets, policy.toGuardrailDef?.() ?? policy, minLength], -}); -``` - -`RegexGuardrail` / `LLMGuardrail` are class instances; the serializer accepts the instance directly. There is also a `guardrail.external({ name, position?, onFail? })` form for guardrails handled by a remote worker, and a `@Guardrail` decorator with `guardrailsFrom(instance)`. - -## Termination + TextGate - -Termination conditions decide when a multi-turn / multi-agent loop should stop. Pass one to `termination:`. They compose with `.and()` / `.or()` (or the variadic `AndCondition` / `OrCondition`). - -```ts -import { TextMention, MaxMessage, TokenUsageCondition, StopMessage } from '@io-orkes/conductor-javascript/agents'; - -const agent = new Agent({ - name: 'debate', - model, - agents: [a, b], - strategy: 'round_robin', - termination: new TextMention('TERMINATE') // stop when output mentions text - .or(new MaxMessage(10)) // …or after 10 messages - .or(new TokenUsageCondition({ maxTotalTokens: 50000 })), -}); -``` - -Available conditions: `TextMention(text, caseSensitive?)`, `StopMessage(stopMessage)`, `MaxMessage(maxMessages)`, `TokenUsageCondition({ maxTotalTokens?, maxPromptTokens?, maxCompletionTokens? })`, and the composites `AndCondition(...)` / `OrCondition(...)`. - -`TextGate` and `gate()` gate transitions (e.g. on `gate:`): - -```ts -import { TextGate } from '@io-orkes/conductor-javascript/agents'; -new Agent({ name: 'a', model, gate: new TextGate({ text: 'APPROVED', caseSensitive: false }) }); -``` - -## Callbacks - -Subclass `CallbackHandler` and override the lifecycle hooks you care about. Each hook runs as a server-registered worker. - -```ts -import { CallbackHandler } from '@io-orkes/conductor-javascript/agents'; - -class Logger extends CallbackHandler { - async onAgentStart(agentName: string, prompt: string) { console.log('[start]', agentName, prompt); } - async onToolStart(agentName: string, toolName: string, args: unknown) { console.log('[tool]', toolName, args); } - async onAgentEnd(agentName: string, result: unknown) { console.log('[end]', agentName); } -} - -const agent = new Agent({ name: 'a', model, instructions: '…', callbacks: [new Logger()] }); -``` - -Hooks: `onAgentStart`, `onAgentEnd`, `onModelStart`, `onModelEnd`, `onToolStart`, `onToolEnd`. - -## Streaming - -`runtime.stream(agent, prompt)` returns an `AgentStream` you can `for await` over. Events have a `type` (`'thinking'`, `'tool_call'`, `'tool_result'`, `'waiting'`, `'handoff'`, `'message'`, `'done'`, ...). You can also `runtime.start(...)` and call `handle.stream()`. - -```ts -const stream = await runtime.stream(agent, 'Plan a 3-day trip to Tokyo.'); -for await (const event of stream) { - if (event.type === 'thinking') console.log('[thinking]', event.content); - else if (event.type === 'tool_call') console.log('[tool]', event.toolName, event.args); - else if (event.type === 'tool_result') console.log('[result]', event.toolName, event.result); - else if (event.type === 'done') console.log('[done]', event.output); -} -const result = await stream.getResult(); // terminal AgentResult after the stream ends -``` - -## Human-in-the-loop (HITL) - -A tool with `approvalRequired: true`, or a `humanTool`, pauses execution and emits a `waiting` event. Resolve it via the handle / stream: `approve(output?)`, `reject(reason?)`, `send(message)`, or `respond(body)`. - -```ts -const deleteData = tool( - async (args: { table: string }) => ({ deleted: args.table }), - { - name: 'delete_data', - description: 'Delete a table. Destructive — requires approval.', - inputSchema: { type: 'object', properties: { table: { type: 'string' } }, required: ['table'] }, - approvalRequired: true, - }, -); - -const agent = new Agent({ name: 'ops', model, tools: [deleteData], instructions: '…' }); - -const handle = await runtime.start(agent, 'Delete the stale_cache table.'); -for await (const event of handle.stream()) { - if (event.type === 'waiting') { - // The waiting event carries the pending tool batch on event.pendingTool, - // or fetch the full status: - const status = await handle.getStatus(); - console.log('Approval needed for:', status.pendingTool?.toolCalls); - - await handle.approve(); // approve, or: - // await handle.reject('Not allowed'); - // await handle.respond({ approved: true, note: 'go ahead' }); - } else if (event.type === 'done') { - console.log('done', event.output); - } -} -``` - -One HUMAN task gates the whole batch of pending tool calls with a single `{ approved, reason }` verdict — iterate `pendingTool.toolCalls` to see every tool covered. The `pendingTool` is mirrored onto the `waiting` event so you can read it without a `getStatus()` round-trip. - -`humanTool` works the same way but lets the LLM ask the human a structured question; the response schema is on `pendingTool.response_schema`. - -## Schedules - -Attach cron schedules to an agent at deploy time. Reconciliation is declarative: a list upserts those and prunes the rest; `[]` purges all; omitting `schedules` leaves them untouched. - -```ts -import { Agent, AgentRuntime, Schedule, schedules } from '@io-orkes/conductor-javascript/agents'; - -const digest = new Agent({ name: 'eng_digest', model, instructions: 'Write a digest.' }); - -await runtime.deploy(digest, { - schedules: [ - new Schedule({ - name: 'weekday-9am', - cron: '0 0 9 * * MON-FRI', - timezone: 'America/Los_Angeles', - input: { channel: '#eng' }, - description: 'Weekday morning digest', - }), - ], -}); - -// Inspect / control via the `schedules` namespace -const infos = await schedules.list({ agent: digest.name }); -await schedules.pause(infos[0].name, { reason: 'cooldown' }); -await schedules.resume(infos[0].name); -const execId = await schedules.runNow(infos[0].name); -const next = await schedules.previewNext('0 0 9 * * MON-FRI', { n: 5 }); - -await runtime.deploy(digest, { schedules: [] }); // purge all -``` - -Lifecycle calls (`get`/`pause`/`resume`/`delete`/`runNow`) key on the **wire name** (the prefixed `name` returned in `ScheduleInfo`), not the short name you supplied. The `AgentClient` also has `schedule(agent, schedules)` (see [advanced.md](advanced.md#agentclient--control-plane)). - -## Agent-from-method (`@AgentDec` / `agentsFrom`) - -Define agents as decorated methods on a class and extract them: - -```ts -import { AgentDec, agentsFrom } from '@io-orkes/conductor-javascript/agents'; - -class MyAgents { - @AgentDec({ name: 'summarizer', model: 'anthropic/claude-sonnet-4-6', instructions: 'Summarize text.' }) - summarize() {} - - @AgentDec({ name: 'classifier', model: 'anthropic/claude-sonnet-4-6', instructions: 'Classify text.' }) - classify() {} -} - -const [summarizer, classifier] = agentsFrom(new MyAgents()); // Agent[] -``` - -## Stateful agents - -Set `stateful: true` on an agent (or `stateful: true` on a tool def) to isolate tool workers per execution via a unique domain UUID. Within a single run, tools share a mutable `context.state` object; mutations are captured and propagated between tool calls. - -```ts -import type { ToolContext } from '@io-orkes/conductor-javascript/agents'; - -const addItem = tool( - async (args: { item: string }, ctx?: ToolContext) => { - const items: string[] = (ctx?.state?.list as string[]) ?? []; - items.push(args.item); - if (ctx?.state) ctx.state.list = items; - return { total: items.length }; - }, - { name: 'add_item', description: 'Add an item.', inputSchema: { - type: 'object', properties: { item: { type: 'string' } }, required: ['item'], - }}, -); - -const agent = new Agent({ name: 'list_agent', model, tools: [addItem], stateful: true }); -``` - -### Liveness monitoring - -For a stateful run (one with a domain-isolated worker), `runtime.start()`/`run()`/`stream()` also start a liveness monitor: it polls the execution's workflow every `livenessCheckIntervalSeconds` and, if a `SCHEDULED`/`IN_PROGRESS` task in that run's domain sits unpolled (`pollCount === 0`) for longer than `livenessStallSeconds`, a blocking `wait()` rejects with `WorkerStallError` instead of hanging forever — the signal that the local worker process for this run's domain died. The monitor stops on terminal status or handle disposal and never keeps the process alive on its own. Configure via `AgentConfig`/env: `livenessEnabled` (`AGENTSPAN_LIVENESS_ENABLED`, default `true`), `livenessStallSeconds` (`AGENTSPAN_LIVENESS_STALL_SECONDS`, default `30`), `livenessCheckIntervalSeconds` (`AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS`, default `10`). Framework-spawned agents (LangGraph/LangChain/Vercel AI wrappers) never route through a per-run domain, so liveness monitoring doesn't apply to them. - -## Next - -- [framework-agents.md](framework-agents.md) — run OpenAI / ADK / LangChain / LangGraph / Vercel AI agents. -- [advanced.md](advanced.md) — deploy/serve, control plane, structured output, credentials, plans, skills. -- [api-reference.md](api-reference.md) — full public surface. +# Writing Conductor agents + +This page has been split into one guide per concept: + +- [Agents and instructions](concepts/agents.md) +- [Tools](concepts/tools.md) +- [Multi-agent orchestration and handoffs](concepts/multi-agent.md) +- [Guardrails](concepts/guardrails.md) +- [Termination and gates](concepts/termination.md) +- [Callbacks](concepts/callbacks.md) +- [Streaming and human-in-the-loop](concepts/streaming-hitl.md) +- [Scheduling](concepts/scheduling.md) +- [Stateful agents and liveness](concepts/stateful.md) diff --git a/docs/api-map.md b/docs/api-map.md new file mode 100644 index 00000000..59e688c1 --- /dev/null +++ b/docs/api-map.md @@ -0,0 +1,79 @@ +# API map + +**Audience:** developers looking for which client owns a given operation. + +## Prerequisites + +A client ([connection-authentication.md](connection-authentication.md)). +`OrkesClients` is the factory — build it once and take the domain clients you +need, so they share one underlying client and one token mint. + +```ts +const clients = await OrkesClients.from(); +``` + +## Clients + +| Getter | Client | Owns | +|---|---|---| +| `getWorkflowClient()` | Workflow | Start, execute, pause, resume, terminate, retry, restart, search, signal. | +| `getTaskClient()` | Task | Poll, update, task logs, queue sizes. | +| `getMetadataClient()` | Metadata | Register and read workflow and task definitions. | +| `getSchedulerClient()` | Scheduler | Cron schedule lifecycle. | +| `getEventClient()` | Event | Event handlers. | +| `getHumanClient()` | Human task | Human task claim, complete, search. | +| `getSchemaClient()` | Schema | Schema registration and lookup. | +| `getSecretClient()` | Secret | Secret store CRUD. | +| `getApplicationClient()` | Application | Applications, access keys, roles. | +| `getAuthorizationClient()` | Authorization | Grants and permissions. | +| `getIntegrationClient()` | Integration | LLM and vector-DB integrations. | +| `getPromptClient()` | Prompt | Server-managed prompt templates. | +| `getTemplateClient()` | Template | Workflow templates. | +| `getServiceRegistryClient()` | Service registry | Registered services. | +| `getAgentClient()` | Agent | The `/agent/*` control plane. See [agents/reference/client.md](agents/reference/client.md). | +| `getAgentWorkflowClient()` | Agent workflow | Read-only agent execution reads. | + +`getClient()` returns the underlying `ConductorClient` — pass it to +`new TaskHandler({ client })` or `new AgentRuntime(client)`. + +## By goal + +| I want to… | Use | +|---|---| +| Register a workflow definition | `ConductorWorkflow.register()` or `getMetadataClient()` | +| Register a task definition | `getMetadataClient().registerTask()` | +| Start a workflow and not wait | `getWorkflowClient().startWorkflow()` | +| Start a workflow and wait | `workflow.execute()` | +| Control a running execution | `getWorkflowClient()` — pause / resume / terminate / retry | +| Read an execution with tasks | `getWorkflowClient().getExecution()` | +| Search executions | `getWorkflowClient().search()` | +| Run workers | `TaskHandler` + `@worker` — see [workers.md](workers.md) | +| Complete a WAIT task | `getWorkflowClient().signal()` | +| Manage a cron schedule | `getSchedulerClient()` | +| Store a secret | `getSecretClient()` | +| Enforce a task input schema | `getSchemaClient()` — see [schema-client.md](schema-client.md) | +| Run a Conductor agent | `AgentRuntime` — see [agents/README.md](agents/README.md) | + +## Per-client reference + +Detailed per-client method documentation lives under `docs/api-reference/`, which +now forwards here. The generated TypeDoc surface is the authoritative signature +reference: + +```shell +npm run generate-docs +``` + +## Two pairs worth not confusing + +- **`getWorkflow()` vs `getExecution()`** on the workflow client hit different + endpoints and return different shapes. Use `getExecution()` when you need + task-level detail. +- **`getAgentClient()` vs `AgentRuntime`** — the client is control plane only and + does not poll local tool workers. See + [agents/reference/api.md](agents/reference/api.md#runtime-vs-client). + +## Next steps + +[workflows.md](workflows.md) · [workers.md](workers.md) · +[agents/reference/api.md](agents/reference/api.md) diff --git a/docs/api-reference/application-client.md b/docs/api-reference/application-client.md index c5cd4841..434a3796 100644 --- a/docs/api-reference/application-client.md +++ b/docs/api-reference/application-client.md @@ -1,565 +1,10 @@ -# ApplicationClient API Reference +# Application Client -The `ApplicationClient` manages applications in Conductor. Applications are security entities that can be granted access keys and roles to interact with Conductor workflows and tasks. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor - -### `new ApplicationClient(client: Client)` - -Creates a new `ApplicationClient`. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `getAllApplications(): Promise` - -Gets all applications registered in Conductor. - -**Returns:** - -- `Promise`: An array of all applications. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Get all applications -const applications = await appClient.getAllApplications(); -console.log(`Found ${applications.length} applications`); -``` - ---- - -### `createApplication(applicationName: string): Promise` - -Creates a new application. - -**Parameters:** - -- `applicationName` (`string`): The name of the application to create. - -**Returns:** - -- `Promise`: The created application. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Create a new application -const app = await appClient.createApplication("my-service"); -console.log(`Created application: ${app.id}`); -``` - ---- - -### `getApplication(applicationId: string): Promise` - -Gets an application by its ID. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. - -**Returns:** - -- `Promise`: The application. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Get a specific application -const app = await appClient.getApplication("app-123"); -console.log(`Application name: ${app.name}`); -``` - ---- - -### `getAppByAccessKeyId(accessKeyId: string): Promise` - -Gets an application by its access key ID. - -**Parameters:** - -- `accessKeyId` (`string`): The access key ID. - -**Returns:** - -- `Promise`: The application associated with the access key. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Get application by access key -const app = await appClient.getAppByAccessKeyId("key-123"); -console.log(`Application: ${app.name}`); -``` - ---- - -### `updateApplication(applicationId: string, newApplicationName: string): Promise` - -Updates an application's name. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application to update. -- `newApplicationName` (`string`): The new name for the application. - -**Returns:** - -- `Promise`: The updated application. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Update application name -const app = await appClient.updateApplication("app-123", "my-service-v2"); -console.log(`Updated application name to: ${app.name}`); -``` - ---- - -### `deleteApplication(applicationId: string): Promise` - -Deletes an application. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application to delete. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Delete an application -await appClient.deleteApplication("app-123"); -console.log("Application deleted"); -``` - ---- - -### `getAccessKeys(applicationId: string): Promise` - -Gets all access keys for an application. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. - -**Returns:** - -- `Promise`: An array of access key information. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Get access keys for an application -const keys = await appClient.getAccessKeys("app-123"); -console.log(`Found ${keys.length} access keys`); -keys.forEach((key) => { - console.log(`Key ${key.id}: ${key.status}`); -}); -``` - ---- - -### `createAccessKey(applicationId: string): Promise` - -Creates a new access key for an application. - -**Important:** Save the access key secret immediately after creation - it cannot be retrieved later. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. - -**Returns:** - -- `Promise`: The created access key with its secret. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Create a new access key -const accessKey = await appClient.createAccessKey("app-123"); -console.log(`Key ID: ${accessKey.id}`); -console.log(`Key Secret: ${accessKey.secret}`); // Save this immediately! -``` - ---- - -### `deleteAccessKey(applicationId: string, keyId: string): Promise` - -Deletes an access key. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `keyId` (`string`): The ID of the access key to delete. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Delete an access key -await appClient.deleteAccessKey("app-123", "key-456"); -console.log("Access key deleted"); -``` - ---- - -### `toggleAccessKeyStatus(applicationId: string, keyId: string): Promise` - -Toggles the status of an access key between `ACTIVE` and `INACTIVE`. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `keyId` (`string`): The ID of the access key. - -**Returns:** - -- `Promise`: The updated access key information. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Toggle access key status -const keyInfo = await appClient.toggleAccessKeyStatus("app-123", "key-456"); -console.log(`Access key is now ${keyInfo.status}`); -``` - ---- - -### `addApplicationRole(applicationId: string, role: ApplicationRole): Promise` - -Adds a role to an application user. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `role` (`ApplicationRole`): The role to add. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient, ApplicationRole } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Add a role to the application -await appClient.addApplicationRole("app-123", "WORKFLOW_MANAGER"); -console.log("Role added"); - -// You can also use the ApplicationRole type -const role: ApplicationRole = "METADATA_MANAGER"; -await appClient.addApplicationRole("app-123", role); -``` - ---- - -### `removeRoleFromApplicationUser(applicationId: string, role: string): Promise` - -Removes a role from an application user. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `role` (`string`): The role to remove. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Remove a role from the application -await appClient.removeRoleFromApplicationUser("app-123", "WORKFLOW_EXECUTOR"); -console.log("Role removed"); -``` - ---- - -### `getApplicationTags(applicationId: string): Promise` - -Gets all tags associated with an application. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. - -**Returns:** - -- `Promise`: An array of tags. - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Get tags for an application -const tags = await appClient.getApplicationTags("app-123"); -console.log(`Application has ${tags.length} tags`); -``` - ---- - -### `addApplicationTags(applicationId: string, tags: Tag[]): Promise` - -Adds multiple tags to an application (replaces existing tags). - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `tags` (`Tag[]`): An array of tags to add. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Add tags to an application -await appClient.addApplicationTags("app-123", [ - { key: "environment", value: "production" }, - { key: "team", value: "backend" }, - { key: "service", value: "payment" }, -]); -``` - ---- - -### `addApplicationTag(applicationId: string, tag: Tag): Promise` - -Adds a single tag to an application. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `tag` (`Tag`): The tag to add. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Add a single tag -await appClient.addApplicationTag("app-123", { - key: "version", - value: "2.0", -}); -``` - ---- - -### `deleteApplicationTags(applicationId: string, tags: Tag[]): Promise` - -Deletes multiple tags from an application. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `tags` (`Tag[]`): An array of tags to delete. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Delete multiple tags -await appClient.deleteApplicationTags("app-123", [ - { key: "environment", value: "production" }, - { key: "team", value: "backend" }, -]); -``` - ---- - -### `deleteApplicationTag(applicationId: string, tag: Tag): Promise` - -Deletes a specific tag from an application. - -**Parameters:** - -- `applicationId` (`string`): The ID of the application. -- `tag` (`Tag`): The tag to delete (must match both `key` and `value`). - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { ApplicationClient } from "@io-orkes/conductor-javascript"; - -const appClient = new ApplicationClient(client); - -// Delete a specific tag -await appClient.deleteApplicationTag("app-123", { - key: "version", - value: "2.0", -}); -``` - ---- - -## Type Definitions - -### `ExtendedConductorApplication` - -```typescript -export interface ExtendedConductorApplication { - id: string; - name: string; - createdBy: string; - createTime: number; - updatedBy: string; - updateTime: number; - tags?: Array; -}; -``` - -### `AccessKey` - -```typescript -export interface AccessKey { - id: string; - secret: string; -}; -``` - -### `AccessKeyInfo` - -```typescript -export interface AccessKeyInfo { - id: string; - createdAt: number; - status: "ACTIVE" | "INACTIVE"; -}; -``` - -### `Tag` - -```typescript -export type Tag = { - key?: string; - /** - * @deprecated - */ - type?: string; - value?: string; -}; -``` - -### `ApplicationRole` - -Defines the available roles that can be assigned to an application. - -```typescript -export type ApplicationRole = - | "ADMIN" - | "UNRESTRICTED_WORKER" - | "METADATA_MANAGER" - | "WORKFLOW_MANAGER" - | "APPLICATION_MANAGER" - | "USER" - | "USER_READ_ONLY" - | "WORKER" - | "APPLICATION_CREATOR" - | "METADATA_API" - | "PROMPT_MANAGER"; -``` - -**Role Descriptions:** - -- `ADMIN` - Full administrative access to all resources -- `UNRESTRICTED_WORKER` - Can execute any task without restrictions -- `METADATA_MANAGER` - Can manage workflow and task definitions -- `WORKFLOW_MANAGER` - Can manage workflow executions -- `APPLICATION_MANAGER` - Can manage applications and access keys -- `USER` - Standard user access -- `USER_READ_ONLY` - Read-only access to resources -- `WORKER` - Can poll for and execute assigned tasks -- `APPLICATION_CREATOR` - Can create new applications -- `METADATA_API` - API access to metadata operations -- `PROMPT_MANAGER` - Can manage AI prompts and templates +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/event-client.md b/docs/api-reference/event-client.md index c5c9f0b1..0133350b 100644 --- a/docs/api-reference/event-client.md +++ b/docs/api-reference/event-client.md @@ -1,811 +1,10 @@ -# EventClient API Reference +# Event Client -The `EventClient` manages event handlers and event processing in Conductor. Event handlers allow you to automatically trigger actions (like starting workflows) when events are received. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor - -### `new EventClient(client: Client)` - -Creates a new `EventClient`. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `getAllEventHandlers(): Promise` - -Gets all event handlers registered in Conductor. - -**Returns:** - -- `Promise`: An array of all event handlers. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get all event handlers -const handlers = await eventClient.getAllEventHandlers(); -console.log(`Found ${handlers.length} event handlers`); -``` - ---- - -### `addEventHandler(eventHandler: EventHandler): Promise` - -Adds a single event handler. - -**Parameters:** - -- `eventHandler` (`EventHandler`): The event handler to add. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Add an event handler that starts a workflow when an event is received -await eventClient.addEventHandler({ - name: "order_created_handler", - event: "order.created", - active: true, - description: "Starts fulfillment workflow when order is created", - actions: [ - { - action: "start_workflow", - start_workflow: { - name: "fulfill_order", - version: 1, - input: { - orderId: "${event.orderId}", - }, - }, - }, - ], -}); -``` - ---- - -### `addEventHandlers(eventHandlers: EventHandler[]): Promise` - -Adds multiple event handlers at once. - -**Parameters:** - -- `eventHandlers` (`EventHandler[]`): An array of event handlers to add. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Add multiple event handlers -await eventClient.addEventHandlers([ - { - name: "order_created_handler", - event: "order.created", - active: true, - actions: [ - { - action: "start_workflow", - start_workflow: { - name: "fulfill_order", - version: 1, - }, - }, - ], - }, - { - name: "order_cancelled_handler", - event: "order.cancelled", - active: true, - actions: [ - { - action: "start_workflow", - start_workflow: { - name: "cancel_order", - version: 1, - }, - }, - ], - }, -]); -``` - ---- - -### `updateEventHandler(eventHandler: EventHandler): Promise` - -Updates an existing event handler. - -**Parameters:** - -- `eventHandler` (`EventHandler`): The updated event handler (must include the `name` field). - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Update an existing handler -await eventClient.updateEventHandler({ - name: "order_created_handler", - event: "order.created", - active: false, // Deactivate the handler - description: "Updated description", - actions: [ - { - action: "start_workflow", - start_workflow: { - name: "fulfill_order_v2", // Updated workflow name - version: 2, - }, - }, - ], -}); -``` - ---- - -### `getEventHandlerByName(eventHandlerName: string): Promise` - -Gets a specific event handler by name. - -**Parameters:** - -- `eventHandlerName` (`string`): The name of the event handler. - -**Returns:** - -- `Promise`: The event handler. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get a specific handler -const handler = await eventClient.getEventHandlerByName("order_created_handler"); -console.log(`Handler is ${handler.active ? "active" : "inactive"}`); -``` - ---- - -### `getEventHandlersForEvent(event: string, activeOnly?: boolean): Promise` - -Gets all event handlers registered for a specific event. - -**Parameters:** - -- `event` (`string`): The event name. -- `activeOnly` (`boolean`, optional): If `true`, only returns active handlers. Defaults to `false`. - -**Returns:** - -- `Promise`: An array of event handlers for the specified event. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get all handlers for an event -const handlers = await eventClient.getEventHandlersForEvent("order.created"); - -// Get only active handlers -const activeHandlers = await eventClient.getEventHandlersForEvent( - "order.created", - true -); -``` - ---- - -### `removeEventHandler(name: string): Promise` - -Removes an event handler by name. - -**Parameters:** - -- `name` (`string`): The name of the event handler to remove. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Remove an event handler -await eventClient.removeEventHandler("order_created_handler"); -``` - ---- - -### `handleIncomingEvent(data: Record): Promise` - -Handles an incoming event. This triggers all active event handlers registered for the event. - -**Parameters:** - -- `data` (`Record`): The event data. Must include an `event` field specifying the event name. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Publish an event -await eventClient.handleIncomingEvent({ - event: "order.created", - orderId: "ORDER-123", - customerId: "CUST-456", - amount: "99.99", - timestamp: Date.now().toString(), -}); -``` - ---- - -### `getTagsForEventHandler(name: string): Promise` - -Gets all tags associated with an event handler. - -**Parameters:** - -- `name` (`string`): The name of the event handler. - -**Returns:** - -- `Promise`: An array of tags. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get tags for a handler -const tags = await eventClient.getTagsForEventHandler("order_created_handler"); -console.log(`Handler has ${tags.length} tags`); -``` - ---- - -### `putTagForEventHandler(name: string, tags: Tag[]): Promise` - -Sets tags for an event handler (replaces existing tags). - -**Parameters:** - -- `name` (`string`): The name of the event handler. -- `tags` (`Tag[]`): An array of tags to set. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Set tags for a handler -await eventClient.putTagForEventHandler("order_created_handler", [ - { key: "environment", value: "production" }, - { key: "team", value: "fulfillment" }, - { key: "priority", value: "high" }, -]); -``` - ---- - -### `deleteTagForEventHandler(name: string, tag: Tag): Promise` - -Deletes a specific tag from an event handler. - -**Parameters:** - -- `name` (`string`): The name of the event handler. -- `tag` (`Tag`): The tag to delete (must match both `key` and `value`). - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Delete a specific tag -await eventClient.deleteTagForEventHandler("order_created_handler", { - key: "priority", - value: "high", -}); -``` - ---- - -### `deleteTagsForEventHandler(name: string, tags: Tag[]): Promise` - -Deletes multiple tags from an event handler. - -**Parameters:** - -- `name` (`string`): The name of the event handler. -- `tags` (`Tag[]`): An array of tags to delete. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Delete multiple tags -await eventClient.deleteTagsForEventHandler("order_created_handler", [ - { key: "priority", value: "high" }, - { key: "team", value: "fulfillment" }, -]); -``` - ---- - -### `getAllActiveEventHandlers(): Promise` - -Gets all active event handlers with execution information (execution view). - -**Returns:** - -- `Promise`: A search result containing active event handlers with execution data. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get all active handlers with execution info -const result = await eventClient.getAllActiveEventHandlers(); -console.log(`Found ${result.totalHits} active handlers`); -result.results?.forEach((handler) => { - console.log(`${handler.name}: ${handler.numberOfActions} actions`); -}); -``` - ---- - -### `getEventExecutions(eventHandlerName: string, from?: number): Promise` - -Gets execution history for a specific event handler. - -**Parameters:** - -- `eventHandlerName` (`string`): The name of the event handler. -- `from` (`number`, optional): Pagination cursor for retrieving more results. - -**Returns:** - -- `Promise`: An array of event executions. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get execution history for a handler -const executions = await eventClient.getEventExecutions("order_created_handler"); -executions.forEach((exec) => { - console.log(`Execution ${exec.id}: ${exec.status}`); -}); -``` - ---- - -### `getEventHandlersWithStats(from?: number): Promise` - -Gets all event handlers with statistics (messages view). - -**Parameters:** - -- `from` (`number`, optional): Pagination cursor for retrieving more results. - -**Returns:** - -- `Promise`: A search result containing event handlers with statistics. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get handlers with statistics -const result = await eventClient.getEventHandlersWithStats(); -result.results?.forEach((handler) => { - console.log( - `${handler.name}: ${handler.numberOfMessages} messages, ${handler.numberOfActions} actions` - ); -}); -``` - ---- - -### `getEventMessages(event: string, from?: number): Promise` - -Gets all messages for a specific event. - -**Parameters:** - -- `event` (`string`): The event name. -- `from` (`number`, optional): Pagination cursor for retrieving more results. - -**Returns:** - -- `Promise`: An array of event messages. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get all messages for an event -const messages = await eventClient.getEventMessages("order.created"); -messages.forEach((msg) => { - console.log(`Message ${msg.id}: ${msg.status}`); - console.log(`Payload:`, msg.fullPayload); -}); -``` - ---- - -### `testConnectivity(input: ConnectivityTestInput): Promise` - -Tests connectivity for a queue using a workflow with an EVENT task and an EventHandler. - -**Parameters:** - -- `input` (`ConnectivityTestInput`): The connectivity test configuration. - -**Returns:** - -- `Promise`: The test result. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Test connectivity -const result = await eventClient.testConnectivity({ - sink: "sqs:my-queue", - input: { - testKey: "testValue", - }, -}); - -console.log(`Test ${result.successful ? "passed" : "failed"}`); -if (!result.successful) { - console.log(`Reason: ${result.reason}`); -} -``` - ---- - -### `test(): Promise` - -Tests the event endpoint (as exposed by API). - -**Returns:** - -- `Promise`: A test event handler response. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Test the endpoint -const result = await eventClient.test(); -console.log("Test endpoint response:", result); -``` - ---- - -### `getAllQueueConfigs(): Promise<{ [key: string]: string }>` - -Gets all queue configurations. - -**Returns:** - -- `Promise<{ [key: string]: string }>`: A record of queue names. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get all queue configs -const queues = await eventClient.getAllQueueConfigs(); -console.log(`Found ${Object.keys(queues).length} queues`); -``` - ---- - -### `getQueueConfig(queueType: string, queueName: string): Promise>` - -Gets the configuration for a specific queue. - -**Parameters:** - -- `queueType` (`string`): The type of queue (e.g., `"sqs"`, `"kafka"`). -- `queueName` (`string`): The name of the queue. - -**Returns:** - -- `Promise>`: The queue configuration. - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Get queue config -const config = await eventClient.getQueueConfig("sqs", "my-queue"); -console.log("Queue config:", config); -``` - ---- - -### `putQueueConfig(queueType: string, queueName: string, config: string): Promise` - -Creates or updates a queue configuration by name. - -**Deprecated:** Prefer server's newer endpoints if available. - -**Parameters:** - -- `queueType` (`string`): The type of queue. -- `queueName` (`string`): The name of the queue. -- `config` (`string`): The queue configuration as a string. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Set queue config -await eventClient.putQueueConfig("sqs", "my-queue", '{"region": "us-east-1"}'); -``` - ---- - -### `deleteQueueConfig(queueType: string, queueName: string): Promise` - -Deletes a queue configuration. - -**Parameters:** - -- `queueType` (`string`): The type of queue. -- `queueName` (`string`): The name of the queue. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { EventClient } from "@io-orkes/conductor-javascript"; - -const eventClient = new EventClient(client); - -// Delete queue config -await eventClient.deleteQueueConfig("sqs", "my-queue"); -``` - ---- - -## Type Definitions - -### `EventHandler` - -```typescript -export type EventHandler = { - actions?: Array; - active?: boolean; - condition?: string; - createdBy?: string; - description?: string; - evaluatorType?: string; - event?: string; - name?: string; - orgId?: string; - tags?: Array; -}; -``` - -### `Action` - -```typescript -export type Action = { - action?: "start_workflow" | "complete_task" | "fail_task" | "terminate_workflow" | "update_workflow_variables"; - complete_task?: TaskDetails; - expandInlineJSON?: boolean; - fail_task?: TaskDetails; - start_workflow?: StartWorkflowRequest; - terminate_workflow?: TerminateWorkflow; - update_workflow_variables?: UpdateWorkflowVariables; -}; -``` - -### `Tag` - -```typescript -export type Tag = { - key?: string; - /** - * @deprecated - */ - type?: string; - value?: string; -}; -``` - -### `ConnectivityTestInput` - -```typescript -export type ConnectivityTestInput = { - input?: { - [key: string]: unknown; - }; - sink: string; -}; -``` - -### `ConnectivityTestResult` - -```typescript -export type ConnectivityTestResult = { - reason?: string; - successful?: boolean; - workflowId?: string; -}; -``` - -### `ExtendedEventExecution` - -```typescript -export type ExtendedEventExecution = { - action?: "start_workflow" | "complete_task" | "fail_task" | "terminate_workflow" | "update_workflow_variables"; - created?: number; - event?: string; - eventHandler?: EventHandler; - fullMessagePayload?: { - [key: string]: unknown; - }; - id?: string; - messageId?: string; - name?: string; - orgId?: string; - output?: { - [key: string]: unknown; - }; - payload?: { - [key: string]: unknown; - }; - status?: "IN_PROGRESS" | "COMPLETED" | "FAILED" | "SKIPPED"; - statusDescription?: string; -}; -``` - -### `EventMessage` - -```typescript -export type EventMessage = { - createdAt?: number; - eventExecutions?: Array; - eventTarget?: string; - eventType?: "WEBHOOK" | "MESSAGE"; - fullPayload?: { - [key: string]: unknown; - }; - id?: string; - orgId?: string; - payload?: string; - status?: "RECEIVED" | "HANDLED" | "REJECTED"; - statusDescription?: string; -}; -``` - -### `SearchResultHandledEventResponse` - -```typescript -export type SearchResultHandledEventResponse = { - results?: Array; - totalHits?: number; -}; -``` - -### `HandledEventResponse` - -```typescript -export type HandledEventResponse = { - active?: boolean; - event?: string; - name?: string; - numberOfActions?: number; - numberOfMessages?: number; -}; -``` +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/human-executor.md b/docs/api-reference/human-executor.md index a8222ade..b122f29b 100644 --- a/docs/api-reference/human-executor.md +++ b/docs/api-reference/human-executor.md @@ -1,532 +1,10 @@ -# HumanExecutor API Reference +# Human Executor -The `HumanExecutor` class provides comprehensive human task management. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new HumanExecutor(client: Client)` - -Creates a new `HumanExecutor`. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `getTasksByFilter(state: "PENDING" | "ASSIGNED" | "IN_PROGRESS" | "COMPLETED" | "TIMED_OUT", assignee?: string, assigneeType?: "EXTERNAL_USER" | "EXTERNAL_GROUP" | "CONDUCTOR_USER" | "CONDUCTOR_GROUP", claimedBy?: string, taskName?: string, taskInputQuery?: string, taskOutputQuery?: string): Promise` - -**⚠️ DEPRECATED**: Use `search()` method instead. - -Gets human tasks by a set of filter parameters. - -**Parameters:** - -- `state` (`"PENDING" | "ASSIGNED" | "IN_PROGRESS" | "COMPLETED" | "TIMED_OUT"`): The state of the tasks to filter by. -- `assignee` (`string`, optional): The assignee of the tasks. -- `assigneeType` (`"EXTERNAL_USER" | "EXTERNAL_GROUP" | "CONDUCTOR_USER" | "CONDUCTOR_GROUP"`, optional): The type of the assignee. -- `claimedBy` (`string`, optional): The user who has claimed the tasks (format: `:`). -- `taskName` (`string`, optional): The name of the tasks. -- `taskInputQuery` (`string`, optional): A query to filter tasks by their input data. -- `taskOutputQuery` (`string`, optional): A query to filter tasks by their output data. - -**Returns:** - -- `Promise`: An array of human task entries. - ---- - -### `search(searchParams: Partial): Promise` - -Searches for human tasks using flexible search parameters. - -**Parameters:** - -- `searchParams` (`Partial`): The search parameters. - -**Returns:** - -- `Promise`: An array of human task entries. - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Search for pending tasks -const pendingTasks = await humanExecutor.search({ - states: ["PENDING"], - definitionNames: ["approval_task"], - size: 20, -}); - -console.log(`Found ${pendingTasks.length} pending tasks`); -``` - ---- - -### `pollSearch(searchParams: Partial, options: PollIntervalOptions = { pollInterval: 100, maxPollTimes: 20 }): Promise` - -Polls for human tasks until a result is returned or maximum poll attempts are reached. - -**Parameters:** - -- `searchParams` (`Partial`): The search parameters. -- `options` (`PollIntervalOptions`, optional): The polling options. - -**Returns:** - -- `Promise`: An array of human task entries. - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Poll for new tasks -const newTasks = await humanExecutor.pollSearch( - { states: ["PENDING"] }, - { pollInterval: 500, maxPollTimes: 10 } -); - -if (newTasks.length > 0) { - console.log(`Found ${newTasks.length} new tasks to process`); -} -``` - ---- - -### `getTaskById(taskId: string): Promise` - -Gets a human task by its ID. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. - -**Returns:** - -- `Promise`: The human task entry. - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Get specific task details -const task = await humanExecutor.getTaskById("task_123"); -console.log(`Task ${task.taskId} is ${task.state}`); -``` - ---- - -### `claimTaskAsExternalUser(taskId: string, assignee: string, options?: Record): Promise` - -Claims a task as an external user. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. -- `assignee` (`string`): The external user to assign the task to. -- `options` (`Record`, optional): Additional options including `overrideAssignment` and `withTemplate`. - -**Returns:** - -- `Promise`: The claimed human task entry. - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Claim task as external user -const claimedTask = await humanExecutor.claimTaskAsExternalUser( - "task_123", - "user@example.com", - { overrideAssignment: false, withTemplate: true } -); - -console.log(`Task claimed by ${claimedTask.claimant?.user}`); -``` - ---- - -### `claimTaskAsConductorUser(taskId: string, options?: Record): Promise` - -Claims a task as a Conductor user. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. -- `options` (`Record`, optional): Additional options including `overrideAssignment` and `withTemplate`. - -**Returns:** - -- `Promise`: The claimed human task entry. - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Claim task as conductor user -const claimedTask = await humanExecutor.claimTaskAsConductorUser("task_123", { - overrideAssignment: false, - withTemplate: true, -}); - -console.log(`Task claimed by conductor user`); -``` - ---- - -### `releaseTask(taskId: string): Promise` - -Releases a claimed task. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Release a task -await humanExecutor.releaseTask("task_123"); -console.log("Task released"); -``` - ---- - -### `getTemplateByNameVersion(name: string, version: number): Promise` - -Gets a human task template by name and version. - -**Parameters:** - -- `name` (`string`): The name of the template. -- `version` (`number`): The version of the template. - -**Returns:** - -- `Promise`: The human task template. - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Get template details -const template = await humanExecutor.getTemplateByNameVersion( - "approval_form", - 1 -); -console.log(`Template version: ${template.version}`); -``` - ---- - -### `getTemplateById(templateNameVersionOne: string): Promise` - -**⚠️ DEPRECATED**: Use `getTemplateByNameVersion()` instead. - -Gets a human task template by ID (name with version 1). - -**Parameters:** - -- `templateNameVersionOne` (`string`): The name of the template. - -**Returns:** - -- `Promise`: The human task template. - ---- - -### `updateTaskOutput(taskId: string, requestBody: Record>): Promise` - -Updates the output of a task without completing it. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. -- `requestBody` (`Record>`): The new output data. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Update task output -await humanExecutor.updateTaskOutput("task_123", { - output: { - status: "in_progress", - comments: "Working on approval", - }, -}); -``` - ---- - -### `completeTask(taskId: string, requestBody: Record> = {}): Promise` - -Completes a task with the provided output data. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. -- `requestBody` (`Record>`, optional): The output data. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Complete task -await humanExecutor.completeTask("task_123", { - output: { - approved: true, - finalComments: "Approved with minor changes", - }, -}); - -console.log("Task completed"); -``` - ---- - -## Type Definitions - -### `HumanTaskEntry` - -```typescript -export type HumanTaskEntry = { - assignee?: HumanTaskUser; - claimant?: HumanTaskUser; - createdBy?: string; - createdOn?: number; - definitionName?: string; - displayName?: string; - humanTaskDef?: HumanTaskDefinition; - input?: { - [key: string]: unknown; - }; - output?: { - [key: string]: unknown; - }; - ownerApp?: string; - state?: - | "PENDING" - | "ASSIGNED" - | "IN_PROGRESS" - | "COMPLETED" - | "TIMED_OUT" - | "DELETED"; - taskId?: string; - taskRefName?: string; - updatedBy?: string; - updatedOn?: number; - workflowId?: string; - workflowName?: string; -}; -``` - -### `HumanTaskUser` - -```typescript -export type HumanTaskUser = { - user?: string; - userType?: - | "EXTERNAL_USER" - | "EXTERNAL_GROUP" - | "CONDUCTOR_USER" - | "CONDUCTOR_GROUP"; -}; -``` - -### `HumanTaskDefinition` - -```typescript -export type HumanTaskDefinition = { - assignmentCompletionStrategy?: "LEAVE_OPEN" | "TERMINATE"; - assignments?: Array; - displayName?: string; - fullTemplate?: HumanTaskTemplate; - taskTriggers?: Array; - userFormTemplate?: UserFormTemplate; -}; -``` - -### `HumanTaskAssignment` - -```typescript -export type HumanTaskAssignment = { - assignee?: HumanTaskUser; - slaMinutes?: number; -}; -``` - -### `HumanTaskTrigger` - -```typescript -export type HumanTaskTrigger = { - startWorkflowRequest?: StartWorkflowRequest; - triggerType?: - | "ASSIGNEE_CHANGED" - | "CLAIMANT_CHANGED" - | "PENDING" - | "IN_PROGRESS" - | "ASSIGNED" - | "COMPLETED" - | "TIMED_OUT"; -}; -``` - -### `HumanTaskTemplate` - -```typescript -export type HumanTaskTemplate = { - createTime?: number; - createdBy?: string; - jsonSchema: { - [key: string]: unknown; - }; - name: string; - ownerApp?: string; - tags?: Array; - templateUI: { - [key: string]: unknown; - }; - updateTime?: number; - updatedBy?: string; - version: number; -}; -``` - -### `UserFormTemplate` - -```typescript -export type UserFormTemplate = { - name?: string; - version?: number; -}; -``` - -### `StartWorkflowRequest` - -```typescript -export type StartWorkflowRequest = { - correlationId?: string; - createdBy?: string; - externalInputPayloadStoragePath?: string; - idempotencyKey?: string; - idempotencyStrategy?: "FAIL" | "RETURN_EXISTING" | "FAIL_ON_RUNNING"; - input?: { - [key: string]: unknown; - }; - name: string; - priority?: number; - taskToDomain?: { - [key: string]: string; - }; - version?: number; - workflowDef?: WorkflowDef; -}; -``` - -### `HumanTaskSearch` - -```typescript -export type HumanTaskSearch = { - assignees?: Array; - claimants?: Array; - definitionNames?: Array; - displayNames?: Array; - fullTextQuery?: string; - searchType?: "ADMIN" | "INBOX"; - size?: number; - start?: number; - states?: Array< - | "PENDING" - | "ASSIGNED" - | "IN_PROGRESS" - | "COMPLETED" - | "TIMED_OUT" - | "DELETED" - >; - taskInputQuery?: string; - taskOutputQuery?: string; - taskRefNames?: Array; - updateEndTime?: number; - updateStartTime?: number; - workflowIds?: Array; - workflowNames?: Array; -}; -``` - -### `PollIntervalOptions` - -```typescript -export interface PollIntervalOptions { - pollInterval: number; - maxPollTimes: number; -} -``` - -### `HumanTaskSearchResult` - -```typescript -export type HumanTaskSearchResult = { - hits?: number; - pageSizeLimit?: number; - results?: Array; - start?: number; - totalHits?: number; -}; -``` - -### `Tag` - -```typescript -export type Tag = { - key?: string; - /** - * @deprecated - */ - type?: string; - value?: string; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/metadata-client.md b/docs/api-reference/metadata-client.md index 3538f32e..4cb8c21b 100644 --- a/docs/api-reference/metadata-client.md +++ b/docs/api-reference/metadata-client.md @@ -1,505 +1,10 @@ -# MetadataClient API Reference +# Metadata Client -The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new MetadataClient(client: Client)` - -Creates a new `MetadataClient`. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `unregisterTask(name: string): Promise` - -Unregisters an existing task definition by name. - -**Parameters:** - -- `name` (`string`): The name of the task definition. - -**Returns:** - -- `Promise` - ---- - -### `registerTask(taskDef: ExtendedTaskDef): Promise` - -Registers a new task definition. - -**Parameters:** - -- `taskDef` (`ExtendedTaskDef`): The task definition to register. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { MetadataClient, taskDefinition } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Register a single task -const taskDef = taskDefinition({ - name: "email_task", - description: "Send an email", - ownerEmail: "dev@example.com", -}); - -await metadataClient.registerTask(taskDef); -``` - ---- - -### `registerTasks(taskDefs: ExtendedTaskDef[]): Promise` - -Registers multiple task definitions. - -**Parameters:** - -- `taskDefs` (`ExtendedTaskDef[]`): Array of task definitions to register. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { MetadataClient, taskDefinition } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Register multiple tasks -const taskDefs = [ - taskDefinition({ name: "email_task", description: "Send email" }), - taskDefinition({ name: "sms_task", description: "Send SMS" }), -]; - -await metadataClient.registerTasks(taskDefs); -``` - ---- - -### `updateTask(taskDef: ExtendedTaskDef): Promise` - -Updates an existing task definition. - -**Parameters:** - -- `taskDef` (`ExtendedTaskDef`): The task definition to update. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { MetadataClient, taskDefinition } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Update an existing task -const updatedTask = taskDefinition({ - name: "email_task", - retryCount: 5, - timeoutSeconds: 300, -}); - -await metadataClient.updateTask(updatedTask); -``` - ---- - -### `getTask(taskName: string): Promise` - -Gets an existing task definition. - -**Parameters:** - -- `taskName` (`string`): The name of the task definition. - -**Returns:** - -- `Promise`: The task definition. - -**Example:** - -```typescript -import { MetadataClient } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Get task definition -const taskDef = await metadataClient.getTask("email_task"); -console.log(`Task timeout: ${taskDef.timeoutSeconds}`); -``` - ---- - -### `registerWorkflowDef(workflowDef: ExtendedWorkflowDef, overwrite: boolean = false): Promise` - -Creates or updates a workflow definition. - -**Parameters:** - -- `workflowDef` (`ExtendedWorkflowDef`): The workflow definition to register. -- `overwrite` (`boolean`, optional): Whether to overwrite an existing workflow definition. Defaults to `false`. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { MetadataClient, workflow } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Register a workflow -const workflowDef = workflow("email_workflow", [ - simpleTask("send_email", "email_task", { to: "user@example.com" }), -]); - -await metadataClient.registerWorkflowDef(workflowDef, true); -``` - ---- - -### `getWorkflowDef(name: string, version?: number, metadata: boolean = false): Promise` - -Gets an existing workflow definition. - -**Parameters:** - -- `name` (`string`): The name of the workflow definition. -- `version` (`number`, optional): The version of the workflow definition. -- `metadata` (`boolean`, optional): Whether to include metadata. Defaults to `false`. - -**Returns:** - -- `Promise`: The workflow definition. - -**Example:** - -```typescript -import { MetadataClient } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Get workflow definition -const workflowDef = await metadataClient.getWorkflowDef("email_workflow", 1); -console.log(`Workflow has ${workflowDef.tasks.length} tasks`); -``` - ---- - -### `unregisterWorkflow(workflowName: string, version: number = 1): Promise` - -Unregisters a workflow definition. - -**Parameters:** - -- `workflowName` (`string`): The name of the workflow to unregister. -- `version` (`number`, optional): The version of the workflow to unregister. Defaults to `1`. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { MetadataClient } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Unregister workflow -await metadataClient.unregisterWorkflow("email_workflow", 1); -``` - ---- - -## Type Definitions - -### `ExtendedTaskDef` - -```typescript -interface ExtendedTaskDef { - name: string; - description?: string; - ownerEmail?: string; - ownerApp?: string; - retryCount?: number; - timeoutSeconds?: number; - timeoutPolicy?: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY"; - retryLogic?: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF"; - retryDelaySeconds?: number; - responseTimeoutSeconds?: number; - concurrentExecLimit?: number; - inputKeys?: string[]; - outputKeys?: string[]; - inputTemplate?: Record; - rateLimitPerFrequency?: number; - rateLimitFrequencyInSeconds?: number; - pollTimeoutSeconds?: number; - backoffScaleFactor?: number; - executionNameSpace?: string; - isolationGroupId?: string; - tags?: Array<{ key: string; value: string }>; - inputSchema?: SchemaDef; - outputSchema?: SchemaDef; - baseType?: string; - enforceSchema?: boolean; - overwriteTags?: boolean; - createTime?: number; - updateTime?: number; - createdBy?: string; - updatedBy?: string; - totalTimeoutSeconds?: number; -} -``` - -## `TaskDef` - -```typescript -export type TaskDef = { - backoffScaleFactor?: number; - baseType?: string; - concurrentExecLimit?: number; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - executionNameSpace?: string; - inputKeys?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - isolationGroupId?: string; - name: string; - outputKeys?: Array; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - pollTimeoutSeconds?: number; - rateLimitFrequencyInSeconds?: number; - rateLimitPerFrequency?: number; - responseTimeoutSeconds?: number; - retryCount?: number; - retryDelaySeconds?: number; - retryLogic?: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF"; - timeoutPolicy?: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - totalTimeoutSeconds: number; - updateTime?: number; - updatedBy?: string; -}; -``` - -### `ExtendedWorkflowDef` - -```typescript -export type ExtendedWorkflowDef = { - cacheConfig?: CacheConfig; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - failureWorkflow?: string; - inputParameters?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - maskedFields?: Array; - metadata?: { - [key: string]: unknown; - }; - name: string; - outputParameters?: { - [key: string]: unknown; - }; - outputSchema?: SchemaDef; - overwriteTags?: boolean; - ownerApp?: string; - ownerEmail?: string; - rateLimitConfig?: RateLimitConfig; - restartable?: boolean; - schemaVersion?: number; - tags?: Array; - tasks: Array; - timeoutPolicy?: "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - updateTime?: number; - updatedBy?: string; - variables?: { - [key: string]: unknown; - }; - version?: number; - workflowStatusListenerEnabled?: boolean; - workflowStatusListenerSink?: string; -}; -``` - -### `WorkflowDef` - -```typescript -export type WorkflowDef = { - cacheConfig?: CacheConfig; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - failureWorkflow?: string; - inputParameters?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - maskedFields?: Array; - metadata?: { - [key: string]: unknown; - }; - name: string; - outputParameters?: { - [key: string]: unknown; - }; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - rateLimitConfig?: RateLimitConfig; - restartable?: boolean; - schemaVersion?: number; - tasks: Array; - timeoutPolicy?: "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - updateTime?: number; - updatedBy?: string; - variables?: { - [key: string]: unknown; - }; - version?: number; - workflowStatusListenerEnabled?: boolean; - workflowStatusListenerSink?: string; -}; -``` - -### `WorkflowTask` - -```typescript -export type WorkflowTask = { - asyncComplete?: boolean; - cacheConfig?: CacheConfig; - /** - * @deprecated - */ - caseExpression?: string; - /** - * @deprecated - */ - caseValueParam?: string; - decisionCases?: { - [key: string]: Array; - }; - defaultCase?: Array; - defaultExclusiveJoinTask?: Array; - description?: string; - /** - * @deprecated - */ - dynamicForkJoinTasksParam?: string; - dynamicForkTasksInputParamName?: string; - dynamicForkTasksParam?: string; - dynamicTaskNameParam?: string; - evaluatorType?: string; - expression?: string; - forkTasks?: Array>; - inputParameters?: { - [key: string]: unknown; - }; - joinOn?: Array; - joinStatus?: string; - loopCondition?: string; - loopOver?: Array; - name: string; - onStateChange?: { - [key: string]: Array; - }; - optional?: boolean; - permissive?: boolean; - rateLimited?: boolean; - retryCount?: number; - scriptExpression?: string; - sink?: string; - startDelay?: number; - subWorkflowParam?: SubWorkflowParams; - taskDefinition?: TaskDef; - taskReferenceName: string; - type: string; - workflowTaskType?: string; -}; -``` - -### `SchemaDef` - -```typescript -export type SchemaDef = { - createTime?: number; - createdBy?: string; - data?: { - [key: string]: unknown; - }; - externalRef?: string; - name: string; - ownerApp?: string; - type: "JSON" | "AVRO" | "PROTOBUF"; - updateTime?: number; - updatedBy?: string; - version: number; -}; -``` - -### `CacheConfig` - -```typescript -export type CacheConfig = { - key?: string; - ttlInSecond?: number; -}; -``` - -### `Tag` - -```typescript -export type Tag = { - key?: string; - /** - * @deprecated - */ - type?: string; - value?: string; -}; -``` - -### `RateLimitConfig` - -```typescript -export type RateLimitConfig = { - concurrentExecLimit?: number; - rateLimitKey?: string; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/scheduler-client.md b/docs/api-reference/scheduler-client.md index 8a49df8c..6e56b966 100644 --- a/docs/api-reference/scheduler-client.md +++ b/docs/api-reference/scheduler-client.md @@ -1,645 +1,10 @@ -# SchedulerClient API Reference +# Scheduler Client -The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new SchedulerClient(client: Client)` - -Creates a new `SchedulerClient`. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `saveSchedule(param: SaveScheduleRequest): Promise` - -Creates or updates a schedule for a specified workflow. - -**Parameters:** - -- `param` (`SaveScheduleRequest`): The request to save a schedule. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Create a schedule -await scheduler.saveSchedule({ - name: "daily_report", - cronExpression: "0 0 9 * * ?", // Daily at 9 AM - startWorkflowRequest: { - name: "generate_report", - version: 1, - input: { reportType: "daily" }, - }, - scheduleStartTime: Date.now(), - scheduleEndTime: Date.now() + 365 * 24 * 60 * 60 * 1000, // 1 year from now -}); -``` - ---- - -### `search(start: number, size: number = 100, sort: string = "", freeText: string = "*", query?: string): Promise` - -Searches for scheduler executions. - -**Parameters:** - -- `start` (`number`): The starting offset. -- `size` (`number`, optional): The number of results to return. Defaults to 100. -- `sort` (`string`, optional): The sort order. Defaults to `""`. -- `freeText` (`string`, optional): The free text to search for. Defaults to `"*"`. -- `query` (`string`, optional): The search query. - -**Returns:** - -- `Promise`: The search results. - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Search for failed executions -const failedExecutions = await scheduler.search( - 0, - 50, - "scheduledTime:DESC", - "*", - "state:FAILED" -); - -console.log(`Found ${failedExecutions.totalHits} failed executions`); -``` - ---- - -### `getSchedule(name: string): Promise` - -Gets an existing schedule by name. - -**Parameters:** - -- `name` (`string`): The name of the schedule. - -**Returns:** - -- `Promise`: The schedule. - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Get schedule details -const schedule = await scheduler.getSchedule("daily_report"); -console.log(`Schedule paused: ${schedule.paused}`); -``` - ---- - -### `pauseSchedule(name: string): Promise` - -Pauses an existing schedule by name. - -**Parameters:** - -- `name` (`string`): The name of the schedule. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Pause a schedule -await scheduler.pauseSchedule("daily_report"); -console.log("Schedule paused"); -``` - ---- - -### `resumeSchedule(name: string): Promise` - -Resumes a paused schedule by name. - -**Parameters:** - -- `name` (`string`): The name of the schedule. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Resume a schedule -await scheduler.resumeSchedule("daily_report"); -console.log("Schedule resumed"); -``` - ---- - -### `deleteSchedule(name: string): Promise` - -Deletes an existing schedule by name. - -**Parameters:** - -- `name` (`string`): The name of the schedule. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Delete a schedule -await scheduler.deleteSchedule("daily_report"); -console.log("Schedule deleted"); -``` - ---- - -### `getAllSchedules(workflowName?: string): Promise` - -Gets all existing workflow schedules, optionally filtering by workflow name. - -**Parameters:** - -- `workflowName` (`string`, optional): The name of the workflow. - -**Returns:** - -- `Promise`: An array of workflow schedules. - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Get all schedules -const schedules = await scheduler.getAllSchedules(); -console.log(`Found ${schedules.length} schedules`); - -// Get schedules for specific workflow -const reportSchedules = await scheduler.getAllSchedules("generate_report"); -``` - ---- - -### `getNextFewSchedules(cronExpression: string, scheduleStartTime?: number, scheduleEndTime?: number, limit: number = 3): Promise` - -Gets a list of the next execution times for a schedule. - -**Parameters:** - -- `cronExpression` (`string`): The cron expression for the schedule. -- `scheduleStartTime` (`number`, optional): The start time for the schedule. -- `scheduleEndTime` (`number`, optional): The end time for the schedule. -- `limit` (`number`, optional): The number of execution times to return. Defaults to 3. - -**Returns:** - -- `Promise`: An array of the next execution times (in milliseconds since epoch). - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Get next 5 execution times -const nextTimes = await scheduler.getNextFewSchedules( - "0 0 9 * * ?", // Daily at 9 AM - Date.now(), - Date.now() + 30 * 24 * 60 * 60 * 1000, // Next 30 days - 5 -); - -console.log("Next execution times:"); -nextTimes.forEach((time) => { - console.log(new Date(time).toISOString()); -}); -``` - ---- - -### `pauseAllSchedules(): Promise` - -Pauses all scheduling in the Conductor server instance (for debugging purposes only). - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Pause all schedules (for maintenance) -await scheduler.pauseAllSchedules(); -console.log("All schedules paused"); -``` - ---- - -### `requeueAllExecutionRecords(): Promise` - -Requeues all execution records that may have failed or been missed. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Requeue failed executions -await scheduler.requeueAllExecutionRecords(); -console.log("All execution records requeued"); -``` - ---- - -### `resumeAllSchedules(): Promise` - -Resumes all scheduling in the Conductor server instance. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); - -// Resume all schedules after maintenance -await scheduler.resumeAllSchedules(); -console.log("All schedules resumed"); -``` - ---- - -## Type Definitions - -### `SaveScheduleRequest` - -```typescript -export type SaveScheduleRequest = { - createdBy?: string; - cronExpression: string; - description?: string; - name: string; - paused?: boolean; - runCatchupScheduleInstances?: boolean; - scheduleEndTime?: number; - scheduleStartTime?: number; - startWorkflowRequest: StartWorkflowRequest; - updatedBy?: string; - zoneId?: string; -}; -``` - -### `WorkflowSchedule` - -```typescript -export type WorkflowSchedule = { - createTime?: number; - createdBy?: string; - cronExpression?: string; - description?: string; - name?: string; - paused?: boolean; - pausedReason?: string; - runCatchupScheduleInstances?: boolean; - scheduleEndTime?: number; - scheduleStartTime?: number; - startWorkflowRequest?: StartWorkflowRequest; - tags?: Tag[]; - updatedBy?: string; - updatedTime?: number; - zoneId?: string; -}; -``` - -### `WorkflowScheduleModel` - -```typescript -export type WorkflowScheduleModel = { - createTime?: number; - createdBy?: string; - cronExpression?: string; - description?: string; - name?: string; - orgId?: string; - paused?: boolean; - pausedReason?: string; - queueMsgId?: string; - runCatchupScheduleInstances?: boolean; - scheduleEndTime?: number; - scheduleStartTime?: number; - startWorkflowRequest?: StartWorkflowRequest; - tags?: Tag[]; - updatedBy?: string; - updatedTime?: number; - zoneId?: string; -}; -``` - -### `SearchResultWorkflowScheduleExecutionModel` - -```typescript -export type SearchResultWorkflowScheduleExecutionModel = { - results?: WorkflowScheduleExecutionModel[]; - totalHits?: number; -}; -``` - -### `WorkflowScheduleExecutionModel` - -```typescript -export type WorkflowScheduleExecutionModel = { - executionId?: string; - executionTime?: number; - orgId?: string; - queueMsgId?: string; - reason?: string; - scheduleName?: string; - scheduledTime?: number; - stackTrace?: string; - startWorkflowRequest?: StartWorkflowRequest; - state?: "POLLED" | "FAILED" | "EXECUTED"; - workflowId?: string; - workflowName?: string; - zoneId?: string; -}; -``` - -### `StartWorkflowRequest` - -```typescript -export type StartWorkflowRequest = { - correlationId?: string; - createdBy?: string; - externalInputPayloadStoragePath?: string; - idempotencyKey?: string; - idempotencyStrategy?: "FAIL" | "RETURN_EXISTING" | "FAIL_ON_RUNNING"; - input?: { - [key: string]: unknown; - }; - name: string; - priority?: number; - taskToDomain?: { - [key: string]: string; - }; - version?: number; - workflowDef?: WorkflowDef; -}; -``` - -### `Tag` - -```typescript -export type Tag = { - key?: string; - /** - * @deprecated - */ - type?: string; -}; -``` - -### `WorkflowDef` - -```typescript -export type WorkflowDef = { - cacheConfig?: CacheConfig; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - failureWorkflow?: string; - inputParameters?: string[]; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - maskedFields?: string[]; - metadata?: { - [key: string]: unknown; - }; - name: string; - outputParameters?: { - [key: string]: unknown; - }; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - rateLimitConfig?: RateLimitConfig; - restartable?: boolean; - schemaVersion?: number; - tasks: WorkflowTask[]; - timeoutPolicy?: "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - updateTime?: number; - updatedBy?: string; -}; -``` - -### `CacheConfig` - -```typescript -export type CacheConfig = { - key?: string; - ttlInSecond?: number; -}; -``` - -### `SchemaDef` - -```typescript -export type SchemaDef = { - createTime?: number; - createdBy?: string; - data?: { - [key: string]: unknown; - }; - externalRef?: string; - name: string; - ownerApp?: string; - type: "JSON" | "AVRO" | "PROTOBUF"; - updateTime?: number; -}; -``` - -### `RateLimitConfig` - -```typescript -export type RateLimitConfig = { - concurrentExecLimit?: number; - rateLimitKey?: string; -}; -``` - -### `WorkflowTask` - -```typescript -export type WorkflowTask = { - asyncComplete?: boolean; - cacheConfig?: CacheConfig; - /** - * @deprecated - */ - caseExpression?: string; - /** - * @deprecated - */ - caseValueParam?: string; - decisionCases?: { - [key: string]: Array; - }; - defaultCase?: Array; - defaultExclusiveJoinTask?: Array; - description?: string; - /** - * @deprecated - */ - dynamicForkJoinTasksParam?: string; - dynamicForkTasksInputParamName?: string; - dynamicForkTasksParam?: string; - dynamicTaskNameParam?: string; - evaluatorType?: string; - expression?: string; - forkTasks?: Array>; - inputParameters?: { - [key: string]: unknown; - }; - joinOn?: Array; - joinStatus?: string; - loopCondition?: string; - loopOver?: Array; - name: string; - onStateChange?: { - [key: string]: Array; - }; - optional?: boolean; - permissive?: boolean; - rateLimited?: boolean; - retryCount?: number; - scriptExpression?: string; - sink?: string; - startDelay?: number; - subWorkflowParam?: SubWorkflowParams; - taskDefinition?: TaskDef; - taskReferenceName: string; - type?: string; -}; -``` - -### `StateChangeEvent` - -```typescript -export type StateChangeEvent = { - payload?: { - [key: string]: unknown; - }; - type: string; -}; -``` - -### `SubWorkflowParams` - -```typescript -export type SubWorkflowParams = { - idempotencyKey?: string; - idempotencyStrategy?: "FAIL" | "RETURN_EXISTING" | "FAIL_ON_RUNNING"; - name?: string; - taskToDomain?: { - [key: string]: string; - }; - version?: number; - workflowDefinition?: WorkflowDef; -}; -``` - -### `TaskDef` - -```typescript -export type TaskDef = { - backoffScaleFactor?: number; - baseType?: string; - concurrentExecLimit?: number; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - executionNameSpace?: string; - inputKeys?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - isolationGroupId?: string; - name: string; - outputKeys?: Array; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - pollTimeoutSeconds?: number; - rateLimitFrequencyInSeconds?: number; - rateLimitPerFrequency?: number; - responseTimeoutSeconds?: number; - retryCount?: number; - retryDelaySeconds?: number; - retryLogic?: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF"; - timeoutPolicy?: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - totalTimeoutSeconds: number; - updateTime?: number; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/service-registry-client.md b/docs/api-reference/service-registry-client.md index 3afd35e9..0b62d3e1 100644 --- a/docs/api-reference/service-registry-client.md +++ b/docs/api-reference/service-registry-client.md @@ -1,330 +1,10 @@ -# ServiceRegistryClient API Reference +# Service Registry Client -The `ServiceRegistryClient` manages service registrations and circuit breakers. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new ServiceRegistryClient(client: Client)` - -Creates a new `ServiceRegistryClient`. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `getRegisteredServices(): Promise` - -Retrieves all registered services. - -**Returns:** - -- `Promise`: An array of all registered services. - ---- - -### `removeService(name: string): Promise` - -Removes a service by name. - -**Parameters:** - -- `name` (`string`): The name of the service to remove. - -**Returns:** - -- `Promise` - ---- - -### `getService(name: string): Promise` - -Gets a service by name. - -**Parameters:** - -- `name` (`string`): The name of the service to retrieve. - -**Returns:** - -- `Promise`: The requested service registry. - ---- - -### `openCircuitBreaker(name: string): Promise` - -Opens the circuit breaker for a service. - -**Parameters:** - -- `name` (`string`): The name of the service. - -**Returns:** - -- `Promise`: A response with the circuit breaker status. - ---- - -### `closeCircuitBreaker(name: string): Promise` - -Closes the circuit breaker for a service. - -**Parameters:** - -- `name` (`string`): The name of the service. - -**Returns:** - -- `Promise`: A response with the circuit breaker status. - ---- - -### `getCircuitBreakerStatus(name: string): Promise` - -Gets the circuit breaker status for a service. - -**Parameters:** - -- `name` (`string`): The name of the service. - -**Returns:** - -- `Promise`: A response with the circuit breaker status. - ---- - -### `addOrUpdateService(serviceRegistry: ServiceRegistry): Promise` - -Adds or updates a service registry. - -**Parameters:** - -- `serviceRegistry` (`ServiceRegistry`): The service registry to add or update. - -**Returns:** - -- `Promise` - ---- - -### `addOrUpdateServiceMethod(registryName: string, method: ServiceMethod): Promise` - -Adds or updates a service method. - -**Parameters:** - -- `registryName` (`string`): The name of the registry. -- `method` (`ServiceMethod`): The service method to add or update. - -**Returns:** - -- `Promise` - ---- - -### `removeMethod(registryName: string, serviceName: string, method: string, methodType: string): Promise` - -Removes a service method. - -**Parameters:** - -- `registryName` (`string`): The name of the registry. -- `serviceName` (`string`): The name of the service. -- `method` (`string`): The name of the method. -- `methodType` (`string`): The type of the method. - -**Returns:** - -- `Promise` - ---- - -### `getProtoData(registryName: string, filename: string): Promise` - -Gets proto data. - -**Parameters:** - -- `registryName` (`string`): The name of the registry. -- `filename` (`string`): The name of the proto file. - -**Returns:** - -- `Promise`: The proto file data as a `Blob`. - ---- - -### `setProtoData(registryName: string, filename: string, data: Blob): Promise` - -Sets proto data. - -**Parameters:** - -- `registryName` (`string`): The name of the registry. -- `filename` (`string`): The name of the proto file. -- `data` (`Blob`): The proto file data. - -**Returns:** - -- `Promise` - ---- - -### `deleteProto(registryName: string, filename: string): Promise` - -Deletes a proto file. - -**Parameters:** - -- `registryName` (`string`): The name of the registry. -- `filename` (`string`): The name of the proto file. - -**Returns:** - -- `Promise` - ---- - -### `getAllProtos(registryName: string): Promise` - -Gets all proto files for a registry. - -**Parameters:** - -- `registryName` (`string`): The name of the registry. - -**Returns:** - -- `Promise`: A list of proto registry entries. - ---- - -### `discover(name: string, create: boolean = false): Promise` - -Discovers service methods. - -**Parameters:** - -- `name` (`string`): The name of the service. -- `create` (`boolean`, optional): Whether to create the discovered methods. Defaults to `false`. - -**Returns:** - -- `Promise`: The discovered service methods. - ---- - -## Type Definitions - -### `ServiceType` - -```typescript -export enum ServiceType { - HTTP = "HTTP", - MCP_REMOTE = "MCP_REMOTE", - gRPC = "gRPC", -} -``` - -### `ServiceRegistry` - -```typescript -export type ServiceRegistry = { - circuitBreakerEnabled?: boolean; - config?: Config; - methods?: ServiceMethod[]; - name?: string; - requestParams?: RequestParam[]; - serviceURI?: string; - type?: "HTTP" | "gRPC" | "MCP_REMOTE"; -}; -``` - -### `ServiceMethod` - -```typescript -export type ServiceMethod = { - exampleInput?: { - [key: string]: unknown; - }; - id?: number; - inputType?: string; - methodName?: string; - methodType?: string; - operationName?: string; - outputType?: string; - requestParams?: RequestParam[]; -}; -``` - -### `CircuitBreakerTransitionResponse` - -```typescript -export type CircuitBreakerTransitionResponse = { - currentState?: string; - message?: string; - previousState?: string; - service?: string; - transitionTimestamp?: number; -}; -``` - -### `ProtoRegistryEntry` - -```typescript -export type ProtoRegistryEntry = { - data?: string; - filename?: string; - serviceName?: string; -}; -``` - -### `Config` - -```typescript -export type Config = { - circuitBreakerConfig?: OrkesCircuitBreakerConfig; -}; -``` - -### `OrkesCircuitBreakerConfig` - -```typescript -export type OrkesCircuitBreakerConfig = { - automaticTransitionFromOpenToHalfOpenEnabled?: boolean; - failureRateThreshold?: number; - maxWaitDurationInHalfOpenState?: number; - minimumNumberOfCalls?: number; - permittedNumberOfCallsInHalfOpenState?: number; - slidingWindowSize?: number; - slowCallDurationThreshold?: number; - slowCallRateThreshold?: number; - waitDurationInOpenState?: number; -}; -``` - -### `RequestParam` - -```typescript -export type RequestParam = { - name?: string; - required?: boolean; - schema?: Schema; - type?: string; -}; -``` - -### `Schema` - -```typescript -export type Schema = { - defaultValue?: { - [key: string]: unknown; - }; - format?: string; - type?: string; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/task-client.md b/docs/api-reference/task-client.md index 66f761ac..e7504366 100644 --- a/docs/api-reference/task-client.md +++ b/docs/api-reference/task-client.md @@ -1,606 +1,10 @@ -# TaskClient API Reference +# Task Client -The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new TaskClient(client: Client)` - -Creates a new TaskClient. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `search(start: number, size: number, sort: string = "", freeText: string, query: string): Promise` - -Searches for tasks. - -**Parameters:** - -- `start` (`number`): The starting offset. -- `size` (`number`): The number of results to return. -- `sort` (`string`, optional): The sort order. Defaults to `""`. -- `freeText` (`string`): The free text to search for. -- `query` (`string`): The search query. - -**Returns:** - -- `Promise`: The search results. - -**Example:** - -```typescript -import { TaskClient } from "@io-orkes/conductor-javascript"; - -const taskClient = new TaskClient(client); - -// Search for failed tasks -const failedTasks = await taskClient.search( - 0, - 100, - "startTime:DESC", - "*", - "status:FAILED" -); - -console.log(`Found ${failedTasks.totalHits} failed tasks`); -``` - ---- - -### `getTask(taskId: string): Promise` - -Gets a task by its ID. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. - -**Returns:** - -- `Promise`: The task details. - -**Example:** - -```typescript -import { TaskClient } from "@io-orkes/conductor-javascript"; - -const taskClient = new TaskClient(client); - -// Get task details -const task = await taskClient.getTask("task_123"); -console.log(`Task ${task.taskId} status: ${task.status}`); -``` - ---- - -### `updateTaskResult(workflowId: string, taskRefName: string, status: TaskResultStatus, outputData: Record): Promise` - -Updates the result of a task. - -**Parameters:** - -- `workflowId` (`string`): The ID of the workflow instance. -- `taskRefName` (`string`): The reference name of the task. -- `status` (`TaskResultStatus`): The new status of the task. -- `outputData` (`Record`): The output data of the task. - -**Returns:** - -- `Promise`: The ID of the updated task. - -**Example:** - -```typescript -import { TaskClient, TaskResultStatus } from "@io-orkes/conductor-javascript"; - -const taskClient = new TaskClient(client); - -// Update task result -const taskId = await taskClient.updateTaskResult( - "workflow_123", - "process_data_ref", - TaskResultStatus.COMPLETED, - { result: "success", processed: true } -); - -console.log(`Updated task: ${taskId}`); -``` - ---- - -## Type Definitions - -### `TaskResultStatus` - -```typescript -export type TaskResultStatus = NonNullable; -``` - -Represents the possible status values for a task result: - -- `"IN_PROGRESS"` - Task is currently running -- `"FAILED"` - Task failed but can be retried -- `"FAILED_WITH_TERMINAL_ERROR"` - Task failed and cannot be retried -- `"COMPLETED"` - Task completed successfully - -### `SearchResultTaskSummary` - -```typescript -export type SearchResultTaskSummary = { - results?: Array; - totalHits?: number; -}; -``` - -### `TaskSummary` - -```typescript -export type TaskSummary = { - correlationId?: string; - endTime?: string; - executionTime?: number; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - input?: string; - output?: string; - queueWaitTime?: number; - reasonForIncompletion?: string; - scheduledTime?: string; - startTime?: string; - status?: - | "IN_PROGRESS" - | "CANCELED" - | "FAILED" - | "FAILED_WITH_TERMINAL_ERROR" - | "COMPLETED" - | "COMPLETED_WITH_ERRORS" - | "SCHEDULED" - | "TIMED_OUT" - | "SKIPPED"; - taskDefName?: string; - taskId?: string; - taskReferenceName?: string; - taskType?: string; - updateTime?: string; - workflowId?: string; - workflowPriority?: number; - workflowType?: string; -}; -``` - -### `Task` - -```typescript -export type Task = { - callbackAfterSeconds?: number; - callbackFromWorker?: boolean; - correlationId?: string; - domain?: string; - endTime?: number; - executed?: boolean; - executionNameSpace?: string; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - firstStartTime?: number; - inputData?: { - [key: string]: unknown; - }; - isolationGroupId?: string; - iteration?: number; - loopOverTask?: boolean; - outputData?: { - [key: string]: unknown; - }; - parentTaskId?: string; - pollCount?: number; - queueWaitTime?: number; - rateLimitFrequencyInSeconds?: number; - rateLimitPerFrequency?: number; - reasonForIncompletion?: string; - referenceTaskName?: string; - responseTimeoutSeconds?: number; - retried?: boolean; - retriedTaskId?: string; - retryCount?: number; - scheduledTime?: number; - seq?: number; - startDelayInSeconds?: number; - startTime?: number; - status?: - | "IN_PROGRESS" - | "CANCELED" - | "FAILED" - | "FAILED_WITH_TERMINAL_ERROR" - | "COMPLETED" - | "COMPLETED_WITH_ERRORS" - | "SCHEDULED" - | "TIMED_OUT" - | "SKIPPED"; - subWorkflowId?: string; - subworkflowChanged?: boolean; - taskDefName?: string; - taskDefinition?: TaskDef; - taskId?: string; - taskType?: string; - updateTime?: number; - workerId?: string; - workflowInstanceId?: string; - workflowPriority?: number; - workflowTask?: WorkflowTask; - workflowType?: string; -}; -``` - -### `TaskDef` - -```typescript -export type TaskDef = { - backoffScaleFactor?: number; - baseType?: string; - concurrentExecLimit?: number; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - executionNameSpace?: string; - inputKeys?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - isolationGroupId?: string; - name: string; - outputKeys?: Array; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - pollTimeoutSeconds?: number; - rateLimitFrequencyInSeconds?: number; - rateLimitPerFrequency?: number; - responseTimeoutSeconds?: number; - retryCount?: number; - retryDelaySeconds?: number; - retryLogic?: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF"; - timeoutPolicy?: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - totalTimeoutSeconds: number; - updateTime?: number; - updatedBy?: string; -}; -``` - -### `TaskResult` - -```typescript -export type TaskResult = { - callbackAfterSeconds?: number; - extendLease?: boolean; - externalOutputPayloadStoragePath?: string; - logs?: Array; - outputData?: { - [key: string]: unknown; - }; - reasonForIncompletion?: string; - status?: - | "IN_PROGRESS" - | "FAILED" - | "FAILED_WITH_TERMINAL_ERROR" - | "COMPLETED"; - subWorkflowId?: string; - taskId: string; - workerId?: string; - workflowInstanceId: string; -}; -``` - -### `TaskExecLog` - -```typescript -export type TaskExecLog = { - createdTime?: number; - log?: string; - taskId?: string; -}; -``` - -### `WorkflowTask` - -```typescript -export type WorkflowTask = { - asyncComplete?: boolean; - cacheConfig?: CacheConfig; - /** - * @deprecated - */ - caseExpression?: string; - /** - * @deprecated - */ - caseValueParam?: string; - decisionCases?: { - [key: string]: Array; - }; - defaultCase?: Array; - defaultExclusiveJoinTask?: Array; - description?: string; - /** - * @deprecated - */ - dynamicForkJoinTasksParam?: string; - dynamicForkTasksInputParamName?: string; - dynamicForkTasksParam?: string; - dynamicTaskNameParam?: string; - evaluatorType?: string; - expression?: string; - forkTasks?: Array>; - inputParameters?: { - [key: string]: unknown; - }; - joinOn?: Array; - joinStatus?: string; - loopCondition?: string; - loopOver?: Array; - name: string; - onStateChange?: { - [key: string]: Array; - }; - optional?: boolean; - permissive?: boolean; - rateLimited?: boolean; - retryCount?: number; - scriptExpression?: string; - sink?: string; - startDelay?: number; - subWorkflowParam?: SubWorkflowParams; - taskDefinition?: TaskDef; - taskReferenceName: string; - type?: string; -}; -``` - -### `SchemaDef` - -```typescript -export type SchemaDef = { - createTime?: number; - createdBy?: string; - data?: { - [key: string]: unknown; - }; - externalRef?: string; - name: string; - ownerApp?: string; - type: "JSON" | "AVRO" | "PROTOBUF"; - updateTime?: number; - updatedBy?: string; - version: number; -}; -``` - -### `TaskListSearchResultSummary` - -```typescript -export type TaskListSearchResultSummary = { - results?: Array; - summary?: { - [key: string]: number; - }; - totalHits?: number; -}; -``` - -### `CacheConfig` - -```typescript -export type CacheConfig = { - key?: string; - ttlInSecond?: number; -}; -``` - -### `StateChangeEvent` - -```typescript -export type StateChangeEvent = { - payload?: { - [key: string]: unknown; - }; - type: string; -}; -``` - -### `SubWorkflowParams` - -```typescript -export type SubWorkflowParams = { - idempotencyKey?: string; - idempotencyStrategy?: "FAIL" | "RETURN_EXISTING" | "FAIL_ON_RUNNING"; - name?: string; - priority?: { - [key: string]: unknown; - }; - taskToDomain?: { - [key: string]: string; - }; - version?: number; - workflowDefinition?: { - [key: string]: unknown; - }; -}; -``` - -### `ConductorSdkError` - -```typescript -export class ConductorSdkError extends Error { - private _trace: unknown; - private __proto__: unknown; - - constructor(message?: string, innerError?: Error); -} -``` - -### `TaskResultOutputData` - -```typescript -export type TaskResultOutputData = NonNullable; -``` - -### `SignalResponse` - -```typescript -export type SignalResponse = { - correlationId?: string; - input?: { - [key: string]: unknown; - }; - output?: { - [key: string]: unknown; - }; - requestId?: string; - responseType?: - | "TARGET_WORKFLOW" - | "BLOCKING_WORKFLOW" - | "BLOCKING_TASK" - | "BLOCKING_TASK_INPUT"; - targetWorkflowId?: string; - targetWorkflowStatus?: string; - workflowId?: string; - priority?: number; - variables?: Record; - tasks?: Task[]; - createdBy?: string; - createTime?: number; - status?: string; - updateTime?: number; - taskType?: string; - taskId?: string; - referenceTaskName?: string; - retryCount?: number; - taskDefName?: string; - workflowType?: string; -}; -``` - -### `EnhancedSignalResponse` - -```typescript -export interface EnhancedSignalResponse extends SignalResponse { - isTargetWorkflow(): boolean; - isBlockingWorkflow(): boolean; - isBlockingTask(): boolean; - isBlockingTaskInput(): boolean; - getWorkflow(): Workflow; - getBlockingTask(): Task; - getTaskInput(): Record; - getWorkflowId(): string; - getTargetWorkflowId(): string; - hasWorkflowData(): boolean; - hasTaskData(): boolean; - getResponseType(): string; - isTerminal(): boolean; - isRunning(): boolean; - isPaused(): boolean; - getSummary(): string; - toDebugJSON(): Record; - toString(): string; -} -``` - -### `Workflow` - -```typescript -export type Workflow = { - correlationId?: string; - createTime?: number; - createdBy?: string; - endTime?: number; - event?: string; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - failedReferenceTaskNames?: Array; - failedTaskNames?: Array; - history?: Array; - idempotencyKey?: string; - input?: { - [key: string]: unknown; - }; - lastRetriedTime?: number; - output?: { - [key: string]: unknown; - }; - ownerApp?: string; - parentWorkflowId?: string; - parentWorkflowTaskId?: string; - priority?: number; - rateLimitKey?: string; - rateLimited?: boolean; - reRunFromWorkflowId?: string; - reasonForIncompletion?: string; - startTime?: number; - status?: - | "RUNNING" - | "COMPLETED" - | "FAILED" - | "TIMED_OUT" - | "TERMINATED" - | "PAUSED"; - taskToDomain?: { - [key: string]: string; - }; - tasks?: Array; - updateTime?: number; - updatedBy?: string; - variables?: { - [key: string]: unknown; - }; - workflowDefinition?: WorkflowDef; - workflowId?: string; - workflowName?: string; - workflowVersion?: number; -}; -``` - -### `WorkflowDef` - -```typescript -export type WorkflowDef = { - cacheConfig?: CacheConfig; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - failureWorkflow?: string; - inputParameters?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - name?: string; - outputParameters?: { - [key: string]: unknown; - }; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - restartable?: boolean; - schemaVersion?: number; - tasks?: Array; - timeoutPolicy?: "ALERT_ONLY" | "TIME_OUT_WF"; - timeoutSeconds?: number; - updateTime?: number; - updatedBy?: string; - variables?: { - [key: string]: unknown; - }; - version?: number; - workflowStatusListenerEnabled?: boolean; - workflowStatusListenerSink?: string; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/task-generators.md b/docs/api-reference/task-generators.md index fb9744f4..b13218b2 100644 --- a/docs/api-reference/task-generators.md +++ b/docs/api-reference/task-generators.md @@ -1,317 +1,10 @@ -# Task Generators Reference +# Task Generators -This section provides code examples for each task type generator. Use these to build your workflow task lists. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -**Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [MetadataClient API Reference](metadata-client.md)). +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -## Simple Task - -_Requires Custom Workers_ - Executes custom business logic via workers you implement. - -```typescript -import { simpleTask } from "@io-orkes/conductor-javascript"; - -const task = simpleTask( - "task_ref", // taskReferenceName (required) - "task_name", // name (required): must match worker's taskDefName - { - // inputParameters (required) - inputParam: "value", - }, - false // optional (optional): if true, workflow continues on failure -); -``` - -## HTTP Task - -_System Task_ - Makes HTTP/REST API calls. - -```typescript -import { httpTask } from "@io-orkes/conductor-javascript"; - -const task = httpTask( - "http_ref", - { - uri: "http://api.example.com/data", - method: "GET", - headers: { Authorization: "Bearer token" }, - connectionTimeOut: 5000, - readTimeOut: 10000, - }, - false, // asyncComplete (optional) - false // optional (optional): workflow continues on failure -); -``` - -## Switch Task - -_System Task_ - Provides conditional branching based on input values. - -```typescript -import { switchTask } from "@io-orkes/conductor-javascript"; - -const task = switchTask( - "switch_ref", - "input.status", // expression to evaluate - { - active: [simpleTask("active_task", "process_active", {})], - inactive: [simpleTask("inactive_task", "process_inactive", {})], - }, - [simpleTask("default_task", "process_default", {})], // defaultCase (optional) - false // optional (optional): workflow continues on failure -); -``` - -## Fork-Join Task - -_System Task_ - Executes multiple task branches in parallel and waits for all to complete. - -```typescript -import { forkTask, forkTaskJoin } from "@io-orkes/conductor-javascript"; - -// Method 1: Using forkTask (creates only the fork) -const task1 = forkTask("fork_ref", [ - simpleTask("task1", "process_1", {}), - simpleTask("task2", "process_2", {}), - simpleTask("task3", "process_3", {}), -]); - -// Method 2: Using forkTaskJoin (creates both fork and join) -const [fork, join] = forkTaskJoin("fork_ref", [ - simpleTask("task1", "process_1", {}), - simpleTask("task2", "process_2", {}), - simpleTask("task3", "process_3", {}), -]); -``` - -## Do-While Task - -_System Task_ - Executes a loop with a condition evaluated after each iteration. - -```typescript -import { doWhileTask } from "@io-orkes/conductor-javascript"; - -const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ - simpleTask("loop_task", "process_item", { - index: "${workflow.variables.counter}", - }), - setVariableTask("increment", { - variableName: "counter", - value: "${workflow.variables.counter + 1}", - }), -]); -``` - -## Sub-Workflow Task - -_System Task_ - Executes another workflow as a task. - -```typescript -import { subWorkflowTask } from "@io-orkes/conductor-javascript"; - -const task = subWorkflowTask( - "sub_ref", - "child_workflow", // workflowName - 1, // version (optional): uses latest if not specified - false // optional (optional) -); - -// Set input parameters -task.inputParameters = { inputParam: "value" }; -``` - -## Event Task - -_System Task_ - Publishes events to external eventing systems. - -```typescript -import { - eventTask, - sqsEventTask, - conductorEventTask, -} from "@io-orkes/conductor-javascript"; - -// Generic event task -const task1 = eventTask("event_ref", "sqs", "my-queue"); - -// SQS specific event task -const task2 = sqsEventTask("sqs_ref", "my-queue"); - -// Conductor internal event task -const task3 = conductorEventTask("conductor_ref", "my-event"); -``` - -## Wait Task - -_System Task_ - Pauses workflow execution for a specified duration or until a specific time. - -```typescript -import { - waitTaskDuration, - waitTaskUntil, -} from "@io-orkes/conductor-javascript"; - -// Wait for a duration (e.g., "30s", "5m", "1h", "2d") -const taskDuration = waitTaskDuration( - "wait_ref", - "30s", // duration string - false // optional (optional) -); - -// Wait until a specific time (ISO 8601 format) -const taskUntil = waitTaskUntil( - "wait_until_ref", - "2025-12-31T23:59:59Z", // ISO 8601 timestamp - false // optional (optional) -); -``` - -## Terminate Task - -_System Task_ - Terminates workflow execution with a specified status. - -```typescript -import { terminateTask } from "@io-orkes/conductor-javascript"; - -const task = terminateTask( - "terminate_ref", - "FAILED", // status: "COMPLETED" or "FAILED" - "Error message" // terminationReason (optional) -); -``` - -## Set Variable Task - -_System Task_ - Sets or updates workflow variables. - -```typescript -import { setVariableTask } from "@io-orkes/conductor-javascript"; - -const task = setVariableTask("var_ref", { - variableName: "result", - value: "computed_value", -}); -``` - -## JSON JQ Transform Task - -_System Task_ - Transforms JSON data using JQ expressions. - -```typescript -import { jsonJqTask } from "@io-orkes/conductor-javascript"; - -const task = jsonJqTask( - "transform_ref", - ".data.items[] | {id: .id, name: .name}" -); -``` - -## Kafka Publish Task - -_System Task_ - Publishes messages to Kafka topics. - -```typescript -import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; - -const task = kafkaPublishTask( - "kafka_ref", - "topic_name", - { - message: "Hello Kafka!", - }, - { - key: "message_key", - partition: 0, - } -); -``` - -## Inline Task - -_System Task_ - Executes JavaScript code inline within the workflow. - -```typescript -import { inlineTask } from "@io-orkes/conductor-javascript"; - -const task = inlineTask( - "inline_ref", - ` - function execute(input) { - return { result: input.value * 2 }; - } -` -); -``` - -## Dynamic Fork Task - -_System Task_ - Dynamically creates parallel task executions based on input. - -```typescript -import { dynamicForkTask } from "@io-orkes/conductor-javascript"; - -const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); -``` - -## Join Task - -_System Task_ - Synchronization point for forked tasks. - -```typescript -import { joinTask } from "@io-orkes/conductor-javascript"; - -const task = joinTask("join_ref"); -``` - -## Human Task - -_System Task_ - Pauses workflow until a person completes an action (approval, form submission, etc.). - -```typescript -import { humanTask } from "@io-orkes/conductor-javascript"; - -const task = humanTask("human_ref", "approval_task", { - assignee: "user@example.com", - form: { - fields: [ - { name: "approved", type: "boolean", required: true }, - { name: "comments", type: "text", required: false }, - ], - }, -}); -``` - -## Loop Task - -_System Task_ - Creates a loop that executes a fixed number of times. - -```typescript -import { newLoopTask } from "@io-orkes/conductor-javascript"; - -const task = newLoopTask("loop_ref", 5, [ - simpleTask("loop_task", "process_item", { - iteration: "${loop_ref.iteration}", - }), -]); -``` - -## Workflow Generator - -Helper function to create workflow definitions. - -```typescript -import { workflow } from "@io-orkes/conductor-javascript"; - -const workflowDef = workflow("my_workflow", [ - simpleTask("task1", "process_data", { input: "value" }), - httpTask("task2", { - uri: "https://api.example.com/process", - method: "POST", - }), -]); - -// The workflow generator creates a basic workflow definition -console.log(workflowDef.name); // "my_workflow" -console.log(workflowDef.version); // 1 -console.log(workflowDef.tasks.length); // 2 -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/task-manager.md b/docs/api-reference/task-manager.md index ee10ff0f..5d80b647 100644 --- a/docs/api-reference/task-manager.md +++ b/docs/api-reference/task-manager.md @@ -1,524 +1,10 @@ -# TaskManager API Reference +# Task Manager -The `TaskManager` is responsible for initializing and managing the runners that poll and work different task queues. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new TaskManager(client: Client, workers: Array, config: TaskManagerConfig = {})` - -Creates a new TaskManager. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. -- `workers` (`Array`): An array of `ConductorWorker` instances. -- `config` (`TaskManagerConfig`, optional): Configuration for the `TaskManager`. - -**Example:** - -```typescript -import { TaskManager } from "@io-orkes/conductor-javascript"; - -const workers = [ - { - taskDefName: "email_task", - execute: async (task) => { - // Task execution logic - return { - status: "COMPLETED", - outputData: { sent: true }, - }; - }, - }, -]; - -const taskManager = new TaskManager(client, workers, { - options: { - concurrency: 5, - pollInterval: 100, - }, - maxRetries: 3, -}); -``` - ---- - -## Properties - -### `isPolling: boolean` - -Returns whether the `TaskManager` is currently polling for tasks. - ---- - -## Methods - -### `updatePollingOptionForWorker(workerTaskDefName: string, options: Partial): void` - -Updates the polling options for a specific worker. - -**Parameters:** - -- `workerTaskDefName` (`string`): The task definition name of the worker. -- `options` (`Partial`): The new polling options. - -**Example:** - -```typescript -import { TaskManager } from "@io-orkes/conductor-javascript"; - -const taskManager = new TaskManager(client, workers); - -// Update polling options for a specific worker -taskManager.updatePollingOptionForWorker("email_task", { - concurrency: 10, - pollInterval: 500, -}); -``` - ---- - -### `updatePollingOptions(options: Partial): void` - -Updates the polling options for all workers. - -**Parameters:** - -- `options` (`Partial`): The new polling options. - -**Example:** - -```typescript -import { TaskManager } from "@io-orkes/conductor-javascript"; - -const taskManager = new TaskManager(client, workers); - -// Update polling options for all workers -taskManager.updatePollingOptions({ - concurrency: 5, - pollInterval: 200, -}); -``` - ---- - -### `startPolling(): void` - -Starts polling for tasks for all workers. - -**Example:** - -```typescript -import { TaskManager } from "@io-orkes/conductor-javascript"; - -const taskManager = new TaskManager(client, workers); - -// Start polling for tasks -taskManager.startPolling(); -console.log(`Polling started: ${taskManager.isPolling}`); -``` - ---- - -### `stopPolling(): Promise` - -Stops polling for tasks for all workers. - -**Example:** - -```typescript -import { TaskManager } from "@io-orkes/conductor-javascript"; - -const taskManager = new TaskManager(client, workers); - -// Stop polling for tasks -await taskManager.stopPolling(); -console.log(`Polling stopped: ${taskManager.isPolling}`); -``` - ---- - -### `sanityCheck(): void` - -Performs a sanity check on the workers, ensuring there are no duplicates and that at least one worker is present. Throws an error if the check fails. - -**Example:** - -```typescript -import { TaskManager } from "@io-orkes/conductor-javascript"; - -const taskManager = new TaskManager(client, workers); - -// Perform sanity check -try { - taskManager.sanityCheck(); - console.log("All workers are valid"); -} catch (error) { - console.error("Worker configuration error:", error.message); -} -``` - -## Type Definitions - -### `TaskManagerConfig` - -```typescript -export interface TaskManagerConfig { - logger?: ConductorLogger; - options?: Partial; - onError?: TaskErrorHandler; - maxRetries?: number; -} -``` - -### `TaskManagerOptions` - -```typescript -export type TaskManagerOptions = TaskRunnerOptions; -``` - -### `TaskRunnerOptions` - -```typescript -export interface TaskRunnerOptions { - workerID: string; - domain: string | undefined; - pollInterval?: number; - concurrency?: number; - batchPollingTimeout?: number; -} -``` - -### `ConductorWorker` - -```typescript -export interface ConductorWorker { - taskDefName: string; - execute: ( - task: Task - ) => Promise>; - domain?: string; - concurrency?: number; - pollInterval?: number; -} -``` - -### `TaskErrorHandler` - -```typescript -export type TaskErrorHandler = (error: Error, task?: Task) => void; -``` - -### `ConductorLogger` - -```typescript -export interface ConductorLogger { - info(...args: unknown[]): void; - error(...args: unknown[]): void; - debug(...args: unknown[]): void; -} -``` - -### `DefaultLogger` - -```typescript -export declare class DefaultLogger implements ConductorLogger { - constructor(config?: DefaultLoggerConfig); - - info(...args: unknown[]): void; - error(...args: unknown[]): void; - debug(...args: unknown[]): void; -} -``` - -### `DefaultLoggerConfig` - -```typescript -export interface DefaultLoggerConfig { - level?: ConductorLogLevel; - tags?: object[]; -} -``` - -### `ConductorLogLevel` - -```typescript -export type ConductorLogLevel = "DEBUG" | "INFO" | "ERROR"; -``` - -### `Task` - -```typescript -export type Task = { - callbackAfterSeconds?: number; - callbackFromWorker?: boolean; - correlationId?: string; - domain?: string; - endTime?: number; - executed?: boolean; - executionNameSpace?: string; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - firstStartTime?: number; - inputData?: { - [key: string]: unknown; - }; - isolationGroupId?: string; - iteration?: number; - loopOverTask?: boolean; - outputData?: { - [key: string]: unknown; - }; - parentTaskId?: string; - pollCount?: number; - queueWaitTime?: number; - rateLimitFrequencyInSeconds?: number; - rateLimitPerFrequency?: number; - reasonForIncompletion?: string; - referenceTaskName?: string; - responseTimeoutSeconds?: number; - retried?: boolean; - retriedTaskId?: string; - retryCount?: number; - scheduledTime?: number; - seq?: number; - startDelayInSeconds?: number; - startTime?: number; - status?: - | "IN_PROGRESS" - | "CANCELED" - | "FAILED" - | "FAILED_WITH_TERMINAL_ERROR" - | "COMPLETED" - | "COMPLETED_WITH_ERRORS" - | "SCHEDULED" - | "TIMED_OUT" - | "SKIPPED"; - subWorkflowId?: string; - subworkflowChanged?: boolean; - taskDefName?: string; - taskDefinition?: TaskDef; - taskId?: string; - taskType?: string; - updateTime?: number; - workerId?: string; - workflowInstanceId?: string; - workflowPriority?: number; - workflowTask?: WorkflowTask; - workflowType?: string; -}; -``` - -### `TaskResult` - -```typescript -export type TaskResult = { - callbackAfterSeconds?: number; - extendLease?: boolean; - externalOutputPayloadStoragePath?: string; - logs?: Array; - outputData?: { - [key: string]: unknown; - }; - reasonForIncompletion?: string; - status?: - | "IN_PROGRESS" - | "FAILED" - | "FAILED_WITH_TERMINAL_ERROR" - | "COMPLETED"; - subWorkflowId?: string; - taskId: string; - workerId?: string; - workflowInstanceId: string; -}; -``` - -### `TaskResultStatusEnum` - -```typescript -export enum TaskResultStatusEnum { - IN_PROGRESS = "IN_PROGRESS", - FAILED = "FAILED", - FAILED_WITH_TERMINAL_ERROR = "FAILED_WITH_TERMINAL_ERROR", - COMPLETED = "COMPLETED", -} -``` - -### `RunnerArgs` - -```typescript -export interface RunnerArgs { - worker: ConductorWorker; - client: Client; - options: TaskRunnerOptions; - logger?: ConductorLogger; - onError?: TaskErrorHandler; - concurrency?: number; - maxRetries?: number; -} -``` - -### `TaskExecLog` - -```typescript -export type TaskExecLog = { - createdTime?: number; - log?: string; - taskId?: string; -}; -``` - -### `TaskDef` - -```typescript -export type TaskDef = { - name?: string; - description?: string; - retryCount?: number; - timeoutSeconds?: number; - inputKeys?: Array; - outputKeys?: Array; - timeoutPolicy?: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY"; - retryLogic?: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF"; - retryDelaySeconds?: number; - responseTimeoutSeconds?: number; - concurrentExecLimit?: number; - inputTemplate?: { - [key: string]: unknown; - }; - rateLimitPerFrequency?: number; - rateLimitFrequencyInSeconds?: number; - isolationGroupId?: string; - executionNameSpace?: string; - ownerEmail?: string; - pollTimeoutSeconds?: number; - backoffScaleFactor?: number; - createTime?: number; - updateTime?: number; - createdBy?: string; - updatedBy?: string; - accessPolicy?: { - [key: string]: Array; - }; - workflowTaskType?: string; - archivalConfig?: { - enabled?: boolean; - archiveAfterDays?: number; - }; -}; -``` - -### `WorkflowTask` - -```typescript -export type WorkflowTask = { - name?: string; - taskReferenceName?: string; - description?: string; - inputParameters?: { - [key: string]: unknown; - }; - type?: string; - dynamicTaskNameParam?: string; - caseValueParam?: string; - caseValues?: Array; - dynamicForkJoinTasksParam?: string; - dynamicForkTasksParam?: string; - dynamicForkTasksInputParamName?: string; - defaultCase?: Array; - forkTasks?: Array>; - startDelay?: number; - subWorkflowParam?: { - name?: string; - version?: number; - taskToDomain?: { - [key: string]: string; - }; - workflowDefinition?: WorkflowDef; - }; - joinOn?: Array; - sink?: string; - optional?: boolean; - taskDefinition?: TaskDef; - rateLimited?: boolean; - defaultExclusiveJoinTask?: Array; - asyncComplete?: boolean; - loopCondition?: string; - loopOver?: Array; - retryCount?: number; - evaluatorType?: string; - expression?: string; - decisionCases?: { - [key: string]: Array; - }; - scriptExpression?: string; - eventTaskName?: string; - eventTaskInput?: { - [key: string]: unknown; - }; - status?: string; - retryLogic?: string; - retryDelaySeconds?: number; - timeoutSeconds?: number; - timeoutPolicy?: string; - responseTimeoutSeconds?: number; - concurrentExecLimit?: number; - rateLimitPerFrequency?: number; - rateLimitFrequencyInSeconds?: number; - isolationGroupId?: string; - executionNameSpace?: string; - ownerEmail?: string; - onStateChange?: { - [key: string]: unknown; - }; - defaultRetryPolicy?: { - initialIntervalSeconds?: number; - backoffCoefficient?: number; - maximumAttempts?: number; - maximumIntervalSeconds?: number; - }; -}; -``` - -### `WorkflowDef` - -```typescript -export type WorkflowDef = { - cacheConfig?: CacheConfig; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - failureWorkflow?: string; - inputParameters?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - maskedFields?: Array; - metadata?: { - [key: string]: unknown; - }; - name: string; - outputParameters?: { - [key: string]: unknown; - }; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - rateLimitConfig?: RateLimitConfig; - restartable?: boolean; - schemaVersion?: number; - tasks: Array; - timeoutPolicy?: "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - updateTime?: number; - updatedBy?: string; - variables?: { - [key: string]: unknown; - }; - version?: number; - workflowStatusListenerEnabled?: boolean; - workflowStatusListenerSink?: string; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/template-client.md b/docs/api-reference/template-client.md index 481be89e..1ed53511 100644 --- a/docs/api-reference/template-client.md +++ b/docs/api-reference/template-client.md @@ -1,129 +1,10 @@ -# TemplateClient API Reference +# Template Client -The `TemplateClient` provides functionality for managing human task templates. Human task templates define the UI forms and schemas that are presented to users when they interact with human tasks in workflows. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new TemplateClient(client: Client)` - -Creates a new TemplateClient. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `registerTemplate(template: HumanTaskTemplate, asNewVersion: boolean = false): Promise` - -Register a new human task template or creates a new version of an existing template. - -**Parameters:** - -- `template` (`HumanTaskTemplate`): The human task template to register. -- `asNewVersion` (`boolean`, optional): Whether to create as a new version. Defaults to `false`. - -**Returns:** - -- `Promise`: The registered template. - -**Example:** - -```typescript -import { TemplateClient } from "@io-orkes/conductor-javascript"; - -const templateClient = new TemplateClient(client); - -// Register a new template -const template = { - name: "approval_form", - version: 1, - jsonSchema: { - type: "object", - properties: { - approved: { - type: "boolean", - description: "Approve the request", - }, - comments: { - type: "string", - description: "Additional comments", - }, - }, - required: ["approved"], - }, - templateUI: { - fields: [ - { - name: "approved", - type: "boolean", - label: "Approval", - required: true, - }, - { - name: "comments", - type: "text", - label: "Comments", - required: false, - }, - ], - }, -}; - -const registeredTemplate = await templateClient.registerTemplate(template); -console.log(`Template registered with version: ${registeredTemplate.version}`); -``` - -## Usage in Workflows - -Once registered, templates can be referenced in human tasks within workflows: - -```typescript -import { humanTask } from "@io-orkes/conductor-javascript"; - -const task = humanTask("approval_ref", "approval_task", { - template: "approval_form", // References the template name -}); -``` - -## Type Definitions - -### `HumanTaskTemplate` - -Human task template definition that specifies the form schema and UI for human tasks. - -```typescript -export type HumanTaskTemplate = { - createTime?: number; - createdBy?: string; - jsonSchema: { - [key: string]: unknown; - }; - name: string; - ownerApp?: string; - tags?: Array; - templateUI: { - [key: string]: unknown; - }; - updateTime?: number; - updatedBy?: string; - version: number; -}; -``` - -### `Tag` - -Tag associated with a template for categorization and search. - -```typescript -export type Tag = { - key?: string; - /** - * @deprecated - */ - type?: string; - value?: string; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/api-reference/workflow-executor.md b/docs/api-reference/workflow-executor.md index eec46f36..5643bea8 100644 --- a/docs/api-reference/workflow-executor.md +++ b/docs/api-reference/workflow-executor.md @@ -1,1021 +1,10 @@ -# WorkflowExecutor API Reference +# Workflow Executor -The `WorkflowExecutor` class is your main interface for managing workflows. It provides methods to register, start, monitor, and control workflow execution. +Per-client reference has consolidated into the API map, with generated TypeDoc as the authoritative signature reference: -## Constructor +- [API map — which client owns which operation](../api-map.md) +- [Workflows](../workflows.md) +- [Workers](../workers.md) +- [Documentation index](../README.md) -### `new WorkflowExecutor(client: Client)` - -Creates a new WorkflowExecutor. - -**Parameters:** - -- `client` (`Client`): An instance of `Client`. - ---- - -## Methods - -### `registerWorkflow(override: boolean, workflow: WorkflowDef): Promise` - -Registers a workflow definition with Conductor. - -**Parameters:** - -- `override` (`boolean`): Whether to override the existing workflow definition. -- `workflow` (`WorkflowDef`): The workflow definition. - -**Returns:** - -- `Promise` - -**Example:** - -```typescript -import { WorkflowExecutor, workflow } from "@io-orkes/conductor-javascript"; - -const executor = new WorkflowExecutor(client); - -// Register a workflow -await executor.registerWorkflow( - true, - workflow("email_workflow", [ - simpleTask("send_email", "email_task", { to: "user@example.com" }), - ]) -); -``` - ---- - -### `startWorkflow(workflowRequest: StartWorkflowRequest): Promise` - -Starts a new workflow execution. - -**Parameters:** - -- `workflowRequest` (`StartWorkflowRequest`): The request to start a workflow. - -**Returns:** - -- `Promise`: The ID of the workflow instance. - -**Example:** - -```typescript -import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; - -const executor = new WorkflowExecutor(client); - -// Start a workflow -const executionId = await executor.startWorkflow({ - name: "email_workflow", - version: 1, - input: { - to: "user@example.com", - subject: "Welcome!", - message: "Welcome to our platform!", - }, -}); - -console.log(`Workflow started with ID: ${executionId}`); -``` - ---- - -### `executeWorkflow(workflowRequest: StartWorkflowRequest, name: string, version: number, requestId: string, waitUntilTaskRef?: string): Promise` - -### `executeWorkflow(workflowRequest: StartWorkflowRequest, name: string, version: number, requestId: string, waitUntilTaskRef: string, waitForSeconds: number, consistency: Consistency, returnStrategy: ReturnStrategy): Promise` - -Executes a workflow synchronously and waits for completion. Can return different responses based on the provided parameters. - -**Parameters:** - -- `workflowRequest` (`StartWorkflowRequest`): The request to start a workflow. -- `name` (`string`): The name of the workflow. -- `version` (`number`): The version of the workflow. -- `requestId` (`string`): A unique ID for the request. -- `waitUntilTaskRef` (`string`, optional): The reference name of the task to wait for. -- `waitForSeconds` (`number`, optional): The number of seconds to wait for the task. -- `consistency` (`Consistency`, optional): The consistency level for the read operations. -- `returnStrategy` (`ReturnStrategy`, optional): The strategy for what data to return. - -**Returns:** - -- `Promise`: A `WorkflowRun` object or a `EnhancedSignalResponse` object. - -**Example:** - -```typescript -import { - WorkflowExecutor, - ReturnStrategy, -} from "@io-orkes/conductor-javascript"; - -const executor = new WorkflowExecutor(client); - -// Execute workflow synchronously -const workflowRun = await executor.executeWorkflow( - { - name: "data_processing", - version: 1, - input: { fileId: "file_123" }, - }, - "data_processing", - 1, - "req_123" -); - -console.log(`Workflow completed with status: ${workflowRun.status}`); -``` - ---- - -### `startWorkflows(workflowsRequest: StartWorkflowRequest[]): Promise[]` - -Starts multiple workflows at once. - -**Parameters:** - -- `workflowsRequest` (`StartWorkflowRequest[]`): An array of workflow start requests. - -**Returns:** - -- `Promise[]`: An array of promises that resolve to the workflow instance IDs. - -**Example:** - -```typescript -import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; - -const executor = new WorkflowExecutor(client); - -// Start multiple workflows -const workflowRequests = [ - { name: "email_workflow", version: 1, input: { to: "user1@example.com" } }, - { name: "email_workflow", version: 1, input: { to: "user2@example.com" } }, - { name: "email_workflow", version: 1, input: { to: "user3@example.com" } }, -]; - -const promises = executor.startWorkflows(workflowRequests); - -// Wait for all to complete -const executionIds = await Promise.all(promises); -console.log(`Started ${executionIds.length} workflows:`, executionIds); -``` - ---- - -### `goBackToTask(workflowInstanceId: string, taskFinderPredicate: TaskFinderPredicate, rerunWorkflowRequestOverrides: Partial = {}): Promise` - -Reruns a workflow from a specific task. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `taskFinderPredicate` (`TaskFinderPredicate`): A function to find the task to rerun from. -- `rerunWorkflowRequestOverrides` (`Partial`, optional): Overrides for the rerun request. - -**Returns:** - -- `Promise` - ---- - -### `goBackToFirstTaskMatchingType(workflowInstanceId: string, taskType: string): Promise` - -Reruns a workflow from the first task of a specific type. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `taskType` (`string`): The type of the task to rerun from. - -**Returns:** - -- `Promise` - ---- - -### `getWorkflow(workflowInstanceId: string, includeTasks: boolean, retry: number = 0): Promise` - -Gets the execution status of a workflow. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `includeTasks` (`boolean`): Whether to include the tasks in the response. -- `retry` (`number`, optional): The number of times to retry on failure. - -**Returns:** - -- `Promise`: The workflow execution status. - ---- - -### `getWorkflowStatus(workflowInstanceId: string, includeOutput: boolean, includeVariables: boolean): Promise` - -Gets a summary of the current workflow status. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `includeOutput` (`boolean`): Whether to include the output in the response. -- `includeVariables` (`boolean`): Whether to include the variables in the response. - -**Returns:** - -- `Promise`: The workflow status summary. - ---- - -### `getExecution(workflowInstanceId: string, includeTasks: boolean = true): Promise` - -Gets the execution status of a workflow, including tasks by default. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `includeTasks` (`boolean`, optional): Whether to include the tasks in the response. Defaults to `true`. - -**Returns:** - -- `Promise`: The workflow execution status. - ---- - -### `pause(workflowInstanceId: string): Promise` - -Pauses a running workflow. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. - -**Returns:** - -- `Promise` - ---- - -### `reRun(workflowInstanceId: string, rerunWorkflowRequest: Partial = {}): Promise` - -Reruns a workflow with new parameters. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `rerunWorkflowRequest` (`Partial`, optional): Overrides for the rerun request. - -**Returns:** - -- `Promise`: The ID of the new workflow instance. - ---- - -### `restart(workflowInstanceId: string, useLatestDefinitions: boolean): Promise` - -Restarts a workflow. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `useLatestDefinitions` (`boolean`): Whether to use the latest workflow definition. - -**Returns:** - -- `Promise` - ---- - -### `resume(workflowInstanceId: string): Promise` - -Resumes a paused workflow. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. - -**Returns:** - -- `Promise` - ---- - -### `retry(workflowInstanceId: string, resumeSubworkflowTasks: boolean): Promise` - -Retries a workflow from the last failing task. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `resumeSubworkflowTasks` (`boolean`): Whether to resume tasks in sub-workflows. - -**Returns:** - -- `Promise` - ---- - -### `search(start: number, size: number, query: string, freeText: string, sort: string = "", skipCache: boolean = false): Promise` - -Searches for workflows. - -**Parameters:** - -- `start` (`number`): The starting offset. -- `size` (`number`): The number of results to return. -- `query` (`string`): The search query. -- `freeText` (`string`): The free text to search for. -- `sort` (`string`, optional): The sort order. -- `skipCache` (`boolean`, optional): Whether to skip the cache. - -**Returns:** - -- `Promise`: The search results. - ---- - -### `skipTasksFromWorkflow(workflowInstanceId: string, taskReferenceName: string, skipTaskRequest: Partial): Promise` - -Skips a task in a running workflow. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `taskReferenceName` (`string`): The reference name of the task to skip. -- `skipTaskRequest` (`Partial`): The request to skip the task. - -**Returns:** - -- `Promise` - ---- - -### `terminate(workflowInstanceId: string, reason: string): Promise` - -Terminates a running workflow. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `reason` (`string`): The reason for termination. - -**Returns:** - -- `Promise` - ---- - -### `updateTask(taskId: string, workflowInstanceId: string, taskStatus: TaskResultStatus, outputData: Record): Promise` - -Updates a task by its ID. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `taskStatus` (`TaskResultStatus`): The new status of the task. -- `outputData` (`Record`): The output data of the task. - -**Returns:** - -- `Promise` - ---- - -### `updateTaskByRefName(taskReferenceName: string, workflowInstanceId: string, status: TaskResultStatus, taskOutput: Record): Promise` - -Updates a task by its reference name. - -**Parameters:** - -- `taskReferenceName` (`string`): The reference name of the task. -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `status` (`TaskResultStatus`): The new status of the task. -- `taskOutput` (`Record`): The output data of the task. - -**Returns:** - -- `Promise` - ---- - -### `getTask(taskId: string): Promise` - -Gets a task by its ID. - -**Parameters:** - -- `taskId` (`string`): The ID of the task. - -**Returns:** - -- `Promise`: The task. - ---- - -### `updateTaskSync(taskReferenceName: string, workflowInstanceId: string, status: TaskResultStatusEnum, taskOutput: Record, workerId?: string): Promise` - -Updates a task by its reference name synchronously and returns the complete workflow. - -**Parameters:** - -- `taskReferenceName` (`string`): The reference name of the task. -- `workflowInstanceId` (`string`): The ID of the workflow instance. -- `status` (`TaskResultStatusEnum`): The new status of the task. -- `taskOutput` (`Record`): The output data of the task. -- `workerId` (`string`, optional): The ID of the worker. - -**Returns:** - -- `Promise`: The updated workflow. - ---- - -### `signal(workflowInstanceId: string, status: TaskResultStatusEnum, taskOutput: Record, returnStrategy: ReturnStrategy = ReturnStrategy.TARGET_WORKFLOW): Promise` - -Signals a workflow task and returns data based on the specified return strategy. - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance to signal. -- `status` (`TaskResultStatusEnum`): The task status to set. -- `taskOutput` (`Record`): The output data for the task. -- `returnStrategy` (`ReturnStrategy`, optional): The strategy for what data to return. Defaults to `TARGET_WORKFLOW`. - -**Returns:** - -- `Promise`: The response from the signal. - ---- - -### `signalAsync(workflowInstanceId: string, status: TaskResultStatusEnum, taskOutput: Record): Promise` - -Signals a workflow task asynchronously (fire-and-forget). - -**Parameters:** - -- `workflowInstanceId` (`string`): The ID of the workflow instance to signal. -- `status` (`TaskResultStatusEnum`): The task status to set. -- `taskOutput` (`Record`): The output data for the task. - -**Returns:** - -- `Promise` - ---- - -## Type Definitions - -### `Consistency` - -```typescript -enum Consistency { - SYNCHRONOUS = "SYNCHRONOUS", - DURABLE = "DURABLE", - REGION_DURABLE = "REGION_DURABLE", -} -``` - -### `ReturnStrategy` - -```typescript -enum ReturnStrategy { - TARGET_WORKFLOW = "TARGET_WORKFLOW", - BLOCKING_WORKFLOW = "BLOCKING_WORKFLOW", - BLOCKING_TASK = "BLOCKING_TASK", - BLOCKING_TASK_INPUT = "BLOCKING_TASK_INPUT", -} -``` - -### `TaskResultStatusEnum` - -```typescript -enum TaskResultStatusEnum { - IN_PROGRESS = "IN_PROGRESS", - FAILED = "FAILED", - FAILED_WITH_TERMINAL_ERROR = "FAILED_WITH_TERMINAL_ERROR", - COMPLETED = "COMPLETED", -} -``` - -### `StartWorkflowRequest` - -```typescript -type StartWorkflowRequest = { - correlationId?: string; - createdBy?: string; - externalInputPayloadStoragePath?: string; - idempotencyKey?: string; - idempotencyStrategy?: "FAIL" | "RETURN_EXISTING" | "FAIL_ON_RUNNING"; - input?: { - [key: string]: unknown; - }; - name: string; - priority?: number; - taskToDomain?: { - [key: string]: string; - }; - version?: number; - workflowDef?: WorkflowDef; -}; -``` - -### `WorkflowDef` - -```typescript -interface WorkflowDef { - name: string; - description?: string; - version?: number; - tasks: WorkflowTask[]; - inputParameters?: string[]; - outputParameters?: Record; - failureWorkflow?: string; - schemaVersion?: number; - restartable?: boolean; - workflowStatusListenerEnabled?: boolean; - workflowStatusListenerSink?: string; - ownerEmail?: string; - ownerApp?: string; - timeoutPolicy?: "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds?: number; - variables?: Record; - inputTemplate?: Record; - inputSchema?: SchemaDef; - outputSchema?: SchemaDef; - enforceSchema?: boolean; - maskedFields?: string[]; - rateLimitConfig?: RateLimitConfig; - cacheConfig?: CacheConfig; - metadata?: Record; - createTime?: number; - updateTime?: number; - createdBy?: string; - updatedBy?: string; -} -``` - -### `CacheConfig` - -```typescript -export type CacheConfig = { - key?: string; - ttlInSecond?: number; -}; -``` - -### `RateLimitConfig` - -```typescript -export type RateLimitConfig = { - concurrentExecLimit?: number; - rateLimitKey?: string; -}; -``` - -### `WorkflowTask` - -```typescript -interface WorkflowTask { - name: string; - taskReferenceName: string; - type: string; - description?: string; - optional?: boolean; - inputParameters?: Record; - asyncComplete?: boolean; - startDelay?: number; - retryCount?: number; - evaluatorType?: string; - expression?: string; - decisionCases?: Record; - defaultCase?: WorkflowTask[]; - forkTasks?: WorkflowTask[][]; - joinOn?: string[]; - joinStatus?: string; - loopCondition?: string; - loopOver?: WorkflowTask[]; - dynamicTaskNameParam?: string; - dynamicForkTasksParam?: string; - dynamicForkTasksInputParamName?: string; - defaultExclusiveJoinTask?: string[]; - caseExpression?: string; - caseValueParam?: string; - sink?: string; - taskDefinition?: TaskDef; - rateLimited?: boolean; - permissive?: boolean; - cacheConfig?: CacheConfig; - onStateChange?: Record; - scriptExpression?: string; - subWorkflowParam?: SubWorkflowParams; -} -``` - -### `SubWorkflowParams` - -```typescript -export type SubWorkflowParams = { - idempotencyKey?: string; - idempotencyStrategy?: "FAIL" | "RETURN_EXISTING" | "FAIL_ON_RUNNING"; - name?: string; - taskToDomain?: { - [key: string]: string; - }; - version?: number; - workflowDefinition?: WorkflowDef; -}; -``` - -### `StateChangeEvent` - -```typescript -export type StateChangeEvent = { - payload?: { - [key: string]: unknown; - }; - type: string; -}; -``` - -### `WorkflowRun` - -```typescript -type WorkflowRun = { - correlationId?: string; - createTime?: number; - createdBy?: string; - input?: { - [key: string]: unknown; - }; - output?: { - [key: string]: unknown; - }; - priority?: number; - requestId?: string; - responseType?: - | "TARGET_WORKFLOW" - | "BLOCKING_WORKFLOW" - | "BLOCKING_TASK" - | "BLOCKING_TASK_INPUT"; - status?: - | "RUNNING" - | "COMPLETED" - | "FAILED" - | "TIMED_OUT" - | "TERMINATED" - | "PAUSED"; - targetWorkflowId?: string; - targetWorkflowStatus?: string; - tasks?: Array; - updateTime?: number; - variables?: { - [key: string]: unknown; - }; - workflowId?: string; -}; -``` - -### `EnhancedSignalResponse` - -```typescript -interface EnhancedSignalResponse extends SignalResponse { - correlationId?: string; - input?: { - [key: string]: unknown; - }; - output?: { - [key: string]: unknown; - }; - requestId?: string; - responseType?: - | "TARGET_WORKFLOW" - | "BLOCKING_WORKFLOW" - | "BLOCKING_TASK" - | "BLOCKING_TASK_INPUT"; - targetWorkflowId?: string; - targetWorkflowStatus?: string; - workflowId?: string; - priority?: number; - variables?: Record; - tasks?: Task[]; - createdBy?: string; - createTime?: number; - status?: string; - updateTime?: number; - taskType?: string; - taskId?: string; - referenceTaskName?: string; - retryCount?: number; - taskDefName?: string; - workflowType?: string; - isTargetWorkflow(): boolean; - isBlockingWorkflow(): boolean; - isBlockingTask(): boolean; - isBlockingTaskInput(): boolean; - getWorkflow(): Workflow; - getBlockingTask(): Task; - getTaskInput(): Record; - getWorkflowId(): string; - getTargetWorkflowId(): string; - hasWorkflowData(): boolean; - hasTaskData(): boolean; - getResponseType(): string; - isTerminal(): boolean; - isRunning(): boolean; - isPaused(): boolean; - getSummary(): string; - toDebugJSON(): Record; - toString(): string; -} -``` - -### `TaskFinderPredicate` - -```typescript -type TaskFinderPredicate = (task: Task) => boolean; -``` - -### `RerunWorkflowRequest` - -```typescript -type RerunWorkflowRequest = { - correlationId?: string; - reRunFromTaskId?: string; - reRunFromWorkflowId?: string; - taskInput?: { - [key: string]: unknown; - }; - workflowInput?: { - [key: string]: unknown; - }; -}; -``` - -### `Workflow` - -```typescript -type Workflow = { - correlationId?: string; - createTime?: number; - createdBy?: string; - endTime?: number; - event?: string; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - failedReferenceTaskNames?: Array; - failedTaskNames?: Array; - history?: Array; - idempotencyKey?: string; - input?: { - [key: string]: unknown; - }; - lastRetriedTime?: number; - output?: { - [key: string]: unknown; - }; - ownerApp?: string; - parentWorkflowId?: string; - parentWorkflowTaskId?: string; - priority?: number; - rateLimitKey?: string; - rateLimited?: boolean; - reRunFromWorkflowId?: string; - reasonForIncompletion?: string; - startTime?: number; - status?: - | "RUNNING" - | "COMPLETED" - | "FAILED" - | "TIMED_OUT" - | "TERMINATED" - | "PAUSED"; - taskToDomain?: { - [key: string]: string; - }; - tasks?: Array; - updateTime?: number; - updatedBy?: string; - variables?: { - [key: string]: unknown; - }; - workflowDefinition?: WorkflowDef; - workflowId?: string; - workflowName?: string; - workflowVersion?: number; -}; -``` - -### `WorkflowStatus` - -```typescript -type WorkflowStatus = { - correlationId?: string; - output?: { - [key: string]: unknown; - }; - status?: - | "RUNNING" - | "COMPLETED" - | "FAILED" - | "TIMED_OUT" - | "TERMINATED" - | "PAUSED"; - variables?: { - [key: string]: unknown; - }; - workflowId?: string; -}; -``` - -### `ScrollableSearchResultWorkflowSummary` - -```typescript -type ScrollableSearchResultWorkflowSummary = { - queryId?: string; - results?: Array; - totalHits?: number; -}; -``` - -### `WorkflowSummary` - -```typescript -export type WorkflowSummary = { - correlationId?: string; - createdBy?: string; - endTime?: string; - event?: string; - executionTime?: number; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - failedReferenceTaskNames?: string; - failedTaskNames?: Array; - idempotencyKey?: string; - input?: string; - inputSize?: number; - output?: string; - outputSize?: number; - priority?: number; - reasonForIncompletion?: string; - startTime?: string; - status?: - | "RUNNING" - | "COMPLETED" - | "FAILED" - | "TIMED_OUT" - | "TERMINATED" - | "PAUSED"; - taskToDomain?: { - [key: string]: string; - }; - updateTime?: string; - version?: number; - workflowId?: string; - workflowType?: string; -}; -``` - -### `SkipTaskRequest` - -```typescript -type SkipTaskRequest = { - taskInput?: { - [key: string]: unknown; - }; - taskOutput?: { - [key: string]: unknown; - }; -}; -``` - -### `TaskResultStatus` - -```typescript -type TaskResultStatus = - | "IN_PROGRESS" - | "FAILED" - | "FAILED_WITH_TERMINAL_ERROR" - | "COMPLETED"; -``` - -### `TaskDef` - -```typescript -type TaskDef = { - backoffScaleFactor?: number; - baseType?: string; - concurrentExecLimit?: number; - createTime?: number; - createdBy?: string; - description?: string; - enforceSchema?: boolean; - executionNameSpace?: string; - inputKeys?: Array; - inputSchema?: SchemaDef; - inputTemplate?: { - [key: string]: unknown; - }; - isolationGroupId?: string; - name: string; - outputKeys?: Array; - outputSchema?: SchemaDef; - ownerApp?: string; - ownerEmail?: string; - pollTimeoutSeconds?: number; - rateLimitFrequencyInSeconds?: number; - rateLimitPerFrequency?: number; - responseTimeoutSeconds?: number; - retryCount?: number; - retryDelaySeconds?: number; - retryLogic?: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF"; - timeoutPolicy?: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY"; - timeoutSeconds: number; - totalTimeoutSeconds: number; - updateTime?: number; - updatedBy?: string; -}; -``` - -### `Task` - -```typescript -type Task = { - callbackAfterSeconds?: number; - callbackFromWorker?: boolean; - correlationId?: string; - domain?: string; - endTime?: number; - executed?: boolean; - executionNameSpace?: string; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - firstStartTime?: number; - inputData?: { - [key: string]: unknown; - }; - isolationGroupId?: string; - iteration?: number; - loopOverTask?: boolean; - outputData?: { - [key: string]: unknown; - }; - parentTaskId?: string; - pollCount?: number; - queueWaitTime?: number; - rateLimitFrequencyInSeconds?: number; - rateLimitPerFrequency?: number; - reasonForIncompletion?: string; - referenceTaskName?: string; - responseTimeoutSeconds?: number; - retried?: boolean; - retriedTaskId?: string; - retryCount?: number; - scheduledTime?: number; - seq?: number; - startDelayInSeconds?: number; - startTime?: number; - status?: - | "IN_PROGRESS" - | "CANCELED" - | "FAILED" - | "FAILED_WITH_TERMINAL_ERROR" - | "COMPLETED" - | "COMPLETED_WITH_ERRORS" - | "SCHEDULED" - | "TIMED_OUT" - | "SKIPPED"; - subWorkflowId?: string; - subworkflowChanged?: boolean; - taskDefName?: string; - taskDefinition?: TaskDef; - taskId?: string; - taskType?: string; - updateTime?: number; - workerId?: string; - workflowInstanceId?: string; - workflowPriority?: number; - workflowTask?: WorkflowTask; - workflowType?: string; -}; -``` - -### `SchemaDef` - -```typescript -export type SchemaDef = { - createTime?: number; - createdBy?: string; - data?: { - [key: string]: unknown; - }; - externalRef?: string; - name: string; - ownerApp?: string; - type: "JSON" | "AVRO" | "PROTOBUF"; - updateTime?: number; - updatedBy?: string; - version: number; -}; -``` +Regenerate the TypeDoc surface with `npm run generate-docs`. diff --git a/docs/compatibility.md b/docs/compatibility.md new file mode 100644 index 00000000..4c646560 --- /dev/null +++ b/docs/compatibility.md @@ -0,0 +1,72 @@ +# Compatibility + +**Audience:** developers checking whether a capability exists on their server and +runtime. + +## Prerequisites + +None. + +## Runtime + +| | Supported | +|---|---| +| Node.js | >= 18. CI covers 20, 22, 24. | +| Modules | ESM and CommonJS — `import` and `require` both work. | +| TypeScript | 5.x. Both decorator styles supported — see [workers.md](workers.md#decorator-styles). | + +## Server families + +| | conductor-oss | Orkes Conductor | +|---|---|---| +| Core workflows and workers | Yes | Yes | +| Auth | Usually unauthenticated locally | Key/secret → JWT | +| Scheduler module | Optional | Included | +| Secret store | May be env-backed and read-only | Writable | +| Agent runtime | `>= 3.32.0-rc.8` | Requires `agentspan.embedded=true` | +| Credential delivery on `runtimeMetadata` | Needs conductor-oss PR #1255 | Yes | + +`CONDUCTOR_SERVER_TYPE=oss` gates Orkes-only integration tests out, so a run against +a standalone server skips rather than fails. + +## Capability gates worth knowing + +**Agent runtime.** Conductor agents need conductor-oss `>= 3.32.0-rc.8`, or +orkes-conductor booted with the `agentspan.embedded=true` boot property. That +property is owned by the server, not this SDK. + +**Credential delivery.** Tool credentials arrive wire-only on the polled task's +`runtimeMetadata`. A server that doesn't persist `TaskDef.runtimeMetadata` +(conductor-oss without PR #1255, agentspan server > 0.4.2) cannot deliver them, +and credentialed tools fail closed. See [security.md](security.md). + +**Read-only secret store.** A standalone OSS server's secret store can be +env-backed, where `PUT /api/secrets/{name}` returns 500 "env-backed secrets are +read-only" and credentials only exist if pre-seeded as server env vars before boot. +The e2e suite probes for this and skips affected steps. + +**Per-schedule verbs.** Pause and resume differ by server family. The SDK issues PUT +first and falls back to GET on HTTP 405, so one client works against both. + +**Streaming.** Agent streaming uses SSE where available and falls back to polling +otherwise. The fallback is silent — you get a correct result with no incremental +events. `SSEUnavailableError` and `SSETimeoutError` cover the explicit cases. + +## Sibling SDKs + +This SDK shares wire contracts with the Python and Java SDKs: the serialized agent +configuration, the plan format (`Ref` markers, step shape), and the agent +metadata keys. The server compiler is the same path for all three. See +[agents/reference/agent-schema.md](agents/reference/agent-schema.md) and +[documentation-parity.md](documentation-parity.md). + +## Backward compatibility + +The `AGENTSPAN_*` environment variables and the `AgentspanError` / +`AgentspanMetadata` exports remain available as deprecated aliases. See +[upgrading.md](upgrading.md) and [../BREAKING_CHANGES.md](../BREAKING_CHANGES.md). + +## Next steps + +[upgrading.md](upgrading.md) · [server-setup.md](server-setup.md) · +[workflow-testing.md](workflow-testing.md) diff --git a/docs/connection-authentication.md b/docs/connection-authentication.md new file mode 100644 index 00000000..4dad6985 --- /dev/null +++ b/docs/connection-authentication.md @@ -0,0 +1,107 @@ +# Connection and authentication + +**Audience:** developers configuring how the SDK reaches a Conductor server. + +## Prerequisites + +A reachable server ([server-setup.md](server-setup.md)) and the SDK installed. + +**Security note:** never commit a key or secret. Set them as environment +variables or inject them from a secret manager. The SDK trims whitespace from +`CONDUCTOR_AUTH_KEY` and `CONDUCTOR_AUTH_SECRET`, because a trailing newline +pasted into a CI secret is a common cause of "Invalid Access Key". + +## Creating a client + +```ts +import { OrkesClients } from "@io-orkes/conductor-javascript"; + +// Reads CONDUCTOR_SERVER_URL / CONDUCTOR_AUTH_KEY / CONDUCTOR_AUTH_SECRET from env +const clients = await OrkesClients.from(); +const executor = clients.getWorkflowClient(); +``` + +Or build a client explicitly: + +```ts +import { createConductorClient } from "@io-orkes/conductor-javascript"; + +const client = createConductorClient({ + serverUrl: "https://your-cluster.orkesconductor.io/api", + keyId: process.env.MY_KEY, + keySecret: process.env.MY_SECRET, +}); +``` + +`orkesConductorClient` is an alias of `createConductorClient` — the two are the +same function. + +**Expected result:** any client method resolves. On an unauthenticated OSS server, +leave the key and secret unset. + +## Environment variables + +| Variable | Purpose | +|---|---| +| `CONDUCTOR_SERVER_URL` | Server URL, with or without `/api`. Defaults to `http://localhost:8080`. | +| `CONDUCTOR_AUTH_KEY` | Auth key. Unset means no-auth mode. | +| `CONDUCTOR_AUTH_SECRET` | Auth secret. Set together with the key. | +| `CONDUCTOR_REQUEST_TIMEOUT_MS` | Per-request timeout. | +| `CONDUCTOR_CONNECT_TIMEOUT_MS` | Connection timeout. | +| `CONDUCTOR_REFRESH_TOKEN_INTERVAL` | JWT refresh interval. | +| `CONDUCTOR_RETRY_SERVER_ERRORS` | Retry 5xx responses. | +| `CONDUCTOR_DISABLE_HTTP2` | Force HTTP/1.1. | +| `CONDUCTOR_MAX_HTTP2_CONNECTIONS` | HTTP/2 connection pool size. | +| `CONDUCTOR_PROXY_URL` | Outbound proxy. | +| `CONDUCTOR_TLS_CERT_PATH` / `CONDUCTOR_TLS_KEY_PATH` / `CONDUCTOR_TLS_CA_PATH` | Client certs and custom CA. | +| `CONDUCTOR_TLS_INSECURE` | Skip certificate verification. Development only. | +| `CONDUCTOR_LOG_LEVEL` | SDK log level. | + +## Precedence + +Configuration resolves in this order (spec R3): + +1. `CONDUCTOR_SERVER_URL` / `CONDUCTOR_AUTH_KEY` / `CONDUCTOR_AUTH_SECRET` +2. explicit values passed to the constructor +3. `CONDUCTOR_AGENT_SERVER_URL` / `CONDUCTOR_AGENT_AUTH_KEY` / `CONDUCTOR_AGENT_AUTH_SECRET` +4. the deprecated `AGENTSPAN_*` spelling of (3), which warns once per name +5. `http://localhost:8080` + +**The core env vars outrank explicit constructor values.** That is deliberate — it +lets an operator redirect a deployed application without a code change — but it +surprises people who expect explicit configuration to win. If you need a client +that ignores the environment, unset the variables for that process. + +The agent-layer tier exists so an agent-only application can be configured with +one set of variables. See +[agents/reference/runtime.md](agents/reference/runtime.md). + +## Sharing one client + +Build one client and share it. Every client class and the agent runtime accept a +pre-built `ConductorClient`, which means one token mint rather than one per +component: + +```ts +const clients = await OrkesClients.from(); +const runtime = new AgentRuntime(clients.getClient()); +``` + +**Common failure mode:** constructing several clients from separate configs, each +minting and refreshing its own JWT. It works, but multiplies auth traffic and +makes token expiry harder to reason about. + +## TLS and proxies + +```shell +export CONDUCTOR_TLS_CA_PATH=/etc/ssl/certs/internal-ca.pem +export CONDUCTOR_PROXY_URL=http://proxy.internal:3128 +``` + +`CONDUCTOR_TLS_INSECURE=true` disables verification. Never set it outside local +development. + +## Next steps + +[core-quickstart.md](core-quickstart.md) · [api-map.md](api-map.md) · +[security.md](security.md) diff --git a/docs/core-quickstart.md b/docs/core-quickstart.md new file mode 100644 index 00000000..69ba7ca1 --- /dev/null +++ b/docs/core-quickstart.md @@ -0,0 +1,114 @@ +# Core quickstart + +**Audience:** developers writing their first Conductor workflow and worker in +TypeScript. + +## Prerequisites + +```shell +npm install @io-orkes/conductor-javascript +export CONDUCTOR_SERVER_URL=http://localhost:8080 +``` + +A running server ([server-setup.md](server-setup.md)) and Node.js >= 18. + +## The whole thing + +Create `quickstart.ts`: + +```ts +import { + OrkesClients, + ConductorWorkflow, + TaskHandler, + worker, + simpleTask, +} from "@io-orkes/conductor-javascript"; +import type { Task } from "@io-orkes/conductor-javascript"; + +// A worker is any TypeScript function. +@worker({ taskDefName: "greet" }) +async function greet(task: Task) { + return { + status: "COMPLETED" as const, + outputData: { result: `Hello ${task.inputData.name}` }, + }; +} + +async function main() { + const clients = await OrkesClients.from(); + const executor = clients.getWorkflowClient(); + + const workflow = new ConductorWorkflow(executor, "greetings") + .add(simpleTask("greet_ref", "greet", { name: "${workflow.input.name}" })) + .outputParameters({ result: "${greet_ref.output.result}" }); + + await workflow.register(); + + const handler = new TaskHandler({ + client: clients.getClient(), + scanForDecorated: true, + }); + await handler.startWorkers(); + + const run = await workflow.execute({ name: "Conductor" }); + console.log(`result: ${run.output?.result}`); + + await handler.stopWorkers(); +} + +main(); +``` + +```shell +npx tsx quickstart.ts +``` + +**Expected result:** `result: Hello Conductor`, and the execution visible in the +UI at `http://localhost:8080`. + +## What each piece does + +1. **`OrkesClients.from()`** reads connection config from the environment and + builds one shared client — see + [connection-authentication.md](connection-authentication.md). +2. **`ConductorWorkflow`** is the fluent builder. `simpleTask(ref, taskDefName, + input)` adds a worker task; `${workflow.input.name}` is a server-evaluated + expression, not TypeScript interpolation. +3. **`register()`** persists the definition. It defaults to `overwrite=true`, so + re-running replaces the definition rather than failing. +4. **`@worker`** registers the function in a global registry. + `scanForDecorated: true` makes `TaskHandler` pick it up. +5. **`startWorkers()`** is `async` — it registers task definitions before polling. + Await it. +6. **`execute()`** starts the workflow and waits for completion. + `startWorkflow()` returns immediately instead. + +## Common failure modes + +- **Task stuck in `SCHEDULED`.** No worker is polling for that `taskDefName`. + Workers must be started *before* the workflow executes, and the name in + `@worker({ taskDefName })` must match the name in `simpleTask(...)` exactly. +- **`result: undefined`.** The workflow's `outputParameters` reference a task ref + that doesn't match. `greet_ref` in `simpleTask("greet_ref", ...)` and in + `${greet_ref.output.result}` must be identical. +- **Process won't exit.** `stopWorkers()` wasn't reached. Wrap in `try`/`finally` + for anything long-lived. +- **Connection refused.** `CONDUCTOR_SERVER_URL` points somewhere else, or the + server isn't up. + +## Decorators and tsconfig + +The SDK supports both decorator styles. Class methods use TypeScript 5.0+ Stage 3 +decorators and need no compiler flag; standalone functions need +`"experimentalDecorators": true`. See [workers.md](workers.md#decorator-styles). + +## Cleanup + +`handler.stopWorkers()` stops polling. Registered workflow and task definitions +persist on the server; remove them via the metadata client or the UI. + +## Next steps + +[workflows.md](workflows.md) · [workers.md](workers.md) · +[agents/README.md](agents/README.md) — the agent layer diff --git a/docs/debugging.md b/docs/debugging.md new file mode 100644 index 00000000..c8a8bb0a --- /dev/null +++ b/docs/debugging.md @@ -0,0 +1,111 @@ +# Debugging + +**Audience:** developers diagnosing a stuck workflow, a failed task, or a silent +worker. + +## Prerequisites + +Access to the Conductor UI or the workflow client, and your worker logs. + +## Start here: read the execution + +```ts +const execution = await clients.getWorkflowClient().getExecution(workflowId); +console.log(execution.status); +for (const task of execution.tasks ?? []) { + console.log(task.referenceTaskName, task.taskDefName, task.status, task.reasonForIncompletion); +} +``` + +`getExecution()` includes task-level detail; `getWorkflow()` hits a different +endpoint and returns a different shape. For task status, use `getExecution()`. + +## Task stuck in SCHEDULED + +By far the most common report. The task was scheduled and nothing polled it. + +| Check | How | +|---|---| +| Is a worker running? | `handler.running`, `handler.runningWorkerCount` | +| Does the name match? | `@worker({ taskDefName })` must equal the second argument to `simpleTask(ref, taskDefName, …)` | +| Argument order | `simpleTask("greet_ref", "greet", …)` — **ref first**, then task name. Reversed, workers poll for a name nothing produces. | +| Started in time? | Workers must be polling before the workflow executes | +| Domain mismatch? | A worker with `domain: "x"` won't pick up tasks queued without a domain | +| New-style decorators | Class-method decorators only register when the class is instantiated — `void new Workers()` | + +For agents specifically: a run that hangs at a tool call usually means the +execution was triggered through the control-plane `AgentClient` while no `serve()` +process was polling. See +[agents/concepts/deploy-serve-run.md](agents/concepts/deploy-serve-run.md). + +## Task ran twice + +The lease lapsed and the server re-queued it. Enable lease extension and check that +`responseTimeoutSeconds >= 1.25`, since shorter values are silently skipped. See +[reliability.md](reliability.md#lease-extension). + +## Worker stops polling + +`TaskHandler` monitors and restarts polling loops by default. Alert on +`worker_restart_total` — a climbing count means something throws repeatedly. +Expose `handler.running` as a health check. + +## `${...}` appears literally in output + +The expression referenced a task ref or field that doesn't exist. The server leaves +unresolvable expressions as-is rather than erroring, so a typo in a ref name shows +up as literal text, not a failure. + +## Task fails and keeps retrying + +`throw new Error()` yields `FAILED` and retries per the task definition. For +failures a retry cannot fix — bad input, missing record — throw +`NonRetryableException` to get `FAILED_WITH_TERMINAL_ERROR` instead. + +## HTTP/2 connection errors + +The SDK uses Undici for HTTP/2 when available and falls back to HTTP/1.1 +automatically. To force HTTP/1.1: + +```shell +export CONDUCTOR_DISABLE_HTTP2=true +``` + +You can also supply a custom fetch: `orkesConductorClient(config, myFetch)`. + +## "Invalid Access Key" + +Usually a trailing newline in `CONDUCTOR_AUTH_KEY` or `CONDUCTOR_AUTH_SECRET`. The +SDK trims them, so if this persists, verify the key is valid for the cluster in +`CONDUCTOR_SERVER_URL` — a key from a different cluster produces the same message. + +## Configuration seems to be ignored + +The core `CONDUCTOR_*` environment variables **outrank** explicit constructor +values (spec R3). If a client isn't using the URL you passed, check the +environment. See +[connection-authentication.md](connection-authentication.md#precedence). + +## Deprecation warnings about AGENTSPAN_* + +Those variables were renamed to `CONDUCTOR_AGENT_*`. The old names still work and +warn once per name per process. See [upgrading.md](upgrading.md). + +## Turn up the logs + +```shell +export CONDUCTOR_LOG_LEVEL=DEBUG +``` + +Inside a worker, `getTaskContext()?.addLog("…")` attaches a log line to that task +execution, visible in the UI — the fastest way to see what one specific task did. + +## Process won't exit + +Something is still polling. `await handler.stopWorkers()` for workers, +`await runtime.shutdown()` for agents. Both belong in a `finally`. + +## Next steps + +[observability.md](observability.md) · [reliability.md](reliability.md) · +[workflow-testing.md](workflow-testing.md) diff --git a/docs/deployment-scaling.md b/docs/deployment-scaling.md new file mode 100644 index 00000000..f8cae786 --- /dev/null +++ b/docs/deployment-scaling.md @@ -0,0 +1,100 @@ +# Deployment and scaling + +**Audience:** developers sizing and shipping Conductor worker processes. + +## Prerequisites + +Workers running locally ([workers.md](workers.md)) and metrics wired up +([observability.md](observability.md)) — sizing without metrics is guessing. + +## Concurrency + +Two independent knobs per worker: + +| Option | Default | Meaning | +|---|---|---| +| `concurrency` | `1` | Max tasks executing simultaneously in this process. | +| `pollInterval` | `100` (ms) | How often to poll when slots are free. | + +```ts +@worker({ taskDefName: "resize_image", concurrency: 8, pollInterval: 50 }) +``` + +Tune without a code change: + +```shell +export CONDUCTOR_WORKER_ALL_CONCURRENCY=10 +export CONDUCTOR_WORKER_ALL_POLL_INTERVAL=500 +export CONDUCTOR_WORKER_RESIZE_IMAGE_CONCURRENCY=20 +``` + +**Node.js is single-threaded.** Raising `concurrency` helps I/O-bound work — HTTP +calls, database queries — and does not help CPU-bound work. For CPU-bound tasks, +scale processes, not concurrency; a `concurrency: 20` CPU-bound worker just queues +20 tasks behind one event loop and risks lease expiry on all of them. + +Watch `task_execution_queue_full_total`: a rising count means polling outpaces +execution. + +## Scaling out + +Run more worker processes. Conductor's queue distributes tasks across every worker +polling a given task type, so horizontal scaling needs no coordination. + +Give each replica a distinct `workerId` when you want to attribute work to an +instance; otherwise the SDK generates one. + +## Domains + +`domain` partitions a task type across worker pools — useful for tenancy or for +routing heavy work to bigger machines. + +```ts +@worker({ taskDefName: "process_payment", domain: "payments" }) +``` + +A worker with a domain **only** receives tasks queued for that domain. A domain +mismatch is a common cause of tasks sitting in `SCHEDULED` with a worker that looks +healthy. + +## Long-running tasks + +For work that exceeds `responseTimeoutSeconds`, enable lease extension rather than +inflating the timeout — see [reliability.md](reliability.md#lease-extension). For +work that waits on something external, return `IN_PROGRESS` with +`callbackAfterSeconds` so the slot is released. + +## Graceful shutdown + +```ts +process.on("SIGTERM", async () => { + await handler.stopWorkers(); + process.exit(0); +}); +``` + +`stopWorkers()` stops polling and lets in-flight tasks finish. Make your +orchestrator's termination grace period longer than your longest task, or in-flight +work is killed and re-queued — which is safe only if your workers are idempotent. + +## Containers + +The repository ships a `Dockerfile`. Points that matter: + +- Node.js >= 18; CI covers 20, 22, and 24. +- Set `CONDUCTOR_SERVER_URL` and auth via environment or secrets, never baked in. +- Expose the metrics port if you use `MetricsServer`. +- Use `handler.running` for the liveness probe and `handler.runningWorkerCount` for + readiness. + +## Agent processes + +An agent `serve()` process is a worker process — the same sizing applies. Note that +stateful agent runs create domain-scoped workers per execution, so a high volume of +concurrent stateful runs creates a lot of short-lived workers. See +[agents/concepts/deploy-serve-run.md](agents/concepts/deploy-serve-run.md). + +## Next steps + +[observability.md](observability.md) · [reliability.md](reliability.md) · +[security.md](security.md) diff --git a/docs/documentation-parity.md b/docs/documentation-parity.md new file mode 100644 index 00000000..573d0eb9 --- /dev/null +++ b/docs/documentation-parity.md @@ -0,0 +1,101 @@ +# Documentation parity + +**Audience:** maintainers keeping this documentation set aligned with the Java and +Python SDKs. + +## Prerequisites + +None. See [documentation-standard.md](documentation-standard.md) for how individual +pages are written. + +## Why parity + +The three SDKs share wire contracts — the serialized agent configuration, the plan +format, the agent metadata keys — and the same server compiler. Aligned +documentation means a reader can move between them, and a maintainer can diff the +sets to find gaps. + +The canonical structure originates in +[conductor-oss/java-sdk](https://github.com/conductor-oss/java-sdk); the +[Python SDK](https://github.com/conductor-oss/python-sdk) adopted it, and this +adopts it in turn. + +## Structure + +``` +docs/ +├── README.md ← index +├── .md ← ~20 pages +└── agents/ + ├── README.md ← agent index + ├── concepts/ ← 11 pages + ├── frameworks/ ← per-framework guides + └── reference/ ← API surface + agent-schema.json +``` + +## Language-specific deltas + +Parity is structural, not literal. Each SDK omits what doesn't apply and adds what +does. + +| Page | Java | Python | JavaScript | +|---|---|---|---| +| `spring-boot.md` | Yes | — | — | +| `file-client.md` | Yes | — | — | +| `documentation-parity.md` | — | Yes | Yes | +| `frameworks/langchain4j.md`, `langgraph4j.md` | Yes | — | — | +| `frameworks/langchain.md`, `langgraph.md` | — | Yes | Yes | +| `frameworks/claude-agent-sdk.md` | — | Yes | — | +| `frameworks/vercel-ai.md` | — | — | **Yes** | + +`frameworks/vercel-ai.md` is unique to this SDK: `src/agents/wrappers/ai.ts` is a +JavaScript-only bridge with no Java or Python counterpart. The canonical set has no +slot for it, so it is an addition rather than a substitution. + +Conversely, this SDK has no `claude-agent-sdk.md` — there is no such wrapper here. + +## Terminology + +Shared with the sibling SDKs: **"Conductor agents"** as the noun, +**"Conductor-agent"** hyphenated only attributively. Every page opens with an +`**Audience:**` line and a `## Prerequisites` section. See +[documentation-standard.md](documentation-standard.md). + +## Legacy paths + +Adopting the structure did not break existing links. The previous +`docs/agents/*.md` pages and `docs/api-reference/*.md` remain as redirect stubs +pointing at their replacements, as the Python SDK did. + +One consequence worth recording: Java's CI greps to **reject** +`docs/agents/api-reference.md` as a retired path, while Python **keeps** it as a +stub. This SDK follows Python — the stub is deliberate — so the retired-reference +grep here omits that pattern and `docs/agents/index.md`. The two siblings genuinely +disagree; preserving inbound links to a public SDK's docs won. + +## Validation + +CI checks, in `.github/workflows/pull_request.yml`: + +| Check | Tool | +|---|---| +| Internal Markdown links | `lycheeverse/lychee-action`, `--offline` over `README.md` and `docs` | +| Retired references | `grep`, narrowed to permit the stubs above | +| Agent configuration schema | `scripts/verify-agent-schema.mjs` against `e2e/_configs/*.json` | + +```shell +npm run verify:agent-schema +``` + +## Adding a page + +1. Add it under the canonical path, or justify the delta in the table above. +2. Follow [documentation-standard.md](documentation-standard.md). +3. Link it from [README.md](README.md) — the link checker only walks reachable + pages. +4. If it replaces an existing page, leave a stub rather than deleting it. + +## Next steps + +[documentation-standard.md](documentation-standard.md) · [README.md](README.md) · +[compatibility.md](compatibility.md) diff --git a/docs/documentation-standard.md b/docs/documentation-standard.md new file mode 100644 index 00000000..a7f80e5d --- /dev/null +++ b/docs/documentation-standard.md @@ -0,0 +1,39 @@ +# Documentation standard + +Every primary JavaScript SDK guide must include its audience and prerequisites, +an OSS/Orkes capability label when behavior differs, and a security note when it +handles credentials, user data, tools, or external side effects. + +Commands must be runnable or marked **Fragment** and linked to a complete +repository example. State the expected result, common failure modes, cleanup, +and next steps. Use `@io-orkes/conductor-javascript` and published npm versions +rather than stale pinned versions. CI validates internal Markdown links and +curated example paths. + +## Terminology + +- **Conductor agents** — the noun. Not "Agentspan agents", not "AI agents". +- **Conductor-agent** — hyphenated only as an attributive adjective, as in + "Conductor-agent lifecycle" or "Conductor-agent API reference". +- **Conductor server** — the server. Say "OSS" or "Orkes" when the distinction + matters. + +This vocabulary is shared with the [Python](https://github.com/conductor-oss/python-sdk) +and [Java](https://github.com/conductor-oss/java-sdk) SDKs so the three +documentation sets stay comparable. See [documentation-parity.md](documentation-parity.md). + +## Page shape + +Open with an `**Audience:**` line, then `## Prerequisites`, then the content. +Close with next steps. Tables that orient the reader use a +goal / guide / expected-result shape. + +## TypeScript specifics + +- Examples use ESM `import`. The package ships ESM and CommonJS, so `require` + works too; say so once rather than duplicating every snippet. +- Import agent symbols from the `/agents` subpath, never the package root — the + root re-exports the generated OpenAPI surface and would collide. +- Prefer `npx tsx file.ts` for runnable snippets. +- Snippets that need `zod` or a framework peer dependency say so in + Prerequisites. diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000..9f7f4ee6 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,88 @@ +# Examples + +**Audience:** developers looking for a runnable starting point. + +## Prerequisites + +A running server ([server-setup.md](server-setup.md)) and credentials in the +environment or a `.env` file: + +```shell +export CONDUCTOR_SERVER_URL=http://localhost:8080 +``` + +Core examples run straight from the repository: + +```shell +npx tsx examples/quickstart.ts +``` + +Agent examples resolve the package name to `src/agents` sources via +`examples/agents/tsconfig.json`: + +```shell +npx tsx examples/agents/01-basic-agent.ts +``` + +Framework subdirectories install their own dependencies: + +```shell +./scripts/install-example-deps.sh +``` + +## Core + +| Example | Covers | +|---|---| +| [quickstart.ts](../examples/quickstart.ts) | `@worker` + workflow + `execute`. The 60-second intro. | +| [helloworld.ts](../examples/helloworld.ts) | Smallest possible workflow. | +| [workers-e2e.ts](../examples/workers-e2e.ts) | Three chained workers with verification. | +| [kitchensink.ts](../examples/kitchensink.ts) | HTTP tasks, waits, switch branching. | +| [dynamic-workflow.ts](../examples/dynamic-workflow.ts) | Building a workflow at runtime. | +| [workflow-ops.ts](../examples/workflow-ops.ts) | Full lifecycle: pause, resume, terminate, retry, restart. | +| [worker-configuration.ts](../examples/worker-configuration.ts) | Concurrency, poll interval, domains. | +| [task-configure.ts](../examples/task-configure.ts) | Task definition fields. | +| [task-context.ts](../examples/task-context.ts) | `IN_PROGRESS`, callbacks, task logs. | +| [metrics.ts](../examples/metrics.ts) | Prometheus collector and scrape server. | +| [event-listeners.ts](../examples/event-listeners.ts) | `TaskHandler` event listeners. | +| [express-worker-service.ts](../examples/express-worker-service.ts) | Workers inside an HTTP service. | +| [perf-test.ts](../examples/perf-test.ts) | Throughput harness. | +| [test-workflows.ts](../examples/test-workflows.ts) | Testing patterns. | + +`examples/advanced/` covers fork/join and human tasks; `examples/api-journeys/` +walks the application, authorization, and event-handler APIs. + +## Agents + +`examples/agents/` holds 113 numbered examples, roughly in increasing complexity — +basic agents, tools, multi-agent strategies, guardrails, streaming, HITL, memory, +credentials, code execution, plans, and schedules. + +| Start with | Covers | +|---|---| +| [01-basic-agent.ts](../examples/agents/01-basic-agent.ts) | Smallest agent. | +| [02a-simple-tools.ts](../examples/agents/02a-simple-tools.ts) | One local tool. | +| [03-multi-agent.ts](../examples/agents/03-multi-agent.ts) | Multi-agent orchestration. | +| [04-guardrails.ts](../examples/agents/04-guardrails.ts) | Input and output validation. | +| [05-streaming.ts](../examples/agents/05-streaming.ts) | Event streaming. | +| [06-hitl.ts](../examples/agents/06-hitl.ts) | Approval gates. | + +Framework subdirectories — `adk/`, `langgraph/`, `openai/`, `vercel-ai/`, and +`quickstart/` — each carry their own `package.json` and README. See +[agents/frameworks/openai.md](agents/frameworks/openai.md) and its siblings. + +## Common failure modes + +- **Connection refused.** `CONDUCTOR_SERVER_URL` unset or pointing at the UI port. +- **An agent example completing with an error in the output.** The model isn't + configured on the server. Provider keys belong to the **server** process. +- **A framework example failing to import.** Run + `./scripts/install-example-deps.sh`; framework peer dependencies are optional and + not installed by default. +- **An example hanging.** A local tool with nothing polling it, or a missing + `shutdown()`. + +## Next steps + +[core-quickstart.md](core-quickstart.md) · [agents/README.md](agents/README.md) · +[../examples/README.md](../examples/README.md) — the full catalog diff --git a/docs/observability.md b/docs/observability.md new file mode 100644 index 00000000..7e9cb636 --- /dev/null +++ b/docs/observability.md @@ -0,0 +1,278 @@ +# Observability + +**Audience:** developers instrumenting Conductor workers with metrics and logs. + +## Prerequisites + +Workers running ([workers.md](workers.md)). Prometheus integration optionally +needs `npm install prom-client`. + +## Metrics + +Create a collector, start a scrape server, and wire the collector into +`TaskHandler` as an event listener: + +```ts +import { + createMetricsCollector, + MetricsServer, + TaskHandler, +} from "@io-orkes/conductor-javascript"; + +const metrics = createMetricsCollector(); +const server = new MetricsServer(metrics, 9090); +await server.start(); + +const handler = new TaskHandler({ + client, + eventListeners: [metrics], + scanForDecorated: true, +}); +await handler.startWorkers(); +``` + +**Expected result:** + +- `GET http://localhost:9090/metrics` — Prometheus text format +- `GET http://localhost:9090/health` — `{"status":"UP"}` + +**Common failure mode:** metrics are empty. The collector must be passed in +`eventListeners` — creating it is not enough, since it collects by observing +worker events. + +## Two metric surfaces + +`createMetricsCollector()` reads `WORKER_CANONICAL_METRICS` and returns either a +`LegacyMetricsCollector` or a `CanonicalMetricsCollector`. Both implement +`MetricsCollectorInterface`, so call sites don't care which is active. + +| | Legacy (default) | Canonical (opt-in) | +|---|---|---| +| Names | Prefixed `conductor_worker_` | Unprefixed | +| Type | Summary | Histogram | +| Time units | Milliseconds | Seconds | +| Size units | — | Bytes | +| Labels | snake_case | camelCase | + +```shell +WORKER_CANONICAL_METRICS=true node my_worker.js +``` + +Accepted true values are `true`, `1`, `yes`, case-insensitive. The variable is read +when the collector is **created**, so changing it needs a worker restart. + +Canonical time histograms use buckets `0.001, 0.005, 0.01, 0.025, 0.05, 0.1, +0.25, 0.5, 1, 2.5, 5, 10`. + +Prefer canonical for new deployments — histograms aggregate correctly across +instances, Summary quantiles don't. Canonical mode is opt-in during the deprecation +period; update dashboards and alerts against a staging worker before switching +production. + +## Configuration + +| Option | Default | Purpose | +|---|---|---| +| `prefix` | `"conductor_worker"` | Name prefix. Legacy only. | +| `httpPort` | — | Start the built-in HTTP server. | +| `filePath` | — | Periodically write metrics to a file. | +| `fileWriteIntervalMs` | `5000` | File write interval. | +| `slidingWindowSize` | `1000` | Quantile window. Legacy only. | +| `usePromClient` | `false` | Register in `prom-client`'s default registry. | + +```ts +const metrics = createMetricsCollector({ + filePath: "/tmp/conductor_metrics.prom", + fileWriteIntervalMs: 10000, +}); +``` + +The file writer does an immediate first write, then writes on the interval. Its +timer is unreferenced, so it never keeps the process alive. + +`usePromClient: true` requires `prom-client` and falls back to the built-in text +format if it isn't installed — a silent fallback worth knowing about if you expect +metrics in the default registry and don't find them. + +## What to alert on + +| Signal | Why | +|---|---| +| `worker_restart_total` | `TaskHandler` restarted a polling loop — something is crashing. | +| `task_update_error_total` | Results are failing to reach the server; work may be re-queued. | +| Heartbeat errors in logs | Lease extension is failing — risk of duplicate execution. See [reliability.md](reliability.md). | +| `handler.runningWorkerCount` | Health check: expected worker count. | +| `task_execution_queue_full_total` | Concurrency is saturated — see [deployment-scaling.md](deployment-scaling.md). | + +## Health checks + +```ts +app.get("/health", (_req, res) => { + res.json({ up: handler.running, workers: handler.runningWorkerCount }); +}); +``` + +## Logging + +`CONDUCTOR_LOG_LEVEL` sets the SDK log level. Inside a worker, +`getTaskContext()?.addLog()` streams a log line into the Conductor UI, attached to +that task execution — the fastest way to explain *why* a specific task did what it +did. + +## Cardinality + +Labels are keyed by task type and exception class. Avoid adding per-execution +identifiers as labels. Canonical HTTP metrics use a bounded path **template** +(`/workflow/{workflowId}`) rather than the interpolated path, precisely to keep +cardinality bounded — don't undo that with custom labels. + +--- + +# Metric reference + +## Canonical metrics + +Time values are seconds, size values are bytes, label names are camelCase. + +### Counters + +| Metric | Labels | Description | +|---|---|---| +| `task_poll_total` | `taskType` | Each poll request issued. | +| `task_execution_started_total` | `taskType` | A polled task dispatched to the worker function. | +| `task_poll_error_total` | `taskType`, `exception` | Poll request failed client-side. | +| `task_execute_error_total` | `taskType`, `exception` | The worker function threw. | +| `task_update_error_total` | `taskType`, `exception` | Updating the task result failed. | +| `task_ack_error_total` | `taskType`, `exception` | Ack errors. The internal runner uses batch poll responses as ack, so this may not emit during normal polling. | +| `task_ack_failed_total` | `taskType` | Failed ack responses. Same caveat. | +| `task_execution_queue_full_total` | `taskType` | Execution queue saturated. | +| `task_paused_total` | `taskType` | Worker paused and skipped acting on a poll. | +| `thread_uncaught_exceptions_total` | `exception` | Uncaught exception in the worker process. | +| `external_payload_used_total` | `entityName`, `operation`, `payloadType` | External payload storage used. | +| `workflow_start_error_total` | `workflowType`, `exception` | Starting a workflow failed client-side. | + +### Time histograms + +Buckets: `0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10`. + +| Metric | Labels | Description | +|---|---|---| +| `task_poll_time_seconds` | `taskType`, `status` | Poll latency. `status` is `SUCCESS` or `FAILURE`. | +| `task_execute_time_seconds` | `taskType`, `status` | Worker function duration. | +| `task_update_time_seconds` | `taskType`, `status` | Task-result update latency. | +| `http_api_client_request_seconds` | `method`, `uri`, `status` | API-client HTTP latency. `status` is the HTTP code as a string, or `"0"` on network failure. | + +```prometheus +task_execute_time_seconds_bucket{taskType="my_task",status="SUCCESS",le="0.1"} 42 +task_execute_time_seconds_count{taskType="my_task",status="SUCCESS"} 50 +task_execute_time_seconds_sum{taskType="my_task",status="SUCCESS"} 2.3 +``` + +### Size histograms + +Buckets: `100, 1000, 10000, 100000, 1000000, 10000000`. + +| Metric | Labels | Description | +|---|---|---| +| `task_result_size_bytes` | `taskType` | Serialized task result size. | +| `workflow_input_size_bytes` | `workflowType`, `version` | Serialized workflow input size. `version` is `""` when absent. | + +### Gauges + +| Metric | Labels | Description | +|---|---|---| +| `active_workers` | `taskType` | Workers currently executing tasks. | + +## Legacy metrics + +Default mode, so existing dashboards keep working. Prefix `conductor_worker` +(configurable via `prefix`). Distribution metrics are sliding-window summaries over +the latest 1,000 observations (`slidingWindowSize`), exposing p50, p75, p90, p95, +p99 plus `_count` and `_sum`. + +### Counters + +| Metric | Labels | Description | +|---|---|---| +| `conductor_worker_task_poll_total` | `task_type` | Each poll. | +| `conductor_worker_task_poll_error_total` | `task_type` | Poll failed. | +| `conductor_worker_task_execute_total` | `task_type` | Task execution completed. | +| `conductor_worker_task_execute_error_total` | `task_type` | Execution errors. Label format `taskType:ExceptionName`. | +| `conductor_worker_task_update_error_total` | `task_type` | Result update failed. | +| `conductor_worker_task_ack_error_total` | `task_type` | Ack errors. | +| `conductor_worker_task_execution_queue_full_total` | `task_type` | Queue saturated. | +| `conductor_worker_task_paused_total` | `task_type` | Worker paused, poll skipped. | +| `conductor_worker_external_payload_used_total` | `payload_type` | External payload storage used. | +| `conductor_worker_thread_uncaught_exceptions_total` | none | Uncaught exceptions. | +| `conductor_worker_worker_restart_total` | none | Worker restart events. | +| `conductor_worker_workflow_start_error_total` | none | Workflow start errors. | + +Legacy mode does **not** emit `task_execution_started_total`, +`task_ack_failed_total`, or `active_workers`. + +### Time and size metrics + +Summary type. Time in milliseconds, size in bytes. + +| Metric | Labels | Description | +|---|---|---| +| `conductor_worker_task_poll_time` | `task_type` | Poll round-trip. | +| `conductor_worker_task_execute_time` | `task_type` | Worker function duration. | +| `conductor_worker_task_update_time` | `task_type` | Result update duration. | +| `conductor_worker_task_result_size_bytes` | `task_type` | Task result payload size. | +| `conductor_worker_workflow_input_size_bytes` | `workflow_type` | Workflow input payload size. | +| `conductor_worker_http_api_client_request` | `endpoint` | API request duration (ms). `endpoint` is a compound `"METHOD:/api/path:STATUS"` string. | + +```prometheus +conductor_worker_task_execute_time{task_type="my_task",quantile="0.5"} 102 +conductor_worker_task_execute_time{task_type="my_task",quantile="0.95"} 250 +conductor_worker_task_execute_time_count{task_type="my_task"} 1000 +conductor_worker_task_execute_time_sum{task_type="my_task"} 120345 +``` + +## Labels + +| Label | Used by | Values | +|---|---|---| +| `task_type` | Legacy worker metrics | Task definition name. | +| `taskType` | Canonical worker metrics | Task definition name. | +| `workflowType` | Canonical workflow metrics | Workflow definition name. | +| `workflow_type` | Legacy `workflow_input_size_bytes` | Workflow definition name. | +| `version` | Canonical `workflow_input_size_bytes` | Workflow version as a string; `""` when absent. | +| `status` | Canonical task time histograms | `SUCCESS` or `FAILURE`. For `http_api_client_request_seconds`, the HTTP code as a string or `"0"`. | +| `exception` | Canonical error counters | Exception type name, from `error.name` or `error.constructor.name`. | +| `entityName` | Canonical `external_payload_used_total` | Task type or workflow name. | +| `operation` | Canonical `external_payload_used_total` | `READ`, `WRITE`. | +| `payload_type` | Legacy external-payload counter | e.g. `workflow_input`, `task_output`. | +| `payloadType` | Canonical external-payload counter | `TASK_INPUT`, `TASK_OUTPUT`, `WORKFLOW_INPUT`, `WORKFLOW_OUTPUT`. | +| `method` | Canonical HTTP metrics | HTTP verb. | +| `uri` | Canonical HTTP metrics | Bounded path template, e.g. `/workflow/{workflowId}`. | +| `endpoint` | Legacy HTTP metrics | Compound `"METHOD:/api/path:STATUS"`. | +| `quantile` | Legacy time and size metrics | `0.5`, `0.75`, `0.9`, `0.95`, `0.99`. | + +## Implementation notes + +**One collector per process.** Only one collector can be active at a time. +`CanonicalMetricsCollector` registers itself as the global HTTP metrics observer on +construction; `LegacyMetricsCollector` does not — use `createMetricsCollector()` or +call `setHttpMetricsObserver()` explicitly to get HTTP metrics in legacy mode. +Creating a second collector **replaces** the first, silently. `stop()` clears the +global observer. + +**Payload size measurement.** `workflow_input_size_bytes` is recorded by +`JSON.stringify`-ing the workflow input and measuring UTF-8 length with +`Buffer.byteLength`. Controlled by `measurePayloadSize`: canonical defaults to +`true` (measured on every `startWorkflow`), legacy defaults to `false`. Disable it +if your inputs are large and the serialization cost matters. + +**Legacy `task_paused_total`.** Defined but never recorded in any release. It stays +unrecorded in legacy mode to preserve byte-for-byte output compatibility. Switch to +canonical to get paused metrics. + +**`WORKER_LEGACY_METRICS`** is reserved and not read by the current implementation. +Once canonical becomes the default, it will re-activate the legacy surface. + +## Next steps + +[debugging.md](debugging.md) · [reliability.md](reliability.md) · +[deployment-scaling.md](deployment-scaling.md) diff --git a/docs/reliability.md b/docs/reliability.md new file mode 100644 index 00000000..358b8a47 --- /dev/null +++ b/docs/reliability.md @@ -0,0 +1,181 @@ +# Reliability + +**Audience:** developers running workers whose failures must not cause duplicate +or lost work. + +## Prerequisites + +Workers running ([workers.md](workers.md)) and the ability to set task definition +fields via the metadata client. + +## Lease extension + +When a task is polled, the server starts a `responseTimeoutSeconds` timer. If no +update arrives before it expires, the server **re-queues the task** — which can +mean duplicate execution by a second worker. For anything that takes longer than +its response timeout, that is the failure mode to design against. + +Lease extension sends a periodic heartbeat that resets the timer. + +```ts +import { worker } from "@io-orkes/conductor-javascript"; + +@worker({ + taskDefName: "process_video", + leaseExtendEnabled: true, // heartbeat at 80% of responseTimeoutSeconds +}) +async function processVideo(task: Task): Promise { + await encodeVideo(task.inputData.videoUrl); // takes minutes + return { status: "COMPLETED", outputData: { done: true } }; +} +``` + +**Expected result:** a task that runs far longer than `responseTimeoutSeconds` +completes once, with no duplicate execution. + +### How it works + +1. `LeaseTracker` records the polled task and computes + `intervalMs = responseTimeoutSeconds × 0.8 × 1000`. +2. A 100 ms-tick `setInterval` runs **independently of the poll loop**, so + heartbeats fire even when every concurrency slot is busy executing. +3. On each interval an `extendLease: true` update resets the server timer. +4. The task is untracked as soon as `execute()` resolves, before the final result + is submitted. + +Running the heartbeat off the poll loop is the whole point: if all slots are full, +no new tasks are polled, but the in-flight tasks still need their leases kept +alive. + +### Requirements and limits + +The task definition must have `responseTimeoutSeconds >= 1.25`. Anything shorter +computes an interval under 1000 ms and is **silently skipped** — matching the +Python SDK. If lease extension seems to do nothing, check this first. + +```ts +await metadataClient.registerTask({ + name: "process_video", + responseTimeoutSeconds: 60, // heartbeat every 48s + timeoutSeconds: 3600, // hard ceiling — NOT extended by heartbeats + retryCount: 0, +}); +``` + +`leaseExtendEnabled` resets `responseTimeoutSeconds` on each heartbeat. It does +**not** extend `timeoutSeconds`, the total execution ceiling. A task that outlives +`timeoutSeconds` is timed out no matter how faithfully it heartbeats. + +### Environment overrides + +```shell +# Per-worker (highest priority) +CONDUCTOR_WORKER_MY_TASK_LEASE_EXTEND_ENABLED=true + +# Global +CONDUCTOR_WORKER_ALL_LEASE_EXTEND_ENABLED=true +``` + +### Constants + +| Constant | Value | Meaning | +|---|---|---| +| `LEASE_EXTEND_DURATION_FACTOR` | `0.8` | Heartbeat at 80% of `responseTimeoutSeconds` | +| `LEASE_EXTEND_RETRY_COUNT` | `3` | Retries per heartbeat | +| `HEARTBEAT_CHECK_INTERVAL_MS` | `100` | Due-check tick | +| `HEARTBEAT_RETRY_DELAY_MS` | `500` | Delay between retries | + +A failed heartbeat is retried three times with a 500 ms delay. If all retries +fail the error is logged, the task stays tracked, and the next interval tries +again — **the task is not failed because of heartbeat errors**. That is +deliberate, but it means a sustained heartbeat outage shows up as duplicate +execution rather than as a task failure. Alert on heartbeat errors in your logs. + +### Direct LeaseTracker usage + +For custom worker implementations that bypass `TaskRunner`, `LeaseTracker` is part +of the public API: + +```ts +import { LeaseTracker, TaskResource, orkesConductorClient } from "@io-orkes/conductor-javascript"; +import type { LeaseInfo } from "@io-orkes/conductor-javascript"; + +const client = await orkesConductorClient(); + +const tracker = new LeaseTracker( + // sendHeartbeatFn — called on each heartbeat + async (taskId, workflowInstanceId) => { + await TaskResource.updateTask({ + client, + body: { taskId, workflowInstanceId, status: "IN_PROGRESS", extendLease: true }, + throwOnError: true, + }); + }, + logger, +); + +tracker.start(); // start the 100ms check interval +tracker.track(task); // track a polled task +// ... worker executes ... +tracker.untrack(task.taskId!); // untrack as soon as execute() resolves +tracker.stop(); // stop the interval on shutdown +``` + +`LeaseInfo` describes the tracked state for one task: + +```ts +interface LeaseInfo { + readonly taskId: string; + readonly workflowInstanceId: string; + readonly responseTimeoutSeconds: number; + readonly lastHeartbeatTime: number; // Date.now() of the last successful heartbeat + readonly intervalMs: number; // responseTimeoutSeconds × 0.8 × 1000 + readonly isHeartbeating: boolean; // true while a heartbeat chain is in flight +} +``` + +Untrack immediately after `execute()` resolves. Leaving a task tracked keeps +heartbeating a task the server already considers finished. + +### Python SDK parity + +Behavior matches the Python SDK, so cross-SDK deployments behave identically: + +| Behavior | Python | JavaScript | +|---|---|---| +| Heartbeat interval | `responseTimeoutSeconds × 0.8` | Same | +| Minimum interval | `< 1s` → skip | `< 1000ms` → skip | +| Retry count | 3 | Same | +| Retry delay | ~500 ms | Same | +| Heartbeat endpoint | v1 `updateTask` | Same | +| Independent of poll loop | Yes (`run_once()` pre-poll) | Yes (`setInterval`) | +| `leaseExtendEnabled` flag | Yes | Yes | + +## Retries and idempotency + +| Thrown from a worker | Status | Retried? | +|---|---|---| +| `Error` | `FAILED` | Yes, per the task definition | +| `NonRetryableException` | `FAILED_WITH_TERMINAL_ERROR` | No | + +Because a lease can lapse and a task can be re-queued, **worker functions should +be idempotent**. Key side effects on something stable from the task input — an +order id, not a generated timestamp. + +## Workflow-level recovery + +`retry(workflowId)` resumes from the last failed task, preserving completed work. +`restart(workflowId)` starts over and discards it. See +[workflow-lifecycle.md](workflow-lifecycle.md). + +## Agent-layer liveness + +Stateful agent runs get a liveness monitor that fails a blocking `wait()` with +`WorkerStallError` when the domain's worker appears to have died, instead of +hanging forever. See +[agents/concepts/stateful.md](agents/concepts/stateful.md#liveness-monitoring). + +## Next steps + +[workers.md](workers.md) · [observability.md](observability.md) · +[debugging.md](debugging.md) diff --git a/docs/schedules-events.md b/docs/schedules-events.md new file mode 100644 index 00000000..99ecd245 --- /dev/null +++ b/docs/schedules-events.md @@ -0,0 +1,105 @@ +# Schedules and events + +**Audience:** developers triggering workflows on a cron or from an external event. + +## Prerequisites + +A registered workflow ([workflows.md](workflows.md)) and a server with the +scheduler module. + +**Capability:** scheduling requires the scheduler module. On a standalone OSS server +without it, scheduler calls fail rather than silently no-op. + +## Schedules + +```ts +const scheduler = clients.getSchedulerClient(); + +await scheduler.saveSchedule({ + name: "nightly-report", + cronExpression: "0 0 0 * * *", + workflowName: "report_flow", + workflowInput: { scope: "daily" }, +}); +``` + +**Expected result:** the schedule appears in the UI and fires at the next matching +time. + +**The cron format has a seconds field.** `0 0 0 * * *` is midnight. A five-field +expression like `0 0 * * *` means something different from what a crontab-trained +reader expects, and is the single most common mistake here. + +Lifecycle: `getSchedule`, `pauseSchedule`, `resumeSchedule`, `deleteSchedule`, plus +typed helpers `pause`, `resume`, `runNow`, `previewNext`, and +`reconcile(agentName, desired)`. + +`previewNext(cron, { n })` is the cheapest way to confirm an expression means what +you think before you rely on it. + +Pause and resume issue PUT first and fall back to GET on HTTP 405, because +per-schedule verbs differ by Conductor server family. One client works against both +OSS/embedded and Orkes. + +## Agent schedules + +Conductor agents attach schedules **declaratively at deploy time** rather than +through the scheduler client: + +```ts +await runtime.deploy(agent, { + schedules: [new Schedule({ name: "weekday-9am", cron: "0 0 9 * * MON-FRI" })], +}); +``` + +A list upserts those and prunes the rest, `[]` purges all, and omitting the key +leaves them untouched. Lifecycle calls key on the **wire name** from +`ScheduleInfo`, not the short name you supplied. See +[agents/concepts/scheduling.md](agents/concepts/scheduling.md). + +## Events + +Event handlers react to messages on an external queue or an internal Conductor +event, and can start a workflow or complete a task. + +```ts +const events = clients.getEventClient(); + +await events.registerEventHandler({ + name: "order_created_handler", + event: "sqs:order-created", + actions: [{ + action: "start_workflow", + start_workflow: { name: "order_flow", input: { orderId: "${payload.orderId}" } }, + }], + active: true, +}); +``` + +`${payload.…}` is evaluated by the server against the incoming event body. + +**Common failure modes:** + +- A handler registered with `active: false` — it exists and never fires. +- An event sink not configured on the server. The handler is valid; nothing + delivers to it. +- An unresolvable `${payload.…}` path passes through literally rather than erroring. + +## Workflow message queue + +Agents can dequeue messages pushed to their own workflow message queue with +`waitForMessageTool`, backed by Conductor's `PULL_WORKFLOW_MESSAGES`. That is a +pull model inside a running execution, distinct from event handlers, which start +executions. See +[agents/concepts/tools.md](agents/concepts/tools.md#waitformessagetool--workflow-message-queue). + +## Cleanup + +Schedules and event handlers persist until deleted. Pause rather than delete when +you want to keep the definition. + +## Next steps + +[workflow-lifecycle.md](workflow-lifecycle.md) · +[agents/concepts/scheduling.md](agents/concepts/scheduling.md) · +[api-map.md](api-map.md) diff --git a/docs/schema-client.md b/docs/schema-client.md new file mode 100644 index 00000000..fcb7fbd7 --- /dev/null +++ b/docs/schema-client.md @@ -0,0 +1,97 @@ +# Schema client + +**Audience:** developers enforcing input and output schemas on tasks and +workflows. + +## Prerequisites + +A client ([connection-authentication.md](connection-authentication.md)) and a +server that supports the schema API. + +**Capability:** schema enforcement is a server feature. Registering a schema against +a server that doesn't support it fails; check +[compatibility.md](compatibility.md). + +## Registering a schema + +```ts +const schemas = clients.getSchemaClient(); + +await schemas.saveSchema({ + name: "order_input", + version: 1, + type: "JSON", + data: { + type: "object", + properties: { + orderId: { type: "string" }, + amount: { type: "number", minimum: 0 }, + }, + required: ["orderId", "amount"], + }, +}); +``` + +**Expected result:** `getSchema("order_input", 1)` returns the definition, and a +task or workflow referencing it validates input on the server. + +Schemas are **versioned**. Saving the same name with a new version adds a version +rather than replacing one, so existing definitions pinned to version 1 keep +validating against version 1. + +## Attaching to a task definition + +```ts +await clients.getMetadataClient().registerTask({ + name: "process_order", + inputSchema: { name: "order_input", version: 1, type: "JSON" }, + responseTimeoutSeconds: 60, +}); +``` + +The server rejects a task whose input fails validation before your worker ever sees +it — which is the point: bad input fails fast and visibly rather than inside your +handler. + +## Generating schemas from TypeScript + +The worker framework can generate JSON Schema from decorated classes: + +```ts +import { jsonSchema, schemaField } from "@io-orkes/conductor-javascript"; + +@jsonSchema({ name: "order_input" }) +class OrderInput { + @schemaField({ type: "string" }) + orderId!: string; + + @schemaField({ type: "number", minimum: 0 }) + amount!: number; +} +``` + +For agents, `outputType` accepts a Zod schema or a plain JSON Schema object and is +converted for you — see +[agents/concepts/structured-output.md](agents/concepts/structured-output.md). + +## Common failure modes + +- **A schema change appearing to have no effect.** The task definition pins a + version. Bump the version *and* update the definition's reference. +- **Validation not firing.** The task definition has no `inputSchema` reference — + registering the schema alone does nothing. +- **A rejected task with no worker log.** Rejection happens server-side, before + dispatch. Look at the execution, not the worker. + +## Agent configuration contract + +The agent layer has its own contract — the serialized agent config — documented and +validated separately at +[agents/reference/agent-schema.md](agents/reference/agent-schema.md), with a +machine-readable +[agent-schema.json](agents/reference/agent-schema.json) checked in CI. + +## Next steps + +[workflows.md](workflows.md) · [workflow-testing.md](workflow-testing.md) · +[api-map.md](api-map.md) diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 00000000..58c87098 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,98 @@ +# Security + +**Audience:** developers handling credentials, secrets, and untrusted input with +this SDK. + +## Prerequisites + +A configured client ([connection-authentication.md](connection-authentication.md)). + +## Server credentials + +`CONDUCTOR_AUTH_KEY` and `CONDUCTOR_AUTH_SECRET` are minted into a short-lived JWT +and sent as the `X-Authorization` header. The SDK handles minting and refresh; you +only supply the key and secret. + +- Never commit them. Inject from the environment or a secret manager. +- The SDK trims surrounding whitespace, because a trailing newline pasted into a CI + secret is a common cause of "Invalid Access Key". +- Share one client so one token is minted and refreshed, rather than one per + component. +- `CONDUCTOR_TLS_INSECURE=true` disables certificate verification. Local + development only. + +## Task and tool secrets + +The server resolves declared secrets when it polls a task and delivers them +**wire-only** on that task's `runtimeMetadata`. They are never persisted by the +SDK and never fetched separately. For the duration of the call the SDK injects +them into the worker's `process.env` — mutate, invoke, restore — serialized so +concurrent calls don't clobber each other's environment. + +```ts +const dbLookup = tool( + async () => ({ ok: (process.env.DB_API_KEY ?? "") !== "" }), + { + name: "db_lookup", + description: "Look up data.", + inputSchema: { type: "object", properties: {}, required: [] }, + credentials: ["DB_API_KEY"], + }, +); +``` + +**Fail-closed, no fallback.** If a tool declares `credentials: [...]` and the +server didn't deliver one, the task fails non-retryably naming the missing +credential. There is deliberately **no ambient-environment fallback** — the SDK +will not silently read a locally-set variable of the same name instead. That +design choice means a misconfigured deployment fails loudly rather than running +with the wrong identity. + +Servers that predate `TaskDef.runtimeMetadata` support (conductor-oss without +PR #1255, agentspan server > 0.4.2) cannot deliver credentials at all. + +**Scope credentials per tool, not per agent.** Declaring every secret at the agent +level hands each tool the whole set. + +## Untrusted input + +Model output reaches your code as tool arguments. Treat it as untrusted: + +- Validate arguments against `inputSchema`, and re-validate anything you pass to a + shell, query, or filesystem path. +- Use `approvalRequired: true` on tools that delete, pay, send, or escalate + privilege — see + [agents/concepts/streaming-hitl.md](agents/concepts/streaming-hitl.md). +- Remember one HUMAN task gates the **whole batch** of pending tool calls with a + single verdict. Approving one approves all of them; iterate + `pendingTool.toolCalls` before approving. +- Guardrails ([agents/concepts/guardrails.md](agents/concepts/guardrails.md)) are a + mitigation, not a boundary. A regex that blocks a secret pattern reduces + accidental disclosure; it does not make an agent safe to hand untrusted input. + +## Code execution + +`LocalCodeExecutor`, `DockerCodeExecutor`, `JupyterCodeExecutor`, and +`ServerlessCodeExecutor` run model-authored code. `LocalCodeExecutor` runs it in +**your process**, with your privileges — prefer a container or serverless executor +for anything reachable by untrusted input. `CommandValidator` and +`cliAllowedCommands` constrain CLI execution; treat an unconstrained +`cliCommands` as equivalent to shell access. + +## Logging + +Callback hooks and task logs receive prompts, tool arguments, and model output. +Anything you forward to an external sink inherits that sensitivity. Redact before +shipping. `getTaskContext()?.addLog()` writes into the Conductor UI, which is +visible to anyone with access to the execution. + +## LLM provider keys + +Provider API keys belong to the **server** process, not your application. The +agent layer never reads them. + +## Next steps + +[connection-authentication.md](connection-authentication.md) · +[agents/concepts/tools.md](agents/concepts/tools.md#credentials) · +[deployment-scaling.md](deployment-scaling.md) diff --git a/docs/server-setup.md b/docs/server-setup.md new file mode 100644 index 00000000..7d7b2d17 --- /dev/null +++ b/docs/server-setup.md @@ -0,0 +1,73 @@ +# Server setup + +**Audience:** developers who need a Conductor server to develop against. + +## Prerequisites + +Docker, or Node.js >= 18 for the CLI option. Nothing from this SDK is needed to +start a server. + +## Options + +**Docker (recommended — includes the UI):** + +```shell +docker run -p 8080:8080 conductoross/conductor:latest +``` + +**macOS / Linux one-liner:** + +```shell +curl -sSL https://raw.githubusercontent.com/conductor-oss/conductor/main/conductor_server.sh | sh +``` + +**Conductor CLI:** + +```shell +npm install -g @conductor-oss/conductor-cli +conductor server start +``` + +**Expected result:** the UI at `http://localhost:8080` and the API at +`http://localhost:8080/api`. + +**Common failure mode:** pointing the SDK at the UI URL rather than the API URL. +`CONDUCTOR_SERVER_URL` may be given with or without the `/api` suffix — the SDK +normalizes it — but a URL pointing at a different port will simply fail to +connect. + +## OSS vs Orkes + +| | conductor-oss | Orkes Conductor | +|---|---|---| +| Auth | Usually none locally | Key/secret, minted to a JWT | +| Agent runtime | `>= 3.32.0-rc.8` | Requires `agentspan.embedded=true` | +| Scheduler module | Optional | Included | +| Secret store | Can be env-backed and read-only | Writable | + +Pages call out Orkes-only capabilities where behavior differs. See +[compatibility.md](compatibility.md) for the version matrix. + +**Agent runtime:** running Conductor agents needs conductor-oss +`>= 3.32.0-rc.8`, or orkes-conductor booted with the `agentspan.embedded=true` +boot property. That property is owned by the server, not this SDK. + +## Local integration testing + +The repository ships a Docker Compose stack used by CI: + +```shell +docker compose -f scripts/docker-compose-oss.yaml up -d +./scripts/run-integration-oss.sh +``` + +**Cleanup:** + +```shell +docker compose -f scripts/docker-compose-oss.yaml down -v +``` + +## Next steps + +[connection-authentication.md](connection-authentication.md) · +[core-quickstart.md](core-quickstart.md) diff --git a/docs/upgrading.md b/docs/upgrading.md new file mode 100644 index 00000000..4472ab26 --- /dev/null +++ b/docs/upgrading.md @@ -0,0 +1,114 @@ +# Upgrading + +**Audience:** developers moving an existing project to a newer SDK version. + +## Prerequisites + +None. `../BREAKING_CHANGES.md` carries the per-change impact tables; this page is +the migration narrative. + +## Agentspan → Conductor + +The agent layer was merged in from the Agentspan TypeScript SDK, which has since +been rebranded to Conductor. Names changed to match; the old ones still work. + +### Environment variables + +Every `AGENTSPAN_` is now `CONDUCTOR_AGENT_`: + +```shell +# Before +export AGENTSPAN_SERVER_URL=http://localhost:8080/api +export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini + +# After +export CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini +``` + +Affected: `SERVER_URL`, `AUTH_KEY`, `AUTH_SECRET`, `LLM_MODEL`, `LOG_LEVEL`, +`CLI_PATH`, `WORKER_POLL_INTERVAL`, `WORKER_THREADS`, `AUTO_START_WORKERS`, +`STREAMING_ENABLED`, and the three `LIVENESS_*` knobs. + +**The old names still resolve** as deprecated fallbacks, warning once per name per +process the first time each supplies a value. Setting both is safe — +`CONDUCTOR_AGENT_*` wins. `AGENTSPAN_LOG_LEVEL` falls back silently, because warning +there would mean logging through the logger whose level is still being resolved. + +Precedence (spec R3), unchanged except for the new tier: + +``` +CONDUCTOR_* env → explicit config → CONDUCTOR_AGENT_* env + → AGENTSPAN_* env (deprecated) → http://localhost:8080 +``` + +### Error class + +`AgentspanError` is now `ConductorAgentError`. + +```ts +// Both work; prefer the first +import { ConductorAgentError } from "@io-orkes/conductor-javascript/agents"; +import { AgentspanError } from "@io-orkes/conductor-javascript/agents"; // @deprecated +``` + +The alias is the **same class object**, not a subclass, so `instanceof` works in +both directions and existing `catch` blocks keep working. Both the value and the +type are exported, so `const e: AgentspanError` still compiles. + +**One behavior change:** `error.name` on a base-class instance now reports +`"ConductorAgentError"`. If you assert on that string, update the assertion. + +`AgentspanMetadata` (from `/agents/langchain` and `/agents/langgraph`) is likewise +aliased to `ConductorAgentMetadata`. + +### What did not change + +Names owned by something other than this SDK were deliberately left alone. +Renaming them would point at flags that don't exist or break interop: + +| Name | Owner | +|---|---| +| `__agentspan_ctx__`, `__agentspan_sdk__.cjs`, `_agentspan.llm`/`.model`/`.framework`/`.tools`, `_agentspan_human_task`, `_agentspan_human_prompt` | Cross-SDK wire keys, shared with the Python SDK and compiled by the same server path. | +| `agentspan.embedded`, `agentspan.default-context-window` | orkes-conductor server boot properties. | +| `agentspan-ai/agentspan`, `agentspan-ai/codingexamples`, `@agentspan-ai`, `agentspan-server` | External repos, npm scope, container image. | +| The `agentspan` CLI binary, `~/.agentspan/config.json`, `agentspan_linux_amd64`, `agentspan deploy`/`credentials`/`login`/`import` | The external CLI's release asset and command surface. | +| `agentspan <= 0.4.2`, `agentspan server > 0.4.2` | Version-qualified references to the upstream product — renaming makes them false. | + +Every cross-SDK wire key is underscore-prefixed. That invariant is the quickest way +to tell a wire contract from prose. + +If you set `_agentspan` metadata by hand on a LangGraph graph, keep the key as-is. +Prefer the SDK's `createReactAgent` wrapper, which sets it for you — see +[agents/frameworks/langgraph.md](agents/frameworks/langgraph.md). + +### E2E bundle consumers + +The released e2e bundle documents `CONDUCTOR_AGENT_*` as its interface. The +`AGENTSPAN_*` spelling still resolves inside the harness, so existing downstream +runs keep working while you migrate. + +## v3.x — worker architecture + +`TaskHandler.startWorkers()` became `async`, because it now registers task +definitions before polling starts. + +```ts +await handler.startWorkers(); +``` + +Fire-and-forget still works; the only hard break is an explicit +`const r: void = handler.startWorkers()` annotation. Awaiting it means task-def +registration completes before your first execution rather than racing it. + +## Documentation layout + +Documentation moved to the canonical structure shared with the Java and Python SDKs: +`docs/*.md` plus `docs/agents/{concepts,frameworks,reference}/`. The previous +`docs/agents/*.md` pages and `docs/api-reference/*.md` remain as redirect stubs, so +existing links keep resolving. Start at [README.md](README.md). + +## Next steps + +[../BREAKING_CHANGES.md](../BREAKING_CHANGES.md) · +[compatibility.md](compatibility.md) · [README.md](README.md) diff --git a/docs/workers.md b/docs/workers.md new file mode 100644 index 00000000..ab42b27f --- /dev/null +++ b/docs/workers.md @@ -0,0 +1,183 @@ +# Workers + +**Audience:** developers writing the code that executes Conductor tasks. + +## Prerequisites + +A client and a registered workflow ([core-quickstart.md](core-quickstart.md)). +Workers are TypeScript functions decorated with `@worker` and discovered by +`TaskHandler`. + +## Decorator styles + +The SDK supports both. Pick one per project. + +**New (TypeScript 5.0+)** — class methods, Stage 3 decorators, no compiler flag: + +```ts +import { worker, TaskHandler } from "@io-orkes/conductor-javascript"; +import type { Task } from "@io-orkes/conductor-javascript"; + +class Workers { + @worker({ taskDefName: "greet", concurrency: 5, pollInterval: 100 }) + async greet(task: Task) { + return { + status: "COMPLETED" as const, + outputData: { result: `Hello ${task.inputData?.name ?? "World"}` }, + }; + } +} + +// Instantiating triggers the decorators — workers register here +void new Workers(); + +const handler = new TaskHandler({ client, scanForDecorated: true }); +await handler.startWorkers(); +``` + +**Legacy** — standalone functions, needs `"experimentalDecorators": true`: + +```ts +@worker({ taskDefName: "greet", concurrency: 5 }) +async function greet(task: Task) { + return { status: "COMPLETED" as const, outputData: { result: "Hello" } }; +} +``` + +| Style | tsconfig.json | +|---|---| +| New (TS 5.0+) | Omit `experimentalDecorators`; use class methods | +| Legacy | `"experimentalDecorators": true`; use standalone functions | + +**Common failure mode with the new style:** forgetting `void new Workers()`. +Decorators on class methods run when the class is *instantiated*, so without it +nothing registers and every task sits in `SCHEDULED`. + +## Configuration + +```ts +@worker({ + taskDefName: "my_task", // required + concurrency: 5, // max concurrent tasks (default 1) + pollInterval: 100, // ms (default 100) + domain: "production", // task domain for multi-tenancy + workerId: "worker-123", +}) +``` + +Environment overrides need no code change: + +```shell +# Global +export CONDUCTOR_WORKER_ALL_POLL_INTERVAL=500 +export CONDUCTOR_WORKER_ALL_CONCURRENCY=10 + +# Per-worker (the task name, upper-cased) +export CONDUCTOR_WORKER_SEND_EMAIL_CONCURRENCY=20 +export CONDUCTOR_WORKER_PROCESS_PAYMENT_DOMAIN=payments +``` + +## Failure semantics + +```ts +import { NonRetryableException } from "@io-orkes/conductor-javascript"; + +@worker({ taskDefName: "validate_order" }) +async function validateOrder(task: Task) { + const order = await getOrder(task.inputData.orderId); + if (!order) { + throw new NonRetryableException("Order not found"); + } + return { status: "COMPLETED", outputData: { validated: true } }; +} +``` + +| Thrown | Task status | Retried? | +|---|---|---| +| `Error` | `FAILED` | Yes, per the task definition's retry policy | +| `NonRetryableException` | `FAILED_WITH_TERMINAL_ERROR` | No | + +Use `NonRetryableException` for anything a retry cannot fix — bad input, a missing +record, a validation failure. Retrying those burns the retry budget and delays the +real failure. + +## Long-running tasks + +Return `IN_PROGRESS` with a callback interval to keep a task alive while something +external finishes: + +```ts +import { worker, getTaskContext } from "@io-orkes/conductor-javascript"; + +@worker({ taskDefName: "process_video" }) +async function processVideo(task: Task) { + const ctx = getTaskContext(); + ctx?.addLog("Starting video processing..."); + + if (!isComplete(task.inputData)) { + ctx?.setCallbackAfter(30); + return { status: "IN_PROGRESS", callbackAfterSeconds: 30 }; + } + + return { status: "COMPLETED", outputData: { url: "..." } }; +} +``` + +`getTaskContext()` is backed by `AsyncLocalStorage`, so it works without threading +a parameter through. `ctx?.addLog()` streams logs into the Conductor UI. + +For tasks that hold a lease while working, see +[reliability.md](reliability.md#lease-extension). + +## Lifecycle + +`startWorkers()` is **`async`** — it registers task definitions before polling +starts. Await it, or task-def registration races your first execution. + +```ts +process.on("SIGTERM", async () => { + await handler.stopWorkers(); + process.exit(0); +}); +``` + +## Organizing across files + +```ts +const handler = await TaskHandler.create({ + client, + importModules: ["./workers/orderWorkers", "./workers/paymentWorkers"], +}); +await handler.startWorkers(); +``` + +## Observability + +```ts +const handler = new TaskHandler({ + client, + scanForDecorated: true, + eventListeners: [{ + onTaskExecutionCompleted(event) { + metrics.histogram("task_duration_ms", event.durationMs, { task_type: event.taskType }); + }, + onTaskUpdateFailure(event) { + alertOps({ severity: "CRITICAL", taskId: event.taskId }); + }, + }], +}); +``` + +`TaskHandler` monitors and restarts worker polling loops by default. Expose +`handler.running` and `handler.runningWorkerCount` as a health check. See +[observability.md](observability.md). + +## Legacy API + +`TaskManager` still works with full backward compatibility. New projects should +use `@worker` plus `TaskHandler`. + +## Next steps + +[reliability.md](reliability.md) · [deployment-scaling.md](deployment-scaling.md) · +[observability.md](observability.md) diff --git a/docs/workflow-lifecycle.md b/docs/workflow-lifecycle.md new file mode 100644 index 00000000..b4b428ed --- /dev/null +++ b/docs/workflow-lifecycle.md @@ -0,0 +1,87 @@ +# Workflow lifecycle + +**Audience:** developers starting, inspecting, and controlling workflow +executions. + +## Prerequisites + +A registered workflow ([workflows.md](workflows.md)) and a workflow client. + +```ts +const executor = clients.getWorkflowClient(); +``` + +## Starting + +```ts +// Async — returns the execution id immediately +const workflowId = await executor.startWorkflow({ + name: "order_flow", + input: { orderId: "ORDER-123" }, +}); + +// Sync — waits for completion +const result = await workflow.execute({ orderId: "123" }); +``` + +**Expected result:** `startWorkflow` gives you an id to poll or correlate; +`execute` gives you the finished run. + +Use `startWorkflow` for anything that might outlive the calling process — a sync +`execute` ties completion to your process staying alive. + +## Controlling + +```ts +await executor.pause(workflowId); +await executor.resume(workflowId); +await executor.terminate(workflowId, "cancelled by user"); +await executor.restart(workflowId); +await executor.retry(workflowId); +``` + +| Operation | Effect | +|---|---| +| `pause` | Stops scheduling new tasks. In-flight tasks finish. | +| `resume` | Resumes scheduling. | +| `terminate` | Ends the execution as `TERMINATED`. Not resumable. | +| `restart` | Starts over from the beginning, same input. | +| `retry` | Retries from the last failed task, keeping completed work. | + +`retry` is what you usually want after a transient failure; `restart` discards +completed work. + +## Signalling a WAIT task + +```ts +await executor.signal(workflowId, TaskResultStatusEnum.COMPLETED, { approved: true }); +``` + +## Reading + +```ts +const results = await executor.search("workflowType = 'order_flow' AND status = 'RUNNING'"); +``` + +`getWorkflow()` and `getExecution()` are **not** interchangeable — they hit +different endpoints and return different shapes. Reach for `getExecution()` when +you need full task-level detail. + +## Common failure modes + +- **`terminate` on an already-terminal execution** fails rather than no-opping. + Check status first if that path is reachable. +- **`retry` with no failed task** has nothing to retry. +- **A search returning nothing** — search is index-backed and eventually + consistent; a just-started execution may not appear immediately. + +## Cleanup + +Terminated and completed executions are retained per your server's retention +policy. Terminate long-running test executions rather than leaving them to time +out. + +## Next steps + +[workers.md](workers.md) · [debugging.md](debugging.md) · +[reliability.md](reliability.md) diff --git a/docs/workflow-testing.md b/docs/workflow-testing.md new file mode 100644 index 00000000..c2658ccf --- /dev/null +++ b/docs/workflow-testing.md @@ -0,0 +1,103 @@ +# Workflow testing + +**Audience:** developers testing workflows, workers, and agents. + +## Prerequisites + +The repository uses Jest. Unit tests need no server; integration tests need a +running one ([server-setup.md](server-setup.md)). + +## Test tiers + +| Tier | Command | Needs a server | +|---|---|---| +| Unit | `npm run test:unit` | No | +| Integration (v5 sdkdev) | `npm run test:integration:v5` | Yes | +| Integration (v4 sm) | `npm run test:integration:v4` | Yes | +| Integration (OSS) | `npm run test:integration:oss` | Yes — local Docker stack | +| Agent e2e | `npm run test:agent-e2e` | Yes — plus LLM keys on the server | + +CI runs unit tests across Node 20, 22, and 24, and shards the integration suites. + +## Unit-testing a worker + +A worker is a plain function. Test it directly — no server, no polling: + +```ts +import { greet } from "../src/workers/greet"; + +it("greets by name", async () => { + const result = await greet({ inputData: { name: "Ada" } } as never); + expect(result.outputData?.result).toBe("Hello Ada"); +}); +``` + +**Expected result:** a fast test with no I/O. This is the highest-value tier — +worker logic is where most bugs live, and none of it needs Conductor. + +## Testing a workflow definition + +`runtime.plan(agent)` for agents, or building the workflow and inspecting it, +lets you assert on the compiled definition without executing: + +```ts +const definition = await runtime.plan(agent); +expect(definition.tasks).toHaveLength(3); +``` + +This catches "the definition changed shape" regressions cheaply, and is the right +place to pin behavior you care about that would otherwise need a live run. + +## Agent testing toolkit + +The `/agents/testing` subpath provides assertion and mocking helpers so agent tests +don't require a live model: + +```ts +import { mockAgent, expectAgent } from "@io-orkes/conductor-javascript/agents/testing"; +``` + +## Integration tests + +Local OSS stack, the same one CI uses: + +```shell +docker compose -f scripts/docker-compose-oss.yaml up -d +./scripts/run-integration-oss.sh +``` + +Orkes-only tests are gated out via `CONDUCTOR_SERVER_TYPE=oss`, so the OSS run +skips capabilities a standalone server lacks rather than failing. + +**Cleanup:** + +```shell +docker compose -f scripts/docker-compose-oss.yaml down -v +``` + +## Common failure modes + +- **Integration test hangs.** Workers weren't started before the workflow executed, + or a `taskDefName` doesn't match. See [debugging.md](debugging.md). +- **Test passes alone, fails in a suite.** Shared server state. Integration tests + run with `--runInBand` for this reason; give each test unique workflow and task + names. +- **A test leaves the process open.** `stopWorkers()` / `runtime.shutdown()` in + `afterEach`. Jest is run with `--force-exit` here to contain it, which hides the + leak rather than fixing it. +- **Dynamic `import()` in a test.** It breaks under Jest in this repo; use static + imports. + +## Doc validation + +Documentation is validated in CI: internal Markdown links, retired references, and +the agent configuration schema. + +```shell +npm run verify:agent-schema +``` + +## Next steps + +[debugging.md](debugging.md) · [workers.md](workers.md) · +[compatibility.md](compatibility.md) diff --git a/docs/workflows.md b/docs/workflows.md new file mode 100644 index 00000000..07b52113 --- /dev/null +++ b/docs/workflows.md @@ -0,0 +1,127 @@ +# Workflows + +**Audience:** developers authoring workflow definitions with the +`ConductorWorkflow` DSL and task builders. + +## Prerequisites + +A client ([connection-authentication.md](connection-authentication.md)) and a +first workflow working ([core-quickstart.md](core-quickstart.md)). + +## The builder + +```ts +import { ConductorWorkflow, simpleTask } from "@io-orkes/conductor-javascript"; + +const workflow = new ConductorWorkflow(executor, "order_flow") + .add(simpleTask("validate_ref", "validate_order", { orderId: "${workflow.input.orderId}" })) + .add(simpleTask("charge_ref", "charge_card", { orderId: "${workflow.input.orderId}" })) + .outputParameters({ status: "${charge_ref.output.status}" }); + +await workflow.register(); +``` + +`${...}` expressions are evaluated by the **server**, not by TypeScript. They +reference workflow input (`${workflow.input.x}`) or a prior task's output by its +reference name (`${validate_ref.output.y}`). + +There is also a simpler factory for flat workflows: + +```ts +import { workflow } from "@io-orkes/conductor-javascript"; + +const wf = workflow({ name: "simple", tasks: [/* ... */] }); +``` + +**Expected result:** `register()` resolves and the definition appears in the UI. + +## Task builder argument order + +Task builders take the **task reference name first**, then the task definition +name: + +```ts +simpleTask("greet_ref", "greet", { name: "..." }); +// ^ref ^taskDefName +``` + +This is the opposite of what most people expect, and getting it backwards +produces a workflow that registers fine and then hangs, because no worker polls +for a task type named `greet_ref`. If a task sits in `SCHEDULED`, check this +first. + +## Control flow + +**HTTP calls without a worker:** + +```ts +httpTask("call_api", { + uri: "https://api.example.com/orders/${workflow.input.orderId}", + method: "POST", + body: { items: "${workflow.input.items}" }, + headers: { Authorization: "Bearer ${workflow.input.token}" }, +}); +``` + +**Wait:** + +```ts +.add(simpleTask("step1_ref", "process_order", {})) +.add(waitTaskDuration("cool_down", "10s")) +.add(simpleTask("step2_ref", "send_confirmation", {})) +``` + +**Fork/join:** + +```ts +workflow.fork([ + [simpleTask("email_ref", "send_email", {})], + [simpleTask("sms_ref", "send_sms", {})], + [simpleTask("push_ref", "send_push", {})], +]); +``` + +**Conditional branching:** + +```ts +switchTask("route_ref", "${workflow.input.tier}", { + premium: [simpleTask("fast_ref", "fast_track", {})], + standard: [simpleTask("normal_ref", "standard_process", {})], +}); +``` + +Builders live under `src/sdk/builders/tasks/`, including 13 LLM task builders +under `tasks/llm/`. + +## Registration + +`register()` defaults to **`overwrite=true`**. Re-registering replaces the +definition in place rather than erroring or creating a version — convenient in +development, and a foot-gun if two environments share a server, since the last +writer wins. + +```ts +await workflow.register(); // overwrite +await workflow.register(false); // fail if it already exists +``` + +## Task definitions + +`taskDefinition({ name, ... })` creates a task definition explicitly. Workers +registered through `TaskHandler` with `registerTaskDef: true` register theirs +automatically on `startWorkers()`. + +## Common failure modes + +- **Task stuck in `SCHEDULED`** — see the argument-order note above, or no worker + is running. +- **`${...}` appearing literally in output** — the expression referenced a ref or + field that doesn't exist. The server leaves unresolvable expressions as-is + rather than erroring. +- **A definition change not taking effect** — a running execution uses the + definition it started with; re-register and start a new execution. + +## Next steps + +[workflow-lifecycle.md](workflow-lifecycle.md) · [workers.md](workers.md) · +[workflow-testing.md](workflow-testing.md) diff --git a/e2e/helpers.ts b/e2e/helpers.ts index c4a48857..cddc2283 100644 --- a/e2e/helpers.ts +++ b/e2e/helpers.ts @@ -37,10 +37,41 @@ export function expectMsg(actual: unknown, message?: string): ReturnType; } -const SERVER_URL = process.env.AGENTSPAN_SERVER_URL ?? 'http://localhost:8080/api'; +/** + * Read a `CONDUCTOR_AGENT_*` var, falling back to its deprecated `AGENTSPAN_*` + * spelling and warning once. + * + * This suite ships as a standalone release bundle consumed by downstream repos + * (see scripts/package-e2e-bundle.sh), so the harness needs its own fallback: + * the SDK-level alias in resolveOrkesConfig only covers vars the SDK reads, not + * the ones this file reads directly. Without it, every downstream consumer + * already exporting AGENTSPAN_SERVER_URL would silently fall through to + * localhost. + */ +const warnedLegacyEnv = new Set(); +function agentEnv(suffix: string): string | undefined { + const canonical = `CONDUCTOR_AGENT_${suffix}`; + const legacy = `AGENTSPAN_${suffix}`; + + const current = process.env[canonical]; + if (current !== undefined && current !== '') return current; + + const legacyValue = process.env[legacy]; + if (legacyValue === undefined || legacyValue === '') return undefined; + + if (!warnedLegacyEnv.has(legacy)) { + warnedLegacyEnv.add(legacy); + console.warn( + `[conductor] ${legacy} is deprecated and will be removed in a future release. Use ${canonical} instead.`, + ); + } + return legacyValue; +} + +const SERVER_URL = agentEnv('SERVER_URL') ?? 'http://localhost:8080/api'; const BASE_URL = SERVER_URL.replace(/\/api$/, ''); -export const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o-mini'; -export const CLI_PATH = process.env.AGENTSPAN_CLI_PATH ?? 'agentspan'; +export const MODEL = agentEnv('LLM_MODEL') ?? 'openai/gpt-4o-mini'; +export const CLI_PATH = agentEnv('CLI_PATH') ?? 'agentspan'; export const MCP_TESTKIT_URL = process.env.MCP_TESTKIT_URL ?? 'http://localhost:3001'; export const TIMEOUT = 300_000; // 5 min per run — CI runners are slower @@ -92,7 +123,7 @@ export function runDiagnostic(result: Record): string { // ── Credential helper ──────────────────────────────────────────────────── // Writes directly to the server's secret store (PUT/DELETE /api/secrets/{name}) — -// the same store the agentspan CLI targets, and what tools resolve at runtime. +// the same store the Conductor CLI targets, and what tools resolve at runtime. // Using the API keeps these tests deterministic regardless of the local CLI's // ambient config (~/.agentspan/config.json may point at a different/managed server). diff --git a/e2e/test_suite11_langgraph.test.ts b/e2e/test_suite11_langgraph.test.ts index 96537968..eb16262b 100644 --- a/e2e/test_suite11_langgraph.test.ts +++ b/e2e/test_suite11_langgraph.test.ts @@ -1,7 +1,7 @@ /** * Suite 11: LangGraph Cross-SDK Parity Tests — serialization and compilation. * - * Tests that LangGraph graphs serialize correctly into Agentspan workflows: + * Tests that LangGraph graphs serialize correctly into Conductor workflows: * - Full extraction: createReactAgent → model + tools in rawConfig * - Graph-structure: StateGraph → nodes + edges in rawConfig._graph * - Tool extraction: tools have correct names, descriptions, schemas @@ -40,7 +40,7 @@ let z: any; let serializeLangGraph: any; let detectFramework: any; -const SERVER_URL = process.env.AGENTSPAN_SERVER_URL ?? 'http://localhost:8080/api'; +const SERVER_URL = process.env.CONDUCTOR_AGENT_SERVER_URL ?? 'http://localhost:8080/api'; const BASE_URL = SERVER_URL.replace(/\/api$/, ''); try { diff --git a/e2e/test_suite15_behavioral_correctness.test.ts b/e2e/test_suite15_behavioral_correctness.test.ts index 36b65e08..1eae42d9 100644 --- a/e2e/test_suite15_behavioral_correctness.test.ts +++ b/e2e/test_suite15_behavioral_correctness.test.ts @@ -573,7 +573,7 @@ describe('Suite 15: Behavioral Correctness', () => { // Same shape as test_three_analysts_all_contribute: the real subject // is the router strategy + tool dispatch; the LLM drives the route + // tool call. gpt-4o-mini sometimes routes elsewhere on first try. - // Retries cope with upstream provider variability, not Agentspan bugs. + // Retries cope with upstream provider variability, not Conductor bugs. it('test_order_routed_and_looked_up', async () => { const desk = makeServiceDesk(); const result = await runtime.run( diff --git a/e2e/test_suite15_skills.test.ts b/e2e/test_suite15_skills.test.ts index ec5d4c81..9eddb557 100644 --- a/e2e/test_suite15_skills.test.ts +++ b/e2e/test_suite15_skills.test.ts @@ -42,7 +42,7 @@ beforeAll(async () => { runtime = new AgentRuntime(); // Create a temp skill directory - skillDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agentspan-skill-test-')); + skillDir = fs.mkdtempSync(path.join(os.tmpdir(), 'conductor-skill-test-')); fs.writeFileSync( path.join(skillDir, 'SKILL.md'), diff --git a/e2e/test_suite1_basic_validation.test.ts b/e2e/test_suite1_basic_validation.test.ts index e90a5d5a..4f5afbaa 100644 --- a/e2e/test_suite1_basic_validation.test.ts +++ b/e2e/test_suite1_basic_validation.test.ts @@ -296,7 +296,7 @@ function makeKitchenSinkAgent() { // ── LLM Judge ─────────────────────────────────────────────────────────── -const JUDGE_MODEL = process.env.AGENTSPAN_JUDGE_MODEL ?? 'claude-sonnet-4-20250514'; +const JUDGE_MODEL = process.env.CONDUCTOR_AGENT_JUDGE_MODEL ?? 'claude-sonnet-4-20250514'; const JUDGE_SYSTEM_PROMPT = `You are a strict validation judge for a workflow compilation system. diff --git a/e2e/test_suite20_plan_execute.test.ts b/e2e/test_suite20_plan_execute.test.ts index 78bd57ff..c597a799 100644 --- a/e2e/test_suite20_plan_execute.test.ts +++ b/e2e/test_suite20_plan_execute.test.ts @@ -600,7 +600,7 @@ describe('Suite 20: Plan-Execute Refs (deterministic)', () => { } async function fetchStepOutputs(executionId: string): Promise> { - const base = (process.env.AGENTSPAN_SERVER_URL ?? 'http://localhost:8080/api') + const base = (process.env.CONDUCTOR_AGENT_SERVER_URL ?? 'http://localhost:8080/api') .replace(/\/api$/, '') .replace(/\/$/, ''); const parent = (await (await fetch(`${base}/api/workflow/${executionId}?includeTasks=true`)).json()) as { diff --git a/e2e/test_suite21_scheduling.test.ts b/e2e/test_suite21_scheduling.test.ts index a69554a4..06995e40 100644 --- a/e2e/test_suite21_scheduling.test.ts +++ b/e2e/test_suite21_scheduling.test.ts @@ -2,7 +2,7 @@ * Suite 21: Agent scheduling (TypeScript SDK). * * Mirrors `sdk/python/e2e/test_suite21_scheduling.py` end-to-end against a - * live agentspan-runtime with the scheduler module enabled. Skipped if the + * live Conductor-runtime with the scheduler module enabled. Skipped if the * `/scheduler/schedules` endpoint isn't reachable. */ @@ -17,7 +17,7 @@ import { } from '@io-orkes/conductor-javascript/agents'; import { expectMsg } from './helpers'; -const SERVER_URL = process.env.AGENTSPAN_SERVER_URL ?? 'http://localhost:8080/api'; +const SERVER_URL = process.env.CONDUCTOR_AGENT_SERVER_URL ?? 'http://localhost:8080/api'; async function schedulerAvailable(): Promise { try { @@ -51,7 +51,7 @@ describe('Suite 21: scheduling', () => { name: agentName, version: 1, description: 'TS scheduling e2e no-op', - ownerEmail: 'e2e@agentspan.test', + ownerEmail: 'e2e@conductor.test', schemaVersion: 2, timeoutSeconds: 60, timeoutPolicy: 'TIME_OUT_WF', diff --git a/e2e/test_suite23_agent_client.test.ts b/e2e/test_suite23_agent_client.test.ts index 445e8a50..d4974631 100644 --- a/e2e/test_suite23_agent_client.test.ts +++ b/e2e/test_suite23_agent_client.test.ts @@ -26,7 +26,7 @@ describe('Suite 23: AgentClient / WorkflowClient', () => { // hard when the server is down (the CI workflow health-gates before tests). beforeAll(async () => { if (!(await checkServerHealth())) { - throw new Error('agentspan server is not healthy — these e2e suites need a running server'); + throw new Error('Conductor server is not healthy — these e2e suites need a running server'); } }); const client = new OrkesAgentClient(); diff --git a/e2e/test_suite6_pdf_tools.test.ts b/e2e/test_suite6_pdf_tools.test.ts index c6f744ef..a832c5a3 100644 --- a/e2e/test_suite6_pdf_tools.test.ts +++ b/e2e/test_suite6_pdf_tools.test.ts @@ -32,7 +32,7 @@ afterAll(() => runtime.shutdown()); // ── Sample markdown ───────────────────────────────────────────────────── -const SAMPLE_MARKDOWN = `# Agentspan E2E Test Report +const SAMPLE_MARKDOWN = `# Conductor E2E Test Report ## Overview diff --git a/e2e/tools/_worker-harness.ts b/e2e/tools/_worker-harness.ts index 688341ac..5a1a93fd 100644 --- a/e2e/tools/_worker-harness.ts +++ b/e2e/tools/_worker-harness.ts @@ -24,9 +24,9 @@ if (!examplePath) { let captured: [Record, any[]] | null = null; -// Duck-type check: is this an Agentspan native Agent? +// Duck-type check: is this a Conductor native Agent? // Check for properties unique to Agent class (name + tools array + agents array + maxTurns number) -function isAgentspanAgent(obj: any): boolean { +function isConductorAgent(obj: any): boolean { return ( obj != null && typeof obj === "object" && @@ -37,7 +37,7 @@ function isAgentspanAgent(obj: any): boolean { ); } -// Helper: serialize a native Agentspan Agent into [rawConfig, workers] +// Helper: serialize a native Conductor Agent into [rawConfig, workers] function serializeNativeAgent(agent: any): [Record, any[]] { const serializer = new AgentConfigSerializer(); const rawConfig = serializer.serializeAgent(agent); @@ -77,7 +77,7 @@ function tryCaptureAgent(agent: any) { } catch { /* ignore serialization failure */ } - } else if (isAgentspanAgent(agent)) { + } else if (isConductorAgent(agent)) { try { captured = serializeNativeAgent(agent); } catch { @@ -154,7 +154,7 @@ console.log = () => {}; console.warn = () => {}; // Set env vars so AgentRuntime constructor doesn't fail -process.env.AGENTSPAN_SERVER_URL ??= "http://localhost:8080/api"; +process.env.CONDUCTOR_AGENT_SERVER_URL ??= "http://localhost:8080/api"; process.env.OPENAI_API_KEY ??= "sk-fake"; process.env.ANTHROPIC_API_KEY ??= "sk-fake"; process.env.GOOGLE_API_KEY ??= "fake"; diff --git a/eslint.config.mjs b/eslint.config.mjs index 3c7e5de4..89826d39 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -28,7 +28,7 @@ export default defineConfig( }, }, { - // src/agents is the Agentspan agent SDK merged in-tree. Its framework + // src/agents is the Conductor agent SDK merged in-tree. Its framework // serializers walk arbitrary langgraph/langchain object graphs, so it keeps // the upstream lint contract for these two rules (upstream pins both to // "warn" in its eslint config); everything else lints at this repo's level. diff --git a/examples/README.md b/examples/README.md index ef1c0cf0..dc22e57f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -48,8 +48,8 @@ npx ts-node examples/workers-e2e.ts Agentic examples live in [`agents/`](agents/) and use the durable agents layer (`@io-orkes/conductor-javascript/agents`). See [agents/README.md](agents/README.md) -for the full catalog and environment setup (`AGENTSPAN_SERVER_URL`, -`AGENTSPAN_LLM_MODEL`, provider API key). Highlights: +for the full catalog and environment setup (`CONDUCTOR_AGENT_SERVER_URL`, +`CONDUCTOR_AGENT_LLM_MODEL`, provider API key). Highlights: | File | Description | Run | |------|-------------|-----| diff --git a/examples/agents/01-basic-agent.ts b/examples/agents/01-basic-agent.ts index d3b6d555..0af31637 100644 --- a/examples/agents/01-basic-agent.ts +++ b/examples/agents/01-basic-agent.ts @@ -5,9 +5,9 @@ * `runtime.run()`, and print the result. * * Requirements: - * - Agentspan server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL set as environment variable (optional) + * - Conductor server with LLM support + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL set as environment variable (optional) */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/02-tools.ts b/examples/agents/02-tools.ts index cdee5e0d..77d95527 100644 --- a/examples/agents/02-tools.ts +++ b/examples/agents/02-tools.ts @@ -8,8 +8,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import * as readline from 'node:readline/promises'; diff --git a/examples/agents/02a-simple-tools.ts b/examples/agents/02a-simple-tools.ts index 5d10b1ea..071f0b3f 100644 --- a/examples/agents/02a-simple-tools.ts +++ b/examples/agents/02a-simple-tools.ts @@ -9,8 +9,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/02b-multi-step-tools.ts b/examples/agents/02b-multi-step-tools.ts index fbfdf807..06fdffc6 100644 --- a/examples/agents/02b-multi-step-tools.ts +++ b/examples/agents/02b-multi-step-tools.ts @@ -16,8 +16,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/03-multi-agent.ts b/examples/agents/03-multi-agent.ts index 03bead06..9f1c819e 100644 --- a/examples/agents/03-multi-agent.ts +++ b/examples/agents/03-multi-agent.ts @@ -13,7 +13,7 @@ import { OnTextMention, } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // ── Sequential: writer -> editor ───────────────────────── diff --git a/examples/agents/03-structured-output.ts b/examples/agents/03-structured-output.ts index eb5ec025..9e01e571 100644 --- a/examples/agents/03-structured-output.ts +++ b/examples/agents/03-structured-output.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/04-guardrails.ts b/examples/agents/04-guardrails.ts index cafbb7b2..566239b6 100644 --- a/examples/agents/04-guardrails.ts +++ b/examples/agents/04-guardrails.ts @@ -16,7 +16,7 @@ import { } from '@io-orkes/conductor-javascript/agents'; import type { GuardrailResult } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // -- RegexGuardrail: block PII patterns -- const piiBlocker = new RegexGuardrail({ diff --git a/examples/agents/04-http-and-mcp-tools.ts b/examples/agents/04-http-and-mcp-tools.ts index 5ee158d9..50803638 100644 --- a/examples/agents/04-http-and-mcp-tools.ts +++ b/examples/agents/04-http-and-mcp-tools.ts @@ -17,15 +17,15 @@ * # Or start with auth (requires storing the secret as a credential): * mcp-testkit --transport http --auth * - * # Store credentials via CLI or Agentspan UI: + * # Store credentials via CLI or Conductor UI: * agentspan credentials set HTTP_TEST_API_KEY * agentspan credentials set MCP_TEST_API_KEY * * Requirements: * - Conductor server with LLM support * - mcp-testkit running on http://localhost:3001 (see setup above) - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool, httpTool, mcpTool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/04-mcp-weather.ts b/examples/agents/04-mcp-weather.ts index 17650bb5..9d8aedab 100644 --- a/examples/agents/04-mcp-weather.ts +++ b/examples/agents/04-mcp-weather.ts @@ -18,14 +18,14 @@ * # Or start with auth (requires storing the secret as a credential): * mcp-testkit --transport http --auth * - * # Store credentials via CLI or Agentspan UI: + * # Store credentials via CLI or Conductor UI: * agentspan credentials set MCP_TEST_API_KEY * * Requirements: * - Conductor server with LLM support * - mcp-testkit running on http://localhost:3001 (see setup above) - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, mcpTool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/05-handoffs.ts b/examples/agents/05-handoffs.ts index 2687a2d0..37b9bc92 100644 --- a/examples/agents/05-handoffs.ts +++ b/examples/agents/05-handoffs.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/05-streaming.ts b/examples/agents/05-streaming.ts index f7a2b434..66b0b89c 100644 --- a/examples/agents/05-streaming.ts +++ b/examples/agents/05-streaming.ts @@ -11,7 +11,7 @@ import { EventTypes, } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; export const agent = new Agent({ name: 'streaming_agent', diff --git a/examples/agents/06-hitl.ts b/examples/agents/06-hitl.ts index a3982a3a..9a89757c 100644 --- a/examples/agents/06-hitl.ts +++ b/examples/agents/06-hitl.ts @@ -13,7 +13,7 @@ import { tool, } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // -- Tool that requires human approval -- const publishArticle = tool( diff --git a/examples/agents/06-sequential-pipeline.ts b/examples/agents/06-sequential-pipeline.ts index bf7a9b0f..01f1f296 100644 --- a/examples/agents/06-sequential-pipeline.ts +++ b/examples/agents/06-sequential-pipeline.ts @@ -8,8 +8,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/07-memory.ts b/examples/agents/07-memory.ts index 2cdbb01a..84876af2 100644 --- a/examples/agents/07-memory.ts +++ b/examples/agents/07-memory.ts @@ -14,7 +14,7 @@ import { tool, } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // ── ConversationMemory ────────────────────────────────── diff --git a/examples/agents/07-parallel-agents.ts b/examples/agents/07-parallel-agents.ts index cd373317..9799ac31 100644 --- a/examples/agents/07-parallel-agents.ts +++ b/examples/agents/07-parallel-agents.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/08-credentials.ts b/examples/agents/08-credentials.ts index f4a82a80..15dbf01e 100644 --- a/examples/agents/08-credentials.ts +++ b/examples/agents/08-credentials.ts @@ -15,7 +15,7 @@ import { } from '@io-orkes/conductor-javascript/agents'; import type { ToolContext } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // -- Tool that declares a credential and reads it at runtime -- const dbLookup = tool( diff --git a/examples/agents/08-router-agent.ts b/examples/agents/08-router-agent.ts index 869bc5ca..edd36f71 100644 --- a/examples/agents/08-router-agent.ts +++ b/examples/agents/08-router-agent.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/09-human-in-the-loop.ts b/examples/agents/09-human-in-the-loop.ts index ca4882bb..0c40bd54 100644 --- a/examples/agents/09-human-in-the-loop.ts +++ b/examples/agents/09-human-in-the-loop.ts @@ -8,8 +8,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import * as readline from 'node:readline/promises'; diff --git a/examples/agents/09-structured-output.ts b/examples/agents/09-structured-output.ts index 8bcb1be3..e133688b 100644 --- a/examples/agents/09-structured-output.ts +++ b/examples/agents/09-structured-output.ts @@ -7,7 +7,7 @@ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // -- Define a Zod schema for the expected output -- const ArticleAnalysis = { diff --git a/examples/agents/09b-hitl-with-feedback.ts b/examples/agents/09b-hitl-with-feedback.ts index 4cf50ce3..61b9200d 100644 --- a/examples/agents/09b-hitl-with-feedback.ts +++ b/examples/agents/09b-hitl-with-feedback.ts @@ -11,8 +11,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import * as readline from 'node:readline/promises'; diff --git a/examples/agents/09c-hitl-streaming.ts b/examples/agents/09c-hitl-streaming.ts index 78b6127b..329b58c7 100644 --- a/examples/agents/09c-hitl-streaming.ts +++ b/examples/agents/09c-hitl-streaming.ts @@ -11,8 +11,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import * as readline from 'node:readline/promises'; diff --git a/examples/agents/09d-human-tool.ts b/examples/agents/09d-human-tool.ts index faf7e23c..06eeafad 100644 --- a/examples/agents/09d-human-tool.ts +++ b/examples/agents/09d-human-tool.ts @@ -16,8 +16,8 @@ * - The LLM using human input to make decisions * * Requirements: - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api - * - AGENTSPAN_LLM_MODEL (default: openai/gpt-4o-mini) + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api + * - CONDUCTOR_AGENT_LLM_MODEL (default: openai/gpt-4o-mini) */ import * as readline from 'node:readline/promises'; diff --git a/examples/agents/10-code-execution.ts b/examples/agents/10-code-execution.ts index 999d5dd1..511a684f 100644 --- a/examples/agents/10-code-execution.ts +++ b/examples/agents/10-code-execution.ts @@ -11,7 +11,7 @@ import { LocalCodeExecutor, } from '@io-orkes/conductor-javascript/agents'; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // -- Create a local code executor -- const executor = new LocalCodeExecutor({ timeout: 10 }); diff --git a/examples/agents/10-guardrails.ts b/examples/agents/10-guardrails.ts index 010752ce..2d9e4a0a 100644 --- a/examples/agents/10-guardrails.ts +++ b/examples/agents/10-guardrails.ts @@ -19,8 +19,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { diff --git a/examples/agents/108-plan-execute-refs.ts b/examples/agents/108-plan-execute-refs.ts index ad7f7377..78f76e84 100644 --- a/examples/agents/108-plan-execute-refs.ts +++ b/examples/agents/108-plan-execute-refs.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. - /** * 108 — Plan-Execute with cross-step output piping via `Ref`. * @@ -19,9 +16,9 @@ * required — because we pass `plan` directly to `runtime.run`. * * Requirements: - * - Agentspan server running on http://localhost:8080 (or - * AGENTSPAN_SERVER_URL) - * - AGENTSPAN_LLM_MODEL set (default: openai/gpt-4o-mini) + * - Conductor server running on http://localhost:8080 (or + * CONDUCTOR_AGENT_SERVER_URL) + * - CONDUCTOR_AGENT_LLM_MODEL set (default: openai/gpt-4o-mini) * * Run: npx tsx examples/108-plan-execute-refs.ts */ @@ -36,7 +33,7 @@ import { tool, } from "../../src/agents/index.js"; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? "openai/gpt-4o-mini"; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? "openai/gpt-4o-mini"; const produce = tool( async ({ record_id }: { record_id: string }) => ({ @@ -151,7 +148,7 @@ async function main() { } async function showPipelineOutputs(executionId: string) { - const base = (process.env.AGENTSPAN_SERVER_URL ?? "http://localhost:8080/api") + const base = (process.env.CONDUCTOR_AGENT_SERVER_URL ?? "http://localhost:8080/api") .replace(/\/api$/, "") .replace(/\/$/, ""); const parent = (await (await fetch(`${base}/api/workflow/${executionId}?includeTasks=true`)).json()) as { diff --git a/examples/agents/11-streaming.ts b/examples/agents/11-streaming.ts index 50788a62..301879d8 100644 --- a/examples/agents/11-streaming.ts +++ b/examples/agents/11-streaming.ts @@ -7,8 +7,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, EventTypes } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/115-plan-execute-planner-context.ts b/examples/agents/115-plan-execute-planner-context.ts index 91740133..4e400012 100644 --- a/examples/agents/115-plan-execute-planner-context.ts +++ b/examples/agents/115-plan-execute-planner-context.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. - /** * 115 — Plan-Execute with `plannerContext`: customer onboarding plan. * @@ -25,9 +22,9 @@ * Mirrors sdk/python/examples/115_plan_execute_planner_context.py. * * Requirements: - * - Agentspan server running on http://localhost:8080 (or - * AGENTSPAN_SERVER_URL) - * - AGENTSPAN_LLM_MODEL set (default: openai/gpt-4o-mini) + * - Conductor server running on http://localhost:8080 (or + * CONDUCTOR_AGENT_SERVER_URL) + * - CONDUCTOR_AGENT_LLM_MODEL set (default: openai/gpt-4o-mini) * * Run: npx tsx examples/115-plan-execute-planner-context.ts */ @@ -39,7 +36,7 @@ import { tool, } from "../../src/agents/index.js"; -const MODEL = process.env.AGENTSPAN_LLM_MODEL ?? "openai/gpt-4o-mini"; +const MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? "openai/gpt-4o-mini"; // ── Onboarding tools (deterministic, no external calls) ────────────── @@ -215,7 +212,7 @@ async function main(): Promise { async function showExecutedSteps(executionId: string): Promise { const baseUrl = ( - process.env.AGENTSPAN_SERVER_URL ?? "http://localhost:8080/api" + process.env.CONDUCTOR_AGENT_SERVER_URL ?? "http://localhost:8080/api" ) .replace(/\/$/, "") .replace(/\/api$/, ""); diff --git a/examples/agents/12-long-running.ts b/examples/agents/12-long-running.ts index aaf19098..4b4f5d84 100644 --- a/examples/agents/12-long-running.ts +++ b/examples/agents/12-long-running.ts @@ -7,8 +7,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/13-hierarchical-agents.ts b/examples/agents/13-hierarchical-agents.ts index 586427e9..c15b9edd 100644 --- a/examples/agents/13-hierarchical-agents.ts +++ b/examples/agents/13-hierarchical-agents.ts @@ -15,8 +15,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, OnTextMention } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/14-existing-workers.ts b/examples/agents/14-existing-workers.ts index 69b7284b..a4ad6f28 100644 --- a/examples/agents/14-existing-workers.ts +++ b/examples/agents/14-existing-workers.ts @@ -15,8 +15,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/15-agent-discussion.ts b/examples/agents/15-agent-discussion.ts index 56fa8210..0b45ec48 100644 --- a/examples/agents/15-agent-discussion.ts +++ b/examples/agents/15-agent-discussion.ts @@ -18,8 +18,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/16-credentials-isolated-tool.ts b/examples/agents/16-credentials-isolated-tool.ts index d88048e0..c637c835 100644 --- a/examples/agents/16-credentials-isolated-tool.ts +++ b/examples/agents/16-credentials-isolated-tool.ts @@ -22,9 +22,9 @@ * agentspan credentials set GITHUB_TOKEN # enter token when prompted * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL (> 0.4.2, for + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL (> 0.4.2, for * runtimeMetadata delivery) or conductor-oss (with PR #1255) - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - GITHUB_TOKEN stored via `agentspan credentials set` */ diff --git a/examples/agents/16-random-strategy.ts b/examples/agents/16-random-strategy.ts index a46812d3..bf110134 100644 --- a/examples/agents/16-random-strategy.ts +++ b/examples/agents/16-random-strategy.ts @@ -7,8 +7,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/16b-credentials-non-isolated.ts b/examples/agents/16b-credentials-non-isolated.ts index 3e65bebe..b427af6e 100644 --- a/examples/agents/16b-credentials-non-isolated.ts +++ b/examples/agents/16b-credentials-non-isolated.ts @@ -9,8 +9,8 @@ * - CredentialNotFoundError handling for graceful degradation * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - STRIPE_SECRET_KEY stored: agentspan credentials set STRIPE_SECRET_KEY */ diff --git a/examples/agents/16c-credentials-cli-tools.ts b/examples/agents/16c-credentials-cli-tools.ts index 068ac5fb..f30199e6 100644 --- a/examples/agents/16c-credentials-cli-tools.ts +++ b/examples/agents/16c-credentials-cli-tools.ts @@ -14,8 +14,8 @@ * agentspan credentials set AWS_SECRET_ACCESS_KEY * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - gh and aws CLIs installed */ diff --git a/examples/agents/16d-credentials-gh-cli.ts b/examples/agents/16d-credentials-gh-cli.ts index cb461188..39474825 100644 --- a/examples/agents/16d-credentials-gh-cli.ts +++ b/examples/agents/16d-credentials-gh-cli.ts @@ -11,8 +11,8 @@ * agentspan credentials set GH_TOKEN * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - `gh` CLI installed (https://cli.github.com) * - GH_TOKEN stored via `agentspan credentials set` */ diff --git a/examples/agents/16e-credentials-http-tool.ts b/examples/agents/16e-credentials-http-tool.ts index 3a5ea9eb..bab148b5 100644 --- a/examples/agents/16e-credentials-http-tool.ts +++ b/examples/agents/16e-credentials-http-tool.ts @@ -14,8 +14,8 @@ * agentspan credentials set GITHUB_TOKEN * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - GITHUB_TOKEN stored via `agentspan credentials set` */ @@ -48,7 +48,7 @@ export const agent = new Agent({ async function main() { const runtime = new AgentRuntime(); try { - const result = await runtime.run(agent, 'List the repos for agentspan'); + const result = await runtime.run(agent, 'List the repos for Conductor'); result.printResult(); // Production pattern: diff --git a/examples/agents/16f-credentials-mcp-tool.ts b/examples/agents/16f-credentials-mcp-tool.ts index 5e2fa16f..3f4d26f6 100644 --- a/examples/agents/16f-credentials-mcp-tool.ts +++ b/examples/agents/16f-credentials-mcp-tool.ts @@ -12,14 +12,14 @@ * # Start with auth (to demonstrate credential resolution): * mcp-testkit --transport http --auth * - * # Store credentials via CLI or Agentspan UI: + * # Store credentials via CLI or Conductor UI: * agentspan credentials set MCP_API_KEY * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - mcp-testkit running on http://localhost:3001 (see setup above) - * - MCP_API_KEY stored via CLI or Agentspan UI + * - MCP_API_KEY stored via CLI or Conductor UI */ import { Agent, AgentRuntime, mcpTool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/16g-credentials-framework-passthrough.ts b/examples/agents/16g-credentials-framework-passthrough.ts index 52b2bb24..b4fa9ffa 100644 --- a/examples/agents/16g-credentials-framework-passthrough.ts +++ b/examples/agents/16g-credentials-framework-passthrough.ts @@ -7,7 +7,7 @@ * - Works the same for LangChain, OpenAI Agent SDK, and Google ADK * * This pattern is used when you run a foreign framework agent (LangGraph, - * LangChain, OpenAI, ADK) through Agentspan and need tools inside the + * LangChain, OpenAI, ADK) through Conductor and need tools inside the * graph to access credentials from the credential store. * * NOTE: Since the TypeScript SDK's RunOptions does not yet support a @@ -19,8 +19,8 @@ * agentspan credentials set GITHUB_TOKEN * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - GITHUB_TOKEN stored via `agentspan credentials set` */ diff --git a/examples/agents/16h-credentials-external-worker.ts b/examples/agents/16h-credentials-external-worker.ts index a3968dfa..3308f0a5 100644 --- a/examples/agents/16h-credentials-external-worker.ts +++ b/examples/agents/16h-credentials-external-worker.ts @@ -23,9 +23,9 @@ * agentspan credentials set GITHUB_TOKEN * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL (> 0.4.2, for + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL (> 0.4.2, for * runtimeMetadata delivery) or conductor-oss (with PR #1255) - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - GITHUB_TOKEN stored via `agentspan credentials set` */ diff --git a/examples/agents/16i-credentials-langchain.ts b/examples/agents/16i-credentials-langchain.ts index 76f4c1f7..d7debfce 100644 --- a/examples/agents/16i-credentials-langchain.ts +++ b/examples/agents/16i-credentials-langchain.ts @@ -6,8 +6,8 @@ * and injected into process.env before the executor runs * * NOTE: This example demonstrates the credential injection pattern for - * LangChain agents running through Agentspan. Since LangChain is an - * optional dependency, the example uses native Agentspan Agent with + * LangChain agents running through Conductor. Since LangChain is an + * optional dependency, the example uses native Conductor Agent with * credential-aware tools that mirror what a LangChain agent would do. * * In a full LangChain integration, you would: @@ -18,8 +18,8 @@ * agentspan credentials set GITHUB_TOKEN * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - GITHUB_TOKEN stored via `agentspan credentials set` */ diff --git a/examples/agents/16j-credentials-openai-sdk.ts b/examples/agents/16j-credentials-openai-sdk.ts index f52fd98b..950de7c5 100644 --- a/examples/agents/16j-credentials-openai-sdk.ts +++ b/examples/agents/16j-credentials-openai-sdk.ts @@ -6,8 +6,8 @@ * - Agent tools can read credentials from process.env * * NOTE: This example demonstrates the credential injection pattern for - * OpenAI Agent SDK agents running through Agentspan. Since the OpenAI - * Agent SDK is an optional dependency, the example uses native Agentspan + * OpenAI Agent SDK agents running through Conductor. Since the OpenAI + * Agent SDK is an optional dependency, the example uses native Conductor * Agent with credential-aware tools that mirror what an OpenAI agent tool * would do. * @@ -19,8 +19,8 @@ * agentspan credentials set GITHUB_TOKEN * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - GITHUB_TOKEN stored via `agentspan credentials set` */ diff --git a/examples/agents/16k-credentials-google-adk.ts b/examples/agents/16k-credentials-google-adk.ts index 291a1e49..d4b48a11 100644 --- a/examples/agents/16k-credentials-google-adk.ts +++ b/examples/agents/16k-credentials-google-adk.ts @@ -6,8 +6,8 @@ * and injected into process.env before agent execution * * NOTE: This example demonstrates the credential injection pattern for - * Google ADK agents running through Agentspan. Since Google ADK is an - * optional dependency, the example uses native Agentspan Agent with + * Google ADK agents running through Conductor. Since Google ADK is an + * optional dependency, the example uses native Conductor Agent with * credential-aware tools that mirror what an ADK agent tool would do. * * In a full Google ADK integration, you would: @@ -18,8 +18,8 @@ * agentspan credentials set GITHUB_TOKEN * * Requirements: - * - Agentspan server running at AGENTSPAN_SERVER_URL - * - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + * - Conductor server running at CONDUCTOR_AGENT_SERVER_URL + * - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) * - GITHUB_TOKEN stored via `agentspan credentials set` */ diff --git a/examples/agents/17-scheduled-agent.ts b/examples/agents/17-scheduled-agent.ts index f2d94cd4..c4c8ad80 100644 --- a/examples/agents/17-scheduled-agent.ts +++ b/examples/agents/17-scheduled-agent.ts @@ -16,11 +16,11 @@ * 8. Redeploy with an empty list to purge all schedules (cleanup). * * Requirements: - * - Conductor server at AGENTSPAN_SERVER_URL (default: http://localhost:8080/api) + * - Conductor server at CONDUCTOR_AGENT_SERVER_URL (default: http://localhost:8080/api) * - Scheduler module enabled (on by default) * * Run: - * AGENTSPAN_SERVER_URL=http://localhost:8080/api \ + * CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api \ * npx ts-node examples/17-scheduled-agent.ts */ diff --git a/examples/agents/17-swarm-orchestration.ts b/examples/agents/17-swarm-orchestration.ts index 6d7ac99f..b25b0ce6 100644 --- a/examples/agents/17-swarm-orchestration.ts +++ b/examples/agents/17-swarm-orchestration.ts @@ -18,8 +18,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, OnTextMention } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/18-manual-selection.ts b/examples/agents/18-manual-selection.ts index 7a53472f..4c2d1e76 100644 --- a/examples/agents/18-manual-selection.ts +++ b/examples/agents/18-manual-selection.ts @@ -13,8 +13,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import * as readline from 'node:readline/promises'; diff --git a/examples/agents/19-composable-termination.ts b/examples/agents/19-composable-termination.ts index 6a038a1c..dda7a10c 100644 --- a/examples/agents/19-composable-termination.ts +++ b/examples/agents/19-composable-termination.ts @@ -11,8 +11,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { diff --git a/examples/agents/20-constrained-transitions.ts b/examples/agents/20-constrained-transitions.ts index 38cba396..4e71c820 100644 --- a/examples/agents/20-constrained-transitions.ts +++ b/examples/agents/20-constrained-transitions.ts @@ -11,8 +11,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/21-regex-guardrails.ts b/examples/agents/21-regex-guardrails.ts index 37294864..c90d3c60 100644 --- a/examples/agents/21-regex-guardrails.ts +++ b/examples/agents/21-regex-guardrails.ts @@ -14,8 +14,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool, RegexGuardrail } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/22-llm-guardrails.ts b/examples/agents/22-llm-guardrails.ts index 6e26bbd8..6025a1e3 100644 --- a/examples/agents/22-llm-guardrails.ts +++ b/examples/agents/22-llm-guardrails.ts @@ -12,8 +12,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, LLMGuardrail } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/23-token-tracking.ts b/examples/agents/23-token-tracking.ts index 87bc6531..5f84081b 100644 --- a/examples/agents/23-token-tracking.ts +++ b/examples/agents/23-token-tracking.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/24-code-execution.ts b/examples/agents/24-code-execution.ts index e40bd569..3daaf2ff 100644 --- a/examples/agents/24-code-execution.ts +++ b/examples/agents/24-code-execution.ts @@ -12,8 +12,8 @@ * Requirements: * - Conductor server with LLM support * - Docker (for DockerCodeExecutor example) - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { diff --git a/examples/agents/25-semantic-memory.ts b/examples/agents/25-semantic-memory.ts index e9ef0fd8..eee44629 100644 --- a/examples/agents/25-semantic-memory.ts +++ b/examples/agents/25-semantic-memory.ts @@ -9,8 +9,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { diff --git a/examples/agents/26-opentelemetry-tracing.ts b/examples/agents/26-opentelemetry-tracing.ts index cfed9dad..fa60ec42 100644 --- a/examples/agents/26-opentelemetry-tracing.ts +++ b/examples/agents/26-opentelemetry-tracing.ts @@ -14,8 +14,8 @@ * Requirements: * - npm install @opentelemetry/api @opentelemetry/sdk-trace-base * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool, isTracingEnabled } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/28-gpt-assistant-agent.ts b/examples/agents/28-gpt-assistant-agent.ts index 59338b91..09e1bc17 100644 --- a/examples/agents/28-gpt-assistant-agent.ts +++ b/examples/agents/28-gpt-assistant-agent.ts @@ -12,8 +12,8 @@ * Requirements: * - Conductor server with LLM support * - OPENAI_API_KEY=sk-... as environment variable - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { AgentRuntime, GPTAssistantAgent } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/29-agent-introductions.ts b/examples/agents/29-agent-introductions.ts index 626d5edc..6a3a8239 100644 --- a/examples/agents/29-agent-introductions.ts +++ b/examples/agents/29-agent-introductions.ts @@ -10,8 +10,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/30-multimodal-agent.ts b/examples/agents/30-multimodal-agent.ts index 71b3b008..0172dd17 100644 --- a/examples/agents/30-multimodal-agent.ts +++ b/examples/agents/30-multimodal-agent.ts @@ -13,8 +13,8 @@ * * Requirements: * - Conductor server with LLM support (OpenAI key configured) - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/30-skills-dg-review.ts b/examples/agents/30-skills-dg-review.ts index e564219b..66cf0919 100644 --- a/examples/agents/30-skills-dg-review.ts +++ b/examples/agents/30-skills-dg-review.ts @@ -10,7 +10,7 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api * - /dg skill installed (https://github.com/v1r3n/dinesh-gilfoyle) */ diff --git a/examples/agents/31-skills-conductor.ts b/examples/agents/31-skills-conductor.ts index bcc0162e..3416cb7a 100644 --- a/examples/agents/31-skills-conductor.ts +++ b/examples/agents/31-skills-conductor.ts @@ -9,7 +9,7 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api * - conductor-skills installed (https://github.com/conductor-oss/conductor-skills) */ diff --git a/examples/agents/31-tool-guardrails.ts b/examples/agents/31-tool-guardrails.ts index 3f67bfd6..282ac81a 100644 --- a/examples/agents/31-tool-guardrails.ts +++ b/examples/agents/31-tool-guardrails.ts @@ -9,8 +9,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, guardrail, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/32-human-guardrail.ts b/examples/agents/32-human-guardrail.ts index 48d3e7f1..4956efef 100644 --- a/examples/agents/32-human-guardrail.ts +++ b/examples/agents/32-human-guardrail.ts @@ -9,8 +9,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import * as readline from 'node:readline/promises'; diff --git a/examples/agents/32-skills-multi-agent.ts b/examples/agents/32-skills-multi-agent.ts index 42b074ec..36a1b14c 100644 --- a/examples/agents/32-skills-multi-agent.ts +++ b/examples/agents/32-skills-multi-agent.ts @@ -10,7 +10,7 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api * - /dg skill installed (https://github.com/v1r3n/dinesh-gilfoyle) * - conductor skill installed (https://github.com/conductor-oss/conductor-skills) */ diff --git a/examples/agents/33-external-workers.ts b/examples/agents/33-external-workers.ts index 179385b7..f6e85772 100644 --- a/examples/agents/33-external-workers.ts +++ b/examples/agents/33-external-workers.ts @@ -15,8 +15,8 @@ * Requirements: * - Conductor server with LLM support * - The referenced workers must be running somewhere - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/33-single-turn-tool.ts b/examples/agents/33-single-turn-tool.ts index 0415239f..9d8520fa 100644 --- a/examples/agents/33-single-turn-tool.ts +++ b/examples/agents/33-single-turn-tool.ts @@ -10,8 +10,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/35-standalone-guardrails.ts b/examples/agents/35-standalone-guardrails.ts index 51751e97..06c16a2b 100644 --- a/examples/agents/35-standalone-guardrails.ts +++ b/examples/agents/35-standalone-guardrails.ts @@ -13,8 +13,8 @@ * Requirements: * Part 1 (standalone): none -- no server, no LLM, no workers. * Part 2 (as workers): Conductor server - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { guardrail } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/36-simple-agent-guardrails.ts b/examples/agents/36-simple-agent-guardrails.ts index f5df0f28..3266252d 100644 --- a/examples/agents/36-simple-agent-guardrails.ts +++ b/examples/agents/36-simple-agent-guardrails.ts @@ -15,8 +15,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, RegexGuardrail, guardrail } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/37-fix-guardrail.ts b/examples/agents/37-fix-guardrail.ts index cbe72a10..fdf45593 100644 --- a/examples/agents/37-fix-guardrail.ts +++ b/examples/agents/37-fix-guardrail.ts @@ -17,8 +17,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, guardrail, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/38-tech-trends.ts b/examples/agents/38-tech-trends.ts index 962d604c..f42696be 100644 --- a/examples/agents/38-tech-trends.ts +++ b/examples/agents/38-tech-trends.ts @@ -13,8 +13,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, pdfTool, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/39-local-code-execution.ts b/examples/agents/39-local-code-execution.ts index 8193914c..b4f15e60 100644 --- a/examples/agents/39-local-code-execution.ts +++ b/examples/agents/39-local-code-execution.ts @@ -12,8 +12,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, LocalCodeExecutor } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/39a-docker-code-execution.ts b/examples/agents/39a-docker-code-execution.ts index 146fd5c5..40afd023 100644 --- a/examples/agents/39a-docker-code-execution.ts +++ b/examples/agents/39a-docker-code-execution.ts @@ -8,8 +8,8 @@ * Requirements: * - Conductor server with LLM support * - Docker installed and daemon running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, DockerCodeExecutor } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/39b-jupyter-code-execution.ts b/examples/agents/39b-jupyter-code-execution.ts index bd510d04..1f54c2cb 100644 --- a/examples/agents/39b-jupyter-code-execution.ts +++ b/examples/agents/39b-jupyter-code-execution.ts @@ -9,8 +9,8 @@ * Requirements: * - Conductor server with LLM support * - Jupyter runtime installed (jupyter_client, ipykernel) - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, JupyterCodeExecutor } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/39c-serverless-code-execution.ts b/examples/agents/39c-serverless-code-execution.ts index c23ced41..f42be938 100644 --- a/examples/agents/39c-serverless-code-execution.ts +++ b/examples/agents/39c-serverless-code-execution.ts @@ -10,8 +10,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { createServer } from 'http'; diff --git a/examples/agents/40-media-generation-agent.ts b/examples/agents/40-media-generation-agent.ts index a7da64bf..3b9c320e 100644 --- a/examples/agents/40-media-generation-agent.ts +++ b/examples/agents/40-media-generation-agent.ts @@ -14,8 +14,8 @@ * * Requirements: * - Conductor server with OpenAI integration configured - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, imageTool, audioTool, videoTool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/41-sequential-pipeline-tools.ts b/examples/agents/41-sequential-pipeline-tools.ts index 7ef8853a..34eae6c5 100644 --- a/examples/agents/41-sequential-pipeline-tools.ts +++ b/examples/agents/41-sequential-pipeline-tools.ts @@ -12,8 +12,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/42-security-testing.ts b/examples/agents/42-security-testing.ts index c8649503..853dc903 100644 --- a/examples/agents/42-security-testing.ts +++ b/examples/agents/42-security-testing.ts @@ -16,8 +16,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/43-data-security-pipeline.ts b/examples/agents/43-data-security-pipeline.ts index dffc7896..05c640a6 100644 --- a/examples/agents/43-data-security-pipeline.ts +++ b/examples/agents/43-data-security-pipeline.ts @@ -15,8 +15,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/44-safety-guardrails.ts b/examples/agents/44-safety-guardrails.ts index a06cf52a..a11909db 100644 --- a/examples/agents/44-safety-guardrails.ts +++ b/examples/agents/44-safety-guardrails.ts @@ -16,8 +16,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/45-agent-tool.ts b/examples/agents/45-agent-tool.ts index 833043a9..c1d08dc6 100644 --- a/examples/agents/45-agent-tool.ts +++ b/examples/agents/45-agent-tool.ts @@ -12,8 +12,8 @@ * * Requirements: * - Conductor server with AgentTool support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, agentTool, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/46-transfer-control.ts b/examples/agents/46-transfer-control.ts index bb066919..e2484773 100644 --- a/examples/agents/46-transfer-control.ts +++ b/examples/agents/46-transfer-control.ts @@ -7,8 +7,8 @@ * * Requirements: * - Conductor server - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/47-callbacks.ts b/examples/agents/47-callbacks.ts index f555c8f1..0e4a4a1f 100644 --- a/examples/agents/47-callbacks.ts +++ b/examples/agents/47-callbacks.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with callback support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, CallbackHandler, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/48-planner.ts b/examples/agents/48-planner.ts index 4fb6b6be..70d7ad25 100644 --- a/examples/agents/48-planner.ts +++ b/examples/agents/48-planner.ts @@ -7,8 +7,8 @@ * * Requirements: * - Conductor server with planner support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/49-include-contents.ts b/examples/agents/49-include-contents.ts index b683fd82..d228e5b4 100644 --- a/examples/agents/49-include-contents.ts +++ b/examples/agents/49-include-contents.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with include_contents support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/50-thinking-config.ts b/examples/agents/50-thinking-config.ts index 4f5ced0c..a67c046a 100644 --- a/examples/agents/50-thinking-config.ts +++ b/examples/agents/50-thinking-config.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with thinking config support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/51-shared-state.ts b/examples/agents/51-shared-state.ts index 4cdd9ec7..9e0832df 100644 --- a/examples/agents/51-shared-state.ts +++ b/examples/agents/51-shared-state.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with state support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/52-nested-strategies.ts b/examples/agents/52-nested-strategies.ts index 90336766..8d46ba2f 100644 --- a/examples/agents/52-nested-strategies.ts +++ b/examples/agents/52-nested-strategies.ts @@ -8,8 +8,8 @@ * * Requirements: * - Conductor server - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/53-agent-lifecycle-callbacks.ts b/examples/agents/53-agent-lifecycle-callbacks.ts index 683a88c4..d8dfaa80 100644 --- a/examples/agents/53-agent-lifecycle-callbacks.ts +++ b/examples/agents/53-agent-lifecycle-callbacks.ts @@ -7,8 +7,8 @@ * * Requirements: * - Conductor server with callback support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, CallbackHandler, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/54-software-bug-assistant.ts b/examples/agents/54-software-bug-assistant.ts index b66c9348..7acb8a64 100644 --- a/examples/agents/54-software-bug-assistant.ts +++ b/examples/agents/54-software-bug-assistant.ts @@ -8,8 +8,8 @@ * * Requirements: * - Conductor server with AgentTool + MCP support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable * - GH_TOKEN in environment (optional, for GitHub MCP) */ diff --git a/examples/agents/55-ml-engineering.ts b/examples/agents/55-ml-engineering.ts index 64156d1d..5204687c 100644 --- a/examples/agents/55-ml-engineering.ts +++ b/examples/agents/55-ml-engineering.ts @@ -11,8 +11,8 @@ * * Requirements: * - Conductor server - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/56-rag-agent.ts b/examples/agents/56-rag-agent.ts index 6947408d..fe7eb3ee 100644 --- a/examples/agents/56-rag-agent.ts +++ b/examples/agents/56-rag-agent.ts @@ -8,8 +8,8 @@ * Requirements: * - Conductor server with RAG system tasks enabled * - A configured vector database (e.g., pgvector) - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, searchTool, indexTool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/57-plan-dry-run.ts b/examples/agents/57-plan-dry-run.ts index 9b588753..9e8328a7 100644 --- a/examples/agents/57-plan-dry-run.ts +++ b/examples/agents/57-plan-dry-run.ts @@ -8,8 +8,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/58-scatter-gather.ts b/examples/agents/58-scatter-gather.ts index 7062e341..f8ccfc57 100644 --- a/examples/agents/58-scatter-gather.ts +++ b/examples/agents/58-scatter-gather.ts @@ -8,8 +8,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_SECONDARY_LLM_MODEL=openai/gpt-4o as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_SECONDARY_LLM_MODEL=openai/gpt-4o as environment variable */ import { Agent, AgentRuntime, scatterGather, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/59-coding-agent.ts b/examples/agents/59-coding-agent.ts index 24c445f0..0d649c46 100644 --- a/examples/agents/59-coding-agent.ts +++ b/examples/agents/59-coding-agent.ts @@ -9,7 +9,7 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/60-github-coding-agent.ts b/examples/agents/60-github-coding-agent.ts index b3bc5cdf..7f6abd3a 100644 --- a/examples/agents/60-github-coding-agent.ts +++ b/examples/agents/60-github-coding-agent.ts @@ -9,7 +9,7 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable * - gh CLI authenticated * - Git configured with push access to the repo */ diff --git a/examples/agents/60a-github-coding-agent-simple.ts b/examples/agents/60a-github-coding-agent-simple.ts index f549fff3..f888420d 100644 --- a/examples/agents/60a-github-coding-agent-simple.ts +++ b/examples/agents/60a-github-coding-agent-simple.ts @@ -6,7 +6,7 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable * - gh CLI authenticated * - Git configured with push access to the repo */ diff --git a/examples/agents/61-github-coding-agent-chained.ts b/examples/agents/61-github-coding-agent-chained.ts index 7d1fd33c..7a5ab7bc 100644 --- a/examples/agents/61-github-coding-agent-chained.ts +++ b/examples/agents/61-github-coding-agent-chained.ts @@ -7,7 +7,7 @@ * 3. Create pull request (CLI tool: gh) * * Requirements: - * - Agentspan server running + * - Conductor server running * - GITHUB_TOKEN stored: agentspan credentials set GITHUB_TOKEN * - gh CLI installed */ diff --git a/examples/agents/62-cli-tool-guardrails.ts b/examples/agents/62-cli-tool-guardrails.ts index 035aaf50..e3367328 100644 --- a/examples/agents/62-cli-tool-guardrails.ts +++ b/examples/agents/62-cli-tool-guardrails.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, RegexGuardrail } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/63-deploy.ts b/examples/agents/63-deploy.ts index 297aed70..996a3c07 100644 --- a/examples/agents/63-deploy.ts +++ b/examples/agents/63-deploy.ts @@ -9,8 +9,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/63b-serve.ts b/examples/agents/63b-serve.ts index 9e0ed3ca..73859df9 100644 --- a/examples/agents/63b-serve.ts +++ b/examples/agents/63b-serve.ts @@ -13,8 +13,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/63c-run-by-name.ts b/examples/agents/63c-run-by-name.ts index d3783d22..b5b7a998 100644 --- a/examples/agents/63c-run-by-name.ts +++ b/examples/agents/63c-run-by-name.ts @@ -9,8 +9,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { docAssistant } from './63-deploy.js'; diff --git a/examples/agents/63d-serve-from-package.ts b/examples/agents/63d-serve-from-package.ts index 4d6a5040..ab465403 100644 --- a/examples/agents/63d-serve-from-package.ts +++ b/examples/agents/63d-serve-from-package.ts @@ -10,8 +10,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/63e-run-monitoring.ts b/examples/agents/63e-run-monitoring.ts index 56a31bce..23a2f0fa 100644 --- a/examples/agents/63e-run-monitoring.ts +++ b/examples/agents/63e-run-monitoring.ts @@ -3,8 +3,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { monitoringAgent } from './63d-serve-from-package.js'; diff --git a/examples/agents/64-swarm-with-tools.ts b/examples/agents/64-swarm-with-tools.ts index 5571ac17..9b9e3f93 100644 --- a/examples/agents/64-swarm-with-tools.ts +++ b/examples/agents/64-swarm-with-tools.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, OnTextMention, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/65-parallel-with-tools.ts b/examples/agents/65-parallel-with-tools.ts index b8b2fef1..96d487f5 100644 --- a/examples/agents/65-parallel-with-tools.ts +++ b/examples/agents/65-parallel-with-tools.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/66-handoff-to-parallel.ts b/examples/agents/66-handoff-to-parallel.ts index f235debd..bd742b06 100644 --- a/examples/agents/66-handoff-to-parallel.ts +++ b/examples/agents/66-handoff-to-parallel.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/67-router-to-sequential.ts b/examples/agents/67-router-to-sequential.ts index e1ddf919..5fde3086 100644 --- a/examples/agents/67-router-to-sequential.ts +++ b/examples/agents/67-router-to-sequential.ts @@ -6,8 +6,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/68-context-condensation.ts b/examples/agents/68-context-condensation.ts index f29e2aad..7e707238 100644 --- a/examples/agents/68-context-condensation.ts +++ b/examples/agents/68-context-condensation.ts @@ -10,8 +10,8 @@ * * Requirements: * - Conductor server with LLM support + agentspan.default-context-window=10000 - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, agentTool, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/70-ce-support-agent.ts b/examples/agents/70-ce-support-agent.ts index 1fa004f4..5db9013b 100644 --- a/examples/agents/70-ce-support-agent.ts +++ b/examples/agents/70-ce-support-agent.ts @@ -14,8 +14,8 @@ * * Requirements: * - Conductor server - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { Agent, AgentRuntime, RegexGuardrail, agentTool, tool } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/71-api-tool.ts b/examples/agents/71-api-tool.ts index 1d8062b4..6dfd9c83 100644 --- a/examples/agents/71-api-tool.ts +++ b/examples/agents/71-api-tool.ts @@ -19,13 +19,13 @@ * # Or start with auth (requires storing the secret as a credential): * mcp-testkit --transport http --auth * - * # Store credentials via CLI or Agentspan UI: + * # Store credentials via CLI or Conductor UI: * agentspan credentials set HTTP_TEST_API_KEY * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable * - mcp-testkit running on http://localhost:3001 (for examples 1-3, see setup above) * - For GitHub example: agentspan credentials set GITHUB_TOKEN ghp_xxx */ diff --git a/examples/agents/74-cli-error-output.ts b/examples/agents/74-cli-error-output.ts index c838e91b..2f4f44d2 100644 --- a/examples/agents/74-cli-error-output.ts +++ b/examples/agents/74-cli-error-output.ts @@ -7,8 +7,8 @@ * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL (e.g. http://localhost:8080/api) - * - AGENTSPAN_LLM_MODEL (e.g. openai/gpt-4o-mini) + * - CONDUCTOR_AGENT_SERVER_URL (e.g. http://localhost:8080/api) + * - CONDUCTOR_AGENT_LLM_MODEL (e.g. openai/gpt-4o-mini) */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/90-guardrail-e2e-tests.ts b/examples/agents/90-guardrail-e2e-tests.ts index f0e81a2f..ca75c498 100644 --- a/examples/agents/90-guardrail-e2e-tests.ts +++ b/examples/agents/90-guardrail-e2e-tests.ts @@ -5,8 +5,8 @@ * * Requirements: * - Conductor server running - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - * - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api as environment variable + * - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable */ import { diff --git a/examples/agents/README.md b/examples/agents/README.md index eef8ae93..6ba81742 100644 --- a/examples/agents/README.md +++ b/examples/agents/README.md @@ -1,6 +1,6 @@ # Examples -Runnable examples demonstrating every feature of the Agentspan TypeScript SDK. +Runnable examples demonstrating every feature of the Conductor TypeScript SDK. **200+ runnable examples in total**: the core examples cataloged below, plus the [quickstart/](quickstart/) guides and framework ports for Google ADK, @@ -23,7 +23,7 @@ In production, the three concerns are separated: ``` ┌──────────────────────────────────────────────────────────────┐ │ 1. DEPLOY (once, during CI/CD) │ -│ Registers the agent definition with the Agentspan server │ +│ Registers the agent definition with the Conductor server │ │ │ │ await runtime.deploy(agent); │ │ // or CLI: agentspan deploy --package my-agents │ @@ -37,7 +37,7 @@ In production, the three concerns are separated: │ 3. RUN (on-demand, from anywhere) │ │ Triggers an agent execution │ │ │ -│ agentspan run "prompt" │ +│ Conductor run "prompt" │ │ // or SDK: await runtime.run("agent_name", "prompt"); │ │ // or REST API │ └──────────────────────────────────────────────────────────────┘ @@ -114,15 +114,15 @@ cd vercel-ai && npm install Export environment variables: ```bash -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -# export AGENTSPAN_AUTH_KEY= # if authentication is enabled -# export AGENTSPAN_AUTH_SECRET= +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini +export CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api +# export CONDUCTOR_AGENT_AUTH_KEY= # if authentication is enabled +# export CONDUCTOR_AGENT_AUTH_SECRET= ``` #### 2.1. Choose a model -The `AGENTSPAN_LLM_MODEL` variable uses the `provider/model-name` format. Examples: +The `CONDUCTOR_AGENT_LLM_MODEL` variable uses the `provider/model-name` format. Examples: | Provider | Model string | API key env var | |----------|-------------|-----------------| @@ -341,4 +341,4 @@ cd examples/agents/openai && npx tsx 01-basic-agent.ts | [langgraph/](langgraph/) | LangGraph | State graphs, react agents, memory, RAG | | [openai/](openai/) | OpenAI Agents SDK | Agents, tools, handoffs, guardrails | | [vercel-ai/](vercel-ai/) | Vercel AI SDK | Agents, tools, streaming, HITL | -| [quickstart/](quickstart/) | Agentspan Quickstart | Minimal getting-started guides | +| [quickstart/](quickstart/) | Conductor Quickstart | Minimal getting-started guides | diff --git a/examples/agents/adk/00-hello-world.ts b/examples/agents/adk/00-hello-world.ts index 0df867ba..44387c63 100644 --- a/examples/agents/adk/00-hello-world.ts +++ b/examples/agents/adk/00-hello-world.ts @@ -6,13 +6,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; export const agent = new LlmAgent({ name: 'greeter', @@ -20,7 +20,7 @@ export const agent = new LlmAgent({ instruction: 'You are a friendly greeter. Reply with a warm hello and one fun fact.', }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/01-basic-agent.ts b/examples/agents/adk/01-basic-agent.ts index c71a53c8..f375ad94 100644 --- a/examples/agents/adk/01-basic-agent.ts +++ b/examples/agents/adk/01-basic-agent.ts @@ -3,18 +3,18 @@ * * Demonstrates: * - Defining an agent using Google's Agent Development Kit (ADK) - * - Running via Agentspan passthrough + * - Running via Conductor passthrough * - The runtime serializes the ADK agent and the server normalizes it * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; export const agent = new LlmAgent({ name: 'greeter', @@ -22,7 +22,7 @@ export const agent = new LlmAgent({ instruction: 'You are a friendly assistant. Keep your responses concise and helpful.', }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/02-function-tools.ts b/examples/agents/adk/02-function-tools.ts index 99760b68..3d868404 100644 --- a/examples/agents/adk/02-function-tools.ts +++ b/examples/agents/adk/02-function-tools.ts @@ -4,18 +4,18 @@ * Demonstrates: * - Defining tools with FunctionTool from @google/adk * - Multiple tools with typed parameters (via zod) - * - Tools registered as workers in Agentspan passthrough mode + * - Tools registered as workers in Conductor passthrough mode * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -85,7 +85,7 @@ export const agent = new LlmAgent({ tools: [getWeather, convertTemperature, getTimeZone], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/03-structured-output.ts b/examples/agents/adk/03-structured-output.ts index 9b7edf1e..2b9a1d15 100644 --- a/examples/agents/adk/03-structured-output.ts +++ b/examples/agents/adk/03-structured-output.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, zodObjectToSchema } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Output schemas ─────────────────────────────────────────────────── @@ -56,7 +56,7 @@ export const agent = new LlmAgent({ }, }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/04-sub-agents.ts b/examples/agents/adk/04-sub-agents.ts index c55f3cca..0dd72533 100644 --- a/examples/agents/adk/04-sub-agents.ts +++ b/examples/agents/adk/04-sub-agents.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Specialist tools ───────────────────────────────────────────────── @@ -118,7 +118,7 @@ export const coordinator = new LlmAgent({ subAgents: [flightAgent, hotelAgent, advisoryAgent], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/05-generation-config.ts b/examples/agents/adk/05-generation-config.ts index 3be42135..5ef93508 100644 --- a/examples/agents/adk/05-generation-config.ts +++ b/examples/agents/adk/05-generation-config.ts @@ -8,13 +8,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Precise agent -- low temperature for factual responses ────────── @@ -42,7 +42,7 @@ export const creativeAgent = new LlmAgent({ }, }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/06-streaming.ts b/examples/agents/adk/06-streaming.ts index bf35d665..ae1fec1b 100644 --- a/examples/agents/adk/06-streaming.ts +++ b/examples/agents/adk/06-streaming.ts @@ -3,18 +3,18 @@ * * Demonstrates: * - Streaming events from a Google ADK agent - * - Agentspan path: runtime.stream() with event types + * - Conductor path: runtime.stream() with event types * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool ───────────────────────────────────────────────────────────── @@ -59,7 +59,7 @@ export const agent = new LlmAgent({ tools: [searchDocumentation], }); -// ── Run on agentspan (streaming) ─────────────────────────────────── +// ── Run on Conductor (streaming) ─────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/07-output-key-state.ts b/examples/agents/adk/07-output-key-state.ts index f7b0ae0a..45381bc0 100644 --- a/examples/agents/adk/07-output-key-state.ts +++ b/examples/agents/adk/07-output-key-state.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -94,7 +94,7 @@ export const coordinator = new LlmAgent({ subAgents: [analyst, visualizer], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/08-instruction-templating.ts b/examples/agents/adk/08-instruction-templating.ts index 132162fe..34bf1f24 100644 --- a/examples/agents/adk/08-instruction-templating.ts +++ b/examples/agents/adk/08-instruction-templating.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -86,7 +86,7 @@ export const agent = new LlmAgent({ tools: [getUserPreferences, searchTutorials], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/09-multi-tool-agent.ts b/examples/agents/adk/09-multi-tool-agent.ts index 937712dd..c334d90a 100644 --- a/examples/agents/adk/09-multi-tool-agent.ts +++ b/examples/agents/adk/09-multi-tool-agent.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -138,7 +138,7 @@ export const agent = new LlmAgent({ tools: [searchProducts, checkInventory, calculateShipping, applyCoupon], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/10-hierarchical-agents.ts b/examples/agents/adk/10-hierarchical-agents.ts index 3a031ee3..b40e3374 100644 --- a/examples/agents/adk/10-hierarchical-agents.ts +++ b/examples/agents/adk/10-hierarchical-agents.ts @@ -9,14 +9,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Level 3: Specialist tools ───────────────────────────────────────── @@ -148,7 +148,7 @@ export const coordinator = new LlmAgent({ subAgents: [reliabilityLead, securityLead], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/11-sequential-agent.ts b/examples/agents/adk/11-sequential-agent.ts index 68fc8060..82f26841 100644 --- a/examples/agents/adk/11-sequential-agent.ts +++ b/examples/agents/adk/11-sequential-agent.ts @@ -8,13 +8,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, SequentialAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // Step 1: Research agent gathers facts export const researcher = new LlmAgent({ @@ -50,7 +50,7 @@ export const pipeline = new SequentialAgent({ subAgents: [researcher, writer, editor], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/12-parallel-agent.ts b/examples/agents/adk/12-parallel-agent.ts index a0e9f99f..6e282dfe 100644 --- a/examples/agents/adk/12-parallel-agent.ts +++ b/examples/agents/adk/12-parallel-agent.ts @@ -8,13 +8,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, ParallelAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // Three analysts run in parallel export const marketAnalyst = new LlmAgent({ @@ -50,7 +50,7 @@ export const parallelAnalysis = new ParallelAgent({ subAgents: [marketAnalyst, techAnalyst, riskAnalyst], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/13-loop-agent.ts b/examples/agents/adk/13-loop-agent.ts index ac22486c..c0184ad0 100644 --- a/examples/agents/adk/13-loop-agent.ts +++ b/examples/agents/adk/13-loop-agent.ts @@ -8,13 +8,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, SequentialAgent, LoopAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // Writer drafts content export const writer = new LlmAgent({ @@ -50,7 +50,7 @@ export const refinementLoop = new LoopAgent({ maxIterations: 3, }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/14-callbacks.ts b/examples/agents/adk/14-callbacks.ts index b6c834f0..33727ddc 100644 --- a/examples/agents/adk/14-callbacks.ts +++ b/examples/agents/adk/14-callbacks.ts @@ -12,14 +12,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -105,7 +105,7 @@ export const agent = new LlmAgent({ }, }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/15-global-instruction.ts b/examples/agents/adk/15-global-instruction.ts index 5c568c7e..478c9225 100644 --- a/examples/agents/adk/15-global-instruction.ts +++ b/examples/agents/adk/15-global-instruction.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -84,7 +84,7 @@ export const agent = new LlmAgent({ tools: [getProductInfo, getStoreHours], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/16-customer-service.ts b/examples/agents/adk/16-customer-service.ts index 7d6837fb..9393ab42 100644 --- a/examples/agents/adk/16-customer-service.ts +++ b/examples/agents/adk/16-customer-service.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Domain tools ────────────────────────────────────────────────── @@ -126,7 +126,7 @@ export const agent = new LlmAgent({ tools: [getAccountDetails, getBillingHistory, submitSupportTicket, updateAccountPlan], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/17-financial-advisor.ts b/examples/agents/adk/17-financial-advisor.ts index 8444df53..ed4d08e3 100644 --- a/examples/agents/adk/17-financial-advisor.ts +++ b/examples/agents/adk/17-financial-advisor.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Portfolio tools ─────────────────────────────────────────────── @@ -162,7 +162,7 @@ export const coordinator = new LlmAgent({ subAgents: [portfolioAnalyst, marketResearcher, taxAdvisor], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/18-order-processing.ts b/examples/agents/adk/18-order-processing.ts index 3f79cdcf..00d5bb43 100644 --- a/examples/agents/adk/18-order-processing.ts +++ b/examples/agents/adk/18-order-processing.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -128,7 +128,7 @@ export const agent = new LlmAgent({ tools: [searchCatalog, checkStock, calculateTotal, placeOrder], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/19-supply-chain.ts b/examples/agents/adk/19-supply-chain.ts index 5efe67bf..f459c050 100644 --- a/examples/agents/adk/19-supply-chain.ts +++ b/examples/agents/adk/19-supply-chain.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Inventory tools ─────────────────────────────────────────────── @@ -163,7 +163,7 @@ export const coordinator = new LlmAgent({ subAgents: [inventoryAgent, logisticsAgent, demandAgent], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/20-blog-writer.ts b/examples/agents/adk/20-blog-writer.ts index d48ca823..928cb13d 100644 --- a/examples/agents/adk/20-blog-writer.ts +++ b/examples/agents/adk/20-blog-writer.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -123,7 +123,7 @@ export const coordinator = new LlmAgent({ subAgents: [researcher, writer, editor], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/21-agent-tool.ts b/examples/agents/adk/21-agent-tool.ts index a4cae9c8..c5bce874 100644 --- a/examples/agents/adk/21-agent-tool.ts +++ b/examples/agents/adk/21-agent-tool.ts @@ -15,14 +15,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool, AgentTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Child agents (each has their own tools) ────────────────────── @@ -123,7 +123,7 @@ export const manager = new LlmAgent({ ], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/22-transfer-control.ts b/examples/agents/adk/22-transfer-control.ts index eab1c9fd..8c92ea3e 100644 --- a/examples/agents/adk/22-transfer-control.ts +++ b/examples/agents/adk/22-transfer-control.ts @@ -15,13 +15,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Specialist agents with transfer restrictions ──────────────────── @@ -67,7 +67,7 @@ export const coordinator = new LlmAgent({ subAgents: [specialistA, specialistB, specialistC], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/23-callbacks-advanced.ts b/examples/agents/adk/23-callbacks-advanced.ts index 979893d9..41ed1f31 100644 --- a/examples/agents/adk/23-callbacks-advanced.ts +++ b/examples/agents/adk/23-callbacks-advanced.ts @@ -8,14 +8,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent } from '@google/adk'; import type { BeforeModelCallback, AfterModelCallback } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Callback functions ──────────────────────────────────────────── // These run before/after each LLM invocation. @@ -57,7 +57,7 @@ export const agent = new LlmAgent({ afterModelCallback: inspectAfterModel, }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/24-planner.ts b/examples/agents/adk/24-planner.ts index af2daf97..d5d11796 100644 --- a/examples/agents/adk/24-planner.ts +++ b/examples/agents/adk/24-planner.ts @@ -12,14 +12,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -86,7 +86,7 @@ export const agent = new LlmAgent({ }, }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/25-camel-security.ts b/examples/agents/adk/25-camel-security.ts index 0df8d28b..87cd4c6b 100644 --- a/examples/agents/adk/25-camel-security.ts +++ b/examples/agents/adk/25-camel-security.ts @@ -11,14 +11,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, SequentialAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -114,7 +114,7 @@ export const pipeline = new SequentialAgent({ subAgents: [collector, validator, responder], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/26-safety-guardrails.ts b/examples/agents/adk/26-safety-guardrails.ts index ddaa999a..23d1d23d 100644 --- a/examples/agents/adk/26-safety-guardrails.ts +++ b/examples/agents/adk/26-safety-guardrails.ts @@ -11,14 +11,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, SequentialAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -106,7 +106,7 @@ export const safePipeline = new SequentialAgent({ subAgents: [assistant, safetyChecker], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/27-security-agent.ts b/examples/agents/adk/27-security-agent.ts index bc7c9d4b..41195eea 100644 --- a/examples/agents/adk/27-security-agent.ts +++ b/examples/agents/adk/27-security-agent.ts @@ -13,14 +13,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, SequentialAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -124,7 +124,7 @@ export const securityTest = new SequentialAgent({ subAgents: [redTeam, target, evaluator], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/28-movie-pipeline.ts b/examples/agents/adk/28-movie-pipeline.ts index 6aa21faf..06011d9e 100644 --- a/examples/agents/adk/28-movie-pipeline.ts +++ b/examples/agents/adk/28-movie-pipeline.ts @@ -11,14 +11,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, SequentialAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Stage tools ────────────────────────────────────────────────────── @@ -191,7 +191,7 @@ export const moviePipeline = new SequentialAgent({ subAgents: [conceptDeveloper, scriptwriter, visualDirector, audioDesigner, producer], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/29-include-contents.ts b/examples/agents/adk/29-include-contents.ts index 7d154872..24273e41 100644 --- a/examples/agents/adk/29-include-contents.ts +++ b/examples/agents/adk/29-include-contents.ts @@ -11,13 +11,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Sub-agents ─────────────────────────────────────────────────────── @@ -51,7 +51,7 @@ export const coordinator = new LlmAgent({ subAgents: [independentSummarizer, contextAwareHelper], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/30-thinking-config.ts b/examples/agents/adk/30-thinking-config.ts index 92f17fec..edaa94ca 100644 --- a/examples/agents/adk/30-thinking-config.ts +++ b/examples/agents/adk/30-thinking-config.ts @@ -10,14 +10,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Tool definitions ───────────────────────────────────────────────── @@ -54,7 +54,7 @@ export const agent = new LlmAgent({ }, }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/31-shared-state.ts b/examples/agents/adk/31-shared-state.ts index 5bd67583..7d0f4b4b 100644 --- a/examples/agents/adk/31-shared-state.ts +++ b/examples/agents/adk/31-shared-state.ts @@ -14,17 +14,17 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── In-memory state (simulating ADK ToolContext shared state) ──────── -// In the real ADK, this would be context.state. For the agentspan +// In the real ADK, this would be context.state. For the Conductor // passthrough, we simulate with module-level state. const sharedState: { shopping_list: string[] } = { shopping_list: [] }; @@ -73,7 +73,7 @@ export const agent = new LlmAgent({ tools: [addItem, getList, clearList], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/32-nested-strategies.ts b/examples/agents/adk/32-nested-strategies.ts index 28bce4eb..33d4aa72 100644 --- a/examples/agents/adk/32-nested-strategies.ts +++ b/examples/agents/adk/32-nested-strategies.ts @@ -14,13 +14,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, ParallelAgent, SequentialAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Parallel research agents ───────────────────────────────────────── @@ -63,7 +63,7 @@ export const pipeline = new SequentialAgent({ subAgents: [parallelResearch, summarizer], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/33-software-bug-assistant.ts b/examples/agents/adk/33-software-bug-assistant.ts index 1146de07..cb98bc6a 100644 --- a/examples/agents/adk/33-software-bug-assistant.ts +++ b/examples/agents/adk/33-software-bug-assistant.ts @@ -19,14 +19,14 @@ * * Requirements: * - Conductor server with AgentTool + MCP support - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api in env or .env - * - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash in env or .env + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api in env or .env + * - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash in env or .env * - GH_TOKEN in env or .env */ import { Agent, AgentRuntime, agentTool, tool, mcpTool } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o-mini'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o-mini'; // ── In-memory ticket store (mirrors real conductor-oss/conductor issues) ── diff --git a/examples/agents/adk/34-ml-engineering.ts b/examples/agents/adk/34-ml-engineering.ts index 22924a74..f503c2a0 100644 --- a/examples/agents/adk/34-ml-engineering.ts +++ b/examples/agents/adk/34-ml-engineering.ts @@ -25,13 +25,13 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, SequentialAgent, ParallelAgent, LoopAgent } from '@google/adk'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Phase 1: Data Analysis ─────────────────────────────────────────── @@ -173,7 +173,7 @@ export const mlPipeline = new SequentialAgent({ subAgents: [dataAnalyst, parallelModeling, evaluator, refinementLoop, reporter], }); -// ── Run on agentspan ─────────────────────────────────────────────── +// ── Run on Conductor ─────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/adk/35-rag-agent.ts b/examples/agents/adk/35-rag-agent.ts index 23122bc3..a802b182 100644 --- a/examples/agents/adk/35-rag-agent.ts +++ b/examples/agents/adk/35-rag-agent.ts @@ -17,14 +17,14 @@ * * Requirements: * - npm install @google/adk zod - * - AGENTSPAN_SERVER_URL for agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for Conductor path */ import { LlmAgent, FunctionTool } from '@google/adk'; import { z } from 'zod'; import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; -const model = process.env.AGENTSPAN_LLM_MODEL ?? 'gemini-2.5-flash'; +const model = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'gemini-2.5-flash'; // ── Knowledge base content ─────────────────────────────────────────── diff --git a/examples/agents/adk/README.md b/examples/agents/adk/README.md index 908bd324..4c94344b 100644 --- a/examples/agents/adk/README.md +++ b/examples/agents/adk/README.md @@ -1,11 +1,11 @@ -# Google ADK + Agentspan +# Google ADK + Conductor The `LlmAgent` and `FunctionTool` formats are natively recognized. Replace the ADK runner with `runtime.run()` — agent and tools stay identical. ## Before / After - +
Before (vanilla Google ADK)After (Agentspan)
Before (vanilla Google ADK)After (Conductor)
```typescript @@ -117,11 +117,11 @@ await runtime.shutdown(); ## Running ```bash -export AGENTSPAN_SERVER_URL=... +export CONDUCTOR_AGENT_SERVER_URL=... # For Gemini models: export GOOGLE_API_KEY=... # Or override with OpenAI: -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini export OPENAI_API_KEY=... # from the repository root diff --git a/examples/agents/adk/package.json b/examples/agents/adk/package.json index 57b6fa0c..3f443f0d 100644 --- a/examples/agents/adk/package.json +++ b/examples/agents/adk/package.json @@ -1,5 +1,5 @@ { - "name": "agentspan-examples-adk", + "name": "conductor-examples-adk", "private": true, "type": "module", "dependencies": { diff --git a/examples/agents/kitchen-sink.ts b/examples/agents/kitchen-sink.ts index 9c802c82..0b1f2e3f 100644 --- a/examples/agents/kitchen-sink.ts +++ b/examples/agents/kitchen-sink.ts @@ -1,7 +1,7 @@ /** * Kitchen Sink — Content Publishing Platform * - * A single mega-workflow that exercises every Agentspan SDK feature (89 features). + * A single mega-workflow that exercises every Conductor SDK feature (89 features). * See design/sdk-design/kitchen-sink.md for the full scenario specification. * * Demonstrates: @@ -26,13 +26,13 @@ * # Or start with auth (requires storing the secret as a credential): * mcp-testkit --transport http --auth * - * # Store credentials via CLI or Agentspan UI: + * # Store credentials via CLI or Conductor UI: * agentspan credentials set MCP_AUTH_TOKEN * agentspan credentials set SEARCH_API_KEY * * Requirements: * - Conductor server with LLM support - * - AGENTSPAN_SERVER_URL, AGENTSPAN_LLM_MODEL env vars + * - CONDUCTOR_AGENT_SERVER_URL, CONDUCTOR_AGENT_LLM_MODEL env vars * - mcp-testkit running on http://localhost:3001 (for MCP/HTTP tools) * - For full execution: Docker, credential store configured */ @@ -133,7 +133,7 @@ import type { // ── Settings ───────────────────────────────────────────── -const LLM_MODEL = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o'; +const LLM_MODEL = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o'; // ── Mock data (equivalent to Python kitchen_sink_helpers) ─ diff --git a/examples/agents/langgraph/01-hello-world.ts b/examples/agents/langgraph/01-hello-world.ts index b3059cf2..96383a63 100644 --- a/examples/agents/langgraph/01-hello-world.ts +++ b/examples/agents/langgraph/01-hello-world.ts @@ -3,7 +3,7 @@ * * Demonstrates: * - Using createReactAgent from @langchain/langgraph/prebuilt - * - Running a graph via Agentspan runtime.run() passthrough + * - Running a graph via Conductor runtime.run() passthrough */ import { createReactAgent } from '@langchain/langgraph/prebuilt'; @@ -16,7 +16,7 @@ import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; const llm = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 }); const graph = createReactAgent({ llm, tools: [], name: "hello_world_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [], @@ -26,7 +26,7 @@ const graph = createReactAgent({ llm, tools: [], name: "hello_world_agent" }); const PROMPT = 'Say hello and tell me a fun fact about Python programming.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/02-react-with-tools.ts b/examples/agents/langgraph/02-react-with-tools.ts index b56596b9..cde0a893 100644 --- a/examples/agents/langgraph/02-react-with-tools.ts +++ b/examples/agents/langgraph/02-react-with-tools.ts @@ -66,7 +66,7 @@ const graph = createReactAgent({ name: "react_tools_agent", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [calculateTool, countWordsTool, getTodayTool], @@ -77,7 +77,7 @@ const PROMPT = "What is the square root of 256? Also, how many words are in 'the quick brown fox'? And what is today's date?"; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/03-memory.ts b/examples/agents/langgraph/03-memory.ts index ae67bdd8..d43043c1 100644 --- a/examples/agents/langgraph/03-memory.ts +++ b/examples/agents/langgraph/03-memory.ts @@ -3,7 +3,7 @@ * * Demonstrates: * - Attaching a MemorySaver checkpointer to createReactAgent - * - Running via Agentspan passthrough (single turn) + * - Running via Conductor passthrough (single turn) * - How the agent remembers context from earlier messages */ @@ -19,7 +19,7 @@ const llm = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 }); const checkpointer = new MemorySaver(); const graph = createReactAgent({ llm, tools: [], checkpointer, name: "memory_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [], @@ -27,7 +27,7 @@ const graph = createReactAgent({ llm, tools: [], checkpointer, name: "memory_age }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); @@ -35,7 +35,7 @@ async function main() { const result = await runtime.run( graph, 'My name is Bob. Tell me something interesting about my name.', - { sessionId: 'agentspan-session-001' }, + { sessionId: 'conductor-session-001' }, ); console.log('Status:', result.status); result.printResult(); diff --git a/examples/agents/langgraph/04-simple-stategraph.ts b/examples/agents/langgraph/04-simple-stategraph.ts index c2d81eea..22a187cd 100644 --- a/examples/agents/langgraph/04-simple-stategraph.ts +++ b/examples/agents/langgraph/04-simple-stategraph.ts @@ -4,7 +4,7 @@ * Demonstrates: * - Defining a typed state schema with Annotation * - Building a StateGraph with multiple sequential nodes - * - LLM calls inside node functions (detected by Agentspan for interception) + * - LLM calls inside node functions (detected by Conductor for interception) * - Connecting nodes with addEdge * - Compiling and naming the graph * @@ -12,7 +12,7 @@ * Same graph structure: validate → refine → answer (3 nodes, 2 with LLM calls) * * Requirements: - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api * - OPENAI_API_KEY for ChatOpenAI */ @@ -86,7 +86,7 @@ const graph = new StateGraph(QueryState) .addEdge('answer', END) .compile({ name: "query_pipeline" }); -// Add agentspan metadata for graph-structure extraction. +// Add Conductor metadata for graph-structure extraction. // Do NOT set tools on StateGraphs — only model + framework. (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', @@ -94,7 +94,7 @@ const graph = new StateGraph(QueryState) }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/05-tool-node.ts b/examples/agents/langgraph/05-tool-node.ts index fb164d4d..e75f977c 100644 --- a/examples/agents/langgraph/05-tool-node.ts +++ b/examples/agents/langgraph/05-tool-node.ts @@ -81,7 +81,7 @@ const builder = new StateGraph(MessagesAnnotation) const graph = builder.compile({ name: "tool_node_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools, @@ -91,7 +91,7 @@ const graph = builder.compile({ name: "tool_node_agent" }); const PROMPT = 'What is the capital and population of Japan and Brazil?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/06-conditional-routing.ts b/examples/agents/langgraph/06-conditional-routing.ts index 8d5b99b5..34a1b0ab 100644 --- a/examples/agents/langgraph/06-conditional-routing.ts +++ b/examples/agents/langgraph/06-conditional-routing.ts @@ -94,14 +94,14 @@ const graph = new StateGraph(SentimentState) .addEdge('neutral', END) .compile({ name: "sentiment_router" }); -// Add agentspan metadata for extraction (no LLM in this pipeline example) +// Add Conductor metadata for extraction (no LLM in this pipeline example) (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/07-system-prompt.ts b/examples/agents/langgraph/07-system-prompt.ts index 60378041..6cb0af4e 100644 --- a/examples/agents/langgraph/07-system-prompt.ts +++ b/examples/agents/langgraph/07-system-prompt.ts @@ -39,7 +39,7 @@ const graph = createReactAgent({ name: "socratic_tutor", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [], @@ -50,7 +50,7 @@ const graph = createReactAgent({ const PROMPT = 'I want to understand why 1 + 1 = 2. Can you just tell me?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/08-structured-output.ts b/examples/agents/langgraph/08-structured-output.ts index 6bf80984..17a69d6d 100644 --- a/examples/agents/langgraph/08-structured-output.ts +++ b/examples/agents/langgraph/08-structured-output.ts @@ -35,7 +35,7 @@ const graph = createReactAgent({ name: "movie_review_agent", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [], @@ -45,7 +45,7 @@ const graph = createReactAgent({ const PROMPT = 'Write a review for the movie Inception (2010).'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/09-math-agent.ts b/examples/agents/langgraph/09-math-agent.ts index b3adf2ee..bbd3a8be 100644 --- a/examples/agents/langgraph/09-math-agent.ts +++ b/examples/agents/langgraph/09-math-agent.ts @@ -106,7 +106,7 @@ const graph = createReactAgent({ name: "math_agent", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [addTool, subtractTool, multiplyTool, divideTool, powerTool, sqrtTool, factorialTool], @@ -117,7 +117,7 @@ const PROMPT = 'Calculate: (2^10 + sqrt(144)) / 4, then compute 5! and tell me the final answers.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/10-research-agent.ts b/examples/agents/langgraph/10-research-agent.ts index c7d80a35..2c18b176 100644 --- a/examples/agents/langgraph/10-research-agent.ts +++ b/examples/agents/langgraph/10-research-agent.ts @@ -107,7 +107,7 @@ const graph = createReactAgent({ name: "research_agent", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [searchTool, summarizeTool, citeSourceTool], @@ -118,7 +118,7 @@ const PROMPT = 'What are the latest developments in climate change research? Please search, summarize, and include citations.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/11-customer-support.ts b/examples/agents/langgraph/11-customer-support.ts index 19f7dd2e..0895566e 100644 --- a/examples/agents/langgraph/11-customer-support.ts +++ b/examples/agents/langgraph/11-customer-support.ts @@ -123,7 +123,7 @@ const graph = new StateGraph(SupportState) .addEdge('general', END) .compile({ name: "customer_support" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -133,7 +133,7 @@ const PROMPT = 'I was charged twice for my subscription this month and need a refund.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/12-code-agent.ts b/examples/agents/langgraph/12-code-agent.ts index 55ad9613..aa4ae57d 100644 --- a/examples/agents/langgraph/12-code-agent.ts +++ b/examples/agents/langgraph/12-code-agent.ts @@ -152,7 +152,7 @@ const graph = createReactAgent({ name: "code_agent", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools, @@ -163,7 +163,7 @@ const PROMPT = 'Write a binary search function in Python and explain how it works.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/13-multi-turn.ts b/examples/agents/langgraph/13-multi-turn.ts index eab4acaf..815d44a1 100644 --- a/examples/agents/langgraph/13-multi-turn.ts +++ b/examples/agents/langgraph/13-multi-turn.ts @@ -29,7 +29,7 @@ const graph = createReactAgent({ name: "interview_coach", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [], @@ -37,7 +37,7 @@ const graph = createReactAgent({ }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const SESSION_A = 'candidate-alice'; diff --git a/examples/agents/langgraph/14-qa-agent.ts b/examples/agents/langgraph/14-qa-agent.ts index 4cfb447f..aca7e00b 100644 --- a/examples/agents/langgraph/14-qa-agent.ts +++ b/examples/agents/langgraph/14-qa-agent.ts @@ -100,7 +100,7 @@ const graph = new StateGraph(QAState) .addEdge('generate', END) .compile({ name: "qa_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -109,7 +109,7 @@ const graph = new StateGraph(QAState) const PROMPT = 'What is Python and how many packages does it have?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/15-data-pipeline.ts b/examples/agents/langgraph/15-data-pipeline.ts index 0cfa224d..2a9b4169 100644 --- a/examples/agents/langgraph/15-data-pipeline.ts +++ b/examples/agents/langgraph/15-data-pipeline.ts @@ -122,7 +122,7 @@ const graph = new StateGraph(PipelineState) .addEdge('report_node', END) .compile({ name: "data_pipeline" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -131,7 +131,7 @@ const graph = new StateGraph(PipelineState) const PROMPT = 'sales'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/16-parallel-branches.ts b/examples/agents/langgraph/16-parallel-branches.ts index 2a512806..e71409f6 100644 --- a/examples/agents/langgraph/16-parallel-branches.ts +++ b/examples/agents/langgraph/16-parallel-branches.ts @@ -99,7 +99,7 @@ const graph = new StateGraph(ParallelState) .addEdge('merge', END) .compile({ name: "parallel_analysis" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -108,7 +108,7 @@ const graph = new StateGraph(ParallelState) const PROMPT = 'remote work for software engineers'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/17-error-recovery.ts b/examples/agents/langgraph/17-error-recovery.ts index 2c98da6c..a722a799 100644 --- a/examples/agents/langgraph/17-error-recovery.ts +++ b/examples/agents/langgraph/17-error-recovery.ts @@ -102,14 +102,14 @@ const graph = new StateGraph(RecoveryState) .addEdge('recover', END) .compile({ name: "error_recovery_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/18-tools-condition.ts b/examples/agents/langgraph/18-tools-condition.ts index 0b32b275..f8c79f79 100644 --- a/examples/agents/langgraph/18-tools-condition.ts +++ b/examples/agents/langgraph/18-tools-condition.ts @@ -75,7 +75,7 @@ const builder = new StateGraph(MessagesAnnotation) const graph = builder.compile({ name: "weather_timezone_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools, @@ -86,7 +86,7 @@ const PROMPT = "What's the weather like in Tokyo and London? Also what timezone are they in?"; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/19-document-analysis.ts b/examples/agents/langgraph/19-document-analysis.ts index 1e521ae4..772c69f6 100644 --- a/examples/agents/langgraph/19-document-analysis.ts +++ b/examples/agents/langgraph/19-document-analysis.ts @@ -198,7 +198,7 @@ const graph = createReactAgent({ name: "document_analysis_agent", }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools, @@ -209,7 +209,7 @@ const PROMPT = "Please provide a full analysis of the 'quarterly_report' document."; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/20-planner-agent.ts b/examples/agents/langgraph/20-planner-agent.ts index 1caac09e..8ee71c69 100644 --- a/examples/agents/langgraph/20-planner-agent.ts +++ b/examples/agents/langgraph/20-planner-agent.ts @@ -122,7 +122,7 @@ const graph = new StateGraph(PlannerState) .addEdge('review_node', END) .compile({ name: "planner_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -132,7 +132,7 @@ const PROMPT = 'Launch a new open-source Python library for data validation.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/21-subgraph.ts b/examples/agents/langgraph/21-subgraph.ts index 005337eb..327e0154 100644 --- a/examples/agents/langgraph/21-subgraph.ts +++ b/examples/agents/langgraph/21-subgraph.ts @@ -157,7 +157,7 @@ const graph = new StateGraph(DocumentState) .addEdge('build_report', END) .compile({ name: "document_pipeline_with_subgraph" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -170,7 +170,7 @@ const PROMPT = 'using simple Python functions.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/22-human-in-the-loop.ts b/examples/agents/langgraph/22-human-in-the-loop.ts index 4028cf47..3ed174bf 100644 --- a/examples/agents/langgraph/22-human-in-the-loop.ts +++ b/examples/agents/langgraph/22-human-in-the-loop.ts @@ -9,7 +9,7 @@ * * Note: In the TypeScript SDK the human_task decorator is not yet available. * This example simulates the human review step with a mock function that - * auto-approves. In production, this would integrate with the AgentSpan UI + * auto-approves. In production, this would integrate with the Conductor UI * or API for real human-in-the-loop review. */ @@ -67,7 +67,7 @@ function reviewEmail(state: State): Partial { * Simulated human review step. * * In production this would be a Conductor HUMAN task that pauses execution - * and waits for a human to approve or reject the draft via the AgentSpan + * and waits for a human to approve or reject the draft via the Conductor * UI or API. For this example we auto-approve. */ return { @@ -120,7 +120,7 @@ const graph = new StateGraph(EmailState) .addEdge('revise', END) .compile({ name: "email_hitl_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -130,7 +130,7 @@ const PROMPT = 'Schedule a team meeting for next Monday at 10am to discuss Q3 plans.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/23-retry-on-error.ts b/examples/agents/langgraph/23-retry-on-error.ts index bc426ceb..6bc89402 100644 --- a/examples/agents/langgraph/23-retry-on-error.ts +++ b/examples/agents/langgraph/23-retry-on-error.ts @@ -110,7 +110,7 @@ const graph = new StateGraph(RetryState) .addEdge('format', END) .compile({ name: "retry_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -119,7 +119,7 @@ const graph = new StateGraph(RetryState) const PROMPT = 'What is the speed of light in meters per second?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/24-map-reduce.ts b/examples/agents/langgraph/24-map-reduce.ts index 325895a5..86aadf4f 100644 --- a/examples/agents/langgraph/24-map-reduce.ts +++ b/examples/agents/langgraph/24-map-reduce.ts @@ -121,7 +121,7 @@ const graph = new StateGraph(OverallState) .addEdge('reduce', END) .compile({ name: "map_reduce_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -130,7 +130,7 @@ const graph = new StateGraph(OverallState) const PROMPT = 'renewable energy breakthroughs in 2024'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/25-supervisor.ts b/examples/agents/langgraph/25-supervisor.ts index da2c2234..4e813cf9 100644 --- a/examples/agents/langgraph/25-supervisor.ts +++ b/examples/agents/langgraph/25-supervisor.ts @@ -118,7 +118,7 @@ const graph = new StateGraph(SupervisorState) .addEdge('editor', 'supervisor') .compile({ name: "supervisor_multiagent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -127,7 +127,7 @@ const graph = new StateGraph(SupervisorState) const PROMPT = 'The impact of large language models on software development'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/26-agent-handoff.ts b/examples/agents/langgraph/26-agent-handoff.ts index 14aa3f44..9388f644 100644 --- a/examples/agents/langgraph/26-agent-handoff.ts +++ b/examples/agents/langgraph/26-agent-handoff.ts @@ -111,7 +111,7 @@ const graph = new StateGraph(HandoffState) .addEdge('general', END) .compile({ name: "agent_handoff" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -120,7 +120,7 @@ const graph = new StateGraph(HandoffState) const PROMPT = 'I was charged twice for my subscription this month.'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/27-persistent-memory.ts b/examples/agents/langgraph/27-persistent-memory.ts index 43b375f2..edf2548b 100644 --- a/examples/agents/langgraph/27-persistent-memory.ts +++ b/examples/agents/langgraph/27-persistent-memory.ts @@ -71,7 +71,7 @@ const graph = new StateGraph(MemoryState) .addEdge('chat', END) .compile({ checkpointer, name: "persistent_memory_chatbot" }); -// Add agentspan metadata for graph-structure extraction. +// Add Conductor metadata for graph-structure extraction. // Do NOT set tools on StateGraphs — only model + framework. (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', @@ -79,7 +79,7 @@ const graph = new StateGraph(MemoryState) }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/28-streaming-tokens.ts b/examples/agents/langgraph/28-streaming-tokens.ts index 410c1e81..c4da8ff2 100644 --- a/examples/agents/langgraph/28-streaming-tokens.ts +++ b/examples/agents/langgraph/28-streaming-tokens.ts @@ -51,7 +51,7 @@ const graph = new StateGraph(StreamState) .addEdge('generate', END) .compile({ name: "streaming_agent" }); -// Add agentspan metadata for graph-structure extraction. +// Add Conductor metadata for graph-structure extraction. // Do NOT set tools on StateGraphs — only model + framework. (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', diff --git a/examples/agents/langgraph/29-tool-categories.ts b/examples/agents/langgraph/29-tool-categories.ts index 2a33984e..80538c6b 100644 --- a/examples/agents/langgraph/29-tool-categories.ts +++ b/examples/agents/langgraph/29-tool-categories.ts @@ -166,7 +166,7 @@ const allTools = [ const graph = createReactAgent({ llm, tools: allTools, name: "tool_categories_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: allTools, @@ -176,7 +176,7 @@ const graph = createReactAgent({ llm, tools: allTools, name: "tool_categories_ag const PROMPT = 'What is the square root of 144?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/30-code-interpreter.ts b/examples/agents/langgraph/30-code-interpreter.ts index abe55a7e..40f4748e 100644 --- a/examples/agents/langgraph/30-code-interpreter.ts +++ b/examples/agents/langgraph/30-code-interpreter.ts @@ -108,7 +108,7 @@ const checkSyntaxTool = new DynamicStructuredTool({ const tools = [evaluateExpressionTool, explainCodeTool, checkSyntaxTool]; const graph = createReactAgent({ llm, tools, name: "code_interpreter_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools, @@ -118,7 +118,7 @@ const graph = createReactAgent({ llm, tools, name: "code_interpreter_agent" }); const PROMPT = 'Calculate (2**10 - 1) * 3 + 7'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/31-classify-and-route.ts b/examples/agents/langgraph/31-classify-and-route.ts index 5adab496..63b69917 100644 --- a/examples/agents/langgraph/31-classify-and-route.ts +++ b/examples/agents/langgraph/31-classify-and-route.ts @@ -129,7 +129,7 @@ const graph = new StateGraph(ClassifyState) .addEdge('cooking', END) .compile({ name: "classify_and_route_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -138,7 +138,7 @@ const graph = new StateGraph(ClassifyState) const PROMPT = 'What is photosynthesis?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/32-reflection-agent.ts b/examples/agents/langgraph/32-reflection-agent.ts index f6964e60..cf77d22e 100644 --- a/examples/agents/langgraph/32-reflection-agent.ts +++ b/examples/agents/langgraph/32-reflection-agent.ts @@ -112,7 +112,7 @@ const graph = new StateGraph(ReflectionState) .addEdge('finalize', END) .compile({ name: "reflection_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -121,7 +121,7 @@ const graph = new StateGraph(ReflectionState) const PROMPT = 'the importance of open-source software in modern technology'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/33-output-validator.ts b/examples/agents/langgraph/33-output-validator.ts index ea7ad653..34190641 100644 --- a/examples/agents/langgraph/33-output-validator.ts +++ b/examples/agents/langgraph/33-output-validator.ts @@ -141,7 +141,7 @@ const graph = new StateGraph(ValidatorState) .addEdge('finalize', END) .compile({ name: "output_validator_agent" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -150,7 +150,7 @@ const graph = new StateGraph(ValidatorState) const PROMPT = 'Create a fictional software engineer from Japan'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/34-rag-pipeline.ts b/examples/agents/langgraph/34-rag-pipeline.ts index c36c1c06..ce8caecc 100644 --- a/examples/agents/langgraph/34-rag-pipeline.ts +++ b/examples/agents/langgraph/34-rag-pipeline.ts @@ -44,10 +44,10 @@ const DOCUMENTS: Document[] = [ }, { pageContent: - 'Agentspan provides a runtime for deploying LangGraph and LangChain agents at scale. ' + + 'Conductor provides a runtime for deploying LangGraph and LangChain agents at scale. ' + 'It uses Conductor as an orchestration engine and exposes agents as Conductor tasks. ' + 'The AgentRuntime class handles worker registration and lifecycle management.', - metadata: { source: 'agentspan_docs', topic: 'agentspan' }, + metadata: { source: 'conductor_docs', topic: 'Conductor' }, }, { pageContent: @@ -186,7 +186,7 @@ const graph = new StateGraph(RAGState) .addEdge('generate', END) .compile({ name: "rag_pipeline" }); -// Add agentspan metadata for extraction +// Add Conductor metadata for extraction (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', framework: 'langgraph', @@ -195,7 +195,7 @@ const graph = new StateGraph(RAGState) const PROMPT = 'What is LangGraph and how does it differ from LangChain?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/35-conversation-manager.ts b/examples/agents/langgraph/35-conversation-manager.ts index f25ac3a9..8b8b0e46 100644 --- a/examples/agents/langgraph/35-conversation-manager.ts +++ b/examples/agents/langgraph/35-conversation-manager.ts @@ -122,7 +122,7 @@ const graph = new StateGraph(ConversationState) }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const turns = [ diff --git a/examples/agents/langgraph/36-debate-agents.ts b/examples/agents/langgraph/36-debate-agents.ts index 52bca9ba..bf293969 100644 --- a/examples/agents/langgraph/36-debate-agents.ts +++ b/examples/agents/langgraph/36-debate-agents.ts @@ -137,7 +137,7 @@ const graph = new StateGraph(DebateState) }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/37-document-grader.ts b/examples/agents/langgraph/37-document-grader.ts index d0aafa66..a1a9f32b 100644 --- a/examples/agents/langgraph/37-document-grader.ts +++ b/examples/agents/langgraph/37-document-grader.ts @@ -163,7 +163,7 @@ const graph = new StateGraph(GraderState) }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/38-state-machine.ts b/examples/agents/langgraph/38-state-machine.ts index 96ffa934..b1e67703 100644 --- a/examples/agents/langgraph/38-state-machine.ts +++ b/examples/agents/langgraph/38-state-machine.ts @@ -174,7 +174,7 @@ const graph = new StateGraph(OrderState) }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/39-tool-call-chain.ts b/examples/agents/langgraph/39-tool-call-chain.ts index eb70b2d5..81e93ca2 100644 --- a/examples/agents/langgraph/39-tool-call-chain.ts +++ b/examples/agents/langgraph/39-tool-call-chain.ts @@ -133,7 +133,7 @@ const graph = builder.compile({ name: "tool_call_chain_agent" }); }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/40-agent-as-tool.ts b/examples/agents/langgraph/40-agent-as-tool.ts index f1b29237..4d3653bb 100644 --- a/examples/agents/langgraph/40-agent-as-tool.ts +++ b/examples/agents/langgraph/40-agent-as-tool.ts @@ -128,7 +128,7 @@ const graph = orchBuilder.compile({ name: "orchestrator_with_subagents" }); }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const queries = [ diff --git a/examples/agents/langgraph/41-react-agent-basic.ts b/examples/agents/langgraph/41-react-agent-basic.ts index 0dd689b5..027b89bf 100644 --- a/examples/agents/langgraph/41-react-agent-basic.ts +++ b/examples/agents/langgraph/41-react-agent-basic.ts @@ -3,8 +3,8 @@ * * Demonstrates: * - Using createReactAgent from @langchain/langgraph/prebuilt directly with AgentRuntime - * - No Agentspan wrapper needed -- pass the graph straight to runtime.run() - * - Agentspan detects the ReAct structure and runs LLM + tools on Conductor + * - No Conductor wrapper needed -- pass the graph straight to runtime.run() + * - Conductor detects the ReAct structure and runs LLM + tools on Conductor */ import { createReactAgent } from '@langchain/langgraph/prebuilt'; @@ -78,10 +78,10 @@ const graph = createReactAgent({ llm, tools, name: "math_and_text_agent" }); const PROMPT = 'What is sqrt(256) + 2**10? ' + "Also count the words in 'the quick brown fox jumps over the lazy dog'. " + - "And what is 'Agentspan' reversed?"; + "And what is 'Conductor' reversed?"; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/42-react-agent-system-prompt.ts b/examples/agents/langgraph/42-react-agent-system-prompt.ts index de4886bd..acc3306c 100644 --- a/examples/agents/langgraph/42-react-agent-system-prompt.ts +++ b/examples/agents/langgraph/42-react-agent-system-prompt.ts @@ -3,7 +3,7 @@ * * Demonstrates: * - Passing a system prompt via the prompt parameter - * - Agentspan extracts the system prompt and forwards it to the server + * - Conductor extracts the system prompt and forwards it to the server * - Custom persona carried through the full Conductor execution */ @@ -97,7 +97,7 @@ const PROMPT = 'How many yen will I get? The flight is 9,540 km — how far is that in miles?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/43-react-agent-multi-model.ts b/examples/agents/langgraph/43-react-agent-multi-model.ts index 6ab351e3..3adbe5b2 100644 --- a/examples/agents/langgraph/43-react-agent-multi-model.ts +++ b/examples/agents/langgraph/43-react-agent-multi-model.ts @@ -4,7 +4,7 @@ * Demonstrates: * - createReactAgent with a different model (still using ChatOpenAI for TS examples) * - Date-related tools for practical utility - * - Same code pattern, swappable model -- no Agentspan-specific changes needed + * - Same code pattern, swappable model -- no Conductor-specific changes needed * * Note: The Python version uses ChatAnthropic. This TypeScript port uses ChatOpenAI * with gpt-4o-mini since @langchain/anthropic may not be installed. The pattern @@ -85,7 +85,7 @@ const PROMPT = 'What day of the week will that be?'; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/44-context-condensation.ts b/examples/agents/langgraph/44-context-condensation.ts index 63dd1d93..92236f16 100644 --- a/examples/agents/langgraph/44-context-condensation.ts +++ b/examples/agents/langgraph/44-context-condensation.ts @@ -383,11 +383,11 @@ const graph = orchBuilder.compile({ name: "research_orchestrator" }); }; // --------------------------------------------------------------------------- -// Run on agentspan +// Run on Conductor // --------------------------------------------------------------------------- async function main() { console.log('Starting context condensation stress test (LangGraph / TypeScript).'); - console.log("Watch the Agentspan server logs for 'Condensed conversation' entries."); + console.log("Watch the Conductor server logs for 'Condensed conversation' entries."); console.log(); const runtime = new AgentRuntime(); diff --git a/examples/agents/langgraph/45-advanced-orchestration.ts b/examples/agents/langgraph/45-advanced-orchestration.ts index 2a9d315e..25455f92 100644 --- a/examples/agents/langgraph/45-advanced-orchestration.ts +++ b/examples/agents/langgraph/45-advanced-orchestration.ts @@ -200,7 +200,7 @@ async function runOrchestrationAgent(prompt: string): Promise { return 'Agent reached maximum iterations.'; } -// ── Wrap for Agentspan ─────────────────────────────────── +// ── Wrap for Conductor ─────────────────────────────────── const agentRunnable = new RunnableLambda({ func: async (input: { input: string }) => { diff --git a/examples/agents/langgraph/46-crash-and-resume.ts b/examples/agents/langgraph/46-crash-and-resume.ts index 3667326c..9a26969c 100644 --- a/examples/agents/langgraph/46-crash-and-resume.ts +++ b/examples/agents/langgraph/46-crash-and-resume.ts @@ -20,7 +20,7 @@ * resume logic, no execution_id needed. * * Why this matters: - * LangGraph graphs running through Agentspan are compiled into durable + * LangGraph graphs running through Conductor are compiled into durable * Conductor workflows. If your process crashes (OOM, deploy, exception), * no work is lost — the server holds the workflow state. You just need * to restart serve() and the workers pick up from where they left off. @@ -36,7 +36,7 @@ * await runtime.start(graph, "prompt"); // or via server API / UI * * Requirements: - * - AGENTSPAN_SERVER_URL=http://localhost:8080/api + * - CONDUCTOR_AGENT_SERVER_URL=http://localhost:8080/api * - OPENAI_API_KEY for ChatOpenAI */ @@ -48,8 +48,8 @@ import { AgentRuntime } from '@io-orkes/conductor-javascript/agents'; import * as fs from 'node:fs'; import * as readline from 'node:readline'; -const SESSION_FILE = '/tmp/agentspan_langgraph_resume.session'; -const SERVER_URL = process.env.AGENTSPAN_SERVER_URL ?? 'http://localhost:8080/api'; +const SESSION_FILE = '/tmp/conductor_langgraph_resume.session'; +const SERVER_URL = process.env.CONDUCTOR_AGENT_SERVER_URL ?? 'http://localhost:8080/api'; const UI_BASE = SERVER_URL.replace('/api', ''); function sleep(ms: number): Promise { @@ -176,7 +176,7 @@ async function main() { const uiLink = `${UI_BASE}/execution/${savedExecutionId}`; console.log('-'.repeat(60)); - console.log('Open the Agentspan UI to see the execution in RUNNING state:'); + console.log('Open the Conductor UI to see the execution in RUNNING state:'); console.log(` ${uiLink}`); console.log(); console.log('The workflow is alive on the server but stalled — no workers are'); diff --git a/examples/agents/langgraph/README.md b/examples/agents/langgraph/README.md index 88d103ac..ee9c6d40 100644 --- a/examples/agents/langgraph/README.md +++ b/examples/agents/langgraph/README.md @@ -1,11 +1,11 @@ -# LangGraph + Agentspan +# LangGraph + Conductor -Keep your existing LangGraph code. Add agentspan metadata and run with `runtime.run()`. +Keep your existing LangGraph code. Add Conductor metadata and run with `runtime.run()`. ## createReactAgent - +
Before (vanilla LangGraph)After (Agentspan)
Before (vanilla LangGraph)After (Conductor)
```typescript @@ -82,7 +82,7 @@ const graph = createReactAgent({ tools: [calculate], }); -// Add agentspan metadata +// Add Conductor metadata (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [calculate], @@ -106,7 +106,7 @@ await runtime.shutdown(); Same pattern — build the graph normally, attach metadata, run with `runtime.run()`. - +
Before (vanilla LangGraph)After (Agentspan)
Before (vanilla LangGraph)After (Conductor)
```typescript @@ -159,7 +159,7 @@ const graph = new StateGraph(State) .addEdge('process', END) .compile(); -// Add agentspan metadata +// Add Conductor metadata (graph as any)._agentspan = { model: 'anthropic/claude-sonnet-4-6', tools: [], @@ -205,7 +205,7 @@ await runtime.shutdown(); ## Running ```bash -export AGENTSPAN_SERVER_URL=... +export CONDUCTOR_AGENT_SERVER_URL=... export OPENAI_API_KEY=... # from the repository root npx tsx examples/agents/langgraph/01-hello-world.ts diff --git a/examples/agents/langgraph/package.json b/examples/agents/langgraph/package.json index 5a41d1cf..7b18c3c0 100644 --- a/examples/agents/langgraph/package.json +++ b/examples/agents/langgraph/package.json @@ -1,5 +1,5 @@ { - "name": "agentspan-examples-langgraph", + "name": "conductor-examples-langgraph", "private": true, "type": "module", "dependencies": { diff --git a/examples/agents/openai/01-basic-agent.ts b/examples/agents/openai/01-basic-agent.ts index 26680002..c4f077e1 100644 --- a/examples/agents/openai/01-basic-agent.ts +++ b/examples/agents/openai/01-basic-agent.ts @@ -1,15 +1,12 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * Basic OpenAI Agent -- simplest possible agent with no tools. * * Demonstrates: * - Defining an agent using the real @openai/agents SDK - * - Running it via Agentspan passthrough (AgentRuntime) + * - Running it via Conductor passthrough (AgentRuntime) * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, setTracingDisabled } from '@openai/agents'; @@ -26,7 +23,7 @@ export const agent = new Agent({ const prompt = 'Say hello and tell me a fun fact about the TypeScript programming language.'; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/02-function-tools.ts b/examples/agents/openai/02-function-tools.ts index c1c71c99..cde5bd71 100644 --- a/examples/agents/openai/02-function-tools.ts +++ b/examples/agents/openai/02-function-tools.ts @@ -1,16 +1,13 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent with Function Tools -- tool calling via the `tool()` helper. * * Demonstrates: * - Defining function tools with zod schemas * - Multiple tools with typed parameters - * - Running via Agentspan passthrough + * - Running via Conductor passthrough * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, tool, setTracingDisabled } from '@openai/agents'; @@ -82,7 +79,7 @@ const prompt = "What's the weather in San Francisco? Also, what's the population there " + "and what's the square root of that number (just the digits)?"; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/03-structured-output.ts b/examples/agents/openai/03-structured-output.ts index f1428530..b39dc01d 100644 --- a/examples/agents/openai/03-structured-output.ts +++ b/examples/agents/openai/03-structured-output.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent with Structured Output -- enforced JSON schema response. * @@ -10,7 +7,7 @@ * - Model settings (temperature) for deterministic output * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, setTracingDisabled } from '@openai/agents'; @@ -51,7 +48,7 @@ export const agent = new Agent({ const prompt = 'Recommend 3 sci-fi movies that explore the concept of artificial intelligence.'; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/04-handoffs.ts b/examples/agents/openai/04-handoffs.ts index c42b0040..a7556639 100644 --- a/examples/agents/openai/04-handoffs.ts +++ b/examples/agents/openai/04-handoffs.ts @@ -1,16 +1,13 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent Handoffs -- multi-agent orchestration with handoffs. * * Demonstrates: * - Defining specialist agents with tools * - A triage agent that routes to the correct specialist via handoffs - * - Running via Agentspan passthrough + * - Running via Conductor passthrough * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, tool, setTracingDisabled } from '@openai/agents'; @@ -107,7 +104,7 @@ export const triageAgent = new Agent({ const prompt = "I'd like a refund for order ORD-002, the product arrived damaged."; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/05-guardrails.ts b/examples/agents/openai/05-guardrails.ts index d8e7075f..5f737445 100644 --- a/examples/agents/openai/05-guardrails.ts +++ b/examples/agents/openai/05-guardrails.ts @@ -1,16 +1,13 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent with Guardrails -- input and output validation. * * Demonstrates: * - Input guardrails that validate user messages before processing * - Output guardrails that validate agent responses - * - Running via Agentspan passthrough + * - Running via Conductor passthrough * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { @@ -126,7 +123,7 @@ export const agent = new Agent({ outputGuardrails: [checkOutputSafety], }); -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/06-model-settings.ts b/examples/agents/openai/06-model-settings.ts index 4d8d92d6..0f47c455 100644 --- a/examples/agents/openai/06-model-settings.ts +++ b/examples/agents/openai/06-model-settings.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent with Model Settings -- temperature, max tokens, and more. * @@ -10,7 +7,7 @@ * - High temperature for creative responses * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, setTracingDisabled } from '@openai/agents'; @@ -46,7 +43,7 @@ export const preciseAgent = new Agent({ }, }); -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/07-streaming.ts b/examples/agents/openai/07-streaming.ts index a6507a27..1ebfd8d9 100644 --- a/examples/agents/openai/07-streaming.ts +++ b/examples/agents/openai/07-streaming.ts @@ -1,15 +1,12 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent with Streaming -- real-time event streaming. * * Demonstrates: * - An OpenAI agent with tools - * - Running via Agentspan passthrough + * - Running via Conductor passthrough * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { @@ -58,7 +55,7 @@ export const agent = new Agent({ const prompt = "What's your return policy for electronics?"; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/08-agent-as-tool.ts b/examples/agents/openai/08-agent-as-tool.ts index 64b8774f..2699c33c 100644 --- a/examples/agents/openai/08-agent-as-tool.ts +++ b/examples/agents/openai/08-agent-as-tool.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent -- Manager Pattern with agents-as-tools. * @@ -10,7 +7,7 @@ * - Differs from handoffs: manager retains control and synthesizes results * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, tool, setTracingDisabled } from '@openai/agents'; @@ -107,7 +104,7 @@ const prompt = "and the battery life is wonderful. However, the keyboard feels terrible " + "and the trackpad is the worst I've used.'"; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/09-dynamic-instructions.ts b/examples/agents/openai/09-dynamic-instructions.ts index 576584d1..c7bea44e 100644 --- a/examples/agents/openai/09-dynamic-instructions.ts +++ b/examples/agents/openai/09-dynamic-instructions.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent with Dynamic Instructions -- callable instruction function. * @@ -10,7 +7,7 @@ * - Function tools alongside dynamic instructions * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, tool, setTracingDisabled } from '@openai/agents'; @@ -91,7 +88,7 @@ export const agent = new Agent({ const prompt = "Show me my todo list and add 'Prepare demo for Friday' as high priority."; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/10-multi-model.ts b/examples/agents/openai/10-multi-model.ts index d0af022f..c6489ed1 100644 --- a/examples/agents/openai/10-multi-model.ts +++ b/examples/agents/openai/10-multi-model.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * OpenAI Agent -- Multi-Model Handoff with different LLMs. * @@ -10,7 +7,7 @@ * - Model override for cost/performance optimization * * Requirements: - * - AGENTSPAN_SERVER_URL for the Agentspan path + * - CONDUCTOR_AGENT_SERVER_URL for the Conductor path */ import { Agent, tool, setTracingDisabled } from '@openai/agents'; @@ -110,7 +107,7 @@ triage.handoffs = [docSpecialist, codeSpecialist]; const prompt = 'I need a Python code example for authenticating with the API.'; -// ── Run on agentspan ────────────────────────────────────────────── +// ── Run on Conductor ────────────────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/openai/README.md b/examples/agents/openai/README.md index 98d98878..257f30b0 100644 --- a/examples/agents/openai/README.md +++ b/examples/agents/openai/README.md @@ -1,11 +1,11 @@ -# OpenAI Agents SDK + Agentspan +# OpenAI Agents SDK + Conductor The OpenAI Agent format is natively recognized. Swap `run()` for `runtime.run()` — agent and tools stay identical. ## Before / After - +
Before (vanilla OpenAI Agents)After (Agentspan)
Before (vanilla OpenAI Agents)After (Conductor)
```typescript @@ -110,7 +110,7 @@ await runtime.shutdown(); ## Running ```bash -export AGENTSPAN_SERVER_URL=... +export CONDUCTOR_AGENT_SERVER_URL=... export OPENAI_API_KEY=... # from the repository root npx tsx examples/agents/openai/01-basic-agent.ts diff --git a/examples/agents/openai/package.json b/examples/agents/openai/package.json index dd84ffa3..931b0eeb 100644 --- a/examples/agents/openai/package.json +++ b/examples/agents/openai/package.json @@ -1,5 +1,5 @@ { - "name": "agentspan-examples-openai", + "name": "conductor-examples-openai", "private": true, "type": "module", "dependencies": { diff --git a/examples/agents/quickstart/01-basic-agent.ts b/examples/agents/quickstart/01-basic-agent.ts index de6d93e6..6c45d70b 100644 --- a/examples/agents/quickstart/01-basic-agent.ts +++ b/examples/agents/quickstart/01-basic-agent.ts @@ -1,5 +1,5 @@ /** - * Basic agent — the simplest possible agentspan example. + * Basic agent — the simplest possible Conductor example. */ import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; diff --git a/examples/agents/quickstart/run-all.ts b/examples/agents/quickstart/run-all.ts index 3da2dbfa..9bc01fcb 100644 --- a/examples/agents/quickstart/run-all.ts +++ b/examples/agents/quickstart/run-all.ts @@ -42,7 +42,7 @@ const tests: TestCase[] = [ // ── Helpers ───────────────────────────────────────────── -const serverUrl = process.env.AGENTSPAN_SERVER_URL ?? 'http://localhost:8080/api'; +const serverUrl = process.env.CONDUCTOR_AGENT_SERVER_URL ?? 'http://localhost:8080/api'; async function fetchExecutionTasks(executionId: string): Promise { const url = `${serverUrl}/agent/executions/${executionId}/full`; @@ -51,7 +51,7 @@ async function fetchExecutionTasks(executionId: string): Promise { return res.json(); } -// System task types managed by Conductor/AgentSpan — everything else is a tool worker task +// System task types managed by Conductor — everything else is a tool worker task const SYSTEM_TASK_TYPES = new Set([ 'LLM_CHAT_COMPLETE', 'SET_VARIABLE', 'DO_WHILE', 'SWITCH', 'FORK', 'JOIN', 'INLINE', 'SUB_WORKFLOW', 'HUMAN', 'TERMINATE', 'WAIT', 'EVENT', diff --git a/examples/agents/settings.ts b/examples/agents/settings.ts index 2a9b94ca..b8b468bb 100644 --- a/examples/agents/settings.ts +++ b/examples/agents/settings.ts @@ -1,3 +1,3 @@ -const llmModel = process.env.AGENTSPAN_LLM_MODEL ?? 'openai/gpt-4o-mini'; -const secondaryLlmModel = process.env.AGENTSPAN_SECONDARY_LLM_MODEL ?? 'openai/gpt-4o'; +const llmModel = process.env.CONDUCTOR_AGENT_LLM_MODEL ?? 'openai/gpt-4o-mini'; +const secondaryLlmModel = process.env.CONDUCTOR_AGENT_SECONDARY_LLM_MODEL ?? 'openai/gpt-4o'; export { llmModel, secondaryLlmModel }; diff --git a/examples/agents/vercel-ai/01-basic-agent.ts b/examples/agents/vercel-ai/01-basic-agent.ts index 91b409a3..cc464db7 100644 --- a/examples/agents/vercel-ai/01-basic-agent.ts +++ b/examples/agents/vercel-ai/01-basic-agent.ts @@ -1,9 +1,9 @@ /** * Vercel AI SDK Tools + Native Agent -- Basic Agent * - * Demonstrates using Vercel AI SDK tool() objects with a native agentspan Agent. + * Demonstrates using Vercel AI SDK tool() objects with a native Conductor Agent. * The superset tool system auto-detects AI SDK tool format (Zod parameters + execute) - * and converts them to agentspan ToolDefs transparently. + * and converts them to Conductor ToolDefs transparently. * * No duck-typed wrappers or passthrough needed -- just native Agent with AI SDK tools. */ @@ -23,7 +23,7 @@ const weatherTool = aiTool({ }), }); -// ── Native agentspan Agent with AI SDK tool ───────────── +// ── Native Conductor Agent with AI SDK tool ───────────── export const agent = new Agent({ name: 'weather_agent', model: 'anthropic/claude-sonnet-4-6', @@ -33,7 +33,7 @@ export const agent = new Agent({ const prompt = 'What is the weather in San Francisco?'; -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/02-tools-compat.ts b/examples/agents/vercel-ai/02-tools-compat.ts index e0c9ce97..88652067 100644 --- a/examples/agents/vercel-ai/02-tools-compat.ts +++ b/examples/agents/vercel-ai/02-tools-compat.ts @@ -1,7 +1,7 @@ /** * Vercel AI SDK Tools + Native Agent -- Tool Compatibility * - * Demonstrates mixing Vercel AI SDK tool() with agentspan native tool() + * Demonstrates mixing Vercel AI SDK tool() with Conductor native tool() * in the same native Agent. The superset tool system normalizes both formats * to ToolDef automatically -- they work side by side without any conversion. */ @@ -11,18 +11,18 @@ import { z } from 'zod'; import { Agent, AgentRuntime, - tool as agentspanTool, + tool as conductorAgentTool, getToolDef, } from '@io-orkes/conductor-javascript/agents'; -// ── Agentspan native tool ──────────────────────────────── -export const nativeSearchTool = agentspanTool( +// ── Conductor native tool ──────────────────────────────── +export const nativeSearchTool = conductorAgentTool( async (args: { query: string }) => ({ results: [`Result for: ${args.query}`], }), { name: 'native_search', - description: 'Search using agentspan native tool format.', + description: 'Search using Conductor native tool format.', inputSchema: z.object({ query: z.string().describe('Search query'), }), @@ -55,7 +55,7 @@ export const agent = new Agent({ const prompt = 'Search for quantum computing and also calculate 2 + 2.'; -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/03-streaming.ts b/examples/agents/vercel-ai/03-streaming.ts index c0cdb19b..7539493c 100644 --- a/examples/agents/vercel-ai/03-streaming.ts +++ b/examples/agents/vercel-ai/03-streaming.ts @@ -30,7 +30,7 @@ export const agent = new Agent({ const prompt = 'Explain quantum computing in one paragraph, then tell me the weather in San Francisco.'; -// ── Stream on agentspan ────────────────────────────────── +// ── Stream on Conductor ────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/04-structured-output.ts b/examples/agents/vercel-ai/04-structured-output.ts index d5eeaebc..64df36d4 100644 --- a/examples/agents/vercel-ai/04-structured-output.ts +++ b/examples/agents/vercel-ai/04-structured-output.ts @@ -2,7 +2,7 @@ * Vercel AI SDK Tools + Native Agent -- Structured Output * * Demonstrates typed structured output using a Zod schema as the Agent's outputType. - * The agentspan runtime sends the schema to the server, which constrains the LLM + * The Conductor runtime sends the schema to the server, which constrains the LLM * to produce valid JSON matching the schema. */ @@ -29,7 +29,7 @@ export const agent = new Agent({ const prompt = 'Generate a profile for a fictional ML engineer from Japan.'; -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/05-multi-step.ts b/examples/agents/vercel-ai/05-multi-step.ts index 02601bac..9073a373 100644 --- a/examples/agents/vercel-ai/05-multi-step.ts +++ b/examples/agents/vercel-ai/05-multi-step.ts @@ -1,7 +1,7 @@ /** * Vercel AI SDK Tools + Native Agent -- Multi-Step * - * Demonstrates a native agentspan Agent with multiple AI SDK tools and maxTurns. + * Demonstrates a native Conductor Agent with multiple AI SDK tools and maxTurns. * The agent calls tools iteratively until it has enough information to produce * a final answer. maxTurns controls the maximum number of LLM turns. */ @@ -56,7 +56,7 @@ export const agent = new Agent({ const prompt = 'What is the current weather and time in San Francisco and Tokyo?'; -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/06-middleware.ts b/examples/agents/vercel-ai/06-middleware.ts index c9aeabe7..dc4f2619 100644 --- a/examples/agents/vercel-ai/06-middleware.ts +++ b/examples/agents/vercel-ai/06-middleware.ts @@ -1,7 +1,7 @@ /** * Vercel AI SDK Tools + Native Agent -- Guardrails (Middleware equivalent) * - * Demonstrates agentspan's guardrail system as the native equivalent of + * Demonstrates Conductor's guardrail system as the native equivalent of * Vercel AI SDK middleware. Guardrails validate input/output and can block, * retry, or fix content -- applied declaratively on the Agent. * @@ -73,7 +73,7 @@ export const agent = new Agent({ guardrails: [piiGuardrail, outputLogGuardrail], }); -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/07-stop-conditions.ts b/examples/agents/vercel-ai/07-stop-conditions.ts index e7b69eda..d94340ad 100644 --- a/examples/agents/vercel-ai/07-stop-conditions.ts +++ b/examples/agents/vercel-ai/07-stop-conditions.ts @@ -1,7 +1,7 @@ /** * Vercel AI SDK Tools + Native Agent -- Termination Conditions * - * Demonstrates agentspan's termination condition system on a native Agent + * Demonstrates Conductor's termination condition system on a native Agent * with AI SDK tools. Termination conditions control when the agent stops: * - MaxMessage: stop after N messages * - TextMention: stop when output contains specific text @@ -66,7 +66,7 @@ export const agent = new Agent({ const prompt = 'Analyze market trends for AI infrastructure companies. Look at revenue growth, adoption rates, and competitive landscape, then summarize.'; -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { analysisStepCount = 0; const runtime = new AgentRuntime(); diff --git a/examples/agents/vercel-ai/08-agent-handoff.ts b/examples/agents/vercel-ai/08-agent-handoff.ts index 55a6a1f1..0010fbfe 100644 --- a/examples/agents/vercel-ai/08-agent-handoff.ts +++ b/examples/agents/vercel-ai/08-agent-handoff.ts @@ -73,7 +73,7 @@ const queries = [ 'What is the weather like today?', ]; -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/09-credentials.ts b/examples/agents/vercel-ai/09-credentials.ts index 27facb80..0fe19cc9 100644 --- a/examples/agents/vercel-ai/09-credentials.ts +++ b/examples/agents/vercel-ai/09-credentials.ts @@ -1,8 +1,8 @@ /** * Vercel AI SDK Tools + Native Agent -- Credentials * - * Demonstrates agentspan's credential system with AI SDK tools on a native Agent. - * Credentials are declared on the Agent and resolved by the agentspan server + * Demonstrates Conductor's credential system with AI SDK tools on a native Agent. + * Credentials are declared on the Agent and resolved by the Conductor server * before tool execution -- the tool receives credentials via environment injection. */ @@ -17,7 +17,7 @@ const fetchReport = aiTool({ reportId: z.string().describe('Report ID to fetch'), }), execute: async ({ reportId }) => { - // In production, the agentspan server injects ANALYTICS_API_KEY + // In production, the Conductor server injects ANALYTICS_API_KEY // into the environment before this tool executes. const apiKey = process.env.ANALYTICS_API_KEY ?? 'demo-key'; return { @@ -37,7 +37,7 @@ export const agent = new Agent({ 'Use the fetchReport tool to retrieve data when asked.', tools: [fetchReport], credentials: [ - // Credential references resolved by the agentspan server. + // Credential references resolved by the Conductor server. // In production, these are stored in the credential vault. 'ANALYTICS_API_KEY', ], @@ -45,7 +45,7 @@ export const agent = new Agent({ const prompt = 'Fetch the analytics report with ID RPT-2024-Q4.'; -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── async function main() { const runtime = new AgentRuntime(); try { diff --git a/examples/agents/vercel-ai/10-hitl.ts b/examples/agents/vercel-ai/10-hitl.ts index 46c1df8f..2ca641b0 100644 --- a/examples/agents/vercel-ai/10-hitl.ts +++ b/examples/agents/vercel-ai/10-hitl.ts @@ -1,13 +1,13 @@ /** * Vercel AI SDK Tools + Native Agent -- Human-in-the-Loop (HITL) * - * Demonstrates approval_required on tools with a native agentspan Agent. + * Demonstrates approval_required on tools with a native Conductor Agent. * When a tool has approvalRequired: true, the agent pauses for human approval * before executing the tool. Uses interactive streaming with schema-driven * console prompts to handle the HITL pause. * * This example mixes Vercel AI SDK tool() (for risk assessment, auto-execute) - * and agentspan native tool() (for action execution, requires approval). + * and Conductor native tool() (for action execution, requires approval). */ import * as readline from 'node:readline/promises'; @@ -17,7 +17,7 @@ import { z } from 'zod'; import { Agent, AgentRuntime, - tool as agentspanTool, + tool as conductorAgentTool, } from '@io-orkes/conductor-javascript/agents'; // ── Risk assessment tool (AI SDK, auto-execute) ────────── @@ -41,8 +41,8 @@ const assessRisk = aiTool({ }, }); -// ── Execution tool (agentspan native, requires approval) ─ -const executeAction = agentspanTool( +// ── Execution tool (Conductor native, requires approval) ─ +const executeAction = conductorAgentTool( async (args: { action: string }) => ({ status: 'completed', message: `Action "${args.action}" executed successfully.`, @@ -92,7 +92,7 @@ async function promptHuman( return response; } -// ── Run on agentspan ───────────────────────────────────── +// ── Run on Conductor ───────────────────────────────────── const rl = readline.createInterface({ input: stdin, output: stdout }); const runtime = new AgentRuntime(); diff --git a/examples/agents/vercel-ai/README.md b/examples/agents/vercel-ai/README.md index ccaba243..ed4746ea 100644 --- a/examples/agents/vercel-ai/README.md +++ b/examples/agents/vercel-ai/README.md @@ -1,4 +1,4 @@ -# Vercel AI SDK + Agentspan +# Vercel AI SDK + Conductor Two ways to integrate — pick what fits your stage. @@ -7,7 +7,7 @@ Two ways to integrate — pick what fits your stage. Swap one import. Your `generateText()` code stays identical. - +
Before (vanilla Vercel AI)After (Agentspan)
Before (vanilla Vercel AI)After (Conductor)
```typescript @@ -65,14 +65,14 @@ console.log(result.text);
-Everything else — tools, model, prompt, result shape — is unchanged. Under the hood, `generateText` builds a Agentspan `Agent`, runs it on the platform, and maps the result back to the AI SDK format. +Everything else — tools, model, prompt, result shape — is unchanged. Under the hood, `generateText` builds a Conductor `Agent`, runs it on the platform, and maps the result back to the AI SDK format. ## Production: Agent API When you need features that `generateText()` can't express — termination conditions, guardrails, multi-agent handoff, human-in-the-loop — use the Agent API directly. - +
Before (vanilla Vercel AI)After (Agentspan Agent API)
Before (vanilla Vercel AI)After (Conductor Agent API)
```typescript @@ -110,7 +110,7 @@ import { tool as aiTool } from 'ai'; import { z } from 'zod'; import { Agent, AgentRuntime } from '@io-orkes/conductor-javascript/agents'; // ^^^^^ ^^^^^^^^^^^^ -// agentspan Agent + Runtime +// Conductor Agent + Runtime const weatherTool = aiTool({ description: 'Get weather for a city', @@ -155,7 +155,7 @@ await runtime.shutdown(); | File | Description | |------|-------------| | `01-basic-agent.ts` | Simple agent with one AI SDK tool | -| `02-tools-compat.ts` | Mix of Agentspan native and AI SDK tools | +| `02-tools-compat.ts` | Mix of Conductor native and AI SDK tools | | `03-streaming.ts` | Default `runtime.run()` flow with a commented `runtime.stream()` alternative | | `04-structured-output.ts` | Zod schema for typed output | | `05-multi-step.ts` | Multiple tools, multi-turn conversation | @@ -168,7 +168,7 @@ await runtime.shutdown(); ## Running ```bash -export AGENTSPAN_SERVER_URL=... +export CONDUCTOR_AGENT_SERVER_URL=... export OPENAI_API_KEY=... # from the repository root npx tsx examples/agents/vercel-ai/01-basic-agent.ts diff --git a/examples/agents/vercel-ai/package.json b/examples/agents/vercel-ai/package.json index b86ec0c6..ae66903b 100644 --- a/examples/agents/vercel-ai/package.json +++ b/examples/agents/vercel-ai/package.json @@ -1,5 +1,5 @@ { - "name": "agentspan-examples-vercel-ai", + "name": "conductor-examples-vercel-ai", "private": true, "type": "module", "dependencies": { diff --git a/jest.e2e.config.mjs b/jest.e2e.config.mjs index 7ef48c0b..49d3d93f 100644 --- a/jest.e2e.config.mjs +++ b/jest.e2e.config.mjs @@ -1,7 +1,7 @@ import baseConfig from "./jest.config.mjs"; /** - * Agent e2e suites (repo-root e2e/) against a live agentspan server. + * Agent e2e suites (repo-root e2e/) against a live Conductor server. * Run with: npm run test:agent-e2e * Not matched by test/test:unit/test:integration globs — per-PR unit CI cost * is unchanged; the agent-e2e workflow runs these against the release JAR. diff --git a/package-lock.json b/package-lock.json index fa493334..411adf73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@tsconfig/node24": "^24.0.4", "@types/node": "^22.0.0", "@types/uuid": "^9.0.1", + "ajv": "^8.17.1", "cross-env": "^10.1.0", "eslint": "^9.34.0", "jest": "^30.1.3", @@ -1147,6 +1148,30 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint/js": { "version": "9.36.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", @@ -3074,16 +3099,16 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -4148,6 +4173,30 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -4343,6 +4392,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -5705,9 +5771,9 @@ "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, @@ -6727,6 +6793,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", diff --git a/package.json b/package.json index b628f181..83ab15f4 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,8 @@ "verify:dist": "node scripts/verify-dist.mjs", "generate-openapi-layer": "openapi-ts", "generate-docs": "typedoc --plugin typedoc-plugin-markdown", - "prepublishOnly": "npm run build" + "prepublishOnly": "npm run build", + "verify:agent-schema": "node scripts/verify-agent-schema.mjs" }, "devDependencies": { "@eslint/js": "^9.34.0", @@ -95,6 +96,7 @@ "@tsconfig/node24": "^24.0.4", "@types/node": "^22.0.0", "@types/uuid": "^9.0.1", + "ajv": "^8.17.1", "cross-env": "^10.1.0", "eslint": "^9.34.0", "jest": "^30.1.3", diff --git a/scripts/package-e2e-bundle.sh b/scripts/package-e2e-bundle.sh index 80b4c6b8..b0988ce9 100755 --- a/scripts/package-e2e-bundle.sh +++ b/scripts/package-e2e-bundle.sh @@ -110,12 +110,12 @@ set -euo pipefail # agentspan.embedded=true). # # Required services (NOT started by this script): -# - Conductor server → AGENTSPAN_SERVER_URL (default http://localhost:8080/api) +# - Conductor server → CONDUCTOR_AGENT_SERVER_URL (default http://localhost:8080/api) # - mcp-testkit → MCP_TESTKIT_URL (default http://localhost:3001) # Optional: -# - AGENTSPAN_LLM_MODEL (default openai/gpt-4o-mini); the provider API key +# - CONDUCTOR_AGENT_LLM_MODEL (default openai/gpt-4o-mini); the provider API key # must be configured on the SERVER — the suites never read it. -# - AGENTSPAN_CLI_PATH (default `agentspan` on PATH) — CLI suites skip if absent. +# - CONDUCTOR_AGENT_CLI_PATH (default `agentspan` on PATH) — CLI suites skip if absent. # # Requires node >= 20. Usage: ./run.sh [extra jest args] HERE="$(cd "$(dirname "$0")" && pwd)" @@ -143,10 +143,10 @@ released from agentspan-ai/agentspan. | Requirement | Env var | Default | |-----------------------------------|------------------------|-----------------------------| | node >= 20 | — | — | -| Conductor server w/ agent runtime | `AGENTSPAN_SERVER_URL` | `http://localhost:8080/api` | -| LLM model | `AGENTSPAN_LLM_MODEL` | `openai/gpt-4o-mini` | +| Conductor server w/ agent runtime | `CONDUCTOR_AGENT_SERVER_URL` | `http://localhost:8080/api` | +| LLM model | `CONDUCTOR_AGENT_LLM_MODEL` | `openai/gpt-4o-mini` | | mcp-testkit (MCP suites) | `MCP_TESTKIT_URL` | `http://localhost:3001` | -| agentspan CLI (CLI suites) | `AGENTSPAN_CLI_PATH` | `agentspan` (on `PATH`) | +| Conductor CLI (CLI suites) | `CONDUCTOR_AGENT_CLI_PATH` | `agentspan` (on `PATH`) | The server needs the agent runtime: conductor-oss `>= 3.32.0-rc.8`, or orkes-conductor booted with `agentspan.embedded=true`. LLM provider API keys diff --git a/scripts/verify-agent-schema.mjs b/scripts/verify-agent-schema.mjs new file mode 100644 index 00000000..98ecec54 --- /dev/null +++ b/scripts/verify-agent-schema.mjs @@ -0,0 +1,157 @@ +#!/usr/bin/env node +/** + * Verify the documented agent configuration contract. + * + * docs/agents/reference/agent-schema.json is the machine-readable contract for + * the JSON this SDK sends to the Conductor server's agent compiler. The shape is + * shared with the Python and Java SDKs, so drift is a cross-SDK compatibility + * bug, not a docs nit. + * + * Checks: + * 1. The schema file exists and is a valid draft-07 JSON Schema. + * 2. Every serialized agent config in e2e/_configs/ validates against it. + * 3. The structural invariants CI asserts in the sibling SDKs hold. + * + * Mirrors the intent of java-sdk's tools/agent-schema/verify.py, in the + * Node-script style this repo already uses for scripts/verify-dist.mjs. + */ +import { readFileSync, readdirSync, existsSync } from "node:fs"; +import { join } from "node:path"; +import process from "node:process"; + +import Ajv from "ajv"; + +const SCHEMA_PATH = "docs/agents/reference/agent-schema.json"; +const CORPUS_DIR = "e2e/_configs"; + +const failures = []; +const fail = (message) => failures.push(message); + +// ── 1. Schema loads and compiles ──────────────────────────────────────────── +if (!existsSync(SCHEMA_PATH)) { + console.error(`FAIL ${SCHEMA_PATH} is missing.`); + process.exit(1); +} + +let schema; +try { + schema = JSON.parse(readFileSync(SCHEMA_PATH, "utf8")); +} catch (error) { + console.error(`FAIL ${SCHEMA_PATH} is not valid JSON: ${error.message}`); + process.exit(1); +} + +const ajv = new Ajv({ allErrors: true, strict: false }); +let validate; +try { + validate = ajv.compile(schema); +} catch (error) { + console.error(`FAIL ${SCHEMA_PATH} is not a valid JSON Schema: ${error.message}`); + process.exit(1); +} + +// ── 2. Every fixture validates ────────────────────────────────────────────── +if (!existsSync(CORPUS_DIR)) { + fail(`${CORPUS_DIR} is missing — the schema has nothing to validate against.`); +} else { + const fixtures = readdirSync(CORPUS_DIR).filter((f) => f.endsWith(".json")).sort(); + + if (fixtures.length === 0) { + fail(`${CORPUS_DIR} contains no fixtures — the schema is unverified.`); + } + + for (const fixture of fixtures) { + const path = join(CORPUS_DIR, fixture); + let config; + try { + config = JSON.parse(readFileSync(path, "utf8")); + } catch (error) { + fail(`${path} is not valid JSON: ${error.message}`); + continue; + } + if (!validate(config)) { + for (const err of validate.errors ?? []) { + fail(`${path}${err.instancePath || ""} ${err.message}`); + } + } + } + + console.log(`Validated ${fixtures.length} agent configs against ${SCHEMA_PATH}.`); +} + +// ── 3. Structural invariants ──────────────────────────────────────────────── +// The sibling SDKs assert these in CI. Keep them in sync. +const assertions = [ + [ + "name is required", + () => Array.isArray(schema.required) && schema.required.includes("name"), + ], + [ + "name is constrained to an identifier pattern (it becomes a workflow definition name)", + () => typeof schema.properties?.name?.pattern === "string", + ], + [ + "additionalProperties stays permissive, so a newer server field does not break an older SDK", + () => schema.additionalProperties !== false, + ], + [ + "agents is recursive, so sub-agents are validated by the same schema", + () => schema.properties?.agents?.items?.$ref === "#", + ], + [ + "tool.toolType is an enum including worker (the only type needing a local poller)", + () => (schema.definitions?.tool?.properties?.toolType?.enum ?? []).includes("worker"), + ], + [ + "strategy enumerates all nine orchestration strategies", + () => (schema.properties?.strategy?.enum ?? []).length === 9, + ], + [ + "guardrail.onFail enumerates raise/retry/fix/human", + () => { + const values = schema.definitions?.guardrail?.properties?.onFail?.enum ?? []; + return ["raise", "retry", "fix", "human"].every((v) => values.includes(v)); + }, + ], + [ + "handoff.target is required (a handoff without a target is meaningless)", + () => (schema.definitions?.handoff?.required ?? []).includes("target"), + ], +]; + +for (const [description, check] of assertions) { + let ok = false; + try { + ok = check() === true; + } catch { + ok = false; + } + if (!ok) fail(`schema invariant violated: ${description}`); +} + +// ── 4. Documentation invariants ───────────────────────────────────────────── +// Paths the canonical structure requires, and paths it must not reintroduce. +for (const required of [ + "docs/agents/reference/agent-schema.json", + "docs/agents/reference/agent-schema.md", + "docs/agents/README.md", + "docs/README.md", + "docs/documentation-standard.md", +]) { + if (!existsSync(required)) fail(`${required} is missing.`); +} + +// docs/agents/generated/ was never part of this SDK and must not appear; +// concepts/skills.md is covered inside multi-agent.md, matching the siblings. +for (const forbidden of ["docs/agents/generated", "docs/agents/concepts/skills.md"]) { + if (existsSync(forbidden)) fail(`${forbidden} must not exist.`); +} + +// ── Report ────────────────────────────────────────────────────────────────── +if (failures.length > 0) { + console.error(`\n${failures.length} problem(s):`); + for (const f of failures) console.error(` FAIL ${f}`); + process.exit(1); +} + +console.log(`All ${assertions.length} schema invariants and documentation paths verified.`); diff --git a/src/agents/__tests__/config.test.ts b/src/agents/__tests__/config.test.ts index 357075a9..620cbd71 100644 --- a/src/agents/__tests__/config.test.ts +++ b/src/agents/__tests__/config.test.ts @@ -6,13 +6,13 @@ describe("AgentConfig", () => { // Save and restore env vars to avoid test pollution const savedEnv: Record = {}; const envKeys = [ - "AGENTSPAN_WORKER_POLL_INTERVAL", - "AGENTSPAN_WORKER_THREADS", - "AGENTSPAN_AUTO_START_WORKERS", - "AGENTSPAN_STREAMING_ENABLED", - "AGENTSPAN_LIVENESS_ENABLED", - "AGENTSPAN_LIVENESS_STALL_SECONDS", - "AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS", + "CONDUCTOR_AGENT_WORKER_POLL_INTERVAL", + "CONDUCTOR_AGENT_WORKER_THREADS", + "CONDUCTOR_AGENT_AUTO_START_WORKERS", + "CONDUCTOR_AGENT_STREAMING_ENABLED", + "CONDUCTOR_AGENT_LIVENESS_ENABLED", + "CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS", + "CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS", ]; beforeEach(() => { @@ -69,14 +69,14 @@ describe("AgentConfig", () => { }); describe("env var loading", () => { - it("reads all AGENTSPAN_ env vars", () => { - process.env.AGENTSPAN_WORKER_POLL_INTERVAL = "200"; - process.env.AGENTSPAN_WORKER_THREADS = "2"; - process.env.AGENTSPAN_AUTO_START_WORKERS = "false"; - process.env.AGENTSPAN_STREAMING_ENABLED = "false"; - process.env.AGENTSPAN_LIVENESS_ENABLED = "false"; - process.env.AGENTSPAN_LIVENESS_STALL_SECONDS = "60.5"; - process.env.AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS = "20.5"; + it("reads all CONDUCTOR_AGENT_ env vars", () => { + process.env.CONDUCTOR_AGENT_WORKER_POLL_INTERVAL = "200"; + process.env.CONDUCTOR_AGENT_WORKER_THREADS = "2"; + process.env.CONDUCTOR_AGENT_AUTO_START_WORKERS = "false"; + process.env.CONDUCTOR_AGENT_STREAMING_ENABLED = "false"; + process.env.CONDUCTOR_AGENT_LIVENESS_ENABLED = "false"; + process.env.CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS = "60.5"; + process.env.CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS = "20.5"; const config = new AgentConfig(); @@ -90,14 +90,14 @@ describe("AgentConfig", () => { }); it("options override env vars", () => { - process.env.AGENTSPAN_WORKER_THREADS = "9"; + process.env.CONDUCTOR_AGENT_WORKER_THREADS = "9"; const config = new AgentConfig({ workerThreadCount: 3 }); expect(config.workerThreadCount).toBe(3); }); it("handles boolean env var variations", () => { - process.env.AGENTSPAN_AUTO_START_WORKERS = "1"; - process.env.AGENTSPAN_STREAMING_ENABLED = "no"; + process.env.CONDUCTOR_AGENT_AUTO_START_WORKERS = "1"; + process.env.CONDUCTOR_AGENT_STREAMING_ENABLED = "no"; const config = new AgentConfig(); @@ -106,8 +106,8 @@ describe("AgentConfig", () => { }); it("handles invalid numeric env vars gracefully", () => { - process.env.AGENTSPAN_WORKER_POLL_INTERVAL = "not-a-number"; - process.env.AGENTSPAN_WORKER_THREADS = ""; + process.env.CONDUCTOR_AGENT_WORKER_POLL_INTERVAL = "not-a-number"; + process.env.CONDUCTOR_AGENT_WORKER_THREADS = ""; const config = new AgentConfig(); @@ -116,8 +116,8 @@ describe("AgentConfig", () => { }); it("falls back to default when a liveness float env var is empty or invalid", () => { - process.env.AGENTSPAN_LIVENESS_STALL_SECONDS = ""; - process.env.AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS = "not-a-float"; + process.env.CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS = ""; + process.env.CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS = "not-a-float"; const config = new AgentConfig(); @@ -128,7 +128,7 @@ describe("AgentConfig", () => { describe("fromEnv", () => { it("creates config from env vars only", () => { - process.env.AGENTSPAN_WORKER_THREADS = "6"; + process.env.CONDUCTOR_AGENT_WORKER_THREADS = "6"; const config = AgentConfig.fromEnv(); expect(config.workerThreadCount).toBe(6); }); diff --git a/src/agents/__tests__/legacy-env.test.ts b/src/agents/__tests__/legacy-env.test.ts new file mode 100644 index 00000000..80ae1579 --- /dev/null +++ b/src/agents/__tests__/legacy-env.test.ts @@ -0,0 +1,100 @@ +import { describe, it, expect, beforeEach, afterEach, jest } from "@jest/globals"; +import { AgentConfig } from "../config.js"; +import { readRenamedEnv, resetRenamedEnvWarnings } from "../legacy-env.js"; +import { ConductorAgentError, AgentspanError, AgentAPIError } from "../errors.js"; + +/** + * The AGENTSPAN_* -> CONDUCTOR_AGENT_* rename keeps the old spellings working + * as deprecated fallbacks. These tests are the guarantee: the rest of the suite + * was mechanically renamed to the new names, so without this file nothing would + * catch the fallback being dropped. + */ +describe("deprecated AGENTSPAN_* environment variables", () => { + const keys = [ + "CONDUCTOR_AGENT_WORKER_THREADS", + "AGENTSPAN_WORKER_THREADS", + "CONDUCTOR_AGENT_STREAMING_ENABLED", + "AGENTSPAN_STREAMING_ENABLED", + ]; + const saved: Record = {}; + let warnSpy: ReturnType; + + beforeEach(() => { + for (const key of keys) { + saved[key] = process.env[key]; + delete process.env[key]; + } + resetRenamedEnvWarnings(); + warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {}); + }); + + afterEach(() => { + for (const key of keys) { + if (saved[key] !== undefined) process.env[key] = saved[key]; + else delete process.env[key]; + } + warnSpy.mockRestore(); + }); + + it("resolves a legacy AGENTSPAN_* value when the canonical name is unset", () => { + process.env.AGENTSPAN_WORKER_THREADS = "7"; + + expect(new AgentConfig().workerThreadCount).toBe(7); + }); + + it("prefers the canonical CONDUCTOR_AGENT_* name over the legacy one", () => { + process.env.CONDUCTOR_AGENT_WORKER_THREADS = "3"; + process.env.AGENTSPAN_WORKER_THREADS = "9"; + + expect(new AgentConfig().workerThreadCount).toBe(3); + }); + + it("treats an empty legacy value as unset and falls through to the default", () => { + process.env.AGENTSPAN_WORKER_THREADS = ""; + + expect(new AgentConfig().workerThreadCount).toBe(1); + }); + + it("warns once per legacy name, naming the replacement", () => { + process.env.AGENTSPAN_WORKER_THREADS = "2"; + + readRenamedEnv("CONDUCTOR_AGENT_WORKER_THREADS", "AGENTSPAN_WORKER_THREADS"); + readRenamedEnv("CONDUCTOR_AGENT_WORKER_THREADS", "AGENTSPAN_WORKER_THREADS"); + readRenamedEnv("CONDUCTOR_AGENT_WORKER_THREADS", "AGENTSPAN_WORKER_THREADS"); + + expect(warnSpy).toHaveBeenCalledTimes(1); + expect(String(warnSpy.mock.calls[0][0])).toContain("AGENTSPAN_WORKER_THREADS is deprecated"); + expect(String(warnSpy.mock.calls[0][0])).toContain("CONDUCTOR_AGENT_WORKER_THREADS"); + }); + + it("does not warn when only the canonical name is set", () => { + process.env.CONDUCTOR_AGENT_STREAMING_ENABLED = "false"; + + expect(new AgentConfig().streamingEnabled).toBe(false); + expect(warnSpy).not.toHaveBeenCalled(); + }); +}); + +describe("deprecated AgentspanError alias", () => { + it("is the same class object as ConductorAgentError, not a subclass", () => { + expect(AgentspanError).toBe(ConductorAgentError); + }); + + it("matches instanceof in both directions", () => { + const viaNew = new ConductorAgentError("boom"); + const viaAlias = new AgentspanError("boom"); + + expect(viaNew).toBeInstanceOf(AgentspanError); + expect(viaAlias).toBeInstanceOf(ConductorAgentError); + }); + + it("still catches subclassed errors raised by the SDK", () => { + const err = new AgentAPIError("failed", 500, "{}"); + + expect(err).toBeInstanceOf(AgentspanError); + }); + + it("reports the canonical name on the instance", () => { + expect(new AgentspanError("boom").name).toBe("ConductorAgentError"); + }); +}); diff --git a/src/agents/__tests__/planner-context.test.ts b/src/agents/__tests__/planner-context.test.ts index f96ab830..7dea065f 100644 --- a/src/agents/__tests__/planner-context.test.ts +++ b/src/agents/__tests__/planner-context.test.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. - // TypeScript SDK mirror of sdk/python/tests/unit/test_planner_context.py. // Pins Agent's plannerContext normalisation + AgentConfigSerializer's wire // emission. Same shape, same wire format — guarantees the four SDKs stay diff --git a/src/agents/__tests__/plans.test.ts b/src/agents/__tests__/plans.test.ts index 5864dcca..b36364b0 100644 --- a/src/agents/__tests__/plans.test.ts +++ b/src/agents/__tests__/plans.test.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. - import { describe, it, expect } from "@jest/globals"; import { Generate, Op, Plan, Ref, Step } from "../plans"; diff --git a/src/agents/__tests__/runtime.test.ts b/src/agents/__tests__/runtime.test.ts index 5efeb16e..d156a0d2 100644 --- a/src/agents/__tests__/runtime.test.ts +++ b/src/agents/__tests__/runtime.test.ts @@ -117,10 +117,10 @@ const jsonResponse = (body: unknown, status = 200) => describe("AgentRuntime", () => { const savedEnv: Record = {}; const envKeys = [ - "AGENTSPAN_SERVER_URL", - "AGENTSPAN_API_KEY", - "AGENTSPAN_AUTH_KEY", - "AGENTSPAN_AUTH_SECRET", + "CONDUCTOR_AGENT_SERVER_URL", + "CONDUCTOR_AGENT_API_KEY", + "CONDUCTOR_AGENT_AUTH_KEY", + "CONDUCTOR_AGENT_AUTH_SECRET", ]; function mockAgentServer(executionId = "wf-cred-test", fetchCalls?: string[]) { @@ -890,7 +890,7 @@ describe("AgentRuntime", () => { describe("Singleton functions", () => { const savedEnv: Record = {}; - const envKeys = ["AGENTSPAN_SERVER_URL", "AGENTSPAN_API_KEY"]; + const envKeys = ["CONDUCTOR_AGENT_SERVER_URL", "CONDUCTOR_AGENT_API_KEY"]; beforeEach(() => { for (const key of envKeys) { diff --git a/src/agents/__tests__/schedules-api.test.ts b/src/agents/__tests__/schedules-api.test.ts index f1639dc2..2e35f828 100644 --- a/src/agents/__tests__/schedules-api.test.ts +++ b/src/agents/__tests__/schedules-api.test.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2026 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - import { describe, it, expect, jest } from "@jest/globals"; import * as schedules from "../schedules-api.js"; import type { AgentRuntime } from "../runtime.js"; diff --git a/src/agents/__tests__/serializer.test.ts b/src/agents/__tests__/serializer.test.ts index 405ba843..fb4e903b 100644 --- a/src/agents/__tests__/serializer.test.ts +++ b/src/agents/__tests__/serializer.test.ts @@ -930,8 +930,8 @@ describe("serializeAgent() — allowedTransitions", () => { // ── Mixed tool formats ───────────────────────────────────── describe("serializer handles mixed tool formats", () => { - it("serializes agentspan + Vercel AI SDK + raw tools in same array", () => { - // agentspan native + it("serializes Conductor + Vercel AI SDK + raw tools in same array", () => { + // Conductor native const t1 = tool(async () => "ok", { name: "native_tool", description: "Native", diff --git a/src/agents/__tests__/skill.test.ts b/src/agents/__tests__/skill.test.ts index 73a04fef..89f08022 100644 --- a/src/agents/__tests__/skill.test.ts +++ b/src/agents/__tests__/skill.test.ts @@ -194,7 +194,7 @@ describe("skill", () => { }); it("resolves nested cross-skill references", () => { - const root = fs.mkdtempSync(path.join(os.tmpdir(), "agentspan-ts-cross-skill-")); + const root = fs.mkdtempSync(path.join(os.tmpdir(), "conductor-ts-cross-skill-")); try { const parent = path.join(root, "parent-skill"); const child = path.join(root, "child-skill"); diff --git a/src/agents/__tests__/tool.test.ts b/src/agents/__tests__/tool.test.ts index 46a9f21b..2a0d726f 100644 --- a/src/agents/__tests__/tool.test.ts +++ b/src/agents/__tests__/tool.test.ts @@ -179,7 +179,7 @@ describe("tool() options", () => { // ── getToolDef() with all 3 formats ──────────────────────── describe("getToolDef()", () => { - it("extracts from agentspan tool() wrapper", () => { + it("extracts from Conductor tool() wrapper", () => { const t = tool(async () => "ok", { name: "test", description: "test desc", @@ -225,7 +225,7 @@ describe("getToolDef()", () => { // ── normalizeToolInput() ─────────────────────────────────── describe("normalizeToolInput()", () => { - it("normalizes agentspan tool", () => { + it("normalizes Conductor tool", () => { const t = tool(async () => "ok", { name: "test", description: "desc", diff --git a/src/agents/claude-code.ts b/src/agents/claude-code.ts index bea52dea..2acb2062 100644 --- a/src/agents/claude-code.ts +++ b/src/agents/claude-code.ts @@ -3,7 +3,7 @@ * * Example: * - * import { Agent, ClaudeCode, PermissionMode } from 'agentspan'; + * import { Agent, ClaudeCode, PermissionMode } from '@io-orkes/conductor-javascript/agents'; * * const reviewer = new Agent({ * name: 'reviewer', diff --git a/src/agents/cli-config.ts b/src/agents/cli-config.ts index f76d5d86..c4ca491d 100644 --- a/src/agents/cli-config.ts +++ b/src/agents/cli-config.ts @@ -7,7 +7,7 @@ * * Example: * - * import { Agent } from 'agentspan'; + * import { Agent } from '@io-orkes/conductor-javascript/agents'; * * // Simple — just flip the flag * const agent = new Agent({ @@ -18,7 +18,7 @@ * }); * * // Full control - * import { CliConfigOptions } from 'agentspan'; + * import { CliConfigOptions } from '@io-orkes/conductor-javascript/agents'; * * const agent = new Agent({ * name: 'ops', diff --git a/src/agents/config.ts b/src/agents/config.ts index daf20a3e..2e8d2f12 100644 --- a/src/agents/config.ts +++ b/src/agents/config.ts @@ -1,8 +1,17 @@ import { config as dotenvConfig } from "dotenv"; +import { readRenamedEnv } from "./legacy-env.js"; // Load .env file on import (no-op if file doesn't exist) dotenvConfig(); +/** + * Read a `CONDUCTOR_AGENT_*` knob, falling back to its deprecated + * `AGENTSPAN_*` spelling. See {@link readRenamedEnv}. + */ +function agentEnv(suffix: string): string | undefined { + return readRenamedEnv(`CONDUCTOR_AGENT_${suffix}`, `AGENTSPAN_${suffix}`); +} + /** * Parse a boolean from an environment variable string. * Recognizes 'true', '1', 'yes' as true; everything else as false. @@ -58,29 +67,27 @@ export class AgentConfig { readonly livenessCheckIntervalSeconds: number; constructor(options?: AgentConfigOptions) { - const env = process.env; - this.workerPollIntervalMs = - options?.workerPollIntervalMs ?? parseIntEnv(env.AGENTSPAN_WORKER_POLL_INTERVAL, 100); + options?.workerPollIntervalMs ?? parseIntEnv(agentEnv("WORKER_POLL_INTERVAL"), 100); this.workerThreadCount = - options?.workerThreadCount ?? parseIntEnv(env.AGENTSPAN_WORKER_THREADS, 1); + options?.workerThreadCount ?? parseIntEnv(agentEnv("WORKER_THREADS"), 1); this.autoStartWorkers = - options?.autoStartWorkers ?? parseBoolEnv(env.AGENTSPAN_AUTO_START_WORKERS, true); + options?.autoStartWorkers ?? parseBoolEnv(agentEnv("AUTO_START_WORKERS"), true); this.streamingEnabled = - options?.streamingEnabled ?? parseBoolEnv(env.AGENTSPAN_STREAMING_ENABLED, true); + options?.streamingEnabled ?? parseBoolEnv(agentEnv("STREAMING_ENABLED"), true); this.livenessEnabled = - options?.livenessEnabled ?? parseBoolEnv(env.AGENTSPAN_LIVENESS_ENABLED, true); + options?.livenessEnabled ?? parseBoolEnv(agentEnv("LIVENESS_ENABLED"), true); this.livenessStallSeconds = - options?.livenessStallSeconds ?? parseFloatEnv(env.AGENTSPAN_LIVENESS_STALL_SECONDS, 30.0); + options?.livenessStallSeconds ?? parseFloatEnv(agentEnv("LIVENESS_STALL_SECONDS"), 30.0); this.livenessCheckIntervalSeconds = options?.livenessCheckIntervalSeconds ?? - parseFloatEnv(env.AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS, 10.0); + parseFloatEnv(agentEnv("LIVENESS_CHECK_INTERVAL_SECONDS"), 10.0); } /** diff --git a/src/agents/errors.ts b/src/agents/errors.ts index 2b69184f..a933badb 100644 --- a/src/agents/errors.ts +++ b/src/agents/errors.ts @@ -1,14 +1,36 @@ /** - * Base error for all Agentspan SDK errors. + * Base error for all Conductor agent errors. */ -export class AgentspanError extends Error { +export class ConductorAgentError extends Error { constructor(message: string) { super(message); - this.name = "AgentspanError"; + this.name = "ConductorAgentError"; Object.setPrototypeOf(this, new.target.prototype); } } +/** + * Previous name for {@link ConductorAgentError}. + * + * A direct alias rather than a subclass, so `instanceof` keeps working in both + * directions for code written against either name. + * + * @deprecated Renamed to `ConductorAgentError` when Agentspan became + * Conductor. Will be removed in a future release. + */ +export const AgentspanError = ConductorAgentError; + +/** + * Previous name for {@link ConductorAgentError}, as a type. + * + * Declared alongside the value alias above so both `instanceof AgentspanError` + * and `const e: AgentspanError` keep compiling. + * + * @deprecated Renamed to `ConductorAgentError` when Agentspan became + * Conductor. Will be removed in a future release. + */ +export type AgentspanError = ConductorAgentError; + /** * HTTP API error with status code and response body. * @@ -18,7 +40,7 @@ export class AgentspanError extends Error { * which 500 responses on /agent/start become impossible to triage from * CI logs alone. */ -export class AgentAPIError extends AgentspanError { +export class AgentAPIError extends ConductorAgentError { readonly statusCode: number; readonly responseBody: string; @@ -38,7 +60,7 @@ export class AgentAPIError extends AgentspanError { /** * Agent not found by name. */ -export class AgentNotFoundError extends AgentspanError { +export class AgentNotFoundError extends ConductorAgentError { readonly agentName: string; constructor(agentName: string) { @@ -52,7 +74,7 @@ export class AgentNotFoundError extends AgentspanError { /** * Configuration error — invalid or missing config values. */ -export class ConfigurationError extends AgentspanError { +export class ConfigurationError extends ConductorAgentError { constructor(message: string) { super(message); this.name = "ConfigurationError"; @@ -63,7 +85,7 @@ export class ConfigurationError extends AgentspanError { /** * Credential not found in the credential store. */ -export class CredentialNotFoundError extends AgentspanError { +export class CredentialNotFoundError extends ConductorAgentError { readonly credentialName: string; constructor(credentialName: string) { @@ -77,7 +99,7 @@ export class CredentialNotFoundError extends AgentspanError { /** * Credential authentication error — execution token invalid or expired. */ -export class CredentialAuthError extends AgentspanError { +export class CredentialAuthError extends ConductorAgentError { constructor(message = "Credential authentication failed") { super(message); this.name = "CredentialAuthError"; @@ -88,7 +110,7 @@ export class CredentialAuthError extends AgentspanError { /** * Credential rate limit exceeded (120 calls/min). */ -export class CredentialRateLimitError extends AgentspanError { +export class CredentialRateLimitError extends ConductorAgentError { constructor(message = "Credential rate limit exceeded") { super(message); this.name = "CredentialRateLimitError"; @@ -99,7 +121,7 @@ export class CredentialRateLimitError extends AgentspanError { /** * Credential service error — server-side failure. */ -export class CredentialServiceError extends AgentspanError { +export class CredentialServiceError extends ConductorAgentError { constructor(message = "Credential service error") { super(message); this.name = "CredentialServiceError"; @@ -110,7 +132,7 @@ export class CredentialServiceError extends AgentspanError { /** * SSE connection timeout — no events received within the timeout window. */ -export class SSETimeoutError extends AgentspanError { +export class SSETimeoutError extends ConductorAgentError { constructor(message = "SSE connection timed out") { super(message); this.name = "SSETimeoutError"; @@ -122,7 +144,7 @@ export class SSETimeoutError extends AgentspanError { * The server rejected the initial SSE connection (non-2xx) — it does not * support streaming for this route. Callers fall back to polling. */ -export class SSEUnavailableError extends AgentspanError { +export class SSEUnavailableError extends ConductorAgentError { constructor(message = "SSE stream is unavailable") { super(message); this.name = "SSEUnavailableError"; @@ -134,7 +156,7 @@ export class SSEUnavailableError extends AgentspanError { * Terminal tool error — non-retryable failure (e.g., CLI command exited non-zero). * Causes the Conductor task to be marked FAILED_WITH_TERMINAL_ERROR. */ -export class TerminalToolError extends AgentspanError { +export class TerminalToolError extends ConductorAgentError { constructor(message: string) { super(message); this.name = "TerminalToolError"; @@ -147,7 +169,7 @@ export class TerminalToolError extends AgentspanError { * for longer than the liveness stall window — the local worker process for * this run's domain likely died. Surfaces from a blocking `wait()`. */ -export class WorkerStallError extends AgentspanError { +export class WorkerStallError extends ConductorAgentError { readonly executionId: string; readonly taskDefName: string; readonly taskId: string; @@ -169,7 +191,7 @@ export class WorkerStallError extends AgentspanError { /** * Guardrail validation failed. */ -export class GuardrailFailedError extends AgentspanError { +export class GuardrailFailedError extends ConductorAgentError { readonly guardrailName: string; readonly failureMessage: string; diff --git a/src/agents/frameworks/detect.ts b/src/agents/frameworks/detect.ts index 49bd3422..493dd9be 100644 --- a/src/agents/frameworks/detect.ts +++ b/src/agents/frameworks/detect.ts @@ -3,7 +3,7 @@ * * Detection order (priority): * 0. agent._framework === 'skill' → 'skill' (checked before instanceof) - * 1. agent instanceof Agent → null (native agentspan) + * 1. agent instanceof Agent → null (native Conductor) * 2. .invoke() + (.getGraph() OR .nodes Map) → 'langgraph' * 3. .invoke() + .lc_namespace → 'langchain' * 4. .name + .instructions + .model + .tools + .handoffs → 'openai' @@ -90,7 +90,7 @@ function hasADKMarkers(obj: any): boolean { /** * Detect which framework (if any) the given agent object belongs to. - * Returns null for native agentspan Agent instances or unknown objects. + * Returns null for native Conductor Agent instances or unknown objects. */ export function detectFramework(agent: unknown): FrameworkId | null { // 0. Skill framework — must be checked before native Agent check @@ -103,7 +103,7 @@ export function detectFramework(agent: unknown): FrameworkId | null { return "skill"; } - // 1. Native agentspan Agent — not a framework + // 1. Native Conductor Agent — not a framework if (agent instanceof Agent) return null; // 2. LangGraph.js: CompiledStateGraph has .invoke() + .getGraph() or .nodes diff --git a/src/agents/frameworks/langgraph-serializer.ts b/src/agents/frameworks/langgraph-serializer.ts index 0be4c3c2..6953021d 100644 --- a/src/agents/frameworks/langgraph-serializer.ts +++ b/src/agents/frameworks/langgraph-serializer.ts @@ -1150,7 +1150,7 @@ function _isLLMEndpoint(url: string): boolean { */ function _fakeLLMResponse(content: string): Response { const body = JSON.stringify({ - id: "agentspan-capture", + id: "conductor-capture", object: "chat.completion", choices: [ { diff --git a/src/agents/index.ts b/src/agents/index.ts index 25e0dfad..6855241b 100644 --- a/src/agents/index.ts +++ b/src/agents/index.ts @@ -31,7 +31,7 @@ export { createAgentResult, normalizeOutput, stripInternalEventKeys } from "./ty // ── Errors ─────────────────────────────────────────────── export { - AgentspanError, + ConductorAgentError, AgentAPIError, AgentNotFoundError, ConfigurationError, @@ -43,6 +43,8 @@ export { TerminalToolError, GuardrailFailedError, WorkerStallError, + // Deprecated: previous name for ConductorAgentError. Removed in a future release. + AgentspanError, } from "./errors.js"; // ── Config ─────────────────────────────────────────────── diff --git a/src/agents/legacy-env.ts b/src/agents/legacy-env.ts new file mode 100644 index 00000000..d6d6f947 --- /dev/null +++ b/src/agents/legacy-env.ts @@ -0,0 +1,43 @@ +/** + * Deprecated `AGENTSPAN_*` environment variable support. + * + * The agent layer's config surface was renamed `AGENTSPAN_*` -> + * `CONDUCTOR_AGENT_*` when Agentspan became Conductor. The old names still + * resolve so existing deployments keep working, but each one warns once per + * process the first time it actually supplies a value. + * + * Deliberately standalone rather than sharing a helper with `src/sdk`: the + * agent layer's only permitted coupling to the workflow layer is in + * `agent-client.ts` and `worker.ts` (see AGENTS.md). + */ + +const warned = new Set(); + +/** + * Read a renamed environment variable, preferring the canonical name. + * + * Warns once per legacy name, and only when the legacy name is the one that + * actually supplied the value — callers already on `CONDUCTOR_AGENT_*` never + * see output, and a value of `""` is treated as unset to match the parsers. + */ +export function readRenamedEnv(canonical: string, legacy: string): string | undefined { + const env = process.env; + const current = env[canonical]; + if (current !== undefined && current !== "") return current; + + const legacyValue = env[legacy]; + if (legacyValue === undefined || legacyValue === "") return undefined; + + if (!warned.has(legacy)) { + warned.add(legacy); + console.warn( + `[conductor] ${legacy} is deprecated and will be removed in a future release. Use ${canonical} instead.`, + ); + } + return legacyValue; +} + +/** Test-only: clear the warn-once state so each case starts clean. */ +export function resetRenamedEnvWarnings(): void { + warned.clear(); +} diff --git a/src/agents/liveness.ts b/src/agents/liveness.ts index 6fd57858..7f87959e 100644 --- a/src/agents/liveness.ts +++ b/src/agents/liveness.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2026 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * Watches a stateful (domain-routed) agent run for tasks nobody is polling. * diff --git a/src/agents/plans.ts b/src/agents/plans.ts index 1bc3cb42..7a12afd7 100644 --- a/src/agents/plans.ts +++ b/src/agents/plans.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2025 Agentspan -// Licensed under the MIT License. - /** * Typed plan builders for `Strategy.PLAN_EXECUTE`. * @@ -8,7 +5,7 @@ * task) consumes. Use them to construct plans in TypeScript with IDE * autocomplete and tsc type-checking, instead of inlining JSON literals. * - * The wire format is identical to the Python SDK's `agentspan.agents.plans` + * The wire format is identical to the Python SDK's `conductor.ai.agents.plans` * dataclasses: same JSON shape, same field names, same Ref marker * (`{"$ref": "step_id"}`). The server compiler is the same path for both * SDKs. diff --git a/src/agents/runtime.ts b/src/agents/runtime.ts index 7137abf9..2052bccc 100644 --- a/src/agents/runtime.ts +++ b/src/agents/runtime.ts @@ -8,7 +8,7 @@ import type { GuardrailDef, FrameworkId, } from "./types.js"; -import { AgentspanError, AgentAPIError, WorkerStallError } from "./errors.js"; +import { ConductorAgentError, AgentAPIError, WorkerStallError } from "./errors.js"; import { LivenessMonitor } from "./liveness.js"; import { AgentConfig } from "./config.js"; import type { AgentConfigOptions } from "./config.js"; @@ -123,7 +123,7 @@ function _resolveRunSettings(options?: RunOptions): RunSettings { // ── AgentRuntime ──────────────────────────────────────── /** - * Core execution runtime for the Agentspan SDK. + * Core execution runtime for the Conductor SDK. * Manages agent lifecycle: run, start, stream, deploy, plan, serve. */ export class AgentRuntime { @@ -139,7 +139,7 @@ export class AgentRuntime { * @param configuration a connection config to build the shared client from, * or an already-built {@link ConductorClient} to reuse (the * `OrkesClients` injection pattern). Env-resolved when omitted - * (`AGENTSPAN_SERVER_URL`/`AGENTSPAN_AUTH_KEY`/`AGENTSPAN_AUTH_SECRET` + * (`CONDUCTOR_AGENT_SERVER_URL`/`CONDUCTOR_AGENT_AUTH_KEY`/`CONDUCTOR_AGENT_AUTH_SECRET` * fallbacks, `localhost:8080` default — spec R3). * @param settings behavior knobs only (spec R4) — no connection/auth here. */ @@ -1515,7 +1515,7 @@ export class AgentRuntime { case "skill": return this._serializeSkill(agent as Agent); default: - throw new AgentspanError(`Unsupported framework: ${frameworkId}`); + throw new ConductorAgentError(`Unsupported framework: ${frameworkId}`); } } diff --git a/src/agents/schedules-api.ts b/src/agents/schedules-api.ts index fd9f087d..24009963 100644 --- a/src/agents/schedules-api.ts +++ b/src/agents/schedules-api.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2026 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * Module-level lifecycle API for schedules. * diff --git a/src/agents/skill.ts b/src/agents/skill.ts index 8ccf647d..ab703103 100644 --- a/src/agents/skill.ts +++ b/src/agents/skill.ts @@ -424,7 +424,7 @@ function listFilesRecursive(dir: string): string[] { // ── Main skill() function ─────────────────────────────────── /** - * Load an Agent Skills directory as an Agentspan Agent. + * Load an Agent Skills directory as a Conductor Agent. * * @param skillPath - Path to skill directory containing SKILL.md. * @param options - Model, agent model overrides, search path, and runtime params. diff --git a/src/agents/stream.ts b/src/agents/stream.ts index f4056f0d..749ae412 100644 --- a/src/agents/stream.ts +++ b/src/agents/stream.ts @@ -1,6 +1,6 @@ import type { AgentEvent, AgentResult, AgentStatus } from "./types.js"; import { stripInternalEventKeys } from "./types.js"; -import { SSETimeoutError, SSEUnavailableError, AgentspanError } from "./errors.js"; +import { SSETimeoutError, SSEUnavailableError, ConductorAgentError } from "./errors.js"; import { makeAgentResult } from "./result.js"; // ── Constants ─────────────────────────────────────────── @@ -137,7 +137,7 @@ export class AgentStream implements AsyncIterable { } if (!response.body) { - throw new AgentspanError("SSE response has no body"); + throw new ConductorAgentError("SSE response has no body"); } const reader = response.body.getReader(); diff --git a/src/agents/tool.ts b/src/agents/tool.ts index b4b5ee3b..a3818acc 100644 --- a/src/agents/tool.ts +++ b/src/agents/tool.ts @@ -175,7 +175,7 @@ function isVercelAITool(obj: unknown): boolean { } /** - * Check if an object has the TOOL_DEF symbol (agentspan tool wrapper). + * Check if an object has the TOOL_DEF symbol (Conductor tool wrapper). * Tool wrappers are functions, so we check both object and function types. */ function hasToolDef(obj: unknown): boolean { @@ -223,14 +223,14 @@ function wrapVercelAITool(aiTool: Record): ToolDef { /** * Extract ToolDef from any supported tool format: - * 1. agentspan tool() wrapper (via Symbol) + * 1. Conductor tool() wrapper (via Symbol) * 2. Vercel AI SDK tool (has parameters as Zod + execute) * 3. Raw ToolDef object * * Throws ConfigurationError if format is unrecognized. */ export function getToolDef(obj: unknown): ToolDef { - // 1. agentspan tool() wrapper + // 1. Conductor tool() wrapper if (hasToolDef(obj)) { return (obj as Record)[TOOL_DEF]; } @@ -278,7 +278,7 @@ export function getToolDef(obj: unknown): ToolDef { /** * Auto-detect format and return ToolDef. - * Handles agentspan tool(), Vercel AI SDK tool, and raw ToolDef objects. + * Handles Conductor tool(), Vercel AI SDK tool, and raw ToolDef objects. */ export function normalizeToolInput(input: unknown): ToolDef { return getToolDef(input); diff --git a/src/agents/worker.ts b/src/agents/worker.ts index f59610f8..e3bc8784 100644 --- a/src/agents/worker.ts +++ b/src/agents/worker.ts @@ -233,7 +233,7 @@ interface PendingWorker { * mirroring the Python SDK's ``WorkerManager`` pattern. Workers are * collected via {@link addWorker} and started/stopped as a group. * - * All agentspan-specific middleware (ToolContext extraction, credential + * All Conductor-specific middleware (ToolContext extraction, credential * injection, state capture, circuit breaker, error mapping) runs inside * each worker's ``execute()`` callback. */ @@ -294,7 +294,7 @@ export class WorkerManager { } /** - * Wrap an agentspan handler into a {@link ConductorWorker}. + * Wrap a Conductor handler into a {@link ConductorWorker}. * * Runs the full middleware chain: circuit breaker, ToolContext extraction, * credential injection, state capture, error mapping. diff --git a/src/agents/wrappers/ai.ts b/src/agents/wrappers/ai.ts index 4447d1e0..ab96e052 100644 --- a/src/agents/wrappers/ai.ts +++ b/src/agents/wrappers/ai.ts @@ -3,7 +3,7 @@ * * Re-exports everything from 'ai', but wraps `generateText` and `streamText` * to intercept the options object, extract model/tools/system, compile to - * AgentConfig, and run on agentspan. + * AgentConfig, and run on Conductor. * * Usage: * // BEFORE: import { generateText } from 'ai'; @@ -105,7 +105,7 @@ function _inferProviderFromModelName(modelName: string): string { // ── Tool extraction ───────────────────────────────────── /** - * Extract agentspan-compatible tools from AI SDK tools Record. + * Extract Conductor-compatible tools from AI SDK tools Record. * * AI SDK tools are Record where each CoreTool has: * - .parameters: Zod schema @@ -140,10 +140,10 @@ export function mapFinishReason(reason: string | undefined): string { import { Agent, AgentRuntime } from "../index.js"; /** - * Wrapped generateText that runs on agentspan. + * Wrapped generateText that runs on Conductor. * * Intercepts the options object, extracts model/tools/system/prompt, - * compiles to an Agent, runs on agentspan, returns the same result type. + * compiles to an Agent, runs on Conductor, returns the same result type. */ export async function generateText( options: Record, @@ -170,13 +170,13 @@ export async function generateText( maxTurns: maxSteps ?? 25, }); - // Run on agentspan + // Run on Conductor const runtime = new AgentRuntime(); try { const promptStr = prompt ?? (messages ? JSON.stringify(messages) : ""); const result = await runtime.run(agent, promptStr); - // Map agentspan result back to Vercel AI SDK result format + // Map Conductor result back to Vercel AI SDK result format return { text: typeof result.output?.result === "string" @@ -206,7 +206,7 @@ export async function generateText( // ── streamText wrapper ────────────────────────────────── /** - * Wrapped streamText that runs on agentspan. + * Wrapped streamText that runs on Conductor. * * For now, this delegates to generateText and wraps the result * in a stream-compatible interface. Full streaming will be added later. @@ -227,12 +227,12 @@ export async function streamText( })(), toAIStream: () => { throw new Error( - "toAIStream() is not supported in the agentspan wrapper. Use textStream instead.", + "toAIStream() is not supported in the Conductor wrapper. Use textStream instead.", ); }, toTextStreamResponse: () => { throw new Error( - "toTextStreamResponse() is not supported in the agentspan wrapper. Use textStream instead.", + "toTextStreamResponse() is not supported in the Conductor wrapper. Use textStream instead.", ); }, }; diff --git a/src/agents/wrappers/langchain.ts b/src/agents/wrappers/langchain.ts index 37ca6525..44ff0a29 100644 --- a/src/agents/wrappers/langchain.ts +++ b/src/agents/wrappers/langchain.ts @@ -32,13 +32,13 @@ function _loadLangChainCore(): Record { } } -// ── Agentspan metadata interface ──────────────────────── +// ── Conductor metadata interface ──────────────────────── /** * Metadata stored on the executor/runnable by the wrapper. * Used by the LangChain serializer for fast extraction. */ -export interface AgentspanMetadata { +export interface ConductorAgentMetadata { model: string; tools: unknown[]; instructions?: string; @@ -104,7 +104,7 @@ function _inferProviderFromModel(modelName: string): string { // ── createAgentExecutor wrapper ───────────────────────── /** - * Create a LangChain AgentExecutor-like object that stores agentspan metadata. + * Create a LangChain AgentExecutor-like object that stores Conductor metadata. * * This wraps the common pattern of creating an agent with tools and an LLM. * It captures the LLM and tools at construction time. @@ -159,8 +159,8 @@ export function createAgentExecutor(options: { }; } - // Store metadata for agentspan extraction - const metadata: AgentspanMetadata = { + // Store metadata for Conductor extraction + const metadata: ConductorAgentMetadata = { model: modelStr, tools: options.tools, instructions: undefined, @@ -175,7 +175,7 @@ export function createAgentExecutor(options: { // ── RunnableLambda wrapper ────────────────────────────── /** - * Create a LangChain RunnableLambda that stores agentspan metadata. + * Create a LangChain RunnableLambda that stores Conductor metadata. * * This wraps the common pattern of creating a custom runnable with * an LLM and tools in the closure. @@ -198,7 +198,7 @@ export function createRunnableWithMetadata(options: { tools: options.tools ?? [], instructions: options.instructions, framework: "langchain", - } as AgentspanMetadata, + } as ConductorAgentMetadata, }; return runnable; @@ -213,3 +213,11 @@ export function createRunnableWithMetadata(options: { export function getLangChainModule(): Record { return _loadLangChainCore(); } + +/** + * Previous name for {@link ConductorAgentMetadata}. + * + * @deprecated Renamed when Agentspan became Conductor. Will be removed in a + * future release. + */ +export type AgentspanMetadata = ConductorAgentMetadata; diff --git a/src/agents/wrappers/langgraph.ts b/src/agents/wrappers/langgraph.ts index bdedb14d..eba69151 100644 --- a/src/agents/wrappers/langgraph.ts +++ b/src/agents/wrappers/langgraph.ts @@ -32,13 +32,13 @@ function _loadLangGraph(): Record { } } -// ── Agentspan metadata interface ──────────────────────── +// ── Conductor metadata interface ──────────────────────── /** * Metadata stored on the graph object by the wrapper. * Used by the LangGraph serializer for fast extraction. */ -export interface AgentspanMetadata { +export interface ConductorAgentMetadata { model: string; tools: unknown[]; instructions?: string; @@ -137,7 +137,7 @@ export function createReactAgent(options: Record): unknown { const modelStr = extractModelFromLLM(llm); // Store metadata on the graph for later extraction - const metadata: AgentspanMetadata = { + const metadata: ConductorAgentMetadata = { model: modelStr, tools, instructions: typeof prompt === "string" ? prompt : undefined, @@ -158,3 +158,11 @@ export function createReactAgent(options: Record): unknown { export function getLangGraphModule(): Record { return _loadLangGraph(); } + +/** + * Previous name for {@link ConductorAgentMetadata}. + * + * @deprecated Renamed when Agentspan became Conductor. Will be removed in a + * future release. + */ +export type AgentspanMetadata = ConductorAgentMetadata; diff --git a/src/sdk/clients/agent/AgentClient.ts b/src/sdk/clients/agent/AgentClient.ts index d2b6bfbc..d3de1c77 100644 --- a/src/sdk/clients/agent/AgentClient.ts +++ b/src/sdk/clients/agent/AgentClient.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2026 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * Control-plane surface for the Agent Runtime API (`/agent/*`). * diff --git a/src/sdk/clients/agent/OrkesAgentClient.ts b/src/sdk/clients/agent/OrkesAgentClient.ts index 56af1976..268a5c6e 100644 --- a/src/sdk/clients/agent/OrkesAgentClient.ts +++ b/src/sdk/clients/agent/OrkesAgentClient.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2026 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * Conductor/Orkes implementation of {@link AgentClient}. * @@ -98,7 +95,7 @@ export class OrkesAgentClient implements AgentClient { /** * Lazily create (once) and return the shared {@link ConductorClient}. * `createConductorClient` is async, so we memoize the promise. Env-resolved - * (`AGENTSPAN_SERVER_URL`/`AGENTSPAN_AUTH_KEY`/`AGENTSPAN_AUTH_SECRET` + * (`CONDUCTOR_AGENT_SERVER_URL`/`CONDUCTOR_AGENT_AUTH_KEY`/`CONDUCTOR_AGENT_AUTH_SECRET` * fallbacks, `localhost:8080` default) via `resolveOrkesConfig` (R3) when * no explicit connection config was given. */ diff --git a/src/sdk/clients/agent/WorkflowClient.ts b/src/sdk/clients/agent/WorkflowClient.ts index 5fafe9e9..c8221beb 100644 --- a/src/sdk/clients/agent/WorkflowClient.ts +++ b/src/sdk/clients/agent/WorkflowClient.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2026 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * Thin wrapper over the conductor client's `workflowResource` for workflow * reads. Mirrors the Java/C#/Python SDK split where workflow-execution reads @@ -39,7 +36,7 @@ export interface WorkflowTokenUsage { export class WorkflowClient { /** * @param getClient resolver for the shared Conductor client. - * @param fetchAgentExecution optional fallback that reads an Agentspan agent + * @param fetchAgentExecution optional fallback that reads a Conductor agent * execution (`GET /agent/execution/{id}`). Agent executions are not stored * in Conductor's workflow index, so `getExecutionStatus` 404s for them; * when this fallback is provided, {@link getWorkflow} uses it. @@ -55,7 +52,7 @@ export class WorkflowClient { * Fetch a workflow execution by id (with tasks). * * Tries Conductor's `getExecutionStatus` first; for agent executions (which - * Conductor's workflow index doesn't hold) falls back to the Agentspan + * Conductor's workflow index doesn't hold) falls back to the Conductor * agent-execution endpoint when available. * * @param executionId Conductor workflow id or agent execution id. diff --git a/src/sdk/clients/agent/schedule.ts b/src/sdk/clients/agent/schedule.ts index 0dfe8aab..ff016901 100644 --- a/src/sdk/clients/agent/schedule.ts +++ b/src/sdk/clients/agent/schedule.ts @@ -1,6 +1,3 @@ -// Copyright (c) 2026 Agentspan -// Licensed under the MIT License. See LICENSE file in the project root for details. - /** * Cron-based scheduling for deployed agents — models, wire-name mapping and * typed errors shared by `SchedulerClient`'s agent-lifecycle methods. diff --git a/src/sdk/createConductorClient/__tests__/createConductorClient.test.ts b/src/sdk/createConductorClient/__tests__/createConductorClient.test.ts index 2361acc6..882d2587 100644 --- a/src/sdk/createConductorClient/__tests__/createConductorClient.test.ts +++ b/src/sdk/createConductorClient/__tests__/createConductorClient.test.ts @@ -458,21 +458,21 @@ describe("createConductorClient integration", () => { expect(client.getConfig().baseUrl).toBe("http://localhost:8080"); }); - it("falls back to AGENTSPAN_SERVER_URL when CONDUCTOR_SERVER_URL and explicit config are absent (spec R3)", async () => { - process.env.AGENTSPAN_SERVER_URL = "http://agentspan-fallback:9090"; + it("falls back to CONDUCTOR_AGENT_SERVER_URL when CONDUCTOR_SERVER_URL and explicit config are absent (spec R3)", async () => { + process.env.CONDUCTOR_AGENT_SERVER_URL = "http://conductor-fallback:9090"; const client = await createConductorClient({}, async () => jsonResponse({})); - expect(client.getConfig().baseUrl).toBe("http://agentspan-fallback:9090"); - delete process.env.AGENTSPAN_SERVER_URL; + expect(client.getConfig().baseUrl).toBe("http://conductor-fallback:9090"); + delete process.env.CONDUCTOR_AGENT_SERVER_URL; }); - it("explicit config wins over AGENTSPAN_SERVER_URL", async () => { - process.env.AGENTSPAN_SERVER_URL = "http://agentspan-fallback:9090"; + it("explicit config wins over CONDUCTOR_AGENT_SERVER_URL", async () => { + process.env.CONDUCTOR_AGENT_SERVER_URL = "http://conductor-fallback:9090"; const client = await createConductorClient( { serverUrl: "http://explicit:1234" }, async () => jsonResponse({}) ); expect(client.getConfig().baseUrl).toBe("http://explicit:1234"); - delete process.env.AGENTSPAN_SERVER_URL; + delete process.env.CONDUCTOR_AGENT_SERVER_URL; }); it("should resolve server URL from env var", async () => { diff --git a/src/sdk/createConductorClient/helpers/__tests__/resolveOrkesConfig.test.ts b/src/sdk/createConductorClient/helpers/__tests__/resolveOrkesConfig.test.ts index 64ab5019..62f83f4f 100644 --- a/src/sdk/createConductorClient/helpers/__tests__/resolveOrkesConfig.test.ts +++ b/src/sdk/createConductorClient/helpers/__tests__/resolveOrkesConfig.test.ts @@ -22,6 +22,9 @@ describe("resolveOrkesConfig", () => { "CONDUCTOR_PROXY_URL", "CONDUCTOR_TLS_INSECURE", "CONDUCTOR_DISABLE_HTTP2", + "CONDUCTOR_AGENT_SERVER_URL", + "CONDUCTOR_AGENT_AUTH_KEY", + "CONDUCTOR_AGENT_AUTH_SECRET", "AGENTSPAN_SERVER_URL", "AGENTSPAN_AUTH_KEY", "AGENTSPAN_AUTH_SECRET", @@ -77,53 +80,81 @@ describe("resolveOrkesConfig", () => { expect(result.serverUrl).toBe("http://localhost:8080"); }); - // ─── R3: CONDUCTOR_* -> explicit -> AGENTSPAN_* -> localhost:8080 ───── + // ─── R3: CONDUCTOR_* -> explicit -> CONDUCTOR_AGENT_* -> localhost:8080 ───── it("defaults to http://localhost:8080 when nothing is set (spec R3)", () => { expect(resolveOrkesConfig({}).serverUrl).toBe("http://localhost:8080"); }); - it("falls back to AGENTSPAN_SERVER_URL when no CONDUCTOR_SERVER_URL/explicit config (spec R3)", () => { - process.env.AGENTSPAN_SERVER_URL = "http://agentspan:9090"; - expect(resolveOrkesConfig({}).serverUrl).toBe("http://agentspan:9090"); + it("falls back to CONDUCTOR_AGENT_SERVER_URL when no CONDUCTOR_SERVER_URL/explicit config (spec R3)", () => { + process.env.CONDUCTOR_AGENT_SERVER_URL = "http://conductor-agent-env:9090"; + expect(resolveOrkesConfig({}).serverUrl).toBe("http://conductor-agent-env:9090"); }); - it("explicit config wins over AGENTSPAN_SERVER_URL", () => { - process.env.AGENTSPAN_SERVER_URL = "http://agentspan:9090"; + it("explicit config wins over CONDUCTOR_AGENT_SERVER_URL", () => { + process.env.CONDUCTOR_AGENT_SERVER_URL = "http://conductor-agent-env:9090"; expect(resolveOrkesConfig({ serverUrl: "http://explicit:1234" }).serverUrl).toBe( "http://explicit:1234" ); }); - it("CONDUCTOR_SERVER_URL wins over both explicit config and AGENTSPAN_SERVER_URL", () => { + it("CONDUCTOR_SERVER_URL wins over both explicit config and CONDUCTOR_AGENT_SERVER_URL", () => { process.env.CONDUCTOR_SERVER_URL = "http://conductor-env:8080"; - process.env.AGENTSPAN_SERVER_URL = "http://agentspan:9090"; + process.env.CONDUCTOR_AGENT_SERVER_URL = "http://conductor-agent-env:9090"; expect(resolveOrkesConfig({ serverUrl: "http://explicit:1234" }).serverUrl).toBe( "http://conductor-env:8080" ); }); + + // ─── Deprecated AGENTSPAN_* spelling of the agent-layer tier ──────────── + + it("falls back to the deprecated AGENTSPAN_SERVER_URL (spec R3)", () => { + process.env.AGENTSPAN_SERVER_URL = "http://legacy-agent-env:9090"; + expect(resolveOrkesConfig({}).serverUrl).toBe("http://legacy-agent-env:9090"); + }); + + it("CONDUCTOR_AGENT_SERVER_URL wins over the deprecated AGENTSPAN_SERVER_URL", () => { + process.env.CONDUCTOR_AGENT_SERVER_URL = "http://conductor-agent-env:9090"; + process.env.AGENTSPAN_SERVER_URL = "http://legacy-agent-env:9090"; + expect(resolveOrkesConfig({}).serverUrl).toBe("http://conductor-agent-env:9090"); + }); + + it("explicit config still wins over the deprecated AGENTSPAN_SERVER_URL", () => { + process.env.AGENTSPAN_SERVER_URL = "http://legacy-agent-env:9090"; + expect(resolveOrkesConfig({ serverUrl: "http://explicit:1234" }).serverUrl).toBe( + "http://explicit:1234" + ); + }); + + it("falls back to the deprecated AGENTSPAN_AUTH_KEY/SECRET", () => { + process.env.AGENTSPAN_AUTH_KEY = "legacy-key"; + process.env.AGENTSPAN_AUTH_SECRET = "legacy-secret"; + const result = resolveOrkesConfig({}); + expect(result.keyId).toBe("legacy-key"); + expect(result.keySecret).toBe("legacy-secret"); + }); }); - // ─── R3: auth key/secret AGENTSPAN_* fallback ─────────────────────── + // ─── R3: auth key/secret CONDUCTOR_AGENT_* fallback ─────────────────────── - describe("auth key/secret AGENTSPAN_* fallback", () => { - it("falls back to AGENTSPAN_AUTH_KEY/SECRET when no CONDUCTOR_* env/explicit config", () => { - process.env.AGENTSPAN_AUTH_KEY = "agentspan-key"; - process.env.AGENTSPAN_AUTH_SECRET = "agentspan-secret"; + describe("auth key/secret CONDUCTOR_AGENT_* fallback", () => { + it("falls back to CONDUCTOR_AGENT_AUTH_KEY/SECRET when no CONDUCTOR_* env/explicit config", () => { + process.env.CONDUCTOR_AGENT_AUTH_KEY = "Conductor-key"; + process.env.CONDUCTOR_AGENT_AUTH_SECRET = "conductor-secret"; const result = resolveOrkesConfig({}); - expect(result.keyId).toBe("agentspan-key"); - expect(result.keySecret).toBe("agentspan-secret"); + expect(result.keyId).toBe("Conductor-key"); + expect(result.keySecret).toBe("conductor-secret"); }); - it("explicit config wins over AGENTSPAN_AUTH_KEY/SECRET", () => { - process.env.AGENTSPAN_AUTH_KEY = "agentspan-key"; + it("explicit config wins over CONDUCTOR_AGENT_AUTH_KEY/SECRET", () => { + process.env.CONDUCTOR_AGENT_AUTH_KEY = "Conductor-key"; const result = resolveOrkesConfig({ keyId: "explicit-key" }); expect(result.keyId).toBe("explicit-key"); }); - it("CONDUCTOR_AUTH_KEY wins over both explicit config and AGENTSPAN_AUTH_KEY", () => { + it("CONDUCTOR_AUTH_KEY wins over both explicit config and CONDUCTOR_AGENT_AUTH_KEY", () => { process.env.CONDUCTOR_AUTH_KEY = "conductor-key"; - process.env.AGENTSPAN_AUTH_KEY = "agentspan-key"; + process.env.CONDUCTOR_AGENT_AUTH_KEY = "Conductor-key"; const result = resolveOrkesConfig({ keyId: "explicit-key" }); expect(result.keyId).toBe("conductor-key"); }); diff --git a/src/sdk/createConductorClient/helpers/resolveOrkesConfig.ts b/src/sdk/createConductorClient/helpers/resolveOrkesConfig.ts index a5abed4c..ca039489 100644 --- a/src/sdk/createConductorClient/helpers/resolveOrkesConfig.ts +++ b/src/sdk/createConductorClient/helpers/resolveOrkesConfig.ts @@ -4,7 +4,7 @@ import { REFRESH_TOKEN_IN_MILLISECONDS, } from "../constants"; import type { OrkesApiConfig } from "../../types"; -import { DefaultLogger } from "../../helpers/logger"; +import { DefaultLogger, type ConductorLogger } from "../../helpers/logger"; /** * Parse an env var as a number, returning undefined if absent or NaN. @@ -21,15 +21,47 @@ const parseEnvBoolean = (value: string | undefined): boolean | undefined => { return value.toLowerCase() === "true" || value === "1"; }; +const legacyEnvWarned = new Set(); + +/** + * Read a `CONDUCTOR_AGENT_*` var, falling back to its deprecated `AGENTSPAN_*` + * spelling and warning once per legacy name. + * + * Duplicated rather than shared with `src/agents/legacy-env.ts` because the + * agent layer must not import from `src/sdk` outside `agent-client.ts` and + * `worker.ts` (see AGENTS.md). + */ +const agentEnv = (suffix: string, logger: ConductorLogger): string | undefined => { + const canonical = `CONDUCTOR_AGENT_${suffix}`; + const legacy = `AGENTSPAN_${suffix}`; + + const current = process.env[canonical]; + if (current !== undefined && current !== "") return current; + + const legacyValue = process.env[legacy]; + if (legacyValue === undefined || legacyValue === "") return undefined; + + if (!legacyEnvWarned.has(legacy)) { + legacyEnvWarned.add(legacy); + // ConductorLogger.warn is optional; fall back to info so the deprecation + // is never silently dropped by a custom logger. + const emit = logger.warn?.bind(logger) ?? logger.info.bind(logger); + emit( + `${legacy} is deprecated and will be removed in a future release. Use ${canonical} instead.`, + ); + } + return legacyValue; +}; + export const resolveOrkesConfig = (config?: Partial) => { const logger = config?.logger ?? new DefaultLogger(); - // R3: CONDUCTOR_* env -> explicit config -> AGENTSPAN_* env (agent-layer - // fallback) -> localhost:8080 default. + // R3: CONDUCTOR_* env -> explicit config -> CONDUCTOR_AGENT_* env (agent-layer + // fallback, with deprecated AGENTSPAN_* spelling) -> localhost:8080 default. let serverUrl = process.env.CONDUCTOR_SERVER_URL || config?.serverUrl || - process.env.AGENTSPAN_SERVER_URL || + agentEnv("SERVER_URL", logger) || "http://localhost:8080"; if (serverUrl.endsWith("/")) serverUrl = serverUrl.slice(0, -1); if (serverUrl.endsWith("/api")) serverUrl = serverUrl.slice(0, -4); @@ -38,13 +70,13 @@ export const resolveOrkesConfig = (config?: Partial) => { const keyId = ( process.env.CONDUCTOR_AUTH_KEY || config?.keyId || - process.env.AGENTSPAN_AUTH_KEY || + agentEnv("AUTH_KEY", logger) || "" ).trim(); const keySecret = ( process.env.CONDUCTOR_AUTH_SECRET || config?.keySecret || - process.env.AGENTSPAN_AUTH_SECRET || + agentEnv("AUTH_SECRET", logger) || "" ).trim(); diff --git a/src/sdk/helpers/logger.ts b/src/sdk/helpers/logger.ts index b4e66937..08fe9b28 100644 --- a/src/sdk/helpers/logger.ts +++ b/src/sdk/helpers/logger.ts @@ -30,9 +30,14 @@ export class DefaultLogger implements ConductorLogger { constructor(config: DefaultLoggerConfig = {}) { const {level, tags = []} = config this.tags = tags + // AGENTSPAN_LOG_LEVEL is the deprecated spelling of + // CONDUCTOR_AGENT_LOG_LEVEL. Unlike the other renamed vars this one falls + // back silently: warning here would mean logging through the very logger + // whose level is still being resolved. const resolvedLevel = level ?? (process.env.CONDUCTOR_LOG_LEVEL as ConductorLogLevel | undefined) ?? + (process.env.CONDUCTOR_AGENT_LOG_LEVEL as ConductorLogLevel | undefined) ?? (process.env.AGENTSPAN_LOG_LEVEL as ConductorLogLevel | undefined) if (resolvedLevel && resolvedLevel in LOG_LEVELS) { this.level = LOG_LEVELS[resolvedLevel]