@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.19.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 +135 -0
- package/dist/src/agent-execution.js +114 -87
- package/dist/src/background-widget.d.ts +61 -0
- package/dist/src/background-widget.js +23 -16
- package/dist/src/decoders.js +28 -1
- package/dist/src/execution.d.ts +4 -2
- package/dist/src/execution.js +43 -2
- package/dist/src/host-delivery.js +1 -0
- package/dist/src/host-navigator.d.ts +18 -0
- package/dist/src/host-navigator.js +71 -13
- package/dist/src/host-recovery.d.ts +5 -2
- package/dist/src/host-recovery.js +3 -3
- package/dist/src/host-view.d.ts +21 -1
- package/dist/src/host-view.js +138 -47
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +246 -60
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +1346 -622
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/registry.js +3 -2
- package/dist/src/roles.d.ts +70 -0
- package/dist/src/roles.js +326 -0
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/settings.d.ts +26 -0
- package/dist/src/settings.js +277 -0
- package/dist/src/trajectory.d.ts +39 -4
- package/dist/src/trajectory.js +82 -5
- package/dist/src/types.d.ts +30 -3
- package/dist/src/types.js +1 -1
- package/dist/src/utils.d.ts +21 -1
- package/dist/src/utils.js +40 -0
- package/dist/src/validation.d.ts +10 -17
- package/dist/src/validation.js +29 -458
- package/dist/starter/index.js +0 -2
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +0 -2
- package/dist/subagents/index.js +1344 -798
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +49 -29
- package/dist/subagents/src/contracts.js +18 -7
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +15 -7
- package/dist/subagents/src/manager.js +32 -13
- package/dist/subagents/src/navigator.js +396 -247
- package/dist/subagents/src/view.d.ts +24 -2
- package/dist/subagents/src/view.js +107 -80
- package/dist/trajectory/assets/index.html +370 -232
- package/dist/trajectory/assets/prism.min.js +13 -0
- package/dist/trajectory/index.js +445 -277
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +370 -232
- package/dist/trajectory/src/assets/prism.min.js +13 -0
- package/dist/trajectory/src/export.js +4 -2
- package/dist/trajectory/src/index.d.ts +2 -0
- package/dist/trajectory/src/index.js +151 -60
- package/dist/trajectory/src/server.js +138 -11
- package/dist/trajectory/src/server.js.map +2 -2
- package/package.json +7 -6
- package/skills/pi-extensible-workflows/SKILL.md +2 -0
- package/src/agent-execution.ts +110 -80
- package/src/background-widget.ts +26 -19
- package/src/decoders.ts +22 -1
- package/src/execution.ts +41 -4
- package/src/host-delivery.ts +1 -0
- package/src/host-navigator.ts +58 -14
- package/src/host-recovery.ts +8 -7
- package/src/host-view.ts +116 -42
- package/src/host.ts +223 -55
- package/src/index.ts +3 -1
- package/src/paths.ts +3 -0
- package/src/registry.ts +3 -2
- package/src/roles.ts +327 -0
- package/src/script-tools.ts +76 -0
- package/src/settings.ts +224 -0
- package/src/trajectory.ts +97 -9
- package/src/types.ts +12 -5
- package/src/utils.ts +36 -1
- package/src/validation.ts +30 -356
- package/starter/review-loop.ts +0 -2
- package/subagents/README.md +3 -3
- package/subagents/src/contracts.ts +22 -7
- package/subagents/src/index.ts +18 -7
- package/subagents/src/manager.ts +31 -13
- package/subagents/src/navigator.ts +336 -180
- package/subagents/src/view.ts +108 -76
- package/trajectory/src/assets/index.html +370 -232
- package/trajectory/src/assets/prism.min.js +13 -0
- package/trajectory/src/export.ts +4 -2
- package/trajectory/src/index.ts +124 -47
- package/trajectory/src/server.ts +114 -14
package/dist/subagents/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// subagents/src/index.ts
|
|
2
|
-
import { join as
|
|
3
|
-
import { defineTool as defineTool2, getAgentDir as
|
|
2
|
+
import { join as join11 } from "node:path";
|
|
3
|
+
import { defineTool as defineTool2, getAgentDir as getAgentDir8 } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Value as Value3 } from "typebox/value";
|
|
5
5
|
|
|
6
6
|
// src/types.ts
|
|
@@ -28,6 +28,7 @@ var ERROR_CODES = [
|
|
|
28
28
|
"AGENT_FAILED",
|
|
29
29
|
"AGENT_RESULT_COLLECTED",
|
|
30
30
|
"RESULT_INVALID",
|
|
31
|
+
"TOOL_FAILED",
|
|
31
32
|
"CANCELLED",
|
|
32
33
|
"WORKER_UNRESPONSIVE",
|
|
33
34
|
"WORKTREE_FAILED",
|
|
@@ -284,6 +285,16 @@ function resolveModelReference(value, aliases = {}, knownModels, settingsPath) {
|
|
|
284
285
|
};
|
|
285
286
|
return resolveReference(value, []);
|
|
286
287
|
}
|
|
288
|
+
function reachableTools(pi) {
|
|
289
|
+
const undeclared = pi.getAllTools?.().filter(({ exposure }) => exposure === "codemode" || exposure === "deferred").map(({ name }) => name) ?? [];
|
|
290
|
+
return [.../* @__PURE__ */ new Set([...pi.getActiveTools(), ...undeclared])];
|
|
291
|
+
}
|
|
292
|
+
var VIRTUAL_MODEL_PROVIDER = "workflow";
|
|
293
|
+
function physicalModel(spec, aliases = {}, knownModels, settingsPath) {
|
|
294
|
+
if (spec.provider !== VIRTUAL_MODEL_PROVIDER || !modelAliasName(spec.model, aliases)) return spec;
|
|
295
|
+
const target = resolveModelReference(spec.model, aliases, knownModels, settingsPath);
|
|
296
|
+
return spec.thinking === void 0 ? target : { ...target, thinking: spec.thinking };
|
|
297
|
+
}
|
|
287
298
|
function modelCapability(value, aliases, knownModels, settingsPath) {
|
|
288
299
|
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
289
300
|
return `${parsed.provider}/${parsed.model}`;
|
|
@@ -328,6 +339,12 @@ function unmatchedResourcePatterns(patterns, resources) {
|
|
|
328
339
|
function loadLaunchSnapshot(input) {
|
|
329
340
|
return deepFreeze(structuredClone(input));
|
|
330
341
|
}
|
|
342
|
+
function validateSchema(schema, at = "schema") {
|
|
343
|
+
if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema)) fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
|
|
344
|
+
if (typeof schema.type !== "string" && !Array.isArray(schema.type) && schema.$ref === void 0 && schema.anyOf === void 0 && schema.oneOf === void 0 && schema.allOf === void 0 && schema.const === void 0 && schema.enum === void 0) fail("INVALID_SCHEMA", `${at} has no JSON Schema shape`);
|
|
345
|
+
if (schema.required !== void 0 && (!Array.isArray(schema.required) || schema.required.some((key) => typeof key !== "string"))) fail("INVALID_SCHEMA", `${at}.required must be an array of strings`);
|
|
346
|
+
if (schema.properties !== void 0 && !object(schema.properties)) fail("INVALID_SCHEMA", `${at}.properties must be an object`);
|
|
347
|
+
}
|
|
331
348
|
|
|
332
349
|
// src/paths.ts
|
|
333
350
|
import { createHash } from "node:crypto";
|
|
@@ -345,6 +362,9 @@ function canonicalPath(path) {
|
|
|
345
362
|
return absolute;
|
|
346
363
|
}
|
|
347
364
|
}
|
|
365
|
+
function extensionIdentity(path) {
|
|
366
|
+
return path.startsWith("builtin:") ? path : canonicalPath(path);
|
|
367
|
+
}
|
|
348
368
|
function sameFilesystemPath(left, right) {
|
|
349
369
|
return canonicalPath(left) === canonicalPath(right);
|
|
350
370
|
}
|
|
@@ -372,6 +392,20 @@ function budgetUsage(value) {
|
|
|
372
392
|
return { tokens: value?.tokens ?? 0, costUsd: value?.costUsd ?? 0, durationMs: value?.durationMs ?? 0, agentLaunches: value?.agentLaunches ?? 0 };
|
|
373
393
|
}
|
|
374
394
|
|
|
395
|
+
// src/validation.ts
|
|
396
|
+
import * as acorn from "acorn";
|
|
397
|
+
|
|
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
|
+
|
|
375
409
|
// src/io.ts
|
|
376
410
|
import { execFile } from "node:child_process";
|
|
377
411
|
import { randomUUID } from "node:crypto";
|
|
@@ -392,7 +426,7 @@ async function renameWithRetry(from, to) {
|
|
|
392
426
|
} catch (error) {
|
|
393
427
|
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
394
428
|
if (!retryableRenameError(error) || delayMs === void 0) throw error;
|
|
395
|
-
await new Promise((
|
|
429
|
+
await new Promise((resolve6) => setTimeout(resolve6, delayMs));
|
|
396
430
|
}
|
|
397
431
|
}
|
|
398
432
|
}
|
|
@@ -593,7 +627,7 @@ function decodeWorkflowExtensions(value) {
|
|
|
593
627
|
}
|
|
594
628
|
if (namespace === "trajectory") {
|
|
595
629
|
if (!object(raw) || Object.keys(raw).some((key) => key !== "port" && key !== "themes") || raw.port !== void 0 && (typeof raw.port !== "number" || !Number.isSafeInteger(raw.port) || raw.port < 1 || raw.port > 65535) || raw.themes !== void 0 && typeof raw.themes !== "boolean") return void 0;
|
|
596
|
-
normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port }
|
|
630
|
+
normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port } };
|
|
597
631
|
continue;
|
|
598
632
|
}
|
|
599
633
|
if (!jsonValue(raw)) return void 0;
|
|
@@ -775,9 +809,9 @@ function decodeAgentAttempt(value) {
|
|
|
775
809
|
const session = value.session === void 0 ? void 0 : decodeAgentSession(value.session);
|
|
776
810
|
const setup = decodeAgentSetupSummary(value.setup);
|
|
777
811
|
const error = value.error === void 0 ? void 0 : decodeAttemptError(value.error);
|
|
778
|
-
const
|
|
779
|
-
if (!setup || !
|
|
780
|
-
return { attempt: value.attempt, transport: value.transport, setup, accounting:
|
|
812
|
+
const accounting2 = decodeAgentAccounting(value.accounting);
|
|
813
|
+
if (!setup || !accounting2 || value.session !== void 0 && !session || value.error !== void 0 && !error) return void 0;
|
|
814
|
+
return { attempt: value.attempt, transport: value.transport, setup, accounting: accounting2, ...session === void 0 ? {} : { session }, ...error === void 0 ? {} : { error } };
|
|
781
815
|
}
|
|
782
816
|
function decodeAgent(value) {
|
|
783
817
|
if (!object(value) || typeof value.id !== "string" || typeof value.name !== "string" || typeof value.path !== "string" || !isAgentState(value.state) || !integer(value.attempts)) return void 0;
|
|
@@ -798,12 +832,12 @@ function decodeAgent(value) {
|
|
|
798
832
|
const startedAt = optionalNumber(value.startedAt);
|
|
799
833
|
const durationMs = optionalNumber(value.durationMs);
|
|
800
834
|
const attemptDetails = value.attemptDetails === void 0 ? void 0 : decodeArray(value.attemptDetails, decodeAgentAttempt);
|
|
801
|
-
const
|
|
835
|
+
const accounting2 = value.accounting === void 0 ? void 0 : decodeAgentAccounting(value.accounting);
|
|
802
836
|
const toolCalls = value.toolCalls === void 0 ? void 0 : decodeArray(value.toolCalls, decodeAgentToolCall);
|
|
803
|
-
const
|
|
837
|
+
const activity = value.activity === void 0 ? void 0 : decodeAgentActivity(value.activity);
|
|
804
838
|
const lastEventAt = optionalNumber(value.lastEventAt);
|
|
805
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 || role === 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;
|
|
806
|
-
if (!model || !tools || value.structuralPath !== void 0 && !structuralPath2 || value.attemptDetails !== void 0 && !attemptDetails || value.accounting !== void 0 && !
|
|
840
|
+
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;
|
|
807
841
|
return {
|
|
808
842
|
...systemPrompt === void 0 ? {} : { systemPrompt },
|
|
809
843
|
...prompt === void 0 ? {} : { prompt },
|
|
@@ -827,9 +861,9 @@ function decodeAgent(value) {
|
|
|
827
861
|
...startedAt === void 0 ? {} : { startedAt },
|
|
828
862
|
...durationMs === void 0 ? {} : { durationMs },
|
|
829
863
|
...attemptDetails === void 0 ? {} : { attemptDetails },
|
|
830
|
-
...
|
|
864
|
+
...accounting2 === void 0 ? {} : { accounting: accounting2 },
|
|
831
865
|
...toolCalls === void 0 ? {} : { toolCalls },
|
|
832
|
-
...
|
|
866
|
+
...activity === void 0 ? {} : { activity },
|
|
833
867
|
...lastEventAt === void 0 ? {} : { lastEventAt }
|
|
834
868
|
};
|
|
835
869
|
}
|
|
@@ -873,9 +907,25 @@ function decodeShellActivity(value) {
|
|
|
873
907
|
}
|
|
874
908
|
function decodeRunEvent(value) {
|
|
875
909
|
if (!object(value) || typeof value.type !== "string" || typeof value.message !== "string") return void 0;
|
|
876
|
-
const
|
|
877
|
-
if (
|
|
878
|
-
return { type: value.type, message: value.message, ...
|
|
910
|
+
const timestamp2 = optionalNumber(value.timestamp);
|
|
911
|
+
if (timestamp2 === INVALID_PERSISTED_VALUE) return void 0;
|
|
912
|
+
return { type: value.type, message: value.message, ...timestamp2 === void 0 ? {} : { timestamp: timestamp2 } };
|
|
913
|
+
}
|
|
914
|
+
function decodeScriptCall(value) {
|
|
915
|
+
if (!object(value) || value.kind !== "shell" && value.kind !== "tool" || typeof value.name !== "string" || typeof value.path !== "string" || !finiteNumber(value.startedAt)) return void 0;
|
|
916
|
+
const shell = value.kind === "shell";
|
|
917
|
+
if (shell && !jsonValue(value.input)) return void 0;
|
|
918
|
+
const inputBytes = shell ? optionalNumber(value.inputBytes) : void 0;
|
|
919
|
+
const phase = optionalString(value.phase);
|
|
920
|
+
const finishedAt = optionalNumber(value.finishedAt);
|
|
921
|
+
let error;
|
|
922
|
+
if (value.error !== void 0) {
|
|
923
|
+
if (shell) error = decodeWorkflowError(value.error);
|
|
924
|
+
else if (object(value.error) && typeof value.error.code === "string") error = { code: isWorkflowErrorCode(value.error.code) ? value.error.code : "INTERNAL_ERROR" };
|
|
925
|
+
if (!error) return void 0;
|
|
926
|
+
}
|
|
927
|
+
if (inputBytes === INVALID_PERSISTED_VALUE || phase === INVALID_PERSISTED_VALUE || finishedAt === INVALID_PERSISTED_VALUE) return void 0;
|
|
928
|
+
return { kind: value.kind, name: value.name, path: value.path, ...shell && jsonValue(value.input) ? { input: value.input } : {}, ...inputBytes === void 0 ? {} : { inputBytes }, ...phase === void 0 ? {} : { phase }, startedAt: value.startedAt, ...finishedAt === void 0 ? {} : { finishedAt }, ...error === void 0 ? {} : { error } };
|
|
879
929
|
}
|
|
880
930
|
function decodeDelivery(value) {
|
|
881
931
|
if (!object(value)) return void 0;
|
|
@@ -906,6 +956,8 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
906
956
|
const budgetEvents = value.budgetEvents === void 0 ? void 0 : decodeArray(value.budgetEvents, decodeBudgetEvent);
|
|
907
957
|
const events = value.events === void 0 ? void 0 : decodeArray(value.events, decodeRunEvent);
|
|
908
958
|
const delivery = value.delivery === void 0 ? void 0 : decodeDelivery(value.delivery);
|
|
959
|
+
const scriptCalls = value.scriptCalls === void 0 ? void 0 : decodeArray(value.scriptCalls, decodeScriptCall);
|
|
960
|
+
if (value.scriptCalls !== void 0 && !scriptCalls) return void 0;
|
|
909
961
|
if (!agentSessions || !agents || parentRunId === INVALID_PERSISTED_VALUE || value.retry !== void 0 && !retry || phase === INVALID_PERSISTED_VALUE || value.phaseHistory !== void 0 && !phaseHistory || phaseHistoryIndex === INVALID_PERSISTED_VALUE || activeShells === INVALID_PERSISTED_VALUE || activeShellStartedAt === INVALID_PERSISTED_VALUE || value.activeShellsByPhase !== void 0 && !activeShellsByPhase || value.error !== void 0 && !error || failedAt === INVALID_PERSISTED_VALUE || value.budget !== void 0 && !budget || budgetVersion === INVALID_PERSISTED_VALUE || value.budgetVersion !== void 0 && !integer(budgetVersion) || value.usage !== void 0 && !usage || value.budgetEvents !== void 0 && !budgetEvents || value.events !== void 0 && !events || value.delivery !== void 0 && !delivery) return void 0;
|
|
910
962
|
if (value.agentSessions === void 0 && !allowLegacyAgentSessions) return void 0;
|
|
911
963
|
return {
|
|
@@ -924,6 +976,7 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
924
976
|
...activeShells === void 0 ? {} : { activeShells },
|
|
925
977
|
...activeShellStartedAt === void 0 ? {} : { activeShellStartedAt },
|
|
926
978
|
...activeShellsByPhase === void 0 ? {} : { activeShellsByPhase },
|
|
979
|
+
...scriptCalls === void 0 ? {} : { scriptCalls },
|
|
927
980
|
...error === void 0 ? {} : { error },
|
|
928
981
|
...failedAt === void 0 ? {} : { failedAt },
|
|
929
982
|
...budget === void 0 ? {} : { budget },
|
|
@@ -1920,20 +1973,236 @@ var RunStore = class _RunStore {
|
|
|
1920
1973
|
}
|
|
1921
1974
|
};
|
|
1922
1975
|
|
|
1923
|
-
// src/
|
|
1924
|
-
import { mkdirSync, readFileSync
|
|
1976
|
+
// src/settings.ts
|
|
1977
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
1925
1978
|
import { homedir as homedir3 } from "node:os";
|
|
1926
|
-
import {
|
|
1927
|
-
import { fileURLToPath
|
|
1928
|
-
import
|
|
1929
|
-
|
|
1979
|
+
import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
|
|
1980
|
+
import { fileURLToPath } from "node:url";
|
|
1981
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
1982
|
+
var ROLE_DIRECTORY = "pi-extensible-workflows";
|
|
1983
|
+
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
1984
|
+
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
1985
|
+
return join3(agentDir, ROLE_DIRECTORY, "settings.json");
|
|
1986
|
+
}
|
|
1987
|
+
function normalizedResourcePath(value, settingsPath) {
|
|
1988
|
+
if (value === "*" || value.startsWith("builtin:")) return value;
|
|
1989
|
+
let expanded = value === "~" ? homedir3() : value.startsWith("~/") || value.startsWith("~\\") ? join3(homedir3(), value.slice(2)) : value;
|
|
1990
|
+
if (expanded.startsWith("file://")) expanded = fileURLToPath(expanded);
|
|
1991
|
+
const resolved = resolve3(dirname2(settingsPath), expanded);
|
|
1992
|
+
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
1993
|
+
if (resourcePatternHasMagic(expanded)) {
|
|
1994
|
+
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
1995
|
+
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
1996
|
+
const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
|
|
1997
|
+
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
1998
|
+
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
1999
|
+
return `${canonicalPath(prefix)}${suffix}`;
|
|
2000
|
+
}
|
|
2001
|
+
return canonicalPath(resolved);
|
|
2002
|
+
}
|
|
2003
|
+
function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
2004
|
+
if (value === void 0) return void 0;
|
|
2005
|
+
if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
|
|
2006
|
+
const normalized = [];
|
|
2007
|
+
for (const [index, entry] of value.entries()) {
|
|
2008
|
+
if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
2009
|
+
let selector = entry.trim();
|
|
2010
|
+
if (kind === "extensions" && normalizeExtensions) {
|
|
2011
|
+
const negated = selector.startsWith("!");
|
|
2012
|
+
const body = negated ? selector.slice(1) : selector;
|
|
2013
|
+
if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
2014
|
+
try {
|
|
2015
|
+
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
2016
|
+
} catch (error) {
|
|
2017
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
try {
|
|
2021
|
+
validateResourcePattern(selector);
|
|
2022
|
+
} catch (error) {
|
|
2023
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
2024
|
+
}
|
|
2025
|
+
normalized.push(selector);
|
|
2026
|
+
}
|
|
2027
|
+
return Object.freeze(normalized);
|
|
2028
|
+
}
|
|
2029
|
+
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
2030
|
+
if (value === void 0) return void 0;
|
|
2031
|
+
if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
2032
|
+
return Object.freeze(value.map((entry, index) => {
|
|
2033
|
+
if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
2034
|
+
return entry;
|
|
2035
|
+
}));
|
|
2036
|
+
}
|
|
2037
|
+
function selectorsFromSettings(settings) {
|
|
2038
|
+
return {
|
|
2039
|
+
...settings.skills === void 0 ? {} : { skills: settings.skills },
|
|
2040
|
+
...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
|
|
2041
|
+
...settings.tools === void 0 ? {} : { tools: settings.tools }
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
function selectorSet(value) {
|
|
2045
|
+
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2046
|
+
}
|
|
2047
|
+
function validateContextFileScopes(value, rolePath) {
|
|
2048
|
+
if (value === void 0) return void 0;
|
|
2049
|
+
if (!Array.isArray(value) || !value.every(isContextFileScope)) fail("INVALID_METADATA", `${rolePath}.contextFiles must be an array containing only global, project, or cwd`);
|
|
2050
|
+
return [...value];
|
|
2051
|
+
}
|
|
2052
|
+
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS") {
|
|
2053
|
+
if (value === void 0) return void 0;
|
|
2054
|
+
const base = `${settingsPath}.extensionSettings`;
|
|
2055
|
+
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2056
|
+
const normalized = {};
|
|
2057
|
+
for (const [namespace, raw] of Object.entries(value)) {
|
|
2058
|
+
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2059
|
+
if (namespace === "herdr") {
|
|
2060
|
+
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2061
|
+
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
2062
|
+
if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
2063
|
+
normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
|
|
2064
|
+
continue;
|
|
2065
|
+
}
|
|
2066
|
+
if (namespace === "trajectory") {
|
|
2067
|
+
if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
|
|
2068
|
+
if (Object.keys(raw).some((key) => key !== "port")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
|
|
2069
|
+
if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
2070
|
+
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port } });
|
|
2071
|
+
continue;
|
|
2072
|
+
}
|
|
2073
|
+
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2074
|
+
normalized[namespace] = structuredClone(raw);
|
|
2075
|
+
}
|
|
2076
|
+
return deepFreeze(normalized);
|
|
2077
|
+
}
|
|
2078
|
+
function positiveRetentionInteger(value) {
|
|
2079
|
+
return positiveInteger(value) && Number.isSafeInteger(value);
|
|
2080
|
+
}
|
|
2081
|
+
function validateRetention(value, settingsPath) {
|
|
2082
|
+
if (value === void 0) return void 0;
|
|
2083
|
+
const base = `${settingsPath}.retention`;
|
|
2084
|
+
if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
2085
|
+
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
2086
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
2087
|
+
if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
2088
|
+
if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
2089
|
+
return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
|
|
2090
|
+
}
|
|
2091
|
+
function parseSettings(path, partial) {
|
|
2092
|
+
let parsed;
|
|
2093
|
+
try {
|
|
2094
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
2095
|
+
} catch (error) {
|
|
2096
|
+
if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
2097
|
+
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2098
|
+
}
|
|
2099
|
+
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2100
|
+
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget", "codemodeTools"]]);
|
|
2101
|
+
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2102
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2103
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
2104
|
+
const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
2105
|
+
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2106
|
+
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2107
|
+
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2108
|
+
const codemodeTools = parsed.codemodeTools;
|
|
2109
|
+
if (codemodeTools !== void 0 && codemodeTools !== "all" && codemodeTools !== "read-only" && codemodeTools !== "none") fail("INVALID_SETTINGS", `${path}.codemodeTools must be "all", "read-only", or "none"`);
|
|
2110
|
+
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2111
|
+
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2112
|
+
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
2113
|
+
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
2114
|
+
const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS");
|
|
2115
|
+
const retention = validateRetention(parsed.retention, path);
|
|
2116
|
+
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
2117
|
+
return Object.freeze({
|
|
2118
|
+
...concurrency === void 0 ? {} : { concurrency },
|
|
2119
|
+
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2120
|
+
...codemodeTools === void 0 ? {} : { codemodeTools },
|
|
2121
|
+
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2122
|
+
...skills === void 0 ? {} : { skills },
|
|
2123
|
+
...extensions === void 0 ? {} : { extensions },
|
|
2124
|
+
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2125
|
+
...tools === void 0 ? {} : { tools },
|
|
2126
|
+
...retention === void 0 ? {} : { retention },
|
|
2127
|
+
...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
function loadSettings(path = workflowSettingsPath()) {
|
|
2131
|
+
return parseSettings(path, false);
|
|
2132
|
+
}
|
|
2133
|
+
var READ_ONLY_WORKFLOW_TOOLS = /* @__PURE__ */ new Set(["workflow_status", "workflow_catalog", "subagents_inspect"]);
|
|
2134
|
+
function workflowToolExposure(name, codemodeTools) {
|
|
2135
|
+
const readOnly = READ_ONLY_WORKFLOW_TOOLS.has(name);
|
|
2136
|
+
return { ...codemodeTools === "none" || codemodeTools === "read-only" && !readOnly ? { exposure: "model-only" } : {}, ...readOnly ? { annotations: { readOnlyHint: true } } : {} };
|
|
2137
|
+
}
|
|
2138
|
+
function loadCodemodeToolsSetting(agentDir) {
|
|
2139
|
+
try {
|
|
2140
|
+
return loadSettings(workflowSettingsPath(agentDir)).codemodeTools;
|
|
2141
|
+
} catch {
|
|
2142
|
+
return void 0;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function loadSettingsOverrides(path) {
|
|
2146
|
+
return parseSettings(path, true);
|
|
2147
|
+
}
|
|
2148
|
+
function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2149
|
+
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2150
|
+
const global = loadSettings(globalSettingsPath);
|
|
2151
|
+
const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2152
|
+
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
2153
|
+
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
2154
|
+
const globalSelectors = selectorsFromSettings(global);
|
|
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
|
+
};
|
|
2173
|
+
const effective = Object.freeze({
|
|
2174
|
+
concurrency: project.concurrency ?? global.concurrency,
|
|
2175
|
+
backgroundWidget: global.backgroundWidget ?? true,
|
|
2176
|
+
...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
|
|
2177
|
+
...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
|
|
2178
|
+
...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
|
|
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 }
|
|
2183
|
+
});
|
|
2184
|
+
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
|
|
2185
|
+
}
|
|
2186
|
+
function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
2187
|
+
for (const name of names) {
|
|
2188
|
+
try {
|
|
2189
|
+
const target = modelCapability(name, aliases, knownModels, settingsPath);
|
|
2190
|
+
if (!availableModels.has(target)) unknownModel(name, target, settingsPath);
|
|
2191
|
+
} catch (error) {
|
|
2192
|
+
throw annotateModelAliasError(error, name);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2197
|
+
const resolved = resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath);
|
|
2198
|
+
const global = selectorSet(selectorsFromSettings(resolved.global));
|
|
2199
|
+
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
2200
|
+
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
2201
|
+
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: selectorsFromSettings(resolved.global), project: selectorsFromSettings(resolved.project) } };
|
|
2202
|
+
}
|
|
1930
2203
|
|
|
1931
2204
|
// src/registry.ts
|
|
1932
|
-
|
|
1933
|
-
import { basename as basename2, dirname as dirname2, isAbsolute, join as join3 } from "node:path";
|
|
1934
|
-
import { fileURLToPath } from "node:url";
|
|
1935
|
-
import { Value } from "typebox/value";
|
|
1936
|
-
var RESERVED_GLOBALS = /* @__PURE__ */ new Set(["agent", "shell", "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"]);
|
|
2205
|
+
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"]);
|
|
1937
2206
|
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
1938
2207
|
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
1939
2208
|
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
@@ -1941,10 +2210,10 @@ function normalizeRoleDirectory(value) {
|
|
|
1941
2210
|
try {
|
|
1942
2211
|
if (value instanceof URL) {
|
|
1943
2212
|
if (value.protocol !== "file:") fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
1944
|
-
return
|
|
2213
|
+
return fileURLToPath2(value);
|
|
1945
2214
|
}
|
|
1946
2215
|
if (typeof value === "string") {
|
|
1947
|
-
if (value.startsWith("file:")) return
|
|
2216
|
+
if (value.startsWith("file:")) return fileURLToPath2(value);
|
|
1948
2217
|
if (isAbsolute(value)) return value;
|
|
1949
2218
|
}
|
|
1950
2219
|
} catch (error) {
|
|
@@ -1954,12 +2223,12 @@ function normalizeRoleDirectory(value) {
|
|
|
1954
2223
|
}
|
|
1955
2224
|
function isBuiltinRoleDirectory(path) {
|
|
1956
2225
|
const canonical = canonicalPath(path);
|
|
1957
|
-
const starterDirectory =
|
|
2226
|
+
const starterDirectory = dirname3(canonical);
|
|
1958
2227
|
if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
|
|
1959
|
-
const distributionDirectory =
|
|
1960
|
-
const packageRoot2 = basename2(distributionDirectory) === "dist" ?
|
|
2228
|
+
const distributionDirectory = dirname3(starterDirectory);
|
|
2229
|
+
const packageRoot2 = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
|
|
1961
2230
|
try {
|
|
1962
|
-
const metadata = JSON.parse(
|
|
2231
|
+
const metadata = JSON.parse(readFileSync2(join4(packageRoot2, "package.json"), "utf8"));
|
|
1963
2232
|
return object(metadata) && isCorePackageName(metadata.name);
|
|
1964
2233
|
} catch {
|
|
1965
2234
|
return false;
|
|
@@ -2240,213 +2509,9 @@ function registeredWorkflowRoleDirectoryRegistrations() {
|
|
|
2240
2509
|
return typeof registrations === "function" ? registrations() : [];
|
|
2241
2510
|
}
|
|
2242
2511
|
|
|
2243
|
-
// src/
|
|
2244
|
-
var
|
|
2245
|
-
|
|
2246
|
-
return join4(agentDir, ROLE_DIRECTORY, "settings.json");
|
|
2247
|
-
}
|
|
2248
|
-
function normalizedResourcePath(value, settingsPath) {
|
|
2249
|
-
if (value === "*") return value;
|
|
2250
|
-
let expanded = value === "~" ? homedir3() : value.startsWith("~/") || value.startsWith("~\\") ? join4(homedir3(), value.slice(2)) : value;
|
|
2251
|
-
if (expanded.startsWith("file://")) expanded = fileURLToPath2(expanded);
|
|
2252
|
-
const resolved = resolve3(dirname3(settingsPath), expanded);
|
|
2253
|
-
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
2254
|
-
if (resourcePatternHasMagic(expanded)) {
|
|
2255
|
-
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
2256
|
-
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
2257
|
-
const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
|
|
2258
|
-
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
2259
|
-
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
2260
|
-
return `${canonicalPath(prefix)}${suffix}`;
|
|
2261
|
-
}
|
|
2262
|
-
return canonicalPath(resolved);
|
|
2263
|
-
}
|
|
2264
|
-
function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
2265
|
-
if (value === void 0) return void 0;
|
|
2266
|
-
if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
|
|
2267
|
-
const normalized = [];
|
|
2268
|
-
for (const [index, entry] of value.entries()) {
|
|
2269
|
-
if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
2270
|
-
let selector = entry.trim();
|
|
2271
|
-
if (kind === "extensions" && normalizeExtensions) {
|
|
2272
|
-
const negated = selector.startsWith("!");
|
|
2273
|
-
const body = negated ? selector.slice(1) : selector;
|
|
2274
|
-
if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
2275
|
-
try {
|
|
2276
|
-
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
2277
|
-
} catch (error) {
|
|
2278
|
-
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
try {
|
|
2282
|
-
validateResourcePattern(selector);
|
|
2283
|
-
} catch (error) {
|
|
2284
|
-
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
2285
|
-
}
|
|
2286
|
-
normalized.push(selector);
|
|
2287
|
-
}
|
|
2288
|
-
return Object.freeze(normalized);
|
|
2289
|
-
}
|
|
2290
|
-
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
2291
|
-
if (value === void 0) return void 0;
|
|
2292
|
-
if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
2293
|
-
return Object.freeze(value.map((entry, index) => {
|
|
2294
|
-
if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
2295
|
-
return entry;
|
|
2296
|
-
}));
|
|
2297
|
-
}
|
|
2298
|
-
function selectorsFromSettings(settings) {
|
|
2299
|
-
return {
|
|
2300
|
-
...settings.skills === void 0 ? {} : { skills: settings.skills },
|
|
2301
|
-
...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
|
|
2302
|
-
...settings.tools === void 0 ? {} : { tools: settings.tools }
|
|
2303
|
-
};
|
|
2304
|
-
}
|
|
2305
|
-
function selectorSet(value) {
|
|
2306
|
-
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2307
|
-
}
|
|
2308
|
-
function validateContextFileScopes(value, rolePath) {
|
|
2309
|
-
if (value === void 0) return void 0;
|
|
2310
|
-
if (!Array.isArray(value) || !value.every(isContextFileScope)) fail("INVALID_METADATA", `${rolePath}.contextFiles must be an array containing only global, project, or cwd`);
|
|
2311
|
-
return [...value];
|
|
2312
|
-
}
|
|
2313
|
-
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS", applyDefaults = true) {
|
|
2314
|
-
if (value === void 0) return void 0;
|
|
2315
|
-
const base = `${settingsPath}.extensionSettings`;
|
|
2316
|
-
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2317
|
-
const normalized = {};
|
|
2318
|
-
for (const [namespace, raw] of Object.entries(value)) {
|
|
2319
|
-
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2320
|
-
if (namespace === "herdr") {
|
|
2321
|
-
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2322
|
-
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
2323
|
-
if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
2324
|
-
normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
|
|
2325
|
-
continue;
|
|
2326
|
-
}
|
|
2327
|
-
if (namespace === "trajectory") {
|
|
2328
|
-
if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
|
|
2329
|
-
if (Object.keys(raw).some((key) => key !== "port" && key !== "themes")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
|
|
2330
|
-
if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
2331
|
-
if (raw.themes !== void 0 && typeof raw.themes !== "boolean") fail(errorCode2, `${base}.trajectory.themes must be a boolean`);
|
|
2332
|
-
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port }, ...raw.themes === void 0 ? applyDefaults ? { themes: false } : {} : { themes: raw.themes } });
|
|
2333
|
-
continue;
|
|
2334
|
-
}
|
|
2335
|
-
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2336
|
-
normalized[namespace] = structuredClone(raw);
|
|
2337
|
-
}
|
|
2338
|
-
return deepFreeze(normalized);
|
|
2339
|
-
}
|
|
2340
|
-
function positiveRetentionInteger(value) {
|
|
2341
|
-
return positiveInteger(value) && Number.isSafeInteger(value);
|
|
2342
|
-
}
|
|
2343
|
-
function validateRetention(value, settingsPath) {
|
|
2344
|
-
if (value === void 0) return void 0;
|
|
2345
|
-
const base = `${settingsPath}.retention`;
|
|
2346
|
-
if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
2347
|
-
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
2348
|
-
if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
2349
|
-
if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
2350
|
-
if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
2351
|
-
return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
|
|
2352
|
-
}
|
|
2353
|
-
function parseSettings(path, partial) {
|
|
2354
|
-
let parsed;
|
|
2355
|
-
try {
|
|
2356
|
-
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
2357
|
-
} catch (error) {
|
|
2358
|
-
if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
2359
|
-
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2360
|
-
}
|
|
2361
|
-
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2362
|
-
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget"]]);
|
|
2363
|
-
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2364
|
-
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2365
|
-
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
2366
|
-
const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
2367
|
-
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2368
|
-
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2369
|
-
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2370
|
-
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2371
|
-
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2372
|
-
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
2373
|
-
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
2374
|
-
const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS", !partial);
|
|
2375
|
-
const retention = validateRetention(parsed.retention, path);
|
|
2376
|
-
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
2377
|
-
return Object.freeze({
|
|
2378
|
-
...concurrency === void 0 ? {} : { concurrency },
|
|
2379
|
-
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2380
|
-
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2381
|
-
...skills === void 0 ? {} : { skills },
|
|
2382
|
-
...extensions === void 0 ? {} : { extensions },
|
|
2383
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2384
|
-
...tools === void 0 ? {} : { tools },
|
|
2385
|
-
...retention === void 0 ? {} : { retention },
|
|
2386
|
-
...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
|
|
2387
|
-
});
|
|
2388
|
-
}
|
|
2389
|
-
function loadSettings(path = workflowSettingsPath()) {
|
|
2390
|
-
return parseSettings(path, false);
|
|
2391
|
-
}
|
|
2392
|
-
function loadSettingsOverrides(path) {
|
|
2393
|
-
return parseSettings(path, true);
|
|
2394
|
-
}
|
|
2395
|
-
function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2396
|
-
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2397
|
-
const global = loadSettings(globalSettingsPath);
|
|
2398
|
-
const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2399
|
-
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
2400
|
-
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
2401
|
-
const globalSelectors = selectorsFromSettings(global);
|
|
2402
|
-
const projectSelectors = selectorsFromSettings(project);
|
|
2403
|
-
const effectiveSelectors = selectorSet({
|
|
2404
|
-
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2405
|
-
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2406
|
-
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2407
|
-
});
|
|
2408
|
-
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2409
|
-
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2410
|
-
const sources = {
|
|
2411
|
-
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2412
|
-
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2413
|
-
skills: sourceFor("skills"),
|
|
2414
|
-
extensions: sourceFor("extensions"),
|
|
2415
|
-
tools: sourceFor("tools"),
|
|
2416
|
-
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2417
|
-
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2418
|
-
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2419
|
-
};
|
|
2420
|
-
const effective = Object.freeze({
|
|
2421
|
-
concurrency: project.concurrency ?? global.concurrency,
|
|
2422
|
-
backgroundWidget: global.backgroundWidget ?? true,
|
|
2423
|
-
...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
|
|
2424
|
-
...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
|
|
2425
|
-
...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
|
|
2426
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2427
|
-
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2428
|
-
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2429
|
-
...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
|
|
2430
|
-
});
|
|
2431
|
-
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
|
|
2432
|
-
}
|
|
2433
|
-
function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
2434
|
-
for (const name of names) {
|
|
2435
|
-
try {
|
|
2436
|
-
const target = modelCapability(name, aliases, knownModels, settingsPath);
|
|
2437
|
-
if (!availableModels.has(target)) unknownModel(name, target, settingsPath);
|
|
2438
|
-
} catch (error) {
|
|
2439
|
-
throw annotateModelAliasError(error, name);
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2444
|
-
const resolved = resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath);
|
|
2445
|
-
const global = selectorSet(selectorsFromSettings(resolved.global));
|
|
2446
|
-
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
2447
|
-
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
2448
|
-
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: selectorsFromSettings(resolved.global), project: selectorsFromSettings(resolved.project) } };
|
|
2449
|
-
}
|
|
2512
|
+
// src/roles.ts
|
|
2513
|
+
var ROLE_DIRECTORY2 = "pi-extensible-workflows";
|
|
2514
|
+
var roleDefinitionPaths = /* @__PURE__ */ new WeakMap();
|
|
2450
2515
|
function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
2451
2516
|
if (!strict) {
|
|
2452
2517
|
if (!content.startsWith("---\n")) return { prompt: content };
|
|
@@ -2473,7 +2538,7 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
2473
2538
|
}
|
|
2474
2539
|
}
|
|
2475
2540
|
const rolePathValue2 = rolePath ?? "<role>";
|
|
2476
|
-
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA"
|
|
2541
|
+
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA");
|
|
2477
2542
|
const definition2 = { prompt: content.slice(end + 4).replace(/^\n/, "") };
|
|
2478
2543
|
if (meta.model) {
|
|
2479
2544
|
const model2 = unquote(meta.model);
|
|
@@ -2513,7 +2578,7 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
2513
2578
|
const normalizedTools = validateSelectorList(tools, rolePathValue, "tools", "INVALID_METADATA");
|
|
2514
2579
|
const normalizedSkills = validateSelectorList(skills, rolePathValue, "skills", "INVALID_METADATA");
|
|
2515
2580
|
const normalizedExtensions = validateSelectorList(extensions, rolePathValue, "extensions", "INVALID_METADATA");
|
|
2516
|
-
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA"
|
|
2581
|
+
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA");
|
|
2517
2582
|
const normalizedDescription = typeof description === "string" ? description.trim() : void 0;
|
|
2518
2583
|
const normalizedModel = typeof model === "string" ? model.trim() : void 0;
|
|
2519
2584
|
if (normalizedModel !== void 0) assertModelThinking(normalizedModel, "Role model");
|
|
@@ -2528,12 +2593,11 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
2528
2593
|
if (normalizedContextFiles !== void 0) definition.contextFiles = normalizedContextFiles;
|
|
2529
2594
|
return definition;
|
|
2530
2595
|
}
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
return [join4(agentDir, ROLE_DIRECTORY, "roles")];
|
|
2596
|
+
function workflowRoleDirectories(agentDir = getAgentDir2()) {
|
|
2597
|
+
return [join5(agentDir, ROLE_DIRECTORY2, "roles")];
|
|
2534
2598
|
}
|
|
2535
2599
|
function projectRoleDirectories(root) {
|
|
2536
|
-
return [
|
|
2600
|
+
return [join5(root, ROLE_DIRECTORY2, "roles")];
|
|
2537
2601
|
}
|
|
2538
2602
|
function roleDirectorySources(dirs) {
|
|
2539
2603
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2552,7 +2616,7 @@ function isRoleFile(dir, entry) {
|
|
|
2552
2616
|
if (entry.isFile()) return true;
|
|
2553
2617
|
if (!entry.isSymbolicLink()) return false;
|
|
2554
2618
|
try {
|
|
2555
|
-
return statSync(
|
|
2619
|
+
return statSync(join5(dir, entry.name)).isFile();
|
|
2556
2620
|
} catch (error) {
|
|
2557
2621
|
if (isNodeError(error, "ENOENT")) return false;
|
|
2558
2622
|
throw error;
|
|
@@ -2568,11 +2632,11 @@ function scanRoleFiles(dirs, extension2) {
|
|
|
2568
2632
|
if (!extension2 && isNodeError(error, "ENOENT")) continue;
|
|
2569
2633
|
fail("INVALID_METADATA", `${roleDirectoryLabel(source, extension2)} role directory "${source.path}" could not be scanned: ${errorText(error)}`);
|
|
2570
2634
|
}
|
|
2571
|
-
for (const entry of entries) if (isRoleFile(source.path, entry)) files.push({ name: basename3(entry.name, ".md"), path:
|
|
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 });
|
|
2572
2636
|
}
|
|
2573
2637
|
return files.sort((left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path));
|
|
2574
2638
|
}
|
|
2575
|
-
function
|
|
2639
|
+
function roleEntries(dirs, extension2) {
|
|
2576
2640
|
const files = scanRoleFiles(dirs, extension2);
|
|
2577
2641
|
const starterFiles = files.filter(({ source }) => source.builtin === true);
|
|
2578
2642
|
const regularFiles = files.filter(({ source }) => source.builtin !== true);
|
|
@@ -2583,7 +2647,9 @@ function readRoleDefinitions(dirs, extension2 = false) {
|
|
|
2583
2647
|
}
|
|
2584
2648
|
const read = (roleFiles) => Object.fromEntries(roleFiles.map(({ name, path, source }) => {
|
|
2585
2649
|
try {
|
|
2586
|
-
|
|
2650
|
+
const definition = parseRoleMarkdown(readFileSync3(path, "utf8"), true, path);
|
|
2651
|
+
roleDefinitionPaths.set(definition, path);
|
|
2652
|
+
return [name, { name, path, source, definition }];
|
|
2587
2653
|
} catch (error) {
|
|
2588
2654
|
if (extension2) fail("INVALID_METADATA", `${roleDirectoryLabel(source)} role directory "${source.path}" contains invalid role "${name}" at "${path}": ${errorText(error)}`);
|
|
2589
2655
|
throw error;
|
|
@@ -2591,15 +2657,114 @@ function readRoleDefinitions(dirs, extension2 = false) {
|
|
|
2591
2657
|
}));
|
|
2592
2658
|
return extension2 ? { ...read(starterFiles), ...read(regularFiles) } : read(files);
|
|
2593
2659
|
}
|
|
2594
|
-
function
|
|
2595
|
-
return
|
|
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
|
+
};
|
|
2596
2670
|
}
|
|
2597
|
-
function
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
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 } };
|
|
2602
2765
|
}
|
|
2766
|
+
|
|
2767
|
+
// src/validation.ts
|
|
2603
2768
|
var AGENT_OPTION_KEYS = /* @__PURE__ */ new Set(["label", "model", "tools", "skills", "extensions", "contextFiles", "role", "outputSchema", "retries", "timeoutMs"]);
|
|
2604
2769
|
function validateAgentOption(key, value, aliases, knownModels, settingsPath) {
|
|
2605
2770
|
switch (key) {
|
|
@@ -2876,6 +3041,30 @@ const functions = Object.freeze(Object.fromEntries(Object.entries(config.functio
|
|
|
2876
3041
|
Object.defineProperty(result, "toJSON", { value() { throw workError("INVALID_METADATA", "Workflow function result is a Promise; await it before serialization"); } });
|
|
2877
3042
|
return result;
|
|
2878
3043
|
}])));
|
|
3044
|
+
// tools.<id>(args) runs a Pi session tool through the host. Any identifier resolves, so a resumed run
|
|
3045
|
+
// replays journaled calls even when the tool is gone; the host rejects identifiers it cannot call.
|
|
3046
|
+
// A call is the n-th call with the same arguments in its scope, so replay does not depend on the
|
|
3047
|
+
// order in which concurrent calls start.
|
|
3048
|
+
const NOT_TOOLS = new Set(["then", "toJSON", "toString", "valueOf", "constructor"]);
|
|
3049
|
+
const sortedJson = value => Array.isArray(value) ? value.map(sortedJson) : value && typeof value === "object" ? Object.fromEntries(Object.keys(value).sort().map(key => [key, sortedJson(value[key])])) : value;
|
|
3050
|
+
const toolOccurrences = new Map();
|
|
3051
|
+
const toolCall = id => (...values) => {
|
|
3052
|
+
if (worktreeOwners.getStore()) throw workError("INVALID_METADATA", "tools." + id + " is unavailable inside withWorktree: session tools resolve paths against the session working directory");
|
|
3053
|
+
if (values.length > 1 || values[0] !== undefined && (!values[0] || typeof values[0] !== "object" || Array.isArray(values[0]))) throw workError("INVALID_METADATA", "tools." + id + " takes one arguments object");
|
|
3054
|
+
const args = values[0] === undefined ? {} : values[0];
|
|
3055
|
+
const structuralPath = inheritedAgentPath.getStore() || [];
|
|
3056
|
+
const key = JSON.stringify([structuralPath, id, sortedJson(args)]);
|
|
3057
|
+
const occurrence = (toolOccurrences.get(key) || 0) + 1;
|
|
3058
|
+
toolOccurrences.set(key, occurrence);
|
|
3059
|
+
return guardedResult(rpc("tool", [id, args, { structuralPath: [...structuralPath], occurrence }]), "tool");
|
|
3060
|
+
};
|
|
3061
|
+
const tools = new Proxy(Object.create(null), {
|
|
3062
|
+
get: (_target, key) => typeof key === "string" && !NOT_TOOLS.has(key) ? toolCall(key) : undefined,
|
|
3063
|
+
// Probing would branch on the session that resumed the run; the launch already checks named tools.
|
|
3064
|
+
has: () => { throw workError("INVALID_METADATA", "Workflow scripts cannot probe tools with 'in'; call tools.<name>(...) directly, and the launch checks that the tool exists"); },
|
|
3065
|
+
set: () => false,
|
|
3066
|
+
defineProperty: () => false,
|
|
3067
|
+
});
|
|
2879
3068
|
const recordEntries = (value, kind) => {
|
|
2880
3069
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw workError("INVALID_METADATA", kind + " must be a record");
|
|
2881
3070
|
return Object.entries(value);
|
|
@@ -2936,7 +3125,7 @@ const pipeline = async (operationName, items, stages) => {
|
|
|
2936
3125
|
return Object.fromEntries(results.map(result => [result.name, result.value]));
|
|
2937
3126
|
};
|
|
2938
3127
|
const safeMath = Object.fromEntries(Object.getOwnPropertyNames(Math).filter(name => name !== "random").map(name => [name, Math[name]]));
|
|
2939
|
-
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
3128
|
+
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, tools, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
2940
3129
|
for (const [name, fn] of Object.entries(functions)) Object.defineProperty(sandbox, name, { value: fn, writable: false, configurable: false });
|
|
2941
3130
|
for (const name of ["Date","eval","Function","WebAssembly","process","require","module","exports","console","fetch","XMLHttpRequest","WebSocket","performance","crypto","setTimeout","setInterval","setImmediate","queueMicrotask","Intl","SharedArrayBuffer","Atomics"]) sandbox[name] = undefined;
|
|
2942
3131
|
const context = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
|
|
@@ -2949,17 +3138,17 @@ Promise.resolve().then(() => new vm.Script("(async(__pi_extensible_workflows_age
|
|
|
2949
3138
|
|
|
2950
3139
|
// src/host.ts
|
|
2951
3140
|
import { Type as Type3 } from "@earendil-works/pi-ai";
|
|
2952
|
-
import { copyToClipboard, getAgentDir as
|
|
3141
|
+
import { copyToClipboard, getAgentDir as getAgentDir5, ModelSelectorComponent } from "@earendil-works/pi-coding-agent";
|
|
2953
3142
|
|
|
2954
3143
|
// src/agent-execution.ts
|
|
2955
|
-
import { existsSync, realpathSync as realpathSync2, readFileSync as
|
|
3144
|
+
import { existsSync, realpathSync as realpathSync2, readFileSync as readFileSync4 } from "node:fs";
|
|
2956
3145
|
import { copyFile } from "node:fs/promises";
|
|
2957
3146
|
import { homedir as homedir4 } from "node:os";
|
|
2958
|
-
import { basename as basename4, dirname as
|
|
3147
|
+
import { basename as basename4, dirname as dirname5, join as join6, resolve as resolve5 } from "node:path";
|
|
2959
3148
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2960
3149
|
import { Type } from "@earendil-works/pi-ai";
|
|
2961
3150
|
import { Compile } from "typebox/compile";
|
|
2962
|
-
import { createAgentSession, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as
|
|
3151
|
+
import { createAgentSession, createCodemodeExtension, createMcpExtension, createToolSearchExtension, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as getAgentDir3, ModelRuntime, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
2963
3152
|
|
|
2964
3153
|
// src/pi-runtime-adapter.ts
|
|
2965
3154
|
var TURN_START_EVENTS = /* @__PURE__ */ new Set(["turn_start", "turn_started", "turnStarted"]);
|
|
@@ -3007,16 +3196,16 @@ function normalizeState(value) {
|
|
|
3007
3196
|
const candidate = record(value);
|
|
3008
3197
|
const model = record(candidate?.model);
|
|
3009
3198
|
const provider = stringValue(model?.provider);
|
|
3010
|
-
const
|
|
3199
|
+
const modelName2 = stringValue(model?.model);
|
|
3011
3200
|
const rawTools = candidate?.tools;
|
|
3012
3201
|
const tools = Array.isArray(rawTools) ? rawTools.filter((tool) => typeof tool === "string") : void 0;
|
|
3013
|
-
if (!candidate || !model || provider === void 0 ||
|
|
3202
|
+
if (!candidate || !model || provider === void 0 || modelName2 === void 0 || tools === void 0 || !Array.isArray(rawTools) || tools.length !== rawTools.length) return void 0;
|
|
3014
3203
|
const thinking = candidate.thinking === void 0 ? model.thinking : candidate.thinking;
|
|
3015
3204
|
if (thinking !== void 0 && !isThinkingLevel(thinking)) return void 0;
|
|
3016
3205
|
const systemPrompt = candidate.systemPrompt;
|
|
3017
3206
|
if (systemPrompt !== void 0 && typeof systemPrompt !== "string") return void 0;
|
|
3018
3207
|
return {
|
|
3019
|
-
model: { provider, model:
|
|
3208
|
+
model: { provider, model: modelName2, ...thinking === void 0 ? {} : { thinking } },
|
|
3020
3209
|
...thinking === void 0 ? {} : { thinking },
|
|
3021
3210
|
tools: [...tools],
|
|
3022
3211
|
...systemPrompt === void 0 ? {} : { systemPrompt }
|
|
@@ -3116,14 +3305,14 @@ function createRuntimeHandoffAdapter(handoff) {
|
|
|
3116
3305
|
function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
3117
3306
|
const neutralHandoff = createRuntimeHandoffAdapter(handoff);
|
|
3118
3307
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
3119
|
-
let
|
|
3308
|
+
let activity;
|
|
3120
3309
|
let streamText = "";
|
|
3121
3310
|
let lastEventAt;
|
|
3122
3311
|
let lastReportedEventAt;
|
|
3123
3312
|
let disposed = false;
|
|
3124
3313
|
let toolCallsView = [];
|
|
3125
3314
|
const progressAt = (persist, stateOverride, toolCallsValue3, activityValue2, eventAtValue) => ({ usage: runtimeUsage(session), toolCalls: toolCallsValue3, state: stateOverride ?? stateFromSession(session), ...activityValue2 === void 0 ? {} : { activity: activityValue2 }, ...eventAtValue === void 0 ? {} : { lastEventAt: eventAtValue }, persist });
|
|
3126
|
-
const snapshot = (persist) => progressAt(persist, void 0, [...toolCalls.values()],
|
|
3315
|
+
const snapshot = (persist) => progressAt(persist, void 0, [...toolCalls.values()], activity, lastEventAt);
|
|
3127
3316
|
return {
|
|
3128
3317
|
handoff: neutralHandoff,
|
|
3129
3318
|
observe(event) {
|
|
@@ -3134,7 +3323,7 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3134
3323
|
const eventState = event.state === void 0 ? void 0 : runtimeState(event.state);
|
|
3135
3324
|
let report = false;
|
|
3136
3325
|
let persist = false;
|
|
3137
|
-
const previousActivity =
|
|
3326
|
+
const previousActivity = activity;
|
|
3138
3327
|
let eventToolCalls = toolCallsView;
|
|
3139
3328
|
if (event.type === "state_changed") {
|
|
3140
3329
|
report = true;
|
|
@@ -3142,7 +3331,7 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3142
3331
|
}
|
|
3143
3332
|
if (event.type === "message_start" && event.message?.role === "assistant") {
|
|
3144
3333
|
streamText = "";
|
|
3145
|
-
|
|
3334
|
+
activity = { kind: "text", text: "" };
|
|
3146
3335
|
report = true;
|
|
3147
3336
|
}
|
|
3148
3337
|
if (event.type === "message_update") {
|
|
@@ -3150,14 +3339,14 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3150
3339
|
if (updateType === "thinking_start" || updateType === "text_start") streamText = "";
|
|
3151
3340
|
const delta = event.assistantMessageEvent?.delta;
|
|
3152
3341
|
if ((updateType === "thinking_delta" || updateType === "text_delta") && delta) streamText = `${streamText}${delta}`.replace(/\s+/g, " ").slice(-MAX_ACTIVITY_TEXT_CHARS);
|
|
3153
|
-
if (updateType && ["thinking_start", "thinking_delta", "thinking_end"].includes(updateType))
|
|
3154
|
-
else if (updateType && ["text_start", "text_delta", "text_end", "toolcall_start", "toolcall_delta", "toolcall_end"].includes(updateType))
|
|
3155
|
-
report = previousActivity?.kind !==
|
|
3342
|
+
if (updateType && ["thinking_start", "thinking_delta", "thinking_end"].includes(updateType)) activity = { kind: "reasoning", text: streamText };
|
|
3343
|
+
else if (updateType && ["text_start", "text_delta", "text_end", "toolcall_start", "toolcall_delta", "toolcall_end"].includes(updateType)) activity = { kind: "text", text: streamText };
|
|
3344
|
+
report = previousActivity?.kind !== activity?.kind;
|
|
3156
3345
|
}
|
|
3157
3346
|
if (event.type === "message_end") {
|
|
3158
3347
|
streamText = "";
|
|
3159
|
-
|
|
3160
|
-
report = !sameActivity(previousActivity,
|
|
3348
|
+
activity = void 0;
|
|
3349
|
+
report = !sameActivity(previousActivity, activity);
|
|
3161
3350
|
if (event.message?.role === "assistant") {
|
|
3162
3351
|
persist = true;
|
|
3163
3352
|
report = true;
|
|
@@ -3166,24 +3355,24 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3166
3355
|
if (event.type === "tool_execution_start" && event.toolCallId && event.toolName) {
|
|
3167
3356
|
toolCalls.set(event.toolCallId, { id: event.toolCallId, name: event.toolName, state: "running" });
|
|
3168
3357
|
toolCallsView = [...toolCalls.values()];
|
|
3169
|
-
|
|
3358
|
+
activity = { kind: "tool", text: event.toolName };
|
|
3170
3359
|
report = true;
|
|
3171
3360
|
}
|
|
3172
3361
|
if (event.type === "tool_execution_update" && event.toolName) {
|
|
3173
|
-
|
|
3174
|
-
report = !sameActivity(previousActivity,
|
|
3362
|
+
activity = { kind: "tool", text: event.toolName };
|
|
3363
|
+
report = !sameActivity(previousActivity, activity);
|
|
3175
3364
|
}
|
|
3176
3365
|
let removeToolCallId;
|
|
3177
3366
|
if (event.type === "tool_execution_end" && event.toolCallId && event.toolName) {
|
|
3178
3367
|
toolCalls.set(event.toolCallId, { id: event.toolCallId, name: event.toolName, state: event.isError ? "failed" : "completed" });
|
|
3179
3368
|
eventToolCalls = [...toolCalls.values()];
|
|
3180
|
-
if (
|
|
3369
|
+
if (activity?.kind === "tool" && activity.text === event.toolName) activity = void 0;
|
|
3181
3370
|
report = true;
|
|
3182
3371
|
removeToolCallId = event.toolCallId;
|
|
3183
3372
|
}
|
|
3184
3373
|
if (removeToolCallId === void 0) eventToolCalls = toolCallsView;
|
|
3185
3374
|
if (!report && (lastReportedEventAt === void 0 || eventAt - lastReportedEventAt >= 1e3)) report = true;
|
|
3186
|
-
const eventActivity =
|
|
3375
|
+
const eventActivity = activity;
|
|
3187
3376
|
let materialized;
|
|
3188
3377
|
const result = { event, get progress() {
|
|
3189
3378
|
return materialized ??= progressAt(persist, eventState, eventToolCalls, eventActivity, eventAt);
|
|
@@ -3200,7 +3389,7 @@ function createPiRuntimeSessionAdapter(session, handoff, clock = Date.now) {
|
|
|
3200
3389
|
disposed = true;
|
|
3201
3390
|
toolCalls.clear();
|
|
3202
3391
|
toolCallsView = [];
|
|
3203
|
-
|
|
3392
|
+
activity = void 0;
|
|
3204
3393
|
}
|
|
3205
3394
|
};
|
|
3206
3395
|
}
|
|
@@ -3234,8 +3423,8 @@ function createLiveSessionHandoff() {
|
|
|
3234
3423
|
let request;
|
|
3235
3424
|
const waitForBoundary = () => {
|
|
3236
3425
|
if (!turnActive) return Promise.resolve();
|
|
3237
|
-
boundary ??= new Promise((
|
|
3238
|
-
resolveBoundary =
|
|
3426
|
+
boundary ??= new Promise((resolve6) => {
|
|
3427
|
+
resolveBoundary = resolve6;
|
|
3239
3428
|
});
|
|
3240
3429
|
return boundary;
|
|
3241
3430
|
};
|
|
@@ -3267,11 +3456,11 @@ function createLiveSessionHandoff() {
|
|
|
3267
3456
|
if (request) return request;
|
|
3268
3457
|
if (isReleased()) return;
|
|
3269
3458
|
state = "handoff-pending";
|
|
3270
|
-
takeover = new Promise((
|
|
3271
|
-
resolveTakeover =
|
|
3459
|
+
takeover = new Promise((resolve6) => {
|
|
3460
|
+
resolveTakeover = resolve6;
|
|
3272
3461
|
});
|
|
3273
|
-
resume = new Promise((
|
|
3274
|
-
resolveResume =
|
|
3462
|
+
resume = new Promise((resolve6) => {
|
|
3463
|
+
resolveResume = resolve6;
|
|
3275
3464
|
});
|
|
3276
3465
|
request = (async () => {
|
|
3277
3466
|
await waitForBoundary();
|
|
@@ -3798,36 +3987,37 @@ function parseModel(value, fallback, aliases = {}, knownModels, settingsPath) {
|
|
|
3798
3987
|
function accounting(stats) {
|
|
3799
3988
|
return { input: stats.tokens.input, output: stats.tokens.output, cacheRead: stats.tokens.cacheRead, cacheWrite: stats.tokens.cacheWrite, cost: stats.cost };
|
|
3800
3989
|
}
|
|
3801
|
-
var
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3990
|
+
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
|
+
function isWorkflowHostEntry(path) {
|
|
3992
|
+
const entryPath = canonicalPath(path);
|
|
3993
|
+
let packageRoot2 = dirname5(entryPath);
|
|
3994
|
+
while (packageRoot2 !== dirname5(packageRoot2)) {
|
|
3995
|
+
const packageMetadataPath = join6(packageRoot2, "package.json");
|
|
3996
|
+
if (existsSync(packageMetadataPath)) {
|
|
3997
|
+
try {
|
|
3998
|
+
const metadata = JSON.parse(readFileSync4(packageMetadataPath, "utf8"));
|
|
3999
|
+
if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || !isCorePackageName(metadata.name)) return false;
|
|
4000
|
+
return WORKFLOW_HOST_ENTRY_PATHS.some((entry) => canonicalPath(join6(packageRoot2, entry)) === entryPath);
|
|
4001
|
+
} catch {
|
|
4002
|
+
return false;
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
packageRoot2 = dirname5(packageRoot2);
|
|
4006
|
+
}
|
|
4007
|
+
return false;
|
|
3818
4008
|
}
|
|
3819
4009
|
var WORKFLOW_DIRECTORY = "pi-extensible-workflows";
|
|
3820
4010
|
function workflowSystemPromptPath(cwd, agentDir, projectTrusted) {
|
|
3821
|
-
const projectPath =
|
|
4011
|
+
const projectPath = join6(cwd, ".pi", WORKFLOW_DIRECTORY, "SYSTEM.md");
|
|
3822
4012
|
if (projectTrusted && existsSync(projectPath)) return projectPath;
|
|
3823
|
-
const globalPaths = [
|
|
4013
|
+
const globalPaths = [join6(homedir4(), ".pi", WORKFLOW_DIRECTORY, "SYSTEM.md"), join6(agentDir, WORKFLOW_DIRECTORY, "SYSTEM.md")];
|
|
3824
4014
|
return globalPaths.find((path) => existsSync(path));
|
|
3825
4015
|
}
|
|
3826
4016
|
function filterContextFiles(base, scopes, cwd, agentDir) {
|
|
3827
4017
|
const globalPath = canonicalPath(agentDir);
|
|
3828
4018
|
const cwdPath = canonicalPath(cwd);
|
|
3829
4019
|
return base.filter(({ path }) => {
|
|
3830
|
-
const resolvedPath = canonicalPath(
|
|
4020
|
+
const resolvedPath = canonicalPath(dirname5(path));
|
|
3831
4021
|
return scopes.some((scope) => scope === "global" ? resolvedPath === globalPath : scope === "cwd" ? resolvedPath === cwdPath : resolvedPath !== globalPath && resolvedPath !== cwdPath);
|
|
3832
4022
|
});
|
|
3833
4023
|
}
|
|
@@ -3848,33 +4038,40 @@ function loadPiPackageDirectory() {
|
|
|
3848
4038
|
}
|
|
3849
4039
|
}).catch((error) => ({ error: `could not load @earendil-works/pi-coding-agent package metadata: ${errorText(error)}` }));
|
|
3850
4040
|
}
|
|
3851
|
-
var
|
|
3852
|
-
async function
|
|
4041
|
+
var nativeCoreModules = /* @__PURE__ */ new Map();
|
|
4042
|
+
async function loadNativeCoreModule(file, label2) {
|
|
3853
4043
|
const packageDirectory = await loadPiPackageDirectory();
|
|
3854
|
-
let
|
|
4044
|
+
let modulePath;
|
|
3855
4045
|
if (packageDirectory.directory) {
|
|
3856
|
-
const candidate =
|
|
3857
|
-
if (existsSync(candidate))
|
|
4046
|
+
const candidate = resolve5(packageDirectory.directory, "dist/core", file);
|
|
4047
|
+
if (existsSync(candidate)) modulePath = candidate;
|
|
3858
4048
|
}
|
|
3859
|
-
|
|
4049
|
+
modulePath ??= resolve5(dirname5(fileURLToPath3(import.meta.resolve("@earendil-works/pi-coding-agent"))), "core", file);
|
|
3860
4050
|
const packageDirectoryError = packageDirectory.error ? ` Package directory lookup failed: ${packageDirectory.error}` : "";
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
4051
|
+
let loaded = nativeCoreModules.get(file);
|
|
4052
|
+
if (!loaded) {
|
|
4053
|
+
loaded = import(modulePath).catch((error) => {
|
|
4054
|
+
throw new Error(`Could not load Pi ${label2}.${packageDirectoryError}`, { cause: error });
|
|
4055
|
+
});
|
|
4056
|
+
nativeCoreModules.set(file, loaded);
|
|
4057
|
+
}
|
|
4058
|
+
return loaded;
|
|
3864
4059
|
}
|
|
3865
4060
|
async function expandPromptTemplateForInspection(text, templates) {
|
|
3866
|
-
return (await
|
|
4061
|
+
return (await loadNativeCoreModule("prompt-templates.js", "prompt templates")).expandPromptTemplate(text, templates);
|
|
4062
|
+
}
|
|
4063
|
+
async function buildSystemPromptForInspection(options) {
|
|
4064
|
+
return (await loadNativeCoreModule("system-prompt.js", "system prompt builder")).buildSystemPrompt(options);
|
|
3867
4065
|
}
|
|
3868
4066
|
async function preparePiPrompt(native, text) {
|
|
3869
4067
|
const session = native;
|
|
3870
4068
|
const diagnostics = [];
|
|
3871
4069
|
const runner = session._extensionRunner;
|
|
3872
|
-
const baseSystemPrompt =
|
|
4070
|
+
const baseSystemPrompt = session.systemPrompt ?? "";
|
|
3873
4071
|
const baseOptions = session._baseSystemPromptOptions;
|
|
3874
4072
|
const expandSkillCommand = session._expandSkillCommand;
|
|
3875
4073
|
const templates = session.promptTemplates;
|
|
3876
4074
|
if (!runner) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: extension runner is missing", source: "Pi session" });
|
|
3877
|
-
if (typeof session._baseSystemPrompt !== "string") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt is missing", source: "Pi session" });
|
|
3878
4075
|
if (baseOptions === void 0) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt options are missing", source: "Pi session" });
|
|
3879
4076
|
if (typeof expandSkillCommand !== "function") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: skill expansion is missing", source: "Pi session" });
|
|
3880
4077
|
if (!Array.isArray(templates)) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: prompt templates are missing", source: "Pi session" });
|
|
@@ -3899,68 +4096,86 @@ async function preparePiPrompt(native, text) {
|
|
|
3899
4096
|
diagnostics.push({ type: "error", message: `Pi prompt template expansion is unavailable: ${errorText(error)}`, source: "Pi session" });
|
|
3900
4097
|
}
|
|
3901
4098
|
}
|
|
3902
|
-
const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0,
|
|
4099
|
+
const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0, baseOptions) : void 0;
|
|
3903
4100
|
const prepared = result;
|
|
3904
|
-
|
|
4101
|
+
const systemPromptOptions = prepared?.systemPromptOptions ?? baseOptions;
|
|
4102
|
+
const systemPrompt = prepared?.systemPromptOptions === void 0 ? baseSystemPrompt : await buildSystemPromptForInspection(prepared.systemPromptOptions);
|
|
4103
|
+
return { prompt: text, expandedPrompt, systemPrompt, systemPromptOptions, messages: prepared?.messages ?? [], inputHandled, diagnostics };
|
|
3905
4104
|
} finally {
|
|
3906
4105
|
unsubscribe?.();
|
|
3907
4106
|
}
|
|
3908
4107
|
}
|
|
3909
4108
|
function flushExtensionProviders(resourceLoader, modelRuntime) {
|
|
3910
|
-
const { runtime
|
|
4109
|
+
const { runtime } = resourceLoader.getExtensions();
|
|
3911
4110
|
const failures = [];
|
|
3912
|
-
for (const { name, config } of
|
|
4111
|
+
for (const { name, config } of runtime.pendingProviderRegistrations) {
|
|
3913
4112
|
try {
|
|
3914
4113
|
modelRuntime.registerProvider(name, config);
|
|
3915
4114
|
} catch (error) {
|
|
3916
4115
|
failures.push(`${name}: ${errorText(error)}`);
|
|
3917
4116
|
}
|
|
3918
4117
|
}
|
|
3919
|
-
|
|
3920
|
-
for (const { provider } of
|
|
4118
|
+
runtime.pendingProviderRegistrations = [];
|
|
4119
|
+
for (const { provider } of runtime.pendingNativeProviderRegistrations) {
|
|
3921
4120
|
try {
|
|
3922
4121
|
modelRuntime.registerNativeProvider(provider);
|
|
3923
4122
|
} catch (error) {
|
|
3924
4123
|
failures.push(`${provider.id || "native provider"}: ${errorText(error)}`);
|
|
3925
4124
|
}
|
|
3926
4125
|
}
|
|
3927
|
-
|
|
4126
|
+
runtime.pendingNativeProviderRegistrations = [];
|
|
3928
4127
|
return failures;
|
|
3929
4128
|
}
|
|
4129
|
+
function builtinExtensions() {
|
|
4130
|
+
return [
|
|
4131
|
+
{ name: "codemode", factory: createCodemodeExtension(), replaceable: true, builtin: true },
|
|
4132
|
+
{ name: "tool-search", factory: createToolSearchExtension(), replaceable: true, builtin: true },
|
|
4133
|
+
{ name: "mcp", factory: createMcpExtension(), replaceable: true, builtin: true }
|
|
4134
|
+
];
|
|
4135
|
+
}
|
|
4136
|
+
var BUILTIN_EXTENSION_PREFIX = "builtin:";
|
|
4137
|
+
function isLlamaModel(model) {
|
|
4138
|
+
return model.provider === "llama.cpp";
|
|
4139
|
+
}
|
|
3930
4140
|
async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
3931
|
-
const agentDir = input.agentDir ??
|
|
4141
|
+
const agentDir = input.agentDir ?? getAgentDir3();
|
|
3932
4142
|
const systemPromptSource = workflowSystemPromptPath(input.cwd, agentDir, input.resourcePolicy?.projectTrusted ?? true);
|
|
3933
4143
|
const systemPromptOptions = input.systemPrompt !== void 0 ? { systemPromptOverride: () => input.systemPrompt } : systemPromptSource !== void 0 ? { systemPrompt: systemPromptSource } : {};
|
|
3934
4144
|
const contextFilesOverride = input.contextFiles === void 0 ? void 0 : (base) => ({ ...base, agentsFiles: filterContextFiles(base.agentsFiles, input.contextFiles ?? [], input.cwd, agentDir) });
|
|
3935
|
-
const manager = input.sessionManager ?? (input.sessionPath ? SessionManager.open(input.sessionPath,
|
|
4145
|
+
const manager = input.sessionManager ?? (input.sessionPath ? SessionManager.open(input.sessionPath, join6(agentDir, "sessions"), input.cwd) : input.agentDir ? SessionManager.create(input.cwd, join6(agentDir, "sessions")) : SessionManager.create(input.cwd));
|
|
3936
4146
|
if (!input.sessionPath) manager.appendSessionInfo(input.sessionLabel);
|
|
3937
|
-
const modelRuntime = await ModelRuntime.create({ authPath:
|
|
4147
|
+
const modelRuntime = await ModelRuntime.create({ authPath: join6(agentDir, "auth.json"), modelsPath: join6(agentDir, "models.json") });
|
|
3938
4148
|
const customTools = [...input.customTools ?? [], ...input.resultTool ? [input.resultTool] : []];
|
|
3939
4149
|
const tools = [.../* @__PURE__ */ new Set([...input.tools, ...customTools.map(({ name }) => name)])];
|
|
3940
4150
|
let settingsManager;
|
|
3941
4151
|
let resourceLoader;
|
|
4152
|
+
const builtins = builtinExtensions();
|
|
4153
|
+
const builtinNames = builtins.flatMap((extension2) => typeof extension2 === "function" ? [] : [extension2.name]);
|
|
4154
|
+
const extensionFactories = [...builtins, ...input.extensionFactories ?? []];
|
|
3942
4155
|
const policy = input.resourcePolicy;
|
|
3943
4156
|
if (policy) {
|
|
3944
4157
|
settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: false });
|
|
3945
4158
|
settingsManager.setProjectTrusted(policy.projectTrusted);
|
|
3946
|
-
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
|
|
4159
|
+
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
|
|
3947
4160
|
const resolved = await packageManager.resolve();
|
|
3948
|
-
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) =>
|
|
4161
|
+
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => extensionIdentity(path)))];
|
|
3949
4162
|
const selectorSources = policy.selectorSources;
|
|
3950
|
-
const
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
4163
|
+
const selection = resolveRole(void 0, {
|
|
4164
|
+
cwd: input.cwd,
|
|
4165
|
+
selectorSources,
|
|
4166
|
+
resources: { extensions: discoveredExtensions }
|
|
4167
|
+
});
|
|
4168
|
+
const selectedExtensions = selection.selectedExtensions ?? [];
|
|
4169
|
+
const extensionPaths = selectedExtensions.filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path));
|
|
3955
4170
|
policy.selectedExtensions = selectedExtensions;
|
|
3956
|
-
policy.unmatchedExtensions = unmatchedExtensions;
|
|
4171
|
+
policy.unmatchedExtensions = selection.unmatchedExtensions ?? [];
|
|
3957
4172
|
const skillPaths = [...new Set(resolved.skills.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => path))];
|
|
3958
4173
|
const updateSkillMatches = (skills) => {
|
|
3959
4174
|
const names = [...new Set(skills.map(({ name }) => name))];
|
|
3960
|
-
const
|
|
3961
|
-
const selectedSkills =
|
|
4175
|
+
const selected = resolveRole(void 0, { cwd: input.cwd, selectorSources, resources: { skills: names } });
|
|
4176
|
+
const selectedSkills = selected.selectedSkills ?? [];
|
|
3962
4177
|
policy.selectedSkills = selectedSkills;
|
|
3963
|
-
policy.unmatchedSkills =
|
|
4178
|
+
policy.unmatchedSkills = selected.unmatchedSkills ?? [];
|
|
3964
4179
|
return new Set(names.filter((name) => !selectedSkills.includes(name)));
|
|
3965
4180
|
};
|
|
3966
4181
|
resourceLoader = new DefaultResourceLoader({
|
|
@@ -3971,7 +4186,7 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
3971
4186
|
additionalExtensionPaths: extensionPaths,
|
|
3972
4187
|
noSkills: true,
|
|
3973
4188
|
additionalSkillPaths: [.../* @__PURE__ */ new Set([...skillPaths, ...input.additionalSkillPaths ?? []])],
|
|
3974
|
-
|
|
4189
|
+
extensionFactories,
|
|
3975
4190
|
...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {},
|
|
3976
4191
|
skillsOverride: (base) => {
|
|
3977
4192
|
const disabledSkills = updateSkillMatches(base.skills);
|
|
@@ -3983,17 +4198,17 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
3983
4198
|
await resourceLoader.reload();
|
|
3984
4199
|
} else {
|
|
3985
4200
|
settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: true });
|
|
3986
|
-
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
|
|
4201
|
+
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
|
|
3987
4202
|
const resolved = await packageManager.resolve();
|
|
3988
|
-
const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) =>
|
|
3989
|
-
resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {},
|
|
4203
|
+
const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) => extensionIdentity(path)).filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path)))];
|
|
4204
|
+
resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {}, extensionFactories, ...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {}, ...systemPromptOptions, ...input.systemPromptAppend ? { appendSystemPromptOverride: (base) => [...base, input.systemPromptAppend ?? ""] } : {} });
|
|
3990
4205
|
await resourceLoader.reload();
|
|
3991
4206
|
}
|
|
3992
4207
|
const providerFailures = flushExtensionProviders(resourceLoader, modelRuntime);
|
|
3993
4208
|
await modelRuntime.refresh({ allowNetwork: false });
|
|
3994
4209
|
const model = modelRuntime.getModel(input.model.provider, input.model.model);
|
|
3995
4210
|
if (!model) {
|
|
3996
|
-
const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : "";
|
|
4211
|
+
const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : isLlamaModel(input.model) ? " (Pi does not expose its built-in llama.cpp provider to extension sessions; declare the llama.cpp server as a provider in models.json and use that model instead)" : "";
|
|
3997
4212
|
throw new WorkflowError("UNKNOWN_MODEL", `Unknown model: ${input.model.provider}/${input.model.model}${because}`);
|
|
3998
4213
|
}
|
|
3999
4214
|
const effectiveSessionStartEvent = input.settings === void 0 && sessionStartEvent === void 0 ? void 0 : { ...sessionStartEvent ?? { type: "session_start", reason: "startup" }, settings: input.settings ?? Object.freeze({}) };
|
|
@@ -4018,13 +4233,13 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
4018
4233
|
await shutdown("quit").catch(() => void 0);
|
|
4019
4234
|
throw error;
|
|
4020
4235
|
}
|
|
4021
|
-
const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) =>
|
|
4236
|
+
const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: resourceLoader.getSkills().skills.map(({ filePath }) => canonicalPath(filePath)) };
|
|
4022
4237
|
const resourceInspection = () => {
|
|
4023
4238
|
const extensions = resourceLoader.getExtensions();
|
|
4024
4239
|
const skills = resourceLoader.getSkills();
|
|
4025
4240
|
const diagnostics = [...extensions.errors.map(({ path, error }) => ({ type: "error", message: error, source: path })), ...skills.diagnostics.map(({ type, path, message }) => ({ type, message, ...path ? { source: path } : {} }))];
|
|
4026
4241
|
const systemSource = systemPromptSource;
|
|
4027
|
-
return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) =>
|
|
4242
|
+
return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: skills.skills.map(({ name }) => name), diagnostics, ...systemSource ? { systemPromptSource: systemSource } : resourceLoader.getSystemPrompt() !== void 0 ? { systemPromptSource: "Pi resource loader" } : {} };
|
|
4028
4243
|
};
|
|
4029
4244
|
const managedSession = Object.assign(session, {
|
|
4030
4245
|
getLeafId: () => manager.getLeafId(),
|
|
@@ -4439,27 +4654,27 @@ function appendResourcePolicyNarrowing(ceiling, candidate) {
|
|
|
4439
4654
|
};
|
|
4440
4655
|
}
|
|
4441
4656
|
function packageRoot(start) {
|
|
4442
|
-
let current =
|
|
4657
|
+
let current = dirname5(realpathSync2(start));
|
|
4443
4658
|
for (; ; ) {
|
|
4444
|
-
const candidates = [current,
|
|
4659
|
+
const candidates = [current, join6(current, "..", "@earendil-works", "pi-coding-agent"), join6(current, "..", "pi-coding-agent")];
|
|
4445
4660
|
for (const candidate of candidates) {
|
|
4446
|
-
const packageJsonPath =
|
|
4661
|
+
const packageJsonPath = join6(candidate, "package.json");
|
|
4447
4662
|
if (!existsSync(packageJsonPath)) continue;
|
|
4448
|
-
const packageJson = JSON.parse(
|
|
4663
|
+
const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
|
|
4449
4664
|
if (object(packageJson) && typeof packageJson.name === "string" && packageJson.name === "@earendil-works/pi-coding-agent") return candidate;
|
|
4450
4665
|
}
|
|
4451
|
-
const parent =
|
|
4666
|
+
const parent = dirname5(current);
|
|
4452
4667
|
if (parent === current) return void 0;
|
|
4453
4668
|
current = parent;
|
|
4454
4669
|
}
|
|
4455
4670
|
}
|
|
4456
4671
|
function piPackageEntrypoint(root) {
|
|
4457
|
-
const packageJson = JSON.parse(
|
|
4672
|
+
const packageJson = JSON.parse(readFileSync4(join6(root, "package.json"), "utf8"));
|
|
4458
4673
|
if (!object(packageJson)) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4459
4674
|
const bin = packageJson.bin;
|
|
4460
4675
|
const cli = typeof bin === "string" ? bin : object(bin) && typeof bin.pi === "string" ? bin.pi : void 0;
|
|
4461
4676
|
if (!cli) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4462
|
-
const entrypoint =
|
|
4677
|
+
const entrypoint = resolve5(root, cli);
|
|
4463
4678
|
if (!existsSync(entrypoint)) throw new Error(`the originating Pi CLI entrypoint is unavailable: ${entrypoint}`);
|
|
4464
4679
|
return entrypoint;
|
|
4465
4680
|
}
|
|
@@ -4500,7 +4715,7 @@ ${instruction}` : ""}`;
|
|
|
4500
4715
|
}
|
|
4501
4716
|
async function preparedAgentSession(input, initialPrompt) {
|
|
4502
4717
|
const { runtime: piRuntime, error: piRuntimeError } = await piRuntimeResolution;
|
|
4503
|
-
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ??
|
|
4718
|
+
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ?? getAgentDir3(), input.resourcePolicy?.projectTrusted ?? true) : void 0;
|
|
4504
4719
|
const prepared = {
|
|
4505
4720
|
cwd: input.cwd,
|
|
4506
4721
|
model: Object.freeze({ ...input.model }),
|
|
@@ -4530,7 +4745,7 @@ function agentSetupSummary(setup, hookNames) {
|
|
|
4530
4745
|
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) } : {} };
|
|
4531
4746
|
}
|
|
4532
4747
|
async function attemptSessionInput(source, attempt) {
|
|
4533
|
-
const target =
|
|
4748
|
+
const target = join6(dirname5(source), `${basename4(source, ".jsonl")}-attempt-${String(attempt)}.jsonl`);
|
|
4534
4749
|
await copyFile(source, target);
|
|
4535
4750
|
return target;
|
|
4536
4751
|
}
|
|
@@ -4632,7 +4847,6 @@ var WorkflowAgentExecutor = class {
|
|
|
4632
4847
|
const roleName2 = typeof options.role === "string" ? options.role : void 0;
|
|
4633
4848
|
const roleDefinition = roleName2 ? this.root.agentDefinitions?.[roleName2] : void 0;
|
|
4634
4849
|
if (roleName2 && !roleDefinition) throw new WorkflowError("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${roleName2}`);
|
|
4635
|
-
const candidateTools = inheritedTools !== void 0 ? [...inheritedTools] : [...this.root.tools];
|
|
4636
4850
|
if (inheritedTools === void 0) for (const pattern of roleDefinition?.tools ?? []) {
|
|
4637
4851
|
const body = pattern.startsWith("!") ? pattern.slice(1) : pattern;
|
|
4638
4852
|
if (!pattern.startsWith("!") && !resourcePatternHasMagic(pattern) && !this.root.tools.has(body)) {
|
|
@@ -4643,33 +4857,30 @@ var WorkflowAgentExecutor = class {
|
|
|
4643
4857
|
}
|
|
4644
4858
|
}
|
|
4645
4859
|
}
|
|
4646
|
-
const
|
|
4647
|
-
this.root.
|
|
4648
|
-
roleDefinition
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
const overrideSystemPrompt = roleDefinition?.overrideSystemPrompt === true;
|
|
4671
|
-
const contextFiles = options.contextFiles ?? roleDefinition?.contextFiles;
|
|
4672
|
-
return { model, ...alias && requestedModel ? { requestedModel } : {}, tools: requested, ...overrideSystemPrompt ? { systemPrompt: roleDefinition.prompt ?? "" } : {}, systemPromptAppend: overrideSystemPrompt ? "" : roleDefinition?.prompt ?? "", ...contextFiles === void 0 ? {} : { contextFiles: [...contextFiles] } };
|
|
4860
|
+
const resolved = resolveRole(roleName2, {
|
|
4861
|
+
cwd: this.root.cwd,
|
|
4862
|
+
...roleDefinition === void 0 ? {} : { definition: roleDefinition },
|
|
4863
|
+
...this.root.agentDefinitions === void 0 ? {} : { definitions: this.root.agentDefinitions },
|
|
4864
|
+
...this.root.agentDir === void 0 ? {} : { agentDir: this.root.agentDir },
|
|
4865
|
+
selectorSources: { global: this.root.resourceSelectors ?? {}, project: {} },
|
|
4866
|
+
...this.root.modelAliases === void 0 ? {} : { modelAliases: this.root.modelAliases },
|
|
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 },
|
|
4875
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
4876
|
+
...options.modelOverride === void 0 ? {} : { modelOverride: options.modelOverride },
|
|
4877
|
+
...options.tools === void 0 ? {} : { tools: options.tools },
|
|
4878
|
+
...options.skills === void 0 ? {} : { skills: options.skills },
|
|
4879
|
+
...options.extensions === void 0 ? {} : { extensions: options.extensions },
|
|
4880
|
+
...options.contextFiles === void 0 ? {} : { contextFiles: options.contextFiles },
|
|
4881
|
+
...options.effectiveTools === void 0 ? {} : { effectiveTools: options.effectiveTools }
|
|
4882
|
+
});
|
|
4883
|
+
return { model: physicalModel(resolved.model ?? this.root.model, this.root.modelAliases, this.root.knownModels, this.root.settingsPath), ...resolved.requestedModel === void 0 ? {} : { requestedModel: resolved.requestedModel }, tools: resolved.tools ?? [], ...resolved.overrideSystemPrompt ? { systemPrompt: resolved.prompt } : {}, systemPromptAppend: resolved.overrideSystemPrompt ? "" : resolved.prompt, ...resolved.contextFiles === void 0 ? {} : { contextFiles: resolved.contextFiles } };
|
|
4673
4884
|
}
|
|
4674
4885
|
async execute(task, options, signal, customTools = [], setSteer, beforeRetry) {
|
|
4675
4886
|
const executionSignal = signal ?? this.root.runContext?.signal;
|
|
@@ -4926,7 +5137,7 @@ import { SettingsManager as SettingsManager2, truncateToVisualLines as truncateT
|
|
|
4926
5137
|
import { spawn } from "node:child_process";
|
|
4927
5138
|
import { mkdtemp, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
4928
5139
|
import { tmpdir } from "node:os";
|
|
4929
|
-
import { join as
|
|
5140
|
+
import { join as join7 } from "node:path";
|
|
4930
5141
|
function workflowPromptArtifact(prompt) {
|
|
4931
5142
|
return { extension: ".md", content: prompt };
|
|
4932
5143
|
}
|
|
@@ -4937,23 +5148,23 @@ function workflowResultArtifact(value) {
|
|
|
4937
5148
|
async function spawnWorkflowEditor(command, path) {
|
|
4938
5149
|
const [editor, ...editorArgs] = command.split(" ");
|
|
4939
5150
|
if (!editor) return null;
|
|
4940
|
-
return new Promise((
|
|
5151
|
+
return new Promise((resolve6) => {
|
|
4941
5152
|
try {
|
|
4942
5153
|
const child = spawn(editor, [...editorArgs, path], { stdio: "inherit", shell: process.platform === "win32" });
|
|
4943
5154
|
child.once("error", () => {
|
|
4944
|
-
|
|
5155
|
+
resolve6(null);
|
|
4945
5156
|
});
|
|
4946
5157
|
child.once("close", (code) => {
|
|
4947
|
-
|
|
5158
|
+
resolve6(code);
|
|
4948
5159
|
});
|
|
4949
5160
|
} catch {
|
|
4950
|
-
|
|
5161
|
+
resolve6(null);
|
|
4951
5162
|
}
|
|
4952
5163
|
});
|
|
4953
5164
|
}
|
|
4954
5165
|
async function openWorkflowArtifact(tui, command, artifact) {
|
|
4955
|
-
const directory = await mkdtemp(
|
|
4956
|
-
const path =
|
|
5166
|
+
const directory = await mkdtemp(join7(tmpdir(), "pi-workflow-editor-"));
|
|
5167
|
+
const path = join7(directory, `artifact${artifact.extension}`);
|
|
4957
5168
|
try {
|
|
4958
5169
|
await writeFile3(path, artifact.content, { encoding: "utf8", mode: 384 });
|
|
4959
5170
|
tui.stop();
|
|
@@ -4970,23 +5181,88 @@ async function openWorkflowArtifact(tui, command, artifact) {
|
|
|
4970
5181
|
|
|
4971
5182
|
// src/host-view.ts
|
|
4972
5183
|
import { keyHint, truncateToVisualLines } from "@earendil-works/pi-coding-agent";
|
|
4973
|
-
import { truncateToWidth as truncateToWidth2 } from "@earendil-works/pi-tui";
|
|
5184
|
+
import { truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
|
|
4974
5185
|
|
|
4975
5186
|
// src/background-widget.ts
|
|
4976
5187
|
import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
5188
|
+
var REPAINT_MS = 125;
|
|
4977
5189
|
var QUIET_MS = 3 * 60 * 1e3;
|
|
5190
|
+
var ROLE = {
|
|
5191
|
+
live: "accent",
|
|
5192
|
+
done: "success",
|
|
5193
|
+
failed: "error",
|
|
5194
|
+
warn: "warning",
|
|
5195
|
+
quiet: "muted"
|
|
5196
|
+
};
|
|
5197
|
+
var RESET = "\x1B[0m";
|
|
5198
|
+
var SPINNER = ["\u28F7", "\u28EF", "\u28DF", "\u287F", "\u28BF", "\u28FD", "\u28FB"];
|
|
5199
|
+
function spinner(now) {
|
|
5200
|
+
return SPINNER[Math.floor(now / REPAINT_MS) % SPINNER.length] ?? SPINNER[0];
|
|
5201
|
+
}
|
|
5202
|
+
function mark(state, now, paint, runState) {
|
|
5203
|
+
if (state === "running" && (runState === void 0 || runState === "running")) return paint(ROLE.live, spinner(now));
|
|
5204
|
+
if (state === "completed") return paint(ROLE.done, "\u2713");
|
|
5205
|
+
if (state === "failed" || state === "budget_exhausted") return paint(ROLE.failed, "\u2717");
|
|
5206
|
+
return paint(ROLE.quiet, "\xB7");
|
|
5207
|
+
}
|
|
5208
|
+
function formatElapsed(ms) {
|
|
5209
|
+
const total = Math.max(0, Math.floor(ms / 1e3));
|
|
5210
|
+
const minutes = Math.floor(total / 60);
|
|
5211
|
+
const seconds = total % 60;
|
|
5212
|
+
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
5213
|
+
}
|
|
5214
|
+
function formatTokens(tokens) {
|
|
5215
|
+
if (!tokens) return "";
|
|
5216
|
+
if (tokens < 1e3) return `${String(tokens)}t`;
|
|
5217
|
+
const thousands = tokens / 1e3;
|
|
5218
|
+
return `${thousands < 10 ? thousands.toFixed(1) : String(Math.round(thousands))}kt`;
|
|
5219
|
+
}
|
|
4978
5220
|
function formatCost(cost) {
|
|
4979
5221
|
if (!cost) return "";
|
|
4980
5222
|
return cost < 0.01 ? `$${cost.toFixed(3)}` : `$${cost.toFixed(2)}`;
|
|
4981
5223
|
}
|
|
5224
|
+
function visibleLength(text) {
|
|
5225
|
+
return visibleWidth(text);
|
|
5226
|
+
}
|
|
5227
|
+
function truncate(text, limit) {
|
|
5228
|
+
return `${truncateToWidth(text, limit, "\u2026")}${RESET}`;
|
|
5229
|
+
}
|
|
5230
|
+
function drawFrame(title, hint, body, width, paint, accent, scrollbar = () => void 0) {
|
|
5231
|
+
const inner = Math.max(20, width - 4);
|
|
5232
|
+
const lines = body.map((row, index) => {
|
|
5233
|
+
const right = row.right ?? "";
|
|
5234
|
+
const rightWidth = right === "" ? 0 : visibleLength(right) + 2;
|
|
5235
|
+
const room = Math.max(0, inner - rightWidth);
|
|
5236
|
+
const left = visibleLength(row.text) > room ? truncate(row.text, room) : row.text;
|
|
5237
|
+
const gap = Math.max(0, inner - visibleLength(left) - (right === "" ? 0 : visibleLength(right)));
|
|
5238
|
+
const text = right === "" ? left + " ".repeat(gap) : `${left}${" ".repeat(gap)}${paint(ROLE.quiet, right)}`;
|
|
5239
|
+
const rule = accent("\u2502");
|
|
5240
|
+
const bar = scrollbar(index);
|
|
5241
|
+
return `${rule} ${text} ${bar === void 0 ? rule : accent(bar)}`;
|
|
5242
|
+
});
|
|
5243
|
+
const label2 = ` ${title} `;
|
|
5244
|
+
const tail = hint === "" ? "" : ` ${hint} `;
|
|
5245
|
+
const spare = inner + 2 - visibleLength(label2) - visibleLength(tail);
|
|
5246
|
+
const heading = spare >= 2 ? accent(`\u256D${label2}${"\u2500".repeat(spare)}${tail}\u256E`) : accent(`\u256D${label2}${"\u2500".repeat(Math.max(0, inner + 2 - visibleLength(label2)))}\u256E`);
|
|
5247
|
+
const foot = accent(`\u2570${"\u2500".repeat(inner + 2)}\u256F`);
|
|
5248
|
+
return [heading, ...lines, foot];
|
|
5249
|
+
}
|
|
4982
5250
|
|
|
4983
5251
|
// src/host-view.ts
|
|
4984
5252
|
var PLAIN_WORKFLOW_PROGRESS_STYLES = { accent: (text) => text, success: (text) => text, error: (text) => text, warning: (text) => text, muted: (text) => text, dim: (text) => text, bold: (text) => text };
|
|
5253
|
+
var RUN_STATE_GLYPH = { "not started": "\u25CB", queued: "\u25CB", pausing: "\u23F8", paused: "\u23F8", completed: "\u2713", failed: "\u2717", stopped: "\u2717", interrupted: "\u21AF", budget_exhausted: "!", awaiting_input: "?" };
|
|
5254
|
+
function runStateGlyph(state, running) {
|
|
5255
|
+
return state === "running" ? running : RUN_STATE_GLYPH[state] ?? "\u25C6";
|
|
5256
|
+
}
|
|
5257
|
+
var PROGRESS_STATE_STYLE = { completed: "success", failed: "error", cancelled: "error", running: "accent", paused: "warning", pausing: "warning", interrupted: "warning", retrying: "accent", budget_exhausted: "warning", awaiting_input: "warning" };
|
|
4985
5258
|
var PHASE_STATE_STYLE = { completed: "success", failed: "error", cancelled: "error", stopped: "error", running: "accent", paused: "warning", pausing: "warning", interrupted: "warning", budget_exhausted: "warning" };
|
|
4986
5259
|
function styleForState(map, state, styles) {
|
|
4987
5260
|
const key = map[state] ?? "muted";
|
|
4988
5261
|
return (text) => styles[key](text);
|
|
4989
5262
|
}
|
|
5263
|
+
function progressStyleForState(state, styles) {
|
|
5264
|
+
return styleForState(PROGRESS_STATE_STYLE, state, styles);
|
|
5265
|
+
}
|
|
4990
5266
|
function phaseStyleForState(state, styles) {
|
|
4991
5267
|
return styleForState(PHASE_STATE_STYLE, state, styles);
|
|
4992
5268
|
}
|
|
@@ -5000,6 +5276,15 @@ function formatWorkflowRuntime(durationMs) {
|
|
|
5000
5276
|
const remainingMinutes = minutes % 60;
|
|
5001
5277
|
return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
|
|
5002
5278
|
}
|
|
5279
|
+
function textBlock(text) {
|
|
5280
|
+
return {
|
|
5281
|
+
render(width) {
|
|
5282
|
+
return text.split("\n").map((line) => truncateToWidth2(line, Math.max(1, width), "\u2026"));
|
|
5283
|
+
},
|
|
5284
|
+
invalidate() {
|
|
5285
|
+
}
|
|
5286
|
+
};
|
|
5287
|
+
}
|
|
5003
5288
|
var ANSI_SGR_SOURCE = `${String.fromCharCode(27)}\\[[0-9;]*m`;
|
|
5004
5289
|
var ANSI_SGR = new RegExp(ANSI_SGR_SOURCE);
|
|
5005
5290
|
function themeWorkflowProgressStyles(theme) {
|
|
@@ -5043,13 +5328,13 @@ function elapsedDurationMs(agent, end) {
|
|
|
5043
5328
|
return agent.startedAt === void 0 || !Number.isFinite(agent.startedAt) ? void 0 : Math.max(0, end - agent.startedAt);
|
|
5044
5329
|
}
|
|
5045
5330
|
function agentActivityLabel(agent) {
|
|
5046
|
-
const
|
|
5047
|
-
if (
|
|
5048
|
-
const name =
|
|
5049
|
-
const text = sanitizeDisplayText(
|
|
5331
|
+
const activity = agent.activity;
|
|
5332
|
+
if (activity?.kind === "reasoning" || activity?.kind === "text") {
|
|
5333
|
+
const name = activity.kind === "reasoning" ? "reasoning" : "responding";
|
|
5334
|
+
const text = sanitizeDisplayText(activity.text);
|
|
5050
5335
|
return text && text !== name ? `${name} \xB7 ${text}` : name;
|
|
5051
5336
|
}
|
|
5052
|
-
if (
|
|
5337
|
+
if (activity?.kind === "tool") return sanitizeDisplayText(activity.text);
|
|
5053
5338
|
return sanitizeDisplayText([...agent.toolCalls ?? []].reverse().find(({ state }) => state === "running")?.name ?? "");
|
|
5054
5339
|
}
|
|
5055
5340
|
function formatAccountingValue(value) {
|
|
@@ -5101,16 +5386,96 @@ function visibleStandaloneAgentAttemptActions(actions, context) {
|
|
|
5101
5386
|
}
|
|
5102
5387
|
});
|
|
5103
5388
|
}
|
|
5104
|
-
function formatAgentAccounting(
|
|
5105
|
-
const total =
|
|
5106
|
-
return [`Tokens: \u2211${formatAccountingValue(total)} \u2191${formatAccountingValue(
|
|
5389
|
+
function formatAgentAccounting(accounting2) {
|
|
5390
|
+
const total = accounting2.input + accounting2.output + accounting2.cacheRead + accounting2.cacheWrite;
|
|
5391
|
+
return [`Tokens: \u2211${formatAccountingValue(total)} \u2191${formatAccountingValue(accounting2.input)} \u2193${formatAccountingValue(accounting2.output)} \u21E2${formatAccountingValue(accounting2.cacheRead)} \u21E0${formatAccountingValue(accounting2.cacheWrite)}`, `Cost: ${formatCost(accounting2.cost) || "$0.00"}`];
|
|
5392
|
+
}
|
|
5393
|
+
function wrapNavigatorLine(text, width) {
|
|
5394
|
+
return truncateToVisualLines(text, Number.MAX_SAFE_INTEGER, Math.max(1, width), 0).visualLines.map((line) => line.trimEnd());
|
|
5395
|
+
}
|
|
5396
|
+
function formatNavigatorColumns(sidebar, detail, width, layout = {}) {
|
|
5397
|
+
const safeWidth = Math.max(1, width);
|
|
5398
|
+
if (safeWidth >= 80) {
|
|
5399
|
+
const sidebarWidth = Math.min(42, Math.max(24, Math.floor((safeWidth - 3) * 0.38)));
|
|
5400
|
+
const left2 = sidebar.flatMap((line) => wrapNavigatorLine(line, sidebarWidth));
|
|
5401
|
+
const right = detail.flatMap((line) => wrapNavigatorLine(line, safeWidth - sidebarWidth - 3));
|
|
5402
|
+
return Array.from({ length: Math.max(left2.length, right.length) }, (_, index) => {
|
|
5403
|
+
const cell = left2[index] ?? "";
|
|
5404
|
+
return `${cell}${" ".repeat(Math.max(0, sidebarWidth - visibleWidth2(cell)))} | ${right[index] ?? ""}`;
|
|
5405
|
+
});
|
|
5406
|
+
}
|
|
5407
|
+
if (layout.detailsOnly) return detail.flatMap((line) => wrapNavigatorLine(line, safeWidth));
|
|
5408
|
+
const left = sidebar.flatMap((line) => wrapNavigatorLine(line, safeWidth));
|
|
5409
|
+
return layout.treeOnly ? left : [...left, ...detail.flatMap((line) => wrapNavigatorLine(line, safeWidth))];
|
|
5107
5410
|
}
|
|
5108
5411
|
|
|
5109
5412
|
// src/retention.ts
|
|
5110
5413
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
5111
5414
|
|
|
5415
|
+
// src/host-navigator.ts
|
|
5416
|
+
function isKeybindingGetKeys(value) {
|
|
5417
|
+
return typeof value === "function";
|
|
5418
|
+
}
|
|
5419
|
+
function keybindingKeys(keybindings, name) {
|
|
5420
|
+
if (!object(keybindings)) return void 0;
|
|
5421
|
+
const getKeys = keybindings.getKeys;
|
|
5422
|
+
return isKeybindingGetKeys(getKeys) ? getKeys.call(keybindings, name) : void 0;
|
|
5423
|
+
}
|
|
5424
|
+
var WORKFLOW_VIM_KEYS = { "tui.select.up": "k", "tui.select.down": "j", "tui.editor.cursorLeft": "h", "tui.editor.cursorRight": "l" };
|
|
5425
|
+
function workflowKeyMatches(keybindings, data, binding) {
|
|
5426
|
+
return keybindings.matches(data, binding) || WORKFLOW_VIM_KEYS[binding] === data;
|
|
5427
|
+
}
|
|
5428
|
+
var WORKFLOW_KEY_LABELS = { up: "\u2191", down: "\u2193", left: "\u2190", right: "\u2192", pageUp: "pgup", pageDown: "pgdn" };
|
|
5429
|
+
function workflowKeyLabel(keybindings, binding, fallback, labels = WORKFLOW_KEY_LABELS) {
|
|
5430
|
+
const keys = keybindingKeys(keybindings, binding);
|
|
5431
|
+
const configured = keys?.length ? keys.map((key) => labels[key] ?? key) : [fallback];
|
|
5432
|
+
const vim = WORKFLOW_VIM_KEYS[binding];
|
|
5433
|
+
return [...new Set(vim ? [...configured, vim] : configured)].join("/");
|
|
5434
|
+
}
|
|
5435
|
+
function createInlineConfirm(requestRender) {
|
|
5436
|
+
let pending;
|
|
5437
|
+
const answer = (value) => {
|
|
5438
|
+
const current = pending;
|
|
5439
|
+
pending = void 0;
|
|
5440
|
+
current?.resolve(value);
|
|
5441
|
+
requestRender();
|
|
5442
|
+
};
|
|
5443
|
+
return {
|
|
5444
|
+
ask(title, message) {
|
|
5445
|
+
answer(false);
|
|
5446
|
+
return new Promise((resolve6) => {
|
|
5447
|
+
pending = { title, message, yes: true, resolve: resolve6 };
|
|
5448
|
+
requestRender();
|
|
5449
|
+
});
|
|
5450
|
+
},
|
|
5451
|
+
active() {
|
|
5452
|
+
return pending !== void 0;
|
|
5453
|
+
},
|
|
5454
|
+
rows(width, styles) {
|
|
5455
|
+
if (!pending) return [];
|
|
5456
|
+
const yes = pending.yes;
|
|
5457
|
+
const message = truncateToVisualLines2(pending.message, Number.MAX_SAFE_INTEGER, Math.max(1, width), 0).visualLines.map((line) => line.trimEnd());
|
|
5458
|
+
return [styles.bold(pending.title), ...message, ...["Yes", "No"].map((option) => option === "Yes" === yes ? `\u2192 ${styles.accent(option)}` : ` ${option}`)];
|
|
5459
|
+
},
|
|
5460
|
+
hint(keybindings) {
|
|
5461
|
+
return `${workflowKeyLabel(keybindings, "tui.select.up", "\u2191")}/${workflowKeyLabel(keybindings, "tui.select.down", "\u2193")} select \xB7 ${workflowKeyLabel(keybindings, "tui.select.confirm", "enter")} confirm \xB7 ${workflowKeyLabel(keybindings, "tui.select.cancel", "esc")} cancel`;
|
|
5462
|
+
},
|
|
5463
|
+
handleInput(keybindings, data) {
|
|
5464
|
+
if (!pending) return;
|
|
5465
|
+
if (workflowKeyMatches(keybindings, data, "tui.select.up") || workflowKeyMatches(keybindings, data, "tui.select.down")) {
|
|
5466
|
+
pending.yes = !pending.yes;
|
|
5467
|
+
requestRender();
|
|
5468
|
+
} else if (workflowKeyMatches(keybindings, data, "tui.select.confirm")) answer(pending.yes);
|
|
5469
|
+
else if (workflowKeyMatches(keybindings, data, "tui.select.cancel")) answer(false);
|
|
5470
|
+
},
|
|
5471
|
+
cancel() {
|
|
5472
|
+
answer(false);
|
|
5473
|
+
}
|
|
5474
|
+
};
|
|
5475
|
+
}
|
|
5476
|
+
|
|
5112
5477
|
// src/trajectory.ts
|
|
5113
|
-
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager as DefaultPackageManager2, getAgentDir as
|
|
5478
|
+
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager as DefaultPackageManager2, getAgentDir as getAgentDir4, SettingsManager as SettingsManager3, DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES2 } from "@earendil-works/pi-coding-agent";
|
|
5114
5479
|
|
|
5115
5480
|
// subagents/src/contracts.ts
|
|
5116
5481
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
@@ -5145,7 +5510,8 @@ var SUBAGENTS_RUN_PARAMETERS = Type2.Object({
|
|
|
5145
5510
|
timeoutMs: Type2.Optional(Type2.Union([Type2.Integer({ minimum: 1, description: "Optional execution timeout in milliseconds" }), Type2.Null()]))
|
|
5146
5511
|
}, { additionalProperties: false });
|
|
5147
5512
|
var SUBAGENTS_INSPECT_PARAMETERS = Type2.Object({
|
|
5148
|
-
id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" }))
|
|
5513
|
+
id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" })),
|
|
5514
|
+
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" }))
|
|
5149
5515
|
}, { additionalProperties: false });
|
|
5150
5516
|
var SUBAGENTS_ID_PARAMETERS = Type2.Object({
|
|
5151
5517
|
id: Type2.String({ description: "Subagent ID" })
|
|
@@ -5160,13 +5526,21 @@ var SUBAGENTS_STOP_PARAMETERS = Type2.Object({
|
|
|
5160
5526
|
var SUBAGENTS_RETRY_PARAMETERS = Type2.Object({
|
|
5161
5527
|
id: Type2.String({ description: "Failed or stopped subagent ID to retry" })
|
|
5162
5528
|
}, { additionalProperties: false });
|
|
5163
|
-
var
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5529
|
+
var SUBAGENT_STATUS_OUTPUT = Type2.Object({
|
|
5530
|
+
id: Type2.String(),
|
|
5531
|
+
state: Type2.String(),
|
|
5532
|
+
sessionId: Type2.Optional(Type2.String()),
|
|
5533
|
+
startedAt: Type2.Optional(Type2.Number()),
|
|
5534
|
+
finishedAt: Type2.Optional(Type2.Number()),
|
|
5535
|
+
attempts: Type2.Optional(Type2.Number()),
|
|
5536
|
+
attemptDetails: Type2.Optional(Type2.Array(Type2.Unknown())),
|
|
5537
|
+
worktree: Type2.Optional(Type2.Object({ path: Type2.String(), branch: Type2.String() })),
|
|
5538
|
+
error: Type2.Optional(Type2.Object({ code: Type2.String(), message: Type2.String() })),
|
|
5539
|
+
progress: Type2.Optional(Type2.Unknown()),
|
|
5540
|
+
value: Type2.Optional(Type2.Unknown())
|
|
5541
|
+
});
|
|
5542
|
+
var SUBAGENTS_INSPECT_OUTPUT = Type2.Union([SUBAGENT_STATUS_OUTPUT, Type2.Array(Type2.Object({ id: Type2.String(), state: Type2.String() }))]);
|
|
5543
|
+
var SUBAGENTS_STEER_OUTPUT = Type2.Object({ id: Type2.String(), accepted: Type2.Boolean() });
|
|
5170
5544
|
function normalizeSubagentRunRequest(value) {
|
|
5171
5545
|
if (!Value2.Check(SUBAGENTS_RUN_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_run parameters");
|
|
5172
5546
|
if (typeof value.worktree === "string" && !value.worktree.trim()) throw new WorkflowError("INVALID_METADATA", "worktree name must be a non-empty string");
|
|
@@ -5214,14 +5588,14 @@ function accountingValue(value) {
|
|
|
5214
5588
|
return { input, output, cacheRead, cacheWrite, cost };
|
|
5215
5589
|
}
|
|
5216
5590
|
function legacyAccountingValue(record2) {
|
|
5217
|
-
const
|
|
5218
|
-
if (record2.accounting !== void 0 && !
|
|
5591
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5592
|
+
if (record2.accounting !== void 0 && !accounting2) return void 0;
|
|
5219
5593
|
const usage = record2.usage;
|
|
5220
|
-
if (usage === void 0) return
|
|
5594
|
+
if (usage === void 0) return accounting2;
|
|
5221
5595
|
const usageRecord = objectValue(usage);
|
|
5222
5596
|
const tokens = objectValue(usageRecord?.tokens);
|
|
5223
5597
|
if (!usageRecord || !tokens || !finiteNumber(tokens.input) || !finiteNumber(tokens.output) || !finiteNumber(tokens.cacheRead) || !finiteNumber(tokens.cacheWrite) || !finiteNumber(tokens.total) || !finiteNumber(usageRecord.cost)) return void 0;
|
|
5224
|
-
return
|
|
5598
|
+
return accounting2 ?? { input: tokens.input, output: tokens.output, cacheRead: tokens.cacheRead, cacheWrite: tokens.cacheWrite, cost: usageRecord.cost };
|
|
5225
5599
|
}
|
|
5226
5600
|
function activityValue(value) {
|
|
5227
5601
|
const record2 = objectValue(value);
|
|
@@ -5255,11 +5629,11 @@ function worktreeValue(value) {
|
|
|
5255
5629
|
function progressValue(value) {
|
|
5256
5630
|
const record2 = objectValue(value);
|
|
5257
5631
|
if (!record2) return void 0;
|
|
5258
|
-
const
|
|
5632
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5259
5633
|
const toolCalls = toolCallsValue(record2.toolCalls);
|
|
5260
|
-
if (!
|
|
5261
|
-
const
|
|
5262
|
-
if (record2.activity !== void 0 &&
|
|
5634
|
+
if (!accounting2 || !toolCalls) return void 0;
|
|
5635
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5636
|
+
if (record2.activity !== void 0 && activity === void 0) return void 0;
|
|
5263
5637
|
const rawState = record2.state;
|
|
5264
5638
|
let state;
|
|
5265
5639
|
if (rawState !== void 0) {
|
|
@@ -5273,16 +5647,16 @@ function progressValue(value) {
|
|
|
5273
5647
|
}
|
|
5274
5648
|
const lastEventAt = record2.lastEventAt;
|
|
5275
5649
|
if (lastEventAt !== void 0 && !nonnegativeInteger(lastEventAt)) return void 0;
|
|
5276
|
-
return { accounting:
|
|
5650
|
+
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...activity === void 0 ? {} : { activity }, ...lastEventAt === void 0 ? {} : { lastEventAt } };
|
|
5277
5651
|
}
|
|
5278
5652
|
function legacyProgressValue(record2) {
|
|
5279
|
-
const
|
|
5653
|
+
const accounting2 = legacyAccountingValue(record2);
|
|
5280
5654
|
const toolCalls = record2.toolCalls === void 0 ? [] : toolCallsValue(record2.toolCalls);
|
|
5281
|
-
if (!
|
|
5282
|
-
const
|
|
5655
|
+
if (!accounting2 || !toolCalls) return void 0;
|
|
5656
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5283
5657
|
const lastEventAt = record2.lastEventAt;
|
|
5284
|
-
if (record2.activity !== void 0 &&
|
|
5285
|
-
return { accounting:
|
|
5658
|
+
if (record2.activity !== void 0 && activity === void 0 || lastEventAt !== void 0 && !nonnegativeInteger(lastEventAt)) return void 0;
|
|
5659
|
+
return { accounting: accounting2, toolCalls, ...activity === void 0 ? {} : { activity }, ...lastEventAt === void 0 ? {} : { lastEventAt } };
|
|
5286
5660
|
}
|
|
5287
5661
|
function attemptValue(value) {
|
|
5288
5662
|
const record2 = objectValue(value);
|
|
@@ -5291,16 +5665,16 @@ function attemptValue(value) {
|
|
|
5291
5665
|
const hookNames = stringArrayValue(setup?.hookNames);
|
|
5292
5666
|
const tools = stringArrayValue(setup?.tools);
|
|
5293
5667
|
const resources = setup?.resourceSelectors === void 0 ? void 0 : resourceSummaryValue(setup.resourceSelectors);
|
|
5294
|
-
const
|
|
5668
|
+
const accounting2 = accountingValue(record2?.accounting);
|
|
5295
5669
|
const session = record2?.session === void 0 ? void 0 : sessionReferenceValue(record2.session);
|
|
5296
5670
|
const error = record2?.error === void 0 ? void 0 : subagentErrorValue(record2.error);
|
|
5297
5671
|
const thinking = parseThinking(model?.thinking);
|
|
5298
|
-
if (!record2 || !nonnegativeInteger(record2.attempt) || record2.attempt < 1 || typeof record2.transport !== "string" || !record2.transport.trim() || !setup || typeof setup.cwd !== "string" || !setup.cwd.trim() || !model || typeof model.provider !== "string" || !model.provider.trim() || typeof model.model !== "string" || !model.model.trim() || model.thinking !== void 0 && thinking === void 0 || !hookNames || !tools || setup.resourceSelectors !== void 0 && resources === void 0 || !
|
|
5672
|
+
if (!record2 || !nonnegativeInteger(record2.attempt) || record2.attempt < 1 || typeof record2.transport !== "string" || !record2.transport.trim() || !setup || typeof setup.cwd !== "string" || !setup.cwd.trim() || !model || typeof model.provider !== "string" || !model.provider.trim() || typeof model.model !== "string" || !model.model.trim() || model.thinking !== void 0 && thinking === void 0 || !hookNames || !tools || setup.resourceSelectors !== void 0 && resources === void 0 || !accounting2 || record2.session !== void 0 && session === void 0 || record2.error !== void 0 && error === void 0) return void 0;
|
|
5299
5673
|
return {
|
|
5300
5674
|
attempt: record2.attempt,
|
|
5301
5675
|
transport: record2.transport,
|
|
5302
5676
|
setup: { hookNames, model: { provider: model.provider, model: model.model, ...thinking === void 0 ? {} : { thinking } }, tools, cwd: setup.cwd, ...resources === void 0 ? {} : { resourceSelectors: resources } },
|
|
5303
|
-
accounting:
|
|
5677
|
+
accounting: accounting2,
|
|
5304
5678
|
...session === void 0 ? {} : { session },
|
|
5305
5679
|
...error === void 0 ? {} : { error }
|
|
5306
5680
|
};
|
|
@@ -5366,15 +5740,29 @@ var WORKFLOW_TOOL_PARAMETERS = Type3.Object({
|
|
|
5366
5740
|
}, { additionalProperties: false });
|
|
5367
5741
|
var WORKFLOW_STATUS_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Workflow run ID visible in the current project" }) }, { additionalProperties: false });
|
|
5368
5742
|
var WORKFLOW_RETRY_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Explicit failed workflow run ID" }), expectedState: Type3.Optional(Type3.String({ description: "Persisted source state observed before recovery" })), foreground: Type3.Optional(Type3.Boolean({ description: "Override the source launch mode for this recovery" })) });
|
|
5743
|
+
var WORKFLOW_ERROR_OUTPUT = Type3.Object({ code: Type3.String(), message: Type3.String() });
|
|
5744
|
+
var WORKFLOW_LAUNCH_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), detached: Type3.Optional(Type3.Boolean()), value: Type3.Optional(Type3.Unknown()) });
|
|
5745
|
+
var WORKFLOW_STATUS_OUTPUT = Type3.Object({ runId: Type3.String(), workflowName: Type3.String(), state: Type3.String(), error: Type3.Optional(WORKFLOW_ERROR_OUTPUT), failedAt: Type3.Optional(Type3.String()), budget: Type3.Optional(Type3.Unknown()), usage: Type3.Optional(Type3.Unknown()), phase: Type3.Optional(Type3.String()), delivery: Type3.Optional(Type3.Object({ mode: Type3.String(), state: Type3.String() })), agents: Type3.Array(Type3.Object({ id: Type3.String(), label: Type3.Optional(Type3.String()), path: Type3.String(), state: Type3.String(), lastEventAt: Type3.Optional(Type3.Number()), accounting: Type3.Optional(Type3.Unknown()) })) });
|
|
5746
|
+
var WORKFLOW_STOP_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), stopped: Type3.Boolean(), reason: Type3.Optional(Type3.String()) });
|
|
5747
|
+
var WORKFLOW_RECOVERY_OUTPUT = Type3.Object({ state: Type3.String(), runId: Type3.Optional(Type3.String()), parentRunId: Type3.Optional(Type3.String()), proposalId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
|
|
5748
|
+
var WORKFLOW_RESPOND_OUTPUT = Type3.Object({ state: Type3.String(), approved: Type3.Boolean(), reason: Type3.String(), accepted: Type3.Optional(Type3.Boolean()), runId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
|
|
5749
|
+
var WORKFLOW_CATALOG_ALIAS_OUTPUT = Type3.Object({ name: Type3.String(), kind: Type3.String(), provenance: Type3.String(), version: Type3.Optional(Type3.String()), headline: Type3.Optional(Type3.String()) });
|
|
5750
|
+
var WORKFLOW_CATALOG_OUTPUT = Type3.Union([
|
|
5751
|
+
Type3.Object({ functions: Type3.Array(Type3.Object({ name: Type3.String(), description: Type3.String(), input: Type3.Unknown() })), modelAliasEntries: Type3.Optional(Type3.Array(WORKFLOW_CATALOG_ALIAS_OUTPUT)), settings: Type3.Optional(Type3.Unknown()) }),
|
|
5752
|
+
Type3.Object({ name: Type3.String(), version: Type3.String(), headline: Type3.String(), description: Type3.String(), input: Type3.Unknown(), output: Type3.Unknown() }),
|
|
5753
|
+
WORKFLOW_CATALOG_ALIAS_OUTPUT,
|
|
5754
|
+
Type3.Object({ error: Type3.Object({ code: Type3.String(), name: Type3.String(), message: Type3.String() }) })
|
|
5755
|
+
]);
|
|
5756
|
+
var SCRIPT_CALL_TEXT_LIMIT_BYTES = 16 * 1024;
|
|
5369
5757
|
|
|
5370
5758
|
// subagents/src/manager.ts
|
|
5371
5759
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
5372
5760
|
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";
|
|
5373
|
-
import { join as
|
|
5374
|
-
import { getAgentDir as
|
|
5761
|
+
import { join as join9 } from "node:path";
|
|
5762
|
+
import { getAgentDir as getAgentDir6 } from "@earendil-works/pi-coding-agent";
|
|
5375
5763
|
|
|
5376
5764
|
// subagents/src/worktree.ts
|
|
5377
|
-
import { join as
|
|
5765
|
+
import { join as join8 } from "node:path";
|
|
5378
5766
|
function syntheticRun(context) {
|
|
5379
5767
|
return {
|
|
5380
5768
|
id: context.runId,
|
|
@@ -5436,7 +5824,7 @@ function createRunStoreWorktreeAdapter(home) {
|
|
|
5436
5824
|
};
|
|
5437
5825
|
}
|
|
5438
5826
|
function defaultWorktreeHome(storageDirectory3) {
|
|
5439
|
-
return
|
|
5827
|
+
return join8(storageDirectory3, ".worktrees");
|
|
5440
5828
|
}
|
|
5441
5829
|
|
|
5442
5830
|
// subagents/src/manager.ts
|
|
@@ -5489,7 +5877,7 @@ function validateStandaloneExtensionSources(context, external, registry, agentDi
|
|
|
5489
5877
|
function executionRoot(context, dependencies, signal, runId, worktree, external) {
|
|
5490
5878
|
const extensionContext = context.extensionContext;
|
|
5491
5879
|
const model = rootModel(context);
|
|
5492
|
-
const agentDir = dependencies.agentDir ??
|
|
5880
|
+
const agentDir = dependencies.agentDir ?? getAgentDir6();
|
|
5493
5881
|
const currentTrustedProject = extensionContext.isProjectTrusted();
|
|
5494
5882
|
if (external.projectTrusted && !currentTrustedProject) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
|
|
5495
5883
|
const trustedProject = currentTrustedProject;
|
|
@@ -5569,25 +5957,25 @@ function executionOptions(request, onAttempt, onProgress) {
|
|
|
5569
5957
|
};
|
|
5570
5958
|
}
|
|
5571
5959
|
function storageDirectory(dependencies) {
|
|
5572
|
-
return dependencies.storageDir ??
|
|
5960
|
+
return dependencies.storageDir ?? join9(dependencies.agentDir ?? getAgentDir6(), STORAGE_DIRECTORY);
|
|
5573
5961
|
}
|
|
5574
5962
|
function runDirectory(root, id) {
|
|
5575
|
-
return
|
|
5963
|
+
return join9(root, id);
|
|
5576
5964
|
}
|
|
5577
5965
|
function requestPath(directory) {
|
|
5578
|
-
return
|
|
5966
|
+
return join9(directory, "request.json");
|
|
5579
5967
|
}
|
|
5580
5968
|
function configurationPath(directory) {
|
|
5581
|
-
return
|
|
5969
|
+
return join9(directory, CONFIGURATION_FILE);
|
|
5582
5970
|
}
|
|
5583
5971
|
function statusPath(directory) {
|
|
5584
|
-
return
|
|
5972
|
+
return join9(directory, "status.json");
|
|
5585
5973
|
}
|
|
5586
5974
|
function resultPath(directory) {
|
|
5587
|
-
return
|
|
5975
|
+
return join9(directory, "result.json");
|
|
5588
5976
|
}
|
|
5589
5977
|
function failurePath(directory) {
|
|
5590
|
-
return
|
|
5978
|
+
return join9(directory, "failure.json");
|
|
5591
5979
|
}
|
|
5592
5980
|
async function secureDirectory(directory) {
|
|
5593
5981
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
@@ -5670,7 +6058,7 @@ async function releaseOwnerMarker(path, token) {
|
|
|
5670
6058
|
}
|
|
5671
6059
|
async function acquireStorageOwner(root, owner, liveness) {
|
|
5672
6060
|
await secureDirectory(root);
|
|
5673
|
-
const path =
|
|
6061
|
+
const path = join9(root, OWNER_FILE);
|
|
5674
6062
|
for (let attempt = 0; attempt < MAX_STORAGE_OWNER_ATTEMPTS; attempt += 1) {
|
|
5675
6063
|
const marker = { ...owner, acquiredAt: Date.now() };
|
|
5676
6064
|
try {
|
|
@@ -5757,7 +6145,7 @@ function decodePersistedStandaloneExternalConfiguration(value) {
|
|
|
5757
6145
|
function standaloneExternalConfiguration(context, dependencies) {
|
|
5758
6146
|
const cwd = context.extensionContext.cwd;
|
|
5759
6147
|
const trustedProject = context.extensionContext.isProjectTrusted();
|
|
5760
|
-
const agentDir = dependencies.agentDir ??
|
|
6148
|
+
const agentDir = dependencies.agentDir ?? getAgentDir6();
|
|
5761
6149
|
const resolution = resolveWorkflowSettings(cwd, trustedProject, workflowSettingsPath(agentDir));
|
|
5762
6150
|
const globalExtensionSettings = resolution.global.extensionSettings;
|
|
5763
6151
|
const projectExtensionSettings = resolution.project.extensionSettings;
|
|
@@ -5923,15 +6311,15 @@ function attemptSummaryValue(value) {
|
|
|
5923
6311
|
const transport = record2.transport;
|
|
5924
6312
|
const session = record2.session === void 0 ? void 0 : sessionReferenceValue2(record2.session);
|
|
5925
6313
|
const setup = setupSummaryValue(record2.setup);
|
|
5926
|
-
const
|
|
6314
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5927
6315
|
const error = record2.error === void 0 ? void 0 : decodeFailure(record2.error);
|
|
5928
|
-
if (typeof attempt !== "number" || !Number.isSafeInteger(attempt) || attempt < 1 || typeof transport !== "string" || !transport.trim() || !setup || !
|
|
6316
|
+
if (typeof attempt !== "number" || !Number.isSafeInteger(attempt) || attempt < 1 || typeof transport !== "string" || !transport.trim() || !setup || !accounting2 || record2.session !== void 0 && !session || record2.error !== void 0 && !error) return void 0;
|
|
5929
6317
|
return {
|
|
5930
6318
|
attempt,
|
|
5931
6319
|
transport: boundedAttemptText(transport),
|
|
5932
6320
|
...session === void 0 ? {} : { session },
|
|
5933
6321
|
...error === void 0 ? {} : { error: { code: boundedAttemptText(error.code), message: boundedAttemptText(error.message) } },
|
|
5934
|
-
accounting: { ...
|
|
6322
|
+
accounting: { ...accounting2 },
|
|
5935
6323
|
setup: boundedAttemptSetup(setup)
|
|
5936
6324
|
};
|
|
5937
6325
|
}
|
|
@@ -5951,8 +6339,8 @@ function isPreparedSession(value) {
|
|
|
5951
6339
|
const textFields = ["initialPrompt", "agentDir", "systemPromptPath", "systemPromptAppend", "piRuntimeError"];
|
|
5952
6340
|
if (textFields.some((field) => record2[field] !== void 0 && typeof record2[field] !== "string")) return false;
|
|
5953
6341
|
if (record2.systemPrompt !== void 0 && (typeof record2.systemPrompt !== "string" || record2.systemPrompt.length > SUBAGENT_SYSTEM_PROMPT_LIMIT)) return false;
|
|
5954
|
-
const
|
|
5955
|
-
if (record2.piRuntime !== void 0 && (!
|
|
6342
|
+
const runtime = recordValue(record2.piRuntime);
|
|
6343
|
+
if (record2.piRuntime !== void 0 && (!runtime || typeof runtime.executable !== "string" || !runtime.executable.trim() || runtime.entrypoint !== void 0 && (typeof runtime.entrypoint !== "string" || !runtime.entrypoint.trim()))) return false;
|
|
5956
6344
|
return true;
|
|
5957
6345
|
}
|
|
5958
6346
|
function isLiveSessionHandoff(value) {
|
|
@@ -5982,23 +6370,23 @@ function sessionStateValue(value) {
|
|
|
5982
6370
|
function progressValue2(value) {
|
|
5983
6371
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
5984
6372
|
const record2 = value;
|
|
5985
|
-
const
|
|
6373
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5986
6374
|
const toolCalls = toolCallsValue2(record2.toolCalls);
|
|
5987
6375
|
const state = record2.state === void 0 ? void 0 : sessionStateValue(record2.state);
|
|
5988
|
-
if (!
|
|
5989
|
-
const
|
|
5990
|
-
if (record2.activity !== void 0 &&
|
|
6376
|
+
if (!accounting2 || !toolCalls || record2.state !== void 0 && state === void 0) return void 0;
|
|
6377
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
6378
|
+
if (record2.activity !== void 0 && activity === void 0) return void 0;
|
|
5991
6379
|
if (record2.lastEventAt !== void 0 && (!Number.isSafeInteger(record2.lastEventAt) || record2.lastEventAt < 0)) return void 0;
|
|
5992
|
-
return { accounting:
|
|
6380
|
+
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...activity === void 0 ? {} : { activity }, ...record2.lastEventAt === void 0 ? {} : { lastEventAt: record2.lastEventAt } };
|
|
5993
6381
|
}
|
|
5994
6382
|
function legacyProgressValue2(record2) {
|
|
5995
|
-
const
|
|
6383
|
+
const accounting2 = legacyAccountingValue(record2);
|
|
5996
6384
|
const toolCalls = record2.toolCalls === void 0 ? [] : toolCallsValue2(record2.toolCalls);
|
|
5997
|
-
if (!
|
|
5998
|
-
const
|
|
5999
|
-
if (record2.activity !== void 0 &&
|
|
6385
|
+
if (!accounting2 || !toolCalls) return void 0;
|
|
6386
|
+
const activity = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
6387
|
+
if (record2.activity !== void 0 && activity === void 0) return void 0;
|
|
6000
6388
|
if (record2.lastEventAt !== void 0 && (!Number.isSafeInteger(record2.lastEventAt) || record2.lastEventAt < 0)) return void 0;
|
|
6001
|
-
return { accounting:
|
|
6389
|
+
return { accounting: accounting2, toolCalls, ...activity === void 0 ? {} : { activity }, ...record2.lastEventAt === void 0 ? {} : { lastEventAt: record2.lastEventAt } };
|
|
6002
6390
|
}
|
|
6003
6391
|
function worktreeContextValue(value) {
|
|
6004
6392
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -6310,7 +6698,7 @@ var PersistentSubagentManager = class {
|
|
|
6310
6698
|
if (!sessionId.trim()) throw new WorkflowError("INTERNAL_ERROR", "Current Pi session identity is unavailable");
|
|
6311
6699
|
const owner = this.runOwner;
|
|
6312
6700
|
if (!owner) throw new WorkflowError("INTERNAL_ERROR", "Subagent storage owner identity is unavailable");
|
|
6313
|
-
const settings = resolveWorkflowSettings(context.extensionContext.cwd, context.extensionContext.isProjectTrusted(), workflowSettingsPath(this.dependencies.agentDir ??
|
|
6701
|
+
const settings = resolveWorkflowSettings(context.extensionContext.cwd, context.extensionContext.isProjectTrusted(), workflowSettingsPath(this.dependencies.agentDir ?? getAgentDir6()));
|
|
6314
6702
|
const concurrency = settings.effective.concurrency;
|
|
6315
6703
|
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.`);
|
|
6316
6704
|
this.activeRunCount += 1;
|
|
@@ -6326,8 +6714,8 @@ var PersistentSubagentManager = class {
|
|
|
6326
6714
|
}
|
|
6327
6715
|
const current = {};
|
|
6328
6716
|
let resolveTerminal;
|
|
6329
|
-
const terminal = new Promise((
|
|
6330
|
-
resolveTerminal =
|
|
6717
|
+
const terminal = new Promise((resolve6) => {
|
|
6718
|
+
resolveTerminal = resolve6;
|
|
6331
6719
|
});
|
|
6332
6720
|
const executorOwnership = { default: true };
|
|
6333
6721
|
const execution = Promise.resolve().then(async () => {
|
|
@@ -6441,8 +6829,9 @@ var PersistentSubagentManager = class {
|
|
|
6441
6829
|
if (request.id !== void 0) {
|
|
6442
6830
|
const id = checkedId({ id: request.id });
|
|
6443
6831
|
const active = this.activeRuns.get(id);
|
|
6444
|
-
const
|
|
6445
|
-
const
|
|
6832
|
+
const includeActivity = context.includeActivity === true;
|
|
6833
|
+
const status = active ? includeActivity ? liveStatus(active) : persistedStatus(active) : this.terminalSummaries.get(id) ?? await loadPersistedStatus(storageDirectory(this.dependencies), id);
|
|
6834
|
+
const inspection = publicStatus(status, context.includeAttemptMetadata === true, includeActivity);
|
|
6446
6835
|
if (status.state === "completed") return { ...inspection, value: await loadPersistedResult(storageDirectory(this.dependencies), id) };
|
|
6447
6836
|
if (status.state === "failed") {
|
|
6448
6837
|
const failure = status.error ?? await loadPersistedFailure(storageDirectory(this.dependencies), id);
|
|
@@ -6450,9 +6839,9 @@ var PersistentSubagentManager = class {
|
|
|
6450
6839
|
}
|
|
6451
6840
|
return inspection;
|
|
6452
6841
|
}
|
|
6453
|
-
return this.inspectList();
|
|
6842
|
+
return this.inspectList(request.scope === "all" ? void 0 : context.extensionContext.sessionManager.getSessionId());
|
|
6454
6843
|
}
|
|
6455
|
-
async inspectList() {
|
|
6844
|
+
async inspectList(sessionId) {
|
|
6456
6845
|
const root = storageDirectory(this.dependencies);
|
|
6457
6846
|
await secureDirectory(root);
|
|
6458
6847
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
@@ -6463,7 +6852,7 @@ var PersistentSubagentManager = class {
|
|
|
6463
6852
|
try {
|
|
6464
6853
|
const active = this.activeRuns.get(entry.name);
|
|
6465
6854
|
const status = publicStatus(active ? persistedStatus(active) : this.terminalSummaries.get(entry.name) ?? await loadPersistedStatus(root, entry.name, false));
|
|
6466
|
-
if (status.startedAt === void 0) continue;
|
|
6855
|
+
if (status.startedAt === void 0 || sessionId !== void 0 && status.sessionId !== sessionId) continue;
|
|
6467
6856
|
statuses.push({ status, startedAt: status.startedAt });
|
|
6468
6857
|
} catch (error) {
|
|
6469
6858
|
if (error instanceof WorkflowError && (error.code === "RUN_NOT_FOUND" || error instanceof InvalidPersistedSubagentStatusError)) continue;
|
|
@@ -6493,6 +6882,18 @@ var PersistentSubagentManager = class {
|
|
|
6493
6882
|
await this.stopRun(run, false);
|
|
6494
6883
|
return publicStatus(persistedStatus(run));
|
|
6495
6884
|
}
|
|
6885
|
+
/** Removes a settled record for the /subagents navigator, as /workflow deletes a terminal run; not a model tool. */
|
|
6886
|
+
async delete(request) {
|
|
6887
|
+
const id = checkedId(request);
|
|
6888
|
+
await this.ensureInitialized();
|
|
6889
|
+
const root = storageDirectory(this.dependencies);
|
|
6890
|
+
const status = this.activeRuns.has(id) ? void 0 : this.terminalSummaries.get(id) ?? await loadPersistedStatus(root, id, false);
|
|
6891
|
+
if (status === void 0 || status.state === "running") throw new WorkflowError("RUN_OWNED", `Stop subagent ${id} before deleting it`);
|
|
6892
|
+
if (status.worktree !== void 0) throw new WorkflowError("WORKTREE_FAILED", `Subagent ${id} still has a worktree to clean up`);
|
|
6893
|
+
await rm5(runDirectory(root, id), { recursive: true, force: true });
|
|
6894
|
+
this.terminalSummaries.delete(id);
|
|
6895
|
+
return { id, deleted: true };
|
|
6896
|
+
}
|
|
6496
6897
|
async dispose() {
|
|
6497
6898
|
if (this.disposePromise) return this.disposePromise;
|
|
6498
6899
|
this.disposePromise = (async () => {
|
|
@@ -6589,8 +6990,8 @@ var PersistentSubagentManager = class {
|
|
|
6589
6990
|
if (!run || !this.canSteer(run)) return;
|
|
6590
6991
|
if (run.activeAttempt !== void 0 && run.finalizedAttemptAccounting.has(run.activeAttempt)) return;
|
|
6591
6992
|
const snapshot = portableProgress(progress, true);
|
|
6592
|
-
const
|
|
6593
|
-
run.progress = { ...snapshot, accounting:
|
|
6993
|
+
const accounting2 = addAccounting(sumAccounting(run.finalizedAttemptAccounting.values()), progress.accounting);
|
|
6994
|
+
run.progress = { ...snapshot, accounting: accounting2 };
|
|
6594
6995
|
emitUpdate(run);
|
|
6595
6996
|
if (!progress.persist) return;
|
|
6596
6997
|
await enqueueWrite(run, () => atomicJson(statusPath(run.directory), persistedStatus(run)));
|
|
@@ -6605,10 +7006,10 @@ var PersistentSubagentManager = class {
|
|
|
6605
7006
|
run.attemptDetails = [...run.attemptDetails ?? [], summary].slice(-SUBAGENT_ATTEMPT_DETAILS_LIMIT);
|
|
6606
7007
|
}
|
|
6607
7008
|
if (!finalized) return;
|
|
6608
|
-
const
|
|
6609
|
-
if (attempt === void 0 ||
|
|
7009
|
+
const accounting2 = summary?.accounting ?? accountingValue(record2?.accounting);
|
|
7010
|
+
if (attempt === void 0 || accounting2 === void 0) return;
|
|
6610
7011
|
if (run.finalizedAttemptAccounting.size >= MAX_PERSISTED_ATTEMPT_ARRAY_ITEMS && !run.finalizedAttemptAccounting.has(attempt)) return;
|
|
6611
|
-
run.finalizedAttemptAccounting.set(attempt,
|
|
7012
|
+
run.finalizedAttemptAccounting.set(attempt, accounting2);
|
|
6612
7013
|
const cumulative = sumAccounting(run.finalizedAttemptAccounting.values());
|
|
6613
7014
|
run.progress = run.progress === void 0 ? { accounting: cumulative, toolCalls: [] } : { ...run.progress, accounting: cumulative };
|
|
6614
7015
|
}
|
|
@@ -6663,8 +7064,8 @@ var PersistentSubagentManager = class {
|
|
|
6663
7064
|
run.disposed = true;
|
|
6664
7065
|
this.clearSteering(run);
|
|
6665
7066
|
await this.cleanupSessions(run, true);
|
|
6666
|
-
|
|
6667
|
-
|
|
7067
|
+
await this.cleanupWorktree(run);
|
|
7068
|
+
await enqueueWrite(run, () => atomicJson(statusPath(run.directory), persistedStatus(run)));
|
|
6668
7069
|
this.resolveTerminal(run);
|
|
6669
7070
|
this.removeLiveRun(run);
|
|
6670
7071
|
}
|
|
@@ -6867,23 +7268,23 @@ function createSubagentManager(dependencies = {}) {
|
|
|
6867
7268
|
return {
|
|
6868
7269
|
run: (request, context) => manager.run(request, context),
|
|
6869
7270
|
inspect: (request, context) => manager.inspect(request, context),
|
|
6870
|
-
getAttemptActionData: (id) => manager.getAttemptActionData
|
|
6871
|
-
steer: (request
|
|
6872
|
-
stop: (request
|
|
7271
|
+
getAttemptActionData: (id) => manager.getAttemptActionData(id),
|
|
7272
|
+
steer: (request) => manager.steer(request),
|
|
7273
|
+
stop: (request) => manager.stop(request),
|
|
6873
7274
|
retry: (request, context) => manager.retry(request, context),
|
|
7275
|
+
delete: (request) => manager.delete(request),
|
|
6874
7276
|
dispose: async () => {
|
|
6875
|
-
await manager.dispose
|
|
7277
|
+
await manager.dispose();
|
|
6876
7278
|
}
|
|
6877
7279
|
};
|
|
6878
7280
|
}
|
|
6879
7281
|
|
|
6880
7282
|
// subagents/src/navigator.ts
|
|
6881
7283
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
6882
|
-
import { join as
|
|
6883
|
-
import { copyToClipboard as copyToClipboard2, getAgentDir as
|
|
7284
|
+
import { join as join10 } from "node:path";
|
|
7285
|
+
import { copyToClipboard as copyToClipboard2, getAgentDir as getAgentDir7, SettingsManager as SettingsManager4 } from "@earendil-works/pi-coding-agent";
|
|
6884
7286
|
import { Editor, truncateToWidth as truncateToWidth3 } from "@earendil-works/pi-tui";
|
|
6885
7287
|
var MAX_DETAIL_TEXT = 4e3;
|
|
6886
|
-
var MAX_DETAIL_TOOL_CALLS = 32;
|
|
6887
7288
|
function objectValue2(value) {
|
|
6888
7289
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
6889
7290
|
}
|
|
@@ -6899,12 +7300,12 @@ function inspectionValue(value) {
|
|
|
6899
7300
|
return record2 && status ? { status, record: record2 } : void 0;
|
|
6900
7301
|
}
|
|
6901
7302
|
function managerContext(context, waitForForeground = true, includeAttemptMetadata = false) {
|
|
6902
|
-
return { toolCallId: "subagents-command", signal: void 0, onUpdate: void 0, ...waitForForeground ? {} : { waitForForeground: false }, ...includeAttemptMetadata ? { includeAttemptMetadata: true } : {}, extensionContext: context };
|
|
7303
|
+
return { toolCallId: "subagents-command", signal: void 0, onUpdate: void 0, ...waitForForeground ? {} : { waitForForeground: false }, ...includeAttemptMetadata ? { includeAttemptMetadata: true, includeActivity: true } : {}, extensionContext: context };
|
|
6903
7304
|
}
|
|
6904
7305
|
async function loadRequest(storageDirectory3, id) {
|
|
6905
7306
|
if (!safeRunId(id)) return {};
|
|
6906
7307
|
try {
|
|
6907
|
-
const value = JSON.parse(await readFile5(
|
|
7308
|
+
const value = JSON.parse(await readFile5(join10(storageDirectory3, id, "request.json"), "utf8"));
|
|
6908
7309
|
return { request: normalizeSubagentRunRequest(value) };
|
|
6909
7310
|
} catch (error) {
|
|
6910
7311
|
if (isFileNotFound(error)) return {};
|
|
@@ -6937,9 +7338,6 @@ async function inspectEntry(manager, storageDirectory3, entry, context) {
|
|
|
6937
7338
|
entry: { status: inspected.status, ...request.request === void 0 ? {} : { request: request.request }, ...request.error === void 0 ? {} : { requestError: request.error } }
|
|
6938
7339
|
};
|
|
6939
7340
|
}
|
|
6940
|
-
function requestLabel(request) {
|
|
6941
|
-
return request?.label?.trim() || "none";
|
|
6942
|
-
}
|
|
6943
7341
|
function requestRole(request) {
|
|
6944
7342
|
const role = request?.role;
|
|
6945
7343
|
if (typeof role === "string" && role.trim()) return role.trim();
|
|
@@ -6949,13 +7347,31 @@ function requestRole(request) {
|
|
|
6949
7347
|
function shortId(id) {
|
|
6950
7348
|
return id.length > 12 ? id.slice(0, 8) : id;
|
|
6951
7349
|
}
|
|
6952
|
-
function
|
|
6953
|
-
|
|
7350
|
+
function entryName(entry) {
|
|
7351
|
+
const role = requestRole(entry.request);
|
|
7352
|
+
return boundedText(entry.request?.label?.trim() || (role === "none" ? shortId(entry.status.id) : role), 256);
|
|
7353
|
+
}
|
|
7354
|
+
function listNames(entries) {
|
|
7355
|
+
const names = entries.map(entryName);
|
|
7356
|
+
return entries.map(({ status }, index) => {
|
|
7357
|
+
const name = names[index] ?? "";
|
|
7358
|
+
return names.indexOf(name) === names.lastIndexOf(name) ? name : `${name} ${shortId(status.id)}`;
|
|
7359
|
+
});
|
|
7360
|
+
}
|
|
7361
|
+
function pickerLabels(entries) {
|
|
7362
|
+
const names = listNames(entries);
|
|
7363
|
+
return entries.map(({ status }, index) => {
|
|
7364
|
+
const name = names[index] ?? "";
|
|
7365
|
+
const model = status.progress?.state?.model;
|
|
7366
|
+
const cost = formatCost(status.progress?.accounting.cost);
|
|
7367
|
+
const runtime = status.startedAt === void 0 ? "" : ` runtime=${formatWorkflowRuntime((status.finishedAt ?? Date.now()) - status.startedAt)}`;
|
|
7368
|
+
return `${runStateGlyph(status.state, "\u2826")} ${name} ${status.state}${model ? ` ${boundedText(model.model, 256)}${model.thinking ? `:${model.thinking}` : ""}` : ""}${cost ? ` ${cost}` : ""}${runtime}`;
|
|
7369
|
+
});
|
|
6954
7370
|
}
|
|
6955
7371
|
function boundedText(value, limit = MAX_DETAIL_TEXT) {
|
|
6956
7372
|
const text = typeof value === "string" ? value : (() => {
|
|
6957
7373
|
try {
|
|
6958
|
-
const serialized = JSON.stringify(value);
|
|
7374
|
+
const serialized = JSON.stringify(value, null, 2);
|
|
6959
7375
|
return typeof serialized === "string" ? serialized : String(value);
|
|
6960
7376
|
} catch {
|
|
6961
7377
|
return String(value);
|
|
@@ -6963,13 +7379,6 @@ function boundedText(value, limit = MAX_DETAIL_TEXT) {
|
|
|
6963
7379
|
})();
|
|
6964
7380
|
return text.length > limit ? `${text.slice(0, limit)}\u2026` : text;
|
|
6965
7381
|
}
|
|
6966
|
-
function timestamp(value) {
|
|
6967
|
-
return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : void 0;
|
|
6968
|
-
}
|
|
6969
|
-
function appendValue(lines, title, value) {
|
|
6970
|
-
lines.push(`${title}:`);
|
|
6971
|
-
for (const line of boundedText(value).split("\n")) lines.push(` ${line}`);
|
|
6972
|
-
}
|
|
6973
7382
|
function latestAttempt(status) {
|
|
6974
7383
|
return [...status.attemptDetails ?? []].sort((left, right) => right.attempt - left.attempt)[0];
|
|
6975
7384
|
}
|
|
@@ -6980,17 +7389,17 @@ function detailPresentation(inspection) {
|
|
|
6980
7389
|
const { status, request } = inspection.entry;
|
|
6981
7390
|
const attempt = latestAttempt(status);
|
|
6982
7391
|
const state = status.progress?.state;
|
|
6983
|
-
const
|
|
7392
|
+
const activity = status.progress?.activity;
|
|
6984
7393
|
const model = state?.model ?? attempt?.setup.model;
|
|
6985
7394
|
const tools = state?.tools ?? attempt?.setup.tools;
|
|
6986
7395
|
const lastEventAt = status.progress?.lastEventAt;
|
|
6987
7396
|
const attempts = status.attempts ?? attempt?.attempt;
|
|
6988
|
-
const
|
|
7397
|
+
const accounting2 = usageAccounting(status);
|
|
6989
7398
|
const role = requestRole(request);
|
|
6990
7399
|
const error = status.error ?? attempt?.error;
|
|
6991
7400
|
return {
|
|
6992
7401
|
state: status.state,
|
|
6993
|
-
...
|
|
7402
|
+
...activity === void 0 ? {} : { activity: { kind: activity.kind, text: boundedText(activity.text) } },
|
|
6994
7403
|
...lastEventAt === void 0 ? {} : { lastEventAt },
|
|
6995
7404
|
...model === void 0 ? {} : { model: { provider: boundedText(model.provider, 256), model: boundedText(model.model, 256), ...model.thinking === void 0 ? {} : { thinking: model.thinking } } },
|
|
6996
7405
|
...role === "none" ? {} : { role: boundedText(role, 256) },
|
|
@@ -6998,33 +7407,34 @@ function detailPresentation(inspection) {
|
|
|
6998
7407
|
...attempts === void 0 ? {} : { attempts },
|
|
6999
7408
|
...status.startedAt === void 0 ? {} : { startedAt: status.startedAt },
|
|
7000
7409
|
...status.finishedAt === void 0 ? {} : { finishedAt: status.finishedAt },
|
|
7001
|
-
...
|
|
7410
|
+
...accounting2 === void 0 ? {} : { accounting: accounting2 },
|
|
7002
7411
|
...error === void 0 ? {} : { error: { code: boundedText(error.code, 256), message: boundedText(error.message) } }
|
|
7003
7412
|
};
|
|
7004
7413
|
}
|
|
7005
|
-
function
|
|
7006
|
-
const { entry
|
|
7007
|
-
const
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
...
|
|
7414
|
+
function detailRows(inspection, styles, menu) {
|
|
7415
|
+
const { entry } = inspection;
|
|
7416
|
+
const presentation = detailPresentation(inspection);
|
|
7417
|
+
return [
|
|
7418
|
+
styles.bold(`Selected subagent: ${entryName(entry)}`),
|
|
7419
|
+
`ID: ${boundedText(entry.status.id, 256)}`,
|
|
7420
|
+
...formatAgentDetail(presentation, styles, entry.status.finishedAt ?? Date.now(), { includeError: false }),
|
|
7421
|
+
...entry.requestError === void 0 ? [] : [styles.warning(`Request unavailable: ${boundedText(entry.requestError)}`)],
|
|
7422
|
+
...menu === "hint" ? [styles.muted("enter agent actions")] : [],
|
|
7423
|
+
...presentation.error === void 0 ? [] : [formatAgentError(presentation.error, styles)],
|
|
7424
|
+
...typeof menu === "object" ? [styles.bold("Agent actions"), ...menu.options.map((option, index) => index === menu.index ? `\u2192 ${styles.accent(option)}` : ` ${option}`)] : []
|
|
7013
7425
|
];
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
if (Object.prototype.hasOwnProperty.call(record2, "value")) appendValue(lines, "value", record2.value);
|
|
7027
|
-
return lines;
|
|
7426
|
+
}
|
|
7427
|
+
function listRows(entries, selectedId, styles) {
|
|
7428
|
+
const names = listNames(entries);
|
|
7429
|
+
return [styles.bold("Runs"), ...entries.map((entry, index) => `${entry.status.id === selectedId ? "\u2192" : " "} \u2022 ${names[index] ?? ""} \xB7 ${progressStyleForState(entry.status.state, styles)(runStateGlyph(entry.status.state, "\u2826"))}`)];
|
|
7430
|
+
}
|
|
7431
|
+
function headerRows(entries, styles) {
|
|
7432
|
+
const counts = ["running", "failed", "stopped", "completed"].flatMap((state) => {
|
|
7433
|
+
const count = entries.filter((entry) => entry.status.state === state).length;
|
|
7434
|
+
return count ? [`${String(count)} ${state}`] : [];
|
|
7435
|
+
});
|
|
7436
|
+
const cost = formatCost(entries.reduce((sum, entry) => sum + (entry.status.progress?.accounting.cost ?? 0), 0));
|
|
7437
|
+
return [styles.bold(styles.accent("Subagents")), [...counts, ...cost ? [cost] : []].join(" \xB7 ")];
|
|
7028
7438
|
}
|
|
7029
7439
|
function tuiRows(tui) {
|
|
7030
7440
|
return typeof tui.terminal?.rows === "number" && Number.isFinite(tui.terminal.rows) ? tui.terminal.rows : 24;
|
|
@@ -7034,6 +7444,7 @@ function unrefTimer(timer) {
|
|
|
7034
7444
|
if (typeof value !== "object" || value === null || !("unref" in value) || typeof value.unref !== "function") return;
|
|
7035
7445
|
Reflect.apply(value.unref, value, []);
|
|
7036
7446
|
}
|
|
7447
|
+
var DASHBOARD_FOOTER_ROWS = 2;
|
|
7037
7448
|
function standaloneActionContext(manager, inspection, context) {
|
|
7038
7449
|
const status = inspection.entry.status;
|
|
7039
7450
|
const request = inspection.entry.request;
|
|
@@ -7070,11 +7481,16 @@ function standaloneActionContext(manager, inspection, context) {
|
|
|
7070
7481
|
function liveSystemPrompt(manager, status) {
|
|
7071
7482
|
return manager.getAttemptActionData?.(status.id)?.prepared?.systemPrompt;
|
|
7072
7483
|
}
|
|
7073
|
-
|
|
7484
|
+
var BULK_DELETE = { "Delete all completed": "completed", "Delete all failed": "failed" };
|
|
7485
|
+
function bulkDeleteLabels(manager, entries) {
|
|
7486
|
+
if (manager.delete === void 0) return [];
|
|
7487
|
+
return Object.entries(BULK_DELETE).filter(([, state]) => entries.some((entry) => entry.status.state === state)).map(([label2]) => label2);
|
|
7488
|
+
}
|
|
7489
|
+
function actionOptions(manager, inspection, context, bulk = []) {
|
|
7074
7490
|
const actionContext = standaloneActionContext(manager, inspection, context);
|
|
7075
7491
|
const extensionLabels = actionContext === void 0 ? [] : visibleStandaloneAgentAttemptActions(loadingRegistry().agentAttemptActions(), actionContext).map(([, action]) => action.label);
|
|
7076
7492
|
const value = inspection.record.value;
|
|
7077
|
-
|
|
7493
|
+
const labels = agentActionLabels({
|
|
7078
7494
|
extensionLabels,
|
|
7079
7495
|
hasWorktree: inspection.entry.status.worktree !== void 0,
|
|
7080
7496
|
openPrompt: context.mode === "tui" && inspection.entry.request?.prompt !== void 0,
|
|
@@ -7082,13 +7498,33 @@ function actionOptions(manager, inspection, context) {
|
|
|
7082
7498
|
openResult: context.mode === "tui" && Object.prototype.hasOwnProperty.call(inspection.record, "value") && jsonValue(value),
|
|
7083
7499
|
standaloneState: inspection.entry.status.state
|
|
7084
7500
|
});
|
|
7501
|
+
const runActions = [...manager.delete !== void 0 && inspection.entry.status.state !== "running" ? ["Delete"] : [], ...bulk, ...context.mode === "tui" ? ["Copy run path"] : []];
|
|
7502
|
+
labels.splice(labels.indexOf("Copy agent ID"), 0, ...runActions);
|
|
7503
|
+
return labels;
|
|
7504
|
+
}
|
|
7505
|
+
async function deleteAll(manager, storageDirectory3, context, state, confirm) {
|
|
7506
|
+
if (!await confirm(`Delete ${state} runs?`, `Delete all ${state} subagent runs and their records? This cannot be undone.`)) return false;
|
|
7507
|
+
const skipped = [];
|
|
7508
|
+
let deleted = 0;
|
|
7509
|
+
for (const entry of await loadEntries(manager, storageDirectory3, context)) {
|
|
7510
|
+
if (entry.status.state !== state) continue;
|
|
7511
|
+
try {
|
|
7512
|
+
await manager.delete?.({ id: entry.status.id }, managerContext(context));
|
|
7513
|
+
deleted += 1;
|
|
7514
|
+
} catch (error) {
|
|
7515
|
+
skipped.push(`${entry.status.id} (${errorText(error)})`);
|
|
7516
|
+
}
|
|
7517
|
+
}
|
|
7518
|
+
if (skipped.length) context.ui.notify(`Skipped ${state} runs: ${skipped.join(", ")}.`, "warning");
|
|
7519
|
+
if (deleted) context.ui.notify(`Deleted ${String(deleted)} ${state} subagent run(s).`, "info");
|
|
7520
|
+
return true;
|
|
7085
7521
|
}
|
|
7086
7522
|
function retryResult(value) {
|
|
7087
7523
|
const record2 = objectValue2(value);
|
|
7088
7524
|
return record2 && typeof record2.id === "string" && record2.id.trim() && record2.state === "running" ? { id: record2.id, state: "running" } : void 0;
|
|
7089
7525
|
}
|
|
7090
7526
|
async function openNavigatorArtifact(context, tui, artifact, label2) {
|
|
7091
|
-
const command = SettingsManager4.create(context.cwd,
|
|
7527
|
+
const command = SettingsManager4.create(context.cwd, getAgentDir7(), { projectTrusted: context.isProjectTrusted() }).getExternalEditorCommand();
|
|
7092
7528
|
if (!command) {
|
|
7093
7529
|
context.ui.notify(`Cannot open ${label2}: no external editor is configured.`, "warning");
|
|
7094
7530
|
return;
|
|
@@ -7102,7 +7538,7 @@ async function steerSubagent(manager, storageDirectory3, entry, context, message
|
|
|
7102
7538
|
await manager.steer({ id: entry.status.id, message }, managerContext(context));
|
|
7103
7539
|
context.ui.notify(`Steered subagent ${entry.status.id}.`, "info");
|
|
7104
7540
|
}
|
|
7105
|
-
async function performAction(manager, storageDirectory3, entry, action, context, tui, clipboard) {
|
|
7541
|
+
async function performAction(manager, storageDirectory3, entry, action, context, tui, clipboard, confirm) {
|
|
7106
7542
|
const fresh = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7107
7543
|
const available = actionOptions(manager, fresh, context);
|
|
7108
7544
|
if (!available.includes(action)) throw new Error(`Action ${action} is no longer available`);
|
|
@@ -7116,40 +7552,52 @@ async function performAction(manager, storageDirectory3, entry, action, context,
|
|
|
7116
7552
|
if (action === "Copy agent ID") {
|
|
7117
7553
|
await clipboard(fresh.entry.status.id);
|
|
7118
7554
|
context.ui.notify("Copied agent ID.", "info");
|
|
7119
|
-
return "
|
|
7555
|
+
return "unchanged";
|
|
7556
|
+
}
|
|
7557
|
+
if (action === "Copy run path") {
|
|
7558
|
+
await clipboard(join10(storageDirectory3, fresh.entry.status.id));
|
|
7559
|
+
context.ui.notify("Copied run path.", "info");
|
|
7560
|
+
return "unchanged";
|
|
7561
|
+
}
|
|
7562
|
+
if (action === "Delete" && manager.delete !== void 0) {
|
|
7563
|
+
if (!await confirm("Delete subagent?", `Delete ${entryName(fresh.entry)} (${fresh.entry.status.id}) and its record? This cannot be undone.`)) return "unchanged";
|
|
7564
|
+
await manager.delete({ id: fresh.entry.status.id }, managerContext(context));
|
|
7565
|
+
context.ui.notify(`Deleted subagent ${fresh.entry.status.id}.`, "info");
|
|
7566
|
+
return "deleted";
|
|
7120
7567
|
}
|
|
7121
7568
|
if (action === "Copy branch" && fresh.entry.status.worktree) {
|
|
7122
7569
|
await clipboard(fresh.entry.status.worktree.branch);
|
|
7123
7570
|
context.ui.notify("Copied branch.", "info");
|
|
7124
|
-
return "
|
|
7571
|
+
return "unchanged";
|
|
7125
7572
|
}
|
|
7126
7573
|
if (action === "Copy worktree path" && fresh.entry.status.worktree) {
|
|
7127
7574
|
await clipboard(fresh.entry.status.worktree.path);
|
|
7128
7575
|
context.ui.notify("Copied worktree path.", "info");
|
|
7129
|
-
return "
|
|
7576
|
+
return "unchanged";
|
|
7130
7577
|
}
|
|
7131
7578
|
if (action === "Open prompt in editor" && tui && fresh.entry.request?.prompt !== void 0) {
|
|
7132
7579
|
await openNavigatorArtifact(context, tui, workflowPromptArtifact(fresh.entry.request.prompt), "agent prompt");
|
|
7133
|
-
return "
|
|
7580
|
+
return "unchanged";
|
|
7134
7581
|
}
|
|
7135
7582
|
if (action === "Open system prompt in editor" && tui) {
|
|
7136
7583
|
const systemPrompt = liveSystemPrompt(manager, fresh.entry.status);
|
|
7137
7584
|
if (systemPrompt !== void 0) {
|
|
7138
7585
|
await openNavigatorArtifact(context, tui, workflowPromptArtifact(systemPrompt), "agent system prompt");
|
|
7139
|
-
return "
|
|
7586
|
+
return "unchanged";
|
|
7140
7587
|
}
|
|
7141
7588
|
}
|
|
7142
7589
|
if (action === "Open result in editor" && tui && Object.prototype.hasOwnProperty.call(fresh.record, "value") && jsonValue(fresh.record.value)) {
|
|
7143
7590
|
await openNavigatorArtifact(context, tui, workflowResultArtifact(fresh.record.value), "agent result");
|
|
7144
|
-
return "
|
|
7591
|
+
return "unchanged";
|
|
7145
7592
|
}
|
|
7146
7593
|
if (action === "Steer") {
|
|
7147
7594
|
const message = await context.ui.input("Steer subagent", "Message for the running subagent");
|
|
7148
|
-
if (message === void 0) return "
|
|
7595
|
+
if (message === void 0) return "unchanged";
|
|
7149
7596
|
await steerSubagent(manager, storageDirectory3, entry, context, message);
|
|
7150
7597
|
return "stay";
|
|
7151
7598
|
}
|
|
7152
7599
|
if (action === "Stop") {
|
|
7600
|
+
if (!await confirm("Stop subagent?", `Stop subagent ${entryName(fresh.entry)} (${fresh.entry.status.id})? This cannot be undone.`)) return "unchanged";
|
|
7153
7601
|
const current = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7154
7602
|
if (current.entry.status.state !== "running") throw new Error(`Subagent ${entry.status.id} is no longer running`);
|
|
7155
7603
|
await manager.stop({ id: entry.status.id }, managerContext(context));
|
|
@@ -7162,31 +7610,36 @@ async function performAction(manager, storageDirectory3, entry, action, context,
|
|
|
7162
7610
|
const result = retryResult(await manager.retry({ id: entry.status.id }, managerContext(context, false)));
|
|
7163
7611
|
if (!result) throw new Error("Retry returned an invalid subagent result");
|
|
7164
7612
|
context.ui.notify(`Retried subagent ${entry.status.id} as ${result.id}.`, "info");
|
|
7165
|
-
return
|
|
7613
|
+
return { retryId: result.id };
|
|
7166
7614
|
}
|
|
7167
7615
|
return "stay";
|
|
7168
7616
|
}
|
|
7169
7617
|
async function showDetail(manager, storageDirectory3, entry, context, clipboard) {
|
|
7170
7618
|
let inspection = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
}
|
|
7619
|
+
for (; ; ) {
|
|
7620
|
+
const action = await context.ui.select(detailRows(inspection, PLAIN_WORKFLOW_PROGRESS_STYLES).join("\n"), actionOptions(manager, inspection, context));
|
|
7621
|
+
if (!action || action === "Back") return;
|
|
7622
|
+
try {
|
|
7623
|
+
const outcome = await performAction(manager, storageDirectory3, entry, action, context, void 0, clipboard, (title, message) => context.ui.confirm(title, message));
|
|
7624
|
+
if (outcome === "deleted" || typeof outcome === "object") return;
|
|
7625
|
+
inspection = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7626
|
+
} catch (error) {
|
|
7627
|
+
context.ui.notify(`Cannot ${action.toLowerCase()}: ${errorText(error)}`, "warning");
|
|
7181
7628
|
}
|
|
7182
7629
|
}
|
|
7630
|
+
}
|
|
7631
|
+
async function showDashboard(manager, storageDirectory3, initial, context, clipboard) {
|
|
7632
|
+
const first = initial[0];
|
|
7633
|
+
if (first === void 0) return;
|
|
7634
|
+
let initialInspection;
|
|
7635
|
+
try {
|
|
7636
|
+
initialInspection = await inspectEntry(manager, storageDirectory3, first, context);
|
|
7637
|
+
} catch (error) {
|
|
7638
|
+
context.ui.notify(`Cannot inspect subagent ${first.status.id}: ${errorText(error)}`, "warning");
|
|
7639
|
+
initialInspection = { entry: first, record: {} };
|
|
7640
|
+
}
|
|
7183
7641
|
const result = await context.ui.custom((tui, theme, keybindings, done) => {
|
|
7184
|
-
|
|
7185
|
-
let actionMode = false;
|
|
7186
|
-
let actionIndex = 0;
|
|
7187
|
-
let actionRunning = false;
|
|
7188
|
-
let disposed = false;
|
|
7189
|
-
let steerMode = false;
|
|
7642
|
+
const styles = themeWorkflowProgressStyles(theme);
|
|
7190
7643
|
const editorTheme = {
|
|
7191
7644
|
borderColor: (text) => theme.fg("accent", text),
|
|
7192
7645
|
selectList: {
|
|
@@ -7198,226 +7651,273 @@ async function showDetail(manager, storageDirectory3, entry, context, clipboard)
|
|
|
7198
7651
|
}
|
|
7199
7652
|
};
|
|
7200
7653
|
const steerEditor = new Editor(tui, editorTheme);
|
|
7201
|
-
let
|
|
7654
|
+
let entries = initial;
|
|
7655
|
+
let inspection = initialInspection;
|
|
7656
|
+
let offset = 0;
|
|
7657
|
+
let detailsMode = false;
|
|
7658
|
+
let actionMode = false;
|
|
7659
|
+
let actionIndex = 0;
|
|
7660
|
+
let steerMode = false;
|
|
7661
|
+
const inlineConfirm = createInlineConfirm(() => {
|
|
7662
|
+
if (!disposed) tui.requestRender();
|
|
7663
|
+
});
|
|
7664
|
+
const confirmInline = (title, message) => inlineConfirm.ask(title, message);
|
|
7665
|
+
let actionRunning = false;
|
|
7202
7666
|
let refreshing = false;
|
|
7203
|
-
let
|
|
7667
|
+
let disposed = false;
|
|
7668
|
+
let generation = 0;
|
|
7669
|
+
let renderedWidth = 80;
|
|
7670
|
+
let selectionNeedsScroll = true;
|
|
7671
|
+
let timer;
|
|
7672
|
+
const keyLabel = (binding, fallback) => workflowKeyLabel(keybindings, binding, fallback);
|
|
7673
|
+
const matches = (data, binding) => workflowKeyMatches(keybindings, data, binding);
|
|
7204
7674
|
const requestRender = () => {
|
|
7205
7675
|
if (!disposed) tui.requestRender();
|
|
7206
7676
|
};
|
|
7207
|
-
const
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7677
|
+
const options = () => actionOptions(manager, inspection, context, bulkDeleteLabels(manager, entries));
|
|
7678
|
+
const warn = (message) => {
|
|
7679
|
+
if (disposed) return;
|
|
7680
|
+
try {
|
|
7681
|
+
context.ui.notify(message, "warning");
|
|
7682
|
+
} catch {
|
|
7211
7683
|
}
|
|
7212
|
-
refreshGeneration += 1;
|
|
7213
7684
|
};
|
|
7214
|
-
const
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
...options.map((option, index) => `${index === actionIndex ? "\u2192 " : " "}${index === actionIndex ? theme.fg("accent", option) : option}`)
|
|
7219
|
-
];
|
|
7220
|
-
const viewportRows = () => Math.max(1, tuiRows(tui) - 1);
|
|
7221
|
-
const maxOffsetFor = (rows) => Math.max(0, rows.length - viewportRows());
|
|
7222
|
-
const clampOffset = (rows) => {
|
|
7223
|
-
offset = Math.max(0, Math.min(maxOffsetFor(rows), offset));
|
|
7685
|
+
const stopTimer = () => {
|
|
7686
|
+
if (timer === void 0) return;
|
|
7687
|
+
clearInterval(timer);
|
|
7688
|
+
timer = void 0;
|
|
7224
7689
|
};
|
|
7225
|
-
const
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7690
|
+
const syncTimer = () => {
|
|
7691
|
+
if (!entries.some((entry) => entry.status.state === "running") && inspection.entry.status.state !== "running") stopTimer();
|
|
7692
|
+
else if (timer === void 0 && !disposed) {
|
|
7693
|
+
timer = setInterval(() => {
|
|
7694
|
+
if (timer !== void 0) void refresh();
|
|
7695
|
+
}, 1e3);
|
|
7696
|
+
unrefTimer(timer);
|
|
7697
|
+
}
|
|
7229
7698
|
};
|
|
7230
|
-
const
|
|
7231
|
-
const
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7699
|
+
const apply = (nextEntries, next) => {
|
|
7700
|
+
const selectedAction = actionMode ? options()[actionIndex] : void 0;
|
|
7701
|
+
const position = (list) => list.findIndex((entry) => entry.status.id === next.entry.status.id);
|
|
7702
|
+
if (position(entries) !== position(nextEntries)) selectionNeedsScroll = true;
|
|
7703
|
+
entries = nextEntries;
|
|
7704
|
+
inspection = next;
|
|
7705
|
+
if (selectedAction !== void 0) {
|
|
7706
|
+
const nextOptions = options();
|
|
7707
|
+
const kept = nextOptions.indexOf(selectedAction);
|
|
7708
|
+
actionIndex = kept >= 0 ? kept : Math.min(actionIndex, Math.max(0, nextOptions.length - 1));
|
|
7235
7709
|
}
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
if (actionRow < offset) offset = actionRow;
|
|
7239
|
-
else if (actionRow >= offset + viewport) offset = actionRow - viewport + 1;
|
|
7240
|
-
clampOffset(rows);
|
|
7710
|
+
syncTimer();
|
|
7711
|
+
requestRender();
|
|
7241
7712
|
};
|
|
7242
|
-
const
|
|
7243
|
-
|
|
7713
|
+
const reload = async (id = inspection.entry.status.id) => {
|
|
7714
|
+
const current = ++generation;
|
|
7715
|
+
const nextEntries = await loadEntries(manager, storageDirectory3, context);
|
|
7716
|
+
if (current !== generation) return;
|
|
7717
|
+
const selected = nextEntries.find((entry) => entry.status.id === id) ?? nextEntries[0];
|
|
7718
|
+
if (selected === void 0) {
|
|
7719
|
+
context.ui.notify("No durable subagent runs in this session.", "info");
|
|
7720
|
+
close(void 0);
|
|
7721
|
+
return;
|
|
7722
|
+
}
|
|
7723
|
+
let next;
|
|
7244
7724
|
try {
|
|
7245
|
-
|
|
7246
|
-
} catch {
|
|
7725
|
+
next = await inspectEntry(manager, storageDirectory3, selected, context);
|
|
7726
|
+
} catch (error) {
|
|
7727
|
+
if (current === generation) apply(nextEntries, { entry: selected, record: {} });
|
|
7728
|
+
throw error;
|
|
7247
7729
|
}
|
|
7730
|
+
if (current === generation) apply(nextEntries, next);
|
|
7248
7731
|
};
|
|
7249
|
-
const
|
|
7250
|
-
if (disposed || actionRunning || refreshing
|
|
7732
|
+
const refresh = async () => {
|
|
7733
|
+
if (disposed || actionRunning || refreshing) return;
|
|
7251
7734
|
refreshing = true;
|
|
7252
|
-
const
|
|
7253
|
-
const
|
|
7254
|
-
const viewBeforeRefresh = actionMode ? actionView() : void 0;
|
|
7255
|
-
const actionRowBeforeRefresh = viewBeforeRefresh === void 0 ? -1 : viewBeforeRefresh.rows.length - viewBeforeRefresh.options.length + actionIndex;
|
|
7256
|
-
const wasVisible = actionRowBeforeRefresh >= offset && actionRowBeforeRefresh < offset + viewportRows();
|
|
7735
|
+
const current = generation;
|
|
7736
|
+
const selectedId = inspection.entry.status.id;
|
|
7257
7737
|
try {
|
|
7258
|
-
const
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
const options = actionOptions(manager, inspection, context);
|
|
7264
|
-
actionIndex = Math.min(actionIndex, Math.max(0, options.length - 1));
|
|
7265
|
-
const view = actionView();
|
|
7266
|
-
if (wasVisible || actionIndex !== actionIndexBeforeRefresh) scrollActionIntoView(view);
|
|
7267
|
-
else clampOffset(view.rows);
|
|
7268
|
-
}
|
|
7269
|
-
requestRender();
|
|
7270
|
-
} catch (error) {
|
|
7271
|
-
if (generation === refreshGeneration) reportRefreshError(error);
|
|
7738
|
+
const stale = entries.filter((entry) => entry.status.state === "running" || entry.status.id === selectedId);
|
|
7739
|
+
const results = await Promise.allSettled(stale.map((entry) => inspectEntry(manager, storageDirectory3, entry, context)));
|
|
7740
|
+
if (current !== generation) return;
|
|
7741
|
+
const fresh = new Map(results.flatMap((result2) => result2.status === "fulfilled" ? [[result2.value.entry.status.id, result2.value]] : []));
|
|
7742
|
+
apply(attentionSort(entries.map((entry) => fresh.get(entry.status.id)?.entry ?? entry)), fresh.get(selectedId) ?? inspection);
|
|
7272
7743
|
} finally {
|
|
7273
7744
|
refreshing = false;
|
|
7274
7745
|
}
|
|
7275
7746
|
};
|
|
7747
|
+
const select = (delta) => {
|
|
7748
|
+
const index = Math.max(0, entries.findIndex((entry2) => entry2.status.id === inspection.entry.status.id));
|
|
7749
|
+
const entry = entries[(index + delta + entries.length) % entries.length];
|
|
7750
|
+
if (entry === void 0 || entry.status.id === inspection.entry.status.id) return;
|
|
7751
|
+
inspection = { entry, record: {} };
|
|
7752
|
+
selectionNeedsScroll = true;
|
|
7753
|
+
const current = ++generation;
|
|
7754
|
+
void inspectEntry(manager, storageDirectory3, entry, context).then((next) => {
|
|
7755
|
+
if (current === generation) apply(entries, next);
|
|
7756
|
+
}, (error) => {
|
|
7757
|
+
if (current === generation) warn(`Cannot inspect subagent ${entry.status.id}: ${errorText(error)}`);
|
|
7758
|
+
});
|
|
7759
|
+
};
|
|
7760
|
+
const shutDown = () => {
|
|
7761
|
+
disposed = true;
|
|
7762
|
+
generation += 1;
|
|
7763
|
+
stopTimer();
|
|
7764
|
+
inlineConfirm.cancel();
|
|
7765
|
+
};
|
|
7276
7766
|
const close = (value) => {
|
|
7277
7767
|
if (disposed) return;
|
|
7278
|
-
|
|
7279
|
-
stopRefresh();
|
|
7768
|
+
shutDown();
|
|
7280
7769
|
done(value);
|
|
7281
7770
|
};
|
|
7282
7771
|
steerEditor.onSubmit = (value) => {
|
|
7283
7772
|
const message = value.trim();
|
|
7284
|
-
if (message) close({
|
|
7773
|
+
if (message) close({ entry: inspection.entry, message });
|
|
7285
7774
|
};
|
|
7286
|
-
const
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
const rows = steerMode ? [...detail, "", theme.bold("Steer subagent"), ...steerEditor.render(renderWidth)] : action?.rows ?? detail;
|
|
7293
|
-
const viewport = viewportRows();
|
|
7294
|
-
const maxOffset = maxOffsetFor(rows);
|
|
7295
|
-
const visibleOffset = steerMode ? maxOffset : Math.max(0, Math.min(maxOffset, offset));
|
|
7296
|
-
const hint = theme.fg("dim", steerMode ? "enter submit \xB7 esc back" : actionMode ? "\u2191/\u2193 actions \xB7 enter run \xB7 esc back" : "\u2191/\u2193 scroll \xB7 a actions \xB7 enter actions \xB7 esc back");
|
|
7297
|
-
return [...rows.slice(visibleOffset, visibleOffset + viewport), hint].map((line) => truncateToWidth3(line, renderWidth, "\u2026"));
|
|
7775
|
+
const reloadAfterAction = async (id) => {
|
|
7776
|
+
try {
|
|
7777
|
+
await reload(id);
|
|
7778
|
+
} catch (error) {
|
|
7779
|
+
warn(`Cannot refresh subagents: ${errorText(error)}`);
|
|
7780
|
+
}
|
|
7298
7781
|
};
|
|
7299
7782
|
const runAction = (action) => {
|
|
7300
|
-
if (disposed) return;
|
|
7301
7783
|
actionRunning = true;
|
|
7302
7784
|
requestRender();
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
}
|
|
7309
|
-
const next = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7310
|
-
if (isDisposed()) return;
|
|
7311
|
-
inspection = next;
|
|
7312
|
-
if (inspection.entry.status.state !== "running") stopRefresh();
|
|
7785
|
+
const bulk = BULK_DELETE[action];
|
|
7786
|
+
const pending = bulk === void 0 ? performAction(manager, storageDirectory3, inspection.entry, action, context, tui, clipboard, confirmInline) : deleteAll(manager, storageDirectory3, context, bulk, confirmInline).then((done2) => done2 ? "stay" : "unchanged");
|
|
7787
|
+
void pending.then(async (outcome) => {
|
|
7788
|
+
if (disposed || outcome === "unchanged") return;
|
|
7789
|
+
await reloadAfterAction(typeof outcome === "object" ? outcome.retryId : void 0);
|
|
7313
7790
|
actionMode = false;
|
|
7314
7791
|
actionIndex = 0;
|
|
7315
7792
|
offset = 0;
|
|
7316
|
-
|
|
7317
|
-
|
|
7793
|
+
selectionNeedsScroll = true;
|
|
7794
|
+
}, async (error) => {
|
|
7795
|
+
warn(`Cannot ${action.toLowerCase()}: ${errorText(error)}`);
|
|
7796
|
+
if (!disposed) await reloadAfterAction();
|
|
7318
7797
|
}).finally(() => {
|
|
7319
7798
|
actionRunning = false;
|
|
7320
|
-
|
|
7799
|
+
requestRender();
|
|
7321
7800
|
});
|
|
7322
7801
|
};
|
|
7323
|
-
|
|
7324
|
-
refreshTimer = setInterval(() => {
|
|
7325
|
-
void refreshInspection().catch(reportRefreshError);
|
|
7326
|
-
}, 1e3);
|
|
7327
|
-
unrefTimer(refreshTimer);
|
|
7328
|
-
}
|
|
7802
|
+
syncTimer();
|
|
7329
7803
|
return {
|
|
7330
|
-
render
|
|
7804
|
+
render(width) {
|
|
7805
|
+
if (disposed) return [];
|
|
7806
|
+
renderedWidth = width;
|
|
7807
|
+
const narrow = width < 80;
|
|
7808
|
+
const actions = actionMode ? options() : [];
|
|
7809
|
+
const menu = actionMode ? { options: actions, index: actionIndex } : steerMode ? void 0 : "hint";
|
|
7810
|
+
const layout = narrow ? detailsMode || actionMode || steerMode ? { detailsOnly: true } : { treeOnly: true } : {};
|
|
7811
|
+
const content = [...headerRows(entries, styles), ...formatNavigatorColumns(listRows(entries, inspection.entry.status.id, styles), detailRows(inspection, styles, menu), width, layout)];
|
|
7812
|
+
const footer = steerMode ? [styles.bold("Steer subagent"), ...steerEditor.render(width)] : inlineConfirm.rows(width, styles);
|
|
7813
|
+
const rows = Math.max(1, tuiRows(tui) - DASHBOARD_FOOTER_ROWS);
|
|
7814
|
+
const hintRows = rows >= 3 ? 1 : 0;
|
|
7815
|
+
const viewport = Math.max(1, rows - hintRows - footer.length);
|
|
7816
|
+
const keepVisible = (row) => {
|
|
7817
|
+
if (row < 0) return;
|
|
7818
|
+
if (row < offset) offset = row;
|
|
7819
|
+
else if (row >= offset + viewport) offset = row - viewport + 1;
|
|
7820
|
+
};
|
|
7821
|
+
if (actionMode) keepVisible(content.findIndex((line) => narrow ? line.startsWith("\u2192 ") : line.includes(" | \u2192 ")));
|
|
7822
|
+
else if (selectionNeedsScroll && !(narrow && detailsMode)) {
|
|
7823
|
+
keepVisible(content.findIndex((line) => line.startsWith("\u2192")));
|
|
7824
|
+
selectionNeedsScroll = false;
|
|
7825
|
+
}
|
|
7826
|
+
offset = Math.max(0, Math.min(Math.max(0, content.length - viewport), offset));
|
|
7827
|
+
const up = keyLabel("tui.select.up", "\u2191");
|
|
7828
|
+
const down = keyLabel("tui.select.down", "\u2193");
|
|
7829
|
+
const enter = keyLabel("tui.select.confirm", "enter");
|
|
7830
|
+
const esc = keyLabel("tui.select.cancel", "esc");
|
|
7831
|
+
const scroll = content.length > viewport ? ` \xB7 ${keyLabel("tui.select.pageUp", "pgup")}/${keyLabel("tui.select.pageDown", "pgdn")} scroll` : "";
|
|
7832
|
+
const refreshHint = timer === void 0 ? "" : " \xB7 auto-refresh 1s";
|
|
7833
|
+
const back = narrow && detailsMode ? "details" : "list";
|
|
7834
|
+
const hint = steerMode ? "enter submit \xB7 esc back" : inlineConfirm.active() ? inlineConfirm.hint(keybindings) : actionMode ? `${up}/${down} actions \xB7 ${enter} run \xB7 ${keyLabel("tui.editor.cursorLeft", "\u2190")} ${back} \xB7 ${esc} ${back}` : narrow && detailsMode ? `${up}/${down} scroll \xB7 ${enter} actions \xB7 a actions \xB7 ${esc} list${scroll}${refreshHint}` : `${up}/${down} select \xB7 ${enter} ${narrow ? "details" : "actions"} \xB7 a actions \xB7 ${esc} close${scroll}${refreshHint}`;
|
|
7835
|
+
return [...content.slice(offset, offset + viewport), ...footer, ...hintRows ? [styles.dim(hint)] : []].map((line) => truncateToWidth3(line, Math.max(1, width), "\u2026"));
|
|
7836
|
+
},
|
|
7331
7837
|
invalidate() {
|
|
7332
7838
|
},
|
|
7333
7839
|
handleInput(data) {
|
|
7334
|
-
if (disposed
|
|
7840
|
+
if (disposed) return;
|
|
7841
|
+
if (inlineConfirm.active()) {
|
|
7842
|
+
inlineConfirm.handleInput(keybindings, data);
|
|
7843
|
+
return;
|
|
7844
|
+
}
|
|
7845
|
+
if (actionRunning) return;
|
|
7335
7846
|
if (steerMode) {
|
|
7336
7847
|
if (keybindings.matches(data, "tui.select.cancel")) {
|
|
7337
7848
|
steerMode = false;
|
|
7338
7849
|
actionMode = true;
|
|
7339
7850
|
steerEditor.setText("");
|
|
7340
|
-
offset = 0;
|
|
7341
|
-
scrollActionIntoView();
|
|
7342
7851
|
} else steerEditor.handleInput(data);
|
|
7343
7852
|
requestRender();
|
|
7344
7853
|
return;
|
|
7345
7854
|
}
|
|
7346
|
-
|
|
7855
|
+
const narrow = renderedWidth < 80;
|
|
7856
|
+
const page = Math.max(1, tuiRows(tui) - DASHBOARD_FOOTER_ROWS - 1);
|
|
7857
|
+
if (matches(data, "tui.select.pageUp")) offset = Math.max(0, offset - page);
|
|
7858
|
+
else if (matches(data, "tui.select.pageDown")) offset += page;
|
|
7859
|
+
else if (actionMode) {
|
|
7860
|
+
const actions = options();
|
|
7861
|
+
if (matches(data, "tui.select.cancel") || matches(data, "tui.editor.cursorLeft")) {
|
|
7862
|
+
actionMode = false;
|
|
7863
|
+
offset = 0;
|
|
7864
|
+
selectionNeedsScroll = true;
|
|
7865
|
+
} else if (matches(data, "tui.select.up")) actionIndex = (actionIndex + actions.length - 1) % actions.length;
|
|
7866
|
+
else if (matches(data, "tui.select.down")) actionIndex = (actionIndex + 1) % actions.length;
|
|
7867
|
+
else if (matches(data, "tui.select.confirm")) {
|
|
7868
|
+
const action = actions[actionIndex];
|
|
7869
|
+
if (action === "Steer") {
|
|
7870
|
+
steerMode = true;
|
|
7871
|
+
actionMode = false;
|
|
7872
|
+
steerEditor.setText("");
|
|
7873
|
+
} else if (action && action !== "Back") runAction(action);
|
|
7874
|
+
else {
|
|
7875
|
+
actionMode = false;
|
|
7876
|
+
offset = 0;
|
|
7877
|
+
selectionNeedsScroll = true;
|
|
7878
|
+
}
|
|
7879
|
+
}
|
|
7880
|
+
} else if (data === "a" || data === "A") {
|
|
7347
7881
|
actionMode = true;
|
|
7348
7882
|
actionIndex = 0;
|
|
7349
7883
|
offset = 0;
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
}
|
|
7363
|
-
if (!actionMode) {
|
|
7364
|
-
if (keybindings.matches(data, "tui.select.confirm")) {
|
|
7884
|
+
} else if (matches(data, "tui.select.cancel")) {
|
|
7885
|
+
if (!narrow || !detailsMode) {
|
|
7886
|
+
close(void 0);
|
|
7887
|
+
return;
|
|
7888
|
+
}
|
|
7889
|
+
detailsMode = false;
|
|
7890
|
+
offset = 0;
|
|
7891
|
+
selectionNeedsScroll = true;
|
|
7892
|
+
} else if (narrow && detailsMode) {
|
|
7893
|
+
if (matches(data, "tui.select.up")) offset = Math.max(0, offset - 1);
|
|
7894
|
+
else if (matches(data, "tui.select.down")) offset += 1;
|
|
7895
|
+
else if (matches(data, "tui.select.confirm")) {
|
|
7365
7896
|
actionMode = true;
|
|
7366
7897
|
actionIndex = 0;
|
|
7367
7898
|
offset = 0;
|
|
7368
|
-
scrollActionIntoView();
|
|
7369
|
-
requestRender();
|
|
7370
|
-
} else if (keybindings.matches(data, "tui.select.up")) {
|
|
7371
|
-
offset = Math.max(0, offset - 1);
|
|
7372
|
-
clampOffset(detailLines(inspection, theme));
|
|
7373
|
-
requestRender();
|
|
7374
|
-
} else if (keybindings.matches(data, "tui.select.down")) {
|
|
7375
|
-
offset += 1;
|
|
7376
|
-
clampOffset(detailLines(inspection, theme));
|
|
7377
|
-
requestRender();
|
|
7378
7899
|
}
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
actionIndex = (actionIndex + 1) % options.length;
|
|
7387
|
-
scrollActionIntoView();
|
|
7388
|
-
} else if (keybindings.matches(data, "tui.select.confirm")) {
|
|
7389
|
-
const action = options[actionIndex];
|
|
7390
|
-
if (action === "Steer") {
|
|
7391
|
-
steerMode = true;
|
|
7392
|
-
actionMode = false;
|
|
7393
|
-
steerEditor.setText("");
|
|
7394
|
-
offset = 0;
|
|
7395
|
-
} else if (action && action !== "Back") runAction(action);
|
|
7396
|
-
else actionMode = false;
|
|
7397
|
-
} else if (keybindings.matches(data, "tui.select.pageUp")) {
|
|
7398
|
-
offset = Math.max(0, offset - viewportRows());
|
|
7399
|
-
clampOffset(actionView().rows);
|
|
7400
|
-
} else if (keybindings.matches(data, "tui.select.pageDown")) {
|
|
7401
|
-
offset += viewportRows();
|
|
7402
|
-
clampOffset(actionView().rows);
|
|
7900
|
+
} else if (matches(data, "tui.select.up")) select(-1);
|
|
7901
|
+
else if (matches(data, "tui.select.down")) select(1);
|
|
7902
|
+
else if (matches(data, "tui.select.confirm")) {
|
|
7903
|
+
if (narrow) detailsMode = true;
|
|
7904
|
+
else actionMode = true;
|
|
7905
|
+
actionIndex = 0;
|
|
7906
|
+
offset = 0;
|
|
7403
7907
|
}
|
|
7404
7908
|
requestRender();
|
|
7405
7909
|
},
|
|
7406
7910
|
dispose() {
|
|
7407
|
-
|
|
7408
|
-
disposed = true;
|
|
7409
|
-
stopRefresh();
|
|
7410
|
-
}
|
|
7911
|
+
shutDown();
|
|
7411
7912
|
}
|
|
7412
7913
|
};
|
|
7413
7914
|
});
|
|
7414
|
-
if (
|
|
7915
|
+
if (result === void 0) return;
|
|
7415
7916
|
try {
|
|
7416
|
-
await steerSubagent(manager, storageDirectory3, entry, context, result.message);
|
|
7917
|
+
await steerSubagent(manager, storageDirectory3, result.entry, context, result.message);
|
|
7417
7918
|
} catch (error) {
|
|
7418
7919
|
context.ui.notify(`Cannot steer: ${errorText(error)}`, "warning");
|
|
7419
7920
|
}
|
|
7420
|
-
return "exit";
|
|
7421
7921
|
}
|
|
7422
7922
|
async function runNavigator(manager, storageDirectory3, args, context, clipboard) {
|
|
7423
7923
|
if (args.trim()) {
|
|
@@ -7430,17 +7930,30 @@ async function runNavigator(manager, storageDirectory3, args, context, clipboard
|
|
|
7430
7930
|
context.ui.notify("No durable subagent runs in this session.", "info");
|
|
7431
7931
|
return;
|
|
7432
7932
|
}
|
|
7933
|
+
const labels = pickerLabels(entries);
|
|
7433
7934
|
if (!context.hasUI) {
|
|
7434
|
-
context.ui.notify(
|
|
7935
|
+
context.ui.notify(labels.join("\n"), "info");
|
|
7936
|
+
return;
|
|
7937
|
+
}
|
|
7938
|
+
if (context.mode === "tui") {
|
|
7939
|
+
await showDashboard(manager, storageDirectory3, entries, context, clipboard);
|
|
7435
7940
|
return;
|
|
7436
7941
|
}
|
|
7437
|
-
const
|
|
7438
|
-
const choice = await context.ui.select("Subagents\n", [...labels, "Close"]);
|
|
7942
|
+
const choice = await context.ui.select("Subagents\n", [...labels, "Close", ...bulkDeleteLabels(manager, entries)]);
|
|
7439
7943
|
if (!choice || choice === "Close") return;
|
|
7944
|
+
const bulk = BULK_DELETE[choice];
|
|
7945
|
+
if (bulk !== void 0) {
|
|
7946
|
+
try {
|
|
7947
|
+
await deleteAll(manager, storageDirectory3, context, bulk, (title, message) => context.ui.confirm(title, message));
|
|
7948
|
+
} catch (error) {
|
|
7949
|
+
context.ui.notify(`Cannot delete ${bulk} runs: ${errorText(error)}`, "warning");
|
|
7950
|
+
}
|
|
7951
|
+
continue;
|
|
7952
|
+
}
|
|
7440
7953
|
const selected = entries[labels.indexOf(choice)];
|
|
7441
7954
|
if (!selected) return;
|
|
7442
7955
|
try {
|
|
7443
|
-
|
|
7956
|
+
await showDetail(manager, storageDirectory3, selected, context, clipboard);
|
|
7444
7957
|
} catch (error) {
|
|
7445
7958
|
context.ui.notify(`Cannot inspect subagent ${selected.status.id}: ${errorText(error)}`, "warning");
|
|
7446
7959
|
}
|
|
@@ -7461,20 +7974,11 @@ function registerSubagentNavigator(registerCommand, manager, storageDirectory3,
|
|
|
7461
7974
|
|
|
7462
7975
|
// subagents/src/view.ts
|
|
7463
7976
|
import { truncateToWidth as truncateToWidth4 } from "@earendil-works/pi-tui";
|
|
7464
|
-
var
|
|
7977
|
+
var SPINNER2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7465
7978
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "failed", "stopped"]);
|
|
7466
7979
|
var MAX_INSPECT_VALUE_CHARS = 4e3;
|
|
7467
7980
|
var MAX_INSPECT_TOOL_CALLS = 32;
|
|
7468
7981
|
var MAX_BACKGROUND_WIDGET_ROWS = 10;
|
|
7469
|
-
function textBlock2(text) {
|
|
7470
|
-
return {
|
|
7471
|
-
render(width) {
|
|
7472
|
-
return text.split("\n").map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026"));
|
|
7473
|
-
},
|
|
7474
|
-
invalidate() {
|
|
7475
|
-
}
|
|
7476
|
-
};
|
|
7477
|
-
}
|
|
7478
7982
|
function roleName(value) {
|
|
7479
7983
|
if (typeof value === "string") return value.trim() || void 0;
|
|
7480
7984
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -7516,49 +8020,23 @@ function statusValue2(value) {
|
|
|
7516
8020
|
if (typeof record2.id !== "string" || record2.state !== "running" && record2.state !== "completed" && record2.state !== "failed" && record2.state !== "stopped") return void 0;
|
|
7517
8021
|
return value;
|
|
7518
8022
|
}
|
|
7519
|
-
function
|
|
7520
|
-
if (
|
|
7521
|
-
const seconds = Math.max(0, Math.floor(((finishedAt ?? now) - startedAt) / 1e3));
|
|
7522
|
-
if (seconds < 60) return `${String(seconds)}s`;
|
|
7523
|
-
const minutes = Math.floor(seconds / 60);
|
|
7524
|
-
const remainingSeconds = seconds % 60;
|
|
7525
|
-
if (minutes < 60) return `${String(minutes)}m${remainingSeconds ? ` ${String(remainingSeconds)}s` : ""}`;
|
|
7526
|
-
const hours = Math.floor(minutes / 60);
|
|
7527
|
-
const remainingMinutes = minutes % 60;
|
|
7528
|
-
return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
|
|
7529
|
-
}
|
|
7530
|
-
function stateGlyph(state, spinner) {
|
|
7531
|
-
if (state === "running") return spinner;
|
|
8023
|
+
function stateGlyph(state, spinner2) {
|
|
8024
|
+
if (state === "running") return spinner2;
|
|
7532
8025
|
return state === "completed" ? "\u2713" : "\u2717";
|
|
7533
8026
|
}
|
|
7534
8027
|
function stateColor(state) {
|
|
7535
8028
|
if (state === "running") return "accent";
|
|
7536
8029
|
return state === "completed" ? "success" : "error";
|
|
7537
8030
|
}
|
|
7538
|
-
function
|
|
7539
|
-
if (status.state !== "running") return void 0;
|
|
7540
|
-
const current = status.progress?.activity;
|
|
7541
|
-
if (current?.kind === "reasoning" || current?.kind === "text") {
|
|
7542
|
-
const name = current.kind === "reasoning" ? "reasoning" : "responding";
|
|
7543
|
-
const text = sanitizeDisplayText(current.text);
|
|
7544
|
-
return text && text !== name ? `${name} \xB7 ${text}` : name;
|
|
7545
|
-
}
|
|
7546
|
-
if (current?.kind === "tool") return sanitizeDisplayText(current.text);
|
|
7547
|
-
return sanitizeDisplayText([...status.progress?.toolCalls ?? []].reverse().find(({ state }) => state === "running")?.name ?? "");
|
|
7548
|
-
}
|
|
7549
|
-
function stalledDuration2(status, now) {
|
|
7550
|
-
const lastEventAt = status.progress?.lastEventAt;
|
|
7551
|
-
if (status.state !== "running" || lastEventAt === void 0 || !Number.isFinite(lastEventAt)) return void 0;
|
|
7552
|
-
const duration = now - lastEventAt;
|
|
7553
|
-
return duration >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? duration : void 0;
|
|
7554
|
-
}
|
|
7555
|
-
function accounting2(status) {
|
|
8031
|
+
function usageStats(status) {
|
|
7556
8032
|
const value = status.progress?.accounting;
|
|
7557
|
-
|
|
7558
|
-
const total = value.input + value.output + value.cacheRead + value.cacheWrite;
|
|
7559
|
-
return `tokens=${String(total)} cost=${formatCost(value.cost) || "$0.00"}`;
|
|
8033
|
+
return value ? [formatTokens(value.input + value.output), formatCost(value.cost)].filter(Boolean).join(" \xB7 ") : "";
|
|
7560
8034
|
}
|
|
7561
|
-
function
|
|
8035
|
+
function modelName(status) {
|
|
8036
|
+
const model = status.progress?.state?.model ?? status.attemptDetails?.at(-1)?.setup.model;
|
|
8037
|
+
return model ? `${model.model}${model.thinking ? `:${model.thinking}` : ""}` : "";
|
|
8038
|
+
}
|
|
8039
|
+
function timestamp(value) {
|
|
7562
8040
|
if (value === void 0 || !Number.isFinite(value) || value < 0) return void 0;
|
|
7563
8041
|
const date = new Date(value);
|
|
7564
8042
|
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
@@ -7573,24 +8051,23 @@ function boundedValue(value) {
|
|
|
7573
8051
|
}
|
|
7574
8052
|
return text.length > MAX_INSPECT_VALUE_CHARS ? `${text.slice(0, MAX_INSPECT_VALUE_CHARS)}\u2026` : text;
|
|
7575
8053
|
}
|
|
7576
|
-
function formatSubagentProgress(status, args, theme,
|
|
8054
|
+
function formatSubagentProgress(status, args, theme, spinner2, now, expanded) {
|
|
7577
8055
|
const color = stateColor(status.state);
|
|
7578
|
-
const elapsed =
|
|
8056
|
+
const elapsed = status.startedAt === void 0 ? "" : formatWorkflowRuntime((status.finishedAt ?? now) - status.startedAt);
|
|
7579
8057
|
const metadata = requestMetadata(args, args.id === void 0);
|
|
8058
|
+
const stats = usageStats(status);
|
|
7580
8059
|
const lines = [
|
|
7581
|
-
`${theme.fg(color, stateGlyph(status.state,
|
|
8060
|
+
`${theme.fg(color, stateGlyph(status.state, "\u25CF"))} ${theme.bold(theme.fg("accent", `Subagent: ${label({ ...args, id: status.id })}`))} ${theme.fg(color, `[${status.state}]`)}${metadata ? ` ${theme.fg("dim", metadata)}` : ""}${stats ? ` ${stats}` : ""}${elapsed ? ` runtime=${elapsed}` : ""}`
|
|
7582
8061
|
];
|
|
7583
|
-
const current =
|
|
7584
|
-
const stalled =
|
|
7585
|
-
if (current) lines.push(` ${theme.fg("accent",
|
|
8062
|
+
const current = status.state === "running" ? agentActivityLabel(status.progress ?? {}) : void 0;
|
|
8063
|
+
const stalled = stalledDuration({ ...status.progress, state: status.state }, now);
|
|
8064
|
+
if (current || status.state === "running") lines.push(` ${theme.fg("accent", spinner2)}${current ? ` ${theme.fg("dim", current)}` : ""}${stalled === void 0 ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
7586
8065
|
else if (stalled !== void 0) lines.push(` ${theme.fg("warning", `stalled? ${formatStalledDuration(stalled)}`)}`);
|
|
7587
8066
|
if (status.error) lines.push(` ${theme.fg("error", `${status.error.code}: ${status.error.message}`)}`);
|
|
7588
8067
|
if (expanded) {
|
|
7589
8068
|
lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
|
|
7590
8069
|
const model = status.progress?.state?.model;
|
|
7591
8070
|
if (model) lines.push(` ${theme.fg("dim", `model=${model.provider}/${model.model}${model.thinking ? `:${model.thinking}` : ""}`)}`);
|
|
7592
|
-
const usage = accounting2(status);
|
|
7593
|
-
if (usage) lines.push(` ${theme.fg("dim", usage)}`);
|
|
7594
8071
|
if (status.worktree) lines.push(` ${theme.fg("dim", `worktree=${status.worktree.path} branch=${status.worktree.branch}`)}`);
|
|
7595
8072
|
}
|
|
7596
8073
|
return lines.join("\n");
|
|
@@ -7607,9 +8084,9 @@ function formatSubagentInspection(status, args, details, theme, expanded) {
|
|
|
7607
8084
|
const lines = [formatSubagentProgress(status, { id: args.id ?? status.id }, theme, "\u25C7", Date.now(), false)];
|
|
7608
8085
|
if (!expanded) return lines[0] ?? "";
|
|
7609
8086
|
lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
|
|
7610
|
-
const startedAt =
|
|
7611
|
-
const finishedAt =
|
|
7612
|
-
const lastEventAt =
|
|
8087
|
+
const startedAt = timestamp(status.startedAt);
|
|
8088
|
+
const finishedAt = timestamp(status.finishedAt);
|
|
8089
|
+
const lastEventAt = timestamp(status.progress?.lastEventAt);
|
|
7613
8090
|
if (startedAt) lines.push(` ${theme.fg("dim", `startedAt=${startedAt}`)}`);
|
|
7614
8091
|
if (finishedAt) lines.push(` ${theme.fg("dim", `finishedAt=${finishedAt}`)}`);
|
|
7615
8092
|
if (lastEventAt) lines.push(` ${theme.fg("dim", `lastEventAt=${lastEventAt}`)}`);
|
|
@@ -7617,8 +8094,8 @@ function formatSubagentInspection(status, args, details, theme, expanded) {
|
|
|
7617
8094
|
if (model) lines.push(` ${theme.fg("dim", `model=${model.provider}/${model.model}${model.thinking ? `:${model.thinking}` : ""}`)}`);
|
|
7618
8095
|
const currentActivity = status.progress?.activity;
|
|
7619
8096
|
if (currentActivity) lines.push(` ${theme.fg("dim", `activity=${currentActivity.kind}${currentActivity.text ? ` ${sanitizeDisplayText(currentActivity.text)}` : ""}`)}`);
|
|
7620
|
-
const
|
|
7621
|
-
if (
|
|
8097
|
+
const accounting2 = status.progress?.accounting;
|
|
8098
|
+
if (accounting2) lines.push(` ${theme.fg("dim", `accounting=input=${String(accounting2.input)} output=${String(accounting2.output)} cacheRead=${String(accounting2.cacheRead)} cacheWrite=${String(accounting2.cacheWrite)} cost=${formatCost(accounting2.cost) || "$0.00"}`)}`);
|
|
7622
8099
|
if (status.worktree) lines.push(` ${theme.fg("dim", `worktree=${status.worktree.path} branch=${status.worktree.branch}`)}`);
|
|
7623
8100
|
const toolCalls = status.progress?.toolCalls;
|
|
7624
8101
|
if (toolCalls?.length) {
|
|
@@ -7650,7 +8127,7 @@ function subagentProgressBlock(status, args, theme, freezeAt) {
|
|
|
7650
8127
|
render(width) {
|
|
7651
8128
|
const now = TERMINAL_STATES.has(current.state) ? frozenAt : Date.now();
|
|
7652
8129
|
if (!TERMINAL_STATES.has(current.state)) frozenAt = now;
|
|
7653
|
-
const frame =
|
|
8130
|
+
const frame = SPINNER2[Math.floor(now / REPAINT_MS) % SPINNER2.length] ?? "\u25C7";
|
|
7654
8131
|
return formatSubagentProgress(current, args, currentTheme, frame, now, expanded).split("\n").map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026"));
|
|
7655
8132
|
},
|
|
7656
8133
|
invalidate() {
|
|
@@ -7668,14 +8145,14 @@ function renderSubagentResult(result, options, theme, context) {
|
|
|
7668
8145
|
if (status?.state === "running" && options.isPartial && !state.subagentSpinner) {
|
|
7669
8146
|
state.subagentSpinner = setInterval(() => {
|
|
7670
8147
|
context.invalidate();
|
|
7671
|
-
},
|
|
8148
|
+
}, REPAINT_MS);
|
|
7672
8149
|
state.subagentSpinner.unref();
|
|
7673
8150
|
} else if ((!options.isPartial || status?.state !== "running") && state.subagentSpinner) {
|
|
7674
8151
|
clearInterval(state.subagentSpinner);
|
|
7675
8152
|
delete state.subagentSpinner;
|
|
7676
8153
|
}
|
|
7677
|
-
if (!status || context.isError) return
|
|
7678
|
-
if (!options.isPartial && status.state === "running") return
|
|
8154
|
+
if (!status || context.isError) return textBlock(resultText(result));
|
|
8155
|
+
if (!options.isPartial && status.state === "running") return textBlock(formatSubagentLaunch(status, context.args, theme, options.expanded));
|
|
7679
8156
|
if (TERMINAL_STATES.has(status.state)) state.subagentProgressFrozenAt ??= Date.now();
|
|
7680
8157
|
else delete state.subagentProgressFrozenAt;
|
|
7681
8158
|
let component = state.subagentProgressComponent;
|
|
@@ -7688,9 +8165,68 @@ function renderSubagentResult(result, options, theme, context) {
|
|
|
7688
8165
|
component.setExpanded(options.expanded);
|
|
7689
8166
|
return component;
|
|
7690
8167
|
}
|
|
7691
|
-
function
|
|
8168
|
+
function widgetRow({ status, request }, now, paint) {
|
|
8169
|
+
const lastEventAt = status.progress?.lastEventAt;
|
|
8170
|
+
const silent = lastEventAt === void 0 || !Number.isFinite(lastEventAt) || now - lastEventAt < QUIET_MS ? void 0 : now - lastEventAt;
|
|
8171
|
+
const role = silent !== void 0 && silent >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? "error" : "warning";
|
|
8172
|
+
const icon = silent === void 0 ? mark(status.state, now, paint) : paint(role, "\u26A0");
|
|
8173
|
+
const warning = silent === void 0 ? "" : ` ${paint(role, `${role === "error" ? "stalled" : "quiet"} ${formatElapsed(silent)}`)}`;
|
|
8174
|
+
const accounting2 = status.progress?.accounting;
|
|
8175
|
+
const attempts = status.attempts ?? 1;
|
|
8176
|
+
const right = [
|
|
8177
|
+
modelName(status),
|
|
8178
|
+
formatTokens(accounting2 === void 0 ? void 0 : accounting2.input + accounting2.output),
|
|
8179
|
+
formatCost(accounting2?.cost),
|
|
8180
|
+
status.startedAt === void 0 ? "" : formatElapsed(now - status.startedAt),
|
|
8181
|
+
attempts > 1 ? `attempt ${String(attempts)}` : ""
|
|
8182
|
+
].filter(Boolean).join(" \xB7 ");
|
|
8183
|
+
return { text: `${icon} ${sanitizeDisplayText(label({ ...request, id: status.id }))}${warning}`, right };
|
|
8184
|
+
}
|
|
8185
|
+
var RECEIPT_ENTRY_TYPE = "piewf-subagent-receipt";
|
|
8186
|
+
function receiptFor(status, request) {
|
|
8187
|
+
const accounting2 = status.progress?.accounting;
|
|
8188
|
+
const model = modelName(status);
|
|
8189
|
+
const role = roleName(request.role);
|
|
8190
|
+
const tools = status.progress?.state?.tools ?? status.attemptDetails?.at(-1)?.setup.tools;
|
|
8191
|
+
return {
|
|
8192
|
+
id: status.id,
|
|
8193
|
+
label: label({ ...request, id: status.id }),
|
|
8194
|
+
state: status.state,
|
|
8195
|
+
...model ? { model } : {},
|
|
8196
|
+
...role === void 0 ? {} : { role },
|
|
8197
|
+
...tools === void 0 ? {} : { tools: [...tools] },
|
|
8198
|
+
input: accounting2?.input ?? 0,
|
|
8199
|
+
output: accounting2?.output ?? 0,
|
|
8200
|
+
cacheRead: accounting2?.cacheRead ?? 0,
|
|
8201
|
+
costUsd: accounting2?.cost ?? 0,
|
|
8202
|
+
durationMs: status.startedAt === void 0 ? 0 : Math.max(0, (status.finishedAt ?? Date.now()) - status.startedAt),
|
|
8203
|
+
attempts: status.attempts ?? 1,
|
|
8204
|
+
...status.error === void 0 ? {} : { error: `${status.error.code}: ${status.error.message}` }
|
|
8205
|
+
};
|
|
8206
|
+
}
|
|
8207
|
+
function renderSubagentReceipt(data, expanded, theme) {
|
|
8208
|
+
const glyph = data.state === "completed" ? theme.fg("success", "\u2713") : data.state === "failed" ? theme.fg("error", "\u2717") : theme.fg("muted", "\xB7");
|
|
8209
|
+
const headline = [formatTokens(data.input + data.output), formatCost(data.costUsd) || "$0.00", formatElapsed(data.durationMs), data.state].filter(Boolean).join(" \xB7 ");
|
|
8210
|
+
const lines = [`${glyph} ${theme.bold(sanitizeDisplayText(data.label))} ${theme.fg("muted", headline)}`];
|
|
8211
|
+
if (!expanded) return lines;
|
|
8212
|
+
const meta = [data.model ?? "", data.role ? `role ${data.role}` : "", data.attempts > 1 ? `${String(data.attempts)} attempts` : "", data.tools?.join(" ") ?? ""].filter(Boolean);
|
|
8213
|
+
if (meta.length > 0) lines.push(theme.fg("muted", ` ${meta.join(" \xB7 ")}`));
|
|
8214
|
+
const split = [data.input ? `in ${formatTokens(data.input)}` : "", data.output ? `out ${formatTokens(data.output)}` : "", data.cacheRead ? `cache ${formatTokens(data.cacheRead)}` : ""].filter(Boolean);
|
|
8215
|
+
if (split.length > 0) lines.push(theme.fg("muted", ` ${split.join(" \xB7 ")}`));
|
|
8216
|
+
if (data.error !== void 0) lines.push(theme.fg("error", ` ${sanitizeDisplayText(data.error)}`));
|
|
8217
|
+
lines.push(theme.fg("muted", ` run ${data.id}`));
|
|
8218
|
+
return lines;
|
|
8219
|
+
}
|
|
8220
|
+
function createSubagentBackgroundWidget(host = {}) {
|
|
7692
8221
|
const key = "piewf-subagents-background";
|
|
7693
8222
|
const runs = /* @__PURE__ */ new Map();
|
|
8223
|
+
const receipted = /* @__PURE__ */ new Set();
|
|
8224
|
+
host.registerEntryRenderer?.(RECEIPT_ENTRY_TYPE, (entry, options, theme) => {
|
|
8225
|
+
if (!entry.data) return void 0;
|
|
8226
|
+
const lines = renderSubagentReceipt(entry.data, options.expanded, theme);
|
|
8227
|
+
return { render: (width) => lines.map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026")), invalidate() {
|
|
8228
|
+
} };
|
|
8229
|
+
});
|
|
7694
8230
|
let context;
|
|
7695
8231
|
let requestRender;
|
|
7696
8232
|
let timer;
|
|
@@ -7724,23 +8260,12 @@ function createSubagentBackgroundWidget() {
|
|
|
7724
8260
|
return {
|
|
7725
8261
|
render(width) {
|
|
7726
8262
|
const now = Date.now();
|
|
7727
|
-
const
|
|
7728
|
-
const
|
|
7729
|
-
const
|
|
7730
|
-
const
|
|
7731
|
-
const
|
|
7732
|
-
|
|
7733
|
-
let runRows = 0;
|
|
7734
|
-
let displayedRuns = 0;
|
|
7735
|
-
for (const block of blocks) {
|
|
7736
|
-
if (runRows + block.length > runRowBudget) break;
|
|
7737
|
-
lines.push(...block);
|
|
7738
|
-
runRows += block.length;
|
|
7739
|
-
displayedRuns += 1;
|
|
7740
|
-
}
|
|
7741
|
-
const hiddenRuns = blocks.length - displayedRuns;
|
|
7742
|
-
if (hiddenRuns > 0) lines.push(`\u2026 ${String(hiddenRuns)} more`);
|
|
7743
|
-
return lines.map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026"));
|
|
8263
|
+
const paintRole = (role, text) => theme.fg(role, text);
|
|
8264
|
+
const budget = MAX_BACKGROUND_WIDGET_ROWS - 2;
|
|
8265
|
+
const rows = [...runs.values()].map((run) => widgetRow(run, now, paintRole));
|
|
8266
|
+
const body = rows.length <= budget ? rows : [...rows.slice(0, budget - 1), { text: theme.fg("muted", `\u2026 ${String(rows.length - budget + 1)} more`) }];
|
|
8267
|
+
const title = runs.size === 1 ? "Subagent" : `Subagents \xB7 ${String(runs.size)} runs`;
|
|
8268
|
+
return drawFrame(title, "", body, width, paintRole, (text) => theme.fg("accent", text)).map((line) => truncateToWidth4(line, Math.max(1, width), ""));
|
|
7744
8269
|
},
|
|
7745
8270
|
invalidate() {
|
|
7746
8271
|
}
|
|
@@ -7749,7 +8274,7 @@ function createSubagentBackgroundWidget() {
|
|
|
7749
8274
|
showing = true;
|
|
7750
8275
|
timer = setInterval(() => {
|
|
7751
8276
|
requestRender?.();
|
|
7752
|
-
},
|
|
8277
|
+
}, REPAINT_MS);
|
|
7753
8278
|
timer.unref();
|
|
7754
8279
|
} catch {
|
|
7755
8280
|
hide();
|
|
@@ -7759,12 +8284,21 @@ function createSubagentBackgroundWidget() {
|
|
|
7759
8284
|
start(next) {
|
|
7760
8285
|
hide();
|
|
7761
8286
|
runs.clear();
|
|
8287
|
+
receipted.clear();
|
|
7762
8288
|
context = next.mode === "tui" ? next : void 0;
|
|
7763
8289
|
},
|
|
7764
8290
|
update(status, request) {
|
|
7765
8291
|
if (request.mode !== "background" || context === void 0) return;
|
|
7766
|
-
if (TERMINAL_STATES.has(status.state))
|
|
7767
|
-
|
|
8292
|
+
if (TERMINAL_STATES.has(status.state)) {
|
|
8293
|
+
runs.delete(status.id);
|
|
8294
|
+
if (!receipted.has(status.id)) {
|
|
8295
|
+
receipted.add(status.id);
|
|
8296
|
+
try {
|
|
8297
|
+
host.appendEntry?.(RECEIPT_ENTRY_TYPE, receiptFor(status, request));
|
|
8298
|
+
} catch {
|
|
8299
|
+
}
|
|
8300
|
+
}
|
|
8301
|
+
} else runs.set(status.id, { status, request });
|
|
7768
8302
|
paint();
|
|
7769
8303
|
},
|
|
7770
8304
|
dispose() {
|
|
@@ -7776,35 +8310,35 @@ function createSubagentBackgroundWidget() {
|
|
|
7776
8310
|
}
|
|
7777
8311
|
function renderSubagentInspectCall(args, theme) {
|
|
7778
8312
|
const title = theme.fg("toolTitle", theme.bold("subagents_inspect"));
|
|
7779
|
-
return
|
|
8313
|
+
return textBlock(args.id ? `${title} ${theme.fg("accent", args.id)}` : `${title} ${theme.fg("muted", args.scope === "all" ? "all" : "session")}`);
|
|
7780
8314
|
}
|
|
7781
8315
|
function renderSubagentInspectResult(result, options, theme, args) {
|
|
7782
8316
|
const status = statusValue2(result.details);
|
|
7783
|
-
if (status) return
|
|
8317
|
+
if (status) return textBlock(formatSubagentInspection(status, args, result.details, theme, options.expanded));
|
|
7784
8318
|
if (Array.isArray(result.details)) {
|
|
7785
8319
|
const statuses = result.details.map(statusValue2).filter((value) => value !== void 0);
|
|
7786
8320
|
const lines = [theme.bold(theme.fg("accent", `Subagents (${String(statuses.length)})`)), ...statuses.map((entry) => {
|
|
7787
8321
|
const color = stateColor(entry.state);
|
|
7788
8322
|
return ` ${theme.fg(color, stateGlyph(entry.state, "\u25C7"))} ${entry.id.slice(0, 8)} ${theme.fg(color, `[${entry.state}]`)}`;
|
|
7789
8323
|
})];
|
|
7790
|
-
return
|
|
8324
|
+
return textBlock(lines.join("\n"));
|
|
7791
8325
|
}
|
|
7792
|
-
return
|
|
8326
|
+
return textBlock(resultText(result));
|
|
7793
8327
|
}
|
|
7794
8328
|
function renderSubagentControlCall(name, args, theme) {
|
|
7795
8329
|
const title = theme.fg("toolTitle", theme.bold(name));
|
|
7796
8330
|
const message = args.message ? ` ${theme.fg("dim", args.message)}` : "";
|
|
7797
|
-
return
|
|
8331
|
+
return textBlock(`${title} ${theme.fg("accent", args.id)}${message}`);
|
|
7798
8332
|
}
|
|
7799
8333
|
function renderSubagentControlResult(result, theme) {
|
|
7800
8334
|
const status = statusValue2(result.details);
|
|
7801
8335
|
if (status) {
|
|
7802
8336
|
const color = stateColor(status.state);
|
|
7803
|
-
return
|
|
8337
|
+
return textBlock(`${theme.fg(color, stateGlyph(status.state, "\u25C7"))} ${status.id.slice(0, 8)} ${theme.fg(color, `[${status.state}]`)}`);
|
|
7804
8338
|
}
|
|
7805
8339
|
const record2 = typeof result.details === "object" && result.details !== null && !Array.isArray(result.details) ? result.details : void 0;
|
|
7806
|
-
if (record2 && typeof record2.id === "string") return
|
|
7807
|
-
return
|
|
8340
|
+
if (record2 && typeof record2.id === "string") return textBlock(`${theme.fg("success", "\u2713")} ${record2.id.slice(0, 8)}`);
|
|
8341
|
+
return textBlock(resultText(result));
|
|
7808
8342
|
}
|
|
7809
8343
|
|
|
7810
8344
|
// subagents/src/index.ts
|
|
@@ -7833,7 +8367,7 @@ function serialize(value) {
|
|
|
7833
8367
|
}
|
|
7834
8368
|
}
|
|
7835
8369
|
function toolResult(value) {
|
|
7836
|
-
return { content: [{ type: "text", text: serialize(value) }], details: value };
|
|
8370
|
+
return { content: [{ type: "text", text: serialize(value) }], details: value, structuredContent: value };
|
|
7837
8371
|
}
|
|
7838
8372
|
function managerContext2(toolCallId, signal, onUpdate, context) {
|
|
7839
8373
|
return {
|
|
@@ -7857,6 +8391,7 @@ function createSubagentTools(manager) {
|
|
|
7857
8391
|
"Do not poll a running ID; use subagents_inspect({ id }) when you need its current status or terminal result."
|
|
7858
8392
|
],
|
|
7859
8393
|
parameters: SUBAGENTS_RUN_PARAMETERS,
|
|
8394
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
7860
8395
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
7861
8396
|
return toolResult(await manager.run(validateSubagentRunRequest(params), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
7862
8397
|
},
|
|
@@ -7870,8 +8405,9 @@ function createSubagentTools(manager) {
|
|
|
7870
8405
|
defineTool2({
|
|
7871
8406
|
name: "subagents_inspect",
|
|
7872
8407
|
label: "Subagents Inspect",
|
|
7873
|
-
description: "Inspect durable subagent runs. Omit id for ordered run summaries, or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
|
|
8408
|
+
description: "Inspect durable subagent runs. Omit id for ordered run summaries of the current session (scope all for every session), or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
|
|
7874
8409
|
parameters: SUBAGENTS_INSPECT_PARAMETERS,
|
|
8410
|
+
outputSchema: SUBAGENTS_INSPECT_OUTPUT,
|
|
7875
8411
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
7876
8412
|
return toolResult(await manager.inspect(validateSubagentInspectRequest(params), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
7877
8413
|
},
|
|
@@ -7887,6 +8423,7 @@ function createSubagentTools(manager) {
|
|
|
7887
8423
|
label: "Subagents Steer",
|
|
7888
8424
|
description: "Send a message to a running subagent. The message is queued safely if its steering handler is not ready; settled runs cannot be steered.",
|
|
7889
8425
|
parameters: SUBAGENTS_STEER_PARAMETERS,
|
|
8426
|
+
outputSchema: SUBAGENTS_STEER_OUTPUT,
|
|
7890
8427
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
7891
8428
|
return toolResult(await manager.steer(validateSubagentSteerRequest(params), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
7892
8429
|
},
|
|
@@ -7902,6 +8439,7 @@ function createSubagentTools(manager) {
|
|
|
7902
8439
|
label: "Subagents Stop",
|
|
7903
8440
|
description: 'Stop one running subagent, abort its active session, persist state "stopped", and clean its worktree without affecting sibling runs.',
|
|
7904
8441
|
parameters: SUBAGENTS_STOP_PARAMETERS,
|
|
8442
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
7905
8443
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
7906
8444
|
return toolResult(await manager.stop(validateSubagentIdRequest(params, "subagents_stop"), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
7907
8445
|
},
|
|
@@ -7917,6 +8455,7 @@ function createSubagentTools(manager) {
|
|
|
7917
8455
|
label: "Subagents Retry",
|
|
7918
8456
|
description: "Start a fresh run from a failed or stopped subagent's persisted request. The new run gets a new ID and preserves its original background or foreground mode.",
|
|
7919
8457
|
parameters: SUBAGENTS_RETRY_PARAMETERS,
|
|
8458
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
7920
8459
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
7921
8460
|
return toolResult(await manager.retry(validateSubagentIdRequest(params, "subagents_retry"), managerContext2(toolCallId, signal, onUpdate, context)));
|
|
7922
8461
|
},
|
|
@@ -7949,7 +8488,7 @@ function managerDependencies(options, activeTools, notify, onStatus, onResourceW
|
|
|
7949
8488
|
}
|
|
7950
8489
|
function storageDirectory2(options) {
|
|
7951
8490
|
const dependencies = options.managerDependencies;
|
|
7952
|
-
return dependencies?.storageDir ??
|
|
8491
|
+
return dependencies?.storageDir ?? join11(dependencies?.agentDir ?? getAgentDir8(), "subagents");
|
|
7953
8492
|
}
|
|
7954
8493
|
function createSubagentsExtension(options = {}, activeTools, notify, onStatus, onResourceWarning) {
|
|
7955
8494
|
const manager = options.manager ?? createSubagentManager(managerDependencies(options, activeTools, notify, onStatus, onResourceWarning));
|
|
@@ -7958,7 +8497,8 @@ function createSubagentsExtension(options = {}, activeTools, notify, onStatus, o
|
|
|
7958
8497
|
}
|
|
7959
8498
|
function registerSubagentsExtension(pi, options = {}) {
|
|
7960
8499
|
const getActiveTools = pi.getActiveTools;
|
|
7961
|
-
const
|
|
8500
|
+
const getAllTools = pi.getAllTools;
|
|
8501
|
+
const activeTools = getActiveTools === void 0 ? void 0 : () => reachableTools({ getActiveTools: () => getActiveTools.call(pi), ...getAllTools === void 0 ? {} : { getAllTools: () => getAllTools.call(pi) } });
|
|
7962
8502
|
const sendMessage = pi.sendMessage;
|
|
7963
8503
|
const notify = sendMessage === void 0 ? void 0 : (notification) => {
|
|
7964
8504
|
sendMessage.call(pi, { customType: "subagents", content: notificationContent(notification), display: true, details: notification }, { deliverAs: "steer", triggerTurn: true });
|
|
@@ -7966,13 +8506,17 @@ function registerSubagentsExtension(pi, options = {}) {
|
|
|
7966
8506
|
const onResourceWarning = sendMessage === void 0 ? void 0 : (message) => {
|
|
7967
8507
|
sendMessage.call(pi, { customType: "subagents", content: `Warning: ${message}`, display: true }, { deliverAs: "steer" });
|
|
7968
8508
|
};
|
|
7969
|
-
const
|
|
8509
|
+
const appendEntry = pi.appendEntry;
|
|
8510
|
+
const widget2 = createSubagentBackgroundWidget({ ...appendEntry === void 0 ? {} : { appendEntry: (customType, data) => {
|
|
8511
|
+
appendEntry.call(pi, customType, data);
|
|
8512
|
+
} }, ...pi.registerEntryRenderer === void 0 ? {} : { registerEntryRenderer: pi.registerEntryRenderer.bind(pi) } });
|
|
7970
8513
|
const extension2 = createSubagentsExtension(options, activeTools, notify, (status, request) => {
|
|
7971
8514
|
widget2.update(status, request);
|
|
7972
8515
|
const registry = loadingRegistry();
|
|
7973
8516
|
if (typeof registry.observeSubagentStatus === "function") registry.observeSubagentStatus(status, request);
|
|
7974
8517
|
}, onResourceWarning);
|
|
7975
|
-
|
|
8518
|
+
const codemodeTools = loadCodemodeToolsSetting(options.managerDependencies?.agentDir);
|
|
8519
|
+
for (const tool of extension2.tools) pi.registerTool({ ...tool, ...workflowToolExposure(tool.name, codemodeTools) });
|
|
7976
8520
|
if (pi.registerCommand !== void 0) registerSubagentNavigator(pi.registerCommand.bind(pi), extension2.manager, storageDirectory2(options), options.clipboard);
|
|
7977
8521
|
if (pi.on !== void 0) {
|
|
7978
8522
|
setSubagentManager(extension2.manager);
|
|
@@ -7995,15 +8539,17 @@ function extension(pi, options = {}) {
|
|
|
7995
8539
|
}
|
|
7996
8540
|
export {
|
|
7997
8541
|
SUBAGENTS_ID_PARAMETERS,
|
|
8542
|
+
SUBAGENTS_INSPECT_OUTPUT,
|
|
7998
8543
|
SUBAGENTS_INSPECT_PARAMETERS,
|
|
7999
8544
|
SUBAGENTS_RETRY_PARAMETERS,
|
|
8000
8545
|
SUBAGENTS_RUN_PARAMETERS,
|
|
8546
|
+
SUBAGENTS_STEER_OUTPUT,
|
|
8001
8547
|
SUBAGENTS_STEER_PARAMETERS,
|
|
8002
8548
|
SUBAGENTS_STOP_PARAMETERS,
|
|
8003
8549
|
SUBAGENTS_TOOL_NAMES,
|
|
8004
|
-
SUBAGENTS_TOOL_SCHEMAS,
|
|
8005
8550
|
SUBAGENT_ATTEMPT_DETAILS_LIMIT,
|
|
8006
8551
|
SUBAGENT_MAX_RETRIES,
|
|
8552
|
+
SUBAGENT_STATUS_OUTPUT,
|
|
8007
8553
|
SUBAGENT_SYSTEM_PROMPT_LIMIT,
|
|
8008
8554
|
attemptValue,
|
|
8009
8555
|
createRunStoreWorktreeAdapter,
|