@goondocks/myco 0.20.2 → 0.21.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/bin/myco-run +68 -7
- package/dist/agent-eval-YK2VP2S4.js +356 -0
- package/dist/agent-eval-YK2VP2S4.js.map +1 -0
- package/dist/{agent-run-X25Q2A6T.js → agent-run-GEJBD2YD.js} +10 -8
- package/dist/{agent-run-X25Q2A6T.js.map → agent-run-GEJBD2YD.js.map} +1 -1
- package/dist/{agent-tasks-7B6OFERB.js → agent-tasks-5XSRGTRX.js} +10 -8
- package/dist/{agent-tasks-7B6OFERB.js.map → agent-tasks-5XSRGTRX.js.map} +1 -1
- package/dist/{chunk-OD4AA7PV.js → chunk-53RPGOEN.js} +56 -8
- package/dist/chunk-53RPGOEN.js.map +1 -0
- package/dist/chunk-54SXG5HF.js +26 -0
- package/dist/chunk-54SXG5HF.js.map +1 -0
- package/dist/{chunk-XATDZX7U.js → chunk-6ALVMIB4.js} +19 -5
- package/dist/{chunk-XATDZX7U.js.map → chunk-6ALVMIB4.js.map} +1 -1
- package/dist/{chunk-FLLBJLHM.js → chunk-6C6QZ4PM.js} +9 -5
- package/dist/chunk-6C6QZ4PM.js.map +1 -0
- package/dist/{chunk-CCRGY3QW.js → chunk-AUIXX33A.js} +24 -95
- package/dist/chunk-AUIXX33A.js.map +1 -0
- package/dist/chunk-CISWUP5W.js +101 -0
- package/dist/chunk-CISWUP5W.js.map +1 -0
- package/dist/{chunk-MYOZLMB2.js → chunk-DTWUHHFI.js} +576 -20
- package/dist/chunk-DTWUHHFI.js.map +1 -0
- package/dist/chunk-EEOJWLMP.js +582 -0
- package/dist/chunk-EEOJWLMP.js.map +1 -0
- package/dist/{chunk-XG5RRUYF.js → chunk-ENZR5NG7.js} +2 -2
- package/dist/{chunk-6RFZWV4R.js → chunk-FCJ5JV54.js} +1 -1
- package/dist/{chunk-6RFZWV4R.js.map → chunk-FCJ5JV54.js.map} +1 -1
- package/dist/{chunk-US4LNCAT.js → chunk-IPPMYQ2Y.js} +5 -1
- package/dist/chunk-IPPMYQ2Y.js.map +1 -0
- package/dist/{chunk-VVNL26WX.js → chunk-KTTSXYEK.js} +22 -10
- package/dist/chunk-KTTSXYEK.js.map +1 -0
- package/dist/chunk-LQIPXVDH.js +17 -0
- package/dist/chunk-LQIPXVDH.js.map +1 -0
- package/dist/{chunk-DCSGJ7W4.js → chunk-N2DGFACQ.js} +3 -3
- package/dist/chunk-N7Z3LUEZ.js +858 -0
- package/dist/chunk-N7Z3LUEZ.js.map +1 -0
- package/dist/{chunk-2PDWCDKY.js → chunk-NFO7BRCO.js} +10 -7
- package/dist/{chunk-2PDWCDKY.js.map → chunk-NFO7BRCO.js.map} +1 -1
- package/dist/{chunk-Q36VMZST.js → chunk-OTQH5KZW.js} +89 -38
- package/dist/chunk-OTQH5KZW.js.map +1 -0
- package/dist/chunk-OUJSQSKE.js +113 -0
- package/dist/chunk-OUJSQSKE.js.map +1 -0
- package/dist/chunk-OZ3FBAK5.js +50 -0
- package/dist/chunk-OZ3FBAK5.js.map +1 -0
- package/dist/chunk-QATYARI5.js +408 -0
- package/dist/chunk-QATYARI5.js.map +1 -0
- package/dist/{chunk-KESLPBKV.js → chunk-QDLVIW2O.js} +4 -4
- package/dist/{chunk-5XIVBO25.js → chunk-QLLBJEM7.js} +6 -2
- package/dist/chunk-QLLBJEM7.js.map +1 -0
- package/dist/{chunk-EVDQKYCG.js → chunk-RQSJLWP4.js} +13 -2
- package/dist/chunk-RQSJLWP4.js.map +1 -0
- package/dist/{chunk-BPRIYNLE.js → chunk-TKAJ3JVF.js} +3 -3
- package/dist/chunk-TSM6VESW.js +25 -0
- package/dist/chunk-TSM6VESW.js.map +1 -0
- package/dist/{chunk-6X2ERTQV.js → chunk-USVFEWYL.js} +6 -4
- package/dist/{chunk-6X2ERTQV.js.map → chunk-USVFEWYL.js.map} +1 -1
- package/dist/{chunk-JZGN33AY.js → chunk-VRI56337.js} +4 -4
- package/dist/chunk-X2IRGXGF.js +14103 -0
- package/dist/chunk-X2IRGXGF.js.map +1 -0
- package/dist/{chunk-FMRZ26U5.js → chunk-X3IGT5RV.js} +5 -2
- package/dist/{chunk-FMRZ26U5.js.map → chunk-X3IGT5RV.js.map} +1 -1
- package/dist/{chunk-KHT24OWC.js → chunk-YDUOSRGD.js} +8 -94
- package/dist/{chunk-KHT24OWC.js.map → chunk-YDUOSRGD.js.map} +1 -1
- package/dist/{chunk-NGROSFOH.js → chunk-Z66IT5KL.js} +14 -9
- package/dist/chunk-Z66IT5KL.js.map +1 -0
- package/dist/{cli-GGPWH4UO.js → cli-HSLIG7EX.js} +50 -43
- package/dist/cli-HSLIG7EX.js.map +1 -0
- package/dist/{client-YXQUTXVZ.js → client-Z43DNLJH.js} +4 -4
- package/dist/{config-OMCYHG2S.js → config-VC4ACP42.js} +6 -4
- package/dist/{config-OMCYHG2S.js.map → config-VC4ACP42.js.map} +1 -1
- package/dist/{detect-PXNM6TA7.js → detect-7NUD5B5R.js} +2 -2
- package/dist/{detect-providers-5KOPZ7J2.js → detect-providers-ILLQZROY.js} +4 -4
- package/dist/{doctor-5JXJ36KA.js → doctor-HJCWHAU4.js} +49 -16
- package/dist/doctor-HJCWHAU4.js.map +1 -0
- package/dist/executor-DO6QFC6G.js +45 -0
- package/dist/{init-LMYOVZAV.js → init-4KVK7W2E.js} +16 -14
- package/dist/{init-LMYOVZAV.js.map → init-4KVK7W2E.js.map} +1 -1
- package/dist/{installer-FS257JRZ.js → installer-N4UTEACX.js} +6 -4
- package/dist/{llm-TH4NLIRM.js → llm-AGVEF5XD.js} +5 -4
- package/dist/{loader-CQYTFHEW.js → loader-LX7TFRM6.js} +5 -3
- package/dist/{loader-NOMBJUPW.js → loader-UDNUMEDA.js} +5 -3
- package/dist/{main-YTBVRTBI.js → main-4J4QZZTZ.js} +2518 -656
- package/dist/main-4J4QZZTZ.js.map +1 -0
- package/dist/{open-HG2DX6RN.js → open-7TXJQM3H.js} +10 -8
- package/dist/{open-HG2DX6RN.js.map → open-7TXJQM3H.js.map} +1 -1
- package/dist/{post-compact-JSECI44W.js → post-compact-7AEFVCZS.js} +8 -8
- package/dist/{post-tool-use-POGPTJBA.js → post-tool-use-TZINWWDH.js} +11 -9
- package/dist/post-tool-use-TZINWWDH.js.map +1 -0
- package/dist/{post-tool-use-failure-OT7BFWQW.js → post-tool-use-failure-TCFEU2GI.js} +8 -8
- package/dist/{pre-compact-OXVODKH4.js → pre-compact-LO2VZCGR.js} +8 -8
- package/dist/{provider-check-43LAMSMH.js → provider-check-ZEV5P4KM.js} +4 -4
- package/dist/{registry-U4CHXK6R.js → registry-F3THYC5M.js} +6 -4
- package/dist/{remove-N7ZPELFU.js → remove-F77AAALE.js} +12 -10
- package/dist/{remove-N7ZPELFU.js.map → remove-F77AAALE.js.map} +1 -1
- package/dist/{restart-ADG5GBTB.js → restart-UEFDPMLT.js} +11 -9
- package/dist/{restart-ADG5GBTB.js.map → restart-UEFDPMLT.js.map} +1 -1
- package/dist/{search-AHZEUNRR.js → search-NHNVUAQQ.js} +11 -9
- package/dist/{search-AHZEUNRR.js.map → search-NHNVUAQQ.js.map} +1 -1
- package/dist/{server-AGVYZVP5.js → server-AZJSTQEK.js} +369 -270
- package/dist/server-AZJSTQEK.js.map +1 -0
- package/dist/{session-6IU4AXYP.js → session-3HLC5KOD.js} +11 -9
- package/dist/{session-6IU4AXYP.js.map → session-3HLC5KOD.js.map} +1 -1
- package/dist/{session-end-FT27DWYZ.js → session-end-FS46UARX.js} +7 -7
- package/dist/session-start-46KPFV2H.js +134 -0
- package/dist/session-start-46KPFV2H.js.map +1 -0
- package/dist/{setup-llm-77MP4I2G.js → setup-llm-JMWSNQ2C.js} +11 -9
- package/dist/{setup-llm-77MP4I2G.js.map → setup-llm-JMWSNQ2C.js.map} +1 -1
- package/dist/src/agent/definitions/agent.yaml +9 -5
- package/dist/src/agent/definitions/tasks/cortex-instructions.yaml +115 -0
- package/dist/src/agent/definitions/tasks/cortex-prompt-builder.yaml +67 -0
- package/dist/src/agent/definitions/tasks/digest-only.yaml +1 -1
- package/dist/src/agent/definitions/tasks/extract-only.yaml +1 -1
- package/dist/src/agent/definitions/tasks/review-session.yaml +10 -39
- package/dist/src/agent/definitions/tasks/skill-evolve.yaml +181 -25
- package/dist/src/agent/definitions/tasks/skill-generate.yaml +21 -7
- package/dist/src/agent/definitions/tasks/skill-survey.yaml +2 -6
- package/dist/src/agent/definitions/tasks/supersession-sweep.yaml +1 -1
- package/dist/src/agent/definitions/tasks/title-summary.yaml +12 -19
- package/dist/src/agent/definitions/tasks/{full-intelligence.yaml → vault-evolve.yaml} +74 -129
- package/dist/src/agent/definitions/tasks/vault-seed.yaml +370 -0
- package/dist/src/agent/prompts/agent.md +12 -38
- package/dist/src/cli.js +1 -1
- package/dist/src/daemon/main.js +1 -1
- package/dist/src/hooks/post-tool-use.js +1 -1
- package/dist/src/hooks/session-end.js +1 -1
- package/dist/src/hooks/session-start.js +1 -1
- package/dist/src/hooks/stop.js +1 -1
- package/dist/src/hooks/user-prompt-submit.js +1 -1
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/symbionts/manifests/claude-code.yaml +4 -0
- package/dist/src/symbionts/manifests/opencode.yaml +7 -0
- package/dist/src/symbionts/manifests/pi.yaml +22 -0
- package/dist/src/symbionts/templates/agents-starter.md +1 -1
- package/dist/src/symbionts/templates/pi/package.json +6 -0
- package/dist/src/symbionts/templates/pi/plugin.ts +559 -0
- package/dist/{stats-NVPWOYTE.js → stats-MKMETHMA.js} +11 -9
- package/dist/{stats-NVPWOYTE.js.map → stats-MKMETHMA.js.map} +1 -1
- package/dist/{stop-ZPIKVLH4.js → stop-OUEX6KA4.js} +7 -7
- package/dist/{stop-failure-2PX67YJC.js → stop-failure-2BWVNZEG.js} +8 -8
- package/dist/{subagent-start-UUE6EHQD.js → subagent-start-J4VV6DEE.js} +8 -8
- package/dist/{subagent-stop-KQWWWPE6.js → subagent-stop-JMLVEPIA.js} +8 -8
- package/dist/{task-completed-WMHOFQ7B.js → task-completed-65CHMMKA.js} +8 -8
- package/dist/{team-LRZ6GTQK.js → team-U2LDKIS4.js} +7 -5
- package/dist/{turns-YFNI5CQC.js → turns-HU2CTZAP.js} +2 -2
- package/dist/ui/assets/index-BUGor9dk.js +842 -0
- package/dist/ui/assets/index-_OP4ifzH.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/{update-O6V4RC4W.js → update-ZSHVXWSQ.js} +12 -10
- package/dist/{update-O6V4RC4W.js.map → update-ZSHVXWSQ.js.map} +1 -1
- package/dist/{user-prompt-submit-N36KUPHI.js → user-prompt-submit-APMO6FVU.js} +10 -9
- package/dist/{user-prompt-submit-N36KUPHI.js.map → user-prompt-submit-APMO6FVU.js.map} +1 -1
- package/dist/{verify-LXPV7NYG.js → verify-R76ZFJSZ.js} +8 -5
- package/dist/{verify-LXPV7NYG.js.map → verify-R76ZFJSZ.js.map} +1 -1
- package/dist/{version-XMPPJQHR.js → version-TXPPS3L5.js} +2 -2
- package/dist/version-TXPPS3L5.js.map +1 -0
- package/package.json +3 -1
- package/skills/myco/SKILL.md +16 -1
- package/skills/myco/references/cli-usage.md +1 -1
- package/skills/myco-rules/SKILL.md +94 -0
- package/skills/{rules → myco-rules}/references/rules-bad-example.md +1 -1
- package/skills/{rules → myco-rules}/references/rules-good-example.md +1 -1
- package/dist/chunk-4YFKBL3F.js +0 -195
- package/dist/chunk-4YFKBL3F.js.map +0 -1
- package/dist/chunk-5XIVBO25.js.map +0 -1
- package/dist/chunk-CCRGY3QW.js.map +0 -1
- package/dist/chunk-CUDIZJY7.js +0 -36
- package/dist/chunk-CUDIZJY7.js.map +0 -1
- package/dist/chunk-EVDQKYCG.js.map +0 -1
- package/dist/chunk-FLLBJLHM.js.map +0 -1
- package/dist/chunk-MYOZLMB2.js.map +0 -1
- package/dist/chunk-NGROSFOH.js.map +0 -1
- package/dist/chunk-OD4AA7PV.js.map +0 -1
- package/dist/chunk-Q36VMZST.js.map +0 -1
- package/dist/chunk-US4LNCAT.js.map +0 -1
- package/dist/chunk-UYMFCYBF.js +0 -2326
- package/dist/chunk-UYMFCYBF.js.map +0 -1
- package/dist/chunk-VVNL26WX.js.map +0 -1
- package/dist/cli-GGPWH4UO.js.map +0 -1
- package/dist/doctor-5JXJ36KA.js.map +0 -1
- package/dist/executor-HWW2QNZQ.js +0 -2472
- package/dist/executor-HWW2QNZQ.js.map +0 -1
- package/dist/main-YTBVRTBI.js.map +0 -1
- package/dist/post-tool-use-POGPTJBA.js.map +0 -1
- package/dist/server-AGVYZVP5.js.map +0 -1
- package/dist/session-start-LAFICHII.js +0 -189
- package/dist/session-start-LAFICHII.js.map +0 -1
- package/dist/src/agent/definitions/tasks/graph-maintenance.yaml +0 -93
- package/dist/ui/assets/index-C2JuNtRB.css +0 -1
- package/dist/ui/assets/index-JLVaQKV2.js +0 -832
- package/skills/myco-curate/SKILL.md +0 -86
- package/skills/rules/SKILL.md +0 -214
- /package/dist/{chunk-XG5RRUYF.js.map → chunk-ENZR5NG7.js.map} +0 -0
- /package/dist/{chunk-DCSGJ7W4.js.map → chunk-N2DGFACQ.js.map} +0 -0
- /package/dist/{chunk-KESLPBKV.js.map → chunk-QDLVIW2O.js.map} +0 -0
- /package/dist/{chunk-BPRIYNLE.js.map → chunk-TKAJ3JVF.js.map} +0 -0
- /package/dist/{chunk-JZGN33AY.js.map → chunk-VRI56337.js.map} +0 -0
- /package/dist/{client-YXQUTXVZ.js.map → client-Z43DNLJH.js.map} +0 -0
- /package/dist/{detect-PXNM6TA7.js.map → detect-7NUD5B5R.js.map} +0 -0
- /package/dist/{detect-providers-5KOPZ7J2.js.map → detect-providers-ILLQZROY.js.map} +0 -0
- /package/dist/{installer-FS257JRZ.js.map → executor-DO6QFC6G.js.map} +0 -0
- /package/dist/{llm-TH4NLIRM.js.map → installer-N4UTEACX.js.map} +0 -0
- /package/dist/{loader-CQYTFHEW.js.map → llm-AGVEF5XD.js.map} +0 -0
- /package/dist/{loader-NOMBJUPW.js.map → loader-LX7TFRM6.js.map} +0 -0
- /package/dist/{provider-check-43LAMSMH.js.map → loader-UDNUMEDA.js.map} +0 -0
- /package/dist/{post-compact-JSECI44W.js.map → post-compact-7AEFVCZS.js.map} +0 -0
- /package/dist/{post-tool-use-failure-OT7BFWQW.js.map → post-tool-use-failure-TCFEU2GI.js.map} +0 -0
- /package/dist/{pre-compact-OXVODKH4.js.map → pre-compact-LO2VZCGR.js.map} +0 -0
- /package/dist/{registry-U4CHXK6R.js.map → provider-check-ZEV5P4KM.js.map} +0 -0
- /package/dist/{team-LRZ6GTQK.js.map → registry-F3THYC5M.js.map} +0 -0
- /package/dist/{session-end-FT27DWYZ.js.map → session-end-FS46UARX.js.map} +0 -0
- /package/dist/{stop-ZPIKVLH4.js.map → stop-OUEX6KA4.js.map} +0 -0
- /package/dist/{stop-failure-2PX67YJC.js.map → stop-failure-2BWVNZEG.js.map} +0 -0
- /package/dist/{subagent-start-UUE6EHQD.js.map → subagent-start-J4VV6DEE.js.map} +0 -0
- /package/dist/{subagent-stop-KQWWWPE6.js.map → subagent-stop-JMLVEPIA.js.map} +0 -0
- /package/dist/{task-completed-WMHOFQ7B.js.map → task-completed-65CHMMKA.js.map} +0 -0
- /package/dist/{turns-YFNI5CQC.js.map → team-U2LDKIS4.js.map} +0 -0
- /package/dist/{version-XMPPJQHR.js.map → turns-HU2CTZAP.js.map} +0 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
enumerateMatrixCells
|
|
4
|
+
} from "./chunk-54SXG5HF.js";
|
|
2
5
|
import {
|
|
3
6
|
DaemonLogger,
|
|
4
7
|
LEVEL_ORDER
|
|
5
8
|
} from "./chunk-3WOS4TAR.js";
|
|
6
9
|
import {
|
|
7
10
|
withTaskConfig
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IPPMYQ2Y.js";
|
|
9
12
|
import {
|
|
10
13
|
EMBEDDABLE_TABLES,
|
|
11
14
|
EMBEDDABLE_TEXT_COLUMNS,
|
|
@@ -15,16 +18,19 @@ import {
|
|
|
15
18
|
getEmbeddingQueueDepth,
|
|
16
19
|
getUnembedded,
|
|
17
20
|
markEmbedded
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-VRI56337.js";
|
|
19
22
|
import {
|
|
23
|
+
deleteSecrets,
|
|
20
24
|
getTeamPackageVersion,
|
|
21
25
|
loadSecrets,
|
|
22
26
|
readJsonConfig,
|
|
23
27
|
readSecrets,
|
|
24
28
|
resolveVaultConfigPath,
|
|
25
29
|
writeSecret
|
|
26
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-6ALVMIB4.js";
|
|
27
31
|
import {
|
|
32
|
+
DEFAULT_OPENAI_URL,
|
|
33
|
+
DEFAULT_OPENROUTER_URL,
|
|
28
34
|
SKILL_SURVEY_TASK,
|
|
29
35
|
buildTaskInstruction,
|
|
30
36
|
closeOpenBatches,
|
|
@@ -33,19 +39,24 @@ import {
|
|
|
33
39
|
countNotifications,
|
|
34
40
|
countRuns,
|
|
35
41
|
countSkillRecords,
|
|
42
|
+
countWriteIntents,
|
|
43
|
+
countWriteIntentsByTool,
|
|
44
|
+
countWriteIntentsByToolForEvaluation,
|
|
36
45
|
createBatchLineage,
|
|
46
|
+
createLocalOpenAIBackend,
|
|
37
47
|
deleteCandidate,
|
|
38
48
|
deleteSkillRecordCascade,
|
|
39
49
|
dismissAllNotifications,
|
|
40
|
-
errorMessage,
|
|
41
50
|
findBatchByPromptPrefix,
|
|
42
51
|
getAllDomains,
|
|
43
52
|
getCandidate,
|
|
44
|
-
getDigestExtract,
|
|
45
|
-
getEntity,
|
|
46
53
|
getGraphForNode,
|
|
47
54
|
getLatestBatch,
|
|
55
|
+
getLatestOpenBatch,
|
|
56
|
+
getLatestResumableRunForTask,
|
|
48
57
|
getLatestRunId,
|
|
58
|
+
getLocalOpenAIBackendDefaultBaseUrl,
|
|
59
|
+
getLocalOpenAIBackendLabel,
|
|
49
60
|
getNotification,
|
|
50
61
|
getRun,
|
|
51
62
|
getSkillRecord,
|
|
@@ -54,47 +65,59 @@ import {
|
|
|
54
65
|
hasConfiguredProvider,
|
|
55
66
|
incrementActivityCount,
|
|
56
67
|
incrementSkillUsageCount,
|
|
68
|
+
inferLocalOpenAIBackendKind,
|
|
57
69
|
insertBatchStateless,
|
|
58
70
|
insertResolutionEvent,
|
|
59
71
|
isInstructionRequiredTask,
|
|
60
72
|
listBatchesBySession,
|
|
61
73
|
listCandidatesWithCount,
|
|
62
|
-
listDigestExtracts,
|
|
63
|
-
listEntities,
|
|
64
74
|
listLineageForSkill,
|
|
65
75
|
listNotifications,
|
|
66
76
|
listReports,
|
|
67
77
|
listRuns,
|
|
78
|
+
listRunsForEvaluation,
|
|
68
79
|
listSkillRecords,
|
|
69
80
|
listSkillRecordsWithCount,
|
|
81
|
+
listWriteIntentTools,
|
|
82
|
+
listWriteIntents,
|
|
70
83
|
markAllRead,
|
|
84
|
+
markRunningRunsInterrupted,
|
|
71
85
|
notify,
|
|
72
86
|
populateBatchResponses,
|
|
73
87
|
register,
|
|
88
|
+
runAgent,
|
|
74
89
|
setResponseSummary,
|
|
90
|
+
tryParseJson,
|
|
75
91
|
updateCandidate,
|
|
76
92
|
updateNotificationStatus
|
|
77
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-X2IRGXGF.js";
|
|
94
|
+
import {
|
|
95
|
+
parseCheckpointState,
|
|
96
|
+
runDurationMs
|
|
97
|
+
} from "./chunk-QATYARI5.js";
|
|
78
98
|
import {
|
|
79
99
|
fullTextSearch,
|
|
80
|
-
hydrateSearchResults
|
|
81
|
-
|
|
100
|
+
hydrateSearchResults,
|
|
101
|
+
sanitizeFtsQuery
|
|
102
|
+
} from "./chunk-QLLBJEM7.js";
|
|
82
103
|
import {
|
|
83
104
|
copyTaskToUser,
|
|
84
105
|
deleteUserTask,
|
|
85
106
|
loadAllTasks,
|
|
86
107
|
validateTaskName,
|
|
87
108
|
writeUserTask
|
|
88
|
-
} from "./chunk-
|
|
109
|
+
} from "./chunk-USVFEWYL.js";
|
|
89
110
|
import {
|
|
90
|
-
AgentTaskSchema,
|
|
91
111
|
registerAgent,
|
|
92
112
|
resolveDefinitionsDir,
|
|
93
113
|
taskFromParsed
|
|
94
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-AUIXX33A.js";
|
|
115
|
+
import {
|
|
116
|
+
errorMessage
|
|
117
|
+
} from "./chunk-LQIPXVDH.js";
|
|
95
118
|
import {
|
|
96
119
|
listTurnsByRun
|
|
97
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-FCJ5JV54.js";
|
|
98
121
|
import {
|
|
99
122
|
cleanupStagedSkill,
|
|
100
123
|
listStaleStagingDirs
|
|
@@ -102,7 +125,11 @@ import {
|
|
|
102
125
|
import {
|
|
103
126
|
Anthropic,
|
|
104
127
|
createEmbeddingProvider
|
|
105
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-YDUOSRGD.js";
|
|
129
|
+
import {
|
|
130
|
+
OPENAI_API_KEY_ENV,
|
|
131
|
+
OPENROUTER_API_KEY_ENV
|
|
132
|
+
} from "./chunk-CISWUP5W.js";
|
|
106
133
|
import {
|
|
107
134
|
getMachineId
|
|
108
135
|
} from "./chunk-ENWBFX7F.js";
|
|
@@ -111,26 +138,42 @@ import {
|
|
|
111
138
|
cleanStaleBuffers,
|
|
112
139
|
listBufferSessionIds
|
|
113
140
|
} from "./chunk-V7XG6V6C.js";
|
|
114
|
-
import "./chunk-
|
|
115
|
-
import "./chunk-KESLPBKV.js";
|
|
141
|
+
import "./chunk-QDLVIW2O.js";
|
|
116
142
|
import "./chunk-SAKJMNSR.js";
|
|
117
143
|
import {
|
|
118
144
|
SymbiontInstaller
|
|
119
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-OTQH5KZW.js";
|
|
120
146
|
import {
|
|
121
147
|
checkLocalProvider
|
|
122
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-TKAJ3JVF.js";
|
|
123
149
|
import {
|
|
124
150
|
LmStudioBackend,
|
|
125
151
|
OllamaBackend
|
|
126
|
-
} from "./chunk-
|
|
152
|
+
} from "./chunk-X3IGT5RV.js";
|
|
127
153
|
import {
|
|
154
|
+
composeSessionStartContext,
|
|
155
|
+
shouldInjectSessionStartDigest
|
|
156
|
+
} from "./chunk-OZ3FBAK5.js";
|
|
157
|
+
import {
|
|
158
|
+
buildCortexInstructionsInput,
|
|
128
159
|
countSpores,
|
|
160
|
+
deletePlan,
|
|
161
|
+
getCortexInstructions,
|
|
162
|
+
getPlan,
|
|
163
|
+
getPlanByLogicalKey,
|
|
129
164
|
getSpore,
|
|
130
165
|
insertSpore,
|
|
166
|
+
listDigestExtracts,
|
|
167
|
+
listDigestRevisions,
|
|
168
|
+
listPlans,
|
|
169
|
+
listPlansBySession,
|
|
131
170
|
listSpores,
|
|
132
|
-
|
|
133
|
-
|
|
171
|
+
resolveInstructionDelivery,
|
|
172
|
+
rollbackDigestExtract,
|
|
173
|
+
shouldInjectCortex,
|
|
174
|
+
updateSporeStatus,
|
|
175
|
+
upsertPlan
|
|
176
|
+
} from "./chunk-N7Z3LUEZ.js";
|
|
134
177
|
import {
|
|
135
178
|
backfillUnsynced,
|
|
136
179
|
closeSession,
|
|
@@ -153,20 +196,29 @@ import {
|
|
|
153
196
|
pruneOld,
|
|
154
197
|
reactivateSessionIfCompleted,
|
|
155
198
|
retryDeadLettered,
|
|
156
|
-
syncRow,
|
|
157
199
|
updateSession,
|
|
158
200
|
upsertSession
|
|
159
|
-
} from "./chunk-
|
|
201
|
+
} from "./chunk-RQSJLWP4.js";
|
|
160
202
|
import {
|
|
161
203
|
evaluateSessionCaptureRules,
|
|
162
204
|
readTranscriptMeta
|
|
163
205
|
} from "./chunk-XL75KZGI.js";
|
|
206
|
+
import {
|
|
207
|
+
PLAN_STATUSES
|
|
208
|
+
} from "./chunk-EEOJWLMP.js";
|
|
164
209
|
import {
|
|
165
210
|
EMBEDDING_DIMENSIONS,
|
|
166
211
|
REST_SETTABLE_STATUSES,
|
|
167
212
|
SCHEMA_VERSION,
|
|
168
|
-
|
|
169
|
-
|
|
213
|
+
TRANSCRIPT_SOURCE_PREFIX,
|
|
214
|
+
buildPathPlanLogicalKey,
|
|
215
|
+
buildPlanId,
|
|
216
|
+
buildSessionPlanLogicalKey,
|
|
217
|
+
buildSessionTagPlanLogicalKey,
|
|
218
|
+
createSchema,
|
|
219
|
+
humanizePlanToken,
|
|
220
|
+
normalizePlanSourcePath
|
|
221
|
+
} from "./chunk-DTWUHHFI.js";
|
|
170
222
|
import {
|
|
171
223
|
CONFIG_FILENAME,
|
|
172
224
|
MycoConfigSchema,
|
|
@@ -179,7 +231,11 @@ import {
|
|
|
179
231
|
updateConfig,
|
|
180
232
|
updateLocalConfig,
|
|
181
233
|
updateTeamConfig
|
|
182
|
-
} from "./chunk-
|
|
234
|
+
} from "./chunk-53RPGOEN.js";
|
|
235
|
+
import {
|
|
236
|
+
AgentTaskSchema
|
|
237
|
+
} from "./chunk-OUJSQSKE.js";
|
|
238
|
+
import "./chunk-POEPHBQK.js";
|
|
183
239
|
import {
|
|
184
240
|
closeDatabase,
|
|
185
241
|
getDatabase,
|
|
@@ -191,14 +247,14 @@ import {
|
|
|
191
247
|
} from "./chunk-ZXZPJJN3.js";
|
|
192
248
|
import {
|
|
193
249
|
resolveCliEntryPath
|
|
194
|
-
} from "./chunk-
|
|
250
|
+
} from "./chunk-KTTSXYEK.js";
|
|
195
251
|
import {
|
|
196
252
|
getPluginVersion
|
|
197
|
-
} from "./chunk-
|
|
253
|
+
} from "./chunk-ENZR5NG7.js";
|
|
198
254
|
import {
|
|
199
255
|
loadManifests,
|
|
200
256
|
resolvePackageRoot
|
|
201
|
-
} from "./chunk-
|
|
257
|
+
} from "./chunk-Z66IT5KL.js";
|
|
202
258
|
import {
|
|
203
259
|
findPackageRoot
|
|
204
260
|
} from "./chunk-LPUQPDC2.js";
|
|
@@ -210,6 +266,7 @@ import {
|
|
|
210
266
|
DEFAULT_AGENT_ID,
|
|
211
267
|
DEFAULT_LIST_LIMIT,
|
|
212
268
|
DEFAULT_RELEASE_CHANNEL,
|
|
269
|
+
DEFAULT_SYMBIONT_NAME,
|
|
213
270
|
EMBEDDING_BATCH_SIZE,
|
|
214
271
|
EXCLUDED_SPORE_STATUSES,
|
|
215
272
|
FEED_DEFAULT_LIMIT,
|
|
@@ -243,8 +300,10 @@ import {
|
|
|
243
300
|
SYNC_PROTOCOL_VERSION,
|
|
244
301
|
TEAM_API_KEY_SECRET,
|
|
245
302
|
TEAM_HEALTH_TIMEOUT_MS,
|
|
303
|
+
TEAM_REQUEST_TIMEOUT_MS,
|
|
246
304
|
TEAM_SEARCH_TIMEOUT_MS,
|
|
247
305
|
TEAM_SOURCE_PREFIX,
|
|
306
|
+
TEAM_SYNC_TIMEOUT_MS,
|
|
248
307
|
UPDATE_CHECK_CACHE_PATH,
|
|
249
308
|
UPDATE_CHECK_INTERVAL_HOURS,
|
|
250
309
|
UPDATE_CONFIG_PATH,
|
|
@@ -257,7 +316,7 @@ import {
|
|
|
257
316
|
USER_TASK_SOURCE,
|
|
258
317
|
epochSeconds,
|
|
259
318
|
estimateTokens
|
|
260
|
-
} from "./chunk-
|
|
319
|
+
} from "./chunk-6C6QZ4PM.js";
|
|
261
320
|
import {
|
|
262
321
|
LOG_KINDS,
|
|
263
322
|
kindToComponent
|
|
@@ -428,13 +487,24 @@ var DaemonServer = class {
|
|
|
428
487
|
uptime: process.uptime()
|
|
429
488
|
}
|
|
430
489
|
}));
|
|
490
|
+
this.registerRoute("GET", "/api/version", async () => ({
|
|
491
|
+
body: { version: this.version }
|
|
492
|
+
}));
|
|
431
493
|
}
|
|
432
494
|
async handleRequest(req, res) {
|
|
433
495
|
const match = this.router.match(req.method, req.url);
|
|
434
496
|
if (match) {
|
|
497
|
+
const rejection = validateLoopbackRequest(req, this.port);
|
|
498
|
+
if (rejection) {
|
|
499
|
+
res.writeHead(rejection.status, { "Content-Type": "application/json" });
|
|
500
|
+
res.end(JSON.stringify({ error: rejection.error }));
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
435
503
|
this.onRequest?.();
|
|
504
|
+
const versionHeader = { "X-Myco-Api-Version": this.version };
|
|
436
505
|
try {
|
|
437
|
-
const
|
|
506
|
+
const needsBody = req.method === "POST" || req.method === "PUT" || req.method === "PATCH" || req.method === "DELETE";
|
|
507
|
+
const body = needsBody ? await readBody(req) : void 0;
|
|
438
508
|
const result = await match.handler({
|
|
439
509
|
body,
|
|
440
510
|
query: match.query,
|
|
@@ -443,11 +513,11 @@ var DaemonServer = class {
|
|
|
443
513
|
});
|
|
444
514
|
const status = result.status ?? DEFAULT_STATUS;
|
|
445
515
|
if (Buffer.isBuffer(result.body)) {
|
|
446
|
-
res.writeHead(status, result.headers
|
|
516
|
+
res.writeHead(status, { ...versionHeader, ...result.headers });
|
|
447
517
|
res.end(result.body);
|
|
448
518
|
return;
|
|
449
519
|
}
|
|
450
|
-
const headers = { "Content-Type": "application/json", ...result.headers };
|
|
520
|
+
const headers = { "Content-Type": "application/json", ...versionHeader, ...result.headers };
|
|
451
521
|
res.writeHead(status, headers);
|
|
452
522
|
res.end(JSON.stringify(result.body));
|
|
453
523
|
} catch (error) {
|
|
@@ -455,7 +525,7 @@ var DaemonServer = class {
|
|
|
455
525
|
path: req.url,
|
|
456
526
|
error: error.message
|
|
457
527
|
});
|
|
458
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
528
|
+
res.writeHead(500, { "Content-Type": "application/json", ...versionHeader });
|
|
459
529
|
res.end(JSON.stringify({ error: error.message }));
|
|
460
530
|
}
|
|
461
531
|
return;
|
|
@@ -578,6 +648,36 @@ function readBody(req) {
|
|
|
578
648
|
req.on("error", reject);
|
|
579
649
|
});
|
|
580
650
|
}
|
|
651
|
+
function validateLoopbackRequest(req, port) {
|
|
652
|
+
const host = req.headers.host;
|
|
653
|
+
if (host && !isLoopbackHost(host, port)) {
|
|
654
|
+
return { status: 403, error: "forbidden_host" };
|
|
655
|
+
}
|
|
656
|
+
const origin = req.headers.origin;
|
|
657
|
+
if (origin && !isLoopbackOrigin(origin, port)) {
|
|
658
|
+
return { status: 403, error: "forbidden_origin" };
|
|
659
|
+
}
|
|
660
|
+
const method = (req.method ?? "").toUpperCase();
|
|
661
|
+
const isMutating = method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
|
|
662
|
+
if (!isMutating) return null;
|
|
663
|
+
const contentLengthHeader = req.headers["content-length"];
|
|
664
|
+
const contentLength = contentLengthHeader ? Number(contentLengthHeader) : NaN;
|
|
665
|
+
const hasBody = Number.isFinite(contentLength) ? contentLength > 0 : (req.headers["transfer-encoding"] ?? "").length > 0;
|
|
666
|
+
if (!hasBody) return null;
|
|
667
|
+
const contentType = (req.headers["content-type"] ?? "").toLowerCase();
|
|
668
|
+
if (!contentType.includes("application/json")) {
|
|
669
|
+
return { status: 415, error: "unsupported_media_type" };
|
|
670
|
+
}
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
function isLoopbackHost(host, port) {
|
|
674
|
+
const portStr = String(port);
|
|
675
|
+
return host === `127.0.0.1:${portStr}` || host === `localhost:${portStr}`;
|
|
676
|
+
}
|
|
677
|
+
function isLoopbackOrigin(origin, port) {
|
|
678
|
+
const portStr = String(port);
|
|
679
|
+
return origin === `http://127.0.0.1:${portStr}` || origin === `http://localhost:${portStr}`;
|
|
680
|
+
}
|
|
581
681
|
|
|
582
682
|
// src/daemon/lifecycle.ts
|
|
583
683
|
var SessionRegistry = class {
|
|
@@ -2599,7 +2699,7 @@ var TeamSyncClient = class _TeamSyncClient {
|
|
|
2599
2699
|
content_hash: data.content_hash ?? null
|
|
2600
2700
|
};
|
|
2601
2701
|
})
|
|
2602
|
-
});
|
|
2702
|
+
}, { timeoutMs: TEAM_SYNC_TIMEOUT_MS });
|
|
2603
2703
|
return res;
|
|
2604
2704
|
}
|
|
2605
2705
|
/**
|
|
@@ -2699,17 +2799,25 @@ var TeamSyncClient = class _TeamSyncClient {
|
|
|
2699
2799
|
"Content-Type": "application/json"
|
|
2700
2800
|
};
|
|
2701
2801
|
}
|
|
2702
|
-
async request(method,
|
|
2703
|
-
const
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2802
|
+
async request(method, path25, body, options = {}) {
|
|
2803
|
+
const timeoutMs = options.timeoutMs ?? TEAM_REQUEST_TIMEOUT_MS;
|
|
2804
|
+
const controller = new AbortController();
|
|
2805
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2806
|
+
try {
|
|
2807
|
+
const res = await this.fetchFn(`${this.workerUrl}${path25}`, {
|
|
2808
|
+
method,
|
|
2809
|
+
headers: this.headers(),
|
|
2810
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
2811
|
+
signal: controller.signal
|
|
2812
|
+
});
|
|
2813
|
+
if (!res.ok) {
|
|
2814
|
+
const text = await res.text().catch(() => "");
|
|
2815
|
+
throw new Error(`Team sync request ${method} ${path25} failed: ${res.status} ${text}`);
|
|
2816
|
+
}
|
|
2817
|
+
return res.json();
|
|
2818
|
+
} finally {
|
|
2819
|
+
clearTimeout(timer);
|
|
2711
2820
|
}
|
|
2712
|
-
return res.json();
|
|
2713
2821
|
}
|
|
2714
2822
|
};
|
|
2715
2823
|
|
|
@@ -2791,13 +2899,17 @@ function createTeamHandlers(deps) {
|
|
|
2791
2899
|
try {
|
|
2792
2900
|
pendingCount = countPending();
|
|
2793
2901
|
deadLetterCount = countDeadLettered();
|
|
2794
|
-
} catch {
|
|
2902
|
+
} catch (err) {
|
|
2903
|
+
const detail = errorMessage(err);
|
|
2904
|
+
logger.warn("team-sync.outbox.count-failed", "team-outbox counts unavailable", { error: detail });
|
|
2795
2905
|
}
|
|
2796
2906
|
let collectiveStatus = null;
|
|
2797
2907
|
if (client && config.team.enabled) {
|
|
2798
2908
|
try {
|
|
2799
2909
|
collectiveStatus = await client.getCollectiveStatus();
|
|
2800
|
-
} catch {
|
|
2910
|
+
} catch (err) {
|
|
2911
|
+
const detail = errorMessage(err);
|
|
2912
|
+
logger.warn("team-sync.collective.status-failed", "Collective status unavailable", { error: detail });
|
|
2801
2913
|
collectiveStatus = null;
|
|
2802
2914
|
}
|
|
2803
2915
|
}
|
|
@@ -2840,7 +2952,7 @@ function createTeamHandlers(deps) {
|
|
|
2840
2952
|
return { body: { retried: count } };
|
|
2841
2953
|
}
|
|
2842
2954
|
async function handleUpgradeWorker(_req) {
|
|
2843
|
-
const { upgradeWorker } = await import("./team-
|
|
2955
|
+
const { upgradeWorker } = await import("./team-U2LDKIS4.js");
|
|
2844
2956
|
logger.info("team-sync.upgrade.start", "Starting worker upgrade");
|
|
2845
2957
|
const result = upgradeWorker(vaultDir);
|
|
2846
2958
|
if (!result.success) {
|
|
@@ -2878,7 +2990,7 @@ function createTeamHandlers(deps) {
|
|
|
2878
2990
|
logger.info("team-sync.mcp-token.rotated", "MCP access token rotated");
|
|
2879
2991
|
return { body: { token } };
|
|
2880
2992
|
} catch (err) {
|
|
2881
|
-
const message =
|
|
2993
|
+
const message = errorMessage(err);
|
|
2882
2994
|
logger.error("team-sync.mcp-token.rotate-failed", "MCP token rotation failed", { error: message });
|
|
2883
2995
|
return {
|
|
2884
2996
|
status: 500,
|
|
@@ -3232,7 +3344,7 @@ function createSkillRecordDeleteHandler(deps) {
|
|
|
3232
3344
|
logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill directory", { name: record.name, error: String(err) });
|
|
3233
3345
|
}
|
|
3234
3346
|
try {
|
|
3235
|
-
const { syncSkillSymlinks } = await import("./installer-
|
|
3347
|
+
const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
|
|
3236
3348
|
syncSkillSymlinks(projectRoot, record.name, { remove: true });
|
|
3237
3349
|
} catch (err) {
|
|
3238
3350
|
logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill symlinks", { name: record.name, error: String(err) });
|
|
@@ -3451,6 +3563,10 @@ async function handleGetProgress(tracker, token) {
|
|
|
3451
3563
|
|
|
3452
3564
|
// src/daemon/api/models.ts
|
|
3453
3565
|
var MODEL_LIST_TIMEOUT_MS = 5e3;
|
|
3566
|
+
var REMOTE_MODELS_ENDPOINT = "/models";
|
|
3567
|
+
var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
3568
|
+
var OPENROUTER_DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
3569
|
+
var REMOTE_PROVIDER_TIMEOUT_MS = 5e3;
|
|
3454
3570
|
var ANTHROPIC_MODELS = [
|
|
3455
3571
|
"claude-sonnet-4-6",
|
|
3456
3572
|
"claude-opus-4-6",
|
|
@@ -3476,31 +3592,78 @@ function filterLlmModels(models) {
|
|
|
3476
3592
|
return !EMBEDDING_PATTERNS.some((p) => name.includes(p));
|
|
3477
3593
|
});
|
|
3478
3594
|
}
|
|
3479
|
-
|
|
3595
|
+
var REMOTE_PROVIDER_DEFAULTS = {
|
|
3596
|
+
openai: OPENAI_DEFAULT_BASE_URL,
|
|
3597
|
+
openrouter: OPENROUTER_DEFAULT_BASE_URL
|
|
3598
|
+
};
|
|
3599
|
+
var REMOTE_PROVIDER_ENV_VARS = {
|
|
3600
|
+
openai: OPENAI_API_KEY_ENV,
|
|
3601
|
+
openrouter: OPENROUTER_API_KEY_ENV
|
|
3602
|
+
};
|
|
3603
|
+
function getRemoteProviderApiKey(provider) {
|
|
3604
|
+
const preferredKey = process.env[REMOTE_PROVIDER_ENV_VARS[provider]];
|
|
3605
|
+
if (preferredKey) return preferredKey;
|
|
3606
|
+
if (provider === "openai") {
|
|
3607
|
+
return process.env.OPENAI_API_KEY;
|
|
3608
|
+
}
|
|
3609
|
+
return void 0;
|
|
3610
|
+
}
|
|
3611
|
+
async function fetchRemoteProviderModels(provider, _baseUrl, timeoutMs = REMOTE_PROVIDER_TIMEOUT_MS) {
|
|
3612
|
+
const apiKey = getRemoteProviderApiKey(provider);
|
|
3613
|
+
if (!apiKey) return [];
|
|
3614
|
+
const baseUrl = REMOTE_PROVIDER_DEFAULTS[provider];
|
|
3615
|
+
const response = await fetch(`${baseUrl}${REMOTE_MODELS_ENDPOINT}`, {
|
|
3616
|
+
headers: {
|
|
3617
|
+
Authorization: `Bearer ${apiKey}`
|
|
3618
|
+
},
|
|
3619
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
3620
|
+
});
|
|
3621
|
+
if (!response.ok) {
|
|
3622
|
+
throw new Error(`${provider} models request failed with ${response.status}`);
|
|
3623
|
+
}
|
|
3624
|
+
const data = await response.json();
|
|
3625
|
+
const modelIds = (data.data ?? []).map((entry) => entry.id).filter((id) => typeof id === "string" && id.length > 0);
|
|
3626
|
+
return filterLlmModels(modelIds);
|
|
3627
|
+
}
|
|
3628
|
+
async function handleGetModels(req, logger) {
|
|
3480
3629
|
const provider = req.query.provider;
|
|
3481
3630
|
const type = req.query.type;
|
|
3631
|
+
const localBackend = req.query.local_backend;
|
|
3482
3632
|
if (!provider) {
|
|
3483
3633
|
return { status: 400, body: { error: "provider query parameter required" } };
|
|
3484
3634
|
}
|
|
3485
3635
|
let models = [];
|
|
3636
|
+
let fetchError;
|
|
3486
3637
|
try {
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3638
|
+
const localBackendKind = inferLocalOpenAIBackendKind({
|
|
3639
|
+
type: provider === "lm-studio" ? "lmstudio" : provider,
|
|
3640
|
+
localBackend,
|
|
3641
|
+
baseUrl: req.query.base_url
|
|
3642
|
+
});
|
|
3643
|
+
if (localBackendKind) {
|
|
3644
|
+
const backend = createLocalOpenAIBackend(localBackendKind, req.query.base_url);
|
|
3492
3645
|
models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
|
|
3493
3646
|
} else if (provider === "anthropic") {
|
|
3494
3647
|
models = ANTHROPIC_MODELS;
|
|
3648
|
+
} else if (provider === "openai" || provider === "openrouter") {
|
|
3649
|
+
models = await fetchRemoteProviderModels(provider, void 0, MODEL_LIST_TIMEOUT_MS);
|
|
3495
3650
|
}
|
|
3496
|
-
} catch {
|
|
3651
|
+
} catch (err) {
|
|
3652
|
+
fetchError = errorMessage(err);
|
|
3653
|
+
logger?.warn(`models.${provider}.list-unavailable`, `${provider} model list unavailable`, { error: fetchError });
|
|
3497
3654
|
}
|
|
3498
3655
|
if (type === "embedding") {
|
|
3499
3656
|
models = filterEmbeddingModels(models);
|
|
3500
3657
|
} else if (type === "llm") {
|
|
3501
3658
|
models = filterLlmModels(models);
|
|
3502
3659
|
}
|
|
3503
|
-
return {
|
|
3660
|
+
return {
|
|
3661
|
+
body: {
|
|
3662
|
+
provider,
|
|
3663
|
+
models,
|
|
3664
|
+
...fetchError ? { error: fetchError } : {}
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3504
3667
|
}
|
|
3505
3668
|
|
|
3506
3669
|
// src/daemon/api/stats.ts
|
|
@@ -3709,138 +3872,6 @@ function getAttachmentByFilePath(filePath) {
|
|
|
3709
3872
|
return row ? toAttachmentRow(row) : null;
|
|
3710
3873
|
}
|
|
3711
3874
|
|
|
3712
|
-
// src/db/queries/plans.ts
|
|
3713
|
-
var DEFAULT_LIST_LIMIT2 = 100;
|
|
3714
|
-
var DEFAULT_STATUS2 = "active";
|
|
3715
|
-
var DEFAULT_PROCESSED2 = 0;
|
|
3716
|
-
var PLAN_COLUMNS = [
|
|
3717
|
-
"id",
|
|
3718
|
-
"status",
|
|
3719
|
-
"author",
|
|
3720
|
-
"title",
|
|
3721
|
-
"content",
|
|
3722
|
-
"source_path",
|
|
3723
|
-
"tags",
|
|
3724
|
-
"session_id",
|
|
3725
|
-
"prompt_batch_id",
|
|
3726
|
-
"content_hash",
|
|
3727
|
-
"processed",
|
|
3728
|
-
"embedded",
|
|
3729
|
-
"created_at",
|
|
3730
|
-
"updated_at",
|
|
3731
|
-
"machine_id",
|
|
3732
|
-
"synced_at"
|
|
3733
|
-
];
|
|
3734
|
-
var SELECT_COLUMNS4 = PLAN_COLUMNS.join(", ");
|
|
3735
|
-
function toPlanRow(row) {
|
|
3736
|
-
return {
|
|
3737
|
-
id: row.id,
|
|
3738
|
-
status: row.status,
|
|
3739
|
-
author: row.author ?? null,
|
|
3740
|
-
title: row.title ?? null,
|
|
3741
|
-
content: row.content ?? null,
|
|
3742
|
-
source_path: row.source_path ?? null,
|
|
3743
|
-
tags: row.tags ?? null,
|
|
3744
|
-
session_id: row.session_id ?? null,
|
|
3745
|
-
prompt_batch_id: row.prompt_batch_id ?? null,
|
|
3746
|
-
content_hash: row.content_hash ?? null,
|
|
3747
|
-
processed: row.processed,
|
|
3748
|
-
embedded: row.embedded ?? 0,
|
|
3749
|
-
created_at: row.created_at,
|
|
3750
|
-
updated_at: row.updated_at ?? null,
|
|
3751
|
-
machine_id: row.machine_id ?? "local",
|
|
3752
|
-
synced_at: row.synced_at ?? null
|
|
3753
|
-
};
|
|
3754
|
-
}
|
|
3755
|
-
function upsertPlan(data) {
|
|
3756
|
-
const db = getDatabase();
|
|
3757
|
-
db.prepare(
|
|
3758
|
-
`INSERT INTO plans (
|
|
3759
|
-
id, status, author, title, content,
|
|
3760
|
-
source_path, tags, session_id, prompt_batch_id, content_hash,
|
|
3761
|
-
processed, created_at, updated_at, machine_id
|
|
3762
|
-
) VALUES (
|
|
3763
|
-
?, ?, ?, ?, ?,
|
|
3764
|
-
?, ?, ?, ?, ?,
|
|
3765
|
-
?, ?, ?, ?
|
|
3766
|
-
)
|
|
3767
|
-
ON CONFLICT (id) DO UPDATE SET
|
|
3768
|
-
status = EXCLUDED.status,
|
|
3769
|
-
author = EXCLUDED.author,
|
|
3770
|
-
title = EXCLUDED.title,
|
|
3771
|
-
content = EXCLUDED.content,
|
|
3772
|
-
source_path = EXCLUDED.source_path,
|
|
3773
|
-
tags = EXCLUDED.tags,
|
|
3774
|
-
session_id = EXCLUDED.session_id,
|
|
3775
|
-
prompt_batch_id = EXCLUDED.prompt_batch_id,
|
|
3776
|
-
content_hash = EXCLUDED.content_hash,
|
|
3777
|
-
processed = EXCLUDED.processed,
|
|
3778
|
-
updated_at = EXCLUDED.updated_at,
|
|
3779
|
-
embedded = CASE
|
|
3780
|
-
WHEN EXCLUDED.content_hash != plans.content_hash THEN 0
|
|
3781
|
-
ELSE plans.embedded
|
|
3782
|
-
END`
|
|
3783
|
-
).run(
|
|
3784
|
-
data.id,
|
|
3785
|
-
data.status ?? DEFAULT_STATUS2,
|
|
3786
|
-
data.author ?? null,
|
|
3787
|
-
data.title ?? null,
|
|
3788
|
-
data.content ?? null,
|
|
3789
|
-
data.source_path ?? null,
|
|
3790
|
-
data.tags ?? null,
|
|
3791
|
-
data.session_id ?? null,
|
|
3792
|
-
data.prompt_batch_id ?? null,
|
|
3793
|
-
data.content_hash ?? null,
|
|
3794
|
-
data.processed ?? DEFAULT_PROCESSED2,
|
|
3795
|
-
data.created_at,
|
|
3796
|
-
data.updated_at ?? null,
|
|
3797
|
-
data.machine_id ?? getTeamMachineId()
|
|
3798
|
-
);
|
|
3799
|
-
const row = toPlanRow(
|
|
3800
|
-
db.prepare(`SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`).get(data.id)
|
|
3801
|
-
);
|
|
3802
|
-
syncRow("plans", row);
|
|
3803
|
-
return row;
|
|
3804
|
-
}
|
|
3805
|
-
function getPlan(id) {
|
|
3806
|
-
const db = getDatabase();
|
|
3807
|
-
const row = db.prepare(
|
|
3808
|
-
`SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`
|
|
3809
|
-
).get(id);
|
|
3810
|
-
if (!row) return null;
|
|
3811
|
-
return toPlanRow(row);
|
|
3812
|
-
}
|
|
3813
|
-
function listPlans(options = {}) {
|
|
3814
|
-
const db = getDatabase();
|
|
3815
|
-
const conditions = [];
|
|
3816
|
-
const params = [];
|
|
3817
|
-
if (options.status !== void 0) {
|
|
3818
|
-
conditions.push(`status = ?`);
|
|
3819
|
-
params.push(options.status);
|
|
3820
|
-
}
|
|
3821
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3822
|
-
const limit = options.limit ?? DEFAULT_LIST_LIMIT2;
|
|
3823
|
-
params.push(limit);
|
|
3824
|
-
const rows = db.prepare(
|
|
3825
|
-
`SELECT ${SELECT_COLUMNS4}
|
|
3826
|
-
FROM plans
|
|
3827
|
-
${where}
|
|
3828
|
-
ORDER BY created_at DESC
|
|
3829
|
-
LIMIT ?`
|
|
3830
|
-
).all(...params);
|
|
3831
|
-
return rows.map(toPlanRow);
|
|
3832
|
-
}
|
|
3833
|
-
function listPlansBySession(sessionId) {
|
|
3834
|
-
const db = getDatabase();
|
|
3835
|
-
const rows = db.prepare(
|
|
3836
|
-
`SELECT ${SELECT_COLUMNS4}
|
|
3837
|
-
FROM plans
|
|
3838
|
-
WHERE session_id = ?
|
|
3839
|
-
ORDER BY created_at DESC`
|
|
3840
|
-
).all(sessionId);
|
|
3841
|
-
return rows.map(toPlanRow);
|
|
3842
|
-
}
|
|
3843
|
-
|
|
3844
3875
|
// src/daemon/jobs/session-cleanup.ts
|
|
3845
3876
|
import { unlink, glob } from "fs/promises";
|
|
3846
3877
|
async function cleanupAfterSessionCascade(sessionId, result, embeddingManager, vaultDir) {
|
|
@@ -3885,11 +3916,12 @@ async function triggerTitleSummary(sessionId, deps) {
|
|
|
3885
3916
|
if (config.agent.summary_batch_interval <= 0) return;
|
|
3886
3917
|
if (config.agent.event_tasks_enabled === false) return;
|
|
3887
3918
|
try {
|
|
3888
|
-
const { runAgent } = await import("./executor-
|
|
3889
|
-
|
|
3919
|
+
const { runAgent: runAgent2 } = await import("./executor-DO6QFC6G.js");
|
|
3920
|
+
runAgent2(vaultDir, {
|
|
3890
3921
|
task: "title-summary",
|
|
3891
3922
|
instruction: `Process session ${sessionId} only`,
|
|
3892
|
-
embeddingManager
|
|
3923
|
+
embeddingManager,
|
|
3924
|
+
logger
|
|
3893
3925
|
}).catch((err) => {
|
|
3894
3926
|
logger.warn(LOG_KINDS.AGENT_ERROR, "Title-summary task failed", {
|
|
3895
3927
|
session_id: sessionId,
|
|
@@ -3900,11 +3932,16 @@ async function triggerTitleSummary(sessionId, deps) {
|
|
|
3900
3932
|
}
|
|
3901
3933
|
}
|
|
3902
3934
|
|
|
3935
|
+
// src/daemon/api/error-envelope.ts
|
|
3936
|
+
function errorBody(code, message) {
|
|
3937
|
+
return { error: { code, message } };
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3903
3940
|
// src/daemon/api/sessions.ts
|
|
3904
|
-
var
|
|
3941
|
+
var DEFAULT_LIST_LIMIT2 = 50;
|
|
3905
3942
|
var DEFAULT_LIST_OFFSET2 = 0;
|
|
3906
3943
|
async function handleListSessions(req) {
|
|
3907
|
-
const limit = req.query.limit ? Number(req.query.limit) :
|
|
3944
|
+
const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIST_LIMIT2;
|
|
3908
3945
|
const offset = req.query.offset ? Number(req.query.offset) : DEFAULT_LIST_OFFSET2;
|
|
3909
3946
|
const status = req.query.status || void 0;
|
|
3910
3947
|
const agent = req.query.agent || void 0;
|
|
@@ -3947,7 +3984,7 @@ async function handleGetSessionAttachments(req) {
|
|
|
3947
3984
|
}
|
|
3948
3985
|
async function handleGetSessionPlans(req) {
|
|
3949
3986
|
const plans = listPlansBySession(req.params.id);
|
|
3950
|
-
return { body: plans };
|
|
3987
|
+
return { body: { plans } };
|
|
3951
3988
|
}
|
|
3952
3989
|
function createSessionMutationHandlers(deps) {
|
|
3953
3990
|
const { embeddingManager, vaultDir, logger, liveConfig } = deps;
|
|
@@ -3988,7 +4025,125 @@ function createSessionMutationHandlers(deps) {
|
|
|
3988
4025
|
const impact = getSessionImpact(sessionId);
|
|
3989
4026
|
return { body: impact };
|
|
3990
4027
|
}
|
|
3991
|
-
|
|
4028
|
+
async function handleDeletePlan(req) {
|
|
4029
|
+
const existing = getPlan(req.params.id);
|
|
4030
|
+
if (!existing) return { status: 404, body: errorBody("plan-not-found", "Plan not found") };
|
|
4031
|
+
const localMachineId = getTeamMachineId();
|
|
4032
|
+
const body = req.body;
|
|
4033
|
+
const forceRemote = body?.force_remote === true;
|
|
4034
|
+
if (existing.machine_id !== localMachineId && !forceRemote) {
|
|
4035
|
+
logger.warn(LOG_KINDS.API_SESSION_DELETE, "Cross-machine plan delete rejected", {
|
|
4036
|
+
plan_id: existing.id,
|
|
4037
|
+
plan_machine_id: existing.machine_id,
|
|
4038
|
+
local_machine_id: localMachineId
|
|
4039
|
+
});
|
|
4040
|
+
return {
|
|
4041
|
+
status: 403,
|
|
4042
|
+
body: errorBody(
|
|
4043
|
+
"cross-machine-delete",
|
|
4044
|
+
'Plan belongs to another machine; pass {"force_remote": true} to delete.'
|
|
4045
|
+
)
|
|
4046
|
+
};
|
|
4047
|
+
}
|
|
4048
|
+
if (existing.machine_id !== localMachineId && forceRemote) {
|
|
4049
|
+
logger.warn(LOG_KINDS.API_SESSION_DELETE, "Cross-machine plan delete allowed by force_remote", {
|
|
4050
|
+
plan_id: existing.id,
|
|
4051
|
+
plan_machine_id: existing.machine_id,
|
|
4052
|
+
local_machine_id: localMachineId
|
|
4053
|
+
});
|
|
4054
|
+
}
|
|
4055
|
+
const deleted = deletePlan(req.params.id);
|
|
4056
|
+
if (!deleted) return { status: 404, body: errorBody("plan-not-found", "Plan not found") };
|
|
4057
|
+
embeddingManager.onRemoved("plans", deleted.id);
|
|
4058
|
+
logger.info(LOG_KINDS.API_SESSION_DELETE, "Plan deleted", {
|
|
4059
|
+
plan_id: deleted.id,
|
|
4060
|
+
session_id: deleted.session_id,
|
|
4061
|
+
logical_key: deleted.logical_key
|
|
4062
|
+
});
|
|
4063
|
+
return {
|
|
4064
|
+
body: {
|
|
4065
|
+
ok: true,
|
|
4066
|
+
id: deleted.id,
|
|
4067
|
+
session_id: deleted.session_id
|
|
4068
|
+
}
|
|
4069
|
+
};
|
|
4070
|
+
}
|
|
4071
|
+
return { handleDeleteSession, handleCompleteSession, handleGetSessionImpact, handleDeletePlan };
|
|
4072
|
+
}
|
|
4073
|
+
|
|
4074
|
+
// src/db/queries/entities.ts
|
|
4075
|
+
var DEFAULT_LIST_LIMIT3 = 100;
|
|
4076
|
+
var ENTITY_COLUMNS = [
|
|
4077
|
+
"id",
|
|
4078
|
+
"agent_id",
|
|
4079
|
+
"type",
|
|
4080
|
+
"name",
|
|
4081
|
+
"properties",
|
|
4082
|
+
"first_seen",
|
|
4083
|
+
"last_seen",
|
|
4084
|
+
"status",
|
|
4085
|
+
"machine_id",
|
|
4086
|
+
"synced_at"
|
|
4087
|
+
];
|
|
4088
|
+
var SELECT_COLUMNS4 = ENTITY_COLUMNS.join(", ");
|
|
4089
|
+
function toEntityRow(row) {
|
|
4090
|
+
return {
|
|
4091
|
+
id: row.id,
|
|
4092
|
+
agent_id: row.agent_id,
|
|
4093
|
+
type: row.type,
|
|
4094
|
+
name: row.name,
|
|
4095
|
+
properties: row.properties ?? null,
|
|
4096
|
+
first_seen: row.first_seen,
|
|
4097
|
+
last_seen: row.last_seen,
|
|
4098
|
+
status: row.status ?? "active",
|
|
4099
|
+
machine_id: row.machine_id ?? "local",
|
|
4100
|
+
synced_at: row.synced_at ?? null
|
|
4101
|
+
};
|
|
4102
|
+
}
|
|
4103
|
+
function listEntities(options = {}) {
|
|
4104
|
+
const db = getDatabase();
|
|
4105
|
+
const conditions = [];
|
|
4106
|
+
const params = [];
|
|
4107
|
+
if (options.agent_id !== void 0) {
|
|
4108
|
+
conditions.push(`agent_id = ?`);
|
|
4109
|
+
params.push(options.agent_id);
|
|
4110
|
+
}
|
|
4111
|
+
if (options.type !== void 0) {
|
|
4112
|
+
conditions.push(`type = ?`);
|
|
4113
|
+
params.push(options.type);
|
|
4114
|
+
}
|
|
4115
|
+
if (options.name !== void 0) {
|
|
4116
|
+
conditions.push(`name = ?`);
|
|
4117
|
+
params.push(options.name);
|
|
4118
|
+
}
|
|
4119
|
+
if (options.status !== void 0) {
|
|
4120
|
+
conditions.push(`status = ?`);
|
|
4121
|
+
params.push(options.status);
|
|
4122
|
+
} else {
|
|
4123
|
+
conditions.push(`status = ?`);
|
|
4124
|
+
params.push("active");
|
|
4125
|
+
}
|
|
4126
|
+
if (options.mentioned_in !== void 0 && options.note_type !== void 0) {
|
|
4127
|
+
conditions.push(
|
|
4128
|
+
`id IN (SELECT entity_id FROM entity_mentions WHERE note_id = ? AND note_type = ?)`
|
|
4129
|
+
);
|
|
4130
|
+
params.push(options.mentioned_in);
|
|
4131
|
+
params.push(options.note_type);
|
|
4132
|
+
}
|
|
4133
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
4134
|
+
const limit = options.limit ?? DEFAULT_LIST_LIMIT3;
|
|
4135
|
+
const offset = options.offset ?? 0;
|
|
4136
|
+
params.push(limit);
|
|
4137
|
+
params.push(offset);
|
|
4138
|
+
const rows = db.prepare(
|
|
4139
|
+
`SELECT ${SELECT_COLUMNS4}
|
|
4140
|
+
FROM entities
|
|
4141
|
+
${where}
|
|
4142
|
+
ORDER BY last_seen DESC
|
|
4143
|
+
LIMIT ?
|
|
4144
|
+
OFFSET ?`
|
|
4145
|
+
).all(...params);
|
|
4146
|
+
return rows.map(toEntityRow);
|
|
3992
4147
|
}
|
|
3993
4148
|
|
|
3994
4149
|
// src/daemon/api/mycelium.ts
|
|
@@ -3997,7 +4152,6 @@ var DEFAULT_LIST_OFFSET3 = 0;
|
|
|
3997
4152
|
var DEFAULT_GRAPH_DEPTH = 1;
|
|
3998
4153
|
var MAX_GRAPH_DEPTH = 3;
|
|
3999
4154
|
var SPORE_NAME_PREVIEW_CHARS = 60;
|
|
4000
|
-
var GRAPH_SEED_ENTITY_LIMIT = 4;
|
|
4001
4155
|
var GRAPH_SEED_SPORE_LIMIT = 4;
|
|
4002
4156
|
var GRAPH_SEED_SESSION_LIMIT = 4;
|
|
4003
4157
|
var EXCLUDED_GRAPH_EDGE_TYPES = /* @__PURE__ */ new Set(["HAS_BATCH", "EXTRACTED_FROM"]);
|
|
@@ -4056,15 +4210,6 @@ async function handleGetGraphSeeds(_req) {
|
|
|
4056
4210
|
ORDER BY started_at DESC
|
|
4057
4211
|
LIMIT ?`
|
|
4058
4212
|
).all(GRAPH_SEED_SESSION_LIMIT);
|
|
4059
|
-
const entityRows = db.prepare(
|
|
4060
|
-
`SELECT e.id, e.type, e.name, e.status, e.first_seen as created_at, COUNT(em.entity_id) as mention_count
|
|
4061
|
-
FROM entities e
|
|
4062
|
-
LEFT JOIN entity_mentions em ON em.entity_id = e.id
|
|
4063
|
-
WHERE e.agent_id = ? AND e.status = 'active'
|
|
4064
|
-
GROUP BY e.id
|
|
4065
|
-
ORDER BY mention_count DESC, e.last_seen DESC
|
|
4066
|
-
LIMIT ?`
|
|
4067
|
-
).all(DEFAULT_AGENT_ID, GRAPH_SEED_ENTITY_LIMIT);
|
|
4068
4213
|
const sporeSeeds = sporeRows.map((row) => ({
|
|
4069
4214
|
id: row.id,
|
|
4070
4215
|
name: (row.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
@@ -4082,20 +4227,66 @@ async function handleGetGraphSeeds(_req) {
|
|
|
4082
4227
|
created_at: row.created_at,
|
|
4083
4228
|
content: row.summary ?? void 0
|
|
4084
4229
|
}));
|
|
4085
|
-
const
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4230
|
+
const topConnectedRow = db.prepare(
|
|
4231
|
+
`SELECT node_id FROM (
|
|
4232
|
+
SELECT source_id AS node_id, COUNT(*) AS cnt
|
|
4233
|
+
FROM graph_edges
|
|
4234
|
+
WHERE agent_id = ?
|
|
4235
|
+
AND type NOT IN ('HAS_BATCH', 'EXTRACTED_FROM')
|
|
4236
|
+
AND source_type IN ('spore', 'session')
|
|
4237
|
+
GROUP BY source_id
|
|
4238
|
+
UNION ALL
|
|
4239
|
+
SELECT target_id, COUNT(*)
|
|
4240
|
+
FROM graph_edges
|
|
4241
|
+
WHERE agent_id = ?
|
|
4242
|
+
AND type NOT IN ('HAS_BATCH', 'EXTRACTED_FROM')
|
|
4243
|
+
AND target_type IN ('spore', 'session')
|
|
4244
|
+
GROUP BY target_id
|
|
4245
|
+
)
|
|
4246
|
+
GROUP BY node_id
|
|
4247
|
+
ORDER BY SUM(cnt) DESC
|
|
4248
|
+
LIMIT 1`
|
|
4249
|
+
).get(DEFAULT_AGENT_ID, DEFAULT_AGENT_ID);
|
|
4250
|
+
let topSeed = null;
|
|
4251
|
+
if (topConnectedRow?.node_id) {
|
|
4252
|
+
const topId = topConnectedRow.node_id;
|
|
4253
|
+
const sporeHit = db.prepare(
|
|
4254
|
+
`SELECT id, observation_type, status, content, created_at
|
|
4255
|
+
FROM spores WHERE id = ?`
|
|
4256
|
+
).get(topId);
|
|
4257
|
+
if (sporeHit) {
|
|
4258
|
+
topSeed = {
|
|
4259
|
+
id: sporeHit.id,
|
|
4260
|
+
name: (sporeHit.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
4261
|
+
type: "spore",
|
|
4262
|
+
status: sporeHit.status ?? void 0,
|
|
4263
|
+
created_at: sporeHit.created_at,
|
|
4264
|
+
content: sporeHit.content,
|
|
4265
|
+
observation_type: sporeHit.observation_type
|
|
4266
|
+
};
|
|
4267
|
+
} else {
|
|
4268
|
+
const sessionHit = db.prepare(
|
|
4269
|
+
`SELECT id, title, summary, status, started_at as created_at
|
|
4270
|
+
FROM sessions WHERE id = ?`
|
|
4271
|
+
).get(topId);
|
|
4272
|
+
if (sessionHit) {
|
|
4273
|
+
topSeed = {
|
|
4274
|
+
id: sessionHit.id,
|
|
4275
|
+
name: sessionHit.title ?? `Session ${sessionHit.id.slice(-6)}`,
|
|
4276
|
+
type: "session",
|
|
4277
|
+
status: sessionHit.status ?? void 0,
|
|
4278
|
+
created_at: sessionHit.created_at,
|
|
4279
|
+
content: sessionHit.summary ?? void 0
|
|
4280
|
+
};
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4283
|
+
}
|
|
4093
4284
|
const seeds = [
|
|
4094
|
-
...
|
|
4095
|
-
...sessionSeeds,
|
|
4096
|
-
...sporeSeeds
|
|
4285
|
+
...topSeed ? [topSeed] : [],
|
|
4286
|
+
...sessionSeeds.filter((s) => s.id !== topSeed?.id),
|
|
4287
|
+
...sporeSeeds.filter((s) => s.id !== topSeed?.id)
|
|
4097
4288
|
];
|
|
4098
|
-
const recommendedId =
|
|
4289
|
+
const recommendedId = topSeed?.id ?? sessionSeeds[0]?.id ?? sporeSeeds[0]?.id ?? null;
|
|
4099
4290
|
return {
|
|
4100
4291
|
body: {
|
|
4101
4292
|
seeds,
|
|
@@ -4106,32 +4297,19 @@ async function handleGetGraphSeeds(_req) {
|
|
|
4106
4297
|
async function handleGetGraph(req) {
|
|
4107
4298
|
const depth = Math.min(Number(req.query.depth) || DEFAULT_GRAPH_DEPTH, MAX_GRAPH_DEPTH);
|
|
4108
4299
|
const id = req.params.id;
|
|
4109
|
-
let
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
if (
|
|
4113
|
-
|
|
4114
|
-
centerType = "entity";
|
|
4300
|
+
let centerType;
|
|
4301
|
+
if (getSpore(id)) {
|
|
4302
|
+
centerType = "spore";
|
|
4303
|
+
} else if (getSession(id)) {
|
|
4304
|
+
centerType = "session";
|
|
4115
4305
|
} else {
|
|
4116
|
-
|
|
4117
|
-
if (spore) {
|
|
4118
|
-
centerNode = spore;
|
|
4119
|
-
centerType = "spore";
|
|
4120
|
-
} else {
|
|
4121
|
-
const session = getSession(id);
|
|
4122
|
-
if (session) {
|
|
4123
|
-
centerNode = session;
|
|
4124
|
-
centerType = "session";
|
|
4125
|
-
}
|
|
4126
|
-
}
|
|
4306
|
+
return { status: 404, body: { error: "not_found" } };
|
|
4127
4307
|
}
|
|
4128
|
-
if (!centerNode) return { status: 404, body: { error: "not_found" } };
|
|
4129
4308
|
const graph = getGraphForNode(id, centerType, { depth });
|
|
4130
4309
|
const filteredEdges = graph.edges.filter(
|
|
4131
4310
|
(e) => !EXCLUDED_GRAPH_EDGE_TYPES.has(e.type)
|
|
4132
4311
|
);
|
|
4133
4312
|
const graphDb = getDatabase();
|
|
4134
|
-
const entityIds = /* @__PURE__ */ new Set();
|
|
4135
4313
|
const sporeIds = /* @__PURE__ */ new Set();
|
|
4136
4314
|
const sessionIds = /* @__PURE__ */ new Set();
|
|
4137
4315
|
for (const edge of filteredEdges) {
|
|
@@ -4139,70 +4317,23 @@ async function handleGetGraph(req) {
|
|
|
4139
4317
|
[edge.source_id, edge.source_type],
|
|
4140
4318
|
[edge.target_id, edge.target_type]
|
|
4141
4319
|
]) {
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
entityIds.add(nodeId);
|
|
4145
|
-
break;
|
|
4146
|
-
case "spore":
|
|
4147
|
-
sporeIds.add(nodeId);
|
|
4148
|
-
break;
|
|
4149
|
-
case "session":
|
|
4150
|
-
sessionIds.add(nodeId);
|
|
4151
|
-
break;
|
|
4152
|
-
}
|
|
4320
|
+
if (type === "spore") sporeIds.add(nodeId);
|
|
4321
|
+
else if (type === "session") sessionIds.add(nodeId);
|
|
4153
4322
|
}
|
|
4154
4323
|
}
|
|
4155
|
-
if (centerType === "entity") entityIds.add(id);
|
|
4156
4324
|
if (centerType === "spore") sporeIds.add(id);
|
|
4157
|
-
|
|
4158
|
-
const entityIdArray = Array.from(entityIds);
|
|
4159
|
-
let entityNodes = [];
|
|
4160
|
-
if (entityIdArray.length > 0) {
|
|
4161
|
-
const placeholders = entityIdArray.map(() => "?").join(", ");
|
|
4162
|
-
entityNodes = graphDb.prepare(
|
|
4163
|
-
`SELECT id, type, name, properties, status, first_seen as created_at
|
|
4164
|
-
FROM entities WHERE id IN (${placeholders})`
|
|
4165
|
-
).all(...entityIdArray);
|
|
4166
|
-
}
|
|
4325
|
+
else sessionIds.add(id);
|
|
4167
4326
|
const sporeIdArray = Array.from(sporeIds);
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
`SELECT id, observation_type, status, content, properties, created_at
|
|
4173
|
-
FROM spores WHERE id IN (${placeholders})`
|
|
4174
|
-
).all(...sporeIdArray);
|
|
4175
|
-
}
|
|
4327
|
+
const sporeNodes = sporeIdArray.length > 0 ? graphDb.prepare(
|
|
4328
|
+
`SELECT id, observation_type, status, content, properties, created_at
|
|
4329
|
+
FROM spores WHERE id IN (${sporeIdArray.map(() => "?").join(", ")})`
|
|
4330
|
+
).all(...sporeIdArray) : [];
|
|
4176
4331
|
const sessionIdArray = Array.from(sessionIds);
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
`SELECT id, title, summary, status, started_at as created_at
|
|
4182
|
-
FROM sessions WHERE id IN (${placeholders})`
|
|
4183
|
-
).all(...sessionIdArray);
|
|
4184
|
-
}
|
|
4185
|
-
const mentionCounts = /* @__PURE__ */ new Map();
|
|
4186
|
-
if (entityIdArray.length > 0) {
|
|
4187
|
-
const placeholders = entityIdArray.map(() => "?").join(", ");
|
|
4188
|
-
const mentionRows = graphDb.prepare(
|
|
4189
|
-
`SELECT entity_id, COUNT(*) as count FROM entity_mentions
|
|
4190
|
-
WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
|
|
4191
|
-
).all(...entityIdArray);
|
|
4192
|
-
for (const row of mentionRows) {
|
|
4193
|
-
mentionCounts.set(row.entity_id, Number(row.count));
|
|
4194
|
-
}
|
|
4195
|
-
}
|
|
4332
|
+
const sessionNodes = sessionIdArray.length > 0 ? graphDb.prepare(
|
|
4333
|
+
`SELECT id, title, summary, status, started_at as created_at
|
|
4334
|
+
FROM sessions WHERE id IN (${sessionIdArray.map(() => "?").join(", ")})`
|
|
4335
|
+
).all(...sessionIdArray) : [];
|
|
4196
4336
|
const allNodes = [
|
|
4197
|
-
...entityNodes.map((n) => ({
|
|
4198
|
-
id: n.id,
|
|
4199
|
-
name: n.name,
|
|
4200
|
-
type: n.type,
|
|
4201
|
-
status: n.status ?? void 0,
|
|
4202
|
-
created_at: n.created_at,
|
|
4203
|
-
properties: n.properties ?? void 0,
|
|
4204
|
-
mention_count: mentionCounts.get(n.id) ?? 0
|
|
4205
|
-
})),
|
|
4206
4337
|
...sporeNodes.map((n) => ({
|
|
4207
4338
|
id: n.id,
|
|
4208
4339
|
name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
@@ -4241,10 +4372,6 @@ async function handleGetGraph(req) {
|
|
|
4241
4372
|
var FULL_GRAPH_NODE_LIMIT = 500;
|
|
4242
4373
|
async function handleGetFullGraph(_req) {
|
|
4243
4374
|
const db = getDatabase();
|
|
4244
|
-
const entityRows = db.prepare(
|
|
4245
|
-
`SELECT id, type, name, properties, status, first_seen as created_at
|
|
4246
|
-
FROM entities WHERE agent_id = ? LIMIT ?`
|
|
4247
|
-
).all(DEFAULT_AGENT_ID, FULL_GRAPH_NODE_LIMIT);
|
|
4248
4375
|
const sporeRows = db.prepare(
|
|
4249
4376
|
`SELECT id, observation_type, status, content, properties, created_at
|
|
4250
4377
|
FROM spores WHERE agent_id = ? AND status = 'active' LIMIT ?`
|
|
@@ -4254,43 +4381,21 @@ async function handleGetFullGraph(_req) {
|
|
|
4254
4381
|
FROM sessions ORDER BY created_at DESC LIMIT ?`
|
|
4255
4382
|
).all(FULL_GRAPH_NODE_LIMIT);
|
|
4256
4383
|
const allIds = /* @__PURE__ */ new Set();
|
|
4257
|
-
for (const r of [...
|
|
4384
|
+
for (const r of [...sporeRows, ...sessionRows]) {
|
|
4258
4385
|
allIds.add(r.id);
|
|
4259
4386
|
}
|
|
4260
4387
|
const excludedTypes = Array.from(EXCLUDED_GRAPH_EDGE_TYPES).map(() => "?").join(", ");
|
|
4261
4388
|
const allIdsList = Array.from(allIds);
|
|
4262
4389
|
const idPlaceholders = allIdsList.map(() => "?").join(", ");
|
|
4263
|
-
const edgeRows = db.prepare(
|
|
4390
|
+
const edgeRows = allIdsList.length > 0 ? db.prepare(
|
|
4264
4391
|
`SELECT source_id, source_type, target_id, target_type, type, confidence
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList);
|
|
4271
|
-
const filteredEdges = edgeRows;
|
|
4272
|
-
const mentionCounts = /* @__PURE__ */ new Map();
|
|
4273
|
-
const entityIdArray = entityRows.map((r) => r.id);
|
|
4274
|
-
if (entityIdArray.length > 0) {
|
|
4275
|
-
const placeholders = entityIdArray.map(() => "?").join(", ");
|
|
4276
|
-
const mentionRows = db.prepare(
|
|
4277
|
-
`SELECT entity_id, COUNT(*) as count FROM entity_mentions
|
|
4278
|
-
WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
|
|
4279
|
-
).all(...entityIdArray);
|
|
4280
|
-
for (const row of mentionRows) {
|
|
4281
|
-
mentionCounts.set(row.entity_id, Number(row.count));
|
|
4282
|
-
}
|
|
4283
|
-
}
|
|
4392
|
+
FROM graph_edges
|
|
4393
|
+
WHERE agent_id = ?
|
|
4394
|
+
AND type NOT IN (${excludedTypes})
|
|
4395
|
+
AND source_id IN (${idPlaceholders})
|
|
4396
|
+
AND target_id IN (${idPlaceholders})`
|
|
4397
|
+
).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList) : [];
|
|
4284
4398
|
const nodes = [
|
|
4285
|
-
...entityRows.map((n) => ({
|
|
4286
|
-
id: n.id,
|
|
4287
|
-
name: n.name,
|
|
4288
|
-
type: n.type,
|
|
4289
|
-
status: n.status ?? void 0,
|
|
4290
|
-
created_at: n.created_at,
|
|
4291
|
-
properties: n.properties ?? void 0,
|
|
4292
|
-
mention_count: mentionCounts.get(n.id) ?? 0
|
|
4293
|
-
})),
|
|
4294
4399
|
...sporeRows.map((n) => ({
|
|
4295
4400
|
id: n.id,
|
|
4296
4401
|
name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
@@ -4310,7 +4415,7 @@ async function handleGetFullGraph(_req) {
|
|
|
4310
4415
|
content: n.summary ?? void 0
|
|
4311
4416
|
}))
|
|
4312
4417
|
];
|
|
4313
|
-
const edges =
|
|
4418
|
+
const edges = edgeRows.map((e) => ({
|
|
4314
4419
|
source_id: e.source_id,
|
|
4315
4420
|
target_id: e.target_id,
|
|
4316
4421
|
label: e.type,
|
|
@@ -4353,15 +4458,40 @@ function createSearchHandler(deps) {
|
|
|
4353
4458
|
const type = req.query.type;
|
|
4354
4459
|
const limit = Number(req.query.limit) || SEARCH_RESULTS_DEFAULT_LIMIT;
|
|
4355
4460
|
const namespace = req.query.namespace;
|
|
4461
|
+
const sanitized = sanitizeFtsQuery(query);
|
|
4356
4462
|
if (mode === "fts") {
|
|
4357
|
-
|
|
4358
|
-
|
|
4463
|
+
try {
|
|
4464
|
+
const results = fullTextSearch(sanitized, { type, limit });
|
|
4465
|
+
return { body: { mode: "fts", results } };
|
|
4466
|
+
} catch (err) {
|
|
4467
|
+
return {
|
|
4468
|
+
status: 500,
|
|
4469
|
+
body: {
|
|
4470
|
+
error: "fts_query_failed",
|
|
4471
|
+
message: errorMessage(err),
|
|
4472
|
+
query,
|
|
4473
|
+
sanitized_query: sanitized !== query ? sanitized : void 0
|
|
4474
|
+
}
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4359
4477
|
}
|
|
4360
4478
|
const queryVector = await deps.embeddingManager.embedQuery(query);
|
|
4361
4479
|
if (queryVector === null) {
|
|
4362
4480
|
if (mode === "auto") {
|
|
4363
|
-
|
|
4364
|
-
|
|
4481
|
+
try {
|
|
4482
|
+
const results = fullTextSearch(sanitized, { type, limit });
|
|
4483
|
+
return { body: { mode: "fts", results, fallback: true } };
|
|
4484
|
+
} catch (err) {
|
|
4485
|
+
return {
|
|
4486
|
+
status: 500,
|
|
4487
|
+
body: {
|
|
4488
|
+
error: "fts_fallback_failed",
|
|
4489
|
+
message: errorMessage(err),
|
|
4490
|
+
query,
|
|
4491
|
+
sanitized_query: sanitized !== query ? sanitized : void 0
|
|
4492
|
+
}
|
|
4493
|
+
};
|
|
4494
|
+
}
|
|
4365
4495
|
}
|
|
4366
4496
|
return { body: { mode: "semantic", results: [], provider_unavailable: true } };
|
|
4367
4497
|
}
|
|
@@ -4393,66 +4523,297 @@ function createSearchHandler(deps) {
|
|
|
4393
4523
|
};
|
|
4394
4524
|
}
|
|
4395
4525
|
|
|
4396
|
-
// src/
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4526
|
+
// src/symbionts/injection-support.ts
|
|
4527
|
+
import fs16 from "fs";
|
|
4528
|
+
import path17 from "path";
|
|
4529
|
+
var SESSION_START_SIGNALS = ["hook session-start", '"/context"', '"/context/resume"'];
|
|
4530
|
+
var PROMPT_SUBMIT_SIGNALS = ["hook user-prompt-submit", '"/context/prompt"'];
|
|
4531
|
+
function resolveTemplateCandidates(manifest) {
|
|
4532
|
+
const packageRoot = resolvePackageRoot();
|
|
4533
|
+
const templateFile = manifest.registration?.hooksFormat === "plugin-file" ? "plugin.ts" : "hooks.json";
|
|
4534
|
+
return [
|
|
4535
|
+
path17.join(packageRoot, "src", "symbionts", "templates", manifest.name, templateFile),
|
|
4536
|
+
path17.join(packageRoot, "dist", "src", "symbionts", "templates", manifest.name, templateFile)
|
|
4537
|
+
];
|
|
4538
|
+
}
|
|
4539
|
+
function readHooksTemplate(manifest) {
|
|
4540
|
+
for (const candidate of resolveTemplateCandidates(manifest)) {
|
|
4541
|
+
if (fs16.existsSync(candidate)) {
|
|
4542
|
+
return fs16.readFileSync(candidate, "utf-8");
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
return "";
|
|
4546
|
+
}
|
|
4547
|
+
function hasAnySignal(template, signals) {
|
|
4548
|
+
return signals.some((signal) => template.includes(signal));
|
|
4549
|
+
}
|
|
4550
|
+
function detectSymbiontInjectionSupport(manifest) {
|
|
4551
|
+
const template = readHooksTemplate(manifest);
|
|
4552
|
+
return {
|
|
4553
|
+
supportsSessionStartInjection: hasAnySignal(template, SESSION_START_SIGNALS),
|
|
4554
|
+
supportsPromptSubmitInjection: hasAnySignal(template, PROMPT_SUBMIT_SIGNALS)
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
|
|
4558
|
+
// src/daemon/api/symbionts.ts
|
|
4559
|
+
function listSymbiontInfos(vaultDir) {
|
|
4560
|
+
const manifests = loadManifests();
|
|
4561
|
+
let enabledNames = null;
|
|
4562
|
+
try {
|
|
4563
|
+
enabledNames = getEnabledSymbiontNames(loadMergedConfig(vaultDir));
|
|
4564
|
+
} catch {
|
|
4565
|
+
}
|
|
4566
|
+
return manifests.map((manifest) => ({
|
|
4567
|
+
name: manifest.name,
|
|
4568
|
+
displayName: manifest.displayName,
|
|
4569
|
+
binary: manifest.binary,
|
|
4570
|
+
enabled: enabledNames ? enabledNames.has(manifest.name) : true,
|
|
4571
|
+
...manifest.resumeCommand ? { resumeCommand: manifest.resumeCommand } : {},
|
|
4572
|
+
...detectSymbiontInjectionSupport(manifest)
|
|
4573
|
+
}));
|
|
4574
|
+
}
|
|
4575
|
+
async function handleListSymbionts(vaultDir) {
|
|
4576
|
+
return { body: { symbionts: listSymbiontInfos(vaultDir) } };
|
|
4577
|
+
}
|
|
4578
|
+
|
|
4579
|
+
// src/daemon/cortex.ts
|
|
4580
|
+
var CORTEX_PROMPT_BUILDER_TASK = "cortex-prompt-builder";
|
|
4581
|
+
var CORTEX_INSTRUCTIONS_TASK = "cortex-instructions";
|
|
4582
|
+
var JSON_INDENT = 2;
|
|
4583
|
+
function isCortexPromptBuilderDetails(value) {
|
|
4584
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4585
|
+
}
|
|
4586
|
+
function getLatestReportForAction(runId, action) {
|
|
4587
|
+
const reports = listReports(runId);
|
|
4588
|
+
for (let i = reports.length - 1; i >= 0; i -= 1) {
|
|
4589
|
+
if (reports[i]?.action === action) return reports[i];
|
|
4590
|
+
}
|
|
4591
|
+
return void 0;
|
|
4592
|
+
}
|
|
4593
|
+
function getCortexInstructionsSnapshot(config) {
|
|
4594
|
+
const row = getCortexInstructions(DEFAULT_AGENT_ID);
|
|
4595
|
+
return {
|
|
4596
|
+
content: row?.content ?? "",
|
|
4597
|
+
generatedAt: row?.generated_at ?? null,
|
|
4598
|
+
sourceRunId: row?.source_run_id ?? null,
|
|
4599
|
+
enabled: config.context.cortex_enabled,
|
|
4600
|
+
stored: Boolean(row)
|
|
4601
|
+
};
|
|
4602
|
+
}
|
|
4603
|
+
function resolvePromptBuilderSymbiont(vaultDir, requestedName) {
|
|
4604
|
+
const enabledSymbionts = listSymbiontInfos(vaultDir).filter((symbiont) => symbiont.enabled);
|
|
4605
|
+
if (enabledSymbionts.length === 0) return null;
|
|
4606
|
+
if (!requestedName) return enabledSymbionts[0] ?? null;
|
|
4607
|
+
return enabledSymbionts.find((symbiont) => symbiont.name === requestedName) ?? null;
|
|
4608
|
+
}
|
|
4609
|
+
async function buildCortexPrompt(vaultDir, deps, goal, requestedSymbiont) {
|
|
4610
|
+
const targetSymbiont = resolvePromptBuilderSymbiont(vaultDir, requestedSymbiont);
|
|
4611
|
+
const delivery = resolveInstructionDelivery(deps.config.context, targetSymbiont);
|
|
4612
|
+
const instructions = delivery.inlineInstructions ? getCortexInstructions(DEFAULT_AGENT_ID) : null;
|
|
4613
|
+
const builderInstruction = [
|
|
4614
|
+
`Goal:
|
|
4615
|
+
${goal.trim()}`,
|
|
4616
|
+
"",
|
|
4617
|
+
"## Target symbiont",
|
|
4618
|
+
JSON.stringify(
|
|
4619
|
+
targetSymbiont ? {
|
|
4620
|
+
name: targetSymbiont.name,
|
|
4621
|
+
displayName: targetSymbiont.displayName,
|
|
4622
|
+
supportsSessionStartInjection: targetSymbiont.supportsSessionStartInjection,
|
|
4623
|
+
supportsPromptSubmitInjection: targetSymbiont.supportsPromptSubmitInjection
|
|
4624
|
+
} : null,
|
|
4625
|
+
null,
|
|
4626
|
+
JSON_INDENT
|
|
4627
|
+
),
|
|
4628
|
+
"",
|
|
4629
|
+
"## Delivery contract",
|
|
4630
|
+
JSON.stringify(
|
|
4631
|
+
{
|
|
4632
|
+
inline_instructions: delivery.inlineInstructions,
|
|
4633
|
+
reason: delivery.reason
|
|
4634
|
+
},
|
|
4635
|
+
null,
|
|
4636
|
+
JSON_INDENT
|
|
4637
|
+
),
|
|
4638
|
+
"",
|
|
4639
|
+
delivery.inlineInstructions ? [
|
|
4640
|
+
"## Current Cortex session-start instructions",
|
|
4641
|
+
instructions?.content || "No current Cortex instructions are available.",
|
|
4642
|
+
""
|
|
4643
|
+
].join("\n") : "## Current Cortex session-start instructions\nOmit them from the prompt because this symbiont receives session-start injection.\n"
|
|
4644
|
+
].join("\n");
|
|
4645
|
+
const resultPromise = runAgent(vaultDir, {
|
|
4646
|
+
task: CORTEX_PROMPT_BUILDER_TASK,
|
|
4647
|
+
agentId: DEFAULT_AGENT_ID,
|
|
4648
|
+
instruction: builderInstruction,
|
|
4649
|
+
embeddingManager: deps.embeddingManager,
|
|
4650
|
+
logger: deps.logger
|
|
4651
|
+
});
|
|
4652
|
+
const runId = getLatestRunId(DEFAULT_AGENT_ID, CORTEX_PROMPT_BUILDER_TASK);
|
|
4653
|
+
const tracked = resultPromise.catch((err) => {
|
|
4654
|
+
deps.logger.warn(LOG_KINDS.AGENT_ERROR, "cortex-prompt-builder task failed", {
|
|
4655
|
+
run_id: runId ?? void 0,
|
|
4656
|
+
error: String(err)
|
|
4657
|
+
});
|
|
4658
|
+
});
|
|
4659
|
+
deps.registerInflightRun?.(tracked);
|
|
4660
|
+
return {
|
|
4661
|
+
started: true,
|
|
4662
|
+
runId,
|
|
4663
|
+
inlineInstructions: delivery.inlineInstructions,
|
|
4664
|
+
targetSymbiont
|
|
4665
|
+
};
|
|
4666
|
+
}
|
|
4667
|
+
function getCortexPromptResult(runId) {
|
|
4668
|
+
const run = getRun(runId);
|
|
4669
|
+
if (!run) return null;
|
|
4670
|
+
const reports = listReports(runId);
|
|
4671
|
+
const promptReport = getLatestReportForAction(runId, "cortex_prompt_builder");
|
|
4672
|
+
const details = tryParseJson(promptReport?.details, isCortexPromptBuilderDetails);
|
|
4673
|
+
return {
|
|
4674
|
+
runId,
|
|
4675
|
+
status: run.status,
|
|
4676
|
+
prompt: details?.prompt ?? "",
|
|
4677
|
+
reports: reports.map((report) => ({
|
|
4678
|
+
id: report.id,
|
|
4679
|
+
action: report.action,
|
|
4680
|
+
summary: report.summary,
|
|
4681
|
+
created_at: report.created_at
|
|
4682
|
+
})),
|
|
4683
|
+
error: run.error
|
|
4684
|
+
};
|
|
4685
|
+
}
|
|
4686
|
+
async function triggerCortexInstructions(deps) {
|
|
4687
|
+
const { vaultDir, embeddingManager, liveConfig, logger, getTeamClient } = deps;
|
|
4688
|
+
const loadExecutor = deps.loadExecutor ?? (() => import("./executor-DO6QFC6G.js"));
|
|
4689
|
+
const config = liveConfig.current;
|
|
4690
|
+
if (config.agent.event_tasks_enabled === false) {
|
|
4691
|
+
return { started: false, reason: "event-tasks-disabled" };
|
|
4692
|
+
}
|
|
4693
|
+
if (!hasConfiguredProvider(config, CORTEX_INSTRUCTIONS_TASK)) {
|
|
4694
|
+
return { started: false, reason: "provider-not-configured" };
|
|
4695
|
+
}
|
|
4696
|
+
let runAgentFn;
|
|
4697
|
+
try {
|
|
4698
|
+
({ runAgent: runAgentFn } = await loadExecutor());
|
|
4699
|
+
} catch (err) {
|
|
4700
|
+
logger.warn(LOG_KINDS.AGENT_ERROR, "cortex-instructions: agent module unavailable", {
|
|
4701
|
+
error: String(err)
|
|
4702
|
+
});
|
|
4703
|
+
return {
|
|
4704
|
+
started: false,
|
|
4705
|
+
reason: "agent-module-unavailable",
|
|
4706
|
+
error: String(err)
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4709
|
+
try {
|
|
4710
|
+
const built = await buildCortexInstructionsInput(config, getTeamClient);
|
|
4711
|
+
const resultPromise = runAgentFn(vaultDir, {
|
|
4712
|
+
task: CORTEX_INSTRUCTIONS_TASK,
|
|
4713
|
+
agentId: DEFAULT_AGENT_ID,
|
|
4714
|
+
instruction: built.instruction,
|
|
4715
|
+
runContext: { cortex_instruction_input_hash: built.inputHash },
|
|
4716
|
+
embeddingManager
|
|
4717
|
+
});
|
|
4718
|
+
const runId = getLatestRunId(DEFAULT_AGENT_ID, CORTEX_INSTRUCTIONS_TASK);
|
|
4719
|
+
const tracked = resultPromise.catch((err) => {
|
|
4720
|
+
logger.warn(LOG_KINDS.AGENT_ERROR, "Cortex instructions task failed", {
|
|
4721
|
+
error: String(err),
|
|
4722
|
+
run_id: runId ?? void 0
|
|
4723
|
+
});
|
|
4724
|
+
});
|
|
4725
|
+
deps.registerInflightRun?.(tracked);
|
|
4726
|
+
return { started: true, runId };
|
|
4727
|
+
} catch (err) {
|
|
4728
|
+
logger.warn(LOG_KINDS.AGENT_ERROR, "Failed to start cortex-instructions task", {
|
|
4729
|
+
error: String(err)
|
|
4730
|
+
});
|
|
4731
|
+
return {
|
|
4732
|
+
started: false,
|
|
4733
|
+
reason: "startup-failed",
|
|
4734
|
+
error: String(err)
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4739
|
+
// src/daemon/api/context.ts
|
|
4740
|
+
var SessionContextBody = external_exports.object({
|
|
4741
|
+
session_id: external_exports.string().optional(),
|
|
4742
|
+
branch: external_exports.string().optional()
|
|
4743
|
+
});
|
|
4744
|
+
var ResumeContextBody = external_exports.object({
|
|
4745
|
+
session_id: external_exports.string(),
|
|
4746
|
+
parent_session_id: external_exports.string().optional(),
|
|
4747
|
+
branch: external_exports.string().optional()
|
|
4748
|
+
});
|
|
4749
|
+
var PromptContextBody = external_exports.object({
|
|
4750
|
+
prompt: external_exports.string(),
|
|
4751
|
+
session_id: external_exports.string().optional()
|
|
4752
|
+
});
|
|
4753
|
+
function createSessionContextHandler(deps) {
|
|
4754
|
+
return async function handleSessionContext(req) {
|
|
4755
|
+
const { session_id, branch } = SessionContextBody.parse(req.body);
|
|
4756
|
+
const { logger, liveConfig } = deps;
|
|
4757
|
+
const config = liveConfig.current;
|
|
4758
|
+
logger.debug(LOG_KINDS.CONTEXT_QUERY, "Session context query", { session_id });
|
|
4759
|
+
try {
|
|
4760
|
+
const cortexEnabled = shouldInjectCortex(config.context);
|
|
4761
|
+
const digestEnabled = shouldInjectSessionStartDigest(config.context);
|
|
4762
|
+
if (!cortexEnabled && !digestEnabled) {
|
|
4763
|
+
logger.debug(LOG_KINDS.CONTEXT_SESSION, "Session-start context disabled", { session_id });
|
|
4764
|
+
return { body: { text: "" } };
|
|
4765
|
+
}
|
|
4766
|
+
let sourceRunId = null;
|
|
4767
|
+
let cortexContent = "";
|
|
4768
|
+
if (cortexEnabled) {
|
|
4769
|
+
const snapshot = getCortexInstructionsSnapshot(config);
|
|
4770
|
+
if (snapshot.content) {
|
|
4771
|
+
cortexContent = snapshot.content;
|
|
4772
|
+
sourceRunId = snapshot.sourceRunId;
|
|
4773
|
+
} else {
|
|
4774
|
+
logger.debug(LOG_KINDS.CONTEXT_SESSION, "No stored Cortex instructions available for session start", {
|
|
4775
|
+
session_id
|
|
4776
|
+
});
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
const composed = composeSessionStartContext(config, cortexContent);
|
|
4780
|
+
const textParts = composed.parts.map((p) => p.text);
|
|
4781
|
+
const sourceParts = composed.parts.map(
|
|
4782
|
+
(p) => p.kind === "cortex" ? "cortex" : `digest:${p.tier ?? config.context.digest_tier}`
|
|
4783
|
+
);
|
|
4784
|
+
if (digestEnabled && !composed.parts.some((p) => p.kind === "digest")) {
|
|
4785
|
+
logger.debug(LOG_KINDS.CONTEXT_SESSION, "No preferred digest extract available for session start", {
|
|
4786
|
+
session_id,
|
|
4787
|
+
preferred_tier: config.context.digest_tier
|
|
4788
|
+
});
|
|
4789
|
+
}
|
|
4790
|
+
if (textParts.length === 0) {
|
|
4791
|
+
return { body: { text: "" } };
|
|
4792
|
+
}
|
|
4793
|
+
if (branch) {
|
|
4794
|
+
textParts.push(`Branch:: \`${branch}\``);
|
|
4795
|
+
}
|
|
4796
|
+
textParts.push(`Session:: \`${session_id}\``);
|
|
4797
|
+
const source = sourceParts.join("+");
|
|
4798
|
+
const contextText = textParts.join("\n\n");
|
|
4799
|
+
const estimatedTokens = estimateTokens(contextText);
|
|
4800
|
+
logger.info(
|
|
4801
|
+
LOG_KINDS.CONTEXT_SESSION,
|
|
4802
|
+
`Session context: ${estimatedTokens} est. tokens, source=${source}`,
|
|
4803
|
+
{
|
|
4804
|
+
session_id,
|
|
4805
|
+
source,
|
|
4806
|
+
branch,
|
|
4807
|
+
source_run_id: sourceRunId,
|
|
4445
4808
|
text_length: contextText.length,
|
|
4446
4809
|
estimated_tokens: estimatedTokens,
|
|
4447
|
-
generated_at: extract?.generated_at,
|
|
4448
4810
|
injected_text: contextText
|
|
4449
4811
|
}
|
|
4450
4812
|
);
|
|
4451
4813
|
return {
|
|
4452
4814
|
body: {
|
|
4453
4815
|
text: contextText,
|
|
4454
|
-
source
|
|
4455
|
-
...extract ? { tier } : {}
|
|
4816
|
+
source
|
|
4456
4817
|
}
|
|
4457
4818
|
};
|
|
4458
4819
|
} catch (error) {
|
|
@@ -4557,9 +4918,7 @@ function createPromptContextHandler(deps) {
|
|
|
4557
4918
|
raw_results: vectorResults.length,
|
|
4558
4919
|
top_similarity: vectorResults[0]?.similarity
|
|
4559
4920
|
});
|
|
4560
|
-
if (vectorResults.length === 0) {
|
|
4561
|
-
return { body: { text: "" } };
|
|
4562
|
-
}
|
|
4921
|
+
if (vectorResults.length === 0) return { body: { text: "" } };
|
|
4563
4922
|
const eligible = vectorResults.filter(
|
|
4564
4923
|
(r) => !EXCLUDED_SPORE_STATUSES.has(r.metadata.status)
|
|
4565
4924
|
);
|
|
@@ -4570,20 +4929,22 @@ function createPromptContextHandler(deps) {
|
|
|
4570
4929
|
const topResults = eligible.slice(0, maxSpores);
|
|
4571
4930
|
const hydrated = hydrateSearchResults(topResults);
|
|
4572
4931
|
const spores = hydrated.filter((r) => r.type === "spore");
|
|
4573
|
-
if (spores.length === 0) {
|
|
4574
|
-
return { body: { text: "" } };
|
|
4575
|
-
}
|
|
4932
|
+
if (spores.length === 0) return { body: { text: "" } };
|
|
4576
4933
|
const text = formatSporeContext(spores);
|
|
4577
4934
|
const promptTokens = estimateTokens(text);
|
|
4578
4935
|
const titles = spores.map((s) => s.title);
|
|
4579
|
-
logger.info(
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4936
|
+
logger.info(
|
|
4937
|
+
LOG_KINDS.CONTEXT_PROMPT,
|
|
4938
|
+
`Prompt context: ${spores.length} spores [${titles.join(", ")}] (~${promptTokens} tokens)`,
|
|
4939
|
+
{
|
|
4940
|
+
session_id,
|
|
4941
|
+
spore_count: spores.length,
|
|
4942
|
+
spore_titles: titles,
|
|
4943
|
+
scores: spores.map((s) => s.score.toFixed(3)),
|
|
4944
|
+
estimated_tokens: promptTokens,
|
|
4945
|
+
injected_text: text
|
|
4946
|
+
}
|
|
4947
|
+
);
|
|
4587
4948
|
return { body: { text } };
|
|
4588
4949
|
};
|
|
4589
4950
|
}
|
|
@@ -4602,6 +4963,75 @@ function formatSporeContext(spores) {
|
|
|
4602
4963
|
return text === header ? "" : text;
|
|
4603
4964
|
}
|
|
4604
4965
|
|
|
4966
|
+
// src/daemon/api/cortex.ts
|
|
4967
|
+
var PromptBuilderBody = external_exports.object({
|
|
4968
|
+
goal: external_exports.string().trim().min(1),
|
|
4969
|
+
symbiont: external_exports.string().trim().optional()
|
|
4970
|
+
});
|
|
4971
|
+
var PromptBuilderStatusParams = external_exports.object({
|
|
4972
|
+
runId: external_exports.string().trim().min(1)
|
|
4973
|
+
});
|
|
4974
|
+
function createCortexHandlers(vaultDir, deps) {
|
|
4975
|
+
async function handleGetInstructions() {
|
|
4976
|
+
const snapshot = getCortexInstructionsSnapshot(deps.liveConfig.current);
|
|
4977
|
+
return { body: snapshot };
|
|
4978
|
+
}
|
|
4979
|
+
async function handleRefreshInstructions() {
|
|
4980
|
+
const result = await triggerCortexInstructions({
|
|
4981
|
+
vaultDir,
|
|
4982
|
+
embeddingManager: deps.embeddingManager,
|
|
4983
|
+
liveConfig: deps.liveConfig,
|
|
4984
|
+
logger: deps.logger,
|
|
4985
|
+
getTeamClient: deps.getTeamClient,
|
|
4986
|
+
registerInflightRun: deps.registerInflightRun
|
|
4987
|
+
});
|
|
4988
|
+
if (!result.started && (result.reason === "provider-not-configured" || result.reason === "event-tasks-disabled")) {
|
|
4989
|
+
return {
|
|
4990
|
+
status: 400,
|
|
4991
|
+
body: {
|
|
4992
|
+
...errorBody(
|
|
4993
|
+
result.reason,
|
|
4994
|
+
result.reason === "provider-not-configured" ? "No agent provider configured. Configure one in Settings." : "Event-driven tasks are disabled. Enable them in Settings."
|
|
4995
|
+
),
|
|
4996
|
+
started: false,
|
|
4997
|
+
reason: result.reason
|
|
4998
|
+
}
|
|
4999
|
+
};
|
|
5000
|
+
}
|
|
5001
|
+
return { body: result };
|
|
5002
|
+
}
|
|
5003
|
+
async function handleBuildPrompt(req) {
|
|
5004
|
+
const { goal, symbiont } = PromptBuilderBody.parse(req.body);
|
|
5005
|
+
const result = await buildCortexPrompt(
|
|
5006
|
+
vaultDir,
|
|
5007
|
+
{
|
|
5008
|
+
config: deps.liveConfig.current,
|
|
5009
|
+
embeddingManager: deps.embeddingManager,
|
|
5010
|
+
getTeamClient: deps.getTeamClient,
|
|
5011
|
+
logger: deps.logger,
|
|
5012
|
+
registerInflightRun: deps.registerInflightRun
|
|
5013
|
+
},
|
|
5014
|
+
goal,
|
|
5015
|
+
symbiont
|
|
5016
|
+
);
|
|
5017
|
+
return { body: result };
|
|
5018
|
+
}
|
|
5019
|
+
async function handleGetPromptResult(req) {
|
|
5020
|
+
const { runId } = PromptBuilderStatusParams.parse(req.params);
|
|
5021
|
+
const result = getCortexPromptResult(runId);
|
|
5022
|
+
if (!result) {
|
|
5023
|
+
return { status: 404, body: errorBody("run-not-found", "Run not found") };
|
|
5024
|
+
}
|
|
5025
|
+
return { body: result };
|
|
5026
|
+
}
|
|
5027
|
+
return {
|
|
5028
|
+
handleGetInstructions,
|
|
5029
|
+
handleRefreshInstructions,
|
|
5030
|
+
handleBuildPrompt,
|
|
5031
|
+
handleGetPromptResult
|
|
5032
|
+
};
|
|
5033
|
+
}
|
|
5034
|
+
|
|
4605
5035
|
// src/db/queries/feed.ts
|
|
4606
5036
|
function getActivityFeed(limit = FEED_DEFAULT_LIMIT) {
|
|
4607
5037
|
const db = getDatabase();
|
|
@@ -4640,24 +5070,6 @@ async function handleGetFeed(req) {
|
|
|
4640
5070
|
return { body: feed };
|
|
4641
5071
|
}
|
|
4642
5072
|
|
|
4643
|
-
// src/daemon/api/symbionts.ts
|
|
4644
|
-
async function handleListSymbionts(vaultDir) {
|
|
4645
|
-
const manifests = loadManifests();
|
|
4646
|
-
let enabledNames = null;
|
|
4647
|
-
try {
|
|
4648
|
-
enabledNames = getEnabledSymbiontNames(loadMergedConfig(vaultDir));
|
|
4649
|
-
} catch {
|
|
4650
|
-
}
|
|
4651
|
-
const symbionts = manifests.map((m) => ({
|
|
4652
|
-
name: m.name,
|
|
4653
|
-
displayName: m.displayName,
|
|
4654
|
-
binary: m.binary,
|
|
4655
|
-
enabled: enabledNames ? enabledNames.has(m.name) : true,
|
|
4656
|
-
...m.resumeCommand ? { resumeCommand: m.resumeCommand } : {}
|
|
4657
|
-
}));
|
|
4658
|
-
return { body: { symbionts } };
|
|
4659
|
-
}
|
|
4660
|
-
|
|
4661
5073
|
// src/daemon/api/embedding.ts
|
|
4662
5074
|
var EMBEDDING_STATUS_IDLE = "idle";
|
|
4663
5075
|
var EMBEDDING_STATUS_PENDING = "pending";
|
|
@@ -5661,17 +6073,17 @@ var SqliteRecordSource = class {
|
|
|
5661
6073
|
};
|
|
5662
6074
|
|
|
5663
6075
|
// src/daemon/database/manager.ts
|
|
5664
|
-
import
|
|
6076
|
+
import fs18 from "fs";
|
|
5665
6077
|
|
|
5666
6078
|
// src/db/queries/database.ts
|
|
5667
|
-
import
|
|
6079
|
+
import fs17 from "fs";
|
|
5668
6080
|
function pragmaScalar(name) {
|
|
5669
6081
|
const db = getDatabase();
|
|
5670
6082
|
return db.pragma(name, { simple: true });
|
|
5671
6083
|
}
|
|
5672
6084
|
function safeFileSize(filePath) {
|
|
5673
6085
|
try {
|
|
5674
|
-
return
|
|
6086
|
+
return fs17.statSync(filePath).size;
|
|
5675
6087
|
} catch (err) {
|
|
5676
6088
|
if (err.code === "ENOENT") return 0;
|
|
5677
6089
|
throw err;
|
|
@@ -5943,7 +6355,7 @@ var DatabaseMaintenanceManager = class {
|
|
|
5943
6355
|
}
|
|
5944
6356
|
async vacuum() {
|
|
5945
6357
|
const size_before = this.fileSize();
|
|
5946
|
-
const stats = await
|
|
6358
|
+
const stats = await fs18.promises.statfs(this.vaultDir);
|
|
5947
6359
|
const free_bytes = Number(stats.bavail) * Number(stats.bsize);
|
|
5948
6360
|
const required_bytes = size_before * VACUUM_FREE_SPACE_MULTIPLIER;
|
|
5949
6361
|
if (free_bytes < required_bytes) {
|
|
@@ -5991,7 +6403,7 @@ var DatabaseMaintenanceManager = class {
|
|
|
5991
6403
|
}
|
|
5992
6404
|
fileSize() {
|
|
5993
6405
|
try {
|
|
5994
|
-
return
|
|
6406
|
+
return fs18.statSync(this.dbPath).size;
|
|
5995
6407
|
} catch {
|
|
5996
6408
|
return 0;
|
|
5997
6409
|
}
|
|
@@ -6308,45 +6720,104 @@ async function handleUpdateTaskConfig(req, vaultDir) {
|
|
|
6308
6720
|
};
|
|
6309
6721
|
}
|
|
6310
6722
|
|
|
6723
|
+
// src/agent/types.ts
|
|
6724
|
+
var PROVIDER_TYPES = [
|
|
6725
|
+
"anthropic",
|
|
6726
|
+
"ollama",
|
|
6727
|
+
"lmstudio",
|
|
6728
|
+
"openai",
|
|
6729
|
+
"openrouter",
|
|
6730
|
+
"openai-compatible"
|
|
6731
|
+
];
|
|
6732
|
+
|
|
6311
6733
|
// src/daemon/api/providers.ts
|
|
6312
6734
|
var ANTHROPIC_MODELS_TIMEOUT_MS = 5e3;
|
|
6313
6735
|
var ANTHROPIC_MODELS_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
6314
6736
|
var anthropicModelsCache = null;
|
|
6315
6737
|
var HTTP_OK2 = 200;
|
|
6316
6738
|
var HTTP_BAD_REQUEST2 = 400;
|
|
6317
|
-
async function handleGetProviders() {
|
|
6318
|
-
const
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6739
|
+
async function handleGetProviders(logger) {
|
|
6740
|
+
const detectionPlan = [
|
|
6741
|
+
{
|
|
6742
|
+
detect: () => detectAnthropic(logger),
|
|
6743
|
+
fallback: { type: "anthropic", runtime: "claude-sdk", available: false, models: [] }
|
|
6744
|
+
},
|
|
6745
|
+
{
|
|
6746
|
+
detect: () => detectLocalProviderInfo("ollama", OllamaBackend.DEFAULT_BASE_URL),
|
|
6747
|
+
fallback: { type: "ollama", runtime: "claude-sdk", available: false, baseUrl: OllamaBackend.DEFAULT_BASE_URL, models: [] }
|
|
6748
|
+
},
|
|
6749
|
+
{
|
|
6750
|
+
detect: () => detectLocalProviderInfo("lmstudio", LmStudioBackend.DEFAULT_BASE_URL),
|
|
6751
|
+
fallback: { type: "lmstudio", runtime: "claude-sdk", available: false, baseUrl: LmStudioBackend.DEFAULT_BASE_URL, models: [] }
|
|
6752
|
+
},
|
|
6753
|
+
{
|
|
6754
|
+
detect: () => detectRemoteProviderInfo("openai", DEFAULT_OPENAI_URL, logger),
|
|
6755
|
+
fallback: { type: "openai", runtime: "openai-agents", available: false, authConfigured: false, baseUrl: DEFAULT_OPENAI_URL, models: [] }
|
|
6756
|
+
},
|
|
6757
|
+
{
|
|
6758
|
+
detect: () => detectRemoteProviderInfo("openrouter", DEFAULT_OPENROUTER_URL, logger),
|
|
6759
|
+
fallback: { type: "openrouter", runtime: "openai-agents", available: false, authConfigured: false, baseUrl: DEFAULT_OPENROUTER_URL, models: [] }
|
|
6760
|
+
},
|
|
6761
|
+
{
|
|
6762
|
+
detect: () => detectLocalProviderInfo("openai-compatible", LmStudioBackend.DEFAULT_BASE_URL),
|
|
6763
|
+
fallback: { type: "openai-compatible", runtime: "openai-agents", available: false, baseUrl: LmStudioBackend.DEFAULT_BASE_URL, models: [] }
|
|
6764
|
+
}
|
|
6765
|
+
];
|
|
6766
|
+
const results = await Promise.allSettled(detectionPlan.map((entry) => entry.detect()));
|
|
6323
6767
|
const providers = results.map(
|
|
6324
|
-
(
|
|
6768
|
+
(result, index) => result.status === "fulfilled" ? result.value : detectionPlan[index].fallback
|
|
6325
6769
|
);
|
|
6326
6770
|
return { status: HTTP_OK2, body: { providers } };
|
|
6327
6771
|
}
|
|
6772
|
+
var ProviderTestBody = external_exports.object({
|
|
6773
|
+
type: external_exports.enum(PROVIDER_TYPES),
|
|
6774
|
+
baseUrl: external_exports.string().optional(),
|
|
6775
|
+
/**
|
|
6776
|
+
* Deprecated: `base_url` is the legacy snake_case key. Accepted for one
|
|
6777
|
+
* release and then removed. Use `baseUrl` going forward.
|
|
6778
|
+
*/
|
|
6779
|
+
base_url: external_exports.string().optional(),
|
|
6780
|
+
local_backend: external_exports.enum(["ollama", "lmstudio"]).optional(),
|
|
6781
|
+
model: external_exports.string().optional()
|
|
6782
|
+
});
|
|
6328
6783
|
async function handleTestProvider(req) {
|
|
6329
|
-
const
|
|
6330
|
-
|
|
6331
|
-
if (!type || !["anthropic", "ollama", "lmstudio"].includes(type)) {
|
|
6784
|
+
const parse = ProviderTestBody.safeParse(req.body);
|
|
6785
|
+
if (!parse.success) {
|
|
6332
6786
|
return {
|
|
6333
6787
|
status: HTTP_BAD_REQUEST2,
|
|
6334
|
-
body: { error:
|
|
6788
|
+
body: { error: `type is required and must be one of: ${PROVIDER_TYPES.join(", ")}` }
|
|
6335
6789
|
};
|
|
6336
6790
|
}
|
|
6337
|
-
const
|
|
6791
|
+
const parsed = parse.data;
|
|
6792
|
+
if (parsed.base_url !== void 0 && parsed.baseUrl === void 0) {
|
|
6793
|
+
process.stderr.write(
|
|
6794
|
+
"[myco providers] POST /api/providers/test: base_url is deprecated; use baseUrl\n"
|
|
6795
|
+
);
|
|
6796
|
+
}
|
|
6797
|
+
const type = parsed.type;
|
|
6798
|
+
const baseUrl = parsed.baseUrl ?? parsed.base_url;
|
|
6799
|
+
const localBackend = parsed.local_backend;
|
|
6338
6800
|
const start = performance.now();
|
|
6339
6801
|
let result;
|
|
6340
6802
|
try {
|
|
6341
6803
|
if (type === "ollama") {
|
|
6342
|
-
result = await
|
|
6804
|
+
result = await testResolvedLocalProvider("ollama", baseUrl);
|
|
6343
6805
|
} else if (type === "lmstudio") {
|
|
6344
|
-
result = await
|
|
6806
|
+
result = await testResolvedLocalProvider("lmstudio", baseUrl);
|
|
6807
|
+
} else if (type === "openai-compatible") {
|
|
6808
|
+
result = await testResolvedLocalProvider("openai-compatible", baseUrl, localBackend);
|
|
6809
|
+
} else if (type === "openai") {
|
|
6810
|
+
result = await testRemoteProvider("openai", "OpenAI");
|
|
6811
|
+
} else if (type === "openrouter") {
|
|
6812
|
+
result = await testRemoteProvider("openrouter", "OpenRouter");
|
|
6345
6813
|
} else {
|
|
6346
6814
|
result = testAnthropic();
|
|
6347
6815
|
}
|
|
6348
6816
|
} catch (err) {
|
|
6349
|
-
result = {
|
|
6817
|
+
result = {
|
|
6818
|
+
ok: false,
|
|
6819
|
+
error: errorMessage(err)
|
|
6820
|
+
};
|
|
6350
6821
|
}
|
|
6351
6822
|
if (result.ok) {
|
|
6352
6823
|
result.latency_ms = Math.round(performance.now() - start);
|
|
@@ -6354,15 +6825,21 @@ async function handleTestProvider(req) {
|
|
|
6354
6825
|
return { status: HTTP_OK2, body: result };
|
|
6355
6826
|
}
|
|
6356
6827
|
async function detectLocalProviderInfo(type, defaultBaseUrl) {
|
|
6357
|
-
const status = await checkLocalProvider(type);
|
|
6828
|
+
const status = await checkLocalProvider(type === "openai-compatible" ? "lmstudio" : type);
|
|
6358
6829
|
const variantFiltered = status.models.filter((m) => !/-ctx\d+/.test(m));
|
|
6359
6830
|
const models = filterLlmModels(variantFiltered);
|
|
6360
|
-
return {
|
|
6831
|
+
return {
|
|
6832
|
+
type,
|
|
6833
|
+
runtime: type === "openai-compatible" ? "openai-agents" : "claude-sdk",
|
|
6834
|
+
available: status.available,
|
|
6835
|
+
baseUrl: defaultBaseUrl,
|
|
6836
|
+
models
|
|
6837
|
+
};
|
|
6361
6838
|
}
|
|
6362
|
-
async function detectAnthropic() {
|
|
6839
|
+
async function detectAnthropic(logger) {
|
|
6363
6840
|
const now = Date.now();
|
|
6364
6841
|
if (anthropicModelsCache && now - anthropicModelsCache.ts < ANTHROPIC_MODELS_CACHE_TTL_MS) {
|
|
6365
|
-
return { type: "anthropic", available: true, models: anthropicModelsCache.models };
|
|
6842
|
+
return { type: "anthropic", runtime: "claude-sdk", available: true, models: anthropicModelsCache.models };
|
|
6366
6843
|
}
|
|
6367
6844
|
let models = ANTHROPIC_MODELS;
|
|
6368
6845
|
try {
|
|
@@ -6375,10 +6852,35 @@ async function detectAnthropic() {
|
|
|
6375
6852
|
if (liveModels.length > 0) {
|
|
6376
6853
|
models = liveModels;
|
|
6377
6854
|
}
|
|
6378
|
-
} catch {
|
|
6855
|
+
} catch (err) {
|
|
6856
|
+
const detail = errorMessage(err);
|
|
6857
|
+
logger?.warn("providers.anthropic.models-unavailable", "Anthropic model list unavailable", { error: detail });
|
|
6379
6858
|
}
|
|
6380
6859
|
anthropicModelsCache = { ts: now, models };
|
|
6381
|
-
return { type: "anthropic", available: true, models };
|
|
6860
|
+
return { type: "anthropic", runtime: "claude-sdk", available: true, models };
|
|
6861
|
+
}
|
|
6862
|
+
async function detectRemoteProviderInfo(type, baseUrl, logger) {
|
|
6863
|
+
const authConfigured = Boolean(getRemoteProviderApiKey(type));
|
|
6864
|
+
let models = [];
|
|
6865
|
+
let available = false;
|
|
6866
|
+
if (authConfigured) {
|
|
6867
|
+
try {
|
|
6868
|
+
models = await fetchRemoteProviderModels(type, void 0, ANTHROPIC_MODELS_TIMEOUT_MS);
|
|
6869
|
+
available = true;
|
|
6870
|
+
} catch (err) {
|
|
6871
|
+
available = false;
|
|
6872
|
+
const detail = errorMessage(err);
|
|
6873
|
+
logger?.warn(`providers.${type}.models-unavailable`, `${type} model list unavailable`, { error: detail });
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
return {
|
|
6877
|
+
type,
|
|
6878
|
+
runtime: "openai-agents",
|
|
6879
|
+
available,
|
|
6880
|
+
authConfigured,
|
|
6881
|
+
baseUrl,
|
|
6882
|
+
models
|
|
6883
|
+
};
|
|
6382
6884
|
}
|
|
6383
6885
|
async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
|
|
6384
6886
|
const available = await backend.isAvailable();
|
|
@@ -6387,9 +6889,114 @@ async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
|
|
|
6387
6889
|
}
|
|
6388
6890
|
return { ok: true };
|
|
6389
6891
|
}
|
|
6892
|
+
async function testResolvedLocalProvider(type, baseUrl, localBackend) {
|
|
6893
|
+
const kind = inferLocalOpenAIBackendKind({ type, localBackend, baseUrl }) ?? "lmstudio";
|
|
6894
|
+
const label = type === "openai-compatible" ? `OpenAI-compatible ${getLocalOpenAIBackendLabel(kind)} provider` : getLocalOpenAIBackendLabel(kind);
|
|
6895
|
+
return testLocalProvider(
|
|
6896
|
+
createLocalOpenAIBackend(kind, baseUrl),
|
|
6897
|
+
label,
|
|
6898
|
+
getLocalOpenAIBackendDefaultBaseUrl(kind),
|
|
6899
|
+
baseUrl
|
|
6900
|
+
);
|
|
6901
|
+
}
|
|
6390
6902
|
function testAnthropic() {
|
|
6391
6903
|
return { ok: true };
|
|
6392
6904
|
}
|
|
6905
|
+
async function testRemoteProvider(provider, label) {
|
|
6906
|
+
if (!getRemoteProviderApiKey(provider)) {
|
|
6907
|
+
return { ok: false, error: `${label} API key not configured in daemon secrets or environment` };
|
|
6908
|
+
}
|
|
6909
|
+
try {
|
|
6910
|
+
await fetchRemoteProviderModels(provider);
|
|
6911
|
+
return { ok: true };
|
|
6912
|
+
} catch (error) {
|
|
6913
|
+
return { ok: false, error: error instanceof Error ? error.message : `${label} connection failed` };
|
|
6914
|
+
}
|
|
6915
|
+
}
|
|
6916
|
+
|
|
6917
|
+
// src/daemon/api/provider-secrets.ts
|
|
6918
|
+
var SECRET_PREVIEW_PREFIX_CHARS = 8;
|
|
6919
|
+
var SECRET_PREVIEW_SUFFIX_CHARS = 4;
|
|
6920
|
+
var SECRET_ENV_BY_PROVIDER = {
|
|
6921
|
+
openai: OPENAI_API_KEY_ENV,
|
|
6922
|
+
openrouter: OPENROUTER_API_KEY_ENV
|
|
6923
|
+
};
|
|
6924
|
+
function isSecretProvider(value) {
|
|
6925
|
+
return value === "openai" || value === "openrouter";
|
|
6926
|
+
}
|
|
6927
|
+
function maskSecret(secret) {
|
|
6928
|
+
if (secret.length <= SECRET_PREVIEW_PREFIX_CHARS + SECRET_PREVIEW_SUFFIX_CHARS) {
|
|
6929
|
+
return "*".repeat(secret.length);
|
|
6930
|
+
}
|
|
6931
|
+
return `${secret.slice(0, SECRET_PREVIEW_PREFIX_CHARS)}${"*".repeat(secret.length - SECRET_PREVIEW_PREFIX_CHARS - SECRET_PREVIEW_SUFFIX_CHARS)}${secret.slice(-SECRET_PREVIEW_SUFFIX_CHARS)}`;
|
|
6932
|
+
}
|
|
6933
|
+
function buildSecretInfo(provider, storedSecrets) {
|
|
6934
|
+
const envKey = SECRET_ENV_BY_PROVIDER[provider];
|
|
6935
|
+
const storedValue = storedSecrets[envKey];
|
|
6936
|
+
const envValue = process.env[envKey];
|
|
6937
|
+
const effectiveValue = storedValue ?? envValue;
|
|
6938
|
+
return {
|
|
6939
|
+
configured: Boolean(effectiveValue),
|
|
6940
|
+
envKey,
|
|
6941
|
+
maskedValue: effectiveValue ? maskSecret(effectiveValue) : null,
|
|
6942
|
+
source: storedValue ? "vault" : envValue ? "env" : "none"
|
|
6943
|
+
};
|
|
6944
|
+
}
|
|
6945
|
+
function getSecretInfo(vaultDir, provider) {
|
|
6946
|
+
return buildSecretInfo(provider, readSecrets(vaultDir));
|
|
6947
|
+
}
|
|
6948
|
+
async function handleGetProviderSecrets(vaultDir) {
|
|
6949
|
+
const storedSecrets = readSecrets(vaultDir);
|
|
6950
|
+
return {
|
|
6951
|
+
body: {
|
|
6952
|
+
secrets: {
|
|
6953
|
+
openai: buildSecretInfo("openai", storedSecrets),
|
|
6954
|
+
openrouter: buildSecretInfo("openrouter", storedSecrets)
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
};
|
|
6958
|
+
}
|
|
6959
|
+
async function handlePutProviderSecret(vaultDir, req) {
|
|
6960
|
+
const provider = req.params.provider;
|
|
6961
|
+
const body = req.body;
|
|
6962
|
+
if (!provider || !isSecretProvider(provider)) {
|
|
6963
|
+
return { status: 400, body: { error: "provider must be one of: openai, openrouter" } };
|
|
6964
|
+
}
|
|
6965
|
+
if (!body?.api_key?.trim()) {
|
|
6966
|
+
return { status: 400, body: { error: "api_key is required" } };
|
|
6967
|
+
}
|
|
6968
|
+
const envKey = SECRET_ENV_BY_PROVIDER[provider];
|
|
6969
|
+
const apiKey = body.api_key.trim();
|
|
6970
|
+
writeSecret(vaultDir, envKey, apiKey);
|
|
6971
|
+
process.env[envKey] = apiKey;
|
|
6972
|
+
if (provider === "openai") {
|
|
6973
|
+
process.env.OPENAI_API_KEY = apiKey;
|
|
6974
|
+
}
|
|
6975
|
+
return {
|
|
6976
|
+
body: {
|
|
6977
|
+
provider,
|
|
6978
|
+
secret: getSecretInfo(vaultDir, provider)
|
|
6979
|
+
}
|
|
6980
|
+
};
|
|
6981
|
+
}
|
|
6982
|
+
async function handleDeleteProviderSecret(vaultDir, req) {
|
|
6983
|
+
const provider = req.params.provider;
|
|
6984
|
+
if (!provider || !isSecretProvider(provider)) {
|
|
6985
|
+
return { status: 400, body: { error: "provider must be one of: openai, openrouter" } };
|
|
6986
|
+
}
|
|
6987
|
+
const envKey = SECRET_ENV_BY_PROVIDER[provider];
|
|
6988
|
+
deleteSecrets(vaultDir, [envKey]);
|
|
6989
|
+
delete process.env[envKey];
|
|
6990
|
+
if (provider === "openai") {
|
|
6991
|
+
delete process.env.OPENAI_API_KEY;
|
|
6992
|
+
}
|
|
6993
|
+
return {
|
|
6994
|
+
body: {
|
|
6995
|
+
provider,
|
|
6996
|
+
secret: getSecretInfo(vaultDir, provider)
|
|
6997
|
+
}
|
|
6998
|
+
};
|
|
6999
|
+
}
|
|
6393
7000
|
|
|
6394
7001
|
// src/daemon/task-scheduling.ts
|
|
6395
7002
|
import { resolve } from "path";
|
|
@@ -6442,7 +7049,7 @@ function buildScheduledJobs(tasks, configOverrides, context, initialLastRuns) {
|
|
|
6442
7049
|
// src/daemon/task-scheduling.ts
|
|
6443
7050
|
var SCHEDULED_JOB_PREFIX = "scheduled:";
|
|
6444
7051
|
async function registerScheduledTasks(powerManager, deps) {
|
|
6445
|
-
const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig } = deps;
|
|
7052
|
+
const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig, getTeamClient } = deps;
|
|
6446
7053
|
const runningTasks = /* @__PURE__ */ new Set();
|
|
6447
7054
|
if (!definitionsDir) {
|
|
6448
7055
|
logger.warn(LOG_KINDS.AGENT_ERROR, "Skipping dynamic task scheduling \u2014 definitions directory unavailable");
|
|
@@ -6452,7 +7059,7 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6452
7059
|
if (!lastEnabled) {
|
|
6453
7060
|
logger.info(LOG_KINDS.AGENT_RUN, "Scheduled agent tasks disabled (agent.scheduled_tasks_enabled: false) \u2014 jobs registered but will no-op until enabled");
|
|
6454
7061
|
}
|
|
6455
|
-
const { loadAllTasks: loadAllTasks2 } = await import("./registry-
|
|
7062
|
+
const { loadAllTasks: loadAllTasks2 } = await import("./registry-F3THYC5M.js");
|
|
6456
7063
|
const allTasks = Array.from(loadAllTasks2(definitionsDir, vaultDir).values());
|
|
6457
7064
|
const taskAgentMap = /* @__PURE__ */ new Map();
|
|
6458
7065
|
for (const task of allTasks) {
|
|
@@ -6488,10 +7095,34 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6488
7095
|
lastEnabled = enabled;
|
|
6489
7096
|
}
|
|
6490
7097
|
if (!enabled) return;
|
|
6491
|
-
const { runAgent } = await import("./executor-
|
|
7098
|
+
const { runAgent: runAgent2 } = await import("./executor-DO6QFC6G.js");
|
|
7099
|
+
const resumableRun = getLatestResumableRunForTask(DEFAULT_AGENT_ID, taskName);
|
|
7100
|
+
if (resumableRun) {
|
|
7101
|
+
const resumed = await runAgent2(vaultDir, {
|
|
7102
|
+
agentId: DEFAULT_AGENT_ID,
|
|
7103
|
+
task: taskName,
|
|
7104
|
+
resumeRunId: resumableRun.id,
|
|
7105
|
+
resumeMode: "scheduled",
|
|
7106
|
+
embeddingManager,
|
|
7107
|
+
logger
|
|
7108
|
+
});
|
|
7109
|
+
logger.info(LOG_KINDS.AGENT_RUN, `Scheduled task ${taskName} resumed`, {
|
|
7110
|
+
status: resumed.status,
|
|
7111
|
+
runId: resumed.runId
|
|
7112
|
+
});
|
|
7113
|
+
return;
|
|
7114
|
+
}
|
|
6492
7115
|
const taskConfig = config.agent.tasks?.[taskName];
|
|
6493
7116
|
const projectRoot = resolve(vaultDir, "..");
|
|
6494
|
-
const built = buildTaskInstruction(
|
|
7117
|
+
const built = await buildTaskInstruction(
|
|
7118
|
+
taskName,
|
|
7119
|
+
taskConfig?.params,
|
|
7120
|
+
taskAgentMap.get(taskName),
|
|
7121
|
+
projectRoot,
|
|
7122
|
+
embeddingManager,
|
|
7123
|
+
config,
|
|
7124
|
+
getTeamClient
|
|
7125
|
+
);
|
|
6495
7126
|
if (isInstructionRequiredTask(taskName) && !built) {
|
|
6496
7127
|
logger.info(
|
|
6497
7128
|
LOG_KINDS.AGENT_RUN,
|
|
@@ -6500,11 +7131,12 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6500
7131
|
);
|
|
6501
7132
|
return;
|
|
6502
7133
|
}
|
|
6503
|
-
const result = await
|
|
7134
|
+
const result = await runAgent2(vaultDir, {
|
|
6504
7135
|
task: taskName,
|
|
6505
7136
|
instruction: built?.instruction,
|
|
6506
7137
|
runContext: built?.context,
|
|
6507
|
-
embeddingManager
|
|
7138
|
+
embeddingManager,
|
|
7139
|
+
logger
|
|
6508
7140
|
});
|
|
6509
7141
|
logger.info(LOG_KINDS.AGENT_RUN, `Scheduled task ${taskName} completed`, {
|
|
6510
7142
|
status: result.status,
|
|
@@ -6527,7 +7159,7 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6527
7159
|
link: `/agent?run=${result.runId}`,
|
|
6528
7160
|
metadata: { taskName, runId: result.runId }
|
|
6529
7161
|
}, config);
|
|
6530
|
-
const { countToolCallsByRun } = await import("./turns-
|
|
7162
|
+
const { countToolCallsByRun } = await import("./turns-HU2CTZAP.js");
|
|
6531
7163
|
const counts = countToolCallsByRun(result.runId, ["vault_create_spore", "vault_write_digest"]);
|
|
6532
7164
|
const sporeCount = counts["vault_create_spore"] ?? 0;
|
|
6533
7165
|
const digestCount = counts["vault_write_digest"] ?? 0;
|
|
@@ -6602,32 +7234,171 @@ function listTeamMembers() {
|
|
|
6602
7234
|
).all();
|
|
6603
7235
|
}
|
|
6604
7236
|
|
|
6605
|
-
// src/daemon/
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
}
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
7237
|
+
// src/daemon/plan-capture.ts
|
|
7238
|
+
import { createHash as createHash4 } from "crypto";
|
|
7239
|
+
import os8 from "os";
|
|
7240
|
+
import path18 from "path";
|
|
7241
|
+
function extractTaggedPlans(text, tags) {
|
|
7242
|
+
const results = [];
|
|
7243
|
+
for (const tag of tags) {
|
|
7244
|
+
const regex = new RegExp(`<${tag}>\\n?([\\s\\S]*?)\\n?</${tag}>`, "g");
|
|
7245
|
+
let match;
|
|
7246
|
+
while ((match = regex.exec(text)) !== null) {
|
|
7247
|
+
const content = match[1].trim();
|
|
7248
|
+
if (content) results.push({ tag, content });
|
|
7249
|
+
}
|
|
7250
|
+
}
|
|
7251
|
+
return results;
|
|
7252
|
+
}
|
|
7253
|
+
var FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
7254
|
+
"Write",
|
|
7255
|
+
"Edit",
|
|
7256
|
+
"Create",
|
|
7257
|
+
"write",
|
|
7258
|
+
"edit",
|
|
7259
|
+
"patch",
|
|
7260
|
+
"create"
|
|
7261
|
+
]);
|
|
7262
|
+
var HEADING_REGEX = /^#\s+(.+)$/m;
|
|
7263
|
+
var TRANSCRIPT_SOURCE_TITLE_PREFIX = `${TRANSCRIPT_SOURCE_PREFIX}`;
|
|
7264
|
+
function resolvePlanWatchDir(watchDir, projectRoot) {
|
|
7265
|
+
const expanded = watchDir.startsWith("~/") ? path18.join(os8.homedir(), watchDir.slice(2)) : watchDir;
|
|
7266
|
+
return path18.isAbsolute(expanded) ? expanded : path18.resolve(projectRoot, expanded);
|
|
7267
|
+
}
|
|
7268
|
+
function isInPlanDirectory(filePath, watchDirs, projectRoot) {
|
|
7269
|
+
const abs = path18.isAbsolute(filePath) ? filePath : path18.resolve(projectRoot, filePath);
|
|
7270
|
+
return watchDirs.some((dir) => {
|
|
7271
|
+
const absDir = resolvePlanWatchDir(dir, projectRoot);
|
|
7272
|
+
const prefix = absDir.endsWith(path18.sep) ? absDir : absDir + path18.sep;
|
|
7273
|
+
return abs === absDir || abs.startsWith(prefix);
|
|
7274
|
+
});
|
|
7275
|
+
}
|
|
7276
|
+
function isPlanWriteEvent(toolName, toolInput, config) {
|
|
7277
|
+
if (!FILE_WRITE_TOOLS.has(toolName)) return null;
|
|
7278
|
+
const filePath = toolInput?.file_path ?? toolInput?.path ?? toolInput?.filePath;
|
|
7279
|
+
if (typeof filePath !== "string") return null;
|
|
7280
|
+
if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;
|
|
7281
|
+
if (config.extensions?.length) {
|
|
7282
|
+
const ext = path18.extname(filePath).toLowerCase();
|
|
7283
|
+
if (!config.extensions.includes(ext)) return null;
|
|
7284
|
+
}
|
|
7285
|
+
return filePath;
|
|
7286
|
+
}
|
|
7287
|
+
function parsePlanTitle(content, filename) {
|
|
7288
|
+
const match = HEADING_REGEX.exec(content);
|
|
7289
|
+
if (match) return match[1].trim();
|
|
7290
|
+
return filename ?? null;
|
|
7291
|
+
}
|
|
7292
|
+
function normalizePlanTags(tags) {
|
|
7293
|
+
if (tags === void 0 || tags === null) return null;
|
|
7294
|
+
return Array.isArray(tags) ? tags.join(", ") : tags;
|
|
7295
|
+
}
|
|
7296
|
+
function fileTitleFromSourcePath(sourcePath) {
|
|
7297
|
+
if (!sourcePath || sourcePath.startsWith(TRANSCRIPT_SOURCE_TITLE_PREFIX)) return null;
|
|
7298
|
+
return path18.basename(sourcePath);
|
|
7299
|
+
}
|
|
7300
|
+
function resolvePlanTitle(input) {
|
|
7301
|
+
const explicitTitle = input.title?.trim();
|
|
7302
|
+
if (explicitTitle) return explicitTitle;
|
|
7303
|
+
const headingTitle = parsePlanTitle(input.content);
|
|
7304
|
+
if (headingTitle) return headingTitle;
|
|
7305
|
+
const sourcePathTitle = fileTitleFromSourcePath(input.sourcePath);
|
|
7306
|
+
if (sourcePathTitle) return sourcePathTitle;
|
|
7307
|
+
return input.planKey ? humanizePlanToken(input.planKey) : null;
|
|
7308
|
+
}
|
|
7309
|
+
function persistPlan(input) {
|
|
7310
|
+
const createdAt = input.createdAt ?? Math.floor(Date.now() / 1e3);
|
|
7311
|
+
const updatedAt = input.updatedAt ?? createdAt;
|
|
7312
|
+
const contentHash = createHash4(CONTENT_HASH_ALGORITHM).update(input.content).digest("hex");
|
|
7313
|
+
const existingPlan = getPlanByLogicalKey(input.logicalKey);
|
|
7314
|
+
const status = input.status ?? existingPlan?.status ?? "active";
|
|
7315
|
+
const promptBatchId = input.promptBatchId === void 0 ? existingPlan?.prompt_batch_id ?? null : input.promptBatchId;
|
|
7316
|
+
const resolvedTitle = resolvePlanTitle({
|
|
7317
|
+
content: input.content,
|
|
7318
|
+
title: input.title,
|
|
7319
|
+
sourcePath: input.sourcePath,
|
|
7320
|
+
planKey: input.planKey
|
|
7321
|
+
});
|
|
7322
|
+
if (existingPlan && existingPlan.content_hash === contentHash && existingPlan.title === resolvedTitle && existingPlan.status === status) {
|
|
7323
|
+
return existingPlan;
|
|
7324
|
+
}
|
|
7325
|
+
if (existingPlan && existingPlan.content_hash !== contentHash) {
|
|
7326
|
+
const priorSource = existingPlan.source_path ?? null;
|
|
7327
|
+
const newSource = input.sourcePath ?? null;
|
|
7328
|
+
if (priorSource !== newSource) {
|
|
7329
|
+
input.logger?.warn(LOG_KINDS.CAPTURE_PLAN, "Plan overwritten mid-session", {
|
|
7330
|
+
logical_key: input.logicalKey,
|
|
7331
|
+
session_id: input.sessionId,
|
|
7332
|
+
prior_source: priorSource,
|
|
7333
|
+
new_source: newSource,
|
|
7334
|
+
prior_updated_at: existingPlan.updated_at
|
|
7335
|
+
});
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
return upsertPlan({
|
|
7339
|
+
id: buildPlanId(input.logicalKey),
|
|
7340
|
+
logical_key: input.logicalKey,
|
|
7341
|
+
title: resolvedTitle,
|
|
7342
|
+
content: input.content,
|
|
7343
|
+
source_path: input.sourcePath ?? null,
|
|
7344
|
+
tags: normalizePlanTags(input.tags),
|
|
7345
|
+
session_id: input.sessionId,
|
|
7346
|
+
prompt_batch_id: promptBatchId,
|
|
7347
|
+
content_hash: contentHash,
|
|
7348
|
+
status,
|
|
7349
|
+
created_at: createdAt,
|
|
7350
|
+
updated_at: updatedAt
|
|
7351
|
+
});
|
|
7352
|
+
}
|
|
7353
|
+
function capturePlan(input) {
|
|
7354
|
+
const normalizedSourcePath = normalizePlanSourcePath(input.sourcePath, input.projectRoot);
|
|
7355
|
+
return persistPlan({
|
|
7356
|
+
sessionId: input.sessionId,
|
|
7357
|
+
content: input.content,
|
|
7358
|
+
logicalKey: buildPathPlanLogicalKey(normalizedSourcePath),
|
|
7359
|
+
sourcePath: normalizedSourcePath,
|
|
7360
|
+
promptBatchId: input.promptBatchId,
|
|
7361
|
+
logger: input.logger
|
|
7362
|
+
});
|
|
7363
|
+
}
|
|
7364
|
+
function captureTaggedPlan(input) {
|
|
7365
|
+
return persistPlan({
|
|
7366
|
+
sessionId: input.sessionId,
|
|
7367
|
+
content: input.content,
|
|
7368
|
+
logicalKey: buildSessionTagPlanLogicalKey(input.sessionId, input.tag),
|
|
7369
|
+
sourcePath: `${TRANSCRIPT_SOURCE_PREFIX}${input.tag}`,
|
|
7370
|
+
promptBatchId: input.promptBatchId,
|
|
7371
|
+
planKey: input.tag,
|
|
7372
|
+
logger: input.logger
|
|
7373
|
+
});
|
|
7374
|
+
}
|
|
7375
|
+
|
|
7376
|
+
// src/daemon/api/mcp-proxy.ts
|
|
7377
|
+
var SPORE_ID_RANDOM_BYTES = 4;
|
|
7378
|
+
var RESOLUTION_ID_RANDOM_BYTES = 8;
|
|
7379
|
+
var MIN_CONSOLIDATE_SOURCES = 2;
|
|
7380
|
+
var RememberBody = external_exports.object({
|
|
7381
|
+
content: external_exports.string(),
|
|
7382
|
+
type: external_exports.string().optional(),
|
|
7383
|
+
tags: external_exports.array(external_exports.string()).optional()
|
|
7384
|
+
});
|
|
7385
|
+
var SupersedeBody = external_exports.object({
|
|
7386
|
+
old_spore_id: external_exports.string(),
|
|
7387
|
+
new_spore_id: external_exports.string(),
|
|
7388
|
+
reason: external_exports.string().optional()
|
|
7389
|
+
});
|
|
7390
|
+
function isoToEpochSeconds(iso) {
|
|
7391
|
+
const ms = Date.parse(iso);
|
|
7392
|
+
return Number.isNaN(ms) ? void 0 : Math.floor(ms / 1e3);
|
|
7393
|
+
}
|
|
7394
|
+
function registerMcpUserAgent(createdAt) {
|
|
7395
|
+
registerAgent({
|
|
7396
|
+
id: USER_AGENT_ID,
|
|
7397
|
+
name: USER_AGENT_NAME,
|
|
7398
|
+
created_at: createdAt
|
|
7399
|
+
});
|
|
7400
|
+
}
|
|
7401
|
+
function toPlanProgress(content) {
|
|
6631
7402
|
const planContent = content ?? "";
|
|
6632
7403
|
const checked = (planContent.match(/- \[x\]/gi) ?? []).length;
|
|
6633
7404
|
const unchecked = (planContent.match(/- \[ \]/g) ?? []).length;
|
|
@@ -6644,8 +7415,20 @@ var ConsolidateBody = external_exports.object({
|
|
|
6644
7415
|
tags: external_exports.array(external_exports.string()).optional(),
|
|
6645
7416
|
reason: external_exports.string().optional()
|
|
6646
7417
|
});
|
|
7418
|
+
var SavePlanBody = external_exports.object({
|
|
7419
|
+
session_id: external_exports.string(),
|
|
7420
|
+
content: external_exports.string().min(1),
|
|
7421
|
+
source_path: external_exports.string().min(1).optional(),
|
|
7422
|
+
plan_key: external_exports.string().min(1).optional(),
|
|
7423
|
+
title: external_exports.string().min(1).optional(),
|
|
7424
|
+
status: external_exports.enum(PLAN_STATUSES).optional(),
|
|
7425
|
+
tags: external_exports.array(external_exports.string()).optional()
|
|
7426
|
+
}).refine(
|
|
7427
|
+
(value) => Boolean(value.source_path) !== Boolean(value.plan_key),
|
|
7428
|
+
{ message: "Provide exactly one of source_path or plan_key" }
|
|
7429
|
+
);
|
|
6647
7430
|
function createMcpProxyHandlers(deps) {
|
|
6648
|
-
const { machineId, embeddingManager } = deps;
|
|
7431
|
+
const { machineId, embeddingManager, projectRoot, logger } = deps;
|
|
6649
7432
|
function toPlanSummary(row) {
|
|
6650
7433
|
return {
|
|
6651
7434
|
id: row.id,
|
|
@@ -6769,8 +7552,46 @@ function createMcpProxyHandlers(deps) {
|
|
|
6769
7552
|
}
|
|
6770
7553
|
};
|
|
6771
7554
|
}
|
|
7555
|
+
async function handleSavePlan(req) {
|
|
7556
|
+
const { session_id, content, source_path, plan_key, title, status, tags } = SavePlanBody.parse(req.body);
|
|
7557
|
+
const session = getSession(session_id);
|
|
7558
|
+
if (!session) return { status: 404, body: errorBody("session-not-found", "Session not found") };
|
|
7559
|
+
const openBatch = getLatestOpenBatch(session_id);
|
|
7560
|
+
const normalizedSourcePath = source_path ? normalizePlanSourcePath(source_path, projectRoot) : null;
|
|
7561
|
+
const logicalKey = normalizedSourcePath ? buildPathPlanLogicalKey(normalizedSourcePath) : buildSessionPlanLogicalKey(session_id, plan_key);
|
|
7562
|
+
const row = persistPlan({
|
|
7563
|
+
sessionId: session_id,
|
|
7564
|
+
content,
|
|
7565
|
+
logicalKey,
|
|
7566
|
+
sourcePath: normalizedSourcePath,
|
|
7567
|
+
promptBatchId: openBatch?.id,
|
|
7568
|
+
title,
|
|
7569
|
+
status,
|
|
7570
|
+
tags,
|
|
7571
|
+
planKey: plan_key ?? null,
|
|
7572
|
+
logger
|
|
7573
|
+
});
|
|
7574
|
+
return {
|
|
7575
|
+
body: {
|
|
7576
|
+
id: row.id,
|
|
7577
|
+
logical_key: row.logical_key,
|
|
7578
|
+
title: row.title,
|
|
7579
|
+
status: row.status,
|
|
7580
|
+
source_path: row.source_path,
|
|
7581
|
+
session_id: row.session_id,
|
|
7582
|
+
prompt_batch_id: row.prompt_batch_id,
|
|
7583
|
+
tags: toPlanTags(row.tags),
|
|
7584
|
+
created_at: row.created_at,
|
|
7585
|
+
updated_at: row.updated_at
|
|
7586
|
+
}
|
|
7587
|
+
};
|
|
7588
|
+
}
|
|
6772
7589
|
async function handlePlans(req) {
|
|
6773
7590
|
const id = typeof req.query.id === "string" ? req.query.id : void 0;
|
|
7591
|
+
const session = typeof req.query.session === "string" ? req.query.session : void 0;
|
|
7592
|
+
if (id && session) {
|
|
7593
|
+
return { status: 400, body: errorBody("mutually-exclusive-query", "Pass either id or session, not both") };
|
|
7594
|
+
}
|
|
6774
7595
|
if (id) {
|
|
6775
7596
|
const row = getPlan(id);
|
|
6776
7597
|
if (!row) return { body: { plans: [] } };
|
|
@@ -6783,6 +7604,11 @@ function createMcpProxyHandlers(deps) {
|
|
|
6783
7604
|
}
|
|
6784
7605
|
};
|
|
6785
7606
|
}
|
|
7607
|
+
if (session) {
|
|
7608
|
+
const rows2 = listPlansBySession(session);
|
|
7609
|
+
const plans2 = rows2.map(toPlanSummary);
|
|
7610
|
+
return { body: { plans: plans2 } };
|
|
7611
|
+
}
|
|
6786
7612
|
const statusFilter = req.query.status === "all" ? void 0 : req.query.status;
|
|
6787
7613
|
const limit = req.query.limit ? Number(req.query.limit) : void 0;
|
|
6788
7614
|
const rows = listPlans({ status: statusFilter, limit });
|
|
@@ -6836,6 +7662,7 @@ function createMcpProxyHandlers(deps) {
|
|
|
6836
7662
|
handleSupersede,
|
|
6837
7663
|
handleConsolidate,
|
|
6838
7664
|
handlePlans,
|
|
7665
|
+
handleSavePlan,
|
|
6839
7666
|
handleSessions,
|
|
6840
7667
|
handleTeam
|
|
6841
7668
|
};
|
|
@@ -6843,16 +7670,389 @@ function createMcpProxyHandlers(deps) {
|
|
|
6843
7670
|
|
|
6844
7671
|
// src/daemon/api/agent-runs.ts
|
|
6845
7672
|
import { resolve as resolve2 } from "path";
|
|
7673
|
+
|
|
7674
|
+
// src/services/phase-audit.ts
|
|
7675
|
+
function isStoredUsageData(value) {
|
|
7676
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7677
|
+
}
|
|
7678
|
+
function parseUsageData(raw) {
|
|
7679
|
+
return tryParseJson(raw, isStoredUsageData) ?? {};
|
|
7680
|
+
}
|
|
7681
|
+
function normalizeStatus(raw) {
|
|
7682
|
+
if (raw === "completed" || raw === "failed" || raw === "skipped") return raw;
|
|
7683
|
+
return "pending";
|
|
7684
|
+
}
|
|
7685
|
+
function aggregateTurnCounts(turns) {
|
|
7686
|
+
const toolCalls = {};
|
|
7687
|
+
for (const turn of turns) {
|
|
7688
|
+
toolCalls[turn.tool_name] = (toolCalls[turn.tool_name] ?? 0) + 1;
|
|
7689
|
+
}
|
|
7690
|
+
const toolErrors = {};
|
|
7691
|
+
return [toolCalls, toolErrors];
|
|
7692
|
+
}
|
|
7693
|
+
function aggregateWriteIntents(intents) {
|
|
7694
|
+
const byPhase = {};
|
|
7695
|
+
const unattributedByTool = {};
|
|
7696
|
+
const totalByTool = {};
|
|
7697
|
+
for (const intent of intents) {
|
|
7698
|
+
const tool = intent.tool_name;
|
|
7699
|
+
totalByTool[tool] = (totalByTool[tool] ?? 0) + 1;
|
|
7700
|
+
if (intent.phase_id) {
|
|
7701
|
+
if (!byPhase[intent.phase_id]) byPhase[intent.phase_id] = {};
|
|
7702
|
+
byPhase[intent.phase_id][tool] = (byPhase[intent.phase_id][tool] ?? 0) + 1;
|
|
7703
|
+
} else {
|
|
7704
|
+
unattributedByTool[tool] = (unattributedByTool[tool] ?? 0) + 1;
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
return {
|
|
7708
|
+
total: intents.length,
|
|
7709
|
+
byPhase,
|
|
7710
|
+
unattributedByTool,
|
|
7711
|
+
totalByTool
|
|
7712
|
+
};
|
|
7713
|
+
}
|
|
7714
|
+
function serializeReports(reports) {
|
|
7715
|
+
return reports.map((r) => ({
|
|
7716
|
+
action: r.action,
|
|
7717
|
+
summary: r.summary ?? null,
|
|
7718
|
+
details: r.details ?? null,
|
|
7719
|
+
createdAt: r.created_at
|
|
7720
|
+
}));
|
|
7721
|
+
}
|
|
7722
|
+
function buildPhaseAudit(runId) {
|
|
7723
|
+
const run = getRun(runId);
|
|
7724
|
+
if (!run) return null;
|
|
7725
|
+
const dryRun = run.dry_run;
|
|
7726
|
+
const usageData = parseUsageData(run.usage_data);
|
|
7727
|
+
const checkpointState = parseCheckpointState(run.checkpoints);
|
|
7728
|
+
const usageByName = new Map(
|
|
7729
|
+
(usageData.phases ?? []).map((p) => [p.name, p])
|
|
7730
|
+
);
|
|
7731
|
+
const checkpointByName = new Map(
|
|
7732
|
+
Object.entries(checkpointState.phases).map(([key, cp]) => [
|
|
7733
|
+
cp.name ?? key,
|
|
7734
|
+
cp
|
|
7735
|
+
])
|
|
7736
|
+
);
|
|
7737
|
+
const phaseNames = [];
|
|
7738
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7739
|
+
for (const p of usageData.phases ?? []) {
|
|
7740
|
+
if (!seen.has(p.name)) {
|
|
7741
|
+
phaseNames.push(p.name);
|
|
7742
|
+
seen.add(p.name);
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7745
|
+
for (const name of checkpointByName.keys()) {
|
|
7746
|
+
if (!seen.has(name)) {
|
|
7747
|
+
phaseNames.push(name);
|
|
7748
|
+
seen.add(name);
|
|
7749
|
+
}
|
|
7750
|
+
}
|
|
7751
|
+
const reports = listReports(runId);
|
|
7752
|
+
const turns = listTurnsByRun(runId);
|
|
7753
|
+
const [toolCalls, toolErrors] = aggregateTurnCounts(turns);
|
|
7754
|
+
const serializedReports = serializeReports(reports);
|
|
7755
|
+
let intentSummary = null;
|
|
7756
|
+
if (dryRun) {
|
|
7757
|
+
const intents = listWriteIntentTools(runId);
|
|
7758
|
+
intentSummary = aggregateWriteIntents(intents);
|
|
7759
|
+
}
|
|
7760
|
+
if (phaseNames.length === 0 && turns.length > 0) {
|
|
7761
|
+
const runStatus = normalizeStatus(run.status);
|
|
7762
|
+
const durationMs = runDurationMs(run);
|
|
7763
|
+
let writeIntents = null;
|
|
7764
|
+
if (intentSummary) {
|
|
7765
|
+
writeIntents = {
|
|
7766
|
+
total: intentSummary.total,
|
|
7767
|
+
byTool: intentSummary.totalByTool
|
|
7768
|
+
};
|
|
7769
|
+
}
|
|
7770
|
+
const summary = run.status === "failed" ? run.error ?? null : null;
|
|
7771
|
+
return {
|
|
7772
|
+
runId,
|
|
7773
|
+
taskName: run.task ?? null,
|
|
7774
|
+
dryRun,
|
|
7775
|
+
phases: [
|
|
7776
|
+
{
|
|
7777
|
+
phaseName: "run",
|
|
7778
|
+
status: runStatus,
|
|
7779
|
+
summary,
|
|
7780
|
+
turnsUsed: turns.length,
|
|
7781
|
+
maxTurns: null,
|
|
7782
|
+
tokensUsed: run.tokens_used ?? 0,
|
|
7783
|
+
costUsd: run.cost_usd ?? null,
|
|
7784
|
+
costSource: run.cost_source ?? null,
|
|
7785
|
+
durationMs,
|
|
7786
|
+
startedAt: run.started_at,
|
|
7787
|
+
completedAt: run.completed_at,
|
|
7788
|
+
skipReason: null,
|
|
7789
|
+
toolCalls,
|
|
7790
|
+
toolErrors,
|
|
7791
|
+
writeIntents,
|
|
7792
|
+
reports: serializedReports
|
|
7793
|
+
}
|
|
7794
|
+
]
|
|
7795
|
+
};
|
|
7796
|
+
}
|
|
7797
|
+
const phases = phaseNames.map((phaseName) => {
|
|
7798
|
+
const usage = usageByName.get(phaseName);
|
|
7799
|
+
const cp = checkpointByName.get(phaseName);
|
|
7800
|
+
const status = normalizeStatus(cp?.status ?? (usage ? "pending" : "pending"));
|
|
7801
|
+
const tokensUsed = cp?.tokensUsed ?? usage?.tokensUsed ?? usage?.usage?.totalTokens ?? 0;
|
|
7802
|
+
const costUsd = cp?.costUsd !== void 0 ? cp.costUsd : usage?.costUsd !== void 0 ? usage.costUsd : null;
|
|
7803
|
+
const costSource = cp?.costSource ?? usage?.costSource ?? null;
|
|
7804
|
+
const durationMs = usage?.usage?.durationMs ?? null;
|
|
7805
|
+
const completedAt = status === "completed" || status === "failed" ? cp?.updatedAt ?? null : null;
|
|
7806
|
+
const startedAt = null;
|
|
7807
|
+
const skipReason = null;
|
|
7808
|
+
let writeIntents = null;
|
|
7809
|
+
if (intentSummary) {
|
|
7810
|
+
const phaseByTool = intentSummary.byPhase[phaseName] ?? {};
|
|
7811
|
+
writeIntents = {
|
|
7812
|
+
// Phase total = sum of tool calls attributed to this phase
|
|
7813
|
+
total: Object.values(phaseByTool).reduce((sum, n) => sum + n, 0),
|
|
7814
|
+
byTool: phaseByTool
|
|
7815
|
+
};
|
|
7816
|
+
}
|
|
7817
|
+
return {
|
|
7818
|
+
phaseName,
|
|
7819
|
+
status,
|
|
7820
|
+
summary: cp?.summary ?? null,
|
|
7821
|
+
turnsUsed: cp?.turnsUsed ?? 0,
|
|
7822
|
+
maxTurns: null,
|
|
7823
|
+
// Not persisted — see module JSDoc
|
|
7824
|
+
tokensUsed,
|
|
7825
|
+
costUsd,
|
|
7826
|
+
costSource,
|
|
7827
|
+
durationMs,
|
|
7828
|
+
startedAt,
|
|
7829
|
+
completedAt,
|
|
7830
|
+
skipReason,
|
|
7831
|
+
toolCalls,
|
|
7832
|
+
// run-level aggregate (no per-phase turn attribution)
|
|
7833
|
+
toolErrors,
|
|
7834
|
+
// always empty — no error flag on turn rows
|
|
7835
|
+
writeIntents,
|
|
7836
|
+
reports: serializedReports
|
|
7837
|
+
// all reports; no phase column exists
|
|
7838
|
+
};
|
|
7839
|
+
});
|
|
7840
|
+
return {
|
|
7841
|
+
runId,
|
|
7842
|
+
taskName: run.task ?? null,
|
|
7843
|
+
dryRun,
|
|
7844
|
+
phases
|
|
7845
|
+
};
|
|
7846
|
+
}
|
|
7847
|
+
|
|
7848
|
+
// src/daemon/api/schemas/execution-overrides.ts
|
|
7849
|
+
var ReasoningLevelEnum = external_exports.enum(["low", "default", "high"]);
|
|
7850
|
+
var RuntimeIdEnum = external_exports.enum(["claude-sdk", "openai-agents"]);
|
|
7851
|
+
var ProviderTypeEnum = external_exports.enum([
|
|
7852
|
+
"anthropic",
|
|
7853
|
+
"ollama",
|
|
7854
|
+
"lmstudio",
|
|
7855
|
+
"openai",
|
|
7856
|
+
"openrouter",
|
|
7857
|
+
"openai-compatible"
|
|
7858
|
+
]);
|
|
7859
|
+
var ProviderOverrideWireSchema = external_exports.object({
|
|
7860
|
+
runtime: RuntimeIdEnum.optional(),
|
|
7861
|
+
type: ProviderTypeEnum,
|
|
7862
|
+
localBackend: external_exports.enum(["ollama", "lmstudio"]).optional(),
|
|
7863
|
+
baseUrl: external_exports.string().optional(),
|
|
7864
|
+
model: external_exports.string().optional(),
|
|
7865
|
+
reasoningMap: external_exports.object({
|
|
7866
|
+
low: external_exports.string().optional(),
|
|
7867
|
+
default: external_exports.string().optional(),
|
|
7868
|
+
high: external_exports.string().optional()
|
|
7869
|
+
}).optional(),
|
|
7870
|
+
contextLength: external_exports.number().int().positive().optional()
|
|
7871
|
+
});
|
|
7872
|
+
var PhaseExecutionOverrideBody = external_exports.object({
|
|
7873
|
+
reasoningLevel: ReasoningLevelEnum.optional(),
|
|
7874
|
+
model: external_exports.string().optional(),
|
|
7875
|
+
provider: ProviderOverrideWireSchema.optional(),
|
|
7876
|
+
maxTurns: external_exports.number().int().positive().optional()
|
|
7877
|
+
});
|
|
7878
|
+
var ExecutionOverrideBody = external_exports.object({
|
|
7879
|
+
runtime: RuntimeIdEnum.optional(),
|
|
7880
|
+
reasoningLevel: ReasoningLevelEnum.optional(),
|
|
7881
|
+
model: external_exports.string().optional(),
|
|
7882
|
+
provider: ProviderOverrideWireSchema.optional(),
|
|
7883
|
+
phases: external_exports.record(external_exports.string(), PhaseExecutionOverrideBody).optional()
|
|
7884
|
+
}).optional();
|
|
7885
|
+
|
|
7886
|
+
// src/daemon/api/schemas/execution-overrides-traversal.ts
|
|
7887
|
+
function transformProviderOverrides(overrides, transform) {
|
|
7888
|
+
if (overrides === null || overrides === void 0) {
|
|
7889
|
+
return overrides ?? null;
|
|
7890
|
+
}
|
|
7891
|
+
if (typeof overrides !== "object") return overrides;
|
|
7892
|
+
const cloned = { ...overrides };
|
|
7893
|
+
if (isPlainObject(cloned.provider)) {
|
|
7894
|
+
const next = transform({ ...cloned.provider });
|
|
7895
|
+
if (next === null) {
|
|
7896
|
+
delete cloned.provider;
|
|
7897
|
+
} else {
|
|
7898
|
+
cloned.provider = next;
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
if (isPlainObject(cloned.phases)) {
|
|
7902
|
+
const nextPhases = {};
|
|
7903
|
+
for (const [name, phase] of Object.entries(cloned.phases)) {
|
|
7904
|
+
if (!isPlainObject(phase)) {
|
|
7905
|
+
nextPhases[name] = phase;
|
|
7906
|
+
continue;
|
|
7907
|
+
}
|
|
7908
|
+
const clonedPhase = { ...phase };
|
|
7909
|
+
if (isPlainObject(clonedPhase.provider)) {
|
|
7910
|
+
const nextProvider = transform({ ...clonedPhase.provider });
|
|
7911
|
+
if (nextProvider === null) {
|
|
7912
|
+
delete clonedPhase.provider;
|
|
7913
|
+
} else {
|
|
7914
|
+
clonedPhase.provider = nextProvider;
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
7917
|
+
nextPhases[name] = clonedPhase;
|
|
7918
|
+
}
|
|
7919
|
+
cloned.phases = nextPhases;
|
|
7920
|
+
}
|
|
7921
|
+
return cloned;
|
|
7922
|
+
}
|
|
7923
|
+
function isPlainObject(value) {
|
|
7924
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7925
|
+
}
|
|
7926
|
+
|
|
7927
|
+
// src/daemon/api/run-serializer.ts
|
|
7928
|
+
function buildPhaseCheckpointSummary(checkpointsRaw, logger) {
|
|
7929
|
+
if (!checkpointsRaw) return [];
|
|
7930
|
+
try {
|
|
7931
|
+
const parsed = JSON.parse(checkpointsRaw);
|
|
7932
|
+
return Object.entries(parsed.phases ?? {}).map(([name, phase]) => ({
|
|
7933
|
+
name: phase.name ?? name,
|
|
7934
|
+
status: phase.status ?? "pending",
|
|
7935
|
+
updatedAt: phase.updatedAt ?? 0,
|
|
7936
|
+
...phase.tokensUsed !== void 0 ? { tokensUsed: phase.tokensUsed } : {},
|
|
7937
|
+
...phase.costUsd !== void 0 ? { costUsd: phase.costUsd } : {},
|
|
7938
|
+
...phase.costSource !== void 0 ? { costSource: phase.costSource } : {}
|
|
7939
|
+
}));
|
|
7940
|
+
} catch (err) {
|
|
7941
|
+
const detail = errorMessage(err);
|
|
7942
|
+
logger?.warn("run-serializer.checkpoints-parse-failed", "checkpoints JSON parse failed", { error: detail });
|
|
7943
|
+
return [];
|
|
7944
|
+
}
|
|
7945
|
+
}
|
|
7946
|
+
function scrubExecutionOverrides(overrides) {
|
|
7947
|
+
if (!overrides || typeof overrides !== "object") return overrides;
|
|
7948
|
+
const topLevelStripped = { ...overrides };
|
|
7949
|
+
if ("apiKey" in topLevelStripped) delete topLevelStripped.apiKey;
|
|
7950
|
+
return transformProviderOverrides(topLevelStripped, stripApiKey);
|
|
7951
|
+
}
|
|
7952
|
+
function stripApiKey(provider) {
|
|
7953
|
+
const cloned = { ...provider };
|
|
7954
|
+
if ("apiKey" in cloned) delete cloned.apiKey;
|
|
7955
|
+
return cloned;
|
|
7956
|
+
}
|
|
7957
|
+
function serializeRun(run, opts = {}) {
|
|
7958
|
+
const {
|
|
7959
|
+
includeResumeFields = true,
|
|
7960
|
+
includePhaseCheckpoints = true,
|
|
7961
|
+
writeIntents,
|
|
7962
|
+
duration_ms,
|
|
7963
|
+
logger
|
|
7964
|
+
} = opts;
|
|
7965
|
+
const base = {
|
|
7966
|
+
id: run.id,
|
|
7967
|
+
agent_id: run.agent_id,
|
|
7968
|
+
task: run.task,
|
|
7969
|
+
instruction: run.instruction,
|
|
7970
|
+
status: run.status,
|
|
7971
|
+
runtime: run.runtime,
|
|
7972
|
+
provider: run.provider,
|
|
7973
|
+
model: run.model,
|
|
7974
|
+
session_ref: run.session_ref,
|
|
7975
|
+
started_at: run.started_at,
|
|
7976
|
+
completed_at: run.completed_at,
|
|
7977
|
+
tokens_used: run.tokens_used,
|
|
7978
|
+
cost_usd: run.cost_usd,
|
|
7979
|
+
actual_cost_usd: run.actual_cost_usd,
|
|
7980
|
+
estimated_cost_usd: run.estimated_cost_usd,
|
|
7981
|
+
cost_source: run.cost_source,
|
|
7982
|
+
cost_data: run.cost_data,
|
|
7983
|
+
actions_taken: run.actions_taken,
|
|
7984
|
+
usage_data: run.usage_data,
|
|
7985
|
+
error: run.error,
|
|
7986
|
+
dry_run: run.dry_run,
|
|
7987
|
+
evaluation_id: run.evaluation_id,
|
|
7988
|
+
reasoning_level: run.reasoning_level,
|
|
7989
|
+
// Strip `apiKey` from historical rows defensively — before this PR the
|
|
7990
|
+
// API accepted apiKey in executionOverrides and stored it unmasked.
|
|
7991
|
+
execution_overrides: scrubExecutionOverrides(run.execution_overrides)
|
|
7992
|
+
};
|
|
7993
|
+
return {
|
|
7994
|
+
...base,
|
|
7995
|
+
...includeResumeFields ? {
|
|
7996
|
+
resumable: run.resumable === 1,
|
|
7997
|
+
resume_status: run.resume_status,
|
|
7998
|
+
resume_mode: run.resume_mode,
|
|
7999
|
+
resumed_at: run.resumed_at,
|
|
8000
|
+
checkpoints: run.checkpoints
|
|
8001
|
+
} : {},
|
|
8002
|
+
...includePhaseCheckpoints ? { phase_checkpoints: buildPhaseCheckpointSummary(run.checkpoints, logger) } : {},
|
|
8003
|
+
...writeIntents !== void 0 && writeIntents !== null ? { write_intents: writeIntents } : {},
|
|
8004
|
+
...duration_ms !== void 0 ? { duration_ms } : {}
|
|
8005
|
+
};
|
|
8006
|
+
}
|
|
8007
|
+
|
|
8008
|
+
// src/daemon/api/agent-runs.ts
|
|
6846
8009
|
var AGENT_RUNS_DEFAULT_LIMIT = 50;
|
|
8010
|
+
var REMOTE_PROVIDER_TYPES = /* @__PURE__ */ new Set(["openai", "openrouter"]);
|
|
8011
|
+
function stripBaseUrlForRemoteProviders(provider) {
|
|
8012
|
+
const type = provider.type;
|
|
8013
|
+
if (typeof type === "string" && REMOTE_PROVIDER_TYPES.has(type)) {
|
|
8014
|
+
const { baseUrl: _dropped, ...rest } = provider;
|
|
8015
|
+
return rest;
|
|
8016
|
+
}
|
|
8017
|
+
return provider;
|
|
8018
|
+
}
|
|
8019
|
+
function sanitizeExecutionOverrides(overrides) {
|
|
8020
|
+
if (!overrides) return overrides;
|
|
8021
|
+
const result = transformProviderOverrides(
|
|
8022
|
+
overrides,
|
|
8023
|
+
stripBaseUrlForRemoteProviders
|
|
8024
|
+
);
|
|
8025
|
+
return result;
|
|
8026
|
+
}
|
|
6847
8027
|
var AgentRunBody = external_exports.object({
|
|
6848
8028
|
task: external_exports.string().optional(),
|
|
6849
8029
|
instruction: external_exports.string().optional(),
|
|
6850
|
-
agentId: external_exports.string().optional()
|
|
8030
|
+
agentId: external_exports.string().optional(),
|
|
8031
|
+
/**
|
|
8032
|
+
* Run in dry-run mode — writes intercepted by the tool surface and
|
|
8033
|
+
* recorded to `agent_run_write_intents` instead of mutating the vault.
|
|
8034
|
+
*/
|
|
8035
|
+
dryRun: external_exports.boolean().optional(),
|
|
8036
|
+
/** Evaluation matrix this run belongs to, if any. */
|
|
8037
|
+
evaluationId: external_exports.string().nullable().optional(),
|
|
8038
|
+
/** Per-run runtime/reasoning/model overrides; also per-phase overrides. */
|
|
8039
|
+
executionOverrides: ExecutionOverrideBody
|
|
8040
|
+
});
|
|
8041
|
+
var ResumeRunBody = external_exports.object({
|
|
8042
|
+
mode: external_exports.enum(["manual", "scheduled"]).optional()
|
|
6851
8043
|
});
|
|
6852
8044
|
function createAgentRunHandlers(deps) {
|
|
6853
|
-
const { vaultDir, embeddingManager, logger } = deps;
|
|
8045
|
+
const { vaultDir, embeddingManager, logger, getTeamClient } = deps;
|
|
6854
8046
|
async function handleRun(req) {
|
|
6855
|
-
const {
|
|
8047
|
+
const {
|
|
8048
|
+
task,
|
|
8049
|
+
instruction: rawInstruction,
|
|
8050
|
+
agentId,
|
|
8051
|
+
dryRun,
|
|
8052
|
+
evaluationId,
|
|
8053
|
+
executionOverrides: rawExecutionOverrides
|
|
8054
|
+
} = AgentRunBody.parse(req.body);
|
|
8055
|
+
const executionOverrides = sanitizeExecutionOverrides(rawExecutionOverrides);
|
|
6856
8056
|
const mycoConfig = loadMergedConfig(vaultDir);
|
|
6857
8057
|
if (!hasConfiguredProvider(mycoConfig, task)) {
|
|
6858
8058
|
return {
|
|
@@ -6870,10 +8070,10 @@ function createAgentRunHandlers(deps) {
|
|
|
6870
8070
|
try {
|
|
6871
8071
|
const taskParams = mycoConfig.agent.tasks?.[task]?.params;
|
|
6872
8072
|
const projectRoot = resolve2(vaultDir, "..");
|
|
6873
|
-
built = buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager);
|
|
8073
|
+
built = await buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
|
|
6874
8074
|
} catch {
|
|
6875
8075
|
const projectRoot = resolve2(vaultDir, "..");
|
|
6876
|
-
built = buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager);
|
|
8076
|
+
built = await buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
|
|
6877
8077
|
}
|
|
6878
8078
|
instruction = built?.instruction;
|
|
6879
8079
|
runContext = built?.context;
|
|
@@ -6888,13 +8088,17 @@ function createAgentRunHandlers(deps) {
|
|
|
6888
8088
|
};
|
|
6889
8089
|
}
|
|
6890
8090
|
}
|
|
6891
|
-
const { runAgent } = await import("./executor-
|
|
6892
|
-
const resultPromise =
|
|
8091
|
+
const { runAgent: runAgent2 } = await import("./executor-DO6QFC6G.js");
|
|
8092
|
+
const resultPromise = runAgent2(vaultDir, {
|
|
6893
8093
|
task,
|
|
6894
8094
|
instruction,
|
|
6895
8095
|
agentId,
|
|
6896
8096
|
embeddingManager,
|
|
6897
|
-
runContext
|
|
8097
|
+
runContext,
|
|
8098
|
+
dryRun,
|
|
8099
|
+
evaluationId,
|
|
8100
|
+
executionOverrides,
|
|
8101
|
+
logger
|
|
6898
8102
|
});
|
|
6899
8103
|
const effectiveAgentId = agentId ?? "myco-agent";
|
|
6900
8104
|
const runId = getLatestRunId(effectiveAgentId, task);
|
|
@@ -6946,14 +8150,59 @@ function createAgentRunHandlers(deps) {
|
|
|
6946
8150
|
const filterOpts = { agent_id: agentId, status, task, search };
|
|
6947
8151
|
const runs = listRuns({ ...filterOpts, limit, offset });
|
|
6948
8152
|
const total = countRuns(filterOpts);
|
|
6949
|
-
return { body: { runs, total, offset, limit } };
|
|
8153
|
+
return { body: { runs: runs.map((run) => serializeRun(run, { logger })), total, offset, limit } };
|
|
6950
8154
|
}
|
|
6951
8155
|
async function handleGetRun(req) {
|
|
6952
8156
|
const run = getRun(req.params.id);
|
|
6953
8157
|
if (!run) {
|
|
6954
8158
|
return { status: 404, body: { error: "Run not found" } };
|
|
6955
8159
|
}
|
|
6956
|
-
|
|
8160
|
+
const byTool = countWriteIntentsByTool(run.id);
|
|
8161
|
+
const total = Object.values(byTool).reduce((acc, n) => acc + n, 0);
|
|
8162
|
+
return {
|
|
8163
|
+
body: {
|
|
8164
|
+
run: serializeRun(run, {
|
|
8165
|
+
writeIntents: { total, by_tool: byTool },
|
|
8166
|
+
duration_ms: runDurationMs(run),
|
|
8167
|
+
logger
|
|
8168
|
+
})
|
|
8169
|
+
}
|
|
8170
|
+
};
|
|
8171
|
+
}
|
|
8172
|
+
async function handleResumeRun(req) {
|
|
8173
|
+
const run = getRun(req.params.id);
|
|
8174
|
+
if (!run) {
|
|
8175
|
+
return { status: 404, body: { error: "Run not found" } };
|
|
8176
|
+
}
|
|
8177
|
+
if (run.resumable !== 1 || run.status !== "failed") {
|
|
8178
|
+
return { status: 400, body: { error: "Run is not resumable" } };
|
|
8179
|
+
}
|
|
8180
|
+
const { mode } = ResumeRunBody.parse(req.body ?? {});
|
|
8181
|
+
const { runAgent: runAgent2 } = await import("./executor-DO6QFC6G.js");
|
|
8182
|
+
const resultPromise = runAgent2(vaultDir, {
|
|
8183
|
+
agentId: run.agent_id,
|
|
8184
|
+
task: run.task ?? void 0,
|
|
8185
|
+
instruction: run.instruction ?? void 0,
|
|
8186
|
+
resumeRunId: run.id,
|
|
8187
|
+
resumeMode: mode ?? "manual",
|
|
8188
|
+
embeddingManager,
|
|
8189
|
+
logger
|
|
8190
|
+
});
|
|
8191
|
+
resultPromise.then((result) => {
|
|
8192
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Agent run resumed", {
|
|
8193
|
+
runId: result.runId,
|
|
8194
|
+
status: result.status,
|
|
8195
|
+
runtime: result.runtime,
|
|
8196
|
+
provider: result.provider,
|
|
8197
|
+
model: result.model
|
|
8198
|
+
});
|
|
8199
|
+
}).catch((err) => {
|
|
8200
|
+
logger.error(LOG_KINDS.AGENT_ERROR, "Agent run resume threw unhandled error", {
|
|
8201
|
+
runId: run.id,
|
|
8202
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8203
|
+
});
|
|
8204
|
+
});
|
|
8205
|
+
return { body: { ok: true, message: "Agent resume started", runId: run.id } };
|
|
6957
8206
|
}
|
|
6958
8207
|
async function handleGetRunReports(req) {
|
|
6959
8208
|
const reports = listReports(req.params.id);
|
|
@@ -6963,18 +8212,377 @@ function createAgentRunHandlers(deps) {
|
|
|
6963
8212
|
const turns = listTurnsByRun(req.params.id);
|
|
6964
8213
|
return { body: turns };
|
|
6965
8214
|
}
|
|
8215
|
+
async function handleGetRunWriteIntents(req) {
|
|
8216
|
+
const rawLimit = req.query.limit ? Number(req.query.limit) : void 0;
|
|
8217
|
+
const rawOffset = req.query.offset ? Number(req.query.offset) : void 0;
|
|
8218
|
+
const limit = Number.isFinite(rawLimit) && rawLimit !== void 0 && rawLimit > 0 ? Math.min(rawLimit, 5e3) : 500;
|
|
8219
|
+
const offset = Number.isFinite(rawOffset) && rawOffset !== void 0 && rawOffset >= 0 ? rawOffset : 0;
|
|
8220
|
+
const intents = listWriteIntents(req.params.id, { limit, offset });
|
|
8221
|
+
const total = countWriteIntents(req.params.id);
|
|
8222
|
+
return { body: { intents, count: intents.length, total } };
|
|
8223
|
+
}
|
|
8224
|
+
async function handleGetRunAudit(req) {
|
|
8225
|
+
const audit = buildPhaseAudit(req.params.id);
|
|
8226
|
+
if (!audit) {
|
|
8227
|
+
return { status: 404, body: { error: "Run not found" } };
|
|
8228
|
+
}
|
|
8229
|
+
return { body: { audit } };
|
|
8230
|
+
}
|
|
6966
8231
|
return {
|
|
6967
8232
|
handleRun,
|
|
6968
8233
|
handleListRuns,
|
|
6969
8234
|
handleGetRun,
|
|
8235
|
+
handleResumeRun,
|
|
6970
8236
|
handleGetRunReports,
|
|
6971
|
-
handleGetRunTurns
|
|
8237
|
+
handleGetRunTurns,
|
|
8238
|
+
handleGetRunWriteIntents,
|
|
8239
|
+
handleGetRunAudit
|
|
6972
8240
|
};
|
|
6973
8241
|
}
|
|
6974
8242
|
|
|
8243
|
+
// src/daemon/api/agent-evaluations.ts
|
|
8244
|
+
import crypto from "crypto";
|
|
8245
|
+
|
|
8246
|
+
// src/db/queries/evaluations.ts
|
|
8247
|
+
var DEFAULT_LIMIT = 50;
|
|
8248
|
+
var EVAL_STATUS_PENDING = "pending";
|
|
8249
|
+
var EVAL_STATUS_RUNNING = "running";
|
|
8250
|
+
var EVAL_STATUS_COMPLETED = "completed";
|
|
8251
|
+
var EVAL_STATUS_FAILED = "failed";
|
|
8252
|
+
var EVAL_COLUMNS = [
|
|
8253
|
+
"id",
|
|
8254
|
+
"task_id",
|
|
8255
|
+
"matrix_json",
|
|
8256
|
+
"notes",
|
|
8257
|
+
"status",
|
|
8258
|
+
"created_at",
|
|
8259
|
+
"completed_at"
|
|
8260
|
+
];
|
|
8261
|
+
var SELECT_COLUMNS5 = EVAL_COLUMNS.join(", ");
|
|
8262
|
+
function toEvaluationRow(row) {
|
|
8263
|
+
const matrixJson = row.matrix_json;
|
|
8264
|
+
let matrix = null;
|
|
8265
|
+
if (matrixJson) {
|
|
8266
|
+
try {
|
|
8267
|
+
matrix = JSON.parse(matrixJson);
|
|
8268
|
+
} catch {
|
|
8269
|
+
matrix = null;
|
|
8270
|
+
}
|
|
8271
|
+
}
|
|
8272
|
+
return {
|
|
8273
|
+
id: row.id,
|
|
8274
|
+
task_id: row.task_id,
|
|
8275
|
+
matrix,
|
|
8276
|
+
notes: row.notes ?? null,
|
|
8277
|
+
status: row.status,
|
|
8278
|
+
created_at: row.created_at,
|
|
8279
|
+
completed_at: row.completed_at ?? null
|
|
8280
|
+
};
|
|
8281
|
+
}
|
|
8282
|
+
function insertEvaluation(data) {
|
|
8283
|
+
const db = getDatabase();
|
|
8284
|
+
const createdAt = data.createdAt ?? epochSeconds();
|
|
8285
|
+
db.prepare(
|
|
8286
|
+
`INSERT INTO agent_run_evaluations
|
|
8287
|
+
(id, task_id, matrix_json, notes, status, created_at, completed_at)
|
|
8288
|
+
VALUES (?, ?, ?, ?, ?, ?, NULL)`
|
|
8289
|
+
).run(
|
|
8290
|
+
data.id,
|
|
8291
|
+
data.taskId,
|
|
8292
|
+
JSON.stringify(data.matrix ?? null),
|
|
8293
|
+
data.notes ?? null,
|
|
8294
|
+
EVAL_STATUS_PENDING,
|
|
8295
|
+
createdAt
|
|
8296
|
+
);
|
|
8297
|
+
return getEvaluation(data.id);
|
|
8298
|
+
}
|
|
8299
|
+
function getEvaluation(id) {
|
|
8300
|
+
const db = getDatabase();
|
|
8301
|
+
const row = db.prepare(
|
|
8302
|
+
`SELECT ${SELECT_COLUMNS5} FROM agent_run_evaluations WHERE id = ?`
|
|
8303
|
+
).get(id);
|
|
8304
|
+
return row ? toEvaluationRow(row) : null;
|
|
8305
|
+
}
|
|
8306
|
+
function listEvaluations(options = {}) {
|
|
8307
|
+
const db = getDatabase();
|
|
8308
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
8309
|
+
const offset = options.offset ?? 0;
|
|
8310
|
+
const rows = db.prepare(
|
|
8311
|
+
`SELECT ${SELECT_COLUMNS5}
|
|
8312
|
+
FROM agent_run_evaluations
|
|
8313
|
+
ORDER BY created_at DESC, id DESC
|
|
8314
|
+
LIMIT ? OFFSET ?`
|
|
8315
|
+
).all(limit, offset);
|
|
8316
|
+
return rows.map(toEvaluationRow);
|
|
8317
|
+
}
|
|
8318
|
+
function updateEvaluationStatus(id, status, completedAt) {
|
|
8319
|
+
const db = getDatabase();
|
|
8320
|
+
if (completedAt === void 0) {
|
|
8321
|
+
db.prepare(
|
|
8322
|
+
`UPDATE agent_run_evaluations SET status = ? WHERE id = ?`
|
|
8323
|
+
).run(status, id);
|
|
8324
|
+
} else {
|
|
8325
|
+
db.prepare(
|
|
8326
|
+
`UPDATE agent_run_evaluations SET status = ?, completed_at = ? WHERE id = ?`
|
|
8327
|
+
).run(status, completedAt, id);
|
|
8328
|
+
}
|
|
8329
|
+
return getEvaluation(id);
|
|
8330
|
+
}
|
|
8331
|
+
|
|
8332
|
+
// src/daemon/api/agent-evaluations.ts
|
|
8333
|
+
var DEFAULT_LIMIT2 = 50;
|
|
8334
|
+
var CreateEvaluationBody = external_exports.object({
|
|
8335
|
+
taskId: external_exports.string(),
|
|
8336
|
+
matrix: external_exports.object({
|
|
8337
|
+
runtimes: external_exports.array(RuntimeIdEnum).optional(),
|
|
8338
|
+
reasoningLevels: external_exports.array(ReasoningLevelEnum).optional(),
|
|
8339
|
+
models: external_exports.array(external_exports.string()).optional(),
|
|
8340
|
+
dryRun: external_exports.boolean().optional(),
|
|
8341
|
+
notes: external_exports.string().optional(),
|
|
8342
|
+
/**
|
|
8343
|
+
* Phase-level overrides applied to EVERY cell in the matrix. The matrix
|
|
8344
|
+
* dimensions (runtimes / reasoningLevels / models) vary the top-level
|
|
8345
|
+
* execution per cell; phase overrides here let you pin specific phases
|
|
8346
|
+
* to fixed reasoning/model/provider/maxTurns regardless of what the cell
|
|
8347
|
+
* is varying at the top level.
|
|
8348
|
+
*
|
|
8349
|
+
* Merged into each cell's `executionOverrides.phases` before runAgent
|
|
8350
|
+
* is invoked. Unknown phase names are ignored by the executor (a
|
|
8351
|
+
* one-shot warning is logged at run startup).
|
|
8352
|
+
*/
|
|
8353
|
+
phases: external_exports.record(external_exports.string(), PhaseExecutionOverrideBody).optional()
|
|
8354
|
+
}),
|
|
8355
|
+
notes: external_exports.string().optional()
|
|
8356
|
+
});
|
|
8357
|
+
function aggregateRuns(runs) {
|
|
8358
|
+
let completed = 0;
|
|
8359
|
+
let failed = 0;
|
|
8360
|
+
let skipped = 0;
|
|
8361
|
+
let totalTokens = 0;
|
|
8362
|
+
let totalCostUsd = 0;
|
|
8363
|
+
for (const run of runs) {
|
|
8364
|
+
if (run.status === "completed") completed++;
|
|
8365
|
+
else if (run.status === "failed") failed++;
|
|
8366
|
+
else if (run.status === "skipped") skipped++;
|
|
8367
|
+
totalTokens += run.tokens_used ?? 0;
|
|
8368
|
+
totalCostUsd += run.cost_usd ?? 0;
|
|
8369
|
+
}
|
|
8370
|
+
return {
|
|
8371
|
+
total: runs.length,
|
|
8372
|
+
completed,
|
|
8373
|
+
failed,
|
|
8374
|
+
skipped,
|
|
8375
|
+
totalTokens,
|
|
8376
|
+
totalCostUsd
|
|
8377
|
+
};
|
|
8378
|
+
}
|
|
8379
|
+
function summarizeFromByTool(byTool) {
|
|
8380
|
+
const tools = byTool ?? {};
|
|
8381
|
+
let total = 0;
|
|
8382
|
+
for (const count of Object.values(tools)) total += count;
|
|
8383
|
+
return { total, by_tool: tools };
|
|
8384
|
+
}
|
|
8385
|
+
function createAgentEvaluationHandlers(deps) {
|
|
8386
|
+
const { vaultDir, embeddingManager, logger } = deps;
|
|
8387
|
+
async function handleCreate(req) {
|
|
8388
|
+
const parsed = CreateEvaluationBody.safeParse(req.body);
|
|
8389
|
+
if (!parsed.success) {
|
|
8390
|
+
return {
|
|
8391
|
+
status: 400,
|
|
8392
|
+
body: { error: "Invalid request body", details: parsed.error.flatten() }
|
|
8393
|
+
};
|
|
8394
|
+
}
|
|
8395
|
+
const body = parsed.data;
|
|
8396
|
+
const cells = enumerateMatrixCells(body.matrix);
|
|
8397
|
+
if (cells.length === 0) {
|
|
8398
|
+
return {
|
|
8399
|
+
status: 400,
|
|
8400
|
+
body: { error: "Matrix produced zero cells" }
|
|
8401
|
+
};
|
|
8402
|
+
}
|
|
8403
|
+
const evalId = crypto.randomUUID();
|
|
8404
|
+
insertEvaluation({
|
|
8405
|
+
id: evalId,
|
|
8406
|
+
taskId: body.taskId,
|
|
8407
|
+
matrix: body.matrix,
|
|
8408
|
+
notes: body.notes ?? null
|
|
8409
|
+
});
|
|
8410
|
+
const dryRun = body.matrix.dryRun ?? false;
|
|
8411
|
+
void (async () => {
|
|
8412
|
+
const { runAgent: runAgent2 } = await import("./executor-DO6QFC6G.js");
|
|
8413
|
+
let anyCompleted = false;
|
|
8414
|
+
let transitionedToRunning = false;
|
|
8415
|
+
const sharedPhases = body.matrix.phases;
|
|
8416
|
+
for (const cell of cells) {
|
|
8417
|
+
const cellOptions = {
|
|
8418
|
+
task: body.taskId,
|
|
8419
|
+
evaluationId: evalId,
|
|
8420
|
+
dryRun,
|
|
8421
|
+
embeddingManager,
|
|
8422
|
+
logger,
|
|
8423
|
+
executionOverrides: {
|
|
8424
|
+
...cell.runtime ? { runtime: cell.runtime } : {},
|
|
8425
|
+
...cell.reasoningLevel ? { reasoningLevel: cell.reasoningLevel } : {},
|
|
8426
|
+
...cell.model ? { model: cell.model } : {},
|
|
8427
|
+
...sharedPhases && Object.keys(sharedPhases).length > 0 ? { phases: sharedPhases } : {}
|
|
8428
|
+
}
|
|
8429
|
+
};
|
|
8430
|
+
if (!transitionedToRunning) {
|
|
8431
|
+
updateEvaluationStatus(evalId, EVAL_STATUS_RUNNING);
|
|
8432
|
+
transitionedToRunning = true;
|
|
8433
|
+
}
|
|
8434
|
+
try {
|
|
8435
|
+
const result = await runAgent2(vaultDir, cellOptions);
|
|
8436
|
+
if (result.status === "completed") {
|
|
8437
|
+
anyCompleted = true;
|
|
8438
|
+
}
|
|
8439
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Evaluation cell finished", {
|
|
8440
|
+
evaluationId: evalId,
|
|
8441
|
+
runId: result.runId,
|
|
8442
|
+
status: result.status,
|
|
8443
|
+
runtime: cell.runtime,
|
|
8444
|
+
reasoningLevel: cell.reasoningLevel,
|
|
8445
|
+
model: cell.model
|
|
8446
|
+
});
|
|
8447
|
+
} catch (err) {
|
|
8448
|
+
logger.error(LOG_KINDS.AGENT_ERROR, "Evaluation cell threw", {
|
|
8449
|
+
evaluationId: evalId,
|
|
8450
|
+
runtime: cell.runtime,
|
|
8451
|
+
reasoningLevel: cell.reasoningLevel,
|
|
8452
|
+
model: cell.model,
|
|
8453
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8454
|
+
});
|
|
8455
|
+
}
|
|
8456
|
+
}
|
|
8457
|
+
const status = anyCompleted ? EVAL_STATUS_COMPLETED : EVAL_STATUS_FAILED;
|
|
8458
|
+
updateEvaluationStatus(evalId, status, epochSeconds());
|
|
8459
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Evaluation finished", {
|
|
8460
|
+
evaluationId: evalId,
|
|
8461
|
+
status,
|
|
8462
|
+
cellCount: cells.length
|
|
8463
|
+
});
|
|
8464
|
+
})().catch((err) => {
|
|
8465
|
+
logger.error(LOG_KINDS.AGENT_ERROR, "Evaluation fan-out failed", {
|
|
8466
|
+
evaluationId: evalId,
|
|
8467
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8468
|
+
});
|
|
8469
|
+
});
|
|
8470
|
+
return { body: { evaluationId: evalId, cellCount: cells.length } };
|
|
8471
|
+
}
|
|
8472
|
+
async function handleGet(req) {
|
|
8473
|
+
const evaluation = getEvaluation(req.params.id);
|
|
8474
|
+
if (!evaluation) {
|
|
8475
|
+
return { status: 404, body: { error: "Evaluation not found" } };
|
|
8476
|
+
}
|
|
8477
|
+
const runs = listRunsForEvaluation(evaluation.id);
|
|
8478
|
+
const writeIntentsByRun = countWriteIntentsByToolForEvaluation(evaluation.id);
|
|
8479
|
+
return {
|
|
8480
|
+
body: {
|
|
8481
|
+
evaluation: {
|
|
8482
|
+
id: evaluation.id,
|
|
8483
|
+
taskId: evaluation.task_id,
|
|
8484
|
+
matrix: evaluation.matrix,
|
|
8485
|
+
notes: evaluation.notes,
|
|
8486
|
+
status: evaluation.status,
|
|
8487
|
+
createdAt: evaluation.created_at,
|
|
8488
|
+
completedAt: evaluation.completed_at
|
|
8489
|
+
},
|
|
8490
|
+
runs: runs.map(
|
|
8491
|
+
(run) => serializeRun(run, {
|
|
8492
|
+
includeResumeFields: false,
|
|
8493
|
+
includePhaseCheckpoints: false,
|
|
8494
|
+
writeIntents: summarizeFromByTool(writeIntentsByRun[run.id]),
|
|
8495
|
+
duration_ms: runDurationMs(run)
|
|
8496
|
+
})
|
|
8497
|
+
),
|
|
8498
|
+
aggregate: aggregateRuns(runs)
|
|
8499
|
+
}
|
|
8500
|
+
};
|
|
8501
|
+
}
|
|
8502
|
+
async function handleList(req) {
|
|
8503
|
+
const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIMIT2;
|
|
8504
|
+
const offset = req.query.offset ? Number(req.query.offset) : 0;
|
|
8505
|
+
const evaluations = listEvaluations({ limit, offset });
|
|
8506
|
+
return {
|
|
8507
|
+
body: {
|
|
8508
|
+
evaluations: evaluations.map((e) => ({
|
|
8509
|
+
id: e.id,
|
|
8510
|
+
taskId: e.task_id,
|
|
8511
|
+
matrix: e.matrix,
|
|
8512
|
+
notes: e.notes,
|
|
8513
|
+
status: e.status,
|
|
8514
|
+
createdAt: e.created_at,
|
|
8515
|
+
completedAt: e.completed_at
|
|
8516
|
+
})),
|
|
8517
|
+
total: evaluations.length
|
|
8518
|
+
}
|
|
8519
|
+
};
|
|
8520
|
+
}
|
|
8521
|
+
return { handleCreate, handleGet, handleList };
|
|
8522
|
+
}
|
|
8523
|
+
|
|
8524
|
+
// src/daemon/api/digest-revisions.ts
|
|
8525
|
+
var DEFAULT_LIMIT3 = 50;
|
|
8526
|
+
var RestoreBody = external_exports.object({
|
|
8527
|
+
/** If supplied, recorded on the new revision row as the run that triggered the restore. */
|
|
8528
|
+
runId: external_exports.string().optional()
|
|
8529
|
+
});
|
|
8530
|
+
function createDigestRevisionHandlers(deps) {
|
|
8531
|
+
const { logger } = deps;
|
|
8532
|
+
async function handleList(req) {
|
|
8533
|
+
const agentId = req.query.agentId || DEFAULT_AGENT_ID;
|
|
8534
|
+
const tierRaw = req.query.tier;
|
|
8535
|
+
if (!tierRaw) {
|
|
8536
|
+
return { status: 400, body: { error: "tier is required" } };
|
|
8537
|
+
}
|
|
8538
|
+
const tier = Number(tierRaw);
|
|
8539
|
+
if (!Number.isFinite(tier)) {
|
|
8540
|
+
return { status: 400, body: { error: `tier must be numeric, got ${tierRaw}` } };
|
|
8541
|
+
}
|
|
8542
|
+
const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIMIT3;
|
|
8543
|
+
const revisions = listDigestRevisions({ agentId, tier, limit });
|
|
8544
|
+
return { body: { revisions, count: revisions.length } };
|
|
8545
|
+
}
|
|
8546
|
+
async function handleRestore(req) {
|
|
8547
|
+
const revisionId = Number(req.params.id);
|
|
8548
|
+
if (!Number.isFinite(revisionId)) {
|
|
8549
|
+
return { status: 400, body: { error: "Invalid revision id" } };
|
|
8550
|
+
}
|
|
8551
|
+
const parsed = RestoreBody.safeParse(req.body ?? {});
|
|
8552
|
+
if (!parsed.success) {
|
|
8553
|
+
return {
|
|
8554
|
+
status: 400,
|
|
8555
|
+
body: { error: "Invalid request body", details: parsed.error.flatten() }
|
|
8556
|
+
};
|
|
8557
|
+
}
|
|
8558
|
+
const result = rollbackDigestExtract({
|
|
8559
|
+
revisionId,
|
|
8560
|
+
runId: parsed.data.runId ?? null
|
|
8561
|
+
});
|
|
8562
|
+
if (!result) {
|
|
8563
|
+
return { status: 404, body: { error: "Revision not found" } };
|
|
8564
|
+
}
|
|
8565
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Digest revision restored", {
|
|
8566
|
+
revisionId,
|
|
8567
|
+
newRevisionId: result.newRevisionId,
|
|
8568
|
+
agentId: result.row.agent_id,
|
|
8569
|
+
tier: result.row.tier,
|
|
8570
|
+
triggeredBy: parsed.data.runId ?? "operator"
|
|
8571
|
+
});
|
|
8572
|
+
return {
|
|
8573
|
+
body: {
|
|
8574
|
+
ok: true,
|
|
8575
|
+
restored: revisionId,
|
|
8576
|
+
newRevisionId: result.newRevisionId
|
|
8577
|
+
}
|
|
8578
|
+
};
|
|
8579
|
+
}
|
|
8580
|
+
return { handleList, handleRestore };
|
|
8581
|
+
}
|
|
8582
|
+
|
|
6975
8583
|
// src/daemon/api/attachments.ts
|
|
6976
|
-
import
|
|
6977
|
-
import
|
|
8584
|
+
import fs19 from "fs";
|
|
8585
|
+
import path19 from "path";
|
|
6978
8586
|
var ATTACHMENT_MEDIA_TYPES = {
|
|
6979
8587
|
png: "image/png",
|
|
6980
8588
|
jpg: "image/jpeg",
|
|
@@ -6994,14 +8602,14 @@ function createAttachmentHandler(deps) {
|
|
|
6994
8602
|
const contentType2 = att.media_type ?? "application/octet-stream";
|
|
6995
8603
|
return { status: 200, headers: { "Content-Type": contentType2 }, body: att.data };
|
|
6996
8604
|
}
|
|
6997
|
-
const filePath =
|
|
8605
|
+
const filePath = path19.join(vaultDir, "attachments", filename);
|
|
6998
8606
|
let diskData;
|
|
6999
8607
|
try {
|
|
7000
|
-
diskData =
|
|
8608
|
+
diskData = fs19.readFileSync(filePath);
|
|
7001
8609
|
} catch {
|
|
7002
8610
|
return { status: 404, body: { error: "not_found" } };
|
|
7003
8611
|
}
|
|
7004
|
-
const ext =
|
|
8612
|
+
const ext = path19.extname(filename).slice(1).toLowerCase();
|
|
7005
8613
|
const contentType = ATTACHMENT_MEDIA_TYPES[ext] ?? "application/octet-stream";
|
|
7006
8614
|
return { status: 200, headers: { "Content-Type": contentType }, body: diskData };
|
|
7007
8615
|
}
|
|
@@ -7009,19 +8617,19 @@ function createAttachmentHandler(deps) {
|
|
|
7009
8617
|
}
|
|
7010
8618
|
|
|
7011
8619
|
// src/daemon/log-reconcile.ts
|
|
7012
|
-
import
|
|
7013
|
-
import
|
|
8620
|
+
import fs20 from "fs";
|
|
8621
|
+
import path20 from "path";
|
|
7014
8622
|
function reconcileLogBuffer(logDir, sinceTimestamp) {
|
|
7015
8623
|
let replayed = 0;
|
|
7016
8624
|
const files = [];
|
|
7017
8625
|
for (let i = 3; i >= 1; i--) {
|
|
7018
|
-
const rotated =
|
|
7019
|
-
if (
|
|
8626
|
+
const rotated = path20.join(logDir, `daemon.${i}.log`);
|
|
8627
|
+
if (fs20.existsSync(rotated)) files.push(rotated);
|
|
7020
8628
|
}
|
|
7021
|
-
const current =
|
|
7022
|
-
if (
|
|
8629
|
+
const current = path20.join(logDir, "daemon.log");
|
|
8630
|
+
if (fs20.existsSync(current)) files.push(current);
|
|
7023
8631
|
for (const file of files) {
|
|
7024
|
-
const content =
|
|
8632
|
+
const content = fs20.readFileSync(file, "utf-8");
|
|
7025
8633
|
for (const line of content.split("\n")) {
|
|
7026
8634
|
if (!line.trim()) continue;
|
|
7027
8635
|
try {
|
|
@@ -7049,11 +8657,14 @@ function reconcileLogBuffer(logDir, sinceTimestamp) {
|
|
|
7049
8657
|
// src/config/focus.ts
|
|
7050
8658
|
var CONFIG_FOCUS_SECTION_PARAM = "configSection";
|
|
7051
8659
|
var CONFIG_FOCUS_FIELD_PARAM = "configField";
|
|
8660
|
+
var CONFIG_FOCUS_TAB_PARAM = "tab";
|
|
7052
8661
|
var CONFIG_SECTION_IDS = {
|
|
7053
8662
|
appearance: "config-section-appearance",
|
|
8663
|
+
cortexInstructions: "config-section-cortex-instructions",
|
|
8664
|
+
cortexBuilder: "config-section-cortex-builder",
|
|
8665
|
+
cortexDigest: "config-section-cortex-digest",
|
|
7054
8666
|
settingsAgent: "config-section-settings-agent",
|
|
7055
8667
|
settingsEmbedding: "config-section-settings-embedding",
|
|
7056
|
-
settingsContextInjection: "config-section-settings-context-injection",
|
|
7057
8668
|
settingsNotifications: "config-section-settings-notifications",
|
|
7058
8669
|
settingsPlanCapture: "config-section-settings-plan-capture",
|
|
7059
8670
|
settingsProject: "config-section-settings-project",
|
|
@@ -7080,11 +8691,30 @@ var SECTION_RULES = [
|
|
|
7080
8691
|
sectionId: CONFIG_SECTION_IDS.settingsEmbedding,
|
|
7081
8692
|
sectionLabel: "Embedding"
|
|
7082
8693
|
},
|
|
8694
|
+
{
|
|
8695
|
+
prefix: "context.digest_tier",
|
|
8696
|
+
page: "/cortex",
|
|
8697
|
+
sectionId: CONFIG_SECTION_IDS.cortexDigest,
|
|
8698
|
+
sectionLabel: "Digest",
|
|
8699
|
+
searchParams: { [CONFIG_FOCUS_TAB_PARAM]: "digest" }
|
|
8700
|
+
},
|
|
8701
|
+
{
|
|
8702
|
+
prefix: "context.cortex",
|
|
8703
|
+
page: "/cortex",
|
|
8704
|
+
sectionId: CONFIG_SECTION_IDS.cortexInstructions,
|
|
8705
|
+
sectionLabel: "Instructions"
|
|
8706
|
+
},
|
|
8707
|
+
{
|
|
8708
|
+
prefix: "context.prompt",
|
|
8709
|
+
page: "/cortex",
|
|
8710
|
+
sectionId: CONFIG_SECTION_IDS.cortexInstructions,
|
|
8711
|
+
sectionLabel: "Instructions"
|
|
8712
|
+
},
|
|
7083
8713
|
{
|
|
7084
8714
|
prefix: "context",
|
|
7085
|
-
page: "/
|
|
7086
|
-
sectionId: CONFIG_SECTION_IDS.
|
|
7087
|
-
sectionLabel: "
|
|
8715
|
+
page: "/cortex",
|
|
8716
|
+
sectionId: CONFIG_SECTION_IDS.cortexInstructions,
|
|
8717
|
+
sectionLabel: "Instructions"
|
|
7088
8718
|
},
|
|
7089
8719
|
{
|
|
7090
8720
|
prefix: "notifications",
|
|
@@ -7151,7 +8781,9 @@ var EXACT_FIELD_LABELS = {
|
|
|
7151
8781
|
"embedding.provider": "Provider",
|
|
7152
8782
|
"embedding.model": "Model",
|
|
7153
8783
|
"embedding.base_url": "Base URL",
|
|
7154
|
-
"context.digest_tier": "Digest Tier",
|
|
8784
|
+
"context.digest_tier": "Preferred Digest Tier",
|
|
8785
|
+
"context.session_start_digest_enabled": "Session-Start Digest",
|
|
8786
|
+
"context.cortex_enabled": "Session-Start Instructions",
|
|
7155
8787
|
"context.prompt_search": "Prompt Search",
|
|
7156
8788
|
"context.prompt_max_spores": "Max Spores per Prompt",
|
|
7157
8789
|
"notifications.enabled": "Notifications",
|
|
@@ -7172,8 +8804,8 @@ var EXACT_FIELD_LABELS = {
|
|
|
7172
8804
|
var DYNAMIC_FIELD_LABEL_RULES = [
|
|
7173
8805
|
{
|
|
7174
8806
|
prefix: "notifications.domains.",
|
|
7175
|
-
format: (
|
|
7176
|
-
const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(
|
|
8807
|
+
format: (path25) => {
|
|
8808
|
+
const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(path25);
|
|
7177
8809
|
if (!match) return null;
|
|
7178
8810
|
const [, domain, leaf] = match;
|
|
7179
8811
|
const domainLabel = humanizeToken(domain);
|
|
@@ -7182,13 +8814,13 @@ var DYNAMIC_FIELD_LABEL_RULES = [
|
|
|
7182
8814
|
}
|
|
7183
8815
|
];
|
|
7184
8816
|
var SAVE_MESSAGE_LABEL_LIMIT = 3;
|
|
7185
|
-
function resolveConfigFocusTarget(
|
|
7186
|
-
const section = findSectionRule(
|
|
8817
|
+
function resolveConfigFocusTarget(path25) {
|
|
8818
|
+
const section = findSectionRule(path25);
|
|
7187
8819
|
if (!section) return null;
|
|
7188
8820
|
return {
|
|
7189
8821
|
...section,
|
|
7190
|
-
fieldPath:
|
|
7191
|
-
fieldLabel: resolveFieldLabel(
|
|
8822
|
+
fieldPath: path25,
|
|
8823
|
+
fieldLabel: resolveFieldLabel(path25)
|
|
7192
8824
|
};
|
|
7193
8825
|
}
|
|
7194
8826
|
function buildConfigFocusLink(target) {
|
|
@@ -7223,25 +8855,25 @@ function buildScopedConfigSaveNotification(scope, touchedPaths) {
|
|
|
7223
8855
|
}
|
|
7224
8856
|
};
|
|
7225
8857
|
}
|
|
7226
|
-
function findSectionRule(
|
|
8858
|
+
function findSectionRule(path25) {
|
|
7227
8859
|
for (const rule of SECTION_RULES) {
|
|
7228
|
-
if (
|
|
8860
|
+
if (path25 === rule.prefix || path25.startsWith(`${rule.prefix}.`)) {
|
|
7229
8861
|
const { page, sectionId, sectionLabel, searchParams } = rule;
|
|
7230
8862
|
return { page, sectionId, sectionLabel, searchParams };
|
|
7231
8863
|
}
|
|
7232
8864
|
}
|
|
7233
8865
|
return null;
|
|
7234
8866
|
}
|
|
7235
|
-
function resolveFieldLabel(
|
|
7236
|
-
const exact = EXACT_FIELD_LABELS[
|
|
8867
|
+
function resolveFieldLabel(path25) {
|
|
8868
|
+
const exact = EXACT_FIELD_LABELS[path25];
|
|
7237
8869
|
if (exact) return exact;
|
|
7238
8870
|
for (const rule of DYNAMIC_FIELD_LABEL_RULES) {
|
|
7239
|
-
if (
|
|
7240
|
-
const label = rule.format(
|
|
8871
|
+
if (path25 === rule.prefix || path25.startsWith(rule.prefix)) {
|
|
8872
|
+
const label = rule.format(path25);
|
|
7241
8873
|
if (label) return label;
|
|
7242
8874
|
}
|
|
7243
8875
|
}
|
|
7244
|
-
return humanizeToken(
|
|
8876
|
+
return humanizeToken(path25.split(".").pop() ?? "setting");
|
|
7245
8877
|
}
|
|
7246
8878
|
function humanizeToken(value) {
|
|
7247
8879
|
return value.split(/[-_]/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -7361,6 +8993,48 @@ var PowerManager = class {
|
|
|
7361
8993
|
}
|
|
7362
8994
|
};
|
|
7363
8995
|
|
|
8996
|
+
// src/daemon/inflight-runs.ts
|
|
8997
|
+
var DEFAULT_DRAIN_TIMEOUT_MS = 3e4;
|
|
8998
|
+
var InflightRunRegistry = class {
|
|
8999
|
+
runs = /* @__PURE__ */ new Set();
|
|
9000
|
+
/**
|
|
9001
|
+
* Track a fire-and-forget agent run. The promise is removed from the
|
|
9002
|
+
* registry in a finally handler so a resolved/rejected run does not hold
|
|
9003
|
+
* memory. Resolves immediately — callers must not await this directly.
|
|
9004
|
+
*/
|
|
9005
|
+
register(promise) {
|
|
9006
|
+
const tracked = Promise.resolve(promise).finally(() => {
|
|
9007
|
+
this.runs.delete(tracked);
|
|
9008
|
+
});
|
|
9009
|
+
this.runs.add(tracked);
|
|
9010
|
+
}
|
|
9011
|
+
/** Number of runs currently being tracked. */
|
|
9012
|
+
get size() {
|
|
9013
|
+
return this.runs.size;
|
|
9014
|
+
}
|
|
9015
|
+
/**
|
|
9016
|
+
* Wait for every tracked run to settle, up to `timeoutMs`. Returns when
|
|
9017
|
+
* either every run completes or the deadline elapses — whichever happens
|
|
9018
|
+
* first. The 30-second default mirrors a typical daemon shutdown budget;
|
|
9019
|
+
* callers are free to override for tests or tighter environments.
|
|
9020
|
+
*/
|
|
9021
|
+
async drain(timeoutMs = DEFAULT_DRAIN_TIMEOUT_MS) {
|
|
9022
|
+
if (this.runs.size === 0) return { settled: true, remaining: 0 };
|
|
9023
|
+
const snapshot = Array.from(this.runs);
|
|
9024
|
+
let timer;
|
|
9025
|
+
const timeoutPromise = new Promise((resolve3) => {
|
|
9026
|
+
timer = setTimeout(() => resolve3("timeout"), timeoutMs);
|
|
9027
|
+
});
|
|
9028
|
+
const allSettled = Promise.allSettled(snapshot).then(() => "settled");
|
|
9029
|
+
try {
|
|
9030
|
+
const outcome = await Promise.race([allSettled, timeoutPromise]);
|
|
9031
|
+
return { settled: outcome === "settled", remaining: this.runs.size };
|
|
9032
|
+
} finally {
|
|
9033
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
9034
|
+
}
|
|
9035
|
+
}
|
|
9036
|
+
};
|
|
9037
|
+
|
|
7364
9038
|
// src/daemon/jobs/session-maintenance.ts
|
|
7365
9039
|
function completeStaleActiveSessions(thresholdSeconds = STALE_SESSION_THRESHOLD_MS / MS_PER_SECOND) {
|
|
7366
9040
|
const db = getDatabase();
|
|
@@ -7505,8 +9179,8 @@ function registerPowerJobs(powerManager, deps) {
|
|
|
7505
9179
|
}
|
|
7506
9180
|
|
|
7507
9181
|
// src/daemon/reconciliation.ts
|
|
7508
|
-
import
|
|
7509
|
-
import
|
|
9182
|
+
import fs21 from "fs";
|
|
9183
|
+
import path21 from "path";
|
|
7510
9184
|
|
|
7511
9185
|
// src/daemon/event-handlers.ts
|
|
7512
9186
|
var TOOL_INPUT_STORE_LIMIT = 4e3;
|
|
@@ -7675,10 +9349,10 @@ function createReconciler({ bufferDir, logger }) {
|
|
|
7675
9349
|
function reconcileSession(sessionId) {
|
|
7676
9350
|
if (reconciledSessions.has(sessionId)) return;
|
|
7677
9351
|
reconciledSessions.add(sessionId);
|
|
7678
|
-
const bufferPath =
|
|
9352
|
+
const bufferPath = path21.join(bufferDir, `${sessionId}.jsonl`);
|
|
7679
9353
|
let content;
|
|
7680
9354
|
try {
|
|
7681
|
-
content =
|
|
9355
|
+
content = fs21.readFileSync(bufferPath, "utf-8").trim();
|
|
7682
9356
|
} catch {
|
|
7683
9357
|
return;
|
|
7684
9358
|
}
|
|
@@ -7747,7 +9421,19 @@ function createReconciler({ bufferDir, logger }) {
|
|
|
7747
9421
|
}
|
|
7748
9422
|
|
|
7749
9423
|
// src/daemon/stop-processing.ts
|
|
7750
|
-
import
|
|
9424
|
+
import fs22 from "fs";
|
|
9425
|
+
import path22 from "path";
|
|
9426
|
+
|
|
9427
|
+
// src/daemon/capture-gating.ts
|
|
9428
|
+
function gateEventByCaptureRules(event, options) {
|
|
9429
|
+
const transcriptMeta = event.transcriptPath ? readTranscriptMeta(event.transcriptPath) ?? void 0 : void 0;
|
|
9430
|
+
const manifests = options?.manifests ?? loadManifests();
|
|
9431
|
+
const decision = evaluateSessionCaptureRules(manifests, event.agent, {
|
|
9432
|
+
transcriptPath: event.transcriptPath,
|
|
9433
|
+
transcriptMeta
|
|
9434
|
+
});
|
|
9435
|
+
return { decision, hadTranscriptMeta: transcriptMeta !== void 0 };
|
|
9436
|
+
}
|
|
7751
9437
|
|
|
7752
9438
|
// src/daemon/capture-images.ts
|
|
7753
9439
|
var SESSION_SHORT_LEN = 6;
|
|
@@ -7784,80 +9470,8 @@ function captureBatchImages(input) {
|
|
|
7784
9470
|
}
|
|
7785
9471
|
}
|
|
7786
9472
|
|
|
7787
|
-
// src/daemon/plan-capture.ts
|
|
7788
|
-
import { createHash as createHash4 } from "crypto";
|
|
7789
|
-
import os8 from "os";
|
|
7790
|
-
import path20 from "path";
|
|
7791
|
-
function extractTaggedPlans(text, tags) {
|
|
7792
|
-
const results = [];
|
|
7793
|
-
for (const tag of tags) {
|
|
7794
|
-
const regex = new RegExp(`<${tag}>\\n?([\\s\\S]*?)\\n?</${tag}>`, "g");
|
|
7795
|
-
let match;
|
|
7796
|
-
while ((match = regex.exec(text)) !== null) {
|
|
7797
|
-
const content = match[1].trim();
|
|
7798
|
-
if (content) results.push({ tag, content });
|
|
7799
|
-
}
|
|
7800
|
-
}
|
|
7801
|
-
return results;
|
|
7802
|
-
}
|
|
7803
|
-
var TRANSCRIPT_SOURCE_PREFIX = "transcript:";
|
|
7804
|
-
var FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
7805
|
-
"Write",
|
|
7806
|
-
"Edit",
|
|
7807
|
-
"Create",
|
|
7808
|
-
"write",
|
|
7809
|
-
"edit",
|
|
7810
|
-
"patch",
|
|
7811
|
-
"create"
|
|
7812
|
-
]);
|
|
7813
|
-
var HEADING_REGEX = /^#\s+(.+)$/m;
|
|
7814
|
-
var PLAN_ID_HASH_LENGTH = 16;
|
|
7815
|
-
function isInPlanDirectory(filePath, watchDirs, projectRoot) {
|
|
7816
|
-
const abs = path20.isAbsolute(filePath) ? filePath : path20.resolve(projectRoot, filePath);
|
|
7817
|
-
return watchDirs.some((dir) => {
|
|
7818
|
-
const expanded = dir.startsWith("~/") ? path20.join(os8.homedir(), dir.slice(2)) : dir;
|
|
7819
|
-
const absDir = path20.isAbsolute(expanded) ? expanded : path20.resolve(projectRoot, expanded);
|
|
7820
|
-
const prefix = absDir.endsWith(path20.sep) ? absDir : absDir + path20.sep;
|
|
7821
|
-
return abs === absDir || abs.startsWith(prefix);
|
|
7822
|
-
});
|
|
7823
|
-
}
|
|
7824
|
-
function isPlanWriteEvent(toolName, toolInput, config) {
|
|
7825
|
-
if (!FILE_WRITE_TOOLS.has(toolName)) return null;
|
|
7826
|
-
const filePath = toolInput?.file_path ?? toolInput?.path ?? toolInput?.filePath;
|
|
7827
|
-
if (typeof filePath !== "string") return null;
|
|
7828
|
-
if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;
|
|
7829
|
-
if (config.extensions?.length) {
|
|
7830
|
-
const ext = path20.extname(filePath).toLowerCase();
|
|
7831
|
-
if (!config.extensions.includes(ext)) return null;
|
|
7832
|
-
}
|
|
7833
|
-
return filePath;
|
|
7834
|
-
}
|
|
7835
|
-
function parsePlanTitle(content, filename) {
|
|
7836
|
-
const match = HEADING_REGEX.exec(content);
|
|
7837
|
-
if (match) return match[1].trim();
|
|
7838
|
-
return filename ?? null;
|
|
7839
|
-
}
|
|
7840
|
-
function capturePlan(input) {
|
|
7841
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
7842
|
-
const contentHash = createHash4(CONTENT_HASH_ALGORITHM).update(input.content).digest("hex");
|
|
7843
|
-
const id = createHash4("md5").update(input.sourcePath).digest("hex").slice(0, PLAN_ID_HASH_LENGTH);
|
|
7844
|
-
const title = parsePlanTitle(input.content, path20.basename(input.sourcePath));
|
|
7845
|
-
return upsertPlan({
|
|
7846
|
-
id,
|
|
7847
|
-
title,
|
|
7848
|
-
content: input.content,
|
|
7849
|
-
source_path: input.sourcePath,
|
|
7850
|
-
session_id: input.sessionId,
|
|
7851
|
-
prompt_batch_id: input.promptBatchId ?? null,
|
|
7852
|
-
content_hash: contentHash,
|
|
7853
|
-
status: "active",
|
|
7854
|
-
created_at: now,
|
|
7855
|
-
updated_at: now
|
|
7856
|
-
});
|
|
7857
|
-
}
|
|
7858
|
-
|
|
7859
9473
|
// src/daemon/skill-usage.ts
|
|
7860
|
-
import
|
|
9474
|
+
import crypto2 from "crypto";
|
|
7861
9475
|
var SKILL_USAGE_DETECTION_ENABLED = false;
|
|
7862
9476
|
var MAX_ACTIVE_SKILLS_CHECK = 1e3;
|
|
7863
9477
|
function detectSkillUsage(sessionId, transcriptContent) {
|
|
@@ -7877,7 +9491,7 @@ function detectSkillUsage(sessionId, transcriptContent) {
|
|
|
7877
9491
|
if (!pattern.test(transcriptContent)) continue;
|
|
7878
9492
|
if (hasUsageForSkillAndSession(skill.id, sessionId)) continue;
|
|
7879
9493
|
insertSkillUsage({
|
|
7880
|
-
id:
|
|
9494
|
+
id: crypto2.randomUUID(),
|
|
7881
9495
|
skill_id: skill.id,
|
|
7882
9496
|
session_id: sessionId,
|
|
7883
9497
|
detected_at: now
|
|
@@ -7892,6 +9506,46 @@ function escapeRegex(s) {
|
|
|
7892
9506
|
}
|
|
7893
9507
|
|
|
7894
9508
|
// src/daemon/stop-processing.ts
|
|
9509
|
+
var MILLISECONDS_PER_SECOND = 1e3;
|
|
9510
|
+
function isEligiblePlanExtension(filePath, extensions) {
|
|
9511
|
+
if (!extensions || extensions.length === 0) return true;
|
|
9512
|
+
const extension = path22.extname(filePath).toLowerCase();
|
|
9513
|
+
return extensions.includes(extension);
|
|
9514
|
+
}
|
|
9515
|
+
function collectPlanFiles(rootDir, extensions) {
|
|
9516
|
+
const files = [];
|
|
9517
|
+
const stack = [rootDir];
|
|
9518
|
+
while (stack.length > 0) {
|
|
9519
|
+
const currentDir = stack.pop();
|
|
9520
|
+
if (!currentDir) continue;
|
|
9521
|
+
let entries;
|
|
9522
|
+
try {
|
|
9523
|
+
entries = fs22.readdirSync(currentDir, { withFileTypes: true });
|
|
9524
|
+
} catch {
|
|
9525
|
+
continue;
|
|
9526
|
+
}
|
|
9527
|
+
for (const entry of entries) {
|
|
9528
|
+
const absolutePath = path22.join(currentDir, entry.name);
|
|
9529
|
+
if (entry.isDirectory()) {
|
|
9530
|
+
stack.push(absolutePath);
|
|
9531
|
+
continue;
|
|
9532
|
+
}
|
|
9533
|
+
if (entry.isFile() && isEligiblePlanExtension(absolutePath, extensions)) {
|
|
9534
|
+
files.push(absolutePath);
|
|
9535
|
+
}
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9538
|
+
return files;
|
|
9539
|
+
}
|
|
9540
|
+
function resolvePromptBatchIdForPlanWrite(batches, modifiedAtSeconds) {
|
|
9541
|
+
for (let index = batches.length - 1; index >= 0; index--) {
|
|
9542
|
+
const startedAt = batches[index].started_at;
|
|
9543
|
+
if (startedAt !== null && startedAt <= modifiedAtSeconds) {
|
|
9544
|
+
return batches[index].id;
|
|
9545
|
+
}
|
|
9546
|
+
}
|
|
9547
|
+
return void 0;
|
|
9548
|
+
}
|
|
7895
9549
|
function enrichTurnsWithToolMetadata(turns, events) {
|
|
7896
9550
|
if (events.length === 0 || turns.length === 0) return;
|
|
7897
9551
|
const toolEvents = events.filter((e) => e.type === "tool_use");
|
|
@@ -7919,7 +9573,7 @@ function enrichTurnsWithToolMetadata(turns, events) {
|
|
|
7919
9573
|
}
|
|
7920
9574
|
}
|
|
7921
9575
|
function createStopProcessor(deps) {
|
|
7922
|
-
const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir } = deps;
|
|
9576
|
+
const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir, planWatchConfig } = deps;
|
|
7923
9577
|
let activeStopProcessing = null;
|
|
7924
9578
|
const sessionTitleCache = /* @__PURE__ */ new Map();
|
|
7925
9579
|
const StopBody = external_exports.object({
|
|
@@ -8021,7 +9675,7 @@ function createStopProcessor(deps) {
|
|
|
8021
9675
|
let transcriptText = null;
|
|
8022
9676
|
if (hookTranscriptPath) {
|
|
8023
9677
|
try {
|
|
8024
|
-
transcriptText =
|
|
9678
|
+
transcriptText = fs22.readFileSync(hookTranscriptPath, "utf-8");
|
|
8025
9679
|
} catch {
|
|
8026
9680
|
}
|
|
8027
9681
|
}
|
|
@@ -8053,11 +9707,12 @@ function createStopProcessor(deps) {
|
|
|
8053
9707
|
const taggedPlans = extractTaggedPlans(turn.aiResponse, deps.planTags);
|
|
8054
9708
|
for (const { tag, content } of taggedPlans) {
|
|
8055
9709
|
try {
|
|
8056
|
-
|
|
8057
|
-
|
|
9710
|
+
captureTaggedPlan({
|
|
9711
|
+
tag,
|
|
8058
9712
|
content,
|
|
8059
9713
|
sessionId,
|
|
8060
|
-
promptBatchId: latestBatch?.id ?? null
|
|
9714
|
+
promptBatchId: latestBatch?.id ?? null,
|
|
9715
|
+
logger
|
|
8061
9716
|
});
|
|
8062
9717
|
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured from transcript tag", {
|
|
8063
9718
|
session_id: sessionId,
|
|
@@ -8074,6 +9729,50 @@ function createStopProcessor(deps) {
|
|
|
8074
9729
|
}
|
|
8075
9730
|
}
|
|
8076
9731
|
}
|
|
9732
|
+
const sessionStartedAt = currentSession?.started_at ?? (sessionMeta?.started_at ? Math.floor(new Date(sessionMeta.started_at).getTime() / MILLISECONDS_PER_SECOND) : epochSeconds());
|
|
9733
|
+
const sessionStopMs = Date.now();
|
|
9734
|
+
const sessionBatches = listBatchesBySession(sessionId);
|
|
9735
|
+
for (const watchDir of planWatchConfig.watchDirs) {
|
|
9736
|
+
const absoluteWatchDir = resolvePlanWatchDir(watchDir, planWatchConfig.projectRoot);
|
|
9737
|
+
if (!fs22.existsSync(absoluteWatchDir)) continue;
|
|
9738
|
+
const planFiles = collectPlanFiles(absoluteWatchDir, planWatchConfig.extensions);
|
|
9739
|
+
for (const planFile of planFiles) {
|
|
9740
|
+
let stats;
|
|
9741
|
+
try {
|
|
9742
|
+
stats = fs22.statSync(planFile);
|
|
9743
|
+
} catch {
|
|
9744
|
+
continue;
|
|
9745
|
+
}
|
|
9746
|
+
if (stats.mtimeMs < sessionStartedAt * MILLISECONDS_PER_SECOND || stats.mtimeMs > sessionStopMs) {
|
|
9747
|
+
continue;
|
|
9748
|
+
}
|
|
9749
|
+
try {
|
|
9750
|
+
const content = fs22.readFileSync(planFile, "utf-8");
|
|
9751
|
+
const promptBatchId = resolvePromptBatchIdForPlanWrite(
|
|
9752
|
+
sessionBatches,
|
|
9753
|
+
Math.floor(stats.mtimeMs / MILLISECONDS_PER_SECOND)
|
|
9754
|
+
);
|
|
9755
|
+
capturePlan({
|
|
9756
|
+
sourcePath: planFile,
|
|
9757
|
+
projectRoot: planWatchConfig.projectRoot,
|
|
9758
|
+
content,
|
|
9759
|
+
sessionId,
|
|
9760
|
+
promptBatchId,
|
|
9761
|
+
logger
|
|
9762
|
+
});
|
|
9763
|
+
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan reconciled from configured plan dir", {
|
|
9764
|
+
session_id: sessionId,
|
|
9765
|
+
source_path: planFile
|
|
9766
|
+
});
|
|
9767
|
+
} catch (err) {
|
|
9768
|
+
logger.warn(LOG_KINDS.CAPTURE_PLAN, "Failed to reconcile plan from configured plan dir", {
|
|
9769
|
+
session_id: sessionId,
|
|
9770
|
+
source_path: planFile,
|
|
9771
|
+
error: err.message
|
|
9772
|
+
});
|
|
9773
|
+
}
|
|
9774
|
+
}
|
|
9775
|
+
}
|
|
8077
9776
|
if (!hasTitle) {
|
|
8078
9777
|
triggerTitleSummary2(sessionId);
|
|
8079
9778
|
}
|
|
@@ -8120,12 +9819,11 @@ function createStopProcessor(deps) {
|
|
|
8120
9819
|
last_assistant_message: lastAssistantMessage
|
|
8121
9820
|
} = StopBody.parse(req.body);
|
|
8122
9821
|
if (hookTranscriptPath) {
|
|
8123
|
-
const transcriptMeta = readTranscriptMeta(hookTranscriptPath) ?? void 0;
|
|
8124
9822
|
const detectedAgent = agent ?? getSession(sessionId)?.agent ?? "claude-code";
|
|
8125
|
-
const decision =
|
|
8126
|
-
transcriptPath: hookTranscriptPath,
|
|
8127
|
-
|
|
8128
|
-
|
|
9823
|
+
const { decision } = gateEventByCaptureRules(
|
|
9824
|
+
{ agent: detectedAgent, transcriptPath: hookTranscriptPath },
|
|
9825
|
+
{ manifests: loadManifests() }
|
|
9826
|
+
);
|
|
8129
9827
|
if (decision.action === "drop") {
|
|
8130
9828
|
const deleted = cleanupInvalidCapturedSession(sessionId);
|
|
8131
9829
|
logger.info(LOG_KINDS.HOOKS_STOP, "Stop ignored \u2014 invalid captured session", {
|
|
@@ -8180,9 +9878,10 @@ function createStopProcessor(deps) {
|
|
|
8180
9878
|
}
|
|
8181
9879
|
|
|
8182
9880
|
// src/daemon/event-dispatch.ts
|
|
8183
|
-
import
|
|
8184
|
-
import
|
|
9881
|
+
import fs23 from "fs";
|
|
9882
|
+
import path23 from "path";
|
|
8185
9883
|
var EventBody = external_exports.object({ type: external_exports.string(), session_id: external_exports.string() }).passthrough();
|
|
9884
|
+
var DROP_DECISION_CACHE_MAX = 1024;
|
|
8186
9885
|
function createEventDispatcher(deps) {
|
|
8187
9886
|
const {
|
|
8188
9887
|
registry,
|
|
@@ -8197,6 +9896,39 @@ function createEventDispatcher(deps) {
|
|
|
8197
9896
|
triggerTitleSummary: triggerTitleSummary2
|
|
8198
9897
|
} = deps;
|
|
8199
9898
|
const projectRoot = process.cwd();
|
|
9899
|
+
const manifests = loadManifests();
|
|
9900
|
+
const planTagsByAgent = new Map(
|
|
9901
|
+
manifests.map((manifest) => [manifest.name, manifest.capture?.planTags ?? []])
|
|
9902
|
+
);
|
|
9903
|
+
const droppedSessions = /* @__PURE__ */ new Map();
|
|
9904
|
+
function rememberDropped(sessionId, reason, hadTranscriptMeta) {
|
|
9905
|
+
if (droppedSessions.size >= DROP_DECISION_CACHE_MAX) {
|
|
9906
|
+
const oldest = droppedSessions.keys().next().value;
|
|
9907
|
+
if (oldest !== void 0) droppedSessions.delete(oldest);
|
|
9908
|
+
}
|
|
9909
|
+
droppedSessions.set(sessionId, { reason, hadTranscriptMeta });
|
|
9910
|
+
}
|
|
9911
|
+
function evaluateAutoRegistration(event) {
|
|
9912
|
+
const transcriptPath = typeof event.transcript_path === "string" && event.transcript_path.length > 0 ? event.transcript_path : void 0;
|
|
9913
|
+
const detectedAgent = typeof event.agent === "string" ? event.agent : DEFAULT_SYMBIONT_NAME;
|
|
9914
|
+
try {
|
|
9915
|
+
return gateEventByCaptureRules(
|
|
9916
|
+
{ agent: detectedAgent, transcriptPath },
|
|
9917
|
+
{ manifests }
|
|
9918
|
+
);
|
|
9919
|
+
} catch (err) {
|
|
9920
|
+
logger.error(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Capture-rules evaluator threw", {
|
|
9921
|
+
error: String(err),
|
|
9922
|
+
session_id: typeof event.session_id === "string" ? event.session_id : void 0,
|
|
9923
|
+
agent: detectedAgent
|
|
9924
|
+
});
|
|
9925
|
+
return { decision: { action: "pass" }, hadTranscriptMeta: false };
|
|
9926
|
+
}
|
|
9927
|
+
}
|
|
9928
|
+
function getPlanTagsForAgent(agent) {
|
|
9929
|
+
const agentName = typeof agent === "string" && agent.length > 0 ? agent : DEFAULT_SYMBIONT_NAME;
|
|
9930
|
+
return planTagsByAgent.get(agentName) ?? [];
|
|
9931
|
+
}
|
|
8200
9932
|
return async (req) => {
|
|
8201
9933
|
const validated = EventBody.parse(req.body);
|
|
8202
9934
|
const event = {
|
|
@@ -8205,13 +9937,38 @@ function createEventDispatcher(deps) {
|
|
|
8205
9937
|
};
|
|
8206
9938
|
logger.debug(LOG_KINDS.HOOKS_EVENT, "Event received", { type: event.type, session_id: event.session_id });
|
|
8207
9939
|
if (!registry.getSession(event.session_id)) {
|
|
9940
|
+
const cached = droppedSessions.get(event.session_id);
|
|
9941
|
+
const hasTranscriptNow = typeof event.transcript_path === "string" && event.transcript_path.length > 0;
|
|
9942
|
+
const shouldReevaluate = cached && !cached.hadTranscriptMeta && hasTranscriptNow;
|
|
9943
|
+
if (cached && !shouldReevaluate) {
|
|
9944
|
+
const reason = cached.reason ?? "rule";
|
|
9945
|
+
logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Ignored event for previously-dropped session", {
|
|
9946
|
+
session_id: event.session_id,
|
|
9947
|
+
type: event.type,
|
|
9948
|
+
reason
|
|
9949
|
+
});
|
|
9950
|
+
return { body: { ok: true, ignored: reason } };
|
|
9951
|
+
}
|
|
9952
|
+
if (shouldReevaluate) {
|
|
9953
|
+
droppedSessions.delete(event.session_id);
|
|
9954
|
+
}
|
|
9955
|
+
const { decision, hadTranscriptMeta } = evaluateAutoRegistration(event);
|
|
9956
|
+
if (decision.action === "drop") {
|
|
9957
|
+
rememberDropped(event.session_id, decision.reason, hadTranscriptMeta);
|
|
9958
|
+
logger.info(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Ignored event that failed session capture rules", {
|
|
9959
|
+
session_id: event.session_id,
|
|
9960
|
+
type: event.type,
|
|
9961
|
+
reason: decision.reason ?? "rule"
|
|
9962
|
+
});
|
|
9963
|
+
return { body: { ok: true, ignored: decision.reason ?? "rule" } };
|
|
9964
|
+
}
|
|
8208
9965
|
registry.register(event.session_id, { started_at: event.timestamp });
|
|
8209
9966
|
logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Auto-registered session from event", { session_id: event.session_id });
|
|
8210
9967
|
const now = epochSeconds();
|
|
8211
9968
|
const startedEpoch = Math.floor(new Date(event.timestamp).getTime() / 1e3);
|
|
8212
9969
|
upsertSession({
|
|
8213
9970
|
id: event.session_id,
|
|
8214
|
-
agent: event.agent ??
|
|
9971
|
+
agent: event.agent ?? DEFAULT_SYMBIONT_NAME,
|
|
8215
9972
|
status: "active",
|
|
8216
9973
|
started_at: startedEpoch,
|
|
8217
9974
|
created_at: now,
|
|
@@ -8220,7 +9977,7 @@ function createEventDispatcher(deps) {
|
|
|
8220
9977
|
reconcileSession(event.session_id);
|
|
8221
9978
|
}
|
|
8222
9979
|
if (!sessionBuffers.has(event.session_id)) {
|
|
8223
|
-
const bufferDir =
|
|
9980
|
+
const bufferDir = path23.join(vaultDir, "buffer");
|
|
8224
9981
|
sessionBuffers.set(event.session_id, new EventBuffer(bufferDir, event.session_id));
|
|
8225
9982
|
}
|
|
8226
9983
|
sessionBuffers.get(event.session_id).append(event);
|
|
@@ -8246,6 +10003,29 @@ function createEventDispatcher(deps) {
|
|
|
8246
10003
|
try {
|
|
8247
10004
|
const { batchId, promptNumber } = handleUserPrompt(event.session_id, promptText || void 0);
|
|
8248
10005
|
logger.debug(LOG_KINDS.CAPTURE_BATCH, "Batch opened", { session_id: event.session_id, batch_id: batchId, prompt_number: promptNumber });
|
|
10006
|
+
const taggedPlans = extractTaggedPlans(promptText, getPlanTagsForAgent(event.agent));
|
|
10007
|
+
for (const { tag, content } of taggedPlans) {
|
|
10008
|
+
try {
|
|
10009
|
+
captureTaggedPlan({
|
|
10010
|
+
tag,
|
|
10011
|
+
content,
|
|
10012
|
+
sessionId: event.session_id,
|
|
10013
|
+
promptBatchId: batchId,
|
|
10014
|
+
logger
|
|
10015
|
+
});
|
|
10016
|
+
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured from prompt tag", {
|
|
10017
|
+
session_id: event.session_id,
|
|
10018
|
+
tag,
|
|
10019
|
+
content_length: content.length
|
|
10020
|
+
});
|
|
10021
|
+
} catch (err) {
|
|
10022
|
+
logger.warn(LOG_KINDS.CAPTURE_PLAN, "Failed to capture plan from prompt tag", {
|
|
10023
|
+
session_id: event.session_id,
|
|
10024
|
+
tag,
|
|
10025
|
+
error: err.message
|
|
10026
|
+
});
|
|
10027
|
+
}
|
|
10028
|
+
}
|
|
8249
10029
|
const eventImages = event.images;
|
|
8250
10030
|
if (Array.isArray(eventImages) && eventImages.length > 0) {
|
|
8251
10031
|
captureBatchImages({
|
|
@@ -8279,13 +10059,15 @@ function createEventDispatcher(deps) {
|
|
|
8279
10059
|
);
|
|
8280
10060
|
if (planFilePath) {
|
|
8281
10061
|
const captureSessionId = event.session_id;
|
|
8282
|
-
|
|
10062
|
+
fs23.promises.readFile(planFilePath, "utf-8").then((planContent) => {
|
|
8283
10063
|
const latestBatch = getLatestBatch(captureSessionId);
|
|
8284
10064
|
capturePlan({
|
|
8285
|
-
sourcePath:
|
|
10065
|
+
sourcePath: planFilePath,
|
|
10066
|
+
projectRoot,
|
|
8286
10067
|
content: planContent,
|
|
8287
10068
|
sessionId: captureSessionId,
|
|
8288
|
-
promptBatchId: latestBatch?.id ?? null
|
|
10069
|
+
promptBatchId: latestBatch?.id ?? null,
|
|
10070
|
+
logger
|
|
8289
10071
|
});
|
|
8290
10072
|
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured", {
|
|
8291
10073
|
session_id: captureSessionId,
|
|
@@ -8445,8 +10227,8 @@ function createConfigReactionRegistry(logger) {
|
|
|
8445
10227
|
function shouldFire(registeredPaths, touched) {
|
|
8446
10228
|
if (registeredPaths.length === 0) return true;
|
|
8447
10229
|
for (const prefix of registeredPaths) {
|
|
8448
|
-
for (const
|
|
8449
|
-
if (
|
|
10230
|
+
for (const path25 of touched) {
|
|
10231
|
+
if (path25 === prefix || path25.startsWith(`${prefix}.`)) return true;
|
|
8450
10232
|
}
|
|
8451
10233
|
}
|
|
8452
10234
|
return false;
|
|
@@ -8480,13 +10262,13 @@ function createPlanWatchReaction(deps) {
|
|
|
8480
10262
|
}
|
|
8481
10263
|
|
|
8482
10264
|
// src/daemon/main.ts
|
|
8483
|
-
import
|
|
8484
|
-
import
|
|
10265
|
+
import fs24 from "fs";
|
|
10266
|
+
import path24 from "path";
|
|
8485
10267
|
function killStaleDaemon(vaultDir, logger) {
|
|
8486
|
-
const daemonJsonPath =
|
|
10268
|
+
const daemonJsonPath = path24.join(vaultDir, "daemon.json");
|
|
8487
10269
|
try {
|
|
8488
|
-
if (!
|
|
8489
|
-
const info = JSON.parse(
|
|
10270
|
+
if (!fs24.existsSync(daemonJsonPath)) return;
|
|
10271
|
+
const info = JSON.parse(fs24.readFileSync(daemonJsonPath, "utf-8"));
|
|
8490
10272
|
if (!info.pid) return;
|
|
8491
10273
|
if (info.pid === process.pid) return;
|
|
8492
10274
|
try {
|
|
@@ -8495,7 +10277,7 @@ function killStaleDaemon(vaultDir, logger) {
|
|
|
8495
10277
|
logger.info(LOG_KINDS.DAEMON_START, "Killed stale daemon", { pid: info.pid });
|
|
8496
10278
|
} catch {
|
|
8497
10279
|
}
|
|
8498
|
-
|
|
10280
|
+
fs24.unlinkSync(daemonJsonPath);
|
|
8499
10281
|
} catch {
|
|
8500
10282
|
}
|
|
8501
10283
|
}
|
|
@@ -8505,7 +10287,7 @@ async function main() {
|
|
|
8505
10287
|
process.stderr.write("Usage: mycod --vault <path>\n");
|
|
8506
10288
|
process.exit(1);
|
|
8507
10289
|
}
|
|
8508
|
-
const vaultDir =
|
|
10290
|
+
const vaultDir = path24.resolve(vaultArg);
|
|
8509
10291
|
loadSecrets(vaultDir);
|
|
8510
10292
|
const config = loadMergedConfig(vaultDir);
|
|
8511
10293
|
const liveConfig = { current: config };
|
|
@@ -8518,7 +10300,7 @@ async function main() {
|
|
|
8518
10300
|
projectRoot,
|
|
8519
10301
|
extensions: config.capture.artifact_extensions
|
|
8520
10302
|
};
|
|
8521
|
-
const logger = new DaemonLogger(
|
|
10303
|
+
const logger = new DaemonLogger(path24.join(vaultDir, "logs"), {
|
|
8522
10304
|
level: config.daemon.log_level
|
|
8523
10305
|
});
|
|
8524
10306
|
if (config.daemon.log_level === "debug") {
|
|
@@ -8547,7 +10329,7 @@ async function main() {
|
|
|
8547
10329
|
const devCliEntry = detectDevBuild(
|
|
8548
10330
|
globalPrefix,
|
|
8549
10331
|
process.argv[1],
|
|
8550
|
-
|
|
10332
|
+
fs24.realpathSync
|
|
8551
10333
|
);
|
|
8552
10334
|
if (devCliEntry) {
|
|
8553
10335
|
setDevBuildCliEntry(devCliEntry);
|
|
@@ -8559,13 +10341,19 @@ async function main() {
|
|
|
8559
10341
|
const db = initDatabase(vaultDbPath(vaultDir));
|
|
8560
10342
|
createSchema(db, machineId);
|
|
8561
10343
|
registerBuiltinDomains();
|
|
10344
|
+
const interruptedRuns = markRunningRunsInterrupted("Daemon restarted before the run completed");
|
|
10345
|
+
if (interruptedRuns > 0) {
|
|
10346
|
+
logger.warn(LOG_KINDS.AGENT_RUN, "Marked stale running runs as resumable after daemon restart", {
|
|
10347
|
+
count: interruptedRuns
|
|
10348
|
+
});
|
|
10349
|
+
}
|
|
8562
10350
|
logger.info(LOG_KINDS.DAEMON_START, "SQLite initialized", { vault: vaultDir });
|
|
8563
10351
|
{
|
|
8564
|
-
const reasonPath =
|
|
10352
|
+
const reasonPath = path24.join(vaultDir, RESTART_REASON_FILENAME);
|
|
8565
10353
|
try {
|
|
8566
|
-
if (
|
|
8567
|
-
const raw = JSON.parse(
|
|
8568
|
-
|
|
10354
|
+
if (fs24.existsSync(reasonPath)) {
|
|
10355
|
+
const raw = JSON.parse(fs24.readFileSync(reasonPath, "utf-8"));
|
|
10356
|
+
fs24.unlinkSync(reasonPath);
|
|
8569
10357
|
if (raw.reason === "version_sync" && raw.to_version) {
|
|
8570
10358
|
const message = raw.local_update_ran ? "Restarted and updated local project hooks." : "Restarted to pick up the latest version.";
|
|
8571
10359
|
notify(vaultDir, {
|
|
@@ -8607,13 +10395,13 @@ async function main() {
|
|
|
8607
10395
|
});
|
|
8608
10396
|
const lastLogTimestamp = getMaxTimestamp();
|
|
8609
10397
|
if (lastLogTimestamp) {
|
|
8610
|
-
const logDir =
|
|
10398
|
+
const logDir = path24.join(vaultDir, "logs");
|
|
8611
10399
|
const replayedCount = reconcileLogBuffer(logDir, lastLogTimestamp);
|
|
8612
10400
|
if (replayedCount > 0) {
|
|
8613
10401
|
logger.info(LOG_KINDS.DAEMON_RECONCILE, `Replayed ${replayedCount} log entries from buffer`, { replayed: replayedCount });
|
|
8614
10402
|
}
|
|
8615
10403
|
}
|
|
8616
|
-
const vectorsDbPath =
|
|
10404
|
+
const vectorsDbPath = path24.join(vaultDir, "vectors.db");
|
|
8617
10405
|
const vectorStore = new SqliteVecVectorStore(vectorsDbPath);
|
|
8618
10406
|
const llmProvider = createEmbeddingProvider(config.embedding);
|
|
8619
10407
|
const embeddingProvider = new EmbeddingProviderAdapter(llmProvider, config.embedding);
|
|
@@ -8623,7 +10411,7 @@ async function main() {
|
|
|
8623
10411
|
const databaseManager = new DatabaseMaintenanceManager(vaultDbPath(vaultDir), vaultDir, logger);
|
|
8624
10412
|
let definitionsDir;
|
|
8625
10413
|
try {
|
|
8626
|
-
const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-
|
|
10414
|
+
const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-UDNUMEDA.js");
|
|
8627
10415
|
definitionsDir = resolveDefinitionsDir2();
|
|
8628
10416
|
await registerBuiltInAgentsAndTasks(definitionsDir, vaultDir);
|
|
8629
10417
|
logger.info(LOG_KINDS.AGENT_TASK, "Built-in agents and tasks registered");
|
|
@@ -8660,10 +10448,10 @@ async function main() {
|
|
|
8660
10448
|
}
|
|
8661
10449
|
let uiDir = null;
|
|
8662
10450
|
{
|
|
8663
|
-
const root = findPackageRoot(
|
|
10451
|
+
const root = findPackageRoot(path24.dirname(new URL(import.meta.url).pathname));
|
|
8664
10452
|
if (root) {
|
|
8665
|
-
const candidate =
|
|
8666
|
-
if (
|
|
10453
|
+
const candidate = path24.join(root, "dist", "ui");
|
|
10454
|
+
if (fs24.existsSync(candidate)) uiDir = candidate;
|
|
8667
10455
|
}
|
|
8668
10456
|
}
|
|
8669
10457
|
if (uiDir) {
|
|
@@ -8677,6 +10465,7 @@ async function main() {
|
|
|
8677
10465
|
sleepIntervalMs: POWER_SLEEP_INTERVAL_MS,
|
|
8678
10466
|
logger
|
|
8679
10467
|
});
|
|
10468
|
+
const inflightRuns = new InflightRunRegistry();
|
|
8680
10469
|
const server = new DaemonServer({
|
|
8681
10470
|
vaultDir,
|
|
8682
10471
|
logger,
|
|
@@ -8696,7 +10485,7 @@ async function main() {
|
|
|
8696
10485
|
(p) => createPerProjectAdapter(p, claudeCodeAdapter.parseTurns)
|
|
8697
10486
|
)
|
|
8698
10487
|
});
|
|
8699
|
-
const bufferDir =
|
|
10488
|
+
const bufferDir = path24.join(vaultDir, "buffer");
|
|
8700
10489
|
const sessionBuffers = /* @__PURE__ */ new Map();
|
|
8701
10490
|
const reconciler = createReconciler({ bufferDir, logger });
|
|
8702
10491
|
reconciler.runStartupReconciliation();
|
|
@@ -8708,7 +10497,8 @@ async function main() {
|
|
|
8708
10497
|
logger,
|
|
8709
10498
|
liveConfig,
|
|
8710
10499
|
vaultDir,
|
|
8711
|
-
planTags: symbiontPlanTags
|
|
10500
|
+
planTags: symbiontPlanTags,
|
|
10501
|
+
planWatchConfig
|
|
8712
10502
|
});
|
|
8713
10503
|
const sessionLifecycle = createSessionLifecycleHandlers({
|
|
8714
10504
|
registry,
|
|
@@ -8738,14 +10528,32 @@ async function main() {
|
|
|
8738
10528
|
});
|
|
8739
10529
|
server.registerRoute("POST", "/events", eventDispatcher);
|
|
8740
10530
|
server.registerRoute("POST", "/events/stop", stopProcessor.handleStopRoute);
|
|
8741
|
-
|
|
10531
|
+
let teamSync;
|
|
10532
|
+
const contextDeps = {
|
|
10533
|
+
vaultDir,
|
|
10534
|
+
embeddingManager,
|
|
10535
|
+
liveConfig,
|
|
10536
|
+
logger,
|
|
10537
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10538
|
+
};
|
|
8742
10539
|
server.registerRoute("POST", "/context", createSessionContextHandler(contextDeps));
|
|
8743
10540
|
server.registerRoute("POST", "/context/resume", createResumeContextHandler(contextDeps));
|
|
8744
10541
|
server.registerRoute("POST", "/context/prompt", createPromptContextHandler(contextDeps));
|
|
8745
10542
|
const progressTracker = new ProgressTracker();
|
|
8746
10543
|
let configHash = computeConfigHash(vaultDir);
|
|
10544
|
+
const cortexHandlers = createCortexHandlers(vaultDir, {
|
|
10545
|
+
liveConfig,
|
|
10546
|
+
embeddingManager,
|
|
10547
|
+
logger,
|
|
10548
|
+
getTeamClient: () => teamSync.getTeamClient(),
|
|
10549
|
+
registerInflightRun: (p) => inflightRuns.register(p)
|
|
10550
|
+
});
|
|
8747
10551
|
server.registerRoute("GET", "/api/config", async () => handleGetConfig(vaultDir));
|
|
8748
10552
|
server.registerRoute("GET", "/api/symbionts", async () => handleListSymbionts(vaultDir));
|
|
10553
|
+
server.registerRoute("GET", "/api/cortex/instructions", cortexHandlers.handleGetInstructions);
|
|
10554
|
+
server.registerRoute("POST", "/api/cortex/instructions/refresh", cortexHandlers.handleRefreshInstructions);
|
|
10555
|
+
server.registerRoute("POST", "/api/cortex/prompt-builder", cortexHandlers.handleBuildPrompt);
|
|
10556
|
+
server.registerRoute("GET", "/api/cortex/prompt-builder/:runId", cortexHandlers.handleGetPromptResult);
|
|
8749
10557
|
server.registerRoute("GET", "/api/config/merged", async () => handleGetMergedConfig(vaultDir));
|
|
8750
10558
|
server.registerRoute("GET", "/api/config/local", async () => handleGetLocalConfig(vaultDir));
|
|
8751
10559
|
const symbiontPlanDirsByAgent = {};
|
|
@@ -8761,7 +10569,7 @@ async function main() {
|
|
|
8761
10569
|
liveConfig.current = ctx;
|
|
8762
10570
|
});
|
|
8763
10571
|
reactions.on(["capture", "symbionts"], (ctx) => {
|
|
8764
|
-
reconcileConfiguredSymbionts(
|
|
10572
|
+
reconcileConfiguredSymbionts(path24.dirname(vaultDir), vaultDir, ctx);
|
|
8765
10573
|
});
|
|
8766
10574
|
reactions.on(["capture"], createPlanWatchReaction({
|
|
8767
10575
|
symbiontPlanDirs,
|
|
@@ -8776,7 +10584,14 @@ async function main() {
|
|
|
8776
10584
|
}
|
|
8777
10585
|
});
|
|
8778
10586
|
async function syncScheduledTasks() {
|
|
8779
|
-
await registerScheduledTasks(powerManager, {
|
|
10587
|
+
await registerScheduledTasks(powerManager, {
|
|
10588
|
+
definitionsDir,
|
|
10589
|
+
vaultDir,
|
|
10590
|
+
embeddingManager,
|
|
10591
|
+
logger,
|
|
10592
|
+
liveConfig,
|
|
10593
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10594
|
+
});
|
|
8780
10595
|
}
|
|
8781
10596
|
reactions.on(["agent.tasks"], async () => {
|
|
8782
10597
|
await syncScheduledTasks();
|
|
@@ -8828,9 +10643,9 @@ async function main() {
|
|
|
8828
10643
|
server.registerRoute("GET", "/api/logs/stream", handleLogStream);
|
|
8829
10644
|
server.registerRoute("GET", "/api/logs/:id", handleLogDetail);
|
|
8830
10645
|
server.registerRoute("POST", "/api/log", createLogIngestionHandler(logger));
|
|
8831
|
-
server.registerRoute("GET", "/api/models", async (req) => handleGetModels(req));
|
|
10646
|
+
server.registerRoute("GET", "/api/models", async (req) => handleGetModels(req, logger));
|
|
8832
10647
|
server.registerRoute("POST", "/api/restart", async (req) => handleRestart({ vaultDir, progressTracker }, req.body));
|
|
8833
|
-
const updateProjectRoot =
|
|
10648
|
+
const updateProjectRoot = path24.dirname(vaultDir);
|
|
8834
10649
|
const updateHandlers = createUpdateHandlers({
|
|
8835
10650
|
vaultDir,
|
|
8836
10651
|
projectRoot: updateProjectRoot,
|
|
@@ -8853,6 +10668,7 @@ async function main() {
|
|
|
8853
10668
|
server.registerRoute("GET", "/api/sessions/:id/impact", sessionMutations.handleGetSessionImpact);
|
|
8854
10669
|
server.registerRoute("POST", "/api/sessions/:id/complete", sessionMutations.handleCompleteSession);
|
|
8855
10670
|
server.registerRoute("DELETE", "/api/sessions/:id", sessionMutations.handleDeleteSession);
|
|
10671
|
+
server.registerRoute("DELETE", "/api/plans/:id", sessionMutations.handleDeletePlan);
|
|
8856
10672
|
server.registerRoute("GET", "/api/sessions/:id/batches", handleGetSessionBatches);
|
|
8857
10673
|
server.registerRoute("GET", "/api/batches/:id/activities", handleGetBatchActivities);
|
|
8858
10674
|
server.registerRoute("GET", "/api/sessions/:id/attachments", handleGetSessionAttachments);
|
|
@@ -8873,12 +10689,27 @@ async function main() {
|
|
|
8873
10689
|
server.registerRoute("GET", "/api/digest", handleGetDigest);
|
|
8874
10690
|
const attachments = createAttachmentHandler({ vaultDir });
|
|
8875
10691
|
server.registerRoute("GET", "/api/attachments/:filename", attachments.handleGetAttachment);
|
|
8876
|
-
const agentRunHandlers = createAgentRunHandlers({
|
|
10692
|
+
const agentRunHandlers = createAgentRunHandlers({
|
|
10693
|
+
vaultDir,
|
|
10694
|
+
embeddingManager,
|
|
10695
|
+
logger,
|
|
10696
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10697
|
+
});
|
|
8877
10698
|
server.registerRoute("POST", "/api/agent/run", agentRunHandlers.handleRun);
|
|
8878
10699
|
server.registerRoute("GET", "/api/agent/runs", agentRunHandlers.handleListRuns);
|
|
8879
10700
|
server.registerRoute("GET", "/api/agent/runs/:id", agentRunHandlers.handleGetRun);
|
|
10701
|
+
server.registerRoute("POST", "/api/agent/runs/:id/resume", agentRunHandlers.handleResumeRun);
|
|
8880
10702
|
server.registerRoute("GET", "/api/agent/runs/:id/reports", agentRunHandlers.handleGetRunReports);
|
|
8881
10703
|
server.registerRoute("GET", "/api/agent/runs/:id/turns", agentRunHandlers.handleGetRunTurns);
|
|
10704
|
+
server.registerRoute("GET", "/api/agent/runs/:id/write-intents", agentRunHandlers.handleGetRunWriteIntents);
|
|
10705
|
+
server.registerRoute("GET", "/api/agent/runs/:id/audit", agentRunHandlers.handleGetRunAudit);
|
|
10706
|
+
const agentEvalHandlers = createAgentEvaluationHandlers({ vaultDir, embeddingManager, logger });
|
|
10707
|
+
server.registerRoute("POST", "/api/agent/evaluations", agentEvalHandlers.handleCreate);
|
|
10708
|
+
server.registerRoute("GET", "/api/agent/evaluations", agentEvalHandlers.handleList);
|
|
10709
|
+
server.registerRoute("GET", "/api/agent/evaluations/:id", agentEvalHandlers.handleGet);
|
|
10710
|
+
const digestRevisionHandlers = createDigestRevisionHandlers({ vaultDir, logger });
|
|
10711
|
+
server.registerRoute("GET", "/api/digest/revisions", digestRevisionHandlers.handleList);
|
|
10712
|
+
server.registerRoute("POST", "/api/digest/revisions/:id/restore", digestRevisionHandlers.handleRestore);
|
|
8882
10713
|
server.registerRoute("GET", "/api/agent/tasks", async (req) => handleListTasks(req, vaultDir));
|
|
8883
10714
|
server.registerRoute("GET", "/api/agent/tasks/:id", async (req) => handleGetTask(req, vaultDir));
|
|
8884
10715
|
server.registerRoute("GET", "/api/agent/tasks/:id/yaml", async (req) => handleGetTaskYaml(req, vaultDir));
|
|
@@ -8918,13 +10749,17 @@ async function main() {
|
|
|
8918
10749
|
}
|
|
8919
10750
|
return result;
|
|
8920
10751
|
});
|
|
8921
|
-
server.registerRoute("GET", "/api/providers", async () => handleGetProviders());
|
|
10752
|
+
server.registerRoute("GET", "/api/providers", async () => handleGetProviders(logger));
|
|
8922
10753
|
server.registerRoute("POST", "/api/providers/test", async (req) => handleTestProvider(req));
|
|
8923
|
-
|
|
10754
|
+
server.registerRoute("GET", "/api/providers/secrets", async () => handleGetProviderSecrets(vaultDir));
|
|
10755
|
+
server.registerRoute("PUT", "/api/providers/secrets/:provider", async (req) => handlePutProviderSecret(vaultDir, req));
|
|
10756
|
+
server.registerRoute("DELETE", "/api/providers/secrets/:provider", async (req) => handleDeleteProviderSecret(vaultDir, req));
|
|
10757
|
+
const mcpProxy = createMcpProxyHandlers({ machineId, embeddingManager, projectRoot, logger });
|
|
8924
10758
|
server.registerRoute("POST", "/api/mcp/remember", mcpProxy.handleRemember);
|
|
8925
10759
|
server.registerRoute("POST", "/api/mcp/supersede", mcpProxy.handleSupersede);
|
|
8926
10760
|
server.registerRoute("POST", "/api/mcp/consolidate", mcpProxy.handleConsolidate);
|
|
8927
10761
|
server.registerRoute("GET", "/api/mcp/plans", mcpProxy.handlePlans);
|
|
10762
|
+
server.registerRoute("POST", "/api/mcp/plans", mcpProxy.handleSavePlan);
|
|
8928
10763
|
server.registerRoute("GET", "/api/mcp/sessions", mcpProxy.handleSessions);
|
|
8929
10764
|
server.registerRoute("GET", "/api/mcp/team", mcpProxy.handleTeam);
|
|
8930
10765
|
const backupHandlers = createBackupHandlers({ db, machineId, vaultDir, liveConfig });
|
|
@@ -8941,7 +10776,7 @@ async function main() {
|
|
|
8941
10776
|
}
|
|
8942
10777
|
return result;
|
|
8943
10778
|
});
|
|
8944
|
-
|
|
10779
|
+
teamSync = initTeamSync({ liveConfig, machineId, logger, vaultDir, serverVersion: server.version });
|
|
8945
10780
|
reactions.on(["team"], async () => {
|
|
8946
10781
|
await teamSync.reconcileClient();
|
|
8947
10782
|
});
|
|
@@ -8950,7 +10785,7 @@ async function main() {
|
|
|
8950
10785
|
vaultDir,
|
|
8951
10786
|
machineId,
|
|
8952
10787
|
logger,
|
|
8953
|
-
getTeamClient: teamSync.getTeamClient,
|
|
10788
|
+
getTeamClient: () => teamSync.getTeamClient(),
|
|
8954
10789
|
setTeamClient: teamSync.setTeamClient
|
|
8955
10790
|
});
|
|
8956
10791
|
server.registerRoute("POST", "/api/team/connect", async (req) => {
|
|
@@ -8973,14 +10808,14 @@ async function main() {
|
|
|
8973
10808
|
server.registerRoute("POST", "/api/team/upgrade-worker", teamHandlers.handleUpgradeWorker);
|
|
8974
10809
|
server.registerRoute("POST", "/api/team/rotate-mcp-token", teamHandlers.handleRotateMcpToken);
|
|
8975
10810
|
const collectiveHandlers = createCollectiveHandlers({
|
|
8976
|
-
getTeamClient: teamSync.getTeamClient
|
|
10811
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
8977
10812
|
});
|
|
8978
10813
|
server.registerRoute("GET", "/api/collective/status", collectiveHandlers.handleStatus);
|
|
8979
10814
|
server.registerRoute("GET", "/api/collective/search", collectiveHandlers.handleSearch);
|
|
8980
10815
|
server.registerRoute("GET", "/api/collective/projects", collectiveHandlers.handleProjects);
|
|
8981
10816
|
server.registerRoute("GET", "/api/collective/project", collectiveHandlers.handleProject);
|
|
8982
10817
|
server.registerRoute("GET", "/api/collective/settings", collectiveHandlers.handleSettings);
|
|
8983
|
-
server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: teamSync.getTeamClient, machineId }));
|
|
10818
|
+
server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: () => teamSync.getTeamClient(), machineId }));
|
|
8984
10819
|
server.registerRoute("GET", "/api/activity", handleGetFeed);
|
|
8985
10820
|
server.registerRoute("GET", "/api/embedding/status", async () => handleGetEmbeddingStatus(vaultDir));
|
|
8986
10821
|
server.registerRoute("GET", "/api/embedding/details", async () => handleEmbeddingDetails(embeddingManager));
|
|
@@ -9018,9 +10853,25 @@ async function main() {
|
|
|
9018
10853
|
logger.warn(LOG_KINDS.DAEMON_CONFIG, "Failed to persist auto-derived port", { error: err.message });
|
|
9019
10854
|
}
|
|
9020
10855
|
}
|
|
9021
|
-
registerPowerJobs(powerManager, {
|
|
10856
|
+
registerPowerJobs(powerManager, {
|
|
10857
|
+
embeddingManager,
|
|
10858
|
+
registry,
|
|
10859
|
+
logger,
|
|
10860
|
+
liveConfig,
|
|
10861
|
+
db,
|
|
10862
|
+
machineId,
|
|
10863
|
+
vaultDir,
|
|
10864
|
+
databaseManager
|
|
10865
|
+
});
|
|
9022
10866
|
teamSync.registerFlushJob(powerManager);
|
|
9023
|
-
await registerScheduledTasks(powerManager, {
|
|
10867
|
+
await registerScheduledTasks(powerManager, {
|
|
10868
|
+
definitionsDir,
|
|
10869
|
+
vaultDir,
|
|
10870
|
+
embeddingManager,
|
|
10871
|
+
logger,
|
|
10872
|
+
liveConfig,
|
|
10873
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10874
|
+
});
|
|
9024
10875
|
powerManager.start();
|
|
9025
10876
|
const shutdown = async (signal) => {
|
|
9026
10877
|
logger.info(LOG_KINDS.DAEMON_START, `${signal} received`);
|
|
@@ -9030,6 +10881,17 @@ async function main() {
|
|
|
9030
10881
|
logger.info(LOG_KINDS.DAEMON_START, "Waiting for active stop processing to complete...");
|
|
9031
10882
|
await activeStopProcessing;
|
|
9032
10883
|
}
|
|
10884
|
+
if (inflightRuns.size > 0) {
|
|
10885
|
+
logger.info(LOG_KINDS.DAEMON_START, "Draining in-flight agent runs before shutdown...", {
|
|
10886
|
+
inflight_count: inflightRuns.size
|
|
10887
|
+
});
|
|
10888
|
+
const outcome = await inflightRuns.drain();
|
|
10889
|
+
if (!outcome.settled) {
|
|
10890
|
+
logger.warn(LOG_KINDS.DAEMON_START, "Some in-flight runs did not settle before shutdown timeout", {
|
|
10891
|
+
remaining: outcome.remaining
|
|
10892
|
+
});
|
|
10893
|
+
}
|
|
10894
|
+
}
|
|
9033
10895
|
registry.destroy();
|
|
9034
10896
|
await server.stop();
|
|
9035
10897
|
vectorStore.close();
|
|
@@ -9052,4 +10914,4 @@ export {
|
|
|
9052
10914
|
handleUserPrompt,
|
|
9053
10915
|
main
|
|
9054
10916
|
};
|
|
9055
|
-
//# sourceMappingURL=main-
|
|
10917
|
+
//# sourceMappingURL=main-4J4QZZTZ.js.map
|