@autonome-research/thread-phase-agents 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +207 -0
  3. package/dist/acp/adapter.d.ts +89 -0
  4. package/dist/acp/adapter.d.ts.map +1 -0
  5. package/dist/acp/adapter.js +461 -0
  6. package/dist/acp/adapter.js.map +1 -0
  7. package/dist/acp/index.d.ts +12 -0
  8. package/dist/acp/index.d.ts.map +1 -0
  9. package/dist/acp/index.js +10 -0
  10. package/dist/acp/index.js.map +1 -0
  11. package/dist/acp/transport.d.ts +82 -0
  12. package/dist/acp/transport.d.ts.map +1 -0
  13. package/dist/acp/transport.js +199 -0
  14. package/dist/acp/transport.js.map +1 -0
  15. package/dist/acp/types.d.ts +157 -0
  16. package/dist/acp/types.d.ts.map +1 -0
  17. package/dist/acp/types.js +34 -0
  18. package/dist/acp/types.js.map +1 -0
  19. package/dist/anthropic/adapter.d.ts +55 -0
  20. package/dist/anthropic/adapter.d.ts.map +1 -0
  21. package/dist/anthropic/adapter.js +259 -0
  22. package/dist/anthropic/adapter.js.map +1 -0
  23. package/dist/anthropic/index.d.ts +8 -0
  24. package/dist/anthropic/index.d.ts.map +1 -0
  25. package/dist/anthropic/index.js +8 -0
  26. package/dist/anthropic/index.js.map +1 -0
  27. package/dist/claude-code/adapter.d.ts +56 -0
  28. package/dist/claude-code/adapter.d.ts.map +1 -0
  29. package/dist/claude-code/adapter.js +319 -0
  30. package/dist/claude-code/adapter.js.map +1 -0
  31. package/dist/claude-code/index.d.ts +8 -0
  32. package/dist/claude-code/index.d.ts.map +1 -0
  33. package/dist/claude-code/index.js +8 -0
  34. package/dist/claude-code/index.js.map +1 -0
  35. package/dist/codex/adapter.d.ts +62 -0
  36. package/dist/codex/adapter.d.ts.map +1 -0
  37. package/dist/codex/adapter.js +258 -0
  38. package/dist/codex/adapter.js.map +1 -0
  39. package/dist/codex/index.d.ts +9 -0
  40. package/dist/codex/index.d.ts.map +1 -0
  41. package/dist/codex/index.js +9 -0
  42. package/dist/codex/index.js.map +1 -0
  43. package/dist/codex-cli/adapter.d.ts +70 -0
  44. package/dist/codex-cli/adapter.d.ts.map +1 -0
  45. package/dist/codex-cli/adapter.js +368 -0
  46. package/dist/codex-cli/adapter.js.map +1 -0
  47. package/dist/codex-cli/index.d.ts +10 -0
  48. package/dist/codex-cli/index.d.ts.map +1 -0
  49. package/dist/codex-cli/index.js +10 -0
  50. package/dist/codex-cli/index.js.map +1 -0
  51. package/dist/hermes/index.d.ts +50 -0
  52. package/dist/hermes/index.d.ts.map +1 -0
  53. package/dist/hermes/index.js +43 -0
  54. package/dist/hermes/index.js.map +1 -0
  55. package/dist/index.d.ts +11 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +14 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/injectors.d.ts +104 -0
  60. package/dist/injectors.d.ts.map +1 -0
  61. package/dist/injectors.js +219 -0
  62. package/dist/injectors.js.map +1 -0
  63. package/dist/openclaw/index.d.ts +54 -0
  64. package/dist/openclaw/index.d.ts.map +1 -0
  65. package/dist/openclaw/index.js +61 -0
  66. package/dist/openclaw/index.js.map +1 -0
  67. package/dist/pi/adapter.d.ts +79 -0
  68. package/dist/pi/adapter.d.ts.map +1 -0
  69. package/dist/pi/adapter.js +396 -0
  70. package/dist/pi/adapter.js.map +1 -0
  71. package/dist/pi/index.d.ts +14 -0
  72. package/dist/pi/index.d.ts.map +1 -0
  73. package/dist/pi/index.js +14 -0
  74. package/dist/pi/index.js.map +1 -0
  75. package/dist/thread-bridge.d.ts +83 -0
  76. package/dist/thread-bridge.d.ts.map +1 -0
  77. package/dist/thread-bridge.js +143 -0
  78. package/dist/thread-bridge.js.map +1 -0
  79. package/package.json +46 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Code4me2
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # @autonome-research/thread-phase-agents
2
+
3
+ Adapter implementations for the `AgentAdapter` protocol from [`@autonome-research/thread-phase`](../thread-phase). Wraps heterogeneous AI agents — CLI-based coding agents, in-process SDK agents — behind a single uniform shape so thread-phase pipelines can compose them.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @autonome-research/thread-phase-agents @autonome-research/thread-phase
9
+ ```
10
+
11
+ `@autonome-research/thread-phase` is a peer dependency. Each adapter has its own dependency on whichever SDK or CLI it wraps:
12
+
13
+ | Adapter | Requires |
14
+ |---|---|
15
+ | `acpAgent` (chassis) | An ACP-speaking subprocess on your machine |
16
+ | `hermesAgent` | `hermes` CLI in PATH (https://github.com/...) |
17
+ | `openClawAgent` | `acpx` in PATH; optional NemoClaw sandbox |
18
+ | `claudeCodeAgent` | `claude` CLI in PATH (https://claude.com/code) |
19
+ | `codexCliAgent` | `codex` CLI in PATH (`codex login` for auth) |
20
+ | `codexAgent` | `OPENAI_API_KEY` env var (Responses API direct) |
21
+ | `anthropicAgent` | `ANTHROPIC_API_KEY` env var |
22
+ | `piAgent` | `@mariozechner/pi-coding-agent` (already bundled), pi config under `~/.pi/agent/` |
23
+
24
+ ## What's in here
25
+
26
+ Seven adapter implementations plus a shared ACP chassis:
27
+
28
+ - **`acpAgent`** — the [Agent Client Protocol](https://agentclientprotocol.com/) chassis. Spawns any ACP-speaking subprocess, parses JSON-RPC over stdio, drives `initialize → session/new → session/prompt → session/cancel`. Other ACP-based adapters compose on top.
29
+ - **`hermesAgent`** — wraps `hermes acp`. Inherits the ACP chassis.
30
+ - **`openClawAgent`** — wraps `acpx` against the OpenClaw Gateway, with optional `sandbox: 'nemoclaw' | 'local'` mode.
31
+ - **`anthropicAgent`** — in-process via `@anthropic-ai/sdk`. Streaming + tool use + extended thinking.
32
+ - **`codexAgent`** — in-process via OpenAI Responses API. Requires `OPENAI_API_KEY`.
33
+ - **`codexCliAgent`** — subprocess wrapper around `codex exec --json`. Uses codex's own auth (ChatGPT subscription OAuth typically).
34
+ - **`claudeCodeAgent`** — subprocess + JSONL streaming. Forgiving parser falls back to `native` events for unknown shapes.
35
+ - **`piAgent`** — in-process via `@mariozechner/pi-coding-agent`. The only adapter where `SteerableAgentRun.steer()` and `.followUp()` work natively at runtime (pi accepts mid-stream steering).
36
+
37
+ ## Using an adapter
38
+
39
+ The basic shape is the same for every adapter — call `meta.adapter(config)` to get an `AgentRun`, then iterate events and/or await the result:
40
+
41
+ ```ts
42
+ import { hermesAgent } from '@autonome-research/thread-phase-agents';
43
+
44
+ const run = hermesAgent.adapter({
45
+ cwd: process.cwd(),
46
+ prompt: 'List the files in this directory and summarize what they are.',
47
+ });
48
+
49
+ // Stream events for display / logging:
50
+ for await (const event of run.events) {
51
+ if (event.type === 'text') process.stdout.write(event.delta);
52
+ }
53
+
54
+ // Await the final result:
55
+ const result = await run.result;
56
+ console.log('finishReason:', result.finishReason);
57
+ console.log('text:', result.text);
58
+ console.log('resumeToken:', result.resumeToken);
59
+ ```
60
+
61
+ `run.result` never rejects — errors are encoded as `finishReason: 'error'` with an `error` event in the stream beforehand. `run.events` is a single-consumer iterable; use the `AgentEventBus` from `@autonome-research/thread-phase/agents` if you need fan-out to multiple subscribers.
62
+
63
+ ### Inside a thread-phase phase
64
+
65
+ Adapters compose with thread-phase's pipeline primitives. The canonical pattern:
66
+
67
+ ```ts
68
+ import { JobRunner, SqliteJobStore, type Phase } from '@autonome-research/thread-phase';
69
+ import { createEventBus, pipeAgentEventsToJobStore } from '@autonome-research/thread-phase/agents';
70
+ import { claudeCodeAgent } from '@autonome-research/thread-phase-agents';
71
+
72
+ interface Ctx {
73
+ taskDescription?: string;
74
+ result?: string;
75
+ }
76
+
77
+ const reviewPhase: Phase<Ctx> = {
78
+ name: 'review',
79
+ async *run(ctx, { jobId, store, signal }) {
80
+ yield { type: 'phase', phase: 'review', detail: 'starting claude-code' };
81
+
82
+ const bus = createEventBus();
83
+ pipeAgentEventsToJobStore(bus, store, jobId, { dropTypes: ['text'] });
84
+
85
+ const run = claudeCodeAgent.adapter(
86
+ { cwd: process.cwd(), prompt: ctx.taskDescription! },
87
+ { signal, eventBus: bus, traceId: jobId },
88
+ );
89
+
90
+ const result = await run.result;
91
+ ctx.result = result.text;
92
+ yield { type: 'data', key: 'result', value: { length: result.text.length } };
93
+ },
94
+ };
95
+ ```
96
+
97
+ `pipeAgentEventsToJobStore` wires the adapter's event stream into the JobStore log so every text delta / tool call / turn boundary is persisted. The `dropTypes: ['text']` filter keeps the high-volume text deltas out of the database while preserving tool calls, turn boundaries, and lifecycle events for audit.
98
+
99
+ ### Memory and Thread auto-wiring
100
+
101
+ Decorate any adapter with `withMemory` to plumb a `MemoryProvider` automatically, or `withThread` to flow conversation state across phases. Use the pre-built injectors from this package so you don't have to write per-adapter splicing logic:
102
+
103
+ ```ts
104
+ import { withMemory, withThread, createThread } from '@autonome-research/thread-phase/agents';
105
+ import { claudeCodeAgent, injectMemory, injectResume } from '@autonome-research/thread-phase-agents';
106
+
107
+ const thread = createThread();
108
+
109
+ const augmented = withThread(
110
+ withMemory(claudeCodeAgent, {
111
+ scope: { userId: 'alice' },
112
+ inject: injectMemory.claudeCode,
113
+ }),
114
+ thread,
115
+ { applyResume: injectResume.claudeCode },
116
+ );
117
+
118
+ // First call — creates a session, fills thread.events.
119
+ await augmented.adapter({ cwd, prompt: 'analyze this codebase' }, { memoryProvider }).result;
120
+
121
+ // Second call — same thread; the wrapper reads thread.resumeTokens['claude-code']
122
+ // and adds --resume <id> automatically.
123
+ await augmented.adapter({ cwd, prompt: 'now refactor the file you mentioned' }, { memoryProvider }).result;
124
+ ```
125
+
126
+ ### Steerable runs (pi, hermes, openclaw)
127
+
128
+ Adapters whose underlying runtime supports follow-up prompts on a live session return a `SteerableAgentRun` at runtime. Narrow with `isSteerable` from `thread-phase/agents`:
129
+
130
+ ```ts
131
+ import { isSteerable } from '@autonome-research/thread-phase/agents';
132
+ import { piAgent } from '@autonome-research/thread-phase-agents';
133
+
134
+ const run = piAgent.adapter({ cwd, prompt: 'start something complex' });
135
+
136
+ if (isSteerable(run)) {
137
+ // Pi accepts mid-stream steering — interrupt the current generation
138
+ // and add context.
139
+ await run.steer('reconsider, the user just clarified X');
140
+
141
+ // Or queue a follow-up that fires after the current response completes:
142
+ await run.followUp('and then also summarize');
143
+ }
144
+
145
+ const result = await run.result;
146
+ ```
147
+
148
+ - `piAgent` supports both `steer()` (mid-generation injection) and `followUp()` (queued additional turn).
149
+ - `hermesAgent` / `openClawAgent` / `acpAgent` support `followUp()` (ACP's `session/prompt` is discrete — multiple prompts on one session). `steer()` rejects with a capability error.
150
+ - All other adapters (`claudeCodeAgent`, `codexCliAgent`, `codexAgent`, `anthropicAgent`) are not steerable. `isSteerable(run)` returns `false`.
151
+
152
+ ### Cross-adapter handoff with Thread
153
+
154
+ When two phases use different adapters, the canonical event log in a shared `Thread` becomes the bridge. Same-adapter chains resume natively via the resume token (lossless); cross-adapter chains fall back to a text rendering of the thread:
155
+
156
+ ```ts
157
+ import { createThread } from '@autonome-research/thread-phase/agents';
158
+ import {
159
+ claudeCodeAgent,
160
+ anthropicAgent,
161
+ threadToAnthropicMessages,
162
+ } from '@autonome-research/thread-phase-agents';
163
+
164
+ const thread = createThread();
165
+
166
+ // Phase A: claude-code does research.
167
+ const runA = withThread(claudeCodeAgent, thread, { applyResume: injectResume.claudeCode });
168
+ await runA.adapter({ cwd, prompt: 'investigate the bug in foo.ts' }).result;
169
+
170
+ // Phase B: anthropic synthesizes a report from claude-code's findings.
171
+ // Different adapter — render the thread to anthropic message format.
172
+ const messages = threadToAnthropicMessages(thread);
173
+ const runB = anthropicAgent.adapter({
174
+ model: 'claude-opus-4-7',
175
+ messages: [...messages, { role: 'user', content: 'Write a 3-bullet summary.' }],
176
+ });
177
+ await runB.result;
178
+ ```
179
+
180
+ The `threadTo<Adapter>{Prompt,Messages,Input}` helpers render the shared event log into each adapter's expected input shape.
181
+
182
+ ## Smoke scripts
183
+
184
+ Each adapter has a runnable real-binary smoke test under `scripts/`:
185
+
186
+ ```bash
187
+ npx tsx scripts/smoke-claude-code.ts "say hello"
188
+ npx tsx scripts/smoke-hermes.ts
189
+ npx tsx scripts/smoke-codex-cli.ts
190
+ npx tsx scripts/smoke-pi.ts
191
+ ```
192
+
193
+ Useful for sanity-checking that the adapter survives contact with whichever version of the binary you have installed. They print every canonical event as it streams and report pass/fail at the end.
194
+
195
+ ## Relationship to @autonome-research/thread-phase
196
+
197
+ [`@autonome-research/thread-phase`](../thread-phase) owns the `AgentAdapter` protocol, the `Thread` primitive, the canonical `AgentEvent` vocabulary, the `inferenceAgent` (which wraps `runAgentWithTools` against any OpenAI-compatible endpoint), and the conformance suite that every adapter must pass.
198
+
199
+ This package ships the *other* adapters — the ones that delegate to pre-built coding / research / life agents rather than driving a raw inference loop. Each adapter passes the conformance suite imported from `@autonome-research/thread-phase/agents/test-utils`.
200
+
201
+ ## Status
202
+
203
+ Pre-1.0. The adapter set covers the common heterogeneous-agent surface; the API shape may still change before 1.0.
204
+
205
+ ## License
206
+
207
+ MIT.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * ACP chassis — `AgentAdapter` that consumes any ACP-speaking subprocess.
3
+ *
4
+ * Lifecycle: spawn → initialize → session/new (or session/load on resume)
5
+ * → session/prompt → stream session/update notifications → await
6
+ * stopReason → emit agent_end → dispose.
7
+ *
8
+ * Translation table:
9
+ * session/update { sessionUpdate: 'agent_message_chunk' } → text event
10
+ * session/update { sessionUpdate: 'agent_thought_chunk' } → thinking event
11
+ * session/update { sessionUpdate: 'tool_call' } → tool_call event
12
+ * session/update { sessionUpdate: 'tool_call_update', status: 'completed' }
13
+ * → tool_result event
14
+ * everything else → native event
15
+ *
16
+ * Cancellation: forceful. abort() sends session/cancel as a notification,
17
+ * then SIGTERM after a short grace window, then SIGKILL. Subprocess
18
+ * cleanup is unconditional on result resolution.
19
+ *
20
+ * Resumption: 'opaque' — the ACP session id is wrapped in a
21
+ * `ResumeToken{ kind: 'opaque', data: sessionId }` and emitted on
22
+ * agent_start (when input resume) and agent_end (always).
23
+ *
24
+ * @internal
25
+ */
26
+ import { type AgentAdapterMeta, type AgentRunResult } from '@autonome-research/thread-phase/agents';
27
+ import { type ContentBlock, type Implementation, type McpServer, type ClientCapabilities } from './types.js';
28
+ import { JsonRpcCallError } from './transport.js';
29
+ /** @internal */
30
+ export interface AcpAgentConfig {
31
+ /** Subprocess executable. */
32
+ command: string;
33
+ /** Arguments. */
34
+ args?: string[];
35
+ /** Working directory advertised to the agent at session/new. Required by ACP. */
36
+ cwd: string;
37
+ /** Subprocess working directory; defaults to `cwd`. */
38
+ subprocessCwd?: string;
39
+ /** Spawn env overrides. Merged with process.env. */
40
+ env?: Record<string, string>;
41
+ /** The prompt. String is wrapped into a single text content block. */
42
+ prompt: string | ContentBlock[];
43
+ /** MCP servers passed to session/new. Default: []. */
44
+ mcpServers?: McpServer[];
45
+ /** Client info for the initialize handshake. */
46
+ clientInfo?: Implementation;
47
+ /** Client capabilities declared at initialize. */
48
+ clientCapabilities?: ClientCapabilities;
49
+ /**
50
+ * If set, the chassis calls session/load instead of session/new and
51
+ * the agent resumes from this id. Requires the agent to declare
52
+ * `loadSession: true` in its capabilities; if it doesn't, an error
53
+ * event is emitted and the run ends with finishReason: 'error'.
54
+ */
55
+ resumeSessionId?: string;
56
+ /**
57
+ * How to respond to session/request_permission. Default 'deny' for
58
+ * safety — coding agents calling destructive tools shouldn't proceed
59
+ * silently unless the caller has opted in.
60
+ */
61
+ permissionMode?: 'allow' | 'deny';
62
+ /**
63
+ * Grace period in ms between session/cancel and SIGTERM on abort.
64
+ * Default 2000.
65
+ */
66
+ cancelGraceMs?: number;
67
+ /**
68
+ * Grace period in ms between SIGTERM and SIGKILL when the subprocess
69
+ * refuses to exit. Default 3000.
70
+ */
71
+ killGraceMs?: number;
72
+ }
73
+ /** @internal */
74
+ export interface CreateAcpAdapterOptions {
75
+ /** Stable id, surfaced on every event's `source`. Default 'acp'. */
76
+ id?: string;
77
+ }
78
+ /**
79
+ * Build an ACP-speaking adapter with the given id. The chassis itself
80
+ * is exported as `acpAgent` (id 'acp'); wrappers like `hermesAgent` and
81
+ * `openClawAgent` are produced by calling this with their own id.
82
+ *
83
+ * @internal
84
+ */
85
+ export declare function createAcpAdapter(opts?: CreateAcpAdapterOptions): AgentAdapterMeta<AcpAgentConfig>;
86
+ /** The unparameterized chassis adapter. @internal */
87
+ export declare const acpAgent: AgentAdapterMeta<AcpAgentConfig, AgentRunResult>;
88
+ export { JsonRpcCallError };
89
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/acp/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,EAOL,KAAK,gBAAgB,EAIrB,KAAK,cAAc,EAEpB,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,cAAc,EAInB,KAAK,SAAS,EAQd,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAyC,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEzF,gBAAgB;AAChB,MAAM,WAAW,cAAc;IAC7B,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,sEAAsE;IACtE,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAAC;IAChC,sDAAsD;IACtD,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,gDAAgD;IAChD,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,gBAAgB;AAChB,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,GAAE,uBAA4B,GACjC,gBAAgB,CAAC,cAAc,CAAC,CAYlC;AAED,qDAAqD;AACrD,eAAO,MAAM,QAAQ,kDAAkC,CAAC;AAucxD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}