@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.17.0-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 +67 -0
- package/dist/src/agent-execution.js +36 -51
- package/dist/src/background-widget.d.ts +56 -0
- package/dist/src/background-widget.js +22 -15
- package/dist/src/decoders.js +2 -1
- package/dist/src/host-navigator.d.ts +18 -0
- package/dist/src/host-navigator.js +71 -13
- package/dist/src/host-view.d.ts +16 -1
- package/dist/src/host-view.js +109 -42
- package/dist/src/host.js +57 -24
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +867 -545
- package/dist/src/index.js.map +3 -3
- package/dist/src/registry.js +2 -1
- package/dist/src/roles.d.ts +70 -0
- package/dist/src/roles.js +325 -0
- package/dist/src/settings.d.ts +13 -0
- package/dist/src/settings.js +253 -0
- package/dist/src/trajectory.d.ts +29 -4
- package/dist/src/trajectory.js +58 -3
- package/dist/src/types.d.ts +0 -1
- package/dist/src/utils.d.ts +2 -1
- package/dist/src/utils.js +10 -0
- package/dist/src/validation.d.ts +5 -17
- package/dist/src/validation.js +9 -458
- package/dist/subagents/index.js +1132 -694
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +4 -0
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +2 -1
- package/dist/subagents/src/manager.js +29 -10
- package/dist/subagents/src/navigator.js +396 -247
- package/dist/subagents/src/view.d.ts +23 -2
- package/dist/subagents/src/view.js +96 -30
- package/dist/trajectory/assets/index.html +347 -231
- package/dist/trajectory/assets/prism.min.js +13 -0
- package/dist/trajectory/index.js +377 -257
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +347 -231
- 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 +143 -42
- package/dist/trajectory/src/server.js +134 -9
- package/dist/trajectory/src/server.js.map +1 -1
- package/package.json +2 -1
- package/src/agent-execution.ts +36 -42
- package/src/background-widget.ts +25 -18
- package/src/decoders.ts +2 -1
- package/src/host-navigator.ts +58 -14
- package/src/host-view.ts +93 -37
- package/src/host.ts +51 -21
- package/src/index.ts +3 -1
- package/src/registry.ts +2 -1
- package/src/roles.ts +326 -0
- package/src/settings.ts +206 -0
- package/src/trajectory.ts +76 -7
- package/src/types.ts +1 -1
- package/src/utils.ts +8 -1
- package/src/validation.ts +12 -356
- package/subagents/README.md +1 -1
- package/subagents/src/contracts.ts +4 -0
- package/subagents/src/index.ts +3 -2
- package/subagents/src/manager.ts +28 -10
- package/subagents/src/navigator.ts +336 -180
- package/subagents/src/view.ts +97 -28
- package/trajectory/src/assets/index.html +347 -231
- package/trajectory/src/assets/prism.min.js +13 -0
- package/trajectory/src/export.ts +4 -2
- package/trajectory/src/index.ts +118 -38
- package/trajectory/src/server.ts +112 -12
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
|
|
@@ -328,6 +328,12 @@ function unmatchedResourcePatterns(patterns, resources) {
|
|
|
328
328
|
function loadLaunchSnapshot(input) {
|
|
329
329
|
return deepFreeze(structuredClone(input));
|
|
330
330
|
}
|
|
331
|
+
function validateSchema(schema, at = "schema") {
|
|
332
|
+
if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema)) fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
|
|
333
|
+
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`);
|
|
334
|
+
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`);
|
|
335
|
+
if (schema.properties !== void 0 && !object(schema.properties)) fail("INVALID_SCHEMA", `${at}.properties must be an object`);
|
|
336
|
+
}
|
|
331
337
|
|
|
332
338
|
// src/paths.ts
|
|
333
339
|
import { createHash } from "node:crypto";
|
|
@@ -372,6 +378,20 @@ function budgetUsage(value) {
|
|
|
372
378
|
return { tokens: value?.tokens ?? 0, costUsd: value?.costUsd ?? 0, durationMs: value?.durationMs ?? 0, agentLaunches: value?.agentLaunches ?? 0 };
|
|
373
379
|
}
|
|
374
380
|
|
|
381
|
+
// src/validation.ts
|
|
382
|
+
import * as acorn from "acorn";
|
|
383
|
+
|
|
384
|
+
// src/roles.ts
|
|
385
|
+
import { readFileSync as readFileSync3, readdirSync, statSync } from "node:fs";
|
|
386
|
+
import { basename as basename3, dirname as dirname4, extname, join as join5, resolve as resolve4 } from "node:path";
|
|
387
|
+
import { getAgentDir as getAgentDir2, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
388
|
+
|
|
389
|
+
// src/registry.ts
|
|
390
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
391
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, join as join4 } from "node:path";
|
|
392
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
393
|
+
import { Value } from "typebox/value";
|
|
394
|
+
|
|
375
395
|
// src/io.ts
|
|
376
396
|
import { execFile } from "node:child_process";
|
|
377
397
|
import { randomUUID } from "node:crypto";
|
|
@@ -392,7 +412,7 @@ async function renameWithRetry(from, to) {
|
|
|
392
412
|
} catch (error) {
|
|
393
413
|
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
394
414
|
if (!retryableRenameError(error) || delayMs === void 0) throw error;
|
|
395
|
-
await new Promise((
|
|
415
|
+
await new Promise((resolve6) => setTimeout(resolve6, delayMs));
|
|
396
416
|
}
|
|
397
417
|
}
|
|
398
418
|
}
|
|
@@ -593,7 +613,7 @@ function decodeWorkflowExtensions(value) {
|
|
|
593
613
|
}
|
|
594
614
|
if (namespace === "trajectory") {
|
|
595
615
|
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 }
|
|
616
|
+
normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port } };
|
|
597
617
|
continue;
|
|
598
618
|
}
|
|
599
619
|
if (!jsonValue(raw)) return void 0;
|
|
@@ -775,9 +795,9 @@ function decodeAgentAttempt(value) {
|
|
|
775
795
|
const session = value.session === void 0 ? void 0 : decodeAgentSession(value.session);
|
|
776
796
|
const setup = decodeAgentSetupSummary(value.setup);
|
|
777
797
|
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:
|
|
798
|
+
const accounting2 = decodeAgentAccounting(value.accounting);
|
|
799
|
+
if (!setup || !accounting2 || value.session !== void 0 && !session || value.error !== void 0 && !error) return void 0;
|
|
800
|
+
return { attempt: value.attempt, transport: value.transport, setup, accounting: accounting2, ...session === void 0 ? {} : { session }, ...error === void 0 ? {} : { error } };
|
|
781
801
|
}
|
|
782
802
|
function decodeAgent(value) {
|
|
783
803
|
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 +818,12 @@ function decodeAgent(value) {
|
|
|
798
818
|
const startedAt = optionalNumber(value.startedAt);
|
|
799
819
|
const durationMs = optionalNumber(value.durationMs);
|
|
800
820
|
const attemptDetails = value.attemptDetails === void 0 ? void 0 : decodeArray(value.attemptDetails, decodeAgentAttempt);
|
|
801
|
-
const
|
|
821
|
+
const accounting2 = value.accounting === void 0 ? void 0 : decodeAgentAccounting(value.accounting);
|
|
802
822
|
const toolCalls = value.toolCalls === void 0 ? void 0 : decodeArray(value.toolCalls, decodeAgentToolCall);
|
|
803
823
|
const activity2 = value.activity === void 0 ? void 0 : decodeAgentActivity(value.activity);
|
|
804
824
|
const lastEventAt = optionalNumber(value.lastEventAt);
|
|
805
825
|
if (systemPrompt === INVALID_PERSISTED_VALUE || prompt === INVALID_PERSISTED_VALUE || label2 === INVALID_PERSISTED_VALUE || parentId === INVALID_PERSISTED_VALUE || resultPath2 === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE || 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 && !
|
|
826
|
+
if (!model || !tools || value.structuralPath !== void 0 && !structuralPath2 || value.attemptDetails !== void 0 && !attemptDetails || value.accounting !== void 0 && !accounting2 || value.toolCalls !== void 0 && !toolCalls || value.activity !== void 0 && !activity2) return void 0;
|
|
807
827
|
return {
|
|
808
828
|
...systemPrompt === void 0 ? {} : { systemPrompt },
|
|
809
829
|
...prompt === void 0 ? {} : { prompt },
|
|
@@ -827,7 +847,7 @@ function decodeAgent(value) {
|
|
|
827
847
|
...startedAt === void 0 ? {} : { startedAt },
|
|
828
848
|
...durationMs === void 0 ? {} : { durationMs },
|
|
829
849
|
...attemptDetails === void 0 ? {} : { attemptDetails },
|
|
830
|
-
...
|
|
850
|
+
...accounting2 === void 0 ? {} : { accounting: accounting2 },
|
|
831
851
|
...toolCalls === void 0 ? {} : { toolCalls },
|
|
832
852
|
...activity2 === void 0 ? {} : { activity: activity2 },
|
|
833
853
|
...lastEventAt === void 0 ? {} : { lastEventAt }
|
|
@@ -873,9 +893,9 @@ function decodeShellActivity(value) {
|
|
|
873
893
|
}
|
|
874
894
|
function decodeRunEvent(value) {
|
|
875
895
|
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, ...
|
|
896
|
+
const timestamp2 = optionalNumber(value.timestamp);
|
|
897
|
+
if (timestamp2 === INVALID_PERSISTED_VALUE) return void 0;
|
|
898
|
+
return { type: value.type, message: value.message, ...timestamp2 === void 0 ? {} : { timestamp: timestamp2 } };
|
|
879
899
|
}
|
|
880
900
|
function decodeDelivery(value) {
|
|
881
901
|
if (!object(value)) return void 0;
|
|
@@ -1920,19 +1940,220 @@ var RunStore = class _RunStore {
|
|
|
1920
1940
|
}
|
|
1921
1941
|
};
|
|
1922
1942
|
|
|
1923
|
-
// src/
|
|
1924
|
-
import { mkdirSync, readFileSync
|
|
1943
|
+
// src/settings.ts
|
|
1944
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
1925
1945
|
import { homedir as homedir3 } from "node:os";
|
|
1926
|
-
import {
|
|
1927
|
-
import { fileURLToPath
|
|
1928
|
-
import
|
|
1929
|
-
|
|
1946
|
+
import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
|
|
1947
|
+
import { fileURLToPath } from "node:url";
|
|
1948
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
1949
|
+
var ROLE_DIRECTORY = "pi-extensible-workflows";
|
|
1950
|
+
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
1951
|
+
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
1952
|
+
return join3(agentDir, ROLE_DIRECTORY, "settings.json");
|
|
1953
|
+
}
|
|
1954
|
+
function normalizedResourcePath(value, settingsPath) {
|
|
1955
|
+
if (value === "*") return value;
|
|
1956
|
+
let expanded = value === "~" ? homedir3() : value.startsWith("~/") || value.startsWith("~\\") ? join3(homedir3(), value.slice(2)) : value;
|
|
1957
|
+
if (expanded.startsWith("file://")) expanded = fileURLToPath(expanded);
|
|
1958
|
+
const resolved = resolve3(dirname2(settingsPath), expanded);
|
|
1959
|
+
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
1960
|
+
if (resourcePatternHasMagic(expanded)) {
|
|
1961
|
+
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
1962
|
+
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
1963
|
+
const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
|
|
1964
|
+
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
1965
|
+
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
1966
|
+
return `${canonicalPath(prefix)}${suffix}`;
|
|
1967
|
+
}
|
|
1968
|
+
return canonicalPath(resolved);
|
|
1969
|
+
}
|
|
1970
|
+
function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
1971
|
+
if (value === void 0) return void 0;
|
|
1972
|
+
if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
|
|
1973
|
+
const normalized = [];
|
|
1974
|
+
for (const [index, entry] of value.entries()) {
|
|
1975
|
+
if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
1976
|
+
let selector = entry.trim();
|
|
1977
|
+
if (kind === "extensions" && normalizeExtensions) {
|
|
1978
|
+
const negated = selector.startsWith("!");
|
|
1979
|
+
const body = negated ? selector.slice(1) : selector;
|
|
1980
|
+
if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
1981
|
+
try {
|
|
1982
|
+
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
1983
|
+
} catch (error) {
|
|
1984
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
try {
|
|
1988
|
+
validateResourcePattern(selector);
|
|
1989
|
+
} catch (error) {
|
|
1990
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
1991
|
+
}
|
|
1992
|
+
normalized.push(selector);
|
|
1993
|
+
}
|
|
1994
|
+
return Object.freeze(normalized);
|
|
1995
|
+
}
|
|
1996
|
+
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
1997
|
+
if (value === void 0) return void 0;
|
|
1998
|
+
if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
1999
|
+
return Object.freeze(value.map((entry, index) => {
|
|
2000
|
+
if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
2001
|
+
return entry;
|
|
2002
|
+
}));
|
|
2003
|
+
}
|
|
2004
|
+
function selectorsFromSettings(settings) {
|
|
2005
|
+
return {
|
|
2006
|
+
...settings.skills === void 0 ? {} : { skills: settings.skills },
|
|
2007
|
+
...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
|
|
2008
|
+
...settings.tools === void 0 ? {} : { tools: settings.tools }
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
function selectorSet(value) {
|
|
2012
|
+
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2013
|
+
}
|
|
2014
|
+
function validateContextFileScopes(value, rolePath) {
|
|
2015
|
+
if (value === void 0) return void 0;
|
|
2016
|
+
if (!Array.isArray(value) || !value.every(isContextFileScope)) fail("INVALID_METADATA", `${rolePath}.contextFiles must be an array containing only global, project, or cwd`);
|
|
2017
|
+
return [...value];
|
|
2018
|
+
}
|
|
2019
|
+
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS") {
|
|
2020
|
+
if (value === void 0) return void 0;
|
|
2021
|
+
const base = `${settingsPath}.extensionSettings`;
|
|
2022
|
+
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2023
|
+
const normalized = {};
|
|
2024
|
+
for (const [namespace, raw] of Object.entries(value)) {
|
|
2025
|
+
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2026
|
+
if (namespace === "herdr") {
|
|
2027
|
+
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2028
|
+
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
2029
|
+
if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
2030
|
+
normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
|
|
2031
|
+
continue;
|
|
2032
|
+
}
|
|
2033
|
+
if (namespace === "trajectory") {
|
|
2034
|
+
if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
|
|
2035
|
+
if (Object.keys(raw).some((key) => key !== "port")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
|
|
2036
|
+
if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
2037
|
+
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port } });
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
2040
|
+
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2041
|
+
normalized[namespace] = structuredClone(raw);
|
|
2042
|
+
}
|
|
2043
|
+
return deepFreeze(normalized);
|
|
2044
|
+
}
|
|
2045
|
+
function positiveRetentionInteger(value) {
|
|
2046
|
+
return positiveInteger(value) && Number.isSafeInteger(value);
|
|
2047
|
+
}
|
|
2048
|
+
function validateRetention(value, settingsPath) {
|
|
2049
|
+
if (value === void 0) return void 0;
|
|
2050
|
+
const base = `${settingsPath}.retention`;
|
|
2051
|
+
if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
2052
|
+
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
2053
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
2054
|
+
if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
2055
|
+
if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
2056
|
+
return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
|
|
2057
|
+
}
|
|
2058
|
+
function parseSettings(path, partial) {
|
|
2059
|
+
let parsed;
|
|
2060
|
+
try {
|
|
2061
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
2062
|
+
} catch (error) {
|
|
2063
|
+
if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
2064
|
+
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2065
|
+
}
|
|
2066
|
+
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2067
|
+
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget"]]);
|
|
2068
|
+
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2069
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2070
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
2071
|
+
const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
2072
|
+
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2073
|
+
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2074
|
+
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2075
|
+
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2076
|
+
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2077
|
+
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
2078
|
+
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
2079
|
+
const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS");
|
|
2080
|
+
const retention = validateRetention(parsed.retention, path);
|
|
2081
|
+
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
2082
|
+
return Object.freeze({
|
|
2083
|
+
...concurrency === void 0 ? {} : { concurrency },
|
|
2084
|
+
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2085
|
+
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2086
|
+
...skills === void 0 ? {} : { skills },
|
|
2087
|
+
...extensions === void 0 ? {} : { extensions },
|
|
2088
|
+
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2089
|
+
...tools === void 0 ? {} : { tools },
|
|
2090
|
+
...retention === void 0 ? {} : { retention },
|
|
2091
|
+
...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
function loadSettings(path = workflowSettingsPath()) {
|
|
2095
|
+
return parseSettings(path, false);
|
|
2096
|
+
}
|
|
2097
|
+
function loadSettingsOverrides(path) {
|
|
2098
|
+
return parseSettings(path, true);
|
|
2099
|
+
}
|
|
2100
|
+
function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2101
|
+
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2102
|
+
const global = loadSettings(globalSettingsPath);
|
|
2103
|
+
const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2104
|
+
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
2105
|
+
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
2106
|
+
const globalSelectors = selectorsFromSettings(global);
|
|
2107
|
+
const projectSelectors = selectorsFromSettings(project);
|
|
2108
|
+
const effectiveSelectors = selectorSet({
|
|
2109
|
+
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2110
|
+
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2111
|
+
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2112
|
+
});
|
|
2113
|
+
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2114
|
+
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2115
|
+
const sources = {
|
|
2116
|
+
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2117
|
+
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2118
|
+
skills: sourceFor("skills"),
|
|
2119
|
+
extensions: sourceFor("extensions"),
|
|
2120
|
+
tools: sourceFor("tools"),
|
|
2121
|
+
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2122
|
+
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2123
|
+
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2124
|
+
};
|
|
2125
|
+
const effective = Object.freeze({
|
|
2126
|
+
concurrency: project.concurrency ?? global.concurrency,
|
|
2127
|
+
backgroundWidget: global.backgroundWidget ?? true,
|
|
2128
|
+
...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
|
|
2129
|
+
...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
|
|
2130
|
+
...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
|
|
2131
|
+
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2132
|
+
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2133
|
+
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2134
|
+
...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
|
|
2135
|
+
});
|
|
2136
|
+
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
|
|
2137
|
+
}
|
|
2138
|
+
function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
2139
|
+
for (const name of names) {
|
|
2140
|
+
try {
|
|
2141
|
+
const target = modelCapability(name, aliases, knownModels, settingsPath);
|
|
2142
|
+
if (!availableModels.has(target)) unknownModel(name, target, settingsPath);
|
|
2143
|
+
} catch (error) {
|
|
2144
|
+
throw annotateModelAliasError(error, name);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2149
|
+
const resolved = resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath);
|
|
2150
|
+
const global = selectorSet(selectorsFromSettings(resolved.global));
|
|
2151
|
+
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
2152
|
+
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
2153
|
+
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: selectorsFromSettings(resolved.global), project: selectorsFromSettings(resolved.project) } };
|
|
2154
|
+
}
|
|
1930
2155
|
|
|
1931
2156
|
// src/registry.ts
|
|
1932
|
-
import { readFileSync } from "node:fs";
|
|
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
2157
|
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"]);
|
|
1937
2158
|
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
1938
2159
|
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
@@ -1941,10 +2162,10 @@ function normalizeRoleDirectory(value) {
|
|
|
1941
2162
|
try {
|
|
1942
2163
|
if (value instanceof URL) {
|
|
1943
2164
|
if (value.protocol !== "file:") fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
1944
|
-
return
|
|
2165
|
+
return fileURLToPath2(value);
|
|
1945
2166
|
}
|
|
1946
2167
|
if (typeof value === "string") {
|
|
1947
|
-
if (value.startsWith("file:")) return
|
|
2168
|
+
if (value.startsWith("file:")) return fileURLToPath2(value);
|
|
1948
2169
|
if (isAbsolute(value)) return value;
|
|
1949
2170
|
}
|
|
1950
2171
|
} catch (error) {
|
|
@@ -1954,12 +2175,12 @@ function normalizeRoleDirectory(value) {
|
|
|
1954
2175
|
}
|
|
1955
2176
|
function isBuiltinRoleDirectory(path) {
|
|
1956
2177
|
const canonical = canonicalPath(path);
|
|
1957
|
-
const starterDirectory =
|
|
2178
|
+
const starterDirectory = dirname3(canonical);
|
|
1958
2179
|
if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
|
|
1959
|
-
const distributionDirectory =
|
|
1960
|
-
const packageRoot2 = basename2(distributionDirectory) === "dist" ?
|
|
2180
|
+
const distributionDirectory = dirname3(starterDirectory);
|
|
2181
|
+
const packageRoot2 = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
|
|
1961
2182
|
try {
|
|
1962
|
-
const metadata = JSON.parse(
|
|
2183
|
+
const metadata = JSON.parse(readFileSync2(join4(packageRoot2, "package.json"), "utf8"));
|
|
1963
2184
|
return object(metadata) && isCorePackageName(metadata.name);
|
|
1964
2185
|
} catch {
|
|
1965
2186
|
return false;
|
|
@@ -2209,244 +2430,40 @@ function createWorkflowRegistryApi(registry) {
|
|
|
2209
2430
|
catalogIndex: (context) => registry.catalogIndex(context),
|
|
2210
2431
|
catalogDetail: (name, context) => registry.catalogDetail(name, context),
|
|
2211
2432
|
globals: () => registry.globals(),
|
|
2212
|
-
invokeFunction: (...args) => registry.invokeFunction(...args),
|
|
2213
|
-
validateExtensionSettings: (...args) => {
|
|
2214
|
-
registry.validateExtensionSettings(...args);
|
|
2215
|
-
},
|
|
2216
|
-
modelAliases: () => registry.modelAliases(),
|
|
2217
|
-
resolveModelAliases: (...args) => registry.resolveModelAliases(...args),
|
|
2218
|
-
roleDirectories: () => registry.roleDirectories(),
|
|
2219
|
-
roleDirectoryRegistrations: () => registry.roleDirectoryRegistrations(),
|
|
2220
|
-
agentSetupHooks: () => registry.agentSetupHooks(),
|
|
2221
|
-
agentAttemptActions: () => registry.agentAttemptActions()
|
|
2222
|
-
};
|
|
2223
|
-
}
|
|
2224
|
-
function workflowRegistryHost() {
|
|
2225
|
-
return globalRegistry[WORKFLOW_REGISTRY_KEY] ??= { api: createWorkflowRegistryApi(new WorkflowRegistry()), activeHosts: 0 };
|
|
2226
|
-
}
|
|
2227
|
-
function resetWorkflowRegistry() {
|
|
2228
|
-
workflowRegistryHost().api = createWorkflowRegistryApi(new WorkflowRegistry());
|
|
2229
|
-
}
|
|
2230
|
-
function beginWorkflowExtensionLoading() {
|
|
2231
|
-
const host = workflowRegistryHost();
|
|
2232
|
-
if (host.api.frozen && host.activeHosts === 0) resetWorkflowRegistry();
|
|
2233
|
-
}
|
|
2234
|
-
function loadingRegistry() {
|
|
2235
|
-
return workflowRegistryHost().api;
|
|
2236
|
-
}
|
|
2237
|
-
beginWorkflowExtensionLoading();
|
|
2238
|
-
function registeredWorkflowRoleDirectoryRegistrations() {
|
|
2239
|
-
const registrations = loadingRegistry().roleDirectoryRegistrations;
|
|
2240
|
-
return typeof registrations === "function" ? registrations() : [];
|
|
2241
|
-
}
|
|
2242
|
-
|
|
2243
|
-
// src/validation.ts
|
|
2244
|
-
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
2245
|
-
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
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 };
|
|
2433
|
+
invokeFunction: (...args) => registry.invokeFunction(...args),
|
|
2434
|
+
validateExtensionSettings: (...args) => {
|
|
2435
|
+
registry.validateExtensionSettings(...args);
|
|
2436
|
+
},
|
|
2437
|
+
modelAliases: () => registry.modelAliases(),
|
|
2438
|
+
resolveModelAliases: (...args) => registry.resolveModelAliases(...args),
|
|
2439
|
+
roleDirectories: () => registry.roleDirectories(),
|
|
2440
|
+
roleDirectoryRegistrations: () => registry.roleDirectoryRegistrations(),
|
|
2441
|
+
agentSetupHooks: () => registry.agentSetupHooks(),
|
|
2442
|
+
agentAttemptActions: () => registry.agentAttemptActions()
|
|
2443
|
+
};
|
|
2432
2444
|
}
|
|
2433
|
-
function
|
|
2434
|
-
|
|
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
|
-
}
|
|
2445
|
+
function workflowRegistryHost() {
|
|
2446
|
+
return globalRegistry[WORKFLOW_REGISTRY_KEY] ??= { api: createWorkflowRegistryApi(new WorkflowRegistry()), activeHosts: 0 };
|
|
2442
2447
|
}
|
|
2443
|
-
function
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
const
|
|
2448
|
-
|
|
2448
|
+
function resetWorkflowRegistry() {
|
|
2449
|
+
workflowRegistryHost().api = createWorkflowRegistryApi(new WorkflowRegistry());
|
|
2450
|
+
}
|
|
2451
|
+
function beginWorkflowExtensionLoading() {
|
|
2452
|
+
const host = workflowRegistryHost();
|
|
2453
|
+
if (host.api.frozen && host.activeHosts === 0) resetWorkflowRegistry();
|
|
2454
|
+
}
|
|
2455
|
+
function loadingRegistry() {
|
|
2456
|
+
return workflowRegistryHost().api;
|
|
2457
|
+
}
|
|
2458
|
+
beginWorkflowExtensionLoading();
|
|
2459
|
+
function registeredWorkflowRoleDirectoryRegistrations() {
|
|
2460
|
+
const registrations = loadingRegistry().roleDirectoryRegistrations;
|
|
2461
|
+
return typeof registrations === "function" ? registrations() : [];
|
|
2449
2462
|
}
|
|
2463
|
+
|
|
2464
|
+
// src/roles.ts
|
|
2465
|
+
var ROLE_DIRECTORY2 = "pi-extensible-workflows";
|
|
2466
|
+
var roleDefinitionPaths = /* @__PURE__ */ new WeakMap();
|
|
2450
2467
|
function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
2451
2468
|
if (!strict) {
|
|
2452
2469
|
if (!content.startsWith("---\n")) return { prompt: content };
|
|
@@ -2473,7 +2490,7 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
2473
2490
|
}
|
|
2474
2491
|
}
|
|
2475
2492
|
const rolePathValue2 = rolePath ?? "<role>";
|
|
2476
|
-
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA"
|
|
2493
|
+
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA");
|
|
2477
2494
|
const definition2 = { prompt: content.slice(end + 4).replace(/^\n/, "") };
|
|
2478
2495
|
if (meta.model) {
|
|
2479
2496
|
const model2 = unquote(meta.model);
|
|
@@ -2513,7 +2530,7 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
2513
2530
|
const normalizedTools = validateSelectorList(tools, rolePathValue, "tools", "INVALID_METADATA");
|
|
2514
2531
|
const normalizedSkills = validateSelectorList(skills, rolePathValue, "skills", "INVALID_METADATA");
|
|
2515
2532
|
const normalizedExtensions = validateSelectorList(extensions, rolePathValue, "extensions", "INVALID_METADATA");
|
|
2516
|
-
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA"
|
|
2533
|
+
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA");
|
|
2517
2534
|
const normalizedDescription = typeof description === "string" ? description.trim() : void 0;
|
|
2518
2535
|
const normalizedModel = typeof model === "string" ? model.trim() : void 0;
|
|
2519
2536
|
if (normalizedModel !== void 0) assertModelThinking(normalizedModel, "Role model");
|
|
@@ -2528,12 +2545,11 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
2528
2545
|
if (normalizedContextFiles !== void 0) definition.contextFiles = normalizedContextFiles;
|
|
2529
2546
|
return definition;
|
|
2530
2547
|
}
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
return [join4(agentDir, ROLE_DIRECTORY, "roles")];
|
|
2548
|
+
function workflowRoleDirectories(agentDir = getAgentDir2()) {
|
|
2549
|
+
return [join5(agentDir, ROLE_DIRECTORY2, "roles")];
|
|
2534
2550
|
}
|
|
2535
2551
|
function projectRoleDirectories(root) {
|
|
2536
|
-
return [
|
|
2552
|
+
return [join5(root, ROLE_DIRECTORY2, "roles")];
|
|
2537
2553
|
}
|
|
2538
2554
|
function roleDirectorySources(dirs) {
|
|
2539
2555
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2552,7 +2568,7 @@ function isRoleFile(dir, entry) {
|
|
|
2552
2568
|
if (entry.isFile()) return true;
|
|
2553
2569
|
if (!entry.isSymbolicLink()) return false;
|
|
2554
2570
|
try {
|
|
2555
|
-
return statSync(
|
|
2571
|
+
return statSync(join5(dir, entry.name)).isFile();
|
|
2556
2572
|
} catch (error) {
|
|
2557
2573
|
if (isNodeError(error, "ENOENT")) return false;
|
|
2558
2574
|
throw error;
|
|
@@ -2568,11 +2584,11 @@ function scanRoleFiles(dirs, extension2) {
|
|
|
2568
2584
|
if (!extension2 && isNodeError(error, "ENOENT")) continue;
|
|
2569
2585
|
fail("INVALID_METADATA", `${roleDirectoryLabel(source, extension2)} role directory "${source.path}" could not be scanned: ${errorText(error)}`);
|
|
2570
2586
|
}
|
|
2571
|
-
for (const entry of entries) if (isRoleFile(source.path, entry)) files.push({ name: basename3(entry.name, ".md"), path:
|
|
2587
|
+
for (const entry of entries) if (isRoleFile(source.path, entry)) files.push({ name: basename3(entry.name, ".md"), path: join5(source.path, entry.name), source });
|
|
2572
2588
|
}
|
|
2573
2589
|
return files.sort((left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path));
|
|
2574
2590
|
}
|
|
2575
|
-
function
|
|
2591
|
+
function roleEntries(dirs, extension2) {
|
|
2576
2592
|
const files = scanRoleFiles(dirs, extension2);
|
|
2577
2593
|
const starterFiles = files.filter(({ source }) => source.builtin === true);
|
|
2578
2594
|
const regularFiles = files.filter(({ source }) => source.builtin !== true);
|
|
@@ -2583,7 +2599,9 @@ function readRoleDefinitions(dirs, extension2 = false) {
|
|
|
2583
2599
|
}
|
|
2584
2600
|
const read = (roleFiles) => Object.fromEntries(roleFiles.map(({ name, path, source }) => {
|
|
2585
2601
|
try {
|
|
2586
|
-
|
|
2602
|
+
const definition = parseRoleMarkdown(readFileSync3(path, "utf8"), true, path);
|
|
2603
|
+
roleDefinitionPaths.set(definition, path);
|
|
2604
|
+
return [name, { name, path, source, definition }];
|
|
2587
2605
|
} catch (error) {
|
|
2588
2606
|
if (extension2) fail("INVALID_METADATA", `${roleDirectoryLabel(source)} role directory "${source.path}" contains invalid role "${name}" at "${path}": ${errorText(error)}`);
|
|
2589
2607
|
throw error;
|
|
@@ -2591,15 +2609,114 @@ function readRoleDefinitions(dirs, extension2 = false) {
|
|
|
2591
2609
|
}));
|
|
2592
2610
|
return extension2 ? { ...read(starterFiles), ...read(regularFiles) } : read(files);
|
|
2593
2611
|
}
|
|
2594
|
-
function
|
|
2595
|
-
return
|
|
2612
|
+
function discoveryInput(input) {
|
|
2613
|
+
return { cwd: input.cwd, agentDir: input.agentDir ?? getAgentDir2(), projectTrusted: input.projectTrusted ?? true, extensionRoleDirectories: input.extensionRoleDirectories ?? registeredWorkflowRoleDirectoryRegistrations() };
|
|
2596
2614
|
}
|
|
2597
|
-
function
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2615
|
+
function discoveredRoleEntries(input) {
|
|
2616
|
+
const resolved = discoveryInput(input);
|
|
2617
|
+
return {
|
|
2618
|
+
...roleEntries(resolved.extensionRoleDirectories, true),
|
|
2619
|
+
...roleEntries(workflowRoleDirectories(resolved.agentDir), false),
|
|
2620
|
+
...resolved.projectTrusted ? roleEntries(projectRoleDirectories(join5(resolved.cwd, ".pi")), false) : {}
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
function discoverRoles(input) {
|
|
2624
|
+
return deepFreeze(Object.fromEntries(Object.entries(discoveredRoleEntries(input)).map(([name, entry]) => [name, entry.definition])));
|
|
2625
|
+
}
|
|
2626
|
+
function loadAgentDefinitions(cwd, agentDir = getAgentDir2(), projectTrusted = true, extensionRoleDirectories = registeredWorkflowRoleDirectoryRegistrations()) {
|
|
2627
|
+
return discoverRoles({ cwd, agentDir, projectTrusted, extensionRoleDirectories });
|
|
2628
|
+
}
|
|
2629
|
+
function canonicalExtensionSelector(selector, base = process.cwd()) {
|
|
2630
|
+
const negated = selector.startsWith("!");
|
|
2631
|
+
const body = negated ? selector.slice(1) : selector;
|
|
2632
|
+
if (body === "*" || body === "**" || body.startsWith("**/")) return selector;
|
|
2633
|
+
const resolved = resolve4(base, body);
|
|
2634
|
+
if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
|
|
2635
|
+
return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
|
|
2636
|
+
}
|
|
2637
|
+
function selectorsFromSettings2(settings) {
|
|
2638
|
+
return { ...settings.skills === void 0 ? {} : { skills: settings.skills }, ...settings.extensions === void 0 ? {} : { extensions: settings.extensions }, ...settings.tools === void 0 ? {} : { tools: settings.tools } };
|
|
2639
|
+
}
|
|
2640
|
+
function rootToolNames(value) {
|
|
2641
|
+
return value === void 0 ? void 0 : value instanceof Set ? [...value] : [...value];
|
|
2642
|
+
}
|
|
2643
|
+
function roleDefinitionFor(name, options) {
|
|
2644
|
+
if (name === void 0) return {};
|
|
2645
|
+
if (options.definition !== void 0) {
|
|
2646
|
+
const path = roleDefinitionPaths.get(options.definition);
|
|
2647
|
+
return { definition: options.definition, ...path === void 0 ? {} : { path } };
|
|
2648
|
+
}
|
|
2649
|
+
if (options.definitions?.[name] !== void 0) {
|
|
2650
|
+
const definition = options.definitions[name];
|
|
2651
|
+
const path = roleDefinitionPaths.get(definition);
|
|
2652
|
+
return { definition, ...path === void 0 ? {} : { path } };
|
|
2653
|
+
}
|
|
2654
|
+
const entries = discoveredRoleEntries(options);
|
|
2655
|
+
const entry = entries[name];
|
|
2656
|
+
if (!entry) fail("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${name}`);
|
|
2657
|
+
return entry;
|
|
2658
|
+
}
|
|
2659
|
+
function resolveRole(name, options) {
|
|
2660
|
+
const { definition, path } = roleDefinitionFor(name, options);
|
|
2661
|
+
const settings = options.selectorSources === void 0 ? resolveWorkflowSettings(options.cwd, options.projectTrusted ?? true, options.settingsPath ?? workflowSettingsPath(options.agentDir ?? getAgentDir2())) : void 0;
|
|
2662
|
+
const baseSources = options.selectorSources ?? { global: selectorsFromSettings2(settings?.global ?? {}), project: selectorsFromSettings2(settings?.project ?? {}) };
|
|
2663
|
+
const canonicalSources = (sources) => ({
|
|
2664
|
+
...sources,
|
|
2665
|
+
global: { ...sources.global, ...sources.global.extensions === void 0 ? {} : { extensions: sources.global.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
2666
|
+
project: { ...sources.project, ...sources.project.extensions === void 0 ? {} : { extensions: sources.project.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
2667
|
+
...sources.role === void 0 ? {} : { role: { ...sources.role, ...sources.role.extensions === void 0 ? {} : { extensions: sources.role.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } },
|
|
2668
|
+
...sources.call === void 0 ? {} : { call: { ...sources.call, ...sources.call.extensions === void 0 ? {} : { extensions: sources.call.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } }
|
|
2669
|
+
});
|
|
2670
|
+
const roleLayer = definition?.skills !== void 0 || definition?.extensions !== void 0 || definition?.tools !== void 0 ? { ...definition.skills === void 0 ? {} : { skills: definition.skills }, ...definition.extensions === void 0 ? {} : { extensions: definition.extensions.map((selector) => canonicalExtensionSelector(selector, path ? dirname4(path) : options.cwd)) }, ...definition.tools === void 0 ? {} : { tools: definition.tools } } : baseSources.role;
|
|
2671
|
+
const callLayer = options.skills !== void 0 || options.extensions !== void 0 || options.tools !== void 0 ? { ...options.skills === void 0 ? {} : { skills: options.skills }, ...options.extensions === void 0 ? {} : { extensions: options.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) }, ...options.tools === void 0 ? {} : { tools: options.tools } } : baseSources.call;
|
|
2672
|
+
const selectorSources = canonicalSources({ global: baseSources.global, project: baseSources.project, ...roleLayer === void 0 ? {} : { role: roleLayer }, ...callLayer === void 0 ? {} : { call: callLayer } });
|
|
2673
|
+
const callSelectors = selectorSources.call;
|
|
2674
|
+
const roleSelectors = selectorSources.role;
|
|
2675
|
+
const selectorLayers = {
|
|
2676
|
+
skills: [selectorSources.global.skills, selectorSources.project.skills, roleSelectors?.skills, callSelectors?.skills],
|
|
2677
|
+
extensions: [selectorSources.global.extensions, selectorSources.project.extensions, roleSelectors?.extensions, callSelectors?.extensions],
|
|
2678
|
+
tools: [selectorSources.global.tools, selectorSources.project.tools, roleSelectors?.tools, callSelectors?.tools]
|
|
2679
|
+
};
|
|
2680
|
+
const resources = options.resources ?? {};
|
|
2681
|
+
const candidateTools = options.inheritedTools ?? rootToolNames(options.rootTools) ?? resources.tools;
|
|
2682
|
+
const tools = options.effectiveTools ?? (candidateTools === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.tools, candidateTools));
|
|
2683
|
+
const rootTools = rootToolNames(options.rootTools);
|
|
2684
|
+
if (rootTools !== void 0) {
|
|
2685
|
+
const outsideEffectiveTool = options.effectiveTools?.find((tool) => !candidateTools?.includes(tool));
|
|
2686
|
+
if (outsideEffectiveTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideEffectiveTool}`);
|
|
2687
|
+
const outsideCallTool = (options.tools ?? []).find((tool) => !tool.startsWith("!") && !resourcePatternHasMagic(tool) && !candidateTools?.includes(tool));
|
|
2688
|
+
if (outsideCallTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideCallTool}`);
|
|
2689
|
+
const outsideRootTool = tools?.find((tool) => !rootTools.includes(tool));
|
|
2690
|
+
if (outsideRootTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideRootTool}`);
|
|
2691
|
+
}
|
|
2692
|
+
const selectedSkills = resources.skills === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.skills, resources.skills);
|
|
2693
|
+
const extensionResources = resources.extensions?.map((extension2) => canonicalPath(extension2));
|
|
2694
|
+
const selectedExtensions = extensionResources === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.extensions, extensionResources);
|
|
2695
|
+
const unmatchedSkills = resources.skills === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.skills.flatMap((layer) => layer ?? []), resources.skills);
|
|
2696
|
+
const unmatchedExtensions = extensionResources === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.extensions.flatMap((layer) => layer ?? []), extensionResources);
|
|
2697
|
+
const unmatchedTools = candidateTools === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.tools.flatMap((layer) => layer ?? []), candidateTools);
|
|
2698
|
+
const aliases = options.modelAliases ?? settings?.effective.modelAliases ?? {};
|
|
2699
|
+
const requestedModel = options.model ?? (definition?.model && definition.thinking && !definition.model.includes(":") ? `${definition.model}:${definition.thinking}` : definition?.model);
|
|
2700
|
+
const blockedAlias = requestedModel?.split(":", 1)[0];
|
|
2701
|
+
if (requestedModel !== void 0 && blockedAlias && options.blockedAliases?.has(blockedAlias) && !modelAliasName(requestedModel, aliases)) {
|
|
2702
|
+
const target = options.blockedAliasTargets?.[blockedAlias];
|
|
2703
|
+
fail("UNKNOWN_MODEL", `Unknown model alias ${requestedModel}${target ? ` resolved to ${target}` : ""}${options.settingsPath ? ` (settings: ${options.settingsPath})` : ""}`);
|
|
2704
|
+
}
|
|
2705
|
+
let model = options.modelOverride;
|
|
2706
|
+
if (model === void 0 && requestedModel !== void 0) {
|
|
2707
|
+
assertModelThinking(requestedModel);
|
|
2708
|
+
model = resolveModelReference(requestedModel, aliases, options.knownModels ?? options.availableModels, options.settingsPath ?? settings?.sources.modelAliases);
|
|
2709
|
+
}
|
|
2710
|
+
if (model === void 0) model = options.rootModel;
|
|
2711
|
+
const availableModels = options.knownModels ?? options.availableModels ?? (options.rootModel === void 0 ? void 0 : /* @__PURE__ */ new Set([modelCapability(options.rootModel)]));
|
|
2712
|
+
if (model !== void 0 && availableModels !== void 0 && !availableModels.has(modelCapability(model))) fail("UNKNOWN_MODEL", `Unknown model${requestedModel ? ` ${requestedModel} resolved to ${modelCapability(model)}` : ""}${options.settingsPath ? ` (settings: ${options.settingsPath})` : ""}`);
|
|
2713
|
+
const prompt = definition?.prompt ?? "";
|
|
2714
|
+
const overrideSystemPrompt = definition?.overrideSystemPrompt === true;
|
|
2715
|
+
const contextFiles = options.contextFiles ?? definition?.contextFiles;
|
|
2716
|
+
return { ...name === void 0 ? {} : { name }, ...definition === void 0 ? {} : { definition }, ...model === void 0 ? {} : { model }, ...modelAliasName(requestedModel ?? "", aliases) && requestedModel ? { requestedModel } : {}, ...tools === void 0 ? {} : { tools }, prompt, overrideSystemPrompt, systemPrompt: { mode: overrideSystemPrompt ? "override" : "append", text: prompt }, ...contextFiles === void 0 ? {} : { contextFiles: [...contextFiles] }, ...definition?.extensionSettings === void 0 ? {} : { extensionSettings: definition.extensionSettings }, selectorSources, selectorLayers, ...selectedSkills === void 0 ? {} : { selectedSkills }, ...selectedExtensions === void 0 ? {} : { selectedExtensions }, ...unmatchedSkills === void 0 ? {} : { unmatchedSkills }, ...unmatchedExtensions === void 0 ? {} : { unmatchedExtensions }, ...unmatchedTools === void 0 ? {} : { unmatchedTools } };
|
|
2602
2717
|
}
|
|
2718
|
+
|
|
2719
|
+
// src/validation.ts
|
|
2603
2720
|
var AGENT_OPTION_KEYS = /* @__PURE__ */ new Set(["label", "model", "tools", "skills", "extensions", "contextFiles", "role", "outputSchema", "retries", "timeoutMs"]);
|
|
2604
2721
|
function validateAgentOption(key, value, aliases, knownModels, settingsPath) {
|
|
2605
2722
|
switch (key) {
|
|
@@ -2949,17 +3066,17 @@ Promise.resolve().then(() => new vm.Script("(async(__pi_extensible_workflows_age
|
|
|
2949
3066
|
|
|
2950
3067
|
// src/host.ts
|
|
2951
3068
|
import { Type as Type3 } from "@earendil-works/pi-ai";
|
|
2952
|
-
import { copyToClipboard, getAgentDir as
|
|
3069
|
+
import { copyToClipboard, getAgentDir as getAgentDir5, ModelSelectorComponent } from "@earendil-works/pi-coding-agent";
|
|
2953
3070
|
|
|
2954
3071
|
// src/agent-execution.ts
|
|
2955
|
-
import { existsSync, realpathSync as realpathSync2, readFileSync as
|
|
3072
|
+
import { existsSync, realpathSync as realpathSync2, readFileSync as readFileSync4 } from "node:fs";
|
|
2956
3073
|
import { copyFile } from "node:fs/promises";
|
|
2957
3074
|
import { homedir as homedir4 } from "node:os";
|
|
2958
|
-
import { basename as basename4, dirname as
|
|
3075
|
+
import { basename as basename4, dirname as dirname5, join as join6, resolve as resolve5 } from "node:path";
|
|
2959
3076
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2960
3077
|
import { Type } from "@earendil-works/pi-ai";
|
|
2961
3078
|
import { Compile } from "typebox/compile";
|
|
2962
|
-
import { createAgentSession, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as
|
|
3079
|
+
import { createAgentSession, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as getAgentDir3, ModelRuntime, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
2963
3080
|
|
|
2964
3081
|
// src/pi-runtime-adapter.ts
|
|
2965
3082
|
var TURN_START_EVENTS = /* @__PURE__ */ new Set(["turn_start", "turn_started", "turnStarted"]);
|
|
@@ -3007,16 +3124,16 @@ function normalizeState(value) {
|
|
|
3007
3124
|
const candidate = record(value);
|
|
3008
3125
|
const model = record(candidate?.model);
|
|
3009
3126
|
const provider = stringValue(model?.provider);
|
|
3010
|
-
const
|
|
3127
|
+
const modelName2 = stringValue(model?.model);
|
|
3011
3128
|
const rawTools = candidate?.tools;
|
|
3012
3129
|
const tools = Array.isArray(rawTools) ? rawTools.filter((tool) => typeof tool === "string") : void 0;
|
|
3013
|
-
if (!candidate || !model || provider === void 0 ||
|
|
3130
|
+
if (!candidate || !model || provider === void 0 || modelName2 === void 0 || tools === void 0 || !Array.isArray(rawTools) || tools.length !== rawTools.length) return void 0;
|
|
3014
3131
|
const thinking = candidate.thinking === void 0 ? model.thinking : candidate.thinking;
|
|
3015
3132
|
if (thinking !== void 0 && !isThinkingLevel(thinking)) return void 0;
|
|
3016
3133
|
const systemPrompt = candidate.systemPrompt;
|
|
3017
3134
|
if (systemPrompt !== void 0 && typeof systemPrompt !== "string") return void 0;
|
|
3018
3135
|
return {
|
|
3019
|
-
model: { provider, model:
|
|
3136
|
+
model: { provider, model: modelName2, ...thinking === void 0 ? {} : { thinking } },
|
|
3020
3137
|
...thinking === void 0 ? {} : { thinking },
|
|
3021
3138
|
tools: [...tools],
|
|
3022
3139
|
...systemPrompt === void 0 ? {} : { systemPrompt }
|
|
@@ -3234,8 +3351,8 @@ function createLiveSessionHandoff() {
|
|
|
3234
3351
|
let request;
|
|
3235
3352
|
const waitForBoundary = () => {
|
|
3236
3353
|
if (!turnActive) return Promise.resolve();
|
|
3237
|
-
boundary ??= new Promise((
|
|
3238
|
-
resolveBoundary =
|
|
3354
|
+
boundary ??= new Promise((resolve6) => {
|
|
3355
|
+
resolveBoundary = resolve6;
|
|
3239
3356
|
});
|
|
3240
3357
|
return boundary;
|
|
3241
3358
|
};
|
|
@@ -3267,11 +3384,11 @@ function createLiveSessionHandoff() {
|
|
|
3267
3384
|
if (request) return request;
|
|
3268
3385
|
if (isReleased()) return;
|
|
3269
3386
|
state = "handoff-pending";
|
|
3270
|
-
takeover = new Promise((
|
|
3271
|
-
resolveTakeover =
|
|
3387
|
+
takeover = new Promise((resolve6) => {
|
|
3388
|
+
resolveTakeover = resolve6;
|
|
3272
3389
|
});
|
|
3273
|
-
resume = new Promise((
|
|
3274
|
-
resolveResume =
|
|
3390
|
+
resume = new Promise((resolve6) => {
|
|
3391
|
+
resolveResume = resolve6;
|
|
3275
3392
|
});
|
|
3276
3393
|
request = (async () => {
|
|
3277
3394
|
await waitForBoundary();
|
|
@@ -3798,36 +3915,28 @@ function parseModel(value, fallback, aliases = {}, knownModels, settingsPath) {
|
|
|
3798
3915
|
function accounting(stats) {
|
|
3799
3916
|
return { input: stats.tokens.input, output: stats.tokens.output, cacheRead: stats.tokens.cacheRead, cacheWrite: stats.tokens.cacheWrite, cost: stats.cost };
|
|
3800
3917
|
}
|
|
3801
|
-
var extensionDirectory =
|
|
3802
|
-
var workflowPackageRoot = basename4(
|
|
3918
|
+
var extensionDirectory = dirname5(fileURLToPath3(import.meta.url));
|
|
3919
|
+
var workflowPackageRoot = basename4(dirname5(extensionDirectory)) === "dist" ? resolve5(extensionDirectory, "../..") : resolve5(extensionDirectory, "..");
|
|
3803
3920
|
var WORKFLOW_HOST_ENTRIES = /* @__PURE__ */ new Set([
|
|
3804
|
-
canonicalPath(
|
|
3805
|
-
canonicalPath(
|
|
3806
|
-
canonicalPath(
|
|
3807
|
-
canonicalPath(
|
|
3808
|
-
canonicalPath(
|
|
3809
|
-
canonicalPath(
|
|
3921
|
+
canonicalPath(resolve5(workflowPackageRoot, "src/index.ts")),
|
|
3922
|
+
canonicalPath(resolve5(workflowPackageRoot, "dist/src/index.js")),
|
|
3923
|
+
canonicalPath(resolve5(workflowPackageRoot, "starter/index.ts")),
|
|
3924
|
+
canonicalPath(resolve5(workflowPackageRoot, "dist/starter/index.js")),
|
|
3925
|
+
canonicalPath(resolve5(workflowPackageRoot, "subagents/index.ts")),
|
|
3926
|
+
canonicalPath(resolve5(workflowPackageRoot, "dist/subagents/index.js"))
|
|
3810
3927
|
]);
|
|
3811
|
-
function canonicalExtensionSelector(selector, base = process.cwd()) {
|
|
3812
|
-
const negated = selector.startsWith("!");
|
|
3813
|
-
const body = negated ? selector.slice(1) : selector;
|
|
3814
|
-
if (body === "*" || body === "**" || body.startsWith("**/")) return selector;
|
|
3815
|
-
const resolved = resolve4(base, body);
|
|
3816
|
-
if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
|
|
3817
|
-
return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
|
|
3818
|
-
}
|
|
3819
3928
|
var WORKFLOW_DIRECTORY = "pi-extensible-workflows";
|
|
3820
3929
|
function workflowSystemPromptPath(cwd, agentDir, projectTrusted) {
|
|
3821
|
-
const projectPath =
|
|
3930
|
+
const projectPath = join6(cwd, ".pi", WORKFLOW_DIRECTORY, "SYSTEM.md");
|
|
3822
3931
|
if (projectTrusted && existsSync(projectPath)) return projectPath;
|
|
3823
|
-
const globalPaths = [
|
|
3932
|
+
const globalPaths = [join6(homedir4(), ".pi", WORKFLOW_DIRECTORY, "SYSTEM.md"), join6(agentDir, WORKFLOW_DIRECTORY, "SYSTEM.md")];
|
|
3824
3933
|
return globalPaths.find((path) => existsSync(path));
|
|
3825
3934
|
}
|
|
3826
3935
|
function filterContextFiles(base, scopes, cwd, agentDir) {
|
|
3827
3936
|
const globalPath = canonicalPath(agentDir);
|
|
3828
3937
|
const cwdPath = canonicalPath(cwd);
|
|
3829
3938
|
return base.filter(({ path }) => {
|
|
3830
|
-
const resolvedPath = canonicalPath(
|
|
3939
|
+
const resolvedPath = canonicalPath(dirname5(path));
|
|
3831
3940
|
return scopes.some((scope) => scope === "global" ? resolvedPath === globalPath : scope === "cwd" ? resolvedPath === cwdPath : resolvedPath !== globalPath && resolvedPath !== cwdPath);
|
|
3832
3941
|
});
|
|
3833
3942
|
}
|
|
@@ -3853,10 +3962,10 @@ async function loadNativePromptTemplateModule() {
|
|
|
3853
3962
|
const packageDirectory = await loadPiPackageDirectory();
|
|
3854
3963
|
let promptTemplatePath;
|
|
3855
3964
|
if (packageDirectory.directory) {
|
|
3856
|
-
const candidate =
|
|
3965
|
+
const candidate = resolve5(packageDirectory.directory, "dist/core/prompt-templates.js");
|
|
3857
3966
|
if (existsSync(candidate)) promptTemplatePath = candidate;
|
|
3858
3967
|
}
|
|
3859
|
-
promptTemplatePath ??=
|
|
3968
|
+
promptTemplatePath ??= resolve5(dirname5(fileURLToPath3(import.meta.resolve("@earendil-works/pi-coding-agent"))), "core/prompt-templates.js");
|
|
3860
3969
|
const packageDirectoryError = packageDirectory.error ? ` Package directory lookup failed: ${packageDirectory.error}` : "";
|
|
3861
3970
|
return nativePromptTemplateModule ??= import(promptTemplatePath).then((module) => module).catch((error) => {
|
|
3862
3971
|
throw new Error(`Could not load Pi prompt templates.${packageDirectoryError}`, { cause: error });
|
|
@@ -3928,13 +4037,13 @@ function flushExtensionProviders(resourceLoader, modelRuntime) {
|
|
|
3928
4037
|
return failures;
|
|
3929
4038
|
}
|
|
3930
4039
|
async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
3931
|
-
const agentDir = input.agentDir ??
|
|
4040
|
+
const agentDir = input.agentDir ?? getAgentDir3();
|
|
3932
4041
|
const systemPromptSource = workflowSystemPromptPath(input.cwd, agentDir, input.resourcePolicy?.projectTrusted ?? true);
|
|
3933
4042
|
const systemPromptOptions = input.systemPrompt !== void 0 ? { systemPromptOverride: () => input.systemPrompt } : systemPromptSource !== void 0 ? { systemPrompt: systemPromptSource } : {};
|
|
3934
4043
|
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,
|
|
4044
|
+
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
4045
|
if (!input.sessionPath) manager.appendSessionInfo(input.sessionLabel);
|
|
3937
|
-
const modelRuntime = await ModelRuntime.create({ authPath:
|
|
4046
|
+
const modelRuntime = await ModelRuntime.create({ authPath: join6(agentDir, "auth.json"), modelsPath: join6(agentDir, "models.json") });
|
|
3938
4047
|
const customTools = [...input.customTools ?? [], ...input.resultTool ? [input.resultTool] : []];
|
|
3939
4048
|
const tools = [.../* @__PURE__ */ new Set([...input.tools, ...customTools.map(({ name }) => name)])];
|
|
3940
4049
|
let settingsManager;
|
|
@@ -3947,20 +4056,22 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
3947
4056
|
const resolved = await packageManager.resolve();
|
|
3948
4057
|
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => canonicalPath(path)))];
|
|
3949
4058
|
const selectorSources = policy.selectorSources;
|
|
3950
|
-
const
|
|
3951
|
-
|
|
3952
|
-
|
|
4059
|
+
const selection = resolveRole(void 0, {
|
|
4060
|
+
cwd: input.cwd,
|
|
4061
|
+
selectorSources,
|
|
4062
|
+
resources: { extensions: discoveredExtensions }
|
|
4063
|
+
});
|
|
4064
|
+
const selectedExtensions = selection.selectedExtensions ?? [];
|
|
3953
4065
|
const extensionPaths = selectedExtensions.filter((path) => !WORKFLOW_HOST_ENTRIES.has(path));
|
|
3954
|
-
const unmatchedExtensions = extensionSelectors.filter(({ matching }) => unmatchedResourcePatterns([matching], discoveredExtensions).length > 0).map(({ original }) => original);
|
|
3955
4066
|
policy.selectedExtensions = selectedExtensions;
|
|
3956
|
-
policy.unmatchedExtensions = unmatchedExtensions;
|
|
4067
|
+
policy.unmatchedExtensions = selection.unmatchedExtensions ?? [];
|
|
3957
4068
|
const skillPaths = [...new Set(resolved.skills.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => path))];
|
|
3958
4069
|
const updateSkillMatches = (skills) => {
|
|
3959
4070
|
const names = [...new Set(skills.map(({ name }) => name))];
|
|
3960
|
-
const
|
|
3961
|
-
const selectedSkills =
|
|
4071
|
+
const selected = resolveRole(void 0, { cwd: input.cwd, selectorSources, resources: { skills: names } });
|
|
4072
|
+
const selectedSkills = selected.selectedSkills ?? [];
|
|
3962
4073
|
policy.selectedSkills = selectedSkills;
|
|
3963
|
-
policy.unmatchedSkills =
|
|
4074
|
+
policy.unmatchedSkills = selected.unmatchedSkills ?? [];
|
|
3964
4075
|
return new Set(names.filter((name) => !selectedSkills.includes(name)));
|
|
3965
4076
|
};
|
|
3966
4077
|
resourceLoader = new DefaultResourceLoader({
|
|
@@ -4439,27 +4550,27 @@ function appendResourcePolicyNarrowing(ceiling, candidate) {
|
|
|
4439
4550
|
};
|
|
4440
4551
|
}
|
|
4441
4552
|
function packageRoot(start) {
|
|
4442
|
-
let current =
|
|
4553
|
+
let current = dirname5(realpathSync2(start));
|
|
4443
4554
|
for (; ; ) {
|
|
4444
|
-
const candidates = [current,
|
|
4555
|
+
const candidates = [current, join6(current, "..", "@earendil-works", "pi-coding-agent"), join6(current, "..", "pi-coding-agent")];
|
|
4445
4556
|
for (const candidate of candidates) {
|
|
4446
|
-
const packageJsonPath =
|
|
4557
|
+
const packageJsonPath = join6(candidate, "package.json");
|
|
4447
4558
|
if (!existsSync(packageJsonPath)) continue;
|
|
4448
|
-
const packageJson = JSON.parse(
|
|
4559
|
+
const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
|
|
4449
4560
|
if (object(packageJson) && typeof packageJson.name === "string" && packageJson.name === "@earendil-works/pi-coding-agent") return candidate;
|
|
4450
4561
|
}
|
|
4451
|
-
const parent =
|
|
4562
|
+
const parent = dirname5(current);
|
|
4452
4563
|
if (parent === current) return void 0;
|
|
4453
4564
|
current = parent;
|
|
4454
4565
|
}
|
|
4455
4566
|
}
|
|
4456
4567
|
function piPackageEntrypoint(root) {
|
|
4457
|
-
const packageJson = JSON.parse(
|
|
4568
|
+
const packageJson = JSON.parse(readFileSync4(join6(root, "package.json"), "utf8"));
|
|
4458
4569
|
if (!object(packageJson)) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4459
4570
|
const bin = packageJson.bin;
|
|
4460
4571
|
const cli = typeof bin === "string" ? bin : object(bin) && typeof bin.pi === "string" ? bin.pi : void 0;
|
|
4461
4572
|
if (!cli) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
4462
|
-
const entrypoint =
|
|
4573
|
+
const entrypoint = resolve5(root, cli);
|
|
4463
4574
|
if (!existsSync(entrypoint)) throw new Error(`the originating Pi CLI entrypoint is unavailable: ${entrypoint}`);
|
|
4464
4575
|
return entrypoint;
|
|
4465
4576
|
}
|
|
@@ -4500,7 +4611,7 @@ ${instruction}` : ""}`;
|
|
|
4500
4611
|
}
|
|
4501
4612
|
async function preparedAgentSession(input, initialPrompt) {
|
|
4502
4613
|
const { runtime: piRuntime, error: piRuntimeError } = await piRuntimeResolution;
|
|
4503
|
-
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ??
|
|
4614
|
+
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ?? getAgentDir3(), input.resourcePolicy?.projectTrusted ?? true) : void 0;
|
|
4504
4615
|
const prepared = {
|
|
4505
4616
|
cwd: input.cwd,
|
|
4506
4617
|
model: Object.freeze({ ...input.model }),
|
|
@@ -4530,7 +4641,7 @@ function agentSetupSummary(setup, hookNames) {
|
|
|
4530
4641
|
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
4642
|
}
|
|
4532
4643
|
async function attemptSessionInput(source, attempt) {
|
|
4533
|
-
const target =
|
|
4644
|
+
const target = join6(dirname5(source), `${basename4(source, ".jsonl")}-attempt-${String(attempt)}.jsonl`);
|
|
4534
4645
|
await copyFile(source, target);
|
|
4535
4646
|
return target;
|
|
4536
4647
|
}
|
|
@@ -4632,7 +4743,6 @@ var WorkflowAgentExecutor = class {
|
|
|
4632
4743
|
const roleName2 = typeof options.role === "string" ? options.role : void 0;
|
|
4633
4744
|
const roleDefinition = roleName2 ? this.root.agentDefinitions?.[roleName2] : void 0;
|
|
4634
4745
|
if (roleName2 && !roleDefinition) throw new WorkflowError("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${roleName2}`);
|
|
4635
|
-
const candidateTools = inheritedTools !== void 0 ? [...inheritedTools] : [...this.root.tools];
|
|
4636
4746
|
if (inheritedTools === void 0) for (const pattern of roleDefinition?.tools ?? []) {
|
|
4637
4747
|
const body = pattern.startsWith("!") ? pattern.slice(1) : pattern;
|
|
4638
4748
|
if (!pattern.startsWith("!") && !resourcePatternHasMagic(pattern) && !this.root.tools.has(body)) {
|
|
@@ -4643,33 +4753,30 @@ var WorkflowAgentExecutor = class {
|
|
|
4643
4753
|
}
|
|
4644
4754
|
}
|
|
4645
4755
|
}
|
|
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] } };
|
|
4756
|
+
const resolved = resolveRole(roleName2, {
|
|
4757
|
+
cwd: this.root.cwd,
|
|
4758
|
+
...roleDefinition === void 0 ? {} : { definition: roleDefinition },
|
|
4759
|
+
...this.root.agentDefinitions === void 0 ? {} : { definitions: this.root.agentDefinitions },
|
|
4760
|
+
...this.root.agentDir === void 0 ? {} : { agentDir: this.root.agentDir },
|
|
4761
|
+
selectorSources: { global: this.root.resourceSelectors ?? {}, project: {} },
|
|
4762
|
+
...this.root.modelAliases === void 0 ? {} : { modelAliases: this.root.modelAliases },
|
|
4763
|
+
...this.root.knownModels === void 0 ? {} : { knownModels: this.root.knownModels },
|
|
4764
|
+
...this.root.availableModels === void 0 ? {} : { availableModels: this.root.availableModels },
|
|
4765
|
+
...this.root.blockedAliases === void 0 ? {} : { blockedAliases: this.root.blockedAliases },
|
|
4766
|
+
...this.root.blockedAliasTargets === void 0 ? {} : { blockedAliasTargets: this.root.blockedAliasTargets },
|
|
4767
|
+
...this.root.settingsPath === void 0 ? {} : { settingsPath: this.root.settingsPath },
|
|
4768
|
+
rootModel: this.root.model,
|
|
4769
|
+
rootTools: this.root.tools,
|
|
4770
|
+
...inheritedTools === void 0 ? {} : { inheritedTools },
|
|
4771
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
4772
|
+
...options.modelOverride === void 0 ? {} : { modelOverride: options.modelOverride },
|
|
4773
|
+
...options.tools === void 0 ? {} : { tools: options.tools },
|
|
4774
|
+
...options.skills === void 0 ? {} : { skills: options.skills },
|
|
4775
|
+
...options.extensions === void 0 ? {} : { extensions: options.extensions },
|
|
4776
|
+
...options.contextFiles === void 0 ? {} : { contextFiles: options.contextFiles },
|
|
4777
|
+
...options.effectiveTools === void 0 ? {} : { effectiveTools: options.effectiveTools }
|
|
4778
|
+
});
|
|
4779
|
+
return { model: resolved.model ?? this.root.model, ...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
4780
|
}
|
|
4674
4781
|
async execute(task, options, signal, customTools = [], setSteer, beforeRetry) {
|
|
4675
4782
|
const executionSignal = signal ?? this.root.runContext?.signal;
|
|
@@ -4926,7 +5033,7 @@ import { SettingsManager as SettingsManager2, truncateToVisualLines as truncateT
|
|
|
4926
5033
|
import { spawn } from "node:child_process";
|
|
4927
5034
|
import { mkdtemp, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
4928
5035
|
import { tmpdir } from "node:os";
|
|
4929
|
-
import { join as
|
|
5036
|
+
import { join as join7 } from "node:path";
|
|
4930
5037
|
function workflowPromptArtifact(prompt) {
|
|
4931
5038
|
return { extension: ".md", content: prompt };
|
|
4932
5039
|
}
|
|
@@ -4937,23 +5044,23 @@ function workflowResultArtifact(value) {
|
|
|
4937
5044
|
async function spawnWorkflowEditor(command, path) {
|
|
4938
5045
|
const [editor, ...editorArgs] = command.split(" ");
|
|
4939
5046
|
if (!editor) return null;
|
|
4940
|
-
return new Promise((
|
|
5047
|
+
return new Promise((resolve6) => {
|
|
4941
5048
|
try {
|
|
4942
5049
|
const child = spawn(editor, [...editorArgs, path], { stdio: "inherit", shell: process.platform === "win32" });
|
|
4943
5050
|
child.once("error", () => {
|
|
4944
|
-
|
|
5051
|
+
resolve6(null);
|
|
4945
5052
|
});
|
|
4946
5053
|
child.once("close", (code) => {
|
|
4947
|
-
|
|
5054
|
+
resolve6(code);
|
|
4948
5055
|
});
|
|
4949
5056
|
} catch {
|
|
4950
|
-
|
|
5057
|
+
resolve6(null);
|
|
4951
5058
|
}
|
|
4952
5059
|
});
|
|
4953
5060
|
}
|
|
4954
5061
|
async function openWorkflowArtifact(tui, command, artifact) {
|
|
4955
|
-
const directory = await mkdtemp(
|
|
4956
|
-
const path =
|
|
5062
|
+
const directory = await mkdtemp(join7(tmpdir(), "pi-workflow-editor-"));
|
|
5063
|
+
const path = join7(directory, `artifact${artifact.extension}`);
|
|
4957
5064
|
try {
|
|
4958
5065
|
await writeFile3(path, artifact.content, { encoding: "utf8", mode: 384 });
|
|
4959
5066
|
tui.stop();
|
|
@@ -4970,23 +5077,88 @@ async function openWorkflowArtifact(tui, command, artifact) {
|
|
|
4970
5077
|
|
|
4971
5078
|
// src/host-view.ts
|
|
4972
5079
|
import { keyHint, truncateToVisualLines } from "@earendil-works/pi-coding-agent";
|
|
4973
|
-
import { truncateToWidth as truncateToWidth2 } from "@earendil-works/pi-tui";
|
|
5080
|
+
import { truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
|
|
4974
5081
|
|
|
4975
5082
|
// src/background-widget.ts
|
|
4976
5083
|
import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
5084
|
+
var REPAINT_MS = 125;
|
|
4977
5085
|
var QUIET_MS = 3 * 60 * 1e3;
|
|
5086
|
+
var ROLE = {
|
|
5087
|
+
live: "accent",
|
|
5088
|
+
done: "success",
|
|
5089
|
+
failed: "error",
|
|
5090
|
+
warn: "warning",
|
|
5091
|
+
quiet: "muted"
|
|
5092
|
+
};
|
|
5093
|
+
var RESET = "\x1B[0m";
|
|
5094
|
+
var SPINNER = ["\u28F7", "\u28EF", "\u28DF", "\u287F", "\u28BF", "\u28FD", "\u28FB"];
|
|
5095
|
+
function spinner(now) {
|
|
5096
|
+
return SPINNER[Math.floor(now / REPAINT_MS) % SPINNER.length] ?? SPINNER[0];
|
|
5097
|
+
}
|
|
5098
|
+
function mark(state, now, paint, runState) {
|
|
5099
|
+
if (state === "running" && (runState === void 0 || runState === "running")) return paint(ROLE.live, spinner(now));
|
|
5100
|
+
if (state === "completed") return paint(ROLE.done, "\u2713");
|
|
5101
|
+
if (state === "failed" || state === "budget_exhausted") return paint(ROLE.failed, "\u2717");
|
|
5102
|
+
return paint(ROLE.quiet, "\xB7");
|
|
5103
|
+
}
|
|
5104
|
+
function formatElapsed(ms) {
|
|
5105
|
+
const total = Math.max(0, Math.floor(ms / 1e3));
|
|
5106
|
+
const minutes = Math.floor(total / 60);
|
|
5107
|
+
const seconds = total % 60;
|
|
5108
|
+
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
5109
|
+
}
|
|
5110
|
+
function formatTokens(tokens) {
|
|
5111
|
+
if (!tokens) return "";
|
|
5112
|
+
if (tokens < 1e3) return `${String(tokens)}t`;
|
|
5113
|
+
const thousands = tokens / 1e3;
|
|
5114
|
+
return `${thousands < 10 ? thousands.toFixed(1) : String(Math.round(thousands))}kt`;
|
|
5115
|
+
}
|
|
4978
5116
|
function formatCost(cost) {
|
|
4979
5117
|
if (!cost) return "";
|
|
4980
5118
|
return cost < 0.01 ? `$${cost.toFixed(3)}` : `$${cost.toFixed(2)}`;
|
|
4981
5119
|
}
|
|
5120
|
+
function visibleLength(text) {
|
|
5121
|
+
return visibleWidth(text);
|
|
5122
|
+
}
|
|
5123
|
+
function truncate(text, limit) {
|
|
5124
|
+
return `${truncateToWidth(text, limit, "\u2026")}${RESET}`;
|
|
5125
|
+
}
|
|
5126
|
+
function drawFrame(title, hint, body, width, paint, accent, scrollbar = () => void 0) {
|
|
5127
|
+
const inner = Math.max(20, width - 4);
|
|
5128
|
+
const lines = body.map((row, index) => {
|
|
5129
|
+
const right = row.right ?? "";
|
|
5130
|
+
const rightWidth = right === "" ? 0 : visibleLength(right) + 2;
|
|
5131
|
+
const room = Math.max(0, inner - rightWidth);
|
|
5132
|
+
const left = visibleLength(row.text) > room ? truncate(row.text, room) : row.text;
|
|
5133
|
+
const gap = Math.max(0, inner - visibleLength(left) - (right === "" ? 0 : visibleLength(right)));
|
|
5134
|
+
const text = right === "" ? left + " ".repeat(gap) : `${left}${" ".repeat(gap)}${paint(ROLE.quiet, right)}`;
|
|
5135
|
+
const rule = accent("\u2502");
|
|
5136
|
+
const bar = scrollbar(index);
|
|
5137
|
+
return `${rule} ${text} ${bar === void 0 ? rule : accent(bar)}`;
|
|
5138
|
+
});
|
|
5139
|
+
const label2 = ` ${title} `;
|
|
5140
|
+
const tail = hint === "" ? "" : ` ${hint} `;
|
|
5141
|
+
const spare = inner + 2 - visibleLength(label2) - visibleLength(tail);
|
|
5142
|
+
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`);
|
|
5143
|
+
const foot = accent(`\u2570${"\u2500".repeat(inner + 2)}\u256F`);
|
|
5144
|
+
return [heading, ...lines, foot];
|
|
5145
|
+
}
|
|
4982
5146
|
|
|
4983
5147
|
// src/host-view.ts
|
|
4984
5148
|
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 };
|
|
5149
|
+
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: "?" };
|
|
5150
|
+
function runStateGlyph(state, running) {
|
|
5151
|
+
return state === "running" ? running : RUN_STATE_GLYPH[state] ?? "\u25C6";
|
|
5152
|
+
}
|
|
5153
|
+
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
5154
|
var PHASE_STATE_STYLE = { completed: "success", failed: "error", cancelled: "error", stopped: "error", running: "accent", paused: "warning", pausing: "warning", interrupted: "warning", budget_exhausted: "warning" };
|
|
4986
5155
|
function styleForState(map, state, styles) {
|
|
4987
5156
|
const key = map[state] ?? "muted";
|
|
4988
5157
|
return (text) => styles[key](text);
|
|
4989
5158
|
}
|
|
5159
|
+
function progressStyleForState(state, styles) {
|
|
5160
|
+
return styleForState(PROGRESS_STATE_STYLE, state, styles);
|
|
5161
|
+
}
|
|
4990
5162
|
function phaseStyleForState(state, styles) {
|
|
4991
5163
|
return styleForState(PHASE_STATE_STYLE, state, styles);
|
|
4992
5164
|
}
|
|
@@ -5101,16 +5273,96 @@ function visibleStandaloneAgentAttemptActions(actions, context) {
|
|
|
5101
5273
|
}
|
|
5102
5274
|
});
|
|
5103
5275
|
}
|
|
5104
|
-
function formatAgentAccounting(
|
|
5105
|
-
const total =
|
|
5106
|
-
return [`Tokens: \u2211${formatAccountingValue(total)} \u2191${formatAccountingValue(
|
|
5276
|
+
function formatAgentAccounting(accounting2) {
|
|
5277
|
+
const total = accounting2.input + accounting2.output + accounting2.cacheRead + accounting2.cacheWrite;
|
|
5278
|
+
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"}`];
|
|
5279
|
+
}
|
|
5280
|
+
function wrapNavigatorLine(text, width) {
|
|
5281
|
+
return truncateToVisualLines(text, Number.MAX_SAFE_INTEGER, Math.max(1, width), 0).visualLines.map((line) => line.trimEnd());
|
|
5282
|
+
}
|
|
5283
|
+
function formatNavigatorColumns(sidebar, detail, width, layout = {}) {
|
|
5284
|
+
const safeWidth = Math.max(1, width);
|
|
5285
|
+
if (safeWidth >= 80) {
|
|
5286
|
+
const sidebarWidth = Math.min(42, Math.max(24, Math.floor((safeWidth - 3) * 0.38)));
|
|
5287
|
+
const left2 = sidebar.flatMap((line) => wrapNavigatorLine(line, sidebarWidth));
|
|
5288
|
+
const right = detail.flatMap((line) => wrapNavigatorLine(line, safeWidth - sidebarWidth - 3));
|
|
5289
|
+
return Array.from({ length: Math.max(left2.length, right.length) }, (_, index) => {
|
|
5290
|
+
const cell = left2[index] ?? "";
|
|
5291
|
+
return `${cell}${" ".repeat(Math.max(0, sidebarWidth - visibleWidth2(cell)))} | ${right[index] ?? ""}`;
|
|
5292
|
+
});
|
|
5293
|
+
}
|
|
5294
|
+
if (layout.detailsOnly) return detail.flatMap((line) => wrapNavigatorLine(line, safeWidth));
|
|
5295
|
+
const left = sidebar.flatMap((line) => wrapNavigatorLine(line, safeWidth));
|
|
5296
|
+
return layout.treeOnly ? left : [...left, ...detail.flatMap((line) => wrapNavigatorLine(line, safeWidth))];
|
|
5107
5297
|
}
|
|
5108
5298
|
|
|
5109
5299
|
// src/retention.ts
|
|
5110
5300
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
5111
5301
|
|
|
5302
|
+
// src/host-navigator.ts
|
|
5303
|
+
function isKeybindingGetKeys(value) {
|
|
5304
|
+
return typeof value === "function";
|
|
5305
|
+
}
|
|
5306
|
+
function keybindingKeys(keybindings, name) {
|
|
5307
|
+
if (!object(keybindings)) return void 0;
|
|
5308
|
+
const getKeys = keybindings.getKeys;
|
|
5309
|
+
return isKeybindingGetKeys(getKeys) ? getKeys.call(keybindings, name) : void 0;
|
|
5310
|
+
}
|
|
5311
|
+
var WORKFLOW_VIM_KEYS = { "tui.select.up": "k", "tui.select.down": "j", "tui.editor.cursorLeft": "h", "tui.editor.cursorRight": "l" };
|
|
5312
|
+
function workflowKeyMatches(keybindings, data, binding) {
|
|
5313
|
+
return keybindings.matches(data, binding) || WORKFLOW_VIM_KEYS[binding] === data;
|
|
5314
|
+
}
|
|
5315
|
+
var WORKFLOW_KEY_LABELS = { up: "\u2191", down: "\u2193", left: "\u2190", right: "\u2192", pageUp: "pgup", pageDown: "pgdn" };
|
|
5316
|
+
function workflowKeyLabel(keybindings, binding, fallback, labels = WORKFLOW_KEY_LABELS) {
|
|
5317
|
+
const keys = keybindingKeys(keybindings, binding);
|
|
5318
|
+
const configured = keys?.length ? keys.map((key) => labels[key] ?? key) : [fallback];
|
|
5319
|
+
const vim = WORKFLOW_VIM_KEYS[binding];
|
|
5320
|
+
return [...new Set(vim ? [...configured, vim] : configured)].join("/");
|
|
5321
|
+
}
|
|
5322
|
+
function createInlineConfirm(requestRender) {
|
|
5323
|
+
let pending;
|
|
5324
|
+
const answer = (value) => {
|
|
5325
|
+
const current = pending;
|
|
5326
|
+
pending = void 0;
|
|
5327
|
+
current?.resolve(value);
|
|
5328
|
+
requestRender();
|
|
5329
|
+
};
|
|
5330
|
+
return {
|
|
5331
|
+
ask(title, message) {
|
|
5332
|
+
answer(false);
|
|
5333
|
+
return new Promise((resolve6) => {
|
|
5334
|
+
pending = { title, message, yes: true, resolve: resolve6 };
|
|
5335
|
+
requestRender();
|
|
5336
|
+
});
|
|
5337
|
+
},
|
|
5338
|
+
active() {
|
|
5339
|
+
return pending !== void 0;
|
|
5340
|
+
},
|
|
5341
|
+
rows(width, styles) {
|
|
5342
|
+
if (!pending) return [];
|
|
5343
|
+
const yes = pending.yes;
|
|
5344
|
+
const message = truncateToVisualLines2(pending.message, Number.MAX_SAFE_INTEGER, Math.max(1, width), 0).visualLines.map((line) => line.trimEnd());
|
|
5345
|
+
return [styles.bold(pending.title), ...message, ...["Yes", "No"].map((option) => option === "Yes" === yes ? `\u2192 ${styles.accent(option)}` : ` ${option}`)];
|
|
5346
|
+
},
|
|
5347
|
+
hint(keybindings) {
|
|
5348
|
+
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`;
|
|
5349
|
+
},
|
|
5350
|
+
handleInput(keybindings, data) {
|
|
5351
|
+
if (!pending) return;
|
|
5352
|
+
if (workflowKeyMatches(keybindings, data, "tui.select.up") || workflowKeyMatches(keybindings, data, "tui.select.down")) {
|
|
5353
|
+
pending.yes = !pending.yes;
|
|
5354
|
+
requestRender();
|
|
5355
|
+
} else if (workflowKeyMatches(keybindings, data, "tui.select.confirm")) answer(pending.yes);
|
|
5356
|
+
else if (workflowKeyMatches(keybindings, data, "tui.select.cancel")) answer(false);
|
|
5357
|
+
},
|
|
5358
|
+
cancel() {
|
|
5359
|
+
answer(false);
|
|
5360
|
+
}
|
|
5361
|
+
};
|
|
5362
|
+
}
|
|
5363
|
+
|
|
5112
5364
|
// src/trajectory.ts
|
|
5113
|
-
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager as DefaultPackageManager2, getAgentDir as
|
|
5365
|
+
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
5366
|
|
|
5115
5367
|
// subagents/src/contracts.ts
|
|
5116
5368
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
@@ -5214,14 +5466,14 @@ function accountingValue(value) {
|
|
|
5214
5466
|
return { input, output, cacheRead, cacheWrite, cost };
|
|
5215
5467
|
}
|
|
5216
5468
|
function legacyAccountingValue(record2) {
|
|
5217
|
-
const
|
|
5218
|
-
if (record2.accounting !== void 0 && !
|
|
5469
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5470
|
+
if (record2.accounting !== void 0 && !accounting2) return void 0;
|
|
5219
5471
|
const usage = record2.usage;
|
|
5220
|
-
if (usage === void 0) return
|
|
5472
|
+
if (usage === void 0) return accounting2;
|
|
5221
5473
|
const usageRecord = objectValue(usage);
|
|
5222
5474
|
const tokens = objectValue(usageRecord?.tokens);
|
|
5223
5475
|
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
|
|
5476
|
+
return accounting2 ?? { input: tokens.input, output: tokens.output, cacheRead: tokens.cacheRead, cacheWrite: tokens.cacheWrite, cost: usageRecord.cost };
|
|
5225
5477
|
}
|
|
5226
5478
|
function activityValue(value) {
|
|
5227
5479
|
const record2 = objectValue(value);
|
|
@@ -5255,9 +5507,9 @@ function worktreeValue(value) {
|
|
|
5255
5507
|
function progressValue(value) {
|
|
5256
5508
|
const record2 = objectValue(value);
|
|
5257
5509
|
if (!record2) return void 0;
|
|
5258
|
-
const
|
|
5510
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5259
5511
|
const toolCalls = toolCallsValue(record2.toolCalls);
|
|
5260
|
-
if (!
|
|
5512
|
+
if (!accounting2 || !toolCalls) return void 0;
|
|
5261
5513
|
const activity2 = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5262
5514
|
if (record2.activity !== void 0 && activity2 === void 0) return void 0;
|
|
5263
5515
|
const rawState = record2.state;
|
|
@@ -5273,16 +5525,16 @@ function progressValue(value) {
|
|
|
5273
5525
|
}
|
|
5274
5526
|
const lastEventAt = record2.lastEventAt;
|
|
5275
5527
|
if (lastEventAt !== void 0 && !nonnegativeInteger(lastEventAt)) return void 0;
|
|
5276
|
-
return { accounting:
|
|
5528
|
+
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...activity2 === void 0 ? {} : { activity: activity2 }, ...lastEventAt === void 0 ? {} : { lastEventAt } };
|
|
5277
5529
|
}
|
|
5278
5530
|
function legacyProgressValue(record2) {
|
|
5279
|
-
const
|
|
5531
|
+
const accounting2 = legacyAccountingValue(record2);
|
|
5280
5532
|
const toolCalls = record2.toolCalls === void 0 ? [] : toolCallsValue(record2.toolCalls);
|
|
5281
|
-
if (!
|
|
5533
|
+
if (!accounting2 || !toolCalls) return void 0;
|
|
5282
5534
|
const activity2 = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5283
5535
|
const lastEventAt = record2.lastEventAt;
|
|
5284
5536
|
if (record2.activity !== void 0 && activity2 === void 0 || lastEventAt !== void 0 && !nonnegativeInteger(lastEventAt)) return void 0;
|
|
5285
|
-
return { accounting:
|
|
5537
|
+
return { accounting: accounting2, toolCalls, ...activity2 === void 0 ? {} : { activity: activity2 }, ...lastEventAt === void 0 ? {} : { lastEventAt } };
|
|
5286
5538
|
}
|
|
5287
5539
|
function attemptValue(value) {
|
|
5288
5540
|
const record2 = objectValue(value);
|
|
@@ -5291,16 +5543,16 @@ function attemptValue(value) {
|
|
|
5291
5543
|
const hookNames = stringArrayValue(setup?.hookNames);
|
|
5292
5544
|
const tools = stringArrayValue(setup?.tools);
|
|
5293
5545
|
const resources = setup?.resourceSelectors === void 0 ? void 0 : resourceSummaryValue(setup.resourceSelectors);
|
|
5294
|
-
const
|
|
5546
|
+
const accounting2 = accountingValue(record2?.accounting);
|
|
5295
5547
|
const session = record2?.session === void 0 ? void 0 : sessionReferenceValue(record2.session);
|
|
5296
5548
|
const error = record2?.error === void 0 ? void 0 : subagentErrorValue(record2.error);
|
|
5297
5549
|
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 || !
|
|
5550
|
+
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
5551
|
return {
|
|
5300
5552
|
attempt: record2.attempt,
|
|
5301
5553
|
transport: record2.transport,
|
|
5302
5554
|
setup: { hookNames, model: { provider: model.provider, model: model.model, ...thinking === void 0 ? {} : { thinking } }, tools, cwd: setup.cwd, ...resources === void 0 ? {} : { resourceSelectors: resources } },
|
|
5303
|
-
accounting:
|
|
5555
|
+
accounting: accounting2,
|
|
5304
5556
|
...session === void 0 ? {} : { session },
|
|
5305
5557
|
...error === void 0 ? {} : { error }
|
|
5306
5558
|
};
|
|
@@ -5370,11 +5622,11 @@ var WORKFLOW_RETRY_PARAMETERS = Type3.Object({ runId: Type3.String({ description
|
|
|
5370
5622
|
// subagents/src/manager.ts
|
|
5371
5623
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
5372
5624
|
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
|
|
5625
|
+
import { join as join9 } from "node:path";
|
|
5626
|
+
import { getAgentDir as getAgentDir6 } from "@earendil-works/pi-coding-agent";
|
|
5375
5627
|
|
|
5376
5628
|
// subagents/src/worktree.ts
|
|
5377
|
-
import { join as
|
|
5629
|
+
import { join as join8 } from "node:path";
|
|
5378
5630
|
function syntheticRun(context) {
|
|
5379
5631
|
return {
|
|
5380
5632
|
id: context.runId,
|
|
@@ -5436,7 +5688,7 @@ function createRunStoreWorktreeAdapter(home) {
|
|
|
5436
5688
|
};
|
|
5437
5689
|
}
|
|
5438
5690
|
function defaultWorktreeHome(storageDirectory3) {
|
|
5439
|
-
return
|
|
5691
|
+
return join8(storageDirectory3, ".worktrees");
|
|
5440
5692
|
}
|
|
5441
5693
|
|
|
5442
5694
|
// subagents/src/manager.ts
|
|
@@ -5489,7 +5741,7 @@ function validateStandaloneExtensionSources(context, external, registry, agentDi
|
|
|
5489
5741
|
function executionRoot(context, dependencies, signal, runId, worktree, external) {
|
|
5490
5742
|
const extensionContext = context.extensionContext;
|
|
5491
5743
|
const model = rootModel(context);
|
|
5492
|
-
const agentDir = dependencies.agentDir ??
|
|
5744
|
+
const agentDir = dependencies.agentDir ?? getAgentDir6();
|
|
5493
5745
|
const currentTrustedProject = extensionContext.isProjectTrusted();
|
|
5494
5746
|
if (external.projectTrusted && !currentTrustedProject) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
|
|
5495
5747
|
const trustedProject = currentTrustedProject;
|
|
@@ -5569,25 +5821,25 @@ function executionOptions(request, onAttempt, onProgress) {
|
|
|
5569
5821
|
};
|
|
5570
5822
|
}
|
|
5571
5823
|
function storageDirectory(dependencies) {
|
|
5572
|
-
return dependencies.storageDir ??
|
|
5824
|
+
return dependencies.storageDir ?? join9(dependencies.agentDir ?? getAgentDir6(), STORAGE_DIRECTORY);
|
|
5573
5825
|
}
|
|
5574
5826
|
function runDirectory(root, id) {
|
|
5575
|
-
return
|
|
5827
|
+
return join9(root, id);
|
|
5576
5828
|
}
|
|
5577
5829
|
function requestPath(directory) {
|
|
5578
|
-
return
|
|
5830
|
+
return join9(directory, "request.json");
|
|
5579
5831
|
}
|
|
5580
5832
|
function configurationPath(directory) {
|
|
5581
|
-
return
|
|
5833
|
+
return join9(directory, CONFIGURATION_FILE);
|
|
5582
5834
|
}
|
|
5583
5835
|
function statusPath(directory) {
|
|
5584
|
-
return
|
|
5836
|
+
return join9(directory, "status.json");
|
|
5585
5837
|
}
|
|
5586
5838
|
function resultPath(directory) {
|
|
5587
|
-
return
|
|
5839
|
+
return join9(directory, "result.json");
|
|
5588
5840
|
}
|
|
5589
5841
|
function failurePath(directory) {
|
|
5590
|
-
return
|
|
5842
|
+
return join9(directory, "failure.json");
|
|
5591
5843
|
}
|
|
5592
5844
|
async function secureDirectory(directory) {
|
|
5593
5845
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
@@ -5670,7 +5922,7 @@ async function releaseOwnerMarker(path, token) {
|
|
|
5670
5922
|
}
|
|
5671
5923
|
async function acquireStorageOwner(root, owner, liveness) {
|
|
5672
5924
|
await secureDirectory(root);
|
|
5673
|
-
const path =
|
|
5925
|
+
const path = join9(root, OWNER_FILE);
|
|
5674
5926
|
for (let attempt = 0; attempt < MAX_STORAGE_OWNER_ATTEMPTS; attempt += 1) {
|
|
5675
5927
|
const marker = { ...owner, acquiredAt: Date.now() };
|
|
5676
5928
|
try {
|
|
@@ -5757,7 +6009,7 @@ function decodePersistedStandaloneExternalConfiguration(value) {
|
|
|
5757
6009
|
function standaloneExternalConfiguration(context, dependencies) {
|
|
5758
6010
|
const cwd = context.extensionContext.cwd;
|
|
5759
6011
|
const trustedProject = context.extensionContext.isProjectTrusted();
|
|
5760
|
-
const agentDir = dependencies.agentDir ??
|
|
6012
|
+
const agentDir = dependencies.agentDir ?? getAgentDir6();
|
|
5761
6013
|
const resolution = resolveWorkflowSettings(cwd, trustedProject, workflowSettingsPath(agentDir));
|
|
5762
6014
|
const globalExtensionSettings = resolution.global.extensionSettings;
|
|
5763
6015
|
const projectExtensionSettings = resolution.project.extensionSettings;
|
|
@@ -5923,15 +6175,15 @@ function attemptSummaryValue(value) {
|
|
|
5923
6175
|
const transport = record2.transport;
|
|
5924
6176
|
const session = record2.session === void 0 ? void 0 : sessionReferenceValue2(record2.session);
|
|
5925
6177
|
const setup = setupSummaryValue(record2.setup);
|
|
5926
|
-
const
|
|
6178
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5927
6179
|
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 || !
|
|
6180
|
+
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
6181
|
return {
|
|
5930
6182
|
attempt,
|
|
5931
6183
|
transport: boundedAttemptText(transport),
|
|
5932
6184
|
...session === void 0 ? {} : { session },
|
|
5933
6185
|
...error === void 0 ? {} : { error: { code: boundedAttemptText(error.code), message: boundedAttemptText(error.message) } },
|
|
5934
|
-
accounting: { ...
|
|
6186
|
+
accounting: { ...accounting2 },
|
|
5935
6187
|
setup: boundedAttemptSetup(setup)
|
|
5936
6188
|
};
|
|
5937
6189
|
}
|
|
@@ -5982,23 +6234,23 @@ function sessionStateValue(value) {
|
|
|
5982
6234
|
function progressValue2(value) {
|
|
5983
6235
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
5984
6236
|
const record2 = value;
|
|
5985
|
-
const
|
|
6237
|
+
const accounting2 = accountingValue(record2.accounting);
|
|
5986
6238
|
const toolCalls = toolCallsValue2(record2.toolCalls);
|
|
5987
6239
|
const state = record2.state === void 0 ? void 0 : sessionStateValue(record2.state);
|
|
5988
|
-
if (!
|
|
6240
|
+
if (!accounting2 || !toolCalls || record2.state !== void 0 && state === void 0) return void 0;
|
|
5989
6241
|
const activity2 = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5990
6242
|
if (record2.activity !== void 0 && activity2 === void 0) return void 0;
|
|
5991
6243
|
if (record2.lastEventAt !== void 0 && (!Number.isSafeInteger(record2.lastEventAt) || record2.lastEventAt < 0)) return void 0;
|
|
5992
|
-
return { accounting:
|
|
6244
|
+
return { accounting: accounting2, toolCalls, ...state === void 0 ? {} : { state }, ...activity2 === void 0 ? {} : { activity: activity2 }, ...record2.lastEventAt === void 0 ? {} : { lastEventAt: record2.lastEventAt } };
|
|
5993
6245
|
}
|
|
5994
6246
|
function legacyProgressValue2(record2) {
|
|
5995
|
-
const
|
|
6247
|
+
const accounting2 = legacyAccountingValue(record2);
|
|
5996
6248
|
const toolCalls = record2.toolCalls === void 0 ? [] : toolCallsValue2(record2.toolCalls);
|
|
5997
|
-
if (!
|
|
6249
|
+
if (!accounting2 || !toolCalls) return void 0;
|
|
5998
6250
|
const activity2 = record2.activity === void 0 ? void 0 : activityValue(record2.activity);
|
|
5999
6251
|
if (record2.activity !== void 0 && activity2 === void 0) return void 0;
|
|
6000
6252
|
if (record2.lastEventAt !== void 0 && (!Number.isSafeInteger(record2.lastEventAt) || record2.lastEventAt < 0)) return void 0;
|
|
6001
|
-
return { accounting:
|
|
6253
|
+
return { accounting: accounting2, toolCalls, ...activity2 === void 0 ? {} : { activity: activity2 }, ...record2.lastEventAt === void 0 ? {} : { lastEventAt: record2.lastEventAt } };
|
|
6002
6254
|
}
|
|
6003
6255
|
function worktreeContextValue(value) {
|
|
6004
6256
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -6310,7 +6562,7 @@ var PersistentSubagentManager = class {
|
|
|
6310
6562
|
if (!sessionId.trim()) throw new WorkflowError("INTERNAL_ERROR", "Current Pi session identity is unavailable");
|
|
6311
6563
|
const owner = this.runOwner;
|
|
6312
6564
|
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 ??
|
|
6565
|
+
const settings = resolveWorkflowSettings(context.extensionContext.cwd, context.extensionContext.isProjectTrusted(), workflowSettingsPath(this.dependencies.agentDir ?? getAgentDir6()));
|
|
6314
6566
|
const concurrency = settings.effective.concurrency;
|
|
6315
6567
|
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
6568
|
this.activeRunCount += 1;
|
|
@@ -6326,8 +6578,8 @@ var PersistentSubagentManager = class {
|
|
|
6326
6578
|
}
|
|
6327
6579
|
const current = {};
|
|
6328
6580
|
let resolveTerminal;
|
|
6329
|
-
const terminal = new Promise((
|
|
6330
|
-
resolveTerminal =
|
|
6581
|
+
const terminal = new Promise((resolve6) => {
|
|
6582
|
+
resolveTerminal = resolve6;
|
|
6331
6583
|
});
|
|
6332
6584
|
const executorOwnership = { default: true };
|
|
6333
6585
|
const execution = Promise.resolve().then(async () => {
|
|
@@ -6441,8 +6693,9 @@ var PersistentSubagentManager = class {
|
|
|
6441
6693
|
if (request.id !== void 0) {
|
|
6442
6694
|
const id = checkedId({ id: request.id });
|
|
6443
6695
|
const active = this.activeRuns.get(id);
|
|
6444
|
-
const
|
|
6445
|
-
const
|
|
6696
|
+
const includeActivity = context.includeActivity === true;
|
|
6697
|
+
const status = active ? includeActivity ? liveStatus(active) : persistedStatus(active) : this.terminalSummaries.get(id) ?? await loadPersistedStatus(storageDirectory(this.dependencies), id);
|
|
6698
|
+
const inspection = publicStatus(status, context.includeAttemptMetadata === true, includeActivity);
|
|
6446
6699
|
if (status.state === "completed") return { ...inspection, value: await loadPersistedResult(storageDirectory(this.dependencies), id) };
|
|
6447
6700
|
if (status.state === "failed") {
|
|
6448
6701
|
const failure = status.error ?? await loadPersistedFailure(storageDirectory(this.dependencies), id);
|
|
@@ -6493,6 +6746,18 @@ var PersistentSubagentManager = class {
|
|
|
6493
6746
|
await this.stopRun(run, false);
|
|
6494
6747
|
return publicStatus(persistedStatus(run));
|
|
6495
6748
|
}
|
|
6749
|
+
/** Removes a settled record for the /subagents navigator, as /workflow deletes a terminal run; not a model tool. */
|
|
6750
|
+
async delete(request) {
|
|
6751
|
+
const id = checkedId(request);
|
|
6752
|
+
await this.ensureInitialized();
|
|
6753
|
+
const root = storageDirectory(this.dependencies);
|
|
6754
|
+
const status = this.activeRuns.has(id) ? void 0 : this.terminalSummaries.get(id) ?? await loadPersistedStatus(root, id, false);
|
|
6755
|
+
if (status === void 0 || status.state === "running") throw new WorkflowError("RUN_OWNED", `Stop subagent ${id} before deleting it`);
|
|
6756
|
+
if (status.worktree !== void 0) throw new WorkflowError("WORKTREE_FAILED", `Subagent ${id} still has a worktree to clean up`);
|
|
6757
|
+
await rm5(runDirectory(root, id), { recursive: true, force: true });
|
|
6758
|
+
this.terminalSummaries.delete(id);
|
|
6759
|
+
return { id, deleted: true };
|
|
6760
|
+
}
|
|
6496
6761
|
async dispose() {
|
|
6497
6762
|
if (this.disposePromise) return this.disposePromise;
|
|
6498
6763
|
this.disposePromise = (async () => {
|
|
@@ -6589,8 +6854,8 @@ var PersistentSubagentManager = class {
|
|
|
6589
6854
|
if (!run || !this.canSteer(run)) return;
|
|
6590
6855
|
if (run.activeAttempt !== void 0 && run.finalizedAttemptAccounting.has(run.activeAttempt)) return;
|
|
6591
6856
|
const snapshot = portableProgress(progress, true);
|
|
6592
|
-
const
|
|
6593
|
-
run.progress = { ...snapshot, accounting:
|
|
6857
|
+
const accounting2 = addAccounting(sumAccounting(run.finalizedAttemptAccounting.values()), progress.accounting);
|
|
6858
|
+
run.progress = { ...snapshot, accounting: accounting2 };
|
|
6594
6859
|
emitUpdate(run);
|
|
6595
6860
|
if (!progress.persist) return;
|
|
6596
6861
|
await enqueueWrite(run, () => atomicJson(statusPath(run.directory), persistedStatus(run)));
|
|
@@ -6605,10 +6870,10 @@ var PersistentSubagentManager = class {
|
|
|
6605
6870
|
run.attemptDetails = [...run.attemptDetails ?? [], summary].slice(-SUBAGENT_ATTEMPT_DETAILS_LIMIT);
|
|
6606
6871
|
}
|
|
6607
6872
|
if (!finalized) return;
|
|
6608
|
-
const
|
|
6609
|
-
if (attempt === void 0 ||
|
|
6873
|
+
const accounting2 = summary?.accounting ?? accountingValue(record2?.accounting);
|
|
6874
|
+
if (attempt === void 0 || accounting2 === void 0) return;
|
|
6610
6875
|
if (run.finalizedAttemptAccounting.size >= MAX_PERSISTED_ATTEMPT_ARRAY_ITEMS && !run.finalizedAttemptAccounting.has(attempt)) return;
|
|
6611
|
-
run.finalizedAttemptAccounting.set(attempt,
|
|
6876
|
+
run.finalizedAttemptAccounting.set(attempt, accounting2);
|
|
6612
6877
|
const cumulative = sumAccounting(run.finalizedAttemptAccounting.values());
|
|
6613
6878
|
run.progress = run.progress === void 0 ? { accounting: cumulative, toolCalls: [] } : { ...run.progress, accounting: cumulative };
|
|
6614
6879
|
}
|
|
@@ -6663,8 +6928,8 @@ var PersistentSubagentManager = class {
|
|
|
6663
6928
|
run.disposed = true;
|
|
6664
6929
|
this.clearSteering(run);
|
|
6665
6930
|
await this.cleanupSessions(run, true);
|
|
6666
|
-
|
|
6667
|
-
|
|
6931
|
+
await this.cleanupWorktree(run);
|
|
6932
|
+
await enqueueWrite(run, () => atomicJson(statusPath(run.directory), persistedStatus(run)));
|
|
6668
6933
|
this.resolveTerminal(run);
|
|
6669
6934
|
this.removeLiveRun(run);
|
|
6670
6935
|
}
|
|
@@ -6867,23 +7132,23 @@ function createSubagentManager(dependencies = {}) {
|
|
|
6867
7132
|
return {
|
|
6868
7133
|
run: (request, context) => manager.run(request, context),
|
|
6869
7134
|
inspect: (request, context) => manager.inspect(request, context),
|
|
6870
|
-
getAttemptActionData: (id) => manager.getAttemptActionData
|
|
6871
|
-
steer: (request
|
|
6872
|
-
stop: (request
|
|
7135
|
+
getAttemptActionData: (id) => manager.getAttemptActionData(id),
|
|
7136
|
+
steer: (request) => manager.steer(request),
|
|
7137
|
+
stop: (request) => manager.stop(request),
|
|
6873
7138
|
retry: (request, context) => manager.retry(request, context),
|
|
7139
|
+
delete: (request) => manager.delete(request),
|
|
6874
7140
|
dispose: async () => {
|
|
6875
|
-
await manager.dispose
|
|
7141
|
+
await manager.dispose();
|
|
6876
7142
|
}
|
|
6877
7143
|
};
|
|
6878
7144
|
}
|
|
6879
7145
|
|
|
6880
7146
|
// subagents/src/navigator.ts
|
|
6881
7147
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
6882
|
-
import { join as
|
|
6883
|
-
import { copyToClipboard as copyToClipboard2, getAgentDir as
|
|
7148
|
+
import { join as join10 } from "node:path";
|
|
7149
|
+
import { copyToClipboard as copyToClipboard2, getAgentDir as getAgentDir7, SettingsManager as SettingsManager4 } from "@earendil-works/pi-coding-agent";
|
|
6884
7150
|
import { Editor, truncateToWidth as truncateToWidth3 } from "@earendil-works/pi-tui";
|
|
6885
7151
|
var MAX_DETAIL_TEXT = 4e3;
|
|
6886
|
-
var MAX_DETAIL_TOOL_CALLS = 32;
|
|
6887
7152
|
function objectValue2(value) {
|
|
6888
7153
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
6889
7154
|
}
|
|
@@ -6899,12 +7164,12 @@ function inspectionValue(value) {
|
|
|
6899
7164
|
return record2 && status ? { status, record: record2 } : void 0;
|
|
6900
7165
|
}
|
|
6901
7166
|
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 };
|
|
7167
|
+
return { toolCallId: "subagents-command", signal: void 0, onUpdate: void 0, ...waitForForeground ? {} : { waitForForeground: false }, ...includeAttemptMetadata ? { includeAttemptMetadata: true, includeActivity: true } : {}, extensionContext: context };
|
|
6903
7168
|
}
|
|
6904
7169
|
async function loadRequest(storageDirectory3, id) {
|
|
6905
7170
|
if (!safeRunId(id)) return {};
|
|
6906
7171
|
try {
|
|
6907
|
-
const value = JSON.parse(await readFile5(
|
|
7172
|
+
const value = JSON.parse(await readFile5(join10(storageDirectory3, id, "request.json"), "utf8"));
|
|
6908
7173
|
return { request: normalizeSubagentRunRequest(value) };
|
|
6909
7174
|
} catch (error) {
|
|
6910
7175
|
if (isFileNotFound(error)) return {};
|
|
@@ -6937,9 +7202,6 @@ async function inspectEntry(manager, storageDirectory3, entry, context) {
|
|
|
6937
7202
|
entry: { status: inspected.status, ...request.request === void 0 ? {} : { request: request.request }, ...request.error === void 0 ? {} : { requestError: request.error } }
|
|
6938
7203
|
};
|
|
6939
7204
|
}
|
|
6940
|
-
function requestLabel(request) {
|
|
6941
|
-
return request?.label?.trim() || "none";
|
|
6942
|
-
}
|
|
6943
7205
|
function requestRole(request) {
|
|
6944
7206
|
const role = request?.role;
|
|
6945
7207
|
if (typeof role === "string" && role.trim()) return role.trim();
|
|
@@ -6949,13 +7211,31 @@ function requestRole(request) {
|
|
|
6949
7211
|
function shortId(id) {
|
|
6950
7212
|
return id.length > 12 ? id.slice(0, 8) : id;
|
|
6951
7213
|
}
|
|
6952
|
-
function
|
|
6953
|
-
|
|
7214
|
+
function entryName(entry) {
|
|
7215
|
+
const role = requestRole(entry.request);
|
|
7216
|
+
return boundedText(entry.request?.label?.trim() || (role === "none" ? shortId(entry.status.id) : role), 256);
|
|
7217
|
+
}
|
|
7218
|
+
function listNames(entries) {
|
|
7219
|
+
const names = entries.map(entryName);
|
|
7220
|
+
return entries.map(({ status }, index) => {
|
|
7221
|
+
const name = names[index] ?? "";
|
|
7222
|
+
return names.indexOf(name) === names.lastIndexOf(name) ? name : `${name} ${shortId(status.id)}`;
|
|
7223
|
+
});
|
|
7224
|
+
}
|
|
7225
|
+
function pickerLabels(entries) {
|
|
7226
|
+
const names = listNames(entries);
|
|
7227
|
+
return entries.map(({ status }, index) => {
|
|
7228
|
+
const name = names[index] ?? "";
|
|
7229
|
+
const model = status.progress?.state?.model;
|
|
7230
|
+
const cost = formatCost(status.progress?.accounting.cost);
|
|
7231
|
+
const runtime2 = status.startedAt === void 0 ? "" : ` runtime=${formatWorkflowRuntime((status.finishedAt ?? Date.now()) - status.startedAt)}`;
|
|
7232
|
+
return `${runStateGlyph(status.state, "\u2826")} ${name} ${status.state}${model ? ` ${boundedText(model.model, 256)}${model.thinking ? `:${model.thinking}` : ""}` : ""}${cost ? ` ${cost}` : ""}${runtime2}`;
|
|
7233
|
+
});
|
|
6954
7234
|
}
|
|
6955
7235
|
function boundedText(value, limit = MAX_DETAIL_TEXT) {
|
|
6956
7236
|
const text = typeof value === "string" ? value : (() => {
|
|
6957
7237
|
try {
|
|
6958
|
-
const serialized = JSON.stringify(value);
|
|
7238
|
+
const serialized = JSON.stringify(value, null, 2);
|
|
6959
7239
|
return typeof serialized === "string" ? serialized : String(value);
|
|
6960
7240
|
} catch {
|
|
6961
7241
|
return String(value);
|
|
@@ -6963,13 +7243,6 @@ function boundedText(value, limit = MAX_DETAIL_TEXT) {
|
|
|
6963
7243
|
})();
|
|
6964
7244
|
return text.length > limit ? `${text.slice(0, limit)}\u2026` : text;
|
|
6965
7245
|
}
|
|
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
7246
|
function latestAttempt(status) {
|
|
6974
7247
|
return [...status.attemptDetails ?? []].sort((left, right) => right.attempt - left.attempt)[0];
|
|
6975
7248
|
}
|
|
@@ -6985,7 +7258,7 @@ function detailPresentation(inspection) {
|
|
|
6985
7258
|
const tools = state?.tools ?? attempt?.setup.tools;
|
|
6986
7259
|
const lastEventAt = status.progress?.lastEventAt;
|
|
6987
7260
|
const attempts = status.attempts ?? attempt?.attempt;
|
|
6988
|
-
const
|
|
7261
|
+
const accounting2 = usageAccounting(status);
|
|
6989
7262
|
const role = requestRole(request);
|
|
6990
7263
|
const error = status.error ?? attempt?.error;
|
|
6991
7264
|
return {
|
|
@@ -6998,33 +7271,34 @@ function detailPresentation(inspection) {
|
|
|
6998
7271
|
...attempts === void 0 ? {} : { attempts },
|
|
6999
7272
|
...status.startedAt === void 0 ? {} : { startedAt: status.startedAt },
|
|
7000
7273
|
...status.finishedAt === void 0 ? {} : { finishedAt: status.finishedAt },
|
|
7001
|
-
...
|
|
7274
|
+
...accounting2 === void 0 ? {} : { accounting: accounting2 },
|
|
7002
7275
|
...error === void 0 ? {} : { error: { code: boundedText(error.code, 256), message: boundedText(error.message) } }
|
|
7003
7276
|
};
|
|
7004
7277
|
}
|
|
7005
|
-
function
|
|
7006
|
-
const { entry
|
|
7007
|
-
const
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
...
|
|
7278
|
+
function detailRows(inspection, styles, menu) {
|
|
7279
|
+
const { entry } = inspection;
|
|
7280
|
+
const presentation = detailPresentation(inspection);
|
|
7281
|
+
return [
|
|
7282
|
+
styles.bold(`Selected subagent: ${entryName(entry)}`),
|
|
7283
|
+
`ID: ${boundedText(entry.status.id, 256)}`,
|
|
7284
|
+
...formatAgentDetail(presentation, styles, entry.status.finishedAt ?? Date.now(), { includeError: false }),
|
|
7285
|
+
...entry.requestError === void 0 ? [] : [styles.warning(`Request unavailable: ${boundedText(entry.requestError)}`)],
|
|
7286
|
+
...menu === "hint" ? [styles.muted("enter agent actions")] : [],
|
|
7287
|
+
...presentation.error === void 0 ? [] : [formatAgentError(presentation.error, styles)],
|
|
7288
|
+
...typeof menu === "object" ? [styles.bold("Agent actions"), ...menu.options.map((option, index) => index === menu.index ? `\u2192 ${styles.accent(option)}` : ` ${option}`)] : []
|
|
7013
7289
|
];
|
|
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;
|
|
7290
|
+
}
|
|
7291
|
+
function listRows(entries, selectedId, styles) {
|
|
7292
|
+
const names = listNames(entries);
|
|
7293
|
+
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"))}`)];
|
|
7294
|
+
}
|
|
7295
|
+
function headerRows(entries, styles) {
|
|
7296
|
+
const counts = ["running", "failed", "stopped", "completed"].flatMap((state) => {
|
|
7297
|
+
const count = entries.filter((entry) => entry.status.state === state).length;
|
|
7298
|
+
return count ? [`${String(count)} ${state}`] : [];
|
|
7299
|
+
});
|
|
7300
|
+
const cost = formatCost(entries.reduce((sum, entry) => sum + (entry.status.progress?.accounting.cost ?? 0), 0));
|
|
7301
|
+
return [styles.bold(styles.accent("Subagents")), [...counts, ...cost ? [cost] : []].join(" \xB7 ")];
|
|
7028
7302
|
}
|
|
7029
7303
|
function tuiRows(tui) {
|
|
7030
7304
|
return typeof tui.terminal?.rows === "number" && Number.isFinite(tui.terminal.rows) ? tui.terminal.rows : 24;
|
|
@@ -7034,6 +7308,7 @@ function unrefTimer(timer) {
|
|
|
7034
7308
|
if (typeof value !== "object" || value === null || !("unref" in value) || typeof value.unref !== "function") return;
|
|
7035
7309
|
Reflect.apply(value.unref, value, []);
|
|
7036
7310
|
}
|
|
7311
|
+
var DASHBOARD_FOOTER_ROWS = 2;
|
|
7037
7312
|
function standaloneActionContext(manager, inspection, context) {
|
|
7038
7313
|
const status = inspection.entry.status;
|
|
7039
7314
|
const request = inspection.entry.request;
|
|
@@ -7070,11 +7345,16 @@ function standaloneActionContext(manager, inspection, context) {
|
|
|
7070
7345
|
function liveSystemPrompt(manager, status) {
|
|
7071
7346
|
return manager.getAttemptActionData?.(status.id)?.prepared?.systemPrompt;
|
|
7072
7347
|
}
|
|
7073
|
-
|
|
7348
|
+
var BULK_DELETE = { "Delete all completed": "completed", "Delete all failed": "failed" };
|
|
7349
|
+
function bulkDeleteLabels(manager, entries) {
|
|
7350
|
+
if (manager.delete === void 0) return [];
|
|
7351
|
+
return Object.entries(BULK_DELETE).filter(([, state]) => entries.some((entry) => entry.status.state === state)).map(([label2]) => label2);
|
|
7352
|
+
}
|
|
7353
|
+
function actionOptions(manager, inspection, context, bulk = []) {
|
|
7074
7354
|
const actionContext = standaloneActionContext(manager, inspection, context);
|
|
7075
7355
|
const extensionLabels = actionContext === void 0 ? [] : visibleStandaloneAgentAttemptActions(loadingRegistry().agentAttemptActions(), actionContext).map(([, action]) => action.label);
|
|
7076
7356
|
const value = inspection.record.value;
|
|
7077
|
-
|
|
7357
|
+
const labels = agentActionLabels({
|
|
7078
7358
|
extensionLabels,
|
|
7079
7359
|
hasWorktree: inspection.entry.status.worktree !== void 0,
|
|
7080
7360
|
openPrompt: context.mode === "tui" && inspection.entry.request?.prompt !== void 0,
|
|
@@ -7082,13 +7362,33 @@ function actionOptions(manager, inspection, context) {
|
|
|
7082
7362
|
openResult: context.mode === "tui" && Object.prototype.hasOwnProperty.call(inspection.record, "value") && jsonValue(value),
|
|
7083
7363
|
standaloneState: inspection.entry.status.state
|
|
7084
7364
|
});
|
|
7365
|
+
const runActions = [...manager.delete !== void 0 && inspection.entry.status.state !== "running" ? ["Delete"] : [], ...bulk, ...context.mode === "tui" ? ["Copy run path"] : []];
|
|
7366
|
+
labels.splice(labels.indexOf("Copy agent ID"), 0, ...runActions);
|
|
7367
|
+
return labels;
|
|
7368
|
+
}
|
|
7369
|
+
async function deleteAll(manager, storageDirectory3, context, state, confirm) {
|
|
7370
|
+
if (!await confirm(`Delete ${state} runs?`, `Delete all ${state} subagent runs and their records? This cannot be undone.`)) return false;
|
|
7371
|
+
const skipped = [];
|
|
7372
|
+
let deleted = 0;
|
|
7373
|
+
for (const entry of await loadEntries(manager, storageDirectory3, context)) {
|
|
7374
|
+
if (entry.status.state !== state) continue;
|
|
7375
|
+
try {
|
|
7376
|
+
await manager.delete?.({ id: entry.status.id }, managerContext(context));
|
|
7377
|
+
deleted += 1;
|
|
7378
|
+
} catch (error) {
|
|
7379
|
+
skipped.push(`${entry.status.id} (${errorText(error)})`);
|
|
7380
|
+
}
|
|
7381
|
+
}
|
|
7382
|
+
if (skipped.length) context.ui.notify(`Skipped ${state} runs: ${skipped.join(", ")}.`, "warning");
|
|
7383
|
+
if (deleted) context.ui.notify(`Deleted ${String(deleted)} ${state} subagent run(s).`, "info");
|
|
7384
|
+
return true;
|
|
7085
7385
|
}
|
|
7086
7386
|
function retryResult(value) {
|
|
7087
7387
|
const record2 = objectValue2(value);
|
|
7088
7388
|
return record2 && typeof record2.id === "string" && record2.id.trim() && record2.state === "running" ? { id: record2.id, state: "running" } : void 0;
|
|
7089
7389
|
}
|
|
7090
7390
|
async function openNavigatorArtifact(context, tui, artifact, label2) {
|
|
7091
|
-
const command = SettingsManager4.create(context.cwd,
|
|
7391
|
+
const command = SettingsManager4.create(context.cwd, getAgentDir7(), { projectTrusted: context.isProjectTrusted() }).getExternalEditorCommand();
|
|
7092
7392
|
if (!command) {
|
|
7093
7393
|
context.ui.notify(`Cannot open ${label2}: no external editor is configured.`, "warning");
|
|
7094
7394
|
return;
|
|
@@ -7102,7 +7402,7 @@ async function steerSubagent(manager, storageDirectory3, entry, context, message
|
|
|
7102
7402
|
await manager.steer({ id: entry.status.id, message }, managerContext(context));
|
|
7103
7403
|
context.ui.notify(`Steered subagent ${entry.status.id}.`, "info");
|
|
7104
7404
|
}
|
|
7105
|
-
async function performAction(manager, storageDirectory3, entry, action, context, tui, clipboard) {
|
|
7405
|
+
async function performAction(manager, storageDirectory3, entry, action, context, tui, clipboard, confirm) {
|
|
7106
7406
|
const fresh = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7107
7407
|
const available = actionOptions(manager, fresh, context);
|
|
7108
7408
|
if (!available.includes(action)) throw new Error(`Action ${action} is no longer available`);
|
|
@@ -7116,40 +7416,52 @@ async function performAction(manager, storageDirectory3, entry, action, context,
|
|
|
7116
7416
|
if (action === "Copy agent ID") {
|
|
7117
7417
|
await clipboard(fresh.entry.status.id);
|
|
7118
7418
|
context.ui.notify("Copied agent ID.", "info");
|
|
7119
|
-
return "
|
|
7419
|
+
return "unchanged";
|
|
7420
|
+
}
|
|
7421
|
+
if (action === "Copy run path") {
|
|
7422
|
+
await clipboard(join10(storageDirectory3, fresh.entry.status.id));
|
|
7423
|
+
context.ui.notify("Copied run path.", "info");
|
|
7424
|
+
return "unchanged";
|
|
7425
|
+
}
|
|
7426
|
+
if (action === "Delete" && manager.delete !== void 0) {
|
|
7427
|
+
if (!await confirm("Delete subagent?", `Delete ${entryName(fresh.entry)} (${fresh.entry.status.id}) and its record? This cannot be undone.`)) return "unchanged";
|
|
7428
|
+
await manager.delete({ id: fresh.entry.status.id }, managerContext(context));
|
|
7429
|
+
context.ui.notify(`Deleted subagent ${fresh.entry.status.id}.`, "info");
|
|
7430
|
+
return "deleted";
|
|
7120
7431
|
}
|
|
7121
7432
|
if (action === "Copy branch" && fresh.entry.status.worktree) {
|
|
7122
7433
|
await clipboard(fresh.entry.status.worktree.branch);
|
|
7123
7434
|
context.ui.notify("Copied branch.", "info");
|
|
7124
|
-
return "
|
|
7435
|
+
return "unchanged";
|
|
7125
7436
|
}
|
|
7126
7437
|
if (action === "Copy worktree path" && fresh.entry.status.worktree) {
|
|
7127
7438
|
await clipboard(fresh.entry.status.worktree.path);
|
|
7128
7439
|
context.ui.notify("Copied worktree path.", "info");
|
|
7129
|
-
return "
|
|
7440
|
+
return "unchanged";
|
|
7130
7441
|
}
|
|
7131
7442
|
if (action === "Open prompt in editor" && tui && fresh.entry.request?.prompt !== void 0) {
|
|
7132
7443
|
await openNavigatorArtifact(context, tui, workflowPromptArtifact(fresh.entry.request.prompt), "agent prompt");
|
|
7133
|
-
return "
|
|
7444
|
+
return "unchanged";
|
|
7134
7445
|
}
|
|
7135
7446
|
if (action === "Open system prompt in editor" && tui) {
|
|
7136
7447
|
const systemPrompt = liveSystemPrompt(manager, fresh.entry.status);
|
|
7137
7448
|
if (systemPrompt !== void 0) {
|
|
7138
7449
|
await openNavigatorArtifact(context, tui, workflowPromptArtifact(systemPrompt), "agent system prompt");
|
|
7139
|
-
return "
|
|
7450
|
+
return "unchanged";
|
|
7140
7451
|
}
|
|
7141
7452
|
}
|
|
7142
7453
|
if (action === "Open result in editor" && tui && Object.prototype.hasOwnProperty.call(fresh.record, "value") && jsonValue(fresh.record.value)) {
|
|
7143
7454
|
await openNavigatorArtifact(context, tui, workflowResultArtifact(fresh.record.value), "agent result");
|
|
7144
|
-
return "
|
|
7455
|
+
return "unchanged";
|
|
7145
7456
|
}
|
|
7146
7457
|
if (action === "Steer") {
|
|
7147
7458
|
const message = await context.ui.input("Steer subagent", "Message for the running subagent");
|
|
7148
|
-
if (message === void 0) return "
|
|
7459
|
+
if (message === void 0) return "unchanged";
|
|
7149
7460
|
await steerSubagent(manager, storageDirectory3, entry, context, message);
|
|
7150
7461
|
return "stay";
|
|
7151
7462
|
}
|
|
7152
7463
|
if (action === "Stop") {
|
|
7464
|
+
if (!await confirm("Stop subagent?", `Stop subagent ${entryName(fresh.entry)} (${fresh.entry.status.id})? This cannot be undone.`)) return "unchanged";
|
|
7153
7465
|
const current = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7154
7466
|
if (current.entry.status.state !== "running") throw new Error(`Subagent ${entry.status.id} is no longer running`);
|
|
7155
7467
|
await manager.stop({ id: entry.status.id }, managerContext(context));
|
|
@@ -7162,31 +7474,36 @@ async function performAction(manager, storageDirectory3, entry, action, context,
|
|
|
7162
7474
|
const result = retryResult(await manager.retry({ id: entry.status.id }, managerContext(context, false)));
|
|
7163
7475
|
if (!result) throw new Error("Retry returned an invalid subagent result");
|
|
7164
7476
|
context.ui.notify(`Retried subagent ${entry.status.id} as ${result.id}.`, "info");
|
|
7165
|
-
return
|
|
7477
|
+
return { retryId: result.id };
|
|
7166
7478
|
}
|
|
7167
7479
|
return "stay";
|
|
7168
7480
|
}
|
|
7169
7481
|
async function showDetail(manager, storageDirectory3, entry, context, clipboard) {
|
|
7170
7482
|
let inspection = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
}
|
|
7483
|
+
for (; ; ) {
|
|
7484
|
+
const action = await context.ui.select(detailRows(inspection, PLAIN_WORKFLOW_PROGRESS_STYLES).join("\n"), actionOptions(manager, inspection, context));
|
|
7485
|
+
if (!action || action === "Back") return;
|
|
7486
|
+
try {
|
|
7487
|
+
const outcome = await performAction(manager, storageDirectory3, entry, action, context, void 0, clipboard, (title, message) => context.ui.confirm(title, message));
|
|
7488
|
+
if (outcome === "deleted" || typeof outcome === "object") return;
|
|
7489
|
+
inspection = await inspectEntry(manager, storageDirectory3, entry, context);
|
|
7490
|
+
} catch (error) {
|
|
7491
|
+
context.ui.notify(`Cannot ${action.toLowerCase()}: ${errorText(error)}`, "warning");
|
|
7181
7492
|
}
|
|
7182
7493
|
}
|
|
7494
|
+
}
|
|
7495
|
+
async function showDashboard(manager, storageDirectory3, initial, context, clipboard) {
|
|
7496
|
+
const first = initial[0];
|
|
7497
|
+
if (first === void 0) return;
|
|
7498
|
+
let initialInspection;
|
|
7499
|
+
try {
|
|
7500
|
+
initialInspection = await inspectEntry(manager, storageDirectory3, first, context);
|
|
7501
|
+
} catch (error) {
|
|
7502
|
+
context.ui.notify(`Cannot inspect subagent ${first.status.id}: ${errorText(error)}`, "warning");
|
|
7503
|
+
initialInspection = { entry: first, record: {} };
|
|
7504
|
+
}
|
|
7183
7505
|
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;
|
|
7506
|
+
const styles = themeWorkflowProgressStyles(theme);
|
|
7190
7507
|
const editorTheme = {
|
|
7191
7508
|
borderColor: (text) => theme.fg("accent", text),
|
|
7192
7509
|
selectList: {
|
|
@@ -7198,226 +7515,273 @@ async function showDetail(manager, storageDirectory3, entry, context, clipboard)
|
|
|
7198
7515
|
}
|
|
7199
7516
|
};
|
|
7200
7517
|
const steerEditor = new Editor(tui, editorTheme);
|
|
7201
|
-
let
|
|
7518
|
+
let entries = initial;
|
|
7519
|
+
let inspection = initialInspection;
|
|
7520
|
+
let offset = 0;
|
|
7521
|
+
let detailsMode = false;
|
|
7522
|
+
let actionMode = false;
|
|
7523
|
+
let actionIndex = 0;
|
|
7524
|
+
let steerMode = false;
|
|
7525
|
+
const inlineConfirm = createInlineConfirm(() => {
|
|
7526
|
+
if (!disposed) tui.requestRender();
|
|
7527
|
+
});
|
|
7528
|
+
const confirmInline = (title, message) => inlineConfirm.ask(title, message);
|
|
7529
|
+
let actionRunning = false;
|
|
7202
7530
|
let refreshing = false;
|
|
7203
|
-
let
|
|
7531
|
+
let disposed = false;
|
|
7532
|
+
let generation = 0;
|
|
7533
|
+
let renderedWidth = 80;
|
|
7534
|
+
let selectionNeedsScroll = true;
|
|
7535
|
+
let timer;
|
|
7536
|
+
const keyLabel = (binding, fallback) => workflowKeyLabel(keybindings, binding, fallback);
|
|
7537
|
+
const matches = (data, binding) => workflowKeyMatches(keybindings, data, binding);
|
|
7204
7538
|
const requestRender = () => {
|
|
7205
7539
|
if (!disposed) tui.requestRender();
|
|
7206
7540
|
};
|
|
7207
|
-
const
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7541
|
+
const options = () => actionOptions(manager, inspection, context, bulkDeleteLabels(manager, entries));
|
|
7542
|
+
const warn = (message) => {
|
|
7543
|
+
if (disposed) return;
|
|
7544
|
+
try {
|
|
7545
|
+
context.ui.notify(message, "warning");
|
|
7546
|
+
} catch {
|
|
7211
7547
|
}
|
|
7212
|
-
refreshGeneration += 1;
|
|
7213
7548
|
};
|
|
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));
|
|
7549
|
+
const stopTimer = () => {
|
|
7550
|
+
if (timer === void 0) return;
|
|
7551
|
+
clearInterval(timer);
|
|
7552
|
+
timer = void 0;
|
|
7224
7553
|
};
|
|
7225
|
-
const
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7554
|
+
const syncTimer = () => {
|
|
7555
|
+
if (!entries.some((entry) => entry.status.state === "running") && inspection.entry.status.state !== "running") stopTimer();
|
|
7556
|
+
else if (timer === void 0 && !disposed) {
|
|
7557
|
+
timer = setInterval(() => {
|
|
7558
|
+
if (timer !== void 0) void refresh();
|
|
7559
|
+
}, 1e3);
|
|
7560
|
+
unrefTimer(timer);
|
|
7561
|
+
}
|
|
7229
7562
|
};
|
|
7230
|
-
const
|
|
7231
|
-
const
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7563
|
+
const apply = (nextEntries, next) => {
|
|
7564
|
+
const selectedAction = actionMode ? options()[actionIndex] : void 0;
|
|
7565
|
+
const position = (list) => list.findIndex((entry) => entry.status.id === next.entry.status.id);
|
|
7566
|
+
if (position(entries) !== position(nextEntries)) selectionNeedsScroll = true;
|
|
7567
|
+
entries = nextEntries;
|
|
7568
|
+
inspection = next;
|
|
7569
|
+
if (selectedAction !== void 0) {
|
|
7570
|
+
const nextOptions = options();
|
|
7571
|
+
const kept = nextOptions.indexOf(selectedAction);
|
|
7572
|
+
actionIndex = kept >= 0 ? kept : Math.min(actionIndex, Math.max(0, nextOptions.length - 1));
|
|
7235
7573
|
}
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
if (actionRow < offset) offset = actionRow;
|
|
7239
|
-
else if (actionRow >= offset + viewport) offset = actionRow - viewport + 1;
|
|
7240
|
-
clampOffset(rows);
|
|
7574
|
+
syncTimer();
|
|
7575
|
+
requestRender();
|
|
7241
7576
|
};
|
|
7242
|
-
const
|
|
7243
|
-
|
|
7577
|
+
const reload = async (id = inspection.entry.status.id) => {
|
|
7578
|
+
const current = ++generation;
|
|
7579
|
+
const nextEntries = await loadEntries(manager, storageDirectory3, context);
|
|
7580
|
+
if (current !== generation) return;
|
|
7581
|
+
const selected = nextEntries.find((entry) => entry.status.id === id) ?? nextEntries[0];
|
|
7582
|
+
if (selected === void 0) {
|
|
7583
|
+
context.ui.notify("No durable subagent runs in this session.", "info");
|
|
7584
|
+
close(void 0);
|
|
7585
|
+
return;
|
|
7586
|
+
}
|
|
7587
|
+
let next;
|
|
7244
7588
|
try {
|
|
7245
|
-
|
|
7246
|
-
} catch {
|
|
7589
|
+
next = await inspectEntry(manager, storageDirectory3, selected, context);
|
|
7590
|
+
} catch (error) {
|
|
7591
|
+
if (current === generation) apply(nextEntries, { entry: selected, record: {} });
|
|
7592
|
+
throw error;
|
|
7247
7593
|
}
|
|
7594
|
+
if (current === generation) apply(nextEntries, next);
|
|
7248
7595
|
};
|
|
7249
|
-
const
|
|
7250
|
-
if (disposed || actionRunning || refreshing
|
|
7596
|
+
const refresh = async () => {
|
|
7597
|
+
if (disposed || actionRunning || refreshing) return;
|
|
7251
7598
|
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();
|
|
7599
|
+
const current = generation;
|
|
7600
|
+
const selectedId = inspection.entry.status.id;
|
|
7257
7601
|
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);
|
|
7602
|
+
const stale = entries.filter((entry) => entry.status.state === "running" || entry.status.id === selectedId);
|
|
7603
|
+
const results = await Promise.allSettled(stale.map((entry) => inspectEntry(manager, storageDirectory3, entry, context)));
|
|
7604
|
+
if (current !== generation) return;
|
|
7605
|
+
const fresh = new Map(results.flatMap((result2) => result2.status === "fulfilled" ? [[result2.value.entry.status.id, result2.value]] : []));
|
|
7606
|
+
apply(attentionSort(entries.map((entry) => fresh.get(entry.status.id)?.entry ?? entry)), fresh.get(selectedId) ?? inspection);
|
|
7272
7607
|
} finally {
|
|
7273
7608
|
refreshing = false;
|
|
7274
7609
|
}
|
|
7275
7610
|
};
|
|
7611
|
+
const select = (delta) => {
|
|
7612
|
+
const index = Math.max(0, entries.findIndex((entry2) => entry2.status.id === inspection.entry.status.id));
|
|
7613
|
+
const entry = entries[(index + delta + entries.length) % entries.length];
|
|
7614
|
+
if (entry === void 0 || entry.status.id === inspection.entry.status.id) return;
|
|
7615
|
+
inspection = { entry, record: {} };
|
|
7616
|
+
selectionNeedsScroll = true;
|
|
7617
|
+
const current = ++generation;
|
|
7618
|
+
void inspectEntry(manager, storageDirectory3, entry, context).then((next) => {
|
|
7619
|
+
if (current === generation) apply(entries, next);
|
|
7620
|
+
}, (error) => {
|
|
7621
|
+
if (current === generation) warn(`Cannot inspect subagent ${entry.status.id}: ${errorText(error)}`);
|
|
7622
|
+
});
|
|
7623
|
+
};
|
|
7624
|
+
const shutDown = () => {
|
|
7625
|
+
disposed = true;
|
|
7626
|
+
generation += 1;
|
|
7627
|
+
stopTimer();
|
|
7628
|
+
inlineConfirm.cancel();
|
|
7629
|
+
};
|
|
7276
7630
|
const close = (value) => {
|
|
7277
7631
|
if (disposed) return;
|
|
7278
|
-
|
|
7279
|
-
stopRefresh();
|
|
7632
|
+
shutDown();
|
|
7280
7633
|
done(value);
|
|
7281
7634
|
};
|
|
7282
7635
|
steerEditor.onSubmit = (value) => {
|
|
7283
7636
|
const message = value.trim();
|
|
7284
|
-
if (message) close({
|
|
7637
|
+
if (message) close({ entry: inspection.entry, message });
|
|
7285
7638
|
};
|
|
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"));
|
|
7639
|
+
const reloadAfterAction = async (id) => {
|
|
7640
|
+
try {
|
|
7641
|
+
await reload(id);
|
|
7642
|
+
} catch (error) {
|
|
7643
|
+
warn(`Cannot refresh subagents: ${errorText(error)}`);
|
|
7644
|
+
}
|
|
7298
7645
|
};
|
|
7299
7646
|
const runAction = (action) => {
|
|
7300
|
-
if (disposed) return;
|
|
7301
7647
|
actionRunning = true;
|
|
7302
7648
|
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();
|
|
7649
|
+
const bulk = BULK_DELETE[action];
|
|
7650
|
+
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");
|
|
7651
|
+
void pending.then(async (outcome) => {
|
|
7652
|
+
if (disposed || outcome === "unchanged") return;
|
|
7653
|
+
await reloadAfterAction(typeof outcome === "object" ? outcome.retryId : void 0);
|
|
7313
7654
|
actionMode = false;
|
|
7314
7655
|
actionIndex = 0;
|
|
7315
7656
|
offset = 0;
|
|
7316
|
-
|
|
7317
|
-
|
|
7657
|
+
selectionNeedsScroll = true;
|
|
7658
|
+
}, async (error) => {
|
|
7659
|
+
warn(`Cannot ${action.toLowerCase()}: ${errorText(error)}`);
|
|
7660
|
+
if (!disposed) await reloadAfterAction();
|
|
7318
7661
|
}).finally(() => {
|
|
7319
7662
|
actionRunning = false;
|
|
7320
|
-
|
|
7663
|
+
requestRender();
|
|
7321
7664
|
});
|
|
7322
7665
|
};
|
|
7323
|
-
|
|
7324
|
-
refreshTimer = setInterval(() => {
|
|
7325
|
-
void refreshInspection().catch(reportRefreshError);
|
|
7326
|
-
}, 1e3);
|
|
7327
|
-
unrefTimer(refreshTimer);
|
|
7328
|
-
}
|
|
7666
|
+
syncTimer();
|
|
7329
7667
|
return {
|
|
7330
|
-
render
|
|
7668
|
+
render(width) {
|
|
7669
|
+
if (disposed) return [];
|
|
7670
|
+
renderedWidth = width;
|
|
7671
|
+
const narrow = width < 80;
|
|
7672
|
+
const actions = actionMode ? options() : [];
|
|
7673
|
+
const menu = actionMode ? { options: actions, index: actionIndex } : steerMode ? void 0 : "hint";
|
|
7674
|
+
const layout = narrow ? detailsMode || actionMode || steerMode ? { detailsOnly: true } : { treeOnly: true } : {};
|
|
7675
|
+
const content = [...headerRows(entries, styles), ...formatNavigatorColumns(listRows(entries, inspection.entry.status.id, styles), detailRows(inspection, styles, menu), width, layout)];
|
|
7676
|
+
const footer = steerMode ? [styles.bold("Steer subagent"), ...steerEditor.render(width)] : inlineConfirm.rows(width, styles);
|
|
7677
|
+
const rows = Math.max(1, tuiRows(tui) - DASHBOARD_FOOTER_ROWS);
|
|
7678
|
+
const hintRows = rows >= 3 ? 1 : 0;
|
|
7679
|
+
const viewport = Math.max(1, rows - hintRows - footer.length);
|
|
7680
|
+
const keepVisible = (row) => {
|
|
7681
|
+
if (row < 0) return;
|
|
7682
|
+
if (row < offset) offset = row;
|
|
7683
|
+
else if (row >= offset + viewport) offset = row - viewport + 1;
|
|
7684
|
+
};
|
|
7685
|
+
if (actionMode) keepVisible(content.findIndex((line) => narrow ? line.startsWith("\u2192 ") : line.includes(" | \u2192 ")));
|
|
7686
|
+
else if (selectionNeedsScroll && !(narrow && detailsMode)) {
|
|
7687
|
+
keepVisible(content.findIndex((line) => line.startsWith("\u2192")));
|
|
7688
|
+
selectionNeedsScroll = false;
|
|
7689
|
+
}
|
|
7690
|
+
offset = Math.max(0, Math.min(Math.max(0, content.length - viewport), offset));
|
|
7691
|
+
const up = keyLabel("tui.select.up", "\u2191");
|
|
7692
|
+
const down = keyLabel("tui.select.down", "\u2193");
|
|
7693
|
+
const enter = keyLabel("tui.select.confirm", "enter");
|
|
7694
|
+
const esc = keyLabel("tui.select.cancel", "esc");
|
|
7695
|
+
const scroll = content.length > viewport ? ` \xB7 ${keyLabel("tui.select.pageUp", "pgup")}/${keyLabel("tui.select.pageDown", "pgdn")} scroll` : "";
|
|
7696
|
+
const refreshHint = timer === void 0 ? "" : " \xB7 auto-refresh 1s";
|
|
7697
|
+
const back = narrow && detailsMode ? "details" : "list";
|
|
7698
|
+
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}`;
|
|
7699
|
+
return [...content.slice(offset, offset + viewport), ...footer, ...hintRows ? [styles.dim(hint)] : []].map((line) => truncateToWidth3(line, Math.max(1, width), "\u2026"));
|
|
7700
|
+
},
|
|
7331
7701
|
invalidate() {
|
|
7332
7702
|
},
|
|
7333
7703
|
handleInput(data) {
|
|
7334
|
-
if (disposed
|
|
7704
|
+
if (disposed) return;
|
|
7705
|
+
if (inlineConfirm.active()) {
|
|
7706
|
+
inlineConfirm.handleInput(keybindings, data);
|
|
7707
|
+
return;
|
|
7708
|
+
}
|
|
7709
|
+
if (actionRunning) return;
|
|
7335
7710
|
if (steerMode) {
|
|
7336
7711
|
if (keybindings.matches(data, "tui.select.cancel")) {
|
|
7337
7712
|
steerMode = false;
|
|
7338
7713
|
actionMode = true;
|
|
7339
7714
|
steerEditor.setText("");
|
|
7340
|
-
offset = 0;
|
|
7341
|
-
scrollActionIntoView();
|
|
7342
7715
|
} else steerEditor.handleInput(data);
|
|
7343
7716
|
requestRender();
|
|
7344
7717
|
return;
|
|
7345
7718
|
}
|
|
7346
|
-
|
|
7719
|
+
const narrow = renderedWidth < 80;
|
|
7720
|
+
const page = Math.max(1, tuiRows(tui) - DASHBOARD_FOOTER_ROWS - 1);
|
|
7721
|
+
if (matches(data, "tui.select.pageUp")) offset = Math.max(0, offset - page);
|
|
7722
|
+
else if (matches(data, "tui.select.pageDown")) offset += page;
|
|
7723
|
+
else if (actionMode) {
|
|
7724
|
+
const actions = options();
|
|
7725
|
+
if (matches(data, "tui.select.cancel") || matches(data, "tui.editor.cursorLeft")) {
|
|
7726
|
+
actionMode = false;
|
|
7727
|
+
offset = 0;
|
|
7728
|
+
selectionNeedsScroll = true;
|
|
7729
|
+
} else if (matches(data, "tui.select.up")) actionIndex = (actionIndex + actions.length - 1) % actions.length;
|
|
7730
|
+
else if (matches(data, "tui.select.down")) actionIndex = (actionIndex + 1) % actions.length;
|
|
7731
|
+
else if (matches(data, "tui.select.confirm")) {
|
|
7732
|
+
const action = actions[actionIndex];
|
|
7733
|
+
if (action === "Steer") {
|
|
7734
|
+
steerMode = true;
|
|
7735
|
+
actionMode = false;
|
|
7736
|
+
steerEditor.setText("");
|
|
7737
|
+
} else if (action && action !== "Back") runAction(action);
|
|
7738
|
+
else {
|
|
7739
|
+
actionMode = false;
|
|
7740
|
+
offset = 0;
|
|
7741
|
+
selectionNeedsScroll = true;
|
|
7742
|
+
}
|
|
7743
|
+
}
|
|
7744
|
+
} else if (data === "a" || data === "A") {
|
|
7347
7745
|
actionMode = true;
|
|
7348
7746
|
actionIndex = 0;
|
|
7349
7747
|
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")) {
|
|
7748
|
+
} else if (matches(data, "tui.select.cancel")) {
|
|
7749
|
+
if (!narrow || !detailsMode) {
|
|
7750
|
+
close(void 0);
|
|
7751
|
+
return;
|
|
7752
|
+
}
|
|
7753
|
+
detailsMode = false;
|
|
7754
|
+
offset = 0;
|
|
7755
|
+
selectionNeedsScroll = true;
|
|
7756
|
+
} else if (narrow && detailsMode) {
|
|
7757
|
+
if (matches(data, "tui.select.up")) offset = Math.max(0, offset - 1);
|
|
7758
|
+
else if (matches(data, "tui.select.down")) offset += 1;
|
|
7759
|
+
else if (matches(data, "tui.select.confirm")) {
|
|
7365
7760
|
actionMode = true;
|
|
7366
7761
|
actionIndex = 0;
|
|
7367
7762
|
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
7763
|
}
|
|
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);
|
|
7764
|
+
} else if (matches(data, "tui.select.up")) select(-1);
|
|
7765
|
+
else if (matches(data, "tui.select.down")) select(1);
|
|
7766
|
+
else if (matches(data, "tui.select.confirm")) {
|
|
7767
|
+
if (narrow) detailsMode = true;
|
|
7768
|
+
else actionMode = true;
|
|
7769
|
+
actionIndex = 0;
|
|
7770
|
+
offset = 0;
|
|
7403
7771
|
}
|
|
7404
7772
|
requestRender();
|
|
7405
7773
|
},
|
|
7406
7774
|
dispose() {
|
|
7407
|
-
|
|
7408
|
-
disposed = true;
|
|
7409
|
-
stopRefresh();
|
|
7410
|
-
}
|
|
7775
|
+
shutDown();
|
|
7411
7776
|
}
|
|
7412
7777
|
};
|
|
7413
7778
|
});
|
|
7414
|
-
if (
|
|
7779
|
+
if (result === void 0) return;
|
|
7415
7780
|
try {
|
|
7416
|
-
await steerSubagent(manager, storageDirectory3, entry, context, result.message);
|
|
7781
|
+
await steerSubagent(manager, storageDirectory3, result.entry, context, result.message);
|
|
7417
7782
|
} catch (error) {
|
|
7418
7783
|
context.ui.notify(`Cannot steer: ${errorText(error)}`, "warning");
|
|
7419
7784
|
}
|
|
7420
|
-
return "exit";
|
|
7421
7785
|
}
|
|
7422
7786
|
async function runNavigator(manager, storageDirectory3, args, context, clipboard) {
|
|
7423
7787
|
if (args.trim()) {
|
|
@@ -7430,17 +7794,30 @@ async function runNavigator(manager, storageDirectory3, args, context, clipboard
|
|
|
7430
7794
|
context.ui.notify("No durable subagent runs in this session.", "info");
|
|
7431
7795
|
return;
|
|
7432
7796
|
}
|
|
7797
|
+
const labels = pickerLabels(entries);
|
|
7433
7798
|
if (!context.hasUI) {
|
|
7434
|
-
context.ui.notify(
|
|
7799
|
+
context.ui.notify(labels.join("\n"), "info");
|
|
7800
|
+
return;
|
|
7801
|
+
}
|
|
7802
|
+
if (context.mode === "tui") {
|
|
7803
|
+
await showDashboard(manager, storageDirectory3, entries, context, clipboard);
|
|
7435
7804
|
return;
|
|
7436
7805
|
}
|
|
7437
|
-
const
|
|
7438
|
-
const choice = await context.ui.select("Subagents\n", [...labels, "Close"]);
|
|
7806
|
+
const choice = await context.ui.select("Subagents\n", [...labels, "Close", ...bulkDeleteLabels(manager, entries)]);
|
|
7439
7807
|
if (!choice || choice === "Close") return;
|
|
7808
|
+
const bulk = BULK_DELETE[choice];
|
|
7809
|
+
if (bulk !== void 0) {
|
|
7810
|
+
try {
|
|
7811
|
+
await deleteAll(manager, storageDirectory3, context, bulk, (title, message) => context.ui.confirm(title, message));
|
|
7812
|
+
} catch (error) {
|
|
7813
|
+
context.ui.notify(`Cannot delete ${bulk} runs: ${errorText(error)}`, "warning");
|
|
7814
|
+
}
|
|
7815
|
+
continue;
|
|
7816
|
+
}
|
|
7440
7817
|
const selected = entries[labels.indexOf(choice)];
|
|
7441
7818
|
if (!selected) return;
|
|
7442
7819
|
try {
|
|
7443
|
-
|
|
7820
|
+
await showDetail(manager, storageDirectory3, selected, context, clipboard);
|
|
7444
7821
|
} catch (error) {
|
|
7445
7822
|
context.ui.notify(`Cannot inspect subagent ${selected.status.id}: ${errorText(error)}`, "warning");
|
|
7446
7823
|
}
|
|
@@ -7461,7 +7838,7 @@ function registerSubagentNavigator(registerCommand, manager, storageDirectory3,
|
|
|
7461
7838
|
|
|
7462
7839
|
// subagents/src/view.ts
|
|
7463
7840
|
import { truncateToWidth as truncateToWidth4 } from "@earendil-works/pi-tui";
|
|
7464
|
-
var
|
|
7841
|
+
var SPINNER2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7465
7842
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "failed", "stopped"]);
|
|
7466
7843
|
var MAX_INSPECT_VALUE_CHARS = 4e3;
|
|
7467
7844
|
var MAX_INSPECT_TOOL_CALLS = 32;
|
|
@@ -7527,8 +7904,8 @@ function runtime(startedAt, finishedAt, now) {
|
|
|
7527
7904
|
const remainingMinutes = minutes % 60;
|
|
7528
7905
|
return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
|
|
7529
7906
|
}
|
|
7530
|
-
function stateGlyph(state,
|
|
7531
|
-
if (state === "running") return
|
|
7907
|
+
function stateGlyph(state, spinner2) {
|
|
7908
|
+
if (state === "running") return spinner2;
|
|
7532
7909
|
return state === "completed" ? "\u2713" : "\u2717";
|
|
7533
7910
|
}
|
|
7534
7911
|
function stateColor(state) {
|
|
@@ -7552,13 +7929,15 @@ function stalledDuration2(status, now) {
|
|
|
7552
7929
|
const duration = now - lastEventAt;
|
|
7553
7930
|
return duration >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? duration : void 0;
|
|
7554
7931
|
}
|
|
7555
|
-
function
|
|
7932
|
+
function usageStats(status) {
|
|
7556
7933
|
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"}`;
|
|
7934
|
+
return value ? [formatTokens(value.input + value.output), formatCost(value.cost)].filter(Boolean).join(" \xB7 ") : "";
|
|
7560
7935
|
}
|
|
7561
|
-
function
|
|
7936
|
+
function modelName(status) {
|
|
7937
|
+
const model = status.progress?.state?.model ?? status.attemptDetails?.at(-1)?.setup.model;
|
|
7938
|
+
return model ? `${model.model}${model.thinking ? `:${model.thinking}` : ""}` : "";
|
|
7939
|
+
}
|
|
7940
|
+
function timestamp(value) {
|
|
7562
7941
|
if (value === void 0 || !Number.isFinite(value) || value < 0) return void 0;
|
|
7563
7942
|
const date = new Date(value);
|
|
7564
7943
|
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
@@ -7573,24 +7952,23 @@ function boundedValue(value) {
|
|
|
7573
7952
|
}
|
|
7574
7953
|
return text.length > MAX_INSPECT_VALUE_CHARS ? `${text.slice(0, MAX_INSPECT_VALUE_CHARS)}\u2026` : text;
|
|
7575
7954
|
}
|
|
7576
|
-
function formatSubagentProgress(status, args, theme,
|
|
7955
|
+
function formatSubagentProgress(status, args, theme, spinner2, now, expanded) {
|
|
7577
7956
|
const color = stateColor(status.state);
|
|
7578
7957
|
const elapsed = runtime(status.startedAt, status.finishedAt, now);
|
|
7579
7958
|
const metadata = requestMetadata(args, args.id === void 0);
|
|
7959
|
+
const stats = usageStats(status);
|
|
7580
7960
|
const lines = [
|
|
7581
|
-
`${theme.fg(color, stateGlyph(status.state,
|
|
7961
|
+
`${theme.fg(color, stateGlyph(status.state, spinner2))} ${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
7962
|
];
|
|
7583
7963
|
const current = activity(status);
|
|
7584
7964
|
const stalled = stalledDuration2(status, now);
|
|
7585
|
-
if (current) lines.push(` ${theme.fg("accent",
|
|
7965
|
+
if (current) lines.push(` ${theme.fg("accent", spinner2)} ${theme.fg("dim", current)}${stalled === void 0 ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
7586
7966
|
else if (stalled !== void 0) lines.push(` ${theme.fg("warning", `stalled? ${formatStalledDuration(stalled)}`)}`);
|
|
7587
7967
|
if (status.error) lines.push(` ${theme.fg("error", `${status.error.code}: ${status.error.message}`)}`);
|
|
7588
7968
|
if (expanded) {
|
|
7589
7969
|
lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
|
|
7590
7970
|
const model = status.progress?.state?.model;
|
|
7591
7971
|
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
7972
|
if (status.worktree) lines.push(` ${theme.fg("dim", `worktree=${status.worktree.path} branch=${status.worktree.branch}`)}`);
|
|
7595
7973
|
}
|
|
7596
7974
|
return lines.join("\n");
|
|
@@ -7607,9 +7985,9 @@ function formatSubagentInspection(status, args, details, theme, expanded) {
|
|
|
7607
7985
|
const lines = [formatSubagentProgress(status, { id: args.id ?? status.id }, theme, "\u25C7", Date.now(), false)];
|
|
7608
7986
|
if (!expanded) return lines[0] ?? "";
|
|
7609
7987
|
lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
|
|
7610
|
-
const startedAt =
|
|
7611
|
-
const finishedAt =
|
|
7612
|
-
const lastEventAt =
|
|
7988
|
+
const startedAt = timestamp(status.startedAt);
|
|
7989
|
+
const finishedAt = timestamp(status.finishedAt);
|
|
7990
|
+
const lastEventAt = timestamp(status.progress?.lastEventAt);
|
|
7613
7991
|
if (startedAt) lines.push(` ${theme.fg("dim", `startedAt=${startedAt}`)}`);
|
|
7614
7992
|
if (finishedAt) lines.push(` ${theme.fg("dim", `finishedAt=${finishedAt}`)}`);
|
|
7615
7993
|
if (lastEventAt) lines.push(` ${theme.fg("dim", `lastEventAt=${lastEventAt}`)}`);
|
|
@@ -7617,8 +7995,8 @@ function formatSubagentInspection(status, args, details, theme, expanded) {
|
|
|
7617
7995
|
if (model) lines.push(` ${theme.fg("dim", `model=${model.provider}/${model.model}${model.thinking ? `:${model.thinking}` : ""}`)}`);
|
|
7618
7996
|
const currentActivity = status.progress?.activity;
|
|
7619
7997
|
if (currentActivity) lines.push(` ${theme.fg("dim", `activity=${currentActivity.kind}${currentActivity.text ? ` ${sanitizeDisplayText(currentActivity.text)}` : ""}`)}`);
|
|
7620
|
-
const
|
|
7621
|
-
if (
|
|
7998
|
+
const accounting2 = status.progress?.accounting;
|
|
7999
|
+
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
8000
|
if (status.worktree) lines.push(` ${theme.fg("dim", `worktree=${status.worktree.path} branch=${status.worktree.branch}`)}`);
|
|
7623
8001
|
const toolCalls = status.progress?.toolCalls;
|
|
7624
8002
|
if (toolCalls?.length) {
|
|
@@ -7650,7 +8028,7 @@ function subagentProgressBlock(status, args, theme, freezeAt) {
|
|
|
7650
8028
|
render(width) {
|
|
7651
8029
|
const now = TERMINAL_STATES.has(current.state) ? frozenAt : Date.now();
|
|
7652
8030
|
if (!TERMINAL_STATES.has(current.state)) frozenAt = now;
|
|
7653
|
-
const frame =
|
|
8031
|
+
const frame = SPINNER2[Math.floor(now / 80) % SPINNER2.length] ?? "\u25C7";
|
|
7654
8032
|
return formatSubagentProgress(current, args, currentTheme, frame, now, expanded).split("\n").map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026"));
|
|
7655
8033
|
},
|
|
7656
8034
|
invalidate() {
|
|
@@ -7688,9 +8066,68 @@ function renderSubagentResult(result, options, theme, context) {
|
|
|
7688
8066
|
component.setExpanded(options.expanded);
|
|
7689
8067
|
return component;
|
|
7690
8068
|
}
|
|
7691
|
-
function
|
|
8069
|
+
function widgetRow({ status, request }, now, paint) {
|
|
8070
|
+
const lastEventAt = status.progress?.lastEventAt;
|
|
8071
|
+
const silent = lastEventAt === void 0 || !Number.isFinite(lastEventAt) || now - lastEventAt < QUIET_MS ? void 0 : now - lastEventAt;
|
|
8072
|
+
const role = silent !== void 0 && silent >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? "error" : "warning";
|
|
8073
|
+
const icon = silent === void 0 ? mark(status.state, now, paint) : paint(role, "\u26A0");
|
|
8074
|
+
const warning = silent === void 0 ? "" : ` ${paint(role, `${role === "error" ? "stalled" : "quiet"} ${formatElapsed(silent)}`)}`;
|
|
8075
|
+
const accounting2 = status.progress?.accounting;
|
|
8076
|
+
const attempts = status.attempts ?? 1;
|
|
8077
|
+
const right = [
|
|
8078
|
+
modelName(status),
|
|
8079
|
+
formatTokens(accounting2 === void 0 ? void 0 : accounting2.input + accounting2.output),
|
|
8080
|
+
formatCost(accounting2?.cost),
|
|
8081
|
+
status.startedAt === void 0 ? "" : formatElapsed(now - status.startedAt),
|
|
8082
|
+
attempts > 1 ? `attempt ${String(attempts)}` : ""
|
|
8083
|
+
].filter(Boolean).join(" \xB7 ");
|
|
8084
|
+
return { text: `${icon} ${sanitizeDisplayText(label({ ...request, id: status.id }))}${warning}`, right };
|
|
8085
|
+
}
|
|
8086
|
+
var RECEIPT_ENTRY_TYPE = "piewf-subagent-receipt";
|
|
8087
|
+
function receiptFor(status, request) {
|
|
8088
|
+
const accounting2 = status.progress?.accounting;
|
|
8089
|
+
const model = modelName(status);
|
|
8090
|
+
const role = roleName(request.role);
|
|
8091
|
+
const tools = status.progress?.state?.tools ?? status.attemptDetails?.at(-1)?.setup.tools;
|
|
8092
|
+
return {
|
|
8093
|
+
id: status.id,
|
|
8094
|
+
label: label({ ...request, id: status.id }),
|
|
8095
|
+
state: status.state,
|
|
8096
|
+
...model ? { model } : {},
|
|
8097
|
+
...role === void 0 ? {} : { role },
|
|
8098
|
+
...tools === void 0 ? {} : { tools: [...tools] },
|
|
8099
|
+
input: accounting2?.input ?? 0,
|
|
8100
|
+
output: accounting2?.output ?? 0,
|
|
8101
|
+
cacheRead: accounting2?.cacheRead ?? 0,
|
|
8102
|
+
costUsd: accounting2?.cost ?? 0,
|
|
8103
|
+
durationMs: status.startedAt === void 0 ? 0 : Math.max(0, (status.finishedAt ?? Date.now()) - status.startedAt),
|
|
8104
|
+
attempts: status.attempts ?? 1,
|
|
8105
|
+
...status.error === void 0 ? {} : { error: `${status.error.code}: ${status.error.message}` }
|
|
8106
|
+
};
|
|
8107
|
+
}
|
|
8108
|
+
function renderSubagentReceipt(data, expanded, theme) {
|
|
8109
|
+
const glyph = data.state === "completed" ? theme.fg("success", "\u2713") : data.state === "failed" ? theme.fg("error", "\u2717") : theme.fg("muted", "\xB7");
|
|
8110
|
+
const headline = [formatTokens(data.input + data.output), formatCost(data.costUsd) || "$0.00", formatElapsed(data.durationMs), data.state].filter(Boolean).join(" \xB7 ");
|
|
8111
|
+
const lines = [`${glyph} ${theme.bold(sanitizeDisplayText(data.label))} ${theme.fg("muted", headline)}`];
|
|
8112
|
+
if (!expanded) return lines;
|
|
8113
|
+
const meta = [data.model ?? "", data.role ? `role ${data.role}` : "", data.attempts > 1 ? `${String(data.attempts)} attempts` : "", data.tools?.join(" ") ?? ""].filter(Boolean);
|
|
8114
|
+
if (meta.length > 0) lines.push(theme.fg("muted", ` ${meta.join(" \xB7 ")}`));
|
|
8115
|
+
const split = [data.input ? `in ${formatTokens(data.input)}` : "", data.output ? `out ${formatTokens(data.output)}` : "", data.cacheRead ? `cache ${formatTokens(data.cacheRead)}` : ""].filter(Boolean);
|
|
8116
|
+
if (split.length > 0) lines.push(theme.fg("muted", ` ${split.join(" \xB7 ")}`));
|
|
8117
|
+
if (data.error !== void 0) lines.push(theme.fg("error", ` ${sanitizeDisplayText(data.error)}`));
|
|
8118
|
+
lines.push(theme.fg("muted", ` run ${data.id}`));
|
|
8119
|
+
return lines;
|
|
8120
|
+
}
|
|
8121
|
+
function createSubagentBackgroundWidget(host = {}) {
|
|
7692
8122
|
const key = "piewf-subagents-background";
|
|
7693
8123
|
const runs = /* @__PURE__ */ new Map();
|
|
8124
|
+
const receipted = /* @__PURE__ */ new Set();
|
|
8125
|
+
host.registerEntryRenderer?.(RECEIPT_ENTRY_TYPE, (entry, options, theme) => {
|
|
8126
|
+
if (!entry.data) return void 0;
|
|
8127
|
+
const lines = renderSubagentReceipt(entry.data, options.expanded, theme);
|
|
8128
|
+
return { render: (width) => lines.map((line) => truncateToWidth4(line, Math.max(1, width), "\u2026")), invalidate() {
|
|
8129
|
+
} };
|
|
8130
|
+
});
|
|
7694
8131
|
let context;
|
|
7695
8132
|
let requestRender;
|
|
7696
8133
|
let timer;
|
|
@@ -7724,23 +8161,12 @@ function createSubagentBackgroundWidget() {
|
|
|
7724
8161
|
return {
|
|
7725
8162
|
render(width) {
|
|
7726
8163
|
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"));
|
|
8164
|
+
const paintRole = (role, text) => theme.fg(role, text);
|
|
8165
|
+
const budget = MAX_BACKGROUND_WIDGET_ROWS - 2;
|
|
8166
|
+
const rows = [...runs.values()].map((run) => widgetRow(run, now, paintRole));
|
|
8167
|
+
const body = rows.length <= budget ? rows : [...rows.slice(0, budget - 1), { text: theme.fg("muted", `\u2026 ${String(rows.length - budget + 1)} more`) }];
|
|
8168
|
+
const title = runs.size === 1 ? "Subagent" : `Subagents \xB7 ${String(runs.size)} runs`;
|
|
8169
|
+
return drawFrame(title, "", body, width, paintRole, (text) => theme.fg("accent", text)).map((line) => truncateToWidth4(line, Math.max(1, width), ""));
|
|
7744
8170
|
},
|
|
7745
8171
|
invalidate() {
|
|
7746
8172
|
}
|
|
@@ -7759,12 +8185,21 @@ function createSubagentBackgroundWidget() {
|
|
|
7759
8185
|
start(next) {
|
|
7760
8186
|
hide();
|
|
7761
8187
|
runs.clear();
|
|
8188
|
+
receipted.clear();
|
|
7762
8189
|
context = next.mode === "tui" ? next : void 0;
|
|
7763
8190
|
},
|
|
7764
8191
|
update(status, request) {
|
|
7765
8192
|
if (request.mode !== "background" || context === void 0) return;
|
|
7766
|
-
if (TERMINAL_STATES.has(status.state))
|
|
7767
|
-
|
|
8193
|
+
if (TERMINAL_STATES.has(status.state)) {
|
|
8194
|
+
runs.delete(status.id);
|
|
8195
|
+
if (!receipted.has(status.id)) {
|
|
8196
|
+
receipted.add(status.id);
|
|
8197
|
+
try {
|
|
8198
|
+
host.appendEntry?.(RECEIPT_ENTRY_TYPE, receiptFor(status, request));
|
|
8199
|
+
} catch {
|
|
8200
|
+
}
|
|
8201
|
+
}
|
|
8202
|
+
} else runs.set(status.id, { status, request });
|
|
7768
8203
|
paint();
|
|
7769
8204
|
},
|
|
7770
8205
|
dispose() {
|
|
@@ -7949,7 +8384,7 @@ function managerDependencies(options, activeTools, notify, onStatus, onResourceW
|
|
|
7949
8384
|
}
|
|
7950
8385
|
function storageDirectory2(options) {
|
|
7951
8386
|
const dependencies = options.managerDependencies;
|
|
7952
|
-
return dependencies?.storageDir ??
|
|
8387
|
+
return dependencies?.storageDir ?? join11(dependencies?.agentDir ?? getAgentDir8(), "subagents");
|
|
7953
8388
|
}
|
|
7954
8389
|
function createSubagentsExtension(options = {}, activeTools, notify, onStatus, onResourceWarning) {
|
|
7955
8390
|
const manager = options.manager ?? createSubagentManager(managerDependencies(options, activeTools, notify, onStatus, onResourceWarning));
|
|
@@ -7966,7 +8401,10 @@ function registerSubagentsExtension(pi, options = {}) {
|
|
|
7966
8401
|
const onResourceWarning = sendMessage === void 0 ? void 0 : (message) => {
|
|
7967
8402
|
sendMessage.call(pi, { customType: "subagents", content: `Warning: ${message}`, display: true }, { deliverAs: "steer" });
|
|
7968
8403
|
};
|
|
7969
|
-
const
|
|
8404
|
+
const appendEntry = pi.appendEntry;
|
|
8405
|
+
const widget2 = createSubagentBackgroundWidget({ ...appendEntry === void 0 ? {} : { appendEntry: (customType, data) => {
|
|
8406
|
+
appendEntry.call(pi, customType, data);
|
|
8407
|
+
} }, ...pi.registerEntryRenderer === void 0 ? {} : { registerEntryRenderer: pi.registerEntryRenderer.bind(pi) } });
|
|
7970
8408
|
const extension2 = createSubagentsExtension(options, activeTools, notify, (status, request) => {
|
|
7971
8409
|
widget2.update(status, request);
|
|
7972
8410
|
const registry = loadingRegistry();
|