@possumtech/rummy 0.4.0 → 2.0.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 (153) hide show
  1. package/.env.example +21 -4
  2. package/PLUGINS.md +389 -194
  3. package/README.md +25 -8
  4. package/SPEC.md +850 -373
  5. package/bin/demo.js +166 -0
  6. package/bin/rummy.js +9 -3
  7. package/biome/no-fallbacks.grit +50 -0
  8. package/lang/en.json +2 -2
  9. package/migrations/001_initial_schema.sql +88 -37
  10. package/package.json +6 -4
  11. package/service.js +50 -9
  12. package/src/agent/AgentLoop.js +460 -331
  13. package/src/agent/ContextAssembler.js +4 -2
  14. package/src/agent/Entries.js +655 -0
  15. package/src/agent/ProjectAgent.js +30 -18
  16. package/src/agent/TurnExecutor.js +232 -379
  17. package/src/agent/XmlParser.js +242 -67
  18. package/src/agent/budget.js +56 -0
  19. package/src/agent/errors.js +22 -0
  20. package/src/agent/httpStatus.js +39 -0
  21. package/src/agent/known_checks.sql +8 -4
  22. package/src/agent/known_queries.sql +9 -13
  23. package/src/agent/known_store.sql +275 -118
  24. package/src/agent/materializeContext.js +102 -0
  25. package/src/agent/runs.sql +10 -7
  26. package/src/agent/schemes.sql +14 -3
  27. package/src/agent/turns.sql +9 -9
  28. package/src/hooks/HookRegistry.js +6 -5
  29. package/src/hooks/Hooks.js +44 -3
  30. package/src/hooks/PluginContext.js +35 -21
  31. package/src/{server → hooks}/RpcRegistry.js +2 -1
  32. package/src/hooks/RummyContext.js +140 -37
  33. package/src/hooks/ToolRegistry.js +36 -35
  34. package/src/llm/LlmProvider.js +64 -90
  35. package/src/llm/errors.js +21 -0
  36. package/src/plugins/ask_user/README.md +1 -1
  37. package/src/plugins/ask_user/ask_user.js +37 -12
  38. package/src/plugins/ask_user/ask_userDoc.js +2 -23
  39. package/src/plugins/ask_user/ask_userDoc.md +10 -0
  40. package/src/plugins/budget/README.md +27 -23
  41. package/src/plugins/budget/budget.js +261 -69
  42. package/src/plugins/cp/README.md +2 -2
  43. package/src/plugins/cp/cp.js +31 -13
  44. package/src/plugins/cp/cpDoc.js +2 -23
  45. package/src/plugins/cp/cpDoc.md +7 -0
  46. package/src/plugins/engine/README.md +2 -2
  47. package/src/plugins/engine/engine.sql +4 -4
  48. package/src/plugins/engine/turn_context.sql +10 -10
  49. package/src/plugins/env/README.md +20 -5
  50. package/src/plugins/env/env.js +47 -8
  51. package/src/plugins/env/envDoc.js +2 -23
  52. package/src/plugins/env/envDoc.md +13 -0
  53. package/src/plugins/error/README.md +16 -0
  54. package/src/plugins/error/error.js +151 -0
  55. package/src/plugins/file/README.md +6 -6
  56. package/src/plugins/file/file.js +15 -7
  57. package/src/plugins/get/README.md +1 -1
  58. package/src/plugins/get/get.js +125 -49
  59. package/src/plugins/get/getDoc.js +2 -43
  60. package/src/plugins/get/getDoc.md +36 -0
  61. package/src/plugins/hedberg/README.md +1 -2
  62. package/src/plugins/hedberg/hedberg.js +8 -4
  63. package/src/plugins/hedberg/matcher.js +16 -17
  64. package/src/plugins/hedberg/normalize.js +0 -48
  65. package/src/plugins/helpers.js +43 -3
  66. package/src/plugins/index.js +146 -123
  67. package/src/plugins/instructions/README.md +35 -9
  68. package/src/plugins/instructions/instructions.js +126 -12
  69. package/src/plugins/instructions/instructions.md +25 -0
  70. package/src/plugins/instructions/instructions_104.md +7 -0
  71. package/src/plugins/instructions/instructions_105.md +46 -0
  72. package/src/plugins/instructions/instructions_106.md +0 -0
  73. package/src/plugins/instructions/instructions_107.md +0 -0
  74. package/src/plugins/instructions/instructions_108.md +8 -0
  75. package/src/plugins/instructions/protocol.js +12 -0
  76. package/src/plugins/known/README.md +2 -2
  77. package/src/plugins/known/known.js +77 -45
  78. package/src/plugins/known/knownDoc.js +2 -29
  79. package/src/plugins/known/knownDoc.md +8 -0
  80. package/src/plugins/log/README.md +48 -0
  81. package/src/plugins/log/log.js +109 -0
  82. package/src/plugins/mv/README.md +2 -2
  83. package/src/plugins/mv/mv.js +57 -24
  84. package/src/plugins/mv/mvDoc.js +2 -29
  85. package/src/plugins/mv/mvDoc.md +10 -0
  86. package/src/plugins/ollama/README.md +15 -0
  87. package/src/{llm/OllamaClient.js → plugins/ollama/ollama.js} +40 -18
  88. package/src/plugins/openai/README.md +17 -0
  89. package/src/plugins/openai/openai.js +120 -0
  90. package/src/plugins/openrouter/README.md +27 -0
  91. package/src/plugins/openrouter/openrouter.js +121 -0
  92. package/src/plugins/persona/README.md +20 -0
  93. package/src/plugins/persona/persona.js +9 -16
  94. package/src/plugins/policy/README.md +21 -0
  95. package/src/plugins/policy/policy.js +29 -14
  96. package/src/plugins/prompt/README.md +1 -1
  97. package/src/plugins/prompt/prompt.js +63 -18
  98. package/src/plugins/rm/README.md +1 -1
  99. package/src/plugins/rm/rm.js +58 -14
  100. package/src/plugins/rm/rmDoc.js +2 -24
  101. package/src/plugins/rm/rmDoc.md +13 -0
  102. package/src/plugins/rpc/README.md +2 -2
  103. package/src/plugins/rpc/rpc.js +515 -296
  104. package/src/plugins/set/README.md +1 -1
  105. package/src/plugins/set/set.js +318 -77
  106. package/src/plugins/set/setDoc.js +2 -35
  107. package/src/plugins/set/setDoc.md +22 -0
  108. package/src/plugins/sh/README.md +28 -5
  109. package/src/plugins/sh/sh.js +52 -8
  110. package/src/plugins/sh/shDoc.js +2 -23
  111. package/src/plugins/sh/shDoc.md +13 -0
  112. package/src/plugins/skill/README.md +23 -0
  113. package/src/plugins/skill/skill.js +14 -17
  114. package/src/plugins/stream/README.md +101 -0
  115. package/src/plugins/stream/stream.js +290 -0
  116. package/src/plugins/telemetry/README.md +1 -1
  117. package/src/plugins/telemetry/telemetry.js +148 -74
  118. package/src/plugins/think/README.md +1 -1
  119. package/src/plugins/think/think.js +14 -1
  120. package/src/plugins/think/thinkDoc.js +2 -17
  121. package/src/plugins/think/thinkDoc.md +7 -0
  122. package/src/plugins/unknown/README.md +3 -3
  123. package/src/plugins/unknown/unknown.js +56 -21
  124. package/src/plugins/unknown/unknownDoc.js +2 -25
  125. package/src/plugins/unknown/unknownDoc.md +11 -0
  126. package/src/plugins/update/README.md +1 -1
  127. package/src/plugins/update/update.js +67 -5
  128. package/src/plugins/update/updateDoc.js +2 -27
  129. package/src/plugins/update/updateDoc.md +8 -0
  130. package/src/plugins/xai/README.md +23 -0
  131. package/src/{llm/XaiClient.js → plugins/xai/xai.js} +58 -37
  132. package/src/server/ClientConnection.js +64 -37
  133. package/src/server/SocketServer.js +23 -10
  134. package/src/server/protocol.js +11 -0
  135. package/src/sql/functions/slugify.js +13 -1
  136. package/src/sql/v_model_context.sql +27 -31
  137. package/src/sql/v_run_log.sql +9 -14
  138. package/EXCEPTIONS.md +0 -46
  139. package/src/agent/KnownStore.js +0 -338
  140. package/src/agent/ResponseHealer.js +0 -188
  141. package/src/llm/OpenAiClient.js +0 -100
  142. package/src/llm/OpenRouterClient.js +0 -100
  143. package/src/plugins/budget/recovery.js +0 -47
  144. package/src/plugins/instructions/preamble.md +0 -37
  145. package/src/plugins/performed/README.md +0 -15
  146. package/src/plugins/performed/performed.js +0 -45
  147. package/src/plugins/previous/README.md +0 -16
  148. package/src/plugins/previous/previous.js +0 -60
  149. package/src/plugins/progress/README.md +0 -16
  150. package/src/plugins/progress/progress.js +0 -26
  151. package/src/plugins/summarize/README.md +0 -19
  152. package/src/plugins/summarize/summarize.js +0 -32
  153. package/src/plugins/summarize/summarizeDoc.js +0 -28
@@ -1,100 +0,0 @@
1
- import msg from "../agent/messages.js";
2
-
3
- const DEFAULT_CONTEXT_SIZE = 131072;
4
-
5
- export default class OpenRouterClient {
6
- #apiKey;
7
- #baseUrl;
8
-
9
- constructor(apiKey) {
10
- this.#apiKey = apiKey;
11
- this.#baseUrl = process.env.OPENROUTER_BASE_URL;
12
- }
13
-
14
- async completion(messages, model, options = {}) {
15
- if (!this.#apiKey) throw new Error(msg("error.openrouter_api_key_missing"));
16
- return this.#fetch(messages, model, options);
17
- }
18
-
19
- async #fetch(messages, model, options) {
20
- const body = { model, messages, include_reasoning: true };
21
- if (options.temperature !== undefined)
22
- body.temperature = options.temperature;
23
-
24
- const timeout = Number(process.env.RUMMY_FETCH_TIMEOUT) || 30_000;
25
- const timeoutSignal = AbortSignal.timeout(timeout);
26
- const signal = options.signal
27
- ? AbortSignal.any([options.signal, timeoutSignal])
28
- : timeoutSignal;
29
-
30
- const response = await fetch(`${this.#baseUrl}/chat/completions`, {
31
- method: "POST",
32
- headers: {
33
- Authorization: `Bearer ${this.#apiKey}`,
34
- "Content-Type": "application/json",
35
- "HTTP-Referer": process.env.RUMMY_HTTP_REFERER,
36
- "X-Title": process.env.RUMMY_X_TITLE,
37
- },
38
- body: JSON.stringify(body),
39
- signal,
40
- });
41
-
42
- if (!response.ok) {
43
- const error = await response.text();
44
- if (response.status === 401 || response.status === 403) {
45
- throw new Error(
46
- msg("error.openrouter_auth", {
47
- status: `${response.status} - ${error}`,
48
- }),
49
- );
50
- }
51
- throw new Error(
52
- msg("error.openrouter_api", {
53
- status: `${response.status} - ${error}`,
54
- }),
55
- );
56
- }
57
- const data = await response.json();
58
-
59
- for (const choice of data.choices || []) {
60
- const cm = choice.message;
61
- if (!cm) continue;
62
- const parts = [
63
- cm.reasoning_content,
64
- cm.reasoning,
65
- cm.thinking,
66
- ...(cm.reasoning_details || []).map((d) => d.text),
67
- ].filter(Boolean);
68
- cm.reasoning_content =
69
- parts.length > 0 ? [...new Set(parts)].join("\n") : null;
70
- }
71
-
72
- return data;
73
- }
74
-
75
- #contextCache = new Map();
76
-
77
- async getContextSize(model) {
78
- if (process.env.RUMMY_CONTEXT_SIZE)
79
- return Number(process.env.RUMMY_CONTEXT_SIZE);
80
-
81
- if (this.#contextCache.has(model)) return this.#contextCache.get(model);
82
-
83
- try {
84
- const res = await fetch(`${this.#baseUrl}/models`, {
85
- headers: { Authorization: `Bearer ${this.#apiKey}` },
86
- signal: AbortSignal.timeout(5000),
87
- });
88
- if (res.ok) {
89
- const data = await res.json();
90
- const entry = data.data?.find((m) => m.id === model);
91
- if (entry?.context_length) {
92
- this.#contextCache.set(model, entry.context_length);
93
- return entry.context_length;
94
- }
95
- }
96
- } catch {}
97
-
98
- return DEFAULT_CONTEXT_SIZE;
99
- }
100
- }
@@ -1,47 +0,0 @@
1
- /**
2
- * Pure recovery state transition — exported for testing.
3
- *
4
- * @param {object|null} recovery Current recovery state.
5
- * @param {{ assembledTokens: number, budgetRecovery?: { target: number, promptPath: string|null } }} result
6
- * @returns {{ next: object|null, action: null|'restore'|'hard413', promptPath: string|null }}
7
- */
8
- export function advanceRecovery(recovery, result) {
9
- // Initialise or update recovery state from a new Turn Demotion event.
10
- if (result.budgetRecovery) {
11
- if (!recovery) {
12
- recovery = {
13
- target: result.budgetRecovery.target,
14
- promptPath: result.budgetRecovery.promptPath,
15
- strikes: 0,
16
- lastTokens: result.assembledTokens,
17
- };
18
- } else {
19
- // Re-overflow during recovery: tighten target, don't count as strike.
20
- recovery = {
21
- ...recovery,
22
- target: Math.min(recovery.target, result.budgetRecovery.target),
23
- };
24
- }
25
- }
26
-
27
- if (recovery === null) return { next: null, action: null, promptPath: null };
28
-
29
- const current = result.assembledTokens;
30
-
31
- if (current <= recovery.target) {
32
- return { next: null, action: "restore", promptPath: recovery.promptPath };
33
- }
34
-
35
- const noProgress = current >= recovery.lastTokens && !result.budgetRecovery;
36
- const strikes = noProgress ? recovery.strikes + 1 : 0;
37
-
38
- if (strikes >= 3) {
39
- return { next: null, action: "hard413", promptPath: null };
40
- }
41
-
42
- return {
43
- next: { ...recovery, strikes, lastTokens: current },
44
- action: null,
45
- promptPath: null,
46
- };
47
- }
@@ -1,37 +0,0 @@
1
- You are a folksonomic knowledgebase assistant. YOU MUST discern what you don't know into unknowns, then extract and organize your findings into navigable and searchable knowns, then YOU MAY answer questions and/or perform actions.
2
-
3
- # Tool Commands
4
-
5
- Tools: [%TOOLS%]
6
-
7
- # Tool Rules
8
-
9
- ## Response Rules
10
- Required: YOU MUST respond with Tool Commands in the XML format. YOU MAY use up to 12 tools in your response.
11
- Required: YOU MUST register all unknowns with <unknown>[specific thing I need to learn]</unknown>.
12
- Required: YOU MUST register all new facts, decisions, and plans with <known path="topic/subtopic" summary="keyword,keyword,keyword">[specific facts, decisions, or plans]</known>.
13
-
14
- ## Folksonomic Memory Management
15
- * Write paths with navigable hierarchies and summaries with searchable tags.
16
- * When new facts, decisions, and plans appear, set them as <known/> entries with navigable hierarchies and summaries with searchable tags to improve your folksonomic knowledgebase.
17
- * When new questions emerge, use pattern matching operations to optimize the fidelity and relevance of your knowledgebase.
18
- * The turn attribute can be helpful for discerning what's fresh or stale, prefer more recent information if conflicts exist.
19
- * YOU MUST promote all relevant entries and demote all irrelevant entries before acting or answering. Use body pattern search (Example: <get path="known://*">John Doe</get>) to recall archived entries when needed.
20
- * Logging entries in <previous/> can also be demoted to optimize context.
21
-
22
- ## Fidelity Management
23
- * full: Entire contents are shown (consumes token budget)
24
- * summary: Only path and summary are shown. (<= 80 chars, saves token budget)
25
- * archive: Archived in an unlimited archive. Entries can be recalled with path recall or pattern search. (use caution)
26
-
27
- ## Token Budget Management
28
- * Entries contain a "fidelity" and a "token" attribute to enable token budget management and context optimization.
29
- * Set relevant entries to "full" and irrelevant entries to "summary" to optimize context.
30
- * The less irrelevant information in your context, the better.
31
-
32
- ## Response Termination
33
- Required: YOU MUST conclude every turn with EITHER <update></update> if still working OR <summarize></summarize> if done. Never both.
34
-
35
- # Tool Usage
36
-
37
- [%TOOLDOCS%]
@@ -1,15 +0,0 @@
1
- # performed
2
-
3
- Renders the `<performed>` section of the user message — the active loop's
4
- tool results and lifecycle signals.
5
-
6
- ## Registration
7
-
8
- - **Filter**: `assembly.user` at priority 100
9
-
10
- ## Behavior
11
-
12
- Filters turn_context rows where `category === "logging"` and
13
- `source_turn >= loopStartTurn`. Renders each entry chronologically
14
- with turn, status, summary, fidelity, and tokens. Empty on the first
15
- turn of a loop.
@@ -1,45 +0,0 @@
1
- export default class Performed {
2
- #core;
3
-
4
- constructor(core) {
5
- this.#core = core;
6
- core.filter("assembly.user", this.assemblePerformed.bind(this), 100);
7
- }
8
-
9
- async assemblePerformed(content, ctx) {
10
- const entries = ctx.rows.filter(
11
- (r) =>
12
- r.category === "logging" &&
13
- r.source_turn >= ctx.loopStartTurn &&
14
- r.scheme !== "unknown",
15
- );
16
- if (entries.length === 0) return content;
17
-
18
- const lines = entries.map((e) => renderToolTag(e));
19
- return `${content}<performed>\n${lines.join("\n")}\n</performed>\n`;
20
- }
21
- }
22
-
23
- function renderToolTag(entry) {
24
- const attrs =
25
- typeof entry.attributes === "string"
26
- ? JSON.parse(entry.attributes)
27
- : entry.attributes;
28
-
29
- const target = attrs?.path || attrs?.file || attrs?.command || "";
30
- const turn = entry.source_turn ? ` turn="${entry.source_turn}"` : "";
31
- const status = entry.status ? ` status="${entry.status}"` : "";
32
- const fidelity = entry.fidelity ? ` fidelity="${entry.fidelity}"` : "";
33
- const tokens = entry.tokens ? ` tokens="${entry.tokens}"` : "";
34
- const summary =
35
- typeof attrs?.summary === "string"
36
- ? ` summary="${attrs.summary.slice(0, 80)}"`
37
- : "";
38
-
39
- const body = entry.body || null;
40
-
41
- if (body) {
42
- return `<${entry.scheme} path="${target}"${turn}${status}${summary}${fidelity}${tokens}>${body}</${entry.scheme}>`;
43
- }
44
- return `<${entry.scheme} path="${target}"${turn}${status}${summary}${fidelity}${tokens}/>`;
45
- }
@@ -1,16 +0,0 @@
1
- # previous
2
-
3
- Renders the `<previous>` section of the system message — completed loop
4
- history from prior ask/act invocations on this run.
5
-
6
- ## Registration
7
-
8
- - **Filter**: `assembly.system` at priority 200
9
- - **Condition**: Omitted when `loopStartTurn <= 1` (first loop has no history)
10
-
11
- ## Behavior
12
-
13
- Filters turn_context rows where `category` is `logging` or `prompt`
14
- and `source_turn < loopStartTurn`. Renders each entry chronologically
15
- with turn, status, summary, fidelity, and tokens. The model can target
16
- these entries by path with `<set>` or `<rm>` to free context space.
@@ -1,60 +0,0 @@
1
- export default class Previous {
2
- #core;
3
-
4
- constructor(core) {
5
- this.#core = core;
6
- core.filter("assembly.system", this.assemblePrevious.bind(this), 200);
7
- }
8
-
9
- async assemblePrevious(content, ctx) {
10
- if (ctx.loopStartTurn <= 1) return content;
11
-
12
- const entries = ctx.rows
13
- .filter(
14
- (r) =>
15
- (r.category === "logging" || r.category === "prompt") &&
16
- r.source_turn < ctx.loopStartTurn,
17
- )
18
- .toSorted((a, b) => {
19
- if (a.source_turn !== b.source_turn)
20
- return a.source_turn - b.source_turn;
21
- // Within the same turn: prompt first (cause before effect)
22
- if (a.category === "prompt" && b.category !== "prompt") return -1;
23
- if (b.category === "prompt" && a.category !== "prompt") return 1;
24
- return 0;
25
- });
26
- if (entries.length === 0) return content;
27
-
28
- const lines = await Promise.all(
29
- entries.map((e) => renderToolTag(e, this.#core)),
30
- );
31
- return `${content}\n\n<previous>\n${lines.join("\n")}\n</previous>`;
32
- }
33
- }
34
-
35
- async function renderToolTag(entry, _core) {
36
- const attrs =
37
- typeof entry.attributes === "string"
38
- ? JSON.parse(entry.attributes)
39
- : entry.attributes;
40
-
41
- const target = attrs?.path || attrs?.file || attrs?.command || "";
42
- const turn = entry.source_turn ? ` turn="${entry.source_turn}"` : "";
43
- const status = entry.status ? ` status="${entry.status}"` : "";
44
- const fidelity = entry.fidelity ? ` fidelity="${entry.fidelity}"` : "";
45
- const tokens = entry.tokens ? ` tokens="${entry.tokens}"` : "";
46
-
47
- // Previous entries render at summary. Prompts get 512 chars for orientation.
48
- const limit = entry.scheme === "prompt" ? 512 : 80;
49
- const rawSummary =
50
- (typeof attrs?.summary === "string" ? attrs.summary : null) ||
51
- entry.body?.slice(0, limit) ||
52
- "";
53
- // Strip internal dedup namespace prefixes (e.g. "get://turn_3/src/app.js" → "src/app.js")
54
- const summaryText = rawSummary.replace(/\b\w+:\/\/turn_\d+\//g, "");
55
- const summaryAttr = summaryText
56
- ? ` summary="${summaryText.replace(/"/g, "'").slice(0, limit)}"`
57
- : "";
58
-
59
- return `<${entry.scheme} path="${target}"${turn}${status}${summaryAttr}${fidelity}${tokens}/>`;
60
- }
@@ -1,16 +0,0 @@
1
- # progress
2
-
3
- Renders the `<progress>` section of the user message — bridges the
4
- current work log to the active prompt.
5
-
6
- ## Registration
7
-
8
- - **Filter**: `assembly.user` at priority 200
9
-
10
- ## Behavior
11
-
12
- Emits `<progress turn="N">` carrying token budget and fidelity stats.
13
- On continuation turns with current entries: "The above actions were
14
- performed in response to the following prompt:"
15
-
16
- Progress text is the tuning knob for model orientation between turns.
@@ -1,26 +0,0 @@
1
- export default class Progress {
2
- #core;
3
-
4
- constructor(core) {
5
- this.#core = core;
6
- core.filter("assembly.user", this.assembleProgress.bind(this), 200);
7
- }
8
-
9
- async assembleProgress(content, ctx) {
10
- const { lastContextTokens: usedTokens, contextSize } = ctx;
11
- const pct = contextSize ? Math.round((usedTokens / contextSize) * 100) : 0;
12
-
13
- const lines = [];
14
- if (contextSize) {
15
- lines.push(
16
- `Using ${usedTokens} tokens (${pct}%) of ${contextSize} token budget. Use <get/> or set entry fidelity to "full" to spend tokens. Set entry fidelity to "summary" to save tokens.`,
17
- );
18
- }
19
- lines.push(
20
- 'Conclude with a brief <update></update> to continue or a brief <summarize></summarize> if done.',
21
- );
22
- const body = lines.join("\n");
23
-
24
- return `${content}<progress turn="${ctx.turn}">${body}</progress>\n`;
25
- }
26
- }
@@ -1,19 +0,0 @@
1
- # summarize
2
-
3
- Lifecycle signal — the model declares it has completed the task.
4
-
5
- ## Registration
6
-
7
- - **Tool**: `summarize`
8
- - **Category**: `logging`
9
- - **Handler**: None — recorded by TurnExecutor as a lifecycle signal.
10
-
11
- ## Projection
12
-
13
- Shows `summarize` followed by the entry body.
14
-
15
- ## Behavior
16
-
17
- If the model sends `<summarize>` but actions in the same turn failed,
18
- TurnExecutor overrides it to `<update>` — the model's assertion that
19
- it's done is false.
@@ -1,32 +0,0 @@
1
- import docs from "./summarizeDoc.js";
2
-
3
- export default class Summarize {
4
- #core;
5
-
6
- constructor(core) {
7
- this.#core = core;
8
- core.ensureTool();
9
- core.registerScheme({ category: "logging" });
10
- core.on("handler", this.handler.bind(this));
11
- core.on("full", this.full.bind(this));
12
- core.on("summary", this.summary.bind(this));
13
- core.filter("instructions.toolDocs", async (docsMap) => {
14
- docsMap.summarize = docs;
15
- return docsMap;
16
- });
17
- }
18
-
19
- async handler(entry, rummy) {
20
- const { entries: store, sequence: turn, runId, loopId } = rummy;
21
- const statusPath = await store.slugPath(runId, "summarize", entry.body);
22
- await store.upsert(runId, turn, statusPath, entry.body, 200, { loopId });
23
- }
24
-
25
- full(entry) {
26
- return `# summarize\n${entry.body}`;
27
- }
28
-
29
- summary(entry) {
30
- return this.full(entry);
31
- }
32
- }
@@ -1,28 +0,0 @@
1
- // Tool doc for <summarize>. Each entry: [text, rationale].
2
- // Text goes to the model. Rationale stays in source.
3
- // Changing ANY line requires reading ALL rationales first.
4
- const LINES = [
5
- ["## <summarize>[answer or summary]</summarize> - Signal completion"],
6
- [
7
- "Example: <summarize>The port is 8080</summarize>",
8
- "Direct answer. Summarize delivers answers.",
9
- ],
10
- [
11
- "Example: <summarize>Installed express, updated config</summarize>",
12
- "Task summary. Action completion.",
13
- ],
14
- [
15
- "* YOU MUST use <summarize></summarize> when done — describes the final state",
16
- "Completion signal.",
17
- ],
18
- [
19
- "* YOU MUST NOT use <summarize> if still working — use <update/> instead",
20
- "Mutual exclusion with update.",
21
- ],
22
- [
23
- "* YOU MUST keep <summarize> to <= 80 characters",
24
- "Length cap.",
25
- ],
26
- ];
27
-
28
- export default LINES.map(([text]) => text).join("\n");