@gajae-code/coding-agent 0.15.6 → 0.16.1
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 +99 -0
- package/dist/types/async/job-manager.d.ts +32 -1
- package/dist/types/browser-backend.d.ts +12 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +14 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/sdk.d.ts +2 -0
- package/dist/types/config/autorouting-tier-map.d.ts +24 -0
- package/dist/types/config/billing-path.d.ts +25 -0
- package/dist/types/config/config-schema-version.d.ts +13 -1
- package/dist/types/config/file-lock.d.ts +5 -0
- package/dist/types/config/model-registry.d.ts +22 -0
- package/dist/types/config/settings-schema.d.ts +27 -8
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -3
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/acp/acp-agent.d.ts +21 -21
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/interactive-mode.d.ts +16 -2
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +79 -2
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +40 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
- package/dist/types/sdk/broker/state-version.d.ts +21 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/index.d.ts +9 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +12 -5
- package/dist/types/sdk/cli/session-cli.d.ts +20 -3
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/events.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/query/cursor.d.ts +4 -2
- package/dist/types/sdk/host/query/handlers.d.ts +1 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +70 -17
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +3 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
- package/dist/types/sdk/receipt-state.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +6 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/sdk/turn-result.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +52 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +29 -2
- package/dist/types/session/session-manager.d.ts +79 -2
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +16 -0
- package/dist/types/tools/browser/launch.d.ts +10 -0
- package/dist/types/tools/browser/registry.d.ts +8 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -10
- package/dist/types/tools/job.d.ts +3 -1
- package/dist/types/utils/external-editor.d.ts +7 -0
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/pirate.ts +7 -4
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +13 -1
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +107 -9
- package/src/browser-backend.ts +29 -0
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/plugin-cli.ts +29 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +140 -23
- package/src/commands/launch.ts +86 -18
- package/src/commands/sdk.ts +135 -28
- package/src/config/autorouting-tier-map.ts +12 -0
- package/src/config/billing-path.ts +42 -0
- package/src/config/config-schema-version.ts +18 -1
- package/src/config/file-lock.ts +158 -83
- package/src/config/model-preset-registry.ts +178 -23
- package/src/config/model-profiles.ts +5 -5
- package/src/config/model-registry.ts +291 -42
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +27 -8
- package/src/config/settings.ts +51 -15
- package/src/coordinator-mcp/projection-scan.ts +27 -6
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +115 -21
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +27 -0
- package/src/extensibility/extensions/types.ts +8 -3
- package/src/extensibility/gjc-plugins/installer.ts +51 -11
- package/src/extensibility/runtime-skill-discovery.ts +110 -3
- package/src/extensibility/skills.ts +22 -12
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/launch-worktree.ts +285 -4
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +213 -14
- package/src/gjc-runtime/session-state-sidecar.ts +938 -39
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/main.ts +61 -3
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +610 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/model-selector.ts +76 -8
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/session-selector.ts +23 -15
- package/src/modes/components/sessions-dashboard.ts +8 -5
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +23 -9
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +30 -4
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +30 -2
- package/src/modes/controllers/selector-controller.ts +200 -98
- package/src/modes/interactive-mode.ts +36 -12
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/prompt-action-autocomplete.ts +30 -4
- package/src/modes/runtime-init.ts +12 -9
- package/src/modes/theme/theme.ts +5 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/ui-helpers.ts +22 -3
- package/src/prompts/agent-fragments/browser-aside.md +22 -0
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/ask.md +1 -1
- package/src/prompts/tools/bash.md +1 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +5 -0
- package/src/sdk/broker/broker.ts +1646 -27
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +81 -3
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle-ledger.ts +6 -0
- package/src/sdk/broker/lifecycle.ts +440 -75
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +591 -0
- package/src/sdk/broker/startup-budget.ts +144 -2
- package/src/sdk/broker/state-version.ts +43 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +136 -18
- package/src/sdk/bus/discord-daemon.ts +50 -10
- package/src/sdk/bus/index.ts +84 -7
- package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
- package/src/sdk/bus/lifecycle-commands.ts +4 -0
- package/src/sdk/bus/notification-service.ts +22 -5
- package/src/sdk/bus/prompt-reconciliation.ts +7 -2
- package/src/sdk/bus/reconciliation-store.ts +55 -31
- package/src/sdk/bus/slack-conversation.ts +1 -1
- package/src/sdk/bus/slack-daemon.ts +72 -38
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +41 -15
- package/src/sdk/cli/session-cli.ts +297 -26
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/host/control/dispatch.ts +101 -40
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/events.ts +1 -0
- package/src/sdk/host/host.ts +29 -2
- package/src/sdk/host/query/cursor.ts +52 -7
- package/src/sdk/host/query/handlers.ts +33 -12
- package/src/sdk/host/query/revision-store.ts +12 -0
- package/src/sdk/host/session-runtime.test.ts +673 -14
- package/src/sdk/host/session-runtime.ts +674 -103
- package/src/sdk/host/surface-policy.ts +3 -3
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +17 -4
- package/src/sdk/prompt-deadline-manager.ts +2 -1
- package/src/sdk/protocol/operation-inventory.generated.json +105 -3
- package/src/sdk/protocol/operation-registry.ts +14 -4
- package/src/sdk/receipt-state.ts +10 -0
- package/src/sdk/router/session-router.ts +219 -118
- package/src/sdk/session.ts +416 -195
- package/src/sdk/startup-capability.ts +1 -0
- package/src/sdk/turn-result.ts +22 -0
- package/src/session/agent-session.ts +1628 -318
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +40 -3
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/session-manager.ts +850 -97
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/slash-commands/builtin-registry.ts +41 -0
- package/src/task/executor.ts +9 -4
- package/src/telemetry/events.ts +1138 -12
- package/src/tools/ask-contract.test.ts +64 -0
- package/src/tools/ask-contract.ts +36 -1
- package/src/tools/ask.ts +10 -8
- package/src/tools/bash-interactive.ts +11 -2
- package/src/tools/bash.ts +412 -75
- package/src/tools/browser/launch.ts +37 -0
- package/src/tools/browser/registry.ts +99 -9
- package/src/tools/cron.ts +6 -4
- package/src/tools/descriptors.test.ts +15 -0
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +10 -6
- package/src/tools/job.ts +4 -1
- package/src/tools/monitor.ts +12 -9
- package/src/tools/resolve.ts +30 -10
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/utils/external-editor.ts +72 -1
|
@@ -166,8 +166,9 @@ export class DiscordNotificationDaemon {
|
|
|
166
166
|
expectedGeneration?: number,
|
|
167
167
|
) => SessionAttachment | null | Promise<SessionAttachment | null>;
|
|
168
168
|
readonly #effects: ChatEffectJournal;
|
|
169
|
-
readonly #activeWork = new
|
|
169
|
+
readonly #activeWork = new Map<Promise<unknown>, string | undefined>();
|
|
170
170
|
readonly #workInvalidators = new Set<() => Promise<void>>();
|
|
171
|
+
readonly #sessionInvalidators = new Map<string, Set<() => Promise<void>>>();
|
|
171
172
|
readonly #inflightInbound = new Set<string>();
|
|
172
173
|
#started = false;
|
|
173
174
|
#lifecycleGeneration = 0;
|
|
@@ -264,7 +265,8 @@ export class DiscordNotificationDaemon {
|
|
|
264
265
|
await this.options.provider.start(
|
|
265
266
|
async event => {
|
|
266
267
|
if (lifecycleGeneration !== this.#lifecycleGeneration || !this.#started) return;
|
|
267
|
-
await this.#
|
|
268
|
+
const record = await this.#byThread(event.guildId, event.parentId, event.threadId);
|
|
269
|
+
await this.#track(this.handleInbound(event), record?.sessionId);
|
|
268
270
|
},
|
|
269
271
|
() => {},
|
|
270
272
|
);
|
|
@@ -375,6 +377,9 @@ export class DiscordNotificationDaemon {
|
|
|
375
377
|
this.#workGeneration += 1;
|
|
376
378
|
await Promise.allSettled([...this.#workInvalidators].map(invalidate => invalidate()));
|
|
377
379
|
}
|
|
380
|
+
async #invalidateSessionWork(sessionId: string): Promise<void> {
|
|
381
|
+
await Promise.allSettled([...(this.#sessionInvalidators.get(sessionId) ?? [])].map(invalidate => invalidate()));
|
|
382
|
+
}
|
|
378
383
|
|
|
379
384
|
async #drainActiveWork(deadline: number): Promise<void> {
|
|
380
385
|
while (this.#activeWork.size > 0) {
|
|
@@ -385,7 +390,7 @@ export class DiscordNotificationDaemon {
|
|
|
385
390
|
return;
|
|
386
391
|
}
|
|
387
392
|
const settled = await Promise.race([
|
|
388
|
-
Promise.allSettled([...this.#activeWork]).then(() => true),
|
|
393
|
+
Promise.allSettled([...this.#activeWork.keys()]).then(() => true),
|
|
389
394
|
Bun.sleep(remaining).then(() => false),
|
|
390
395
|
]);
|
|
391
396
|
if (!settled) {
|
|
@@ -395,6 +400,17 @@ export class DiscordNotificationDaemon {
|
|
|
395
400
|
}
|
|
396
401
|
}
|
|
397
402
|
}
|
|
403
|
+
async #drainSessionWork(sessionId: string, deadline: number): Promise<void> {
|
|
404
|
+
while (true) {
|
|
405
|
+
const work = [...this.#activeWork.entries()]
|
|
406
|
+
.filter(([, activeSessionId]) => activeSessionId === sessionId)
|
|
407
|
+
.map(([promise]) => promise);
|
|
408
|
+
if (work.length === 0) return;
|
|
409
|
+
const remaining = deadline - this.#now();
|
|
410
|
+
if (remaining <= 0) return;
|
|
411
|
+
await Promise.race([Promise.allSettled(work), Bun.sleep(remaining)]);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
398
414
|
|
|
399
415
|
#recordProviderLifecycleError(error: unknown): void {
|
|
400
416
|
this.#providerLifecycleError = error;
|
|
@@ -422,7 +438,7 @@ export class DiscordNotificationDaemon {
|
|
|
422
438
|
* legitimate create into a spurious teardown rejection.
|
|
423
439
|
*/
|
|
424
440
|
async notify(input: DiscordNotificationInput): Promise<DiscordConversation> {
|
|
425
|
-
return await this.#track(this.#notify(input));
|
|
441
|
+
return await this.#track(this.#notify(input), input.sessionId);
|
|
426
442
|
}
|
|
427
443
|
async #notify(input: DiscordNotificationInput): Promise<DiscordConversation> {
|
|
428
444
|
await this.#requireLiveBinding(input.sessionId, input.endpointGeneration, input.attachmentAuthorityId);
|
|
@@ -457,7 +473,7 @@ export class DiscordNotificationDaemon {
|
|
|
457
473
|
|
|
458
474
|
/** Posts a safe command outcome to the active mapped conversation. */
|
|
459
475
|
async postCommandResult(sessionId: string, content: string): Promise<boolean> {
|
|
460
|
-
return await this.#track(this.#postCommandResult(sessionId, content));
|
|
476
|
+
return await this.#track(this.#postCommandResult(sessionId, content), sessionId);
|
|
461
477
|
}
|
|
462
478
|
async #postCommandResult(sessionId: string, content: string): Promise<boolean> {
|
|
463
479
|
const record = await this.#bySession(sessionId);
|
|
@@ -471,13 +487,20 @@ export class DiscordNotificationDaemon {
|
|
|
471
487
|
if (closing) await this.#driveClose(closing);
|
|
472
488
|
}
|
|
473
489
|
|
|
474
|
-
async recoverCleanup(sessionId: string, endpointGeneration: number): Promise<
|
|
490
|
+
async recoverCleanup(sessionId: string, endpointGeneration: number, authorityId?: string): Promise<boolean> {
|
|
475
491
|
const record = await this.#bySession(sessionId);
|
|
476
|
-
if (record?.endpointGeneration !== endpointGeneration
|
|
492
|
+
if (record?.endpointGeneration !== endpointGeneration) return true;
|
|
493
|
+
if (record?.state === "closed" || record?.state === "archived" || record?.state === "resuming") return true;
|
|
494
|
+
if (!closingIntent(record) && record?.state === "active" && record.attachmentAuthorityId === authorityId)
|
|
495
|
+
return true;
|
|
496
|
+
if (!closingIntent(record)) return false;
|
|
477
497
|
await this.#driveClose(record);
|
|
498
|
+
return true;
|
|
478
499
|
}
|
|
479
500
|
|
|
480
501
|
async retireAttachment(sessionId: string, endpointGeneration: number): Promise<void> {
|
|
502
|
+
await this.#invalidateSessionWork(sessionId);
|
|
503
|
+
await this.#drainSessionWork(sessionId, this.#now() + 5_000);
|
|
481
504
|
const intentKey = this.#intentKey(sessionId);
|
|
482
505
|
const now = this.#now();
|
|
483
506
|
await this.#store.transact(intentKey, current => {
|
|
@@ -548,7 +571,7 @@ export class DiscordNotificationDaemon {
|
|
|
548
571
|
endpointGeneration: number,
|
|
549
572
|
attachmentAuthorityId?: string,
|
|
550
573
|
): Promise<DiscordConversation | undefined> {
|
|
551
|
-
return await this.#track(this.#resumeAdmission(sessionId, endpointGeneration, attachmentAuthorityId));
|
|
574
|
+
return await this.#track(this.#resumeAdmission(sessionId, endpointGeneration, attachmentAuthorityId), sessionId);
|
|
552
575
|
}
|
|
553
576
|
async #resumeAdmission(
|
|
554
577
|
sessionId: string,
|
|
@@ -665,8 +688,8 @@ export class DiscordNotificationDaemon {
|
|
|
665
688
|
}
|
|
666
689
|
}
|
|
667
690
|
|
|
668
|
-
#track<T>(work: Promise<T
|
|
669
|
-
this.#activeWork.
|
|
691
|
+
#track<T>(work: Promise<T>, sessionId?: string): Promise<T> {
|
|
692
|
+
this.#activeWork.set(work, sessionId);
|
|
670
693
|
return work.finally(() => this.#activeWork.delete(work));
|
|
671
694
|
}
|
|
672
695
|
|
|
@@ -1871,6 +1894,11 @@ export class DiscordNotificationDaemon {
|
|
|
1871
1894
|
#intentKey(sessionId: string): string {
|
|
1872
1895
|
return `${this.options.provider.applicationId}:${this.options.guildId}:${this.options.parentChannelId}:creating:${sessionId}`;
|
|
1873
1896
|
}
|
|
1897
|
+
#removeSessionInvalidator(sessionId: string, set: Set<() => Promise<void>>, invalidate: () => Promise<void>): void {
|
|
1898
|
+
set.delete(invalidate);
|
|
1899
|
+
if (set.size === 0 && this.#sessionInvalidators.get(sessionId) === set)
|
|
1900
|
+
this.#sessionInvalidators.delete(sessionId);
|
|
1901
|
+
}
|
|
1874
1902
|
async #withCreateIntentLease<T>(intent: DiscordConversation, work: () => Promise<T>): Promise<T> {
|
|
1875
1903
|
let lost = false;
|
|
1876
1904
|
let renewal: Promise<boolean> | undefined;
|
|
@@ -1886,6 +1914,9 @@ export class DiscordNotificationDaemon {
|
|
|
1886
1914
|
await abandonCurrent();
|
|
1887
1915
|
};
|
|
1888
1916
|
this.#workInvalidators.add(invalidate);
|
|
1917
|
+
const sessionInvalidators = this.#sessionInvalidators.get(intent.sessionId!) ?? new Set<() => Promise<void>>();
|
|
1918
|
+
sessionInvalidators.add(invalidate);
|
|
1919
|
+
this.#sessionInvalidators.set(intent.sessionId!, sessionInvalidators);
|
|
1889
1920
|
const renew = async (): Promise<boolean> => {
|
|
1890
1921
|
if (lost || workGeneration !== this.#workGeneration) {
|
|
1891
1922
|
await invalidate();
|
|
@@ -1953,6 +1984,7 @@ export class DiscordNotificationDaemon {
|
|
|
1953
1984
|
} finally {
|
|
1954
1985
|
clearInterval(timer);
|
|
1955
1986
|
this.#workInvalidators.delete(invalidate);
|
|
1987
|
+
this.#removeSessionInvalidator(intent.sessionId!, sessionInvalidators, invalidate);
|
|
1956
1988
|
}
|
|
1957
1989
|
}
|
|
1958
1990
|
async #clearPending(record: DiscordConversation, actionId: string, actionNonce: string): Promise<void> {
|
|
@@ -2076,6 +2108,12 @@ export class DiscordNotificationDaemon {
|
|
|
2076
2108
|
);
|
|
2077
2109
|
};
|
|
2078
2110
|
this.#workInvalidators.add(invalidate);
|
|
2111
|
+
let sessionInvalidators: Set<() => Promise<void>> | undefined;
|
|
2112
|
+
if (sessionId !== undefined) {
|
|
2113
|
+
sessionInvalidators = this.#sessionInvalidators.get(sessionId) ?? new Set<() => Promise<void>>();
|
|
2114
|
+
sessionInvalidators.add(invalidate);
|
|
2115
|
+
this.#sessionInvalidators.set(sessionId, sessionInvalidators);
|
|
2116
|
+
}
|
|
2079
2117
|
const renewLease = async (): Promise<boolean> => {
|
|
2080
2118
|
if (renewalLost || workGeneration !== this.#workGeneration) {
|
|
2081
2119
|
renewalLost = true;
|
|
@@ -2136,6 +2174,8 @@ export class DiscordNotificationDaemon {
|
|
|
2136
2174
|
} finally {
|
|
2137
2175
|
clearInterval(timer);
|
|
2138
2176
|
this.#workInvalidators.delete(invalidate);
|
|
2177
|
+
if (sessionId !== undefined && sessionInvalidators !== undefined)
|
|
2178
|
+
this.#removeSessionInvalidator(sessionId, sessionInvalidators, invalidate);
|
|
2139
2179
|
}
|
|
2140
2180
|
}
|
|
2141
2181
|
|
package/src/sdk/bus/index.ts
CHANGED
|
@@ -90,14 +90,17 @@ import { acpFinalTextFromMessage } from "../acp/final-text";
|
|
|
90
90
|
import { ensureBroker } from "../broker/ensure";
|
|
91
91
|
import { publishSessionHostRuntimeEvidence, type SessionHostRuntimePublication } from "../broker/lifecycle";
|
|
92
92
|
import { processIncarnation } from "../broker/process-incarnation";
|
|
93
|
-
import { SessionIndex } from "../broker/session-index";
|
|
93
|
+
import { resolveSessionLocator, SessionIndex } from "../broker/session-index";
|
|
94
94
|
import {
|
|
95
95
|
CAP_GATED_FRAME_KINDS,
|
|
96
96
|
createSdkSurfaceFactory,
|
|
97
|
+
masterAttestationForEffectiveHost,
|
|
98
|
+
reattestMasterSessionIdentity,
|
|
97
99
|
type SessionSdkHost,
|
|
98
100
|
SessionSdkSessionRuntime,
|
|
99
101
|
shouldHostSdk,
|
|
100
102
|
TOOL_ACTIVITY_CAPABILITY,
|
|
103
|
+
verifyMasterCapabilityFrame,
|
|
101
104
|
} from "../host";
|
|
102
105
|
import { type AbortScope, type ControlSurface, dispatchControl, TypedControlError } from "../host/control";
|
|
103
106
|
import { BROKER_RUNTIME_CLOSE_CAPABILITY_FIELD } from "../host/control/runtime-gate";
|
|
@@ -371,6 +374,8 @@ export type LifecycleErrorReason =
|
|
|
371
374
|
| "spawn_failed"
|
|
372
375
|
| "discovery_timeout"
|
|
373
376
|
| "readiness_timeout"
|
|
377
|
+
| "worktree_preparation_timeout"
|
|
378
|
+
| "dependency_preparation_timeout"
|
|
374
379
|
| "close_refused"
|
|
375
380
|
| "not_found"
|
|
376
381
|
| "terminal_uncertain"
|
|
@@ -1220,6 +1225,8 @@ interface SessionRuntime {
|
|
|
1220
1225
|
disposeGateTerminalController: () => void;
|
|
1221
1226
|
disposeAckRecoveryParticipant: () => void;
|
|
1222
1227
|
disposeGateEmitterListener: () => void;
|
|
1228
|
+
/** Deregisters this session's job-fold listener (`bash_folded`). */
|
|
1229
|
+
disposeFoldListener: () => void;
|
|
1223
1230
|
/** Aborts and fences side turns while notification delivery is disabled. */
|
|
1224
1231
|
disableEphemeralTurns: () => void;
|
|
1225
1232
|
waitForGateResolutionQuiescence: () => Promise<void>;
|
|
@@ -2381,6 +2388,7 @@ function sdkQuerySurface(
|
|
|
2381
2388
|
steerStatusLookup: (selector: { commandId?: string; turnId?: string; clientRef?: string }) => unknown = () => ({
|
|
2382
2389
|
status: "unknown",
|
|
2383
2390
|
}),
|
|
2391
|
+
getRuntimeHost: () => SessionSdkHost | undefined = () => undefined,
|
|
2384
2392
|
): SessionSurface {
|
|
2385
2393
|
return createSdkSurfaceFactory({
|
|
2386
2394
|
ctx,
|
|
@@ -2393,6 +2401,7 @@ function sdkQuerySurface(
|
|
|
2393
2401
|
turnResultLookup,
|
|
2394
2402
|
steerStatusLookup,
|
|
2395
2403
|
hostTools: () => getInstalledDefinitions("host_tools") !== undefined,
|
|
2404
|
+
getRuntimeHost,
|
|
2396
2405
|
}).query;
|
|
2397
2406
|
}
|
|
2398
2407
|
|
|
@@ -3519,7 +3528,7 @@ function sdkControlSurface(
|
|
|
3519
3528
|
setQueueMode: (kind, mode) => {
|
|
3520
3529
|
if (!bindings.has("setQueueMode"))
|
|
3521
3530
|
return unavailable(`queue.${kind}_mode.set`, "no session queue-mode seam is installed")();
|
|
3522
|
-
if (!ctx.setQueueMode(kind as "steering" | "follow_up" | "
|
|
3531
|
+
if (!ctx.setQueueMode(kind as "steering" | "follow_up" | "tool_interrupt", mode))
|
|
3523
3532
|
throw Object.assign(new Error("Invalid queue mode."), { code: "invalid_input" });
|
|
3524
3533
|
return { changed: true };
|
|
3525
3534
|
},
|
|
@@ -4043,6 +4052,12 @@ export function createNotificationsExtension(
|
|
|
4043
4052
|
controller?: NotificationSessionController;
|
|
4044
4053
|
/** Whether this host mode can own the root SDK endpoint. Default: true. */
|
|
4045
4054
|
sdkHostModeSupported?: boolean;
|
|
4055
|
+
/** In-memory master capability for private broker verification only. */
|
|
4056
|
+
masterCapability?: string;
|
|
4057
|
+
/** Opaque direct-role epoch this effective host may adopt. */
|
|
4058
|
+
masterAttestationEpoch?: string;
|
|
4059
|
+
/** Stable master lineage identity retained across session switches/branches. */
|
|
4060
|
+
masterOwnerSessionId?: string;
|
|
4046
4061
|
onSdkRequest?: (kind: "control" | "query", connectionId: string, frame: Record<string, unknown>) => void;
|
|
4047
4062
|
runBtwTurn?: (question: string, signal: AbortSignal) => Promise<{ replyText: string }>;
|
|
4048
4063
|
/** Observes settlement of optional session-branch startup after reconciliation completes. */
|
|
@@ -4103,6 +4118,7 @@ export function createNotificationsExtension(
|
|
|
4103
4118
|
}
|
|
4104
4119
|
| undefined;
|
|
4105
4120
|
let extensionShuttingDown = false;
|
|
4121
|
+
const consumedMasterNonces = new Map<string, number>();
|
|
4106
4122
|
|
|
4107
4123
|
async function ensureTelegramOwner(settings: Settings): Promise<"ready" | "blocked_identity"> {
|
|
4108
4124
|
if (!telegramTopicsEnabled()) return "blocked_identity";
|
|
@@ -4368,8 +4384,14 @@ export function createNotificationsExtension(
|
|
|
4368
4384
|
rt.workflowGate?.setRuntimeTurnProvider?.(null);
|
|
4369
4385
|
} catch {}
|
|
4370
4386
|
try {
|
|
4387
|
+
const attachedClients = rt.server.clientCount();
|
|
4371
4388
|
const delivered = await pushTerminalSessionFrame(rt, { type: "session_closed", sessionId: id });
|
|
4372
|
-
if (!delivered)
|
|
4389
|
+
if (!delivered) {
|
|
4390
|
+
const fields = { sessionId: id, endpointScope: rt.endpointScope, attachedClients };
|
|
4391
|
+
if (attachedClients > 0)
|
|
4392
|
+
logger.warn("notifications: session_closed socket delivery was not acknowledged", fields);
|
|
4393
|
+
else logger.debug("notifications: session_closed had no attached client to acknowledge", fields);
|
|
4394
|
+
}
|
|
4373
4395
|
} catch (e) {
|
|
4374
4396
|
logger.warn(`notifications: session_closed failed: ${String(e)}`);
|
|
4375
4397
|
}
|
|
@@ -4380,6 +4402,9 @@ export function createNotificationsExtension(
|
|
|
4380
4402
|
try {
|
|
4381
4403
|
rt.disposeGateEmitterListener();
|
|
4382
4404
|
} catch {}
|
|
4405
|
+
try {
|
|
4406
|
+
rt.disposeFoldListener();
|
|
4407
|
+
} catch {}
|
|
4383
4408
|
rt.gatePresentations?.dispose();
|
|
4384
4409
|
try {
|
|
4385
4410
|
rt.disposeGateTerminalController();
|
|
@@ -4489,12 +4514,14 @@ export function createNotificationsExtension(
|
|
|
4489
4514
|
|
|
4490
4515
|
async function startSession(ctx: ExtensionContext): Promise<SessionStartResult> {
|
|
4491
4516
|
const id = sessionId(ctx);
|
|
4492
|
-
const lifecycleRequestId = safeLifecycleRequestId(process.env.GJC_LIFECYCLE_REQUEST_ID);
|
|
4493
4517
|
const { settings, cfg, settingsAvailable } = resolveSettings(options.settings);
|
|
4494
4518
|
const notificationsEnabledForSession = controller.query(ctx).genericSessionEnabled;
|
|
4495
4519
|
const sdkEnabledForSession =
|
|
4496
4520
|
(options.sdkHostModeSupported ?? true) && shouldHostSdk(settings, isNotificationEligibleContext(ctx));
|
|
4497
4521
|
const lifecycleRequired = lifecycleStartupCapability !== undefined;
|
|
4522
|
+
const lifecycleRequestId = lifecycleRequired
|
|
4523
|
+
? safeLifecycleRequestId(lifecycleStartupCapability.lifecycleRequestId)
|
|
4524
|
+
: safeLifecycleRequestId(process.env.GJC_LIFECYCLE_REQUEST_ID);
|
|
4498
4525
|
/** The broker-issued readiness intent for this exact lifecycle-managed session. */
|
|
4499
4526
|
const lifecycleReadiness = lifecycleStartupCapability?.readiness ?? "immediate";
|
|
4500
4527
|
const failLifecycleStartup = (
|
|
@@ -5270,7 +5297,6 @@ export function createNotificationsExtension(
|
|
|
5270
5297
|
submission.reconciliationKind,
|
|
5271
5298
|
correlation,
|
|
5272
5299
|
winner,
|
|
5273
|
-
undefined,
|
|
5274
5300
|
extra?.error,
|
|
5275
5301
|
extra?.finalText,
|
|
5276
5302
|
);
|
|
@@ -5442,6 +5468,7 @@ export function createNotificationsExtension(
|
|
|
5442
5468
|
settings,
|
|
5443
5469
|
selector => kindReconciliation.lookupResult(selector.kind, selector),
|
|
5444
5470
|
selector => kindReconciliation.lookupSteer(selector),
|
|
5471
|
+
() => host,
|
|
5445
5472
|
),
|
|
5446
5473
|
id,
|
|
5447
5474
|
revisions,
|
|
@@ -6588,6 +6615,7 @@ export function createNotificationsExtension(
|
|
|
6588
6615
|
}
|
|
6589
6616
|
|
|
6590
6617
|
sdkRuntime = new SessionSdkSessionRuntime({
|
|
6618
|
+
masterOwnerSessionId: options.masterOwnerSessionId,
|
|
6591
6619
|
transport: {
|
|
6592
6620
|
sessionId: id,
|
|
6593
6621
|
stateRoot,
|
|
@@ -6607,6 +6635,13 @@ export function createNotificationsExtension(
|
|
|
6607
6635
|
...(activationGate ? { activationGate } : {}),
|
|
6608
6636
|
...(settings ? { settings } : {}),
|
|
6609
6637
|
...(configOverrides ? { configOverrides } : {}),
|
|
6638
|
+
masterCapabilityVerify: frame =>
|
|
6639
|
+
verifyMasterCapabilityFrame({
|
|
6640
|
+
frame,
|
|
6641
|
+
expectedCapability: options.masterCapability,
|
|
6642
|
+
expectedEpoch: options.masterAttestationEpoch,
|
|
6643
|
+
replay: consumedMasterNonces,
|
|
6644
|
+
}),
|
|
6610
6645
|
connectionCapabilities: connectionId => hostCapCache.get(connectionId),
|
|
6611
6646
|
installProviderDefinitions,
|
|
6612
6647
|
onProviderDefinitionsRemoved: removeProviderDefinitions,
|
|
@@ -6965,6 +7000,7 @@ export function createNotificationsExtension(
|
|
|
6965
7000
|
]);
|
|
6966
7001
|
},
|
|
6967
7002
|
disposeGateEmitterListener: () => {},
|
|
7003
|
+
disposeFoldListener: () => {},
|
|
6968
7004
|
trackGateResolution,
|
|
6969
7005
|
waitForGateResolutionQuiescence: async () => {
|
|
6970
7006
|
await Promise.allSettled(inFlightGateResolutions);
|
|
@@ -7751,6 +7787,8 @@ export function createNotificationsExtension(
|
|
|
7751
7787
|
}
|
|
7752
7788
|
const agentDir = lifecycleAgentDir ?? settings?.getAgentDir?.();
|
|
7753
7789
|
if (lifecycleRequired && !agentDir) throw new Error("Lifecycle SDK host requires an agent directory.");
|
|
7790
|
+
if (lifecycleRequired && !lifecycleRequestId)
|
|
7791
|
+
throw new Error("Lifecycle SDK host requires a capability-bound request identity.");
|
|
7754
7792
|
|
|
7755
7793
|
if (agentDir) {
|
|
7756
7794
|
try {
|
|
@@ -7758,13 +7796,36 @@ export function createNotificationsExtension(
|
|
|
7758
7796
|
throwIfLifecycleStopped();
|
|
7759
7797
|
const index = await new SessionIndex(agentDir).open();
|
|
7760
7798
|
throwIfLifecycleStopped();
|
|
7761
|
-
const locator =
|
|
7762
|
-
const
|
|
7799
|
+
const locator = await resolveSessionLocator(ctx.cwd, endpointStateRoot);
|
|
7800
|
+
const endpointPath = path.join(endpointStateRoot, "sdk", `${id}.json`);
|
|
7801
|
+
const endpointMtimeMs = fs.statSync(endpointPath).mtimeMs;
|
|
7802
|
+
const endpointIdentity = fs.statSync(endpointPath, { bigint: true });
|
|
7803
|
+
const endpointFileId = `${endpointIdentity.dev}:${endpointIdentity.ino}`;
|
|
7763
7804
|
const hostProcessIncarnation = processIncarnation(process.pid);
|
|
7805
|
+
const direct = await reattestMasterSessionIdentity({
|
|
7806
|
+
index,
|
|
7807
|
+
locator,
|
|
7808
|
+
masterCapability: options.masterCapability,
|
|
7809
|
+
attestationEpoch: options.masterAttestationEpoch,
|
|
7810
|
+
ownerSessionId: options.masterOwnerSessionId,
|
|
7811
|
+
sessionId: id,
|
|
7812
|
+
pid: process.pid,
|
|
7813
|
+
processIncarnation: hostProcessIncarnation,
|
|
7814
|
+
});
|
|
7815
|
+
throwIfLifecycleStopped();
|
|
7764
7816
|
await host.registerWithBroker({
|
|
7765
7817
|
// The endpoint is written before registration. Its exact mtime
|
|
7766
7818
|
// binds this index generation to that discovery record.
|
|
7767
7819
|
register: async input => {
|
|
7820
|
+
const masterRole = masterAttestationForEffectiveHost({
|
|
7821
|
+
masterCapability: options.masterCapability,
|
|
7822
|
+
attestationEpoch: options.masterAttestationEpoch,
|
|
7823
|
+
ownerSessionId: options.masterOwnerSessionId,
|
|
7824
|
+
sessionId: input.sessionId,
|
|
7825
|
+
pid: process.pid,
|
|
7826
|
+
processIncarnation: hostProcessIncarnation,
|
|
7827
|
+
direct,
|
|
7828
|
+
});
|
|
7768
7829
|
await index.append({
|
|
7769
7830
|
type: "host_registered",
|
|
7770
7831
|
...input,
|
|
@@ -7776,6 +7837,8 @@ export function createNotificationsExtension(
|
|
|
7776
7837
|
// teardown identity provable after that workspace is gone.
|
|
7777
7838
|
...(hostProcessIncarnation ? { processIncarnation: hostProcessIncarnation } : {}),
|
|
7778
7839
|
endpointMtimeMs,
|
|
7840
|
+
endpointFileId,
|
|
7841
|
+
...(masterRole ? { masterRole } : {}),
|
|
7779
7842
|
...(lifecycleRequestId ? { lifecycleRequestId } : {}),
|
|
7780
7843
|
});
|
|
7781
7844
|
},
|
|
@@ -7962,6 +8025,20 @@ export function createNotificationsExtension(
|
|
|
7962
8025
|
};
|
|
7963
8026
|
activeRuntime.disposeGateEmitterListener = registerWorkflowGateEmitterListener(id, attachWorkflowGate);
|
|
7964
8027
|
if (ctx.workflowGate) attachWorkflowGate(ctx.workflowGate);
|
|
8028
|
+
// Every fold (steer, chord, auto-background timer, `bash.background`)
|
|
8029
|
+
// publishes one replayable `bash_folded` frame. `runtime.jobs.list`
|
|
8030
|
+
// `foldReason` remains the source of truth clients reconcile against after
|
|
8031
|
+
// a replay, since a replayed frame may describe a job that has since ended.
|
|
8032
|
+
activeRuntime.disposeFoldListener =
|
|
8033
|
+
ctx.onJobFold?.(event => {
|
|
8034
|
+
emitSessionEvent(activeRuntime, {
|
|
8035
|
+
type: "bash_folded",
|
|
8036
|
+
sessionId: id,
|
|
8037
|
+
jobId: event.jobId,
|
|
8038
|
+
generation: event.generation,
|
|
8039
|
+
reason: event.reason,
|
|
8040
|
+
});
|
|
8041
|
+
}) ?? (() => {});
|
|
7965
8042
|
finishStartup({ status: "started", runtime: initializedRuntime });
|
|
7966
8043
|
return { status: "started", runtime: initializedRuntime };
|
|
7967
8044
|
} catch (e) {
|