@gajae-code/coding-agent 0.18.7 → 0.18.9
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 +377 -1
- package/dist/types/async/endpoint-id.d.ts +16 -0
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/async/support.d.ts +0 -14
- 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/commands/mcp-serve.d.ts +4 -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/discovery/mcp-json.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +18 -0
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/html-export-destination.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
- 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 +4 -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/psmux-detect.d.ts +3 -3
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
- 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 +14 -1
- package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
- 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/config.d.ts +8 -5
- 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/manager.d.ts +2 -1
- package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/project-host-values.d.ts +27 -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 +24 -2
- package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
- package/dist/types/sdk/broker/ensure.d.ts +14 -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 +69 -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/skill-state/workflow-mutation-guard.d.ts +3 -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/task/isolation-dir.d.ts +26 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/browser/cdp-url.d.ts +2 -0
- package/dist/types/tools/github-side-effect.d.ts +22 -0
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/monitor-mutation.d.ts +9 -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/endpoint-id.ts +26 -0
- package/src/async/job-manager.ts +247 -132
- package/src/async/support.ts +0 -25
- package/src/capability/index.ts +15 -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/doctor/repairs.ts +4 -1
- package/src/cli/doctor-supervisor.ts +3 -0
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/public-command-registry.ts +28 -6
- 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/mcp-serve.ts +7 -0
- package/src/commands/sdk.ts +455 -397
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +316 -79
- 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/coordinator-mcp/server.ts +2 -0
- 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 +4 -13
- package/src/dap/config.ts +2 -2
- 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/register.ts +10 -6
- 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 +13 -1
- package/src/discovery/helpers.ts +171 -29
- package/src/discovery/mcp-json.ts +26 -12
- 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 +796 -154
- package/src/eval/py/kernel.ts +79 -13
- package/src/eval/py/tool-bridge.ts +4 -0
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/html-export-destination.ts +16 -0
- package/src/export/html/index.ts +7 -10
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
- package/src/extensibility/extensions/runner.ts +15 -1
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +85 -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 +9 -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 +70 -9
- package/src/gjc-runtime/psmux-detect.ts +28 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/session-state-sidecar.ts +3 -2
- package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
- 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 +105 -16
- package/src/gjc-runtime/tmux-common.ts +8 -5
- 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 +20 -19
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/config.ts +12 -63
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/memories/index.ts +88 -7
- 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/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +1 -1
- 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 +76 -31
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -16
- package/src/modes/controllers/selector-controller.ts +24 -15
- package/src/modes/controllers/todo-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +5 -1
- 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/client.ts +4 -2
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/json-rpc.ts +2 -1
- package/src/runtime-mcp/manager.ts +29 -11
- package/src/runtime-mcp/mcp-redirect.ts +73 -0
- package/src/runtime-mcp/oauth-flow.ts +14 -2
- package/src/runtime-mcp/oauth-public-url.ts +14 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/pool.ts +6 -0
- package/src/runtime-mcp/project-host-values.ts +46 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +4 -3
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +95 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +81 -6
- package/src/sdk/broker/doctor-restart.ts +4 -0
- package/src/sdk/broker/ensure.ts +31 -2
- 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 +2705 -303
- package/src/sdk/host/session-runtime.ts +490 -143
- 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 +82 -18
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +69 -45
- package/src/session/agent-session.ts +679 -238
- package/src/session/artifacts.ts +176 -71
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3779 -462
- package/src/session/internal/managed-session-storage.ts +501 -78
- 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 +951 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +447 -15
- 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 +145 -2
- package/src/skill-state/workflow-mutation-guard.ts +102 -10
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/mcp.ts +8 -4
- 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/task/isolation-dir.ts +143 -0
- package/src/task/worktree.ts +16 -7
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit-write-path.ts +68 -0
- package/src/tools/ast-edit.ts +10 -1
- 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/browser/cdp-url.ts +18 -0
- package/src/tools/browser.ts +8 -1
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/gh.ts +10 -0
- package/src/tools/github-side-effect.ts +112 -0
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/monitor-mutation.ts +27 -0
- package/src/tools/monitor.ts +7 -0
- package/src/tools/python.ts +188 -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/external-editor.ts +3 -2
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/insane/url-guard.ts +2 -1
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -2,6 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
+
import * as util from "node:util";
|
|
5
6
|
|
|
6
7
|
import type * as native from "@gajae-code/natives";
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ function nativeSessionStorage(): typeof import("@gajae-code/natives") {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
import { isEnoent, pathIsWithin, peekFile, toError } from "@gajae-code/utils";
|
|
19
|
+
import type { ManagedGcProtocolScopeInspector } from "./internal/managed-session-scope";
|
|
18
20
|
import {
|
|
19
21
|
assertManagedDirectoryRoot,
|
|
20
22
|
type ManagedDirectoryRoot,
|
|
@@ -28,7 +30,26 @@ import {
|
|
|
28
30
|
type NativePublishOutcome,
|
|
29
31
|
} from "./internal/native-publish-outcome";
|
|
30
32
|
import { isDerivedSessionMemoryFile } from "./internal/session-memory-sidecar";
|
|
33
|
+
import { captureTaskArtifactOwnerDeletionEvidence } from "./internal/task-artifact-owner-access";
|
|
34
|
+
import {
|
|
35
|
+
hasSiblingTaskArtifactOwnerTranscript,
|
|
36
|
+
taskArtifactOwnerLocatorFromTranscriptBytes,
|
|
37
|
+
} from "./internal/task-artifact-owner-transcript";
|
|
31
38
|
import { parseFirstJsonlLine } from "./session-transcript-header";
|
|
39
|
+
import {
|
|
40
|
+
parseTaskArtifactOwnerDeletionEvidence,
|
|
41
|
+
parseTaskArtifactOwnerRetirementOutcome,
|
|
42
|
+
type TaskArtifactOwnerDeletionEvidence,
|
|
43
|
+
type TaskArtifactOwnerLocator,
|
|
44
|
+
type TaskArtifactOwnerRetirementContinuation,
|
|
45
|
+
type TaskArtifactOwnerRetirementOutcome,
|
|
46
|
+
type TaskArtifactOwnerStorageContext,
|
|
47
|
+
} from "./task-artifact-owner-codec";
|
|
48
|
+
import {
|
|
49
|
+
retireTaskArtifactOwner,
|
|
50
|
+
verifyTaskArtifactOwnerPhysicalRetirement,
|
|
51
|
+
verifyTaskArtifactOwnerRetirementContinuation,
|
|
52
|
+
} from "./task-artifact-owner-retirement";
|
|
32
53
|
|
|
33
54
|
const utf8Decoder = new TextDecoder("utf-8");
|
|
34
55
|
const newlineBuffer = Buffer.from("\n", "utf8");
|
|
@@ -443,8 +464,24 @@ export interface VerifiedSessionDeleteTarget {
|
|
|
443
464
|
retainedTranscriptSuccessorPath?: string;
|
|
444
465
|
/** Native-retained exchange placeholder observed during transcript cleanup. */
|
|
445
466
|
retainedTranscriptPlaceholderPath?: string;
|
|
446
|
-
/** Native-retained
|
|
467
|
+
/** Native-retained entry whose identity could not be verified. */
|
|
447
468
|
retainedTranscriptUnknownPath?: string;
|
|
469
|
+
/** Trusted managed authority for the path-free persisted task-artifact locator. */
|
|
470
|
+
taskArtifactOwnerStorageContext?: TaskArtifactOwnerStorageContext;
|
|
471
|
+
/** Exact owner-tree evidence captured before deletion and retained across retries. */
|
|
472
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
473
|
+
/** Owning journal persists the full native disposition across delete effects. */
|
|
474
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
475
|
+
/** Native terminal evidence of logical transcript deletion; pathname absence alone is insufficient. */
|
|
476
|
+
taskArtifactOwnerTranscriptDeleted?: true;
|
|
477
|
+
/** Explicitly leave owner retirement to its owning coordinator. */
|
|
478
|
+
deferTaskArtifactOwnerRetirement?: true;
|
|
479
|
+
/** Durable evidence that exact owner retirement completed. */
|
|
480
|
+
taskArtifactOwnerRetired?: true;
|
|
481
|
+
/** Exact native remnant, separate from immutable original authority. */
|
|
482
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
483
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
484
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
448
485
|
/** Native-retained publisher successor observed during cleanup. */
|
|
449
486
|
retainedArtifactsSuccessorPath?: string;
|
|
450
487
|
/** Native-retained exchange placeholder observed during cleanup. */
|
|
@@ -467,8 +504,28 @@ export interface VerifiedSessionDeleteTarget {
|
|
|
467
504
|
* `deleted` and never `{}`.
|
|
468
505
|
*/
|
|
469
506
|
export type VerifiedSessionDeleteResult =
|
|
470
|
-
| {
|
|
471
|
-
|
|
507
|
+
| {
|
|
508
|
+
kind: "artifacts_removed";
|
|
509
|
+
phase: "artifacts";
|
|
510
|
+
transcriptIdentity: SessionStorageFileIdentity;
|
|
511
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
512
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
513
|
+
taskArtifactOwnerTranscriptDeleted?: true;
|
|
514
|
+
taskArtifactOwnerRetired?: true;
|
|
515
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
516
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
517
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
518
|
+
}
|
|
519
|
+
| {
|
|
520
|
+
kind: "deleted";
|
|
521
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
522
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
523
|
+
taskArtifactOwnerTranscriptDeleted?: true;
|
|
524
|
+
taskArtifactOwnerRetired?: true;
|
|
525
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
526
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
527
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
528
|
+
}
|
|
472
529
|
| {
|
|
473
530
|
kind: "cleanup_pending";
|
|
474
531
|
phase: "artifacts";
|
|
@@ -485,6 +542,28 @@ export type VerifiedSessionDeleteResult =
|
|
|
485
542
|
retainedSuccessorPath?: string;
|
|
486
543
|
retainedPlaceholderPath?: string;
|
|
487
544
|
retainedUnknownPath?: string;
|
|
545
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
546
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
547
|
+
taskArtifactOwnerTranscriptDeleted?: true;
|
|
548
|
+
taskArtifactOwnerRetired?: true;
|
|
549
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
550
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
551
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
552
|
+
}
|
|
553
|
+
| {
|
|
554
|
+
kind: "cleanup_pending";
|
|
555
|
+
phase: "task_artifact_owner";
|
|
556
|
+
/** True only after verified artifact completion, never inferred from the failure phase. */
|
|
557
|
+
artifactsRemoved: boolean;
|
|
558
|
+
error: Error;
|
|
559
|
+
transcriptIdentity: SessionStorageFileIdentity;
|
|
560
|
+
taskArtifactOwnerDeletionEvidence: TaskArtifactOwnerDeletionEvidence;
|
|
561
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
562
|
+
taskArtifactOwnerTranscriptDeleted?: true;
|
|
563
|
+
taskArtifactOwnerRetired?: true;
|
|
564
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
565
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
566
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
488
567
|
}
|
|
489
568
|
| {
|
|
490
569
|
kind: "cleanup_pending";
|
|
@@ -498,6 +577,13 @@ export type VerifiedSessionDeleteResult =
|
|
|
498
577
|
retainedSuccessorPath?: string;
|
|
499
578
|
retainedPlaceholderPath?: string;
|
|
500
579
|
retainedUnknownPath?: string;
|
|
580
|
+
taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
|
|
581
|
+
taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
|
|
582
|
+
taskArtifactOwnerTranscriptDeleted?: true;
|
|
583
|
+
taskArtifactOwnerRetired?: true;
|
|
584
|
+
taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
|
|
585
|
+
taskArtifactOwnerPayloadRetired?: true;
|
|
586
|
+
taskArtifactOwnerNamespaceRetained?: true;
|
|
501
587
|
};
|
|
502
588
|
|
|
503
589
|
/** Default OS-close dispatcher: a direct `fs.closeSync`. */
|
|
@@ -736,6 +822,34 @@ function secureOwnerOnlyFileDescriptor(
|
|
|
736
822
|
if (!result.ok) throw new Error(`Owner-only security rejected ${pathname}: ${result.code}`);
|
|
737
823
|
}
|
|
738
824
|
|
|
825
|
+
/**
|
|
826
|
+
* Replace an explicit session file. Creation mode `0600` does not install a
|
|
827
|
+
* Windows owner-only DACL, so this applies and verifies the same native path a
|
|
828
|
+
* primary transcript uses when it has no managed security context. POSIX mode
|
|
829
|
+
* stays `0600`.
|
|
830
|
+
*/
|
|
831
|
+
export function writeExplicitOwnerOnlyTextSync(pathname: string, content: string): void {
|
|
832
|
+
const fd = fs.openSync(
|
|
833
|
+
pathname,
|
|
834
|
+
fs.constants.O_WRONLY | fs.constants.O_CREAT | (fs.constants.O_NOFOLLOW ?? 0),
|
|
835
|
+
0o600,
|
|
836
|
+
);
|
|
837
|
+
try {
|
|
838
|
+
secureOwnerOnlyFileDescriptor(pathname, fd, "apply", undefined);
|
|
839
|
+
fs.ftruncateSync(fd, 0);
|
|
840
|
+
const bytes = Buffer.from(content, "utf8");
|
|
841
|
+
let offset = 0;
|
|
842
|
+
while (offset < bytes.byteLength) {
|
|
843
|
+
const written = fs.writeSync(fd, bytes, offset, bytes.byteLength - offset);
|
|
844
|
+
if (written === 0) throw new Error("Short write");
|
|
845
|
+
offset += written;
|
|
846
|
+
}
|
|
847
|
+
secureOwnerOnlyFileDescriptor(pathname, fd, "verify", undefined);
|
|
848
|
+
} finally {
|
|
849
|
+
fs.closeSync(fd);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
739
853
|
/** Reject a symlink/junction/reparse component before a storage path is created or opened. */
|
|
740
854
|
function assertNoReparsePath(pathname: string): void {
|
|
741
855
|
const resolved = path.resolve(pathname);
|
|
@@ -1987,7 +2101,8 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1987
2101
|
|
|
1988
2102
|
writeTextSync(fpath: string, content: string): void {
|
|
1989
2103
|
this.ensureDirSync(path.dirname(fpath));
|
|
1990
|
-
|
|
2104
|
+
// New fork and branch transcripts are created here.
|
|
2105
|
+
writeExplicitOwnerOnlyTextSync(fpath, content);
|
|
1991
2106
|
}
|
|
1992
2107
|
|
|
1993
2108
|
readTextSync(fpath: string): string {
|
|
@@ -2265,7 +2380,10 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2265
2380
|
* revalidate, transcript last. Partial deletion returns typed cleanup_pending
|
|
2266
2381
|
* evidence; identity/symlink/containment/header/cwd mismatch throws.
|
|
2267
2382
|
*/
|
|
2268
|
-
async deleteSessionVerified(
|
|
2383
|
+
async deleteSessionVerified(
|
|
2384
|
+
target: VerifiedSessionDeleteTarget,
|
|
2385
|
+
inspectProtocol?: ManagedGcProtocolScopeInspector,
|
|
2386
|
+
): Promise<VerifiedSessionDeleteResult> {
|
|
2269
2387
|
const {
|
|
2270
2388
|
sessionsRoot,
|
|
2271
2389
|
transcriptPath,
|
|
@@ -2287,6 +2405,15 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2287
2405
|
plannedArtifactsPath,
|
|
2288
2406
|
plannedTranscriptPath,
|
|
2289
2407
|
artifactsRemoved,
|
|
2408
|
+
taskArtifactOwnerStorageContext,
|
|
2409
|
+
taskArtifactOwnerDeletionEvidence: ownerEvidenceValue,
|
|
2410
|
+
deferTaskArtifactOwnerRetirement,
|
|
2411
|
+
taskArtifactOwnerRetirementOutcome: ownerOutcomeValue,
|
|
2412
|
+
taskArtifactOwnerTranscriptDeleted: ownerTranscriptDeletedValue,
|
|
2413
|
+
taskArtifactOwnerRetired: ownerRetiredValue,
|
|
2414
|
+
taskArtifactOwnerRetirementContinuation: ownerContinuationValue,
|
|
2415
|
+
taskArtifactOwnerPayloadRetired: ownerPayloadRetiredValue,
|
|
2416
|
+
taskArtifactOwnerNamespaceRetained: ownerNamespaceRetainedValue,
|
|
2290
2417
|
} = target;
|
|
2291
2418
|
try {
|
|
2292
2419
|
assertNoReparsePath(sessionsRoot);
|
|
@@ -2302,6 +2429,16 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2302
2429
|
if (!pathIsWithin(sessionsRoot, transcriptPath)) {
|
|
2303
2430
|
throw new SessionDeleteVerificationError("containment", "Transcript is outside the sessions root");
|
|
2304
2431
|
}
|
|
2432
|
+
if (
|
|
2433
|
+
taskArtifactOwnerStorageContext &&
|
|
2434
|
+
path.resolve(taskArtifactOwnerStorageContext.sessionsRoot) !== path.resolve(sessionsRoot)
|
|
2435
|
+
)
|
|
2436
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_root_mismatch");
|
|
2437
|
+
if (
|
|
2438
|
+
(ownerTranscriptDeletedValue !== undefined && ownerTranscriptDeletedValue !== true) ||
|
|
2439
|
+
(deferTaskArtifactOwnerRetirement !== undefined && deferTaskArtifactOwnerRetirement !== true)
|
|
2440
|
+
)
|
|
2441
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_flag_invalid");
|
|
2305
2442
|
if (
|
|
2306
2443
|
!plannedArtifactsPath ||
|
|
2307
2444
|
!plannedTranscriptPath ||
|
|
@@ -2355,6 +2492,243 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2355
2492
|
"Transcript parent identity does not match authorization",
|
|
2356
2493
|
);
|
|
2357
2494
|
const authorizedTranscriptParentIdentity = transcriptParentIdentity ?? parentIdentity;
|
|
2495
|
+
let initial: { snapshot: SessionStorageSnapshot } | undefined;
|
|
2496
|
+
if (ownerTranscriptDeletedValue === true && fs.existsSync(transcriptPath))
|
|
2497
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_transcript_deleted_mismatch");
|
|
2498
|
+
if (!hasDetachedTranscript && ownerTranscriptDeletedValue !== true) {
|
|
2499
|
+
try {
|
|
2500
|
+
initial = this.#verifiedReadAndHeader(transcriptPath, sessionId, cwd);
|
|
2501
|
+
} catch (error) {
|
|
2502
|
+
const absent =
|
|
2503
|
+
error instanceof SessionDeleteVerificationError &&
|
|
2504
|
+
error.kind === "stat" &&
|
|
2505
|
+
(error.cause as NodeJS.ErrnoException | undefined)?.code === "ENOENT";
|
|
2506
|
+
if (!absent || !detachedArtifactsPath || artifactsRemoved) throw error;
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
const ownerTranscript =
|
|
2510
|
+
ownerTranscriptDeletedValue === true
|
|
2511
|
+
? undefined
|
|
2512
|
+
: (initial ??
|
|
2513
|
+
(hasDetachedTranscript && fs.existsSync(cleanupTranscriptPath)
|
|
2514
|
+
? this.#verifiedReadAndHeader(cleanupTranscriptPath, sessionId, cwd)
|
|
2515
|
+
: undefined));
|
|
2516
|
+
let ownerLocator: TaskArtifactOwnerLocator | undefined;
|
|
2517
|
+
try {
|
|
2518
|
+
ownerLocator = ownerTranscript
|
|
2519
|
+
? taskArtifactOwnerLocatorFromTranscriptBytes(ownerTranscript.snapshot.bytes, sessionId)
|
|
2520
|
+
: undefined;
|
|
2521
|
+
} catch (error) {
|
|
2522
|
+
const failure = toError(error);
|
|
2523
|
+
if (failure.message.startsWith("task_artifact_owner_")) throw failure;
|
|
2524
|
+
throw new SessionDeleteVerificationError("artifacts", `task_artifact_owner_patch_invalid:${failure.message}`, {
|
|
2525
|
+
cause: failure,
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
const withTaskArtifactOwnerFailure = <T>(operation: () => T): T => {
|
|
2529
|
+
try {
|
|
2530
|
+
return operation();
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
const failure = toError(error);
|
|
2533
|
+
if (failure.message.startsWith("task_artifact_owner_")) throw failure;
|
|
2534
|
+
throw new SessionDeleteVerificationError(
|
|
2535
|
+
"artifacts",
|
|
2536
|
+
`task_artifact_owner_cleanup_failed:${failure.message}`,
|
|
2537
|
+
{ cause: failure },
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
let ownerEvidence = ownerEvidenceValue
|
|
2542
|
+
? withTaskArtifactOwnerFailure(() => parseTaskArtifactOwnerDeletionEvidence(ownerEvidenceValue))
|
|
2543
|
+
: undefined;
|
|
2544
|
+
if (ownerEvidence && ownerEvidence.sessionId !== sessionId)
|
|
2545
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_session_mismatch");
|
|
2546
|
+
if (ownerLocator && ownerEvidence && !util.isDeepStrictEqual(ownerLocator, ownerEvidence.locator))
|
|
2547
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_locator_mismatch");
|
|
2548
|
+
if (ownerLocator && !ownerEvidence && taskArtifactOwnerStorageContext)
|
|
2549
|
+
ownerEvidence = withTaskArtifactOwnerFailure(() =>
|
|
2550
|
+
captureTaskArtifactOwnerDeletionEvidence(taskArtifactOwnerStorageContext, sessionId, ownerLocator),
|
|
2551
|
+
);
|
|
2552
|
+
if (ownerLocator && !ownerEvidence)
|
|
2553
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_locator_missing");
|
|
2554
|
+
if (ownerEvidence && ownerTranscript && !ownerLocator)
|
|
2555
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_evidence_not_in_transcript");
|
|
2556
|
+
if (ownerEvidence && !taskArtifactOwnerStorageContext)
|
|
2557
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_context_missing");
|
|
2558
|
+
if (ownerLocator && deferTaskArtifactOwnerRetirement && (!ownerEvidence || !taskArtifactOwnerStorageContext))
|
|
2559
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_defer_evidence_missing");
|
|
2560
|
+
if (ownerTranscriptDeletedValue === true && !ownerEvidence)
|
|
2561
|
+
throw new SessionDeleteVerificationError(
|
|
2562
|
+
"artifacts",
|
|
2563
|
+
"task_artifact_owner_transcript_deleted_evidence_missing",
|
|
2564
|
+
);
|
|
2565
|
+
let ownerOutcome =
|
|
2566
|
+
ownerOutcomeValue !== undefined && ownerEvidence && taskArtifactOwnerStorageContext
|
|
2567
|
+
? withTaskArtifactOwnerFailure(() =>
|
|
2568
|
+
parseTaskArtifactOwnerRetirementOutcome(
|
|
2569
|
+
taskArtifactOwnerStorageContext,
|
|
2570
|
+
ownerEvidence!,
|
|
2571
|
+
ownerOutcomeValue,
|
|
2572
|
+
),
|
|
2573
|
+
)
|
|
2574
|
+
: undefined;
|
|
2575
|
+
if (ownerOutcomeValue !== undefined && !ownerOutcome)
|
|
2576
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_outcome_missing");
|
|
2577
|
+
if (
|
|
2578
|
+
ownerTranscriptDeletedValue === true &&
|
|
2579
|
+
ownerOutcome?.kind !== "completed" &&
|
|
2580
|
+
ownerOutcome?.kind !== "payload_retired"
|
|
2581
|
+
)
|
|
2582
|
+
throw new SessionDeleteVerificationError(
|
|
2583
|
+
"artifacts",
|
|
2584
|
+
"task_artifact_owner_transcript_deleted_outcome_missing",
|
|
2585
|
+
);
|
|
2586
|
+
if ((ownerContinuationValue !== undefined) !== (ownerOutcome !== undefined && ownerOutcome.kind !== "completed"))
|
|
2587
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_continuation_mismatch");
|
|
2588
|
+
if (
|
|
2589
|
+
ownerOutcome &&
|
|
2590
|
+
ownerOutcome.kind !== "completed" &&
|
|
2591
|
+
!util.isDeepStrictEqual(ownerContinuationValue, ownerOutcome.continuation)
|
|
2592
|
+
)
|
|
2593
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_continuation_mismatch");
|
|
2594
|
+
const ownerContinuationRecord =
|
|
2595
|
+
ownerOutcome && ownerOutcome.kind !== "completed" ? ownerOutcome.continuation : undefined;
|
|
2596
|
+
let ownerContinuation = ownerContinuationRecord
|
|
2597
|
+
? withTaskArtifactOwnerFailure(() =>
|
|
2598
|
+
verifyTaskArtifactOwnerRetirementContinuation(
|
|
2599
|
+
taskArtifactOwnerStorageContext!,
|
|
2600
|
+
ownerEvidence!,
|
|
2601
|
+
ownerContinuationRecord,
|
|
2602
|
+
),
|
|
2603
|
+
)
|
|
2604
|
+
: undefined;
|
|
2605
|
+
const payloadRetired = ownerOutcome?.kind === "payload_retired";
|
|
2606
|
+
if (
|
|
2607
|
+
(ownerRetiredValue !== undefined && ownerRetiredValue !== true) ||
|
|
2608
|
+
(ownerPayloadRetiredValue !== undefined && ownerPayloadRetiredValue !== true) ||
|
|
2609
|
+
(ownerNamespaceRetainedValue !== undefined && ownerNamespaceRetainedValue !== true) ||
|
|
2610
|
+
(ownerRetiredValue === true) !== (ownerOutcome?.kind === "completed") ||
|
|
2611
|
+
(ownerPayloadRetiredValue === true) !== payloadRetired ||
|
|
2612
|
+
(ownerNamespaceRetainedValue === true) !== payloadRetired
|
|
2613
|
+
)
|
|
2614
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_outcome_mismatch");
|
|
2615
|
+
if (ownerRetiredValue === true) {
|
|
2616
|
+
if (!ownerEvidence || !taskArtifactOwnerStorageContext || !ownerOutcome)
|
|
2617
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retired_evidence_missing");
|
|
2618
|
+
withTaskArtifactOwnerFailure(() =>
|
|
2619
|
+
verifyTaskArtifactOwnerPhysicalRetirement(taskArtifactOwnerStorageContext, ownerEvidence!, ownerOutcome),
|
|
2620
|
+
);
|
|
2621
|
+
}
|
|
2622
|
+
if (
|
|
2623
|
+
ownerEvidence &&
|
|
2624
|
+
ownerLocator &&
|
|
2625
|
+
taskArtifactOwnerStorageContext &&
|
|
2626
|
+
!ownerContinuation &&
|
|
2627
|
+
!ownerRetiredValue
|
|
2628
|
+
) {
|
|
2629
|
+
const current = withTaskArtifactOwnerFailure(() =>
|
|
2630
|
+
captureTaskArtifactOwnerDeletionEvidence(taskArtifactOwnerStorageContext, sessionId, ownerLocator),
|
|
2631
|
+
);
|
|
2632
|
+
if (!current || !util.isDeepStrictEqual(current, ownerEvidence))
|
|
2633
|
+
throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_changed_since_capture");
|
|
2634
|
+
}
|
|
2635
|
+
let ownerRetired = ownerRetiredValue === true;
|
|
2636
|
+
let ownerPayloadRetired = ownerPayloadRetiredValue === true;
|
|
2637
|
+
let ownerNamespaceRetained = ownerNamespaceRetainedValue === true;
|
|
2638
|
+
let ownerTranscriptDeleted = ownerTranscriptDeletedValue === true;
|
|
2639
|
+
const ownerSiblingRefusal = async (): Promise<Error | undefined> => {
|
|
2640
|
+
// Deferral changes who retires the owner, not the pre-artifact sibling authority boundary.
|
|
2641
|
+
if (!ownerEvidence) return undefined;
|
|
2642
|
+
if (!taskArtifactOwnerStorageContext)
|
|
2643
|
+
return new SessionDeleteVerificationError("artifacts", "task_artifact_owner_context_missing");
|
|
2644
|
+
try {
|
|
2645
|
+
if (
|
|
2646
|
+
await hasSiblingTaskArtifactOwnerTranscript(
|
|
2647
|
+
this,
|
|
2648
|
+
transcriptPath,
|
|
2649
|
+
ownerEvidence.locator,
|
|
2650
|
+
taskArtifactOwnerStorageContext,
|
|
2651
|
+
inspectProtocol,
|
|
2652
|
+
)
|
|
2653
|
+
)
|
|
2654
|
+
return new SessionDeleteVerificationError(
|
|
2655
|
+
"artifacts",
|
|
2656
|
+
"task_artifact_owner_shared_with_sibling_transcript",
|
|
2657
|
+
);
|
|
2658
|
+
return undefined;
|
|
2659
|
+
} catch (error) {
|
|
2660
|
+
return new SessionDeleteVerificationError(
|
|
2661
|
+
"artifacts",
|
|
2662
|
+
`task_artifact_owner_cleanup_failed:${toError(error).message}`,
|
|
2663
|
+
{ cause: toError(error) },
|
|
2664
|
+
);
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2667
|
+
const retireOwnerIfRequired = async (): Promise<Error | undefined> => {
|
|
2668
|
+
const refusal = await ownerSiblingRefusal();
|
|
2669
|
+
if (refusal) return refusal;
|
|
2670
|
+
if (deferTaskArtifactOwnerRetirement || ownerRetired || !ownerEvidence || !taskArtifactOwnerStorageContext)
|
|
2671
|
+
return undefined;
|
|
2672
|
+
try {
|
|
2673
|
+
ownerOutcome = withTaskArtifactOwnerFailure(() =>
|
|
2674
|
+
retireTaskArtifactOwner(taskArtifactOwnerStorageContext, ownerEvidence!, ownerContinuation),
|
|
2675
|
+
);
|
|
2676
|
+
if (ownerOutcome.kind === "completed") {
|
|
2677
|
+
ownerRetired = true;
|
|
2678
|
+
ownerContinuation = undefined;
|
|
2679
|
+
ownerPayloadRetired = false;
|
|
2680
|
+
ownerNamespaceRetained = false;
|
|
2681
|
+
return undefined;
|
|
2682
|
+
}
|
|
2683
|
+
ownerContinuation = ownerOutcome.continuation;
|
|
2684
|
+
ownerPayloadRetired = ownerOutcome.kind === "payload_retired";
|
|
2685
|
+
ownerNamespaceRetained = ownerOutcome.kind === "payload_retired";
|
|
2686
|
+
return new SessionDeleteVerificationError(
|
|
2687
|
+
"artifacts",
|
|
2688
|
+
ownerOutcome.kind === "uncertain"
|
|
2689
|
+
? ownerOutcome.reason
|
|
2690
|
+
: "task_artifact_owner_namespace_cleanup_pending",
|
|
2691
|
+
);
|
|
2692
|
+
} catch (error) {
|
|
2693
|
+
return new SessionDeleteVerificationError(
|
|
2694
|
+
"artifacts",
|
|
2695
|
+
`task_artifact_owner_cleanup_failed:${toError(error).message}`,
|
|
2696
|
+
{ cause: toError(error) },
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2700
|
+
const ownerResultFields = () => ({
|
|
2701
|
+
...(ownerEvidence ? { taskArtifactOwnerDeletionEvidence: ownerEvidence } : {}),
|
|
2702
|
+
...(ownerOutcome ? { taskArtifactOwnerRetirementOutcome: ownerOutcome } : {}),
|
|
2703
|
+
...(ownerTranscriptDeleted ? { taskArtifactOwnerTranscriptDeleted: true as const } : {}),
|
|
2704
|
+
...(ownerRetired ? { taskArtifactOwnerRetired: true as const } : {}),
|
|
2705
|
+
...(ownerContinuation ? { taskArtifactOwnerRetirementContinuation: ownerContinuation } : {}),
|
|
2706
|
+
...(ownerPayloadRetired ? { taskArtifactOwnerPayloadRetired: true as const } : {}),
|
|
2707
|
+
...(ownerNamespaceRetained ? { taskArtifactOwnerNamespaceRetained: true as const } : {}),
|
|
2708
|
+
});
|
|
2709
|
+
const ownerCleanupPending = (error: Error, completedArtifacts: boolean): VerifiedSessionDeleteResult => ({
|
|
2710
|
+
kind: "cleanup_pending",
|
|
2711
|
+
phase: "task_artifact_owner",
|
|
2712
|
+
artifactsRemoved: completedArtifacts,
|
|
2713
|
+
error,
|
|
2714
|
+
transcriptIdentity,
|
|
2715
|
+
taskArtifactOwnerDeletionEvidence: ownerEvidence!,
|
|
2716
|
+
...ownerResultFields(),
|
|
2717
|
+
});
|
|
2718
|
+
const finishArtifactPhase = async (): Promise<VerifiedSessionDeleteResult> => {
|
|
2719
|
+
const ownerError = await retireOwnerIfRequired();
|
|
2720
|
+
if (ownerError && ownerEvidence) return ownerCleanupPending(ownerError, true);
|
|
2721
|
+
if (ownerTranscriptDeleted) return { kind: "deleted", ...ownerResultFields() };
|
|
2722
|
+
return {
|
|
2723
|
+
kind: "artifacts_removed",
|
|
2724
|
+
phase: "artifacts",
|
|
2725
|
+
transcriptIdentity,
|
|
2726
|
+
...ownerResultFields(),
|
|
2727
|
+
};
|
|
2728
|
+
};
|
|
2729
|
+
const ownerPreflightError = await ownerSiblingRefusal();
|
|
2730
|
+
if (ownerPreflightError && ownerEvidence)
|
|
2731
|
+
return ownerCleanupPending(ownerPreflightError, artifactsRemoved === true);
|
|
2358
2732
|
if (detachedArtifactsPath && !artifactsRemoved) {
|
|
2359
2733
|
if (
|
|
2360
2734
|
!expectedArtifactsIdentity ||
|
|
@@ -2427,16 +2801,16 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2427
2801
|
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
2428
2802
|
: {}),
|
|
2429
2803
|
transcriptIdentity,
|
|
2804
|
+
...ownerResultFields(),
|
|
2430
2805
|
};
|
|
2431
2806
|
}
|
|
2432
|
-
return
|
|
2807
|
+
return await finishArtifactPhase();
|
|
2433
2808
|
}
|
|
2434
2809
|
if (transcriptIdentity.nlink === undefined || transcriptIdentity.nlink !== 1n)
|
|
2435
2810
|
throw new SessionDeleteVerificationError(
|
|
2436
2811
|
"identity",
|
|
2437
2812
|
"Single-link transcript authority is required for exact deletion",
|
|
2438
2813
|
);
|
|
2439
|
-
const initial = hasDetachedTranscript ? undefined : this.#verifiedReadAndHeader(transcriptPath, sessionId, cwd);
|
|
2440
2814
|
const initialStat = initial?.snapshot.stat;
|
|
2441
2815
|
const initialDigest = initial ? createHash("sha256").update(initial.snapshot.bytes).digest("hex") : undefined;
|
|
2442
2816
|
if (
|
|
@@ -2491,7 +2865,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2491
2865
|
"artifacts",
|
|
2492
2866
|
"Authorized artifact removal root remains after restart",
|
|
2493
2867
|
);
|
|
2494
|
-
return
|
|
2868
|
+
return await finishArtifactPhase();
|
|
2495
2869
|
}
|
|
2496
2870
|
|
|
2497
2871
|
if (artifactsIdentity && !artifactsRemoved) {
|
|
@@ -2564,6 +2938,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2564
2938
|
detachedArtifactsPath: detach.detachedPath,
|
|
2565
2939
|
artifactsTree,
|
|
2566
2940
|
transcriptIdentity,
|
|
2941
|
+
...ownerResultFields(),
|
|
2567
2942
|
};
|
|
2568
2943
|
} finally {
|
|
2569
2944
|
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
@@ -2581,6 +2956,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2581
2956
|
...(detach.retainedPlaceholderPath ? { retainedPlaceholderPath: detach.retainedPlaceholderPath } : {}),
|
|
2582
2957
|
...(detach.retainedUnknownPath ? { retainedUnknownPath: detach.retainedUnknownPath } : {}),
|
|
2583
2958
|
transcriptIdentity,
|
|
2959
|
+
...ownerResultFields(),
|
|
2584
2960
|
};
|
|
2585
2961
|
}
|
|
2586
2962
|
const removal = removeDirectoryTreeExact(
|
|
@@ -2633,6 +3009,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2633
3009
|
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
2634
3010
|
: {}),
|
|
2635
3011
|
transcriptIdentity,
|
|
3012
|
+
...ownerResultFields(),
|
|
2636
3013
|
};
|
|
2637
3014
|
}
|
|
2638
3015
|
}
|
|
@@ -2658,8 +3035,19 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2658
3035
|
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
2659
3036
|
}
|
|
2660
3037
|
}
|
|
2661
|
-
return
|
|
3038
|
+
return await finishArtifactPhase();
|
|
2662
3039
|
}
|
|
3040
|
+
if (deferTaskArtifactOwnerRetirement && ownerEvidence && !ownerRetired && !ownerPayloadRetired) {
|
|
3041
|
+
return {
|
|
3042
|
+
kind: "artifacts_removed",
|
|
3043
|
+
phase: "artifacts",
|
|
3044
|
+
transcriptIdentity,
|
|
3045
|
+
...ownerResultFields(),
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
const ownerError = await retireOwnerIfRequired();
|
|
3049
|
+
if (ownerError && ownerEvidence) return ownerCleanupPending(ownerError, true);
|
|
3050
|
+
if (ownerTranscriptDeleted) return { kind: "deleted", ...ownerResultFields() };
|
|
2663
3051
|
if (hasDetachedTranscript) {
|
|
2664
3052
|
const deletion = nativeExactUnlink(cleanupTranscriptPath, {
|
|
2665
3053
|
dev: transcriptIdentity.dev,
|
|
@@ -2673,7 +3061,10 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2673
3061
|
quarantineName: path.basename(plannedTranscriptPath),
|
|
2674
3062
|
});
|
|
2675
3063
|
if (!deletion.ok) {
|
|
2676
|
-
if (transcriptDeletionTerminal(deletion))
|
|
3064
|
+
if (transcriptDeletionTerminal(deletion)) {
|
|
3065
|
+
if (ownerEvidence) ownerTranscriptDeleted = true;
|
|
3066
|
+
return { kind: "deleted", ...ownerResultFields() };
|
|
3067
|
+
}
|
|
2677
3068
|
const error = exactUnlinkFailure(deletion);
|
|
2678
3069
|
const retainedAuthority =
|
|
2679
3070
|
deletion.detachedPath ||
|
|
@@ -2689,6 +3080,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2689
3080
|
? { transcriptPayloadDurable: true as const }
|
|
2690
3081
|
: {}),
|
|
2691
3082
|
transcriptIdentity,
|
|
3083
|
+
...ownerResultFields(),
|
|
2692
3084
|
detachedTranscriptPath: deletion.detachedPath ?? detachedTranscriptPath,
|
|
2693
3085
|
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
2694
3086
|
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
@@ -2701,7 +3093,8 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2701
3093
|
: {}),
|
|
2702
3094
|
};
|
|
2703
3095
|
}
|
|
2704
|
-
|
|
3096
|
+
if (ownerEvidence) ownerTranscriptDeleted = true;
|
|
3097
|
+
return { kind: "deleted", ...ownerResultFields() };
|
|
2705
3098
|
}
|
|
2706
3099
|
if (!initialStat || !initialDigest)
|
|
2707
3100
|
throw new SessionDeleteVerificationError("stat", "Transcript cleanup state is invalid");
|
|
@@ -2742,7 +3135,10 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2742
3135
|
quarantineName: path.basename(plannedTranscriptPath),
|
|
2743
3136
|
});
|
|
2744
3137
|
if (!deletion.ok) {
|
|
2745
|
-
if (transcriptDeletionTerminal(deletion))
|
|
3138
|
+
if (transcriptDeletionTerminal(deletion)) {
|
|
3139
|
+
if (ownerEvidence) ownerTranscriptDeleted = true;
|
|
3140
|
+
return { kind: "deleted", ...ownerResultFields() };
|
|
3141
|
+
}
|
|
2746
3142
|
const error = exactUnlinkFailure(deletion);
|
|
2747
3143
|
const retainedAuthority =
|
|
2748
3144
|
deletion.detachedPath ||
|
|
@@ -2758,6 +3154,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2758
3154
|
? { transcriptPayloadDurable: true as const }
|
|
2759
3155
|
: {}),
|
|
2760
3156
|
transcriptIdentity,
|
|
3157
|
+
...ownerResultFields(),
|
|
2761
3158
|
detachedTranscriptPath: deletion.detachedPath,
|
|
2762
3159
|
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
2763
3160
|
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
@@ -2770,7 +3167,8 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
2770
3167
|
: {}),
|
|
2771
3168
|
};
|
|
2772
3169
|
}
|
|
2773
|
-
|
|
3170
|
+
if (ownerEvidence) ownerTranscriptDeleted = true;
|
|
3171
|
+
return { kind: "deleted", ...ownerResultFields() };
|
|
2774
3172
|
}
|
|
2775
3173
|
|
|
2776
3174
|
#verifiedReadAndHeader(
|
|
@@ -3085,9 +3483,11 @@ class MemorySessionStorageWriter implements SessionStorageBufferedWriter {
|
|
|
3085
3483
|
this.#length = nextLength;
|
|
3086
3484
|
}
|
|
3087
3485
|
|
|
3486
|
+
// Memory storage retains this view, so expose only visible bytes, never capacity.
|
|
3487
|
+
// Appends write after each published prefix; growth switches backing buffers.
|
|
3088
3488
|
#writeCurrent(bytesWritten: number): void {
|
|
3089
3489
|
this.#writeCalls++;
|
|
3090
|
-
this.#storage.writeBytesOwnedSync(this.#path,
|
|
3490
|
+
this.#storage.writeBytesOwnedSync(this.#path, this.#bytes.subarray(0, this.#length));
|
|
3091
3491
|
this.#bytesWritten += bytesWritten;
|
|
3092
3492
|
}
|
|
3093
3493
|
|
|
@@ -3103,7 +3503,7 @@ class MemorySessionStorageWriter implements SessionStorageBufferedWriter {
|
|
|
3103
3503
|
}
|
|
3104
3504
|
this.#buffer.copy(this.#bytes, this.#length, 0, pendingBytes);
|
|
3105
3505
|
this.#writeCalls++;
|
|
3106
|
-
this.#storage.writeBytesOwnedSync(this.#path,
|
|
3506
|
+
this.#storage.writeBytesOwnedSync(this.#path, this.#bytes.subarray(0, nextLength));
|
|
3107
3507
|
this.#length = nextLength;
|
|
3108
3508
|
this.#bufferedBytes = 0;
|
|
3109
3509
|
this.#bytesWritten += pendingBytes;
|
|
@@ -3453,6 +3853,23 @@ export class MemorySessionStorage implements SessionStorage {
|
|
|
3453
3853
|
new SessionDeleteVerificationError("containment", "Transcript is outside the sessions root"),
|
|
3454
3854
|
);
|
|
3455
3855
|
}
|
|
3856
|
+
if (
|
|
3857
|
+
[
|
|
3858
|
+
target.taskArtifactOwnerStorageContext,
|
|
3859
|
+
target.taskArtifactOwnerDeletionEvidence,
|
|
3860
|
+
target.taskArtifactOwnerRetirementOutcome,
|
|
3861
|
+
target.taskArtifactOwnerTranscriptDeleted,
|
|
3862
|
+
target.deferTaskArtifactOwnerRetirement,
|
|
3863
|
+
target.taskArtifactOwnerRetired,
|
|
3864
|
+
target.taskArtifactOwnerRetirementContinuation,
|
|
3865
|
+
target.taskArtifactOwnerPayloadRetired,
|
|
3866
|
+
target.taskArtifactOwnerNamespaceRetained,
|
|
3867
|
+
].some(value => value !== undefined)
|
|
3868
|
+
) {
|
|
3869
|
+
return Promise.reject(
|
|
3870
|
+
new SessionDeleteVerificationError("artifacts", "task_artifact_owner_memory_backend_unsupported"),
|
|
3871
|
+
);
|
|
3872
|
+
}
|
|
3456
3873
|
const entry = this.#files.get(transcriptPath);
|
|
3457
3874
|
if (!entry) return Promise.resolve({ kind: "deleted" });
|
|
3458
3875
|
const snapshot = this.readSnapshotSync(transcriptPath);
|
|
@@ -3485,6 +3902,21 @@ export class MemorySessionStorage implements SessionStorage {
|
|
|
3485
3902
|
if (path.resolve(header.cwd) !== path.resolve(cwd)) {
|
|
3486
3903
|
return Promise.reject(new SessionDeleteVerificationError("cwd", "Transcript header cwd mismatch"));
|
|
3487
3904
|
}
|
|
3905
|
+
let ownerLocator: TaskArtifactOwnerLocator | undefined;
|
|
3906
|
+
try {
|
|
3907
|
+
ownerLocator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, sessionId);
|
|
3908
|
+
} catch (error) {
|
|
3909
|
+
return Promise.reject(
|
|
3910
|
+
new SessionDeleteVerificationError("artifacts", "task_artifact_owner_memory_backend_unsupported", {
|
|
3911
|
+
cause: toError(error),
|
|
3912
|
+
}),
|
|
3913
|
+
);
|
|
3914
|
+
}
|
|
3915
|
+
if (ownerLocator) {
|
|
3916
|
+
return Promise.reject(
|
|
3917
|
+
new SessionDeleteVerificationError("artifacts", "task_artifact_owner_memory_backend_unsupported"),
|
|
3918
|
+
);
|
|
3919
|
+
}
|
|
3488
3920
|
// Compatible artifact semantics: the memory backend models no directories, so
|
|
3489
3921
|
// a key at the artifact path is a non-directory sibling that must fail closed
|
|
3490
3922
|
// rather than be silently treated as an absent artifacts directory.
|