@telora/daemon 0.16.8 → 0.16.16
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/build-info.json +4 -3
- package/dist/backends/agent-backend.d.ts +63 -0
- package/dist/backends/agent-backend.d.ts.map +1 -0
- package/dist/backends/agent-backend.js +19 -0
- package/dist/backends/agent-backend.js.map +1 -0
- package/dist/backends/availability.d.ts +28 -0
- package/dist/backends/availability.d.ts.map +1 -0
- package/dist/backends/availability.js +49 -0
- package/dist/backends/availability.js.map +1 -0
- package/dist/backends/claude/claude-backend.d.ts +12 -0
- package/dist/backends/claude/claude-backend.d.ts.map +1 -0
- package/dist/backends/claude/claude-backend.js +86 -0
- package/dist/backends/claude/claude-backend.js.map +1 -0
- package/dist/backends/codex/codex-backend.d.ts +23 -0
- package/dist/backends/codex/codex-backend.d.ts.map +1 -0
- package/dist/backends/codex/codex-backend.js +116 -0
- package/dist/backends/codex/codex-backend.js.map +1 -0
- package/dist/backends/index.d.ts +13 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +20 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/registry.d.ts +29 -0
- package/dist/backends/registry.d.ts.map +1 -0
- package/dist/backends/registry.js +45 -0
- package/dist/backends/registry.js.map +1 -0
- package/dist/backends/resolve.d.ts +44 -0
- package/dist/backends/resolve.d.ts.map +1 -0
- package/dist/backends/resolve.js +57 -0
- package/dist/backends/resolve.js.map +1 -0
- package/dist/completion-detector.d.ts +18 -16
- package/dist/completion-detector.d.ts.map +1 -1
- package/dist/completion-detector.js +36 -36
- package/dist/completion-detector.js.map +1 -1
- package/dist/focus-completion-event.d.ts +20 -7
- package/dist/focus-completion-event.d.ts.map +1 -1
- package/dist/focus-completion-event.js +29 -10
- package/dist/focus-completion-event.js.map +1 -1
- package/dist/focus-executor.d.ts +8 -0
- package/dist/focus-executor.d.ts.map +1 -1
- package/dist/focus-executor.js +71 -59
- package/dist/focus-executor.js.map +1 -1
- package/dist/focus-spawn-helpers.d.ts +2 -0
- package/dist/focus-spawn-helpers.d.ts.map +1 -1
- package/dist/focus-spawn-helpers.js +39 -37
- package/dist/focus-spawn-helpers.js.map +1 -1
- package/dist/git-merge.d.ts.map +1 -1
- package/dist/git-merge.js +42 -16
- package/dist/git-merge.js.map +1 -1
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +7 -0
- package/dist/heartbeat.js.map +1 -1
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +3 -1
- package/dist/listener.js.map +1 -1
- package/dist/output-monitor.d.ts +11 -6
- package/dist/output-monitor.d.ts.map +1 -1
- package/dist/output-monitor.js +53 -43
- package/dist/output-monitor.js.map +1 -1
- package/dist/queries/deliveries.d.ts +8 -0
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +4 -0
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/schemas.d.ts +8 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +8 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/spawn-environment.d.ts +11 -0
- package/dist/spawn-environment.d.ts.map +1 -1
- package/dist/spawn-environment.js +27 -0
- package/dist/spawn-environment.js.map +1 -1
- package/dist/spawner-stream-handlers.d.ts +3 -2
- package/dist/spawner-stream-handlers.d.ts.map +1 -1
- package/dist/spawner-stream-handlers.js +37 -34
- package/dist/spawner-stream-handlers.js.map +1 -1
- package/dist/spawner.d.ts +8 -17
- package/dist/spawner.d.ts.map +1 -1
- package/dist/spawner.js +8 -319
- package/dist/spawner.js.map +1 -1
- package/dist/team-prompt-base.d.ts +8 -2
- package/dist/team-prompt-base.d.ts.map +1 -1
- package/dist/team-prompt-base.js +35 -9
- package/dist/team-prompt-base.js.map +1 -1
- package/dist/team-spawner.d.ts +15 -2
- package/dist/team-spawner.d.ts.map +1 -1
- package/dist/team-spawner.js +36 -31
- package/dist/team-spawner.js.map +1 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/session.d.ts +4 -4
- package/dist/types/session.d.ts.map +1 -1
- package/package.json +3 -3
package/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commitSha": "
|
|
3
|
-
"builtAt": "2026-05-
|
|
2
|
+
"commitSha": "f1330817",
|
|
3
|
+
"builtAt": "2026-05-26T14:53:55.053Z",
|
|
4
4
|
"expectedMigrations": [
|
|
5
5
|
"20250829113330_create_org_nodes_table.sql",
|
|
6
6
|
"20250829113402_fix_function_security_search_path.sql",
|
|
@@ -591,6 +591,7 @@
|
|
|
591
591
|
"20260525092825_focus_lineage_session_ids.sql",
|
|
592
592
|
"20260525094853_focus_directive_lineage_continuity.sql",
|
|
593
593
|
"20260525095445_review_lineage_resume.sql",
|
|
594
|
-
"20260525124226_coding_stage_delivery_index.sql"
|
|
594
|
+
"20260525124226_coding_stage_delivery_index.sql",
|
|
595
|
+
"20260526025901_engine_selection_columns.sql"
|
|
595
596
|
]
|
|
596
597
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentBackend interface -- the engine-agnostic seam for spawning coding agents.
|
|
3
|
+
*
|
|
4
|
+
* The daemon's focus-execution path used to be welded to Claude Code (the
|
|
5
|
+
* `claude` binary, stream-json I/O, `--resume` session ids). An `AgentBackend`
|
|
6
|
+
* abstracts the four engine-specific concerns behind one interface so the daemon
|
|
7
|
+
* can run a focus on any registered engine:
|
|
8
|
+
*
|
|
9
|
+
* 1. spawn-arg / resume-arg construction (CLI invocation per engine)
|
|
10
|
+
* 2. input encoding (how a prompt/message is written to the process)
|
|
11
|
+
* 3. an event-parser factory (engine stream -> neutral AgentEvent stream)
|
|
12
|
+
* 4. an availability probe (is this engine installed + authenticated here?)
|
|
13
|
+
*
|
|
14
|
+
* Session-id capture rides on the parser (`AgentEventParser.sessionId`, set from
|
|
15
|
+
* the engine's neutral `session_started` event). Backends are looked up by id
|
|
16
|
+
* from the registry (./registry.ts); `claude` is the only one registered in D1.
|
|
17
|
+
*/
|
|
18
|
+
import type { AgentEventParser } from '@telora/daemon-core';
|
|
19
|
+
import type { DaemonConfig } from '../types.js';
|
|
20
|
+
/** Inputs that vary per fresh spawn. */
|
|
21
|
+
export interface AgentSpawnContext {
|
|
22
|
+
/** Daemon config -- backends read what they need (paths, tracker id, URL). */
|
|
23
|
+
config: DaemonConfig;
|
|
24
|
+
/** Worktree the agent runs in. Claude uses the spawn cwd; Codex needs `--cd`. */
|
|
25
|
+
worktreePath: string;
|
|
26
|
+
/** Model override (e.g. from pipeline config or a stage directive). */
|
|
27
|
+
model?: string | null;
|
|
28
|
+
}
|
|
29
|
+
/** Inputs for resuming a prior session. */
|
|
30
|
+
export interface AgentResumeContext extends AgentSpawnContext {
|
|
31
|
+
/** The engine session id to resume (captured from a prior session_started). */
|
|
32
|
+
resumeSessionId: string;
|
|
33
|
+
}
|
|
34
|
+
/** Result of probing whether a backend is usable on the current daemon host. */
|
|
35
|
+
export interface BackendAvailability {
|
|
36
|
+
/** True when the engine CLI is present (and, where checkable, authenticated). */
|
|
37
|
+
available: boolean;
|
|
38
|
+
/** Human-readable reason when unavailable (drives picker help text in D4). */
|
|
39
|
+
detail?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One coding-agent engine (Claude Code, Codex, ...). Implementations are
|
|
43
|
+
* registered in the backend registry and resolved by `id`.
|
|
44
|
+
*/
|
|
45
|
+
export interface AgentBackend {
|
|
46
|
+
/** Stable engine id used as the registry key (e.g. "claude", "codex"). */
|
|
47
|
+
readonly id: string;
|
|
48
|
+
/** Human-friendly name for UI/logs (e.g. "Claude Code"). */
|
|
49
|
+
readonly displayName: string;
|
|
50
|
+
/** Resolve the executable command for this engine from daemon config. */
|
|
51
|
+
resolveCommand(config: DaemonConfig): string;
|
|
52
|
+
/** Build CLI args for a fresh (non-resume) spawn. */
|
|
53
|
+
buildSpawnArgs(ctx: AgentSpawnContext): string[];
|
|
54
|
+
/** Build CLI args for resuming a prior session. */
|
|
55
|
+
buildResumeArgs(ctx: AgentResumeContext): string[];
|
|
56
|
+
/** Encode a prompt/message into the exact bytes to write to the process stdin. */
|
|
57
|
+
encodeInput(content: string): string;
|
|
58
|
+
/** Create a fresh parser that maps this engine's stream to AgentEvents. */
|
|
59
|
+
createParser(): AgentEventParser;
|
|
60
|
+
/** Probe whether this engine is available on the current daemon host. */
|
|
61
|
+
probeAvailability(config: DaemonConfig): Promise<BackendAvailability>;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=agent-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-backend.d.ts","sourceRoot":"","sources":["../../src/backends/agent-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,MAAM,EAAE,YAAY,CAAC;IACrB,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,2CAA2C;AAC3C,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,+EAA+E;IAC/E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,yEAAyE;IACzE,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;IAE7C,qDAAqD;IACrD,cAAc,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAAC;IAEjD,mDAAmD;IACnD,eAAe,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAAC;IAEnD,kFAAkF;IAClF,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAErC,2EAA2E;IAC3E,YAAY,IAAI,gBAAgB,CAAC;IAEjC,yEAAyE;IACzE,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACvE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentBackend interface -- the engine-agnostic seam for spawning coding agents.
|
|
3
|
+
*
|
|
4
|
+
* The daemon's focus-execution path used to be welded to Claude Code (the
|
|
5
|
+
* `claude` binary, stream-json I/O, `--resume` session ids). An `AgentBackend`
|
|
6
|
+
* abstracts the four engine-specific concerns behind one interface so the daemon
|
|
7
|
+
* can run a focus on any registered engine:
|
|
8
|
+
*
|
|
9
|
+
* 1. spawn-arg / resume-arg construction (CLI invocation per engine)
|
|
10
|
+
* 2. input encoding (how a prompt/message is written to the process)
|
|
11
|
+
* 3. an event-parser factory (engine stream -> neutral AgentEvent stream)
|
|
12
|
+
* 4. an availability probe (is this engine installed + authenticated here?)
|
|
13
|
+
*
|
|
14
|
+
* Session-id capture rides on the parser (`AgentEventParser.sessionId`, set from
|
|
15
|
+
* the engine's neutral `session_started` event). Backends are looked up by id
|
|
16
|
+
* from the registry (./registry.ts); `claude` is the only one registered in D1.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=agent-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-backend.js","sourceRoot":"","sources":["../../src/backends/agent-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine availability probing for the daemon host.
|
|
3
|
+
*
|
|
4
|
+
* On connect (via the heartbeat metadata), the daemon publishes which
|
|
5
|
+
* registered engines are usable on its host so the app's engine picker can
|
|
6
|
+
* grey out the rest. The probe runs once per daemon process and is cached:
|
|
7
|
+
* installed CLIs don't change during a run, and a restart re-probes.
|
|
8
|
+
*/
|
|
9
|
+
import type { DaemonConfig } from '../types.js';
|
|
10
|
+
/** One engine's availability on this host, as published to the app. */
|
|
11
|
+
export interface BackendAvailabilityReport {
|
|
12
|
+
/** Registry id (e.g. 'claude', 'codex'). */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Human-friendly name. */
|
|
15
|
+
displayName: string;
|
|
16
|
+
/** True when the engine CLI is present (and, where checkable, authenticated). */
|
|
17
|
+
available: boolean;
|
|
18
|
+
/** Why unavailable (host + fix command); drives the picker's help text. */
|
|
19
|
+
detail?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Probe every registered backend's availability on this host. Cached after the
|
|
23
|
+
* first call (the result is stable for the life of the daemon process).
|
|
24
|
+
*/
|
|
25
|
+
export declare function probeAllBackends(config: DaemonConfig): Promise<BackendAvailabilityReport[]>;
|
|
26
|
+
/** Test-only: clear the cached probe so the next call re-probes. */
|
|
27
|
+
export declare function _resetAvailabilityCache(): void;
|
|
28
|
+
//# sourceMappingURL=availability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability.d.ts","sourceRoot":"","sources":["../../src/backends/availability.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,uEAAuE;AACvE,MAAM,WAAW,yBAAyB;IACxC,4CAA4C;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA4BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAK3F;AAED,oEAAoE;AACpE,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine availability probing for the daemon host.
|
|
3
|
+
*
|
|
4
|
+
* On connect (via the heartbeat metadata), the daemon publishes which
|
|
5
|
+
* registered engines are usable on its host so the app's engine picker can
|
|
6
|
+
* grey out the rest. The probe runs once per daemon process and is cached:
|
|
7
|
+
* installed CLIs don't change during a run, and a restart re-probes.
|
|
8
|
+
*/
|
|
9
|
+
import { listBackends } from './registry.js';
|
|
10
|
+
let cached = null;
|
|
11
|
+
async function doProbe(config) {
|
|
12
|
+
const results = [];
|
|
13
|
+
for (const backend of listBackends()) {
|
|
14
|
+
try {
|
|
15
|
+
const availability = await backend.probeAvailability(config);
|
|
16
|
+
results.push({
|
|
17
|
+
id: backend.id,
|
|
18
|
+
displayName: backend.displayName,
|
|
19
|
+
available: availability.available,
|
|
20
|
+
detail: availability.detail,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
// A probe must never break the heartbeat -- report the engine unavailable.
|
|
25
|
+
results.push({
|
|
26
|
+
id: backend.id,
|
|
27
|
+
displayName: backend.displayName,
|
|
28
|
+
available: false,
|
|
29
|
+
detail: err instanceof Error ? err.message : 'availability probe failed',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Probe every registered backend's availability on this host. Cached after the
|
|
37
|
+
* first call (the result is stable for the life of the daemon process).
|
|
38
|
+
*/
|
|
39
|
+
export function probeAllBackends(config) {
|
|
40
|
+
if (!cached) {
|
|
41
|
+
cached = doProbe(config);
|
|
42
|
+
}
|
|
43
|
+
return cached;
|
|
44
|
+
}
|
|
45
|
+
/** Test-only: clear the cached probe so the next call re-probes. */
|
|
46
|
+
export function _resetAvailabilityCache() {
|
|
47
|
+
cached = null;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=availability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability.js","sourceRoot":"","sources":["../../src/backends/availability.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAe7C,IAAI,MAAM,GAAgD,IAAI,CAAC;AAE/D,KAAK,UAAU,OAAO,CAAC,MAAoB;IACzC,MAAM,OAAO,GAAgC,EAAE,CAAC;IAChD,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC7D,OAAO,CAAC,IAAI,CAAC;gBACX,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,SAAS,EAAE,YAAY,CAAC,SAAS;gBACjC,MAAM,EAAE,YAAY,CAAC,MAAM;aAC5B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,2EAA2E;YAC3E,OAAO,CAAC,IAAI,CAAC;gBACX,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,2BAA2B;aACzE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACnD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,uBAAuB;IACrC,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code backend -- the built-in `AgentBackend` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Owns everything Claude-Code-specific that used to be inlined across the spawn
|
|
5
|
+
* paths: the `--teammate-mode in-process` + stream-json flag set, the `--resume`
|
|
6
|
+
* arg shape, the stream-json stdin encoding, and the StreamJsonParser-based
|
|
7
|
+
* event stream (via the `ClaudeAgentParser` adapter). The rest of the daemon
|
|
8
|
+
* now talks to this through the engine-agnostic interface.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentBackend } from '../agent-backend.js';
|
|
11
|
+
export declare const claudeBackend: AgentBackend;
|
|
12
|
+
//# sourceMappingURL=claude-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-backend.d.ts","sourceRoot":"","sources":["../../../src/backends/claude/claude-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,qBAAqB,CAAC;AAoC7B,eAAO,MAAM,aAAa,EAAE,YA8C3B,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code backend -- the built-in `AgentBackend` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Owns everything Claude-Code-specific that used to be inlined across the spawn
|
|
5
|
+
* paths: the `--teammate-mode in-process` + stream-json flag set, the `--resume`
|
|
6
|
+
* arg shape, the stream-json stdin encoding, and the StreamJsonParser-based
|
|
7
|
+
* event stream (via the `ClaudeAgentParser` adapter). The rest of the daemon
|
|
8
|
+
* now talks to this through the engine-agnostic interface.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { join, isAbsolute } from 'node:path';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { ClaudeAgentParser } from '@telora/daemon-core';
|
|
14
|
+
/**
|
|
15
|
+
* Build the Claude Code stream-json flag set shared by fresh + resume spawns.
|
|
16
|
+
* Mirrors the previous `buildTeamLeadArgs` body (minus the leading `--resume`,
|
|
17
|
+
* which `buildResumeArgs` prepends): teammate mode + stream-json + the optional
|
|
18
|
+
* model override.
|
|
19
|
+
*/
|
|
20
|
+
function buildBaseArgs(ctx) {
|
|
21
|
+
const args = [];
|
|
22
|
+
args.push('--dangerously-skip-permissions');
|
|
23
|
+
args.push('--setting-sources', 'project,local');
|
|
24
|
+
args.push('--input-format', 'stream-json');
|
|
25
|
+
args.push('--output-format', 'stream-json');
|
|
26
|
+
args.push('--verbose');
|
|
27
|
+
// Enable in-process teammate mode for the team lead.
|
|
28
|
+
args.push('--teammate-mode', 'in-process');
|
|
29
|
+
if (ctx.model) {
|
|
30
|
+
args.push('--model', ctx.model);
|
|
31
|
+
}
|
|
32
|
+
return args;
|
|
33
|
+
}
|
|
34
|
+
/** Resolve whether an executable name/path exists on the host. */
|
|
35
|
+
function commandExists(command) {
|
|
36
|
+
if (isAbsolute(command) || command.includes('/')) {
|
|
37
|
+
return existsSync(command);
|
|
38
|
+
}
|
|
39
|
+
const pathEnv = process.env.PATH ?? '';
|
|
40
|
+
return pathEnv
|
|
41
|
+
.split(':')
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.some((dir) => existsSync(join(dir, command)));
|
|
44
|
+
}
|
|
45
|
+
export const claudeBackend = {
|
|
46
|
+
id: 'claude',
|
|
47
|
+
displayName: 'Claude Code',
|
|
48
|
+
resolveCommand(config) {
|
|
49
|
+
return config.claudeCodePath;
|
|
50
|
+
},
|
|
51
|
+
buildSpawnArgs(ctx) {
|
|
52
|
+
return buildBaseArgs(ctx);
|
|
53
|
+
},
|
|
54
|
+
buildResumeArgs(ctx) {
|
|
55
|
+
// --resume must come early, before --input-format stream-json.
|
|
56
|
+
return ['--resume', ctx.resumeSessionId, ...buildBaseArgs(ctx)];
|
|
57
|
+
},
|
|
58
|
+
encodeInput(content) {
|
|
59
|
+
// Identical wire format to daemon-core's `sendMessage`: a single
|
|
60
|
+
// stream-json user message line.
|
|
61
|
+
return JSON.stringify({ type: 'user', message: { role: 'user', content } }) + '\n';
|
|
62
|
+
},
|
|
63
|
+
createParser() {
|
|
64
|
+
return new ClaudeAgentParser();
|
|
65
|
+
},
|
|
66
|
+
async probeAvailability(config) {
|
|
67
|
+
const command = config.claudeCodePath;
|
|
68
|
+
if (!commandExists(command)) {
|
|
69
|
+
return {
|
|
70
|
+
available: false,
|
|
71
|
+
detail: `Claude Code CLI not found (looked for "${command}" on PATH). Install it and ensure it is on the daemon host's PATH.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
75
|
+
return { available: true };
|
|
76
|
+
}
|
|
77
|
+
if (existsSync(join(homedir(), '.claude.json'))) {
|
|
78
|
+
return { available: true };
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
available: false,
|
|
82
|
+
detail: `Claude Code found at "${command}" but not authenticated. Run \`claude auth login\` or set ANTHROPIC_API_KEY on the daemon host.`,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=claude-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-backend.js","sourceRoot":"","sources":["../../../src/backends/claude/claude-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex CLI backend -- runs a focus on OpenAI Codex (`codex exec`).
|
|
3
|
+
*
|
|
4
|
+
* Recipe validated against Codex CLI 0.133.0 (spike: /tmp/codex-spike):
|
|
5
|
+
* - spawn: `codex exec --json --dangerously-bypass-approvals-and-sandbox
|
|
6
|
+
* --cd <worktree> -c mcp_servers.telora.* -` (prompt on stdin)
|
|
7
|
+
* - resume: `codex exec resume <sessionId> --json --dangerously-bypass-...`
|
|
8
|
+
* NO `--cd` (the resume subcommand rejects it; workspace is recovered
|
|
9
|
+
* from the session). NEVER `--ephemeral` (it skips session persistence
|
|
10
|
+
* and breaks resume).
|
|
11
|
+
*
|
|
12
|
+
* Bypass mode matches the Claude `--dangerously-skip-permissions` posture (the
|
|
13
|
+
* daemon runs in isolated worktrees) and is also what makes headless MCP work
|
|
14
|
+
* without an approval prompt. The Telora MCP server is wired in as a config
|
|
15
|
+
* override so each Codex agent spawns its own MCP subprocess.
|
|
16
|
+
*
|
|
17
|
+
* Auth: Codex uses the host `~/.codex` OAuth login by default; a `CODEX_API_KEY`
|
|
18
|
+
* in the spawn environment overrides it (org-secret injection rides the spawn
|
|
19
|
+
* environment, not this backend).
|
|
20
|
+
*/
|
|
21
|
+
import type { AgentBackend } from '../agent-backend.js';
|
|
22
|
+
export declare const codexBackend: AgentBackend;
|
|
23
|
+
//# sourceMappingURL=codex-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-backend.d.ts","sourceRoot":"","sources":["../../../src/backends/codex/codex-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,qBAAqB,CAAC;AAyC7B,eAAO,MAAM,YAAY,EAAE,YAwD1B,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex CLI backend -- runs a focus on OpenAI Codex (`codex exec`).
|
|
3
|
+
*
|
|
4
|
+
* Recipe validated against Codex CLI 0.133.0 (spike: /tmp/codex-spike):
|
|
5
|
+
* - spawn: `codex exec --json --dangerously-bypass-approvals-and-sandbox
|
|
6
|
+
* --cd <worktree> -c mcp_servers.telora.* -` (prompt on stdin)
|
|
7
|
+
* - resume: `codex exec resume <sessionId> --json --dangerously-bypass-...`
|
|
8
|
+
* NO `--cd` (the resume subcommand rejects it; workspace is recovered
|
|
9
|
+
* from the session). NEVER `--ephemeral` (it skips session persistence
|
|
10
|
+
* and breaks resume).
|
|
11
|
+
*
|
|
12
|
+
* Bypass mode matches the Claude `--dangerously-skip-permissions` posture (the
|
|
13
|
+
* daemon runs in isolated worktrees) and is also what makes headless MCP work
|
|
14
|
+
* without an approval prompt. The Telora MCP server is wired in as a config
|
|
15
|
+
* override so each Codex agent spawns its own MCP subprocess.
|
|
16
|
+
*
|
|
17
|
+
* Auth: Codex uses the host `~/.codex` OAuth login by default; a `CODEX_API_KEY`
|
|
18
|
+
* in the spawn environment overrides it (org-secret injection rides the spawn
|
|
19
|
+
* environment, not this backend).
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync } from 'node:fs';
|
|
22
|
+
import { join, isAbsolute } from 'node:path';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
import { CodexAgentParser, resolveMcpServerPath } from '@telora/daemon-core';
|
|
25
|
+
/** Bypass approvals + sandbox -- matches Claude's --dangerously-skip-permissions. */
|
|
26
|
+
const BYPASS_FLAG = '--dangerously-bypass-approvals-and-sandbox';
|
|
27
|
+
function codexCommand(config) {
|
|
28
|
+
return config.codexPath ?? 'codex';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build `-c mcp_servers.telora.*` config overrides wiring the Telora MCP server
|
|
32
|
+
* into the Codex run. Values are TOML literals, so strings are quoted.
|
|
33
|
+
* Best-effort: if the MCP package can't be resolved, MCP is omitted (the run
|
|
34
|
+
* still starts; the gap surfaces at runtime).
|
|
35
|
+
*/
|
|
36
|
+
function mcpConfigFlags(config) {
|
|
37
|
+
let mcpServerPath;
|
|
38
|
+
try {
|
|
39
|
+
mcpServerPath = resolveMcpServerPath(import.meta.url);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.warn('[codex-backend] Could not resolve Telora MCP server path; running without MCP:', err.message);
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const flags = [
|
|
46
|
+
'-c', `mcp_servers.telora.command="node"`,
|
|
47
|
+
'-c', `mcp_servers.telora.args=[${JSON.stringify(mcpServerPath)}]`,
|
|
48
|
+
'-c', `mcp_servers.telora.env.TELORA_MCP_PROFILE="execution"`,
|
|
49
|
+
'-c', `mcp_servers.telora.required=true`,
|
|
50
|
+
];
|
|
51
|
+
if (config.teloraUrl)
|
|
52
|
+
flags.push('-c', `mcp_servers.telora.env.TELORA_URL=${JSON.stringify(config.teloraUrl)}`);
|
|
53
|
+
if (config.trackerId)
|
|
54
|
+
flags.push('-c', `mcp_servers.telora.env.TELORA_TRACKER_ID=${JSON.stringify(config.trackerId)}`);
|
|
55
|
+
return flags;
|
|
56
|
+
}
|
|
57
|
+
function commandExists(command) {
|
|
58
|
+
if (isAbsolute(command) || command.includes('/'))
|
|
59
|
+
return existsSync(command);
|
|
60
|
+
const pathEnv = process.env.PATH ?? '';
|
|
61
|
+
return pathEnv.split(':').filter(Boolean).some((dir) => existsSync(join(dir, command)));
|
|
62
|
+
}
|
|
63
|
+
export const codexBackend = {
|
|
64
|
+
id: 'codex',
|
|
65
|
+
displayName: 'Codex CLI',
|
|
66
|
+
resolveCommand(config) {
|
|
67
|
+
return codexCommand(config);
|
|
68
|
+
},
|
|
69
|
+
buildSpawnArgs(ctx) {
|
|
70
|
+
const args = ['exec', '--json', BYPASS_FLAG, '--cd', ctx.worktreePath];
|
|
71
|
+
if (ctx.model)
|
|
72
|
+
args.push('--model', ctx.model);
|
|
73
|
+
args.push(...mcpConfigFlags(ctx.config));
|
|
74
|
+
// Trailing '-' tells codex exec to read the prompt from stdin.
|
|
75
|
+
args.push('-');
|
|
76
|
+
return args;
|
|
77
|
+
},
|
|
78
|
+
buildResumeArgs(ctx) {
|
|
79
|
+
// resume recovers the workspace from the session -- NO --cd, NEVER --ephemeral.
|
|
80
|
+
const args = ['exec', 'resume', ctx.resumeSessionId, '--json', BYPASS_FLAG];
|
|
81
|
+
if (ctx.model)
|
|
82
|
+
args.push('--model', ctx.model);
|
|
83
|
+
// MCP config overrides are global `-c` flags (accepted by the resume
|
|
84
|
+
// subcommand) so the resumed agent keeps Telora MCP access.
|
|
85
|
+
args.push(...mcpConfigFlags(ctx.config));
|
|
86
|
+
args.push('-');
|
|
87
|
+
return args;
|
|
88
|
+
},
|
|
89
|
+
encodeInput(content) {
|
|
90
|
+
// Codex reads the prompt as plain text on stdin (not stream-json).
|
|
91
|
+
return content.endsWith('\n') ? content : content + '\n';
|
|
92
|
+
},
|
|
93
|
+
createParser() {
|
|
94
|
+
return new CodexAgentParser();
|
|
95
|
+
},
|
|
96
|
+
async probeAvailability(config) {
|
|
97
|
+
const command = codexCommand(config);
|
|
98
|
+
if (!commandExists(command)) {
|
|
99
|
+
return {
|
|
100
|
+
available: false,
|
|
101
|
+
detail: `Codex CLI not found (looked for "${command}" on PATH). Install it (npm i -g @openai/codex) and run \`codex login\` on the daemon host.`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (process.env.CODEX_API_KEY) {
|
|
105
|
+
return { available: true };
|
|
106
|
+
}
|
|
107
|
+
if (existsSync(join(homedir(), '.codex', 'auth.json'))) {
|
|
108
|
+
return { available: true };
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
available: false,
|
|
112
|
+
detail: `Codex CLI found at "${command}" but not authenticated. Run \`codex login\` or set CODEX_API_KEY on the daemon host.`,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=codex-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-backend.js","sourceRoot":"","sources":["../../../src/backends/codex/codex-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent backend layer -- engine-agnostic spawn seam.
|
|
3
|
+
*
|
|
4
|
+
* Importing this barrel registers all built-in backends as a side effect, so
|
|
5
|
+
* any module that resolves a backend from the registry first imports from here.
|
|
6
|
+
*/
|
|
7
|
+
export { type AgentBackend, type AgentSpawnContext, type AgentResumeContext, type BackendAvailability, } from './agent-backend.js';
|
|
8
|
+
export { DEFAULT_BACKEND_ID, registerBackend, getBackend, hasBackend, listBackends, _resetRegistry, } from './registry.js';
|
|
9
|
+
export { claudeBackend } from './claude/claude-backend.js';
|
|
10
|
+
export { codexBackend } from './codex/codex-backend.js';
|
|
11
|
+
export { resolveBackend, type EnginePass, type EngineOverride, type EngineSource, type ResolvedEngine, type ResolveBackendArgs, } from './resolve.js';
|
|
12
|
+
export { probeAllBackends, _resetAvailabilityCache, type BackendAvailabilityReport, } from './availability.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent backend layer -- engine-agnostic spawn seam.
|
|
3
|
+
*
|
|
4
|
+
* Importing this barrel registers all built-in backends as a side effect, so
|
|
5
|
+
* any module that resolves a backend from the registry first imports from here.
|
|
6
|
+
*/
|
|
7
|
+
import { registerBackend } from './registry.js';
|
|
8
|
+
import { claudeBackend } from './claude/claude-backend.js';
|
|
9
|
+
import { codexBackend } from './codex/codex-backend.js';
|
|
10
|
+
// Register built-in backends. `claude` is the default; `codex` is selectable
|
|
11
|
+
// once per-spawn engine resolution (D3) lands -- registering it here makes it
|
|
12
|
+
// resolvable and lets the availability probe (D4) report it.
|
|
13
|
+
registerBackend(claudeBackend);
|
|
14
|
+
registerBackend(codexBackend);
|
|
15
|
+
export { DEFAULT_BACKEND_ID, registerBackend, getBackend, hasBackend, listBackends, _resetRegistry, } from './registry.js';
|
|
16
|
+
export { claudeBackend } from './claude/claude-backend.js';
|
|
17
|
+
export { codexBackend } from './codex/codex-backend.js';
|
|
18
|
+
export { resolveBackend, } from './resolve.js';
|
|
19
|
+
export { probeAllBackends, _resetAvailabilityCache, } from './availability.js';
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,6EAA6E;AAC7E,8EAA8E;AAC9E,6DAA6D;AAC7D,eAAe,CAAC,aAAa,CAAC,CAAC;AAC/B,eAAe,CAAC,YAAY,CAAC,CAAC;AAS9B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACL,cAAc,GAMf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,GAExB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentBackend registry -- resolves an engine id to its implementation.
|
|
3
|
+
*
|
|
4
|
+
* Backends register themselves at module load (see ./index.ts, which imports
|
|
5
|
+
* and registers the built-in `claude` backend). Spawn paths resolve a backend
|
|
6
|
+
* by id; selection of WHICH id to use (org default + per-focus override) is a
|
|
7
|
+
* later delivery -- this registry only maps id -> implementation.
|
|
8
|
+
*
|
|
9
|
+
* Built N-backends-ready by design: adding Codex/Gemini later is a register
|
|
10
|
+
* call, not a rewrite.
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentBackend } from './agent-backend.js';
|
|
13
|
+
/** The default backend id used when no selection is provided. */
|
|
14
|
+
export declare const DEFAULT_BACKEND_ID = "claude";
|
|
15
|
+
/** Register (or replace) a backend implementation under its id. */
|
|
16
|
+
export declare function registerBackend(backend: AgentBackend): void;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a backend by id. Falls back to the default backend when the id is
|
|
19
|
+
* null/undefined. Throws when an explicitly requested id is not registered --
|
|
20
|
+
* a misconfigured engine should fail loudly, not silently run on the wrong one.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getBackend(id?: string | null): AgentBackend;
|
|
23
|
+
/** True when a backend is registered for the given id. */
|
|
24
|
+
export declare function hasBackend(id: string): boolean;
|
|
25
|
+
/** List all registered backends (for availability probing + the picker). */
|
|
26
|
+
export declare function listBackends(): AgentBackend[];
|
|
27
|
+
/** Test-only: clear the registry. */
|
|
28
|
+
export declare function _resetRegistry(): void;
|
|
29
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/backends/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CAQ3D;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,IAAI,YAAY,EAAE,CAE7C;AAED,qCAAqC;AACrC,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentBackend registry -- resolves an engine id to its implementation.
|
|
3
|
+
*
|
|
4
|
+
* Backends register themselves at module load (see ./index.ts, which imports
|
|
5
|
+
* and registers the built-in `claude` backend). Spawn paths resolve a backend
|
|
6
|
+
* by id; selection of WHICH id to use (org default + per-focus override) is a
|
|
7
|
+
* later delivery -- this registry only maps id -> implementation.
|
|
8
|
+
*
|
|
9
|
+
* Built N-backends-ready by design: adding Codex/Gemini later is a register
|
|
10
|
+
* call, not a rewrite.
|
|
11
|
+
*/
|
|
12
|
+
const registry = new Map();
|
|
13
|
+
/** The default backend id used when no selection is provided. */
|
|
14
|
+
export const DEFAULT_BACKEND_ID = 'claude';
|
|
15
|
+
/** Register (or replace) a backend implementation under its id. */
|
|
16
|
+
export function registerBackend(backend) {
|
|
17
|
+
registry.set(backend.id, backend);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a backend by id. Falls back to the default backend when the id is
|
|
21
|
+
* null/undefined. Throws when an explicitly requested id is not registered --
|
|
22
|
+
* a misconfigured engine should fail loudly, not silently run on the wrong one.
|
|
23
|
+
*/
|
|
24
|
+
export function getBackend(id) {
|
|
25
|
+
const resolvedId = id ?? DEFAULT_BACKEND_ID;
|
|
26
|
+
const backend = registry.get(resolvedId);
|
|
27
|
+
if (!backend) {
|
|
28
|
+
const known = Array.from(registry.keys()).join(', ') || '(none)';
|
|
29
|
+
throw new Error(`No agent backend registered for id "${resolvedId}". Registered: ${known}`);
|
|
30
|
+
}
|
|
31
|
+
return backend;
|
|
32
|
+
}
|
|
33
|
+
/** True when a backend is registered for the given id. */
|
|
34
|
+
export function hasBackend(id) {
|
|
35
|
+
return registry.has(id);
|
|
36
|
+
}
|
|
37
|
+
/** List all registered backends (for availability probing + the picker). */
|
|
38
|
+
export function listBackends() {
|
|
39
|
+
return Array.from(registry.values());
|
|
40
|
+
}
|
|
41
|
+
/** Test-only: clear the registry. */
|
|
42
|
+
export function _resetRegistry() {
|
|
43
|
+
registry.clear();
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/backends/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEjD,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE3C,mEAAmE;AACnE,MAAM,UAAU,eAAe,CAAC,OAAqB;IACnD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,EAAkB;IAC3C,MAAM,UAAU,GAAG,EAAE,IAAI,kBAAkB,CAAC;IAC5C,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,uCAAuC,UAAU,kBAAkB,KAAK,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,cAAc;IAC5B,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-spawn engine resolution -- the single place the daemon decides WHICH
|
|
3
|
+
* engine a spawn runs on.
|
|
4
|
+
*
|
|
5
|
+
* Selection is one knob (the engine) with a default + one override layer,
|
|
6
|
+
* most-specific wins, resolution always logged (the no-invisible-cascading
|
|
7
|
+
* principle). Mirrors the centralization of `shouldRunLoop()` in
|
|
8
|
+
* focus-engine.ts: one exported pure function, no side effects, easy to test.
|
|
9
|
+
*
|
|
10
|
+
* Resolution order for a pass (coding | review):
|
|
11
|
+
* 1. per-focus override for that pass (focusOverride[pass], if a truthy id)
|
|
12
|
+
* 2. org-level default (orgDefault, if a truthy id)
|
|
13
|
+
* 3. the global default (DEFAULT_BACKEND_ID = 'claude')
|
|
14
|
+
*
|
|
15
|
+
* Guard: a resolved id that is not a registered backend (typo, or an engine the
|
|
16
|
+
* host lacks) falls back to the default rather than spawning on a bogus engine.
|
|
17
|
+
* With no override and no org default, this returns 'claude' -- behavior parity.
|
|
18
|
+
*/
|
|
19
|
+
/** A spawn "pass" -- the coding vs review lineage. */
|
|
20
|
+
export type EnginePass = 'coding' | 'review';
|
|
21
|
+
/** Per-focus engine override: an engine id per pass; absent/null = inherit. */
|
|
22
|
+
export interface EngineOverride {
|
|
23
|
+
coding?: string | null;
|
|
24
|
+
review?: string | null;
|
|
25
|
+
}
|
|
26
|
+
/** Where the resolved engine came from (always logged). */
|
|
27
|
+
export type EngineSource = 'focus-override' | 'org-default' | 'default' | 'fallback';
|
|
28
|
+
export interface ResolvedEngine {
|
|
29
|
+
engineId: string;
|
|
30
|
+
source: EngineSource;
|
|
31
|
+
}
|
|
32
|
+
export interface ResolveBackendArgs {
|
|
33
|
+
/** Per-focus override map (null/absent = inherit the org default). */
|
|
34
|
+
focusOverride?: EngineOverride | null;
|
|
35
|
+
/** The pass being spawned. */
|
|
36
|
+
pass: EnginePass;
|
|
37
|
+
/** Org-level default engine id (null/absent = the global default). */
|
|
38
|
+
orgDefault?: string | null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the engine id (and its source) for a spawn pass. Pure + deterministic.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveBackend(args: ResolveBackendArgs): ResolvedEngine;
|
|
44
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/backends/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE7C,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GAAG,gBAAgB,GAAG,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AAErF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,aAAa,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,8BAA8B;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AASD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CA4BvE"}
|