Skip to content

Merge the Agentspan agent SDK into conductor-javascript#131

Open
kowser-orkes wants to merge 13 commits into
mainfrom
feature/combine-agentspan
Open

Merge the Agentspan agent SDK into conductor-javascript#131
kowser-orkes wants to merge 13 commits into
mainfrom
feature/combine-agentspan

Conversation

@kowser-orkes

@kowser-orkes kowser-orkes commented Jul 9, 2026

Copy link
Copy Markdown

What

Folds the Agentspan TypeScript SDK (@conductor-oss/conductor-agent-sdk 0.3.0, upstream
f8704fa3) into this package as a durable AI-agent layer, exposed only via new
subpath exports — one install, one package:

import { Agent, AgentRuntime, tool } from "@io-orkes/conductor-javascript/agents";
Export Contents
./agents Agent, AgentRuntime, tool, guardrails, handoffs, multi-agent strategies (223 names)
./agents/testing mock/eval test harness
./agents/vercel-ai, ./agents/langgraph, ./agents/langchain framework wrappers (optional peer deps)

The root export surface is byte-identical to main (309 names) — existing users are
unaffected. Agent-layer deps (zod, ai, @langchain/*) are optional peers; installing
the package without them keeps working.

What's included

  • src/agents/ — agent runtime, verbatim from upstream except recorded deltas
    (import seam to ../sdk/../open-api, lint conformance, package rebrand)
  • 39 unit-test suites ported vitest → jest (full upstream parity), 25 e2e suites
  • examples/agents/ (100+ runnable examples) + docs/agents/
  • New agent-e2e CI workflow: boots the released Agentspan server JAR (0.4.0) +
    mcp-testkit per PR and runs the e2e suites against real LLMs
    (needs OPENAI_API_KEY / ANTHROPIC_API_KEY repo secrets)
  • Build: tsup multi-entry (ESM + CJS + dts) with a verify:dist gate that loads every
    subpath in both formats

Notable decisions

  • Default AGENTSPAN_SERVER_URL is http://localhost:8080/api — aligned with the merged
    Python SDK (upstream Agentspan defaulted to 6767).
  • AGENTSPAN_* env vars kept as-is; CONDUCTOR_* aliases are a possible follow-up.
  • Migration from @conductor-oss/conductor-agent-sdk: change the import to
    @io-orkes/conductor-javascript/agents — API unchanged.

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
integration-v4-sm 41.79% <ø> (-22.43%) ⬇️
integration-v5-sdkdev 44.27% <ø> (-24.54%) ⬇️
unit 73.35% <ø> (+22.39%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 44 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

kowser-orkes pushed a commit that referenced this pull request Jul 9, 2026
actions/setup-python's `cache: pip` hard-fails when the repo has no
requirements.txt or pyproject.toml to hash for the cache key — true for
this repo, where Python exists only to run mcp-testkit. Carried over
from the Python SDK's agent-e2e workflow, where a pyproject.toml exists.

First observed on PR #131: the job died in "Set up Python" before any
test ran, and the silently-empty guard correctly reported 0/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kowser-orkes kowser-orkes force-pushed the feature/combine-agentspan branch 2 times, most recently from df8aa64 to d43dea6 Compare July 9, 2026 02:53
@kowser-orkes kowser-orkes changed the title Feature/combine agentspan Merge the Agentspan agent SDK into conductor-javascript Jul 9, 2026
Kowser and others added 9 commits July 9, 2026 12:41
…orts

Merge the Agentspan TypeScript SDK source (@conductor-oss/conductor-agent-sdk
0.3.0, agentspan@f8704fa3) into this package as src/agents/, published only
via subpath exports: ./agents, ./agents/testing, ./agents/vercel-ai,
./agents/langgraph, ./agents/langchain. The root export surface is
byte-identical to before (verified via compiler API), so the one name
collision (Action) never materializes.

Changes beyond the verbatim copy:
- agent-client.ts / worker.ts: import from ../sdk and ../open-api instead of
  the package name (the only coupling to the workflow layer)
- tool.ts: rename the createRequire binding to peerRequire (top-level
  `require` is reserved in this repo's CJS module flavor)
- lint conformance to this repo's strict+stylistic ruleset (~41 fixes:
  no-op arrows, guard-throws replacing non-null assertions,
  Reflect.deleteProperty, for-of); no-explicit-any and
  no-unsafe-function-type stay at upstream's own "warn" contract via a
  scoped eslint override
- tsup.config.ts replaces the inline package.json block: 6 entries,
  esm+cjs+dts; optional peers external; verify:dist gate loads every
  exports subpath in both formats
- package.json: dotenv to dependencies (import-time side effect in
  config.ts), 5 optional peerDependencies, sideEffects allowlists
  dist/agents/**, tsx/zod/zod-to-json-schema devDeps
- tsconfig: lib es2022 -> es2023 (stream.ts uses Array.findLast; superset,
  Node >= 18 supports it), package-name paths for in-repo resolution

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
38 of upstream's 41 unit/cli-bin suites move to src/agents/__tests__/, where
the existing test:unit glob and per-PR CI matrix pick them up with zero
workflow changes. Test-count parity with upstream vitest is exact:
761 = 860 - 99 in the three deferred files. Deferred:
- kitchen-sink-structural.test.ts (imports examples/kitchen-sink; lands with
  the examples in the next commit)
- validation/{algorithmic,event-audit}.test.ts (they test the validation/
  LLM-judge harness, whose migration is explicitly deferred)

Port mechanics: vitest imports -> @jest/globals, vi.* -> jest.*,
toBeTypeOf/toHaveBeenCalledOnce -> jest equivalents, vi.stubGlobal -> local
helpers/stub-global.ts (upstream never unstubs; per-file isolation contains
it), relative ../../src paths -> colocated paths, skills fixtures colocated
under __tests__/fixtures/. The two jest.mock("child_process") factories
hoist correctly under ts-jest (verified green).

cli-bin/ lands at repo root (the Go CLI walk-up probe expects <dir>/cli-bin)
with imports retargeted to src/agents/. deploy.ts gets a type-only fix for
its workflowName read (upstream never typechecked cli-bin; runtime shape
unchanged).

jest.config.mjs: package-name mappers to in-repo sources plus the standard
ESM .js-suffix strip for relative imports. eslint: upstream's warn contract
extended to cli-bin; stylistic-only relaxations scoped to the migrated tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
221 example files (top-level + quickstart + adk/langgraph/openai/vercel-ai
framework subdirs) land at examples/agents/, the 6 agent docs at docs/agents/
(sidestepping the README and api-reference path collisions). All imports and
install instructions rewritten from @conductor-oss/conductor-agent-sdk to
@io-orkes/conductor-javascript (/agents subpaths), including the runtime
error-message strings in src/agents wrappers — zero old-name references
remain outside git history.

Run story: top-level/quickstart examples resolve the package name straight to
src/agents via examples/agents/tsconfig.json paths (npx tsx, no install);
framework subdirs install their own deps (scripts/install-example-deps.sh,
nonexistent langchain/ entry dropped) and gain an explicit
file:../../.. SDK dep; langgraph/ also gains the @langchain deps it
silently inherited from upstream's deleted examples workspace manifest.

Also lands the deferred kitchen-sink-structural.test.ts (66 tests; unit suite
now 1500 green) and a type-only fix upstream never caught because examples
were never typechecked: schedules-api.ts runNow's first overload makes opts
optional, matching the documented 1-arg default usage.

Root tsconfig excludes examples/agents (framework subdir deps are not
installed at the root); eslint gains a scoped override for the four rules the
didactic files break en masse — upstream ignored examples/ in lint entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 25 upstream e2e suites move to e2e/ (with fixtures/, _configs/, and the
loose harness scripts under e2e/tools/), run by jest.e2e.config.mjs:
testMatch e2e/**, 60s default timeout, maxWorkers 3 (upstream's maxForks 3),
jest-junit to results/junit-e2e.xml. New script: npm run test:agent-e2e
(--forceExit, matching the repo's other jest scripts — agent polling keeps
handles open after LLM suites). Deliberately invisible to
test/test:unit/test:integration globs — per-PR CI cost is unchanged
(verified: unit run still 74 suites).

Beyond the mechanical vitest-to-jest pass, six vitest-isms needed real
porting (verified by loading all 25 suites under jest — 196 tests enumerate
clean — and by running suites 1/2/9 against a live release-JAR server):
- expect(actual, message) two-arg form (265 sites, jest 30 rejects it) ->
  expectMsg helper in e2e/helpers.ts that prepends the diagnostic message
  to matcher failures
- describe(name, { timeout }, fn) options object (19 files) -> file-level
  jest.setTimeout
- it.skipIf(cond) (19 sites) -> itSkipIf helper
- it.fails -> it.failing (suite 7)
- it(name, { retry: 2 }, fn) (4 sites) -> file-level jest.retryTimes(2)
  (jest has no per-test retry; both files are LLM-variability suites)
- top-level await gates (suites 11/21/23; illegal in CJS) -> synchronous
  require for the langgraph availability probe, and fail-hard beforeAll
  health/scheduler gates (matches the TS-e2e fail-hard philosophy; the CI
  workflow health-gates the server before tests run)

Live-server proof (local V1): suites 1/2/9 against agentspan-server 0.4.0 —
11 tests pass; the only failures are server-side OpenAI 401s from the
missing local LLM key (the CI run with repo secrets is the full proof).

Unused-var noise (port artifacts + upstream) fixed with _-prefixes and
import trims; the stylistic eslint relaxations for migrated tests extend to
e2e/**. jest-junit output dirs (reports/, results/) are now gitignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Port of the Python SDK's implemented agent-e2e workflow. Kept verbatim:
pull_request + workflow_dispatch triggers, per-ref concurrency with
cancel-in-progress, pinned AGENTSPAN_VERSION 0.4.0 (server JAR from the
GitHub release, cached by version; linux CLI binary), temurin 21, hard
90s health gate that dumps server.log on failure, executed>0 junit guard
(defense-in-depth here — the TS suites fail hard when the server is down),
junit + HTML + server.log artifacts at 14-day retention, 45-min timeout,
job-level OPENAI_API_KEY/ANTHROPIC_API_KEY secrets (must be added to this
repo's settings before the first live run).

JS deltas: Node 22 + npm ci + npm run build + npm run test:agent-e2e as the
test step; Python setup retained only for mcp-testkit; HTML report via
npx tsx e2e/generate-report.ts; no CONDUCTOR_MP_START_METHOD (Python
multiprocessing concern, no JS analog).

Rehearsed locally: actionlint clean; health gate passed against the booted
0.4.0 JAR; guard correctly fails an all-skipped junit (0/196) and the
report generator renders the real junit output. The test:agent-e2e script
pins jest-junit's output to results/junit-e2e.xml via env vars — the
package.json jest-junit block (reports/jest-junit.xml) outranks reporter
options, and env vars outrank the block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
README: new Durable AI Agents section (subpath quickstart mirroring
examples/agents/quickstart/01-basic-agent.ts, wrapper/testing subpaths,
docs + examples pointers) and a Documentation-table row.

AGENTS.md: src/agents feature area — repo layout additions (src/agents,
e2e/, cli-bin/, examples/agents, docs/agents), the subpath-only export rule
(root re-export ban and the OpenAPI Action collision that motivates it),
colocated-test and e2e conventions, example run story, AGENTSPAN_* config
surface, and the agent-e2e command.

CHANGELOG: additive-minor entry with a migration note for
@conductor-oss/conductor-agent-sdk users (specifier rewrite, /agents-prefixed
wrapper subpaths, optional peers, dotenv/sideEffects behavior).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
actions/setup-python's `cache: pip` hard-fails when the repo has no
requirements.txt or pyproject.toml to hash for the cache key — true for
this repo, where Python exists only to run mcp-testkit. Carried over
from the Python SDK's agent-e2e workflow, where a pyproject.toml exists.

First observed on PR #131: the job died in "Set up Python" before any
test ran, and the silently-empty guard correctly reported 0/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Stage-4 lint pass blanket-prefixed suite 10's diag and codeOutputs
locals as unused, but most scopes still reference the bare names inside
expectMsg messages and output assertions — ReferenceError at runtime.
ts-jest is transpile-only, so it surfaced only in the first secrets-
enabled CI run (6 failures, the run's only red).

Only genuinely-unused declarations keep the underscore (upstream parity;
upstream never linted e2e). Verified live: suite 10 vs local server JAR
0.4.0 with real keys — 8 passed, 1 skipped (Jupyter, needs kernel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kowser-orkes kowser-orkes force-pushed the feature/combine-agentspan branch from 4141a1e to 6c75d48 Compare July 9, 2026 19:41
Kowser and others added 4 commits July 9, 2026 16:30
… agent lifecycle surface

Per-schedule pause/resume verbs differ by server family: OSS/embedded
Conductor maps them PUT-only, Orkes Conductor GET-only. SchedulerClient
now issues PUT first and retries via GET only on HTTP 405, stateless per
call, with the pause reason re-applied on the fallback URL (mirrors the
Python SDK's OrkesSchedulerClient). Implemented in the hand-written
wrapper via raw non-throwing client calls — the generated transport is
untouched and regeneration stays safe.

SchedulerClient also absorbs the typed agent-schedule lifecycle surface
(save/get/listForAgent/pause/resume/delete/runNow/previewNext/reconcile)
operating on Schedule/ScheduleInfo with wire-name prefixing and typed
errors via _translate. The models/mapping/errors relocate verbatim from
src/agents/schedule.ts to src/sdk/clients/agent/schedule.ts (Python
parity: conductor/client/ai/schedule.py); the agents module re-exports
them. Ctor widened to Client | PromiseLike<Client> so callers with async
client construction keep synchronous accessors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e SchedulerClient

BREAKING CHANGE (agent layer, no backward compatibility): the raw-fetch
ScheduleClient class and SchedulerFetcher interface are gone from the
./agents surface. SchedulerClient — which absorbed the same typed
lifecycle methods (save/get/listForAgent/pause/resume/delete/runNow/
previewNext/reconcile) in the previous commit — is re-exported from
./agents in their place; every method call site keeps its name and
signature. The scheduling e2e suite needed exactly two lines (import +
one type annotation); the schedules.* module facade, runtime
schedulesClient(), deploy({schedules}) reconciliation and
AgentClient.schedule() are unchanged.

AgentClient.schedules now hands its memoized Conductor-client promise to
SchedulerClient (single transport; the SchedulerFetcher raw-fetch path is
deleted, _rawRequestUntyped remains for /agent/* control-plane calls).
An absence test pins the deletion, mirroring the Python SDK's
test_agent_schedule_client_is_gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…posed via OrkesClients

Moves the agent control-plane client and the agent-flavored workflow
client from src/agents/ to src/sdk/clients/agent/ (names kept — nothing
on the root surface collides: Conductor's workflow client is
WorkflowExecutor). OrkesClients gains getAgentClient() and
getAgentWorkflowClient(); AgentClient's constructor accepts an optional
pre-built client ((AgentConfigOptions & {client?}) | AgentConfig) which
pre-seeds the memoized promise, so the factory path reuses one Conductor
client across workflow + schedule surfaces. The /agent/* raw transport
and JWT mint path are unchanged; decodeJwtExp stays exported.

Cycle guard: the moved files import createConductorClient and the agent
domain modules via deep paths, never the sdk barrel; madge reports no
new cycles (the AgentClient↔WorkflowClient type-only edge predates the
move). The ./agents subpath re-exports everything from the new homes —
its surface is unchanged. The enriched ConductorClient type is not
re-exported at root (the deprecated bare alias keeps that name).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CHANGELOG (Unreleased): OrkesClients agent getters, PUT-first/405→GET
pause-resume verb handling, absorbed typed schedule lifecycle, and the
ScheduleClient/SchedulerFetcher removal with its one-line migration.
Agent docs re-point the schedule surface at SchedulerClient and note the
WorkflowExecutor vs agent WorkflowClient distinction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant