@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
|
@@ -13,7 +13,7 @@ function nativeLifecycle(): typeof import("@gajae-code/natives") {
|
|
|
13
13
|
return nativeLifecycleBindings;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
import { $credentialEnv, logger, resolveEquivalentPath } from "@gajae-code/utils";
|
|
16
|
+
import { $credentialEnv, getSessionsDir, logger, resolveEquivalentPath } from "@gajae-code/utils";
|
|
17
17
|
import {
|
|
18
18
|
loadAcceptedModelPresetRegistry,
|
|
19
19
|
loadAcceptedModelPresetRegistryAsync,
|
|
@@ -43,7 +43,19 @@ import {
|
|
|
43
43
|
GJC_COORDINATOR_SIDECAR_SIGNATURE_REQUIRED_ENV,
|
|
44
44
|
GJC_COORDINATOR_SIDECAR_SIGNING_KEY_ENV,
|
|
45
45
|
} from "../../gjc-runtime/session-state-sidecar";
|
|
46
|
+
import {
|
|
47
|
+
type ManagedGcProtocolScopeInspector,
|
|
48
|
+
type ManagedScope,
|
|
49
|
+
managedGcProtocolScopeInspectorForScope,
|
|
50
|
+
resolveManagedGcScopeForRead,
|
|
51
|
+
taskArtifactOwnerStorageContextForScope,
|
|
52
|
+
} from "../../session/internal/managed-session-scope";
|
|
46
53
|
import { validateManagedArtifactTree } from "../../session/internal/managed-session-storage";
|
|
54
|
+
import { captureTaskArtifactOwnerDeletionEvidence } from "../../session/internal/task-artifact-owner-access";
|
|
55
|
+
import {
|
|
56
|
+
hasSiblingTaskArtifactOwnerTranscript,
|
|
57
|
+
taskArtifactOwnerLocatorFromTranscriptBytes,
|
|
58
|
+
} from "../../session/internal/task-artifact-owner-transcript";
|
|
47
59
|
import {
|
|
48
60
|
FileSessionStorage,
|
|
49
61
|
SessionDeleteVerificationError,
|
|
@@ -52,7 +64,18 @@ import {
|
|
|
52
64
|
type VerifiedSessionDeleteResult,
|
|
53
65
|
type VerifiedSessionDeleteTarget,
|
|
54
66
|
} from "../../session/session-storage";
|
|
67
|
+
import { parseFirstJsonlLine } from "../../session/session-transcript-header";
|
|
55
68
|
import type { SessionWorkLease } from "../../session/session-work-lease";
|
|
69
|
+
import type {
|
|
70
|
+
TaskArtifactOwnerDeletionEvidence,
|
|
71
|
+
TaskArtifactOwnerLocator,
|
|
72
|
+
TaskArtifactOwnerRetirementOutcome,
|
|
73
|
+
TaskArtifactOwnerStorageContext,
|
|
74
|
+
} from "../../session/task-artifact-owner-codec";
|
|
75
|
+
import {
|
|
76
|
+
retireTaskArtifactOwner,
|
|
77
|
+
verifyTaskArtifactOwnerPhysicalRetirement,
|
|
78
|
+
} from "../../session/task-artifact-owner-retirement";
|
|
56
79
|
import type { SessionLifecycleMcpServer } from "../acp/mcp";
|
|
57
80
|
import { SdkClient, SdkClientError } from "../client/client";
|
|
58
81
|
import { BROKER_RUNTIME_CLOSE_CAPABILITY_FIELD } from "../host/control/runtime-gate";
|
|
@@ -106,6 +129,12 @@ import {
|
|
|
106
129
|
readPreparationTimeouts,
|
|
107
130
|
startupQueueWaitMs,
|
|
108
131
|
} from "./startup-budget";
|
|
132
|
+
import {
|
|
133
|
+
type BrokerTaskArtifactOwnerCleanupValidation,
|
|
134
|
+
type BrokerTaskArtifactOwnerRetirementDisposition,
|
|
135
|
+
decodeBrokerTaskArtifactOwnerCleanupFields,
|
|
136
|
+
serializeBrokerTaskArtifactOwnerRetirementDisposition,
|
|
137
|
+
} from "./task-artifact-owner-validation";
|
|
109
138
|
import { worktreeOccupant } from "./worktree-occupancy";
|
|
110
139
|
|
|
111
140
|
export {
|
|
@@ -961,6 +990,130 @@ function lifecycleLaunchKnownSecrets(launch: SessionLaunch): string[] {
|
|
|
961
990
|
}
|
|
962
991
|
|
|
963
992
|
type CleanupEvidence = BrokerCleanupEvidence;
|
|
993
|
+
|
|
994
|
+
function brokerTaskArtifactOwnerCleanupFields(cleanup: CleanupEvidence): Record<string, unknown> {
|
|
995
|
+
const fields: Record<string, unknown> = {};
|
|
996
|
+
for (const [key, value] of Object.entries(cleanup)) {
|
|
997
|
+
if (key.startsWith("taskArtifactOwner")) fields[key] = value;
|
|
998
|
+
}
|
|
999
|
+
return fields;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function managedOwnerScopeFromInventory(scope: ManagedSessionScope): ManagedScope {
|
|
1003
|
+
const resolved = resolveManagedGcScopeForRead({
|
|
1004
|
+
cwd: scope.legacyLexicalCwd,
|
|
1005
|
+
agentDir: scope.agentDir,
|
|
1006
|
+
sessionsRoot: scope.sessionsRoot,
|
|
1007
|
+
});
|
|
1008
|
+
if (
|
|
1009
|
+
resolved.kind !== "resolved" ||
|
|
1010
|
+
resolved.scope.apiVersion !== scope.apiVersion ||
|
|
1011
|
+
resolved.scope.layoutVersion !== scope.layoutVersion ||
|
|
1012
|
+
resolved.scope.identityVersion !== scope.identityVersion ||
|
|
1013
|
+
resolved.scope.agentDir !== scope.agentDir ||
|
|
1014
|
+
resolved.scope.sessionsRoot !== scope.sessionsRoot ||
|
|
1015
|
+
resolved.scope.canonicalCwd !== scope.canonicalCwd ||
|
|
1016
|
+
resolved.scope.legacyLexicalCwd !== scope.legacyLexicalCwd ||
|
|
1017
|
+
resolved.scope.directoryName !== scope.directoryName ||
|
|
1018
|
+
resolved.scope.directoryPath !== scope.directoryPath
|
|
1019
|
+
)
|
|
1020
|
+
throw new Error("managed_task_artifact_owner_scope_changed");
|
|
1021
|
+
return resolved.scope;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
function taskArtifactOwnerTranscriptMatches(
|
|
1025
|
+
storage: FileSessionStorage,
|
|
1026
|
+
target: VerifiedSessionDeleteTarget,
|
|
1027
|
+
evidence: TaskArtifactOwnerDeletionEvidence,
|
|
1028
|
+
): boolean {
|
|
1029
|
+
const transcriptPath = target.detachedTranscriptPath ?? target.transcriptPath;
|
|
1030
|
+
try {
|
|
1031
|
+
const parent = fsSync.lstatSync(path.dirname(transcriptPath), { bigint: true });
|
|
1032
|
+
if (
|
|
1033
|
+
!parent.isDirectory() ||
|
|
1034
|
+
parent.isSymbolicLink() ||
|
|
1035
|
+
parent.dev !== target.transcriptParentIdentity?.dev ||
|
|
1036
|
+
parent.ino !== target.transcriptParentIdentity?.ino
|
|
1037
|
+
)
|
|
1038
|
+
return false;
|
|
1039
|
+
const snapshot = storage.readSnapshotSync(transcriptPath);
|
|
1040
|
+
const digest = createHash("sha256").update(snapshot.bytes).digest("hex");
|
|
1041
|
+
if (
|
|
1042
|
+
!snapshot.stat.isFile ||
|
|
1043
|
+
snapshot.stat.dev !== target.transcriptIdentity.dev ||
|
|
1044
|
+
snapshot.stat.ino !== target.transcriptIdentity.ino ||
|
|
1045
|
+
snapshot.stat.nlink !== target.transcriptIdentity.nlink ||
|
|
1046
|
+
snapshot.stat.size !== target.transcriptIdentity.size ||
|
|
1047
|
+
snapshot.stat.mtimeNs !== target.transcriptIdentity.mtimeNs ||
|
|
1048
|
+
digest !== target.transcriptIdentity.sha256
|
|
1049
|
+
)
|
|
1050
|
+
return false;
|
|
1051
|
+
const header = parseFirstJsonlLine(snapshot.bytes);
|
|
1052
|
+
if (
|
|
1053
|
+
!header ||
|
|
1054
|
+
typeof header.cwd !== "string" ||
|
|
1055
|
+
canonicalExistingPath(header.cwd) !== canonicalExistingPath(target.cwd)
|
|
1056
|
+
)
|
|
1057
|
+
return false;
|
|
1058
|
+
const locator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, target.sessionId);
|
|
1059
|
+
return (
|
|
1060
|
+
locator?.schemaVersion === evidence.locator.schemaVersion &&
|
|
1061
|
+
locator.ownerId === evidence.locator.ownerId &&
|
|
1062
|
+
locator.directoryDev === evidence.locator.directoryDev &&
|
|
1063
|
+
locator.directoryIno === evidence.locator.directoryIno
|
|
1064
|
+
);
|
|
1065
|
+
} catch {
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
function taskArtifactOwnerFailureDiagnostic(error: unknown): string {
|
|
1071
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1072
|
+
return (
|
|
1073
|
+
message.startsWith("task_artifact_owner_") ? message : `task_artifact_owner_cleanup_failed:${message}`
|
|
1074
|
+
).slice(0, 4096);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
function taskArtifactOwnerRetirementDisposition(
|
|
1078
|
+
outcome: TaskArtifactOwnerRetirementOutcome,
|
|
1079
|
+
): BrokerTaskArtifactOwnerRetirementDisposition {
|
|
1080
|
+
return serializeBrokerTaskArtifactOwnerRetirementDisposition(outcome);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
function captureTaskArtifactOwnerEvidence(
|
|
1084
|
+
storage: FileSessionStorage,
|
|
1085
|
+
target: VerifiedSessionDeleteTarget,
|
|
1086
|
+
): { evidence?: TaskArtifactOwnerDeletionEvidence; error?: string } {
|
|
1087
|
+
const transcriptPath = target.detachedTranscriptPath ?? target.transcriptPath;
|
|
1088
|
+
let snapshot: SessionStorageSnapshot;
|
|
1089
|
+
try {
|
|
1090
|
+
snapshot = storage.readSnapshotSync(transcriptPath);
|
|
1091
|
+
} catch {
|
|
1092
|
+
return { error: "task_artifact_owner_transcript_unavailable" };
|
|
1093
|
+
}
|
|
1094
|
+
const digest = createHash("sha256").update(snapshot.bytes).digest("hex");
|
|
1095
|
+
if (
|
|
1096
|
+
!snapshot.stat.isFile ||
|
|
1097
|
+
snapshot.stat.dev !== target.transcriptIdentity.dev ||
|
|
1098
|
+
snapshot.stat.ino !== target.transcriptIdentity.ino ||
|
|
1099
|
+
snapshot.stat.nlink !== target.transcriptIdentity.nlink ||
|
|
1100
|
+
snapshot.stat.size !== target.transcriptIdentity.size ||
|
|
1101
|
+
snapshot.stat.mtimeNs !== target.transcriptIdentity.mtimeNs ||
|
|
1102
|
+
digest !== target.transcriptIdentity.sha256
|
|
1103
|
+
)
|
|
1104
|
+
return { error: "task_artifact_owner_transcript_identity_mismatch" };
|
|
1105
|
+
try {
|
|
1106
|
+
const locator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, target.sessionId);
|
|
1107
|
+
if (!locator) return {};
|
|
1108
|
+
const context = target.taskArtifactOwnerStorageContext;
|
|
1109
|
+
if (!context) return { error: "task_artifact_owner_context_missing" };
|
|
1110
|
+
const evidence = captureTaskArtifactOwnerDeletionEvidence(context, target.sessionId, locator);
|
|
1111
|
+
return evidence ? { evidence } : { error: "task_artifact_owner_evidence_missing" };
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
return { error: taskArtifactOwnerFailureDiagnostic(error) };
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
964
1117
|
type CleanupIdentity = {
|
|
965
1118
|
dev: bigint;
|
|
966
1119
|
ino: bigint;
|
|
@@ -5965,6 +6118,8 @@ type ValidatedDelete = {
|
|
|
5965
6118
|
target: VerifiedSessionDeleteTarget;
|
|
5966
6119
|
metadataRoot: string;
|
|
5967
6120
|
transcriptParentIdentity: { dev: string; ino: string };
|
|
6121
|
+
taskArtifactOwnerCaptureError?: string;
|
|
6122
|
+
inspectProtocol?: ManagedGcProtocolScopeInspector;
|
|
5968
6123
|
};
|
|
5969
6124
|
function cleanupIdentity(
|
|
5970
6125
|
identity: BrokerCleanupEvidence["transcriptIdentity"],
|
|
@@ -6038,8 +6193,12 @@ function replayDeleteTarget(cleanup: CleanupEvidence): ValidatedDelete | BrokerR
|
|
|
6038
6193
|
(artifactsIdentity.dev !== artifactTreeIdentity.dev || artifactsIdentity.ino !== artifactTreeIdentity.ino)
|
|
6039
6194
|
)
|
|
6040
6195
|
return fail("terminal_uncertain", "Artifact cleanup tree does not match its ledger-bound root identity.");
|
|
6041
|
-
if (
|
|
6042
|
-
|
|
6196
|
+
if (
|
|
6197
|
+
cleanup.phase === "artifacts" &&
|
|
6198
|
+
cleanup.artifactsRemoved === true &&
|
|
6199
|
+
cleanup.taskArtifactOwnerDeletionEvidence === undefined
|
|
6200
|
+
)
|
|
6201
|
+
return fail("terminal_uncertain", "Artifacts-phase cleanup receipt lacks immutable owner evidence.");
|
|
6043
6202
|
if (
|
|
6044
6203
|
cleanup.artifactsRemoved === true &&
|
|
6045
6204
|
cleanup.artifactTree &&
|
|
@@ -6243,7 +6402,126 @@ async function validateDeletePath(
|
|
|
6243
6402
|
canonicalExistingPath(replay.metadataRoot) !== canonicalRequestedRoot
|
|
6244
6403
|
)
|
|
6245
6404
|
return fail("invalid_input", "Cleanup receipt does not match the requested saved-session locator.");
|
|
6246
|
-
|
|
6405
|
+
const ownerFields = brokerTaskArtifactOwnerCleanupFields(cleanup);
|
|
6406
|
+
if (Object.keys(ownerFields).length === 0) {
|
|
6407
|
+
const sessionsRoot = canonicalExistingPath(getSessionsDir(broker.settings.agentDir));
|
|
6408
|
+
const relative = path.relative(sessionsRoot, replay.target.transcriptPath);
|
|
6409
|
+
if (
|
|
6410
|
+
path.resolve(replay.target.sessionsRoot) !== sessionsRoot ||
|
|
6411
|
+
relative === "" ||
|
|
6412
|
+
relative === ".." ||
|
|
6413
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
6414
|
+
path.isAbsolute(relative)
|
|
6415
|
+
)
|
|
6416
|
+
return fail("terminal_uncertain", "Cleanup receipt does not match the configured session root.");
|
|
6417
|
+
return replay;
|
|
6418
|
+
}
|
|
6419
|
+
const inventory = await managedCandidates(broker, cwd, "Saved");
|
|
6420
|
+
if ("ok" in inventory) return inventory;
|
|
6421
|
+
const transcriptRelative = path.relative(inventory.scope.sessionsRoot, replay.target.transcriptPath);
|
|
6422
|
+
if (
|
|
6423
|
+
path.resolve(replay.target.sessionsRoot) !== inventory.scope.sessionsRoot ||
|
|
6424
|
+
transcriptRelative === "" ||
|
|
6425
|
+
transcriptRelative === ".." ||
|
|
6426
|
+
transcriptRelative.startsWith(`..${path.sep}`) ||
|
|
6427
|
+
path.isAbsolute(transcriptRelative)
|
|
6428
|
+
)
|
|
6429
|
+
return fail("terminal_uncertain", "Cleanup receipt does not match the current managed session authority.");
|
|
6430
|
+
replay.target.sessionsRoot = inventory.scope.sessionsRoot;
|
|
6431
|
+
let ownerContext: TaskArtifactOwnerStorageContext;
|
|
6432
|
+
let ownerScope: ManagedScope;
|
|
6433
|
+
try {
|
|
6434
|
+
ownerScope = managedOwnerScopeFromInventory(inventory.scope);
|
|
6435
|
+
ownerContext = taskArtifactOwnerStorageContextForScope(ownerScope);
|
|
6436
|
+
} catch {
|
|
6437
|
+
return fail(
|
|
6438
|
+
"terminal_uncertain",
|
|
6439
|
+
"Managed task-artifact-owner authority could not be restored for cleanup replay.",
|
|
6440
|
+
);
|
|
6441
|
+
}
|
|
6442
|
+
let owner: BrokerTaskArtifactOwnerCleanupValidation;
|
|
6443
|
+
try {
|
|
6444
|
+
owner = decodeBrokerTaskArtifactOwnerCleanupFields(
|
|
6445
|
+
ownerFields,
|
|
6446
|
+
ownerContext,
|
|
6447
|
+
id,
|
|
6448
|
+
cleanup.phase,
|
|
6449
|
+
cleanup.artifactsRemoved,
|
|
6450
|
+
);
|
|
6451
|
+
} catch {
|
|
6452
|
+
return fail("terminal_uncertain", "Cleanup receipt contains invalid task-artifact owner state.");
|
|
6453
|
+
}
|
|
6454
|
+
let captureError = owner.cleanupDiagnostic;
|
|
6455
|
+
if (!owner.deletionEvidence && captureError === "task_artifact_owner_writer_not_quiescent") {
|
|
6456
|
+
const retainedPaths = [
|
|
6457
|
+
replay.target.detachedArtifactsPath,
|
|
6458
|
+
replay.target.detachedTranscriptPath,
|
|
6459
|
+
replay.target.retainedArtifactsSuccessorPath,
|
|
6460
|
+
replay.target.retainedArtifactsPlaceholderPath,
|
|
6461
|
+
replay.target.retainedArtifactsUnknownPath,
|
|
6462
|
+
replay.target.retainedTranscriptSuccessorPath,
|
|
6463
|
+
replay.target.retainedTranscriptPlaceholderPath,
|
|
6464
|
+
replay.target.retainedTranscriptUnknownPath,
|
|
6465
|
+
];
|
|
6466
|
+
const plannedPaths = [
|
|
6467
|
+
cleanup.plannedArtifactsPath,
|
|
6468
|
+
cleanup.artifactTree?.plannedPath,
|
|
6469
|
+
cleanup.plannedTranscriptPath,
|
|
6470
|
+
];
|
|
6471
|
+
const occupiedPlan = plannedPaths.some(planned => {
|
|
6472
|
+
if (!planned) return false;
|
|
6473
|
+
return [planned, `${planned}.removing`].some(candidate => {
|
|
6474
|
+
try {
|
|
6475
|
+
fsSync.lstatSync(candidate);
|
|
6476
|
+
return true;
|
|
6477
|
+
} catch (error) {
|
|
6478
|
+
return (error as NodeJS.ErrnoException).code !== "ENOENT";
|
|
6479
|
+
}
|
|
6480
|
+
});
|
|
6481
|
+
});
|
|
6482
|
+
if (
|
|
6483
|
+
cleanup.phase !== "artifacts" ||
|
|
6484
|
+
cleanup.artifactsRemoved === true ||
|
|
6485
|
+
Object.keys(ownerFields).some(key => key !== "taskArtifactOwnerCleanupError") ||
|
|
6486
|
+
retainedPaths.some(candidate => candidate !== undefined) ||
|
|
6487
|
+
occupiedPlan ||
|
|
6488
|
+
transcriptParentStat.dev.toString() !== replay.transcriptParentIdentity.dev ||
|
|
6489
|
+
transcriptParentStat.ino.toString() !== replay.transcriptParentIdentity.ino
|
|
6490
|
+
)
|
|
6491
|
+
return fail(
|
|
6492
|
+
"terminal_uncertain",
|
|
6493
|
+
"Initial task-artifact owner capture cannot be retried after unverified cleanup effects.",
|
|
6494
|
+
);
|
|
6495
|
+
replay.target.taskArtifactOwnerStorageContext = ownerContext;
|
|
6496
|
+
const captured = captureTaskArtifactOwnerEvidence(replay.storage, replay.target);
|
|
6497
|
+
if (!captured.evidence && !captured.error)
|
|
6498
|
+
return fail(
|
|
6499
|
+
"terminal_uncertain",
|
|
6500
|
+
"The original task-artifact owner claim could not be restored for capture retry.",
|
|
6501
|
+
);
|
|
6502
|
+
if (captured.evidence) {
|
|
6503
|
+
replay.target.taskArtifactOwnerDeletionEvidence = captured.evidence;
|
|
6504
|
+
replay.target.deferTaskArtifactOwnerRetirement = true;
|
|
6505
|
+
}
|
|
6506
|
+
captureError = captured.error;
|
|
6507
|
+
}
|
|
6508
|
+
if (owner.deletionEvidence) {
|
|
6509
|
+
replay.target.taskArtifactOwnerStorageContext = ownerContext;
|
|
6510
|
+
replay.target.taskArtifactOwnerDeletionEvidence = owner.deletionEvidence;
|
|
6511
|
+
replay.target.deferTaskArtifactOwnerRetirement = true;
|
|
6512
|
+
if (owner.retirementContinuation)
|
|
6513
|
+
replay.target.taskArtifactOwnerRetirementContinuation = owner.retirementContinuation;
|
|
6514
|
+
if (owner.retirementOutcome) replay.target.taskArtifactOwnerRetirementOutcome = owner.retirementOutcome;
|
|
6515
|
+
if (owner.retired) replay.target.taskArtifactOwnerRetired = true;
|
|
6516
|
+
if (owner.payloadRetired) replay.target.taskArtifactOwnerPayloadRetired = true;
|
|
6517
|
+
if (owner.namespaceRetained) replay.target.taskArtifactOwnerNamespaceRetained = true;
|
|
6518
|
+
if (owner.transcriptDeleted) replay.target.taskArtifactOwnerTranscriptDeleted = true;
|
|
6519
|
+
}
|
|
6520
|
+
return {
|
|
6521
|
+
...replay,
|
|
6522
|
+
inspectProtocol: managedGcProtocolScopeInspectorForScope(ownerScope),
|
|
6523
|
+
...(captureError && !owner.deletionEvidence ? { taskArtifactOwnerCaptureError: captureError } : {}),
|
|
6524
|
+
};
|
|
6247
6525
|
}
|
|
6248
6526
|
const inventory = await managedCandidates(broker, cwd, "Saved");
|
|
6249
6527
|
if ("ok" in inventory) return inventory;
|
|
@@ -6255,7 +6533,6 @@ async function validateDeletePath(
|
|
|
6255
6533
|
const match = matches[0]!;
|
|
6256
6534
|
if (inventory.migrationPolicy === "disabled" && match.provenance === "legacy")
|
|
6257
6535
|
return fail("legacy_migration_disabled", "Saved legacy session migration is disabled for this workspace.");
|
|
6258
|
-
|
|
6259
6536
|
const storage = new FileSessionStorage();
|
|
6260
6537
|
let snapshot: SessionStorageSnapshot;
|
|
6261
6538
|
try {
|
|
@@ -6284,28 +6561,56 @@ async function validateDeletePath(
|
|
|
6284
6561
|
} catch {
|
|
6285
6562
|
return fail("invalid_input", "session.delete transcript parent changed during authorization.");
|
|
6286
6563
|
}
|
|
6564
|
+
let ownerContext: TaskArtifactOwnerStorageContext | undefined;
|
|
6565
|
+
let ownerScope: ManagedScope | undefined;
|
|
6566
|
+
let ownerCaptureError: string | undefined;
|
|
6567
|
+
let ownerLocator: TaskArtifactOwnerLocator | undefined;
|
|
6568
|
+
try {
|
|
6569
|
+
ownerLocator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, id);
|
|
6570
|
+
} catch (error) {
|
|
6571
|
+
ownerCaptureError = taskArtifactOwnerFailureDiagnostic(error);
|
|
6572
|
+
}
|
|
6573
|
+
if (ownerLocator) {
|
|
6574
|
+
try {
|
|
6575
|
+
ownerScope = managedOwnerScopeFromInventory(inventory.scope);
|
|
6576
|
+
ownerContext = taskArtifactOwnerStorageContextForScope(ownerScope);
|
|
6577
|
+
} catch {
|
|
6578
|
+
return fail("invalid_input", "Managed task-artifact-owner authority could not be established for deletion.");
|
|
6579
|
+
}
|
|
6580
|
+
}
|
|
6581
|
+
const target: VerifiedSessionDeleteTarget = {
|
|
6582
|
+
sessionsRoot: canonicalExistingPath(inventory.scope.sessionsRoot),
|
|
6583
|
+
transcriptPath: candidatePath,
|
|
6584
|
+
sessionId: id,
|
|
6585
|
+
cwd,
|
|
6586
|
+
transcriptIdentity: {
|
|
6587
|
+
dev: snapshot.stat.dev,
|
|
6588
|
+
ino: snapshot.stat.ino,
|
|
6589
|
+
nlink: snapshot.stat.nlink,
|
|
6590
|
+
size: snapshot.stat.size,
|
|
6591
|
+
mtimeNs: snapshot.stat.mtimeNs,
|
|
6592
|
+
sha256: digest,
|
|
6593
|
+
},
|
|
6594
|
+
transcriptParentIdentity: { dev: transcriptParentStat.dev, ino: transcriptParentStat.ino },
|
|
6595
|
+
...(ownerContext ? { taskArtifactOwnerStorageContext: ownerContext } : {}),
|
|
6596
|
+
};
|
|
6597
|
+
const capturedOwner: { evidence?: TaskArtifactOwnerDeletionEvidence; error?: string } = ownerCaptureError
|
|
6598
|
+
? { error: ownerCaptureError }
|
|
6599
|
+
: captureTaskArtifactOwnerEvidence(storage, target);
|
|
6600
|
+
if (capturedOwner.evidence) {
|
|
6601
|
+
target.taskArtifactOwnerDeletionEvidence = capturedOwner.evidence;
|
|
6602
|
+
target.deferTaskArtifactOwnerRetirement = true;
|
|
6603
|
+
}
|
|
6287
6604
|
return {
|
|
6288
6605
|
storage,
|
|
6289
|
-
target
|
|
6290
|
-
sessionsRoot: canonicalExistingPath(inventory.scope.sessionsRoot),
|
|
6291
|
-
transcriptPath: candidatePath,
|
|
6292
|
-
sessionId: id,
|
|
6293
|
-
cwd,
|
|
6294
|
-
transcriptIdentity: {
|
|
6295
|
-
dev: snapshot.stat.dev,
|
|
6296
|
-
ino: snapshot.stat.ino,
|
|
6297
|
-
nlink: snapshot.stat.nlink,
|
|
6298
|
-
size: snapshot.stat.size,
|
|
6299
|
-
mtimeNs: snapshot.stat.mtimeNs,
|
|
6300
|
-
sha256: digest,
|
|
6301
|
-
},
|
|
6302
|
-
transcriptParentIdentity: { dev: transcriptParentStat.dev, ino: transcriptParentStat.ino },
|
|
6303
|
-
},
|
|
6606
|
+
target,
|
|
6304
6607
|
metadataRoot: canonicalRequestedRoot,
|
|
6608
|
+
...(ownerScope ? { inspectProtocol: managedGcProtocolScopeInspectorForScope(ownerScope) } : {}),
|
|
6305
6609
|
transcriptParentIdentity: {
|
|
6306
6610
|
dev: transcriptParentStat.dev.toString(),
|
|
6307
6611
|
ino: transcriptParentStat.ino.toString(),
|
|
6308
6612
|
},
|
|
6613
|
+
...(capturedOwner.error ? { taskArtifactOwnerCaptureError: capturedOwner.error } : {}),
|
|
6309
6614
|
};
|
|
6310
6615
|
}
|
|
6311
6616
|
type CloseAuthority = { endpointGeneration: number; endpointIncarnation: string };
|
|
@@ -6448,7 +6753,13 @@ async function executeLifecycleResponse(
|
|
|
6448
6753
|
if (requestedSourceSessionId !== undefined && !isCanonicalSessionId(requestedSourceSessionId))
|
|
6449
6754
|
return fail("invalid_input", "sourceSessionId must be a canonical safe identifier.");
|
|
6450
6755
|
if (operation === "session.create" || operation === "session.fork" || operation === "session.resume") {
|
|
6451
|
-
|
|
6756
|
+
// Creates without managed-worktree admission have no existing session
|
|
6757
|
+
// authority to reconcile, so the change-stamp fast path avoids replaying
|
|
6758
|
+
// the full index under its lock on every ACP spawn. Managed-worktree creates
|
|
6759
|
+
// must serialize with registrations before checking checkout occupancy.
|
|
6760
|
+
if (operation === "session.create" && lifecycleWorktreeTarget(input) === undefined)
|
|
6761
|
+
await broker.index.refreshIfChanged();
|
|
6762
|
+
else await broker.index.refresh();
|
|
6452
6763
|
if (operation === "session.create") {
|
|
6453
6764
|
// Creation has no existing session authority to reconcile. Keep the
|
|
6454
6765
|
// broker's periodic/startup heartbeat checkpoint as the reaping owner,
|
|
@@ -7483,15 +7794,45 @@ async function executeLifecycleResponse(
|
|
|
7483
7794
|
const transcriptParentIdentity = cleanup?.transcriptParentIdentity ?? validated.transcriptParentIdentity;
|
|
7484
7795
|
const durableArtifactsPlan =
|
|
7485
7796
|
cleanup?.artifactTree?.plannedPath ?? cleanup?.plannedArtifactsPath ?? cleanupTarget.plannedArtifactsPath;
|
|
7797
|
+
let ownerRetirementOutcome = cleanupTarget.taskArtifactOwnerRetirementOutcome
|
|
7798
|
+
? taskArtifactOwnerRetirementDisposition(cleanupTarget.taskArtifactOwnerRetirementOutcome)
|
|
7799
|
+
: undefined;
|
|
7800
|
+
let ownerTranscriptDeleted = cleanupTarget.taskArtifactOwnerTranscriptDeleted;
|
|
7801
|
+
let freshPayloadRetirementForThisAttempt = false;
|
|
7802
|
+
const ownerNamespacePending =
|
|
7803
|
+
cleanupTarget.taskArtifactOwnerPayloadRetired === true &&
|
|
7804
|
+
cleanupTarget.taskArtifactOwnerNamespaceRetained === true &&
|
|
7805
|
+
cleanupTarget.taskArtifactOwnerRetired !== true;
|
|
7486
7806
|
const preauthorizedCleanup: CleanupEvidence = {
|
|
7487
7807
|
cleanupReceiptVersion: 1,
|
|
7488
|
-
phase:
|
|
7808
|
+
phase:
|
|
7809
|
+
cleanupTarget.artifactsRemoved &&
|
|
7810
|
+
!ownerNamespacePending &&
|
|
7811
|
+
(cleanupTarget.taskArtifactOwnerRetired === true || !cleanupTarget.taskArtifactOwnerDeletionEvidence)
|
|
7812
|
+
? "transcript"
|
|
7813
|
+
: "artifacts",
|
|
7489
7814
|
sessionId: cleanupTarget.sessionId,
|
|
7490
7815
|
sessionsRoot: cleanupTarget.sessionsRoot,
|
|
7491
7816
|
transcriptPath: cleanupTarget.transcriptPath,
|
|
7492
7817
|
cwd: cleanupTarget.cwd,
|
|
7493
7818
|
...(cleanupTarget.artifactsRemoved ? { artifactsRemoved: true } : {}),
|
|
7494
7819
|
...(cleanupTarget.artifactsAbsentAtAuthorization ? { artifactsAbsentAtAuthorization: true as const } : {}),
|
|
7820
|
+
...(cleanupTarget.taskArtifactOwnerDeletionEvidence
|
|
7821
|
+
? { taskArtifactOwnerDeletionEvidence: cleanupTarget.taskArtifactOwnerDeletionEvidence }
|
|
7822
|
+
: {}),
|
|
7823
|
+
...(cleanupTarget.taskArtifactOwnerRetirementContinuation
|
|
7824
|
+
? { taskArtifactOwnerRetirementContinuation: cleanupTarget.taskArtifactOwnerRetirementContinuation }
|
|
7825
|
+
: {}),
|
|
7826
|
+
...(ownerRetirementOutcome ? { taskArtifactOwnerRetirementOutcome: ownerRetirementOutcome } : {}),
|
|
7827
|
+
...(cleanupTarget.taskArtifactOwnerPayloadRetired ? { taskArtifactOwnerPayloadRetired: true as const } : {}),
|
|
7828
|
+
...(cleanupTarget.taskArtifactOwnerNamespaceRetained
|
|
7829
|
+
? { taskArtifactOwnerNamespaceRetained: true as const }
|
|
7830
|
+
: {}),
|
|
7831
|
+
...(cleanupTarget.taskArtifactOwnerRetired ? { taskArtifactOwnerRetired: true as const } : {}),
|
|
7832
|
+
...(ownerTranscriptDeleted ? { taskArtifactOwnerTranscriptDeleted: true as const } : {}),
|
|
7833
|
+
...(validated.taskArtifactOwnerCaptureError
|
|
7834
|
+
? { taskArtifactOwnerCleanupError: validated.taskArtifactOwnerCaptureError }
|
|
7835
|
+
: {}),
|
|
7495
7836
|
metadataRoot: validated.metadataRoot,
|
|
7496
7837
|
transcriptIdentity: serializeCleanupIdentity(cleanupTarget.transcriptIdentity),
|
|
7497
7838
|
transcriptParentIdentity,
|
|
@@ -7591,6 +7932,145 @@ async function executeLifecycleResponse(
|
|
|
7591
7932
|
});
|
|
7592
7933
|
return pending;
|
|
7593
7934
|
};
|
|
7935
|
+
const cleanupWithFreshOwnerPayload = (receipt: CleanupEvidence): CleanupEvidence => {
|
|
7936
|
+
const current = { ...receipt };
|
|
7937
|
+
delete current.taskArtifactOwnerDeletionEvidence;
|
|
7938
|
+
delete current.taskArtifactOwnerRetirementContinuation;
|
|
7939
|
+
delete current.taskArtifactOwnerRetirementOutcome;
|
|
7940
|
+
delete current.taskArtifactOwnerPayloadRetired;
|
|
7941
|
+
delete current.taskArtifactOwnerNamespaceRetained;
|
|
7942
|
+
delete current.taskArtifactOwnerRetired;
|
|
7943
|
+
delete current.taskArtifactOwnerTranscriptDeleted;
|
|
7944
|
+
delete current.taskArtifactOwnerCleanupError;
|
|
7945
|
+
return {
|
|
7946
|
+
...current,
|
|
7947
|
+
...(cleanupTarget.taskArtifactOwnerDeletionEvidence
|
|
7948
|
+
? { taskArtifactOwnerDeletionEvidence: cleanupTarget.taskArtifactOwnerDeletionEvidence }
|
|
7949
|
+
: {}),
|
|
7950
|
+
...(cleanupTarget.taskArtifactOwnerRetirementContinuation
|
|
7951
|
+
? { taskArtifactOwnerRetirementContinuation: cleanupTarget.taskArtifactOwnerRetirementContinuation }
|
|
7952
|
+
: {}),
|
|
7953
|
+
...(ownerRetirementOutcome ? { taskArtifactOwnerRetirementOutcome: ownerRetirementOutcome } : {}),
|
|
7954
|
+
...(cleanupTarget.taskArtifactOwnerPayloadRetired
|
|
7955
|
+
? { taskArtifactOwnerPayloadRetired: true as const }
|
|
7956
|
+
: {}),
|
|
7957
|
+
...(cleanupTarget.taskArtifactOwnerNamespaceRetained
|
|
7958
|
+
? { taskArtifactOwnerNamespaceRetained: true as const }
|
|
7959
|
+
: {}),
|
|
7960
|
+
...(cleanupTarget.taskArtifactOwnerRetired ? { taskArtifactOwnerRetired: true as const } : {}),
|
|
7961
|
+
...(ownerTranscriptDeleted ? { taskArtifactOwnerTranscriptDeleted: true as const } : {}),
|
|
7962
|
+
};
|
|
7963
|
+
};
|
|
7964
|
+
const publishTaskArtifactOwnerPending = async (
|
|
7965
|
+
error: unknown,
|
|
7966
|
+
receipt: CleanupEvidence = preauthorizedCleanup,
|
|
7967
|
+
): Promise<BrokerResponse> => {
|
|
7968
|
+
const diagnostic = taskArtifactOwnerFailureDiagnostic(error);
|
|
7969
|
+
const evidence = cleanupTarget.taskArtifactOwnerDeletionEvidence;
|
|
7970
|
+
const ownerReceipt: CleanupEvidence = {
|
|
7971
|
+
...cleanupWithFreshOwnerPayload(receipt),
|
|
7972
|
+
phase: "artifacts",
|
|
7973
|
+
artifactsRemoved: receipt.artifactsRemoved === true && evidence ? true : undefined,
|
|
7974
|
+
artifactsAbsentAtAuthorization:
|
|
7975
|
+
receipt.artifactsRemoved === true ? undefined : receipt.artifactsAbsentAtAuthorization,
|
|
7976
|
+
taskArtifactOwnerCleanupError: diagnostic,
|
|
7977
|
+
};
|
|
7978
|
+
const pending = fail(
|
|
7979
|
+
"cleanup_pending",
|
|
7980
|
+
`Saved session cleanup is pending in artifacts: ${diagnostic}`,
|
|
7981
|
+
ownerReceipt,
|
|
7982
|
+
);
|
|
7983
|
+
await broker.ledger.transition(identity, "effect_started", {
|
|
7984
|
+
intendedSessionId: id,
|
|
7985
|
+
response: pending,
|
|
7986
|
+
});
|
|
7987
|
+
return pending;
|
|
7988
|
+
};
|
|
7989
|
+
const persistFreshTaskArtifactOwnerRetirement = async (
|
|
7990
|
+
receipt: CleanupEvidence,
|
|
7991
|
+
): Promise<BrokerResponse | undefined> => {
|
|
7992
|
+
const evidence = cleanupTarget.taskArtifactOwnerDeletionEvidence;
|
|
7993
|
+
if (!evidence) return undefined;
|
|
7994
|
+
const ownerContext = cleanupTarget.taskArtifactOwnerStorageContext;
|
|
7995
|
+
if (!ownerContext)
|
|
7996
|
+
return await publishTaskArtifactOwnerPending("task_artifact_owner_context_missing", receipt);
|
|
7997
|
+
if (
|
|
7998
|
+
ownerTranscriptDeleted === true
|
|
7999
|
+
? !logicalTranscriptRetirementMatches()
|
|
8000
|
+
: !taskArtifactOwnerTranscriptMatches(validated.storage, cleanupTarget, evidence)
|
|
8001
|
+
)
|
|
8002
|
+
return await publishTaskArtifactOwnerPending("task_artifact_owner_transcript_identity_mismatch", receipt);
|
|
8003
|
+
let outcome: TaskArtifactOwnerRetirementOutcome;
|
|
8004
|
+
try {
|
|
8005
|
+
if (
|
|
8006
|
+
await hasSiblingTaskArtifactOwnerTranscript(
|
|
8007
|
+
validated.storage,
|
|
8008
|
+
cleanupTarget.transcriptPath,
|
|
8009
|
+
evidence.locator,
|
|
8010
|
+
ownerContext,
|
|
8011
|
+
validated.inspectProtocol,
|
|
8012
|
+
)
|
|
8013
|
+
)
|
|
8014
|
+
return await publishTaskArtifactOwnerPending(
|
|
8015
|
+
"task_artifact_owner_shared_with_sibling_transcript",
|
|
8016
|
+
receipt,
|
|
8017
|
+
);
|
|
8018
|
+
outcome = retireTaskArtifactOwner(
|
|
8019
|
+
ownerContext,
|
|
8020
|
+
evidence,
|
|
8021
|
+
cleanupTarget.taskArtifactOwnerRetirementContinuation,
|
|
8022
|
+
);
|
|
8023
|
+
} catch (error) {
|
|
8024
|
+
freshPayloadRetirementForThisAttempt = false;
|
|
8025
|
+
return await publishTaskArtifactOwnerPending(error, receipt);
|
|
8026
|
+
}
|
|
8027
|
+
freshPayloadRetirementForThisAttempt = outcome.kind === "payload_retired";
|
|
8028
|
+
cleanupTarget.taskArtifactOwnerRetirementOutcome = outcome;
|
|
8029
|
+
ownerRetirementOutcome = taskArtifactOwnerRetirementDisposition(outcome);
|
|
8030
|
+
cleanupTarget.taskArtifactOwnerRetired = outcome.kind === "completed" ? true : undefined;
|
|
8031
|
+
cleanupTarget.taskArtifactOwnerRetirementContinuation =
|
|
8032
|
+
outcome.kind === "completed" ? undefined : outcome.continuation;
|
|
8033
|
+
cleanupTarget.taskArtifactOwnerPayloadRetired = outcome.kind === "payload_retired" ? true : undefined;
|
|
8034
|
+
cleanupTarget.taskArtifactOwnerNamespaceRetained = outcome.kind === "payload_retired" ? true : undefined;
|
|
8035
|
+
if (outcome.kind === "completed") ownerTranscriptDeleted = undefined;
|
|
8036
|
+
const ownerError =
|
|
8037
|
+
outcome.kind === "uncertain"
|
|
8038
|
+
? taskArtifactOwnerFailureDiagnostic(outcome.reason)
|
|
8039
|
+
: outcome.kind === "cleanup_pending"
|
|
8040
|
+
? taskArtifactOwnerFailureDiagnostic(
|
|
8041
|
+
outcome.nativeOutcome?.code ?? "task_artifact_owner_cleanup_pending",
|
|
8042
|
+
)
|
|
8043
|
+
: undefined;
|
|
8044
|
+
const ownerReceipt: CleanupEvidence = {
|
|
8045
|
+
...cleanupWithFreshOwnerPayload(receipt),
|
|
8046
|
+
phase: "artifacts",
|
|
8047
|
+
artifactsRemoved: true,
|
|
8048
|
+
artifactsAbsentAtAuthorization: undefined,
|
|
8049
|
+
...(ownerError ? { taskArtifactOwnerCleanupError: ownerError } : {}),
|
|
8050
|
+
};
|
|
8051
|
+
const stateResponse = fail(
|
|
8052
|
+
"cleanup_pending",
|
|
8053
|
+
outcome.kind === "payload_retired"
|
|
8054
|
+
? "Task-artifact payload is retired; its native namespace remains pending in artifacts."
|
|
8055
|
+
: outcome.kind === "completed"
|
|
8056
|
+
? "Task-artifact owner retirement is durably complete; transcript cleanup is preauthorized."
|
|
8057
|
+
: `Task-artifact owner retirement remains ${outcome.kind} in artifacts.`,
|
|
8058
|
+
ownerReceipt,
|
|
8059
|
+
);
|
|
8060
|
+
await broker.ledger.transition(identity, "effect_started", { intendedSessionId: id, response: stateResponse });
|
|
8061
|
+
return outcome.kind === "completed" || outcome.kind === "payload_retired" ? undefined : stateResponse;
|
|
8062
|
+
};
|
|
8063
|
+
const storageOwnerFields = (): Partial<VerifiedSessionDeleteTarget> => {
|
|
8064
|
+
const evidence = cleanupTarget.taskArtifactOwnerDeletionEvidence;
|
|
8065
|
+
if (!evidence) return {};
|
|
8066
|
+
if (!cleanupTarget.taskArtifactOwnerStorageContext) throw new Error("task_artifact_owner_context_missing");
|
|
8067
|
+
return {
|
|
8068
|
+
deferTaskArtifactOwnerRetirement: true,
|
|
8069
|
+
...(cleanupTarget.taskArtifactOwnerRetirementOutcome
|
|
8070
|
+
? { taskArtifactOwnerRetirementOutcome: cleanupTarget.taskArtifactOwnerRetirementOutcome }
|
|
8071
|
+
: {}),
|
|
8072
|
+
};
|
|
8073
|
+
};
|
|
7594
8074
|
const canonicalArtifactsPath = cleanupTarget.transcriptPath.slice(0, -6);
|
|
7595
8075
|
const transcriptCleanupAuthorityIsAbsent = (): boolean =>
|
|
7596
8076
|
[
|
|
@@ -7646,6 +8126,11 @@ async function executeLifecycleResponse(
|
|
|
7646
8126
|
return false;
|
|
7647
8127
|
}
|
|
7648
8128
|
};
|
|
8129
|
+
const logicalTranscriptRetirementMatches = (): boolean =>
|
|
8130
|
+
transcriptParentMatchesPersistedIdentity() &&
|
|
8131
|
+
pathIsAbsent(cleanupTarget.transcriptPath) &&
|
|
8132
|
+
pathIsAbsent(cleanupTarget.retainedTranscriptSuccessorPath) &&
|
|
8133
|
+
pathIsAbsent(cleanupTarget.retainedTranscriptUnknownPath);
|
|
7649
8134
|
const retainedTranscriptIdentityIsAbsentFromParent = (): boolean => {
|
|
7650
8135
|
const transcriptParent = path.dirname(cleanupTarget.transcriptPath);
|
|
7651
8136
|
const expectedParent = preauthorizedCleanup.transcriptParentIdentity;
|
|
@@ -7741,31 +8226,81 @@ async function executeLifecycleResponse(
|
|
|
7741
8226
|
preauthorizedCleanup,
|
|
7742
8227
|
),
|
|
7743
8228
|
});
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
8229
|
+
if (validated.taskArtifactOwnerCaptureError)
|
|
8230
|
+
return await publishTaskArtifactOwnerPending(validated.taskArtifactOwnerCaptureError);
|
|
8231
|
+
if (
|
|
8232
|
+
cleanup?.phase === "artifacts" &&
|
|
8233
|
+
cleanupTarget.artifactsRemoved === true &&
|
|
8234
|
+
cleanupTarget.taskArtifactOwnerDeletionEvidence &&
|
|
8235
|
+
cleanupTarget.taskArtifactOwnerRetired !== true
|
|
8236
|
+
) {
|
|
8237
|
+
if (!pathIsAbsent(canonicalArtifactsPath))
|
|
7748
8238
|
return await publishCanonicalArtifactReappearance(preauthorizedCleanup);
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
"
|
|
8239
|
+
const ownerPending = await persistFreshTaskArtifactOwnerRetirement(preauthorizedCleanup);
|
|
8240
|
+
if (ownerPending) return ownerPending;
|
|
8241
|
+
}
|
|
8242
|
+
let deleted: VerifiedSessionDeleteResult | undefined;
|
|
8243
|
+
if (cleanup?.taskArtifactOwnerTranscriptDeleted === true) {
|
|
8244
|
+
if (!logicalTranscriptRetirementMatches())
|
|
8245
|
+
return await publishTaskArtifactOwnerPending(
|
|
8246
|
+
"task_artifact_owner_transcript_disposition_mismatch",
|
|
7757
8247
|
preauthorizedCleanup,
|
|
7758
8248
|
);
|
|
7759
|
-
|
|
8249
|
+
if (cleanupTarget.taskArtifactOwnerPayloadRetired === true) {
|
|
8250
|
+
const residual = fail(
|
|
8251
|
+
"cleanup_pending",
|
|
8252
|
+
"Transcript retirement is complete; the task-artifact owner namespace remains pending in artifacts.",
|
|
8253
|
+
cleanupWithFreshOwnerPayload({ ...preauthorizedCleanup, phase: "artifacts", artifactsRemoved: true }),
|
|
8254
|
+
);
|
|
8255
|
+
await broker.ledger.transition(identity, "effect_started", { intendedSessionId: id, response: residual });
|
|
8256
|
+
return residual;
|
|
8257
|
+
}
|
|
8258
|
+
if (cleanupTarget.taskArtifactOwnerRetired === true) {
|
|
8259
|
+
const ownerEvidence = cleanupTarget.taskArtifactOwnerDeletionEvidence;
|
|
8260
|
+
const ownerContext = cleanupTarget.taskArtifactOwnerStorageContext;
|
|
8261
|
+
const ownerOutcome = cleanupTarget.taskArtifactOwnerRetirementOutcome;
|
|
8262
|
+
if (!ownerEvidence || !ownerContext || !ownerOutcome)
|
|
8263
|
+
return await publishTaskArtifactOwnerPending(
|
|
8264
|
+
"task_artifact_owner_retired_evidence_missing",
|
|
8265
|
+
preauthorizedCleanup,
|
|
8266
|
+
);
|
|
8267
|
+
try {
|
|
8268
|
+
verifyTaskArtifactOwnerPhysicalRetirement(ownerContext, ownerEvidence, ownerOutcome);
|
|
8269
|
+
} catch (error) {
|
|
8270
|
+
return await publishTaskArtifactOwnerPending(error, preauthorizedCleanup);
|
|
8271
|
+
}
|
|
8272
|
+
deleted = { kind: "deleted" };
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8275
|
+
try {
|
|
8276
|
+
if (deleted === undefined) {
|
|
8277
|
+
const completedArtifactReplay = cleanup?.phase === "transcript" && cleanup.artifactsRemoved === true;
|
|
8278
|
+
if (completedArtifactReplay && !pathIsAbsent(canonicalArtifactsPath))
|
|
8279
|
+
return await publishCanonicalArtifactReappearance(preauthorizedCleanup);
|
|
8280
|
+
if (
|
|
8281
|
+
completedArtifactReplay &&
|
|
8282
|
+
transcriptCleanupAuthorityIsAbsent() &&
|
|
8283
|
+
retainedTranscriptIdentityIsAbsentFromParent()
|
|
8284
|
+
)
|
|
8285
|
+
return fail(
|
|
8286
|
+
"cleanup_pending",
|
|
8287
|
+
"Saved session cleanup remains pending because transcript authority disappeared without native deletion proof.",
|
|
8288
|
+
preauthorizedCleanup,
|
|
8289
|
+
);
|
|
7760
8290
|
if (!transcriptParentMatchesPersistedIdentity())
|
|
7761
8291
|
return fail(
|
|
7762
8292
|
"cleanup_pending",
|
|
7763
8293
|
"Saved session cleanup is pending because transcript parent identity changed before exact mutation.",
|
|
7764
8294
|
preauthorizedCleanup,
|
|
7765
8295
|
);
|
|
7766
|
-
deleted = await validated.storage.deleteSessionVerified(
|
|
8296
|
+
deleted = await validated.storage.deleteSessionVerified(
|
|
8297
|
+
{ ...cleanupTarget, ...storageOwnerFields() },
|
|
8298
|
+
validated.inspectProtocol,
|
|
8299
|
+
);
|
|
7767
8300
|
}
|
|
7768
8301
|
} catch (error) {
|
|
8302
|
+
if (error instanceof Error && error.message.startsWith("task_artifact_owner_"))
|
|
8303
|
+
return await publishTaskArtifactOwnerPending(error);
|
|
7769
8304
|
if (error instanceof SessionDeleteVerificationError) {
|
|
7770
8305
|
if (cleanup?.phase === "artifacts")
|
|
7771
8306
|
return await publishChangedArtifactRoot(
|
|
@@ -7784,6 +8319,7 @@ async function executeLifecycleResponse(
|
|
|
7784
8319
|
`Unable to delete saved session artifacts: ${error instanceof Error ? error.message : String(error)}`,
|
|
7785
8320
|
);
|
|
7786
8321
|
}
|
|
8322
|
+
if (!deleted) return fail("unavailable", "Saved session deletion did not produce an outcome.");
|
|
7787
8323
|
if (
|
|
7788
8324
|
deleted.kind === "deleted" &&
|
|
7789
8325
|
cleanup?.phase === "transcript" &&
|
|
@@ -7807,10 +8343,22 @@ async function executeLifecycleResponse(
|
|
|
7807
8343
|
cleanupTarget.detachedArtifactsPath,
|
|
7808
8344
|
"Saved session cleanup is pending in artifacts: an authorized quarantine alias remains after exact removal.",
|
|
7809
8345
|
);
|
|
7810
|
-
const
|
|
7811
|
-
|
|
7812
|
-
|
|
8346
|
+
const returnedOwnerEvidence = deleted.taskArtifactOwnerDeletionEvidence;
|
|
8347
|
+
if (
|
|
8348
|
+
cleanupTarget.taskArtifactOwnerDeletionEvidence &&
|
|
8349
|
+
returnedOwnerEvidence &&
|
|
8350
|
+
JSON.stringify(cleanupTarget.taskArtifactOwnerDeletionEvidence) !== JSON.stringify(returnedOwnerEvidence)
|
|
8351
|
+
)
|
|
8352
|
+
return await publishTaskArtifactOwnerPending("task_artifact_owner_evidence_changed_during_delete");
|
|
8353
|
+
if (returnedOwnerEvidence && !cleanupTarget.taskArtifactOwnerDeletionEvidence)
|
|
8354
|
+
return await publishTaskArtifactOwnerPending("task_artifact_owner_evidence_not_prepared_before_effect");
|
|
8355
|
+
const ownerEvidence = cleanupTarget.taskArtifactOwnerDeletionEvidence;
|
|
8356
|
+
const artifactCompletionCleanup: CleanupEvidence = {
|
|
8357
|
+
...cleanupWithFreshOwnerPayload(preauthorizedCleanup),
|
|
8358
|
+
phase: ownerEvidence ? "artifacts" : "transcript",
|
|
7813
8359
|
artifactsRemoved: true,
|
|
8360
|
+
artifactsAbsentAtAuthorization: undefined,
|
|
8361
|
+
...(ownerEvidence ? { taskArtifactOwnerDeletionEvidence: ownerEvidence } : {}),
|
|
7814
8362
|
detachedArtifactsPath: undefined,
|
|
7815
8363
|
retainedArtifactsSuccessorPath: undefined,
|
|
7816
8364
|
retainedArtifactsPlaceholderPath: undefined,
|
|
@@ -7826,12 +8374,41 @@ async function executeLifecycleResponse(
|
|
|
7826
8374
|
}
|
|
7827
8375
|
: {}),
|
|
7828
8376
|
};
|
|
7829
|
-
|
|
7830
8377
|
await broker.ledger.transition(identity, "effect_started", {
|
|
7831
8378
|
intendedSessionId: id,
|
|
7832
8379
|
response: fail(
|
|
7833
8380
|
"cleanup_pending",
|
|
7834
|
-
"Saved session artifacts
|
|
8381
|
+
"Saved session artifacts are removed; owner cleanup is durably preauthorized.",
|
|
8382
|
+
artifactCompletionCleanup,
|
|
8383
|
+
),
|
|
8384
|
+
});
|
|
8385
|
+
if (ownerEvidence && cleanupTarget.taskArtifactOwnerRetired !== true) {
|
|
8386
|
+
const ownerPending = await persistFreshTaskArtifactOwnerRetirement(artifactCompletionCleanup);
|
|
8387
|
+
if (ownerPending) return ownerPending;
|
|
8388
|
+
}
|
|
8389
|
+
const ownerNamespaceRetained =
|
|
8390
|
+
cleanupTarget.taskArtifactOwnerPayloadRetired === true &&
|
|
8391
|
+
cleanupTarget.taskArtifactOwnerNamespaceRetained === true;
|
|
8392
|
+
const transcriptPhaseCleanup: CleanupEvidence = {
|
|
8393
|
+
...cleanupWithFreshOwnerPayload(artifactCompletionCleanup),
|
|
8394
|
+
phase: ownerNamespaceRetained ? "artifacts" : "transcript",
|
|
8395
|
+
artifactsRemoved: true,
|
|
8396
|
+
artifactsAbsentAtAuthorization: undefined,
|
|
8397
|
+
...(artifactCompletionCleanup.artifactTree
|
|
8398
|
+
? {
|
|
8399
|
+
artifactTree: {
|
|
8400
|
+
...artifactCompletionCleanup.artifactTree,
|
|
8401
|
+
detachedPath: undefined,
|
|
8402
|
+
completed: true as const,
|
|
8403
|
+
},
|
|
8404
|
+
}
|
|
8405
|
+
: {}),
|
|
8406
|
+
};
|
|
8407
|
+
await broker.ledger.transition(identity, "effect_started", {
|
|
8408
|
+
intendedSessionId: id,
|
|
8409
|
+
response: fail(
|
|
8410
|
+
"cleanup_pending",
|
|
8411
|
+
"Saved session artifacts and owner cleanup are durably recorded; transcript cleanup is preauthorized.",
|
|
7835
8412
|
transcriptPhaseCleanup,
|
|
7836
8413
|
),
|
|
7837
8414
|
});
|
|
@@ -7843,33 +8420,39 @@ async function executeLifecycleResponse(
|
|
|
7843
8420
|
"Saved session cleanup remains pending because transcript authority disappeared without native deletion proof.",
|
|
7844
8421
|
transcriptPhaseCleanup,
|
|
7845
8422
|
);
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
8423
|
+
if (!transcriptParentMatchesPersistedIdentity())
|
|
8424
|
+
return fail(
|
|
8425
|
+
"cleanup_pending",
|
|
8426
|
+
"Saved session cleanup is pending because transcript parent identity changed before exact mutation.",
|
|
8427
|
+
transcriptPhaseCleanup,
|
|
8428
|
+
);
|
|
8429
|
+
try {
|
|
8430
|
+
deleted = await validated.storage.deleteSessionVerified(
|
|
8431
|
+
{
|
|
7855
8432
|
...cleanupTarget,
|
|
8433
|
+
...storageOwnerFields(),
|
|
7856
8434
|
expectedArtifactsIdentity: undefined,
|
|
7857
8435
|
detachedArtifactsPath: undefined,
|
|
7858
8436
|
artifactsRemoved: true,
|
|
7859
|
-
}
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
8437
|
+
},
|
|
8438
|
+
validated.inspectProtocol,
|
|
8439
|
+
);
|
|
8440
|
+
} catch (error) {
|
|
8441
|
+
if (error instanceof Error && error.message.startsWith("task_artifact_owner_"))
|
|
8442
|
+
return await publishTaskArtifactOwnerPending(error, transcriptPhaseCleanup);
|
|
8443
|
+
if (error instanceof SessionDeleteVerificationError && error.kind === "artifacts")
|
|
8444
|
+
return await publishCanonicalArtifactReappearance(transcriptPhaseCleanup);
|
|
8445
|
+
if (error instanceof SessionDeleteVerificationError)
|
|
7868
8446
|
return fail(
|
|
7869
|
-
"
|
|
7870
|
-
`
|
|
8447
|
+
"invalid_input",
|
|
8448
|
+
`Saved session deletion verification failed (${error.kind}): ${error.message}`,
|
|
7871
8449
|
);
|
|
7872
|
-
|
|
8450
|
+
if (freshPayloadRetirementForThisAttempt)
|
|
8451
|
+
return await publishTaskArtifactOwnerPending(error, transcriptPhaseCleanup);
|
|
8452
|
+
return fail(
|
|
8453
|
+
"unavailable",
|
|
8454
|
+
`Unable to delete saved session transcript: ${error instanceof Error ? error.message : String(error)}`,
|
|
8455
|
+
);
|
|
7873
8456
|
}
|
|
7874
8457
|
if (deleted.kind === "deleted" && !pathIsAbsent(canonicalArtifactsPath))
|
|
7875
8458
|
return await publishCanonicalArtifactReappearance(transcriptPhaseCleanup);
|
|
@@ -7885,13 +8468,47 @@ async function executeLifecycleResponse(
|
|
|
7885
8468
|
"Saved session cleanup is pending in artifacts: a planned quarantine alias remains before terminal completion.",
|
|
7886
8469
|
);
|
|
7887
8470
|
const retainedRootArtifactsPlan = durableArtifactsPlan;
|
|
7888
|
-
if (deleted.kind === "cleanup_pending")
|
|
7889
|
-
|
|
8471
|
+
if (deleted.kind === "cleanup_pending") {
|
|
8472
|
+
if (deleted.phase === "task_artifact_owner" && !deleted.artifactsRemoved)
|
|
8473
|
+
return await publishTaskArtifactOwnerPending(deleted.error.message);
|
|
8474
|
+
const ownerEvidence =
|
|
8475
|
+
deleted.taskArtifactOwnerDeletionEvidence ?? cleanupTarget.taskArtifactOwnerDeletionEvidence;
|
|
8476
|
+
const ownerRetired = deleted.taskArtifactOwnerRetired ?? cleanupTarget.taskArtifactOwnerRetired;
|
|
8477
|
+
const ownerPayloadRetired =
|
|
8478
|
+
deleted.taskArtifactOwnerPayloadRetired ?? cleanupTarget.taskArtifactOwnerPayloadRetired;
|
|
8479
|
+
const ownerNamespaceRetained =
|
|
8480
|
+
deleted.taskArtifactOwnerNamespaceRetained ?? cleanupTarget.taskArtifactOwnerNamespaceRetained;
|
|
8481
|
+
const retainedOwnerNamespace = ownerPayloadRetired === true && ownerNamespaceRetained === true;
|
|
8482
|
+
const ownerContinuation =
|
|
8483
|
+
deleted.taskArtifactOwnerRetirementContinuation ?? cleanupTarget.taskArtifactOwnerRetirementContinuation;
|
|
8484
|
+
const ownerOutcome = deleted.taskArtifactOwnerRetirementOutcome
|
|
8485
|
+
? taskArtifactOwnerRetirementDisposition(deleted.taskArtifactOwnerRetirementOutcome)
|
|
8486
|
+
: ownerRetirementOutcome;
|
|
8487
|
+
const transcriptDeleted =
|
|
8488
|
+
deleted.taskArtifactOwnerTranscriptDeleted === true || ownerTranscriptDeleted === true;
|
|
8489
|
+
const ownerError =
|
|
8490
|
+
deleted.phase === "task_artifact_owner" || deleted.error.message.startsWith("task_artifact_owner_")
|
|
8491
|
+
? taskArtifactOwnerFailureDiagnostic(deleted.error)
|
|
8492
|
+
: undefined;
|
|
8493
|
+
const publicPhase =
|
|
8494
|
+
retainedOwnerNamespace || deleted.phase === "task_artifact_owner" ? "artifacts" : deleted.phase;
|
|
8495
|
+
const pending = fail(
|
|
7890
8496
|
"cleanup_pending",
|
|
7891
|
-
`Saved session cleanup is pending in ${
|
|
8497
|
+
`Saved session cleanup is pending in ${publicPhase}: ${deleted.error.message}`,
|
|
7892
8498
|
{
|
|
7893
8499
|
cleanupReceiptVersion: 1,
|
|
7894
|
-
phase:
|
|
8500
|
+
phase: publicPhase,
|
|
8501
|
+
...(ownerEvidence && (deleted.phase === "task_artifact_owner" || retainedOwnerNamespace)
|
|
8502
|
+
? { artifactsRemoved: true }
|
|
8503
|
+
: {}),
|
|
8504
|
+
...(ownerEvidence ? { taskArtifactOwnerDeletionEvidence: ownerEvidence } : {}),
|
|
8505
|
+
...(ownerContinuation ? { taskArtifactOwnerRetirementContinuation: ownerContinuation } : {}),
|
|
8506
|
+
...(ownerOutcome ? { taskArtifactOwnerRetirementOutcome: ownerOutcome } : {}),
|
|
8507
|
+
...(ownerPayloadRetired ? { taskArtifactOwnerPayloadRetired: true as const } : {}),
|
|
8508
|
+
...(ownerNamespaceRetained ? { taskArtifactOwnerNamespaceRetained: true as const } : {}),
|
|
8509
|
+
...(ownerRetired ? { taskArtifactOwnerRetired: true as const } : {}),
|
|
8510
|
+
...(transcriptDeleted ? { taskArtifactOwnerTranscriptDeleted: true as const } : {}),
|
|
8511
|
+
...(ownerError ? { taskArtifactOwnerCleanupError: ownerError } : {}),
|
|
7895
8512
|
sessionId: validated.target.sessionId,
|
|
7896
8513
|
sessionsRoot: validated.target.sessionsRoot,
|
|
7897
8514
|
transcriptPath: validated.target.transcriptPath,
|
|
@@ -7975,6 +8592,34 @@ async function executeLifecycleResponse(
|
|
|
7975
8592
|
: {}),
|
|
7976
8593
|
},
|
|
7977
8594
|
);
|
|
8595
|
+
await broker.ledger.transition(identity, "effect_started", { intendedSessionId: id, response: pending });
|
|
8596
|
+
return pending;
|
|
8597
|
+
}
|
|
8598
|
+
if (
|
|
8599
|
+
deleted.kind === "deleted" &&
|
|
8600
|
+
cleanupTarget.taskArtifactOwnerPayloadRetired === true &&
|
|
8601
|
+
cleanupTarget.taskArtifactOwnerNamespaceRetained === true
|
|
8602
|
+
) {
|
|
8603
|
+
if (!logicalTranscriptRetirementMatches())
|
|
8604
|
+
return await publishTaskArtifactOwnerPending(
|
|
8605
|
+
"task_artifact_owner_transcript_disposition_mismatch",
|
|
8606
|
+
preauthorizedCleanup,
|
|
8607
|
+
);
|
|
8608
|
+
if (record) await appendSessionDeletedEvidence(broker, record);
|
|
8609
|
+
const residual = fail(
|
|
8610
|
+
"cleanup_pending",
|
|
8611
|
+
"Transcript retirement is complete; the task-artifact owner namespace remains pending in artifacts.",
|
|
8612
|
+
{
|
|
8613
|
+
...cleanupWithFreshOwnerPayload(preauthorizedCleanup),
|
|
8614
|
+
phase: "artifacts",
|
|
8615
|
+
artifactsRemoved: true,
|
|
8616
|
+
artifactsAbsentAtAuthorization: undefined,
|
|
8617
|
+
taskArtifactOwnerTranscriptDeleted: true,
|
|
8618
|
+
},
|
|
8619
|
+
);
|
|
8620
|
+
await broker.ledger.transition(identity, "effect_started", { intendedSessionId: id, response: residual });
|
|
8621
|
+
return residual;
|
|
8622
|
+
}
|
|
7978
8623
|
|
|
7979
8624
|
const completion = { ok: true, result: { sessionId: id } } as const;
|
|
7980
8625
|
if (metadataCleanup.lifecycleFiles?.length) {
|