@goondocks/myco 0.20.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-eval-RJSQI5S2.js +355 -0
- package/dist/agent-eval-RJSQI5S2.js.map +1 -0
- package/dist/{agent-run-PQXC246Y.js → agent-run-2JSYFOKU.js} +10 -8
- package/dist/{agent-run-PQXC246Y.js.map → agent-run-2JSYFOKU.js.map} +1 -1
- package/dist/{agent-tasks-323BZEBX.js → agent-tasks-APFJIM2T.js} +10 -8
- package/dist/{agent-tasks-323BZEBX.js.map → agent-tasks-APFJIM2T.js.map} +1 -1
- package/dist/{chunk-SRXTSI25.js → chunk-53RPGOEN.js} +57 -9
- 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-DCSGJ7W4.js → chunk-5ZG4RMUH.js} +2 -2
- package/dist/{chunk-FLLBJLHM.js → chunk-6C6QZ4PM.js} +9 -5
- package/dist/chunk-6C6QZ4PM.js.map +1 -0
- package/dist/chunk-6LB7XELY.js +406 -0
- package/dist/chunk-6LB7XELY.js.map +1 -0
- package/dist/{chunk-22JALAXN.js → chunk-75Z7UKDY.js} +4 -4
- package/dist/{chunk-AIYFHQRX.js → chunk-BUTL6IFS.js} +2 -2
- package/dist/chunk-CESKJD44.js +586 -0
- package/dist/chunk-CESKJD44.js.map +1 -0
- package/dist/chunk-CISWUP5W.js +101 -0
- package/dist/chunk-CISWUP5W.js.map +1 -0
- package/dist/chunk-DJ3IHNYO.js +50 -0
- package/dist/chunk-DJ3IHNYO.js.map +1 -0
- package/dist/chunk-F3OEQYLS.js +847 -0
- package/dist/chunk-F3OEQYLS.js.map +1 -0
- 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-63ZGP4Q2.js → chunk-G6QIBNZM.js} +9 -6
- package/dist/{chunk-63ZGP4Q2.js.map → chunk-G6QIBNZM.js.map} +1 -1
- package/dist/{chunk-57O67XVF.js → chunk-ILJPRYES.js} +6 -4
- package/dist/{chunk-57O67XVF.js.map → chunk-ILJPRYES.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-UEWMSIL3.js → chunk-JR54LTPP.js} +4 -4
- package/dist/{chunk-4M7EWPIA.js → chunk-JZS6GZ6T.js} +16 -93
- package/dist/chunk-JZS6GZ6T.js.map +1 -0
- package/dist/{chunk-5XIVBO25.js → chunk-LVIY7P35.js} +2 -2
- package/dist/chunk-NGH7U6A3.js +13844 -0
- package/dist/chunk-NGH7U6A3.js.map +1 -0
- package/dist/chunk-OUJSQSKE.js +113 -0
- package/dist/chunk-OUJSQSKE.js.map +1 -0
- package/dist/{chunk-3WWJOTYG.js → chunk-P66DLD6G.js} +22 -10
- package/dist/chunk-P66DLD6G.js.map +1 -0
- package/dist/{chunk-QS5TWZBL.js → chunk-R2JIJBCL.js} +18 -4
- package/dist/{chunk-QS5TWZBL.js.map → chunk-R2JIJBCL.js.map} +1 -1
- package/dist/{chunk-MYOZLMB2.js → chunk-RL5R4CQU.js} +538 -19
- package/dist/chunk-RL5R4CQU.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-LZP4IJB3.js → chunk-VHNRMM4O.js} +3 -2
- 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/{cli-UWBAOHLL.js → cli-LNYSTDQM.js} +49 -42
- package/dist/cli-LNYSTDQM.js.map +1 -0
- package/dist/{client-SLDDMSKN.js → client-NWE4TCNO.js} +4 -4
- package/dist/{config-XPV5GDE4.js → config-VC4ACP42.js} +6 -4
- package/dist/{config-XPV5GDE4.js.map → config-VC4ACP42.js.map} +1 -1
- package/dist/{detect-providers-5KOPZ7J2.js → detect-providers-ILLQZROY.js} +4 -4
- package/dist/{doctor-UUUOVDZS.js → doctor-TI7EZ3RW.js} +48 -15
- package/dist/doctor-TI7EZ3RW.js.map +1 -0
- package/dist/executor-F2YU7HXJ.js +44 -0
- package/dist/{init-UDH3ZBDD.js → init-KG3TYVGE.js} +14 -12
- package/dist/{init-UDH3ZBDD.js.map → init-KG3TYVGE.js.map} +1 -1
- package/dist/{installer-I6KRGJOO.js → installer-UMH7OJ5A.js} +6 -4
- package/dist/{llm-TH4NLIRM.js → llm-AGVEF5XD.js} +5 -4
- package/dist/{loader-H7OFASVC.js → loader-LX7TFRM6.js} +5 -3
- package/dist/{loader-TSB5M7FD.js → loader-NAVVZK63.js} +4 -3
- package/dist/{main-E7HU4QYR.js → main-5PRQNEEE.js} +2457 -649
- package/dist/main-5PRQNEEE.js.map +1 -0
- package/dist/{open-4UGDPBKN.js → open-5A27BCSB.js} +10 -8
- package/dist/{open-4UGDPBKN.js.map → open-5A27BCSB.js.map} +1 -1
- package/dist/{post-compact-BFU3WZSV.js → post-compact-USAODKPQ.js} +6 -6
- package/dist/{post-tool-use-RYHXLCTC.js → post-tool-use-GMMSYBII.js} +9 -7
- package/dist/post-tool-use-GMMSYBII.js.map +1 -0
- package/dist/{post-tool-use-failure-DCVHK2P3.js → post-tool-use-failure-NZVSL2PO.js} +6 -6
- package/dist/{pre-compact-EZZCJ6AG.js → pre-compact-LZ57DLUS.js} +6 -6
- package/dist/{provider-check-43LAMSMH.js → provider-check-ZEV5P4KM.js} +4 -4
- package/dist/{registry-MGJSJBAS.js → registry-M2Z5QBWH.js} +5 -4
- package/dist/{remove-GBBQG3SJ.js → remove-T3KE6C5N.js} +10 -8
- package/dist/{remove-GBBQG3SJ.js.map → remove-T3KE6C5N.js.map} +1 -1
- package/dist/{restart-RMIGKMA6.js → restart-YWDEVZUJ.js} +11 -9
- package/dist/{restart-RMIGKMA6.js.map → restart-YWDEVZUJ.js.map} +1 -1
- package/dist/{search-2A5AJVNG.js → search-GKFDGELR.js} +11 -9
- package/dist/{search-2A5AJVNG.js.map → search-GKFDGELR.js.map} +1 -1
- package/dist/{server-TSYZMFFK.js → server-AHUR6CWF.js} +368 -269
- package/dist/server-AHUR6CWF.js.map +1 -0
- package/dist/{session-NKREOTEO.js → session-2ZEPLWW6.js} +11 -9
- package/dist/{session-NKREOTEO.js.map → session-2ZEPLWW6.js.map} +1 -1
- package/dist/{session-end-DQELLTGJ.js → session-end-LWJYQAXX.js} +5 -5
- package/dist/session-start-WTA6GCOQ.js +134 -0
- package/dist/session-start-WTA6GCOQ.js.map +1 -0
- package/dist/{setup-llm-SLX5764H.js → setup-llm-E7UU5IO7.js} +11 -9
- package/dist/{setup-llm-SLX5764H.js.map → setup-llm-E7UU5IO7.js.map} +1 -1
- package/dist/src/agent/definitions/agent.yaml +9 -5
- package/dist/src/agent/definitions/tasks/cortex-instructions.yaml +93 -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 +5 -4
- package/dist/src/agent/definitions/tasks/skill-generate.yaml +2 -1
- package/dist/src/agent/definitions/tasks/skill-survey.yaml +25 -13
- 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} +18 -83
- 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/pi.yaml +22 -0
- package/dist/src/symbionts/templates/pi/package.json +6 -0
- package/dist/src/symbionts/templates/pi/plugin.ts +559 -0
- package/dist/{stats-ARBLNEE3.js → stats-DFG6S23S.js} +11 -9
- package/dist/{stats-ARBLNEE3.js.map → stats-DFG6S23S.js.map} +1 -1
- package/dist/{stop-5AJXBKEE.js → stop-WRBTXEVT.js} +5 -5
- package/dist/{stop-failure-XGKTZPLR.js → stop-failure-32MGIG2Q.js} +6 -6
- package/dist/{subagent-start-2JLIPGJN.js → subagent-start-VFGHQFVL.js} +6 -6
- package/dist/{subagent-stop-FV4EOKWZ.js → subagent-stop-663FXG3P.js} +6 -6
- package/dist/{task-completed-XJKT366I.js → task-completed-ZCQYEFMZ.js} +6 -6
- package/dist/{team-3JKF7VAD.js → team-JTI5CDUO.js} +7 -5
- package/dist/{turns-YFNI5CQC.js → turns-HU2CTZAP.js} +2 -2
- package/dist/ui/assets/index-DGf1h-Ha.js +842 -0
- package/dist/ui/assets/index-_OP4ifzH.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/{update-XVSAMN4O.js → update-3NBQTG32.js} +10 -8
- package/dist/{update-XVSAMN4O.js.map → update-3NBQTG32.js.map} +1 -1
- package/dist/{user-prompt-submit-TTBTHBBH.js → user-prompt-submit-ME2TBKOS.js} +8 -7
- package/dist/{user-prompt-submit-TTBTHBBH.js.map → user-prompt-submit-ME2TBKOS.js.map} +1 -1
- package/dist/{verify-LMHNEYIP.js → verify-R76ZFJSZ.js} +8 -5
- package/dist/{verify-LMHNEYIP.js.map → verify-R76ZFJSZ.js.map} +1 -1
- package/dist/{version-HRVSEMUR.js → version-GQAFBBPX.js} +2 -2
- package/dist/version-GQAFBBPX.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-curate/SKILL.md +1 -1
- package/dist/chunk-3WWJOTYG.js.map +0 -1
- package/dist/chunk-4M7EWPIA.js.map +0 -1
- package/dist/chunk-4YFKBL3F.js +0 -195
- package/dist/chunk-4YFKBL3F.js.map +0 -1
- package/dist/chunk-EVDQKYCG.js.map +0 -1
- package/dist/chunk-FGY7J6EZ.js +0 -2276
- package/dist/chunk-FGY7J6EZ.js.map +0 -1
- package/dist/chunk-FLLBJLHM.js.map +0 -1
- package/dist/chunk-MYOZLMB2.js.map +0 -1
- package/dist/chunk-SRXTSI25.js.map +0 -1
- package/dist/chunk-US4LNCAT.js.map +0 -1
- package/dist/cli-UWBAOHLL.js.map +0 -1
- package/dist/doctor-UUUOVDZS.js.map +0 -1
- package/dist/executor-J4IBXSBY.js +0 -2472
- package/dist/executor-J4IBXSBY.js.map +0 -1
- package/dist/main-E7HU4QYR.js.map +0 -1
- package/dist/post-tool-use-RYHXLCTC.js.map +0 -1
- package/dist/server-TSYZMFFK.js.map +0 -1
- package/dist/session-start-5SUTR5GP.js +0 -189
- package/dist/session-start-5SUTR5GP.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/dist/{chunk-DCSGJ7W4.js.map → chunk-5ZG4RMUH.js.map} +0 -0
- /package/dist/{chunk-22JALAXN.js.map → chunk-75Z7UKDY.js.map} +0 -0
- /package/dist/{chunk-AIYFHQRX.js.map → chunk-BUTL6IFS.js.map} +0 -0
- /package/dist/{chunk-UEWMSIL3.js.map → chunk-JR54LTPP.js.map} +0 -0
- /package/dist/{chunk-5XIVBO25.js.map → chunk-LVIY7P35.js.map} +0 -0
- /package/dist/{chunk-BPRIYNLE.js.map → chunk-TKAJ3JVF.js.map} +0 -0
- /package/dist/{chunk-LZP4IJB3.js.map → chunk-VHNRMM4O.js.map} +0 -0
- /package/dist/{client-SLDDMSKN.js.map → client-NWE4TCNO.js.map} +0 -0
- /package/dist/{detect-providers-5KOPZ7J2.js.map → detect-providers-ILLQZROY.js.map} +0 -0
- /package/dist/{installer-I6KRGJOO.js.map → executor-F2YU7HXJ.js.map} +0 -0
- /package/dist/{llm-TH4NLIRM.js.map → installer-UMH7OJ5A.js.map} +0 -0
- /package/dist/{loader-H7OFASVC.js.map → llm-AGVEF5XD.js.map} +0 -0
- /package/dist/{loader-TSB5M7FD.js.map → loader-LX7TFRM6.js.map} +0 -0
- /package/dist/{provider-check-43LAMSMH.js.map → loader-NAVVZK63.js.map} +0 -0
- /package/dist/{post-compact-BFU3WZSV.js.map → post-compact-USAODKPQ.js.map} +0 -0
- /package/dist/{post-tool-use-failure-DCVHK2P3.js.map → post-tool-use-failure-NZVSL2PO.js.map} +0 -0
- /package/dist/{pre-compact-EZZCJ6AG.js.map → pre-compact-LZ57DLUS.js.map} +0 -0
- /package/dist/{registry-MGJSJBAS.js.map → provider-check-ZEV5P4KM.js.map} +0 -0
- /package/dist/{team-3JKF7VAD.js.map → registry-M2Z5QBWH.js.map} +0 -0
- /package/dist/{session-end-DQELLTGJ.js.map → session-end-LWJYQAXX.js.map} +0 -0
- /package/dist/{stop-5AJXBKEE.js.map → stop-WRBTXEVT.js.map} +0 -0
- /package/dist/{stop-failure-XGKTZPLR.js.map → stop-failure-32MGIG2Q.js.map} +0 -0
- /package/dist/{subagent-start-2JLIPGJN.js.map → subagent-start-VFGHQFVL.js.map} +0 -0
- /package/dist/{subagent-stop-FV4EOKWZ.js.map → subagent-stop-663FXG3P.js.map} +0 -0
- /package/dist/{task-completed-XJKT366I.js.map → task-completed-ZCQYEFMZ.js.map} +0 -0
- /package/dist/{turns-YFNI5CQC.js.map → team-JTI5CDUO.js.map} +0 -0
- /package/dist/{version-HRVSEMUR.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,20 @@ import {
|
|
|
15
18
|
getEmbeddingQueueDepth,
|
|
16
19
|
getUnembedded,
|
|
17
20
|
markEmbedded
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-75Z7UKDY.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-R2JIJBCL.js";
|
|
27
31
|
import {
|
|
32
|
+
DEFAULT_OPENAI_URL,
|
|
33
|
+
DEFAULT_OPENROUTER_URL,
|
|
34
|
+
SKILL_SURVEY_TASK,
|
|
28
35
|
buildTaskInstruction,
|
|
29
36
|
closeOpenBatches,
|
|
30
37
|
countBatchesBySession,
|
|
@@ -32,67 +39,82 @@ import {
|
|
|
32
39
|
countNotifications,
|
|
33
40
|
countRuns,
|
|
34
41
|
countSkillRecords,
|
|
42
|
+
countWriteIntents,
|
|
43
|
+
countWriteIntentsByTool,
|
|
44
|
+
countWriteIntentsByToolForEvaluation,
|
|
35
45
|
createBatchLineage,
|
|
46
|
+
createLocalOpenAIBackend,
|
|
36
47
|
deleteCandidate,
|
|
37
48
|
deleteSkillRecordCascade,
|
|
38
49
|
dismissAllNotifications,
|
|
39
|
-
errorMessage,
|
|
40
50
|
findBatchByPromptPrefix,
|
|
41
51
|
getAllDomains,
|
|
42
52
|
getCandidate,
|
|
43
|
-
getDigestExtract,
|
|
44
|
-
getEntity,
|
|
45
53
|
getGraphForNode,
|
|
46
54
|
getLatestBatch,
|
|
55
|
+
getLatestOpenBatch,
|
|
56
|
+
getLatestResumableRunForTask,
|
|
47
57
|
getLatestRunId,
|
|
58
|
+
getLocalOpenAIBackendDefaultBaseUrl,
|
|
59
|
+
getLocalOpenAIBackendLabel,
|
|
48
60
|
getNotification,
|
|
49
61
|
getRun,
|
|
50
62
|
getSkillRecord,
|
|
51
63
|
getSkillRecordByName,
|
|
64
|
+
getSkillSurveyEligibility,
|
|
52
65
|
hasConfiguredProvider,
|
|
53
66
|
incrementActivityCount,
|
|
54
67
|
incrementSkillUsageCount,
|
|
68
|
+
inferLocalOpenAIBackendKind,
|
|
55
69
|
insertBatchStateless,
|
|
56
70
|
insertResolutionEvent,
|
|
57
71
|
isInstructionRequiredTask,
|
|
58
72
|
listBatchesBySession,
|
|
59
73
|
listCandidatesWithCount,
|
|
60
|
-
listDigestExtracts,
|
|
61
|
-
listEntities,
|
|
62
74
|
listLineageForSkill,
|
|
63
75
|
listNotifications,
|
|
64
76
|
listReports,
|
|
65
77
|
listRuns,
|
|
78
|
+
listRunsForEvaluation,
|
|
66
79
|
listSkillRecords,
|
|
67
80
|
listSkillRecordsWithCount,
|
|
81
|
+
listWriteIntentTools,
|
|
82
|
+
listWriteIntents,
|
|
68
83
|
markAllRead,
|
|
84
|
+
markRunningRunsInterrupted,
|
|
69
85
|
notify,
|
|
70
86
|
populateBatchResponses,
|
|
71
87
|
register,
|
|
88
|
+
runAgent,
|
|
72
89
|
setResponseSummary,
|
|
90
|
+
tryParseJson,
|
|
73
91
|
updateCandidate,
|
|
74
92
|
updateNotificationStatus
|
|
75
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-NGH7U6A3.js";
|
|
94
|
+
import {
|
|
95
|
+
errorMessage,
|
|
96
|
+
parseCheckpointState,
|
|
97
|
+
runDurationMs
|
|
98
|
+
} from "./chunk-6LB7XELY.js";
|
|
76
99
|
import {
|
|
77
100
|
fullTextSearch,
|
|
78
101
|
hydrateSearchResults
|
|
79
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-LVIY7P35.js";
|
|
80
103
|
import {
|
|
81
104
|
copyTaskToUser,
|
|
82
105
|
deleteUserTask,
|
|
83
106
|
loadAllTasks,
|
|
84
107
|
validateTaskName,
|
|
85
108
|
writeUserTask
|
|
86
|
-
} from "./chunk-
|
|
109
|
+
} from "./chunk-ILJPRYES.js";
|
|
87
110
|
import {
|
|
88
|
-
AgentTaskSchema,
|
|
89
111
|
registerAgent,
|
|
90
112
|
resolveDefinitionsDir,
|
|
91
113
|
taskFromParsed
|
|
92
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-JZS6GZ6T.js";
|
|
93
115
|
import {
|
|
94
116
|
listTurnsByRun
|
|
95
|
-
} from "./chunk-
|
|
117
|
+
} from "./chunk-FCJ5JV54.js";
|
|
96
118
|
import {
|
|
97
119
|
cleanupStagedSkill,
|
|
98
120
|
listStaleStagingDirs
|
|
@@ -100,7 +122,11 @@ import {
|
|
|
100
122
|
import {
|
|
101
123
|
Anthropic,
|
|
102
124
|
createEmbeddingProvider
|
|
103
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-YDUOSRGD.js";
|
|
126
|
+
import {
|
|
127
|
+
OPENAI_API_KEY_ENV,
|
|
128
|
+
OPENROUTER_API_KEY_ENV
|
|
129
|
+
} from "./chunk-CISWUP5W.js";
|
|
104
130
|
import {
|
|
105
131
|
getMachineId
|
|
106
132
|
} from "./chunk-ENWBFX7F.js";
|
|
@@ -109,26 +135,42 @@ import {
|
|
|
109
135
|
cleanStaleBuffers,
|
|
110
136
|
listBufferSessionIds
|
|
111
137
|
} from "./chunk-V7XG6V6C.js";
|
|
112
|
-
import "./chunk-
|
|
113
|
-
import "./chunk-UEWMSIL3.js";
|
|
138
|
+
import "./chunk-JR54LTPP.js";
|
|
114
139
|
import "./chunk-SAKJMNSR.js";
|
|
115
140
|
import {
|
|
116
141
|
SymbiontInstaller
|
|
117
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-VHNRMM4O.js";
|
|
118
143
|
import {
|
|
119
144
|
checkLocalProvider
|
|
120
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-TKAJ3JVF.js";
|
|
121
146
|
import {
|
|
122
147
|
LmStudioBackend,
|
|
123
148
|
OllamaBackend
|
|
124
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-X3IGT5RV.js";
|
|
150
|
+
import {
|
|
151
|
+
composeSessionStartContext,
|
|
152
|
+
shouldInjectSessionStartDigest
|
|
153
|
+
} from "./chunk-DJ3IHNYO.js";
|
|
125
154
|
import {
|
|
155
|
+
buildCortexInstructionsInput,
|
|
126
156
|
countSpores,
|
|
157
|
+
deletePlan,
|
|
158
|
+
getCortexInstructions,
|
|
159
|
+
getPlan,
|
|
160
|
+
getPlanByLogicalKey,
|
|
127
161
|
getSpore,
|
|
128
162
|
insertSpore,
|
|
163
|
+
listDigestExtracts,
|
|
164
|
+
listDigestRevisions,
|
|
165
|
+
listPlans,
|
|
166
|
+
listPlansBySession,
|
|
129
167
|
listSpores,
|
|
130
|
-
|
|
131
|
-
|
|
168
|
+
resolveInstructionDelivery,
|
|
169
|
+
rollbackDigestExtract,
|
|
170
|
+
shouldInjectCortex,
|
|
171
|
+
updateSporeStatus,
|
|
172
|
+
upsertPlan
|
|
173
|
+
} from "./chunk-F3OEQYLS.js";
|
|
132
174
|
import {
|
|
133
175
|
backfillUnsynced,
|
|
134
176
|
closeSession,
|
|
@@ -151,20 +193,29 @@ import {
|
|
|
151
193
|
pruneOld,
|
|
152
194
|
reactivateSessionIfCompleted,
|
|
153
195
|
retryDeadLettered,
|
|
154
|
-
syncRow,
|
|
155
196
|
updateSession,
|
|
156
197
|
upsertSession
|
|
157
|
-
} from "./chunk-
|
|
198
|
+
} from "./chunk-RQSJLWP4.js";
|
|
158
199
|
import {
|
|
159
200
|
evaluateSessionCaptureRules,
|
|
160
201
|
readTranscriptMeta
|
|
161
202
|
} from "./chunk-XL75KZGI.js";
|
|
203
|
+
import {
|
|
204
|
+
PLAN_STATUSES
|
|
205
|
+
} from "./chunk-CESKJD44.js";
|
|
162
206
|
import {
|
|
163
207
|
EMBEDDING_DIMENSIONS,
|
|
164
208
|
REST_SETTABLE_STATUSES,
|
|
165
209
|
SCHEMA_VERSION,
|
|
166
|
-
|
|
167
|
-
|
|
210
|
+
TRANSCRIPT_SOURCE_PREFIX,
|
|
211
|
+
buildPathPlanLogicalKey,
|
|
212
|
+
buildPlanId,
|
|
213
|
+
buildSessionPlanLogicalKey,
|
|
214
|
+
buildSessionTagPlanLogicalKey,
|
|
215
|
+
createSchema,
|
|
216
|
+
humanizePlanToken,
|
|
217
|
+
normalizePlanSourcePath
|
|
218
|
+
} from "./chunk-RL5R4CQU.js";
|
|
168
219
|
import {
|
|
169
220
|
CONFIG_FILENAME,
|
|
170
221
|
MycoConfigSchema,
|
|
@@ -177,7 +228,11 @@ import {
|
|
|
177
228
|
updateConfig,
|
|
178
229
|
updateLocalConfig,
|
|
179
230
|
updateTeamConfig
|
|
180
|
-
} from "./chunk-
|
|
231
|
+
} from "./chunk-53RPGOEN.js";
|
|
232
|
+
import {
|
|
233
|
+
AgentTaskSchema
|
|
234
|
+
} from "./chunk-OUJSQSKE.js";
|
|
235
|
+
import "./chunk-POEPHBQK.js";
|
|
181
236
|
import {
|
|
182
237
|
closeDatabase,
|
|
183
238
|
getDatabase,
|
|
@@ -189,10 +244,10 @@ import {
|
|
|
189
244
|
} from "./chunk-ZXZPJJN3.js";
|
|
190
245
|
import {
|
|
191
246
|
resolveCliEntryPath
|
|
192
|
-
} from "./chunk-
|
|
247
|
+
} from "./chunk-P66DLD6G.js";
|
|
193
248
|
import {
|
|
194
249
|
getPluginVersion
|
|
195
|
-
} from "./chunk-
|
|
250
|
+
} from "./chunk-BUTL6IFS.js";
|
|
196
251
|
import {
|
|
197
252
|
loadManifests,
|
|
198
253
|
resolvePackageRoot
|
|
@@ -208,6 +263,7 @@ import {
|
|
|
208
263
|
DEFAULT_AGENT_ID,
|
|
209
264
|
DEFAULT_LIST_LIMIT,
|
|
210
265
|
DEFAULT_RELEASE_CHANNEL,
|
|
266
|
+
DEFAULT_SYMBIONT_NAME,
|
|
211
267
|
EMBEDDING_BATCH_SIZE,
|
|
212
268
|
EXCLUDED_SPORE_STATUSES,
|
|
213
269
|
FEED_DEFAULT_LIMIT,
|
|
@@ -241,8 +297,10 @@ import {
|
|
|
241
297
|
SYNC_PROTOCOL_VERSION,
|
|
242
298
|
TEAM_API_KEY_SECRET,
|
|
243
299
|
TEAM_HEALTH_TIMEOUT_MS,
|
|
300
|
+
TEAM_REQUEST_TIMEOUT_MS,
|
|
244
301
|
TEAM_SEARCH_TIMEOUT_MS,
|
|
245
302
|
TEAM_SOURCE_PREFIX,
|
|
303
|
+
TEAM_SYNC_TIMEOUT_MS,
|
|
246
304
|
UPDATE_CHECK_CACHE_PATH,
|
|
247
305
|
UPDATE_CHECK_INTERVAL_HOURS,
|
|
248
306
|
UPDATE_CONFIG_PATH,
|
|
@@ -255,7 +313,7 @@ import {
|
|
|
255
313
|
USER_TASK_SOURCE,
|
|
256
314
|
epochSeconds,
|
|
257
315
|
estimateTokens
|
|
258
|
-
} from "./chunk-
|
|
316
|
+
} from "./chunk-6C6QZ4PM.js";
|
|
259
317
|
import {
|
|
260
318
|
LOG_KINDS,
|
|
261
319
|
kindToComponent
|
|
@@ -426,13 +484,24 @@ var DaemonServer = class {
|
|
|
426
484
|
uptime: process.uptime()
|
|
427
485
|
}
|
|
428
486
|
}));
|
|
487
|
+
this.registerRoute("GET", "/api/version", async () => ({
|
|
488
|
+
body: { version: this.version }
|
|
489
|
+
}));
|
|
429
490
|
}
|
|
430
491
|
async handleRequest(req, res) {
|
|
431
492
|
const match = this.router.match(req.method, req.url);
|
|
432
493
|
if (match) {
|
|
494
|
+
const rejection = validateLoopbackRequest(req, this.port);
|
|
495
|
+
if (rejection) {
|
|
496
|
+
res.writeHead(rejection.status, { "Content-Type": "application/json" });
|
|
497
|
+
res.end(JSON.stringify({ error: rejection.error }));
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
433
500
|
this.onRequest?.();
|
|
501
|
+
const versionHeader = { "X-Myco-Api-Version": this.version };
|
|
434
502
|
try {
|
|
435
|
-
const
|
|
503
|
+
const needsBody = req.method === "POST" || req.method === "PUT" || req.method === "PATCH" || req.method === "DELETE";
|
|
504
|
+
const body = needsBody ? await readBody(req) : void 0;
|
|
436
505
|
const result = await match.handler({
|
|
437
506
|
body,
|
|
438
507
|
query: match.query,
|
|
@@ -441,11 +510,11 @@ var DaemonServer = class {
|
|
|
441
510
|
});
|
|
442
511
|
const status = result.status ?? DEFAULT_STATUS;
|
|
443
512
|
if (Buffer.isBuffer(result.body)) {
|
|
444
|
-
res.writeHead(status, result.headers
|
|
513
|
+
res.writeHead(status, { ...versionHeader, ...result.headers });
|
|
445
514
|
res.end(result.body);
|
|
446
515
|
return;
|
|
447
516
|
}
|
|
448
|
-
const headers = { "Content-Type": "application/json", ...result.headers };
|
|
517
|
+
const headers = { "Content-Type": "application/json", ...versionHeader, ...result.headers };
|
|
449
518
|
res.writeHead(status, headers);
|
|
450
519
|
res.end(JSON.stringify(result.body));
|
|
451
520
|
} catch (error) {
|
|
@@ -453,7 +522,7 @@ var DaemonServer = class {
|
|
|
453
522
|
path: req.url,
|
|
454
523
|
error: error.message
|
|
455
524
|
});
|
|
456
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
525
|
+
res.writeHead(500, { "Content-Type": "application/json", ...versionHeader });
|
|
457
526
|
res.end(JSON.stringify({ error: error.message }));
|
|
458
527
|
}
|
|
459
528
|
return;
|
|
@@ -576,6 +645,36 @@ function readBody(req) {
|
|
|
576
645
|
req.on("error", reject);
|
|
577
646
|
});
|
|
578
647
|
}
|
|
648
|
+
function validateLoopbackRequest(req, port) {
|
|
649
|
+
const host = req.headers.host;
|
|
650
|
+
if (host && !isLoopbackHost(host, port)) {
|
|
651
|
+
return { status: 403, error: "forbidden_host" };
|
|
652
|
+
}
|
|
653
|
+
const origin = req.headers.origin;
|
|
654
|
+
if (origin && !isLoopbackOrigin(origin, port)) {
|
|
655
|
+
return { status: 403, error: "forbidden_origin" };
|
|
656
|
+
}
|
|
657
|
+
const method = (req.method ?? "").toUpperCase();
|
|
658
|
+
const isMutating = method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
|
|
659
|
+
if (!isMutating) return null;
|
|
660
|
+
const contentLengthHeader = req.headers["content-length"];
|
|
661
|
+
const contentLength = contentLengthHeader ? Number(contentLengthHeader) : NaN;
|
|
662
|
+
const hasBody = Number.isFinite(contentLength) ? contentLength > 0 : (req.headers["transfer-encoding"] ?? "").length > 0;
|
|
663
|
+
if (!hasBody) return null;
|
|
664
|
+
const contentType = (req.headers["content-type"] ?? "").toLowerCase();
|
|
665
|
+
if (!contentType.includes("application/json")) {
|
|
666
|
+
return { status: 415, error: "unsupported_media_type" };
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
function isLoopbackHost(host, port) {
|
|
671
|
+
const portStr = String(port);
|
|
672
|
+
return host === `127.0.0.1:${portStr}` || host === `localhost:${portStr}`;
|
|
673
|
+
}
|
|
674
|
+
function isLoopbackOrigin(origin, port) {
|
|
675
|
+
const portStr = String(port);
|
|
676
|
+
return origin === `http://127.0.0.1:${portStr}` || origin === `http://localhost:${portStr}`;
|
|
677
|
+
}
|
|
579
678
|
|
|
580
679
|
// src/daemon/lifecycle.ts
|
|
581
680
|
var SessionRegistry = class {
|
|
@@ -2597,7 +2696,7 @@ var TeamSyncClient = class _TeamSyncClient {
|
|
|
2597
2696
|
content_hash: data.content_hash ?? null
|
|
2598
2697
|
};
|
|
2599
2698
|
})
|
|
2600
|
-
});
|
|
2699
|
+
}, { timeoutMs: TEAM_SYNC_TIMEOUT_MS });
|
|
2601
2700
|
return res;
|
|
2602
2701
|
}
|
|
2603
2702
|
/**
|
|
@@ -2697,17 +2796,25 @@ var TeamSyncClient = class _TeamSyncClient {
|
|
|
2697
2796
|
"Content-Type": "application/json"
|
|
2698
2797
|
};
|
|
2699
2798
|
}
|
|
2700
|
-
async request(method,
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2799
|
+
async request(method, path25, body, options = {}) {
|
|
2800
|
+
const timeoutMs = options.timeoutMs ?? TEAM_REQUEST_TIMEOUT_MS;
|
|
2801
|
+
const controller = new AbortController();
|
|
2802
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2803
|
+
try {
|
|
2804
|
+
const res = await this.fetchFn(`${this.workerUrl}${path25}`, {
|
|
2805
|
+
method,
|
|
2806
|
+
headers: this.headers(),
|
|
2807
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
2808
|
+
signal: controller.signal
|
|
2809
|
+
});
|
|
2810
|
+
if (!res.ok) {
|
|
2811
|
+
const text = await res.text().catch(() => "");
|
|
2812
|
+
throw new Error(`Team sync request ${method} ${path25} failed: ${res.status} ${text}`);
|
|
2813
|
+
}
|
|
2814
|
+
return res.json();
|
|
2815
|
+
} finally {
|
|
2816
|
+
clearTimeout(timer);
|
|
2709
2817
|
}
|
|
2710
|
-
return res.json();
|
|
2711
2818
|
}
|
|
2712
2819
|
};
|
|
2713
2820
|
|
|
@@ -2838,7 +2945,7 @@ function createTeamHandlers(deps) {
|
|
|
2838
2945
|
return { body: { retried: count } };
|
|
2839
2946
|
}
|
|
2840
2947
|
async function handleUpgradeWorker(_req) {
|
|
2841
|
-
const { upgradeWorker } = await import("./team-
|
|
2948
|
+
const { upgradeWorker } = await import("./team-JTI5CDUO.js");
|
|
2842
2949
|
logger.info("team-sync.upgrade.start", "Starting worker upgrade");
|
|
2843
2950
|
const result = upgradeWorker(vaultDir);
|
|
2844
2951
|
if (!result.success) {
|
|
@@ -3230,7 +3337,7 @@ function createSkillRecordDeleteHandler(deps) {
|
|
|
3230
3337
|
logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill directory", { name: record.name, error: String(err) });
|
|
3231
3338
|
}
|
|
3232
3339
|
try {
|
|
3233
|
-
const { syncSkillSymlinks } = await import("./installer-
|
|
3340
|
+
const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
|
|
3234
3341
|
syncSkillSymlinks(projectRoot, record.name, { remove: true });
|
|
3235
3342
|
} catch (err) {
|
|
3236
3343
|
logger.warn(LOG_KINDS.PROCESSOR_BATCH, "Failed to remove skill symlinks", { name: record.name, error: String(err) });
|
|
@@ -3449,6 +3556,10 @@ async function handleGetProgress(tracker, token) {
|
|
|
3449
3556
|
|
|
3450
3557
|
// src/daemon/api/models.ts
|
|
3451
3558
|
var MODEL_LIST_TIMEOUT_MS = 5e3;
|
|
3559
|
+
var REMOTE_MODELS_ENDPOINT = "/models";
|
|
3560
|
+
var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
3561
|
+
var OPENROUTER_DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
3562
|
+
var REMOTE_PROVIDER_TIMEOUT_MS = 5e3;
|
|
3452
3563
|
var ANTHROPIC_MODELS = [
|
|
3453
3564
|
"claude-sonnet-4-6",
|
|
3454
3565
|
"claude-opus-4-6",
|
|
@@ -3474,22 +3585,60 @@ function filterLlmModels(models) {
|
|
|
3474
3585
|
return !EMBEDDING_PATTERNS.some((p) => name.includes(p));
|
|
3475
3586
|
});
|
|
3476
3587
|
}
|
|
3588
|
+
var REMOTE_PROVIDER_DEFAULTS = {
|
|
3589
|
+
openai: OPENAI_DEFAULT_BASE_URL,
|
|
3590
|
+
openrouter: OPENROUTER_DEFAULT_BASE_URL
|
|
3591
|
+
};
|
|
3592
|
+
var REMOTE_PROVIDER_ENV_VARS = {
|
|
3593
|
+
openai: OPENAI_API_KEY_ENV,
|
|
3594
|
+
openrouter: OPENROUTER_API_KEY_ENV
|
|
3595
|
+
};
|
|
3596
|
+
function getRemoteProviderApiKey(provider) {
|
|
3597
|
+
const preferredKey = process.env[REMOTE_PROVIDER_ENV_VARS[provider]];
|
|
3598
|
+
if (preferredKey) return preferredKey;
|
|
3599
|
+
if (provider === "openai") {
|
|
3600
|
+
return process.env.OPENAI_API_KEY;
|
|
3601
|
+
}
|
|
3602
|
+
return void 0;
|
|
3603
|
+
}
|
|
3604
|
+
async function fetchRemoteProviderModels(provider, _baseUrl, timeoutMs = REMOTE_PROVIDER_TIMEOUT_MS) {
|
|
3605
|
+
const apiKey = getRemoteProviderApiKey(provider);
|
|
3606
|
+
if (!apiKey) return [];
|
|
3607
|
+
const baseUrl = REMOTE_PROVIDER_DEFAULTS[provider];
|
|
3608
|
+
const response = await fetch(`${baseUrl}${REMOTE_MODELS_ENDPOINT}`, {
|
|
3609
|
+
headers: {
|
|
3610
|
+
Authorization: `Bearer ${apiKey}`
|
|
3611
|
+
},
|
|
3612
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
3613
|
+
});
|
|
3614
|
+
if (!response.ok) {
|
|
3615
|
+
throw new Error(`${provider} models request failed with ${response.status}`);
|
|
3616
|
+
}
|
|
3617
|
+
const data = await response.json();
|
|
3618
|
+
const modelIds = (data.data ?? []).map((entry) => entry.id).filter((id) => typeof id === "string" && id.length > 0);
|
|
3619
|
+
return filterLlmModels(modelIds);
|
|
3620
|
+
}
|
|
3477
3621
|
async function handleGetModels(req) {
|
|
3478
3622
|
const provider = req.query.provider;
|
|
3479
3623
|
const type = req.query.type;
|
|
3624
|
+
const localBackend = req.query.local_backend;
|
|
3480
3625
|
if (!provider) {
|
|
3481
3626
|
return { status: 400, body: { error: "provider query parameter required" } };
|
|
3482
3627
|
}
|
|
3483
3628
|
let models = [];
|
|
3484
3629
|
try {
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3630
|
+
const localBackendKind = inferLocalOpenAIBackendKind({
|
|
3631
|
+
type: provider === "lm-studio" ? "lmstudio" : provider,
|
|
3632
|
+
localBackend,
|
|
3633
|
+
baseUrl: req.query.base_url
|
|
3634
|
+
});
|
|
3635
|
+
if (localBackendKind) {
|
|
3636
|
+
const backend = createLocalOpenAIBackend(localBackendKind, req.query.base_url);
|
|
3490
3637
|
models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
|
|
3491
3638
|
} else if (provider === "anthropic") {
|
|
3492
3639
|
models = ANTHROPIC_MODELS;
|
|
3640
|
+
} else if (provider === "openai" || provider === "openrouter") {
|
|
3641
|
+
models = await fetchRemoteProviderModels(provider, void 0, MODEL_LIST_TIMEOUT_MS);
|
|
3493
3642
|
}
|
|
3494
3643
|
} catch {
|
|
3495
3644
|
}
|
|
@@ -3707,138 +3856,6 @@ function getAttachmentByFilePath(filePath) {
|
|
|
3707
3856
|
return row ? toAttachmentRow(row) : null;
|
|
3708
3857
|
}
|
|
3709
3858
|
|
|
3710
|
-
// src/db/queries/plans.ts
|
|
3711
|
-
var DEFAULT_LIST_LIMIT2 = 100;
|
|
3712
|
-
var DEFAULT_STATUS2 = "active";
|
|
3713
|
-
var DEFAULT_PROCESSED2 = 0;
|
|
3714
|
-
var PLAN_COLUMNS = [
|
|
3715
|
-
"id",
|
|
3716
|
-
"status",
|
|
3717
|
-
"author",
|
|
3718
|
-
"title",
|
|
3719
|
-
"content",
|
|
3720
|
-
"source_path",
|
|
3721
|
-
"tags",
|
|
3722
|
-
"session_id",
|
|
3723
|
-
"prompt_batch_id",
|
|
3724
|
-
"content_hash",
|
|
3725
|
-
"processed",
|
|
3726
|
-
"embedded",
|
|
3727
|
-
"created_at",
|
|
3728
|
-
"updated_at",
|
|
3729
|
-
"machine_id",
|
|
3730
|
-
"synced_at"
|
|
3731
|
-
];
|
|
3732
|
-
var SELECT_COLUMNS4 = PLAN_COLUMNS.join(", ");
|
|
3733
|
-
function toPlanRow(row) {
|
|
3734
|
-
return {
|
|
3735
|
-
id: row.id,
|
|
3736
|
-
status: row.status,
|
|
3737
|
-
author: row.author ?? null,
|
|
3738
|
-
title: row.title ?? null,
|
|
3739
|
-
content: row.content ?? null,
|
|
3740
|
-
source_path: row.source_path ?? null,
|
|
3741
|
-
tags: row.tags ?? null,
|
|
3742
|
-
session_id: row.session_id ?? null,
|
|
3743
|
-
prompt_batch_id: row.prompt_batch_id ?? null,
|
|
3744
|
-
content_hash: row.content_hash ?? null,
|
|
3745
|
-
processed: row.processed,
|
|
3746
|
-
embedded: row.embedded ?? 0,
|
|
3747
|
-
created_at: row.created_at,
|
|
3748
|
-
updated_at: row.updated_at ?? null,
|
|
3749
|
-
machine_id: row.machine_id ?? "local",
|
|
3750
|
-
synced_at: row.synced_at ?? null
|
|
3751
|
-
};
|
|
3752
|
-
}
|
|
3753
|
-
function upsertPlan(data) {
|
|
3754
|
-
const db = getDatabase();
|
|
3755
|
-
db.prepare(
|
|
3756
|
-
`INSERT INTO plans (
|
|
3757
|
-
id, status, author, title, content,
|
|
3758
|
-
source_path, tags, session_id, prompt_batch_id, content_hash,
|
|
3759
|
-
processed, created_at, updated_at, machine_id
|
|
3760
|
-
) VALUES (
|
|
3761
|
-
?, ?, ?, ?, ?,
|
|
3762
|
-
?, ?, ?, ?, ?,
|
|
3763
|
-
?, ?, ?, ?
|
|
3764
|
-
)
|
|
3765
|
-
ON CONFLICT (id) DO UPDATE SET
|
|
3766
|
-
status = EXCLUDED.status,
|
|
3767
|
-
author = EXCLUDED.author,
|
|
3768
|
-
title = EXCLUDED.title,
|
|
3769
|
-
content = EXCLUDED.content,
|
|
3770
|
-
source_path = EXCLUDED.source_path,
|
|
3771
|
-
tags = EXCLUDED.tags,
|
|
3772
|
-
session_id = EXCLUDED.session_id,
|
|
3773
|
-
prompt_batch_id = EXCLUDED.prompt_batch_id,
|
|
3774
|
-
content_hash = EXCLUDED.content_hash,
|
|
3775
|
-
processed = EXCLUDED.processed,
|
|
3776
|
-
updated_at = EXCLUDED.updated_at,
|
|
3777
|
-
embedded = CASE
|
|
3778
|
-
WHEN EXCLUDED.content_hash != plans.content_hash THEN 0
|
|
3779
|
-
ELSE plans.embedded
|
|
3780
|
-
END`
|
|
3781
|
-
).run(
|
|
3782
|
-
data.id,
|
|
3783
|
-
data.status ?? DEFAULT_STATUS2,
|
|
3784
|
-
data.author ?? null,
|
|
3785
|
-
data.title ?? null,
|
|
3786
|
-
data.content ?? null,
|
|
3787
|
-
data.source_path ?? null,
|
|
3788
|
-
data.tags ?? null,
|
|
3789
|
-
data.session_id ?? null,
|
|
3790
|
-
data.prompt_batch_id ?? null,
|
|
3791
|
-
data.content_hash ?? null,
|
|
3792
|
-
data.processed ?? DEFAULT_PROCESSED2,
|
|
3793
|
-
data.created_at,
|
|
3794
|
-
data.updated_at ?? null,
|
|
3795
|
-
data.machine_id ?? getTeamMachineId()
|
|
3796
|
-
);
|
|
3797
|
-
const row = toPlanRow(
|
|
3798
|
-
db.prepare(`SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`).get(data.id)
|
|
3799
|
-
);
|
|
3800
|
-
syncRow("plans", row);
|
|
3801
|
-
return row;
|
|
3802
|
-
}
|
|
3803
|
-
function getPlan(id) {
|
|
3804
|
-
const db = getDatabase();
|
|
3805
|
-
const row = db.prepare(
|
|
3806
|
-
`SELECT ${SELECT_COLUMNS4} FROM plans WHERE id = ?`
|
|
3807
|
-
).get(id);
|
|
3808
|
-
if (!row) return null;
|
|
3809
|
-
return toPlanRow(row);
|
|
3810
|
-
}
|
|
3811
|
-
function listPlans(options = {}) {
|
|
3812
|
-
const db = getDatabase();
|
|
3813
|
-
const conditions = [];
|
|
3814
|
-
const params = [];
|
|
3815
|
-
if (options.status !== void 0) {
|
|
3816
|
-
conditions.push(`status = ?`);
|
|
3817
|
-
params.push(options.status);
|
|
3818
|
-
}
|
|
3819
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3820
|
-
const limit = options.limit ?? DEFAULT_LIST_LIMIT2;
|
|
3821
|
-
params.push(limit);
|
|
3822
|
-
const rows = db.prepare(
|
|
3823
|
-
`SELECT ${SELECT_COLUMNS4}
|
|
3824
|
-
FROM plans
|
|
3825
|
-
${where}
|
|
3826
|
-
ORDER BY created_at DESC
|
|
3827
|
-
LIMIT ?`
|
|
3828
|
-
).all(...params);
|
|
3829
|
-
return rows.map(toPlanRow);
|
|
3830
|
-
}
|
|
3831
|
-
function listPlansBySession(sessionId) {
|
|
3832
|
-
const db = getDatabase();
|
|
3833
|
-
const rows = db.prepare(
|
|
3834
|
-
`SELECT ${SELECT_COLUMNS4}
|
|
3835
|
-
FROM plans
|
|
3836
|
-
WHERE session_id = ?
|
|
3837
|
-
ORDER BY created_at DESC`
|
|
3838
|
-
).all(sessionId);
|
|
3839
|
-
return rows.map(toPlanRow);
|
|
3840
|
-
}
|
|
3841
|
-
|
|
3842
3859
|
// src/daemon/jobs/session-cleanup.ts
|
|
3843
3860
|
import { unlink, glob } from "fs/promises";
|
|
3844
3861
|
async function cleanupAfterSessionCascade(sessionId, result, embeddingManager, vaultDir) {
|
|
@@ -3883,8 +3900,8 @@ async function triggerTitleSummary(sessionId, deps) {
|
|
|
3883
3900
|
if (config.agent.summary_batch_interval <= 0) return;
|
|
3884
3901
|
if (config.agent.event_tasks_enabled === false) return;
|
|
3885
3902
|
try {
|
|
3886
|
-
const { runAgent } = await import("./executor-
|
|
3887
|
-
|
|
3903
|
+
const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
|
|
3904
|
+
runAgent2(vaultDir, {
|
|
3888
3905
|
task: "title-summary",
|
|
3889
3906
|
instruction: `Process session ${sessionId} only`,
|
|
3890
3907
|
embeddingManager
|
|
@@ -3898,11 +3915,16 @@ async function triggerTitleSummary(sessionId, deps) {
|
|
|
3898
3915
|
}
|
|
3899
3916
|
}
|
|
3900
3917
|
|
|
3918
|
+
// src/daemon/api/error-envelope.ts
|
|
3919
|
+
function errorBody(code, message) {
|
|
3920
|
+
return { error: { code, message } };
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3901
3923
|
// src/daemon/api/sessions.ts
|
|
3902
|
-
var
|
|
3924
|
+
var DEFAULT_LIST_LIMIT2 = 50;
|
|
3903
3925
|
var DEFAULT_LIST_OFFSET2 = 0;
|
|
3904
3926
|
async function handleListSessions(req) {
|
|
3905
|
-
const limit = req.query.limit ? Number(req.query.limit) :
|
|
3927
|
+
const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIST_LIMIT2;
|
|
3906
3928
|
const offset = req.query.offset ? Number(req.query.offset) : DEFAULT_LIST_OFFSET2;
|
|
3907
3929
|
const status = req.query.status || void 0;
|
|
3908
3930
|
const agent = req.query.agent || void 0;
|
|
@@ -3945,7 +3967,7 @@ async function handleGetSessionAttachments(req) {
|
|
|
3945
3967
|
}
|
|
3946
3968
|
async function handleGetSessionPlans(req) {
|
|
3947
3969
|
const plans = listPlansBySession(req.params.id);
|
|
3948
|
-
return { body: plans };
|
|
3970
|
+
return { body: { plans } };
|
|
3949
3971
|
}
|
|
3950
3972
|
function createSessionMutationHandlers(deps) {
|
|
3951
3973
|
const { embeddingManager, vaultDir, logger, liveConfig } = deps;
|
|
@@ -3986,7 +4008,125 @@ function createSessionMutationHandlers(deps) {
|
|
|
3986
4008
|
const impact = getSessionImpact(sessionId);
|
|
3987
4009
|
return { body: impact };
|
|
3988
4010
|
}
|
|
3989
|
-
|
|
4011
|
+
async function handleDeletePlan(req) {
|
|
4012
|
+
const existing = getPlan(req.params.id);
|
|
4013
|
+
if (!existing) return { status: 404, body: errorBody("plan-not-found", "Plan not found") };
|
|
4014
|
+
const localMachineId = getTeamMachineId();
|
|
4015
|
+
const body = req.body;
|
|
4016
|
+
const forceRemote = body?.force_remote === true;
|
|
4017
|
+
if (existing.machine_id !== localMachineId && !forceRemote) {
|
|
4018
|
+
logger.warn(LOG_KINDS.API_SESSION_DELETE, "Cross-machine plan delete rejected", {
|
|
4019
|
+
plan_id: existing.id,
|
|
4020
|
+
plan_machine_id: existing.machine_id,
|
|
4021
|
+
local_machine_id: localMachineId
|
|
4022
|
+
});
|
|
4023
|
+
return {
|
|
4024
|
+
status: 403,
|
|
4025
|
+
body: errorBody(
|
|
4026
|
+
"cross-machine-delete",
|
|
4027
|
+
'Plan belongs to another machine; pass {"force_remote": true} to delete.'
|
|
4028
|
+
)
|
|
4029
|
+
};
|
|
4030
|
+
}
|
|
4031
|
+
if (existing.machine_id !== localMachineId && forceRemote) {
|
|
4032
|
+
logger.warn(LOG_KINDS.API_SESSION_DELETE, "Cross-machine plan delete allowed by force_remote", {
|
|
4033
|
+
plan_id: existing.id,
|
|
4034
|
+
plan_machine_id: existing.machine_id,
|
|
4035
|
+
local_machine_id: localMachineId
|
|
4036
|
+
});
|
|
4037
|
+
}
|
|
4038
|
+
const deleted = deletePlan(req.params.id);
|
|
4039
|
+
if (!deleted) return { status: 404, body: errorBody("plan-not-found", "Plan not found") };
|
|
4040
|
+
embeddingManager.onRemoved("plans", deleted.id);
|
|
4041
|
+
logger.info(LOG_KINDS.API_SESSION_DELETE, "Plan deleted", {
|
|
4042
|
+
plan_id: deleted.id,
|
|
4043
|
+
session_id: deleted.session_id,
|
|
4044
|
+
logical_key: deleted.logical_key
|
|
4045
|
+
});
|
|
4046
|
+
return {
|
|
4047
|
+
body: {
|
|
4048
|
+
ok: true,
|
|
4049
|
+
id: deleted.id,
|
|
4050
|
+
session_id: deleted.session_id
|
|
4051
|
+
}
|
|
4052
|
+
};
|
|
4053
|
+
}
|
|
4054
|
+
return { handleDeleteSession, handleCompleteSession, handleGetSessionImpact, handleDeletePlan };
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
// src/db/queries/entities.ts
|
|
4058
|
+
var DEFAULT_LIST_LIMIT3 = 100;
|
|
4059
|
+
var ENTITY_COLUMNS = [
|
|
4060
|
+
"id",
|
|
4061
|
+
"agent_id",
|
|
4062
|
+
"type",
|
|
4063
|
+
"name",
|
|
4064
|
+
"properties",
|
|
4065
|
+
"first_seen",
|
|
4066
|
+
"last_seen",
|
|
4067
|
+
"status",
|
|
4068
|
+
"machine_id",
|
|
4069
|
+
"synced_at"
|
|
4070
|
+
];
|
|
4071
|
+
var SELECT_COLUMNS4 = ENTITY_COLUMNS.join(", ");
|
|
4072
|
+
function toEntityRow(row) {
|
|
4073
|
+
return {
|
|
4074
|
+
id: row.id,
|
|
4075
|
+
agent_id: row.agent_id,
|
|
4076
|
+
type: row.type,
|
|
4077
|
+
name: row.name,
|
|
4078
|
+
properties: row.properties ?? null,
|
|
4079
|
+
first_seen: row.first_seen,
|
|
4080
|
+
last_seen: row.last_seen,
|
|
4081
|
+
status: row.status ?? "active",
|
|
4082
|
+
machine_id: row.machine_id ?? "local",
|
|
4083
|
+
synced_at: row.synced_at ?? null
|
|
4084
|
+
};
|
|
4085
|
+
}
|
|
4086
|
+
function listEntities(options = {}) {
|
|
4087
|
+
const db = getDatabase();
|
|
4088
|
+
const conditions = [];
|
|
4089
|
+
const params = [];
|
|
4090
|
+
if (options.agent_id !== void 0) {
|
|
4091
|
+
conditions.push(`agent_id = ?`);
|
|
4092
|
+
params.push(options.agent_id);
|
|
4093
|
+
}
|
|
4094
|
+
if (options.type !== void 0) {
|
|
4095
|
+
conditions.push(`type = ?`);
|
|
4096
|
+
params.push(options.type);
|
|
4097
|
+
}
|
|
4098
|
+
if (options.name !== void 0) {
|
|
4099
|
+
conditions.push(`name = ?`);
|
|
4100
|
+
params.push(options.name);
|
|
4101
|
+
}
|
|
4102
|
+
if (options.status !== void 0) {
|
|
4103
|
+
conditions.push(`status = ?`);
|
|
4104
|
+
params.push(options.status);
|
|
4105
|
+
} else {
|
|
4106
|
+
conditions.push(`status = ?`);
|
|
4107
|
+
params.push("active");
|
|
4108
|
+
}
|
|
4109
|
+
if (options.mentioned_in !== void 0 && options.note_type !== void 0) {
|
|
4110
|
+
conditions.push(
|
|
4111
|
+
`id IN (SELECT entity_id FROM entity_mentions WHERE note_id = ? AND note_type = ?)`
|
|
4112
|
+
);
|
|
4113
|
+
params.push(options.mentioned_in);
|
|
4114
|
+
params.push(options.note_type);
|
|
4115
|
+
}
|
|
4116
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
4117
|
+
const limit = options.limit ?? DEFAULT_LIST_LIMIT3;
|
|
4118
|
+
const offset = options.offset ?? 0;
|
|
4119
|
+
params.push(limit);
|
|
4120
|
+
params.push(offset);
|
|
4121
|
+
const rows = db.prepare(
|
|
4122
|
+
`SELECT ${SELECT_COLUMNS4}
|
|
4123
|
+
FROM entities
|
|
4124
|
+
${where}
|
|
4125
|
+
ORDER BY last_seen DESC
|
|
4126
|
+
LIMIT ?
|
|
4127
|
+
OFFSET ?`
|
|
4128
|
+
).all(...params);
|
|
4129
|
+
return rows.map(toEntityRow);
|
|
3990
4130
|
}
|
|
3991
4131
|
|
|
3992
4132
|
// src/daemon/api/mycelium.ts
|
|
@@ -3995,7 +4135,6 @@ var DEFAULT_LIST_OFFSET3 = 0;
|
|
|
3995
4135
|
var DEFAULT_GRAPH_DEPTH = 1;
|
|
3996
4136
|
var MAX_GRAPH_DEPTH = 3;
|
|
3997
4137
|
var SPORE_NAME_PREVIEW_CHARS = 60;
|
|
3998
|
-
var GRAPH_SEED_ENTITY_LIMIT = 4;
|
|
3999
4138
|
var GRAPH_SEED_SPORE_LIMIT = 4;
|
|
4000
4139
|
var GRAPH_SEED_SESSION_LIMIT = 4;
|
|
4001
4140
|
var EXCLUDED_GRAPH_EDGE_TYPES = /* @__PURE__ */ new Set(["HAS_BATCH", "EXTRACTED_FROM"]);
|
|
@@ -4054,15 +4193,6 @@ async function handleGetGraphSeeds(_req) {
|
|
|
4054
4193
|
ORDER BY started_at DESC
|
|
4055
4194
|
LIMIT ?`
|
|
4056
4195
|
).all(GRAPH_SEED_SESSION_LIMIT);
|
|
4057
|
-
const entityRows = db.prepare(
|
|
4058
|
-
`SELECT e.id, e.type, e.name, e.status, e.first_seen as created_at, COUNT(em.entity_id) as mention_count
|
|
4059
|
-
FROM entities e
|
|
4060
|
-
LEFT JOIN entity_mentions em ON em.entity_id = e.id
|
|
4061
|
-
WHERE e.agent_id = ? AND e.status = 'active'
|
|
4062
|
-
GROUP BY e.id
|
|
4063
|
-
ORDER BY mention_count DESC, e.last_seen DESC
|
|
4064
|
-
LIMIT ?`
|
|
4065
|
-
).all(DEFAULT_AGENT_ID, GRAPH_SEED_ENTITY_LIMIT);
|
|
4066
4196
|
const sporeSeeds = sporeRows.map((row) => ({
|
|
4067
4197
|
id: row.id,
|
|
4068
4198
|
name: (row.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
@@ -4080,20 +4210,66 @@ async function handleGetGraphSeeds(_req) {
|
|
|
4080
4210
|
created_at: row.created_at,
|
|
4081
4211
|
content: row.summary ?? void 0
|
|
4082
4212
|
}));
|
|
4083
|
-
const
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4213
|
+
const topConnectedRow = db.prepare(
|
|
4214
|
+
`SELECT node_id FROM (
|
|
4215
|
+
SELECT source_id AS node_id, COUNT(*) AS cnt
|
|
4216
|
+
FROM graph_edges
|
|
4217
|
+
WHERE agent_id = ?
|
|
4218
|
+
AND type NOT IN ('HAS_BATCH', 'EXTRACTED_FROM')
|
|
4219
|
+
AND source_type IN ('spore', 'session')
|
|
4220
|
+
GROUP BY source_id
|
|
4221
|
+
UNION ALL
|
|
4222
|
+
SELECT target_id, COUNT(*)
|
|
4223
|
+
FROM graph_edges
|
|
4224
|
+
WHERE agent_id = ?
|
|
4225
|
+
AND type NOT IN ('HAS_BATCH', 'EXTRACTED_FROM')
|
|
4226
|
+
AND target_type IN ('spore', 'session')
|
|
4227
|
+
GROUP BY target_id
|
|
4228
|
+
)
|
|
4229
|
+
GROUP BY node_id
|
|
4230
|
+
ORDER BY SUM(cnt) DESC
|
|
4231
|
+
LIMIT 1`
|
|
4232
|
+
).get(DEFAULT_AGENT_ID, DEFAULT_AGENT_ID);
|
|
4233
|
+
let topSeed = null;
|
|
4234
|
+
if (topConnectedRow?.node_id) {
|
|
4235
|
+
const topId = topConnectedRow.node_id;
|
|
4236
|
+
const sporeHit = db.prepare(
|
|
4237
|
+
`SELECT id, observation_type, status, content, created_at
|
|
4238
|
+
FROM spores WHERE id = ?`
|
|
4239
|
+
).get(topId);
|
|
4240
|
+
if (sporeHit) {
|
|
4241
|
+
topSeed = {
|
|
4242
|
+
id: sporeHit.id,
|
|
4243
|
+
name: (sporeHit.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
4244
|
+
type: "spore",
|
|
4245
|
+
status: sporeHit.status ?? void 0,
|
|
4246
|
+
created_at: sporeHit.created_at,
|
|
4247
|
+
content: sporeHit.content,
|
|
4248
|
+
observation_type: sporeHit.observation_type
|
|
4249
|
+
};
|
|
4250
|
+
} else {
|
|
4251
|
+
const sessionHit = db.prepare(
|
|
4252
|
+
`SELECT id, title, summary, status, started_at as created_at
|
|
4253
|
+
FROM sessions WHERE id = ?`
|
|
4254
|
+
).get(topId);
|
|
4255
|
+
if (sessionHit) {
|
|
4256
|
+
topSeed = {
|
|
4257
|
+
id: sessionHit.id,
|
|
4258
|
+
name: sessionHit.title ?? `Session ${sessionHit.id.slice(-6)}`,
|
|
4259
|
+
type: "session",
|
|
4260
|
+
status: sessionHit.status ?? void 0,
|
|
4261
|
+
created_at: sessionHit.created_at,
|
|
4262
|
+
content: sessionHit.summary ?? void 0
|
|
4263
|
+
};
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4091
4267
|
const seeds = [
|
|
4092
|
-
...
|
|
4093
|
-
...sessionSeeds,
|
|
4094
|
-
...sporeSeeds
|
|
4268
|
+
...topSeed ? [topSeed] : [],
|
|
4269
|
+
...sessionSeeds.filter((s) => s.id !== topSeed?.id),
|
|
4270
|
+
...sporeSeeds.filter((s) => s.id !== topSeed?.id)
|
|
4095
4271
|
];
|
|
4096
|
-
const recommendedId =
|
|
4272
|
+
const recommendedId = topSeed?.id ?? sessionSeeds[0]?.id ?? sporeSeeds[0]?.id ?? null;
|
|
4097
4273
|
return {
|
|
4098
4274
|
body: {
|
|
4099
4275
|
seeds,
|
|
@@ -4104,32 +4280,19 @@ async function handleGetGraphSeeds(_req) {
|
|
|
4104
4280
|
async function handleGetGraph(req) {
|
|
4105
4281
|
const depth = Math.min(Number(req.query.depth) || DEFAULT_GRAPH_DEPTH, MAX_GRAPH_DEPTH);
|
|
4106
4282
|
const id = req.params.id;
|
|
4107
|
-
let
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
if (
|
|
4111
|
-
|
|
4112
|
-
centerType = "entity";
|
|
4283
|
+
let centerType;
|
|
4284
|
+
if (getSpore(id)) {
|
|
4285
|
+
centerType = "spore";
|
|
4286
|
+
} else if (getSession(id)) {
|
|
4287
|
+
centerType = "session";
|
|
4113
4288
|
} else {
|
|
4114
|
-
|
|
4115
|
-
if (spore) {
|
|
4116
|
-
centerNode = spore;
|
|
4117
|
-
centerType = "spore";
|
|
4118
|
-
} else {
|
|
4119
|
-
const session = getSession(id);
|
|
4120
|
-
if (session) {
|
|
4121
|
-
centerNode = session;
|
|
4122
|
-
centerType = "session";
|
|
4123
|
-
}
|
|
4124
|
-
}
|
|
4289
|
+
return { status: 404, body: { error: "not_found" } };
|
|
4125
4290
|
}
|
|
4126
|
-
if (!centerNode) return { status: 404, body: { error: "not_found" } };
|
|
4127
4291
|
const graph = getGraphForNode(id, centerType, { depth });
|
|
4128
4292
|
const filteredEdges = graph.edges.filter(
|
|
4129
4293
|
(e) => !EXCLUDED_GRAPH_EDGE_TYPES.has(e.type)
|
|
4130
4294
|
);
|
|
4131
4295
|
const graphDb = getDatabase();
|
|
4132
|
-
const entityIds = /* @__PURE__ */ new Set();
|
|
4133
4296
|
const sporeIds = /* @__PURE__ */ new Set();
|
|
4134
4297
|
const sessionIds = /* @__PURE__ */ new Set();
|
|
4135
4298
|
for (const edge of filteredEdges) {
|
|
@@ -4137,70 +4300,23 @@ async function handleGetGraph(req) {
|
|
|
4137
4300
|
[edge.source_id, edge.source_type],
|
|
4138
4301
|
[edge.target_id, edge.target_type]
|
|
4139
4302
|
]) {
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
entityIds.add(nodeId);
|
|
4143
|
-
break;
|
|
4144
|
-
case "spore":
|
|
4145
|
-
sporeIds.add(nodeId);
|
|
4146
|
-
break;
|
|
4147
|
-
case "session":
|
|
4148
|
-
sessionIds.add(nodeId);
|
|
4149
|
-
break;
|
|
4150
|
-
}
|
|
4303
|
+
if (type === "spore") sporeIds.add(nodeId);
|
|
4304
|
+
else if (type === "session") sessionIds.add(nodeId);
|
|
4151
4305
|
}
|
|
4152
4306
|
}
|
|
4153
|
-
if (centerType === "entity") entityIds.add(id);
|
|
4154
4307
|
if (centerType === "spore") sporeIds.add(id);
|
|
4155
|
-
|
|
4156
|
-
const entityIdArray = Array.from(entityIds);
|
|
4157
|
-
let entityNodes = [];
|
|
4158
|
-
if (entityIdArray.length > 0) {
|
|
4159
|
-
const placeholders = entityIdArray.map(() => "?").join(", ");
|
|
4160
|
-
entityNodes = graphDb.prepare(
|
|
4161
|
-
`SELECT id, type, name, properties, status, first_seen as created_at
|
|
4162
|
-
FROM entities WHERE id IN (${placeholders})`
|
|
4163
|
-
).all(...entityIdArray);
|
|
4164
|
-
}
|
|
4308
|
+
else sessionIds.add(id);
|
|
4165
4309
|
const sporeIdArray = Array.from(sporeIds);
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
`SELECT id, observation_type, status, content, properties, created_at
|
|
4171
|
-
FROM spores WHERE id IN (${placeholders})`
|
|
4172
|
-
).all(...sporeIdArray);
|
|
4173
|
-
}
|
|
4310
|
+
const sporeNodes = sporeIdArray.length > 0 ? graphDb.prepare(
|
|
4311
|
+
`SELECT id, observation_type, status, content, properties, created_at
|
|
4312
|
+
FROM spores WHERE id IN (${sporeIdArray.map(() => "?").join(", ")})`
|
|
4313
|
+
).all(...sporeIdArray) : [];
|
|
4174
4314
|
const sessionIdArray = Array.from(sessionIds);
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
`SELECT id, title, summary, status, started_at as created_at
|
|
4180
|
-
FROM sessions WHERE id IN (${placeholders})`
|
|
4181
|
-
).all(...sessionIdArray);
|
|
4182
|
-
}
|
|
4183
|
-
const mentionCounts = /* @__PURE__ */ new Map();
|
|
4184
|
-
if (entityIdArray.length > 0) {
|
|
4185
|
-
const placeholders = entityIdArray.map(() => "?").join(", ");
|
|
4186
|
-
const mentionRows = graphDb.prepare(
|
|
4187
|
-
`SELECT entity_id, COUNT(*) as count FROM entity_mentions
|
|
4188
|
-
WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
|
|
4189
|
-
).all(...entityIdArray);
|
|
4190
|
-
for (const row of mentionRows) {
|
|
4191
|
-
mentionCounts.set(row.entity_id, Number(row.count));
|
|
4192
|
-
}
|
|
4193
|
-
}
|
|
4315
|
+
const sessionNodes = sessionIdArray.length > 0 ? graphDb.prepare(
|
|
4316
|
+
`SELECT id, title, summary, status, started_at as created_at
|
|
4317
|
+
FROM sessions WHERE id IN (${sessionIdArray.map(() => "?").join(", ")})`
|
|
4318
|
+
).all(...sessionIdArray) : [];
|
|
4194
4319
|
const allNodes = [
|
|
4195
|
-
...entityNodes.map((n) => ({
|
|
4196
|
-
id: n.id,
|
|
4197
|
-
name: n.name,
|
|
4198
|
-
type: n.type,
|
|
4199
|
-
status: n.status ?? void 0,
|
|
4200
|
-
created_at: n.created_at,
|
|
4201
|
-
properties: n.properties ?? void 0,
|
|
4202
|
-
mention_count: mentionCounts.get(n.id) ?? 0
|
|
4203
|
-
})),
|
|
4204
4320
|
...sporeNodes.map((n) => ({
|
|
4205
4321
|
id: n.id,
|
|
4206
4322
|
name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
@@ -4239,10 +4355,6 @@ async function handleGetGraph(req) {
|
|
|
4239
4355
|
var FULL_GRAPH_NODE_LIMIT = 500;
|
|
4240
4356
|
async function handleGetFullGraph(_req) {
|
|
4241
4357
|
const db = getDatabase();
|
|
4242
|
-
const entityRows = db.prepare(
|
|
4243
|
-
`SELECT id, type, name, properties, status, first_seen as created_at
|
|
4244
|
-
FROM entities WHERE agent_id = ? LIMIT ?`
|
|
4245
|
-
).all(DEFAULT_AGENT_ID, FULL_GRAPH_NODE_LIMIT);
|
|
4246
4358
|
const sporeRows = db.prepare(
|
|
4247
4359
|
`SELECT id, observation_type, status, content, properties, created_at
|
|
4248
4360
|
FROM spores WHERE agent_id = ? AND status = 'active' LIMIT ?`
|
|
@@ -4252,43 +4364,21 @@ async function handleGetFullGraph(_req) {
|
|
|
4252
4364
|
FROM sessions ORDER BY created_at DESC LIMIT ?`
|
|
4253
4365
|
).all(FULL_GRAPH_NODE_LIMIT);
|
|
4254
4366
|
const allIds = /* @__PURE__ */ new Set();
|
|
4255
|
-
for (const r of [...
|
|
4367
|
+
for (const r of [...sporeRows, ...sessionRows]) {
|
|
4256
4368
|
allIds.add(r.id);
|
|
4257
4369
|
}
|
|
4258
4370
|
const excludedTypes = Array.from(EXCLUDED_GRAPH_EDGE_TYPES).map(() => "?").join(", ");
|
|
4259
4371
|
const allIdsList = Array.from(allIds);
|
|
4260
4372
|
const idPlaceholders = allIdsList.map(() => "?").join(", ");
|
|
4261
|
-
const edgeRows = db.prepare(
|
|
4373
|
+
const edgeRows = allIdsList.length > 0 ? db.prepare(
|
|
4262
4374
|
`SELECT source_id, source_type, target_id, target_type, type, confidence
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList);
|
|
4269
|
-
const filteredEdges = edgeRows;
|
|
4270
|
-
const mentionCounts = /* @__PURE__ */ new Map();
|
|
4271
|
-
const entityIdArray = entityRows.map((r) => r.id);
|
|
4272
|
-
if (entityIdArray.length > 0) {
|
|
4273
|
-
const placeholders = entityIdArray.map(() => "?").join(", ");
|
|
4274
|
-
const mentionRows = db.prepare(
|
|
4275
|
-
`SELECT entity_id, COUNT(*) as count FROM entity_mentions
|
|
4276
|
-
WHERE entity_id IN (${placeholders}) GROUP BY entity_id`
|
|
4277
|
-
).all(...entityIdArray);
|
|
4278
|
-
for (const row of mentionRows) {
|
|
4279
|
-
mentionCounts.set(row.entity_id, Number(row.count));
|
|
4280
|
-
}
|
|
4281
|
-
}
|
|
4375
|
+
FROM graph_edges
|
|
4376
|
+
WHERE agent_id = ?
|
|
4377
|
+
AND type NOT IN (${excludedTypes})
|
|
4378
|
+
AND source_id IN (${idPlaceholders})
|
|
4379
|
+
AND target_id IN (${idPlaceholders})`
|
|
4380
|
+
).all(DEFAULT_AGENT_ID, ...Array.from(EXCLUDED_GRAPH_EDGE_TYPES), ...allIdsList, ...allIdsList) : [];
|
|
4282
4381
|
const nodes = [
|
|
4283
|
-
...entityRows.map((n) => ({
|
|
4284
|
-
id: n.id,
|
|
4285
|
-
name: n.name,
|
|
4286
|
-
type: n.type,
|
|
4287
|
-
status: n.status ?? void 0,
|
|
4288
|
-
created_at: n.created_at,
|
|
4289
|
-
properties: n.properties ?? void 0,
|
|
4290
|
-
mention_count: mentionCounts.get(n.id) ?? 0
|
|
4291
|
-
})),
|
|
4292
4382
|
...sporeRows.map((n) => ({
|
|
4293
4383
|
id: n.id,
|
|
4294
4384
|
name: (n.content ?? "").slice(0, SPORE_NAME_PREVIEW_CHARS),
|
|
@@ -4308,7 +4398,7 @@ async function handleGetFullGraph(_req) {
|
|
|
4308
4398
|
content: n.summary ?? void 0
|
|
4309
4399
|
}))
|
|
4310
4400
|
];
|
|
4311
|
-
const edges =
|
|
4401
|
+
const edges = edgeRows.map((e) => ({
|
|
4312
4402
|
source_id: e.source_id,
|
|
4313
4403
|
target_id: e.target_id,
|
|
4314
4404
|
label: e.type,
|
|
@@ -4391,75 +4481,305 @@ function createSearchHandler(deps) {
|
|
|
4391
4481
|
};
|
|
4392
4482
|
}
|
|
4393
4483
|
|
|
4394
|
-
// src/
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
const { logger, liveConfig } = deps;
|
|
4412
|
-
const config = liveConfig.current;
|
|
4413
|
-
logger.debug(LOG_KINDS.CONTEXT_QUERY, "Session context query", { session_id });
|
|
4414
|
-
try {
|
|
4415
|
-
const parts = [];
|
|
4416
|
-
const tier = config.context.digest_tier;
|
|
4417
|
-
const extract = getDigestExtract(DEFAULT_AGENT_ID, tier);
|
|
4418
|
-
if (extract) {
|
|
4419
|
-
parts.push(extract.content);
|
|
4420
|
-
logger.info(LOG_KINDS.CONTEXT_DIGEST, "Digest extract found", {
|
|
4421
|
-
session_id,
|
|
4422
|
-
tier,
|
|
4423
|
-
content_length: extract.content.length,
|
|
4424
|
-
generated_at: extract.generated_at
|
|
4425
|
-
});
|
|
4426
|
-
} else {
|
|
4427
|
-
logger.debug(LOG_KINDS.CONTEXT_DIGEST, "No digest extract available", { session_id, tier });
|
|
4428
|
-
}
|
|
4429
|
-
if (branch) {
|
|
4430
|
-
parts.push(`Branch:: \`${branch}\``);
|
|
4431
|
-
}
|
|
4432
|
-
parts.push(`Session:: \`${session_id}\``);
|
|
4433
|
-
const source = extract ? "digest" : "basic";
|
|
4434
|
-
const contextText = parts.join("\n\n");
|
|
4435
|
-
const estimatedTokens = estimateTokens(contextText);
|
|
4436
|
-
logger.info(
|
|
4437
|
-
LOG_KINDS.CONTEXT_SESSION,
|
|
4438
|
-
`Session context: ${estimatedTokens} est. tokens, source=${source}${extract ? `, tier=${tier}` : ""}`,
|
|
4439
|
-
{
|
|
4440
|
-
session_id,
|
|
4441
|
-
source,
|
|
4442
|
-
tier: extract ? tier : void 0,
|
|
4443
|
-
text_length: contextText.length,
|
|
4444
|
-
estimated_tokens: estimatedTokens,
|
|
4445
|
-
generated_at: extract?.generated_at,
|
|
4446
|
-
injected_text: contextText
|
|
4447
|
-
}
|
|
4448
|
-
);
|
|
4449
|
-
return {
|
|
4450
|
-
body: {
|
|
4451
|
-
text: contextText,
|
|
4452
|
-
source,
|
|
4453
|
-
...extract ? { tier } : {}
|
|
4454
|
-
}
|
|
4455
|
-
};
|
|
4456
|
-
} catch (error) {
|
|
4457
|
-
logger.error(LOG_KINDS.CONTEXT_SESSION, "Session context failed", { error: error.message });
|
|
4458
|
-
return { body: { text: "" } };
|
|
4484
|
+
// src/symbionts/injection-support.ts
|
|
4485
|
+
import fs16 from "fs";
|
|
4486
|
+
import path17 from "path";
|
|
4487
|
+
var SESSION_START_SIGNALS = ["hook session-start", '"/context"', '"/context/resume"'];
|
|
4488
|
+
var PROMPT_SUBMIT_SIGNALS = ["hook user-prompt-submit", '"/context/prompt"'];
|
|
4489
|
+
function resolveTemplateCandidates(manifest) {
|
|
4490
|
+
const packageRoot = resolvePackageRoot();
|
|
4491
|
+
const templateFile = manifest.registration?.hooksFormat === "plugin-file" ? "plugin.ts" : "hooks.json";
|
|
4492
|
+
return [
|
|
4493
|
+
path17.join(packageRoot, "src", "symbionts", "templates", manifest.name, templateFile),
|
|
4494
|
+
path17.join(packageRoot, "dist", "src", "symbionts", "templates", manifest.name, templateFile)
|
|
4495
|
+
];
|
|
4496
|
+
}
|
|
4497
|
+
function readHooksTemplate(manifest) {
|
|
4498
|
+
for (const candidate of resolveTemplateCandidates(manifest)) {
|
|
4499
|
+
if (fs16.existsSync(candidate)) {
|
|
4500
|
+
return fs16.readFileSync(candidate, "utf-8");
|
|
4459
4501
|
}
|
|
4502
|
+
}
|
|
4503
|
+
return "";
|
|
4504
|
+
}
|
|
4505
|
+
function hasAnySignal(template, signals) {
|
|
4506
|
+
return signals.some((signal) => template.includes(signal));
|
|
4507
|
+
}
|
|
4508
|
+
function detectSymbiontInjectionSupport(manifest) {
|
|
4509
|
+
const template = readHooksTemplate(manifest);
|
|
4510
|
+
return {
|
|
4511
|
+
supportsSessionStartInjection: hasAnySignal(template, SESSION_START_SIGNALS),
|
|
4512
|
+
supportsPromptSubmitInjection: hasAnySignal(template, PROMPT_SUBMIT_SIGNALS)
|
|
4460
4513
|
};
|
|
4461
4514
|
}
|
|
4462
|
-
|
|
4515
|
+
|
|
4516
|
+
// src/daemon/api/symbionts.ts
|
|
4517
|
+
function listSymbiontInfos(vaultDir) {
|
|
4518
|
+
const manifests = loadManifests();
|
|
4519
|
+
let enabledNames = null;
|
|
4520
|
+
try {
|
|
4521
|
+
enabledNames = getEnabledSymbiontNames(loadMergedConfig(vaultDir));
|
|
4522
|
+
} catch {
|
|
4523
|
+
}
|
|
4524
|
+
return manifests.map((manifest) => ({
|
|
4525
|
+
name: manifest.name,
|
|
4526
|
+
displayName: manifest.displayName,
|
|
4527
|
+
binary: manifest.binary,
|
|
4528
|
+
enabled: enabledNames ? enabledNames.has(manifest.name) : true,
|
|
4529
|
+
...manifest.resumeCommand ? { resumeCommand: manifest.resumeCommand } : {},
|
|
4530
|
+
...detectSymbiontInjectionSupport(manifest)
|
|
4531
|
+
}));
|
|
4532
|
+
}
|
|
4533
|
+
async function handleListSymbionts(vaultDir) {
|
|
4534
|
+
return { body: { symbionts: listSymbiontInfos(vaultDir) } };
|
|
4535
|
+
}
|
|
4536
|
+
|
|
4537
|
+
// src/daemon/cortex.ts
|
|
4538
|
+
var CORTEX_PROMPT_BUILDER_TASK = "cortex-prompt-builder";
|
|
4539
|
+
var CORTEX_INSTRUCTIONS_TASK = "cortex-instructions";
|
|
4540
|
+
var JSON_INDENT = 2;
|
|
4541
|
+
function isCortexPromptBuilderDetails(value) {
|
|
4542
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4543
|
+
}
|
|
4544
|
+
function getLatestReportForAction(runId, action) {
|
|
4545
|
+
const reports = listReports(runId);
|
|
4546
|
+
for (let i = reports.length - 1; i >= 0; i -= 1) {
|
|
4547
|
+
if (reports[i]?.action === action) return reports[i];
|
|
4548
|
+
}
|
|
4549
|
+
return void 0;
|
|
4550
|
+
}
|
|
4551
|
+
function getCortexInstructionsSnapshot(config) {
|
|
4552
|
+
const row = getCortexInstructions(DEFAULT_AGENT_ID);
|
|
4553
|
+
return {
|
|
4554
|
+
content: row?.content ?? "",
|
|
4555
|
+
generatedAt: row?.generated_at ?? null,
|
|
4556
|
+
sourceRunId: row?.source_run_id ?? null,
|
|
4557
|
+
enabled: config.context.cortex_enabled,
|
|
4558
|
+
stored: Boolean(row)
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
function resolvePromptBuilderSymbiont(vaultDir, requestedName) {
|
|
4562
|
+
const enabledSymbionts = listSymbiontInfos(vaultDir).filter((symbiont) => symbiont.enabled);
|
|
4563
|
+
if (enabledSymbionts.length === 0) return null;
|
|
4564
|
+
if (!requestedName) return enabledSymbionts[0] ?? null;
|
|
4565
|
+
return enabledSymbionts.find((symbiont) => symbiont.name === requestedName) ?? null;
|
|
4566
|
+
}
|
|
4567
|
+
async function buildCortexPrompt(vaultDir, deps, goal, requestedSymbiont) {
|
|
4568
|
+
const targetSymbiont = resolvePromptBuilderSymbiont(vaultDir, requestedSymbiont);
|
|
4569
|
+
const delivery = resolveInstructionDelivery(deps.config.context, targetSymbiont);
|
|
4570
|
+
const instructions = delivery.inlineInstructions ? getCortexInstructions(DEFAULT_AGENT_ID) : null;
|
|
4571
|
+
const builderInstruction = [
|
|
4572
|
+
`Goal:
|
|
4573
|
+
${goal.trim()}`,
|
|
4574
|
+
"",
|
|
4575
|
+
"## Target symbiont",
|
|
4576
|
+
JSON.stringify(
|
|
4577
|
+
targetSymbiont ? {
|
|
4578
|
+
name: targetSymbiont.name,
|
|
4579
|
+
displayName: targetSymbiont.displayName,
|
|
4580
|
+
supportsSessionStartInjection: targetSymbiont.supportsSessionStartInjection,
|
|
4581
|
+
supportsPromptSubmitInjection: targetSymbiont.supportsPromptSubmitInjection
|
|
4582
|
+
} : null,
|
|
4583
|
+
null,
|
|
4584
|
+
JSON_INDENT
|
|
4585
|
+
),
|
|
4586
|
+
"",
|
|
4587
|
+
"## Delivery contract",
|
|
4588
|
+
JSON.stringify(
|
|
4589
|
+
{
|
|
4590
|
+
inline_instructions: delivery.inlineInstructions,
|
|
4591
|
+
reason: delivery.reason
|
|
4592
|
+
},
|
|
4593
|
+
null,
|
|
4594
|
+
JSON_INDENT
|
|
4595
|
+
),
|
|
4596
|
+
"",
|
|
4597
|
+
delivery.inlineInstructions ? [
|
|
4598
|
+
"## Current Cortex session-start instructions",
|
|
4599
|
+
instructions?.content || "No current Cortex instructions are available.",
|
|
4600
|
+
""
|
|
4601
|
+
].join("\n") : "## Current Cortex session-start instructions\nOmit them from the prompt because this symbiont receives session-start injection.\n"
|
|
4602
|
+
].join("\n");
|
|
4603
|
+
const resultPromise = runAgent(vaultDir, {
|
|
4604
|
+
task: CORTEX_PROMPT_BUILDER_TASK,
|
|
4605
|
+
agentId: DEFAULT_AGENT_ID,
|
|
4606
|
+
instruction: builderInstruction,
|
|
4607
|
+
embeddingManager: deps.embeddingManager
|
|
4608
|
+
});
|
|
4609
|
+
const runId = getLatestRunId(DEFAULT_AGENT_ID, CORTEX_PROMPT_BUILDER_TASK);
|
|
4610
|
+
const tracked = resultPromise.catch((err) => {
|
|
4611
|
+
deps.logger.warn(LOG_KINDS.AGENT_ERROR, "cortex-prompt-builder task failed", {
|
|
4612
|
+
run_id: runId ?? void 0,
|
|
4613
|
+
error: String(err)
|
|
4614
|
+
});
|
|
4615
|
+
});
|
|
4616
|
+
deps.registerInflightRun?.(tracked);
|
|
4617
|
+
return {
|
|
4618
|
+
started: true,
|
|
4619
|
+
runId,
|
|
4620
|
+
inlineInstructions: delivery.inlineInstructions,
|
|
4621
|
+
targetSymbiont
|
|
4622
|
+
};
|
|
4623
|
+
}
|
|
4624
|
+
function getCortexPromptResult(runId) {
|
|
4625
|
+
const run = getRun(runId);
|
|
4626
|
+
if (!run) return null;
|
|
4627
|
+
const reports = listReports(runId);
|
|
4628
|
+
const promptReport = getLatestReportForAction(runId, "cortex_prompt_builder");
|
|
4629
|
+
const details = tryParseJson(promptReport?.details, isCortexPromptBuilderDetails);
|
|
4630
|
+
return {
|
|
4631
|
+
runId,
|
|
4632
|
+
status: run.status,
|
|
4633
|
+
prompt: details?.prompt ?? "",
|
|
4634
|
+
reports: reports.map((report) => ({
|
|
4635
|
+
id: report.id,
|
|
4636
|
+
action: report.action,
|
|
4637
|
+
summary: report.summary,
|
|
4638
|
+
created_at: report.created_at
|
|
4639
|
+
})),
|
|
4640
|
+
error: run.error
|
|
4641
|
+
};
|
|
4642
|
+
}
|
|
4643
|
+
async function triggerCortexInstructions(deps) {
|
|
4644
|
+
const { vaultDir, embeddingManager, liveConfig, logger, getTeamClient } = deps;
|
|
4645
|
+
const loadExecutor = deps.loadExecutor ?? (() => import("./executor-F2YU7HXJ.js"));
|
|
4646
|
+
const config = liveConfig.current;
|
|
4647
|
+
if (config.agent.event_tasks_enabled === false) {
|
|
4648
|
+
return { started: false, reason: "event-tasks-disabled" };
|
|
4649
|
+
}
|
|
4650
|
+
if (!hasConfiguredProvider(config, CORTEX_INSTRUCTIONS_TASK)) {
|
|
4651
|
+
return { started: false, reason: "provider-not-configured" };
|
|
4652
|
+
}
|
|
4653
|
+
let runAgentFn;
|
|
4654
|
+
try {
|
|
4655
|
+
({ runAgent: runAgentFn } = await loadExecutor());
|
|
4656
|
+
} catch (err) {
|
|
4657
|
+
logger.warn(LOG_KINDS.AGENT_ERROR, "cortex-instructions: agent module unavailable", {
|
|
4658
|
+
error: String(err)
|
|
4659
|
+
});
|
|
4660
|
+
return {
|
|
4661
|
+
started: false,
|
|
4662
|
+
reason: "agent-module-unavailable",
|
|
4663
|
+
error: String(err)
|
|
4664
|
+
};
|
|
4665
|
+
}
|
|
4666
|
+
try {
|
|
4667
|
+
const built = await buildCortexInstructionsInput(config, getTeamClient);
|
|
4668
|
+
const resultPromise = runAgentFn(vaultDir, {
|
|
4669
|
+
task: CORTEX_INSTRUCTIONS_TASK,
|
|
4670
|
+
agentId: DEFAULT_AGENT_ID,
|
|
4671
|
+
instruction: built.instruction,
|
|
4672
|
+
runContext: { cortex_instruction_input_hash: built.inputHash },
|
|
4673
|
+
embeddingManager
|
|
4674
|
+
});
|
|
4675
|
+
const runId = getLatestRunId(DEFAULT_AGENT_ID, CORTEX_INSTRUCTIONS_TASK);
|
|
4676
|
+
const tracked = resultPromise.catch((err) => {
|
|
4677
|
+
logger.warn(LOG_KINDS.AGENT_ERROR, "Cortex instructions task failed", {
|
|
4678
|
+
error: String(err),
|
|
4679
|
+
run_id: runId ?? void 0
|
|
4680
|
+
});
|
|
4681
|
+
});
|
|
4682
|
+
deps.registerInflightRun?.(tracked);
|
|
4683
|
+
return { started: true, runId };
|
|
4684
|
+
} catch (err) {
|
|
4685
|
+
logger.warn(LOG_KINDS.AGENT_ERROR, "Failed to start cortex-instructions task", {
|
|
4686
|
+
error: String(err)
|
|
4687
|
+
});
|
|
4688
|
+
return {
|
|
4689
|
+
started: false,
|
|
4690
|
+
reason: "startup-failed",
|
|
4691
|
+
error: String(err)
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
|
|
4696
|
+
// src/daemon/api/context.ts
|
|
4697
|
+
var SessionContextBody = external_exports.object({
|
|
4698
|
+
session_id: external_exports.string().optional(),
|
|
4699
|
+
branch: external_exports.string().optional()
|
|
4700
|
+
});
|
|
4701
|
+
var ResumeContextBody = external_exports.object({
|
|
4702
|
+
session_id: external_exports.string(),
|
|
4703
|
+
parent_session_id: external_exports.string().optional(),
|
|
4704
|
+
branch: external_exports.string().optional()
|
|
4705
|
+
});
|
|
4706
|
+
var PromptContextBody = external_exports.object({
|
|
4707
|
+
prompt: external_exports.string(),
|
|
4708
|
+
session_id: external_exports.string().optional()
|
|
4709
|
+
});
|
|
4710
|
+
function createSessionContextHandler(deps) {
|
|
4711
|
+
return async function handleSessionContext(req) {
|
|
4712
|
+
const { session_id, branch } = SessionContextBody.parse(req.body);
|
|
4713
|
+
const { logger, liveConfig } = deps;
|
|
4714
|
+
const config = liveConfig.current;
|
|
4715
|
+
logger.debug(LOG_KINDS.CONTEXT_QUERY, "Session context query", { session_id });
|
|
4716
|
+
try {
|
|
4717
|
+
const cortexEnabled = shouldInjectCortex(config.context);
|
|
4718
|
+
const digestEnabled = shouldInjectSessionStartDigest(config.context);
|
|
4719
|
+
if (!cortexEnabled && !digestEnabled) {
|
|
4720
|
+
logger.debug(LOG_KINDS.CONTEXT_SESSION, "Session-start context disabled", { session_id });
|
|
4721
|
+
return { body: { text: "" } };
|
|
4722
|
+
}
|
|
4723
|
+
let sourceRunId = null;
|
|
4724
|
+
let cortexContent = "";
|
|
4725
|
+
if (cortexEnabled) {
|
|
4726
|
+
const snapshot = getCortexInstructionsSnapshot(config);
|
|
4727
|
+
if (snapshot.content) {
|
|
4728
|
+
cortexContent = snapshot.content;
|
|
4729
|
+
sourceRunId = snapshot.sourceRunId;
|
|
4730
|
+
} else {
|
|
4731
|
+
logger.debug(LOG_KINDS.CONTEXT_SESSION, "No stored Cortex instructions available for session start", {
|
|
4732
|
+
session_id
|
|
4733
|
+
});
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
const composed = composeSessionStartContext(config, cortexContent);
|
|
4737
|
+
const textParts = composed.parts.map((p) => p.text);
|
|
4738
|
+
const sourceParts = composed.parts.map(
|
|
4739
|
+
(p) => p.kind === "cortex" ? "cortex" : `digest:${p.tier ?? config.context.digest_tier}`
|
|
4740
|
+
);
|
|
4741
|
+
if (digestEnabled && !composed.parts.some((p) => p.kind === "digest")) {
|
|
4742
|
+
logger.debug(LOG_KINDS.CONTEXT_SESSION, "No preferred digest extract available for session start", {
|
|
4743
|
+
session_id,
|
|
4744
|
+
preferred_tier: config.context.digest_tier
|
|
4745
|
+
});
|
|
4746
|
+
}
|
|
4747
|
+
if (textParts.length === 0) {
|
|
4748
|
+
return { body: { text: "" } };
|
|
4749
|
+
}
|
|
4750
|
+
if (branch) {
|
|
4751
|
+
textParts.push(`Branch:: \`${branch}\``);
|
|
4752
|
+
}
|
|
4753
|
+
textParts.push(`Session:: \`${session_id}\``);
|
|
4754
|
+
const source = sourceParts.join("+");
|
|
4755
|
+
const contextText = textParts.join("\n\n");
|
|
4756
|
+
const estimatedTokens = estimateTokens(contextText);
|
|
4757
|
+
logger.info(
|
|
4758
|
+
LOG_KINDS.CONTEXT_SESSION,
|
|
4759
|
+
`Session context: ${estimatedTokens} est. tokens, source=${source}`,
|
|
4760
|
+
{
|
|
4761
|
+
session_id,
|
|
4762
|
+
source,
|
|
4763
|
+
branch,
|
|
4764
|
+
source_run_id: sourceRunId,
|
|
4765
|
+
text_length: contextText.length,
|
|
4766
|
+
estimated_tokens: estimatedTokens,
|
|
4767
|
+
injected_text: contextText
|
|
4768
|
+
}
|
|
4769
|
+
);
|
|
4770
|
+
return {
|
|
4771
|
+
body: {
|
|
4772
|
+
text: contextText,
|
|
4773
|
+
source
|
|
4774
|
+
}
|
|
4775
|
+
};
|
|
4776
|
+
} catch (error) {
|
|
4777
|
+
logger.error(LOG_KINDS.CONTEXT_SESSION, "Session context failed", { error: error.message });
|
|
4778
|
+
return { body: { text: "" } };
|
|
4779
|
+
}
|
|
4780
|
+
};
|
|
4781
|
+
}
|
|
4782
|
+
function createResumeContextHandler(deps) {
|
|
4463
4783
|
return async function handleResumeContext(req) {
|
|
4464
4784
|
const { session_id, parent_session_id, branch } = ResumeContextBody.parse(req.body);
|
|
4465
4785
|
const { logger } = deps;
|
|
@@ -4555,9 +4875,7 @@ function createPromptContextHandler(deps) {
|
|
|
4555
4875
|
raw_results: vectorResults.length,
|
|
4556
4876
|
top_similarity: vectorResults[0]?.similarity
|
|
4557
4877
|
});
|
|
4558
|
-
if (vectorResults.length === 0) {
|
|
4559
|
-
return { body: { text: "" } };
|
|
4560
|
-
}
|
|
4878
|
+
if (vectorResults.length === 0) return { body: { text: "" } };
|
|
4561
4879
|
const eligible = vectorResults.filter(
|
|
4562
4880
|
(r) => !EXCLUDED_SPORE_STATUSES.has(r.metadata.status)
|
|
4563
4881
|
);
|
|
@@ -4568,20 +4886,22 @@ function createPromptContextHandler(deps) {
|
|
|
4568
4886
|
const topResults = eligible.slice(0, maxSpores);
|
|
4569
4887
|
const hydrated = hydrateSearchResults(topResults);
|
|
4570
4888
|
const spores = hydrated.filter((r) => r.type === "spore");
|
|
4571
|
-
if (spores.length === 0) {
|
|
4572
|
-
return { body: { text: "" } };
|
|
4573
|
-
}
|
|
4889
|
+
if (spores.length === 0) return { body: { text: "" } };
|
|
4574
4890
|
const text = formatSporeContext(spores);
|
|
4575
4891
|
const promptTokens = estimateTokens(text);
|
|
4576
4892
|
const titles = spores.map((s) => s.title);
|
|
4577
|
-
logger.info(
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4893
|
+
logger.info(
|
|
4894
|
+
LOG_KINDS.CONTEXT_PROMPT,
|
|
4895
|
+
`Prompt context: ${spores.length} spores [${titles.join(", ")}] (~${promptTokens} tokens)`,
|
|
4896
|
+
{
|
|
4897
|
+
session_id,
|
|
4898
|
+
spore_count: spores.length,
|
|
4899
|
+
spore_titles: titles,
|
|
4900
|
+
scores: spores.map((s) => s.score.toFixed(3)),
|
|
4901
|
+
estimated_tokens: promptTokens,
|
|
4902
|
+
injected_text: text
|
|
4903
|
+
}
|
|
4904
|
+
);
|
|
4585
4905
|
return { body: { text } };
|
|
4586
4906
|
};
|
|
4587
4907
|
}
|
|
@@ -4600,6 +4920,75 @@ function formatSporeContext(spores) {
|
|
|
4600
4920
|
return text === header ? "" : text;
|
|
4601
4921
|
}
|
|
4602
4922
|
|
|
4923
|
+
// src/daemon/api/cortex.ts
|
|
4924
|
+
var PromptBuilderBody = external_exports.object({
|
|
4925
|
+
goal: external_exports.string().trim().min(1),
|
|
4926
|
+
symbiont: external_exports.string().trim().optional()
|
|
4927
|
+
});
|
|
4928
|
+
var PromptBuilderStatusParams = external_exports.object({
|
|
4929
|
+
runId: external_exports.string().trim().min(1)
|
|
4930
|
+
});
|
|
4931
|
+
function createCortexHandlers(vaultDir, deps) {
|
|
4932
|
+
async function handleGetInstructions() {
|
|
4933
|
+
const snapshot = getCortexInstructionsSnapshot(deps.liveConfig.current);
|
|
4934
|
+
return { body: snapshot };
|
|
4935
|
+
}
|
|
4936
|
+
async function handleRefreshInstructions() {
|
|
4937
|
+
const result = await triggerCortexInstructions({
|
|
4938
|
+
vaultDir,
|
|
4939
|
+
embeddingManager: deps.embeddingManager,
|
|
4940
|
+
liveConfig: deps.liveConfig,
|
|
4941
|
+
logger: deps.logger,
|
|
4942
|
+
getTeamClient: deps.getTeamClient,
|
|
4943
|
+
registerInflightRun: deps.registerInflightRun
|
|
4944
|
+
});
|
|
4945
|
+
if (!result.started && (result.reason === "provider-not-configured" || result.reason === "event-tasks-disabled")) {
|
|
4946
|
+
return {
|
|
4947
|
+
status: 400,
|
|
4948
|
+
body: {
|
|
4949
|
+
...errorBody(
|
|
4950
|
+
result.reason,
|
|
4951
|
+
result.reason === "provider-not-configured" ? "No agent provider configured. Configure one in Settings." : "Event-driven tasks are disabled. Enable them in Settings."
|
|
4952
|
+
),
|
|
4953
|
+
started: false,
|
|
4954
|
+
reason: result.reason
|
|
4955
|
+
}
|
|
4956
|
+
};
|
|
4957
|
+
}
|
|
4958
|
+
return { body: result };
|
|
4959
|
+
}
|
|
4960
|
+
async function handleBuildPrompt(req) {
|
|
4961
|
+
const { goal, symbiont } = PromptBuilderBody.parse(req.body);
|
|
4962
|
+
const result = await buildCortexPrompt(
|
|
4963
|
+
vaultDir,
|
|
4964
|
+
{
|
|
4965
|
+
config: deps.liveConfig.current,
|
|
4966
|
+
embeddingManager: deps.embeddingManager,
|
|
4967
|
+
getTeamClient: deps.getTeamClient,
|
|
4968
|
+
logger: deps.logger,
|
|
4969
|
+
registerInflightRun: deps.registerInflightRun
|
|
4970
|
+
},
|
|
4971
|
+
goal,
|
|
4972
|
+
symbiont
|
|
4973
|
+
);
|
|
4974
|
+
return { body: result };
|
|
4975
|
+
}
|
|
4976
|
+
async function handleGetPromptResult(req) {
|
|
4977
|
+
const { runId } = PromptBuilderStatusParams.parse(req.params);
|
|
4978
|
+
const result = getCortexPromptResult(runId);
|
|
4979
|
+
if (!result) {
|
|
4980
|
+
return { status: 404, body: errorBody("run-not-found", "Run not found") };
|
|
4981
|
+
}
|
|
4982
|
+
return { body: result };
|
|
4983
|
+
}
|
|
4984
|
+
return {
|
|
4985
|
+
handleGetInstructions,
|
|
4986
|
+
handleRefreshInstructions,
|
|
4987
|
+
handleBuildPrompt,
|
|
4988
|
+
handleGetPromptResult
|
|
4989
|
+
};
|
|
4990
|
+
}
|
|
4991
|
+
|
|
4603
4992
|
// src/db/queries/feed.ts
|
|
4604
4993
|
function getActivityFeed(limit = FEED_DEFAULT_LIMIT) {
|
|
4605
4994
|
const db = getDatabase();
|
|
@@ -4638,24 +5027,6 @@ async function handleGetFeed(req) {
|
|
|
4638
5027
|
return { body: feed };
|
|
4639
5028
|
}
|
|
4640
5029
|
|
|
4641
|
-
// src/daemon/api/symbionts.ts
|
|
4642
|
-
async function handleListSymbionts(vaultDir) {
|
|
4643
|
-
const manifests = loadManifests();
|
|
4644
|
-
let enabledNames = null;
|
|
4645
|
-
try {
|
|
4646
|
-
enabledNames = getEnabledSymbiontNames(loadMergedConfig(vaultDir));
|
|
4647
|
-
} catch {
|
|
4648
|
-
}
|
|
4649
|
-
const symbionts = manifests.map((m) => ({
|
|
4650
|
-
name: m.name,
|
|
4651
|
-
displayName: m.displayName,
|
|
4652
|
-
binary: m.binary,
|
|
4653
|
-
enabled: enabledNames ? enabledNames.has(m.name) : true,
|
|
4654
|
-
...m.resumeCommand ? { resumeCommand: m.resumeCommand } : {}
|
|
4655
|
-
}));
|
|
4656
|
-
return { body: { symbionts } };
|
|
4657
|
-
}
|
|
4658
|
-
|
|
4659
5030
|
// src/daemon/api/embedding.ts
|
|
4660
5031
|
var EMBEDDING_STATUS_IDLE = "idle";
|
|
4661
5032
|
var EMBEDDING_STATUS_PENDING = "pending";
|
|
@@ -5659,17 +6030,17 @@ var SqliteRecordSource = class {
|
|
|
5659
6030
|
};
|
|
5660
6031
|
|
|
5661
6032
|
// src/daemon/database/manager.ts
|
|
5662
|
-
import
|
|
6033
|
+
import fs18 from "fs";
|
|
5663
6034
|
|
|
5664
6035
|
// src/db/queries/database.ts
|
|
5665
|
-
import
|
|
6036
|
+
import fs17 from "fs";
|
|
5666
6037
|
function pragmaScalar(name) {
|
|
5667
6038
|
const db = getDatabase();
|
|
5668
6039
|
return db.pragma(name, { simple: true });
|
|
5669
6040
|
}
|
|
5670
6041
|
function safeFileSize(filePath) {
|
|
5671
6042
|
try {
|
|
5672
|
-
return
|
|
6043
|
+
return fs17.statSync(filePath).size;
|
|
5673
6044
|
} catch (err) {
|
|
5674
6045
|
if (err.code === "ENOENT") return 0;
|
|
5675
6046
|
throw err;
|
|
@@ -5941,7 +6312,7 @@ var DatabaseMaintenanceManager = class {
|
|
|
5941
6312
|
}
|
|
5942
6313
|
async vacuum() {
|
|
5943
6314
|
const size_before = this.fileSize();
|
|
5944
|
-
const stats = await
|
|
6315
|
+
const stats = await fs18.promises.statfs(this.vaultDir);
|
|
5945
6316
|
const free_bytes = Number(stats.bavail) * Number(stats.bsize);
|
|
5946
6317
|
const required_bytes = size_before * VACUUM_FREE_SPACE_MULTIPLIER;
|
|
5947
6318
|
if (free_bytes < required_bytes) {
|
|
@@ -5989,7 +6360,7 @@ var DatabaseMaintenanceManager = class {
|
|
|
5989
6360
|
}
|
|
5990
6361
|
fileSize() {
|
|
5991
6362
|
try {
|
|
5992
|
-
return
|
|
6363
|
+
return fs18.statSync(this.dbPath).size;
|
|
5993
6364
|
} catch {
|
|
5994
6365
|
return 0;
|
|
5995
6366
|
}
|
|
@@ -6306,6 +6677,16 @@ async function handleUpdateTaskConfig(req, vaultDir) {
|
|
|
6306
6677
|
};
|
|
6307
6678
|
}
|
|
6308
6679
|
|
|
6680
|
+
// src/agent/types.ts
|
|
6681
|
+
var PROVIDER_TYPES = [
|
|
6682
|
+
"anthropic",
|
|
6683
|
+
"ollama",
|
|
6684
|
+
"lmstudio",
|
|
6685
|
+
"openai",
|
|
6686
|
+
"openrouter",
|
|
6687
|
+
"openai-compatible"
|
|
6688
|
+
];
|
|
6689
|
+
|
|
6309
6690
|
// src/daemon/api/providers.ts
|
|
6310
6691
|
var ANTHROPIC_MODELS_TIMEOUT_MS = 5e3;
|
|
6311
6692
|
var ANTHROPIC_MODELS_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
@@ -6313,33 +6694,79 @@ var anthropicModelsCache = null;
|
|
|
6313
6694
|
var HTTP_OK2 = 200;
|
|
6314
6695
|
var HTTP_BAD_REQUEST2 = 400;
|
|
6315
6696
|
async function handleGetProviders() {
|
|
6316
|
-
const
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6697
|
+
const detectionPlan = [
|
|
6698
|
+
{
|
|
6699
|
+
detect: () => detectAnthropic(),
|
|
6700
|
+
fallback: { type: "anthropic", runtime: "claude-sdk", available: false, models: [] }
|
|
6701
|
+
},
|
|
6702
|
+
{
|
|
6703
|
+
detect: () => detectLocalProviderInfo("ollama", OllamaBackend.DEFAULT_BASE_URL),
|
|
6704
|
+
fallback: { type: "ollama", runtime: "claude-sdk", available: false, baseUrl: OllamaBackend.DEFAULT_BASE_URL, models: [] }
|
|
6705
|
+
},
|
|
6706
|
+
{
|
|
6707
|
+
detect: () => detectLocalProviderInfo("lmstudio", LmStudioBackend.DEFAULT_BASE_URL),
|
|
6708
|
+
fallback: { type: "lmstudio", runtime: "claude-sdk", available: false, baseUrl: LmStudioBackend.DEFAULT_BASE_URL, models: [] }
|
|
6709
|
+
},
|
|
6710
|
+
{
|
|
6711
|
+
detect: () => detectRemoteProviderInfo("openai", DEFAULT_OPENAI_URL),
|
|
6712
|
+
fallback: { type: "openai", runtime: "openai-agents", available: false, authConfigured: false, baseUrl: DEFAULT_OPENAI_URL, models: [] }
|
|
6713
|
+
},
|
|
6714
|
+
{
|
|
6715
|
+
detect: () => detectRemoteProviderInfo("openrouter", DEFAULT_OPENROUTER_URL),
|
|
6716
|
+
fallback: { type: "openrouter", runtime: "openai-agents", available: false, authConfigured: false, baseUrl: DEFAULT_OPENROUTER_URL, models: [] }
|
|
6717
|
+
},
|
|
6718
|
+
{
|
|
6719
|
+
detect: () => detectLocalProviderInfo("openai-compatible", LmStudioBackend.DEFAULT_BASE_URL),
|
|
6720
|
+
fallback: { type: "openai-compatible", runtime: "openai-agents", available: false, baseUrl: LmStudioBackend.DEFAULT_BASE_URL, models: [] }
|
|
6721
|
+
}
|
|
6722
|
+
];
|
|
6723
|
+
const results = await Promise.allSettled(detectionPlan.map((entry) => entry.detect()));
|
|
6321
6724
|
const providers = results.map(
|
|
6322
|
-
(
|
|
6725
|
+
(result, index) => result.status === "fulfilled" ? result.value : detectionPlan[index].fallback
|
|
6323
6726
|
);
|
|
6324
6727
|
return { status: HTTP_OK2, body: { providers } };
|
|
6325
6728
|
}
|
|
6729
|
+
var ProviderTestBody = external_exports.object({
|
|
6730
|
+
type: external_exports.enum(PROVIDER_TYPES),
|
|
6731
|
+
baseUrl: external_exports.string().optional(),
|
|
6732
|
+
/**
|
|
6733
|
+
* Deprecated: `base_url` is the legacy snake_case key. Accepted for one
|
|
6734
|
+
* release and then removed. Use `baseUrl` going forward.
|
|
6735
|
+
*/
|
|
6736
|
+
base_url: external_exports.string().optional(),
|
|
6737
|
+
local_backend: external_exports.enum(["ollama", "lmstudio"]).optional(),
|
|
6738
|
+
model: external_exports.string().optional()
|
|
6739
|
+
});
|
|
6326
6740
|
async function handleTestProvider(req) {
|
|
6327
|
-
const
|
|
6328
|
-
|
|
6329
|
-
if (!type || !["anthropic", "ollama", "lmstudio"].includes(type)) {
|
|
6741
|
+
const parse = ProviderTestBody.safeParse(req.body);
|
|
6742
|
+
if (!parse.success) {
|
|
6330
6743
|
return {
|
|
6331
6744
|
status: HTTP_BAD_REQUEST2,
|
|
6332
|
-
body: { error:
|
|
6745
|
+
body: { error: `type is required and must be one of: ${PROVIDER_TYPES.join(", ")}` }
|
|
6333
6746
|
};
|
|
6334
6747
|
}
|
|
6335
|
-
const
|
|
6748
|
+
const parsed = parse.data;
|
|
6749
|
+
if (parsed.base_url !== void 0 && parsed.baseUrl === void 0) {
|
|
6750
|
+
process.stderr.write(
|
|
6751
|
+
"[myco providers] POST /api/providers/test: base_url is deprecated; use baseUrl\n"
|
|
6752
|
+
);
|
|
6753
|
+
}
|
|
6754
|
+
const type = parsed.type;
|
|
6755
|
+
const baseUrl = parsed.baseUrl ?? parsed.base_url;
|
|
6756
|
+
const localBackend = parsed.local_backend;
|
|
6336
6757
|
const start = performance.now();
|
|
6337
6758
|
let result;
|
|
6338
6759
|
try {
|
|
6339
6760
|
if (type === "ollama") {
|
|
6340
|
-
result = await
|
|
6761
|
+
result = await testResolvedLocalProvider("ollama", baseUrl);
|
|
6341
6762
|
} else if (type === "lmstudio") {
|
|
6342
|
-
result = await
|
|
6763
|
+
result = await testResolvedLocalProvider("lmstudio", baseUrl);
|
|
6764
|
+
} else if (type === "openai-compatible") {
|
|
6765
|
+
result = await testResolvedLocalProvider("openai-compatible", baseUrl, localBackend);
|
|
6766
|
+
} else if (type === "openai") {
|
|
6767
|
+
result = await testRemoteProvider("openai", "OpenAI");
|
|
6768
|
+
} else if (type === "openrouter") {
|
|
6769
|
+
result = await testRemoteProvider("openrouter", "OpenRouter");
|
|
6343
6770
|
} else {
|
|
6344
6771
|
result = testAnthropic();
|
|
6345
6772
|
}
|
|
@@ -6352,15 +6779,21 @@ async function handleTestProvider(req) {
|
|
|
6352
6779
|
return { status: HTTP_OK2, body: result };
|
|
6353
6780
|
}
|
|
6354
6781
|
async function detectLocalProviderInfo(type, defaultBaseUrl) {
|
|
6355
|
-
const status = await checkLocalProvider(type);
|
|
6782
|
+
const status = await checkLocalProvider(type === "openai-compatible" ? "lmstudio" : type);
|
|
6356
6783
|
const variantFiltered = status.models.filter((m) => !/-ctx\d+/.test(m));
|
|
6357
6784
|
const models = filterLlmModels(variantFiltered);
|
|
6358
|
-
return {
|
|
6785
|
+
return {
|
|
6786
|
+
type,
|
|
6787
|
+
runtime: type === "openai-compatible" ? "openai-agents" : "claude-sdk",
|
|
6788
|
+
available: status.available,
|
|
6789
|
+
baseUrl: defaultBaseUrl,
|
|
6790
|
+
models
|
|
6791
|
+
};
|
|
6359
6792
|
}
|
|
6360
6793
|
async function detectAnthropic() {
|
|
6361
6794
|
const now = Date.now();
|
|
6362
6795
|
if (anthropicModelsCache && now - anthropicModelsCache.ts < ANTHROPIC_MODELS_CACHE_TTL_MS) {
|
|
6363
|
-
return { type: "anthropic", available: true, models: anthropicModelsCache.models };
|
|
6796
|
+
return { type: "anthropic", runtime: "claude-sdk", available: true, models: anthropicModelsCache.models };
|
|
6364
6797
|
}
|
|
6365
6798
|
let models = ANTHROPIC_MODELS;
|
|
6366
6799
|
try {
|
|
@@ -6376,7 +6809,28 @@ async function detectAnthropic() {
|
|
|
6376
6809
|
} catch {
|
|
6377
6810
|
}
|
|
6378
6811
|
anthropicModelsCache = { ts: now, models };
|
|
6379
|
-
return { type: "anthropic", available: true, models };
|
|
6812
|
+
return { type: "anthropic", runtime: "claude-sdk", available: true, models };
|
|
6813
|
+
}
|
|
6814
|
+
async function detectRemoteProviderInfo(type, baseUrl) {
|
|
6815
|
+
const authConfigured = Boolean(getRemoteProviderApiKey(type));
|
|
6816
|
+
let models = [];
|
|
6817
|
+
let available = false;
|
|
6818
|
+
if (authConfigured) {
|
|
6819
|
+
try {
|
|
6820
|
+
models = await fetchRemoteProviderModels(type, void 0, ANTHROPIC_MODELS_TIMEOUT_MS);
|
|
6821
|
+
available = true;
|
|
6822
|
+
} catch {
|
|
6823
|
+
available = false;
|
|
6824
|
+
}
|
|
6825
|
+
}
|
|
6826
|
+
return {
|
|
6827
|
+
type,
|
|
6828
|
+
runtime: "openai-agents",
|
|
6829
|
+
available,
|
|
6830
|
+
authConfigured,
|
|
6831
|
+
baseUrl,
|
|
6832
|
+
models
|
|
6833
|
+
};
|
|
6380
6834
|
}
|
|
6381
6835
|
async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
|
|
6382
6836
|
const available = await backend.isAvailable();
|
|
@@ -6385,9 +6839,114 @@ async function testLocalProvider(backend, label, defaultBaseUrl, baseUrl) {
|
|
|
6385
6839
|
}
|
|
6386
6840
|
return { ok: true };
|
|
6387
6841
|
}
|
|
6842
|
+
async function testResolvedLocalProvider(type, baseUrl, localBackend) {
|
|
6843
|
+
const kind = inferLocalOpenAIBackendKind({ type, localBackend, baseUrl }) ?? "lmstudio";
|
|
6844
|
+
const label = type === "openai-compatible" ? `OpenAI-compatible ${getLocalOpenAIBackendLabel(kind)} provider` : getLocalOpenAIBackendLabel(kind);
|
|
6845
|
+
return testLocalProvider(
|
|
6846
|
+
createLocalOpenAIBackend(kind, baseUrl),
|
|
6847
|
+
label,
|
|
6848
|
+
getLocalOpenAIBackendDefaultBaseUrl(kind),
|
|
6849
|
+
baseUrl
|
|
6850
|
+
);
|
|
6851
|
+
}
|
|
6388
6852
|
function testAnthropic() {
|
|
6389
6853
|
return { ok: true };
|
|
6390
6854
|
}
|
|
6855
|
+
async function testRemoteProvider(provider, label) {
|
|
6856
|
+
if (!getRemoteProviderApiKey(provider)) {
|
|
6857
|
+
return { ok: false, error: `${label} API key not configured in daemon secrets or environment` };
|
|
6858
|
+
}
|
|
6859
|
+
try {
|
|
6860
|
+
await fetchRemoteProviderModels(provider);
|
|
6861
|
+
return { ok: true };
|
|
6862
|
+
} catch (error) {
|
|
6863
|
+
return { ok: false, error: error instanceof Error ? error.message : `${label} connection failed` };
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
|
|
6867
|
+
// src/daemon/api/provider-secrets.ts
|
|
6868
|
+
var SECRET_PREVIEW_PREFIX_CHARS = 8;
|
|
6869
|
+
var SECRET_PREVIEW_SUFFIX_CHARS = 4;
|
|
6870
|
+
var SECRET_ENV_BY_PROVIDER = {
|
|
6871
|
+
openai: OPENAI_API_KEY_ENV,
|
|
6872
|
+
openrouter: OPENROUTER_API_KEY_ENV
|
|
6873
|
+
};
|
|
6874
|
+
function isSecretProvider(value) {
|
|
6875
|
+
return value === "openai" || value === "openrouter";
|
|
6876
|
+
}
|
|
6877
|
+
function maskSecret(secret) {
|
|
6878
|
+
if (secret.length <= SECRET_PREVIEW_PREFIX_CHARS + SECRET_PREVIEW_SUFFIX_CHARS) {
|
|
6879
|
+
return "*".repeat(secret.length);
|
|
6880
|
+
}
|
|
6881
|
+
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)}`;
|
|
6882
|
+
}
|
|
6883
|
+
function buildSecretInfo(provider, storedSecrets) {
|
|
6884
|
+
const envKey = SECRET_ENV_BY_PROVIDER[provider];
|
|
6885
|
+
const storedValue = storedSecrets[envKey];
|
|
6886
|
+
const envValue = process.env[envKey];
|
|
6887
|
+
const effectiveValue = storedValue ?? envValue;
|
|
6888
|
+
return {
|
|
6889
|
+
configured: Boolean(effectiveValue),
|
|
6890
|
+
envKey,
|
|
6891
|
+
maskedValue: effectiveValue ? maskSecret(effectiveValue) : null,
|
|
6892
|
+
source: storedValue ? "vault" : envValue ? "env" : "none"
|
|
6893
|
+
};
|
|
6894
|
+
}
|
|
6895
|
+
function getSecretInfo(vaultDir, provider) {
|
|
6896
|
+
return buildSecretInfo(provider, readSecrets(vaultDir));
|
|
6897
|
+
}
|
|
6898
|
+
async function handleGetProviderSecrets(vaultDir) {
|
|
6899
|
+
const storedSecrets = readSecrets(vaultDir);
|
|
6900
|
+
return {
|
|
6901
|
+
body: {
|
|
6902
|
+
secrets: {
|
|
6903
|
+
openai: buildSecretInfo("openai", storedSecrets),
|
|
6904
|
+
openrouter: buildSecretInfo("openrouter", storedSecrets)
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
};
|
|
6908
|
+
}
|
|
6909
|
+
async function handlePutProviderSecret(vaultDir, req) {
|
|
6910
|
+
const provider = req.params.provider;
|
|
6911
|
+
const body = req.body;
|
|
6912
|
+
if (!provider || !isSecretProvider(provider)) {
|
|
6913
|
+
return { status: 400, body: { error: "provider must be one of: openai, openrouter" } };
|
|
6914
|
+
}
|
|
6915
|
+
if (!body?.api_key?.trim()) {
|
|
6916
|
+
return { status: 400, body: { error: "api_key is required" } };
|
|
6917
|
+
}
|
|
6918
|
+
const envKey = SECRET_ENV_BY_PROVIDER[provider];
|
|
6919
|
+
const apiKey = body.api_key.trim();
|
|
6920
|
+
writeSecret(vaultDir, envKey, apiKey);
|
|
6921
|
+
process.env[envKey] = apiKey;
|
|
6922
|
+
if (provider === "openai") {
|
|
6923
|
+
process.env.OPENAI_API_KEY = apiKey;
|
|
6924
|
+
}
|
|
6925
|
+
return {
|
|
6926
|
+
body: {
|
|
6927
|
+
provider,
|
|
6928
|
+
secret: getSecretInfo(vaultDir, provider)
|
|
6929
|
+
}
|
|
6930
|
+
};
|
|
6931
|
+
}
|
|
6932
|
+
async function handleDeleteProviderSecret(vaultDir, req) {
|
|
6933
|
+
const provider = req.params.provider;
|
|
6934
|
+
if (!provider || !isSecretProvider(provider)) {
|
|
6935
|
+
return { status: 400, body: { error: "provider must be one of: openai, openrouter" } };
|
|
6936
|
+
}
|
|
6937
|
+
const envKey = SECRET_ENV_BY_PROVIDER[provider];
|
|
6938
|
+
deleteSecrets(vaultDir, [envKey]);
|
|
6939
|
+
delete process.env[envKey];
|
|
6940
|
+
if (provider === "openai") {
|
|
6941
|
+
delete process.env.OPENAI_API_KEY;
|
|
6942
|
+
}
|
|
6943
|
+
return {
|
|
6944
|
+
body: {
|
|
6945
|
+
provider,
|
|
6946
|
+
secret: getSecretInfo(vaultDir, provider)
|
|
6947
|
+
}
|
|
6948
|
+
};
|
|
6949
|
+
}
|
|
6391
6950
|
|
|
6392
6951
|
// src/daemon/task-scheduling.ts
|
|
6393
6952
|
import { resolve } from "path";
|
|
@@ -6440,7 +6999,7 @@ function buildScheduledJobs(tasks, configOverrides, context, initialLastRuns) {
|
|
|
6440
6999
|
// src/daemon/task-scheduling.ts
|
|
6441
7000
|
var SCHEDULED_JOB_PREFIX = "scheduled:";
|
|
6442
7001
|
async function registerScheduledTasks(powerManager, deps) {
|
|
6443
|
-
const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig } = deps;
|
|
7002
|
+
const { definitionsDir, vaultDir, embeddingManager, logger, liveConfig, getTeamClient } = deps;
|
|
6444
7003
|
const runningTasks = /* @__PURE__ */ new Set();
|
|
6445
7004
|
if (!definitionsDir) {
|
|
6446
7005
|
logger.warn(LOG_KINDS.AGENT_ERROR, "Skipping dynamic task scheduling \u2014 definitions directory unavailable");
|
|
@@ -6450,7 +7009,7 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6450
7009
|
if (!lastEnabled) {
|
|
6451
7010
|
logger.info(LOG_KINDS.AGENT_RUN, "Scheduled agent tasks disabled (agent.scheduled_tasks_enabled: false) \u2014 jobs registered but will no-op until enabled");
|
|
6452
7011
|
}
|
|
6453
|
-
const { loadAllTasks: loadAllTasks2 } = await import("./registry-
|
|
7012
|
+
const { loadAllTasks: loadAllTasks2 } = await import("./registry-M2Z5QBWH.js");
|
|
6454
7013
|
const allTasks = Array.from(loadAllTasks2(definitionsDir, vaultDir).values());
|
|
6455
7014
|
const taskAgentMap = /* @__PURE__ */ new Map();
|
|
6456
7015
|
for (const task of allTasks) {
|
|
@@ -6486,10 +7045,33 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6486
7045
|
lastEnabled = enabled;
|
|
6487
7046
|
}
|
|
6488
7047
|
if (!enabled) return;
|
|
6489
|
-
const { runAgent } = await import("./executor-
|
|
7048
|
+
const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
|
|
7049
|
+
const resumableRun = getLatestResumableRunForTask(DEFAULT_AGENT_ID, taskName);
|
|
7050
|
+
if (resumableRun) {
|
|
7051
|
+
const resumed = await runAgent2(vaultDir, {
|
|
7052
|
+
agentId: DEFAULT_AGENT_ID,
|
|
7053
|
+
task: taskName,
|
|
7054
|
+
resumeRunId: resumableRun.id,
|
|
7055
|
+
resumeMode: "scheduled",
|
|
7056
|
+
embeddingManager
|
|
7057
|
+
});
|
|
7058
|
+
logger.info(LOG_KINDS.AGENT_RUN, `Scheduled task ${taskName} resumed`, {
|
|
7059
|
+
status: resumed.status,
|
|
7060
|
+
runId: resumed.runId
|
|
7061
|
+
});
|
|
7062
|
+
return;
|
|
7063
|
+
}
|
|
6490
7064
|
const taskConfig = config.agent.tasks?.[taskName];
|
|
6491
7065
|
const projectRoot = resolve(vaultDir, "..");
|
|
6492
|
-
const built = buildTaskInstruction(
|
|
7066
|
+
const built = await buildTaskInstruction(
|
|
7067
|
+
taskName,
|
|
7068
|
+
taskConfig?.params,
|
|
7069
|
+
taskAgentMap.get(taskName),
|
|
7070
|
+
projectRoot,
|
|
7071
|
+
embeddingManager,
|
|
7072
|
+
config,
|
|
7073
|
+
getTeamClient
|
|
7074
|
+
);
|
|
6493
7075
|
if (isInstructionRequiredTask(taskName) && !built) {
|
|
6494
7076
|
logger.info(
|
|
6495
7077
|
LOG_KINDS.AGENT_RUN,
|
|
@@ -6498,7 +7080,7 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6498
7080
|
);
|
|
6499
7081
|
return;
|
|
6500
7082
|
}
|
|
6501
|
-
const result = await
|
|
7083
|
+
const result = await runAgent2(vaultDir, {
|
|
6502
7084
|
task: taskName,
|
|
6503
7085
|
instruction: built?.instruction,
|
|
6504
7086
|
runContext: built?.context,
|
|
@@ -6525,7 +7107,7 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6525
7107
|
link: `/agent?run=${result.runId}`,
|
|
6526
7108
|
metadata: { taskName, runId: result.runId }
|
|
6527
7109
|
}, config);
|
|
6528
|
-
const { countToolCallsByRun } = await import("./turns-
|
|
7110
|
+
const { countToolCallsByRun } = await import("./turns-HU2CTZAP.js");
|
|
6529
7111
|
const counts = countToolCallsByRun(result.runId, ["vault_create_spore", "vault_write_digest"]);
|
|
6530
7112
|
const sporeCount = counts["vault_create_spore"] ?? 0;
|
|
6531
7113
|
const digestCount = counts["vault_write_digest"] ?? 0;
|
|
@@ -6568,6 +7150,9 @@ async function registerScheduledTasks(powerManager, deps) {
|
|
|
6568
7150
|
},
|
|
6569
7151
|
"has-approved-candidates": () => {
|
|
6570
7152
|
return countCandidates({ status: "approved" }) > 0;
|
|
7153
|
+
},
|
|
7154
|
+
"has-skill-survey-evidence": () => {
|
|
7155
|
+
return getSkillSurveyEligibility(taskAgentMap.get(SKILL_SURVEY_TASK)).eligible;
|
|
6571
7156
|
}
|
|
6572
7157
|
},
|
|
6573
7158
|
onTaskError: (taskName, err) => {
|
|
@@ -6597,31 +7182,170 @@ function listTeamMembers() {
|
|
|
6597
7182
|
).all();
|
|
6598
7183
|
}
|
|
6599
7184
|
|
|
6600
|
-
// src/daemon/
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
}
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
7185
|
+
// src/daemon/plan-capture.ts
|
|
7186
|
+
import { createHash as createHash4 } from "crypto";
|
|
7187
|
+
import os8 from "os";
|
|
7188
|
+
import path18 from "path";
|
|
7189
|
+
function extractTaggedPlans(text, tags) {
|
|
7190
|
+
const results = [];
|
|
7191
|
+
for (const tag of tags) {
|
|
7192
|
+
const regex = new RegExp(`<${tag}>\\n?([\\s\\S]*?)\\n?</${tag}>`, "g");
|
|
7193
|
+
let match;
|
|
7194
|
+
while ((match = regex.exec(text)) !== null) {
|
|
7195
|
+
const content = match[1].trim();
|
|
7196
|
+
if (content) results.push({ tag, content });
|
|
7197
|
+
}
|
|
7198
|
+
}
|
|
7199
|
+
return results;
|
|
7200
|
+
}
|
|
7201
|
+
var FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
7202
|
+
"Write",
|
|
7203
|
+
"Edit",
|
|
7204
|
+
"Create",
|
|
7205
|
+
"write",
|
|
7206
|
+
"edit",
|
|
7207
|
+
"patch",
|
|
7208
|
+
"create"
|
|
7209
|
+
]);
|
|
7210
|
+
var HEADING_REGEX = /^#\s+(.+)$/m;
|
|
7211
|
+
var TRANSCRIPT_SOURCE_TITLE_PREFIX = `${TRANSCRIPT_SOURCE_PREFIX}`;
|
|
7212
|
+
function resolvePlanWatchDir(watchDir, projectRoot) {
|
|
7213
|
+
const expanded = watchDir.startsWith("~/") ? path18.join(os8.homedir(), watchDir.slice(2)) : watchDir;
|
|
7214
|
+
return path18.isAbsolute(expanded) ? expanded : path18.resolve(projectRoot, expanded);
|
|
7215
|
+
}
|
|
7216
|
+
function isInPlanDirectory(filePath, watchDirs, projectRoot) {
|
|
7217
|
+
const abs = path18.isAbsolute(filePath) ? filePath : path18.resolve(projectRoot, filePath);
|
|
7218
|
+
return watchDirs.some((dir) => {
|
|
7219
|
+
const absDir = resolvePlanWatchDir(dir, projectRoot);
|
|
7220
|
+
const prefix = absDir.endsWith(path18.sep) ? absDir : absDir + path18.sep;
|
|
7221
|
+
return abs === absDir || abs.startsWith(prefix);
|
|
7222
|
+
});
|
|
7223
|
+
}
|
|
7224
|
+
function isPlanWriteEvent(toolName, toolInput, config) {
|
|
7225
|
+
if (!FILE_WRITE_TOOLS.has(toolName)) return null;
|
|
7226
|
+
const filePath = toolInput?.file_path ?? toolInput?.path ?? toolInput?.filePath;
|
|
7227
|
+
if (typeof filePath !== "string") return null;
|
|
7228
|
+
if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;
|
|
7229
|
+
if (config.extensions?.length) {
|
|
7230
|
+
const ext = path18.extname(filePath).toLowerCase();
|
|
7231
|
+
if (!config.extensions.includes(ext)) return null;
|
|
7232
|
+
}
|
|
7233
|
+
return filePath;
|
|
7234
|
+
}
|
|
7235
|
+
function parsePlanTitle(content, filename) {
|
|
7236
|
+
const match = HEADING_REGEX.exec(content);
|
|
7237
|
+
if (match) return match[1].trim();
|
|
7238
|
+
return filename ?? null;
|
|
7239
|
+
}
|
|
7240
|
+
function normalizePlanTags(tags) {
|
|
7241
|
+
if (tags === void 0 || tags === null) return null;
|
|
7242
|
+
return Array.isArray(tags) ? tags.join(", ") : tags;
|
|
7243
|
+
}
|
|
7244
|
+
function fileTitleFromSourcePath(sourcePath) {
|
|
7245
|
+
if (!sourcePath || sourcePath.startsWith(TRANSCRIPT_SOURCE_TITLE_PREFIX)) return null;
|
|
7246
|
+
return path18.basename(sourcePath);
|
|
7247
|
+
}
|
|
7248
|
+
function resolvePlanTitle(input) {
|
|
7249
|
+
const explicitTitle = input.title?.trim();
|
|
7250
|
+
if (explicitTitle) return explicitTitle;
|
|
7251
|
+
const headingTitle = parsePlanTitle(input.content);
|
|
7252
|
+
if (headingTitle) return headingTitle;
|
|
7253
|
+
const sourcePathTitle = fileTitleFromSourcePath(input.sourcePath);
|
|
7254
|
+
if (sourcePathTitle) return sourcePathTitle;
|
|
7255
|
+
return input.planKey ? humanizePlanToken(input.planKey) : null;
|
|
7256
|
+
}
|
|
7257
|
+
function persistPlan(input) {
|
|
7258
|
+
const createdAt = input.createdAt ?? Math.floor(Date.now() / 1e3);
|
|
7259
|
+
const updatedAt = input.updatedAt ?? createdAt;
|
|
7260
|
+
const contentHash = createHash4(CONTENT_HASH_ALGORITHM).update(input.content).digest("hex");
|
|
7261
|
+
const existingPlan = getPlanByLogicalKey(input.logicalKey);
|
|
7262
|
+
const status = input.status ?? existingPlan?.status ?? "active";
|
|
7263
|
+
const promptBatchId = input.promptBatchId === void 0 ? existingPlan?.prompt_batch_id ?? null : input.promptBatchId;
|
|
7264
|
+
const resolvedTitle = resolvePlanTitle({
|
|
7265
|
+
content: input.content,
|
|
7266
|
+
title: input.title,
|
|
7267
|
+
sourcePath: input.sourcePath,
|
|
7268
|
+
planKey: input.planKey
|
|
7269
|
+
});
|
|
7270
|
+
if (existingPlan && existingPlan.content_hash === contentHash && existingPlan.title === resolvedTitle && existingPlan.status === status) {
|
|
7271
|
+
return existingPlan;
|
|
7272
|
+
}
|
|
7273
|
+
if (existingPlan && existingPlan.content_hash !== contentHash) {
|
|
7274
|
+
const priorSource = existingPlan.source_path ?? null;
|
|
7275
|
+
const newSource = input.sourcePath ?? null;
|
|
7276
|
+
if (priorSource !== newSource) {
|
|
7277
|
+
input.logger?.warn(LOG_KINDS.CAPTURE_PLAN, "Plan overwritten mid-session", {
|
|
7278
|
+
logical_key: input.logicalKey,
|
|
7279
|
+
session_id: input.sessionId,
|
|
7280
|
+
prior_source: priorSource,
|
|
7281
|
+
new_source: newSource,
|
|
7282
|
+
prior_updated_at: existingPlan.updated_at
|
|
7283
|
+
});
|
|
7284
|
+
}
|
|
7285
|
+
}
|
|
7286
|
+
return upsertPlan({
|
|
7287
|
+
id: buildPlanId(input.logicalKey),
|
|
7288
|
+
logical_key: input.logicalKey,
|
|
7289
|
+
title: resolvedTitle,
|
|
7290
|
+
content: input.content,
|
|
7291
|
+
source_path: input.sourcePath ?? null,
|
|
7292
|
+
tags: normalizePlanTags(input.tags),
|
|
7293
|
+
session_id: input.sessionId,
|
|
7294
|
+
prompt_batch_id: promptBatchId,
|
|
7295
|
+
content_hash: contentHash,
|
|
7296
|
+
status,
|
|
7297
|
+
created_at: createdAt,
|
|
7298
|
+
updated_at: updatedAt
|
|
7299
|
+
});
|
|
7300
|
+
}
|
|
7301
|
+
function capturePlan(input) {
|
|
7302
|
+
const normalizedSourcePath = normalizePlanSourcePath(input.sourcePath, input.projectRoot);
|
|
7303
|
+
return persistPlan({
|
|
7304
|
+
sessionId: input.sessionId,
|
|
7305
|
+
content: input.content,
|
|
7306
|
+
logicalKey: buildPathPlanLogicalKey(normalizedSourcePath),
|
|
7307
|
+
sourcePath: normalizedSourcePath,
|
|
7308
|
+
promptBatchId: input.promptBatchId,
|
|
7309
|
+
logger: input.logger
|
|
7310
|
+
});
|
|
7311
|
+
}
|
|
7312
|
+
function captureTaggedPlan(input) {
|
|
7313
|
+
return persistPlan({
|
|
7314
|
+
sessionId: input.sessionId,
|
|
7315
|
+
content: input.content,
|
|
7316
|
+
logicalKey: buildSessionTagPlanLogicalKey(input.sessionId, input.tag),
|
|
7317
|
+
sourcePath: `${TRANSCRIPT_SOURCE_PREFIX}${input.tag}`,
|
|
7318
|
+
promptBatchId: input.promptBatchId,
|
|
7319
|
+
planKey: input.tag,
|
|
7320
|
+
logger: input.logger
|
|
7321
|
+
});
|
|
7322
|
+
}
|
|
7323
|
+
|
|
7324
|
+
// src/daemon/api/mcp-proxy.ts
|
|
7325
|
+
var SPORE_ID_RANDOM_BYTES = 4;
|
|
7326
|
+
var RESOLUTION_ID_RANDOM_BYTES = 8;
|
|
7327
|
+
var MIN_CONSOLIDATE_SOURCES = 2;
|
|
7328
|
+
var RememberBody = external_exports.object({
|
|
7329
|
+
content: external_exports.string(),
|
|
7330
|
+
type: external_exports.string().optional(),
|
|
7331
|
+
tags: external_exports.array(external_exports.string()).optional()
|
|
7332
|
+
});
|
|
7333
|
+
var SupersedeBody = external_exports.object({
|
|
7334
|
+
old_spore_id: external_exports.string(),
|
|
7335
|
+
new_spore_id: external_exports.string(),
|
|
7336
|
+
reason: external_exports.string().optional()
|
|
7337
|
+
});
|
|
7338
|
+
function isoToEpochSeconds(iso) {
|
|
7339
|
+
const ms = Date.parse(iso);
|
|
7340
|
+
return Number.isNaN(ms) ? void 0 : Math.floor(ms / 1e3);
|
|
7341
|
+
}
|
|
7342
|
+
function registerMcpUserAgent(createdAt) {
|
|
7343
|
+
registerAgent({
|
|
7344
|
+
id: USER_AGENT_ID,
|
|
7345
|
+
name: USER_AGENT_NAME,
|
|
7346
|
+
created_at: createdAt
|
|
7347
|
+
});
|
|
7348
|
+
}
|
|
6625
7349
|
function toPlanProgress(content) {
|
|
6626
7350
|
const planContent = content ?? "";
|
|
6627
7351
|
const checked = (planContent.match(/- \[x\]/gi) ?? []).length;
|
|
@@ -6639,8 +7363,20 @@ var ConsolidateBody = external_exports.object({
|
|
|
6639
7363
|
tags: external_exports.array(external_exports.string()).optional(),
|
|
6640
7364
|
reason: external_exports.string().optional()
|
|
6641
7365
|
});
|
|
7366
|
+
var SavePlanBody = external_exports.object({
|
|
7367
|
+
session_id: external_exports.string(),
|
|
7368
|
+
content: external_exports.string().min(1),
|
|
7369
|
+
source_path: external_exports.string().min(1).optional(),
|
|
7370
|
+
plan_key: external_exports.string().min(1).optional(),
|
|
7371
|
+
title: external_exports.string().min(1).optional(),
|
|
7372
|
+
status: external_exports.enum(PLAN_STATUSES).optional(),
|
|
7373
|
+
tags: external_exports.array(external_exports.string()).optional()
|
|
7374
|
+
}).refine(
|
|
7375
|
+
(value) => Boolean(value.source_path) !== Boolean(value.plan_key),
|
|
7376
|
+
{ message: "Provide exactly one of source_path or plan_key" }
|
|
7377
|
+
);
|
|
6642
7378
|
function createMcpProxyHandlers(deps) {
|
|
6643
|
-
const { machineId, embeddingManager } = deps;
|
|
7379
|
+
const { machineId, embeddingManager, projectRoot, logger } = deps;
|
|
6644
7380
|
function toPlanSummary(row) {
|
|
6645
7381
|
return {
|
|
6646
7382
|
id: row.id,
|
|
@@ -6764,8 +7500,46 @@ function createMcpProxyHandlers(deps) {
|
|
|
6764
7500
|
}
|
|
6765
7501
|
};
|
|
6766
7502
|
}
|
|
7503
|
+
async function handleSavePlan(req) {
|
|
7504
|
+
const { session_id, content, source_path, plan_key, title, status, tags } = SavePlanBody.parse(req.body);
|
|
7505
|
+
const session = getSession(session_id);
|
|
7506
|
+
if (!session) return { status: 404, body: errorBody("session-not-found", "Session not found") };
|
|
7507
|
+
const openBatch = getLatestOpenBatch(session_id);
|
|
7508
|
+
const normalizedSourcePath = source_path ? normalizePlanSourcePath(source_path, projectRoot) : null;
|
|
7509
|
+
const logicalKey = normalizedSourcePath ? buildPathPlanLogicalKey(normalizedSourcePath) : buildSessionPlanLogicalKey(session_id, plan_key);
|
|
7510
|
+
const row = persistPlan({
|
|
7511
|
+
sessionId: session_id,
|
|
7512
|
+
content,
|
|
7513
|
+
logicalKey,
|
|
7514
|
+
sourcePath: normalizedSourcePath,
|
|
7515
|
+
promptBatchId: openBatch?.id,
|
|
7516
|
+
title,
|
|
7517
|
+
status,
|
|
7518
|
+
tags,
|
|
7519
|
+
planKey: plan_key ?? null,
|
|
7520
|
+
logger
|
|
7521
|
+
});
|
|
7522
|
+
return {
|
|
7523
|
+
body: {
|
|
7524
|
+
id: row.id,
|
|
7525
|
+
logical_key: row.logical_key,
|
|
7526
|
+
title: row.title,
|
|
7527
|
+
status: row.status,
|
|
7528
|
+
source_path: row.source_path,
|
|
7529
|
+
session_id: row.session_id,
|
|
7530
|
+
prompt_batch_id: row.prompt_batch_id,
|
|
7531
|
+
tags: toPlanTags(row.tags),
|
|
7532
|
+
created_at: row.created_at,
|
|
7533
|
+
updated_at: row.updated_at
|
|
7534
|
+
}
|
|
7535
|
+
};
|
|
7536
|
+
}
|
|
6767
7537
|
async function handlePlans(req) {
|
|
6768
7538
|
const id = typeof req.query.id === "string" ? req.query.id : void 0;
|
|
7539
|
+
const session = typeof req.query.session === "string" ? req.query.session : void 0;
|
|
7540
|
+
if (id && session) {
|
|
7541
|
+
return { status: 400, body: errorBody("mutually-exclusive-query", "Pass either id or session, not both") };
|
|
7542
|
+
}
|
|
6769
7543
|
if (id) {
|
|
6770
7544
|
const row = getPlan(id);
|
|
6771
7545
|
if (!row) return { body: { plans: [] } };
|
|
@@ -6778,6 +7552,11 @@ function createMcpProxyHandlers(deps) {
|
|
|
6778
7552
|
}
|
|
6779
7553
|
};
|
|
6780
7554
|
}
|
|
7555
|
+
if (session) {
|
|
7556
|
+
const rows2 = listPlansBySession(session);
|
|
7557
|
+
const plans2 = rows2.map(toPlanSummary);
|
|
7558
|
+
return { body: { plans: plans2 } };
|
|
7559
|
+
}
|
|
6781
7560
|
const statusFilter = req.query.status === "all" ? void 0 : req.query.status;
|
|
6782
7561
|
const limit = req.query.limit ? Number(req.query.limit) : void 0;
|
|
6783
7562
|
const rows = listPlans({ status: statusFilter, limit });
|
|
@@ -6831,6 +7610,7 @@ function createMcpProxyHandlers(deps) {
|
|
|
6831
7610
|
handleSupersede,
|
|
6832
7611
|
handleConsolidate,
|
|
6833
7612
|
handlePlans,
|
|
7613
|
+
handleSavePlan,
|
|
6834
7614
|
handleSessions,
|
|
6835
7615
|
handleTeam
|
|
6836
7616
|
};
|
|
@@ -6838,16 +7618,386 @@ function createMcpProxyHandlers(deps) {
|
|
|
6838
7618
|
|
|
6839
7619
|
// src/daemon/api/agent-runs.ts
|
|
6840
7620
|
import { resolve as resolve2 } from "path";
|
|
7621
|
+
|
|
7622
|
+
// src/services/phase-audit.ts
|
|
7623
|
+
function isStoredUsageData(value) {
|
|
7624
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7625
|
+
}
|
|
7626
|
+
function parseUsageData(raw) {
|
|
7627
|
+
return tryParseJson(raw, isStoredUsageData) ?? {};
|
|
7628
|
+
}
|
|
7629
|
+
function normalizeStatus(raw) {
|
|
7630
|
+
if (raw === "completed" || raw === "failed" || raw === "skipped") return raw;
|
|
7631
|
+
return "pending";
|
|
7632
|
+
}
|
|
7633
|
+
function aggregateTurnCounts(turns) {
|
|
7634
|
+
const toolCalls = {};
|
|
7635
|
+
for (const turn of turns) {
|
|
7636
|
+
toolCalls[turn.tool_name] = (toolCalls[turn.tool_name] ?? 0) + 1;
|
|
7637
|
+
}
|
|
7638
|
+
const toolErrors = {};
|
|
7639
|
+
return [toolCalls, toolErrors];
|
|
7640
|
+
}
|
|
7641
|
+
function aggregateWriteIntents(intents) {
|
|
7642
|
+
const byPhase = {};
|
|
7643
|
+
const unattributedByTool = {};
|
|
7644
|
+
const totalByTool = {};
|
|
7645
|
+
for (const intent of intents) {
|
|
7646
|
+
const tool = intent.tool_name;
|
|
7647
|
+
totalByTool[tool] = (totalByTool[tool] ?? 0) + 1;
|
|
7648
|
+
if (intent.phase_id) {
|
|
7649
|
+
if (!byPhase[intent.phase_id]) byPhase[intent.phase_id] = {};
|
|
7650
|
+
byPhase[intent.phase_id][tool] = (byPhase[intent.phase_id][tool] ?? 0) + 1;
|
|
7651
|
+
} else {
|
|
7652
|
+
unattributedByTool[tool] = (unattributedByTool[tool] ?? 0) + 1;
|
|
7653
|
+
}
|
|
7654
|
+
}
|
|
7655
|
+
return {
|
|
7656
|
+
total: intents.length,
|
|
7657
|
+
byPhase,
|
|
7658
|
+
unattributedByTool,
|
|
7659
|
+
totalByTool
|
|
7660
|
+
};
|
|
7661
|
+
}
|
|
7662
|
+
function serializeReports(reports) {
|
|
7663
|
+
return reports.map((r) => ({
|
|
7664
|
+
action: r.action,
|
|
7665
|
+
summary: r.summary ?? null,
|
|
7666
|
+
details: r.details ?? null,
|
|
7667
|
+
createdAt: r.created_at
|
|
7668
|
+
}));
|
|
7669
|
+
}
|
|
7670
|
+
function buildPhaseAudit(runId) {
|
|
7671
|
+
const run = getRun(runId);
|
|
7672
|
+
if (!run) return null;
|
|
7673
|
+
const dryRun = run.dry_run;
|
|
7674
|
+
const usageData = parseUsageData(run.usage_data);
|
|
7675
|
+
const checkpointState = parseCheckpointState(run.checkpoints);
|
|
7676
|
+
const usageByName = new Map(
|
|
7677
|
+
(usageData.phases ?? []).map((p) => [p.name, p])
|
|
7678
|
+
);
|
|
7679
|
+
const checkpointByName = new Map(
|
|
7680
|
+
Object.entries(checkpointState.phases).map(([key, cp]) => [
|
|
7681
|
+
cp.name ?? key,
|
|
7682
|
+
cp
|
|
7683
|
+
])
|
|
7684
|
+
);
|
|
7685
|
+
const phaseNames = [];
|
|
7686
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7687
|
+
for (const p of usageData.phases ?? []) {
|
|
7688
|
+
if (!seen.has(p.name)) {
|
|
7689
|
+
phaseNames.push(p.name);
|
|
7690
|
+
seen.add(p.name);
|
|
7691
|
+
}
|
|
7692
|
+
}
|
|
7693
|
+
for (const name of checkpointByName.keys()) {
|
|
7694
|
+
if (!seen.has(name)) {
|
|
7695
|
+
phaseNames.push(name);
|
|
7696
|
+
seen.add(name);
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7699
|
+
const reports = listReports(runId);
|
|
7700
|
+
const turns = listTurnsByRun(runId);
|
|
7701
|
+
const [toolCalls, toolErrors] = aggregateTurnCounts(turns);
|
|
7702
|
+
const serializedReports = serializeReports(reports);
|
|
7703
|
+
let intentSummary = null;
|
|
7704
|
+
if (dryRun) {
|
|
7705
|
+
const intents = listWriteIntentTools(runId);
|
|
7706
|
+
intentSummary = aggregateWriteIntents(intents);
|
|
7707
|
+
}
|
|
7708
|
+
if (phaseNames.length === 0 && turns.length > 0) {
|
|
7709
|
+
const runStatus = normalizeStatus(run.status);
|
|
7710
|
+
const durationMs = runDurationMs(run);
|
|
7711
|
+
let writeIntents = null;
|
|
7712
|
+
if (intentSummary) {
|
|
7713
|
+
writeIntents = {
|
|
7714
|
+
total: intentSummary.total,
|
|
7715
|
+
byTool: intentSummary.totalByTool
|
|
7716
|
+
};
|
|
7717
|
+
}
|
|
7718
|
+
const summary = run.status === "failed" ? run.error ?? null : null;
|
|
7719
|
+
return {
|
|
7720
|
+
runId,
|
|
7721
|
+
taskName: run.task ?? null,
|
|
7722
|
+
dryRun,
|
|
7723
|
+
phases: [
|
|
7724
|
+
{
|
|
7725
|
+
phaseName: "run",
|
|
7726
|
+
status: runStatus,
|
|
7727
|
+
summary,
|
|
7728
|
+
turnsUsed: turns.length,
|
|
7729
|
+
maxTurns: null,
|
|
7730
|
+
tokensUsed: run.tokens_used ?? 0,
|
|
7731
|
+
costUsd: run.cost_usd ?? null,
|
|
7732
|
+
costSource: run.cost_source ?? null,
|
|
7733
|
+
durationMs,
|
|
7734
|
+
startedAt: run.started_at,
|
|
7735
|
+
completedAt: run.completed_at,
|
|
7736
|
+
skipReason: null,
|
|
7737
|
+
toolCalls,
|
|
7738
|
+
toolErrors,
|
|
7739
|
+
writeIntents,
|
|
7740
|
+
reports: serializedReports
|
|
7741
|
+
}
|
|
7742
|
+
]
|
|
7743
|
+
};
|
|
7744
|
+
}
|
|
7745
|
+
const phases = phaseNames.map((phaseName) => {
|
|
7746
|
+
const usage = usageByName.get(phaseName);
|
|
7747
|
+
const cp = checkpointByName.get(phaseName);
|
|
7748
|
+
const status = normalizeStatus(cp?.status ?? (usage ? "pending" : "pending"));
|
|
7749
|
+
const tokensUsed = cp?.tokensUsed ?? usage?.tokensUsed ?? usage?.usage?.totalTokens ?? 0;
|
|
7750
|
+
const costUsd = cp?.costUsd !== void 0 ? cp.costUsd : usage?.costUsd !== void 0 ? usage.costUsd : null;
|
|
7751
|
+
const costSource = cp?.costSource ?? usage?.costSource ?? null;
|
|
7752
|
+
const durationMs = usage?.usage?.durationMs ?? null;
|
|
7753
|
+
const completedAt = status === "completed" || status === "failed" ? cp?.updatedAt ?? null : null;
|
|
7754
|
+
const startedAt = null;
|
|
7755
|
+
const skipReason = null;
|
|
7756
|
+
let writeIntents = null;
|
|
7757
|
+
if (intentSummary) {
|
|
7758
|
+
const phaseByTool = intentSummary.byPhase[phaseName] ?? {};
|
|
7759
|
+
writeIntents = {
|
|
7760
|
+
// Phase total = sum of tool calls attributed to this phase
|
|
7761
|
+
total: Object.values(phaseByTool).reduce((sum, n) => sum + n, 0),
|
|
7762
|
+
byTool: phaseByTool
|
|
7763
|
+
};
|
|
7764
|
+
}
|
|
7765
|
+
return {
|
|
7766
|
+
phaseName,
|
|
7767
|
+
status,
|
|
7768
|
+
summary: cp?.summary ?? null,
|
|
7769
|
+
turnsUsed: cp?.turnsUsed ?? 0,
|
|
7770
|
+
maxTurns: null,
|
|
7771
|
+
// Not persisted — see module JSDoc
|
|
7772
|
+
tokensUsed,
|
|
7773
|
+
costUsd,
|
|
7774
|
+
costSource,
|
|
7775
|
+
durationMs,
|
|
7776
|
+
startedAt,
|
|
7777
|
+
completedAt,
|
|
7778
|
+
skipReason,
|
|
7779
|
+
toolCalls,
|
|
7780
|
+
// run-level aggregate (no per-phase turn attribution)
|
|
7781
|
+
toolErrors,
|
|
7782
|
+
// always empty — no error flag on turn rows
|
|
7783
|
+
writeIntents,
|
|
7784
|
+
reports: serializedReports
|
|
7785
|
+
// all reports; no phase column exists
|
|
7786
|
+
};
|
|
7787
|
+
});
|
|
7788
|
+
return {
|
|
7789
|
+
runId,
|
|
7790
|
+
taskName: run.task ?? null,
|
|
7791
|
+
dryRun,
|
|
7792
|
+
phases
|
|
7793
|
+
};
|
|
7794
|
+
}
|
|
7795
|
+
|
|
7796
|
+
// src/daemon/api/schemas/execution-overrides.ts
|
|
7797
|
+
var ReasoningLevelEnum = external_exports.enum(["low", "default", "high"]);
|
|
7798
|
+
var RuntimeIdEnum = external_exports.enum(["claude-sdk", "openai-agents"]);
|
|
7799
|
+
var ProviderTypeEnum = external_exports.enum([
|
|
7800
|
+
"anthropic",
|
|
7801
|
+
"ollama",
|
|
7802
|
+
"lmstudio",
|
|
7803
|
+
"openai",
|
|
7804
|
+
"openrouter",
|
|
7805
|
+
"openai-compatible"
|
|
7806
|
+
]);
|
|
7807
|
+
var ProviderOverrideWireSchema = external_exports.object({
|
|
7808
|
+
runtime: RuntimeIdEnum.optional(),
|
|
7809
|
+
type: ProviderTypeEnum,
|
|
7810
|
+
localBackend: external_exports.enum(["ollama", "lmstudio"]).optional(),
|
|
7811
|
+
baseUrl: external_exports.string().optional(),
|
|
7812
|
+
model: external_exports.string().optional(),
|
|
7813
|
+
reasoningMap: external_exports.object({
|
|
7814
|
+
low: external_exports.string().optional(),
|
|
7815
|
+
default: external_exports.string().optional(),
|
|
7816
|
+
high: external_exports.string().optional()
|
|
7817
|
+
}).optional(),
|
|
7818
|
+
contextLength: external_exports.number().int().positive().optional()
|
|
7819
|
+
});
|
|
7820
|
+
var PhaseExecutionOverrideBody = external_exports.object({
|
|
7821
|
+
reasoningLevel: ReasoningLevelEnum.optional(),
|
|
7822
|
+
model: external_exports.string().optional(),
|
|
7823
|
+
provider: ProviderOverrideWireSchema.optional(),
|
|
7824
|
+
maxTurns: external_exports.number().int().positive().optional()
|
|
7825
|
+
});
|
|
7826
|
+
var ExecutionOverrideBody = external_exports.object({
|
|
7827
|
+
runtime: RuntimeIdEnum.optional(),
|
|
7828
|
+
reasoningLevel: ReasoningLevelEnum.optional(),
|
|
7829
|
+
model: external_exports.string().optional(),
|
|
7830
|
+
provider: ProviderOverrideWireSchema.optional(),
|
|
7831
|
+
phases: external_exports.record(external_exports.string(), PhaseExecutionOverrideBody).optional()
|
|
7832
|
+
}).optional();
|
|
7833
|
+
|
|
7834
|
+
// src/daemon/api/schemas/execution-overrides-traversal.ts
|
|
7835
|
+
function transformProviderOverrides(overrides, transform) {
|
|
7836
|
+
if (overrides === null || overrides === void 0) {
|
|
7837
|
+
return overrides ?? null;
|
|
7838
|
+
}
|
|
7839
|
+
if (typeof overrides !== "object") return overrides;
|
|
7840
|
+
const cloned = { ...overrides };
|
|
7841
|
+
if (isPlainObject(cloned.provider)) {
|
|
7842
|
+
const next = transform({ ...cloned.provider });
|
|
7843
|
+
if (next === null) {
|
|
7844
|
+
delete cloned.provider;
|
|
7845
|
+
} else {
|
|
7846
|
+
cloned.provider = next;
|
|
7847
|
+
}
|
|
7848
|
+
}
|
|
7849
|
+
if (isPlainObject(cloned.phases)) {
|
|
7850
|
+
const nextPhases = {};
|
|
7851
|
+
for (const [name, phase] of Object.entries(cloned.phases)) {
|
|
7852
|
+
if (!isPlainObject(phase)) {
|
|
7853
|
+
nextPhases[name] = phase;
|
|
7854
|
+
continue;
|
|
7855
|
+
}
|
|
7856
|
+
const clonedPhase = { ...phase };
|
|
7857
|
+
if (isPlainObject(clonedPhase.provider)) {
|
|
7858
|
+
const nextProvider = transform({ ...clonedPhase.provider });
|
|
7859
|
+
if (nextProvider === null) {
|
|
7860
|
+
delete clonedPhase.provider;
|
|
7861
|
+
} else {
|
|
7862
|
+
clonedPhase.provider = nextProvider;
|
|
7863
|
+
}
|
|
7864
|
+
}
|
|
7865
|
+
nextPhases[name] = clonedPhase;
|
|
7866
|
+
}
|
|
7867
|
+
cloned.phases = nextPhases;
|
|
7868
|
+
}
|
|
7869
|
+
return cloned;
|
|
7870
|
+
}
|
|
7871
|
+
function isPlainObject(value) {
|
|
7872
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7873
|
+
}
|
|
7874
|
+
|
|
7875
|
+
// src/daemon/api/run-serializer.ts
|
|
7876
|
+
function buildPhaseCheckpointSummary(checkpointsRaw) {
|
|
7877
|
+
if (!checkpointsRaw) return [];
|
|
7878
|
+
try {
|
|
7879
|
+
const parsed = JSON.parse(checkpointsRaw);
|
|
7880
|
+
return Object.entries(parsed.phases ?? {}).map(([name, phase]) => ({
|
|
7881
|
+
name: phase.name ?? name,
|
|
7882
|
+
status: phase.status ?? "pending",
|
|
7883
|
+
updatedAt: phase.updatedAt ?? 0,
|
|
7884
|
+
...phase.tokensUsed !== void 0 ? { tokensUsed: phase.tokensUsed } : {},
|
|
7885
|
+
...phase.costUsd !== void 0 ? { costUsd: phase.costUsd } : {},
|
|
7886
|
+
...phase.costSource !== void 0 ? { costSource: phase.costSource } : {}
|
|
7887
|
+
}));
|
|
7888
|
+
} catch {
|
|
7889
|
+
return [];
|
|
7890
|
+
}
|
|
7891
|
+
}
|
|
7892
|
+
function scrubExecutionOverrides(overrides) {
|
|
7893
|
+
if (!overrides || typeof overrides !== "object") return overrides;
|
|
7894
|
+
const topLevelStripped = { ...overrides };
|
|
7895
|
+
if ("apiKey" in topLevelStripped) delete topLevelStripped.apiKey;
|
|
7896
|
+
return transformProviderOverrides(topLevelStripped, stripApiKey);
|
|
7897
|
+
}
|
|
7898
|
+
function stripApiKey(provider) {
|
|
7899
|
+
const cloned = { ...provider };
|
|
7900
|
+
if ("apiKey" in cloned) delete cloned.apiKey;
|
|
7901
|
+
return cloned;
|
|
7902
|
+
}
|
|
7903
|
+
function serializeRun(run, opts = {}) {
|
|
7904
|
+
const {
|
|
7905
|
+
includeResumeFields = true,
|
|
7906
|
+
includePhaseCheckpoints = true,
|
|
7907
|
+
writeIntents,
|
|
7908
|
+
duration_ms
|
|
7909
|
+
} = opts;
|
|
7910
|
+
const base = {
|
|
7911
|
+
id: run.id,
|
|
7912
|
+
agent_id: run.agent_id,
|
|
7913
|
+
task: run.task,
|
|
7914
|
+
instruction: run.instruction,
|
|
7915
|
+
status: run.status,
|
|
7916
|
+
runtime: run.runtime,
|
|
7917
|
+
provider: run.provider,
|
|
7918
|
+
model: run.model,
|
|
7919
|
+
session_ref: run.session_ref,
|
|
7920
|
+
started_at: run.started_at,
|
|
7921
|
+
completed_at: run.completed_at,
|
|
7922
|
+
tokens_used: run.tokens_used,
|
|
7923
|
+
cost_usd: run.cost_usd,
|
|
7924
|
+
actual_cost_usd: run.actual_cost_usd,
|
|
7925
|
+
estimated_cost_usd: run.estimated_cost_usd,
|
|
7926
|
+
cost_source: run.cost_source,
|
|
7927
|
+
cost_data: run.cost_data,
|
|
7928
|
+
actions_taken: run.actions_taken,
|
|
7929
|
+
usage_data: run.usage_data,
|
|
7930
|
+
error: run.error,
|
|
7931
|
+
dry_run: run.dry_run,
|
|
7932
|
+
evaluation_id: run.evaluation_id,
|
|
7933
|
+
reasoning_level: run.reasoning_level,
|
|
7934
|
+
// Strip `apiKey` from historical rows defensively — before this PR the
|
|
7935
|
+
// API accepted apiKey in executionOverrides and stored it unmasked.
|
|
7936
|
+
execution_overrides: scrubExecutionOverrides(run.execution_overrides)
|
|
7937
|
+
};
|
|
7938
|
+
return {
|
|
7939
|
+
...base,
|
|
7940
|
+
...includeResumeFields ? {
|
|
7941
|
+
resumable: run.resumable === 1,
|
|
7942
|
+
resume_status: run.resume_status,
|
|
7943
|
+
resume_mode: run.resume_mode,
|
|
7944
|
+
resumed_at: run.resumed_at,
|
|
7945
|
+
checkpoints: run.checkpoints
|
|
7946
|
+
} : {},
|
|
7947
|
+
...includePhaseCheckpoints ? { phase_checkpoints: buildPhaseCheckpointSummary(run.checkpoints) } : {},
|
|
7948
|
+
...writeIntents !== void 0 && writeIntents !== null ? { write_intents: writeIntents } : {},
|
|
7949
|
+
...duration_ms !== void 0 ? { duration_ms } : {}
|
|
7950
|
+
};
|
|
7951
|
+
}
|
|
7952
|
+
|
|
7953
|
+
// src/daemon/api/agent-runs.ts
|
|
6841
7954
|
var AGENT_RUNS_DEFAULT_LIMIT = 50;
|
|
7955
|
+
var REMOTE_PROVIDER_TYPES = /* @__PURE__ */ new Set(["openai", "openrouter"]);
|
|
7956
|
+
function stripBaseUrlForRemoteProviders(provider) {
|
|
7957
|
+
const type = provider.type;
|
|
7958
|
+
if (typeof type === "string" && REMOTE_PROVIDER_TYPES.has(type)) {
|
|
7959
|
+
const { baseUrl: _dropped, ...rest } = provider;
|
|
7960
|
+
return rest;
|
|
7961
|
+
}
|
|
7962
|
+
return provider;
|
|
7963
|
+
}
|
|
7964
|
+
function sanitizeExecutionOverrides(overrides) {
|
|
7965
|
+
if (!overrides) return overrides;
|
|
7966
|
+
const result = transformProviderOverrides(
|
|
7967
|
+
overrides,
|
|
7968
|
+
stripBaseUrlForRemoteProviders
|
|
7969
|
+
);
|
|
7970
|
+
return result;
|
|
7971
|
+
}
|
|
6842
7972
|
var AgentRunBody = external_exports.object({
|
|
6843
7973
|
task: external_exports.string().optional(),
|
|
6844
7974
|
instruction: external_exports.string().optional(),
|
|
6845
|
-
agentId: external_exports.string().optional()
|
|
7975
|
+
agentId: external_exports.string().optional(),
|
|
7976
|
+
/**
|
|
7977
|
+
* Run in dry-run mode — writes intercepted by the tool surface and
|
|
7978
|
+
* recorded to `agent_run_write_intents` instead of mutating the vault.
|
|
7979
|
+
*/
|
|
7980
|
+
dryRun: external_exports.boolean().optional(),
|
|
7981
|
+
/** Evaluation matrix this run belongs to, if any. */
|
|
7982
|
+
evaluationId: external_exports.string().nullable().optional(),
|
|
7983
|
+
/** Per-run runtime/reasoning/model overrides; also per-phase overrides. */
|
|
7984
|
+
executionOverrides: ExecutionOverrideBody
|
|
7985
|
+
});
|
|
7986
|
+
var ResumeRunBody = external_exports.object({
|
|
7987
|
+
mode: external_exports.enum(["manual", "scheduled"]).optional()
|
|
6846
7988
|
});
|
|
6847
7989
|
function createAgentRunHandlers(deps) {
|
|
6848
|
-
const { vaultDir, embeddingManager, logger } = deps;
|
|
7990
|
+
const { vaultDir, embeddingManager, logger, getTeamClient } = deps;
|
|
6849
7991
|
async function handleRun(req) {
|
|
6850
|
-
const {
|
|
7992
|
+
const {
|
|
7993
|
+
task,
|
|
7994
|
+
instruction: rawInstruction,
|
|
7995
|
+
agentId,
|
|
7996
|
+
dryRun,
|
|
7997
|
+
evaluationId,
|
|
7998
|
+
executionOverrides: rawExecutionOverrides
|
|
7999
|
+
} = AgentRunBody.parse(req.body);
|
|
8000
|
+
const executionOverrides = sanitizeExecutionOverrides(rawExecutionOverrides);
|
|
6851
8001
|
const mycoConfig = loadMergedConfig(vaultDir);
|
|
6852
8002
|
if (!hasConfiguredProvider(mycoConfig, task)) {
|
|
6853
8003
|
return {
|
|
@@ -6865,10 +8015,10 @@ function createAgentRunHandlers(deps) {
|
|
|
6865
8015
|
try {
|
|
6866
8016
|
const taskParams = mycoConfig.agent.tasks?.[task]?.params;
|
|
6867
8017
|
const projectRoot = resolve2(vaultDir, "..");
|
|
6868
|
-
built = buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager);
|
|
8018
|
+
built = await buildTaskInstruction(task, taskParams, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
|
|
6869
8019
|
} catch {
|
|
6870
8020
|
const projectRoot = resolve2(vaultDir, "..");
|
|
6871
|
-
built = buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager);
|
|
8021
|
+
built = await buildTaskInstruction(task, void 0, agentId, projectRoot, embeddingManager, mycoConfig, getTeamClient);
|
|
6872
8022
|
}
|
|
6873
8023
|
instruction = built?.instruction;
|
|
6874
8024
|
runContext = built?.context;
|
|
@@ -6883,13 +8033,16 @@ function createAgentRunHandlers(deps) {
|
|
|
6883
8033
|
};
|
|
6884
8034
|
}
|
|
6885
8035
|
}
|
|
6886
|
-
const { runAgent } = await import("./executor-
|
|
6887
|
-
const resultPromise =
|
|
8036
|
+
const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
|
|
8037
|
+
const resultPromise = runAgent2(vaultDir, {
|
|
6888
8038
|
task,
|
|
6889
8039
|
instruction,
|
|
6890
8040
|
agentId,
|
|
6891
8041
|
embeddingManager,
|
|
6892
|
-
runContext
|
|
8042
|
+
runContext,
|
|
8043
|
+
dryRun,
|
|
8044
|
+
evaluationId,
|
|
8045
|
+
executionOverrides
|
|
6893
8046
|
});
|
|
6894
8047
|
const effectiveAgentId = agentId ?? "myco-agent";
|
|
6895
8048
|
const runId = getLatestRunId(effectiveAgentId, task);
|
|
@@ -6941,14 +8094,57 @@ function createAgentRunHandlers(deps) {
|
|
|
6941
8094
|
const filterOpts = { agent_id: agentId, status, task, search };
|
|
6942
8095
|
const runs = listRuns({ ...filterOpts, limit, offset });
|
|
6943
8096
|
const total = countRuns(filterOpts);
|
|
6944
|
-
return { body: { runs, total, offset, limit } };
|
|
8097
|
+
return { body: { runs: runs.map((run) => serializeRun(run)), total, offset, limit } };
|
|
6945
8098
|
}
|
|
6946
8099
|
async function handleGetRun(req) {
|
|
6947
8100
|
const run = getRun(req.params.id);
|
|
6948
8101
|
if (!run) {
|
|
6949
8102
|
return { status: 404, body: { error: "Run not found" } };
|
|
6950
8103
|
}
|
|
6951
|
-
|
|
8104
|
+
const byTool = countWriteIntentsByTool(run.id);
|
|
8105
|
+
const total = Object.values(byTool).reduce((acc, n) => acc + n, 0);
|
|
8106
|
+
return {
|
|
8107
|
+
body: {
|
|
8108
|
+
run: serializeRun(run, {
|
|
8109
|
+
writeIntents: { total, by_tool: byTool },
|
|
8110
|
+
duration_ms: runDurationMs(run)
|
|
8111
|
+
})
|
|
8112
|
+
}
|
|
8113
|
+
};
|
|
8114
|
+
}
|
|
8115
|
+
async function handleResumeRun(req) {
|
|
8116
|
+
const run = getRun(req.params.id);
|
|
8117
|
+
if (!run) {
|
|
8118
|
+
return { status: 404, body: { error: "Run not found" } };
|
|
8119
|
+
}
|
|
8120
|
+
if (run.resumable !== 1 || run.status !== "failed") {
|
|
8121
|
+
return { status: 400, body: { error: "Run is not resumable" } };
|
|
8122
|
+
}
|
|
8123
|
+
const { mode } = ResumeRunBody.parse(req.body ?? {});
|
|
8124
|
+
const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
|
|
8125
|
+
const resultPromise = runAgent2(vaultDir, {
|
|
8126
|
+
agentId: run.agent_id,
|
|
8127
|
+
task: run.task ?? void 0,
|
|
8128
|
+
instruction: run.instruction ?? void 0,
|
|
8129
|
+
resumeRunId: run.id,
|
|
8130
|
+
resumeMode: mode ?? "manual",
|
|
8131
|
+
embeddingManager
|
|
8132
|
+
});
|
|
8133
|
+
resultPromise.then((result) => {
|
|
8134
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Agent run resumed", {
|
|
8135
|
+
runId: result.runId,
|
|
8136
|
+
status: result.status,
|
|
8137
|
+
runtime: result.runtime,
|
|
8138
|
+
provider: result.provider,
|
|
8139
|
+
model: result.model
|
|
8140
|
+
});
|
|
8141
|
+
}).catch((err) => {
|
|
8142
|
+
logger.error(LOG_KINDS.AGENT_ERROR, "Agent run resume threw unhandled error", {
|
|
8143
|
+
runId: run.id,
|
|
8144
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8145
|
+
});
|
|
8146
|
+
});
|
|
8147
|
+
return { body: { ok: true, message: "Agent resume started", runId: run.id } };
|
|
6952
8148
|
}
|
|
6953
8149
|
async function handleGetRunReports(req) {
|
|
6954
8150
|
const reports = listReports(req.params.id);
|
|
@@ -6958,18 +8154,376 @@ function createAgentRunHandlers(deps) {
|
|
|
6958
8154
|
const turns = listTurnsByRun(req.params.id);
|
|
6959
8155
|
return { body: turns };
|
|
6960
8156
|
}
|
|
8157
|
+
async function handleGetRunWriteIntents(req) {
|
|
8158
|
+
const rawLimit = req.query.limit ? Number(req.query.limit) : void 0;
|
|
8159
|
+
const rawOffset = req.query.offset ? Number(req.query.offset) : void 0;
|
|
8160
|
+
const limit = Number.isFinite(rawLimit) && rawLimit !== void 0 && rawLimit > 0 ? Math.min(rawLimit, 5e3) : 500;
|
|
8161
|
+
const offset = Number.isFinite(rawOffset) && rawOffset !== void 0 && rawOffset >= 0 ? rawOffset : 0;
|
|
8162
|
+
const intents = listWriteIntents(req.params.id, { limit, offset });
|
|
8163
|
+
const total = countWriteIntents(req.params.id);
|
|
8164
|
+
return { body: { intents, count: intents.length, total } };
|
|
8165
|
+
}
|
|
8166
|
+
async function handleGetRunAudit(req) {
|
|
8167
|
+
const audit = buildPhaseAudit(req.params.id);
|
|
8168
|
+
if (!audit) {
|
|
8169
|
+
return { status: 404, body: { error: "Run not found" } };
|
|
8170
|
+
}
|
|
8171
|
+
return { body: { audit } };
|
|
8172
|
+
}
|
|
6961
8173
|
return {
|
|
6962
8174
|
handleRun,
|
|
6963
8175
|
handleListRuns,
|
|
6964
8176
|
handleGetRun,
|
|
8177
|
+
handleResumeRun,
|
|
6965
8178
|
handleGetRunReports,
|
|
6966
|
-
handleGetRunTurns
|
|
8179
|
+
handleGetRunTurns,
|
|
8180
|
+
handleGetRunWriteIntents,
|
|
8181
|
+
handleGetRunAudit
|
|
6967
8182
|
};
|
|
6968
8183
|
}
|
|
6969
8184
|
|
|
8185
|
+
// src/daemon/api/agent-evaluations.ts
|
|
8186
|
+
import crypto from "crypto";
|
|
8187
|
+
|
|
8188
|
+
// src/db/queries/evaluations.ts
|
|
8189
|
+
var DEFAULT_LIMIT = 50;
|
|
8190
|
+
var EVAL_STATUS_PENDING = "pending";
|
|
8191
|
+
var EVAL_STATUS_RUNNING = "running";
|
|
8192
|
+
var EVAL_STATUS_COMPLETED = "completed";
|
|
8193
|
+
var EVAL_STATUS_FAILED = "failed";
|
|
8194
|
+
var EVAL_COLUMNS = [
|
|
8195
|
+
"id",
|
|
8196
|
+
"task_id",
|
|
8197
|
+
"matrix_json",
|
|
8198
|
+
"notes",
|
|
8199
|
+
"status",
|
|
8200
|
+
"created_at",
|
|
8201
|
+
"completed_at"
|
|
8202
|
+
];
|
|
8203
|
+
var SELECT_COLUMNS5 = EVAL_COLUMNS.join(", ");
|
|
8204
|
+
function toEvaluationRow(row) {
|
|
8205
|
+
const matrixJson = row.matrix_json;
|
|
8206
|
+
let matrix = null;
|
|
8207
|
+
if (matrixJson) {
|
|
8208
|
+
try {
|
|
8209
|
+
matrix = JSON.parse(matrixJson);
|
|
8210
|
+
} catch {
|
|
8211
|
+
matrix = null;
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
return {
|
|
8215
|
+
id: row.id,
|
|
8216
|
+
task_id: row.task_id,
|
|
8217
|
+
matrix,
|
|
8218
|
+
notes: row.notes ?? null,
|
|
8219
|
+
status: row.status,
|
|
8220
|
+
created_at: row.created_at,
|
|
8221
|
+
completed_at: row.completed_at ?? null
|
|
8222
|
+
};
|
|
8223
|
+
}
|
|
8224
|
+
function insertEvaluation(data) {
|
|
8225
|
+
const db = getDatabase();
|
|
8226
|
+
const createdAt = data.createdAt ?? epochSeconds();
|
|
8227
|
+
db.prepare(
|
|
8228
|
+
`INSERT INTO agent_run_evaluations
|
|
8229
|
+
(id, task_id, matrix_json, notes, status, created_at, completed_at)
|
|
8230
|
+
VALUES (?, ?, ?, ?, ?, ?, NULL)`
|
|
8231
|
+
).run(
|
|
8232
|
+
data.id,
|
|
8233
|
+
data.taskId,
|
|
8234
|
+
JSON.stringify(data.matrix ?? null),
|
|
8235
|
+
data.notes ?? null,
|
|
8236
|
+
EVAL_STATUS_PENDING,
|
|
8237
|
+
createdAt
|
|
8238
|
+
);
|
|
8239
|
+
return getEvaluation(data.id);
|
|
8240
|
+
}
|
|
8241
|
+
function getEvaluation(id) {
|
|
8242
|
+
const db = getDatabase();
|
|
8243
|
+
const row = db.prepare(
|
|
8244
|
+
`SELECT ${SELECT_COLUMNS5} FROM agent_run_evaluations WHERE id = ?`
|
|
8245
|
+
).get(id);
|
|
8246
|
+
return row ? toEvaluationRow(row) : null;
|
|
8247
|
+
}
|
|
8248
|
+
function listEvaluations(options = {}) {
|
|
8249
|
+
const db = getDatabase();
|
|
8250
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
8251
|
+
const offset = options.offset ?? 0;
|
|
8252
|
+
const rows = db.prepare(
|
|
8253
|
+
`SELECT ${SELECT_COLUMNS5}
|
|
8254
|
+
FROM agent_run_evaluations
|
|
8255
|
+
ORDER BY created_at DESC, id DESC
|
|
8256
|
+
LIMIT ? OFFSET ?`
|
|
8257
|
+
).all(limit, offset);
|
|
8258
|
+
return rows.map(toEvaluationRow);
|
|
8259
|
+
}
|
|
8260
|
+
function updateEvaluationStatus(id, status, completedAt) {
|
|
8261
|
+
const db = getDatabase();
|
|
8262
|
+
if (completedAt === void 0) {
|
|
8263
|
+
db.prepare(
|
|
8264
|
+
`UPDATE agent_run_evaluations SET status = ? WHERE id = ?`
|
|
8265
|
+
).run(status, id);
|
|
8266
|
+
} else {
|
|
8267
|
+
db.prepare(
|
|
8268
|
+
`UPDATE agent_run_evaluations SET status = ?, completed_at = ? WHERE id = ?`
|
|
8269
|
+
).run(status, completedAt, id);
|
|
8270
|
+
}
|
|
8271
|
+
return getEvaluation(id);
|
|
8272
|
+
}
|
|
8273
|
+
|
|
8274
|
+
// src/daemon/api/agent-evaluations.ts
|
|
8275
|
+
var DEFAULT_LIMIT2 = 50;
|
|
8276
|
+
var CreateEvaluationBody = external_exports.object({
|
|
8277
|
+
taskId: external_exports.string(),
|
|
8278
|
+
matrix: external_exports.object({
|
|
8279
|
+
runtimes: external_exports.array(RuntimeIdEnum).optional(),
|
|
8280
|
+
reasoningLevels: external_exports.array(ReasoningLevelEnum).optional(),
|
|
8281
|
+
models: external_exports.array(external_exports.string()).optional(),
|
|
8282
|
+
dryRun: external_exports.boolean().optional(),
|
|
8283
|
+
notes: external_exports.string().optional(),
|
|
8284
|
+
/**
|
|
8285
|
+
* Phase-level overrides applied to EVERY cell in the matrix. The matrix
|
|
8286
|
+
* dimensions (runtimes / reasoningLevels / models) vary the top-level
|
|
8287
|
+
* execution per cell; phase overrides here let you pin specific phases
|
|
8288
|
+
* to fixed reasoning/model/provider/maxTurns regardless of what the cell
|
|
8289
|
+
* is varying at the top level.
|
|
8290
|
+
*
|
|
8291
|
+
* Merged into each cell's `executionOverrides.phases` before runAgent
|
|
8292
|
+
* is invoked. Unknown phase names are ignored by the executor (a
|
|
8293
|
+
* one-shot warning is logged at run startup).
|
|
8294
|
+
*/
|
|
8295
|
+
phases: external_exports.record(external_exports.string(), PhaseExecutionOverrideBody).optional()
|
|
8296
|
+
}),
|
|
8297
|
+
notes: external_exports.string().optional()
|
|
8298
|
+
});
|
|
8299
|
+
function aggregateRuns(runs) {
|
|
8300
|
+
let completed = 0;
|
|
8301
|
+
let failed = 0;
|
|
8302
|
+
let skipped = 0;
|
|
8303
|
+
let totalTokens = 0;
|
|
8304
|
+
let totalCostUsd = 0;
|
|
8305
|
+
for (const run of runs) {
|
|
8306
|
+
if (run.status === "completed") completed++;
|
|
8307
|
+
else if (run.status === "failed") failed++;
|
|
8308
|
+
else if (run.status === "skipped") skipped++;
|
|
8309
|
+
totalTokens += run.tokens_used ?? 0;
|
|
8310
|
+
totalCostUsd += run.cost_usd ?? 0;
|
|
8311
|
+
}
|
|
8312
|
+
return {
|
|
8313
|
+
total: runs.length,
|
|
8314
|
+
completed,
|
|
8315
|
+
failed,
|
|
8316
|
+
skipped,
|
|
8317
|
+
totalTokens,
|
|
8318
|
+
totalCostUsd
|
|
8319
|
+
};
|
|
8320
|
+
}
|
|
8321
|
+
function summarizeFromByTool(byTool) {
|
|
8322
|
+
const tools = byTool ?? {};
|
|
8323
|
+
let total = 0;
|
|
8324
|
+
for (const count of Object.values(tools)) total += count;
|
|
8325
|
+
return { total, by_tool: tools };
|
|
8326
|
+
}
|
|
8327
|
+
function createAgentEvaluationHandlers(deps) {
|
|
8328
|
+
const { vaultDir, embeddingManager, logger } = deps;
|
|
8329
|
+
async function handleCreate(req) {
|
|
8330
|
+
const parsed = CreateEvaluationBody.safeParse(req.body);
|
|
8331
|
+
if (!parsed.success) {
|
|
8332
|
+
return {
|
|
8333
|
+
status: 400,
|
|
8334
|
+
body: { error: "Invalid request body", details: parsed.error.flatten() }
|
|
8335
|
+
};
|
|
8336
|
+
}
|
|
8337
|
+
const body = parsed.data;
|
|
8338
|
+
const cells = enumerateMatrixCells(body.matrix);
|
|
8339
|
+
if (cells.length === 0) {
|
|
8340
|
+
return {
|
|
8341
|
+
status: 400,
|
|
8342
|
+
body: { error: "Matrix produced zero cells" }
|
|
8343
|
+
};
|
|
8344
|
+
}
|
|
8345
|
+
const evalId = crypto.randomUUID();
|
|
8346
|
+
insertEvaluation({
|
|
8347
|
+
id: evalId,
|
|
8348
|
+
taskId: body.taskId,
|
|
8349
|
+
matrix: body.matrix,
|
|
8350
|
+
notes: body.notes ?? null
|
|
8351
|
+
});
|
|
8352
|
+
const dryRun = body.matrix.dryRun ?? false;
|
|
8353
|
+
void (async () => {
|
|
8354
|
+
const { runAgent: runAgent2 } = await import("./executor-F2YU7HXJ.js");
|
|
8355
|
+
let anyCompleted = false;
|
|
8356
|
+
let transitionedToRunning = false;
|
|
8357
|
+
const sharedPhases = body.matrix.phases;
|
|
8358
|
+
for (const cell of cells) {
|
|
8359
|
+
const cellOptions = {
|
|
8360
|
+
task: body.taskId,
|
|
8361
|
+
evaluationId: evalId,
|
|
8362
|
+
dryRun,
|
|
8363
|
+
embeddingManager,
|
|
8364
|
+
executionOverrides: {
|
|
8365
|
+
...cell.runtime ? { runtime: cell.runtime } : {},
|
|
8366
|
+
...cell.reasoningLevel ? { reasoningLevel: cell.reasoningLevel } : {},
|
|
8367
|
+
...cell.model ? { model: cell.model } : {},
|
|
8368
|
+
...sharedPhases && Object.keys(sharedPhases).length > 0 ? { phases: sharedPhases } : {}
|
|
8369
|
+
}
|
|
8370
|
+
};
|
|
8371
|
+
if (!transitionedToRunning) {
|
|
8372
|
+
updateEvaluationStatus(evalId, EVAL_STATUS_RUNNING);
|
|
8373
|
+
transitionedToRunning = true;
|
|
8374
|
+
}
|
|
8375
|
+
try {
|
|
8376
|
+
const result = await runAgent2(vaultDir, cellOptions);
|
|
8377
|
+
if (result.status === "completed") {
|
|
8378
|
+
anyCompleted = true;
|
|
8379
|
+
}
|
|
8380
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Evaluation cell finished", {
|
|
8381
|
+
evaluationId: evalId,
|
|
8382
|
+
runId: result.runId,
|
|
8383
|
+
status: result.status,
|
|
8384
|
+
runtime: cell.runtime,
|
|
8385
|
+
reasoningLevel: cell.reasoningLevel,
|
|
8386
|
+
model: cell.model
|
|
8387
|
+
});
|
|
8388
|
+
} catch (err) {
|
|
8389
|
+
logger.error(LOG_KINDS.AGENT_ERROR, "Evaluation cell threw", {
|
|
8390
|
+
evaluationId: evalId,
|
|
8391
|
+
runtime: cell.runtime,
|
|
8392
|
+
reasoningLevel: cell.reasoningLevel,
|
|
8393
|
+
model: cell.model,
|
|
8394
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8395
|
+
});
|
|
8396
|
+
}
|
|
8397
|
+
}
|
|
8398
|
+
const status = anyCompleted ? EVAL_STATUS_COMPLETED : EVAL_STATUS_FAILED;
|
|
8399
|
+
updateEvaluationStatus(evalId, status, epochSeconds());
|
|
8400
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Evaluation finished", {
|
|
8401
|
+
evaluationId: evalId,
|
|
8402
|
+
status,
|
|
8403
|
+
cellCount: cells.length
|
|
8404
|
+
});
|
|
8405
|
+
})().catch((err) => {
|
|
8406
|
+
logger.error(LOG_KINDS.AGENT_ERROR, "Evaluation fan-out failed", {
|
|
8407
|
+
evaluationId: evalId,
|
|
8408
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8409
|
+
});
|
|
8410
|
+
});
|
|
8411
|
+
return { body: { evaluationId: evalId, cellCount: cells.length } };
|
|
8412
|
+
}
|
|
8413
|
+
async function handleGet(req) {
|
|
8414
|
+
const evaluation = getEvaluation(req.params.id);
|
|
8415
|
+
if (!evaluation) {
|
|
8416
|
+
return { status: 404, body: { error: "Evaluation not found" } };
|
|
8417
|
+
}
|
|
8418
|
+
const runs = listRunsForEvaluation(evaluation.id);
|
|
8419
|
+
const writeIntentsByRun = countWriteIntentsByToolForEvaluation(evaluation.id);
|
|
8420
|
+
return {
|
|
8421
|
+
body: {
|
|
8422
|
+
evaluation: {
|
|
8423
|
+
id: evaluation.id,
|
|
8424
|
+
taskId: evaluation.task_id,
|
|
8425
|
+
matrix: evaluation.matrix,
|
|
8426
|
+
notes: evaluation.notes,
|
|
8427
|
+
status: evaluation.status,
|
|
8428
|
+
createdAt: evaluation.created_at,
|
|
8429
|
+
completedAt: evaluation.completed_at
|
|
8430
|
+
},
|
|
8431
|
+
runs: runs.map(
|
|
8432
|
+
(run) => serializeRun(run, {
|
|
8433
|
+
includeResumeFields: false,
|
|
8434
|
+
includePhaseCheckpoints: false,
|
|
8435
|
+
writeIntents: summarizeFromByTool(writeIntentsByRun[run.id]),
|
|
8436
|
+
duration_ms: runDurationMs(run)
|
|
8437
|
+
})
|
|
8438
|
+
),
|
|
8439
|
+
aggregate: aggregateRuns(runs)
|
|
8440
|
+
}
|
|
8441
|
+
};
|
|
8442
|
+
}
|
|
8443
|
+
async function handleList(req) {
|
|
8444
|
+
const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIMIT2;
|
|
8445
|
+
const offset = req.query.offset ? Number(req.query.offset) : 0;
|
|
8446
|
+
const evaluations = listEvaluations({ limit, offset });
|
|
8447
|
+
return {
|
|
8448
|
+
body: {
|
|
8449
|
+
evaluations: evaluations.map((e) => ({
|
|
8450
|
+
id: e.id,
|
|
8451
|
+
taskId: e.task_id,
|
|
8452
|
+
matrix: e.matrix,
|
|
8453
|
+
notes: e.notes,
|
|
8454
|
+
status: e.status,
|
|
8455
|
+
createdAt: e.created_at,
|
|
8456
|
+
completedAt: e.completed_at
|
|
8457
|
+
})),
|
|
8458
|
+
total: evaluations.length
|
|
8459
|
+
}
|
|
8460
|
+
};
|
|
8461
|
+
}
|
|
8462
|
+
return { handleCreate, handleGet, handleList };
|
|
8463
|
+
}
|
|
8464
|
+
|
|
8465
|
+
// src/daemon/api/digest-revisions.ts
|
|
8466
|
+
var DEFAULT_LIMIT3 = 50;
|
|
8467
|
+
var RestoreBody = external_exports.object({
|
|
8468
|
+
/** If supplied, recorded on the new revision row as the run that triggered the restore. */
|
|
8469
|
+
runId: external_exports.string().optional()
|
|
8470
|
+
});
|
|
8471
|
+
function createDigestRevisionHandlers(deps) {
|
|
8472
|
+
const { logger } = deps;
|
|
8473
|
+
async function handleList(req) {
|
|
8474
|
+
const agentId = req.query.agentId || DEFAULT_AGENT_ID;
|
|
8475
|
+
const tierRaw = req.query.tier;
|
|
8476
|
+
if (!tierRaw) {
|
|
8477
|
+
return { status: 400, body: { error: "tier is required" } };
|
|
8478
|
+
}
|
|
8479
|
+
const tier = Number(tierRaw);
|
|
8480
|
+
if (!Number.isFinite(tier)) {
|
|
8481
|
+
return { status: 400, body: { error: `tier must be numeric, got ${tierRaw}` } };
|
|
8482
|
+
}
|
|
8483
|
+
const limit = req.query.limit ? Number(req.query.limit) : DEFAULT_LIMIT3;
|
|
8484
|
+
const revisions = listDigestRevisions({ agentId, tier, limit });
|
|
8485
|
+
return { body: { revisions, count: revisions.length } };
|
|
8486
|
+
}
|
|
8487
|
+
async function handleRestore(req) {
|
|
8488
|
+
const revisionId = Number(req.params.id);
|
|
8489
|
+
if (!Number.isFinite(revisionId)) {
|
|
8490
|
+
return { status: 400, body: { error: "Invalid revision id" } };
|
|
8491
|
+
}
|
|
8492
|
+
const parsed = RestoreBody.safeParse(req.body ?? {});
|
|
8493
|
+
if (!parsed.success) {
|
|
8494
|
+
return {
|
|
8495
|
+
status: 400,
|
|
8496
|
+
body: { error: "Invalid request body", details: parsed.error.flatten() }
|
|
8497
|
+
};
|
|
8498
|
+
}
|
|
8499
|
+
const result = rollbackDigestExtract({
|
|
8500
|
+
revisionId,
|
|
8501
|
+
runId: parsed.data.runId ?? null
|
|
8502
|
+
});
|
|
8503
|
+
if (!result) {
|
|
8504
|
+
return { status: 404, body: { error: "Revision not found" } };
|
|
8505
|
+
}
|
|
8506
|
+
logger.info(LOG_KINDS.AGENT_RUN, "Digest revision restored", {
|
|
8507
|
+
revisionId,
|
|
8508
|
+
newRevisionId: result.newRevisionId,
|
|
8509
|
+
agentId: result.row.agent_id,
|
|
8510
|
+
tier: result.row.tier,
|
|
8511
|
+
triggeredBy: parsed.data.runId ?? "operator"
|
|
8512
|
+
});
|
|
8513
|
+
return {
|
|
8514
|
+
body: {
|
|
8515
|
+
ok: true,
|
|
8516
|
+
restored: revisionId,
|
|
8517
|
+
newRevisionId: result.newRevisionId
|
|
8518
|
+
}
|
|
8519
|
+
};
|
|
8520
|
+
}
|
|
8521
|
+
return { handleList, handleRestore };
|
|
8522
|
+
}
|
|
8523
|
+
|
|
6970
8524
|
// src/daemon/api/attachments.ts
|
|
6971
|
-
import
|
|
6972
|
-
import
|
|
8525
|
+
import fs19 from "fs";
|
|
8526
|
+
import path19 from "path";
|
|
6973
8527
|
var ATTACHMENT_MEDIA_TYPES = {
|
|
6974
8528
|
png: "image/png",
|
|
6975
8529
|
jpg: "image/jpeg",
|
|
@@ -6989,14 +8543,14 @@ function createAttachmentHandler(deps) {
|
|
|
6989
8543
|
const contentType2 = att.media_type ?? "application/octet-stream";
|
|
6990
8544
|
return { status: 200, headers: { "Content-Type": contentType2 }, body: att.data };
|
|
6991
8545
|
}
|
|
6992
|
-
const filePath =
|
|
8546
|
+
const filePath = path19.join(vaultDir, "attachments", filename);
|
|
6993
8547
|
let diskData;
|
|
6994
8548
|
try {
|
|
6995
|
-
diskData =
|
|
8549
|
+
diskData = fs19.readFileSync(filePath);
|
|
6996
8550
|
} catch {
|
|
6997
8551
|
return { status: 404, body: { error: "not_found" } };
|
|
6998
8552
|
}
|
|
6999
|
-
const ext =
|
|
8553
|
+
const ext = path19.extname(filename).slice(1).toLowerCase();
|
|
7000
8554
|
const contentType = ATTACHMENT_MEDIA_TYPES[ext] ?? "application/octet-stream";
|
|
7001
8555
|
return { status: 200, headers: { "Content-Type": contentType }, body: diskData };
|
|
7002
8556
|
}
|
|
@@ -7004,19 +8558,19 @@ function createAttachmentHandler(deps) {
|
|
|
7004
8558
|
}
|
|
7005
8559
|
|
|
7006
8560
|
// src/daemon/log-reconcile.ts
|
|
7007
|
-
import
|
|
7008
|
-
import
|
|
8561
|
+
import fs20 from "fs";
|
|
8562
|
+
import path20 from "path";
|
|
7009
8563
|
function reconcileLogBuffer(logDir, sinceTimestamp) {
|
|
7010
8564
|
let replayed = 0;
|
|
7011
8565
|
const files = [];
|
|
7012
8566
|
for (let i = 3; i >= 1; i--) {
|
|
7013
|
-
const rotated =
|
|
7014
|
-
if (
|
|
8567
|
+
const rotated = path20.join(logDir, `daemon.${i}.log`);
|
|
8568
|
+
if (fs20.existsSync(rotated)) files.push(rotated);
|
|
7015
8569
|
}
|
|
7016
|
-
const current =
|
|
7017
|
-
if (
|
|
8570
|
+
const current = path20.join(logDir, "daemon.log");
|
|
8571
|
+
if (fs20.existsSync(current)) files.push(current);
|
|
7018
8572
|
for (const file of files) {
|
|
7019
|
-
const content =
|
|
8573
|
+
const content = fs20.readFileSync(file, "utf-8");
|
|
7020
8574
|
for (const line of content.split("\n")) {
|
|
7021
8575
|
if (!line.trim()) continue;
|
|
7022
8576
|
try {
|
|
@@ -7044,11 +8598,14 @@ function reconcileLogBuffer(logDir, sinceTimestamp) {
|
|
|
7044
8598
|
// src/config/focus.ts
|
|
7045
8599
|
var CONFIG_FOCUS_SECTION_PARAM = "configSection";
|
|
7046
8600
|
var CONFIG_FOCUS_FIELD_PARAM = "configField";
|
|
8601
|
+
var CONFIG_FOCUS_TAB_PARAM = "tab";
|
|
7047
8602
|
var CONFIG_SECTION_IDS = {
|
|
7048
8603
|
appearance: "config-section-appearance",
|
|
8604
|
+
cortexInstructions: "config-section-cortex-instructions",
|
|
8605
|
+
cortexBuilder: "config-section-cortex-builder",
|
|
8606
|
+
cortexDigest: "config-section-cortex-digest",
|
|
7049
8607
|
settingsAgent: "config-section-settings-agent",
|
|
7050
8608
|
settingsEmbedding: "config-section-settings-embedding",
|
|
7051
|
-
settingsContextInjection: "config-section-settings-context-injection",
|
|
7052
8609
|
settingsNotifications: "config-section-settings-notifications",
|
|
7053
8610
|
settingsPlanCapture: "config-section-settings-plan-capture",
|
|
7054
8611
|
settingsProject: "config-section-settings-project",
|
|
@@ -7075,11 +8632,30 @@ var SECTION_RULES = [
|
|
|
7075
8632
|
sectionId: CONFIG_SECTION_IDS.settingsEmbedding,
|
|
7076
8633
|
sectionLabel: "Embedding"
|
|
7077
8634
|
},
|
|
8635
|
+
{
|
|
8636
|
+
prefix: "context.digest_tier",
|
|
8637
|
+
page: "/cortex",
|
|
8638
|
+
sectionId: CONFIG_SECTION_IDS.cortexDigest,
|
|
8639
|
+
sectionLabel: "Digest",
|
|
8640
|
+
searchParams: { [CONFIG_FOCUS_TAB_PARAM]: "digest" }
|
|
8641
|
+
},
|
|
8642
|
+
{
|
|
8643
|
+
prefix: "context.cortex",
|
|
8644
|
+
page: "/cortex",
|
|
8645
|
+
sectionId: CONFIG_SECTION_IDS.cortexInstructions,
|
|
8646
|
+
sectionLabel: "Instructions"
|
|
8647
|
+
},
|
|
8648
|
+
{
|
|
8649
|
+
prefix: "context.prompt",
|
|
8650
|
+
page: "/cortex",
|
|
8651
|
+
sectionId: CONFIG_SECTION_IDS.cortexInstructions,
|
|
8652
|
+
sectionLabel: "Instructions"
|
|
8653
|
+
},
|
|
7078
8654
|
{
|
|
7079
8655
|
prefix: "context",
|
|
7080
|
-
page: "/
|
|
7081
|
-
sectionId: CONFIG_SECTION_IDS.
|
|
7082
|
-
sectionLabel: "
|
|
8656
|
+
page: "/cortex",
|
|
8657
|
+
sectionId: CONFIG_SECTION_IDS.cortexInstructions,
|
|
8658
|
+
sectionLabel: "Instructions"
|
|
7083
8659
|
},
|
|
7084
8660
|
{
|
|
7085
8661
|
prefix: "notifications",
|
|
@@ -7146,7 +8722,9 @@ var EXACT_FIELD_LABELS = {
|
|
|
7146
8722
|
"embedding.provider": "Provider",
|
|
7147
8723
|
"embedding.model": "Model",
|
|
7148
8724
|
"embedding.base_url": "Base URL",
|
|
7149
|
-
"context.digest_tier": "Digest Tier",
|
|
8725
|
+
"context.digest_tier": "Preferred Digest Tier",
|
|
8726
|
+
"context.session_start_digest_enabled": "Session-Start Digest",
|
|
8727
|
+
"context.cortex_enabled": "Session-Start Instructions",
|
|
7150
8728
|
"context.prompt_search": "Prompt Search",
|
|
7151
8729
|
"context.prompt_max_spores": "Max Spores per Prompt",
|
|
7152
8730
|
"notifications.enabled": "Notifications",
|
|
@@ -7167,8 +8745,8 @@ var EXACT_FIELD_LABELS = {
|
|
|
7167
8745
|
var DYNAMIC_FIELD_LABEL_RULES = [
|
|
7168
8746
|
{
|
|
7169
8747
|
prefix: "notifications.domains.",
|
|
7170
|
-
format: (
|
|
7171
|
-
const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(
|
|
8748
|
+
format: (path25) => {
|
|
8749
|
+
const match = /^notifications\.domains\.([^.]+)\.(enabled|mode)$/.exec(path25);
|
|
7172
8750
|
if (!match) return null;
|
|
7173
8751
|
const [, domain, leaf] = match;
|
|
7174
8752
|
const domainLabel = humanizeToken(domain);
|
|
@@ -7177,13 +8755,13 @@ var DYNAMIC_FIELD_LABEL_RULES = [
|
|
|
7177
8755
|
}
|
|
7178
8756
|
];
|
|
7179
8757
|
var SAVE_MESSAGE_LABEL_LIMIT = 3;
|
|
7180
|
-
function resolveConfigFocusTarget(
|
|
7181
|
-
const section = findSectionRule(
|
|
8758
|
+
function resolveConfigFocusTarget(path25) {
|
|
8759
|
+
const section = findSectionRule(path25);
|
|
7182
8760
|
if (!section) return null;
|
|
7183
8761
|
return {
|
|
7184
8762
|
...section,
|
|
7185
|
-
fieldPath:
|
|
7186
|
-
fieldLabel: resolveFieldLabel(
|
|
8763
|
+
fieldPath: path25,
|
|
8764
|
+
fieldLabel: resolveFieldLabel(path25)
|
|
7187
8765
|
};
|
|
7188
8766
|
}
|
|
7189
8767
|
function buildConfigFocusLink(target) {
|
|
@@ -7218,25 +8796,25 @@ function buildScopedConfigSaveNotification(scope, touchedPaths) {
|
|
|
7218
8796
|
}
|
|
7219
8797
|
};
|
|
7220
8798
|
}
|
|
7221
|
-
function findSectionRule(
|
|
8799
|
+
function findSectionRule(path25) {
|
|
7222
8800
|
for (const rule of SECTION_RULES) {
|
|
7223
|
-
if (
|
|
8801
|
+
if (path25 === rule.prefix || path25.startsWith(`${rule.prefix}.`)) {
|
|
7224
8802
|
const { page, sectionId, sectionLabel, searchParams } = rule;
|
|
7225
8803
|
return { page, sectionId, sectionLabel, searchParams };
|
|
7226
8804
|
}
|
|
7227
8805
|
}
|
|
7228
8806
|
return null;
|
|
7229
8807
|
}
|
|
7230
|
-
function resolveFieldLabel(
|
|
7231
|
-
const exact = EXACT_FIELD_LABELS[
|
|
8808
|
+
function resolveFieldLabel(path25) {
|
|
8809
|
+
const exact = EXACT_FIELD_LABELS[path25];
|
|
7232
8810
|
if (exact) return exact;
|
|
7233
8811
|
for (const rule of DYNAMIC_FIELD_LABEL_RULES) {
|
|
7234
|
-
if (
|
|
7235
|
-
const label = rule.format(
|
|
8812
|
+
if (path25 === rule.prefix || path25.startsWith(rule.prefix)) {
|
|
8813
|
+
const label = rule.format(path25);
|
|
7236
8814
|
if (label) return label;
|
|
7237
8815
|
}
|
|
7238
8816
|
}
|
|
7239
|
-
return humanizeToken(
|
|
8817
|
+
return humanizeToken(path25.split(".").pop() ?? "setting");
|
|
7240
8818
|
}
|
|
7241
8819
|
function humanizeToken(value) {
|
|
7242
8820
|
return value.split(/[-_]/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -7356,6 +8934,48 @@ var PowerManager = class {
|
|
|
7356
8934
|
}
|
|
7357
8935
|
};
|
|
7358
8936
|
|
|
8937
|
+
// src/daemon/inflight-runs.ts
|
|
8938
|
+
var DEFAULT_DRAIN_TIMEOUT_MS = 3e4;
|
|
8939
|
+
var InflightRunRegistry = class {
|
|
8940
|
+
runs = /* @__PURE__ */ new Set();
|
|
8941
|
+
/**
|
|
8942
|
+
* Track a fire-and-forget agent run. The promise is removed from the
|
|
8943
|
+
* registry in a finally handler so a resolved/rejected run does not hold
|
|
8944
|
+
* memory. Resolves immediately — callers must not await this directly.
|
|
8945
|
+
*/
|
|
8946
|
+
register(promise) {
|
|
8947
|
+
const tracked = Promise.resolve(promise).finally(() => {
|
|
8948
|
+
this.runs.delete(tracked);
|
|
8949
|
+
});
|
|
8950
|
+
this.runs.add(tracked);
|
|
8951
|
+
}
|
|
8952
|
+
/** Number of runs currently being tracked. */
|
|
8953
|
+
get size() {
|
|
8954
|
+
return this.runs.size;
|
|
8955
|
+
}
|
|
8956
|
+
/**
|
|
8957
|
+
* Wait for every tracked run to settle, up to `timeoutMs`. Returns when
|
|
8958
|
+
* either every run completes or the deadline elapses — whichever happens
|
|
8959
|
+
* first. The 30-second default mirrors a typical daemon shutdown budget;
|
|
8960
|
+
* callers are free to override for tests or tighter environments.
|
|
8961
|
+
*/
|
|
8962
|
+
async drain(timeoutMs = DEFAULT_DRAIN_TIMEOUT_MS) {
|
|
8963
|
+
if (this.runs.size === 0) return { settled: true, remaining: 0 };
|
|
8964
|
+
const snapshot = Array.from(this.runs);
|
|
8965
|
+
let timer;
|
|
8966
|
+
const timeoutPromise = new Promise((resolve3) => {
|
|
8967
|
+
timer = setTimeout(() => resolve3("timeout"), timeoutMs);
|
|
8968
|
+
});
|
|
8969
|
+
const allSettled = Promise.allSettled(snapshot).then(() => "settled");
|
|
8970
|
+
try {
|
|
8971
|
+
const outcome = await Promise.race([allSettled, timeoutPromise]);
|
|
8972
|
+
return { settled: outcome === "settled", remaining: this.runs.size };
|
|
8973
|
+
} finally {
|
|
8974
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
8975
|
+
}
|
|
8976
|
+
}
|
|
8977
|
+
};
|
|
8978
|
+
|
|
7359
8979
|
// src/daemon/jobs/session-maintenance.ts
|
|
7360
8980
|
function completeStaleActiveSessions(thresholdSeconds = STALE_SESSION_THRESHOLD_MS / MS_PER_SECOND) {
|
|
7361
8981
|
const db = getDatabase();
|
|
@@ -7500,8 +9120,8 @@ function registerPowerJobs(powerManager, deps) {
|
|
|
7500
9120
|
}
|
|
7501
9121
|
|
|
7502
9122
|
// src/daemon/reconciliation.ts
|
|
7503
|
-
import
|
|
7504
|
-
import
|
|
9123
|
+
import fs21 from "fs";
|
|
9124
|
+
import path21 from "path";
|
|
7505
9125
|
|
|
7506
9126
|
// src/daemon/event-handlers.ts
|
|
7507
9127
|
var TOOL_INPUT_STORE_LIMIT = 4e3;
|
|
@@ -7670,10 +9290,10 @@ function createReconciler({ bufferDir, logger }) {
|
|
|
7670
9290
|
function reconcileSession(sessionId) {
|
|
7671
9291
|
if (reconciledSessions.has(sessionId)) return;
|
|
7672
9292
|
reconciledSessions.add(sessionId);
|
|
7673
|
-
const bufferPath =
|
|
9293
|
+
const bufferPath = path21.join(bufferDir, `${sessionId}.jsonl`);
|
|
7674
9294
|
let content;
|
|
7675
9295
|
try {
|
|
7676
|
-
content =
|
|
9296
|
+
content = fs21.readFileSync(bufferPath, "utf-8").trim();
|
|
7677
9297
|
} catch {
|
|
7678
9298
|
return;
|
|
7679
9299
|
}
|
|
@@ -7742,7 +9362,19 @@ function createReconciler({ bufferDir, logger }) {
|
|
|
7742
9362
|
}
|
|
7743
9363
|
|
|
7744
9364
|
// src/daemon/stop-processing.ts
|
|
7745
|
-
import
|
|
9365
|
+
import fs22 from "fs";
|
|
9366
|
+
import path22 from "path";
|
|
9367
|
+
|
|
9368
|
+
// src/daemon/capture-gating.ts
|
|
9369
|
+
function gateEventByCaptureRules(event, options) {
|
|
9370
|
+
const transcriptMeta = event.transcriptPath ? readTranscriptMeta(event.transcriptPath) ?? void 0 : void 0;
|
|
9371
|
+
const manifests = options?.manifests ?? loadManifests();
|
|
9372
|
+
const decision = evaluateSessionCaptureRules(manifests, event.agent, {
|
|
9373
|
+
transcriptPath: event.transcriptPath,
|
|
9374
|
+
transcriptMeta
|
|
9375
|
+
});
|
|
9376
|
+
return { decision, hadTranscriptMeta: transcriptMeta !== void 0 };
|
|
9377
|
+
}
|
|
7746
9378
|
|
|
7747
9379
|
// src/daemon/capture-images.ts
|
|
7748
9380
|
var SESSION_SHORT_LEN = 6;
|
|
@@ -7779,80 +9411,8 @@ function captureBatchImages(input) {
|
|
|
7779
9411
|
}
|
|
7780
9412
|
}
|
|
7781
9413
|
|
|
7782
|
-
// src/daemon/plan-capture.ts
|
|
7783
|
-
import { createHash as createHash4 } from "crypto";
|
|
7784
|
-
import os8 from "os";
|
|
7785
|
-
import path20 from "path";
|
|
7786
|
-
function extractTaggedPlans(text, tags) {
|
|
7787
|
-
const results = [];
|
|
7788
|
-
for (const tag of tags) {
|
|
7789
|
-
const regex = new RegExp(`<${tag}>\\n?([\\s\\S]*?)\\n?</${tag}>`, "g");
|
|
7790
|
-
let match;
|
|
7791
|
-
while ((match = regex.exec(text)) !== null) {
|
|
7792
|
-
const content = match[1].trim();
|
|
7793
|
-
if (content) results.push({ tag, content });
|
|
7794
|
-
}
|
|
7795
|
-
}
|
|
7796
|
-
return results;
|
|
7797
|
-
}
|
|
7798
|
-
var TRANSCRIPT_SOURCE_PREFIX = "transcript:";
|
|
7799
|
-
var FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
7800
|
-
"Write",
|
|
7801
|
-
"Edit",
|
|
7802
|
-
"Create",
|
|
7803
|
-
"write",
|
|
7804
|
-
"edit",
|
|
7805
|
-
"patch",
|
|
7806
|
-
"create"
|
|
7807
|
-
]);
|
|
7808
|
-
var HEADING_REGEX = /^#\s+(.+)$/m;
|
|
7809
|
-
var PLAN_ID_HASH_LENGTH = 16;
|
|
7810
|
-
function isInPlanDirectory(filePath, watchDirs, projectRoot) {
|
|
7811
|
-
const abs = path20.isAbsolute(filePath) ? filePath : path20.resolve(projectRoot, filePath);
|
|
7812
|
-
return watchDirs.some((dir) => {
|
|
7813
|
-
const expanded = dir.startsWith("~/") ? path20.join(os8.homedir(), dir.slice(2)) : dir;
|
|
7814
|
-
const absDir = path20.isAbsolute(expanded) ? expanded : path20.resolve(projectRoot, expanded);
|
|
7815
|
-
const prefix = absDir.endsWith(path20.sep) ? absDir : absDir + path20.sep;
|
|
7816
|
-
return abs === absDir || abs.startsWith(prefix);
|
|
7817
|
-
});
|
|
7818
|
-
}
|
|
7819
|
-
function isPlanWriteEvent(toolName, toolInput, config) {
|
|
7820
|
-
if (!FILE_WRITE_TOOLS.has(toolName)) return null;
|
|
7821
|
-
const filePath = toolInput?.file_path ?? toolInput?.path ?? toolInput?.filePath;
|
|
7822
|
-
if (typeof filePath !== "string") return null;
|
|
7823
|
-
if (!isInPlanDirectory(filePath, config.watchDirs, config.projectRoot)) return null;
|
|
7824
|
-
if (config.extensions?.length) {
|
|
7825
|
-
const ext = path20.extname(filePath).toLowerCase();
|
|
7826
|
-
if (!config.extensions.includes(ext)) return null;
|
|
7827
|
-
}
|
|
7828
|
-
return filePath;
|
|
7829
|
-
}
|
|
7830
|
-
function parsePlanTitle(content, filename) {
|
|
7831
|
-
const match = HEADING_REGEX.exec(content);
|
|
7832
|
-
if (match) return match[1].trim();
|
|
7833
|
-
return filename ?? null;
|
|
7834
|
-
}
|
|
7835
|
-
function capturePlan(input) {
|
|
7836
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
7837
|
-
const contentHash = createHash4(CONTENT_HASH_ALGORITHM).update(input.content).digest("hex");
|
|
7838
|
-
const id = createHash4("md5").update(input.sourcePath).digest("hex").slice(0, PLAN_ID_HASH_LENGTH);
|
|
7839
|
-
const title = parsePlanTitle(input.content, path20.basename(input.sourcePath));
|
|
7840
|
-
return upsertPlan({
|
|
7841
|
-
id,
|
|
7842
|
-
title,
|
|
7843
|
-
content: input.content,
|
|
7844
|
-
source_path: input.sourcePath,
|
|
7845
|
-
session_id: input.sessionId,
|
|
7846
|
-
prompt_batch_id: input.promptBatchId ?? null,
|
|
7847
|
-
content_hash: contentHash,
|
|
7848
|
-
status: "active",
|
|
7849
|
-
created_at: now,
|
|
7850
|
-
updated_at: now
|
|
7851
|
-
});
|
|
7852
|
-
}
|
|
7853
|
-
|
|
7854
9414
|
// src/daemon/skill-usage.ts
|
|
7855
|
-
import
|
|
9415
|
+
import crypto2 from "crypto";
|
|
7856
9416
|
var SKILL_USAGE_DETECTION_ENABLED = false;
|
|
7857
9417
|
var MAX_ACTIVE_SKILLS_CHECK = 1e3;
|
|
7858
9418
|
function detectSkillUsage(sessionId, transcriptContent) {
|
|
@@ -7872,7 +9432,7 @@ function detectSkillUsage(sessionId, transcriptContent) {
|
|
|
7872
9432
|
if (!pattern.test(transcriptContent)) continue;
|
|
7873
9433
|
if (hasUsageForSkillAndSession(skill.id, sessionId)) continue;
|
|
7874
9434
|
insertSkillUsage({
|
|
7875
|
-
id:
|
|
9435
|
+
id: crypto2.randomUUID(),
|
|
7876
9436
|
skill_id: skill.id,
|
|
7877
9437
|
session_id: sessionId,
|
|
7878
9438
|
detected_at: now
|
|
@@ -7887,6 +9447,46 @@ function escapeRegex(s) {
|
|
|
7887
9447
|
}
|
|
7888
9448
|
|
|
7889
9449
|
// src/daemon/stop-processing.ts
|
|
9450
|
+
var MILLISECONDS_PER_SECOND = 1e3;
|
|
9451
|
+
function isEligiblePlanExtension(filePath, extensions) {
|
|
9452
|
+
if (!extensions || extensions.length === 0) return true;
|
|
9453
|
+
const extension = path22.extname(filePath).toLowerCase();
|
|
9454
|
+
return extensions.includes(extension);
|
|
9455
|
+
}
|
|
9456
|
+
function collectPlanFiles(rootDir, extensions) {
|
|
9457
|
+
const files = [];
|
|
9458
|
+
const stack = [rootDir];
|
|
9459
|
+
while (stack.length > 0) {
|
|
9460
|
+
const currentDir = stack.pop();
|
|
9461
|
+
if (!currentDir) continue;
|
|
9462
|
+
let entries;
|
|
9463
|
+
try {
|
|
9464
|
+
entries = fs22.readdirSync(currentDir, { withFileTypes: true });
|
|
9465
|
+
} catch {
|
|
9466
|
+
continue;
|
|
9467
|
+
}
|
|
9468
|
+
for (const entry of entries) {
|
|
9469
|
+
const absolutePath = path22.join(currentDir, entry.name);
|
|
9470
|
+
if (entry.isDirectory()) {
|
|
9471
|
+
stack.push(absolutePath);
|
|
9472
|
+
continue;
|
|
9473
|
+
}
|
|
9474
|
+
if (entry.isFile() && isEligiblePlanExtension(absolutePath, extensions)) {
|
|
9475
|
+
files.push(absolutePath);
|
|
9476
|
+
}
|
|
9477
|
+
}
|
|
9478
|
+
}
|
|
9479
|
+
return files;
|
|
9480
|
+
}
|
|
9481
|
+
function resolvePromptBatchIdForPlanWrite(batches, modifiedAtSeconds) {
|
|
9482
|
+
for (let index = batches.length - 1; index >= 0; index--) {
|
|
9483
|
+
const startedAt = batches[index].started_at;
|
|
9484
|
+
if (startedAt !== null && startedAt <= modifiedAtSeconds) {
|
|
9485
|
+
return batches[index].id;
|
|
9486
|
+
}
|
|
9487
|
+
}
|
|
9488
|
+
return void 0;
|
|
9489
|
+
}
|
|
7890
9490
|
function enrichTurnsWithToolMetadata(turns, events) {
|
|
7891
9491
|
if (events.length === 0 || turns.length === 0) return;
|
|
7892
9492
|
const toolEvents = events.filter((e) => e.type === "tool_use");
|
|
@@ -7914,7 +9514,7 @@ function enrichTurnsWithToolMetadata(turns, events) {
|
|
|
7914
9514
|
}
|
|
7915
9515
|
}
|
|
7916
9516
|
function createStopProcessor(deps) {
|
|
7917
|
-
const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir } = deps;
|
|
9517
|
+
const { registry, sessionBuffers, transcriptMiner, embeddingManager, logger, liveConfig, vaultDir, planWatchConfig } = deps;
|
|
7918
9518
|
let activeStopProcessing = null;
|
|
7919
9519
|
const sessionTitleCache = /* @__PURE__ */ new Map();
|
|
7920
9520
|
const StopBody = external_exports.object({
|
|
@@ -8016,7 +9616,7 @@ function createStopProcessor(deps) {
|
|
|
8016
9616
|
let transcriptText = null;
|
|
8017
9617
|
if (hookTranscriptPath) {
|
|
8018
9618
|
try {
|
|
8019
|
-
transcriptText =
|
|
9619
|
+
transcriptText = fs22.readFileSync(hookTranscriptPath, "utf-8");
|
|
8020
9620
|
} catch {
|
|
8021
9621
|
}
|
|
8022
9622
|
}
|
|
@@ -8048,11 +9648,12 @@ function createStopProcessor(deps) {
|
|
|
8048
9648
|
const taggedPlans = extractTaggedPlans(turn.aiResponse, deps.planTags);
|
|
8049
9649
|
for (const { tag, content } of taggedPlans) {
|
|
8050
9650
|
try {
|
|
8051
|
-
|
|
8052
|
-
|
|
9651
|
+
captureTaggedPlan({
|
|
9652
|
+
tag,
|
|
8053
9653
|
content,
|
|
8054
9654
|
sessionId,
|
|
8055
|
-
promptBatchId: latestBatch?.id ?? null
|
|
9655
|
+
promptBatchId: latestBatch?.id ?? null,
|
|
9656
|
+
logger
|
|
8056
9657
|
});
|
|
8057
9658
|
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured from transcript tag", {
|
|
8058
9659
|
session_id: sessionId,
|
|
@@ -8069,6 +9670,50 @@ function createStopProcessor(deps) {
|
|
|
8069
9670
|
}
|
|
8070
9671
|
}
|
|
8071
9672
|
}
|
|
9673
|
+
const sessionStartedAt = currentSession?.started_at ?? (sessionMeta?.started_at ? Math.floor(new Date(sessionMeta.started_at).getTime() / MILLISECONDS_PER_SECOND) : epochSeconds());
|
|
9674
|
+
const sessionStopMs = Date.now();
|
|
9675
|
+
const sessionBatches = listBatchesBySession(sessionId);
|
|
9676
|
+
for (const watchDir of planWatchConfig.watchDirs) {
|
|
9677
|
+
const absoluteWatchDir = resolvePlanWatchDir(watchDir, planWatchConfig.projectRoot);
|
|
9678
|
+
if (!fs22.existsSync(absoluteWatchDir)) continue;
|
|
9679
|
+
const planFiles = collectPlanFiles(absoluteWatchDir, planWatchConfig.extensions);
|
|
9680
|
+
for (const planFile of planFiles) {
|
|
9681
|
+
let stats;
|
|
9682
|
+
try {
|
|
9683
|
+
stats = fs22.statSync(planFile);
|
|
9684
|
+
} catch {
|
|
9685
|
+
continue;
|
|
9686
|
+
}
|
|
9687
|
+
if (stats.mtimeMs < sessionStartedAt * MILLISECONDS_PER_SECOND || stats.mtimeMs > sessionStopMs) {
|
|
9688
|
+
continue;
|
|
9689
|
+
}
|
|
9690
|
+
try {
|
|
9691
|
+
const content = fs22.readFileSync(planFile, "utf-8");
|
|
9692
|
+
const promptBatchId = resolvePromptBatchIdForPlanWrite(
|
|
9693
|
+
sessionBatches,
|
|
9694
|
+
Math.floor(stats.mtimeMs / MILLISECONDS_PER_SECOND)
|
|
9695
|
+
);
|
|
9696
|
+
capturePlan({
|
|
9697
|
+
sourcePath: planFile,
|
|
9698
|
+
projectRoot: planWatchConfig.projectRoot,
|
|
9699
|
+
content,
|
|
9700
|
+
sessionId,
|
|
9701
|
+
promptBatchId,
|
|
9702
|
+
logger
|
|
9703
|
+
});
|
|
9704
|
+
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan reconciled from configured plan dir", {
|
|
9705
|
+
session_id: sessionId,
|
|
9706
|
+
source_path: planFile
|
|
9707
|
+
});
|
|
9708
|
+
} catch (err) {
|
|
9709
|
+
logger.warn(LOG_KINDS.CAPTURE_PLAN, "Failed to reconcile plan from configured plan dir", {
|
|
9710
|
+
session_id: sessionId,
|
|
9711
|
+
source_path: planFile,
|
|
9712
|
+
error: err.message
|
|
9713
|
+
});
|
|
9714
|
+
}
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
8072
9717
|
if (!hasTitle) {
|
|
8073
9718
|
triggerTitleSummary2(sessionId);
|
|
8074
9719
|
}
|
|
@@ -8115,12 +9760,11 @@ function createStopProcessor(deps) {
|
|
|
8115
9760
|
last_assistant_message: lastAssistantMessage
|
|
8116
9761
|
} = StopBody.parse(req.body);
|
|
8117
9762
|
if (hookTranscriptPath) {
|
|
8118
|
-
const transcriptMeta = readTranscriptMeta(hookTranscriptPath) ?? void 0;
|
|
8119
9763
|
const detectedAgent = agent ?? getSession(sessionId)?.agent ?? "claude-code";
|
|
8120
|
-
const decision =
|
|
8121
|
-
transcriptPath: hookTranscriptPath,
|
|
8122
|
-
|
|
8123
|
-
|
|
9764
|
+
const { decision } = gateEventByCaptureRules(
|
|
9765
|
+
{ agent: detectedAgent, transcriptPath: hookTranscriptPath },
|
|
9766
|
+
{ manifests: loadManifests() }
|
|
9767
|
+
);
|
|
8124
9768
|
if (decision.action === "drop") {
|
|
8125
9769
|
const deleted = cleanupInvalidCapturedSession(sessionId);
|
|
8126
9770
|
logger.info(LOG_KINDS.HOOKS_STOP, "Stop ignored \u2014 invalid captured session", {
|
|
@@ -8175,9 +9819,10 @@ function createStopProcessor(deps) {
|
|
|
8175
9819
|
}
|
|
8176
9820
|
|
|
8177
9821
|
// src/daemon/event-dispatch.ts
|
|
8178
|
-
import
|
|
8179
|
-
import
|
|
9822
|
+
import fs23 from "fs";
|
|
9823
|
+
import path23 from "path";
|
|
8180
9824
|
var EventBody = external_exports.object({ type: external_exports.string(), session_id: external_exports.string() }).passthrough();
|
|
9825
|
+
var DROP_DECISION_CACHE_MAX = 1024;
|
|
8181
9826
|
function createEventDispatcher(deps) {
|
|
8182
9827
|
const {
|
|
8183
9828
|
registry,
|
|
@@ -8192,6 +9837,39 @@ function createEventDispatcher(deps) {
|
|
|
8192
9837
|
triggerTitleSummary: triggerTitleSummary2
|
|
8193
9838
|
} = deps;
|
|
8194
9839
|
const projectRoot = process.cwd();
|
|
9840
|
+
const manifests = loadManifests();
|
|
9841
|
+
const planTagsByAgent = new Map(
|
|
9842
|
+
manifests.map((manifest) => [manifest.name, manifest.capture?.planTags ?? []])
|
|
9843
|
+
);
|
|
9844
|
+
const droppedSessions = /* @__PURE__ */ new Map();
|
|
9845
|
+
function rememberDropped(sessionId, reason, hadTranscriptMeta) {
|
|
9846
|
+
if (droppedSessions.size >= DROP_DECISION_CACHE_MAX) {
|
|
9847
|
+
const oldest = droppedSessions.keys().next().value;
|
|
9848
|
+
if (oldest !== void 0) droppedSessions.delete(oldest);
|
|
9849
|
+
}
|
|
9850
|
+
droppedSessions.set(sessionId, { reason, hadTranscriptMeta });
|
|
9851
|
+
}
|
|
9852
|
+
function evaluateAutoRegistration(event) {
|
|
9853
|
+
const transcriptPath = typeof event.transcript_path === "string" && event.transcript_path.length > 0 ? event.transcript_path : void 0;
|
|
9854
|
+
const detectedAgent = typeof event.agent === "string" ? event.agent : DEFAULT_SYMBIONT_NAME;
|
|
9855
|
+
try {
|
|
9856
|
+
return gateEventByCaptureRules(
|
|
9857
|
+
{ agent: detectedAgent, transcriptPath },
|
|
9858
|
+
{ manifests }
|
|
9859
|
+
);
|
|
9860
|
+
} catch (err) {
|
|
9861
|
+
logger.error(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Capture-rules evaluator threw", {
|
|
9862
|
+
error: String(err),
|
|
9863
|
+
session_id: typeof event.session_id === "string" ? event.session_id : void 0,
|
|
9864
|
+
agent: detectedAgent
|
|
9865
|
+
});
|
|
9866
|
+
return { decision: { action: "pass" }, hadTranscriptMeta: false };
|
|
9867
|
+
}
|
|
9868
|
+
}
|
|
9869
|
+
function getPlanTagsForAgent(agent) {
|
|
9870
|
+
const agentName = typeof agent === "string" && agent.length > 0 ? agent : DEFAULT_SYMBIONT_NAME;
|
|
9871
|
+
return planTagsByAgent.get(agentName) ?? [];
|
|
9872
|
+
}
|
|
8195
9873
|
return async (req) => {
|
|
8196
9874
|
const validated = EventBody.parse(req.body);
|
|
8197
9875
|
const event = {
|
|
@@ -8200,13 +9878,38 @@ function createEventDispatcher(deps) {
|
|
|
8200
9878
|
};
|
|
8201
9879
|
logger.debug(LOG_KINDS.HOOKS_EVENT, "Event received", { type: event.type, session_id: event.session_id });
|
|
8202
9880
|
if (!registry.getSession(event.session_id)) {
|
|
9881
|
+
const cached = droppedSessions.get(event.session_id);
|
|
9882
|
+
const hasTranscriptNow = typeof event.transcript_path === "string" && event.transcript_path.length > 0;
|
|
9883
|
+
const shouldReevaluate = cached && !cached.hadTranscriptMeta && hasTranscriptNow;
|
|
9884
|
+
if (cached && !shouldReevaluate) {
|
|
9885
|
+
const reason = cached.reason ?? "rule";
|
|
9886
|
+
logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Ignored event for previously-dropped session", {
|
|
9887
|
+
session_id: event.session_id,
|
|
9888
|
+
type: event.type,
|
|
9889
|
+
reason
|
|
9890
|
+
});
|
|
9891
|
+
return { body: { ok: true, ignored: reason } };
|
|
9892
|
+
}
|
|
9893
|
+
if (shouldReevaluate) {
|
|
9894
|
+
droppedSessions.delete(event.session_id);
|
|
9895
|
+
}
|
|
9896
|
+
const { decision, hadTranscriptMeta } = evaluateAutoRegistration(event);
|
|
9897
|
+
if (decision.action === "drop") {
|
|
9898
|
+
rememberDropped(event.session_id, decision.reason, hadTranscriptMeta);
|
|
9899
|
+
logger.info(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Ignored event that failed session capture rules", {
|
|
9900
|
+
session_id: event.session_id,
|
|
9901
|
+
type: event.type,
|
|
9902
|
+
reason: decision.reason ?? "rule"
|
|
9903
|
+
});
|
|
9904
|
+
return { body: { ok: true, ignored: decision.reason ?? "rule" } };
|
|
9905
|
+
}
|
|
8203
9906
|
registry.register(event.session_id, { started_at: event.timestamp });
|
|
8204
9907
|
logger.debug(LOG_KINDS.LIFECYCLE_AUTO_REGISTER, "Auto-registered session from event", { session_id: event.session_id });
|
|
8205
9908
|
const now = epochSeconds();
|
|
8206
9909
|
const startedEpoch = Math.floor(new Date(event.timestamp).getTime() / 1e3);
|
|
8207
9910
|
upsertSession({
|
|
8208
9911
|
id: event.session_id,
|
|
8209
|
-
agent: event.agent ??
|
|
9912
|
+
agent: event.agent ?? DEFAULT_SYMBIONT_NAME,
|
|
8210
9913
|
status: "active",
|
|
8211
9914
|
started_at: startedEpoch,
|
|
8212
9915
|
created_at: now,
|
|
@@ -8215,7 +9918,7 @@ function createEventDispatcher(deps) {
|
|
|
8215
9918
|
reconcileSession(event.session_id);
|
|
8216
9919
|
}
|
|
8217
9920
|
if (!sessionBuffers.has(event.session_id)) {
|
|
8218
|
-
const bufferDir =
|
|
9921
|
+
const bufferDir = path23.join(vaultDir, "buffer");
|
|
8219
9922
|
sessionBuffers.set(event.session_id, new EventBuffer(bufferDir, event.session_id));
|
|
8220
9923
|
}
|
|
8221
9924
|
sessionBuffers.get(event.session_id).append(event);
|
|
@@ -8241,6 +9944,29 @@ function createEventDispatcher(deps) {
|
|
|
8241
9944
|
try {
|
|
8242
9945
|
const { batchId, promptNumber } = handleUserPrompt(event.session_id, promptText || void 0);
|
|
8243
9946
|
logger.debug(LOG_KINDS.CAPTURE_BATCH, "Batch opened", { session_id: event.session_id, batch_id: batchId, prompt_number: promptNumber });
|
|
9947
|
+
const taggedPlans = extractTaggedPlans(promptText, getPlanTagsForAgent(event.agent));
|
|
9948
|
+
for (const { tag, content } of taggedPlans) {
|
|
9949
|
+
try {
|
|
9950
|
+
captureTaggedPlan({
|
|
9951
|
+
tag,
|
|
9952
|
+
content,
|
|
9953
|
+
sessionId: event.session_id,
|
|
9954
|
+
promptBatchId: batchId,
|
|
9955
|
+
logger
|
|
9956
|
+
});
|
|
9957
|
+
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured from prompt tag", {
|
|
9958
|
+
session_id: event.session_id,
|
|
9959
|
+
tag,
|
|
9960
|
+
content_length: content.length
|
|
9961
|
+
});
|
|
9962
|
+
} catch (err) {
|
|
9963
|
+
logger.warn(LOG_KINDS.CAPTURE_PLAN, "Failed to capture plan from prompt tag", {
|
|
9964
|
+
session_id: event.session_id,
|
|
9965
|
+
tag,
|
|
9966
|
+
error: err.message
|
|
9967
|
+
});
|
|
9968
|
+
}
|
|
9969
|
+
}
|
|
8244
9970
|
const eventImages = event.images;
|
|
8245
9971
|
if (Array.isArray(eventImages) && eventImages.length > 0) {
|
|
8246
9972
|
captureBatchImages({
|
|
@@ -8274,13 +10000,15 @@ function createEventDispatcher(deps) {
|
|
|
8274
10000
|
);
|
|
8275
10001
|
if (planFilePath) {
|
|
8276
10002
|
const captureSessionId = event.session_id;
|
|
8277
|
-
|
|
10003
|
+
fs23.promises.readFile(planFilePath, "utf-8").then((planContent) => {
|
|
8278
10004
|
const latestBatch = getLatestBatch(captureSessionId);
|
|
8279
10005
|
capturePlan({
|
|
8280
|
-
sourcePath:
|
|
10006
|
+
sourcePath: planFilePath,
|
|
10007
|
+
projectRoot,
|
|
8281
10008
|
content: planContent,
|
|
8282
10009
|
sessionId: captureSessionId,
|
|
8283
|
-
promptBatchId: latestBatch?.id ?? null
|
|
10010
|
+
promptBatchId: latestBatch?.id ?? null,
|
|
10011
|
+
logger
|
|
8284
10012
|
});
|
|
8285
10013
|
logger.info(LOG_KINDS.CAPTURE_PLAN, "Plan captured", {
|
|
8286
10014
|
session_id: captureSessionId,
|
|
@@ -8440,8 +10168,8 @@ function createConfigReactionRegistry(logger) {
|
|
|
8440
10168
|
function shouldFire(registeredPaths, touched) {
|
|
8441
10169
|
if (registeredPaths.length === 0) return true;
|
|
8442
10170
|
for (const prefix of registeredPaths) {
|
|
8443
|
-
for (const
|
|
8444
|
-
if (
|
|
10171
|
+
for (const path25 of touched) {
|
|
10172
|
+
if (path25 === prefix || path25.startsWith(`${prefix}.`)) return true;
|
|
8445
10173
|
}
|
|
8446
10174
|
}
|
|
8447
10175
|
return false;
|
|
@@ -8475,13 +10203,13 @@ function createPlanWatchReaction(deps) {
|
|
|
8475
10203
|
}
|
|
8476
10204
|
|
|
8477
10205
|
// src/daemon/main.ts
|
|
8478
|
-
import
|
|
8479
|
-
import
|
|
10206
|
+
import fs24 from "fs";
|
|
10207
|
+
import path24 from "path";
|
|
8480
10208
|
function killStaleDaemon(vaultDir, logger) {
|
|
8481
|
-
const daemonJsonPath =
|
|
10209
|
+
const daemonJsonPath = path24.join(vaultDir, "daemon.json");
|
|
8482
10210
|
try {
|
|
8483
|
-
if (!
|
|
8484
|
-
const info = JSON.parse(
|
|
10211
|
+
if (!fs24.existsSync(daemonJsonPath)) return;
|
|
10212
|
+
const info = JSON.parse(fs24.readFileSync(daemonJsonPath, "utf-8"));
|
|
8485
10213
|
if (!info.pid) return;
|
|
8486
10214
|
if (info.pid === process.pid) return;
|
|
8487
10215
|
try {
|
|
@@ -8490,7 +10218,7 @@ function killStaleDaemon(vaultDir, logger) {
|
|
|
8490
10218
|
logger.info(LOG_KINDS.DAEMON_START, "Killed stale daemon", { pid: info.pid });
|
|
8491
10219
|
} catch {
|
|
8492
10220
|
}
|
|
8493
|
-
|
|
10221
|
+
fs24.unlinkSync(daemonJsonPath);
|
|
8494
10222
|
} catch {
|
|
8495
10223
|
}
|
|
8496
10224
|
}
|
|
@@ -8500,7 +10228,7 @@ async function main() {
|
|
|
8500
10228
|
process.stderr.write("Usage: mycod --vault <path>\n");
|
|
8501
10229
|
process.exit(1);
|
|
8502
10230
|
}
|
|
8503
|
-
const vaultDir =
|
|
10231
|
+
const vaultDir = path24.resolve(vaultArg);
|
|
8504
10232
|
loadSecrets(vaultDir);
|
|
8505
10233
|
const config = loadMergedConfig(vaultDir);
|
|
8506
10234
|
const liveConfig = { current: config };
|
|
@@ -8513,7 +10241,7 @@ async function main() {
|
|
|
8513
10241
|
projectRoot,
|
|
8514
10242
|
extensions: config.capture.artifact_extensions
|
|
8515
10243
|
};
|
|
8516
|
-
const logger = new DaemonLogger(
|
|
10244
|
+
const logger = new DaemonLogger(path24.join(vaultDir, "logs"), {
|
|
8517
10245
|
level: config.daemon.log_level
|
|
8518
10246
|
});
|
|
8519
10247
|
if (config.daemon.log_level === "debug") {
|
|
@@ -8542,7 +10270,7 @@ async function main() {
|
|
|
8542
10270
|
const devCliEntry = detectDevBuild(
|
|
8543
10271
|
globalPrefix,
|
|
8544
10272
|
process.argv[1],
|
|
8545
|
-
|
|
10273
|
+
fs24.realpathSync
|
|
8546
10274
|
);
|
|
8547
10275
|
if (devCliEntry) {
|
|
8548
10276
|
setDevBuildCliEntry(devCliEntry);
|
|
@@ -8554,13 +10282,19 @@ async function main() {
|
|
|
8554
10282
|
const db = initDatabase(vaultDbPath(vaultDir));
|
|
8555
10283
|
createSchema(db, machineId);
|
|
8556
10284
|
registerBuiltinDomains();
|
|
10285
|
+
const interruptedRuns = markRunningRunsInterrupted("Daemon restarted before the run completed");
|
|
10286
|
+
if (interruptedRuns > 0) {
|
|
10287
|
+
logger.warn(LOG_KINDS.AGENT_RUN, "Marked stale running runs as resumable after daemon restart", {
|
|
10288
|
+
count: interruptedRuns
|
|
10289
|
+
});
|
|
10290
|
+
}
|
|
8557
10291
|
logger.info(LOG_KINDS.DAEMON_START, "SQLite initialized", { vault: vaultDir });
|
|
8558
10292
|
{
|
|
8559
|
-
const reasonPath =
|
|
10293
|
+
const reasonPath = path24.join(vaultDir, RESTART_REASON_FILENAME);
|
|
8560
10294
|
try {
|
|
8561
|
-
if (
|
|
8562
|
-
const raw = JSON.parse(
|
|
8563
|
-
|
|
10295
|
+
if (fs24.existsSync(reasonPath)) {
|
|
10296
|
+
const raw = JSON.parse(fs24.readFileSync(reasonPath, "utf-8"));
|
|
10297
|
+
fs24.unlinkSync(reasonPath);
|
|
8564
10298
|
if (raw.reason === "version_sync" && raw.to_version) {
|
|
8565
10299
|
const message = raw.local_update_ran ? "Restarted and updated local project hooks." : "Restarted to pick up the latest version.";
|
|
8566
10300
|
notify(vaultDir, {
|
|
@@ -8602,13 +10336,13 @@ async function main() {
|
|
|
8602
10336
|
});
|
|
8603
10337
|
const lastLogTimestamp = getMaxTimestamp();
|
|
8604
10338
|
if (lastLogTimestamp) {
|
|
8605
|
-
const logDir =
|
|
10339
|
+
const logDir = path24.join(vaultDir, "logs");
|
|
8606
10340
|
const replayedCount = reconcileLogBuffer(logDir, lastLogTimestamp);
|
|
8607
10341
|
if (replayedCount > 0) {
|
|
8608
10342
|
logger.info(LOG_KINDS.DAEMON_RECONCILE, `Replayed ${replayedCount} log entries from buffer`, { replayed: replayedCount });
|
|
8609
10343
|
}
|
|
8610
10344
|
}
|
|
8611
|
-
const vectorsDbPath =
|
|
10345
|
+
const vectorsDbPath = path24.join(vaultDir, "vectors.db");
|
|
8612
10346
|
const vectorStore = new SqliteVecVectorStore(vectorsDbPath);
|
|
8613
10347
|
const llmProvider = createEmbeddingProvider(config.embedding);
|
|
8614
10348
|
const embeddingProvider = new EmbeddingProviderAdapter(llmProvider, config.embedding);
|
|
@@ -8618,7 +10352,7 @@ async function main() {
|
|
|
8618
10352
|
const databaseManager = new DatabaseMaintenanceManager(vaultDbPath(vaultDir), vaultDir, logger);
|
|
8619
10353
|
let definitionsDir;
|
|
8620
10354
|
try {
|
|
8621
|
-
const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-
|
|
10355
|
+
const { registerBuiltInAgentsAndTasks, resolveDefinitionsDir: resolveDefinitionsDir2 } = await import("./loader-NAVVZK63.js");
|
|
8622
10356
|
definitionsDir = resolveDefinitionsDir2();
|
|
8623
10357
|
await registerBuiltInAgentsAndTasks(definitionsDir, vaultDir);
|
|
8624
10358
|
logger.info(LOG_KINDS.AGENT_TASK, "Built-in agents and tasks registered");
|
|
@@ -8655,10 +10389,10 @@ async function main() {
|
|
|
8655
10389
|
}
|
|
8656
10390
|
let uiDir = null;
|
|
8657
10391
|
{
|
|
8658
|
-
const root = findPackageRoot(
|
|
10392
|
+
const root = findPackageRoot(path24.dirname(new URL(import.meta.url).pathname));
|
|
8659
10393
|
if (root) {
|
|
8660
|
-
const candidate =
|
|
8661
|
-
if (
|
|
10394
|
+
const candidate = path24.join(root, "dist", "ui");
|
|
10395
|
+
if (fs24.existsSync(candidate)) uiDir = candidate;
|
|
8662
10396
|
}
|
|
8663
10397
|
}
|
|
8664
10398
|
if (uiDir) {
|
|
@@ -8672,6 +10406,7 @@ async function main() {
|
|
|
8672
10406
|
sleepIntervalMs: POWER_SLEEP_INTERVAL_MS,
|
|
8673
10407
|
logger
|
|
8674
10408
|
});
|
|
10409
|
+
const inflightRuns = new InflightRunRegistry();
|
|
8675
10410
|
const server = new DaemonServer({
|
|
8676
10411
|
vaultDir,
|
|
8677
10412
|
logger,
|
|
@@ -8691,7 +10426,7 @@ async function main() {
|
|
|
8691
10426
|
(p) => createPerProjectAdapter(p, claudeCodeAdapter.parseTurns)
|
|
8692
10427
|
)
|
|
8693
10428
|
});
|
|
8694
|
-
const bufferDir =
|
|
10429
|
+
const bufferDir = path24.join(vaultDir, "buffer");
|
|
8695
10430
|
const sessionBuffers = /* @__PURE__ */ new Map();
|
|
8696
10431
|
const reconciler = createReconciler({ bufferDir, logger });
|
|
8697
10432
|
reconciler.runStartupReconciliation();
|
|
@@ -8703,7 +10438,8 @@ async function main() {
|
|
|
8703
10438
|
logger,
|
|
8704
10439
|
liveConfig,
|
|
8705
10440
|
vaultDir,
|
|
8706
|
-
planTags: symbiontPlanTags
|
|
10441
|
+
planTags: symbiontPlanTags,
|
|
10442
|
+
planWatchConfig
|
|
8707
10443
|
});
|
|
8708
10444
|
const sessionLifecycle = createSessionLifecycleHandlers({
|
|
8709
10445
|
registry,
|
|
@@ -8733,14 +10469,32 @@ async function main() {
|
|
|
8733
10469
|
});
|
|
8734
10470
|
server.registerRoute("POST", "/events", eventDispatcher);
|
|
8735
10471
|
server.registerRoute("POST", "/events/stop", stopProcessor.handleStopRoute);
|
|
8736
|
-
|
|
10472
|
+
let teamSync;
|
|
10473
|
+
const contextDeps = {
|
|
10474
|
+
vaultDir,
|
|
10475
|
+
embeddingManager,
|
|
10476
|
+
liveConfig,
|
|
10477
|
+
logger,
|
|
10478
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10479
|
+
};
|
|
8737
10480
|
server.registerRoute("POST", "/context", createSessionContextHandler(contextDeps));
|
|
8738
10481
|
server.registerRoute("POST", "/context/resume", createResumeContextHandler(contextDeps));
|
|
8739
10482
|
server.registerRoute("POST", "/context/prompt", createPromptContextHandler(contextDeps));
|
|
8740
10483
|
const progressTracker = new ProgressTracker();
|
|
8741
10484
|
let configHash = computeConfigHash(vaultDir);
|
|
10485
|
+
const cortexHandlers = createCortexHandlers(vaultDir, {
|
|
10486
|
+
liveConfig,
|
|
10487
|
+
embeddingManager,
|
|
10488
|
+
logger,
|
|
10489
|
+
getTeamClient: () => teamSync.getTeamClient(),
|
|
10490
|
+
registerInflightRun: (p) => inflightRuns.register(p)
|
|
10491
|
+
});
|
|
8742
10492
|
server.registerRoute("GET", "/api/config", async () => handleGetConfig(vaultDir));
|
|
8743
10493
|
server.registerRoute("GET", "/api/symbionts", async () => handleListSymbionts(vaultDir));
|
|
10494
|
+
server.registerRoute("GET", "/api/cortex/instructions", cortexHandlers.handleGetInstructions);
|
|
10495
|
+
server.registerRoute("POST", "/api/cortex/instructions/refresh", cortexHandlers.handleRefreshInstructions);
|
|
10496
|
+
server.registerRoute("POST", "/api/cortex/prompt-builder", cortexHandlers.handleBuildPrompt);
|
|
10497
|
+
server.registerRoute("GET", "/api/cortex/prompt-builder/:runId", cortexHandlers.handleGetPromptResult);
|
|
8744
10498
|
server.registerRoute("GET", "/api/config/merged", async () => handleGetMergedConfig(vaultDir));
|
|
8745
10499
|
server.registerRoute("GET", "/api/config/local", async () => handleGetLocalConfig(vaultDir));
|
|
8746
10500
|
const symbiontPlanDirsByAgent = {};
|
|
@@ -8756,7 +10510,7 @@ async function main() {
|
|
|
8756
10510
|
liveConfig.current = ctx;
|
|
8757
10511
|
});
|
|
8758
10512
|
reactions.on(["capture", "symbionts"], (ctx) => {
|
|
8759
|
-
reconcileConfiguredSymbionts(
|
|
10513
|
+
reconcileConfiguredSymbionts(path24.dirname(vaultDir), vaultDir, ctx);
|
|
8760
10514
|
});
|
|
8761
10515
|
reactions.on(["capture"], createPlanWatchReaction({
|
|
8762
10516
|
symbiontPlanDirs,
|
|
@@ -8771,7 +10525,14 @@ async function main() {
|
|
|
8771
10525
|
}
|
|
8772
10526
|
});
|
|
8773
10527
|
async function syncScheduledTasks() {
|
|
8774
|
-
await registerScheduledTasks(powerManager, {
|
|
10528
|
+
await registerScheduledTasks(powerManager, {
|
|
10529
|
+
definitionsDir,
|
|
10530
|
+
vaultDir,
|
|
10531
|
+
embeddingManager,
|
|
10532
|
+
logger,
|
|
10533
|
+
liveConfig,
|
|
10534
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10535
|
+
});
|
|
8775
10536
|
}
|
|
8776
10537
|
reactions.on(["agent.tasks"], async () => {
|
|
8777
10538
|
await syncScheduledTasks();
|
|
@@ -8825,7 +10586,7 @@ async function main() {
|
|
|
8825
10586
|
server.registerRoute("POST", "/api/log", createLogIngestionHandler(logger));
|
|
8826
10587
|
server.registerRoute("GET", "/api/models", async (req) => handleGetModels(req));
|
|
8827
10588
|
server.registerRoute("POST", "/api/restart", async (req) => handleRestart({ vaultDir, progressTracker }, req.body));
|
|
8828
|
-
const updateProjectRoot =
|
|
10589
|
+
const updateProjectRoot = path24.dirname(vaultDir);
|
|
8829
10590
|
const updateHandlers = createUpdateHandlers({
|
|
8830
10591
|
vaultDir,
|
|
8831
10592
|
projectRoot: updateProjectRoot,
|
|
@@ -8848,6 +10609,7 @@ async function main() {
|
|
|
8848
10609
|
server.registerRoute("GET", "/api/sessions/:id/impact", sessionMutations.handleGetSessionImpact);
|
|
8849
10610
|
server.registerRoute("POST", "/api/sessions/:id/complete", sessionMutations.handleCompleteSession);
|
|
8850
10611
|
server.registerRoute("DELETE", "/api/sessions/:id", sessionMutations.handleDeleteSession);
|
|
10612
|
+
server.registerRoute("DELETE", "/api/plans/:id", sessionMutations.handleDeletePlan);
|
|
8851
10613
|
server.registerRoute("GET", "/api/sessions/:id/batches", handleGetSessionBatches);
|
|
8852
10614
|
server.registerRoute("GET", "/api/batches/:id/activities", handleGetBatchActivities);
|
|
8853
10615
|
server.registerRoute("GET", "/api/sessions/:id/attachments", handleGetSessionAttachments);
|
|
@@ -8868,12 +10630,27 @@ async function main() {
|
|
|
8868
10630
|
server.registerRoute("GET", "/api/digest", handleGetDigest);
|
|
8869
10631
|
const attachments = createAttachmentHandler({ vaultDir });
|
|
8870
10632
|
server.registerRoute("GET", "/api/attachments/:filename", attachments.handleGetAttachment);
|
|
8871
|
-
const agentRunHandlers = createAgentRunHandlers({
|
|
10633
|
+
const agentRunHandlers = createAgentRunHandlers({
|
|
10634
|
+
vaultDir,
|
|
10635
|
+
embeddingManager,
|
|
10636
|
+
logger,
|
|
10637
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10638
|
+
});
|
|
8872
10639
|
server.registerRoute("POST", "/api/agent/run", agentRunHandlers.handleRun);
|
|
8873
10640
|
server.registerRoute("GET", "/api/agent/runs", agentRunHandlers.handleListRuns);
|
|
8874
10641
|
server.registerRoute("GET", "/api/agent/runs/:id", agentRunHandlers.handleGetRun);
|
|
10642
|
+
server.registerRoute("POST", "/api/agent/runs/:id/resume", agentRunHandlers.handleResumeRun);
|
|
8875
10643
|
server.registerRoute("GET", "/api/agent/runs/:id/reports", agentRunHandlers.handleGetRunReports);
|
|
8876
10644
|
server.registerRoute("GET", "/api/agent/runs/:id/turns", agentRunHandlers.handleGetRunTurns);
|
|
10645
|
+
server.registerRoute("GET", "/api/agent/runs/:id/write-intents", agentRunHandlers.handleGetRunWriteIntents);
|
|
10646
|
+
server.registerRoute("GET", "/api/agent/runs/:id/audit", agentRunHandlers.handleGetRunAudit);
|
|
10647
|
+
const agentEvalHandlers = createAgentEvaluationHandlers({ vaultDir, embeddingManager, logger });
|
|
10648
|
+
server.registerRoute("POST", "/api/agent/evaluations", agentEvalHandlers.handleCreate);
|
|
10649
|
+
server.registerRoute("GET", "/api/agent/evaluations", agentEvalHandlers.handleList);
|
|
10650
|
+
server.registerRoute("GET", "/api/agent/evaluations/:id", agentEvalHandlers.handleGet);
|
|
10651
|
+
const digestRevisionHandlers = createDigestRevisionHandlers({ vaultDir, logger });
|
|
10652
|
+
server.registerRoute("GET", "/api/digest/revisions", digestRevisionHandlers.handleList);
|
|
10653
|
+
server.registerRoute("POST", "/api/digest/revisions/:id/restore", digestRevisionHandlers.handleRestore);
|
|
8877
10654
|
server.registerRoute("GET", "/api/agent/tasks", async (req) => handleListTasks(req, vaultDir));
|
|
8878
10655
|
server.registerRoute("GET", "/api/agent/tasks/:id", async (req) => handleGetTask(req, vaultDir));
|
|
8879
10656
|
server.registerRoute("GET", "/api/agent/tasks/:id/yaml", async (req) => handleGetTaskYaml(req, vaultDir));
|
|
@@ -8915,11 +10692,15 @@ async function main() {
|
|
|
8915
10692
|
});
|
|
8916
10693
|
server.registerRoute("GET", "/api/providers", async () => handleGetProviders());
|
|
8917
10694
|
server.registerRoute("POST", "/api/providers/test", async (req) => handleTestProvider(req));
|
|
8918
|
-
|
|
10695
|
+
server.registerRoute("GET", "/api/providers/secrets", async () => handleGetProviderSecrets(vaultDir));
|
|
10696
|
+
server.registerRoute("PUT", "/api/providers/secrets/:provider", async (req) => handlePutProviderSecret(vaultDir, req));
|
|
10697
|
+
server.registerRoute("DELETE", "/api/providers/secrets/:provider", async (req) => handleDeleteProviderSecret(vaultDir, req));
|
|
10698
|
+
const mcpProxy = createMcpProxyHandlers({ machineId, embeddingManager, projectRoot, logger });
|
|
8919
10699
|
server.registerRoute("POST", "/api/mcp/remember", mcpProxy.handleRemember);
|
|
8920
10700
|
server.registerRoute("POST", "/api/mcp/supersede", mcpProxy.handleSupersede);
|
|
8921
10701
|
server.registerRoute("POST", "/api/mcp/consolidate", mcpProxy.handleConsolidate);
|
|
8922
10702
|
server.registerRoute("GET", "/api/mcp/plans", mcpProxy.handlePlans);
|
|
10703
|
+
server.registerRoute("POST", "/api/mcp/plans", mcpProxy.handleSavePlan);
|
|
8923
10704
|
server.registerRoute("GET", "/api/mcp/sessions", mcpProxy.handleSessions);
|
|
8924
10705
|
server.registerRoute("GET", "/api/mcp/team", mcpProxy.handleTeam);
|
|
8925
10706
|
const backupHandlers = createBackupHandlers({ db, machineId, vaultDir, liveConfig });
|
|
@@ -8936,7 +10717,7 @@ async function main() {
|
|
|
8936
10717
|
}
|
|
8937
10718
|
return result;
|
|
8938
10719
|
});
|
|
8939
|
-
|
|
10720
|
+
teamSync = initTeamSync({ liveConfig, machineId, logger, vaultDir, serverVersion: server.version });
|
|
8940
10721
|
reactions.on(["team"], async () => {
|
|
8941
10722
|
await teamSync.reconcileClient();
|
|
8942
10723
|
});
|
|
@@ -8945,7 +10726,7 @@ async function main() {
|
|
|
8945
10726
|
vaultDir,
|
|
8946
10727
|
machineId,
|
|
8947
10728
|
logger,
|
|
8948
|
-
getTeamClient: teamSync.getTeamClient,
|
|
10729
|
+
getTeamClient: () => teamSync.getTeamClient(),
|
|
8949
10730
|
setTeamClient: teamSync.setTeamClient
|
|
8950
10731
|
});
|
|
8951
10732
|
server.registerRoute("POST", "/api/team/connect", async (req) => {
|
|
@@ -8968,14 +10749,14 @@ async function main() {
|
|
|
8968
10749
|
server.registerRoute("POST", "/api/team/upgrade-worker", teamHandlers.handleUpgradeWorker);
|
|
8969
10750
|
server.registerRoute("POST", "/api/team/rotate-mcp-token", teamHandlers.handleRotateMcpToken);
|
|
8970
10751
|
const collectiveHandlers = createCollectiveHandlers({
|
|
8971
|
-
getTeamClient: teamSync.getTeamClient
|
|
10752
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
8972
10753
|
});
|
|
8973
10754
|
server.registerRoute("GET", "/api/collective/status", collectiveHandlers.handleStatus);
|
|
8974
10755
|
server.registerRoute("GET", "/api/collective/search", collectiveHandlers.handleSearch);
|
|
8975
10756
|
server.registerRoute("GET", "/api/collective/projects", collectiveHandlers.handleProjects);
|
|
8976
10757
|
server.registerRoute("GET", "/api/collective/project", collectiveHandlers.handleProject);
|
|
8977
10758
|
server.registerRoute("GET", "/api/collective/settings", collectiveHandlers.handleSettings);
|
|
8978
|
-
server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: teamSync.getTeamClient, machineId }));
|
|
10759
|
+
server.registerRoute("GET", "/api/search", createSearchHandler({ embeddingManager, getTeamClient: () => teamSync.getTeamClient(), machineId }));
|
|
8979
10760
|
server.registerRoute("GET", "/api/activity", handleGetFeed);
|
|
8980
10761
|
server.registerRoute("GET", "/api/embedding/status", async () => handleGetEmbeddingStatus(vaultDir));
|
|
8981
10762
|
server.registerRoute("GET", "/api/embedding/details", async () => handleEmbeddingDetails(embeddingManager));
|
|
@@ -9013,9 +10794,25 @@ async function main() {
|
|
|
9013
10794
|
logger.warn(LOG_KINDS.DAEMON_CONFIG, "Failed to persist auto-derived port", { error: err.message });
|
|
9014
10795
|
}
|
|
9015
10796
|
}
|
|
9016
|
-
registerPowerJobs(powerManager, {
|
|
10797
|
+
registerPowerJobs(powerManager, {
|
|
10798
|
+
embeddingManager,
|
|
10799
|
+
registry,
|
|
10800
|
+
logger,
|
|
10801
|
+
liveConfig,
|
|
10802
|
+
db,
|
|
10803
|
+
machineId,
|
|
10804
|
+
vaultDir,
|
|
10805
|
+
databaseManager
|
|
10806
|
+
});
|
|
9017
10807
|
teamSync.registerFlushJob(powerManager);
|
|
9018
|
-
await registerScheduledTasks(powerManager, {
|
|
10808
|
+
await registerScheduledTasks(powerManager, {
|
|
10809
|
+
definitionsDir,
|
|
10810
|
+
vaultDir,
|
|
10811
|
+
embeddingManager,
|
|
10812
|
+
logger,
|
|
10813
|
+
liveConfig,
|
|
10814
|
+
getTeamClient: () => teamSync.getTeamClient()
|
|
10815
|
+
});
|
|
9019
10816
|
powerManager.start();
|
|
9020
10817
|
const shutdown = async (signal) => {
|
|
9021
10818
|
logger.info(LOG_KINDS.DAEMON_START, `${signal} received`);
|
|
@@ -9025,6 +10822,17 @@ async function main() {
|
|
|
9025
10822
|
logger.info(LOG_KINDS.DAEMON_START, "Waiting for active stop processing to complete...");
|
|
9026
10823
|
await activeStopProcessing;
|
|
9027
10824
|
}
|
|
10825
|
+
if (inflightRuns.size > 0) {
|
|
10826
|
+
logger.info(LOG_KINDS.DAEMON_START, "Draining in-flight agent runs before shutdown...", {
|
|
10827
|
+
inflight_count: inflightRuns.size
|
|
10828
|
+
});
|
|
10829
|
+
const outcome = await inflightRuns.drain();
|
|
10830
|
+
if (!outcome.settled) {
|
|
10831
|
+
logger.warn(LOG_KINDS.DAEMON_START, "Some in-flight runs did not settle before shutdown timeout", {
|
|
10832
|
+
remaining: outcome.remaining
|
|
10833
|
+
});
|
|
10834
|
+
}
|
|
10835
|
+
}
|
|
9028
10836
|
registry.destroy();
|
|
9029
10837
|
await server.stop();
|
|
9030
10838
|
vectorStore.close();
|
|
@@ -9047,4 +10855,4 @@ export {
|
|
|
9047
10855
|
handleUserPrompt,
|
|
9048
10856
|
main
|
|
9049
10857
|
};
|
|
9050
|
-
//# sourceMappingURL=main-
|
|
10858
|
+
//# sourceMappingURL=main-5PRQNEEE.js.map
|