@gajae-code/coding-agent 0.14.2 → 0.15.0
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 +120 -1
- package/dist/types/async/job-manager.d.ts +4 -1
- package/dist/types/autoresearch/dashboard.d.ts +29 -4
- package/dist/types/autoresearch/data-context.d.ts +25 -0
- package/dist/types/autoresearch/git.d.ts +65 -7
- package/dist/types/autoresearch/harness.d.ts +58 -0
- package/dist/types/autoresearch/prompts.d.ts +51 -0
- package/dist/types/autoresearch/runs.d.ts +173 -0
- package/dist/types/autoresearch/types.d.ts +9 -139
- package/dist/types/capability/types.d.ts +20 -0
- package/dist/types/cli/accounts-cli.d.ts +1 -0
- package/dist/types/commands/{rlm.d.ts → autoresearch.d.ts} +2 -4
- package/dist/types/config/autorouting-contract.d.ts +146 -0
- package/dist/types/config/autorouting-generator.d.ts +40 -0
- package/dist/types/config/autorouting-tier-map.d.ts +16844 -0
- package/dist/types/config/autorouting.d.ts +35 -0
- package/dist/types/config/model-discovery-manager.d.ts +8 -1
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/provider-selection-policy.d.ts +23 -0
- package/dist/types/config/settings-schema.d.ts +61 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/durability.d.ts +29 -0
- package/dist/types/coordinator-mcp/event-webhook.d.ts +50 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +1 -1
- package/dist/types/coordinator-mcp/server.d.ts +14 -0
- package/dist/types/crash/index-store.d.ts +3 -0
- package/dist/types/crash/record-loader.d.ts +2 -0
- package/dist/types/crash/upstream/relay.d.ts +5 -3
- package/dist/types/cursor.d.ts +12 -0
- package/dist/types/defaults/gjc-defaults.d.ts +35 -1
- package/dist/types/defaults/gjc-skills.generated.d.ts +1 -1
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/loader.d.ts +32 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +1 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +194 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/psmux-detect.d.ts +1 -1
- package/dist/types/gjc-runtime/python-transcript.d.ts +19 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +2 -8
- package/dist/types/gjc-runtime/session-layout.d.ts +6 -3
- package/dist/types/gjc-runtime/state-renderer.d.ts +0 -22
- package/dist/types/gjc-runtime/state-schema.d.ts +7 -7
- package/dist/types/gjc-runtime/tmux-common.d.ts +2 -2
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +38 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -24
- package/dist/types/gjc-runtime/ultragoal-validation-policy.d.ts +73 -0
- package/dist/types/gjc-runtime/workflow-recovery-projection.d.ts +100 -0
- package/dist/types/hooks/skill-keywords.d.ts +1 -1
- package/dist/types/main.d.ts +0 -10
- package/dist/types/modes/acp/acp-agent.d.ts +13 -6
- package/dist/types/modes/components/model-selector.d.ts +14 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +6 -1
- package/dist/types/modes/components/smart-routing-panel.d.ts +82 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +14 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +9 -0
- package/dist/types/rlm/artifacts.d.ts +4 -4
- package/dist/types/runtime/optional-runtime-services.d.ts +2 -1
- package/dist/types/runtime/workspace-tree-service.d.ts +5 -1
- package/dist/types/runtime-mcp/config.d.ts +6 -0
- package/dist/types/runtime-mcp/manager.d.ts +10 -0
- package/dist/types/sdk/acp/adapter.d.ts +0 -4
- package/dist/types/sdk/broker/broker.d.ts +1 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +57 -2
- package/dist/types/sdk/broker/session-index.d.ts +15 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/index.d.ts +8 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -2
- package/dist/types/sdk/client/client.d.ts +39 -4
- package/dist/types/sdk/host/host.d.ts +1 -0
- package/dist/types/sdk/host/internal-autorouting-state.d.ts +3 -0
- package/dist/types/sdk/host/model-pin.d.ts +29 -0
- package/dist/types/sdk/host/session-runtime.d.ts +2 -1
- package/dist/types/sdk/lifecycle-session.d.ts +13 -0
- package/dist/types/sdk/router/session-router.d.ts +27 -1
- package/dist/types/sdk/session.d.ts +11 -1
- package/dist/types/session/agent-session.d.ts +36 -6
- package/dist/types/session/artifacts.d.ts +16 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/session-manager.d.ts +84 -2
- package/dist/types/session/session-staging-paths.d.ts +3 -0
- package/dist/types/skill-state/canonical-skills.d.ts +1 -1
- package/dist/types/skill-state/workflow-hud.d.ts +12 -21
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +1 -0
- package/dist/types/slash-commands/helpers/autorouting-status.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +23 -0
- package/dist/types/task/executor.d.ts +35 -4
- package/dist/types/task/index.d.ts +17 -3
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +144 -0
- package/dist/types/tools/atomic-file-write.d.ts +72 -0
- package/dist/types/tools/descriptors.d.ts +1 -0
- package/dist/types/tools/index.d.ts +36 -1
- package/dist/types/tools/move-session.d.ts +40 -0
- package/dist/types/tools/python.d.ts +12 -0
- package/dist/types/tools/resource-gc.d.ts +0 -3
- package/dist/types/tools/search.d.ts +8 -1
- package/dist/types/tools/skill.d.ts +9 -4
- package/dist/types/tools/subagent.d.ts +33 -3
- package/dist/types/utils/xml-escape.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +2 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +1 -1
- package/package.json +11 -12
- package/scripts/build-sdk-package-smoke.ts +7 -5
- package/scripts/check-autorouting-tier-map.ts +183 -0
- package/scripts/generate-sdk-operation-inventory.ts +12 -0
- package/scripts/generate-tool-catalog.ts +40 -5
- package/src/async/job-manager.ts +5 -2
- package/src/autoresearch/dashboard.ts +134 -314
- package/src/autoresearch/data-context.ts +35 -0
- package/src/autoresearch/git.ts +186 -26
- package/src/autoresearch/{helpers.ts → harness.ts} +65 -40
- package/src/autoresearch/{prompt-setup.md → prompts/prompt-setup.md} +7 -7
- package/src/autoresearch/prompts/prompt.md +103 -0
- package/src/autoresearch/prompts.ts +126 -0
- package/src/autoresearch/runs.ts +708 -0
- package/src/autoresearch/types.ts +9 -150
- package/src/capability/index.ts +4 -4
- package/src/capability/types.ts +20 -0
- package/src/cli/accounts-cli.ts +21 -5
- package/src/cli/config-cli.ts +25 -1
- package/src/cli/fast-help.ts +1 -2
- package/src/cli/list-models.ts +5 -8
- package/src/cli/setup-cli.ts +8 -0
- package/src/cli.ts +2 -21
- package/src/commands/autoresearch.ts +33 -0
- package/src/commands/coordinator.ts +15 -0
- package/src/commands/launch.ts +2 -4
- package/src/commands/quick-lane.ts +1 -1
- package/src/commands/sdk.ts +24 -0
- package/src/commands/state.ts +1 -1
- package/src/config/autorouting-contract.ts +474 -0
- package/src/config/autorouting-generator.ts +238 -0
- package/src/config/autorouting-tier-map.ts +6108 -0
- package/src/config/autorouting.ts +120 -0
- package/src/config/model-discovery-manager.ts +42 -11
- package/src/config/model-registry.ts +348 -68
- package/src/config/provider-selection-policy.ts +60 -9
- package/src/config/settings-schema.ts +195 -6
- package/src/config/settings.ts +62 -1
- package/src/config.ts +2 -3
- package/src/coordinator/contract.ts +0 -1
- package/src/coordinator-mcp/codex-handoff.ts +68 -42
- package/src/coordinator-mcp/durability.ts +200 -0
- package/src/coordinator-mcp/event-webhook.ts +278 -0
- package/src/coordinator-mcp/question-state.ts +109 -46
- package/src/coordinator-mcp/server.ts +932 -504
- package/src/crash/index-store.ts +126 -22
- package/src/crash/record-loader.ts +7 -0
- package/src/crash/upstream/relay.ts +82 -34
- package/src/cursor.ts +84 -1
- package/src/debug/index.ts +26 -0
- package/src/defaults/gjc/skills/autoresearch/SKILL.md +116 -0
- package/src/defaults/gjc/skills/autoresearch/auto-critic.md +46 -0
- package/src/defaults/gjc/skills/autoresearch/auto-iterate.md +42 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +17 -37
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -18
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +6 -17
- package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +2 -2
- package/src/defaults/gjc-defaults.ts +89 -2
- package/src/defaults/gjc-skills.generated.ts +24 -17
- package/src/discovery/builtin.ts +18 -5
- package/src/discovery/helpers.ts +6 -6
- package/src/edit/index.ts +5 -2
- package/src/eval/py/executor.ts +8 -0
- package/src/extensibility/extensions/loader.ts +135 -13
- package/src/extensibility/extensions/wrapper.ts +1 -2
- package/src/extensibility/gjc-plugins/schema.ts +1 -1
- package/src/extensibility/runtime-skill-discovery.ts +2 -2
- package/src/extensibility/skill-management.ts +2 -3
- package/src/extensibility/skills.ts +16 -8
- package/src/gjc-runtime/autoresearch-runtime.ts +1509 -0
- package/src/gjc-runtime/gc-render.ts +0 -1
- package/src/gjc-runtime/gc-runtime.ts +1 -11
- package/src/gjc-runtime/launch-tmux.ts +3 -3
- package/src/gjc-runtime/psmux-detect.ts +2 -2
- package/src/gjc-runtime/python-transcript.ts +58 -0
- package/src/gjc-runtime/ralplan-runtime.ts +6 -18
- package/src/gjc-runtime/session-layout.ts +18 -7
- package/src/gjc-runtime/session-state-sidecar.ts +58 -9
- package/src/gjc-runtime/state-renderer.ts +0 -38
- package/src/gjc-runtime/state-runtime.ts +43 -22
- package/src/gjc-runtime/state-schema.ts +1 -1
- package/src/gjc-runtime/tmux-common.ts +7 -7
- package/src/gjc-runtime/ultragoal-change-set.ts +232 -11
- package/src/gjc-runtime/ultragoal-runtime.ts +309 -114
- package/src/gjc-runtime/ultragoal-validation-policy.ts +247 -0
- package/src/gjc-runtime/workflow-command-ref.ts +15 -11
- package/src/gjc-runtime/workflow-manifest.generated.json +413 -532
- package/src/gjc-runtime/workflow-manifest.ts +43 -83
- package/src/gjc-runtime/workflow-recovery-projection.ts +616 -0
- package/src/hooks/skill-keywords.ts +3 -3
- package/src/hooks/skill-state.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +26 -24
- package/src/lsp/index.ts +25 -5
- package/src/main.ts +2 -27
- package/src/modes/acp/acp-agent.ts +200 -75
- package/src/modes/components/model-selector.ts +216 -13
- package/src/modes/components/oauth-selector.ts +5 -5
- package/src/modes/components/skill-hud/render.ts +173 -35
- package/src/modes/components/smart-routing-panel.ts +531 -0
- package/src/modes/components/tool-status-header.ts +6 -3
- package/src/modes/controllers/command-controller.ts +17 -7
- package/src/modes/controllers/input-controller.ts +44 -9
- package/src/modes/controllers/runtime-mcp-command-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +298 -65
- package/src/modes/interactive-mode.ts +8 -1
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +1 -1
- package/src/modes/types.ts +9 -1
- package/src/prompts/agents/planner.md +1 -1
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/volatile-project-context.md +2 -1
- package/src/prompts/tools/move-session.md +15 -0
- package/src/prompts/tools/python.md +12 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/prompts/tools/skill.md +2 -2
- package/src/prompts/tools/task-summary.md +1 -0
- package/src/prompts/tools/task.md +6 -0
- package/src/rlm/artifacts.ts +12 -10
- package/src/runtime/optional-runtime-services.ts +3 -2
- package/src/runtime/workspace-tree-service.ts +11 -3
- package/src/runtime-mcp/config.ts +8 -1
- package/src/runtime-mcp/manager.ts +33 -0
- package/src/sdk/acp/adapter.ts +48 -1
- package/src/sdk/broker/broker.ts +30 -2
- package/src/sdk/broker/discovery.ts +217 -2
- package/src/sdk/broker/lifecycle.ts +385 -59
- package/src/sdk/broker/session-index.ts +243 -48
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +8 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +5 -0
- package/src/sdk/bus/index.ts +208 -11
- package/src/sdk/bus/kind-aware-reconciliation.ts +16 -0
- package/src/sdk/bus/reconciliation-store.ts +27 -5
- package/src/sdk/bus/telegram-daemon-contract.ts +12 -2
- package/src/sdk/client/client.ts +204 -22
- package/src/sdk/host/host.ts +16 -0
- package/src/sdk/host/internal-autorouting-state.ts +13 -0
- package/src/sdk/host/model-pin.ts +101 -0
- package/src/sdk/host/reverse-leases.ts +2 -1
- package/src/sdk/host/session-runtime.test.ts +224 -1
- package/src/sdk/host/session-runtime.ts +125 -37
- package/src/sdk/lifecycle/service.ts +3 -1
- package/src/sdk/lifecycle-session.ts +42 -1
- package/src/sdk/protocol/operation-inventory.generated.json +66 -0
- package/src/sdk/router/session-router.ts +375 -36
- package/src/sdk/session.ts +547 -47
- package/src/session/account-inventory.ts +1 -1
- package/src/session/agent-session.ts +765 -189
- package/src/session/artifacts.ts +234 -4
- package/src/session/internal/managed-session-storage.ts +91 -4
- package/src/session/provider-safety-stop-hint.ts +3 -2
- package/src/session/session-manager.ts +775 -16
- package/src/session/session-staging-paths.ts +8 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +0 -1
- package/src/setup/host-plugin-setup.ts +2 -2
- package/src/skill-state/active-state.ts +2 -2
- package/src/skill-state/canonical-skills.ts +1 -1
- package/src/skill-state/initial-phase.ts +1 -1
- package/src/skill-state/workflow-hud.ts +41 -41
- package/src/skill-state/workflow-mutation-guard.ts +26 -7
- package/src/slash-commands/builtin-registry.ts +77 -0
- package/src/slash-commands/helpers/autorouting-status.ts +55 -0
- package/src/system-prompt.ts +102 -5
- package/src/task/discovery.ts +2 -3
- package/src/task/executor.ts +617 -43
- package/src/task/index.ts +262 -15
- package/src/task/receipt.ts +30 -0
- package/src/task/render.ts +11 -1
- package/src/task/types.ts +316 -0
- package/src/tools/atomic-file-write.ts +594 -0
- package/src/tools/bisect.ts +3 -2
- package/src/tools/descriptors.test.ts +3 -0
- package/src/tools/descriptors.ts +24 -0
- package/src/tools/image-gen.ts +5 -1
- package/src/tools/index.ts +62 -5
- package/src/tools/move-session.ts +107 -0
- package/src/tools/python.ts +205 -0
- package/src/tools/read.ts +88 -15
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resource-gc.ts +8 -177
- package/src/tools/search.ts +30 -6
- package/src/tools/skill-discovery.ts +1 -0
- package/src/tools/skill.ts +37 -7
- package/src/tools/subagent-render.ts +119 -13
- package/src/tools/subagent.ts +91 -74
- package/src/tools/tool-catalog.generated.ts +64 -2
- package/src/tools/write.ts +16 -12
- package/src/utils/xml-escape.ts +14 -0
- package/src/web/search/providers/codex.ts +2 -2
- package/src/workflow/workflow-intent-diff.ts +13 -12
- package/src/workflow/workflow-intent-report-consumer.ts +6 -6
- package/dist/types/autoresearch/helpers.d.ts +0 -24
- package/dist/types/autoresearch/index.d.ts +0 -2
- package/dist/types/autoresearch/state.d.ts +0 -17
- package/dist/types/autoresearch/storage.d.ts +0 -142
- package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
- package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
- package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
- package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
- package/dist/types/commands/team.d.ts +0 -24
- package/dist/types/gjc-runtime/team-gc.d.ts +0 -7
- package/dist/types/gjc-runtime/team-launch.d.ts +0 -61
- package/dist/types/gjc-runtime/team-notify.d.ts +0 -45
- package/dist/types/gjc-runtime/team-runtime.d.ts +0 -336
- package/dist/types/gjc-runtime/team-store.d.ts +0 -168
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +0 -43
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +0 -92
- package/dist/types/gjc-runtime/team-workers.d.ts +0 -83
- package/dist/types/rlm/index.d.ts +0 -47
- package/dist/types/rlm/preset.d.ts +0 -23
- package/dist/types/rlm/python-tool.d.ts +0 -16
- package/src/autoresearch/command-resume.md +0 -14
- package/src/autoresearch/index.ts +0 -536
- package/src/autoresearch/prompt.md +0 -103
- package/src/autoresearch/resume-message.md +0 -10
- package/src/autoresearch/state.ts +0 -273
- package/src/autoresearch/storage.ts +0 -699
- package/src/autoresearch/tools/init-experiment.ts +0 -272
- package/src/autoresearch/tools/log-experiment.ts +0 -524
- package/src/autoresearch/tools/run-experiment.ts +0 -475
- package/src/autoresearch/tools/update-notes.ts +0 -109
- package/src/commands/rlm.ts +0 -19
- package/src/commands/team.ts +0 -238
- package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +0 -42
- package/src/defaults/gjc/skills/team/SKILL.md +0 -467
- package/src/gjc-runtime/team-gc.ts +0 -49
- package/src/gjc-runtime/team-launch.ts +0 -404
- package/src/gjc-runtime/team-notify.ts +0 -326
- package/src/gjc-runtime/team-runtime.ts +0 -5923
- package/src/gjc-runtime/team-store.ts +0 -878
- package/src/gjc-runtime/team-worker-heartbeat.ts +0 -149
- package/src/gjc-runtime/team-worker-memory-guard.ts +0 -363
- package/src/gjc-runtime/team-workers.ts +0 -1123
- package/src/prompts/system/rlm-report-command.md +0 -1
- package/src/prompts/system/rlm-research.md +0 -23
- package/src/rlm/index.ts +0 -365
- package/src/rlm/preset.ts +0 -76
- package/src/rlm/python-tool.ts +0 -68
package/src/session/artifacts.ts
CHANGED
|
@@ -34,6 +34,13 @@ function isSafeFilename(filename: string): boolean {
|
|
|
34
34
|
return /^[a-zA-Z0-9_.-]+$/.test(filename);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const MAX_ATTEMPT_ID_LENGTH = 128;
|
|
38
|
+
|
|
39
|
+
function assertSafeAttemptId(attemptId: string): void {
|
|
40
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(attemptId) || attemptId.length > MAX_ATTEMPT_ID_LENGTH)
|
|
41
|
+
throw new Error("Unsafe artifact attempt id");
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
function parseManagedOutputGeneration(value: Uint8Array, outputFilenamePrefix: string): ManagedOutputGeneration | null {
|
|
38
45
|
try {
|
|
39
46
|
const parsed = JSON.parse(Buffer.from(value).toString("utf8")) as Partial<ManagedOutputGeneration>;
|
|
@@ -99,13 +106,29 @@ export class ArtifactManager {
|
|
|
99
106
|
#dirCreated = false;
|
|
100
107
|
#initialized: Promise<void> | undefined;
|
|
101
108
|
#initializedComplete = false;
|
|
109
|
+
readonly #attemptId: string | undefined;
|
|
110
|
+
readonly #allocatedIds = new Set<string>();
|
|
111
|
+
readonly #retiredIds = new Set<string>();
|
|
112
|
+
#reservations = new Map<string, { start: number; count: number; names: string[] }>();
|
|
113
|
+
readonly #stagingParentStore: ManagedSessionDescendantStore | undefined;
|
|
114
|
+
readonly #stagingRelativePath: string | undefined;
|
|
102
115
|
|
|
103
116
|
/**
|
|
104
117
|
* @param dir Directory that will hold artifact files. Created lazily on first save.
|
|
105
118
|
*/
|
|
106
|
-
constructor(
|
|
119
|
+
constructor(
|
|
120
|
+
target: string | ManagedSessionDescendantStore,
|
|
121
|
+
options?: {
|
|
122
|
+
readonly attemptId?: string;
|
|
123
|
+
readonly stagingParentStore?: ManagedSessionDescendantStore;
|
|
124
|
+
readonly stagingRelativePath?: string;
|
|
125
|
+
},
|
|
126
|
+
) {
|
|
107
127
|
this.#store = typeof target === "string" ? undefined : target;
|
|
108
128
|
this.#dir = typeof target === "string" ? target : target.dir;
|
|
129
|
+
this.#attemptId = options?.attemptId;
|
|
130
|
+
this.#stagingParentStore = options?.stagingParentStore;
|
|
131
|
+
this.#stagingRelativePath = options?.stagingRelativePath;
|
|
109
132
|
}
|
|
110
133
|
|
|
111
134
|
/**
|
|
@@ -188,8 +211,12 @@ export class ArtifactManager {
|
|
|
188
211
|
}
|
|
189
212
|
|
|
190
213
|
async #publish(content: string, filename: string): Promise<void> {
|
|
191
|
-
|
|
192
|
-
|
|
214
|
+
await this.#publishBytes(Buffer.from(content, "utf8"), filename);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async #publishBytes(bytes: Uint8Array, filename: string): Promise<void> {
|
|
218
|
+
if (this.#store) await this.#store.publishNoReplace(filename, bytes);
|
|
219
|
+
else await publishManagedFileNoReplace(path.join(this.#dir, filename), bytes);
|
|
193
220
|
}
|
|
194
221
|
|
|
195
222
|
async replaceNamed(filename: string, content: string): Promise<void> {
|
|
@@ -289,8 +316,19 @@ export class ArtifactManager {
|
|
|
289
316
|
if (!/^[a-zA-Z0-9_.-]+$/.test(filename)) return false;
|
|
290
317
|
try {
|
|
291
318
|
if (this.#store) {
|
|
319
|
+
const beforePaths = new Set(await fs.readdir(this.#store.dir));
|
|
292
320
|
const staged = this.#store.readExpected(filename);
|
|
293
321
|
if (staged) this.#store.removeExpected(filename, staged);
|
|
322
|
+
// Capture only root names outside retained authority so foreign sibling
|
|
323
|
+
// placeholders cannot make rollback fail before exact removal runs.
|
|
324
|
+
for (const basename of await fs.readdir(this.#store.dir)) {
|
|
325
|
+
const nativeResidue = /^\.gjc-/u.test(basename);
|
|
326
|
+
const ownQuarantine = basename === `${filename}.removing` || basename.startsWith(`${filename}.`);
|
|
327
|
+
if (beforePaths.has(basename) || (!nativeResidue && !ownQuarantine)) continue;
|
|
328
|
+
const residuePath = path.join(this.#store.dir, basename);
|
|
329
|
+
const stat = await fs.lstat(residuePath);
|
|
330
|
+
await fs.rm(residuePath, { recursive: stat.isDirectory(), force: true });
|
|
331
|
+
}
|
|
294
332
|
} else {
|
|
295
333
|
await fs.unlink(path.join(this.#dir, filename));
|
|
296
334
|
}
|
|
@@ -326,7 +364,10 @@ export class ArtifactManager {
|
|
|
326
364
|
* Prefer `allocatePath` or `save`; this synchronous seam exists for pruning callbacks.
|
|
327
365
|
*/
|
|
328
366
|
allocateId(): number {
|
|
329
|
-
|
|
367
|
+
while (this.#retiredIds.has(String(this.#nextId))) this.#nextId++;
|
|
368
|
+
const id = this.#claimNextIdSync();
|
|
369
|
+
this.#allocatedIds.add(String(id));
|
|
370
|
+
return id;
|
|
330
371
|
}
|
|
331
372
|
|
|
332
373
|
/**
|
|
@@ -338,6 +379,7 @@ export class ArtifactManager {
|
|
|
338
379
|
async allocatePath(toolType: string): Promise<{ id: string; path?: string }> {
|
|
339
380
|
await this.#ensureDir();
|
|
340
381
|
const id = String(await this.#claimNextId());
|
|
382
|
+
this.#allocatedIds.add(id);
|
|
341
383
|
if (this.#store) return { id };
|
|
342
384
|
return { id, path: path.join(this.#dir, this.#filename(id, toolType)) };
|
|
343
385
|
}
|
|
@@ -350,6 +392,7 @@ export class ArtifactManager {
|
|
|
350
392
|
async save(content: string, toolType: string, options: ArtifactSaveOptions = {}): Promise<string> {
|
|
351
393
|
await this.#ensureDir();
|
|
352
394
|
const id = String(await this.#claimNextId());
|
|
395
|
+
this.#allocatedIds.add(id);
|
|
353
396
|
const maxBytes = Math.max(0, options.maxBytes ?? DEFAULT_ARTIFACT_MAX_BYTES);
|
|
354
397
|
const contentBytes = Buffer.byteLength(content, "utf-8");
|
|
355
398
|
const published =
|
|
@@ -378,11 +421,198 @@ export class ArtifactManager {
|
|
|
378
421
|
*/
|
|
379
422
|
async listFiles(): Promise<string[]> {
|
|
380
423
|
try {
|
|
424
|
+
if (this.#store) {
|
|
425
|
+
return this.#store
|
|
426
|
+
.captureTree("")
|
|
427
|
+
.entries.filter(entry => entry.kind === "file" && entry.relativePath.length > 0)
|
|
428
|
+
.map(entry => entry.relativePath);
|
|
429
|
+
}
|
|
381
430
|
return await fs.readdir(this.#dir);
|
|
382
431
|
} catch {
|
|
383
432
|
return [];
|
|
384
433
|
}
|
|
385
434
|
}
|
|
435
|
+
getAttemptId(): string | undefined {
|
|
436
|
+
return this.#attemptId;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
getAllocatedIds(): readonly string[] {
|
|
440
|
+
return [...this.#allocatedIds].sort((a, b) => Number(a) - Number(b));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Create an isolated artifact manager rooted below this manager's staging area. */
|
|
444
|
+
createAttemptStaging(attemptId: string): ArtifactManager {
|
|
445
|
+
assertSafeAttemptId(attemptId);
|
|
446
|
+
const stagingRelativePath = path.posix.join(".staging", attemptId);
|
|
447
|
+
const target = this.#store
|
|
448
|
+
? this.#store.deriveSubtree(stagingRelativePath)
|
|
449
|
+
: path.join(this.#dir, stagingRelativePath);
|
|
450
|
+
return new ArtifactManager(target, {
|
|
451
|
+
attemptId,
|
|
452
|
+
...(this.#store ? { stagingParentStore: this.#store, stagingRelativePath } : {}),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Reserve and publish a candidate's staged artifacts with a contiguous parent ID block. */
|
|
457
|
+
async commitAttemptStaging(
|
|
458
|
+
staging: ArtifactManager,
|
|
459
|
+
attemptId: string,
|
|
460
|
+
options?: { beforePublish?: (mapping: ReadonlyMap<string, string>) => Promise<void> | void },
|
|
461
|
+
): Promise<ReadonlyMap<string, string>> {
|
|
462
|
+
if (staging.#attemptId !== attemptId) throw new Error("Artifact staging ownership mismatch");
|
|
463
|
+
assertSafeAttemptId(attemptId);
|
|
464
|
+
await this.#ensureDir();
|
|
465
|
+
await staging.#ensureDir();
|
|
466
|
+
const ids = staging.getAllocatedIds();
|
|
467
|
+
const start = this.#nextId;
|
|
468
|
+
this.#nextId += ids.length;
|
|
469
|
+
const mapping = new Map<string, string>();
|
|
470
|
+
for (let index = 0; index < ids.length; index++) mapping.set(ids[index]!, String(start + index));
|
|
471
|
+
const frozenMap = new Map(mapping) as Map<string, string> & ReadonlyMap<string, string>;
|
|
472
|
+
Object.defineProperties(frozenMap, {
|
|
473
|
+
set: {
|
|
474
|
+
value: () => {
|
|
475
|
+
throw new Error("Artifact ID map is immutable");
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
delete: {
|
|
479
|
+
value: () => {
|
|
480
|
+
throw new Error("Artifact ID map is immutable");
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
clear: {
|
|
484
|
+
value: () => {
|
|
485
|
+
throw new Error("Artifact ID map is immutable");
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
Object.freeze(frozenMap);
|
|
490
|
+
const publishedNames: string[] = [];
|
|
491
|
+
try {
|
|
492
|
+
await options?.beforePublish?.(frozenMap);
|
|
493
|
+
for (const filename of await staging.listFiles()) {
|
|
494
|
+
if (/^\.artifact-id-\d+$/.test(filename)) continue;
|
|
495
|
+
const bytes = staging.#store
|
|
496
|
+
? staging.#store.readExpected(filename)?.bytes
|
|
497
|
+
: await fs.readFile(path.join(staging.#dir, filename));
|
|
498
|
+
if (!bytes) continue;
|
|
499
|
+
const match = filename.match(/^(\d+)(\..*)$/);
|
|
500
|
+
const mappedFilename = match ? `${mapping.get(match[1]!) ?? match[1]}${match[2]}` : filename;
|
|
501
|
+
await this.#publishBytes(bytes, mappedFilename);
|
|
502
|
+
publishedNames.push(mappedFilename);
|
|
503
|
+
}
|
|
504
|
+
this.#reservations.set(attemptId, { start, count: ids.length, names: [...publishedNames] });
|
|
505
|
+
await staging.discardAttemptStaging();
|
|
506
|
+
return frozenMap;
|
|
507
|
+
} catch (error) {
|
|
508
|
+
// Cleanup is best-effort, but a durable removal failure must never be silent: it leaves a
|
|
509
|
+
// published artifact behind under an id we are about to retire. Surface it alongside the
|
|
510
|
+
// original publication error rather than dropping the boolean.
|
|
511
|
+
const unremoved: string[] = [];
|
|
512
|
+
for (const filename of publishedNames.reverse())
|
|
513
|
+
if (!(await this.removeNamedBestEffort(filename))) unremoved.push(filename);
|
|
514
|
+
// Only rewind the tail when every published artifact was actually removed. If any removal
|
|
515
|
+
// failed, the file still occupies its id, so retire the whole block instead of handing the
|
|
516
|
+
// ids out again.
|
|
517
|
+
if (unremoved.length === 0 && this.#nextId === start + ids.length) this.#nextId = start;
|
|
518
|
+
else for (const id of mapping.values()) this.#retiredIds.add(id);
|
|
519
|
+
if (unremoved.length > 0)
|
|
520
|
+
throw new AggregateError(
|
|
521
|
+
[error, new Error(`Failed to roll back published artifacts: ${unremoved.join(", ")}`)],
|
|
522
|
+
"Attempt-staging publication failed and rollback left artifacts behind.",
|
|
523
|
+
);
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async rollbackLastAttemptCommit(attemptId?: string): Promise<void> {
|
|
528
|
+
if (attemptId === undefined) return;
|
|
529
|
+
const reservation = this.#reservations.get(attemptId);
|
|
530
|
+
if (!reservation) return;
|
|
531
|
+
const unremoved: string[] = [];
|
|
532
|
+
for (const filename of [...reservation.names].reverse())
|
|
533
|
+
if (!(await this.removeNamedBestEffort(filename))) unremoved.push(filename);
|
|
534
|
+
// Only rewind the tail when every published artifact was actually removed; otherwise retire the
|
|
535
|
+
// whole block so a leaked file's id can never be reallocated.
|
|
536
|
+
if (unremoved.length === 0 && this.#nextId === reservation.start + reservation.count)
|
|
537
|
+
this.#nextId = reservation.start;
|
|
538
|
+
else
|
|
539
|
+
for (let index = 0; index < reservation.count; index++)
|
|
540
|
+
this.#retiredIds.add(String(reservation.start + index));
|
|
541
|
+
this.#reservations.delete(attemptId);
|
|
542
|
+
// The reservation is always released so ids can never be reused, but a failed durable removal
|
|
543
|
+
// is reported rather than swallowed.
|
|
544
|
+
if (unremoved.length > 0) throw new Error(`Failed to roll back published artifacts: ${unremoved.join(", ")}`);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
finalizeLastAttemptCommit(attemptId?: string): void {
|
|
548
|
+
if (attemptId !== undefined) this.#reservations.delete(attemptId);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async discardAttemptStaging(): Promise<void> {
|
|
552
|
+
if (this.#store) {
|
|
553
|
+
const cleanupStore = this.#stagingParentStore ?? this.#store;
|
|
554
|
+
const cleanupPath = this.#stagingParentStore ? this.#stagingRelativePath! : "";
|
|
555
|
+
const parentCleanupPath = cleanupPath ? path.posix.dirname(cleanupPath) : "";
|
|
556
|
+
let parentBefore: ReturnType<ManagedSessionDescendantStore["captureTree"]> | undefined;
|
|
557
|
+
if (this.#stagingParentStore) {
|
|
558
|
+
try {
|
|
559
|
+
parentBefore = cleanupStore.captureTree(parentCleanupPath);
|
|
560
|
+
} catch {
|
|
561
|
+
parentBefore = undefined;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
const snapshot = cleanupStore.captureTree(cleanupPath);
|
|
566
|
+
cleanupStore.removeTreeExpected(cleanupPath, snapshot);
|
|
567
|
+
} catch (error) {
|
|
568
|
+
if (!(error instanceof Error && (error.message === "not_found" || error.message === "cleanup_pending")))
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
if (this.#stagingParentStore && parentBefore) {
|
|
572
|
+
try {
|
|
573
|
+
const after = cleanupStore.captureTree(parentCleanupPath);
|
|
574
|
+
const beforePaths = new Set(parentBefore.entries.map(entry => entry.relativePath));
|
|
575
|
+
for (const entry of after.entries) {
|
|
576
|
+
if (
|
|
577
|
+
entry.kind === "directory" &&
|
|
578
|
+
entry.relativePath.length > 0 &&
|
|
579
|
+
!beforePaths.has(entry.relativePath) &&
|
|
580
|
+
/\.removing$/u.test(path.posix.basename(entry.relativePath))
|
|
581
|
+
) {
|
|
582
|
+
await fs.rm(path.join(cleanupStore.dir, parentCleanupPath, entry.relativePath), {
|
|
583
|
+
recursive: true,
|
|
584
|
+
force: true,
|
|
585
|
+
});
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if (
|
|
589
|
+
entry.kind !== "file" ||
|
|
590
|
+
beforePaths.has(entry.relativePath) ||
|
|
591
|
+
!/^\\.gjc-(?:exact-unlink-placeholder|remove)-/u.test(path.posix.basename(entry.relativePath))
|
|
592
|
+
)
|
|
593
|
+
continue;
|
|
594
|
+
const relative = path.posix.join(parentCleanupPath, entry.relativePath);
|
|
595
|
+
const expected = cleanupStore.readExpected(relative);
|
|
596
|
+
if (expected) {
|
|
597
|
+
try {
|
|
598
|
+
cleanupStore.removeExpected(relative, expected);
|
|
599
|
+
} catch (cleanupError) {
|
|
600
|
+
if (!(cleanupError instanceof Error && cleanupError.message === "cleanup_pending"))
|
|
601
|
+
throw cleanupError;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
await fs.rm(path.join(cleanupStore.dir, relative), { force: true }).catch(() => undefined);
|
|
605
|
+
}
|
|
606
|
+
} catch {
|
|
607
|
+
// Retained cleanup evidence is safe to leave for a later maintenance pass.
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
this.#store.close();
|
|
611
|
+
} else {
|
|
612
|
+
await fs.rm(this.#dir, { recursive: true, force: true });
|
|
613
|
+
}
|
|
614
|
+
this.#dirCreated = false;
|
|
615
|
+
}
|
|
386
616
|
|
|
387
617
|
/** Persist exact UTF-8 text for heap-eviction rehydration. */
|
|
388
618
|
async publishExactText(text: string, options: ArtifactPublishOptions = {}): Promise<ArtifactPublishOutcome> {
|
|
@@ -176,6 +176,7 @@ function sameDirectoryTreeSnapshotAfterRename(
|
|
|
176
176
|
);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/** A managed move outcome that reports whether the mutation is known not to have committed. */
|
|
179
180
|
export class ManagedTreeMoveOutcomeError extends Error {
|
|
180
181
|
constructor(
|
|
181
182
|
message: string,
|
|
@@ -1391,14 +1392,21 @@ export class ManagedSessionDescendantStore {
|
|
|
1391
1392
|
try {
|
|
1392
1393
|
const directory = fs.opendirSync(this.#baseDir);
|
|
1393
1394
|
try {
|
|
1395
|
+
// The limit bounds receipt reconciliation work, so it counts receipts —
|
|
1396
|
+
// not every dirent in the directory. Counting all entries conflated
|
|
1397
|
+
// "this scope has too many receipts to reconcile" with "this directory
|
|
1398
|
+
// has many files", so a scope whose bulk was inert zero-byte remnants
|
|
1399
|
+
// aborted before examining a single receipt (observed: 50,003 dirents,
|
|
1400
|
+
// 0 receipts) and every mutation failed permanently. Unbounded receipt
|
|
1401
|
+
// growth is still rejected, which is the case the limit exists for.
|
|
1394
1402
|
let examined = 0;
|
|
1395
1403
|
for (let entry = directory.readSync(); entry; entry = directory.readSync()) {
|
|
1396
|
-
examined++;
|
|
1397
|
-
if (examined > REPLACEMENT_CLEANUP_RECEIPT_SCAN_LIMIT)
|
|
1398
|
-
throw new Error("managed_replace_cleanup_receipt_limit_exceeded");
|
|
1399
1404
|
const name = entry.name;
|
|
1400
1405
|
if (!name.startsWith(".gjc-replace-receipt-pending-") && !name.startsWith(".gjc-replace-cleanup-"))
|
|
1401
1406
|
continue;
|
|
1407
|
+
examined++;
|
|
1408
|
+
if (examined > REPLACEMENT_CLEANUP_RECEIPT_SCAN_LIMIT)
|
|
1409
|
+
throw new Error("managed_replace_cleanup_receipt_limit_exceeded");
|
|
1402
1410
|
if (name.startsWith(".gjc-replace-receipt-pending-")) {
|
|
1403
1411
|
this.#reconcilePendingReplacementReceipt(path.join(this.#baseDir, name));
|
|
1404
1412
|
continue;
|
|
@@ -1427,9 +1435,21 @@ export class ManagedSessionDescendantStore {
|
|
|
1427
1435
|
|
|
1428
1436
|
#beforeMutation(): void {
|
|
1429
1437
|
this.#assertBound();
|
|
1438
|
+
// Reaping is scheduled BEFORE reconciliation, not after. The receipt scan
|
|
1439
|
+
// throws `managed_replace_cleanup_receipt_limit_exceeded` once the bound
|
|
1440
|
+
// directory exceeds the dirent scan limit, and the only thing that brings a
|
|
1441
|
+
// directory back under that limit is this reaper. Scheduling it after the
|
|
1442
|
+
// scan made the recovery unreachable exactly when it was needed: a scope
|
|
1443
|
+
// that crossed the limit failed every mutation forever, so tool-output
|
|
1444
|
+
// eviction could never persist and its originals stayed resident in heap
|
|
1445
|
+
// until the non-disableable emergency compaction floor cut the session
|
|
1446
|
+
// (observed: 50,003 dirents, 47,043 of them zero-byte remnants, 55 failed
|
|
1447
|
+
// evictions and 9 heap compactions in one day). Reaping is throttled,
|
|
1448
|
+
// asynchronous, and never fails the triggering mutation, so hoisting it
|
|
1449
|
+
// cannot make a healthy scope worse.
|
|
1450
|
+
this.#scheduleRemnantReap();
|
|
1430
1451
|
this.#reconcileReplacementCleanupReceipts();
|
|
1431
1452
|
this.#assertBound();
|
|
1432
|
-
this.#scheduleRemnantReap();
|
|
1433
1453
|
}
|
|
1434
1454
|
|
|
1435
1455
|
/**
|
|
@@ -2371,6 +2391,73 @@ export class ManagedSessionDescendantStore {
|
|
|
2371
2391
|
return movedSnapshot.snapshot;
|
|
2372
2392
|
}
|
|
2373
2393
|
|
|
2394
|
+
/** Move one exact managed regular file to an absent destination through retained authority. */
|
|
2395
|
+
moveFileNoReplace(
|
|
2396
|
+
sourceRelativePath: string,
|
|
2397
|
+
destinationRelativePath: string,
|
|
2398
|
+
expected: ManagedFileSnapshot,
|
|
2399
|
+
options?: {
|
|
2400
|
+
/** Subtree store bound to the source's directory; keeps the verification read basename-scoped when this store lacks retained authority. */
|
|
2401
|
+
sourceStore: ManagedSessionDescendantStore;
|
|
2402
|
+
sourceStoreRelativePath: string;
|
|
2403
|
+
},
|
|
2404
|
+
): ManagedFileSnapshot {
|
|
2405
|
+
this.#assertBound();
|
|
2406
|
+
const sourceResolved = this.#resolve(sourceRelativePath);
|
|
2407
|
+
const destinationResolved = this.#resolve(destinationRelativePath);
|
|
2408
|
+
if (
|
|
2409
|
+
options &&
|
|
2410
|
+
path.resolve(options.sourceStore.#resolve(options.sourceStoreRelativePath)) !== path.resolve(sourceResolved)
|
|
2411
|
+
)
|
|
2412
|
+
throw new ManagedTreeMoveOutcomeError("artifact_source_changed", false);
|
|
2413
|
+
const source = options
|
|
2414
|
+
? options.sourceStore.readExpected(options.sourceStoreRelativePath)
|
|
2415
|
+
: this.readExpected(sourceRelativePath);
|
|
2416
|
+
if (
|
|
2417
|
+
!source ||
|
|
2418
|
+
!sameReplacementIdentity(source.identity, expected.identity) ||
|
|
2419
|
+
source.identity.sha256 !== expected.identity.sha256 ||
|
|
2420
|
+
!source.bytes.equals(expected.bytes)
|
|
2421
|
+
)
|
|
2422
|
+
throw new ManagedTreeMoveOutcomeError("artifact_source_changed", false);
|
|
2423
|
+
|
|
2424
|
+
const moved = this.#authority
|
|
2425
|
+
? this.#authority.renameManagedFileNoReplace(
|
|
2426
|
+
this.#relative(sourceResolved),
|
|
2427
|
+
this.#relative(destinationResolved),
|
|
2428
|
+
expected.identity.dev.toString(),
|
|
2429
|
+
expected.identity.ino.toString(),
|
|
2430
|
+
expected.identity.size.toString(),
|
|
2431
|
+
expected.identity.mtimeNs.toString(),
|
|
2432
|
+
expected.identity.ctimeNs.toString(),
|
|
2433
|
+
expected.identity.sha256,
|
|
2434
|
+
)
|
|
2435
|
+
: nativeSessionStorage().renameNoReplacePath(sourceResolved, destinationResolved);
|
|
2436
|
+
const outcome = classifyNativePublishOutcome(moved, this.#authority ? "retained_file" : "direct_rename");
|
|
2437
|
+
if (!outcome.ok)
|
|
2438
|
+
throw new ManagedTreeMoveOutcomeError(publishFailure(outcome).message, mayCleanCurrentStaging(outcome));
|
|
2439
|
+
|
|
2440
|
+
const movedSnapshot = this.readExpected(destinationRelativePath);
|
|
2441
|
+
if (
|
|
2442
|
+
!movedSnapshot ||
|
|
2443
|
+
!sameReplacementIdentity(movedSnapshot.identity, expected.identity) ||
|
|
2444
|
+
movedSnapshot.identity.sha256 !== expected.identity.sha256 ||
|
|
2445
|
+
!movedSnapshot.bytes.equals(expected.bytes)
|
|
2446
|
+
)
|
|
2447
|
+
throw new ManagedTreeMoveOutcomeError("artifact_destination_mismatch", false);
|
|
2448
|
+
if (!this.#authority) {
|
|
2449
|
+
try {
|
|
2450
|
+
fsyncDirectory(path.dirname(sourceResolved));
|
|
2451
|
+
if (path.dirname(sourceResolved) !== path.dirname(destinationResolved))
|
|
2452
|
+
fsyncDirectory(path.dirname(destinationResolved));
|
|
2453
|
+
} catch {
|
|
2454
|
+
throw new ManagedTreeMoveOutcomeError("managed_publish_fsync_failed", false);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
this.#assertBound();
|
|
2458
|
+
return movedSnapshot;
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2374
2461
|
removeTreeExpected(relativePath: string, expected: NativeDirectoryTreeSnapshot): void {
|
|
2375
2462
|
this.#beforeMutation();
|
|
2376
2463
|
this.#assertBound();
|
|
@@ -54,8 +54,9 @@ export interface ProviderSafetyStopHintSession {
|
|
|
54
54
|
/** Whether an assistant message terminated as a provider safety stop (typed or legacy-persisted). */
|
|
55
55
|
export function isProviderSafetyStop(message: AssistantMessage): boolean {
|
|
56
56
|
return (
|
|
57
|
-
message.
|
|
58
|
-
(message.
|
|
57
|
+
message.stopReason === "error" &&
|
|
58
|
+
(message.errorKind === "provider_safety_stop" ||
|
|
59
|
+
(message.errorMessage !== undefined && isLegacyProviderSafetyStopMessage(message.errorMessage)))
|
|
59
60
|
);
|
|
60
61
|
}
|
|
61
62
|
|