@wrongstack/core 0.313.1 → 0.316.1
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/chronicle/index.js +31 -31
- package/dist/chronicle/legacy-journal-import.d.ts +0 -6
- package/dist/chronicle/metrics-ingest.d.ts +14 -0
- package/dist/chronicle/metrics-schema.d.ts +10 -1
- package/dist/chronicle/metrics-store.d.ts +20 -2
- package/dist/chronicle/project-server.js +39 -31
- package/dist/chronicle/sink.d.ts +13 -0
- package/dist/chronicle/sqlite-journal.d.ts +20 -2
- package/dist/{chunk-WOEZV3Q4.js → chunk-25LSLC3G.js} +2 -1
- package/dist/{chunk-AI4LLQ2U.js → chunk-27QIURLN.js} +229 -12
- package/dist/{chunk-I45KAW2N.js → chunk-3AGWVSS7.js} +38 -450
- package/dist/chunk-3BDV7RL3.js +211 -0
- package/dist/{chunk-NFAYHPRH.js → chunk-3ECENVHL.js} +10 -8
- package/dist/{chunk-FW5LDDMJ.js → chunk-45WI72ZB.js} +1576 -1156
- package/dist/{chunk-AT4V4XGI.js → chunk-4BMVPOHO.js} +539 -313
- package/dist/{chunk-YBUJCZ57.js → chunk-4I7HGVVA.js} +17 -5
- package/dist/{chunk-JVGPR3TS.js → chunk-4LCZVJVF.js} +7 -55
- package/dist/{chunk-4ZHTKJHM.js → chunk-4SJNS7OB.js} +36 -2
- package/dist/{chunk-AD7ZFZCS.js → chunk-5BMIE7QG.js} +3 -3
- package/dist/{chunk-IQ35HMQL.js → chunk-5YPSXQXM.js} +71 -24
- package/dist/chunk-6FHWXLPT.js +10 -0
- package/dist/{chunk-FIV4IJL7.js → chunk-6H5GEOET.js} +163 -20
- package/dist/{chunk-VB3H5WWB.js → chunk-6NWEVFEL.js} +2 -2
- package/dist/{chunk-E6I5RXVK.js → chunk-AYW5YINX.js} +2 -2
- package/dist/{chunk-DEIQMBD2.js → chunk-B7F4WVP4.js} +2 -2
- package/dist/{chunk-XQ2XGIBF.js → chunk-BPBKKBUK.js} +6 -7
- package/dist/{chunk-K4YUNKU7.js → chunk-BYD345OX.js} +2 -2
- package/dist/{chunk-LZDDLNCZ.js → chunk-C27KDLOX.js} +4 -4
- package/dist/{chunk-6AKOTGLM.js → chunk-CU546ESW.js} +7 -7
- package/dist/{chunk-ZZ4V3WIZ.js → chunk-DLHHXUAM.js} +10 -1
- package/dist/{chunk-BAKVJLEV.js → chunk-DOTHQSJ2.js} +28 -16
- package/dist/{chunk-KBZGE6LN.js → chunk-DYLPMV4Q.js} +4 -3
- package/dist/{chunk-52RTVH4M.js → chunk-F3JWNST7.js} +2 -2
- package/dist/{chunk-WEBMSQP2.js → chunk-FC2ZGRF3.js} +21 -4
- package/dist/{chunk-FHAMQS7Q.js → chunk-FCBTO76Z.js} +76 -146
- package/dist/{chunk-4LJJYBAU.js → chunk-FHZVFYSL.js} +9 -1
- package/dist/{chunk-W4A6QA3D.js → chunk-FWNLEZYG.js} +4 -3
- package/dist/{chunk-OJZEX73F.js → chunk-GDQ7CZRQ.js} +5 -1
- package/dist/{chunk-QVOVMXJR.js → chunk-GJJGJB2R.js} +32 -9
- package/dist/{chunk-4XV6IR37.js → chunk-HI3JGZR2.js} +7 -6
- package/dist/{chunk-FSWCWSTS.js → chunk-HMLIGAU7.js} +26 -26
- package/dist/{chunk-PFXFQQRT.js → chunk-HXKZ7OBQ.js} +7 -43
- package/dist/{chunk-2FQL4PDC.js → chunk-I7DALG7J.js} +5 -5
- package/dist/{chunk-GM54GJBU.js → chunk-IRUTEHCE.js} +3 -3
- package/dist/{chunk-TYW2GI5I.js → chunk-J2WRWR37.js} +40 -13
- package/dist/{chunk-ZEHUCJHY.js → chunk-JFPJ6LLQ.js} +2 -2
- package/dist/chunk-JMULFFLN.js +9 -0
- package/dist/{chunk-RD6DEE36.js → chunk-JO4MGB6Z.js} +94 -8
- package/dist/{chunk-5DSMEVA4.js → chunk-K6BXGITA.js} +7 -7
- package/dist/{chunk-O74MJHXU.js → chunk-KZMSZV32.js} +2 -2
- package/dist/{chunk-GFJOMSHG.js → chunk-LA7CLDRW.js} +6 -2
- package/dist/{chunk-M2WF6AXG.js → chunk-LSMMOS2Z.js} +7 -3
- package/dist/{chunk-ASX6AW36.js → chunk-LXWCMQNJ.js} +3 -3
- package/dist/{chunk-WZO23AWO.js → chunk-M27LIEKV.js} +72 -22
- package/dist/{chunk-JTHKOYWU.js → chunk-MJPHJRAL.js} +15 -15
- package/dist/{chunk-G5AAKEEZ.js → chunk-MTL4O5YR.js} +5 -5
- package/dist/chunk-NHJ75HXY.js +1 -0
- package/dist/{chunk-ZYU2YOJ6.js → chunk-NIOAGFPB.js} +29 -15
- package/dist/{chunk-Y2GE5TSI.js → chunk-NS52ISWS.js} +2 -2
- package/dist/{chunk-UIWONWVG.js → chunk-NSOFPKGT.js} +541 -148
- package/dist/{chunk-RCQYT2I4.js → chunk-NVIZG63R.js} +2 -2
- package/dist/{chunk-EPC3GZD4.js → chunk-OOYZPOXM.js} +2 -2
- package/dist/{chunk-7ZHZE5EO.js → chunk-OSGLYW5J.js} +2 -2
- package/dist/{chunk-CE4ZOCOI.js → chunk-PFCMSSUF.js} +9 -9
- package/dist/{chunk-GFXZTHNI.js → chunk-PGRVXIMY.js} +5 -5
- package/dist/{chunk-JJPMWPB6.js → chunk-PISBFBJN.js} +6 -6
- package/dist/chunk-POOOIWE5.js +9 -0
- package/dist/{chunk-5RP3QTRL.js → chunk-PQNS3U7Z.js} +20 -3
- package/dist/{chunk-3E244UDA.js → chunk-QSTSJOCK.js} +52 -6
- package/dist/{chunk-YUPMXOGP.js → chunk-RFQMOKWK.js} +3 -3
- package/dist/chunk-RTOTNK3P.js +484 -0
- package/dist/{chunk-SN2DQRL6.js → chunk-RXKSDUAU.js} +7 -7
- package/dist/{chunk-T3KGZBU5.js → chunk-S45JPKQN.js} +189 -43
- package/dist/{chunk-YNCVSFGF.js → chunk-SAFHACFA.js} +4 -1
- package/dist/{chunk-KI4L2DUL.js → chunk-TCCHINEF.js} +114 -19
- package/dist/{chunk-AWNQ6ED6.js → chunk-THEGTRAG.js} +7 -2
- package/dist/{chunk-2KKODIQU.js → chunk-TR4LQE7L.js} +15 -15
- package/dist/{chunk-GPQ3NJPR.js → chunk-TWJMGXCI.js} +3 -3
- package/dist/{chunk-H72H4XDU.js → chunk-U45AXT5D.js} +9 -9
- package/dist/{chunk-62V2AFVG.js → chunk-U4FWBSO3.js} +160 -73
- package/dist/{chunk-A455ATZB.js → chunk-UBGDX6FI.js} +8 -1
- package/dist/chunk-UJILKYES.js +75 -0
- package/dist/{chunk-KSJ5UTP7.js → chunk-ULMAQV3T.js} +84 -4
- package/dist/{chunk-CB2HFKTP.js → chunk-VVBP6KGT.js} +1 -1
- package/dist/{chunk-RPOA7IAE.js → chunk-W5VITDM2.js} +43 -25
- package/dist/{chunk-QRTG4XTI.js → chunk-WAHQVNWT.js} +7 -1
- package/dist/{chunk-SQP64RCS.js → chunk-WGGYRBXH.js} +3019 -2976
- package/dist/{chunk-K3G6JBUC.js → chunk-WLYKU45P.js} +679 -141
- package/dist/chunk-WP7BVANJ.js +53 -0
- package/dist/{chunk-GAZEWQEZ.js → chunk-XF4VUMKW.js} +2 -2
- package/dist/{chunk-PAAWLLZX.js → chunk-Y5YXDM7D.js} +2 -2
- package/dist/{chunk-65RDIOR2.js → chunk-YOPAUVU4.js} +162 -44
- package/dist/{chunk-FFSX2QDQ.js → chunk-YPT4A46F.js} +3 -3
- package/dist/chunk-YWXT3JNM.js +425 -0
- package/dist/{chunk-EFEHDQGY.js → chunk-ZHZ2Y2DH.js} +64 -10
- package/dist/{chunk-CQQL4ZOQ.js → chunk-ZT7JNIGX.js} +4 -3
- package/dist/coordination/agent-monitor.d.ts +9 -1
- package/dist/coordination/agent-subagent-runner.d.ts +1 -1
- package/dist/coordination/agents/agent-prompts.d.ts +8 -0
- package/dist/coordination/agents/index.d.ts +33 -1
- package/dist/coordination/agents/index.js +52 -22
- package/dist/coordination/agents/phase3-wave1-platform.d.ts +1 -3
- package/dist/coordination/agents/phase3-wave2-meta.d.ts +1 -3
- package/dist/coordination/agents/phase8-wave3-products.d.ts +1 -3
- package/dist/coordination/agents/phase9-wave4-platform-meta.d.ts +1 -3
- package/dist/coordination/agents/project-agent-identity.d.ts +11 -11
- package/dist/coordination/agents/types.d.ts +13 -2
- package/dist/coordination/brain-monitor.d.ts +30 -7
- package/dist/coordination/collab-debug.d.ts +1 -1
- package/dist/coordination/collab-pause.d.ts +1 -1
- package/dist/coordination/delegate-tool.d.ts +7 -5
- package/dist/coordination/director-kanban-queue-helpers.d.ts +1 -0
- package/dist/coordination/director-options.d.ts +7 -0
- package/dist/coordination/director-spawn-model.d.ts +17 -0
- package/dist/coordination/director-tools.d.ts +2 -2
- package/dist/coordination/director.d.ts +12 -0
- package/dist/coordination/dispatcher.d.ts +8 -0
- package/dist/coordination/fleet-bus.d.ts +35 -4
- package/dist/coordination/fleet-supervisor.d.ts +10 -2
- package/dist/coordination/fleet.d.ts +16 -7
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.js +86 -49
- package/dist/coordination/kanban-dispatch-port.d.ts +2 -2
- package/dist/coordination/mailbox-http-router.d.ts +4 -3
- package/dist/coordination/mailbox-http-validation.d.ts +1 -1
- package/dist/coordination/mailbox-project-server.js +15 -15
- package/dist/coordination/mailbox-types.d.ts +24 -6
- package/dist/coordination/model-tier-leader.d.ts +122 -0
- package/dist/coordination/model-tier.d.ts +122 -0
- package/dist/coordination/multi-agent-coordinator.d.ts +24 -0
- package/dist/coordination/origin-session.d.ts +16 -0
- package/dist/coordination/provider-status-tracker.d.ts +37 -5
- package/dist/coordination/session-note-hub.d.ts +44 -0
- package/dist/coordination/session-note-tool.d.ts +10 -0
- package/dist/core/agent.d.ts +1 -1
- package/dist/core/context.d.ts +22 -3
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +130 -38
- package/dist/core/request-conversation-binding.d.ts +37 -0
- package/dist/core/session-notes.d.ts +25 -0
- package/dist/core/system-prompt-builder.d.ts +11 -2
- package/dist/core/system-prompt-variants.d.ts +78 -0
- package/dist/design/index.js +3 -3
- package/dist/execution/auto-compaction-middleware.d.ts +11 -0
- package/dist/execution/autonomy-brain.d.ts +2 -1
- package/dist/execution/autonomy-prompt-contributor.d.ts +7 -2
- package/dist/execution/compaction-core.d.ts +4 -4
- package/dist/execution/eternal-autonomy.d.ts +1 -1
- package/dist/execution/index.js +138 -129
- package/dist/execution/model-runtime.d.ts +0 -7
- package/dist/execution/parallel-eternal-engine.d.ts +7 -7
- package/dist/extension/index.js +3 -3
- package/dist/goal/index.js +6 -6
- package/dist/hooks/index.js +3 -3
- package/dist/hq/index.js +14 -12
- package/dist/hq/persistence/event-log.d.ts +0 -1
- package/dist/hq/persistence.d.ts +4 -4
- package/dist/hq/publisher.d.ts +2 -0
- package/dist/index.d.ts +14 -11
- package/dist/index.js +728 -635
- package/dist/infrastructure/index.d.ts +4 -4
- package/dist/infrastructure/index.js +281 -260
- package/dist/infrastructure/token-counter.d.ts +9 -0
- package/dist/kernel/events/agent-events.d.ts +77 -3
- package/dist/kernel/events/brain-events.d.ts +12 -0
- package/dist/kernel/events/provider-events.d.ts +7 -1
- package/dist/kernel/events/session-events.d.ts +14 -0
- package/dist/kernel/events/tool-events.d.ts +13 -0
- package/dist/kernel/index.js +4 -4
- package/dist/mailbox-attach.d.ts +1 -1
- package/dist/models/index.js +10 -9
- package/dist/observability/event-bridge.d.ts +25 -5
- package/dist/observability/index.d.ts +9 -8
- package/dist/observability/index.js +459 -351
- package/dist/observability/tool-usage-source.d.ts +65 -0
- package/dist/plugin/index.d.ts +5 -0
- package/dist/plugin/index.js +66 -50
- package/dist/plugins/auto-review-plugin.d.ts +3 -2
- package/dist/plugins/review-finding-commands.d.ts +0 -8
- package/dist/{review-finding-store-DKAV3AS6.js → plugins/review-finding-store.js} +7 -6
- package/dist/plugins/review-finding-types.js +19 -0
- package/dist/plugins/review-report-integration.js +21 -0
- package/dist/{review-report-store-OWY3EC3N.js → plugins/review-report-store.js} +7 -6
- package/dist/plugins/review-report-types.js +13 -0
- package/dist/prompts/index.js +5 -5
- package/dist/registry/index.js +3 -3
- package/dist/registry/tool-registry.d.ts +59 -9
- package/dist/{registry-R23UBE7H.js → registry-7N4O3E7W.js} +5 -5
- package/dist/security/capabilities.d.ts +6 -0
- package/dist/security/file-permissions.d.ts +1 -1
- package/dist/security/index.js +47 -33
- package/dist/security/permission-policy.d.ts +12 -1
- package/dist/security/secret-scrubber.d.ts +15 -0
- package/dist/session-catalog/index.d.ts +1 -0
- package/dist/session-catalog/index.js +19 -14
- package/dist/session-catalog/project-server.js +30 -13
- package/dist/session-catalog/protocol.d.ts +32 -1
- package/dist/session-catalog/registry.d.ts +17 -1
- package/dist/session-catalog/session-agents.d.ts +56 -0
- package/dist/session-catalog/store-rebuild.d.ts +14 -0
- package/dist/session-catalog/store-schema.d.ts +26 -0
- package/dist/session-catalog/store.d.ts +27 -1
- package/dist/session-note-attach.d.ts +9 -0
- package/dist/skills/index.js +5 -5
- package/dist/statusline/index.d.ts +43 -0
- package/dist/statusline/index.js +102 -0
- package/dist/storage/board-store-port.d.ts +2 -2
- package/dist/storage/config-loader/in-project-policy.d.ts +29 -0
- package/dist/storage/config-loader.d.ts +3 -2
- package/dist/storage/file-session-writer.d.ts +4 -2
- package/dist/storage/goal-coordination.d.ts +6 -1
- package/dist/storage/goal-kanban.d.ts +4 -1
- package/dist/storage/index.d.ts +12 -4
- package/dist/storage/index.js +76 -61
- package/dist/storage/input-history-store.d.ts +2 -4
- package/dist/storage/memory-curator.d.ts +133 -0
- package/dist/storage/session-agent-attribution.d.ts +39 -0
- package/dist/storage/session-doctor.d.ts +109 -0
- package/dist/storage/session-event-bridge.d.ts +5 -7
- package/dist/storage/session-outcome.d.ts +42 -0
- package/dist/storage/session-recovery.d.ts +55 -0
- package/dist/storage/session-store/load-session-data.d.ts +11 -0
- package/dist/storage/session-store/prune-helpers.d.ts +1 -1
- package/dist/storage/session-store/resume-session.d.ts +0 -2
- package/dist/storage/session-store/types.d.ts +8 -0
- package/dist/storage/session-store.d.ts +3 -3
- package/dist/storage/session-summary-tracker.d.ts +8 -1
- package/dist/storage/session-write-buffer.d.ts +18 -0
- package/dist/storage/session-writer-truncate.d.ts +2 -0
- package/dist/tasking/index.js +2 -2
- package/dist/tools/fallback-manage-tools.d.ts +6 -4
- package/dist/tools/index.js +21 -21
- package/dist/tools/model-tier-set-tool.d.ts +66 -0
- package/dist/types/agent-bridge.d.ts +2 -1
- package/dist/types/config/mcp-features.d.ts +7 -0
- package/dist/types/config/model-tiers.d.ts +148 -0
- package/dist/types/config/root.d.ts +9 -0
- package/dist/types/config/tools.d.ts +73 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/context.d.ts +2 -0
- package/dist/types/default-config.d.ts +6 -0
- package/dist/types/errors.d.ts +2 -1
- package/dist/types/index.d.ts +11 -9
- package/dist/types/index.js +25 -15
- package/dist/types/multi-agent.d.ts +43 -0
- package/dist/types/plugin.d.ts +1 -1
- package/dist/types/provider.d.ts +15 -0
- package/dist/types/runtime-capability-manifest.d.ts +1 -1
- package/dist/types/secret-scrubber.d.ts +15 -0
- package/dist/types/session-markers.d.ts +37 -10
- package/dist/types/session-markers.js +1 -1
- package/dist/types/session-timeline.d.ts +197 -0
- package/dist/types/session-timeline.js +12 -0
- package/dist/types/session.d.ts +172 -11
- package/dist/types/system-prompt.d.ts +27 -0
- package/dist/utils/config-backup.d.ts +9 -1
- package/dist/utils/heap-watchdog.js +2 -2
- package/dist/utils/index.js +50 -48
- package/dist/utils/message-invariants.d.ts +4 -1
- package/dist/utils/regex-guard.d.ts +1 -1
- package/dist/utils/socket-path.d.ts +1 -1
- package/dist/utils/token-estimate.d.ts +6 -2
- package/dist/utils/wstack-paths.d.ts +2 -0
- package/dist/wiring/proxy-rewrite.d.ts +28 -0
- package/dist/wiring/proxy-rewrite.js +7 -1
- package/dist/worktree/index.js +2 -2
- package/instructions/agents/explore-companion.md +45 -18
- package/instructions/agents/memory-curator.md +34 -22
- package/instructions/coordination/director-preamble.md +12 -7
- package/instructions/coordination/subagent-baseline.md +26 -5
- package/instructions/leader-after-task.md +4 -1
- package/instructions/llm/chimera-review.md +30 -9
- package/instructions/llm/memory-curator.md +31 -0
- package/instructions/sections/tool/delegation-compact.md +2 -0
- package/instructions/sections/tool/delegation-full.md +5 -1
- package/instructions/sections/tool/mailbox-compact.md +5 -1
- package/instructions/sections/tool/mailbox-full.md +3 -0
- package/instructions/sections/tool/session-note-compact.md +3 -0
- package/instructions/sections/tool/session-note-full.md +15 -0
- package/instructions/system-lite.md +9 -4
- package/instructions/system-pro.md +66 -143
- package/instructions/system.md +73 -175
- package/package.json +32 -4
- package/dist/chunk-2BUUIMQB.js +0 -225
- package/dist/chunk-FAYRX2AD.js +0 -13
- package/dist/chunk-H5E77KUJ.js +0 -15
- package/dist/chunk-M7BS2K6U.js +0 -1
- package/dist/coordination/agents.d.ts +0 -8
- package/dist/coordination/transport.d.ts +0 -2
- package/dist/storage/session-writer/session-writer-summary-tracker.d.ts +0 -28
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-SWUFN7XM.js";
|
|
4
4
|
import {
|
|
5
5
|
normalizeInlineProviderModels
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5YPSXQXM.js";
|
|
7
7
|
import {
|
|
8
8
|
decryptConfigSecrets
|
|
9
9
|
} from "./chunk-YY6APYUM.js";
|
|
@@ -13,30 +13,28 @@ import {
|
|
|
13
13
|
scrubPersistedSessionEvent,
|
|
14
14
|
scrubPersistedSessionSummary,
|
|
15
15
|
sessionContentPreview,
|
|
16
|
+
summarizeSessionFile,
|
|
16
17
|
userInputTitle
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import {
|
|
19
|
-
SECRET_FILE_MODE
|
|
20
|
-
} from "./chunk-H5E77KUJ.js";
|
|
21
|
-
import {
|
|
22
|
-
DefaultSecretScrubber
|
|
23
|
-
} from "./chunk-3E244UDA.js";
|
|
24
|
-
import {
|
|
25
|
-
MEMORY_TYPE_LABELS
|
|
26
|
-
} from "./chunk-57GEWK5X.js";
|
|
18
|
+
} from "./chunk-NSOFPKGT.js";
|
|
27
19
|
import {
|
|
28
20
|
compileUserRegex
|
|
29
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-J2WRWR37.js";
|
|
30
22
|
import {
|
|
31
23
|
safeParse
|
|
32
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-W5VITDM2.js";
|
|
33
25
|
import {
|
|
34
26
|
sessionScopedPath
|
|
35
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-5BMIE7QG.js";
|
|
36
28
|
import {
|
|
37
29
|
readBundledInstructionText,
|
|
38
30
|
renderInstructionTemplate
|
|
39
31
|
} from "./chunk-5S437PRC.js";
|
|
32
|
+
import {
|
|
33
|
+
DefaultSecretScrubber
|
|
34
|
+
} from "./chunk-QSTSJOCK.js";
|
|
35
|
+
import {
|
|
36
|
+
SECRET_FILE_MODE
|
|
37
|
+
} from "./chunk-6FHWXLPT.js";
|
|
40
38
|
import {
|
|
41
39
|
isPidAlive
|
|
42
40
|
} from "./chunk-TRT2HRWZ.js";
|
|
@@ -44,19 +42,22 @@ import {
|
|
|
44
42
|
atomicWrite,
|
|
45
43
|
ensureDir,
|
|
46
44
|
withFileLock
|
|
47
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-OSGLYW5J.js";
|
|
46
|
+
import {
|
|
47
|
+
MEMORY_TYPE_LABELS
|
|
48
|
+
} from "./chunk-57GEWK5X.js";
|
|
49
|
+
import {
|
|
50
|
+
expectDefined
|
|
51
|
+
} from "./chunk-PFZO3C6H.js";
|
|
48
52
|
import {
|
|
49
53
|
ConfigError,
|
|
50
54
|
ERROR_CODES,
|
|
51
55
|
SessionError,
|
|
52
56
|
WrongStackError
|
|
53
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-25LSLC3G.js";
|
|
54
58
|
import {
|
|
55
59
|
toErrorMessage
|
|
56
60
|
} from "./chunk-TBRL2RCC.js";
|
|
57
|
-
import {
|
|
58
|
-
expectDefined
|
|
59
|
-
} from "./chunk-PFZO3C6H.js";
|
|
60
61
|
|
|
61
62
|
// src/session-catalog/session-registry.ts
|
|
62
63
|
import * as fs2 from "node:fs/promises";
|
|
@@ -109,8 +110,8 @@ async function breakStaleLock(lockPath) {
|
|
|
109
110
|
const ownerPid = Number.parseInt(trimmed.slice(colonIdx + 1), 10);
|
|
110
111
|
if (ownerHost === HOST_NAME && Number.isInteger(ownerPid) && ownerPid > 0) {
|
|
111
112
|
if (isPidAlive(ownerPid)) {
|
|
112
|
-
const
|
|
113
|
-
if (Date.now() -
|
|
113
|
+
const stat8 = await fs.stat(lockPath);
|
|
114
|
+
if (Date.now() - stat8.mtimeMs > SAME_HOST_STALE_MS) {
|
|
114
115
|
return await breakStaleLockVerified(lockPath, async () => {
|
|
115
116
|
const st = await fs.stat(lockPath);
|
|
116
117
|
return Date.now() - st.mtimeMs > SAME_HOST_STALE_MS;
|
|
@@ -196,9 +197,9 @@ async function pruneStaleTempFiles(filePath) {
|
|
|
196
197
|
for (const name of await fs.readdir(dir)) {
|
|
197
198
|
const isTemp = (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith(".tmp");
|
|
198
199
|
if (!isTemp) continue;
|
|
199
|
-
const
|
|
200
|
-
if (!
|
|
201
|
-
if (now -
|
|
200
|
+
const stat8 = await fs.stat(path.join(dir, name)).catch(() => null);
|
|
201
|
+
if (!stat8) continue;
|
|
202
|
+
if (now - stat8.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat8.mtimeMs });
|
|
202
203
|
}
|
|
203
204
|
stale.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
204
205
|
await Promise.all(
|
|
@@ -293,10 +294,6 @@ var SessionRegistry = class {
|
|
|
293
294
|
await this.atomicUpdate((registry) => {
|
|
294
295
|
const now = Date.now();
|
|
295
296
|
for (const [id, existing] of Object.entries(registry)) {
|
|
296
|
-
if (existing.pid === entry.pid) {
|
|
297
|
-
if (id !== entry.sessionId) delete registry[id];
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
297
|
const heartbeatAt = Date.parse(existing.lastHeartbeatAt);
|
|
301
298
|
if (!Number.isFinite(heartbeatAt) || now - heartbeatAt > PID_CHECK_AFTER_MS && !pidAlive(existing.pid)) {
|
|
302
299
|
delete registry[id];
|
|
@@ -1035,7 +1032,8 @@ var DefaultAttachmentStore = class {
|
|
|
1035
1032
|
let ref;
|
|
1036
1033
|
if (m[3] !== void 0) {
|
|
1037
1034
|
const wantPath = m[3];
|
|
1038
|
-
|
|
1035
|
+
const norm = (s) => s?.replace(/\\/g, "/");
|
|
1036
|
+
ref = findLast(this.refs, (r) => r.kind === "file" && norm(refPath(r)) === norm(wantPath));
|
|
1039
1037
|
} else {
|
|
1040
1038
|
const kind = prefixToKind(m[1]);
|
|
1041
1039
|
const seq = Number(m[2]);
|
|
@@ -1123,6 +1121,19 @@ async function loadCompletedWorkCheckpoint(filePath, events, traceId) {
|
|
|
1123
1121
|
try {
|
|
1124
1122
|
raw = await fsp2.readFile(filePath, "utf8");
|
|
1125
1123
|
} catch (err) {
|
|
1124
|
+
const code = err.code;
|
|
1125
|
+
if (code === "ENOENT") {
|
|
1126
|
+
events?.emit("storage.read", {
|
|
1127
|
+
sessionId: traceId ?? "~boot~",
|
|
1128
|
+
store: "completed-work",
|
|
1129
|
+
filePath,
|
|
1130
|
+
operation: "load",
|
|
1131
|
+
outcome: "success",
|
|
1132
|
+
durationMs: Date.now() - t0,
|
|
1133
|
+
...traceId !== void 0 && { traceId }
|
|
1134
|
+
});
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1126
1137
|
events?.emit("storage.error", {
|
|
1127
1138
|
sessionId: traceId ?? "~boot~",
|
|
1128
1139
|
store: "completed-work",
|
|
@@ -1307,7 +1318,7 @@ var DefaultConfigStore = class {
|
|
|
1307
1318
|
}
|
|
1308
1319
|
const prev = this.current;
|
|
1309
1320
|
this.current = next;
|
|
1310
|
-
for (const w of this.watchers) {
|
|
1321
|
+
for (const w of [...this.watchers]) {
|
|
1311
1322
|
try {
|
|
1312
1323
|
w(next, prev);
|
|
1313
1324
|
} catch (err) {
|
|
@@ -1409,13 +1420,11 @@ var InputHistoryStore = class {
|
|
|
1409
1420
|
}
|
|
1410
1421
|
/**
|
|
1411
1422
|
* Heuristic: did the scrubber actually redact anything? DefaultSecretScrubber
|
|
1412
|
-
* replaces matches with "
|
|
1413
|
-
*
|
|
1414
|
-
* can be accommodated by widening this check in the future; for now the
|
|
1415
|
-
* marker check covers the shipped scrubber.
|
|
1423
|
+
* replaces matches with "[REDACTED:<type>]" (and legacy mocks with
|
|
1424
|
+
* "***REDACTED***"), so a residual marker means a secret was detected.
|
|
1416
1425
|
*/
|
|
1417
1426
|
looksRedacted(text) {
|
|
1418
|
-
return text.includes("***REDACTED***");
|
|
1427
|
+
return text.includes("***REDACTED***") || /\[REDACTED:[a-zA-Z0-9_-]+\]/.test(text);
|
|
1419
1428
|
}
|
|
1420
1429
|
};
|
|
1421
1430
|
|
|
@@ -1577,8 +1586,8 @@ var FileMemoryBackend = class {
|
|
|
1577
1586
|
}
|
|
1578
1587
|
async getMtime(file) {
|
|
1579
1588
|
try {
|
|
1580
|
-
const
|
|
1581
|
-
return
|
|
1589
|
+
const stat8 = await fs5.stat(file);
|
|
1590
|
+
return stat8.mtimeMs;
|
|
1582
1591
|
} catch {
|
|
1583
1592
|
return 0;
|
|
1584
1593
|
}
|
|
@@ -1631,13 +1640,13 @@ var FileMemoryBackend = class {
|
|
|
1631
1640
|
await withFileLock(file, async () => {
|
|
1632
1641
|
const handle = await fs5.open(file, "a+", SECRET_FILE_MODE);
|
|
1633
1642
|
try {
|
|
1634
|
-
const
|
|
1643
|
+
const stat8 = await handle.stat();
|
|
1635
1644
|
let prefix = "";
|
|
1636
|
-
if (
|
|
1645
|
+
if (stat8.size === 0) {
|
|
1637
1646
|
prefix = "# Agent Memory\n\n";
|
|
1638
1647
|
} else {
|
|
1639
1648
|
const tail = Buffer.allocUnsafe(1);
|
|
1640
|
-
const { bytesRead } = await handle.read(tail, 0, 1,
|
|
1649
|
+
const { bytesRead } = await handle.read(tail, 0, 1, stat8.size - 1);
|
|
1641
1650
|
if (bytesRead === 1 && tail[0] !== 10) prefix = "\n";
|
|
1642
1651
|
}
|
|
1643
1652
|
await handle.appendFile(`${prefix}${line}`, "utf8");
|
|
@@ -1828,8 +1837,8 @@ ${existingEntries.map((e) => `- [${e.ts.slice(0, 10)}] ${e.text}`).join("\n")}`
|
|
|
1828
1837
|
});
|
|
1829
1838
|
}
|
|
1830
1839
|
function relativeEvidencePath(projectRoot, filePath) {
|
|
1831
|
-
const
|
|
1832
|
-
return
|
|
1840
|
+
const relative4 = path6.relative(projectRoot, filePath);
|
|
1841
|
+
return relative4 && !relative4.startsWith("..") && !path6.isAbsolute(relative4) ? relative4.replaceAll("\\", "/") : filePath.replaceAll("\\", "/");
|
|
1833
1842
|
}
|
|
1834
1843
|
function safeCommandEvidence(value) {
|
|
1835
1844
|
if (typeof value !== "string") return void 0;
|
|
@@ -2038,6 +2047,252 @@ function buildSessionDigestText(finalText, iterations, factsAdded) {
|
|
|
2038
2047
|
return `Session digest (${iterations} iter${factsAdded > 0 ? `, ${factsAdded} facts added` : ""}): ` + summary + (cleaned.length > 400 ? "\u2026" : "");
|
|
2039
2048
|
}
|
|
2040
2049
|
|
|
2050
|
+
// src/storage/memory-curator.ts
|
|
2051
|
+
import * as path7 from "node:path";
|
|
2052
|
+
function toSageKind2(type) {
|
|
2053
|
+
switch (type) {
|
|
2054
|
+
case "decision":
|
|
2055
|
+
case "convention":
|
|
2056
|
+
case "preference":
|
|
2057
|
+
case "anti_pattern":
|
|
2058
|
+
case "warning":
|
|
2059
|
+
case "workflow":
|
|
2060
|
+
case "bug_root_cause":
|
|
2061
|
+
case "file_note":
|
|
2062
|
+
case "symbol_note":
|
|
2063
|
+
case "command_note":
|
|
2064
|
+
return type;
|
|
2065
|
+
default:
|
|
2066
|
+
return "fact";
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
function importanceFromPriority2(priority) {
|
|
2070
|
+
switch (priority) {
|
|
2071
|
+
case "critical":
|
|
2072
|
+
return 0.95;
|
|
2073
|
+
case "high":
|
|
2074
|
+
return 0.8;
|
|
2075
|
+
case "medium":
|
|
2076
|
+
return 0.55;
|
|
2077
|
+
case "low":
|
|
2078
|
+
return 0.25;
|
|
2079
|
+
default:
|
|
2080
|
+
return 0.6;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
function relativePath(projectRoot, filePath) {
|
|
2084
|
+
const rel = path7.relative(projectRoot, filePath);
|
|
2085
|
+
return rel && !rel.startsWith("..") && !path7.isAbsolute(rel) ? rel.replaceAll("\\", "/") : filePath.replaceAll("\\", "/");
|
|
2086
|
+
}
|
|
2087
|
+
function formatCandidateRecord(record) {
|
|
2088
|
+
const anchors = record.anchors && record.anchors.length > 0 ? ` [anchors: ${record.anchors.map((a) => a.path ? `${a.type}:${a.path}` : a.type).join(", ")}]` : "";
|
|
2089
|
+
const tags = record.tags && record.tags.length > 0 ? ` [tags: ${record.tags.join(", ")}]` : "";
|
|
2090
|
+
const status = record.status ? ` (status: ${record.status})` : "";
|
|
2091
|
+
const kind = record.kind ? ` [${record.kind}]` : "";
|
|
2092
|
+
return `- ID: ${record.id}${status}${kind}: "${record.text}"${tags}${anchors} (importance: ${record.importance ?? 0.6}, confidence: ${record.confidence ?? 0.8})`;
|
|
2093
|
+
}
|
|
2094
|
+
var SessionMemoryCurator = class {
|
|
2095
|
+
name = "session-memory-curator";
|
|
2096
|
+
owner = "core";
|
|
2097
|
+
Sage;
|
|
2098
|
+
provider;
|
|
2099
|
+
model;
|
|
2100
|
+
maxTargetMemories;
|
|
2101
|
+
oneShotOrchestrator;
|
|
2102
|
+
constructor(opts) {
|
|
2103
|
+
this.Sage = opts.Sage;
|
|
2104
|
+
this.provider = opts.provider;
|
|
2105
|
+
this.model = opts.model;
|
|
2106
|
+
this.maxTargetMemories = opts.maxTargetMemories ?? 10;
|
|
2107
|
+
this.oneShotOrchestrator = opts.oneShotOrchestrator;
|
|
2108
|
+
}
|
|
2109
|
+
afterRun = async (ctx, result) => {
|
|
2110
|
+
if (result.status !== "done") return;
|
|
2111
|
+
if (!this.Sage?.updateSage) return;
|
|
2112
|
+
const provider = this.provider ?? ctx.provider;
|
|
2113
|
+
if (!provider?.complete && !this.oneShotOrchestrator) return;
|
|
2114
|
+
const projectRoot = ctx.projectRoot ?? ctx.cwd ?? "";
|
|
2115
|
+
const writtenFiles = [...ctx.writtenFiles ?? []].slice(0, 6).map((f) => relativePath(projectRoot, f));
|
|
2116
|
+
const targetMap = /* @__PURE__ */ new Map();
|
|
2117
|
+
try {
|
|
2118
|
+
for (const file of writtenFiles) {
|
|
2119
|
+
if (targetMap.size >= this.maxTargetMemories) break;
|
|
2120
|
+
if (this.Sage.retrieveForPath) {
|
|
2121
|
+
const matched = await this.Sage.retrieveForPath({
|
|
2122
|
+
path: file,
|
|
2123
|
+
limit: 4,
|
|
2124
|
+
includeStatuses: ["active", "stale"]
|
|
2125
|
+
});
|
|
2126
|
+
for (const m of matched) {
|
|
2127
|
+
if (m.id && !targetMap.has(m.id)) {
|
|
2128
|
+
targetMap.set(m.id, m);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
} else if (this.Sage.searchSage) {
|
|
2132
|
+
const searchHits = await this.Sage.searchSage(file, {
|
|
2133
|
+
limit: 4,
|
|
2134
|
+
scope: "project",
|
|
2135
|
+
includeStatuses: ["active", "stale"]
|
|
2136
|
+
});
|
|
2137
|
+
for (const m of searchHits) {
|
|
2138
|
+
if (m.id && !targetMap.has(m.id)) {
|
|
2139
|
+
targetMap.set(m.id, m);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
if (this.Sage.listCandidates && targetMap.size < this.maxTargetMemories) {
|
|
2145
|
+
const candidates = await this.Sage.listCandidates(false);
|
|
2146
|
+
for (const c of candidates) {
|
|
2147
|
+
if (targetMap.size >= this.maxTargetMemories) break;
|
|
2148
|
+
if (c.memoryId && !targetMap.has(c.memoryId) && this.Sage.getSage) {
|
|
2149
|
+
const mem = await this.Sage.getSage(c.memoryId);
|
|
2150
|
+
if (mem?.id) {
|
|
2151
|
+
targetMap.set(mem.id, mem);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
if (targetMap.size === 0) return;
|
|
2157
|
+
const candidatesBlock = [...targetMap.values()].slice(0, this.maxTargetMemories).map(formatCandidateRecord).join("\n");
|
|
2158
|
+
const prompt = renderInstructionTemplate(
|
|
2159
|
+
readBundledInstructionText("llm/memory-curator.md"),
|
|
2160
|
+
{
|
|
2161
|
+
writtenFiles: writtenFiles.length > 0 ? writtenFiles.join(", ") : "(none)",
|
|
2162
|
+
summary: (result.finalText ?? "").slice(0, 800),
|
|
2163
|
+
candidates: candidatesBlock
|
|
2164
|
+
}
|
|
2165
|
+
);
|
|
2166
|
+
const _model = this.model ?? ctx.model;
|
|
2167
|
+
let text = "";
|
|
2168
|
+
if (this.oneShotOrchestrator) {
|
|
2169
|
+
const oneShotResult = await this.oneShotOrchestrator.call({
|
|
2170
|
+
system: prompt,
|
|
2171
|
+
userPrompt: "Review candidate memories against session changes and return JSON operations.",
|
|
2172
|
+
model: _model ?? "deepseek-chat",
|
|
2173
|
+
maxTokens: 400,
|
|
2174
|
+
timeoutMs: 1e4
|
|
2175
|
+
});
|
|
2176
|
+
text = oneShotResult.text;
|
|
2177
|
+
} else if (provider?.complete) {
|
|
2178
|
+
const signal = AbortSignal.timeout(1e4);
|
|
2179
|
+
const response = await provider.complete(
|
|
2180
|
+
{
|
|
2181
|
+
model: _model,
|
|
2182
|
+
system: [{ type: "text", text: prompt }],
|
|
2183
|
+
messages: [
|
|
2184
|
+
{
|
|
2185
|
+
role: "user",
|
|
2186
|
+
content: "Review candidate memories against session changes and return JSON operations."
|
|
2187
|
+
}
|
|
2188
|
+
],
|
|
2189
|
+
maxTokens: 400
|
|
2190
|
+
},
|
|
2191
|
+
{ signal }
|
|
2192
|
+
);
|
|
2193
|
+
text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
|
|
2194
|
+
}
|
|
2195
|
+
if (!text) return;
|
|
2196
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
2197
|
+
if (!jsonMatch) return;
|
|
2198
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
2199
|
+
if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;
|
|
2200
|
+
let supersededCount = 0;
|
|
2201
|
+
let mergedCount = 0;
|
|
2202
|
+
let splitCount = 0;
|
|
2203
|
+
let recalibratedCount = 0;
|
|
2204
|
+
for (const op of parsed.operations.slice(0, 15)) {
|
|
2205
|
+
try {
|
|
2206
|
+
if (!op || typeof op !== "object") continue;
|
|
2207
|
+
if (op.action === "supersede" && typeof op.targetId === "string") {
|
|
2208
|
+
const target = targetMap.get(op.targetId);
|
|
2209
|
+
if (target?.persistence === "permanent") continue;
|
|
2210
|
+
await this.Sage.updateSage(op.targetId, {
|
|
2211
|
+
status: "superseded",
|
|
2212
|
+
...op.supersededBy ? { supersedes: [op.supersededBy] } : {}
|
|
2213
|
+
});
|
|
2214
|
+
supersededCount++;
|
|
2215
|
+
} else if (op.action === "contradict" && typeof op.targetId === "string") {
|
|
2216
|
+
const target = targetMap.get(op.targetId);
|
|
2217
|
+
if (target?.persistence === "permanent") continue;
|
|
2218
|
+
await this.Sage.updateSage(op.targetId, {
|
|
2219
|
+
status: "contradicted",
|
|
2220
|
+
...op.contradictsWith ? { contradicts: [op.contradictsWith] } : {}
|
|
2221
|
+
});
|
|
2222
|
+
supersededCount++;
|
|
2223
|
+
} else if (op.action === "merge" && Array.isArray(op.targetIds) && op.text?.trim()) {
|
|
2224
|
+
const remembered = await this.Sage.rememberSage({
|
|
2225
|
+
text: op.text.trim(),
|
|
2226
|
+
kind: toSageKind2(op.type),
|
|
2227
|
+
importance: importanceFromPriority2(op.priority),
|
|
2228
|
+
confidence: typeof op.confidence === "number" ? op.confidence : 0.85,
|
|
2229
|
+
tags: op.tags,
|
|
2230
|
+
anchors: op.anchors,
|
|
2231
|
+
persistence: "long_lived",
|
|
2232
|
+
supersedes: op.targetIds,
|
|
2233
|
+
sources: [{ type: "session", sessionId: ctx.session.id }]
|
|
2234
|
+
});
|
|
2235
|
+
const newId = remembered?.id;
|
|
2236
|
+
for (const oldId of op.targetIds) {
|
|
2237
|
+
const old = targetMap.get(oldId);
|
|
2238
|
+
if (old?.persistence !== "permanent") {
|
|
2239
|
+
await this.Sage.updateSage(oldId, {
|
|
2240
|
+
status: "superseded",
|
|
2241
|
+
...newId ? { supersedes: [newId] } : {}
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
mergedCount++;
|
|
2246
|
+
} else if (op.action === "split" && typeof op.targetId === "string" && Array.isArray(op.items)) {
|
|
2247
|
+
for (const item of op.items.slice(0, 4)) {
|
|
2248
|
+
if (!item.text?.trim()) continue;
|
|
2249
|
+
await this.Sage.rememberSage({
|
|
2250
|
+
text: item.text.trim(),
|
|
2251
|
+
kind: toSageKind2(item.type),
|
|
2252
|
+
importance: importanceFromPriority2(item.priority),
|
|
2253
|
+
confidence: typeof item.confidence === "number" ? item.confidence : 0.85,
|
|
2254
|
+
tags: item.tags,
|
|
2255
|
+
anchors: item.anchors,
|
|
2256
|
+
persistence: "long_lived",
|
|
2257
|
+
supersedes: [op.targetId],
|
|
2258
|
+
sources: [{ type: "session", sessionId: ctx.session.id }]
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
const target = targetMap.get(op.targetId);
|
|
2262
|
+
if (target?.persistence !== "permanent") {
|
|
2263
|
+
await this.Sage.updateSage(op.targetId, { status: "superseded" });
|
|
2264
|
+
}
|
|
2265
|
+
splitCount++;
|
|
2266
|
+
} else if (op.action === "recalibrate" && typeof op.targetId === "string") {
|
|
2267
|
+
const patch = {};
|
|
2268
|
+
if (typeof op.importance === "number") patch.importance = Math.max(0, Math.min(1, op.importance));
|
|
2269
|
+
if (typeof op.confidence === "number") patch.confidence = Math.max(0, Math.min(1, op.confidence));
|
|
2270
|
+
if (typeof op.freshness === "number") patch.freshness = Math.max(0, Math.min(1, op.freshness));
|
|
2271
|
+
if (typeof op.status === "string" && ["active", "stale", "archived"].includes(op.status)) {
|
|
2272
|
+
const target = targetMap.get(op.targetId);
|
|
2273
|
+
if (target?.persistence !== "permanent" || op.status === "active") {
|
|
2274
|
+
patch.status = op.status;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
if (Object.keys(patch).length > 0) {
|
|
2278
|
+
await this.Sage.updateSage(op.targetId, patch);
|
|
2279
|
+
recalibratedCount++;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
} catch {
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
if (supersededCount > 0 || mergedCount > 0 || splitCount > 0 || recalibratedCount > 0) {
|
|
2286
|
+
process.stderr.write(
|
|
2287
|
+
`[memory] Session curation: ${supersededCount} superseded, ${mergedCount} merged, ${splitCount} split, ${recalibratedCount} recalibrated
|
|
2288
|
+
`
|
|
2289
|
+
);
|
|
2290
|
+
}
|
|
2291
|
+
} catch {
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
};
|
|
2295
|
+
|
|
2041
2296
|
// src/storage/memory-graph-backend.ts
|
|
2042
2297
|
import * as fs6 from "node:fs/promises";
|
|
2043
2298
|
var GraphMemoryBackend = class _GraphMemoryBackend {
|
|
@@ -2389,9 +2644,63 @@ function simpleHash(s) {
|
|
|
2389
2644
|
return Math.abs(h).toString(36);
|
|
2390
2645
|
}
|
|
2391
2646
|
|
|
2647
|
+
// src/storage/orphan-lock-cleaner.ts
|
|
2648
|
+
import * as fsp3 from "node:fs/promises";
|
|
2649
|
+
import * as path8 from "node:path";
|
|
2650
|
+
async function cleanOrphanLocks(projectRoot, options) {
|
|
2651
|
+
const result = {
|
|
2652
|
+
cleanedGitLocks: [],
|
|
2653
|
+
cleanedWorktrees: [],
|
|
2654
|
+
errors: []
|
|
2655
|
+
};
|
|
2656
|
+
const pidAlive2 = options?.isPidAlive ?? isPidAlive;
|
|
2657
|
+
const maxAgeMs = options?.maxLockAgeMs ?? 10 * 60 * 1e3;
|
|
2658
|
+
const now = Date.now();
|
|
2659
|
+
const worktreesDir = path8.join(projectRoot, ".wrongstack", "worktrees");
|
|
2660
|
+
try {
|
|
2661
|
+
const entries = await fsp3.readdir(worktreesDir, { withFileTypes: true });
|
|
2662
|
+
for (const entry of entries) {
|
|
2663
|
+
if (!entry.isDirectory()) continue;
|
|
2664
|
+
const wtPath = path8.join(worktreesDir, entry.name);
|
|
2665
|
+
const lockPath = path8.join(wtPath, ".lock");
|
|
2666
|
+
try {
|
|
2667
|
+
const lockStat = await fsp3.stat(lockPath).catch(() => null);
|
|
2668
|
+
if (lockStat) {
|
|
2669
|
+
const raw = await fsp3.readFile(lockPath, "utf8").catch(() => "");
|
|
2670
|
+
const pid = parseInt(raw.trim(), 10);
|
|
2671
|
+
if (Number.isInteger(pid) && pid > 0) {
|
|
2672
|
+
if (!pidAlive2(pid)) {
|
|
2673
|
+
try {
|
|
2674
|
+
await fsp3.unlink(lockPath);
|
|
2675
|
+
result.cleanedWorktrees.push(wtPath);
|
|
2676
|
+
} catch (unlinkErr) {
|
|
2677
|
+
result.errors.push(`Failed removing stale lock ${lockPath}: ${toErrorMessage(unlinkErr)}`);
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
} else if (now - lockStat.mtimeMs > maxAgeMs) {
|
|
2681
|
+
try {
|
|
2682
|
+
await fsp3.unlink(lockPath);
|
|
2683
|
+
result.cleanedWorktrees.push(wtPath);
|
|
2684
|
+
} catch (unlinkErr) {
|
|
2685
|
+
result.errors.push(`Failed removing expired lock ${lockPath}: ${toErrorMessage(unlinkErr)}`);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
} catch (err) {
|
|
2690
|
+
result.errors.push(`Failed inspecting worktree ${entry.name}: ${toErrorMessage(err)}`);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
} catch (err) {
|
|
2694
|
+
if (err.code !== "ENOENT") {
|
|
2695
|
+
result.errors.push(`Failed reading worktrees dir: ${toErrorMessage(err)}`);
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
return result;
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2392
2701
|
// src/storage/plan-store.ts
|
|
2393
2702
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
2394
|
-
import * as
|
|
2703
|
+
import * as fsp4 from "node:fs/promises";
|
|
2395
2704
|
function assertPlanMutationInvariants(previous, updated) {
|
|
2396
2705
|
const ids = updated.items.map((item) => item.id);
|
|
2397
2706
|
const uniqueIds = new Set(ids);
|
|
@@ -2419,7 +2728,7 @@ async function loadPlan(filePath, events) {
|
|
|
2419
2728
|
const t0 = Date.now();
|
|
2420
2729
|
let raw;
|
|
2421
2730
|
try {
|
|
2422
|
-
raw = await
|
|
2731
|
+
raw = await fsp4.readFile(filePath, "utf8");
|
|
2423
2732
|
} catch (err) {
|
|
2424
2733
|
events?.emit("storage.error", {
|
|
2425
2734
|
sessionId: "~boot~",
|
|
@@ -2731,7 +3040,7 @@ ${cat}:`);
|
|
|
2731
3040
|
// src/storage/provider-config-watcher.ts
|
|
2732
3041
|
import * as syncFs from "node:fs";
|
|
2733
3042
|
import * as fs7 from "node:fs/promises";
|
|
2734
|
-
import * as
|
|
3043
|
+
import * as path9 from "node:path";
|
|
2735
3044
|
async function readProviderSnapshot(configPath, vault, warn) {
|
|
2736
3045
|
let raw;
|
|
2737
3046
|
try {
|
|
@@ -2807,7 +3116,7 @@ function serializeSnapshot(s) {
|
|
|
2807
3116
|
function watchProviderConfig(configPath, vault, onChange, opts = {}) {
|
|
2808
3117
|
const debounceMs = opts.debounceMs ?? 200;
|
|
2809
3118
|
const warn = opts.warn;
|
|
2810
|
-
const base =
|
|
3119
|
+
const base = path9.basename(configPath);
|
|
2811
3120
|
let timer;
|
|
2812
3121
|
let closed = false;
|
|
2813
3122
|
let lastSerialized;
|
|
@@ -2818,7 +3127,7 @@ function watchProviderConfig(configPath, vault, onChange, opts = {}) {
|
|
|
2818
3127
|
});
|
|
2819
3128
|
let watcher;
|
|
2820
3129
|
try {
|
|
2821
|
-
watcher = syncFs.watch(
|
|
3130
|
+
watcher = syncFs.watch(path9.dirname(configPath), { recursive: false });
|
|
2822
3131
|
} catch (err) {
|
|
2823
3132
|
warn?.(`Provider config watcher could not start: ${err.message}`);
|
|
2824
3133
|
return { close: () => {
|
|
@@ -2861,8 +3170,8 @@ function watchProviderConfig(configPath, vault, onChange, opts = {}) {
|
|
|
2861
3170
|
}
|
|
2862
3171
|
|
|
2863
3172
|
// src/storage/queue-store.ts
|
|
2864
|
-
import * as
|
|
2865
|
-
import * as
|
|
3173
|
+
import * as fsp5 from "node:fs/promises";
|
|
3174
|
+
import * as path10 from "node:path";
|
|
2866
3175
|
var QUEUE_MAX_ITEMS = 100;
|
|
2867
3176
|
var QUEUE_MAX_BYTES = 16 * 1024 * 1024;
|
|
2868
3177
|
var QUEUE_MAX_ITEM_BYTES = 8 * 1024 * 1024;
|
|
@@ -2895,7 +3204,7 @@ var QueueStore = class {
|
|
|
2895
3204
|
traceId;
|
|
2896
3205
|
logger;
|
|
2897
3206
|
constructor(opts) {
|
|
2898
|
-
this.file =
|
|
3207
|
+
this.file = path10.join(opts.dir, "queue.json");
|
|
2899
3208
|
this.events = opts.events;
|
|
2900
3209
|
this.traceId = opts.traceId;
|
|
2901
3210
|
this.logger = opts.logger;
|
|
@@ -2964,12 +3273,12 @@ var QueueStore = class {
|
|
|
2964
3273
|
const t0 = Date.now();
|
|
2965
3274
|
let raw;
|
|
2966
3275
|
try {
|
|
2967
|
-
const
|
|
2968
|
-
if (
|
|
3276
|
+
const stat8 = await fsp5.stat(this.file);
|
|
3277
|
+
if (stat8.size > QUEUE_MAX_BYTES) {
|
|
2969
3278
|
this.logWarn("Queue file exceeds the safe read budget; ignoring it", {
|
|
2970
3279
|
event: "queue_store.read_budget_exceeded",
|
|
2971
3280
|
path: this.file,
|
|
2972
|
-
bytes:
|
|
3281
|
+
bytes: stat8.size,
|
|
2973
3282
|
maxBytes: QUEUE_MAX_BYTES
|
|
2974
3283
|
});
|
|
2975
3284
|
this.events?.emit("storage.read", {
|
|
@@ -2984,7 +3293,7 @@ var QueueStore = class {
|
|
|
2984
3293
|
});
|
|
2985
3294
|
return [];
|
|
2986
3295
|
}
|
|
2987
|
-
raw = await
|
|
3296
|
+
raw = await fsp5.readFile(this.file, "utf8");
|
|
2988
3297
|
} catch (err) {
|
|
2989
3298
|
const code = err.code;
|
|
2990
3299
|
if (code === "ENOENT") {
|
|
@@ -3071,7 +3380,7 @@ var QueueStore = class {
|
|
|
3071
3380
|
async clear() {
|
|
3072
3381
|
const t0 = Date.now();
|
|
3073
3382
|
try {
|
|
3074
|
-
await
|
|
3383
|
+
await fsp5.unlink(this.file);
|
|
3075
3384
|
this.events?.emit("storage.write", {
|
|
3076
3385
|
sessionId: this.traceId ?? "~boot~",
|
|
3077
3386
|
store: "queue",
|
|
@@ -3109,9 +3418,9 @@ function isPersistedQueueItem(v) {
|
|
|
3109
3418
|
}
|
|
3110
3419
|
|
|
3111
3420
|
// src/storage/recovery-lock.ts
|
|
3112
|
-
import * as
|
|
3421
|
+
import * as fsp6 from "node:fs/promises";
|
|
3113
3422
|
import * as os from "node:os";
|
|
3114
|
-
import * as
|
|
3423
|
+
import * as path11 from "node:path";
|
|
3115
3424
|
var LOCK_FILE = "active.json";
|
|
3116
3425
|
var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
3117
3426
|
var RecoveryLock = class {
|
|
@@ -3122,7 +3431,7 @@ var RecoveryLock = class {
|
|
|
3122
3431
|
sessionStore;
|
|
3123
3432
|
probe;
|
|
3124
3433
|
constructor(opts) {
|
|
3125
|
-
this.file =
|
|
3434
|
+
this.file = path11.join(opts.dir, LOCK_FILE);
|
|
3126
3435
|
this.pid = opts.pid ?? process.pid;
|
|
3127
3436
|
this.hostname = opts.hostname ?? os.hostname();
|
|
3128
3437
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
@@ -3160,13 +3469,14 @@ var RecoveryLock = class {
|
|
|
3160
3469
|
if (this.sessionStore) {
|
|
3161
3470
|
try {
|
|
3162
3471
|
const data = await this.sessionStore.load(lock.sessionId);
|
|
3163
|
-
const
|
|
3164
|
-
const
|
|
3472
|
+
const events = Array.isArray(data?.events) ? data.events : [];
|
|
3473
|
+
const lastEnd = events.findLastIndex ? events.findLastIndex((e) => e.type === "session_end") : -1;
|
|
3474
|
+
const closed = lastEnd >= 0 && !events.slice(lastEnd + 1).some(
|
|
3165
3475
|
(e) => e.type === "user_input" || e.type === "llm_response" || e.type === "in_flight_start"
|
|
3166
3476
|
);
|
|
3167
3477
|
if (closed) return null;
|
|
3168
|
-
messageCount = data.messages.length;
|
|
3169
|
-
for (const event of
|
|
3478
|
+
messageCount = Array.isArray(data?.messages) ? data.messages.length : 0;
|
|
3479
|
+
for (const event of events) {
|
|
3170
3480
|
if (event.type !== "user_input") continue;
|
|
3171
3481
|
if (title === void 0) title = userInputTitle(event.content);
|
|
3172
3482
|
lastUserMessage = sessionContentPreview(event.content);
|
|
@@ -3196,7 +3506,7 @@ var RecoveryLock = class {
|
|
|
3196
3506
|
* null return before calling this.
|
|
3197
3507
|
*/
|
|
3198
3508
|
async write(sessionId) {
|
|
3199
|
-
await ensureDir(
|
|
3509
|
+
await ensureDir(path11.dirname(this.file));
|
|
3200
3510
|
const lock = {
|
|
3201
3511
|
v: 1,
|
|
3202
3512
|
sessionId,
|
|
@@ -3205,7 +3515,7 @@ var RecoveryLock = class {
|
|
|
3205
3515
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3206
3516
|
};
|
|
3207
3517
|
try {
|
|
3208
|
-
await
|
|
3518
|
+
await fsp6.writeFile(this.file, JSON.stringify(lock), { flag: "wx", mode: 384 });
|
|
3209
3519
|
} catch (err) {
|
|
3210
3520
|
const code = err.code;
|
|
3211
3521
|
if (code === "EEXIST") {
|
|
@@ -3221,7 +3531,7 @@ var RecoveryLock = class {
|
|
|
3221
3531
|
*/
|
|
3222
3532
|
async clear() {
|
|
3223
3533
|
try {
|
|
3224
|
-
await
|
|
3534
|
+
await fsp6.unlink(this.file);
|
|
3225
3535
|
} catch (err) {
|
|
3226
3536
|
const code = err.code;
|
|
3227
3537
|
if (code === "ENOENT") return;
|
|
@@ -3231,7 +3541,7 @@ var RecoveryLock = class {
|
|
|
3231
3541
|
async readLock() {
|
|
3232
3542
|
let raw;
|
|
3233
3543
|
try {
|
|
3234
|
-
raw = await
|
|
3544
|
+
raw = await fsp6.readFile(this.file, "utf8");
|
|
3235
3545
|
} catch (err) {
|
|
3236
3546
|
const code = err.code;
|
|
3237
3547
|
if (code === "ENOENT") return null;
|
|
@@ -3253,55 +3563,9 @@ function isLockFile(v) {
|
|
|
3253
3563
|
}
|
|
3254
3564
|
var defaultIsPidAlive = isPidAlive;
|
|
3255
3565
|
|
|
3256
|
-
// src/storage/orphan-lock-cleaner.ts
|
|
3257
|
-
import * as fsp6 from "node:fs/promises";
|
|
3258
|
-
import * as path10 from "node:path";
|
|
3259
|
-
async function cleanOrphanLocks(projectRoot, options) {
|
|
3260
|
-
const result = {
|
|
3261
|
-
cleanedGitLocks: [],
|
|
3262
|
-
cleanedWorktrees: [],
|
|
3263
|
-
errors: []
|
|
3264
|
-
};
|
|
3265
|
-
const pidAlive2 = options?.isPidAlive ?? isPidAlive;
|
|
3266
|
-
const maxAgeMs = options?.maxLockAgeMs ?? 10 * 60 * 1e3;
|
|
3267
|
-
const now = Date.now();
|
|
3268
|
-
const worktreesDir = path10.join(projectRoot, ".wrongstack", "worktrees");
|
|
3269
|
-
try {
|
|
3270
|
-
const entries = await fsp6.readdir(worktreesDir, { withFileTypes: true });
|
|
3271
|
-
for (const entry of entries) {
|
|
3272
|
-
if (!entry.isDirectory()) continue;
|
|
3273
|
-
const wtPath = path10.join(worktreesDir, entry.name);
|
|
3274
|
-
const lockPath = path10.join(wtPath, ".lock");
|
|
3275
|
-
try {
|
|
3276
|
-
const lockStat = await fsp6.stat(lockPath).catch(() => null);
|
|
3277
|
-
if (lockStat) {
|
|
3278
|
-
const raw = await fsp6.readFile(lockPath, "utf8").catch(() => "");
|
|
3279
|
-
const pid = parseInt(raw.trim(), 10);
|
|
3280
|
-
if (Number.isInteger(pid) && pid > 0) {
|
|
3281
|
-
if (!pidAlive2(pid)) {
|
|
3282
|
-
await fsp6.unlink(lockPath).catch(() => void 0);
|
|
3283
|
-
result.cleanedWorktrees.push(wtPath);
|
|
3284
|
-
}
|
|
3285
|
-
} else if (now - lockStat.mtimeMs > maxAgeMs) {
|
|
3286
|
-
await fsp6.unlink(lockPath).catch(() => void 0);
|
|
3287
|
-
result.cleanedWorktrees.push(wtPath);
|
|
3288
|
-
}
|
|
3289
|
-
}
|
|
3290
|
-
} catch (err) {
|
|
3291
|
-
result.errors.push(`Failed inspecting worktree ${entry.name}: ${toErrorMessage(err)}`);
|
|
3292
|
-
}
|
|
3293
|
-
}
|
|
3294
|
-
} catch (err) {
|
|
3295
|
-
if (err.code !== "ENOENT") {
|
|
3296
|
-
result.errors.push(`Failed reading worktrees dir: ${toErrorMessage(err)}`);
|
|
3297
|
-
}
|
|
3298
|
-
}
|
|
3299
|
-
return result;
|
|
3300
|
-
}
|
|
3301
|
-
|
|
3302
3566
|
// src/storage/replay-log-store.ts
|
|
3303
3567
|
import * as fs8 from "node:fs/promises";
|
|
3304
|
-
import * as
|
|
3568
|
+
import * as path12 from "node:path";
|
|
3305
3569
|
function storageErrorString(err) {
|
|
3306
3570
|
if (err instanceof Error) {
|
|
3307
3571
|
const code = err.code;
|
|
@@ -3355,11 +3619,12 @@ var ReplayLogStore = class _ReplayLogStore {
|
|
|
3355
3619
|
const line = JSON.stringify(entry) + "\n";
|
|
3356
3620
|
let offset2 = 0;
|
|
3357
3621
|
try {
|
|
3358
|
-
const
|
|
3359
|
-
offset2 =
|
|
3622
|
+
const stat8 = await fs8.stat(fp);
|
|
3623
|
+
offset2 = stat8.size;
|
|
3360
3624
|
} catch (err) {
|
|
3361
3625
|
if (err.code !== "ENOENT") throw err;
|
|
3362
3626
|
}
|
|
3627
|
+
await fs8.mkdir(path12.dirname(fp), { recursive: true });
|
|
3363
3628
|
await fs8.appendFile(fp, line, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
3364
3629
|
cache.set(hash, { entry, offset: offset2, length: Buffer.byteLength(line, "utf8") });
|
|
3365
3630
|
this.diskCount.set(input.sessionId, currentCount + 1);
|
|
@@ -3506,13 +3771,13 @@ var ReplayLogStore = class _ReplayLogStore {
|
|
|
3506
3771
|
for (const entry of entries) {
|
|
3507
3772
|
if (entry.name.startsWith(".")) continue;
|
|
3508
3773
|
if (entry.isDirectory()) {
|
|
3509
|
-
if (depth === 0) await scan(
|
|
3774
|
+
if (depth === 0) await scan(path12.join(dir, entry.name), entry.name, depth + 1);
|
|
3510
3775
|
continue;
|
|
3511
3776
|
}
|
|
3512
3777
|
if (!entry.isFile() || !entry.name.endsWith(".replay.jsonl")) continue;
|
|
3513
3778
|
const base = entry.name.slice(0, -".replay.jsonl".length);
|
|
3514
3779
|
const sessionId = prefix ? `${prefix}/${base}` : base;
|
|
3515
|
-
const fp =
|
|
3780
|
+
const fp = path12.join(dir, entry.name);
|
|
3516
3781
|
out.push({
|
|
3517
3782
|
sessionId,
|
|
3518
3783
|
entryCount: await this.countEntries(fp),
|
|
@@ -3721,6 +3986,28 @@ var ReplayLogStore = class _ReplayLogStore {
|
|
|
3721
3986
|
}
|
|
3722
3987
|
};
|
|
3723
3988
|
|
|
3989
|
+
// src/storage/session-agent-attribution.ts
|
|
3990
|
+
function stampAgentId(event, agentId) {
|
|
3991
|
+
if (event.agentId !== void 0) return event;
|
|
3992
|
+
return { ...event, agentId };
|
|
3993
|
+
}
|
|
3994
|
+
function withAgentAttribution(writer, agentId) {
|
|
3995
|
+
const cleanId = agentId?.trim();
|
|
3996
|
+
if (!cleanId) return writer;
|
|
3997
|
+
return new Proxy(writer, {
|
|
3998
|
+
get(target, property, receiver) {
|
|
3999
|
+
if (property === "append") {
|
|
4000
|
+
return (event) => target.append(stampAgentId(event, cleanId));
|
|
4001
|
+
}
|
|
4002
|
+
if (property === "appendBatch") {
|
|
4003
|
+
return (events) => target.appendBatch(events.map((event) => stampAgentId(event, cleanId)));
|
|
4004
|
+
}
|
|
4005
|
+
const value = Reflect.get(target, property, receiver);
|
|
4006
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
4007
|
+
}
|
|
4008
|
+
});
|
|
4009
|
+
}
|
|
4010
|
+
|
|
3724
4011
|
// src/storage/session-analyzer.ts
|
|
3725
4012
|
var SessionAnalyzer = class {
|
|
3726
4013
|
analyze(events) {
|
|
@@ -3802,9 +4089,9 @@ var SessionAnalyzer = class {
|
|
|
3802
4089
|
if (!filter.toolNames.includes(toolEvent.name)) return false;
|
|
3803
4090
|
}
|
|
3804
4091
|
if (filter.timeRange) {
|
|
3805
|
-
const ts =
|
|
3806
|
-
const start =
|
|
3807
|
-
const end =
|
|
4092
|
+
const ts = Date.parse(e.ts) || 0;
|
|
4093
|
+
const start = Date.parse(filter.timeRange.start) || 0;
|
|
4094
|
+
const end = Date.parse(filter.timeRange.end) || Number.POSITIVE_INFINITY;
|
|
3808
4095
|
if (ts < start || ts > end) return false;
|
|
3809
4096
|
}
|
|
3810
4097
|
return true;
|
|
@@ -3815,12 +4102,253 @@ var SessionAnalyzer = class {
|
|
|
3815
4102
|
const firstEvent = events[0];
|
|
3816
4103
|
const lastEvent = events[events.length - 1];
|
|
3817
4104
|
if (!firstEvent || !lastEvent) return 0;
|
|
3818
|
-
const first =
|
|
3819
|
-
const last =
|
|
3820
|
-
return last - first;
|
|
4105
|
+
const first = Date.parse(firstEvent.ts) || 0;
|
|
4106
|
+
const last = Date.parse(lastEvent.ts) || 0;
|
|
4107
|
+
return Math.max(0, last - first);
|
|
3821
4108
|
}
|
|
3822
4109
|
};
|
|
3823
4110
|
|
|
4111
|
+
// src/storage/session-doctor.ts
|
|
4112
|
+
import { createReadStream } from "node:fs";
|
|
4113
|
+
import * as fsp7 from "node:fs/promises";
|
|
4114
|
+
import * as path13 from "node:path";
|
|
4115
|
+
import { createInterface } from "node:readline";
|
|
4116
|
+
var BOUNDARY_TYPES = /* @__PURE__ */ new Set([
|
|
4117
|
+
"session_start",
|
|
4118
|
+
"in_flight_start",
|
|
4119
|
+
"in_flight_end",
|
|
4120
|
+
"session_end"
|
|
4121
|
+
]);
|
|
4122
|
+
var TYPE_RE = /"type"\s*:\s*"([A-Za-z0-9_:.-]+)"/;
|
|
4123
|
+
var TYPE_PROBE_CHARS = 200;
|
|
4124
|
+
function readEventType(line) {
|
|
4125
|
+
if (line.length === 0 || line[0] !== "{") return null;
|
|
4126
|
+
const fast = TYPE_RE.exec(line.slice(0, TYPE_PROBE_CHARS));
|
|
4127
|
+
if (fast?.[1]) return fast[1];
|
|
4128
|
+
return line.length > TYPE_PROBE_CHARS ? TYPE_RE.exec(line)?.[1] ?? null : null;
|
|
4129
|
+
}
|
|
4130
|
+
function severityRank(diagnosis) {
|
|
4131
|
+
if (diagnosis.findings.some((f) => f.severity === "error")) return 0;
|
|
4132
|
+
if (diagnosis.findings.some((f) => f.severity === "warn")) return 1;
|
|
4133
|
+
return 2;
|
|
4134
|
+
}
|
|
4135
|
+
async function collectJournals(sessionsDir) {
|
|
4136
|
+
const out = [];
|
|
4137
|
+
let days;
|
|
4138
|
+
try {
|
|
4139
|
+
const entries = await fsp7.readdir(sessionsDir, { withFileTypes: true });
|
|
4140
|
+
days = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4141
|
+
} catch {
|
|
4142
|
+
return out;
|
|
4143
|
+
}
|
|
4144
|
+
for (const day of days) {
|
|
4145
|
+
if (day.startsWith("_")) continue;
|
|
4146
|
+
let names;
|
|
4147
|
+
try {
|
|
4148
|
+
names = await fsp7.readdir(path13.join(sessionsDir, day));
|
|
4149
|
+
} catch {
|
|
4150
|
+
continue;
|
|
4151
|
+
}
|
|
4152
|
+
for (const name of names) {
|
|
4153
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
4154
|
+
out.push({
|
|
4155
|
+
id: `${day}/${name.slice(0, -".jsonl".length)}`,
|
|
4156
|
+
file: path13.join(sessionsDir, day, name)
|
|
4157
|
+
});
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
return out.sort((a, b) => a.id.localeCompare(b.id));
|
|
4161
|
+
}
|
|
4162
|
+
async function endsWithNewline(file, bytes) {
|
|
4163
|
+
if (bytes === 0) return true;
|
|
4164
|
+
const handle = await fsp7.open(file, "r");
|
|
4165
|
+
try {
|
|
4166
|
+
const buffer = Buffer.alloc(1);
|
|
4167
|
+
await handle.read(buffer, 0, 1, bytes - 1);
|
|
4168
|
+
return buffer.toString("utf8") === "\n";
|
|
4169
|
+
} finally {
|
|
4170
|
+
await handle.close();
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
async function scanJournal(id, file, oversizedBytes, signal) {
|
|
4174
|
+
const stat8 = await fsp7.stat(file);
|
|
4175
|
+
const diagnosis = {
|
|
4176
|
+
id,
|
|
4177
|
+
file,
|
|
4178
|
+
bytes: stat8.size,
|
|
4179
|
+
lines: 0,
|
|
4180
|
+
unparsableLines: 0,
|
|
4181
|
+
snapshotBytes: 0,
|
|
4182
|
+
lastBoundary: null,
|
|
4183
|
+
hasSessionStart: false,
|
|
4184
|
+
hasSummarySidecar: false,
|
|
4185
|
+
summaryStale: false,
|
|
4186
|
+
findings: []
|
|
4187
|
+
};
|
|
4188
|
+
const stream = createReadStream(file, { encoding: "utf8" });
|
|
4189
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
4190
|
+
try {
|
|
4191
|
+
for await (const line of rl) {
|
|
4192
|
+
signal?.throwIfAborted();
|
|
4193
|
+
diagnosis.lines++;
|
|
4194
|
+
if (line.length === 0) continue;
|
|
4195
|
+
const type = readEventType(line);
|
|
4196
|
+
if (type === null) {
|
|
4197
|
+
diagnosis.unparsableLines++;
|
|
4198
|
+
continue;
|
|
4199
|
+
}
|
|
4200
|
+
if (type === "session_start") diagnosis.hasSessionStart = true;
|
|
4201
|
+
if (BOUNDARY_TYPES.has(type)) {
|
|
4202
|
+
diagnosis.lastBoundary = type;
|
|
4203
|
+
}
|
|
4204
|
+
if (type === "messages_replaced" || type === "context_snapshot") {
|
|
4205
|
+
diagnosis.snapshotBytes += line.length;
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
} finally {
|
|
4209
|
+
rl.close();
|
|
4210
|
+
stream.destroy();
|
|
4211
|
+
}
|
|
4212
|
+
const sidecar = `${file.slice(0, -".jsonl".length)}.summary.json`;
|
|
4213
|
+
const sidecarStat = await fsp7.stat(sidecar).catch(() => null);
|
|
4214
|
+
diagnosis.hasSummarySidecar = sidecarStat !== null;
|
|
4215
|
+
diagnosis.summaryStale = sidecarStat !== null && sidecarStat.mtimeMs + 1 < stat8.mtimeMs;
|
|
4216
|
+
const add = (finding) => {
|
|
4217
|
+
diagnosis.findings.push(finding);
|
|
4218
|
+
};
|
|
4219
|
+
if (diagnosis.lines === 0) {
|
|
4220
|
+
add({ code: "empty", severity: "info", detail: "no events recorded" });
|
|
4221
|
+
}
|
|
4222
|
+
if (diagnosis.unparsableLines > 0) {
|
|
4223
|
+
add({
|
|
4224
|
+
code: "unparsable_lines",
|
|
4225
|
+
severity: "error",
|
|
4226
|
+
// The loader skips these without a word, so the only symptom is a
|
|
4227
|
+
// transcript that is quietly missing turns.
|
|
4228
|
+
detail: `${diagnosis.unparsableLines} of ${diagnosis.lines} lines cannot be parsed \u2014 the loader skips them silently`
|
|
4229
|
+
});
|
|
4230
|
+
}
|
|
4231
|
+
if (!await endsWithNewline(file, stat8.size)) {
|
|
4232
|
+
add({
|
|
4233
|
+
code: "truncated_tail",
|
|
4234
|
+
severity: "warn",
|
|
4235
|
+
detail: "last line is incomplete \u2014 the process died mid-write"
|
|
4236
|
+
});
|
|
4237
|
+
}
|
|
4238
|
+
if (diagnosis.lines > 0 && !diagnosis.hasSessionStart) {
|
|
4239
|
+
add({
|
|
4240
|
+
code: "missing_session_start",
|
|
4241
|
+
severity: "warn",
|
|
4242
|
+
detail: "no session_start \u2014 model/provider metadata is unknown"
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
if (diagnosis.lastBoundary === "in_flight_start") {
|
|
4246
|
+
add({
|
|
4247
|
+
code: "died_mid_turn",
|
|
4248
|
+
severity: "warn",
|
|
4249
|
+
// Not a defect to fix here: `store.resume()` heals it on open, pairing
|
|
4250
|
+
// the dangling tool_uses with synthesized results.
|
|
4251
|
+
detail: "died mid-turn \u2014 resume repairs this on open"
|
|
4252
|
+
});
|
|
4253
|
+
} else if (diagnosis.lines > 0 && diagnosis.lastBoundary !== "session_end") {
|
|
4254
|
+
add({
|
|
4255
|
+
code: "never_closed",
|
|
4256
|
+
severity: "info",
|
|
4257
|
+
detail: "no session_end \u2014 killed rather than closed"
|
|
4258
|
+
});
|
|
4259
|
+
}
|
|
4260
|
+
if (!diagnosis.hasSummarySidecar && diagnosis.lines > 0) {
|
|
4261
|
+
add({
|
|
4262
|
+
code: "missing_summary",
|
|
4263
|
+
severity: "warn",
|
|
4264
|
+
detail: "no .summary.json \u2014 the picker has no title or counts for it",
|
|
4265
|
+
fix: "rebuild the summary from the journal"
|
|
4266
|
+
});
|
|
4267
|
+
} else if (diagnosis.summaryStale) {
|
|
4268
|
+
add({
|
|
4269
|
+
code: "stale_summary",
|
|
4270
|
+
severity: "info",
|
|
4271
|
+
detail: ".summary.json is older than the transcript",
|
|
4272
|
+
fix: "rebuild the summary from the journal"
|
|
4273
|
+
});
|
|
4274
|
+
}
|
|
4275
|
+
if (stat8.size >= oversizedBytes) {
|
|
4276
|
+
const share = stat8.size > 0 ? Math.round(diagnosis.snapshotBytes / stat8.size * 100) : 0;
|
|
4277
|
+
add({
|
|
4278
|
+
code: "oversized",
|
|
4279
|
+
severity: "info",
|
|
4280
|
+
detail: `${(stat8.size / 1e6).toFixed(0)} MB \u2014 slow to open` + (share >= 50 ? `; ${share}% is superseded conversation snapshots` : "")
|
|
4281
|
+
});
|
|
4282
|
+
}
|
|
4283
|
+
return diagnosis;
|
|
4284
|
+
}
|
|
4285
|
+
async function diagnoseSessions(options) {
|
|
4286
|
+
const { sessionsDir, onProgress, signal } = options;
|
|
4287
|
+
const oversizedBytes = options.oversizedBytes ?? 64 * 1024 * 1024;
|
|
4288
|
+
const journals = await collectJournals(sessionsDir);
|
|
4289
|
+
const sessions = [];
|
|
4290
|
+
const unreadable = [];
|
|
4291
|
+
let scanned = 0;
|
|
4292
|
+
for (const { id, file } of journals) {
|
|
4293
|
+
signal?.throwIfAborted();
|
|
4294
|
+
onProgress?.({ scanned, total: journals.length, id });
|
|
4295
|
+
try {
|
|
4296
|
+
sessions.push(await scanJournal(id, file, oversizedBytes, signal));
|
|
4297
|
+
} catch (error) {
|
|
4298
|
+
if (signal?.aborted) throw error;
|
|
4299
|
+
unreadable.push({ id, reason: error instanceof Error ? error.message : String(error) });
|
|
4300
|
+
}
|
|
4301
|
+
scanned++;
|
|
4302
|
+
}
|
|
4303
|
+
onProgress?.({ scanned, total: journals.length, id: "" });
|
|
4304
|
+
const totals = {
|
|
4305
|
+
sessions: sessions.length,
|
|
4306
|
+
bytes: sessions.reduce((sum, s) => sum + s.bytes, 0),
|
|
4307
|
+
snapshotBytes: sessions.reduce((sum, s) => sum + s.snapshotBytes, 0),
|
|
4308
|
+
unparsableLines: sessions.reduce((sum, s) => sum + s.unparsableLines, 0)
|
|
4309
|
+
};
|
|
4310
|
+
const byCode = {};
|
|
4311
|
+
for (const session of sessions) {
|
|
4312
|
+
for (const finding of session.findings) {
|
|
4313
|
+
byCode[finding.code] = (byCode[finding.code] ?? 0) + 1;
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
sessions.sort((a, b) => severityRank(a) - severityRank(b) || b.bytes - a.bytes);
|
|
4317
|
+
return { sessionsDir, totals, byCode, sessions, unreadable };
|
|
4318
|
+
}
|
|
4319
|
+
async function repairSessionSummaries(options) {
|
|
4320
|
+
const { report, onProgress, signal } = options;
|
|
4321
|
+
const scrubber = options.secretScrubber ?? new DefaultSecretScrubber();
|
|
4322
|
+
const targets = report.sessions.filter(
|
|
4323
|
+
(session) => session.findings.some((f) => f.code === "missing_summary" || f.code === "stale_summary")
|
|
4324
|
+
);
|
|
4325
|
+
const result = { repaired: [], failed: [] };
|
|
4326
|
+
for (const session of targets) {
|
|
4327
|
+
signal?.throwIfAborted();
|
|
4328
|
+
onProgress?.({ repaired: result.repaired.length, total: targets.length, id: session.id });
|
|
4329
|
+
try {
|
|
4330
|
+
const stat8 = await fsp7.stat(session.file);
|
|
4331
|
+
const summary = await summarizeSessionFile({
|
|
4332
|
+
id: session.id,
|
|
4333
|
+
file: session.file,
|
|
4334
|
+
mtime: stat8.mtime.toISOString(),
|
|
4335
|
+
secretScrubber: scrubber
|
|
4336
|
+
});
|
|
4337
|
+
const sidecar = `${session.file.slice(0, -".jsonl".length)}.summary.json`;
|
|
4338
|
+
await atomicWrite(sidecar, `${JSON.stringify(summary, null, 2)}
|
|
4339
|
+
`, { mode: 384 });
|
|
4340
|
+
result.repaired.push(session.id);
|
|
4341
|
+
} catch (error) {
|
|
4342
|
+
result.failed.push({
|
|
4343
|
+
id: session.id,
|
|
4344
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
onProgress?.({ repaired: result.repaired.length, total: targets.length, id: "" });
|
|
4349
|
+
return result;
|
|
4350
|
+
}
|
|
4351
|
+
|
|
3824
4352
|
// src/storage/session-event-bridge.ts
|
|
3825
4353
|
var CORE_RECONSTRUCT_EVENTS = /* @__PURE__ */ new Set([
|
|
3826
4354
|
"session_start",
|
|
@@ -3828,6 +4356,7 @@ var CORE_RECONSTRUCT_EVENTS = /* @__PURE__ */ new Set([
|
|
|
3828
4356
|
"session_forked",
|
|
3829
4357
|
"user_input",
|
|
3830
4358
|
"llm_response",
|
|
4359
|
+
"tool_use",
|
|
3831
4360
|
"tool_result",
|
|
3832
4361
|
"message_appended",
|
|
3833
4362
|
"message_updated",
|
|
@@ -3844,7 +4373,6 @@ var CORE_RECONSTRUCT_EVENTS = /* @__PURE__ */ new Set([
|
|
|
3844
4373
|
]);
|
|
3845
4374
|
var STANDARD_AUDIT_EVENTS = /* @__PURE__ */ new Set([
|
|
3846
4375
|
"llm_request",
|
|
3847
|
-
"tool_use",
|
|
3848
4376
|
"tool_call_start",
|
|
3849
4377
|
"tool_call_end",
|
|
3850
4378
|
"compaction",
|
|
@@ -3860,8 +4388,13 @@ var STANDARD_AUDIT_EVENTS = /* @__PURE__ */ new Set([
|
|
|
3860
4388
|
// at the DEFAULT level and the resume timeline would lose them silently.
|
|
3861
4389
|
"mode_changed",
|
|
3862
4390
|
"agent_spawned",
|
|
4391
|
+
"agent_session_linked",
|
|
3863
4392
|
"agent_stopped",
|
|
3864
4393
|
"agent_error",
|
|
4394
|
+
"delegate_started",
|
|
4395
|
+
"delegate_completed",
|
|
4396
|
+
"loop_detected",
|
|
4397
|
+
"model_switched",
|
|
3865
4398
|
"task_created",
|
|
3866
4399
|
"task_updated",
|
|
3867
4400
|
"task_completed",
|
|
@@ -4322,10 +4855,10 @@ async function applyRewindToConversation(opts) {
|
|
|
4322
4855
|
}
|
|
4323
4856
|
|
|
4324
4857
|
// src/storage/session-rewinder.ts
|
|
4325
|
-
import { createReadStream } from "node:fs";
|
|
4326
|
-
import * as
|
|
4327
|
-
import * as
|
|
4328
|
-
import { createInterface } from "node:readline";
|
|
4858
|
+
import { createReadStream as createReadStream2 } from "node:fs";
|
|
4859
|
+
import * as fsp8 from "node:fs/promises";
|
|
4860
|
+
import * as path14 from "node:path";
|
|
4861
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
4329
4862
|
var DefaultSessionRewinder = class {
|
|
4330
4863
|
constructor(sessionsDir, projectRoot) {
|
|
4331
4864
|
this.sessionsDir = sessionsDir;
|
|
@@ -4337,8 +4870,8 @@ var DefaultSessionRewinder = class {
|
|
|
4337
4870
|
return sessionScopedPath(this.sessionsDir, sessionId, ".jsonl");
|
|
4338
4871
|
}
|
|
4339
4872
|
async *readEvents(file) {
|
|
4340
|
-
const stream =
|
|
4341
|
-
const lines =
|
|
4873
|
+
const stream = createReadStream2(file, { encoding: "utf8" });
|
|
4874
|
+
const lines = createInterface2({ input: stream, crlfDelay: Infinity });
|
|
4342
4875
|
try {
|
|
4343
4876
|
for await (const line of lines) {
|
|
4344
4877
|
if (!line.trim()) continue;
|
|
@@ -4458,10 +4991,10 @@ async function revertSnapshots(snapshots, projectRoot) {
|
|
|
4458
4991
|
for (const snapshot of [...snapshots].reverse()) {
|
|
4459
4992
|
for (const file of [...snapshot.files].reverse()) {
|
|
4460
4993
|
try {
|
|
4461
|
-
const absPath =
|
|
4462
|
-
const root =
|
|
4463
|
-
const rel =
|
|
4464
|
-
if (rel.startsWith("..") ||
|
|
4994
|
+
const absPath = path14.resolve(file.path);
|
|
4995
|
+
const root = path14.resolve(projectRoot);
|
|
4996
|
+
const rel = path14.relative(root, absPath);
|
|
4997
|
+
if (rel.startsWith("..") || path14.isAbsolute(rel)) {
|
|
4465
4998
|
errors.push(`${file.path}: path resolves outside project root \u2014 skipping`);
|
|
4466
4999
|
continue;
|
|
4467
5000
|
}
|
|
@@ -4471,7 +5004,7 @@ async function revertSnapshots(snapshots, projectRoot) {
|
|
|
4471
5004
|
revertedFiles.push(file.path);
|
|
4472
5005
|
}
|
|
4473
5006
|
} else if (file.action === "created") {
|
|
4474
|
-
await
|
|
5007
|
+
await fsp8.unlink(file.path);
|
|
4475
5008
|
revertedFiles.push(file.path);
|
|
4476
5009
|
} else if (file.action === "modified") {
|
|
4477
5010
|
if (file.before !== null) {
|
|
@@ -4488,7 +5021,7 @@ async function revertSnapshots(snapshots, projectRoot) {
|
|
|
4488
5021
|
}
|
|
4489
5022
|
|
|
4490
5023
|
// src/storage/task-store.ts
|
|
4491
|
-
import * as
|
|
5024
|
+
import * as fsp9 from "node:fs/promises";
|
|
4492
5025
|
function assertTaskMutationInvariants(previous, updated) {
|
|
4493
5026
|
const ids = updated.tasks.map((task) => task.id);
|
|
4494
5027
|
const uniqueIds = new Set(ids);
|
|
@@ -4549,7 +5082,7 @@ async function loadTasks(filePath, events, traceId) {
|
|
|
4549
5082
|
const t0 = Date.now();
|
|
4550
5083
|
let raw;
|
|
4551
5084
|
try {
|
|
4552
|
-
raw = await
|
|
5085
|
+
raw = await fsp9.readFile(filePath, "utf8");
|
|
4553
5086
|
} catch (err) {
|
|
4554
5087
|
events?.emit("storage.error", {
|
|
4555
5088
|
sessionId: traceId ?? "~boot~",
|
|
@@ -4658,12 +5191,12 @@ async function mutateTasks(filePath, sessionId, fn, events, traceId) {
|
|
|
4658
5191
|
}
|
|
4659
5192
|
|
|
4660
5193
|
// src/storage/todos-checkpoint.ts
|
|
4661
|
-
import * as
|
|
5194
|
+
import * as fsp10 from "node:fs/promises";
|
|
4662
5195
|
async function loadTodosCheckpoint(filePath, events, traceId, sessionId) {
|
|
4663
5196
|
const t0 = Date.now();
|
|
4664
5197
|
let raw;
|
|
4665
5198
|
try {
|
|
4666
|
-
raw = await
|
|
5199
|
+
raw = await fsp10.readFile(filePath, "utf8");
|
|
4667
5200
|
} catch (err) {
|
|
4668
5201
|
events?.emit("storage.error", {
|
|
4669
5202
|
sessionId: sessionId ?? "~boot~",
|
|
@@ -5186,7 +5719,9 @@ export {
|
|
|
5186
5719
|
FileMemoryBackend,
|
|
5187
5720
|
parseEntries,
|
|
5188
5721
|
SessionMemoryConsolidator,
|
|
5722
|
+
SessionMemoryCurator,
|
|
5189
5723
|
GraphMemoryBackend,
|
|
5724
|
+
cleanOrphanLocks,
|
|
5190
5725
|
loadPlan,
|
|
5191
5726
|
savePlan,
|
|
5192
5727
|
emptyPlan,
|
|
@@ -5209,9 +5744,12 @@ export {
|
|
|
5209
5744
|
retainPersistedQueueItems,
|
|
5210
5745
|
QueueStore,
|
|
5211
5746
|
RecoveryLock,
|
|
5212
|
-
cleanOrphanLocks,
|
|
5213
5747
|
ReplayLogStore,
|
|
5748
|
+
stampAgentId,
|
|
5749
|
+
withAgentAttribution,
|
|
5214
5750
|
SessionAnalyzer,
|
|
5751
|
+
diagnoseSessions,
|
|
5752
|
+
repairSessionSummaries,
|
|
5215
5753
|
CORE_RECONSTRUCT_EVENTS,
|
|
5216
5754
|
STANDARD_AUDIT_EVENTS,
|
|
5217
5755
|
createSessionEventBridge,
|
|
@@ -5229,4 +5767,4 @@ export {
|
|
|
5229
5767
|
attachTodosCheckpoint,
|
|
5230
5768
|
ToolAuditLog
|
|
5231
5769
|
};
|
|
5232
|
-
//# sourceMappingURL=chunk-
|
|
5770
|
+
//# sourceMappingURL=chunk-WLYKU45P.js.map
|