@gajae-code/coding-agent 0.15.6 → 0.16.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 +59 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +13 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/config/autorouting-tier-map.d.ts +24 -0
- package/dist/types/config/file-lock.d.ts +5 -0
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +10 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +77 -1
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/state-version.d.ts +4 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/index.d.ts +6 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +4 -3
- package/dist/types/sdk/cli/session-cli.d.ts +18 -2
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/session-runtime.d.ts +52 -0
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/session/agent-session.d.ts +5 -0
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +65 -0
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +4 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +6 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +77 -11
- package/src/commands/launch.ts +86 -18
- package/src/commands/sdk.ts +113 -21
- package/src/config/autorouting-tier-map.ts +12 -0
- package/src/config/file-lock.ts +158 -83
- package/src/config/model-preset-registry.ts +1 -5
- package/src/config/model-profiles.ts +5 -5
- package/src/config/model-registry.ts +23 -0
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +8 -1
- package/src/config/settings.ts +5 -0
- package/src/coordinator-mcp/projection-scan.ts +21 -0
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +94 -20
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +79 -6
- package/src/gjc-runtime/session-state-sidecar.ts +929 -23
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/main.ts +57 -2
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +605 -156
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +23 -9
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/input-controller.ts +28 -0
- package/src/modes/controllers/selector-controller.ts +191 -96
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/ui-language.ts +49 -2
- package/src/prompts/system/master-mode.md +28 -0
- package/src/sdk/broker/broker.ts +1636 -26
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +5 -0
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +186 -41
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +548 -0
- package/src/sdk/broker/state-version.ts +26 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +35 -4
- package/src/sdk/bus/index.ts +51 -4
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +11 -8
- package/src/sdk/cli/session-cli.ts +264 -5
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/host/control/dispatch.ts +99 -38
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/host.ts +27 -2
- package/src/sdk/host/session-runtime.test.ts +210 -6
- package/src/sdk/host/session-runtime.ts +291 -49
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +10 -2
- package/src/sdk/protocol/operation-inventory.generated.json +51 -0
- package/src/sdk/protocol/operation-registry.ts +13 -3
- package/src/sdk/router/session-router.ts +67 -59
- package/src/sdk/session.ts +357 -155
- package/src/sdk/startup-capability.ts +1 -0
- package/src/session/agent-session.ts +640 -103
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/session-manager.ts +337 -24
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/telemetry/events.ts +1138 -12
- package/src/tools/bash.ts +256 -42
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +4 -0
package/src/config/file-lock.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
|
-
import type { BigIntStats
|
|
2
|
+
import type { BigIntStats } from "node:fs";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import type { NativeDirectoryTreeResult, NativeExactUnlinkResult } from "@gajae-code/natives";
|
|
6
|
-
import { renameNoReplacePathAsync } from "@gajae-code/natives";
|
|
5
|
+
import type { NativeDirectoryTreeResult, NativeExactUnlinkResult, NativeNoReplaceResult } from "@gajae-code/natives";
|
|
6
|
+
import { renameDirectoryNoReplacePathAsync, renameNoReplacePathAsync } from "@gajae-code/natives";
|
|
7
7
|
import { isEnoent } from "@gajae-code/utils/fs-error";
|
|
8
8
|
import { nativeProcessBindings } from "@gajae-code/utils/native-process";
|
|
9
9
|
|
|
@@ -49,6 +49,10 @@ type LockInfo = FileLockOwnerToken;
|
|
|
49
49
|
|
|
50
50
|
export const FileLockTestHooks: {
|
|
51
51
|
afterParentMkdir?: (lockPath: string) => void | Promise<void>;
|
|
52
|
+
nativePublicationBindings?: () => {
|
|
53
|
+
renameNoReplacePathAsync: typeof renameNoReplacePathAsync;
|
|
54
|
+
renameDirectoryNoReplacePathAsync: typeof renameDirectoryNoReplacePathAsync;
|
|
55
|
+
};
|
|
52
56
|
nativeQuarantineBindings?: () => NativeFileLockBindings;
|
|
53
57
|
} = {};
|
|
54
58
|
|
|
@@ -423,6 +427,118 @@ async function ensureLockParent(directory: string): Promise<void> {
|
|
|
423
427
|
}
|
|
424
428
|
}
|
|
425
429
|
|
|
430
|
+
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
431
|
+
return (
|
|
432
|
+
typeof value === "object" &&
|
|
433
|
+
value !== null &&
|
|
434
|
+
!Array.isArray(value) &&
|
|
435
|
+
Object.getPrototypeOf(value) === Object.prototype
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function isValidNativeNoReplaceResult(value: unknown): value is NativeNoReplaceResult {
|
|
440
|
+
if (!isPlainRecord(value)) return false;
|
|
441
|
+
const expectedKeys = [
|
|
442
|
+
"ok",
|
|
443
|
+
"code",
|
|
444
|
+
"mutationState",
|
|
445
|
+
"durabilityState",
|
|
446
|
+
"reason",
|
|
447
|
+
"primitive",
|
|
448
|
+
"phase",
|
|
449
|
+
"diagnostic",
|
|
450
|
+
];
|
|
451
|
+
if (Object.keys(value).some(key => !expectedKeys.includes(key))) return false;
|
|
452
|
+
if (
|
|
453
|
+
typeof value.ok !== "boolean" ||
|
|
454
|
+
(value.code !== undefined && (typeof value.code !== "string" || !/^[a-z0-9_]{1,64}$/.test(value.code))) ||
|
|
455
|
+
!(["not_committed", "committed", "unknown"] as const).includes(value.mutationState as never) ||
|
|
456
|
+
!(["not_attempted", "proven", "not_provable"] as const).includes(value.durabilityState as never) ||
|
|
457
|
+
!(
|
|
458
|
+
[
|
|
459
|
+
"none",
|
|
460
|
+
"destination_exists",
|
|
461
|
+
"atomic_unavailable",
|
|
462
|
+
"cross_device",
|
|
463
|
+
"permission_denied",
|
|
464
|
+
"io_failure",
|
|
465
|
+
"invalid_request",
|
|
466
|
+
"interrupted",
|
|
467
|
+
"identity_violation",
|
|
468
|
+
"durability_not_provable",
|
|
469
|
+
"unknown",
|
|
470
|
+
] as const
|
|
471
|
+
).includes(value.reason as never) ||
|
|
472
|
+
!(
|
|
473
|
+
[
|
|
474
|
+
"renameat2_noreplace",
|
|
475
|
+
"linkat_noreplace",
|
|
476
|
+
"mkdirat_renameat_noreplace",
|
|
477
|
+
"renameatx_np_excl",
|
|
478
|
+
"windows_rename_noreplace",
|
|
479
|
+
"unsupported",
|
|
480
|
+
"unknown",
|
|
481
|
+
] as const
|
|
482
|
+
).includes(value.primitive as never) ||
|
|
483
|
+
!(
|
|
484
|
+
[
|
|
485
|
+
"preflight",
|
|
486
|
+
"file_sync",
|
|
487
|
+
"rename",
|
|
488
|
+
"source_unlink",
|
|
489
|
+
"source_parent_sync",
|
|
490
|
+
"destination_parent_sync",
|
|
491
|
+
"terminal_identity",
|
|
492
|
+
"complete",
|
|
493
|
+
"unknown",
|
|
494
|
+
] as const
|
|
495
|
+
).includes(value.phase as never)
|
|
496
|
+
)
|
|
497
|
+
return false;
|
|
498
|
+
if (!isPlainRecord(value.diagnostic)) return false;
|
|
499
|
+
if (
|
|
500
|
+
Object.keys(value.diagnostic).some(
|
|
501
|
+
key => !["schemaVersion", "collectionState", "osCode", "syncFailures"].includes(key),
|
|
502
|
+
) ||
|
|
503
|
+
value.diagnostic.schemaVersion !== 1 ||
|
|
504
|
+
!(["complete", "partial", "unavailable"] as const).includes(value.diagnostic.collectionState as never) ||
|
|
505
|
+
(value.diagnostic.osCode !== undefined &&
|
|
506
|
+
(typeof value.diagnostic.osCode !== "number" || !Number.isInteger(value.diagnostic.osCode))) ||
|
|
507
|
+
(value.diagnostic.syncFailures !== undefined && !Array.isArray(value.diagnostic.syncFailures))
|
|
508
|
+
)
|
|
509
|
+
return false;
|
|
510
|
+
if (value.ok)
|
|
511
|
+
return (
|
|
512
|
+
value.mutationState === "committed" &&
|
|
513
|
+
value.reason === "none" &&
|
|
514
|
+
value.phase === "complete" &&
|
|
515
|
+
(value.durabilityState === "not_attempted" || value.durabilityState === "proven")
|
|
516
|
+
);
|
|
517
|
+
if (value.mutationState === "not_committed") return value.durabilityState === "not_attempted";
|
|
518
|
+
return value.mutationState === "unknown" && value.durabilityState === "not_provable";
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Only a complete native envelope proving that no namespace mutation happened
|
|
523
|
+
* may authorize the directory fallback. A legacy or malformed result is
|
|
524
|
+
* treated as an unknown publication outcome and never followed by another
|
|
525
|
+
* mutating primitive.
|
|
526
|
+
*/
|
|
527
|
+
function isPreMutationUnsupportedRenameResult(value: unknown): value is NativeNoReplaceResult {
|
|
528
|
+
if (!isValidNativeNoReplaceResult(value)) return false;
|
|
529
|
+
if (
|
|
530
|
+
value.ok !== false ||
|
|
531
|
+
(value.code !== "invalid_request" && value.code !== "atomic_unavailable") ||
|
|
532
|
+
value.mutationState !== "not_committed" ||
|
|
533
|
+
value.durabilityState !== "not_attempted" ||
|
|
534
|
+
value.reason !== value.code ||
|
|
535
|
+
(value.primitive !== "renameat2_noreplace" && value.primitive !== "renameatx_np_excl") ||
|
|
536
|
+
(value.phase !== "preflight" && value.phase !== "rename")
|
|
537
|
+
)
|
|
538
|
+
return false;
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
|
|
426
542
|
async function localLockKey(lockPath: string): Promise<string> {
|
|
427
543
|
try {
|
|
428
544
|
return normalizeLockKey(await fs.realpath(lockPath));
|
|
@@ -452,17 +568,9 @@ function ownerIncarnationChanged(owner: FileLockOwnerToken, startTimeCache?: Map
|
|
|
452
568
|
/** Outcome of a guarded lock-dir removal attempt (`removeFileLockDirForGc`). */
|
|
453
569
|
export type FileLockGcRemoval = "removed" | "owner_changed" | "missing" | "cleanup_failed";
|
|
454
570
|
|
|
455
|
-
interface LockDirStatToken {
|
|
456
|
-
dev: number;
|
|
457
|
-
ino: number;
|
|
458
|
-
mtimeMs: number;
|
|
459
|
-
ctimeMs: number;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
571
|
type LockStaleSnapshot =
|
|
463
572
|
| { stale: false }
|
|
464
|
-
| { stale: true; owner: FileLockOwnerToken; identity: GenericFileLockDirIdentity }
|
|
465
|
-
| { stale: true; owner: null; stat: LockDirStatToken };
|
|
573
|
+
| { stale: true; owner: FileLockOwnerToken; identity: GenericFileLockDirIdentity };
|
|
466
574
|
|
|
467
575
|
/** Identity evidence carried by the generic stale verdict into a later removal. */
|
|
468
576
|
export interface GenericFileLockDirIdentity {
|
|
@@ -634,22 +742,9 @@ function ownerLiveness(pid: number): OwnerLiveness {
|
|
|
634
742
|
}
|
|
635
743
|
}
|
|
636
744
|
|
|
637
|
-
function statToken(stats: Stats): LockDirStatToken {
|
|
638
|
-
return {
|
|
639
|
-
dev: stats.dev,
|
|
640
|
-
ino: stats.ino,
|
|
641
|
-
mtimeMs: stats.mtimeMs,
|
|
642
|
-
ctimeMs: stats.ctimeMs,
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
function sameStatToken(a: LockDirStatToken, b: LockDirStatToken): boolean {
|
|
647
|
-
return a.dev === b.dev && a.ino === b.ino && a.mtimeMs === b.mtimeMs && a.ctimeMs === b.ctimeMs;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
745
|
async function staleLockSnapshot(
|
|
651
746
|
lockPath: string,
|
|
652
|
-
|
|
747
|
+
_staleMs: number,
|
|
653
748
|
ownerHostId?: string,
|
|
654
749
|
previousOwnerHostIds: readonly string[] = [],
|
|
655
750
|
startTimeCache?: Map<string, string | null>,
|
|
@@ -673,26 +768,12 @@ async function staleLockSnapshot(
|
|
|
673
768
|
if (isTransientReleaseError(error)) return { stale: false };
|
|
674
769
|
throw error;
|
|
675
770
|
}
|
|
676
|
-
if (!info && ownerHostId !== undefined) return { stale: false };
|
|
677
771
|
if (!info) {
|
|
678
|
-
// A
|
|
679
|
-
//
|
|
680
|
-
//
|
|
681
|
-
// elapsed mtime
|
|
682
|
-
|
|
683
|
-
await fs.lstat(path.join(lockPath, "info"));
|
|
684
|
-
return { stale: false };
|
|
685
|
-
} catch (error) {
|
|
686
|
-
if (!isEnoent(error)) throw error;
|
|
687
|
-
}
|
|
688
|
-
try {
|
|
689
|
-
const stats = await fs.stat(lockPath);
|
|
690
|
-
if (Date.now() - stats.mtimeMs <= staleMs) return { stale: false };
|
|
691
|
-
return { stale: true, owner: null, stat: statToken(stats) };
|
|
692
|
-
} catch (err) {
|
|
693
|
-
if (isEnoent(err)) return { stale: false };
|
|
694
|
-
throw err;
|
|
695
|
-
}
|
|
772
|
+
// A directory without a valid owner record is either a contender between
|
|
773
|
+
// native mkdirat ownership and metadata publication, or malformed/foreign
|
|
774
|
+
// state. Neither case carries enough identity evidence for stale removal;
|
|
775
|
+
// elapsed mtime must never make this empty namespace reclaimable.
|
|
776
|
+
return { stale: false };
|
|
696
777
|
}
|
|
697
778
|
|
|
698
779
|
// A host-qualified lock may only be reclaimed after proving that its owner is
|
|
@@ -741,38 +822,7 @@ async function staleLockSnapshot(
|
|
|
741
822
|
|
|
742
823
|
async function removeStaleLockForAcquire(lockPath: string, snapshot: LockStaleSnapshot): Promise<boolean> {
|
|
743
824
|
if (!snapshot.stale) return false;
|
|
744
|
-
|
|
745
|
-
return (await removeFileLockDirForGc(lockPath, snapshot.owner, snapshot.identity)) === "removed";
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
let currentInfo: LockInfo | null;
|
|
749
|
-
try {
|
|
750
|
-
currentInfo = await readLockInfo(lockPath);
|
|
751
|
-
} catch (error) {
|
|
752
|
-
if (isTransientReleaseError(error)) return false;
|
|
753
|
-
throw error;
|
|
754
|
-
}
|
|
755
|
-
if (currentInfo) return false;
|
|
756
|
-
try {
|
|
757
|
-
const currentStats = await fs.lstat(lockPath);
|
|
758
|
-
if (currentStats.isSymbolicLink() || !currentStats.isDirectory()) return false;
|
|
759
|
-
if (!sameStatToken(statToken(currentStats), snapshot.stat)) return false;
|
|
760
|
-
const nativeCapturePath = await canonicalLockPathPreservingFinal(lockPath);
|
|
761
|
-
const captured = nativeFileLockBindings().snapshotDirectoryTree(nativeCapturePath);
|
|
762
|
-
if (captured.code === "sharing_violation") throwTransientNativeResult(captured.code);
|
|
763
|
-
if (!captured.ok || !captured.snapshot) return false;
|
|
764
|
-
if (
|
|
765
|
-
captured.snapshot.rootDev !== String(currentStats.dev) ||
|
|
766
|
-
captured.snapshot.rootIno !== String(currentStats.ino)
|
|
767
|
-
)
|
|
768
|
-
return false;
|
|
769
|
-
const removed = nativeFileLockBindings().exactRemoveDirectoryTree(nativeCapturePath, captured.snapshot);
|
|
770
|
-
return removed.ok || removed.code === "not_found";
|
|
771
|
-
} catch (err) {
|
|
772
|
-
if (isEnoent(err)) return false;
|
|
773
|
-
if (isTransientReleaseError(err)) return false;
|
|
774
|
-
throw err;
|
|
775
|
-
}
|
|
825
|
+
return (await removeFileLockDirForGc(lockPath, snapshot.owner, snapshot.identity)) === "removed";
|
|
776
826
|
}
|
|
777
827
|
|
|
778
828
|
/**
|
|
@@ -812,7 +862,7 @@ export async function genericFileLockDirStaleVerdict(
|
|
|
812
862
|
ownerHostId?: string,
|
|
813
863
|
): Promise<GenericFileLockDirStaleVerdict> {
|
|
814
864
|
const verdict = await staleLockSnapshot(lockDir, staleMs, ownerHostId);
|
|
815
|
-
if (!verdict.stale
|
|
865
|
+
if (!verdict.stale) return { stale: false };
|
|
816
866
|
return {
|
|
817
867
|
stale: true,
|
|
818
868
|
identity: verdict.identity,
|
|
@@ -841,11 +891,32 @@ async function tryAcquireLock(
|
|
|
841
891
|
// already-created staging entry and its parent before publication so aliases
|
|
842
892
|
// retain the same lock identity as the ordinary path.
|
|
843
893
|
const canonicalParent = await fs.realpath(path.dirname(lockPath));
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
894
|
+
const destinationPath = path.join(canonicalParent, path.basename(lockPath));
|
|
895
|
+
// Resolve only the stable parent: resolving the mutable staging final
|
|
896
|
+
// component would follow an attacker-replaced symlink before native no-follow
|
|
897
|
+
// validation gets a chance to reject it.
|
|
898
|
+
const canonicalPendingPath = path.join(canonicalParent, path.basename(pendingPath));
|
|
899
|
+
const publication = FileLockTestHooks.nativePublicationBindings?.() ?? {
|
|
900
|
+
renameNoReplacePathAsync,
|
|
901
|
+
renameDirectoryNoReplacePathAsync,
|
|
902
|
+
};
|
|
903
|
+
const published = await publication.renameNoReplacePathAsync(canonicalPendingPath, destinationPath);
|
|
904
|
+
let publishedSuccessfully = published.ok;
|
|
905
|
+
if (!published.ok && isPreMutationUnsupportedRenameResult(published)) {
|
|
906
|
+
const fallback = await publication.renameDirectoryNoReplacePathAsync(canonicalPendingPath, destinationPath);
|
|
907
|
+
if (fallback.ok) {
|
|
908
|
+
publishedSuccessfully = true;
|
|
909
|
+
} else if (fallback.reason === "destination_exists") {
|
|
910
|
+
return null;
|
|
911
|
+
} else {
|
|
912
|
+
const failure = new Error(
|
|
913
|
+
`Failed to publish file lock: ${fallback.code ?? fallback.reason ?? "unknown"}.`,
|
|
914
|
+
) as NodeJS.ErrnoException;
|
|
915
|
+
if (fallback.code) failure.code = fallback.code;
|
|
916
|
+
throw failure;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
if (!publishedSuccessfully) {
|
|
849
920
|
if (published.reason === "destination_exists") return null;
|
|
850
921
|
const failure = new Error(
|
|
851
922
|
`Failed to publish file lock: ${published.code ?? published.reason ?? "unknown"}.`,
|
|
@@ -1103,6 +1174,10 @@ async function lockHolderDescription(lockPath: string): Promise<string> {
|
|
|
1103
1174
|
}
|
|
1104
1175
|
|
|
1105
1176
|
async function acquireLock(filePath: string, options: FileLockOptions = {}): Promise<() => Promise<void>> {
|
|
1177
|
+
const requestedFilePath: unknown = filePath;
|
|
1178
|
+
if (typeof requestedFilePath !== "string" || requestedFilePath.length === 0 || !path.isAbsolute(requestedFilePath))
|
|
1179
|
+
throw new TypeError("filePath must be a non-empty absolute path");
|
|
1180
|
+
if (requestedFilePath.includes("\0")) throw new TypeError("filePath must not contain NUL bytes");
|
|
1106
1181
|
if (options.ownerHostId !== undefined && !options.ownerHostId) throw new Error("ownerHostId must be non-empty");
|
|
1107
1182
|
if (options.previousOwnerHostIds?.some(hostId => !hostId))
|
|
1108
1183
|
throw new Error("previousOwnerHostIds must contain only non-empty identities");
|
|
@@ -1167,11 +1167,7 @@ function recoverStateCopiesSync(
|
|
|
1167
1167
|
(!primaryEvidence.allGenerationsVerified || primaryEvidence.duplicateRevision)
|
|
1168
1168
|
)
|
|
1169
1169
|
if (!options.allowInvalidPrimaryFallback || !backupEvidence?.fullyVerified) {
|
|
1170
|
-
|
|
1171
|
-
validateStateGenerations(primaryEvidence.state, trustedKeys);
|
|
1172
|
-
} catch (error) {
|
|
1173
|
-
throw error;
|
|
1174
|
-
}
|
|
1170
|
+
validateStateGenerations(primaryEvidence.state, trustedKeys);
|
|
1175
1171
|
}
|
|
1176
1172
|
if (
|
|
1177
1173
|
backupEvidence &&
|
|
@@ -241,11 +241,11 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
241
241
|
architect: ["anthropic/claude-opus-5:xhigh", "anthropic/claude-opus-4-6:xhigh"],
|
|
242
242
|
}),
|
|
243
243
|
profile("claude-fable", ["anthropic"], {
|
|
244
|
-
default: "anthropic/claude-fable-5:xhigh",
|
|
244
|
+
default: "anthropic/claude-fable-5-1:xhigh",
|
|
245
245
|
executor: "anthropic/claude-sonnet-5",
|
|
246
|
-
planner: "anthropic/claude-fable-5:low",
|
|
247
|
-
critic: "anthropic/claude-fable-5:high",
|
|
248
|
-
architect: "anthropic/claude-fable-5:xhigh",
|
|
246
|
+
planner: "anthropic/claude-fable-5-1:low",
|
|
247
|
+
critic: "anthropic/claude-fable-5-1:high",
|
|
248
|
+
architect: "anthropic/claude-fable-5-1:xhigh",
|
|
249
249
|
}),
|
|
250
250
|
profile("glm-eco", ["zai"], {
|
|
251
251
|
default: "zai/glm-5.3-flash:low",
|
|
@@ -482,7 +482,7 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
482
482
|
architect: "openai-codex/gpt-5.6-sol:high",
|
|
483
483
|
}),
|
|
484
484
|
profile("fable-opus-codex", ["anthropic", "openai-codex"], {
|
|
485
|
-
default: "anthropic/claude-fable-5:high",
|
|
485
|
+
default: "anthropic/claude-fable-5-1:high",
|
|
486
486
|
executor: "openai-codex/gpt-5.6-terra:medium",
|
|
487
487
|
planner: "anthropic/claude-opus-5:medium",
|
|
488
488
|
critic: "anthropic/claude-opus-5:high",
|
|
@@ -1192,6 +1192,7 @@ type CustomModelOverlay = {
|
|
|
1192
1192
|
wireModelId?: string;
|
|
1193
1193
|
requestTransform?: ModelRequestTransform;
|
|
1194
1194
|
cacheRetention?: CacheRetention;
|
|
1195
|
+
transport?: Model<Api>["transport"];
|
|
1195
1196
|
isOAuth?: boolean;
|
|
1196
1197
|
};
|
|
1197
1198
|
|
|
@@ -1303,6 +1304,7 @@ function buildCustomModelOverlay(
|
|
|
1303
1304
|
providerRequestTransform: ModelRequestTransform | undefined,
|
|
1304
1305
|
providerAuth: ProviderAuthMode | undefined,
|
|
1305
1306
|
providerCacheRetention: CacheRetention | undefined,
|
|
1307
|
+
providerTransport: Model<Api>["transport"] | undefined,
|
|
1306
1308
|
modelDef: CustomModelDefinitionLike,
|
|
1307
1309
|
): CustomModelOverlay | undefined {
|
|
1308
1310
|
const api = modelDef.api ?? providerApi;
|
|
@@ -1331,6 +1333,7 @@ function buildCustomModelOverlay(
|
|
|
1331
1333
|
contextPromotionTarget: modelDef.contextPromotionTarget,
|
|
1332
1334
|
premiumMultiplier: modelDef.premiumMultiplier,
|
|
1333
1335
|
cacheRetention: modelDef.cacheRetention ?? providerCacheRetention,
|
|
1336
|
+
transport: providerTransport,
|
|
1334
1337
|
isOAuth: resolveCustomModelIsOAuth(api, providerAuth),
|
|
1335
1338
|
};
|
|
1336
1339
|
}
|
|
@@ -1466,6 +1469,7 @@ function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuil
|
|
|
1466
1469
|
wireModelId: resolvedModel.wireModelId,
|
|
1467
1470
|
requestTransform: resolvedModel.requestTransform,
|
|
1468
1471
|
cacheRetention: resolvedModel.cacheRetention ?? reference?.cacheRetention,
|
|
1472
|
+
transport: resolvedModel.transport,
|
|
1469
1473
|
premiumMultiplier: resolvedModel.premiumMultiplier,
|
|
1470
1474
|
isOAuth: resolvedModel.isOAuth,
|
|
1471
1475
|
} as Model<Api>);
|
|
@@ -2291,6 +2295,7 @@ export class ModelRegistry {
|
|
|
2291
2295
|
// re-merge bundled transport metadata here.
|
|
2292
2296
|
headers: customModel.headers,
|
|
2293
2297
|
compat: customModel.compat,
|
|
2298
|
+
transport: customModel.transport,
|
|
2294
2299
|
contextPromotionTarget: customModel.contextPromotionTarget ?? existingModel.contextPromotionTarget,
|
|
2295
2300
|
wireModelId: customModel.wireModelId,
|
|
2296
2301
|
requestTransform: customModel.requestTransform,
|
|
@@ -4452,6 +4457,7 @@ export class ModelRegistry {
|
|
|
4452
4457
|
providerConfig.requestTransform,
|
|
4453
4458
|
(providerConfig.auth as ProviderAuthMode | undefined) ?? undefined,
|
|
4454
4459
|
providerConfig.cacheRetention,
|
|
4460
|
+
providerConfig.transport,
|
|
4455
4461
|
modelDef as CustomModelDefinitionLike,
|
|
4456
4462
|
);
|
|
4457
4463
|
if (!model) continue;
|
|
@@ -4584,6 +4590,22 @@ export class ModelRegistry {
|
|
|
4584
4590
|
autoroutingProviderOrder(): readonly string[] {
|
|
4585
4591
|
return projectCatalogProviderOrder(getConfiguredProviderOrderFromSettings(), this.#models);
|
|
4586
4592
|
}
|
|
4593
|
+
|
|
4594
|
+
/**
|
|
4595
|
+
* Provider priority for automatic model selection. Explicit provider order
|
|
4596
|
+
* wins first, followed by omitted OAuth providers, then other providers;
|
|
4597
|
+
* catalog order breaks ties within each policy band.
|
|
4598
|
+
*/
|
|
4599
|
+
automaticProviderOrder(sessionId?: string): readonly string[] {
|
|
4600
|
+
const policy = this.#buildProviderSelectionPolicy(sessionId);
|
|
4601
|
+
return [...policy.orderedProviders()].sort((left, right) => {
|
|
4602
|
+
const rankDifference = policy.rank(left) - policy.rank(right);
|
|
4603
|
+
return rankDifference !== 0
|
|
4604
|
+
? rankDifference
|
|
4605
|
+
: policy.providerCatalogIndex(left) - policy.providerCatalogIndex(right);
|
|
4606
|
+
});
|
|
4607
|
+
}
|
|
4608
|
+
|
|
4587
4609
|
#providerRankMap(policy: ProviderSelectionPolicy): Map<string, number> {
|
|
4588
4610
|
const providerRank = new Map<string, number>();
|
|
4589
4611
|
for (const provider of policy.orderedProviders()) {
|
|
@@ -5608,6 +5630,7 @@ export class ModelRegistry {
|
|
|
5608
5630
|
config.requestTransform,
|
|
5609
5631
|
undefined,
|
|
5610
5632
|
undefined,
|
|
5633
|
+
config.transport,
|
|
5611
5634
|
modelDef as CustomModelDefinitionLike,
|
|
5612
5635
|
);
|
|
5613
5636
|
if (!overlay) {
|
|
@@ -1580,6 +1580,11 @@ export async function findInitialModel(options: {
|
|
|
1580
1580
|
thinkingLevel = resolveThinkingLevelForModel(found, defaultThinkingSelector);
|
|
1581
1581
|
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
1582
1582
|
}
|
|
1583
|
+
return {
|
|
1584
|
+
model: undefined,
|
|
1585
|
+
thinkingLevel: undefined,
|
|
1586
|
+
fallbackMessage: `Model ${defaultProvider}/${defaultModelId} not found`,
|
|
1587
|
+
};
|
|
1583
1588
|
}
|
|
1584
1589
|
|
|
1585
1590
|
// 4. Try first available model with valid API key
|
|
@@ -421,6 +421,12 @@ export const SETTINGS_SCHEMA = {
|
|
|
421
421
|
description: "Hard maximum runtime for an SDK prompt from acceptance, bounding progress-aware renewals.",
|
|
422
422
|
validate: (value: number) => Number.isSafeInteger(value) && value >= 60_000 && value <= 86_400_000,
|
|
423
423
|
},
|
|
424
|
+
"sdk.masterOrphanGraceMs": {
|
|
425
|
+
type: "number",
|
|
426
|
+
default: 120_000,
|
|
427
|
+
description: "Grace period before a master-spawned child orphaned by confirmed master loss is closed.",
|
|
428
|
+
validate: (value: number) => Number.isSafeInteger(value) && value >= 60_000 && value <= 3_600_000,
|
|
429
|
+
},
|
|
424
430
|
|
|
425
431
|
// Notifications (shared daemon with Telegram/Discord/Slack presentation adapters)
|
|
426
432
|
"notifications.enabled": { type: "boolean", default: false },
|
|
@@ -745,7 +751,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
745
751
|
// ────────────────────────────────────────────────────────────────────────
|
|
746
752
|
"ui.language": {
|
|
747
753
|
type: "enum",
|
|
748
|
-
values: ["en", "ko", "ja"] as const,
|
|
754
|
+
values: ["en", "ko", "zh", "ja"] as const,
|
|
749
755
|
default: "en",
|
|
750
756
|
ui: {
|
|
751
757
|
tab: "appearance",
|
|
@@ -754,6 +760,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
754
760
|
options: [
|
|
755
761
|
{ value: "en", label: "English" },
|
|
756
762
|
{ value: "ko", label: "Korean (한국어)" },
|
|
763
|
+
{ value: "zh", label: "Chinese (简体中文)" },
|
|
757
764
|
{ value: "ja", label: "Japanese (日本語)" },
|
|
758
765
|
],
|
|
759
766
|
},
|
package/src/config/settings.ts
CHANGED
|
@@ -748,6 +748,11 @@ export class Settings implements NotificationSettingsReader {
|
|
|
748
748
|
return !this.#persist || !this.#hasRecoveredConfigSyntax;
|
|
749
749
|
}
|
|
750
750
|
|
|
751
|
+
/** Whether this instance can publish a mutation to a durable config file. */
|
|
752
|
+
canPersistDurableConfig(): boolean {
|
|
753
|
+
return this.#persist && !this.#hasRecoveredConfigSyntax;
|
|
754
|
+
}
|
|
755
|
+
|
|
751
756
|
/**
|
|
752
757
|
* Set a setting value (sync).
|
|
753
758
|
* Updates global settings and reserves its background persistence slot before
|
|
@@ -36,6 +36,8 @@ function projectionPlatform(): NodeJS.Platform {
|
|
|
36
36
|
|
|
37
37
|
/** Post-filter parse-candidate cap. Exhaustion returns an explicit incomplete result. */
|
|
38
38
|
export const COORDINATOR_JSON_SCAN_CAP = 10_000;
|
|
39
|
+
/** Number of fresh authoritative attempts allowed for a scan that observed churn. */
|
|
40
|
+
export const COORDINATOR_JSON_SCAN_RETRY_ATTEMPTS = 3;
|
|
39
41
|
|
|
40
42
|
export interface ProjectionScanStat {
|
|
41
43
|
size: number | bigint;
|
|
@@ -569,3 +571,22 @@ export async function listCoordinatorJsonFiles(
|
|
|
569
571
|
await authority?.close().catch(() => undefined);
|
|
570
572
|
}
|
|
571
573
|
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Retry only scans invalidated by observed directory/file churn. A candidate cap,
|
|
577
|
+
* unsupported authority, parse error, or any other failure remains fail-closed.
|
|
578
|
+
* Each attempt acquires a new authority, so no partial result crosses attempts.
|
|
579
|
+
*/
|
|
580
|
+
export async function listCoordinatorJsonFilesWithRetry(
|
|
581
|
+
dir: string,
|
|
582
|
+
io: ProjectionScanFs = defaultFs,
|
|
583
|
+
cap: number = COORDINATOR_JSON_SCAN_CAP,
|
|
584
|
+
maxAttempts: number = COORDINATOR_JSON_SCAN_RETRY_ATTEMPTS,
|
|
585
|
+
): Promise<ProjectionScanResult> {
|
|
586
|
+
const attempts = Math.max(1, Math.floor(maxAttempts));
|
|
587
|
+
let scan = await listCoordinatorJsonFiles(dir, io, cap);
|
|
588
|
+
for (let attempt = 1; attempt < attempts && scan.incomplete && scan.raced > 0; attempt++) {
|
|
589
|
+
scan = await listCoordinatorJsonFiles(dir, io, cap);
|
|
590
|
+
}
|
|
591
|
+
return scan;
|
|
592
|
+
}
|
|
@@ -1029,7 +1029,7 @@ function assertTransaction(transaction: CoordinatorSessionTransactionV1, namespa
|
|
|
1029
1029
|
const rejectedHistorical = request.phase === "rejected";
|
|
1030
1030
|
if (
|
|
1031
1031
|
rejectedHistorical
|
|
1032
|
-
?
|
|
1032
|
+
? request.safe_receipt?.status !== "rejected"
|
|
1033
1033
|
: question.answer_request_id !== request.request_id ||
|
|
1034
1034
|
question.claim_fence_epoch !== request.claim_fence_epoch
|
|
1035
1035
|
)
|
|
@@ -1083,7 +1083,7 @@ function assertTransaction(transaction: CoordinatorSessionTransactionV1, namespa
|
|
|
1083
1083
|
event.transaction_revision > transaction.revision ||
|
|
1084
1084
|
typeof event.kind !== "string" ||
|
|
1085
1085
|
event.kind.length === 0 ||
|
|
1086
|
-
!
|
|
1086
|
+
!isOutboxEntity(event.entity) ||
|
|
1087
1087
|
typeof event.entity_id !== "string" ||
|
|
1088
1088
|
event.entity_id.length === 0 ||
|
|
1089
1089
|
!isRecord(event.payload) ||
|
|
@@ -1296,7 +1296,7 @@ export async function listCanonicalActiveSessions(
|
|
|
1296
1296
|
paths.registryLock,
|
|
1297
1297
|
async () => {
|
|
1298
1298
|
const registry = await readJson<NamespaceRegistryV1>(paths.registry);
|
|
1299
|
-
if (
|
|
1299
|
+
if (registry?.schema_version !== 1 || registry.namespace_id !== path.basename(paths.root))
|
|
1300
1300
|
throw new Error("state_corrupt");
|
|
1301
1301
|
return Object.values(registry.roster ?? {})
|
|
1302
1302
|
.filter(entry => entry.active || entry.dirty)
|
|
@@ -1333,7 +1333,7 @@ export async function readDeliveryDiscoveryCursor(
|
|
|
1333
1333
|
paths.registryLock,
|
|
1334
1334
|
async () => {
|
|
1335
1335
|
const registry = await readJson<NamespaceRegistryV1>(paths.registry);
|
|
1336
|
-
if (
|
|
1336
|
+
if (registry?.schema_version !== 1 || registry.namespace_id !== path.basename(paths.root))
|
|
1337
1337
|
throw new Error("state_corrupt");
|
|
1338
1338
|
return registry.delivery_discovery_cursor ?? "";
|
|
1339
1339
|
},
|
|
@@ -1660,7 +1660,7 @@ export async function startCreationRemote(
|
|
|
1660
1660
|
): Promise<CreationRequestV1> {
|
|
1661
1661
|
return await withNamespaceRegistry(paths, async registry => {
|
|
1662
1662
|
const request = registry.creations[keyDigest];
|
|
1663
|
-
if (!request
|
|
1663
|
+
if (!request?.sidecar_verifier) throw new Error("state_corrupt");
|
|
1664
1664
|
if (
|
|
1665
1665
|
request.phase === "claimed" &&
|
|
1666
1666
|
(request.sidecar_verifier.key_id !== expectedVerifier.key_id ||
|
|
@@ -1687,8 +1687,7 @@ export async function reconcileCreationRemoteVerifier(
|
|
|
1687
1687
|
): Promise<CreationRequestV1> {
|
|
1688
1688
|
return await withNamespaceRegistry(paths, async registry => {
|
|
1689
1689
|
const request = registry.creations[keyDigest];
|
|
1690
|
-
if (!request
|
|
1691
|
-
throw new Error("terminal_uncertain");
|
|
1690
|
+
if (!request?.sidecar_verifier || request.phase !== "remote_started") throw new Error("terminal_uncertain");
|
|
1692
1691
|
if (!/^[a-f0-9]{64}$/.test(usedKeyId)) throw new Error("terminal_uncertain");
|
|
1693
1692
|
if (usedKeyId === candidate.key_id) request.sidecar_verifier = candidate;
|
|
1694
1693
|
else if (usedKeyId !== request.sidecar_verifier.key_id) throw new Error("terminal_uncertain");
|
|
@@ -2241,7 +2240,7 @@ export async function enumeratePublicDeliveries(
|
|
|
2241
2240
|
paths.registryLock,
|
|
2242
2241
|
async () => {
|
|
2243
2242
|
const registry = await readJson<NamespaceRegistryV1>(paths.registry);
|
|
2244
|
-
if (
|
|
2243
|
+
if (registry?.schema_version !== 1 || registry.namespace_id !== path.basename(paths.root))
|
|
2245
2244
|
throw new Error("state_corrupt");
|
|
2246
2245
|
return [...new Set([...Object.keys(registry.roster ?? {}), ...Object.keys(registry.retained_sessions ?? {})])]
|
|
2247
2246
|
.filter(name => COORDINATOR_SESSION_ID_PATTERN.test(name))
|
|
@@ -2526,7 +2525,7 @@ export async function replaceCreationRetirementIntent(
|
|
|
2526
2525
|
if (request.phase !== "remote_started" && request.phase !== "uncertain" && request.phase !== "retired")
|
|
2527
2526
|
throw new Error("retire_not_allowed");
|
|
2528
2527
|
const staged = request.retirement_intent;
|
|
2529
|
-
if (
|
|
2528
|
+
if (staged?.phase !== "intent" || staged.broker_proof) throw new Error("state_corrupt");
|
|
2530
2529
|
if (staged.retirement_key_digest !== retirementKeyDigest) throw new Error("idempotency_conflict");
|
|
2531
2530
|
assertCreationRetirementProofMatches(request, proof, false);
|
|
2532
2531
|
staged.phase = "pre_effect_rejected";
|
|
@@ -2716,8 +2715,7 @@ export async function removeSessionTransaction(
|
|
|
2716
2715
|
// exporter lease expires instead of deleting an undelivered event.
|
|
2717
2716
|
if (Object.values(transaction.outbox).some(event => event.public_delivery.state !== "acknowledged"))
|
|
2718
2717
|
return false;
|
|
2719
|
-
await
|
|
2720
|
-
await syncCoordinatorDirectory(path.dirname(file));
|
|
2718
|
+
await removeCoordinatorStateFile(file);
|
|
2721
2719
|
delete registry.roster?.[sessionId];
|
|
2722
2720
|
delete registry.retained_sessions?.[sessionId];
|
|
2723
2721
|
return true;
|