@gobing-ai/ts-ai-runner 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +336 -32
  2. package/dist/agent-detector.d.ts +1 -0
  3. package/dist/agent-detector.d.ts.map +1 -1
  4. package/dist/agent-detector.js +13 -5
  5. package/dist/agent-spec.d.ts +6 -0
  6. package/dist/agent-spec.d.ts.map +1 -1
  7. package/dist/agent-spec.js +12 -8
  8. package/dist/ai-runner.d.ts +18 -2
  9. package/dist/ai-runner.d.ts.map +1 -1
  10. package/dist/ai-runner.js +52 -6
  11. package/dist/doctor-runner.d.ts +7 -0
  12. package/dist/doctor-runner.d.ts.map +1 -1
  13. package/dist/doctor-runner.js +69 -15
  14. package/dist/events.d.ts +38 -0
  15. package/dist/events.d.ts.map +1 -0
  16. package/dist/events.js +0 -0
  17. package/dist/identity.d.ts +3 -0
  18. package/dist/identity.d.ts.map +1 -1
  19. package/dist/identity.js +2 -0
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -1
  23. package/dist/messages.d.ts +4 -0
  24. package/dist/messages.d.ts.map +1 -0
  25. package/dist/messages.js +4 -0
  26. package/dist/team-agent-process.d.ts +12 -4
  27. package/dist/team-agent-process.d.ts.map +1 -1
  28. package/dist/team-agent-process.js +31 -19
  29. package/dist/team-orchestrator.d.ts +13 -8
  30. package/dist/team-orchestrator.d.ts.map +1 -1
  31. package/dist/team-orchestrator.js +32 -25
  32. package/package.json +4 -4
  33. package/src/agent-detector.ts +14 -5
  34. package/src/agent-spec.ts +20 -8
  35. package/src/ai-runner.ts +75 -13
  36. package/src/doctor-runner.ts +77 -16
  37. package/src/events.ts +25 -0
  38. package/src/identity.ts +3 -0
  39. package/src/index.ts +2 -1
  40. package/src/messages.ts +6 -0
  41. package/src/team-agent-process.ts +36 -21
  42. package/src/team-orchestrator.ts +36 -25
  43. package/dist/message-service.d.ts +0 -13
  44. package/dist/message-service.d.ts.map +0 -1
  45. package/dist/message-service.js +0 -27
  46. package/src/message-service.ts +0 -33
@@ -1,5 +1,7 @@
1
- import { type ProcessExecutor } from '@gobing-ai/ts-runtime';
2
- import { type AgentName, type PromptOptions } from './agents/shims';
1
+ import { type EventBus, type Logger } from '@gobing-ai/ts-infra';
2
+ import { type ProcessExecutor, type TracerPort } from '@gobing-ai/ts-runtime';
3
+ import { type AgentName, type PromptOptions, type ShimCommand } from './agents/shims';
4
+ import type { AgentEvents, AiRunnerProcessEvents } from './events';
3
5
  /** Result returned by every AI runner dispatch method. */
4
6
  export interface AgentRunResult {
5
7
  /** Process exit code; null indicates signal or timeout termination. */
@@ -28,12 +30,22 @@ export interface AiRunnerOptions {
28
30
  defaultCwd?: string;
29
31
  /** Default timeout in milliseconds. */
30
32
  defaultTimeout?: number;
33
+ /** Logger for invocation diagnostics. Defaults to `getLogger('ai-runner')`. */
34
+ logger?: Logger;
35
+ /** Event bus receiving process-level observability from the default executor. */
36
+ processEvents?: EventBus<AiRunnerProcessEvents>;
37
+ /** Event bus receiving agent-level invocation observability. */
38
+ events?: EventBus<AgentEvents>;
39
+ /** Tracer adapter for the default executor. Defaults to `ts-infra` traceAsync. */
40
+ tracer?: TracerPort;
31
41
  }
32
42
  /** Dispatches coding-agent CLI commands through pure command shims. */
33
43
  export declare class AiRunner {
34
44
  private readonly processExecutor;
35
45
  private readonly defaultCwd;
36
46
  private readonly defaultTimeout;
47
+ private readonly logger;
48
+ private readonly events;
37
49
  constructor(options?: AiRunnerOptions);
38
50
  /** Run an agent help command. */
39
51
  runHelpCommand(agent: AgentName, options?: AgentRunOptions): Promise<AgentRunResult>;
@@ -41,6 +53,10 @@ export declare class AiRunner {
41
53
  runVersionCommand(agent: AgentName, options?: AgentRunOptions): Promise<AgentRunResult>;
42
54
  /** Run an agent prompt command. */
43
55
  runPromptCommand(agent: AgentName, promptOptions: PromptOptions, options?: AgentRunOptions): Promise<AgentRunResult>;
56
+ /** Translate a Claude-style slash command and run it as a prompt command. */
57
+ runSlashCommand(agent: AgentName, input: string, promptOptions: PromptOptions, options?: AgentRunOptions): Promise<AgentRunResult>;
58
+ /** Build an agent prompt command without executing it. */
59
+ buildPromptCommand(agent: AgentName, promptOptions: PromptOptions, options?: AgentRunOptions): ShimCommand;
44
60
  /** Run an agent authentication command, or return null when unsupported. */
45
61
  runAuthCommand(agent: AgentName, options?: AgentRunOptions): Promise<AgentRunResult> | null;
46
62
  private invoke;
@@ -1 +1 @@
1
- {"version":3,"file":"ai-runner.d.ts","sourceRoot":"","sources":["../src/ai-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAsB,MAAM,uBAAuB,CAAC;AACtG,OAAO,EAAE,KAAK,SAAS,EAAgB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGlF,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC3B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC5B,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC5B,4DAA4D;IAC5D,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,uEAAuE;AACvE,qBAAa,QAAQ;IACjB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;gBAExC,OAAO,GAAE,eAAoB;IAMzC,iCAAiC;IACjC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIxF,oCAAoC;IACpC,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IAI3F,mCAAmC;IACnC,gBAAgB,CACZ,KAAK,EAAE,SAAS,EAChB,aAAa,EAAE,aAAa,EAC5B,OAAO,GAAE,eAAoB,GAC9B,OAAO,CAAC,cAAc,CAAC;IAW1B,4EAA4E;IAC5E,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI;YAKjF,MAAM;IAyBpB,OAAO,CAAC,oBAAoB;CAmB/B"}
1
+ {"version":3,"file":"ai-runner.d.ts","sourceRoot":"","sources":["../src/ai-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAa,KAAK,MAAM,EAAc,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAGH,KAAK,eAAe,EAEpB,KAAK,UAAU,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAgB,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACpG,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAInE,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC3B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC5B,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC5B,4DAA4D;IAC5D,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,aAAa,CAAC,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IAChD,gEAAgE;IAChE,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC/B,kFAAkF;IAClF,MAAM,CAAC,EAAE,UAAU,CAAC;CACvB;AAED,uEAAuE;AACvE,qBAAa,QAAQ;IACjB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;gBAE/C,OAAO,GAAE,eAAoB;IAuBzC,iCAAiC;IACjC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIxF,oCAAoC;IACpC,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IAI3F,mCAAmC;IACnC,gBAAgB,CACZ,KAAK,EAAE,SAAS,EAChB,aAAa,EAAE,aAAa,EAC5B,OAAO,GAAE,eAAoB,GAC9B,OAAO,CAAC,cAAc,CAAC;IAI1B,6EAA6E;IAC7E,eAAe,CACX,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,aAAa,EAC5B,OAAO,GAAE,eAAoB,GAC9B,OAAO,CAAC,cAAc,CAAC;IAI1B,0DAA0D;IAC1D,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,OAAO,GAAE,eAAoB,GAAG,WAAW;IAI9G,4EAA4E;IAC5E,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI;YAKjF,MAAM;IA2CpB,OAAO,CAAC,oBAAoB;CAmB/B"}
package/dist/ai-runner.js CHANGED
@@ -1,15 +1,36 @@
1
- import { NodeProcessExecutor } from '@gobing-ai/ts-runtime';
1
+ import { getLogger, traceAsync } from '@gobing-ai/ts-infra';
2
+ import { getProcessCwd, NodeProcessExecutor, } from '@gobing-ai/ts-runtime';
2
3
  import { getAgentShim } from './agents/shims.js';
3
4
  import { buildIdentityPreamble } from './identity.js';
5
+ import { translateSlashCommand } from './slash-command.js';
4
6
  /** Dispatches coding-agent CLI commands through pure command shims. */
5
7
  export class AiRunner {
6
8
  processExecutor;
7
9
  defaultCwd;
8
10
  defaultTimeout;
11
+ logger;
12
+ events;
9
13
  constructor(options = {}) {
10
- this.processExecutor = options.processExecutor ?? new NodeProcessExecutor();
14
+ this.processExecutor =
15
+ options.processExecutor ??
16
+ new NodeProcessExecutor({
17
+ ...(options.processEvents !== undefined
18
+ ? {
19
+ events: {
20
+ emit: (event, detail) => {
21
+ void options.processEvents?.emit(event, detail);
22
+ },
23
+ },
24
+ }
25
+ : {}),
26
+ tracer: options.tracer ?? {
27
+ traceAsync: async (name, fn) => await traceAsync(name, (span) => fn(span)),
28
+ },
29
+ });
11
30
  this.defaultCwd = options.defaultCwd;
12
31
  this.defaultTimeout = options.defaultTimeout;
32
+ this.logger = options.logger ?? getLogger('ai-runner');
33
+ this.events = options.events;
13
34
  }
14
35
  /** Run an agent help command. */
15
36
  runHelpCommand(agent, options = {}) {
@@ -21,8 +42,15 @@ export class AiRunner {
21
42
  }
22
43
  /** Run an agent prompt command. */
23
44
  runPromptCommand(agent, promptOptions, options = {}) {
24
- const enrichedPromptOptions = this.withIdentityPreamble(agent, promptOptions, options);
25
- return this.invoke(agent, 'prompt', getAgentShim(agent).getPromptCommand(enrichedPromptOptions), options, false);
45
+ return this.invoke(agent, 'prompt', this.buildPromptCommand(agent, promptOptions, options), options, false);
46
+ }
47
+ /** Translate a Claude-style slash command and run it as a prompt command. */
48
+ runSlashCommand(agent, input, promptOptions, options = {}) {
49
+ return this.runPromptCommand(agent, { ...promptOptions, input: translateSlashCommand(agent, input) }, options);
50
+ }
51
+ /** Build an agent prompt command without executing it. */
52
+ buildPromptCommand(agent, promptOptions, options = {}) {
53
+ return getAgentShim(agent).getPromptCommand(this.withIdentityPreamble(agent, promptOptions, options));
26
54
  }
27
55
  /** Run an agent authentication command, or return null when unsupported. */
28
56
  runAuthCommand(agent, options = {}) {
@@ -30,15 +58,33 @@ export class AiRunner {
30
58
  return command === null ? null : this.invoke(agent, 'auth', command, options, true);
31
59
  }
32
60
  async invoke(agent, operation, command, options, forceBuffered) {
61
+ const label = `ai-runner.${agent}.${operation}`;
62
+ this.logger.debug('invoke', { label, command: command.command, args: command.args.join(' ') });
63
+ void this.events?.emit('agent.invoke.start', { agent, operation, label });
33
64
  const result = await this.processExecutor.run({
34
65
  command: command.command,
35
66
  args: command.args,
36
- label: `ai-runner.${agent}.${operation}`,
67
+ label,
37
68
  rejectOnError: false,
38
69
  forceBuffered,
39
70
  cwd: options.cwd ?? this.defaultCwd,
40
71
  timeout: options.timeout ?? this.defaultTimeout,
41
72
  });
73
+ if (result.exitCode !== 0) {
74
+ this.logger.error('invoke exited non-zero', {
75
+ label,
76
+ exitCode: result.exitCode,
77
+ signal: result.signal,
78
+ });
79
+ }
80
+ void this.events?.emit('agent.invoke.exit', {
81
+ agent,
82
+ operation,
83
+ label,
84
+ exitCode: result.exitCode,
85
+ ...(result.signal !== undefined ? { signal: result.signal } : {}),
86
+ durationMs: result.durationMs,
87
+ });
42
88
  return {
43
89
  exitCode: result.exitCode,
44
90
  stdout: result.stdout,
@@ -50,7 +96,7 @@ export class AiRunner {
50
96
  withIdentityPreamble(agent, promptOptions, options) {
51
97
  if (!hasIdentityOptions(promptOptions))
52
98
  return promptOptions;
53
- const workspace = options.cwd ?? this.defaultCwd ?? process.cwd();
99
+ const workspace = options.cwd ?? this.defaultCwd ?? getProcessCwd();
54
100
  const preamble = buildIdentityPreamble({
55
101
  agentId: agent,
56
102
  agentType: agent,
@@ -1,3 +1,4 @@
1
+ import { type Logger } from '@gobing-ai/ts-infra';
1
2
  import { AgentDetector } from './agent-detector';
2
3
  import { AiRunner } from './ai-runner';
3
4
  /** Health-check result for one coding agent. */
@@ -29,6 +30,8 @@ export interface DoctorRunnerOptions {
29
30
  timeout?: number;
30
31
  /** Environment map for file/env auth checks. */
31
32
  env?: Record<string, string | undefined>;
33
+ /** Logger for health-check diagnostics. Defaults to `getLogger('doctor')`. */
34
+ logger?: Logger;
32
35
  }
33
36
  /** Runs installation and auth health checks for supported coding agents. */
34
37
  export declare class DoctorRunner {
@@ -37,6 +40,7 @@ export declare class DoctorRunner {
37
40
  private readonly timeout;
38
41
  private readonly env;
39
42
  private readonly fs;
43
+ private readonly logger;
40
44
  constructor(options?: DoctorRunnerOptions);
41
45
  /** Run a health check on all supported agents. */
42
46
  runAll(): Promise<DoctorResult[]>;
@@ -44,6 +48,9 @@ export declare class DoctorRunner {
44
48
  runOne(agent: string): Promise<DoctorResult>;
45
49
  private buildResult;
46
50
  private checkAuth;
51
+ private checkCodexAuth;
52
+ private geminiSettingsContainCredentials;
53
+ private probeAuthOutput;
47
54
  /** True when the path exists and has a non-zero size. */
48
55
  private hasNonEmptyFile;
49
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-runner.d.ts","sourceRoot":"","sources":["../src/doctor-runner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAsB,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,gDAAgD;AAChD,MAAM,WAAW,YAAY;IACzB,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,+CAA+C;IAC/C,aAAa,EAAE,OAAO,CAAC;IACvB,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,oEAAoE;IACpE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;IACZ,wDAAwD;IACxD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oCAAoC;IACpC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAChC,uBAAuB;IACvB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,qBAAqB;IACrB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5C;AASD,4EAA4E;AAC5E,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;IACzD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAwB;gBAE/B,OAAO,GAAE,mBAAwB;IAO7C,kDAAkD;IAC5C,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAKvC,uCAAuC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;YAIpC,WAAW;YAgBX,SAAS;IAmBvB,yDAAyD;YAC3C,eAAe;CAKhC"}
1
+ {"version":3,"file":"doctor-runner.d.ts","sourceRoot":"","sources":["../src/doctor-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAsB,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAuB,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5D,gDAAgD;AAChD,MAAM,WAAW,YAAY;IACzB,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,+CAA+C;IAC/C,aAAa,EAAE,OAAO,CAAC;IACvB,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,oEAAoE;IACpE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;IACZ,wDAAwD;IACxD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oCAAoC;IACpC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAChC,uBAAuB;IACvB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,qBAAqB;IACrB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAgCD,4EAA4E;AAC5E,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;IACzD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAwB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,mBAAwB;IAQ7C,kDAAkD;IAC5C,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAkBvC,uCAAuC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;YAIpC,WAAW;YAiBX,SAAS;YAWT,cAAc;YASd,gCAAgC;YAQhC,eAAe;IAY7B,yDAAyD;YAC3C,eAAe;CAKhC"}
@@ -1,10 +1,31 @@
1
- import { homedir } from 'node:os';
2
- import { join } from 'node:path';
3
- import { getProcessEnv, NodeFileSystem } from '@gobing-ai/ts-runtime';
1
+ import { getLogger } from '@gobing-ai/ts-infra';
2
+ import { getProcessEnv, joinPath, NodeFileSystem } from '@gobing-ai/ts-runtime';
4
3
  import { AgentDetector } from './agent-detector.js';
5
- import { isAgentName, TIER2_AGENTS } from './agents/shims.js';
4
+ import { DISPLAY_ORDER, isAgentName, TIER2_AGENTS } from './agents/shims.js';
6
5
  import { AiRunner } from './ai-runner.js';
7
6
  const DEFAULT_TIMEOUT_MS = 5_000;
7
+ const AUTH_PATTERNS = {
8
+ claude: {
9
+ positive: /authenticated|logged[\s_-]*in|"loggedIn"\s*:\s*true/i,
10
+ negative: /not[\s_-]*authenticated|not[\s_-]*logged[\s_-]*in|logged[\s_-]*out|unauthenticated|"loggedIn"\s*:\s*false/i,
11
+ },
12
+ codex: {
13
+ positive: /logged[\s_-]*in|authenticated/i,
14
+ negative: /not[\s_-]*authenticated|not[\s_-]*logged[\s_-]*in|logged[\s_-]*out|unauthenticated/i,
15
+ },
16
+ opencode: {
17
+ positive: /configured|available/i,
18
+ negative: /not[\s_-]*configured|no[\s_-]+providers?[\s_-]+available|unavailable/i,
19
+ },
20
+ openclaw: {
21
+ positive: /(^|[^a-z])ok([^a-z]|$)|healthy/i,
22
+ negative: /not[\s_-]*healthy|unhealthy|not[\s_-]*ok/i,
23
+ },
24
+ pi: {
25
+ positive: /\S/,
26
+ negative: /not[\s_-]*authenticated|not[\s_-]*logged[\s_-]*in|unauthenticated|no[\s_-]+providers?/i,
27
+ },
28
+ };
8
29
  /** True when a value is a defined, non-blank string. */
9
30
  function isNonEmpty(value) {
10
31
  return value !== undefined && value.trim().length > 0;
@@ -16,16 +37,25 @@ export class DoctorRunner {
16
37
  timeout;
17
38
  env;
18
39
  fs = new NodeFileSystem();
40
+ logger;
19
41
  constructor(options = {}) {
20
42
  this.runner = options.runner ?? new AiRunner();
21
43
  this.detector = options.agentDetector ?? new AgentDetector({ runner: this.runner });
22
44
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
23
45
  this.env = options.env ?? getProcessEnv();
46
+ this.logger = options.logger ?? getLogger('doctor');
24
47
  }
25
48
  /** Run a health check on all supported agents. */
26
49
  async runAll() {
27
50
  const detected = await this.detector.detectAll();
28
- return await Promise.all(detected.map((agent) => this.buildResult(agent)));
51
+ const byName = new Map(detected.map((agent) => [agent.name, agent]));
52
+ return await Promise.all(DISPLAY_ORDER.map((agent) => this.buildResult(byName.get(agent) ?? {
53
+ name: agent,
54
+ installed: false,
55
+ version: null,
56
+ channels: [],
57
+ error: `Unknown agent: ${agent}`,
58
+ })));
29
59
  }
30
60
  /** Run a health check on one agent. */
31
61
  async runOne(agent) {
@@ -33,6 +63,7 @@ export class DoctorRunner {
33
63
  }
34
64
  async buildResult(detected) {
35
65
  const tier = TIER2_AGENTS.has(detected.name) ? 2 : 1;
66
+ this.logger.debug('checking agent', { agent: detected.name, installed: detected.installed, tier });
36
67
  const authenticated = detected.installed && isAgentName(detected.name) ? await this.checkAuth(detected.name) : false;
37
68
  return {
38
69
  agent: detected.name,
@@ -46,23 +77,46 @@ export class DoctorRunner {
46
77
  };
47
78
  }
48
79
  async checkAuth(agent) {
49
- // gemini/codex expose no auth-status command; treat a non-empty credential
50
- // file as authenticated. An empty/zero-byte file is a stale-credential
51
- // false positive, so existence alone is insufficient.
80
+ const home = this.env.HOME || this.env.USERPROFILE || '';
52
81
  if (agent === 'gemini')
53
- return this.hasNonEmptyFile(join(homedir(), '.gemini', 'settings.json'));
54
- if (agent === 'codex' && (await this.hasNonEmptyFile(join(homedir(), '.codex', 'auth.json'))))
55
- return true;
82
+ return this.geminiSettingsContainCredentials(home);
83
+ if (agent === 'codex')
84
+ return this.checkCodexAuth(home);
56
85
  // pi reads provider keys from the environment; require a non-empty value
57
86
  // rather than mere presence (an empty export is not a usable credential).
58
87
  if (agent === 'pi' && (isNonEmpty(this.env.GOOGLE_API_KEY) || isNonEmpty(this.env.ANTHROPIC_API_KEY)))
59
88
  return true;
60
- const command = this.runner.runAuthCommand(agent, { timeout: this.timeout });
61
- if (command === null)
89
+ return (await this.probeAuthOutput(agent)) === true;
90
+ }
91
+ async checkCodexAuth(home) {
92
+ const probeStatus = await this.probeAuthOutput('codex');
93
+ if (probeStatus !== null)
94
+ return probeStatus;
95
+ return ((await this.hasNonEmptyFile(joinPath(home, '.codex', 'auth.json'))) ||
96
+ (await this.hasNonEmptyFile(joinPath(home, '.codex', 'auth'))));
97
+ }
98
+ async geminiSettingsContainCredentials(home) {
99
+ try {
100
+ return /auth|token|key/i.test(await this.fs.readFile(joinPath(home, '.gemini', 'settings.json')));
101
+ }
102
+ catch {
62
103
  return false;
104
+ }
105
+ }
106
+ async probeAuthOutput(agent) {
107
+ const command = this.runner.runAuthCommand(agent, { timeout: this.timeout });
108
+ const patterns = AUTH_PATTERNS[agent];
109
+ if (command === null || patterns === undefined)
110
+ return null;
63
111
  const result = await command;
64
- return (result.exitCode === 0 &&
65
- !/not authenticated|not logged|unauthenticated/i.test(`${result.stdout}\n${result.stderr}`));
112
+ if (result.exitCode !== 0)
113
+ return false;
114
+ const output = `${result.stdout}\n${result.stderr}`;
115
+ if (patterns.negative.test(output))
116
+ return false;
117
+ if (patterns.positive.test(output))
118
+ return true;
119
+ return null;
66
120
  }
67
121
  /** True when the path exists and has a non-zero size. */
68
122
  async hasNonEmptyFile(path) {
@@ -0,0 +1,38 @@
1
+ import type { ProcessEvents } from '@gobing-ai/ts-runtime';
2
+ /** Typed event map for agent-runner observability. All events prefixed `agent.`. */
3
+ export type AgentEvents = {
4
+ /** Emitted immediately before an agent CLI invocation starts. */
5
+ 'agent.invoke.start': (data: {
6
+ agent: string;
7
+ operation: string;
8
+ label: string;
9
+ }) => void;
10
+ /** Emitted after an agent CLI invocation exits. */
11
+ 'agent.invoke.exit': (data: {
12
+ agent: string;
13
+ operation: string;
14
+ label: string;
15
+ exitCode: number | null;
16
+ signal?: string;
17
+ durationMs: number;
18
+ }) => void;
19
+ /** Emitted when a long-running team agent process starts. */
20
+ 'agent.started': (data: {
21
+ agentId: string;
22
+ agentType: string;
23
+ pid: number | null;
24
+ }) => void;
25
+ /** Emitted when a long-running team agent process stops. */
26
+ 'agent.stopped': (data: {
27
+ agentId: string;
28
+ exitCode: number | null;
29
+ }) => void;
30
+ /** Emitted when a message is sent to a team agent process. */
31
+ 'agent.message.sent': (data: {
32
+ agentId: string;
33
+ ok: boolean;
34
+ }) => void;
35
+ };
36
+ /** Event map for process-level observability emitted by AiRunner-owned executors. */
37
+ export type AiRunnerProcessEvents = ProcessEvents;
38
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG;IACtB,iEAAiE;IACjE,oBAAoB,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1F,mDAAmD;IACnD,mBAAmB,EAAE,CAAC,IAAI,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACtB,KAAK,IAAI,CAAC;IACX,6DAA6D;IAC7D,eAAe,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IAC5F,4DAA4D;IAC5D,eAAe,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9E,8DAA8D;IAC9D,oBAAoB,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;CAC1E,CAAC;AAEF,qFAAqF;AACrF,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC"}
package/dist/events.js ADDED
File without changes
@@ -1,4 +1,5 @@
1
1
  import { type SyncProcessExecutor } from '@gobing-ai/ts-runtime';
2
+ /** Context used to construct the identity preamble injected into agent prompts. */
2
3
  export interface IdentityContext {
3
4
  agentId: string;
4
5
  agentType: string;
@@ -16,6 +17,8 @@ export interface IdentityContext {
16
17
  gitDirty?: boolean;
17
18
  guardrails?: string[];
18
19
  }
20
+ /** Build a human-readable identity preamble string that describes the agent, its task, peers, guardrails, and git context. */
19
21
  export declare function buildIdentityPreamble(ctx: IdentityContext): string;
22
+ /** Query git for the current branch name and dirty file count in `workspacePath`. Returns a pre-formatted "Git context" block, or `null` if git is unavailable or the directory is not a repo. */
20
23
  export declare function getGitContext(workspacePath: string, executor?: SyncProcessExecutor): string | null;
21
24
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEzF,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA8ClE;AAED,wBAAgB,aAAa,CACzB,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,mBAAkD,GAC7D,MAAM,GAAG,IAAI,CAYf"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEzF,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8HAA8H;AAC9H,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA8ClE;AAED,kMAAkM;AAClM,wBAAgB,aAAa,CACzB,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,mBAAkD,GAC7D,MAAM,GAAG,IAAI,CAYf"}
package/dist/identity.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BunSyncProcessExecutor } from '@gobing-ai/ts-runtime';
2
+ /** Build a human-readable identity preamble string that describes the agent, its task, peers, guardrails, and git context. */
2
3
  export function buildIdentityPreamble(ctx) {
3
4
  const sections = [
4
5
  `You are agent \`${ctx.agentId}\` (${ctx.agentType}) in workspace \`${ctx.workspace}\`.`,
@@ -31,6 +32,7 @@ export function buildIdentityPreamble(ctx) {
31
32
  }
32
33
  return `${sections.join('\n\n')}\n`;
33
34
  }
35
+ /** Query git for the current branch name and dirty file count in `workspacePath`. Returns a pre-formatted "Git context" block, or `null` if git is unavailable or the directory is not a repo. */
34
36
  export function getGitContext(workspacePath, executor = new BunSyncProcessExecutor()) {
35
37
  const git = Bun.which('git');
36
38
  if (git === null)
package/dist/index.d.ts CHANGED
@@ -3,8 +3,9 @@ export * from './agent-spec';
3
3
  export * from './agents/shims';
4
4
  export * from './ai-runner';
5
5
  export * from './doctor-runner';
6
+ export * from './events';
6
7
  export * from './identity';
7
- export * from './message-service';
8
+ export * from './messages';
8
9
  export * from './slash-command';
9
10
  export * from './team-agent-process';
10
11
  export * from './team-orchestrator';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -3,8 +3,9 @@ export * from './agent-spec.js';
3
3
  export * from './agents/shims.js';
4
4
  export * from './ai-runner.js';
5
5
  export * from './doctor-runner.js';
6
+ export * from './events.js';
6
7
  export * from './identity.js';
7
- export * from './message-service.js';
8
+ export * from './messages.js';
8
9
  export * from './slash-command.js';
9
10
  export * from './team-agent-process.js';
10
11
  export * from './team-orchestrator.js';
@@ -0,0 +1,4 @@
1
+ import type { InboxMessage } from '@gobing-ai/ts-db/inbox';
2
+ /** Renders an inbox message into the line injected into an agent's stdin. */
3
+ export declare function formatMessage(msg: InboxMessage): string;
4
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,4 @@
1
+ /** Renders an inbox message into the line injected into an agent's stdin. */
2
+ export function formatMessage(msg) {
3
+ return `[task from=${msg.fromId ?? 'operator'} id=${msg.id}] ${msg.body}`;
4
+ }
@@ -1,21 +1,28 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import { type PipeProcessSpawner } from '@gobing-ai/ts-runtime';
2
+ import { type Logger } from '@gobing-ai/ts-infra';
3
+ import { ProcessExecutor } from '@gobing-ai/ts-runtime';
3
4
  import type { AgentSpec } from './agent-spec';
5
+ /** Options for spawning a team agent subprocess. */
4
6
  export interface AgentProcessOptions {
5
7
  spec: AgentSpec;
6
8
  command: string[];
7
9
  env?: Record<string, string>;
8
10
  cwd?: string;
9
- processSpawner?: PipeProcessSpawner;
11
+ processExecutor?: ProcessExecutor;
12
+ logger?: Logger;
10
13
  }
11
14
  type ProcessStatus = 'running' | 'stopped' | 'errored';
15
+ /**
16
+ * Manages the lifecycle of a single agent subprocess — start, stop, message send, and stdout/stderr subscription.
17
+ * The identity preamble is built by `TeamOrchestrator` and baked into `command` before the process is constructed.
18
+ */
12
19
  export declare class TeamAgentProcess {
13
20
  readonly agentId: string;
14
- readonly identityPreamble: string;
15
21
  private readonly command;
16
22
  private readonly env;
17
23
  private readonly cwd;
18
- private readonly processSpawner;
24
+ private readonly processExecutor;
25
+ private readonly logger;
19
26
  private subprocess;
20
27
  private status;
21
28
  private exitCode;
@@ -31,6 +38,7 @@ export declare class TeamAgentProcess {
31
38
  getPid(): number | null;
32
39
  getExitCode(): number | null;
33
40
  private pipe;
41
+ private warn;
34
42
  }
35
43
  export {};
36
44
  //# sourceMappingURL=team-agent-process.d.ts.map
@@ -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,EAA2C,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACzG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,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,cAAc,CAAC,EAAE,kBAAkB,CAAC;CACvC;AAED,KAAK,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEvD,qBAAa,gBAAgB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,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,cAAc,CAAqB;IACpD,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqC;gBAErD,OAAO,EAAE,mBAAmB;IAgBlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAWrD,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;CAerB"}
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,EAAa,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAoB,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,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;CACnB;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;gBAErD,OAAO,EAAE,mBAAmB;IASlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBtB,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;IAiBlB,OAAO,CAAC,IAAI;CAOf"}
@@ -1,30 +1,28 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import { BunPipeProcessSpawner } from '@gobing-ai/ts-runtime';
3
- import { buildIdentityPreamble } from './identity.js';
2
+ import { getLogger } from '@gobing-ai/ts-infra';
3
+ import { ProcessExecutor } from '@gobing-ai/ts-runtime';
4
+ /**
5
+ * Manages the lifecycle of a single agent subprocess — start, stop, message send, and stdout/stderr subscription.
6
+ * The identity preamble is built by `TeamOrchestrator` and baked into `command` before the process is constructed.
7
+ */
4
8
  export class TeamAgentProcess {
5
9
  agentId;
6
- identityPreamble;
7
10
  command;
8
11
  env;
9
12
  cwd;
10
- processSpawner;
13
+ processExecutor;
14
+ logger;
11
15
  subprocess = null;
12
16
  status = 'stopped';
13
17
  exitCode = null;
14
18
  subscribers = new Set();
15
19
  constructor(options) {
16
20
  this.agentId = options.spec.id;
17
- this.identityPreamble = buildIdentityPreamble({
18
- agentId: options.spec.id,
19
- agentType: options.spec.type,
20
- workspace: options.spec.workspace,
21
- purpose: options.spec.purpose,
22
- systemPrompt: typeof options.spec.config.systemPrompt === 'string' ? options.spec.config.systemPrompt : undefined,
23
- });
24
21
  this.command = options.command;
25
22
  this.env = options.env;
26
23
  this.cwd = options.cwd ?? options.spec.workspace;
27
- this.processSpawner = options.processSpawner ?? new BunPipeProcessSpawner();
24
+ this.processExecutor = options.processExecutor ?? new ProcessExecutor();
25
+ this.logger = options.logger ?? getLogger('team-agent');
28
26
  }
29
27
  async start() {
30
28
  if (this.status === 'running')
@@ -32,9 +30,10 @@ export class TeamAgentProcess {
32
30
  const [command, ...args] = this.command;
33
31
  if (command === undefined)
34
32
  throw new Error(`${this.agentId}: command must not be empty`);
35
- this.subprocess = this.processSpawner.spawn({
33
+ this.subprocess = this.processExecutor.runStreaming({
36
34
  command,
37
35
  args,
36
+ label: `team-agent.${this.agentId}`,
38
37
  ...(this.cwd !== undefined ? { cwd: this.cwd } : {}),
39
38
  ...(this.env !== undefined ? { env: this.env } : {}),
40
39
  });
@@ -59,11 +58,13 @@ export class TeamAgentProcess {
59
58
  try {
60
59
  process.endStdin();
61
60
  }
62
- catch {
63
- // Process may have already closed stdin.
61
+ catch (error) {
62
+ this.warn('stdin close failed', 'stop.endStdin', error);
64
63
  }
65
64
  process.kill('SIGTERM');
66
- const timeout = Bun.sleep(5000).then(() => 'timeout');
65
+ const timeout = new Promise((resolve) => {
66
+ setTimeout(() => resolve('timeout'), 5000);
67
+ });
67
68
  const result = await Promise.race([process.exited, timeout]);
68
69
  if (result === 'timeout') {
69
70
  process.kill('SIGKILL');
@@ -76,13 +77,16 @@ export class TeamAgentProcess {
76
77
  this.subprocess = null;
77
78
  }
78
79
  async send(message) {
79
- if (this.status !== 'running' || this.subprocess === null)
80
+ if (this.status !== 'running' || this.subprocess === null) {
81
+ this.warn('send skipped because process is not running', 'send.notRunning');
80
82
  return { ok: false };
83
+ }
81
84
  try {
82
85
  this.subprocess.writeStdin(`${message}\n`);
83
86
  return { ok: true };
84
87
  }
85
- catch {
88
+ catch (error) {
89
+ this.warn('stdin write failed', 'send.writeStdin', error);
86
90
  this.status = 'errored';
87
91
  return { ok: false };
88
92
  }
@@ -114,7 +118,8 @@ export class TeamAgentProcess {
114
118
  subscriber(buffer);
115
119
  }
116
120
  }
117
- catch {
121
+ catch (error) {
122
+ this.warn('stream pipe failed', 'pipe', error);
118
123
  if (this.status === 'running')
119
124
  this.status = 'errored';
120
125
  }
@@ -122,4 +127,11 @@ export class TeamAgentProcess {
122
127
  reader.releaseLock();
123
128
  }
124
129
  }
130
+ warn(message, op, error) {
131
+ this.logger.warn(message, {
132
+ agentId: this.agentId,
133
+ op,
134
+ ...(error !== undefined ? { error: error instanceof Error ? error.message : String(error) } : {}),
135
+ });
136
+ }
125
137
  }