@gajae-code/coding-agent 0.12.0 → 0.12.2
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 +111 -0
- package/dist/types/async/job-manager.d.ts +17 -3
- package/dist/types/config/settings-schema.d.ts +56 -2
- package/dist/types/exa/mcp-client.d.ts +10 -1
- package/dist/types/exec/bash-executor.d.ts +45 -12
- package/dist/types/extensibility/extensions/types.d.ts +16 -2
- package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
- package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
- package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
- package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
- package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-review-conflicts.d.ts +114 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +23 -3
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +25 -0
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +10 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +37 -80
- package/dist/types/modes/acp/acp-agent.d.ts +2 -19
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +16 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +18 -0
- package/dist/types/modes/components/settings-selector.d.ts +9 -0
- package/dist/types/modes/components/status-line/gh.d.ts +5 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +1 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +9 -0
- package/dist/types/modes/theme/theme.d.ts +3 -1
- package/dist/types/modes/types.d.ts +32 -0
- package/dist/types/reminders/star-reminder.d.ts +3 -13
- package/dist/types/runtime-mcp/manager.d.ts +1 -0
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/config.d.ts +6 -2
- package/dist/types/sdk/bus/index.d.ts +29 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +7 -3
- package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
- package/dist/types/sdk/bus/rich-render.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
- package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
- package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
- package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/prompt-status.d.ts +23 -3
- package/dist/types/sdk/session.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +40 -2
- package/dist/types/session/artifacts.d.ts +8 -0
- package/dist/types/session/blob-store.d.ts +57 -4
- package/dist/types/session/internal/managed-session-storage.d.ts +30 -4
- package/dist/types/session/session-manager.d.ts +23 -6
- package/dist/types/session/session-storage.d.ts +4 -0
- package/dist/types/session/streaming-output.d.ts +36 -7
- package/dist/types/setup/credential-import.d.ts +12 -0
- package/dist/types/skill-state/active-state.d.ts +12 -0
- package/dist/types/skill-state/workflow-hud.d.ts +6 -0
- package/dist/types/task/executor.d.ts +3 -2
- package/dist/types/task/index.d.ts +7 -1
- package/dist/types/task/output-manager.d.ts +1 -0
- package/dist/types/task/provider-retry-status.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +9 -1
- package/dist/types/task/types.d.ts +23 -0
- package/dist/types/tools/ask-answer-registry.d.ts +2 -2
- package/dist/types/tools/bash-interactive.d.ts +4 -1
- package/dist/types/tools/bash.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +4 -0
- package/dist/types/tools/index.d.ts +6 -0
- package/dist/types/tools/output-meta.d.ts +23 -2
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read.d.ts +0 -6
- package/dist/types/tools/review.d.ts +5 -7
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/utils/gh.d.ts +13 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +2 -0
- package/dist/types/web/search/providers/searxng.d.ts +7 -0
- package/dist/types/web/search/providers/xai.d.ts +2 -0
- package/package.json +10 -8
- package/scripts/acp-conformance-agent.ts +218 -0
- package/scripts/capture-gjc-bundle-settings.ts +306 -0
- package/scripts/dogfood-ralplan-review-conflicts.ts +272 -0
- package/scripts/generate-sdk-operation-inventory.ts +5 -0
- package/scripts/resident-memory-bench.ts +1341 -0
- package/scripts/run-acp-conformance.ts +290 -0
- package/src/async/job-manager.ts +99 -39
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/plugin-cli.ts +176 -18
- package/src/commands/team.ts +19 -1
- package/src/config/file-lock.ts +11 -2
- package/src/config/settings-schema.ts +53 -2
- package/src/coordinator-mcp/question-gate-codec.ts +1 -1
- package/src/coordinator-mcp/server.ts +5 -12
- package/src/debug/index.ts +42 -17
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +25 -20
- package/src/defaults/gjc/skills/team/SKILL.md +2 -0
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +39 -26
- package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
- package/src/defaults/gjc-defaults.ts +4 -4
- package/src/exa/mcp-client.ts +12 -3
- package/src/exec/bash-executor.ts +144 -27
- package/src/extensibility/extensions/runner.ts +15 -0
- package/src/extensibility/extensions/types.ts +18 -1
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
- package/src/extensibility/gjc-plugins/index.ts +19 -2
- package/src/extensibility/gjc-plugins/installer.ts +189 -50
- package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
- package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
- package/src/extensibility/gjc-plugins/observability.ts +3 -3
- package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
- package/src/extensibility/gjc-plugins/schema.ts +84 -12
- package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
- package/src/extensibility/gjc-plugins/types.ts +141 -0
- package/src/extensibility/gjc-plugins/validation.ts +9 -0
- package/src/gjc-runtime/launch-tmux.ts +25 -6
- package/src/gjc-runtime/launch-worktree.ts +103 -1
- package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
- package/src/gjc-runtime/managed-owner-admission.ts +1 -1
- package/src/gjc-runtime/ralplan-review-conflicts.ts +472 -0
- package/src/gjc-runtime/ralplan-runtime.ts +491 -27
- package/src/gjc-runtime/team-launch.ts +8 -0
- package/src/gjc-runtime/team-runtime.ts +140 -32
- package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +10 -0
- package/src/gjc-runtime/tmux-sessions.ts +58 -13
- package/src/gjc-runtime/ultragoal-evidence.ts +6 -74
- package/src/gjc-runtime/ultragoal-runtime.ts +635 -612
- package/src/gjc-runtime/workflow-manifest.generated.json +37 -99
- package/src/gjc-runtime/workflow-manifest.ts +21 -70
- package/src/internal-urls/docs-index.generated.ts +16 -16
- package/src/modes/DESIGN.md +104 -0
- package/src/modes/acp/acp-agent.ts +298 -186
- package/src/modes/components/gjc-bundle-settings.ts +508 -0
- package/src/modes/components/irc-sidebar.ts +30 -1
- package/src/modes/components/notifications-settings-editor.ts +12 -1
- package/src/modes/components/oauth-selector.ts +68 -30
- package/src/modes/components/session-observer-overlay.ts +305 -86
- package/src/modes/components/settings-selector.ts +56 -5
- package/src/modes/components/status-line/gh.ts +16 -0
- package/src/modes/components/tool-status-header.ts +3 -15
- package/src/modes/components/transcript-viewer-overlay.ts +99 -24
- package/src/modes/controllers/command-controller.ts +42 -34
- package/src/modes/controllers/event-controller.ts +27 -16
- package/src/modes/controllers/extension-ui-controller.ts +193 -34
- package/src/modes/controllers/selector-controller.ts +160 -22
- package/src/modes/interactive-mode.ts +189 -62
- package/src/modes/jobs-observer.ts +1 -1
- package/src/modes/runtime-init.ts +3 -0
- package/src/modes/theme/theme.ts +11 -9
- package/src/modes/types.ts +88 -0
- package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
- package/src/prompts/system/eager-todo.md +8 -4
- package/src/prompts/system/system-prompt.md +11 -4
- package/src/prompts/tools/bash.md +5 -5
- package/src/prompts/tools/search-tool-bm25.md +5 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/reminders/star-reminder.ts +4 -42
- package/src/runtime-mcp/manager.ts +19 -4
- package/src/sdk/acp/adapter.ts +22 -6
- package/src/sdk/broker/lifecycle.ts +18 -13
- package/src/sdk/broker/transport.ts +35 -3
- package/src/sdk/bus/attachment-registry.ts +5 -1
- package/src/sdk/bus/chat-daemon-cli.ts +4 -39
- package/src/sdk/bus/chat-daemon-control.ts +7 -3
- package/src/sdk/bus/config.ts +18 -2
- package/src/sdk/bus/index.ts +661 -126
- package/src/sdk/bus/kind-aware-reconciliation.ts +148 -73
- package/src/sdk/bus/lifecycle-commands.ts +4 -1
- package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
- package/src/sdk/bus/reconciliation-store.ts +95 -16
- package/src/sdk/bus/rich-draft.ts +1 -1
- package/src/sdk/bus/rich-render.ts +2 -2
- package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
- package/src/sdk/bus/telegram-cli.ts +33 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +12 -4
- package/src/sdk/bus/telegram-daemon.ts +916 -23
- package/src/sdk/bus/telegram-reference.ts +40 -3
- package/src/sdk/bus/threaded-render.ts +48 -3
- package/src/sdk/bus/topic-registry.ts +20 -0
- package/src/sdk/host/query/revision-store.ts +36 -2
- package/src/sdk/host/reverse-leases.ts +44 -7
- package/src/sdk/prompt-status.ts +16 -3
- package/src/sdk/protocol/operation-inventory.generated.json +33 -0
- package/src/sdk/session.ts +89 -9
- package/src/session/agent-session.ts +844 -173
- package/src/session/artifacts.ts +22 -0
- package/src/session/auth-broker-config.ts +10 -3
- package/src/session/blob-store.ts +915 -24
- package/src/session/internal/managed-session-scope.ts +200 -111
- package/src/session/internal/managed-session-storage.ts +492 -62
- package/src/session/session-manager.ts +1614 -435
- package/src/session/session-storage.ts +38 -45
- package/src/session/streaming-output.ts +238 -68
- package/src/setup/credential-import.ts +75 -13
- package/src/skill-state/active-state.ts +32 -0
- package/src/skill-state/workflow-hud.ts +17 -0
- package/src/skill-state/workflow-mutation-guard.ts +496 -2
- package/src/task/executor.ts +122 -39
- package/src/task/index.ts +280 -45
- package/src/task/output-manager.ts +24 -14
- package/src/task/provider-retry-status.ts +16 -0
- package/src/task/receipt.ts +34 -8
- package/src/task/render.ts +6 -2
- package/src/task/types.ts +108 -0
- package/src/tools/ask-answer-registry.ts +12 -6
- package/src/tools/ask.ts +86 -55
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash-interactive.ts +8 -3
- package/src/tools/bash.ts +503 -60
- package/src/tools/find.ts +1 -0
- package/src/tools/image-gen.ts +29 -5
- package/src/tools/index.ts +6 -0
- package/src/tools/output-meta.ts +108 -14
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +18 -0
- package/src/tools/review.ts +5 -7
- package/src/tools/search.ts +1 -0
- package/src/tools/subagent-render.ts +6 -2
- package/src/tools/subagent.ts +6 -0
- package/src/tools/telegram-send.ts +6 -3
- package/src/utils/gh.ts +39 -0
- package/src/utils/image-resize.ts +3 -0
- package/src/web/search/providers/anthropic.ts +4 -4
- package/src/web/search/providers/kimi.ts +15 -5
- package/src/web/search/providers/searxng.ts +47 -4
- package/src/web/search/providers/xai.ts +7 -2
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
- package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
verifyOwnerOnlyPathSecurity,
|
|
9
9
|
verifyOwnerOnlyPathSecurityExpected,
|
|
10
10
|
} from "@gajae-code/natives";
|
|
11
|
-
import { logger, pathIsWithin } from "@gajae-code/utils";
|
|
11
|
+
import { hasFsCode, logger, pathIsWithin } from "@gajae-code/utils";
|
|
12
12
|
import type { ResumeSessionIdentity } from "../session-manager";
|
|
13
13
|
import {
|
|
14
14
|
FileSessionStorage,
|
|
@@ -31,8 +31,10 @@ import {
|
|
|
31
31
|
type ManagedFileSnapshot,
|
|
32
32
|
ManagedPublishError,
|
|
33
33
|
ManagedSessionDescendantStore,
|
|
34
|
+
ManagedSessionSecurityError,
|
|
34
35
|
type ManagedSessionSecurityPolicy,
|
|
35
36
|
type ManagedStorageLock,
|
|
37
|
+
managedSecurityFailureClassification,
|
|
36
38
|
prepareManagedDirectoryRoot,
|
|
37
39
|
publishManagedFileNoReplace,
|
|
38
40
|
publishManagedTombstone,
|
|
@@ -126,7 +128,7 @@ function configuredRootPath(scope: ManagedScope): string {
|
|
|
126
128
|
const canonical = fs.realpathSync.native(candidate);
|
|
127
129
|
return suffix.length === 0 ? canonical : path.join(canonical, ...suffix);
|
|
128
130
|
} catch (error) {
|
|
129
|
-
if ((error
|
|
131
|
+
if (!hasFsCode(error, "ENOENT")) throw error;
|
|
130
132
|
const parent = path.dirname(candidate);
|
|
131
133
|
if (parent === candidate) throw new Error("Configured managed root is unavailable.");
|
|
132
134
|
suffix.unshift(path.basename(candidate));
|
|
@@ -176,6 +178,23 @@ export type ManagedScopeResolution =
|
|
|
176
178
|
cause?: { readonly classification: string; readonly diagnostic?: string };
|
|
177
179
|
};
|
|
178
180
|
|
|
181
|
+
function managedScopeFailureCause(error: unknown): { readonly classification: string } {
|
|
182
|
+
return { classification: managedSecurityFailureClassification(error) ?? "binding_invalid" };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const managedScopeFailureCodes = new Set([
|
|
186
|
+
"atomic_unavailable",
|
|
187
|
+
"invalid_request",
|
|
188
|
+
"durability_failed",
|
|
189
|
+
"durability_not_provable",
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
function managedScopeFailureMessage(error: unknown, fallback: string): string {
|
|
193
|
+
const classification = managedSecurityFailureClassification(error);
|
|
194
|
+
if (classification) return classification;
|
|
195
|
+
return error instanceof Error && managedScopeFailureCodes.has(error.message) ? error.message : fallback;
|
|
196
|
+
}
|
|
197
|
+
|
|
179
198
|
export interface ManagedCandidate {
|
|
180
199
|
sessionId: string;
|
|
181
200
|
path: string;
|
|
@@ -276,7 +295,7 @@ function identityFor(cwd: string): NativeIdentity {
|
|
|
276
295
|
function verifyExistingManagedScopeDirectory(pathname: string) {
|
|
277
296
|
if (process.platform !== "win32") return verifyOwnerOnlyPathSecurity(pathname, "directory");
|
|
278
297
|
const expected = fs.lstatSync(pathname, { bigint: true });
|
|
279
|
-
if (!expected.isDirectory() || expected.isSymbolicLink()) throw new
|
|
298
|
+
if (!expected.isDirectory() || expected.isSymbolicLink()) throw new ManagedSessionSecurityError("reparse_point");
|
|
280
299
|
const verified = verifyOwnerOnlyPathSecurityExpected(pathname, "directory", expected.dev, expected.ino);
|
|
281
300
|
const current = fs.lstatSync(pathname, { bigint: true });
|
|
282
301
|
if (
|
|
@@ -285,7 +304,7 @@ function verifyExistingManagedScopeDirectory(pathname: string) {
|
|
|
285
304
|
current.dev !== expected.dev ||
|
|
286
305
|
current.ino !== expected.ino
|
|
287
306
|
)
|
|
288
|
-
throw new
|
|
307
|
+
throw new ManagedSessionSecurityError("identity_mismatch");
|
|
289
308
|
return verified;
|
|
290
309
|
}
|
|
291
310
|
|
|
@@ -404,8 +423,18 @@ function validateExistingBinding(scope: ManagedScope): ManagedScopeResolution |
|
|
|
404
423
|
try {
|
|
405
424
|
raw = captureManagedFileNoFollow(bindingPath).bytes.toString("utf8");
|
|
406
425
|
} catch (error) {
|
|
407
|
-
if ((error
|
|
408
|
-
|
|
426
|
+
if (hasFsCode(error, "ENOENT")) return undefined;
|
|
427
|
+
const classification = hasFsCode(error, "EACCES")
|
|
428
|
+
? "EACCES"
|
|
429
|
+
: hasFsCode(error, "EPERM")
|
|
430
|
+
? "EPERM"
|
|
431
|
+
: "binding_invalid";
|
|
432
|
+
return {
|
|
433
|
+
kind: "error",
|
|
434
|
+
code: "binding_invalid",
|
|
435
|
+
message: "The managed scope binding is invalid JSON.",
|
|
436
|
+
cause: { classification },
|
|
437
|
+
};
|
|
409
438
|
}
|
|
410
439
|
return validateBindingRaw(scope, raw);
|
|
411
440
|
}
|
|
@@ -428,14 +457,16 @@ function resolveManagedScopeInternal(
|
|
|
428
457
|
kind: "error",
|
|
429
458
|
code: "sessions_root_unavailable",
|
|
430
459
|
message: "The sessions root is not a safe directory.",
|
|
460
|
+
cause: { classification: "reparse_point" },
|
|
431
461
|
};
|
|
432
462
|
}
|
|
433
463
|
} catch (error) {
|
|
434
|
-
if ((error
|
|
464
|
+
if (!hasFsCode(error, "ENOENT")) {
|
|
435
465
|
return {
|
|
436
466
|
kind: "error",
|
|
437
467
|
code: "sessions_root_unavailable",
|
|
438
468
|
message: "The sessions root could not be inspected.",
|
|
469
|
+
cause: managedScopeFailureCause(error),
|
|
439
470
|
};
|
|
440
471
|
}
|
|
441
472
|
}
|
|
@@ -461,21 +492,28 @@ function resolveManagedScopeInternal(
|
|
|
461
492
|
kind: "error",
|
|
462
493
|
code: "sessions_root_unavailable",
|
|
463
494
|
message: "The sessions root is not a safe directory.",
|
|
495
|
+
cause: { classification: "reparse_point" },
|
|
464
496
|
};
|
|
465
497
|
}
|
|
466
498
|
} catch (error) {
|
|
467
|
-
if ((error
|
|
499
|
+
if (!hasFsCode(error, "ENOENT")) {
|
|
468
500
|
return {
|
|
469
501
|
kind: "error",
|
|
470
502
|
code: "sessions_root_unavailable",
|
|
471
503
|
message: "The sessions root could not be inspected.",
|
|
504
|
+
cause: managedScopeFailureCause(error),
|
|
472
505
|
};
|
|
473
506
|
}
|
|
474
507
|
}
|
|
475
508
|
try {
|
|
476
509
|
const directory = fs.lstatSync(scope.directoryPath);
|
|
477
510
|
if (!directory.isDirectory() || directory.isSymbolicLink()) {
|
|
478
|
-
return {
|
|
511
|
+
return {
|
|
512
|
+
kind: "error",
|
|
513
|
+
code: "binding_invalid",
|
|
514
|
+
message: "The managed scope path is not a safe directory.",
|
|
515
|
+
cause: { classification: "reparse_point" },
|
|
516
|
+
};
|
|
479
517
|
}
|
|
480
518
|
const security = validateNativeSecurityResult(
|
|
481
519
|
verifyExistingManagedScopeDirectory(scope.directoryPath),
|
|
@@ -487,11 +525,17 @@ function resolveManagedScopeInternal(
|
|
|
487
525
|
kind: "error",
|
|
488
526
|
code: "binding_invalid",
|
|
489
527
|
message: "The managed scope security could not be verified.",
|
|
528
|
+
cause: { classification: security.code ?? "acl_verify_failed" },
|
|
490
529
|
};
|
|
491
530
|
}
|
|
492
531
|
} catch (error) {
|
|
493
|
-
if ((error
|
|
494
|
-
return {
|
|
532
|
+
if (!hasFsCode(error, "ENOENT")) {
|
|
533
|
+
return {
|
|
534
|
+
kind: "error",
|
|
535
|
+
code: "binding_invalid",
|
|
536
|
+
message: "The managed scope path could not be inspected.",
|
|
537
|
+
cause: managedScopeFailureCause(error),
|
|
538
|
+
};
|
|
495
539
|
}
|
|
496
540
|
}
|
|
497
541
|
return validateExistingBinding(scope) ?? { kind: "resolved", scope };
|
|
@@ -556,7 +600,7 @@ function fsyncManagedParent(pathname: string): void {
|
|
|
556
600
|
try {
|
|
557
601
|
descriptor = fs.openSync(parent, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
|
|
558
602
|
} catch (error) {
|
|
559
|
-
if ((error
|
|
603
|
+
if (hasFsCode(error, "ENOENT") && path.dirname(parent) !== parent) {
|
|
560
604
|
parent = path.dirname(parent);
|
|
561
605
|
continue;
|
|
562
606
|
}
|
|
@@ -778,7 +822,7 @@ export async function ensureManagedScope(
|
|
|
778
822
|
try {
|
|
779
823
|
await publishManagedFileNoReplace(bindingPath, new TextEncoder().encode(binding), undefined, root, policy);
|
|
780
824
|
} catch (error) {
|
|
781
|
-
if ((error
|
|
825
|
+
if (!(error instanceof Error) || error.message !== "destination_conflict") throw error;
|
|
782
826
|
bindingCollision = true;
|
|
783
827
|
}
|
|
784
828
|
const validated = validateExistingBinding(scope);
|
|
@@ -787,14 +831,14 @@ export async function ensureManagedScope(
|
|
|
787
831
|
|
|
788
832
|
const preparedDirectory = fs.lstatSync(scope.directoryPath, { bigint: true });
|
|
789
833
|
if (!preparedDirectory.isDirectory() || preparedDirectory.isSymbolicLink())
|
|
790
|
-
throw new
|
|
834
|
+
throw new ManagedSessionSecurityError("reparse_point");
|
|
791
835
|
managedDirectoryIdentities.set(scope, { dev: preparedDirectory.dev, ino: preparedDirectory.ino });
|
|
792
836
|
return { kind: "resolved", scope };
|
|
793
837
|
} catch (error) {
|
|
794
838
|
const publication = error instanceof ManagedPublishError ? error : undefined;
|
|
795
839
|
const message =
|
|
796
840
|
publication?.classification ??
|
|
797
|
-
(error
|
|
841
|
+
managedScopeFailureMessage(error, "The managed scope could not be initialized.");
|
|
798
842
|
const code =
|
|
799
843
|
message === "atomic_unavailable" ||
|
|
800
844
|
message === "invalid_request" ||
|
|
@@ -802,14 +846,13 @@ export async function ensureManagedScope(
|
|
|
802
846
|
message === "durability_not_provable"
|
|
803
847
|
? message
|
|
804
848
|
: "binding_invalid";
|
|
805
|
-
|
|
806
849
|
return {
|
|
807
850
|
kind: "error",
|
|
808
851
|
code,
|
|
809
852
|
message,
|
|
810
|
-
|
|
811
|
-
? {
|
|
812
|
-
:
|
|
853
|
+
cause: publication
|
|
854
|
+
? { classification: publication.classification, diagnostic: publication.diagnostic }
|
|
855
|
+
: managedScopeFailureCause(error),
|
|
813
856
|
};
|
|
814
857
|
}
|
|
815
858
|
}
|
|
@@ -873,21 +916,44 @@ function isRecoverableOwnerOnlyModeDrift(error: unknown): boolean {
|
|
|
873
916
|
return message === "mode_mismatch" || message.endsWith(": mode_mismatch");
|
|
874
917
|
}
|
|
875
918
|
|
|
919
|
+
type ManagedScopePrepareStage =
|
|
920
|
+
| "retained_identity"
|
|
921
|
+
| "root_authority"
|
|
922
|
+
| "sessions_root"
|
|
923
|
+
| "scope_directory"
|
|
924
|
+
| "scope_identity"
|
|
925
|
+
| "retained_authority"
|
|
926
|
+
| "store"
|
|
927
|
+
| "binding_publish"
|
|
928
|
+
| "binding_read"
|
|
929
|
+
| "binding_validate"
|
|
930
|
+
| "scope_revalidate"
|
|
931
|
+
| "internal_directory"
|
|
932
|
+
| "locks_directory"
|
|
933
|
+
| "receipts_directory"
|
|
934
|
+
| "tombstones_directory";
|
|
935
|
+
|
|
876
936
|
/** Synchronously create and validate the v2 binding before a default session writer exists. */
|
|
877
937
|
export function prepareManagedSessionScopeForWriteSync(
|
|
878
938
|
scope: ManagedScope,
|
|
879
939
|
policy: ManagedSessionSecurityPolicy = "default",
|
|
880
940
|
authority?: ManagedCandidateWriteAuthority,
|
|
881
941
|
): ManagedScopeResolution {
|
|
942
|
+
let stage: ManagedScopePrepareStage = "retained_identity";
|
|
882
943
|
try {
|
|
883
944
|
assertRetainedManagedDirectoryIdentity(scope);
|
|
945
|
+
stage = "root_authority";
|
|
884
946
|
const root = authority?.rootAuthority ?? scopeRoot(scope, policy);
|
|
885
947
|
if (authority) bindManagedWriteAuthority(scope, authority);
|
|
948
|
+
stage = "sessions_root";
|
|
886
949
|
ensureManagedDirectory(scope.sessionsRoot, root, policy);
|
|
950
|
+
stage = "scope_directory";
|
|
887
951
|
ensureManagedDirectory(scope.directoryPath, root, policy);
|
|
952
|
+
stage = "scope_identity";
|
|
888
953
|
const preparedDirectory = fs.lstatSync(scope.directoryPath, { bigint: true });
|
|
889
954
|
if (!preparedDirectory.isDirectory() || preparedDirectory.isSymbolicLink())
|
|
890
955
|
throw new Error("Managed session directory changed");
|
|
956
|
+
stage = "retained_authority";
|
|
891
957
|
const retainedAuthority =
|
|
892
958
|
authority?.retainedAuthority &&
|
|
893
959
|
authority.retainedDirectory !== undefined &&
|
|
@@ -904,6 +970,7 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
904
970
|
retainedAuthority ? { authority: retainedAuthority, authorityBaseDir: scope.directoryPath } : undefined,
|
|
905
971
|
policy,
|
|
906
972
|
);
|
|
973
|
+
stage = "store";
|
|
907
974
|
let store: ManagedSessionDescendantStore;
|
|
908
975
|
try {
|
|
909
976
|
store = buildStore();
|
|
@@ -917,15 +984,19 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
917
984
|
store = buildStore();
|
|
918
985
|
}
|
|
919
986
|
const binding = new TextEncoder().encode(`${JSON.stringify(bindingFor(scope))}\n`);
|
|
987
|
+
stage = "binding_publish";
|
|
920
988
|
try {
|
|
921
989
|
store.publishNoReplaceSync(MANAGED_SESSION_BINDING_FILE, binding);
|
|
922
990
|
} catch (error) {
|
|
923
|
-
if ((error
|
|
991
|
+
if (!(error instanceof Error) || error.message !== "destination_conflict") throw error;
|
|
924
992
|
}
|
|
993
|
+
stage = "binding_read";
|
|
925
994
|
const capturedBinding = store.readExpected(MANAGED_SESSION_BINDING_FILE);
|
|
926
995
|
if (!capturedBinding) throw new Error("Managed scope binding is unavailable");
|
|
996
|
+
stage = "binding_validate";
|
|
927
997
|
const validated = validateBindingRaw(scope, capturedBinding.bytes.toString("utf8"));
|
|
928
998
|
managedDirectoryAuthorities.set(scope, retainedAuthority);
|
|
999
|
+
stage = "scope_revalidate";
|
|
929
1000
|
const directoryStat = fs.lstatSync(scope.directoryPath, { bigint: true });
|
|
930
1001
|
if (
|
|
931
1002
|
!directoryStat.isDirectory() ||
|
|
@@ -935,18 +1006,27 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
935
1006
|
)
|
|
936
1007
|
throw new Error("Managed session directory changed");
|
|
937
1008
|
managedDirectoryIdentities.set(scope, { dev: preparedDirectory.dev, ino: preparedDirectory.ino });
|
|
938
|
-
if (validated)
|
|
1009
|
+
if (validated) {
|
|
1010
|
+
if (validated.kind !== "error") throw new Error("Unexpected managed scope binding result");
|
|
1011
|
+
return {
|
|
1012
|
+
...validated,
|
|
1013
|
+
cause: { classification: validated.code, diagnostic: "prepare:binding_validate" },
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
939
1016
|
const internal = managedInternalDirectory(scope);
|
|
1017
|
+
stage = "internal_directory";
|
|
940
1018
|
ensureManagedDirectory(internal, root, policy);
|
|
1019
|
+
stage = "locks_directory";
|
|
941
1020
|
ensureManagedDirectory(path.join(internal, MANAGED_LOCKS_DIRECTORY), root, policy);
|
|
1021
|
+
stage = "receipts_directory";
|
|
942
1022
|
ensureManagedDirectory(path.join(internal, MANAGED_RECEIPTS_DIRECTORY), root, policy);
|
|
1023
|
+
stage = "tombstones_directory";
|
|
943
1024
|
ensureManagedDirectory(path.join(internal, MANAGED_TOMBSTONES_DIRECTORY), root, policy);
|
|
944
1025
|
return { kind: "resolved", scope };
|
|
945
1026
|
} catch (error) {
|
|
946
1027
|
const publication = error instanceof ManagedPublishError ? error : undefined;
|
|
947
1028
|
const message =
|
|
948
|
-
publication?.classification ??
|
|
949
|
-
(error instanceof Error ? error.message : "Managed write protocol setup failed.");
|
|
1029
|
+
publication?.classification ?? managedScopeFailureMessage(error, "Managed write protocol setup failed.");
|
|
950
1030
|
const code =
|
|
951
1031
|
message === "atomic_unavailable" ||
|
|
952
1032
|
message === "invalid_request" ||
|
|
@@ -954,15 +1034,14 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
954
1034
|
message === "durability_not_provable"
|
|
955
1035
|
? message
|
|
956
1036
|
: "binding_invalid";
|
|
1037
|
+
|
|
957
1038
|
return {
|
|
958
1039
|
kind: "error",
|
|
959
1040
|
code,
|
|
960
1041
|
message,
|
|
961
|
-
|
|
962
|
-
? {
|
|
963
|
-
:
|
|
964
|
-
? {}
|
|
965
|
-
: { cause: { classification: code } }),
|
|
1042
|
+
cause: publication
|
|
1043
|
+
? { classification: publication.classification, diagnostic: publication.diagnostic }
|
|
1044
|
+
: { ...managedScopeFailureCause(error), diagnostic: `prepare:${stage}` },
|
|
966
1045
|
};
|
|
967
1046
|
}
|
|
968
1047
|
}
|
|
@@ -1725,6 +1804,14 @@ function sameArtifactTreeSnapshot(left: NativeDirectoryTreeSnapshot, right: Nati
|
|
|
1725
1804
|
return leftEntries.every((entry, index) => entry === rightEntries[index]);
|
|
1726
1805
|
}
|
|
1727
1806
|
|
|
1807
|
+
function artifactTreePayloadAbsent(snapshot: NativeDirectoryTreeSnapshot): boolean {
|
|
1808
|
+
return (
|
|
1809
|
+
snapshot.entries.length === 1 &&
|
|
1810
|
+
snapshot.entries[0]?.relativePath === "" &&
|
|
1811
|
+
snapshot.entries[0].kind === "directory"
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1728
1815
|
function assertRetainedArtifactsAuthority(pending: CleanupReceipt): void {
|
|
1729
1816
|
if (!pending.detachedArtifactsPath) return;
|
|
1730
1817
|
if (!pending.expectedArtifactsIdentity || !pending.expectedArtifactsTree) throw new Error("durability_failed");
|
|
@@ -1735,10 +1822,12 @@ function assertRetainedArtifactsAuthority(pending: CleanupReceipt): void {
|
|
|
1735
1822
|
throw new Error("durability_failed");
|
|
1736
1823
|
}
|
|
1737
1824
|
const observed = artifactIdentityAt(pending.detachedArtifactsPath);
|
|
1825
|
+
const observedTree = snapshotArtifactTree(pending.detachedArtifactsPath);
|
|
1738
1826
|
if (
|
|
1739
1827
|
!observed ||
|
|
1740
1828
|
!sameArtifactRootIdentity(observed, pending.expectedArtifactsIdentity) ||
|
|
1741
|
-
!sameArtifactTreeSnapshot(
|
|
1829
|
+
(!sameArtifactTreeSnapshot(observedTree, pending.expectedArtifactsTree) &&
|
|
1830
|
+
!artifactTreePayloadAbsent(observedTree))
|
|
1742
1831
|
)
|
|
1743
1832
|
throw new Error("binding_invalid");
|
|
1744
1833
|
}
|
|
@@ -1964,6 +2053,15 @@ function snapshotArtifactTree(pathname: string): NativeDirectoryTreeSnapshot {
|
|
|
1964
2053
|
return result.snapshot;
|
|
1965
2054
|
}
|
|
1966
2055
|
|
|
2056
|
+
function retainedArtifactPayloadAbsent(pathname: string): boolean {
|
|
2057
|
+
try {
|
|
2058
|
+
return artifactTreePayloadAbsent(snapshotArtifactTree(pathname));
|
|
2059
|
+
} catch (error) {
|
|
2060
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return true;
|
|
2061
|
+
throw error;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
|
|
1967
2065
|
function nextCleanupReceipt(target: RetiredTarget, pending: CleanupReceipt | undefined): CleanupReceipt {
|
|
1968
2066
|
const attempt = (pending?.attempt ?? 0) + 1;
|
|
1969
2067
|
const directory = path.dirname(target.path);
|
|
@@ -2924,53 +3022,44 @@ export async function reconcileManagedTombstones(
|
|
|
2924
3022
|
}
|
|
2925
3023
|
if (deletion.kind === "cleanup_pending") {
|
|
2926
3024
|
const retry = nextCleanupReceipt(target, active);
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
? deletion.detachedArtifactsPath
|
|
2941
|
-
: active.detachedArtifactsPath,
|
|
3025
|
+
const pendingEvidence = cleanupPendingEvidence(retry, active, deletion);
|
|
3026
|
+
await publishCleanupPending(scope, tombstone, pendingEvidence, lock);
|
|
3027
|
+
if (deletion.phase === "artifacts") {
|
|
3028
|
+
if (!retainedArtifactPayloadAbsent(deletion.detachedArtifactsPath)) continue;
|
|
3029
|
+
await publishCleanupArtifactsRemoved(scope, tombstone, pendingEvidence, lock);
|
|
3030
|
+
deletion = await new FileSessionStorage().deleteSessionVerified({
|
|
3031
|
+
sessionsRoot: scope.sessionsRoot,
|
|
3032
|
+
transcriptPath: target.path,
|
|
3033
|
+
sessionId: target.sessionId,
|
|
3034
|
+
cwd: target.cwd,
|
|
3035
|
+
transcriptIdentity: target.identity,
|
|
3036
|
+
plannedArtifactsPath: pendingEvidence.plannedArtifactsPath,
|
|
3037
|
+
plannedTranscriptPath: pendingEvidence.plannedTranscriptPath,
|
|
2942
3038
|
detachedTranscriptPath:
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
deletion.
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
deletion.phase === "transcript"
|
|
2968
|
-
? deletion.retainedUnknownPath
|
|
2969
|
-
: active.retainedTranscriptUnknownPath,
|
|
2970
|
-
},
|
|
2971
|
-
lock,
|
|
2972
|
-
);
|
|
2973
|
-
continue;
|
|
3039
|
+
pendingEvidence.detachedTranscriptPath ?? observedPending?.detachedTranscriptPath,
|
|
3040
|
+
retainedArtifactsSuccessorPath: pendingEvidence.retainedArtifactsSuccessorPath,
|
|
3041
|
+
retainedArtifactsPlaceholderPath: pendingEvidence.retainedArtifactsPlaceholderPath,
|
|
3042
|
+
retainedArtifactsUnknownPath: pendingEvidence.retainedArtifactsUnknownPath,
|
|
3043
|
+
retainedTranscriptSuccessorPath: pendingEvidence.retainedTranscriptSuccessorPath,
|
|
3044
|
+
retainedTranscriptPlaceholderPath: pendingEvidence.retainedTranscriptPlaceholderPath,
|
|
3045
|
+
retainedTranscriptUnknownPath: pendingEvidence.retainedTranscriptUnknownPath,
|
|
3046
|
+
artifactsRemoved: true,
|
|
3047
|
+
});
|
|
3048
|
+
if (deletion.kind === "cleanup_pending") {
|
|
3049
|
+
if (
|
|
3050
|
+
deletion.phase !== "transcript" ||
|
|
3051
|
+
deletion.detachedTranscriptPath !== pendingEvidence.plannedTranscriptPath
|
|
3052
|
+
)
|
|
3053
|
+
throw new Error("durability_failed");
|
|
3054
|
+
const followup = nextCleanupReceipt(target, pendingEvidence);
|
|
3055
|
+
await publishCleanupPending(
|
|
3056
|
+
scope,
|
|
3057
|
+
tombstone,
|
|
3058
|
+
cleanupPendingEvidence(followup, pendingEvidence, deletion),
|
|
3059
|
+
lock,
|
|
3060
|
+
);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
2974
3063
|
}
|
|
2975
3064
|
fsyncManagedParent(target.path);
|
|
2976
3065
|
await publishCleanupCompleted(scope, tombstone, target, lock);
|
|
@@ -2984,7 +3073,7 @@ export async function reconcileManagedTombstones(
|
|
|
2984
3073
|
}
|
|
2985
3074
|
}
|
|
2986
3075
|
} finally {
|
|
2987
|
-
if (lock) await lock.release()
|
|
3076
|
+
if (lock) await lock.release();
|
|
2988
3077
|
}
|
|
2989
3078
|
}
|
|
2990
3079
|
}
|
|
@@ -3013,8 +3102,7 @@ export async function prepareManagedSessionScopeForWrite(
|
|
|
3013
3102
|
} catch (error) {
|
|
3014
3103
|
const publication = error instanceof ManagedPublishError ? error : undefined;
|
|
3015
3104
|
const message =
|
|
3016
|
-
publication?.classification ??
|
|
3017
|
-
(error instanceof Error ? error.message : "Managed write protocol setup failed.");
|
|
3105
|
+
publication?.classification ?? managedScopeFailureMessage(error, "Managed write protocol setup failed.");
|
|
3018
3106
|
const code =
|
|
3019
3107
|
message === "atomic_unavailable" ||
|
|
3020
3108
|
message === "invalid_request" ||
|
|
@@ -3026,9 +3114,9 @@ export async function prepareManagedSessionScopeForWrite(
|
|
|
3026
3114
|
kind: "error",
|
|
3027
3115
|
code,
|
|
3028
3116
|
message,
|
|
3029
|
-
|
|
3030
|
-
? {
|
|
3031
|
-
:
|
|
3117
|
+
cause: publication
|
|
3118
|
+
? { classification: publication.classification, diagnostic: publication.diagnostic }
|
|
3119
|
+
: managedScopeFailureCause(error),
|
|
3032
3120
|
};
|
|
3033
3121
|
}
|
|
3034
3122
|
}
|
|
@@ -3358,7 +3446,7 @@ export async function openManagedCandidateForWrite(
|
|
|
3358
3446
|
: "Managed migration failed.",
|
|
3359
3447
|
};
|
|
3360
3448
|
} finally {
|
|
3361
|
-
if (lock) await lock.release()
|
|
3449
|
+
if (lock) await lock.release();
|
|
3362
3450
|
}
|
|
3363
3451
|
}
|
|
3364
3452
|
|
|
@@ -3510,51 +3598,52 @@ export async function deleteManagedSessionCandidate(
|
|
|
3510
3598
|
)
|
|
3511
3599
|
throw new Error("durability_failed");
|
|
3512
3600
|
const retry = nextCleanupReceipt(target, active);
|
|
3513
|
-
|
|
3601
|
+
const pendingEvidence = cleanupPendingEvidence(retry, active, deletion);
|
|
3602
|
+
await publishCleanupPending(scope, tombstone, pendingEvidence, lock);
|
|
3514
3603
|
if (deletion.phase === "artifacts") {
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3604
|
+
if (!retainedArtifactPayloadAbsent(deletion.detachedArtifactsPath))
|
|
3605
|
+
return {
|
|
3606
|
+
kind: "cleanup_pending",
|
|
3607
|
+
tombstonePath: tombstone,
|
|
3608
|
+
phase: deletion.phase,
|
|
3609
|
+
message: "Exact cleanup remains pending because descriptor-bound final deletion is unavailable.",
|
|
3610
|
+
};
|
|
3611
|
+
await publishCleanupArtifactsRemoved(scope, tombstone, pendingEvidence, lock);
|
|
3518
3612
|
deletion = await new FileSessionStorage().deleteSessionVerified({
|
|
3519
3613
|
sessionsRoot: scope.sessionsRoot,
|
|
3520
3614
|
transcriptPath: target.path,
|
|
3521
3615
|
sessionId: target.sessionId,
|
|
3522
3616
|
cwd: target.cwd,
|
|
3523
3617
|
transcriptIdentity: target.identity,
|
|
3524
|
-
plannedArtifactsPath:
|
|
3525
|
-
plannedTranscriptPath:
|
|
3526
|
-
detachedTranscriptPath:
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3618
|
+
plannedArtifactsPath: pendingEvidence.plannedArtifactsPath,
|
|
3619
|
+
plannedTranscriptPath: pendingEvidence.plannedTranscriptPath,
|
|
3620
|
+
detachedTranscriptPath:
|
|
3621
|
+
pendingEvidence.detachedTranscriptPath ?? observedPending?.detachedTranscriptPath,
|
|
3622
|
+
retainedArtifactsSuccessorPath: pendingEvidence.retainedArtifactsSuccessorPath,
|
|
3623
|
+
retainedArtifactsPlaceholderPath: pendingEvidence.retainedArtifactsPlaceholderPath,
|
|
3624
|
+
retainedArtifactsUnknownPath: pendingEvidence.retainedArtifactsUnknownPath,
|
|
3625
|
+
retainedTranscriptSuccessorPath: pendingEvidence.retainedTranscriptSuccessorPath,
|
|
3626
|
+
retainedTranscriptPlaceholderPath: pendingEvidence.retainedTranscriptPlaceholderPath,
|
|
3627
|
+
retainedTranscriptUnknownPath: pendingEvidence.retainedTranscriptUnknownPath,
|
|
3533
3628
|
artifactsRemoved: true,
|
|
3534
3629
|
});
|
|
3535
3630
|
if (deletion.kind === "cleanup_pending") {
|
|
3536
|
-
|
|
3631
|
+
if (
|
|
3632
|
+
deletion.phase !== "transcript" ||
|
|
3633
|
+
deletion.detachedTranscriptPath !== pendingEvidence.plannedTranscriptPath
|
|
3634
|
+
)
|
|
3635
|
+
throw new Error("durability_failed");
|
|
3636
|
+
const followup = nextCleanupReceipt(target, pendingEvidence);
|
|
3537
3637
|
await publishCleanupPending(
|
|
3538
3638
|
scope,
|
|
3539
3639
|
tombstone,
|
|
3540
|
-
cleanupPendingEvidence(followup,
|
|
3640
|
+
cleanupPendingEvidence(followup, pendingEvidence, deletion),
|
|
3541
3641
|
lock,
|
|
3542
3642
|
);
|
|
3543
|
-
if (
|
|
3544
|
-
deletion.phase !== "transcript" ||
|
|
3545
|
-
deletion.detachedTranscriptPath !== active.plannedTranscriptPath
|
|
3546
|
-
)
|
|
3547
|
-
return {
|
|
3548
|
-
kind: "cleanup_pending",
|
|
3549
|
-
tombstonePath: tombstone,
|
|
3550
|
-
phase: deletion.phase,
|
|
3551
|
-
message:
|
|
3552
|
-
"Exact cleanup remains pending because descriptor-bound final deletion is unavailable.",
|
|
3553
|
-
};
|
|
3554
3643
|
}
|
|
3555
3644
|
}
|
|
3556
|
-
// A
|
|
3557
|
-
//
|
|
3645
|
+
// A retained transcript quarantine proves canonical absence and remains
|
|
3646
|
+
// identity-bound in the durable pending receipt.
|
|
3558
3647
|
}
|
|
3559
3648
|
fsyncManagedParent(target.path);
|
|
3560
3649
|
await publishCleanupCompleted(scope, tombstone, target, lock);
|
|
@@ -3564,6 +3653,6 @@ export async function deleteManagedSessionCandidate(
|
|
|
3564
3653
|
const code = expectedFailure(error);
|
|
3565
3654
|
return { kind: "error", code, message: error instanceof Error ? error.message : "Managed deletion failed." };
|
|
3566
3655
|
} finally {
|
|
3567
|
-
if (lock) await lock.release()
|
|
3656
|
+
if (lock) await lock.release();
|
|
3568
3657
|
}
|
|
3569
3658
|
}
|