@marcoscale98/pi-extensible-workflows 5.17.0-fork.1 → 6.2.1-fork.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +145 -0
- package/README.md +2 -2
- package/dist/src/agent-execution.d.ts +43 -10
- package/dist/src/agent-execution.js +442 -162
- package/dist/src/background-widget.d.ts +5 -1
- package/dist/src/background-widget.js +5 -10
- package/dist/src/decoders.d.ts +2 -3
- package/dist/src/decoders.js +59 -37
- package/dist/src/execution.d.ts +6 -2
- package/dist/src/execution.js +48 -7
- package/dist/src/host-delivery.js +5 -8
- package/dist/src/host-navigator.js +5 -2
- package/dist/src/host-phases.d.ts +1 -0
- package/dist/src/host-recovery.d.ts +11 -8
- package/dist/src/host-recovery.js +14 -22
- package/dist/src/host-runtime.js +56 -5
- package/dist/src/host-view.d.ts +6 -1
- package/dist/src/host-view.js +46 -20
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +246 -118
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +1985 -1690
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +8 -5
- package/dist/src/paths.js +24 -13
- package/dist/src/persistence.d.ts +1 -0
- package/dist/src/persistence.js +1 -0
- package/dist/src/pi-runtime-runner.js +4 -1
- package/dist/src/registry.d.ts +9 -7
- package/dist/src/registry.js +29 -71
- package/dist/src/runtime/agent-runner.d.ts +2 -0
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/session-lease.d.ts +1 -1
- package/dist/src/session-lease.js +45 -11
- package/dist/src/settings.d.ts +19 -3
- package/dist/src/settings.js +63 -50
- package/dist/src/store.d.ts +3 -1
- package/dist/src/store.js +17 -1
- package/dist/src/trajectory.d.ts +12 -3
- package/dist/src/trajectory.js +32 -11
- package/dist/src/types.d.ts +129 -45
- package/dist/src/types.js +5 -8
- package/dist/src/utils.d.ts +21 -1
- package/dist/src/utils.js +46 -6
- package/dist/src/validation.d.ts +14 -3
- package/dist/src/validation.js +143 -87
- package/dist/starter/index.js +8 -11
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +8 -9
- package/dist/subagents/index.js +904 -956
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +48 -31
- package/dist/subagents/src/contracts.js +20 -9
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +14 -8
- package/dist/subagents/src/manager.js +52 -73
- package/dist/subagents/src/navigator.js +3 -14
- package/dist/subagents/src/view.d.ts +1 -1
- package/dist/subagents/src/view.js +15 -68
- package/dist/subagents/src/worktree.js +1 -1
- package/dist/trajectory/assets/index.html +35 -13
- package/dist/trajectory/index.js +576 -932
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +35 -13
- package/dist/trajectory/src/index.js +134 -104
- package/dist/trajectory/src/server.js +268 -27
- package/dist/trajectory/src/server.js.map +3 -3
- package/dist/trajectory/src/startup-mutex.d.ts +17 -0
- package/dist/trajectory/src/startup-mutex.js +172 -0
- package/examples/workflow-extension-template/README.md +4 -7
- package/examples/workflow-extension-template/extension.test.mjs +4 -8
- package/examples/workflow-extension-template/index.js +0 -3
- package/package.json +10 -10
- package/skills/pi-extensible-workflows/SKILL.md +12 -8
- package/src/agent-execution.ts +412 -164
- package/src/background-widget.ts +5 -11
- package/src/decoders.ts +51 -37
- package/src/execution.ts +46 -9
- package/src/host-delivery.ts +5 -8
- package/src/host-navigator.ts +4 -2
- package/src/host-phases.ts +1 -1
- package/src/host-recovery.ts +23 -28
- package/src/host-runtime.ts +44 -6
- package/src/host-view.ts +41 -16
- package/src/host.ts +229 -142
- package/src/index.ts +1 -2
- package/src/paths.ts +18 -9
- package/src/persistence.ts +1 -0
- package/src/pi-runtime-runner.ts +3 -1
- package/src/registry.ts +28 -64
- package/src/runtime/agent-runner.ts +2 -0
- package/src/script-tools.ts +76 -0
- package/src/session-lease.ts +34 -6
- package/src/settings.ts +56 -50
- package/src/store.ts +18 -2
- package/src/trajectory.ts +32 -15
- package/src/types.ts +82 -34
- package/src/utils.ts +42 -5
- package/src/validation.ts +121 -80
- package/starter/index.ts +0 -1
- package/starter/review-loop.ts +8 -9
- package/starter/roles/developer.md +0 -1
- package/starter/roles/oracle.md +0 -1
- package/starter/roles/researcher.md +0 -1
- package/starter/roles/reviewer.md +0 -1
- package/starter/roles/scout.md +0 -1
- package/subagents/README.md +10 -16
- package/subagents/src/contracts.ts +21 -11
- package/subagents/src/index.ts +17 -8
- package/subagents/src/manager.ts +48 -128
- package/subagents/src/navigator.ts +4 -13
- package/subagents/src/view.ts +16 -63
- package/subagents/src/worktree.ts +1 -1
- package/trajectory/src/assets/index.html +35 -13
- package/trajectory/src/index.ts +112 -91
- package/trajectory/src/server.ts +46 -10
- package/trajectory/src/startup-mutex.ts +127 -0
- package/dist/src/roles.d.ts +0 -70
- package/dist/src/roles.js +0 -325
- package/dist/starter/roles/developer.md +0 -6
- package/dist/starter/roles/oracle.md +0 -18
- package/dist/starter/roles/researcher.md +0 -20
- package/dist/starter/roles/reviewer.md +0 -19
- package/dist/starter/roles/scout.md +0 -18
- package/examples/workflow-extension-template/roles/reviewer.md +0 -4
- package/src/roles.ts +0 -326
package/dist/subagents/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// subagents/src/index.ts
|
|
2
|
-
import { join as
|
|
3
|
-
import { defineTool as defineTool2, getAgentDir as
|
|
4
|
-
import { Value as
|
|
2
|
+
import { join as join10 } from "node:path";
|
|
3
|
+
import { defineTool as defineTool2, getAgentDir as getAgentDir7 } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { Value as Value4 } from "typebox/value";
|
|
5
5
|
|
|
6
6
|
// src/types.ts
|
|
7
7
|
var RUN_STATES = ["queued", "running", "pausing", "paused", "awaiting_input", "completed", "failed", "stopped", "interrupted", "budget_exhausted"];
|
|
@@ -16,7 +16,7 @@ var ERROR_CODES = [
|
|
|
16
16
|
"INVALID_SCHEMA",
|
|
17
17
|
"UNKNOWN_MODEL",
|
|
18
18
|
"UNKNOWN_TOOL",
|
|
19
|
-
"
|
|
19
|
+
"UNKNOWN_AGENT",
|
|
20
20
|
"RUN_OWNED",
|
|
21
21
|
"RUN_NOT_FOUND",
|
|
22
22
|
"REGISTRY_FROZEN",
|
|
@@ -28,6 +28,7 @@ var ERROR_CODES = [
|
|
|
28
28
|
"AGENT_FAILED",
|
|
29
29
|
"AGENT_RESULT_COLLECTED",
|
|
30
30
|
"RESULT_INVALID",
|
|
31
|
+
"TOOL_FAILED",
|
|
31
32
|
"CANCELLED",
|
|
32
33
|
"WORKER_UNRESPONSIVE",
|
|
33
34
|
"WORKTREE_FAILED",
|
|
@@ -36,15 +37,12 @@ var ERROR_CODES = [
|
|
|
36
37
|
"INTERNAL_ERROR"
|
|
37
38
|
];
|
|
38
39
|
var THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
39
|
-
function roleNameOf(value) {
|
|
40
|
-
return typeof value === "string" ? value : void 0;
|
|
41
|
-
}
|
|
42
|
-
var BUDGET_DIMENSIONS = ["tokens", "costUsd", "durationMs", "agentLaunches"];
|
|
43
|
-
var BUDGET_EVENT_TYPES = ["soft_crossed", "hard_overrun", "hard_exhausted", "adjustment_requested", "adjustment_approved", "adjustment_rejected"];
|
|
44
40
|
var CONTEXT_FILE_SCOPES = ["global", "project", "cwd"];
|
|
45
41
|
function isContextFileScope(value) {
|
|
46
42
|
return CONTEXT_FILE_SCOPES.some((scope) => scope === value);
|
|
47
43
|
}
|
|
44
|
+
var BUDGET_DIMENSIONS = ["tokens", "costUsd", "durationMs", "agentLaunches"];
|
|
45
|
+
var BUDGET_EVENT_TYPES = ["soft_crossed", "hard_overrun", "hard_exhausted", "adjustment_requested", "adjustment_approved", "adjustment_rejected"];
|
|
48
46
|
var WORKFLOW_AGENT_STALL_THRESHOLD_MS = 10 * 60 * 1e3;
|
|
49
47
|
function zeroAccounting() {
|
|
50
48
|
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
|
|
@@ -57,7 +55,7 @@ function sumAccounting(values) {
|
|
|
57
55
|
for (const value of values) total = addAccounting(total, value);
|
|
58
56
|
return total;
|
|
59
57
|
}
|
|
60
|
-
var LAUNCH_SNAPSHOT_IDENTITY_VERSION =
|
|
58
|
+
var LAUNCH_SNAPSHOT_IDENTITY_VERSION = 6;
|
|
61
59
|
var WORKFLOW_ERROR_BRAND = /* @__PURE__ */ Symbol.for("pi-extensible-workflows.workflow-error");
|
|
62
60
|
var WorkflowError = class extends Error {
|
|
63
61
|
constructor(code, message) {
|
|
@@ -155,15 +153,8 @@ function jsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
|
155
153
|
seen.delete(value);
|
|
156
154
|
return valid;
|
|
157
155
|
}
|
|
158
|
-
function
|
|
159
|
-
|
|
160
|
-
let present = false;
|
|
161
|
-
for (const layer of layers) {
|
|
162
|
-
if (layer === void 0) continue;
|
|
163
|
-
present = true;
|
|
164
|
-
for (const [namespace, value] of Object.entries(layer)) Object.defineProperty(merged, namespace, { value: structuredClone(value), enumerable: true, configurable: true, writable: true });
|
|
165
|
-
}
|
|
166
|
-
return present ? deepFreeze(merged) : void 0;
|
|
156
|
+
function jsonObject(value) {
|
|
157
|
+
return jsonValue(value) && object(value);
|
|
167
158
|
}
|
|
168
159
|
function positiveInteger(value) {
|
|
169
160
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
@@ -213,11 +204,16 @@ function parseThinking(value) {
|
|
|
213
204
|
return isThinkingLevel(value) ? value : void 0;
|
|
214
205
|
}
|
|
215
206
|
function parseModelReference(value) {
|
|
216
|
-
const match = /^([^/:\s]+)\/([
|
|
207
|
+
const match = /^([^/:\s]+)\/([^\s]+)$/.exec(value);
|
|
217
208
|
if (!match?.[1] || !match[2]) fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
209
|
+
const reference = match[2];
|
|
210
|
+
const separator = reference.lastIndexOf(":");
|
|
211
|
+
const suffix = separator === -1 ? void 0 : reference.slice(separator + 1);
|
|
212
|
+
if (suffix === "") fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
213
|
+
const thinking = suffix !== void 0 && isThinkingLevel(suffix) ? suffix : void 0;
|
|
214
|
+
const model = thinking === void 0 ? reference : reference.slice(0, separator);
|
|
215
|
+
if (model === "") fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
216
|
+
return { provider: match[1], model, ...thinking === void 0 ? {} : { thinking } };
|
|
221
217
|
}
|
|
222
218
|
function assertModelThinking(value, path = "model") {
|
|
223
219
|
if (value.includes("/") && parseModelReference(value).thinking === void 0) fail("INVALID_METADATA", `${path} must be provider/model:thinking`);
|
|
@@ -288,6 +284,16 @@ function modelCapability(value, aliases, knownModels, settingsPath) {
|
|
|
288
284
|
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
289
285
|
return `${parsed.provider}/${parsed.model}`;
|
|
290
286
|
}
|
|
287
|
+
function reachableTools(pi) {
|
|
288
|
+
const undeclared = pi.getAllTools?.().filter(({ exposure }) => exposure === "codemode" || exposure === "deferred").map(({ name }) => name) ?? [];
|
|
289
|
+
return [.../* @__PURE__ */ new Set([...pi.getActiveTools(), ...undeclared])];
|
|
290
|
+
}
|
|
291
|
+
var VIRTUAL_MODEL_PROVIDER = "workflow";
|
|
292
|
+
function physicalModel(spec, aliases = {}, knownModels, settingsPath) {
|
|
293
|
+
if (spec.provider !== VIRTUAL_MODEL_PROVIDER || !modelAliasName(spec.model, aliases)) return spec;
|
|
294
|
+
const target = resolveModelReference(spec.model, aliases, knownModels, settingsPath);
|
|
295
|
+
return spec.thinking === void 0 ? target : { ...target, thinking: spec.thinking };
|
|
296
|
+
}
|
|
291
297
|
var RESOURCE_PATTERN_OPTIONS = { dot: true, nonegate: true, nocomment: true };
|
|
292
298
|
function resourcePatternBody(pattern) {
|
|
293
299
|
return pattern.startsWith("!") ? pattern.slice(1) : pattern;
|
|
@@ -338,8 +344,9 @@ function validateSchema(schema, at = "schema") {
|
|
|
338
344
|
// src/paths.ts
|
|
339
345
|
import { createHash } from "node:crypto";
|
|
340
346
|
import { realpathSync } from "node:fs";
|
|
347
|
+
import { fileURLToPath } from "node:url";
|
|
341
348
|
import { homedir } from "node:os";
|
|
342
|
-
import { basename, join, resolve } from "node:path";
|
|
349
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
343
350
|
function safePart(value) {
|
|
344
351
|
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
345
352
|
}
|
|
@@ -351,9 +358,22 @@ function canonicalPath(path) {
|
|
|
351
358
|
return absolute;
|
|
352
359
|
}
|
|
353
360
|
}
|
|
361
|
+
function extensionIdentity(path) {
|
|
362
|
+
return path.startsWith("builtin:") ? path : canonicalPath(path.startsWith("file:") ? fileURLToPath(path) : path);
|
|
363
|
+
}
|
|
354
364
|
function sameFilesystemPath(left, right) {
|
|
355
365
|
return canonicalPath(left) === canonicalPath(right);
|
|
356
366
|
}
|
|
367
|
+
function piResourcePath(path, cwd) {
|
|
368
|
+
if (path.startsWith("<") || path.startsWith("builtin:")) return path;
|
|
369
|
+
const trimmed = path.trim();
|
|
370
|
+
const expanded = trimmed === "~" ? homedir() : trimmed.startsWith("~/") ? join(homedir(), trimmed.slice(2)) : /^file:\/\//.test(trimmed) ? fileURLToPath(trimmed) : trimmed;
|
|
371
|
+
return isAbsolute(expanded) ? resolve(expanded) : resolve(cwd, expanded);
|
|
372
|
+
}
|
|
373
|
+
function piResourceContains(root, path) {
|
|
374
|
+
const relativePath = relative(resolve(root), resolve(path));
|
|
375
|
+
return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
|
|
376
|
+
}
|
|
357
377
|
function workflowProjectSettingsPath(cwd) {
|
|
358
378
|
return join(cwd, ".pi", "pi-extensible-workflows", "settings.json");
|
|
359
379
|
}
|
|
@@ -381,17 +401,6 @@ function budgetUsage(value) {
|
|
|
381
401
|
// src/validation.ts
|
|
382
402
|
import * as acorn from "acorn";
|
|
383
403
|
|
|
384
|
-
// src/roles.ts
|
|
385
|
-
import { readFileSync as readFileSync3, readdirSync, statSync } from "node:fs";
|
|
386
|
-
import { basename as basename3, dirname as dirname4, extname, join as join5, resolve as resolve4 } from "node:path";
|
|
387
|
-
import { getAgentDir as getAgentDir2, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
388
|
-
|
|
389
|
-
// src/registry.ts
|
|
390
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
391
|
-
import { basename as basename2, dirname as dirname3, isAbsolute, join as join4 } from "node:path";
|
|
392
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
393
|
-
import { Value } from "typebox/value";
|
|
394
|
-
|
|
395
404
|
// src/io.ts
|
|
396
405
|
import { execFile } from "node:child_process";
|
|
397
406
|
import { randomUUID } from "node:crypto";
|
|
@@ -412,7 +421,7 @@ async function renameWithRetry(from, to) {
|
|
|
412
421
|
} catch (error) {
|
|
413
422
|
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
414
423
|
if (!retryableRenameError(error) || delayMs === void 0) throw error;
|
|
415
|
-
await new Promise((
|
|
424
|
+
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
416
425
|
}
|
|
417
426
|
}
|
|
418
427
|
}
|
|
@@ -478,7 +487,9 @@ async function git(cwd, args, extraEnv = {}) {
|
|
|
478
487
|
}
|
|
479
488
|
|
|
480
489
|
// src/session-lease.ts
|
|
490
|
+
import { execFileSync } from "node:child_process";
|
|
481
491
|
import { link, mkdir, open, readFile as readFile2, readdir, rename as rename2, rm as rm2, stat } from "node:fs/promises";
|
|
492
|
+
import { join as join2 } from "node:path";
|
|
482
493
|
|
|
483
494
|
// src/decoders.ts
|
|
484
495
|
var INVALID_PERSISTED_VALUE = /* @__PURE__ */ Symbol("invalid persisted value");
|
|
@@ -568,32 +579,19 @@ function decodeAgentResourceSelectors(value) {
|
|
|
568
579
|
function decodeContextFileScopes(value) {
|
|
569
580
|
return decodeArray(value, (scope) => isContextFileScope(scope) ? scope : void 0);
|
|
570
581
|
}
|
|
571
|
-
function
|
|
572
|
-
if (!object(value)) return void 0;
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
const
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
const skills = value.skills === void 0 ? void 0 : decodeStringArray(value.skills);
|
|
579
|
-
const extensions = value.extensions === void 0 ? void 0 : decodeStringArray(value.extensions);
|
|
580
|
-
const overrideSystemPrompt = optionalBoolean(value.overrideSystemPrompt);
|
|
582
|
+
function decodePreparedAgentConfiguration(value) {
|
|
583
|
+
if (!object(value) || !jsonValue(value) || typeof value.projectTrusted !== "boolean" || typeof value.systemPromptAppend !== "string") return void 0;
|
|
584
|
+
const model = decodeModelSpec(value.model);
|
|
585
|
+
const tools = decodeStringArray(value.tools);
|
|
586
|
+
const skills = decodeStringArray(value.skills);
|
|
587
|
+
const extensions = decodeStringArray(value.extensions);
|
|
588
|
+
const settings = decodeWorkflowExtensions(value.settings);
|
|
581
589
|
const contextFiles = value.contextFiles === void 0 ? void 0 : decodeContextFileScopes(value.contextFiles);
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
return {
|
|
587
|
-
...prompt === void 0 ? {} : { prompt },
|
|
588
|
-
...description === void 0 ? {} : { description },
|
|
589
|
-
...foldedModel === void 0 ? {} : { model: foldedModel },
|
|
590
|
-
...tools === void 0 ? {} : { tools },
|
|
591
|
-
...skills === void 0 ? {} : { skills },
|
|
592
|
-
...extensions === void 0 ? {} : { extensions },
|
|
593
|
-
...overrideSystemPrompt === void 0 ? {} : { overrideSystemPrompt },
|
|
594
|
-
...contextFiles === void 0 ? {} : { contextFiles },
|
|
595
|
-
...extensionSettings === void 0 ? {} : { extensionSettings }
|
|
596
|
-
};
|
|
590
|
+
const label2 = optionalString(value.label);
|
|
591
|
+
const systemPrompt = optionalString(value.systemPrompt);
|
|
592
|
+
const excludeTools = value.excludeTools === void 0 ? void 0 : decodeStringArray(value.excludeTools);
|
|
593
|
+
if (!model || !tools || !skills || !extensions || !settings || label2 === INVALID_PERSISTED_VALUE || systemPrompt === INVALID_PERSISTED_VALUE || value.contextFiles !== void 0 && !contextFiles || value.excludeTools !== void 0 && !excludeTools) return void 0;
|
|
594
|
+
return { model, tools, skills, extensions, settings, projectTrusted: value.projectTrusted, systemPromptAppend: value.systemPromptAppend, ...contextFiles === void 0 ? {} : { contextFiles }, ...excludeTools === void 0 ? {} : { excludeTools }, ...label2 === void 0 ? {} : { label: label2 }, ...systemPrompt === void 0 ? {} : { systemPrompt } };
|
|
597
595
|
}
|
|
598
596
|
function decodeWorkflowMetadata(value) {
|
|
599
597
|
if (!object(value) || typeof value.name !== "string") return void 0;
|
|
@@ -702,17 +700,22 @@ function decodeScheduledAgentOptions(value) {
|
|
|
702
700
|
const parentBreadcrumb = optionalString(value.parentBreadcrumb);
|
|
703
701
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
704
702
|
const model = optionalString(value.model);
|
|
705
|
-
const role = typeof value.role === "string" ? value.role : void 0;
|
|
706
703
|
const contextFiles = value.contextFiles === void 0 ? void 0 : decodeContextFileScopes(value.contextFiles);
|
|
707
704
|
const schema = value.schema === void 0 ? void 0 : decodeJsonObject(value.schema);
|
|
708
705
|
const retries = optionalNumber(value.retries);
|
|
709
706
|
const timeoutMs = value.timeoutMs;
|
|
707
|
+
if (value.projectTrusted !== void 0 && typeof value.projectTrusted !== "boolean") return void 0;
|
|
708
|
+
const rawCapabilities = value.capabilities;
|
|
709
|
+
const capabilities = rawCapabilities === void 0 ? void 0 : object(rawCapabilities) ? { tools: decodeStringArray(rawCapabilities.tools), skills: decodeStringArray(rawCapabilities.skills), extensions: decodeStringArray(rawCapabilities.extensions) } : void 0;
|
|
710
|
+
if (rawCapabilities !== void 0 && (!capabilities?.tools || !capabilities.skills || !capabilities.extensions)) return void 0;
|
|
711
|
+
const configuration = value.configuration === void 0 ? void 0 : decodePreparedAgentConfiguration(value.configuration);
|
|
712
|
+
if (value.configuration !== void 0 && configuration === void 0) return void 0;
|
|
710
713
|
const agentOptions = value.agentOptions === void 0 ? void 0 : decodeJsonObject(value.agentOptions);
|
|
711
714
|
const agentIdentity = value.agentIdentity === void 0 ? void 0 : decodeIdentity(value.agentIdentity);
|
|
712
715
|
const sessionPath = optionalString(value.sessionPath);
|
|
713
716
|
const continuity = value.continuity === void 0 ? void 0 : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
714
717
|
const extensionSettings = value.extensionSettings === void 0 ? void 0 : decodeWorkflowExtensions(value.extensionSettings);
|
|
715
|
-
if (sessionPath === INVALID_PERSISTED_VALUE || continuity === INVALID_PERSISTED_VALUE || requestedLabel === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE || model === INVALID_PERSISTED_VALUE || value.skills !== void 0 && !skills || value.extensions !== void 0 && !extensions || value.
|
|
718
|
+
if (sessionPath === INVALID_PERSISTED_VALUE || continuity === INVALID_PERSISTED_VALUE || requestedLabel === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE || model === INVALID_PERSISTED_VALUE || value.skills !== void 0 && !skills || value.extensions !== void 0 && !extensions || value.contextFiles !== void 0 && !contextFiles || value.schema !== void 0 && !schema || retries === INVALID_PERSISTED_VALUE || retries !== void 0 && !integer(retries) || timeoutMs !== void 0 && timeoutMs !== null && !finiteNumber(timeoutMs) || value.agentOptions !== void 0 && !agentOptions || value.agentIdentity !== void 0 && !agentIdentity || value.extensionSettings !== void 0 && !extensionSettings) return void 0;
|
|
716
719
|
return {
|
|
717
720
|
label: value.label,
|
|
718
721
|
...requestedLabel === void 0 ? {} : { requestedLabel },
|
|
@@ -723,7 +726,6 @@ function decodeScheduledAgentOptions(value) {
|
|
|
723
726
|
...extensions === void 0 ? {} : { extensions },
|
|
724
727
|
...worktreeOwner === void 0 ? {} : { worktreeOwner },
|
|
725
728
|
...model === void 0 ? {} : { model },
|
|
726
|
-
...role === void 0 ? {} : { role },
|
|
727
729
|
...contextFiles === void 0 ? {} : { contextFiles },
|
|
728
730
|
...schema === void 0 ? {} : { schema },
|
|
729
731
|
...retries === void 0 ? {} : { retries },
|
|
@@ -731,6 +733,9 @@ function decodeScheduledAgentOptions(value) {
|
|
|
731
733
|
...sessionPath === void 0 ? {} : { sessionPath },
|
|
732
734
|
...continuity === void 0 ? {} : { continuity },
|
|
733
735
|
...agentOptions === void 0 ? {} : { agentOptions },
|
|
736
|
+
...configuration === void 0 ? {} : { configuration },
|
|
737
|
+
...value.projectTrusted === void 0 ? {} : { projectTrusted: value.projectTrusted },
|
|
738
|
+
...capabilities?.tools && capabilities.skills && capabilities.extensions ? { capabilities: { tools: capabilities.tools, skills: capabilities.skills, extensions: capabilities.extensions } } : {},
|
|
734
739
|
...agentIdentity === void 0 ? {} : { agentIdentity },
|
|
735
740
|
...extensionSettings === void 0 ? {} : { extensionSettings }
|
|
736
741
|
};
|
|
@@ -771,11 +776,10 @@ function decodeAgentResourceInspection(value) {
|
|
|
771
776
|
const sources = sourceRecord === void 0 ? void 0 : {
|
|
772
777
|
global: decodeAgentResourceSelectors(sourceRecord.global),
|
|
773
778
|
project: decodeAgentResourceSelectors(sourceRecord.project),
|
|
774
|
-
...sourceRecord.role === void 0 ? {} : { role: decodeAgentResourceSelectors(sourceRecord.role) },
|
|
775
779
|
...sourceRecord.call === void 0 ? {} : { call: decodeAgentResourceSelectors(sourceRecord.call) }
|
|
776
780
|
};
|
|
777
|
-
if (!selectors || !skills || !extensions || !tools || !unmatchedSkills || !unmatchedExtensions || !unmatchedTools || rawSources !== void 0 && (!sources || !sources.global || !sources.project || sources.
|
|
778
|
-
return { selectors: { skills: [...selectors.skills ?? []], extensions: [...selectors.extensions ?? []], tools: [...selectors.tools ?? []] }, skills, extensions, tools, unmatchedSkills, unmatchedExtensions, unmatchedTools, ...sources === void 0 ? {} : { selectorSources: { global: sources.global ?? {}, project: sources.project ?? {}, ...sources.
|
|
781
|
+
if (!selectors || !skills || !extensions || !tools || !unmatchedSkills || !unmatchedExtensions || !unmatchedTools || rawSources !== void 0 && (!sources || !sources.global || !sources.project || sources.call === void 0 && sourceRecord?.call !== void 0)) return void 0;
|
|
782
|
+
return { selectors: { skills: [...selectors.skills ?? []], extensions: [...selectors.extensions ?? []], tools: [...selectors.tools ?? []] }, skills, extensions, tools, unmatchedSkills, unmatchedExtensions, unmatchedTools, ...sources === void 0 ? {} : { selectorSources: { global: sources.global ?? {}, project: sources.project ?? {}, ...sources.call === void 0 ? {} : { call: sources.call } } } };
|
|
779
783
|
}
|
|
780
784
|
function decodeAgentSetupSummary(value) {
|
|
781
785
|
if (!object(value) || typeof value.cwd !== "string") return void 0;
|
|
@@ -813,17 +817,16 @@ function decodeAgent(value) {
|
|
|
813
817
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
814
818
|
const handle = optionalString(value.handle);
|
|
815
819
|
const continuity = value.continuity === void 0 ? void 0 : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
816
|
-
const role = optionalString(value.role);
|
|
817
820
|
const requestedModel = optionalString(value.requestedModel);
|
|
818
821
|
const startedAt = optionalNumber(value.startedAt);
|
|
819
822
|
const durationMs = optionalNumber(value.durationMs);
|
|
820
823
|
const attemptDetails = value.attemptDetails === void 0 ? void 0 : decodeArray(value.attemptDetails, decodeAgentAttempt);
|
|
821
824
|
const accounting2 = value.accounting === void 0 ? void 0 : decodeAgentAccounting(value.accounting);
|
|
822
825
|
const toolCalls = value.toolCalls === void 0 ? void 0 : decodeArray(value.toolCalls, decodeAgentToolCall);
|
|
823
|
-
const
|
|
826
|
+
const activity = value.activity === void 0 ? void 0 : decodeAgentActivity(value.activity);
|
|
824
827
|
const lastEventAt = optionalNumber(value.lastEventAt);
|
|
825
|
-
if (systemPrompt === INVALID_PERSISTED_VALUE || prompt === INVALID_PERSISTED_VALUE || label2 === INVALID_PERSISTED_VALUE || parentId === INVALID_PERSISTED_VALUE || resultPath2 === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE ||
|
|
826
|
-
if (!model || !tools || value.structuralPath !== void 0 && !structuralPath2 || value.attemptDetails !== void 0 && !attemptDetails || value.accounting !== void 0 && !accounting2 || value.toolCalls !== void 0 && !toolCalls || value.activity !== void 0 && !
|
|
828
|
+
if (systemPrompt === INVALID_PERSISTED_VALUE || prompt === INVALID_PERSISTED_VALUE || label2 === INVALID_PERSISTED_VALUE || parentId === INVALID_PERSISTED_VALUE || resultPath2 === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE || requestedModel === INVALID_PERSISTED_VALUE || startedAt === INVALID_PERSISTED_VALUE || durationMs === INVALID_PERSISTED_VALUE || lastEventAt === INVALID_PERSISTED_VALUE || handle === INVALID_PERSISTED_VALUE || continuity === INVALID_PERSISTED_VALUE || handle !== void 0 && !positiveInteger(value.turn)) return void 0;
|
|
829
|
+
if (!model || !tools || value.structuralPath !== void 0 && !structuralPath2 || value.attemptDetails !== void 0 && !attemptDetails || value.accounting !== void 0 && !accounting2 || value.toolCalls !== void 0 && !toolCalls || value.activity !== void 0 && !activity) return void 0;
|
|
827
830
|
return {
|
|
828
831
|
...systemPrompt === void 0 ? {} : { systemPrompt },
|
|
829
832
|
...prompt === void 0 ? {} : { prompt },
|
|
@@ -839,7 +842,6 @@ function decodeAgent(value) {
|
|
|
839
842
|
...worktreeOwner === void 0 ? {} : { worktreeOwner },
|
|
840
843
|
...handle === void 0 ? {} : { handle, turn: value.turn },
|
|
841
844
|
...continuity === void 0 ? {} : { continuity },
|
|
842
|
-
...role === void 0 ? {} : { role },
|
|
843
845
|
...requestedModel === void 0 ? {} : { requestedModel },
|
|
844
846
|
model,
|
|
845
847
|
tools,
|
|
@@ -849,7 +851,7 @@ function decodeAgent(value) {
|
|
|
849
851
|
...attemptDetails === void 0 ? {} : { attemptDetails },
|
|
850
852
|
...accounting2 === void 0 ? {} : { accounting: accounting2 },
|
|
851
853
|
...toolCalls === void 0 ? {} : { toolCalls },
|
|
852
|
-
...
|
|
854
|
+
...activity === void 0 ? {} : { activity },
|
|
853
855
|
...lastEventAt === void 0 ? {} : { lastEventAt }
|
|
854
856
|
};
|
|
855
857
|
}
|
|
@@ -897,6 +899,22 @@ function decodeRunEvent(value) {
|
|
|
897
899
|
if (timestamp2 === INVALID_PERSISTED_VALUE) return void 0;
|
|
898
900
|
return { type: value.type, message: value.message, ...timestamp2 === void 0 ? {} : { timestamp: timestamp2 } };
|
|
899
901
|
}
|
|
902
|
+
function decodeScriptCall(value) {
|
|
903
|
+
if (!object(value) || value.kind !== "shell" && value.kind !== "tool" || typeof value.name !== "string" || typeof value.path !== "string" || !finiteNumber(value.startedAt)) return void 0;
|
|
904
|
+
const shell = value.kind === "shell";
|
|
905
|
+
if (shell && !jsonValue(value.input)) return void 0;
|
|
906
|
+
const inputBytes = shell ? optionalNumber(value.inputBytes) : void 0;
|
|
907
|
+
const phase = optionalString(value.phase);
|
|
908
|
+
const finishedAt = optionalNumber(value.finishedAt);
|
|
909
|
+
let error;
|
|
910
|
+
if (value.error !== void 0) {
|
|
911
|
+
if (shell) error = decodeWorkflowError(value.error);
|
|
912
|
+
else if (object(value.error) && typeof value.error.code === "string") error = { code: isWorkflowErrorCode(value.error.code) ? value.error.code : "INTERNAL_ERROR" };
|
|
913
|
+
if (!error) return void 0;
|
|
914
|
+
}
|
|
915
|
+
if (inputBytes === INVALID_PERSISTED_VALUE || phase === INVALID_PERSISTED_VALUE || finishedAt === INVALID_PERSISTED_VALUE) return void 0;
|
|
916
|
+
return { kind: value.kind, name: value.name, path: value.path, ...shell && jsonValue(value.input) ? { input: value.input } : {}, ...inputBytes === void 0 ? {} : { inputBytes }, ...phase === void 0 ? {} : { phase }, startedAt: value.startedAt, ...finishedAt === void 0 ? {} : { finishedAt }, ...error === void 0 ? {} : { error } };
|
|
917
|
+
}
|
|
900
918
|
function decodeDelivery(value) {
|
|
901
919
|
if (!object(value)) return void 0;
|
|
902
920
|
const mode = value.mode;
|
|
@@ -926,6 +944,8 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
926
944
|
const budgetEvents = value.budgetEvents === void 0 ? void 0 : decodeArray(value.budgetEvents, decodeBudgetEvent);
|
|
927
945
|
const events = value.events === void 0 ? void 0 : decodeArray(value.events, decodeRunEvent);
|
|
928
946
|
const delivery = value.delivery === void 0 ? void 0 : decodeDelivery(value.delivery);
|
|
947
|
+
const scriptCalls = value.scriptCalls === void 0 ? void 0 : decodeArray(value.scriptCalls, decodeScriptCall);
|
|
948
|
+
if (value.scriptCalls !== void 0 && !scriptCalls) return void 0;
|
|
929
949
|
if (!agentSessions || !agents || parentRunId === INVALID_PERSISTED_VALUE || value.retry !== void 0 && !retry || phase === INVALID_PERSISTED_VALUE || value.phaseHistory !== void 0 && !phaseHistory || phaseHistoryIndex === INVALID_PERSISTED_VALUE || activeShells === INVALID_PERSISTED_VALUE || activeShellStartedAt === INVALID_PERSISTED_VALUE || value.activeShellsByPhase !== void 0 && !activeShellsByPhase || value.error !== void 0 && !error || failedAt === INVALID_PERSISTED_VALUE || value.budget !== void 0 && !budget || budgetVersion === INVALID_PERSISTED_VALUE || value.budgetVersion !== void 0 && !integer(budgetVersion) || value.usage !== void 0 && !usage || value.budgetEvents !== void 0 && !budgetEvents || value.events !== void 0 && !events || value.delivery !== void 0 && !delivery) return void 0;
|
|
930
950
|
if (value.agentSessions === void 0 && !allowLegacyAgentSessions) return void 0;
|
|
931
951
|
return {
|
|
@@ -944,6 +964,7 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
944
964
|
...activeShells === void 0 ? {} : { activeShells },
|
|
945
965
|
...activeShellStartedAt === void 0 ? {} : { activeShellStartedAt },
|
|
946
966
|
...activeShellsByPhase === void 0 ? {} : { activeShellsByPhase },
|
|
967
|
+
...scriptCalls === void 0 ? {} : { scriptCalls },
|
|
947
968
|
...error === void 0 ? {} : { error },
|
|
948
969
|
...failedAt === void 0 ? {} : { failedAt },
|
|
949
970
|
...budget === void 0 ? {} : { budget },
|
|
@@ -968,13 +989,11 @@ function decodeLaunchSnapshot(value) {
|
|
|
968
989
|
const phases = value.phases === void 0 ? void 0 : decodeStringArray(value.phases);
|
|
969
990
|
const models = decodeStringArray(value.models);
|
|
970
991
|
const tools = decodeStringArray(value.tools);
|
|
971
|
-
const
|
|
972
|
-
const roles = value.roles === void 0 ? void 0 : decodeRecord(value.roles, decodeAgentDefinition);
|
|
973
|
-
const projectRoles = value.projectRoles === void 0 ? void 0 : decodeStringArray(value.projectRoles);
|
|
992
|
+
const agentConfigurations = decodeRecord(value.agentConfigurations, decodePreparedAgentConfiguration);
|
|
974
993
|
const schemas = decodeArray(value.schemas, decodeJsonObject);
|
|
975
|
-
if (identityVersion
|
|
994
|
+
if (identityVersion !== LAUNCH_SNAPSHOT_IDENTITY_VERSION || launchMode !== void 0 && !isLaunchMode(launchMode) || args === void 0 || !metadata || !settings || value.settingsSources !== void 0 && !settingsSources || value.budget !== void 0 && !budget || settingsPath === INVALID_PERSISTED_VALUE || value.modelAliases !== void 0 && !modelAliases || value.phases !== void 0 && !phases || !models || !tools || !agentConfigurations || !schemas) return void 0;
|
|
976
995
|
return {
|
|
977
|
-
|
|
996
|
+
identityVersion: LAUNCH_SNAPSHOT_IDENTITY_VERSION,
|
|
978
997
|
...launchMode === void 0 ? {} : { launchMode },
|
|
979
998
|
script: value.script,
|
|
980
999
|
args,
|
|
@@ -987,9 +1006,7 @@ function decodeLaunchSnapshot(value) {
|
|
|
987
1006
|
...phases === void 0 ? {} : { phases },
|
|
988
1007
|
models,
|
|
989
1008
|
tools,
|
|
990
|
-
|
|
991
|
-
...roles === void 0 ? {} : { roles },
|
|
992
|
-
...projectRoles === void 0 ? {} : { projectRoles },
|
|
1009
|
+
agentConfigurations,
|
|
993
1010
|
schemas
|
|
994
1011
|
};
|
|
995
1012
|
}
|
|
@@ -1070,18 +1087,43 @@ function decodeSystemPromptArtifact(value) {
|
|
|
1070
1087
|
}
|
|
1071
1088
|
|
|
1072
1089
|
// src/session-lease.ts
|
|
1073
|
-
|
|
1090
|
+
var START_MARGIN_MS = 6e4;
|
|
1091
|
+
var clockTicks;
|
|
1092
|
+
function clockTickRate() {
|
|
1093
|
+
if (clockTicks !== void 0) return clockTicks;
|
|
1094
|
+
try {
|
|
1095
|
+
const rate = Number(execFileSync("getconf", ["CLK_TCK"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim());
|
|
1096
|
+
clockTicks = Number.isInteger(rate) && rate > 0 ? rate : 100;
|
|
1097
|
+
} catch {
|
|
1098
|
+
clockTicks = 100;
|
|
1099
|
+
}
|
|
1100
|
+
return clockTicks;
|
|
1101
|
+
}
|
|
1102
|
+
async function linuxProcessStart(pid, procRoot) {
|
|
1103
|
+
try {
|
|
1104
|
+
const text = await readFile2(join2(procRoot, String(pid), "stat"), "utf8");
|
|
1105
|
+
const close = text.lastIndexOf(")");
|
|
1106
|
+
const ticks = close < 0 ? Number.NaN : Number(text.slice(close + 2).split(" ")[19]);
|
|
1107
|
+
const btime = /^btime (\d+)$/m.exec(await readFile2(join2(procRoot, "stat"), "utf8"))?.[1];
|
|
1108
|
+
if (btime === void 0 || !Number.isInteger(ticks) || ticks < 0) return void 0;
|
|
1109
|
+
return Number(btime) * 1e3 + ticks / clockTickRate() * 1e3;
|
|
1110
|
+
} catch {
|
|
1111
|
+
return void 0;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
async function processAlive(pid, startedAt, procRoot = "/proc") {
|
|
1074
1115
|
try {
|
|
1075
1116
|
process.kill(pid, 0);
|
|
1076
1117
|
} catch (error) {
|
|
1077
1118
|
return !isNodeError(error, "ESRCH");
|
|
1078
1119
|
}
|
|
1079
|
-
if (startedAt
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1120
|
+
if (startedAt === void 0 || process.platform !== "linux") return true;
|
|
1121
|
+
const start = await linuxProcessStart(pid, procRoot);
|
|
1122
|
+
if (start !== void 0) return start <= startedAt + START_MARGIN_MS;
|
|
1123
|
+
try {
|
|
1124
|
+
process.kill(pid, 0);
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
return !isNodeError(error, "ESRCH");
|
|
1085
1127
|
}
|
|
1086
1128
|
return true;
|
|
1087
1129
|
}
|
|
@@ -1091,7 +1133,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
1091
1133
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
1092
1134
|
import { access, mkdir as mkdir2, readdir as readdir2, readFile as readFile3, realpath, rename as rename3, rm as rm3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1093
1135
|
import { homedir as homedir2 } from "node:os";
|
|
1094
|
-
import { dirname, join as
|
|
1136
|
+
import { dirname, join as join3, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
1095
1137
|
import { promisify as promisify2 } from "node:util";
|
|
1096
1138
|
var executeFile = promisify2(execFile2);
|
|
1097
1139
|
var WORKTREE_POST_CREATE_TIMEOUT_MS = 6e4;
|
|
@@ -1102,36 +1144,36 @@ var SYSTEM_PROMPT_BODIES = "bodies";
|
|
|
1102
1144
|
var SYSTEM_PROMPT_SEQUENCE = "sequence";
|
|
1103
1145
|
var SYSTEM_PROMPT_ARTIFACT = { version: 2, format: "append-only", storage: SYSTEM_PROMPT_STORAGE };
|
|
1104
1146
|
function summaryArtifacts(directory) {
|
|
1105
|
-
return { runDirectory: directory, statePath:
|
|
1147
|
+
return { runDirectory: directory, statePath: join3(directory, "state.json"), journalPath: join3(directory, "journal.json"), snapshotPath: join3(directory, "snapshot.json"), workflowPath: join3(directory, "workflow.js"), resultPath: join3(directory, "result.json"), summaryPath: join3(directory, "summary.json") };
|
|
1106
1148
|
}
|
|
1107
1149
|
function summaryFromRun(run, directory, journal, previous, fallbackCreatedAt, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1108
1150
|
const createdAt = typeof previous?.createdAt === "string" ? previous.createdAt : fallbackCreatedAt;
|
|
1109
1151
|
const failedAt = run.failedAt ?? run.error?.failedAt;
|
|
1110
1152
|
const replayablePaths = [.../* @__PURE__ */ new Set([...run.retry?.completedPaths ?? [], ...Object.keys(journal.completed)])];
|
|
1111
1153
|
const incompletePaths = [.../* @__PURE__ */ new Set([...run.retry?.incompletePaths ?? [], ...failedAt ? [failedAt] : []])];
|
|
1112
|
-
return { schemaVersion: 1, runId: run.id, sessionId: run.sessionId, workflowName: run.workflowName, state: run.state, createdAt, updatedAt: now, ...previous?.terminalAt || HARD_TERMINAL_RUN_STATES.has(run.state) ? { terminalAt: previous?.terminalAt ?? now } : {}, usage: budgetUsage(run.usage), agents: run.agents.map(({ id, name, label: label2, state,
|
|
1154
|
+
return { schemaVersion: 1, runId: run.id, sessionId: run.sessionId, workflowName: run.workflowName, state: run.state, createdAt, updatedAt: now, ...previous?.terminalAt || HARD_TERMINAL_RUN_STATES.has(run.state) ? { terminalAt: previous?.terminalAt ?? now } : {}, usage: budgetUsage(run.usage), agents: run.agents.map(({ id, name, label: label2, state, attempts }) => ({ id, name, ...label2 ? { label: label2 } : {}, state, attempts })), ...run.error ? { error: run.error } : {}, ...failedAt ? { failedAt } : {}, replayablePaths, incompletePaths, artifacts: summaryArtifacts(directory) };
|
|
1113
1155
|
}
|
|
1114
1156
|
function systemPromptStoragePath(directory) {
|
|
1115
|
-
return
|
|
1157
|
+
return join3(directory, SYSTEM_PROMPT_STORAGE);
|
|
1116
1158
|
}
|
|
1117
1159
|
function systemPromptRecordsPath(directory) {
|
|
1118
|
-
return
|
|
1160
|
+
return join3(systemPromptStoragePath(directory), SYSTEM_PROMPT_RECORDS);
|
|
1119
1161
|
}
|
|
1120
1162
|
function systemPromptBodiesPath(directory) {
|
|
1121
|
-
return
|
|
1163
|
+
return join3(systemPromptStoragePath(directory), SYSTEM_PROMPT_BODIES);
|
|
1122
1164
|
}
|
|
1123
1165
|
function systemPromptSequencePath(directory) {
|
|
1124
|
-
return
|
|
1166
|
+
return join3(systemPromptStoragePath(directory), SYSTEM_PROMPT_SEQUENCE);
|
|
1125
1167
|
}
|
|
1126
1168
|
function systemPromptRecordName(sequence) {
|
|
1127
1169
|
return `${String(sequence).padStart(20, "0")}.json`;
|
|
1128
1170
|
}
|
|
1129
1171
|
async function createSystemPromptStorage(directory, writeArtifact) {
|
|
1130
1172
|
const storage = systemPromptStoragePath(directory);
|
|
1131
|
-
await mkdir2(
|
|
1132
|
-
await mkdir2(
|
|
1173
|
+
await mkdir2(join3(storage, SYSTEM_PROMPT_RECORDS), { recursive: true, mode: 448 });
|
|
1174
|
+
await mkdir2(join3(storage, SYSTEM_PROMPT_BODIES), { recursive: true, mode: 448 });
|
|
1133
1175
|
await atomicWriteFile(systemPromptSequencePath(directory), "0\n");
|
|
1134
|
-
if (writeArtifact) await atomicJson(
|
|
1176
|
+
if (writeArtifact) await atomicJson(join3(directory, "system-prompts.json"), SYSTEM_PROMPT_ARTIFACT);
|
|
1135
1177
|
}
|
|
1136
1178
|
var RunStore = class _RunStore {
|
|
1137
1179
|
constructor(cwd, sessionId, runId, home = homedir2()) {
|
|
@@ -1140,7 +1182,7 @@ var RunStore = class _RunStore {
|
|
|
1140
1182
|
this.runId = runId;
|
|
1141
1183
|
this.home = home;
|
|
1142
1184
|
this.cwd = resolve2(cwd);
|
|
1143
|
-
this.directory =
|
|
1185
|
+
this.directory = join3(runsDirectory(this.cwd, sessionId, home), safePart(runId));
|
|
1144
1186
|
}
|
|
1145
1187
|
cwd;
|
|
1146
1188
|
sessionId;
|
|
@@ -1162,19 +1204,19 @@ var RunStore = class _RunStore {
|
|
|
1162
1204
|
}
|
|
1163
1205
|
async create(run, snapshot) {
|
|
1164
1206
|
this.#assertRunIdentity(run, "INTERNAL_ERROR", "Run identity does not match its session-scoped store");
|
|
1165
|
-
const temporary =
|
|
1207
|
+
const temporary = join3(dirname(this.directory), `.${safePart(this.runId)}.${String(process.pid)}.${randomUUID2()}.tmp`);
|
|
1166
1208
|
await mkdir2(dirname(this.directory), { recursive: true, mode: 448 });
|
|
1167
1209
|
await mkdir2(temporary, { mode: 448 });
|
|
1168
1210
|
try {
|
|
1169
|
-
await writeFile2(
|
|
1170
|
-
await atomicJson(
|
|
1171
|
-
await atomicJson(
|
|
1172
|
-
await atomicJson(
|
|
1173
|
-
await atomicJson(
|
|
1174
|
-
await atomicJson(
|
|
1175
|
-
await atomicJson(
|
|
1211
|
+
await writeFile2(join3(temporary, "workflow.js"), snapshot.script, { encoding: "utf8", mode: 384 });
|
|
1212
|
+
await atomicJson(join3(temporary, "snapshot.json"), snapshot);
|
|
1213
|
+
await atomicJson(join3(temporary, "journal.json"), { completed: {}, awaiting: {}, decisions: {} });
|
|
1214
|
+
await atomicJson(join3(temporary, "ownership.json"), []);
|
|
1215
|
+
await atomicJson(join3(temporary, "worktrees.json"), []);
|
|
1216
|
+
await atomicJson(join3(temporary, "borrowed-worktrees.json"), []);
|
|
1217
|
+
await atomicJson(join3(temporary, "state.json"), run);
|
|
1176
1218
|
await createSystemPromptStorage(temporary, true);
|
|
1177
|
-
await atomicJson(
|
|
1219
|
+
await atomicJson(join3(temporary, "summary.json"), summaryFromRun(run, this.directory, { completed: {} }, void 0, (/* @__PURE__ */ new Date()).toISOString()));
|
|
1178
1220
|
await rename3(temporary, this.directory);
|
|
1179
1221
|
} catch (error) {
|
|
1180
1222
|
await rm3(temporary, { recursive: true, force: true });
|
|
@@ -1183,12 +1225,12 @@ var RunStore = class _RunStore {
|
|
|
1183
1225
|
}
|
|
1184
1226
|
async refreshSummary() {
|
|
1185
1227
|
const write = this.summaryLane.run(async () => {
|
|
1186
|
-
const run = decodePersistedRun(await json(
|
|
1187
|
-
const journal = decodeJournal(await json(
|
|
1188
|
-
const previous = await json(
|
|
1228
|
+
const run = decodePersistedRun(await json(join3(this.directory, "state.json")), true);
|
|
1229
|
+
const journal = decodeJournal(await json(join3(this.directory, "journal.json")));
|
|
1230
|
+
const previous = await json(join3(this.directory, "summary.json")).then(decodeSummaryProjection).catch(() => void 0);
|
|
1189
1231
|
if (!run || !journal) throw new Error("Persisted run or journal is invalid");
|
|
1190
|
-
const fallbackCreatedAt = await stat2(
|
|
1191
|
-
await atomicJson(
|
|
1232
|
+
const fallbackCreatedAt = await stat2(join3(this.directory, "state.json")).then((value) => new Date(value.mtimeMs).toISOString());
|
|
1233
|
+
await atomicJson(join3(this.directory, "summary.json"), summaryFromRun(run, this.directory, journal, previous, fallbackCreatedAt));
|
|
1192
1234
|
});
|
|
1193
1235
|
await write;
|
|
1194
1236
|
}
|
|
@@ -1198,7 +1240,7 @@ var RunStore = class _RunStore {
|
|
|
1198
1240
|
}
|
|
1199
1241
|
async isComplete() {
|
|
1200
1242
|
try {
|
|
1201
|
-
await Promise.all([access(
|
|
1243
|
+
await Promise.all([access(join3(this.directory, "snapshot.json")), access(join3(this.directory, "journal.json")), access(join3(this.directory, "ownership.json")), access(join3(this.directory, "state.json"))]);
|
|
1202
1244
|
return true;
|
|
1203
1245
|
} catch {
|
|
1204
1246
|
return false;
|
|
@@ -1206,18 +1248,18 @@ var RunStore = class _RunStore {
|
|
|
1206
1248
|
}
|
|
1207
1249
|
async load() {
|
|
1208
1250
|
await this.stateLane.run(async () => void 0);
|
|
1209
|
-
const rawRun = await json(
|
|
1251
|
+
const rawRun = await json(join3(this.directory, "state.json"));
|
|
1210
1252
|
const run = decodePersistedRun(rawRun, true);
|
|
1211
1253
|
if (!run) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted run is invalid");
|
|
1212
1254
|
this.#assertRunIdentity(run, "RESUME_INCOMPATIBLE", "Persisted run belongs to another cwd or Pi session");
|
|
1213
1255
|
if (!object(rawRun) || !Array.isArray(rawRun.agentSessions) || Object.hasOwn(rawRun, "nativeSessions")) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted run uses an unsupported agent session format");
|
|
1214
|
-
const snapshot = decodeLaunchSnapshot(await json(
|
|
1256
|
+
const snapshot = decodeLaunchSnapshot(await json(join3(this.directory, "snapshot.json")));
|
|
1215
1257
|
if (!snapshot) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted launch snapshot is invalid");
|
|
1216
1258
|
return { run, snapshot: loadLaunchSnapshot(snapshot) };
|
|
1217
1259
|
}
|
|
1218
1260
|
async loadStatus() {
|
|
1219
1261
|
await this.stateLane.run(async () => void 0);
|
|
1220
|
-
const run = decodePersistedRun(await json(
|
|
1262
|
+
const run = decodePersistedRun(await json(join3(this.directory, "state.json")), true);
|
|
1221
1263
|
if (!run) throw new WorkflowError("RUN_NOT_FOUND", "Persisted run is invalid");
|
|
1222
1264
|
this.#assertRunIdentity(run, "RUN_NOT_FOUND", "Persisted run does not belong to this project");
|
|
1223
1265
|
return run;
|
|
@@ -1226,11 +1268,11 @@ var RunStore = class _RunStore {
|
|
|
1226
1268
|
await this.stateLane.run(async () => void 0);
|
|
1227
1269
|
await this.journalLane.run(async () => void 0);
|
|
1228
1270
|
await this.summaryLane.run(async () => void 0);
|
|
1229
|
-
const run = decodePersistedRun(await json(
|
|
1230
|
-
const journal = decodeJournal(await json(
|
|
1231
|
-
const previous = await json(
|
|
1271
|
+
const run = decodePersistedRun(await json(join3(this.directory, "state.json")), true);
|
|
1272
|
+
const journal = decodeJournal(await json(join3(this.directory, "journal.json")));
|
|
1273
|
+
const previous = await json(join3(this.directory, "summary.json")).then(decodeSummaryProjection).catch(() => void 0);
|
|
1232
1274
|
if (!run || !journal) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted run or journal is invalid");
|
|
1233
|
-
const [stateStat, journalStat] = await Promise.all([stat2(
|
|
1275
|
+
const [stateStat, journalStat] = await Promise.all([stat2(join3(this.directory, "state.json")), stat2(join3(this.directory, "journal.json"))]);
|
|
1234
1276
|
const fallbackCreatedAt = new Date(stateStat.mtimeMs).toISOString();
|
|
1235
1277
|
const previousUpdatedAt = previous?.updatedAt === void 0 ? Number.NaN : Date.parse(previous.updatedAt);
|
|
1236
1278
|
const updatedAt = new Date(Math.max(stateStat.mtimeMs, journalStat.mtimeMs, Number.isNaN(previousUpdatedAt) ? 0 : previousUpdatedAt)).toISOString();
|
|
@@ -1239,41 +1281,55 @@ var RunStore = class _RunStore {
|
|
|
1239
1281
|
async saveState(run) {
|
|
1240
1282
|
const write = this.stateLane.run(async () => {
|
|
1241
1283
|
this.#assertRunIdentity(run, "INTERNAL_ERROR", "Run identity does not match its session-scoped store");
|
|
1242
|
-
await atomicJson(
|
|
1284
|
+
await atomicJson(join3(this.directory, "state.json"), run);
|
|
1243
1285
|
this.refreshSummaryBestEffort();
|
|
1244
1286
|
});
|
|
1245
1287
|
await write;
|
|
1246
1288
|
}
|
|
1247
1289
|
async updateState(update) {
|
|
1248
1290
|
const write = this.stateLane.run(async () => {
|
|
1249
|
-
const current = decodePersistedRun(await json(
|
|
1291
|
+
const current = decodePersistedRun(await json(join3(this.directory, "state.json")));
|
|
1250
1292
|
if (!current) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted run is invalid");
|
|
1251
1293
|
this.#assertRunIdentity(current, "RESUME_INCOMPATIBLE", "Persisted run belongs to another cwd or Pi session");
|
|
1252
1294
|
const result = await update(current);
|
|
1253
1295
|
this.#assertRunIdentity(result, "INTERNAL_ERROR", "Run identity does not match its session-scoped store");
|
|
1254
|
-
await atomicJson(
|
|
1296
|
+
await atomicJson(join3(this.directory, "state.json"), result);
|
|
1255
1297
|
this.refreshSummaryBestEffort();
|
|
1256
1298
|
return result;
|
|
1257
1299
|
});
|
|
1258
1300
|
return write;
|
|
1259
1301
|
}
|
|
1302
|
+
async saveAgentConfiguration(key, configuration) {
|
|
1303
|
+
await this.launchSnapshotLane.run(async () => {
|
|
1304
|
+
const snapshot = decodeLaunchSnapshot(await json(join3(this.directory, "snapshot.json")));
|
|
1305
|
+
if (!snapshot) throw new WorkflowError("RESUME_INCOMPATIBLE", "Workflow launch snapshot identity version is incompatible");
|
|
1306
|
+
await atomicJson(join3(this.directory, "snapshot.json"), { ...snapshot, agentConfigurations: { ...snapshot.agentConfigurations, [key]: configuration } });
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
async setLaunchMode(launchMode) {
|
|
1310
|
+
await this.launchSnapshotLane.run(async () => {
|
|
1311
|
+
const snapshot = decodeLaunchSnapshot(await json(join3(this.directory, "snapshot.json")));
|
|
1312
|
+
if (!snapshot) throw new WorkflowError("RESUME_INCOMPATIBLE", "Workflow launch snapshot identity version is incompatible");
|
|
1313
|
+
await atomicJson(join3(this.directory, "snapshot.json"), { ...snapshot, launchMode });
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1260
1316
|
async saveSnapshot(snapshot) {
|
|
1261
|
-
const write = this.launchSnapshotLane.run(() => atomicJson(
|
|
1317
|
+
const write = this.launchSnapshotLane.run(() => atomicJson(join3(this.directory, "snapshot.json"), snapshot));
|
|
1262
1318
|
await write;
|
|
1263
1319
|
}
|
|
1264
1320
|
async appendEvent(event) {
|
|
1265
1321
|
await this.updateState((run) => ({ ...run, events: [...run.events ?? [], ...event.type !== "log" && run.events?.some((current) => current.type === event.type && current.message === event.message) ? [] : [event]] }));
|
|
1266
1322
|
}
|
|
1267
1323
|
async saveOwnership(nodes) {
|
|
1268
|
-
await atomicJson(
|
|
1324
|
+
await atomicJson(join3(this.directory, "ownership.json"), nodes);
|
|
1269
1325
|
}
|
|
1270
1326
|
async loadOwnership() {
|
|
1271
|
-
const nodes = decodeOwnershipRecords(await json(
|
|
1327
|
+
const nodes = decodeOwnershipRecords(await json(join3(this.directory, "ownership.json")));
|
|
1272
1328
|
if (!nodes) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted ownership records are invalid");
|
|
1273
1329
|
return nodes;
|
|
1274
1330
|
}
|
|
1275
1331
|
systemPromptPath() {
|
|
1276
|
-
return
|
|
1332
|
+
return join3(this.directory, "system-prompts.json");
|
|
1277
1333
|
}
|
|
1278
1334
|
async readSystemPromptArtifact() {
|
|
1279
1335
|
try {
|
|
@@ -1287,7 +1343,7 @@ var RunStore = class _RunStore {
|
|
|
1287
1343
|
}
|
|
1288
1344
|
async appendSystemPromptV2(entry) {
|
|
1289
1345
|
const sha256 = createHash2("sha256").update(entry.prompt).digest("hex");
|
|
1290
|
-
const bodyPath =
|
|
1346
|
+
const bodyPath = join3(systemPromptBodiesPath(this.directory), sha256);
|
|
1291
1347
|
try {
|
|
1292
1348
|
await access(bodyPath);
|
|
1293
1349
|
} catch (error) {
|
|
@@ -1299,7 +1355,7 @@ var RunStore = class _RunStore {
|
|
|
1299
1355
|
const sequence = previous + 1;
|
|
1300
1356
|
await atomicWriteFile(systemPromptSequencePath(this.directory), `${String(sequence)}
|
|
1301
1357
|
`);
|
|
1302
|
-
await atomicJson(
|
|
1358
|
+
await atomicJson(join3(systemPromptRecordsPath(this.directory), systemPromptRecordName(sequence)), { sessionId: entry.sessionId, attempt: entry.attempt, turn: entry.turn, sha256 });
|
|
1303
1359
|
}
|
|
1304
1360
|
async migrateSystemPrompts(legacy) {
|
|
1305
1361
|
await rm3(systemPromptStoragePath(this.directory), { recursive: true, force: true });
|
|
@@ -1331,14 +1387,14 @@ var RunStore = class _RunStore {
|
|
|
1331
1387
|
const recordSequence = Number(match[1]);
|
|
1332
1388
|
if (!Number.isSafeInteger(recordSequence) || recordSequence < 1) throw new Error("Persisted system-prompt records are invalid");
|
|
1333
1389
|
highest = Math.max(highest, recordSequence);
|
|
1334
|
-
const record2 = await json(
|
|
1390
|
+
const record2 = await json(join3(systemPromptRecordsPath(this.directory), name));
|
|
1335
1391
|
if (!object(record2)) throw new Error("Persisted system-prompt record is invalid");
|
|
1336
1392
|
const sessionId = record2.sessionId;
|
|
1337
1393
|
const attempt = record2.attempt;
|
|
1338
1394
|
const turn = record2.turn;
|
|
1339
1395
|
const sha256 = record2.sha256;
|
|
1340
1396
|
if (typeof sessionId !== "string" || !sessionId || !positiveInteger(attempt) || !positiveInteger(turn) || typeof sha256 !== "string" || !/^[0-9a-f]{64}$/.test(sha256)) throw new Error("Persisted system-prompt record is invalid");
|
|
1341
|
-
const prompt = await readFile3(
|
|
1397
|
+
const prompt = await readFile3(join3(systemPromptBodiesPath(this.directory), sha256), "utf8");
|
|
1342
1398
|
if (createHash2("sha256").update(prompt).digest("hex") !== sha256) throw new Error("Persisted system-prompt body is invalid");
|
|
1343
1399
|
entries.push({ sessionId, attempt, turn, sha256, prompt });
|
|
1344
1400
|
}
|
|
@@ -1361,7 +1417,7 @@ var RunStore = class _RunStore {
|
|
|
1361
1417
|
throw new Error("Persisted system prompts are invalid");
|
|
1362
1418
|
}
|
|
1363
1419
|
async #decodeJournal() {
|
|
1364
|
-
const journal = decodeJournal(await json(
|
|
1420
|
+
const journal = decodeJournal(await json(join3(this.directory, "journal.json")));
|
|
1365
1421
|
if (!journal) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted journal is invalid");
|
|
1366
1422
|
return journal;
|
|
1367
1423
|
}
|
|
@@ -1375,7 +1431,7 @@ var RunStore = class _RunStore {
|
|
|
1375
1431
|
const journal = await this.#decodeJournal();
|
|
1376
1432
|
journal.awaiting ??= {};
|
|
1377
1433
|
const result = await update(journal);
|
|
1378
|
-
await atomicJson(
|
|
1434
|
+
await atomicJson(join3(this.directory, "journal.json"), journal);
|
|
1379
1435
|
this.refreshSummaryBestEffort();
|
|
1380
1436
|
return result;
|
|
1381
1437
|
});
|
|
@@ -1485,10 +1541,10 @@ var RunStore = class _RunStore {
|
|
|
1485
1541
|
}
|
|
1486
1542
|
expectedWorktree(owner) {
|
|
1487
1543
|
const key = this.#worktreeKey(owner);
|
|
1488
|
-
return { path:
|
|
1544
|
+
return { path: join3(this.directory, "worktrees", key), branch: `pi-extensible-workflows/${safePart(this.runId)}/${key}` };
|
|
1489
1545
|
}
|
|
1490
1546
|
markerPath(owner) {
|
|
1491
|
-
return
|
|
1547
|
+
return join3(this.directory, `worktree-${this.#worktreeKey(owner)}.creating`);
|
|
1492
1548
|
}
|
|
1493
1549
|
namedWorktreeOwner(name) {
|
|
1494
1550
|
if (!name.trim()) throw new WorkflowError("WORKTREE_FAILED", "Named worktree names must be non-empty");
|
|
@@ -1531,13 +1587,13 @@ var RunStore = class _RunStore {
|
|
|
1531
1587
|
}
|
|
1532
1588
|
structuralWorktree(owner, record2) {
|
|
1533
1589
|
const expected = this.expectedWorktree(owner);
|
|
1534
|
-
const relativePath =
|
|
1535
|
-
const relativeCwd =
|
|
1536
|
-
if (record2.owner !== owner || resolve2(record2.path) !== expected.path || record2.branch !== expected.branch || relativePath === ".." || relativePath.startsWith(`..${
|
|
1590
|
+
const relativePath = relative2(this.directory, record2.path);
|
|
1591
|
+
const relativeCwd = relative2(record2.path, record2.cwd);
|
|
1592
|
+
if (record2.owner !== owner || resolve2(record2.path) !== expected.path || record2.branch !== expected.branch || relativePath === ".." || relativePath.startsWith(`..${sep2}`) || relativeCwd === ".." || relativeCwd.startsWith(`..${sep2}`)) throw new Error(`Invalid worktree record for ${owner}`);
|
|
1537
1593
|
return record2;
|
|
1538
1594
|
}
|
|
1539
1595
|
async #loadWorktreeRecords(missingOk = true) {
|
|
1540
|
-
const rawRecords = await json(
|
|
1596
|
+
const rawRecords = await json(join3(this.directory, "worktrees.json")).catch((error) => {
|
|
1541
1597
|
if (missingOk && isNodeError(error, "ENOENT")) return [];
|
|
1542
1598
|
throw error;
|
|
1543
1599
|
});
|
|
@@ -1547,7 +1603,7 @@ var RunStore = class _RunStore {
|
|
|
1547
1603
|
}
|
|
1548
1604
|
async borrowedWorktreeRecords(wait = true) {
|
|
1549
1605
|
if (wait) await this.borrowedWorktreeLane.run(async () => void 0);
|
|
1550
|
-
const rawRecords = await json(
|
|
1606
|
+
const rawRecords = await json(join3(this.directory, "borrowed-worktrees.json")).catch((error) => {
|
|
1551
1607
|
if (isNodeError(error, "ENOENT")) return [];
|
|
1552
1608
|
throw error;
|
|
1553
1609
|
});
|
|
@@ -1672,11 +1728,11 @@ var RunStore = class _RunStore {
|
|
|
1672
1728
|
const reference = this.structuralWorktree(owner, record2);
|
|
1673
1729
|
paths.add(resolve2(reference.path));
|
|
1674
1730
|
}
|
|
1675
|
-
const entries = await readdir2(
|
|
1731
|
+
const entries = await readdir2(join3(this.directory, "worktrees"), { withFileTypes: true }).catch((error) => {
|
|
1676
1732
|
if (isNodeError(error, "ENOENT")) return [];
|
|
1677
1733
|
throw error;
|
|
1678
1734
|
});
|
|
1679
|
-
for (const entry of entries) if (!entry.isDirectory() || entry.isSymbolicLink() || !paths.has(resolve2(
|
|
1735
|
+
for (const entry of entries) if (!entry.isDirectory() || entry.isSymbolicLink() || !paths.has(resolve2(join3(this.directory, "worktrees", entry.name)))) throw new Error(`Unrecorded worktree artifact: ${join3(this.directory, "worktrees", entry.name)}`);
|
|
1680
1736
|
} catch (error) {
|
|
1681
1737
|
throw coerceWorkflowError("WORKTREE_FAILED", error);
|
|
1682
1738
|
}
|
|
@@ -1728,7 +1784,7 @@ var RunStore = class _RunStore {
|
|
|
1728
1784
|
}
|
|
1729
1785
|
async cleanupOrphanWorktrees() {
|
|
1730
1786
|
const entries = await readdir2(this.directory).catch(() => []);
|
|
1731
|
-
for (const entry of entries.filter((name) => name.endsWith(".creating"))) await this.cleanupMarker(
|
|
1787
|
+
for (const entry of entries.filter((name) => name.endsWith(".creating"))) await this.cleanupMarker(join3(this.directory, entry));
|
|
1732
1788
|
}
|
|
1733
1789
|
async validateWorktree(owner, cwd) {
|
|
1734
1790
|
try {
|
|
@@ -1771,8 +1827,8 @@ var RunStore = class _RunStore {
|
|
|
1771
1827
|
try {
|
|
1772
1828
|
const root = (await git(this.cwd, ["rev-parse", "--show-toplevel"])).trim();
|
|
1773
1829
|
const [canonicalRoot, canonicalCwd] = await Promise.all([realpath(root), realpath(this.cwd)]);
|
|
1774
|
-
const launchRelative =
|
|
1775
|
-
if (launchRelative === ".." || launchRelative.startsWith(`..${
|
|
1830
|
+
const launchRelative = relative2(canonicalRoot, canonicalCwd);
|
|
1831
|
+
if (launchRelative === ".." || launchRelative.startsWith(`..${sep2}`)) throw new Error("launch cwd is outside the repository");
|
|
1776
1832
|
await this.cleanupMarker(markerPath);
|
|
1777
1833
|
const dirty = (await git(root, ["status", "--porcelain", "--untracked-files=normal"])).trim();
|
|
1778
1834
|
if (dirty) {
|
|
@@ -1788,7 +1844,7 @@ var RunStore = class _RunStore {
|
|
|
1788
1844
|
throw new Error("repository has no commits");
|
|
1789
1845
|
}
|
|
1790
1846
|
await mkdir2(dirname(path), { recursive: true, mode: 448 });
|
|
1791
|
-
const record2 = { owner, path, branch, cwd:
|
|
1847
|
+
const record2 = { owner, path, branch, cwd: join3(path, launchRelative), base: commit };
|
|
1792
1848
|
await atomicJson(markerPath, { owner, path, branch, base: commit });
|
|
1793
1849
|
markerWritten = true;
|
|
1794
1850
|
await git(root, ["branch", branch, commit]);
|
|
@@ -1833,7 +1889,7 @@ var RunStore = class _RunStore {
|
|
|
1833
1889
|
return;
|
|
1834
1890
|
}
|
|
1835
1891
|
records.push(binding);
|
|
1836
|
-
await atomicJson(
|
|
1892
|
+
await atomicJson(join3(this.directory, "borrowed-worktrees.json"), records);
|
|
1837
1893
|
});
|
|
1838
1894
|
await write;
|
|
1839
1895
|
}
|
|
@@ -1914,12 +1970,12 @@ var RunStore = class _RunStore {
|
|
|
1914
1970
|
return changed;
|
|
1915
1971
|
}
|
|
1916
1972
|
async saveResult(value) {
|
|
1917
|
-
const path =
|
|
1973
|
+
const path = join3(this.directory, "result.json");
|
|
1918
1974
|
await atomicPrettyJson(path, value);
|
|
1919
1975
|
return path;
|
|
1920
1976
|
}
|
|
1921
1977
|
async resultBytes() {
|
|
1922
|
-
return (await stat2(
|
|
1978
|
+
return (await stat2(join3(this.directory, "result.json"))).size;
|
|
1923
1979
|
}
|
|
1924
1980
|
async delete(confirmed) {
|
|
1925
1981
|
if (!confirmed) throw new WorkflowError("CANCELLED", "Run deletion requires confirmation");
|
|
@@ -1942,19 +1998,20 @@ var RunStore = class _RunStore {
|
|
|
1942
1998
|
|
|
1943
1999
|
// src/settings.ts
|
|
1944
2000
|
import { mkdirSync, readFileSync } from "node:fs";
|
|
1945
|
-
import {
|
|
1946
|
-
import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
|
|
1947
|
-
import { fileURLToPath } from "node:url";
|
|
2001
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
1948
2002
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
1949
|
-
|
|
2003
|
+
import { homedir as homedir3 } from "node:os";
|
|
2004
|
+
import { resolve as resolve3 } from "node:path";
|
|
2005
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2006
|
+
var WORKFLOW_DIRECTORY = "pi-extensible-workflows";
|
|
1950
2007
|
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
1951
2008
|
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
1952
|
-
return
|
|
2009
|
+
return join4(agentDir, WORKFLOW_DIRECTORY, "settings.json");
|
|
1953
2010
|
}
|
|
1954
2011
|
function normalizedResourcePath(value, settingsPath) {
|
|
1955
|
-
if (value === "*") return value;
|
|
1956
|
-
let expanded = value === "~" ? homedir3() : value.startsWith("~/") || value.startsWith("~\\") ?
|
|
1957
|
-
if (expanded.startsWith("file://")) expanded =
|
|
2012
|
+
if (value === "*" || value.startsWith("builtin:")) return value;
|
|
2013
|
+
let expanded = value === "~" ? homedir3() : value.startsWith("~/") || value.startsWith("~\\") ? join4(homedir3(), value.slice(2)) : value;
|
|
2014
|
+
if (expanded.startsWith("file://")) expanded = fileURLToPath2(expanded);
|
|
1958
2015
|
const resolved = resolve3(dirname2(settingsPath), expanded);
|
|
1959
2016
|
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
1960
2017
|
if (resourcePatternHasMagic(expanded)) {
|
|
@@ -2011,18 +2068,19 @@ function selectorsFromSettings(settings) {
|
|
|
2011
2068
|
function selectorSet(value) {
|
|
2012
2069
|
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2013
2070
|
}
|
|
2014
|
-
function validateContextFileScopes(value,
|
|
2071
|
+
function validateContextFileScopes(value, sourcePath) {
|
|
2015
2072
|
if (value === void 0) return void 0;
|
|
2016
|
-
if (!Array.isArray(value) || !value.every(isContextFileScope)) fail("INVALID_METADATA", `${
|
|
2073
|
+
if (!Array.isArray(value) || !value.every(isContextFileScope)) fail("INVALID_METADATA", `${sourcePath}.contextFiles must be an array containing only global, project, or cwd`);
|
|
2017
2074
|
return [...value];
|
|
2018
2075
|
}
|
|
2019
2076
|
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS") {
|
|
2020
2077
|
if (value === void 0) return void 0;
|
|
2078
|
+
if (!object(value) || !jsonValue(value)) fail(errorCode2, `${settingsPath}.extensionSettings must be a JSON object`);
|
|
2079
|
+
for (const namespace of Object.keys(value)) if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${settingsPath}.extensionSettings contains an invalid namespace: ${namespace}`);
|
|
2080
|
+
const generic = value;
|
|
2021
2081
|
const base = `${settingsPath}.extensionSettings`;
|
|
2022
|
-
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2023
2082
|
const normalized = {};
|
|
2024
|
-
for (const [namespace, raw] of Object.entries(
|
|
2025
|
-
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2083
|
+
for (const [namespace, raw] of Object.entries(generic)) {
|
|
2026
2084
|
if (namespace === "herdr") {
|
|
2027
2085
|
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2028
2086
|
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
@@ -2037,7 +2095,6 @@ function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "IN
|
|
|
2037
2095
|
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port } });
|
|
2038
2096
|
continue;
|
|
2039
2097
|
}
|
|
2040
|
-
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2041
2098
|
normalized[namespace] = structuredClone(raw);
|
|
2042
2099
|
}
|
|
2043
2100
|
return deepFreeze(normalized);
|
|
@@ -2064,7 +2121,7 @@ function parseSettings(path, partial) {
|
|
|
2064
2121
|
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2065
2122
|
}
|
|
2066
2123
|
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2067
|
-
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget"]]);
|
|
2124
|
+
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget", "codemodeTools"]]);
|
|
2068
2125
|
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2069
2126
|
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2070
2127
|
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
@@ -2072,6 +2129,8 @@ function parseSettings(path, partial) {
|
|
|
2072
2129
|
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2073
2130
|
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2074
2131
|
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2132
|
+
const codemodeTools = parsed.codemodeTools;
|
|
2133
|
+
if (codemodeTools !== void 0 && codemodeTools !== "all" && codemodeTools !== "read-only" && codemodeTools !== "none") fail("INVALID_SETTINGS", `${path}.codemodeTools must be "all", "read-only", or "none"`);
|
|
2075
2134
|
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2076
2135
|
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2077
2136
|
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
@@ -2082,6 +2141,7 @@ function parseSettings(path, partial) {
|
|
|
2082
2141
|
return Object.freeze({
|
|
2083
2142
|
...concurrency === void 0 ? {} : { concurrency },
|
|
2084
2143
|
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2144
|
+
...codemodeTools === void 0 ? {} : { codemodeTools },
|
|
2085
2145
|
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2086
2146
|
...skills === void 0 ? {} : { skills },
|
|
2087
2147
|
...extensions === void 0 ? {} : { extensions },
|
|
@@ -2094,46 +2154,38 @@ function parseSettings(path, partial) {
|
|
|
2094
2154
|
function loadSettings(path = workflowSettingsPath()) {
|
|
2095
2155
|
return parseSettings(path, false);
|
|
2096
2156
|
}
|
|
2157
|
+
var READ_ONLY_WORKFLOW_TOOLS = /* @__PURE__ */ new Set(["workflow_status", "workflow_catalog", "subagents_inspect"]);
|
|
2158
|
+
function workflowToolExposure(name, codemodeTools) {
|
|
2159
|
+
const readOnly = READ_ONLY_WORKFLOW_TOOLS.has(name);
|
|
2160
|
+
return { ...codemodeTools === "none" || codemodeTools === "read-only" && !readOnly ? { exposure: "model-only" } : {}, ...readOnly ? { annotations: { readOnlyHint: true } } : {} };
|
|
2161
|
+
}
|
|
2162
|
+
function loadCodemodeToolsSetting(agentDir) {
|
|
2163
|
+
try {
|
|
2164
|
+
return loadSettings(workflowSettingsPath(agentDir)).codemodeTools;
|
|
2165
|
+
} catch {
|
|
2166
|
+
return void 0;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2097
2169
|
function loadSettingsOverrides(path) {
|
|
2098
2170
|
return parseSettings(path, true);
|
|
2099
2171
|
}
|
|
2100
2172
|
function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2101
2173
|
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2102
|
-
const
|
|
2103
|
-
const
|
|
2104
|
-
const
|
|
2105
|
-
const
|
|
2106
|
-
const
|
|
2107
|
-
const projectSelectors = selectorsFromSettings(project);
|
|
2108
|
-
const effectiveSelectors = selectorSet({
|
|
2109
|
-
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2110
|
-
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2111
|
-
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2112
|
-
});
|
|
2113
|
-
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2114
|
-
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2115
|
-
const sources = {
|
|
2116
|
-
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2117
|
-
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2118
|
-
skills: sourceFor("skills"),
|
|
2119
|
-
extensions: sourceFor("extensions"),
|
|
2120
|
-
tools: sourceFor("tools"),
|
|
2121
|
-
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2122
|
-
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2123
|
-
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2124
|
-
};
|
|
2174
|
+
const consumerGlobal = loadSettings(globalSettingsPath);
|
|
2175
|
+
const consumerProject = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2176
|
+
const global = consumerGlobal;
|
|
2177
|
+
const project = consumerProject;
|
|
2178
|
+
const selectorSources = { global: selectorsFromSettings(global), project: selectorsFromSettings(project) };
|
|
2125
2179
|
const effective = Object.freeze({
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
...
|
|
2131
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2132
|
-
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2133
|
-
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2134
|
-
...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
|
|
2180
|
+
...global,
|
|
2181
|
+
...project,
|
|
2182
|
+
skills: [...global.skills ?? [], ...project.skills ?? []],
|
|
2183
|
+
extensions: [...global.extensions ?? [], ...project.extensions ?? []],
|
|
2184
|
+
...global.tools === void 0 && project.tools === void 0 ? {} : { tools: [...global.tools ?? [], ...project.tools ?? []] }
|
|
2135
2185
|
});
|
|
2136
|
-
|
|
2186
|
+
const sources = { concurrency: project.concurrency === void 0 ? globalSettingsPath : projectSettingsPath, modelAliases: project.modelAliases === void 0 ? globalSettingsPath : projectSettingsPath };
|
|
2187
|
+
for (const key of ["skills", "extensions", "tools", "extensionSettings", "retention", "worktreePostCreateCommand"]) if (effective[key] !== void 0) sources[key] = project[key] === void 0 ? globalSettingsPath : projectSettingsPath;
|
|
2188
|
+
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources, selectorSources };
|
|
2137
2189
|
}
|
|
2138
2190
|
function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
2139
2191
|
for (const name of names) {
|
|
@@ -2150,42 +2202,63 @@ function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = wo
|
|
|
2150
2202
|
const global = selectorSet(selectorsFromSettings(resolved.global));
|
|
2151
2203
|
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
2152
2204
|
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
2153
|
-
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources:
|
|
2205
|
+
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: resolved.selectorSources };
|
|
2206
|
+
}
|
|
2207
|
+
function canonicalExtensionSelector(selector, cwd) {
|
|
2208
|
+
const negated = selector.startsWith("!");
|
|
2209
|
+
return `${negated ? "!" : ""}${normalizedResourcePath(negated ? selector.slice(1) : selector, join4(cwd, "settings.json"))}`;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
// src/validation.ts
|
|
2213
|
+
var AGENT_OPTION_KEYS = /* @__PURE__ */ new Set(["label", "model", "tools", "excludeTools", "skills", "extensions", "contextFiles", "outputSchema", "retries", "timeoutMs"]);
|
|
2214
|
+
function isCoreAgentOption(key) {
|
|
2215
|
+
return AGENT_OPTION_KEYS.has(key);
|
|
2216
|
+
}
|
|
2217
|
+
function validateAgentOption(key, value) {
|
|
2218
|
+
switch (key) {
|
|
2219
|
+
case "label":
|
|
2220
|
+
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent label must be a non-empty string");
|
|
2221
|
+
break;
|
|
2222
|
+
case "model":
|
|
2223
|
+
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent model must be a non-empty string");
|
|
2224
|
+
assertModelThinking(value, "agent model");
|
|
2225
|
+
break;
|
|
2226
|
+
case "tools":
|
|
2227
|
+
case "skills":
|
|
2228
|
+
case "extensions":
|
|
2229
|
+
validateSelectorList(value, "agent options", key, "INVALID_METADATA", key !== "extensions");
|
|
2230
|
+
break;
|
|
2231
|
+
case "excludeTools":
|
|
2232
|
+
if (!Array.isArray(value) || value.some((tool) => typeof tool !== "string" || !tool.trim() || tool !== tool.trim() || tool.startsWith("!") || resourcePatternHasMagic(tool))) fail("INVALID_METADATA", "agent excludeTools must be an array of exact tool names");
|
|
2233
|
+
if (value.includes("workflow_result")) fail("INVALID_METADATA", "agent excludeTools cannot remove workflow_result");
|
|
2234
|
+
break;
|
|
2235
|
+
case "contextFiles":
|
|
2236
|
+
validateContextFileScopes(value, "agent options");
|
|
2237
|
+
break;
|
|
2238
|
+
case "outputSchema":
|
|
2239
|
+
validateSchema(value, "agent outputSchema");
|
|
2240
|
+
break;
|
|
2241
|
+
case "retries":
|
|
2242
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) fail("INVALID_METADATA", "agent retries must be a non-negative integer");
|
|
2243
|
+
break;
|
|
2244
|
+
case "timeoutMs":
|
|
2245
|
+
if (value !== null && !positiveInteger(value)) fail("INVALID_METADATA", "agent timeoutMs must be null or a positive integer");
|
|
2246
|
+
break;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
function validateAgentOptions(value) {
|
|
2250
|
+
if (!object(value) || !jsonValue(value)) fail("INVALID_METADATA", "agent options must be a JSON object");
|
|
2251
|
+
if (Object.prototype.hasOwnProperty.call(value, "thinking")) fail("INVALID_METADATA", "agent thinking is not supported; use model provider/model:thinking");
|
|
2252
|
+
for (const [key, option] of Object.entries(value)) if (AGENT_OPTION_KEYS.has(key)) validateAgentOption(key, option);
|
|
2253
|
+
return value;
|
|
2154
2254
|
}
|
|
2155
2255
|
|
|
2156
2256
|
// src/registry.ts
|
|
2157
|
-
|
|
2257
|
+
import { Value } from "typebox/value";
|
|
2258
|
+
var RESERVED_GLOBALS = /* @__PURE__ */ new Set(["agent", "shell", "tools", "prompt", "checkpoint", "parallel", "pipeline", "phase", "withWorktree", "log", "args", "Promise", "JSON", "Math", "Date", "eval", "Function", "WebAssembly", "process", "require", "module", "exports", "console", "fetch", "XMLHttpRequest", "WebSocket", "performance", "crypto", "setTimeout", "setInterval", "setImmediate", "queueMicrotask", "Intl", "SharedArrayBuffer", "Atomics", "globalThis", "global", "undefined", "NaN", "Infinity", "extensions", "workflow_catalog"]);
|
|
2158
2259
|
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
2159
2260
|
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
2160
2261
|
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
2161
|
-
function normalizeRoleDirectory(value) {
|
|
2162
|
-
try {
|
|
2163
|
-
if (value instanceof URL) {
|
|
2164
|
-
if (value.protocol !== "file:") fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
2165
|
-
return fileURLToPath2(value);
|
|
2166
|
-
}
|
|
2167
|
-
if (typeof value === "string") {
|
|
2168
|
-
if (value.startsWith("file:")) return fileURLToPath2(value);
|
|
2169
|
-
if (isAbsolute(value)) return value;
|
|
2170
|
-
}
|
|
2171
|
-
} catch (error) {
|
|
2172
|
-
fail("INVALID_METADATA", `Invalid workflow role directory: ${errorText(error)}`);
|
|
2173
|
-
}
|
|
2174
|
-
fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
2175
|
-
}
|
|
2176
|
-
function isBuiltinRoleDirectory(path) {
|
|
2177
|
-
const canonical = canonicalPath(path);
|
|
2178
|
-
const starterDirectory = dirname3(canonical);
|
|
2179
|
-
if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
|
|
2180
|
-
const distributionDirectory = dirname3(starterDirectory);
|
|
2181
|
-
const packageRoot2 = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
|
|
2182
|
-
try {
|
|
2183
|
-
const metadata = JSON.parse(readFileSync2(join4(packageRoot2, "package.json"), "utf8"));
|
|
2184
|
-
return object(metadata) && isCorePackageName(metadata.name);
|
|
2185
|
-
} catch {
|
|
2186
|
-
return false;
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
2262
|
function catalogSchema(schema, at) {
|
|
2190
2263
|
validateSchema(schema, at);
|
|
2191
2264
|
return structuredClone(schema);
|
|
@@ -2194,9 +2267,9 @@ var WorkflowRegistry = class {
|
|
|
2194
2267
|
#extensions = /* @__PURE__ */ new Set();
|
|
2195
2268
|
#functionSources = /* @__PURE__ */ new Map();
|
|
2196
2269
|
#globals = /* @__PURE__ */ new Map();
|
|
2270
|
+
#preparationHooks = /* @__PURE__ */ new Map();
|
|
2197
2271
|
#hooks = /* @__PURE__ */ new Map();
|
|
2198
2272
|
#agentAttemptActions = /* @__PURE__ */ new Map();
|
|
2199
|
-
#roleDirectories = /* @__PURE__ */ new Map();
|
|
2200
2273
|
#modelAliases = /* @__PURE__ */ new Map();
|
|
2201
2274
|
#settingsValidators = /* @__PURE__ */ new Set();
|
|
2202
2275
|
#frozen = false;
|
|
@@ -2216,9 +2289,10 @@ var WorkflowRegistry = class {
|
|
|
2216
2289
|
register(extension2) {
|
|
2217
2290
|
if (this.#frozen) fail("REGISTRY_FROZEN", "Workflow extension registration is closed after session_start");
|
|
2218
2291
|
if (object(extension2) && Object.prototype.hasOwnProperty.call(extension2, "workflows")) fail("INVALID_METADATA", "Separate registered workflow definitions were removed; register a function with input and output schemas instead");
|
|
2219
|
-
if (!object(extension2) || Object.keys(extension2).some((key) => !["version", "headline", "description", "source", "dependencies", "validateSettings", "functions", "modelAliases", "
|
|
2292
|
+
if (!object(extension2) || Object.keys(extension2).some((key) => !["version", "headline", "description", "source", "dependencies", "validateSettings", "functions", "modelAliases", "agentPreparationHooks", "agentSetupHooks", "agentAttemptActions"].includes(key)) || typeof extension2.version !== "string" || !SEMVER.test(extension2.version) || typeof extension2.headline !== "string" || !extension2.headline.trim()) fail("INVALID_METADATA", "Workflow extensions require a semantic version and non-empty headline");
|
|
2220
2293
|
const functions = extension2.functions ?? {};
|
|
2221
2294
|
const modelAliases = extension2.modelAliases ?? {};
|
|
2295
|
+
const agentPreparationHooks = extension2.agentPreparationHooks ?? {};
|
|
2222
2296
|
const agentSetupHooks = extension2.agentSetupHooks ?? {};
|
|
2223
2297
|
const agentAttemptActions = extension2.agentAttemptActions ?? {};
|
|
2224
2298
|
const source = extension2.source;
|
|
@@ -2228,10 +2302,7 @@ var WorkflowRegistry = class {
|
|
|
2228
2302
|
if (validateSettings !== void 0 && typeof validateSettings !== "function") fail("INVALID_METADATA", "Workflow extension validateSettings must be a function");
|
|
2229
2303
|
if (source !== void 0 && (typeof source !== "string" || !source.trim())) fail("INVALID_METADATA", "Workflow extension source must be a non-empty module URL");
|
|
2230
2304
|
if (dependencyValues !== void 0 && (!Array.isArray(dependencyValues) || dependencyValues.some((dependency) => typeof dependency !== "string" || dependency.trim() !== dependency || !PACKAGE_NAME.test(dependency)) || new Set(dependencyValues).size !== dependencyValues.length)) fail("INVALID_METADATA", "Workflow extension dependencies must be unique, non-empty package names");
|
|
2231
|
-
|
|
2232
|
-
if (!Array.isArray(roleDirectoryValues)) fail("INVALID_METADATA", "Workflow extension roleDirectories must be an array");
|
|
2233
|
-
const roleDirectories = [...new Set(Array.from(roleDirectoryValues, (value) => normalizeRoleDirectory(value)))];
|
|
2234
|
-
if (!object(functions) || !object(modelAliases) || !object(agentSetupHooks) || !object(agentAttemptActions) || Object.keys(functions).length === 0 && Object.keys(modelAliases).length === 0 && Object.keys(agentSetupHooks).length === 0 && Object.keys(agentAttemptActions).length === 0 && roleDirectories.length === 0 && validateSettings === void 0) fail("INVALID_METADATA", "Workflow extensions require functions, model aliases, agent setup hooks, agent attempt actions, role directories, or a settings validator");
|
|
2305
|
+
if (!object(functions) || !object(modelAliases) || !object(agentPreparationHooks) || !object(agentSetupHooks) || !object(agentAttemptActions) || Object.keys(functions).length === 0 && Object.keys(modelAliases).length === 0 && Object.keys(agentPreparationHooks).length === 0 && Object.keys(agentSetupHooks).length === 0 && Object.keys(agentAttemptActions).length === 0 && validateSettings === void 0) fail("INVALID_METADATA", "Workflow extensions require functions, model aliases, agent preparation hooks, agent setup hooks, agent attempt actions, or a settings validator");
|
|
2235
2306
|
const names = Object.keys(functions);
|
|
2236
2307
|
if (new Set(names).size !== names.length) fail("GLOBAL_COLLISION", "Global name collision inside extension");
|
|
2237
2308
|
for (const name of names) {
|
|
@@ -2250,6 +2321,11 @@ var WorkflowRegistry = class {
|
|
|
2250
2321
|
if (!object(alias) || Object.keys(alias).some((key) => key !== "resolve") || typeof alias.resolve !== "function") fail("INVALID_METADATA", `Invalid model alias resolver: ${name}`);
|
|
2251
2322
|
if (this.#modelAliases.has(name)) fail("DUPLICATE_NAME", `Model alias already registered: ${name}`);
|
|
2252
2323
|
}
|
|
2324
|
+
for (const [name, hook] of Object.entries(agentPreparationHooks)) {
|
|
2325
|
+
if (!IDENTIFIER.test(name) || !object(hook) || Object.keys(hook).some((key) => !["priority", "optionsSchema", "prepare"].includes(key)) || typeof hook.prepare !== "function" || hook.priority !== void 0 && (typeof hook.priority !== "number" || !Number.isFinite(hook.priority))) fail("INVALID_METADATA", `Invalid agent preparation hook: ${name}`);
|
|
2326
|
+
if (this.#preparationHooks.has(name)) fail("DUPLICATE_NAME", `Agent preparation hook already registered: ${name}`);
|
|
2327
|
+
if (hook.optionsSchema !== void 0) validateSchema(hook.optionsSchema, `${name} options schema`);
|
|
2328
|
+
}
|
|
2253
2329
|
for (const [name, hook] of Object.entries(agentSetupHooks)) {
|
|
2254
2330
|
if (!IDENTIFIER.test(name) || !object(hook) || Object.keys(hook).some((key) => !["priority", "setup"].includes(key)) || typeof hook.setup !== "function" || hook.priority !== void 0 && (typeof hook.priority !== "number" || !Number.isFinite(hook.priority))) fail("INVALID_METADATA", `Invalid agent setup hook: ${name}`);
|
|
2255
2331
|
if (this.#hooks.has(name)) fail("DUPLICATE_NAME", `Agent setup hook already registered: ${name}`);
|
|
@@ -2259,16 +2335,20 @@ var WorkflowRegistry = class {
|
|
|
2259
2335
|
if (action.visibleStandalone !== void 0 !== (action.runStandalone !== void 0)) fail("INVALID_METADATA", `Standalone agent attempt actions require visibleStandalone and runStandalone: ${name}`);
|
|
2260
2336
|
if (this.#agentAttemptActions.has(name)) fail("DUPLICATE_NAME", `Agent attempt action already registered: ${name}`);
|
|
2261
2337
|
}
|
|
2262
|
-
const stored = deepFreeze({ ...extension2, functions, modelAliases, agentSetupHooks, agentAttemptActions
|
|
2338
|
+
const stored = deepFreeze({ ...extension2, functions, modelAliases, agentPreparationHooks, agentSetupHooks, agentAttemptActions });
|
|
2263
2339
|
if (source !== void 0) for (const name of names) this.#functionSources.set(name, Object.freeze({ module: source, export: "default", dependencies: Object.freeze([...dependencies]) }));
|
|
2264
2340
|
this.#extensions.add(stored);
|
|
2265
|
-
for (const directory of roleDirectories) if (!this.#roleDirectories.has(directory)) this.#roleDirectories.set(directory, deepFreeze({ path: directory, extension: { version: extension2.version, headline: extension2.headline }, ...isBuiltinRoleDirectory(directory) ? { builtin: true } : {} }));
|
|
2266
2341
|
for (const name of names) this.#globals.set(name, name);
|
|
2267
2342
|
for (const [name, alias] of Object.entries(modelAliases)) this.#modelAliases.set(name, { name, version: extension2.version, headline: extension2.headline, resolve: alias.resolve });
|
|
2343
|
+
for (const [name, hook] of Object.entries(agentPreparationHooks)) this.#preparationHooks.set(name, { name, priority: hook.priority ?? 10, prepare: hook.prepare, ...hook.optionsSchema === void 0 ? {} : { optionsSchema: structuredClone(hook.optionsSchema) }, ...source === void 0 ? {} : { source } });
|
|
2268
2344
|
for (const [name, hook] of Object.entries(agentSetupHooks)) this.#hooks.set(name, { name, priority: hook.priority ?? 10, setup: hook.setup });
|
|
2269
2345
|
for (const [name, action] of Object.entries(agentAttemptActions)) this.#agentAttemptActions.set(name, action);
|
|
2270
2346
|
if (validateSettings !== void 0) this.#settingsValidators.add({ headline: extension2.headline, validate: validateSettings });
|
|
2271
2347
|
}
|
|
2348
|
+
/** Every registration's headline and declared module provenance, for hosts that must prove which extension made it. */
|
|
2349
|
+
registrations() {
|
|
2350
|
+
return [...this.#extensions].map(({ headline, source }) => Object.freeze({ headline, ...source === void 0 ? {} : { source } }));
|
|
2351
|
+
}
|
|
2272
2352
|
function(name) {
|
|
2273
2353
|
if (!IDENTIFIER.test(name)) fail("MISSING_WORKFLOW", `Registered functions require an unqualified name: ${name}`);
|
|
2274
2354
|
const fn = [...this.#extensions].find((extension2) => extension2.functions?.[name])?.functions?.[name];
|
|
@@ -2291,7 +2371,7 @@ var WorkflowRegistry = class {
|
|
|
2291
2371
|
let source = "global settings";
|
|
2292
2372
|
try {
|
|
2293
2373
|
const resolved = context ? resolveWorkflowSettings(context.cwd, context.projectTrusted, context.globalSettingsPath) : void 0;
|
|
2294
|
-
aliases = resolved?.effective.modelAliases ?? loadSettings().modelAliases;
|
|
2374
|
+
aliases = resolved?.effective.modelAliases ?? (context ? loadSettings().modelAliases : resolveWorkflowSettings(process.cwd(), false).effective.modelAliases);
|
|
2295
2375
|
if (resolved) {
|
|
2296
2376
|
source = resolved.projectTrusted && [resolved.sources.modelAliases, resolved.sources.skills, resolved.sources.extensions, resolved.sources.tools].some((path) => path === resolved.projectSettingsPath) ? "trusted project settings" : "global settings";
|
|
2297
2377
|
settings = { concurrency: resolved.effective.concurrency, backgroundWidget: resolved.effective.backgroundWidget ?? true, modelAliases: resolved.effective.modelAliases ?? {}, skills: resolved.effective.skills ?? [], extensions: Array.isArray(resolved.effective.extensions) ? resolved.effective.extensions : [], tools: resolved.effective.tools ?? [], ...resolved.effective.extensionSettings === void 0 ? {} : { extensionSettings: resolved.effective.extensionSettings }, ...resolved.effective.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: resolved.effective.worktreePostCreateCommand }, globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted: resolved.projectTrusted, sources: resolved.sources };
|
|
@@ -2350,7 +2430,7 @@ var WorkflowRegistry = class {
|
|
|
2350
2430
|
try {
|
|
2351
2431
|
validate(frozenSettings, Object.freeze({ ...context }));
|
|
2352
2432
|
} catch (error) {
|
|
2353
|
-
const location = context.source
|
|
2433
|
+
const location = context.source;
|
|
2354
2434
|
fail("INVALID_SETTINGS", `Invalid extension settings (${headline}; ${location}): ${errorText(error)}`);
|
|
2355
2435
|
}
|
|
2356
2436
|
}
|
|
@@ -2369,18 +2449,15 @@ var WorkflowRegistry = class {
|
|
|
2369
2449
|
journal.put(path, stored);
|
|
2370
2450
|
return structuredClone(stored);
|
|
2371
2451
|
}
|
|
2452
|
+
agentPreparationHooks() {
|
|
2453
|
+
return [...this.#preparationHooks.values()].sort(byPriorityThenName);
|
|
2454
|
+
}
|
|
2372
2455
|
agentSetupHooks() {
|
|
2373
2456
|
return [...this.#hooks.values()].sort(byPriorityThenName);
|
|
2374
2457
|
}
|
|
2375
2458
|
agentAttemptActions() {
|
|
2376
2459
|
return Object.freeze(Object.fromEntries(this.#agentAttemptActions.entries()));
|
|
2377
2460
|
}
|
|
2378
|
-
roleDirectories() {
|
|
2379
|
-
return [...this.#roleDirectories.keys()];
|
|
2380
|
-
}
|
|
2381
|
-
roleDirectoryRegistrations() {
|
|
2382
|
-
return [...this.#roleDirectories.values()];
|
|
2383
|
-
}
|
|
2384
2461
|
modelAliases() {
|
|
2385
2462
|
return [...this.#modelAliases.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
2386
2463
|
}
|
|
@@ -2436,10 +2513,10 @@ function createWorkflowRegistryApi(registry) {
|
|
|
2436
2513
|
},
|
|
2437
2514
|
modelAliases: () => registry.modelAliases(),
|
|
2438
2515
|
resolveModelAliases: (...args) => registry.resolveModelAliases(...args),
|
|
2439
|
-
|
|
2440
|
-
roleDirectoryRegistrations: () => registry.roleDirectoryRegistrations(),
|
|
2516
|
+
agentPreparationHooks: () => registry.agentPreparationHooks(),
|
|
2441
2517
|
agentSetupHooks: () => registry.agentSetupHooks(),
|
|
2442
|
-
agentAttemptActions: () => registry.agentAttemptActions()
|
|
2518
|
+
agentAttemptActions: () => registry.agentAttemptActions(),
|
|
2519
|
+
registrations: () => registry.registrations()
|
|
2443
2520
|
};
|
|
2444
2521
|
}
|
|
2445
2522
|
function workflowRegistryHost() {
|
|
@@ -2456,310 +2533,11 @@ function loadingRegistry() {
|
|
|
2456
2533
|
return workflowRegistryHost().api;
|
|
2457
2534
|
}
|
|
2458
2535
|
beginWorkflowExtensionLoading();
|
|
2459
|
-
function registeredWorkflowRoleDirectoryRegistrations() {
|
|
2460
|
-
const registrations = loadingRegistry().roleDirectoryRegistrations;
|
|
2461
|
-
return typeof registrations === "function" ? registrations() : [];
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
// src/roles.ts
|
|
2465
|
-
var ROLE_DIRECTORY2 = "pi-extensible-workflows";
|
|
2466
|
-
var roleDefinitionPaths = /* @__PURE__ */ new WeakMap();
|
|
2467
|
-
function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
2468
|
-
if (!strict) {
|
|
2469
|
-
if (!content.startsWith("---\n")) return { prompt: content };
|
|
2470
|
-
const end = content.indexOf("\n---", 4);
|
|
2471
|
-
if (end < 0) return { prompt: content };
|
|
2472
|
-
const meta = {};
|
|
2473
|
-
for (const line of content.slice(4, end).split("\n")) {
|
|
2474
|
-
const match = /^(model|tools|skills|extensions|description|overrideSystemPrompt|override_system_prompt|is_system_prompt|contextFiles|extensionSettings|disabledAgentResources|thinking)\s*:\s*(.+)$/.exec(line.trim());
|
|
2475
|
-
if (match?.[1] === "disabledAgentResources") fail("INVALID_METADATA", "disabledAgentResources is no longer supported; use skills, extensions, and tools selectors");
|
|
2476
|
-
if (match?.[1] === "thinking") fail("INVALID_METADATA", "Role thinking is not supported; put it on model as provider/model:thinking");
|
|
2477
|
-
if (match?.[1] && match[2]) meta[match[1]] = match[2].trim();
|
|
2478
|
-
}
|
|
2479
|
-
const unquote = (v) => v.replace(/^['"]|['"]$/g, "");
|
|
2480
|
-
const parseList = (value) => value === void 0 ? void 0 : value.replace(/^\[|\]$/g, "").split(",").map((entry) => unquote(entry.trim())).filter(Boolean);
|
|
2481
|
-
const tools2 = parseList(meta.tools);
|
|
2482
|
-
const skills2 = parseList(meta.skills);
|
|
2483
|
-
const extensions2 = parseList(meta.extensions);
|
|
2484
|
-
let extensionSettings2;
|
|
2485
|
-
if (meta.extensionSettings !== void 0) {
|
|
2486
|
-
try {
|
|
2487
|
-
extensionSettings2 = JSON.parse(meta.extensionSettings);
|
|
2488
|
-
} catch (error) {
|
|
2489
|
-
fail("INVALID_METADATA", `Invalid role extensionSettings: ${errorText(error)}`);
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
|
-
const rolePathValue2 = rolePath ?? "<role>";
|
|
2493
|
-
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA");
|
|
2494
|
-
const definition2 = { prompt: content.slice(end + 4).replace(/^\n/, "") };
|
|
2495
|
-
if (meta.model) {
|
|
2496
|
-
const model2 = unquote(meta.model);
|
|
2497
|
-
assertModelThinking(model2, "Role model");
|
|
2498
|
-
definition2.model = model2;
|
|
2499
|
-
}
|
|
2500
|
-
if (meta.description) definition2.description = unquote(meta.description);
|
|
2501
|
-
if (tools2) definition2.tools = tools2;
|
|
2502
|
-
if (skills2) definition2.skills = skills2;
|
|
2503
|
-
if (extensions2) definition2.extensions = extensions2;
|
|
2504
|
-
if (normalizedExtensionSettings2 !== void 0) definition2.extensionSettings = normalizedExtensionSettings2;
|
|
2505
|
-
const overrideSystemPrompt2 = meta.overrideSystemPrompt ?? meta.override_system_prompt ?? meta.is_system_prompt;
|
|
2506
|
-
const contextFiles2 = meta.contextFiles ? meta.contextFiles.replace(/^\[|\]$/g, "").split(",").map((scope) => unquote(scope.trim())).filter(Boolean) : void 0;
|
|
2507
|
-
const normalizedContextFiles2 = validateContextFileScopes(contextFiles2, "role");
|
|
2508
|
-
if (overrideSystemPrompt2) definition2.overrideSystemPrompt = overrideSystemPrompt2 === "true";
|
|
2509
|
-
if (normalizedContextFiles2) definition2.contextFiles = normalizedContextFiles2;
|
|
2510
|
-
return definition2;
|
|
2511
|
-
}
|
|
2512
|
-
const normalized = content.replace(/\r\n?/g, "\n");
|
|
2513
|
-
if (normalized.startsWith("---\n") && normalized.indexOf("\n---", 3) < 0) fail("INVALID_METADATA", "Role frontmatter is missing its closing delimiter");
|
|
2514
|
-
let parsed;
|
|
2515
|
-
try {
|
|
2516
|
-
parsed = parseFrontmatter(content);
|
|
2517
|
-
} catch (error) {
|
|
2518
|
-
fail("INVALID_METADATA", `Invalid role frontmatter: ${errorText(error)}`);
|
|
2519
|
-
}
|
|
2520
|
-
if (!object(parsed.frontmatter)) fail("INVALID_METADATA", "Role frontmatter must be an object");
|
|
2521
|
-
const { model, tools, skills, extensions, extensionSettings, description, contextFiles } = parsed.frontmatter;
|
|
2522
|
-
if (Object.prototype.hasOwnProperty.call(parsed.frontmatter, "disabledAgentResources")) fail("INVALID_METADATA", "disabledAgentResources is no longer supported; use skills, extensions, and tools selectors");
|
|
2523
|
-
if (Object.prototype.hasOwnProperty.call(parsed.frontmatter, "thinking")) fail("INVALID_METADATA", "Role thinking is not supported; put it on model as provider/model:thinking");
|
|
2524
|
-
const overrideSystemPrompt = parsed.frontmatter.overrideSystemPrompt ?? parsed.frontmatter.override_system_prompt ?? parsed.frontmatter.is_system_prompt;
|
|
2525
|
-
if (model !== void 0 && (typeof model !== "string" || model.trim() === "")) fail("INVALID_METADATA", "Role model must be a non-empty string");
|
|
2526
|
-
if (description !== void 0 && (typeof description !== "string" || description.trim() === "" || description.length > 1024 || /[\r\n]/.test(description))) fail("INVALID_METADATA", "Role description must be a non-empty single-line string of at most 1024 characters");
|
|
2527
|
-
if (overrideSystemPrompt !== void 0 && typeof overrideSystemPrompt !== "boolean") fail("INVALID_METADATA", "Role overrideSystemPrompt must be a boolean");
|
|
2528
|
-
const normalizedContextFiles = validateContextFileScopes(contextFiles, rolePath ?? "<role>");
|
|
2529
|
-
const rolePathValue = rolePath ?? "<role>";
|
|
2530
|
-
const normalizedTools = validateSelectorList(tools, rolePathValue, "tools", "INVALID_METADATA");
|
|
2531
|
-
const normalizedSkills = validateSelectorList(skills, rolePathValue, "skills", "INVALID_METADATA");
|
|
2532
|
-
const normalizedExtensions = validateSelectorList(extensions, rolePathValue, "extensions", "INVALID_METADATA");
|
|
2533
|
-
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA");
|
|
2534
|
-
const normalizedDescription = typeof description === "string" ? description.trim() : void 0;
|
|
2535
|
-
const normalizedModel = typeof model === "string" ? model.trim() : void 0;
|
|
2536
|
-
if (normalizedModel !== void 0) assertModelThinking(normalizedModel, "Role model");
|
|
2537
|
-
const definition = { prompt: parsed.body };
|
|
2538
|
-
if (normalizedDescription !== void 0) definition.description = normalizedDescription;
|
|
2539
|
-
if (normalizedModel !== void 0) definition.model = normalizedModel;
|
|
2540
|
-
if (normalizedTools !== void 0) definition.tools = normalizedTools;
|
|
2541
|
-
if (normalizedSkills !== void 0) definition.skills = normalizedSkills;
|
|
2542
|
-
if (normalizedExtensions !== void 0) definition.extensions = normalizedExtensions;
|
|
2543
|
-
if (normalizedExtensionSettings !== void 0) definition.extensionSettings = normalizedExtensionSettings;
|
|
2544
|
-
if (typeof overrideSystemPrompt === "boolean") definition.overrideSystemPrompt = overrideSystemPrompt;
|
|
2545
|
-
if (normalizedContextFiles !== void 0) definition.contextFiles = normalizedContextFiles;
|
|
2546
|
-
return definition;
|
|
2547
|
-
}
|
|
2548
|
-
function workflowRoleDirectories(agentDir = getAgentDir2()) {
|
|
2549
|
-
return [join5(agentDir, ROLE_DIRECTORY2, "roles")];
|
|
2550
|
-
}
|
|
2551
|
-
function projectRoleDirectories(root) {
|
|
2552
|
-
return [join5(root, ROLE_DIRECTORY2, "roles")];
|
|
2553
|
-
}
|
|
2554
|
-
function roleDirectorySources(dirs) {
|
|
2555
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2556
|
-
return dirs.flatMap((value) => {
|
|
2557
|
-
const source = typeof value === "string" ? { path: value } : { path: value.path, extension: value.extension, ...value.builtin === true ? { builtin: true } : {} };
|
|
2558
|
-
if (seen.has(source.path)) return [];
|
|
2559
|
-
seen.add(source.path);
|
|
2560
|
-
return [source];
|
|
2561
|
-
});
|
|
2562
|
-
}
|
|
2563
|
-
function roleDirectoryLabel(source, extension2 = true) {
|
|
2564
|
-
return source.extension ? `Extension "${source.extension.headline}" (${source.extension.version})` : extension2 ? "Registered workflow extension" : "Standard workflow";
|
|
2565
|
-
}
|
|
2566
|
-
function isRoleFile(dir, entry) {
|
|
2567
|
-
if (extname(entry.name) !== ".md") return false;
|
|
2568
|
-
if (entry.isFile()) return true;
|
|
2569
|
-
if (!entry.isSymbolicLink()) return false;
|
|
2570
|
-
try {
|
|
2571
|
-
return statSync(join5(dir, entry.name)).isFile();
|
|
2572
|
-
} catch (error) {
|
|
2573
|
-
if (isNodeError(error, "ENOENT")) return false;
|
|
2574
|
-
throw error;
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
function scanRoleFiles(dirs, extension2) {
|
|
2578
|
-
const files = [];
|
|
2579
|
-
for (const source of roleDirectorySources(dirs)) {
|
|
2580
|
-
let entries;
|
|
2581
|
-
try {
|
|
2582
|
-
entries = readdirSync(source.path, { withFileTypes: true });
|
|
2583
|
-
} catch (error) {
|
|
2584
|
-
if (!extension2 && isNodeError(error, "ENOENT")) continue;
|
|
2585
|
-
fail("INVALID_METADATA", `${roleDirectoryLabel(source, extension2)} role directory "${source.path}" could not be scanned: ${errorText(error)}`);
|
|
2586
|
-
}
|
|
2587
|
-
for (const entry of entries) if (isRoleFile(source.path, entry)) files.push({ name: basename3(entry.name, ".md"), path: join5(source.path, entry.name), source });
|
|
2588
|
-
}
|
|
2589
|
-
return files.sort((left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path));
|
|
2590
|
-
}
|
|
2591
|
-
function roleEntries(dirs, extension2) {
|
|
2592
|
-
const files = scanRoleFiles(dirs, extension2);
|
|
2593
|
-
const starterFiles = files.filter(({ source }) => source.builtin === true);
|
|
2594
|
-
const regularFiles = files.filter(({ source }) => source.builtin !== true);
|
|
2595
|
-
if (extension2) {
|
|
2596
|
-
const byName = /* @__PURE__ */ new Map();
|
|
2597
|
-
for (const file of regularFiles) byName.set(file.name, [...byName.get(file.name) ?? [], file]);
|
|
2598
|
-
for (const [name, matches] of byName) if (matches.length > 1) fail("INVALID_METADATA", `Duplicate extension role "${name}": ${matches.map(({ path, source }) => `${roleDirectoryLabel(source)} role directory "${source.path}" (${path})`).join("; ")}`);
|
|
2599
|
-
}
|
|
2600
|
-
const read = (roleFiles) => Object.fromEntries(roleFiles.map(({ name, path, source }) => {
|
|
2601
|
-
try {
|
|
2602
|
-
const definition = parseRoleMarkdown(readFileSync3(path, "utf8"), true, path);
|
|
2603
|
-
roleDefinitionPaths.set(definition, path);
|
|
2604
|
-
return [name, { name, path, source, definition }];
|
|
2605
|
-
} catch (error) {
|
|
2606
|
-
if (extension2) fail("INVALID_METADATA", `${roleDirectoryLabel(source)} role directory "${source.path}" contains invalid role "${name}" at "${path}": ${errorText(error)}`);
|
|
2607
|
-
throw error;
|
|
2608
|
-
}
|
|
2609
|
-
}));
|
|
2610
|
-
return extension2 ? { ...read(starterFiles), ...read(regularFiles) } : read(files);
|
|
2611
|
-
}
|
|
2612
|
-
function discoveryInput(input) {
|
|
2613
|
-
return { cwd: input.cwd, agentDir: input.agentDir ?? getAgentDir2(), projectTrusted: input.projectTrusted ?? true, extensionRoleDirectories: input.extensionRoleDirectories ?? registeredWorkflowRoleDirectoryRegistrations() };
|
|
2614
|
-
}
|
|
2615
|
-
function discoveredRoleEntries(input) {
|
|
2616
|
-
const resolved = discoveryInput(input);
|
|
2617
|
-
return {
|
|
2618
|
-
...roleEntries(resolved.extensionRoleDirectories, true),
|
|
2619
|
-
...roleEntries(workflowRoleDirectories(resolved.agentDir), false),
|
|
2620
|
-
...resolved.projectTrusted ? roleEntries(projectRoleDirectories(join5(resolved.cwd, ".pi")), false) : {}
|
|
2621
|
-
};
|
|
2622
|
-
}
|
|
2623
|
-
function discoverRoles(input) {
|
|
2624
|
-
return deepFreeze(Object.fromEntries(Object.entries(discoveredRoleEntries(input)).map(([name, entry]) => [name, entry.definition])));
|
|
2625
|
-
}
|
|
2626
|
-
function loadAgentDefinitions(cwd, agentDir = getAgentDir2(), projectTrusted = true, extensionRoleDirectories = registeredWorkflowRoleDirectoryRegistrations()) {
|
|
2627
|
-
return discoverRoles({ cwd, agentDir, projectTrusted, extensionRoleDirectories });
|
|
2628
|
-
}
|
|
2629
|
-
function canonicalExtensionSelector(selector, base = process.cwd()) {
|
|
2630
|
-
const negated = selector.startsWith("!");
|
|
2631
|
-
const body = negated ? selector.slice(1) : selector;
|
|
2632
|
-
if (body === "*" || body === "**" || body.startsWith("**/")) return selector;
|
|
2633
|
-
const resolved = resolve4(base, body);
|
|
2634
|
-
if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
|
|
2635
|
-
return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
|
|
2636
|
-
}
|
|
2637
|
-
function selectorsFromSettings2(settings) {
|
|
2638
|
-
return { ...settings.skills === void 0 ? {} : { skills: settings.skills }, ...settings.extensions === void 0 ? {} : { extensions: settings.extensions }, ...settings.tools === void 0 ? {} : { tools: settings.tools } };
|
|
2639
|
-
}
|
|
2640
|
-
function rootToolNames(value) {
|
|
2641
|
-
return value === void 0 ? void 0 : value instanceof Set ? [...value] : [...value];
|
|
2642
|
-
}
|
|
2643
|
-
function roleDefinitionFor(name, options) {
|
|
2644
|
-
if (name === void 0) return {};
|
|
2645
|
-
if (options.definition !== void 0) {
|
|
2646
|
-
const path = roleDefinitionPaths.get(options.definition);
|
|
2647
|
-
return { definition: options.definition, ...path === void 0 ? {} : { path } };
|
|
2648
|
-
}
|
|
2649
|
-
if (options.definitions?.[name] !== void 0) {
|
|
2650
|
-
const definition = options.definitions[name];
|
|
2651
|
-
const path = roleDefinitionPaths.get(definition);
|
|
2652
|
-
return { definition, ...path === void 0 ? {} : { path } };
|
|
2653
|
-
}
|
|
2654
|
-
const entries = discoveredRoleEntries(options);
|
|
2655
|
-
const entry = entries[name];
|
|
2656
|
-
if (!entry) fail("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${name}`);
|
|
2657
|
-
return entry;
|
|
2658
|
-
}
|
|
2659
|
-
function resolveRole(name, options) {
|
|
2660
|
-
const { definition, path } = roleDefinitionFor(name, options);
|
|
2661
|
-
const settings = options.selectorSources === void 0 ? resolveWorkflowSettings(options.cwd, options.projectTrusted ?? true, options.settingsPath ?? workflowSettingsPath(options.agentDir ?? getAgentDir2())) : void 0;
|
|
2662
|
-
const baseSources = options.selectorSources ?? { global: selectorsFromSettings2(settings?.global ?? {}), project: selectorsFromSettings2(settings?.project ?? {}) };
|
|
2663
|
-
const canonicalSources = (sources) => ({
|
|
2664
|
-
...sources,
|
|
2665
|
-
global: { ...sources.global, ...sources.global.extensions === void 0 ? {} : { extensions: sources.global.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
2666
|
-
project: { ...sources.project, ...sources.project.extensions === void 0 ? {} : { extensions: sources.project.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
2667
|
-
...sources.role === void 0 ? {} : { role: { ...sources.role, ...sources.role.extensions === void 0 ? {} : { extensions: sources.role.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } },
|
|
2668
|
-
...sources.call === void 0 ? {} : { call: { ...sources.call, ...sources.call.extensions === void 0 ? {} : { extensions: sources.call.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } }
|
|
2669
|
-
});
|
|
2670
|
-
const roleLayer = definition?.skills !== void 0 || definition?.extensions !== void 0 || definition?.tools !== void 0 ? { ...definition.skills === void 0 ? {} : { skills: definition.skills }, ...definition.extensions === void 0 ? {} : { extensions: definition.extensions.map((selector) => canonicalExtensionSelector(selector, path ? dirname4(path) : options.cwd)) }, ...definition.tools === void 0 ? {} : { tools: definition.tools } } : baseSources.role;
|
|
2671
|
-
const callLayer = options.skills !== void 0 || options.extensions !== void 0 || options.tools !== void 0 ? { ...options.skills === void 0 ? {} : { skills: options.skills }, ...options.extensions === void 0 ? {} : { extensions: options.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) }, ...options.tools === void 0 ? {} : { tools: options.tools } } : baseSources.call;
|
|
2672
|
-
const selectorSources = canonicalSources({ global: baseSources.global, project: baseSources.project, ...roleLayer === void 0 ? {} : { role: roleLayer }, ...callLayer === void 0 ? {} : { call: callLayer } });
|
|
2673
|
-
const callSelectors = selectorSources.call;
|
|
2674
|
-
const roleSelectors = selectorSources.role;
|
|
2675
|
-
const selectorLayers = {
|
|
2676
|
-
skills: [selectorSources.global.skills, selectorSources.project.skills, roleSelectors?.skills, callSelectors?.skills],
|
|
2677
|
-
extensions: [selectorSources.global.extensions, selectorSources.project.extensions, roleSelectors?.extensions, callSelectors?.extensions],
|
|
2678
|
-
tools: [selectorSources.global.tools, selectorSources.project.tools, roleSelectors?.tools, callSelectors?.tools]
|
|
2679
|
-
};
|
|
2680
|
-
const resources = options.resources ?? {};
|
|
2681
|
-
const candidateTools = options.inheritedTools ?? rootToolNames(options.rootTools) ?? resources.tools;
|
|
2682
|
-
const tools = options.effectiveTools ?? (candidateTools === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.tools, candidateTools));
|
|
2683
|
-
const rootTools = rootToolNames(options.rootTools);
|
|
2684
|
-
if (rootTools !== void 0) {
|
|
2685
|
-
const outsideEffectiveTool = options.effectiveTools?.find((tool) => !candidateTools?.includes(tool));
|
|
2686
|
-
if (outsideEffectiveTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideEffectiveTool}`);
|
|
2687
|
-
const outsideCallTool = (options.tools ?? []).find((tool) => !tool.startsWith("!") && !resourcePatternHasMagic(tool) && !candidateTools?.includes(tool));
|
|
2688
|
-
if (outsideCallTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideCallTool}`);
|
|
2689
|
-
const outsideRootTool = tools?.find((tool) => !rootTools.includes(tool));
|
|
2690
|
-
if (outsideRootTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideRootTool}`);
|
|
2691
|
-
}
|
|
2692
|
-
const selectedSkills = resources.skills === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.skills, resources.skills);
|
|
2693
|
-
const extensionResources = resources.extensions?.map((extension2) => canonicalPath(extension2));
|
|
2694
|
-
const selectedExtensions = extensionResources === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.extensions, extensionResources);
|
|
2695
|
-
const unmatchedSkills = resources.skills === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.skills.flatMap((layer) => layer ?? []), resources.skills);
|
|
2696
|
-
const unmatchedExtensions = extensionResources === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.extensions.flatMap((layer) => layer ?? []), extensionResources);
|
|
2697
|
-
const unmatchedTools = candidateTools === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.tools.flatMap((layer) => layer ?? []), candidateTools);
|
|
2698
|
-
const aliases = options.modelAliases ?? settings?.effective.modelAliases ?? {};
|
|
2699
|
-
const requestedModel = options.model ?? (definition?.model && definition.thinking && !definition.model.includes(":") ? `${definition.model}:${definition.thinking}` : definition?.model);
|
|
2700
|
-
const blockedAlias = requestedModel?.split(":", 1)[0];
|
|
2701
|
-
if (requestedModel !== void 0 && blockedAlias && options.blockedAliases?.has(blockedAlias) && !modelAliasName(requestedModel, aliases)) {
|
|
2702
|
-
const target = options.blockedAliasTargets?.[blockedAlias];
|
|
2703
|
-
fail("UNKNOWN_MODEL", `Unknown model alias ${requestedModel}${target ? ` resolved to ${target}` : ""}${options.settingsPath ? ` (settings: ${options.settingsPath})` : ""}`);
|
|
2704
|
-
}
|
|
2705
|
-
let model = options.modelOverride;
|
|
2706
|
-
if (model === void 0 && requestedModel !== void 0) {
|
|
2707
|
-
assertModelThinking(requestedModel);
|
|
2708
|
-
model = resolveModelReference(requestedModel, aliases, options.knownModels ?? options.availableModels, options.settingsPath ?? settings?.sources.modelAliases);
|
|
2709
|
-
}
|
|
2710
|
-
if (model === void 0) model = options.rootModel;
|
|
2711
|
-
const availableModels = options.knownModels ?? options.availableModels ?? (options.rootModel === void 0 ? void 0 : /* @__PURE__ */ new Set([modelCapability(options.rootModel)]));
|
|
2712
|
-
if (model !== void 0 && availableModels !== void 0 && !availableModels.has(modelCapability(model))) fail("UNKNOWN_MODEL", `Unknown model${requestedModel ? ` ${requestedModel} resolved to ${modelCapability(model)}` : ""}${options.settingsPath ? ` (settings: ${options.settingsPath})` : ""}`);
|
|
2713
|
-
const prompt = definition?.prompt ?? "";
|
|
2714
|
-
const overrideSystemPrompt = definition?.overrideSystemPrompt === true;
|
|
2715
|
-
const contextFiles = options.contextFiles ?? definition?.contextFiles;
|
|
2716
|
-
return { ...name === void 0 ? {} : { name }, ...definition === void 0 ? {} : { definition }, ...model === void 0 ? {} : { model }, ...modelAliasName(requestedModel ?? "", aliases) && requestedModel ? { requestedModel } : {}, ...tools === void 0 ? {} : { tools }, prompt, overrideSystemPrompt, systemPrompt: { mode: overrideSystemPrompt ? "override" : "append", text: prompt }, ...contextFiles === void 0 ? {} : { contextFiles: [...contextFiles] }, ...definition?.extensionSettings === void 0 ? {} : { extensionSettings: definition.extensionSettings }, selectorSources, selectorLayers, ...selectedSkills === void 0 ? {} : { selectedSkills }, ...selectedExtensions === void 0 ? {} : { selectedExtensions }, ...unmatchedSkills === void 0 ? {} : { unmatchedSkills }, ...unmatchedExtensions === void 0 ? {} : { unmatchedExtensions }, ...unmatchedTools === void 0 ? {} : { unmatchedTools } };
|
|
2717
|
-
}
|
|
2718
|
-
|
|
2719
|
-
// src/validation.ts
|
|
2720
|
-
var AGENT_OPTION_KEYS = /* @__PURE__ */ new Set(["label", "model", "tools", "skills", "extensions", "contextFiles", "role", "outputSchema", "retries", "timeoutMs"]);
|
|
2721
|
-
function validateAgentOption(key, value, aliases, knownModels, settingsPath) {
|
|
2722
|
-
switch (key) {
|
|
2723
|
-
case "label":
|
|
2724
|
-
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent label must be a non-empty string");
|
|
2725
|
-
break;
|
|
2726
|
-
case "model":
|
|
2727
|
-
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent model must be a non-empty string");
|
|
2728
|
-
assertModelThinking(value, "agent model");
|
|
2729
|
-
if (aliases !== void 0) resolveModelReference(value, aliases, knownModels, settingsPath);
|
|
2730
|
-
break;
|
|
2731
|
-
case "tools":
|
|
2732
|
-
case "skills":
|
|
2733
|
-
case "extensions":
|
|
2734
|
-
validateSelectorList(value, "agent options", key, "INVALID_METADATA", key !== "extensions");
|
|
2735
|
-
break;
|
|
2736
|
-
case "contextFiles":
|
|
2737
|
-
validateContextFileScopes(value, "agent options");
|
|
2738
|
-
break;
|
|
2739
|
-
case "role":
|
|
2740
|
-
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent role must be a non-empty string");
|
|
2741
|
-
break;
|
|
2742
|
-
case "outputSchema":
|
|
2743
|
-
validateSchema(value, "agent outputSchema");
|
|
2744
|
-
break;
|
|
2745
|
-
case "retries":
|
|
2746
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) fail("INVALID_METADATA", "agent retries must be a non-negative integer");
|
|
2747
|
-
break;
|
|
2748
|
-
case "timeoutMs":
|
|
2749
|
-
if (value !== null && !positiveInteger(value)) fail("INVALID_METADATA", "agent timeoutMs must be null or a positive integer");
|
|
2750
|
-
break;
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
function validateAgentOptions(value) {
|
|
2754
|
-
if (!object(value) || !jsonValue(value)) fail("INVALID_METADATA", "agent options must be a JSON object");
|
|
2755
|
-
if (Object.prototype.hasOwnProperty.call(value, "thinking")) fail("INVALID_METADATA", "agent thinking is not supported; use model provider/model:thinking");
|
|
2756
|
-
for (const [key, option] of Object.entries(value)) if (AGENT_OPTION_KEYS.has(key)) validateAgentOption(key, option);
|
|
2757
|
-
return value;
|
|
2758
|
-
}
|
|
2759
2536
|
|
|
2760
2537
|
// src/execution.ts
|
|
2761
2538
|
var RPC_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
2762
2539
|
var HEARTBEAT_INTERVAL_MS = 1e3;
|
|
2540
|
+
var FUNCTION_HANDLE_PREFIX = "function-handle";
|
|
2763
2541
|
var WORK_RESULT_BRAND = "__workResult";
|
|
2764
2542
|
var childSource = String.raw`
|
|
2765
2543
|
"use strict";
|
|
@@ -2870,15 +2648,13 @@ const internalAgent = (...values) => {
|
|
|
2870
2648
|
void result.then(() => agentInflight.delete(occurrenceKey), () => agentInflight.delete(occurrenceKey));
|
|
2871
2649
|
return guardedAgentResult(result);
|
|
2872
2650
|
};
|
|
2873
|
-
const AGENT_HANDLE_KEYS = ["name", "role", "model", "tools", "skills", "extensions", "contextFiles", "label"];
|
|
2874
2651
|
const agentHandleNames = new Set();
|
|
2875
2652
|
const agentHandleInflight = new Set();
|
|
2876
2653
|
const internalAgentCreate = (...values) => {
|
|
2877
2654
|
if (values.length !== 1 || !values[0] || typeof values[0] !== "object" || Array.isArray(values[0])) throw workError("INVALID_METADATA", "agent.create requires one options object");
|
|
2878
2655
|
const options = values[0];
|
|
2879
2656
|
const name = named(options.name, "agent.create");
|
|
2880
|
-
|
|
2881
|
-
if (unsupported) throw workError("INVALID_METADATA", "agent.create option is unsupported: " + unsupported);
|
|
2657
|
+
if (name.startsWith("${FUNCTION_HANDLE_PREFIX}/")) throw workError("INVALID_METADATA", "Agent handle names may not start with ${FUNCTION_HANDLE_PREFIX}/");
|
|
2882
2658
|
if (agentHandleNames.has(name)) throw workError("INVALID_METADATA", "Duplicate agent handle name: " + name);
|
|
2883
2659
|
agentHandleNames.add(name);
|
|
2884
2660
|
const config = Object.freeze(Object.fromEntries(Object.entries(options).filter(([key]) => key !== "name")));
|
|
@@ -2993,6 +2769,30 @@ const functions = Object.freeze(Object.fromEntries(Object.entries(config.functio
|
|
|
2993
2769
|
Object.defineProperty(result, "toJSON", { value() { throw workError("INVALID_METADATA", "Workflow function result is a Promise; await it before serialization"); } });
|
|
2994
2770
|
return result;
|
|
2995
2771
|
}])));
|
|
2772
|
+
// tools.<id>(args) runs a Pi session tool through the host. Any identifier resolves, so a resumed run
|
|
2773
|
+
// replays journaled calls even when the tool is gone; the host rejects identifiers it cannot call.
|
|
2774
|
+
// A call is the n-th call with the same arguments in its scope, so replay does not depend on the
|
|
2775
|
+
// order in which concurrent calls start.
|
|
2776
|
+
const NOT_TOOLS = new Set(["then", "toJSON", "toString", "valueOf", "constructor"]);
|
|
2777
|
+
const sortedJson = value => Array.isArray(value) ? value.map(sortedJson) : value && typeof value === "object" ? Object.fromEntries(Object.keys(value).sort().map(key => [key, sortedJson(value[key])])) : value;
|
|
2778
|
+
const toolOccurrences = new Map();
|
|
2779
|
+
const toolCall = id => (...values) => {
|
|
2780
|
+
if (worktreeOwners.getStore()) throw workError("INVALID_METADATA", "tools." + id + " is unavailable inside withWorktree: session tools resolve paths against the session working directory");
|
|
2781
|
+
if (values.length > 1 || values[0] !== undefined && (!values[0] || typeof values[0] !== "object" || Array.isArray(values[0]))) throw workError("INVALID_METADATA", "tools." + id + " takes one arguments object");
|
|
2782
|
+
const args = values[0] === undefined ? {} : values[0];
|
|
2783
|
+
const structuralPath = inheritedAgentPath.getStore() || [];
|
|
2784
|
+
const key = JSON.stringify([structuralPath, id, sortedJson(args)]);
|
|
2785
|
+
const occurrence = (toolOccurrences.get(key) || 0) + 1;
|
|
2786
|
+
toolOccurrences.set(key, occurrence);
|
|
2787
|
+
return guardedResult(rpc("tool", [id, args, { structuralPath: [...structuralPath], occurrence }]), "tool");
|
|
2788
|
+
};
|
|
2789
|
+
const tools = new Proxy(Object.create(null), {
|
|
2790
|
+
get: (_target, key) => typeof key === "string" && !NOT_TOOLS.has(key) ? toolCall(key) : undefined,
|
|
2791
|
+
// Probing would branch on the session that resumed the run; the launch already checks named tools.
|
|
2792
|
+
has: () => { throw workError("INVALID_METADATA", "Workflow scripts cannot probe tools with 'in'; call tools.<name>(...) directly, and the launch checks that the tool exists"); },
|
|
2793
|
+
set: () => false,
|
|
2794
|
+
defineProperty: () => false,
|
|
2795
|
+
});
|
|
2996
2796
|
const recordEntries = (value, kind) => {
|
|
2997
2797
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw workError("INVALID_METADATA", kind + " must be a record");
|
|
2998
2798
|
return Object.entries(value);
|
|
@@ -3053,7 +2853,7 @@ const pipeline = async (operationName, items, stages) => {
|
|
|
3053
2853
|
return Object.fromEntries(results.map(result => [result.name, result.value]));
|
|
3054
2854
|
};
|
|
3055
2855
|
const safeMath = Object.fromEntries(Object.getOwnPropertyNames(Math).filter(name => name !== "random").map(name => [name, Math[name]]));
|
|
3056
|
-
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
2856
|
+
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, tools, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
3057
2857
|
for (const [name, fn] of Object.entries(functions)) Object.defineProperty(sandbox, name, { value: fn, writable: false, configurable: false });
|
|
3058
2858
|
for (const name of ["Date","eval","Function","WebAssembly","process","require","module","exports","console","fetch","XMLHttpRequest","WebSocket","performance","crypto","setTimeout","setInterval","setImmediate","queueMicrotask","Intl","SharedArrayBuffer","Atomics"]) sandbox[name] = undefined;
|
|
3059
2859
|
const context = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
|
|
@@ -3063,20 +2863,28 @@ Promise.resolve().then(() => new vm.Script("(async(__pi_extensible_workflows_age
|
|
|
3063
2863
|
.catch(error => send({ type: "error", error: workerError(error) }))
|
|
3064
2864
|
.finally(() => clearInterval(heartbeat));
|
|
3065
2865
|
`;
|
|
2866
|
+
function agentHandleTurnPath(handle, turn) {
|
|
2867
|
+
return structuralPath("agent", "handle", handle, `turn:${String(turn)}`);
|
|
2868
|
+
}
|
|
2869
|
+
function agentIdentityPath(identity) {
|
|
2870
|
+
if (identity.handle !== void 0 && identity.turn !== void 0) return agentHandleTurnPath(identity.handle, identity.turn);
|
|
2871
|
+
return structuralPath("agent", ...identity.structuralPath, `callsite:${identity.callSite}`, `occurrence:${String(identity.occurrence)}`);
|
|
2872
|
+
}
|
|
3066
2873
|
|
|
3067
2874
|
// src/host.ts
|
|
3068
2875
|
import { Type as Type3 } from "@earendil-works/pi-ai";
|
|
3069
|
-
import { copyToClipboard, getAgentDir as
|
|
2876
|
+
import { copyToClipboard, getAgentDir as getAgentDir4, ModelSelectorComponent } from "@earendil-works/pi-coding-agent";
|
|
3070
2877
|
|
|
3071
2878
|
// src/agent-execution.ts
|
|
3072
|
-
import { existsSync,
|
|
2879
|
+
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
3073
2880
|
import { copyFile } from "node:fs/promises";
|
|
3074
2881
|
import { homedir as homedir4 } from "node:os";
|
|
3075
|
-
import { basename as
|
|
2882
|
+
import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve4 } from "node:path";
|
|
3076
2883
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
3077
2884
|
import { Type } from "@earendil-works/pi-ai";
|
|
2885
|
+
import { Value as Value2 } from "typebox/value";
|
|
3078
2886
|
import { Compile } from "typebox/compile";
|
|
3079
|
-
import { createAgentSession, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as
|
|
2887
|
+
import { createAgentSession, createCodemodeExtension, createMcpExtension, createToolSearchExtension, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as getAgentDir2, ModelRuntime, SessionManager, SettingsManager, loadSkills } from "@earendil-works/pi-coding-agent";
|
|
3080
2888
|
|
|
3081
2889
|
// src/pi-runtime-adapter.ts
|
|
3082
2890
|
var TURN_START_EVENTS = /* @__PURE__ */ new Set(["turn_start", "turn_started", "turnStarted"]);
|
|
@@ -3233,14 +3041,14 @@ function createRuntimeHandoffAdapter(handoff) {
|
|
|
3233
3041
|
function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
3234
3042
|
const neutralHandoff = createRuntimeHandoffAdapter(handoff);
|
|
3235
3043
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
3236
|
-
let
|
|
3044
|
+
let activity;
|
|
3237
3045
|
let streamText = "";
|
|
3238
3046
|
let lastEventAt;
|
|
3239
3047
|
let lastReportedEventAt;
|
|
3240
3048
|
let disposed = false;
|
|
3241
3049
|
let toolCallsView = [];
|
|
3242
3050
|
const progressAt = (persist, stateOverride, toolCallsValue3, activityValue2, eventAtValue) => ({ usage: runtimeUsage(session), toolCalls: toolCallsValue3, state: stateOverride ?? stateFromSession(session), ...activityValue2 === void 0 ? {} : { activity: activityValue2 }, ...eventAtValue === void 0 ? {} : { lastEventAt: eventAtValue }, persist });
|
|
3243
|
-
const snapshot = (persist) => progressAt(persist, void 0, [...toolCalls.values()],
|
|
3051
|
+
const snapshot = (persist) => progressAt(persist, void 0, [...toolCalls.values()], activity, lastEventAt);
|
|
3244
3052
|
return {
|
|
3245
3053
|
handoff: neutralHandoff,
|
|
3246
3054
|
observe(event) {
|
|
@@ -3251,7 +3059,7 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3251
3059
|
const eventState = event.state === void 0 ? void 0 : runtimeState(event.state);
|
|
3252
3060
|
let report = false;
|
|
3253
3061
|
let persist = false;
|
|
3254
|
-
const previousActivity =
|
|
3062
|
+
const previousActivity = activity;
|
|
3255
3063
|
let eventToolCalls = toolCallsView;
|
|
3256
3064
|
if (event.type === "state_changed") {
|
|
3257
3065
|
report = true;
|
|
@@ -3259,7 +3067,7 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3259
3067
|
}
|
|
3260
3068
|
if (event.type === "message_start" && event.message?.role === "assistant") {
|
|
3261
3069
|
streamText = "";
|
|
3262
|
-
|
|
3070
|
+
activity = { kind: "text", text: "" };
|
|
3263
3071
|
report = true;
|
|
3264
3072
|
}
|
|
3265
3073
|
if (event.type === "message_update") {
|
|
@@ -3267,14 +3075,14 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3267
3075
|
if (updateType === "thinking_start" || updateType === "text_start") streamText = "";
|
|
3268
3076
|
const delta = event.assistantMessageEvent?.delta;
|
|
3269
3077
|
if ((updateType === "thinking_delta" || updateType === "text_delta") && delta) streamText = `${streamText}${delta}`.replace(/\s+/g, " ").slice(-MAX_ACTIVITY_TEXT_CHARS);
|
|
3270
|
-
if (updateType && ["thinking_start", "thinking_delta", "thinking_end"].includes(updateType))
|
|
3271
|
-
else if (updateType && ["text_start", "text_delta", "text_end", "toolcall_start", "toolcall_delta", "toolcall_end"].includes(updateType))
|
|
3272
|
-
report = previousActivity?.kind !==
|
|
3078
|
+
if (updateType && ["thinking_start", "thinking_delta", "thinking_end"].includes(updateType)) activity = { kind: "reasoning", text: streamText };
|
|
3079
|
+
else if (updateType && ["text_start", "text_delta", "text_end", "toolcall_start", "toolcall_delta", "toolcall_end"].includes(updateType)) activity = { kind: "text", text: streamText };
|
|
3080
|
+
report = previousActivity?.kind !== activity?.kind;
|
|
3273
3081
|
}
|
|
3274
3082
|
if (event.type === "message_end") {
|
|
3275
3083
|
streamText = "";
|
|
3276
|
-
|
|
3277
|
-
report = !sameActivity(previousActivity,
|
|
3084
|
+
activity = void 0;
|
|
3085
|
+
report = !sameActivity(previousActivity, activity);
|
|
3278
3086
|
if (event.message?.role === "assistant") {
|
|
3279
3087
|
persist = true;
|
|
3280
3088
|
report = true;
|
|
@@ -3283,24 +3091,24 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3283
3091
|
if (event.type === "tool_execution_start" && event.toolCallId && event.toolName) {
|
|
3284
3092
|
toolCalls.set(event.toolCallId, { id: event.toolCallId, name: event.toolName, state: "running" });
|
|
3285
3093
|
toolCallsView = [...toolCalls.values()];
|
|
3286
|
-
|
|
3094
|
+
activity = { kind: "tool", text: event.toolName };
|
|
3287
3095
|
report = true;
|
|
3288
3096
|
}
|
|
3289
3097
|
if (event.type === "tool_execution_update" && event.toolName) {
|
|
3290
|
-
|
|
3291
|
-
report = !sameActivity(previousActivity,
|
|
3098
|
+
activity = { kind: "tool", text: event.toolName };
|
|
3099
|
+
report = !sameActivity(previousActivity, activity);
|
|
3292
3100
|
}
|
|
3293
3101
|
let removeToolCallId;
|
|
3294
3102
|
if (event.type === "tool_execution_end" && event.toolCallId && event.toolName) {
|
|
3295
3103
|
toolCalls.set(event.toolCallId, { id: event.toolCallId, name: event.toolName, state: event.isError ? "failed" : "completed" });
|
|
3296
3104
|
eventToolCalls = [...toolCalls.values()];
|
|
3297
|
-
if (
|
|
3105
|
+
if (activity?.kind === "tool" && activity.text === event.toolName) activity = void 0;
|
|
3298
3106
|
report = true;
|
|
3299
3107
|
removeToolCallId = event.toolCallId;
|
|
3300
3108
|
}
|
|
3301
3109
|
if (removeToolCallId === void 0) eventToolCalls = toolCallsView;
|
|
3302
3110
|
if (!report && (lastReportedEventAt === void 0 || eventAt - lastReportedEventAt >= 1e3)) report = true;
|
|
3303
|
-
const eventActivity =
|
|
3111
|
+
const eventActivity = activity;
|
|
3304
3112
|
let materialized;
|
|
3305
3113
|
const result = { event, get progress() {
|
|
3306
3114
|
return materialized ??= progressAt(persist, eventState, eventToolCalls, eventActivity, eventAt);
|
|
@@ -3317,7 +3125,7 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3317
3125
|
disposed = true;
|
|
3318
3126
|
toolCalls.clear();
|
|
3319
3127
|
toolCallsView = [];
|
|
3320
|
-
|
|
3128
|
+
activity = void 0;
|
|
3321
3129
|
}
|
|
3322
3130
|
};
|
|
3323
3131
|
}
|
|
@@ -3351,8 +3159,8 @@ function createLiveSessionHandoff() {
|
|
|
3351
3159
|
let request;
|
|
3352
3160
|
const waitForBoundary = () => {
|
|
3353
3161
|
if (!turnActive) return Promise.resolve();
|
|
3354
|
-
boundary ??= new Promise((
|
|
3355
|
-
resolveBoundary =
|
|
3162
|
+
boundary ??= new Promise((resolve5) => {
|
|
3163
|
+
resolveBoundary = resolve5;
|
|
3356
3164
|
});
|
|
3357
3165
|
return boundary;
|
|
3358
3166
|
};
|
|
@@ -3384,11 +3192,11 @@ function createLiveSessionHandoff() {
|
|
|
3384
3192
|
if (request) return request;
|
|
3385
3193
|
if (isReleased()) return;
|
|
3386
3194
|
state = "handoff-pending";
|
|
3387
|
-
takeover = new Promise((
|
|
3388
|
-
resolveTakeover =
|
|
3195
|
+
takeover = new Promise((resolve5) => {
|
|
3196
|
+
resolveTakeover = resolve5;
|
|
3389
3197
|
});
|
|
3390
|
-
resume = new Promise((
|
|
3391
|
-
resolveResume =
|
|
3198
|
+
resume = new Promise((resolve5) => {
|
|
3199
|
+
resolveResume = resolve5;
|
|
3392
3200
|
});
|
|
3393
3201
|
request = (async () => {
|
|
3394
3202
|
await waitForBoundary();
|
|
@@ -3587,9 +3395,9 @@ var PiRuntimeAgentRunner = class {
|
|
|
3587
3395
|
let structuredResult;
|
|
3588
3396
|
const resultTool = prepared.resultTool;
|
|
3589
3397
|
if (!resultTool) throw new WorkflowError("INTERNAL_ERROR", "Workflow agent session is missing its workflow_result tool");
|
|
3590
|
-
const preparedForSession = preparedForRuntimeRequest(prepared, request.customTools, request.signal, (value) => {
|
|
3398
|
+
const preparedForSession = Object.freeze({ ...preparedForRuntimeRequest(prepared, request.customTools, request.signal, (value) => {
|
|
3591
3399
|
structuredResult = value;
|
|
3592
|
-
}, resultTool);
|
|
3400
|
+
}, resultTool), ...request.turnControl ? { turnControl: request.turnControl } : {} });
|
|
3593
3401
|
const callbacks = this.#options.callbacks;
|
|
3594
3402
|
const handoff = this.#options.handoff;
|
|
3595
3403
|
let adapter;
|
|
@@ -3822,6 +3630,7 @@ var PiRuntimeAgentRunner = class {
|
|
|
3822
3630
|
throwIfProgressFailed();
|
|
3823
3631
|
beginTurn(false);
|
|
3824
3632
|
throwIfTurnPolicyFailed();
|
|
3633
|
+
if (request.turnControl && !session.turnControlDelivered) await activeSession.steer(request.turnControl);
|
|
3825
3634
|
await promptAndRecover(request.task, true);
|
|
3826
3635
|
completeTurn(hasResult() || !hasToolCall(lastAssistant), false, true);
|
|
3827
3636
|
if (turnPolicyFailure) throw turnPolicyFailure;
|
|
@@ -3912,31 +3721,83 @@ function parseModel(value, fallback, aliases = {}, knownModels, settingsPath) {
|
|
|
3912
3721
|
assertModelThinking(value);
|
|
3913
3722
|
return resolveModelReference(value, aliases, knownModels, settingsPath);
|
|
3914
3723
|
}
|
|
3724
|
+
async function authorizedResources(root, cwd, options) {
|
|
3725
|
+
const agentDir = root.agentDir ?? getAgentDir2();
|
|
3726
|
+
const trusted = root.projectTrusted ?? false;
|
|
3727
|
+
const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });
|
|
3728
|
+
settingsManager.setProjectTrusted(trusted);
|
|
3729
|
+
const resolved = await new DefaultPackageManager({ cwd, agentDir, settingsManager, builtinExtensions: ["codemode", "tool-search", "mcp"] }).resolve();
|
|
3730
|
+
const extensions = resolved.extensions.filter(({ enabled, metadata }) => enabled && (trusted || metadata.scope !== "project")).map(({ path }) => extensionIdentity(path));
|
|
3731
|
+
const skillPaths = resolved.skills.filter(({ enabled, metadata }) => enabled && (trusted || metadata.scope !== "project")).map(({ path }) => path);
|
|
3732
|
+
const skills = loadSkills({ cwd, agentDir, skillPaths: [...skillPaths, ...root.additionalSkillPaths ?? []], includeDefaults: false }).skills.map(({ name }) => name);
|
|
3733
|
+
const parent = options.capabilities;
|
|
3734
|
+
return { tools: [...root.tools].filter((tool) => options.effectiveTools === void 0 || options.effectiveTools.includes(tool)).filter((tool) => parent === void 0 || parent.tools.includes(tool)), skills: [...new Set(skills)].filter((name) => parent === void 0 || parent.skills.includes(name)), extensions: [...new Set(extensions)].filter((path) => parent === void 0 || parent.extensions.includes(path)) };
|
|
3735
|
+
}
|
|
3736
|
+
function validatePreparedConfiguration(root, configuration, capabilities, cwd, resumed) {
|
|
3737
|
+
const code = resumed ? "RESUME_INCOMPATIBLE" : "INVALID_METADATA";
|
|
3738
|
+
if (!jsonObject(configuration) || typeof configuration.projectTrusted !== "boolean" || typeof configuration.systemPromptAppend !== "string" || configuration.systemPrompt !== void 0 && typeof configuration.systemPrompt !== "string") throw new WorkflowError(code, "Invalid prepared agent configuration");
|
|
3739
|
+
if (configuration.projectTrusted && !root.projectTrusted) throw new WorkflowError("RESUME_INCOMPATIBLE", "Prepared agent configuration lost project trust");
|
|
3740
|
+
if (!object(configuration.model) || typeof configuration.model.provider !== "string" || typeof configuration.model.model !== "string") throw new WorkflowError(code, "Invalid prepared model");
|
|
3741
|
+
const model = resolveModelReference(`${configuration.model.provider}/${configuration.model.model}${configuration.model.thinking === void 0 ? "" : `:${configuration.model.thinking}`}`);
|
|
3742
|
+
if (root.availableModels !== void 0 && !root.availableModels.has(modelCapability(model))) throw new WorkflowError(resumed ? "RESUME_INCOMPATIBLE" : "UNKNOWN_MODEL", `Required model is unavailable: ${modelCapability(model)}`);
|
|
3743
|
+
const tools = validateSelectorList(configuration.tools, "configuration", "tools", "INVALID_METADATA", false);
|
|
3744
|
+
if (tools === void 0) throw new WorkflowError(code, "Prepared tools are missing");
|
|
3745
|
+
const missing = tools.find((tool) => !capabilities.tools.includes(tool));
|
|
3746
|
+
if (missing) throw new WorkflowError(resumed ? "RESUME_INCOMPATIBLE" : "UNKNOWN_TOOL", `Required tool is unavailable: ${missing}`);
|
|
3747
|
+
const excludeTools = configuration.excludeTools;
|
|
3748
|
+
try {
|
|
3749
|
+
validateAgentOptions({ excludeTools });
|
|
3750
|
+
} catch {
|
|
3751
|
+
throw new WorkflowError(code, "Invalid prepared excludeTools");
|
|
3752
|
+
}
|
|
3753
|
+
if (!Array.isArray(excludeTools) || tools.some((tool) => excludeTools.includes(tool))) throw new WorkflowError(code, "Prepared excludeTools contradict the prepared tools");
|
|
3754
|
+
validateSelectorList(configuration.skills, "configuration", "skills", "INVALID_METADATA", false);
|
|
3755
|
+
validateSelectorList(configuration.extensions, "configuration", "extensions", "INVALID_METADATA", false);
|
|
3756
|
+
if (resumed) for (const key of ["skills", "extensions"]) {
|
|
3757
|
+
const selectors = configuration[key];
|
|
3758
|
+
const selected = selectors.slice(selectors.lastIndexOf("!*") + 1);
|
|
3759
|
+
const missing2 = selected.find((name) => !capabilities[key].includes(name));
|
|
3760
|
+
if (missing2) throw new WorkflowError("RESUME_INCOMPATIBLE", `Required ${key} capability is unavailable: ${missing2}`);
|
|
3761
|
+
}
|
|
3762
|
+
validateContextFileScopes(configuration.contextFiles, "configuration");
|
|
3763
|
+
validateWorkflowExtensionSettings(configuration.settings, "configuration", "INVALID_METADATA");
|
|
3764
|
+
root.validateExtensionSettings?.(configuration.settings, { source: "effective", cwd, projectTrusted: root.projectTrusted ?? false });
|
|
3765
|
+
return deepFreeze(structuredClone(configuration));
|
|
3766
|
+
}
|
|
3915
3767
|
function accounting(stats) {
|
|
3916
3768
|
return { input: stats.tokens.input, output: stats.tokens.output, cacheRead: stats.tokens.cacheRead, cacheWrite: stats.tokens.cacheWrite, cost: stats.cost };
|
|
3917
3769
|
}
|
|
3918
|
-
var
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3770
|
+
var WORKFLOW_HOST_ENTRY_PATHS = ["src/index.ts", "dist/src/index.js", "starter/index.ts", "dist/starter/index.js", "subagents/index.ts", "dist/subagents/index.js"];
|
|
3771
|
+
function isWorkflowHostEntry(path) {
|
|
3772
|
+
const entryPath = canonicalPath(path);
|
|
3773
|
+
let packageRoot2 = dirname3(entryPath);
|
|
3774
|
+
while (packageRoot2 !== dirname3(packageRoot2)) {
|
|
3775
|
+
const packageMetadataPath = join5(packageRoot2, "package.json");
|
|
3776
|
+
if (existsSync(packageMetadataPath)) {
|
|
3777
|
+
try {
|
|
3778
|
+
const metadata = JSON.parse(readFileSync2(packageMetadataPath, "utf8"));
|
|
3779
|
+
if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || !isCorePackageName(metadata.name)) return false;
|
|
3780
|
+
return WORKFLOW_HOST_ENTRY_PATHS.some((entry) => canonicalPath(join5(packageRoot2, entry)) === entryPath);
|
|
3781
|
+
} catch {
|
|
3782
|
+
return false;
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
packageRoot2 = dirname3(packageRoot2);
|
|
3786
|
+
}
|
|
3787
|
+
return false;
|
|
3788
|
+
}
|
|
3789
|
+
var WORKFLOW_DIRECTORY2 = "pi-extensible-workflows";
|
|
3929
3790
|
function workflowSystemPromptPath(cwd, agentDir, projectTrusted) {
|
|
3930
|
-
const projectPath =
|
|
3791
|
+
const projectPath = join5(cwd, ".pi", WORKFLOW_DIRECTORY2, "SYSTEM.md");
|
|
3931
3792
|
if (projectTrusted && existsSync(projectPath)) return projectPath;
|
|
3932
|
-
const globalPaths = [
|
|
3793
|
+
const globalPaths = [join5(homedir4(), ".pi", WORKFLOW_DIRECTORY2, "SYSTEM.md"), join5(agentDir, WORKFLOW_DIRECTORY2, "SYSTEM.md")];
|
|
3933
3794
|
return globalPaths.find((path) => existsSync(path));
|
|
3934
3795
|
}
|
|
3935
3796
|
function filterContextFiles(base, scopes, cwd, agentDir) {
|
|
3936
3797
|
const globalPath = canonicalPath(agentDir);
|
|
3937
3798
|
const cwdPath = canonicalPath(cwd);
|
|
3938
3799
|
return base.filter(({ path }) => {
|
|
3939
|
-
const resolvedPath = canonicalPath(
|
|
3800
|
+
const resolvedPath = canonicalPath(dirname3(path));
|
|
3940
3801
|
return scopes.some((scope) => scope === "global" ? resolvedPath === globalPath : scope === "cwd" ? resolvedPath === cwdPath : resolvedPath !== globalPath && resolvedPath !== cwdPath);
|
|
3941
3802
|
});
|
|
3942
3803
|
}
|
|
@@ -3957,33 +3818,40 @@ function loadPiPackageDirectory() {
|
|
|
3957
3818
|
}
|
|
3958
3819
|
}).catch((error) => ({ error: `could not load @earendil-works/pi-coding-agent package metadata: ${errorText(error)}` }));
|
|
3959
3820
|
}
|
|
3960
|
-
var
|
|
3961
|
-
async function
|
|
3821
|
+
var nativeCoreModules = /* @__PURE__ */ new Map();
|
|
3822
|
+
async function loadNativeCoreModule(file, label2) {
|
|
3962
3823
|
const packageDirectory = await loadPiPackageDirectory();
|
|
3963
|
-
let
|
|
3824
|
+
let modulePath;
|
|
3964
3825
|
if (packageDirectory.directory) {
|
|
3965
|
-
const candidate =
|
|
3966
|
-
if (existsSync(candidate))
|
|
3826
|
+
const candidate = resolve4(packageDirectory.directory, "dist/core", file);
|
|
3827
|
+
if (existsSync(candidate)) modulePath = candidate;
|
|
3967
3828
|
}
|
|
3968
|
-
|
|
3829
|
+
modulePath ??= resolve4(dirname3(fileURLToPath3(import.meta.resolve("@earendil-works/pi-coding-agent"))), "core", file);
|
|
3969
3830
|
const packageDirectoryError = packageDirectory.error ? ` Package directory lookup failed: ${packageDirectory.error}` : "";
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3831
|
+
let loaded = nativeCoreModules.get(file);
|
|
3832
|
+
if (!loaded) {
|
|
3833
|
+
loaded = import(modulePath).catch((error) => {
|
|
3834
|
+
throw new Error(`Could not load Pi ${label2}.${packageDirectoryError}`, { cause: error });
|
|
3835
|
+
});
|
|
3836
|
+
nativeCoreModules.set(file, loaded);
|
|
3837
|
+
}
|
|
3838
|
+
return loaded;
|
|
3973
3839
|
}
|
|
3974
3840
|
async function expandPromptTemplateForInspection(text, templates) {
|
|
3975
|
-
return (await
|
|
3841
|
+
return (await loadNativeCoreModule("prompt-templates.js", "prompt templates")).expandPromptTemplate(text, templates);
|
|
3842
|
+
}
|
|
3843
|
+
async function buildSystemPromptForInspection(options) {
|
|
3844
|
+
return (await loadNativeCoreModule("system-prompt.js", "system prompt builder")).buildSystemPrompt(options);
|
|
3976
3845
|
}
|
|
3977
3846
|
async function preparePiPrompt(native, text) {
|
|
3978
3847
|
const session = native;
|
|
3979
3848
|
const diagnostics = [];
|
|
3980
3849
|
const runner = session._extensionRunner;
|
|
3981
|
-
const baseSystemPrompt =
|
|
3850
|
+
const baseSystemPrompt = session.systemPrompt ?? "";
|
|
3982
3851
|
const baseOptions = session._baseSystemPromptOptions;
|
|
3983
3852
|
const expandSkillCommand = session._expandSkillCommand;
|
|
3984
3853
|
const templates = session.promptTemplates;
|
|
3985
3854
|
if (!runner) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: extension runner is missing", source: "Pi session" });
|
|
3986
|
-
if (typeof session._baseSystemPrompt !== "string") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt is missing", source: "Pi session" });
|
|
3987
3855
|
if (baseOptions === void 0) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt options are missing", source: "Pi session" });
|
|
3988
3856
|
if (typeof expandSkillCommand !== "function") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: skill expansion is missing", source: "Pi session" });
|
|
3989
3857
|
if (!Array.isArray(templates)) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: prompt templates are missing", source: "Pi session" });
|
|
@@ -4008,73 +3876,116 @@ async function preparePiPrompt(native, text) {
|
|
|
4008
3876
|
diagnostics.push({ type: "error", message: `Pi prompt template expansion is unavailable: ${errorText(error)}`, source: "Pi session" });
|
|
4009
3877
|
}
|
|
4010
3878
|
}
|
|
4011
|
-
const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0,
|
|
3879
|
+
const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0, baseOptions) : void 0;
|
|
4012
3880
|
const prepared = result;
|
|
4013
|
-
|
|
3881
|
+
const systemPromptOptions = prepared?.systemPromptOptions ?? baseOptions;
|
|
3882
|
+
const systemPrompt = prepared?.systemPromptOptions === void 0 ? baseSystemPrompt : await buildSystemPromptForInspection(prepared.systemPromptOptions);
|
|
3883
|
+
return { prompt: text, expandedPrompt, systemPrompt, systemPromptOptions, messages: prepared?.messages ?? [], inputHandled, diagnostics };
|
|
4014
3884
|
} finally {
|
|
4015
3885
|
unsubscribe?.();
|
|
4016
3886
|
}
|
|
4017
3887
|
}
|
|
4018
3888
|
function flushExtensionProviders(resourceLoader, modelRuntime) {
|
|
4019
|
-
const { runtime
|
|
3889
|
+
const { runtime } = resourceLoader.getExtensions();
|
|
4020
3890
|
const failures = [];
|
|
4021
|
-
for (const { name, config } of
|
|
3891
|
+
for (const { name, config } of runtime.pendingProviderRegistrations) {
|
|
4022
3892
|
try {
|
|
4023
3893
|
modelRuntime.registerProvider(name, config);
|
|
4024
3894
|
} catch (error) {
|
|
4025
3895
|
failures.push(`${name}: ${errorText(error)}`);
|
|
4026
3896
|
}
|
|
4027
3897
|
}
|
|
4028
|
-
|
|
4029
|
-
for (const { provider } of
|
|
3898
|
+
runtime.pendingProviderRegistrations = [];
|
|
3899
|
+
for (const { provider } of runtime.pendingNativeProviderRegistrations) {
|
|
4030
3900
|
try {
|
|
4031
3901
|
modelRuntime.registerNativeProvider(provider);
|
|
4032
3902
|
} catch (error) {
|
|
4033
3903
|
failures.push(`${provider.id || "native provider"}: ${errorText(error)}`);
|
|
4034
3904
|
}
|
|
4035
3905
|
}
|
|
4036
|
-
|
|
3906
|
+
runtime.pendingNativeProviderRegistrations = [];
|
|
4037
3907
|
return failures;
|
|
4038
3908
|
}
|
|
3909
|
+
function builtinExtensions() {
|
|
3910
|
+
return [
|
|
3911
|
+
{ name: "codemode", factory: createCodemodeExtension(), replaceable: true, builtin: true },
|
|
3912
|
+
{ name: "tool-search", factory: createToolSearchExtension(), replaceable: true, builtin: true },
|
|
3913
|
+
{ name: "mcp", factory: createMcpExtension(), replaceable: true, builtin: true }
|
|
3914
|
+
];
|
|
3915
|
+
}
|
|
3916
|
+
var BUILTIN_EXTENSION_PREFIX = "builtin:";
|
|
3917
|
+
function isLlamaModel(model) {
|
|
3918
|
+
return model.provider === "llama.cpp";
|
|
3919
|
+
}
|
|
3920
|
+
var WorkflowResourceLoader = class extends DefaultResourceLoader {
|
|
3921
|
+
constructor(loaderCwd, options) {
|
|
3922
|
+
super(options);
|
|
3923
|
+
this.loaderCwd = loaderCwd;
|
|
3924
|
+
}
|
|
3925
|
+
loaderCwd;
|
|
3926
|
+
extensionSkillPaths = [];
|
|
3927
|
+
async reload(...args) {
|
|
3928
|
+
this.extensionSkillPaths.length = 0;
|
|
3929
|
+
await super.reload(...args);
|
|
3930
|
+
}
|
|
3931
|
+
extendResources(paths) {
|
|
3932
|
+
this.extensionSkillPaths.push(...(paths.skillPaths ?? []).map(({ path }) => piResourcePath(path, this.loaderCwd)));
|
|
3933
|
+
super.extendResources(paths);
|
|
3934
|
+
}
|
|
3935
|
+
};
|
|
3936
|
+
function discoveredSkillSelectors(selectors, inventory) {
|
|
3937
|
+
if (selectors === void 0 || inventory === void 0) return selectors;
|
|
3938
|
+
const frozen = /* @__PURE__ */ new Set();
|
|
3939
|
+
let index = selectors.length;
|
|
3940
|
+
for (const name of [...inventory].reverse().concat("!*")) {
|
|
3941
|
+
do
|
|
3942
|
+
index -= 1;
|
|
3943
|
+
while (index >= 0 && selectors[index] !== name);
|
|
3944
|
+
if (index < 0) return selectors;
|
|
3945
|
+
frozen.add(index);
|
|
3946
|
+
}
|
|
3947
|
+
return selectors.filter((_selector, position) => !frozen.has(position));
|
|
3948
|
+
}
|
|
4039
3949
|
async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
4040
|
-
const agentDir = input.agentDir ??
|
|
3950
|
+
const agentDir = input.agentDir ?? getAgentDir2();
|
|
4041
3951
|
const systemPromptSource = workflowSystemPromptPath(input.cwd, agentDir, input.resourcePolicy?.projectTrusted ?? true);
|
|
4042
3952
|
const systemPromptOptions = input.systemPrompt !== void 0 ? { systemPromptOverride: () => input.systemPrompt } : systemPromptSource !== void 0 ? { systemPrompt: systemPromptSource } : {};
|
|
4043
3953
|
const contextFilesOverride = input.contextFiles === void 0 ? void 0 : (base) => ({ ...base, agentsFiles: filterContextFiles(base.agentsFiles, input.contextFiles ?? [], input.cwd, agentDir) });
|
|
4044
|
-
const manager = input.sessionManager ?? (input.sessionPath ? SessionManager.open(input.sessionPath,
|
|
3954
|
+
const manager = input.sessionManager ?? (input.sessionPath ? SessionManager.open(input.sessionPath, join5(agentDir, "sessions"), input.cwd) : input.agentDir ? SessionManager.create(input.cwd, join5(agentDir, "sessions")) : SessionManager.create(input.cwd));
|
|
4045
3955
|
if (!input.sessionPath) manager.appendSessionInfo(input.sessionLabel);
|
|
4046
|
-
const modelRuntime = await ModelRuntime.create({ authPath:
|
|
3956
|
+
const modelRuntime = await ModelRuntime.create({ authPath: join5(agentDir, "auth.json"), modelsPath: join5(agentDir, "models.json") });
|
|
4047
3957
|
const customTools = [...input.customTools ?? [], ...input.resultTool ? [input.resultTool] : []];
|
|
4048
3958
|
const tools = [.../* @__PURE__ */ new Set([...input.tools, ...customTools.map(({ name }) => name)])];
|
|
4049
3959
|
let settingsManager;
|
|
4050
3960
|
let resourceLoader;
|
|
3961
|
+
const builtins = builtinExtensions();
|
|
3962
|
+
const builtinNames = builtins.flatMap((extension2) => typeof extension2 === "function" ? [] : [extension2.name]);
|
|
3963
|
+
const extensionFactories = [...builtins, ...input.extensionFactories ?? []];
|
|
4051
3964
|
const policy = input.resourcePolicy;
|
|
4052
3965
|
if (policy) {
|
|
4053
3966
|
settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: false });
|
|
4054
3967
|
settingsManager.setProjectTrusted(policy.projectTrusted);
|
|
4055
|
-
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
|
|
3968
|
+
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
|
|
4056
3969
|
const resolved = await packageManager.resolve();
|
|
4057
|
-
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) =>
|
|
3970
|
+
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => extensionIdentity(path)))];
|
|
4058
3971
|
const selectorSources = policy.selectorSources;
|
|
4059
|
-
const
|
|
4060
|
-
|
|
4061
|
-
selectorSources,
|
|
4062
|
-
resources: { extensions: discoveredExtensions }
|
|
4063
|
-
});
|
|
4064
|
-
const selectedExtensions = selection.selectedExtensions ?? [];
|
|
4065
|
-
const extensionPaths = selectedExtensions.filter((path) => !WORKFLOW_HOST_ENTRIES.has(path));
|
|
3972
|
+
const selectedExtensions = selectResourcesByLayers(resourceSelectorLayers(selectorSources, "extensions"), discoveredExtensions).filter((path) => policy.capabilities === void 0 || policy.capabilities.extensions.includes(path));
|
|
3973
|
+
const extensionPaths = selectedExtensions.filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path));
|
|
4066
3974
|
policy.selectedExtensions = selectedExtensions;
|
|
4067
|
-
policy.unmatchedExtensions =
|
|
3975
|
+
policy.unmatchedExtensions = unmatchedResourcePatterns(policy.effective.extensions, discoveredExtensions);
|
|
4068
3976
|
const skillPaths = [...new Set(resolved.skills.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => path))];
|
|
4069
|
-
const updateSkillMatches = (skills) => {
|
|
3977
|
+
const updateSkillMatches = (skills, extensionSkillPaths) => {
|
|
4070
3978
|
const names = [...new Set(skills.map(({ name }) => name))];
|
|
4071
|
-
const
|
|
4072
|
-
const
|
|
3979
|
+
const discovered = new Set(skills.filter(({ filePath }) => extensionSkillPaths.some((path) => piResourceContains(path, filePath))).map(({ name }) => name));
|
|
3980
|
+
const [global, project, call] = resourceSelectorLayers(selectorSources, "skills");
|
|
3981
|
+
const selectedStatic = selectResourcesByLayers([global, project, call], names.filter((name) => !discovered.has(name))).filter((name) => policy.capabilities === void 0 || policy.capabilities.skills.includes(name));
|
|
3982
|
+
const selectedDiscovered = selectResourcesByLayers([discoveredSkillSelectors(global, policy.capabilities?.skills), project, call], [...discovered]).filter((name) => policy.parentSkills === void 0 || policy.parentSkills.includes(name));
|
|
3983
|
+
const selectedSkills = [...selectedStatic, ...selectedDiscovered];
|
|
4073
3984
|
policy.selectedSkills = selectedSkills;
|
|
4074
|
-
policy.unmatchedSkills =
|
|
3985
|
+
policy.unmatchedSkills = unmatchedResourcePatterns(policy.effective.skills, names);
|
|
4075
3986
|
return new Set(names.filter((name) => !selectedSkills.includes(name)));
|
|
4076
3987
|
};
|
|
4077
|
-
|
|
3988
|
+
const workflowLoader = new WorkflowResourceLoader(input.cwd, {
|
|
4078
3989
|
cwd: input.cwd,
|
|
4079
3990
|
agentDir,
|
|
4080
3991
|
settingsManager,
|
|
@@ -4082,33 +3993,34 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
4082
3993
|
additionalExtensionPaths: extensionPaths,
|
|
4083
3994
|
noSkills: true,
|
|
4084
3995
|
additionalSkillPaths: [.../* @__PURE__ */ new Set([...skillPaths, ...input.additionalSkillPaths ?? []])],
|
|
4085
|
-
|
|
3996
|
+
extensionFactories,
|
|
4086
3997
|
...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {},
|
|
4087
3998
|
skillsOverride: (base) => {
|
|
4088
|
-
const disabledSkills = updateSkillMatches(base.skills);
|
|
3999
|
+
const disabledSkills = updateSkillMatches(base.skills, workflowLoader.extensionSkillPaths);
|
|
4089
4000
|
return { ...base, skills: base.skills.filter(({ name }) => !disabledSkills.has(name)) };
|
|
4090
4001
|
},
|
|
4091
4002
|
...input.systemPromptAppend ? { appendSystemPromptOverride: (base) => [...base, input.systemPromptAppend ?? ""] } : {},
|
|
4092
4003
|
...systemPromptOptions
|
|
4093
4004
|
});
|
|
4005
|
+
resourceLoader = workflowLoader;
|
|
4094
4006
|
await resourceLoader.reload();
|
|
4095
4007
|
} else {
|
|
4096
4008
|
settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: true });
|
|
4097
|
-
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
|
|
4009
|
+
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
|
|
4098
4010
|
const resolved = await packageManager.resolve();
|
|
4099
|
-
const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) =>
|
|
4100
|
-
resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {},
|
|
4011
|
+
const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) => extensionIdentity(path)).filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path)))];
|
|
4012
|
+
resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {}, extensionFactories, ...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {}, ...systemPromptOptions, ...input.systemPromptAppend ? { appendSystemPromptOverride: (base) => [...base, input.systemPromptAppend ?? ""] } : {} });
|
|
4101
4013
|
await resourceLoader.reload();
|
|
4102
4014
|
}
|
|
4103
4015
|
const providerFailures = flushExtensionProviders(resourceLoader, modelRuntime);
|
|
4104
4016
|
await modelRuntime.refresh({ allowNetwork: false });
|
|
4105
4017
|
const model = modelRuntime.getModel(input.model.provider, input.model.model);
|
|
4106
4018
|
if (!model) {
|
|
4107
|
-
const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : "";
|
|
4019
|
+
const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : isLlamaModel(input.model) ? " (Pi does not expose its built-in llama.cpp provider to extension sessions; declare the llama.cpp server as a provider in models.json and use that model instead)" : "";
|
|
4108
4020
|
throw new WorkflowError("UNKNOWN_MODEL", `Unknown model: ${input.model.provider}/${input.model.model}${because}`);
|
|
4109
4021
|
}
|
|
4110
4022
|
const effectiveSessionStartEvent = input.settings === void 0 && sessionStartEvent === void 0 ? void 0 : { ...sessionStartEvent ?? { type: "session_start", reason: "startup" }, settings: input.settings ?? Object.freeze({}) };
|
|
4111
|
-
const { session } = await createAgentSession({
|
|
4023
|
+
const { session } = await createAgentSession({ cwd: input.cwd, agentDir, modelRuntime, model, settingsManager, ...input.model.thinking ? { thinkingLevel: input.model.thinking } : {}, tools, ...customTools.length ? { customTools } : {}, ...input.extensionFactories?.length ? { extensionFactories: input.extensionFactories } : {}, resourceLoader, ...effectiveSessionStartEvent ? { sessionStartEvent: effectiveSessionStartEvent } : {}, sessionManager: manager });
|
|
4112
4024
|
const nativeDispose = session.dispose.bind(session);
|
|
4113
4025
|
let disposal;
|
|
4114
4026
|
const shutdown = (reason, targetSessionFile) => {
|
|
@@ -4129,13 +4041,13 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
4129
4041
|
await shutdown("quit").catch(() => void 0);
|
|
4130
4042
|
throw error;
|
|
4131
4043
|
}
|
|
4132
|
-
const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) =>
|
|
4044
|
+
const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: resourceLoader.getSkills().skills.map(({ filePath }) => canonicalPath(filePath)) };
|
|
4133
4045
|
const resourceInspection = () => {
|
|
4134
4046
|
const extensions = resourceLoader.getExtensions();
|
|
4135
4047
|
const skills = resourceLoader.getSkills();
|
|
4136
4048
|
const diagnostics = [...extensions.errors.map(({ path, error }) => ({ type: "error", message: error, source: path })), ...skills.diagnostics.map(({ type, path, message }) => ({ type, message, ...path ? { source: path } : {} }))];
|
|
4137
4049
|
const systemSource = systemPromptSource;
|
|
4138
|
-
return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) =>
|
|
4050
|
+
return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: skills.skills.map(({ name }) => name), diagnostics, ...systemSource ? { systemPromptSource: systemSource } : resourceLoader.getSystemPrompt() !== void 0 ? { systemPromptSource: "Pi resource loader" } : {} };
|
|
4139
4051
|
};
|
|
4140
4052
|
const managedSession = Object.assign(session, {
|
|
4141
4053
|
getLeafId: () => manager.getLeafId(),
|
|
@@ -4471,6 +4383,10 @@ async function createLocalWorkflowAgentSession(prepared, context) {
|
|
|
4471
4383
|
return session;
|
|
4472
4384
|
}
|
|
4473
4385
|
var localAgentTransport = Object.freeze({ id: "local", createSession: createLocalWorkflowAgentSession });
|
|
4386
|
+
function excludedToolNames(options) {
|
|
4387
|
+
const own = options.agentOptions?.excludeTools;
|
|
4388
|
+
return /* @__PURE__ */ new Set([...options.inheritedExcludeTools ?? [], ...Array.isArray(own) ? own.filter((tool) => typeof tool === "string") : []]);
|
|
4389
|
+
}
|
|
4474
4390
|
function changedOption(options, baseline, key) {
|
|
4475
4391
|
return JSON.stringify(options[key]) !== JSON.stringify(baseline[key]);
|
|
4476
4392
|
}
|
|
@@ -4493,7 +4409,7 @@ function resourcePolicySummary(policy, tools = policy.selectedTools ?? []) {
|
|
|
4493
4409
|
}
|
|
4494
4410
|
var RESOURCE_SELECTOR_KEYS = ["skills", "extensions", "tools"];
|
|
4495
4411
|
function resourceSelectorLayers(sources, key) {
|
|
4496
|
-
return [sources.global[key], sources.project[key], sources.
|
|
4412
|
+
return [sources.global[key], sources.project[key], sources.call?.[key]];
|
|
4497
4413
|
}
|
|
4498
4414
|
function selectorListWidened(ceiling, candidate) {
|
|
4499
4415
|
let candidateIndex = 0;
|
|
@@ -4550,32 +4466,32 @@ function appendResourcePolicyNarrowing(ceiling, candidate) {
|
|
|
4550
4466
|
};
|
|
4551
4467
|
}
|
|
4552
4468
|
function packageRoot(start) {
|
|
4553
|
-
let current =
|
|
4469
|
+
let current = dirname3(canonicalPath(start));
|
|
4554
4470
|
for (; ; ) {
|
|
4555
|
-
const candidates = [current,
|
|
4471
|
+
const candidates = [current, join5(current, "..", "@earendil-works", "pi-coding-agent"), join5(current, "..", "pi-coding-agent")];
|
|
4556
4472
|
for (const candidate of candidates) {
|
|
4557
|
-
const packageJsonPath =
|
|
4473
|
+
const packageJsonPath = join5(candidate, "package.json");
|
|
4558
4474
|
if (!existsSync(packageJsonPath)) continue;
|
|
4559
|
-
const packageJson = JSON.parse(
|
|
4475
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
4560
4476
|
if (object(packageJson) && typeof packageJson.name === "string" && packageJson.name === "@earendil-works/pi-coding-agent") return candidate;
|
|
4561
4477
|
}
|
|
4562
|
-
const parent =
|
|
4478
|
+
const parent = dirname3(current);
|
|
4563
4479
|
if (parent === current) return void 0;
|
|
4564
4480
|
current = parent;
|
|
4565
4481
|
}
|
|
4566
4482
|
}
|
|
4567
4483
|
function piPackageEntrypoint(root) {
|
|
4568
|
-
const packageJson = JSON.parse(
|
|
4484
|
+
const packageJson = JSON.parse(readFileSync2(join5(root, "package.json"), "utf8"));
|
|
4569
4485
|
if (!object(packageJson)) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4570
4486
|
const bin = packageJson.bin;
|
|
4571
4487
|
const cli = typeof bin === "string" ? bin : object(bin) && typeof bin.pi === "string" ? bin.pi : void 0;
|
|
4572
4488
|
if (!cli) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4573
|
-
const entrypoint =
|
|
4489
|
+
const entrypoint = resolve4(root, cli);
|
|
4574
4490
|
if (!existsSync(entrypoint)) throw new Error(`the originating Pi CLI entrypoint is unavailable: ${entrypoint}`);
|
|
4575
4491
|
return entrypoint;
|
|
4576
4492
|
}
|
|
4577
4493
|
async function resolvePiRuntime() {
|
|
4578
|
-
const executableName =
|
|
4494
|
+
const executableName = basename2(process.execPath).toLowerCase();
|
|
4579
4495
|
const bunBinary = ["$bunfs", "~BUN", "%7EBUN"].some((marker) => import.meta.url.includes(marker)) || Boolean(process.versions.bun && !["bun", "bun.exe"].includes(executableName));
|
|
4580
4496
|
if (bunBinary) return { runtime: Object.freeze({ executable: process.execPath }) };
|
|
4581
4497
|
const errors = [];
|
|
@@ -4599,19 +4515,12 @@ async function resolvePiRuntime() {
|
|
|
4599
4515
|
}
|
|
4600
4516
|
}
|
|
4601
4517
|
var piRuntimeResolution = resolvePiRuntime();
|
|
4602
|
-
function
|
|
4603
|
-
|
|
4604
|
-
const instruction = options.budget?.instruction();
|
|
4605
|
-
return `${basePrompt}
|
|
4606
|
-
|
|
4607
|
-
Task:
|
|
4608
|
-
${task}${instruction ? `
|
|
4609
|
-
|
|
4610
|
-
${instruction}` : ""}`;
|
|
4518
|
+
function attemptControlText(options, attempt, previousError) {
|
|
4519
|
+
return [attempt > 1 ? `Retry attempt ${String(attempt)}. Previous state: ${options.retryState ?? previousError ?? "failed attempt"}` : "", options.budget?.instruction() ?? ""].filter(Boolean).join("\n");
|
|
4611
4520
|
}
|
|
4612
4521
|
async function preparedAgentSession(input, initialPrompt) {
|
|
4613
4522
|
const { runtime: piRuntime, error: piRuntimeError } = await piRuntimeResolution;
|
|
4614
|
-
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ??
|
|
4523
|
+
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ?? getAgentDir2(), input.resourcePolicy?.projectTrusted ?? true) : void 0;
|
|
4615
4524
|
const prepared = {
|
|
4616
4525
|
cwd: input.cwd,
|
|
4617
4526
|
model: Object.freeze({ ...input.model }),
|
|
@@ -4641,49 +4550,30 @@ function agentSetupSummary(setup, hookNames) {
|
|
|
4641
4550
|
return { hookNames: [...hookNames], model: { provider: model.provider, model: model.model, ...model.thinking ? { thinking: model.thinking } : {} }, tools: [...setup.sessionInput.tools, ...setup.sessionInput.resultTool ? [setup.sessionInput.resultTool.name] : []], cwd: setup.sessionInput.cwd, ...setup.sessionInput.resourcePolicy ? { resourceSelectors: resourcePolicySummary(setup.sessionInput.resourcePolicy, setup.sessionInput.tools) } : {} };
|
|
4642
4551
|
}
|
|
4643
4552
|
async function attemptSessionInput(source, attempt) {
|
|
4644
|
-
const target =
|
|
4553
|
+
const target = join5(dirname3(source), `${basename2(source, ".jsonl")}-attempt-${String(attempt)}.jsonl`);
|
|
4645
4554
|
await copyFile(source, target);
|
|
4646
4555
|
return target;
|
|
4647
4556
|
}
|
|
4648
|
-
async function prepareAgentSetup(root, transport, task, options, resolved, cwd, attempt, signal, customTools, resultTool, inspection = false
|
|
4557
|
+
async function prepareAgentSetup(root, transport, task, options, resolved, cwd, attempt, signal, customTools, resultTool, inspection = false) {
|
|
4649
4558
|
const setupSignal = signal ?? root.runContext?.signal ?? new AbortController().signal;
|
|
4650
4559
|
const baselineOptions = structuredClone(options.agentOptions ?? {});
|
|
4651
4560
|
const baseResourcePolicy = await root.agentResourcePolicy?.();
|
|
4652
|
-
const
|
|
4653
|
-
const
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) return value;
|
|
4662
|
-
return options.agentOptions === void 0 ? fallback : void 0;
|
|
4561
|
+
const configuration = options.configuration;
|
|
4562
|
+
const resourcePolicy = {
|
|
4563
|
+
...baseResourcePolicy ?? { globalSettingsPath: "", projectSettingsPath: "", projectTrusted: root.projectTrusted ?? false, global: { skills: [], extensions: [] }, project: { skills: [], extensions: [] }, unmatchedSkills: [], unmatchedExtensions: [] },
|
|
4564
|
+
projectTrusted: configuration?.projectTrusted ?? root.projectTrusted ?? baseResourcePolicy?.projectTrusted ?? false,
|
|
4565
|
+
effective: { skills: configuration?.skills ?? [], extensions: configuration?.extensions ?? [], tools: resolved.tools },
|
|
4566
|
+
selectorSources: { global: { skills: configuration?.skills ?? [], extensions: configuration?.extensions ?? [], tools: resolved.tools }, project: {} },
|
|
4567
|
+
...options.capabilities ? { capabilities: options.capabilities } : {},
|
|
4568
|
+
...options.parentSkills ? { parentSkills: [...options.parentSkills] } : {},
|
|
4569
|
+
selectedTools: resolved.tools
|
|
4663
4570
|
};
|
|
4664
|
-
const
|
|
4665
|
-
const rawCallExtensions = selectorValue("extensions", options.extensions)?.map((selector) => canonicalExtensionSelector(selector, cwd));
|
|
4666
|
-
const rawCallTools = selectorValue("tools", options.tools);
|
|
4667
|
-
const callSelectors = { ...rawCallSkills === void 0 ? {} : { skills: rawCallSkills }, ...rawCallExtensions === void 0 ? {} : { extensions: rawCallExtensions }, ...rawCallTools === void 0 ? {} : { tools: rawCallTools } };
|
|
4668
|
-
const resourcePolicy = baseResourcePolicy ? {
|
|
4669
|
-
...baseResourcePolicy,
|
|
4670
|
-
effective: {
|
|
4671
|
-
skills: [...baseResourcePolicy.effective.skills, ...roleSelectors?.skills ?? [], ...rawCallSkills ?? []],
|
|
4672
|
-
extensions: [...baseResourcePolicy.effective.extensions, ...roleSelectors?.extensions ?? [], ...rawCallExtensions ?? []],
|
|
4673
|
-
...baseResourcePolicy.effective.tools === void 0 && roleSelectors?.tools === void 0 && rawCallTools === void 0 ? {} : { tools: [...baseResourcePolicy.effective.tools ?? [], ...roleSelectors?.tools ?? [], ...rawCallTools ?? []] }
|
|
4674
|
-
},
|
|
4675
|
-
selectorSources: { global: baseResourcePolicy.selectorSources.global, project: baseResourcePolicy.selectorSources.project, ...roleSelectors ? { role: roleSelectors } : {}, ...Object.keys(callSelectors).length ? { call: callSelectors } : {} }
|
|
4676
|
-
} : void 0;
|
|
4677
|
-
if (resourcePolicy) {
|
|
4678
|
-
resourcePolicy.selectedTools = resolved.tools;
|
|
4679
|
-
resourcePolicy.unmatchedTools = unmatchedResourcePatterns(resourceSelectorLayers(resourcePolicy.selectorSources, "tools").flatMap((layer) => layer ?? []), [...root.tools]);
|
|
4680
|
-
}
|
|
4681
|
-
const resourcePolicyCeiling = resourcePolicy ? structuredClone(resourcePolicy) : void 0;
|
|
4571
|
+
const resourcePolicyCeiling = structuredClone(resourcePolicy);
|
|
4682
4572
|
const sessionPath = options.sessionPath === void 0 || inspection ? options.sessionPath : await attemptSessionInput(options.sessionPath, attempt);
|
|
4683
|
-
const extensionSettings =
|
|
4684
|
-
root.validateExtensionSettings?.(extensionSettings, { source:
|
|
4573
|
+
const extensionSettings = configuration?.settings ?? options.inheritedExtensionSettings ?? root.extensionSettings ?? Object.freeze({});
|
|
4574
|
+
root.validateExtensionSettings?.(extensionSettings, { source: "effective", cwd, projectTrusted: resourcePolicy.projectTrusted, ...root.extensionSettingsPath ?? root.settingsPath ? { settingsPath: root.extensionSettingsPath ?? root.settingsPath } : {} });
|
|
4685
4575
|
if (options.agentNodeId !== void 0) root.onAgentSettings?.(options.agentNodeId, extensionSettings);
|
|
4686
|
-
const sessionInput = { cwd, model: { ...resolved.model }, tools: [...resolved.tools], sessionLabel: `${options.workflowName}:${options.label}:attempt-${String(attempt)}`, ...sessionPath ? { sessionPath } : {}, ...root.agentDir ? { agentDir: root.agentDir } : {}, ...root.additionalSkillPaths?.length ? { additionalSkillPaths: [...root.additionalSkillPaths] } : {}, ...resolved.contextFiles === void 0 ? {} : { contextFiles: [...resolved.contextFiles] }, ...customTools.length ? { customTools: [...customTools] } : {}, ...resultTool ? { resultTool } : {}, ...resolved.systemPrompt !== void 0 ? { systemPrompt: resolved.systemPrompt } : {}, systemPromptAppend: resolved.systemPromptAppend,
|
|
4576
|
+
const sessionInput = { cwd, model: { ...resolved.model }, tools: [...resolved.tools], sessionLabel: `${options.workflowName}:${options.label}:attempt-${String(attempt)}`, ...sessionPath ? { sessionPath } : {}, ...root.agentDir ? { agentDir: root.agentDir } : {}, ...root.additionalSkillPaths?.length ? { additionalSkillPaths: [...root.additionalSkillPaths] } : {}, ...resolved.contextFiles === void 0 ? {} : { contextFiles: [...resolved.contextFiles] }, ...customTools.length ? { customTools: [...customTools] } : {}, ...resultTool ? { resultTool } : {}, ...resolved.systemPrompt !== void 0 ? { systemPrompt: resolved.systemPrompt } : {}, systemPromptAppend: resolved.systemPromptAppend, resourcePolicy, settings: extensionSettings, options: structuredClone(baselineOptions) };
|
|
4687
4577
|
const setup = { prompt: task, options: sessionInput.options ?? {}, sessionInput, prepared: await preparedAgentSession(sessionInput, task), transport };
|
|
4688
4578
|
const base = fallbackSetupContext(root, options, setupSignal);
|
|
4689
4579
|
const context = Object.freeze({ run: base.run, identity: base.identity, attempt, signal: setupSignal, settings: extensionSettings, ...base.tuiIndex === void 0 ? {} : { tuiIndex: base.tuiIndex }, ...base.tuiLabel === void 0 ? {} : { tuiLabel: base.tuiLabel }, ...inspection ? { mode: "inspection" } : {} });
|
|
@@ -4703,19 +4593,42 @@ async function prepareAgentSetup(root, transport, task, options, resolved, cwd,
|
|
|
4703
4593
|
try {
|
|
4704
4594
|
if (resourcePolicyWidened(resourcePolicyCeiling, setup.sessionInput.resourcePolicy)) throw new WorkflowError("INVALID_METADATA", "Agent setup widened the prepared resource policy");
|
|
4705
4595
|
appendResourcePolicyNarrowing(resourcePolicyCeiling, setup.sessionInput.resourcePolicy);
|
|
4596
|
+
if (setup.sessionInput.resourcePolicy) {
|
|
4597
|
+
const policy = setup.sessionInput.resourcePolicy;
|
|
4598
|
+
policy.effective.extensions = policy.effective.extensions.map((selector) => canonicalExtensionSelector(selector, cwd));
|
|
4599
|
+
for (const layer of [policy.selectorSources.global, policy.selectorSources.project, policy.selectorSources.call]) if (layer?.extensions !== void 0) layer.extensions = layer.extensions.map((selector) => canonicalExtensionSelector(selector, cwd));
|
|
4600
|
+
if (resourcePolicyCeiling.parentSkills === void 0) Reflect.deleteProperty(policy, "parentSkills");
|
|
4601
|
+
else policy.parentSkills = [...resourcePolicyCeiling.parentSkills];
|
|
4602
|
+
}
|
|
4706
4603
|
setup.sessionInput.options = setup.options;
|
|
4707
4604
|
if (changedOption(setup.options, baselineOptions, "model") && typeof setup.options.model === "string") setup.sessionInput.model = parseModel(setup.options.model, setup.sessionInput.model, root.modelAliases, root.knownModels ?? root.availableModels, root.settingsPath);
|
|
4708
4605
|
if (changedOption(setup.options, baselineOptions, "tools") && Array.isArray(setup.options.tools) && setup.options.tools.every((tool) => typeof tool === "string")) setup.sessionInput.tools = [...setup.options.tools];
|
|
4709
|
-
if (changedOption(setup.options, baselineOptions, "cwd")
|
|
4606
|
+
if (changedOption(setup.options, baselineOptions, "cwd") || !sameFilesystemPath(cwd, setup.sessionInput.cwd)) throw new WorkflowError("INVALID_METADATA", "Agent setup cannot change cwd");
|
|
4607
|
+
if (!root.availableModels?.has(modelCapability(setup.sessionInput.model)) && root.availableModels !== void 0) throw new WorkflowError("UNKNOWN_MODEL", "Agent setup selected an unavailable model");
|
|
4608
|
+
const shadowed = (setup.sessionInput.customTools ?? []).map(({ name }) => name).find((name) => root.tools.has(name) && !resolved.tools.includes(name));
|
|
4609
|
+
if (shadowed) throw new WorkflowError("UNKNOWN_TOOL", `Custom tool reintroduces a tool outside the prepared agent policy: ${shadowed}`);
|
|
4610
|
+
const excludedTools = new Set(configuration?.excludeTools ?? excludedToolNames(options));
|
|
4611
|
+
const excludedCustomTools = (setup.sessionInput.customTools ?? []).filter(({ name }) => excludedTools.has(name)).map(({ name }) => name);
|
|
4612
|
+
if (excludedCustomTools.length) {
|
|
4613
|
+
setup.sessionInput.customTools = setup.sessionInput.customTools?.filter(({ name }) => !excludedTools.has(name)) ?? [];
|
|
4614
|
+
setup.sessionInput.tools = setup.sessionInput.tools.filter((tool) => root.tools.has(tool) || !excludedCustomTools.includes(tool));
|
|
4615
|
+
}
|
|
4616
|
+
for (const tool of excludedTools) if (!options.inheritedExcludeTools?.includes(tool) && !root.tools.has(tool) && !excludedCustomTools.includes(tool)) root.onResourceWarning?.(`excludeTools names no root or custom tool of this agent: ${tool}`);
|
|
4710
4617
|
const customToolNames = /* @__PURE__ */ new Set([...(setup.sessionInput.customTools ?? []).map(({ name }) => name), ...setup.sessionInput.resultTool ? [setup.sessionInput.resultTool.name] : []]);
|
|
4711
4618
|
const widened = setup.sessionInput.tools.find((tool) => !resolved.tools.includes(tool) && !customToolNames.has(tool));
|
|
4712
4619
|
const outsideTool = widened ?? setup.sessionInput.tools.find((tool) => !root.tools.has(tool) && !customToolNames.has(tool));
|
|
4713
4620
|
if (outsideTool) throw new WorkflowError("UNKNOWN_TOOL", `Tool is outside the prepared agent policy: ${outsideTool}`);
|
|
4621
|
+
if (setup.sessionInput.resourcePolicy) {
|
|
4622
|
+
const policy = setup.sessionInput.resourcePolicy;
|
|
4623
|
+
setup.sessionInput.tools = selectResourcesByLayers(resourceSelectorLayers(policy.selectorSources, "tools"), setup.sessionInput.tools);
|
|
4624
|
+
if (policy.projectTrusted !== resourcePolicyCeiling.projectTrusted) policy.capabilities = await authorizedResources({ ...root, projectTrusted: policy.projectTrusted }, cwd, options);
|
|
4625
|
+
}
|
|
4626
|
+
if (!setup.sessionInput.tools.includes("agent")) setup.sessionInput.customTools = setup.sessionInput.customTools?.filter(({ name }) => name !== "agent" && name !== "get_subagent_result" && name !== "steer_subagent") ?? [];
|
|
4714
4627
|
} catch (error) {
|
|
4715
4628
|
setup.prepared = await preparedAgentSession(setup.sessionInput, task);
|
|
4716
4629
|
return { setup, summary: agentSetupSummary(setup, hookNames), failure: { error } };
|
|
4717
4630
|
}
|
|
4718
|
-
setup.prepared = await preparedAgentSession(setup.sessionInput, inspection ? task :
|
|
4631
|
+
setup.prepared = await preparedAgentSession(setup.sessionInput, inspection ? task : setup.prompt);
|
|
4719
4632
|
return { setup, summary: agentSetupSummary(setup, hookNames) };
|
|
4720
4633
|
}
|
|
4721
4634
|
function attemptRecord(transport, attempt, session, setup, stats, result, error) {
|
|
@@ -4735,54 +4648,93 @@ var WorkflowAgentExecutor = class {
|
|
|
4735
4648
|
}
|
|
4736
4649
|
root;
|
|
4737
4650
|
transport;
|
|
4738
|
-
warnedRoleTools = /* @__PURE__ */ new Set();
|
|
4739
4651
|
setRunContext(runContext) {
|
|
4740
4652
|
this.root.runContext = runContext;
|
|
4741
4653
|
}
|
|
4742
4654
|
resolve(options, inheritedTools) {
|
|
4743
|
-
|
|
4744
|
-
const
|
|
4745
|
-
if (
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
const
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
...this.root.knownModels === void 0 ? {} : { knownModels: this.root.knownModels },
|
|
4764
|
-
...this.root.availableModels === void 0 ? {} : { availableModels: this.root.availableModels },
|
|
4765
|
-
...this.root.blockedAliases === void 0 ? {} : { blockedAliases: this.root.blockedAliases },
|
|
4766
|
-
...this.root.blockedAliasTargets === void 0 ? {} : { blockedAliasTargets: this.root.blockedAliasTargets },
|
|
4767
|
-
...this.root.settingsPath === void 0 ? {} : { settingsPath: this.root.settingsPath },
|
|
4768
|
-
rootModel: this.root.model,
|
|
4769
|
-
rootTools: this.root.tools,
|
|
4770
|
-
...inheritedTools === void 0 ? {} : { inheritedTools },
|
|
4655
|
+
if (options.configuration) return { ...options.configuration, ...options.modelOverride ? { model: options.modelOverride } : {} };
|
|
4656
|
+
const ceiling = inheritedTools ?? [...this.root.tools];
|
|
4657
|
+
for (const pattern of options.tools ?? []) if (!pattern.startsWith("!") && !resourcePatternHasMagic(pattern) && !ceiling.includes(pattern)) throw new WorkflowError("UNKNOWN_TOOL", `Tool is outside the prepared agent policy: ${pattern}`);
|
|
4658
|
+
const tools = options.effectiveTools ?? selectResourcesByLayers([this.root.resourceSelectors?.tools, options.tools], [...inheritedTools ?? this.root.tools]);
|
|
4659
|
+
const missing = tools.find((tool) => !ceiling.includes(tool));
|
|
4660
|
+
if (missing) throw new WorkflowError("UNKNOWN_TOOL", `Tool is outside the prepared agent policy: ${missing}`);
|
|
4661
|
+
const model = options.modelOverride ?? parseModel(options.model, this.root.model, this.root.modelAliases, this.root.knownModels, this.root.settingsPath);
|
|
4662
|
+
return { model: physicalModel(model, this.root.modelAliases, this.root.knownModels, this.root.settingsPath), tools, systemPromptAppend: "", ...options.contextFiles === void 0 ? {} : { contextFiles: options.contextFiles } };
|
|
4663
|
+
}
|
|
4664
|
+
async prepare(options, cwd = this.root.cwd, signal = this.root.runContext?.signal ?? new AbortController().signal, mode = "execution") {
|
|
4665
|
+
const root = { ...this.root, projectTrusted: Boolean(this.root.projectTrusted) && options.projectTrusted !== false };
|
|
4666
|
+
const policy = await this.root.agentResourcePolicy?.();
|
|
4667
|
+
const capabilities = await authorizedResources(root, cwd, options);
|
|
4668
|
+
const persistedKey = options.parent !== void 0 ? options.agentNodeId : options.agentIdentity?.handle !== void 0 ? `handle:${options.agentIdentity.handle}` : options.agentIdentity ? agentIdentityPath(options.agentIdentity) : options.agentNodeId;
|
|
4669
|
+
const stored = options.configuration ?? (mode === "execution" && persistedKey && this.root.runStore?.load ? (await this.root.runStore.load()).snapshot.agentConfigurations[persistedKey] : void 0);
|
|
4670
|
+
if (stored) {
|
|
4671
|
+
if (stored.excludeTools === void 0) throw new WorkflowError("RESUME_INCOMPATIBLE", "Prepared agent configuration predates frozen excludeTools; relaunch the workflow");
|
|
4672
|
+
return validatePreparedConfiguration(root, stored, capabilities, cwd, true);
|
|
4673
|
+
}
|
|
4674
|
+
const original = deepFreeze(structuredClone(validateAgentOptions(options.agentOptions ?? {
|
|
4771
4675
|
...options.model === void 0 ? {} : { model: options.model },
|
|
4772
|
-
...options.
|
|
4773
|
-
...options.
|
|
4774
|
-
...options.
|
|
4775
|
-
...options.
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
});
|
|
4779
|
-
|
|
4676
|
+
...options.tools === void 0 ? {} : { tools: [...options.tools] },
|
|
4677
|
+
...options.skills === void 0 ? {} : { skills: [...options.skills] },
|
|
4678
|
+
...options.extensions === void 0 ? {} : { extensions: [...options.extensions] },
|
|
4679
|
+
...options.contextFiles === void 0 ? {} : { contextFiles: [...options.contextFiles] }
|
|
4680
|
+
})));
|
|
4681
|
+
const dynamicModelAliasNames = this.root.dynamicModelAliasNames;
|
|
4682
|
+
const defaults = deepFreeze({ model: { ...this.root.model }, modelAliases: { ...this.root.modelAliases }, ...dynamicModelAliasNames === void 0 ? {} : { dynamicModelAliasNames: [...dynamicModelAliasNames] }, selectorSources: structuredClone(policy?.selectorSources ?? { global: this.root.resourceSelectors ?? {}, project: {} }), settings: structuredClone(options.inheritedExtensionSettings ?? this.root.extensionSettings ?? {}) });
|
|
4683
|
+
const context = Object.freeze({ options: original, cwd, projectCwd: this.root.cwd, agentDir: this.root.agentDir ?? getAgentDir2(), projectTrusted: root.projectTrusted, defaults, capabilities: deepFreeze(structuredClone(capabilities)), knownModels: new Set(this.root.knownModels), availableModels: new Set(this.root.availableModels), signal, mode });
|
|
4684
|
+
const configuration = {
|
|
4685
|
+
...typeof original.label === "string" ? { label: original.label } : {},
|
|
4686
|
+
...typeof original.model === "string" ? { model: original.model } : {},
|
|
4687
|
+
tools: [...policy?.effective.tools ?? this.root.resourceSelectors?.tools ?? [], ...options.tools ?? []],
|
|
4688
|
+
skills: [...policy?.effective.skills ?? this.root.resourceSelectors?.skills ?? [], ...options.skills ?? []],
|
|
4689
|
+
extensions: [...policy?.effective.extensions ?? this.root.resourceSelectors?.extensions ?? [], ...(options.extensions ?? []).map((selector) => canonicalExtensionSelector(selector, cwd))],
|
|
4690
|
+
...options.contextFiles === void 0 ? {} : { contextFiles: [...options.contextFiles] },
|
|
4691
|
+
systemPromptAppend: "",
|
|
4692
|
+
settings: structuredClone(defaults.settings)
|
|
4693
|
+
};
|
|
4694
|
+
for (const key of ["tools", "skills", "extensions"]) if (Array.isArray(original[key])) {
|
|
4695
|
+
const selectors = validateSelectorList(original[key], "agent options", key, "INVALID_METADATA", false) ?? [];
|
|
4696
|
+
configuration[key] = [...policy?.effective[key] ?? this.root.resourceSelectors?.[key] ?? [], ...selectors.map((selector) => key === "extensions" ? canonicalExtensionSelector(selector, cwd) : selector)];
|
|
4697
|
+
}
|
|
4698
|
+
if (original.contextFiles !== void 0) configuration.contextFiles = [...validateContextFileScopes(original.contextFiles, "agent options") ?? []];
|
|
4699
|
+
const hooks = [...this.root.agentPreparationHooks ?? []].sort(byPriorityThenName);
|
|
4700
|
+
const load = this.root.extensionLoad;
|
|
4701
|
+
if (load?.errors.length) {
|
|
4702
|
+
const loaded = new Set(load.loaded.map(extensionIdentity));
|
|
4703
|
+
const declared = new Set(hooks.flatMap(({ optionsSchema, source }) => source !== void 0 && loaded.has(extensionIdentity(source)) && object(optionsSchema?.properties) ? Object.keys(optionsSchema.properties) : []));
|
|
4704
|
+
const unowned = Object.keys(original).find((key) => !isCoreAgentOption(key) && !declared.has(key));
|
|
4705
|
+
if (unowned !== void 0) throw new WorkflowError("INVALID_METADATA", `Agent option ${unowned} has no owner proven to have loaded while extensions failed to load: ${load.errors.join("; ")}`);
|
|
4706
|
+
}
|
|
4707
|
+
for (const hook of hooks) if (hook.optionsSchema !== void 0 && !Value2.Check(hook.optionsSchema, original)) throw new WorkflowError("INVALID_METADATA", `Agent options do not match preparation schema: ${hook.name}`);
|
|
4708
|
+
if (typeof original.model === "string" && this.root.blockedAliases?.has(original.model.split(":")[0] ?? "")) throw new WorkflowError("UNKNOWN_MODEL", `Unknown model alias ${original.model} resolved to ${this.root.blockedAliasTargets?.[original.model.split(":")[0] ?? ""] ?? "an unavailable target"}`);
|
|
4709
|
+
for (const hook of hooks) {
|
|
4710
|
+
if (signal.aborted) throw new WorkflowError("CANCELLED", "Agent preparation cancelled");
|
|
4711
|
+
await hook.prepare(configuration, context);
|
|
4712
|
+
}
|
|
4713
|
+
if (signal.aborted) throw new WorkflowError("CANCELLED", "Agent preparation cancelled");
|
|
4714
|
+
const { model: preparedModel, ...preparedOptions } = configuration;
|
|
4715
|
+
validateAgentOptions(preparedOptions);
|
|
4716
|
+
if (preparedModel !== void 0 && (typeof preparedModel !== "string" || !preparedModel.trim())) throw new WorkflowError("INVALID_METADATA", "Prepared agent model must be a non-empty string");
|
|
4717
|
+
if (preparedModel && this.root.blockedAliases?.has(preparedModel.split(":")[0] ?? "")) throw new WorkflowError("UNKNOWN_MODEL", `Unknown model alias ${preparedModel} resolved to ${this.root.blockedAliasTargets?.[preparedModel.split(":")[0] ?? ""] ?? "an unavailable target"}`);
|
|
4718
|
+
configuration.extensions = configuration.extensions.map((selector) => canonicalExtensionSelector(selector, cwd));
|
|
4719
|
+
const model = physicalModel(preparedModel === void 0 ? this.root.model : resolveModelReference(preparedModel, this.root.modelAliases, this.root.knownModels, this.root.settingsPath), this.root.modelAliases, this.root.knownModels, this.root.settingsPath);
|
|
4720
|
+
const excludedTools = excludedToolNames({ agentOptions: original, ...options.inheritedExcludeTools ? { inheritedExcludeTools: options.inheritedExcludeTools } : {} });
|
|
4721
|
+
const tools = selectResourcesByLayers([configuration.tools], capabilities.tools).filter((tool) => !excludedTools.has(tool));
|
|
4722
|
+
for (const pattern of validateSelectorList(original.tools, "agent options", "tools", "INVALID_METADATA", false) ?? []) if (!pattern.startsWith("!") && !resourcePatternHasMagic(pattern) && !capabilities.tools.includes(pattern)) throw new WorkflowError("UNKNOWN_TOOL", `Tool is outside the prepared agent policy: ${pattern}`);
|
|
4723
|
+
for (const pattern of unmatchedResourcePatterns(configuration.tools, capabilities.tools)) this.root.onResourceWarning?.(`Tool selector currently matches no authorized tool: ${pattern}`);
|
|
4724
|
+
const skills = selectResourcesByLayers([configuration.skills], capabilities.skills);
|
|
4725
|
+
const extensions = selectResourcesByLayers([configuration.extensions], capabilities.extensions);
|
|
4726
|
+
const concrete = validatePreparedConfiguration(root, { ...configuration, model, tools, excludeTools: [...excludedTools], skills: [...configuration.skills, "!*", ...skills], extensions: [...configuration.extensions, "!*", ...extensions], projectTrusted: context.projectTrusted }, capabilities, cwd, false);
|
|
4727
|
+
if (mode === "execution") {
|
|
4728
|
+
if (persistedKey && this.root.runStore?.saveAgentConfiguration) await this.root.runStore.saveAgentConfiguration(persistedKey, concrete);
|
|
4729
|
+
await options.onConfiguration?.(concrete);
|
|
4730
|
+
}
|
|
4731
|
+
return concrete;
|
|
4780
4732
|
}
|
|
4781
4733
|
async execute(task, options, signal, customTools = [], setSteer, beforeRetry) {
|
|
4782
4734
|
const executionSignal = signal ?? this.root.runContext?.signal;
|
|
4783
4735
|
if (!Number.isInteger(options.retries ?? 0) || (options.retries ?? 0) < 0) throw new WorkflowError("INVALID_METADATA", "retries must be a non-negative integer");
|
|
4784
4736
|
if (options.timeoutMs !== void 0 && options.timeoutMs !== null && (!Number.isInteger(options.timeoutMs) || options.timeoutMs <= 0)) throw new WorkflowError("INVALID_METADATA", "timeoutMs must be null or a positive integer");
|
|
4785
|
-
let resolved
|
|
4737
|
+
let resolved;
|
|
4786
4738
|
let recoveryModel;
|
|
4787
4739
|
let cwd;
|
|
4788
4740
|
if (options.parent) {
|
|
@@ -4803,6 +4755,16 @@ var WorkflowAgentExecutor = class {
|
|
|
4803
4755
|
if (options.cwd) throw new WorkflowError("INVALID_METADATA", "Only child agents or worktree scopes may provide a cwd");
|
|
4804
4756
|
cwd = this.root.cwd;
|
|
4805
4757
|
}
|
|
4758
|
+
const configuration = await this.prepare(options, cwd, executionSignal);
|
|
4759
|
+
const parentSkills = options.parent === void 0 ? void 0 : options.capabilities?.skills;
|
|
4760
|
+
options = { ...options, label: configuration.label ?? options.label, configuration, ...parentSkills ? { parentSkills } : {}, capabilities: { tools: configuration.tools, skills: configuration.skills.slice(configuration.skills.lastIndexOf("!*") + 1), extensions: configuration.extensions.slice(configuration.extensions.lastIndexOf("!*") + 1) } };
|
|
4761
|
+
resolved = this.resolve(options);
|
|
4762
|
+
if (!resolved.tools.includes("agent")) customTools = customTools.filter(({ name }) => name !== "agent" && name !== "get_subagent_result" && name !== "steer_subagent");
|
|
4763
|
+
let publishLoadedSkills;
|
|
4764
|
+
customTools = customTools.map((tool) => tool.name !== "agent" ? tool : { ...tool, execute: (...args) => {
|
|
4765
|
+
publishLoadedSkills?.();
|
|
4766
|
+
return tool.execute(...args);
|
|
4767
|
+
} });
|
|
4806
4768
|
const attempts = [];
|
|
4807
4769
|
let maxAttempts = (options.retries ?? 0) + 1;
|
|
4808
4770
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
@@ -4832,7 +4794,7 @@ var WorkflowAgentExecutor = class {
|
|
|
4832
4794
|
const resultTool = defineTool({
|
|
4833
4795
|
name: "workflow_result",
|
|
4834
4796
|
label: "Workflow Result",
|
|
4835
|
-
description: "Submit the terminal workflow result",
|
|
4797
|
+
description: options.schema ? "Submit the terminal workflow result. Call exactly once to finish the current task or message, with a value matching the required schema. Do not return prose as the final result." : 'Submit the terminal workflow result. Call exactly once to finish the current task or message, with { result: "final response" }. Do not return prose as the final result.',
|
|
4836
4798
|
parameters: resultSchema.Type(),
|
|
4837
4799
|
async execute(_id, value) {
|
|
4838
4800
|
if (!resultSchema.Check(value) || !jsonValue(value)) return { content: [{ type: "text", text: "Result does not match the required schema." }], details: {}, isError: true };
|
|
@@ -4864,11 +4826,23 @@ var WorkflowAgentExecutor = class {
|
|
|
4864
4826
|
} : void 0;
|
|
4865
4827
|
try {
|
|
4866
4828
|
setupFailed = true;
|
|
4867
|
-
const prepared = await prepareAgentSetup(this.root, this.transport, task, options, resolved, cwd, attempt, attemptSignal, customTools, resultTool, false
|
|
4829
|
+
const prepared = await prepareAgentSetup(this.root, this.transport, task, options, resolved, cwd, attempt, attemptSignal, customTools, resultTool, false);
|
|
4868
4830
|
setup = prepared.setup;
|
|
4869
4831
|
const attemptSetup = prepared.setup;
|
|
4870
4832
|
setupSummary = prepared.summary;
|
|
4871
4833
|
if (prepared.failure) throw prepared.failure.error;
|
|
4834
|
+
let attemptCapabilities;
|
|
4835
|
+
if (options.agentNodeId !== void 0) {
|
|
4836
|
+
const policy = setup.prepared.resourcePolicy;
|
|
4837
|
+
const sources = policy?.selectorSources;
|
|
4838
|
+
const capabilities = policy?.capabilities ?? options.capabilities;
|
|
4839
|
+
attemptCapabilities = {
|
|
4840
|
+
tools: setup.prepared.tools,
|
|
4841
|
+
skills: selectResourcesByLayers(sources ? resourceSelectorLayers(sources, "skills") : [], capabilities?.skills ?? []),
|
|
4842
|
+
extensions: selectResourcesByLayers(sources ? resourceSelectorLayers(sources, "extensions") : [], capabilities?.extensions ?? [])
|
|
4843
|
+
};
|
|
4844
|
+
this.root.onAgentCapabilities?.(options.agentNodeId, attemptCapabilities, policy?.projectTrusted ?? false);
|
|
4845
|
+
}
|
|
4872
4846
|
setupFailed = false;
|
|
4873
4847
|
if (attemptSignal.aborted) throw new WorkflowError("CANCELLED", "Agent cancelled");
|
|
4874
4848
|
await options.onAttempt?.({ attempt, transport: attemptSetup.transport.id, accounting: zeroAccounting(), setup: setupSummary });
|
|
@@ -4904,6 +4878,12 @@ var WorkflowAgentExecutor = class {
|
|
|
4904
4878
|
}
|
|
4905
4879
|
}
|
|
4906
4880
|
if (policy) setupSummary = { ...setupSummary, resourceSelectors: { ...resourcePolicySummary(policy, attemptSetup.sessionInput.tools), ...inspection ? { skills: [...inspection.skills], extensions: [...inspection.extensions] } : {} } };
|
|
4881
|
+
const nodeId = options.agentNodeId, selected = attemptCapabilities, live = session;
|
|
4882
|
+
publishLoadedSkills = nodeId === void 0 || selected === void 0 || live?.getResourceInspection === void 0 ? void 0 : () => {
|
|
4883
|
+
const loaded = live.getResourceInspection?.();
|
|
4884
|
+
if (loaded) this.root.onAgentCapabilities?.(nodeId, { ...selected, skills: [...loaded.skills] }, attemptSetup.prepared.resourcePolicy?.projectTrusted ?? false);
|
|
4885
|
+
};
|
|
4886
|
+
publishLoadedSkills?.();
|
|
4907
4887
|
},
|
|
4908
4888
|
onBeforeComplete: async () => {
|
|
4909
4889
|
if (options.worktreeOwner) await runStore?.snapshotWorktree(options.worktreeOwner);
|
|
@@ -4939,6 +4919,7 @@ var WorkflowAgentExecutor = class {
|
|
|
4939
4919
|
const runtimeRun = { id: transportBase.run.runId, namespaceId: transportBase.run.sessionId, workflowName: transportBase.run.workflow.name };
|
|
4940
4920
|
const providerRecovery = options.providerErrorRecovery;
|
|
4941
4921
|
const runtimeAgent = { id: transportBase.identity.callSite, structuralPath: [...transportBase.identity.structuralPath], ...options.parent ? { parentId: options.parent } : {} };
|
|
4922
|
+
const attemptControl = attemptControlText(options, attempt, attempts.at(-1)?.error?.message);
|
|
4942
4923
|
const runtimeRequest = {
|
|
4943
4924
|
task: setup.prepared.initialPrompt ?? setup.prompt,
|
|
4944
4925
|
cwd: setup.prepared.cwd,
|
|
@@ -4947,6 +4928,7 @@ var WorkflowAgentExecutor = class {
|
|
|
4947
4928
|
// Pi sessions execute the prepared native definitions; the executor does not duplicate them as neutral request tools.
|
|
4948
4929
|
customTools: [],
|
|
4949
4930
|
...options.schema ? { resultSchema: options.schema } : {},
|
|
4931
|
+
...attemptControl ? { turnControl: attemptControl } : {},
|
|
4950
4932
|
run: runtimeRun,
|
|
4951
4933
|
agent: runtimeAgent,
|
|
4952
4934
|
signal: attemptSignal,
|
|
@@ -5033,7 +5015,7 @@ import { SettingsManager as SettingsManager2, truncateToVisualLines as truncateT
|
|
|
5033
5015
|
import { spawn } from "node:child_process";
|
|
5034
5016
|
import { mkdtemp, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
5035
5017
|
import { tmpdir } from "node:os";
|
|
5036
|
-
import { join as
|
|
5018
|
+
import { join as join6 } from "node:path";
|
|
5037
5019
|
function workflowPromptArtifact(prompt) {
|
|
5038
5020
|
return { extension: ".md", content: prompt };
|
|
5039
5021
|
}
|
|
@@ -5044,23 +5026,23 @@ function workflowResultArtifact(value) {
|
|
|
5044
5026
|
async function spawnWorkflowEditor(command, path) {
|
|
5045
5027
|
const [editor, ...editorArgs] = command.split(" ");
|
|
5046
5028
|
if (!editor) return null;
|
|
5047
|
-
return new Promise((
|
|
5029
|
+
return new Promise((resolve5) => {
|
|
5048
5030
|
try {
|
|
5049
5031
|
const child = spawn(editor, [...editorArgs, path], { stdio: "inherit", shell: process.platform === "win32" });
|
|
5050
5032
|
child.once("error", () => {
|
|
5051
|
-
|
|
5033
|
+
resolve5(null);
|
|
5052
5034
|
});
|
|
5053
5035
|
child.once("close", (code) => {
|
|
5054
|
-
|
|
5036
|
+
resolve5(code);
|
|
5055
5037
|
});
|
|
5056
5038
|
} catch {
|
|
5057
|
-
|
|
5039
|
+
resolve5(null);
|
|
5058
5040
|
}
|
|
5059
5041
|
});
|
|
5060
5042
|
}
|
|
5061
5043
|
async function openWorkflowArtifact(tui, command, artifact) {
|
|
5062
|
-
const directory = await mkdtemp(
|
|
5063
|
-
const path =
|
|
5044
|
+
const directory = await mkdtemp(join6(tmpdir(), "pi-workflow-editor-"));
|
|
5045
|
+
const path = join6(directory, `artifact${artifact.extension}`);
|
|
5064
5046
|
try {
|
|
5065
5047
|
await writeFile3(path, artifact.content, { encoding: "utf8", mode: 384 });
|
|
5066
5048
|
tui.stop();
|
|
@@ -5172,6 +5154,15 @@ function formatWorkflowRuntime(durationMs) {
|
|
|
5172
5154
|
const remainingMinutes = minutes % 60;
|
|
5173
5155
|
return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
|
|
5174
5156
|
}
|
|
5157
|
+
function textBlock(text) {
|
|
5158
|
+
return {
|
|
5159
|
+
render(width) {
|
|
5160
|
+
return text.split("\n").map((line) => truncateToWidth2(line, Math.max(1, width), "\u2026"));
|
|
5161
|
+
},
|
|
5162
|
+
invalidate() {
|
|
5163
|
+
}
|
|
5164
|
+
};
|
|
5165
|
+
}
|
|
5175
5166
|
var ANSI_SGR_SOURCE = `${String.fromCharCode(27)}\\[[0-9;]*m`;
|
|
5176
5167
|
var ANSI_SGR = new RegExp(ANSI_SGR_SOURCE);
|
|
5177
5168
|
function themeWorkflowProgressStyles(theme) {
|
|
@@ -5215,13 +5206,13 @@ function elapsedDurationMs(agent, end) {
|
|
|
5215
5206
|
return agent.startedAt === void 0 || !Number.isFinite(agent.startedAt) ? void 0 : Math.max(0, end - agent.startedAt);
|
|
5216
5207
|
}
|
|
5217
5208
|
function agentActivityLabel(agent) {
|
|
5218
|
-
const
|
|
5219
|
-
if (
|
|
5220
|
-
const name =
|
|
5221
|
-
const text = sanitizeDisplayText(
|
|
5209
|
+
const activity = agent.activity;
|
|
5210
|
+
if (activity?.kind === "reasoning" || activity?.kind === "text") {
|
|
5211
|
+
const name = activity.kind === "reasoning" ? "reasoning" : "responding";
|
|
5212
|
+
const text = sanitizeDisplayText(activity.text);
|
|
5222
5213
|
return text && text !== name ? `${name} \xB7 ${text}` : name;
|
|
5223
5214
|
}
|
|
5224
|
-
if (
|
|
5215
|
+
if (activity?.kind === "tool") return sanitizeDisplayText(activity.text);
|
|
5225
5216
|
return sanitizeDisplayText([...agent.toolCalls ?? []].reverse().find(({ state }) => state === "running")?.name ?? "");
|
|
5226
5217
|
}
|
|
5227
5218
|
function formatAccountingValue(value) {
|
|
@@ -5242,7 +5233,6 @@ function formatAgentDetail(agent, styles = PLAIN_WORKFLOW_PROGRESS_STYLES, now =
|
|
|
5242
5233
|
`State: ${state(agent.state)}`,
|
|
5243
5234
|
...agent.structuralPath?.length ? [`Structural path: ${agent.structuralPath.join(" > ")}`] : [],
|
|
5244
5235
|
...model === void 0 ? [] : [`Model: ${model}`],
|
|
5245
|
-
`Role: ${agent.role ?? "(none)"}`,
|
|
5246
5236
|
`Tools: ${tools}`,
|
|
5247
5237
|
...agent.attempts !== void 0 && Number.isFinite(agent.attempts) && agent.attempts > 1 ? [`Attempts: ${String(agent.attempts)}`] : [],
|
|
5248
5238
|
...duration === void 0 ? [] : [`Duration: ${formatWorkflowRuntime(duration)}`],
|
|
@@ -5285,7 +5275,9 @@ function formatNavigatorColumns(sidebar, detail, width, layout = {}) {
|
|
|
5285
5275
|
if (safeWidth >= 80) {
|
|
5286
5276
|
const sidebarWidth = Math.min(42, Math.max(24, Math.floor((safeWidth - 3) * 0.38)));
|
|
5287
5277
|
const left2 = sidebar.flatMap((line) => wrapNavigatorLine(line, sidebarWidth));
|
|
5288
|
-
const
|
|
5278
|
+
const wrapped = detail.flatMap((line) => wrapNavigatorLine(line, safeWidth - sidebarWidth - 3));
|
|
5279
|
+
const shift = Math.min(Math.max(0, layout.detailOffset ?? 0), Math.max(0, left2.length - wrapped.length));
|
|
5280
|
+
const right = [...Array(shift).fill(""), ...wrapped];
|
|
5289
5281
|
return Array.from({ length: Math.max(left2.length, right.length) }, (_, index) => {
|
|
5290
5282
|
const cell = left2[index] ?? "";
|
|
5291
5283
|
return `${cell}${" ".repeat(Math.max(0, sidebarWidth - visibleWidth2(cell)))} | ${right[index] ?? ""}`;
|
|
@@ -5330,8 +5322,8 @@ function createInlineConfirm(requestRender) {
|
|
|
5330
5322
|
return {
|
|
5331
5323
|
ask(title, message) {
|
|
5332
5324
|
answer(false);
|
|
5333
|
-
return new Promise((
|
|
5334
|
-
pending = { title, message, yes: true, resolve:
|
|
5325
|
+
return new Promise((resolve5) => {
|
|
5326
|
+
pending = { title, message, yes: true, resolve: resolve5 };
|
|
5335
5327
|
requestRender();
|
|
5336
5328
|
});
|
|
5337
5329
|
},
|
|
@@ -5362,11 +5354,11 @@ function createInlineConfirm(requestRender) {
|
|
|
5362
5354
|
}
|
|
5363
5355
|
|
|
5364
5356
|
// src/trajectory.ts
|
|
5365
|
-
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager as DefaultPackageManager2, getAgentDir as
|
|
5357
|
+
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager as DefaultPackageManager2, getAgentDir as getAgentDir3, SettingsManager as SettingsManager3, DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES2 } from "@earendil-works/pi-coding-agent";
|
|
5366
5358
|
|
|
5367
5359
|
// subagents/src/contracts.ts
|
|
5368
5360
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
5369
|
-
import { Value as
|
|
5361
|
+
import { Value as Value3 } from "typebox/value";
|
|
5370
5362
|
var SUBAGENTS_TOOL_NAMES = [
|
|
5371
5363
|
"subagents_run",
|
|
5372
5364
|
"subagents_inspect",
|
|
@@ -5387,17 +5379,18 @@ var SUBAGENTS_RUN_PARAMETERS = Type2.Object({
|
|
|
5387
5379
|
label: Type2.Optional(Type2.String({ description: "Optional display label for the subagent" })),
|
|
5388
5380
|
model: Type2.Optional(Type2.String({ description: "Optional model as provider/model:thinking or alias[:thinking]" })),
|
|
5389
5381
|
tools: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional ordered tool selectors; candidates start enabled and !* restricts the set" })),
|
|
5382
|
+
excludeTools: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional exact tool names removed after preparation; no patterns, cannot remove workflow_result" })),
|
|
5390
5383
|
skills: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional ordered skill selectors; candidates start enabled and !* restricts the set" })),
|
|
5391
5384
|
extensions: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional ordered extension selectors; candidates start enabled and !* restricts the set" })),
|
|
5392
5385
|
contextFiles: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional context-file scopes: global, project, cwd" })),
|
|
5393
|
-
role: Type2.Optional(Type2.String({ description: "Workflow role name" })),
|
|
5394
5386
|
worktree: Type2.Optional(Type2.String({ description: "Optional named worktree; requires a clean working tree" })),
|
|
5395
5387
|
outputSchema: Type2.Optional(Type2.Record(Type2.String(), Type2.Unknown(), { description: "Optional JSON schema for the result" })),
|
|
5396
5388
|
retries: Type2.Optional(Type2.Integer({ minimum: 0, maximum: SUBAGENT_MAX_RETRIES, description: "Optional retry count; at most 255 retries" })),
|
|
5397
5389
|
timeoutMs: Type2.Optional(Type2.Union([Type2.Integer({ minimum: 1, description: "Optional execution timeout in milliseconds" }), Type2.Null()]))
|
|
5398
|
-
}, { additionalProperties:
|
|
5390
|
+
}, { additionalProperties: true });
|
|
5399
5391
|
var SUBAGENTS_INSPECT_PARAMETERS = Type2.Object({
|
|
5400
|
-
id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" }))
|
|
5392
|
+
id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" })),
|
|
5393
|
+
scope: Type2.Optional(Type2.Union([Type2.Literal("session"), Type2.Literal("all")], { description: "List scope when id is omitted: session (default) lists runs from the current Pi session; all lists every stored run" }))
|
|
5401
5394
|
}, { additionalProperties: false });
|
|
5402
5395
|
var SUBAGENTS_ID_PARAMETERS = Type2.Object({
|
|
5403
5396
|
id: Type2.String({ description: "Subagent ID" })
|
|
@@ -5412,15 +5405,23 @@ var SUBAGENTS_STOP_PARAMETERS = Type2.Object({
|
|
|
5412
5405
|
var SUBAGENTS_RETRY_PARAMETERS = Type2.Object({
|
|
5413
5406
|
id: Type2.String({ description: "Failed or stopped subagent ID to retry" })
|
|
5414
5407
|
}, { additionalProperties: false });
|
|
5415
|
-
var
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5408
|
+
var SUBAGENT_STATUS_OUTPUT = Type2.Object({
|
|
5409
|
+
id: Type2.String(),
|
|
5410
|
+
state: Type2.String(),
|
|
5411
|
+
sessionId: Type2.Optional(Type2.String()),
|
|
5412
|
+
startedAt: Type2.Optional(Type2.Number()),
|
|
5413
|
+
finishedAt: Type2.Optional(Type2.Number()),
|
|
5414
|
+
attempts: Type2.Optional(Type2.Number()),
|
|
5415
|
+
attemptDetails: Type2.Optional(Type2.Array(Type2.Unknown())),
|
|
5416
|
+
worktree: Type2.Optional(Type2.Object({ path: Type2.String(), branch: Type2.String() })),
|
|
5417
|
+
error: Type2.Optional(Type2.Object({ code: Type2.String(), message: Type2.String() })),
|
|
5418
|
+
progress: Type2.Optional(Type2.Unknown()),
|
|
5419
|
+
value: Type2.Optional(Type2.Unknown())
|
|
5420
|
+
});
|
|
5421
|
+
var SUBAGENTS_INSPECT_OUTPUT = Type2.Union([SUBAGENT_STATUS_OUTPUT, Type2.Array(Type2.Object({ id: Type2.String(), state: Type2.String() }))]);
|
|
5422
|
+
var SUBAGENTS_STEER_OUTPUT = Type2.Object({ id: Type2.String(), accepted: Type2.Boolean() });
|
|
5422
5423
|
function normalizeSubagentRunRequest(value) {
|
|
5423
|
-
if (!
|
|
5424
|
+
if (!Value3.Check(SUBAGENTS_RUN_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_run parameters");
|
|
5424
5425
|
if (typeof value.worktree === "string" && !value.worktree.trim()) throw new WorkflowError("INVALID_METADATA", "worktree name must be a non-empty string");
|
|
5425
5426
|
const snapshot = structuredClone(value);
|
|
5426
5427
|
snapshot.mode ??= "background";
|
|
@@ -5510,8 +5511,8 @@ function progressValue(value) {
|
|
|
5510
5511
|
const accounting2 = accountingValue(record2.accounting);
|
|
5511
5512
|
const toolCalls = toolCallsValue(record2.toolCalls);
|
|
5512
5513
|
if (!accounting2 || !toolCalls) return void 0;
|
|
5513
|
-
const
|
|
5514
|
-
if (record2.activity !== void 0 &&
|
|
5514
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5515
|
+
if (record2.activity !== void 0 && activity === void 0) return void 0;
|
|
5515
5516
|
const rawState = record2.state;
|
|
5516
5517
|
let state;
|
|
5517
5518
|
if (rawState !== void 0) {
|
|
@@ -5525,16 +5526,16 @@ function progressValue(value) {
|
|
|
5525
5526
|
}
|
|
5526
5527
|
const lastEventAt = record2.lastEventAt;
|
|
5527
5528
|
if (lastEventAt !== void 0 && !nonnegativeInteger(lastEventAt)) return void 0;
|
|
5528
|
-
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...
|
|
5529
|
+
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...activity === void 0 ? {} : { activity }, ...lastEventAt === void 0 ? {} : { lastEventAt } };
|
|
5529
5530
|
}
|
|
5530
5531
|
function legacyProgressValue(record2) {
|
|
5531
5532
|
const accounting2 = legacyAccountingValue(record2);
|
|
5532
5533
|
const toolCalls = record2.toolCalls === void 0 ? [] : toolCallsValue(record2.toolCalls);
|
|
5533
5534
|
if (!accounting2 || !toolCalls) return void 0;
|
|
5534
|
-
const
|
|
5535
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5535
5536
|
const lastEventAt = record2.lastEventAt;
|
|
5536
|
-
if (record2.activity !== void 0 &&
|
|
5537
|
-
return { accounting: accounting2, toolCalls, ...
|
|
5537
|
+
if (record2.activity !== void 0 && activity === void 0 || lastEventAt !== void 0 && !nonnegativeInteger(lastEventAt)) return void 0;
|
|
5538
|
+
return { accounting: accounting2, toolCalls, ...activity === void 0 ? {} : { activity }, ...lastEventAt === void 0 ? {} : { lastEventAt } };
|
|
5538
5539
|
}
|
|
5539
5540
|
function attemptValue(value) {
|
|
5540
5541
|
const record2 = objectValue(value);
|
|
@@ -5618,15 +5619,29 @@ var WORKFLOW_TOOL_PARAMETERS = Type3.Object({
|
|
|
5618
5619
|
}, { additionalProperties: false });
|
|
5619
5620
|
var WORKFLOW_STATUS_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Workflow run ID visible in the current project" }) }, { additionalProperties: false });
|
|
5620
5621
|
var WORKFLOW_RETRY_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Explicit failed workflow run ID" }), expectedState: Type3.Optional(Type3.String({ description: "Persisted source state observed before recovery" })), foreground: Type3.Optional(Type3.Boolean({ description: "Override the source launch mode for this recovery" })) });
|
|
5622
|
+
var WORKFLOW_ERROR_OUTPUT = Type3.Object({ code: Type3.String(), message: Type3.String() });
|
|
5623
|
+
var WORKFLOW_LAUNCH_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), detached: Type3.Optional(Type3.Boolean()), value: Type3.Optional(Type3.Unknown()) });
|
|
5624
|
+
var WORKFLOW_STATUS_OUTPUT = Type3.Object({ runId: Type3.String(), workflowName: Type3.String(), state: Type3.String(), error: Type3.Optional(WORKFLOW_ERROR_OUTPUT), failedAt: Type3.Optional(Type3.String()), budget: Type3.Optional(Type3.Unknown()), usage: Type3.Optional(Type3.Unknown()), phase: Type3.Optional(Type3.String()), delivery: Type3.Optional(Type3.Object({ mode: Type3.String(), state: Type3.String() })), agents: Type3.Array(Type3.Object({ id: Type3.String(), label: Type3.Optional(Type3.String()), path: Type3.String(), state: Type3.String(), lastEventAt: Type3.Optional(Type3.Number()), accounting: Type3.Optional(Type3.Unknown()) })) });
|
|
5625
|
+
var WORKFLOW_STOP_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), stopped: Type3.Boolean(), reason: Type3.Optional(Type3.String()) });
|
|
5626
|
+
var WORKFLOW_RECOVERY_OUTPUT = Type3.Object({ state: Type3.String(), runId: Type3.Optional(Type3.String()), parentRunId: Type3.Optional(Type3.String()), proposalId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
|
|
5627
|
+
var WORKFLOW_RESPOND_OUTPUT = Type3.Object({ state: Type3.String(), approved: Type3.Boolean(), reason: Type3.String(), accepted: Type3.Optional(Type3.Boolean()), runId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
|
|
5628
|
+
var WORKFLOW_CATALOG_ALIAS_OUTPUT = Type3.Object({ name: Type3.String(), kind: Type3.String(), provenance: Type3.String(), version: Type3.Optional(Type3.String()), headline: Type3.Optional(Type3.String()) });
|
|
5629
|
+
var WORKFLOW_CATALOG_OUTPUT = Type3.Union([
|
|
5630
|
+
Type3.Object({ functions: Type3.Array(Type3.Object({ name: Type3.String(), description: Type3.String(), input: Type3.Unknown() })), modelAliasEntries: Type3.Optional(Type3.Array(WORKFLOW_CATALOG_ALIAS_OUTPUT)), settings: Type3.Optional(Type3.Unknown()) }),
|
|
5631
|
+
Type3.Object({ name: Type3.String(), version: Type3.String(), headline: Type3.String(), description: Type3.String(), input: Type3.Unknown(), output: Type3.Unknown() }),
|
|
5632
|
+
WORKFLOW_CATALOG_ALIAS_OUTPUT,
|
|
5633
|
+
Type3.Object({ error: Type3.Object({ code: Type3.String(), name: Type3.String(), message: Type3.String() }) })
|
|
5634
|
+
]);
|
|
5635
|
+
var SCRIPT_CALL_TEXT_LIMIT_BYTES = 16 * 1024;
|
|
5621
5636
|
|
|
5622
5637
|
// subagents/src/manager.ts
|
|
5623
5638
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
5624
5639
|
import { chmod, link as link2, mkdir as mkdir3, open as open2, readFile as readFile4, readdir as readdir3, rename as rename4, rm as rm5, stat as stat3 } from "node:fs/promises";
|
|
5625
|
-
import { join as
|
|
5626
|
-
import { getAgentDir as
|
|
5640
|
+
import { join as join8 } from "node:path";
|
|
5641
|
+
import { getAgentDir as getAgentDir5 } from "@earendil-works/pi-coding-agent";
|
|
5627
5642
|
|
|
5628
5643
|
// subagents/src/worktree.ts
|
|
5629
|
-
import { join as
|
|
5644
|
+
import { join as join7 } from "node:path";
|
|
5630
5645
|
function syntheticRun(context) {
|
|
5631
5646
|
return {
|
|
5632
5647
|
id: context.runId,
|
|
@@ -5649,7 +5664,7 @@ function syntheticSnapshot(context) {
|
|
|
5649
5664
|
...context.worktreePostCreateCommandSource === void 0 ? {} : { settingsSources: { concurrency: "subagents", modelAliases: "subagents", worktreePostCreateCommand: context.worktreePostCreateCommandSource } },
|
|
5650
5665
|
models: [],
|
|
5651
5666
|
tools: [],
|
|
5652
|
-
|
|
5667
|
+
agentConfigurations: {},
|
|
5653
5668
|
schemas: []
|
|
5654
5669
|
};
|
|
5655
5670
|
}
|
|
@@ -5688,7 +5703,7 @@ function createRunStoreWorktreeAdapter(home) {
|
|
|
5688
5703
|
};
|
|
5689
5704
|
}
|
|
5690
5705
|
function defaultWorktreeHome(storageDirectory3) {
|
|
5691
|
-
return
|
|
5706
|
+
return join7(storageDirectory3, ".worktrees");
|
|
5692
5707
|
}
|
|
5693
5708
|
|
|
5694
5709
|
// subagents/src/manager.ts
|
|
@@ -5728,22 +5743,11 @@ function rootModel(context) {
|
|
|
5728
5743
|
const thinking = context.extensionContext.thinkingLevel;
|
|
5729
5744
|
return { provider: model.provider, model: model.id, ...thinking === void 0 ? {} : { thinking } };
|
|
5730
5745
|
}
|
|
5731
|
-
function
|
|
5732
|
-
return trustedProject && external.projectExtensionSettings !== void 0 ? workflowProjectSettingsPath(cwd) : workflowSettingsPath(agentDir);
|
|
5733
|
-
}
|
|
5734
|
-
function validateStandaloneExtensionSources(context, external, registry, agentDir) {
|
|
5735
|
-
const cwd = context.extensionContext.cwd;
|
|
5736
|
-
const globalSettingsPath = workflowSettingsPath(agentDir);
|
|
5737
|
-
const trustedProject = context.extensionContext.isProjectTrusted();
|
|
5738
|
-
if (external.globalExtensionSettings !== void 0) registry.validateExtensionSettings(external.globalExtensionSettings, { source: "global", cwd, projectTrusted: trustedProject, settingsPath: globalSettingsPath });
|
|
5739
|
-
if (trustedProject && external.projectExtensionSettings !== void 0) registry.validateExtensionSettings(external.projectExtensionSettings, { source: "project", cwd, projectTrusted: true, settingsPath: workflowProjectSettingsPath(cwd) });
|
|
5740
|
-
}
|
|
5741
|
-
function executionRoot(context, dependencies, signal, runId, worktree, external) {
|
|
5746
|
+
function executionRoot(context, dependencies, signal, runId, worktree) {
|
|
5742
5747
|
const extensionContext = context.extensionContext;
|
|
5743
5748
|
const model = rootModel(context);
|
|
5744
|
-
const agentDir = dependencies.agentDir ??
|
|
5749
|
+
const agentDir = dependencies.agentDir ?? getAgentDir5();
|
|
5745
5750
|
const currentTrustedProject = extensionContext.isProjectTrusted();
|
|
5746
|
-
if (external.projectTrusted && !currentTrustedProject) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
|
|
5747
5751
|
const trustedProject = currentTrustedProject;
|
|
5748
5752
|
const settingsPath = workflowSettingsPath(agentDir);
|
|
5749
5753
|
const settings = resolveWorkflowSettings(extensionContext.cwd, trustedProject, settingsPath);
|
|
@@ -5766,11 +5770,13 @@ function executionRoot(context, dependencies, signal, runId, worktree, external)
|
|
|
5766
5770
|
signal
|
|
5767
5771
|
};
|
|
5768
5772
|
const registry = loadingRegistry();
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
const
|
|
5773
|
+
if (settings.global.extensionSettings !== void 0) registry.validateExtensionSettings(settings.global.extensionSettings, { source: "global", cwd: extensionContext.cwd, projectTrusted: trustedProject, settingsPath: settings.globalSettingsPath });
|
|
5774
|
+
if (trustedProject && settings.project.extensionSettings !== void 0) registry.validateExtensionSettings(settings.project.extensionSettings, { source: "project", cwd: extensionContext.cwd, projectTrusted: true, settingsPath: settings.projectSettingsPath });
|
|
5775
|
+
const extensionSettings = settings.effective.extensionSettings;
|
|
5776
|
+
const extensionSettingsPath = settings.sources.extensionSettings ?? settingsPath;
|
|
5772
5777
|
return {
|
|
5773
5778
|
cwd: extensionContext.cwd,
|
|
5779
|
+
projectTrusted: trustedProject,
|
|
5774
5780
|
model,
|
|
5775
5781
|
tools,
|
|
5776
5782
|
resourceSelectors: resourcePolicy.effective,
|
|
@@ -5780,9 +5786,10 @@ function executionRoot(context, dependencies, signal, runId, worktree, external)
|
|
|
5780
5786
|
availableModels,
|
|
5781
5787
|
knownModels,
|
|
5782
5788
|
...Object.keys(staticAliases).length ? { modelAliases: staticAliases } : {},
|
|
5789
|
+
dynamicModelAliasNames: [],
|
|
5783
5790
|
settingsPath: settings.sources.modelAliases,
|
|
5784
5791
|
extensionSettingsPath,
|
|
5785
|
-
|
|
5792
|
+
agentPreparationHooks: registry.agentPreparationHooks(),
|
|
5786
5793
|
agentSetupHooks: registry.agentSetupHooks(),
|
|
5787
5794
|
validateExtensionSettings: registry.validateExtensionSettings,
|
|
5788
5795
|
agentResourcePolicy: () => structuredClone(resourcePolicy),
|
|
@@ -5798,11 +5805,10 @@ async function addDynamicAliases(context, signal, root) {
|
|
|
5798
5805
|
const trustedProject = resourcePolicy?.projectTrusted ?? context.extensionContext.isProjectTrusted();
|
|
5799
5806
|
const dynamicAliases = await registry.resolveModelAliases({ cwd: context.extensionContext.cwd, projectTrusted: trustedProject, rootModel: root.model, knownModels: root.knownModels ?? /* @__PURE__ */ new Set(), availableModels: root.availableModels ?? /* @__PURE__ */ new Set(), signal }, new Set(Object.keys(staticAliases)));
|
|
5800
5807
|
validateModelAliasAvailability(dynamicAliases, Object.keys(dynamicAliases), root.availableModels ?? /* @__PURE__ */ new Set(), root.knownModels ?? /* @__PURE__ */ new Set(), root.settingsPath);
|
|
5801
|
-
return { ...root, modelAliases: { ...dynamicAliases, ...staticAliases } };
|
|
5808
|
+
return { ...root, modelAliases: { ...dynamicAliases, ...staticAliases }, dynamicModelAliasNames: Object.keys(dynamicAliases) };
|
|
5802
5809
|
}
|
|
5803
5810
|
function executionOptions(request, onAttempt, onProgress) {
|
|
5804
|
-
const
|
|
5805
|
-
const label2 = request.label ?? roleNameOf(role) ?? "subagent";
|
|
5811
|
+
const label2 = request.label ?? "subagent";
|
|
5806
5812
|
return {
|
|
5807
5813
|
label: label2,
|
|
5808
5814
|
workflowName: WORKFLOW_NAME,
|
|
@@ -5811,35 +5817,35 @@ function executionOptions(request, onAttempt, onProgress) {
|
|
|
5811
5817
|
...request.skills === void 0 ? {} : { skills: request.skills },
|
|
5812
5818
|
...request.extensions === void 0 ? {} : { extensions: request.extensions },
|
|
5813
5819
|
...request.contextFiles === void 0 ? {} : { contextFiles: request.contextFiles },
|
|
5814
|
-
...role === void 0 ? {} : { role },
|
|
5815
5820
|
...request.worktree === void 0 ? {} : { worktreeOwner: structuralPath("worktree", "named", request.worktree) },
|
|
5816
5821
|
...request.outputSchema === void 0 ? {} : { schema: request.outputSchema },
|
|
5817
5822
|
...request.retries === void 0 ? {} : { retries: request.retries },
|
|
5818
5823
|
...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
|
|
5824
|
+
agentOptions: Object.fromEntries(Object.entries(request).filter(([key]) => !["prompt", "mode", "worktree"].includes(key))),
|
|
5819
5825
|
onAttempt,
|
|
5820
5826
|
onProgress
|
|
5821
5827
|
};
|
|
5822
5828
|
}
|
|
5823
5829
|
function storageDirectory(dependencies) {
|
|
5824
|
-
return dependencies.storageDir ??
|
|
5830
|
+
return dependencies.storageDir ?? join8(dependencies.agentDir ?? getAgentDir5(), STORAGE_DIRECTORY);
|
|
5825
5831
|
}
|
|
5826
5832
|
function runDirectory(root, id) {
|
|
5827
|
-
return
|
|
5833
|
+
return join8(root, id);
|
|
5828
5834
|
}
|
|
5829
5835
|
function requestPath(directory) {
|
|
5830
|
-
return
|
|
5836
|
+
return join8(directory, "request.json");
|
|
5831
5837
|
}
|
|
5832
5838
|
function configurationPath(directory) {
|
|
5833
|
-
return
|
|
5839
|
+
return join8(directory, CONFIGURATION_FILE);
|
|
5834
5840
|
}
|
|
5835
5841
|
function statusPath(directory) {
|
|
5836
|
-
return
|
|
5842
|
+
return join8(directory, "status.json");
|
|
5837
5843
|
}
|
|
5838
5844
|
function resultPath(directory) {
|
|
5839
|
-
return
|
|
5845
|
+
return join8(directory, "result.json");
|
|
5840
5846
|
}
|
|
5841
5847
|
function failurePath(directory) {
|
|
5842
|
-
return
|
|
5848
|
+
return join8(directory, "failure.json");
|
|
5843
5849
|
}
|
|
5844
5850
|
async function secureDirectory(directory) {
|
|
5845
5851
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
@@ -5922,7 +5928,7 @@ async function releaseOwnerMarker(path, token) {
|
|
|
5922
5928
|
}
|
|
5923
5929
|
async function acquireStorageOwner(root, owner, liveness) {
|
|
5924
5930
|
await secureDirectory(root);
|
|
5925
|
-
const path =
|
|
5931
|
+
const path = join8(root, OWNER_FILE);
|
|
5926
5932
|
for (let attempt = 0; attempt < MAX_STORAGE_OWNER_ATTEMPTS; attempt += 1) {
|
|
5927
5933
|
const marker = { ...owner, acquiredAt: Date.now() };
|
|
5928
5934
|
try {
|
|
@@ -5993,32 +5999,8 @@ function checkedRequest(request) {
|
|
|
5993
5999
|
return normalizeSubagentRunRequest(request);
|
|
5994
6000
|
}
|
|
5995
6001
|
function decodePersistedStandaloneExternalConfiguration(value) {
|
|
5996
|
-
if (!object(value) ||
|
|
5997
|
-
|
|
5998
|
-
const projectExtensionSettings = value.projectExtensionSettings === void 0 ? void 0 : decodeWorkflowExtensions(value.projectExtensionSettings);
|
|
5999
|
-
if (value.globalExtensionSettings !== void 0 && globalExtensionSettings === void 0 || value.projectExtensionSettings !== void 0 && projectExtensionSettings === void 0) return void 0;
|
|
6000
|
-
if (!object(value.agentDefinitions)) return void 0;
|
|
6001
|
-
const agentDefinitions = /* @__PURE__ */ Object.create(null);
|
|
6002
|
-
for (const [name, definition] of Object.entries(value.agentDefinitions)) {
|
|
6003
|
-
const decoded = decodeAgentDefinition(definition);
|
|
6004
|
-
if (decoded === void 0) return void 0;
|
|
6005
|
-
agentDefinitions[name] = decoded;
|
|
6006
|
-
}
|
|
6007
|
-
return deepFreeze({ projectTrusted: value.projectTrusted, ...globalExtensionSettings === void 0 ? {} : { globalExtensionSettings }, ...projectExtensionSettings === void 0 ? {} : { projectExtensionSettings }, agentDefinitions });
|
|
6008
|
-
}
|
|
6009
|
-
function standaloneExternalConfiguration(context, dependencies) {
|
|
6010
|
-
const cwd = context.extensionContext.cwd;
|
|
6011
|
-
const trustedProject = context.extensionContext.isProjectTrusted();
|
|
6012
|
-
const agentDir = dependencies.agentDir ?? getAgentDir6();
|
|
6013
|
-
const resolution = resolveWorkflowSettings(cwd, trustedProject, workflowSettingsPath(agentDir));
|
|
6014
|
-
const globalExtensionSettings = resolution.global.extensionSettings;
|
|
6015
|
-
const projectExtensionSettings = resolution.project.extensionSettings;
|
|
6016
|
-
return deepFreeze({
|
|
6017
|
-
projectTrusted: trustedProject,
|
|
6018
|
-
...globalExtensionSettings === void 0 ? {} : { globalExtensionSettings: structuredClone(globalExtensionSettings) },
|
|
6019
|
-
...projectExtensionSettings === void 0 ? {} : { projectExtensionSettings: structuredClone(projectExtensionSettings) },
|
|
6020
|
-
agentDefinitions: structuredClone(loadAgentDefinitions(cwd, agentDir, trustedProject))
|
|
6021
|
-
});
|
|
6002
|
+
if (!object(value) || value.version !== 1) return void 0;
|
|
6003
|
+
return decodePreparedAgentConfiguration(value.configuration);
|
|
6022
6004
|
}
|
|
6023
6005
|
function checkedId(request) {
|
|
6024
6006
|
if (typeof request.id !== "string" || !validSubagentId(request.id)) throw new WorkflowError("RUN_NOT_FOUND", `Unknown subagent ${request.id}`);
|
|
@@ -6203,8 +6185,8 @@ function isPreparedSession(value) {
|
|
|
6203
6185
|
const textFields = ["initialPrompt", "agentDir", "systemPromptPath", "systemPromptAppend", "piRuntimeError"];
|
|
6204
6186
|
if (textFields.some((field) => record2[field] !== void 0 && typeof record2[field] !== "string")) return false;
|
|
6205
6187
|
if (record2.systemPrompt !== void 0 && (typeof record2.systemPrompt !== "string" || record2.systemPrompt.length > SUBAGENT_SYSTEM_PROMPT_LIMIT)) return false;
|
|
6206
|
-
const
|
|
6207
|
-
if (record2.piRuntime !== void 0 && (!
|
|
6188
|
+
const runtime = recordValue(record2.piRuntime);
|
|
6189
|
+
if (record2.piRuntime !== void 0 && (!runtime || typeof runtime.executable !== "string" || !runtime.executable.trim() || runtime.entrypoint !== void 0 && (typeof runtime.entrypoint !== "string" || !runtime.entrypoint.trim()))) return false;
|
|
6208
6190
|
return true;
|
|
6209
6191
|
}
|
|
6210
6192
|
function isLiveSessionHandoff(value) {
|
|
@@ -6238,19 +6220,19 @@ function progressValue2(value) {
|
|
|
6238
6220
|
const toolCalls = toolCallsValue2(record2.toolCalls);
|
|
6239
6221
|
const state = record2.state === void 0 ? void 0 : sessionStateValue(record2.state);
|
|
6240
6222
|
if (!accounting2 || !toolCalls || record2.state !== void 0 && state === void 0) return void 0;
|
|
6241
|
-
const
|
|
6242
|
-
if (record2.activity !== void 0 &&
|
|
6223
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
6224
|
+
if (record2.activity !== void 0 && activity === void 0) return void 0;
|
|
6243
6225
|
if (record2.lastEventAt !== void 0 && (!Number.isSafeInteger(record2.lastEventAt) || record2.lastEventAt < 0)) return void 0;
|
|
6244
|
-
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...
|
|
6226
|
+
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...activity === void 0 ? {} : { activity }, ...record2.lastEventAt === void 0 ? {} : { lastEventAt: record2.lastEventAt } };
|
|
6245
6227
|
}
|
|
6246
6228
|
function legacyProgressValue2(record2) {
|
|
6247
6229
|
const accounting2 = legacyAccountingValue(record2);
|
|
6248
6230
|
const toolCalls = record2.toolCalls === void 0 ? [] : toolCallsValue2(record2.toolCalls);
|
|
6249
6231
|
if (!accounting2 || !toolCalls) return void 0;
|
|
6250
|
-
const
|
|
6251
|
-
if (record2.activity !== void 0 &&
|
|
6232
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
6233
|
+
if (record2.activity !== void 0 && activity === void 0) return void 0;
|
|
6252
6234
|
if (record2.lastEventAt !== void 0 && (!Number.isSafeInteger(record2.lastEventAt) || record2.lastEventAt < 0)) return void 0;
|
|
6253
|
-
return { accounting: accounting2, toolCalls, ...
|
|
6235
|
+
return { accounting: accounting2, toolCalls, ...activity === void 0 ? {} : { activity }, ...record2.lastEventAt === void 0 ? {} : { lastEventAt: record2.lastEventAt } };
|
|
6254
6236
|
}
|
|
6255
6237
|
function worktreeContextValue(value) {
|
|
6256
6238
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -6329,7 +6311,7 @@ async function createRunStorage(root, id, request, status, external) {
|
|
|
6329
6311
|
created = true;
|
|
6330
6312
|
await chmod(directory, 448);
|
|
6331
6313
|
await atomicJson(requestPath(directory), request);
|
|
6332
|
-
await atomicJson(configurationPath(directory), external);
|
|
6314
|
+
if (external !== void 0) await atomicJson(configurationPath(directory), { version: 1, configuration: external });
|
|
6333
6315
|
await atomicJson(statusPath(directory), status);
|
|
6334
6316
|
return directory;
|
|
6335
6317
|
} catch (error) {
|
|
@@ -6511,6 +6493,7 @@ var PersistentSubagentManager = class {
|
|
|
6511
6493
|
}
|
|
6512
6494
|
dependencies;
|
|
6513
6495
|
activeRuns = /* @__PURE__ */ new Map();
|
|
6496
|
+
pendingStarts = /* @__PURE__ */ new Set();
|
|
6514
6497
|
activeRunCount = 0;
|
|
6515
6498
|
terminalSummaries = /* @__PURE__ */ new Map();
|
|
6516
6499
|
reconciliationErrors = /* @__PURE__ */ new Map();
|
|
@@ -6554,32 +6537,44 @@ var PersistentSubagentManager = class {
|
|
|
6554
6537
|
await this.ensureInitialized();
|
|
6555
6538
|
if (this.disposed) throw new WorkflowError("CANCELLED", "Subagent manager is disposed");
|
|
6556
6539
|
if (context.signal?.aborted) throw new WorkflowError("CANCELLED", "Subagent cancelled");
|
|
6557
|
-
const external = frozenExternal
|
|
6558
|
-
if (external
|
|
6540
|
+
const external = frozenExternal;
|
|
6541
|
+
if (external?.projectTrusted && !context.extensionContext.isProjectTrusted()) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
|
|
6559
6542
|
const id = randomUUID3();
|
|
6560
6543
|
const startedAt = Date.now();
|
|
6561
6544
|
const sessionId = context.extensionContext.sessionManager.getSessionId();
|
|
6562
6545
|
if (!sessionId.trim()) throw new WorkflowError("INTERNAL_ERROR", "Current Pi session identity is unavailable");
|
|
6563
6546
|
const owner = this.runOwner;
|
|
6564
6547
|
if (!owner) throw new WorkflowError("INTERNAL_ERROR", "Subagent storage owner identity is unavailable");
|
|
6565
|
-
const settings = resolveWorkflowSettings(context.extensionContext.cwd, context.extensionContext.isProjectTrusted(), workflowSettingsPath(this.dependencies.agentDir ??
|
|
6548
|
+
const settings = resolveWorkflowSettings(context.extensionContext.cwd, context.extensionContext.isProjectTrusted(), workflowSettingsPath(this.dependencies.agentDir ?? getAgentDir5()));
|
|
6566
6549
|
const concurrency = settings.effective.concurrency;
|
|
6567
6550
|
if (this.activeRunCount >= concurrency) throw new WorkflowError("AGENT_FAILED", `Subagent concurrency limit reached (${String(this.activeRunCount)}/${String(concurrency)} active runs); no queue is maintained. Retry after an active run settles.`);
|
|
6568
6551
|
this.activeRunCount += 1;
|
|
6569
6552
|
const controller = new AbortController();
|
|
6570
6553
|
const initialStatus = { id, sessionId, state: "running", startedAt, owner: { ...owner } };
|
|
6554
|
+
let finishStart;
|
|
6555
|
+
const pendingStart = new Promise((resolve5) => {
|
|
6556
|
+
finishStart = resolve5;
|
|
6557
|
+
});
|
|
6558
|
+
this.pendingStarts.add(pendingStart);
|
|
6571
6559
|
let directory;
|
|
6572
6560
|
try {
|
|
6573
6561
|
directory = await createRunStorage(storageDirectory(this.dependencies), id, snapshot, initialStatus, external);
|
|
6562
|
+
if (this.disposePromise !== void 0 || context.signal?.aborted) {
|
|
6563
|
+
await atomicJson(statusPath(directory), reconciledStatus(initialStatus, "stopped", Date.now()));
|
|
6564
|
+
throw new WorkflowError("CANCELLED", "Subagent cancelled");
|
|
6565
|
+
}
|
|
6574
6566
|
} catch (error) {
|
|
6575
6567
|
this.activeRunCount -= 1;
|
|
6576
6568
|
if (error instanceof WorkflowError) throw error;
|
|
6577
6569
|
throw internalStorageError(error, `Unable to start subagent ${id}`);
|
|
6570
|
+
} finally {
|
|
6571
|
+
this.pendingStarts.delete(pendingStart);
|
|
6572
|
+
finishStart();
|
|
6578
6573
|
}
|
|
6579
6574
|
const current = {};
|
|
6580
6575
|
let resolveTerminal;
|
|
6581
|
-
const terminal = new Promise((
|
|
6582
|
-
resolveTerminal =
|
|
6576
|
+
const terminal = new Promise((resolve5) => {
|
|
6577
|
+
resolveTerminal = resolve5;
|
|
6583
6578
|
});
|
|
6584
6579
|
const executorOwnership = { default: true };
|
|
6585
6580
|
const execution = Promise.resolve().then(async () => {
|
|
@@ -6607,12 +6602,9 @@ var PersistentSubagentManager = class {
|
|
|
6607
6602
|
throw new WorkflowError("CANCELLED", "Subagent cancelled");
|
|
6608
6603
|
}
|
|
6609
6604
|
}
|
|
6610
|
-
const baseRoot = executionRoot(context, this.dependencies, controller.signal, id, worktree
|
|
6611
|
-
const root = loadingRegistry().modelAliases().length === 0 ? baseRoot : await addDynamicAliases(context, controller.signal, baseRoot);
|
|
6605
|
+
const baseRoot = executionRoot(context, this.dependencies, controller.signal, id, worktree);
|
|
6606
|
+
const root = external !== void 0 || loadingRegistry().modelAliases().length === 0 ? baseRoot : await addDynamicAliases(context, controller.signal, baseRoot);
|
|
6612
6607
|
const transport = this.dependencies.transport ?? localAgentTransport;
|
|
6613
|
-
const injectedExecutor = this.dependencies.createExecutor?.(root, transport);
|
|
6614
|
-
executorOwnership.default = injectedExecutor === void 0;
|
|
6615
|
-
if (current.run) current.run.executorOwnsSession = executorOwnership.default;
|
|
6616
6608
|
const setSteer = (handler) => {
|
|
6617
6609
|
const run2 = current.run;
|
|
6618
6610
|
if (run2) this.registerSteerHandler(run2, handler);
|
|
@@ -6653,6 +6645,13 @@ var PersistentSubagentManager = class {
|
|
|
6653
6645
|
if (run2.state !== "running") return;
|
|
6654
6646
|
emitUpdate(run2);
|
|
6655
6647
|
}, (progress) => this.recordProgress(current.run, progress));
|
|
6648
|
+
if (external !== void 0) options.configuration = structuredClone(external);
|
|
6649
|
+
options.onConfiguration = (configuration) => atomicJson(configurationPath(directory), { version: 1, configuration });
|
|
6650
|
+
const cwd = worktree?.cwd ?? root.cwd;
|
|
6651
|
+
options.configuration = await new WorkflowAgentExecutor(root, transport).prepare(options, cwd, controller.signal);
|
|
6652
|
+
const injectedExecutor = this.dependencies.createExecutor?.({ ...root, extensionSettings: options.configuration.settings }, transport);
|
|
6653
|
+
executorOwnership.default = injectedExecutor === void 0;
|
|
6654
|
+
if (current.run) current.run.executorOwnsSession = executorOwnership.default;
|
|
6656
6655
|
if (injectedExecutor) return injectedExecutor.execute(snapshot.prompt, options, controller.signal, setSteer);
|
|
6657
6656
|
return new WorkflowAgentExecutor(root, transport).execute(snapshot.prompt, options, controller.signal, [], setSteer);
|
|
6658
6657
|
});
|
|
@@ -6703,9 +6702,9 @@ var PersistentSubagentManager = class {
|
|
|
6703
6702
|
}
|
|
6704
6703
|
return inspection;
|
|
6705
6704
|
}
|
|
6706
|
-
return this.inspectList();
|
|
6705
|
+
return this.inspectList(request.scope === "all" ? void 0 : context.extensionContext.sessionManager.getSessionId());
|
|
6707
6706
|
}
|
|
6708
|
-
async inspectList() {
|
|
6707
|
+
async inspectList(sessionId) {
|
|
6709
6708
|
const root = storageDirectory(this.dependencies);
|
|
6710
6709
|
await secureDirectory(root);
|
|
6711
6710
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
@@ -6716,7 +6715,7 @@ var PersistentSubagentManager = class {
|
|
|
6716
6715
|
try {
|
|
6717
6716
|
const active = this.activeRuns.get(entry.name);
|
|
6718
6717
|
const status = publicStatus(active ? persistedStatus(active) : this.terminalSummaries.get(entry.name) ?? await loadPersistedStatus(root, entry.name, false));
|
|
6719
|
-
if (status.startedAt === void 0) continue;
|
|
6718
|
+
if (status.startedAt === void 0 || sessionId !== void 0 && status.sessionId !== sessionId) continue;
|
|
6720
6719
|
statuses.push({ status, startedAt: status.startedAt });
|
|
6721
6720
|
} catch (error) {
|
|
6722
6721
|
if (error instanceof WorkflowError && (error.code === "RUN_NOT_FOUND" || error instanceof InvalidPersistedSubagentStatusError)) continue;
|
|
@@ -6760,11 +6759,11 @@ var PersistentSubagentManager = class {
|
|
|
6760
6759
|
}
|
|
6761
6760
|
async dispose() {
|
|
6762
6761
|
if (this.disposePromise) return this.disposePromise;
|
|
6762
|
+
this.disposed = true;
|
|
6763
6763
|
this.disposePromise = (async () => {
|
|
6764
6764
|
await this.initialization;
|
|
6765
|
-
this.disposed = true;
|
|
6766
6765
|
const runs = [...this.activeRuns.values()];
|
|
6767
|
-
await Promise.allSettled(runs.map((run) => this.stopRun(run, true)));
|
|
6766
|
+
await Promise.allSettled([...this.pendingStarts, ...runs.map((run) => this.stopRun(run, true))]);
|
|
6768
6767
|
await Promise.allSettled([...this.notificationPromises]);
|
|
6769
6768
|
const owner = this.storageOwner;
|
|
6770
6769
|
this.storageOwner = void 0;
|
|
@@ -7095,9 +7094,8 @@ var PersistentSubagentManager = class {
|
|
|
7095
7094
|
const notify = this.dependencies.notify;
|
|
7096
7095
|
if (!notify || run.notificationSent || run.disposed) return;
|
|
7097
7096
|
run.notificationSent = true;
|
|
7098
|
-
const
|
|
7099
|
-
const
|
|
7100
|
-
const notification = { id: run.id, label: label2, role, state: run.state, ...run.error === void 0 ? {} : { error: run.error } };
|
|
7097
|
+
const label2 = run.request.label?.trim() || "subagent";
|
|
7098
|
+
const notification = { id: run.id, label: label2, state: run.state, ...run.error === void 0 ? {} : { error: run.error } };
|
|
7101
7099
|
const pending = Promise.resolve().then(() => notify(notification));
|
|
7102
7100
|
this.notificationPromises.add(pending);
|
|
7103
7101
|
void pending.then(() => {
|
|
@@ -7145,8 +7143,8 @@ function createSubagentManager(dependencies = {}) {
|
|
|
7145
7143
|
|
|
7146
7144
|
// subagents/src/navigator.ts
|
|
7147
7145
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
7148
|
-
import { join as
|
|
7149
|
-
import { copyToClipboard as copyToClipboard2, getAgentDir as
|
|
7146
|
+
import { join as join9 } from "node:path";
|
|
7147
|
+
import { copyToClipboard as copyToClipboard2, getAgentDir as getAgentDir6, SettingsManager as SettingsManager4 } from "@earendil-works/pi-coding-agent";
|
|
7150
7148
|
import { Editor, truncateToWidth as truncateToWidth3 } from "@earendil-works/pi-tui";
|
|
7151
7149
|
var MAX_DETAIL_TEXT = 4e3;
|
|
7152
7150
|
function objectValue2(value) {
|
|
@@ -7169,7 +7167,7 @@ function managerContext(context, waitForForeground = true, includeAttemptMetadat
|
|
|
7169
7167
|
async function loadRequest(storageDirectory3, id) {
|
|
7170
7168
|
if (!safeRunId(id)) return {};
|
|
7171
7169
|
try {
|
|
7172
|
-
const value = JSON.parse(await readFile5(
|
|
7170
|
+
const value = JSON.parse(await readFile5(join9(storageDirectory3, id, "request.json"), "utf8"));
|
|
7173
7171
|
return { request: normalizeSubagentRunRequest(value) };
|
|
7174
7172
|
} catch (error) {
|
|
7175
7173
|
if (isFileNotFound(error)) return {};
|
|
@@ -7202,18 +7200,11 @@ async function inspectEntry(manager, storageDirectory3, entry, context) {
|
|
|
7202
7200
|
entry: { status: inspected.status, ...request.request === void 0 ? {} : { request: request.request }, ...request.error === void 0 ? {} : { requestError: request.error } }
|
|
7203
7201
|
};
|
|
7204
7202
|
}
|
|
7205
|
-
function requestRole(request) {
|
|
7206
|
-
const role = request?.role;
|
|
7207
|
-
if (typeof role === "string" && role.trim()) return role.trim();
|
|
7208
|
-
const override = objectValue2(role);
|
|
7209
|
-
return typeof override?.name === "string" && override.name.trim() ? override.name.trim() : "none";
|
|
7210
|
-
}
|
|
7211
7203
|
function shortId(id) {
|
|
7212
7204
|
return id.length > 12 ? id.slice(0, 8) : id;
|
|
7213
7205
|
}
|
|
7214
7206
|
function entryName(entry) {
|
|
7215
|
-
|
|
7216
|
-
return boundedText(entry.request?.label?.trim() || (role === "none" ? shortId(entry.status.id) : role), 256);
|
|
7207
|
+
return boundedText(entry.request?.label?.trim() || shortId(entry.status.id), 256);
|
|
7217
7208
|
}
|
|
7218
7209
|
function listNames(entries) {
|
|
7219
7210
|
const names = entries.map(entryName);
|
|
@@ -7228,8 +7219,8 @@ function pickerLabels(entries) {
|
|
|
7228
7219
|
const name = names[index] ?? "";
|
|
7229
7220
|
const model = status.progress?.state?.model;
|
|
7230
7221
|
const cost = formatCost(status.progress?.accounting.cost);
|
|
7231
|
-
const
|
|
7232
|
-
return `${runStateGlyph(status.state, "\u2826")} ${name} ${status.state}${model ? ` ${boundedText(model.model, 256)}${model.thinking ? `:${model.thinking}` : ""}` : ""}${cost ? ` ${cost}` : ""}${
|
|
7222
|
+
const runtime = status.startedAt === void 0 ? "" : ` runtime=${formatWorkflowRuntime((status.finishedAt ?? Date.now()) - status.startedAt)}`;
|
|
7223
|
+
return `${runStateGlyph(status.state, "\u2826")} ${name} ${status.state}${model ? ` ${boundedText(model.model, 256)}${model.thinking ? `:${model.thinking}` : ""}` : ""}${cost ? ` ${cost}` : ""}${runtime}`;
|
|
7233
7224
|
});
|
|
7234
7225
|
}
|
|
7235
7226
|
function boundedText(value, limit = MAX_DETAIL_TEXT) {
|
|
@@ -7250,23 +7241,21 @@ function usageAccounting(status) {
|
|
|
7250
7241
|
return status.progress?.accounting;
|
|
7251
7242
|
}
|
|
7252
7243
|
function detailPresentation(inspection) {
|
|
7253
|
-
const { status
|
|
7244
|
+
const { status } = inspection.entry;
|
|
7254
7245
|
const attempt = latestAttempt(status);
|
|
7255
7246
|
const state = status.progress?.state;
|
|
7256
|
-
const
|
|
7247
|
+
const activity = status.progress?.activity;
|
|
7257
7248
|
const model = state?.model ?? attempt?.setup.model;
|
|
7258
7249
|
const tools = state?.tools ?? attempt?.setup.tools;
|
|
7259
7250
|
const lastEventAt = status.progress?.lastEventAt;
|
|
7260
7251
|
const attempts = status.attempts ?? attempt?.attempt;
|
|
7261
7252
|
const accounting2 = usageAccounting(status);
|
|
7262
|
-
const role = requestRole(request);
|
|
7263
7253
|
const error = status.error ?? attempt?.error;
|
|
7264
7254
|
return {
|
|
7265
7255
|
state: status.state,
|
|
7266
|
-
...
|
|
7256
|
+
...activity === void 0 ? {} : { activity: { kind: activity.kind, text: boundedText(activity.text) } },
|
|
7267
7257
|
...lastEventAt === void 0 ? {} : { lastEventAt },
|
|
7268
7258
|
...model === void 0 ? {} : { model: { provider: boundedText(model.provider, 256), model: boundedText(model.model, 256), ...model.thinking === void 0 ? {} : { thinking: model.thinking } } },
|
|
7269
|
-
...role === "none" ? {} : { role: boundedText(role, 256) },
|
|
7270
7259
|
...tools === void 0 ? {} : { tools: tools.slice(0, 256).map((tool) => boundedText(tool, 256)) },
|
|
7271
7260
|
...attempts === void 0 ? {} : { attempts },
|
|
7272
7261
|
...status.startedAt === void 0 ? {} : { startedAt: status.startedAt },
|
|
@@ -7318,8 +7307,7 @@ function standaloneActionContext(manager, inspection, context) {
|
|
|
7318
7307
|
const session = attempt.session;
|
|
7319
7308
|
const live = liveData?.liveSession && session && liveData.liveSession.reference.transport === session.transport && liveData.liveSession.reference.sessionId === session.sessionId ? liveData.liveSession : void 0;
|
|
7320
7309
|
const label2 = request?.label?.trim();
|
|
7321
|
-
const
|
|
7322
|
-
const name = label2 || (role === "none" ? "subagent" : role);
|
|
7310
|
+
const name = label2 || "subagent";
|
|
7323
7311
|
const ui = {
|
|
7324
7312
|
notify: (message, level = "info") => {
|
|
7325
7313
|
context.ui.notify(message, level);
|
|
@@ -7388,7 +7376,7 @@ function retryResult(value) {
|
|
|
7388
7376
|
return record2 && typeof record2.id === "string" && record2.id.trim() && record2.state === "running" ? { id: record2.id, state: "running" } : void 0;
|
|
7389
7377
|
}
|
|
7390
7378
|
async function openNavigatorArtifact(context, tui, artifact, label2) {
|
|
7391
|
-
const command = SettingsManager4.create(context.cwd,
|
|
7379
|
+
const command = SettingsManager4.create(context.cwd, getAgentDir6(), { projectTrusted: context.isProjectTrusted() }).getExternalEditorCommand();
|
|
7392
7380
|
if (!command) {
|
|
7393
7381
|
context.ui.notify(`Cannot open ${label2}: no external editor is configured.`, "warning");
|
|
7394
7382
|
return;
|
|
@@ -7419,7 +7407,7 @@ async function performAction(manager, storageDirectory3, entry, action, context,
|
|
|
7419
7407
|
return "unchanged";
|
|
7420
7408
|
}
|
|
7421
7409
|
if (action === "Copy run path") {
|
|
7422
|
-
await clipboard(
|
|
7410
|
+
await clipboard(join9(storageDirectory3, fresh.entry.status.id));
|
|
7423
7411
|
context.ui.notify("Copied run path.", "info");
|
|
7424
7412
|
return "unchanged";
|
|
7425
7413
|
}
|
|
@@ -7843,33 +7831,15 @@ var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "failed", "stopped"]
|
|
|
7843
7831
|
var MAX_INSPECT_VALUE_CHARS = 4e3;
|
|
7844
7832
|
var MAX_INSPECT_TOOL_CALLS = 32;
|
|
7845
7833
|
var MAX_BACKGROUND_WIDGET_ROWS = 10;
|
|
7846
|
-
function textBlock2(text) {
|
|
7847
|
-
return {
|
|
7848
|
-
render(width) {
|
|
7849
|
-
return text.split("\n").map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026"));
|
|
7850
|
-
},
|
|
7851
|
-
invalidate() {
|
|
7852
|
-
}
|
|
7853
|
-
};
|
|
7854
|
-
}
|
|
7855
|
-
function roleName(value) {
|
|
7856
|
-
if (typeof value === "string") return value.trim() || void 0;
|
|
7857
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
7858
|
-
const name = value.name;
|
|
7859
|
-
return typeof name === "string" ? name.trim() || void 0 : void 0;
|
|
7860
|
-
}
|
|
7861
7834
|
function modeName(value) {
|
|
7862
7835
|
return value === "background" || value === "foreground" ? value : void 0;
|
|
7863
7836
|
}
|
|
7864
7837
|
function requestMetadata(args, metadataAvailable) {
|
|
7865
7838
|
const mode = modeName(args.mode) ?? (metadataAvailable ? "background" : void 0);
|
|
7866
|
-
|
|
7867
|
-
return [mode === void 0 ? void 0 : `mode=${mode}`, role === void 0 ? void 0 : `role=${role}`].filter((value) => value !== void 0).join(" ");
|
|
7839
|
+
return [mode === void 0 ? void 0 : `mode=${mode}`].filter((value) => value !== void 0).join(" ");
|
|
7868
7840
|
}
|
|
7869
7841
|
function label(args) {
|
|
7870
7842
|
if (typeof args.label === "string" && args.label.trim()) return args.label.trim();
|
|
7871
|
-
const role = roleName(args.role);
|
|
7872
|
-
if (role) return role;
|
|
7873
7843
|
if (typeof args.id === "string" && args.id) return args.id.slice(0, 8);
|
|
7874
7844
|
return "subagent";
|
|
7875
7845
|
}
|
|
@@ -7893,17 +7863,6 @@ function statusValue2(value) {
|
|
|
7893
7863
|
if (typeof record2.id !== "string" || record2.state !== "running" && record2.state !== "completed" && record2.state !== "failed" && record2.state !== "stopped") return void 0;
|
|
7894
7864
|
return value;
|
|
7895
7865
|
}
|
|
7896
|
-
function runtime(startedAt, finishedAt, now) {
|
|
7897
|
-
if (startedAt === void 0) return "";
|
|
7898
|
-
const seconds = Math.max(0, Math.floor(((finishedAt ?? now) - startedAt) / 1e3));
|
|
7899
|
-
if (seconds < 60) return `${String(seconds)}s`;
|
|
7900
|
-
const minutes = Math.floor(seconds / 60);
|
|
7901
|
-
const remainingSeconds = seconds % 60;
|
|
7902
|
-
if (minutes < 60) return `${String(minutes)}m${remainingSeconds ? ` ${String(remainingSeconds)}s` : ""}`;
|
|
7903
|
-
const hours = Math.floor(minutes / 60);
|
|
7904
|
-
const remainingMinutes = minutes % 60;
|
|
7905
|
-
return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
|
|
7906
|
-
}
|
|
7907
7866
|
function stateGlyph(state, spinner2) {
|
|
7908
7867
|
if (state === "running") return spinner2;
|
|
7909
7868
|
return state === "completed" ? "\u2713" : "\u2717";
|
|
@@ -7912,23 +7871,6 @@ function stateColor(state) {
|
|
|
7912
7871
|
if (state === "running") return "accent";
|
|
7913
7872
|
return state === "completed" ? "success" : "error";
|
|
7914
7873
|
}
|
|
7915
|
-
function activity(status) {
|
|
7916
|
-
if (status.state !== "running") return void 0;
|
|
7917
|
-
const current = status.progress?.activity;
|
|
7918
|
-
if (current?.kind === "reasoning" || current?.kind === "text") {
|
|
7919
|
-
const name = current.kind === "reasoning" ? "reasoning" : "responding";
|
|
7920
|
-
const text = sanitizeDisplayText(current.text);
|
|
7921
|
-
return text && text !== name ? `${name} \xB7 ${text}` : name;
|
|
7922
|
-
}
|
|
7923
|
-
if (current?.kind === "tool") return sanitizeDisplayText(current.text);
|
|
7924
|
-
return sanitizeDisplayText([...status.progress?.toolCalls ?? []].reverse().find(({ state }) => state === "running")?.name ?? "");
|
|
7925
|
-
}
|
|
7926
|
-
function stalledDuration2(status, now) {
|
|
7927
|
-
const lastEventAt = status.progress?.lastEventAt;
|
|
7928
|
-
if (status.state !== "running" || lastEventAt === void 0 || !Number.isFinite(lastEventAt)) return void 0;
|
|
7929
|
-
const duration = now - lastEventAt;
|
|
7930
|
-
return duration >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? duration : void 0;
|
|
7931
|
-
}
|
|
7932
7874
|
function usageStats(status) {
|
|
7933
7875
|
const value = status.progress?.accounting;
|
|
7934
7876
|
return value ? [formatTokens(value.input + value.output), formatCost(value.cost)].filter(Boolean).join(" \xB7 ") : "";
|
|
@@ -7954,15 +7896,15 @@ function boundedValue(value) {
|
|
|
7954
7896
|
}
|
|
7955
7897
|
function formatSubagentProgress(status, args, theme, spinner2, now, expanded) {
|
|
7956
7898
|
const color = stateColor(status.state);
|
|
7957
|
-
const elapsed =
|
|
7899
|
+
const elapsed = status.startedAt === void 0 ? "" : formatWorkflowRuntime((status.finishedAt ?? now) - status.startedAt);
|
|
7958
7900
|
const metadata = requestMetadata(args, args.id === void 0);
|
|
7959
7901
|
const stats = usageStats(status);
|
|
7960
7902
|
const lines = [
|
|
7961
|
-
`${theme.fg(color, stateGlyph(status.state,
|
|
7903
|
+
`${theme.fg(color, stateGlyph(status.state, "\u25CF"))} ${theme.bold(theme.fg("accent", `Subagent: ${label({ ...args, id: status.id })}`))} ${theme.fg(color, `[${status.state}]`)}${metadata ? ` ${theme.fg("dim", metadata)}` : ""}${stats ? ` ${stats}` : ""}${elapsed ? ` runtime=${elapsed}` : ""}`
|
|
7962
7904
|
];
|
|
7963
|
-
const current =
|
|
7964
|
-
const stalled =
|
|
7965
|
-
if (current) lines.push(` ${theme.fg("accent", spinner2)} ${theme.fg("dim", current)}${stalled === void 0 ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
7905
|
+
const current = status.state === "running" ? agentActivityLabel(status.progress ?? {}) : void 0;
|
|
7906
|
+
const stalled = stalledDuration({ ...status.progress, state: status.state }, now);
|
|
7907
|
+
if (current || status.state === "running") lines.push(` ${theme.fg("accent", spinner2)}${current ? ` ${theme.fg("dim", current)}` : ""}${stalled === void 0 ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
7966
7908
|
else if (stalled !== void 0) lines.push(` ${theme.fg("warning", `stalled? ${formatStalledDuration(stalled)}`)}`);
|
|
7967
7909
|
if (status.error) lines.push(` ${theme.fg("error", `${status.error.code}: ${status.error.message}`)}`);
|
|
7968
7910
|
if (expanded) {
|
|
@@ -8028,7 +7970,7 @@ function subagentProgressBlock(status, args, theme, freezeAt) {
|
|
|
8028
7970
|
render(width) {
|
|
8029
7971
|
const now = TERMINAL_STATES.has(current.state) ? frozenAt : Date.now();
|
|
8030
7972
|
if (!TERMINAL_STATES.has(current.state)) frozenAt = now;
|
|
8031
|
-
const frame = SPINNER2[Math.floor(now /
|
|
7973
|
+
const frame = SPINNER2[Math.floor(now / REPAINT_MS) % SPINNER2.length] ?? "\u25C7";
|
|
8032
7974
|
return formatSubagentProgress(current, args, currentTheme, frame, now, expanded).split("\n").map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026"));
|
|
8033
7975
|
},
|
|
8034
7976
|
invalidate() {
|
|
@@ -8046,14 +7988,14 @@ function renderSubagentResult(result, options, theme, context) {
|
|
|
8046
7988
|
if (status?.state === "running" && options.isPartial && !state.subagentSpinner) {
|
|
8047
7989
|
state.subagentSpinner = setInterval(() => {
|
|
8048
7990
|
context.invalidate();
|
|
8049
|
-
},
|
|
7991
|
+
}, REPAINT_MS);
|
|
8050
7992
|
state.subagentSpinner.unref();
|
|
8051
7993
|
} else if ((!options.isPartial || status?.state !== "running") && state.subagentSpinner) {
|
|
8052
7994
|
clearInterval(state.subagentSpinner);
|
|
8053
7995
|
delete state.subagentSpinner;
|
|
8054
7996
|
}
|
|
8055
|
-
if (!status || context.isError) return
|
|
8056
|
-
if (!options.isPartial && status.state === "running") return
|
|
7997
|
+
if (!status || context.isError) return textBlock(resultText(result));
|
|
7998
|
+
if (!options.isPartial && status.state === "running") return textBlock(formatSubagentLaunch(status, context.args, theme, options.expanded));
|
|
8057
7999
|
if (TERMINAL_STATES.has(status.state)) state.subagentProgressFrozenAt ??= Date.now();
|
|
8058
8000
|
else delete state.subagentProgressFrozenAt;
|
|
8059
8001
|
let component = state.subagentProgressComponent;
|
|
@@ -8087,14 +8029,12 @@ var RECEIPT_ENTRY_TYPE = "piewf-subagent-receipt";
|
|
|
8087
8029
|
function receiptFor(status, request) {
|
|
8088
8030
|
const accounting2 = status.progress?.accounting;
|
|
8089
8031
|
const model = modelName(status);
|
|
8090
|
-
const role = roleName(request.role);
|
|
8091
8032
|
const tools = status.progress?.state?.tools ?? status.attemptDetails?.at(-1)?.setup.tools;
|
|
8092
8033
|
return {
|
|
8093
8034
|
id: status.id,
|
|
8094
8035
|
label: label({ ...request, id: status.id }),
|
|
8095
8036
|
state: status.state,
|
|
8096
8037
|
...model ? { model } : {},
|
|
8097
|
-
...role === void 0 ? {} : { role },
|
|
8098
8038
|
...tools === void 0 ? {} : { tools: [...tools] },
|
|
8099
8039
|
input: accounting2?.input ?? 0,
|
|
8100
8040
|
output: accounting2?.output ?? 0,
|
|
@@ -8110,7 +8050,7 @@ function renderSubagentReceipt(data, expanded, theme) {
|
|
|
8110
8050
|
const headline = [formatTokens(data.input + data.output), formatCost(data.costUsd) || "$0.00", formatElapsed(data.durationMs), data.state].filter(Boolean).join(" \xB7 ");
|
|
8111
8051
|
const lines = [`${glyph} ${theme.bold(sanitizeDisplayText(data.label))} ${theme.fg("muted", headline)}`];
|
|
8112
8052
|
if (!expanded) return lines;
|
|
8113
|
-
const meta = [data.model ?? "", data.
|
|
8053
|
+
const meta = [data.model ?? "", data.attempts > 1 ? `${String(data.attempts)} attempts` : "", data.tools?.join(" ") ?? ""].filter(Boolean);
|
|
8114
8054
|
if (meta.length > 0) lines.push(theme.fg("muted", ` ${meta.join(" \xB7 ")}`));
|
|
8115
8055
|
const split = [data.input ? `in ${formatTokens(data.input)}` : "", data.output ? `out ${formatTokens(data.output)}` : "", data.cacheRead ? `cache ${formatTokens(data.cacheRead)}` : ""].filter(Boolean);
|
|
8116
8056
|
if (split.length > 0) lines.push(theme.fg("muted", ` ${split.join(" \xB7 ")}`));
|
|
@@ -8175,7 +8115,7 @@ function createSubagentBackgroundWidget(host = {}) {
|
|
|
8175
8115
|
showing = true;
|
|
8176
8116
|
timer = setInterval(() => {
|
|
8177
8117
|
requestRender?.();
|
|
8178
|
-
},
|
|
8118
|
+
}, REPAINT_MS);
|
|
8179
8119
|
timer.unref();
|
|
8180
8120
|
} catch {
|
|
8181
8121
|
hide();
|
|
@@ -8211,35 +8151,35 @@ function createSubagentBackgroundWidget(host = {}) {
|
|
|
8211
8151
|
}
|
|
8212
8152
|
function renderSubagentInspectCall(args, theme) {
|
|
8213
8153
|
const title = theme.fg("toolTitle", theme.bold("subagents_inspect"));
|
|
8214
|
-
return
|
|
8154
|
+
return textBlock(args.id ? `${title} ${theme.fg("accent", args.id)}` : `${title} ${theme.fg("muted", args.scope === "all" ? "all" : "session")}`);
|
|
8215
8155
|
}
|
|
8216
8156
|
function renderSubagentInspectResult(result, options, theme, args) {
|
|
8217
8157
|
const status = statusValue2(result.details);
|
|
8218
|
-
if (status) return
|
|
8158
|
+
if (status) return textBlock(formatSubagentInspection(status, args, result.details, theme, options.expanded));
|
|
8219
8159
|
if (Array.isArray(result.details)) {
|
|
8220
8160
|
const statuses = result.details.map(statusValue2).filter((value) => value !== void 0);
|
|
8221
8161
|
const lines = [theme.bold(theme.fg("accent", `Subagents (${String(statuses.length)})`)), ...statuses.map((entry) => {
|
|
8222
8162
|
const color = stateColor(entry.state);
|
|
8223
8163
|
return ` ${theme.fg(color, stateGlyph(entry.state, "\u25C7"))} ${entry.id.slice(0, 8)} ${theme.fg(color, `[${entry.state}]`)}`;
|
|
8224
8164
|
})];
|
|
8225
|
-
return
|
|
8165
|
+
return textBlock(lines.join("\n"));
|
|
8226
8166
|
}
|
|
8227
|
-
return
|
|
8167
|
+
return textBlock(resultText(result));
|
|
8228
8168
|
}
|
|
8229
8169
|
function renderSubagentControlCall(name, args, theme) {
|
|
8230
8170
|
const title = theme.fg("toolTitle", theme.bold(name));
|
|
8231
8171
|
const message = args.message ? ` ${theme.fg("dim", args.message)}` : "";
|
|
8232
|
-
return
|
|
8172
|
+
return textBlock(`${title} ${theme.fg("accent", args.id)}${message}`);
|
|
8233
8173
|
}
|
|
8234
8174
|
function renderSubagentControlResult(result, theme) {
|
|
8235
8175
|
const status = statusValue2(result.details);
|
|
8236
8176
|
if (status) {
|
|
8237
8177
|
const color = stateColor(status.state);
|
|
8238
|
-
return
|
|
8178
|
+
return textBlock(`${theme.fg(color, stateGlyph(status.state, "\u25C7"))} ${status.id.slice(0, 8)} ${theme.fg(color, `[${status.state}]`)}`);
|
|
8239
8179
|
}
|
|
8240
8180
|
const record2 = typeof result.details === "object" && result.details !== null && !Array.isArray(result.details) ? result.details : void 0;
|
|
8241
|
-
if (record2 && typeof record2.id === "string") return
|
|
8242
|
-
return
|
|
8181
|
+
if (record2 && typeof record2.id === "string") return textBlock(`${theme.fg("success", "\u2713")} ${record2.id.slice(0, 8)}`);
|
|
8182
|
+
return textBlock(resultText(result));
|
|
8243
8183
|
}
|
|
8244
8184
|
|
|
8245
8185
|
// subagents/src/index.ts
|
|
@@ -8247,15 +8187,15 @@ function validateSubagentRunRequest(value) {
|
|
|
8247
8187
|
return normalizeSubagentRunRequest(value);
|
|
8248
8188
|
}
|
|
8249
8189
|
function validateSubagentIdRequest(value, operation) {
|
|
8250
|
-
if (!
|
|
8190
|
+
if (!Value4.Check(SUBAGENTS_ID_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", `Invalid ${operation} parameters`);
|
|
8251
8191
|
return value;
|
|
8252
8192
|
}
|
|
8253
8193
|
function validateSubagentInspectRequest(value) {
|
|
8254
|
-
if (!
|
|
8194
|
+
if (!Value4.Check(SUBAGENTS_INSPECT_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_inspect parameters");
|
|
8255
8195
|
return value;
|
|
8256
8196
|
}
|
|
8257
8197
|
function validateSubagentSteerRequest(value) {
|
|
8258
|
-
if (!
|
|
8198
|
+
if (!Value4.Check(SUBAGENTS_STEER_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_steer parameters");
|
|
8259
8199
|
return value;
|
|
8260
8200
|
}
|
|
8261
8201
|
function serialize(value) {
|
|
@@ -8268,7 +8208,7 @@ function serialize(value) {
|
|
|
8268
8208
|
}
|
|
8269
8209
|
}
|
|
8270
8210
|
function toolResult(value) {
|
|
8271
|
-
return { content: [{ type: "text", text: serialize(value) }], details: value };
|
|
8211
|
+
return { content: [{ type: "text", text: serialize(value) }], details: value, structuredContent: value };
|
|
8272
8212
|
}
|
|
8273
8213
|
function managerContext2(toolCallId, signal, onUpdate, context) {
|
|
8274
8214
|
return {
|
|
@@ -8292,6 +8232,7 @@ function createSubagentTools(manager) {
|
|
|
8292
8232
|
"Do not poll a running ID; use subagents_inspect({ id }) when you need its current status or terminal result."
|
|
8293
8233
|
],
|
|
8294
8234
|
parameters: SUBAGENTS_RUN_PARAMETERS,
|
|
8235
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
8295
8236
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
8296
8237
|
return toolResult(await manager.run(validateSubagentRunRequest(params), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
8297
8238
|
},
|
|
@@ -8305,8 +8246,9 @@ function createSubagentTools(manager) {
|
|
|
8305
8246
|
defineTool2({
|
|
8306
8247
|
name: "subagents_inspect",
|
|
8307
8248
|
label: "Subagents Inspect",
|
|
8308
|
-
description: "Inspect durable subagent runs. Omit id for ordered run summaries, or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
|
|
8249
|
+
description: "Inspect durable subagent runs. Omit id for ordered run summaries of the current session (scope all for every session), or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
|
|
8309
8250
|
parameters: SUBAGENTS_INSPECT_PARAMETERS,
|
|
8251
|
+
outputSchema: SUBAGENTS_INSPECT_OUTPUT,
|
|
8310
8252
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
8311
8253
|
return toolResult(await manager.inspect(validateSubagentInspectRequest(params), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
8312
8254
|
},
|
|
@@ -8322,6 +8264,7 @@ function createSubagentTools(manager) {
|
|
|
8322
8264
|
label: "Subagents Steer",
|
|
8323
8265
|
description: "Send a message to a running subagent. The message is queued safely if its steering handler is not ready; settled runs cannot be steered.",
|
|
8324
8266
|
parameters: SUBAGENTS_STEER_PARAMETERS,
|
|
8267
|
+
outputSchema: SUBAGENTS_STEER_OUTPUT,
|
|
8325
8268
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
8326
8269
|
return toolResult(await manager.steer(validateSubagentSteerRequest(params), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
8327
8270
|
},
|
|
@@ -8337,6 +8280,7 @@ function createSubagentTools(manager) {
|
|
|
8337
8280
|
label: "Subagents Stop",
|
|
8338
8281
|
description: 'Stop one running subagent, abort its active session, persist state "stopped", and clean its worktree without affecting sibling runs.',
|
|
8339
8282
|
parameters: SUBAGENTS_STOP_PARAMETERS,
|
|
8283
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
8340
8284
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
8341
8285
|
return toolResult(await manager.stop(validateSubagentIdRequest(params, "subagents_stop"), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
8342
8286
|
},
|
|
@@ -8352,6 +8296,7 @@ function createSubagentTools(manager) {
|
|
|
8352
8296
|
label: "Subagents Retry",
|
|
8353
8297
|
description: "Start a fresh run from a failed or stopped subagent's persisted request. The new run gets a new ID and preserves its original background or foreground mode.",
|
|
8354
8298
|
parameters: SUBAGENTS_RETRY_PARAMETERS,
|
|
8299
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
8355
8300
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
8356
8301
|
return toolResult(await manager.retry(validateSubagentIdRequest(params, "subagents_retry"), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
8357
8302
|
},
|
|
@@ -8366,8 +8311,7 @@ function createSubagentTools(manager) {
|
|
|
8366
8311
|
}
|
|
8367
8312
|
function notificationContent(notification) {
|
|
8368
8313
|
const label2 = notification.label?.trim();
|
|
8369
|
-
const
|
|
8370
|
-
const identity = label2 ? `${label2} role=${role} (${notification.id})` : `${notification.id} role=${role}`;
|
|
8314
|
+
const identity = label2 ? `${label2} (${notification.id})` : notification.id;
|
|
8371
8315
|
if (notification.state === "completed") return `Subagent ${identity} completed. Inspect it with subagents_inspect({ id: "${notification.id}" }).`;
|
|
8372
8316
|
return `Subagent ${identity} failed: ${notification.error?.message ?? "unknown error"}. Inspect it with subagents_inspect({ id: "${notification.id}" }).`;
|
|
8373
8317
|
}
|
|
@@ -8384,7 +8328,7 @@ function managerDependencies(options, activeTools, notify, onStatus, onResourceW
|
|
|
8384
8328
|
}
|
|
8385
8329
|
function storageDirectory2(options) {
|
|
8386
8330
|
const dependencies = options.managerDependencies;
|
|
8387
|
-
return dependencies?.storageDir ??
|
|
8331
|
+
return dependencies?.storageDir ?? join10(dependencies?.agentDir ?? getAgentDir7(), "subagents");
|
|
8388
8332
|
}
|
|
8389
8333
|
function createSubagentsExtension(options = {}, activeTools, notify, onStatus, onResourceWarning) {
|
|
8390
8334
|
const manager = options.manager ?? createSubagentManager(managerDependencies(options, activeTools, notify, onStatus, onResourceWarning));
|
|
@@ -8393,7 +8337,8 @@ function createSubagentsExtension(options = {}, activeTools, notify, onStatus, o
|
|
|
8393
8337
|
}
|
|
8394
8338
|
function registerSubagentsExtension(pi, options = {}) {
|
|
8395
8339
|
const getActiveTools = pi.getActiveTools;
|
|
8396
|
-
const
|
|
8340
|
+
const getAllTools = pi.getAllTools;
|
|
8341
|
+
const activeTools = getActiveTools === void 0 ? void 0 : () => reachableTools({ getActiveTools: () => getActiveTools.call(pi), ...getAllTools === void 0 ? {} : { getAllTools: () => getAllTools.call(pi) } });
|
|
8397
8342
|
const sendMessage = pi.sendMessage;
|
|
8398
8343
|
const notify = sendMessage === void 0 ? void 0 : (notification) => {
|
|
8399
8344
|
sendMessage.call(pi, { customType: "subagents", content: notificationContent(notification), display: true, details: notification }, { deliverAs: "steer", triggerTurn: true });
|
|
@@ -8410,7 +8355,8 @@ function registerSubagentsExtension(pi, options = {}) {
|
|
|
8410
8355
|
const registry = loadingRegistry();
|
|
8411
8356
|
if (typeof registry.observeSubagentStatus === "function") registry.observeSubagentStatus(status, request);
|
|
8412
8357
|
}, onResourceWarning);
|
|
8413
|
-
|
|
8358
|
+
const codemodeTools = loadCodemodeToolsSetting(options.managerDependencies?.agentDir);
|
|
8359
|
+
for (const tool of extension2.tools) pi.registerTool({ ...tool, ...workflowToolExposure(tool.name, codemodeTools) });
|
|
8414
8360
|
if (pi.registerCommand !== void 0) registerSubagentNavigator(pi.registerCommand.bind(pi), extension2.manager, storageDirectory2(options), options.clipboard);
|
|
8415
8361
|
if (pi.on !== void 0) {
|
|
8416
8362
|
setSubagentManager(extension2.manager);
|
|
@@ -8433,15 +8379,17 @@ function extension(pi, options = {}) {
|
|
|
8433
8379
|
}
|
|
8434
8380
|
export {
|
|
8435
8381
|
SUBAGENTS_ID_PARAMETERS,
|
|
8382
|
+
SUBAGENTS_INSPECT_OUTPUT,
|
|
8436
8383
|
SUBAGENTS_INSPECT_PARAMETERS,
|
|
8437
8384
|
SUBAGENTS_RETRY_PARAMETERS,
|
|
8438
8385
|
SUBAGENTS_RUN_PARAMETERS,
|
|
8386
|
+
SUBAGENTS_STEER_OUTPUT,
|
|
8439
8387
|
SUBAGENTS_STEER_PARAMETERS,
|
|
8440
8388
|
SUBAGENTS_STOP_PARAMETERS,
|
|
8441
8389
|
SUBAGENTS_TOOL_NAMES,
|
|
8442
|
-
SUBAGENTS_TOOL_SCHEMAS,
|
|
8443
8390
|
SUBAGENT_ATTEMPT_DETAILS_LIMIT,
|
|
8444
8391
|
SUBAGENT_MAX_RETRIES,
|
|
8392
|
+
SUBAGENT_STATUS_OUTPUT,
|
|
8445
8393
|
SUBAGENT_SYSTEM_PROMPT_LIMIT,
|
|
8446
8394
|
attemptValue,
|
|
8447
8395
|
createRunStoreWorktreeAdapter,
|