@gajae-code/coding-agent 0.12.6 → 0.12.7
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 +3 -1
- package/dist/types/async/job-manager.d.ts +36 -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/sdk/bus/chat-daemon-control.d.ts +1 -3
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +2 -4
- package/dist/types/sdk/host/host.d.ts +2 -23
- package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
- 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-storage.d.ts +2 -5
- package/dist/types/session/messages.d.ts +1 -4
- package/dist/types/session/session-manager.d.ts +4 -5
- 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 +8 -8
- package/src/async/job-manager.ts +430 -46
- 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/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 +1 -1
- package/src/internal-urls/registry-helpers.ts +0 -62
- package/src/modes/acp/acp-agent.ts +0 -24
- package/src/modes/acp/acp-client-bridge.ts +11 -154
- package/src/prompts/tools/bash.md +2 -2
- package/src/sdk/acp/adapter.ts +9 -200
- package/src/sdk/bus/chat-daemon-control.ts +3 -5
- package/src/sdk/bus/index.ts +8 -324
- package/src/sdk/bus/telegram-daemon-contract.ts +2 -4
- package/src/sdk/host/host.ts +9 -187
- package/src/sdk/host/reverse-leases.ts +42 -249
- package/src/session/agent-session.ts +15 -178
- package/src/session/artifacts.ts +74 -211
- package/src/session/client-bridge.ts +0 -15
- package/src/session/internal/managed-session-storage.ts +5 -104
- package/src/session/messages.ts +7 -32
- package/src/session/session-manager.ts +21 -12
- package/src/session/streaming-output.ts +40 -198
- package/src/task/index.ts +230 -64
- package/src/task/receipt.ts +2 -4
- package/src/task/render.ts +0 -30
- package/src/task/types.ts +10 -34
- 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/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/bus/index.ts
CHANGED
|
@@ -61,7 +61,7 @@ import { registerAskAnswerSource, registerWorkflowGateEmitterListener } from "..
|
|
|
61
61
|
import { acpFinalTextFromMessage } from "../acp/final-text";
|
|
62
62
|
import { ensureBroker } from "../broker/ensure";
|
|
63
63
|
import { SessionIndex } from "../broker/session-index";
|
|
64
|
-
import {
|
|
64
|
+
import { SessionSdkHost, shouldHostSdk } from "../host";
|
|
65
65
|
import { type ControlSurface, dispatchControl } from "../host/control";
|
|
66
66
|
import { CursorRegistry, QueryHandlers, RevisionStore, type SessionSurface } from "../host/query";
|
|
67
67
|
import { projectQ10Models } from "../models.js";
|
|
@@ -3258,8 +3258,6 @@ export function createNotificationsExtension(
|
|
|
3258
3258
|
awaitStartup: boolean;
|
|
3259
3259
|
}
|
|
3260
3260
|
| undefined;
|
|
3261
|
-
const MAX_SYNTHETIC_SEAM_ADMISSIONS = 64;
|
|
3262
|
-
let syntheticSeamAdmissions = 0;
|
|
3263
3261
|
let extensionShuttingDown = false;
|
|
3264
3262
|
|
|
3265
3263
|
async function ensureTelegramOwner(
|
|
@@ -3569,64 +3567,8 @@ export function createNotificationsExtension(
|
|
|
3569
3567
|
const revisions = new RevisionStore(id, Date.now, { storageDir: stateRoot });
|
|
3570
3568
|
let host: SessionSdkHost | undefined;
|
|
3571
3569
|
let disposeUiAnswerSource: (() => void) | undefined;
|
|
3572
|
-
|
|
3573
|
-
let terminalClientBridge: ClientBridge | undefined;
|
|
3574
|
-
let terminalPublicationFailedLeaseId: string | undefined;
|
|
3575
|
-
const MAX_PENDING_TERMINAL_CREATIONS = 32;
|
|
3576
|
-
let pendingTerminalCreations = 0;
|
|
3577
|
-
let terminalCreationTail = Promise.resolve();
|
|
3578
|
-
const withTerminalCreationAdmission = async <T>(operation: () => Promise<T>): Promise<T> => {
|
|
3579
|
-
if (pendingTerminalCreations >= MAX_PENDING_TERMINAL_CREATIONS)
|
|
3580
|
-
throw new Error("terminal creation admission limit reached");
|
|
3581
|
-
pendingTerminalCreations++;
|
|
3582
|
-
const previous = terminalCreationTail;
|
|
3583
|
-
const release = Promise.withResolvers<void>();
|
|
3584
|
-
terminalCreationTail = previous.then(() => release.promise);
|
|
3585
|
-
try {
|
|
3586
|
-
await previous;
|
|
3587
|
-
return await operation();
|
|
3588
|
-
} finally {
|
|
3589
|
-
pendingTerminalCreations--;
|
|
3590
|
-
release.resolve();
|
|
3591
|
-
}
|
|
3592
|
-
};
|
|
3593
|
-
const providerLeaseIdentities = new Map<
|
|
3594
|
-
string,
|
|
3595
|
-
{ connectionId: string; connectionGeneration: number; fence: string }
|
|
3596
|
-
>();
|
|
3597
|
-
const refreshSdkClientBridge = () => {
|
|
3598
|
-
if (!fsClientBridge && !terminalClientBridge) {
|
|
3599
|
-
ctx.setSdkClientBridge?.(undefined);
|
|
3600
|
-
return;
|
|
3601
|
-
}
|
|
3602
|
-
const bridge: ClientBridge = {
|
|
3603
|
-
capabilities: {
|
|
3604
|
-
readTextFile: fsClientBridge?.capabilities.readTextFile,
|
|
3605
|
-
writeTextFile: fsClientBridge?.capabilities.writeTextFile,
|
|
3606
|
-
terminal: terminalClientBridge?.capabilities.terminal,
|
|
3607
|
-
},
|
|
3608
|
-
deferAgentInitiatedTurns: true,
|
|
3609
|
-
};
|
|
3610
|
-
if (fsClientBridge?.readTextFile) bridge.readTextFile = fsClientBridge.readTextFile;
|
|
3611
|
-
if (fsClientBridge?.writeTextFile) bridge.writeTextFile = fsClientBridge.writeTextFile;
|
|
3612
|
-
if (terminalClientBridge?.quarantineProviderLease)
|
|
3613
|
-
bridge.quarantineProviderLease = terminalClientBridge.quarantineProviderLease;
|
|
3614
|
-
if (terminalClientBridge?.createTerminal) bridge.createTerminal = terminalClientBridge.createTerminal;
|
|
3615
|
-
ctx.setSdkClientBridge?.(bridge);
|
|
3616
|
-
};
|
|
3617
|
-
const installProviderDefinitions = (
|
|
3618
|
-
capability: string,
|
|
3619
|
-
definitions: unknown,
|
|
3620
|
-
leaseId: string,
|
|
3621
|
-
connectionId: string,
|
|
3622
|
-
connectionGeneration: number,
|
|
3623
|
-
) => {
|
|
3570
|
+
const installProviderDefinitions = (capability: string, definitions: unknown) => {
|
|
3624
3571
|
validateProviderDefinitions(capability, definitions);
|
|
3625
|
-
providerLeaseIdentities.set(leaseId, {
|
|
3626
|
-
connectionId,
|
|
3627
|
-
connectionGeneration,
|
|
3628
|
-
fence: `${connectionId}:${connectionGeneration}:${leaseId}`,
|
|
3629
|
-
});
|
|
3630
3572
|
if (capability === "permission") {
|
|
3631
3573
|
ctx.setSdkPermissionProvider?.(async (toolCall, permissionOptions, signal) => {
|
|
3632
3574
|
const result = await host!.reverse.request(
|
|
@@ -3664,179 +3606,6 @@ export function createNotificationsExtension(
|
|
|
3664
3606
|
);
|
|
3665
3607
|
return;
|
|
3666
3608
|
}
|
|
3667
|
-
if (capability === "terminal") {
|
|
3668
|
-
const terminalLeaseId = leaseId;
|
|
3669
|
-
if (!terminalLeaseId) throw new Error("terminal provider lease is unavailable");
|
|
3670
|
-
const terminalProviderIdentity = providerLeaseIdentities.get(terminalLeaseId);
|
|
3671
|
-
if (!terminalProviderIdentity) throw new Error("terminal provider generation is unavailable");
|
|
3672
|
-
if (terminalPublicationFailedLeaseId !== terminalLeaseId) terminalPublicationFailedLeaseId = undefined;
|
|
3673
|
-
const requestTerminal = (
|
|
3674
|
-
method: string,
|
|
3675
|
-
payload: Record<string, unknown>,
|
|
3676
|
-
signal?: AbortSignal,
|
|
3677
|
-
cleanup = false,
|
|
3678
|
-
) =>
|
|
3679
|
-
host!.reverse.request(
|
|
3680
|
-
"terminal",
|
|
3681
|
-
method,
|
|
3682
|
-
payload,
|
|
3683
|
-
signal,
|
|
3684
|
-
terminalLeaseId,
|
|
3685
|
-
cleanup,
|
|
3686
|
-
terminalProviderIdentity.connectionId,
|
|
3687
|
-
);
|
|
3688
|
-
const settleTerminalCleanup = async (
|
|
3689
|
-
method: "terminal.kill" | "terminal.release",
|
|
3690
|
-
terminalId: string,
|
|
3691
|
-
): Promise<boolean> =>
|
|
3692
|
-
await Promise.race([
|
|
3693
|
-
Promise.resolve()
|
|
3694
|
-
.then(() => requestTerminal(method, { terminalId }, AbortSignal.timeout(500), true))
|
|
3695
|
-
.then(
|
|
3696
|
-
() => true,
|
|
3697
|
-
() => false,
|
|
3698
|
-
),
|
|
3699
|
-
Bun.sleep(500).then(() => false),
|
|
3700
|
-
]);
|
|
3701
|
-
terminalClientBridge = {
|
|
3702
|
-
capabilities: { terminal: true },
|
|
3703
|
-
deferAgentInitiatedTurns: true,
|
|
3704
|
-
async quarantineProviderLease(identity, _reason) {
|
|
3705
|
-
if (typeof identity === "string") throw new Error("terminal provider generation is required");
|
|
3706
|
-
if (
|
|
3707
|
-
identity.leaseId !== terminalLeaseId ||
|
|
3708
|
-
identity.connectionId !== terminalProviderIdentity.connectionId ||
|
|
3709
|
-
identity.connectionGeneration !== terminalProviderIdentity.connectionGeneration ||
|
|
3710
|
-
identity.fence !== terminalProviderIdentity.fence
|
|
3711
|
-
)
|
|
3712
|
-
throw new Error("terminal provider lease mismatch");
|
|
3713
|
-
terminalPublicationFailedLeaseId = terminalLeaseId;
|
|
3714
|
-
host!.quarantineProviderLease(identity, "terminal cleanup ownership uncertain");
|
|
3715
|
-
},
|
|
3716
|
-
async createTerminal(params) {
|
|
3717
|
-
return withTerminalCreationAdmission(async () => {
|
|
3718
|
-
if (terminalPublicationFailedLeaseId === terminalLeaseId)
|
|
3719
|
-
throw new Error("terminal provider quarantined after publication failure");
|
|
3720
|
-
if (params.signal?.aborted) throw new Error("terminal creation aborted");
|
|
3721
|
-
const creationAbort = new AbortController();
|
|
3722
|
-
const creationSignal = params.signal
|
|
3723
|
-
? AbortSignal.any([params.signal, creationAbort.signal])
|
|
3724
|
-
: creationAbort.signal;
|
|
3725
|
-
let created: unknown;
|
|
3726
|
-
let creationTimedOut = false;
|
|
3727
|
-
try {
|
|
3728
|
-
created = await Promise.race([
|
|
3729
|
-
requestTerminal(
|
|
3730
|
-
"terminal.create",
|
|
3731
|
-
{
|
|
3732
|
-
command: params.command,
|
|
3733
|
-
...(params.args ? { args: params.args } : {}),
|
|
3734
|
-
...(params.env ? { env: params.env } : {}),
|
|
3735
|
-
...(params.cwd ? { cwd: params.cwd } : {}),
|
|
3736
|
-
...(typeof params.outputByteLimit === "number"
|
|
3737
|
-
? { outputByteLimit: params.outputByteLimit }
|
|
3738
|
-
: {}),
|
|
3739
|
-
},
|
|
3740
|
-
creationSignal,
|
|
3741
|
-
),
|
|
3742
|
-
Bun.sleep(500).then(() => {
|
|
3743
|
-
creationTimedOut = true;
|
|
3744
|
-
creationAbort.abort();
|
|
3745
|
-
throw new Error("terminal creation did not settle within 500ms");
|
|
3746
|
-
}),
|
|
3747
|
-
]);
|
|
3748
|
-
} catch (error) {
|
|
3749
|
-
if (
|
|
3750
|
-
creationSignal.aborted ||
|
|
3751
|
-
(error instanceof ReverseLeaseError && error.code === "payload_too_large")
|
|
3752
|
-
)
|
|
3753
|
-
terminalPublicationFailedLeaseId = terminalLeaseId;
|
|
3754
|
-
if (creationTimedOut) throw new Error("terminal creation did not settle within 500ms");
|
|
3755
|
-
throw error;
|
|
3756
|
-
} finally {
|
|
3757
|
-
creationAbort.abort();
|
|
3758
|
-
}
|
|
3759
|
-
const terminalId =
|
|
3760
|
-
created &&
|
|
3761
|
-
typeof created === "object" &&
|
|
3762
|
-
typeof (created as { terminalId?: unknown }).terminalId === "string"
|
|
3763
|
-
? (created as { terminalId: string }).terminalId
|
|
3764
|
-
: undefined;
|
|
3765
|
-
if (!terminalId) {
|
|
3766
|
-
terminalPublicationFailedLeaseId = terminalLeaseId;
|
|
3767
|
-
throw new Error("terminal provider returned an invalid create response");
|
|
3768
|
-
}
|
|
3769
|
-
const publicationAbort = new AbortController();
|
|
3770
|
-
const publicationSignal = params.signal
|
|
3771
|
-
? AbortSignal.any([params.signal, publicationAbort.signal])
|
|
3772
|
-
: publicationAbort.signal;
|
|
3773
|
-
try {
|
|
3774
|
-
await Promise.race([
|
|
3775
|
-
requestTerminal(
|
|
3776
|
-
"terminal.publish",
|
|
3777
|
-
{ toolCallId: params.toolCallId, terminalId },
|
|
3778
|
-
publicationSignal,
|
|
3779
|
-
),
|
|
3780
|
-
Bun.sleep(500).then(() => {
|
|
3781
|
-
publicationAbort.abort();
|
|
3782
|
-
throw new Error("terminal publication did not settle within 500ms");
|
|
3783
|
-
}),
|
|
3784
|
-
]);
|
|
3785
|
-
} catch (error) {
|
|
3786
|
-
const killed = await settleTerminalCleanup("terminal.kill", terminalId);
|
|
3787
|
-
const released = await settleTerminalCleanup("terminal.release", terminalId);
|
|
3788
|
-
if (!params.signal?.aborted || !killed || !released)
|
|
3789
|
-
terminalPublicationFailedLeaseId = terminalLeaseId;
|
|
3790
|
-
throw error;
|
|
3791
|
-
} finally {
|
|
3792
|
-
publicationAbort.abort();
|
|
3793
|
-
}
|
|
3794
|
-
return {
|
|
3795
|
-
terminalId,
|
|
3796
|
-
providerLeaseId: terminalLeaseId,
|
|
3797
|
-
providerConnectionId: terminalProviderIdentity.connectionId,
|
|
3798
|
-
providerConnectionGeneration: terminalProviderIdentity.connectionGeneration,
|
|
3799
|
-
providerFence: terminalProviderIdentity.fence,
|
|
3800
|
-
async currentOutput() {
|
|
3801
|
-
const output = await requestTerminal("terminal.output", { terminalId });
|
|
3802
|
-
if (
|
|
3803
|
-
!output ||
|
|
3804
|
-
typeof output !== "object" ||
|
|
3805
|
-
typeof (output as { output?: unknown }).output !== "string" ||
|
|
3806
|
-
typeof (output as { truncated?: unknown }).truncated !== "boolean"
|
|
3807
|
-
) {
|
|
3808
|
-
throw new Error("terminal provider returned an invalid output response");
|
|
3809
|
-
}
|
|
3810
|
-
const response = output as { output: string; truncated: boolean; exitStatus?: unknown };
|
|
3811
|
-
return {
|
|
3812
|
-
output: response.output,
|
|
3813
|
-
truncated: response.truncated,
|
|
3814
|
-
exitStatus:
|
|
3815
|
-
response.exitStatus && typeof response.exitStatus === "object"
|
|
3816
|
-
? (response.exitStatus as { exitCode?: number | null; signal?: string | null })
|
|
3817
|
-
: null,
|
|
3818
|
-
};
|
|
3819
|
-
},
|
|
3820
|
-
async waitForExit() {
|
|
3821
|
-
const status = await requestTerminal("terminal.waitForExit", { terminalId });
|
|
3822
|
-
if (!status || typeof status !== "object")
|
|
3823
|
-
throw new Error("terminal provider returned an invalid exit response");
|
|
3824
|
-
const response = status as { exitCode?: number | null; signal?: string | null };
|
|
3825
|
-
return { exitCode: response.exitCode ?? null, signal: response.signal ?? null };
|
|
3826
|
-
},
|
|
3827
|
-
async kill() {
|
|
3828
|
-
await requestTerminal("terminal.kill", { terminalId }, undefined, true);
|
|
3829
|
-
},
|
|
3830
|
-
async release() {
|
|
3831
|
-
await requestTerminal("terminal.release", { terminalId }, undefined, true);
|
|
3832
|
-
},
|
|
3833
|
-
};
|
|
3834
|
-
});
|
|
3835
|
-
},
|
|
3836
|
-
};
|
|
3837
|
-
refreshSdkClientBridge();
|
|
3838
|
-
return;
|
|
3839
|
-
}
|
|
3840
3609
|
if (capability !== "fs") return;
|
|
3841
3610
|
// Only advertise the methods the client actually declared; a read-only client
|
|
3842
3611
|
// must keep using the local write path instead of failing against the bridge.
|
|
@@ -3868,17 +3637,11 @@ export function createNotificationsExtension(
|
|
|
3868
3637
|
};
|
|
3869
3638
|
if (!canRead) delete bridge.readTextFile;
|
|
3870
3639
|
if (!canWrite) delete bridge.writeTextFile;
|
|
3871
|
-
|
|
3872
|
-
refreshSdkClientBridge();
|
|
3640
|
+
ctx.setSdkClientBridge?.(bridge);
|
|
3873
3641
|
};
|
|
3874
3642
|
const removeProviderDefinitions = (capability: string) => {
|
|
3875
3643
|
if (capability === "permission") ctx.setSdkPermissionProvider?.(undefined);
|
|
3876
|
-
if (capability === "fs")
|
|
3877
|
-
if (capability === "terminal") {
|
|
3878
|
-
terminalClientBridge = undefined;
|
|
3879
|
-
terminalPublicationFailedLeaseId = undefined;
|
|
3880
|
-
}
|
|
3881
|
-
if (capability === "fs" || capability === "terminal") refreshSdkClientBridge();
|
|
3644
|
+
if (capability === "fs") ctx.setSdkClientBridge?.(undefined);
|
|
3882
3645
|
if (capability === "ui") {
|
|
3883
3646
|
disposeUiAnswerSource?.();
|
|
3884
3647
|
disposeUiAnswerSource = undefined;
|
|
@@ -3886,12 +3649,6 @@ export function createNotificationsExtension(
|
|
|
3886
3649
|
};
|
|
3887
3650
|
|
|
3888
3651
|
const hostCapCache = new Map<string, ReadonlySet<string>>();
|
|
3889
|
-
const MAX_PENDING_PROVIDER_RECLAIMS = 64;
|
|
3890
|
-
const pendingProviderReclaims = new Map<
|
|
3891
|
-
string,
|
|
3892
|
-
Array<{ connectionId: string; connectionGeneration: number; frame: Record<string, unknown> }>
|
|
3893
|
-
>();
|
|
3894
|
-
let pendingProviderReclaimCount = 0;
|
|
3895
3652
|
|
|
3896
3653
|
const configOverrides = new Map<string, unknown>();
|
|
3897
3654
|
const configRevision = { current: 0 };
|
|
@@ -4445,8 +4202,7 @@ export function createNotificationsExtension(
|
|
|
4445
4202
|
};
|
|
4446
4203
|
|
|
4447
4204
|
const sendSdkFrame = (connectionId: string, frame: Record<string, unknown>) => {
|
|
4448
|
-
|
|
4449
|
-
if (!cancellation && (extensionShuttingDown || runtime?.stopping || runtimes.get(id) !== runtime)) {
|
|
4205
|
+
if (extensionShuttingDown || runtime?.stopping || runtimes.get(id) !== runtime) {
|
|
4450
4206
|
abandonPromptResponse(connectionId, frame);
|
|
4451
4207
|
return;
|
|
4452
4208
|
}
|
|
@@ -4465,15 +4221,13 @@ export function createNotificationsExtension(
|
|
|
4465
4221
|
abandonPromptResponse(connectionId, frame);
|
|
4466
4222
|
throw error;
|
|
4467
4223
|
}
|
|
4468
|
-
syntheticSeamAdmissions = Math.max(0, syntheticSeamAdmissions - 1);
|
|
4469
|
-
host?.handleDisconnect(connectionId);
|
|
4470
4224
|
return;
|
|
4471
4225
|
}
|
|
4472
4226
|
try {
|
|
4473
4227
|
server.sendTo(connectionId, json);
|
|
4474
4228
|
} catch (error) {
|
|
4229
|
+
logger.warn(`sdk: directed response delivery failed for ${connectionId}: ${String(error)}`);
|
|
4475
4230
|
abandonPromptResponse(connectionId, frame);
|
|
4476
|
-
logger.warn(`sdk: directed response delivery rejected for ${connectionId}: ${String(error)}`);
|
|
4477
4231
|
throw error;
|
|
4478
4232
|
}
|
|
4479
4233
|
};
|
|
@@ -4486,13 +4240,6 @@ export function createNotificationsExtension(
|
|
|
4486
4240
|
connectionCapabilities: connectionId => hostCapCache.get(connectionId),
|
|
4487
4241
|
installProviderDefinitions,
|
|
4488
4242
|
onProviderDefinitionsRemoved: removeProviderDefinitions,
|
|
4489
|
-
onProviderLeaseRegistered: (lease, connectionGeneration) => {
|
|
4490
|
-
providerLeaseIdentities.set(lease.leaseId, {
|
|
4491
|
-
connectionId: lease.connectionId,
|
|
4492
|
-
connectionGeneration,
|
|
4493
|
-
fence: `${lease.connectionId}:${connectionGeneration}:${lease.leaseId}`,
|
|
4494
|
-
});
|
|
4495
|
-
},
|
|
4496
4243
|
onFrame: handler => {
|
|
4497
4244
|
inboundSdkFrame = handler;
|
|
4498
4245
|
return () => {
|
|
@@ -4500,11 +4247,6 @@ export function createNotificationsExtension(
|
|
|
4500
4247
|
};
|
|
4501
4248
|
},
|
|
4502
4249
|
onRequest: options.onSdkRequest,
|
|
4503
|
-
onFrameOverflow: connectionId => {
|
|
4504
|
-
fencedConnections.add(connectionId);
|
|
4505
|
-
controlSurface.cancelPendingPreflightsForConnection(connectionId);
|
|
4506
|
-
host?.handleDisconnect(connectionId);
|
|
4507
|
-
},
|
|
4508
4250
|
beforeControlResponse: async (_connectionId, request, response, sendTerminal) => {
|
|
4509
4251
|
if (typeof request.operation !== "string" || !identityControlOperations.has(request.operation)) return;
|
|
4510
4252
|
const pending = deferredIdentityRotation;
|
|
@@ -4796,47 +4538,6 @@ export function createNotificationsExtension(
|
|
|
4796
4538
|
server.sendTo(connectionId, JSON.stringify({ type: responseType, id, ok: false, error }));
|
|
4797
4539
|
} catch {}
|
|
4798
4540
|
};
|
|
4799
|
-
const queueExpectedProviderReclaim = (connectionId: string, frame: Record<string, unknown>): boolean => {
|
|
4800
|
-
if (frame.type !== "register_provider") return false;
|
|
4801
|
-
const capability = typeof frame.capability === "string" ? frame.capability : undefined;
|
|
4802
|
-
const expectedLeaseId = typeof frame.expectedLeaseId === "string" ? frame.expectedLeaseId : undefined;
|
|
4803
|
-
if (!capability || !expectedLeaseId) return false;
|
|
4804
|
-
const lease = host?.reverse.getLease(capability);
|
|
4805
|
-
if (!lease || lease.connectionId === connectionId || lease.leaseId !== expectedLeaseId) return false;
|
|
4806
|
-
if (pendingProviderReclaimCount >= MAX_PENDING_PROVIDER_RECLAIMS) return false;
|
|
4807
|
-
const queued = pendingProviderReclaims.get(lease.connectionId) ?? [];
|
|
4808
|
-
if (typeof frame.id === "string" && queued.some(entry => entry.frame.id === frame.id)) return true;
|
|
4809
|
-
const currentGeneration = host?.connectionGeneration(connectionId) ?? 0;
|
|
4810
|
-
if (currentGeneration <= 0) return false;
|
|
4811
|
-
queued.push({ connectionId, connectionGeneration: currentGeneration, frame });
|
|
4812
|
-
pendingProviderReclaims.set(lease.connectionId, queued);
|
|
4813
|
-
pendingProviderReclaimCount++;
|
|
4814
|
-
return true;
|
|
4815
|
-
};
|
|
4816
|
-
const purgeProviderReclaimsForClaimant = (connectionId: string): void => {
|
|
4817
|
-
for (const [incumbentId, queued] of pendingProviderReclaims) {
|
|
4818
|
-
const retained = queued.filter(entry => entry.connectionId !== connectionId);
|
|
4819
|
-
pendingProviderReclaimCount = Math.max(0, pendingProviderReclaimCount - (queued.length - retained.length));
|
|
4820
|
-
if (retained.length === 0) pendingProviderReclaims.delete(incumbentId);
|
|
4821
|
-
else pendingProviderReclaims.set(incumbentId, retained);
|
|
4822
|
-
}
|
|
4823
|
-
};
|
|
4824
|
-
const drainProviderReclaims = (closedConnectionId: string): void => {
|
|
4825
|
-
const queued = pendingProviderReclaims.get(closedConnectionId);
|
|
4826
|
-
if (!queued) return;
|
|
4827
|
-
pendingProviderReclaims.delete(closedConnectionId);
|
|
4828
|
-
pendingProviderReclaimCount = Math.max(0, pendingProviderReclaimCount - queued.length);
|
|
4829
|
-
for (const entry of queued) {
|
|
4830
|
-
if (
|
|
4831
|
-
initializedRuntime.inboundFenced ||
|
|
4832
|
-
initializedRuntime.stopping ||
|
|
4833
|
-
runtimes.get(id) !== initializedRuntime
|
|
4834
|
-
)
|
|
4835
|
-
continue;
|
|
4836
|
-
if (!host?.isConnectionOpen(entry.connectionId, entry.connectionGeneration)) continue;
|
|
4837
|
-
inboundSdkFrame?.(entry.connectionId, entry.frame);
|
|
4838
|
-
}
|
|
4839
|
-
};
|
|
4840
4541
|
try {
|
|
4841
4542
|
server.onSdkFrame((err, inbound) => {
|
|
4842
4543
|
if (err || !inbound) return;
|
|
@@ -4844,13 +4545,6 @@ export function createNotificationsExtension(
|
|
|
4844
4545
|
const frame = JSON.parse(inbound.json) as unknown;
|
|
4845
4546
|
if (!frame || typeof frame !== "object") return;
|
|
4846
4547
|
const typedFrame = frame as Record<string, unknown>;
|
|
4847
|
-
if (!host.isConnectionOpen(inbound.connectionId)) {
|
|
4848
|
-
if (host.connectionGeneration(inbound.connectionId) > 0) {
|
|
4849
|
-
sendEndpointStale(inbound.connectionId, typedFrame);
|
|
4850
|
-
return;
|
|
4851
|
-
}
|
|
4852
|
-
host.handleConnect(inbound.connectionId);
|
|
4853
|
-
}
|
|
4854
4548
|
if (inbound.connectionId && fencedConnections.has(inbound.connectionId)) {
|
|
4855
4549
|
sendEndpointStale(inbound.connectionId, typedFrame);
|
|
4856
4550
|
return;
|
|
@@ -4859,7 +4553,6 @@ export function createNotificationsExtension(
|
|
|
4859
4553
|
sendEndpointStale(inbound.connectionId, typedFrame);
|
|
4860
4554
|
return;
|
|
4861
4555
|
}
|
|
4862
|
-
if (queueExpectedProviderReclaim(inbound.connectionId, typedFrame)) return;
|
|
4863
4556
|
if (typedFrame.type === "ephemeral_turn" || typedFrame.type === "ephemeral_turn_cancel") return;
|
|
4864
4557
|
if (typedFrame.type === "event_replay") {
|
|
4865
4558
|
const capabilities = Array.isArray(typedFrame.capabilities) ? typedFrame.capabilities : [];
|
|
@@ -4887,16 +4580,11 @@ export function createNotificationsExtension(
|
|
|
4887
4580
|
server.onConnectionClose((_err, connectionId) => {
|
|
4888
4581
|
if (!connectionId) return;
|
|
4889
4582
|
controlSurface.cancelPendingPreflightsForConnection(connectionId);
|
|
4890
|
-
|
|
4891
|
-
host.handleDisconnect(connectionId);
|
|
4892
|
-
} finally {
|
|
4893
|
-
drainProviderReclaims(connectionId);
|
|
4894
|
-
}
|
|
4583
|
+
host.handleDisconnect(connectionId);
|
|
4895
4584
|
hostCapCache.delete(connectionId);
|
|
4896
4585
|
// The socket is gone, so its fence has nothing left to refuse. Dropping the
|
|
4897
4586
|
// entry keeps the set bounded by live connections instead of growing forever.
|
|
4898
4587
|
fencedConnections.delete(connectionId);
|
|
4899
|
-
purgeProviderReclaimsForClaimant(connectionId);
|
|
4900
4588
|
// Deliberate deviation from the plan's "claim prompt_failed on old-owner
|
|
4901
4589
|
// disconnect": that would break the shipped Q26 reconnect contract, where a
|
|
4902
4590
|
// client may drop its socket and reconcile the still-running prompt without
|
|
@@ -5177,9 +4865,7 @@ export function createNotificationsExtension(
|
|
|
5177
4865
|
!suspendedRuntime.stopping &&
|
|
5178
4866
|
!suspendedRuntime.policySuspended
|
|
5179
4867
|
)
|
|
5180
|
-
|
|
5181
|
-
syntheticSeamAdmissions++;
|
|
5182
|
-
inboundSdkFrame?.(`seam:${inbound.requestId ?? "notification"}`, frame);
|
|
4868
|
+
inboundSdkFrame?.(`seam:${inbound.requestId ?? "notification"}`, frame);
|
|
5183
4869
|
});
|
|
5184
4870
|
}
|
|
5185
4871
|
}
|
|
@@ -5188,8 +4874,6 @@ export function createNotificationsExtension(
|
|
|
5188
4874
|
if (inbound.kind === "control_command") {
|
|
5189
4875
|
const frame = sdkInboundFrame(inbound.commandJson);
|
|
5190
4876
|
if (frame) {
|
|
5191
|
-
if (syntheticSeamAdmissions >= MAX_SYNTHETIC_SEAM_ADMISSIONS) return;
|
|
5192
|
-
syntheticSeamAdmissions++;
|
|
5193
4877
|
inboundSdkFrame?.(`seam:${inbound.requestId ?? "notification"}`, frame);
|
|
5194
4878
|
return;
|
|
5195
4879
|
}
|
|
@@ -49,11 +49,9 @@ export const NOTIFICATION_PROTOCOL_VERSION = 3;
|
|
|
49
49
|
* user-created topic adoption (forum-topic folder picker). Generation 36 bound
|
|
50
50
|
* managed-session replacement to exact native filesystem authority; generation
|
|
51
51
|
* 37 retires that binding (revert of #3489, which stalled POSIX artifact
|
|
52
|
-
* cleanup); generation 38 binds exact cleanup to parent and link-count authority
|
|
53
|
-
* generation 39 hardens exact Bash process-tree ownership, settlement, and
|
|
54
|
-
* descendant cleanup authority.
|
|
52
|
+
* cleanup); generation 38 binds exact cleanup to parent and link-count authority.
|
|
55
53
|
*/
|
|
56
|
-
export const DAEMON_GENERATION =
|
|
54
|
+
export const DAEMON_GENERATION = 38;
|
|
57
55
|
|
|
58
56
|
/**
|
|
59
57
|
* Serving-compatibility boundary for daemon lifecycle requests. Epoch 1 covers
|