@wrongstack/core 0.286.0 → 0.287.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/dist/boot.d.ts.map +1 -1
- package/dist/coordination/brain-monitor.d.ts +15 -0
- package/dist/coordination/brain-monitor.d.ts.map +1 -1
- package/dist/coordination/delegate-tool.d.ts +8 -14
- package/dist/coordination/delegate-tool.d.ts.map +1 -1
- package/dist/coordination/director/director-btw-notes.d.ts +20 -0
- package/dist/coordination/director/director-btw-notes.d.ts.map +1 -0
- package/dist/coordination/director/director-collab.d.ts +40 -0
- package/dist/coordination/director/director-collab.d.ts.map +1 -0
- package/dist/coordination/director.d.ts +27 -8
- package/dist/coordination/director.d.ts.map +1 -1
- package/dist/coordination/fleet-manager.d.ts +21 -0
- package/dist/coordination/fleet-manager.d.ts.map +1 -1
- package/dist/coordination/fleet.d.ts +2 -3
- package/dist/coordination/fleet.d.ts.map +1 -1
- package/dist/coordination/global-mailbox.d.ts +36 -22
- package/dist/coordination/global-mailbox.d.ts.map +1 -1
- package/dist/coordination/icoordinator.d.ts +1 -3
- package/dist/coordination/icoordinator.d.ts.map +1 -1
- package/dist/coordination/index.js +708 -442
- package/dist/coordination/index.js.map +4 -4
- package/dist/coordination/mail-tools.d.ts.map +1 -1
- package/dist/coordination/mailbox-message-codec.d.ts +21 -1
- package/dist/coordination/mailbox-message-codec.d.ts.map +1 -1
- package/dist/coordination/mailbox-types.d.ts +42 -1
- package/dist/coordination/mailbox-types.d.ts.map +1 -1
- package/dist/core/agent-loop.d.ts.map +1 -1
- package/dist/core/agent-response.d.ts +10 -1
- package/dist/core/agent-response.d.ts.map +1 -1
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/agent.d.ts +8 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/context.d.ts +25 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/conversation-state.d.ts +6 -0
- package/dist/core/conversation-state.d.ts.map +1 -1
- package/dist/core/fallback-model.d.ts.map +1 -1
- package/dist/core/mailbox-loop.d.ts +12 -5
- package/dist/core/mailbox-loop.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.d.ts +14 -6
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/defaults/index.d.ts +0 -1
- package/dist/defaults/index.d.ts.map +1 -1
- package/dist/defaults/index.js +834 -1465
- package/dist/defaults/index.js.map +4 -4
- package/dist/execution/auto-compaction-middleware.d.ts +8 -1
- package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
- package/dist/execution/compaction-core.d.ts.map +1 -1
- package/dist/execution/council-brain.d.ts.map +1 -1
- package/dist/execution/council-orchestrator.d.ts +32 -0
- package/dist/execution/council-orchestrator.d.ts.map +1 -0
- package/dist/execution/council-personas.d.ts +19 -0
- package/dist/execution/council-personas.d.ts.map +1 -0
- package/dist/execution/council-profiles.d.ts +34 -0
- package/dist/execution/council-profiles.d.ts.map +1 -0
- package/dist/execution/council-prompts.d.ts +21 -0
- package/dist/execution/council-prompts.d.ts.map +1 -0
- package/dist/execution/council-resolution.d.ts +57 -0
- package/dist/execution/council-resolution.d.ts.map +1 -0
- package/dist/execution/design-kit-loader.d.ts.map +1 -1
- package/dist/execution/enhance-recovery.d.ts +35 -0
- package/dist/execution/enhance-recovery.d.ts.map +1 -0
- package/dist/execution/error-handler.d.ts.map +1 -1
- package/dist/execution/index.d.ts +6 -0
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +1738 -161
- package/dist/execution/index.js.map +4 -4
- package/dist/execution/intelligent-compactor.d.ts +18 -7
- package/dist/execution/intelligent-compactor.d.ts.map +1 -1
- package/dist/execution/one-shot-llm.d.ts +52 -0
- package/dist/execution/one-shot-llm.d.ts.map +1 -0
- package/dist/execution/prompt-enhancer.d.ts +23 -4
- package/dist/execution/prompt-enhancer.d.ts.map +1 -1
- package/dist/execution/prompt-enhancer.js +24 -6
- package/dist/execution/prompt-enhancer.js.map +2 -2
- package/dist/execution/selective-compactor.d.ts +4 -4
- package/dist/execution/strategy-compactor.d.ts +7 -0
- package/dist/execution/strategy-compactor.d.ts.map +1 -1
- package/dist/execution/tool-executor.d.ts +19 -5
- package/dist/execution/tool-executor.d.ts.map +1 -1
- package/dist/hooks/registry.d.ts.map +1 -1
- package/dist/hooks/runner.d.ts.map +1 -1
- package/dist/hooks/shell-executor.d.ts.map +1 -1
- package/dist/hq/alerts.d.ts +5 -0
- package/dist/hq/alerts.d.ts.map +1 -1
- package/dist/hq/auth-store.d.ts +17 -7
- package/dist/hq/auth-store.d.ts.map +1 -1
- package/dist/hq/commands.d.ts +8 -4
- package/dist/hq/commands.d.ts.map +1 -1
- package/dist/hq/cost-bridge.d.ts +5 -3
- package/dist/hq/cost-bridge.d.ts.map +1 -1
- package/dist/hq/fleet-bridge.d.ts.map +1 -1
- package/dist/hq/index.d.ts +0 -1
- package/dist/hq/index.d.ts.map +1 -1
- package/dist/hq/index.js +471 -294
- package/dist/hq/index.js.map +4 -4
- package/dist/hq/persistence.d.ts +42 -0
- package/dist/hq/persistence.d.ts.map +1 -1
- package/dist/hq/protocol/brain.d.ts +24 -0
- package/dist/hq/protocol/brain.d.ts.map +1 -0
- package/dist/hq/protocol/browser.d.ts +12 -0
- package/dist/hq/protocol/browser.d.ts.map +1 -0
- package/dist/hq/protocol/client.d.ts +69 -0
- package/dist/hq/protocol/client.d.ts.map +1 -0
- package/dist/hq/protocol/core.d.ts +130 -0
- package/dist/hq/protocol/core.d.ts.map +1 -0
- package/dist/hq/protocol/fleet.d.ts +73 -0
- package/dist/hq/protocol/fleet.d.ts.map +1 -0
- package/dist/hq/protocol/mailbox.d.ts +76 -0
- package/dist/hq/protocol/mailbox.d.ts.map +1 -0
- package/dist/hq/protocol/mcp.d.ts +51 -0
- package/dist/hq/protocol/mcp.d.ts.map +1 -0
- package/dist/hq/protocol/project.d.ts +26 -0
- package/dist/hq/protocol/project.d.ts.map +1 -0
- package/dist/hq/protocol/session.d.ts +153 -0
- package/dist/hq/protocol/session.d.ts.map +1 -0
- package/dist/hq/protocol/tool.d.ts +31 -0
- package/dist/hq/protocol/tool.d.ts.map +1 -0
- package/dist/hq/protocol.d.ts +10 -611
- package/dist/hq/protocol.d.ts.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4540 -2283
- package/dist/index.js.map +4 -4
- package/dist/infrastructure/index.js +13 -11
- package/dist/infrastructure/index.js.map +2 -2
- package/dist/infrastructure/token-counter.d.ts.map +1 -1
- package/dist/kernel/events/agent-events.d.ts +283 -0
- package/dist/kernel/events/agent-events.d.ts.map +1 -0
- package/dist/kernel/events/brain-events.d.ts +176 -0
- package/dist/kernel/events/brain-events.d.ts.map +1 -0
- package/dist/kernel/events/fleet-events.d.ts +39 -0
- package/dist/kernel/events/fleet-events.d.ts.map +1 -0
- package/dist/kernel/events/memory-events.d.ts +165 -0
- package/dist/kernel/events/memory-events.d.ts.map +1 -0
- package/dist/kernel/events/provider-events.d.ts +98 -0
- package/dist/kernel/events/provider-events.d.ts.map +1 -0
- package/dist/kernel/events/sdd-events.d.ts +118 -0
- package/dist/kernel/events/sdd-events.d.ts.map +1 -0
- package/dist/kernel/events/session-events.d.ts +227 -0
- package/dist/kernel/events/session-events.d.ts.map +1 -0
- package/dist/kernel/events/tool-events.d.ts +185 -0
- package/dist/kernel/events/tool-events.d.ts.map +1 -0
- package/dist/kernel/events/worktree-events.d.ts +58 -0
- package/dist/kernel/events/worktree-events.d.ts.map +1 -0
- package/dist/kernel/events.d.ts +10 -1298
- package/dist/kernel/events.d.ts.map +1 -1
- package/dist/kernel/index.js +22 -2
- package/dist/kernel/index.js.map +2 -2
- package/dist/kernel/run-controller.d.ts +15 -0
- package/dist/kernel/run-controller.d.ts.map +1 -1
- package/dist/mailbox-attach.d.ts.map +1 -1
- package/dist/models/index.js +19 -6
- package/dist/models/index.js.map +2 -2
- package/dist/models/llm-selector.d.ts +7 -0
- package/dist/models/llm-selector.d.ts.map +1 -1
- package/dist/plugins/auto-review-plugin.d.ts +36 -0
- package/dist/plugins/auto-review-plugin.d.ts.map +1 -0
- package/dist/plugins/chimera-plugin.d.ts +8 -0
- package/dist/plugins/chimera-plugin.d.ts.map +1 -1
- package/dist/security/index.js +7 -2
- package/dist/security/index.js.map +2 -2
- package/dist/security/secret-scrubber.d.ts.map +1 -1
- package/dist/skills/index.js.map +2 -2
- package/dist/storage/config-loader.d.ts.map +1 -1
- package/dist/storage/goal-kanban.d.ts +69 -0
- package/dist/storage/goal-kanban.d.ts.map +1 -0
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +425 -717
- package/dist/storage/index.js.map +4 -4
- package/dist/storage/memory-consolidator.d.ts +9 -1
- package/dist/storage/memory-consolidator.d.ts.map +1 -1
- package/dist/tools/council-tool.d.ts +25 -0
- package/dist/tools/council-tool.d.ts.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1884 -12
- package/dist/tools/index.js.map +4 -4
- package/dist/tools/one-shot-llm-tool.d.ts +51 -0
- package/dist/tools/one-shot-llm-tool.d.ts.map +1 -0
- package/dist/types/config.d.ts +27 -3
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/context-window.d.ts.map +1 -1
- package/dist/types/council.d.ts +134 -0
- package/dist/types/council.d.ts.map +1 -0
- package/dist/types/default-config.d.ts +3 -3
- package/dist/types/hooks.d.ts +14 -0
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +29 -8
- package/dist/types/index.js.map +3 -3
- package/dist/types/one-shot-llm.d.ts +128 -0
- package/dist/types/one-shot-llm.d.ts.map +1 -0
- package/dist/types/plugin.d.ts +6 -0
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/provider.d.ts +17 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/system-prompt.d.ts +2 -1
- package/dist/types/system-prompt.d.ts.map +1 -1
- package/dist/types/tool-executor.d.ts +10 -0
- package/dist/types/tool-executor.d.ts.map +1 -1
- package/dist/utils/atomic-write.d.ts.map +1 -1
- package/dist/utils/index.js +75 -8
- package/dist/utils/index.js.map +3 -3
- package/dist/utils/tool-wire-compact.d.ts +1 -0
- package/dist/utils/tool-wire-compact.d.ts.map +1 -1
- package/dist/utils/wstack-paths.d.ts +20 -4
- package/dist/utils/wstack-paths.d.ts.map +1 -1
- package/instructions/cli/next-task-predictor.md +5 -3
- package/instructions/coordination/subagent-baseline.md +8 -7
- package/instructions/leader-after-task.md +18 -9
- package/instructions/llm/council-judge.md +19 -0
- package/instructions/llm/council-voter.md +22 -0
- package/instructions/modes/architect.md +14 -9
- package/instructions/modes/audit-lite.md +9 -7
- package/instructions/modes/brief-system.md +12 -12
- package/instructions/modes/brief.md +7 -5
- package/instructions/modes/code-auditor.md +13 -8
- package/instructions/modes/code-reviewer.md +13 -8
- package/instructions/modes/debug-lite.md +7 -6
- package/instructions/modes/debugger.md +13 -9
- package/instructions/modes/devops.md +13 -9
- package/instructions/modes/plan-lite.md +7 -6
- package/instructions/modes/refactor-lite.md +7 -6
- package/instructions/modes/refactorer.md +14 -9
- package/instructions/modes/research-lite.md +8 -6
- package/instructions/modes/research-web.md +18 -55
- package/instructions/modes/review-lite.md +7 -6
- package/instructions/modes/teach-system.md +15 -15
- package/instructions/modes/teach.md +13 -12
- package/instructions/modes/test-lite.md +6 -5
- package/instructions/modes/tester.md +13 -9
- package/instructions/modes/ui-design.md +14 -8
- package/instructions/sections/tool/commit-hygiene.md +1 -7
- package/instructions/sections/tool/common-patterns.md +5 -5
- package/instructions/sections/tool/delegation-full.md +3 -31
- package/instructions/sections/tool/mailbox-compact.md +1 -1
- package/instructions/sections/tool/mailbox-full.md +6 -22
- package/instructions/system.md +278 -39
- package/package.json +2 -2
- package/skills/mailbox-bridge/SKILL.md +16 -7
- package/skills/output-standards/SKILL.md +16 -16
- package/skills/wrongstack-mailbox/SKILL.md +6 -0
- package/dist/coordination/director-construction.d.ts +0 -90
- package/dist/coordination/director-construction.d.ts.map +0 -1
- package/dist/hq/agent-bridge.d.ts +0 -27
- package/dist/hq/agent-bridge.d.ts.map +0 -1
- package/dist/storage/memory-store.d.ts +0 -119
- package/dist/storage/memory-store.d.ts.map +0 -1
package/dist/storage/index.js
CHANGED
|
@@ -7,7 +7,8 @@ import { createInterface } from "node:readline";
|
|
|
7
7
|
|
|
8
8
|
// src/utils/atomic-write.ts
|
|
9
9
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
10
|
-
import * as
|
|
10
|
+
import * as fs2 from "node:fs/promises";
|
|
11
|
+
import { watch as watchDir } from "node:fs";
|
|
11
12
|
import * as path3 from "node:path";
|
|
12
13
|
|
|
13
14
|
// src/utils/child-env.ts
|
|
@@ -453,14 +454,34 @@ function ulid(seedTime = Date.now()) {
|
|
|
453
454
|
|
|
454
455
|
// src/utils/wstack-paths.ts
|
|
455
456
|
import { createHash } from "node:crypto";
|
|
457
|
+
import * as fs from "node:fs";
|
|
456
458
|
import * as os from "node:os";
|
|
457
459
|
import * as path2 from "node:path";
|
|
460
|
+
function canonicalProjectRoot(absRoot) {
|
|
461
|
+
const checkoutRoot = path2.resolve(absRoot);
|
|
462
|
+
const dotGit = path2.join(checkoutRoot, ".git");
|
|
463
|
+
try {
|
|
464
|
+
if (!fs.statSync(dotGit).isFile()) return checkoutRoot;
|
|
465
|
+
const gitDirLine = fs.readFileSync(dotGit, "utf8").trim();
|
|
466
|
+
const match = /^gitdir:\s*(.+)$/i.exec(gitDirLine);
|
|
467
|
+
if (!match?.[1]) return checkoutRoot;
|
|
468
|
+
const gitDir = path2.resolve(checkoutRoot, match[1].trim());
|
|
469
|
+
const commonDirFile = path2.join(gitDir, "commondir");
|
|
470
|
+
if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;
|
|
471
|
+
const commonDir = path2.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
|
|
472
|
+
if (path2.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
|
|
473
|
+
return path2.dirname(commonDir);
|
|
474
|
+
} catch {
|
|
475
|
+
return checkoutRoot;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
458
478
|
function projectHash(absRoot) {
|
|
459
|
-
return createHash("sha256").update(
|
|
479
|
+
return createHash("sha256").update(canonicalProjectRoot(absRoot)).digest("hex").slice(0, 12);
|
|
460
480
|
}
|
|
461
481
|
function projectSlug(absRoot) {
|
|
462
|
-
const
|
|
463
|
-
const
|
|
482
|
+
const identityRoot = canonicalProjectRoot(absRoot);
|
|
483
|
+
const base = slugify2(path2.basename(identityRoot));
|
|
484
|
+
const hash = createHash("sha256").update(identityRoot).digest("hex").slice(0, 6);
|
|
464
485
|
return `${base}-${hash}`;
|
|
465
486
|
}
|
|
466
487
|
function slugify2(name) {
|
|
@@ -661,16 +682,16 @@ var FsError = class extends WrongStackError {
|
|
|
661
682
|
// src/utils/atomic-write.ts
|
|
662
683
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
663
684
|
const dir = path3.dirname(targetPath);
|
|
664
|
-
await
|
|
685
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
665
686
|
const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes2(6).toString("hex")}.tmp`);
|
|
666
687
|
try {
|
|
667
688
|
if (typeof content === "string") {
|
|
668
|
-
await
|
|
689
|
+
await fs2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
669
690
|
} else {
|
|
670
|
-
await
|
|
691
|
+
await fs2.writeFile(tmp, content, { flag: "wx" });
|
|
671
692
|
}
|
|
672
693
|
try {
|
|
673
|
-
const fh = await
|
|
694
|
+
const fh = await fs2.open(tmp, "r+");
|
|
674
695
|
try {
|
|
675
696
|
await fh.sync();
|
|
676
697
|
} finally {
|
|
@@ -680,35 +701,35 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
680
701
|
}
|
|
681
702
|
let mode;
|
|
682
703
|
try {
|
|
683
|
-
const stat13 = await
|
|
704
|
+
const stat13 = await fs2.stat(targetPath);
|
|
684
705
|
mode = stat13.mode & 511;
|
|
685
706
|
} catch {
|
|
686
707
|
mode = opts.mode;
|
|
687
708
|
}
|
|
688
709
|
if (mode !== void 0) {
|
|
689
|
-
await
|
|
710
|
+
await fs2.chmod(tmp, mode);
|
|
690
711
|
}
|
|
691
712
|
await renameWithRetry(tmp, targetPath);
|
|
692
713
|
if (mode !== void 0 && process.platform === "win32") {
|
|
693
714
|
try {
|
|
694
|
-
await
|
|
715
|
+
await fs2.chmod(targetPath, mode);
|
|
695
716
|
} catch {
|
|
696
717
|
}
|
|
697
718
|
}
|
|
698
719
|
} catch (err) {
|
|
699
720
|
try {
|
|
700
|
-
await
|
|
721
|
+
await fs2.unlink(tmp);
|
|
701
722
|
} catch {
|
|
702
723
|
}
|
|
703
724
|
throw err;
|
|
704
725
|
}
|
|
705
726
|
}
|
|
706
727
|
async function ensureDir(dir) {
|
|
707
|
-
await
|
|
728
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
708
729
|
}
|
|
709
730
|
async function withFileLock(targetPath, fn, opts = {}) {
|
|
710
731
|
const dir = path3.dirname(targetPath);
|
|
711
|
-
await
|
|
732
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
712
733
|
const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
|
|
713
734
|
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
714
735
|
const staleMs = opts.staleMs ?? 3e4;
|
|
@@ -716,26 +737,27 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
716
737
|
let handle;
|
|
717
738
|
for (; ; ) {
|
|
718
739
|
try {
|
|
719
|
-
handle = await
|
|
740
|
+
handle = await fs2.open(lockPath, "wx");
|
|
720
741
|
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
721
742
|
break;
|
|
722
743
|
} catch (err) {
|
|
723
744
|
const code = err.code;
|
|
724
745
|
if (code === "ENOENT") {
|
|
725
|
-
await
|
|
746
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
726
747
|
continue;
|
|
727
748
|
}
|
|
728
|
-
if (code !== "EEXIST") throw err;
|
|
749
|
+
if (code !== "EEXIST" && code !== "EPERM") throw err;
|
|
729
750
|
try {
|
|
730
|
-
const stat13 = await
|
|
751
|
+
const stat13 = await fs2.stat(lockPath);
|
|
731
752
|
if (Date.now() - stat13.mtimeMs > staleMs) {
|
|
732
|
-
await
|
|
753
|
+
await fs2.unlink(lockPath);
|
|
733
754
|
continue;
|
|
734
755
|
}
|
|
735
756
|
} catch {
|
|
736
757
|
continue;
|
|
737
758
|
}
|
|
738
|
-
|
|
759
|
+
const elapsed = Date.now() - started;
|
|
760
|
+
if (elapsed >= timeoutMs) {
|
|
739
761
|
throw new FsError({
|
|
740
762
|
message: `Timed out waiting for file lock: ${targetPath}`,
|
|
741
763
|
code: "FS_ATOMIC_WRITE_FAILED",
|
|
@@ -743,7 +765,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
743
765
|
context: { timeoutMs }
|
|
744
766
|
});
|
|
745
767
|
}
|
|
746
|
-
await
|
|
768
|
+
await waitForLockRelease(lockPath, timeoutMs - elapsed);
|
|
747
769
|
}
|
|
748
770
|
}
|
|
749
771
|
try {
|
|
@@ -754,22 +776,66 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
754
776
|
} catch {
|
|
755
777
|
}
|
|
756
778
|
try {
|
|
757
|
-
await
|
|
779
|
+
await fs2.unlink(lockPath);
|
|
758
780
|
} catch {
|
|
759
781
|
}
|
|
760
782
|
}
|
|
761
783
|
}
|
|
784
|
+
async function waitForLockRelease(lockPath, remainingMs) {
|
|
785
|
+
const parentDir = path3.dirname(lockPath);
|
|
786
|
+
const lockName = path3.basename(lockPath);
|
|
787
|
+
const intervalMs = Math.min(remainingMs, 100);
|
|
788
|
+
return new Promise((resolve11) => {
|
|
789
|
+
let settled = false;
|
|
790
|
+
let watcher = null;
|
|
791
|
+
const timer = setTimeout(() => {
|
|
792
|
+
settled = true;
|
|
793
|
+
watcher?.close();
|
|
794
|
+
resolve11();
|
|
795
|
+
}, intervalMs);
|
|
796
|
+
try {
|
|
797
|
+
watcher = watchDir(parentDir, (eventType, filename) => {
|
|
798
|
+
if (settled) return;
|
|
799
|
+
if (filename === lockName && (eventType === "rename" || eventType === "change")) {
|
|
800
|
+
settled = true;
|
|
801
|
+
clearTimeout(timer);
|
|
802
|
+
watcher?.close();
|
|
803
|
+
resolve11();
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
} catch {
|
|
807
|
+
clearTimeout(timer);
|
|
808
|
+
if (!settled) {
|
|
809
|
+
settled = true;
|
|
810
|
+
setTimeout(resolve11, Math.min(remainingMs, 25));
|
|
811
|
+
}
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
fs2.access(lockPath).then(
|
|
815
|
+
() => {
|
|
816
|
+
},
|
|
817
|
+
() => {
|
|
818
|
+
if (!settled) {
|
|
819
|
+
settled = true;
|
|
820
|
+
clearTimeout(timer);
|
|
821
|
+
watcher?.close();
|
|
822
|
+
resolve11();
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
}
|
|
762
828
|
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
763
829
|
async function renameWithRetry(from, to) {
|
|
764
830
|
if (process.platform !== "win32") {
|
|
765
|
-
await
|
|
831
|
+
await fs2.rename(from, to);
|
|
766
832
|
return;
|
|
767
833
|
}
|
|
768
834
|
const delays = [10, 25, 60, 120, 250];
|
|
769
835
|
let lastErr;
|
|
770
836
|
for (let i = 0; i <= delays.length; i++) {
|
|
771
837
|
try {
|
|
772
|
-
await
|
|
838
|
+
await fs2.rename(from, to);
|
|
773
839
|
return;
|
|
774
840
|
} catch (err) {
|
|
775
841
|
lastErr = err;
|
|
@@ -3700,7 +3766,7 @@ function mergeAdjacentText(blocks) {
|
|
|
3700
3766
|
|
|
3701
3767
|
// src/storage/memory-backend.ts
|
|
3702
3768
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3703
|
-
import * as
|
|
3769
|
+
import * as fs3 from "node:fs/promises";
|
|
3704
3770
|
import * as path10 from "node:path";
|
|
3705
3771
|
|
|
3706
3772
|
// src/types/memory.ts
|
|
@@ -3868,7 +3934,7 @@ var FileMemoryBackend = class {
|
|
|
3868
3934
|
}
|
|
3869
3935
|
async getMtime(file) {
|
|
3870
3936
|
try {
|
|
3871
|
-
const stat13 = await
|
|
3937
|
+
const stat13 = await fs3.stat(file);
|
|
3872
3938
|
return stat13.mtimeMs;
|
|
3873
3939
|
} catch {
|
|
3874
3940
|
return 0;
|
|
@@ -3918,7 +3984,7 @@ var FileMemoryBackend = class {
|
|
|
3918
3984
|
await ensureDir(path10.dirname(file));
|
|
3919
3985
|
let existing = "";
|
|
3920
3986
|
try {
|
|
3921
|
-
existing = await
|
|
3987
|
+
existing = await fs3.readFile(file, "utf8");
|
|
3922
3988
|
} catch {
|
|
3923
3989
|
}
|
|
3924
3990
|
const id = `mem_${Date.now()}_${randomUUID2().slice(0, 8)}`;
|
|
@@ -3936,7 +4002,7 @@ ${line}`;
|
|
|
3936
4002
|
return withFileLock(file, async () => {
|
|
3937
4003
|
let existing;
|
|
3938
4004
|
try {
|
|
3939
|
-
existing = await
|
|
4005
|
+
existing = await fs3.readFile(file, "utf8");
|
|
3940
4006
|
} catch {
|
|
3941
4007
|
return 0;
|
|
3942
4008
|
}
|
|
@@ -3973,7 +4039,7 @@ ${line}`;
|
|
|
3973
4039
|
async readAll(scope, filePath) {
|
|
3974
4040
|
const file = this.resolveFile(filePath, scope);
|
|
3975
4041
|
try {
|
|
3976
|
-
return await
|
|
4042
|
+
return await fs3.readFile(file, "utf8");
|
|
3977
4043
|
} catch {
|
|
3978
4044
|
return "";
|
|
3979
4045
|
}
|
|
@@ -3997,7 +4063,7 @@ ${line}`;
|
|
|
3997
4063
|
const file = this.resolveFile(filePath, scope);
|
|
3998
4064
|
let existing;
|
|
3999
4065
|
try {
|
|
4000
|
-
existing = await
|
|
4066
|
+
existing = await fs3.readFile(file, "utf8");
|
|
4001
4067
|
} catch {
|
|
4002
4068
|
return 0;
|
|
4003
4069
|
}
|
|
@@ -4017,7 +4083,7 @@ ${line}`;
|
|
|
4017
4083
|
const next = lines.join("\n");
|
|
4018
4084
|
const backup = `${file}.bak.${Date.now()}`;
|
|
4019
4085
|
try {
|
|
4020
|
-
await
|
|
4086
|
+
await fs3.copyFile(file, backup);
|
|
4021
4087
|
await pruneConsolidateBackups(file);
|
|
4022
4088
|
} catch {
|
|
4023
4089
|
}
|
|
@@ -4034,11 +4100,11 @@ async function pruneConsolidateBackups(file) {
|
|
|
4034
4100
|
const dir = path10.dirname(file);
|
|
4035
4101
|
const base = path10.basename(file);
|
|
4036
4102
|
const prefix = `${base}.bak.`;
|
|
4037
|
-
const backups = (await
|
|
4103
|
+
const backups = (await fs3.readdir(dir)).filter((name) => name.startsWith(prefix)).sort().reverse();
|
|
4038
4104
|
await Promise.all(
|
|
4039
4105
|
backups.slice(MAX_MEMORY_CONSOLIDATE_BACKUPS).map(async (name) => {
|
|
4040
4106
|
try {
|
|
4041
|
-
await
|
|
4107
|
+
await fs3.unlink(path10.join(dir, name));
|
|
4042
4108
|
} catch {
|
|
4043
4109
|
}
|
|
4044
4110
|
})
|
|
@@ -4053,582 +4119,8 @@ function parseEntries(raw, scope = "project-memory") {
|
|
|
4053
4119
|
return entries.reverse();
|
|
4054
4120
|
}
|
|
4055
4121
|
|
|
4056
|
-
// src/storage/memory-store.ts
|
|
4057
|
-
var MAX_BYTES_TOTAL = 32e3;
|
|
4058
|
-
var DefaultMemoryStore = class {
|
|
4059
|
-
files;
|
|
4060
|
-
events;
|
|
4061
|
-
traceId;
|
|
4062
|
-
backend;
|
|
4063
|
-
/**
|
|
4064
|
-
* Per-scope serialization queue. `remember` / `forget` / `consolidate` /
|
|
4065
|
-
* `clear` are read-modify-write against a single file; without a lock,
|
|
4066
|
-
* two concurrent calls on the same scope can read the same baseline and
|
|
4067
|
-
* the later write silently drops the earlier entry. We chain each
|
|
4068
|
-
* mutation onto the prior promise for the same scope so they run in
|
|
4069
|
-
* issue order. Different scopes still proceed in parallel.
|
|
4070
|
-
*
|
|
4071
|
-
* The chain tracks only the last pending write. If a write fails, its
|
|
4072
|
-
* error is caught and swallowed so the chain stays alive for subsequent
|
|
4073
|
-
* calls. The error is stored in `writeErrors` so callers can learn about
|
|
4074
|
-
* it on the next read operation.
|
|
4075
|
-
*/
|
|
4076
|
-
writeChain = /* @__PURE__ */ new Map();
|
|
4077
|
-
/** Last write error per scope — surfaced as warnings on the next readAll(). */
|
|
4078
|
-
writeErrors = /* @__PURE__ */ new Map();
|
|
4079
|
-
/**
|
|
4080
|
-
* When the global root is a temporary directory (opencode, CI sandboxes),
|
|
4081
|
-
* memory files are also mirrored to the project tree so they survive
|
|
4082
|
-
* session cleanup. The primary path stays in the temp root for isolation;
|
|
4083
|
-
* this backup ensures memory is never lost.
|
|
4084
|
-
*/
|
|
4085
|
-
persistBackup;
|
|
4086
|
-
backupDir;
|
|
4087
|
-
/**
|
|
4088
|
-
* Per-scope tracked byte sizes — incremented on `remember()`, decremented on
|
|
4089
|
-
* `forget()`, recalculated after `consolidate()`. Eliminates the redundant
|
|
4090
|
-
* readAll() call that previously checked the file size after every write.
|
|
4091
|
-
*/
|
|
4092
|
-
_trackedByteSizes = {};
|
|
4093
|
-
/** Result cache for scoreRelevant() — keyed by scope + context hash, TTL 30s. */
|
|
4094
|
-
_scoreCache = /* @__PURE__ */ new Map();
|
|
4095
|
-
/**
|
|
4096
|
-
* Per-entry cached lowercase strings — lazily allocated once and reused
|
|
4097
|
-
* across scoreRelevant() calls so repeated scoring of the same entries skips
|
|
4098
|
-
* re-lowercasing. Keyed by entry object identity; cleared (set to null) on
|
|
4099
|
-
* every mutation (remember/forget/consolidate/clear) via invalidateScoreCaches().
|
|
4100
|
-
*/
|
|
4101
|
-
_cachedLower = null;
|
|
4102
|
-
constructor(opts) {
|
|
4103
|
-
this.files = {
|
|
4104
|
-
"project-agents": opts.paths.inProjectAgentsFile,
|
|
4105
|
-
"project-memory": opts.paths.projectMemory,
|
|
4106
|
-
"user-memory": opts.paths.globalMemory
|
|
4107
|
-
};
|
|
4108
|
-
this.events = opts.events;
|
|
4109
|
-
this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
|
|
4110
|
-
const root = opts.paths.globalRoot.toLowerCase();
|
|
4111
|
-
this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
|
|
4112
|
-
this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
|
|
4113
|
-
}
|
|
4114
|
-
/** Expose the backend for plugin introspection and advanced queries. */
|
|
4115
|
-
getBackend() {
|
|
4116
|
-
return this.backend;
|
|
4117
|
-
}
|
|
4118
|
-
async runSerialized(scope, work) {
|
|
4119
|
-
const prior = this.writeChain.get(scope) ?? Promise.resolve();
|
|
4120
|
-
const next = prior.catch((err) => {
|
|
4121
|
-
this.writeErrors.set(scope, err);
|
|
4122
|
-
}).then(() => work());
|
|
4123
|
-
this.writeChain.set(scope, next);
|
|
4124
|
-
try {
|
|
4125
|
-
return await next;
|
|
4126
|
-
} catch (err) {
|
|
4127
|
-
this.writeErrors.set(scope, err);
|
|
4128
|
-
throw err;
|
|
4129
|
-
} finally {
|
|
4130
|
-
if (this.writeChain.get(scope) === next) {
|
|
4131
|
-
this.writeChain.delete(scope);
|
|
4132
|
-
}
|
|
4133
|
-
}
|
|
4134
|
-
}
|
|
4135
|
-
/**
|
|
4136
|
-
* Recalculate the tracked byte size for a scope by re-reading the file and
|
|
4137
|
-
* summing the serialized byte length of each line. Called after consolidate()
|
|
4138
|
-
* (which modifies the file) to keep the tracker accurate.
|
|
4139
|
-
*/
|
|
4140
|
-
async _recalcTrackedByteSize(scope) {
|
|
4141
|
-
const raw = await this.backend.readAll(scope, this.files[scope]);
|
|
4142
|
-
let total = 0;
|
|
4143
|
-
for (const line of raw.split("\n")) {
|
|
4144
|
-
if (line.trim()) total += Buffer.byteLength(line, "utf8");
|
|
4145
|
-
}
|
|
4146
|
-
this._trackedByteSizes[scope] = total;
|
|
4147
|
-
return total;
|
|
4148
|
-
}
|
|
4149
|
-
async readAll() {
|
|
4150
|
-
const parts = [];
|
|
4151
|
-
for (const scope of ["project-agents", "project-memory", "user-memory"]) {
|
|
4152
|
-
const writeErr = this.writeErrors.get(scope);
|
|
4153
|
-
if (writeErr) {
|
|
4154
|
-
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr.message}`);
|
|
4155
|
-
}
|
|
4156
|
-
const t0 = Date.now();
|
|
4157
|
-
const filePath = this.files[scope];
|
|
4158
|
-
try {
|
|
4159
|
-
const body = await this.backend.readAll(scope, filePath);
|
|
4160
|
-
const dur = Date.now() - t0;
|
|
4161
|
-
this.events?.emit("storage.read", {
|
|
4162
|
-
sessionId: "~memory~",
|
|
4163
|
-
store: "memory",
|
|
4164
|
-
filePath,
|
|
4165
|
-
operation: "readAll",
|
|
4166
|
-
outcome: "success",
|
|
4167
|
-
durationMs: dur,
|
|
4168
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4169
|
-
});
|
|
4170
|
-
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
4171
|
-
|
|
4172
|
-
${body.trim()}`);
|
|
4173
|
-
} catch (err) {
|
|
4174
|
-
const dur = Date.now() - t0;
|
|
4175
|
-
this.events?.emit("storage.read", {
|
|
4176
|
-
sessionId: "~memory~",
|
|
4177
|
-
store: "memory",
|
|
4178
|
-
filePath,
|
|
4179
|
-
operation: "readAll",
|
|
4180
|
-
outcome: "failure",
|
|
4181
|
-
durationMs: dur,
|
|
4182
|
-
error: toErrorMessage(err),
|
|
4183
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4184
|
-
});
|
|
4185
|
-
throw err;
|
|
4186
|
-
}
|
|
4187
|
-
}
|
|
4188
|
-
return parts.join("\n\n");
|
|
4189
|
-
}
|
|
4190
|
-
async read(scope) {
|
|
4191
|
-
const t0 = Date.now();
|
|
4192
|
-
const filePath = this.files[scope];
|
|
4193
|
-
try {
|
|
4194
|
-
const body = await this.backend.readAll(scope, filePath);
|
|
4195
|
-
const dur = Date.now() - t0;
|
|
4196
|
-
this.events?.emit("storage.read", {
|
|
4197
|
-
sessionId: "~memory~",
|
|
4198
|
-
store: "memory",
|
|
4199
|
-
filePath,
|
|
4200
|
-
operation: "read",
|
|
4201
|
-
outcome: "success",
|
|
4202
|
-
durationMs: dur,
|
|
4203
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4204
|
-
});
|
|
4205
|
-
return body;
|
|
4206
|
-
} catch (err) {
|
|
4207
|
-
const dur = Date.now() - t0;
|
|
4208
|
-
this.events?.emit("storage.read", {
|
|
4209
|
-
sessionId: "~memory~",
|
|
4210
|
-
store: "memory",
|
|
4211
|
-
filePath,
|
|
4212
|
-
operation: "read",
|
|
4213
|
-
outcome: "failure",
|
|
4214
|
-
durationMs: dur,
|
|
4215
|
-
error: toErrorMessage(err),
|
|
4216
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4217
|
-
});
|
|
4218
|
-
throw err;
|
|
4219
|
-
}
|
|
4220
|
-
}
|
|
4221
|
-
/**
|
|
4222
|
-
* List entries from a scope, newest first. Delegates to the backend
|
|
4223
|
-
* so graph/semantic backends can return enriched or filtered results.
|
|
4224
|
-
*/
|
|
4225
|
-
async list(scope = "project-memory", limit) {
|
|
4226
|
-
return this.backend.list(scope, this.files[scope], limit);
|
|
4227
|
-
}
|
|
4228
|
-
/**
|
|
4229
|
-
* Find memories related to the given text via graph traversal.
|
|
4230
|
-
* Falls back to content search when no graph backend is available.
|
|
4231
|
-
*/
|
|
4232
|
-
async findRelated(text, scope = "project-memory", limit = 5) {
|
|
4233
|
-
if (this.backend.findRelated) {
|
|
4234
|
-
return this.backend.findRelated(scope, this.files[scope], text, limit);
|
|
4235
|
-
}
|
|
4236
|
-
return this.search(text, scope, limit);
|
|
4237
|
-
}
|
|
4238
|
-
async search(query, scope = "project-memory", limit) {
|
|
4239
|
-
return this.backend.search(scope, query, this.files[scope], limit);
|
|
4240
|
-
}
|
|
4241
|
-
async remember(text, scope = "project-memory", metadata) {
|
|
4242
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
4243
|
-
return this.runSerialized(scope, async () => {
|
|
4244
|
-
const entry = { scope, text, ts, ...metadata };
|
|
4245
|
-
const filePath = this.files[scope];
|
|
4246
|
-
const t0 = Date.now();
|
|
4247
|
-
try {
|
|
4248
|
-
await this.backend.remember(scope, entry, filePath);
|
|
4249
|
-
this.invalidateScoreCaches();
|
|
4250
|
-
const dur = Date.now() - t0;
|
|
4251
|
-
this.events?.emit("storage.write", {
|
|
4252
|
-
sessionId: "~memory~",
|
|
4253
|
-
store: "memory",
|
|
4254
|
-
filePath,
|
|
4255
|
-
operation: "remember",
|
|
4256
|
-
outcome: "success",
|
|
4257
|
-
durationMs: dur,
|
|
4258
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4259
|
-
});
|
|
4260
|
-
} catch (err) {
|
|
4261
|
-
const dur = Date.now() - t0;
|
|
4262
|
-
this.events?.emit("storage.write", {
|
|
4263
|
-
sessionId: "~memory~",
|
|
4264
|
-
store: "memory",
|
|
4265
|
-
filePath,
|
|
4266
|
-
operation: "remember",
|
|
4267
|
-
outcome: "failure",
|
|
4268
|
-
durationMs: dur,
|
|
4269
|
-
error: toErrorMessage(err),
|
|
4270
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4271
|
-
});
|
|
4272
|
-
throw err;
|
|
4273
|
-
}
|
|
4274
|
-
let trackedSize = this._trackedByteSizes[scope];
|
|
4275
|
-
if (trackedSize === void 0) {
|
|
4276
|
-
trackedSize = await this._recalcTrackedByteSize(scope);
|
|
4277
|
-
}
|
|
4278
|
-
if (trackedSize > MAX_BYTES_TOTAL) {
|
|
4279
|
-
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
4280
|
-
if (removed > 0) {
|
|
4281
|
-
this.events?.emit("memory.consolidated", {
|
|
4282
|
-
scope,
|
|
4283
|
-
removed
|
|
4284
|
-
});
|
|
4285
|
-
await this._recalcTrackedByteSize(scope);
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
this._trackedByteSizes[scope] = (this._trackedByteSizes[scope] ?? 0) + Buffer.byteLength(JSON.stringify(entry), "utf8");
|
|
4289
|
-
await this.mirrorBackup(scope);
|
|
4290
|
-
this.events?.emit("memory.remembered", {
|
|
4291
|
-
scope,
|
|
4292
|
-
text,
|
|
4293
|
-
ts,
|
|
4294
|
-
type: entry.type,
|
|
4295
|
-
tags: entry.tags,
|
|
4296
|
-
priority: entry.priority
|
|
4297
|
-
});
|
|
4298
|
-
});
|
|
4299
|
-
}
|
|
4300
|
-
/**
|
|
4301
|
-
* Drop the relevance caches after a mutation. Both the per-context score
|
|
4302
|
-
* cache and the per-entry lowercase cache are keyed on entry objects that a
|
|
4303
|
-
* mutation invalidates, so they must be cleared together when entries change.
|
|
4304
|
-
*/
|
|
4305
|
-
invalidateScoreCaches() {
|
|
4306
|
-
this._scoreCache.clear();
|
|
4307
|
-
this._cachedLower = null;
|
|
4308
|
-
}
|
|
4309
|
-
/**
|
|
4310
|
-
* Score and rank memories by relevance to the current context.
|
|
4311
|
-
* Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
|
|
4312
|
-
*/
|
|
4313
|
-
async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
|
|
4314
|
-
const all = await this.list(scope);
|
|
4315
|
-
if (all.length === 0) return [];
|
|
4316
|
-
const ctxHash = `${scope}|${ctx.currentTask}|${(ctx.activeSkills ?? []).join(",")}|${(ctx.toolNames ?? []).join(",")}`;
|
|
4317
|
-
const now = Date.now();
|
|
4318
|
-
const TTL_MS = 3e4;
|
|
4319
|
-
const cached = this._scoreCache.get(ctxHash);
|
|
4320
|
-
if (cached && cached.expiresAt > now && cached.entries === all) {
|
|
4321
|
-
return cached.scored.slice(0, Math.min(limit, 15));
|
|
4322
|
-
}
|
|
4323
|
-
const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
4324
|
-
const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
|
|
4325
|
-
const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
|
|
4326
|
-
this._cachedLower ??= /* @__PURE__ */ new WeakMap();
|
|
4327
|
-
const lowerCache = this._cachedLower;
|
|
4328
|
-
const scored = [];
|
|
4329
|
-
for (const entry of all) {
|
|
4330
|
-
let score = 0;
|
|
4331
|
-
const reasons = [];
|
|
4332
|
-
let cachedLower = lowerCache.get(entry);
|
|
4333
|
-
if (!cachedLower) {
|
|
4334
|
-
cachedLower = {
|
|
4335
|
-
textLower: entry.text.toLowerCase(),
|
|
4336
|
-
tagsLower: (entry.tags ?? []).map((t) => t.toLowerCase())
|
|
4337
|
-
};
|
|
4338
|
-
lowerCache.set(entry, cachedLower);
|
|
4339
|
-
}
|
|
4340
|
-
const { textLower, tagsLower } = cachedLower;
|
|
4341
|
-
let taskHits = 0;
|
|
4342
|
-
for (const w of taskWords) {
|
|
4343
|
-
if (textLower.includes(w)) {
|
|
4344
|
-
taskHits++;
|
|
4345
|
-
score += 2;
|
|
4346
|
-
}
|
|
4347
|
-
if (tagsLower.some((t) => t.includes(w))) {
|
|
4348
|
-
taskHits++;
|
|
4349
|
-
score += 3;
|
|
4350
|
-
}
|
|
4351
|
-
}
|
|
4352
|
-
if (taskHits > 0) reasons.push(`task match (${taskHits})`);
|
|
4353
|
-
let skillHits = 0;
|
|
4354
|
-
for (const w of skillWords) {
|
|
4355
|
-
if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
|
|
4356
|
-
skillHits++;
|
|
4357
|
-
}
|
|
4358
|
-
}
|
|
4359
|
-
score += skillHits;
|
|
4360
|
-
if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
|
|
4361
|
-
for (const w of toolWords) {
|
|
4362
|
-
if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
|
|
4363
|
-
score += 1;
|
|
4364
|
-
reasons.push(`tool mention: ${w}`);
|
|
4365
|
-
}
|
|
4366
|
-
}
|
|
4367
|
-
switch (entry.priority) {
|
|
4368
|
-
case "critical":
|
|
4369
|
-
score += 5;
|
|
4370
|
-
reasons.push("critical");
|
|
4371
|
-
break;
|
|
4372
|
-
case "high":
|
|
4373
|
-
score += 3;
|
|
4374
|
-
reasons.push("high priority");
|
|
4375
|
-
break;
|
|
4376
|
-
case "medium":
|
|
4377
|
-
score += 1;
|
|
4378
|
-
break;
|
|
4379
|
-
case "low":
|
|
4380
|
-
score -= 2;
|
|
4381
|
-
reasons.push("low priority");
|
|
4382
|
-
break;
|
|
4383
|
-
}
|
|
4384
|
-
switch (entry.type) {
|
|
4385
|
-
case "decision":
|
|
4386
|
-
score += 2;
|
|
4387
|
-
reasons.push("decision");
|
|
4388
|
-
break;
|
|
4389
|
-
case "convention":
|
|
4390
|
-
score += 2;
|
|
4391
|
-
reasons.push("convention");
|
|
4392
|
-
break;
|
|
4393
|
-
case "anti_pattern":
|
|
4394
|
-
score += 3;
|
|
4395
|
-
reasons.push("anti-pattern");
|
|
4396
|
-
break;
|
|
4397
|
-
case "preference":
|
|
4398
|
-
score += 1;
|
|
4399
|
-
reasons.push("preference");
|
|
4400
|
-
break;
|
|
4401
|
-
case "reference":
|
|
4402
|
-
break;
|
|
4403
|
-
case "fact":
|
|
4404
|
-
break;
|
|
4405
|
-
}
|
|
4406
|
-
const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
|
|
4407
|
-
if (ageDays < 1) score += 1;
|
|
4408
|
-
else if (ageDays > 30) score -= 1;
|
|
4409
|
-
if (entry.confidence !== void 0 && entry.confidence < 0.5) {
|
|
4410
|
-
score -= 2;
|
|
4411
|
-
reasons.push("low confidence");
|
|
4412
|
-
}
|
|
4413
|
-
if (entry.lastAccessed) {
|
|
4414
|
-
const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
|
|
4415
|
-
if (hoursSinceAccess < 1) score -= 1;
|
|
4416
|
-
}
|
|
4417
|
-
if (score > 0) {
|
|
4418
|
-
scored.push({
|
|
4419
|
-
...entry,
|
|
4420
|
-
score,
|
|
4421
|
-
matchReason: reasons.join(", ") || "keyword match"
|
|
4422
|
-
});
|
|
4423
|
-
}
|
|
4424
|
-
}
|
|
4425
|
-
scored.sort((a, b) => b.score - a.score);
|
|
4426
|
-
const threshold = 2;
|
|
4427
|
-
const relevant = scored.filter(
|
|
4428
|
-
(s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
|
|
4429
|
-
);
|
|
4430
|
-
this._scoreCache.set(ctxHash, { entries: all, scored: relevant, expiresAt: now + TTL_MS });
|
|
4431
|
-
return relevant.slice(0, Math.min(limit, 15));
|
|
4432
|
-
}
|
|
4433
|
-
async forget(query, scope = "project-memory") {
|
|
4434
|
-
return this.runSerialized(scope, async () => {
|
|
4435
|
-
const filePath = this.files[scope];
|
|
4436
|
-
const t0 = Date.now();
|
|
4437
|
-
let removed = 0;
|
|
4438
|
-
try {
|
|
4439
|
-
removed = await this.backend.forget(scope, query, filePath);
|
|
4440
|
-
this.invalidateScoreCaches();
|
|
4441
|
-
const dur = Date.now() - t0;
|
|
4442
|
-
this.events?.emit("storage.write", {
|
|
4443
|
-
sessionId: "~memory~",
|
|
4444
|
-
store: "memory",
|
|
4445
|
-
filePath,
|
|
4446
|
-
operation: "forget",
|
|
4447
|
-
outcome: "success",
|
|
4448
|
-
durationMs: dur,
|
|
4449
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4450
|
-
});
|
|
4451
|
-
} catch (err) {
|
|
4452
|
-
const dur = Date.now() - t0;
|
|
4453
|
-
this.events?.emit("storage.write", {
|
|
4454
|
-
sessionId: "~memory~",
|
|
4455
|
-
store: "memory",
|
|
4456
|
-
filePath,
|
|
4457
|
-
operation: "forget",
|
|
4458
|
-
outcome: "failure",
|
|
4459
|
-
durationMs: dur,
|
|
4460
|
-
error: toErrorMessage(err),
|
|
4461
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4462
|
-
});
|
|
4463
|
-
throw err;
|
|
4464
|
-
}
|
|
4465
|
-
if (removed > 0) {
|
|
4466
|
-
this.events?.emit("memory.forgotten", {
|
|
4467
|
-
scope,
|
|
4468
|
-
query,
|
|
4469
|
-
removed
|
|
4470
|
-
});
|
|
4471
|
-
await this.mirrorBackup(scope);
|
|
4472
|
-
await this._recalcTrackedByteSize(scope);
|
|
4473
|
-
}
|
|
4474
|
-
return removed;
|
|
4475
|
-
});
|
|
4476
|
-
}
|
|
4477
|
-
async consolidate(scope) {
|
|
4478
|
-
return this.runSerialized(scope, async () => {
|
|
4479
|
-
const filePath = this.files[scope];
|
|
4480
|
-
const t0 = Date.now();
|
|
4481
|
-
let removed = 0;
|
|
4482
|
-
try {
|
|
4483
|
-
removed = await this.backend.consolidate(scope, filePath);
|
|
4484
|
-
this.invalidateScoreCaches();
|
|
4485
|
-
const dur = Date.now() - t0;
|
|
4486
|
-
this.events?.emit("storage.write", {
|
|
4487
|
-
sessionId: "~memory~",
|
|
4488
|
-
store: "memory",
|
|
4489
|
-
filePath,
|
|
4490
|
-
operation: "consolidate",
|
|
4491
|
-
outcome: "success",
|
|
4492
|
-
durationMs: dur,
|
|
4493
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4494
|
-
});
|
|
4495
|
-
} catch (err) {
|
|
4496
|
-
const dur = Date.now() - t0;
|
|
4497
|
-
this.events?.emit("storage.write", {
|
|
4498
|
-
sessionId: "~memory~",
|
|
4499
|
-
store: "memory",
|
|
4500
|
-
filePath,
|
|
4501
|
-
operation: "consolidate",
|
|
4502
|
-
outcome: "failure",
|
|
4503
|
-
durationMs: dur,
|
|
4504
|
-
error: toErrorMessage(err),
|
|
4505
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4506
|
-
});
|
|
4507
|
-
throw err;
|
|
4508
|
-
}
|
|
4509
|
-
if (removed > 0) {
|
|
4510
|
-
this.events?.emit("memory.consolidated", {
|
|
4511
|
-
scope,
|
|
4512
|
-
removed
|
|
4513
|
-
});
|
|
4514
|
-
await this.mirrorBackup(scope);
|
|
4515
|
-
await this._recalcTrackedByteSize(scope);
|
|
4516
|
-
}
|
|
4517
|
-
});
|
|
4518
|
-
}
|
|
4519
|
-
async clear(scope) {
|
|
4520
|
-
if (scope) {
|
|
4521
|
-
await this.runSerialized(scope, async () => {
|
|
4522
|
-
const filePath = this.files[scope];
|
|
4523
|
-
const t0 = Date.now();
|
|
4524
|
-
try {
|
|
4525
|
-
await this.backend.clear(scope, filePath);
|
|
4526
|
-
this.invalidateScoreCaches();
|
|
4527
|
-
const dur = Date.now() - t0;
|
|
4528
|
-
this.events?.emit("storage.write", {
|
|
4529
|
-
sessionId: "~memory~",
|
|
4530
|
-
store: "memory",
|
|
4531
|
-
filePath,
|
|
4532
|
-
operation: "clear",
|
|
4533
|
-
outcome: "success",
|
|
4534
|
-
durationMs: dur,
|
|
4535
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4536
|
-
});
|
|
4537
|
-
} catch (err) {
|
|
4538
|
-
const dur = Date.now() - t0;
|
|
4539
|
-
this.events?.emit("storage.write", {
|
|
4540
|
-
sessionId: "~memory~",
|
|
4541
|
-
store: "memory",
|
|
4542
|
-
filePath,
|
|
4543
|
-
operation: "clear",
|
|
4544
|
-
outcome: "failure",
|
|
4545
|
-
durationMs: dur,
|
|
4546
|
-
error: toErrorMessage(err),
|
|
4547
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4548
|
-
});
|
|
4549
|
-
throw err;
|
|
4550
|
-
}
|
|
4551
|
-
this.events?.emit("memory.cleared", { scope });
|
|
4552
|
-
await this.mirrorBackup(scope);
|
|
4553
|
-
this._trackedByteSizes[scope] = 0;
|
|
4554
|
-
});
|
|
4555
|
-
return;
|
|
4556
|
-
}
|
|
4557
|
-
await Promise.all(
|
|
4558
|
-
["project-agents", "project-memory", "user-memory"].map(
|
|
4559
|
-
async (s) => this.runSerialized(s, async () => {
|
|
4560
|
-
const filePath = this.files[s];
|
|
4561
|
-
const t0 = Date.now();
|
|
4562
|
-
try {
|
|
4563
|
-
await this.backend.clear(s, filePath);
|
|
4564
|
-
const dur = Date.now() - t0;
|
|
4565
|
-
this.events?.emit("storage.write", {
|
|
4566
|
-
sessionId: "~memory~",
|
|
4567
|
-
store: "memory",
|
|
4568
|
-
filePath,
|
|
4569
|
-
operation: "clear",
|
|
4570
|
-
outcome: "success",
|
|
4571
|
-
durationMs: dur,
|
|
4572
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4573
|
-
});
|
|
4574
|
-
} catch (err) {
|
|
4575
|
-
const dur = Date.now() - t0;
|
|
4576
|
-
this.events?.emit("storage.write", {
|
|
4577
|
-
sessionId: "~memory~",
|
|
4578
|
-
store: "memory",
|
|
4579
|
-
filePath,
|
|
4580
|
-
operation: "clear",
|
|
4581
|
-
outcome: "failure",
|
|
4582
|
-
durationMs: dur,
|
|
4583
|
-
error: toErrorMessage(err),
|
|
4584
|
-
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
4585
|
-
});
|
|
4586
|
-
throw err;
|
|
4587
|
-
}
|
|
4588
|
-
this.events?.emit("memory.cleared", { scope: s });
|
|
4589
|
-
await this.mirrorBackup(s);
|
|
4590
|
-
})
|
|
4591
|
-
)
|
|
4592
|
-
);
|
|
4593
|
-
this.invalidateScoreCaches();
|
|
4594
|
-
}
|
|
4595
|
-
/**
|
|
4596
|
-
* Return a new MemoryStore proxy that carries `traceId` on every storage
|
|
4597
|
-
* event. The original store is left unchanged — callers that need a
|
|
4598
|
-
* trace-decorated view (e.g. session-run tools) receive the proxy while
|
|
4599
|
-
* the singleton remains trace-free for boot-time use.
|
|
4600
|
-
*
|
|
4601
|
-
* The proxy implements the full `MemoryStore` interface; all other
|
|
4602
|
-
* properties (backend, etc.) are delegated to the original store.
|
|
4603
|
-
*/
|
|
4604
|
-
withTraceId(traceId) {
|
|
4605
|
-
this.traceId = traceId;
|
|
4606
|
-
return this;
|
|
4607
|
-
}
|
|
4608
|
-
async mirrorBackup(scope) {
|
|
4609
|
-
if (!this.persistBackup || scope === "project-agents") return;
|
|
4610
|
-
try {
|
|
4611
|
-
const content = await this.backend.readAll(scope, this.files[scope]);
|
|
4612
|
-
const { writeFile: writeFile7, mkdir: mkdir7 } = await import("node:fs/promises");
|
|
4613
|
-
await mkdir7(this.backupDir, { recursive: true });
|
|
4614
|
-
await writeFile7(`${this.backupDir}/${scope}.md`, content, "utf8");
|
|
4615
|
-
} catch {
|
|
4616
|
-
}
|
|
4617
|
-
}
|
|
4618
|
-
};
|
|
4619
|
-
function labelOf(scope) {
|
|
4620
|
-
switch (scope) {
|
|
4621
|
-
case "project-agents":
|
|
4622
|
-
return "Project AGENTS.md";
|
|
4623
|
-
case "project-memory":
|
|
4624
|
-
return "Project memory";
|
|
4625
|
-
case "user-memory":
|
|
4626
|
-
return "User memory";
|
|
4627
|
-
}
|
|
4628
|
-
}
|
|
4629
|
-
|
|
4630
4122
|
// src/storage/memory-graph-backend.ts
|
|
4631
|
-
import * as
|
|
4123
|
+
import * as fs4 from "node:fs/promises";
|
|
4632
4124
|
var GraphMemoryBackend = class _GraphMemoryBackend {
|
|
4633
4125
|
kind = "graph";
|
|
4634
4126
|
file;
|
|
@@ -4796,7 +4288,7 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
|
|
|
4796
4288
|
this.loadedScope = scope;
|
|
4797
4289
|
this.loaded = true;
|
|
4798
4290
|
try {
|
|
4799
|
-
await
|
|
4291
|
+
await fs4.unlink(this.graphFile);
|
|
4800
4292
|
} catch {
|
|
4801
4293
|
}
|
|
4802
4294
|
}
|
|
@@ -4836,7 +4328,7 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
|
|
|
4836
4328
|
async loadGraph(scope) {
|
|
4837
4329
|
if (this.loaded && this.loadedScope === scope) return;
|
|
4838
4330
|
try {
|
|
4839
|
-
const raw = await
|
|
4331
|
+
const raw = await fs4.readFile(this.graphFile, "utf8");
|
|
4840
4332
|
const data = JSON.parse(raw);
|
|
4841
4333
|
this.nodes = new Map(data.nodes);
|
|
4842
4334
|
this.edges = data.edges;
|
|
@@ -4858,10 +4350,10 @@ var GraphMemoryBackend = class _GraphMemoryBackend {
|
|
|
4858
4350
|
edges: this.edges
|
|
4859
4351
|
};
|
|
4860
4352
|
const dir = this.graphFile.substring(0, this.graphFile.lastIndexOf("/"));
|
|
4861
|
-
await
|
|
4353
|
+
await fs4.mkdir(dir, { recursive: true });
|
|
4862
4354
|
const tmp = `${this.graphFile}.tmp`;
|
|
4863
|
-
await
|
|
4864
|
-
await
|
|
4355
|
+
await fs4.writeFile(tmp, JSON.stringify(data));
|
|
4356
|
+
await fs4.rename(tmp, this.graphFile);
|
|
4865
4357
|
} catch {
|
|
4866
4358
|
}
|
|
4867
4359
|
}
|
|
@@ -4979,7 +4471,7 @@ function simpleHash(s) {
|
|
|
4979
4471
|
}
|
|
4980
4472
|
|
|
4981
4473
|
// src/utils/instruction-file.ts
|
|
4982
|
-
import { readFileSync, statSync } from "node:fs";
|
|
4474
|
+
import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
4983
4475
|
import * as path11 from "node:path";
|
|
4984
4476
|
import { fileURLToPath } from "node:url";
|
|
4985
4477
|
var textCache = /* @__PURE__ */ new Map();
|
|
@@ -4990,7 +4482,7 @@ function readBundledInstructionText(relativePath) {
|
|
|
4990
4482
|
let resolved = "";
|
|
4991
4483
|
for (const root of instructionRootCandidates()) {
|
|
4992
4484
|
try {
|
|
4993
|
-
resolved =
|
|
4485
|
+
resolved = readFileSync2(path11.join(root, relativePath), "utf8").trimEnd();
|
|
4994
4486
|
break;
|
|
4995
4487
|
} catch {
|
|
4996
4488
|
}
|
|
@@ -5017,7 +4509,7 @@ function instructionRootCandidates() {
|
|
|
5017
4509
|
}
|
|
5018
4510
|
function isDirectory(candidate) {
|
|
5019
4511
|
try {
|
|
5020
|
-
return
|
|
4512
|
+
return statSync2(candidate).isDirectory();
|
|
5021
4513
|
} catch {
|
|
5022
4514
|
return false;
|
|
5023
4515
|
}
|
|
@@ -5043,12 +4535,14 @@ var SessionMemoryConsolidator = class {
|
|
|
5043
4535
|
model;
|
|
5044
4536
|
minIterations;
|
|
5045
4537
|
maxExistingEntries;
|
|
4538
|
+
oneShotOrchestrator;
|
|
5046
4539
|
constructor(opts) {
|
|
5047
4540
|
this.memoryStore = opts.memoryStore;
|
|
5048
4541
|
this.provider = opts.provider;
|
|
5049
4542
|
this.model = opts.model;
|
|
5050
4543
|
this.minIterations = opts.minIterations ?? 2;
|
|
5051
4544
|
this.maxExistingEntries = opts.maxExistingEntries ?? 15;
|
|
4545
|
+
this.oneShotOrchestrator = opts.oneShotOrchestrator;
|
|
5052
4546
|
}
|
|
5053
4547
|
afterRun = (ctx, result) => {
|
|
5054
4548
|
if (result.status !== "done") return;
|
|
@@ -5067,19 +4561,32 @@ var SessionMemoryConsolidator = class {
|
|
|
5067
4561
|
_iterations,
|
|
5068
4562
|
existingEntries
|
|
5069
4563
|
);
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
{
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
}
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
4564
|
+
let text;
|
|
4565
|
+
if (this.oneShotOrchestrator) {
|
|
4566
|
+
const oneShotResult = await this.oneShotOrchestrator.call({
|
|
4567
|
+
system: prompt,
|
|
4568
|
+
userPrompt: "Review the session and return memory operations as JSON.",
|
|
4569
|
+
model: _model ?? "deepseek-chat",
|
|
4570
|
+
maxTokens: 500,
|
|
4571
|
+
timeoutMs: 15e3,
|
|
4572
|
+
fallbackProfile: "memory"
|
|
4573
|
+
});
|
|
4574
|
+
text = oneShotResult.text;
|
|
4575
|
+
} else {
|
|
4576
|
+
const signal = AbortSignal.timeout(15e3);
|
|
4577
|
+
const response = await provider.complete(
|
|
4578
|
+
{
|
|
4579
|
+
model: _model,
|
|
4580
|
+
system: [{ type: "text", text: prompt }],
|
|
4581
|
+
messages: [
|
|
4582
|
+
{ role: "user", content: "Review the session and return memory operations as JSON." }
|
|
4583
|
+
],
|
|
4584
|
+
maxTokens: 500
|
|
4585
|
+
},
|
|
4586
|
+
{ signal }
|
|
4587
|
+
);
|
|
4588
|
+
text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
|
|
4589
|
+
}
|
|
5083
4590
|
if (!text) return;
|
|
5084
4591
|
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
5085
4592
|
if (!jsonMatch) return;
|
|
@@ -5209,7 +4716,7 @@ function deepFreeze(obj) {
|
|
|
5209
4716
|
|
|
5210
4717
|
// src/storage/provider-config-watcher.ts
|
|
5211
4718
|
import * as syncFs from "node:fs";
|
|
5212
|
-
import * as
|
|
4719
|
+
import * as fs5 from "node:fs/promises";
|
|
5213
4720
|
import * as path12 from "node:path";
|
|
5214
4721
|
|
|
5215
4722
|
// src/security/config-secrets.ts
|
|
@@ -5244,7 +4751,7 @@ function walk(node, vault, transform) {
|
|
|
5244
4751
|
}
|
|
5245
4752
|
return out;
|
|
5246
4753
|
}
|
|
5247
|
-
var SECRET_KEY_PATTERN = /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key)/i;
|
|
4754
|
+
var SECRET_KEY_PATTERN = /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key|token\b)/i;
|
|
5248
4755
|
var NON_SECRET_OVERRIDES = /* @__PURE__ */ new Set(["publickey", "public_key"]);
|
|
5249
4756
|
function isSecretField(name) {
|
|
5250
4757
|
const lc = name.toLowerCase();
|
|
@@ -5256,7 +4763,7 @@ function isSecretField(name) {
|
|
|
5256
4763
|
async function readProviderSnapshot(configPath, vault, warn) {
|
|
5257
4764
|
let raw;
|
|
5258
4765
|
try {
|
|
5259
|
-
raw = await
|
|
4766
|
+
raw = await fs5.readFile(configPath, "utf8");
|
|
5260
4767
|
} catch (err) {
|
|
5261
4768
|
if (err.code !== "ENOENT") {
|
|
5262
4769
|
warn?.(`Could not read ${configPath}: ${err.message}`);
|
|
@@ -5345,24 +4852,24 @@ function watchProviderConfig(configPath, vault, onChange, opts = {}) {
|
|
|
5345
4852
|
}
|
|
5346
4853
|
|
|
5347
4854
|
// src/storage/config-loader.ts
|
|
5348
|
-
import * as
|
|
4855
|
+
import * as fs6 from "node:fs/promises";
|
|
5349
4856
|
import * as path13 from "node:path";
|
|
5350
4857
|
|
|
5351
4858
|
// src/types/config.ts
|
|
5352
4859
|
var DEFAULT_TUI_THINKING_WORD = "thinking";
|
|
5353
4860
|
|
|
5354
4861
|
// src/types/context-window.ts
|
|
5355
|
-
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "
|
|
4862
|
+
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "frugal";
|
|
5356
4863
|
var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
5357
4864
|
{
|
|
5358
4865
|
id: "balanced",
|
|
5359
4866
|
name: "Balanced",
|
|
5360
4867
|
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
5361
|
-
thresholds: { warn: 0.
|
|
4868
|
+
thresholds: { warn: 0.5, soft: 0.65, hard: 0.8 },
|
|
5362
4869
|
aggressiveOn: "soft",
|
|
5363
|
-
preserveK:
|
|
5364
|
-
eliseThreshold:
|
|
5365
|
-
targetLoad: 0.
|
|
4870
|
+
preserveK: 8,
|
|
4871
|
+
eliseThreshold: 1e3,
|
|
4872
|
+
targetLoad: 0.55
|
|
5366
4873
|
},
|
|
5367
4874
|
{
|
|
5368
4875
|
id: "frugal",
|
|
@@ -5413,7 +4920,7 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
|
5413
4920
|
descriptionMode: Object.freeze({}),
|
|
5414
4921
|
disabledTools: Object.freeze([]),
|
|
5415
4922
|
autoExtendLimit: true,
|
|
5416
|
-
restrictToProjectRoot:
|
|
4923
|
+
restrictToProjectRoot: true,
|
|
5417
4924
|
loopDetection: Object.freeze({
|
|
5418
4925
|
mode: "steer-then-cut",
|
|
5419
4926
|
steerThreshold: 3,
|
|
@@ -5423,8 +4930,8 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
|
5423
4930
|
})
|
|
5424
4931
|
});
|
|
5425
4932
|
var DEFAULT_CONTEXT_CONFIG = Object.freeze({
|
|
5426
|
-
preserveK:
|
|
5427
|
-
eliseThreshold:
|
|
4933
|
+
preserveK: 8,
|
|
4934
|
+
eliseThreshold: 1e3
|
|
5428
4935
|
});
|
|
5429
4936
|
var DEFAULT_AUTONOMY_CONFIG = Object.freeze({
|
|
5430
4937
|
autoProceedDelayMs: 45e3
|
|
@@ -5516,12 +5023,11 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
5516
5023
|
mcpServers: {},
|
|
5517
5024
|
fallbackAuto: true,
|
|
5518
5025
|
maxConcurrent: 4,
|
|
5519
|
-
// YOLO
|
|
5520
|
-
// prompt
|
|
5521
|
-
//
|
|
5522
|
-
//
|
|
5523
|
-
|
|
5524
|
-
yolo: true,
|
|
5026
|
+
// YOLO opt-in: users who want auto-approve behaviour set `yolo: true`.
|
|
5027
|
+
// Off by default so prompt injection in repository content, fetched
|
|
5028
|
+
// content, or mailbox messages cannot induce arbitrary shell calls or
|
|
5029
|
+
// write operations without human confirmation.
|
|
5030
|
+
yolo: false,
|
|
5525
5031
|
nextPrediction: false,
|
|
5526
5032
|
hints: true,
|
|
5527
5033
|
debugStream: false,
|
|
@@ -5546,7 +5052,7 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
5546
5052
|
terminalTitleAnimation: true,
|
|
5547
5053
|
// Mirrored from the top-level yolo default so the autonomy subsystem
|
|
5548
5054
|
// (which reads autonomy.yolo) stays consistent with config.yolo.
|
|
5549
|
-
yolo:
|
|
5055
|
+
yolo: false,
|
|
5550
5056
|
streamFleet: true,
|
|
5551
5057
|
chime: false,
|
|
5552
5058
|
confirmExit: true,
|
|
@@ -5998,7 +5504,7 @@ var DefaultConfigLoader = class {
|
|
|
5998
5504
|
await withFileLock(fp, async () => {
|
|
5999
5505
|
let parsed;
|
|
6000
5506
|
try {
|
|
6001
|
-
const raw = await
|
|
5507
|
+
const raw = await fs6.readFile(fp, "utf8");
|
|
6002
5508
|
const result = safeParse(raw);
|
|
6003
5509
|
if (!result.ok || !isPlainRecord(result.value)) {
|
|
6004
5510
|
return;
|
|
@@ -6107,7 +5613,7 @@ var DefaultConfigLoader = class {
|
|
|
6107
5613
|
const fp = this.paths.syncConfig;
|
|
6108
5614
|
const t0 = Date.now();
|
|
6109
5615
|
try {
|
|
6110
|
-
const raw = await
|
|
5616
|
+
const raw = await fs6.readFile(fp, "utf8");
|
|
6111
5617
|
const parsed = safeParse(raw);
|
|
6112
5618
|
if (!parsed.ok || !parsed.value) {
|
|
6113
5619
|
this.events?.emit("storage.read", {
|
|
@@ -6169,7 +5675,7 @@ var DefaultConfigLoader = class {
|
|
|
6169
5675
|
const t0 = Date.now();
|
|
6170
5676
|
let mtimeMs = null;
|
|
6171
5677
|
try {
|
|
6172
|
-
const stat13 = await
|
|
5678
|
+
const stat13 = await fs6.stat(file);
|
|
6173
5679
|
mtimeMs = stat13.mtimeMs;
|
|
6174
5680
|
const cached = this.jsonCache.get(file);
|
|
6175
5681
|
if (cached && cached.mtimeMs === mtimeMs) {
|
|
@@ -6198,7 +5704,7 @@ var DefaultConfigLoader = class {
|
|
|
6198
5704
|
}
|
|
6199
5705
|
let raw;
|
|
6200
5706
|
try {
|
|
6201
|
-
raw = await
|
|
5707
|
+
raw = await fs6.readFile(file, "utf8");
|
|
6202
5708
|
} catch (err) {
|
|
6203
5709
|
if (err.code !== "ENOENT") {
|
|
6204
5710
|
this.events?.emit("storage.read", {
|
|
@@ -6500,7 +6006,7 @@ function defaultIsPidAlive(pid) {
|
|
|
6500
6006
|
}
|
|
6501
6007
|
|
|
6502
6008
|
// src/storage/session-reader.ts
|
|
6503
|
-
import * as
|
|
6009
|
+
import * as fs7 from "node:fs/promises";
|
|
6504
6010
|
var DefaultSessionReader = class _DefaultSessionReader {
|
|
6505
6011
|
store;
|
|
6506
6012
|
eventCache = /* @__PURE__ */ new Map();
|
|
@@ -6518,7 +6024,7 @@ var DefaultSessionReader = class _DefaultSessionReader {
|
|
|
6518
6024
|
const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
|
|
6519
6025
|
let mtimeMs = null;
|
|
6520
6026
|
try {
|
|
6521
|
-
const stat13 = await
|
|
6027
|
+
const stat13 = await fs7.stat(sessionPath);
|
|
6522
6028
|
mtimeMs = stat13.mtimeMs;
|
|
6523
6029
|
} catch {
|
|
6524
6030
|
this.eventCache.delete(sessionId);
|
|
@@ -6882,7 +6388,7 @@ function renderPlainText(meta, events) {
|
|
|
6882
6388
|
|
|
6883
6389
|
// src/storage/annotations-store.ts
|
|
6884
6390
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
6885
|
-
import * as
|
|
6391
|
+
import * as fs8 from "node:fs/promises";
|
|
6886
6392
|
var FILE_VERSION = 1;
|
|
6887
6393
|
var MAX_TEXT_LENGTH = 2e3;
|
|
6888
6394
|
var MAX_ANNOTATIONS = 1e3;
|
|
@@ -7109,7 +6615,7 @@ var AnnotationsStore = class {
|
|
|
7109
6615
|
const fp = this.filePath(sessionId);
|
|
7110
6616
|
let raw;
|
|
7111
6617
|
try {
|
|
7112
|
-
raw = await
|
|
6618
|
+
raw = await fs8.readFile(fp, "utf8");
|
|
7113
6619
|
} catch (err) {
|
|
7114
6620
|
if (err.code === "ENOENT") return null;
|
|
7115
6621
|
throw err;
|
|
@@ -7146,7 +6652,7 @@ var AnnotationsStore = class {
|
|
|
7146
6652
|
};
|
|
7147
6653
|
|
|
7148
6654
|
// src/storage/replay-log-store.ts
|
|
7149
|
-
import * as
|
|
6655
|
+
import * as fs9 from "node:fs/promises";
|
|
7150
6656
|
import * as path15 from "node:path";
|
|
7151
6657
|
|
|
7152
6658
|
// src/replay/hash.ts
|
|
@@ -7236,12 +6742,12 @@ var ReplayLogStore = class {
|
|
|
7236
6742
|
const line = JSON.stringify(entry) + "\n";
|
|
7237
6743
|
let offset2 = 0;
|
|
7238
6744
|
try {
|
|
7239
|
-
const stat13 = await
|
|
6745
|
+
const stat13 = await fs9.stat(fp);
|
|
7240
6746
|
offset2 = stat13.size;
|
|
7241
6747
|
} catch (err) {
|
|
7242
6748
|
if (err.code !== "ENOENT") throw err;
|
|
7243
6749
|
}
|
|
7244
|
-
await
|
|
6750
|
+
await fs9.appendFile(fp, line, "utf8");
|
|
7245
6751
|
cache.set(hash, { entry, offset: offset2, length: Buffer.byteLength(line, "utf8") });
|
|
7246
6752
|
this.diskCount.set(input.sessionId, currentCount + 1);
|
|
7247
6753
|
this.events?.emit("storage.write", {
|
|
@@ -7369,7 +6875,7 @@ var ReplayLogStore = class {
|
|
|
7369
6875
|
const scan = async (dir, prefix, depth) => {
|
|
7370
6876
|
let entries;
|
|
7371
6877
|
try {
|
|
7372
|
-
entries = await
|
|
6878
|
+
entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
7373
6879
|
} catch (err) {
|
|
7374
6880
|
if (depth === 0 && err.code !== "ENOENT") {
|
|
7375
6881
|
console.warn(JSON.stringify({
|
|
@@ -7407,7 +6913,7 @@ var ReplayLogStore = class {
|
|
|
7407
6913
|
return sessionScopedPath(this.dir, sessionId, ".replay.jsonl");
|
|
7408
6914
|
}
|
|
7409
6915
|
async countEntries(filePath) {
|
|
7410
|
-
const handle = await
|
|
6916
|
+
const handle = await fs9.open(filePath, "r");
|
|
7411
6917
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
7412
6918
|
let count = 0;
|
|
7413
6919
|
let hasNonWhitespace2 = false;
|
|
@@ -7448,7 +6954,7 @@ var ReplayLogStore = class {
|
|
|
7448
6954
|
async readAll(sessionId) {
|
|
7449
6955
|
const fp = this.filePath(sessionId);
|
|
7450
6956
|
try {
|
|
7451
|
-
const raw = await
|
|
6957
|
+
const raw = await fs9.readFile(fp, "utf8");
|
|
7452
6958
|
const out = [];
|
|
7453
6959
|
for (const line of raw.split("\n")) {
|
|
7454
6960
|
if (!line.trim()) continue;
|
|
@@ -7484,7 +6990,7 @@ var ReplayLogStore = class {
|
|
|
7484
6990
|
const fp = this.filePath(sessionId);
|
|
7485
6991
|
cache = /* @__PURE__ */ new Map();
|
|
7486
6992
|
try {
|
|
7487
|
-
const handle = await
|
|
6993
|
+
const handle = await fs9.open(fp, "r");
|
|
7488
6994
|
const CHUNK = 64 * 1024;
|
|
7489
6995
|
const buffer = Buffer.alloc(CHUNK);
|
|
7490
6996
|
let leftover = "";
|
|
@@ -7535,7 +7041,7 @@ var ReplayLogStore = class {
|
|
|
7535
7041
|
async hydrateEntry(sessionId, hash, location) {
|
|
7536
7042
|
if (location.entry) return location.entry;
|
|
7537
7043
|
const fp = this.filePath(sessionId);
|
|
7538
|
-
const handle = await
|
|
7044
|
+
const handle = await fs9.open(fp, "r");
|
|
7539
7045
|
try {
|
|
7540
7046
|
const buffer = Buffer.alloc(location.length);
|
|
7541
7047
|
const { bytesRead } = await handle.read(buffer, 0, location.length, location.offset);
|
|
@@ -7562,7 +7068,7 @@ var ReplayLogStore = class {
|
|
|
7562
7068
|
};
|
|
7563
7069
|
|
|
7564
7070
|
// src/storage/session-recovery.ts
|
|
7565
|
-
import * as
|
|
7071
|
+
import * as fs10 from "node:fs/promises";
|
|
7566
7072
|
import * as path16 from "node:path";
|
|
7567
7073
|
var SessionRecovery = class {
|
|
7568
7074
|
constructor(dir) {
|
|
@@ -7589,7 +7095,7 @@ var SessionRecovery = class {
|
|
|
7589
7095
|
const fp = this.filePath(sessionId);
|
|
7590
7096
|
let stat13;
|
|
7591
7097
|
try {
|
|
7592
|
-
stat13 = await
|
|
7098
|
+
stat13 = await fs10.stat(fp);
|
|
7593
7099
|
} catch (err) {
|
|
7594
7100
|
if (err.code === "ENOENT") return null;
|
|
7595
7101
|
return null;
|
|
@@ -7623,7 +7129,7 @@ var SessionRecovery = class {
|
|
|
7623
7129
|
const fp = this.filePath(sessionId);
|
|
7624
7130
|
let raw;
|
|
7625
7131
|
try {
|
|
7626
|
-
raw = await
|
|
7132
|
+
raw = await fs10.readFile(fp, "utf8");
|
|
7627
7133
|
} catch (err) {
|
|
7628
7134
|
if (err.code === "ENOENT") return null;
|
|
7629
7135
|
return null;
|
|
@@ -7668,7 +7174,7 @@ var SessionRecovery = class {
|
|
|
7668
7174
|
const collect = async (dir, prefix, depth) => {
|
|
7669
7175
|
let entries;
|
|
7670
7176
|
try {
|
|
7671
|
-
entries = await
|
|
7177
|
+
entries = await fs10.readdir(dir, { withFileTypes: true });
|
|
7672
7178
|
} catch {
|
|
7673
7179
|
return;
|
|
7674
7180
|
}
|
|
@@ -7719,7 +7225,7 @@ function hasNonWhitespace(line) {
|
|
|
7719
7225
|
}
|
|
7720
7226
|
async function scanLatestLifecycleBoundary(filePath, size) {
|
|
7721
7227
|
const CHUNK_SIZE = 64 * 1024;
|
|
7722
|
-
const handle = await
|
|
7228
|
+
const handle = await fs10.open(filePath, "r");
|
|
7723
7229
|
let position = size;
|
|
7724
7230
|
let laterLineFragment = Buffer.alloc(0);
|
|
7725
7231
|
let latestBoundary = null;
|
|
@@ -7758,7 +7264,7 @@ async function scanLatestLifecycleBoundary(filePath, size) {
|
|
|
7758
7264
|
|
|
7759
7265
|
// src/storage/tool-audit-log.ts
|
|
7760
7266
|
import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
|
|
7761
|
-
import * as
|
|
7267
|
+
import * as fs11 from "node:fs/promises";
|
|
7762
7268
|
var GENESIS_PREV = "0".repeat(64);
|
|
7763
7269
|
var DEFAULT_FSYNC_EVERY = 100;
|
|
7764
7270
|
var ToolAuditLog = class {
|
|
@@ -7828,9 +7334,9 @@ var ToolAuditLog = class {
|
|
|
7828
7334
|
isError: input.isError,
|
|
7829
7335
|
index
|
|
7830
7336
|
};
|
|
7831
|
-
await
|
|
7337
|
+
await fs11.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
|
|
7832
7338
|
try {
|
|
7833
|
-
const st = await
|
|
7339
|
+
const st = await fs11.stat(fp);
|
|
7834
7340
|
this.tailStat.set(input.sessionId, { mtimeMs: st.mtimeMs, size: st.size });
|
|
7835
7341
|
} catch {
|
|
7836
7342
|
}
|
|
@@ -7877,7 +7383,7 @@ var ToolAuditLog = class {
|
|
|
7877
7383
|
const cachedStat = this.tailStat.get(sessionId);
|
|
7878
7384
|
if (cachedHash !== void 0 && cachedIndex !== void 0 && cachedStat) {
|
|
7879
7385
|
try {
|
|
7880
|
-
const st = await
|
|
7386
|
+
const st = await fs11.stat(fp);
|
|
7881
7387
|
if (st.mtimeMs === cachedStat.mtimeMs && st.size === cachedStat.size) {
|
|
7882
7388
|
return { prevHash: cachedHash, nextIndex: cachedIndex };
|
|
7883
7389
|
}
|
|
@@ -7898,7 +7404,7 @@ var ToolAuditLog = class {
|
|
|
7898
7404
|
this.tailHash.set(sessionId, prevHash);
|
|
7899
7405
|
this.tailIndex.set(sessionId, nextIndex);
|
|
7900
7406
|
try {
|
|
7901
|
-
const st = await
|
|
7407
|
+
const st = await fs11.stat(fp);
|
|
7902
7408
|
this.tailStat.set(sessionId, { mtimeMs: st.mtimeMs, size: st.size });
|
|
7903
7409
|
} catch {
|
|
7904
7410
|
}
|
|
@@ -8019,7 +7525,7 @@ var ToolAuditLog = class {
|
|
|
8019
7525
|
async readAll(sessionId) {
|
|
8020
7526
|
const fp = this.filePath(sessionId);
|
|
8021
7527
|
try {
|
|
8022
|
-
const raw = await
|
|
7528
|
+
const raw = await fs11.readFile(fp, "utf8");
|
|
8023
7529
|
const out = [];
|
|
8024
7530
|
for (const line of raw.split("\n")) {
|
|
8025
7531
|
if (!line.trim()) continue;
|
|
@@ -8057,7 +7563,7 @@ var ToolAuditLog = class {
|
|
|
8057
7563
|
}
|
|
8058
7564
|
async sync(sessionId, fp) {
|
|
8059
7565
|
try {
|
|
8060
|
-
const fh = await
|
|
7566
|
+
const fh = await fs11.open(fp, "r+");
|
|
8061
7567
|
try {
|
|
8062
7568
|
await fh.sync();
|
|
8063
7569
|
} finally {
|
|
@@ -8195,7 +7701,7 @@ var SessionAnalyzer = class {
|
|
|
8195
7701
|
};
|
|
8196
7702
|
|
|
8197
7703
|
// src/session-registry.ts
|
|
8198
|
-
import * as
|
|
7704
|
+
import * as fs12 from "node:fs/promises";
|
|
8199
7705
|
import * as path17 from "node:path";
|
|
8200
7706
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
8201
7707
|
var REGISTRY_FILE = "session-registry.json";
|
|
@@ -8392,7 +7898,7 @@ var SessionRegistry = class {
|
|
|
8392
7898
|
}
|
|
8393
7899
|
async readAndPrune() {
|
|
8394
7900
|
try {
|
|
8395
|
-
const raw = await
|
|
7901
|
+
const raw = await fs12.readFile(this.filePath, "utf8");
|
|
8396
7902
|
const registry = parseRegistry(raw);
|
|
8397
7903
|
const now = Date.now();
|
|
8398
7904
|
let pruned = false;
|
|
@@ -8445,11 +7951,11 @@ var SessionRegistry = class {
|
|
|
8445
7951
|
const retryDelayMs = 20;
|
|
8446
7952
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
8447
7953
|
try {
|
|
8448
|
-
await
|
|
8449
|
-
let lockHandle = await
|
|
7954
|
+
await fs12.mkdir(path17.dirname(this.filePath), { recursive: true });
|
|
7955
|
+
let lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
|
|
8450
7956
|
if (!lockHandle) {
|
|
8451
7957
|
if (await this.breakStaleLock(lockPath)) {
|
|
8452
|
-
lockHandle = await
|
|
7958
|
+
lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
|
|
8453
7959
|
}
|
|
8454
7960
|
if (!lockHandle) {
|
|
8455
7961
|
await new Promise((r) => setTimeout(r, retryDelayMs * (attempt + 1)));
|
|
@@ -8458,14 +7964,14 @@ var SessionRegistry = class {
|
|
|
8458
7964
|
}
|
|
8459
7965
|
try {
|
|
8460
7966
|
await lockHandle.writeFile(String(process.pid)).catch(() => void 0);
|
|
8461
|
-
const raw = await
|
|
7967
|
+
const raw = await fs12.readFile(this.filePath, "utf8").catch(() => "{}");
|
|
8462
7968
|
const registry = parseRegistry(raw);
|
|
8463
7969
|
fn(registry);
|
|
8464
7970
|
await this.writeAtomicLocked(registry);
|
|
8465
7971
|
return;
|
|
8466
7972
|
} finally {
|
|
8467
7973
|
await lockHandle.close();
|
|
8468
|
-
await
|
|
7974
|
+
await fs12.unlink(lockPath).catch(() => void 0);
|
|
8469
7975
|
}
|
|
8470
7976
|
} catch {
|
|
8471
7977
|
return;
|
|
@@ -8482,16 +7988,16 @@ var SessionRegistry = class {
|
|
|
8482
7988
|
async breakStaleLock(lockPath) {
|
|
8483
7989
|
try {
|
|
8484
7990
|
const [stat13, content] = await Promise.all([
|
|
8485
|
-
|
|
7991
|
+
fs12.stat(lockPath),
|
|
8486
7992
|
/* v8 ignore start -- best-effort lock-content read; .catch only fires if the lock vanished */
|
|
8487
|
-
|
|
7993
|
+
fs12.readFile(lockPath, "utf8").catch(() => "")
|
|
8488
7994
|
/* v8 ignore stop */
|
|
8489
7995
|
]);
|
|
8490
7996
|
const ageMs = Date.now() - stat13.mtimeMs;
|
|
8491
7997
|
const ownerPid = Number.parseInt(content.trim(), 10);
|
|
8492
7998
|
const ownerDead = Number.isInteger(ownerPid) && ownerPid > 0 && ownerPid !== process.pid && !pidAlive(ownerPid);
|
|
8493
7999
|
if (ownerDead || ageMs > STALE_LOCK_MS) {
|
|
8494
|
-
await
|
|
8000
|
+
await fs12.unlink(lockPath).catch(() => void 0);
|
|
8495
8001
|
return true;
|
|
8496
8002
|
}
|
|
8497
8003
|
return false;
|
|
@@ -8514,16 +8020,16 @@ var SessionRegistry = class {
|
|
|
8514
8020
|
`.${path17.basename(this.filePath)}.${randomUUID5().slice(0, 8)}.tmp`
|
|
8515
8021
|
);
|
|
8516
8022
|
try {
|
|
8517
|
-
const handle = await
|
|
8023
|
+
const handle = await fs12.open(tmp, "w");
|
|
8518
8024
|
try {
|
|
8519
8025
|
await handle.writeFile(JSON.stringify(registry, null, 2), "utf8");
|
|
8520
8026
|
await handle.sync().catch(() => void 0);
|
|
8521
8027
|
} finally {
|
|
8522
8028
|
await handle.close();
|
|
8523
8029
|
}
|
|
8524
|
-
await
|
|
8030
|
+
await fs12.rename(tmp, this.filePath);
|
|
8525
8031
|
} catch (err) {
|
|
8526
|
-
await
|
|
8032
|
+
await fs12.unlink(tmp).catch(() => void 0);
|
|
8527
8033
|
throw err;
|
|
8528
8034
|
}
|
|
8529
8035
|
}
|
|
@@ -8533,17 +8039,17 @@ var SessionRegistry = class {
|
|
|
8533
8039
|
const base = path17.basename(this.filePath);
|
|
8534
8040
|
const now = Date.now();
|
|
8535
8041
|
const stale = [];
|
|
8536
|
-
for (const name of await
|
|
8042
|
+
for (const name of await fs12.readdir(dir)) {
|
|
8537
8043
|
const isTemp = (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith(".tmp");
|
|
8538
8044
|
if (!isTemp) continue;
|
|
8539
|
-
const stat13 = await
|
|
8045
|
+
const stat13 = await fs12.stat(path17.join(dir, name)).catch(() => null);
|
|
8540
8046
|
if (!stat13) continue;
|
|
8541
8047
|
if (now - stat13.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat13.mtimeMs });
|
|
8542
8048
|
}
|
|
8543
8049
|
stale.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
8544
8050
|
await Promise.all(
|
|
8545
8051
|
stale.slice(MAX_STALE_TMP_FILES).map(async ({ name }) => {
|
|
8546
|
-
await
|
|
8052
|
+
await fs12.unlink(path17.join(dir, name)).catch(() => void 0);
|
|
8547
8053
|
})
|
|
8548
8054
|
);
|
|
8549
8055
|
} catch {
|
|
@@ -8959,7 +8465,7 @@ var AgentStatusTracker = class {
|
|
|
8959
8465
|
};
|
|
8960
8466
|
|
|
8961
8467
|
// src/fleet-notifier.ts
|
|
8962
|
-
import * as
|
|
8468
|
+
import * as fs13 from "node:fs/promises";
|
|
8963
8469
|
import * as path18 from "node:path";
|
|
8964
8470
|
var INSTANCES_FILE = "webui-instances.json";
|
|
8965
8471
|
var DISCOVERY_TTL_MS = 2500;
|
|
@@ -9022,7 +8528,7 @@ var FleetNotifier = class {
|
|
|
9022
8528
|
}
|
|
9023
8529
|
async discover() {
|
|
9024
8530
|
try {
|
|
9025
|
-
const raw = await
|
|
8531
|
+
const raw = await fs13.readFile(path18.join(this.baseDir, INSTANCES_FILE), "utf8");
|
|
9026
8532
|
const data = JSON.parse(raw);
|
|
9027
8533
|
const list = Array.isArray(data?.instances) ? data.instances : [];
|
|
9028
8534
|
return list.filter((i) => i && typeof i.httpPort === "number").filter((i) => i.pid !== this.selfPid).filter((i) => normRoot(i.projectRoot) === this.projectRoot).filter((i) => pidAlive2(i.pid)).map((i) => {
|
|
@@ -10327,9 +9833,204 @@ function computeTrend(history) {
|
|
|
10327
9833
|
return "steady";
|
|
10328
9834
|
}
|
|
10329
9835
|
|
|
9836
|
+
// src/storage/goal-kanban.ts
|
|
9837
|
+
import {
|
|
9838
|
+
addColumn,
|
|
9839
|
+
addTask,
|
|
9840
|
+
createBoard,
|
|
9841
|
+
getBoard,
|
|
9842
|
+
listBoards,
|
|
9843
|
+
removeBoard
|
|
9844
|
+
} from "@wrongstack/kanban";
|
|
9845
|
+
var GOAL_BOARD_TAG_PREFIX = "goal:";
|
|
9846
|
+
var DEFAULT_COLUMNS = [
|
|
9847
|
+
{ title: "Backlog", order: 0 },
|
|
9848
|
+
{ title: "In Progress", order: 1 },
|
|
9849
|
+
{ title: "Done", order: 2 }
|
|
9850
|
+
];
|
|
9851
|
+
async function createGoalKanbanBoard(projectRoot, goalFile) {
|
|
9852
|
+
if (!projectRoot) return null;
|
|
9853
|
+
const existingId = goalFile.kanbanBoardId;
|
|
9854
|
+
if (existingId) {
|
|
9855
|
+
const existing = await getBoard(projectRoot, existingId).catch(() => null);
|
|
9856
|
+
if (existing) return existingId;
|
|
9857
|
+
}
|
|
9858
|
+
const displayGoal = (goalFile.refinedGoal || goalFile.goal).replace(/\s+/g, " ").trim();
|
|
9859
|
+
const titleSuffix = displayGoal.length > 80 ? displayGoal.slice(0, 77) + "\u2026" : displayGoal;
|
|
9860
|
+
const board = await createBoard(projectRoot, {
|
|
9861
|
+
title: `\u{1F3AF} ${titleSuffix}`,
|
|
9862
|
+
description: `Kanban board auto-created for goal: ${displayGoal}`,
|
|
9863
|
+
tags: [goalTag(goalFile)]
|
|
9864
|
+
});
|
|
9865
|
+
const backlogCol = board.columns.find(
|
|
9866
|
+
(c) => ["backlog", "todo", "open"].includes(c.id.toLowerCase())
|
|
9867
|
+
);
|
|
9868
|
+
const existingColumnIds = new Set(board.columns.map((c) => c.id));
|
|
9869
|
+
for (const col of DEFAULT_COLUMNS) {
|
|
9870
|
+
if (!existingColumnIds.has(col.title.toLowerCase().replace(/\s+/g, "-"))) {
|
|
9871
|
+
const result = await addColumn(projectRoot, board.id, { title: col.title });
|
|
9872
|
+
if (result?.column && backlogCol) {
|
|
9873
|
+
}
|
|
9874
|
+
}
|
|
9875
|
+
}
|
|
9876
|
+
const targetColumnId = board.columns[0]?.id ?? "backlog";
|
|
9877
|
+
if (goalFile.deliverables && goalFile.deliverables.length > 0) {
|
|
9878
|
+
for (const d of goalFile.deliverables) {
|
|
9879
|
+
const cleaned = d.replace(/^\[[x✓]\]|✅|\(done\)\s*/i, "").trim();
|
|
9880
|
+
if (cleaned) {
|
|
9881
|
+
await addTask(projectRoot, board.id, {
|
|
9882
|
+
title: cleaned,
|
|
9883
|
+
columnId: targetColumnId,
|
|
9884
|
+
description: `Deliverable for goal: ${displayGoal}`,
|
|
9885
|
+
priority: "medium"
|
|
9886
|
+
}).catch(() => {
|
|
9887
|
+
});
|
|
9888
|
+
}
|
|
9889
|
+
}
|
|
9890
|
+
}
|
|
9891
|
+
goalFile.kanbanBoardId = board.id;
|
|
9892
|
+
return board.id;
|
|
9893
|
+
}
|
|
9894
|
+
async function findGoalKanbanBoard(projectRoot, boardId) {
|
|
9895
|
+
if (!projectRoot || !boardId) return null;
|
|
9896
|
+
try {
|
|
9897
|
+
return await getBoard(projectRoot, boardId);
|
|
9898
|
+
} catch {
|
|
9899
|
+
return null;
|
|
9900
|
+
}
|
|
9901
|
+
}
|
|
9902
|
+
async function deleteGoalKanbanBoard(projectRoot, boardId) {
|
|
9903
|
+
if (!projectRoot || !boardId) return;
|
|
9904
|
+
await removeBoard(projectRoot, boardId).catch(() => {
|
|
9905
|
+
});
|
|
9906
|
+
}
|
|
9907
|
+
async function findGoalBoardByTag(projectRoot, goalFile) {
|
|
9908
|
+
if (!projectRoot) return null;
|
|
9909
|
+
const tag = goalTag(goalFile);
|
|
9910
|
+
const boards = await listBoards(projectRoot);
|
|
9911
|
+
const matched = boards.find((b) => b.tags?.includes(tag));
|
|
9912
|
+
if (!matched) return null;
|
|
9913
|
+
return getBoard(projectRoot, matched.id).catch(() => null);
|
|
9914
|
+
}
|
|
9915
|
+
function formatGoalKanbanPreview(goalFile, boardId, taskCount) {
|
|
9916
|
+
const lines = [];
|
|
9917
|
+
const displayGoal = (goalFile.refinedGoal || goalFile.goal).replace(/\s+/g, " ").trim();
|
|
9918
|
+
lines.push("");
|
|
9919
|
+
lines.push(color.bold(color.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")));
|
|
9920
|
+
lines.push(color.bold(color.cyan(" \u{1F3AF} GOAL KANBAN \u2014 Mission Board")));
|
|
9921
|
+
lines.push(color.bold(color.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")));
|
|
9922
|
+
lines.push("");
|
|
9923
|
+
lines.push(` ${color.bold("Mission:")} ${color.bold(displayGoal)}`);
|
|
9924
|
+
if (boardId) {
|
|
9925
|
+
lines.push(` ${color.dim(`Board: ${boardId.slice(0, 12)}\u2026`)}`);
|
|
9926
|
+
}
|
|
9927
|
+
if (typeof taskCount === "number") {
|
|
9928
|
+
lines.push(` ${color.dim(`${taskCount} task(s)`)}`);
|
|
9929
|
+
}
|
|
9930
|
+
lines.push("");
|
|
9931
|
+
const deliverables = goalFile.deliverables ?? [];
|
|
9932
|
+
const doneDeliverables = deliverables.filter(
|
|
9933
|
+
(d) => /^\[[x✓]\]|✅|\(done\)/i.test(d)
|
|
9934
|
+
).length;
|
|
9935
|
+
const totalDeliverables = deliverables.length;
|
|
9936
|
+
if (totalDeliverables > 0) {
|
|
9937
|
+
const backlog = deliverables.filter(
|
|
9938
|
+
(d) => !/^\[[x✓]\]|✅|\(done\)|🔄|in.progress/i.test(d)
|
|
9939
|
+
);
|
|
9940
|
+
const inProgress = deliverables.filter((d) => /🔄|in.progress/i.test(d));
|
|
9941
|
+
const done = deliverables.filter((d) => /^\[[x✓]\]|✅|\(done\)/i.test(d));
|
|
9942
|
+
const renderColumn = (title, items, icon, accent) => {
|
|
9943
|
+
if (items.length === 0) {
|
|
9944
|
+
lines.push(` ${accent(color.bold(`${icon} ${title}`))} ${color.dim("(empty)")}`);
|
|
9945
|
+
return;
|
|
9946
|
+
}
|
|
9947
|
+
lines.push(` ${accent(color.bold(`${icon} ${title} (${items.length})`))}`);
|
|
9948
|
+
for (const item of items) {
|
|
9949
|
+
const cleaned = item.replace(/^\[[x✓]\]|✅|\(done\)|🔄|in.progress\s*/gi, "").trim();
|
|
9950
|
+
lines.push(` \u25CB ${cleaned}`);
|
|
9951
|
+
}
|
|
9952
|
+
};
|
|
9953
|
+
renderColumn("Backlog", backlog, "\u{1F4CB}", (s) => color.dim(s));
|
|
9954
|
+
renderColumn("In Progress", inProgress, "\u{1F504}", (s) => color.cyan(s));
|
|
9955
|
+
renderColumn("Done", done, "\u2705", (s) => color.green(s));
|
|
9956
|
+
lines.push("");
|
|
9957
|
+
lines.push(
|
|
9958
|
+
` ${color.dim(`Progress: ${doneDeliverables}/${totalDeliverables} deliverables complete`)}`
|
|
9959
|
+
);
|
|
9960
|
+
}
|
|
9961
|
+
if (typeof goalFile.progress === "number") {
|
|
9962
|
+
const pct = Math.min(100, Math.max(0, Math.round(goalFile.progress)));
|
|
9963
|
+
const filled = Math.round(pct / 5);
|
|
9964
|
+
const empty = 20 - filled;
|
|
9965
|
+
const bar = color.green("\u2588".repeat(filled)) + color.dim("\u2591".repeat(empty));
|
|
9966
|
+
lines.push(` ${color.dim("Overall:")} ${bar} ${color.bold(`${pct}%`)}`);
|
|
9967
|
+
if (goalFile.progressNote) {
|
|
9968
|
+
lines.push(` ${color.dim(goalFile.progressNote)}`);
|
|
9969
|
+
}
|
|
9970
|
+
}
|
|
9971
|
+
if (typeof goalFile.progress === "undefined" && totalDeliverables > 0) {
|
|
9972
|
+
const pct = Math.round(doneDeliverables / totalDeliverables * 100);
|
|
9973
|
+
const filled = Math.round(pct / 5);
|
|
9974
|
+
const empty = 20 - filled;
|
|
9975
|
+
const bar = (pct > 0 ? color.green("\u2588".repeat(filled)) : "") + color.dim("\u2591".repeat(empty));
|
|
9976
|
+
lines.push(` ${color.dim("Overall:")} ${bar} ${color.bold(`${pct}%`)}`);
|
|
9977
|
+
}
|
|
9978
|
+
lines.push("");
|
|
9979
|
+
lines.push(color.bold(color.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")));
|
|
9980
|
+
return lines.join("\n");
|
|
9981
|
+
}
|
|
9982
|
+
function formatGoalEvent(goalFile, boardId) {
|
|
9983
|
+
const lines = [];
|
|
9984
|
+
lines.push("");
|
|
9985
|
+
lines.push(color.bold(color.green("\u250C\u2500\u2500\u2500 Goal Event \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510")));
|
|
9986
|
+
lines.push(color.bold(color.green("\u2502")) + " \u{1F3AF} Mission locked and ready");
|
|
9987
|
+
if (boardId) {
|
|
9988
|
+
lines.push(
|
|
9989
|
+
color.bold(color.green("\u2502")) + ` \u{1F4CB} Kanban board created: ${color.cyan(boardId.slice(0, 12) + "\u2026")}`
|
|
9990
|
+
);
|
|
9991
|
+
}
|
|
9992
|
+
const deliverables = goalFile.deliverables ?? [];
|
|
9993
|
+
lines.push(
|
|
9994
|
+
color.bold(color.green("\u2502")) + ` \u{1F4E6} ${deliverables.length} deliverable${deliverables.length !== 1 ? "s" : ""} extracted`
|
|
9995
|
+
);
|
|
9996
|
+
if (typeof goalFile.progress === "number") {
|
|
9997
|
+
lines.push(
|
|
9998
|
+
color.bold(color.green("\u2502")) + ` \u{1F4CA} Progress: ${goalFile.progress}%`
|
|
9999
|
+
);
|
|
10000
|
+
}
|
|
10001
|
+
lines.push(color.bold(color.green("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518")));
|
|
10002
|
+
return lines.join("\n");
|
|
10003
|
+
}
|
|
10004
|
+
function formatGoalAutonomyChoice() {
|
|
10005
|
+
const lines = [];
|
|
10006
|
+
lines.push("");
|
|
10007
|
+
lines.push(color.bold(color.magenta("\u2550\u2550\u2550 Choose Autonomy Mode \u2550\u2550\u2550")));
|
|
10008
|
+
lines.push("");
|
|
10009
|
+
lines.push(
|
|
10010
|
+
` ${color.bold("1")}. ${color.red("Autonomy Eternal")} ${color.dim("\u2014 Single-agent sense/decide/execute/reflect loop")}`
|
|
10011
|
+
);
|
|
10012
|
+
lines.push(
|
|
10013
|
+
` ${color.bold("2")}. ${color.magenta("Eternal Parallel")} ${color.dim("\u2014 Multi-agent fan-out (4-8 subagents per tick)")}`
|
|
10014
|
+
);
|
|
10015
|
+
lines.push("");
|
|
10016
|
+
lines.push(` ${color.dim("Enter 1 or 2 (or press Enter to skip):")}`);
|
|
10017
|
+
return lines.join("\n");
|
|
10018
|
+
}
|
|
10019
|
+
function parseAutonomyChoice(input) {
|
|
10020
|
+
const trimmed = input.trim().toLowerCase();
|
|
10021
|
+
if (trimmed === "1" || trimmed === "eternal" || trimmed === "e") return "eternal";
|
|
10022
|
+
if (trimmed === "2" || trimmed === "parallel" || trimmed === "p") return "eternal-parallel";
|
|
10023
|
+
if (trimmed === "" || trimmed === "skip" || trimmed === "s" || trimmed === "0") return null;
|
|
10024
|
+
return null;
|
|
10025
|
+
}
|
|
10026
|
+
function goalTag(goalFile) {
|
|
10027
|
+
const goal = (goalFile.refinedGoal || goalFile.goal).replace(/\s+/g, "-").slice(0, 24).toLowerCase();
|
|
10028
|
+
return `${GOAL_BOARD_TAG_PREFIX}${goal}`;
|
|
10029
|
+
}
|
|
10030
|
+
|
|
10330
10031
|
// src/storage/prompt-store.ts
|
|
10331
10032
|
import { createHash as createHash7 } from "node:crypto";
|
|
10332
|
-
import * as
|
|
10033
|
+
import * as fs14 from "node:fs/promises";
|
|
10333
10034
|
import * as path20 from "node:path";
|
|
10334
10035
|
var SCHEMA_VERSION = 2;
|
|
10335
10036
|
function promptChecksum(content) {
|
|
@@ -10394,12 +10095,12 @@ var DefaultPromptStore = class {
|
|
|
10394
10095
|
await ensureDir(this.dir);
|
|
10395
10096
|
const entries = [];
|
|
10396
10097
|
try {
|
|
10397
|
-
const files = await
|
|
10098
|
+
const files = await fs14.readdir(this.dir);
|
|
10398
10099
|
for (const file of files) {
|
|
10399
10100
|
if (!file.endsWith(".json")) continue;
|
|
10400
10101
|
try {
|
|
10401
10102
|
const raw = JSON.parse(
|
|
10402
|
-
await
|
|
10103
|
+
await fs14.readFile(path20.join(this.dir, file), "utf8")
|
|
10403
10104
|
);
|
|
10404
10105
|
const migrated = migratePromptEntry(raw.entry);
|
|
10405
10106
|
if (migrated) entries.push(migrated);
|
|
@@ -10415,7 +10116,7 @@ var DefaultPromptStore = class {
|
|
|
10415
10116
|
async get(id) {
|
|
10416
10117
|
const file = path20.join(this.dir, `${id}.json`);
|
|
10417
10118
|
try {
|
|
10418
|
-
const raw = JSON.parse(await
|
|
10119
|
+
const raw = JSON.parse(await fs14.readFile(file, "utf8"));
|
|
10419
10120
|
return migratePromptEntry(raw.entry);
|
|
10420
10121
|
} catch {
|
|
10421
10122
|
return null;
|
|
@@ -10430,7 +10131,7 @@ var DefaultPromptStore = class {
|
|
|
10430
10131
|
async delete(id) {
|
|
10431
10132
|
const file = path20.join(this.dir, `${id}.json`);
|
|
10432
10133
|
try {
|
|
10433
|
-
await
|
|
10134
|
+
await fs14.unlink(file);
|
|
10434
10135
|
return true;
|
|
10435
10136
|
} catch {
|
|
10436
10137
|
return false;
|
|
@@ -10469,7 +10170,7 @@ var DefaultPromptStore = class {
|
|
|
10469
10170
|
};
|
|
10470
10171
|
|
|
10471
10172
|
// src/storage/prompt-usage-store.ts
|
|
10472
|
-
import * as
|
|
10173
|
+
import * as fs15 from "node:fs/promises";
|
|
10473
10174
|
import * as path21 from "node:path";
|
|
10474
10175
|
var PromptUsageStore = class {
|
|
10475
10176
|
constructor(file) {
|
|
@@ -10478,7 +10179,7 @@ var PromptUsageStore = class {
|
|
|
10478
10179
|
file;
|
|
10479
10180
|
async load() {
|
|
10480
10181
|
try {
|
|
10481
|
-
const raw = JSON.parse(await
|
|
10182
|
+
const raw = JSON.parse(await fs15.readFile(this.file, "utf8"));
|
|
10482
10183
|
if (raw && typeof raw === "object" && raw.usage && typeof raw.usage === "object") {
|
|
10483
10184
|
return raw.usage;
|
|
10484
10185
|
}
|
|
@@ -10515,7 +10216,7 @@ var PromptUsageStore = class {
|
|
|
10515
10216
|
};
|
|
10516
10217
|
|
|
10517
10218
|
// src/storage/input-history-store.ts
|
|
10518
|
-
import * as
|
|
10219
|
+
import * as fs16 from "node:fs/promises";
|
|
10519
10220
|
import * as path22 from "node:path";
|
|
10520
10221
|
var INPUT_HISTORY_DEFAULT_MAX = 100;
|
|
10521
10222
|
var InputHistoryStore = class {
|
|
@@ -10538,7 +10239,7 @@ var InputHistoryStore = class {
|
|
|
10538
10239
|
*/
|
|
10539
10240
|
async load() {
|
|
10540
10241
|
try {
|
|
10541
|
-
const raw = JSON.parse(await
|
|
10242
|
+
const raw = JSON.parse(await fs16.readFile(this.file, "utf8"));
|
|
10542
10243
|
if (raw && typeof raw === "object" && Array.isArray(raw.entries) && raw.entries.every((e) => typeof e === "string")) {
|
|
10543
10244
|
return raw.entries.slice(0, this.maxEntries);
|
|
10544
10245
|
}
|
|
@@ -10595,7 +10296,7 @@ var InputHistoryStore = class {
|
|
|
10595
10296
|
};
|
|
10596
10297
|
|
|
10597
10298
|
// src/storage/cloud-sync.ts
|
|
10598
|
-
import * as
|
|
10299
|
+
import * as fs17 from "node:fs/promises";
|
|
10599
10300
|
import * as path23 from "node:path";
|
|
10600
10301
|
import { createHash as createHash8 } from "node:crypto";
|
|
10601
10302
|
var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
|
|
@@ -10679,7 +10380,7 @@ var CloudSync = class {
|
|
|
10679
10380
|
lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10680
10381
|
localRev: rev
|
|
10681
10382
|
};
|
|
10682
|
-
await
|
|
10383
|
+
await fs17.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
|
|
10683
10384
|
this.state = syncState;
|
|
10684
10385
|
return {
|
|
10685
10386
|
ok: true,
|
|
@@ -10711,8 +10412,8 @@ var CloudSync = class {
|
|
|
10711
10412
|
const rel = segments.slice(2).join("/");
|
|
10712
10413
|
const destPath = resolvePulledCategoryPath(cat, localPath, rel, entry.path);
|
|
10713
10414
|
const blobData = await this.getBlob(token, owner, repoName, entry.sha);
|
|
10714
|
-
await
|
|
10715
|
-
await
|
|
10415
|
+
await fs17.mkdir(path23.dirname(destPath), { recursive: true });
|
|
10416
|
+
await fs17.writeFile(destPath, Buffer.from(blobData, "base64"));
|
|
10716
10417
|
}
|
|
10717
10418
|
const localRev = await this.hashLocalCategories(cfg.categories);
|
|
10718
10419
|
const syncState = {
|
|
@@ -10721,7 +10422,7 @@ var CloudSync = class {
|
|
|
10721
10422
|
lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10722
10423
|
localRev
|
|
10723
10424
|
};
|
|
10724
|
-
await
|
|
10425
|
+
await fs17.writeFile(this.statePath, JSON.stringify(syncState, null, 2), "utf8");
|
|
10725
10426
|
this.state = syncState;
|
|
10726
10427
|
return {
|
|
10727
10428
|
ok: true,
|
|
@@ -10740,7 +10441,7 @@ var CloudSync = class {
|
|
|
10740
10441
|
}
|
|
10741
10442
|
async loadState() {
|
|
10742
10443
|
try {
|
|
10743
|
-
const raw = await
|
|
10444
|
+
const raw = await fs17.readFile(this.statePath, "utf8");
|
|
10744
10445
|
this.state = JSON.parse(raw);
|
|
10745
10446
|
} catch {
|
|
10746
10447
|
this.state = null;
|
|
@@ -10818,17 +10519,17 @@ var CloudSync = class {
|
|
|
10818
10519
|
const localPath = this.categoryToPath(cat);
|
|
10819
10520
|
if (!localPath) continue;
|
|
10820
10521
|
try {
|
|
10821
|
-
const stat13 = await
|
|
10522
|
+
const stat13 = await fs17.stat(localPath);
|
|
10822
10523
|
if (stat13.isDirectory()) {
|
|
10823
10524
|
const files = await this.walkDir(localPath, localPath);
|
|
10824
10525
|
for (const file of files) {
|
|
10825
|
-
const content = await
|
|
10526
|
+
const content = await fs17.readFile(file, "utf8");
|
|
10826
10527
|
const rel = path23.relative(localPath, file).replace(/\\/g, "/");
|
|
10827
10528
|
entries.push({ path: `data/${cat}/${rel}`, content, mode: "100644" });
|
|
10828
10529
|
hashes.push(content);
|
|
10829
10530
|
}
|
|
10830
10531
|
} else {
|
|
10831
|
-
const content = await
|
|
10532
|
+
const content = await fs17.readFile(localPath, "utf8");
|
|
10832
10533
|
entries.push({ path: `data/${cat}`, content, mode: "100644" });
|
|
10833
10534
|
hashes.push(content);
|
|
10834
10535
|
}
|
|
@@ -10844,15 +10545,15 @@ var CloudSync = class {
|
|
|
10844
10545
|
const localPath = this.categoryToPath(cat);
|
|
10845
10546
|
if (!localPath) continue;
|
|
10846
10547
|
try {
|
|
10847
|
-
const stat13 = await
|
|
10548
|
+
const stat13 = await fs17.stat(localPath);
|
|
10848
10549
|
if (stat13.isDirectory()) {
|
|
10849
10550
|
const files = await this.walkDir(localPath, localPath);
|
|
10850
10551
|
for (const file of files) {
|
|
10851
|
-
const content = await
|
|
10552
|
+
const content = await fs17.readFile(file);
|
|
10852
10553
|
hashes.push(content.toString("base64") + file);
|
|
10853
10554
|
}
|
|
10854
10555
|
} else {
|
|
10855
|
-
const content = await
|
|
10556
|
+
const content = await fs17.readFile(localPath);
|
|
10856
10557
|
hashes.push(content.toString("base64") + localPath);
|
|
10857
10558
|
}
|
|
10858
10559
|
} catch {
|
|
@@ -10879,7 +10580,7 @@ var CloudSync = class {
|
|
|
10879
10580
|
}
|
|
10880
10581
|
async walkDir(dir, base) {
|
|
10881
10582
|
const results = [];
|
|
10882
|
-
const entries = await
|
|
10583
|
+
const entries = await fs17.readdir(dir, { withFileTypes: true });
|
|
10883
10584
|
for (const entry of entries) {
|
|
10884
10585
|
const full = path23.join(dir, entry.name);
|
|
10885
10586
|
if (entry.isDirectory()) {
|
|
@@ -11066,7 +10767,6 @@ export {
|
|
|
11066
10767
|
DefaultAttachmentStore,
|
|
11067
10768
|
DefaultConfigLoader,
|
|
11068
10769
|
DefaultConfigStore,
|
|
11069
|
-
DefaultMemoryStore,
|
|
11070
10770
|
DefaultPromptStore,
|
|
11071
10771
|
DefaultSessionReader,
|
|
11072
10772
|
DefaultSessionRewinder,
|
|
@@ -11095,12 +10795,19 @@ export {
|
|
|
11095
10795
|
attachPlanCheckpoint,
|
|
11096
10796
|
attachTodosCheckpoint,
|
|
11097
10797
|
clearPlan,
|
|
10798
|
+
createGoalKanbanBoard,
|
|
11098
10799
|
createSessionEventBridge,
|
|
10800
|
+
deleteGoalKanbanBoard,
|
|
11099
10801
|
deriveTodosFromPlanItem,
|
|
11100
10802
|
emptyGoal,
|
|
11101
10803
|
emptyPlan,
|
|
11102
10804
|
emptyTaskFile,
|
|
10805
|
+
findGoalBoardByTag,
|
|
10806
|
+
findGoalKanbanBoard,
|
|
11103
10807
|
formatGoal,
|
|
10808
|
+
formatGoalAutonomyChoice,
|
|
10809
|
+
formatGoalEvent,
|
|
10810
|
+
formatGoalKanbanPreview,
|
|
11104
10811
|
formatPlan,
|
|
11105
10812
|
formatPlanTemplates,
|
|
11106
10813
|
generateSessionId,
|
|
@@ -11118,6 +10825,7 @@ export {
|
|
|
11118
10825
|
migratePromptEntry,
|
|
11119
10826
|
mutatePlan,
|
|
11120
10827
|
mutateTasks,
|
|
10828
|
+
parseAutonomyChoice,
|
|
11121
10829
|
parseEntries,
|
|
11122
10830
|
parseProgressFromText,
|
|
11123
10831
|
promptChecksum,
|