@possumtech/rummy 0.2.8 → 0.3.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.
- package/.env.example +13 -2
- package/EXCEPTIONS.md +46 -0
- package/PLUGINS.md +422 -188
- package/SPEC.md +440 -106
- package/migrations/001_initial_schema.sql +5 -3
- package/package.json +17 -5
- package/service.js +5 -3
- package/src/agent/AgentLoop.js +252 -55
- package/src/agent/ContextAssembler.js +20 -4
- package/src/agent/KnownStore.js +82 -25
- package/src/agent/ProjectAgent.js +4 -1
- package/src/agent/ResponseHealer.js +86 -32
- package/src/agent/TurnExecutor.js +542 -207
- package/src/agent/XmlParser.js +77 -41
- package/src/agent/known_store.sql +68 -4
- package/src/agent/schemes.sql +3 -0
- package/src/agent/tokens.js +7 -21
- package/src/agent/turns.sql +15 -1
- package/src/hooks/HookRegistry.js +7 -0
- package/src/hooks/Hooks.js +15 -0
- package/src/hooks/PluginContext.js +14 -1
- package/src/hooks/RummyContext.js +16 -4
- package/src/hooks/ToolRegistry.js +77 -19
- package/src/llm/LlmProvider.js +27 -8
- package/src/llm/OpenAiClient.js +20 -0
- package/src/llm/OpenRouterClient.js +24 -2
- package/src/llm/XaiClient.js +47 -2
- package/src/plugins/ask_user/README.md +4 -4
- package/src/plugins/ask_user/ask_user.js +5 -5
- package/src/plugins/ask_user/ask_userDoc.js +29 -0
- package/src/plugins/budget/README.md +31 -0
- package/src/plugins/budget/budget.js +55 -0
- package/src/plugins/cp/README.md +5 -4
- package/src/plugins/cp/cp.js +10 -6
- package/src/plugins/cp/cpDoc.js +29 -0
- package/src/plugins/engine/engine.sql +1 -8
- package/src/plugins/engine/turn_context.sql +4 -9
- package/src/plugins/env/README.md +3 -4
- package/src/plugins/env/env.js +5 -5
- package/src/plugins/env/envDoc.js +29 -0
- package/src/plugins/file/README.md +9 -12
- package/src/plugins/file/file.js +34 -35
- package/src/plugins/get/README.md +2 -2
- package/src/plugins/get/get.js +77 -6
- package/src/plugins/get/getDoc.js +51 -0
- package/src/plugins/hedberg/hedberg.js +2 -1
- package/src/plugins/hedberg/matcher.js +10 -29
- package/src/plugins/hedberg/normalize.js +28 -0
- package/src/plugins/hedberg/patterns.js +25 -27
- package/src/plugins/hedberg/sed.js +17 -10
- package/src/plugins/index.js +66 -14
- package/src/plugins/instructions/README.md +6 -2
- package/src/plugins/instructions/instructions.js +20 -4
- package/src/plugins/instructions/preamble.md +19 -5
- package/src/plugins/known/README.md +10 -7
- package/src/plugins/known/known.js +23 -17
- package/src/plugins/known/knownDoc.js +34 -0
- package/src/plugins/mv/README.md +5 -4
- package/src/plugins/mv/mv.js +27 -6
- package/src/plugins/mv/mvDoc.js +45 -0
- package/src/plugins/performed/README.md +15 -0
- package/src/plugins/performed/performed.js +45 -0
- package/src/plugins/persona/persona.js +78 -0
- package/src/plugins/previous/README.md +3 -2
- package/src/plugins/previous/previous.js +33 -24
- package/src/plugins/progress/README.md +1 -2
- package/src/plugins/progress/progress.js +33 -21
- package/src/plugins/prompt/README.md +5 -5
- package/src/plugins/prompt/prompt.js +15 -17
- package/src/plugins/rm/README.md +4 -4
- package/src/plugins/rm/rm.js +32 -20
- package/src/plugins/rm/rmDoc.js +30 -0
- package/src/plugins/rpc/README.md +15 -28
- package/src/plugins/rpc/rpc.js +42 -77
- package/src/plugins/set/README.md +13 -12
- package/src/plugins/set/set.js +107 -16
- package/src/plugins/set/setDoc.js +49 -0
- package/src/plugins/sh/README.md +4 -4
- package/src/plugins/sh/sh.js +5 -5
- package/src/plugins/sh/shDoc.js +29 -0
- package/src/plugins/{skills/skills.js → skill/skill.js} +10 -51
- package/src/plugins/summarize/README.md +6 -5
- package/src/plugins/summarize/summarize.js +7 -6
- package/src/plugins/summarize/summarizeDoc.js +33 -0
- package/src/plugins/telemetry/telemetry.js +16 -9
- package/src/plugins/think/README.md +20 -0
- package/src/plugins/think/think.js +5 -0
- package/src/plugins/unknown/README.md +6 -5
- package/src/plugins/unknown/unknown.js +12 -9
- package/src/plugins/unknown/unknownDoc.js +31 -0
- package/src/plugins/update/README.md +3 -8
- package/src/plugins/update/update.js +7 -6
- package/src/plugins/update/updateDoc.js +33 -0
- package/src/server/ClientConnection.js +59 -45
- package/src/server/RpcRegistry.js +52 -4
- package/src/sql/v_model_context.sql +10 -25
- package/src/plugins/ask_user/docs.md +0 -2
- package/src/plugins/cp/docs.md +0 -2
- package/src/plugins/current/README.md +0 -14
- package/src/plugins/current/current.js +0 -47
- package/src/plugins/env/docs.md +0 -4
- package/src/plugins/get/docs.md +0 -10
- package/src/plugins/known/docs.md +0 -3
- package/src/plugins/mv/docs.md +0 -2
- package/src/plugins/rm/docs.md +0 -6
- package/src/plugins/set/docs.md +0 -6
- package/src/plugins/sh/docs.md +0 -2
- package/src/plugins/skills/README.md +0 -25
- package/src/plugins/store/README.md +0 -20
- package/src/plugins/store/docs.md +0 -6
- package/src/plugins/store/store.js +0 -63
- package/src/plugins/summarize/docs.md +0 -4
- package/src/plugins/unknown/docs.md +0 -5
- package/src/plugins/update/docs.md +0 -4
package/src/plugins/sh/README.md
CHANGED
|
@@ -5,9 +5,8 @@ Proposes shell command execution for client approval.
|
|
|
5
5
|
## Registration
|
|
6
6
|
|
|
7
7
|
- **Tool**: `sh`
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **Handler**: Upserts the entry as `proposed` state. The client must approve execution.
|
|
8
|
+
- **Category**: `logging`
|
|
9
|
+
- **Handler**: Upserts the entry at status 202 (proposed). The client must approve execution.
|
|
11
10
|
|
|
12
11
|
## Projection
|
|
13
12
|
|
|
@@ -15,4 +14,5 @@ Shows `sh {command}` followed by the entry body.
|
|
|
15
14
|
|
|
16
15
|
## Behavior
|
|
17
16
|
|
|
18
|
-
All shell commands require client-side approval — nothing executes
|
|
17
|
+
All shell commands require client-side approval — nothing executes
|
|
18
|
+
server-side. Act mode only; excluded in ask mode by `resolveForLoop`.
|
package/src/plugins/sh/sh.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import docs from "./shDoc.js";
|
|
2
2
|
|
|
3
3
|
export default class Sh {
|
|
4
4
|
#core;
|
|
@@ -9,10 +9,10 @@ export default class Sh {
|
|
|
9
9
|
core.on("handler", this.handler.bind(this));
|
|
10
10
|
core.on("full", this.full.bind(this));
|
|
11
11
|
core.on("summary", this.summary.bind(this));
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
);
|
|
12
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
13
|
+
docsMap.sh = docs;
|
|
14
|
+
return docsMap;
|
|
15
|
+
});
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async handler(entry, rummy) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Tool doc for <sh>. 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
|
+
// --- Syntax
|
|
6
|
+
["## <sh>[command]</sh> - Run a shell command with side effects"],
|
|
7
|
+
|
|
8
|
+
// --- Examples: install and test — real mutations
|
|
9
|
+
[
|
|
10
|
+
"Example: <sh>npm install express</sh>",
|
|
11
|
+
"Package install. Shows a real side-effect command.",
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
"Example: <sh>npm test</sh>",
|
|
15
|
+
"Test execution. Another common side-effect action.",
|
|
16
|
+
],
|
|
17
|
+
|
|
18
|
+
// --- Constraints
|
|
19
|
+
[
|
|
20
|
+
"* YOU MUST NOT use <sh/> to read, create, or edit files — use <get/> and <set/>",
|
|
21
|
+
"Forces file operations through the entry system. Prevents untracked mutations.",
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"* YOU MUST use <env/> for commands without side effects",
|
|
25
|
+
"Reinforces the env/sh split. Read = env, mutate = sh.",
|
|
26
|
+
],
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export default LINES.map(([text]) => text).join("\n");
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
-
export default class
|
|
4
|
+
export default class Skill {
|
|
5
5
|
#core;
|
|
6
6
|
|
|
7
7
|
constructor(core) {
|
|
8
8
|
this.#core = core;
|
|
9
9
|
core.registerScheme({
|
|
10
10
|
name: "skill",
|
|
11
|
-
category: "
|
|
11
|
+
category: "data",
|
|
12
12
|
});
|
|
13
|
+
core.hooks.tools.onView("skill", (entry) => entry.body);
|
|
14
|
+
|
|
13
15
|
const r = core.hooks.rpc.registry;
|
|
14
16
|
|
|
15
17
|
r.register("skill/add", {
|
|
@@ -22,19 +24,12 @@ export default class Skills {
|
|
|
22
24
|
|
|
23
25
|
const body = await loadFile("skills", params.name);
|
|
24
26
|
const store = ctx.projectAgent.entries;
|
|
25
|
-
await store.upsert(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
body,
|
|
30
|
-
200,
|
|
31
|
-
{
|
|
32
|
-
attributes: {
|
|
33
|
-
name: params.name,
|
|
34
|
-
source: filePath("skills", params.name),
|
|
35
|
-
},
|
|
27
|
+
await store.upsert(runRow.id, 0, `skill://${params.name}`, body, 200, {
|
|
28
|
+
attributes: {
|
|
29
|
+
name: params.name,
|
|
30
|
+
source: filePath("skills", params.name),
|
|
36
31
|
},
|
|
37
|
-
);
|
|
32
|
+
});
|
|
38
33
|
|
|
39
34
|
return { status: "ok", skill: params.name };
|
|
40
35
|
},
|
|
@@ -97,43 +92,7 @@ export default class Skills {
|
|
|
97
92
|
requiresInit: true,
|
|
98
93
|
});
|
|
99
94
|
|
|
100
|
-
|
|
101
|
-
handler: async (params, ctx) => {
|
|
102
|
-
if (!params.run) throw new Error("run is required");
|
|
103
|
-
|
|
104
|
-
const runRow = await ctx.db.get_run_by_alias.get({ alias: params.run });
|
|
105
|
-
if (!runRow) throw new Error(`Run not found: ${params.run}`);
|
|
106
|
-
|
|
107
|
-
let text = params.text;
|
|
108
|
-
if (params.name && !text) {
|
|
109
|
-
text = await loadFile("personas", params.name);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
await ctx.db.update_run_config.run({
|
|
113
|
-
id: runRow.id,
|
|
114
|
-
temperature: null,
|
|
115
|
-
persona: text || null,
|
|
116
|
-
context_limit: null,
|
|
117
|
-
model: null,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
return { status: "ok" };
|
|
121
|
-
},
|
|
122
|
-
description:
|
|
123
|
-
"Set persona on a run. Pass name or text. Pass neither to clear.",
|
|
124
|
-
params: {
|
|
125
|
-
run: "string — run alias",
|
|
126
|
-
name: "string? — persona filename (without .md)",
|
|
127
|
-
text: "string? — raw persona text (overrides name)",
|
|
128
|
-
},
|
|
129
|
-
requiresInit: true,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
r.register("listPersonas", {
|
|
133
|
-
handler: async () => listAvailable("personas"),
|
|
134
|
-
description: "List available persona files. Returns [{ name, path }].",
|
|
135
|
-
requiresInit: true,
|
|
136
|
-
});
|
|
95
|
+
// Persona methods extracted to persona plugin.
|
|
137
96
|
}
|
|
138
97
|
}
|
|
139
98
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# summarize
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lifecycle signal — the model declares it has completed the task.
|
|
4
4
|
|
|
5
5
|
## Registration
|
|
6
6
|
|
|
7
7
|
- **Tool**: `summarize`
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **Handler**: None — projection only.
|
|
8
|
+
- **Category**: `logging`
|
|
9
|
+
- **Handler**: None — recorded by TurnExecutor as a lifecycle signal.
|
|
11
10
|
|
|
12
11
|
## Projection
|
|
13
12
|
|
|
@@ -15,4 +14,6 @@ Shows `summarize` followed by the entry body.
|
|
|
15
14
|
|
|
16
15
|
## Behavior
|
|
17
16
|
|
|
18
|
-
|
|
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,17 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import docs from "./summarizeDoc.js";
|
|
2
2
|
|
|
3
3
|
export default class Summarize {
|
|
4
4
|
#core;
|
|
5
5
|
|
|
6
6
|
constructor(core) {
|
|
7
7
|
this.#core = core;
|
|
8
|
-
core.
|
|
8
|
+
core.ensureTool();
|
|
9
|
+
core.registerScheme({ category: "logging" });
|
|
9
10
|
core.on("full", this.full.bind(this));
|
|
10
11
|
core.on("summary", this.summary.bind(this));
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
);
|
|
12
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
13
|
+
docsMap.summarize = docs;
|
|
14
|
+
return docsMap;
|
|
15
|
+
});
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
full(entry) {
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
// --- Syntax
|
|
6
|
+
["## <summarize>[answer or summary]</summarize> - Signal completion"],
|
|
7
|
+
|
|
8
|
+
// --- Examples: answer and task completion
|
|
9
|
+
[
|
|
10
|
+
"Example: <summarize>The port is 8080</summarize>",
|
|
11
|
+
"Direct answer. Shows summarize as the vehicle for delivering answers.",
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
"Example: <summarize>Installed express, updated config</summarize>",
|
|
15
|
+
"Task summary. Shows summarize for action completion.",
|
|
16
|
+
],
|
|
17
|
+
|
|
18
|
+
// --- Constraints: RFC-style MUST/MUST NOT
|
|
19
|
+
[
|
|
20
|
+
"* YOU MUST use <summarize> when done — describes the final state",
|
|
21
|
+
"Completion signal. Without this, the loop continues indefinitely.",
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"* YOU MUST NOT use <summarize> if still working — use <update/> instead",
|
|
25
|
+
"Mutual exclusion with update. Prevents premature completion.",
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"* YOU MUST keep <summarize> to <= 80 characters",
|
|
29
|
+
"Length cap. Matches the summary attribute constraint. Prevents verbose output.",
|
|
30
|
+
],
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export default LINES.map(([text]) => text).join("\n");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
export default class Telemetry {
|
|
@@ -75,8 +75,8 @@ export default class Telemetry {
|
|
|
75
75
|
result,
|
|
76
76
|
responseMessage,
|
|
77
77
|
content,
|
|
78
|
-
commands,
|
|
79
78
|
unparsed,
|
|
79
|
+
assembledTokens,
|
|
80
80
|
systemMsg,
|
|
81
81
|
userMsg,
|
|
82
82
|
}) {
|
|
@@ -85,17 +85,20 @@ export default class Telemetry {
|
|
|
85
85
|
// assistant://N — the model's raw response
|
|
86
86
|
await store.upsert(runId, turn, `assistant://${turn}`, content, 200, {
|
|
87
87
|
loopId,
|
|
88
|
+
fidelity: "archive",
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
// system://N, user://N — assembled messages as audit
|
|
91
92
|
if (systemMsg) {
|
|
92
93
|
await store.upsert(runId, turn, `system://${turn}`, systemMsg, 200, {
|
|
93
94
|
loopId,
|
|
95
|
+
fidelity: "archive",
|
|
94
96
|
});
|
|
95
97
|
}
|
|
96
98
|
if (userMsg) {
|
|
97
99
|
await store.upsert(runId, turn, `user://${turn}`, userMsg, 200, {
|
|
98
100
|
loopId,
|
|
101
|
+
fidelity: "archive",
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -112,7 +115,7 @@ export default class Telemetry {
|
|
|
112
115
|
model: result.model || null,
|
|
113
116
|
}),
|
|
114
117
|
200,
|
|
115
|
-
{ loopId },
|
|
118
|
+
{ loopId, fidelity: "archive" },
|
|
116
119
|
);
|
|
117
120
|
|
|
118
121
|
// reasoning://N
|
|
@@ -123,7 +126,7 @@ export default class Telemetry {
|
|
|
123
126
|
`reasoning://${turn}`,
|
|
124
127
|
responseMessage.reasoning_content,
|
|
125
128
|
200,
|
|
126
|
-
{ loopId },
|
|
129
|
+
{ loopId, fidelity: "archive" },
|
|
127
130
|
);
|
|
128
131
|
}
|
|
129
132
|
|
|
@@ -131,6 +134,7 @@ export default class Telemetry {
|
|
|
131
134
|
if (unparsed) {
|
|
132
135
|
await store.upsert(runId, turn, `content://${turn}`, unparsed, 200, {
|
|
133
136
|
loopId,
|
|
137
|
+
fidelity: "archive",
|
|
134
138
|
});
|
|
135
139
|
}
|
|
136
140
|
|
|
@@ -147,8 +151,13 @@ export default class Telemetry {
|
|
|
147
151
|
usage.completion_tokens_details?.reasoning_tokens ||
|
|
148
152
|
usage.output_tokens_details?.reasoning_tokens ||
|
|
149
153
|
0;
|
|
154
|
+
// Use LLM's actual prompt_tokens as the ground-truth context size when available.
|
|
155
|
+
// This back-fills context_tokens so get_last_context_tokens reflects reality for the next turn.
|
|
156
|
+
const actualContextTokens = usage.prompt_tokens || assembledTokens || 0;
|
|
150
157
|
await rummy.db.update_turn_stats.run({
|
|
151
158
|
id: rummy.turnId,
|
|
159
|
+
context_tokens: actualContextTokens,
|
|
160
|
+
reasoning_content: responseMessage?.reasoning_content || null,
|
|
152
161
|
prompt_tokens: usage.prompt_tokens ?? 0,
|
|
153
162
|
cached_tokens: cachedTokens ?? 0,
|
|
154
163
|
completion_tokens: usage.completion_tokens ?? 0,
|
|
@@ -187,10 +196,8 @@ export default class Telemetry {
|
|
|
187
196
|
|
|
188
197
|
#flush() {
|
|
189
198
|
if (!this.#lastRunPath || this.#turnLog.length === 0) return;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// RUMMY_HOME may not exist yet
|
|
194
|
-
}
|
|
199
|
+
writeFile(this.#lastRunPath, `${this.#turnLog.join("\n")}\n`).catch(
|
|
200
|
+
() => {},
|
|
201
|
+
);
|
|
195
202
|
}
|
|
196
203
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# think
|
|
2
|
+
|
|
3
|
+
Provides a `<think>` tag for model reasoning. Not a tool — does not
|
|
4
|
+
appear in the tool list.
|
|
5
|
+
|
|
6
|
+
## Registration
|
|
7
|
+
|
|
8
|
+
- **Scheme**: `think` — `category: "logging"`, `model_visible: 0`
|
|
9
|
+
- **No handler, no view, no tool registration**
|
|
10
|
+
|
|
11
|
+
## Behavior
|
|
12
|
+
|
|
13
|
+
The model writes `<think>reasoning</think>` before tool commands.
|
|
14
|
+
XmlParser captures it, TurnExecutor records it as a `think://` entry.
|
|
15
|
+
Invisible to the model on subsequent turns (`model_visible: 0`).
|
|
16
|
+
Available for debugging and audit.
|
|
17
|
+
|
|
18
|
+
Models with server-side reasoning (extended thinking) use that
|
|
19
|
+
capability independently. The `<think>` tag is a floor — every model
|
|
20
|
+
gets at least this.
|
|
@@ -7,9 +7,9 @@ The Rumsfeld mechanism. The model registers what it doesn't know before acting.
|
|
|
7
7
|
## Registration
|
|
8
8
|
|
|
9
9
|
- **Tool**: `unknown`
|
|
10
|
-
- **
|
|
11
|
-
- **Category**: structural
|
|
10
|
+
- **Category**: `unknown`
|
|
12
11
|
- **Handler**: None — recorded by TurnExecutor, deduplicated against existing unknowns.
|
|
12
|
+
- **Filter**: `assembly.system` at priority 300 — renders `<unknowns>` section.
|
|
13
13
|
|
|
14
14
|
## Projection
|
|
15
15
|
|
|
@@ -18,6 +18,7 @@ The Rumsfeld mechanism. The model registers what it doesn't know before acting.
|
|
|
18
18
|
## Behavior
|
|
19
19
|
|
|
20
20
|
Unknowns are sticky — they persist across turns until the model explicitly
|
|
21
|
-
|
|
22
|
-
`<env>`, or `<ask_user>`, then removes resolved ones
|
|
23
|
-
|
|
21
|
+
removes them with `<rm>`. The model investigates unknowns using `<get>`,
|
|
22
|
+
`<env>`, or `<ask_user>`, then removes resolved ones. Server deduplicates
|
|
23
|
+
on insert. Each unknown renders with turn, fidelity, and tokens for
|
|
24
|
+
temporal reasoning and context management.
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import docs from "./unknownDoc.js";
|
|
2
2
|
|
|
3
3
|
export default class Unknown {
|
|
4
4
|
#core;
|
|
5
5
|
|
|
6
6
|
constructor(core) {
|
|
7
7
|
this.#core = core;
|
|
8
|
+
core.ensureTool();
|
|
8
9
|
core.registerScheme({
|
|
9
|
-
category: "
|
|
10
|
+
category: "unknown",
|
|
10
11
|
});
|
|
11
12
|
core.on("full", this.full.bind(this));
|
|
12
13
|
core.filter("assembly.system", this.assembleUnknowns.bind(this), 300);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
);
|
|
14
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
15
|
+
docsMap.unknown = docs;
|
|
16
|
+
return docsMap;
|
|
17
|
+
});
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
full(entry) {
|
|
@@ -24,9 +25,11 @@ export default class Unknown {
|
|
|
24
25
|
const entries = ctx.rows.filter((r) => r.category === "unknown");
|
|
25
26
|
if (entries.length === 0) return content;
|
|
26
27
|
|
|
27
|
-
const lines = entries.map(
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const lines = entries.map((u) => {
|
|
29
|
+
const fidelity = u.fidelity ? ` fidelity="${u.fidelity}"` : "";
|
|
30
|
+
const tokens = u.tokens ? ` tokens="${u.tokens}"` : "";
|
|
31
|
+
return `<unknown path="${u.path}" turn="${u.source_turn || u.turn}"${fidelity}${tokens}>${u.body}</unknown>`;
|
|
32
|
+
});
|
|
30
33
|
return `${content}\n\n<unknowns>\n${lines.join("\n")}\n</unknowns>`;
|
|
31
34
|
}
|
|
32
35
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Tool doc for <unknown>. 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
|
+
// --- Syntax: body = what you need to learn
|
|
6
|
+
[
|
|
7
|
+
`## <unknown>[specific thing I need to learn]</unknown> - Track open questions`,
|
|
8
|
+
],
|
|
9
|
+
|
|
10
|
+
// --- Examples: concrete unknowns, not abstract
|
|
11
|
+
[
|
|
12
|
+
`Example: <unknown path="unknown://answer">contents of answer.txt</unknown>`,
|
|
13
|
+
`Specific and actionable. Shows that unknowns are concrete investigation targets.`,
|
|
14
|
+
],
|
|
15
|
+
[
|
|
16
|
+
`Example: <unknown>which database adapter is configured</unknown>`,
|
|
17
|
+
`Domain question. Shows unknowns for configuration/architecture questions.`,
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
// --- Lifecycle: register → investigate → resolve
|
|
21
|
+
[
|
|
22
|
+
`* Investigate with Tool Commands`,
|
|
23
|
+
`Cross-tool lifecycle: unknowns drive get/env/ask_user actions.`,
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
`* When resolved or irrelevant, remove with <rm path="unknown://..."/>`,
|
|
27
|
+
`Cross-tool lifecycle: rm cleans resolved unknowns from context.`,
|
|
28
|
+
],
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export default LINES.map(([text]) => text).join("\n");
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
# update
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lifecycle signal — the model declares it has more work to do.
|
|
4
4
|
|
|
5
5
|
## Registration
|
|
6
6
|
|
|
7
7
|
- **Tool**: `update`
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **Handler**: None — projection only.
|
|
8
|
+
- **Category**: `logging`
|
|
9
|
+
- **Handler**: None — recorded by TurnExecutor as a lifecycle signal.
|
|
11
10
|
|
|
12
11
|
## Projection
|
|
13
12
|
|
|
14
13
|
Shows `update` followed by the entry body.
|
|
15
|
-
|
|
16
|
-
## Behavior
|
|
17
|
-
|
|
18
|
-
No handler logic. Allows the model to emit progress/status entries that appear in context via projection.
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import docs from "./updateDoc.js";
|
|
2
2
|
|
|
3
3
|
export default class Update {
|
|
4
4
|
#core;
|
|
5
5
|
|
|
6
6
|
constructor(core) {
|
|
7
7
|
this.#core = core;
|
|
8
|
-
core.
|
|
8
|
+
core.ensureTool();
|
|
9
|
+
core.registerScheme({ category: "logging" });
|
|
9
10
|
core.on("full", this.full.bind(this));
|
|
10
11
|
core.on("summary", this.summary.bind(this));
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
);
|
|
12
|
+
core.filter("instructions.toolDocs", async (docsMap) => {
|
|
13
|
+
docsMap.update = docs;
|
|
14
|
+
return docsMap;
|
|
15
|
+
});
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
full(entry) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Tool doc for <update>. 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
|
+
// --- Syntax
|
|
6
|
+
["## <update>[brief status]</update> - Signal continuation"],
|
|
7
|
+
|
|
8
|
+
// --- Examples: research progress and multi-step work
|
|
9
|
+
[
|
|
10
|
+
"Example: <update>Reading config files</update>",
|
|
11
|
+
"Progress checkpoint. Shows update as a status signal, not a log entry.",
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
"Example: <update>Found 3 issues, fixing first</update>",
|
|
15
|
+
"Multi-step progress. Shows update for ongoing work.",
|
|
16
|
+
],
|
|
17
|
+
|
|
18
|
+
// --- Constraints: RFC-style MUST/MUST NOT
|
|
19
|
+
[
|
|
20
|
+
"* YOU MUST use <update> if still working — describes the current state",
|
|
21
|
+
"Continuation signal. Triggers the next turn in the loop.",
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"* YOU MUST NOT use <update> if done — use <summarize/> instead",
|
|
25
|
+
"Mutual exclusion with summarize. Prevents infinite loops.",
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"* YOU MUST keep <update> to <= 80 characters",
|
|
29
|
+
"Length cap. Prevents models from writing essays in status updates.",
|
|
30
|
+
],
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export default LINES.map(([text]) => text).join("\n");
|