@marcoscale98/pi-extensible-workflows 5.19.1-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 +77 -0
- package/README.md +2 -2
- package/dist/src/agent-execution.d.ts +43 -10
- package/dist/src/agent-execution.js +365 -127
- package/dist/src/background-widget.d.ts +0 -1
- package/dist/src/background-widget.js +4 -9
- package/dist/src/decoders.d.ts +2 -3
- package/dist/src/decoders.js +33 -37
- package/dist/src/execution.d.ts +2 -0
- package/dist/src/execution.js +5 -5
- package/dist/src/host-delivery.js +4 -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 +6 -6
- package/dist/src/host-recovery.js +14 -22
- package/dist/src/host-runtime.js +56 -5
- package/dist/src/host-view.d.ts +1 -1
- package/dist/src/host-view.js +17 -15
- package/dist/src/host.js +62 -87
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +1523 -1630
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +7 -6
- package/dist/src/paths.js +24 -15
- 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 +28 -70
- package/dist/src/runtime/agent-runner.d.ts +2 -0
- package/dist/src/session-lease.d.ts +1 -1
- package/dist/src/session-lease.js +45 -11
- package/dist/src/settings.d.ts +6 -3
- package/dist/src/settings.js +36 -47
- package/dist/src/store.d.ts +3 -1
- package/dist/src/store.js +17 -1
- package/dist/src/trajectory.d.ts +0 -1
- package/dist/src/trajectory.js +2 -3
- package/dist/src/types.d.ts +100 -44
- package/dist/src/types.js +4 -7
- package/dist/src/utils.d.ts +3 -2
- package/dist/src/utils.js +20 -10
- package/dist/src/validation.d.ts +8 -2
- package/dist/src/validation.js +122 -86
- package/dist/starter/index.js +8 -9
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.js +8 -7
- package/dist/subagents/index.js +654 -814
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +4 -3
- package/dist/subagents/src/contracts.js +2 -2
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +1 -2
- package/dist/subagents/src/manager.js +49 -70
- package/dist/subagents/src/navigator.js +3 -14
- package/dist/subagents/src/view.d.ts +0 -1
- package/dist/subagents/src/view.js +2 -16
- package/dist/subagents/src/worktree.js +1 -1
- package/dist/trajectory/assets/index.html +8 -8
- package/dist/trajectory/index.js +487 -891
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +8 -8
- package/dist/trajectory/src/index.js +126 -86
- package/dist/trajectory/src/server.js +264 -25
- 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 +5 -5
- package/skills/pi-extensible-workflows/SKILL.md +10 -8
- package/src/agent-execution.ts +339 -127
- package/src/background-widget.ts +4 -10
- package/src/decoders.ts +31 -37
- package/src/execution.ts +5 -5
- package/src/host-delivery.ts +4 -8
- package/src/host-navigator.ts +4 -2
- package/src/host-phases.ts +1 -1
- package/src/host-recovery.ts +20 -26
- package/src/host-runtime.ts +44 -6
- package/src/host-view.ts +18 -11
- package/src/host.ts +62 -113
- package/src/index.ts +1 -2
- package/src/paths.ts +18 -12
- package/src/persistence.ts +1 -0
- package/src/pi-runtime-runner.ts +3 -1
- package/src/registry.ts +27 -63
- package/src/runtime/agent-runner.ts +2 -0
- package/src/session-lease.ts +34 -6
- package/src/settings.ts +35 -47
- package/src/store.ts +18 -2
- package/src/trajectory.ts +2 -4
- package/src/types.ts +71 -30
- package/src/utils.ts +18 -9
- package/src/validation.ts +102 -79
- package/starter/index.ts +0 -1
- package/starter/review-loop.ts +8 -7
- 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 +8 -14
- package/subagents/src/contracts.ts +3 -4
- package/subagents/src/index.ts +2 -3
- package/subagents/src/manager.ts +45 -125
- package/subagents/src/navigator.ts +4 -13
- package/subagents/src/view.ts +4 -14
- package/subagents/src/worktree.ts +1 -1
- package/trajectory/src/assets/index.html +8 -8
- package/trajectory/src/index.ts +106 -82
- package/trajectory/src/server.ts +44 -8
- package/trajectory/src/startup-mutex.ts +127 -0
- package/dist/src/roles.d.ts +0 -70
- package/dist/src/roles.js +0 -326
- 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 -327
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",
|
|
@@ -37,15 +37,12 @@ var ERROR_CODES = [
|
|
|
37
37
|
"INTERNAL_ERROR"
|
|
38
38
|
];
|
|
39
39
|
var THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
40
|
-
function roleNameOf(value) {
|
|
41
|
-
return typeof value === "string" ? value : void 0;
|
|
42
|
-
}
|
|
43
|
-
var BUDGET_DIMENSIONS = ["tokens", "costUsd", "durationMs", "agentLaunches"];
|
|
44
|
-
var BUDGET_EVENT_TYPES = ["soft_crossed", "hard_overrun", "hard_exhausted", "adjustment_requested", "adjustment_approved", "adjustment_rejected"];
|
|
45
40
|
var CONTEXT_FILE_SCOPES = ["global", "project", "cwd"];
|
|
46
41
|
function isContextFileScope(value) {
|
|
47
42
|
return CONTEXT_FILE_SCOPES.some((scope) => scope === value);
|
|
48
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"];
|
|
49
46
|
var WORKFLOW_AGENT_STALL_THRESHOLD_MS = 10 * 60 * 1e3;
|
|
50
47
|
function zeroAccounting() {
|
|
51
48
|
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
|
|
@@ -58,7 +55,7 @@ function sumAccounting(values) {
|
|
|
58
55
|
for (const value of values) total = addAccounting(total, value);
|
|
59
56
|
return total;
|
|
60
57
|
}
|
|
61
|
-
var LAUNCH_SNAPSHOT_IDENTITY_VERSION =
|
|
58
|
+
var LAUNCH_SNAPSHOT_IDENTITY_VERSION = 6;
|
|
62
59
|
var WORKFLOW_ERROR_BRAND = /* @__PURE__ */ Symbol.for("pi-extensible-workflows.workflow-error");
|
|
63
60
|
var WorkflowError = class extends Error {
|
|
64
61
|
constructor(code, message) {
|
|
@@ -156,15 +153,8 @@ function jsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
|
156
153
|
seen.delete(value);
|
|
157
154
|
return valid;
|
|
158
155
|
}
|
|
159
|
-
function
|
|
160
|
-
|
|
161
|
-
let present = false;
|
|
162
|
-
for (const layer of layers) {
|
|
163
|
-
if (layer === void 0) continue;
|
|
164
|
-
present = true;
|
|
165
|
-
for (const [namespace, value] of Object.entries(layer)) Object.defineProperty(merged, namespace, { value: structuredClone(value), enumerable: true, configurable: true, writable: true });
|
|
166
|
-
}
|
|
167
|
-
return present ? deepFreeze(merged) : void 0;
|
|
156
|
+
function jsonObject(value) {
|
|
157
|
+
return jsonValue(value) && object(value);
|
|
168
158
|
}
|
|
169
159
|
function positiveInteger(value) {
|
|
170
160
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
@@ -214,11 +204,16 @@ function parseThinking(value) {
|
|
|
214
204
|
return isThinkingLevel(value) ? value : void 0;
|
|
215
205
|
}
|
|
216
206
|
function parseModelReference(value) {
|
|
217
|
-
const match = /^([^/:\s]+)\/([
|
|
207
|
+
const match = /^([^/:\s]+)\/([^\s]+)$/.exec(value);
|
|
218
208
|
if (!match?.[1] || !match[2]) fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
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 } };
|
|
222
217
|
}
|
|
223
218
|
function assertModelThinking(value, path = "model") {
|
|
224
219
|
if (value.includes("/") && parseModelReference(value).thinking === void 0) fail("INVALID_METADATA", `${path} must be provider/model:thinking`);
|
|
@@ -285,6 +280,10 @@ function resolveModelReference(value, aliases = {}, knownModels, settingsPath) {
|
|
|
285
280
|
};
|
|
286
281
|
return resolveReference(value, []);
|
|
287
282
|
}
|
|
283
|
+
function modelCapability(value, aliases, knownModels, settingsPath) {
|
|
284
|
+
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
285
|
+
return `${parsed.provider}/${parsed.model}`;
|
|
286
|
+
}
|
|
288
287
|
function reachableTools(pi) {
|
|
289
288
|
const undeclared = pi.getAllTools?.().filter(({ exposure }) => exposure === "codemode" || exposure === "deferred").map(({ name }) => name) ?? [];
|
|
290
289
|
return [.../* @__PURE__ */ new Set([...pi.getActiveTools(), ...undeclared])];
|
|
@@ -295,10 +294,6 @@ function physicalModel(spec, aliases = {}, knownModels, settingsPath) {
|
|
|
295
294
|
const target = resolveModelReference(spec.model, aliases, knownModels, settingsPath);
|
|
296
295
|
return spec.thinking === void 0 ? target : { ...target, thinking: spec.thinking };
|
|
297
296
|
}
|
|
298
|
-
function modelCapability(value, aliases, knownModels, settingsPath) {
|
|
299
|
-
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
300
|
-
return `${parsed.provider}/${parsed.model}`;
|
|
301
|
-
}
|
|
302
297
|
var RESOURCE_PATTERN_OPTIONS = { dot: true, nonegate: true, nocomment: true };
|
|
303
298
|
function resourcePatternBody(pattern) {
|
|
304
299
|
return pattern.startsWith("!") ? pattern.slice(1) : pattern;
|
|
@@ -349,8 +344,9 @@ function validateSchema(schema, at = "schema") {
|
|
|
349
344
|
// src/paths.ts
|
|
350
345
|
import { createHash } from "node:crypto";
|
|
351
346
|
import { realpathSync } from "node:fs";
|
|
347
|
+
import { fileURLToPath } from "node:url";
|
|
352
348
|
import { homedir } from "node:os";
|
|
353
|
-
import { basename, join, resolve } from "node:path";
|
|
349
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
354
350
|
function safePart(value) {
|
|
355
351
|
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
356
352
|
}
|
|
@@ -363,11 +359,21 @@ function canonicalPath(path) {
|
|
|
363
359
|
}
|
|
364
360
|
}
|
|
365
361
|
function extensionIdentity(path) {
|
|
366
|
-
return path.startsWith("builtin:") ? path : canonicalPath(path);
|
|
362
|
+
return path.startsWith("builtin:") ? path : canonicalPath(path.startsWith("file:") ? fileURLToPath(path) : path);
|
|
367
363
|
}
|
|
368
364
|
function sameFilesystemPath(left, right) {
|
|
369
365
|
return canonicalPath(left) === canonicalPath(right);
|
|
370
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
|
+
}
|
|
371
377
|
function workflowProjectSettingsPath(cwd) {
|
|
372
378
|
return join(cwd, ".pi", "pi-extensible-workflows", "settings.json");
|
|
373
379
|
}
|
|
@@ -395,17 +401,6 @@ function budgetUsage(value) {
|
|
|
395
401
|
// src/validation.ts
|
|
396
402
|
import * as acorn from "acorn";
|
|
397
403
|
|
|
398
|
-
// src/roles.ts
|
|
399
|
-
import { readFileSync as readFileSync3, readdirSync, statSync } from "node:fs";
|
|
400
|
-
import { basename as basename3, dirname as dirname4, extname, join as join5, resolve as resolve4 } from "node:path";
|
|
401
|
-
import { getAgentDir as getAgentDir2, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
402
|
-
|
|
403
|
-
// src/registry.ts
|
|
404
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
405
|
-
import { basename as basename2, dirname as dirname3, isAbsolute, join as join4 } from "node:path";
|
|
406
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
407
|
-
import { Value } from "typebox/value";
|
|
408
|
-
|
|
409
404
|
// src/io.ts
|
|
410
405
|
import { execFile } from "node:child_process";
|
|
411
406
|
import { randomUUID } from "node:crypto";
|
|
@@ -426,7 +421,7 @@ async function renameWithRetry(from, to) {
|
|
|
426
421
|
} catch (error) {
|
|
427
422
|
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
428
423
|
if (!retryableRenameError(error) || delayMs === void 0) throw error;
|
|
429
|
-
await new Promise((
|
|
424
|
+
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
430
425
|
}
|
|
431
426
|
}
|
|
432
427
|
}
|
|
@@ -492,7 +487,9 @@ async function git(cwd, args, extraEnv = {}) {
|
|
|
492
487
|
}
|
|
493
488
|
|
|
494
489
|
// src/session-lease.ts
|
|
490
|
+
import { execFileSync } from "node:child_process";
|
|
495
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";
|
|
496
493
|
|
|
497
494
|
// src/decoders.ts
|
|
498
495
|
var INVALID_PERSISTED_VALUE = /* @__PURE__ */ Symbol("invalid persisted value");
|
|
@@ -582,32 +579,19 @@ function decodeAgentResourceSelectors(value) {
|
|
|
582
579
|
function decodeContextFileScopes(value) {
|
|
583
580
|
return decodeArray(value, (scope) => isContextFileScope(scope) ? scope : void 0);
|
|
584
581
|
}
|
|
585
|
-
function
|
|
586
|
-
if (!object(value)) return void 0;
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
const
|
|
590
|
-
const
|
|
591
|
-
const
|
|
592
|
-
const skills = value.skills === void 0 ? void 0 : decodeStringArray(value.skills);
|
|
593
|
-
const extensions = value.extensions === void 0 ? void 0 : decodeStringArray(value.extensions);
|
|
594
|
-
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);
|
|
595
589
|
const contextFiles = value.contextFiles === void 0 ? void 0 : decodeContextFileScopes(value.contextFiles);
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return {
|
|
601
|
-
...prompt === void 0 ? {} : { prompt },
|
|
602
|
-
...description === void 0 ? {} : { description },
|
|
603
|
-
...foldedModel === void 0 ? {} : { model: foldedModel },
|
|
604
|
-
...tools === void 0 ? {} : { tools },
|
|
605
|
-
...skills === void 0 ? {} : { skills },
|
|
606
|
-
...extensions === void 0 ? {} : { extensions },
|
|
607
|
-
...overrideSystemPrompt === void 0 ? {} : { overrideSystemPrompt },
|
|
608
|
-
...contextFiles === void 0 ? {} : { contextFiles },
|
|
609
|
-
...extensionSettings === void 0 ? {} : { extensionSettings }
|
|
610
|
-
};
|
|
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 } };
|
|
611
595
|
}
|
|
612
596
|
function decodeWorkflowMetadata(value) {
|
|
613
597
|
if (!object(value) || typeof value.name !== "string") return void 0;
|
|
@@ -716,17 +700,22 @@ function decodeScheduledAgentOptions(value) {
|
|
|
716
700
|
const parentBreadcrumb = optionalString(value.parentBreadcrumb);
|
|
717
701
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
718
702
|
const model = optionalString(value.model);
|
|
719
|
-
const role = typeof value.role === "string" ? value.role : void 0;
|
|
720
703
|
const contextFiles = value.contextFiles === void 0 ? void 0 : decodeContextFileScopes(value.contextFiles);
|
|
721
704
|
const schema = value.schema === void 0 ? void 0 : decodeJsonObject(value.schema);
|
|
722
705
|
const retries = optionalNumber(value.retries);
|
|
723
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;
|
|
724
713
|
const agentOptions = value.agentOptions === void 0 ? void 0 : decodeJsonObject(value.agentOptions);
|
|
725
714
|
const agentIdentity = value.agentIdentity === void 0 ? void 0 : decodeIdentity(value.agentIdentity);
|
|
726
715
|
const sessionPath = optionalString(value.sessionPath);
|
|
727
716
|
const continuity = value.continuity === void 0 ? void 0 : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
728
717
|
const extensionSettings = value.extensionSettings === void 0 ? void 0 : decodeWorkflowExtensions(value.extensionSettings);
|
|
729
|
-
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;
|
|
730
719
|
return {
|
|
731
720
|
label: value.label,
|
|
732
721
|
...requestedLabel === void 0 ? {} : { requestedLabel },
|
|
@@ -737,7 +726,6 @@ function decodeScheduledAgentOptions(value) {
|
|
|
737
726
|
...extensions === void 0 ? {} : { extensions },
|
|
738
727
|
...worktreeOwner === void 0 ? {} : { worktreeOwner },
|
|
739
728
|
...model === void 0 ? {} : { model },
|
|
740
|
-
...role === void 0 ? {} : { role },
|
|
741
729
|
...contextFiles === void 0 ? {} : { contextFiles },
|
|
742
730
|
...schema === void 0 ? {} : { schema },
|
|
743
731
|
...retries === void 0 ? {} : { retries },
|
|
@@ -745,6 +733,9 @@ function decodeScheduledAgentOptions(value) {
|
|
|
745
733
|
...sessionPath === void 0 ? {} : { sessionPath },
|
|
746
734
|
...continuity === void 0 ? {} : { continuity },
|
|
747
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 } } : {},
|
|
748
739
|
...agentIdentity === void 0 ? {} : { agentIdentity },
|
|
749
740
|
...extensionSettings === void 0 ? {} : { extensionSettings }
|
|
750
741
|
};
|
|
@@ -785,11 +776,10 @@ function decodeAgentResourceInspection(value) {
|
|
|
785
776
|
const sources = sourceRecord === void 0 ? void 0 : {
|
|
786
777
|
global: decodeAgentResourceSelectors(sourceRecord.global),
|
|
787
778
|
project: decodeAgentResourceSelectors(sourceRecord.project),
|
|
788
|
-
...sourceRecord.role === void 0 ? {} : { role: decodeAgentResourceSelectors(sourceRecord.role) },
|
|
789
779
|
...sourceRecord.call === void 0 ? {} : { call: decodeAgentResourceSelectors(sourceRecord.call) }
|
|
790
780
|
};
|
|
791
|
-
if (!selectors || !skills || !extensions || !tools || !unmatchedSkills || !unmatchedExtensions || !unmatchedTools || rawSources !== void 0 && (!sources || !sources.global || !sources.project || sources.
|
|
792
|
-
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 } } } };
|
|
793
783
|
}
|
|
794
784
|
function decodeAgentSetupSummary(value) {
|
|
795
785
|
if (!object(value) || typeof value.cwd !== "string") return void 0;
|
|
@@ -827,7 +817,6 @@ function decodeAgent(value) {
|
|
|
827
817
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
828
818
|
const handle = optionalString(value.handle);
|
|
829
819
|
const continuity = value.continuity === void 0 ? void 0 : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
830
|
-
const role = optionalString(value.role);
|
|
831
820
|
const requestedModel = optionalString(value.requestedModel);
|
|
832
821
|
const startedAt = optionalNumber(value.startedAt);
|
|
833
822
|
const durationMs = optionalNumber(value.durationMs);
|
|
@@ -836,7 +825,7 @@ function decodeAgent(value) {
|
|
|
836
825
|
const toolCalls = value.toolCalls === void 0 ? void 0 : decodeArray(value.toolCalls, decodeAgentToolCall);
|
|
837
826
|
const activity = value.activity === void 0 ? void 0 : decodeAgentActivity(value.activity);
|
|
838
827
|
const lastEventAt = optionalNumber(value.lastEventAt);
|
|
839
|
-
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 ||
|
|
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;
|
|
840
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;
|
|
841
830
|
return {
|
|
842
831
|
...systemPrompt === void 0 ? {} : { systemPrompt },
|
|
@@ -853,7 +842,6 @@ function decodeAgent(value) {
|
|
|
853
842
|
...worktreeOwner === void 0 ? {} : { worktreeOwner },
|
|
854
843
|
...handle === void 0 ? {} : { handle, turn: value.turn },
|
|
855
844
|
...continuity === void 0 ? {} : { continuity },
|
|
856
|
-
...role === void 0 ? {} : { role },
|
|
857
845
|
...requestedModel === void 0 ? {} : { requestedModel },
|
|
858
846
|
model,
|
|
859
847
|
tools,
|
|
@@ -1001,13 +989,11 @@ function decodeLaunchSnapshot(value) {
|
|
|
1001
989
|
const phases = value.phases === void 0 ? void 0 : decodeStringArray(value.phases);
|
|
1002
990
|
const models = decodeStringArray(value.models);
|
|
1003
991
|
const tools = decodeStringArray(value.tools);
|
|
1004
|
-
const
|
|
1005
|
-
const roles = value.roles === void 0 ? void 0 : decodeRecord(value.roles, decodeAgentDefinition);
|
|
1006
|
-
const projectRoles = value.projectRoles === void 0 ? void 0 : decodeStringArray(value.projectRoles);
|
|
992
|
+
const agentConfigurations = decodeRecord(value.agentConfigurations, decodePreparedAgentConfiguration);
|
|
1007
993
|
const schemas = decodeArray(value.schemas, decodeJsonObject);
|
|
1008
|
-
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;
|
|
1009
995
|
return {
|
|
1010
|
-
|
|
996
|
+
identityVersion: LAUNCH_SNAPSHOT_IDENTITY_VERSION,
|
|
1011
997
|
...launchMode === void 0 ? {} : { launchMode },
|
|
1012
998
|
script: value.script,
|
|
1013
999
|
args,
|
|
@@ -1020,9 +1006,7 @@ function decodeLaunchSnapshot(value) {
|
|
|
1020
1006
|
...phases === void 0 ? {} : { phases },
|
|
1021
1007
|
models,
|
|
1022
1008
|
tools,
|
|
1023
|
-
|
|
1024
|
-
...roles === void 0 ? {} : { roles },
|
|
1025
|
-
...projectRoles === void 0 ? {} : { projectRoles },
|
|
1009
|
+
agentConfigurations,
|
|
1026
1010
|
schemas
|
|
1027
1011
|
};
|
|
1028
1012
|
}
|
|
@@ -1103,18 +1087,43 @@ function decodeSystemPromptArtifact(value) {
|
|
|
1103
1087
|
}
|
|
1104
1088
|
|
|
1105
1089
|
// src/session-lease.ts
|
|
1106
|
-
|
|
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") {
|
|
1107
1115
|
try {
|
|
1108
1116
|
process.kill(pid, 0);
|
|
1109
1117
|
} catch (error) {
|
|
1110
1118
|
return !isNodeError(error, "ESRCH");
|
|
1111
1119
|
}
|
|
1112
|
-
if (startedAt
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
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");
|
|
1118
1127
|
}
|
|
1119
1128
|
return true;
|
|
1120
1129
|
}
|
|
@@ -1124,7 +1133,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
1124
1133
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
1125
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";
|
|
1126
1135
|
import { homedir as homedir2 } from "node:os";
|
|
1127
|
-
import { dirname, join as
|
|
1136
|
+
import { dirname, join as join3, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
1128
1137
|
import { promisify as promisify2 } from "node:util";
|
|
1129
1138
|
var executeFile = promisify2(execFile2);
|
|
1130
1139
|
var WORKTREE_POST_CREATE_TIMEOUT_MS = 6e4;
|
|
@@ -1135,36 +1144,36 @@ var SYSTEM_PROMPT_BODIES = "bodies";
|
|
|
1135
1144
|
var SYSTEM_PROMPT_SEQUENCE = "sequence";
|
|
1136
1145
|
var SYSTEM_PROMPT_ARTIFACT = { version: 2, format: "append-only", storage: SYSTEM_PROMPT_STORAGE };
|
|
1137
1146
|
function summaryArtifacts(directory) {
|
|
1138
|
-
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") };
|
|
1139
1148
|
}
|
|
1140
1149
|
function summaryFromRun(run, directory, journal, previous, fallbackCreatedAt, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1141
1150
|
const createdAt = typeof previous?.createdAt === "string" ? previous.createdAt : fallbackCreatedAt;
|
|
1142
1151
|
const failedAt = run.failedAt ?? run.error?.failedAt;
|
|
1143
1152
|
const replayablePaths = [.../* @__PURE__ */ new Set([...run.retry?.completedPaths ?? [], ...Object.keys(journal.completed)])];
|
|
1144
1153
|
const incompletePaths = [.../* @__PURE__ */ new Set([...run.retry?.incompletePaths ?? [], ...failedAt ? [failedAt] : []])];
|
|
1145
|
-
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) };
|
|
1146
1155
|
}
|
|
1147
1156
|
function systemPromptStoragePath(directory) {
|
|
1148
|
-
return
|
|
1157
|
+
return join3(directory, SYSTEM_PROMPT_STORAGE);
|
|
1149
1158
|
}
|
|
1150
1159
|
function systemPromptRecordsPath(directory) {
|
|
1151
|
-
return
|
|
1160
|
+
return join3(systemPromptStoragePath(directory), SYSTEM_PROMPT_RECORDS);
|
|
1152
1161
|
}
|
|
1153
1162
|
function systemPromptBodiesPath(directory) {
|
|
1154
|
-
return
|
|
1163
|
+
return join3(systemPromptStoragePath(directory), SYSTEM_PROMPT_BODIES);
|
|
1155
1164
|
}
|
|
1156
1165
|
function systemPromptSequencePath(directory) {
|
|
1157
|
-
return
|
|
1166
|
+
return join3(systemPromptStoragePath(directory), SYSTEM_PROMPT_SEQUENCE);
|
|
1158
1167
|
}
|
|
1159
1168
|
function systemPromptRecordName(sequence) {
|
|
1160
1169
|
return `${String(sequence).padStart(20, "0")}.json`;
|
|
1161
1170
|
}
|
|
1162
1171
|
async function createSystemPromptStorage(directory, writeArtifact) {
|
|
1163
1172
|
const storage = systemPromptStoragePath(directory);
|
|
1164
|
-
await mkdir2(
|
|
1165
|
-
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 });
|
|
1166
1175
|
await atomicWriteFile(systemPromptSequencePath(directory), "0\n");
|
|
1167
|
-
if (writeArtifact) await atomicJson(
|
|
1176
|
+
if (writeArtifact) await atomicJson(join3(directory, "system-prompts.json"), SYSTEM_PROMPT_ARTIFACT);
|
|
1168
1177
|
}
|
|
1169
1178
|
var RunStore = class _RunStore {
|
|
1170
1179
|
constructor(cwd, sessionId, runId, home = homedir2()) {
|
|
@@ -1173,7 +1182,7 @@ var RunStore = class _RunStore {
|
|
|
1173
1182
|
this.runId = runId;
|
|
1174
1183
|
this.home = home;
|
|
1175
1184
|
this.cwd = resolve2(cwd);
|
|
1176
|
-
this.directory =
|
|
1185
|
+
this.directory = join3(runsDirectory(this.cwd, sessionId, home), safePart(runId));
|
|
1177
1186
|
}
|
|
1178
1187
|
cwd;
|
|
1179
1188
|
sessionId;
|
|
@@ -1195,19 +1204,19 @@ var RunStore = class _RunStore {
|
|
|
1195
1204
|
}
|
|
1196
1205
|
async create(run, snapshot) {
|
|
1197
1206
|
this.#assertRunIdentity(run, "INTERNAL_ERROR", "Run identity does not match its session-scoped store");
|
|
1198
|
-
const temporary =
|
|
1207
|
+
const temporary = join3(dirname(this.directory), `.${safePart(this.runId)}.${String(process.pid)}.${randomUUID2()}.tmp`);
|
|
1199
1208
|
await mkdir2(dirname(this.directory), { recursive: true, mode: 448 });
|
|
1200
1209
|
await mkdir2(temporary, { mode: 448 });
|
|
1201
1210
|
try {
|
|
1202
|
-
await writeFile2(
|
|
1203
|
-
await atomicJson(
|
|
1204
|
-
await atomicJson(
|
|
1205
|
-
await atomicJson(
|
|
1206
|
-
await atomicJson(
|
|
1207
|
-
await atomicJson(
|
|
1208
|
-
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);
|
|
1209
1218
|
await createSystemPromptStorage(temporary, true);
|
|
1210
|
-
await atomicJson(
|
|
1219
|
+
await atomicJson(join3(temporary, "summary.json"), summaryFromRun(run, this.directory, { completed: {} }, void 0, (/* @__PURE__ */ new Date()).toISOString()));
|
|
1211
1220
|
await rename3(temporary, this.directory);
|
|
1212
1221
|
} catch (error) {
|
|
1213
1222
|
await rm3(temporary, { recursive: true, force: true });
|
|
@@ -1216,12 +1225,12 @@ var RunStore = class _RunStore {
|
|
|
1216
1225
|
}
|
|
1217
1226
|
async refreshSummary() {
|
|
1218
1227
|
const write = this.summaryLane.run(async () => {
|
|
1219
|
-
const run = decodePersistedRun(await json(
|
|
1220
|
-
const journal = decodeJournal(await json(
|
|
1221
|
-
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);
|
|
1222
1231
|
if (!run || !journal) throw new Error("Persisted run or journal is invalid");
|
|
1223
|
-
const fallbackCreatedAt = await stat2(
|
|
1224
|
-
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));
|
|
1225
1234
|
});
|
|
1226
1235
|
await write;
|
|
1227
1236
|
}
|
|
@@ -1231,7 +1240,7 @@ var RunStore = class _RunStore {
|
|
|
1231
1240
|
}
|
|
1232
1241
|
async isComplete() {
|
|
1233
1242
|
try {
|
|
1234
|
-
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"))]);
|
|
1235
1244
|
return true;
|
|
1236
1245
|
} catch {
|
|
1237
1246
|
return false;
|
|
@@ -1239,18 +1248,18 @@ var RunStore = class _RunStore {
|
|
|
1239
1248
|
}
|
|
1240
1249
|
async load() {
|
|
1241
1250
|
await this.stateLane.run(async () => void 0);
|
|
1242
|
-
const rawRun = await json(
|
|
1251
|
+
const rawRun = await json(join3(this.directory, "state.json"));
|
|
1243
1252
|
const run = decodePersistedRun(rawRun, true);
|
|
1244
1253
|
if (!run) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted run is invalid");
|
|
1245
1254
|
this.#assertRunIdentity(run, "RESUME_INCOMPATIBLE", "Persisted run belongs to another cwd or Pi session");
|
|
1246
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");
|
|
1247
|
-
const snapshot = decodeLaunchSnapshot(await json(
|
|
1256
|
+
const snapshot = decodeLaunchSnapshot(await json(join3(this.directory, "snapshot.json")));
|
|
1248
1257
|
if (!snapshot) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted launch snapshot is invalid");
|
|
1249
1258
|
return { run, snapshot: loadLaunchSnapshot(snapshot) };
|
|
1250
1259
|
}
|
|
1251
1260
|
async loadStatus() {
|
|
1252
1261
|
await this.stateLane.run(async () => void 0);
|
|
1253
|
-
const run = decodePersistedRun(await json(
|
|
1262
|
+
const run = decodePersistedRun(await json(join3(this.directory, "state.json")), true);
|
|
1254
1263
|
if (!run) throw new WorkflowError("RUN_NOT_FOUND", "Persisted run is invalid");
|
|
1255
1264
|
this.#assertRunIdentity(run, "RUN_NOT_FOUND", "Persisted run does not belong to this project");
|
|
1256
1265
|
return run;
|
|
@@ -1259,11 +1268,11 @@ var RunStore = class _RunStore {
|
|
|
1259
1268
|
await this.stateLane.run(async () => void 0);
|
|
1260
1269
|
await this.journalLane.run(async () => void 0);
|
|
1261
1270
|
await this.summaryLane.run(async () => void 0);
|
|
1262
|
-
const run = decodePersistedRun(await json(
|
|
1263
|
-
const journal = decodeJournal(await json(
|
|
1264
|
-
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);
|
|
1265
1274
|
if (!run || !journal) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted run or journal is invalid");
|
|
1266
|
-
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"))]);
|
|
1267
1276
|
const fallbackCreatedAt = new Date(stateStat.mtimeMs).toISOString();
|
|
1268
1277
|
const previousUpdatedAt = previous?.updatedAt === void 0 ? Number.NaN : Date.parse(previous.updatedAt);
|
|
1269
1278
|
const updatedAt = new Date(Math.max(stateStat.mtimeMs, journalStat.mtimeMs, Number.isNaN(previousUpdatedAt) ? 0 : previousUpdatedAt)).toISOString();
|
|
@@ -1272,41 +1281,55 @@ var RunStore = class _RunStore {
|
|
|
1272
1281
|
async saveState(run) {
|
|
1273
1282
|
const write = this.stateLane.run(async () => {
|
|
1274
1283
|
this.#assertRunIdentity(run, "INTERNAL_ERROR", "Run identity does not match its session-scoped store");
|
|
1275
|
-
await atomicJson(
|
|
1284
|
+
await atomicJson(join3(this.directory, "state.json"), run);
|
|
1276
1285
|
this.refreshSummaryBestEffort();
|
|
1277
1286
|
});
|
|
1278
1287
|
await write;
|
|
1279
1288
|
}
|
|
1280
1289
|
async updateState(update) {
|
|
1281
1290
|
const write = this.stateLane.run(async () => {
|
|
1282
|
-
const current = decodePersistedRun(await json(
|
|
1291
|
+
const current = decodePersistedRun(await json(join3(this.directory, "state.json")));
|
|
1283
1292
|
if (!current) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted run is invalid");
|
|
1284
1293
|
this.#assertRunIdentity(current, "RESUME_INCOMPATIBLE", "Persisted run belongs to another cwd or Pi session");
|
|
1285
1294
|
const result = await update(current);
|
|
1286
1295
|
this.#assertRunIdentity(result, "INTERNAL_ERROR", "Run identity does not match its session-scoped store");
|
|
1287
|
-
await atomicJson(
|
|
1296
|
+
await atomicJson(join3(this.directory, "state.json"), result);
|
|
1288
1297
|
this.refreshSummaryBestEffort();
|
|
1289
1298
|
return result;
|
|
1290
1299
|
});
|
|
1291
1300
|
return write;
|
|
1292
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
|
+
}
|
|
1293
1316
|
async saveSnapshot(snapshot) {
|
|
1294
|
-
const write = this.launchSnapshotLane.run(() => atomicJson(
|
|
1317
|
+
const write = this.launchSnapshotLane.run(() => atomicJson(join3(this.directory, "snapshot.json"), snapshot));
|
|
1295
1318
|
await write;
|
|
1296
1319
|
}
|
|
1297
1320
|
async appendEvent(event) {
|
|
1298
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]] }));
|
|
1299
1322
|
}
|
|
1300
1323
|
async saveOwnership(nodes) {
|
|
1301
|
-
await atomicJson(
|
|
1324
|
+
await atomicJson(join3(this.directory, "ownership.json"), nodes);
|
|
1302
1325
|
}
|
|
1303
1326
|
async loadOwnership() {
|
|
1304
|
-
const nodes = decodeOwnershipRecords(await json(
|
|
1327
|
+
const nodes = decodeOwnershipRecords(await json(join3(this.directory, "ownership.json")));
|
|
1305
1328
|
if (!nodes) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted ownership records are invalid");
|
|
1306
1329
|
return nodes;
|
|
1307
1330
|
}
|
|
1308
1331
|
systemPromptPath() {
|
|
1309
|
-
return
|
|
1332
|
+
return join3(this.directory, "system-prompts.json");
|
|
1310
1333
|
}
|
|
1311
1334
|
async readSystemPromptArtifact() {
|
|
1312
1335
|
try {
|
|
@@ -1320,7 +1343,7 @@ var RunStore = class _RunStore {
|
|
|
1320
1343
|
}
|
|
1321
1344
|
async appendSystemPromptV2(entry) {
|
|
1322
1345
|
const sha256 = createHash2("sha256").update(entry.prompt).digest("hex");
|
|
1323
|
-
const bodyPath =
|
|
1346
|
+
const bodyPath = join3(systemPromptBodiesPath(this.directory), sha256);
|
|
1324
1347
|
try {
|
|
1325
1348
|
await access(bodyPath);
|
|
1326
1349
|
} catch (error) {
|
|
@@ -1332,7 +1355,7 @@ var RunStore = class _RunStore {
|
|
|
1332
1355
|
const sequence = previous + 1;
|
|
1333
1356
|
await atomicWriteFile(systemPromptSequencePath(this.directory), `${String(sequence)}
|
|
1334
1357
|
`);
|
|
1335
|
-
await atomicJson(
|
|
1358
|
+
await atomicJson(join3(systemPromptRecordsPath(this.directory), systemPromptRecordName(sequence)), { sessionId: entry.sessionId, attempt: entry.attempt, turn: entry.turn, sha256 });
|
|
1336
1359
|
}
|
|
1337
1360
|
async migrateSystemPrompts(legacy) {
|
|
1338
1361
|
await rm3(systemPromptStoragePath(this.directory), { recursive: true, force: true });
|
|
@@ -1364,14 +1387,14 @@ var RunStore = class _RunStore {
|
|
|
1364
1387
|
const recordSequence = Number(match[1]);
|
|
1365
1388
|
if (!Number.isSafeInteger(recordSequence) || recordSequence < 1) throw new Error("Persisted system-prompt records are invalid");
|
|
1366
1389
|
highest = Math.max(highest, recordSequence);
|
|
1367
|
-
const record2 = await json(
|
|
1390
|
+
const record2 = await json(join3(systemPromptRecordsPath(this.directory), name));
|
|
1368
1391
|
if (!object(record2)) throw new Error("Persisted system-prompt record is invalid");
|
|
1369
1392
|
const sessionId = record2.sessionId;
|
|
1370
1393
|
const attempt = record2.attempt;
|
|
1371
1394
|
const turn = record2.turn;
|
|
1372
1395
|
const sha256 = record2.sha256;
|
|
1373
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");
|
|
1374
|
-
const prompt = await readFile3(
|
|
1397
|
+
const prompt = await readFile3(join3(systemPromptBodiesPath(this.directory), sha256), "utf8");
|
|
1375
1398
|
if (createHash2("sha256").update(prompt).digest("hex") !== sha256) throw new Error("Persisted system-prompt body is invalid");
|
|
1376
1399
|
entries.push({ sessionId, attempt, turn, sha256, prompt });
|
|
1377
1400
|
}
|
|
@@ -1394,7 +1417,7 @@ var RunStore = class _RunStore {
|
|
|
1394
1417
|
throw new Error("Persisted system prompts are invalid");
|
|
1395
1418
|
}
|
|
1396
1419
|
async #decodeJournal() {
|
|
1397
|
-
const journal = decodeJournal(await json(
|
|
1420
|
+
const journal = decodeJournal(await json(join3(this.directory, "journal.json")));
|
|
1398
1421
|
if (!journal) throw new WorkflowError("RESUME_INCOMPATIBLE", "Persisted journal is invalid");
|
|
1399
1422
|
return journal;
|
|
1400
1423
|
}
|
|
@@ -1408,7 +1431,7 @@ var RunStore = class _RunStore {
|
|
|
1408
1431
|
const journal = await this.#decodeJournal();
|
|
1409
1432
|
journal.awaiting ??= {};
|
|
1410
1433
|
const result = await update(journal);
|
|
1411
|
-
await atomicJson(
|
|
1434
|
+
await atomicJson(join3(this.directory, "journal.json"), journal);
|
|
1412
1435
|
this.refreshSummaryBestEffort();
|
|
1413
1436
|
return result;
|
|
1414
1437
|
});
|
|
@@ -1518,10 +1541,10 @@ var RunStore = class _RunStore {
|
|
|
1518
1541
|
}
|
|
1519
1542
|
expectedWorktree(owner) {
|
|
1520
1543
|
const key = this.#worktreeKey(owner);
|
|
1521
|
-
return { path:
|
|
1544
|
+
return { path: join3(this.directory, "worktrees", key), branch: `pi-extensible-workflows/${safePart(this.runId)}/${key}` };
|
|
1522
1545
|
}
|
|
1523
1546
|
markerPath(owner) {
|
|
1524
|
-
return
|
|
1547
|
+
return join3(this.directory, `worktree-${this.#worktreeKey(owner)}.creating`);
|
|
1525
1548
|
}
|
|
1526
1549
|
namedWorktreeOwner(name) {
|
|
1527
1550
|
if (!name.trim()) throw new WorkflowError("WORKTREE_FAILED", "Named worktree names must be non-empty");
|
|
@@ -1564,13 +1587,13 @@ var RunStore = class _RunStore {
|
|
|
1564
1587
|
}
|
|
1565
1588
|
structuralWorktree(owner, record2) {
|
|
1566
1589
|
const expected = this.expectedWorktree(owner);
|
|
1567
|
-
const relativePath =
|
|
1568
|
-
const relativeCwd =
|
|
1569
|
-
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}`);
|
|
1570
1593
|
return record2;
|
|
1571
1594
|
}
|
|
1572
1595
|
async #loadWorktreeRecords(missingOk = true) {
|
|
1573
|
-
const rawRecords = await json(
|
|
1596
|
+
const rawRecords = await json(join3(this.directory, "worktrees.json")).catch((error) => {
|
|
1574
1597
|
if (missingOk && isNodeError(error, "ENOENT")) return [];
|
|
1575
1598
|
throw error;
|
|
1576
1599
|
});
|
|
@@ -1580,7 +1603,7 @@ var RunStore = class _RunStore {
|
|
|
1580
1603
|
}
|
|
1581
1604
|
async borrowedWorktreeRecords(wait = true) {
|
|
1582
1605
|
if (wait) await this.borrowedWorktreeLane.run(async () => void 0);
|
|
1583
|
-
const rawRecords = await json(
|
|
1606
|
+
const rawRecords = await json(join3(this.directory, "borrowed-worktrees.json")).catch((error) => {
|
|
1584
1607
|
if (isNodeError(error, "ENOENT")) return [];
|
|
1585
1608
|
throw error;
|
|
1586
1609
|
});
|
|
@@ -1705,11 +1728,11 @@ var RunStore = class _RunStore {
|
|
|
1705
1728
|
const reference = this.structuralWorktree(owner, record2);
|
|
1706
1729
|
paths.add(resolve2(reference.path));
|
|
1707
1730
|
}
|
|
1708
|
-
const entries = await readdir2(
|
|
1731
|
+
const entries = await readdir2(join3(this.directory, "worktrees"), { withFileTypes: true }).catch((error) => {
|
|
1709
1732
|
if (isNodeError(error, "ENOENT")) return [];
|
|
1710
1733
|
throw error;
|
|
1711
1734
|
});
|
|
1712
|
-
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)}`);
|
|
1713
1736
|
} catch (error) {
|
|
1714
1737
|
throw coerceWorkflowError("WORKTREE_FAILED", error);
|
|
1715
1738
|
}
|
|
@@ -1761,7 +1784,7 @@ var RunStore = class _RunStore {
|
|
|
1761
1784
|
}
|
|
1762
1785
|
async cleanupOrphanWorktrees() {
|
|
1763
1786
|
const entries = await readdir2(this.directory).catch(() => []);
|
|
1764
|
-
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));
|
|
1765
1788
|
}
|
|
1766
1789
|
async validateWorktree(owner, cwd) {
|
|
1767
1790
|
try {
|
|
@@ -1804,8 +1827,8 @@ var RunStore = class _RunStore {
|
|
|
1804
1827
|
try {
|
|
1805
1828
|
const root = (await git(this.cwd, ["rev-parse", "--show-toplevel"])).trim();
|
|
1806
1829
|
const [canonicalRoot, canonicalCwd] = await Promise.all([realpath(root), realpath(this.cwd)]);
|
|
1807
|
-
const launchRelative =
|
|
1808
|
-
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");
|
|
1809
1832
|
await this.cleanupMarker(markerPath);
|
|
1810
1833
|
const dirty = (await git(root, ["status", "--porcelain", "--untracked-files=normal"])).trim();
|
|
1811
1834
|
if (dirty) {
|
|
@@ -1821,7 +1844,7 @@ var RunStore = class _RunStore {
|
|
|
1821
1844
|
throw new Error("repository has no commits");
|
|
1822
1845
|
}
|
|
1823
1846
|
await mkdir2(dirname(path), { recursive: true, mode: 448 });
|
|
1824
|
-
const record2 = { owner, path, branch, cwd:
|
|
1847
|
+
const record2 = { owner, path, branch, cwd: join3(path, launchRelative), base: commit };
|
|
1825
1848
|
await atomicJson(markerPath, { owner, path, branch, base: commit });
|
|
1826
1849
|
markerWritten = true;
|
|
1827
1850
|
await git(root, ["branch", branch, commit]);
|
|
@@ -1866,7 +1889,7 @@ var RunStore = class _RunStore {
|
|
|
1866
1889
|
return;
|
|
1867
1890
|
}
|
|
1868
1891
|
records.push(binding);
|
|
1869
|
-
await atomicJson(
|
|
1892
|
+
await atomicJson(join3(this.directory, "borrowed-worktrees.json"), records);
|
|
1870
1893
|
});
|
|
1871
1894
|
await write;
|
|
1872
1895
|
}
|
|
@@ -1947,12 +1970,12 @@ var RunStore = class _RunStore {
|
|
|
1947
1970
|
return changed;
|
|
1948
1971
|
}
|
|
1949
1972
|
async saveResult(value) {
|
|
1950
|
-
const path =
|
|
1973
|
+
const path = join3(this.directory, "result.json");
|
|
1951
1974
|
await atomicPrettyJson(path, value);
|
|
1952
1975
|
return path;
|
|
1953
1976
|
}
|
|
1954
1977
|
async resultBytes() {
|
|
1955
|
-
return (await stat2(
|
|
1978
|
+
return (await stat2(join3(this.directory, "result.json"))).size;
|
|
1956
1979
|
}
|
|
1957
1980
|
async delete(confirmed) {
|
|
1958
1981
|
if (!confirmed) throw new WorkflowError("CANCELLED", "Run deletion requires confirmation");
|
|
@@ -1975,19 +1998,20 @@ var RunStore = class _RunStore {
|
|
|
1975
1998
|
|
|
1976
1999
|
// src/settings.ts
|
|
1977
2000
|
import { mkdirSync, readFileSync } from "node:fs";
|
|
1978
|
-
import {
|
|
1979
|
-
import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
|
|
1980
|
-
import { fileURLToPath } from "node:url";
|
|
2001
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
1981
2002
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
1982
|
-
|
|
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";
|
|
1983
2007
|
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
1984
2008
|
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
1985
|
-
return
|
|
2009
|
+
return join4(agentDir, WORKFLOW_DIRECTORY, "settings.json");
|
|
1986
2010
|
}
|
|
1987
2011
|
function normalizedResourcePath(value, settingsPath) {
|
|
1988
2012
|
if (value === "*" || value.startsWith("builtin:")) return value;
|
|
1989
|
-
let expanded = value === "~" ? homedir3() : value.startsWith("~/") || value.startsWith("~\\") ?
|
|
1990
|
-
if (expanded.startsWith("file://")) expanded =
|
|
2013
|
+
let expanded = value === "~" ? homedir3() : value.startsWith("~/") || value.startsWith("~\\") ? join4(homedir3(), value.slice(2)) : value;
|
|
2014
|
+
if (expanded.startsWith("file://")) expanded = fileURLToPath2(expanded);
|
|
1991
2015
|
const resolved = resolve3(dirname2(settingsPath), expanded);
|
|
1992
2016
|
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
1993
2017
|
if (resourcePatternHasMagic(expanded)) {
|
|
@@ -2044,18 +2068,19 @@ function selectorsFromSettings(settings) {
|
|
|
2044
2068
|
function selectorSet(value) {
|
|
2045
2069
|
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2046
2070
|
}
|
|
2047
|
-
function validateContextFileScopes(value,
|
|
2071
|
+
function validateContextFileScopes(value, sourcePath) {
|
|
2048
2072
|
if (value === void 0) return void 0;
|
|
2049
|
-
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`);
|
|
2050
2074
|
return [...value];
|
|
2051
2075
|
}
|
|
2052
2076
|
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS") {
|
|
2053
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;
|
|
2054
2081
|
const base = `${settingsPath}.extensionSettings`;
|
|
2055
|
-
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2056
2082
|
const normalized = {};
|
|
2057
|
-
for (const [namespace, raw] of Object.entries(
|
|
2058
|
-
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2083
|
+
for (const [namespace, raw] of Object.entries(generic)) {
|
|
2059
2084
|
if (namespace === "herdr") {
|
|
2060
2085
|
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2061
2086
|
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
@@ -2070,7 +2095,6 @@ function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "IN
|
|
|
2070
2095
|
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port } });
|
|
2071
2096
|
continue;
|
|
2072
2097
|
}
|
|
2073
|
-
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2074
2098
|
normalized[namespace] = structuredClone(raw);
|
|
2075
2099
|
}
|
|
2076
2100
|
return deepFreeze(normalized);
|
|
@@ -2147,41 +2171,21 @@ function loadSettingsOverrides(path) {
|
|
|
2147
2171
|
}
|
|
2148
2172
|
function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2149
2173
|
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2150
|
-
const
|
|
2151
|
-
const
|
|
2152
|
-
const
|
|
2153
|
-
const
|
|
2154
|
-
const
|
|
2155
|
-
const projectSelectors = selectorsFromSettings(project);
|
|
2156
|
-
const effectiveSelectors = selectorSet({
|
|
2157
|
-
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2158
|
-
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2159
|
-
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2160
|
-
});
|
|
2161
|
-
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2162
|
-
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2163
|
-
const sources = {
|
|
2164
|
-
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2165
|
-
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2166
|
-
skills: sourceFor("skills"),
|
|
2167
|
-
extensions: sourceFor("extensions"),
|
|
2168
|
-
tools: sourceFor("tools"),
|
|
2169
|
-
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2170
|
-
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2171
|
-
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2172
|
-
};
|
|
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) };
|
|
2173
2179
|
const effective = Object.freeze({
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
...
|
|
2179
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2180
|
-
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2181
|
-
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2182
|
-
...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 ?? []] }
|
|
2183
2185
|
});
|
|
2184
|
-
|
|
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 };
|
|
2185
2189
|
}
|
|
2186
2190
|
function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
2187
2191
|
for (const name of names) {
|
|
@@ -2198,42 +2202,63 @@ function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = wo
|
|
|
2198
2202
|
const global = selectorSet(selectorsFromSettings(resolved.global));
|
|
2199
2203
|
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
2200
2204
|
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
2201
|
-
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;
|
|
2202
2254
|
}
|
|
2203
2255
|
|
|
2204
2256
|
// src/registry.ts
|
|
2257
|
+
import { Value } from "typebox/value";
|
|
2205
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"]);
|
|
2206
2259
|
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
2207
2260
|
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
2208
2261
|
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
2209
|
-
function normalizeRoleDirectory(value) {
|
|
2210
|
-
try {
|
|
2211
|
-
if (value instanceof URL) {
|
|
2212
|
-
if (value.protocol !== "file:") fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
2213
|
-
return fileURLToPath2(value);
|
|
2214
|
-
}
|
|
2215
|
-
if (typeof value === "string") {
|
|
2216
|
-
if (value.startsWith("file:")) return fileURLToPath2(value);
|
|
2217
|
-
if (isAbsolute(value)) return value;
|
|
2218
|
-
}
|
|
2219
|
-
} catch (error) {
|
|
2220
|
-
fail("INVALID_METADATA", `Invalid workflow role directory: ${errorText(error)}`);
|
|
2221
|
-
}
|
|
2222
|
-
fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
2223
|
-
}
|
|
2224
|
-
function isBuiltinRoleDirectory(path) {
|
|
2225
|
-
const canonical = canonicalPath(path);
|
|
2226
|
-
const starterDirectory = dirname3(canonical);
|
|
2227
|
-
if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
|
|
2228
|
-
const distributionDirectory = dirname3(starterDirectory);
|
|
2229
|
-
const packageRoot2 = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
|
|
2230
|
-
try {
|
|
2231
|
-
const metadata = JSON.parse(readFileSync2(join4(packageRoot2, "package.json"), "utf8"));
|
|
2232
|
-
return object(metadata) && isCorePackageName(metadata.name);
|
|
2233
|
-
} catch {
|
|
2234
|
-
return false;
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
2262
|
function catalogSchema(schema, at) {
|
|
2238
2263
|
validateSchema(schema, at);
|
|
2239
2264
|
return structuredClone(schema);
|
|
@@ -2242,9 +2267,9 @@ var WorkflowRegistry = class {
|
|
|
2242
2267
|
#extensions = /* @__PURE__ */ new Set();
|
|
2243
2268
|
#functionSources = /* @__PURE__ */ new Map();
|
|
2244
2269
|
#globals = /* @__PURE__ */ new Map();
|
|
2270
|
+
#preparationHooks = /* @__PURE__ */ new Map();
|
|
2245
2271
|
#hooks = /* @__PURE__ */ new Map();
|
|
2246
2272
|
#agentAttemptActions = /* @__PURE__ */ new Map();
|
|
2247
|
-
#roleDirectories = /* @__PURE__ */ new Map();
|
|
2248
2273
|
#modelAliases = /* @__PURE__ */ new Map();
|
|
2249
2274
|
#settingsValidators = /* @__PURE__ */ new Set();
|
|
2250
2275
|
#frozen = false;
|
|
@@ -2264,9 +2289,10 @@ var WorkflowRegistry = class {
|
|
|
2264
2289
|
register(extension2) {
|
|
2265
2290
|
if (this.#frozen) fail("REGISTRY_FROZEN", "Workflow extension registration is closed after session_start");
|
|
2266
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");
|
|
2267
|
-
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");
|
|
2268
2293
|
const functions = extension2.functions ?? {};
|
|
2269
2294
|
const modelAliases = extension2.modelAliases ?? {};
|
|
2295
|
+
const agentPreparationHooks = extension2.agentPreparationHooks ?? {};
|
|
2270
2296
|
const agentSetupHooks = extension2.agentSetupHooks ?? {};
|
|
2271
2297
|
const agentAttemptActions = extension2.agentAttemptActions ?? {};
|
|
2272
2298
|
const source = extension2.source;
|
|
@@ -2276,10 +2302,7 @@ var WorkflowRegistry = class {
|
|
|
2276
2302
|
if (validateSettings !== void 0 && typeof validateSettings !== "function") fail("INVALID_METADATA", "Workflow extension validateSettings must be a function");
|
|
2277
2303
|
if (source !== void 0 && (typeof source !== "string" || !source.trim())) fail("INVALID_METADATA", "Workflow extension source must be a non-empty module URL");
|
|
2278
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");
|
|
2279
|
-
|
|
2280
|
-
if (!Array.isArray(roleDirectoryValues)) fail("INVALID_METADATA", "Workflow extension roleDirectories must be an array");
|
|
2281
|
-
const roleDirectories = [...new Set(Array.from(roleDirectoryValues, (value) => normalizeRoleDirectory(value)))];
|
|
2282
|
-
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");
|
|
2283
2306
|
const names = Object.keys(functions);
|
|
2284
2307
|
if (new Set(names).size !== names.length) fail("GLOBAL_COLLISION", "Global name collision inside extension");
|
|
2285
2308
|
for (const name of names) {
|
|
@@ -2298,6 +2321,11 @@ var WorkflowRegistry = class {
|
|
|
2298
2321
|
if (!object(alias) || Object.keys(alias).some((key) => key !== "resolve") || typeof alias.resolve !== "function") fail("INVALID_METADATA", `Invalid model alias resolver: ${name}`);
|
|
2299
2322
|
if (this.#modelAliases.has(name)) fail("DUPLICATE_NAME", `Model alias already registered: ${name}`);
|
|
2300
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
|
+
}
|
|
2301
2329
|
for (const [name, hook] of Object.entries(agentSetupHooks)) {
|
|
2302
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}`);
|
|
2303
2331
|
if (this.#hooks.has(name)) fail("DUPLICATE_NAME", `Agent setup hook already registered: ${name}`);
|
|
@@ -2307,16 +2335,20 @@ var WorkflowRegistry = class {
|
|
|
2307
2335
|
if (action.visibleStandalone !== void 0 !== (action.runStandalone !== void 0)) fail("INVALID_METADATA", `Standalone agent attempt actions require visibleStandalone and runStandalone: ${name}`);
|
|
2308
2336
|
if (this.#agentAttemptActions.has(name)) fail("DUPLICATE_NAME", `Agent attempt action already registered: ${name}`);
|
|
2309
2337
|
}
|
|
2310
|
-
const stored = deepFreeze({ ...extension2, functions, modelAliases, agentSetupHooks, agentAttemptActions
|
|
2338
|
+
const stored = deepFreeze({ ...extension2, functions, modelAliases, agentPreparationHooks, agentSetupHooks, agentAttemptActions });
|
|
2311
2339
|
if (source !== void 0) for (const name of names) this.#functionSources.set(name, Object.freeze({ module: source, export: "default", dependencies: Object.freeze([...dependencies]) }));
|
|
2312
2340
|
this.#extensions.add(stored);
|
|
2313
|
-
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 } : {} }));
|
|
2314
2341
|
for (const name of names) this.#globals.set(name, name);
|
|
2315
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 } });
|
|
2316
2344
|
for (const [name, hook] of Object.entries(agentSetupHooks)) this.#hooks.set(name, { name, priority: hook.priority ?? 10, setup: hook.setup });
|
|
2317
2345
|
for (const [name, action] of Object.entries(agentAttemptActions)) this.#agentAttemptActions.set(name, action);
|
|
2318
2346
|
if (validateSettings !== void 0) this.#settingsValidators.add({ headline: extension2.headline, validate: validateSettings });
|
|
2319
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
|
+
}
|
|
2320
2352
|
function(name) {
|
|
2321
2353
|
if (!IDENTIFIER.test(name)) fail("MISSING_WORKFLOW", `Registered functions require an unqualified name: ${name}`);
|
|
2322
2354
|
const fn = [...this.#extensions].find((extension2) => extension2.functions?.[name])?.functions?.[name];
|
|
@@ -2339,7 +2371,7 @@ var WorkflowRegistry = class {
|
|
|
2339
2371
|
let source = "global settings";
|
|
2340
2372
|
try {
|
|
2341
2373
|
const resolved = context ? resolveWorkflowSettings(context.cwd, context.projectTrusted, context.globalSettingsPath) : void 0;
|
|
2342
|
-
aliases = resolved?.effective.modelAliases ?? loadSettings().modelAliases;
|
|
2374
|
+
aliases = resolved?.effective.modelAliases ?? (context ? loadSettings().modelAliases : resolveWorkflowSettings(process.cwd(), false).effective.modelAliases);
|
|
2343
2375
|
if (resolved) {
|
|
2344
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";
|
|
2345
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 };
|
|
@@ -2398,7 +2430,7 @@ var WorkflowRegistry = class {
|
|
|
2398
2430
|
try {
|
|
2399
2431
|
validate(frozenSettings, Object.freeze({ ...context }));
|
|
2400
2432
|
} catch (error) {
|
|
2401
|
-
const location = context.source
|
|
2433
|
+
const location = context.source;
|
|
2402
2434
|
fail("INVALID_SETTINGS", `Invalid extension settings (${headline}; ${location}): ${errorText(error)}`);
|
|
2403
2435
|
}
|
|
2404
2436
|
}
|
|
@@ -2417,18 +2449,15 @@ var WorkflowRegistry = class {
|
|
|
2417
2449
|
journal.put(path, stored);
|
|
2418
2450
|
return structuredClone(stored);
|
|
2419
2451
|
}
|
|
2452
|
+
agentPreparationHooks() {
|
|
2453
|
+
return [...this.#preparationHooks.values()].sort(byPriorityThenName);
|
|
2454
|
+
}
|
|
2420
2455
|
agentSetupHooks() {
|
|
2421
2456
|
return [...this.#hooks.values()].sort(byPriorityThenName);
|
|
2422
2457
|
}
|
|
2423
2458
|
agentAttemptActions() {
|
|
2424
2459
|
return Object.freeze(Object.fromEntries(this.#agentAttemptActions.entries()));
|
|
2425
2460
|
}
|
|
2426
|
-
roleDirectories() {
|
|
2427
|
-
return [...this.#roleDirectories.keys()];
|
|
2428
|
-
}
|
|
2429
|
-
roleDirectoryRegistrations() {
|
|
2430
|
-
return [...this.#roleDirectories.values()];
|
|
2431
|
-
}
|
|
2432
2461
|
modelAliases() {
|
|
2433
2462
|
return [...this.#modelAliases.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
2434
2463
|
}
|
|
@@ -2484,10 +2513,10 @@ function createWorkflowRegistryApi(registry) {
|
|
|
2484
2513
|
},
|
|
2485
2514
|
modelAliases: () => registry.modelAliases(),
|
|
2486
2515
|
resolveModelAliases: (...args) => registry.resolveModelAliases(...args),
|
|
2487
|
-
|
|
2488
|
-
roleDirectoryRegistrations: () => registry.roleDirectoryRegistrations(),
|
|
2516
|
+
agentPreparationHooks: () => registry.agentPreparationHooks(),
|
|
2489
2517
|
agentSetupHooks: () => registry.agentSetupHooks(),
|
|
2490
|
-
agentAttemptActions: () => registry.agentAttemptActions()
|
|
2518
|
+
agentAttemptActions: () => registry.agentAttemptActions(),
|
|
2519
|
+
registrations: () => registry.registrations()
|
|
2491
2520
|
};
|
|
2492
2521
|
}
|
|
2493
2522
|
function workflowRegistryHost() {
|
|
@@ -2504,310 +2533,11 @@ function loadingRegistry() {
|
|
|
2504
2533
|
return workflowRegistryHost().api;
|
|
2505
2534
|
}
|
|
2506
2535
|
beginWorkflowExtensionLoading();
|
|
2507
|
-
function registeredWorkflowRoleDirectoryRegistrations() {
|
|
2508
|
-
const registrations = loadingRegistry().roleDirectoryRegistrations;
|
|
2509
|
-
return typeof registrations === "function" ? registrations() : [];
|
|
2510
|
-
}
|
|
2511
|
-
|
|
2512
|
-
// src/roles.ts
|
|
2513
|
-
var ROLE_DIRECTORY2 = "pi-extensible-workflows";
|
|
2514
|
-
var roleDefinitionPaths = /* @__PURE__ */ new WeakMap();
|
|
2515
|
-
function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
2516
|
-
if (!strict) {
|
|
2517
|
-
if (!content.startsWith("---\n")) return { prompt: content };
|
|
2518
|
-
const end = content.indexOf("\n---", 4);
|
|
2519
|
-
if (end < 0) return { prompt: content };
|
|
2520
|
-
const meta = {};
|
|
2521
|
-
for (const line of content.slice(4, end).split("\n")) {
|
|
2522
|
-
const match = /^(model|tools|skills|extensions|description|overrideSystemPrompt|override_system_prompt|is_system_prompt|contextFiles|extensionSettings|disabledAgentResources|thinking)\s*:\s*(.+)$/.exec(line.trim());
|
|
2523
|
-
if (match?.[1] === "disabledAgentResources") fail("INVALID_METADATA", "disabledAgentResources is no longer supported; use skills, extensions, and tools selectors");
|
|
2524
|
-
if (match?.[1] === "thinking") fail("INVALID_METADATA", "Role thinking is not supported; put it on model as provider/model:thinking");
|
|
2525
|
-
if (match?.[1] && match[2]) meta[match[1]] = match[2].trim();
|
|
2526
|
-
}
|
|
2527
|
-
const unquote = (v) => v.replace(/^['"]|['"]$/g, "");
|
|
2528
|
-
const parseList = (value) => value === void 0 ? void 0 : value.replace(/^\[|\]$/g, "").split(",").map((entry) => unquote(entry.trim())).filter(Boolean);
|
|
2529
|
-
const tools2 = parseList(meta.tools);
|
|
2530
|
-
const skills2 = parseList(meta.skills);
|
|
2531
|
-
const extensions2 = parseList(meta.extensions);
|
|
2532
|
-
let extensionSettings2;
|
|
2533
|
-
if (meta.extensionSettings !== void 0) {
|
|
2534
|
-
try {
|
|
2535
|
-
extensionSettings2 = JSON.parse(meta.extensionSettings);
|
|
2536
|
-
} catch (error) {
|
|
2537
|
-
fail("INVALID_METADATA", `Invalid role extensionSettings: ${errorText(error)}`);
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
const rolePathValue2 = rolePath ?? "<role>";
|
|
2541
|
-
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA");
|
|
2542
|
-
const definition2 = { prompt: content.slice(end + 4).replace(/^\n/, "") };
|
|
2543
|
-
if (meta.model) {
|
|
2544
|
-
const model2 = unquote(meta.model);
|
|
2545
|
-
assertModelThinking(model2, "Role model");
|
|
2546
|
-
definition2.model = model2;
|
|
2547
|
-
}
|
|
2548
|
-
if (meta.description) definition2.description = unquote(meta.description);
|
|
2549
|
-
if (tools2) definition2.tools = tools2;
|
|
2550
|
-
if (skills2) definition2.skills = skills2;
|
|
2551
|
-
if (extensions2) definition2.extensions = extensions2;
|
|
2552
|
-
if (normalizedExtensionSettings2 !== void 0) definition2.extensionSettings = normalizedExtensionSettings2;
|
|
2553
|
-
const overrideSystemPrompt2 = meta.overrideSystemPrompt ?? meta.override_system_prompt ?? meta.is_system_prompt;
|
|
2554
|
-
const contextFiles2 = meta.contextFiles ? meta.contextFiles.replace(/^\[|\]$/g, "").split(",").map((scope) => unquote(scope.trim())).filter(Boolean) : void 0;
|
|
2555
|
-
const normalizedContextFiles2 = validateContextFileScopes(contextFiles2, "role");
|
|
2556
|
-
if (overrideSystemPrompt2) definition2.overrideSystemPrompt = overrideSystemPrompt2 === "true";
|
|
2557
|
-
if (normalizedContextFiles2) definition2.contextFiles = normalizedContextFiles2;
|
|
2558
|
-
return definition2;
|
|
2559
|
-
}
|
|
2560
|
-
const normalized = content.replace(/\r\n?/g, "\n");
|
|
2561
|
-
if (normalized.startsWith("---\n") && normalized.indexOf("\n---", 3) < 0) fail("INVALID_METADATA", "Role frontmatter is missing its closing delimiter");
|
|
2562
|
-
let parsed;
|
|
2563
|
-
try {
|
|
2564
|
-
parsed = parseFrontmatter(content);
|
|
2565
|
-
} catch (error) {
|
|
2566
|
-
fail("INVALID_METADATA", `Invalid role frontmatter: ${errorText(error)}`);
|
|
2567
|
-
}
|
|
2568
|
-
if (!object(parsed.frontmatter)) fail("INVALID_METADATA", "Role frontmatter must be an object");
|
|
2569
|
-
const { model, tools, skills, extensions, extensionSettings, description, contextFiles } = parsed.frontmatter;
|
|
2570
|
-
if (Object.prototype.hasOwnProperty.call(parsed.frontmatter, "disabledAgentResources")) fail("INVALID_METADATA", "disabledAgentResources is no longer supported; use skills, extensions, and tools selectors");
|
|
2571
|
-
if (Object.prototype.hasOwnProperty.call(parsed.frontmatter, "thinking")) fail("INVALID_METADATA", "Role thinking is not supported; put it on model as provider/model:thinking");
|
|
2572
|
-
const overrideSystemPrompt = parsed.frontmatter.overrideSystemPrompt ?? parsed.frontmatter.override_system_prompt ?? parsed.frontmatter.is_system_prompt;
|
|
2573
|
-
if (model !== void 0 && (typeof model !== "string" || model.trim() === "")) fail("INVALID_METADATA", "Role model must be a non-empty string");
|
|
2574
|
-
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");
|
|
2575
|
-
if (overrideSystemPrompt !== void 0 && typeof overrideSystemPrompt !== "boolean") fail("INVALID_METADATA", "Role overrideSystemPrompt must be a boolean");
|
|
2576
|
-
const normalizedContextFiles = validateContextFileScopes(contextFiles, rolePath ?? "<role>");
|
|
2577
|
-
const rolePathValue = rolePath ?? "<role>";
|
|
2578
|
-
const normalizedTools = validateSelectorList(tools, rolePathValue, "tools", "INVALID_METADATA");
|
|
2579
|
-
const normalizedSkills = validateSelectorList(skills, rolePathValue, "skills", "INVALID_METADATA");
|
|
2580
|
-
const normalizedExtensions = validateSelectorList(extensions, rolePathValue, "extensions", "INVALID_METADATA");
|
|
2581
|
-
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA");
|
|
2582
|
-
const normalizedDescription = typeof description === "string" ? description.trim() : void 0;
|
|
2583
|
-
const normalizedModel = typeof model === "string" ? model.trim() : void 0;
|
|
2584
|
-
if (normalizedModel !== void 0) assertModelThinking(normalizedModel, "Role model");
|
|
2585
|
-
const definition = { prompt: parsed.body };
|
|
2586
|
-
if (normalizedDescription !== void 0) definition.description = normalizedDescription;
|
|
2587
|
-
if (normalizedModel !== void 0) definition.model = normalizedModel;
|
|
2588
|
-
if (normalizedTools !== void 0) definition.tools = normalizedTools;
|
|
2589
|
-
if (normalizedSkills !== void 0) definition.skills = normalizedSkills;
|
|
2590
|
-
if (normalizedExtensions !== void 0) definition.extensions = normalizedExtensions;
|
|
2591
|
-
if (normalizedExtensionSettings !== void 0) definition.extensionSettings = normalizedExtensionSettings;
|
|
2592
|
-
if (typeof overrideSystemPrompt === "boolean") definition.overrideSystemPrompt = overrideSystemPrompt;
|
|
2593
|
-
if (normalizedContextFiles !== void 0) definition.contextFiles = normalizedContextFiles;
|
|
2594
|
-
return definition;
|
|
2595
|
-
}
|
|
2596
|
-
function workflowRoleDirectories(agentDir = getAgentDir2()) {
|
|
2597
|
-
return [join5(agentDir, ROLE_DIRECTORY2, "roles")];
|
|
2598
|
-
}
|
|
2599
|
-
function projectRoleDirectories(root) {
|
|
2600
|
-
return [join5(root, ROLE_DIRECTORY2, "roles")];
|
|
2601
|
-
}
|
|
2602
|
-
function roleDirectorySources(dirs) {
|
|
2603
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2604
|
-
return dirs.flatMap((value) => {
|
|
2605
|
-
const source = typeof value === "string" ? { path: value } : { path: value.path, extension: value.extension, ...value.builtin === true ? { builtin: true } : {} };
|
|
2606
|
-
if (seen.has(source.path)) return [];
|
|
2607
|
-
seen.add(source.path);
|
|
2608
|
-
return [source];
|
|
2609
|
-
});
|
|
2610
|
-
}
|
|
2611
|
-
function roleDirectoryLabel(source, extension2 = true) {
|
|
2612
|
-
return source.extension ? `Extension "${source.extension.headline}" (${source.extension.version})` : extension2 ? "Registered workflow extension" : "Standard workflow";
|
|
2613
|
-
}
|
|
2614
|
-
function isRoleFile(dir, entry) {
|
|
2615
|
-
if (extname(entry.name) !== ".md") return false;
|
|
2616
|
-
if (entry.isFile()) return true;
|
|
2617
|
-
if (!entry.isSymbolicLink()) return false;
|
|
2618
|
-
try {
|
|
2619
|
-
return statSync(join5(dir, entry.name)).isFile();
|
|
2620
|
-
} catch (error) {
|
|
2621
|
-
if (isNodeError(error, "ENOENT")) return false;
|
|
2622
|
-
throw error;
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
function scanRoleFiles(dirs, extension2) {
|
|
2626
|
-
const files = [];
|
|
2627
|
-
for (const source of roleDirectorySources(dirs)) {
|
|
2628
|
-
let entries;
|
|
2629
|
-
try {
|
|
2630
|
-
entries = readdirSync(source.path, { withFileTypes: true });
|
|
2631
|
-
} catch (error) {
|
|
2632
|
-
if (!extension2 && isNodeError(error, "ENOENT")) continue;
|
|
2633
|
-
fail("INVALID_METADATA", `${roleDirectoryLabel(source, extension2)} role directory "${source.path}" could not be scanned: ${errorText(error)}`);
|
|
2634
|
-
}
|
|
2635
|
-
for (const entry of entries) if (isRoleFile(source.path, entry)) files.push({ name: basename3(entry.name, ".md"), path: join5(source.path, entry.name), source });
|
|
2636
|
-
}
|
|
2637
|
-
return files.sort((left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path));
|
|
2638
|
-
}
|
|
2639
|
-
function roleEntries(dirs, extension2) {
|
|
2640
|
-
const files = scanRoleFiles(dirs, extension2);
|
|
2641
|
-
const starterFiles = files.filter(({ source }) => source.builtin === true);
|
|
2642
|
-
const regularFiles = files.filter(({ source }) => source.builtin !== true);
|
|
2643
|
-
if (extension2) {
|
|
2644
|
-
const byName = /* @__PURE__ */ new Map();
|
|
2645
|
-
for (const file of regularFiles) byName.set(file.name, [...byName.get(file.name) ?? [], file]);
|
|
2646
|
-
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("; ")}`);
|
|
2647
|
-
}
|
|
2648
|
-
const read = (roleFiles) => Object.fromEntries(roleFiles.map(({ name, path, source }) => {
|
|
2649
|
-
try {
|
|
2650
|
-
const definition = parseRoleMarkdown(readFileSync3(path, "utf8"), true, path);
|
|
2651
|
-
roleDefinitionPaths.set(definition, path);
|
|
2652
|
-
return [name, { name, path, source, definition }];
|
|
2653
|
-
} catch (error) {
|
|
2654
|
-
if (extension2) fail("INVALID_METADATA", `${roleDirectoryLabel(source)} role directory "${source.path}" contains invalid role "${name}" at "${path}": ${errorText(error)}`);
|
|
2655
|
-
throw error;
|
|
2656
|
-
}
|
|
2657
|
-
}));
|
|
2658
|
-
return extension2 ? { ...read(starterFiles), ...read(regularFiles) } : read(files);
|
|
2659
|
-
}
|
|
2660
|
-
function discoveryInput(input) {
|
|
2661
|
-
return { cwd: input.cwd, agentDir: input.agentDir ?? getAgentDir2(), projectTrusted: input.projectTrusted ?? true, extensionRoleDirectories: input.extensionRoleDirectories ?? registeredWorkflowRoleDirectoryRegistrations() };
|
|
2662
|
-
}
|
|
2663
|
-
function discoveredRoleEntries(input) {
|
|
2664
|
-
const resolved = discoveryInput(input);
|
|
2665
|
-
return {
|
|
2666
|
-
...roleEntries(resolved.extensionRoleDirectories, true),
|
|
2667
|
-
...roleEntries(workflowRoleDirectories(resolved.agentDir), false),
|
|
2668
|
-
...resolved.projectTrusted ? roleEntries(projectRoleDirectories(join5(resolved.cwd, ".pi")), false) : {}
|
|
2669
|
-
};
|
|
2670
|
-
}
|
|
2671
|
-
function discoverRoles(input) {
|
|
2672
|
-
return deepFreeze(Object.fromEntries(Object.entries(discoveredRoleEntries(input)).map(([name, entry]) => [name, entry.definition])));
|
|
2673
|
-
}
|
|
2674
|
-
function loadAgentDefinitions(cwd, agentDir = getAgentDir2(), projectTrusted = true, extensionRoleDirectories = registeredWorkflowRoleDirectoryRegistrations()) {
|
|
2675
|
-
return discoverRoles({ cwd, agentDir, projectTrusted, extensionRoleDirectories });
|
|
2676
|
-
}
|
|
2677
|
-
function canonicalExtensionSelector(selector, base = process.cwd()) {
|
|
2678
|
-
const negated = selector.startsWith("!");
|
|
2679
|
-
const body = negated ? selector.slice(1) : selector;
|
|
2680
|
-
if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:")) return selector;
|
|
2681
|
-
const resolved = resolve4(base, body);
|
|
2682
|
-
if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
|
|
2683
|
-
return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
|
|
2684
|
-
}
|
|
2685
|
-
function selectorsFromSettings2(settings) {
|
|
2686
|
-
return { ...settings.skills === void 0 ? {} : { skills: settings.skills }, ...settings.extensions === void 0 ? {} : { extensions: settings.extensions }, ...settings.tools === void 0 ? {} : { tools: settings.tools } };
|
|
2687
|
-
}
|
|
2688
|
-
function rootToolNames(value) {
|
|
2689
|
-
return value === void 0 ? void 0 : value instanceof Set ? [...value] : [...value];
|
|
2690
|
-
}
|
|
2691
|
-
function roleDefinitionFor(name, options) {
|
|
2692
|
-
if (name === void 0) return {};
|
|
2693
|
-
if (options.definition !== void 0) {
|
|
2694
|
-
const path = roleDefinitionPaths.get(options.definition);
|
|
2695
|
-
return { definition: options.definition, ...path === void 0 ? {} : { path } };
|
|
2696
|
-
}
|
|
2697
|
-
if (options.definitions?.[name] !== void 0) {
|
|
2698
|
-
const definition = options.definitions[name];
|
|
2699
|
-
const path = roleDefinitionPaths.get(definition);
|
|
2700
|
-
return { definition, ...path === void 0 ? {} : { path } };
|
|
2701
|
-
}
|
|
2702
|
-
const entries = discoveredRoleEntries(options);
|
|
2703
|
-
const entry = entries[name];
|
|
2704
|
-
if (!entry) fail("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${name}`);
|
|
2705
|
-
return entry;
|
|
2706
|
-
}
|
|
2707
|
-
function resolveRole(name, options) {
|
|
2708
|
-
const { definition, path } = roleDefinitionFor(name, options);
|
|
2709
|
-
const settings = options.selectorSources === void 0 ? resolveWorkflowSettings(options.cwd, options.projectTrusted ?? true, options.settingsPath ?? workflowSettingsPath(options.agentDir ?? getAgentDir2())) : void 0;
|
|
2710
|
-
const baseSources = options.selectorSources ?? { global: selectorsFromSettings2(settings?.global ?? {}), project: selectorsFromSettings2(settings?.project ?? {}) };
|
|
2711
|
-
const canonicalSources = (sources) => ({
|
|
2712
|
-
...sources,
|
|
2713
|
-
global: { ...sources.global, ...sources.global.extensions === void 0 ? {} : { extensions: sources.global.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
2714
|
-
project: { ...sources.project, ...sources.project.extensions === void 0 ? {} : { extensions: sources.project.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
2715
|
-
...sources.role === void 0 ? {} : { role: { ...sources.role, ...sources.role.extensions === void 0 ? {} : { extensions: sources.role.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } },
|
|
2716
|
-
...sources.call === void 0 ? {} : { call: { ...sources.call, ...sources.call.extensions === void 0 ? {} : { extensions: sources.call.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } }
|
|
2717
|
-
});
|
|
2718
|
-
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;
|
|
2719
|
-
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;
|
|
2720
|
-
const selectorSources = canonicalSources({ global: baseSources.global, project: baseSources.project, ...roleLayer === void 0 ? {} : { role: roleLayer }, ...callLayer === void 0 ? {} : { call: callLayer } });
|
|
2721
|
-
const callSelectors = selectorSources.call;
|
|
2722
|
-
const roleSelectors = selectorSources.role;
|
|
2723
|
-
const selectorLayers = {
|
|
2724
|
-
skills: [selectorSources.global.skills, selectorSources.project.skills, roleSelectors?.skills, callSelectors?.skills],
|
|
2725
|
-
extensions: [selectorSources.global.extensions, selectorSources.project.extensions, roleSelectors?.extensions, callSelectors?.extensions],
|
|
2726
|
-
tools: [selectorSources.global.tools, selectorSources.project.tools, roleSelectors?.tools, callSelectors?.tools]
|
|
2727
|
-
};
|
|
2728
|
-
const resources = options.resources ?? {};
|
|
2729
|
-
const candidateTools = options.inheritedTools ?? rootToolNames(options.rootTools) ?? resources.tools;
|
|
2730
|
-
const tools = options.effectiveTools ?? (candidateTools === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.tools, candidateTools));
|
|
2731
|
-
const rootTools = rootToolNames(options.rootTools);
|
|
2732
|
-
if (rootTools !== void 0) {
|
|
2733
|
-
const outsideEffectiveTool = options.effectiveTools?.find((tool) => !candidateTools?.includes(tool));
|
|
2734
|
-
if (outsideEffectiveTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideEffectiveTool}`);
|
|
2735
|
-
const outsideCallTool = (options.tools ?? []).find((tool) => !tool.startsWith("!") && !resourcePatternHasMagic(tool) && !candidateTools?.includes(tool));
|
|
2736
|
-
if (outsideCallTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideCallTool}`);
|
|
2737
|
-
const outsideRootTool = tools?.find((tool) => !rootTools.includes(tool));
|
|
2738
|
-
if (outsideRootTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideRootTool}`);
|
|
2739
|
-
}
|
|
2740
|
-
const selectedSkills = resources.skills === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.skills, resources.skills);
|
|
2741
|
-
const extensionResources = resources.extensions?.map((extension2) => extensionIdentity(extension2));
|
|
2742
|
-
const selectedExtensions = extensionResources === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.extensions, extensionResources);
|
|
2743
|
-
const unmatchedSkills = resources.skills === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.skills.flatMap((layer) => layer ?? []), resources.skills);
|
|
2744
|
-
const unmatchedExtensions = extensionResources === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.extensions.flatMap((layer) => layer ?? []), extensionResources);
|
|
2745
|
-
const unmatchedTools = candidateTools === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.tools.flatMap((layer) => layer ?? []), candidateTools);
|
|
2746
|
-
const aliases = options.modelAliases ?? settings?.effective.modelAliases ?? {};
|
|
2747
|
-
const requestedModel = options.model ?? (definition?.model && definition.thinking && !definition.model.includes(":") ? `${definition.model}:${definition.thinking}` : definition?.model);
|
|
2748
|
-
const blockedAlias = requestedModel?.split(":", 1)[0];
|
|
2749
|
-
if (requestedModel !== void 0 && blockedAlias && options.blockedAliases?.has(blockedAlias) && !modelAliasName(requestedModel, aliases)) {
|
|
2750
|
-
const target = options.blockedAliasTargets?.[blockedAlias];
|
|
2751
|
-
fail("UNKNOWN_MODEL", `Unknown model alias ${requestedModel}${target ? ` resolved to ${target}` : ""}${options.settingsPath ? ` (settings: ${options.settingsPath})` : ""}`);
|
|
2752
|
-
}
|
|
2753
|
-
let model = options.modelOverride;
|
|
2754
|
-
if (model === void 0 && requestedModel !== void 0) {
|
|
2755
|
-
assertModelThinking(requestedModel);
|
|
2756
|
-
model = resolveModelReference(requestedModel, aliases, options.knownModels ?? options.availableModels, options.settingsPath ?? settings?.sources.modelAliases);
|
|
2757
|
-
}
|
|
2758
|
-
if (model === void 0) model = options.rootModel;
|
|
2759
|
-
const availableModels = options.knownModels ?? options.availableModels ?? (options.rootModel === void 0 ? void 0 : /* @__PURE__ */ new Set([modelCapability(options.rootModel)]));
|
|
2760
|
-
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})` : ""}`);
|
|
2761
|
-
const prompt = definition?.prompt ?? "";
|
|
2762
|
-
const overrideSystemPrompt = definition?.overrideSystemPrompt === true;
|
|
2763
|
-
const contextFiles = options.contextFiles ?? definition?.contextFiles;
|
|
2764
|
-
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 } };
|
|
2765
|
-
}
|
|
2766
|
-
|
|
2767
|
-
// src/validation.ts
|
|
2768
|
-
var AGENT_OPTION_KEYS = /* @__PURE__ */ new Set(["label", "model", "tools", "skills", "extensions", "contextFiles", "role", "outputSchema", "retries", "timeoutMs"]);
|
|
2769
|
-
function validateAgentOption(key, value, aliases, knownModels, settingsPath) {
|
|
2770
|
-
switch (key) {
|
|
2771
|
-
case "label":
|
|
2772
|
-
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent label must be a non-empty string");
|
|
2773
|
-
break;
|
|
2774
|
-
case "model":
|
|
2775
|
-
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent model must be a non-empty string");
|
|
2776
|
-
assertModelThinking(value, "agent model");
|
|
2777
|
-
if (aliases !== void 0) resolveModelReference(value, aliases, knownModels, settingsPath);
|
|
2778
|
-
break;
|
|
2779
|
-
case "tools":
|
|
2780
|
-
case "skills":
|
|
2781
|
-
case "extensions":
|
|
2782
|
-
validateSelectorList(value, "agent options", key, "INVALID_METADATA", key !== "extensions");
|
|
2783
|
-
break;
|
|
2784
|
-
case "contextFiles":
|
|
2785
|
-
validateContextFileScopes(value, "agent options");
|
|
2786
|
-
break;
|
|
2787
|
-
case "role":
|
|
2788
|
-
if (typeof value !== "string" || !value.trim()) fail("INVALID_METADATA", "agent role must be a non-empty string");
|
|
2789
|
-
break;
|
|
2790
|
-
case "outputSchema":
|
|
2791
|
-
validateSchema(value, "agent outputSchema");
|
|
2792
|
-
break;
|
|
2793
|
-
case "retries":
|
|
2794
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) fail("INVALID_METADATA", "agent retries must be a non-negative integer");
|
|
2795
|
-
break;
|
|
2796
|
-
case "timeoutMs":
|
|
2797
|
-
if (value !== null && !positiveInteger(value)) fail("INVALID_METADATA", "agent timeoutMs must be null or a positive integer");
|
|
2798
|
-
break;
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
function validateAgentOptions(value) {
|
|
2802
|
-
if (!object(value) || !jsonValue(value)) fail("INVALID_METADATA", "agent options must be a JSON object");
|
|
2803
|
-
if (Object.prototype.hasOwnProperty.call(value, "thinking")) fail("INVALID_METADATA", "agent thinking is not supported; use model provider/model:thinking");
|
|
2804
|
-
for (const [key, option] of Object.entries(value)) if (AGENT_OPTION_KEYS.has(key)) validateAgentOption(key, option);
|
|
2805
|
-
return value;
|
|
2806
|
-
}
|
|
2807
2536
|
|
|
2808
2537
|
// src/execution.ts
|
|
2809
2538
|
var RPC_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
2810
2539
|
var HEARTBEAT_INTERVAL_MS = 1e3;
|
|
2540
|
+
var FUNCTION_HANDLE_PREFIX = "function-handle";
|
|
2811
2541
|
var WORK_RESULT_BRAND = "__workResult";
|
|
2812
2542
|
var childSource = String.raw`
|
|
2813
2543
|
"use strict";
|
|
@@ -2918,15 +2648,13 @@ const internalAgent = (...values) => {
|
|
|
2918
2648
|
void result.then(() => agentInflight.delete(occurrenceKey), () => agentInflight.delete(occurrenceKey));
|
|
2919
2649
|
return guardedAgentResult(result);
|
|
2920
2650
|
};
|
|
2921
|
-
const AGENT_HANDLE_KEYS = ["name", "role", "model", "tools", "skills", "extensions", "contextFiles", "label"];
|
|
2922
2651
|
const agentHandleNames = new Set();
|
|
2923
2652
|
const agentHandleInflight = new Set();
|
|
2924
2653
|
const internalAgentCreate = (...values) => {
|
|
2925
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");
|
|
2926
2655
|
const options = values[0];
|
|
2927
2656
|
const name = named(options.name, "agent.create");
|
|
2928
|
-
|
|
2929
|
-
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}/");
|
|
2930
2658
|
if (agentHandleNames.has(name)) throw workError("INVALID_METADATA", "Duplicate agent handle name: " + name);
|
|
2931
2659
|
agentHandleNames.add(name);
|
|
2932
2660
|
const config = Object.freeze(Object.fromEntries(Object.entries(options).filter(([key]) => key !== "name")));
|
|
@@ -3135,20 +2863,28 @@ Promise.resolve().then(() => new vm.Script("(async(__pi_extensible_workflows_age
|
|
|
3135
2863
|
.catch(error => send({ type: "error", error: workerError(error) }))
|
|
3136
2864
|
.finally(() => clearInterval(heartbeat));
|
|
3137
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
|
+
}
|
|
3138
2873
|
|
|
3139
2874
|
// src/host.ts
|
|
3140
2875
|
import { Type as Type3 } from "@earendil-works/pi-ai";
|
|
3141
|
-
import { copyToClipboard, getAgentDir as
|
|
2876
|
+
import { copyToClipboard, getAgentDir as getAgentDir4, ModelSelectorComponent } from "@earendil-works/pi-coding-agent";
|
|
3142
2877
|
|
|
3143
2878
|
// src/agent-execution.ts
|
|
3144
|
-
import { existsSync,
|
|
2879
|
+
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
3145
2880
|
import { copyFile } from "node:fs/promises";
|
|
3146
2881
|
import { homedir as homedir4 } from "node:os";
|
|
3147
|
-
import { basename as
|
|
2882
|
+
import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve4 } from "node:path";
|
|
3148
2883
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
3149
2884
|
import { Type } from "@earendil-works/pi-ai";
|
|
2885
|
+
import { Value as Value2 } from "typebox/value";
|
|
3150
2886
|
import { Compile } from "typebox/compile";
|
|
3151
|
-
import { createAgentSession, createCodemodeExtension, createMcpExtension, createToolSearchExtension, 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";
|
|
3152
2888
|
|
|
3153
2889
|
// src/pi-runtime-adapter.ts
|
|
3154
2890
|
var TURN_START_EVENTS = /* @__PURE__ */ new Set(["turn_start", "turn_started", "turnStarted"]);
|
|
@@ -3423,8 +3159,8 @@ function createLiveSessionHandoff() {
|
|
|
3423
3159
|
let request;
|
|
3424
3160
|
const waitForBoundary = () => {
|
|
3425
3161
|
if (!turnActive) return Promise.resolve();
|
|
3426
|
-
boundary ??= new Promise((
|
|
3427
|
-
resolveBoundary =
|
|
3162
|
+
boundary ??= new Promise((resolve5) => {
|
|
3163
|
+
resolveBoundary = resolve5;
|
|
3428
3164
|
});
|
|
3429
3165
|
return boundary;
|
|
3430
3166
|
};
|
|
@@ -3456,11 +3192,11 @@ function createLiveSessionHandoff() {
|
|
|
3456
3192
|
if (request) return request;
|
|
3457
3193
|
if (isReleased()) return;
|
|
3458
3194
|
state = "handoff-pending";
|
|
3459
|
-
takeover = new Promise((
|
|
3460
|
-
resolveTakeover =
|
|
3195
|
+
takeover = new Promise((resolve5) => {
|
|
3196
|
+
resolveTakeover = resolve5;
|
|
3461
3197
|
});
|
|
3462
|
-
resume = new Promise((
|
|
3463
|
-
resolveResume =
|
|
3198
|
+
resume = new Promise((resolve5) => {
|
|
3199
|
+
resolveResume = resolve5;
|
|
3464
3200
|
});
|
|
3465
3201
|
request = (async () => {
|
|
3466
3202
|
await waitForBoundary();
|
|
@@ -3659,9 +3395,9 @@ var PiRuntimeAgentRunner = class {
|
|
|
3659
3395
|
let structuredResult;
|
|
3660
3396
|
const resultTool = prepared.resultTool;
|
|
3661
3397
|
if (!resultTool) throw new WorkflowError("INTERNAL_ERROR", "Workflow agent session is missing its workflow_result tool");
|
|
3662
|
-
const preparedForSession = preparedForRuntimeRequest(prepared, request.customTools, request.signal, (value) => {
|
|
3398
|
+
const preparedForSession = Object.freeze({ ...preparedForRuntimeRequest(prepared, request.customTools, request.signal, (value) => {
|
|
3663
3399
|
structuredResult = value;
|
|
3664
|
-
}, resultTool);
|
|
3400
|
+
}, resultTool), ...request.turnControl ? { turnControl: request.turnControl } : {} });
|
|
3665
3401
|
const callbacks = this.#options.callbacks;
|
|
3666
3402
|
const handoff = this.#options.handoff;
|
|
3667
3403
|
let adapter;
|
|
@@ -3894,6 +3630,7 @@ var PiRuntimeAgentRunner = class {
|
|
|
3894
3630
|
throwIfProgressFailed();
|
|
3895
3631
|
beginTurn(false);
|
|
3896
3632
|
throwIfTurnPolicyFailed();
|
|
3633
|
+
if (request.turnControl && !session.turnControlDelivered) await activeSession.steer(request.turnControl);
|
|
3897
3634
|
await promptAndRecover(request.task, true);
|
|
3898
3635
|
completeTurn(hasResult() || !hasToolCall(lastAssistant), false, true);
|
|
3899
3636
|
if (turnPolicyFailure) throw turnPolicyFailure;
|
|
@@ -3984,40 +3721,83 @@ function parseModel(value, fallback, aliases = {}, knownModels, settingsPath) {
|
|
|
3984
3721
|
assertModelThinking(value);
|
|
3985
3722
|
return resolveModelReference(value, aliases, knownModels, settingsPath);
|
|
3986
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
|
+
}
|
|
3987
3767
|
function accounting(stats) {
|
|
3988
3768
|
return { input: stats.tokens.input, output: stats.tokens.output, cacheRead: stats.tokens.cacheRead, cacheWrite: stats.tokens.cacheWrite, cost: stats.cost };
|
|
3989
3769
|
}
|
|
3990
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"];
|
|
3991
3771
|
function isWorkflowHostEntry(path) {
|
|
3992
3772
|
const entryPath = canonicalPath(path);
|
|
3993
|
-
let packageRoot2 =
|
|
3994
|
-
while (packageRoot2 !==
|
|
3995
|
-
const packageMetadataPath =
|
|
3773
|
+
let packageRoot2 = dirname3(entryPath);
|
|
3774
|
+
while (packageRoot2 !== dirname3(packageRoot2)) {
|
|
3775
|
+
const packageMetadataPath = join5(packageRoot2, "package.json");
|
|
3996
3776
|
if (existsSync(packageMetadataPath)) {
|
|
3997
3777
|
try {
|
|
3998
|
-
const metadata = JSON.parse(
|
|
3778
|
+
const metadata = JSON.parse(readFileSync2(packageMetadataPath, "utf8"));
|
|
3999
3779
|
if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || !isCorePackageName(metadata.name)) return false;
|
|
4000
|
-
return WORKFLOW_HOST_ENTRY_PATHS.some((entry) => canonicalPath(
|
|
3780
|
+
return WORKFLOW_HOST_ENTRY_PATHS.some((entry) => canonicalPath(join5(packageRoot2, entry)) === entryPath);
|
|
4001
3781
|
} catch {
|
|
4002
3782
|
return false;
|
|
4003
3783
|
}
|
|
4004
3784
|
}
|
|
4005
|
-
packageRoot2 =
|
|
3785
|
+
packageRoot2 = dirname3(packageRoot2);
|
|
4006
3786
|
}
|
|
4007
3787
|
return false;
|
|
4008
3788
|
}
|
|
4009
|
-
var
|
|
3789
|
+
var WORKFLOW_DIRECTORY2 = "pi-extensible-workflows";
|
|
4010
3790
|
function workflowSystemPromptPath(cwd, agentDir, projectTrusted) {
|
|
4011
|
-
const projectPath =
|
|
3791
|
+
const projectPath = join5(cwd, ".pi", WORKFLOW_DIRECTORY2, "SYSTEM.md");
|
|
4012
3792
|
if (projectTrusted && existsSync(projectPath)) return projectPath;
|
|
4013
|
-
const globalPaths = [
|
|
3793
|
+
const globalPaths = [join5(homedir4(), ".pi", WORKFLOW_DIRECTORY2, "SYSTEM.md"), join5(agentDir, WORKFLOW_DIRECTORY2, "SYSTEM.md")];
|
|
4014
3794
|
return globalPaths.find((path) => existsSync(path));
|
|
4015
3795
|
}
|
|
4016
3796
|
function filterContextFiles(base, scopes, cwd, agentDir) {
|
|
4017
3797
|
const globalPath = canonicalPath(agentDir);
|
|
4018
3798
|
const cwdPath = canonicalPath(cwd);
|
|
4019
3799
|
return base.filter(({ path }) => {
|
|
4020
|
-
const resolvedPath = canonicalPath(
|
|
3800
|
+
const resolvedPath = canonicalPath(dirname3(path));
|
|
4021
3801
|
return scopes.some((scope) => scope === "global" ? resolvedPath === globalPath : scope === "cwd" ? resolvedPath === cwdPath : resolvedPath !== globalPath && resolvedPath !== cwdPath);
|
|
4022
3802
|
});
|
|
4023
3803
|
}
|
|
@@ -4043,10 +3823,10 @@ async function loadNativeCoreModule(file, label2) {
|
|
|
4043
3823
|
const packageDirectory = await loadPiPackageDirectory();
|
|
4044
3824
|
let modulePath;
|
|
4045
3825
|
if (packageDirectory.directory) {
|
|
4046
|
-
const candidate =
|
|
3826
|
+
const candidate = resolve4(packageDirectory.directory, "dist/core", file);
|
|
4047
3827
|
if (existsSync(candidate)) modulePath = candidate;
|
|
4048
3828
|
}
|
|
4049
|
-
modulePath ??=
|
|
3829
|
+
modulePath ??= resolve4(dirname3(fileURLToPath3(import.meta.resolve("@earendil-works/pi-coding-agent"))), "core", file);
|
|
4050
3830
|
const packageDirectoryError = packageDirectory.error ? ` Package directory lookup failed: ${packageDirectory.error}` : "";
|
|
4051
3831
|
let loaded = nativeCoreModules.get(file);
|
|
4052
3832
|
if (!loaded) {
|
|
@@ -4137,14 +3917,43 @@ var BUILTIN_EXTENSION_PREFIX = "builtin:";
|
|
|
4137
3917
|
function isLlamaModel(model) {
|
|
4138
3918
|
return model.provider === "llama.cpp";
|
|
4139
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
|
+
}
|
|
4140
3949
|
async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
4141
|
-
const agentDir = input.agentDir ??
|
|
3950
|
+
const agentDir = input.agentDir ?? getAgentDir2();
|
|
4142
3951
|
const systemPromptSource = workflowSystemPromptPath(input.cwd, agentDir, input.resourcePolicy?.projectTrusted ?? true);
|
|
4143
3952
|
const systemPromptOptions = input.systemPrompt !== void 0 ? { systemPromptOverride: () => input.systemPrompt } : systemPromptSource !== void 0 ? { systemPrompt: systemPromptSource } : {};
|
|
4144
3953
|
const contextFilesOverride = input.contextFiles === void 0 ? void 0 : (base) => ({ ...base, agentsFiles: filterContextFiles(base.agentsFiles, input.contextFiles ?? [], input.cwd, agentDir) });
|
|
4145
|
-
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));
|
|
4146
3955
|
if (!input.sessionPath) manager.appendSessionInfo(input.sessionLabel);
|
|
4147
|
-
const modelRuntime = await ModelRuntime.create({ authPath:
|
|
3956
|
+
const modelRuntime = await ModelRuntime.create({ authPath: join5(agentDir, "auth.json"), modelsPath: join5(agentDir, "models.json") });
|
|
4148
3957
|
const customTools = [...input.customTools ?? [], ...input.resultTool ? [input.resultTool] : []];
|
|
4149
3958
|
const tools = [.../* @__PURE__ */ new Set([...input.tools, ...customTools.map(({ name }) => name)])];
|
|
4150
3959
|
let settingsManager;
|
|
@@ -4160,25 +3969,23 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
4160
3969
|
const resolved = await packageManager.resolve();
|
|
4161
3970
|
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => extensionIdentity(path)))];
|
|
4162
3971
|
const selectorSources = policy.selectorSources;
|
|
4163
|
-
const
|
|
4164
|
-
cwd: input.cwd,
|
|
4165
|
-
selectorSources,
|
|
4166
|
-
resources: { extensions: discoveredExtensions }
|
|
4167
|
-
});
|
|
4168
|
-
const selectedExtensions = selection.selectedExtensions ?? [];
|
|
3972
|
+
const selectedExtensions = selectResourcesByLayers(resourceSelectorLayers(selectorSources, "extensions"), discoveredExtensions).filter((path) => policy.capabilities === void 0 || policy.capabilities.extensions.includes(path));
|
|
4169
3973
|
const extensionPaths = selectedExtensions.filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path));
|
|
4170
3974
|
policy.selectedExtensions = selectedExtensions;
|
|
4171
|
-
policy.unmatchedExtensions =
|
|
3975
|
+
policy.unmatchedExtensions = unmatchedResourcePatterns(policy.effective.extensions, discoveredExtensions);
|
|
4172
3976
|
const skillPaths = [...new Set(resolved.skills.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => path))];
|
|
4173
|
-
const updateSkillMatches = (skills) => {
|
|
3977
|
+
const updateSkillMatches = (skills, extensionSkillPaths) => {
|
|
4174
3978
|
const names = [...new Set(skills.map(({ name }) => name))];
|
|
4175
|
-
const
|
|
4176
|
-
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];
|
|
4177
3984
|
policy.selectedSkills = selectedSkills;
|
|
4178
|
-
policy.unmatchedSkills =
|
|
3985
|
+
policy.unmatchedSkills = unmatchedResourcePatterns(policy.effective.skills, names);
|
|
4179
3986
|
return new Set(names.filter((name) => !selectedSkills.includes(name)));
|
|
4180
3987
|
};
|
|
4181
|
-
|
|
3988
|
+
const workflowLoader = new WorkflowResourceLoader(input.cwd, {
|
|
4182
3989
|
cwd: input.cwd,
|
|
4183
3990
|
agentDir,
|
|
4184
3991
|
settingsManager,
|
|
@@ -4189,12 +3996,13 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
4189
3996
|
extensionFactories,
|
|
4190
3997
|
...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {},
|
|
4191
3998
|
skillsOverride: (base) => {
|
|
4192
|
-
const disabledSkills = updateSkillMatches(base.skills);
|
|
3999
|
+
const disabledSkills = updateSkillMatches(base.skills, workflowLoader.extensionSkillPaths);
|
|
4193
4000
|
return { ...base, skills: base.skills.filter(({ name }) => !disabledSkills.has(name)) };
|
|
4194
4001
|
},
|
|
4195
4002
|
...input.systemPromptAppend ? { appendSystemPromptOverride: (base) => [...base, input.systemPromptAppend ?? ""] } : {},
|
|
4196
4003
|
...systemPromptOptions
|
|
4197
4004
|
});
|
|
4005
|
+
resourceLoader = workflowLoader;
|
|
4198
4006
|
await resourceLoader.reload();
|
|
4199
4007
|
} else {
|
|
4200
4008
|
settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: true });
|
|
@@ -4212,7 +4020,7 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
4212
4020
|
throw new WorkflowError("UNKNOWN_MODEL", `Unknown model: ${input.model.provider}/${input.model.model}${because}`);
|
|
4213
4021
|
}
|
|
4214
4022
|
const effectiveSessionStartEvent = input.settings === void 0 && sessionStartEvent === void 0 ? void 0 : { ...sessionStartEvent ?? { type: "session_start", reason: "startup" }, settings: input.settings ?? Object.freeze({}) };
|
|
4215
|
-
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 });
|
|
4216
4024
|
const nativeDispose = session.dispose.bind(session);
|
|
4217
4025
|
let disposal;
|
|
4218
4026
|
const shutdown = (reason, targetSessionFile) => {
|
|
@@ -4575,6 +4383,10 @@ async function createLocalWorkflowAgentSession(prepared, context) {
|
|
|
4575
4383
|
return session;
|
|
4576
4384
|
}
|
|
4577
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
|
+
}
|
|
4578
4390
|
function changedOption(options, baseline, key) {
|
|
4579
4391
|
return JSON.stringify(options[key]) !== JSON.stringify(baseline[key]);
|
|
4580
4392
|
}
|
|
@@ -4597,7 +4409,7 @@ function resourcePolicySummary(policy, tools = policy.selectedTools ?? []) {
|
|
|
4597
4409
|
}
|
|
4598
4410
|
var RESOURCE_SELECTOR_KEYS = ["skills", "extensions", "tools"];
|
|
4599
4411
|
function resourceSelectorLayers(sources, key) {
|
|
4600
|
-
return [sources.global[key], sources.project[key], sources.
|
|
4412
|
+
return [sources.global[key], sources.project[key], sources.call?.[key]];
|
|
4601
4413
|
}
|
|
4602
4414
|
function selectorListWidened(ceiling, candidate) {
|
|
4603
4415
|
let candidateIndex = 0;
|
|
@@ -4654,32 +4466,32 @@ function appendResourcePolicyNarrowing(ceiling, candidate) {
|
|
|
4654
4466
|
};
|
|
4655
4467
|
}
|
|
4656
4468
|
function packageRoot(start) {
|
|
4657
|
-
let current =
|
|
4469
|
+
let current = dirname3(canonicalPath(start));
|
|
4658
4470
|
for (; ; ) {
|
|
4659
|
-
const candidates = [current,
|
|
4471
|
+
const candidates = [current, join5(current, "..", "@earendil-works", "pi-coding-agent"), join5(current, "..", "pi-coding-agent")];
|
|
4660
4472
|
for (const candidate of candidates) {
|
|
4661
|
-
const packageJsonPath =
|
|
4473
|
+
const packageJsonPath = join5(candidate, "package.json");
|
|
4662
4474
|
if (!existsSync(packageJsonPath)) continue;
|
|
4663
|
-
const packageJson = JSON.parse(
|
|
4475
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
4664
4476
|
if (object(packageJson) && typeof packageJson.name === "string" && packageJson.name === "@earendil-works/pi-coding-agent") return candidate;
|
|
4665
4477
|
}
|
|
4666
|
-
const parent =
|
|
4478
|
+
const parent = dirname3(current);
|
|
4667
4479
|
if (parent === current) return void 0;
|
|
4668
4480
|
current = parent;
|
|
4669
4481
|
}
|
|
4670
4482
|
}
|
|
4671
4483
|
function piPackageEntrypoint(root) {
|
|
4672
|
-
const packageJson = JSON.parse(
|
|
4484
|
+
const packageJson = JSON.parse(readFileSync2(join5(root, "package.json"), "utf8"));
|
|
4673
4485
|
if (!object(packageJson)) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4674
4486
|
const bin = packageJson.bin;
|
|
4675
4487
|
const cli = typeof bin === "string" ? bin : object(bin) && typeof bin.pi === "string" ? bin.pi : void 0;
|
|
4676
4488
|
if (!cli) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4677
|
-
const entrypoint =
|
|
4489
|
+
const entrypoint = resolve4(root, cli);
|
|
4678
4490
|
if (!existsSync(entrypoint)) throw new Error(`the originating Pi CLI entrypoint is unavailable: ${entrypoint}`);
|
|
4679
4491
|
return entrypoint;
|
|
4680
4492
|
}
|
|
4681
4493
|
async function resolvePiRuntime() {
|
|
4682
|
-
const executableName =
|
|
4494
|
+
const executableName = basename2(process.execPath).toLowerCase();
|
|
4683
4495
|
const bunBinary = ["$bunfs", "~BUN", "%7EBUN"].some((marker) => import.meta.url.includes(marker)) || Boolean(process.versions.bun && !["bun", "bun.exe"].includes(executableName));
|
|
4684
4496
|
if (bunBinary) return { runtime: Object.freeze({ executable: process.execPath }) };
|
|
4685
4497
|
const errors = [];
|
|
@@ -4703,19 +4515,12 @@ async function resolvePiRuntime() {
|
|
|
4703
4515
|
}
|
|
4704
4516
|
}
|
|
4705
4517
|
var piRuntimeResolution = resolvePiRuntime();
|
|
4706
|
-
function
|
|
4707
|
-
|
|
4708
|
-
const instruction = options.budget?.instruction();
|
|
4709
|
-
return `${basePrompt}
|
|
4710
|
-
|
|
4711
|
-
Task:
|
|
4712
|
-
${task}${instruction ? `
|
|
4713
|
-
|
|
4714
|
-
${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");
|
|
4715
4520
|
}
|
|
4716
4521
|
async function preparedAgentSession(input, initialPrompt) {
|
|
4717
4522
|
const { runtime: piRuntime, error: piRuntimeError } = await piRuntimeResolution;
|
|
4718
|
-
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;
|
|
4719
4524
|
const prepared = {
|
|
4720
4525
|
cwd: input.cwd,
|
|
4721
4526
|
model: Object.freeze({ ...input.model }),
|
|
@@ -4745,49 +4550,30 @@ function agentSetupSummary(setup, hookNames) {
|
|
|
4745
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) } : {} };
|
|
4746
4551
|
}
|
|
4747
4552
|
async function attemptSessionInput(source, attempt) {
|
|
4748
|
-
const target =
|
|
4553
|
+
const target = join5(dirname3(source), `${basename2(source, ".jsonl")}-attempt-${String(attempt)}.jsonl`);
|
|
4749
4554
|
await copyFile(source, target);
|
|
4750
4555
|
return target;
|
|
4751
4556
|
}
|
|
4752
|
-
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) {
|
|
4753
4558
|
const setupSignal = signal ?? root.runContext?.signal ?? new AbortController().signal;
|
|
4754
4559
|
const baselineOptions = structuredClone(options.agentOptions ?? {});
|
|
4755
4560
|
const baseResourcePolicy = await root.agentResourcePolicy?.();
|
|
4756
|
-
const
|
|
4757
|
-
const
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) return value;
|
|
4766
|
-
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
|
|
4767
4570
|
};
|
|
4768
|
-
const
|
|
4769
|
-
const rawCallExtensions = selectorValue("extensions", options.extensions)?.map((selector) => canonicalExtensionSelector(selector, cwd));
|
|
4770
|
-
const rawCallTools = selectorValue("tools", options.tools);
|
|
4771
|
-
const callSelectors = { ...rawCallSkills === void 0 ? {} : { skills: rawCallSkills }, ...rawCallExtensions === void 0 ? {} : { extensions: rawCallExtensions }, ...rawCallTools === void 0 ? {} : { tools: rawCallTools } };
|
|
4772
|
-
const resourcePolicy = baseResourcePolicy ? {
|
|
4773
|
-
...baseResourcePolicy,
|
|
4774
|
-
effective: {
|
|
4775
|
-
skills: [...baseResourcePolicy.effective.skills, ...roleSelectors?.skills ?? [], ...rawCallSkills ?? []],
|
|
4776
|
-
extensions: [...baseResourcePolicy.effective.extensions, ...roleSelectors?.extensions ?? [], ...rawCallExtensions ?? []],
|
|
4777
|
-
...baseResourcePolicy.effective.tools === void 0 && roleSelectors?.tools === void 0 && rawCallTools === void 0 ? {} : { tools: [...baseResourcePolicy.effective.tools ?? [], ...roleSelectors?.tools ?? [], ...rawCallTools ?? []] }
|
|
4778
|
-
},
|
|
4779
|
-
selectorSources: { global: baseResourcePolicy.selectorSources.global, project: baseResourcePolicy.selectorSources.project, ...roleSelectors ? { role: roleSelectors } : {}, ...Object.keys(callSelectors).length ? { call: callSelectors } : {} }
|
|
4780
|
-
} : void 0;
|
|
4781
|
-
if (resourcePolicy) {
|
|
4782
|
-
resourcePolicy.selectedTools = resolved.tools;
|
|
4783
|
-
resourcePolicy.unmatchedTools = unmatchedResourcePatterns(resourceSelectorLayers(resourcePolicy.selectorSources, "tools").flatMap((layer) => layer ?? []), [...root.tools]);
|
|
4784
|
-
}
|
|
4785
|
-
const resourcePolicyCeiling = resourcePolicy ? structuredClone(resourcePolicy) : void 0;
|
|
4571
|
+
const resourcePolicyCeiling = structuredClone(resourcePolicy);
|
|
4786
4572
|
const sessionPath = options.sessionPath === void 0 || inspection ? options.sessionPath : await attemptSessionInput(options.sessionPath, attempt);
|
|
4787
|
-
const extensionSettings =
|
|
4788
|
-
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 } : {} });
|
|
4789
4575
|
if (options.agentNodeId !== void 0) root.onAgentSettings?.(options.agentNodeId, extensionSettings);
|
|
4790
|
-
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) };
|
|
4791
4577
|
const setup = { prompt: task, options: sessionInput.options ?? {}, sessionInput, prepared: await preparedAgentSession(sessionInput, task), transport };
|
|
4792
4578
|
const base = fallbackSetupContext(root, options, setupSignal);
|
|
4793
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" } : {} });
|
|
@@ -4807,19 +4593,42 @@ async function prepareAgentSetup(root, transport, task, options, resolved, cwd,
|
|
|
4807
4593
|
try {
|
|
4808
4594
|
if (resourcePolicyWidened(resourcePolicyCeiling, setup.sessionInput.resourcePolicy)) throw new WorkflowError("INVALID_METADATA", "Agent setup widened the prepared resource policy");
|
|
4809
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
|
+
}
|
|
4810
4603
|
setup.sessionInput.options = setup.options;
|
|
4811
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);
|
|
4812
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];
|
|
4813
|
-
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}`);
|
|
4814
4617
|
const customToolNames = /* @__PURE__ */ new Set([...(setup.sessionInput.customTools ?? []).map(({ name }) => name), ...setup.sessionInput.resultTool ? [setup.sessionInput.resultTool.name] : []]);
|
|
4815
4618
|
const widened = setup.sessionInput.tools.find((tool) => !resolved.tools.includes(tool) && !customToolNames.has(tool));
|
|
4816
4619
|
const outsideTool = widened ?? setup.sessionInput.tools.find((tool) => !root.tools.has(tool) && !customToolNames.has(tool));
|
|
4817
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") ?? [];
|
|
4818
4627
|
} catch (error) {
|
|
4819
4628
|
setup.prepared = await preparedAgentSession(setup.sessionInput, task);
|
|
4820
4629
|
return { setup, summary: agentSetupSummary(setup, hookNames), failure: { error } };
|
|
4821
4630
|
}
|
|
4822
|
-
setup.prepared = await preparedAgentSession(setup.sessionInput, inspection ? task :
|
|
4631
|
+
setup.prepared = await preparedAgentSession(setup.sessionInput, inspection ? task : setup.prompt);
|
|
4823
4632
|
return { setup, summary: agentSetupSummary(setup, hookNames) };
|
|
4824
4633
|
}
|
|
4825
4634
|
function attemptRecord(transport, attempt, session, setup, stats, result, error) {
|
|
@@ -4839,54 +4648,93 @@ var WorkflowAgentExecutor = class {
|
|
|
4839
4648
|
}
|
|
4840
4649
|
root;
|
|
4841
4650
|
transport;
|
|
4842
|
-
warnedRoleTools = /* @__PURE__ */ new Set();
|
|
4843
4651
|
setRunContext(runContext) {
|
|
4844
4652
|
this.root.runContext = runContext;
|
|
4845
4653
|
}
|
|
4846
4654
|
resolve(options, inheritedTools) {
|
|
4847
|
-
|
|
4848
|
-
const
|
|
4849
|
-
if (
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
const
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
...this.root.knownModels === void 0 ? {} : { knownModels: this.root.knownModels },
|
|
4868
|
-
...this.root.availableModels === void 0 ? {} : { availableModels: this.root.availableModels },
|
|
4869
|
-
...this.root.blockedAliases === void 0 ? {} : { blockedAliases: this.root.blockedAliases },
|
|
4870
|
-
...this.root.blockedAliasTargets === void 0 ? {} : { blockedAliasTargets: this.root.blockedAliasTargets },
|
|
4871
|
-
...this.root.settingsPath === void 0 ? {} : { settingsPath: this.root.settingsPath },
|
|
4872
|
-
rootModel: this.root.model,
|
|
4873
|
-
rootTools: this.root.tools,
|
|
4874
|
-
...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 ?? {
|
|
4875
4675
|
...options.model === void 0 ? {} : { model: options.model },
|
|
4876
|
-
...options.
|
|
4877
|
-
...options.
|
|
4878
|
-
...options.
|
|
4879
|
-
...options.
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
});
|
|
4883
|
-
|
|
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;
|
|
4884
4732
|
}
|
|
4885
4733
|
async execute(task, options, signal, customTools = [], setSteer, beforeRetry) {
|
|
4886
4734
|
const executionSignal = signal ?? this.root.runContext?.signal;
|
|
4887
4735
|
if (!Number.isInteger(options.retries ?? 0) || (options.retries ?? 0) < 0) throw new WorkflowError("INVALID_METADATA", "retries must be a non-negative integer");
|
|
4888
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");
|
|
4889
|
-
let resolved
|
|
4737
|
+
let resolved;
|
|
4890
4738
|
let recoveryModel;
|
|
4891
4739
|
let cwd;
|
|
4892
4740
|
if (options.parent) {
|
|
@@ -4907,6 +4755,16 @@ var WorkflowAgentExecutor = class {
|
|
|
4907
4755
|
if (options.cwd) throw new WorkflowError("INVALID_METADATA", "Only child agents or worktree scopes may provide a cwd");
|
|
4908
4756
|
cwd = this.root.cwd;
|
|
4909
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
|
+
} });
|
|
4910
4768
|
const attempts = [];
|
|
4911
4769
|
let maxAttempts = (options.retries ?? 0) + 1;
|
|
4912
4770
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
@@ -4936,7 +4794,7 @@ var WorkflowAgentExecutor = class {
|
|
|
4936
4794
|
const resultTool = defineTool({
|
|
4937
4795
|
name: "workflow_result",
|
|
4938
4796
|
label: "Workflow Result",
|
|
4939
|
-
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.',
|
|
4940
4798
|
parameters: resultSchema.Type(),
|
|
4941
4799
|
async execute(_id, value) {
|
|
4942
4800
|
if (!resultSchema.Check(value) || !jsonValue(value)) return { content: [{ type: "text", text: "Result does not match the required schema." }], details: {}, isError: true };
|
|
@@ -4968,11 +4826,23 @@ var WorkflowAgentExecutor = class {
|
|
|
4968
4826
|
} : void 0;
|
|
4969
4827
|
try {
|
|
4970
4828
|
setupFailed = true;
|
|
4971
|
-
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);
|
|
4972
4830
|
setup = prepared.setup;
|
|
4973
4831
|
const attemptSetup = prepared.setup;
|
|
4974
4832
|
setupSummary = prepared.summary;
|
|
4975
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
|
+
}
|
|
4976
4846
|
setupFailed = false;
|
|
4977
4847
|
if (attemptSignal.aborted) throw new WorkflowError("CANCELLED", "Agent cancelled");
|
|
4978
4848
|
await options.onAttempt?.({ attempt, transport: attemptSetup.transport.id, accounting: zeroAccounting(), setup: setupSummary });
|
|
@@ -5008,6 +4878,12 @@ var WorkflowAgentExecutor = class {
|
|
|
5008
4878
|
}
|
|
5009
4879
|
}
|
|
5010
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?.();
|
|
5011
4887
|
},
|
|
5012
4888
|
onBeforeComplete: async () => {
|
|
5013
4889
|
if (options.worktreeOwner) await runStore?.snapshotWorktree(options.worktreeOwner);
|
|
@@ -5043,6 +4919,7 @@ var WorkflowAgentExecutor = class {
|
|
|
5043
4919
|
const runtimeRun = { id: transportBase.run.runId, namespaceId: transportBase.run.sessionId, workflowName: transportBase.run.workflow.name };
|
|
5044
4920
|
const providerRecovery = options.providerErrorRecovery;
|
|
5045
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);
|
|
5046
4923
|
const runtimeRequest = {
|
|
5047
4924
|
task: setup.prepared.initialPrompt ?? setup.prompt,
|
|
5048
4925
|
cwd: setup.prepared.cwd,
|
|
@@ -5051,6 +4928,7 @@ var WorkflowAgentExecutor = class {
|
|
|
5051
4928
|
// Pi sessions execute the prepared native definitions; the executor does not duplicate them as neutral request tools.
|
|
5052
4929
|
customTools: [],
|
|
5053
4930
|
...options.schema ? { resultSchema: options.schema } : {},
|
|
4931
|
+
...attemptControl ? { turnControl: attemptControl } : {},
|
|
5054
4932
|
run: runtimeRun,
|
|
5055
4933
|
agent: runtimeAgent,
|
|
5056
4934
|
signal: attemptSignal,
|
|
@@ -5137,7 +5015,7 @@ import { SettingsManager as SettingsManager2, truncateToVisualLines as truncateT
|
|
|
5137
5015
|
import { spawn } from "node:child_process";
|
|
5138
5016
|
import { mkdtemp, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
5139
5017
|
import { tmpdir } from "node:os";
|
|
5140
|
-
import { join as
|
|
5018
|
+
import { join as join6 } from "node:path";
|
|
5141
5019
|
function workflowPromptArtifact(prompt) {
|
|
5142
5020
|
return { extension: ".md", content: prompt };
|
|
5143
5021
|
}
|
|
@@ -5148,23 +5026,23 @@ function workflowResultArtifact(value) {
|
|
|
5148
5026
|
async function spawnWorkflowEditor(command, path) {
|
|
5149
5027
|
const [editor, ...editorArgs] = command.split(" ");
|
|
5150
5028
|
if (!editor) return null;
|
|
5151
|
-
return new Promise((
|
|
5029
|
+
return new Promise((resolve5) => {
|
|
5152
5030
|
try {
|
|
5153
5031
|
const child = spawn(editor, [...editorArgs, path], { stdio: "inherit", shell: process.platform === "win32" });
|
|
5154
5032
|
child.once("error", () => {
|
|
5155
|
-
|
|
5033
|
+
resolve5(null);
|
|
5156
5034
|
});
|
|
5157
5035
|
child.once("close", (code) => {
|
|
5158
|
-
|
|
5036
|
+
resolve5(code);
|
|
5159
5037
|
});
|
|
5160
5038
|
} catch {
|
|
5161
|
-
|
|
5039
|
+
resolve5(null);
|
|
5162
5040
|
}
|
|
5163
5041
|
});
|
|
5164
5042
|
}
|
|
5165
5043
|
async function openWorkflowArtifact(tui, command, artifact) {
|
|
5166
|
-
const directory = await mkdtemp(
|
|
5167
|
-
const path =
|
|
5044
|
+
const directory = await mkdtemp(join6(tmpdir(), "pi-workflow-editor-"));
|
|
5045
|
+
const path = join6(directory, `artifact${artifact.extension}`);
|
|
5168
5046
|
try {
|
|
5169
5047
|
await writeFile3(path, artifact.content, { encoding: "utf8", mode: 384 });
|
|
5170
5048
|
tui.stop();
|
|
@@ -5355,7 +5233,6 @@ function formatAgentDetail(agent, styles = PLAIN_WORKFLOW_PROGRESS_STYLES, now =
|
|
|
5355
5233
|
`State: ${state(agent.state)}`,
|
|
5356
5234
|
...agent.structuralPath?.length ? [`Structural path: ${agent.structuralPath.join(" > ")}`] : [],
|
|
5357
5235
|
...model === void 0 ? [] : [`Model: ${model}`],
|
|
5358
|
-
`Role: ${agent.role ?? "(none)"}`,
|
|
5359
5236
|
`Tools: ${tools}`,
|
|
5360
5237
|
...agent.attempts !== void 0 && Number.isFinite(agent.attempts) && agent.attempts > 1 ? [`Attempts: ${String(agent.attempts)}`] : [],
|
|
5361
5238
|
...duration === void 0 ? [] : [`Duration: ${formatWorkflowRuntime(duration)}`],
|
|
@@ -5398,7 +5275,9 @@ function formatNavigatorColumns(sidebar, detail, width, layout = {}) {
|
|
|
5398
5275
|
if (safeWidth >= 80) {
|
|
5399
5276
|
const sidebarWidth = Math.min(42, Math.max(24, Math.floor((safeWidth - 3) * 0.38)));
|
|
5400
5277
|
const left2 = sidebar.flatMap((line) => wrapNavigatorLine(line, sidebarWidth));
|
|
5401
|
-
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];
|
|
5402
5281
|
return Array.from({ length: Math.max(left2.length, right.length) }, (_, index) => {
|
|
5403
5282
|
const cell = left2[index] ?? "";
|
|
5404
5283
|
return `${cell}${" ".repeat(Math.max(0, sidebarWidth - visibleWidth2(cell)))} | ${right[index] ?? ""}`;
|
|
@@ -5443,8 +5322,8 @@ function createInlineConfirm(requestRender) {
|
|
|
5443
5322
|
return {
|
|
5444
5323
|
ask(title, message) {
|
|
5445
5324
|
answer(false);
|
|
5446
|
-
return new Promise((
|
|
5447
|
-
pending = { title, message, yes: true, resolve:
|
|
5325
|
+
return new Promise((resolve5) => {
|
|
5326
|
+
pending = { title, message, yes: true, resolve: resolve5 };
|
|
5448
5327
|
requestRender();
|
|
5449
5328
|
});
|
|
5450
5329
|
},
|
|
@@ -5475,11 +5354,11 @@ function createInlineConfirm(requestRender) {
|
|
|
5475
5354
|
}
|
|
5476
5355
|
|
|
5477
5356
|
// src/trajectory.ts
|
|
5478
|
-
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";
|
|
5479
5358
|
|
|
5480
5359
|
// subagents/src/contracts.ts
|
|
5481
5360
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
5482
|
-
import { Value as
|
|
5361
|
+
import { Value as Value3 } from "typebox/value";
|
|
5483
5362
|
var SUBAGENTS_TOOL_NAMES = [
|
|
5484
5363
|
"subagents_run",
|
|
5485
5364
|
"subagents_inspect",
|
|
@@ -5500,15 +5379,15 @@ var SUBAGENTS_RUN_PARAMETERS = Type2.Object({
|
|
|
5500
5379
|
label: Type2.Optional(Type2.String({ description: "Optional display label for the subagent" })),
|
|
5501
5380
|
model: Type2.Optional(Type2.String({ description: "Optional model as provider/model:thinking or alias[:thinking]" })),
|
|
5502
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" })),
|
|
5503
5383
|
skills: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional ordered skill selectors; candidates start enabled and !* restricts the set" })),
|
|
5504
5384
|
extensions: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional ordered extension selectors; candidates start enabled and !* restricts the set" })),
|
|
5505
5385
|
contextFiles: Type2.Optional(Type2.Array(Type2.String(), { description: "Optional context-file scopes: global, project, cwd" })),
|
|
5506
|
-
role: Type2.Optional(Type2.String({ description: "Workflow role name" })),
|
|
5507
5386
|
worktree: Type2.Optional(Type2.String({ description: "Optional named worktree; requires a clean working tree" })),
|
|
5508
5387
|
outputSchema: Type2.Optional(Type2.Record(Type2.String(), Type2.Unknown(), { description: "Optional JSON schema for the result" })),
|
|
5509
5388
|
retries: Type2.Optional(Type2.Integer({ minimum: 0, maximum: SUBAGENT_MAX_RETRIES, description: "Optional retry count; at most 255 retries" })),
|
|
5510
5389
|
timeoutMs: Type2.Optional(Type2.Union([Type2.Integer({ minimum: 1, description: "Optional execution timeout in milliseconds" }), Type2.Null()]))
|
|
5511
|
-
}, { additionalProperties:
|
|
5390
|
+
}, { additionalProperties: true });
|
|
5512
5391
|
var SUBAGENTS_INSPECT_PARAMETERS = Type2.Object({
|
|
5513
5392
|
id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" })),
|
|
5514
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" }))
|
|
@@ -5542,7 +5421,7 @@ var SUBAGENT_STATUS_OUTPUT = Type2.Object({
|
|
|
5542
5421
|
var SUBAGENTS_INSPECT_OUTPUT = Type2.Union([SUBAGENT_STATUS_OUTPUT, Type2.Array(Type2.Object({ id: Type2.String(), state: Type2.String() }))]);
|
|
5543
5422
|
var SUBAGENTS_STEER_OUTPUT = Type2.Object({ id: Type2.String(), accepted: Type2.Boolean() });
|
|
5544
5423
|
function normalizeSubagentRunRequest(value) {
|
|
5545
|
-
if (!
|
|
5424
|
+
if (!Value3.Check(SUBAGENTS_RUN_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_run parameters");
|
|
5546
5425
|
if (typeof value.worktree === "string" && !value.worktree.trim()) throw new WorkflowError("INVALID_METADATA", "worktree name must be a non-empty string");
|
|
5547
5426
|
const snapshot = structuredClone(value);
|
|
5548
5427
|
snapshot.mode ??= "background";
|
|
@@ -5758,11 +5637,11 @@ var SCRIPT_CALL_TEXT_LIMIT_BYTES = 16 * 1024;
|
|
|
5758
5637
|
// subagents/src/manager.ts
|
|
5759
5638
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
5760
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";
|
|
5761
|
-
import { join as
|
|
5762
|
-
import { getAgentDir as
|
|
5640
|
+
import { join as join8 } from "node:path";
|
|
5641
|
+
import { getAgentDir as getAgentDir5 } from "@earendil-works/pi-coding-agent";
|
|
5763
5642
|
|
|
5764
5643
|
// subagents/src/worktree.ts
|
|
5765
|
-
import { join as
|
|
5644
|
+
import { join as join7 } from "node:path";
|
|
5766
5645
|
function syntheticRun(context) {
|
|
5767
5646
|
return {
|
|
5768
5647
|
id: context.runId,
|
|
@@ -5785,7 +5664,7 @@ function syntheticSnapshot(context) {
|
|
|
5785
5664
|
...context.worktreePostCreateCommandSource === void 0 ? {} : { settingsSources: { concurrency: "subagents", modelAliases: "subagents", worktreePostCreateCommand: context.worktreePostCreateCommandSource } },
|
|
5786
5665
|
models: [],
|
|
5787
5666
|
tools: [],
|
|
5788
|
-
|
|
5667
|
+
agentConfigurations: {},
|
|
5789
5668
|
schemas: []
|
|
5790
5669
|
};
|
|
5791
5670
|
}
|
|
@@ -5824,7 +5703,7 @@ function createRunStoreWorktreeAdapter(home) {
|
|
|
5824
5703
|
};
|
|
5825
5704
|
}
|
|
5826
5705
|
function defaultWorktreeHome(storageDirectory3) {
|
|
5827
|
-
return
|
|
5706
|
+
return join7(storageDirectory3, ".worktrees");
|
|
5828
5707
|
}
|
|
5829
5708
|
|
|
5830
5709
|
// subagents/src/manager.ts
|
|
@@ -5864,22 +5743,11 @@ function rootModel(context) {
|
|
|
5864
5743
|
const thinking = context.extensionContext.thinkingLevel;
|
|
5865
5744
|
return { provider: model.provider, model: model.id, ...thinking === void 0 ? {} : { thinking } };
|
|
5866
5745
|
}
|
|
5867
|
-
function
|
|
5868
|
-
return trustedProject && external.projectExtensionSettings !== void 0 ? workflowProjectSettingsPath(cwd) : workflowSettingsPath(agentDir);
|
|
5869
|
-
}
|
|
5870
|
-
function validateStandaloneExtensionSources(context, external, registry, agentDir) {
|
|
5871
|
-
const cwd = context.extensionContext.cwd;
|
|
5872
|
-
const globalSettingsPath = workflowSettingsPath(agentDir);
|
|
5873
|
-
const trustedProject = context.extensionContext.isProjectTrusted();
|
|
5874
|
-
if (external.globalExtensionSettings !== void 0) registry.validateExtensionSettings(external.globalExtensionSettings, { source: "global", cwd, projectTrusted: trustedProject, settingsPath: globalSettingsPath });
|
|
5875
|
-
if (trustedProject && external.projectExtensionSettings !== void 0) registry.validateExtensionSettings(external.projectExtensionSettings, { source: "project", cwd, projectTrusted: true, settingsPath: workflowProjectSettingsPath(cwd) });
|
|
5876
|
-
}
|
|
5877
|
-
function executionRoot(context, dependencies, signal, runId, worktree, external) {
|
|
5746
|
+
function executionRoot(context, dependencies, signal, runId, worktree) {
|
|
5878
5747
|
const extensionContext = context.extensionContext;
|
|
5879
5748
|
const model = rootModel(context);
|
|
5880
|
-
const agentDir = dependencies.agentDir ??
|
|
5749
|
+
const agentDir = dependencies.agentDir ?? getAgentDir5();
|
|
5881
5750
|
const currentTrustedProject = extensionContext.isProjectTrusted();
|
|
5882
|
-
if (external.projectTrusted && !currentTrustedProject) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
|
|
5883
5751
|
const trustedProject = currentTrustedProject;
|
|
5884
5752
|
const settingsPath = workflowSettingsPath(agentDir);
|
|
5885
5753
|
const settings = resolveWorkflowSettings(extensionContext.cwd, trustedProject, settingsPath);
|
|
@@ -5902,11 +5770,13 @@ function executionRoot(context, dependencies, signal, runId, worktree, external)
|
|
|
5902
5770
|
signal
|
|
5903
5771
|
};
|
|
5904
5772
|
const registry = loadingRegistry();
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
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;
|
|
5908
5777
|
return {
|
|
5909
5778
|
cwd: extensionContext.cwd,
|
|
5779
|
+
projectTrusted: trustedProject,
|
|
5910
5780
|
model,
|
|
5911
5781
|
tools,
|
|
5912
5782
|
resourceSelectors: resourcePolicy.effective,
|
|
@@ -5916,9 +5786,10 @@ function executionRoot(context, dependencies, signal, runId, worktree, external)
|
|
|
5916
5786
|
availableModels,
|
|
5917
5787
|
knownModels,
|
|
5918
5788
|
...Object.keys(staticAliases).length ? { modelAliases: staticAliases } : {},
|
|
5789
|
+
dynamicModelAliasNames: [],
|
|
5919
5790
|
settingsPath: settings.sources.modelAliases,
|
|
5920
5791
|
extensionSettingsPath,
|
|
5921
|
-
|
|
5792
|
+
agentPreparationHooks: registry.agentPreparationHooks(),
|
|
5922
5793
|
agentSetupHooks: registry.agentSetupHooks(),
|
|
5923
5794
|
validateExtensionSettings: registry.validateExtensionSettings,
|
|
5924
5795
|
agentResourcePolicy: () => structuredClone(resourcePolicy),
|
|
@@ -5934,11 +5805,10 @@ async function addDynamicAliases(context, signal, root) {
|
|
|
5934
5805
|
const trustedProject = resourcePolicy?.projectTrusted ?? context.extensionContext.isProjectTrusted();
|
|
5935
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)));
|
|
5936
5807
|
validateModelAliasAvailability(dynamicAliases, Object.keys(dynamicAliases), root.availableModels ?? /* @__PURE__ */ new Set(), root.knownModels ?? /* @__PURE__ */ new Set(), root.settingsPath);
|
|
5937
|
-
return { ...root, modelAliases: { ...dynamicAliases, ...staticAliases } };
|
|
5808
|
+
return { ...root, modelAliases: { ...dynamicAliases, ...staticAliases }, dynamicModelAliasNames: Object.keys(dynamicAliases) };
|
|
5938
5809
|
}
|
|
5939
5810
|
function executionOptions(request, onAttempt, onProgress) {
|
|
5940
|
-
const
|
|
5941
|
-
const label2 = request.label ?? roleNameOf(role) ?? "subagent";
|
|
5811
|
+
const label2 = request.label ?? "subagent";
|
|
5942
5812
|
return {
|
|
5943
5813
|
label: label2,
|
|
5944
5814
|
workflowName: WORKFLOW_NAME,
|
|
@@ -5947,35 +5817,35 @@ function executionOptions(request, onAttempt, onProgress) {
|
|
|
5947
5817
|
...request.skills === void 0 ? {} : { skills: request.skills },
|
|
5948
5818
|
...request.extensions === void 0 ? {} : { extensions: request.extensions },
|
|
5949
5819
|
...request.contextFiles === void 0 ? {} : { contextFiles: request.contextFiles },
|
|
5950
|
-
...role === void 0 ? {} : { role },
|
|
5951
5820
|
...request.worktree === void 0 ? {} : { worktreeOwner: structuralPath("worktree", "named", request.worktree) },
|
|
5952
5821
|
...request.outputSchema === void 0 ? {} : { schema: request.outputSchema },
|
|
5953
5822
|
...request.retries === void 0 ? {} : { retries: request.retries },
|
|
5954
5823
|
...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
|
|
5824
|
+
agentOptions: Object.fromEntries(Object.entries(request).filter(([key]) => !["prompt", "mode", "worktree"].includes(key))),
|
|
5955
5825
|
onAttempt,
|
|
5956
5826
|
onProgress
|
|
5957
5827
|
};
|
|
5958
5828
|
}
|
|
5959
5829
|
function storageDirectory(dependencies) {
|
|
5960
|
-
return dependencies.storageDir ??
|
|
5830
|
+
return dependencies.storageDir ?? join8(dependencies.agentDir ?? getAgentDir5(), STORAGE_DIRECTORY);
|
|
5961
5831
|
}
|
|
5962
5832
|
function runDirectory(root, id) {
|
|
5963
|
-
return
|
|
5833
|
+
return join8(root, id);
|
|
5964
5834
|
}
|
|
5965
5835
|
function requestPath(directory) {
|
|
5966
|
-
return
|
|
5836
|
+
return join8(directory, "request.json");
|
|
5967
5837
|
}
|
|
5968
5838
|
function configurationPath(directory) {
|
|
5969
|
-
return
|
|
5839
|
+
return join8(directory, CONFIGURATION_FILE);
|
|
5970
5840
|
}
|
|
5971
5841
|
function statusPath(directory) {
|
|
5972
|
-
return
|
|
5842
|
+
return join8(directory, "status.json");
|
|
5973
5843
|
}
|
|
5974
5844
|
function resultPath(directory) {
|
|
5975
|
-
return
|
|
5845
|
+
return join8(directory, "result.json");
|
|
5976
5846
|
}
|
|
5977
5847
|
function failurePath(directory) {
|
|
5978
|
-
return
|
|
5848
|
+
return join8(directory, "failure.json");
|
|
5979
5849
|
}
|
|
5980
5850
|
async function secureDirectory(directory) {
|
|
5981
5851
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
@@ -6058,7 +5928,7 @@ async function releaseOwnerMarker(path, token) {
|
|
|
6058
5928
|
}
|
|
6059
5929
|
async function acquireStorageOwner(root, owner, liveness) {
|
|
6060
5930
|
await secureDirectory(root);
|
|
6061
|
-
const path =
|
|
5931
|
+
const path = join8(root, OWNER_FILE);
|
|
6062
5932
|
for (let attempt = 0; attempt < MAX_STORAGE_OWNER_ATTEMPTS; attempt += 1) {
|
|
6063
5933
|
const marker = { ...owner, acquiredAt: Date.now() };
|
|
6064
5934
|
try {
|
|
@@ -6129,32 +5999,8 @@ function checkedRequest(request) {
|
|
|
6129
5999
|
return normalizeSubagentRunRequest(request);
|
|
6130
6000
|
}
|
|
6131
6001
|
function decodePersistedStandaloneExternalConfiguration(value) {
|
|
6132
|
-
if (!object(value) ||
|
|
6133
|
-
|
|
6134
|
-
const projectExtensionSettings = value.projectExtensionSettings === void 0 ? void 0 : decodeWorkflowExtensions(value.projectExtensionSettings);
|
|
6135
|
-
if (value.globalExtensionSettings !== void 0 && globalExtensionSettings === void 0 || value.projectExtensionSettings !== void 0 && projectExtensionSettings === void 0) return void 0;
|
|
6136
|
-
if (!object(value.agentDefinitions)) return void 0;
|
|
6137
|
-
const agentDefinitions = /* @__PURE__ */ Object.create(null);
|
|
6138
|
-
for (const [name, definition] of Object.entries(value.agentDefinitions)) {
|
|
6139
|
-
const decoded = decodeAgentDefinition(definition);
|
|
6140
|
-
if (decoded === void 0) return void 0;
|
|
6141
|
-
agentDefinitions[name] = decoded;
|
|
6142
|
-
}
|
|
6143
|
-
return deepFreeze({ projectTrusted: value.projectTrusted, ...globalExtensionSettings === void 0 ? {} : { globalExtensionSettings }, ...projectExtensionSettings === void 0 ? {} : { projectExtensionSettings }, agentDefinitions });
|
|
6144
|
-
}
|
|
6145
|
-
function standaloneExternalConfiguration(context, dependencies) {
|
|
6146
|
-
const cwd = context.extensionContext.cwd;
|
|
6147
|
-
const trustedProject = context.extensionContext.isProjectTrusted();
|
|
6148
|
-
const agentDir = dependencies.agentDir ?? getAgentDir6();
|
|
6149
|
-
const resolution = resolveWorkflowSettings(cwd, trustedProject, workflowSettingsPath(agentDir));
|
|
6150
|
-
const globalExtensionSettings = resolution.global.extensionSettings;
|
|
6151
|
-
const projectExtensionSettings = resolution.project.extensionSettings;
|
|
6152
|
-
return deepFreeze({
|
|
6153
|
-
projectTrusted: trustedProject,
|
|
6154
|
-
...globalExtensionSettings === void 0 ? {} : { globalExtensionSettings: structuredClone(globalExtensionSettings) },
|
|
6155
|
-
...projectExtensionSettings === void 0 ? {} : { projectExtensionSettings: structuredClone(projectExtensionSettings) },
|
|
6156
|
-
agentDefinitions: structuredClone(loadAgentDefinitions(cwd, agentDir, trustedProject))
|
|
6157
|
-
});
|
|
6002
|
+
if (!object(value) || value.version !== 1) return void 0;
|
|
6003
|
+
return decodePreparedAgentConfiguration(value.configuration);
|
|
6158
6004
|
}
|
|
6159
6005
|
function checkedId(request) {
|
|
6160
6006
|
if (typeof request.id !== "string" || !validSubagentId(request.id)) throw new WorkflowError("RUN_NOT_FOUND", `Unknown subagent ${request.id}`);
|
|
@@ -6465,7 +6311,7 @@ async function createRunStorage(root, id, request, status, external) {
|
|
|
6465
6311
|
created = true;
|
|
6466
6312
|
await chmod(directory, 448);
|
|
6467
6313
|
await atomicJson(requestPath(directory), request);
|
|
6468
|
-
await atomicJson(configurationPath(directory), external);
|
|
6314
|
+
if (external !== void 0) await atomicJson(configurationPath(directory), { version: 1, configuration: external });
|
|
6469
6315
|
await atomicJson(statusPath(directory), status);
|
|
6470
6316
|
return directory;
|
|
6471
6317
|
} catch (error) {
|
|
@@ -6647,6 +6493,7 @@ var PersistentSubagentManager = class {
|
|
|
6647
6493
|
}
|
|
6648
6494
|
dependencies;
|
|
6649
6495
|
activeRuns = /* @__PURE__ */ new Map();
|
|
6496
|
+
pendingStarts = /* @__PURE__ */ new Set();
|
|
6650
6497
|
activeRunCount = 0;
|
|
6651
6498
|
terminalSummaries = /* @__PURE__ */ new Map();
|
|
6652
6499
|
reconciliationErrors = /* @__PURE__ */ new Map();
|
|
@@ -6690,32 +6537,44 @@ var PersistentSubagentManager = class {
|
|
|
6690
6537
|
await this.ensureInitialized();
|
|
6691
6538
|
if (this.disposed) throw new WorkflowError("CANCELLED", "Subagent manager is disposed");
|
|
6692
6539
|
if (context.signal?.aborted) throw new WorkflowError("CANCELLED", "Subagent cancelled");
|
|
6693
|
-
const external = frozenExternal
|
|
6694
|
-
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");
|
|
6695
6542
|
const id = randomUUID3();
|
|
6696
6543
|
const startedAt = Date.now();
|
|
6697
6544
|
const sessionId = context.extensionContext.sessionManager.getSessionId();
|
|
6698
6545
|
if (!sessionId.trim()) throw new WorkflowError("INTERNAL_ERROR", "Current Pi session identity is unavailable");
|
|
6699
6546
|
const owner = this.runOwner;
|
|
6700
6547
|
if (!owner) throw new WorkflowError("INTERNAL_ERROR", "Subagent storage owner identity is unavailable");
|
|
6701
|
-
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()));
|
|
6702
6549
|
const concurrency = settings.effective.concurrency;
|
|
6703
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.`);
|
|
6704
6551
|
this.activeRunCount += 1;
|
|
6705
6552
|
const controller = new AbortController();
|
|
6706
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);
|
|
6707
6559
|
let directory;
|
|
6708
6560
|
try {
|
|
6709
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
|
+
}
|
|
6710
6566
|
} catch (error) {
|
|
6711
6567
|
this.activeRunCount -= 1;
|
|
6712
6568
|
if (error instanceof WorkflowError) throw error;
|
|
6713
6569
|
throw internalStorageError(error, `Unable to start subagent ${id}`);
|
|
6570
|
+
} finally {
|
|
6571
|
+
this.pendingStarts.delete(pendingStart);
|
|
6572
|
+
finishStart();
|
|
6714
6573
|
}
|
|
6715
6574
|
const current = {};
|
|
6716
6575
|
let resolveTerminal;
|
|
6717
|
-
const terminal = new Promise((
|
|
6718
|
-
resolveTerminal =
|
|
6576
|
+
const terminal = new Promise((resolve5) => {
|
|
6577
|
+
resolveTerminal = resolve5;
|
|
6719
6578
|
});
|
|
6720
6579
|
const executorOwnership = { default: true };
|
|
6721
6580
|
const execution = Promise.resolve().then(async () => {
|
|
@@ -6743,12 +6602,9 @@ var PersistentSubagentManager = class {
|
|
|
6743
6602
|
throw new WorkflowError("CANCELLED", "Subagent cancelled");
|
|
6744
6603
|
}
|
|
6745
6604
|
}
|
|
6746
|
-
const baseRoot = executionRoot(context, this.dependencies, controller.signal, id, worktree
|
|
6747
|
-
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);
|
|
6748
6607
|
const transport = this.dependencies.transport ?? localAgentTransport;
|
|
6749
|
-
const injectedExecutor = this.dependencies.createExecutor?.(root, transport);
|
|
6750
|
-
executorOwnership.default = injectedExecutor === void 0;
|
|
6751
|
-
if (current.run) current.run.executorOwnsSession = executorOwnership.default;
|
|
6752
6608
|
const setSteer = (handler) => {
|
|
6753
6609
|
const run2 = current.run;
|
|
6754
6610
|
if (run2) this.registerSteerHandler(run2, handler);
|
|
@@ -6789,6 +6645,13 @@ var PersistentSubagentManager = class {
|
|
|
6789
6645
|
if (run2.state !== "running") return;
|
|
6790
6646
|
emitUpdate(run2);
|
|
6791
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;
|
|
6792
6655
|
if (injectedExecutor) return injectedExecutor.execute(snapshot.prompt, options, controller.signal, setSteer);
|
|
6793
6656
|
return new WorkflowAgentExecutor(root, transport).execute(snapshot.prompt, options, controller.signal, [], setSteer);
|
|
6794
6657
|
});
|
|
@@ -6896,11 +6759,11 @@ var PersistentSubagentManager = class {
|
|
|
6896
6759
|
}
|
|
6897
6760
|
async dispose() {
|
|
6898
6761
|
if (this.disposePromise) return this.disposePromise;
|
|
6762
|
+
this.disposed = true;
|
|
6899
6763
|
this.disposePromise = (async () => {
|
|
6900
6764
|
await this.initialization;
|
|
6901
|
-
this.disposed = true;
|
|
6902
6765
|
const runs = [...this.activeRuns.values()];
|
|
6903
|
-
await Promise.allSettled(runs.map((run) => this.stopRun(run, true)));
|
|
6766
|
+
await Promise.allSettled([...this.pendingStarts, ...runs.map((run) => this.stopRun(run, true))]);
|
|
6904
6767
|
await Promise.allSettled([...this.notificationPromises]);
|
|
6905
6768
|
const owner = this.storageOwner;
|
|
6906
6769
|
this.storageOwner = void 0;
|
|
@@ -7231,9 +7094,8 @@ var PersistentSubagentManager = class {
|
|
|
7231
7094
|
const notify = this.dependencies.notify;
|
|
7232
7095
|
if (!notify || run.notificationSent || run.disposed) return;
|
|
7233
7096
|
run.notificationSent = true;
|
|
7234
|
-
const
|
|
7235
|
-
const
|
|
7236
|
-
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 } };
|
|
7237
7099
|
const pending = Promise.resolve().then(() => notify(notification));
|
|
7238
7100
|
this.notificationPromises.add(pending);
|
|
7239
7101
|
void pending.then(() => {
|
|
@@ -7281,8 +7143,8 @@ function createSubagentManager(dependencies = {}) {
|
|
|
7281
7143
|
|
|
7282
7144
|
// subagents/src/navigator.ts
|
|
7283
7145
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
7284
|
-
import { join as
|
|
7285
|
-
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";
|
|
7286
7148
|
import { Editor, truncateToWidth as truncateToWidth3 } from "@earendil-works/pi-tui";
|
|
7287
7149
|
var MAX_DETAIL_TEXT = 4e3;
|
|
7288
7150
|
function objectValue2(value) {
|
|
@@ -7305,7 +7167,7 @@ function managerContext(context, waitForForeground = true, includeAttemptMetadat
|
|
|
7305
7167
|
async function loadRequest(storageDirectory3, id) {
|
|
7306
7168
|
if (!safeRunId(id)) return {};
|
|
7307
7169
|
try {
|
|
7308
|
-
const value = JSON.parse(await readFile5(
|
|
7170
|
+
const value = JSON.parse(await readFile5(join9(storageDirectory3, id, "request.json"), "utf8"));
|
|
7309
7171
|
return { request: normalizeSubagentRunRequest(value) };
|
|
7310
7172
|
} catch (error) {
|
|
7311
7173
|
if (isFileNotFound(error)) return {};
|
|
@@ -7338,18 +7200,11 @@ async function inspectEntry(manager, storageDirectory3, entry, context) {
|
|
|
7338
7200
|
entry: { status: inspected.status, ...request.request === void 0 ? {} : { request: request.request }, ...request.error === void 0 ? {} : { requestError: request.error } }
|
|
7339
7201
|
};
|
|
7340
7202
|
}
|
|
7341
|
-
function requestRole(request) {
|
|
7342
|
-
const role = request?.role;
|
|
7343
|
-
if (typeof role === "string" && role.trim()) return role.trim();
|
|
7344
|
-
const override = objectValue2(role);
|
|
7345
|
-
return typeof override?.name === "string" && override.name.trim() ? override.name.trim() : "none";
|
|
7346
|
-
}
|
|
7347
7203
|
function shortId(id) {
|
|
7348
7204
|
return id.length > 12 ? id.slice(0, 8) : id;
|
|
7349
7205
|
}
|
|
7350
7206
|
function entryName(entry) {
|
|
7351
|
-
|
|
7352
|
-
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);
|
|
7353
7208
|
}
|
|
7354
7209
|
function listNames(entries) {
|
|
7355
7210
|
const names = entries.map(entryName);
|
|
@@ -7386,7 +7241,7 @@ function usageAccounting(status) {
|
|
|
7386
7241
|
return status.progress?.accounting;
|
|
7387
7242
|
}
|
|
7388
7243
|
function detailPresentation(inspection) {
|
|
7389
|
-
const { status
|
|
7244
|
+
const { status } = inspection.entry;
|
|
7390
7245
|
const attempt = latestAttempt(status);
|
|
7391
7246
|
const state = status.progress?.state;
|
|
7392
7247
|
const activity = status.progress?.activity;
|
|
@@ -7395,14 +7250,12 @@ function detailPresentation(inspection) {
|
|
|
7395
7250
|
const lastEventAt = status.progress?.lastEventAt;
|
|
7396
7251
|
const attempts = status.attempts ?? attempt?.attempt;
|
|
7397
7252
|
const accounting2 = usageAccounting(status);
|
|
7398
|
-
const role = requestRole(request);
|
|
7399
7253
|
const error = status.error ?? attempt?.error;
|
|
7400
7254
|
return {
|
|
7401
7255
|
state: status.state,
|
|
7402
7256
|
...activity === void 0 ? {} : { activity: { kind: activity.kind, text: boundedText(activity.text) } },
|
|
7403
7257
|
...lastEventAt === void 0 ? {} : { lastEventAt },
|
|
7404
7258
|
...model === void 0 ? {} : { model: { provider: boundedText(model.provider, 256), model: boundedText(model.model, 256), ...model.thinking === void 0 ? {} : { thinking: model.thinking } } },
|
|
7405
|
-
...role === "none" ? {} : { role: boundedText(role, 256) },
|
|
7406
7259
|
...tools === void 0 ? {} : { tools: tools.slice(0, 256).map((tool) => boundedText(tool, 256)) },
|
|
7407
7260
|
...attempts === void 0 ? {} : { attempts },
|
|
7408
7261
|
...status.startedAt === void 0 ? {} : { startedAt: status.startedAt },
|
|
@@ -7454,8 +7307,7 @@ function standaloneActionContext(manager, inspection, context) {
|
|
|
7454
7307
|
const session = attempt.session;
|
|
7455
7308
|
const live = liveData?.liveSession && session && liveData.liveSession.reference.transport === session.transport && liveData.liveSession.reference.sessionId === session.sessionId ? liveData.liveSession : void 0;
|
|
7456
7309
|
const label2 = request?.label?.trim();
|
|
7457
|
-
const
|
|
7458
|
-
const name = label2 || (role === "none" ? "subagent" : role);
|
|
7310
|
+
const name = label2 || "subagent";
|
|
7459
7311
|
const ui = {
|
|
7460
7312
|
notify: (message, level = "info") => {
|
|
7461
7313
|
context.ui.notify(message, level);
|
|
@@ -7524,7 +7376,7 @@ function retryResult(value) {
|
|
|
7524
7376
|
return record2 && typeof record2.id === "string" && record2.id.trim() && record2.state === "running" ? { id: record2.id, state: "running" } : void 0;
|
|
7525
7377
|
}
|
|
7526
7378
|
async function openNavigatorArtifact(context, tui, artifact, label2) {
|
|
7527
|
-
const command = SettingsManager4.create(context.cwd,
|
|
7379
|
+
const command = SettingsManager4.create(context.cwd, getAgentDir6(), { projectTrusted: context.isProjectTrusted() }).getExternalEditorCommand();
|
|
7528
7380
|
if (!command) {
|
|
7529
7381
|
context.ui.notify(`Cannot open ${label2}: no external editor is configured.`, "warning");
|
|
7530
7382
|
return;
|
|
@@ -7555,7 +7407,7 @@ async function performAction(manager, storageDirectory3, entry, action, context,
|
|
|
7555
7407
|
return "unchanged";
|
|
7556
7408
|
}
|
|
7557
7409
|
if (action === "Copy run path") {
|
|
7558
|
-
await clipboard(
|
|
7410
|
+
await clipboard(join9(storageDirectory3, fresh.entry.status.id));
|
|
7559
7411
|
context.ui.notify("Copied run path.", "info");
|
|
7560
7412
|
return "unchanged";
|
|
7561
7413
|
}
|
|
@@ -7979,24 +7831,15 @@ var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "failed", "stopped"]
|
|
|
7979
7831
|
var MAX_INSPECT_VALUE_CHARS = 4e3;
|
|
7980
7832
|
var MAX_INSPECT_TOOL_CALLS = 32;
|
|
7981
7833
|
var MAX_BACKGROUND_WIDGET_ROWS = 10;
|
|
7982
|
-
function roleName(value) {
|
|
7983
|
-
if (typeof value === "string") return value.trim() || void 0;
|
|
7984
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
7985
|
-
const name = value.name;
|
|
7986
|
-
return typeof name === "string" ? name.trim() || void 0 : void 0;
|
|
7987
|
-
}
|
|
7988
7834
|
function modeName(value) {
|
|
7989
7835
|
return value === "background" || value === "foreground" ? value : void 0;
|
|
7990
7836
|
}
|
|
7991
7837
|
function requestMetadata(args, metadataAvailable) {
|
|
7992
7838
|
const mode = modeName(args.mode) ?? (metadataAvailable ? "background" : void 0);
|
|
7993
|
-
|
|
7994
|
-
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(" ");
|
|
7995
7840
|
}
|
|
7996
7841
|
function label(args) {
|
|
7997
7842
|
if (typeof args.label === "string" && args.label.trim()) return args.label.trim();
|
|
7998
|
-
const role = roleName(args.role);
|
|
7999
|
-
if (role) return role;
|
|
8000
7843
|
if (typeof args.id === "string" && args.id) return args.id.slice(0, 8);
|
|
8001
7844
|
return "subagent";
|
|
8002
7845
|
}
|
|
@@ -8186,14 +8029,12 @@ var RECEIPT_ENTRY_TYPE = "piewf-subagent-receipt";
|
|
|
8186
8029
|
function receiptFor(status, request) {
|
|
8187
8030
|
const accounting2 = status.progress?.accounting;
|
|
8188
8031
|
const model = modelName(status);
|
|
8189
|
-
const role = roleName(request.role);
|
|
8190
8032
|
const tools = status.progress?.state?.tools ?? status.attemptDetails?.at(-1)?.setup.tools;
|
|
8191
8033
|
return {
|
|
8192
8034
|
id: status.id,
|
|
8193
8035
|
label: label({ ...request, id: status.id }),
|
|
8194
8036
|
state: status.state,
|
|
8195
8037
|
...model ? { model } : {},
|
|
8196
|
-
...role === void 0 ? {} : { role },
|
|
8197
8038
|
...tools === void 0 ? {} : { tools: [...tools] },
|
|
8198
8039
|
input: accounting2?.input ?? 0,
|
|
8199
8040
|
output: accounting2?.output ?? 0,
|
|
@@ -8209,7 +8050,7 @@ function renderSubagentReceipt(data, expanded, theme) {
|
|
|
8209
8050
|
const headline = [formatTokens(data.input + data.output), formatCost(data.costUsd) || "$0.00", formatElapsed(data.durationMs), data.state].filter(Boolean).join(" \xB7 ");
|
|
8210
8051
|
const lines = [`${glyph} ${theme.bold(sanitizeDisplayText(data.label))} ${theme.fg("muted", headline)}`];
|
|
8211
8052
|
if (!expanded) return lines;
|
|
8212
|
-
const meta = [data.model ?? "", data.
|
|
8053
|
+
const meta = [data.model ?? "", data.attempts > 1 ? `${String(data.attempts)} attempts` : "", data.tools?.join(" ") ?? ""].filter(Boolean);
|
|
8213
8054
|
if (meta.length > 0) lines.push(theme.fg("muted", ` ${meta.join(" \xB7 ")}`));
|
|
8214
8055
|
const split = [data.input ? `in ${formatTokens(data.input)}` : "", data.output ? `out ${formatTokens(data.output)}` : "", data.cacheRead ? `cache ${formatTokens(data.cacheRead)}` : ""].filter(Boolean);
|
|
8215
8056
|
if (split.length > 0) lines.push(theme.fg("muted", ` ${split.join(" \xB7 ")}`));
|
|
@@ -8346,15 +8187,15 @@ function validateSubagentRunRequest(value) {
|
|
|
8346
8187
|
return normalizeSubagentRunRequest(value);
|
|
8347
8188
|
}
|
|
8348
8189
|
function validateSubagentIdRequest(value, operation) {
|
|
8349
|
-
if (!
|
|
8190
|
+
if (!Value4.Check(SUBAGENTS_ID_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", `Invalid ${operation} parameters`);
|
|
8350
8191
|
return value;
|
|
8351
8192
|
}
|
|
8352
8193
|
function validateSubagentInspectRequest(value) {
|
|
8353
|
-
if (!
|
|
8194
|
+
if (!Value4.Check(SUBAGENTS_INSPECT_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_inspect parameters");
|
|
8354
8195
|
return value;
|
|
8355
8196
|
}
|
|
8356
8197
|
function validateSubagentSteerRequest(value) {
|
|
8357
|
-
if (!
|
|
8198
|
+
if (!Value4.Check(SUBAGENTS_STEER_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_steer parameters");
|
|
8358
8199
|
return value;
|
|
8359
8200
|
}
|
|
8360
8201
|
function serialize(value) {
|
|
@@ -8470,8 +8311,7 @@ function createSubagentTools(manager) {
|
|
|
8470
8311
|
}
|
|
8471
8312
|
function notificationContent(notification) {
|
|
8472
8313
|
const label2 = notification.label?.trim();
|
|
8473
|
-
const
|
|
8474
|
-
const identity = label2 ? `${label2} role=${role} (${notification.id})` : `${notification.id} role=${role}`;
|
|
8314
|
+
const identity = label2 ? `${label2} (${notification.id})` : notification.id;
|
|
8475
8315
|
if (notification.state === "completed") return `Subagent ${identity} completed. Inspect it with subagents_inspect({ id: "${notification.id}" }).`;
|
|
8476
8316
|
return `Subagent ${identity} failed: ${notification.error?.message ?? "unknown error"}. Inspect it with subagents_inspect({ id: "${notification.id}" }).`;
|
|
8477
8317
|
}
|
|
@@ -8488,7 +8328,7 @@ function managerDependencies(options, activeTools, notify, onStatus, onResourceW
|
|
|
8488
8328
|
}
|
|
8489
8329
|
function storageDirectory2(options) {
|
|
8490
8330
|
const dependencies = options.managerDependencies;
|
|
8491
|
-
return dependencies?.storageDir ??
|
|
8331
|
+
return dependencies?.storageDir ?? join10(dependencies?.agentDir ?? getAgentDir7(), "subagents");
|
|
8492
8332
|
}
|
|
8493
8333
|
function createSubagentsExtension(options = {}, activeTools, notify, onStatus, onResourceWarning) {
|
|
8494
8334
|
const manager = options.manager ?? createSubagentManager(managerDependencies(options, activeTools, notify, onStatus, onResourceWarning));
|