@gobing-ai/ts-ai-runner 0.4.68 → 0.5.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/dist/agents/shims.d.ts +23 -0
- package/dist/agents/shims.d.ts.map +1 -1
- package/dist/agents/shims.js +47 -13
- package/dist/team-agent-process.d.ts +7 -0
- package/dist/team-agent-process.d.ts.map +1 -1
- package/dist/team-agent-process.js +3 -1
- package/package.json +4 -4
- package/src/agents/shims.ts +70 -13
- package/src/team-agent-process.ts +9 -1
package/dist/agents/shims.d.ts
CHANGED
|
@@ -54,7 +54,21 @@ export interface PromptOptions {
|
|
|
54
54
|
type: string;
|
|
55
55
|
purpose?: string;
|
|
56
56
|
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Request the agent's persistent-stdin dispatch: argv starts a long-lived
|
|
59
|
+
* process that reads framed prompts from stdin (one per line) instead of
|
|
60
|
+
* consuming a one-shot `-p <input>` and exiting. Only shims exposing a
|
|
61
|
+
* `persistentStdinProtocol` accept this — callers must consult
|
|
62
|
+
* {@link getAgentSessionCapability}.`supportsPersistentStdin` first. In
|
|
63
|
+
* persistent mode the shim omits `input` from argv entirely; the caller
|
|
64
|
+
* writes the identity preamble / prompts via the shim's `frame` (see
|
|
65
|
+
* `TeamAgentProcess` `stdinFramer`). rpc/stream-json modes supersede
|
|
66
|
+
* `mode` (the structured-output flag rides inside the protocol).
|
|
67
|
+
*/
|
|
68
|
+
persistentStdin?: boolean;
|
|
57
69
|
}
|
|
70
|
+
/** Frames one user prompt as a single stdin line for a persistent-stdin dispatch. */
|
|
71
|
+
export type StdinFrame = (input: string) => string;
|
|
58
72
|
/** Deprecation metadata attached to a retired or superseded agent shim. */
|
|
59
73
|
export interface AgentDeprecation {
|
|
60
74
|
/** Release date (ISO) when the id was marked deprecated. */
|
|
@@ -82,6 +96,15 @@ export interface AgentShim {
|
|
|
82
96
|
getPromptCommand(options: PromptOptions): ShimCommand;
|
|
83
97
|
/** Build an auth-status command, or null when unsupported. */
|
|
84
98
|
getAuthCommand(): ShimCommand | null;
|
|
99
|
+
/**
|
|
100
|
+
* Persistent-stdin framing contract; present iff the shim wires a
|
|
101
|
+
* stdin-listener dispatch (mirrors `supportsPersistentStdin`). Callers
|
|
102
|
+
* frame every prompt (including the identity preamble) through it before
|
|
103
|
+
* writing to the process stdin.
|
|
104
|
+
*/
|
|
105
|
+
readonly persistentStdinProtocol?: {
|
|
106
|
+
readonly frame: StdinFrame;
|
|
107
|
+
};
|
|
85
108
|
}
|
|
86
109
|
/** All bundled agent shims keyed by canonical agent name. */
|
|
87
110
|
export declare const AGENT_SHIMS: Readonly<Record<AgentName, AgentShim>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,MAAM,MAAM,SAAS,GACf,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,IAAI,GACJ,UAAU,GACV,iBAAiB,GACjB,UAAU,GACV,QAAQ,GACR,KAAK,GACL,MAAM,GACN,UAAU,CAAC;AAEjB,0CAA0C;AAC1C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC,mEAAmE;AACnE,MAAM,WAAW,WAAW;IACxB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC1B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"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"}
|
package/dist/agents/shims.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { getLogger } from '@gobing-ai/ts-infra';
|
|
2
|
+
/** pi-family rpc dialect (pi docs/rpc.md): one JSON object per line. */
|
|
3
|
+
const piRpcFrame = (input) => `${JSON.stringify({ type: 'prompt', message: input })}\n`;
|
|
4
|
+
/** claude stream-json input dialect: one user-message JSONL envelope per line. */
|
|
5
|
+
const claudeStreamJsonFrame = (input) => `${JSON.stringify({ type: 'user', message: { role: 'user', content: [{ type: 'text', text: input }] } })}\n`;
|
|
2
6
|
const claudeShim = {
|
|
3
7
|
name: 'claude',
|
|
4
8
|
command: 'claude',
|
|
@@ -8,7 +12,22 @@ const claudeShim = {
|
|
|
8
12
|
getPromptCommand: (options) => {
|
|
9
13
|
// Headless dispatch cannot answer edit approval prompts. Keep the grant
|
|
10
14
|
// scoped to file edits; shell and broader tools remain gated.
|
|
11
|
-
|
|
15
|
+
// Persistent stdin rides the print path: `--input-format` only works
|
|
16
|
+
// with --print, and stream-json input keeps the process alive reading
|
|
17
|
+
// JSONL envelopes until stdin closes (verified --help, CLI 2.1.274).
|
|
18
|
+
const persistent = options.persistentStdin === true;
|
|
19
|
+
const args = persistent
|
|
20
|
+
? [
|
|
21
|
+
'-p',
|
|
22
|
+
'--input-format',
|
|
23
|
+
'stream-json',
|
|
24
|
+
'--permission-mode',
|
|
25
|
+
'acceptEdits',
|
|
26
|
+
'--allowedTools',
|
|
27
|
+
'Write',
|
|
28
|
+
'Edit',
|
|
29
|
+
]
|
|
30
|
+
: ['-p', options.input ?? '', '--permission-mode', 'acceptEdits', '--allowedTools', 'Write', 'Edit'];
|
|
12
31
|
const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
|
|
13
32
|
if (hasSession) {
|
|
14
33
|
// Session/pin path — never emit --continue. Claude has no session-dir
|
|
@@ -21,10 +40,13 @@ const claudeShim = {
|
|
|
21
40
|
}
|
|
22
41
|
if (options.model !== undefined)
|
|
23
42
|
args.push('--model', options.model);
|
|
24
|
-
|
|
43
|
+
// stream-json output is part of the persistent protocol (the framing
|
|
44
|
+
// assumes JSONL stdout); one-shot keeps the caller-requested mode.
|
|
45
|
+
args.push('--output-format', persistent ? 'stream-json' : (options.mode ?? 'text'));
|
|
25
46
|
return { command: 'claude', args };
|
|
26
47
|
},
|
|
27
48
|
getAuthCommand: () => ({ command: 'claude', args: ['auth', 'status'] }),
|
|
49
|
+
persistentStdinProtocol: { frame: claudeStreamJsonFrame },
|
|
28
50
|
};
|
|
29
51
|
const codexShim = {
|
|
30
52
|
name: 'codex',
|
|
@@ -82,11 +104,16 @@ const piShim = {
|
|
|
82
104
|
getPromptCommand: (options) => {
|
|
83
105
|
const args = [];
|
|
84
106
|
const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
|
|
107
|
+
const persistent = options.persistentStdin === true;
|
|
85
108
|
// Session/pin path: durable — never ephemeral --no-session, never global -c.
|
|
86
|
-
if (!hasSession && options.continue !== true)
|
|
109
|
+
if (!hasSession && !persistent && options.continue !== true)
|
|
87
110
|
args.push('--no-session');
|
|
88
|
-
|
|
89
|
-
|
|
111
|
+
// Persistent stdin: no -p positional (prompts arrive framed over stdin);
|
|
112
|
+
// rpc mode is the long-lived listener (verified headless probe + pi
|
|
113
|
+
// docs/rpc.md). It supersedes any text/json output mode.
|
|
114
|
+
if (!persistent)
|
|
115
|
+
args.push('-p', options.input ?? '');
|
|
116
|
+
if (!hasSession && !persistent && options.continue === true)
|
|
90
117
|
args.push('-c');
|
|
91
118
|
if (options.sessionDir !== undefined)
|
|
92
119
|
args.push('--session-dir', options.sessionDir);
|
|
@@ -94,10 +121,11 @@ const piShim = {
|
|
|
94
121
|
args.push('-r', options.sessionId);
|
|
95
122
|
if (options.model !== undefined)
|
|
96
123
|
args.push('--model', options.model);
|
|
97
|
-
args.push('--mode', options.mode ?? 'text');
|
|
124
|
+
args.push('--mode', persistent ? 'rpc' : (options.mode ?? 'text'));
|
|
98
125
|
return { command: 'pi', args };
|
|
99
126
|
},
|
|
100
127
|
getAuthCommand: () => ({ command: 'pi', args: ['--list-models'] }),
|
|
128
|
+
persistentStdinProtocol: { frame: piRpcFrame },
|
|
101
129
|
};
|
|
102
130
|
const opencodeShim = {
|
|
103
131
|
name: 'opencode',
|
|
@@ -206,11 +234,15 @@ const ompShim = {
|
|
|
206
234
|
getPromptCommand: (options) => {
|
|
207
235
|
const args = [];
|
|
208
236
|
const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
|
|
237
|
+
const persistent = options.persistentStdin === true;
|
|
209
238
|
// Session/pin path: durable — never ephemeral --no-session, never global -c.
|
|
210
|
-
if (!hasSession && options.continue !== true)
|
|
239
|
+
if (!hasSession && !persistent && options.continue !== true)
|
|
211
240
|
args.push('--no-session');
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
// Persistent stdin mirrors pi (omp is a pi fork; same rpc dialect) —
|
|
242
|
+
// verified `--mode rpc` in `omp --help` at the pi-compatible surface.
|
|
243
|
+
if (!persistent)
|
|
244
|
+
args.push('-p', options.input ?? '');
|
|
245
|
+
if (!hasSession && !persistent && options.continue === true)
|
|
214
246
|
args.push('-c');
|
|
215
247
|
if (options.sessionDir !== undefined)
|
|
216
248
|
args.push('--session-dir', options.sessionDir);
|
|
@@ -218,10 +250,11 @@ const ompShim = {
|
|
|
218
250
|
args.push('-r', options.sessionId);
|
|
219
251
|
if (options.model !== undefined)
|
|
220
252
|
args.push('--model', options.model);
|
|
221
|
-
args.push('--mode', options.mode ?? 'text');
|
|
253
|
+
args.push('--mode', persistent ? 'rpc' : (options.mode ?? 'text'));
|
|
222
254
|
return { command: 'omp', args };
|
|
223
255
|
},
|
|
224
256
|
getAuthCommand: () => ({ command: 'omp', args: ['--list-models'] }),
|
|
257
|
+
persistentStdinProtocol: { frame: piRpcFrame },
|
|
225
258
|
};
|
|
226
259
|
/**
|
|
227
260
|
* Grok Build CLI (`grok`) — xAI coding agent. Headless one-shot via `-p`/`--single`;
|
|
@@ -331,9 +364,10 @@ const AGENT_SESSION_CAPABILITY = {
|
|
|
331
364
|
supportsPersistentStdin: true,
|
|
332
365
|
supportsStructuredOutput: true,
|
|
333
366
|
verifiedAgainst: '2.1.274',
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
|
|
367
|
+
// Persistent stdin is shim-wired: `-p --input-format stream-json --output-format
|
|
368
|
+
// stream-json` keeps the process alive reading JSONL envelopes (verified --help;
|
|
369
|
+
// --input-format only works with --print).
|
|
370
|
+
note: 'no session-dir flag — sessionDir is ignored (best-effort isolate)',
|
|
337
371
|
},
|
|
338
372
|
codex: {
|
|
339
373
|
supportsResumeById: true,
|
|
@@ -2,6 +2,7 @@ import { Buffer } from 'node:buffer';
|
|
|
2
2
|
import { type EventBus, type Logger } from '@gobing-ai/ts-infra';
|
|
3
3
|
import { type ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
4
4
|
import type { AgentSpec } from './agent-spec';
|
|
5
|
+
import type { StdinFrame } from './agents/shims';
|
|
5
6
|
import type { AgentEvents } from './events';
|
|
6
7
|
import { type QuotaAttribution } from './quota';
|
|
7
8
|
/** Options for spawning a team agent subprocess. */
|
|
@@ -16,6 +17,11 @@ export interface AgentProcessOptions {
|
|
|
16
17
|
events?: EventBus<AgentEvents>;
|
|
17
18
|
/** Optional exact quota attribution; absent fields stay absent on observations — never inferred. */
|
|
18
19
|
quotaContext?: QuotaAttribution;
|
|
20
|
+
/**
|
|
21
|
+
* Frames each sent prompt for the agent's persistent-stdin dialect (from
|
|
22
|
+
* the shim's `persistentStdinProtocol.frame`); absent = raw text + newline.
|
|
23
|
+
*/
|
|
24
|
+
stdinFramer?: StdinFrame;
|
|
19
25
|
}
|
|
20
26
|
type ProcessStatus = 'running' | 'stopped' | 'errored';
|
|
21
27
|
/**
|
|
@@ -38,6 +44,7 @@ export declare class TeamAgentProcess {
|
|
|
38
44
|
private readonly quotaProducer;
|
|
39
45
|
private readonly spec;
|
|
40
46
|
private readonly quotaContext;
|
|
47
|
+
private readonly stdinFramer;
|
|
41
48
|
constructor(options: AgentProcessOptions);
|
|
42
49
|
start(): Promise<void>;
|
|
43
50
|
stop(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-agent-process.d.ts","sourceRoot":"","sources":["../src/team-agent-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,QAAQ,EAAa,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAIH,KAAK,gBAAgB,EAExB,MAAM,SAAS,CAAC;AAEjB,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sHAAsH;IACtH,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC/B,oGAAoG;IACpG,YAAY,CAAC,EAAE,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"team-agent-process.d.ts","sourceRoot":"","sources":["../src/team-agent-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,QAAQ,EAAa,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAIH,KAAK,gBAAgB,EAExB,MAAM,SAAS,CAAC;AAEjB,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sHAAsH;IACtH,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC/B,oGAAoG;IACpG,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,KAAK,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEvD;;;GAGG;AACH,qBAAa,gBAAgB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqC;IACjE,kGAAkG;IAClG,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;IACzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAC5D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;gBAEzC,OAAO,EAAE,mBAAmB;IAalC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAerD,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAOvD,SAAS,IAAI,aAAa;IAI1B,MAAM,IAAI,MAAM,GAAG,IAAI;IAIvB,WAAW,IAAI,MAAM,GAAG,IAAI;YAId,IAAI;IAkBlB,sHAAsH;IACtH,OAAO,CAAC,gBAAgB;IAIxB,qGAAqG;IACrG,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,IAAI;CAOf"}
|
|
@@ -22,6 +22,7 @@ export class TeamAgentProcess {
|
|
|
22
22
|
quotaProducer;
|
|
23
23
|
spec;
|
|
24
24
|
quotaContext;
|
|
25
|
+
stdinFramer;
|
|
25
26
|
constructor(options) {
|
|
26
27
|
this.agentId = options.spec.id;
|
|
27
28
|
this.command = options.command;
|
|
@@ -32,6 +33,7 @@ export class TeamAgentProcess {
|
|
|
32
33
|
this.quotaProducer = new QuotaObservationProducer(options.events);
|
|
33
34
|
this.spec = options.spec;
|
|
34
35
|
this.quotaContext = options.quotaContext;
|
|
36
|
+
this.stdinFramer = options.stdinFramer;
|
|
35
37
|
}
|
|
36
38
|
async start() {
|
|
37
39
|
if (this.status === 'running')
|
|
@@ -95,7 +97,7 @@ export class TeamAgentProcess {
|
|
|
95
97
|
return { ok: false };
|
|
96
98
|
}
|
|
97
99
|
try {
|
|
98
|
-
this.subprocess.writeStdin(`${message}\n`);
|
|
100
|
+
this.subprocess.writeStdin(this.stdinFramer !== undefined ? this.stdinFramer(message) : `${message}\n`);
|
|
99
101
|
return { ok: true };
|
|
100
102
|
}
|
|
101
103
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/ts-ai-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "@gobing-ai/ts-ai-runner — Coding-agent shims, detection, doctor checks, and prompt execution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
"release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-ai-runner-v<version> && git push --tags' && exit 1"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@gobing-ai/ts-infra": "^0.
|
|
51
|
-
"@gobing-ai/ts-runtime": "^0.
|
|
50
|
+
"@gobing-ai/ts-infra": "^0.5.0",
|
|
51
|
+
"@gobing-ai/ts-runtime": "^0.5.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/bun": "1.3.14",
|
|
55
|
-
"@gobing-ai/ts-db": "^0.
|
|
55
|
+
"@gobing-ai/ts-db": "^0.5.0"
|
|
56
56
|
},
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
package/src/agents/shims.ts
CHANGED
|
@@ -66,8 +66,30 @@ export interface PromptOptions {
|
|
|
66
66
|
taskId?: string;
|
|
67
67
|
/** Peer agents included in the identity preamble. */
|
|
68
68
|
peers?: Array<{ id: string; type: string; purpose?: string }>;
|
|
69
|
+
/**
|
|
70
|
+
* Request the agent's persistent-stdin dispatch: argv starts a long-lived
|
|
71
|
+
* process that reads framed prompts from stdin (one per line) instead of
|
|
72
|
+
* consuming a one-shot `-p <input>` and exiting. Only shims exposing a
|
|
73
|
+
* `persistentStdinProtocol` accept this — callers must consult
|
|
74
|
+
* {@link getAgentSessionCapability}.`supportsPersistentStdin` first. In
|
|
75
|
+
* persistent mode the shim omits `input` from argv entirely; the caller
|
|
76
|
+
* writes the identity preamble / prompts via the shim's `frame` (see
|
|
77
|
+
* `TeamAgentProcess` `stdinFramer`). rpc/stream-json modes supersede
|
|
78
|
+
* `mode` (the structured-output flag rides inside the protocol).
|
|
79
|
+
*/
|
|
80
|
+
persistentStdin?: boolean;
|
|
69
81
|
}
|
|
70
82
|
|
|
83
|
+
/** Frames one user prompt as a single stdin line for a persistent-stdin dispatch. */
|
|
84
|
+
export type StdinFrame = (input: string) => string;
|
|
85
|
+
|
|
86
|
+
/** pi-family rpc dialect (pi docs/rpc.md): one JSON object per line. */
|
|
87
|
+
const piRpcFrame: StdinFrame = (input) => `${JSON.stringify({ type: 'prompt', message: input })}\n`;
|
|
88
|
+
|
|
89
|
+
/** claude stream-json input dialect: one user-message JSONL envelope per line. */
|
|
90
|
+
const claudeStreamJsonFrame: StdinFrame = (input) =>
|
|
91
|
+
`${JSON.stringify({ type: 'user', message: { role: 'user', content: [{ type: 'text', text: input }] } })}\n`;
|
|
92
|
+
|
|
71
93
|
/** Deprecation metadata attached to a retired or superseded agent shim. */
|
|
72
94
|
export interface AgentDeprecation {
|
|
73
95
|
/** Release date (ISO) when the id was marked deprecated. */
|
|
@@ -96,6 +118,13 @@ export interface AgentShim {
|
|
|
96
118
|
getPromptCommand(options: PromptOptions): ShimCommand;
|
|
97
119
|
/** Build an auth-status command, or null when unsupported. */
|
|
98
120
|
getAuthCommand(): ShimCommand | null;
|
|
121
|
+
/**
|
|
122
|
+
* Persistent-stdin framing contract; present iff the shim wires a
|
|
123
|
+
* stdin-listener dispatch (mirrors `supportsPersistentStdin`). Callers
|
|
124
|
+
* frame every prompt (including the identity preamble) through it before
|
|
125
|
+
* writing to the process stdin.
|
|
126
|
+
*/
|
|
127
|
+
readonly persistentStdinProtocol?: { readonly frame: StdinFrame };
|
|
99
128
|
}
|
|
100
129
|
|
|
101
130
|
const claudeShim: AgentShim = {
|
|
@@ -107,7 +136,22 @@ const claudeShim: AgentShim = {
|
|
|
107
136
|
getPromptCommand: (options) => {
|
|
108
137
|
// Headless dispatch cannot answer edit approval prompts. Keep the grant
|
|
109
138
|
// scoped to file edits; shell and broader tools remain gated.
|
|
110
|
-
|
|
139
|
+
// Persistent stdin rides the print path: `--input-format` only works
|
|
140
|
+
// with --print, and stream-json input keeps the process alive reading
|
|
141
|
+
// JSONL envelopes until stdin closes (verified --help, CLI 2.1.274).
|
|
142
|
+
const persistent = options.persistentStdin === true;
|
|
143
|
+
const args = persistent
|
|
144
|
+
? [
|
|
145
|
+
'-p',
|
|
146
|
+
'--input-format',
|
|
147
|
+
'stream-json',
|
|
148
|
+
'--permission-mode',
|
|
149
|
+
'acceptEdits',
|
|
150
|
+
'--allowedTools',
|
|
151
|
+
'Write',
|
|
152
|
+
'Edit',
|
|
153
|
+
]
|
|
154
|
+
: ['-p', options.input ?? '', '--permission-mode', 'acceptEdits', '--allowedTools', 'Write', 'Edit'];
|
|
111
155
|
const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
|
|
112
156
|
if (hasSession) {
|
|
113
157
|
// Session/pin path — never emit --continue. Claude has no session-dir
|
|
@@ -117,10 +161,13 @@ const claudeShim: AgentShim = {
|
|
|
117
161
|
args.push('--continue');
|
|
118
162
|
}
|
|
119
163
|
if (options.model !== undefined) args.push('--model', options.model);
|
|
120
|
-
|
|
164
|
+
// stream-json output is part of the persistent protocol (the framing
|
|
165
|
+
// assumes JSONL stdout); one-shot keeps the caller-requested mode.
|
|
166
|
+
args.push('--output-format', persistent ? 'stream-json' : (options.mode ?? 'text'));
|
|
121
167
|
return { command: 'claude', args };
|
|
122
168
|
},
|
|
123
169
|
getAuthCommand: () => ({ command: 'claude', args: ['auth', 'status'] }),
|
|
170
|
+
persistentStdinProtocol: { frame: claudeStreamJsonFrame },
|
|
124
171
|
};
|
|
125
172
|
|
|
126
173
|
const codexShim: AgentShim = {
|
|
@@ -178,17 +225,22 @@ const piShim: AgentShim = {
|
|
|
178
225
|
getPromptCommand: (options) => {
|
|
179
226
|
const args: string[] = [];
|
|
180
227
|
const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
|
|
228
|
+
const persistent = options.persistentStdin === true;
|
|
181
229
|
// Session/pin path: durable — never ephemeral --no-session, never global -c.
|
|
182
|
-
if (!hasSession && options.continue !== true) args.push('--no-session');
|
|
183
|
-
|
|
184
|
-
|
|
230
|
+
if (!hasSession && !persistent && options.continue !== true) args.push('--no-session');
|
|
231
|
+
// Persistent stdin: no -p positional (prompts arrive framed over stdin);
|
|
232
|
+
// rpc mode is the long-lived listener (verified headless probe + pi
|
|
233
|
+
// docs/rpc.md). It supersedes any text/json output mode.
|
|
234
|
+
if (!persistent) args.push('-p', options.input ?? '');
|
|
235
|
+
if (!hasSession && !persistent && options.continue === true) args.push('-c');
|
|
185
236
|
if (options.sessionDir !== undefined) args.push('--session-dir', options.sessionDir);
|
|
186
237
|
if (options.sessionId !== undefined) args.push('-r', options.sessionId);
|
|
187
238
|
if (options.model !== undefined) args.push('--model', options.model);
|
|
188
|
-
args.push('--mode', options.mode ?? 'text');
|
|
239
|
+
args.push('--mode', persistent ? 'rpc' : (options.mode ?? 'text'));
|
|
189
240
|
return { command: 'pi', args };
|
|
190
241
|
},
|
|
191
242
|
getAuthCommand: () => ({ command: 'pi', args: ['--list-models'] }),
|
|
243
|
+
persistentStdinProtocol: { frame: piRpcFrame },
|
|
192
244
|
};
|
|
193
245
|
|
|
194
246
|
const opencodeShim: AgentShim = {
|
|
@@ -292,17 +344,21 @@ const ompShim: AgentShim = {
|
|
|
292
344
|
getPromptCommand: (options) => {
|
|
293
345
|
const args: string[] = [];
|
|
294
346
|
const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
|
|
347
|
+
const persistent = options.persistentStdin === true;
|
|
295
348
|
// Session/pin path: durable — never ephemeral --no-session, never global -c.
|
|
296
|
-
if (!hasSession && options.continue !== true) args.push('--no-session');
|
|
297
|
-
|
|
298
|
-
|
|
349
|
+
if (!hasSession && !persistent && options.continue !== true) args.push('--no-session');
|
|
350
|
+
// Persistent stdin mirrors pi (omp is a pi fork; same rpc dialect) —
|
|
351
|
+
// verified `--mode rpc` in `omp --help` at the pi-compatible surface.
|
|
352
|
+
if (!persistent) args.push('-p', options.input ?? '');
|
|
353
|
+
if (!hasSession && !persistent && options.continue === true) args.push('-c');
|
|
299
354
|
if (options.sessionDir !== undefined) args.push('--session-dir', options.sessionDir);
|
|
300
355
|
if (options.sessionId !== undefined) args.push('-r', options.sessionId);
|
|
301
356
|
if (options.model !== undefined) args.push('--model', options.model);
|
|
302
|
-
args.push('--mode', options.mode ?? 'text');
|
|
357
|
+
args.push('--mode', persistent ? 'rpc' : (options.mode ?? 'text'));
|
|
303
358
|
return { command: 'omp', args };
|
|
304
359
|
},
|
|
305
360
|
getAuthCommand: () => ({ command: 'omp', args: ['--list-models'] }),
|
|
361
|
+
persistentStdinProtocol: { frame: piRpcFrame },
|
|
306
362
|
};
|
|
307
363
|
|
|
308
364
|
/**
|
|
@@ -429,9 +485,10 @@ const AGENT_SESSION_CAPABILITY: Readonly<Record<AgentName, AgentSessionCapabilit
|
|
|
429
485
|
supportsPersistentStdin: true,
|
|
430
486
|
supportsStructuredOutput: true,
|
|
431
487
|
verifiedAgainst: '2.1.274',
|
|
432
|
-
//
|
|
433
|
-
//
|
|
434
|
-
|
|
488
|
+
// Persistent stdin is shim-wired: `-p --input-format stream-json --output-format
|
|
489
|
+
// stream-json` keeps the process alive reading JSONL envelopes (verified --help;
|
|
490
|
+
// --input-format only works with --print).
|
|
491
|
+
note: 'no session-dir flag — sessionDir is ignored (best-effort isolate)',
|
|
435
492
|
},
|
|
436
493
|
codex: {
|
|
437
494
|
supportsResumeById: true,
|
|
@@ -2,6 +2,7 @@ import { Buffer } from 'node:buffer';
|
|
|
2
2
|
import { type EventBus, getLogger, type Logger } from '@gobing-ai/ts-infra';
|
|
3
3
|
import { nodeBunFactory, type PipeProcess, type ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
4
4
|
import type { AgentSpec } from './agent-spec';
|
|
5
|
+
import type { StdinFrame } from './agents/shims';
|
|
5
6
|
import type { AgentEvents } from './events';
|
|
6
7
|
import {
|
|
7
8
|
buildQuotaObservation,
|
|
@@ -23,6 +24,11 @@ export interface AgentProcessOptions {
|
|
|
23
24
|
events?: EventBus<AgentEvents>;
|
|
24
25
|
/** Optional exact quota attribution; absent fields stay absent on observations — never inferred. */
|
|
25
26
|
quotaContext?: QuotaAttribution;
|
|
27
|
+
/**
|
|
28
|
+
* Frames each sent prompt for the agent's persistent-stdin dialect (from
|
|
29
|
+
* the shim's `persistentStdinProtocol.frame`); absent = raw text + newline.
|
|
30
|
+
*/
|
|
31
|
+
stdinFramer?: StdinFrame;
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
type ProcessStatus = 'running' | 'stopped' | 'errored';
|
|
@@ -47,6 +53,7 @@ export class TeamAgentProcess {
|
|
|
47
53
|
private readonly quotaProducer: QuotaObservationProducer;
|
|
48
54
|
private readonly spec: AgentSpec;
|
|
49
55
|
private readonly quotaContext: QuotaAttribution | undefined;
|
|
56
|
+
private readonly stdinFramer: StdinFrame | undefined;
|
|
50
57
|
|
|
51
58
|
constructor(options: AgentProcessOptions) {
|
|
52
59
|
this.agentId = options.spec.id;
|
|
@@ -58,6 +65,7 @@ export class TeamAgentProcess {
|
|
|
58
65
|
this.quotaProducer = new QuotaObservationProducer(options.events);
|
|
59
66
|
this.spec = options.spec;
|
|
60
67
|
this.quotaContext = options.quotaContext;
|
|
68
|
+
this.stdinFramer = options.stdinFramer;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
async start(): Promise<void> {
|
|
@@ -117,7 +125,7 @@ export class TeamAgentProcess {
|
|
|
117
125
|
return { ok: false };
|
|
118
126
|
}
|
|
119
127
|
try {
|
|
120
|
-
this.subprocess.writeStdin(`${message}\n`);
|
|
128
|
+
this.subprocess.writeStdin(this.stdinFramer !== undefined ? this.stdinFramer(message) : `${message}\n`);
|
|
121
129
|
return { ok: true };
|
|
122
130
|
} catch (error) {
|
|
123
131
|
this.warn('stdin write failed', 'send.writeStdin', error);
|