@selesai/code 0.13.2 → 0.13.4
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 +20 -0
- package/dist/defaults/models.json +85 -13
- package/dist/extensions/cost-reconcile.test.ts +200 -4
- package/dist/extensions/cost-reconcile.ts +88 -102
- package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
- package/dist/extensions/pi-intercom/README.md +4 -5
- package/dist/extensions/pi-intercom/config.test.ts +3 -31
- package/dist/extensions/pi-intercom/config.ts +0 -15
- package/dist/extensions/pi-intercom/index.ts +9 -46
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +49 -57
- package/dist/extensions/pi-intercom/package.json +1 -1
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +20 -0
- package/dist/extensions/pi-intercom/reply-tracker.ts +8 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +27 -0
- package/dist/extensions/pi-subagents/docs/tool-reference.md +4 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +2 -2
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +48 -243
- package/dist/extensions/pi-subagents/skills/council-mode/references/pass-contracts.md +150 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +87 -37
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +29 -233
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +49 -8
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +34 -27
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +157 -28
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -0
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +45 -2
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +3 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +13 -2
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +5 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +10 -2
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +11 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +98 -1
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +138 -4
- package/dist/extensions/pi-subagents/src/runs/shared/background-process-options.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
- package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +52 -3
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +47 -1
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +45 -18
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +21 -2
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +15 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +34 -1
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +11 -3
- package/dist/extensions/pi-subagents/src/tui/render-helpers.ts +31 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +597 -112
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +40 -1
- package/dist/extensions/pi-subagents/src/workflows/host-command.ts +6 -1
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +53 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +55 -6
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +111 -1
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +206 -38
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +522 -31
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +123 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +57 -1
- package/dist/extensions/pi-subagents/test/unit/background-process-options.test.ts +17 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +44 -4
- package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +20 -3
- package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +93 -17
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +103 -26
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +58 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +22 -2
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +13 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +54 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +48 -1
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +21 -6
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +11 -0
- package/dist/extensions/pi-web-agent/CHANGELOG.md +410 -0
- package/dist/extensions/pi-web-agent/README.md +131 -0
- package/dist/extensions/pi-web-agent/package.json +4 -2
- package/dist/extensions/pi-web-agent/src/backends/config.ts +62 -5
- package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -0
- package/dist/extensions/pi-web-agent/src/backends/factory.ts +96 -6
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +183 -47
- package/dist/extensions/pi-web-agent/src/extension.ts +62 -25
- package/dist/extensions/pi-web-agent/src/extract/readability.ts +19 -11
- package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +5 -4
- package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +2 -25
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +5 -2
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +2 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +72 -7
- package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +8 -1
- package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +28 -3
- package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +4 -0
- package/dist/extensions/pi-web-agent/src/orchestration/url.ts +35 -0
- package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +18 -6
- package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +14 -2
- package/dist/extensions/pi-web-agent/src/readers/github-reader.ts +150 -0
- package/dist/extensions/pi-web-agent/src/readers/limits.ts +3 -0
- package/dist/extensions/pi-web-agent/src/readers/pdf-reader.ts +87 -0
- package/dist/extensions/pi-web-agent/src/readers/resolver.ts +25 -0
- package/dist/extensions/pi-web-agent/src/readers/types.ts +11 -0
- package/dist/extensions/pi-web-agent/src/readers/youtube-reader.ts +79 -0
- package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +32 -5
- package/dist/extensions/pi-web-agent/src/search/exa.ts +109 -0
- package/dist/extensions/pi-web-agent/src/search/fanout.ts +154 -0
- package/dist/extensions/pi-web-agent/src/search/tavily.ts +113 -0
- package/dist/extensions/pi-web-agent/src/search/youcom.ts +109 -0
- package/dist/extensions/pi-web-agent/src/tools/web-search.ts +22 -9
- package/dist/extensions/pi-web-agent/src/types.ts +19 -4
- package/dist/extensions/tokenin-onboarding.ts +302 -0
- package/package.json +1 -1
|
@@ -32,9 +32,10 @@ import { toModelInfo } from "../shared/model-info.ts";
|
|
|
32
32
|
import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/model-fallback.ts";
|
|
33
33
|
import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
|
|
34
34
|
import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
|
|
35
|
-
import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
|
|
36
|
-
import type { AcceptanceInput, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
|
|
35
|
+
import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, resolveExternalCliRunnerStatus, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
|
|
36
|
+
import type { AcceptanceInput, AgentCapabilitiesSnapshot, AgentCapabilityRow, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
|
|
37
37
|
import { getProjectConfigDir } from "../shared/utils.ts";
|
|
38
|
+
import { previewDisplayText } from "../shared/display-text.ts";
|
|
38
39
|
import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
39
40
|
import { listRuntimeAgentConfigs, mergeRuntimeAgents, type RuntimeAgentOwner } from "./runtime-agent-registry.ts";
|
|
40
41
|
import { listExternalJobProviders } from "../api/external-job-provider.ts";
|
|
@@ -47,11 +48,21 @@ interface ManagementParams {
|
|
|
47
48
|
action?: string;
|
|
48
49
|
agent?: string;
|
|
49
50
|
agentScope?: unknown;
|
|
51
|
+
capabilities?: unknown;
|
|
50
52
|
config?: unknown;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
function result(text: string, isError = false): AgentToolResult<Details> {
|
|
54
|
-
return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
|
|
55
|
+
function result(text: string, isError = false, details?: Partial<Details>): AgentToolResult<Details> {
|
|
56
|
+
return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [], ...details } };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function jsonDetails<T>(value: T): T {
|
|
60
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function presentDetails<T extends Record<string, unknown>>(value: T): T | undefined {
|
|
64
|
+
const cleaned = jsonDetails(value);
|
|
65
|
+
return Object.keys(cleaned).length > 0 ? cleaned : undefined;
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
function parseCsv(value: string): string[] {
|
|
@@ -645,18 +656,135 @@ function runnerListBadge(agent: AgentConfig, providerNames: Set<string> | undefi
|
|
|
645
656
|
return undefined;
|
|
646
657
|
}
|
|
647
658
|
|
|
648
|
-
function
|
|
659
|
+
function agentListMetadata(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
649
660
|
const source = agent.source === "package" ? packageSourceLabel(agent) : agent.source;
|
|
650
|
-
|
|
661
|
+
return [
|
|
651
662
|
source,
|
|
652
663
|
runnerListBadge(agent, providerNames),
|
|
653
664
|
agent.defaultContext ? `context: ${agent.defaultContext}` : undefined,
|
|
654
665
|
agent.aliases?.length ? `aliases: ${agent.aliases.join(", ")}` : undefined,
|
|
655
|
-
].filter((part): part is string => Boolean(part));
|
|
656
|
-
|
|
666
|
+
].filter((part): part is string => Boolean(part)).join(", ");
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function formatAgentListLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
670
|
+
return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): ${agent.description}`;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
674
|
+
const declaredTools = [
|
|
675
|
+
...(agent.tools ?? []),
|
|
676
|
+
...(agent.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
|
|
677
|
+
];
|
|
678
|
+
let tools = "none";
|
|
679
|
+
if (agent.tools === undefined && agent.mcpDirectTools === undefined) {
|
|
680
|
+
tools = "default/ambient";
|
|
681
|
+
} else if (declaredTools.length > 0) {
|
|
682
|
+
tools = declaredTools.join(", ");
|
|
683
|
+
}
|
|
684
|
+
let model = "inherits current session";
|
|
685
|
+
if (agent.model !== undefined) {
|
|
686
|
+
model = agent.model;
|
|
687
|
+
if (agent.modelProvider && !agent.model.includes("/")) model = `${agent.modelProvider}/${agent.model}`;
|
|
688
|
+
}
|
|
689
|
+
const thinking = agent.thinking === false ? "off" : agent.thinking ?? "default";
|
|
690
|
+
return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): Description: ${previewDisplayText(agent.description, 240)}; Tools: ${tools}; Model: ${model}; Thinking: ${thinking}`;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const EXTERNAL_JOB_CAPABILITIES = { stop: false, steer: false, resume: false, structuredOutput: false, toolEvents: false } as const;
|
|
694
|
+
const PI_AGENT_RUNNER = { type: "pi" } as const;
|
|
695
|
+
|
|
696
|
+
function listOrEmpty<T>(values: T[] | undefined): T[] {
|
|
697
|
+
return values ?? [];
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function agentCapabilityRunner(agent: AgentConfig, providerNames: Set<string> | undefined): AgentCapabilityRow["runner"] {
|
|
701
|
+
const runner = agent.runner;
|
|
702
|
+
if (!runner || runner.type === "pi") return PI_AGENT_RUNNER;
|
|
703
|
+
if (runner.type === "external-cli") return { type: "external-cli", adapter: runner.adapter, capabilities: resolveExternalCliRunnerStatus(runner).capabilities };
|
|
704
|
+
return { type: "external-job", provider: runner.provider, available: providerNames?.has(runner.provider), capabilities: EXTERNAL_JOB_CAPABILITIES };
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function agentCapabilityTools(agent: AgentConfig): AgentCapabilityRow["tools"] {
|
|
708
|
+
return {
|
|
709
|
+
ambient: agent.tools === undefined && agent.mcpDirectTools === undefined,
|
|
710
|
+
names: listOrEmpty(agent.tools),
|
|
711
|
+
mcpDirectTools: listOrEmpty(agent.mcpDirectTools),
|
|
712
|
+
mutationTools: agent.mutationTools,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean; providerNames?: Set<string>; restrictionSources?: string[] }): AgentCapabilityRow {
|
|
717
|
+
return {
|
|
718
|
+
name: agent.name,
|
|
719
|
+
description: previewDisplayText(agent.description, 1000),
|
|
720
|
+
source: agent.source,
|
|
721
|
+
executable: options.executable,
|
|
722
|
+
restrictionSources: options.executable ? undefined : options.restrictionSources ?? [],
|
|
723
|
+
aliases: agent.aliases ? [...agent.aliases] : undefined,
|
|
724
|
+
runner: agentCapabilityRunner(agent, options.providerNames),
|
|
725
|
+
tools: agentCapabilityTools(agent),
|
|
726
|
+
model: presentDetails({ value: agent.model, fallbackModels: agent.fallbackModels, thinking: agent.thinking }),
|
|
727
|
+
execution: presentDetails({ defaultAsync: agent.defaultAsync, timeoutMs: agent.defaultTimeoutMs }),
|
|
728
|
+
output: presentDetails({ path: agent.output, mode: agent.outputMode }),
|
|
729
|
+
extensions: presentDetails({ names: agent.extensions, subagentOnly: agent.subagentOnlyExtensions, skills: agent.skills }),
|
|
730
|
+
};
|
|
657
731
|
}
|
|
658
732
|
|
|
659
|
-
function
|
|
733
|
+
function agentCapabilitiesSnapshot(input: { agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): AgentCapabilitiesSnapshot {
|
|
734
|
+
return {
|
|
735
|
+
agents: [
|
|
736
|
+
...input.agents.map((agent) => agentCapabilityRow(agent, { executable: true, providerNames: input.providerNames })),
|
|
737
|
+
...input.restrictedAgents.map((agent) => agentCapabilityRow(agent, { executable: false, providerNames: input.providerNames, restrictionSources: input.restrictedSources })),
|
|
738
|
+
],
|
|
739
|
+
restrictedCount: input.restrictedAgents.length,
|
|
740
|
+
...(input.restrictedSources?.length ? { capabilityCeilingSources: [...input.restrictedSources] } : {}),
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function providerNames(status: ExternalJobProviderStatus): Set<string> | undefined {
|
|
745
|
+
return status.ok ? status.names : undefined;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function appendRestrictedAgentLines(input: { lines: string[]; agents: AgentConfig[]; sources?: string[]; providerNames?: Set<string>; formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string }): void {
|
|
749
|
+
if (input.agents.length === 0) return;
|
|
750
|
+
input.lines.push(
|
|
751
|
+
"",
|
|
752
|
+
`Restricted agents (not executable in this session${input.sources?.length ? `; capability ceiling: ${input.sources.join(", ")}` : ""}):`,
|
|
753
|
+
...input.agents.map((agent) => input.formatLine(agent, input.providerNames)),
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function appendExternalJobRegistryLine(lines: string[], agents: AgentConfig[], status: ExternalJobProviderStatus): void {
|
|
758
|
+
if (status.ok || !agents.some((agent) => agent.runner?.type === "external-job")) return;
|
|
759
|
+
lines.push("", `External-job provider registry unavailable: ${status.error}`);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function appendAgentDiagnosticLines(lines: string[], diagnostics: AgentDiscoveryDiagnostic[] | undefined): void {
|
|
763
|
+
if (!diagnostics?.length) return;
|
|
764
|
+
lines.push(
|
|
765
|
+
"",
|
|
766
|
+
"Invalid agent definitions:",
|
|
767
|
+
...diagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function agentCapabilityDetails(input: { capabilityMode: boolean; agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): Partial<Details> | undefined {
|
|
772
|
+
if (!input.capabilityMode) return undefined;
|
|
773
|
+
return {
|
|
774
|
+
agentCapabilities: jsonDetails(agentCapabilitiesSnapshot({
|
|
775
|
+
agents: input.agents,
|
|
776
|
+
restrictedAgents: input.restrictedAgents,
|
|
777
|
+
providerNames: input.providerNames,
|
|
778
|
+
restrictedSources: input.restrictedSources,
|
|
779
|
+
})),
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function formatAgentListSections(
|
|
784
|
+
agents: AgentConfig[],
|
|
785
|
+
providerNames: Set<string> | undefined,
|
|
786
|
+
formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string = formatAgentListLine,
|
|
787
|
+
): string[] {
|
|
660
788
|
if (agents.length === 0) return ["- (none)"];
|
|
661
789
|
const sections: Array<[AgentSource, string]> = [
|
|
662
790
|
["package", "Package agents"],
|
|
@@ -670,7 +798,7 @@ function formatAgentListSections(agents: AgentConfig[], providerNames: Set<strin
|
|
|
670
798
|
const matches = agents.filter((agent) => agent.source === source);
|
|
671
799
|
if (matches.length === 0) continue;
|
|
672
800
|
if (lines.length > 0) lines.push("");
|
|
673
|
-
lines.push(label, ...matches.map((agent) =>
|
|
801
|
+
lines.push(label, ...matches.map((agent) => formatLine(agent, providerNames)));
|
|
674
802
|
}
|
|
675
803
|
return lines;
|
|
676
804
|
}
|
|
@@ -758,23 +886,24 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
758
886
|
discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd),
|
|
759
887
|
});
|
|
760
888
|
const providerStatus = registeredExternalJobProviderStatus();
|
|
889
|
+
const providerNameSet = providerNames(providerStatus);
|
|
890
|
+
const capabilityMode = params.capabilities === true;
|
|
891
|
+
const formatLine = capabilityMode ? formatAgentCapabilitiesLine : formatAgentListLine;
|
|
761
892
|
const lines = [
|
|
762
|
-
"Executable agents:",
|
|
763
|
-
...formatAgentListSections(agents,
|
|
764
|
-
...(restrictedAgents.length ? [
|
|
765
|
-
"",
|
|
766
|
-
`Restricted agents (not executable in this session${restrictedSources?.length ? `; capability ceiling: ${restrictedSources.join(", ")}` : ""}):`,
|
|
767
|
-
...restrictedAgents.map((a) => formatAgentListLine(a, providerStatus.ok ? providerStatus.names : undefined)),
|
|
768
|
-
] : []),
|
|
769
|
-
...(!providerStatus.ok && [...agents, ...restrictedAgents].some((agent) => agent.runner?.type === "external-job") ? ["", `External-job provider registry unavailable: ${providerStatus.error}`] : []),
|
|
770
|
-
...(d.agentDiagnostics?.length ? [
|
|
771
|
-
"",
|
|
772
|
-
"Invalid agent definitions:",
|
|
773
|
-
...d.agentDiagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
|
|
774
|
-
] : []),
|
|
775
|
-
...(proactiveSuggestions.length ? ["", ...proactiveSuggestions] : []),
|
|
893
|
+
capabilityMode ? "Executable agents (capabilities):" : "Executable agents:",
|
|
894
|
+
...formatAgentListSections(agents, providerNameSet, formatLine),
|
|
776
895
|
];
|
|
777
|
-
|
|
896
|
+
appendRestrictedAgentLines({ lines, agents: restrictedAgents, sources: restrictedSources, providerNames: providerNameSet, formatLine });
|
|
897
|
+
appendExternalJobRegistryLine(lines, [...agents, ...restrictedAgents], providerStatus);
|
|
898
|
+
appendAgentDiagnosticLines(lines, d.agentDiagnostics);
|
|
899
|
+
if (proactiveSuggestions.length) lines.push("", ...proactiveSuggestions);
|
|
900
|
+
return result(lines.join("\n"), false, agentCapabilityDetails({
|
|
901
|
+
capabilityMode,
|
|
902
|
+
agents,
|
|
903
|
+
restrictedAgents,
|
|
904
|
+
providerNames: providerNameSet,
|
|
905
|
+
restrictedSources,
|
|
906
|
+
}));
|
|
778
907
|
}
|
|
779
908
|
|
|
780
909
|
function formatModelSource(agent: AgentConfig, currentModel: ParentModel | undefined): string {
|
|
@@ -1066,7 +1195,7 @@ function handleDisable(params: ManagementParams, ctx: ManagementContext): AgentT
|
|
|
1066
1195
|
const scope = parsedScope.scope!;
|
|
1067
1196
|
const d = discoverAgentsAll(ctx.cwd);
|
|
1068
1197
|
if (scope === "project" && d.projectSettingsPath === null) {
|
|
1069
|
-
return result("Project override is not available here: no project config root (.
|
|
1198
|
+
return result("Project override is not available here: no project config root (.selesai or .agents) was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
|
|
1070
1199
|
}
|
|
1071
1200
|
const effective = resolveEffectiveAgent(d, raw);
|
|
1072
1201
|
if (effective.error) return result(effective.error, true);
|
|
@@ -1090,7 +1219,7 @@ function handleEnable(params: ManagementParams, ctx: ManagementContext): AgentTo
|
|
|
1090
1219
|
const scope = parsedScope.scope!;
|
|
1091
1220
|
const d = discoverAgentsAll(ctx.cwd);
|
|
1092
1221
|
if (scope === "project" && d.projectSettingsPath === null) {
|
|
1093
|
-
return result("Project override is not available here: no project config root (.
|
|
1222
|
+
return result("Project override is not available here: no project config root (.selesai or .agents) was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
|
|
1094
1223
|
}
|
|
1095
1224
|
const effective = resolveEffectiveAgent(d, raw);
|
|
1096
1225
|
if (effective.error) return result(effective.error, true);
|
|
@@ -1119,7 +1248,7 @@ function handleReset(params: ManagementParams, ctx: ManagementContext): AgentToo
|
|
|
1119
1248
|
const scope = parsedScope.scope!;
|
|
1120
1249
|
const d = discoverAgentsAll(ctx.cwd);
|
|
1121
1250
|
if (scope === "project" && d.projectSettingsPath === null) {
|
|
1122
|
-
return result("Project override is not available here: no project config root (.
|
|
1251
|
+
return result("Project override is not available here: no project config root (.selesai or .agents) was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
|
|
1123
1252
|
}
|
|
1124
1253
|
const bundled = [...d.package, ...d.builtin].find((a) => a.name === raw || a.name === sanitized);
|
|
1125
1254
|
if (!bundled) {
|
|
@@ -282,6 +282,7 @@ const SubagentParamProperties = {
|
|
|
282
282
|
action: Type.Optional(Type.String({ minLength: 1,
|
|
283
283
|
description: "Optional management/control action. Use action='validate' with workflowScript or workflowScriptPath for offline checks. Omit this field for structured single-child or workflow execution; otherwise, use it only for management/control actions."
|
|
284
284
|
})),
|
|
285
|
+
capabilities: Type.Optional(Type.Boolean({ description: "For action='list', return compact capability rows and structured details without system prompts." })),
|
|
285
286
|
name: Type.Optional(Type.String({ description: "Human-readable name for action='schedule.create'." })),
|
|
286
287
|
id: Type.Optional(Type.String({
|
|
287
288
|
description: "Run id/prefix for status/debug.run, interrupt, steer, or mission.attach-run."
|
|
@@ -9,7 +9,9 @@ const EXTERNAL_CLI_RUNNER_GUIDANCE = "External CLI agents (codex-exec, codex-exe
|
|
|
9
9
|
const WORKFLOW_RESUME_KEY_GUIDANCE = "Each workflow key identifies one result lane: use a new stable workflow key for every distinct retained resume pass; same-key calls are reused only when launch parameters are identical, and incompatible parameters are rejected.";
|
|
10
10
|
const WORKFLOW_OUTPUT_BINDING_GUIDANCE = "For durable workflow child files, set output on runs.run/runs.all; task filename prose is not an output declaration, and return the child's outputReference, outputPathMapping, or artifactPaths instead of inventing a literal path.";
|
|
11
11
|
const WORKFLOW_LANES_GUIDANCE = "For bounded parallel sequential chains, use runs.lanes([{key,stages:[{key,agent,task},{key,resume:'previous',task},...]}]); first stages run together, later stages sequence per lane, and the bounded board reports lane-local failures. Only an explicit structuredOutput.verdict === 'blocked' blocks a successful stage; reviewer prose is not parsed.";
|
|
12
|
-
const
|
|
12
|
+
const WORKFLOW_SCRIPT_PORTABILITY_GUIDANCE = "workflowScript rejects nested async function, arrow, and method helpers; use top-level await, plain helper functions that return runs.run(...), or explicit Promise chains instead.";
|
|
13
|
+
const WORKFLOW_HOST_GUIDANCE = "For one non-interactive operator-owned command, await runs.host(key,{kind:'command',command,timeoutMs,output?,role?,provider?}). runs.host has no per-step cwd: commands and relative output paths use the workflow cwd; set cwd on the outer subagent request instead (for example, {cwd:'/path/to/worktree',workflowScript:'...'}), or put a trusted directory change in the command (for example, 'cd /path/to/worktree && npm test'). v1 supports only command steps; output is bounded and command failure fails the workflow.";
|
|
14
|
+
const AGENT_CAPABILITY_GUIDANCE = "For capability selection, use { action: \"list\", capabilities: true } for compact prompt-free rows.";
|
|
13
15
|
|
|
14
16
|
export const SUBAGENT_SAFETY_GUIDANCE = `SAFETY-CRITICAL SUBAGENT GUIDANCE:
|
|
15
17
|
• Use { action: "list" } before execution and only run executable/non-disabled agents.
|
|
@@ -66,6 +68,7 @@ ASYNC / SAFETY:
|
|
|
66
68
|
• Omitted async follows asyncByDefault config; set async:true explicitly when async behavior matters. Continue independent work only until its next dependency barrier; consume the result before work that depends on it. Do not sleep or poll merely to wait; use subagent_wait only when this turn must receive results.
|
|
67
69
|
• ${WORKFLOW_RESUME_KEY_GUIDANCE}
|
|
68
70
|
• ${WORKFLOW_OUTPUT_BINDING_GUIDANCE}
|
|
71
|
+
• ${WORKFLOW_HOST_GUIDANCE}
|
|
69
72
|
• Ordinary children are not orchestrators. Keep one writer per cwd/worktree and use fresh read-only reviewers for independent checks.
|
|
70
73
|
• Oracle/advisor consultations use available supervisor dialogue for material unknowns; request one-shot when desired.
|
|
71
74
|
• Status and artifacts live under asyncId/asyncDir with status.json, events.jsonl, output logs, and {action:"status",id:"..."}.`;
|
|
@@ -20,6 +20,7 @@ import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveE
|
|
|
20
20
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
21
21
|
import type { ContextMode } from "../shared/context-mode.ts";
|
|
22
22
|
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
23
|
+
import { backgroundProcessOptions } from "../shared/background-process-options.ts";
|
|
23
24
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
24
25
|
import { resolveNodeExecutable } from "../../shared/node-executable.ts";
|
|
25
26
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
@@ -562,9 +563,8 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
562
563
|
}
|
|
563
564
|
const proc = spawn(nodeCommand, [jitiCliPath, runner, cfgPath], {
|
|
564
565
|
cwd,
|
|
565
|
-
|
|
566
|
+
...backgroundProcessOptions(),
|
|
566
567
|
stdio: ["ignore", stdoutFd ?? "ignore", stderrFd ?? "ignore"],
|
|
567
|
-
windowsHide: true,
|
|
568
568
|
env: {
|
|
569
569
|
...omitExtensionBindingsEnv(process.env),
|
|
570
570
|
...(piPackageRoot ? { [SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}),
|
|
@@ -146,6 +146,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
146
146
|
chainStepCount: run.chainStepCount,
|
|
147
147
|
parallelGroups: groups,
|
|
148
148
|
hostSteps: run.hostSteps,
|
|
149
|
+
...(run.mode === "workflow" && run.workflowGraph ? { workflowGraph: run.workflowGraph } : {}),
|
|
149
150
|
preflight: run.preflight,
|
|
150
151
|
steps: visibleSteps,
|
|
151
152
|
stepsTotal: visibleSteps.length,
|
|
@@ -401,6 +402,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
401
402
|
job.workflowKey = status.workflowKey ?? job.workflowKey;
|
|
402
403
|
job.lane = status.lane ?? job.lane;
|
|
403
404
|
job.workflow = status.workflow ?? job.workflow;
|
|
405
|
+
if (status.mode === "workflow") job.workflowGraph = status.workflowGraph ?? job.workflowGraph;
|
|
404
406
|
job.hostSteps = validHostStepNodes(status.workflowGraph);
|
|
405
407
|
const workflowChildren = parseWorkflowChildSummary(status.workflowChildren);
|
|
406
408
|
if (workflowChildren && workflowChildren.workflowRunId !== status.runId) throw new Error("workflowChildren.workflowRunId does not match async status runId.");
|
|
@@ -627,6 +629,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
627
629
|
turnBudget: info.turnBudget,
|
|
628
630
|
parentWorkflowRunId: info.parentWorkflowRunId,
|
|
629
631
|
workflowKey: info.workflowKey,
|
|
632
|
+
...(info.mode === "workflow" && info.workflowGraph ? { workflowGraph: info.workflowGraph } : {}),
|
|
630
633
|
controlEventCursor: 0,
|
|
631
634
|
});
|
|
632
635
|
const job = state.asyncJobs.get(info.id)!;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../../shared/formatters.ts";
|
|
4
|
+
import { previewDisplayText } from "../../shared/display-text.ts";
|
|
4
5
|
import { formatActivityLabel, formatParallelOutcome } from "../../shared/status-format.ts";
|
|
5
|
-
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type Details, type HostStepNodeV1, type HostStepState, type LaunchResolvedChildExtensionsV1, type RuntimeAcknowledgedChildExtensionsV1, type NestedRunSummary, type SteeringStatus, type SubagentRunMode, type TokenUsage, type TurnBudgetState, type UsageBudgetState, type WorkflowPreflightV1 } from "../../shared/types.ts";
|
|
6
|
+
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type Details, type HostStepNodeV1, type HostStepState, type LaunchResolvedChildExtensionsV1, type RuntimeAcknowledgedChildExtensionsV1, type NestedRunSummary, type SteeringStatus, type SubagentRunMode, type TimeoutRecoveryProjection, type TokenUsage, type TurnBudgetState, type UsageBudgetState, type WorkflowPreflightV1, type WorkflowGraphSnapshot } from "../../shared/types.ts";
|
|
6
7
|
import type { ResolvedSubagentCapabilityCeiling, SubagentCapabilityAudit } from "../shared/capability-ceiling.ts";
|
|
7
8
|
import { readStatus } from "../../shared/utils.ts";
|
|
8
9
|
import { attachRootChildrenToSteps, buildNestedRouteIndex, findNestedRouteForRootId, type NestedRoute, projectNestedEvents } from "../shared/nested-events.ts";
|
|
@@ -21,6 +22,8 @@ import { assertWorkflowGraphHostSteps, hostStepReportName, hostStepVerdictLabel,
|
|
|
21
22
|
import { projectAsyncWorkflowRows } from "../shared/async-status-projection.ts";
|
|
22
23
|
import { validateAsyncStatusLaneMetadata } from "../shared/lane-metadata.ts";
|
|
23
24
|
import { formatWorkflowPreflightPlanSummary, formatWorkflowPreflightWarningSummary } from "../../workflows/workflow-preflight.ts";
|
|
25
|
+
import { workflowGraphStageNodes } from "../shared/workflow-graph.ts";
|
|
26
|
+
import { formatTimeoutRecoveryLines, projectTimeoutRecovery } from "../shared/mutation-evidence.ts";
|
|
24
27
|
|
|
25
28
|
interface AsyncRunStepSummary {
|
|
26
29
|
index: number;
|
|
@@ -75,6 +78,7 @@ interface AsyncRunStepSummary {
|
|
|
75
78
|
review?: AsyncJobStep["review"];
|
|
76
79
|
effects?: AsyncJobStep["effects"];
|
|
77
80
|
processTerminal?: AsyncJobStep["processTerminal"];
|
|
81
|
+
timeoutRecovery?: TimeoutRecoveryProjection;
|
|
78
82
|
launchResolvedExtensions?: LaunchResolvedChildExtensionsV1;
|
|
79
83
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
80
84
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
@@ -116,6 +120,7 @@ export interface AsyncRunSummary {
|
|
|
116
120
|
pendingAppends?: number;
|
|
117
121
|
parallelGroups?: AsyncParallelGroupStatus[];
|
|
118
122
|
hostSteps?: HostStepNodeV1[];
|
|
123
|
+
workflowGraph?: AsyncStatus["workflowGraph"];
|
|
119
124
|
steps: AsyncRunStepSummary[];
|
|
120
125
|
sessionDir?: string;
|
|
121
126
|
outputFile?: string;
|
|
@@ -282,6 +287,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
282
287
|
const summarizedSteps = steps.map((step, index) => {
|
|
283
288
|
const stepActivityState = step.activityState;
|
|
284
289
|
const stepLastActivityAt = step.lastActivityAt;
|
|
290
|
+
const timeoutRecovery = projectTimeoutRecovery(step.timeoutRecovery);
|
|
285
291
|
return {
|
|
286
292
|
index,
|
|
287
293
|
childId: asyncStatusChildIdentity(step, index),
|
|
@@ -341,6 +347,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
341
347
|
...(step.effects ? { effects: step.effects } : {}),
|
|
342
348
|
...(step.watchdog ? { watchdog: step.watchdog } : {}),
|
|
343
349
|
...(step.processTerminal ? { processTerminal: sanitizeProcessTerminal(step.processTerminal, { runId: status.runId, runnerProcessInstanceId: step.processTerminal.runnerProcessInstanceId }, `${path.join(asyncDir, "status.json")} step ${index}`) } : {}),
|
|
350
|
+
...(timeoutRecovery ? { timeoutRecovery } : {}),
|
|
344
351
|
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
345
352
|
...(step.capabilityAudit ? { capabilityAudit: step.capabilityAudit } : {}),
|
|
346
353
|
...(step.children?.length ? { children: step.children } : {}),
|
|
@@ -381,6 +388,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
381
388
|
...(status.pendingAppends !== undefined ? { pendingAppends: status.pendingAppends } : {}),
|
|
382
389
|
...(parallelGroups.length ? { parallelGroups } : {}),
|
|
383
390
|
...(hostSteps.length ? { hostSteps } : {}),
|
|
391
|
+
...(status.mode === "workflow" && status.workflowGraph ? { workflowGraph: status.workflowGraph } : {}),
|
|
384
392
|
steps: summarizedSteps,
|
|
385
393
|
...(nestedChildren.length ? { nestedChildren } : {}),
|
|
386
394
|
...(nestedWarnings.length ? { nestedWarnings } : {}),
|
|
@@ -575,12 +583,41 @@ function formatHostStepLine(row: ReturnType<typeof projectAsyncWorkflowRows>[num
|
|
|
575
583
|
return `host ${row.kind}: ${row.name} | ${state}${details.length ? ` | ${details.join(" | ")}` : ""}`;
|
|
576
584
|
}
|
|
577
585
|
|
|
586
|
+
function workflowStageStateLabel(status: WorkflowGraphSnapshot["nodes"][number]["status"]): string {
|
|
587
|
+
switch (status) {
|
|
588
|
+
case "completed":
|
|
589
|
+
return "complete";
|
|
590
|
+
case "detached":
|
|
591
|
+
return "paused";
|
|
592
|
+
default:
|
|
593
|
+
return status;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export function formatWorkflowStageLine(node: WorkflowGraphSnapshot["nodes"][number], index: number, total: number): string {
|
|
598
|
+
const state = workflowStageStateLabel(node.status);
|
|
599
|
+
const id = previewDisplayText(node.id, 160);
|
|
600
|
+
const label = node.label ? previewDisplayText(node.label, 160) : "";
|
|
601
|
+
const display = label && label !== id ? ` | ${label}` : "";
|
|
602
|
+
const agent = node.agent ? ` | ${previewDisplayText(node.agent, 80)}` : "";
|
|
603
|
+
const error = node.error ? ` | ${previewDisplayText(node.error, 240)}` : "";
|
|
604
|
+
return `stage ${index + 1}/${total}: ${id}${display}${agent} | ${state}${error}`;
|
|
605
|
+
}
|
|
606
|
+
|
|
578
607
|
export function formatAsyncRunOutputPath(run: Pick<AsyncRunSummary, "asyncDir" | "outputFile">): string | undefined {
|
|
579
608
|
if (!run.outputFile) return undefined;
|
|
580
609
|
return path.isAbsolute(run.outputFile) ? run.outputFile : path.join(run.asyncDir, run.outputFile);
|
|
581
610
|
}
|
|
582
611
|
|
|
583
|
-
export function formatAsyncRunProgressLabel(run: Pick<AsyncRunSummary, "mode" | "state" | "currentStep" | "chainStepCount" | "parallelGroups" | "steps">): string {
|
|
612
|
+
export function formatAsyncRunProgressLabel(run: Pick<AsyncRunSummary, "mode" | "state" | "currentStep" | "chainStepCount" | "parallelGroups" | "steps"> & { workflowGraph?: WorkflowGraphSnapshot }): string {
|
|
613
|
+
const graphStages = run.mode === "workflow" ? workflowGraphStageNodes(run.workflowGraph) : [];
|
|
614
|
+
if (graphStages.length > 0) {
|
|
615
|
+
const currentNode = graphStages.find((node) => node.id === run.workflowGraph?.currentNodeId);
|
|
616
|
+
if (currentNode && currentNode.status !== "completed") return `stage ${graphStages.indexOf(currentNode) + 1}/${graphStages.length}`;
|
|
617
|
+
const activeNode = graphStages.find((node) => node.status === "running") ?? graphStages.find((node) => node.status !== "completed");
|
|
618
|
+
if (activeNode) return `stage ${graphStages.indexOf(activeNode) + 1}/${graphStages.length}`;
|
|
619
|
+
return `stage ${graphStages.length}/${graphStages.length}`;
|
|
620
|
+
}
|
|
584
621
|
const stepCount = run.steps.length || 1;
|
|
585
622
|
const chainStepCount = run.chainStepCount ?? stepCount;
|
|
586
623
|
const groups = normalizeParallelGroups(run.parallelGroups, run.steps.length, chainStepCount);
|
|
@@ -622,8 +659,14 @@ export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active as
|
|
|
622
659
|
if (preflightWarning) lines.push(preflightWarning);
|
|
623
660
|
for (const step of run.steps) {
|
|
624
661
|
lines.push(` ${formatStepLine(step)}`);
|
|
662
|
+
lines.push(...formatTimeoutRecoveryLines(step.timeoutRecovery, " "));
|
|
625
663
|
lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", maxLines: 12 }));
|
|
626
664
|
}
|
|
665
|
+
const loadedWorkflowKeys = new Set(run.steps.flatMap((step) => step.workflowKey ? [step.workflowKey] : []));
|
|
666
|
+
const graphStages = run.mode === "workflow" ? workflowGraphStageNodes(run.workflowGraph) : [];
|
|
667
|
+
for (const [index, node] of graphStages.entries()) {
|
|
668
|
+
if (!loadedWorkflowKeys.has(node.id)) lines.push(` ${formatWorkflowStageLine(node, index, graphStages.length)}`);
|
|
669
|
+
}
|
|
627
670
|
for (const row of projectAsyncWorkflowRows([], run.hostSteps)) {
|
|
628
671
|
const line = formatHostStepLine(row);
|
|
629
672
|
if (line) lines.push(` ${line}`);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-OS control channel for async subagent runs.
|
|
3
3
|
*
|
|
4
|
-
* Background runs
|
|
5
|
-
*
|
|
4
|
+
* Background runs use child processes. Unix detaches them from the parent process.
|
|
5
|
+
* The original control path delivered an interrupt with
|
|
6
|
+
* `process.kill(pid, SIGUSR2|SIGBREAK)`, but Windows cannot
|
|
6
7
|
* deliver those signals cross-process via `process.kill` and throws `ENOSYS`,
|
|
7
8
|
* which left async runs uninterruptible (no stop, no live steer) on Windows.
|
|
8
9
|
*
|
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
4
4
|
import { safeTerminalText } from "../../shared/display-text.ts";
|
|
5
|
-
import { formatAsyncRunList, formatAsyncRunOutputPath, formatAsyncRunProgressLabel, listAsyncRuns } from "./async-status.ts";
|
|
5
|
+
import { formatAsyncRunList, formatAsyncRunOutputPath, formatAsyncRunProgressLabel, formatWorkflowStageLine, listAsyncRuns } from "./async-status.ts";
|
|
6
6
|
import { formatAsyncResultTranscript, formatAsyncRunTranscript, formatNestedRunTranscript, inspectSubagentFleet } from "./fleet-view.ts";
|
|
7
7
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
8
8
|
import { formatModelThinking } from "../../shared/formatters.ts";
|
|
@@ -25,7 +25,9 @@ import { formatWorkflowJsonPreview } from "../../workflows/scripted-workflow.ts"
|
|
|
25
25
|
import { parseWorkflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
26
26
|
import { formatWorkflowPreflightPlanSummary, formatWorkflowPreflightWarningSummary } from "../../workflows/workflow-preflight.ts";
|
|
27
27
|
import { formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
|
|
28
|
+
import { workflowGraphStageNodes } from "../shared/workflow-graph.ts";
|
|
28
29
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
30
|
+
import { formatTimeoutRecoveryLines } from "../shared/mutation-evidence.ts";
|
|
29
31
|
|
|
30
32
|
interface RunStatusParams {
|
|
31
33
|
action?: string;
|
|
@@ -481,6 +483,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
481
483
|
currentStep: status.currentStep,
|
|
482
484
|
chainStepCount: status.chainStepCount,
|
|
483
485
|
parallelGroups: status.parallelGroups,
|
|
486
|
+
workflowGraph: status.workflowGraph,
|
|
484
487
|
steps: (status.steps ?? []).map((step, index) => ({ index, agent: step.agent, status: step.status })),
|
|
485
488
|
});
|
|
486
489
|
const started = new Date(status.startedAt).toISOString();
|
|
@@ -548,6 +551,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
548
551
|
const display = runStatusStepDisplayName(step);
|
|
549
552
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
550
553
|
lines.push(`${stepLineLabel(status, index)}: ${phase}${display} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${steeringSuffix}${acceptanceText}${budgetText}${errorText}`);
|
|
554
|
+
lines.push(...formatTimeoutRecoveryLines(step.timeoutRecovery, " "));
|
|
551
555
|
if (step.runner?.type === "external-cli") {
|
|
552
556
|
const runner = normalizeExternalCliRunnerStatus(step.runner);
|
|
553
557
|
if (runner) {
|
|
@@ -593,6 +597,11 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
593
597
|
lines.push(" Steer: unavailable; external runners do not accept live messages.");
|
|
594
598
|
}
|
|
595
599
|
}
|
|
600
|
+
const loadedWorkflowKeys = new Set((status.steps ?? []).flatMap((step) => step.workflowKey ? [step.workflowKey] : []));
|
|
601
|
+
const graphStages = status.mode === "workflow" ? workflowGraphStageNodes(status.workflowGraph) : [];
|
|
602
|
+
for (const [index, node] of graphStages.entries()) {
|
|
603
|
+
if (!loadedWorkflowKeys.has(node.id)) lines.push(` ${formatWorkflowStageLine(node, index, graphStages.length)}`);
|
|
604
|
+
}
|
|
596
605
|
const attached = new Set((status.steps ?? []).flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
597
606
|
const unattached = nestedChildren.filter((child) => !attached.has(child.id));
|
|
598
607
|
lines.push(...formatNestedRunStatusLines(unattached, { indent: "", commandHints: true, maxLines: 20 }));
|
|
@@ -632,7 +641,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
632
641
|
}
|
|
633
642
|
try {
|
|
634
643
|
const raw = fs.readFileSync(resultPath, "utf-8");
|
|
635
|
-
const data = JSON.parse(raw) as { id?: string; runId?: string; toolCallId?: string; agent?: string; success?: boolean; summary?: string; output?: string; exitCode?: number; state?: string; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; processSignal?: string | null; sessionFile?: string; parallelHandoff?: { path?: string }; results?: Array<{ agent?: string; sessionName?: string; runId?: string; workflowKey?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; interrupted?: boolean; processSignal?: string | null }> };
|
|
644
|
+
const data = JSON.parse(raw) as { id?: string; runId?: string; toolCallId?: string; agent?: string; success?: boolean; summary?: string; output?: string; exitCode?: number; state?: string; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; processSignal?: string | null; sessionFile?: string; timeoutRecovery?: unknown; parallelHandoff?: { path?: string }; results?: Array<{ agent?: string; sessionName?: string; runId?: string; workflowKey?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; interrupted?: boolean; processSignal?: string | null; timeoutRecovery?: unknown }> };
|
|
636
645
|
if (params.view === "transcript") {
|
|
637
646
|
try {
|
|
638
647
|
return { content: [{ type: "text", text: formatAsyncResultTranscript(data, resultPath, { index: params.index, lines: params.lines }) }], details: { mode: "single", results: [] } };
|
|
@@ -660,6 +669,8 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
660
669
|
const lines = [`Run: ${runId}`, data.toolCallId ? `Tool call: ${data.toolCallId}` : undefined, `State: ${status}`, `Result: ${resultPath}`].filter((line): line is string => Boolean(line));
|
|
661
670
|
if (data.parallelHandoff?.path) lines.push(`Parallel handoff: ${data.parallelHandoff.path}`);
|
|
662
671
|
const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, sessionFile: data.sessionFile }] : [];
|
|
672
|
+
lines.push(...formatTimeoutRecoveryLines(data.timeoutRecovery, " "));
|
|
673
|
+
for (const child of children) lines.push(...formatTimeoutRecoveryLines(child.timeoutRecovery, " "));
|
|
663
674
|
lines.push(formatResumeGuidance(runId, children, data.sessionFile, { stopped: status === "stopped" }));
|
|
664
675
|
if (data.summary) lines.push("", data.summary);
|
|
665
676
|
const workflowChildren = parseWorkflowChildSummary((data as unknown as Record<string, unknown>).workflowChildren);
|
|
@@ -75,7 +75,7 @@ import {
|
|
|
75
75
|
DEFAULT_GLOBAL_CONCURRENCY_LIMIT,
|
|
76
76
|
Semaphore,
|
|
77
77
|
} from "../shared/parallel-utils.ts";
|
|
78
|
-
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
78
|
+
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, deriveForkPromptCacheKey, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
79
79
|
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
80
80
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
81
81
|
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
@@ -1641,6 +1641,7 @@ async function runSingleStepInner(
|
|
|
1641
1641
|
let writerAttemptCount = 0;
|
|
1642
1642
|
const attemptNotes: string[] = [];
|
|
1643
1643
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
1644
|
+
let finalRequiredOutputMissing: boolean | undefined;
|
|
1644
1645
|
let finalResult: RunPiStreamingResult | undefined;
|
|
1645
1646
|
let finalOutputSnapshot: SingleOutputSnapshot | undefined;
|
|
1646
1647
|
let structuredAcceptanceReport: unknown;
|
|
@@ -1698,6 +1699,7 @@ async function runSingleStepInner(
|
|
|
1698
1699
|
: undefined;
|
|
1699
1700
|
const { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: attemptCapabilityAudit, warnings } = buildPiArgs(omitUndefinedProperties({
|
|
1700
1701
|
parentSessionId: step.parentSessionId,
|
|
1702
|
+
forkCacheKey: step.context === "fork" ? deriveForkPromptCacheKey(step.parentSessionId) : undefined,
|
|
1701
1703
|
baseArgs: ["--mode", "json", "-p"],
|
|
1702
1704
|
task: attemptTask,
|
|
1703
1705
|
taskDelivery: taskDeliveryOverride,
|
|
@@ -1930,6 +1932,7 @@ async function runSingleStepInner(
|
|
|
1930
1932
|
? { kind: "structured" as const, path: effectiveStructuredOutput.outputPath, missing: !fs.existsSync(effectiveStructuredOutput.outputPath) }
|
|
1931
1933
|
: undefined;
|
|
1932
1934
|
const missingRequiredOutputError = formatRequiredOutputError(requiredOutput);
|
|
1935
|
+
finalRequiredOutputMissing = requiredOutput?.missing;
|
|
1933
1936
|
const missingRequiredOutputAfterMutation = Boolean(missingRequiredOutputError) && (mutationAttemptObserved || Boolean(mutationEvidence.changedFiles.length));
|
|
1934
1937
|
const effectiveExitCode = toolAvailabilityError || completionEvidence.legacyFailureError || midToolExitError || structuredError || emptyOutputError || missingRequiredOutputError
|
|
1935
1938
|
? 1
|
|
@@ -2117,6 +2120,7 @@ async function runSingleStepInner(
|
|
|
2117
2120
|
? buildTimeoutRecoverySummary({
|
|
2118
2121
|
termination: "timed-out",
|
|
2119
2122
|
evidence: finalMutationEvidence,
|
|
2123
|
+
requiredOutputMissing: finalRequiredOutputMissing,
|
|
2120
2124
|
currentTool: finalResult?.currentTool,
|
|
2121
2125
|
currentToolArgs: finalResult?.currentToolArgs,
|
|
2122
2126
|
currentPath: finalResult?.currentPath,
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
type WaitCompletion,
|
|
63
63
|
} from "../../shared/types.ts";
|
|
64
64
|
import { formatDuration, shortenPath } from "../../shared/formatters.ts";
|
|
65
|
+
import { formatTimeoutRecoveryLines } from "../shared/mutation-evidence.ts";
|
|
65
66
|
import { toAgentToolUsage } from "../../shared/utils.ts";
|
|
66
67
|
import { collectWaitCompletions } from "./wait-completions.ts";
|
|
67
68
|
import { formatResumeFirstFailedRunsNote } from "./resume-guidance.ts";
|
|
@@ -349,6 +350,12 @@ function result(text: string, isError = false, completions?: WaitCompletion[]):
|
|
|
349
350
|
};
|
|
350
351
|
}
|
|
351
352
|
|
|
353
|
+
function formatCompletionRecovery(completions: WaitCompletion[] | undefined): string {
|
|
354
|
+
const lines = (completions ?? []).flatMap((completion) =>
|
|
355
|
+
(completion.results ?? []).flatMap((child) => formatTimeoutRecoveryLines(child.timeoutRecovery)));
|
|
356
|
+
return lines.length > 0 ? `\n${lines.join("\n")}` : "";
|
|
357
|
+
}
|
|
358
|
+
|
|
352
359
|
function windowElapsedResult(
|
|
353
360
|
text: string,
|
|
354
361
|
activeRunIds: string[],
|
|
@@ -685,6 +692,7 @@ export async function waitForSubagents(
|
|
|
685
692
|
+ providerActive.filter((item) => initialProviderIds.has(backgroundWorkIdentity(item))).length;
|
|
686
693
|
const elapsed = formatDuration(now() - startedAt);
|
|
687
694
|
const outcome = terminalSummary ? ` Outcome: ${terminalSummary}.` : "";
|
|
695
|
+
const recoveryNote = formatCompletionRecovery(completions);
|
|
688
696
|
|
|
689
697
|
if (waitForAll) {
|
|
690
698
|
const scope = params.id
|
|
@@ -694,7 +702,7 @@ export async function waitForSubagents(
|
|
|
694
702
|
: `${initialAsyncIds.size} async run(s) and ${initialProviderIds.size} provider item(s)`;
|
|
695
703
|
const status = relevantAttention.length > 0 ? "attention required" : "done";
|
|
696
704
|
return result(
|
|
697
|
-
`Waited ${elapsed} for ${scope}; ${status}.${outcome}${resumeGuidance}${attentionNote} Completion/control events have been observed; inspect status if a notification is not visible yet.`,
|
|
705
|
+
`Waited ${elapsed} for ${scope}; ${status}.${outcome}${recoveryNote}${resumeGuidance}${attentionNote} Completion/control events have been observed; inspect status if a notification is not visible yet.`,
|
|
698
706
|
(deps.failOnFailedRuns === true && failedAsyncCount > 0) || (deps.failOnAttention === true && relevantAttention.length > 0),
|
|
699
707
|
completions,
|
|
700
708
|
);
|
|
@@ -711,7 +719,7 @@ export async function waitForSubagents(
|
|
|
711
719
|
? `${relevantAttention.length} of ${initialCount} ${subject} need attention`
|
|
712
720
|
: `${finishedCount} of ${initialCount} ${subject} finished`;
|
|
713
721
|
return result(
|
|
714
|
-
`Waited ${elapsed}; ${progress}.${outcome}${resumeGuidance}${attentionNote}${remainder} Relevant completion/control events have been observed; inspect status if a notification is not visible yet.`,
|
|
722
|
+
`Waited ${elapsed}; ${progress}.${outcome}${recoveryNote}${resumeGuidance}${attentionNote}${remainder} Relevant completion/control events have been observed; inspect status if a notification is not visible yet.`,
|
|
715
723
|
(deps.failOnFailedRuns === true && failedAsyncCount > 0) || (deps.failOnAttention === true && relevantAttention.length > 0),
|
|
716
724
|
completions,
|
|
717
725
|
);
|