@gobing-ai/ts-ai-runner 0.3.19 → 0.3.21

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/ai-runner.js CHANGED
@@ -50,7 +50,9 @@ export class AiRunner {
50
50
  }
51
51
  /** Build an agent prompt command without executing it. */
52
52
  buildPromptCommand(agent, promptOptions, options = {}) {
53
- return getAgentShim(agent).getPromptCommand(this.withIdentityPreamble(agent, promptOptions, options));
53
+ return buildAgentCommand(agent, promptOptions, {
54
+ workspace: options.cwd ?? this.defaultCwd ?? getProcessCwd(),
55
+ });
54
56
  }
55
57
  /** Run an agent authentication command, or return null when unsupported. */
56
58
  runAuthCommand(agent, options = {}) {
@@ -93,22 +95,6 @@ export class AiRunner {
93
95
  durationMs: result.durationMs,
94
96
  };
95
97
  }
96
- withIdentityPreamble(agent, promptOptions, options) {
97
- if (!hasIdentityOptions(promptOptions))
98
- return promptOptions;
99
- const workspace = options.cwd ?? this.defaultCwd ?? getProcessCwd();
100
- const preamble = buildIdentityPreamble({
101
- agentId: agent,
102
- agentType: agent,
103
- workspace,
104
- purpose: promptOptions.purpose,
105
- systemPrompt: promptOptions.systemPrompt,
106
- taskId: promptOptions.taskId,
107
- peers: promptOptions.peers,
108
- });
109
- const input = promptOptions.input === undefined ? preamble : `${preamble}\n${promptOptions.input}`;
110
- return { ...promptOptions, input };
111
- }
112
98
  }
113
99
  function hasIdentityOptions(options) {
114
100
  return (options.purpose !== undefined ||
@@ -116,3 +102,27 @@ function hasIdentityOptions(options) {
116
102
  options.taskId !== undefined ||
117
103
  (options.peers !== undefined && options.peers.length > 0));
118
104
  }
105
+ /**
106
+ * Shared command-build seam: identity preamble + shim dispatch. Both the
107
+ * one-shot path (`AiRunner.buildPromptCommand`) and the team-mode path
108
+ * (`TeamOrchestrator.startAgent`) resolve argv through this single function
109
+ * so equivalent `PromptOptions` produce equivalent argv (R5 parity).
110
+ */
111
+ export function buildAgentCommand(agent, promptOptions, context) {
112
+ return getAgentShim(agent).getPromptCommand(applyIdentityPreamble(agent, promptOptions, context));
113
+ }
114
+ function applyIdentityPreamble(agent, promptOptions, context) {
115
+ if (!hasIdentityOptions(promptOptions))
116
+ return promptOptions;
117
+ const preamble = buildIdentityPreamble({
118
+ agentId: agent,
119
+ agentType: agent,
120
+ workspace: context.workspace,
121
+ purpose: promptOptions.purpose,
122
+ systemPrompt: promptOptions.systemPrompt,
123
+ taskId: promptOptions.taskId,
124
+ peers: promptOptions.peers,
125
+ });
126
+ const input = promptOptions.input === undefined ? preamble : `${preamble}\n${promptOptions.input}`;
127
+ return { ...promptOptions, input };
128
+ }
@@ -1,5 +1,7 @@
1
1
  import { type Logger } from '@gobing-ai/ts-infra';
2
+ import { type FileSystem } from '@gobing-ai/ts-runtime';
2
3
  import { AgentDetector } from './agent-detector';
4
+ import { type AgentName } from './agents/shims';
3
5
  import { AiRunner } from './ai-runner';
4
6
  /** Health-check result for one coding agent. */
5
7
  export interface DoctorResult {
@@ -17,6 +19,10 @@ export interface DoctorResult {
17
19
  tier: 1 | 2;
18
20
  /** Agent-specific channels or models when available. */
19
21
  channels: string[];
22
+ /** True when the resolved canonical id is marked deprecated. */
23
+ deprecated?: boolean;
24
+ /** Canonical replacement when the resolved id is deprecated, if any. */
25
+ replacedBy?: AgentName;
20
26
  /** Probe error when unavailable. */
21
27
  error: string | null;
22
28
  }
@@ -32,6 +38,8 @@ export interface DoctorRunnerOptions {
32
38
  env?: Record<string, string | undefined>;
33
39
  /** Logger for health-check diagnostics. Defaults to `getLogger('doctor')`. */
34
40
  logger?: Logger;
41
+ /** Filesystem for auth-file checks. Defaults to `createNodeFileSystem()`; inject to test without disk. */
42
+ fileSystem?: FileSystem;
35
43
  }
36
44
  /** Runs installation and auth health checks for supported coding agents. */
37
45
  export declare class DoctorRunner {
@@ -1 +1 @@
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
+ {"version":3,"file":"doctor-runner.d.ts","sourceRoot":"","sources":["../src/doctor-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAwB,KAAK,UAAU,EAA2B,MAAM,uBAAuB,CAAC;AACvG,OAAO,EAAE,aAAa,EAAsB,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,KAAK,SAAS,EAAiD,MAAM,gBAAgB,CAAC;AAC/F,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,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,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;IAChB,0GAA0G;IAC1G,UAAU,CAAC,EAAE,UAAU,CAAC;CAC3B;AAwCD,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,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,mBAAwB;IAS7C,kDAAkD;IAC5C,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAkBvC,uCAAuC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;YAIpC,WAAW;YAmBX,SAAS;YAcT,cAAc;YASd,gCAAgC;YAQhC,eAAe;IAY7B,yDAAyD;YAC3C,eAAe;CAKhC"}
@@ -1,7 +1,7 @@
1
1
  import { getLogger } from '@gobing-ai/ts-infra';
2
- import { getProcessEnv, joinPath, NodeFileSystem } from '@gobing-ai/ts-runtime';
2
+ import { createNodeFileSystem, getProcessEnv, joinPath } from '@gobing-ai/ts-runtime';
3
3
  import { AgentDetector } from './agent-detector.js';
4
- import { DISPLAY_ORDER, isAgentName, TIER2_AGENTS } from './agents/shims.js';
4
+ import { DISPLAY_ORDER, resolveAgentName, TIER2_AGENTS } from './agents/shims.js';
5
5
  import { AiRunner } from './ai-runner.js';
6
6
  const DEFAULT_TIMEOUT_MS = 5_000;
7
7
  const AUTH_PATTERNS = {
@@ -25,6 +25,14 @@ const AUTH_PATTERNS = {
25
25
  positive: /\S/,
26
26
  negative: /not[\s_-]*authenticated|not[\s_-]*logged[\s_-]*in|unauthenticated|no[\s_-]+providers?/i,
27
27
  },
28
+ omp: {
29
+ positive: /\S/,
30
+ negative: /not[\s_-]*authenticated|not[\s_-]*logged[\s_-]*in|unauthenticated|no[\s_-]+providers?/i,
31
+ },
32
+ hermes: {
33
+ positive: /(^|[^a-z])ok([^a-z]|$)|healthy|configured|ready/i,
34
+ negative: /not[\s_-]*(configured|healthy|ok)|unhealthy|missing[\s_-]+dependenc|error|failed/i,
35
+ },
28
36
  };
29
37
  /** True when a value is a defined, non-blank string. */
30
38
  function isNonEmpty(value) {
@@ -36,13 +44,14 @@ export class DoctorRunner {
36
44
  runner;
37
45
  timeout;
38
46
  env;
39
- fs = new NodeFileSystem();
47
+ fs;
40
48
  logger;
41
49
  constructor(options = {}) {
42
50
  this.runner = options.runner ?? new AiRunner();
43
51
  this.detector = options.agentDetector ?? new AgentDetector({ runner: this.runner });
44
52
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
45
53
  this.env = options.env ?? getProcessEnv();
54
+ this.fs = options.fileSystem ?? createNodeFileSystem();
46
55
  this.logger = options.logger ?? getLogger('doctor');
47
56
  }
48
57
  /** Run a health check on all supported agents. */
@@ -62,9 +71,10 @@ export class DoctorRunner {
62
71
  return this.buildResult(await this.detector.detectOne(agent));
63
72
  }
64
73
  async buildResult(detected) {
65
- const tier = TIER2_AGENTS.has(detected.name) ? 2 : 1;
74
+ const canonical = resolveAgentName(detected.name) ?? null;
75
+ const tier = canonical !== null && TIER2_AGENTS.has(canonical) ? 2 : 1;
66
76
  this.logger.debug('checking agent', { agent: detected.name, installed: detected.installed, tier });
67
- const authenticated = detected.installed && isAgentName(detected.name) ? await this.checkAuth(detected.name) : false;
77
+ const authenticated = detected.installed && canonical !== null ? await this.checkAuth(canonical) : false;
68
78
  return {
69
79
  agent: detected.name,
70
80
  installed: detected.installed,
@@ -73,6 +83,8 @@ export class DoctorRunner {
73
83
  usable: detected.installed && detected.version !== null && authenticated,
74
84
  tier,
75
85
  channels: detected.channels,
86
+ deprecated: detected.deprecated,
87
+ replacedBy: detected.replacedBy,
76
88
  error: detected.error,
77
89
  };
78
90
  }
@@ -82,9 +94,10 @@ export class DoctorRunner {
82
94
  return this.geminiSettingsContainCredentials(home);
83
95
  if (agent === 'codex')
84
96
  return this.checkCodexAuth(home);
85
- // pi reads provider keys from the environment; require a non-empty value
86
- // rather than mere presence (an empty export is not a usable credential).
87
- if (agent === 'pi' && (isNonEmpty(this.env.GOOGLE_API_KEY) || isNonEmpty(this.env.ANTHROPIC_API_KEY)))
97
+ // pi and omp read provider keys from the environment; require a non-empty
98
+ // value rather than mere presence (an empty export is not a usable credential).
99
+ if ((agent === 'pi' || agent === 'omp') &&
100
+ (isNonEmpty(this.env.GOOGLE_API_KEY) || isNonEmpty(this.env.ANTHROPIC_API_KEY)))
88
101
  return true;
89
102
  return (await this.probeAuthOutput(agent)) === true;
90
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"slash-command.d.ts","sourceRoot":"","sources":["../src/slash-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAShD,2EAA2E;AAC3E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAgB7E"}
1
+ {"version":3,"file":"slash-command.d.ts","sourceRoot":"","sources":["../src/slash-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAShD,2EAA2E;AAC3E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiB7E"}
@@ -32,6 +32,7 @@ export function translateSlashCommand(agent, input) {
32
32
  case 'codex':
33
33
  return `$${plugin}-${command}${suffix}`;
34
34
  case 'pi':
35
+ case 'omp':
35
36
  return `/skill:${plugin}-${command}${suffix}`;
36
37
  default:
37
38
  return `/${plugin}-${command}${suffix}`;
@@ -21,15 +21,15 @@ export declare class TeamOrchestrator {
21
21
  private readonly processFactory;
22
22
  private readonly events;
23
23
  constructor(configDir: string, inbox: InboxMessageDao, options?: TeamOrchestratorOptions);
24
- loadSpecs(): AgentSpec[];
25
- getSpec(id: string): AgentSpec | undefined;
24
+ loadSpecs(): Promise<AgentSpec[]>;
25
+ getSpec(id: string): Promise<AgentSpec | undefined>;
26
26
  startAgent(id: string): Promise<TeamAgentProcess>;
27
27
  stopAgent(id: string): Promise<void>;
28
28
  restartAgent(id: string): Promise<TeamAgentProcess>;
29
29
  sendMessage(fromId: string | null, toId: string, body: string, inReplyTo?: string): Promise<string>;
30
30
  getRunningAgents(): Map<string, TeamAgentProcess>;
31
- getAgentStatus(id: string): 'running' | 'stopped' | 'errored' | 'unknown';
32
- getPeerSpecs(workspace: string, excludeId?: string): AgentSpec[];
31
+ getAgentStatus(id: string): Promise<'running' | 'stopped' | 'errored' | 'unknown'>;
32
+ getPeerSpecs(workspace: string, excludeId?: string): Promise<AgentSpec[]>;
33
33
  stopAll(): Promise<void>;
34
34
  on<K extends keyof AgentEvents>(event: K, listener: AgentEvents[K]): () => void;
35
35
  private requireSpec;
@@ -1 +1 @@
1
- {"version":3,"file":"team-orchestrator.d.ts","sourceRoot":"","sources":["../src/team-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,KAAK,mBAAmB,GAAG,CAAC,OAAO,EAAE,qBAAqB,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK,gBAAgB,CAAC;AAE5G,oDAAoD;AACpD,MAAM,WAAW,uBAAuB;IACpC,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAOrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAP1B,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsB;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAG1B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,eAAe,EACvC,OAAO,GAAE,uBAA4B;IAMzC,SAAS,IAAI,SAAS,EAAE;IAKxB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAKpC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAsCjD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKnD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQzG,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIjD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS;IAMzE,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE;IAK1D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,EAAE,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAK/E,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,gBAAgB;YAKV,qBAAqB;YAIrB,UAAU;CAa3B"}
1
+ {"version":3,"file":"team-orchestrator.d.ts","sourceRoot":"","sources":["../src/team-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,KAAK,mBAAmB,GAAG,CAAC,OAAO,EAAE,qBAAqB,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK,gBAAgB,CAAC;AAE5G,oDAAoD;AACpD,MAAM,WAAW,uBAAuB;IACpC,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAOrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAP1B,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsB;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAG1B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,eAAe,EACvC,OAAO,GAAE,uBAA4B;IAMnC,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAKjC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAKnD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiCjD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKnD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQzG,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAI3C,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAMlF,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAKzE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,EAAE,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;YAKjE,WAAW;IAMzB,OAAO,CAAC,gBAAgB;YAMV,qBAAqB;YAIrB,UAAU;CAa3B"}
@@ -1,7 +1,7 @@
1
1
  import { EventBus } from '@gobing-ai/ts-infra';
2
2
  import { loadAgentSpecs } from './agent-spec.js';
3
- import { getAgentShim, isAgentName } from './agents/shims.js';
4
- import { buildIdentityPreamble } from './identity.js';
3
+ import { resolveAgentName } from './agents/shims.js';
4
+ import { buildAgentCommand } from './ai-runner.js';
5
5
  import { formatMessage } from './messages.js';
6
6
  import { TeamAgentProcess } from './team-agent-process.js';
7
7
  /**
@@ -21,37 +21,28 @@ export class TeamOrchestrator {
21
21
  this.processFactory = options.processFactory ?? ((processOptions) => new TeamAgentProcess(processOptions));
22
22
  this.events = options.events ?? new EventBus();
23
23
  }
24
- loadSpecs() {
25
- this.specs = loadAgentSpecs(this.configDir);
24
+ async loadSpecs() {
25
+ this.specs = await loadAgentSpecs(this.configDir);
26
26
  return [...this.specs];
27
27
  }
28
- getSpec(id) {
28
+ async getSpec(id) {
29
29
  if (this.specs.length === 0)
30
- this.loadSpecs();
30
+ await this.loadSpecs();
31
31
  return this.specs.find((spec) => spec.id === id);
32
32
  }
33
33
  async startAgent(id) {
34
- const spec = this.requireSpec(id);
34
+ const spec = await this.requireSpec(id);
35
35
  const agentType = this.requireAgentName(spec.type);
36
- const peers = this.getPeerSpecs(spec.workspace, spec.id).map((peer) => ({
36
+ const peers = (await this.getPeerSpecs(spec.workspace, spec.id)).map((peer) => ({
37
37
  id: peer.id,
38
38
  type: peer.type,
39
39
  purpose: peer.purpose,
40
40
  }));
41
- const identityPreamble = buildIdentityPreamble({
42
- agentId: spec.id,
43
- agentType: spec.type,
44
- workspace: spec.workspace,
41
+ const command = buildAgentCommand(agentType, {
45
42
  purpose: spec.purpose,
46
43
  systemPrompt: typeof spec.config.systemPrompt === 'string' ? spec.config.systemPrompt : undefined,
47
44
  peers,
48
- });
49
- const command = getAgentShim(agentType).getPromptCommand({
50
- input: identityPreamble,
51
- purpose: spec.purpose,
52
- systemPrompt: typeof spec.config.systemPrompt === 'string' ? spec.config.systemPrompt : undefined,
53
- peers,
54
- });
45
+ }, { workspace: spec.workspace });
55
46
  const process = this.processFactory({
56
47
  spec,
57
48
  command: [command.command, ...command.args],
@@ -89,15 +80,15 @@ export class TeamOrchestrator {
89
80
  getRunningAgents() {
90
81
  return new Map(this.running);
91
82
  }
92
- getAgentStatus(id) {
83
+ async getAgentStatus(id) {
93
84
  const process = this.running.get(id);
94
85
  if (process === undefined)
95
- return this.getSpec(id) === undefined ? 'unknown' : 'stopped';
86
+ return (await this.getSpec(id)) === undefined ? 'unknown' : 'stopped';
96
87
  return process.getStatus();
97
88
  }
98
- getPeerSpecs(workspace, excludeId) {
89
+ async getPeerSpecs(workspace, excludeId) {
99
90
  if (this.specs.length === 0)
100
- this.loadSpecs();
91
+ await this.loadSpecs();
101
92
  return this.specs.filter((spec) => spec.workspace === workspace && spec.id !== excludeId);
102
93
  }
103
94
  async stopAll() {
@@ -107,16 +98,17 @@ export class TeamOrchestrator {
107
98
  this.events.on(event, listener);
108
99
  return () => this.events.off(event, listener);
109
100
  }
110
- requireSpec(id) {
111
- const spec = this.getSpec(id);
101
+ async requireSpec(id) {
102
+ const spec = await this.getSpec(id);
112
103
  if (spec === undefined)
113
104
  throw new Error(`Agent spec not found: ${id}`);
114
105
  return spec;
115
106
  }
116
107
  requireAgentName(type) {
117
- if (!isAgentName(type))
108
+ const canonical = resolveAgentName(type);
109
+ if (canonical === undefined)
118
110
  throw new Error(`Unsupported agent type: ${type}`);
119
- return type;
111
+ return canonical;
120
112
  }
121
113
  async injectPendingMessages(process) {
122
114
  return this.flushInbox(process, 'startup stdin injection failed');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-ai-runner",
3
- "version": "0.3.19",
3
+ "version": "0.3.21",
4
4
  "description": "@gobing-ai/ts-ai-runner — Coding-agent shims, detection, doctor checks, and prompt execution.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,9 +47,9 @@
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-db": "^0.3.19",
51
- "@gobing-ai/ts-infra": "^0.3.19",
52
- "@gobing-ai/ts-runtime": "^0.3.19"
50
+ "@gobing-ai/ts-db": "^0.3.21",
51
+ "@gobing-ai/ts-infra": "^0.3.21",
52
+ "@gobing-ai/ts-runtime": "^0.3.21"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/bun": "1.3.14"
@@ -1,9 +1,9 @@
1
- import { type AgentName, DISPLAY_ORDER, getAgentShim, isAgentName } from './agents/shims';
1
+ import { type AgentName, DISPLAY_ORDER, getAgentShim, resolveAgentName } from './agents/shims';
2
2
  import { type AgentRunResult, AiRunner } from './ai-runner';
3
3
 
4
4
  /** Installation/version probe result for one coding agent. */
5
5
  export interface DetectedAgent {
6
- /** Agent identifier. */
6
+ /** Canonical agent identifier (alias input is resolved to its canonical id). */
7
7
  name: AgentName | string;
8
8
  /** Whether the agent CLI was found and returned a parseable version. */
9
9
  installed: boolean;
@@ -13,6 +13,10 @@ export interface DetectedAgent {
13
13
  channels: string[];
14
14
  /** Detection error when unavailable. */
15
15
  error: string | null;
16
+ /** True when the resolved canonical id is marked deprecated. */
17
+ deprecated?: boolean;
18
+ /** Canonical replacement when the resolved id is deprecated, if any. */
19
+ replacedBy?: AgentName;
16
20
  }
17
21
 
18
22
  /** Constructor options for AgentDetector. */
@@ -35,49 +39,56 @@ export class AgentDetector {
35
39
  this.runner = options.runner ?? new AiRunner();
36
40
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
37
41
  }
38
-
39
- /** Probe all bundled agents in display order. */
42
+ /** Probe all bundled canonical agents in display order. */
40
43
  async detectAll(): Promise<DetectedAgent[]> {
41
44
  return await Promise.all(DISPLAY_ORDER.map((agent) => this.detectOne(agent)));
42
45
  }
43
46
 
44
- /** Probe one agent by name. */
47
+ /** Probe one agent by canonical id or alias. */
45
48
  async detectOne(agent: string): Promise<DetectedAgent> {
46
- if (!isAgentName(agent)) return unavailable(agent, `Unknown agent: ${agent}`);
49
+ const canonical = resolveAgentName(agent);
50
+ if (canonical === undefined) return unavailable(agent, `Unknown agent: ${agent}`);
47
51
  try {
48
- return this.parseResult(agent, await this.runner.runVersionCommand(agent, { timeout: this.timeout }));
52
+ return this.parseResult(
53
+ canonical,
54
+ await this.runner.runVersionCommand(canonical, { timeout: this.timeout }),
55
+ );
49
56
  } catch (error) {
50
57
  return unavailable(agent, error instanceof Error ? error.message : String(error));
51
58
  }
52
59
  }
53
60
 
54
- private parseResult(agent: AgentName, result: AgentRunResult): DetectedAgent {
55
- const command = getAgentShim(agent).command;
61
+ private parseResult(canonical: AgentName, result: AgentRunResult): DetectedAgent {
62
+ const command = getAgentShim(canonical).command;
56
63
  const output = `${result.stdout}\n${result.stderr}`.trim();
57
64
  const lower = output.toLowerCase();
58
65
  if (lower.includes('command not found') || lower.includes('enoent') || lower.includes('not recognized')) {
59
- return unavailable(agent, `${command}: command not found`);
66
+ return unavailable(canonical, `${command}: command not found`);
60
67
  }
61
68
  if (result.signal !== undefined) {
62
- return unavailable(agent, `Terminated by signal: ${result.signal}`);
69
+ return unavailable(canonical, `Terminated by signal: ${result.signal}`);
63
70
  }
64
71
  if (result.exitCode === null) {
65
- return unavailable(agent, 'Process did not produce an exit code');
72
+ return unavailable(canonical, 'Process did not produce an exit code');
66
73
  }
67
74
  if (result.exitCode !== 0) {
68
- return unavailable(agent, `Non-zero exit code: ${result.exitCode}. stderr: ${result.stderr.slice(0, 200)}`);
75
+ return unavailable(
76
+ canonical,
77
+ `Non-zero exit code: ${result.exitCode}. stderr: ${result.stderr.slice(0, 200)}`,
78
+ );
69
79
  }
70
80
  const match = VERSION_PATTERN.exec(output);
71
81
  if (match?.groups?.version === undefined) {
72
- return unavailable(agent, 'Could not parse version output');
82
+ return unavailable(canonical, 'Could not parse version output');
73
83
  }
74
84
  const version = output.split('\n')[0]?.trim() || match.groups.version;
75
85
  return {
76
- name: agent,
86
+ name: canonical,
77
87
  installed: true,
78
88
  version,
79
- channels: this.detectChannels(agent, output),
89
+ channels: this.detectChannels(canonical, output),
80
90
  error: null,
91
+ ...deprecationOf(canonical),
81
92
  };
82
93
  }
83
94
 
@@ -86,8 +97,16 @@ export class AgentDetector {
86
97
  return [];
87
98
  }
88
99
  }
89
-
90
100
  /** Build an "unavailable" detection result for an agent with the given error. */
91
101
  function unavailable(name: AgentName | string, error: string): DetectedAgent {
92
- return { name, installed: false, version: null, channels: [], error };
102
+ return { name, installed: false, version: null, channels: [], error, ...deprecationOf(name) };
103
+ }
104
+
105
+ /** Spread deprecation fields when the name resolves to a deprecated canonical id. */
106
+ function deprecationOf(name: AgentName | string): Partial<DetectedAgent> {
107
+ const canonical = resolveAgentName(name);
108
+ if (canonical === undefined) return {};
109
+ const shim = getAgentShim(canonical);
110
+ if (shim.deprecated === undefined) return {};
111
+ return { deprecated: true, replacedBy: shim.deprecated.replacedBy };
93
112
  }
package/src/agent-spec.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  basenamePath,
3
+ createNodeFileSystem,
4
+ type FileSystem,
3
5
  joinPath,
4
- NodeSyncFileSystem,
5
6
  parseYamlObject,
6
- type SyncFileSystem,
7
7
  stringifyYamlObject,
8
8
  } from '@gobing-ai/ts-runtime';
9
9
 
@@ -36,11 +36,13 @@ export function validateAgentId(id: string): string {
36
36
  }
37
37
 
38
38
  /** Load and validate all YAML agent spec files from `configDir`. Throws `ValueError` on parse failures or duplicate IDs. */
39
- export function loadAgentSpecs(configDir: string, fs: SyncFileSystem = new NodeSyncFileSystem()): AgentSpec[] {
40
- const entries = safeReadDir(configDir, fs)
39
+ export async function loadAgentSpecs(configDir: string, fs: FileSystem = createNodeFileSystem()): Promise<AgentSpec[]> {
40
+ const entries = (await safeReadDir(configDir, fs))
41
41
  .filter((entry) => entry.endsWith('.yaml') || entry.endsWith('.yml'))
42
42
  .sort();
43
- const specs = entries.map((entry) => parseAgentSpec(fs.readFile(joinPath(configDir, entry)), entry));
43
+ const specs = await Promise.all(
44
+ entries.map(async (entry) => parseAgentSpec(await fs.readFile(joinPath(configDir, entry)), entry)),
45
+ );
44
46
  const seen = new Set<string>();
45
47
  for (const spec of specs) {
46
48
  validateAgentId(spec.id);
@@ -54,26 +56,26 @@ export function loadAgentSpecs(configDir: string, fs: SyncFileSystem = new NodeS
54
56
  export async function saveAgentSpec(
55
57
  spec: AgentSpec,
56
58
  configDir: string,
57
- fs: SyncFileSystem = new NodeSyncFileSystem(),
59
+ fs: FileSystem = createNodeFileSystem(),
58
60
  ): Promise<void> {
59
61
  validateAgentId(spec.id);
60
- fs.mkdir(configDir);
61
- fs.writeFile(joinPath(configDir, `${spec.id}.yaml`), serializeAgentSpec(spec));
62
+ await fs.ensureDir(configDir);
63
+ await fs.writeFile(joinPath(configDir, `${spec.id}.yaml`), serializeAgentSpec(spec));
62
64
  }
63
65
 
64
66
  /** Remove the YAML file for agent `id` from `configDir`. Does nothing if the file doesn't exist. */
65
67
  export async function deleteAgentSpec(
66
68
  id: string,
67
69
  configDir: string,
68
- fs: SyncFileSystem = new NodeSyncFileSystem(),
70
+ fs: FileSystem = createNodeFileSystem(),
69
71
  ): Promise<void> {
70
72
  validateAgentId(id);
71
- fs.unlink(joinPath(configDir, `${id}.yaml`));
73
+ await fs.deleteFile(joinPath(configDir, `${id}.yaml`));
72
74
  }
73
75
 
74
- function safeReadDir(configDir: string, fs: SyncFileSystem = new NodeSyncFileSystem()): string[] {
76
+ async function safeReadDir(configDir: string, fs: FileSystem = createNodeFileSystem()): Promise<string[]> {
75
77
  try {
76
- return fs.readDir(configDir);
78
+ return await fs.readDir(configDir);
77
79
  } catch (error) {
78
80
  if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT') {
79
81
  return [];