@possumtech/rummy 0.5.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 -5
  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 -330
  13. package/src/agent/ContextAssembler.js +4 -4
  14. package/src/agent/Entries.js +655 -0
  15. package/src/agent/ProjectAgent.js +30 -18
  16. package/src/agent/TurnExecutor.js +229 -421
  17. package/src/agent/XmlParser.js +99 -33
  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 -125
  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 +29 -21
  31. package/src/{server → hooks}/RpcRegistry.js +2 -1
  32. package/src/hooks/RummyContext.js +135 -35
  33. package/src/hooks/ToolRegistry.js +21 -16
  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 -25
  39. package/src/plugins/ask_user/ask_userDoc.md +10 -0
  40. package/src/plugins/budget/README.md +27 -25
  41. package/src/plugins/budget/budget.js +260 -88
  42. package/src/plugins/cp/README.md +2 -2
  43. package/src/plugins/cp/cp.js +29 -11
  44. package/src/plugins/cp/cpDoc.js +2 -15
  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 +45 -6
  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 -2
  57. package/src/plugins/get/README.md +1 -1
  58. package/src/plugins/get/get.js +103 -48
  59. package/src/plugins/get/getDoc.js +2 -32
  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 +42 -2
  66. package/src/plugins/index.js +146 -123
  67. package/src/plugins/instructions/README.md +35 -9
  68. package/src/plugins/instructions/instructions.js +122 -9
  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 +67 -36
  78. package/src/plugins/known/knownDoc.js +2 -17
  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 +55 -22
  84. package/src/plugins/mv/mvDoc.js +2 -18
  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 +58 -16
  98. package/src/plugins/rm/README.md +1 -1
  99. package/src/plugins/rm/rm.js +56 -12
  100. package/src/plugins/rm/rmDoc.js +2 -20
  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 -75
  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 +50 -6
  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 -18
  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 +129 -80
  118. package/src/plugins/think/README.md +1 -1
  119. package/src/plugins/think/think.js +12 -0
  120. package/src/plugins/think/thinkDoc.js +2 -15
  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 +47 -19
  124. package/src/plugins/unknown/unknownDoc.js +2 -21
  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 -30
  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/v_model_context.sql +27 -31
  136. package/src/sql/v_run_log.sql +9 -14
  137. package/EXCEPTIONS.md +0 -46
  138. package/FIDELITY_CONTRACT.md +0 -172
  139. package/src/agent/KnownStore.js +0 -337
  140. package/src/agent/ResponseHealer.js +0 -241
  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 -45
  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 -56
  149. package/src/plugins/progress/README.md +0 -16
  150. package/src/plugins/progress/progress.js +0 -43
  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 -27
@@ -1,44 +1,59 @@
1
- import KnownStore from "../../agent/KnownStore.js";
1
+ import Entries from "../../agent/Entries.js";
2
2
 
3
3
  export default class Policy {
4
+ #core;
5
+
4
6
  constructor(core) {
7
+ this.#core = core;
5
8
  core.filter("entry.recording", this.#enforceAskMode.bind(this), 1);
6
9
  }
7
10
 
11
+ async #reject(ctx, message) {
12
+ await this.#core.hooks.error.log.emit({
13
+ store: ctx.store,
14
+ runId: ctx.runId,
15
+ turn: ctx.turn,
16
+ loopId: ctx.loopId,
17
+ message,
18
+ });
19
+ }
20
+
8
21
  async #enforceAskMode(entry, ctx) {
9
22
  if (ctx.mode !== "ask") return entry;
10
23
 
11
24
  if (entry.scheme === "sh") {
12
- console.warn("[RUMMY] Rejected <sh> in ask mode");
13
- return { ...entry, status: 403 };
25
+ await this.#reject(ctx, "Rejected <sh> in ask mode");
26
+ return { ...entry, state: "failed", outcome: "permission" };
14
27
  }
15
28
 
16
29
  if (entry.scheme === "set" && entry.attributes?.path) {
17
- const scheme = KnownStore.scheme(entry.attributes.path);
30
+ const scheme = Entries.scheme(entry.attributes.path);
18
31
  if (scheme === null && entry.body) {
19
- console.warn(
20
- `[RUMMY] Rejected file edit to ${entry.attributes.path} in ask mode`,
32
+ await this.#reject(
33
+ ctx,
34
+ `Rejected file edit to ${entry.attributes.path} in ask mode`,
21
35
  );
22
- return { ...entry, status: 403 };
36
+ return { ...entry, state: "failed", outcome: "permission" };
23
37
  }
24
38
  }
25
39
 
26
40
  if (entry.scheme === "rm") {
27
41
  const pathAttr = entry.attributes?.path || entry.path;
28
- const scheme = KnownStore.scheme(pathAttr);
42
+ const scheme = Entries.scheme(pathAttr);
29
43
  if (scheme === null) {
30
- console.warn(`[RUMMY] Rejected file rm of ${pathAttr} in ask mode`);
31
- return { ...entry, status: 403 };
44
+ await this.#reject(ctx, `Rejected file rm of ${pathAttr} in ask mode`);
45
+ return { ...entry, state: "failed", outcome: "permission" };
32
46
  }
33
47
  }
34
48
 
35
49
  if (entry.scheme === "mv" || entry.scheme === "cp") {
36
- const destScheme = KnownStore.scheme(entry.attributes?.to);
50
+ const destScheme = Entries.scheme(entry.attributes?.to);
37
51
  if (destScheme === null) {
38
- console.warn(
39
- `[RUMMY] Rejected ${entry.scheme} to file ${entry.attributes?.to} in ask mode`,
52
+ await this.#reject(
53
+ ctx,
54
+ `Rejected ${entry.scheme} to file ${entry.attributes?.to} in ask mode`,
40
55
  );
41
- return { ...entry, status: 403 };
56
+ return { ...entry, state: "failed", outcome: "permission" };
42
57
  }
43
58
  }
44
59
 
@@ -1,4 +1,4 @@
1
- # prompt
1
+ # prompt {#prompt_plugin}
2
2
 
3
3
  Renders the `<prompt mode="ask|act">` tag at the end of the user message.
4
4
  Always present on every turn — the model always sees its task.
@@ -3,17 +3,16 @@ export default class Prompt {
3
3
 
4
4
  constructor(core) {
5
5
  this.#core = core;
6
- core.hooks.tools.onView("prompt", (entry) => entry.body, "promoted");
6
+ core.hooks.tools.onView("prompt", (entry) => entry.body, "visible");
7
7
  core.hooks.tools.onView(
8
8
  "prompt",
9
9
  (entry) => {
10
10
  const limit = 500;
11
- const text = entry.body?.slice(0, limit) || "";
12
- return text.length < (entry.body?.length || 0)
13
- ? `${text}\n[truncated — promote to see the complete prompt]`
14
- : text;
11
+ const full = entry.body;
12
+ if (full.length <= limit) return full;
13
+ return `${full.slice(0, limit)}\n[truncated — promote to see the complete prompt]`;
15
14
  },
16
- "demoted",
15
+ "summarized",
17
16
  );
18
17
  core.on("turn.started", this.onTurnStarted.bind(this));
19
18
  core.filter("assembly.user", this.assemblePrompt.bind(this), 300);
@@ -23,15 +22,23 @@ export default class Prompt {
23
22
  const { entries: store, sequence: turn, runId, loopId } = rummy;
24
23
 
25
24
  if (!isContinuation && prompt) {
26
- await store.upsert(runId, turn, `prompt://${turn}`, prompt, 200, {
25
+ // prompt:// writable_by: ["plugin"] explicit for clarity.
26
+ await store.set({
27
+ runId,
28
+ turn,
29
+ path: `prompt://${turn}`,
30
+ body: prompt,
31
+ state: "resolved",
27
32
  attributes: { mode },
28
33
  loopId,
34
+ writer: "plugin",
29
35
  });
30
36
  }
31
37
  }
32
38
 
33
39
  async assemblePrompt(content, ctx) {
34
- const promptEntry = ctx.rows.findLast(
40
+ const { rows, contextSize, toolSet } = ctx;
41
+ const promptEntry = rows.findLast(
35
42
  (r) => r.category === "prompt" && r.scheme === "prompt",
36
43
  );
37
44
 
@@ -39,16 +46,51 @@ export default class Prompt {
39
46
  typeof promptEntry?.attributes === "string"
40
47
  ? JSON.parse(promptEntry.attributes)
41
48
  : promptEntry?.attributes;
42
- const mode = attrs?.mode || ctx.type;
43
- const body = promptEntry?.body || "";
44
- // No tools="..." attribute. The OpenAI-shaped
45
- // `<prompt mode tools="x,y,z">` rendering was priming gemma's
46
- // native-tool-call training prior — A/B test confirmed removing
47
- // the attribute dropped native-format emissions from ~50% to 0%.
48
- // Tools list lives in the system prompt as "XML Command Tools:".
49
+ const mode = attrs?.mode ? attrs.mode : ctx.type;
50
+ const body = promptEntry ? promptEntry.body : "";
51
+ const activeTools = toolSet
52
+ ? new Set(toolSet)
53
+ : new Set(this.#core.hooks.tools.names);
54
+ const commands = this.#core.hooks.tools.advertisedNames
55
+ .filter((n) => activeTools.has(n))
56
+ .join(",");
49
57
  let warn = "";
50
58
  if (mode === "ask") warn = ' warn="File editing disallowed."';
51
59
 
52
- return `${content}<prompt mode="${mode}"${warn}>${body}</prompt>`;
60
+ // Surface the most recent prior-turn budget demotion as a
61
+ // `reverted="N"` attribute on <prompt>. Historical error
62
+ // entries sit in <log> but read as ambient noise; this signal
63
+ // is dynamic and always fresh — the model sees that its
64
+ // promotions last turn were reverted, in the same spot where
65
+ // it reads budget numbers.
66
+ let reverted = "";
67
+ const priorTurn = ctx.turn - 1;
68
+ if (priorTurn >= 1) {
69
+ const priorDemotion = rows.find((r) => {
70
+ if (!r.path.startsWith(`log://turn_${priorTurn}/error/`)) return false;
71
+ const attrs =
72
+ typeof r.attributes === "string"
73
+ ? JSON.parse(r.attributes)
74
+ : r.attributes;
75
+ return attrs?.status === 413 && attrs?.demotedCount > 0;
76
+ });
77
+ if (priorDemotion) {
78
+ const attrs =
79
+ typeof priorDemotion.attributes === "string"
80
+ ? JSON.parse(priorDemotion.attributes)
81
+ : priorDemotion.attributes;
82
+ reverted = ` reverted="${attrs.demotedCount}"`;
83
+ }
84
+ }
85
+
86
+ const path = promptEntry ? ` path="${promptEntry.path}"` : "";
87
+ const visibility = promptEntry?.visibility
88
+ ? ` visibility="${promptEntry.visibility}"`
89
+ : "";
90
+ const tokens =
91
+ promptEntry?.aTokens != null
92
+ ? ` tokens="${promptEntry.aTokens}"`
93
+ : "";
94
+ return `${content}<prompt mode="${mode}"${path} commands="${commands}"${warn}${reverted}${visibility}${tokens}>${body}</prompt>`;
53
95
  }
54
96
  }
@@ -1,4 +1,4 @@
1
- # rm
1
+ # rm {#rm_plugin}
2
2
 
3
3
  Removes entries by path or glob pattern.
4
4
 
@@ -1,6 +1,8 @@
1
- import KnownStore from "../../agent/KnownStore.js";
1
+ import Entries from "../../agent/Entries.js";
2
2
  import docs from "./rmDoc.js";
3
3
 
4
+ const LOG_ACTION_RE = /^log:\/\/turn_\d+\/(\w+)\//;
5
+
4
6
  export default class Rm {
5
7
  #core;
6
8
 
@@ -8,25 +10,50 @@ export default class Rm {
8
10
  this.#core = core;
9
11
  core.registerScheme();
10
12
  core.on("handler", this.handler.bind(this));
11
- core.on("promoted", this.full.bind(this));
12
- core.on("demoted", this.summary.bind(this));
13
+ core.on("visible", this.full.bind(this));
14
+ core.on("summarized", this.summary.bind(this));
13
15
  core.filter("instructions.toolDocs", async (docsMap) => {
14
16
  docsMap.rm = docs;
15
17
  return docsMap;
16
18
  });
19
+ core.on("proposal.accepted", this.#onAccepted.bind(this));
20
+ }
21
+
22
+ async #onAccepted(ctx) {
23
+ const m = LOG_ACTION_RE.exec(ctx.path);
24
+ if (m?.[1] !== "rm") return;
25
+ const target = ctx.attrs?.path;
26
+ if (!target) return;
27
+ await ctx.entries.rm({ runId: ctx.runId, path: target });
28
+ if (ctx.projectRoot) {
29
+ const { unlink } = await import("node:fs/promises");
30
+ const { join } = await import("node:path");
31
+ try {
32
+ await unlink(join(ctx.projectRoot, target));
33
+ } catch (err) {
34
+ // File may already be absent — entry rm'd regardless.
35
+ if (err.code !== "ENOENT") throw err;
36
+ }
37
+ }
17
38
  }
18
39
 
19
40
  async handler(entry, rummy) {
20
41
  const { entries: store, sequence: turn, runId, loopId } = rummy;
21
42
  const target = entry.attributes.path;
22
43
  if (!target) {
23
- await store.upsert(runId, turn, entry.resultPath, "", 400, {
44
+ await store.set({
45
+ runId,
46
+ turn,
47
+ path: entry.resultPath,
48
+ body: "",
49
+ state: "failed",
50
+ outcome: "validation",
24
51
  attributes: { error: "path is required" },
25
52
  loopId,
26
53
  });
27
54
  return;
28
55
  }
29
- const normalized = KnownStore.normalizePath(target);
56
+ const normalized = Entries.normalizePath(target);
30
57
  const matches = await store.getEntriesByPattern(
31
58
  runId,
32
59
  normalized,
@@ -34,7 +61,13 @@ export default class Rm {
34
61
  );
35
62
 
36
63
  if (matches.length === 0) {
37
- await store.upsert(runId, turn, entry.resultPath, "", 404, {
64
+ await store.set({
65
+ runId,
66
+ turn,
67
+ path: entry.resultPath,
68
+ body: "",
69
+ state: "failed",
70
+ outcome: "not_found",
38
71
  attributes: { path: target, error: `${target} not found` },
39
72
  loopId,
40
73
  });
@@ -45,24 +78,35 @@ export default class Rm {
45
78
  const schemeMatches = matches.filter((m) => m.scheme !== null);
46
79
 
47
80
  // Scheme entries: remove all, write one aggregate result entry
48
- for (const match of schemeMatches) await store.remove(runId, match.path);
81
+ for (const match of schemeMatches)
82
+ await store.rm({ runId: runId, path: match.path });
49
83
  if (schemeMatches.length > 0) {
50
84
  const paths = schemeMatches.map((m) => m.path).join("\n");
51
- await store.upsert(runId, turn, entry.resultPath, paths, 200, {
85
+ await store.set({
86
+ runId,
87
+ turn,
88
+ path: entry.resultPath,
89
+ body: paths,
90
+ state: "resolved",
52
91
  attributes: { path: target },
53
92
  loopId,
54
93
  });
55
94
  }
56
95
 
57
- // File entries: individual 202 proposals (require user resolution)
96
+ // File entries: individual proposals (require user resolution)
58
97
  if (fileMatches.length > 0 && schemeMatches.length > 0)
59
- await store.remove(runId, entry.resultPath);
98
+ await store.rm({ runId: runId, path: entry.resultPath });
60
99
  for (const match of fileMatches) {
61
100
  const resultPath =
62
101
  schemeMatches.length === 0 && fileMatches.length === 1
63
102
  ? entry.resultPath
64
- : await store.dedup(runId, "rm", match.path, turn);
65
- await store.upsert(runId, turn, resultPath, match.path, 202, {
103
+ : await store.logPath(runId, turn, "rm", match.path);
104
+ await store.set({
105
+ runId,
106
+ turn,
107
+ path: resultPath,
108
+ body: match.path,
109
+ state: "proposed",
66
110
  attributes: { path: match.path },
67
111
  loopId,
68
112
  });
@@ -1,21 +1,3 @@
1
- // Tool doc for <rm>. 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
- ['## <rm path="[path]"/> - Remove a file or entry'],
6
- ['Example: <rm path="src/config.js"/>', "File removal. Simplest form."],
7
- [
8
- 'Example: <rm path="known://temp_*" preview/>',
9
- "Preview before deleting. Safety pattern for bulk operations.",
10
- ],
11
- [
12
- '* Permanent. Prefer <set path="..." fidelity="archived"/> to preserve for later retrieval',
13
- "Nudges toward archive over rm. Path attr included so the model sees a complete invocation shape, not a fragment.",
14
- ],
15
- [
16
- "* `preview` shows what paths would be affected without performing the operation.",
17
- "Canonical preview teaching lives here — rm is the most intuitive 'check before committing' case. Model generalizes to cp/mv/get by analogy. Advanced uses (e.g. archive rediscovery via <get preview>) belong in persona/skill docs, not here.",
18
- ],
19
- ];
1
+ import { loadDoc } from "../helpers.js";
20
2
 
21
- export default LINES.map(([text]) => text).join("\n");
3
+ export default loadDoc(import.meta.url, "rmDoc.md");
@@ -0,0 +1,13 @@
1
+ ## <rm path="[path]"/> - Remove a file or entry
2
+
3
+ Example: <rm path="src/config.js"/>
4
+ <!-- File removal. Simplest form. -->
5
+
6
+ Example: <rm path="known://temp_*" preview/>
7
+ <!-- Preview before deleting. Safety pattern for bulk operations. -->
8
+
9
+ * Permanent. Prefer <set path="..." visibility="archived"/> to preserve for later retrieval
10
+ <!-- Nudges toward archive over rm. Path attr included so the model sees a complete invocation shape, not a fragment. -->
11
+
12
+ * `preview` shows what paths would be affected without performing the operation.
13
+ <!-- Canonical preview teaching lives here — rm is the most intuitive 'check before committing' case. Model generalizes to cp/mv/get by analogy. Advanced uses (e.g. archive rediscovery via <get preview>) belong in persona/skill docs, not here. -->
@@ -1,4 +1,4 @@
1
- # rpc
1
+ # rpc {#rpc_plugin}
2
2
 
3
3
  Registers core RPC methods and provides automatic tool dispatch for
4
4
  all registered tools.
@@ -29,4 +29,4 @@ all registered tools.
29
29
  - `getRuns`, `getRun`
30
30
 
31
31
  ### Notifications
32
- - `run/state`, `run/progress`, `ui/render`, `ui/notify`
32
+ - `run/state`, `run/progress`, `run/proposal`, `ui/render`, `ui/notify`, `stream/cancelled`