@gajae-code/coding-agent 0.18.7 → 0.18.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +279 -1
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/capability/skill.d.ts +2 -0
- package/dist/types/capability/types.d.ts +2 -0
- package/dist/types/cli/public-command-errors.d.ts +3 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/autorouting-tier-map.d.ts +348 -9
- package/dist/types/config/file-lock.d.ts +1 -0
- package/dist/types/config/model-bindings-applier.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -3
- package/dist/types/config/model-registry.d.ts +4 -9
- package/dist/types/config/skill-settings-defaults.d.ts +2 -0
- package/dist/types/cursor-delete-path.d.ts +1 -0
- package/dist/types/cursor-mcp-name.d.ts +8 -0
- package/dist/types/customization/inventory.d.ts +2 -0
- package/dist/types/debug/report-bundle-parent.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
- package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
- package/dist/types/discovery/helpers.d.ts +6 -1
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
- package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
- package/dist/types/extensibility/skill-management.d.ts +3 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
- package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
- package/dist/types/internal-urls/skill-path.d.ts +1 -0
- package/dist/types/lsp/workspace-path.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
- package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
- package/dist/types/modes/controllers/input-controller.d.ts +9 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
- package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
- package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
- package/dist/types/progress/collect-project-progress.d.ts +38 -0
- package/dist/types/progress/progress-contract.d.ts +154 -0
- package/dist/types/progress/progress-snapshot.d.ts +11 -0
- package/dist/types/progress/project-progress.d.ts +141 -0
- package/dist/types/progress/render-progress.d.ts +28 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk/acp/adapter.d.ts +1 -1
- package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
- package/dist/types/sdk/broker/broker.d.ts +18 -1
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/bus/index.d.ts +12 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +3 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +8 -0
- package/dist/types/session/agent-session.d.ts +38 -9
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
- package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +31 -7
- package/dist/types/session/session-storage.d.ts +62 -2
- package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
- package/dist/types/setup/credential-auto-import.d.ts +1 -1
- package/dist/types/setup/credential-import.d.ts +6 -4
- package/dist/types/skill-state/active-state.d.ts +38 -0
- package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/python.d.ts +9 -1
- package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
- package/dist/types/tools/restricted-cwd.d.ts +1 -0
- package/dist/types/tools/todo-write.d.ts +6 -0
- package/dist/types/utils/tools-checksum.d.ts +7 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +21 -0
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +17 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +2 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +28 -8
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +307 -68
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-bindings-applier.ts +20 -0
- package/src/config/model-profile-activation.ts +240 -46
- package/src/config/model-registry.ts +47 -153
- package/src/config/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/cursor-delete-path.ts +10 -0
- package/src/cursor-mcp-name.ts +15 -0
- package/src/cursor.ts +13 -2
- package/src/customization/inventory.ts +3 -0
- package/src/debug/report-bundle-parent.ts +22 -0
- package/src/debug/report-bundle.ts +3 -0
- package/src/debug/system-info.ts +13 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
- package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
- package/src/defaults/gjc-defaults.test.ts +18 -1
- package/src/defaults/gjc-defaults.ts +3 -0
- package/src/defaults/gjc-skills.generated.ts +58 -36
- package/src/discovery/agents.ts +6 -1
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +159 -20
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +380 -120
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/index.ts +5 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/plugins/marketplace/cache.ts +15 -0
- package/src/extensibility/plugins/marketplace/manager.ts +11 -3
- package/src/extensibility/plugins/plugin-link-path.ts +12 -0
- package/src/extensibility/runtime-skill-discovery.ts +20 -2
- package/src/extensibility/skill-management.ts +6 -2
- package/src/extensibility/skills.ts +6 -1
- package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
- package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
- package/src/gjc-runtime/deep-interview-stage.ts +98 -21
- package/src/gjc-runtime/gc-runtime.ts +206 -9
- package/src/gjc-runtime/launch-worktree.ts +14 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/state-argv.ts +2 -0
- package/src/gjc-runtime/state-migrations.ts +91 -29
- package/src/gjc-runtime/state-runtime.ts +3716 -135
- package/src/gjc-runtime/state-writer.ts +83 -8
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
- package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
- package/src/gjc-runtime/workflow-manifest.ts +54 -21
- package/src/harness-control-plane/operate.ts +12 -4
- package/src/harness-control-plane/owner.ts +5 -1
- package/src/hashline/parser.ts +48 -9
- package/src/hashline/recovery.ts +13 -4
- package/src/internal-urls/docs-index.generated.ts +18 -17
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/modes/acp/acp-agent.ts +88 -4
- package/src/modes/components/custom-editor.ts +14 -0
- package/src/modes/components/customization/customization-dashboard.ts +12 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/state-manager.ts +15 -5
- package/src/modes/components/model-selector.ts +1 -4
- package/src/modes/components/provider-onboarding-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +4 -0
- package/src/modes/controllers/event-controller.ts +11 -4
- package/src/modes/controllers/input-controller.ts +74 -29
- package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
- package/src/modes/controllers/selector-controller.ts +28 -15
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/runtime-init.ts +4 -0
- package/src/modes/utils/abort-message.ts +3 -1
- package/src/modes/utils/interrupt-ownership.ts +12 -0
- package/src/modes/utils/keybinding-matchers.ts +4 -0
- package/src/modes/utils/macos-option-forwarding.ts +80 -0
- package/src/progress/collect-project-progress.ts +153 -0
- package/src/progress/progress-contract.ts +172 -0
- package/src/progress/progress-snapshot.ts +140 -0
- package/src/progress/project-progress.ts +446 -0
- package/src/progress/render-progress.ts +209 -0
- package/src/prompts/system/master-mode.md +1 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/oauth-flow.ts +5 -0
- package/src/runtime-mcp/oauth-public-url.ts +8 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +2 -2
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +93 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +75 -6
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +711 -66
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +238 -43
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2647 -294
- package/src/sdk/host/session-runtime.ts +426 -109
- package/src/sdk/host/surface-policy.ts +1 -0
- package/src/sdk/index.ts +8 -0
- package/src/sdk/lifecycle-session.ts +3 -1
- package/src/sdk/prompt-deadline-manager.ts +15 -0
- package/src/sdk/protocol/operation-inventory.generated.json +146 -0
- package/src/sdk/protocol/operation-registry.ts +9 -2
- package/src/sdk/router/session-router.ts +75 -12
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +67 -38
- package/src/session/agent-session.ts +623 -224
- package/src/session/artifacts.ts +174 -70
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3457 -462
- package/src/session/internal/managed-session-storage.ts +376 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +19 -3
- package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +417 -14
- package/src/session/task-artifact-owner-codec.ts +71 -8
- package/src/session/task-artifact-owner-retirement.ts +21 -19
- package/src/setup/credential-auto-import.ts +12 -3
- package/src/setup/credential-import.ts +176 -5
- package/src/setup/paseo/announce.ts +6 -1
- package/src/skill-state/active-state.ts +132 -2
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/progress-report.ts +23 -0
- package/src/stt/setup.ts +3 -5
- package/src/task/commands.ts +3 -1
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +6 -0
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit.ts +4 -0
- package/src/tools/bash-allowed-prefixes.ts +15 -0
- package/src/tools/bash-skill-urls.ts +56 -3
- package/src/tools/bash.ts +48 -79
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/python.ts +187 -66
- package/src/tools/recipe/index.ts +9 -0
- package/src/tools/recipe/recipe-guard.ts +19 -0
- package/src/tools/restricted-cwd.ts +10 -0
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +28 -1
- package/src/tools/todo-write.ts +10 -1
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/write.ts +1 -0
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import * as util from "node:util";
|
|
3
|
+
import type { ResumeSessionIdentity } from "../session-manager";
|
|
4
|
+
import {
|
|
5
|
+
FileSessionStorage,
|
|
6
|
+
type SessionStorageSnapshot,
|
|
7
|
+
type VerifiedSessionDeleteResult,
|
|
8
|
+
type VerifiedSessionDeleteTarget,
|
|
9
|
+
} from "../session-storage";
|
|
10
|
+
import type { TaskArtifactOwnerDeletionEvidence, TaskArtifactOwnerStorageContext } from "../task-artifact-owner-codec";
|
|
11
|
+
import {
|
|
12
|
+
parseTaskArtifactOwnerDeletionEvidence,
|
|
13
|
+
parseTaskArtifactOwnerRetirementOutcome,
|
|
14
|
+
} from "../task-artifact-owner-codec";
|
|
15
|
+
import { retireTaskArtifactOwner, verifyTaskArtifactOwnerPhysicalRetirement } from "../task-artifact-owner-retirement";
|
|
16
|
+
import type {
|
|
17
|
+
ManagedGcSessionRetirementReceipt,
|
|
18
|
+
ManagedGcSessionRetirementTarget,
|
|
19
|
+
} from "./managed-gc-retirement-codec";
|
|
20
|
+
import type { ManagedGcProtocolScopeInspector } from "./managed-session-scope";
|
|
21
|
+
import { captureTaskArtifactOwnerDeletionEvidence } from "./task-artifact-owner-access";
|
|
22
|
+
import {
|
|
23
|
+
hasSiblingTaskArtifactOwnerTranscript,
|
|
24
|
+
taskArtifactOwnerLocatorFromTranscriptBytes,
|
|
25
|
+
} from "./task-artifact-owner-transcript";
|
|
26
|
+
|
|
27
|
+
export interface ManagedGcOwnerCleanupTarget {
|
|
28
|
+
readonly path: string;
|
|
29
|
+
readonly sessionId: string;
|
|
30
|
+
readonly cwd: string;
|
|
31
|
+
readonly identity: ResumeSessionIdentity;
|
|
32
|
+
readonly taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Authenticated receipt operations and fencing supplied by the prepared managed scope. */
|
|
36
|
+
export interface ManagedGcOwnerCleanupAuthority {
|
|
37
|
+
readonly agentDir: string;
|
|
38
|
+
readonly sessionsRoot: string;
|
|
39
|
+
readonly directoryPath: string;
|
|
40
|
+
readonly storageContext: TaskArtifactOwnerStorageContext;
|
|
41
|
+
readonly inspectProtocol: ManagedGcProtocolScopeInspector;
|
|
42
|
+
readonly assertOwned: () => void;
|
|
43
|
+
readonly bindTarget: (transcriptPath: string) => ManagedGcSessionRetirementTarget;
|
|
44
|
+
readonly readReceipt: (transcriptPath: string) => Promise<ManagedGcSessionRetirementReceipt | undefined>;
|
|
45
|
+
readonly publishReceipt: (receipt: ManagedGcSessionRetirementReceipt) => Promise<ManagedGcSessionRetirementReceipt>;
|
|
46
|
+
readonly findCompletedRetirement: (
|
|
47
|
+
evidence: TaskArtifactOwnerDeletionEvidence,
|
|
48
|
+
exceptTranscriptPath: string,
|
|
49
|
+
) => Promise<
|
|
50
|
+
| {
|
|
51
|
+
scope: { readonly agentDir: string; readonly sessionsRoot: string; readonly directoryPath: string };
|
|
52
|
+
receipt: ManagedGcSessionRetirementReceipt;
|
|
53
|
+
}
|
|
54
|
+
| undefined
|
|
55
|
+
>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type ManagedGcOwnerProgress = {
|
|
59
|
+
readonly state: "none" | "owner_retired" | "payload_retired" | "pending";
|
|
60
|
+
readonly message?: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type ManagedGcOwnerDeleteFields = Pick<
|
|
64
|
+
VerifiedSessionDeleteTarget,
|
|
65
|
+
| "taskArtifactOwnerStorageContext"
|
|
66
|
+
| "taskArtifactOwnerDeletionEvidence"
|
|
67
|
+
| "taskArtifactOwnerRetirementOutcome"
|
|
68
|
+
| "taskArtifactOwnerRetirementContinuation"
|
|
69
|
+
| "taskArtifactOwnerPayloadRetired"
|
|
70
|
+
| "taskArtifactOwnerNamespaceRetained"
|
|
71
|
+
| "deferTaskArtifactOwnerRetirement"
|
|
72
|
+
| "taskArtifactOwnerRetired"
|
|
73
|
+
>;
|
|
74
|
+
|
|
75
|
+
function deepSame(left: unknown, right: unknown): boolean {
|
|
76
|
+
return util.isDeepStrictEqual(left, right);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function managedGcTargetMatchesCandidate(
|
|
80
|
+
target: ManagedGcSessionRetirementTarget,
|
|
81
|
+
candidate: ManagedGcOwnerCleanupTarget,
|
|
82
|
+
): boolean {
|
|
83
|
+
return (
|
|
84
|
+
target.transcriptPath === candidate.path &&
|
|
85
|
+
target.sessionId === candidate.sessionId &&
|
|
86
|
+
target.cwd === candidate.cwd &&
|
|
87
|
+
target.transcriptIdentity.nlink === 1n &&
|
|
88
|
+
target.transcriptIdentity.dev === candidate.identity.dev &&
|
|
89
|
+
target.transcriptIdentity.ino === candidate.identity.ino &&
|
|
90
|
+
target.transcriptIdentity.size === candidate.identity.size &&
|
|
91
|
+
target.transcriptIdentity.mtimeNs === candidate.identity.mtimeNs &&
|
|
92
|
+
target.transcriptIdentity.sha256 === candidate.identity.sha256
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function managedGcReceiptTarget(receipt: ManagedGcSessionRetirementReceipt): ManagedGcSessionRetirementTarget {
|
|
97
|
+
return {
|
|
98
|
+
transcriptPath: receipt.transcriptPath,
|
|
99
|
+
sessionId: receipt.sessionId,
|
|
100
|
+
cwd: receipt.cwd,
|
|
101
|
+
transcriptIdentity: receipt.transcriptIdentity,
|
|
102
|
+
taskArtifactOwnerLocator: receipt.taskArtifactOwnerDeletionEvidence.locator,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function hasUnsupportedLegacyOwnerTarget(scopeDirectory: string, target: ManagedGcOwnerCleanupTarget): boolean {
|
|
107
|
+
if (path.dirname(target.path) === scopeDirectory) return false;
|
|
108
|
+
if (target.taskArtifactOwnerDeletionEvidence) return true;
|
|
109
|
+
let snapshot: SessionStorageSnapshot;
|
|
110
|
+
try {
|
|
111
|
+
snapshot = new FileSessionStorage().readSnapshotSync(target.path);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return false;
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
return taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, target.sessionId) !== undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function prepareManagedGcOwnerTarget<T extends ManagedGcOwnerCleanupTarget>(
|
|
120
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
121
|
+
target: T,
|
|
122
|
+
): Promise<T & ManagedGcOwnerCleanupTarget> {
|
|
123
|
+
if (path.dirname(target.path) !== authority.directoryPath) {
|
|
124
|
+
if (hasUnsupportedLegacyOwnerTarget(authority.directoryPath, target))
|
|
125
|
+
throw new Error("task_artifact_owner_legacy_scope_unsupported");
|
|
126
|
+
return target;
|
|
127
|
+
}
|
|
128
|
+
const receipt = await authority.readReceipt(target.path);
|
|
129
|
+
if (receipt) {
|
|
130
|
+
if (
|
|
131
|
+
!managedGcTargetMatchesCandidate(receipt, target) ||
|
|
132
|
+
(target.taskArtifactOwnerDeletionEvidence &&
|
|
133
|
+
!deepSame(target.taskArtifactOwnerDeletionEvidence, receipt.taskArtifactOwnerDeletionEvidence))
|
|
134
|
+
)
|
|
135
|
+
throw new Error("task_artifact_owner_continuation_evidence_mismatch");
|
|
136
|
+
return { ...target, taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence };
|
|
137
|
+
}
|
|
138
|
+
if (target.taskArtifactOwnerDeletionEvidence) throw new Error("task_artifact_owner_continuation_state_missing");
|
|
139
|
+
let snapshot: SessionStorageSnapshot;
|
|
140
|
+
try {
|
|
141
|
+
snapshot = new FileSessionStorage().readSnapshotSync(target.path);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return target;
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
const locator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, target.sessionId);
|
|
147
|
+
if (!locator) return target;
|
|
148
|
+
const bound = authority.bindTarget(target.path);
|
|
149
|
+
if (!managedGcTargetMatchesCandidate(bound, target) || !deepSame(locator, bound.taskArtifactOwnerLocator))
|
|
150
|
+
throw new Error("task_artifact_owner_continuation_transcript_replaced");
|
|
151
|
+
const evidence = captureTaskArtifactOwnerDeletionEvidence(authority.storageContext, target.sessionId, locator);
|
|
152
|
+
if (!evidence) throw new Error("task_artifact_owner_deletion_evidence_missing");
|
|
153
|
+
const prepared = await authority.publishReceipt({
|
|
154
|
+
...bound,
|
|
155
|
+
state: "prepared",
|
|
156
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
157
|
+
});
|
|
158
|
+
if (
|
|
159
|
+
!deepSame(prepared.taskArtifactOwnerDeletionEvidence, evidence) ||
|
|
160
|
+
!managedGcTargetMatchesCandidate(prepared, target)
|
|
161
|
+
)
|
|
162
|
+
throw new Error("task_artifact_owner_continuation_evidence_mismatch");
|
|
163
|
+
return { ...target, taskArtifactOwnerDeletionEvidence: evidence };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function prepareManagedGcOwnerTargets<T extends ManagedGcOwnerCleanupTarget>(
|
|
167
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
168
|
+
targets: readonly T[],
|
|
169
|
+
): Promise<(T & ManagedGcOwnerCleanupTarget)[]> {
|
|
170
|
+
const prepared: (T & ManagedGcOwnerCleanupTarget)[] = [];
|
|
171
|
+
for (const target of targets) prepared.push(await prepareManagedGcOwnerTarget(authority, target));
|
|
172
|
+
return prepared;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function managedGcOwnerDeleteFields(
|
|
176
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
177
|
+
target: ManagedGcOwnerCleanupTarget,
|
|
178
|
+
): Promise<ManagedGcOwnerDeleteFields> {
|
|
179
|
+
if (path.dirname(target.path) !== authority.directoryPath) {
|
|
180
|
+
if (hasUnsupportedLegacyOwnerTarget(authority.directoryPath, target))
|
|
181
|
+
throw new Error("task_artifact_owner_legacy_scope_unsupported");
|
|
182
|
+
return {};
|
|
183
|
+
}
|
|
184
|
+
const receipt = await authority.readReceipt(target.path);
|
|
185
|
+
if (!receipt) {
|
|
186
|
+
if (target.taskArtifactOwnerDeletionEvidence) throw new Error("task_artifact_owner_continuation_state_missing");
|
|
187
|
+
return {};
|
|
188
|
+
}
|
|
189
|
+
if (
|
|
190
|
+
!managedGcTargetMatchesCandidate(receipt, target) ||
|
|
191
|
+
!deepSame(receipt.taskArtifactOwnerDeletionEvidence, target.taskArtifactOwnerDeletionEvidence)
|
|
192
|
+
)
|
|
193
|
+
throw new Error("task_artifact_owner_continuation_evidence_mismatch");
|
|
194
|
+
const outcome = receipt.taskArtifactOwnerRetirementOutcome;
|
|
195
|
+
return {
|
|
196
|
+
taskArtifactOwnerStorageContext: authority.storageContext,
|
|
197
|
+
taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence,
|
|
198
|
+
deferTaskArtifactOwnerRetirement: true,
|
|
199
|
+
...(outcome ? { taskArtifactOwnerRetirementOutcome: outcome } : {}),
|
|
200
|
+
...(outcome && outcome.kind !== "completed"
|
|
201
|
+
? { taskArtifactOwnerRetirementContinuation: outcome.continuation }
|
|
202
|
+
: {}),
|
|
203
|
+
...(outcome?.kind === "payload_retired"
|
|
204
|
+
? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
|
|
205
|
+
: {}),
|
|
206
|
+
...(receipt.state === "owner_retired" ? { taskArtifactOwnerRetired: true as const } : {}),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function publishManagedGcArtifactsRemoved(
|
|
211
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
212
|
+
target: ManagedGcOwnerCleanupTarget,
|
|
213
|
+
): Promise<ManagedGcSessionRetirementReceipt | undefined> {
|
|
214
|
+
if (!target.taskArtifactOwnerDeletionEvidence) return undefined;
|
|
215
|
+
const receipt = await authority.readReceipt(target.path);
|
|
216
|
+
if (!receipt) throw new Error("task_artifact_owner_continuation_state_missing");
|
|
217
|
+
if (!deepSame(receipt.taskArtifactOwnerDeletionEvidence, target.taskArtifactOwnerDeletionEvidence))
|
|
218
|
+
throw new Error("task_artifact_owner_continuation_evidence_mismatch");
|
|
219
|
+
if (receipt.state !== "prepared") return receipt;
|
|
220
|
+
return authority.publishReceipt({
|
|
221
|
+
...managedGcReceiptTarget(receipt),
|
|
222
|
+
state: "artifacts_removed",
|
|
223
|
+
taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence,
|
|
224
|
+
artifactsRemoved: true,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function retireManagedGcOwnerAfterArtifacts(
|
|
229
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
230
|
+
target: ManagedGcOwnerCleanupTarget,
|
|
231
|
+
tombstoneTargets: readonly ManagedGcOwnerCleanupTarget[],
|
|
232
|
+
): Promise<ManagedGcOwnerProgress> {
|
|
233
|
+
if (!target.taskArtifactOwnerDeletionEvidence) return { state: "none" };
|
|
234
|
+
const receipt = await publishManagedGcArtifactsRemoved(authority, target);
|
|
235
|
+
if (!receipt) return { state: "none" };
|
|
236
|
+
const evidence = receipt.taskArtifactOwnerDeletionEvidence;
|
|
237
|
+
if (!evidence) throw new Error("task_artifact_owner_continuation_evidence_missing");
|
|
238
|
+
authority.assertOwned();
|
|
239
|
+
const sharedBefore = await hasSiblingTaskArtifactOwnerTranscript(
|
|
240
|
+
new FileSessionStorage(),
|
|
241
|
+
target.path,
|
|
242
|
+
evidence.locator,
|
|
243
|
+
authority.storageContext,
|
|
244
|
+
authority.inspectProtocol,
|
|
245
|
+
);
|
|
246
|
+
authority.assertOwned();
|
|
247
|
+
if (sharedBefore) return { state: "pending", message: "task_artifact_owner_shared_with_sibling_transcript" };
|
|
248
|
+
if (receipt.state === "owner_retired") return { state: "owner_retired" };
|
|
249
|
+
for (const sibling of tombstoneTargets) {
|
|
250
|
+
if (
|
|
251
|
+
sibling.path === target.path ||
|
|
252
|
+
!sibling.taskArtifactOwnerDeletionEvidence ||
|
|
253
|
+
!deepSame(sibling.taskArtifactOwnerDeletionEvidence, evidence)
|
|
254
|
+
)
|
|
255
|
+
continue;
|
|
256
|
+
const siblingReceipt = await authority.readReceipt(sibling.path);
|
|
257
|
+
const siblingOutcome = siblingReceipt?.taskArtifactOwnerRetirementOutcome;
|
|
258
|
+
if (
|
|
259
|
+
siblingReceipt?.state !== "owner_retired" ||
|
|
260
|
+
siblingOutcome?.kind !== "completed" ||
|
|
261
|
+
!deepSame(siblingReceipt.taskArtifactOwnerDeletionEvidence, evidence)
|
|
262
|
+
)
|
|
263
|
+
continue;
|
|
264
|
+
const inherited = await authority.publishReceipt({
|
|
265
|
+
...managedGcReceiptTarget(receipt),
|
|
266
|
+
state: "owner_retired",
|
|
267
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
268
|
+
artifactsRemoved: true,
|
|
269
|
+
taskArtifactOwnerRetirementOutcome: siblingOutcome,
|
|
270
|
+
taskArtifactOwnerRetired: true,
|
|
271
|
+
});
|
|
272
|
+
if (inherited.state !== "owner_retired") throw new Error("task_artifact_owner_inheritance_pending");
|
|
273
|
+
return { state: "owner_retired" };
|
|
274
|
+
}
|
|
275
|
+
const inherited = await authority.findCompletedRetirement(evidence, target.path);
|
|
276
|
+
if (inherited) {
|
|
277
|
+
if (
|
|
278
|
+
inherited.scope.agentDir !== authority.agentDir ||
|
|
279
|
+
inherited.scope.sessionsRoot !== authority.sessionsRoot ||
|
|
280
|
+
inherited.scope.directoryPath === authority.directoryPath ||
|
|
281
|
+
path.dirname(inherited.receipt.transcriptPath) !== inherited.scope.directoryPath ||
|
|
282
|
+
inherited.receipt.transcriptPath === target.path ||
|
|
283
|
+
inherited.receipt.state !== "owner_retired"
|
|
284
|
+
)
|
|
285
|
+
throw new Error("task_artifact_owner_continuation_authority_mismatch");
|
|
286
|
+
const inheritedEvidence = parseTaskArtifactOwnerDeletionEvidence(
|
|
287
|
+
inherited.receipt.taskArtifactOwnerDeletionEvidence,
|
|
288
|
+
);
|
|
289
|
+
if (!deepSame(inheritedEvidence, evidence)) throw new Error("task_artifact_owner_continuation_evidence_mismatch");
|
|
290
|
+
const inheritedOutcome = parseTaskArtifactOwnerRetirementOutcome(
|
|
291
|
+
authority.storageContext,
|
|
292
|
+
evidence,
|
|
293
|
+
inherited.receipt.taskArtifactOwnerRetirementOutcome,
|
|
294
|
+
);
|
|
295
|
+
if (inheritedOutcome.kind !== "completed") throw new Error("task_artifact_owner_physical_retirement_unverified");
|
|
296
|
+
verifyTaskArtifactOwnerPhysicalRetirement(authority.storageContext, evidence, inheritedOutcome);
|
|
297
|
+
authority.assertOwned();
|
|
298
|
+
const sharedAfterInheritance = await hasSiblingTaskArtifactOwnerTranscript(
|
|
299
|
+
new FileSessionStorage(),
|
|
300
|
+
target.path,
|
|
301
|
+
evidence.locator,
|
|
302
|
+
authority.storageContext,
|
|
303
|
+
authority.inspectProtocol,
|
|
304
|
+
);
|
|
305
|
+
authority.assertOwned();
|
|
306
|
+
if (sharedAfterInheritance)
|
|
307
|
+
return { state: "pending", message: "task_artifact_owner_shared_with_sibling_transcript" };
|
|
308
|
+
const published = await authority.publishReceipt({
|
|
309
|
+
...managedGcReceiptTarget(receipt),
|
|
310
|
+
state: "owner_retired",
|
|
311
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
312
|
+
artifactsRemoved: true,
|
|
313
|
+
taskArtifactOwnerRetirementOutcome: inheritedOutcome,
|
|
314
|
+
taskArtifactOwnerRetired: true,
|
|
315
|
+
});
|
|
316
|
+
if (published.state !== "owner_retired") throw new Error("task_artifact_owner_inheritance_pending");
|
|
317
|
+
return { state: "owner_retired" };
|
|
318
|
+
}
|
|
319
|
+
const previous = receipt.taskArtifactOwnerRetirementOutcome;
|
|
320
|
+
const continuation = previous && previous.kind !== "completed" ? previous.continuation : undefined;
|
|
321
|
+
authority.assertOwned();
|
|
322
|
+
const outcome = retireTaskArtifactOwner(authority.storageContext, evidence, continuation);
|
|
323
|
+
authority.assertOwned();
|
|
324
|
+
if (outcome.kind === "completed") {
|
|
325
|
+
await authority.publishReceipt({
|
|
326
|
+
...managedGcReceiptTarget(receipt),
|
|
327
|
+
state: "owner_retired",
|
|
328
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
329
|
+
artifactsRemoved: true,
|
|
330
|
+
taskArtifactOwnerRetirementOutcome: outcome,
|
|
331
|
+
taskArtifactOwnerRetired: true,
|
|
332
|
+
});
|
|
333
|
+
return { state: "owner_retired" };
|
|
334
|
+
}
|
|
335
|
+
await authority.publishReceipt({
|
|
336
|
+
...managedGcReceiptTarget(receipt),
|
|
337
|
+
state: "owner_pending",
|
|
338
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
339
|
+
artifactsRemoved: true,
|
|
340
|
+
taskArtifactOwnerRetirementOutcome: outcome,
|
|
341
|
+
taskArtifactOwnerRetirementContinuation: outcome.continuation,
|
|
342
|
+
...(outcome.kind === "payload_retired"
|
|
343
|
+
? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
|
|
344
|
+
: {}),
|
|
345
|
+
});
|
|
346
|
+
return outcome.kind === "payload_retired"
|
|
347
|
+
? { state: "payload_retired", message: "task_artifact_owner_namespace_cleanup_pending" }
|
|
348
|
+
: {
|
|
349
|
+
state: "pending",
|
|
350
|
+
message: outcome.kind === "uncertain" ? outcome.reason : "task_artifact_owner_namespace_cleanup_pending",
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export async function persistManagedGcStorageOwnerDisposition(
|
|
355
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
356
|
+
target: ManagedGcOwnerCleanupTarget,
|
|
357
|
+
deletion: Extract<VerifiedSessionDeleteResult, { kind: "cleanup_pending"; phase: "task_artifact_owner" }>,
|
|
358
|
+
): Promise<ManagedGcOwnerProgress> {
|
|
359
|
+
const evidence = parseTaskArtifactOwnerDeletionEvidence(deletion.taskArtifactOwnerDeletionEvidence);
|
|
360
|
+
if (!target.taskArtifactOwnerDeletionEvidence || !deepSame(evidence, target.taskArtifactOwnerDeletionEvidence))
|
|
361
|
+
throw new Error("task_artifact_owner_continuation_evidence_mismatch");
|
|
362
|
+
if (!deletion.artifactsRemoved) return { state: "pending", message: deletion.error.message };
|
|
363
|
+
const receipt = await publishManagedGcArtifactsRemoved(authority, target);
|
|
364
|
+
if (!receipt) throw new Error("task_artifact_owner_continuation_state_missing");
|
|
365
|
+
const outcomeValue = deletion.taskArtifactOwnerRetirementOutcome;
|
|
366
|
+
if (!outcomeValue) return { state: "pending", message: deletion.error.message };
|
|
367
|
+
const outcome = parseTaskArtifactOwnerRetirementOutcome(authority.storageContext, evidence, outcomeValue);
|
|
368
|
+
if (
|
|
369
|
+
(deletion.taskArtifactOwnerRetired === true) !== (outcome.kind === "completed") ||
|
|
370
|
+
(deletion.taskArtifactOwnerPayloadRetired === true) !== (outcome.kind === "payload_retired") ||
|
|
371
|
+
(deletion.taskArtifactOwnerNamespaceRetained === true) !== (outcome.kind === "payload_retired") ||
|
|
372
|
+
(outcome.kind !== "completed" &&
|
|
373
|
+
!deepSame(deletion.taskArtifactOwnerRetirementContinuation, outcome.continuation))
|
|
374
|
+
)
|
|
375
|
+
throw new Error("task_artifact_owner_retirement_outcome_mismatch");
|
|
376
|
+
if (outcome.kind === "completed") {
|
|
377
|
+
await authority.publishReceipt({
|
|
378
|
+
...managedGcReceiptTarget(receipt),
|
|
379
|
+
state: "owner_retired",
|
|
380
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
381
|
+
artifactsRemoved: true,
|
|
382
|
+
taskArtifactOwnerRetirementOutcome: outcome,
|
|
383
|
+
taskArtifactOwnerRetired: true,
|
|
384
|
+
});
|
|
385
|
+
return { state: "owner_retired" };
|
|
386
|
+
}
|
|
387
|
+
await authority.publishReceipt({
|
|
388
|
+
...managedGcReceiptTarget(receipt),
|
|
389
|
+
state: "owner_pending",
|
|
390
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
391
|
+
artifactsRemoved: true,
|
|
392
|
+
taskArtifactOwnerRetirementOutcome: outcome,
|
|
393
|
+
taskArtifactOwnerRetirementContinuation: outcome.continuation,
|
|
394
|
+
...(outcome.kind === "payload_retired"
|
|
395
|
+
? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
|
|
396
|
+
: {}),
|
|
397
|
+
});
|
|
398
|
+
return outcome.kind === "payload_retired"
|
|
399
|
+
? { state: "payload_retired", message: "task_artifact_owner_namespace_cleanup_pending" }
|
|
400
|
+
: {
|
|
401
|
+
state: "pending",
|
|
402
|
+
message: outcome.kind === "uncertain" ? outcome.reason : "task_artifact_owner_namespace_cleanup_pending",
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export async function managedGcOwnerProgressBeforeDelete(
|
|
407
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
408
|
+
target: ManagedGcOwnerCleanupTarget,
|
|
409
|
+
tombstoneTargets: readonly ManagedGcOwnerCleanupTarget[],
|
|
410
|
+
artifactsRemoved: boolean,
|
|
411
|
+
): Promise<ManagedGcOwnerProgress> {
|
|
412
|
+
const evidence = target.taskArtifactOwnerDeletionEvidence;
|
|
413
|
+
if (!evidence) return { state: "none" };
|
|
414
|
+
const receipt = await authority.readReceipt(target.path);
|
|
415
|
+
if (!receipt || !deepSame(receipt.taskArtifactOwnerDeletionEvidence, evidence))
|
|
416
|
+
throw new Error("task_artifact_owner_continuation_state_missing");
|
|
417
|
+
if (receipt.state === "prepared" && !artifactsRemoved) {
|
|
418
|
+
authority.assertOwned();
|
|
419
|
+
const shared = await hasSiblingTaskArtifactOwnerTranscript(
|
|
420
|
+
new FileSessionStorage(),
|
|
421
|
+
target.path,
|
|
422
|
+
evidence.locator,
|
|
423
|
+
authority.storageContext,
|
|
424
|
+
authority.inspectProtocol,
|
|
425
|
+
);
|
|
426
|
+
authority.assertOwned();
|
|
427
|
+
return shared
|
|
428
|
+
? { state: "pending", message: "task_artifact_owner_shared_with_sibling_transcript" }
|
|
429
|
+
: { state: "none" };
|
|
430
|
+
}
|
|
431
|
+
return retireManagedGcOwnerAfterArtifacts(authority, target, tombstoneTargets);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export async function taskArtifactOwnerTranscriptResult(
|
|
435
|
+
authority: ManagedGcOwnerCleanupAuthority,
|
|
436
|
+
target: ManagedGcOwnerCleanupTarget,
|
|
437
|
+
): Promise<"none" | "retired" | "payload_retired"> {
|
|
438
|
+
if (path.dirname(target.path) !== authority.directoryPath) {
|
|
439
|
+
if (hasUnsupportedLegacyOwnerTarget(authority.directoryPath, target))
|
|
440
|
+
throw new Error("task_artifact_owner_legacy_scope_unsupported");
|
|
441
|
+
return "none";
|
|
442
|
+
}
|
|
443
|
+
const receipt = await authority.readReceipt(target.path);
|
|
444
|
+
if (!receipt) {
|
|
445
|
+
if (target.taskArtifactOwnerDeletionEvidence) throw new Error("task_artifact_owner_continuation_state_missing");
|
|
446
|
+
return "none";
|
|
447
|
+
}
|
|
448
|
+
if (!deepSame(receipt.taskArtifactOwnerDeletionEvidence, target.taskArtifactOwnerDeletionEvidence))
|
|
449
|
+
throw new Error("task_artifact_owner_continuation_evidence_mismatch");
|
|
450
|
+
if (receipt.state === "owner_retired") return "retired";
|
|
451
|
+
return receipt.taskArtifactOwnerRetirementOutcome?.kind === "payload_retired" ? "payload_retired" : "none";
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export interface ManagedGcOwnerTranscriptDeleteReceipt {
|
|
455
|
+
readonly taskArtifactOwnerTranscriptDeleted?: true;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export async function persistManagedGcOwnerTranscriptDeletion<T extends ManagedGcOwnerTranscriptDeleteReceipt>(input: {
|
|
459
|
+
target: ManagedGcOwnerCleanupTarget;
|
|
460
|
+
fallback: T;
|
|
461
|
+
deletion: Extract<VerifiedSessionDeleteResult, { kind: "deleted" }>;
|
|
462
|
+
readLatest: () => T | undefined;
|
|
463
|
+
nextReceipt: (latest: T) => T;
|
|
464
|
+
publishPending: (receipt: T) => Promise<void>;
|
|
465
|
+
}): Promise<void> {
|
|
466
|
+
if (!input.target.taskArtifactOwnerDeletionEvidence) return;
|
|
467
|
+
if (input.deletion.taskArtifactOwnerTranscriptDeleted !== true)
|
|
468
|
+
throw new Error("task_artifact_owner_transcript_deletion_unverified");
|
|
469
|
+
const latest = input.readLatest() ?? input.fallback;
|
|
470
|
+
if (latest.taskArtifactOwnerTranscriptDeleted) return;
|
|
471
|
+
const next = input.nextReceipt(latest);
|
|
472
|
+
await input.publishPending({ ...next, taskArtifactOwnerTranscriptDeleted: true as const });
|
|
473
|
+
}
|
|
@@ -26,7 +26,10 @@ export interface OwnerManifest extends TaskArtifactOwnerLocator {
|
|
|
26
26
|
readonly sessionId: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
function sessionRootStore(
|
|
30
|
+
context: TaskArtifactOwnerStorageContext,
|
|
31
|
+
access: "read-only" | "read-write",
|
|
32
|
+
): ManagedSessionDescendantStore {
|
|
30
33
|
assertSessionRoot(context);
|
|
31
34
|
if (path.resolve(context.profileAgentDir) !== context.profileAgentDir)
|
|
32
35
|
throw new Error("task_artifact_owner_profile_invalid");
|
|
@@ -45,9 +48,19 @@ export function newSessionRootStore(context: TaskArtifactOwnerStorageContext): M
|
|
|
45
48
|
dev: BigInt.asUintN(64, stat.dev),
|
|
46
49
|
ino: BigInt.asUintN(64, stat.ino),
|
|
47
50
|
},
|
|
51
|
+
access,
|
|
48
52
|
);
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
export function newSessionRootStore(context: TaskArtifactOwnerStorageContext): ManagedSessionDescendantStore {
|
|
56
|
+
return sessionRootStore(context, "read-write");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Path-backed reader with no retained or recovery-mutable native authority. */
|
|
60
|
+
export function newSessionRootReaderStore(context: TaskArtifactOwnerStorageContext): ManagedSessionDescendantStore {
|
|
61
|
+
return sessionRootStore(context, "read-only");
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
function parseOwnerManifest(bytes: Uint8Array): OwnerManifest {
|
|
52
65
|
let parsed: unknown;
|
|
53
66
|
try {
|
|
@@ -141,7 +154,8 @@ export function openOwnerStore(
|
|
|
141
154
|
);
|
|
142
155
|
try {
|
|
143
156
|
assertOwnerIdentity(ownerStore, locator);
|
|
144
|
-
readOwnerManifest(ownerStore, locator, OWNER_MANIFEST);
|
|
157
|
+
const finalManifest = readOwnerManifest(ownerStore, locator, OWNER_MANIFEST);
|
|
158
|
+
if (finalManifest.sessionId !== sessionId) throw new Error("task_artifact_owner_session_mismatch");
|
|
145
159
|
return ownerStore;
|
|
146
160
|
} catch (error) {
|
|
147
161
|
ownerStore.close();
|
|
@@ -211,7 +225,7 @@ export function captureTaskArtifactOwnerDeletionEvidence(
|
|
|
211
225
|
): TaskArtifactOwnerDeletionEvidence | undefined {
|
|
212
226
|
const locator = parseTaskArtifactOwnerLocator(locatorValue);
|
|
213
227
|
if (!locator) return undefined;
|
|
214
|
-
const rootStore =
|
|
228
|
+
const rootStore = newSessionRootReaderStore(context);
|
|
215
229
|
try {
|
|
216
230
|
rootStore.verifyRootSecurity();
|
|
217
231
|
const parentIdentity: TaskArtifactOwnerParentIdentity = rootStore.captureDirectoryIdentity(OWNER_DIRECTORY);
|
|
@@ -219,6 +233,8 @@ export function captureTaskArtifactOwnerDeletionEvidence(
|
|
|
219
233
|
// This refuses to capture during publication; it does not revoke independent open descriptors.
|
|
220
234
|
if (ownerTreeHasPendingManagedPublication(treeSnapshot))
|
|
221
235
|
throw new Error("task_artifact_owner_writer_not_quiescent");
|
|
236
|
+
const finalManifest = readOwnerManifest(rootStore, locator);
|
|
237
|
+
if (finalManifest.sessionId !== sessionId) throw new Error("task_artifact_owner_session_mismatch");
|
|
222
238
|
const parentAfter = rootStore.captureDirectoryIdentity(OWNER_DIRECTORY);
|
|
223
239
|
if (!sameOwnerParentIdentity(parentIdentity, parentAfter))
|
|
224
240
|
throw new Error("task_artifact_owner_parent_changed_during_capture");
|