@gobing-ai/ts-ai-runner 0.4.8 → 0.4.9
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/README.md +56 -42
- package/dist/ai-runner.d.ts +9 -1
- package/dist/ai-runner.d.ts.map +1 -1
- package/dist/ai-runner.js +15 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/message-store.d.ts +30 -0
- package/dist/message-store.d.ts.map +1 -0
- package/dist/message-store.js +7 -0
- package/dist/messages.d.ts +3 -3
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +1 -1
- package/dist/team-agent-process.d.ts +1 -1
- package/dist/team-agent-process.d.ts.map +1 -1
- package/dist/team-agent-process.js +2 -2
- package/dist/team-orchestrator.d.ts +9 -3
- package/dist/team-orchestrator.d.ts.map +1 -1
- package/dist/team-orchestrator.js +5 -1
- package/package.json +5 -5
- package/src/ai-runner.ts +23 -6
- package/src/index.ts +1 -0
- package/src/message-store.ts +31 -0
- package/src/messages.ts +3 -3
- package/src/team-agent-process.ts +2 -2
- package/src/team-orchestrator.ts +14 -4
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ bun add @gobing-ai/ts-ai-runner
|
|
|
13
13
|
`ts-ai-runner` normalizes the command-line surface of common coding agents so application code can work with stable TypeScript APIs instead of hard-coded executable arguments.
|
|
14
14
|
|
|
15
15
|
| Export | Purpose |
|
|
16
|
-
|
|
16
|
+
| -------- | --------- |
|
|
17
17
|
| `AiRunner` | Runs help, version, auth, prompt, and slash commands through a pluggable process executor; can also build a prompt command without executing it. Emits typed events via optional `EventBus<AgentEvents>`. |
|
|
18
18
|
| `AgentDetector` | Probes supported agent CLIs and parses version output |
|
|
19
19
|
| `DoctorRunner` | Combines installation and authentication checks into a usability report |
|
|
@@ -25,7 +25,8 @@ bun add @gobing-ai/ts-ai-runner
|
|
|
25
25
|
| `buildIdentityPreamble()` / `getGitContext()` | Builds team-mode identity, communication context, and git metadata for prompts |
|
|
26
26
|
| `loadAgentSpecs()` / `saveAgentSpec()` / `deleteAgentSpec()` | Persist agent definitions as YAML-compatible config |
|
|
27
27
|
| `validateAgentId()` | Enforces agent ID format rules |
|
|
28
|
-
| `
|
|
28
|
+
| `formatMessage()` | Renders a `DrainedMessage` into the line injected into an agent's stdin pipe |
|
|
29
|
+
| `MessageStore` / `DrainedMessage` | ai-runner-owned orchestration persistence port and minimal message view consumed by `TeamOrchestrator` |
|
|
29
30
|
| `TeamAgentProcess` | Manages a long-running agent subprocess with pipe-mode stdin/stdout |
|
|
30
31
|
| `TeamOrchestrator` | Loads specs, starts/stops agents, routes durable/live messages, and emits lifecycle events |
|
|
31
32
|
| `AgentEvents` / `AiRunnerProcessEvents` | Typed event maps for agent and process-level observability |
|
|
@@ -62,11 +63,13 @@ graph TB
|
|
|
62
63
|
TeamOrchestrator["TeamOrchestrator<br/>loadSpecs / startAgent / stopAgent<br/>sendMessage / getAgentStatus / stopAll"]
|
|
63
64
|
AgentSpec["AgentSpec<br/>(YAML config)<br/>load / save / delete"]
|
|
64
65
|
TeamAgentProcess["TeamAgentProcess<br/>(pipe-mode subprocess)<br/>start / stop / send / subscribe"]
|
|
65
|
-
|
|
66
|
+
MessageStore["<b>MessageStore</b><br/>(ai-runner-owned port)<br/>enqueue / drainPending / markDelivered / markFailed"]
|
|
67
|
+
InboxMessageDao["<i>InboxMessageDao<br/>(@gobing-ai/ts-db/inbox)</i><br/>structural provider"]
|
|
66
68
|
|
|
67
69
|
TeamOrchestrator -->|"loads"| AgentSpec
|
|
68
70
|
TeamOrchestrator -->|"creates + manages"| TeamAgentProcess
|
|
69
|
-
TeamOrchestrator -->|"
|
|
71
|
+
TeamOrchestrator -->|"writes/reads via"| MessageStore
|
|
72
|
+
InboxMessageDao -.->|"satisfies"| MessageStore
|
|
70
73
|
TeamOrchestrator -->|"resolves command via"| AgentShim
|
|
71
74
|
TeamOrchestrator -.->|"emits events"| EventBus
|
|
72
75
|
TeamOrchestrator -.->|"builds preamble"| Identity
|
|
@@ -75,6 +78,7 @@ graph TB
|
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
### One-shot prompt flow
|
|
81
|
+
|
|
78
82
|
```mermaid
|
|
79
83
|
sequenceDiagram
|
|
80
84
|
participant Caller
|
|
@@ -116,7 +120,6 @@ sequenceDiagram
|
|
|
116
120
|
participant Shim as AgentShim
|
|
117
121
|
participant Identity as buildIdentityPreamble
|
|
118
122
|
participant Proc as TeamAgentProcess
|
|
119
|
-
participant Msg as MessageService
|
|
120
123
|
participant DB as InboxMessageDao
|
|
121
124
|
participant Agent as Coding Agent (CLI)
|
|
122
125
|
participant EventBus
|
|
@@ -135,15 +138,13 @@ sequenceDiagram
|
|
|
135
138
|
Orch->>Proc: start()
|
|
136
139
|
Proc->>Agent: spawn pipe-mode subprocess
|
|
137
140
|
Agent-->>Proc: stdout/stderr streams
|
|
138
|
-
Orch->>
|
|
139
|
-
|
|
140
|
-
DB-->>Msg: pending messages
|
|
141
|
-
Msg-->>Orch: messages[]
|
|
141
|
+
Orch->>DB: drainPending("coder")
|
|
142
|
+
DB-->>Orch: pending messages
|
|
142
143
|
alt pending messages exist
|
|
143
144
|
loop for each message
|
|
144
145
|
Orch->>Proc: send(formattedMessage)
|
|
145
146
|
Proc->>Agent: write to stdin
|
|
146
|
-
Orch->>
|
|
147
|
+
Orch->>DB: markDelivered(msg.id)
|
|
147
148
|
end
|
|
148
149
|
end
|
|
149
150
|
Orch->>EventBus: emit("agent.started")
|
|
@@ -151,18 +152,15 @@ sequenceDiagram
|
|
|
151
152
|
|
|
152
153
|
Note over Host,EventBus: Sending a message (durable + live)
|
|
153
154
|
Host->>Orch: sendMessage(null, "coder", "Implement task 0005")
|
|
154
|
-
Orch->>
|
|
155
|
-
|
|
156
|
-
DB-->>Msg: messageId
|
|
157
|
-
Msg-->>Orch: messageId
|
|
155
|
+
Orch->>DB: enqueue(null, "coder", body)
|
|
156
|
+
DB-->>Orch: messageId
|
|
158
157
|
alt agent is running
|
|
159
|
-
Orch->>
|
|
160
|
-
|
|
161
|
-
DB-->>Msg: pending messages
|
|
158
|
+
Orch->>DB: drainPending("coder")
|
|
159
|
+
DB-->>Orch: pending messages
|
|
162
160
|
loop for each message
|
|
163
161
|
Orch->>Proc: send(formattedMessage)
|
|
164
162
|
Proc->>Agent: write to stdin
|
|
165
|
-
Orch->>
|
|
163
|
+
Orch->>DB: markDelivered(msg.id)
|
|
166
164
|
end
|
|
167
165
|
end
|
|
168
166
|
Orch->>EventBus: emit("agent.message.sent")
|
|
@@ -182,9 +180,9 @@ Key design decisions:
|
|
|
182
180
|
- **Shims are pure**: `AgentShim` produces `{ command, args }` without touching the filesystem or launching processes. All side effects live in `AiRunner` and `TeamAgentProcess`.
|
|
183
181
|
- **ProcessExecutor is injectable**: tests inject a stub executor; production uses `NodeProcessExecutor` (or the Bun pipe-process seam for team mode).
|
|
184
182
|
- **Events are opt-in**: `AiRunnerOptions.events` and `TeamOrchestratorOptions.events` accept an `EventBus<AgentEvents>` for structured observability. Without it, the runner is silent.
|
|
185
|
-
- **Team mode is composable**: `AgentSpec`, `TeamAgentProcess`,
|
|
183
|
+
- **Team mode is composable**: `AgentSpec`, `TeamAgentProcess`, and `TeamOrchestrator` are small building blocks. `TeamOrchestrator` depends on the ai-runner-owned `MessageStore` port; `InboxMessageDao` from `@gobing-ai/ts-db/inbox` is one structural provider — no adapter class — and in-memory test doubles implement the port directly. Downstream apps compose them into their own orchestration layer.
|
|
186
184
|
|
|
187
|
-
The package depends on `@gobing-ai/ts-runtime` for process execution
|
|
185
|
+
The package depends on `@gobing-ai/ts-runtime` for process execution and `@gobing-ai/ts-infra` for structured logging and EventBus. `@gobing-ai/ts-db` is a development dependency only: production source under `packages/ai-runner/src` has no direct `@gobing-ai/ts-db` imports for message access, and only the DB-backed integration test resolves the concrete `InboxMessageDao`. The target agent CLIs are not bundled; install them separately in the host environment.
|
|
188
186
|
|
|
189
187
|
## Detect Installed Agents
|
|
190
188
|
|
|
@@ -312,7 +310,7 @@ const runner = new AiRunner({ events: bus });
|
|
|
312
310
|
Available events:
|
|
313
311
|
|
|
314
312
|
| Event | When |
|
|
315
|
-
|
|
313
|
+
| ------- | ------ |
|
|
316
314
|
| `agent.invoke.start` | Immediately before an agent CLI invocation starts |
|
|
317
315
|
| `agent.invoke.exit` | After an agent CLI invocation exits |
|
|
318
316
|
| `agent.started` | When a long-running team agent process starts |
|
|
@@ -424,7 +422,7 @@ interface AgentShim {
|
|
|
424
422
|
Agent-specific behavior:
|
|
425
423
|
|
|
426
424
|
| Agent | CLI | Tier | Auth check | Prompt flags |
|
|
427
|
-
|
|
425
|
+
| ------- | ----- | ------ | ------------ | -------------- |
|
|
428
426
|
| `claude` | `claude` | 1 | `claude auth status` | `-p`, `--continue`, `--model`, `--output-format` |
|
|
429
427
|
| `codex` | `codex` | 1 | `codex login status` | `exec <prompt>`, `exec resume --last`, `-m`, `--json` |
|
|
430
428
|
| `gemini` *(deprecated)* | `gemini` | 1 | env-only | `-p`, `-r latest` (resume), `-m`, `-o` |
|
|
@@ -456,7 +454,7 @@ resolveAgentName('cursor'); // → undefined
|
|
|
456
454
|
**Current deprecation map:**
|
|
457
455
|
|
|
458
456
|
| Id | Status | Canonical | Notes |
|
|
459
|
-
|
|
457
|
+
| ---- | -------- | ----------- | ------- |
|
|
460
458
|
| `antigravity` | alias of `antigravity-cli` | `antigravity-cli` | Old tier-2 id; both use binary `agy`. Resolving warns. |
|
|
461
459
|
| `gemini` | deprecated | `gemini` (self) → replaced by `antigravity-cli` | Gemini CLI sunset 2026-06-18. Shim stays functional. |
|
|
462
460
|
| `omp` | canonical | `omp` | First-class; NOT a pi alias. |
|
|
@@ -465,7 +463,6 @@ resolveAgentName('cursor'); // → undefined
|
|
|
465
463
|
|
|
466
464
|
`getAgentShim()` and `isAgentName()` are alias-aware: passing `'antigravity'` resolves to the `antigravity-cli` shim. `DoctorResult` and `DetectedAgent` surface `deprecated` + `replacedBy` when the resolved canonical id is marked deprecated.
|
|
467
465
|
|
|
468
|
-
|
|
469
466
|
## Team Mode Primitives
|
|
470
467
|
|
|
471
468
|
The team-mode APIs are intentionally small building blocks. They do not implement an HTTP API, dashboard, or product workflow; downstream apps compose them into their own orchestration layer.
|
|
@@ -498,24 +495,38 @@ const specs = loadAgentSpecs('./agents');
|
|
|
498
495
|
|
|
499
496
|
### Durable messages
|
|
500
497
|
|
|
501
|
-
`
|
|
498
|
+
`TeamOrchestrator` depends on the ai-runner-owned `MessageStore` port — a minimal interface with
|
|
499
|
+
`enqueue`, `drainPending`, `markDelivered`, and `markFailed` — plus the `DrainedMessage` view
|
|
500
|
+
containing only the fields the orchestrator consumes (`id`, `fromId`, `body`). The port is the
|
|
501
|
+
orchestration boundary; production source under `packages/ai-runner/src` has no direct
|
|
502
|
+
`@gobing-ai/ts-db` import for message access.
|
|
503
|
+
|
|
504
|
+
`InboxMessageDao` from `@gobing-ai/ts-db/inbox` is one structural provider: it satisfies
|
|
505
|
+
`MessageStore` without an adapter class, so no `ts-db` runtime changes are required. In-memory
|
|
506
|
+
test doubles implement the port directly. Consumers compose `InboxMessageDao` +
|
|
507
|
+
`EventBus<InboxMessageEvents>` and pass the DAO to `TeamOrchestrator`:
|
|
502
508
|
|
|
503
509
|
```ts
|
|
504
|
-
import {
|
|
505
|
-
import {
|
|
510
|
+
import { type BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
|
|
511
|
+
import { InboxMessageDao, type InboxMessageEvents } from '@gobing-ai/ts-db/inbox';
|
|
512
|
+
import { formatMessage, type MessageStore } from '@gobing-ai/ts-ai-runner';
|
|
506
513
|
|
|
507
|
-
const
|
|
514
|
+
const lifecycleBus = new EventBus<BusLifecycleEvents>();
|
|
515
|
+
const events = new EventBus<InboxMessageEvents>({ lifecycleBus });
|
|
516
|
+
const inbox: MessageStore = new InboxMessageDao(adapter, { events });
|
|
508
517
|
|
|
509
|
-
const id = await
|
|
510
|
-
const pending = await
|
|
518
|
+
const id = await inbox.enqueue(null, 'coder', 'Review the runtime process seam');
|
|
519
|
+
const pending = await inbox.drainPending('coder');
|
|
511
520
|
|
|
512
521
|
for (const msg of pending) {
|
|
513
|
-
console.log(
|
|
514
|
-
await
|
|
522
|
+
console.log(formatMessage(msg));
|
|
523
|
+
await inbox.markDelivered(msg.id);
|
|
515
524
|
}
|
|
516
525
|
```
|
|
517
526
|
|
|
518
|
-
|
|
527
|
+
Message lifecycle events are metadata-only and do not include the durable message body. A
|
|
528
|
+
`message.failed` event does include the caller-provided error string; pre-redact it when the
|
|
529
|
+
lifecycle bus is attached to persistent System Events observers.
|
|
519
530
|
|
|
520
531
|
### Persistent agent processes
|
|
521
532
|
|
|
@@ -551,24 +562,27 @@ unsubscribe();
|
|
|
551
562
|
|
|
552
563
|
### Team orchestrator
|
|
553
564
|
|
|
554
|
-
`TeamOrchestrator` connects specs, shims, processes, and
|
|
565
|
+
`TeamOrchestrator` connects specs, shims, processes, and the `MessageStore` port. On start it loads an agent spec, builds the agent command through the matching shim, starts the process, drains pending inbox messages, and injects them live. `sendMessage()` always persists first, then injects immediately when the target agent is running.
|
|
555
566
|
|
|
556
567
|
```ts
|
|
557
|
-
import {
|
|
558
|
-
import {
|
|
568
|
+
import { type BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
|
|
569
|
+
import { InboxMessageDao, type InboxMessageEvents } from '@gobing-ai/ts-db/inbox';
|
|
570
|
+
import { TeamOrchestrator, type MessageStore } from '@gobing-ai/ts-ai-runner';
|
|
559
571
|
|
|
560
|
-
const
|
|
561
|
-
const
|
|
572
|
+
const lifecycleBus = new EventBus<BusLifecycleEvents>();
|
|
573
|
+
const events = new EventBus<InboxMessageEvents>({ lifecycleBus });
|
|
574
|
+
const inbox: MessageStore = new InboxMessageDao(adapter, { events });
|
|
575
|
+
const team = new TeamOrchestrator('./agents', inbox, { lifecycleBus });
|
|
562
576
|
|
|
563
577
|
await team.startAgent('coder');
|
|
564
578
|
await team.sendMessage(null, 'coder', 'Please implement task 0005');
|
|
565
579
|
|
|
566
|
-
console.log(team.getAgentStatus('coder')); // running
|
|
580
|
+
console.log(await team.getAgentStatus('coder')); // running
|
|
567
581
|
|
|
568
582
|
await team.stopAll();
|
|
569
583
|
```
|
|
570
584
|
|
|
571
|
-
The orchestrator also provides `restartAgent(id)`, `getRunningAgents()`, `getPeerSpecs(workspace, excludeId?)`, and `on(event, listener)` for event subscription.
|
|
585
|
+
Any object implementing `MessageStore` can be supplied to `TeamOrchestrator`; `InboxMessageDao` is one structural provider, and in-memory test doubles implement the port directly without an adapter class. The orchestrator also provides `restartAgent(id)`, `getRunningAgents()`, `getPeerSpecs(workspace, excludeId?)`, and `on(event, listener)` for event subscription.
|
|
572
586
|
|
|
573
587
|
## Adding a New Coding Agent
|
|
574
588
|
|
|
@@ -635,7 +649,7 @@ If the agent supports an auth-status command, `getAuthCommand()` already returns
|
|
|
635
649
|
### Summary checklist
|
|
636
650
|
|
|
637
651
|
| Step | File | What to change |
|
|
638
|
-
|
|
652
|
+
| ------ | ------ | ---------------- |
|
|
639
653
|
| Shim | `src/agents/shims.ts` | Add `AgentShim` impl, update `AgentName`, `AGENT_SHIMS`, `DISPLAY_ORDER` |
|
|
640
654
|
| Tier | `src/agents/shims.ts` | Add to `TIER1_PRIORITY` or `TIER2_AGENTS` |
|
|
641
655
|
| Slash | `src/slash-command.ts` | Add case if dialect differs from default |
|
|
@@ -650,5 +664,5 @@ After these changes, the new agent is automatically available to `AiRunner`, `Ag
|
|
|
650
664
|
- It does not install agent CLIs or manage credentials.
|
|
651
665
|
- It does not parse agent responses beyond process result capture.
|
|
652
666
|
- It keeps subprocess launching behind `ProcessExecutor` / `PipeProcess`, so tests can stay deterministic.
|
|
653
|
-
- Team-mode persistence is
|
|
667
|
+
- Team-mode persistence is consumed through the ai-runner-owned `MessageStore` port; `InboxMessageDao` from `@gobing-ai/ts-db/inbox` is one structural provider. Host apps own migrations and adapter lifecycle, and `@gobing-ai/ts-db` is a development dependency of this package (only the DB-backed integration test resolves the concrete DAO).
|
|
654
668
|
- Platform APIs (`node:fs`, `node:path`, `Bun.spawn`, etc.) are confined to `@gobing-ai/ts-runtime` per ADR-011. This package accesses them through the runtime's `FileSystem`, `ProcessExecutor`, and path utilities.
|
package/dist/ai-runner.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EventBus, type Logger } from '@gobing-ai/ts-infra';
|
|
1
|
+
import { type BusLifecycleEvents, EventBus, type Logger } from '@gobing-ai/ts-infra';
|
|
2
2
|
import { type ProcessExecutor, type TracerPort } from '@gobing-ai/ts-runtime';
|
|
3
3
|
import { type AgentName, type PromptOptions, type ShimCommand } from './agents/shims';
|
|
4
4
|
import type { AgentEvents, AiRunnerProcessEvents } from './events';
|
|
@@ -38,6 +38,12 @@ export interface AiRunnerOptions {
|
|
|
38
38
|
processEvents?: EventBus<AiRunnerProcessEvents>;
|
|
39
39
|
/** Event bus receiving agent-level invocation observability. */
|
|
40
40
|
events?: EventBus<AgentEvents>;
|
|
41
|
+
/**
|
|
42
|
+
* Optional lifecycle bus to bridge `agent.*` and `process.*` events into
|
|
43
|
+
* the application System Events stream (R4). When `events` / `processEvents`
|
|
44
|
+
* are omitted the runner constructs internal buses parented to this bus.
|
|
45
|
+
*/
|
|
46
|
+
lifecycleBus?: EventBus<BusLifecycleEvents>;
|
|
41
47
|
/** Tracer adapter for the default executor. Defaults to `ts-infra` traceAsync. */
|
|
42
48
|
tracer?: TracerPort;
|
|
43
49
|
}
|
|
@@ -48,6 +54,8 @@ export declare class AiRunner {
|
|
|
48
54
|
private readonly defaultTimeout;
|
|
49
55
|
private readonly logger;
|
|
50
56
|
private readonly events;
|
|
57
|
+
/** Internal process-level observability bus, parented to `lifecycleBus` when auto-constructed. Exposed for introspection/testing. */
|
|
58
|
+
readonly processEvents: EventBus<AiRunnerProcessEvents> | undefined;
|
|
51
59
|
constructor(options?: AiRunnerOptions);
|
|
52
60
|
/** Run an agent help command. */
|
|
53
61
|
runHelpCommand(agent: AgentName, options?: AgentRunOptions): Promise<AgentRunResult>;
|
package/dist/ai-runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"ai-runner.d.ts","sourceRoot":"","sources":["../src/ai-runner.ts"],"names":[],"mappings":"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"}
|
package/dist/ai-runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getLogger, traceAsync } from '@gobing-ai/ts-infra';
|
|
2
|
-
import { getProcessCwd,
|
|
1
|
+
import { EventBus, getLogger, traceAsync } from '@gobing-ai/ts-infra';
|
|
2
|
+
import { getProcessCwd, nodeBunFactory, } from '@gobing-ai/ts-runtime';
|
|
3
3
|
import { getAgentShim } from './agents/shims.js';
|
|
4
4
|
import { buildIdentityPreamble } from './identity.js';
|
|
5
5
|
import { translateSlashCommand } from './slash-command.js';
|
|
@@ -10,15 +10,23 @@ export class AiRunner {
|
|
|
10
10
|
defaultTimeout;
|
|
11
11
|
logger;
|
|
12
12
|
events;
|
|
13
|
+
/** Internal process-level observability bus, parented to `lifecycleBus` when auto-constructed. Exposed for introspection/testing. */
|
|
14
|
+
processEvents;
|
|
13
15
|
constructor(options = {}) {
|
|
16
|
+
const processEvents = options.processEvents ??
|
|
17
|
+
(options.lifecycleBus
|
|
18
|
+
? new EventBus({ lifecycleBus: options.lifecycleBus })
|
|
19
|
+
: undefined);
|
|
20
|
+
const events = options.events ??
|
|
21
|
+
(options.lifecycleBus ? new EventBus({ lifecycleBus: options.lifecycleBus }) : undefined);
|
|
14
22
|
this.processExecutor =
|
|
15
23
|
options.processExecutor ??
|
|
16
|
-
|
|
17
|
-
...(
|
|
24
|
+
nodeBunFactory.createProcessExecutor({
|
|
25
|
+
...(processEvents !== undefined
|
|
18
26
|
? {
|
|
19
27
|
events: {
|
|
20
28
|
emit: (event, detail) => {
|
|
21
|
-
void
|
|
29
|
+
void processEvents?.emit(event, detail);
|
|
22
30
|
},
|
|
23
31
|
},
|
|
24
32
|
}
|
|
@@ -30,7 +38,8 @@ export class AiRunner {
|
|
|
30
38
|
this.defaultCwd = options.defaultCwd;
|
|
31
39
|
this.defaultTimeout = options.defaultTimeout;
|
|
32
40
|
this.logger = options.logger ?? getLogger('ai-runner');
|
|
33
|
-
this.events =
|
|
41
|
+
this.events = events;
|
|
42
|
+
this.processEvents = processEvents;
|
|
34
43
|
}
|
|
35
44
|
/** Run an agent help command. */
|
|
36
45
|
runHelpCommand(agent, options = {}) {
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -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,qBAAqB,CAAC;AACpC,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,sBAAsB,CAAC;AACrC,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,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from './ai-runner.js';
|
|
|
6
6
|
export * from './doctor-runner.js';
|
|
7
7
|
export * from './events.js';
|
|
8
8
|
export * from './identity.js';
|
|
9
|
+
export * from './message-store.js';
|
|
9
10
|
export * from './messages.js';
|
|
10
11
|
export * from './model-health-probe.js';
|
|
11
12
|
export * from './slash-command.js';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-runner-owned orchestration persistence boundary (ADR-023 follow-up A4).
|
|
3
|
+
*
|
|
4
|
+
* `TeamOrchestrator` depends only on this port. `InboxMessageDao` from
|
|
5
|
+
* `@gobing-ai/ts-db/inbox` satisfies it structurally — no adapter class — and
|
|
6
|
+
* in-memory test doubles implement it directly.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Minimal read-only view of a drained message containing only the fields
|
|
10
|
+
* `ts-ai-runner` consumes. It deliberately does not mirror the full
|
|
11
|
+
* `InboxMessage` persistence model.
|
|
12
|
+
*/
|
|
13
|
+
export interface DrainedMessage {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly fromId: string | null;
|
|
16
|
+
readonly body: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Message store operations consumed by `TeamOrchestrator`. Mirrors the subset
|
|
20
|
+
* of `InboxMessageDao` operations the orchestrator uses: enqueue a message,
|
|
21
|
+
* drain pending messages for a recipient, mark a message delivered, and mark
|
|
22
|
+
* a message failed.
|
|
23
|
+
*/
|
|
24
|
+
export interface MessageStore {
|
|
25
|
+
enqueue(fromId: string | null, toId: string, body: string, inReplyTo?: string): Promise<string>;
|
|
26
|
+
drainPending(toId: string): Promise<DrainedMessage[]>;
|
|
27
|
+
markDelivered(msgId: string): Promise<void>;
|
|
28
|
+
markFailed(msgId: string, error: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=message-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-store.d.ts","sourceRoot":"","sources":["../src/message-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IACzB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACtD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-runner-owned orchestration persistence boundary (ADR-023 follow-up A4).
|
|
3
|
+
*
|
|
4
|
+
* `TeamOrchestrator` depends only on this port. `InboxMessageDao` from
|
|
5
|
+
* `@gobing-ai/ts-db/inbox` satisfies it structurally — no adapter class — and
|
|
6
|
+
* in-memory test doubles implement it directly.
|
|
7
|
+
*/
|
package/dist/messages.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
/** Renders
|
|
3
|
-
export declare function formatMessage(msg:
|
|
1
|
+
import type { DrainedMessage } from './message-store';
|
|
2
|
+
/** Renders a drained message into the line injected into an agent's stdin. */
|
|
3
|
+
export declare function formatMessage(msg: DrainedMessage): string;
|
|
4
4
|
//# sourceMappingURL=messages.d.ts.map
|
package/dist/messages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAEzD"}
|
package/dist/messages.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Renders
|
|
1
|
+
/** Renders a drained message into the line injected into an agent's stdin. */
|
|
2
2
|
export function formatMessage(msg) {
|
|
3
3
|
return `[task from=${msg.fromId ?? 'operator'} id=${msg.id}] ${msg.body}`;
|
|
4
4
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { type Logger } from '@gobing-ai/ts-infra';
|
|
3
|
-
import { ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
3
|
+
import { type ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
4
4
|
import type { AgentSpec } from './agent-spec';
|
|
5
5
|
/** Options for spawning a team agent subprocess. */
|
|
6
6
|
export interface AgentProcessOptions {
|
|
@@ -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,EAAa,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,
|
|
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,EAAoC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC/F,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,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { getLogger } from '@gobing-ai/ts-infra';
|
|
3
|
-
import {
|
|
3
|
+
import { nodeBunFactory } from '@gobing-ai/ts-runtime';
|
|
4
4
|
/**
|
|
5
5
|
* Manages the lifecycle of a single agent subprocess — start, stop, message send, and stdout/stderr subscription.
|
|
6
6
|
* The identity preamble is built by `TeamOrchestrator` and baked into `command` before the process is constructed.
|
|
@@ -21,7 +21,7 @@ export class TeamAgentProcess {
|
|
|
21
21
|
this.command = options.command;
|
|
22
22
|
this.env = options.env;
|
|
23
23
|
this.cwd = options.cwd ?? options.spec.workspace;
|
|
24
|
-
this.processExecutor = options.processExecutor ??
|
|
24
|
+
this.processExecutor = options.processExecutor ?? nodeBunFactory.createProcessExecutor();
|
|
25
25
|
this.logger = options.logger ?? getLogger('team-agent');
|
|
26
26
|
}
|
|
27
27
|
async start() {
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { EventBus } from '@gobing-ai/ts-infra';
|
|
1
|
+
import { type BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
|
|
3
2
|
import type { AgentSpec } from './agent-spec';
|
|
4
3
|
import type { AgentEvents } from './events';
|
|
4
|
+
import type { MessageStore } from './message-store';
|
|
5
5
|
import { TeamAgentProcess } from './team-agent-process';
|
|
6
6
|
type AgentProcessFactory = (options: ConstructorParameters<typeof TeamAgentProcess>[0]) => TeamAgentProcess;
|
|
7
7
|
/** Configuration options for `TeamOrchestrator`. */
|
|
8
8
|
export interface TeamOrchestratorOptions {
|
|
9
9
|
processFactory?: AgentProcessFactory;
|
|
10
10
|
events?: EventBus<AgentEvents>;
|
|
11
|
+
/**
|
|
12
|
+
* Optional lifecycle bus to bridge `agent.*` events into the application
|
|
13
|
+
* System Events stream (R4). When `events` is omitted the orchestrator
|
|
14
|
+
* constructs an internal `EventBus<AgentEvents>` parented to this bus.
|
|
15
|
+
*/
|
|
16
|
+
lifecycleBus?: EventBus<BusLifecycleEvents>;
|
|
11
17
|
}
|
|
12
18
|
/**
|
|
13
19
|
* Orchestrates a team of AI agents — loads specs, starts/stops agent processes, routes messages between them,
|
|
@@ -20,7 +26,7 @@ export declare class TeamOrchestrator {
|
|
|
20
26
|
private readonly running;
|
|
21
27
|
private readonly processFactory;
|
|
22
28
|
private readonly events;
|
|
23
|
-
constructor(configDir: string, inbox:
|
|
29
|
+
constructor(configDir: string, inbox: MessageStore, options?: TeamOrchestratorOptions);
|
|
24
30
|
loadSpecs(): Promise<AgentSpec[]>;
|
|
25
31
|
getSpec(id: string): Promise<AgentSpec | undefined>;
|
|
26
32
|
startAgent(id: string): Promise<TeamAgentProcess>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-orchestrator.d.ts","sourceRoot":"","sources":["../src/team-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"team-orchestrator.d.ts","sourceRoot":"","sources":["../src/team-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,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;IAC/B;;;;OAIG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;CAC/C;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,YAAY,EACpC,OAAO,GAAE,uBAA4B;IAUnC,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"}
|
|
@@ -19,7 +19,11 @@ export class TeamOrchestrator {
|
|
|
19
19
|
this.configDir = configDir;
|
|
20
20
|
this.inbox = inbox;
|
|
21
21
|
this.processFactory = options.processFactory ?? ((processOptions) => new TeamAgentProcess(processOptions));
|
|
22
|
-
this.events =
|
|
22
|
+
this.events =
|
|
23
|
+
options.events ??
|
|
24
|
+
(options.lifecycleBus
|
|
25
|
+
? new EventBus({ lifecycleBus: options.lifecycleBus })
|
|
26
|
+
: new EventBus());
|
|
23
27
|
}
|
|
24
28
|
async loadSpecs() {
|
|
25
29
|
this.specs = await loadAgentSpecs(this.configDir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/ts-ai-runner",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
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-
|
|
51
|
-
"@gobing-ai/ts-
|
|
52
|
-
"@gobing-ai/ts-runtime": "^0.4.8"
|
|
50
|
+
"@gobing-ai/ts-infra": "^0.4.9",
|
|
51
|
+
"@gobing-ai/ts-runtime": "^0.4.9"
|
|
53
52
|
},
|
|
54
53
|
"devDependencies": {
|
|
55
|
-
"@types/bun": "1.3.14"
|
|
54
|
+
"@types/bun": "1.3.14",
|
|
55
|
+
"@gobing-ai/ts-db": "^0.4.9"
|
|
56
56
|
},
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
package/src/ai-runner.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type EventBus, getLogger, type Logger, traceAsync } from '@gobing-ai/ts-infra';
|
|
1
|
+
import { type BusLifecycleEvents, EventBus, getLogger, type Logger, traceAsync } from '@gobing-ai/ts-infra';
|
|
2
2
|
import {
|
|
3
3
|
getProcessCwd,
|
|
4
|
-
|
|
4
|
+
nodeBunFactory,
|
|
5
5
|
type ProcessExecutor,
|
|
6
6
|
type ProcessResult,
|
|
7
7
|
type TracerPort,
|
|
@@ -49,6 +49,12 @@ export interface AiRunnerOptions {
|
|
|
49
49
|
processEvents?: EventBus<AiRunnerProcessEvents>;
|
|
50
50
|
/** Event bus receiving agent-level invocation observability. */
|
|
51
51
|
events?: EventBus<AgentEvents>;
|
|
52
|
+
/**
|
|
53
|
+
* Optional lifecycle bus to bridge `agent.*` and `process.*` events into
|
|
54
|
+
* the application System Events stream (R4). When `events` / `processEvents`
|
|
55
|
+
* are omitted the runner constructs internal buses parented to this bus.
|
|
56
|
+
*/
|
|
57
|
+
lifecycleBus?: EventBus<BusLifecycleEvents>;
|
|
52
58
|
/** Tracer adapter for the default executor. Defaults to `ts-infra` traceAsync. */
|
|
53
59
|
tracer?: TracerPort;
|
|
54
60
|
}
|
|
@@ -60,16 +66,26 @@ export class AiRunner {
|
|
|
60
66
|
private readonly defaultTimeout: number | undefined;
|
|
61
67
|
private readonly logger: Logger;
|
|
62
68
|
private readonly events: EventBus<AgentEvents> | undefined;
|
|
69
|
+
/** Internal process-level observability bus, parented to `lifecycleBus` when auto-constructed. Exposed for introspection/testing. */
|
|
70
|
+
readonly processEvents: EventBus<AiRunnerProcessEvents> | undefined;
|
|
63
71
|
|
|
64
72
|
constructor(options: AiRunnerOptions = {}) {
|
|
73
|
+
const processEvents =
|
|
74
|
+
options.processEvents ??
|
|
75
|
+
(options.lifecycleBus
|
|
76
|
+
? new EventBus<AiRunnerProcessEvents>({ lifecycleBus: options.lifecycleBus })
|
|
77
|
+
: undefined);
|
|
78
|
+
const events =
|
|
79
|
+
options.events ??
|
|
80
|
+
(options.lifecycleBus ? new EventBus<AgentEvents>({ lifecycleBus: options.lifecycleBus }) : undefined);
|
|
65
81
|
this.processExecutor =
|
|
66
82
|
options.processExecutor ??
|
|
67
|
-
|
|
68
|
-
...(
|
|
83
|
+
nodeBunFactory.createProcessExecutor({
|
|
84
|
+
...(processEvents !== undefined
|
|
69
85
|
? {
|
|
70
86
|
events: {
|
|
71
87
|
emit: (event, detail) => {
|
|
72
|
-
void
|
|
88
|
+
void processEvents?.emit(event, detail);
|
|
73
89
|
},
|
|
74
90
|
},
|
|
75
91
|
}
|
|
@@ -81,7 +97,8 @@ export class AiRunner {
|
|
|
81
97
|
this.defaultCwd = options.defaultCwd;
|
|
82
98
|
this.defaultTimeout = options.defaultTimeout;
|
|
83
99
|
this.logger = options.logger ?? getLogger('ai-runner');
|
|
84
|
-
this.events =
|
|
100
|
+
this.events = events;
|
|
101
|
+
this.processEvents = processEvents;
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
/** Run an agent help command. */
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-runner-owned orchestration persistence boundary (ADR-023 follow-up A4).
|
|
3
|
+
*
|
|
4
|
+
* `TeamOrchestrator` depends only on this port. `InboxMessageDao` from
|
|
5
|
+
* `@gobing-ai/ts-db/inbox` satisfies it structurally — no adapter class — and
|
|
6
|
+
* in-memory test doubles implement it directly.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Minimal read-only view of a drained message containing only the fields
|
|
11
|
+
* `ts-ai-runner` consumes. It deliberately does not mirror the full
|
|
12
|
+
* `InboxMessage` persistence model.
|
|
13
|
+
*/
|
|
14
|
+
export interface DrainedMessage {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly fromId: string | null;
|
|
17
|
+
readonly body: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Message store operations consumed by `TeamOrchestrator`. Mirrors the subset
|
|
22
|
+
* of `InboxMessageDao` operations the orchestrator uses: enqueue a message,
|
|
23
|
+
* drain pending messages for a recipient, mark a message delivered, and mark
|
|
24
|
+
* a message failed.
|
|
25
|
+
*/
|
|
26
|
+
export interface MessageStore {
|
|
27
|
+
enqueue(fromId: string | null, toId: string, body: string, inReplyTo?: string): Promise<string>;
|
|
28
|
+
drainPending(toId: string): Promise<DrainedMessage[]>;
|
|
29
|
+
markDelivered(msgId: string): Promise<void>;
|
|
30
|
+
markFailed(msgId: string, error: string): Promise<void>;
|
|
31
|
+
}
|
package/src/messages.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DrainedMessage } from './message-store';
|
|
2
2
|
|
|
3
|
-
/** Renders
|
|
4
|
-
export function formatMessage(msg:
|
|
3
|
+
/** Renders a drained message into the line injected into an agent's stdin. */
|
|
4
|
+
export function formatMessage(msg: DrainedMessage): string {
|
|
5
5
|
return `[task from=${msg.fromId ?? 'operator'} id=${msg.id}] ${msg.body}`;
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { getLogger, type Logger } from '@gobing-ai/ts-infra';
|
|
3
|
-
import { type PipeProcess, ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
3
|
+
import { nodeBunFactory, type PipeProcess, type ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
4
4
|
import type { AgentSpec } from './agent-spec';
|
|
5
5
|
|
|
6
6
|
/** Options for spawning a team agent subprocess. */
|
|
@@ -36,7 +36,7 @@ export class TeamAgentProcess {
|
|
|
36
36
|
this.command = options.command;
|
|
37
37
|
this.env = options.env;
|
|
38
38
|
this.cwd = options.cwd ?? options.spec.workspace;
|
|
39
|
-
this.processExecutor = options.processExecutor ??
|
|
39
|
+
this.processExecutor = options.processExecutor ?? nodeBunFactory.createProcessExecutor();
|
|
40
40
|
this.logger = options.logger ?? getLogger('team-agent');
|
|
41
41
|
}
|
|
42
42
|
|
package/src/team-orchestrator.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { EventBus } from '@gobing-ai/ts-infra';
|
|
1
|
+
import { type BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
|
|
3
2
|
import type { AgentSpec } from './agent-spec';
|
|
4
3
|
import { loadAgentSpecs } from './agent-spec';
|
|
5
4
|
import { type AgentName, resolveAgentName } from './agents/shims';
|
|
6
5
|
import { buildAgentCommand } from './ai-runner';
|
|
7
6
|
import type { AgentEvents } from './events';
|
|
7
|
+
import type { MessageStore } from './message-store';
|
|
8
8
|
import { formatMessage } from './messages';
|
|
9
9
|
import { TeamAgentProcess } from './team-agent-process';
|
|
10
10
|
|
|
@@ -14,6 +14,12 @@ type AgentProcessFactory = (options: ConstructorParameters<typeof TeamAgentProce
|
|
|
14
14
|
export interface TeamOrchestratorOptions {
|
|
15
15
|
processFactory?: AgentProcessFactory;
|
|
16
16
|
events?: EventBus<AgentEvents>;
|
|
17
|
+
/**
|
|
18
|
+
* Optional lifecycle bus to bridge `agent.*` events into the application
|
|
19
|
+
* System Events stream (R4). When `events` is omitted the orchestrator
|
|
20
|
+
* constructs an internal `EventBus<AgentEvents>` parented to this bus.
|
|
21
|
+
*/
|
|
22
|
+
lifecycleBus?: EventBus<BusLifecycleEvents>;
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
/**
|
|
@@ -28,11 +34,15 @@ export class TeamOrchestrator {
|
|
|
28
34
|
|
|
29
35
|
constructor(
|
|
30
36
|
private readonly configDir: string,
|
|
31
|
-
private readonly inbox:
|
|
37
|
+
private readonly inbox: MessageStore,
|
|
32
38
|
options: TeamOrchestratorOptions = {},
|
|
33
39
|
) {
|
|
34
40
|
this.processFactory = options.processFactory ?? ((processOptions) => new TeamAgentProcess(processOptions));
|
|
35
|
-
this.events =
|
|
41
|
+
this.events =
|
|
42
|
+
options.events ??
|
|
43
|
+
(options.lifecycleBus
|
|
44
|
+
? new EventBus<AgentEvents>({ lifecycleBus: options.lifecycleBus })
|
|
45
|
+
: new EventBus<AgentEvents>());
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
async loadSpecs(): Promise<AgentSpec[]> {
|