@gajae-code/coding-agent 0.12.4 → 0.12.6
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 +36 -0
- package/README.md +4 -1
- package/dist/types/async/job-manager.d.ts +9 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +10 -8
- package/dist/types/extensibility/extensions/runner.d.ts +9 -7
- package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
- package/dist/types/gjc-runtime/team-store.d.ts +0 -4
- package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +83 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +57 -4
- package/dist/types/internal-urls/registry-helpers.d.ts +18 -0
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
- package/dist/types/modes/components/plugin-settings.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +18 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -2
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -3
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
- package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
- package/dist/types/sdk/host/host.d.ts +23 -2
- package/dist/types/sdk/host/reverse-leases.d.ts +7 -4
- package/dist/types/session/agent-session.d.ts +5 -4
- package/dist/types/session/artifacts.d.ts +8 -5
- package/dist/types/session/attempt-record-store.d.ts +19 -0
- package/dist/types/session/client-bridge.d.ts +14 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -23
- package/dist/types/session/messages.d.ts +4 -1
- package/dist/types/session/session-manager.d.ts +7 -5
- package/dist/types/session/session-storage.d.ts +8 -0
- package/dist/types/session/streaming-output.d.ts +32 -0
- package/dist/types/task/executor.d.ts +1 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/receipt.d.ts +3 -0
- package/dist/types/task/types.d.ts +194 -0
- package/dist/types/task/worktree.d.ts +13 -6
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +3 -0
- package/dist/types/tools/output-meta.d.ts +37 -5
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/tool-result.d.ts +1 -0
- package/dist/types/utils/artifact-id.d.ts +2 -0
- package/dist/types/utils/git.d.ts +1 -0
- package/package.json +8 -8
- package/scripts/capture-sticky-viewport-showcase.ts +131 -5
- package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
- package/scripts/verify-sticky-viewport-showcase.ts +620 -93
- package/src/async/job-manager.ts +13 -5
- package/src/config/model-profiles.ts +8 -0
- package/src/config/settings-schema.ts +17 -3
- package/src/cursor.ts +20 -13
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +5 -2
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +425 -151
- package/src/extensibility/extensions/runner.ts +80 -6
- package/src/extensibility/extensions/wrapper.ts +22 -15
- package/src/extensibility/runtime-skill-discovery.ts +4 -6
- package/src/gjc-runtime/launch-tmux.ts +309 -293
- package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
- package/src/gjc-runtime/psmux-detect.ts +52 -6
- package/src/gjc-runtime/team-launch.ts +50 -2
- package/src/gjc-runtime/team-runtime.ts +716 -251
- package/src/gjc-runtime/team-store.ts +25 -7
- package/src/gjc-runtime/team-workers.ts +161 -1
- package/src/gjc-runtime/tmux-common.ts +16 -4
- package/src/gjc-runtime/tmux-gc.ts +37 -5
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
- package/src/gjc-runtime/tmux-provider-context.ts +451 -0
- package/src/gjc-runtime/tmux-sessions.ts +409 -156
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
- package/src/gjc-runtime/ultragoal-review-source.ts +378 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +640 -159
- package/src/gjc-runtime/workflow-manifest.generated.json +40 -1
- package/src/gjc-runtime/workflow-manifest.ts +12 -0
- package/src/internal-urls/agent-protocol.ts +98 -38
- package/src/internal-urls/artifact-protocol.ts +28 -17
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/internal-urls/local-protocol.ts +39 -18
- package/src/internal-urls/registry-helpers.ts +62 -0
- package/src/modes/acp/acp-agent.ts +51 -13
- package/src/modes/acp/acp-client-bridge.ts +154 -11
- package/src/modes/components/gjc-bundle-settings.ts +2 -0
- package/src/modes/components/plugin-settings.ts +5 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/monitor.md +4 -4
- package/src/sdk/acp/adapter.ts +200 -9
- package/src/sdk/broker/broker.ts +93 -10
- package/src/sdk/broker/lifecycle-ledger.ts +227 -11
- package/src/sdk/broker/lifecycle.ts +692 -98
- package/src/sdk/bus/chat-daemon-control.ts +33 -5
- package/src/sdk/bus/index.ts +347 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -3
- package/src/sdk/bus/telegram-daemon.ts +283 -44
- package/src/sdk/bus/topic-registry.ts +133 -10
- package/src/sdk/host/host.ts +187 -9
- package/src/sdk/host/reverse-leases.ts +249 -42
- package/src/sdk/session.ts +8 -6
- package/src/session/agent-session.ts +930 -354
- package/src/session/artifacts.ts +211 -74
- package/src/session/attempt-record-store.ts +106 -0
- package/src/session/client-bridge.ts +15 -0
- package/src/session/internal/managed-session-scope.ts +332 -198
- package/src/session/internal/managed-session-storage.ts +162 -450
- package/src/session/messages.ts +32 -7
- package/src/session/session-manager.ts +31 -28
- package/src/session/session-storage.ts +190 -53
- package/src/session/streaming-output.ts +198 -40
- package/src/task/executor.ts +5 -2
- package/src/task/index.ts +277 -64
- package/src/task/provider-retry-status.ts +4 -1
- package/src/task/receipt.ts +26 -2
- package/src/task/render.ts +42 -1
- package/src/task/types.ts +52 -0
- package/src/task/worktree.ts +122 -43
- package/src/tools/bash.ts +818 -239
- package/src/tools/eval.ts +99 -4
- package/src/tools/fetch.ts +77 -15
- package/src/tools/find.ts +7 -3
- package/src/tools/monitor.ts +7 -1
- package/src/tools/output-meta.ts +706 -116
- package/src/tools/search.ts +9 -4
- package/src/tools/subagent-render.ts +5 -2
- package/src/tools/subagent.ts +8 -0
- package/src/tools/tool-result.ts +1 -0
- package/src/utils/artifact-id.ts +6 -0
- package/src/utils/git.ts +2 -0
|
@@ -80,6 +80,11 @@ export interface LifecycleLedgerEntry {
|
|
|
80
80
|
endpointGeneration?: number;
|
|
81
81
|
responseDigest?: string;
|
|
82
82
|
response?: unknown;
|
|
83
|
+
unresolvedCleanupResponse?: unknown;
|
|
84
|
+
unresolvedCleanupResponseDigest?: string;
|
|
85
|
+
uncertainCleanupSessionId?: string;
|
|
86
|
+
uncertainCleanupSessionIds?: string[];
|
|
87
|
+
uncertainCleanupAllSessions?: true;
|
|
83
88
|
ts: number;
|
|
84
89
|
}
|
|
85
90
|
export type BeginResult =
|
|
@@ -95,6 +100,10 @@ export interface LifecycleLedgerLimits {
|
|
|
95
100
|
maxLineBytes?: number;
|
|
96
101
|
maxRows?: number;
|
|
97
102
|
}
|
|
103
|
+
function canonicalCleanupSessionId(value: unknown): value is string {
|
|
104
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value);
|
|
105
|
+
}
|
|
106
|
+
|
|
98
107
|
const DEFAULT_LIFECYCLE_LEDGER_LIMITS: Required<LifecycleLedgerLimits> = {
|
|
99
108
|
maxBytes: 64 * 1024 * 1024,
|
|
100
109
|
maxLineBytes: 8 * 1024 * 1024,
|
|
@@ -143,20 +152,71 @@ function canonicalJson(value: unknown): string {
|
|
|
143
152
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
144
153
|
const record = value as Record<string, unknown>;
|
|
145
154
|
return `{${Object.keys(record)
|
|
155
|
+
.filter(key => record[key] !== undefined)
|
|
146
156
|
.sort()
|
|
147
157
|
.map(key => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
|
|
148
158
|
.join(",")}}`;
|
|
149
159
|
}
|
|
160
|
+
function pendingCleanupSessionId(response: unknown): string | undefined {
|
|
161
|
+
if (!response || typeof response !== "object") return undefined;
|
|
162
|
+
const cleanup = (response as { error?: { cleanup?: { sessionId?: unknown } } }).error?.cleanup;
|
|
163
|
+
return canonicalCleanupSessionId(cleanup?.sessionId) ? cleanup.sessionId : undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function hasCleanupAuthorityShape(response: unknown): boolean {
|
|
167
|
+
if (!response || typeof response !== "object") return false;
|
|
168
|
+
const error = (response as { error?: unknown }).error;
|
|
169
|
+
return Boolean(error && typeof error === "object" && "cleanup" in error);
|
|
170
|
+
}
|
|
150
171
|
|
|
151
172
|
function hasValidTerminalDigests(entry: LifecycleLedgerEntry): boolean {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
173
|
+
const response = entry.response as { ok?: unknown; error?: { code?: unknown; cleanup?: unknown } } | undefined;
|
|
174
|
+
const cleanupPendingResponse =
|
|
175
|
+
response?.ok === false && response.error?.code === "cleanup_pending" && response.error.cleanup !== undefined;
|
|
176
|
+
const responseDigestRequired =
|
|
177
|
+
cleanupPendingResponse ||
|
|
178
|
+
((terminal(entry.state) || entry.state === "terminal_uncertain") &&
|
|
179
|
+
(entry.state !== "terminal_uncertain" || entry.response !== undefined));
|
|
180
|
+
if (cleanupPendingResponse) {
|
|
181
|
+
const cleanupSessionId = (response.error?.cleanup as { sessionId?: unknown }).sessionId;
|
|
182
|
+
if (!canonicalCleanupSessionId(entry.intendedSessionId) || cleanupSessionId !== entry.intendedSessionId)
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
const expectedResponseDigest =
|
|
186
|
+
entry.response === undefined
|
|
187
|
+
? undefined
|
|
188
|
+
: createHash("sha256")
|
|
189
|
+
.update(
|
|
190
|
+
canonicalJson(
|
|
191
|
+
cleanupPendingResponse
|
|
192
|
+
? { intendedSessionId: entry.intendedSessionId, response: entry.response }
|
|
193
|
+
: entry.response,
|
|
194
|
+
),
|
|
195
|
+
)
|
|
196
|
+
.digest("hex");
|
|
197
|
+
if (entry.responseDigest !== undefined) {
|
|
198
|
+
if (entry.response === undefined || entry.responseDigest !== expectedResponseDigest) return false;
|
|
199
|
+
} else if (responseDigestRequired) return false;
|
|
200
|
+
if (entry.unresolvedCleanupResponse !== undefined || entry.unresolvedCleanupResponseDigest !== undefined) {
|
|
201
|
+
const unresolvedResponse = entry.unresolvedCleanupResponse as {
|
|
202
|
+
error?: { cleanup?: { sessionId?: unknown } };
|
|
203
|
+
};
|
|
204
|
+
if (
|
|
205
|
+
entry.unresolvedCleanupResponse === undefined ||
|
|
206
|
+
typeof entry.unresolvedCleanupResponseDigest !== "string" ||
|
|
207
|
+
unresolvedResponse.error?.cleanup?.sessionId !== entry.intendedSessionId ||
|
|
208
|
+
entry.unresolvedCleanupResponseDigest !==
|
|
209
|
+
createHash("sha256")
|
|
210
|
+
.update(
|
|
211
|
+
canonicalJson({
|
|
212
|
+
intendedSessionId: entry.intendedSessionId,
|
|
213
|
+
response: entry.unresolvedCleanupResponse,
|
|
214
|
+
}),
|
|
215
|
+
)
|
|
216
|
+
.digest("hex")
|
|
217
|
+
)
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
160
220
|
if (!entry.durableEffects) return true;
|
|
161
221
|
const { digest, ...body } = entry.durableEffects;
|
|
162
222
|
return typeof digest === "string" && digest === createHash("sha256").update(canonicalJson(body)).digest("hex");
|
|
@@ -237,8 +297,31 @@ export class LifecycleLedger {
|
|
|
237
297
|
if (invalidHistory) {
|
|
238
298
|
await this.#quarantine(line);
|
|
239
299
|
invalidIdentities.add(entry.identity);
|
|
240
|
-
if (!syntheticUncertain.has(entry.identity))
|
|
241
|
-
|
|
300
|
+
if (!syntheticUncertain.has(entry.identity)) {
|
|
301
|
+
const uncertain = this.#uncertainFrom(prior ?? entry, prior !== undefined);
|
|
302
|
+
const nestedCleanupSessionId =
|
|
303
|
+
pendingCleanupSessionId(entry.unresolvedCleanupResponse) ??
|
|
304
|
+
pendingCleanupSessionId(entry.response) ??
|
|
305
|
+
entry.intendedSessionId;
|
|
306
|
+
if (canonicalCleanupSessionId(nestedCleanupSessionId))
|
|
307
|
+
uncertain.uncertainCleanupSessionId = nestedCleanupSessionId;
|
|
308
|
+
uncertain.uncertainCleanupSessionIds = [
|
|
309
|
+
entry.intendedSessionId,
|
|
310
|
+
pendingCleanupSessionId(entry.response),
|
|
311
|
+
pendingCleanupSessionId(entry.unresolvedCleanupResponse),
|
|
312
|
+
nestedCleanupSessionId,
|
|
313
|
+
].filter(
|
|
314
|
+
(candidate, index, candidates): candidate is string =>
|
|
315
|
+
canonicalCleanupSessionId(candidate) && candidates.indexOf(candidate) === index,
|
|
316
|
+
);
|
|
317
|
+
if (
|
|
318
|
+
hasCleanupAuthorityShape(entry.response) ||
|
|
319
|
+
hasCleanupAuthorityShape(entry.unresolvedCleanupResponse) ||
|
|
320
|
+
(entry.state === "terminal_uncertain" && entry.response === undefined)
|
|
321
|
+
)
|
|
322
|
+
uncertain.uncertainCleanupAllSessions = true;
|
|
323
|
+
syntheticUncertain.set(entry.identity, uncertain);
|
|
324
|
+
}
|
|
242
325
|
continue;
|
|
243
326
|
}
|
|
244
327
|
this.#entries.push(entry);
|
|
@@ -336,6 +419,45 @@ export class LifecycleLedger {
|
|
|
336
419
|
response.ok === false && response.error?.code === "cleanup_pending" && response.error.cleanup !== undefined
|
|
337
420
|
);
|
|
338
421
|
}
|
|
422
|
+
#isSessionDeleteCleanupPending(entry: LifecycleLedgerEntry): boolean {
|
|
423
|
+
if (!this.#isCleanupPending(entry)) return false;
|
|
424
|
+
const response = entry.response as {
|
|
425
|
+
error?: {
|
|
426
|
+
cleanup?: {
|
|
427
|
+
phase?: unknown;
|
|
428
|
+
sessionId?: unknown;
|
|
429
|
+
sessionsRoot?: unknown;
|
|
430
|
+
transcriptPath?: unknown;
|
|
431
|
+
cwd?: unknown;
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
const cleanup = response.error?.cleanup;
|
|
436
|
+
return (
|
|
437
|
+
(cleanup?.phase === "artifacts" || cleanup?.phase === "transcript" || cleanup?.phase === "metadata") &&
|
|
438
|
+
typeof cleanup.sessionId === "string" &&
|
|
439
|
+
typeof cleanup.sessionsRoot === "string" &&
|
|
440
|
+
typeof cleanup.transcriptPath === "string" &&
|
|
441
|
+
typeof cleanup.cwd === "string"
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
#matchesSessionDeleteTarget(
|
|
446
|
+
entry: LifecycleLedgerEntry,
|
|
447
|
+
target: { sessionId: string; sessionsRoot?: string; transcriptPath: string; cwd: string },
|
|
448
|
+
): boolean {
|
|
449
|
+
if (!this.#isSessionDeleteCleanupPending(entry)) return false;
|
|
450
|
+
const response = entry.response as {
|
|
451
|
+
error: { cleanup: { sessionId: string; sessionsRoot: string; transcriptPath: string; cwd: string } };
|
|
452
|
+
};
|
|
453
|
+
const cleanup = response.error.cleanup;
|
|
454
|
+
return (
|
|
455
|
+
cleanup.sessionId === target.sessionId &&
|
|
456
|
+
(target.sessionsRoot === undefined || cleanup.sessionsRoot === target.sessionsRoot) &&
|
|
457
|
+
cleanup.transcriptPath === target.transcriptPath &&
|
|
458
|
+
cleanup.cwd === target.cwd
|
|
459
|
+
);
|
|
460
|
+
}
|
|
339
461
|
|
|
340
462
|
#uncertainFrom(entry: LifecycleLedgerEntry, trusted = true): LifecycleLedgerEntry {
|
|
341
463
|
if (trusted) return { ...entry, state: "terminal_uncertain", ts: Date.now() };
|
|
@@ -545,7 +667,37 @@ export class LifecycleLedger {
|
|
|
545
667
|
const previous = this.#byIdentity.get(identity);
|
|
546
668
|
if (!previous) throw new Error("Unknown lifecycle identity");
|
|
547
669
|
const next = { ...previous, ...fields, state, ts: Date.now() };
|
|
548
|
-
if (
|
|
670
|
+
if (this.#isCleanupPending(next)) {
|
|
671
|
+
next.unresolvedCleanupResponse = undefined;
|
|
672
|
+
next.unresolvedCleanupResponseDigest = undefined;
|
|
673
|
+
} else if (state === "terminal_ok") {
|
|
674
|
+
next.unresolvedCleanupResponse = undefined;
|
|
675
|
+
next.unresolvedCleanupResponseDigest = undefined;
|
|
676
|
+
} else if (this.#isCleanupPending(previous)) {
|
|
677
|
+
next.unresolvedCleanupResponse = previous.response;
|
|
678
|
+
next.unresolvedCleanupResponseDigest = createHash("sha256")
|
|
679
|
+
.update(
|
|
680
|
+
canonicalJson({
|
|
681
|
+
intendedSessionId: previous.intendedSessionId,
|
|
682
|
+
response: previous.response,
|
|
683
|
+
}),
|
|
684
|
+
)
|
|
685
|
+
.digest("hex");
|
|
686
|
+
}
|
|
687
|
+
if (this.#isCleanupPending(next)) {
|
|
688
|
+
const cleanupSessionId = (next.response as { error?: { cleanup?: { sessionId?: unknown } } }).error?.cleanup
|
|
689
|
+
?.sessionId;
|
|
690
|
+
if (!canonicalCleanupSessionId(cleanupSessionId))
|
|
691
|
+
throw new Error("Cleanup response lacks a canonical session fence");
|
|
692
|
+
if (next.intendedSessionId === undefined) next.intendedSessionId = cleanupSessionId;
|
|
693
|
+
else if (next.intendedSessionId !== cleanupSessionId)
|
|
694
|
+
throw new Error("Cleanup response session does not match its outer lifecycle fence");
|
|
695
|
+
next.responseDigest = createHash("sha256")
|
|
696
|
+
.update(canonicalJson({ intendedSessionId: next.intendedSessionId, response: next.response }))
|
|
697
|
+
.digest("hex");
|
|
698
|
+
} else if (fields.response !== undefined && fields.responseDigest === undefined)
|
|
699
|
+
next.responseDigest = createHash("sha256").update(canonicalJson(next.response)).digest("hex");
|
|
700
|
+
else if (
|
|
549
701
|
(terminal(state) || state === "terminal_uncertain") &&
|
|
550
702
|
next.response !== undefined &&
|
|
551
703
|
next.responseDigest === undefined
|
|
@@ -577,6 +729,70 @@ export class LifecycleLedger {
|
|
|
577
729
|
}
|
|
578
730
|
}
|
|
579
731
|
}
|
|
732
|
+
findCleanupPendingByDeleteTarget(
|
|
733
|
+
target: { sessionId: string; sessionsRoot?: string; transcriptPath: string; cwd: string },
|
|
734
|
+
excludingIdentity: string,
|
|
735
|
+
): LifecycleLedgerEntry | undefined {
|
|
736
|
+
let latestPending: LifecycleLedgerEntry | undefined;
|
|
737
|
+
for (const current of this.#byIdentity.values()) {
|
|
738
|
+
if (
|
|
739
|
+
current.identity === excludingIdentity ||
|
|
740
|
+
current.state === "terminal_ok" ||
|
|
741
|
+
current.intendedSessionId !== target.sessionId
|
|
742
|
+
)
|
|
743
|
+
continue;
|
|
744
|
+
if (current.unresolvedCleanupResponse !== undefined) {
|
|
745
|
+
const retained = { ...current, response: current.unresolvedCleanupResponse };
|
|
746
|
+
if (this.#matchesSessionDeleteTarget(retained, target)) {
|
|
747
|
+
if (!latestPending || retained.ts > latestPending.ts) latestPending = retained;
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
for (let index = this.#entries.length - 1; index >= 0; index -= 1) {
|
|
752
|
+
const historical = this.#entries[index];
|
|
753
|
+
if (
|
|
754
|
+
!historical ||
|
|
755
|
+
historical.identity !== current.identity ||
|
|
756
|
+
historical.intendedSessionId !== target.sessionId ||
|
|
757
|
+
!this.#matchesSessionDeleteTarget(historical, target)
|
|
758
|
+
)
|
|
759
|
+
continue;
|
|
760
|
+
if (!latestPending || historical.ts > latestPending.ts) latestPending = historical;
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return latestPending;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
findCleanupPendingBySessionId(sessionId: string, excludingIdentity: string): LifecycleLedgerEntry | undefined {
|
|
768
|
+
for (const current of this.#byIdentity.values()) {
|
|
769
|
+
if (current.identity === excludingIdentity) continue;
|
|
770
|
+
if (pendingCleanupSessionId(current.response) === sessionId) return current;
|
|
771
|
+
if (pendingCleanupSessionId(current.unresolvedCleanupResponse) === sessionId) return current;
|
|
772
|
+
}
|
|
773
|
+
return undefined;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
hasUncertainCleanupForSession(sessionId: string, excludingIdentity: string): boolean {
|
|
777
|
+
for (const current of this.#byIdentity.values()) {
|
|
778
|
+
if (current.state !== "terminal_uncertain") continue;
|
|
779
|
+
if (current.uncertainCleanupAllSessions === true) return true;
|
|
780
|
+
if (current.response === undefined) return true;
|
|
781
|
+
if (current.identity === excludingIdentity) continue;
|
|
782
|
+
const fencedSessions = [
|
|
783
|
+
current.uncertainCleanupSessionId,
|
|
784
|
+
current.intendedSessionId,
|
|
785
|
+
pendingCleanupSessionId(current.response),
|
|
786
|
+
pendingCleanupSessionId(current.unresolvedCleanupResponse),
|
|
787
|
+
...(current.uncertainCleanupSessionIds ?? []),
|
|
788
|
+
];
|
|
789
|
+
const boundSessions = fencedSessions.filter((value): value is string => typeof value === "string");
|
|
790
|
+
if (boundSessions.length === 0) return true;
|
|
791
|
+
if (fencedSessions.includes(sessionId)) return true;
|
|
792
|
+
}
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
|
|
580
796
|
get(identity: string): LifecycleLedgerEntry | undefined {
|
|
581
797
|
return this.#byIdentity.get(identity);
|
|
582
798
|
}
|