@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.
- package/.env.example +21 -4
- package/PLUGINS.md +389 -194
- package/README.md +25 -8
- package/SPEC.md +850 -373
- package/bin/demo.js +166 -0
- package/bin/rummy.js +9 -3
- package/biome/no-fallbacks.grit +50 -0
- package/lang/en.json +2 -2
- package/migrations/001_initial_schema.sql +88 -37
- package/package.json +6 -4
- package/service.js +50 -9
- package/src/agent/AgentLoop.js +460 -331
- package/src/agent/ContextAssembler.js +4 -2
- package/src/agent/Entries.js +655 -0
- package/src/agent/ProjectAgent.js +30 -18
- package/src/agent/TurnExecutor.js +232 -379
- package/src/agent/XmlParser.js +242 -67
- package/src/agent/budget.js +56 -0
- package/src/agent/errors.js +22 -0
- package/src/agent/httpStatus.js +39 -0
- package/src/agent/known_checks.sql +8 -4
- package/src/agent/known_queries.sql +9 -13
- package/src/agent/known_store.sql +275 -118
- package/src/agent/materializeContext.js +102 -0
- package/src/agent/runs.sql +10 -7
- package/src/agent/schemes.sql +14 -3
- package/src/agent/turns.sql +9 -9
- package/src/hooks/HookRegistry.js +6 -5
- package/src/hooks/Hooks.js +44 -3
- package/src/hooks/PluginContext.js +35 -21
- package/src/{server → hooks}/RpcRegistry.js +2 -1
- package/src/hooks/RummyContext.js +140 -37
- package/src/hooks/ToolRegistry.js +36 -35
- package/src/llm/LlmProvider.js +64 -90
- package/src/llm/errors.js +21 -0
- package/src/plugins/ask_user/README.md +1 -1
- package/src/plugins/ask_user/ask_user.js +37 -12
- package/src/plugins/ask_user/ask_userDoc.js +2 -23
- package/src/plugins/ask_user/ask_userDoc.md +10 -0
- package/src/plugins/budget/README.md +27 -23
- package/src/plugins/budget/budget.js +261 -69
- package/src/plugins/cp/README.md +2 -2
- package/src/plugins/cp/cp.js +31 -13
- package/src/plugins/cp/cpDoc.js +2 -23
- package/src/plugins/cp/cpDoc.md +7 -0
- package/src/plugins/engine/README.md +2 -2
- package/src/plugins/engine/engine.sql +4 -4
- package/src/plugins/engine/turn_context.sql +10 -10
- package/src/plugins/env/README.md +20 -5
- package/src/plugins/env/env.js +47 -8
- package/src/plugins/env/envDoc.js +2 -23
- package/src/plugins/env/envDoc.md +13 -0
- package/src/plugins/error/README.md +16 -0
- package/src/plugins/error/error.js +151 -0
- package/src/plugins/file/README.md +6 -6
- package/src/plugins/file/file.js +15 -7
- package/src/plugins/get/README.md +1 -1
- package/src/plugins/get/get.js +125 -49
- package/src/plugins/get/getDoc.js +2 -43
- package/src/plugins/get/getDoc.md +36 -0
- package/src/plugins/hedberg/README.md +1 -2
- package/src/plugins/hedberg/hedberg.js +8 -4
- package/src/plugins/hedberg/matcher.js +16 -17
- package/src/plugins/hedberg/normalize.js +0 -48
- package/src/plugins/helpers.js +43 -3
- package/src/plugins/index.js +146 -123
- package/src/plugins/instructions/README.md +35 -9
- package/src/plugins/instructions/instructions.js +126 -12
- package/src/plugins/instructions/instructions.md +25 -0
- package/src/plugins/instructions/instructions_104.md +7 -0
- package/src/plugins/instructions/instructions_105.md +46 -0
- package/src/plugins/instructions/instructions_106.md +0 -0
- package/src/plugins/instructions/instructions_107.md +0 -0
- package/src/plugins/instructions/instructions_108.md +8 -0
- package/src/plugins/instructions/protocol.js +12 -0
- package/src/plugins/known/README.md +2 -2
- package/src/plugins/known/known.js +77 -45
- package/src/plugins/known/knownDoc.js +2 -29
- package/src/plugins/known/knownDoc.md +8 -0
- package/src/plugins/log/README.md +48 -0
- package/src/plugins/log/log.js +109 -0
- package/src/plugins/mv/README.md +2 -2
- package/src/plugins/mv/mv.js +57 -24
- package/src/plugins/mv/mvDoc.js +2 -29
- package/src/plugins/mv/mvDoc.md +10 -0
- package/src/plugins/ollama/README.md +15 -0
- package/src/{llm/OllamaClient.js → plugins/ollama/ollama.js} +40 -18
- package/src/plugins/openai/README.md +17 -0
- package/src/plugins/openai/openai.js +120 -0
- package/src/plugins/openrouter/README.md +27 -0
- package/src/plugins/openrouter/openrouter.js +121 -0
- package/src/plugins/persona/README.md +20 -0
- package/src/plugins/persona/persona.js +9 -16
- package/src/plugins/policy/README.md +21 -0
- package/src/plugins/policy/policy.js +29 -14
- package/src/plugins/prompt/README.md +1 -1
- package/src/plugins/prompt/prompt.js +63 -18
- package/src/plugins/rm/README.md +1 -1
- package/src/plugins/rm/rm.js +58 -14
- package/src/plugins/rm/rmDoc.js +2 -24
- package/src/plugins/rm/rmDoc.md +13 -0
- package/src/plugins/rpc/README.md +2 -2
- package/src/plugins/rpc/rpc.js +515 -296
- package/src/plugins/set/README.md +1 -1
- package/src/plugins/set/set.js +318 -77
- package/src/plugins/set/setDoc.js +2 -35
- package/src/plugins/set/setDoc.md +22 -0
- package/src/plugins/sh/README.md +28 -5
- package/src/plugins/sh/sh.js +52 -8
- package/src/plugins/sh/shDoc.js +2 -23
- package/src/plugins/sh/shDoc.md +13 -0
- package/src/plugins/skill/README.md +23 -0
- package/src/plugins/skill/skill.js +14 -17
- package/src/plugins/stream/README.md +101 -0
- package/src/plugins/stream/stream.js +290 -0
- package/src/plugins/telemetry/README.md +1 -1
- package/src/plugins/telemetry/telemetry.js +148 -74
- package/src/plugins/think/README.md +1 -1
- package/src/plugins/think/think.js +14 -1
- package/src/plugins/think/thinkDoc.js +2 -17
- package/src/plugins/think/thinkDoc.md +7 -0
- package/src/plugins/unknown/README.md +3 -3
- package/src/plugins/unknown/unknown.js +56 -21
- package/src/plugins/unknown/unknownDoc.js +2 -25
- package/src/plugins/unknown/unknownDoc.md +11 -0
- package/src/plugins/update/README.md +1 -1
- package/src/plugins/update/update.js +67 -5
- package/src/plugins/update/updateDoc.js +2 -27
- package/src/plugins/update/updateDoc.md +8 -0
- package/src/plugins/xai/README.md +23 -0
- package/src/{llm/XaiClient.js → plugins/xai/xai.js} +58 -37
- package/src/server/ClientConnection.js +64 -37
- package/src/server/SocketServer.js +23 -10
- package/src/server/protocol.js +11 -0
- package/src/sql/functions/slugify.js +13 -1
- package/src/sql/v_model_context.sql +27 -31
- package/src/sql/v_run_log.sql +9 -14
- package/EXCEPTIONS.md +0 -46
- package/src/agent/KnownStore.js +0 -338
- package/src/agent/ResponseHealer.js +0 -188
- package/src/llm/OpenAiClient.js +0 -100
- package/src/llm/OpenRouterClient.js +0 -100
- package/src/plugins/budget/recovery.js +0 -47
- package/src/plugins/instructions/preamble.md +0 -37
- package/src/plugins/performed/README.md +0 -15
- package/src/plugins/performed/performed.js +0 -45
- package/src/plugins/previous/README.md +0 -16
- package/src/plugins/previous/previous.js +0 -60
- package/src/plugins/progress/README.md +0 -16
- package/src/plugins/progress/progress.js +0 -26
- package/src/plugins/summarize/README.md +0 -19
- package/src/plugins/summarize/summarize.js +0 -32
- 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");
|