@agentworkforce/cli 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +29 -5
- package/dist/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +178 -9
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.js +94 -5
- package/dist/cli.test.js.map +1 -1
- package/dist/launch-metadata.d.ts +53 -0
- package/dist/launch-metadata.d.ts.map +1 -0
- package/dist/launch-metadata.js +192 -0
- package/dist/launch-metadata.js.map +1 -0
- package/dist/launch-metadata.test.d.ts +2 -0
- package/dist/launch-metadata.test.d.ts.map +1 -0
- package/dist/launch-metadata.test.js +152 -0
- package/dist/launch-metadata.test.js.map +1 -0
- package/dist/persona-picker.d.ts +66 -0
- package/dist/persona-picker.d.ts.map +1 -0
- package/dist/persona-picker.js +152 -0
- package/dist/persona-picker.js.map +1 -0
- package/dist/persona-picker.test.d.ts +2 -0
- package/dist/persona-picker.test.d.ts.map +1 -0
- package/dist/persona-picker.test.js +184 -0
- package/dist/persona-picker.test.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Harness, PersonaSelection } from '@agentworkforce/workload-router';
|
|
2
|
+
export declare const LAUNCH_METADATA_INTERVAL_MS = 1000;
|
|
3
|
+
export declare const LAUNCH_METADATA_OPT_OUT_ENV = "AGENTWORKFORCE_LAUNCH_METADATA";
|
|
4
|
+
export type LaunchMetadataIngestHarness = 'claude-code' | 'codex' | 'opencode';
|
|
5
|
+
export type LaunchMetadataPendingStampHarness = Harness;
|
|
6
|
+
export interface LaunchMetadataPendingStampOptions {
|
|
7
|
+
harness: LaunchMetadataPendingStampHarness;
|
|
8
|
+
cwd: string;
|
|
9
|
+
enrichment: Record<string, string>;
|
|
10
|
+
sessionDirHint?: string;
|
|
11
|
+
spawnStartTs?: string;
|
|
12
|
+
spawnerPid?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface LaunchMetadataIngestOptions {
|
|
15
|
+
harness: LaunchMetadataIngestHarness;
|
|
16
|
+
}
|
|
17
|
+
export interface LaunchMetadataBackendLike {
|
|
18
|
+
writePendingStamp?: (opts: LaunchMetadataPendingStampOptions) => unknown | Promise<unknown>;
|
|
19
|
+
ingest?: (opts?: LaunchMetadataIngestOptions) => unknown | Promise<unknown>;
|
|
20
|
+
}
|
|
21
|
+
export interface LaunchMetadataStartOptions {
|
|
22
|
+
selection: Pick<PersonaSelection, 'personaId' | 'tier' | 'runtime'>;
|
|
23
|
+
personaSpec: unknown;
|
|
24
|
+
personaSource: string;
|
|
25
|
+
cwd: string;
|
|
26
|
+
noLaunchMetadata?: boolean;
|
|
27
|
+
env?: NodeJS.ProcessEnv;
|
|
28
|
+
sdk?: LaunchMetadataBackendLike | (() => Promise<LaunchMetadataBackendLike>);
|
|
29
|
+
intervalMs?: number;
|
|
30
|
+
now?: () => Date;
|
|
31
|
+
onWarn?: (message: string) => void;
|
|
32
|
+
}
|
|
33
|
+
export interface LaunchMetadataRun {
|
|
34
|
+
readonly enabled: boolean;
|
|
35
|
+
readonly metadata: Record<string, string>;
|
|
36
|
+
stop(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export declare function shouldRecordLaunchMetadata(input: {
|
|
39
|
+
noLaunchMetadata?: boolean;
|
|
40
|
+
env?: NodeJS.ProcessEnv;
|
|
41
|
+
}): boolean;
|
|
42
|
+
export declare function canonicalJson(value: unknown): string;
|
|
43
|
+
export declare function personaVersionHash(personaSpec: unknown): string;
|
|
44
|
+
export declare function personaVersionShort(personaSpec: unknown): string;
|
|
45
|
+
export declare function buildLaunchMetadata(input: {
|
|
46
|
+
selection: Pick<PersonaSelection, 'personaId' | 'tier'>;
|
|
47
|
+
personaSpec: unknown;
|
|
48
|
+
personaSource: string;
|
|
49
|
+
}): Record<string, string>;
|
|
50
|
+
export declare function launchMetadataIngestHarness(harness: Harness): LaunchMetadataIngestHarness;
|
|
51
|
+
export declare function launchMetadataSessionDirHint(harness: Harness): string | undefined;
|
|
52
|
+
export declare function startLaunchMetadataRecording(options: LaunchMetadataStartOptions): Promise<LaunchMetadataRun>;
|
|
53
|
+
//# sourceMappingURL=launch-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-metadata.d.ts","sourceRoot":"","sources":["../src/launch-metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import * as launchMetadataBackendSdk from '@relayburn/sdk';
|
|
5
|
+
export const LAUNCH_METADATA_INTERVAL_MS = 1_000;
|
|
6
|
+
export const LAUNCH_METADATA_OPT_OUT_ENV = 'AGENTWORKFORCE_LAUNCH_METADATA';
|
|
7
|
+
const LAUNCH_METADATA_BACKEND_CALL_TIMEOUT_MS = 5_000;
|
|
8
|
+
const NOOP_RUN = Object.freeze({
|
|
9
|
+
enabled: false,
|
|
10
|
+
metadata: Object.freeze({}),
|
|
11
|
+
async stop() {
|
|
12
|
+
/* no-op */
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
export function shouldRecordLaunchMetadata(input) {
|
|
16
|
+
if (input.noLaunchMetadata === true)
|
|
17
|
+
return false;
|
|
18
|
+
const env = input.env ?? process.env;
|
|
19
|
+
return env[LAUNCH_METADATA_OPT_OUT_ENV] !== '0';
|
|
20
|
+
}
|
|
21
|
+
export function canonicalJson(value) {
|
|
22
|
+
return JSON.stringify(canonicalize(value));
|
|
23
|
+
}
|
|
24
|
+
export function personaVersionHash(personaSpec) {
|
|
25
|
+
return createHash('sha256').update(canonicalJson(personaSpec)).digest('hex');
|
|
26
|
+
}
|
|
27
|
+
export function personaVersionShort(personaSpec) {
|
|
28
|
+
return personaVersionHash(personaSpec).slice(0, 12);
|
|
29
|
+
}
|
|
30
|
+
export function buildLaunchMetadata(input) {
|
|
31
|
+
return {
|
|
32
|
+
agentworkforce: '1',
|
|
33
|
+
persona: input.selection.personaId,
|
|
34
|
+
personaTier: input.selection.tier,
|
|
35
|
+
personaVersion: personaVersionHash(input.personaSpec),
|
|
36
|
+
personaSource: input.personaSource
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function launchMetadataIngestHarness(harness) {
|
|
40
|
+
return harness === 'claude' ? 'claude-code' : harness;
|
|
41
|
+
}
|
|
42
|
+
export function launchMetadataSessionDirHint(harness) {
|
|
43
|
+
const home = homedir();
|
|
44
|
+
switch (harness) {
|
|
45
|
+
case 'claude':
|
|
46
|
+
return join(home, '.claude', 'projects');
|
|
47
|
+
case 'codex':
|
|
48
|
+
return join(home, '.codex', 'sessions');
|
|
49
|
+
case 'opencode':
|
|
50
|
+
return join(home, '.local', 'share', 'opencode', 'storage', 'session');
|
|
51
|
+
default: {
|
|
52
|
+
const _exhaustive = harness;
|
|
53
|
+
return _exhaustive;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function startLaunchMetadataRecording(options) {
|
|
58
|
+
if (!shouldRecordLaunchMetadata(options))
|
|
59
|
+
return NOOP_RUN;
|
|
60
|
+
const metadata = buildLaunchMetadata({
|
|
61
|
+
selection: options.selection,
|
|
62
|
+
personaSpec: options.personaSpec,
|
|
63
|
+
personaSource: options.personaSource
|
|
64
|
+
});
|
|
65
|
+
const warn = makeOnceWarn(options.onWarn ?? ((msg) => process.stderr.write(`warning: ${msg}\n`)));
|
|
66
|
+
let sdk;
|
|
67
|
+
try {
|
|
68
|
+
sdk = await resolveLaunchMetadataBackend(options.sdk);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
warn(`launch metadata recording unavailable: ${errorMessage(err)}`);
|
|
72
|
+
return disabledRun(metadata);
|
|
73
|
+
}
|
|
74
|
+
if (typeof sdk.writePendingStamp !== 'function') {
|
|
75
|
+
warn('launch metadata recording unavailable: installed metadata backend does not support launcher metadata yet.');
|
|
76
|
+
return disabledRun(metadata);
|
|
77
|
+
}
|
|
78
|
+
if (typeof sdk.ingest !== 'function') {
|
|
79
|
+
warn('launch metadata recording unavailable: installed metadata backend does not support ingest.');
|
|
80
|
+
return disabledRun(metadata);
|
|
81
|
+
}
|
|
82
|
+
const writePendingStamp = sdk.writePendingStamp.bind(sdk);
|
|
83
|
+
const ingest = sdk.ingest.bind(sdk);
|
|
84
|
+
try {
|
|
85
|
+
await withTimeout(writePendingStamp({
|
|
86
|
+
harness: options.selection.runtime.harness,
|
|
87
|
+
cwd: options.cwd,
|
|
88
|
+
enrichment: metadata,
|
|
89
|
+
sessionDirHint: launchMetadataSessionDirHint(options.selection.runtime.harness),
|
|
90
|
+
spawnStartTs: (options.now?.() ?? new Date()).toISOString(),
|
|
91
|
+
spawnerPid: process.pid
|
|
92
|
+
}), LAUNCH_METADATA_BACKEND_CALL_TIMEOUT_MS, 'writePendingStamp');
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
warn(`launch metadata stamp failed: ${errorMessage(err)}`);
|
|
96
|
+
return disabledRun(metadata);
|
|
97
|
+
}
|
|
98
|
+
let stopped = false;
|
|
99
|
+
let inFlight;
|
|
100
|
+
let ingestWarned = false;
|
|
101
|
+
const runIngest = async () => {
|
|
102
|
+
try {
|
|
103
|
+
await withTimeout(ingest({ harness: launchMetadataIngestHarness(options.selection.runtime.harness) }), LAUNCH_METADATA_BACKEND_CALL_TIMEOUT_MS, 'ingest');
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
if (!ingestWarned) {
|
|
107
|
+
ingestWarned = true;
|
|
108
|
+
warn(`launch metadata ingest failed: ${errorMessage(err)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const tick = () => {
|
|
113
|
+
if (stopped || inFlight)
|
|
114
|
+
return;
|
|
115
|
+
inFlight = runIngest().finally(() => {
|
|
116
|
+
inFlight = undefined;
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
const interval = setInterval(tick, options.intervalMs ?? LAUNCH_METADATA_INTERVAL_MS);
|
|
120
|
+
interval.unref?.();
|
|
121
|
+
return {
|
|
122
|
+
enabled: true,
|
|
123
|
+
metadata,
|
|
124
|
+
async stop() {
|
|
125
|
+
if (stopped)
|
|
126
|
+
return;
|
|
127
|
+
stopped = true;
|
|
128
|
+
clearInterval(interval);
|
|
129
|
+
if (inFlight)
|
|
130
|
+
await inFlight;
|
|
131
|
+
await runIngest();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function resolveLaunchMetadataBackend(sdk) {
|
|
136
|
+
if (typeof sdk === 'function')
|
|
137
|
+
return await sdk();
|
|
138
|
+
if (sdk)
|
|
139
|
+
return sdk;
|
|
140
|
+
return launchMetadataBackendSdk;
|
|
141
|
+
}
|
|
142
|
+
function disabledRun(metadata) {
|
|
143
|
+
return Object.freeze({
|
|
144
|
+
enabled: false,
|
|
145
|
+
metadata,
|
|
146
|
+
async stop() {
|
|
147
|
+
/* no-op */
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function makeOnceWarn(onWarn) {
|
|
152
|
+
let warned = false;
|
|
153
|
+
return (message) => {
|
|
154
|
+
if (warned)
|
|
155
|
+
return;
|
|
156
|
+
warned = true;
|
|
157
|
+
onWarn(message);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function errorMessage(err) {
|
|
161
|
+
return err instanceof Error ? err.message : String(err);
|
|
162
|
+
}
|
|
163
|
+
async function withTimeout(value, timeoutMs, label) {
|
|
164
|
+
let timeout;
|
|
165
|
+
try {
|
|
166
|
+
return await Promise.race([
|
|
167
|
+
Promise.resolve(value),
|
|
168
|
+
new Promise((_, reject) => {
|
|
169
|
+
timeout = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
170
|
+
timeout.unref?.();
|
|
171
|
+
})
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
if (timeout)
|
|
176
|
+
clearTimeout(timeout);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function canonicalize(value) {
|
|
180
|
+
if (value === null || typeof value !== 'object')
|
|
181
|
+
return value;
|
|
182
|
+
if (Array.isArray(value))
|
|
183
|
+
return value.map((item) => canonicalize(item));
|
|
184
|
+
const out = {};
|
|
185
|
+
for (const key of Object.keys(value).sort()) {
|
|
186
|
+
const child = value[key];
|
|
187
|
+
if (child !== undefined)
|
|
188
|
+
out[key] = canonicalize(child);
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=launch-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-metadata.js","sourceRoot":"","sources":["../src/launch-metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-metadata.test.d.ts","sourceRoot":"","sources":["../src/launch-metadata.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { buildLaunchMetadata, canonicalJson, launchMetadataIngestHarness, personaVersionHash, shouldRecordLaunchMetadata, startLaunchMetadataRecording } from './launch-metadata.js';
|
|
4
|
+
function fakeSelection() {
|
|
5
|
+
return {
|
|
6
|
+
personaId: 'code-reviewer',
|
|
7
|
+
tier: 'best',
|
|
8
|
+
runtime: {
|
|
9
|
+
harness: 'codex',
|
|
10
|
+
model: 'openai-codex/gpt-5.3-codex',
|
|
11
|
+
systemPrompt: 'Review the diff.',
|
|
12
|
+
harnessSettings: {
|
|
13
|
+
reasoning: 'high',
|
|
14
|
+
timeoutSeconds: 1200
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function fakeSpec(overrides = {}) {
|
|
20
|
+
return {
|
|
21
|
+
id: 'code-reviewer',
|
|
22
|
+
intent: 'review',
|
|
23
|
+
tags: ['review'],
|
|
24
|
+
description: 'Reviews code.',
|
|
25
|
+
skills: [],
|
|
26
|
+
tiers: {
|
|
27
|
+
best: fakeSelection().runtime,
|
|
28
|
+
'best-value': {
|
|
29
|
+
harness: 'opencode',
|
|
30
|
+
model: 'opencode/gpt-5-nano',
|
|
31
|
+
systemPrompt: 'Review concisely.',
|
|
32
|
+
harnessSettings: {
|
|
33
|
+
reasoning: 'medium',
|
|
34
|
+
timeoutSeconds: 900
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
minimum: {
|
|
38
|
+
harness: 'opencode',
|
|
39
|
+
model: 'opencode/minimax-m2.5-free',
|
|
40
|
+
systemPrompt: 'Review blockers.',
|
|
41
|
+
harnessSettings: {
|
|
42
|
+
reasoning: 'low',
|
|
43
|
+
timeoutSeconds: 600
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
...overrides
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
test('personaVersionHash canonicalizes object keys and changes with effective content', () => {
|
|
51
|
+
assert.equal(canonicalJson({ b: 1, a: { d: 4, c: 3 } }), '{"a":{"c":3,"d":4},"b":1}');
|
|
52
|
+
const left = {
|
|
53
|
+
id: 'p',
|
|
54
|
+
tiers: {
|
|
55
|
+
best: { model: 'm', harness: 'codex' }
|
|
56
|
+
},
|
|
57
|
+
tags: ['review']
|
|
58
|
+
};
|
|
59
|
+
const right = {
|
|
60
|
+
tags: ['review'],
|
|
61
|
+
tiers: {
|
|
62
|
+
best: { harness: 'codex', model: 'm' }
|
|
63
|
+
},
|
|
64
|
+
id: 'p'
|
|
65
|
+
};
|
|
66
|
+
assert.equal(personaVersionHash(left), personaVersionHash(right));
|
|
67
|
+
assert.notEqual(personaVersionHash(left), personaVersionHash({ ...right, tags: ['testing'] }));
|
|
68
|
+
});
|
|
69
|
+
test('buildLaunchMetadata emits the required AgentWorkforce metadata', () => {
|
|
70
|
+
const spec = fakeSpec();
|
|
71
|
+
const metadata = buildLaunchMetadata({
|
|
72
|
+
selection: fakeSelection(),
|
|
73
|
+
personaSpec: spec,
|
|
74
|
+
personaSource: 'dir:1'
|
|
75
|
+
});
|
|
76
|
+
assert.deepEqual(metadata, {
|
|
77
|
+
agentworkforce: '1',
|
|
78
|
+
persona: 'code-reviewer',
|
|
79
|
+
personaTier: 'best',
|
|
80
|
+
personaVersion: personaVersionHash(spec),
|
|
81
|
+
personaSource: 'dir:1'
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
test('startLaunchMetadataRecording writes a pending stamp and runs periodic plus final ingest', async (t) => {
|
|
85
|
+
t.mock.timers.enable({ apis: ['setInterval'] });
|
|
86
|
+
const stamps = [];
|
|
87
|
+
const ingests = [];
|
|
88
|
+
const run = await startLaunchMetadataRecording({
|
|
89
|
+
selection: fakeSelection(),
|
|
90
|
+
personaSpec: fakeSpec(),
|
|
91
|
+
personaSource: 'cwd',
|
|
92
|
+
cwd: '/tmp/project',
|
|
93
|
+
intervalMs: 5,
|
|
94
|
+
now: () => new Date('2026-05-08T12:00:00.000Z'),
|
|
95
|
+
sdk: {
|
|
96
|
+
writePendingStamp: (opts) => {
|
|
97
|
+
stamps.push(opts);
|
|
98
|
+
},
|
|
99
|
+
ingest: (opts) => {
|
|
100
|
+
if (opts)
|
|
101
|
+
ingests.push(opts);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
assert.equal(run.enabled, true);
|
|
106
|
+
t.mock.timers.tick(5);
|
|
107
|
+
await run.stop();
|
|
108
|
+
assert.equal(stamps.length, 1);
|
|
109
|
+
assert.equal(stamps[0]?.harness, 'codex');
|
|
110
|
+
assert.equal(stamps[0]?.cwd, '/tmp/project');
|
|
111
|
+
assert.equal(stamps[0]?.spawnStartTs, '2026-05-08T12:00:00.000Z');
|
|
112
|
+
assert.equal(stamps[0]?.enrichment.persona, 'code-reviewer');
|
|
113
|
+
assert.ok(ingests.length >= 2, 'expected at least one periodic ingest plus final ingest');
|
|
114
|
+
assert.deepEqual(ingests.at(-1), { harness: 'codex' });
|
|
115
|
+
});
|
|
116
|
+
test('startLaunchMetadataRecording skips SDK loading and ingest when opted out', async () => {
|
|
117
|
+
let loads = 0;
|
|
118
|
+
const run = await startLaunchMetadataRecording({
|
|
119
|
+
selection: fakeSelection(),
|
|
120
|
+
personaSpec: fakeSpec(),
|
|
121
|
+
personaSource: 'library',
|
|
122
|
+
cwd: '/tmp/project',
|
|
123
|
+
noLaunchMetadata: true,
|
|
124
|
+
sdk: async () => {
|
|
125
|
+
loads += 1;
|
|
126
|
+
throw new Error('should not load');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
await run.stop();
|
|
130
|
+
assert.equal(run.enabled, false);
|
|
131
|
+
assert.equal(loads, 0);
|
|
132
|
+
assert.equal(shouldRecordLaunchMetadata({ env: { AGENTWORKFORCE_LAUNCH_METADATA: '0' } }), false);
|
|
133
|
+
const originalEnvValue = process.env.AGENTWORKFORCE_LAUNCH_METADATA;
|
|
134
|
+
try {
|
|
135
|
+
process.env.AGENTWORKFORCE_LAUNCH_METADATA = '0';
|
|
136
|
+
assert.equal(shouldRecordLaunchMetadata({}), false);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
if (originalEnvValue === undefined) {
|
|
140
|
+
delete process.env.AGENTWORKFORCE_LAUNCH_METADATA;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
process.env.AGENTWORKFORCE_LAUNCH_METADATA = originalEnvValue;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
test('launchMetadataIngestHarness maps AgentWorkforce claude to backend claude-code', () => {
|
|
148
|
+
assert.equal(launchMetadataIngestHarness('claude'), 'claude-code');
|
|
149
|
+
assert.equal(launchMetadataIngestHarness('codex'), 'codex');
|
|
150
|
+
assert.equal(launchMetadataIngestHarness('opencode'), 'opencode');
|
|
151
|
+
});
|
|
152
|
+
//# sourceMappingURL=launch-metadata.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-metadata.test.js","sourceRoot":"","sources":["../src/launch-metadata.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact projection of a persona used for picker prompts. The model only
|
|
3
|
+
* needs enough to reason about intent fit — full systemPrompts would inflate
|
|
4
|
+
* the prompt without improving quality.
|
|
5
|
+
*/
|
|
6
|
+
export interface PickCandidate {
|
|
7
|
+
id: string;
|
|
8
|
+
intent: string;
|
|
9
|
+
tags: readonly string[];
|
|
10
|
+
description: string;
|
|
11
|
+
}
|
|
12
|
+
export type PickConfidence = 'high' | 'medium' | 'low';
|
|
13
|
+
export type PickResult = {
|
|
14
|
+
kind: 'match';
|
|
15
|
+
personaId: string;
|
|
16
|
+
confidence: 'high' | 'medium';
|
|
17
|
+
reason: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'no-match';
|
|
20
|
+
reason: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'picker-unavailable';
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Test seam describing one round-trip to the picker subprocess. Returning a
|
|
27
|
+
* structured shape keeps tests free of real `spawnSync` calls while preserving
|
|
28
|
+
* the stdout/stderr/status surface the parser exercises.
|
|
29
|
+
*/
|
|
30
|
+
export interface PickerSubprocessRequest {
|
|
31
|
+
bin: string;
|
|
32
|
+
args: readonly string[];
|
|
33
|
+
systemPrompt: string;
|
|
34
|
+
userPrompt: string;
|
|
35
|
+
timeoutMs: number;
|
|
36
|
+
}
|
|
37
|
+
export interface PickerSubprocessResult {
|
|
38
|
+
status: 'ok' | 'enoent' | 'error';
|
|
39
|
+
stdout: string;
|
|
40
|
+
stderr: string;
|
|
41
|
+
errorMessage?: string;
|
|
42
|
+
}
|
|
43
|
+
export type PickerRunner = (req: PickerSubprocessRequest) => PickerSubprocessResult;
|
|
44
|
+
export interface PickPersonaOptions {
|
|
45
|
+
claudeBin?: string;
|
|
46
|
+
model?: string;
|
|
47
|
+
timeoutMs?: number;
|
|
48
|
+
runner?: PickerRunner;
|
|
49
|
+
}
|
|
50
|
+
export declare function buildUserPrompt(task: string, candidates: readonly PickCandidate[]): string;
|
|
51
|
+
interface PickerJsonResponse {
|
|
52
|
+
persona: string | null;
|
|
53
|
+
confidence: PickConfidence;
|
|
54
|
+
reason: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse the model's text output. Accepts either a bare JSON object or the
|
|
58
|
+
* Claude CLI `--output-format json` envelope `{type, result, ...}` whose
|
|
59
|
+
* `result` field carries the model text. Returns undefined when the output
|
|
60
|
+
* cannot be coerced to the expected shape — callers translate that into a
|
|
61
|
+
* `no-match` result.
|
|
62
|
+
*/
|
|
63
|
+
export declare function parsePickerOutput(stdout: string): PickerJsonResponse | undefined;
|
|
64
|
+
export declare function pickPersona(task: string, candidates: readonly PickCandidate[], opts?: PickPersonaOptions): PickResult;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=persona-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persona-picker.d.ts","sourceRoot":"","sources":["../src/persona-picker.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEvD,MAAM,MAAM,UAAU,GAClB;IACE,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,uBAAuB,KAAK,sBAAsB,CAAC;AAEpF,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAoBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAQ1F;AAED,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,cAAc,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAWD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CA0BhF;AA4BD,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,aAAa,EAAE,EACpC,IAAI,GAAE,kBAAuB,GAC5B,UAAU,CA+DZ"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
const DEFAULT_BIN = 'claude';
|
|
3
|
+
// Use the dated model id rather than the `claude-haiku-4-5` alias so the
|
|
4
|
+
// subprocess survives any future change in the Claude CLI's alias resolver.
|
|
5
|
+
// The alias works today but the dated id is strictly more robust.
|
|
6
|
+
const DEFAULT_MODEL = 'claude-haiku-4-5-20251001';
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
8
|
+
const SYSTEM_PROMPT = `You pick the single best persona for a user task from a fixed catalog.
|
|
9
|
+
|
|
10
|
+
Rules:
|
|
11
|
+
- Read the task and the candidate list.
|
|
12
|
+
- Choose the persona whose intent and description most directly match the task.
|
|
13
|
+
- If no persona is a clear fit, set "persona" to null with confidence "low".
|
|
14
|
+
- Output ONE JSON object on a single line. No prose, no code fences, no commentary.
|
|
15
|
+
|
|
16
|
+
Output schema:
|
|
17
|
+
{"persona": "<id from the candidate list, or null>", "confidence": "high" | "medium" | "low", "reason": "<one short sentence>"}`;
|
|
18
|
+
export function buildUserPrompt(task, candidates) {
|
|
19
|
+
const compact = candidates.map((c) => ({
|
|
20
|
+
id: c.id,
|
|
21
|
+
intent: c.intent,
|
|
22
|
+
tags: [...c.tags],
|
|
23
|
+
description: c.description
|
|
24
|
+
}));
|
|
25
|
+
return `Task:\n${task.trim()}\n\nCandidates (JSON):\n${JSON.stringify(compact)}`;
|
|
26
|
+
}
|
|
27
|
+
function isPickerJsonResponse(value) {
|
|
28
|
+
if (typeof value !== 'object' || value === null)
|
|
29
|
+
return false;
|
|
30
|
+
const v = value;
|
|
31
|
+
if (v.persona !== null && typeof v.persona !== 'string')
|
|
32
|
+
return false;
|
|
33
|
+
if (v.confidence !== 'high' && v.confidence !== 'medium' && v.confidence !== 'low')
|
|
34
|
+
return false;
|
|
35
|
+
if (typeof v.reason !== 'string')
|
|
36
|
+
return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse the model's text output. Accepts either a bare JSON object or the
|
|
41
|
+
* Claude CLI `--output-format json` envelope `{type, result, ...}` whose
|
|
42
|
+
* `result` field carries the model text. Returns undefined when the output
|
|
43
|
+
* cannot be coerced to the expected shape — callers translate that into a
|
|
44
|
+
* `no-match` result.
|
|
45
|
+
*/
|
|
46
|
+
export function parsePickerOutput(stdout) {
|
|
47
|
+
const trimmed = stdout.trim();
|
|
48
|
+
if (!trimmed)
|
|
49
|
+
return undefined;
|
|
50
|
+
let candidate;
|
|
51
|
+
try {
|
|
52
|
+
candidate = JSON.parse(trimmed);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
if (typeof candidate === 'object' &&
|
|
58
|
+
candidate !== null &&
|
|
59
|
+
'result' in candidate &&
|
|
60
|
+
typeof candidate.result === 'string') {
|
|
61
|
+
const inner = candidate.result.trim();
|
|
62
|
+
try {
|
|
63
|
+
candidate = JSON.parse(inner);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return isPickerJsonResponse(candidate) ? candidate : undefined;
|
|
70
|
+
}
|
|
71
|
+
function defaultRunner(req) {
|
|
72
|
+
const opts = {
|
|
73
|
+
encoding: 'utf8',
|
|
74
|
+
timeout: req.timeoutMs
|
|
75
|
+
};
|
|
76
|
+
const child = spawnSync(req.bin, [...req.args], opts);
|
|
77
|
+
if (child.error) {
|
|
78
|
+
const err = child.error;
|
|
79
|
+
if (err.code === 'ENOENT') {
|
|
80
|
+
return { status: 'enoent', stdout: '', stderr: '', errorMessage: err.message };
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
status: 'error',
|
|
84
|
+
stdout: typeof child.stdout === 'string' ? child.stdout : '',
|
|
85
|
+
stderr: typeof child.stderr === 'string' ? child.stderr : '',
|
|
86
|
+
errorMessage: err.message
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const stdout = typeof child.stdout === 'string' ? child.stdout : '';
|
|
90
|
+
const stderr = typeof child.stderr === 'string' ? child.stderr : '';
|
|
91
|
+
if (typeof child.status === 'number' && child.status !== 0) {
|
|
92
|
+
return { status: 'error', stdout, stderr, errorMessage: `exit ${child.status}` };
|
|
93
|
+
}
|
|
94
|
+
return { status: 'ok', stdout, stderr };
|
|
95
|
+
}
|
|
96
|
+
export function pickPersona(task, candidates, opts = {}) {
|
|
97
|
+
if (!task.trim()) {
|
|
98
|
+
return { kind: 'no-match', reason: 'empty task description' };
|
|
99
|
+
}
|
|
100
|
+
if (candidates.length === 0) {
|
|
101
|
+
return { kind: 'no-match', reason: 'no candidates available' };
|
|
102
|
+
}
|
|
103
|
+
const bin = opts.claudeBin ?? DEFAULT_BIN;
|
|
104
|
+
const model = opts.model ?? DEFAULT_MODEL;
|
|
105
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
106
|
+
const runner = opts.runner ?? defaultRunner;
|
|
107
|
+
const userPrompt = buildUserPrompt(task, candidates);
|
|
108
|
+
const args = [
|
|
109
|
+
'-p',
|
|
110
|
+
'--model',
|
|
111
|
+
model,
|
|
112
|
+
'--output-format',
|
|
113
|
+
'json',
|
|
114
|
+
'--system-prompt',
|
|
115
|
+
SYSTEM_PROMPT,
|
|
116
|
+
userPrompt
|
|
117
|
+
];
|
|
118
|
+
const result = runner({ bin, args, systemPrompt: SYSTEM_PROMPT, userPrompt, timeoutMs });
|
|
119
|
+
if (result.status === 'enoent') {
|
|
120
|
+
return {
|
|
121
|
+
kind: 'picker-unavailable',
|
|
122
|
+
message: `\`${bin}\` not found on PATH. Install Claude Code (https://docs.claude.com/en/docs/claude-code) or run \`agentworkforce harness check\` to verify.`
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (result.status === 'error') {
|
|
126
|
+
return {
|
|
127
|
+
kind: 'picker-unavailable',
|
|
128
|
+
message: `picker subprocess failed: ${result.errorMessage ?? 'unknown error'}${result.stderr ? `\n${result.stderr.trim()}` : ''}`
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const parsed = parsePickerOutput(result.stdout);
|
|
132
|
+
if (!parsed) {
|
|
133
|
+
return { kind: 'no-match', reason: 'picker returned no parseable response' };
|
|
134
|
+
}
|
|
135
|
+
if (parsed.persona === null || parsed.confidence === 'low') {
|
|
136
|
+
return { kind: 'no-match', reason: parsed.reason || 'low confidence' };
|
|
137
|
+
}
|
|
138
|
+
const known = new Set(candidates.map((c) => c.id));
|
|
139
|
+
if (!known.has(parsed.persona)) {
|
|
140
|
+
return {
|
|
141
|
+
kind: 'no-match',
|
|
142
|
+
reason: `picker returned unknown persona id "${parsed.persona}"`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
kind: 'match',
|
|
147
|
+
personaId: parsed.persona,
|
|
148
|
+
confidence: parsed.confidence,
|
|
149
|
+
reason: parsed.reason
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=persona-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persona-picker.js","sourceRoot":"","sources":["../src/persona-picker.ts"],"names":[],"mappings":"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"}
|