subharness 0.0.5 → 0.0.7
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.
- package/README.md +59 -3
- package/dist/adapters/claude-process.js +8 -1
- package/dist/adapters/claude-process.js.map +1 -1
- package/dist/adapters/claude-tools.d.ts +6 -2
- package/dist/adapters/claude-tools.js +14 -12
- package/dist/adapters/claude-tools.js.map +1 -1
- package/dist/adapters/claude-worker-client.d.ts +49 -0
- package/dist/adapters/claude-worker-client.js +359 -0
- package/dist/adapters/claude-worker-client.js.map +1 -0
- package/dist/adapters/claude-worker-process.d.ts +20 -0
- package/dist/adapters/claude-worker-process.js +76 -0
- package/dist/adapters/claude-worker-process.js.map +1 -0
- package/dist/adapters/claude-worker-protocol.d.ts +38 -0
- package/dist/adapters/claude-worker-protocol.js +2 -0
- package/dist/adapters/claude-worker-protocol.js.map +1 -0
- package/dist/adapters/claude-worker.d.ts +1 -0
- package/dist/adapters/claude-worker.js +126 -0
- package/dist/adapters/claude-worker.js.map +1 -0
- package/dist/adapters/claude.d.ts +16 -6
- package/dist/adapters/claude.js +138 -68
- package/dist/adapters/claude.js.map +1 -1
- package/dist/cli/catalog-worker.js.map +1 -1
- package/dist/cli/dashboard-client.js +19 -30
- package/dist/cli/dashboard-client.js.map +1 -1
- package/dist/cli/main.js +53 -45
- package/dist/cli/main.js.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/approval-registry.d.ts +9 -0
- package/dist/runtime/approval-registry.js +143 -4
- package/dist/runtime/approval-registry.js.map +1 -1
- package/dist/runtime/capture.d.ts +22 -0
- package/dist/runtime/capture.js +320 -0
- package/dist/runtime/capture.js.map +1 -0
- package/dist/runtime/catalog.d.ts +39 -0
- package/dist/runtime/catalog.js +84 -0
- package/dist/runtime/catalog.js.map +1 -0
- package/dist/runtime/client.d.ts +2 -1
- package/dist/runtime/client.js +52 -65
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +44 -7
- package/dist/runtime/coordinator.js +265 -31
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/daemon.js +12 -6
- package/dist/runtime/daemon.js.map +1 -1
- package/dist/runtime/dashboard-workspace.js +5 -1
- package/dist/runtime/dashboard-workspace.js.map +1 -1
- package/dist/runtime/definition.d.ts +6 -1
- package/dist/runtime/definition.js +31 -1
- package/dist/runtime/definition.js.map +1 -1
- package/dist/runtime/native-owner.js +7 -1
- package/dist/runtime/native-owner.js.map +1 -1
- package/dist/runtime/pagination.d.ts +9 -0
- package/dist/runtime/pagination.js +36 -0
- package/dist/runtime/pagination.js.map +1 -0
- package/dist/runtime/sdk-observation.d.ts +8 -0
- package/dist/runtime/sdk-observation.js +75 -0
- package/dist/runtime/sdk-observation.js.map +1 -0
- package/dist/runtime/sdk-projection.d.ts +8 -0
- package/dist/runtime/sdk-projection.js +19 -0
- package/dist/runtime/sdk-projection.js.map +1 -0
- package/dist/runtime/sdk-service.d.ts +11 -0
- package/dist/runtime/sdk-service.js +109 -0
- package/dist/runtime/sdk-service.js.map +1 -0
- package/dist/runtime/select-native.d.ts +4 -1
- package/dist/runtime/select-native.js +14 -17
- package/dist/runtime/select-native.js.map +1 -1
- package/dist/runtime/service.d.ts +11 -1
- package/dist/runtime/service.js +178 -3
- package/dist/runtime/service.js.map +1 -1
- package/dist/runtime/session-launcher.js +1 -1
- package/dist/runtime/session-launcher.js.map +1 -1
- package/dist/runtime/snapshots.d.ts +103 -0
- package/dist/runtime/snapshots.js +124 -0
- package/dist/runtime/snapshots.js.map +1 -0
- package/dist/runtime/state.d.ts +26 -0
- package/dist/runtime/state.js +86 -17
- package/dist/runtime/state.js.map +1 -1
- package/dist/runtime/task-data.d.ts +38 -0
- package/dist/runtime/task-data.js +2 -0
- package/dist/runtime/task-data.js.map +1 -0
- package/dist/runtime/transport.d.ts +13 -0
- package/dist/runtime/transport.js +166 -0
- package/dist/runtime/transport.js.map +1 -0
- package/dist/runtime/types.d.ts +28 -9
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/worker-client.js +2 -0
- package/dist/runtime/worker-client.js.map +1 -1
- package/dist/runtime/worker-server.js +105 -24
- package/dist/runtime/worker-server.js.map +1 -1
- package/dist/sdk/connected-types.d.ts +42 -0
- package/dist/sdk/connected-types.js +2 -0
- package/dist/sdk/connected-types.js.map +1 -0
- package/dist/sdk/connected.d.ts +3 -0
- package/dist/sdk/connected.js +232 -0
- package/dist/sdk/connected.js.map +1 -0
- package/dist/sdk/execution-definition.d.ts +3 -0
- package/dist/sdk/execution-definition.js +59 -0
- package/dist/sdk/execution-definition.js.map +1 -0
- package/dist/sdk/execution-driver.d.ts +6 -0
- package/dist/sdk/execution-driver.js +86 -0
- package/dist/sdk/execution-driver.js.map +1 -0
- package/dist/sdk/execution-observation.d.ts +3 -0
- package/dist/sdk/execution-observation.js +35 -0
- package/dist/sdk/execution-observation.js.map +1 -0
- package/dist/sdk/execution-options.d.ts +17 -0
- package/dist/sdk/execution-options.js +115 -0
- package/dist/sdk/execution-options.js.map +1 -0
- package/dist/sdk/execution-types.d.ts +72 -0
- package/dist/sdk/execution-types.js +2 -0
- package/dist/sdk/execution-types.js.map +1 -0
- package/dist/sdk/execution.d.ts +3 -0
- package/dist/sdk/execution.js +3 -0
- package/dist/sdk/execution.js.map +1 -0
- package/dist/sdk/hosted-error.d.ts +3 -0
- package/dist/sdk/hosted-error.js +14 -0
- package/dist/sdk/hosted-error.js.map +1 -0
- package/dist/sdk/hosted.d.ts +23 -0
- package/dist/sdk/hosted.js +204 -0
- package/dist/sdk/hosted.js.map +1 -0
- package/dist/sdk/runner.d.ts +5 -0
- package/dist/sdk/runner.js +107 -0
- package/dist/sdk/runner.js.map +1 -0
- package/dist/sdk/tools.js +4 -1
- package/dist/sdk/tools.js.map +1 -1
- package/package.json +3 -3
- package/sdk/access-config.md +2 -0
- package/sdk/adapter-contract.md +8 -0
- package/sdk/agent.md +1 -1
- package/sdk/approvals.md +2 -0
- package/sdk/completion-notifications.md +2 -0
- package/sdk/distribution.md +4 -2
- package/sdk/examples/chat-tool.ts +126 -0
- package/sdk/execution.md +743 -0
- package/sdk/index.md +45 -21
- package/sdk/message-delivery.md +2 -0
- package/sdk/permissions.md +2 -0
- package/sdk/plugins/sub-agents.md +4 -2
- package/sdk/project-team.md +23 -1
- package/sdk/sessions.md +2 -0
- package/sdk/tools.md +3 -1
- package/sdk/v1-runtime.md +7 -5
- package/dist/cli/catalog.d.ts +0 -26
- package/dist/cli/catalog.js +0 -41
- package/dist/cli/catalog.js.map +0 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { AgentError } from '../errors.js';
|
|
2
|
+
import { Coordinator } from '../runtime/coordinator.js';
|
|
3
|
+
import { captureDefinition } from './execution-definition.js';
|
|
4
|
+
import { executionDriver } from './execution-driver.js';
|
|
5
|
+
import { delivery, observeOptions, runCwd, runnerConfiguration, validateCwd, validatePrompt, watchOptions } from './execution-options.js';
|
|
6
|
+
export function createRunner(options) { return createApplicationRunner(options); }
|
|
7
|
+
/** Internal test seam replacing only the external native adapter boundary. */
|
|
8
|
+
export function createRunnerWithNativeOpener(options, open) {
|
|
9
|
+
return createApplicationRunner(options, open);
|
|
10
|
+
}
|
|
11
|
+
function createApplicationRunner(options, open) {
|
|
12
|
+
const configuration = runnerConfiguration(options);
|
|
13
|
+
let coordinator;
|
|
14
|
+
coordinator = new Coordinator(input => executionDriver(coordinator, configuration.access, open)(input));
|
|
15
|
+
return new ApplicationRunner(coordinator, configuration.env);
|
|
16
|
+
}
|
|
17
|
+
class ApplicationRunner {
|
|
18
|
+
coordinator;
|
|
19
|
+
env;
|
|
20
|
+
accepting = true;
|
|
21
|
+
constructor(coordinator, env) {
|
|
22
|
+
this.coordinator = coordinator;
|
|
23
|
+
this.env = env;
|
|
24
|
+
}
|
|
25
|
+
async createSession(definition, options) {
|
|
26
|
+
this.assertAccepting();
|
|
27
|
+
const captured = captureDefinition(definition);
|
|
28
|
+
const cwd = await validateCwd(runCwd(options));
|
|
29
|
+
this.assertAccepting();
|
|
30
|
+
const id = this.coordinator.createSession({ reference: { kind: 'inline', definition: captured }, cwd, env: { ...this.env } });
|
|
31
|
+
return this.session(id);
|
|
32
|
+
}
|
|
33
|
+
session(id) {
|
|
34
|
+
this.coordinator.sessionSnapshot(id);
|
|
35
|
+
return new ApplicationSession(this, this.coordinator, id);
|
|
36
|
+
}
|
|
37
|
+
task(id) {
|
|
38
|
+
const snapshot = this.coordinator.snapshot(id);
|
|
39
|
+
return new ApplicationTask(this.coordinator, snapshot.taskId, snapshot.sessionId);
|
|
40
|
+
}
|
|
41
|
+
async respond(requestId, response) {
|
|
42
|
+
this.assertAccepting();
|
|
43
|
+
this.coordinator.respondSdk(requestId, response);
|
|
44
|
+
}
|
|
45
|
+
async close() { this.accepting = false; await this.coordinator.close(); }
|
|
46
|
+
assertAccepting() { if (!this.accepting)
|
|
47
|
+
throw new AgentError('RUNNER_CLOSED', 'The application runner is closing or closed.'); }
|
|
48
|
+
}
|
|
49
|
+
class ApplicationSession {
|
|
50
|
+
runner;
|
|
51
|
+
coordinator;
|
|
52
|
+
id;
|
|
53
|
+
constructor(runner, coordinator, id) {
|
|
54
|
+
this.runner = runner;
|
|
55
|
+
this.coordinator = coordinator;
|
|
56
|
+
this.id = id;
|
|
57
|
+
}
|
|
58
|
+
async prompt(text, options) {
|
|
59
|
+
this.runner.assertAccepting();
|
|
60
|
+
validatePrompt(text);
|
|
61
|
+
const requestedDelivery = delivery(options);
|
|
62
|
+
let receipt;
|
|
63
|
+
try {
|
|
64
|
+
receipt = await this.coordinator.send(this.id, text, requestedDelivery);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error instanceof AgentError && error.code === 'COORDINATOR_UNAVAILABLE')
|
|
68
|
+
throw new AgentError('RUNNER_CLOSED', 'The application runner is closing or closed.');
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
return { task: new ApplicationTask(this.coordinator, receipt.taskId, receipt.sessionId), requestedDelivery,
|
|
72
|
+
effectiveDelivery: receipt.delivery ?? requestedDelivery,
|
|
73
|
+
...(receipt.paused ? { paused: true, blockedByTaskId: receipt.blockedByTaskId } : {}) };
|
|
74
|
+
}
|
|
75
|
+
snapshot() { return this.coordinator.sdkSessionSnapshot(this.id); }
|
|
76
|
+
async close() { await this.coordinator.closeSession(this.id); }
|
|
77
|
+
}
|
|
78
|
+
class ApplicationTask {
|
|
79
|
+
coordinator;
|
|
80
|
+
id;
|
|
81
|
+
sessionId;
|
|
82
|
+
resultPromise;
|
|
83
|
+
constructor(coordinator, id, sessionId) {
|
|
84
|
+
this.coordinator = coordinator;
|
|
85
|
+
this.id = id;
|
|
86
|
+
this.sessionId = sessionId;
|
|
87
|
+
}
|
|
88
|
+
get result() {
|
|
89
|
+
if (!this.resultPromise) {
|
|
90
|
+
this.resultPromise = this.coordinator.taskResult(this.id);
|
|
91
|
+
void this.resultPromise.catch(() => undefined);
|
|
92
|
+
}
|
|
93
|
+
return this.resultPromise;
|
|
94
|
+
}
|
|
95
|
+
async snapshot() { return this.coordinator.sdkTaskSnapshot(this.id); }
|
|
96
|
+
async inspect() { return this.coordinator.taskDetails(this.id); }
|
|
97
|
+
watch(options) {
|
|
98
|
+
const selected = watchOptions(options);
|
|
99
|
+
return this.coordinator.watchTask(this.id, selected.signal);
|
|
100
|
+
}
|
|
101
|
+
responses(options) {
|
|
102
|
+
const selected = observeOptions(options);
|
|
103
|
+
return this.coordinator.taskResponses(this.id, selected.after, selected.signal);
|
|
104
|
+
}
|
|
105
|
+
async cancel() { await this.coordinator.cancel(this.id); return this.snapshot(); }
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/sdk/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import { AgentError } from '../errors.js';\nimport { Coordinator } from '../runtime/coordinator.js';\nimport type { ApprovalResponse } from '../runtime/task-data.js';\nimport type { TaskSnapshot } from './execution-types.js';\nimport { captureDefinition } from './execution-definition.js';\nimport { executionDriver, type NativeOpener } from './execution-driver.js';\nimport { delivery, observeOptions, runCwd, runnerConfiguration, validateCwd, validatePrompt, watchOptions } from './execution-options.js';\nimport type { Delivery, PromptReceipt, Runner, RunnerOptions, SessionHandle, TaskHandle } from './execution-types.js';\nimport type { AgentDefinition } from './types.js';\n\nexport function createRunner(options?:RunnerOptions):Runner { return createApplicationRunner(options); }\n/** Internal test seam replacing only the external native adapter boundary. */\nexport function createRunnerWithNativeOpener(options:RunnerOptions|undefined,open:NativeOpener):Runner {\n return createApplicationRunner(options,open);\n}\n\nfunction createApplicationRunner(options?:RunnerOptions,open?:NativeOpener):Runner {\n const configuration=runnerConfiguration(options);\n let coordinator!:Coordinator;\n coordinator=new Coordinator(input=>executionDriver(coordinator,configuration.access,open)(input));\n return new ApplicationRunner(coordinator,configuration.env);\n}\n\nclass ApplicationRunner implements Runner {\n private accepting=true;\n constructor(private readonly coordinator:Coordinator,private readonly env:NodeJS.ProcessEnv) {}\n\n async createSession(definition:AgentDefinition,options:{readonly cwd:string}):Promise<SessionHandle> {\n this.assertAccepting();\n const captured=captureDefinition(definition);\n const cwd=await validateCwd(runCwd(options));\n this.assertAccepting();\n const id=this.coordinator.createSession({reference:{kind:'inline',definition:captured},cwd,env:{...this.env}});\n return this.session(id);\n }\n session(id:string):SessionHandle {\n this.coordinator.sessionSnapshot(id);\n return new ApplicationSession(this,this.coordinator,id);\n }\n task(id:string):TaskHandle {\n const snapshot=this.coordinator.snapshot(id);\n return new ApplicationTask(this.coordinator,snapshot.taskId,snapshot.sessionId);\n }\n async respond(requestId:string,response:ApprovalResponse):Promise<void> {\n this.assertAccepting(); this.coordinator.respondSdk(requestId,response);\n }\n async close():Promise<void> { this.accepting=false; await this.coordinator.close(); }\n assertAccepting() { if (!this.accepting) throw new AgentError('RUNNER_CLOSED','The application runner is closing or closed.'); }\n}\n\nclass ApplicationSession implements SessionHandle {\n constructor(private readonly runner:ApplicationRunner,private readonly coordinator:Coordinator,readonly id:string) {}\n async prompt(text:string,options?:{readonly delivery?:Delivery}):Promise<PromptReceipt> {\n this.runner.assertAccepting(); validatePrompt(text);\n const requestedDelivery=delivery(options);\n let receipt;\n try { receipt=await this.coordinator.send(this.id,text,requestedDelivery); }\n catch(error) {\n if (error instanceof AgentError && error.code === 'COORDINATOR_UNAVAILABLE')\n throw new AgentError('RUNNER_CLOSED','The application runner is closing or closed.');\n throw error;\n }\n return {task:new ApplicationTask(this.coordinator,receipt.taskId,receipt.sessionId),requestedDelivery,\n effectiveDelivery:receipt.delivery ?? requestedDelivery,\n ...(receipt.paused ? {paused:true as const,blockedByTaskId:receipt.blockedByTaskId!} : {})};\n }\n snapshot() { return this.coordinator.sdkSessionSnapshot(this.id); }\n async close() { await this.coordinator.closeSession(this.id); }\n}\n\nclass ApplicationTask implements TaskHandle {\n private resultPromise?:Promise<import('./execution-types.js').TaskResult>;\n constructor(private readonly coordinator:Coordinator,readonly id:string,readonly sessionId:string) {}\n get result() {\n if (!this.resultPromise) {\n this.resultPromise=this.coordinator.taskResult(this.id);\n void this.resultPromise.catch(()=>undefined);\n }\n return this.resultPromise;\n }\n async snapshot() { return this.coordinator.sdkTaskSnapshot(this.id); }\n async inspect() { return this.coordinator.taskDetails(this.id); }\n watch(options?:{readonly signal?:AbortSignal}) {\n const selected=watchOptions(options); return this.coordinator.watchTask(this.id,selected.signal);\n }\n responses(options?:{readonly after?:string;readonly signal?:AbortSignal}) {\n const selected=observeOptions(options); return this.coordinator.taskResponses(this.id,selected.after,selected.signal);\n }\n async cancel():Promise<TaskSnapshot> { await this.coordinator.cancel(this.id); return this.snapshot(); }\n}\n"]}
|
package/dist/sdk/tools.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AgentError } from "../errors.js";
|
|
3
3
|
import { richToolResult } from "./tool-result.js";
|
|
4
|
+
import { isHostedError } from "./hosted-error.js";
|
|
4
5
|
import { invalid, nonempty, object } from "./validation.js";
|
|
5
6
|
export { toolResult } from "./tool-result.js";
|
|
6
7
|
export function tool(definition) {
|
|
@@ -38,7 +39,9 @@ export async function executeTool(definition, input) {
|
|
|
38
39
|
try {
|
|
39
40
|
result = await definition.execute(parsed.data);
|
|
40
41
|
}
|
|
41
|
-
catch {
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (isHostedError(error))
|
|
44
|
+
throw error;
|
|
42
45
|
throw new AgentError("TOOL_EXECUTION_FAILED", "The custom tool failed during execution.");
|
|
43
46
|
}
|
|
44
47
|
let rich;
|
package/dist/sdk/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/sdk/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,cAAc,EAA2B,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,MAAM,UAAU,IAAI,CAAwB,UAA6B;IACvE,YAAY,CAAC,UAAU,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAAmB;IAC9C,MAAM,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;IAC9D,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;IACrD,IAAI,CAAC,CAAC,UAAU,CAAC,WAAW,YAAY,CAAC,CAAC,SAAS,CAAC;QAAE,OAAO,CAAC,sCAAsC,CAAC,CAAC;IACtG,IAAI,OAAO,UAAU,CAAC,OAAO,KAAK,UAAU;QAAE,OAAO,CAAC,kCAAkC,CAAC,CAAC;IAC1F,cAAc,CAAC,UAAuC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAA+B;IAC5D,IAAI,CAAC;QACH,OAAO,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC,yDAAyD,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAA+B,EAAE,KAAc;IAC/E,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC5D,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,kDAAkD,CAAC,CAAC;IACjG,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/sdk/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport { AgentError } from \"../errors.js\";\nimport type { ToolDefinition } from \"./types.js\";\nimport { richToolResult, type ExecutedToolResult } from \"./tool-result.js\";\nimport { isHostedError } from \"./hosted-error.js\";\nimport { invalid, nonempty, object } from \"./validation.js\";\n\nexport { toolResult } from \"./tool-result.js\";\nexport type { ToolContent, ToolResult } from \"./tool-result.js\";\n\nexport function tool<S extends z.ZodObject>(definition: ToolDefinition<S>): ToolDefinition<S> {\n validateTool(definition);\n return Object.freeze({ ...definition });\n}\n\nexport function validateTool(definition: unknown): asserts definition is ToolDefinition<any> {\n object(definition, [\"description\", \"inputSchema\", \"execute\"]);\n nonempty(definition.description, \"Tool description\");\n if (!(definition.inputSchema instanceof z.ZodObject)) invalid(\"Tools require a Zod 4 object schema.\");\n if (typeof definition.execute !== \"function\") invalid(\"Tool execute must be a function.\");\n toolJsonSchema(definition as unknown as ToolDefinition);\n}\n\nexport function toolJsonSchema(definition: ToolDefinition<any>): Record<string, unknown> {\n try {\n return z.toJSONSchema(definition.inputSchema, { io: \"input\" });\n } catch {\n return invalid(\"Tool input schema cannot be represented as JSON Schema.\");\n }\n}\n\nexport async function executeTool(definition: ToolDefinition<any>, input: unknown): Promise<ExecutedToolResult> {\n let parsed;\n try {\n parsed = await definition.inputSchema.safeParseAsync(input);\n if (!parsed.success) throw new Error();\n } catch {\n throw new AgentError(\"TOOL_INPUT_INVALID\", \"Tool arguments do not match the declared schema.\");\n }\n let result: unknown;\n try {\n result = await definition.execute(parsed.data);\n } catch (error) {\n if (isHostedError(error)) throw error;\n throw new AgentError(\"TOOL_EXECUTION_FAILED\", \"The custom tool failed during execution.\");\n }\n let rich;\n try {\n rich = richToolResult(result);\n } catch (error) {\n if (error instanceof AgentError) throw error;\n throw new AgentError(\"TOOL_RESULT_INVALID\", \"Tool returned invalid rich content.\");\n }\n if (rich) return { kind: \"content\", content: rich.content };\n let text: string | undefined;\n try {\n text = typeof result === \"string\" ? result : JSON.stringify(result, (_key, value: unknown) => {\n if ([\"undefined\", \"function\", \"symbol\", \"bigint\"].includes(typeof value)\n || (typeof value === \"number\" && !Number.isFinite(value))) throw new Error();\n return value;\n });\n } catch {\n throw new AgentError(\"TOOL_RESULT_INVALID\", \"Tool result must be a string or JSON-serializable value.\");\n }\n if (text === undefined) throw new AgentError(\"TOOL_RESULT_INVALID\", \"Tool result must be a string or JSON-serializable value.\");\n if (Buffer.byteLength(text, \"utf8\") > 1024 * 1024) throw new AgentError(\"TOOL_RESULT_TOO_LARGE\", \"Tool result exceeds 1 MiB.\");\n return text;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "subharness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.18.0"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"errore": "0.14.1",
|
|
36
36
|
"typescript": "7.0.2"
|
|
37
37
|
},
|
|
38
|
-
"description": "Run native coding agents
|
|
38
|
+
"description": "Run native coding agents from application code or a local CLI, with sessions and optional specialists.",
|
|
39
39
|
"license": "Apache-2.0",
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "tsc -p tsconfig.build.json && node --input-type=module -e \"import { chmodSync } from 'node:fs'; chmodSync('dist/cli/main.js', 0o755)\"",
|
|
54
54
|
"typecheck": "tsc --noEmit",
|
|
55
|
-
"test": "node --import tsx --test test/*.test.ts",
|
|
55
|
+
"test": "node --import tsx --test --test-concurrency=2 test/*.test.ts",
|
|
56
56
|
"test:evals": "node --import tsx --test test/eval-*.test.ts",
|
|
57
57
|
"evals": "node --import tsx evals/main.ts",
|
|
58
58
|
"check": "pnpm run build && pnpm run typecheck && pnpm test",
|
package/sdk/access-config.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Personal Access Configuration
|
|
2
2
|
|
|
3
|
+
Embedded [execution SDK](execution.md) runners accept explicit `RunnerOptions.access` and an environment snapshot. They use the connection rules here without implicitly loading personal settings or modifying Git excludes. Connected sessions use the same project-based personal settings discovery as the CLI, with the caller's captured environment.
|
|
4
|
+
|
|
3
5
|
The optional `.subharness/agents.local.json` file selects access independently of versioned agent definitions. In linked Git worktrees it is read from the main checkout. Without Git, it is read from the execution project root. Global definitions still use the selected project's access preferences.
|
|
4
6
|
|
|
5
7
|
```json
|
package/sdk/adapter-contract.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Native Adapter Contract
|
|
2
2
|
|
|
3
|
+
The [execution SDK](execution.md) reuses native adapters. Embedded runners use an in-process session driver and host-owned custom tools; connected clients use coordinator-owned captured definitions and native sessions. It exposes complete responses and task snapshots, without promising a native token/tool-progress stream.
|
|
4
|
+
|
|
3
5
|
Adapters create native Codex, Claude Code, fx, OpenCode, GitHub Copilot, or Cursor conversations in the caller-supplied directory. Their implementation interface is internal and is not an SDK extension API. A session supplies `turn(prompt)`, `steer(prompt)`, `interrupt()`, `resume()`, and `close()`. A turn resolves with its complete text; machine-reported execution failures reject with a stable error code. The Cursor subscription ACP route has the documented limitation that some native backend failures arrive only as assistant text. Interrupt resolves only when active execution has stopped. Unsupported operations return explicit capability errors. Harness-specific contracts are defined for [fx](fx.md), [OpenCode](opencode.md), [Copilot](copilot.md), and [Cursor](cursor.md).
|
|
4
6
|
|
|
5
7
|
[Error diagnostics](diagnostics.md) defines startup failure classification, safe native failure categories, and process termination context. Unexpected Claude account-inspection or model-catalog exceptions are harness failures, not evidence that another access route should be tried.
|
|
@@ -14,6 +16,12 @@ Agent instructions supplement native instructions. The adapter exposes declared
|
|
|
14
16
|
|
|
15
17
|
Codex uses its native App Server protocol. Claude Code uses its native Agent SDK with the installed Claude executable. A Claude session accepts queued native turns and interruption. Its native steering method reports `UNSUPPORTED_DELIVERY` before any mutation; the coordinator converts that request to the documented interrupt operation and reports the effective delivery mode. Codex steering targets the active native turn. Neither adapter synthesizes native recovery by replaying the original prompt; unavailable recovery returns `RECOVERY_UNSUPPORTED`.
|
|
16
18
|
|
|
19
|
+
Adapter selection loads only the adapter being attempted. Claude's Agent SDK runs in a private worker for each session because its module initialization can change process environment variables. The worker receives a copy of the captured session environment and never shares the application's `process.env`. This boundary is internal and does not change the public session or tool interfaces.
|
|
20
|
+
|
|
21
|
+
The host retains the original tool schemas, closures, validation, and callback admission and draining. The worker receives tool descriptions and JSON schemas and forwards calls to those original host tools; it does not reconstruct application schemas or execute application callbacks. Native approval requests, answers, and withdrawals cross the same private boundary without changing offered permissions or admitting stale answers. No environment or credential values enter bridge diagnostics.
|
|
22
|
+
|
|
23
|
+
The host owns Claude's concrete native subprocess and private configuration cleanup, including when the worker fails. The SDK's custom process hook bridges native streams and process events to the worker with backpressure. Closing confirms native termination and drains admitted callbacks before reporting completion; terminating the worker alone is insufficient. A close requested during startup waits for that attempt to settle and closes any resulting session without submitting a turn; it does not impose a startup deadline. Interruption confirms that the active native turn has stopped and drains its admitted callbacks while retaining the session. Cleanup failures retain ownership for retry. Unexpected worker or bridge failures are execution failures, not reasons to replay input or switch providers.
|
|
24
|
+
|
|
17
25
|
Personal subscription selection must verify native subscription access. Ambient API keys, alternate endpoints, provider overrides, and native API-key helpers must not silently change the selected billing method. Explicit API or Gateway selections supply only the selected credential route. Credentials are never included in CLI records or diagnostic output.
|
|
18
26
|
|
|
19
27
|
Unsupported approval and interactive-input diagnostics use `INPUT_REQUIRED`. Claude diagnostics may identify a bounded native tool name; Codex diagnostics identify the recognized fixed approval method and category. Diagnostics do not expose raw tool arguments, settings or credentials. Supported approval requests expose the bounded action context needed for the caller's decision under the [approval contract](approvals.md), separately from error diagnostics.
|
package/sdk/agent.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Defining an Agent
|
|
2
2
|
|
|
3
|
-
Import definitions from `subharness`. The helpers are synchronous declarations: they do not start harnesses, authenticate, call models, or execute tools. Execution uses the CLI.
|
|
3
|
+
Import definitions from `subharness`. The helpers are synchronous declarations: they do not start harnesses, authenticate, call models, or execute tools. Execution uses either the CLI or the application-owned [execution SDK](execution.md).
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { agent, codex, claudeCode } from "subharness";
|
package/sdk/approvals.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Responding to Native Permission Requests
|
|
2
2
|
|
|
3
|
+
The [execution SDK](execution.md#unified-approval-types) exposes normalized approval actions through task snapshots. `runner.respond` and `client.approvals.respond` select an advertised action ID, with explicit permission grants when required. They resolve the same retained requests as the native-schema CLI and hosted-tool flow below. These are separate response formats: SDK callers use `{ actionId, grants? }`; CLI and hosted tools use request-specific native schema content. Neither response completes or restarts the task. The internal native approval callback remains adapter-facing.
|
|
4
|
+
|
|
3
5
|
A native tool permission request suspends the operation that needs authorization while its original native turn stays open. Subharness returns a structured request to the observing caller. The caller can answer the request and then observe the same task. Permission handling never replays a prompt, creates a replacement task, changes the selected native policy, or automatically approves an operation. Independent native operations and sibling tasks may continue.
|
|
4
6
|
|
|
5
7
|
## Observe and respond
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Responses and Task Completion
|
|
2
2
|
|
|
3
|
+
The [execution SDK](execution.md) provides complete-response cursors and task snapshot observation directly to application code. It does not provide token deltas or native tool-progress events. UI disconnects detach observers. Embedded runner lifetime is application-owned; shared coordinator work survives connected-client disconnect.
|
|
4
|
+
|
|
3
5
|
Returning a response, completing a task, and starting another caller model turn are separate operations. The CLI returns control after each complete native response from the requested agent. It includes task/session identity, response identity, and the task state. It does not continuously forward token streams, native tool events, or descendants' transcripts.
|
|
4
6
|
|
|
5
7
|
The adapter captures complete responses automatically. Agents do not need a reporting tool, special JSON format, or a classifier that recognizes questions. A question is delivered through the same response mechanism as any other text.
|
package/sdk/distribution.md
CHANGED
|
@@ -10,7 +10,9 @@ Node.js 22.18 or newer is required. The SDK is ESM and includes TypeScript decla
|
|
|
10
10
|
|
|
11
11
|
The public npm package is `subharness`, with an initial release version of `0.0.1`. Install the CLI globally with `npm install --global subharness`, or run it without a global installation using `npx subharness`. Registry installation does not require access to the internal source repository. The root package is publishable; the documentation workspace remains private.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Application backends install `subharness` with `npm install subharness` and import the [execution SDK](execution.md). Native executables remain external dependencies; the package does not require a globally installed Subharness CLI for SDK execution.
|
|
14
|
+
|
|
15
|
+
For project-local CLI use, install `subharness` with `npm install --save-dev subharness` and invoke its CLI with `npx subharness`. This also makes SDK imports resolve from repository agent definitions. A global CLI installation alone does not make SDK imports resolve from project-local definitions. Global definitions need an SDK dependency reachable from their own directory under normal Node package resolution.
|
|
14
16
|
|
|
15
17
|
Source development uses pnpm 11.20.0, pinned in the root `packageManager` field. `pnpm-workspace.yaml` includes the root SDK and `apps/docs`, with one committed `pnpm-lock.yaml`. Run `pnpm install --frozen-lockfile` from the repository root, then `pnpm run build`. Run the local CLI with `node dist/cli/main.js`; this requires no global link. Direct harness targets do not need a consumer SDK dependency. For use in another project, build a tarball with `pnpm pack` and install that tarball in the consumer so its agent definitions can resolve SDK imports.
|
|
16
18
|
|
|
@@ -22,7 +24,7 @@ A tarball built from a source checkout can differ from the registry release with
|
|
|
22
24
|
|
|
23
25
|
Packages built from this source contain the built JavaScript, declaration files and source maps under `dist/`, SDK Markdown documentation under `sdk/`, the README, the Apache License 2.0, and package metadata. Source maps embed the original TypeScript so debuggers can display it without a separate source checkout. Website code, development agents, skills, source assets, tests, `.context`, personal settings, and environment files are excluded.
|
|
24
26
|
|
|
25
|
-
Packing builds the SDK from the current source before assembling its files. The CLI's `--version` output matches the package version. A clean consumer must be able to import the SDK and discover a TypeScript agent definition using the installed executable without access to the source checkout or a paid model call. `pnpm run check:package` packs with pnpm and verifies this clean-consumer behavior and the packaged file boundary without running a coding model. It requires network access to install dependencies from the public npm registry and disables install lifecycle scripts in the temporary consumer.
|
|
27
|
+
Packing builds the SDK from the current source before assembling its files. The CLI's `--version` output matches the package version. A clean consumer must be able to import the execution SDK, run an inline definition against a controlled native protocol fixture, and discover a TypeScript agent definition using the installed executable without access to the source checkout or a paid model call. `pnpm run check:package` packs with pnpm and verifies this clean-consumer behavior and the packaged file boundary without running a coding model. It requires network access to install dependencies from the public npm registry and disables install lifecycle scripts in the temporary consumer.
|
|
26
28
|
|
|
27
29
|
The committed lockfile pins dependency content with integrity hashes and does not embed company registry URLs. pnpm resolves packages through the configured registry, which defaults to the public npm registry. Installation and package verification do not change the developer's global npm registry configuration. Registry authentication remains with npm.
|
|
28
30
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRunner,
|
|
3
|
+
type AgentDefinition,
|
|
4
|
+
type ApprovalResponse,
|
|
5
|
+
type CompleteResponse,
|
|
6
|
+
type PromptReceipt,
|
|
7
|
+
type RunnerOptions,
|
|
8
|
+
type SessionHandle,
|
|
9
|
+
type TaskHandle,
|
|
10
|
+
type TaskResult,
|
|
11
|
+
type TaskSnapshot,
|
|
12
|
+
} from "subharness";
|
|
13
|
+
|
|
14
|
+
type StringField = Readonly<{
|
|
15
|
+
type: "string";
|
|
16
|
+
description: string;
|
|
17
|
+
minLength: number;
|
|
18
|
+
}>;
|
|
19
|
+
|
|
20
|
+
interface HostTool<Input, Output> {
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly inputSchema: Readonly<{
|
|
23
|
+
type: "object";
|
|
24
|
+
properties: Readonly<Record<keyof Input, StringField>>;
|
|
25
|
+
required: readonly (keyof Input)[];
|
|
26
|
+
additionalProperties: false;
|
|
27
|
+
}>;
|
|
28
|
+
execute(input: Input): Promise<Output>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ChatToolApplication {
|
|
32
|
+
readonly consultation: HostTool<{ prompt: string }, ObservedTask>;
|
|
33
|
+
readonly followup: HostTool<{ prompt: string }, ObservedTask>;
|
|
34
|
+
responses(taskId: string, options?: { readonly after?: string; readonly signal?: AbortSignal }): AsyncIterable<CompleteResponse>;
|
|
35
|
+
watch(taskId: string, options?: { readonly signal?: AbortSignal }): AsyncIterable<TaskSnapshot>;
|
|
36
|
+
answerApproval(requestId: string, response: ApprovalResponse): Promise<void>;
|
|
37
|
+
cancel(taskId: string): Promise<TaskSnapshot>;
|
|
38
|
+
dispose(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ObservedTask {
|
|
42
|
+
readonly taskId: string;
|
|
43
|
+
readonly sessionId: string;
|
|
44
|
+
readonly result: TaskResult;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createChatToolApplication(options: {
|
|
48
|
+
readonly definition: AgentDefinition;
|
|
49
|
+
/** An existing absolute directory, validated by the runner when consultation starts. */
|
|
50
|
+
readonly cwd: string;
|
|
51
|
+
readonly runner?: RunnerOptions;
|
|
52
|
+
/** Called at task admission so the host can expose approval, observation, and stop controls immediately. */
|
|
53
|
+
readonly onTask?: (task: TaskHandle) => void;
|
|
54
|
+
}): ChatToolApplication {
|
|
55
|
+
const runner = createRunner(options.runner);
|
|
56
|
+
const tasks = new Map<string, TaskHandle>();
|
|
57
|
+
let session: SessionHandle | undefined;
|
|
58
|
+
let sessionStarting = false;
|
|
59
|
+
|
|
60
|
+
const ownedTask = (taskId: string): TaskHandle => {
|
|
61
|
+
const task = tasks.get(taskId);
|
|
62
|
+
if (!task) throw new Error("The task does not belong to this chat execution scope.");
|
|
63
|
+
return task;
|
|
64
|
+
};
|
|
65
|
+
const remember = (task: TaskHandle): TaskHandle => {
|
|
66
|
+
tasks.set(task.id, task);
|
|
67
|
+
return task;
|
|
68
|
+
};
|
|
69
|
+
const observeTask = async (task: TaskHandle): Promise<ObservedTask> => {
|
|
70
|
+
const owned = remember(task);
|
|
71
|
+
options.onTask?.(owned);
|
|
72
|
+
return {taskId: owned.id, sessionId: owned.sessionId, result: await owned.result};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
consultation: {
|
|
77
|
+
description: "Consult the configured coding agent.",
|
|
78
|
+
inputSchema: promptSchema("The self-contained task and context for the specialist."),
|
|
79
|
+
execute: async ({ prompt }) => {
|
|
80
|
+
if (session || sessionStarting) throw new Error("This chat scope already has a specialist session; use followup.");
|
|
81
|
+
sessionStarting = true;
|
|
82
|
+
try {
|
|
83
|
+
const created = await runner.createSession(options.definition, { cwd: options.cwd });
|
|
84
|
+
let admitted: PromptReceipt;
|
|
85
|
+
try { admitted = await created.prompt(prompt); }
|
|
86
|
+
catch (error) { await created.close(); throw error; }
|
|
87
|
+
session = created;
|
|
88
|
+
return observeTask(admitted.task);
|
|
89
|
+
} finally {
|
|
90
|
+
sessionStarting = false;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
followup: {
|
|
95
|
+
description: "Send a follow-up to the retained specialist session and observe its task.",
|
|
96
|
+
inputSchema: promptSchema("Additional context or a follow-up request for the specialist."),
|
|
97
|
+
execute: async ({ prompt }) => {
|
|
98
|
+
if (!session) throw new Error("Start a consultation before sending a follow-up.");
|
|
99
|
+
const sent = await session.prompt(prompt);
|
|
100
|
+
return observeTask(sent.task);
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
responses: (taskId, responseOptions) => ownedTask(taskId).responses(responseOptions),
|
|
104
|
+
watch: (taskId, watchOptions) => ownedTask(taskId).watch(watchOptions),
|
|
105
|
+
// The host must authenticate this route and submit an action offered by the request.
|
|
106
|
+
// Resolution only acknowledges the answer; keep observing to learn the outcome.
|
|
107
|
+
answerApproval: (requestId, content) => runner.respond(requestId, content),
|
|
108
|
+
cancel: taskId => ownedTask(taskId).cancel(),
|
|
109
|
+
dispose: () => runner.close(),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function promptSchema(description: string) {
|
|
114
|
+
return {
|
|
115
|
+
type: "object" as const,
|
|
116
|
+
properties: { prompt: { type: "string" as const, description, minLength: 1 } },
|
|
117
|
+
required: ["prompt"] as const,
|
|
118
|
+
additionalProperties: false as const,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Keep one factory instance per authenticated, bounded chat/job scope. The host schedules
|
|
123
|
+
// its next chat turn after receiving the result. Complete response replay can include `waiting`
|
|
124
|
+
// and task completion alone does not prove the caller's objective succeeded.
|
|
125
|
+
// Passing an AbortSignal to responses/watch disconnects only that reader. Use cancel for an
|
|
126
|
+
// explicit Stop action, and dispose when the whole scope ends (not on browser disconnect).
|