@goondocks/myco 0.21.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/bin/myco-run +68 -7
  2. package/dist/{agent-eval-RJSQI5S2.js → agent-eval-YK2VP2S4.js} +7 -6
  3. package/dist/{agent-eval-RJSQI5S2.js.map → agent-eval-YK2VP2S4.js.map} +1 -1
  4. package/dist/{agent-run-2JSYFOKU.js → agent-run-GEJBD2YD.js} +5 -5
  5. package/dist/{agent-tasks-APFJIM2T.js → agent-tasks-5XSRGTRX.js} +5 -5
  6. package/dist/{chunk-R2JIJBCL.js → chunk-6ALVMIB4.js} +3 -3
  7. package/dist/{chunk-JZS6GZ6T.js → chunk-AUIXX33A.js} +10 -3
  8. package/dist/chunk-AUIXX33A.js.map +1 -0
  9. package/dist/{chunk-RL5R4CQU.js → chunk-DTWUHHFI.js} +39 -2
  10. package/dist/{chunk-RL5R4CQU.js.map → chunk-DTWUHHFI.js.map} +1 -1
  11. package/dist/{chunk-CESKJD44.js → chunk-EEOJWLMP.js} +5 -9
  12. package/dist/chunk-EEOJWLMP.js.map +1 -0
  13. package/dist/{chunk-BUTL6IFS.js → chunk-ENZR5NG7.js} +2 -2
  14. package/dist/{chunk-P66DLD6G.js → chunk-KTTSXYEK.js} +2 -2
  15. package/dist/chunk-LQIPXVDH.js +17 -0
  16. package/dist/chunk-LQIPXVDH.js.map +1 -0
  17. package/dist/{chunk-5ZG4RMUH.js → chunk-N2DGFACQ.js} +2 -2
  18. package/dist/{chunk-F3OEQYLS.js → chunk-N7Z3LUEZ.js} +33 -22
  19. package/dist/{chunk-F3OEQYLS.js.map → chunk-N7Z3LUEZ.js.map} +1 -1
  20. package/dist/{chunk-G6QIBNZM.js → chunk-NFO7BRCO.js} +4 -4
  21. package/dist/{chunk-VHNRMM4O.js → chunk-OTQH5KZW.js} +87 -37
  22. package/dist/chunk-OTQH5KZW.js.map +1 -0
  23. package/dist/{chunk-DJ3IHNYO.js → chunk-OZ3FBAK5.js} +2 -2
  24. package/dist/{chunk-6LB7XELY.js → chunk-QATYARI5.js} +15 -13
  25. package/dist/chunk-QATYARI5.js.map +1 -0
  26. package/dist/{chunk-JR54LTPP.js → chunk-QDLVIW2O.js} +3 -3
  27. package/dist/{chunk-LVIY7P35.js → chunk-QLLBJEM7.js} +5 -1
  28. package/dist/chunk-QLLBJEM7.js.map +1 -0
  29. package/dist/chunk-TSM6VESW.js +25 -0
  30. package/dist/chunk-TSM6VESW.js.map +1 -0
  31. package/dist/{chunk-ILJPRYES.js → chunk-USVFEWYL.js} +2 -2
  32. package/dist/{chunk-75Z7UKDY.js → chunk-VRI56337.js} +2 -2
  33. package/dist/{chunk-NGH7U6A3.js → chunk-X2IRGXGF.js} +336 -77
  34. package/dist/chunk-X2IRGXGF.js.map +1 -0
  35. package/dist/{chunk-NGROSFOH.js → chunk-Z66IT5KL.js} +14 -9
  36. package/dist/chunk-Z66IT5KL.js.map +1 -0
  37. package/dist/{cli-LNYSTDQM.js → cli-HSLIG7EX.js} +37 -37
  38. package/dist/{client-NWE4TCNO.js → client-Z43DNLJH.js} +3 -3
  39. package/dist/{detect-PXNM6TA7.js → detect-7NUD5B5R.js} +2 -2
  40. package/dist/{doctor-TI7EZ3RW.js → doctor-HJCWHAU4.js} +6 -6
  41. package/dist/{executor-F2YU7HXJ.js → executor-DO6QFC6G.js} +11 -10
  42. package/dist/{init-KG3TYVGE.js → init-4KVK7W2E.js} +9 -9
  43. package/dist/{installer-UMH7OJ5A.js → installer-N4UTEACX.js} +2 -2
  44. package/dist/{loader-NAVVZK63.js → loader-UDNUMEDA.js} +3 -2
  45. package/dist/{main-5PRQNEEE.js → main-4J4QZZTZ.js} +121 -62
  46. package/dist/main-4J4QZZTZ.js.map +1 -0
  47. package/dist/{open-5A27BCSB.js → open-7TXJQM3H.js} +5 -5
  48. package/dist/{post-compact-USAODKPQ.js → post-compact-7AEFVCZS.js} +7 -7
  49. package/dist/{post-tool-use-GMMSYBII.js → post-tool-use-TZINWWDH.js} +6 -6
  50. package/dist/{post-tool-use-failure-NZVSL2PO.js → post-tool-use-failure-TCFEU2GI.js} +7 -7
  51. package/dist/{pre-compact-LZ57DLUS.js → pre-compact-LO2VZCGR.js} +7 -7
  52. package/dist/{registry-M2Z5QBWH.js → registry-F3THYC5M.js} +4 -3
  53. package/dist/{remove-T3KE6C5N.js → remove-F77AAALE.js} +7 -7
  54. package/dist/{restart-YWDEVZUJ.js → restart-UEFDPMLT.js} +6 -6
  55. package/dist/{search-GKFDGELR.js → search-NHNVUAQQ.js} +6 -6
  56. package/dist/{server-AHUR6CWF.js → server-AZJSTQEK.js} +5 -5
  57. package/dist/{session-2ZEPLWW6.js → session-3HLC5KOD.js} +5 -5
  58. package/dist/{session-end-LWJYQAXX.js → session-end-FS46UARX.js} +6 -6
  59. package/dist/{session-start-WTA6GCOQ.js → session-start-46KPFV2H.js} +10 -10
  60. package/dist/{setup-llm-E7UU5IO7.js → setup-llm-JMWSNQ2C.js} +5 -5
  61. package/dist/src/agent/definitions/tasks/cortex-instructions.yaml +63 -41
  62. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +177 -21
  63. package/dist/src/agent/definitions/tasks/skill-generate.yaml +20 -6
  64. package/dist/src/agent/definitions/tasks/vault-evolve.yaml +65 -55
  65. package/dist/src/cli.js +1 -1
  66. package/dist/src/daemon/main.js +1 -1
  67. package/dist/src/hooks/post-tool-use.js +1 -1
  68. package/dist/src/hooks/session-end.js +1 -1
  69. package/dist/src/hooks/session-start.js +1 -1
  70. package/dist/src/hooks/stop.js +1 -1
  71. package/dist/src/hooks/user-prompt-submit.js +1 -1
  72. package/dist/src/mcp/server.js +1 -1
  73. package/dist/src/symbionts/manifests/opencode.yaml +7 -0
  74. package/dist/src/symbionts/templates/agents-starter.md +1 -1
  75. package/dist/{stats-DFG6S23S.js → stats-MKMETHMA.js} +6 -6
  76. package/dist/{stop-WRBTXEVT.js → stop-OUEX6KA4.js} +6 -6
  77. package/dist/{stop-failure-32MGIG2Q.js → stop-failure-2BWVNZEG.js} +7 -7
  78. package/dist/{subagent-start-VFGHQFVL.js → subagent-start-J4VV6DEE.js} +7 -7
  79. package/dist/{subagent-stop-663FXG3P.js → subagent-stop-JMLVEPIA.js} +7 -7
  80. package/dist/{task-completed-ZCQYEFMZ.js → task-completed-65CHMMKA.js} +7 -7
  81. package/dist/{team-JTI5CDUO.js → team-U2LDKIS4.js} +3 -3
  82. package/dist/ui/assets/{index-DGf1h-Ha.js → index-BUGor9dk.js} +1 -1
  83. package/dist/ui/index.html +1 -1
  84. package/dist/{update-3NBQTG32.js → update-ZSHVXWSQ.js} +7 -7
  85. package/dist/{user-prompt-submit-ME2TBKOS.js → user-prompt-submit-APMO6FVU.js} +6 -6
  86. package/dist/{version-GQAFBBPX.js → version-TXPPS3L5.js} +2 -2
  87. package/package.json +1 -1
  88. package/skills/myco-rules/SKILL.md +94 -0
  89. package/skills/{rules → myco-rules}/references/rules-bad-example.md +1 -1
  90. package/skills/{rules → myco-rules}/references/rules-good-example.md +1 -1
  91. package/dist/chunk-6LB7XELY.js.map +0 -1
  92. package/dist/chunk-CESKJD44.js.map +0 -1
  93. package/dist/chunk-CUDIZJY7.js +0 -36
  94. package/dist/chunk-CUDIZJY7.js.map +0 -1
  95. package/dist/chunk-JZS6GZ6T.js.map +0 -1
  96. package/dist/chunk-LVIY7P35.js.map +0 -1
  97. package/dist/chunk-NGH7U6A3.js.map +0 -1
  98. package/dist/chunk-NGROSFOH.js.map +0 -1
  99. package/dist/chunk-VHNRMM4O.js.map +0 -1
  100. package/dist/main-5PRQNEEE.js.map +0 -1
  101. package/skills/myco-curate/SKILL.md +0 -86
  102. package/skills/rules/SKILL.md +0 -214
  103. /package/dist/{agent-run-2JSYFOKU.js.map → agent-run-GEJBD2YD.js.map} +0 -0
  104. /package/dist/{agent-tasks-APFJIM2T.js.map → agent-tasks-5XSRGTRX.js.map} +0 -0
  105. /package/dist/{chunk-R2JIJBCL.js.map → chunk-6ALVMIB4.js.map} +0 -0
  106. /package/dist/{chunk-BUTL6IFS.js.map → chunk-ENZR5NG7.js.map} +0 -0
  107. /package/dist/{chunk-P66DLD6G.js.map → chunk-KTTSXYEK.js.map} +0 -0
  108. /package/dist/{chunk-5ZG4RMUH.js.map → chunk-N2DGFACQ.js.map} +0 -0
  109. /package/dist/{chunk-G6QIBNZM.js.map → chunk-NFO7BRCO.js.map} +0 -0
  110. /package/dist/{chunk-DJ3IHNYO.js.map → chunk-OZ3FBAK5.js.map} +0 -0
  111. /package/dist/{chunk-JR54LTPP.js.map → chunk-QDLVIW2O.js.map} +0 -0
  112. /package/dist/{chunk-ILJPRYES.js.map → chunk-USVFEWYL.js.map} +0 -0
  113. /package/dist/{chunk-75Z7UKDY.js.map → chunk-VRI56337.js.map} +0 -0
  114. /package/dist/{cli-LNYSTDQM.js.map → cli-HSLIG7EX.js.map} +0 -0
  115. /package/dist/{client-NWE4TCNO.js.map → client-Z43DNLJH.js.map} +0 -0
  116. /package/dist/{detect-PXNM6TA7.js.map → detect-7NUD5B5R.js.map} +0 -0
  117. /package/dist/{doctor-TI7EZ3RW.js.map → doctor-HJCWHAU4.js.map} +0 -0
  118. /package/dist/{executor-F2YU7HXJ.js.map → executor-DO6QFC6G.js.map} +0 -0
  119. /package/dist/{init-KG3TYVGE.js.map → init-4KVK7W2E.js.map} +0 -0
  120. /package/dist/{installer-UMH7OJ5A.js.map → installer-N4UTEACX.js.map} +0 -0
  121. /package/dist/{loader-NAVVZK63.js.map → loader-UDNUMEDA.js.map} +0 -0
  122. /package/dist/{open-5A27BCSB.js.map → open-7TXJQM3H.js.map} +0 -0
  123. /package/dist/{post-compact-USAODKPQ.js.map → post-compact-7AEFVCZS.js.map} +0 -0
  124. /package/dist/{post-tool-use-GMMSYBII.js.map → post-tool-use-TZINWWDH.js.map} +0 -0
  125. /package/dist/{post-tool-use-failure-NZVSL2PO.js.map → post-tool-use-failure-TCFEU2GI.js.map} +0 -0
  126. /package/dist/{pre-compact-LZ57DLUS.js.map → pre-compact-LO2VZCGR.js.map} +0 -0
  127. /package/dist/{registry-M2Z5QBWH.js.map → registry-F3THYC5M.js.map} +0 -0
  128. /package/dist/{remove-T3KE6C5N.js.map → remove-F77AAALE.js.map} +0 -0
  129. /package/dist/{restart-YWDEVZUJ.js.map → restart-UEFDPMLT.js.map} +0 -0
  130. /package/dist/{search-GKFDGELR.js.map → search-NHNVUAQQ.js.map} +0 -0
  131. /package/dist/{server-AHUR6CWF.js.map → server-AZJSTQEK.js.map} +0 -0
  132. /package/dist/{session-2ZEPLWW6.js.map → session-3HLC5KOD.js.map} +0 -0
  133. /package/dist/{session-end-LWJYQAXX.js.map → session-end-FS46UARX.js.map} +0 -0
  134. /package/dist/{session-start-WTA6GCOQ.js.map → session-start-46KPFV2H.js.map} +0 -0
  135. /package/dist/{setup-llm-E7UU5IO7.js.map → setup-llm-JMWSNQ2C.js.map} +0 -0
  136. /package/dist/{stats-DFG6S23S.js.map → stats-MKMETHMA.js.map} +0 -0
  137. /package/dist/{stop-WRBTXEVT.js.map → stop-OUEX6KA4.js.map} +0 -0
  138. /package/dist/{stop-failure-32MGIG2Q.js.map → stop-failure-2BWVNZEG.js.map} +0 -0
  139. /package/dist/{subagent-start-VFGHQFVL.js.map → subagent-start-J4VV6DEE.js.map} +0 -0
  140. /package/dist/{subagent-stop-663FXG3P.js.map → subagent-stop-JMLVEPIA.js.map} +0 -0
  141. /package/dist/{task-completed-ZCQYEFMZ.js.map → task-completed-65CHMMKA.js.map} +0 -0
  142. /package/dist/{team-JTI5CDUO.js.map → team-U2LDKIS4.js.map} +0 -0
  143. /package/dist/{update-3NBQTG32.js.map → update-ZSHVXWSQ.js.map} +0 -0
  144. /package/dist/{user-prompt-submit-ME2TBKOS.js.map → user-prompt-submit-APMO6FVU.js.map} +0 -0
  145. /package/dist/{version-GQAFBBPX.js.map → version-TXPPS3L5.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,
@@ -64,7 +68,7 @@ import {
64
68
  updateSporeStatus,
65
69
  upsertCortexInstructions,
66
70
  upsertDigestExtract
67
- } from "./chunk-F3OEQYLS.js";
71
+ } from "./chunk-N7Z3LUEZ.js";
68
72
  import {
69
73
  countSessions,
70
74
  getActiveSessionIds,
@@ -78,7 +82,7 @@ import {
78
82
  AGENT_SETTABLE_STATUSES,
79
83
  CANDIDATE_STATUS,
80
84
  createSchema
81
- } from "./chunk-RL5R4CQU.js";
85
+ } from "./chunk-DTWUHHFI.js";
82
86
  import {
83
87
  loadMergedConfig
84
88
  } from "./chunk-53RPGOEN.js";
@@ -89,7 +93,7 @@ import {
89
93
  } from "./chunk-MYX5NCRH.js";
90
94
  import {
91
95
  getPluginVersion
92
- } from "./chunk-BUTL6IFS.js";
96
+ } from "./chunk-ENZR5NG7.js";
93
97
  import {
94
98
  findPackageRoot
95
99
  } from "./chunk-LPUQPDC2.js";
@@ -97,6 +101,7 @@ import {
97
101
  CONTENT_HASH_ALGORITHM,
98
102
  DEFAULT_AGENT_ID,
99
103
  DEFAULT_LIST_LIMIT,
104
+ DIGEST_TIERS,
100
105
  EDGE_TYPE_DERIVED_FROM,
101
106
  EDGE_TYPE_EXTRACTED_FROM,
102
107
  EDGE_TYPE_FROM_SESSION,
@@ -197,6 +202,7 @@ var STATUS_RUNNING = "running";
197
202
  var STATUS_COMPLETED = "completed";
198
203
  var STATUS_FAILED = "failed";
199
204
  var RESUME_STATUS_READY = "ready";
205
+ var RESUME_STATUS_SESSION_EXPIRED = "session_expired";
200
206
  var RUN_COLUMNS = [
201
207
  "id",
202
208
  "agent_id",
@@ -2403,6 +2409,7 @@ var ORCHESTRATOR_GUIDANCE_HEADER = "## Orchestrator Guidance";
2403
2409
  var NO_CONTEXT_QUERIES_TEXT = "No context queries configured.";
2404
2410
  var FALLBACK_REASONING_PARSE_ERROR = "Orchestrator response could not be parsed \u2014 running all phases with defaults.";
2405
2411
  var FALLBACK_REASONING_MISSING_PHASES = "Orchestrator plan missing phases array \u2014 running all phases with defaults.";
2412
+ var ORCHESTRATOR_PARSE_ERROR_PREVIEW_CHARS = 200;
2406
2413
  var PLACEHOLDER_VAULT_STATE = "{{vault_state}}";
2407
2414
  var PLACEHOLDER_PHASE_DEFINITIONS = "{{phase_definitions}}";
2408
2415
  var PLACEHOLDER_CONTEXT_RESULTS = "{{context_results}}";
@@ -2436,7 +2443,7 @@ function composeOrchestratorPrompt(vaultState, phases, contextResults) {
2436
2443
  const contextSection = formatContextResults(contextResults);
2437
2444
  return template.replace(PLACEHOLDER_VAULT_STATE, vaultState).replace(PLACEHOLDER_PHASE_DEFINITIONS, phaseList).replace(PLACEHOLDER_CONTEXT_RESULTS, contextSection);
2438
2445
  }
2439
- function parseOrchestratorPlan(response, phases) {
2446
+ function parseOrchestratorPlan(response, phases, logger) {
2440
2447
  const trimmed = response.trim();
2441
2448
  if (!trimmed) {
2442
2449
  return buildRunAllPlan(phases, FALLBACK_REASONING_PARSE_ERROR);
@@ -2447,8 +2454,14 @@ function parseOrchestratorPlan(response, phases) {
2447
2454
  return buildRunAllPlan(phases, FALLBACK_REASONING_MISSING_PHASES);
2448
2455
  }
2449
2456
  return parsed;
2450
- } catch {
2451
- return buildRunAllPlan(phases, FALLBACK_REASONING_PARSE_ERROR);
2457
+ } catch (err) {
2458
+ const detail = errorMessage(err);
2459
+ const truncated = detail.length > ORCHESTRATOR_PARSE_ERROR_PREVIEW_CHARS ? `${detail.slice(0, ORCHESTRATOR_PARSE_ERROR_PREVIEW_CHARS)}\u2026` : detail;
2460
+ logger?.warn("agent.orchestrator.parse-failed", "Orchestrator plan parse failed", {
2461
+ error: detail,
2462
+ responsePreview: trimmed.slice(0, 200)
2463
+ });
2464
+ return buildRunAllPlan(phases, `${FALLBACK_REASONING_PARSE_ERROR} (${truncated})`);
2452
2465
  }
2453
2466
  }
2454
2467
  function applyDirectives(phases, directives) {
@@ -3355,23 +3368,36 @@ function createReadTools(deps) {
3355
3368
  );
3356
3369
  const vaultSearchFts = tool(
3357
3370
  "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.",
3371
+ '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
3372
  {
3360
- query: external_exports.string().describe("Search query text"),
3373
+ query: external_exports.string().describe("Search query text. Special characters (hyphens, slashes, dots, colons) are auto-quoted \u2014 no manual escaping needed."),
3361
3374
  type: external_exports.string().optional().describe("Restrict to a result type (session, spore, prompt_batch, activity)"),
3362
3375
  limit: external_exports.number().optional().describe("Maximum number of results to return"),
3363
3376
  include_active: external_exports.boolean().optional().describe("Include results from sessions still in active status (default: false)")
3364
3377
  },
3365
3378
  async (args) => {
3379
+ const sanitizedQuery = sanitizeFtsQuery(args.query);
3366
3380
  try {
3367
- const results = fullTextSearch(args.query, {
3381
+ const results = fullTextSearch(sanitizedQuery, {
3368
3382
  type: args.type,
3369
3383
  limit: args.limit ?? DEFAULT_SEARCH_LIMIT,
3370
3384
  includeActive: args.include_active === true
3371
3385
  });
3372
- return textResult({ results });
3373
- } catch {
3374
- return textResult({ results: [], message: "Search unavailable" });
3386
+ return textResult({ results, sanitized_query: sanitizedQuery !== args.query ? sanitizedQuery : void 0 });
3387
+ } catch (err) {
3388
+ const message = errorMessage(err);
3389
+ let hint;
3390
+ if (/no such table|no such module: fts5/i.test(message)) {
3391
+ hint = "FTS index missing \u2014 the searched table may not have an FTS5 mirror. Check the type parameter.";
3392
+ } else if (/syntax error|malformed MATCH/i.test(message)) {
3393
+ hint = 'FTS5 query syntax rejected. Quote special characters (e.g., wrap file paths in double quotes: "packages/myco/src/loader.ts").';
3394
+ }
3395
+ return textResult({
3396
+ error: `vault_search_fts failed: ${message}${hint ? ` \u2014 ${hint}` : ""}`,
3397
+ results: [],
3398
+ query: args.query,
3399
+ type: args.type ?? null
3400
+ });
3375
3401
  }
3376
3402
  },
3377
3403
  { annotations: { readOnlyHint: true } }
@@ -3427,8 +3453,22 @@ function createReadTools(deps) {
3427
3453
  ...dedupedTeam
3428
3454
  ].sort((a, b) => (b.score ?? 0) - (a.score ?? 0)).slice(0, searchLimit);
3429
3455
  return textResult({ results: merged });
3430
- } catch {
3431
- return textResult({ results: [], message: "Semantic search unavailable" });
3456
+ } catch (err) {
3457
+ const message = errorMessage(err);
3458
+ let hint;
3459
+ if (/timeout|ETIMEDOUT|AbortError/i.test(message)) {
3460
+ hint = "Embedding provider timed out. Retry once or reduce query length.";
3461
+ } else if (/ECONNREFUSED|fetch failed|network/i.test(message)) {
3462
+ hint = "Embedding provider unreachable. Check that the configured provider (Ollama/LM Studio/Anthropic) is running.";
3463
+ } else if (/no such table|no such column/i.test(message)) {
3464
+ hint = "Vector table missing or schema mismatch. Run `myco rebuild` or check migrations.";
3465
+ }
3466
+ return textResult({
3467
+ error: `vault_search_semantic failed: ${message}${hint ? ` \u2014 ${hint}` : ""}`,
3468
+ results: [],
3469
+ query: args.query,
3470
+ namespace: args.namespace ?? null
3471
+ });
3432
3472
  }
3433
3473
  },
3434
3474
  { annotations: { readOnlyHint: true, openWorldHint: true } }
@@ -3726,12 +3766,54 @@ function createWriteTools(deps) {
3726
3766
  );
3727
3767
  const vaultReadDigest = tool2(
3728
3768
  "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.",
3769
+ '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
3770
  {
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.")
3771
+ 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."),
3772
+ 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.'),
3773
+ 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
3774
  },
3733
3775
  async (args) => {
3734
3776
  const extracts = listDigestExtracts(agentId);
3777
+ if (args.pick === "rotate_oldest") {
3778
+ const now = epochSeconds();
3779
+ const canonical = DIGEST_TIERS.map((tier) => {
3780
+ const existing = extracts.find((e) => e.tier === tier);
3781
+ return {
3782
+ tier,
3783
+ generated_at: existing?.generated_at ?? 0,
3784
+ content_length: existing?.content.length ?? 0,
3785
+ content: existing?.content ?? null
3786
+ };
3787
+ });
3788
+ const minStale = args.min_staleness_seconds ?? 0;
3789
+ if (minStale > 0) {
3790
+ const allPresent = canonical.every((t) => t.generated_at > 0);
3791
+ const cutoff = now - minStale;
3792
+ const allFresh = canonical.every((t) => t.generated_at > cutoff);
3793
+ if (allPresent && allFresh) {
3794
+ return textResult({
3795
+ mode: "rotate_oldest",
3796
+ skip: true,
3797
+ reason: `All ${canonical.length} tiers were generated within the last ${minStale} seconds (cutoff=${cutoff}).`,
3798
+ all_tiers: canonical.map((t) => ({ tier: t.tier, generated_at: t.generated_at, content_length: t.content_length }))
3799
+ });
3800
+ }
3801
+ }
3802
+ const sorted = [...canonical].sort((a, b) => {
3803
+ if (a.generated_at !== b.generated_at) return a.generated_at - b.generated_at;
3804
+ return b.tier - a.tier;
3805
+ });
3806
+ const selected = sorted[0];
3807
+ 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}.`;
3808
+ return textResult({
3809
+ mode: "rotate_oldest",
3810
+ selected_tier: selected.tier,
3811
+ selected_generated_at: selected.generated_at,
3812
+ selected_content: selected.content,
3813
+ rotation_reason: reason,
3814
+ all_tiers: canonical.map((t) => ({ tier: t.tier, generated_at: t.generated_at, content_length: t.content_length }))
3815
+ });
3816
+ }
3735
3817
  if (args.tier !== void 0) {
3736
3818
  const extract = extracts.find((e) => e.tier === args.tier);
3737
3819
  if (!extract) return textResult({ tier: args.tier, content: null, message: "No digest at this tier" });
@@ -4131,7 +4213,7 @@ function createSkillTools(deps) {
4131
4213
  );
4132
4214
  }
4133
4215
  try {
4134
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4216
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4135
4217
  syncSkillSymlinks(root, params.name, { remove: true });
4136
4218
  } catch (rollbackErr) {
4137
4219
  console.warn(
@@ -4149,7 +4231,7 @@ function createSkillTools(deps) {
4149
4231
  };
4150
4232
  }
4151
4233
  try {
4152
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4234
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4153
4235
  syncSkillSymlinks(root, params.name);
4154
4236
  } catch (err) {
4155
4237
  console.warn(
@@ -4351,14 +4433,15 @@ function createSkillTools(deps) {
4351
4433
  );
4352
4434
  const vaultSkillRecords = tool4(
4353
4435
  "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.",
4436
+ "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
4437
  {
4356
4438
  action: external_exports.enum(["list", "get", "update", "delete"]).describe("Action to perform"),
4357
4439
  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"),
4440
+ status: external_exports.enum(["active", "stale", "retired"]).optional().describe("Filter by status or new status (for update)"),
4359
4441
  generation: external_exports.number().optional().describe("New generation number (for update)"),
4360
4442
  source_ids: external_exports.string().optional().describe("JSON array of source IDs (for update)"),
4361
4443
  description: external_exports.string().optional().describe("Updated description (for update)"),
4444
+ 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
4445
  limit: external_exports.number().optional().describe("Maximum records to return (for list)")
4363
4446
  },
4364
4447
  async (args) => {
@@ -4386,14 +4469,44 @@ function createSkillTools(deps) {
4386
4469
  }
4387
4470
  case "update": {
4388
4471
  if (!args.id) return textResult({ error: "id is required for update action" });
4472
+ const hasMutatingField = args.status !== void 0 || args.generation !== void 0 || args.source_ids !== void 0 || args.description !== void 0 || args.properties !== void 0;
4473
+ if (!hasMutatingField) {
4474
+ return textResult({
4475
+ 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."
4476
+ });
4477
+ }
4389
4478
  const existing = getSkillRecord(args.id) ?? getSkillRecordByName(args.id);
4390
4479
  if (!existing) return textResult({ error: `Skill record not found: ${args.id}` });
4480
+ let mergedProperties;
4481
+ if (args.properties !== void 0) {
4482
+ let incoming;
4483
+ try {
4484
+ const parsed = JSON.parse(args.properties);
4485
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
4486
+ return textResult({ error: "properties must be a JSON-encoded object (not null, array, or primitive)." });
4487
+ }
4488
+ incoming = parsed;
4489
+ } catch (err) {
4490
+ return textResult({ error: `properties is not valid JSON: ${errorMessage(err)}` });
4491
+ }
4492
+ let existingProps = {};
4493
+ try {
4494
+ const parsedExisting = JSON.parse(existing.properties || "{}");
4495
+ if (parsedExisting && typeof parsedExisting === "object" && !Array.isArray(parsedExisting)) {
4496
+ existingProps = parsedExisting;
4497
+ }
4498
+ } catch (err) {
4499
+ console.warn(`[vault_skill_records] Skill ${existing.id} has unparseable properties; treating as empty for merge. Error: ${errorMessage(err)}`);
4500
+ }
4501
+ mergedProperties = JSON.stringify({ ...existingProps, ...incoming });
4502
+ }
4391
4503
  const now = epochSeconds();
4392
4504
  const updated = updateSkillRecord(existing.id, {
4393
4505
  ...args.status !== void 0 ? { status: args.status } : {},
4394
4506
  ...args.generation !== void 0 ? { generation: args.generation } : {},
4395
4507
  ...args.source_ids !== void 0 ? { source_ids: args.source_ids } : {},
4396
4508
  ...args.description !== void 0 ? { description: args.description } : {},
4509
+ ...mergedProperties !== void 0 ? { properties: mergedProperties } : {},
4397
4510
  updated_at: now
4398
4511
  });
4399
4512
  if (!updated) return textResult({ error: `Failed to update skill record: ${existing.id}` });
@@ -4416,7 +4529,7 @@ function createSkillTools(deps) {
4416
4529
  console.warn("[vault_skill_records] Failed to remove skill directory:", err instanceof Error ? err.message : err);
4417
4530
  }
4418
4531
  try {
4419
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4532
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4420
4533
  syncSkillSymlinks(root, result.name, { remove: true });
4421
4534
  } catch (err) {
4422
4535
  console.warn("[vault_skill_records] Failed to remove symlinks:", err instanceof Error ? err.message : err);
@@ -4534,7 +4647,7 @@ function createSkillTools(deps) {
4534
4647
  return textResult({ error: `Failed to write skill file: ${err instanceof Error ? err.message : String(err)}` });
4535
4648
  }
4536
4649
  try {
4537
- const { syncSkillSymlinks } = await import("./installer-UMH7OJ5A.js");
4650
+ const { syncSkillSymlinks } = await import("./installer-N4UTEACX.js");
4538
4651
  syncSkillSymlinks(root, args.name);
4539
4652
  } catch (err) {
4540
4653
  console.warn("[vault_write_skill] syncSkillSymlinks failed:", err instanceof Error ? err.message : err);
@@ -5116,19 +5229,34 @@ function truncateSummary(text) {
5116
5229
  if (!text) return null;
5117
5230
  return text.length > TOOL_OUTPUT_SUMMARY_LIMIT ? `${text.slice(0, TOOL_OUTPUT_SUMMARY_LIMIT - 1)}\u2026` : text;
5118
5231
  }
5232
+ var TOOL_ERROR_PREFIX = "[ERROR] ";
5233
+ function isErrorResult(result) {
5234
+ if (!result || typeof result !== "object") return false;
5235
+ const content = result.content;
5236
+ if (!Array.isArray(content) || content.length === 0) return false;
5237
+ const first = content[0];
5238
+ if (!first || first.type !== "text" || typeof first.text !== "string") return false;
5239
+ const trimmed = first.text.trimStart();
5240
+ if (!trimmed.startsWith("{")) return false;
5241
+ try {
5242
+ const parsed = JSON.parse(trimmed);
5243
+ return !!parsed && typeof parsed === "object" && "error" in parsed && parsed.error !== void 0;
5244
+ } catch {
5245
+ return false;
5246
+ }
5247
+ }
5119
5248
  function summarizeToolResult(result) {
5120
5249
  if (!result || typeof result !== "object") return null;
5121
5250
  const content = result.content;
5122
5251
  if (!Array.isArray(content) || content.length === 0) return null;
5123
5252
  const first = content[0];
5124
5253
  if (!first || first.type !== "text" || typeof first.text !== "string") return null;
5125
- return truncateSummary(first.text.replace(/\s+/g, " ").trim());
5254
+ const body = first.text.replace(/\s+/g, " ").trim();
5255
+ const prefix = isErrorResult(result) ? TOOL_ERROR_PREFIX : "";
5256
+ return truncateSummary(prefix + body);
5126
5257
  }
5127
5258
  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";
5259
+ return truncateSummary(TOOL_ERROR_PREFIX + errorMessage(error)) ?? TOOL_ERROR_PREFIX + "Tool failed";
5132
5260
  }
5133
5261
  function buildRepeatedReadSuppressionResult(toolName, repeatedCalls) {
5134
5262
  return {
@@ -5265,6 +5393,7 @@ function createVaultTools(agentId, runId, options) {
5265
5393
  }
5266
5394
  function wrapToolWithAudit(toolDef) {
5267
5395
  const originalHandler = toolDef.handler;
5396
+ const declaredKeys = toolDef.inputSchema && typeof toolDef.inputSchema === "object" ? new Set(Object.keys(toolDef.inputSchema)) : /* @__PURE__ */ new Set();
5268
5397
  return {
5269
5398
  ...toolDef,
5270
5399
  handler: async (args, extra) => {
@@ -5272,6 +5401,28 @@ function createVaultTools(agentId, runId, options) {
5272
5401
  const priorIdenticalCalls = repeatedReadKey ? repeatedReadCounts.get(repeatedReadKey) ?? 0 : 0;
5273
5402
  const turnId = recordTurn(toolDef.name, args);
5274
5403
  try {
5404
+ if (declaredKeys.size > 0 && args && typeof args === "object") {
5405
+ const unknown = [];
5406
+ for (const key of Object.keys(args)) {
5407
+ if (!declaredKeys.has(key)) unknown.push(key);
5408
+ }
5409
+ if (unknown.length > 0) {
5410
+ const accepted = Array.from(declaredKeys).sort().join(", ");
5411
+ const result2 = textResult({
5412
+ error: `Unknown parameter(s) for ${toolDef.name}: ${unknown.join(", ")}. Accepted parameters: ${accepted}.`
5413
+ });
5414
+ if (turnId !== null) {
5415
+ try {
5416
+ updateTurn(turnId, {
5417
+ tool_output_summary: summarizeToolResult(result2),
5418
+ completed_at: epochSeconds()
5419
+ });
5420
+ } catch {
5421
+ }
5422
+ }
5423
+ return result2;
5424
+ }
5425
+ }
5275
5426
  if (priorIdenticalCalls >= REPEATED_READ_FAILURE_THRESHOLD) {
5276
5427
  throw new Error(
5277
5428
  `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 +5556,13 @@ function getIsolatedPluginCacheDir() {
5405
5556
  if (isolatedPluginCacheDir) return isolatedPluginCacheDir;
5406
5557
  const dir = path3.join(os.tmpdir(), `myco-agent-plugin-cache-${process.pid}`);
5407
5558
  fs3.mkdirSync(dir, { recursive: true });
5559
+ const manifestPath = path3.join(dir, "installed_plugins.json");
5560
+ if (!fs3.existsSync(manifestPath)) {
5561
+ fs3.writeFileSync(
5562
+ manifestPath,
5563
+ JSON.stringify({ version: 2, plugins: {} })
5564
+ );
5565
+ }
5408
5566
  isolatedPluginCacheDir = dir;
5409
5567
  return dir;
5410
5568
  }
@@ -5450,6 +5608,18 @@ var ClaudeSdkRuntime = class {
5450
5608
  // user hasn't explicitly overridden the cache dir themselves.
5451
5609
  CLAUDE_CODE_PLUGIN_CACHE_DIR: process.env.CLAUDE_CODE_PLUGIN_CACHE_DIR ?? getIsolatedPluginCacheDir()
5452
5610
  };
5611
+ if (input.logger) {
5612
+ const mcpToolNames = input.toolSurface.toolNames ?? (toolServer ? ["<full-vault-surface>"] : []);
5613
+ input.logger.debug("agent.runtime.request", "Agent runtime request", {
5614
+ runId: input.toolSurface.runId,
5615
+ agentId: input.toolSurface.agentId,
5616
+ model: input.model,
5617
+ mcpToolCount: mcpToolNames.length,
5618
+ mcpTools: mcpToolNames,
5619
+ pluginCacheDir: env.CLAUDE_CODE_PLUGIN_CACHE_DIR,
5620
+ sessionRef: input.sessionRef ?? null
5621
+ });
5622
+ }
5453
5623
  let finalText = "";
5454
5624
  let turnsUsed = 0;
5455
5625
  let inputTokens = 0;
@@ -5464,6 +5634,7 @@ var ClaudeSdkRuntime = class {
5464
5634
  tools: [],
5465
5635
  mcpServers: toolServer ? { [MCP_SERVER_NAME]: toolServer } : {},
5466
5636
  strictMcpConfig: true,
5637
+ settingSources: [],
5467
5638
  maxTurns: input.maxTurns,
5468
5639
  permissionMode: "bypassPermissions",
5469
5640
  allowDangerouslySkipPermissions: true,
@@ -5504,7 +5675,7 @@ var ClaudeSdkRuntime = class {
5504
5675
  } catch (err) {
5505
5676
  if (turnsUsed > 0 || inputTokens > 0 || outputTokens > 0) {
5506
5677
  throw new RuntimeExecutionError(
5507
- err instanceof Error ? err.message : String(err),
5678
+ errorMessage(err),
5508
5679
  { usage: buildUsage(), sessionRef: input.sessionRef },
5509
5680
  { cause: err }
5510
5681
  );
@@ -13069,7 +13240,17 @@ function getAgentRuntime(runtimeId) {
13069
13240
  // src/agent/phase-loop.ts
13070
13241
  async function executePhase(input) {
13071
13242
  const { ctx, phasePrompt, phaseModel, phase, toolSurface, provider, sessionId, sessionData } = input;
13243
+ const logger = ctx.options?.logger;
13072
13244
  const runtime = getAgentRuntime(ctx.config.runtime);
13245
+ logger?.debug("agent.phase.start", `Phase ${phase.name} starting`, {
13246
+ runId: ctx.runId,
13247
+ phase: phase.name,
13248
+ model: phaseModel,
13249
+ maxTurns: phase.maxTurns,
13250
+ required: phase.required ?? false,
13251
+ toolNames: toolSurface.toolNames ?? null,
13252
+ sessionRef: sessionId ?? null
13253
+ });
13073
13254
  try {
13074
13255
  let result;
13075
13256
  try {
@@ -13082,13 +13263,19 @@ async function executePhase(input) {
13082
13263
  sessionRef: sessionId,
13083
13264
  sessionData,
13084
13265
  abortController: ctx.abortController,
13085
- toolSurface
13266
+ toolSurface,
13267
+ logger
13086
13268
  });
13087
13269
  } catch (error) {
13088
- if (!sessionId || !runtime.supports("supportsSessionResume") || !isSessionResumeFailure(error)) {
13270
+ if (!sessionId || !runtime.supports("supportsSessionResume") || !isSessionResumeFailure(error) && !isExpiredSessionError(error)) {
13089
13271
  throw error;
13090
13272
  }
13091
- console.warn(`[agent] Resuming phase "${phase.name}" session failed, retrying without prior session`);
13273
+ logger?.info("agent.phase.session-retry", `Phase ${phase.name} session failed, retrying without prior session`, {
13274
+ runId: ctx.runId,
13275
+ phase: phase.name,
13276
+ priorSession: sessionId,
13277
+ error: errorMessage(error)
13278
+ });
13092
13279
  result = await runtime.execute({
13093
13280
  prompt: phasePrompt,
13094
13281
  model: phaseModel,
@@ -13096,14 +13283,24 @@ async function executePhase(input) {
13096
13283
  systemPrompt: ctx.systemPrompt,
13097
13284
  provider,
13098
13285
  abortController: ctx.abortController,
13099
- toolSurface
13286
+ toolSurface,
13287
+ logger
13100
13288
  });
13101
13289
  }
13102
- if (phase.maxTurns && result.turnsUsed > 0) {
13103
- console.log(`[agent] Phase "${phase.name}": num_turns=${result.turnsUsed}, budget=${phase.maxTurns}`);
13104
- }
13290
+ logger?.debug("agent.phase.end", `Phase ${phase.name} finished`, {
13291
+ runId: ctx.runId,
13292
+ phase: phase.name,
13293
+ status: "completed",
13294
+ turnsUsed: result.turnsUsed,
13295
+ maxTurns: phase.maxTurns ?? null,
13296
+ tokensUsed: result.usage.totalTokens ?? 0,
13297
+ costUsd: result.usage.costUsd ?? null
13298
+ });
13105
13299
  if (phase.required && result.turnsUsed === 0) {
13106
- console.warn(`[agent] Required phase "${phase.name}" produced 0 turns`);
13300
+ logger?.warn("agent.phase.zero-turns", `Required phase ${phase.name} produced 0 turns`, {
13301
+ runId: ctx.runId,
13302
+ phase: phase.name
13303
+ });
13107
13304
  }
13108
13305
  const costData = await resolveCost({
13109
13306
  runtime: ctx.config.runtime,
@@ -13129,6 +13326,15 @@ async function executePhase(input) {
13129
13326
  model: phaseModel,
13130
13327
  usage: telemetry.usage
13131
13328
  }) : void 0;
13329
+ logger?.debug("agent.phase.end", `Phase ${phase.name} failed`, {
13330
+ runId: ctx.runId,
13331
+ phase: phase.name,
13332
+ status: "failed",
13333
+ turnsUsed: telemetry?.usage.requests ?? 0,
13334
+ tokensUsed: telemetry?.usage.totalTokens ?? 0,
13335
+ costUsd: telemetry?.usage.costUsd ?? null,
13336
+ error: abortReason ?? errorMessage(err)
13337
+ });
13132
13338
  return buildPhaseResult({
13133
13339
  name: phase.name,
13134
13340
  status: "failed",
@@ -13146,23 +13352,41 @@ async function executeSingleQuery(ctx, taskPrompt, provider, sessionRef, session
13146
13352
  provider,
13147
13353
  ctx.config.model
13148
13354
  );
13149
- const result = await runtime.execute({
13355
+ const toolSurface = {
13356
+ agentId: ctx.agentId,
13357
+ runId: ctx.runId,
13358
+ vaultDir: ctx.vaultDir,
13359
+ embeddingManager: ctx.embeddingManager,
13360
+ dryRun: ctx.config.dryRun ?? false
13361
+ };
13362
+ const baseInput = {
13150
13363
  prompt: taskPrompt,
13151
13364
  model: effectiveModel,
13152
13365
  maxTurns: ctx.config.maxTurns,
13153
13366
  systemPrompt: ctx.systemPrompt,
13154
13367
  provider,
13155
13368
  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
13369
+ toolSurface,
13370
+ logger: ctx.options?.logger
13371
+ };
13372
+ let result;
13373
+ try {
13374
+ result = await runtime.execute({ ...baseInput, sessionRef, sessionData });
13375
+ } catch (err) {
13376
+ if (!sessionRef || !runtime.supports("supportsSessionResume") || !isSessionResumeFailure(err) && !isExpiredSessionError(err)) {
13377
+ throw err;
13164
13378
  }
13165
- });
13379
+ ctx.options?.logger?.info(
13380
+ "agent.single-query.session-retry",
13381
+ "Single-query session failed, retrying without prior session",
13382
+ {
13383
+ runId: ctx.runId,
13384
+ priorSession: sessionRef,
13385
+ error: errorMessage(err)
13386
+ }
13387
+ );
13388
+ result = await runtime.execute(baseInput);
13389
+ }
13166
13390
  const costData = await resolveCost({
13167
13391
  runtime: ctx.config.runtime,
13168
13392
  provider,
@@ -13210,10 +13434,17 @@ async function executePhasedQuery(ctx) {
13210
13434
  vaultDir: ctx.vaultDir,
13211
13435
  dryRun: config.dryRun ?? false
13212
13436
  },
13213
- abortController: ctx.abortController
13437
+ abortController: ctx.abortController,
13438
+ logger: ctx.options?.logger
13214
13439
  });
13215
- const plan = parseOrchestratorPlan(planResponse.finalText, phases);
13440
+ const plan = parseOrchestratorPlan(planResponse.finalText, phases, ctx.options?.logger);
13216
13441
  effectivePhases = applyDirectives(phases, plan.phases);
13442
+ ctx.options?.logger?.debug("agent.orchestrator.plan", "Orchestrator plan applied", {
13443
+ runId,
13444
+ reasoning: plan.reasoning,
13445
+ effectivePhases: effectivePhases.map((p) => p.name),
13446
+ skippedPhases: plan.phases.filter((d) => d.skip).map((d) => d.name)
13447
+ });
13217
13448
  }
13218
13449
  const declarationOrder = new Map(phases.map((p, i) => [p.name, i]));
13219
13450
  const waves = computeWaves(effectivePhases);
@@ -13373,12 +13604,16 @@ var SKIP_REASON_ALREADY_RUNNING = "already_running";
13373
13604
  var CORTEX_INSTRUCTIONS_REPORT_ACTION = "cortex_instructions";
13374
13605
  var CORTEX_INSTRUCTIONS_CONTENT_KEY = "content";
13375
13606
  var TOKEN_BUDGET_PRESSURE_STATUSES = /* @__PURE__ */ new Set(["warning", "post_run_pressure"]);
13376
- function logTokenBudgetPressure(taskName, usage, provider) {
13607
+ function logTokenBudgetPressure(taskName, usage, provider, logger) {
13377
13608
  const budget = analyzeRuntimeTokenBudget(usage, provider);
13378
13609
  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
- );
13610
+ logger?.warn("agent.token-budget-pressure", `${taskName} token budget ${budget.status}`, {
13611
+ task: taskName,
13612
+ status: budget.status,
13613
+ utilizationPercent: budget.utilizationPercent,
13614
+ contextWindowTokens: budget.contextWindowTokens,
13615
+ peakRequestTotalTokens: budget.peakRequestTotalTokens
13616
+ });
13382
13617
  }
13383
13618
  async function runAgent(vaultDir, options) {
13384
13619
  const db = initDatabase(vaultDbPath(vaultDir));
@@ -13523,15 +13758,25 @@ async function runAgent(vaultDir, options) {
13523
13758
  taskProviderOverride = resolved.taskProvider;
13524
13759
  phaseProviderOverrides = resolved.phaseOverrides;
13525
13760
  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.`
13761
+ options?.logger?.warn(
13762
+ "agent.ollama.context-variant-conflict",
13763
+ `Ollama model "${conflict.model}" referenced with conflicting context_length values \u2014 reconciled to ${conflict.resolved}`,
13764
+ {
13765
+ model: conflict.model,
13766
+ values: conflict.values,
13767
+ resolved: conflict.resolved
13768
+ }
13528
13769
  );
13529
13770
  }
13530
13771
  }
13531
13772
  const taskAbortController = new AbortController();
13532
13773
  const timeoutMs = config.timeoutSeconds * MS_PER_SECOND;
13533
13774
  const timeoutId = setTimeout(() => {
13534
- console.warn(`[agent] Run ${runId} exceeded timeout (${config.timeoutSeconds}s), aborting`);
13775
+ options?.logger?.warn("agent.run.timeout", `Run ${runId} exceeded timeout, aborting`, {
13776
+ runId,
13777
+ taskName: config.taskName,
13778
+ timeoutSeconds: config.timeoutSeconds
13779
+ });
13535
13780
  taskAbortController.abort(new Error(`Agent run timed out after ${config.timeoutSeconds} seconds`));
13536
13781
  }, timeoutMs);
13537
13782
  timeoutId.unref?.();
@@ -13623,7 +13868,7 @@ async function runAgent(vaultDir, options) {
13623
13868
  }
13624
13869
  }
13625
13870
  clearTimeout(timeoutId);
13626
- logTokenBudgetPressure(config.taskName, usage, effectiveProvider);
13871
+ logTokenBudgetPressure(config.taskName, usage, effectiveProvider, options?.logger);
13627
13872
  await finalizeOnTaskSuccess({
13628
13873
  taskName: config.taskName,
13629
13874
  agentId,
@@ -13677,34 +13922,48 @@ ${err.stack.split("\n").slice(0, 3).join("\n")}`;
13677
13922
  }
13678
13923
  }
13679
13924
  const failedAt = epochSeconds();
13680
- console.error(`[agent] Run ${runId} failed: ${errorMessage2}`);
13925
+ options?.logger?.error("agent.run.failed", `Run ${runId} failed`, {
13926
+ runId,
13927
+ taskName: config.taskName,
13928
+ error: errorMessage2
13929
+ });
13681
13930
  try {
13682
13931
  const usage = aggregateUsage(phaseResults?.map((phase) => phase.usage) ?? []);
13683
- logTokenBudgetPressure(config.taskName, usage, effectiveProvider);
13932
+ logTokenBudgetPressure(config.taskName, usage, effectiveProvider, options?.logger);
13684
13933
  const costData = phaseResults ? summarizePhaseCosts(phaseResults) : await resolveCost({
13685
13934
  runtime: runtimeId,
13686
13935
  provider: effectiveProvider,
13687
13936
  model: effectiveModel,
13688
13937
  usage
13689
13938
  });
13939
+ const hadPriorSession = Boolean(checkpointState.sessionRef) || Object.values(checkpointState.phases).some((phase) => Boolean(phase.sessionRef));
13940
+ const recordedAnyTurns = (usage.requests ?? 0) > 0 || (phaseResults?.some((phase) => phase.turnsUsed > 0) ?? false);
13941
+ const sessionExpired = Boolean(options?.resumeRunId) && hadPriorSession && !recordedAnyTurns && isExpiredSessionError(err);
13942
+ const accountingUpdate = buildRunAccountingUpdate({
13943
+ runtime: runtimeId,
13944
+ provider: effectiveProvider,
13945
+ model: effectiveModel,
13946
+ checkpointState,
13947
+ usage,
13948
+ costData,
13949
+ phaseResults
13950
+ });
13951
+ if (sessionExpired) {
13952
+ accountingUpdate.checkpoints = null;
13953
+ }
13690
13954
  updateRunStatus(runId, STATUS_FAILED, {
13691
- resumable: 1,
13692
- resume_status: RESUME_STATUS_READY,
13955
+ resumable: sessionExpired ? 0 : 1,
13956
+ resume_status: sessionExpired ? RESUME_STATUS_SESSION_EXPIRED : RESUME_STATUS_READY,
13693
13957
  completed_at: failedAt,
13694
13958
  tokens_used: usage.totalTokens ?? phaseResults?.reduce((sum, phase) => sum + phase.tokensUsed, 0) ?? void 0,
13695
13959
  error: errorMessage2,
13696
- ...buildRunAccountingUpdate({
13697
- runtime: runtimeId,
13698
- provider: effectiveProvider,
13699
- model: effectiveModel,
13700
- checkpointState,
13701
- usage,
13702
- costData,
13703
- phaseResults
13704
- })
13960
+ ...accountingUpdate
13705
13961
  });
13706
13962
  } catch (dbErr) {
13707
- console.error(`[agent] Failed to save error to DB:`, dbErr);
13963
+ options?.logger?.error("agent.run.db-save-failed", `Failed to save error to DB for run ${runId}`, {
13964
+ runId,
13965
+ error: errorMessage(dbErr)
13966
+ });
13708
13967
  }
13709
13968
  await cleanupOnTaskFailure({
13710
13969
  taskName: config.taskName,
@@ -13841,4 +14100,4 @@ export {
13841
14100
  cleanupOnTaskFailure,
13842
14101
  finalizeOnTaskSuccess
13843
14102
  };
13844
- //# sourceMappingURL=chunk-NGH7U6A3.js.map
14103
+ //# sourceMappingURL=chunk-X2IRGXGF.js.map