@gajae-code/coding-agent 0.12.1 → 0.12.4
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 +39 -0
- package/dist/types/config/settings-schema.d.ts +2 -2
- package/dist/types/gjc-runtime/ralplan-review-conflicts.d.ts +114 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -3
- package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +5 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -2
- package/dist/types/modes/components/irc-sidebar.d.ts +16 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +1 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +9 -0
- package/dist/types/modes/theme/theme.d.ts +3 -1
- package/dist/types/modes/types.d.ts +32 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -2
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +22 -3
- package/dist/types/task/index.d.ts +4 -0
- package/dist/types/task/output-manager.d.ts +1 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/tools/index.d.ts +2 -0
- package/package.json +8 -8
- package/scripts/dogfood-ralplan-review-conflicts.ts +272 -0
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-acp-conformance.ts +5 -0
- package/src/async/job-manager.ts +22 -20
- package/src/config/provider-ranking.ts +1 -0
- package/src/config/settings-schema.ts +2 -2
- package/src/debug/index.ts +42 -17
- package/src/defaults/gjc/skills/ralplan/SKILL.md +9 -7
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -5
- package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +2 -1
- package/src/gjc-runtime/launch-worktree.ts +103 -1
- package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
- package/src/gjc-runtime/ralplan-review-conflicts.ts +472 -0
- package/src/gjc-runtime/ralplan-runtime.ts +80 -10
- package/src/gjc-runtime/ultragoal-evidence.ts +110 -49
- package/src/gjc-runtime/ultragoal-runtime.ts +467 -22
- package/src/gjc-runtime/workflow-manifest.generated.json +29 -0
- package/src/gjc-runtime/workflow-manifest.ts +17 -2
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/modes/DESIGN.md +53 -0
- package/src/modes/components/irc-sidebar.ts +30 -1
- package/src/modes/components/tool-status-header.ts +0 -1
- package/src/modes/controllers/command-controller.ts +38 -34
- package/src/modes/controllers/event-controller.ts +27 -16
- package/src/modes/controllers/extension-ui-controller.ts +186 -34
- package/src/modes/controllers/selector-controller.ts +115 -22
- package/src/modes/interactive-mode.ts +189 -62
- package/src/modes/jobs-observer.ts +1 -1
- package/src/modes/theme/theme.ts +11 -9
- package/src/modes/types.ts +88 -0
- package/src/sdk/broker/lifecycle.ts +18 -13
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -2
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/session.ts +1 -0
- package/src/session/agent-session.ts +22 -1
- package/src/session/internal/managed-session-scope.ts +158 -107
- package/src/session/internal/managed-session-storage.ts +445 -57
- package/src/session/session-storage.ts +29 -44
- package/src/task/index.ts +257 -31
- package/src/task/output-manager.ts +24 -14
- package/src/task/receipt.ts +11 -1
- package/src/tools/index.ts +2 -0
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
verifyOwnerOnlyPathSecurity,
|
|
9
9
|
verifyOwnerOnlyPathSecurityExpected,
|
|
10
10
|
} from "@gajae-code/natives";
|
|
11
|
-
import { logger, pathIsWithin } from "@gajae-code/utils";
|
|
11
|
+
import { hasFsCode, logger, pathIsWithin } from "@gajae-code/utils";
|
|
12
12
|
import type { ResumeSessionIdentity } from "../session-manager";
|
|
13
13
|
import {
|
|
14
14
|
FileSessionStorage,
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
type ManagedFileSnapshot,
|
|
32
32
|
ManagedPublishError,
|
|
33
33
|
ManagedSessionDescendantStore,
|
|
34
|
+
ManagedSessionSecurityError,
|
|
34
35
|
type ManagedSessionSecurityPolicy,
|
|
35
36
|
type ManagedStorageLock,
|
|
36
37
|
managedSecurityFailureClassification,
|
|
@@ -127,7 +128,7 @@ function configuredRootPath(scope: ManagedScope): string {
|
|
|
127
128
|
const canonical = fs.realpathSync.native(candidate);
|
|
128
129
|
return suffix.length === 0 ? canonical : path.join(canonical, ...suffix);
|
|
129
130
|
} catch (error) {
|
|
130
|
-
if ((error
|
|
131
|
+
if (!hasFsCode(error, "ENOENT")) throw error;
|
|
131
132
|
const parent = path.dirname(candidate);
|
|
132
133
|
if (parent === candidate) throw new Error("Configured managed root is unavailable.");
|
|
133
134
|
suffix.unshift(path.basename(candidate));
|
|
@@ -177,6 +178,23 @@ export type ManagedScopeResolution =
|
|
|
177
178
|
cause?: { readonly classification: string; readonly diagnostic?: string };
|
|
178
179
|
};
|
|
179
180
|
|
|
181
|
+
function managedScopeFailureCause(error: unknown): { readonly classification: string } {
|
|
182
|
+
return { classification: managedSecurityFailureClassification(error) ?? "binding_invalid" };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const managedScopeFailureCodes = new Set([
|
|
186
|
+
"atomic_unavailable",
|
|
187
|
+
"invalid_request",
|
|
188
|
+
"durability_failed",
|
|
189
|
+
"durability_not_provable",
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
function managedScopeFailureMessage(error: unknown, fallback: string): string {
|
|
193
|
+
const classification = managedSecurityFailureClassification(error);
|
|
194
|
+
if (classification) return classification;
|
|
195
|
+
return error instanceof Error && managedScopeFailureCodes.has(error.message) ? error.message : fallback;
|
|
196
|
+
}
|
|
197
|
+
|
|
180
198
|
export interface ManagedCandidate {
|
|
181
199
|
sessionId: string;
|
|
182
200
|
path: string;
|
|
@@ -277,7 +295,7 @@ function identityFor(cwd: string): NativeIdentity {
|
|
|
277
295
|
function verifyExistingManagedScopeDirectory(pathname: string) {
|
|
278
296
|
if (process.platform !== "win32") return verifyOwnerOnlyPathSecurity(pathname, "directory");
|
|
279
297
|
const expected = fs.lstatSync(pathname, { bigint: true });
|
|
280
|
-
if (!expected.isDirectory() || expected.isSymbolicLink()) throw new
|
|
298
|
+
if (!expected.isDirectory() || expected.isSymbolicLink()) throw new ManagedSessionSecurityError("reparse_point");
|
|
281
299
|
const verified = verifyOwnerOnlyPathSecurityExpected(pathname, "directory", expected.dev, expected.ino);
|
|
282
300
|
const current = fs.lstatSync(pathname, { bigint: true });
|
|
283
301
|
if (
|
|
@@ -286,7 +304,7 @@ function verifyExistingManagedScopeDirectory(pathname: string) {
|
|
|
286
304
|
current.dev !== expected.dev ||
|
|
287
305
|
current.ino !== expected.ino
|
|
288
306
|
)
|
|
289
|
-
throw new
|
|
307
|
+
throw new ManagedSessionSecurityError("identity_mismatch");
|
|
290
308
|
return verified;
|
|
291
309
|
}
|
|
292
310
|
|
|
@@ -405,8 +423,18 @@ function validateExistingBinding(scope: ManagedScope): ManagedScopeResolution |
|
|
|
405
423
|
try {
|
|
406
424
|
raw = captureManagedFileNoFollow(bindingPath).bytes.toString("utf8");
|
|
407
425
|
} catch (error) {
|
|
408
|
-
if ((error
|
|
409
|
-
|
|
426
|
+
if (hasFsCode(error, "ENOENT")) return undefined;
|
|
427
|
+
const classification = hasFsCode(error, "EACCES")
|
|
428
|
+
? "EACCES"
|
|
429
|
+
: hasFsCode(error, "EPERM")
|
|
430
|
+
? "EPERM"
|
|
431
|
+
: "binding_invalid";
|
|
432
|
+
return {
|
|
433
|
+
kind: "error",
|
|
434
|
+
code: "binding_invalid",
|
|
435
|
+
message: "The managed scope binding is invalid JSON.",
|
|
436
|
+
cause: { classification },
|
|
437
|
+
};
|
|
410
438
|
}
|
|
411
439
|
return validateBindingRaw(scope, raw);
|
|
412
440
|
}
|
|
@@ -429,14 +457,16 @@ function resolveManagedScopeInternal(
|
|
|
429
457
|
kind: "error",
|
|
430
458
|
code: "sessions_root_unavailable",
|
|
431
459
|
message: "The sessions root is not a safe directory.",
|
|
460
|
+
cause: { classification: "reparse_point" },
|
|
432
461
|
};
|
|
433
462
|
}
|
|
434
463
|
} catch (error) {
|
|
435
|
-
if ((error
|
|
464
|
+
if (!hasFsCode(error, "ENOENT")) {
|
|
436
465
|
return {
|
|
437
466
|
kind: "error",
|
|
438
467
|
code: "sessions_root_unavailable",
|
|
439
468
|
message: "The sessions root could not be inspected.",
|
|
469
|
+
cause: managedScopeFailureCause(error),
|
|
440
470
|
};
|
|
441
471
|
}
|
|
442
472
|
}
|
|
@@ -462,21 +492,28 @@ function resolveManagedScopeInternal(
|
|
|
462
492
|
kind: "error",
|
|
463
493
|
code: "sessions_root_unavailable",
|
|
464
494
|
message: "The sessions root is not a safe directory.",
|
|
495
|
+
cause: { classification: "reparse_point" },
|
|
465
496
|
};
|
|
466
497
|
}
|
|
467
498
|
} catch (error) {
|
|
468
|
-
if ((error
|
|
499
|
+
if (!hasFsCode(error, "ENOENT")) {
|
|
469
500
|
return {
|
|
470
501
|
kind: "error",
|
|
471
502
|
code: "sessions_root_unavailable",
|
|
472
503
|
message: "The sessions root could not be inspected.",
|
|
504
|
+
cause: managedScopeFailureCause(error),
|
|
473
505
|
};
|
|
474
506
|
}
|
|
475
507
|
}
|
|
476
508
|
try {
|
|
477
509
|
const directory = fs.lstatSync(scope.directoryPath);
|
|
478
510
|
if (!directory.isDirectory() || directory.isSymbolicLink()) {
|
|
479
|
-
return {
|
|
511
|
+
return {
|
|
512
|
+
kind: "error",
|
|
513
|
+
code: "binding_invalid",
|
|
514
|
+
message: "The managed scope path is not a safe directory.",
|
|
515
|
+
cause: { classification: "reparse_point" },
|
|
516
|
+
};
|
|
480
517
|
}
|
|
481
518
|
const security = validateNativeSecurityResult(
|
|
482
519
|
verifyExistingManagedScopeDirectory(scope.directoryPath),
|
|
@@ -488,11 +525,17 @@ function resolveManagedScopeInternal(
|
|
|
488
525
|
kind: "error",
|
|
489
526
|
code: "binding_invalid",
|
|
490
527
|
message: "The managed scope security could not be verified.",
|
|
528
|
+
cause: { classification: security.code ?? "acl_verify_failed" },
|
|
491
529
|
};
|
|
492
530
|
}
|
|
493
531
|
} catch (error) {
|
|
494
|
-
if ((error
|
|
495
|
-
return {
|
|
532
|
+
if (!hasFsCode(error, "ENOENT")) {
|
|
533
|
+
return {
|
|
534
|
+
kind: "error",
|
|
535
|
+
code: "binding_invalid",
|
|
536
|
+
message: "The managed scope path could not be inspected.",
|
|
537
|
+
cause: managedScopeFailureCause(error),
|
|
538
|
+
};
|
|
496
539
|
}
|
|
497
540
|
}
|
|
498
541
|
return validateExistingBinding(scope) ?? { kind: "resolved", scope };
|
|
@@ -557,7 +600,7 @@ function fsyncManagedParent(pathname: string): void {
|
|
|
557
600
|
try {
|
|
558
601
|
descriptor = fs.openSync(parent, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
|
|
559
602
|
} catch (error) {
|
|
560
|
-
if ((error
|
|
603
|
+
if (hasFsCode(error, "ENOENT") && path.dirname(parent) !== parent) {
|
|
561
604
|
parent = path.dirname(parent);
|
|
562
605
|
continue;
|
|
563
606
|
}
|
|
@@ -779,7 +822,7 @@ export async function ensureManagedScope(
|
|
|
779
822
|
try {
|
|
780
823
|
await publishManagedFileNoReplace(bindingPath, new TextEncoder().encode(binding), undefined, root, policy);
|
|
781
824
|
} catch (error) {
|
|
782
|
-
if ((error
|
|
825
|
+
if (!(error instanceof Error) || error.message !== "destination_conflict") throw error;
|
|
783
826
|
bindingCollision = true;
|
|
784
827
|
}
|
|
785
828
|
const validated = validateExistingBinding(scope);
|
|
@@ -788,14 +831,14 @@ export async function ensureManagedScope(
|
|
|
788
831
|
|
|
789
832
|
const preparedDirectory = fs.lstatSync(scope.directoryPath, { bigint: true });
|
|
790
833
|
if (!preparedDirectory.isDirectory() || preparedDirectory.isSymbolicLink())
|
|
791
|
-
throw new
|
|
834
|
+
throw new ManagedSessionSecurityError("reparse_point");
|
|
792
835
|
managedDirectoryIdentities.set(scope, { dev: preparedDirectory.dev, ino: preparedDirectory.ino });
|
|
793
836
|
return { kind: "resolved", scope };
|
|
794
837
|
} catch (error) {
|
|
795
838
|
const publication = error instanceof ManagedPublishError ? error : undefined;
|
|
796
839
|
const message =
|
|
797
840
|
publication?.classification ??
|
|
798
|
-
(error
|
|
841
|
+
managedScopeFailureMessage(error, "The managed scope could not be initialized.");
|
|
799
842
|
const code =
|
|
800
843
|
message === "atomic_unavailable" ||
|
|
801
844
|
message === "invalid_request" ||
|
|
@@ -803,14 +846,13 @@ export async function ensureManagedScope(
|
|
|
803
846
|
message === "durability_not_provable"
|
|
804
847
|
? message
|
|
805
848
|
: "binding_invalid";
|
|
806
|
-
|
|
807
849
|
return {
|
|
808
850
|
kind: "error",
|
|
809
851
|
code,
|
|
810
852
|
message,
|
|
811
|
-
|
|
812
|
-
? {
|
|
813
|
-
:
|
|
853
|
+
cause: publication
|
|
854
|
+
? { classification: publication.classification, diagnostic: publication.diagnostic }
|
|
855
|
+
: managedScopeFailureCause(error),
|
|
814
856
|
};
|
|
815
857
|
}
|
|
816
858
|
}
|
|
@@ -946,7 +988,7 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
946
988
|
try {
|
|
947
989
|
store.publishNoReplaceSync(MANAGED_SESSION_BINDING_FILE, binding);
|
|
948
990
|
} catch (error) {
|
|
949
|
-
if ((error
|
|
991
|
+
if (!(error instanceof Error) || error.message !== "destination_conflict") throw error;
|
|
950
992
|
}
|
|
951
993
|
stage = "binding_read";
|
|
952
994
|
const capturedBinding = store.readExpected(MANAGED_SESSION_BINDING_FILE);
|
|
@@ -984,8 +1026,7 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
984
1026
|
} catch (error) {
|
|
985
1027
|
const publication = error instanceof ManagedPublishError ? error : undefined;
|
|
986
1028
|
const message =
|
|
987
|
-
publication?.classification ??
|
|
988
|
-
(error instanceof Error ? error.message : "Managed write protocol setup failed.");
|
|
1029
|
+
publication?.classification ?? managedScopeFailureMessage(error, "Managed write protocol setup failed.");
|
|
989
1030
|
const code =
|
|
990
1031
|
message === "atomic_unavailable" ||
|
|
991
1032
|
message === "invalid_request" ||
|
|
@@ -993,14 +1034,14 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
993
1034
|
message === "durability_not_provable"
|
|
994
1035
|
? message
|
|
995
1036
|
: "binding_invalid";
|
|
996
|
-
|
|
1037
|
+
|
|
997
1038
|
return {
|
|
998
1039
|
kind: "error",
|
|
999
1040
|
code,
|
|
1000
1041
|
message,
|
|
1001
1042
|
cause: publication
|
|
1002
1043
|
? { classification: publication.classification, diagnostic: publication.diagnostic }
|
|
1003
|
-
: {
|
|
1044
|
+
: { ...managedScopeFailureCause(error), diagnostic: `prepare:${stage}` },
|
|
1004
1045
|
};
|
|
1005
1046
|
}
|
|
1006
1047
|
}
|
|
@@ -1763,6 +1804,14 @@ function sameArtifactTreeSnapshot(left: NativeDirectoryTreeSnapshot, right: Nati
|
|
|
1763
1804
|
return leftEntries.every((entry, index) => entry === rightEntries[index]);
|
|
1764
1805
|
}
|
|
1765
1806
|
|
|
1807
|
+
function artifactTreePayloadAbsent(snapshot: NativeDirectoryTreeSnapshot): boolean {
|
|
1808
|
+
return (
|
|
1809
|
+
snapshot.entries.length === 1 &&
|
|
1810
|
+
snapshot.entries[0]?.relativePath === "" &&
|
|
1811
|
+
snapshot.entries[0].kind === "directory"
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1766
1815
|
function assertRetainedArtifactsAuthority(pending: CleanupReceipt): void {
|
|
1767
1816
|
if (!pending.detachedArtifactsPath) return;
|
|
1768
1817
|
if (!pending.expectedArtifactsIdentity || !pending.expectedArtifactsTree) throw new Error("durability_failed");
|
|
@@ -1773,10 +1822,12 @@ function assertRetainedArtifactsAuthority(pending: CleanupReceipt): void {
|
|
|
1773
1822
|
throw new Error("durability_failed");
|
|
1774
1823
|
}
|
|
1775
1824
|
const observed = artifactIdentityAt(pending.detachedArtifactsPath);
|
|
1825
|
+
const observedTree = snapshotArtifactTree(pending.detachedArtifactsPath);
|
|
1776
1826
|
if (
|
|
1777
1827
|
!observed ||
|
|
1778
1828
|
!sameArtifactRootIdentity(observed, pending.expectedArtifactsIdentity) ||
|
|
1779
|
-
!sameArtifactTreeSnapshot(
|
|
1829
|
+
(!sameArtifactTreeSnapshot(observedTree, pending.expectedArtifactsTree) &&
|
|
1830
|
+
!artifactTreePayloadAbsent(observedTree))
|
|
1780
1831
|
)
|
|
1781
1832
|
throw new Error("binding_invalid");
|
|
1782
1833
|
}
|
|
@@ -2002,6 +2053,15 @@ function snapshotArtifactTree(pathname: string): NativeDirectoryTreeSnapshot {
|
|
|
2002
2053
|
return result.snapshot;
|
|
2003
2054
|
}
|
|
2004
2055
|
|
|
2056
|
+
function retainedArtifactPayloadAbsent(pathname: string): boolean {
|
|
2057
|
+
try {
|
|
2058
|
+
return artifactTreePayloadAbsent(snapshotArtifactTree(pathname));
|
|
2059
|
+
} catch (error) {
|
|
2060
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return true;
|
|
2061
|
+
throw error;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2005
2065
|
function nextCleanupReceipt(target: RetiredTarget, pending: CleanupReceipt | undefined): CleanupReceipt {
|
|
2006
2066
|
const attempt = (pending?.attempt ?? 0) + 1;
|
|
2007
2067
|
const directory = path.dirname(target.path);
|
|
@@ -2962,53 +3022,44 @@ export async function reconcileManagedTombstones(
|
|
|
2962
3022
|
}
|
|
2963
3023
|
if (deletion.kind === "cleanup_pending") {
|
|
2964
3024
|
const retry = nextCleanupReceipt(target, active);
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
? deletion.detachedArtifactsPath
|
|
2979
|
-
: active.detachedArtifactsPath,
|
|
3025
|
+
const pendingEvidence = cleanupPendingEvidence(retry, active, deletion);
|
|
3026
|
+
await publishCleanupPending(scope, tombstone, pendingEvidence, lock);
|
|
3027
|
+
if (deletion.phase === "artifacts") {
|
|
3028
|
+
if (!retainedArtifactPayloadAbsent(deletion.detachedArtifactsPath)) continue;
|
|
3029
|
+
await publishCleanupArtifactsRemoved(scope, tombstone, pendingEvidence, lock);
|
|
3030
|
+
deletion = await new FileSessionStorage().deleteSessionVerified({
|
|
3031
|
+
sessionsRoot: scope.sessionsRoot,
|
|
3032
|
+
transcriptPath: target.path,
|
|
3033
|
+
sessionId: target.sessionId,
|
|
3034
|
+
cwd: target.cwd,
|
|
3035
|
+
transcriptIdentity: target.identity,
|
|
3036
|
+
plannedArtifactsPath: pendingEvidence.plannedArtifactsPath,
|
|
3037
|
+
plannedTranscriptPath: pendingEvidence.plannedTranscriptPath,
|
|
2980
3038
|
detachedTranscriptPath:
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
deletion.
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
deletion.phase === "transcript"
|
|
3006
|
-
? deletion.retainedUnknownPath
|
|
3007
|
-
: active.retainedTranscriptUnknownPath,
|
|
3008
|
-
},
|
|
3009
|
-
lock,
|
|
3010
|
-
);
|
|
3011
|
-
continue;
|
|
3039
|
+
pendingEvidence.detachedTranscriptPath ?? observedPending?.detachedTranscriptPath,
|
|
3040
|
+
retainedArtifactsSuccessorPath: pendingEvidence.retainedArtifactsSuccessorPath,
|
|
3041
|
+
retainedArtifactsPlaceholderPath: pendingEvidence.retainedArtifactsPlaceholderPath,
|
|
3042
|
+
retainedArtifactsUnknownPath: pendingEvidence.retainedArtifactsUnknownPath,
|
|
3043
|
+
retainedTranscriptSuccessorPath: pendingEvidence.retainedTranscriptSuccessorPath,
|
|
3044
|
+
retainedTranscriptPlaceholderPath: pendingEvidence.retainedTranscriptPlaceholderPath,
|
|
3045
|
+
retainedTranscriptUnknownPath: pendingEvidence.retainedTranscriptUnknownPath,
|
|
3046
|
+
artifactsRemoved: true,
|
|
3047
|
+
});
|
|
3048
|
+
if (deletion.kind === "cleanup_pending") {
|
|
3049
|
+
if (
|
|
3050
|
+
deletion.phase !== "transcript" ||
|
|
3051
|
+
deletion.detachedTranscriptPath !== pendingEvidence.plannedTranscriptPath
|
|
3052
|
+
)
|
|
3053
|
+
throw new Error("durability_failed");
|
|
3054
|
+
const followup = nextCleanupReceipt(target, pendingEvidence);
|
|
3055
|
+
await publishCleanupPending(
|
|
3056
|
+
scope,
|
|
3057
|
+
tombstone,
|
|
3058
|
+
cleanupPendingEvidence(followup, pendingEvidence, deletion),
|
|
3059
|
+
lock,
|
|
3060
|
+
);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3012
3063
|
}
|
|
3013
3064
|
fsyncManagedParent(target.path);
|
|
3014
3065
|
await publishCleanupCompleted(scope, tombstone, target, lock);
|
|
@@ -3022,7 +3073,7 @@ export async function reconcileManagedTombstones(
|
|
|
3022
3073
|
}
|
|
3023
3074
|
}
|
|
3024
3075
|
} finally {
|
|
3025
|
-
if (lock) await lock.release()
|
|
3076
|
+
if (lock) await lock.release();
|
|
3026
3077
|
}
|
|
3027
3078
|
}
|
|
3028
3079
|
}
|
|
@@ -3051,8 +3102,7 @@ export async function prepareManagedSessionScopeForWrite(
|
|
|
3051
3102
|
} catch (error) {
|
|
3052
3103
|
const publication = error instanceof ManagedPublishError ? error : undefined;
|
|
3053
3104
|
const message =
|
|
3054
|
-
publication?.classification ??
|
|
3055
|
-
(error instanceof Error ? error.message : "Managed write protocol setup failed.");
|
|
3105
|
+
publication?.classification ?? managedScopeFailureMessage(error, "Managed write protocol setup failed.");
|
|
3056
3106
|
const code =
|
|
3057
3107
|
message === "atomic_unavailable" ||
|
|
3058
3108
|
message === "invalid_request" ||
|
|
@@ -3064,9 +3114,9 @@ export async function prepareManagedSessionScopeForWrite(
|
|
|
3064
3114
|
kind: "error",
|
|
3065
3115
|
code,
|
|
3066
3116
|
message,
|
|
3067
|
-
|
|
3068
|
-
? {
|
|
3069
|
-
:
|
|
3117
|
+
cause: publication
|
|
3118
|
+
? { classification: publication.classification, diagnostic: publication.diagnostic }
|
|
3119
|
+
: managedScopeFailureCause(error),
|
|
3070
3120
|
};
|
|
3071
3121
|
}
|
|
3072
3122
|
}
|
|
@@ -3396,7 +3446,7 @@ export async function openManagedCandidateForWrite(
|
|
|
3396
3446
|
: "Managed migration failed.",
|
|
3397
3447
|
};
|
|
3398
3448
|
} finally {
|
|
3399
|
-
if (lock) await lock.release()
|
|
3449
|
+
if (lock) await lock.release();
|
|
3400
3450
|
}
|
|
3401
3451
|
}
|
|
3402
3452
|
|
|
@@ -3548,51 +3598,52 @@ export async function deleteManagedSessionCandidate(
|
|
|
3548
3598
|
)
|
|
3549
3599
|
throw new Error("durability_failed");
|
|
3550
3600
|
const retry = nextCleanupReceipt(target, active);
|
|
3551
|
-
|
|
3601
|
+
const pendingEvidence = cleanupPendingEvidence(retry, active, deletion);
|
|
3602
|
+
await publishCleanupPending(scope, tombstone, pendingEvidence, lock);
|
|
3552
3603
|
if (deletion.phase === "artifacts") {
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3604
|
+
if (!retainedArtifactPayloadAbsent(deletion.detachedArtifactsPath))
|
|
3605
|
+
return {
|
|
3606
|
+
kind: "cleanup_pending",
|
|
3607
|
+
tombstonePath: tombstone,
|
|
3608
|
+
phase: deletion.phase,
|
|
3609
|
+
message: "Exact cleanup remains pending because descriptor-bound final deletion is unavailable.",
|
|
3610
|
+
};
|
|
3611
|
+
await publishCleanupArtifactsRemoved(scope, tombstone, pendingEvidence, lock);
|
|
3556
3612
|
deletion = await new FileSessionStorage().deleteSessionVerified({
|
|
3557
3613
|
sessionsRoot: scope.sessionsRoot,
|
|
3558
3614
|
transcriptPath: target.path,
|
|
3559
3615
|
sessionId: target.sessionId,
|
|
3560
3616
|
cwd: target.cwd,
|
|
3561
3617
|
transcriptIdentity: target.identity,
|
|
3562
|
-
plannedArtifactsPath:
|
|
3563
|
-
plannedTranscriptPath:
|
|
3564
|
-
detachedTranscriptPath:
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3618
|
+
plannedArtifactsPath: pendingEvidence.plannedArtifactsPath,
|
|
3619
|
+
plannedTranscriptPath: pendingEvidence.plannedTranscriptPath,
|
|
3620
|
+
detachedTranscriptPath:
|
|
3621
|
+
pendingEvidence.detachedTranscriptPath ?? observedPending?.detachedTranscriptPath,
|
|
3622
|
+
retainedArtifactsSuccessorPath: pendingEvidence.retainedArtifactsSuccessorPath,
|
|
3623
|
+
retainedArtifactsPlaceholderPath: pendingEvidence.retainedArtifactsPlaceholderPath,
|
|
3624
|
+
retainedArtifactsUnknownPath: pendingEvidence.retainedArtifactsUnknownPath,
|
|
3625
|
+
retainedTranscriptSuccessorPath: pendingEvidence.retainedTranscriptSuccessorPath,
|
|
3626
|
+
retainedTranscriptPlaceholderPath: pendingEvidence.retainedTranscriptPlaceholderPath,
|
|
3627
|
+
retainedTranscriptUnknownPath: pendingEvidence.retainedTranscriptUnknownPath,
|
|
3571
3628
|
artifactsRemoved: true,
|
|
3572
3629
|
});
|
|
3573
3630
|
if (deletion.kind === "cleanup_pending") {
|
|
3574
|
-
|
|
3631
|
+
if (
|
|
3632
|
+
deletion.phase !== "transcript" ||
|
|
3633
|
+
deletion.detachedTranscriptPath !== pendingEvidence.plannedTranscriptPath
|
|
3634
|
+
)
|
|
3635
|
+
throw new Error("durability_failed");
|
|
3636
|
+
const followup = nextCleanupReceipt(target, pendingEvidence);
|
|
3575
3637
|
await publishCleanupPending(
|
|
3576
3638
|
scope,
|
|
3577
3639
|
tombstone,
|
|
3578
|
-
cleanupPendingEvidence(followup,
|
|
3640
|
+
cleanupPendingEvidence(followup, pendingEvidence, deletion),
|
|
3579
3641
|
lock,
|
|
3580
3642
|
);
|
|
3581
|
-
if (
|
|
3582
|
-
deletion.phase !== "transcript" ||
|
|
3583
|
-
deletion.detachedTranscriptPath !== active.plannedTranscriptPath
|
|
3584
|
-
)
|
|
3585
|
-
return {
|
|
3586
|
-
kind: "cleanup_pending",
|
|
3587
|
-
tombstonePath: tombstone,
|
|
3588
|
-
phase: deletion.phase,
|
|
3589
|
-
message:
|
|
3590
|
-
"Exact cleanup remains pending because descriptor-bound final deletion is unavailable.",
|
|
3591
|
-
};
|
|
3592
3643
|
}
|
|
3593
3644
|
}
|
|
3594
|
-
// A
|
|
3595
|
-
//
|
|
3645
|
+
// A retained transcript quarantine proves canonical absence and remains
|
|
3646
|
+
// identity-bound in the durable pending receipt.
|
|
3596
3647
|
}
|
|
3597
3648
|
fsyncManagedParent(target.path);
|
|
3598
3649
|
await publishCleanupCompleted(scope, tombstone, target, lock);
|
|
@@ -3602,6 +3653,6 @@ export async function deleteManagedSessionCandidate(
|
|
|
3602
3653
|
const code = expectedFailure(error);
|
|
3603
3654
|
return { kind: "error", code, message: error instanceof Error ? error.message : "Managed deletion failed." };
|
|
3604
3655
|
} finally {
|
|
3605
|
-
if (lock) await lock.release()
|
|
3656
|
+
if (lock) await lock.release();
|
|
3606
3657
|
}
|
|
3607
3658
|
}
|