@automatalabs/acp-agents 1.2.6 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -3
- package/dist/acp-client.d.ts +21 -1
- package/dist/acp-client.d.ts.map +1 -1
- package/dist/acp-client.js +31 -6
- package/dist/agent/acp-agent.d.ts +141 -0
- package/dist/agent/acp-agent.d.ts.map +1 -0
- package/dist/agent/acp-agent.js +832 -0
- package/dist/agent/errors.d.ts +17 -0
- package/dist/agent/errors.d.ts.map +1 -0
- package/dist/agent/errors.js +37 -0
- package/dist/agent/events.d.ts +26 -0
- package/dist/agent/events.d.ts.map +1 -0
- package/dist/agent/events.js +165 -0
- package/dist/agent/fork.d.ts +27 -0
- package/dist/agent/fork.d.ts.map +1 -0
- package/dist/agent/fork.js +28 -0
- package/dist/agent/probe.d.ts +14 -0
- package/dist/agent/probe.d.ts.map +1 -0
- package/dist/agent/probe.js +87 -0
- package/dist/agent/process-registry.d.ts +9 -0
- package/dist/agent/process-registry.d.ts.map +1 -0
- package/dist/agent/process-registry.js +28 -0
- package/dist/agent/queue.d.ts +19 -0
- package/dist/agent/queue.d.ts.map +1 -0
- package/dist/agent/queue.js +90 -0
- package/dist/agent/routing.d.ts +27 -0
- package/dist/agent/routing.d.ts.map +1 -0
- package/dist/agent/routing.js +87 -0
- package/dist/agent/structured.d.ts +47 -0
- package/dist/agent/structured.d.ts.map +1 -0
- package/dist/agent/structured.js +90 -0
- package/dist/agent/turn.d.ts +65 -0
- package/dist/agent/turn.d.ts.map +1 -0
- package/dist/agent/turn.js +187 -0
- package/dist/agent/types.d.ts +221 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +9 -0
- package/dist/backend.d.ts +6 -0
- package/dist/backend.d.ts.map +1 -1
- package/dist/backends/claude.d.ts +1 -0
- package/dist/backends/claude.d.ts.map +1 -1
- package/dist/backends/claude.js +6 -1
- package/dist/config-catalog.d.ts +173 -0
- package/dist/config-catalog.d.ts.map +1 -0
- package/dist/config-catalog.js +408 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/permissions.js +5 -0
- package/dist/protocol-coverage.d.ts +56 -0
- package/dist/protocol-coverage.d.ts.map +1 -1
- package/dist/protocol-coverage.js +52 -0
- package/dist/registry.d.ts +12 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +22 -0
- package/dist/routing.d.ts +14 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +53 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +3 -64
- package/dist/session-ref.d.ts +8 -0
- package/dist/session-ref.d.ts.map +1 -0
- package/dist/session-ref.js +21 -0
- package/dist/structured-tool.d.ts +4 -0
- package/dist/structured-tool.d.ts.map +1 -1
- package/dist/structured-tool.js +5 -0
- package/package.json +5 -5
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WorkflowError } from "@automatalabs/shared-types";
|
|
2
|
+
import { type ErrorMapContext } from "../errors-map.js";
|
|
3
|
+
import type { TypedSessionFailure } from "../typed-failures.js";
|
|
4
|
+
import type { AcpAgentTurn, AcpAgentTurnError } from "./types.js";
|
|
5
|
+
/** A deterministic caller error (bad option, misuse): SCRIPT_VALIDATION_ERROR, non-recoverable. */
|
|
6
|
+
export declare function agentValidationError(message: string, label?: string): WorkflowError;
|
|
7
|
+
/** Every method except `close()`/`cancel()`/getters after the agent closed, aborted, or its process died. */
|
|
8
|
+
export declare function agentClosedError(label: string | undefined, backendId: string, detail?: string): WorkflowError;
|
|
9
|
+
/** Map a thrown wire/lifecycle error onto the seam contract — unless `signal` aborted, in which
|
|
10
|
+
* case the error is returned untouched so the caller can rethrow `signal.reason` itself. */
|
|
11
|
+
export declare function mapAgentError(error: unknown, ctx: ErrorMapContext, signal?: AbortSignal): unknown;
|
|
12
|
+
/** The `prompt()` rejection for a walled turn: `mapTypedSessionFailure` (codes/recoverable/details
|
|
13
|
+
* unchanged) with the complete `AcpAgentTurn` attached as `error.turn`. */
|
|
14
|
+
export declare function agentTurnError(failure: TypedSessionFailure, turn: AcpAgentTurn, ctx: ErrorMapContext): AcpAgentTurnError;
|
|
15
|
+
/** Narrow a `prompt()` rejection to the typed-session-failure shape that carries the turn. */
|
|
16
|
+
export declare function isAcpAgentTurnError(error: unknown): error is AcpAgentTurnError;
|
|
17
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/agent/errors.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAsC,MAAM,4BAA4B,CAAC;AAC/F,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAElE,mGAAmG;AACnG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa,CAKnF;AAED,6GAA6G;AAC7G,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAM7G;AAED;6FAC6F;AAC7F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAGjG;AAED;4EAC4E;AAC5E,wBAAgB,cAAc,CAC5B,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,eAAe,GACnB,iBAAiB,CAInB;AAED,8FAA8F;AAC9F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAM9E"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// AcpAgent error helpers: SDK misuse is uniformly a WorkflowError(SCRIPT_VALIDATION_ERROR); wire
|
|
2
|
+
// failures go through the same `mapThrownError` ladder the runner uses; an abort is never mapped
|
|
3
|
+
// (the affected promise rejects with `signal.reason` untouched); a typed session failure rejects
|
|
4
|
+
// with the runner's mapped error carrying the complete turn.
|
|
5
|
+
import { WorkflowError, WorkflowErrorCode, isWorkflowError } from "@automatalabs/shared-types";
|
|
6
|
+
import { mapThrownError, mapTypedSessionFailure } from "../errors-map.js";
|
|
7
|
+
/** A deterministic caller error (bad option, misuse): SCRIPT_VALIDATION_ERROR, non-recoverable. */
|
|
8
|
+
export function agentValidationError(message, label) {
|
|
9
|
+
return new WorkflowError(message, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, {
|
|
10
|
+
recoverable: false,
|
|
11
|
+
agentLabel: label,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/** Every method except `close()`/`cancel()`/getters after the agent closed, aborted, or its process died. */
|
|
15
|
+
export function agentClosedError(label, backendId, detail) {
|
|
16
|
+
return new WorkflowError(`AcpAgent (${backendId}) is closed${detail ? `: ${detail}` : ""}`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false, agentLabel: label });
|
|
17
|
+
}
|
|
18
|
+
/** Map a thrown wire/lifecycle error onto the seam contract — unless `signal` aborted, in which
|
|
19
|
+
* case the error is returned untouched so the caller can rethrow `signal.reason` itself. */
|
|
20
|
+
export function mapAgentError(error, ctx, signal) {
|
|
21
|
+
if (signal?.aborted)
|
|
22
|
+
return error;
|
|
23
|
+
return mapThrownError(error, ctx);
|
|
24
|
+
}
|
|
25
|
+
/** The `prompt()` rejection for a walled turn: `mapTypedSessionFailure` (codes/recoverable/details
|
|
26
|
+
* unchanged) with the complete `AcpAgentTurn` attached as `error.turn`. */
|
|
27
|
+
export function agentTurnError(failure, turn, ctx) {
|
|
28
|
+
const mapped = mapTypedSessionFailure(failure, ctx);
|
|
29
|
+
Object.defineProperty(mapped, "turn", { value: turn, enumerable: false, writable: false, configurable: false });
|
|
30
|
+
return mapped;
|
|
31
|
+
}
|
|
32
|
+
/** Narrow a `prompt()` rejection to the typed-session-failure shape that carries the turn. */
|
|
33
|
+
export function isAcpAgentTurnError(error) {
|
|
34
|
+
return (isWorkflowError(error) &&
|
|
35
|
+
typeof error.turn === "object" &&
|
|
36
|
+
error.turn !== null);
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AcpEventSink } from "../events.js";
|
|
2
|
+
import type { AcpAgentEventListener, AcpAgentEventMap, AcpAgentEventName } from "./types.js";
|
|
3
|
+
/** An internal listener that runs BEFORE public listeners for every dispatched event. */
|
|
4
|
+
export type AgentEventTap = <K extends AcpAgentEventName>(name: K, event: AcpAgentEventMap[K]) => void;
|
|
5
|
+
export declare class AgentEventBus {
|
|
6
|
+
#private;
|
|
7
|
+
/** Passed to `PooledConnection.create({ onEvent })`. */
|
|
8
|
+
readonly sink: AcpEventSink;
|
|
9
|
+
constructor();
|
|
10
|
+
on<K extends AcpAgentEventName>(name: K, listener: AcpAgentEventListener<K>): () => void;
|
|
11
|
+
once<K extends AcpAgentEventName>(name: K, listener: AcpAgentEventListener<K>): () => void;
|
|
12
|
+
off<K extends AcpAgentEventName>(name: K, listener: AcpAgentEventListener<K>): void;
|
|
13
|
+
/** Internal observer; runs before public listeners, exception-isolated. Returns the untap thunk. */
|
|
14
|
+
tap(listener: AgentEventTap): () => void;
|
|
15
|
+
/** Buffer events for ids not yet known (a session is being opened/forked/reattached). */
|
|
16
|
+
beginAcquisition(): void;
|
|
17
|
+
/** Adopt `sessionId`: mark it known, replay the buffered entries for it (taps first, then public
|
|
18
|
+
* listeners) with the acquisition still flagged pending so the hand-off drops stay armed, then
|
|
19
|
+
* drop everything else. */
|
|
20
|
+
endAcquisition(sessionId: string): void;
|
|
21
|
+
/** Drop the buffer (the open failed). */
|
|
22
|
+
abortAcquisition(): void;
|
|
23
|
+
/** After teardown: listeners cleared; `on`/`once` become no-ops returning `() => {}`. */
|
|
24
|
+
close(): void;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/agent/events.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE7F,yFAAyF;AACzF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,SAAS,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAoBvG,qBAAa,aAAa;;IACxB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;;IAe5B,EAAE,CAAC,CAAC,SAAS,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIxF,IAAI,CAAC,CAAC,SAAS,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAI1F,GAAG,CAAC,CAAC,SAAS,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI;IAKnF,oGAAoG;IACpG,GAAG,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAOxC,yFAAyF;IACzF,gBAAgB,IAAI,IAAI;IAIxB;;gCAE4B;IAC5B,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAWvC,yCAAyC;IACzC,gBAAgB,IAAI,IAAI;IAMxB,yFAAyF;IACzF,KAAK,IAAI,IAAI;CA2Fd"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
function sessionIdOf(event) {
|
|
2
|
+
const sid = event?.sessionId;
|
|
3
|
+
return typeof sid === "string" ? sid : undefined;
|
|
4
|
+
}
|
|
5
|
+
export class AgentEventBus {
|
|
6
|
+
/** Passed to `PooledConnection.create({ onEvent })`. */
|
|
7
|
+
sink;
|
|
8
|
+
#entries = [];
|
|
9
|
+
#taps = new Set();
|
|
10
|
+
#known = new Set();
|
|
11
|
+
#acquiring = false;
|
|
12
|
+
#buffer = [];
|
|
13
|
+
#bufferedOpenIds = new Set();
|
|
14
|
+
#sawSessionOpen = false;
|
|
15
|
+
#sticky;
|
|
16
|
+
#closed = false;
|
|
17
|
+
constructor() {
|
|
18
|
+
this.sink = (name, event) => this.#route(name, event);
|
|
19
|
+
}
|
|
20
|
+
on(name, listener) {
|
|
21
|
+
return this.#subscribe(name, listener, false);
|
|
22
|
+
}
|
|
23
|
+
once(name, listener) {
|
|
24
|
+
return this.#subscribe(name, listener, true);
|
|
25
|
+
}
|
|
26
|
+
off(name, listener) {
|
|
27
|
+
const index = this.#entries.findIndex((entry) => entry.name === name && entry.listener === listener);
|
|
28
|
+
if (index >= 0)
|
|
29
|
+
this.#entries.splice(index, 1);
|
|
30
|
+
}
|
|
31
|
+
/** Internal observer; runs before public listeners, exception-isolated. Returns the untap thunk. */
|
|
32
|
+
tap(listener) {
|
|
33
|
+
this.#taps.add(listener);
|
|
34
|
+
return () => {
|
|
35
|
+
this.#taps.delete(listener);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Buffer events for ids not yet known (a session is being opened/forked/reattached). */
|
|
39
|
+
beginAcquisition() {
|
|
40
|
+
this.#acquiring = true;
|
|
41
|
+
}
|
|
42
|
+
/** Adopt `sessionId`: mark it known, replay the buffered entries for it (taps first, then public
|
|
43
|
+
* listeners) with the acquisition still flagged pending so the hand-off drops stay armed, then
|
|
44
|
+
* drop everything else. */
|
|
45
|
+
endAcquisition(sessionId) {
|
|
46
|
+
this.#known.add(sessionId);
|
|
47
|
+
const buffered = this.#buffer;
|
|
48
|
+
this.#buffer = [];
|
|
49
|
+
this.#bufferedOpenIds.clear();
|
|
50
|
+
for (const { name, event } of buffered) {
|
|
51
|
+
if (sessionIdOf(event) === sessionId)
|
|
52
|
+
this.#route(name, event);
|
|
53
|
+
}
|
|
54
|
+
this.#acquiring = false;
|
|
55
|
+
}
|
|
56
|
+
/** Drop the buffer (the open failed). */
|
|
57
|
+
abortAcquisition() {
|
|
58
|
+
this.#acquiring = false;
|
|
59
|
+
this.#buffer = [];
|
|
60
|
+
this.#bufferedOpenIds.clear();
|
|
61
|
+
}
|
|
62
|
+
/** After teardown: listeners cleared; `on`/`once` become no-ops returning `() => {}`. */
|
|
63
|
+
close() {
|
|
64
|
+
this.#closed = true;
|
|
65
|
+
this.#entries.length = 0;
|
|
66
|
+
this.#taps.clear();
|
|
67
|
+
}
|
|
68
|
+
#subscribe(name, listener, once) {
|
|
69
|
+
if (this.#closed)
|
|
70
|
+
return () => { };
|
|
71
|
+
const entry = {
|
|
72
|
+
name,
|
|
73
|
+
listener: listener,
|
|
74
|
+
once,
|
|
75
|
+
sawSessionOpen: false,
|
|
76
|
+
};
|
|
77
|
+
this.#entries.push(entry);
|
|
78
|
+
const off = () => {
|
|
79
|
+
const index = this.#entries.indexOf(entry);
|
|
80
|
+
if (index >= 0)
|
|
81
|
+
this.#entries.splice(index, 1);
|
|
82
|
+
};
|
|
83
|
+
if (name === "session_open" && this.#sticky) {
|
|
84
|
+
// Late subscriber: deliver the retained open once, on the next tick, only if still
|
|
85
|
+
// subscribed and not already served live in the meantime.
|
|
86
|
+
const sticky = this.#sticky;
|
|
87
|
+
queueMicrotask(() => {
|
|
88
|
+
if (entry.sawSessionOpen || !this.#entries.includes(entry))
|
|
89
|
+
return;
|
|
90
|
+
this.#deliver(entry, "session_open", sticky);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return off;
|
|
94
|
+
}
|
|
95
|
+
#route(name, event) {
|
|
96
|
+
if (this.#closed)
|
|
97
|
+
return;
|
|
98
|
+
const sid = sessionIdOf(event);
|
|
99
|
+
// Only `backend_error` carries no session id; the connection is dedicated, so it is ours.
|
|
100
|
+
if (sid === undefined) {
|
|
101
|
+
this.#dispatch(name, event);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (this.#known.has(sid)) {
|
|
105
|
+
// The id-only fork registers the same id twice (fork, then reattach) — one open per agent.
|
|
106
|
+
if (name === "session_open" && this.#sawSessionOpen)
|
|
107
|
+
return;
|
|
108
|
+
// The hand-off release unregisters the id while the reattach is pending — not our close.
|
|
109
|
+
if (name === "session_close" && this.#acquiring)
|
|
110
|
+
return;
|
|
111
|
+
this.#dispatch(name, event);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!this.#acquiring)
|
|
115
|
+
return;
|
|
116
|
+
// Unknown id while acquiring: never buffer a close (it is the hand-off release or a
|
|
117
|
+
// stranger's), buffer at most one open per id (the reattach registers before its wire call),
|
|
118
|
+
// buffer everything else for adoption.
|
|
119
|
+
if (name === "session_close")
|
|
120
|
+
return;
|
|
121
|
+
if (name === "session_open") {
|
|
122
|
+
if (this.#bufferedOpenIds.has(sid))
|
|
123
|
+
return;
|
|
124
|
+
this.#bufferedOpenIds.add(sid);
|
|
125
|
+
}
|
|
126
|
+
this.#buffer.push({ name, event });
|
|
127
|
+
}
|
|
128
|
+
#dispatch(name, event) {
|
|
129
|
+
if (name === "session_open") {
|
|
130
|
+
this.#sawSessionOpen = true;
|
|
131
|
+
this.#sticky ??= event;
|
|
132
|
+
}
|
|
133
|
+
for (const tap of [...this.#taps]) {
|
|
134
|
+
try {
|
|
135
|
+
tap(name, event);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Taps are internal observers; a throwing one never breaks delivery.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Snapshot so a listener that (un)subscribes during dispatch cannot perturb this emit.
|
|
142
|
+
for (const entry of [...this.#entries]) {
|
|
143
|
+
if (entry.name !== name)
|
|
144
|
+
continue;
|
|
145
|
+
this.#deliver(entry, name, event);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
#deliver(entry, name, event) {
|
|
149
|
+
if (!this.#entries.includes(entry))
|
|
150
|
+
return;
|
|
151
|
+
if (entry.once) {
|
|
152
|
+
const index = this.#entries.indexOf(entry);
|
|
153
|
+
if (index >= 0)
|
|
154
|
+
this.#entries.splice(index, 1);
|
|
155
|
+
}
|
|
156
|
+
if (name === "session_open")
|
|
157
|
+
entry.sawSessionOpen = true;
|
|
158
|
+
try {
|
|
159
|
+
entry.listener(event);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Listeners are observers — never let one break the turn or sibling listeners.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AcpSessionOptions, PooledConnection, SessionHandle } from "../acp-client.js";
|
|
2
|
+
import type { Backend } from "../backend.js";
|
|
3
|
+
import { type ForkSessionTraitRow } from "../protocol-coverage.js";
|
|
4
|
+
import type { BackendRegistry } from "../registry.js";
|
|
5
|
+
/** A registered custom backend uses its declared `fork` trait (or the live default); a built-in
|
|
6
|
+
* uses its `FORK_SESSION_TRAITS` row. A registry lookup, never an identity branch: a custom entry
|
|
7
|
+
* named like a built-in is a different program and never inherits the built-in's row. */
|
|
8
|
+
export declare function forkTraitFor(backend: Backend, registry: BackendRegistry): ForkSessionTraitRow;
|
|
9
|
+
export interface AcquiredFork {
|
|
10
|
+
/** The LIVE session: the fork handle itself (`live`), or the reattached handle (`id-only`). */
|
|
11
|
+
readonly handle: SessionHandle;
|
|
12
|
+
/** How the live handle was obtained. `load` means the agent replayed the transcript. */
|
|
13
|
+
readonly method: "fork" | "resume" | "load";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fork `sourceSessionId` on `connection` (a dedicated process; the source's own process is not
|
|
17
|
+
* involved on the wire) and hand back the live handle per `trait`:
|
|
18
|
+
* - `live`: the fork response IS the session.
|
|
19
|
+
* - `id-only`: the response names a persisted copy that is not live; release the fork handle
|
|
20
|
+
* with `keepOpen` (unregister only — no `session/close`, so the reattach's `register()` never
|
|
21
|
+
* replaces a live state and the slot count stays at one), then reattach it (resume preferred,
|
|
22
|
+
* load fallback). The catalog/modes come from the reattach response; the bare fork response is
|
|
23
|
+
* never read for them.
|
|
24
|
+
* `prepare` is re-evaluated for the reattach so it sees the same options the fork carried.
|
|
25
|
+
*/
|
|
26
|
+
export declare function acquireForkedSession(connection: PooledConnection, sourceSessionId: string, opts: AcpSessionOptions, trait: ForkSessionTraitRow): Promise<AcquiredFork>;
|
|
27
|
+
//# sourceMappingURL=fork.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.d.ts","sourceRoot":"","sources":["../../src/agent/fork.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAoB,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD;;0FAE0F;AAC1F,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,GAAG,mBAAmB,CAG7F;AAED,MAAM,WAAW,YAAY;IAC3B,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC7C;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,gBAAgB,EAC5B,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,iBAAiB,EACvB,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,YAAY,CAAC,CAOvB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { forkSessionTrait } from "../protocol-coverage.js";
|
|
2
|
+
/** A registered custom backend uses its declared `fork` trait (or the live default); a built-in
|
|
3
|
+
* uses its `FORK_SESSION_TRAITS` row. A registry lookup, never an identity branch: a custom entry
|
|
4
|
+
* named like a built-in is a different program and never inherits the built-in's row. */
|
|
5
|
+
export function forkTraitFor(backend, registry) {
|
|
6
|
+
const custom = registry.get(backend.id);
|
|
7
|
+
return custom ? forkSessionTrait(backend.id, custom.fork ?? { disposition: "live" }) : forkSessionTrait(backend.id);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Fork `sourceSessionId` on `connection` (a dedicated process; the source's own process is not
|
|
11
|
+
* involved on the wire) and hand back the live handle per `trait`:
|
|
12
|
+
* - `live`: the fork response IS the session.
|
|
13
|
+
* - `id-only`: the response names a persisted copy that is not live; release the fork handle
|
|
14
|
+
* with `keepOpen` (unregister only — no `session/close`, so the reattach's `register()` never
|
|
15
|
+
* replaces a live state and the slot count stays at one), then reattach it (resume preferred,
|
|
16
|
+
* load fallback). The catalog/modes come from the reattach response; the bare fork response is
|
|
17
|
+
* never read for them.
|
|
18
|
+
* `prepare` is re-evaluated for the reattach so it sees the same options the fork carried.
|
|
19
|
+
*/
|
|
20
|
+
export async function acquireForkedSession(connection, sourceSessionId, opts, trait) {
|
|
21
|
+
const forkHandle = await connection.forkSession(sourceSessionId, opts);
|
|
22
|
+
if (trait.disposition !== "id-only")
|
|
23
|
+
return { handle: forkHandle, method: "fork" };
|
|
24
|
+
const forkedId = forkHandle.sessionId;
|
|
25
|
+
await forkHandle.release({ keepOpen: true });
|
|
26
|
+
const { handle, method } = await connection.openPreparedReattachedSession(forkedId, () => opts);
|
|
27
|
+
return { handle, method };
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ValidateProbeRunner } from "../config-catalog.js";
|
|
2
|
+
import { type CustomBackendConfig } from "../registry.js";
|
|
3
|
+
import type { AcpAgentCatalog, AcpAgentProbeOptions } from "./types.js";
|
|
4
|
+
/** A `ValidateProbeRunner` over a dedicated connection per probe: spawn, initialize, one no-prompt
|
|
5
|
+
* session/new (+ model selection for exact specs), read the catalog, release, dispose. No
|
|
6
|
+
* `listBackends`/`defaultBackendId`/`dispose`: `probeHarnessConfig` derives the default targets
|
|
7
|
+
* from the registry itself and never owns a caller-supplied runner. */
|
|
8
|
+
export declare function createAgentProbeRunner(backends: Record<string, CustomBackendConfig> | undefined): ValidateProbeRunner;
|
|
9
|
+
/** The catalog behind `AcpAgent.probe` (see the file header). Per-target failures never throw
|
|
10
|
+
* (`probed: false` with a redacted `error`); a bad `modelFilter`, `probeTimeoutMs`, or
|
|
11
|
+
* `probeConcurrency` throws a TypeError and a malformed registry a SCRIPT_VALIDATION_ERROR — all
|
|
12
|
+
* before any process spawns. */
|
|
13
|
+
export declare function probeCatalog(options?: AcpAgentProbeOptions): Promise<AcpAgentCatalog>;
|
|
14
|
+
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/agent/probe.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAInF,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAExE;;;wEAGwE;AACxE,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,SAAS,GAAG,mBAAmB,CAuCrH;AAED;;;iCAGiC;AACjC,wBAAsB,YAAY,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,eAAe,CAAC,CA8B/F"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { PooledConnection, isChildCleanupError } from "../acp-client.js";
|
|
2
|
+
import { buildHarnessModelsView, buildModelFilter, probeHarnessConfig, } from "../config-catalog.js";
|
|
3
|
+
import { mapThrownError } from "../errors-map.js";
|
|
4
|
+
import { registryWithRunBackends } from "../registry.js";
|
|
5
|
+
import { resolveModelRoute } from "../routing.js";
|
|
6
|
+
import { releaseOnExit, retainOnExit } from "./process-registry.js";
|
|
7
|
+
import { resolveAgentRegistry } from "./routing.js";
|
|
8
|
+
/** A `ValidateProbeRunner` over a dedicated connection per probe: spawn, initialize, one no-prompt
|
|
9
|
+
* session/new (+ model selection for exact specs), read the catalog, release, dispose. No
|
|
10
|
+
* `listBackends`/`defaultBackendId`/`dispose`: `probeHarnessConfig` derives the default targets
|
|
11
|
+
* from the registry itself and never owns a caller-supplied runner. */
|
|
12
|
+
export function createAgentProbeRunner(backends) {
|
|
13
|
+
return {
|
|
14
|
+
async probeConfigOptions(spec, opts = {}) {
|
|
15
|
+
opts.signal?.throwIfAborted();
|
|
16
|
+
const registry = registryWithRunBackends(resolveAgentRegistry(backends), opts.backends);
|
|
17
|
+
const route = resolveModelRoute(spec, registry);
|
|
18
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
19
|
+
const connection = PooledConnection.create(route.backend, { onDead: () => undefined });
|
|
20
|
+
retainOnExit(connection);
|
|
21
|
+
// Unsticks a hung initialize/session-new: the raced wire call rejects with the death error.
|
|
22
|
+
const onAbort = () => {
|
|
23
|
+
void connection.dispose();
|
|
24
|
+
};
|
|
25
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
26
|
+
let handle;
|
|
27
|
+
try {
|
|
28
|
+
handle = await connection.openSession({ cwd, schema: undefined, policy: {} });
|
|
29
|
+
opts.signal?.throwIfAborted();
|
|
30
|
+
if (opts.selectModel && route.modelSpec !== undefined)
|
|
31
|
+
await handle.selectModel(route.modelSpec);
|
|
32
|
+
return {
|
|
33
|
+
backendId: route.backend.id,
|
|
34
|
+
...(route.backend.defaultModeId === undefined ? {} : { defaultModeId: route.backend.defaultModeId }),
|
|
35
|
+
options: handle.advertisedConfigOptions,
|
|
36
|
+
modes: handle.modes ?? null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
41
|
+
try {
|
|
42
|
+
await handle?.release();
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (isChildCleanupError(error)) {
|
|
46
|
+
throw mapThrownError(error, { backendId: route.backend.id, backend: route.backend });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
await connection.dispose().catch(() => undefined);
|
|
50
|
+
releaseOnExit(connection);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** The catalog behind `AcpAgent.probe` (see the file header). Per-target failures never throw
|
|
56
|
+
* (`probed: false` with a redacted `error`); a bad `modelFilter`, `probeTimeoutMs`, or
|
|
57
|
+
* `probeConcurrency` throws a TypeError and a malformed registry a SCRIPT_VALIDATION_ERROR — all
|
|
58
|
+
* before any process spawns. */
|
|
59
|
+
export async function probeCatalog(options = {}) {
|
|
60
|
+
// Validation only: a bad regex must surface BEFORE any spawn (the MCP handler does the same).
|
|
61
|
+
if (options.modelFilter !== undefined)
|
|
62
|
+
buildModelFilter(options.modelFilter);
|
|
63
|
+
// The SDK surfaces a malformed registry as a WorkflowError; probeHarnessConfig re-reads it.
|
|
64
|
+
resolveAgentRegistry(options.backends);
|
|
65
|
+
const modelSpecs = [...(options.model === undefined ? [] : [options.model]), ...(options.models ?? [])];
|
|
66
|
+
const probeRunner = createAgentProbeRunner(options.backends);
|
|
67
|
+
const common = {
|
|
68
|
+
backends: options.backends,
|
|
69
|
+
cwd: options.cwd,
|
|
70
|
+
probeTimeoutMs: options.probeTimeoutMs,
|
|
71
|
+
probeConcurrency: options.probeConcurrency,
|
|
72
|
+
signal: options.signal,
|
|
73
|
+
probeRunner,
|
|
74
|
+
};
|
|
75
|
+
let report = await probeHarnessConfig({ harnesses: options.harnesses, modelSpecs, ...common });
|
|
76
|
+
// Mirror the MCP `action:"config"` fallback: when an exact model probe fails, still return the
|
|
77
|
+
// bare harness catalog next to the failure so the caller can pick a valid id.
|
|
78
|
+
const missingCatalogBackends = [...new Set(report.harnessOptions
|
|
79
|
+
.filter((harness) => !harness.probed && harness.model !== undefined)
|
|
80
|
+
.map((harness) => harness.backendId)
|
|
81
|
+
.filter((backendId) => !report.harnessOptions.some((harness) => harness.probed && harness.backendId === backendId && harness.model === undefined)))];
|
|
82
|
+
if (missingCatalogBackends.length > 0) {
|
|
83
|
+
const catalogs = await probeHarnessConfig({ harnesses: missingCatalogBackends, ...common });
|
|
84
|
+
report = { ok: false, exitCode: 1, harnessOptions: [...report.harnessOptions, ...catalogs.harnessOptions] };
|
|
85
|
+
}
|
|
86
|
+
return { ...report, models: buildHarnessModelsView(report, options.modelFilter) };
|
|
87
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PooledConnection } from "../acp-client.js";
|
|
2
|
+
/** Register a connection for the exit hook; call right after `PooledConnection.create`. */
|
|
3
|
+
export declare function retainOnExit(connection: PooledConnection): void;
|
|
4
|
+
/** Forget a connection; call in the `finally` of every teardown path. Removes the listener with
|
|
5
|
+
* the last connection so the process never accumulates dead hooks. */
|
|
6
|
+
export declare function releaseOnExit(connection: PooledConnection): void;
|
|
7
|
+
/** Test seam: how many connections the hook currently guards. */
|
|
8
|
+
export declare function liveConnectionCount(): number;
|
|
9
|
+
//# sourceMappingURL=process-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-registry.d.ts","sourceRoot":"","sources":["../../src/agent/process-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAUzD,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAM/D;AAED;uEACuE;AACvE,wBAAgB,aAAa,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAMhE;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const live = new Set();
|
|
2
|
+
let installed = false;
|
|
3
|
+
/** `killNow()` is synchronous and idempotent — the only thing an `exit` handler may call. */
|
|
4
|
+
const onExit = () => {
|
|
5
|
+
for (const connection of live)
|
|
6
|
+
connection.killNow();
|
|
7
|
+
};
|
|
8
|
+
/** Register a connection for the exit hook; call right after `PooledConnection.create`. */
|
|
9
|
+
export function retainOnExit(connection) {
|
|
10
|
+
live.add(connection);
|
|
11
|
+
if (!installed) {
|
|
12
|
+
installed = true;
|
|
13
|
+
process.once("exit", onExit);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Forget a connection; call in the `finally` of every teardown path. Removes the listener with
|
|
17
|
+
* the last connection so the process never accumulates dead hooks. */
|
|
18
|
+
export function releaseOnExit(connection) {
|
|
19
|
+
live.delete(connection);
|
|
20
|
+
if (installed && live.size === 0) {
|
|
21
|
+
installed = false;
|
|
22
|
+
process.removeListener("exit", onExit);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Test seam: how many connections the hook currently guards. */
|
|
26
|
+
export function liveConnectionCount() {
|
|
27
|
+
return live.size;
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class SerialQueue {
|
|
2
|
+
#private;
|
|
3
|
+
/** Queued + running. */
|
|
4
|
+
get depth(): number;
|
|
5
|
+
/** Whether an op is executing right now. */
|
|
6
|
+
get running(): boolean;
|
|
7
|
+
/** Resolves once the currently running op (if any) has settled and nothing else is queued. */
|
|
8
|
+
whenIdle(): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Append `op`; it starts strictly after every earlier entry settled. A `signal` that is already
|
|
11
|
+
* aborted rejects with `signal.reason` without calling `op`; an abort while the entry is still
|
|
12
|
+
* queued removes it and rejects it (the listener is dropped the moment the entry starts — an
|
|
13
|
+
* in-flight abort is the op's own concern).
|
|
14
|
+
*/
|
|
15
|
+
run<T>(op: () => Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
16
|
+
/** Reject every NOT-YET-STARTED entry with `reason`; the running op is untouched. */
|
|
17
|
+
drain(reason: unknown): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/agent/queue.ts"],"names":[],"mappings":"AAaA,qBAAa,WAAW;;IAMtB,wBAAwB;IACxB,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,4CAA4C;IAC5C,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,8FAA8F;IAC9F,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IA0B9D,qFAAqF;IACrF,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;CAiC7B"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// The per-agent FIFO. `prompt`, `fork`, `setMode`, `setConfigOptions`, `close` and the implicit
|
|
2
|
+
// open all run through one SerialQueue so no two of them ever overlap (a second concurrent
|
|
3
|
+
// `SessionHandle.prompt` would clobber the handle's active turn; pi rejects a fork while the
|
|
4
|
+
// source has a turn in flight; Claude would copy a partial transcript). Pure: no ACP imports.
|
|
5
|
+
export class SerialQueue {
|
|
6
|
+
#entries = [];
|
|
7
|
+
#running = false;
|
|
8
|
+
#idle = Promise.resolve();
|
|
9
|
+
#resolveIdle;
|
|
10
|
+
/** Queued + running. */
|
|
11
|
+
get depth() {
|
|
12
|
+
return this.#entries.length + (this.#running ? 1 : 0);
|
|
13
|
+
}
|
|
14
|
+
/** Whether an op is executing right now. */
|
|
15
|
+
get running() {
|
|
16
|
+
return this.#running;
|
|
17
|
+
}
|
|
18
|
+
/** Resolves once the currently running op (if any) has settled and nothing else is queued. */
|
|
19
|
+
whenIdle() {
|
|
20
|
+
return this.#idle;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Append `op`; it starts strictly after every earlier entry settled. A `signal` that is already
|
|
24
|
+
* aborted rejects with `signal.reason` without calling `op`; an abort while the entry is still
|
|
25
|
+
* queued removes it and rejects it (the listener is dropped the moment the entry starts — an
|
|
26
|
+
* in-flight abort is the op's own concern).
|
|
27
|
+
*/
|
|
28
|
+
run(op, signal) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
if (signal?.aborted) {
|
|
31
|
+
reject(signal.reason);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const entry = {
|
|
35
|
+
op,
|
|
36
|
+
signal,
|
|
37
|
+
resolve: resolve,
|
|
38
|
+
reject,
|
|
39
|
+
};
|
|
40
|
+
if (signal) {
|
|
41
|
+
const onAbort = () => {
|
|
42
|
+
const index = this.#entries.indexOf(entry);
|
|
43
|
+
if (index >= 0)
|
|
44
|
+
this.#entries.splice(index, 1);
|
|
45
|
+
reject(signal.reason);
|
|
46
|
+
};
|
|
47
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
48
|
+
entry.removeAbort = () => signal.removeEventListener("abort", onAbort);
|
|
49
|
+
}
|
|
50
|
+
this.#entries.push(entry);
|
|
51
|
+
void this.#pump();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Reject every NOT-YET-STARTED entry with `reason`; the running op is untouched. */
|
|
55
|
+
drain(reason) {
|
|
56
|
+
for (const entry of this.#entries.splice(0)) {
|
|
57
|
+
entry.removeAbort?.();
|
|
58
|
+
entry.reject(reason);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async #pump() {
|
|
62
|
+
if (this.#running)
|
|
63
|
+
return;
|
|
64
|
+
this.#running = true;
|
|
65
|
+
this.#idle = new Promise((resolve) => {
|
|
66
|
+
this.#resolveIdle = resolve;
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
while (this.#entries.length > 0) {
|
|
70
|
+
const entry = this.#entries.shift();
|
|
71
|
+
entry.removeAbort?.();
|
|
72
|
+
if (entry.signal?.aborted) {
|
|
73
|
+
entry.reject(entry.signal.reason);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
entry.resolve(await entry.op());
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
entry.reject(error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
this.#running = false;
|
|
86
|
+
this.#resolveIdle?.();
|
|
87
|
+
this.#resolveIdle = undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AgentSessionRef } from "@automatalabs/shared-types";
|
|
2
|
+
import type { Backend } from "../backend.js";
|
|
3
|
+
import { type BackendRegistry, type CustomBackendConfig } from "../registry.js";
|
|
4
|
+
import { type ModelRoute } from "../routing.js";
|
|
5
|
+
import type { AcpAgentOptions } from "./types.js";
|
|
6
|
+
/** The custom-backend registry for an agent: `backends` merged over `AGENTPRISM_BACKENDS`. A
|
|
7
|
+
* malformed registry is a caller error (SCRIPT_VALIDATION_ERROR), mirroring the runner's wrap. */
|
|
8
|
+
export declare function resolveAgentRegistry(backends: Record<string, CustomBackendConfig> | undefined, label?: string): BackendRegistry;
|
|
9
|
+
/** The runner's routing grammar applied to `new AcpAgent({ model })`. */
|
|
10
|
+
export declare function resolveAgentRoute(options: Pick<AcpAgentOptions, "model">, registry: BackendRegistry): ModelRoute;
|
|
11
|
+
/**
|
|
12
|
+
* Route a cold reopen from a session ref. `ref.backendId` must name a registered custom backend
|
|
13
|
+
* (wins, like `resolveModelRoute`) or a built-in — NEVER the default backend; the ref's `poolKey`
|
|
14
|
+
* must match the currently resolved `poolKey ?? id` (the runner's silent `backend-mismatch` skip,
|
|
15
|
+
* made loud); an optional `model` must stay on the ref's backend: `<ref.backendId>/<inner>` strips
|
|
16
|
+
* the prefix, a spec routing to another known backend is rejected, and an unrouted spec goes
|
|
17
|
+
* VERBATIM to the ref's backend (the same unrouted rule as the runner, applied to this backend).
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveRefRoute(ref: AgentSessionRef, model: string | undefined, registry: BackendRegistry, label?: string): ModelRoute;
|
|
20
|
+
/** A fresh `Backend` instance with the same identity as `backend` (a registered name wins, as in
|
|
21
|
+
* routing; else the built-in of that id). A fork child must own its own instance — pooling identity
|
|
22
|
+
* is `poolKey ?? id`, never the object. */
|
|
23
|
+
export declare function freshBackendFor(backend: Backend, registry: BackendRegistry): Backend;
|
|
24
|
+
/** Stricter than the runner's interactive check and equal to the Claude adapter's own: absolute,
|
|
25
|
+
* existing, and a directory — so the failure happens before a process spawns. */
|
|
26
|
+
export declare function validateAgentCwd(cwd: unknown, label: string | undefined, method: string): asserts cwd is string;
|
|
27
|
+
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/agent/routing.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAA0B,KAAK,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACxG,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAEnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;mGACmG;AACnG,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,SAAS,EACzD,KAAK,CAAC,EAAE,MAAM,GACb,eAAe,CAMjB;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,eAAe,GAAG,UAAU,CAEhH;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,EAAE,eAAe,EACzB,KAAK,CAAC,EAAE,MAAM,GACb,UAAU,CA4BZ;AAED;;4CAE4C;AAC5C,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAEpF;AAQD;kFACkF;AAClF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,MAAM,CAiB/G"}
|