@selesai/code 0.5.29 → 0.6.2
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 +34 -0
- package/README.md +1 -1
- package/dist/config.d.ts +16 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +106 -5
- package/dist/config.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +18 -0
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/system-prompt.test.d.ts +2 -0
- package/dist/core/system-prompt.test.d.ts.map +1 -0
- package/dist/core/system-prompt.test.js +89 -0
- package/dist/core/system-prompt.test.js.map +1 -0
- package/dist/defaults/models.json +13 -45
- package/dist/defaults/settings.json +5 -7
- package/dist/extensions/copy-turn.test.ts +131 -0
- package/dist/extensions/copy-turn.ts +6 -1
- package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/dist/extensions/package.json +0 -1
- package/dist/extensions/pi-subagents/CHANGELOG.md +3 -0
- package/dist/extensions/pi-subagents/README.md +27 -32
- package/dist/extensions/pi-subagents/agents/architect.md +4 -4
- package/dist/extensions/pi-subagents/agents/builder.md +5 -4
- package/dist/extensions/pi-subagents/agents/commentator.md +3 -2
- package/dist/extensions/pi-subagents/agents/explorer.md +3 -2
- package/dist/extensions/pi-subagents/agents/recapper.md +3 -2
- package/dist/extensions/pi-subagents/agents/researcher.md +4 -3
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +10 -9
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +12 -11
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +10 -11
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +56 -9
- package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +125 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +1 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +5 -1
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +2 -2
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +24 -7
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +23 -5
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +27 -1
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +64 -6
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +16 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +72 -18
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +19 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +127 -31
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +4 -6
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +63 -9
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +41 -2
- package/dist/extensions/pi-subagents/src/shared/utils.ts +29 -1
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +5 -1
- package/dist/extensions/pi-subagents/src/tui/render.ts +32 -6
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +111 -6
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +74 -43
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +36 -21
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +5 -3
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -8
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +14 -7
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +227 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +81 -5
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +49 -10
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +18 -2
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -6
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +161 -1
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +63 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +6 -1
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +29 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +12 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +91 -1
- package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +213 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +23 -1
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +60 -9
- package/dist/extensions/pi-web-agent/package.json +1 -1
- package/dist/skills/pi-subagents/SKILL.md +43 -0
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +257 -0
- package/dist/skills/pi-subagents/references/execution-controls.md +431 -0
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +281 -0
- package/dist/skills/ponytail/SKILL.md +1 -3
- package/docs/plans/subagent-delegation/phase-0-correctness.md +265 -0
- package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +486 -0
- package/docs/plans/subagent-delegation/phase-2-context-controls.md +282 -0
- package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +362 -0
- package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +381 -0
- package/package.json +2 -2
- package/dist/extensions/caveman/caveman-instructions.cjs +0 -11
- package/dist/extensions/caveman/index.js +0 -118
- package/dist/extensions/caveman/package.json +0 -8
- package/dist/extensions/caveman/test/extension.test.js +0 -203
- package/dist/extensions/caveman/test/helpers.test.js +0 -58
- package/dist/skills/caveman/SKILL.md +0 -50
|
@@ -25,7 +25,6 @@ import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/
|
|
|
25
25
|
import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
|
|
26
26
|
import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
|
|
27
27
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
28
|
-
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
29
28
|
import { recordRun } from "../shared/run-history.ts";
|
|
30
29
|
import {
|
|
31
30
|
buildChainInstructions,
|
|
@@ -57,7 +56,7 @@ import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
|
57
56
|
import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
|
|
58
57
|
import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
59
58
|
import { isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
60
|
-
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
59
|
+
import { finalizeSingleOutput, formatBoundedPersistenceFallback, formatSavedOutputReference, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
61
60
|
import { cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
62
61
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
|
|
63
62
|
import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
|
|
@@ -521,7 +520,12 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
521
520
|
updatedAt,
|
|
522
521
|
...(input.result.exitCode !== undefined ? { exitCode: input.result.exitCode } : {}),
|
|
523
522
|
...(input.result.error ? { error: input.result.error } : {}),
|
|
524
|
-
|
|
523
|
+
// Reference-first remembered state: when an authoritative saved output path
|
|
524
|
+
// exists, do not retain the full final output in memory; status/transcript
|
|
525
|
+
// views recover it from `savedOutputPath` via `rememberedForegroundChildOutput`
|
|
526
|
+
// (runs/background/run-status.ts) and the TUI via `resultOutputForUi`
|
|
527
|
+
// (tui/render.ts).
|
|
528
|
+
...(input.result.finalOutput && !input.result.savedOutputPath ? { finalOutput: input.result.finalOutput } : {}),
|
|
525
529
|
outputState: input.result.outputState,
|
|
526
530
|
outputMode: input.result.outputMode,
|
|
527
531
|
savedOutputPath: input.result.savedOutputPath,
|
|
@@ -541,10 +545,11 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
|
|
|
541
545
|
...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
|
|
542
546
|
};
|
|
543
547
|
trimRememberedForegroundRuns(state);
|
|
544
|
-
const
|
|
548
|
+
const reference = input.result.outputReference?.message;
|
|
549
|
+
const output = reference ?? getSingleResultOutput(input.result).trim();
|
|
545
550
|
const success = terminalStatus === "completed";
|
|
546
551
|
const summary = !success && input.result.error
|
|
547
|
-
? `${input.result.error}${output ? `\n\
|
|
552
|
+
? `${input.result.error}${output ? `\n\n${output}` : ""}`
|
|
548
553
|
: output || input.result.error || "Detached child exited without final output.";
|
|
549
554
|
// A detached callback may outlive its extension runtime. Stale sessions are
|
|
550
555
|
// intentionally dropped rather than routed through a replacement runtime.
|
|
@@ -1288,7 +1293,7 @@ async function resumeAsyncRun(input: {
|
|
|
1288
1293
|
};
|
|
1289
1294
|
}
|
|
1290
1295
|
const runId = randomUUID().slice(0, 8);
|
|
1291
|
-
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts
|
|
1296
|
+
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts === true, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1292
1297
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1293
1298
|
const contextPolicy = resolveExplicitContextPolicy(input.params);
|
|
1294
1299
|
const workflowTask = (input.params.task ?? followUp) || undefined;
|
|
@@ -1354,7 +1359,7 @@ async function resumeAsyncRun(input: {
|
|
|
1354
1359
|
|
|
1355
1360
|
const runId = randomUUID().slice(0, 8);
|
|
1356
1361
|
const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(agentConfig, recoveryDescriptor) : agentConfig;
|
|
1357
|
-
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts
|
|
1362
|
+
const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts === true, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir };
|
|
1358
1363
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
1359
1364
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
1360
1365
|
const parentModel = input.parentModel;
|
|
@@ -1435,14 +1440,82 @@ async function resumeAsyncRun(input: {
|
|
|
1435
1440
|
}
|
|
1436
1441
|
|
|
1437
1442
|
function resultSummaryForIntercom(result: SingleResult): string {
|
|
1438
|
-
const
|
|
1439
|
-
if (
|
|
1440
|
-
return
|
|
1443
|
+
const reference = result.outputReference?.message;
|
|
1444
|
+
if (reference) {
|
|
1445
|
+
return result.exitCode !== 0 && result.error ? `${result.error}\n\n${reference}` : reference;
|
|
1441
1446
|
}
|
|
1442
|
-
|
|
1447
|
+
if (result.savedOutputPath) {
|
|
1448
|
+
try {
|
|
1449
|
+
const ref = formatSavedOutputReference(result.savedOutputPath, fs.readFileSync(result.savedOutputPath, "utf-8"));
|
|
1450
|
+
return result.exitCode !== 0 && result.error ? `${result.error}\n\n${ref.message}` : ref.message;
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
const fallback = formatBoundedPersistenceFallback({
|
|
1453
|
+
error: `Failed to read saved output: ${error instanceof Error ? error.message : String(error)}`,
|
|
1454
|
+
outputPath: result.savedOutputPath,
|
|
1455
|
+
fullOutput: getSingleResultOutput(result),
|
|
1456
|
+
exitCode: result.exitCode,
|
|
1457
|
+
processError: result.error,
|
|
1458
|
+
});
|
|
1459
|
+
return result.exitCode !== 0 && result.error ? `${result.error}\n\n${fallback}` : fallback;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
if (result.outputSaveError) {
|
|
1463
|
+
return formatBoundedPersistenceFallback({
|
|
1464
|
+
error: result.outputSaveError,
|
|
1465
|
+
outputPath: result.savedOutputPath,
|
|
1466
|
+
fullOutput: getSingleResultOutput(result),
|
|
1467
|
+
exitCode: result.exitCode,
|
|
1468
|
+
processError: result.error,
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
// Legacy / `output: false` child without a durable saved path: bounded fallback
|
|
1472
|
+
// only, never the raw full output.
|
|
1473
|
+
return formatBoundedPersistenceFallback({
|
|
1474
|
+
error: "No saved output path is available for this child.",
|
|
1475
|
+
fullOutput: getSingleResultOutput(result),
|
|
1476
|
+
exitCode: result.exitCode,
|
|
1477
|
+
processError: result.error,
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Compact terminal per-child line for bridge-off foreground parallel content:
|
|
1483
|
+
* status plus either the saved-output reference (default/file-only), the legacy
|
|
1484
|
+
* inline full output when explicitly requested, or the bounded fallback.
|
|
1485
|
+
*/
|
|
1486
|
+
function formatParallelChildResult(result: SingleResult, index: number): string {
|
|
1487
|
+
const status = resolveSubagentResultStatus({
|
|
1488
|
+
exitCode: result.exitCode,
|
|
1489
|
+
interrupted: result.interrupted,
|
|
1490
|
+
detached: result.detached,
|
|
1491
|
+
processSignal: result.processSignal,
|
|
1492
|
+
timedOut: result.timedOut,
|
|
1493
|
+
stopped: result.stopped,
|
|
1494
|
+
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
1495
|
+
});
|
|
1496
|
+
const header = `=== Task ${index + 1}: ${result.agent} [${status}] ===`;
|
|
1497
|
+
if (result.outputMode === "inline") {
|
|
1498
|
+
const output = getSingleResultOutput(result).trim();
|
|
1499
|
+
return `${header}\n${output || "(no output)"}`;
|
|
1500
|
+
}
|
|
1501
|
+
const reference = result.outputReference?.message;
|
|
1502
|
+
if (reference) {
|
|
1503
|
+
const body = result.exitCode !== 0 && result.error ? `${result.error}\n${reference}` : reference;
|
|
1504
|
+
return `${header}\n${body}`;
|
|
1505
|
+
}
|
|
1506
|
+
return `${header}\n${formatBoundedPersistenceFallback({
|
|
1507
|
+
error: result.outputSaveError ?? "No saved output path is available for this child.",
|
|
1508
|
+
outputPath: result.savedOutputPath,
|
|
1509
|
+
fullOutput: getSingleResultOutput(result),
|
|
1510
|
+
exitCode: result.exitCode,
|
|
1511
|
+
processError: result.error,
|
|
1512
|
+
})}`;
|
|
1443
1513
|
}
|
|
1444
1514
|
|
|
1445
1515
|
function formatFailedSingleRunOutput(result: SingleResult, displayOutput: string): string {
|
|
1516
|
+
// Reference-first and persistence-failure fallbacks already carry error/status
|
|
1517
|
+
// and must not be re-wrapped as raw "Output:" text.
|
|
1518
|
+
if (result.savedOutputPath || result.outputSaveError) return displayOutput;
|
|
1446
1519
|
const error = result.error || "Failed";
|
|
1447
1520
|
const output = displayOutput.trim();
|
|
1448
1521
|
const lines = [error];
|
|
@@ -2287,7 +2360,6 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2287
2360
|
}
|
|
2288
2361
|
const rawOutput = params.output !== undefined ? params.output : a.output;
|
|
2289
2362
|
const effectiveOutput = normalizeSingleOutputOverride(rawOutput, a.output);
|
|
2290
|
-
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
2291
2363
|
const normalizedSkills = normalizeSkillInput(params.skill);
|
|
2292
2364
|
const skills = normalizedSkills === false ? [] : normalizedSkills;
|
|
2293
2365
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, a.maxSubagentDepth);
|
|
@@ -2309,7 +2381,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
2309
2381
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
2310
2382
|
skills,
|
|
2311
2383
|
output: effectiveOutput,
|
|
2312
|
-
outputMode:
|
|
2384
|
+
outputMode: params.outputMode,
|
|
2313
2385
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
2314
2386
|
modelOverride,
|
|
2315
2387
|
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
@@ -2610,6 +2682,19 @@ function resolveSingleRunOutputBaseDir(deps: ExecutorDeps, artifactsDir: string,
|
|
|
2610
2682
|
: path.join(artifactsDir, "outputs", runId);
|
|
2611
2683
|
}
|
|
2612
2684
|
|
|
2685
|
+
/**
|
|
2686
|
+
* Generated per-run durable output path for foreground single runs when the
|
|
2687
|
+
* caller omits `output` (and does not set `output: false`):
|
|
2688
|
+
* `<singleRunOutputBaseDir>/<runId>/result.md`. The base is the configured
|
|
2689
|
+
* `singleRunOutputBaseDir` when present, otherwise `{artifactsDir}/outputs`.
|
|
2690
|
+
*/
|
|
2691
|
+
function resolveGeneratedSingleOutputPath(deps: ExecutorDeps, artifactsDir: string, runId: string): string {
|
|
2692
|
+
const base = deps.config.singleRunOutputBaseDir
|
|
2693
|
+
? path.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir))
|
|
2694
|
+
: path.join(artifactsDir, "outputs");
|
|
2695
|
+
return path.join(base, runId, "result.md");
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2613
2698
|
function buildChainWorktreeTaskCwdError(chain: ChainStep[], sharedCwd: string): string | undefined {
|
|
2614
2699
|
for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
|
|
2615
2700
|
const step = chain[stepIndex]!;
|
|
@@ -3068,8 +3153,19 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3068
3153
|
|
|
3069
3154
|
const behaviors = tasks.map((task, index) => {
|
|
3070
3155
|
let behavior = suppressProgressForReadOnlyTask(resolveStepBehavior(agentConfigs[index]!, behaviorOverrides[index]!), taskTexts[index]);
|
|
3071
|
-
if (behaviorOverrides[index]?.output === undefined
|
|
3072
|
-
|
|
3156
|
+
if (behaviorOverrides[index]?.output === undefined) {
|
|
3157
|
+
if (typeof behavior.output === "string" && !path.isAbsolute(behavior.output)) {
|
|
3158
|
+
behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, behavior.output) };
|
|
3159
|
+
} else if (behavior.output === false && behaviorOverrides[index]?.output !== false && behaviorOverrides[index]?.output !== "false") {
|
|
3160
|
+
// Omitted output: generate a collision-safe per-task durable path in the
|
|
3161
|
+
// existing per-task namespace (parallel-0/<index>-<agent>/result.md).
|
|
3162
|
+
behavior = { ...behavior, output: path.join("parallel-0", `${index}-${task.agent}`, "result.md") };
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
// Omitted outputMode resolves to file-only whenever an output path is active;
|
|
3166
|
+
// explicit `outputMode: "inline"` is preserved exactly.
|
|
3167
|
+
if (behaviorOverrides[index]?.outputMode === undefined && typeof behavior.output === "string") {
|
|
3168
|
+
behavior = { ...behavior, outputMode: "file-only" };
|
|
3073
3169
|
}
|
|
3074
3170
|
return behavior;
|
|
3075
3171
|
});
|
|
@@ -3235,21 +3331,12 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
3235
3331
|
const worktreeSuffix = handoff?.suffix ?? "";
|
|
3236
3332
|
const ok = results.filter((result) => result.exitCode === 0).length;
|
|
3237
3333
|
const downgradeNote = backgroundRequestedWhileClarifying ? " (background requested, but clarify kept this run foreground)" : "";
|
|
3238
|
-
const
|
|
3239
|
-
results.map((result) => ({
|
|
3240
|
-
agent: result.agent,
|
|
3241
|
-
output: result.truncation?.text || getSingleResultOutput(result),
|
|
3242
|
-
exitCode: result.exitCode,
|
|
3243
|
-
error: result.error,
|
|
3244
|
-
timedOut: result.timedOut,
|
|
3245
|
-
})),
|
|
3246
|
-
(i, agent) => `=== Task ${i + 1}: ${agent} ===`,
|
|
3247
|
-
);
|
|
3334
|
+
const perChildResults = results.map((result, i) => formatParallelChildResult(result, i)).join("\n\n");
|
|
3248
3335
|
|
|
3249
3336
|
const summary = `${ok}/${results.length} succeeded${downgradeNote}`;
|
|
3250
3337
|
const fullContent = worktreeSuffix
|
|
3251
|
-
? `${summary}\n\n${
|
|
3252
|
-
: `${summary}\n\n${
|
|
3338
|
+
? `${summary}\n\n${perChildResults}\n\n${worktreeSuffix}`
|
|
3339
|
+
: `${summary}\n\n${perChildResults}`;
|
|
3253
3340
|
|
|
3254
3341
|
return {
|
|
3255
3342
|
content: [{ type: "text", text: fullContent }],
|
|
@@ -3309,7 +3396,6 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3309
3396
|
let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
|
|
3310
3397
|
const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
|
|
3311
3398
|
let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
|
|
3312
|
-
const effectiveOutputMode = params.outputMode ?? "inline";
|
|
3313
3399
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
3314
3400
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
|
|
3315
3401
|
|
|
@@ -3381,7 +3467,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3381
3467
|
context: contextPolicy.contextForAgent(params.agent!),
|
|
3382
3468
|
skills: skillOverride === false ? [] : skillOverride,
|
|
3383
3469
|
output: effectiveOutput,
|
|
3384
|
-
outputMode:
|
|
3470
|
+
outputMode: params.outputMode,
|
|
3385
3471
|
outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
|
|
3386
3472
|
modelOverride,
|
|
3387
3473
|
thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
|
|
@@ -3410,7 +3496,16 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3410
3496
|
task = wrapForkTask(task);
|
|
3411
3497
|
}
|
|
3412
3498
|
const cleanTask = task;
|
|
3413
|
-
|
|
3499
|
+
// Omitted output resolves to a generated per-run durable path
|
|
3500
|
+
// (`<singleRunOutputBaseDir>/<runId>/result.md`); omitted outputMode resolves to
|
|
3501
|
+
// file-only whenever an output path is active. Explicit paths, `output: false`,
|
|
3502
|
+
// and explicit `outputMode: "inline"` are preserved exactly.
|
|
3503
|
+
const outputPath = typeof effectiveOutput === "string"
|
|
3504
|
+
? resolveSingleOutputPath(effectiveOutput, ctx.cwd, effectiveCwd, resolveSingleRunOutputBaseDir(deps, artifactsDir, runId))
|
|
3505
|
+
: effectiveOutput === false
|
|
3506
|
+
? undefined
|
|
3507
|
+
: resolveGeneratedSingleOutputPath(deps, artifactsDir, runId);
|
|
3508
|
+
const effectiveOutputMode = params.outputMode ?? (outputPath ? "file-only" : "inline");
|
|
3414
3509
|
const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Single run (${params.agent})`);
|
|
3415
3510
|
if (validationError) {
|
|
3416
3511
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
|
|
@@ -3549,6 +3644,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
3549
3644
|
savedPath: r.savedOutputPath,
|
|
3550
3645
|
outputReference: r.outputReference,
|
|
3551
3646
|
saveError: r.outputSaveError,
|
|
3647
|
+
error: r.error,
|
|
3552
3648
|
});
|
|
3553
3649
|
if (foregroundControl) {
|
|
3554
3650
|
updateForegroundNestedProjection(foregroundControl);
|
|
@@ -4225,7 +4321,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
4225
4321
|
|
|
4226
4322
|
const artifactConfig: ArtifactConfig = {
|
|
4227
4323
|
...DEFAULT_ARTIFACT_CONFIG,
|
|
4228
|
-
enabled: effectiveParams.artifacts
|
|
4324
|
+
enabled: effectiveParams.artifacts === true,
|
|
4229
4325
|
dir: deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
4230
4326
|
};
|
|
4231
4327
|
const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
@@ -22,7 +22,7 @@ import type {
|
|
|
22
22
|
SubagentRunMode,
|
|
23
23
|
} from "../../shared/types.ts";
|
|
24
24
|
import { isAgentContractV1 } from "./agent-contract.ts";
|
|
25
|
-
import { classifyTaskMutationIntent, taskMayMutate } from "./task-intent.ts";
|
|
25
|
+
import { classifyTaskMutationIntent, resolveAgentRoutingRole, taskMayMutate } from "./task-intent.ts";
|
|
26
26
|
|
|
27
27
|
const LEVEL_RANK: Record<Exclude<AcceptanceLevel, "auto">, number> = {
|
|
28
28
|
none: 0,
|
|
@@ -82,7 +82,6 @@ function inferLevel(input: {
|
|
|
82
82
|
dynamic?: boolean;
|
|
83
83
|
dynamicGroup?: boolean;
|
|
84
84
|
}): { level: Exclude<AcceptanceLevel, "auto">; reasons: string[]; criteria: string[]; evidence: AcceptanceEvidenceKind[]; review?: { agent?: string; required?: boolean } } {
|
|
85
|
-
const agent = input.agentName.toLowerCase();
|
|
86
85
|
const task = input.task?.toLowerCase() ?? "";
|
|
87
86
|
const reasons: string[] = [];
|
|
88
87
|
// Declared roles replace name heuristics, so use the full writer grammar to detect explicit mutation independently of the actual agent name.
|
|
@@ -94,11 +93,10 @@ function inferLevel(input: {
|
|
|
94
93
|
&& !/\b(?:do not|don't|must not)\s+patch\b/.test(task)
|
|
95
94
|
&& /\bpatch\s+(?:(?:\.{0,2}[\\/])?(?:[\w.-]+[\\/])+[\w.-]+|[\w.-]+\.[a-z0-9]+\b|(?:the\s+)?parser\b)/.test(task);
|
|
96
95
|
const taskMayWrite = readOnlyTask ? false : taskMayMutate(input.task ?? "") || intent.kind === "implementation" || rolePatchTask;
|
|
97
|
-
const
|
|
98
|
-
|
|
96
|
+
const routingRole = resolveAgentRoutingRole(input.agentName, input.acceptanceRole);
|
|
97
|
+
const readOnlyAgent = routingRole === "read-only";
|
|
99
98
|
const writeTask = taskMayWrite
|
|
100
|
-
|| (
|
|
101
|
-
|| (input.acceptanceRole === undefined && /\bbuilder\b/.test(agent) && !readOnlyTask);
|
|
99
|
+
|| (routingRole === "writer" && !readOnlyTask);
|
|
102
100
|
const inferredReadOnly = readOnlyTask || (input.acceptanceRole === "read-only" && !taskMayWrite);
|
|
103
101
|
const roleResolvesReadOnly = input.acceptanceRole !== undefined && inferredReadOnly;
|
|
104
102
|
const keywordRiskReadOnly = input.acceptanceRole === undefined ? intent.kind === "read-only" : inferredReadOnly;
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { Message } from "@earendil-works/pi-ai";
|
|
4
|
-
import type { OutputMode, SavedOutputReference } from "../../shared/types.ts";
|
|
4
|
+
import type { MaxOutputConfig, OutputMode, SavedOutputReference } from "../../shared/types.ts";
|
|
5
|
+
import { truncateOutput } from "../../shared/types.ts";
|
|
5
6
|
import { hasMutationToolCapability } from "./completion-guard.ts";
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Fixed internal context-fallback cap used when persisted output is unavailable
|
|
10
|
+
* (persistence/read failure, `output: false`, or legacy results without a saved
|
|
11
|
+
* path). Deliberately not exposed as a public parameter; reuse `truncateOutput()`
|
|
12
|
+
* so the excerpt is bounded by both lines and bytes and never falls back to
|
|
13
|
+
* unbounded output.
|
|
14
|
+
*/
|
|
15
|
+
export const CONTEXT_FALLBACK_LIMIT: Required<MaxOutputConfig> = { bytes: 4096, lines: 80 };
|
|
16
|
+
|
|
7
17
|
export interface SingleOutputSnapshot {
|
|
8
18
|
exists: boolean;
|
|
9
19
|
mtimeMs?: number;
|
|
@@ -125,6 +135,34 @@ function formatByteSize(bytes: number): string {
|
|
|
125
135
|
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
126
136
|
}
|
|
127
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Bounded fallback for deliveries where the durable full output cannot be
|
|
140
|
+
* referenced: persistence/write errors, unreadable saved paths, or no saved
|
|
141
|
+
* path at all (legacy/`output: false`). Contains process status, the intended
|
|
142
|
+
* output path, the concrete error, and a bounded 80-line/4-KiB excerpt of the
|
|
143
|
+
* available output. Never claims a full artifact exists.
|
|
144
|
+
*/
|
|
145
|
+
export function formatBoundedPersistenceFallback(input: {
|
|
146
|
+
error: string;
|
|
147
|
+
outputPath?: string;
|
|
148
|
+
fullOutput: string;
|
|
149
|
+
exitCode: number;
|
|
150
|
+
processError?: string;
|
|
151
|
+
}): string {
|
|
152
|
+
const status = input.processError?.trim()
|
|
153
|
+
? `Process status: ${input.processError}`
|
|
154
|
+
: `Process status: ${input.exitCode === 0 ? "completed" : `failed (exit ${input.exitCode})`}`;
|
|
155
|
+
const bounded = truncateOutput(input.fullOutput, CONTEXT_FALLBACK_LIMIT);
|
|
156
|
+
const lines = [
|
|
157
|
+
`[Full output unavailable] ${input.error}`,
|
|
158
|
+
status,
|
|
159
|
+
...(input.outputPath ? [`Intended output path: ${input.outputPath}`] : []),
|
|
160
|
+
"Full output is unavailable; showing a bounded excerpt (first 80 lines / 4 KiB).",
|
|
161
|
+
bounded.text,
|
|
162
|
+
];
|
|
163
|
+
return lines.join("\n");
|
|
164
|
+
}
|
|
165
|
+
|
|
128
166
|
export function formatSavedOutputReference(savedPath: string, fullOutput: string): SavedOutputReference {
|
|
129
167
|
const absolutePath = path.resolve(savedPath);
|
|
130
168
|
const bytes = Buffer.byteLength(fullOutput, "utf-8");
|
|
@@ -217,19 +255,35 @@ export function finalizeSingleOutput(params: {
|
|
|
217
255
|
savedPath?: string;
|
|
218
256
|
outputReference?: SavedOutputReference;
|
|
219
257
|
saveError?: string;
|
|
258
|
+
error?: string;
|
|
220
259
|
}): { displayOutput: string; savedPath?: string; outputReference?: SavedOutputReference; saveError?: string } {
|
|
221
|
-
|
|
222
|
-
if (params.exitCode === 0 && params.savedPath) {
|
|
260
|
+
if (params.savedPath) {
|
|
223
261
|
const outputReference = params.outputReference ?? formatSavedOutputReference(params.savedPath, params.fullOutput);
|
|
224
|
-
if (params.outputMode === "file-only") {
|
|
262
|
+
if (params.exitCode === 0 && params.outputMode === "file-only") {
|
|
225
263
|
return { displayOutput: outputReference.message, savedPath: params.savedPath, outputReference };
|
|
226
264
|
}
|
|
227
|
-
|
|
265
|
+
if (params.exitCode !== 0) {
|
|
266
|
+
// Failed runs with a successfully persisted result surface the error/status
|
|
267
|
+
// plus the saved-output reference, never raw child output.
|
|
268
|
+
const status = params.error?.trim() || `Subagent failed with exit code ${params.exitCode}`;
|
|
269
|
+
return { displayOutput: `${status}\n\n${outputReference.message}`, savedPath: params.savedPath, outputReference };
|
|
270
|
+
}
|
|
271
|
+
const displayOutput = `${params.truncatedOutput || params.fullOutput}\n\n${outputReference.message}`;
|
|
228
272
|
return { displayOutput, savedPath: params.savedPath, outputReference };
|
|
229
273
|
}
|
|
230
|
-
if (params.
|
|
231
|
-
|
|
232
|
-
|
|
274
|
+
if (params.saveError && params.outputPath) {
|
|
275
|
+
// Persistence/read failure: only the bounded fallback is delivered. It must
|
|
276
|
+
// not claim a full artifact exists.
|
|
277
|
+
return {
|
|
278
|
+
displayOutput: formatBoundedPersistenceFallback({
|
|
279
|
+
error: params.saveError,
|
|
280
|
+
outputPath: params.outputPath,
|
|
281
|
+
fullOutput: params.fullOutput,
|
|
282
|
+
exitCode: params.exitCode,
|
|
283
|
+
processError: params.error,
|
|
284
|
+
}),
|
|
285
|
+
saveError: params.saveError,
|
|
286
|
+
};
|
|
233
287
|
}
|
|
234
|
-
return { displayOutput };
|
|
288
|
+
return { displayOutput: params.truncatedOutput || params.fullOutput };
|
|
235
289
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AcceptanceRole } from "../../shared/types.ts";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Shared task mutation-intent classifier.
|
|
3
5
|
*
|
|
@@ -139,6 +141,25 @@ function isReviewerStyleAgent(agent: string): boolean {
|
|
|
139
141
|
return /\b(?:architect|commentator|explorer|recapper|researcher)\b/i.test(agent);
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Resolve an agent's acceptance routing role: a declared `acceptanceRole`
|
|
146
|
+
* always wins; otherwise the established name heuristics apply (builder-named
|
|
147
|
+
* agents are writers; architect/commentator/explorer/recapper/researcher/
|
|
148
|
+
* analyst-named agents are read-only). This is the single shared source of
|
|
149
|
+
* truth for acceptance inference and task-aware advisory routing so the two
|
|
150
|
+
* cannot drift. Alias values are never role signals.
|
|
151
|
+
*/
|
|
152
|
+
export function resolveAgentRoutingRole(
|
|
153
|
+
agentName: string,
|
|
154
|
+
acceptanceRole?: AcceptanceRole,
|
|
155
|
+
): "writer" | "read-only" | undefined {
|
|
156
|
+
if (acceptanceRole !== undefined) return acceptanceRole;
|
|
157
|
+
const agent = agentName.toLowerCase();
|
|
158
|
+
if (/\bbuilder\b/.test(agent)) return "writer";
|
|
159
|
+
if (/\b(?:architect|commentator|explorer|recapper|researcher|analyst)\b/.test(agent)) return "read-only";
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
142
163
|
function hasImplementationIntent(agent: string, taskText: string): boolean {
|
|
143
164
|
if (isReviewerStyleAgent(agent)) return REVIEWER_REQUIRED_EDIT_PATTERNS.some((pattern) => pattern.test(taskText));
|
|
144
165
|
if (agent === "builder") return WORKER_IMPLEMENTATION_PATTERNS.some((pattern) => pattern.test(taskText));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import * as os from "node:os";
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import type { Message } from "@earendil-works/pi-ai";
|
|
8
|
-
import type { AgentConfig } from "../agents/agents.ts";
|
|
8
|
+
import type { AgentConfig, AgentSource } from "../agents/agents.ts";
|
|
9
9
|
import type { FSWatcher } from "node:fs";
|
|
10
10
|
import type { ExtensionContext } from "@selesai/code";
|
|
11
11
|
import type { ModelScopeConfig } from "../runs/shared/model-scope.ts";
|
|
@@ -912,12 +912,51 @@ export interface SpawnBudgetSnapshot {
|
|
|
912
912
|
grantHistory: SpawnBudgetGrant[];
|
|
913
913
|
}
|
|
914
914
|
|
|
915
|
+
// ============================================================================
|
|
916
|
+
// Runtime catalog (action:list machine metadata)
|
|
917
|
+
// ============================================================================
|
|
918
|
+
|
|
919
|
+
/** Machine-readable catalog entry for one visible runtime agent. */
|
|
920
|
+
export interface CatalogAgentMetadata {
|
|
921
|
+
name: string;
|
|
922
|
+
source: AgentSource;
|
|
923
|
+
description: string;
|
|
924
|
+
/** false for capability-ceiling-restricted agents (still visible, not launchable). */
|
|
925
|
+
executable: boolean;
|
|
926
|
+
/** Present only when the agent is capability-ceiling-restricted. */
|
|
927
|
+
restrictionSources?: string[];
|
|
928
|
+
aliases?: string[];
|
|
929
|
+
/** Normalized to explicit "fresh" when the agent has no defaultContext. */
|
|
930
|
+
defaultContext: "fresh" | "fork";
|
|
931
|
+
acceptanceRole?: AcceptanceRole;
|
|
932
|
+
/** Effective declared tools: normal tools plus mcp:-prefixed direct MCP tools. */
|
|
933
|
+
tools?: string[];
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/** Machine-readable catalog entry for one visible chain. */
|
|
937
|
+
export interface CatalogChainMetadata {
|
|
938
|
+
name: string;
|
|
939
|
+
source: AgentSource;
|
|
940
|
+
description: string;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/** Versioned action:list catalog mirroring the human list output. */
|
|
944
|
+
export interface CatalogMetadataV1 {
|
|
945
|
+
version: 1;
|
|
946
|
+
agents: CatalogAgentMetadata[];
|
|
947
|
+
chains: CatalogChainMetadata[];
|
|
948
|
+
/** Present only when a capability ceiling restricted visible agents. */
|
|
949
|
+
capabilityCeilingSources?: string[];
|
|
950
|
+
}
|
|
951
|
+
|
|
915
952
|
export interface Details {
|
|
916
953
|
mode: SubagentRunMode | "management";
|
|
917
954
|
runId?: string;
|
|
918
955
|
/** Run-level context summary. "mixed" when children resolved to different modes. */
|
|
919
956
|
context?: "fresh" | "fork" | "mixed";
|
|
920
957
|
results: SingleResult[];
|
|
958
|
+
/** Runtime-resolved human+machine catalog for { action: "list" } results. */
|
|
959
|
+
catalog?: CatalogMetadataV1;
|
|
921
960
|
controlEvents?: ControlEvent[];
|
|
922
961
|
steering?: SteerActionResult;
|
|
923
962
|
asyncId?: string;
|
|
@@ -1645,7 +1684,7 @@ export const DEFAULT_MAX_OUTPUT: Required<MaxOutputConfig> = {
|
|
|
1645
1684
|
};
|
|
1646
1685
|
|
|
1647
1686
|
export const DEFAULT_ARTIFACT_CONFIG: ArtifactConfig = {
|
|
1648
|
-
enabled:
|
|
1687
|
+
enabled: false,
|
|
1649
1688
|
dir: "project",
|
|
1650
1689
|
includeInput: true,
|
|
1651
1690
|
includeOutput: true,
|
|
@@ -7,7 +7,7 @@ import * as os from "node:os";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import type { Message } from "@earendil-works/pi-ai";
|
|
9
9
|
import { formatToolCall } from "./formatters.ts";
|
|
10
|
-
import type { AgentProgress, AsyncStatus, Details, DisplayItem, ErrorInfo, NestedRunSummary, SingleResult, ToolCallSummary, Usage } from "./types.ts";
|
|
10
|
+
import type { AgentProgress, AsyncStatus, ChainOutputMap, Details, DisplayItem, ErrorInfo, NestedRunSummary, SingleResult, ToolCallSummary, Usage } from "./types.ts";
|
|
11
11
|
|
|
12
12
|
// ============================================================================
|
|
13
13
|
// File System Utilities
|
|
@@ -414,12 +414,40 @@ export function compactForegroundResult(result: SingleResult): SingleResult {
|
|
|
414
414
|
messages: undefined,
|
|
415
415
|
progress: undefined,
|
|
416
416
|
toolCalls: toolCalls.length ? toolCalls : undefined,
|
|
417
|
+
// Reference-first terminal details: once an authoritative saved output path
|
|
418
|
+
// exists, drop the raw final output and truncation marker from the model-
|
|
419
|
+
// visible projection; consumers recover the file from `savedOutputPath`.
|
|
420
|
+
// Explicit `outputMode: "inline"` is the sole legacy full-text opt-out and
|
|
421
|
+
// keeps its final output in the terminal projection (e.g. delegation v1
|
|
422
|
+
// `response.output` stays populated).
|
|
423
|
+
finalOutput: result.savedOutputPath && result.outputMode !== "inline" ? undefined : result.finalOutput,
|
|
424
|
+
truncation: result.savedOutputPath && result.outputMode !== "inline" ? undefined : result.truncation,
|
|
417
425
|
};
|
|
418
426
|
}
|
|
419
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Strip chain `details.outputs` text/structured payloads from the terminal
|
|
430
|
+
* projection while retaining the output names and step metadata. Chain output
|
|
431
|
+
* bindings themselves remain reference-first in the completion content and
|
|
432
|
+
* `{outputs.name}` interpolation (see outputEntryFromResult).
|
|
433
|
+
*/
|
|
434
|
+
function compactChainOutputs(outputs: ChainOutputMap | undefined): ChainOutputMap | undefined {
|
|
435
|
+
if (!outputs) return undefined;
|
|
436
|
+
const compact: ChainOutputMap = {};
|
|
437
|
+
for (const [name, entry] of Object.entries(outputs)) {
|
|
438
|
+
compact[name] = {
|
|
439
|
+
agent: entry.agent,
|
|
440
|
+
stepIndex: entry.stepIndex,
|
|
441
|
+
text: "",
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return compact;
|
|
445
|
+
}
|
|
446
|
+
|
|
420
447
|
export function compactForegroundDetails(details: Details): Details {
|
|
421
448
|
return {
|
|
422
449
|
...details,
|
|
450
|
+
outputs: compactChainOutputs(details.outputs),
|
|
423
451
|
results: details.results.map(compactForegroundResult),
|
|
424
452
|
progress: details.progress
|
|
425
453
|
? details.progress.map(compactCompletedProgress)
|
|
@@ -375,7 +375,11 @@ export function toSubagentDelegationExecutionParams(request: SubagentDelegationR
|
|
|
375
375
|
toolBudget: request.toolBudget,
|
|
376
376
|
skill: request.skill,
|
|
377
377
|
output: request.output,
|
|
378
|
-
outputMode:
|
|
378
|
+
// v1 has no default for outputMode: preserve the legacy full-text contract by
|
|
379
|
+
// resolving an omitted outputMode to explicit inline (response.output stays
|
|
380
|
+
// populated). The model-facing tool's mode-dependent reference-first default
|
|
381
|
+
// (omitted outputMode -> file-only) is intentionally NOT applied here.
|
|
382
|
+
outputMode: request.outputMode ?? "inline",
|
|
379
383
|
outputSchema: request.outputSchema,
|
|
380
384
|
agentContract: request.agentContract,
|
|
381
385
|
acceptance: request.acceptance,
|