@gajae-code/coding-agent 0.12.6 → 0.12.8
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/CHANGELOG.md +22 -1
- package/dist/types/async/job-manager.d.ts +36 -1
- package/dist/types/commands/notify.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +3 -0
- package/dist/types/config/model-discovery-manager.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/config/resolve-config-value.d.ts +2 -1
- package/dist/types/config/settings-schema.d.ts +17 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +8 -10
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
- package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/hook-selector.d.ts +20 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
- package/dist/types/sdk/broker/broker.d.ts +5 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -3
- package/dist/types/sdk/bus/config.d.ts +75 -113
- package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
- package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
- package/dist/types/sdk/bus/index.d.ts +1 -1
- package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
- package/dist/types/sdk/bus/notification-service.d.ts +31 -4
- package/dist/types/sdk/bus/session-control.d.ts +8 -3
- package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
- package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
- package/dist/types/sdk/client/discovery.d.ts +2 -1
- package/dist/types/sdk/host/host.d.ts +2 -23
- package/dist/types/sdk/host/query/handlers.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/providers.d.ts +22 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/session/artifacts.d.ts +5 -8
- package/dist/types/session/client-bridge.d.ts +0 -14
- package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +11 -4
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -0
- package/dist/types/session/internal/session-open-errors.d.ts +5 -0
- package/dist/types/session/messages.d.ts +1 -4
- package/dist/types/session/session-manager.d.ts +7 -6
- package/dist/types/session/session-storage.d.ts +2 -0
- package/dist/types/session/streaming-output.d.ts +0 -32
- package/dist/types/task/receipt.d.ts +1 -2
- package/dist/types/task/types.d.ts +43 -178
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +0 -3
- package/dist/types/tools/output-meta.d.ts +5 -37
- package/dist/types/tools/subagent.d.ts +15 -0
- package/dist/types/tools/todo-write.d.ts +3 -0
- package/dist/types/tools/tool-result.d.ts +0 -1
- package/package.json +10 -8
- package/scripts/build-sdk-package-smoke.ts +51 -0
- package/scripts/capture-notifications-settings-showcase.ts +1 -1
- package/src/async/job-manager.ts +430 -46
- package/src/cli/notify-cli.ts +204 -94
- package/src/commands/notify.ts +19 -8
- package/src/config/file-lock.ts +6 -0
- package/src/config/model-discovery-manager.ts +50 -8
- package/src/config/model-registry.ts +862 -75
- package/src/config/resolve-config-value.ts +10 -8
- package/src/config/settings-schema.ts +25 -15
- package/src/config/settings.ts +15 -15
- package/src/cursor.ts +13 -20
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +151 -425
- package/src/gjc-runtime/session-state-sidecar.ts +12 -2
- package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
- package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
- package/src/gjc-runtime/workflow-manifest.ts +0 -12
- package/src/internal-urls/agent-protocol.ts +38 -98
- package/src/internal-urls/artifact-protocol.ts +17 -28
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/registry-helpers.ts +0 -62
- package/src/main.ts +43 -7
- package/src/modes/DESIGN.md +11 -11
- package/src/modes/acp/acp-agent.ts +0 -24
- package/src/modes/acp/acp-client-bridge.ts +11 -154
- package/src/modes/components/hook-selector.ts +155 -44
- package/src/modes/components/model-selector.ts +242 -49
- package/src/modes/components/notifications-settings-editor.ts +631 -85
- package/src/modes/controllers/extension-ui-controller.ts +114 -15
- package/src/modes/controllers/selector-controller.ts +464 -50
- package/src/prompts/tools/bash.md +2 -2
- package/src/sdk/acp/adapter.ts +9 -200
- package/src/sdk/broker/broker.ts +54 -10
- package/src/sdk/broker/lifecycle-ledger.ts +34 -1
- package/src/sdk/broker/lifecycle.ts +47 -50
- package/src/sdk/bus/chat-daemon-cli.ts +12 -5
- package/src/sdk/bus/chat-daemon-control.ts +35 -8
- package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
- package/src/sdk/bus/config.ts +534 -286
- package/src/sdk/bus/discord-live-provider.ts +57 -2
- package/src/sdk/bus/discord-provider.ts +21 -0
- package/src/sdk/bus/index.ts +105 -359
- package/src/sdk/bus/notification-orchestration.ts +371 -68
- package/src/sdk/bus/notification-service.ts +431 -130
- package/src/sdk/bus/session-control.ts +119 -44
- package/src/sdk/bus/slack-live-provider.ts +78 -2
- package/src/sdk/bus/slack-provider.ts +24 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
- package/src/sdk/bus/telegram-daemon-control.ts +3 -3
- package/src/sdk/bus/telegram-daemon.ts +23 -15
- package/src/sdk/client/discovery.ts +10 -4
- package/src/sdk/host/host.ts +9 -187
- package/src/sdk/host/query/handlers.ts +20 -1
- package/src/sdk/host/reverse-leases.ts +42 -249
- package/src/sdk/index.ts +1 -0
- package/src/sdk/protocol/operation-inventory.generated.json +18 -0
- package/src/sdk/protocol/operation-registry.ts +5 -2
- package/src/sdk/providers.ts +51 -0
- package/src/sdk/session-directory.ts +1 -0
- package/src/sdk/session.ts +10 -7
- package/src/session/agent-session.ts +59 -202
- package/src/session/artifacts.ts +74 -211
- package/src/session/client-bridge.ts +0 -15
- package/src/session/internal/managed-session-scope.ts +981 -450
- package/src/session/internal/managed-session-storage.ts +362 -124
- package/src/session/internal/native-publish-outcome.ts +3 -1
- package/src/session/internal/session-open-errors.ts +11 -0
- package/src/session/messages.ts +7 -32
- package/src/session/session-manager.ts +48 -15
- package/src/session/session-storage.ts +72 -5
- package/src/session/streaming-output.ts +40 -198
- package/src/slash-commands/builtin-registry.ts +82 -4
- package/src/task/index.ts +237 -65
- package/src/task/receipt.ts +2 -4
- package/src/task/render.ts +0 -30
- package/src/task/types.ts +10 -34
- package/src/tools/ask.ts +1 -1
- package/src/tools/bash.ts +239 -818
- package/src/tools/eval.ts +4 -99
- package/src/tools/fetch.ts +15 -77
- package/src/tools/find.ts +3 -7
- package/src/tools/output-meta.ts +114 -696
- package/src/tools/search.ts +4 -9
- package/src/tools/subagent.ts +97 -75
- package/src/tools/telegram-send.ts +4 -2
- package/src/tools/todo-write.ts +47 -0
- package/src/tools/tool-result.ts +0 -1
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
- package/dist/types/utils/artifact-id.d.ts +0 -2
- package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
- package/src/utils/artifact-id.ts +0 -6
package/src/sdk/host/host.ts
CHANGED
|
@@ -4,21 +4,11 @@ import type { BrokerIndexWriter, HostEndpointAdapters, SdkFrame } from "./types"
|
|
|
4
4
|
|
|
5
5
|
export type SdkRequestObserver = (kind: "control" | "query", connectionId: string, frame: SdkFrame) => void;
|
|
6
6
|
|
|
7
|
-
export interface ProviderLeaseIdentity {
|
|
8
|
-
leaseId: string;
|
|
9
|
-
connectionId: string;
|
|
10
|
-
connectionGeneration: number;
|
|
11
|
-
fence: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type ProviderLeaseFence = (identity: ProviderLeaseIdentity, reason?: string) => boolean;
|
|
15
|
-
|
|
16
7
|
export interface SessionSdkHostOptions extends HostEndpointAdapters {
|
|
17
8
|
control?: (connectionId: string, frame: SdkFrame) => unknown | Promise<unknown>;
|
|
18
9
|
query?: (connectionId: string, frame: SdkFrame) => unknown | Promise<unknown>;
|
|
19
10
|
/** Best-effort diagnostic observation of accepted control/query frames. */
|
|
20
11
|
onRequest?: SdkRequestObserver;
|
|
21
|
-
onFrameOverflow?: (connectionId: string) => void;
|
|
22
12
|
/** Runs before a control response is sent; identity transitions use sendTerminal. */
|
|
23
13
|
beforeControlResponse?: (
|
|
24
14
|
connectionId: string,
|
|
@@ -28,25 +18,13 @@ export interface SessionSdkHostOptions extends HostEndpointAdapters {
|
|
|
28
18
|
) => void | Promise<void>;
|
|
29
19
|
/** Runs only after a successful control response has been sent to the client. */
|
|
30
20
|
afterControlResponse?: (connectionId: string, request: SdkFrame, response: SdkFrame) => void | Promise<void>;
|
|
31
|
-
|
|
32
|
-
installProviderDefinitions?: (
|
|
33
|
-
capability: string,
|
|
34
|
-
definitions: unknown,
|
|
35
|
-
leaseId: string,
|
|
36
|
-
connectionId: string,
|
|
37
|
-
connectionGeneration: number,
|
|
38
|
-
) => void;
|
|
21
|
+
installProviderDefinitions?: (capability: string, definitions: unknown) => void;
|
|
39
22
|
onProviderDefinitionsRemoved?: (capability: string) => void;
|
|
40
23
|
onReverseCancel?: (requestId: string, reason: "provider_disconnected" | "lease_released") => void;
|
|
41
24
|
/** Best-effort capabilities mirrored from the native transport for out-of-band consumers. */
|
|
42
25
|
connectionCapabilities?: (connectionId: string) => ReadonlySet<string> | undefined;
|
|
43
26
|
}
|
|
44
27
|
|
|
45
|
-
/** Bound asynchronous frame admission per transport connection. */
|
|
46
|
-
const MAX_PENDING_FRAMES_PER_CONNECTION = 32;
|
|
47
|
-
const MAX_QUARANTINED_PROVIDER_LEASES = 256;
|
|
48
|
-
const MAX_RETAINED_CLOSED_CONNECTIONS = 256;
|
|
49
|
-
|
|
50
28
|
const TOOL_ACTIVITY_CAPABILITY = "tool_activity_v2";
|
|
51
29
|
const CAP_GATED_FRAME_KINDS = new Set(["tool_activity", "reasoning_summary"]);
|
|
52
30
|
const EMPTY_CAPABILITIES: ReadonlySet<string> = new Set();
|
|
@@ -142,12 +120,6 @@ export class SessionSdkHost {
|
|
|
142
120
|
#stopPromise?: Promise<"stopped">;
|
|
143
121
|
#unsubscribe?: () => void;
|
|
144
122
|
#registration?: { writer: BrokerIndexWriter; generation: number };
|
|
145
|
-
#connectionGenerations = new Map<string, number>();
|
|
146
|
-
#openConnections = new Set<string>();
|
|
147
|
-
#pendingFrames = new Map<string, { generation: number; count: number }>();
|
|
148
|
-
#leaseRecords = new Map<string, { capability: string; connectionId: string; connectionGeneration: number }>();
|
|
149
|
-
#quarantinedLeases = new Map<string, ProviderLeaseIdentity>();
|
|
150
|
-
#closedConnectionOrder: string[] = [];
|
|
151
123
|
|
|
152
124
|
constructor(options: SessionSdkHostOptions) {
|
|
153
125
|
this.#options = options;
|
|
@@ -169,120 +141,9 @@ export class SessionSdkHost {
|
|
|
169
141
|
getProviderDefinitions(capability: string): unknown | undefined {
|
|
170
142
|
return this.reverse.getInstalledDefinitions(capability);
|
|
171
143
|
}
|
|
172
|
-
/** Mark a transport connection live and return its stable generation. */
|
|
173
|
-
handleConnect(connectionId: string): number {
|
|
174
|
-
if (!this.#openConnections.has(connectionId)) {
|
|
175
|
-
const generation = this.#connectionGenerations.get(connectionId) ?? 1;
|
|
176
|
-
this.#connectionGenerations.set(connectionId, generation);
|
|
177
|
-
this.#openConnections.add(connectionId);
|
|
178
|
-
this.#pendingFrames.set(connectionId, { generation, count: 0 });
|
|
179
|
-
}
|
|
180
|
-
return this.#connectionGenerations.get(connectionId) ?? 0;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** Current generation for a transport connection, if it has been observed. */
|
|
184
|
-
connectionGeneration(connectionId: string): number {
|
|
185
|
-
return this.#connectionGenerations.get(connectionId) ?? 0;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
isConnectionOpen(connectionId: string, generation?: number): boolean {
|
|
189
|
-
return (
|
|
190
|
-
this.#openConnections.has(connectionId) &&
|
|
191
|
-
(generation === undefined || this.#connectionGenerations.get(connectionId) === generation)
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
144
|
/** Release reverse leases after the transport reports a WebSocket disconnect. */
|
|
196
145
|
handleDisconnect(connectionId: string): void {
|
|
197
|
-
if (!this.#openConnections.delete(connectionId)) return;
|
|
198
|
-
this.#connectionGenerations.set(connectionId, (this.#connectionGenerations.get(connectionId) ?? 0) + 1);
|
|
199
|
-
this.#pendingFrames.delete(connectionId);
|
|
200
|
-
for (const [leaseId, record] of this.#leaseRecords) {
|
|
201
|
-
if (record.connectionId === connectionId) this.#leaseRecords.delete(leaseId);
|
|
202
|
-
}
|
|
203
146
|
this.reverse.disconnect(connectionId);
|
|
204
|
-
this.#closedConnectionOrder.push(connectionId);
|
|
205
|
-
while (this.#closedConnectionOrder.length > MAX_RETAINED_CLOSED_CONNECTIONS) {
|
|
206
|
-
const retired = this.#closedConnectionOrder.shift();
|
|
207
|
-
if (retired && !this.#openConnections.has(retired)) this.#connectionGenerations.delete(retired);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Fences one exact provider lease after uncertain terminal cleanup. The fence
|
|
213
|
-
* is idempotent for the same identity and never affects a newer generation.
|
|
214
|
-
*/
|
|
215
|
-
quarantineProviderLease(identity: ProviderLeaseIdentity, _reason?: string): boolean {
|
|
216
|
-
if (
|
|
217
|
-
!identity ||
|
|
218
|
-
typeof identity.leaseId !== "string" ||
|
|
219
|
-
identity.leaseId.length === 0 ||
|
|
220
|
-
typeof identity.connectionId !== "string" ||
|
|
221
|
-
identity.connectionId.length === 0 ||
|
|
222
|
-
!Number.isSafeInteger(identity.connectionGeneration) ||
|
|
223
|
-
identity.connectionGeneration < 0 ||
|
|
224
|
-
typeof identity.fence !== "string" ||
|
|
225
|
-
identity.fence.length === 0
|
|
226
|
-
)
|
|
227
|
-
return false;
|
|
228
|
-
const previous = this.#quarantinedLeases.get(identity.leaseId);
|
|
229
|
-
if (
|
|
230
|
-
previous &&
|
|
231
|
-
previous.connectionId === identity.connectionId &&
|
|
232
|
-
previous.connectionGeneration === identity.connectionGeneration &&
|
|
233
|
-
previous.fence === identity.fence
|
|
234
|
-
)
|
|
235
|
-
return true;
|
|
236
|
-
if (this.#quarantinedLeases.size >= MAX_QUARANTINED_PROVIDER_LEASES) return false;
|
|
237
|
-
if (this.#connectionGenerations.get(identity.connectionId) !== identity.connectionGeneration) return false;
|
|
238
|
-
const record = this.#leaseRecords.get(identity.leaseId);
|
|
239
|
-
if (
|
|
240
|
-
!record ||
|
|
241
|
-
record.connectionId !== identity.connectionId ||
|
|
242
|
-
record.connectionGeneration !== identity.connectionGeneration
|
|
243
|
-
)
|
|
244
|
-
return false;
|
|
245
|
-
const lease = this.reverse.getLease(record.capability);
|
|
246
|
-
if (!lease || lease.leaseId !== identity.leaseId || lease.connectionId !== identity.connectionId || !lease.active)
|
|
247
|
-
return false;
|
|
248
|
-
try {
|
|
249
|
-
this.reverse.release(identity.connectionId, identity.leaseId);
|
|
250
|
-
} catch {
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
253
|
-
this.#quarantinedLeases.set(identity.leaseId, { ...identity });
|
|
254
|
-
return true;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/** Dispatch a frame through bounded per-connection asynchronous admission. */
|
|
258
|
-
dispatchFrame(connectionId: string, frame: SdkFrame): Promise<void> {
|
|
259
|
-
const generation = this.#connectionGenerations.has(connectionId)
|
|
260
|
-
? (this.#connectionGenerations.get(connectionId) ?? 0)
|
|
261
|
-
: this.handleConnect(connectionId);
|
|
262
|
-
const pending = this.#pendingFrames.get(connectionId);
|
|
263
|
-
const pendingCount = pending?.generation === generation ? pending.count : 0;
|
|
264
|
-
if (pendingCount >= MAX_PENDING_FRAMES_PER_CONNECTION) {
|
|
265
|
-
this.#options.onFrameOverflow?.(connectionId);
|
|
266
|
-
return Promise.resolve();
|
|
267
|
-
}
|
|
268
|
-
this.#pendingFrames.set(connectionId, { generation, count: pendingCount + 1 });
|
|
269
|
-
const task = Promise.resolve().then(() => {
|
|
270
|
-
if (this.#connectionGenerations.get(connectionId) !== generation) return;
|
|
271
|
-
if (!this.#openConnections.has(connectionId)) return;
|
|
272
|
-
return this.#onFrame(connectionId, frame, generation);
|
|
273
|
-
});
|
|
274
|
-
void task.finally(() => {
|
|
275
|
-
const current = this.#pendingFrames.get(connectionId);
|
|
276
|
-
if (!current || current.generation !== generation) return;
|
|
277
|
-
this.#pendingFrames.set(connectionId, {
|
|
278
|
-
generation,
|
|
279
|
-
count: Math.max(0, current.count - 1),
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
return task.then(
|
|
283
|
-
() => {},
|
|
284
|
-
() => {},
|
|
285
|
-
);
|
|
286
147
|
}
|
|
287
148
|
|
|
288
149
|
/** Adds an event to the resumable event ring. Transport delivery is owned by bus wiring. */
|
|
@@ -295,7 +156,7 @@ export class SessionSdkHost {
|
|
|
295
156
|
this.events.restart();
|
|
296
157
|
this.emitEvent({ name: "session_ready", sessionId: this.#options.sessionId, generation: this.events.generation });
|
|
297
158
|
const disposer = this.#options.onFrame((connectionId, frame) => {
|
|
298
|
-
void this
|
|
159
|
+
void this.#onFrame(connectionId, frame);
|
|
299
160
|
});
|
|
300
161
|
this.#unsubscribe = typeof disposer === "function" ? disposer : undefined;
|
|
301
162
|
this.#started = true;
|
|
@@ -360,30 +221,18 @@ export class SessionSdkHost {
|
|
|
360
221
|
}
|
|
361
222
|
}
|
|
362
223
|
|
|
363
|
-
async #onFrame(connectionId: string, frame: SdkFrame
|
|
364
|
-
if (this.#connectionGenerations.has(connectionId) && this.#connectionGenerations.get(connectionId) !== generation)
|
|
365
|
-
return;
|
|
224
|
+
async #onFrame(connectionId: string, frame: SdkFrame): Promise<void> {
|
|
366
225
|
try {
|
|
367
226
|
switch (frame.type) {
|
|
368
227
|
case "control_request": {
|
|
369
228
|
this.#observeRequest("control", connectionId, frame);
|
|
370
229
|
const result = await this.#options.control?.(connectionId, frame);
|
|
371
|
-
if (
|
|
372
|
-
this.#connectionGenerations.has(connectionId) &&
|
|
373
|
-
this.#connectionGenerations.get(connectionId) !== generation
|
|
374
|
-
)
|
|
375
|
-
return;
|
|
376
230
|
if (result !== undefined) {
|
|
377
231
|
const response = { type: "control_response", ...(result as SdkFrame) };
|
|
378
232
|
let terminalSent = false;
|
|
379
233
|
const sendTerminal = async (): Promise<void> => {
|
|
380
234
|
if (terminalSent) return;
|
|
381
235
|
terminalSent = true;
|
|
382
|
-
if (
|
|
383
|
-
this.#connectionGenerations.has(connectionId) &&
|
|
384
|
-
this.#connectionGenerations.get(connectionId) !== generation
|
|
385
|
-
)
|
|
386
|
-
return;
|
|
387
236
|
await this.#send(connectionId, response);
|
|
388
237
|
};
|
|
389
238
|
await this.#options.beforeControlResponse?.(connectionId, frame, response, sendTerminal);
|
|
@@ -422,11 +271,6 @@ export class SessionSdkHost {
|
|
|
422
271
|
case "query_request": {
|
|
423
272
|
this.#observeRequest("query", connectionId, frame);
|
|
424
273
|
const result = await this.#options.query?.(connectionId, frame);
|
|
425
|
-
if (
|
|
426
|
-
this.#connectionGenerations.has(connectionId) &&
|
|
427
|
-
this.#connectionGenerations.get(connectionId) !== generation
|
|
428
|
-
)
|
|
429
|
-
return;
|
|
430
274
|
if (result !== undefined)
|
|
431
275
|
await this.#send(connectionId, { type: "query_response", ...(result as SdkFrame) });
|
|
432
276
|
break;
|
|
@@ -436,30 +280,13 @@ export class SessionSdkHost {
|
|
|
436
280
|
requireConnection(connectionId, frame);
|
|
437
281
|
const capability = requiredString(frame, "capability");
|
|
438
282
|
if (!has(frame, "definitions")) throw invalidFrame("definitions is required.");
|
|
439
|
-
const expectedLeaseId = optionalString(frame, "expectedLeaseId");
|
|
440
|
-
if (expectedLeaseId && this.#quarantinedLeases.has(expectedLeaseId))
|
|
441
|
-
throw new ReverseLeaseError("lease_unavailable");
|
|
442
|
-
const connectionGeneration = this.#connectionGenerations.get(connectionId) ?? 0;
|
|
443
283
|
const lease = this.reverse.registerProvider(
|
|
444
284
|
connectionId,
|
|
445
285
|
capability,
|
|
446
286
|
frame.definitions,
|
|
447
|
-
expectedLeaseId,
|
|
287
|
+
optionalString(frame, "expectedLeaseId"),
|
|
448
288
|
optionalString(frame, "idempotencyKey"),
|
|
449
|
-
connectionGeneration,
|
|
450
289
|
);
|
|
451
|
-
for (const [priorLeaseId, record] of this.#leaseRecords) {
|
|
452
|
-
if (record.capability === lease.capability && priorLeaseId !== lease.leaseId)
|
|
453
|
-
this.#leaseRecords.delete(priorLeaseId);
|
|
454
|
-
}
|
|
455
|
-
this.#leaseRecords.set(lease.leaseId, {
|
|
456
|
-
capability: lease.capability,
|
|
457
|
-
connectionId: lease.connectionId,
|
|
458
|
-
connectionGeneration,
|
|
459
|
-
});
|
|
460
|
-
try {
|
|
461
|
-
this.#options.onProviderLeaseRegistered?.(lease, connectionGeneration);
|
|
462
|
-
} catch {}
|
|
463
290
|
await this.#send(connectionId, {
|
|
464
291
|
id: frame.id,
|
|
465
292
|
type: "register_provider_result",
|
|
@@ -479,7 +306,6 @@ export class SessionSdkHost {
|
|
|
479
306
|
requireConnection(connectionId, frame);
|
|
480
307
|
const handoffTo = optionalString(frame, "handoffTo");
|
|
481
308
|
const lease = this.reverse.release(connectionId, requiredString(frame, "leaseId"), handoffTo);
|
|
482
|
-
this.#leaseRecords.delete(lease.leaseId);
|
|
483
309
|
await this.#send(connectionId, leaseState(undefined, lease));
|
|
484
310
|
break;
|
|
485
311
|
}
|
|
@@ -492,7 +318,7 @@ export class SessionSdkHost {
|
|
|
492
318
|
if (frame.ok) {
|
|
493
319
|
if (!has(frame, "result") || has(frame, "error"))
|
|
494
320
|
throw invalidFrame("Successful reverse responses require result and no error.");
|
|
495
|
-
this.reverse.respond(connectionId, id, leaseId, frame.result
|
|
321
|
+
this.reverse.respond(connectionId, id, leaseId, frame.result);
|
|
496
322
|
} else {
|
|
497
323
|
if (
|
|
498
324
|
has(frame, "result") ||
|
|
@@ -501,14 +327,10 @@ export class SessionSdkHost {
|
|
|
501
327
|
typeof responseError.message !== "string"
|
|
502
328
|
)
|
|
503
329
|
throw invalidFrame("Failed reverse responses require a structured error and no result.");
|
|
504
|
-
this.reverse.respond(
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
undefined,
|
|
509
|
-
{ code: responseError.code, message: responseError.message },
|
|
510
|
-
frame,
|
|
511
|
-
);
|
|
330
|
+
this.reverse.respond(connectionId, id, leaseId, undefined, {
|
|
331
|
+
code: responseError.code,
|
|
332
|
+
message: responseError.message,
|
|
333
|
+
});
|
|
512
334
|
}
|
|
513
335
|
break;
|
|
514
336
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { PROMPT_CLIENT_REF_MAX_LENGTH } from "../../prompt-status.js";
|
|
2
|
+
import type { ActiveProviderDescriptor } from "../../providers.js";
|
|
3
|
+
import { ActiveProviderResolutionError } from "../../providers.js";
|
|
2
4
|
import {
|
|
3
5
|
assertCursorSelector,
|
|
4
6
|
type CursorEnvelope,
|
|
@@ -22,6 +24,7 @@ export interface SessionSurface {
|
|
|
22
24
|
getUsage(): unknown | Promise<unknown>;
|
|
23
25
|
getModels(): unknown | Promise<unknown>;
|
|
24
26
|
getSkillState(): unknown | Promise<unknown>;
|
|
27
|
+
getActiveProviders?(): ActiveProviderDescriptor[] | Promise<ActiveProviderDescriptor[]>;
|
|
25
28
|
/** Q12 rows preserve workflow gate fields and include stable durable gate metadata. */
|
|
26
29
|
getGates(): unknown | Promise<unknown>;
|
|
27
30
|
getConfigItems(): unknown | Promise<unknown>;
|
|
@@ -102,6 +105,7 @@ const sources: Record<string, { resource: string; method: keyof SessionSurface;
|
|
|
102
105
|
Q21: { resource: "queue", method: "getQueueMessages", mvcc: true },
|
|
103
106
|
Q22: { resource: "extensions", method: "getExtensions", mvcc: true },
|
|
104
107
|
Q25: { resource: "jobs", method: "getJobs", mvcc: false },
|
|
108
|
+
Q29: { resource: "activeProviders", method: "getActiveProviders", mvcc: false },
|
|
105
109
|
Q27: { resource: "modelProfiles", method: "getModelProfiles", mvcc: true },
|
|
106
110
|
};
|
|
107
111
|
const names = [
|
|
@@ -133,6 +137,7 @@ const names = [
|
|
|
133
137
|
"turn.prompt_status",
|
|
134
138
|
"models.profiles.list",
|
|
135
139
|
"skill.invoke_status",
|
|
140
|
+
"providers.list/active",
|
|
136
141
|
];
|
|
137
142
|
|
|
138
143
|
export class QueryHandlers {
|
|
@@ -168,6 +173,15 @@ export class QueryHandlers {
|
|
|
168
173
|
return this.#error(request, "invalid_request", false, "models.profiles.list does not accept input fields.");
|
|
169
174
|
if (query === "Q27" && typeof this.surface.getModelProfiles !== "function")
|
|
170
175
|
return this.#error(request, "unavailable", false, "models.profiles.list is unavailable for this session.");
|
|
176
|
+
if (query === "Q29" && request.input && Object.keys(request.input).length > 0)
|
|
177
|
+
return this.#error(
|
|
178
|
+
request,
|
|
179
|
+
"invalid_request",
|
|
180
|
+
false,
|
|
181
|
+
"providers.list/active does not accept input fields.",
|
|
182
|
+
);
|
|
183
|
+
if (query === "Q29" && typeof this.surface.getActiveProviders !== "function")
|
|
184
|
+
return this.#error(request, "unavailable", false, "providers.list/active is unavailable for this session.");
|
|
171
185
|
const source = sources[query];
|
|
172
186
|
if (!source) return this.#error(request, "invalid_request");
|
|
173
187
|
return await this.#pageSource(request, query, source);
|
|
@@ -257,7 +271,12 @@ export class QueryHandlers {
|
|
|
257
271
|
source.resource === "transcript" ? { highWatermark: cursor.highWatermark } : {},
|
|
258
272
|
);
|
|
259
273
|
} else {
|
|
260
|
-
|
|
274
|
+
try {
|
|
275
|
+
snapshot = await (this.surface[source.method] as () => unknown)();
|
|
276
|
+
} catch (error) {
|
|
277
|
+
if (queryId === "Q29") throw new ActiveProviderResolutionError();
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
261
280
|
revision = await this.revisions.createRevision(source.resource, resourceId, snapshot);
|
|
262
281
|
}
|
|
263
282
|
if (snapshot === undefined) return this.#error(request, "resource_gone");
|