subharness 0.0.1 → 0.0.3
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/LICENSE +201 -21
- package/README.md +51 -18
- package/dist/adapters/claude-process.d.ts +10 -0
- package/dist/adapters/claude-process.js +58 -0
- package/dist/adapters/claude-process.js.map +1 -0
- package/dist/adapters/claude.js +52 -10
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex-input.d.ts +2 -0
- package/dist/adapters/codex-input.js +14 -0
- package/dist/adapters/codex-input.js.map +1 -0
- package/dist/adapters/codex-permissions.d.ts +13 -0
- package/dist/adapters/codex-permissions.js +30 -0
- package/dist/adapters/codex-permissions.js.map +1 -0
- package/dist/adapters/codex.js +12 -6
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/fx-auth.js +3 -0
- package/dist/adapters/fx-auth.js.map +1 -1
- package/dist/adapters/fx-permissions.d.ts +3 -0
- package/dist/adapters/fx-permissions.js +128 -0
- package/dist/adapters/fx-permissions.js.map +1 -0
- package/dist/adapters/fx.js +4 -0
- package/dist/adapters/fx.js.map +1 -1
- package/dist/cli/args.d.ts +3 -1
- package/dist/cli/args.js +18 -12
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/dashboard-client.d.ts +3 -0
- package/dist/cli/dashboard-client.js +78 -0
- package/dist/cli/dashboard-client.js.map +1 -0
- package/dist/cli/dashboard-layout.d.ts +21 -0
- package/dist/cli/dashboard-layout.js +211 -0
- package/dist/cli/dashboard-layout.js.map +1 -0
- package/dist/cli/dashboard-renderer.d.ts +30 -0
- package/dist/cli/dashboard-renderer.js +80 -0
- package/dist/cli/dashboard-renderer.js.map +1 -0
- package/dist/cli/dashboard.d.ts +31 -0
- package/dist/cli/dashboard.js +81 -0
- package/dist/cli/dashboard.js.map +1 -0
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +35 -9
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +19 -6
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/output.js +10 -1
- package/dist/cli/output.js.map +1 -1
- package/dist/config/access.js +4 -4
- package/dist/config/access.js.map +1 -1
- package/dist/config/loader.js +2 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/runtime/client.d.ts +5 -1
- package/dist/runtime/client.js +91 -40
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +7 -1
- package/dist/runtime/coordinator.js +72 -8
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/daemon.js +100 -18
- package/dist/runtime/daemon.js.map +1 -1
- package/dist/runtime/dashboard-workspace.d.ts +2 -0
- package/dist/runtime/dashboard-workspace.js +38 -0
- package/dist/runtime/dashboard-workspace.js.map +1 -0
- package/dist/runtime/dashboard.d.ts +28 -0
- package/dist/runtime/dashboard.js +19 -0
- package/dist/runtime/dashboard.js.map +1 -0
- package/dist/runtime/readiness.d.ts +4 -0
- package/dist/runtime/readiness.js +17 -0
- package/dist/runtime/readiness.js.map +1 -0
- package/dist/runtime/select-native.d.ts +2 -1
- package/dist/runtime/select-native.js +5 -3
- package/dist/runtime/select-native.js.map +1 -1
- package/dist/runtime/service.d.ts +1 -1
- package/dist/runtime/service.js +10 -3
- package/dist/runtime/service.js.map +1 -1
- package/dist/runtime/startup-lock.d.ts +5 -0
- package/dist/runtime/startup-lock.js +37 -0
- package/dist/runtime/startup-lock.js.map +1 -0
- package/dist/runtime/startup-protocol.d.ts +17 -0
- package/dist/runtime/startup-protocol.js +23 -0
- package/dist/runtime/startup-protocol.js.map +1 -0
- package/dist/runtime/state.d.ts +1 -2
- package/dist/runtime/state.js +8 -43
- package/dist/runtime/state.js.map +1 -1
- package/dist/runtime/types.d.ts +16 -2
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/worker-client.d.ts +7 -2
- package/dist/runtime/worker-client.js +69 -12
- package/dist/runtime/worker-client.js.map +1 -1
- package/dist/runtime/worker-server.d.ts +7 -0
- package/dist/runtime/worker-server.js +101 -0
- package/dist/runtime/worker-server.js.map +1 -0
- package/dist/runtime/worker.js +2 -66
- package/dist/runtime/worker.js.map +1 -1
- package/dist/sdk/definitions.js +23 -13
- package/dist/sdk/definitions.js.map +1 -1
- package/dist/sdk/permission-validation.d.ts +4 -0
- package/dist/sdk/permission-validation.js +55 -0
- package/dist/sdk/permission-validation.js.map +1 -0
- package/dist/sdk/types.d.ts +14 -0
- package/dist/sdk/types.js.map +1 -1
- package/package.json +17 -17
- package/sdk/access-config.md +4 -2
- package/sdk/adapter-contract.md +14 -2
- package/sdk/agent-skill.md +38 -0
- package/sdk/agent.md +6 -2
- package/sdk/cli/dashboard-design.md +40 -0
- package/sdk/cli/index.md +47 -13
- package/sdk/cli/output.md +21 -3
- package/sdk/completion-notifications.md +9 -5
- package/sdk/config.md +33 -8
- package/sdk/distribution.md +21 -7
- package/sdk/evals.md +171 -0
- package/sdk/fx.md +4 -3
- package/sdk/harnesses.md +6 -0
- package/sdk/index.md +2 -0
- package/sdk/message-delivery.md +6 -2
- package/sdk/permissions.md +65 -0
- package/sdk/plugins/sub-agents.md +7 -4
- package/sdk/pr-integration.md +37 -0
- package/sdk/project-team.md +13 -8
- package/sdk/sessions.md +1 -1
- package/sdk/tools.md +2 -0
- package/sdk/v1-runtime.md +22 -7
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createWorker } from './worker-client.js';
|
|
3
|
+
import { stateDirectory } from './state.js';
|
|
4
|
+
export async function closeStartupSession(open) {
|
|
5
|
+
const native = await open();
|
|
6
|
+
await native.close();
|
|
7
|
+
}
|
|
8
|
+
export async function checkReadiness(reference, cwd, env) {
|
|
9
|
+
await closeStartupSession(() => createWorker({
|
|
10
|
+
reference,
|
|
11
|
+
cwd,
|
|
12
|
+
env: { ...env, AGENT_STATE_DIR: stateDirectory() },
|
|
13
|
+
token: randomUUID(),
|
|
14
|
+
sessionId: `check_${randomUUID()}`,
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../../src/runtime/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAkC;IAC1E,MAAM,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;IAC5B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAA6B,EAAE,GAAW,EAAE,GAAsB;IACrG,MAAM,mBAAmB,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;QAC3C,SAAS;QACT,GAAG;QACH,GAAG,EAAE,EAAC,GAAG,GAAG,EAAE,eAAe,EAAE,cAAc,EAAE,EAAC;QAChD,KAAK,EAAE,UAAU,EAAE;QACnB,SAAS,EAAE,SAAS,UAAU,EAAE,EAAE;KACnC,CAAC,CAAC,CAAC;AACN,CAAC","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { ExecutionReference } from './types.js';\nimport { createWorker } from './worker-client.js';\nimport { stateDirectory } from './state.js';\nimport type { NativeSession } from '../adapters/types.js';\n\nexport async function closeStartupSession(open: () => Promise<NativeSession>): Promise<void> {\n const native = await open();\n await native.close();\n}\n\nexport async function checkReadiness(reference: ExecutionReference, cwd: string, env: NodeJS.ProcessEnv): Promise<void> {\n await closeStartupSession(() => createWorker({\n reference,\n cwd,\n env: {...env, AGENT_STATE_DIR: stateDirectory()},\n token: randomUUID(),\n sessionId: `check_${randomUUID()}`,\n }));\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HarnessKind } from '../sdk/types.js';
|
|
1
2
|
import type { AdapterOptions, ExecutionDefinition, NativeSession } from '../adapters/types.js';
|
|
2
3
|
import { type AccessConfig } from '../config/access.js';
|
|
3
|
-
export declare function selectNative(agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig, open?: (options: AdapterOptions) => Promise<NativeSession
|
|
4
|
+
export declare function selectNative(agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig, open?: (options: AdapterOptions) => Promise<NativeSession>, selected?: (kind: HarnessKind) => void): Promise<NativeSession>;
|
|
@@ -4,7 +4,7 @@ import { openFx } from '../adapters/fx.js';
|
|
|
4
4
|
import { resolveAccess } from '../config/access.js';
|
|
5
5
|
import { AgentError } from '../errors.js';
|
|
6
6
|
const openers = { codex: openCodex, claudeCode: openClaude, fx: openFx };
|
|
7
|
-
export async function selectNative(agent, cwd, env, settings, open = (options) => openers[options.harness.kind](options)) {
|
|
7
|
+
export async function selectNative(agent, cwd, env, settings, open = (options) => openers[options.harness.kind](options), selected = () => { }) {
|
|
8
8
|
const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];
|
|
9
9
|
const unavailable = [];
|
|
10
10
|
for (const harness of alternatives) {
|
|
@@ -12,7 +12,7 @@ export async function selectNative(agent, cwd, env, settings, open = (options) =
|
|
|
12
12
|
const connections = configuredConnections ??
|
|
13
13
|
(harness.kind === 'fx' ? [] : [{ type: 'subscription' }]);
|
|
14
14
|
if (configuredConnections?.length === 0) {
|
|
15
|
-
unavailable.push(`${harness.kind}: Access is disabled in the main checkout's .
|
|
15
|
+
unavailable.push(`${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);
|
|
16
16
|
}
|
|
17
17
|
else if (harness.kind === 'fx' && !connections.length) {
|
|
18
18
|
unavailable.push('fx: Enable an explicit AI Gateway connection in personal access settings.');
|
|
@@ -20,7 +20,9 @@ export async function selectNative(agent, cwd, env, settings, open = (options) =
|
|
|
20
20
|
for (const connection of connections) {
|
|
21
21
|
try {
|
|
22
22
|
const access = await resolveAccess(connection, harness.kind, cwd, env);
|
|
23
|
-
|
|
23
|
+
const native = await open({ agent, harness, cwd, env, access });
|
|
24
|
+
selected(harness.kind);
|
|
25
|
+
return native;
|
|
24
26
|
}
|
|
25
27
|
catch (error) {
|
|
26
28
|
if (!(error instanceof AgentError) || !['HARNESS_UNAVAILABLE', 'ACCESS_UNAVAILABLE'].includes(error.code))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-native.js","sourceRoot":"","sources":["../../src/runtime/select-native.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAqB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,OAAO,GAAG,EAAC,KAAK,EAAC,SAAS,EAAC,UAAU,EAAC,UAAU,EAAC,EAAE,EAAC,MAAM,EAAC,CAAC;AAElE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAA0B,EAAE,GAAW,EAAE,GAAsB,EAAE,QAAsB,EACvF,IAAI,GAAG,CAAC,OAAuB,EAA0B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"select-native.js","sourceRoot":"","sources":["../../src/runtime/select-native.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAqB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,OAAO,GAAG,EAAC,KAAK,EAAC,SAAS,EAAC,UAAU,EAAC,UAAU,EAAC,EAAE,EAAC,MAAM,EAAC,CAAC;AAElE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAA0B,EAAE,GAAW,EAAE,GAAsB,EAAE,QAAsB,EACvF,IAAI,GAAG,CAAC,OAAuB,EAA0B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAClG,QAAQ,GAAgC,GAAG,EAAE,GAAE,CAAC;IAEhD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpF,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,MAAM,qBAAqB,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAmB,CAAC,CAAC;QAC7E,MAAM,WAAW,GAAG,qBAAqB;YACvC,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAC,IAAI,EAAC,cAAuB,EAAC,CAAC,CAAC,CAAC;QAClE,IAAI,qBAAqB,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;YACxC,WAAW,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,4EAA4E,CAAC,CAAC;QAChH,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;YACxD,WAAW,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;QAChG,CAAC;QACD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;gBACvE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAC,KAAK,EAAC,OAAO,EAAC,GAAG,EAAC,GAAG,EAAC,MAAM,EAAC,CAAC,CAAC;gBAC1D,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACvB,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU,CAAC,IAAI,CAAC,CAAC,qBAAqB,EAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,MAAM,KAAK,CAAC;gBACtH,WAAW,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,yCAAyC,CAAC,CAAC;AACtI,CAAC","sourcesContent":["import type { HarnessKind } from '../sdk/types.js';\nimport type { AdapterOptions, ExecutionDefinition, NativeSession } from '../adapters/types.js';\nimport { openCodex } from '../adapters/codex.js';\nimport { openClaude } from '../adapters/claude.js';\nimport { openFx } from '../adapters/fx.js';\nimport { resolveAccess, type AccessConfig } from '../config/access.js';\nimport { AgentError } from '../errors.js';\n\nconst openers = {codex:openCodex,claudeCode:openClaude,fx:openFx};\n\nexport async function selectNative(\n agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig,\n open = (options: AdapterOptions): Promise<NativeSession> => openers[options.harness.kind](options),\n selected: (kind: HarnessKind) => void = () => {},\n): Promise<NativeSession> {\n const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];\n const unavailable: string[] = [];\n for (const harness of alternatives) {\n const configuredConnections = settings.access?.[harness.kind as HarnessKind];\n const connections = configuredConnections ??\n (harness.kind === 'fx' ? [] : [{type:'subscription' as const}]);\n if (configuredConnections?.length === 0) {\n unavailable.push(`${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);\n } else if (harness.kind === 'fx' && !connections.length) {\n unavailable.push('fx: Enable an explicit AI Gateway connection in personal access settings.');\n }\n for (const connection of connections) {\n try {\n const access = await resolveAccess(connection, harness.kind, cwd, env);\n const native = await open({agent,harness,cwd,env,access});\n selected(harness.kind);\n return native;\n } catch (error) {\n if (!(error instanceof AgentError) || !['HARNESS_UNAVAILABLE','ACCESS_UNAVAILABLE'].includes(error.code)) throw error;\n unavailable.push(`${harness.kind}: ${error.message}`);\n }\n }\n }\n throw new AgentError('ACCESS_UNAVAILABLE', unavailable.length ? unavailable.join('\\n') : 'No declared harness has enabled access.');\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Coordinator } from './coordinator.js';
|
|
2
|
-
export declare function startService(engine: Coordinator, token: string): Promise<{
|
|
2
|
+
export declare function startService(engine: Coordinator, token: string, instanceId: string): Promise<{
|
|
3
3
|
url: string;
|
|
4
4
|
close(): Promise<void>;
|
|
5
5
|
}>;
|
package/dist/runtime/service.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
2
|
import { AgentError, errorInfo } from '../errors.js';
|
|
3
|
-
export async function startService(engine, token) {
|
|
3
|
+
export async function startService(engine, token, instanceId) {
|
|
4
4
|
const server = createServer(async (request, response) => {
|
|
5
5
|
if (request.headers.authorization !== `Bearer ${token}`) {
|
|
6
6
|
response.writeHead(401).end();
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
9
|
if (request.method === 'GET' && request.url === '/health') {
|
|
10
|
-
response.
|
|
10
|
+
response.setHeader('content-type', 'application/json');
|
|
11
|
+
response.end(JSON.stringify({ version: 1, type: 'coordinator-health', instanceId }));
|
|
11
12
|
return;
|
|
12
13
|
}
|
|
13
14
|
if (request.method !== 'POST' || request.url !== '/') {
|
|
@@ -32,6 +33,9 @@ export async function startService(engine, token) {
|
|
|
32
33
|
const data = JSON.parse(body);
|
|
33
34
|
let receipt;
|
|
34
35
|
switch (data.command) {
|
|
36
|
+
case 'dashboard':
|
|
37
|
+
write(await engine.dashboard());
|
|
38
|
+
break;
|
|
35
39
|
case 'parent':
|
|
36
40
|
write(engine.parentContext(data.parentToken));
|
|
37
41
|
break;
|
|
@@ -39,6 +43,8 @@ export async function startService(engine, token) {
|
|
|
39
43
|
receipt = engine.run(data.input);
|
|
40
44
|
break;
|
|
41
45
|
case 'send':
|
|
46
|
+
if (data.detach && data.delivery === 'steer')
|
|
47
|
+
throw new AgentError('INVALID_ARGUMENT', '--detach cannot be combined with --delivery steer.');
|
|
42
48
|
receipt = await engine.send(data.id, data.prompt, data.delivery ?? 'queue', data.parentToken);
|
|
43
49
|
break;
|
|
44
50
|
case 'resume': {
|
|
@@ -68,8 +74,9 @@ export async function startService(engine, token) {
|
|
|
68
74
|
}
|
|
69
75
|
if (receipt) {
|
|
70
76
|
write(receipt);
|
|
71
|
-
if (receipt.type === 'started')
|
|
77
|
+
if (receipt.type === 'started' && !data.detach) {
|
|
72
78
|
write(await engine.observe(receipt.taskId, undefined, data.parentToken ?? data.input?.parentToken, observer.signal));
|
|
79
|
+
}
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/runtime/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AASrD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAmB,EAAE,KAAa;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/runtime/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { AgentError, errorInfo } from '../errors.js';\nimport { Coordinator } from './coordinator.js';\nimport type { Delivery, Receipt, SessionInput } from './types.js';\n\ninterface Request {\n command: string; input?: SessionInput; id?: string; after?: string;\n prompt?: string; delivery?: Delivery; full?: boolean; detach?: boolean; parentToken?: string;\n}\n\nexport async function startService(engine: Coordinator, token: string, instanceId: string) {\n const server = createServer(async (request, response) => {\n if (request.headers.authorization !== `Bearer ${token}`) { response.writeHead(401).end(); return; }\n if (request.method === 'GET' && request.url === '/health') {\n response.setHeader('content-type','application/json');\n response.end(JSON.stringify({version:1,type:'coordinator-health',instanceId}));\n return;\n }\n if (request.method !== 'POST' || request.url !== '/') { response.writeHead(404).end(); return; }\n response.setHeader('content-type', 'application/x-ndjson');\n const observer = new AbortController();\n // Keep long, quiet observations alive without exposing progress records to callers.\n const heartbeat = setInterval(() => { if (!response.destroyed) response.write('\\n'); }, 15_000);\n response.once('close', () => { clearInterval(heartbeat); observer.abort(); });\n const write = (record: unknown) => { if (!response.destroyed) response.write(JSON.stringify(record) + '\\n'); };\n try {\n let body = '';\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > 4 * 1024 * 1024) throw new AgentError('INVALID_ARGUMENT','Request exceeds 4 MiB.');\n }\n const data = JSON.parse(body) as Request;\n let receipt: Receipt | undefined;\n switch (data.command) {\n case 'dashboard': write(await engine.dashboard()); break;\n case 'parent': write(engine.parentContext(data.parentToken!)); break;\n case 'run': receipt = engine.run(data.input!); break;\n case 'send':\n if (data.detach && data.delivery === 'steer') throw new AgentError('INVALID_ARGUMENT','--detach cannot be combined with --delivery steer.');\n receipt = await engine.send(data.id!, data.prompt!, data.delivery ?? 'queue', data.parentToken); break;\n case 'resume': {\n const active = engine.queue(data.id!).active;\n if (!active || active.state !== 'failed') throw new AgentError('NOT_FAILED','This session has no failed task to resume.');\n const after = engine.status(active.taskId).response?.responseId;\n receipt = await engine.resume(data.id!);\n write(receipt); write(await engine.observe(receipt.taskId, after, data.parentToken, observer.signal));\n receipt = undefined; break;\n }\n case 'wait': write(await engine.observe(data.id!, data.after, data.parentToken, observer.signal)); break;\n case 'status': write(engine.status(data.id!, data.full)); break;\n case 'queue': write(engine.queue(data.id!)); break;\n case 'cancel': write(await engine.cancel(data.id!)); break;\n default: throw new AgentError('INVALID_ARGUMENT','Unknown coordinator operation.');\n }\n if (receipt) {\n write(receipt);\n if (receipt.type === 'started' && !data.detach) {\n write(await engine.observe(receipt.taskId, undefined, data.parentToken ?? data.input?.parentToken, observer.signal));\n }\n }\n } catch (error) { write({version:1,type:'error',error:errorInfo(error)}); }\n clearInterval(heartbeat);\n response.end();\n });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', resolve);\n });\n return {\n url: `http://127.0.0.1:${(server.address() as AddressInfo).port}/`,\n async close() { await engine.close(); server.closeAllConnections(); await new Promise<void>(resolve => server.close(() => resolve())); },\n };\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { chmodSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
4
|
+
import { AgentError } from '../errors.js';
|
|
5
|
+
export function acquireStartupOwnership(directory, timeoutMs) {
|
|
6
|
+
const timeout = Number.isFinite(timeoutMs) ? Math.max(0, Math.ceil(timeoutMs)) : 0;
|
|
7
|
+
let database;
|
|
8
|
+
try {
|
|
9
|
+
const path = join(directory, 'startup.sqlite');
|
|
10
|
+
database = new DatabaseSync(path, { timeout });
|
|
11
|
+
chmodSync(path, 0o600);
|
|
12
|
+
database.exec('BEGIN IMMEDIATE');
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
try {
|
|
16
|
+
database?.close();
|
|
17
|
+
}
|
|
18
|
+
catch { /* Process exit also releases SQLite ownership. */ }
|
|
19
|
+
throw new AgentError('STARTUP_FAILED', 'Another command did not finish coordinator startup.');
|
|
20
|
+
}
|
|
21
|
+
let held = true;
|
|
22
|
+
return {
|
|
23
|
+
isHeld: () => held,
|
|
24
|
+
release() {
|
|
25
|
+
if (!held)
|
|
26
|
+
return;
|
|
27
|
+
held = false;
|
|
28
|
+
try {
|
|
29
|
+
database.exec('ROLLBACK');
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
database.close();
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=startup-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-lock.js","sourceRoot":"","sources":["../../src/runtime/startup-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAO1C,MAAM,UAAU,uBAAuB,CAAC,SAAiB,EAAE,SAAiB;IAC1E,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,IAAI,QAAkC,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAC,gBAAgB,CAAC,CAAC;QAC9C,QAAQ,GAAG,IAAI,YAAY,CAAC,IAAI,EAAC,EAAC,OAAO,EAAC,CAAC,CAAC;QAC5C,SAAS,CAAC,IAAI,EAAC,KAAK,CAAC,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,kDAAkD,CAAC,CAAC;QACvF,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAC,qDAAqD,CAAC,CAAC;IAC/F,CAAC;IAED,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO;QACL,MAAM,EAAC,GAAG,EAAE,CAAC,IAAI;QACjB,OAAO;YACL,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,IAAI,GAAG,KAAK,CAAC;YACb,IAAI,CAAC;gBAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAAC,CAAC;oBAC1B,CAAC;gBAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YAAC,CAAC;QAC/B,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { chmodSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport { AgentError } from '../errors.js';\n\nexport interface StartupOwnership {\n isHeld(): boolean;\n release(): void;\n}\n\nexport function acquireStartupOwnership(directory: string, timeoutMs: number): StartupOwnership {\n const timeout = Number.isFinite(timeoutMs) ? Math.max(0,Math.ceil(timeoutMs)) : 0;\n let database: DatabaseSync | undefined;\n try {\n const path = join(directory,'startup.sqlite');\n database = new DatabaseSync(path,{timeout});\n chmodSync(path,0o600);\n database.exec('BEGIN IMMEDIATE');\n } catch {\n try { database?.close(); } catch { /* Process exit also releases SQLite ownership. */ }\n throw new AgentError('STARTUP_FAILED','Another command did not finish coordinator startup.');\n }\n\n let held = true;\n return {\n isHeld:() => held,\n release() {\n if (!held) return;\n held = false;\n try { database.exec('ROLLBACK'); }\n finally { database.close(); }\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Endpoint } from './state.js';
|
|
2
|
+
export declare const startupRetirementRequest: {
|
|
3
|
+
readonly version: 1;
|
|
4
|
+
readonly type: 'startup-retire';
|
|
5
|
+
};
|
|
6
|
+
export type StartupOutcome = {
|
|
7
|
+
version: 1;
|
|
8
|
+
type: 'startup-outcome';
|
|
9
|
+
state: 'retired';
|
|
10
|
+
} | {
|
|
11
|
+
version: 1;
|
|
12
|
+
type: 'startup-outcome';
|
|
13
|
+
state: 'published';
|
|
14
|
+
endpoint: Endpoint;
|
|
15
|
+
};
|
|
16
|
+
export declare function isStartupRetirementRequest(message: unknown): boolean;
|
|
17
|
+
export declare function startupOutcome(message: unknown): StartupOutcome | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const startupRetirementRequest = { version: 1, type: 'startup-retire' };
|
|
2
|
+
export function isStartupRetirementRequest(message) {
|
|
3
|
+
return typeof message === 'object' && message !== null
|
|
4
|
+
&& message.version === 1
|
|
5
|
+
&& message.type === 'startup-retire';
|
|
6
|
+
}
|
|
7
|
+
export function startupOutcome(message) {
|
|
8
|
+
if (typeof message !== 'object' || message === null
|
|
9
|
+
|| message.version !== 1
|
|
10
|
+
|| message.type !== 'startup-outcome')
|
|
11
|
+
return;
|
|
12
|
+
const candidate = message;
|
|
13
|
+
if (candidate.state === 'retired')
|
|
14
|
+
return { version: 1, type: 'startup-outcome', state: 'retired' };
|
|
15
|
+
if (candidate.state !== 'published' || typeof candidate.endpoint !== 'object' || candidate.endpoint === null)
|
|
16
|
+
return;
|
|
17
|
+
const endpoint = candidate.endpoint;
|
|
18
|
+
if (typeof endpoint.url !== 'string' || typeof endpoint.token !== 'string'
|
|
19
|
+
|| typeof endpoint.pid !== 'number' || typeof endpoint.instanceId !== 'string')
|
|
20
|
+
return;
|
|
21
|
+
return { version: 1, type: 'startup-outcome', state: 'published', endpoint: endpoint };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=startup-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-protocol.js","sourceRoot":"","sources":["../../src/runtime/startup-protocol.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,gBAAgB,EAAU,CAAC;AAMnF,MAAM,UAAU,0BAA0B,CAAC,OAAgB;IACzD,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;WAChD,OAA8B,CAAC,OAAO,KAAK,CAAC;WAC5C,OAA2B,CAAC,IAAI,KAAK,gBAAgB,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;WAC7C,OAA8B,CAAC,OAAO,KAAK,CAAC;WAC5C,OAA2B,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAO;IACrE,MAAM,SAAS,GAAG,OAAuE,CAAC;IAC1F,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,iBAAiB,EAAC,KAAK,EAAC,SAAS,EAAC,CAAC;IAC7F,IAAI,SAAS,CAAC,KAAK,KAAK,WAAW,IAAI,OAAO,SAAS,CAAC,QAAQ,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,KAAK,IAAI;QAAE,OAAO;IACrH,MAAM,QAAQ,GAAG,SAAS,CAAC,QAA6B,CAAC;IACzD,IAAI,OAAO,QAAQ,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ;WACrE,OAAO,QAAQ,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO;IACzF,OAAO,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,iBAAiB,EAAC,KAAK,EAAC,WAAW,EAAC,QAAQ,EAAC,QAAoB,EAAC,CAAC;AAC5F,CAAC","sourcesContent":["import type { Endpoint } from './state.js';\n\nexport const startupRetirementRequest = {version:1,type:'startup-retire'} as const;\n\nexport type StartupOutcome =\n | {version:1;type:'startup-outcome';state:'retired'}\n | {version:1;type:'startup-outcome';state:'published';endpoint:Endpoint};\n\nexport function isStartupRetirementRequest(message: unknown): boolean {\n return typeof message === 'object' && message !== null\n && (message as {version?:unknown}).version === 1\n && (message as {type?:unknown}).type === 'startup-retire';\n}\n\nexport function startupOutcome(message: unknown): StartupOutcome | undefined {\n if (typeof message !== 'object' || message === null\n || (message as {version?:unknown}).version !== 1\n || (message as {type?:unknown}).type !== 'startup-outcome') return;\n const candidate = message as Partial<StartupOutcome> & {state?:unknown;endpoint?:unknown};\n if (candidate.state === 'retired') return {version:1,type:'startup-outcome',state:'retired'};\n if (candidate.state !== 'published' || typeof candidate.endpoint !== 'object' || candidate.endpoint === null) return;\n const endpoint = candidate.endpoint as Partial<Endpoint>;\n if (typeof endpoint.url !== 'string' || typeof endpoint.token !== 'string'\n || typeof endpoint.pid !== 'number' || typeof endpoint.instanceId !== 'string') return;\n return {version:1,type:'startup-outcome',state:'published',endpoint:endpoint as Endpoint};\n}\n"]}
|
package/dist/runtime/state.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export interface Endpoint {
|
|
|
2
2
|
url: string;
|
|
3
3
|
token: string;
|
|
4
4
|
pid: number;
|
|
5
|
+
instanceId: string;
|
|
5
6
|
}
|
|
6
7
|
export declare function stateDirectory(): string;
|
|
7
8
|
export declare function privateDirectory(directory: string): Promise<void>;
|
|
@@ -10,5 +11,3 @@ export declare function localProgram(name: string): {
|
|
|
10
11
|
file: string;
|
|
11
12
|
execArgv: string[];
|
|
12
13
|
};
|
|
13
|
-
/** An incomplete PID write gets a grace period; live or inaccessible owners are never reclaimed. */
|
|
14
|
-
export declare function removeStaleStartupLock(path: string): Promise<boolean>;
|
package/dist/runtime/state.js
CHANGED
|
@@ -2,10 +2,10 @@ import { homedir } from 'node:os';
|
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { mkdir, readFile, chmod
|
|
5
|
+
import { mkdir, readFile, chmod } from 'node:fs/promises';
|
|
6
6
|
export function stateDirectory() {
|
|
7
7
|
const installation = createHash('sha256').update(dirname(import.meta.url)).digest('hex').slice(0, 12);
|
|
8
|
-
return process.env.AGENT_STATE_DIR ?? join(homedir(), '.
|
|
8
|
+
return process.env.AGENT_STATE_DIR ?? join(homedir(), '.subharness', 'state', installation);
|
|
9
9
|
}
|
|
10
10
|
export async function privateDirectory(directory) {
|
|
11
11
|
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
@@ -14,10 +14,14 @@ export async function privateDirectory(directory) {
|
|
|
14
14
|
export async function readEndpoint(directory) {
|
|
15
15
|
try {
|
|
16
16
|
const endpoint = JSON.parse(await readFile(join(directory, 'coordinator.json'), 'utf8'));
|
|
17
|
-
if (!/^http:\/\/127\.0\.0\.1:\d+\/$/.test(endpoint.url) || typeof endpoint.token !== 'string'
|
|
17
|
+
if (!/^http:\/\/127\.0\.0\.1:\d+\/$/.test(endpoint.url) || typeof endpoint.token !== 'string'
|
|
18
|
+
|| typeof endpoint.instanceId !== 'string' || endpoint.instanceId.length === 0)
|
|
18
19
|
return;
|
|
19
20
|
const response = await fetch(endpoint.url + 'health', { headers: { authorization: `Bearer ${endpoint.token}` }, signal: AbortSignal.timeout(500) });
|
|
20
|
-
if (response.ok)
|
|
21
|
+
if (!response.ok)
|
|
22
|
+
return;
|
|
23
|
+
const health = await response.json();
|
|
24
|
+
if (health.version === 1 && health.type === 'coordinator-health' && health.instanceId === endpoint.instanceId)
|
|
21
25
|
return endpoint;
|
|
22
26
|
}
|
|
23
27
|
catch { /* An unavailable endpoint never establishes an active coordinator. */ }
|
|
@@ -27,43 +31,4 @@ export function localProgram(name) {
|
|
|
27
31
|
return { file: fileURLToPath(new URL(`./${name}.${extension}`, import.meta.url)),
|
|
28
32
|
execArgv: extension === 'ts' ? ['--import', import.meta.resolve('tsx')] : [] };
|
|
29
33
|
}
|
|
30
|
-
/** An incomplete PID write gets a grace period; live or inaccessible owners are never reclaimed. */
|
|
31
|
-
export async function removeStaleStartupLock(path) {
|
|
32
|
-
try {
|
|
33
|
-
const before = await lstat(path);
|
|
34
|
-
if (!before.isFile() || Date.now() - before.mtimeMs < 5_000)
|
|
35
|
-
return false;
|
|
36
|
-
const contents = await readFile(path, "utf8");
|
|
37
|
-
const owner = lockOwner(contents);
|
|
38
|
-
if (owner !== undefined && !processAbsent(owner))
|
|
39
|
-
return false;
|
|
40
|
-
const current = await lstat(path);
|
|
41
|
-
if (before.dev !== current.dev || before.ino !== current.ino || before.size !== current.size
|
|
42
|
-
|| before.mtimeMs !== current.mtimeMs || before.ctimeMs !== current.ctimeMs)
|
|
43
|
-
return false;
|
|
44
|
-
if (await readFile(path, "utf8") !== contents)
|
|
45
|
-
return false;
|
|
46
|
-
if (owner !== undefined && !processAbsent(owner))
|
|
47
|
-
return false;
|
|
48
|
-
await unlink(path);
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return false;
|
|
53
|
-
} // Startup contenders can replace or remove the file while it is inspected.
|
|
54
|
-
}
|
|
55
|
-
function lockOwner(contents) {
|
|
56
|
-
const text = contents.trim();
|
|
57
|
-
const pid = Number(text);
|
|
58
|
-
return /^[1-9][0-9]*$/.test(text) && Number.isSafeInteger(pid) && pid <= 2_147_483_647 ? pid : undefined;
|
|
59
|
-
}
|
|
60
|
-
function processAbsent(pid) {
|
|
61
|
-
try {
|
|
62
|
-
process.kill(pid, 0);
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
return error.code === "ESRCH";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
34
|
//# sourceMappingURL=state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/runtime/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/runtime/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAG1D,MAAM,UAAU,cAAc;IAC5B,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtG,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,EAAC,aAAa,EAAC,OAAO,EAAC,YAAY,CAAC,CAAC;AAC3F,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IACtD,MAAM,KAAK,CAAC,SAAS,EAAC,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,CAAC,CAAC;IACnD,MAAM,KAAK,CAAC,SAAS,EAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAAiB;IAClD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAC,kBAAkB,CAAC,EAAC,MAAM,CAAC,CAAa,CAAC;QACnG,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ;eACxF,OAAO,QAAQ,CAAC,UAAU,KAAK,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,GAAG,GAAG,QAAQ,EAAE,EAAC,OAAO,EAAC,EAAC,aAAa,EAAC,UAAU,QAAQ,CAAC,KAAK,EAAE,EAAC,EAAC,MAAM,EAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,EAAC,CAAC,CAAC;QAC5I,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO;QACzB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA0D,CAAC;QAC7F,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,oBAAoB,IAAI,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;YAAE,OAAO,QAAQ,CAAC;IACjI,CAAC;IAAC,MAAM,CAAC,CAAC,sEAAsE,CAAC,CAAC;AACpF,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,OAAO,EAAE,IAAI,EAAC,aAAa,CAAC,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,SAAS,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7E,QAAQ,EAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,EAAC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACjF,CAAC","sourcesContent":["import { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createHash } from 'node:crypto';\nimport { mkdir, readFile, chmod } from 'node:fs/promises';\n\nexport interface Endpoint { url: string; token: string; pid: number; instanceId: string }\nexport function stateDirectory() {\n const installation = createHash('sha256').update(dirname(import.meta.url)).digest('hex').slice(0, 12);\n return process.env.AGENT_STATE_DIR ?? join(homedir(),'.subharness','state',installation);\n}\nexport async function privateDirectory(directory: string) {\n await mkdir(directory,{recursive:true,mode:0o700});\n await chmod(directory,0o700);\n}\nexport async function readEndpoint(directory: string): Promise<Endpoint | undefined> {\n try {\n const endpoint = JSON.parse(await readFile(join(directory,'coordinator.json'),'utf8')) as Endpoint;\n if (!/^http:\\/\\/127\\.0\\.0\\.1:\\d+\\/$/.test(endpoint.url) || typeof endpoint.token !== 'string'\n || typeof endpoint.instanceId !== 'string' || endpoint.instanceId.length === 0) return;\n const response = await fetch(endpoint.url + 'health', {headers:{authorization:`Bearer ${endpoint.token}`},signal:AbortSignal.timeout(500)});\n if (!response.ok) return;\n const health = await response.json() as {version?:unknown;type?:unknown;instanceId?:unknown};\n if (health.version === 1 && health.type === 'coordinator-health' && health.instanceId === endpoint.instanceId) return endpoint;\n } catch { /* An unavailable endpoint never establishes an active coordinator. */ }\n}\nexport function localProgram(name: string) {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'js';\n return { file:fileURLToPath(new URL(`./${name}.${extension}`, import.meta.url)),\n execArgv:extension === 'ts' ? ['--import',import.meta.resolve('tsx')] : [] };\n}\n"]}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ErrorInfo } from '../errors.js';
|
|
2
2
|
import type { HarnessRequest, NativeSession } from '../adapters/types.js';
|
|
3
|
+
import type { HarnessKind } from '../sdk/types.js';
|
|
4
|
+
import type { DashboardHarness, DashboardWorkspace } from './dashboard.js';
|
|
3
5
|
export type TaskState = 'queued' | 'running' | 'waiting' | 'completed' | 'failed' | 'cancelled' | 'interrupted';
|
|
4
6
|
export type Delivery = 'queue' | 'steer' | 'interrupt';
|
|
5
7
|
export interface AgentReference {
|
|
@@ -22,7 +24,10 @@ export interface DriverInput extends Omit<SessionInput, 'prompt' | 'parentToken'
|
|
|
22
24
|
token: string;
|
|
23
25
|
sessionId: string;
|
|
24
26
|
}
|
|
25
|
-
export type
|
|
27
|
+
export type ManagedNativeSession = NativeSession & {
|
|
28
|
+
selectedHarness?: HarnessKind;
|
|
29
|
+
};
|
|
30
|
+
export type DriverFactory = (input: DriverInput) => Promise<ManagedNativeSession>;
|
|
26
31
|
export interface RecordBase {
|
|
27
32
|
version: 1;
|
|
28
33
|
type: string;
|
|
@@ -40,6 +45,8 @@ export interface Receipt extends RecordBase {
|
|
|
40
45
|
delivery?: Delivery;
|
|
41
46
|
requestedDelivery?: Delivery;
|
|
42
47
|
resumed?: boolean;
|
|
48
|
+
paused?: true;
|
|
49
|
+
blockedByTaskId?: string;
|
|
43
50
|
}
|
|
44
51
|
export interface Response extends Observation {
|
|
45
52
|
type: 'response';
|
|
@@ -55,6 +62,7 @@ export interface Task {
|
|
|
55
62
|
id: string;
|
|
56
63
|
sessionId: string;
|
|
57
64
|
prompt: string;
|
|
65
|
+
title: string;
|
|
58
66
|
parentId?: string;
|
|
59
67
|
state: TaskState;
|
|
60
68
|
responses: Response[];
|
|
@@ -65,17 +73,23 @@ export interface Task {
|
|
|
65
73
|
stopPromise?: Promise<void>;
|
|
66
74
|
stopUnconfirmed?: boolean;
|
|
67
75
|
error?: ErrorInfo;
|
|
76
|
+
dispatchedAt?: number;
|
|
77
|
+
failedAt?: number;
|
|
78
|
+
finishedAt?: number;
|
|
79
|
+
updatedAt: number;
|
|
68
80
|
changed: Set<() => void>;
|
|
69
81
|
}
|
|
70
82
|
export interface Session {
|
|
71
83
|
id: string;
|
|
72
84
|
token: string;
|
|
73
85
|
input: SessionInput;
|
|
74
|
-
driver?: Promise<
|
|
86
|
+
driver?: Promise<ManagedNativeSession>;
|
|
75
87
|
tasks: string[];
|
|
76
88
|
active?: string;
|
|
77
89
|
paused: boolean;
|
|
78
90
|
commands: Promise<unknown>;
|
|
79
91
|
controls: number;
|
|
92
|
+
harness: DashboardHarness;
|
|
93
|
+
workspace: Promise<DashboardWorkspace>;
|
|
80
94
|
}
|
|
81
95
|
export declare const terminal: (state: TaskState) => boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"AAiCA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAgB,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC","sourcesContent":["import type { ErrorInfo } from '../errors.js';\nimport type { HarnessRequest, NativeSession } from '../adapters/types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport type { DashboardHarness, DashboardWorkspace } from './dashboard.js';\n\nexport type TaskState = 'queued' | 'running' | 'waiting' | 'completed' | 'failed' | 'cancelled' | 'interrupted';\nexport type Delivery = 'queue' | 'steer' | 'interrupt';\nexport interface AgentReference { file: string; subagents: string[] }\nexport interface HarnessReference { kind: 'harness'; harness: HarnessRequest }\nexport type ExecutionReference = AgentReference | HarnessReference;\nexport interface SessionInput { reference: ExecutionReference; cwd: string; env: NodeJS.ProcessEnv; prompt: string; parentToken?: string }\nexport interface DriverInput extends Omit<SessionInput, 'prompt' | 'parentToken'> { token: string; sessionId: string }\nexport type ManagedNativeSession = NativeSession & { selectedHarness?: HarnessKind };\nexport type DriverFactory = (input: DriverInput) => Promise<ManagedNativeSession>;\nexport interface RecordBase { version: 1; type: string; sessionId: string; taskId: string; state?: TaskState }\nexport interface Observation extends RecordBase { state: TaskState; responseId?: string; text?: string; error?: ErrorInfo }\nexport interface Receipt extends RecordBase {\n delivery?: Delivery; requestedDelivery?: Delivery; resumed?: boolean;\n paused?: true; blockedByTaskId?: string;\n}\nexport interface Response extends Observation { type: 'response'; responseId: string; text: string }\nexport interface ChildResult { taskId: string; state: TaskState; text: string }\nexport interface Task {\n id: string; sessionId: string; prompt: string; title: string; parentId?: string; state: TaskState;\n responses: Response[]; children: Set<string>; inbox: ChildResult[]; nativeActive: boolean;\n stopping: boolean; stopPromise?: Promise<void>; stopUnconfirmed?: boolean; error?: ErrorInfo;\n dispatchedAt?: number; failedAt?: number; finishedAt?: number; updatedAt: number; changed: Set<() => void>;\n}\nexport interface Session {\n id: string; token: string; input: SessionInput; driver?: Promise<ManagedNativeSession>;\n tasks: string[]; active?: string; paused: boolean; commands: Promise<unknown>; controls: number;\n harness: DashboardHarness; workspace: Promise<DashboardWorkspace>;\n}\nexport const terminal = (state: TaskState) => ['completed', 'failed', 'cancelled', 'interrupted'].includes(state);\n"]}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import type { NativeSession } from '../adapters/types.js';
|
|
2
1
|
import type { DriverInput } from './types.js';
|
|
3
|
-
|
|
2
|
+
import type { ManagedNativeSession } from './types.js';
|
|
3
|
+
interface WorkerClientOptions {
|
|
4
|
+
workerModule?: URL;
|
|
5
|
+
shutdownTimeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function createWorker(input: DriverInput, options?: WorkerClientOptions): Promise<ManagedNativeSession>;
|
|
8
|
+
export {};
|
|
@@ -3,12 +3,27 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import { delimiter } from 'node:path';
|
|
4
4
|
import { AgentError } from '../errors.js';
|
|
5
5
|
import { createSessionLauncher } from './session-launcher.js';
|
|
6
|
-
|
|
6
|
+
const closeResponseTimeoutMs = 15_000;
|
|
7
|
+
const shutdownTimeoutMs = 1_000;
|
|
8
|
+
function waitWithin(promise, timeout) {
|
|
9
|
+
return new Promise(resolve => {
|
|
10
|
+
const timer = setTimeout(() => resolve(false), timeout);
|
|
11
|
+
promise.then(() => { clearTimeout(timer); resolve(true); }, () => { clearTimeout(timer); resolve(true); });
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function withTimeout(promise, timeout) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const timer = setTimeout(() => reject(new AgentError('HARNESS_FAILED', 'The session worker did not complete cleanup.')), timeout);
|
|
17
|
+
promise.then(value => { clearTimeout(timer); resolve(value); }, error => { clearTimeout(timer); reject(error); });
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export async function createWorker(input, options = {}) {
|
|
7
21
|
const launcher = await createSessionLauncher(input.env.AGENT_STATE_DIR, input.token);
|
|
8
|
-
const
|
|
22
|
+
const workerModule = options.workerModule ?? new URL(`./worker.${import.meta.url.endsWith('.ts') ? 'ts' : 'js'}`, import.meta.url);
|
|
23
|
+
const extension = workerModule.pathname.endsWith('.ts') ? 'ts' : 'js';
|
|
9
24
|
let child;
|
|
10
25
|
try {
|
|
11
|
-
child = fork(fileURLToPath(
|
|
26
|
+
child = fork(fileURLToPath(workerModule), [], {
|
|
12
27
|
cwd: input.cwd, env: { ...input.env, AGENT_PARENT_TOKEN: input.token, AGENT_CLI_PATH: launcher.path,
|
|
13
28
|
PATH: launcher.directory + delimiter + (input.env.PATH ?? '') },
|
|
14
29
|
execArgv: extension === 'ts' ? ['--import', import.meta.resolve('tsx')] : [],
|
|
@@ -22,11 +37,14 @@ export async function createWorker(input) {
|
|
|
22
37
|
let nextId = 1;
|
|
23
38
|
const pending = new Map();
|
|
24
39
|
let exited = false;
|
|
40
|
+
let markExited;
|
|
41
|
+
const processExit = new Promise(resolve => { markExited = resolve; });
|
|
25
42
|
const exit = () => {
|
|
26
43
|
if (exited)
|
|
27
44
|
return;
|
|
28
45
|
exited = true;
|
|
29
|
-
|
|
46
|
+
markExited();
|
|
47
|
+
void launcher.close().catch(() => { }).finally(() => {
|
|
30
48
|
for (const waiting of pending.values())
|
|
31
49
|
waiting.reject(new AgentError('HARNESS_FAILED', 'The session worker exited.'));
|
|
32
50
|
pending.clear();
|
|
@@ -56,26 +74,65 @@ export async function createWorker(input) {
|
|
|
56
74
|
reject(new AgentError('HARNESS_FAILED', 'Cannot reach the session worker.'));
|
|
57
75
|
} });
|
|
58
76
|
});
|
|
77
|
+
let selectedHarness;
|
|
59
78
|
try {
|
|
60
|
-
await request('open', { input });
|
|
79
|
+
selectedHarness = await request('open', { input });
|
|
61
80
|
}
|
|
62
81
|
catch (error) {
|
|
63
82
|
child.kill();
|
|
64
83
|
await launcher.close();
|
|
65
84
|
throw error;
|
|
66
85
|
}
|
|
86
|
+
const stopWorker = async () => {
|
|
87
|
+
if (exited)
|
|
88
|
+
return;
|
|
89
|
+
if (child.connected)
|
|
90
|
+
try {
|
|
91
|
+
child.disconnect();
|
|
92
|
+
}
|
|
93
|
+
catch { /* Continue to termination escalation. */ }
|
|
94
|
+
const timeout = options.shutdownTimeoutMs ?? shutdownTimeoutMs;
|
|
95
|
+
if (await waitWithin(processExit, timeout))
|
|
96
|
+
return;
|
|
97
|
+
child.kill('SIGTERM');
|
|
98
|
+
if (await waitWithin(processExit, timeout))
|
|
99
|
+
return;
|
|
100
|
+
child.kill('SIGKILL');
|
|
101
|
+
if (await waitWithin(processExit, timeout))
|
|
102
|
+
return;
|
|
103
|
+
throw new AgentError('HARNESS_FAILED', 'The session worker did not confirm termination.');
|
|
104
|
+
};
|
|
67
105
|
return {
|
|
106
|
+
selectedHarness,
|
|
68
107
|
turn: prompt => request('turn', { prompt }),
|
|
69
108
|
steer: async (prompt) => { await request('steer', { prompt }); },
|
|
70
109
|
interrupt: async () => { await request('interrupt'); },
|
|
71
110
|
resume: () => request('resume'),
|
|
72
|
-
close: async () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
111
|
+
close: async () => {
|
|
112
|
+
let failure;
|
|
113
|
+
if (!exited) {
|
|
114
|
+
try {
|
|
115
|
+
await withTimeout(request('close'), closeResponseTimeoutMs);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
failure = error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await stopWorker();
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
failure ??= error;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
await launcher.close();
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
failure ??= error;
|
|
132
|
+
}
|
|
133
|
+
if (failure)
|
|
134
|
+
throw failure;
|
|
135
|
+
},
|
|
79
136
|
};
|
|
80
137
|
}
|
|
81
138
|
//# sourceMappingURL=worker-client.js.map
|