pi-forge 1.2.3 → 1.2.5
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/README.md +1 -1
- package/dist/client/assets/{CodeMirrorEditor-1gu-DS9k.js → CodeMirrorEditor-DXmxwE2Z.js} +2 -2
- package/dist/client/assets/{CodeMirrorEditor-1gu-DS9k.js.map → CodeMirrorEditor-DXmxwE2Z.js.map} +1 -1
- package/dist/client/assets/index-CMSjnWtF.js +365 -0
- package/dist/client/assets/index-CMSjnWtF.js.map +1 -0
- package/dist/client/assets/index-Cp8qEy7Q.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +1 -1
- package/dist/client/sw.js.map +1 -1
- package/dist/server/agent-extensions/compaction-continuation.js +65 -0
- package/dist/server/agent-extensions/compaction-continuation.js.map +1 -0
- package/dist/server/agent-resource-loader.js +10 -0
- package/dist/server/agent-resource-loader.js.map +1 -1
- package/dist/server/ask-user-question/envelope.js +56 -0
- package/dist/server/ask-user-question/envelope.js.map +1 -0
- package/dist/server/ask-user-question/prompt-strings.js +44 -0
- package/dist/server/ask-user-question/prompt-strings.js.map +1 -0
- package/dist/server/ask-user-question/registry.js +157 -0
- package/dist/server/ask-user-question/registry.js.map +1 -0
- package/dist/server/ask-user-question/tool.js +115 -0
- package/dist/server/ask-user-question/tool.js.map +1 -0
- package/dist/server/ask-user-question/types.js +27 -0
- package/dist/server/ask-user-question/types.js.map +1 -0
- package/dist/server/ask-user-question/validate.js +135 -0
- package/dist/server/ask-user-question/validate.js.map +1 -0
- package/dist/server/config.js +10 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/index.js +16 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/tool-bridge.js +14 -8
- package/dist/server/mcp/tool-bridge.js.map +1 -1
- package/dist/server/processes/envelope.js +60 -0
- package/dist/server/processes/envelope.js.map +1 -0
- package/dist/server/processes/log-store.js +132 -0
- package/dist/server/processes/log-store.js.map +1 -0
- package/dist/server/processes/manager.js +348 -0
- package/dist/server/processes/manager.js.map +1 -0
- package/dist/server/processes/prompt-strings.js +43 -0
- package/dist/server/processes/prompt-strings.js.map +1 -0
- package/dist/server/processes/tool.js +273 -0
- package/dist/server/processes/tool.js.map +1 -0
- package/dist/server/processes/types.js +21 -0
- package/dist/server/processes/types.js.map +1 -0
- package/dist/server/processes/watches.js +59 -0
- package/dist/server/processes/watches.js.map +1 -0
- package/dist/server/quick-actions.js +141 -0
- package/dist/server/quick-actions.js.map +1 -0
- package/dist/server/routes/ask-user-question.js +129 -0
- package/dist/server/routes/ask-user-question.js.map +1 -0
- package/dist/server/routes/config.js +12 -0
- package/dist/server/routes/config.js.map +1 -1
- package/dist/server/routes/processes.js +228 -0
- package/dist/server/routes/processes.js.map +1 -0
- package/dist/server/routes/quick-actions.js +384 -0
- package/dist/server/routes/quick-actions.js.map +1 -0
- package/dist/server/routes/todos.js +67 -0
- package/dist/server/routes/todos.js.map +1 -0
- package/dist/server/session-registry.js +72 -4
- package/dist/server/session-registry.js.map +1 -1
- package/dist/server/sse-bridge.js +225 -4
- package/dist/server/sse-bridge.js.map +1 -1
- package/dist/server/todo/envelope.js +87 -0
- package/dist/server/todo/envelope.js.map +1 -0
- package/dist/server/todo/invariants.js +21 -0
- package/dist/server/todo/invariants.js.map +1 -0
- package/dist/server/todo/prompt-strings.js +29 -0
- package/dist/server/todo/prompt-strings.js.map +1 -0
- package/dist/server/todo/reducer.js +189 -0
- package/dist/server/todo/reducer.js.map +1 -0
- package/dist/server/todo/replay.js +45 -0
- package/dist/server/todo/replay.js.map +1 -0
- package/dist/server/todo/store.js +92 -0
- package/dist/server/todo/store.js.map +1 -0
- package/dist/server/todo/task-graph.js +60 -0
- package/dist/server/todo/task-graph.js.map +1 -0
- package/dist/server/todo/tool.js +95 -0
- package/dist/server/todo/tool.js.map +1 -0
- package/dist/server/todo/types.js +23 -0
- package/dist/server/todo/types.js.map +1 -0
- package/package.json +1 -1
- package/dist/client/assets/index-BxZV6ddv.js +0 -359
- package/dist/client/assets/index-BxZV6ddv.js.map +0 -1
- package/dist/client/assets/index-KUhxvBxw.css +0 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { config } from "../config.js";
|
|
3
|
+
import { buildListMessage, buildStartMessage, err, ok } from "./envelope.js";
|
|
4
|
+
import { processManager } from "./manager.js";
|
|
5
|
+
import { PROMPT_GUIDELINES, PROMPT_SNIPPET, TOOL_DESCRIPTION } from "./prompt-strings.js";
|
|
6
|
+
import { TOOL_LABEL, TOOL_NAME, } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* JSON Schema for `process` tool params. Mirrors `@aliou/pi-processes`'s
|
|
9
|
+
* TypeBox schema field-for-field so an agent prompt authored
|
|
10
|
+
* against the plugin sees the same input surface.
|
|
11
|
+
*/
|
|
12
|
+
const inputSchema = {
|
|
13
|
+
type: "object",
|
|
14
|
+
required: ["action"],
|
|
15
|
+
properties: {
|
|
16
|
+
action: {
|
|
17
|
+
type: "string",
|
|
18
|
+
enum: ["start", "list", "output", "logs", "kill", "clear", "write"],
|
|
19
|
+
},
|
|
20
|
+
command: { type: "string", description: "Command to run (required for start)" },
|
|
21
|
+
name: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Friendly name for the process (required for start, e.g. 'backend-dev', 'test-runner')",
|
|
24
|
+
},
|
|
25
|
+
id: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Process ID, returned by start and list actions (required for output/kill/logs/write)",
|
|
28
|
+
},
|
|
29
|
+
input: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Data to write to process stdin (required for write action)",
|
|
32
|
+
},
|
|
33
|
+
end: {
|
|
34
|
+
type: "boolean",
|
|
35
|
+
description: "Close stdin after writing (optional for write action, use for programs reading until EOF)",
|
|
36
|
+
},
|
|
37
|
+
alertOnSuccess: {
|
|
38
|
+
type: "boolean",
|
|
39
|
+
description: "Get a turn to react when process completes successfully (default: false). Use for builds/tests where you need confirmation.",
|
|
40
|
+
},
|
|
41
|
+
alertOnFailure: {
|
|
42
|
+
type: "boolean",
|
|
43
|
+
description: "Get a turn to react when process fails/crashes (default: true). Use to be alerted of unexpected failures.",
|
|
44
|
+
},
|
|
45
|
+
alertOnKill: {
|
|
46
|
+
type: "boolean",
|
|
47
|
+
description: "Get a turn to react when process is killed by external signal (default: false). Note: killing via tool never triggers a turn.",
|
|
48
|
+
},
|
|
49
|
+
logWatches: {
|
|
50
|
+
type: "array",
|
|
51
|
+
items: {
|
|
52
|
+
type: "object",
|
|
53
|
+
required: ["pattern"],
|
|
54
|
+
additionalProperties: false,
|
|
55
|
+
properties: {
|
|
56
|
+
pattern: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "Regular expression pattern to match against process output lines",
|
|
59
|
+
},
|
|
60
|
+
stream: {
|
|
61
|
+
type: "string",
|
|
62
|
+
enum: ["stdout", "stderr", "both"],
|
|
63
|
+
description: "Which stream to watch (default: both). Use stdout/stderr to reduce noise.",
|
|
64
|
+
},
|
|
65
|
+
repeat: {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
description: "Trigger every time this pattern matches (default: false, one-time)",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
function validateLogWatches(watches) {
|
|
75
|
+
if (watches === undefined)
|
|
76
|
+
return null;
|
|
77
|
+
if (!Array.isArray(watches))
|
|
78
|
+
return "Invalid parameter: logWatches must be an array";
|
|
79
|
+
for (const [i, w] of watches.entries()) {
|
|
80
|
+
if (typeof w.pattern !== "string" || w.pattern.trim().length === 0) {
|
|
81
|
+
return `Invalid logWatches[${i}].pattern: expected non-empty string`;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
new RegExp(w.pattern);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
return `Invalid logWatches[${i}].pattern: ${e instanceof Error ? e.message : "invalid regex"}`;
|
|
88
|
+
}
|
|
89
|
+
if (w.stream !== undefined && !["stdout", "stderr", "both"].includes(w.stream)) {
|
|
90
|
+
return `Invalid logWatches[${i}].stream: expected stdout, stderr, or both`;
|
|
91
|
+
}
|
|
92
|
+
if (w.repeat !== undefined && typeof w.repeat !== "boolean") {
|
|
93
|
+
return `Invalid logWatches[${i}].repeat: expected boolean`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Build the `process` tool for one session. Bound to the
|
|
100
|
+
* sessionId + workspace path so `start` knows the right cwd and
|
|
101
|
+
* the manager keys state correctly.
|
|
102
|
+
*
|
|
103
|
+
* Contract-compatible with `@aliou/pi-processes`. Implementation
|
|
104
|
+
* is independent; see `manager.ts` for the spawn/lifecycle code
|
|
105
|
+
* and `docs/processes.md` for the cross-reference.
|
|
106
|
+
*/
|
|
107
|
+
export function createProcessTool(sessionId, workspacePath) {
|
|
108
|
+
return {
|
|
109
|
+
name: TOOL_NAME,
|
|
110
|
+
label: TOOL_LABEL,
|
|
111
|
+
description: TOOL_DESCRIPTION,
|
|
112
|
+
promptSnippet: PROMPT_SNIPPET,
|
|
113
|
+
promptGuidelines: PROMPT_GUIDELINES,
|
|
114
|
+
parameters: Type.Unsafe(inputSchema),
|
|
115
|
+
async execute(_toolCallId, params) {
|
|
116
|
+
const p = params;
|
|
117
|
+
switch (p.action) {
|
|
118
|
+
case "start":
|
|
119
|
+
return executeStart(sessionId, workspacePath, p);
|
|
120
|
+
case "list":
|
|
121
|
+
return executeList(sessionId);
|
|
122
|
+
case "output":
|
|
123
|
+
return executeOutput(sessionId, p);
|
|
124
|
+
case "logs":
|
|
125
|
+
return executeLogs(sessionId, p);
|
|
126
|
+
case "kill":
|
|
127
|
+
return executeKill(sessionId, p);
|
|
128
|
+
case "clear":
|
|
129
|
+
return executeClear(sessionId);
|
|
130
|
+
case "write":
|
|
131
|
+
return executeWrite(sessionId, p);
|
|
132
|
+
default:
|
|
133
|
+
return err(p.action, `Unknown action: ${String(p.action)}`);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function executeStart(sessionId, workspacePath, p) {
|
|
139
|
+
// Defense in depth — the client also hides the tool under
|
|
140
|
+
// MINIMAL_UI, but a stale tab or scripted caller could still
|
|
141
|
+
// invoke it. Refuse at the tool boundary.
|
|
142
|
+
if (config.minimalUi) {
|
|
143
|
+
return err("start", "process.start is disabled under MINIMAL_UI");
|
|
144
|
+
}
|
|
145
|
+
if (p.name === undefined || p.name.length === 0) {
|
|
146
|
+
return err("start", "Missing required parameter: name");
|
|
147
|
+
}
|
|
148
|
+
if (p.command === undefined || p.command.length === 0) {
|
|
149
|
+
return err("start", "Missing required parameter: command");
|
|
150
|
+
}
|
|
151
|
+
const watchErr = validateLogWatches(p.logWatches);
|
|
152
|
+
if (watchErr !== null)
|
|
153
|
+
return err("start", watchErr);
|
|
154
|
+
let info;
|
|
155
|
+
try {
|
|
156
|
+
const startOpts = {};
|
|
157
|
+
if (p.alertOnSuccess !== undefined)
|
|
158
|
+
startOpts.alertOnSuccess = p.alertOnSuccess;
|
|
159
|
+
if (p.alertOnFailure !== undefined)
|
|
160
|
+
startOpts.alertOnFailure = p.alertOnFailure;
|
|
161
|
+
if (p.alertOnKill !== undefined)
|
|
162
|
+
startOpts.alertOnKill = p.alertOnKill;
|
|
163
|
+
if (p.logWatches !== undefined)
|
|
164
|
+
startOpts.logWatches = p.logWatches;
|
|
165
|
+
info = processManager.start(sessionId, p.name, p.command, workspacePath, startOpts);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
return err("start", `Failed to start: ${e instanceof Error ? e.message : String(e)}`);
|
|
169
|
+
}
|
|
170
|
+
return ok({
|
|
171
|
+
action: "start",
|
|
172
|
+
success: true,
|
|
173
|
+
message: buildStartMessage(info),
|
|
174
|
+
process: info,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function executeList(sessionId) {
|
|
178
|
+
const processes = processManager.list(sessionId);
|
|
179
|
+
return ok({
|
|
180
|
+
action: "list",
|
|
181
|
+
success: true,
|
|
182
|
+
message: buildListMessage(processes),
|
|
183
|
+
processes,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function executeOutput(sessionId, p) {
|
|
187
|
+
if (p.id === undefined || p.id.length === 0) {
|
|
188
|
+
return err("output", "Missing required parameter: id");
|
|
189
|
+
}
|
|
190
|
+
const out = processManager.output(sessionId, p.id);
|
|
191
|
+
if (out === undefined)
|
|
192
|
+
return err("output", `Process not found: ${p.id}`);
|
|
193
|
+
const stdoutTail = out.stdout.slice(Math.max(0, out.stdout.length - 50)).join("\n");
|
|
194
|
+
const stderrTail = out.stderr.slice(Math.max(0, out.stderr.length - 50)).join("\n");
|
|
195
|
+
const parts = [`Status: ${out.status}`];
|
|
196
|
+
if (stdoutTail.length > 0)
|
|
197
|
+
parts.push("--- stdout ---", stdoutTail);
|
|
198
|
+
if (stderrTail.length > 0)
|
|
199
|
+
parts.push("--- stderr ---", stderrTail);
|
|
200
|
+
return ok({
|
|
201
|
+
action: "output",
|
|
202
|
+
success: true,
|
|
203
|
+
message: parts.join("\n"),
|
|
204
|
+
output: out,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function executeLogs(sessionId, p) {
|
|
208
|
+
if (p.id === undefined || p.id.length === 0) {
|
|
209
|
+
return err("logs", "Missing required parameter: id");
|
|
210
|
+
}
|
|
211
|
+
const files = processManager.logFiles(sessionId, p.id);
|
|
212
|
+
if (files === undefined)
|
|
213
|
+
return err("logs", `Process not found: ${p.id}`);
|
|
214
|
+
return ok({
|
|
215
|
+
action: "logs",
|
|
216
|
+
success: true,
|
|
217
|
+
message: `Log files for ${p.id}:\n stdout: ${files.stdoutFile}\n stderr: ${files.stderrFile}\n\nUse the read tool to inspect them.`,
|
|
218
|
+
logFiles: files,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async function executeKill(sessionId, p) {
|
|
222
|
+
if (p.id === undefined || p.id.length === 0) {
|
|
223
|
+
return err("kill", "Missing required parameter: id");
|
|
224
|
+
}
|
|
225
|
+
const result = await processManager.kill(sessionId, p.id);
|
|
226
|
+
if (!result.ok) {
|
|
227
|
+
if (result.reason === "not_found") {
|
|
228
|
+
return err("kill", `Process not found: ${p.id}`);
|
|
229
|
+
}
|
|
230
|
+
return err("kill", `Failed to kill ${p.id}: ${result.reason}`);
|
|
231
|
+
}
|
|
232
|
+
return ok({
|
|
233
|
+
action: "kill",
|
|
234
|
+
success: true,
|
|
235
|
+
message: `Killed "${result.info.name}" (${result.info.id})`,
|
|
236
|
+
process: result.info,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function executeClear(sessionId) {
|
|
240
|
+
const cleared = processManager.clear(sessionId);
|
|
241
|
+
return ok({
|
|
242
|
+
action: "clear",
|
|
243
|
+
success: true,
|
|
244
|
+
message: `Cleared ${cleared} finished process(es)`,
|
|
245
|
+
cleared,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
async function executeWrite(sessionId, p) {
|
|
249
|
+
if (p.id === undefined || p.id.length === 0) {
|
|
250
|
+
return err("write", "Missing required parameter: id");
|
|
251
|
+
}
|
|
252
|
+
if (p.input === undefined) {
|
|
253
|
+
return err("write", "Missing required parameter: input");
|
|
254
|
+
}
|
|
255
|
+
const result = await processManager.write(sessionId, p.id, p.input, p.end === true);
|
|
256
|
+
if (!result.ok) {
|
|
257
|
+
const reason = result.reason;
|
|
258
|
+
const message = reason === "not_found"
|
|
259
|
+
? `Process not found: ${p.id}`
|
|
260
|
+
: reason === "process_exited"
|
|
261
|
+
? `Process ${p.id} has already exited`
|
|
262
|
+
: reason === "stdin_closed"
|
|
263
|
+
? `stdin for ${p.id} is already closed`
|
|
264
|
+
: `Failed to write to ${p.id}`;
|
|
265
|
+
return err("write", message);
|
|
266
|
+
}
|
|
267
|
+
return ok({
|
|
268
|
+
action: "write",
|
|
269
|
+
success: true,
|
|
270
|
+
message: p.end === true ? `Wrote to ${p.id} and closed stdin` : `Wrote to ${p.id}`,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/processes/tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shape definitions for the `process` tool. The wire contract —
|
|
3
|
+
* tool name (`process`), action enum (`start | list | output |
|
|
4
|
+
* logs | kill | clear | write`), per-process `ProcessInfo`
|
|
5
|
+
* fields, status state machine, log-watch shape, and
|
|
6
|
+
* `ProcessesDetails` envelope — is contract-compatible with
|
|
7
|
+
* `@aliou/pi-processes`. An agent prompt authored against the
|
|
8
|
+
* plugin works against this implementation unchanged.
|
|
9
|
+
*
|
|
10
|
+
* Implementation is independent; types and validation rules were
|
|
11
|
+
* derived from the plugin's published schema and tests. See
|
|
12
|
+
* `docs/processes.md` for the cross-reference.
|
|
13
|
+
*/
|
|
14
|
+
export const TOOL_NAME = "process";
|
|
15
|
+
export const TOOL_LABEL = "Process";
|
|
16
|
+
export const LIVE_STATUSES = new Set([
|
|
17
|
+
"running",
|
|
18
|
+
"terminating",
|
|
19
|
+
"terminate_timeout",
|
|
20
|
+
]);
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/processes/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,SAAS,CAAC;AACnC,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AAYpC,MAAM,CAAC,MAAM,aAAa,GAA+B,IAAI,GAAG,CAAC;IAC/D,SAAS;IACT,aAAa;IACb,mBAAmB;CACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile the agent-supplied watches. Validation lives at the
|
|
3
|
+
* tool boundary (see tool.ts); this function trusts shape +
|
|
4
|
+
* regex validity and just normalises defaults.
|
|
5
|
+
*/
|
|
6
|
+
export function compileWatches(input) {
|
|
7
|
+
if (input === undefined)
|
|
8
|
+
return [];
|
|
9
|
+
return input.map((w, i) => ({
|
|
10
|
+
index: i,
|
|
11
|
+
pattern: w.pattern,
|
|
12
|
+
stream: w.stream ?? "both",
|
|
13
|
+
repeat: w.repeat === true,
|
|
14
|
+
regex: new RegExp(w.pattern),
|
|
15
|
+
fired: false,
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run a freshly-emitted line against the compiled set. Returns
|
|
20
|
+
* the watches that matched AND are eligible to fire (skipping
|
|
21
|
+
* single-fire watches that already fired). Mutates `fired` for
|
|
22
|
+
* matched single-fire watches so the same line stream of repeats
|
|
23
|
+
* doesn't keep alerting.
|
|
24
|
+
*/
|
|
25
|
+
export function evaluateWatches(watches, source, line) {
|
|
26
|
+
const hits = [];
|
|
27
|
+
for (const w of watches) {
|
|
28
|
+
if (w.stream !== "both" && w.stream !== source)
|
|
29
|
+
continue;
|
|
30
|
+
if (!w.repeat && w.fired)
|
|
31
|
+
continue;
|
|
32
|
+
if (!w.regex.test(line))
|
|
33
|
+
continue;
|
|
34
|
+
if (!w.repeat)
|
|
35
|
+
w.fired = true;
|
|
36
|
+
hits.push(w);
|
|
37
|
+
}
|
|
38
|
+
return hits;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build the wire shape for a single match. Used by the manager
|
|
42
|
+
* when it fans out a `process_watch_matched` event.
|
|
43
|
+
*/
|
|
44
|
+
export function buildMatchEvent(watch, processId, processName, processCommand, source, line) {
|
|
45
|
+
return {
|
|
46
|
+
processId,
|
|
47
|
+
processName,
|
|
48
|
+
processCommand,
|
|
49
|
+
source,
|
|
50
|
+
line,
|
|
51
|
+
watch: {
|
|
52
|
+
index: watch.index,
|
|
53
|
+
pattern: watch.pattern,
|
|
54
|
+
stream: watch.stream,
|
|
55
|
+
repeat: watch.repeat,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=watches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watches.js","sourceRoot":"","sources":["../../src/processes/watches.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAsC;IACnE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,MAAM;QAC1B,MAAM,EAAE,CAAC,CAAC,MAAM,KAAK,IAAI;QACzB,KAAK,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5B,KAAK,EAAE,KAAK;KACb,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAwB,EACxB,MAA2B,EAC3B,IAAY;IAEZ,MAAM,IAAI,GAAoB,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;YAAE,SAAS;QACzD,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK;YAAE,SAAS;QACnC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,IAAI,CAAC,CAAC,CAAC,MAAM;YAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,SAAiB,EACjB,WAAmB,EACnB,cAAsB,EACtB,MAA2B,EAC3B,IAAY;IAEZ,OAAO;QACL,SAAS;QACT,WAAW;QACX,cAAc;QACd,MAAM;QACN,IAAI;QACJ,KAAK,EAAE;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { config } from "./config.js";
|
|
5
|
+
export class QuickActionNotFoundError extends Error {
|
|
6
|
+
constructor(id) {
|
|
7
|
+
super(`quick action not found: ${id}`);
|
|
8
|
+
this.name = "QuickActionNotFoundError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Hard cap on a single command string. Mirrors the system-prompt
|
|
12
|
+
* addendum cap — large enough for a multi-line shell snippet, small
|
|
13
|
+
* enough to keep the wire surface bounded. */
|
|
14
|
+
export const MAX_COMMAND_BYTES = 20_000;
|
|
15
|
+
/** Hard cap on a single prompt template. Pi compaction lives further
|
|
16
|
+
* down the stack, but bounding here keeps an accidental megabyte paste
|
|
17
|
+
* from silently landing in the composer. */
|
|
18
|
+
export const MAX_PROMPT_BYTES = 50_000;
|
|
19
|
+
/** Default command timeout (30 s) and absolute ceiling (5 min). A
|
|
20
|
+
* five-minute build is the upper end of "still feels like a chip";
|
|
21
|
+
* past that, the user should be using the terminal. */
|
|
22
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
23
|
+
export const MAX_TIMEOUT_MS = 300_000;
|
|
24
|
+
export function isCommandAction(a) {
|
|
25
|
+
return typeof a.command === "string" && a.command.length > 0;
|
|
26
|
+
}
|
|
27
|
+
export function isPromptAction(a) {
|
|
28
|
+
return typeof a.text === "string" && a.text.length > 0;
|
|
29
|
+
}
|
|
30
|
+
async function ensureDir() {
|
|
31
|
+
await mkdir(dirname(config.quickActionsFile), { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
async function atomicWrite(actions) {
|
|
34
|
+
await ensureDir();
|
|
35
|
+
const target = config.quickActionsFile;
|
|
36
|
+
const tmp = `${target}.${randomUUID()}.tmp`;
|
|
37
|
+
await writeFile(tmp, JSON.stringify(actions, null, 2), { mode: 0o600 });
|
|
38
|
+
try {
|
|
39
|
+
await rename(tmp, target);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
await unlink(tmp).catch(() => undefined);
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function isAction(v) {
|
|
47
|
+
if (typeof v !== "object" || v === null)
|
|
48
|
+
return false;
|
|
49
|
+
const r = v;
|
|
50
|
+
if (typeof r.id !== "string" || typeof r.name !== "string")
|
|
51
|
+
return false;
|
|
52
|
+
const hasCmd = typeof r.command === "string" && r.command.length > 0;
|
|
53
|
+
const hasText = typeof r.text === "string" && r.text.length > 0;
|
|
54
|
+
// Drop entries that look corrupted (neither kind, or both) rather
|
|
55
|
+
// than surfacing them to the route layer, which would have to
|
|
56
|
+
// re-validate. Strict-on-read keeps the in-memory shape clean.
|
|
57
|
+
return hasCmd !== hasText;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Serialise all read-modify-write sequences over quick-actions.json
|
|
61
|
+
* — same pattern as projects.json. Without it, concurrent
|
|
62
|
+
* POST /quick-actions calls can race the rename().
|
|
63
|
+
*/
|
|
64
|
+
let lock = Promise.resolve();
|
|
65
|
+
function withLock(fn) {
|
|
66
|
+
const next = lock.then(fn, fn);
|
|
67
|
+
lock = next.catch(() => undefined);
|
|
68
|
+
return next;
|
|
69
|
+
}
|
|
70
|
+
export async function readQuickActions() {
|
|
71
|
+
try {
|
|
72
|
+
const raw = await readFile(config.quickActionsFile, "utf8");
|
|
73
|
+
if (raw.trim().length === 0)
|
|
74
|
+
return [];
|
|
75
|
+
const parsed = JSON.parse(raw);
|
|
76
|
+
if (!Array.isArray(parsed))
|
|
77
|
+
return [];
|
|
78
|
+
return parsed.filter(isAction);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err.code === "ENOENT")
|
|
82
|
+
return [];
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function getQuickAction(id) {
|
|
87
|
+
const list = await readQuickActions();
|
|
88
|
+
return list.find((a) => a.id === id);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Create a new action. The caller (route layer) is responsible for
|
|
92
|
+
* shape validation (one-of command/text, byte caps, etc.) — this
|
|
93
|
+
* function only assigns the id and persists.
|
|
94
|
+
*/
|
|
95
|
+
export async function createQuickAction(input) {
|
|
96
|
+
return withLock(async () => {
|
|
97
|
+
const list = await readQuickActions();
|
|
98
|
+
const action = { ...input, id: randomUUID() };
|
|
99
|
+
list.push(action);
|
|
100
|
+
await atomicWrite(list);
|
|
101
|
+
return action;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
export async function updateQuickAction(id, patch) {
|
|
105
|
+
return withLock(async () => {
|
|
106
|
+
const list = await readQuickActions();
|
|
107
|
+
const idx = list.findIndex((a) => a.id === id);
|
|
108
|
+
if (idx === -1)
|
|
109
|
+
throw new QuickActionNotFoundError(id);
|
|
110
|
+
const existing = list[idx];
|
|
111
|
+
if (existing === undefined)
|
|
112
|
+
throw new QuickActionNotFoundError(id);
|
|
113
|
+
// Build the merged record explicitly so a switch from command to
|
|
114
|
+
// prompt (or vice-versa) drops the now-unused fields rather than
|
|
115
|
+
// carrying them as dead weight. The caller passes the FULL desired
|
|
116
|
+
// shape on every update; the patch arg is union-typed for ergonomic
|
|
117
|
+
// partial calls but the route layer always sends the complete form.
|
|
118
|
+
const merged = { ...existing, ...patch, id };
|
|
119
|
+
if (typeof patch.command === "string" && patch.command.length > 0) {
|
|
120
|
+
delete merged.text;
|
|
121
|
+
delete merged.mode;
|
|
122
|
+
}
|
|
123
|
+
else if (typeof patch.text === "string" && patch.text.length > 0) {
|
|
124
|
+
delete merged.command;
|
|
125
|
+
delete merged.timeoutMs;
|
|
126
|
+
}
|
|
127
|
+
list[idx] = merged;
|
|
128
|
+
await atomicWrite(list);
|
|
129
|
+
return merged;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export async function deleteQuickAction(id) {
|
|
133
|
+
await withLock(async () => {
|
|
134
|
+
const list = await readQuickActions();
|
|
135
|
+
const next = list.filter((a) => a.id !== id);
|
|
136
|
+
if (next.length === list.length)
|
|
137
|
+
throw new QuickActionNotFoundError(id);
|
|
138
|
+
await atomicWrite(next);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=quick-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quick-actions.js","sourceRoot":"","sources":["../src/quick-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { getSession } from "../session-registry.js";
|
|
2
|
+
import { answerPending, getPendingForSession } from "../ask-user-question/registry.js";
|
|
3
|
+
import { buildResult } from "../ask-user-question/envelope.js";
|
|
4
|
+
import { errorSchema } from "./_schemas.js";
|
|
5
|
+
/**
|
|
6
|
+
* POST /sessions/:id/ask-user-question/answer
|
|
7
|
+
*
|
|
8
|
+
* The browser modal calls this with the user's answers (or with
|
|
9
|
+
* `cancelled: true` when they pick "Chat about this" / close the
|
|
10
|
+
* modal). Resolves the pending entry in the registry, which
|
|
11
|
+
* propagates back to the tool's awaiting `execute()` — the agent
|
|
12
|
+
* gets a clean tool result and continues.
|
|
13
|
+
*
|
|
14
|
+
* Per-call ownership is enforced by matching `requestId` against
|
|
15
|
+
* this session's pending list. A spoofed requestId from a session
|
|
16
|
+
* the caller doesn't own returns 404.
|
|
17
|
+
*/
|
|
18
|
+
const answerBodySchema = {
|
|
19
|
+
type: "object",
|
|
20
|
+
required: ["requestId"],
|
|
21
|
+
additionalProperties: false,
|
|
22
|
+
properties: {
|
|
23
|
+
requestId: { type: "string", minLength: 1 },
|
|
24
|
+
cancelled: { type: "boolean" },
|
|
25
|
+
answers: {
|
|
26
|
+
type: "array",
|
|
27
|
+
items: {
|
|
28
|
+
type: "object",
|
|
29
|
+
required: ["questionIndex", "question", "kind"],
|
|
30
|
+
properties: {
|
|
31
|
+
questionIndex: { type: "integer", minimum: 0 },
|
|
32
|
+
question: { type: "string" },
|
|
33
|
+
kind: { type: "string", enum: ["option", "custom", "chat", "multi"] },
|
|
34
|
+
answer: { type: ["string", "null"] },
|
|
35
|
+
selected: { type: "array", items: { type: "string" } },
|
|
36
|
+
notes: { type: "string" },
|
|
37
|
+
preview: { type: "string" },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export const askUserQuestionRoutes = async (fastify) => {
|
|
44
|
+
fastify.get("/sessions/:id/ask-user-question/pending", {
|
|
45
|
+
schema: {
|
|
46
|
+
description: "List ask_user_question requests currently waiting on an answer " +
|
|
47
|
+
"for this session. The browser modal uses this on initial mount " +
|
|
48
|
+
"as a fallback to the SSE snapshot re-delivery path.",
|
|
49
|
+
tags: ["sessions"],
|
|
50
|
+
params: {
|
|
51
|
+
type: "object",
|
|
52
|
+
required: ["id"],
|
|
53
|
+
properties: { id: { type: "string" } },
|
|
54
|
+
},
|
|
55
|
+
response: {
|
|
56
|
+
200: {
|
|
57
|
+
type: "object",
|
|
58
|
+
required: ["pending"],
|
|
59
|
+
properties: {
|
|
60
|
+
pending: {
|
|
61
|
+
type: "array",
|
|
62
|
+
items: {
|
|
63
|
+
type: "object",
|
|
64
|
+
required: ["requestId", "questions"],
|
|
65
|
+
properties: {
|
|
66
|
+
requestId: { type: "string" },
|
|
67
|
+
questions: { type: "array" },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
404: errorSchema,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}, async (req, reply) => {
|
|
77
|
+
const live = getSession(req.params.id);
|
|
78
|
+
if (live === undefined) {
|
|
79
|
+
return reply.code(404).send({ error: "session_not_found" });
|
|
80
|
+
}
|
|
81
|
+
const pending = getPendingForSession(req.params.id).map((p) => ({
|
|
82
|
+
requestId: p.requestId,
|
|
83
|
+
questions: p.questions,
|
|
84
|
+
}));
|
|
85
|
+
return { pending };
|
|
86
|
+
});
|
|
87
|
+
fastify.post("/sessions/:id/ask-user-question/answer", {
|
|
88
|
+
schema: {
|
|
89
|
+
description: "Submit a user's answers to an in-flight ask_user_question " +
|
|
90
|
+
"tool call. Pass `cancelled: true` (with or without partial " +
|
|
91
|
+
"answers) when the user dismissed the modal or picked the " +
|
|
92
|
+
"'Chat about this' escape. The tool's execute() resolves " +
|
|
93
|
+
"with the constructed envelope; the agent then continues.",
|
|
94
|
+
tags: ["sessions"],
|
|
95
|
+
params: {
|
|
96
|
+
type: "object",
|
|
97
|
+
required: ["id"],
|
|
98
|
+
properties: { id: { type: "string" } },
|
|
99
|
+
},
|
|
100
|
+
body: answerBodySchema,
|
|
101
|
+
response: {
|
|
102
|
+
204: { type: "null" },
|
|
103
|
+
404: errorSchema,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}, async (req, reply) => {
|
|
107
|
+
const live = getSession(req.params.id);
|
|
108
|
+
if (live === undefined) {
|
|
109
|
+
return reply.code(404).send({ error: "session_not_found" });
|
|
110
|
+
}
|
|
111
|
+
const cancelled = req.body.cancelled === true;
|
|
112
|
+
const answers = Array.isArray(req.body.answers) ? req.body.answers : [];
|
|
113
|
+
// Total question count is whatever the registry has on file —
|
|
114
|
+
// the envelope's "partial cancel" summary line reads it from
|
|
115
|
+
// here to phrase correctly. Look it up before answering since
|
|
116
|
+
// the registry entry vanishes on resolve.
|
|
117
|
+
const pending = getPendingForSession(req.params.id).find((p) => p.requestId === req.body.requestId);
|
|
118
|
+
const questionCount = pending?.questions.length ?? answers.length;
|
|
119
|
+
const envelope = buildResult(answers, { cancelled, questionCount });
|
|
120
|
+
const ok = answerPending(req.body.requestId, req.params.id, envelope);
|
|
121
|
+
if (!ok) {
|
|
122
|
+
// Either unknown requestId or one that belongs to another
|
|
123
|
+
// session (defense against cross-session spoofing).
|
|
124
|
+
return reply.code(404).send({ error: "request_not_found" });
|
|
125
|
+
}
|
|
126
|
+
return reply.code(204).send();
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=ask-user-question.js.map
|