@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.
- package/.env.example +21 -5
- 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 -330
- package/src/agent/ContextAssembler.js +4 -4
- package/src/agent/Entries.js +655 -0
- package/src/agent/ProjectAgent.js +30 -18
- package/src/agent/TurnExecutor.js +229 -421
- package/src/agent/XmlParser.js +99 -33
- 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 -125
- 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 +29 -21
- package/src/{server → hooks}/RpcRegistry.js +2 -1
- package/src/hooks/RummyContext.js +135 -35
- package/src/hooks/ToolRegistry.js +21 -16
- 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 -25
- package/src/plugins/ask_user/ask_userDoc.md +10 -0
- package/src/plugins/budget/README.md +27 -25
- package/src/plugins/budget/budget.js +260 -88
- package/src/plugins/cp/README.md +2 -2
- package/src/plugins/cp/cp.js +29 -11
- package/src/plugins/cp/cpDoc.js +2 -15
- 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 +45 -6
- 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 -2
- package/src/plugins/get/README.md +1 -1
- package/src/plugins/get/get.js +103 -48
- package/src/plugins/get/getDoc.js +2 -32
- 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 +42 -2
- package/src/plugins/index.js +146 -123
- package/src/plugins/instructions/README.md +35 -9
- package/src/plugins/instructions/instructions.js +122 -9
- 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 +67 -36
- package/src/plugins/known/knownDoc.js +2 -17
- 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 +55 -22
- package/src/plugins/mv/mvDoc.js +2 -18
- 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 +58 -16
- package/src/plugins/rm/README.md +1 -1
- package/src/plugins/rm/rm.js +56 -12
- package/src/plugins/rm/rmDoc.js +2 -20
- 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 -75
- 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 +50 -6
- 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 -18
- 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 +129 -80
- package/src/plugins/think/README.md +1 -1
- package/src/plugins/think/think.js +12 -0
- package/src/plugins/think/thinkDoc.js +2 -15
- package/src/plugins/think/thinkDoc.md +7 -0
- package/src/plugins/unknown/README.md +3 -3
- package/src/plugins/unknown/unknown.js +47 -19
- package/src/plugins/unknown/unknownDoc.js +2 -21
- 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 -30
- 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/v_model_context.sql +27 -31
- package/src/sql/v_run_log.sql +9 -14
- package/EXCEPTIONS.md +0 -46
- package/FIDELITY_CONTRACT.md +0 -172
- package/src/agent/KnownStore.js +0 -337
- package/src/agent/ResponseHealer.js +0 -241
- 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 -45
- 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 -56
- package/src/plugins/progress/README.md +0 -16
- package/src/plugins/progress/progress.js +0 -43
- package/src/plugins/summarize/README.md +0 -19
- package/src/plugins/summarize/summarize.js +0 -32
- package/src/plugins/summarize/summarizeDoc.js +0 -27
package/src/plugins/rpc/rpc.js
CHANGED
|
@@ -2,6 +2,8 @@ import msg from "../../agent/messages.js";
|
|
|
2
2
|
import RummyContext from "../../hooks/RummyContext.js";
|
|
3
3
|
import File from "../file/file.js";
|
|
4
4
|
|
|
5
|
+
const CONSTRAINT_VISIBILITIES = new Set(["active", "readonly", "ignore"]);
|
|
6
|
+
|
|
5
7
|
export default class Rpc {
|
|
6
8
|
#core;
|
|
7
9
|
|
|
@@ -22,21 +24,176 @@ export default class Rpc {
|
|
|
22
24
|
description: "Returns { methods, notifications } catalog.",
|
|
23
25
|
});
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
// --- Primitives (SPEC primitives) ---
|
|
28
|
+
// The client surface is a thin projection of the plugin API.
|
|
29
|
+
// Six verbs, each takes an object of entry-grammar params.
|
|
30
|
+
// Writer is fixed to "client"; permissions enforced per scheme.
|
|
31
|
+
|
|
32
|
+
r.register("set", {
|
|
33
|
+
handler: async (params, ctx) => {
|
|
34
|
+
return await this.#dispatchSet(params, ctx);
|
|
35
|
+
},
|
|
36
|
+
description:
|
|
37
|
+
"Create or update an entry. Wide semantic: write content, change " +
|
|
38
|
+
"visibility/state, merge attributes, append (streaming), pattern update. " +
|
|
39
|
+
"Writing to run://<alias> starts or cancels a run.",
|
|
40
|
+
params: {
|
|
41
|
+
run: "string — run alias (except for new run:// writes, where the alias is in the path)",
|
|
42
|
+
path: "string — entry path (e.g. known://fact or run://abc)",
|
|
43
|
+
body: "string? — entry body",
|
|
44
|
+
state: "string? — proposed | streaming | resolved | failed | cancelled",
|
|
45
|
+
visibility: "string? — visible | summarized | archived",
|
|
46
|
+
outcome: "string? — reason when state ∈ {failed, cancelled}",
|
|
47
|
+
attributes: "object? — JSON attributes",
|
|
48
|
+
append: "boolean? — append body rather than overwrite",
|
|
49
|
+
pattern: "boolean? — treat path as a glob pattern for bulk update",
|
|
50
|
+
bodyFilter: "string? — narrow pattern matches by body content",
|
|
51
|
+
},
|
|
52
|
+
requiresInit: true,
|
|
53
|
+
longRunning: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
r.register("get", {
|
|
57
|
+
handler: async (params, ctx) => {
|
|
58
|
+
return await this.#dispatchGet(params, ctx);
|
|
59
|
+
},
|
|
60
|
+
description:
|
|
61
|
+
"Promote an entry (or matching pattern) to visible visibility.",
|
|
62
|
+
params: {
|
|
63
|
+
run: "string — run alias",
|
|
64
|
+
path: "string — entry path or glob pattern",
|
|
65
|
+
bodyFilter: "string? — narrow pattern matches by body content",
|
|
66
|
+
visibility: "string? — target visibility (default: visible)",
|
|
67
|
+
},
|
|
68
|
+
requiresInit: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
r.register("rm", {
|
|
72
|
+
handler: async (params, ctx) => {
|
|
73
|
+
return await this.#dispatchRm(params, ctx);
|
|
74
|
+
},
|
|
75
|
+
description: "Remove an entry's view (or matching pattern).",
|
|
76
|
+
params: {
|
|
77
|
+
run: "string — run alias",
|
|
78
|
+
path: "string — entry path or glob pattern",
|
|
79
|
+
bodyFilter: "string? — narrow pattern matches by body content",
|
|
80
|
+
},
|
|
81
|
+
requiresInit: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
r.register("cp", {
|
|
85
|
+
handler: async (params, ctx) => {
|
|
86
|
+
const runRow = await this.#resolveRun(params.run, ctx);
|
|
87
|
+
await ctx.projectAgent.entries.cp({
|
|
88
|
+
runId: runRow.id,
|
|
89
|
+
from: params.from,
|
|
90
|
+
to: params.to,
|
|
91
|
+
visibility: params.visibility,
|
|
92
|
+
writer: "client",
|
|
93
|
+
});
|
|
94
|
+
return { ok: true };
|
|
95
|
+
},
|
|
96
|
+
description: "Copy an entry to a new path.",
|
|
97
|
+
params: {
|
|
98
|
+
run: "string — run alias",
|
|
99
|
+
from: "string — source path",
|
|
100
|
+
to: "string — destination path",
|
|
101
|
+
visibility: "string? — target visibility (default: visible)",
|
|
102
|
+
},
|
|
103
|
+
requiresInit: true,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
r.register("mv", {
|
|
107
|
+
handler: async (params, ctx) => {
|
|
108
|
+
const runRow = await this.#resolveRun(params.run, ctx);
|
|
109
|
+
await ctx.projectAgent.entries.mv({
|
|
110
|
+
runId: runRow.id,
|
|
111
|
+
from: params.from,
|
|
112
|
+
to: params.to,
|
|
113
|
+
visibility: params.visibility,
|
|
114
|
+
writer: "client",
|
|
115
|
+
});
|
|
116
|
+
return { ok: true };
|
|
117
|
+
},
|
|
118
|
+
description: "Rename an entry (copy then remove source).",
|
|
119
|
+
params: {
|
|
120
|
+
run: "string — run alias",
|
|
121
|
+
from: "string — source path",
|
|
122
|
+
to: "string — destination path",
|
|
123
|
+
visibility: "string? — target visibility (default: visible)",
|
|
124
|
+
},
|
|
125
|
+
requiresInit: true,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
r.register("update", {
|
|
26
129
|
handler: async (params, ctx) => {
|
|
130
|
+
const runRow = await this.#resolveRun(params.run, ctx);
|
|
131
|
+
const { status = 102, attributes = {} } = params;
|
|
132
|
+
const path = await ctx.projectAgent.entries.update({
|
|
133
|
+
runId: runRow.id,
|
|
134
|
+
body: params.body,
|
|
135
|
+
status,
|
|
136
|
+
attributes,
|
|
137
|
+
writer: "client",
|
|
138
|
+
});
|
|
139
|
+
return { ok: true, path };
|
|
140
|
+
},
|
|
141
|
+
description:
|
|
142
|
+
"Write an update:// entry carrying a turn's continuation/terminal " +
|
|
143
|
+
"signal. Not general — this is the lifecycle verb.",
|
|
144
|
+
params: {
|
|
145
|
+
run: "string — run alias",
|
|
146
|
+
body: "string — update text",
|
|
147
|
+
status:
|
|
148
|
+
"number? — 102 (continue) | 200/204 (terminal) | 422 (can't answer)",
|
|
149
|
+
attributes: "object? — extra attributes",
|
|
150
|
+
},
|
|
151
|
+
requiresInit: true,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Connection handshake. First call a client makes. Establishes
|
|
155
|
+
// the project identity for this connection and announces the
|
|
156
|
+
// server's protocol version.
|
|
157
|
+
r.register("rummy/hello", {
|
|
158
|
+
handler: async (params, ctx) => {
|
|
159
|
+
const { RUMMY_PROTOCOL_VERSION } = await import(
|
|
160
|
+
"../../server/protocol.js"
|
|
161
|
+
);
|
|
162
|
+
if (params.clientVersion) {
|
|
163
|
+
const clientMajor = String(params.clientVersion).split(".")[0];
|
|
164
|
+
const serverMajor = RUMMY_PROTOCOL_VERSION.split(".")[0];
|
|
165
|
+
if (clientMajor !== serverMajor) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`protocol mismatch: server ${RUMMY_PROTOCOL_VERSION}, client ${params.clientVersion}. Clients must match MAJOR.`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (!params.name) throw new Error("rummy/hello: name is required");
|
|
172
|
+
if (!params.projectRoot) {
|
|
173
|
+
throw new Error("rummy/hello: projectRoot is required");
|
|
174
|
+
}
|
|
27
175
|
const result = await ctx.projectAgent.init(
|
|
28
176
|
params.name,
|
|
29
177
|
params.projectRoot,
|
|
30
178
|
params.configPath,
|
|
31
179
|
);
|
|
32
180
|
ctx.setContext(result.projectId, params.projectRoot);
|
|
33
|
-
return
|
|
181
|
+
return {
|
|
182
|
+
rummyVersion: RUMMY_PROTOCOL_VERSION,
|
|
183
|
+
projectId: result.projectId,
|
|
184
|
+
projectRoot: params.projectRoot,
|
|
185
|
+
};
|
|
34
186
|
},
|
|
35
|
-
description:
|
|
187
|
+
description:
|
|
188
|
+
"Connection handshake. First call a client makes. Establishes the " +
|
|
189
|
+
"project identity and returns the server's protocol version. " +
|
|
190
|
+
"Clients must match MAJOR or the call rejects.",
|
|
36
191
|
params: {
|
|
37
192
|
name: "string — project name (unique identifier)",
|
|
38
193
|
projectRoot: "string — absolute path to source code",
|
|
39
194
|
configPath: "string? — path to rummy config directory",
|
|
195
|
+
clientVersion:
|
|
196
|
+
"string? — client's protocol version; server rejects MAJOR mismatch",
|
|
40
197
|
},
|
|
41
198
|
});
|
|
42
199
|
|
|
@@ -44,10 +201,8 @@ export default class Rpc {
|
|
|
44
201
|
|
|
45
202
|
r.register("getModels", {
|
|
46
203
|
handler: async (params, ctx) => {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
offset: params.offset ?? null,
|
|
50
|
-
});
|
|
204
|
+
const { limit = null, offset = null } = params;
|
|
205
|
+
const rows = await ctx.db.get_models.all({ limit, offset });
|
|
51
206
|
return rows.map((m) => ({
|
|
52
207
|
alias: m.alias,
|
|
53
208
|
actual: m.actual,
|
|
@@ -63,10 +218,11 @@ export default class Rpc {
|
|
|
63
218
|
|
|
64
219
|
r.register("addModel", {
|
|
65
220
|
handler: async (params, ctx) => {
|
|
221
|
+
const { contextLength = null } = params;
|
|
66
222
|
const row = await ctx.db.upsert_model.get({
|
|
67
223
|
alias: params.alias,
|
|
68
224
|
actual: params.actual,
|
|
69
|
-
context_length:
|
|
225
|
+
context_length: contextLength,
|
|
70
226
|
});
|
|
71
227
|
return { id: row.id, alias: params.alias };
|
|
72
228
|
},
|
|
@@ -87,316 +243,121 @@ export default class Rpc {
|
|
|
87
243
|
params: { alias: "string — model alias to remove" },
|
|
88
244
|
});
|
|
89
245
|
|
|
90
|
-
// ---
|
|
91
|
-
|
|
92
|
-
// Override: get has persist flag for file constraint management
|
|
93
|
-
r.register("get", {
|
|
94
|
-
handler: async (params, ctx) => {
|
|
95
|
-
if (!params.path) throw new Error("path is required");
|
|
96
|
-
|
|
97
|
-
if (params.persist) {
|
|
98
|
-
const visibility = params.readonly ? "readonly" : "active";
|
|
99
|
-
await File.setConstraint(
|
|
100
|
-
ctx.db,
|
|
101
|
-
ctx.projectId,
|
|
102
|
-
params.path,
|
|
103
|
-
visibility,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!params.run) throw new Error("run is required");
|
|
108
|
-
const { rummy } = await buildRunContext(hooks, ctx, params.run);
|
|
109
|
-
await dispatchTool(hooks, rummy, "get", params.path, "", {
|
|
110
|
-
path: params.path,
|
|
111
|
-
});
|
|
112
|
-
return { status: "ok" };
|
|
113
|
-
},
|
|
114
|
-
description: "Promote entry fidelity.",
|
|
115
|
-
params: {
|
|
116
|
-
path: "string — file path or glob pattern",
|
|
117
|
-
run: "string — run alias",
|
|
118
|
-
persist: "boolean? — also create file constraint",
|
|
119
|
-
readonly: "boolean? — with persist, set readonly instead of active",
|
|
120
|
-
},
|
|
121
|
-
requiresInit: true,
|
|
122
|
-
});
|
|
246
|
+
// --- File constraints (project-scoped overlay) ---
|
|
123
247
|
|
|
124
|
-
|
|
125
|
-
r.register("store", {
|
|
248
|
+
r.register("file/constraint", {
|
|
126
249
|
handler: async (params, ctx) => {
|
|
127
|
-
if (!params.
|
|
128
|
-
|
|
129
|
-
if (params.clear) {
|
|
130
|
-
await File.dropConstraint(ctx.db, ctx.projectId, params.path);
|
|
131
|
-
return { status: "ok" };
|
|
250
|
+
if (!params.pattern) {
|
|
251
|
+
throw new Error("file/constraint: pattern is required");
|
|
132
252
|
}
|
|
133
|
-
if (params.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
ctx.db,
|
|
137
|
-
ctx.projectId,
|
|
138
|
-
params.path,
|
|
139
|
-
visibility,
|
|
253
|
+
if (!CONSTRAINT_VISIBILITIES.has(params.visibility)) {
|
|
254
|
+
throw new Error(
|
|
255
|
+
`file/constraint: visibility must be one of ${[...CONSTRAINT_VISIBILITIES].join(", ")}`,
|
|
140
256
|
);
|
|
141
257
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const runRow = await ctx.db.get_run_by_alias.get({ alias: params.run });
|
|
145
|
-
if (!runRow) throw new Error(`Run not found: ${params.run}`);
|
|
146
|
-
const store = ctx.projectAgent.entries;
|
|
147
|
-
await store.demoteByPattern(runRow.id, params.path, null);
|
|
148
|
-
return { status: "ok" };
|
|
149
|
-
},
|
|
150
|
-
description: "Demote entry to stored state.",
|
|
151
|
-
params: {
|
|
152
|
-
path: "string — file path or glob pattern",
|
|
153
|
-
run: "string? — run alias (required without persist)",
|
|
154
|
-
persist: "boolean? — also create file constraint",
|
|
155
|
-
ignore: "boolean? — with persist, exclude from scan",
|
|
156
|
-
clear: "boolean? — remove existing constraint",
|
|
157
|
-
},
|
|
158
|
-
requiresInit: true,
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
r.register("getEntries", {
|
|
162
|
-
handler: async (params, ctx) => {
|
|
163
|
-
let run;
|
|
164
|
-
if (params.run) {
|
|
165
|
-
run = await ctx.db.get_run_by_alias.get({ alias: params.run });
|
|
166
|
-
} else {
|
|
167
|
-
run = await ctx.db.get_latest_run.get({ project_id: ctx.projectId });
|
|
168
|
-
}
|
|
169
|
-
if (!run) return [];
|
|
170
|
-
const entries = await ctx.projectAgent.entries.getEntriesByPattern(
|
|
171
|
-
run.id,
|
|
172
|
-
params.pattern ?? "*",
|
|
173
|
-
params.body ?? null,
|
|
174
|
-
{ limit: params.limit ?? null, offset: params.offset ?? null },
|
|
175
|
-
);
|
|
176
|
-
return entries.map((e) => ({
|
|
177
|
-
path: e.path,
|
|
178
|
-
scheme: e.scheme,
|
|
179
|
-
status: e.status,
|
|
180
|
-
fidelity: e.fidelity,
|
|
181
|
-
tokens: e.tokens,
|
|
182
|
-
}));
|
|
183
|
-
},
|
|
184
|
-
description: "Query entries by pattern.",
|
|
185
|
-
params: {
|
|
186
|
-
pattern: "string? — glob pattern (default: *)",
|
|
187
|
-
body: "string? — filter by body content",
|
|
188
|
-
run: "string? — run alias (default: latest run)",
|
|
189
|
-
limit: "number? — max results",
|
|
190
|
-
offset: "number? — skip first N results",
|
|
191
|
-
},
|
|
192
|
-
requiresInit: true,
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// --- Runs ---
|
|
196
|
-
|
|
197
|
-
r.register("startRun", {
|
|
198
|
-
handler: async (params, ctx) => {
|
|
199
|
-
if (!params.model) throw new Error("model is required");
|
|
200
|
-
const alias = `${params.model}_${Date.now()}`;
|
|
201
|
-
const runRow = await ctx.db.create_run.get({
|
|
202
|
-
project_id: ctx.projectId,
|
|
203
|
-
parent_run_id: null,
|
|
204
|
-
model: params.model ?? null,
|
|
205
|
-
alias,
|
|
206
|
-
temperature: params.temperature ?? null,
|
|
207
|
-
persona: params.persona ?? null,
|
|
208
|
-
context_limit: params.contextLimit ?? null,
|
|
209
|
-
});
|
|
210
|
-
return { run: alias, id: runRow.id };
|
|
211
|
-
},
|
|
212
|
-
description: "Pre-create a run. Returns { run, id }.",
|
|
213
|
-
params: {
|
|
214
|
-
model: "string — model alias (required)",
|
|
215
|
-
temperature: "number? — 0 to 2",
|
|
216
|
-
persona: "string?",
|
|
217
|
-
contextLimit: "number?",
|
|
218
|
-
},
|
|
219
|
-
requiresInit: true,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
r.register("ask", {
|
|
223
|
-
handler: async (params, ctx) => {
|
|
224
|
-
if (!params.model) throw new Error("model is required");
|
|
225
|
-
return ctx.projectAgent.ask(
|
|
258
|
+
const normalized = await File.setConstraint(
|
|
259
|
+
ctx.db,
|
|
226
260
|
ctx.projectId,
|
|
227
|
-
params.
|
|
228
|
-
params.
|
|
229
|
-
params.run,
|
|
230
|
-
{
|
|
231
|
-
temperature: params.temperature ?? null,
|
|
232
|
-
persona: params.persona ?? null,
|
|
233
|
-
contextLimit: params.contextLimit,
|
|
234
|
-
noRepo: params.noRepo,
|
|
235
|
-
noInteraction: params.noInteraction,
|
|
236
|
-
noProposals: params.noProposals,
|
|
237
|
-
noWeb: params.noWeb,
|
|
238
|
-
fork: params.fork,
|
|
239
|
-
},
|
|
261
|
+
params.pattern,
|
|
262
|
+
params.visibility,
|
|
240
263
|
);
|
|
264
|
+
return { ok: true, pattern: normalized };
|
|
241
265
|
},
|
|
242
|
-
description:
|
|
243
|
-
|
|
266
|
+
description:
|
|
267
|
+
"Set a project-level file constraint. Visibility ∈ " +
|
|
268
|
+
"{active, readonly, ignore}. Patterns can be globs. " +
|
|
269
|
+
"Persists across runs; overlays git defaults.",
|
|
244
270
|
params: {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
run: "string? — continue existing run",
|
|
248
|
-
temperature: "number?",
|
|
249
|
-
persona: "string?",
|
|
250
|
-
contextLimit: "number?",
|
|
251
|
-
noRepo: "boolean?",
|
|
252
|
-
noInteraction: "boolean? — disable ask_user tool",
|
|
253
|
-
noWeb: "boolean? — disable search and URL fetch",
|
|
254
|
-
fork: "boolean?",
|
|
271
|
+
pattern: "string — file path or glob",
|
|
272
|
+
visibility: "string — active | readonly | ignore",
|
|
255
273
|
},
|
|
256
274
|
requiresInit: true,
|
|
257
275
|
});
|
|
258
276
|
|
|
259
|
-
r.register("
|
|
277
|
+
r.register("file/drop", {
|
|
260
278
|
handler: async (params, ctx) => {
|
|
261
|
-
if (!params.
|
|
262
|
-
|
|
279
|
+
if (!params.pattern) {
|
|
280
|
+
throw new Error("file/drop: pattern is required");
|
|
281
|
+
}
|
|
282
|
+
const normalized = await File.dropConstraint(
|
|
283
|
+
ctx.db,
|
|
263
284
|
ctx.projectId,
|
|
264
|
-
params.
|
|
265
|
-
params.prompt,
|
|
266
|
-
params.run,
|
|
267
|
-
{
|
|
268
|
-
temperature: params.temperature ?? null,
|
|
269
|
-
persona: params.persona ?? null,
|
|
270
|
-
contextLimit: params.contextLimit,
|
|
271
|
-
noRepo: params.noRepo,
|
|
272
|
-
noInteraction: params.noInteraction,
|
|
273
|
-
noProposals: params.noProposals,
|
|
274
|
-
noWeb: params.noWeb,
|
|
275
|
-
fork: params.fork,
|
|
276
|
-
},
|
|
285
|
+
params.pattern,
|
|
277
286
|
);
|
|
287
|
+
return { ok: true, pattern: normalized };
|
|
278
288
|
},
|
|
279
|
-
description: "
|
|
280
|
-
|
|
281
|
-
params: {
|
|
282
|
-
prompt: "string — user message",
|
|
283
|
-
model: "string — model alias (required)",
|
|
284
|
-
run: "string? — continue existing run",
|
|
285
|
-
temperature: "number?",
|
|
286
|
-
persona: "string?",
|
|
287
|
-
contextLimit: "number?",
|
|
288
|
-
noRepo: "boolean?",
|
|
289
|
-
noInteraction: "boolean? — disable ask_user tool",
|
|
290
|
-
noWeb: "boolean? — disable search and URL fetch",
|
|
291
|
-
fork: "boolean?",
|
|
292
|
-
},
|
|
289
|
+
description: "Remove a project-level file constraint.",
|
|
290
|
+
params: { pattern: "string — file path or glob to drop" },
|
|
293
291
|
requiresInit: true,
|
|
294
292
|
});
|
|
295
293
|
|
|
296
|
-
r.register("
|
|
297
|
-
handler: async (
|
|
298
|
-
ctx.
|
|
299
|
-
|
|
300
|
-
longRunning: true,
|
|
301
|
-
params: {
|
|
302
|
-
run: "string — run alias",
|
|
303
|
-
resolution: "{ path, action: 'accept'|'reject', output? }",
|
|
304
|
-
},
|
|
305
|
-
requiresInit: true,
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
r.register("run/abort", {
|
|
309
|
-
handler: async (params, ctx) => {
|
|
310
|
-
const runRow = await ctx.db.get_run_by_alias.get({ alias: params.run });
|
|
311
|
-
if (!runRow)
|
|
312
|
-
throw new Error(msg("error.run_not_found", { runId: params.run }));
|
|
313
|
-
ctx.projectAgent.abortRun(runRow.id);
|
|
314
|
-
await ctx.db.update_run_status.run({
|
|
315
|
-
id: runRow.id,
|
|
316
|
-
status: 499,
|
|
294
|
+
r.register("getConstraints", {
|
|
295
|
+
handler: async (_params, ctx) => {
|
|
296
|
+
const rows = await ctx.db.get_file_constraints.all({
|
|
297
|
+
project_id: ctx.projectId,
|
|
317
298
|
});
|
|
318
|
-
return
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
requiresInit: true,
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
r.register("run/rename", {
|
|
326
|
-
handler: async (params, ctx) => {
|
|
327
|
-
const { run, name } = params;
|
|
328
|
-
if (!name || !/^[a-zA-Z0-9_]+$/.test(name)) {
|
|
329
|
-
throw new Error(msg("error.run_name_invalid"));
|
|
330
|
-
}
|
|
331
|
-
const runRow = await ctx.db.get_run_by_alias.get({ alias: run });
|
|
332
|
-
if (!runRow)
|
|
333
|
-
throw new Error(msg("error.run_not_found", { runId: run }));
|
|
334
|
-
try {
|
|
335
|
-
await ctx.db.rename_run.run({
|
|
336
|
-
id: runRow.id,
|
|
337
|
-
old_alias: runRow.alias,
|
|
338
|
-
new_alias: name,
|
|
339
|
-
});
|
|
340
|
-
} catch (err) {
|
|
341
|
-
if (err.message.includes("UNIQUE"))
|
|
342
|
-
throw new Error(msg("error.run_name_taken", { name }));
|
|
343
|
-
throw err;
|
|
344
|
-
}
|
|
345
|
-
return { run: name };
|
|
346
|
-
},
|
|
347
|
-
description: "Rename a run.",
|
|
348
|
-
params: {
|
|
349
|
-
run: "string — current run alias",
|
|
350
|
-
name: "string — new name",
|
|
299
|
+
return rows.map((r) => ({
|
|
300
|
+
pattern: r.pattern,
|
|
301
|
+
visibility: r.visibility,
|
|
302
|
+
}));
|
|
351
303
|
},
|
|
304
|
+
description:
|
|
305
|
+
"List project-level file constraints as [{pattern, visibility}].",
|
|
352
306
|
requiresInit: true,
|
|
353
307
|
});
|
|
354
308
|
|
|
355
|
-
|
|
356
|
-
handler: async (params, ctx) =>
|
|
357
|
-
ctx.projectAgent.inject(params.run, params.message),
|
|
358
|
-
description: "Inject a message into a run.",
|
|
359
|
-
longRunning: true,
|
|
360
|
-
params: {
|
|
361
|
-
run: "string — run alias",
|
|
362
|
-
message: "string — message to inject",
|
|
363
|
-
},
|
|
364
|
-
requiresInit: true,
|
|
365
|
-
});
|
|
309
|
+
// --- Queries ---
|
|
366
310
|
|
|
367
|
-
r.register("
|
|
311
|
+
r.register("getEntries", {
|
|
368
312
|
handler: async (params, ctx) => {
|
|
369
|
-
const runRow = await
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
313
|
+
const runRow = await this.#resolveRun(params.run, ctx);
|
|
314
|
+
const { pattern = "*", bodyFilter = null } = params;
|
|
315
|
+
const rows = await ctx.projectAgent.entries.getEntriesByPattern(
|
|
316
|
+
runRow.id,
|
|
317
|
+
pattern,
|
|
318
|
+
bodyFilter,
|
|
319
|
+
);
|
|
320
|
+
return rows
|
|
321
|
+
.filter((e) => !params.scheme || e.scheme === params.scheme)
|
|
322
|
+
.filter((e) => !params.state || e.state === params.state)
|
|
323
|
+
.filter(
|
|
324
|
+
(e) => !params.visibility || e.visibility === params.visibility,
|
|
325
|
+
)
|
|
326
|
+
.map((e) => ({
|
|
327
|
+
path: e.path,
|
|
328
|
+
scheme: e.scheme,
|
|
329
|
+
state: e.state,
|
|
330
|
+
outcome: e.outcome,
|
|
331
|
+
visibility: e.visibility,
|
|
332
|
+
turn: e.turn,
|
|
333
|
+
tokens: e.tokens,
|
|
334
|
+
attributes:
|
|
335
|
+
typeof e.attributes === "string"
|
|
336
|
+
? JSON.parse(e.attributes)
|
|
337
|
+
: e.attributes,
|
|
338
|
+
}));
|
|
380
339
|
},
|
|
381
|
-
description:
|
|
340
|
+
description:
|
|
341
|
+
"List entries matching a pattern. Read-only — no promotion. " +
|
|
342
|
+
"Optional filters: scheme, state, visibility, bodyFilter.",
|
|
382
343
|
params: {
|
|
383
344
|
run: "string — run alias",
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
345
|
+
pattern: "string? — glob pattern (default '*')",
|
|
346
|
+
scheme: "string? — filter by scheme (e.g. 'file')",
|
|
347
|
+
state: "string? — filter by state",
|
|
348
|
+
visibility: "string? — filter by visibility",
|
|
349
|
+
bodyFilter: "string? — narrow pattern matches by body content",
|
|
388
350
|
},
|
|
389
351
|
requiresInit: true,
|
|
390
352
|
});
|
|
391
353
|
|
|
392
|
-
// --- Queries ---
|
|
393
|
-
|
|
394
354
|
r.register("getRuns", {
|
|
395
355
|
handler: async (params, ctx) => {
|
|
356
|
+
const { limit = null, offset = null } = params;
|
|
396
357
|
const rows = await ctx.db.get_runs_by_project.all({
|
|
397
358
|
project_id: ctx.projectId,
|
|
398
|
-
limit
|
|
399
|
-
offset
|
|
359
|
+
limit,
|
|
360
|
+
offset,
|
|
400
361
|
});
|
|
401
362
|
return rows.map((row) => ({
|
|
402
363
|
run: row.alias,
|
|
@@ -425,7 +386,7 @@ export default class Rpc {
|
|
|
425
386
|
ctx.db.get_run_usage.get({ run_id: run.id }),
|
|
426
387
|
ctx.db.get_reasoning.all({ run_id: run.id }),
|
|
427
388
|
ctx.db.get_content.all({ run_id: run.id }),
|
|
428
|
-
ctx.db.
|
|
389
|
+
ctx.db.get_results.all({ run_id: run.id }),
|
|
429
390
|
ctx.db.get_latest_user_prompt.get({ run_id: run.id }),
|
|
430
391
|
ctx.db.get_latest_summary.get({ run_id: run.id }),
|
|
431
392
|
]);
|
|
@@ -455,17 +416,14 @@ export default class Rpc {
|
|
|
455
416
|
body: c.body,
|
|
456
417
|
turn: c.turn,
|
|
457
418
|
})),
|
|
458
|
-
history: history.map((h) => {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
turn: h.turn,
|
|
467
|
-
};
|
|
468
|
-
}),
|
|
419
|
+
history: history.map((h) => ({
|
|
420
|
+
tool: h.tool,
|
|
421
|
+
path: h.path,
|
|
422
|
+
status: h.status,
|
|
423
|
+
body: h.body,
|
|
424
|
+
attributes: h.attributes ? JSON.parse(h.attributes) : null,
|
|
425
|
+
turn: h.turn,
|
|
426
|
+
})),
|
|
469
427
|
},
|
|
470
428
|
last_user_prompt: promptRow?.body,
|
|
471
429
|
last_summary: summaryRow?.body,
|
|
@@ -480,6 +438,11 @@ export default class Rpc {
|
|
|
480
438
|
|
|
481
439
|
r.registerNotification("run/state", "Turn state update.");
|
|
482
440
|
r.registerNotification("run/progress", "Turn status.");
|
|
441
|
+
r.registerNotification("run/proposal", "Proposal awaiting resolution.");
|
|
442
|
+
r.registerNotification(
|
|
443
|
+
"stream/cancelled",
|
|
444
|
+
"Server-initiated stream cancellation.",
|
|
445
|
+
);
|
|
483
446
|
r.registerNotification("ui/render", "Streaming output.");
|
|
484
447
|
r.registerNotification("ui/notify", "Toast notification.");
|
|
485
448
|
|
|
@@ -487,6 +450,257 @@ export default class Rpc {
|
|
|
487
450
|
// Checked at request time — no timing dependency on plugin load order.
|
|
488
451
|
r.setToolFallback(hooks, buildRunContext, dispatchTool);
|
|
489
452
|
}
|
|
453
|
+
|
|
454
|
+
// --- Primitive dispatch helpers ---
|
|
455
|
+
|
|
456
|
+
async #resolveRun(runAlias, ctx) {
|
|
457
|
+
if (!runAlias) throw new Error("run is required");
|
|
458
|
+
const runRow = await ctx.db.get_run_by_alias.get({ alias: runAlias });
|
|
459
|
+
if (!runRow)
|
|
460
|
+
throw new Error(msg("error.run_not_found", { runId: runAlias }));
|
|
461
|
+
return runRow;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async #dispatchSet(params, ctx) {
|
|
465
|
+
if (!params.path) throw new Error("set: path is required");
|
|
466
|
+
|
|
467
|
+
// run:// is the lifecycle surface. A set to a brand-new run://
|
|
468
|
+
// alias starts a run loop; a state transition cancels or resolves.
|
|
469
|
+
if (params.path.startsWith("run://")) {
|
|
470
|
+
return await this.#dispatchRunSet(params, ctx);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const runRow = await this.#resolveRun(params.run, ctx);
|
|
474
|
+
|
|
475
|
+
// State transition on an existing proposed entry → route through
|
|
476
|
+
// AgentLoop.resolve, which applies scheme-specific side effects
|
|
477
|
+
// (patch application for set://, file removal for rm://, stream
|
|
478
|
+
// setup for sh:// / env://, etc.).
|
|
479
|
+
if (params.state && !params.append && !params.pattern) {
|
|
480
|
+
const current = await ctx.projectAgent.entries.getState(
|
|
481
|
+
runRow.id,
|
|
482
|
+
params.path,
|
|
483
|
+
);
|
|
484
|
+
if (current?.state === "proposed") {
|
|
485
|
+
const action =
|
|
486
|
+
params.state === "resolved"
|
|
487
|
+
? "accept"
|
|
488
|
+
: params.state === "failed"
|
|
489
|
+
? "error"
|
|
490
|
+
: params.state === "cancelled"
|
|
491
|
+
? "reject"
|
|
492
|
+
: null;
|
|
493
|
+
if (action) {
|
|
494
|
+
const { body = null } = params;
|
|
495
|
+
return await ctx.projectAgent.resolve(params.run, {
|
|
496
|
+
path: params.path,
|
|
497
|
+
action,
|
|
498
|
+
output: body,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
await ctx.projectAgent.entries.set({
|
|
505
|
+
runId: runRow.id,
|
|
506
|
+
projectId: ctx.projectId,
|
|
507
|
+
path: params.path,
|
|
508
|
+
body: params.body,
|
|
509
|
+
state: params.state,
|
|
510
|
+
visibility: params.visibility,
|
|
511
|
+
outcome: params.outcome,
|
|
512
|
+
attributes: params.attributes,
|
|
513
|
+
append: params.append,
|
|
514
|
+
pattern: params.pattern,
|
|
515
|
+
bodyFilter: params.bodyFilter,
|
|
516
|
+
writer: "client",
|
|
517
|
+
});
|
|
518
|
+
return { ok: true };
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async #dispatchRunSet(params, ctx) {
|
|
522
|
+
let alias = params.path.slice("run://".length);
|
|
523
|
+
|
|
524
|
+
// Empty alias on a new-run set → synthesize ${model}_${epoch}.
|
|
525
|
+
// Matches AgentLoop.#generateAlias so server- and client-initiated
|
|
526
|
+
// runs share one naming scheme. Clients that want a specific name
|
|
527
|
+
// pass it in the path; anonymous starts get the synthesized one.
|
|
528
|
+
if (!alias) {
|
|
529
|
+
const { attributes: attrs = {} } = params;
|
|
530
|
+
if (!attrs.model) {
|
|
531
|
+
throw new Error(
|
|
532
|
+
"set run://: attributes.model is required when alias is omitted",
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
alias = `${attrs.model}_${Date.now()}`;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const existing = await ctx.db.get_run_by_alias.get({ alias });
|
|
539
|
+
|
|
540
|
+
const runPath = `run://${alias}`;
|
|
541
|
+
|
|
542
|
+
// State transition on an existing run.
|
|
543
|
+
if (existing && params.state) {
|
|
544
|
+
if (params.state === "cancelled") {
|
|
545
|
+
ctx.projectAgent.abortRun(existing.id);
|
|
546
|
+
}
|
|
547
|
+
await ctx.projectAgent.entries.set({
|
|
548
|
+
runId: existing.id,
|
|
549
|
+
path: runPath,
|
|
550
|
+
state: params.state,
|
|
551
|
+
outcome: params.outcome,
|
|
552
|
+
writer: "client",
|
|
553
|
+
});
|
|
554
|
+
return { ok: true, alias };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// New run — kick off the loop. AgentLoop handles row + entry creation.
|
|
558
|
+
if (!existing) {
|
|
559
|
+
const { attributes: attrs = {} } = params;
|
|
560
|
+
if (!attrs.model) {
|
|
561
|
+
throw new Error(
|
|
562
|
+
"set run://: attributes.model is required for a new run",
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
const { mode } = attrs;
|
|
566
|
+
if (mode !== "ask" && mode !== "act") {
|
|
567
|
+
throw new Error(
|
|
568
|
+
`set run://: attributes.mode is required and must be "ask" or "act" (got ${JSON.stringify(mode)})`,
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
const options = {
|
|
572
|
+
temperature: attrs.temperature,
|
|
573
|
+
persona: attrs.persona,
|
|
574
|
+
contextLimit: attrs.contextLimit,
|
|
575
|
+
noRepo: attrs.noRepo,
|
|
576
|
+
noInteraction: attrs.noInteraction,
|
|
577
|
+
noWeb: attrs.noWeb,
|
|
578
|
+
noProposals: attrs.noProposals,
|
|
579
|
+
fork: attrs.fork,
|
|
580
|
+
};
|
|
581
|
+
const { body = "" } = params;
|
|
582
|
+
// Fire-and-forget: client watches state via entry notifications.
|
|
583
|
+
// ProjectAgent exposes .ask/.act wrappers over AgentLoop#run; route
|
|
584
|
+
// by mode rather than calling the private loop directly.
|
|
585
|
+
const kickoff =
|
|
586
|
+
mode === "act"
|
|
587
|
+
? ctx.projectAgent.act(
|
|
588
|
+
ctx.projectId,
|
|
589
|
+
attrs.model,
|
|
590
|
+
body,
|
|
591
|
+
alias,
|
|
592
|
+
options,
|
|
593
|
+
)
|
|
594
|
+
: ctx.projectAgent.ask(
|
|
595
|
+
ctx.projectId,
|
|
596
|
+
attrs.model,
|
|
597
|
+
body,
|
|
598
|
+
alias,
|
|
599
|
+
options,
|
|
600
|
+
);
|
|
601
|
+
kickoff.catch((err) => {
|
|
602
|
+
console.error(`[RUMMY] run ${alias} crashed: ${err.message}`);
|
|
603
|
+
});
|
|
604
|
+
return { ok: true, alias };
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Existing run + fork=true: create a child run synchronously so we
|
|
608
|
+
// can return the child alias, then kick off the loop against it.
|
|
609
|
+
// fork needs a brand-new run row with parent_run_id set; inject()
|
|
610
|
+
// would just add another prompt to the parent.
|
|
611
|
+
const attrs = params.attributes ? params.attributes : {};
|
|
612
|
+
if (attrs.fork === true) {
|
|
613
|
+
const { mode } = attrs;
|
|
614
|
+
if (mode !== "ask" && mode !== "act") {
|
|
615
|
+
throw new Error(
|
|
616
|
+
`set run://: attributes.mode is required on fork and must be "ask" or "act" (got ${JSON.stringify(mode)})`,
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
const model = attrs.model ? attrs.model : existing.model;
|
|
620
|
+
const prompt = params.body ? params.body : "";
|
|
621
|
+
const childInfo = await ctx.projectAgent.ensureRun(
|
|
622
|
+
ctx.projectId,
|
|
623
|
+
model,
|
|
624
|
+
alias,
|
|
625
|
+
prompt,
|
|
626
|
+
{
|
|
627
|
+
fork: true,
|
|
628
|
+
temperature: attrs.temperature,
|
|
629
|
+
persona: attrs.persona,
|
|
630
|
+
contextLimit: attrs.contextLimit,
|
|
631
|
+
},
|
|
632
|
+
);
|
|
633
|
+
const options = {
|
|
634
|
+
temperature: attrs.temperature,
|
|
635
|
+
persona: attrs.persona,
|
|
636
|
+
contextLimit: attrs.contextLimit,
|
|
637
|
+
noRepo: attrs.noRepo,
|
|
638
|
+
noInteraction: attrs.noInteraction,
|
|
639
|
+
noWeb: attrs.noWeb,
|
|
640
|
+
noProposals: attrs.noProposals,
|
|
641
|
+
// fork already applied — pass false to reuse the child row.
|
|
642
|
+
fork: false,
|
|
643
|
+
};
|
|
644
|
+
const kickoff =
|
|
645
|
+
mode === "act"
|
|
646
|
+
? ctx.projectAgent.act(
|
|
647
|
+
ctx.projectId,
|
|
648
|
+
model,
|
|
649
|
+
prompt,
|
|
650
|
+
childInfo.alias,
|
|
651
|
+
options,
|
|
652
|
+
)
|
|
653
|
+
: ctx.projectAgent.ask(
|
|
654
|
+
ctx.projectId,
|
|
655
|
+
model,
|
|
656
|
+
prompt,
|
|
657
|
+
childInfo.alias,
|
|
658
|
+
options,
|
|
659
|
+
);
|
|
660
|
+
kickoff.catch((err) => {
|
|
661
|
+
console.error(
|
|
662
|
+
`[RUMMY] fork ${childInfo.alias} crashed: ${err.message}`,
|
|
663
|
+
);
|
|
664
|
+
});
|
|
665
|
+
return { ok: true, alias: childInfo.alias };
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Existing run with body-only update (continuation prompt). Inject.
|
|
669
|
+
if (params.body) {
|
|
670
|
+
const { mode } = attrs;
|
|
671
|
+
if (mode !== "ask" && mode !== "act") {
|
|
672
|
+
throw new Error(
|
|
673
|
+
`set run://: attributes.mode is required on inject and must be "ask" or "act" (got ${JSON.stringify(mode)})`,
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
await ctx.projectAgent.inject(alias, params.body, mode);
|
|
677
|
+
return { ok: true, alias };
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return { ok: true, alias };
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
async #dispatchGet(params, ctx) {
|
|
684
|
+
const runRow = await this.#resolveRun(params.run, ctx);
|
|
685
|
+
await ctx.projectAgent.entries.get({
|
|
686
|
+
runId: runRow.id,
|
|
687
|
+
turn: await ctx.projectAgent.entries.nextTurn(runRow.id),
|
|
688
|
+
path: params.path,
|
|
689
|
+
bodyFilter: params.bodyFilter,
|
|
690
|
+
visibility: params.visibility,
|
|
691
|
+
});
|
|
692
|
+
return { ok: true };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async #dispatchRm(params, ctx) {
|
|
696
|
+
const runRow = await this.#resolveRun(params.run, ctx);
|
|
697
|
+
await ctx.projectAgent.entries.rm({
|
|
698
|
+
runId: runRow.id,
|
|
699
|
+
path: params.path,
|
|
700
|
+
bodyFilter: params.bodyFilter,
|
|
701
|
+
});
|
|
702
|
+
return { ok: true };
|
|
703
|
+
}
|
|
490
704
|
}
|
|
491
705
|
|
|
492
706
|
async function buildRunContext(hooks, ctx, runAlias) {
|
|
@@ -517,14 +731,19 @@ async function buildRunContext(hooks, ctx, runAlias) {
|
|
|
517
731
|
|
|
518
732
|
async function dispatchTool(hooks, rummy, scheme, path, body, attributes) {
|
|
519
733
|
const store = rummy.entries;
|
|
520
|
-
const resultPath = await store.
|
|
734
|
+
const resultPath = await store.logPath(
|
|
521
735
|
rummy.runId,
|
|
736
|
+
rummy.sequence,
|
|
522
737
|
scheme,
|
|
523
738
|
path,
|
|
524
|
-
rummy.sequence,
|
|
525
739
|
);
|
|
526
740
|
|
|
527
|
-
await store.
|
|
741
|
+
await store.set({
|
|
742
|
+
runId: rummy.runId,
|
|
743
|
+
turn: rummy.sequence,
|
|
744
|
+
path: resultPath,
|
|
745
|
+
body,
|
|
746
|
+
state: "resolved",
|
|
528
747
|
attributes: attributes,
|
|
529
748
|
loopId: rummy.loopId,
|
|
530
749
|
});
|
|
@@ -534,7 +753,7 @@ async function dispatchTool(hooks, rummy, scheme, path, body, attributes) {
|
|
|
534
753
|
path: resultPath,
|
|
535
754
|
body: body,
|
|
536
755
|
attributes: attributes,
|
|
537
|
-
|
|
756
|
+
state: "resolved",
|
|
538
757
|
resultPath,
|
|
539
758
|
};
|
|
540
759
|
|