@goondocks/myco 0.21.0 → 0.21.2

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.
Files changed (157) hide show
  1. package/bin/myco-run +68 -7
  2. package/dist/{agent-eval-RJSQI5S2.js → agent-eval-2MQKTXX3.js} +7 -6
  3. package/dist/{agent-eval-RJSQI5S2.js.map → agent-eval-2MQKTXX3.js.map} +1 -1
  4. package/dist/{agent-run-2JSYFOKU.js → agent-run-XJBTSVJR.js} +5 -5
  5. package/dist/{agent-tasks-APFJIM2T.js → agent-tasks-7MWBZOC7.js} +5 -5
  6. package/dist/{chunk-75Z7UKDY.js → chunk-4D22KTXY.js} +2 -2
  7. package/dist/{chunk-P66DLD6G.js → chunk-6FBLL7MD.js} +8 -2
  8. package/dist/chunk-6FBLL7MD.js.map +1 -0
  9. package/dist/{chunk-JZS6GZ6T.js → chunk-AUIXX33A.js} +10 -3
  10. package/dist/chunk-AUIXX33A.js.map +1 -0
  11. package/dist/{chunk-F3OEQYLS.js → chunk-DBBO6FHE.js} +33 -30
  12. package/dist/{chunk-F3OEQYLS.js.map → chunk-DBBO6FHE.js.map} +1 -1
  13. package/dist/{chunk-CESKJD44.js → chunk-DMPCC7V6.js} +19 -11
  14. package/dist/chunk-DMPCC7V6.js.map +1 -0
  15. package/dist/{chunk-RL5R4CQU.js → chunk-DTWUHHFI.js} +39 -2
  16. package/dist/{chunk-RL5R4CQU.js.map → chunk-DTWUHHFI.js.map} +1 -1
  17. package/dist/{chunk-XL75KZGI.js → chunk-EKZG2MCD.js} +7 -3
  18. package/dist/chunk-EKZG2MCD.js.map +1 -0
  19. package/dist/{chunk-NGH7U6A3.js → chunk-HCT7RMM2.js} +487 -98
  20. package/dist/chunk-HCT7RMM2.js.map +1 -0
  21. package/dist/{chunk-G6QIBNZM.js → chunk-IMW5TJ3O.js} +7 -6
  22. package/dist/chunk-IMW5TJ3O.js.map +1 -0
  23. package/dist/chunk-LQIPXVDH.js +17 -0
  24. package/dist/chunk-LQIPXVDH.js.map +1 -0
  25. package/dist/{chunk-5ZG4RMUH.js → chunk-N2DGFACQ.js} +2 -2
  26. package/dist/{chunk-VHNRMM4O.js → chunk-OTQH5KZW.js} +87 -37
  27. package/dist/chunk-OTQH5KZW.js.map +1 -0
  28. package/dist/{chunk-6LB7XELY.js → chunk-QATYARI5.js} +15 -13
  29. package/dist/chunk-QATYARI5.js.map +1 -0
  30. package/dist/{chunk-LVIY7P35.js → chunk-QLLBJEM7.js} +5 -1
  31. package/dist/chunk-QLLBJEM7.js.map +1 -0
  32. package/dist/{chunk-DJ3IHNYO.js → chunk-TFRUDNLI.js} +2 -2
  33. package/dist/{chunk-R2JIJBCL.js → chunk-TMAXWERS.js} +87 -4
  34. package/dist/chunk-TMAXWERS.js.map +1 -0
  35. package/dist/chunk-TSM6VESW.js +25 -0
  36. package/dist/chunk-TSM6VESW.js.map +1 -0
  37. package/dist/{chunk-ILJPRYES.js → chunk-USVFEWYL.js} +2 -2
  38. package/dist/{chunk-JR54LTPP.js → chunk-W5L5IHP5.js} +3 -3
  39. package/dist/{chunk-BUTL6IFS.js → chunk-Z55WGA2J.js} +2 -2
  40. package/dist/{chunk-NGROSFOH.js → chunk-Z66IT5KL.js} +14 -9
  41. package/dist/chunk-Z66IT5KL.js.map +1 -0
  42. package/dist/{cli-LNYSTDQM.js → cli-DDHTHU2J.js} +37 -37
  43. package/dist/{client-NWE4TCNO.js → client-PQU53UQU.js} +5 -3
  44. package/dist/{detect-PXNM6TA7.js → detect-7NUD5B5R.js} +2 -2
  45. package/dist/{doctor-TI7EZ3RW.js → doctor-QK6KFY6H.js} +6 -6
  46. package/dist/{executor-F2YU7HXJ.js → executor-FJCMNSXM.js} +11 -10
  47. package/dist/{init-KG3TYVGE.js → init-GQPD6HHX.js} +9 -9
  48. package/dist/{installer-UMH7OJ5A.js → installer-N4UTEACX.js} +2 -2
  49. package/dist/{loader-NAVVZK63.js → loader-UDNUMEDA.js} +3 -2
  50. package/dist/{main-5PRQNEEE.js → main-4HKTZFIM.js} +469 -187
  51. package/dist/main-4HKTZFIM.js.map +1 -0
  52. package/dist/{open-5A27BCSB.js → open-3P3DDAOA.js} +5 -5
  53. package/dist/{post-compact-USAODKPQ.js → post-compact-QA5LME2J.js} +7 -7
  54. package/dist/{post-tool-use-GMMSYBII.js → post-tool-use-QRZMPNYL.js} +6 -6
  55. package/dist/{post-tool-use-failure-NZVSL2PO.js → post-tool-use-failure-XNHIKBZG.js} +7 -7
  56. package/dist/{pre-compact-LZ57DLUS.js → pre-compact-HDV6X5QM.js} +7 -7
  57. package/dist/{registry-M2Z5QBWH.js → registry-F3THYC5M.js} +4 -3
  58. package/dist/{remove-T3KE6C5N.js → remove-USQDLGTJ.js} +7 -7
  59. package/dist/{restart-YWDEVZUJ.js → restart-FQLZE2TW.js} +6 -6
  60. package/dist/{search-GKFDGELR.js → search-5COKV6TD.js} +6 -6
  61. package/dist/{server-AHUR6CWF.js → server-KRMBRW4T.js} +23 -7
  62. package/dist/{server-AHUR6CWF.js.map → server-KRMBRW4T.js.map} +1 -1
  63. package/dist/{session-2ZEPLWW6.js → session-NJCUW3OX.js} +5 -5
  64. package/dist/{session-end-LWJYQAXX.js → session-end-XD27GRYF.js} +6 -6
  65. package/dist/{session-start-WTA6GCOQ.js → session-start-RDTXUSYL.js} +11 -11
  66. package/dist/{setup-llm-E7UU5IO7.js → setup-llm-FYPPJI6W.js} +5 -5
  67. package/dist/src/agent/definitions/tasks/cortex-instructions.yaml +63 -41
  68. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +178 -22
  69. package/dist/src/agent/definitions/tasks/skill-generate.yaml +20 -6
  70. package/dist/src/agent/definitions/tasks/vault-evolve.yaml +65 -55
  71. package/dist/src/cli.js +1 -1
  72. package/dist/src/daemon/main.js +1 -1
  73. package/dist/src/hooks/post-tool-use.js +1 -1
  74. package/dist/src/hooks/session-end.js +1 -1
  75. package/dist/src/hooks/session-start.js +1 -1
  76. package/dist/src/hooks/stop.js +1 -1
  77. package/dist/src/hooks/user-prompt-submit.js +1 -1
  78. package/dist/src/mcp/server.js +1 -1
  79. package/dist/src/symbionts/manifests/opencode.yaml +7 -0
  80. package/dist/src/symbionts/templates/agents-starter.md +1 -1
  81. package/dist/src/symbionts/templates/opencode/plugin.ts +41 -1
  82. package/dist/src/symbionts/templates/pi/plugin.ts +12 -1
  83. package/dist/{stats-DFG6S23S.js → stats-JCLZLA5G.js} +6 -6
  84. package/dist/{stop-WRBTXEVT.js → stop-B7XCXEM5.js} +6 -6
  85. package/dist/{stop-failure-32MGIG2Q.js → stop-failure-R6QZCUOZ.js} +7 -7
  86. package/dist/{subagent-start-VFGHQFVL.js → subagent-start-N7A622F3.js} +7 -7
  87. package/dist/{subagent-stop-663FXG3P.js → subagent-stop-SVOG5MZJ.js} +7 -7
  88. package/dist/{task-completed-ZCQYEFMZ.js → task-completed-3DL5LJXF.js} +7 -7
  89. package/dist/{team-JTI5CDUO.js → team-VJ3M263F.js} +3 -3
  90. package/dist/ui/assets/{index-DGf1h-Ha.js → index-O1kNWlWM.js} +119 -119
  91. package/dist/ui/assets/index-z2Jm8i4A.css +1 -0
  92. package/dist/ui/index.html +2 -2
  93. package/dist/{update-3NBQTG32.js → update-TVXAUJMZ.js} +45 -11
  94. package/dist/update-TVXAUJMZ.js.map +1 -0
  95. package/dist/{user-prompt-submit-ME2TBKOS.js → user-prompt-submit-KYO2VGLB.js} +10 -9
  96. package/dist/user-prompt-submit-KYO2VGLB.js.map +1 -0
  97. package/dist/{version-GQAFBBPX.js → version-LDFEALUJ.js} +2 -2
  98. package/package.json +1 -1
  99. package/skills/myco-rules/SKILL.md +94 -0
  100. package/skills/{rules → myco-rules}/references/rules-bad-example.md +1 -1
  101. package/skills/{rules → myco-rules}/references/rules-good-example.md +1 -1
  102. package/dist/chunk-6LB7XELY.js.map +0 -1
  103. package/dist/chunk-CESKJD44.js.map +0 -1
  104. package/dist/chunk-CUDIZJY7.js +0 -36
  105. package/dist/chunk-CUDIZJY7.js.map +0 -1
  106. package/dist/chunk-G6QIBNZM.js.map +0 -1
  107. package/dist/chunk-JZS6GZ6T.js.map +0 -1
  108. package/dist/chunk-LVIY7P35.js.map +0 -1
  109. package/dist/chunk-NGH7U6A3.js.map +0 -1
  110. package/dist/chunk-NGROSFOH.js.map +0 -1
  111. package/dist/chunk-P66DLD6G.js.map +0 -1
  112. package/dist/chunk-R2JIJBCL.js.map +0 -1
  113. package/dist/chunk-VHNRMM4O.js.map +0 -1
  114. package/dist/chunk-XL75KZGI.js.map +0 -1
  115. package/dist/main-5PRQNEEE.js.map +0 -1
  116. package/dist/ui/assets/index-_OP4ifzH.css +0 -1
  117. package/dist/update-3NBQTG32.js.map +0 -1
  118. package/dist/user-prompt-submit-ME2TBKOS.js.map +0 -1
  119. package/skills/myco-curate/SKILL.md +0 -86
  120. package/skills/rules/SKILL.md +0 -214
  121. /package/dist/{agent-run-2JSYFOKU.js.map → agent-run-XJBTSVJR.js.map} +0 -0
  122. /package/dist/{agent-tasks-APFJIM2T.js.map → agent-tasks-7MWBZOC7.js.map} +0 -0
  123. /package/dist/{chunk-75Z7UKDY.js.map → chunk-4D22KTXY.js.map} +0 -0
  124. /package/dist/{chunk-5ZG4RMUH.js.map → chunk-N2DGFACQ.js.map} +0 -0
  125. /package/dist/{chunk-DJ3IHNYO.js.map → chunk-TFRUDNLI.js.map} +0 -0
  126. /package/dist/{chunk-ILJPRYES.js.map → chunk-USVFEWYL.js.map} +0 -0
  127. /package/dist/{chunk-JR54LTPP.js.map → chunk-W5L5IHP5.js.map} +0 -0
  128. /package/dist/{chunk-BUTL6IFS.js.map → chunk-Z55WGA2J.js.map} +0 -0
  129. /package/dist/{cli-LNYSTDQM.js.map → cli-DDHTHU2J.js.map} +0 -0
  130. /package/dist/{client-NWE4TCNO.js.map → client-PQU53UQU.js.map} +0 -0
  131. /package/dist/{detect-PXNM6TA7.js.map → detect-7NUD5B5R.js.map} +0 -0
  132. /package/dist/{doctor-TI7EZ3RW.js.map → doctor-QK6KFY6H.js.map} +0 -0
  133. /package/dist/{executor-F2YU7HXJ.js.map → executor-FJCMNSXM.js.map} +0 -0
  134. /package/dist/{init-KG3TYVGE.js.map → init-GQPD6HHX.js.map} +0 -0
  135. /package/dist/{installer-UMH7OJ5A.js.map → installer-N4UTEACX.js.map} +0 -0
  136. /package/dist/{loader-NAVVZK63.js.map → loader-UDNUMEDA.js.map} +0 -0
  137. /package/dist/{open-5A27BCSB.js.map → open-3P3DDAOA.js.map} +0 -0
  138. /package/dist/{post-compact-USAODKPQ.js.map → post-compact-QA5LME2J.js.map} +0 -0
  139. /package/dist/{post-tool-use-GMMSYBII.js.map → post-tool-use-QRZMPNYL.js.map} +0 -0
  140. /package/dist/{post-tool-use-failure-NZVSL2PO.js.map → post-tool-use-failure-XNHIKBZG.js.map} +0 -0
  141. /package/dist/{pre-compact-LZ57DLUS.js.map → pre-compact-HDV6X5QM.js.map} +0 -0
  142. /package/dist/{registry-M2Z5QBWH.js.map → registry-F3THYC5M.js.map} +0 -0
  143. /package/dist/{remove-T3KE6C5N.js.map → remove-USQDLGTJ.js.map} +0 -0
  144. /package/dist/{restart-YWDEVZUJ.js.map → restart-FQLZE2TW.js.map} +0 -0
  145. /package/dist/{search-GKFDGELR.js.map → search-5COKV6TD.js.map} +0 -0
  146. /package/dist/{session-2ZEPLWW6.js.map → session-NJCUW3OX.js.map} +0 -0
  147. /package/dist/{session-end-LWJYQAXX.js.map → session-end-XD27GRYF.js.map} +0 -0
  148. /package/dist/{session-start-WTA6GCOQ.js.map → session-start-RDTXUSYL.js.map} +0 -0
  149. /package/dist/{setup-llm-E7UU5IO7.js.map → setup-llm-FYPPJI6W.js.map} +0 -0
  150. /package/dist/{stats-DFG6S23S.js.map → stats-JCLZLA5G.js.map} +0 -0
  151. /package/dist/{stop-WRBTXEVT.js.map → stop-B7XCXEM5.js.map} +0 -0
  152. /package/dist/{stop-failure-32MGIG2Q.js.map → stop-failure-R6QZCUOZ.js.map} +0 -0
  153. /package/dist/{subagent-start-VFGHQFVL.js.map → subagent-start-N7A622F3.js.map} +0 -0
  154. /package/dist/{subagent-stop-663FXG3P.js.map → subagent-stop-SVOG5MZJ.js.map} +0 -0
  155. /package/dist/{task-completed-ZCQYEFMZ.js.map → task-completed-3DL5LJXF.js.map} +0 -0
  156. /package/dist/{team-JTI5CDUO.js.map → team-VJ3M263F.js.map} +0 -0
  157. /package/dist/{version-GQAFBBPX.js.map → version-LDFEALUJ.js.map} +0 -0
@@ -8,21 +8,22 @@ import {
8
8
  buildRunAccountingUpdate,
9
9
  buildUsageData,
10
10
  checkpointResultsForResume,
11
- errorMessage,
12
11
  inferRuntimeFromProviderType,
12
+ isExpiredSessionError,
13
13
  isSessionResumeFailure,
14
14
  parseCheckpointState,
15
15
  resolveProviderForResume,
16
16
  serializeCheckpointState,
17
17
  summarizePhaseCosts
18
- } from "./chunk-6LB7XELY.js";
18
+ } from "./chunk-QATYARI5.js";
19
19
  import {
20
20
  fullTextSearch,
21
- hydrateSearchResults
22
- } from "./chunk-LVIY7P35.js";
21
+ hydrateSearchResults,
22
+ sanitizeFtsQuery
23
+ } from "./chunk-QLLBJEM7.js";
23
24
  import {
24
25
  loadAllTasks
25
- } from "./chunk-ILJPRYES.js";
26
+ } from "./chunk-USVFEWYL.js";
26
27
  import {
27
28
  getAgent,
28
29
  getDefaultTask,
@@ -31,7 +32,10 @@ import {
31
32
  loadSystemPrompt,
32
33
  resolveDefinitionsDir,
33
34
  resolveEffectiveConfig
34
- } from "./chunk-JZS6GZ6T.js";
35
+ } from "./chunk-AUIXX33A.js";
36
+ import {
37
+ errorMessage
38
+ } from "./chunk-LQIPXVDH.js";
35
39
  import {
36
40
  countToolCallsByRun,
37
41
  insertTurn,
@@ -59,12 +63,11 @@ import {
59
63
  getSpore,
60
64
  insertSpore,
61
65
  listDigestExtracts,
62
- listSporeIdsSince,
63
66
  listSpores,
64
67
  updateSporeStatus,
65
68
  upsertCortexInstructions,
66
69
  upsertDigestExtract
67
- } from "./chunk-F3OEQYLS.js";
70
+ } from "./chunk-DBBO6FHE.js";
68
71
  import {
69
72
  countSessions,
70
73
  getActiveSessionIds,
@@ -78,7 +81,7 @@ import {
78
81
  AGENT_SETTABLE_STATUSES,
79
82
  CANDIDATE_STATUS,
80
83
  createSchema
81
- } from "./chunk-RL5R4CQU.js";
84
+ } from "./chunk-DTWUHHFI.js";
82
85
  import {
83
86
  loadMergedConfig
84
87
  } from "./chunk-53RPGOEN.js";
@@ -89,7 +92,7 @@ import {
89
92
  } from "./chunk-MYX5NCRH.js";
90
93
  import {
91
94
  getPluginVersion
92
- } from "./chunk-BUTL6IFS.js";
95
+ } from "./chunk-Z55WGA2J.js";
93
96
  import {
94
97
  findPackageRoot
95
98
  } from "./chunk-LPUQPDC2.js";
@@ -97,6 +100,7 @@ import {
97
100
  CONTENT_HASH_ALGORITHM,
98
101
  DEFAULT_AGENT_ID,
99
102
  DEFAULT_LIST_LIMIT,
103
+ DIGEST_TIERS,
100
104
  EDGE_TYPE_DERIVED_FROM,
101
105
  EDGE_TYPE_EXTRACTED_FROM,
102
106
  EDGE_TYPE_FROM_SESSION,
@@ -197,6 +201,7 @@ var STATUS_RUNNING = "running";
197
201
  var STATUS_COMPLETED = "completed";
198
202
  var STATUS_FAILED = "failed";
199
203
  var RESUME_STATUS_READY = "ready";
204
+ var RESUME_STATUS_SESSION_EXPIRED = "session_expired";
200
205
  var RUN_COLUMNS = [
201
206
  "id",
202
207
  "agent_id",
@@ -1251,6 +1256,32 @@ function deleteSkillRecordCascade(idOrName) {
1251
1256
  return { id: record.id, name: record.name };
1252
1257
  }
1253
1258
 
1259
+ // src/agent/semantic-shortlist.ts
1260
+ async function shortlistSemanticIds(options) {
1261
+ const {
1262
+ provider,
1263
+ namespace,
1264
+ query: query2,
1265
+ candidateIds,
1266
+ maxResults,
1267
+ overFetch,
1268
+ threshold,
1269
+ filters
1270
+ } = options;
1271
+ if (!provider || maxResults <= 0) return [];
1272
+ const queryVector = await provider.embedQuery(query2);
1273
+ if (!queryVector) return [];
1274
+ const results = provider.searchVectors(queryVector, {
1275
+ namespace,
1276
+ limit: Math.max(maxResults, maxResults * overFetch),
1277
+ threshold,
1278
+ filters
1279
+ });
1280
+ const shortlisted = candidateIds ? results.filter((result) => candidateIds.has(result.id)) : results;
1281
+ shortlisted.sort((a, b) => b.similarity - a.similarity);
1282
+ return shortlisted.slice(0, maxResults).map((result) => result.id);
1283
+ }
1284
+
1254
1285
  // src/agent/tools/skill-validator.ts
1255
1286
  var import_yaml = __toESM(require_dist(), 1);
1256
1287
  var MAX_SKILL_LINES = 800;
@@ -1706,7 +1737,57 @@ function buildSkillSurveyInstruction(agentId) {
1706
1737
  return { instruction: parts.join("\n") };
1707
1738
  }
1708
1739
  var SKILL_EVOLVE_DEFAULT_ASSESS_INTERVAL_HOURS = 24;
1709
- var SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN = 8;
1740
+ var SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN = 3;
1741
+ var SKILL_EVOLVE_RECENT_SPORE_SCAN_LIMIT = 40;
1742
+ var SKILL_EVOLVE_RELEVANT_SPORE_LIMIT = 10;
1743
+ var SKILL_EVOLVE_SEMANTIC_OVERFETCH = 4;
1744
+ var SKILL_EVOLVE_SEMANTIC_THRESHOLD = 0;
1745
+ function normalizeSkillName(name) {
1746
+ return name.replace(/[-_:]+/g, " ");
1747
+ }
1748
+ function buildSporeSearchText(spore) {
1749
+ return [spore.observation_type, spore.content, spore.context, spore.tags, spore.file_path].filter(Boolean).join(" ");
1750
+ }
1751
+ function selectRelevantSporeIdsByLexicalOverlap(skill, recentSpores) {
1752
+ if (recentSpores.length === 0) return [];
1753
+ const skillName = normalizeSkillName(skill.name);
1754
+ const skillQuery = `${skillName} ${skill.description}`;
1755
+ return recentSpores.map((spore) => {
1756
+ const sporeText = buildSporeSearchText(spore);
1757
+ const descriptionScore = descriptionSimilarity(skillQuery, sporeText);
1758
+ const nameScore = descriptionSimilarity(skillName, sporeText);
1759
+ const totalScore = descriptionScore + nameScore * 1.5;
1760
+ return { id: spore.id, totalScore, createdAt: spore.created_at, importance: spore.importance };
1761
+ }).filter((candidate) => candidate.totalScore > 0).sort((a, b) => {
1762
+ if (b.totalScore !== a.totalScore) return b.totalScore - a.totalScore;
1763
+ if (b.importance !== a.importance) return b.importance - a.importance;
1764
+ return b.createdAt - a.createdAt;
1765
+ }).slice(0, SKILL_EVOLVE_RELEVANT_SPORE_LIMIT).map((candidate) => candidate.id);
1766
+ }
1767
+ async function selectRelevantSporeIdsForSkill(skill, sinceEpoch, retrievalProvider) {
1768
+ const recentSpores = listSpores({
1769
+ status: "active",
1770
+ since: sinceEpoch,
1771
+ includeActive: false,
1772
+ limit: SKILL_EVOLVE_RECENT_SPORE_SCAN_LIMIT
1773
+ });
1774
+ if (recentSpores.length === 0) return [];
1775
+ const semanticIds = await shortlistSemanticIds({
1776
+ provider: retrievalProvider,
1777
+ namespace: "spores",
1778
+ query: `${normalizeSkillName(skill.name)} ${skill.description}`,
1779
+ candidateIds: new Set(recentSpores.map((spore) => spore.id)),
1780
+ maxResults: SKILL_EVOLVE_RELEVANT_SPORE_LIMIT,
1781
+ overFetch: SKILL_EVOLVE_SEMANTIC_OVERFETCH,
1782
+ threshold: SKILL_EVOLVE_SEMANTIC_THRESHOLD,
1783
+ filters: {
1784
+ status: "active",
1785
+ created_at_gte: sinceEpoch
1786
+ }
1787
+ });
1788
+ if (semanticIds.length > 0) return semanticIds;
1789
+ return selectRelevantSporeIdsByLexicalOverlap(skill, recentSpores);
1790
+ }
1710
1791
  var MIN_SECTIONS_FOR_STANDALONE = 2;
1711
1792
  function extractHeadings(content) {
1712
1793
  const bodyMatch = content.match(/^---[\s\S]*?---\n([\s\S]*)$/);
@@ -1733,7 +1814,7 @@ function headingOverlap(headingsA, headingsB) {
1733
1814
  const smaller = Math.min(headingsA.length, headingsB.length);
1734
1815
  return { score: smaller > 0 ? shared.length / smaller : 0, shared };
1735
1816
  }
1736
- function buildSkillEvolveInstruction(params, projectRoot, similarityProvider) {
1817
+ async function buildSkillEvolveInstruction(params, projectRoot, retrievalProvider) {
1737
1818
  const assessIntervalHours = Number(params?.assess_interval_hours ?? SKILL_EVOLVE_DEFAULT_ASSESS_INTERVAL_HOURS);
1738
1819
  const maxSkillsPerRun = Number(params?.max_skills_per_run ?? SKILL_EVOLVE_DEFAULT_MAX_SKILLS_PER_RUN);
1739
1820
  const now = epochSeconds();
@@ -1750,20 +1831,20 @@ function buildSkillEvolveInstruction(params, projectRoot, similarityProvider) {
1750
1831
  const lastAssessedAt = typeof props.last_assessed_at === "number" ? props.last_assessed_at : 0;
1751
1832
  const knowledgeWatermark = typeof props.knowledge_watermark === "number" ? props.knowledge_watermark : 0;
1752
1833
  if (lastAssessedAt > 0 && now - lastAssessedAt < intervalSeconds) continue;
1753
- const newSporeIds = listSporeIdsSince(knowledgeWatermark, 10);
1834
+ const newSporeIds = await selectRelevantSporeIdsForSkill(skill, knowledgeWatermark, retrievalProvider);
1754
1835
  if (newSporeIds.length === 0) continue;
1755
1836
  needsAssessment.push({
1756
1837
  id: skill.id,
1757
1838
  name: skill.name,
1758
1839
  generation: skill.generation,
1759
1840
  description: skill.description,
1760
- newSporeIds
1841
+ newSporeIds,
1842
+ lastAssessedAt
1761
1843
  });
1762
- if (needsAssessment.length >= maxSkillsPerRun) {
1763
- break;
1764
- }
1765
1844
  }
1766
- if (needsAssessment.length === 0) {
1845
+ needsAssessment.sort((a, b) => a.lastAssessedAt - b.lastAssessedAt);
1846
+ const selectedSkills = needsAssessment.slice(0, maxSkillsPerRun);
1847
+ if (selectedSkills.length === 0) {
1767
1848
  return "No skills need assessment. All active skills are current or were recently assessed. Report skip via vault_report and finish.";
1768
1849
  }
1769
1850
  const structures = [];
@@ -1809,11 +1890,11 @@ function buildSkillEvolveInstruction(params, projectRoot, similarityProvider) {
1809
1890
  }
1810
1891
  }
1811
1892
  const parts = [
1812
- `${needsAssessment.length} skill(s) need assessment.`,
1893
+ `${selectedSkills.length} skill(s) need assessment.`,
1813
1894
  `assess_interval_hours: ${assessIntervalHours}`,
1814
1895
  `max_skills_per_run: ${maxSkillsPerRun}`
1815
1896
  ];
1816
- for (const skill of needsAssessment) {
1897
+ for (const skill of selectedSkills) {
1817
1898
  parts.push("");
1818
1899
  parts.push("---");
1819
1900
  parts.push(`## Skill: ${skill.name} (gen ${skill.generation})`);
@@ -1852,10 +1933,10 @@ function buildSkillEvolveInstruction(params, projectRoot, similarityProvider) {
1852
1933
  }
1853
1934
  }
1854
1935
  }
1855
- if (similarityProvider) {
1936
+ if (retrievalProvider) {
1856
1937
  const idToName = new Map(allSkills.map((s) => [s.id, s.name]));
1857
1938
  try {
1858
- const semanticPairs = similarityProvider.pairwiseSimilarity("skill_records", 0.65);
1939
+ const semanticPairs = retrievalProvider.pairwiseSimilarity("skill_records", 0.65);
1859
1940
  if (semanticPairs.length > 0) {
1860
1941
  parts.push("");
1861
1942
  parts.push("## Semantic Similarity (embedding cosine distance)");
@@ -1872,14 +1953,14 @@ function buildSkillEvolveInstruction(params, projectRoot, similarityProvider) {
1872
1953
  }
1873
1954
  return parts.join("\n");
1874
1955
  }
1875
- async function buildTaskInstruction(taskName, taskParams, agentId, projectRoot, similarityProvider, config, getTeamClient) {
1956
+ async function buildTaskInstruction(taskName, taskParams, agentId, projectRoot, retrievalProvider, config, getTeamClient) {
1876
1957
  switch (taskName) {
1877
1958
  case SKILL_GENERATE_TASK:
1878
1959
  return buildSkillGenerateInstruction();
1879
1960
  case SKILL_SURVEY_TASK:
1880
1961
  return agentId ? buildSkillSurveyInstruction(agentId) : void 0;
1881
1962
  case SKILL_EVOLVE_TASK: {
1882
- const instruction = buildSkillEvolveInstruction(taskParams, projectRoot, similarityProvider);
1963
+ const instruction = await buildSkillEvolveInstruction(taskParams, projectRoot, retrievalProvider);
1883
1964
  return instruction ? { instruction } : void 0;
1884
1965
  }
1885
1966
  case CORTEX_INSTRUCTIONS_TASK: {
@@ -2403,6 +2484,7 @@ var ORCHESTRATOR_GUIDANCE_HEADER = "## Orchestrator Guidance";
2403
2484
  var NO_CONTEXT_QUERIES_TEXT = "No context queries configured.";
2404
2485
  var FALLBACK_REASONING_PARSE_ERROR = "Orchestrator response could not be parsed \u2014 running all phases with defaults.";
2405
2486
  var FALLBACK_REASONING_MISSING_PHASES = "Orchestrator plan missing phases array \u2014 running all phases with defaults.";
2487
+ var ORCHESTRATOR_PARSE_ERROR_PREVIEW_CHARS = 200;
2406
2488
  var PLACEHOLDER_VAULT_STATE = "{{vault_state}}";
2407
2489
  var PLACEHOLDER_PHASE_DEFINITIONS = "{{phase_definitions}}";
2408
2490
  var PLACEHOLDER_CONTEXT_RESULTS = "{{context_results}}";
@@ -2436,7 +2518,7 @@ function composeOrchestratorPrompt(vaultState, phases, contextResults) {
2436
2518
  const contextSection = formatContextResults(contextResults);
2437
2519
  return template.replace(PLACEHOLDER_VAULT_STATE, vaultState).replace(PLACEHOLDER_PHASE_DEFINITIONS, phaseList).replace(PLACEHOLDER_CONTEXT_RESULTS, contextSection);
2438
2520
  }
2439
- function parseOrchestratorPlan(response, phases) {
2521
+ function parseOrchestratorPlan(response, phases, logger) {
2440
2522
  const trimmed = response.trim();
2441
2523
  if (!trimmed) {
2442
2524
  return buildRunAllPlan(phases, FALLBACK_REASONING_PARSE_ERROR);
@@ -2447,8 +2529,14 @@ function parseOrchestratorPlan(response, phases) {
2447
2529
  return buildRunAllPlan(phases, FALLBACK_REASONING_MISSING_PHASES);
2448
2530
  }
2449
2531
  return parsed;
2450
- } catch {
2451
- return buildRunAllPlan(phases, FALLBACK_REASONING_PARSE_ERROR);
2532
+ } catch (err) {
2533
+ const detail = errorMessage(err);
2534
+ const truncated = detail.length > ORCHESTRATOR_PARSE_ERROR_PREVIEW_CHARS ? `${detail.slice(0, ORCHESTRATOR_PARSE_ERROR_PREVIEW_CHARS)}\u2026` : detail;
2535
+ logger?.warn("agent.orchestrator.parse-failed", "Orchestrator plan parse failed", {
2536
+ error: detail,
2537
+ responsePreview: trimmed.slice(0, 200)
2538
+ });
2539
+ return buildRunAllPlan(phases, `${FALLBACK_REASONING_PARSE_ERROR} (${truncated})`);
2452
2540
  }
2453
2541
  }
2454
2542
  function applyDirectives(phases, directives) {
@@ -3055,6 +3143,27 @@ function countWriteIntentsByToolForEvaluation(evaluationId) {
3055
3143
  // src/agent/tools/read-tools.ts
3056
3144
  import { tool } from "@anthropic-ai/claude-agent-sdk";
3057
3145
 
3146
+ // src/semantic-search-filters.ts
3147
+ function hasSemanticSearchFilters(filters) {
3148
+ if (!filters) return false;
3149
+ return Object.values(filters).some((value) => value !== void 0);
3150
+ }
3151
+ function matchesSemanticSearchFilters(metadata, filters) {
3152
+ if (!filters) return true;
3153
+ if (filters.status !== void 0 && metadata?.status !== filters.status) return false;
3154
+ if (filters.session_id !== void 0 && metadata?.session_id !== filters.session_id) return false;
3155
+ if (filters.observation_type !== void 0 && metadata?.observation_type !== filters.observation_type) return false;
3156
+ if (filters.project_root !== void 0 && metadata?.project_root !== filters.project_root) return false;
3157
+ if (filters.name !== void 0 && metadata?.name !== filters.name) return false;
3158
+ if (filters.source_path !== void 0 && metadata?.source_path !== filters.source_path) return false;
3159
+ const createdAt = typeof metadata?.created_at === "number" ? metadata.created_at : void 0;
3160
+ if (filters.created_at_gte !== void 0 && (createdAt === void 0 || createdAt < filters.created_at_gte)) return false;
3161
+ if (filters.created_at_lte !== void 0 && (createdAt === void 0 || createdAt > filters.created_at_lte)) return false;
3162
+ if (filters.created_at_gt !== void 0 && (createdAt === void 0 || createdAt <= filters.created_at_gt)) return false;
3163
+ if (filters.created_at_lt !== void 0 && (createdAt === void 0 || createdAt >= filters.created_at_lt)) return false;
3164
+ return true;
3165
+ }
3166
+
3058
3167
  // src/db/queries/graph-edges.ts
3059
3168
  import crypto3 from "crypto";
3060
3169
  var DEFAULT_BFS_DEPTH = 2;
@@ -3355,23 +3464,36 @@ function createReadTools(deps) {
3355
3464
  );
3356
3465
  const vaultSearchFts = tool(
3357
3466
  "vault_search_fts",
3358
- "Full-text search across sessions, spores, prompt batches, and activities using FTS5. Best for finding exact keywords, file paths, function names, and specific text. Results from in-flight sessions are hidden by default so intelligence tasks only see settled work; pass include_active=true to bypass.",
3467
+ 'Full-text search across sessions, spores, prompt batches, and activities using FTS5. Best for finding exact keywords, file paths, function names, and specific text. You can pass natural-language queries containing hyphens, slashes, dots, etc. \u2014 the tool auto-quotes tokens with non-word characters so file paths and hyphenated identifiers (e.g., "skill-evolve-inventory", "packages/myco/src/loader.ts") are treated as literal phrases instead of being rejected by FTS5 MATCH syntax. Plain alphanumeric/underscore tokens are left unquoted so they AND together normally. Results from in-flight sessions are hidden by default so intelligence tasks only see settled work; pass include_active=true to bypass.',
3359
3468
  {
3360
- query: external_exports.string().describe("Search query text"),
3469
+ query: external_exports.string().describe("Search query text. Special characters (hyphens, slashes, dots, colons) are auto-quoted \u2014 no manual escaping needed."),
3361
3470
  type: external_exports.string().optional().describe("Restrict to a result type (session, spore, prompt_batch, activity)"),
3362
3471
  limit: external_exports.number().optional().describe("Maximum number of results to return"),
3363
3472
  include_active: external_exports.boolean().optional().describe("Include results from sessions still in active status (default: false)")
3364
3473
  },
3365
3474
  async (args) => {
3475
+ const sanitizedQuery = sanitizeFtsQuery(args.query);
3366
3476
  try {
3367
- const results = fullTextSearch(args.query, {
3477
+ const results = fullTextSearch(sanitizedQuery, {
3368
3478
  type: args.type,
3369
3479
  limit: args.limit ?? DEFAULT_SEARCH_LIMIT,
3370
3480
  includeActive: args.include_active === true
3371
3481
  });
3372
- return textResult({ results });
3373
- } catch {
3374
- return textResult({ results: [], message: "Search unavailable" });
3482
+ return textResult({ results, sanitized_query: sanitizedQuery !== args.query ? sanitizedQuery : void 0 });
3483
+ } catch (err) {
3484
+ const message = errorMessage(err);
3485
+ let hint;
3486
+ if (/no such table|no such module: fts5/i.test(message)) {
3487
+ hint = "FTS index missing \u2014 the searched table may not have an FTS5 mirror. Check the type parameter.";
3488
+ } else if (/syntax error|malformed MATCH/i.test(message)) {
3489
+ hint = 'FTS5 query syntax rejected. Quote special characters (e.g., wrap file paths in double quotes: "packages/myco/src/loader.ts").';
3490
+ }
3491
+ return textResult({
3492
+ error: `vault_search_fts failed: ${message}${hint ? ` \u2014 ${hint}` : ""}`,
3493
+ results: [],
3494
+ query: args.query,
3495
+ type: args.type ?? null
3496
+ });
3375
3497
  }
3376
3498
  },
3377
3499
  { annotations: { readOnlyHint: true } }
@@ -3383,7 +3505,12 @@ function createReadTools(deps) {
3383
3505
  query: external_exports.string().describe("Search query text"),
3384
3506
  namespace: external_exports.string().optional().describe("Restrict to a content type: spores, sessions, plans, artifacts, skill_records. Omit to search all."),
3385
3507
  limit: external_exports.number().optional().describe("Maximum results to return"),
3386
- include_active: external_exports.boolean().optional().describe("Include results from sessions still in active status (default: false)")
3508
+ include_active: external_exports.boolean().optional().describe("Include results from sessions still in active status (default: false)"),
3509
+ status: external_exports.string().optional().describe("Optional metadata filter, e.g. active/superseded for spores and skill_records."),
3510
+ session_id: external_exports.string().optional().describe("Optional metadata filter for a linked session id."),
3511
+ observation_type: external_exports.string().optional().describe("Optional metadata filter for spore observation type."),
3512
+ since: external_exports.number().optional().describe("Optional created_at lower bound (epoch seconds)."),
3513
+ until: external_exports.number().optional().describe("Optional created_at upper bound (epoch seconds).")
3387
3514
  },
3388
3515
  async (args) => {
3389
3516
  if (!embeddingManager) {
@@ -3397,21 +3524,39 @@ function createReadTools(deps) {
3397
3524
  const searchLimit = args.limit ?? DEFAULT_SEARCH_LIMIT;
3398
3525
  const excludeActive = args.include_active !== true;
3399
3526
  const activeIds = excludeActive ? getActiveSessionIds() : /* @__PURE__ */ new Set();
3527
+ const metadataFilters = {
3528
+ ...args.status !== void 0 ? { status: args.status } : {},
3529
+ ...args.session_id !== void 0 ? { session_id: args.session_id } : {},
3530
+ ...args.observation_type !== void 0 ? { observation_type: args.observation_type } : {},
3531
+ ...args.since !== void 0 ? { created_at_gte: args.since } : {},
3532
+ ...args.until !== void 0 ? { created_at_lte: args.until } : {}
3533
+ };
3534
+ const vectorFilters = hasSemanticSearchFilters(metadataFilters) ? metadataFilters : void 0;
3400
3535
  const [rawLocalResults, teamResults] = await Promise.all([
3401
3536
  Promise.resolve(
3402
3537
  embeddingManager.searchVectors(queryVector, {
3403
3538
  namespace: args.namespace,
3404
3539
  limit: searchLimit,
3405
- threshold: SEARCH_SIMILARITY_THRESHOLD
3540
+ threshold: SEARCH_SIMILARITY_THRESHOLD,
3541
+ filters: vectorFilters
3406
3542
  }).map((r) => ({ ...r, source: "local" }))
3407
3543
  ),
3408
- teamClient ? teamClient.search(args.query, { limit: searchLimit }).then((res) => res.results.map((r) => ({ ...r, source: `${TEAM_SOURCE_PREFIX}${r.machine_id}` }))).catch(() => []) : Promise.resolve([])
3544
+ teamClient ? teamClient.search(args.query, {
3545
+ limit: searchLimit,
3546
+ tables: args.namespace ? [args.namespace] : void 0,
3547
+ status: args.status,
3548
+ observation_type: args.observation_type,
3549
+ since: args.since,
3550
+ until: args.until,
3551
+ session_id: args.session_id
3552
+ }).then((res) => res.results.map((r) => ({ ...r, source: `${TEAM_SOURCE_PREFIX}${r.machine_id}` }))).catch(() => []) : Promise.resolve([])
3409
3553
  ]);
3410
3554
  const localResults = activeIds.size > 0 ? rawLocalResults.filter((r) => {
3411
3555
  const sid = r.metadata?.session_id;
3412
3556
  return typeof sid !== "string" || !activeIds.has(sid);
3413
3557
  }) : rawLocalResults;
3414
- const hydratedLocalResults = hydrateSearchResults(localResults).map((r) => ({
3558
+ const filteredLocalResults = vectorFilters ? localResults.filter((r) => matchesSemanticSearchFilters(r.metadata, metadataFilters)) : localResults;
3559
+ const hydratedLocalResults = hydrateSearchResults(filteredLocalResults).map((r) => ({
3415
3560
  ...r,
3416
3561
  source: "local"
3417
3562
  }));
@@ -3422,13 +3567,33 @@ function createReadTools(deps) {
3422
3567
  return typeof sid !== "string" || !activeIds.has(sid);
3423
3568
  });
3424
3569
  }
3570
+ if (vectorFilters) {
3571
+ dedupedTeam = dedupedTeam.filter((r) => matchesSemanticSearchFilters(
3572
+ r.metadata,
3573
+ metadataFilters
3574
+ ));
3575
+ }
3425
3576
  const merged = [
3426
3577
  ...hydratedLocalResults,
3427
3578
  ...dedupedTeam
3428
3579
  ].sort((a, b) => (b.score ?? 0) - (a.score ?? 0)).slice(0, searchLimit);
3429
3580
  return textResult({ results: merged });
3430
- } catch {
3431
- return textResult({ results: [], message: "Semantic search unavailable" });
3581
+ } catch (err) {
3582
+ const message = errorMessage(err);
3583
+ let hint;
3584
+ if (/timeout|ETIMEDOUT|AbortError/i.test(message)) {
3585
+ hint = "Embedding provider timed out. Retry once or reduce query length.";
3586
+ } else if (/ECONNREFUSED|fetch failed|network/i.test(message)) {
3587
+ hint = "Embedding provider unreachable. Check that the configured provider (Ollama/LM Studio/Anthropic) is running.";
3588
+ } else if (/no such table|no such column/i.test(message)) {
3589
+ hint = "Vector table missing or schema mismatch. Run `myco rebuild` or check migrations.";
3590
+ }
3591
+ return textResult({
3592
+ error: `vault_search_semantic failed: ${message}${hint ? ` \u2014 ${hint}` : ""}`,
3593
+ results: [],
3594
+ query: args.query,
3595
+ namespace: args.namespace ?? null
3596
+ });
3432
3597
  }
3433
3598
  },
3434
3599
  { annotations: { readOnlyHint: true, openWorldHint: true } }
@@ -3636,7 +3801,8 @@ function createWriteTools(deps) {
3636
3801
  embeddingManager?.onContentWritten("spores", spore.id, args.content, {
3637
3802
  status: "active",
3638
3803
  observation_type: args.observation_type,
3639
- session_id: args.session_id
3804
+ session_id: args.session_id,
3805
+ created_at: now
3640
3806
  }).catch(() => {
3641
3807
  });
3642
3808
  return textResult(spore);
@@ -3726,12 +3892,54 @@ function createWriteTools(deps) {
3726
3892
  );
3727
3893
  const vaultReadDigest = tool2(
3728
3894
  "vault_read_digest",
3729
- "Read current digest extracts. Without a tier parameter, returns a summary of all tiers (content length, generation time). With a tier parameter, returns the full content for that specific tier.",
3895
+ 'Read current digest extracts. Three modes: (1) no args \u2192 summary metadata for all tiers; (2) tier \u2192 full content for that tier; (3) pick: "rotate_oldest" \u2192 the digest-rotation decision, returning the tier whose generated_at is oldest plus its full content (or skip:true when all tiers are fresher than min_staleness_seconds). The rotation mode lets callers outsource the "which tier should we update this run" choice to a deterministic tool decision instead of prompting the LLM to compare timestamps.',
3730
3896
  {
3731
- tier: external_exports.number().optional().describe("Specific tier to read in full (e.g., 1500, 5000, 10000). Omit to get summary of all tiers.")
3897
+ tier: external_exports.number().optional().describe("Specific tier to read in full (e.g., 1500, 5000, 10000). Omit to get summary of all tiers. Ignored when pick is set."),
3898
+ pick: external_exports.enum(["rotate_oldest"]).optional().describe('Rotation mode. "rotate_oldest" picks the tier with the oldest generated_at (missing tiers count as never-generated and sort first). The response includes the selected tier, its full content, a rotation_reason explaining the choice, and metadata for all tiers so the caller can audit the decision.'),
3899
+ min_staleness_seconds: external_exports.number().optional().describe(`Used with pick="rotate_oldest". If every tier's generated_at is newer than (now - min_staleness_seconds), the tool returns {skip: true, reason, all_tiers} instead of selecting a tier. Defaults to 0 (never skip).`)
3732
3900
  },
3733
3901
  async (args) => {
3734
3902
  const extracts = listDigestExtracts(agentId);
3903
+ if (args.pick === "rotate_oldest") {
3904
+ const now = epochSeconds();
3905
+ const canonical = DIGEST_TIERS.map((tier) => {
3906
+ const existing = extracts.find((e) => e.tier === tier);
3907
+ return {
3908
+ tier,
3909
+ generated_at: existing?.generated_at ?? 0,
3910
+ content_length: existing?.content.length ?? 0,
3911
+ content: existing?.content ?? null
3912
+ };
3913
+ });
3914
+ const minStale = args.min_staleness_seconds ?? 0;
3915
+ if (minStale > 0) {
3916
+ const allPresent = canonical.every((t) => t.generated_at > 0);
3917
+ const cutoff = now - minStale;
3918
+ const allFresh = canonical.every((t) => t.generated_at > cutoff);
3919
+ if (allPresent && allFresh) {
3920
+ return textResult({
3921
+ mode: "rotate_oldest",
3922
+ skip: true,
3923
+ reason: `All ${canonical.length} tiers were generated within the last ${minStale} seconds (cutoff=${cutoff}).`,
3924
+ all_tiers: canonical.map((t) => ({ tier: t.tier, generated_at: t.generated_at, content_length: t.content_length }))
3925
+ });
3926
+ }
3927
+ }
3928
+ const sorted = [...canonical].sort((a, b) => {
3929
+ if (a.generated_at !== b.generated_at) return a.generated_at - b.generated_at;
3930
+ return b.tier - a.tier;
3931
+ });
3932
+ const selected = sorted[0];
3933
+ const reason = selected.generated_at === 0 ? `Tier ${selected.tier} has never been generated \u2014 seeding.` : `Tier ${selected.tier} has the oldest generated_at (${selected.generated_at}); next oldest is tier ${sorted[1]?.tier} at ${sorted[1]?.generated_at}.`;
3934
+ return textResult({
3935
+ mode: "rotate_oldest",
3936
+ selected_tier: selected.tier,
3937
+ selected_generated_at: selected.generated_at,
3938
+ selected_content: selected.content,
3939
+ rotation_reason: reason,
3940
+ all_tiers: canonical.map((t) => ({ tier: t.tier, generated_at: t.generated_at, content_length: t.content_length }))
3941
+ });
3942
+ }
3735
3943
  if (args.tier !== void 0) {
3736
3944
  const extract = extracts.find((e) => e.tier === args.tier);
3737
3945
  if (!extract) return textResult({ tier: args.tier, content: null, message: "No digest at this tier" });
@@ -4131,7 +4339,7 @@ function createSkillTools(deps) {
4131
4339
  );
4132
4340
  }
4133
4341
  try {
4134
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4342
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4135
4343
  syncSkillSymlinks(root, params.name, { remove: true });
4136
4344
  } catch (rollbackErr) {
4137
4345
  console.warn(
@@ -4149,7 +4357,7 @@ function createSkillTools(deps) {
4149
4357
  };
4150
4358
  }
4151
4359
  try {
4152
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4360
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4153
4361
  syncSkillSymlinks(root, params.name);
4154
4362
  } catch (err) {
4155
4363
  console.warn(
@@ -4351,14 +4559,15 @@ function createSkillTools(deps) {
4351
4559
  );
4352
4560
  const vaultSkillRecords = tool4(
4353
4561
  "vault_skill_records",
4354
- "Read, update, and delete skill records (materialized skills on disk). Supports list, get, update, and delete actions. The get action includes the full SKILL.md file content.",
4562
+ "Read, update, and delete skill records (materialized skills on disk). Supports list, get, update, and delete actions. The get action includes the full SKILL.md file content. For update, at least one mutating field (status, generation, source_ids, description, or properties) is required \u2014 calls with only {action, id} are rejected to prevent silent no-op updates.",
4355
4563
  {
4356
4564
  action: external_exports.enum(["list", "get", "update", "delete"]).describe("Action to perform"),
4357
4565
  id: external_exports.string().optional().describe("Skill record ID or name (required for get/update/delete)"),
4358
- status: external_exports.enum(["active", "stale", "retired"]).optional().describe("Filter by status"),
4566
+ status: external_exports.enum(["active", "stale", "retired"]).optional().describe("Filter by status or new status (for update)"),
4359
4567
  generation: external_exports.number().optional().describe("New generation number (for update)"),
4360
4568
  source_ids: external_exports.string().optional().describe("JSON array of source IDs (for update)"),
4361
4569
  description: external_exports.string().optional().describe("Updated description (for update)"),
4570
+ properties: external_exports.string().optional().describe(`JSON-encoded object of properties to MERGE into the existing properties (for update). Example: '{"last_verified_at": 1776580022}'. Existing keys not included in the payload are preserved; included keys overwrite. Used by skill-evolve to persist watermarks like last_assessed_at, knowledge_watermark, last_verified_at, last_classification.`),
4362
4571
  limit: external_exports.number().optional().describe("Maximum records to return (for list)")
4363
4572
  },
4364
4573
  async (args) => {
@@ -4386,14 +4595,44 @@ function createSkillTools(deps) {
4386
4595
  }
4387
4596
  case "update": {
4388
4597
  if (!args.id) return textResult({ error: "id is required for update action" });
4598
+ const hasMutatingField = args.status !== void 0 || args.generation !== void 0 || args.source_ids !== void 0 || args.description !== void 0 || args.properties !== void 0;
4599
+ if (!hasMutatingField) {
4600
+ return textResult({
4601
+ error: "update action requires at least one mutating field (status, generation, source_ids, description, or properties). Calls with only {action, id} are rejected to prevent silent no-op updates."
4602
+ });
4603
+ }
4389
4604
  const existing = getSkillRecord(args.id) ?? getSkillRecordByName(args.id);
4390
4605
  if (!existing) return textResult({ error: `Skill record not found: ${args.id}` });
4606
+ let mergedProperties;
4607
+ if (args.properties !== void 0) {
4608
+ let incoming;
4609
+ try {
4610
+ const parsed = JSON.parse(args.properties);
4611
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
4612
+ return textResult({ error: "properties must be a JSON-encoded object (not null, array, or primitive)." });
4613
+ }
4614
+ incoming = parsed;
4615
+ } catch (err) {
4616
+ return textResult({ error: `properties is not valid JSON: ${errorMessage(err)}` });
4617
+ }
4618
+ let existingProps = {};
4619
+ try {
4620
+ const parsedExisting = JSON.parse(existing.properties || "{}");
4621
+ if (parsedExisting && typeof parsedExisting === "object" && !Array.isArray(parsedExisting)) {
4622
+ existingProps = parsedExisting;
4623
+ }
4624
+ } catch (err) {
4625
+ console.warn(`[vault_skill_records] Skill ${existing.id} has unparseable properties; treating as empty for merge. Error: ${errorMessage(err)}`);
4626
+ }
4627
+ mergedProperties = JSON.stringify({ ...existingProps, ...incoming });
4628
+ }
4391
4629
  const now = epochSeconds();
4392
4630
  const updated = updateSkillRecord(existing.id, {
4393
4631
  ...args.status !== void 0 ? { status: args.status } : {},
4394
4632
  ...args.generation !== void 0 ? { generation: args.generation } : {},
4395
4633
  ...args.source_ids !== void 0 ? { source_ids: args.source_ids } : {},
4396
4634
  ...args.description !== void 0 ? { description: args.description } : {},
4635
+ ...mergedProperties !== void 0 ? { properties: mergedProperties } : {},
4397
4636
  updated_at: now
4398
4637
  });
4399
4638
  if (!updated) return textResult({ error: `Failed to update skill record: ${existing.id}` });
@@ -4416,7 +4655,7 @@ function createSkillTools(deps) {
4416
4655
  console.warn("[vault_skill_records] Failed to remove skill directory:", err instanceof Error ? err.message : err);
4417
4656
  }
4418
4657
  try {
4419
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4658
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4420
4659
  syncSkillSymlinks(root, result.name, { remove: true });
4421
4660
  } catch (err) {
4422
4661
  console.warn("[vault_skill_records] Failed to remove symlinks:", err instanceof Error ? err.message : err);
@@ -4534,7 +4773,7 @@ function createSkillTools(deps) {
4534
4773
  return textResult({ error: `Failed to write skill file: ${err instanceof Error ? err.message : String(err)}` });
4535
4774
  }
4536
4775
  try {
4537
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4776
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4538
4777
  syncSkillSymlinks(root, args.name);
4539
4778
  } catch (err) {
4540
4779
  console.warn("[vault_write_skill] syncSkillSymlinks failed:", err instanceof Error ? err.message : err);
@@ -5116,19 +5355,34 @@ function truncateSummary(text) {
5116
5355
  if (!text) return null;
5117
5356
  return text.length > TOOL_OUTPUT_SUMMARY_LIMIT ? `${text.slice(0, TOOL_OUTPUT_SUMMARY_LIMIT - 1)}\u2026` : text;
5118
5357
  }
5358
+ var TOOL_ERROR_PREFIX = "[ERROR] ";
5359
+ function isErrorResult(result) {
5360
+ if (!result || typeof result !== "object") return false;
5361
+ const content = result.content;
5362
+ if (!Array.isArray(content) || content.length === 0) return false;
5363
+ const first = content[0];
5364
+ if (!first || first.type !== "text" || typeof first.text !== "string") return false;
5365
+ const trimmed = first.text.trimStart();
5366
+ if (!trimmed.startsWith("{")) return false;
5367
+ try {
5368
+ const parsed = JSON.parse(trimmed);
5369
+ return !!parsed && typeof parsed === "object" && "error" in parsed && parsed.error !== void 0;
5370
+ } catch {
5371
+ return false;
5372
+ }
5373
+ }
5119
5374
  function summarizeToolResult(result) {
5120
5375
  if (!result || typeof result !== "object") return null;
5121
5376
  const content = result.content;
5122
5377
  if (!Array.isArray(content) || content.length === 0) return null;
5123
5378
  const first = content[0];
5124
5379
  if (!first || first.type !== "text" || typeof first.text !== "string") return null;
5125
- return truncateSummary(first.text.replace(/\s+/g, " ").trim());
5380
+ const body = first.text.replace(/\s+/g, " ").trim();
5381
+ const prefix = isErrorResult(result) ? TOOL_ERROR_PREFIX : "";
5382
+ return truncateSummary(prefix + body);
5126
5383
  }
5127
5384
  function summarizeToolError(error) {
5128
- if (error instanceof Error && error.message) {
5129
- return truncateSummary(error.message) ?? "Tool failed";
5130
- }
5131
- return truncateSummary(String(error)) ?? "Tool failed";
5385
+ return truncateSummary(TOOL_ERROR_PREFIX + errorMessage(error)) ?? TOOL_ERROR_PREFIX + "Tool failed";
5132
5386
  }
5133
5387
  function buildRepeatedReadSuppressionResult(toolName, repeatedCalls) {
5134
5388
  return {
@@ -5265,6 +5519,7 @@ function createVaultTools(agentId, runId, options) {
5265
5519
  }
5266
5520
  function wrapToolWithAudit(toolDef) {
5267
5521
  const originalHandler = toolDef.handler;
5522
+ const declaredKeys = toolDef.inputSchema && typeof toolDef.inputSchema === "object" ? new Set(Object.keys(toolDef.inputSchema)) : /* @__PURE__ */ new Set();
5268
5523
  return {
5269
5524
  ...toolDef,
5270
5525
  handler: async (args, extra) => {
@@ -5272,6 +5527,28 @@ function createVaultTools(agentId, runId, options) {
5272
5527
  const priorIdenticalCalls = repeatedReadKey ? repeatedReadCounts.get(repeatedReadKey) ?? 0 : 0;
5273
5528
  const turnId = recordTurn(toolDef.name, args);
5274
5529
  try {
5530
+ if (declaredKeys.size > 0 && args && typeof args === "object") {
5531
+ const unknown = [];
5532
+ for (const key of Object.keys(args)) {
5533
+ if (!declaredKeys.has(key)) unknown.push(key);
5534
+ }
5535
+ if (unknown.length > 0) {
5536
+ const accepted = Array.from(declaredKeys).sort().join(", ");
5537
+ const result2 = textResult({
5538
+ error: `Unknown parameter(s) for ${toolDef.name}: ${unknown.join(", ")}. Accepted parameters: ${accepted}.`
5539
+ });
5540
+ if (turnId !== null) {
5541
+ try {
5542
+ updateTurn(turnId, {
5543
+ tool_output_summary: summarizeToolResult(result2),
5544
+ completed_at: epochSeconds()
5545
+ });
5546
+ } catch {
5547
+ }
5548
+ }
5549
+ return result2;
5550
+ }
5551
+ }
5275
5552
  if (priorIdenticalCalls >= REPEATED_READ_FAILURE_THRESHOLD) {
5276
5553
  throw new Error(
5277
5554
  `Repeated identical ${toolDef.name} reads detected (${priorIdenticalCalls + 1} calls). Reuse the prior result already in context and proceed to a write, report, or different query.`
@@ -5405,6 +5682,13 @@ function getIsolatedPluginCacheDir() {
5405
5682
  if (isolatedPluginCacheDir) return isolatedPluginCacheDir;
5406
5683
  const dir = path3.join(os.tmpdir(), `myco-agent-plugin-cache-${process.pid}`);
5407
5684
  fs3.mkdirSync(dir, { recursive: true });
5685
+ const manifestPath = path3.join(dir, "installed_plugins.json");
5686
+ if (!fs3.existsSync(manifestPath)) {
5687
+ fs3.writeFileSync(
5688
+ manifestPath,
5689
+ JSON.stringify({ version: 2, plugins: {} })
5690
+ );
5691
+ }
5408
5692
  isolatedPluginCacheDir = dir;
5409
5693
  return dir;
5410
5694
  }
@@ -5450,6 +5734,18 @@ var ClaudeSdkRuntime = class {
5450
5734
  // user hasn't explicitly overridden the cache dir themselves.
5451
5735
  CLAUDE_CODE_PLUGIN_CACHE_DIR: process.env.CLAUDE_CODE_PLUGIN_CACHE_DIR ?? getIsolatedPluginCacheDir()
5452
5736
  };
5737
+ if (input.logger) {
5738
+ const mcpToolNames = input.toolSurface.toolNames ?? (toolServer ? ["<full-vault-surface>"] : []);
5739
+ input.logger.debug("agent.runtime.request", "Agent runtime request", {
5740
+ runId: input.toolSurface.runId,
5741
+ agentId: input.toolSurface.agentId,
5742
+ model: input.model,
5743
+ mcpToolCount: mcpToolNames.length,
5744
+ mcpTools: mcpToolNames,
5745
+ pluginCacheDir: env.CLAUDE_CODE_PLUGIN_CACHE_DIR,
5746
+ sessionRef: input.sessionRef ?? null
5747
+ });
5748
+ }
5453
5749
  let finalText = "";
5454
5750
  let turnsUsed = 0;
5455
5751
  let inputTokens = 0;
@@ -5464,6 +5760,7 @@ var ClaudeSdkRuntime = class {
5464
5760
  tools: [],
5465
5761
  mcpServers: toolServer ? { [MCP_SERVER_NAME]: toolServer } : {},
5466
5762
  strictMcpConfig: true,
5763
+ settingSources: [],
5467
5764
  maxTurns: input.maxTurns,
5468
5765
  permissionMode: "bypassPermissions",
5469
5766
  allowDangerouslySkipPermissions: true,
@@ -5504,7 +5801,7 @@ var ClaudeSdkRuntime = class {
5504
5801
  } catch (err) {
5505
5802
  if (turnsUsed > 0 || inputTokens > 0 || outputTokens > 0) {
5506
5803
  throw new RuntimeExecutionError(
5507
- err instanceof Error ? err.message : String(err),
5804
+ errorMessage(err),
5508
5805
  { usage: buildUsage(), sessionRef: input.sessionRef },
5509
5806
  { cause: err }
5510
5807
  );
@@ -13069,7 +13366,17 @@ function getAgentRuntime(runtimeId) {
13069
13366
  // src/agent/phase-loop.ts
13070
13367
  async function executePhase(input) {
13071
13368
  const { ctx, phasePrompt, phaseModel, phase, toolSurface, provider, sessionId, sessionData } = input;
13369
+ const logger = ctx.options?.logger;
13072
13370
  const runtime = getAgentRuntime(ctx.config.runtime);
13371
+ logger?.debug("agent.phase.start", `Phase ${phase.name} starting`, {
13372
+ runId: ctx.runId,
13373
+ phase: phase.name,
13374
+ model: phaseModel,
13375
+ maxTurns: phase.maxTurns,
13376
+ required: phase.required ?? false,
13377
+ toolNames: toolSurface.toolNames ?? null,
13378
+ sessionRef: sessionId ?? null
13379
+ });
13073
13380
  try {
13074
13381
  let result;
13075
13382
  try {
@@ -13082,13 +13389,19 @@ async function executePhase(input) {
13082
13389
  sessionRef: sessionId,
13083
13390
  sessionData,
13084
13391
  abortController: ctx.abortController,
13085
- toolSurface
13392
+ toolSurface,
13393
+ logger
13086
13394
  });
13087
13395
  } catch (error) {
13088
- if (!sessionId || !runtime.supports("supportsSessionResume") || !isSessionResumeFailure(error)) {
13396
+ if (!sessionId || !runtime.supports("supportsSessionResume") || !isSessionResumeFailure(error) && !isExpiredSessionError(error)) {
13089
13397
  throw error;
13090
13398
  }
13091
- console.warn(`[agent] Resuming phase "${phase.name}" session failed, retrying without prior session`);
13399
+ logger?.info("agent.phase.session-retry", `Phase ${phase.name} session failed, retrying without prior session`, {
13400
+ runId: ctx.runId,
13401
+ phase: phase.name,
13402
+ priorSession: sessionId,
13403
+ error: errorMessage(error)
13404
+ });
13092
13405
  result = await runtime.execute({
13093
13406
  prompt: phasePrompt,
13094
13407
  model: phaseModel,
@@ -13096,14 +13409,24 @@ async function executePhase(input) {
13096
13409
  systemPrompt: ctx.systemPrompt,
13097
13410
  provider,
13098
13411
  abortController: ctx.abortController,
13099
- toolSurface
13412
+ toolSurface,
13413
+ logger
13100
13414
  });
13101
13415
  }
13102
- if (phase.maxTurns && result.turnsUsed > 0) {
13103
- console.log(`[agent] Phase "${phase.name}": num_turns=${result.turnsUsed}, budget=${phase.maxTurns}`);
13104
- }
13416
+ logger?.debug("agent.phase.end", `Phase ${phase.name} finished`, {
13417
+ runId: ctx.runId,
13418
+ phase: phase.name,
13419
+ status: "completed",
13420
+ turnsUsed: result.turnsUsed,
13421
+ maxTurns: phase.maxTurns ?? null,
13422
+ tokensUsed: result.usage.totalTokens ?? 0,
13423
+ costUsd: result.usage.costUsd ?? null
13424
+ });
13105
13425
  if (phase.required && result.turnsUsed === 0) {
13106
- console.warn(`[agent] Required phase "${phase.name}" produced 0 turns`);
13426
+ logger?.warn("agent.phase.zero-turns", `Required phase ${phase.name} produced 0 turns`, {
13427
+ runId: ctx.runId,
13428
+ phase: phase.name
13429
+ });
13107
13430
  }
13108
13431
  const costData = await resolveCost({
13109
13432
  runtime: ctx.config.runtime,
@@ -13129,6 +13452,15 @@ async function executePhase(input) {
13129
13452
  model: phaseModel,
13130
13453
  usage: telemetry.usage
13131
13454
  }) : void 0;
13455
+ logger?.debug("agent.phase.end", `Phase ${phase.name} failed`, {
13456
+ runId: ctx.runId,
13457
+ phase: phase.name,
13458
+ status: "failed",
13459
+ turnsUsed: telemetry?.usage.requests ?? 0,
13460
+ tokensUsed: telemetry?.usage.totalTokens ?? 0,
13461
+ costUsd: telemetry?.usage.costUsd ?? null,
13462
+ error: abortReason ?? errorMessage(err)
13463
+ });
13132
13464
  return buildPhaseResult({
13133
13465
  name: phase.name,
13134
13466
  status: "failed",
@@ -13146,23 +13478,41 @@ async function executeSingleQuery(ctx, taskPrompt, provider, sessionRef, session
13146
13478
  provider,
13147
13479
  ctx.config.model
13148
13480
  );
13149
- const result = await runtime.execute({
13481
+ const toolSurface = {
13482
+ agentId: ctx.agentId,
13483
+ runId: ctx.runId,
13484
+ vaultDir: ctx.vaultDir,
13485
+ embeddingManager: ctx.embeddingManager,
13486
+ dryRun: ctx.config.dryRun ?? false
13487
+ };
13488
+ const baseInput = {
13150
13489
  prompt: taskPrompt,
13151
13490
  model: effectiveModel,
13152
13491
  maxTurns: ctx.config.maxTurns,
13153
13492
  systemPrompt: ctx.systemPrompt,
13154
13493
  provider,
13155
13494
  abortController: ctx.abortController,
13156
- sessionRef,
13157
- sessionData,
13158
- toolSurface: {
13159
- agentId: ctx.agentId,
13160
- runId: ctx.runId,
13161
- vaultDir: ctx.vaultDir,
13162
- embeddingManager: ctx.embeddingManager,
13163
- dryRun: ctx.config.dryRun ?? false
13495
+ toolSurface,
13496
+ logger: ctx.options?.logger
13497
+ };
13498
+ let result;
13499
+ try {
13500
+ result = await runtime.execute({ ...baseInput, sessionRef, sessionData });
13501
+ } catch (err) {
13502
+ if (!sessionRef || !runtime.supports("supportsSessionResume") || !isSessionResumeFailure(err) && !isExpiredSessionError(err)) {
13503
+ throw err;
13164
13504
  }
13165
- });
13505
+ ctx.options?.logger?.info(
13506
+ "agent.single-query.session-retry",
13507
+ "Single-query session failed, retrying without prior session",
13508
+ {
13509
+ runId: ctx.runId,
13510
+ priorSession: sessionRef,
13511
+ error: errorMessage(err)
13512
+ }
13513
+ );
13514
+ result = await runtime.execute(baseInput);
13515
+ }
13166
13516
  const costData = await resolveCost({
13167
13517
  runtime: ctx.config.runtime,
13168
13518
  provider,
@@ -13210,10 +13560,17 @@ async function executePhasedQuery(ctx) {
13210
13560
  vaultDir: ctx.vaultDir,
13211
13561
  dryRun: config.dryRun ?? false
13212
13562
  },
13213
- abortController: ctx.abortController
13563
+ abortController: ctx.abortController,
13564
+ logger: ctx.options?.logger
13214
13565
  });
13215
- const plan = parseOrchestratorPlan(planResponse.finalText, phases);
13566
+ const plan = parseOrchestratorPlan(planResponse.finalText, phases, ctx.options?.logger);
13216
13567
  effectivePhases = applyDirectives(phases, plan.phases);
13568
+ ctx.options?.logger?.debug("agent.orchestrator.plan", "Orchestrator plan applied", {
13569
+ runId,
13570
+ reasoning: plan.reasoning,
13571
+ effectivePhases: effectivePhases.map((p) => p.name),
13572
+ skippedPhases: plan.phases.filter((d) => d.skip).map((d) => d.name)
13573
+ });
13217
13574
  }
13218
13575
  const declarationOrder = new Map(phases.map((p, i) => [p.name, i]));
13219
13576
  const waves = computeWaves(effectivePhases);
@@ -13373,12 +13730,16 @@ var SKIP_REASON_ALREADY_RUNNING = "already_running";
13373
13730
  var CORTEX_INSTRUCTIONS_REPORT_ACTION = "cortex_instructions";
13374
13731
  var CORTEX_INSTRUCTIONS_CONTENT_KEY = "content";
13375
13732
  var TOKEN_BUDGET_PRESSURE_STATUSES = /* @__PURE__ */ new Set(["warning", "post_run_pressure"]);
13376
- function logTokenBudgetPressure(taskName, usage, provider) {
13733
+ function logTokenBudgetPressure(taskName, usage, provider, logger) {
13377
13734
  const budget = analyzeRuntimeTokenBudget(usage, provider);
13378
13735
  if (!TOKEN_BUDGET_PRESSURE_STATUSES.has(budget.status)) return;
13379
- console.warn(
13380
- `[agent] ${taskName} token budget ${budget.status}: ${budget.utilizationPercent}% of ${budget.contextWindowTokens} tokens at peak request (${budget.peakRequestTotalTokens} tokens)`
13381
- );
13736
+ logger?.warn("agent.token-budget-pressure", `${taskName} token budget ${budget.status}`, {
13737
+ task: taskName,
13738
+ status: budget.status,
13739
+ utilizationPercent: budget.utilizationPercent,
13740
+ contextWindowTokens: budget.contextWindowTokens,
13741
+ peakRequestTotalTokens: budget.peakRequestTotalTokens
13742
+ });
13382
13743
  }
13383
13744
  async function runAgent(vaultDir, options) {
13384
13745
  const db = initDatabase(vaultDbPath(vaultDir));
@@ -13523,15 +13884,25 @@ async function runAgent(vaultDir, options) {
13523
13884
  taskProviderOverride = resolved.taskProvider;
13524
13885
  phaseProviderOverrides = resolved.phaseOverrides;
13525
13886
  for (const conflict of resolved.conflicts) {
13526
- console.warn(
13527
- `[agent] Ollama model "${conflict.model}" referenced with conflicting context_length values [${conflict.values.join(", ")}] \u2014 reconciled to ${conflict.resolved} to avoid loading multiple variants. Configure one value per model to silence this warning.`
13887
+ options?.logger?.warn(
13888
+ "agent.ollama.context-variant-conflict",
13889
+ `Ollama model "${conflict.model}" referenced with conflicting context_length values \u2014 reconciled to ${conflict.resolved}`,
13890
+ {
13891
+ model: conflict.model,
13892
+ values: conflict.values,
13893
+ resolved: conflict.resolved
13894
+ }
13528
13895
  );
13529
13896
  }
13530
13897
  }
13531
13898
  const taskAbortController = new AbortController();
13532
13899
  const timeoutMs = config.timeoutSeconds * MS_PER_SECOND;
13533
13900
  const timeoutId = setTimeout(() => {
13534
- console.warn(`[agent] Run ${runId} exceeded timeout (${config.timeoutSeconds}s), aborting`);
13901
+ options?.logger?.warn("agent.run.timeout", `Run ${runId} exceeded timeout, aborting`, {
13902
+ runId,
13903
+ taskName: config.taskName,
13904
+ timeoutSeconds: config.timeoutSeconds
13905
+ });
13535
13906
  taskAbortController.abort(new Error(`Agent run timed out after ${config.timeoutSeconds} seconds`));
13536
13907
  }, timeoutMs);
13537
13908
  timeoutId.unref?.();
@@ -13623,13 +13994,14 @@ async function runAgent(vaultDir, options) {
13623
13994
  }
13624
13995
  }
13625
13996
  clearTimeout(timeoutId);
13626
- logTokenBudgetPressure(config.taskName, usage, effectiveProvider);
13997
+ logTokenBudgetPressure(config.taskName, usage, effectiveProvider, options?.logger);
13627
13998
  await finalizeOnTaskSuccess({
13628
13999
  taskName: config.taskName,
13629
14000
  agentId,
13630
14001
  runId,
13631
14002
  runContext: options?.runContext,
13632
- instruction: options?.instruction
14003
+ instruction: options?.instruction,
14004
+ dryRun: options?.dryRun
13633
14005
  });
13634
14006
  const completedAt = epochSeconds();
13635
14007
  updateRunStatus(runId, STATUS_COMPLETED, {
@@ -13677,34 +14049,48 @@ ${err.stack.split("\n").slice(0, 3).join("\n")}`;
13677
14049
  }
13678
14050
  }
13679
14051
  const failedAt = epochSeconds();
13680
- console.error(`[agent] Run ${runId} failed: ${errorMessage2}`);
14052
+ options?.logger?.error("agent.run.failed", `Run ${runId} failed`, {
14053
+ runId,
14054
+ taskName: config.taskName,
14055
+ error: errorMessage2
14056
+ });
13681
14057
  try {
13682
14058
  const usage = aggregateUsage(phaseResults?.map((phase) => phase.usage) ?? []);
13683
- logTokenBudgetPressure(config.taskName, usage, effectiveProvider);
14059
+ logTokenBudgetPressure(config.taskName, usage, effectiveProvider, options?.logger);
13684
14060
  const costData = phaseResults ? summarizePhaseCosts(phaseResults) : await resolveCost({
13685
14061
  runtime: runtimeId,
13686
14062
  provider: effectiveProvider,
13687
14063
  model: effectiveModel,
13688
14064
  usage
13689
14065
  });
14066
+ const hadPriorSession = Boolean(checkpointState.sessionRef) || Object.values(checkpointState.phases).some((phase) => Boolean(phase.sessionRef));
14067
+ const recordedAnyTurns = (usage.requests ?? 0) > 0 || (phaseResults?.some((phase) => phase.turnsUsed > 0) ?? false);
14068
+ const sessionExpired = Boolean(options?.resumeRunId) && hadPriorSession && !recordedAnyTurns && isExpiredSessionError(err);
14069
+ const accountingUpdate = buildRunAccountingUpdate({
14070
+ runtime: runtimeId,
14071
+ provider: effectiveProvider,
14072
+ model: effectiveModel,
14073
+ checkpointState,
14074
+ usage,
14075
+ costData,
14076
+ phaseResults
14077
+ });
14078
+ if (sessionExpired) {
14079
+ accountingUpdate.checkpoints = null;
14080
+ }
13690
14081
  updateRunStatus(runId, STATUS_FAILED, {
13691
- resumable: 1,
13692
- resume_status: RESUME_STATUS_READY,
14082
+ resumable: sessionExpired ? 0 : 1,
14083
+ resume_status: sessionExpired ? RESUME_STATUS_SESSION_EXPIRED : RESUME_STATUS_READY,
13693
14084
  completed_at: failedAt,
13694
14085
  tokens_used: usage.totalTokens ?? phaseResults?.reduce((sum, phase) => sum + phase.tokensUsed, 0) ?? void 0,
13695
14086
  error: errorMessage2,
13696
- ...buildRunAccountingUpdate({
13697
- runtime: runtimeId,
13698
- provider: effectiveProvider,
13699
- model: effectiveModel,
13700
- checkpointState,
13701
- usage,
13702
- costData,
13703
- phaseResults
13704
- })
14087
+ ...accountingUpdate
13705
14088
  });
13706
14089
  } catch (dbErr) {
13707
- console.error(`[agent] Failed to save error to DB:`, dbErr);
14090
+ options?.logger?.error("agent.run.db-save-failed", `Failed to save error to DB for run ${runId}`, {
14091
+ runId,
14092
+ error: errorMessage(dbErr)
14093
+ });
13708
14094
  }
13709
14095
  await cleanupOnTaskFailure({
13710
14096
  taskName: config.taskName,
@@ -13742,6 +14128,7 @@ async function cleanupOnTaskFailure(args) {
13742
14128
  }
13743
14129
  async function finalizeOnTaskSuccess(args) {
13744
14130
  if (args.taskName !== CORTEX_INSTRUCTIONS_TASK) return;
14131
+ if (args.dryRun) return;
13745
14132
  const reports = listReports(args.runId);
13746
14133
  let report;
13747
14134
  for (let i = reports.length - 1; i >= 0; i -= 1) {
@@ -13832,6 +14219,8 @@ export {
13832
14219
  countWriteIntents,
13833
14220
  countWriteIntentsByTool,
13834
14221
  countWriteIntentsByToolForEvaluation,
14222
+ hasSemanticSearchFilters,
14223
+ matchesSemanticSearchFilters,
13835
14224
  getGraphForNode,
13836
14225
  createBatchLineage,
13837
14226
  insertResolutionEvent,
@@ -13841,4 +14230,4 @@ export {
13841
14230
  cleanupOnTaskFailure,
13842
14231
  finalizeOnTaskSuccess
13843
14232
  };
13844
- //# sourceMappingURL=chunk-NGH7U6A3.js.map
14233
+ //# sourceMappingURL=chunk-HCT7RMM2.js.map