@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
|
@@ -1,14 +1,49 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import * as util from "node:util";
|
|
3
4
|
|
|
4
5
|
import { toError } from "@gajae-code/utils";
|
|
6
|
+
import type {
|
|
7
|
+
ManagedGcSessionRetirementReceipt,
|
|
8
|
+
ManagedGcSessionRetirementTarget,
|
|
9
|
+
} from "./internal/managed-gc-retirement-codec";
|
|
10
|
+
import {
|
|
11
|
+
bindManagedGcSessionRetirementTarget,
|
|
12
|
+
type ManagedScope,
|
|
13
|
+
managedGcProtocolScopeInspectorForScope,
|
|
14
|
+
publishManagedGcSessionRetirementReceipt,
|
|
15
|
+
readManagedGcSessionRetirementReceiptReadOnly,
|
|
16
|
+
taskArtifactOwnerStorageContextForScope,
|
|
17
|
+
} from "./internal/managed-session-scope";
|
|
18
|
+
import { captureTaskArtifactOwnerDeletionEvidence } from "./internal/task-artifact-owner-access";
|
|
19
|
+
import {
|
|
20
|
+
hasSiblingTaskArtifactOwnerTranscript,
|
|
21
|
+
taskArtifactOwnerLocatorFromTranscriptBytes,
|
|
22
|
+
} from "./internal/task-artifact-owner-transcript";
|
|
5
23
|
import {
|
|
6
24
|
type FileSessionStorage,
|
|
7
25
|
SessionDeleteVerificationError,
|
|
26
|
+
type SessionStorageFileIdentity,
|
|
8
27
|
type SessionStorageSnapshot,
|
|
9
28
|
type VerifiedSessionDeleteTarget,
|
|
10
29
|
} from "./session-storage";
|
|
11
30
|
import { parseFirstJsonlLine } from "./session-transcript-header";
|
|
31
|
+
import type {
|
|
32
|
+
TaskArtifactOwnerDeletionEvidence,
|
|
33
|
+
TaskArtifactOwnerLocator,
|
|
34
|
+
TaskArtifactOwnerRetirementContinuation,
|
|
35
|
+
TaskArtifactOwnerRetirementOutcome,
|
|
36
|
+
TaskArtifactOwnerStorageContext,
|
|
37
|
+
} from "./task-artifact-owner-codec";
|
|
38
|
+
import {
|
|
39
|
+
retireTaskArtifactOwner,
|
|
40
|
+
verifyTaskArtifactOwnerPhysicalRetirement,
|
|
41
|
+
verifyTaskArtifactOwnerRetirementContinuation,
|
|
42
|
+
} from "./task-artifact-owner-retirement";
|
|
43
|
+
|
|
44
|
+
export interface SessionRetirementContinuation {
|
|
45
|
+
readonly managedScope: ManagedScope;
|
|
46
|
+
}
|
|
12
47
|
|
|
13
48
|
/**
|
|
14
49
|
* Outcome of a disk-retention retirement attempt.
|
|
@@ -21,25 +56,143 @@ import { parseFirstJsonlLine } from "./session-transcript-header";
|
|
|
21
56
|
* as an ordinary keep.
|
|
22
57
|
*/
|
|
23
58
|
export type SessionRetirementOutcome =
|
|
24
|
-
| { kind: "retired" }
|
|
59
|
+
| { kind: "retired"; taskArtifactOwnerRetired?: true }
|
|
25
60
|
| { kind: "kept"; reason: string }
|
|
26
|
-
| {
|
|
61
|
+
| {
|
|
62
|
+
kind: "cleanup_pending";
|
|
63
|
+
reason: string;
|
|
64
|
+
phase?: "artifacts" | "transcript";
|
|
65
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
66
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
67
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
68
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
69
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
70
|
+
taskArtifactOwnerRetired?: true;
|
|
71
|
+
};
|
|
27
72
|
|
|
28
73
|
/** A retirement that cleared every precondition the retention pass itself owns. */
|
|
29
74
|
type SessionRetirementPlan =
|
|
30
|
-
| {
|
|
75
|
+
| {
|
|
76
|
+
kind: "retirable";
|
|
77
|
+
target: VerifiedSessionDeleteTarget;
|
|
78
|
+
artifactsPresent: boolean;
|
|
79
|
+
taskArtifactOwnerLocator?: TaskArtifactOwnerLocator;
|
|
80
|
+
gcContinuationReceipt?: ManagedGcSessionRetirementReceipt;
|
|
81
|
+
}
|
|
82
|
+
| { kind: "owner_shared"; reason: string; gcContinuationReceipt?: ManagedGcSessionRetirementReceipt }
|
|
83
|
+
| { kind: "cleanup_pending"; reason: string; gcContinuationReceipt: ManagedGcSessionRetirementReceipt }
|
|
31
84
|
| { kind: "kept"; reason: string };
|
|
32
85
|
|
|
86
|
+
type OwnerRetirementFields = {
|
|
87
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
88
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
89
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
90
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
91
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
92
|
+
taskArtifactOwnerRetired?: true;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type SessionRetirementProbeOutcome =
|
|
96
|
+
| Extract<SessionRetirementOutcome, { kind: "cleanup_pending" }>
|
|
97
|
+
| {
|
|
98
|
+
kind: "retirable";
|
|
99
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
100
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
101
|
+
taskArtifactOwnerRetired?: true;
|
|
102
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
103
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
104
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
105
|
+
}
|
|
106
|
+
| { kind: "kept"; reason: string };
|
|
107
|
+
|
|
108
|
+
function isWithinPath(root: string, target: string): boolean {
|
|
109
|
+
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
110
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function sameTranscriptIdentity(left: SessionStorageFileIdentity, right: SessionStorageFileIdentity): boolean {
|
|
114
|
+
return (
|
|
115
|
+
left.dev === right.dev &&
|
|
116
|
+
left.ino === right.ino &&
|
|
117
|
+
left.nlink === right.nlink &&
|
|
118
|
+
left.size === right.size &&
|
|
119
|
+
left.mtimeNs === right.mtimeNs &&
|
|
120
|
+
left.sha256 === right.sha256
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function sameManagedGcTarget(left: ManagedGcSessionRetirementTarget, right: ManagedGcSessionRetirementTarget): boolean {
|
|
125
|
+
return (
|
|
126
|
+
left.transcriptPath === right.transcriptPath &&
|
|
127
|
+
left.sessionId === right.sessionId &&
|
|
128
|
+
left.cwd === right.cwd &&
|
|
129
|
+
sameTranscriptIdentity(left.transcriptIdentity, right.transcriptIdentity) &&
|
|
130
|
+
util.isDeepStrictEqual(left.taskArtifactOwnerLocator, right.taskArtifactOwnerLocator)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function sameEvidence(left: TaskArtifactOwnerDeletionEvidence, right: TaskArtifactOwnerDeletionEvidence): boolean {
|
|
135
|
+
return util.isDeepStrictEqual(left, right);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function targetFromReceipt(receipt: ManagedGcSessionRetirementReceipt): ManagedGcSessionRetirementTarget {
|
|
139
|
+
return {
|
|
140
|
+
transcriptPath: receipt.transcriptPath,
|
|
141
|
+
sessionId: receipt.sessionId,
|
|
142
|
+
cwd: receipt.cwd,
|
|
143
|
+
transcriptIdentity: receipt.transcriptIdentity,
|
|
144
|
+
taskArtifactOwnerLocator: receipt.taskArtifactOwnerLocator,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function ownerOutcomeFields(
|
|
149
|
+
evidence: TaskArtifactOwnerDeletionEvidence,
|
|
150
|
+
outcome: TaskArtifactOwnerRetirementOutcome | undefined,
|
|
151
|
+
ownerRetired: boolean,
|
|
152
|
+
): OwnerRetirementFields {
|
|
153
|
+
return {
|
|
154
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
155
|
+
...(outcome ? { taskArtifactOwnerRetirementOutcome: outcome } : {}),
|
|
156
|
+
...(outcome && outcome.kind !== "completed"
|
|
157
|
+
? { taskArtifactOwnerRetirementContinuation: outcome.continuation }
|
|
158
|
+
: {}),
|
|
159
|
+
...(outcome?.kind === "payload_retired"
|
|
160
|
+
? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
|
|
161
|
+
: {}),
|
|
162
|
+
...(ownerRetired ? { taskArtifactOwnerRetired: true as const } : {}),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function planRetirementOutcome(
|
|
167
|
+
plan: Extract<SessionRetirementPlan, { kind: "retirable" }>,
|
|
168
|
+
): SessionRetirementProbeOutcome {
|
|
169
|
+
const receipt = plan.gcContinuationReceipt;
|
|
170
|
+
const outcome = receipt?.taskArtifactOwnerRetirementOutcome;
|
|
171
|
+
return {
|
|
172
|
+
kind: "retirable",
|
|
173
|
+
...(receipt ? { taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence } : {}),
|
|
174
|
+
...(outcome ? { taskArtifactOwnerRetirementOutcome: outcome } : {}),
|
|
175
|
+
...(receipt?.state === "owner_retired" ? { taskArtifactOwnerRetired: true as const } : {}),
|
|
176
|
+
...(outcome && outcome.kind !== "completed"
|
|
177
|
+
? { taskArtifactOwnerRetirementContinuation: outcome.continuation }
|
|
178
|
+
: {}),
|
|
179
|
+
...(outcome?.kind === "payload_retired"
|
|
180
|
+
? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
|
|
181
|
+
: {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
33
185
|
/**
|
|
34
186
|
* Evaluate the preconditions retirement owns — the transcript must be readable
|
|
35
187
|
* and carry a usable session header — and bind the delete target to the exact
|
|
36
188
|
* bytes just read. Nothing here mutates the store.
|
|
37
189
|
*/
|
|
38
|
-
function planSessionRetirement(
|
|
190
|
+
async function planSessionRetirement(
|
|
39
191
|
storage: FileSessionStorage,
|
|
40
192
|
sessionsRoot: string,
|
|
41
193
|
transcriptPath: string,
|
|
42
|
-
|
|
194
|
+
continuation?: SessionRetirementContinuation,
|
|
195
|
+
): Promise<SessionRetirementPlan> {
|
|
43
196
|
let snapshot: SessionStorageSnapshot;
|
|
44
197
|
try {
|
|
45
198
|
snapshot = storage.readSnapshotSync(transcriptPath);
|
|
@@ -55,17 +208,107 @@ function planSessionRetirement(
|
|
|
55
208
|
// The verified delete authority only unlinks a single-link transcript, so a
|
|
56
209
|
// hard-linked one can never be retired. Refusing here keeps the dry run from
|
|
57
210
|
// promising bytes prune cannot release, and — because this runs before the
|
|
58
|
-
// artifact phase — keeps a doomed retirement from destroying
|
|
59
|
-
// tree of a session whose transcript will survive anyway.
|
|
211
|
+
// artifact phase — keeps a doomed retirement from destroying its artifacts.
|
|
60
212
|
if (snapshot.stat.nlink === undefined || snapshot.stat.nlink !== 1n) {
|
|
61
213
|
return { kind: "kept", reason: "transcript_not_single_link" };
|
|
62
214
|
}
|
|
63
215
|
|
|
216
|
+
let ownerLocator: TaskArtifactOwnerLocator | undefined;
|
|
217
|
+
try {
|
|
218
|
+
ownerLocator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, header.id);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
return { kind: "kept", reason: `task_artifact_owner_locator_invalid: ${toError(error).message}` };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let ownerContext: TaskArtifactOwnerStorageContext | undefined;
|
|
224
|
+
let ownerEvidence: TaskArtifactOwnerDeletionEvidence | undefined;
|
|
225
|
+
let gcContinuationReceipt: ManagedGcSessionRetirementReceipt | undefined;
|
|
226
|
+
let managedTarget: ManagedGcSessionRetirementTarget | undefined;
|
|
227
|
+
let partialReceipt: ManagedGcSessionRetirementReceipt | undefined;
|
|
228
|
+
const continuationRefusal = (reason: string): SessionRetirementPlan =>
|
|
229
|
+
partialReceipt
|
|
230
|
+
? { kind: "cleanup_pending", reason, gcContinuationReceipt: partialReceipt }
|
|
231
|
+
: { kind: "kept", reason };
|
|
232
|
+
if (ownerLocator) {
|
|
233
|
+
if (!continuation) return { kind: "kept", reason: "task_artifact_owner_context_missing" };
|
|
234
|
+
try {
|
|
235
|
+
ownerContext = taskArtifactOwnerStorageContextForScope(continuation.managedScope);
|
|
236
|
+
if (path.resolve(ownerContext.sessionsRoot) !== path.resolve(sessionsRoot))
|
|
237
|
+
return { kind: "kept", reason: "task_artifact_owner_root_mismatch" };
|
|
238
|
+
managedTarget = bindManagedGcSessionRetirementTarget(continuation.managedScope, transcriptPath);
|
|
239
|
+
const snapshotIdentity: SessionStorageFileIdentity = {
|
|
240
|
+
dev: snapshot.stat.dev,
|
|
241
|
+
ino: snapshot.stat.ino,
|
|
242
|
+
nlink: snapshot.stat.nlink,
|
|
243
|
+
size: snapshot.stat.size,
|
|
244
|
+
mtimeNs: snapshot.stat.mtimeNs,
|
|
245
|
+
sha256: crypto.createHash("sha256").update(snapshot.bytes).digest("hex"),
|
|
246
|
+
};
|
|
247
|
+
if (
|
|
248
|
+
managedTarget.sessionId !== header.id ||
|
|
249
|
+
managedTarget.cwd !== header.cwd ||
|
|
250
|
+
!sameTranscriptIdentity(managedTarget.transcriptIdentity, snapshotIdentity) ||
|
|
251
|
+
!util.isDeepStrictEqual(managedTarget.taskArtifactOwnerLocator, ownerLocator)
|
|
252
|
+
)
|
|
253
|
+
return { kind: "kept", reason: "task_artifact_owner_continuation_transcript_replaced" };
|
|
254
|
+
gcContinuationReceipt = await readManagedGcSessionRetirementReceiptReadOnly(
|
|
255
|
+
continuation.managedScope,
|
|
256
|
+
transcriptPath,
|
|
257
|
+
);
|
|
258
|
+
if (gcContinuationReceipt) {
|
|
259
|
+
if (!sameManagedGcTarget(gcContinuationReceipt, managedTarget))
|
|
260
|
+
return { kind: "kept", reason: "task_artifact_owner_continuation_transcript_replaced" };
|
|
261
|
+
if (gcContinuationReceipt.artifactsRemoved) partialReceipt = gcContinuationReceipt;
|
|
262
|
+
ownerEvidence = gcContinuationReceipt.taskArtifactOwnerDeletionEvidence;
|
|
263
|
+
if (!ownerEvidence) return continuationRefusal("task_artifact_owner_continuation_evidence_missing");
|
|
264
|
+
if (gcContinuationReceipt.state === "prepared" || gcContinuationReceipt.state === "artifacts_removed") {
|
|
265
|
+
const currentEvidence = captureTaskArtifactOwnerDeletionEvidence(ownerContext, header.id, ownerLocator);
|
|
266
|
+
if (!currentEvidence || !sameEvidence(currentEvidence, ownerEvidence))
|
|
267
|
+
return continuationRefusal("task_artifact_owner_continuation_evidence_mismatch");
|
|
268
|
+
} else if (gcContinuationReceipt.state === "owner_pending") {
|
|
269
|
+
const outcome = gcContinuationReceipt.taskArtifactOwnerRetirementOutcome;
|
|
270
|
+
if (!outcome || outcome.kind === "completed")
|
|
271
|
+
return continuationRefusal("task_artifact_owner_continuation_outcome_mismatch");
|
|
272
|
+
verifyTaskArtifactOwnerRetirementContinuation(ownerContext, ownerEvidence, outcome.continuation);
|
|
273
|
+
} else {
|
|
274
|
+
const outcome = gcContinuationReceipt.taskArtifactOwnerRetirementOutcome;
|
|
275
|
+
if (outcome?.kind !== "completed")
|
|
276
|
+
return continuationRefusal("task_artifact_owner_continuation_outcome_mismatch");
|
|
277
|
+
verifyTaskArtifactOwnerPhysicalRetirement(ownerContext, ownerEvidence, outcome);
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
ownerEvidence = captureTaskArtifactOwnerDeletionEvidence(ownerContext, header.id, ownerLocator);
|
|
281
|
+
if (!ownerEvidence) return { kind: "kept", reason: "task_artifact_owner_continuation_evidence_missing" };
|
|
282
|
+
}
|
|
283
|
+
if (
|
|
284
|
+
await hasSiblingTaskArtifactOwnerTranscript(
|
|
285
|
+
storage,
|
|
286
|
+
transcriptPath,
|
|
287
|
+
ownerLocator,
|
|
288
|
+
ownerContext,
|
|
289
|
+
managedGcProtocolScopeInspectorForScope(continuation.managedScope),
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
return {
|
|
293
|
+
kind: "owner_shared",
|
|
294
|
+
reason: "task_artifact_owner_shared_with_sibling_transcript",
|
|
295
|
+
...(gcContinuationReceipt ? { gcContinuationReceipt } : {}),
|
|
296
|
+
};
|
|
297
|
+
} catch (error) {
|
|
298
|
+
return continuationRefusal(`task_artifact_owner_continuation_invalid: ${toError(error).message}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
64
302
|
const directory = path.dirname(transcriptPath);
|
|
65
303
|
return {
|
|
66
304
|
kind: "retirable",
|
|
67
305
|
artifactsPresent: storage.existsSync(transcriptPath.slice(0, -".jsonl".length)),
|
|
306
|
+
...(ownerLocator ? { taskArtifactOwnerLocator: ownerLocator } : {}),
|
|
307
|
+
...(gcContinuationReceipt ? { gcContinuationReceipt } : {}),
|
|
68
308
|
target: {
|
|
309
|
+
...(ownerContext ? { taskArtifactOwnerStorageContext: ownerContext } : {}),
|
|
310
|
+
...(ownerEvidence ? { taskArtifactOwnerDeletionEvidence: ownerEvidence } : {}),
|
|
311
|
+
...(ownerLocator ? { deferTaskArtifactOwnerRetirement: true as const } : {}),
|
|
69
312
|
sessionsRoot,
|
|
70
313
|
transcriptPath,
|
|
71
314
|
sessionId: header.id,
|
|
@@ -93,13 +336,25 @@ function planSessionRetirement(
|
|
|
93
336
|
* authority's verdict (containment, identity, artifact tree) is deliberately
|
|
94
337
|
* not predicted here — it is re-derived against live state at delete time.
|
|
95
338
|
*/
|
|
96
|
-
export function probeSessionRetirement(
|
|
339
|
+
export async function probeSessionRetirement(
|
|
97
340
|
storage: FileSessionStorage,
|
|
98
341
|
sessionsRoot: string,
|
|
99
342
|
transcriptPath: string,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
343
|
+
continuation?: SessionRetirementContinuation,
|
|
344
|
+
): Promise<SessionRetirementProbeOutcome> {
|
|
345
|
+
const plan = await planSessionRetirement(storage, sessionsRoot, transcriptPath, continuation);
|
|
346
|
+
if (plan.kind === "kept") return plan;
|
|
347
|
+
if (plan.kind === "owner_shared" || plan.kind === "cleanup_pending") {
|
|
348
|
+
const receipt = plan.gcContinuationReceipt;
|
|
349
|
+
if (!receipt?.artifactsRemoved) return { kind: "kept", reason: plan.reason };
|
|
350
|
+
return cleanupPending(
|
|
351
|
+
plan.reason,
|
|
352
|
+
receipt.taskArtifactOwnerDeletionEvidence,
|
|
353
|
+
receipt.taskArtifactOwnerRetirementOutcome,
|
|
354
|
+
receipt.state === "owner_retired",
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return planRetirementOutcome(plan);
|
|
103
358
|
}
|
|
104
359
|
|
|
105
360
|
/**
|
|
@@ -110,6 +365,300 @@ function retirementIncomplete(artifactsRemoved: boolean, reason: string): Sessio
|
|
|
110
365
|
return artifactsRemoved ? { kind: "cleanup_pending", reason } : { kind: "kept", reason };
|
|
111
366
|
}
|
|
112
367
|
|
|
368
|
+
function cleanupPending(
|
|
369
|
+
reason: string,
|
|
370
|
+
evidence: TaskArtifactOwnerDeletionEvidence,
|
|
371
|
+
outcome: TaskArtifactOwnerRetirementOutcome | undefined,
|
|
372
|
+
ownerRetired: boolean,
|
|
373
|
+
phase: "artifacts" | "transcript" = "artifacts",
|
|
374
|
+
): Extract<SessionRetirementOutcome, { kind: "cleanup_pending" }> {
|
|
375
|
+
return { kind: "cleanup_pending", reason, phase, ...ownerOutcomeFields(evidence, outcome, ownerRetired) };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function errorMessage(error: unknown): string {
|
|
379
|
+
if (!(error instanceof AggregateError)) return toError(error).message;
|
|
380
|
+
const details = [...error.errors].map(cause => toError(cause).message).join("; ");
|
|
381
|
+
return details ? `${error.message}: ${details}` : error.message;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
type ManagedGcOwnerProgress =
|
|
385
|
+
| { kind: "owner_retired"; receipt: ManagedGcSessionRetirementReceipt; outcome: TaskArtifactOwnerRetirementOutcome }
|
|
386
|
+
| { kind: "owner_pending"; receipt: ManagedGcSessionRetirementReceipt; outcome: TaskArtifactOwnerRetirementOutcome };
|
|
387
|
+
|
|
388
|
+
async function advanceManagedGcOwnerRetirement(
|
|
389
|
+
scope: ManagedScope,
|
|
390
|
+
receipt: ManagedGcSessionRetirementReceipt,
|
|
391
|
+
context: TaskArtifactOwnerStorageContext,
|
|
392
|
+
evidence: TaskArtifactOwnerDeletionEvidence,
|
|
393
|
+
): Promise<ManagedGcOwnerProgress> {
|
|
394
|
+
if (receipt.state === "owner_retired") {
|
|
395
|
+
const outcome = receipt.taskArtifactOwnerRetirementOutcome;
|
|
396
|
+
if (outcome?.kind !== "completed") throw new Error("task_artifact_owner_continuation_outcome_mismatch");
|
|
397
|
+
verifyTaskArtifactOwnerPhysicalRetirement(context, evidence, outcome);
|
|
398
|
+
return { kind: "owner_retired", receipt, outcome };
|
|
399
|
+
}
|
|
400
|
+
if (receipt.state === "prepared") throw new Error("task_artifact_owner_continuation_state_order_invalid");
|
|
401
|
+
// `artifacts_removed` is the durable authorization for the first native
|
|
402
|
+
// scrub. Persist owner_pending only after that scrub returns its real proof;
|
|
403
|
+
// a replay from owner_pending revalidates the prior continuation first.
|
|
404
|
+
let previousContinuation: TaskArtifactOwnerRetirementContinuation | undefined;
|
|
405
|
+
if (receipt.state === "owner_pending") {
|
|
406
|
+
const previous = receipt.taskArtifactOwnerRetirementOutcome;
|
|
407
|
+
if (!previous || previous.kind === "completed")
|
|
408
|
+
throw new Error("task_artifact_owner_continuation_outcome_mismatch");
|
|
409
|
+
previousContinuation = verifyTaskArtifactOwnerRetirementContinuation(context, evidence, previous.continuation);
|
|
410
|
+
}
|
|
411
|
+
const outcome = retireTaskArtifactOwner(context, evidence, previousContinuation);
|
|
412
|
+
if (outcome.kind === "completed") {
|
|
413
|
+
const retired = await publishManagedGcSessionRetirementReceipt(scope, {
|
|
414
|
+
...targetFromReceipt(receipt),
|
|
415
|
+
state: "owner_retired",
|
|
416
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
417
|
+
artifactsRemoved: true,
|
|
418
|
+
taskArtifactOwnerRetirementOutcome: outcome,
|
|
419
|
+
taskArtifactOwnerRetired: true,
|
|
420
|
+
});
|
|
421
|
+
return { kind: "owner_retired", receipt: retired, outcome: retired.taskArtifactOwnerRetirementOutcome! };
|
|
422
|
+
}
|
|
423
|
+
const pending = await publishManagedGcSessionRetirementReceipt(scope, {
|
|
424
|
+
...targetFromReceipt(receipt),
|
|
425
|
+
state: "owner_pending",
|
|
426
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
427
|
+
artifactsRemoved: true,
|
|
428
|
+
taskArtifactOwnerRetirementOutcome: outcome,
|
|
429
|
+
taskArtifactOwnerRetirementContinuation: outcome.continuation,
|
|
430
|
+
...(outcome.kind === "payload_retired"
|
|
431
|
+
? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
|
|
432
|
+
: {}),
|
|
433
|
+
});
|
|
434
|
+
return { kind: "owner_pending", receipt: pending, outcome: pending.taskArtifactOwnerRetirementOutcome! };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Resume journaled owner cleanup without broadening the captured tree authority. */
|
|
438
|
+
async function retireGcOwnerTranscript(
|
|
439
|
+
storage: FileSessionStorage,
|
|
440
|
+
plan: Extract<SessionRetirementPlan, { kind: "retirable" }>,
|
|
441
|
+
continuation: SessionRetirementContinuation,
|
|
442
|
+
): Promise<SessionRetirementOutcome> {
|
|
443
|
+
const target = plan.target;
|
|
444
|
+
const ownerContext = target.taskArtifactOwnerStorageContext;
|
|
445
|
+
const evidence = target.taskArtifactOwnerDeletionEvidence;
|
|
446
|
+
const locator = plan.taskArtifactOwnerLocator;
|
|
447
|
+
if (!ownerContext || !evidence || !locator)
|
|
448
|
+
return { kind: "kept", reason: "task_artifact_owner_continuation_evidence_missing" };
|
|
449
|
+
const receiptTarget: ManagedGcSessionRetirementTarget = {
|
|
450
|
+
transcriptPath: target.transcriptPath,
|
|
451
|
+
sessionId: target.sessionId,
|
|
452
|
+
cwd: target.cwd,
|
|
453
|
+
transcriptIdentity: target.transcriptIdentity,
|
|
454
|
+
taskArtifactOwnerLocator: locator,
|
|
455
|
+
};
|
|
456
|
+
let receipt = plan.gcContinuationReceipt;
|
|
457
|
+
let artifactsRemoved = receipt?.artifactsRemoved === true;
|
|
458
|
+
let ownerOutcome = receipt?.taskArtifactOwnerRetirementOutcome;
|
|
459
|
+
let ownerRetired = receipt?.state === "owner_retired";
|
|
460
|
+
try {
|
|
461
|
+
if (
|
|
462
|
+
await hasSiblingTaskArtifactOwnerTranscript(
|
|
463
|
+
storage,
|
|
464
|
+
target.transcriptPath,
|
|
465
|
+
locator,
|
|
466
|
+
ownerContext,
|
|
467
|
+
managedGcProtocolScopeInspectorForScope(continuation.managedScope),
|
|
468
|
+
)
|
|
469
|
+
)
|
|
470
|
+
return artifactsRemoved
|
|
471
|
+
? cleanupPending("task_artifact_owner_shared_with_sibling_transcript", evidence, ownerOutcome, ownerRetired)
|
|
472
|
+
: { kind: "kept", reason: "task_artifact_owner_shared_with_sibling_transcript" };
|
|
473
|
+
|
|
474
|
+
if (!receipt) {
|
|
475
|
+
receipt = await publishManagedGcSessionRetirementReceipt(continuation.managedScope, {
|
|
476
|
+
...receiptTarget,
|
|
477
|
+
state: "prepared",
|
|
478
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
if (receipt.state === "prepared") {
|
|
482
|
+
const artifactPhase = await storage.deleteSessionVerified(
|
|
483
|
+
{
|
|
484
|
+
...target,
|
|
485
|
+
taskArtifactOwnerStorageContext: ownerContext,
|
|
486
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
487
|
+
deferTaskArtifactOwnerRetirement: true,
|
|
488
|
+
},
|
|
489
|
+
managedGcProtocolScopeInspectorForScope(continuation.managedScope),
|
|
490
|
+
);
|
|
491
|
+
if (artifactPhase.kind === "cleanup_pending") {
|
|
492
|
+
if (artifactPhase.phase === "task_artifact_owner" && artifactPhase.artifactsRemoved === false)
|
|
493
|
+
return { kind: "kept", reason: artifactPhase.error.message };
|
|
494
|
+
return cleanupPending(
|
|
495
|
+
`cleanup_pending_${artifactPhase.phase}: ${artifactPhase.error.message}`,
|
|
496
|
+
evidence,
|
|
497
|
+
ownerOutcome,
|
|
498
|
+
ownerRetired,
|
|
499
|
+
"artifacts",
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
if (artifactPhase.kind !== "artifacts_removed") {
|
|
503
|
+
return { kind: "kept", reason: "task_artifact_owner_continuation_artifact_phase_incomplete" };
|
|
504
|
+
}
|
|
505
|
+
artifactsRemoved = true;
|
|
506
|
+
receipt = await publishManagedGcSessionRetirementReceipt(continuation.managedScope, {
|
|
507
|
+
...receiptTarget,
|
|
508
|
+
state: "artifacts_removed",
|
|
509
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
510
|
+
artifactsRemoved: true,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (
|
|
514
|
+
await hasSiblingTaskArtifactOwnerTranscript(
|
|
515
|
+
storage,
|
|
516
|
+
target.transcriptPath,
|
|
517
|
+
locator,
|
|
518
|
+
ownerContext,
|
|
519
|
+
managedGcProtocolScopeInspectorForScope(continuation.managedScope),
|
|
520
|
+
)
|
|
521
|
+
)
|
|
522
|
+
return cleanupPending(
|
|
523
|
+
"task_artifact_owner_shared_with_sibling_transcript",
|
|
524
|
+
evidence,
|
|
525
|
+
ownerOutcome,
|
|
526
|
+
ownerRetired,
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
const progress = await advanceManagedGcOwnerRetirement(
|
|
530
|
+
continuation.managedScope,
|
|
531
|
+
receipt,
|
|
532
|
+
ownerContext,
|
|
533
|
+
evidence,
|
|
534
|
+
);
|
|
535
|
+
receipt = progress.receipt;
|
|
536
|
+
ownerOutcome = progress.outcome;
|
|
537
|
+
ownerRetired = progress.kind === "owner_retired";
|
|
538
|
+
if (progress.kind === "owner_pending")
|
|
539
|
+
return cleanupPending(
|
|
540
|
+
progress.outcome.kind === "uncertain"
|
|
541
|
+
? progress.outcome.reason
|
|
542
|
+
: "task_artifact_owner_namespace_cleanup_pending",
|
|
543
|
+
evidence,
|
|
544
|
+
progress.outcome,
|
|
545
|
+
false,
|
|
546
|
+
);
|
|
547
|
+
if (
|
|
548
|
+
await hasSiblingTaskArtifactOwnerTranscript(
|
|
549
|
+
storage,
|
|
550
|
+
target.transcriptPath,
|
|
551
|
+
locator,
|
|
552
|
+
ownerContext,
|
|
553
|
+
managedGcProtocolScopeInspectorForScope(continuation.managedScope),
|
|
554
|
+
)
|
|
555
|
+
)
|
|
556
|
+
return cleanupPending("task_artifact_owner_shared_with_sibling_transcript", evidence, ownerOutcome, true);
|
|
557
|
+
|
|
558
|
+
const deletion = await storage.deleteSessionVerified(
|
|
559
|
+
{
|
|
560
|
+
...target,
|
|
561
|
+
taskArtifactOwnerStorageContext: ownerContext,
|
|
562
|
+
taskArtifactOwnerDeletionEvidence: evidence,
|
|
563
|
+
taskArtifactOwnerRetirementOutcome: ownerOutcome,
|
|
564
|
+
taskArtifactOwnerRetired: true,
|
|
565
|
+
deferTaskArtifactOwnerRetirement: true,
|
|
566
|
+
artifactsRemoved: true,
|
|
567
|
+
},
|
|
568
|
+
managedGcProtocolScopeInspectorForScope(continuation.managedScope),
|
|
569
|
+
);
|
|
570
|
+
if (deletion.kind === "deleted") return { kind: "retired", taskArtifactOwnerRetired: true };
|
|
571
|
+
if (deletion.kind === "cleanup_pending")
|
|
572
|
+
return cleanupPending(
|
|
573
|
+
`cleanup_pending_${deletion.phase}: ${deletion.error.message}`,
|
|
574
|
+
evidence,
|
|
575
|
+
ownerOutcome,
|
|
576
|
+
true,
|
|
577
|
+
deletion.phase === "transcript" ? "transcript" : "artifacts",
|
|
578
|
+
);
|
|
579
|
+
return cleanupPending("transcript_not_deleted", evidence, ownerOutcome, true, "transcript");
|
|
580
|
+
} catch (error) {
|
|
581
|
+
const reason = `gc_task_artifact_owner_continuation_failed: ${errorMessage(error)}`;
|
|
582
|
+
return artifactsRemoved ? cleanupPending(reason, evidence, ownerOutcome, ownerRetired) : { kind: "kept", reason };
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
export async function retireOrphanSessionTranscript(
|
|
587
|
+
storage: FileSessionStorage,
|
|
588
|
+
sessionsRoot: string,
|
|
589
|
+
continuation: SessionRetirementContinuation,
|
|
590
|
+
receipt: ManagedGcSessionRetirementReceipt,
|
|
591
|
+
): Promise<SessionRetirementOutcome> {
|
|
592
|
+
const evidence = receipt.taskArtifactOwnerDeletionEvidence;
|
|
593
|
+
let ownerOutcome = receipt.taskArtifactOwnerRetirementOutcome;
|
|
594
|
+
let ownerRetired = receipt.state === "owner_retired";
|
|
595
|
+
const artifactsRemoved = receipt.artifactsRemoved === true;
|
|
596
|
+
try {
|
|
597
|
+
const ownerContext = taskArtifactOwnerStorageContextForScope(continuation.managedScope);
|
|
598
|
+
if (
|
|
599
|
+
path.resolve(ownerContext.sessionsRoot) !== path.resolve(sessionsRoot) ||
|
|
600
|
+
!path.isAbsolute(receipt.transcriptPath) ||
|
|
601
|
+
path.resolve(receipt.transcriptPath) !== receipt.transcriptPath ||
|
|
602
|
+
!isWithinPath(sessionsRoot, receipt.transcriptPath) ||
|
|
603
|
+
path.dirname(receipt.transcriptPath) !== continuation.managedScope.directoryPath
|
|
604
|
+
)
|
|
605
|
+
return { kind: "kept", reason: "task_artifact_owner_continuation_authority_mismatch" };
|
|
606
|
+
const currentReceipt = await readManagedGcSessionRetirementReceiptReadOnly(
|
|
607
|
+
continuation.managedScope,
|
|
608
|
+
receipt.transcriptPath,
|
|
609
|
+
);
|
|
610
|
+
if (!currentReceipt || !util.isDeepStrictEqual(currentReceipt, receipt))
|
|
611
|
+
return { kind: "kept", reason: "task_artifact_owner_continuation_state_changed" };
|
|
612
|
+
if (storage.existsSync(receipt.transcriptPath))
|
|
613
|
+
return { kind: "kept", reason: "task_artifact_owner_orphan_transcript_reappeared" };
|
|
614
|
+
if (receipt.state === "prepared")
|
|
615
|
+
return cleanupPending("transcript_missing_before_artifact_phase", evidence, ownerOutcome, false, "transcript");
|
|
616
|
+
if (
|
|
617
|
+
await hasSiblingTaskArtifactOwnerTranscript(
|
|
618
|
+
storage,
|
|
619
|
+
receipt.transcriptPath,
|
|
620
|
+
evidence.locator,
|
|
621
|
+
ownerContext,
|
|
622
|
+
managedGcProtocolScopeInspectorForScope(continuation.managedScope),
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
return cleanupPending(
|
|
626
|
+
"task_artifact_owner_shared_with_sibling_transcript",
|
|
627
|
+
evidence,
|
|
628
|
+
ownerOutcome,
|
|
629
|
+
ownerRetired,
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
const progress = await advanceManagedGcOwnerRetirement(
|
|
633
|
+
continuation.managedScope,
|
|
634
|
+
receipt,
|
|
635
|
+
ownerContext,
|
|
636
|
+
evidence,
|
|
637
|
+
);
|
|
638
|
+
ownerOutcome = progress.outcome;
|
|
639
|
+
ownerRetired = progress.kind === "owner_retired";
|
|
640
|
+
if (progress.kind === "owner_pending")
|
|
641
|
+
return cleanupPending(
|
|
642
|
+
progress.outcome.kind === "uncertain"
|
|
643
|
+
? progress.outcome.reason
|
|
644
|
+
: "task_artifact_owner_namespace_cleanup_pending",
|
|
645
|
+
evidence,
|
|
646
|
+
progress.outcome,
|
|
647
|
+
false,
|
|
648
|
+
);
|
|
649
|
+
return cleanupPending(
|
|
650
|
+
"transcript_missing_without_verified_retirement",
|
|
651
|
+
evidence,
|
|
652
|
+
ownerOutcome,
|
|
653
|
+
ownerRetired,
|
|
654
|
+
"transcript",
|
|
655
|
+
);
|
|
656
|
+
} catch (error) {
|
|
657
|
+
const reason = `gc_task_artifact_owner_continuation_failed: ${errorMessage(error)}`;
|
|
658
|
+
return artifactsRemoved ? cleanupPending(reason, evidence, ownerOutcome, ownerRetired) : { kind: "kept", reason };
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
113
662
|
/**
|
|
114
663
|
* Retire one managed session transcript through the verified hard-delete
|
|
115
664
|
* authority.
|
|
@@ -125,9 +674,21 @@ export async function retireSessionTranscript(
|
|
|
125
674
|
storage: FileSessionStorage,
|
|
126
675
|
sessionsRoot: string,
|
|
127
676
|
transcriptPath: string,
|
|
677
|
+
continuation?: SessionRetirementContinuation,
|
|
128
678
|
): Promise<SessionRetirementOutcome> {
|
|
129
|
-
const plan = planSessionRetirement(storage, sessionsRoot, transcriptPath);
|
|
679
|
+
const plan = await planSessionRetirement(storage, sessionsRoot, transcriptPath, continuation);
|
|
130
680
|
if (plan.kind === "kept") return plan;
|
|
681
|
+
if (plan.kind === "owner_shared" || plan.kind === "cleanup_pending") {
|
|
682
|
+
const receipt = plan.gcContinuationReceipt;
|
|
683
|
+
if (!receipt?.artifactsRemoved) return { kind: "kept", reason: plan.reason };
|
|
684
|
+
return cleanupPending(
|
|
685
|
+
plan.reason,
|
|
686
|
+
receipt.taskArtifactOwnerDeletionEvidence,
|
|
687
|
+
receipt.taskArtifactOwnerRetirementOutcome,
|
|
688
|
+
receipt.state === "owner_retired",
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
if (plan.taskArtifactOwnerLocator && continuation) return retireGcOwnerTranscript(storage, plan, continuation);
|
|
131
692
|
|
|
132
693
|
let artifactsRemoved = false;
|
|
133
694
|
try {
|
|
@@ -138,8 +699,6 @@ export async function retireSessionTranscript(
|
|
|
138
699
|
if (artifacts.kind === "cleanup_pending") {
|
|
139
700
|
return { kind: "cleanup_pending", reason: `cleanup_pending_${artifacts.phase}: ${artifacts.error.message}` };
|
|
140
701
|
}
|
|
141
|
-
// Phase 1 is durable: whatever artifact tree the session had is gone, so
|
|
142
|
-
// every later refusal leaves a record without its artifacts.
|
|
143
702
|
artifactsRemoved = plan.artifactsPresent;
|
|
144
703
|
const deletion = await storage.deleteSessionVerified({ ...plan.target, artifactsRemoved: true });
|
|
145
704
|
if (deletion.kind === "deleted") return { kind: "retired" };
|