thoth-agents 0.1.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +55 -12
  2. package/dist/agents/prompt-dialects.d.ts +9 -0
  3. package/dist/{chunk-6K3ZXIMC.js → chunk-3NOVCFN7.js} +88 -29
  4. package/dist/chunk-4WYCZ5Z7.js +698 -0
  5. package/dist/{chunk-SOT5ZY53.js → chunk-WH3F3GWE.js} +1498 -350
  6. package/dist/cli/claude-code-install.d.ts +53 -0
  7. package/dist/cli/claude-code-paths.d.ts +31 -0
  8. package/dist/cli/codex-install.d.ts +1 -5
  9. package/dist/cli/commands.d.ts +1 -1
  10. package/dist/cli/index.js +85 -27
  11. package/dist/cli/managed-state-io.d.ts +16 -0
  12. package/dist/cli/operations/claude-code.d.ts +21 -0
  13. package/dist/cli/tui/index.js +87 -9
  14. package/dist/cli/tui/operations.d.ts +2 -0
  15. package/dist/cli/types.d.ts +3 -3
  16. package/dist/config/index.d.ts +1 -1
  17. package/dist/config/schema.d.ts +11 -0
  18. package/dist/config/utils.d.ts +5 -0
  19. package/dist/harness/adapters/claude-code.d.ts +24 -0
  20. package/dist/harness/core/memory-governance.d.ts +39 -4
  21. package/dist/harness/core/package-version.d.ts +7 -0
  22. package/dist/harness/types.d.ts +1 -1
  23. package/dist/harness/writers/claude-code-plugin-package.d.ts +32 -0
  24. package/dist/harness/writers/claude-code-skill-layout.d.ts +16 -0
  25. package/dist/harness/writers/claude-code-subagent.d.ts +26 -0
  26. package/dist/harness/writers/fs-skill-collect.d.ts +7 -0
  27. package/dist/hooks/phase-reminder/index.d.ts +2 -0
  28. package/dist/hooks/thoth-mem/protocol.d.ts +2 -2
  29. package/dist/index.js +54 -512
  30. package/package.json +1 -1
  31. package/src/skills/_shared/persistence-contract.md +18 -14
  32. package/src/skills/_shared/thoth-mem-convention.md +18 -18
  33. package/src/skills/executing-plans/SKILL.md +6 -4
  34. package/src/skills/plan-reviewer/SKILL.md +4 -2
  35. package/src/skills/thoth-mem-agents/SKILL.md +3 -0
  36. package/thoth-agents.schema.json +16 -0
  37. package/dist/chunk-DYGVRAMS.js +0 -182
package/dist/index.js CHANGED
@@ -1,492 +1,24 @@
1
1
  import {
2
+ createAgents,
3
+ renderOpenCodeAgentConfigs,
2
4
  spawn,
3
5
  spawnSync
4
- } from "./chunk-DYGVRAMS.js";
6
+ } from "./chunk-4WYCZ5Z7.js";
5
7
  import {
6
- DEFAULT_MODELS,
7
8
  DEFAULT_THOTH_COMMAND,
8
- OPENCODE_PROMPT_DIALECT,
9
9
  POLL_INTERVAL_BACKGROUND_MS,
10
- SUBAGENT_NAMES,
11
- appendPromptSections,
12
- composeAgentPrompt,
13
10
  context7,
14
- createOrchestratorPromptSections,
15
- createReadOnlySpecialistPromptSections,
16
- createWriteCapableSpecialistPromptSections,
17
- detectModelFamily,
18
11
  exa,
19
- getAgentOverride,
20
- getModelFamilyPromptSection,
21
12
  getOpenCodeConfigPaths,
22
- getStepBudgetPromptSection,
23
13
  grep_app,
24
14
  installCustomSkills,
25
- loadAgentPrompt,
26
15
  loadPluginConfig,
27
- renderRolePrompt,
28
16
  stripJsonComments
29
- } from "./chunk-6K3ZXIMC.js";
17
+ } from "./chunk-3NOVCFN7.js";
30
18
 
31
19
  // src/index.ts
32
20
  import path4 from "path";
33
21
 
34
- // src/agents/deep.ts
35
- var DEEP_PROMPT = renderRolePrompt(
36
- createWriteCapableSpecialistPromptSections("deep"),
37
- OPENCODE_PROMPT_DIALECT
38
- );
39
- function createDeepAgent(model, customPrompt, customAppendPrompt) {
40
- const prompt = composeAgentPrompt({
41
- basePrompt: DEEP_PROMPT,
42
- customPrompt,
43
- customAppendPrompt: appendPromptSections(
44
- getModelFamilyPromptSection("deep", model),
45
- customAppendPrompt
46
- )
47
- });
48
- return {
49
- name: "deep",
50
- description: "Synchronous write-capable implementation agent optimized for thorough context analysis, edge cases, and correctness \u2014 not for bulk mechanical changes.",
51
- config: {
52
- model,
53
- temperature: 0.1,
54
- prompt,
55
- color: "secondary"
56
- // steps: 80,
57
- }
58
- };
59
- }
60
-
61
- // src/agents/designer.ts
62
- var DESIGNER_PROMPT = renderRolePrompt(
63
- createWriteCapableSpecialistPromptSections("designer"),
64
- OPENCODE_PROMPT_DIALECT
65
- );
66
- function createDesignerAgent(model, customPrompt, customAppendPrompt) {
67
- const prompt = composeAgentPrompt({
68
- basePrompt: DESIGNER_PROMPT,
69
- customPrompt,
70
- customAppendPrompt: appendPromptSections(
71
- getModelFamilyPromptSection("designer", model),
72
- customAppendPrompt
73
- )
74
- });
75
- return {
76
- name: "designer",
77
- description: "Synchronous write-capable UI/UX implementation agent with ownership of approach, execution, and visual verification.",
78
- config: {
79
- model,
80
- temperature: 0.4,
81
- prompt,
82
- color: "accent"
83
- // steps: 50,
84
- }
85
- };
86
- }
87
-
88
- // src/agents/explorer.ts
89
- var EXPLORER_PROMPT = renderRolePrompt(
90
- createReadOnlySpecialistPromptSections("explorer"),
91
- OPENCODE_PROMPT_DIALECT
92
- );
93
- function createExplorerAgent(model, customPrompt, customAppendPrompt) {
94
- const prompt = composeAgentPrompt({
95
- basePrompt: EXPLORER_PROMPT,
96
- customPrompt,
97
- customAppendPrompt: appendPromptSections(
98
- getModelFamilyPromptSection("explorer", model),
99
- customAppendPrompt
100
- )
101
- });
102
- return {
103
- name: "explorer",
104
- description: "Read-only local discovery agent for fast codebase search, references, and repository mapping.",
105
- config: {
106
- model,
107
- temperature: 0.1,
108
- prompt,
109
- color: "info"
110
- }
111
- };
112
- }
113
-
114
- // src/agents/librarian.ts
115
- var LIBRARIAN_PROMPT = renderRolePrompt(
116
- createReadOnlySpecialistPromptSections("librarian"),
117
- OPENCODE_PROMPT_DIALECT
118
- );
119
- function createLibrarianAgent(model, customPrompt, customAppendPrompt) {
120
- const prompt = composeAgentPrompt({
121
- basePrompt: LIBRARIAN_PROMPT,
122
- customPrompt,
123
- customAppendPrompt: appendPromptSections(
124
- getModelFamilyPromptSection("librarian", model),
125
- customAppendPrompt
126
- )
127
- });
128
- return {
129
- name: "librarian",
130
- description: "Read-only research agent for official docs, public examples, and externally sourced implementation guidance.",
131
- config: {
132
- model,
133
- temperature: 0.1,
134
- prompt,
135
- color: "info"
136
- }
137
- };
138
- }
139
-
140
- // src/agents/oracle.ts
141
- var ORACLE_PROMPT = renderRolePrompt(
142
- createReadOnlySpecialistPromptSections("oracle"),
143
- OPENCODE_PROMPT_DIALECT
144
- );
145
- function createOracleAgent(model, customPrompt, customAppendPrompt) {
146
- const prompt = composeAgentPrompt({
147
- basePrompt: ORACLE_PROMPT,
148
- customPrompt,
149
- customAppendPrompt: appendPromptSections(
150
- getModelFamilyPromptSection("oracle", model),
151
- customAppendPrompt
152
- )
153
- });
154
- return {
155
- name: "oracle",
156
- description: "Synchronous read-only strategic advisor for debugging, architecture, code review, and SDD plan review.",
157
- config: {
158
- model,
159
- temperature: 0.1,
160
- prompt,
161
- color: "warning"
162
- }
163
- };
164
- }
165
-
166
- // src/agents/orchestrator.ts
167
- var ORCHESTRATOR_PROMPT = renderRolePrompt(
168
- createOrchestratorPromptSections(),
169
- OPENCODE_PROMPT_DIALECT
170
- );
171
- function createOrchestratorAgent(model, customPrompt, customAppendPrompt) {
172
- const prompt = composeAgentPrompt({
173
- basePrompt: ORCHESTRATOR_PROMPT,
174
- customPrompt,
175
- customAppendPrompt: appendPromptSections(
176
- getModelFamilyPromptSection("orchestrator", model),
177
- customAppendPrompt
178
- )
179
- });
180
- const definition = {
181
- name: "orchestrator",
182
- description: "Delegate-first coordinator for SDD workflow, specialist dispatch, and root-session memory ownership.",
183
- config: {
184
- temperature: 0.1,
185
- prompt,
186
- color: "primary"
187
- // steps: 100,
188
- }
189
- };
190
- if (Array.isArray(model)) {
191
- definition._modelArray = model.map(
192
- (entry) => typeof entry === "string" ? { id: entry } : entry
193
- );
194
- } else if (typeof model === "string" && model) {
195
- definition.config.model = model;
196
- }
197
- return definition;
198
- }
199
-
200
- // src/agents/quick.ts
201
- var QUICK_PROMPT = renderRolePrompt(
202
- createWriteCapableSpecialistPromptSections("quick"),
203
- OPENCODE_PROMPT_DIALECT
204
- );
205
- function createQuickAgent(model, customPrompt, customAppendPrompt) {
206
- const prompt = composeAgentPrompt({
207
- basePrompt: QUICK_PROMPT,
208
- customPrompt,
209
- customAppendPrompt: appendPromptSections(
210
- getModelFamilyPromptSection("quick", model),
211
- customAppendPrompt
212
- )
213
- });
214
- return {
215
- name: "quick",
216
- description: "Synchronous write-capable implementation agent optimized for fast, mechanical, well-bounded changes \u2014 including uniform patterns across multiple files.",
217
- config: {
218
- model,
219
- temperature: 0.2,
220
- prompt,
221
- color: "success"
222
- // steps: 30,
223
- }
224
- };
225
- }
226
-
227
- // src/agents/index.ts
228
- var GEMINI_DEFAULT_STEPS = {
229
- explorer: 120,
230
- librarian: 80,
231
- oracle: 80,
232
- designer: 80,
233
- quick: 40,
234
- deep: 120
235
- };
236
- var BUILTIN_PERMISSION_PRESETS = {
237
- orchestrator: {
238
- read: "allow",
239
- edit: "allow",
240
- write: "allow",
241
- glob: "allow",
242
- grep: "allow",
243
- list: "allow",
244
- bash: "allow",
245
- codesearch: "allow",
246
- lsp: "allow",
247
- skill: "allow",
248
- question: "allow",
249
- webfetch: "allow",
250
- exa: "allow",
251
- todowrite: "allow",
252
- task: "allow",
253
- external_directory: "allow"
254
- },
255
- explorer: {
256
- read: "allow",
257
- glob: "allow",
258
- grep: "allow",
259
- list: "allow",
260
- codesearch: "allow",
261
- lsp: "allow",
262
- external_directory: "allow",
263
- bash: "allow",
264
- question: "allow",
265
- skill: "allow",
266
- edit: "deny",
267
- todowrite: "deny",
268
- task: "deny"
269
- },
270
- librarian: {
271
- read: "allow",
272
- glob: "allow",
273
- grep: "allow",
274
- external_directory: "allow",
275
- bash: "allow",
276
- webfetch: "allow",
277
- exa: "allow",
278
- codesearch: "allow",
279
- question: "allow",
280
- skill: "allow",
281
- edit: "deny",
282
- todowrite: "deny",
283
- task: "deny"
284
- },
285
- oracle: {
286
- read: "allow",
287
- glob: "allow",
288
- grep: "allow",
289
- list: "allow",
290
- lsp: "allow",
291
- codesearch: "allow",
292
- webfetch: "allow",
293
- exa: "allow",
294
- external_directory: "allow",
295
- bash: "allow",
296
- question: "allow",
297
- skill: "allow",
298
- edit: "deny",
299
- todowrite: "deny",
300
- task: "deny"
301
- },
302
- designer: {
303
- read: "allow",
304
- edit: "allow",
305
- glob: "allow",
306
- grep: "allow",
307
- list: "allow",
308
- bash: "allow",
309
- codesearch: "allow",
310
- lsp: "allow",
311
- skill: "allow",
312
- question: "allow",
313
- todowrite: "deny",
314
- task: "deny",
315
- external_directory: {
316
- "~/.config/opencode/skills/**": "allow"
317
- }
318
- },
319
- quick: {
320
- read: "allow",
321
- edit: "allow",
322
- glob: "allow",
323
- grep: "allow",
324
- list: "allow",
325
- bash: "allow",
326
- question: "allow",
327
- codesearch: "allow",
328
- lsp: "allow",
329
- skill: "allow",
330
- todowrite: "deny",
331
- task: "deny",
332
- external_directory: {
333
- "~/.config/opencode/skills/**": "allow"
334
- }
335
- },
336
- deep: {
337
- read: "allow",
338
- edit: "allow",
339
- glob: "allow",
340
- grep: "allow",
341
- list: "allow",
342
- bash: "allow",
343
- codesearch: "allow",
344
- lsp: "allow",
345
- skill: "allow",
346
- question: "allow",
347
- webfetch: "allow",
348
- exa: "allow",
349
- todowrite: "deny",
350
- task: "deny",
351
- external_directory: {
352
- "~/.config/opencode/skills/**": "allow"
353
- }
354
- }
355
- };
356
- function normalizeModelArray(model) {
357
- return model.map(
358
- (entry) => typeof entry === "string" ? { id: entry } : entry
359
- );
360
- }
361
- function applyOverrides(agent, override) {
362
- if (override.model) {
363
- if (Array.isArray(override.model)) {
364
- agent._modelArray = normalizeModelArray(override.model);
365
- agent.config.model = void 0;
366
- } else {
367
- agent.config.model = override.model;
368
- }
369
- }
370
- if (override.variant) {
371
- agent.config.variant = override.variant;
372
- }
373
- if (override.temperature !== void 0) {
374
- agent.config.temperature = override.temperature;
375
- }
376
- if (override.steps !== void 0) {
377
- agent.config.steps = override.steps;
378
- }
379
- }
380
- function applyStepBudgetPrompt(agent) {
381
- const stepBudgetPrompt = getStepBudgetPromptSection(agent.config.steps);
382
- if (!stepBudgetPrompt) {
383
- return;
384
- }
385
- agent.config.prompt = appendPromptSections(
386
- agent.config.prompt,
387
- stepBudgetPrompt
388
- );
389
- }
390
- function applyGeminiDefaultSteps(agent) {
391
- if (!isSubagent(agent.name) || agent.config.steps !== void 0) {
392
- return;
393
- }
394
- if (detectModelFamily(agent._modelArray ?? agent.config.model) !== "gemini") {
395
- return;
396
- }
397
- agent.config.steps = GEMINI_DEFAULT_STEPS[agent.name];
398
- }
399
- function clonePermissionConfig(permission) {
400
- if (typeof permission === "string") {
401
- return permission;
402
- }
403
- return Object.fromEntries(
404
- Object.entries(permission).map(([key, value]) => [
405
- key,
406
- value && typeof value === "object" && !Array.isArray(value) ? { ...value } : value
407
- ])
408
- );
409
- }
410
- function getBuiltinPermissionPreset(name) {
411
- return clonePermissionConfig(BUILTIN_PERMISSION_PRESETS[name]);
412
- }
413
- function getExplicitPermissionOverride(override) {
414
- return override?.permission;
415
- }
416
- function getPrimaryModelForPrompt(model) {
417
- if (Array.isArray(model)) {
418
- const first = model[0];
419
- return typeof first === "string" ? first : first?.id;
420
- }
421
- return model;
422
- }
423
- function isSubagent(name) {
424
- return SUBAGENT_NAMES.includes(name);
425
- }
426
- var SUBAGENT_FACTORIES = {
427
- explorer: createExplorerAgent,
428
- librarian: createLibrarianAgent,
429
- oracle: createOracleAgent,
430
- designer: createDesignerAgent,
431
- quick: createQuickAgent,
432
- deep: createDeepAgent
433
- };
434
- function createAgents(config) {
435
- const protoSubAgents = Object.entries(SUBAGENT_FACTORIES).map(([name, factory]) => {
436
- const override = getAgentOverride(config, name);
437
- const prompts = loadAgentPrompt(name, config?.preset);
438
- const model = getPrimaryModelForPrompt(override?.model) ?? DEFAULT_MODELS[name];
439
- return factory(model, prompts.prompt, prompts.appendPrompt);
440
- });
441
- const allSubAgents = protoSubAgents.map((agent) => {
442
- const override = getAgentOverride(config, agent.name);
443
- if (override) {
444
- applyOverrides(agent, override);
445
- }
446
- applyGeminiDefaultSteps(agent);
447
- applyStepBudgetPrompt(agent);
448
- return agent;
449
- });
450
- const orchestratorOverride = getAgentOverride(config, "orchestrator");
451
- const orchestratorPrompts = loadAgentPrompt("orchestrator", config?.preset);
452
- const orchestrator = createOrchestratorAgent(
453
- orchestratorOverride?.model ?? DEFAULT_MODELS.orchestrator,
454
- orchestratorPrompts.prompt,
455
- orchestratorPrompts.appendPrompt
456
- );
457
- if (orchestratorOverride) {
458
- applyOverrides(orchestrator, orchestratorOverride);
459
- }
460
- applyStepBudgetPrompt(orchestrator);
461
- return [orchestrator, ...allSubAgents];
462
- }
463
- function getAgentConfigs(config) {
464
- const agents = createAgents(config);
465
- return Object.fromEntries(
466
- agents.map((agent) => {
467
- const override = getAgentOverride(config, agent.name);
468
- const sdkConfig = {
469
- ...agent.config,
470
- description: agent.description
471
- };
472
- const builtinPermission = isSubagent(agent.name) ? getBuiltinPermissionPreset(agent.name) : agent.name === "orchestrator" ? getBuiltinPermissionPreset("orchestrator") : void 0;
473
- const explicitPermissionOverride = getExplicitPermissionOverride(override);
474
- sdkConfig.permission = explicitPermissionOverride ?? agent.config.permission ?? builtinPermission;
475
- if (isSubagent(agent.name)) {
476
- sdkConfig.mode = "subagent";
477
- } else if (agent.name === "orchestrator") {
478
- sdkConfig.mode = "primary";
479
- }
480
- return [agent.name, sdkConfig];
481
- })
482
- );
483
- }
484
-
485
- // src/harness/adapters/opencode.ts
486
- function renderOpenCodeAgentConfigs(config) {
487
- return getAgentConfigs(config);
488
- }
489
-
490
22
  // src/utils/logger.ts
491
23
  import { createRequire } from "module";
492
24
  var require2 = createRequire(import.meta.url);
@@ -1744,6 +1276,14 @@ Understand \u2192 split discovery into surgical probes with explorer/librarian \
1744
1276
  If delegating, write sub-agent prompts in English and launch the specialist in the same turn you mention it. If multiple delegations are independent, emit all tool calls in a single response.
1745
1277
  Before write-capable dispatch, give concrete scope, anchors, steps, non-goals, and verification.
1746
1278
  In SDD, after oracle returns [OKAY], give a deep approved-plan overview, then ask the user before implementation.</reminder>`;
1279
+ var PHASE_REMINDER_SEPARATOR = "\n\n---\n\n";
1280
+ function stripPhaseReminder(text) {
1281
+ if (!text) {
1282
+ return "";
1283
+ }
1284
+ const prefix = `${PHASE_REMINDER}${PHASE_REMINDER_SEPARATOR}`;
1285
+ return text.startsWith(prefix) ? text.slice(prefix.length) : text;
1286
+ }
1747
1287
  function createPhaseReminderHook() {
1748
1288
  return {
1749
1289
  "experimental.chat.messages.transform": async (_input, output) => {
@@ -1776,11 +1316,7 @@ function createPhaseReminderHook() {
1776
1316
  if (originalText.includes(LITE_INTERNAL_INITIATOR_MARKER)) {
1777
1317
  return;
1778
1318
  }
1779
- lastUserMessage.parts[textPartIndex].text = `${PHASE_REMINDER}
1780
-
1781
- ---
1782
-
1783
- ${originalText}`;
1319
+ lastUserMessage.parts[textPartIndex].text = `${PHASE_REMINDER}${PHASE_REMINDER_SEPARATOR}${originalText}`;
1784
1320
  }
1785
1321
  };
1786
1322
  }
@@ -2013,8 +1549,8 @@ function createThothClient(options) {
2013
1549
 
2014
1550
  // src/hooks/thoth-mem/protocol.ts
2015
1551
  var SDD_TOPIC_KEY_FORMAT = "sdd/{change}/{artifact}";
2016
- var FIRST_ACTION_INSTRUCTION = 'FIRST ACTION REQUIRED: Call mem_session_summary with the content of the compacted summary. This preserves what was accomplished before compaction. Do this BEFORE any other work. Then call mem_context for a recent-session overview, and use the 3-layer recall protocol (mem_search with mode "compact" -> mem_timeline -> mem_get_observation) for precise retrieval.';
2017
- var SESSION_SUMMARY_TEMPLATE = `Use this exact structure for \`mem_session_summary\` content:
1552
+ var FIRST_ACTION_INSTRUCTION = 'FIRST ACTION REQUIRED: Call mem_session(action="summary") with the content of the compacted summary. This preserves what was accomplished before compaction. Do this BEFORE any other work. Then call mem_context(recall_query=...) for fused recent context, and use the recall funnel (mem_recall(mode="compact") -> mem_recall(mode="context") -> mem_get(...)) for precise retrieval.';
1553
+ var SESSION_SUMMARY_TEMPLATE = `Use this exact structure for \`mem_session(action="summary")\` content:
2018
1554
 
2019
1555
  ## Goal
2020
1556
  [What we were working on this session]
@@ -2034,10 +1570,10 @@ var SESSION_SUMMARY_TEMPLATE = `Use this exact structure for \`mem_session_summa
2034
1570
  ## Relevant Files
2035
1571
  - path/to/file.ts - [what it does or what changed]`;
2036
1572
  function buildCompactionReminder(sessionID) {
2037
- return `FIRST ACTION REQUIRED: this session was compacted. Call \`mem_session_summary\` with the content of the compacted summary and \`session_id\` \`${sessionID}\`. This preserves what was accomplished before compaction. Do this BEFORE any other work. After that, call \`mem_capture_passive\` if the summary includes \`## Key Learnings:\`, call \`mem_context\` for a recent-session overview, and use the 3-layer recall protocol (\`mem_search\` with \`mode: "compact"\` -> \`mem_timeline\` -> \`mem_get_observation\`) for precise retrieval.`;
1573
+ return `FIRST ACTION REQUIRED: this session was compacted. Call \`mem_session(action="summary")\` with the content of the compacted summary and \`session_id\` \`${sessionID}\`. This preserves what was accomplished before compaction. Do this BEFORE any other work. After that, call \`mem_context(recall_query=...)\` for fused recent context, and use the recall funnel (\`mem_recall(mode="compact")\` -> \`mem_recall(mode="context")\` -> \`mem_get(...)\`) for precise retrieval.`;
2038
1574
  }
2039
1575
  function buildCompactorInstruction(project) {
2040
- return `CRITICAL INSTRUCTION: place this at the TOP of the compacted summary exactly as an action item for the resumed agent: "FIRST ACTION REQUIRED: Call mem_session_summary with the content of this compacted summary. Use project: '${project}'. This preserves what was accomplished before compaction. Do this BEFORE any other work."`;
1576
+ return `CRITICAL INSTRUCTION: place this at the TOP of the compacted summary exactly as an action item for the resumed agent: "FIRST ACTION REQUIRED: Call mem_session(action="summary") with the content of this compacted summary. Use project: '${project}'. This preserves what was accomplished before compaction. Do this BEFORE any other work."`;
2041
1577
  }
2042
1578
  function buildMemoryInstructions(sessionID, project) {
2043
1579
  return `
@@ -2045,18 +1581,20 @@ function buildMemoryInstructions(sessionID, project) {
2045
1581
  Persistent memory is available through thoth-mem. Follow this protocol.
2046
1582
 
2047
1583
  IMPORTANT: Your current session_id is \`${sessionID}\` and project is \`${project}\`.
2048
- Always pass these values when calling memory tools that accept them (mem_session_summary, mem_save, mem_capture_passive, etc.).
1584
+ Always pass these values when calling memory tools that accept them.
2049
1585
 
2050
1586
  ### CORE TOOLS
2051
- mem_save, mem_search, mem_context, mem_session_summary, mem_get_observation, mem_save_prompt, mem_update, mem_suggest_topic_key, mem_timeline, mem_capture_passive
1587
+ mem_save, mem_recall, mem_context, mem_get, mem_project, mem_session
2052
1588
 
2053
- ### SUBAGENT HANDOFF
2054
- - When dispatching subagents for thoth-mem work, load the bundled \`thoth-mem-agents\` skill.
2055
- - Orchestrator owns \`mem_session_start\`, \`mem_session_summary\`, and \`mem_save_prompt\`.
2056
- - Do not ask subagents to save prompts or session summaries; pass parent \`session_id\` and \`project\` instead.
1589
+ ### OWNERSHIP AND SUBAGENT HANDOFF
1590
+ - Root owns \`mem_session(action="start"|"checkpoint"|"summary")\` and \`mem_save(kind="prompt"|"session_summary")\`.
1591
+ - Start root memory-backed workflows with \`mem_session(action="start")\` before any other thoth-mem operation when tools and identity are available.
1592
+ - Save the real user prompt with \`mem_save(kind="prompt")\`; never save generated subagent prompts as user intent.
1593
+ - Before memory-dependent delegation, save the handoff body with \`mem_session(action="summary")\` or \`mem_save(kind="session_summary")\`.
1594
+ - Subagent handoff prompts carry parent \`session_id\`, \`project\`, permissions, and recovery instructions only; do not ask subagents to own session lifecycle actions or save prompts.
2057
1595
 
2058
1596
  ### WHEN TO SAVE
2059
- Call \`mem_save\` IMMEDIATELY after ANY of these:
1597
+ Call \`mem_save(kind="observation")\` IMMEDIATELY after ANY of these:
2060
1598
  - Architecture, design, or workflow decision made
2061
1599
  - Bug fixed (include root cause)
2062
1600
  - Non-obvious discovery, gotcha, or edge case found
@@ -2069,44 +1607,45 @@ Call \`mem_save\` IMMEDIATELY after ANY of these:
2069
1607
  - Discussion concludes with a clear direction chosen
2070
1608
 
2071
1609
  Use \`title\` as Verb + what changed or was learned.
1610
+ Use \`kind\` intentionally: \`observation\`, \`prompt\`, \`session_summary\`, or \`passive_learnings\`.
2072
1611
  Use \`type\` from: bugfix | decision | architecture | discovery | pattern | config | learning | manual.
2073
1612
  Set \`scope\` intentionally.
2074
- Reuse \`topic_key\` for the same evolving topic. Do not overwrite unrelated topics.
2075
- If unsure about a stable \`topic_key\`, call \`mem_suggest_topic_key\` first.
2076
- If you need to modify a known observation by exact ID, call \`mem_update\` instead of creating a new record.
2077
- Put the durable details in \`content\` with this structure:
1613
+ Reuse a stable \`topic_key\` for the same evolving topic. Do not overwrite unrelated topics.
1614
+ Put durable observation details in \`content\` with this structure:
2078
1615
  - What: concise description of what changed or was learned
2079
1616
  - Why: why it mattered or what problem it solved
2080
1617
  - Where: files, paths, or systems involved
2081
1618
  - Learned: edge cases, caveats, or follow-up notes
2082
1619
 
2083
- **Self-check after EVERY task**: "Did I or the user just make a decision, confirm a recommendation, express a preference, fix a bug, learn something, or establish a convention? If yes \u2192 mem_save NOW."
1620
+ **Self-check after EVERY task**: "Did I or the user just make a decision, confirm a recommendation, express a preference, fix a bug, learn something, or establish a convention? If yes \u2192 mem_save(kind="observation") NOW."
2084
1621
 
2085
- You can also call \`mem_save_prompt\` to manually save a user prompt that you consider particularly important for future context.
2086
-
2087
- ### WHEN TO SEARCH MEMORY
2088
- - Broad recovery (session start, after compaction): call \`mem_context\` for a recent-session overview.
2089
- - Targeted 3-layer recall (specific memory retrieval):
2090
- 1. Call \`mem_search\` with \`mode: "compact"\` (default) to scan the compact index of IDs + titles.
2091
- 2. Call \`mem_timeline\` around promising observation IDs for chronological context within the same session.
2092
- 3. Call \`mem_get_observation\` only for observations you need in full.
2093
- - Use \`mode: "preview"\` with \`mem_search\` only when compact results are insufficient to disambiguate.
1622
+ ### RECALL FUNNEL
1623
+ - Broad recovery (session start, after compaction): call \`mem_context(recall_query=...)\` for fused recent context when useful.
1624
+ - Targeted retrieval:
1625
+ 1. Call \`mem_recall(mode="compact")\` to scan candidate IDs and titles.
1626
+ 2. Call \`mem_recall(mode="context")\` to expand the strongest hits into retrieved context.
1627
+ 3. Call \`mem_get(...)\` only for records you need in full.
1628
+ - Use HyDE/fused recall for semantic or ambiguous searches.
1629
+ - Set \`mem_recall\` \`limit\` from 1 to 20 for candidate/result count.
1630
+ - Narrow with \`topic_key\`, \`type\`, \`time_from\`, \`time_to\`, \`scope\`, \`project\`, and \`session_id\` filters.
1631
+ - Use \`mem_get\` with \`kind="observation"|"prompt"\`; use \`mem_get(include_timeline=true)\` with \`before\`/\`after\`, and \`offset\`/\`max_length\` for large content.
1632
+ - Use bounded \`mem_project(action="graph"|"topics"|"topic")\` for relationship and topic navigation; \`mem_project(action="graph")\` relations are \`HAS_TYPE\`, \`IN_PROJECT\`, \`HAS_TOPIC_KEY\`, \`HAS_WHAT\`, \`HAS_WHY\`, \`HAS_WHERE\`, and \`HAS_LEARNED\`; these calls supplement, not replace, the recall funnel.
2094
1633
  - Search proactively on the first message about a project, feature, or problem when prior context may matter.
2095
1634
  - Search before starting work that may have been done before.
2096
1635
  - Search when the user mentions a topic that lacks enough local context.
2097
1636
 
2098
1637
  ### SESSION CLOSE PROTOCOL
2099
- - Before ending the session, call \`mem_session_summary\` with this exact template.
1638
+ - Before ending the session, call \`mem_session(action="summary")\` with this exact template.
2100
1639
  - This is NOT optional. If you skip this, the next session starts blind.
2101
1640
  - Do not claim memory was saved unless the tool call succeeded.
2102
- - If your response includes \`## Key Learnings:\`, also call \`mem_capture_passive\`.
1641
+ - If your response includes \`## Key Learnings:\`, preserve them with \`mem_save(kind="passive_learnings")\`.
2103
1642
 
2104
1643
  ${SESSION_SUMMARY_TEMPLATE}
2105
1644
 
2106
1645
  ### AFTER COMPACTION
2107
- - IMMEDIATELY call \`mem_session_summary\` with the compacted summary content.
2108
- - Then call \`mem_context\` for a recent-session overview.
2109
- - Use the 3-layer recall protocol (\`mem_search\` with \`mode: "compact"\` -> \`mem_timeline\` -> \`mem_get_observation\`) for precise artifact/prior-observation retrieval.
1646
+ - IMMEDIATELY call \`mem_session(action="summary")\` with the compacted summary content.
1647
+ - Then call \`mem_context(recall_query=...)\` for fused recent context.
1648
+ - Use the recall funnel (\`mem_recall(mode="compact")\` -> \`mem_recall(mode="context")\` -> \`mem_get(...)\`) for precise artifact/prior-observation retrieval.
2110
1649
  - Only then continue working.
2111
1650
 
2112
1651
  ### SDD TOPIC KEY CONVENTION
@@ -2155,11 +1694,15 @@ function truncate(str, max) {
2155
1694
  return str.length > max ? `${str.slice(0, max)}...` : str;
2156
1695
  }
2157
1696
  function sanitizePromptText(text) {
2158
- return truncate(stripPrivateTags(text), 2e3);
1697
+ return truncate(stripPrivateTags(stripPhaseReminder(text)), 2e3);
2159
1698
  }
2160
- function isSessionSummaryTool(toolName) {
1699
+ function isSessionSummaryTool(toolName, args) {
1700
+ if (!args || typeof args !== "object" || Array.isArray(args)) {
1701
+ return false;
1702
+ }
2161
1703
  const normalized = toolName.toLowerCase();
2162
- return normalized === "mem_session_summary" || normalized.endsWith(".mem_session_summary") || normalized.endsWith("_mem_session_summary");
1704
+ const isMemSessionTool = normalized === "mem_session" || normalized.endsWith(".mem_session") || normalized.endsWith("_mem_session");
1705
+ return isMemSessionTool && args.action === "summary";
2163
1706
  }
2164
1707
  function isMemSaveTool(toolName) {
2165
1708
  const normalized = toolName.toLowerCase();
@@ -2331,7 +1874,6 @@ function createThothMemHook(options) {
2331
1874
  },
2332
1875
  "tool.execute.after": async (input, output) => {
2333
1876
  void input.callID;
2334
- void input.args;
2335
1877
  void output.title;
2336
1878
  void output.output;
2337
1879
  void output.metadata;
@@ -2342,7 +1884,7 @@ function createThothMemHook(options) {
2342
1884
  lastMemSaveAt.set(input.sessionID, Date.now());
2343
1885
  nudgePending.delete(input.sessionID);
2344
1886
  }
2345
- if (isSessionSummaryTool(input.tool)) {
1887
+ if (isSessionSummaryTool(input.tool, input.args)) {
2346
1888
  needsCompactionFollowUp.delete(input.sessionID);
2347
1889
  }
2348
1890
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.1.18",
3
+ "version": "0.2.0",
4
4
  "description": "Delegate-first OpenCode plugin with seven agents, thoth-mem persistence, and bundled SDD skills.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",