opencode-agent-skill 14.4.0 → 15.1.0
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/README.md +34 -16
- package/docs/PI-COMPAT.md +151 -2
- package/docs/V15-MANAGED-RUNTIME.md +128 -0
- package/global-config/agents/architect.md +63 -1
- package/global-config/agents/executor.md +7 -0
- package/global-config/agents/plan-checker.md +8 -1
- package/global-config/agents/verifier.md +3 -3
- package/global-config/skills/database-engineering/references/workflow.md +15 -0
- package/global-config/skills/engineering-orchestrator/references/long-horizon.md +21 -11
- package/lib/activity-deadline.mjs +70 -0
- package/lib/adaptive-context-budget.mjs +25 -5
- package/lib/affected-tests.mjs +38 -5
- package/lib/capability-fabric.mjs +17 -0
- package/lib/code-intelligence/index.mjs +3 -2
- package/lib/code-intelligence/lsp-provider.mjs +326 -52
- package/lib/compaction-resume-guard.mjs +255 -0
- package/lib/completion-auditor.mjs +25 -5
- package/lib/context-engine-v11.mjs +20 -7
- package/lib/evidence-store.mjs +92 -8
- package/lib/execution-contract.mjs +592 -0
- package/lib/leaf-runtime-optimizer.mjs +76 -0
- package/lib/memory-engine.mjs +189 -16
- package/lib/performance-fabric.mjs +262 -107
- package/lib/pi-child-invocation.mjs +65 -0
- package/lib/pi-rpc-pool.mjs +61 -9
- package/lib/plan-salvage.mjs +124 -0
- package/lib/planning-speed-policy.mjs +131 -0
- package/lib/repo-graph.mjs +1 -1
- package/lib/repo-inspect.mjs +1 -0
- package/lib/runtime-artifacts.mjs +42 -0
- package/lib/runtime-events.mjs +33 -1
- package/lib/safety.mjs +1 -0
- package/lib/semantic-index.mjs +1 -1
- package/lib/service-manager.mjs +526 -0
- package/lib/session-display.mjs +49 -0
- package/lib/subagent-artifacts.mjs +182 -0
- package/lib/task-graph.mjs +87 -0
- package/lib/task-policy.mjs +150 -4
- package/lib/trajectory.mjs +111 -1
- package/lib/turbo-fast-path.mjs +44 -0
- package/lib/verification-broker.mjs +32 -27
- package/lib/workspace-fingerprint.mjs +45 -19
- package/lib/workspace-hygiene.mjs +481 -0
- package/lib/workspace-root.mjs +49 -0
- package/lib/worktree-sandbox.mjs +234 -11
- package/package.json +8 -5
- package/pi/extensions/ues-child-runtime.ts +170 -4
- package/pi/extensions/ues.ts +2063 -188
- package/scripts/check-release-consistency.mjs +25 -2
- package/scripts/check-source-integrity.mjs +471 -4
- package/scripts/eval-pi.mjs +76 -36
- package/scripts/smoke-pi-extension.mjs +1 -1
- package/pi/prompts/ues-run.md +0 -17
package/pi/extensions/ues.ts
CHANGED
|
@@ -7,15 +7,16 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { Type } from "typebox";
|
|
9
9
|
import { destructiveShellRisk } from "../../lib/safety.mjs";
|
|
10
|
-
import { classifyEngineeringTask, shouldRunDedicatedDiagnosis } from "../../lib/task-policy.mjs";
|
|
10
|
+
import { automaticUesAdmission, automaticUesContinuation, classifyEngineeringTask, deterministicReadOnlyGitCommands, shouldRunDedicatedDiagnosis } from "../../lib/task-policy.mjs";
|
|
11
11
|
import { resolveCapabilityModel } from "../../lib/model-policy.mjs";
|
|
12
12
|
import { readModelPolicy, recordModelPerformance } from "../../lib/model-config.mjs";
|
|
13
13
|
import { getUesConfigDir } from "../../lib/runtime-config.mjs";
|
|
14
14
|
import { buildAdaptiveTaskContext } from "../../lib/context-engine-v11.mjs";
|
|
15
15
|
import { recordVerifiedTaskMemory } from "../../lib/memory-engine.mjs";
|
|
16
|
-
import { computeSafeWaves, taskVerificationCommands, taskWriteFiles, validatePlan } from "../../lib/task-graph.mjs";
|
|
16
|
+
import { computeSafeWaves, normalizePlanForValidation, taskVerificationCommands, taskWriteFiles, validatePlan } from "../../lib/task-graph.mjs";
|
|
17
17
|
import { planDynamicWorkflow } from "../../lib/dynamic-workflow.mjs";
|
|
18
18
|
import { compactReversibleOutput } from "../../lib/performance-fabric.mjs";
|
|
19
|
+
import { gcEvidenceStore } from "../../lib/evidence-store.mjs";
|
|
19
20
|
import {
|
|
20
21
|
createToolOutputAccumulator,
|
|
21
22
|
detectHungToolEvidence,
|
|
@@ -24,15 +25,28 @@ import {
|
|
|
24
25
|
} from "../../lib/process-hang-detector.mjs";
|
|
25
26
|
import { runSupervisedProcess, terminateProcessTree } from "../../lib/process-supervisor.mjs";
|
|
26
27
|
import { PiRpcWorkerPool } from "../../lib/pi-rpc-pool.mjs";
|
|
28
|
+
import { resolvePiChildInvocation } from "../../lib/pi-child-invocation.mjs";
|
|
27
29
|
import { adaptiveContextBudget } from "../../lib/adaptive-context-budget.mjs";
|
|
28
30
|
import { clearSkillCompilerCache, compileSkillContext } from "../../lib/skill-compiler.mjs";
|
|
29
31
|
import { clearAffectedTestCache, resolveAffectedTests } from "../../lib/affected-tests.mjs";
|
|
30
32
|
import { findReusableVerification, listReusableVerification, recordVerification } from "../../lib/verification-broker.mjs";
|
|
31
33
|
import { evaluateFastVerificationGate } from "../../lib/fast-verification-gate.mjs";
|
|
34
|
+
import { turboFastPathDecision, turboFastTimeoutBudget } from "../../lib/turbo-fast-path.mjs";
|
|
35
|
+
import { failureDelta, leafTaskPolicy } from "../../lib/leaf-runtime-optimizer.mjs";
|
|
36
|
+
import { planningRuntimeBudget, shouldSoftSteerArchitect, shouldSoftSteerPlanningRole } from "../../lib/planning-speed-policy.mjs";
|
|
37
|
+
import { sourceFacingPaths, sourceGitPathspecs } from "../../lib/runtime-artifacts.mjs";
|
|
38
|
+
import { createSubagentArtifact, failSubagentArtifact, finalizeSubagentArtifact, listSubagentArtifacts, readSubagentArtifact } from "../../lib/subagent-artifacts.mjs";
|
|
39
|
+
import { buildExecutionContract, buildFinalVerdictMatrix, captureInheritedDirtyState, detectInheritedDirtyViolations, enforcePhaseGates, executionContractPrompt, phaseArtifactPayloads, taskExplicitlyAllowsLocalEnvWrite } from "../../lib/execution-contract.mjs";
|
|
40
|
+
import { buildCompactionResumeGuard, checkpointDurableWorkBeforeCompaction, renderCompactionResumeGuard } from "../../lib/compaction-resume-guard.mjs";
|
|
41
|
+
import { sessionNameFromUesInput, uesSessionName } from "../../lib/session-display.mjs";
|
|
42
|
+
import { requireGitWorkspaceRoot, resolveGitWorkspaceRoot } from "../../lib/workspace-root.mjs";
|
|
43
|
+
import { createAdaptiveDeadline } from "../../lib/activity-deadline.mjs";
|
|
44
|
+
import { extractValidatedPlan } from "../../lib/plan-salvage.mjs";
|
|
32
45
|
import { auditCompletion } from "../../lib/completion-auditor.mjs";
|
|
33
46
|
import { mcpExecutionPolicy } from "../../lib/mcp-tool-policy.mjs";
|
|
34
47
|
import { McpHealthTracker } from "../../lib/mcp-health.mjs";
|
|
35
|
-
import { runtimeWorkspaceFingerprint, runtimeWorkspaceSnapshot } from "../../lib/workspace-fingerprint.mjs";
|
|
48
|
+
import { captureWorkspaceStateV2, runtimeWorkspaceFingerprint, runtimeWorkspaceSnapshot } from "../../lib/workspace-fingerprint.mjs";
|
|
49
|
+
import { captureWorkspaceHygieneBaseline, postRunFileHygiene, preFinalWorkspaceAudit } from "../../lib/workspace-hygiene.mjs";
|
|
36
50
|
import { appendTrajectoryEvent, createTraceID } from "../../lib/trajectory.mjs";
|
|
37
51
|
import {
|
|
38
52
|
browserEvidenceNeeded,
|
|
@@ -47,14 +61,37 @@ import {
|
|
|
47
61
|
integrateTaskSandbox,
|
|
48
62
|
removeTaskSandbox,
|
|
49
63
|
rollbackTaskSandbox,
|
|
64
|
+
pruneOrphanTaskSandboxes,
|
|
65
|
+
taskSandboxOwnerRoot,
|
|
50
66
|
} from "../../lib/worktree-sandbox.mjs";
|
|
67
|
+
import {
|
|
68
|
+
looksLikeLongRunningServiceCommand,
|
|
69
|
+
restartService,
|
|
70
|
+
serviceLogs,
|
|
71
|
+
serviceStatus,
|
|
72
|
+
startService,
|
|
73
|
+
stopAllServices,
|
|
74
|
+
stopService,
|
|
75
|
+
waitForService,
|
|
76
|
+
} from "../../lib/service-manager.mjs";
|
|
51
77
|
|
|
52
78
|
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
79
|
+
const PACKAGE_VERSION = (() => {
|
|
80
|
+
try {
|
|
81
|
+
return String(JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"))?.version || "unknown");
|
|
82
|
+
} catch {
|
|
83
|
+
return "unknown";
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
53
86
|
const OCSKILL_BIN = path.join(PACKAGE_ROOT, "bin", "ocskill.mjs");
|
|
54
87
|
const CHILD_RUNTIME_EXTENSION = path.join(PACKAGE_ROOT, "pi", "extensions", "ues-child-runtime.ts");
|
|
55
88
|
const AGENT_DIR = path.join(PACKAGE_ROOT, "global-config", "agents");
|
|
56
89
|
const MAX_PARALLEL_TASKS = 8;
|
|
57
90
|
const MAX_CONCURRENCY = 4;
|
|
91
|
+
const MAX_WRITER_CONCURRENCY = Math.max(
|
|
92
|
+
1,
|
|
93
|
+
Math.min(4, Number(process.env.UES_MAX_WRITER_CONCURRENCY || (process.platform === "win32" ? 2 : 3))),
|
|
94
|
+
);
|
|
58
95
|
const OUTPUT_LIMIT = 512 * 1024;
|
|
59
96
|
|
|
60
97
|
function configuredDuration(name: string, fallback: number, min: number, max: number) {
|
|
@@ -93,6 +130,12 @@ const POST_TOOL_ERROR_IDLE_TIMEOUT_MS = configuredDuration(
|
|
|
93
130
|
5_000,
|
|
94
131
|
5 * 60_000,
|
|
95
132
|
);
|
|
133
|
+
const TURBO_FAST_TIMEOUTS = turboFastTimeoutBudget({
|
|
134
|
+
hardTimeoutMs: configuredDuration("UES_FAST_CHILD_HARD_TIMEOUT_MS", 180_000, 30_000, 10 * 60_000),
|
|
135
|
+
idleTimeoutMs: configuredDuration("UES_FAST_CHILD_IDLE_TIMEOUT_MS", 60_000, 20_000, 5 * 60_000),
|
|
136
|
+
postToolErrorIdleTimeoutMs: configuredDuration("UES_FAST_POST_TOOL_ERROR_IDLE_TIMEOUT_MS", 30_000, 5_000, 2 * 60_000),
|
|
137
|
+
verificationTimeoutSec: configuredDuration("UES_FAST_VERIFICATION_TIMEOUT_SEC", 90, 30, 300),
|
|
138
|
+
});
|
|
96
139
|
const MODEL_VISIBLE_OUTPUT_LIMIT = configuredDuration(
|
|
97
140
|
"UES_MODEL_VISIBLE_OUTPUT_LIMIT",
|
|
98
141
|
64 * 1024,
|
|
@@ -160,6 +203,7 @@ function abortActiveCliChildren() {
|
|
|
160
203
|
|
|
161
204
|
let HOST_BROWSER_TOOL_NAMES: string[] = [];
|
|
162
205
|
const CONTEXT_PACK_CACHE = new Map<string, any>();
|
|
206
|
+
const ACTIVE_TASK_SANDBOXES = new Map<string, string>();
|
|
163
207
|
|
|
164
208
|
function configuredBrowserToolNames() {
|
|
165
209
|
return String(process.env.UES_BROWSER_MCP_TOOL_NAMES || "")
|
|
@@ -231,6 +275,7 @@ type RunResult = {
|
|
|
231
275
|
browserTools?: string[];
|
|
232
276
|
childRuntime?: "rpc" | "cli";
|
|
233
277
|
workerReused?: boolean;
|
|
278
|
+
subagentArtifact?: any;
|
|
234
279
|
optimizations?: any;
|
|
235
280
|
};
|
|
236
281
|
|
|
@@ -257,6 +302,8 @@ function getAgentPrompt(agent: AgentName) {
|
|
|
257
302
|
"- Do not call OpenCode-only dispatch tools such as \`ues.dispatch_task\` or \`ues.dispatch_parallel\`.",
|
|
258
303
|
"- Respect the original role's edit/read-only boundary and return evidence to the parent Pi session.",
|
|
259
304
|
"- Prefer ues_code for bounded semantic/AST search, hash-anchored reads and optional LSP diagnostics. Writer roles may use ues_code_edit only after an anchored read; stale anchors must be re-read rather than fuzzily retried.",
|
|
305
|
+
"- Never launch a persistent dev server/watcher in foreground bash/powershell. Use ues_service start, wait-ready/status/logs, then stop; the runtime blocks common foreground-service commands to prevent hangs.",
|
|
306
|
+
"- On Windows, never pass /tmp or /var/tmp paths between Pi file tools and bash/powershell: their path namespaces may differ. Keep temporary transforms inside one shell pipeline, or use an ignored repository-local scratch path such as .ues-cache/tmp for cross-tool scratch.",
|
|
260
307
|
"- For PDF/DOCX/PPTX/XLSX evidence, ues_code action=document may use optional MarkItDown when installed; do not install it unless that capability is needed.",
|
|
261
308
|
"",
|
|
262
309
|
].join("\n");
|
|
@@ -272,16 +319,8 @@ function getAgentPrompt(agent: AgentName) {
|
|
|
272
319
|
}
|
|
273
320
|
|
|
274
321
|
function getPiInvocation(args: string[]): { command: string; args: string[] } {
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
if (currentScript && !isBunVirtualScript && fs.existsSync(currentScript)) {
|
|
278
|
-
return { command: process.execPath, args: [currentScript, ...args] };
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const execName = path.basename(process.execPath).toLowerCase();
|
|
282
|
-
const isGenericRuntime = /^(node|bun)(\.exe)?$/.test(execName);
|
|
283
|
-
if (!isGenericRuntime) return { command: process.execPath, args };
|
|
284
|
-
return { command: "pi", args };
|
|
322
|
+
const invocation = resolvePiChildInvocation(args);
|
|
323
|
+
return { command: invocation.command, args: invocation.args };
|
|
285
324
|
}
|
|
286
325
|
|
|
287
326
|
function extractAssistantText(message: any): string {
|
|
@@ -345,6 +384,83 @@ async function runOcskillJson(args: string[], cwd: string, signal?: AbortSignal)
|
|
|
345
384
|
}
|
|
346
385
|
}
|
|
347
386
|
|
|
387
|
+
async function persistExecutionContractArtifacts(dir: string, contract: any) {
|
|
388
|
+
if (!contract || !dir) return null;
|
|
389
|
+
const phasesDir = path.join(dir, "phases");
|
|
390
|
+
await fs.promises.mkdir(phasesDir, { recursive: true });
|
|
391
|
+
await fs.promises.writeFile(
|
|
392
|
+
path.join(dir, "EXECUTION_CONTRACT.json"),
|
|
393
|
+
JSON.stringify(contract, null, 2) + "\n",
|
|
394
|
+
"utf8",
|
|
395
|
+
);
|
|
396
|
+
await fs.promises.writeFile(
|
|
397
|
+
path.join(phasesDir, "MANIFEST.json"),
|
|
398
|
+
JSON.stringify({
|
|
399
|
+
schemaVersion: 1,
|
|
400
|
+
taskHash: contract.taskHash,
|
|
401
|
+
phaseCount: contract.phases?.length || 0,
|
|
402
|
+
artifacts: phaseArtifactPayloads(contract).map((item: any) => item.file),
|
|
403
|
+
}, null, 2) + "\n",
|
|
404
|
+
"utf8",
|
|
405
|
+
);
|
|
406
|
+
for (const item of phaseArtifactPayloads(contract)) {
|
|
407
|
+
const target = path.join(dir, item.file);
|
|
408
|
+
await fs.promises.mkdir(path.dirname(target), { recursive: true });
|
|
409
|
+
await fs.promises.writeFile(target, JSON.stringify(item.value, null, 2) + "\n", "utf8");
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
contract: path.join(dir, "EXECUTION_CONTRACT.json"),
|
|
413
|
+
manifest: path.join(phasesDir, "MANIFEST.json"),
|
|
414
|
+
phaseCount: contract.phases?.length || 0,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async function finalizeExecutionContractArtifacts(
|
|
419
|
+
dir: string,
|
|
420
|
+
contract: any,
|
|
421
|
+
verdictMatrix: any,
|
|
422
|
+
evidence: string,
|
|
423
|
+
) {
|
|
424
|
+
if (!dir || !contract) return null;
|
|
425
|
+
const payload = {
|
|
426
|
+
schemaVersion: 1,
|
|
427
|
+
verdictMatrix,
|
|
428
|
+
evidence: cap(evidence || "", 12000),
|
|
429
|
+
finalizedAt: new Date().toISOString(),
|
|
430
|
+
};
|
|
431
|
+
await fs.promises.writeFile(
|
|
432
|
+
path.join(dir, "FINAL_VERDICTS.json"),
|
|
433
|
+
JSON.stringify(payload, null, 2) + "\n",
|
|
434
|
+
"utf8",
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
for (const item of phaseArtifactPayloads(contract)) {
|
|
438
|
+
const phaseText = String(item.value.title || "") + "\n" + String(item.value.sourceBody || "");
|
|
439
|
+
let status = verdictMatrix?.source === "SOURCE_PASS" ? "VERIFIED" : "NOT_VERIFIED";
|
|
440
|
+
if (/device|expo go|thiết bị/i.test(phaseText) && verdictMatrix?.device !== "DEVICE_PASS") {
|
|
441
|
+
status = "NOT_VERIFIED";
|
|
442
|
+
}
|
|
443
|
+
if (/(?:cleanup|fixture|database|db|dữ liệu)/i.test(phaseText) && contract.gates?.dbClean === true && verdictMatrix?.dbClean !== "DB_CLEAN_PASS") {
|
|
444
|
+
status = "NOT_VERIFIED";
|
|
445
|
+
}
|
|
446
|
+
if (/(?:runtime|smoke|e2e|integration|typecheck|lint|test|verify|xác minh|kiểm tra)/i.test(phaseText) && contract.gates?.runtime === true && verdictMatrix?.runtime !== "RUNTIME_PASS") {
|
|
447
|
+
status = "NOT_VERIFIED";
|
|
448
|
+
}
|
|
449
|
+
const target = path.join(dir, item.file);
|
|
450
|
+
await fs.promises.writeFile(
|
|
451
|
+
target,
|
|
452
|
+
JSON.stringify({
|
|
453
|
+
...item.value,
|
|
454
|
+
status,
|
|
455
|
+
finalVerdicts: verdictMatrix,
|
|
456
|
+
evidence: [cap(evidence || "", 6000)],
|
|
457
|
+
}, null, 2) + "\n",
|
|
458
|
+
"utf8",
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
return payload;
|
|
462
|
+
}
|
|
463
|
+
|
|
348
464
|
async function initializeDurableControllerWork(
|
|
349
465
|
root: string,
|
|
350
466
|
originalTask: string,
|
|
@@ -540,9 +656,22 @@ async function runAgentCli(
|
|
|
540
656
|
compactToolOutput?: boolean;
|
|
541
657
|
toolOutputLimit?: number;
|
|
542
658
|
verificationTimeoutSec?: number;
|
|
659
|
+
hardTimeoutMs?: number;
|
|
660
|
+
absoluteHardTimeoutMs?: number;
|
|
661
|
+
activityExtensionMs?: number;
|
|
662
|
+
activityWindowMs?: number;
|
|
663
|
+
idleTimeoutMs?: number;
|
|
664
|
+
postToolErrorIdleTimeoutMs?: number;
|
|
665
|
+
allowLocalEnvWrite?: boolean;
|
|
543
666
|
} = {},
|
|
544
667
|
): Promise<RunResult> {
|
|
545
668
|
const config = AGENTS[agent];
|
|
669
|
+
const hardTimeoutMs = Number(runtimeOptions.hardTimeoutMs || CHILD_HARD_TIMEOUT_MS);
|
|
670
|
+
const absoluteHardTimeoutMs = Number(runtimeOptions.absoluteHardTimeoutMs || hardTimeoutMs);
|
|
671
|
+
const activityExtensionMs = Number(runtimeOptions.activityExtensionMs || 0);
|
|
672
|
+
const activityWindowMs = Number(runtimeOptions.activityWindowMs || Math.max(5_000, Math.min(activityExtensionMs || hardTimeoutMs, 30_000)));
|
|
673
|
+
const idleTimeoutMs = Number(runtimeOptions.idleTimeoutMs || CHILD_IDLE_TIMEOUT_MS);
|
|
674
|
+
const postToolErrorIdleTimeoutMs = Number(runtimeOptions.postToolErrorIdleTimeoutMs || POST_TOOL_ERROR_IDLE_TIMEOUT_MS);
|
|
546
675
|
const args: string[] = [
|
|
547
676
|
"--mode", "json", "-p", "--no-session",
|
|
548
677
|
// Keep extension discovery enabled so custom model providers (for example
|
|
@@ -559,6 +688,7 @@ async function runAgentCli(
|
|
|
559
688
|
const allowedTools = [...new Set([
|
|
560
689
|
...config.tools,
|
|
561
690
|
...codeIntelligenceTools,
|
|
691
|
+
"ues_service",
|
|
562
692
|
...extraTools,
|
|
563
693
|
...(runtimeOptions.compactToolOutput ? ["ues_evidence_get"] : []),
|
|
564
694
|
])];
|
|
@@ -588,9 +718,11 @@ async function runAgentCli(
|
|
|
588
718
|
env: {
|
|
589
719
|
...process.env,
|
|
590
720
|
UES_CHILD_PROCESS: "1",
|
|
721
|
+
UES_CHILD_AGENT: agent,
|
|
591
722
|
UES_CHILD_TOOL_COMPACTION: runtimeOptions.compactToolOutput ? "1" : "0",
|
|
592
723
|
UES_CHILD_TOOL_OUTPUT_LIMIT: String(runtimeOptions.toolOutputLimit || 24 * 1024),
|
|
593
724
|
UES_CHILD_VERIFICATION_TIMEOUT_SEC: String(runtimeOptions.verificationTimeoutSec || 300),
|
|
725
|
+
UES_CHILD_ALLOW_LOCAL_ENV_WRITE: runtimeOptions.allowLocalEnvWrite ? "1" : "0",
|
|
594
726
|
},
|
|
595
727
|
shell: false,
|
|
596
728
|
detached: process.platform !== "win32",
|
|
@@ -599,6 +731,12 @@ async function runAgentCli(
|
|
|
599
731
|
});
|
|
600
732
|
const childStartedAt = Date.now();
|
|
601
733
|
let lastActivityAt = childStartedAt;
|
|
734
|
+
const adaptiveDeadline = createAdaptiveDeadline({
|
|
735
|
+
hardTimeoutMs,
|
|
736
|
+
absoluteHardTimeoutMs,
|
|
737
|
+
activityExtensionMs,
|
|
738
|
+
activityWindowMs,
|
|
739
|
+
}, childStartedAt);
|
|
602
740
|
let buffer = "";
|
|
603
741
|
let settled = false;
|
|
604
742
|
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
@@ -722,19 +860,20 @@ async function runAgentCli(
|
|
|
722
860
|
heartbeatTimer.unref?.();
|
|
723
861
|
watchdogTimer = setInterval(() => {
|
|
724
862
|
const now = Date.now();
|
|
725
|
-
|
|
863
|
+
const deadline = adaptiveDeadline.shouldAbort(now, lastActivityAt);
|
|
864
|
+
if (deadline.abort) {
|
|
726
865
|
terminateForTimeout("hard");
|
|
727
866
|
return;
|
|
728
867
|
}
|
|
729
868
|
if (
|
|
730
869
|
lastToolErrorAt > 0 &&
|
|
731
|
-
now - lastToolErrorAt >=
|
|
732
|
-
now - lastActivityAt >=
|
|
870
|
+
now - lastToolErrorAt >= postToolErrorIdleTimeoutMs &&
|
|
871
|
+
now - lastActivityAt >= postToolErrorIdleTimeoutMs
|
|
733
872
|
) {
|
|
734
873
|
terminateForTimeout("post-tool-error");
|
|
735
874
|
return;
|
|
736
875
|
}
|
|
737
|
-
if (now - lastActivityAt >=
|
|
876
|
+
if (now - lastActivityAt >= idleTimeoutMs) {
|
|
738
877
|
terminateForTimeout("idle");
|
|
739
878
|
}
|
|
740
879
|
}, 1000);
|
|
@@ -787,8 +926,13 @@ async function runAgentCli(
|
|
|
787
926
|
lastToolErrorEvidence = "";
|
|
788
927
|
}
|
|
789
928
|
}
|
|
790
|
-
if (event.type === "message_update"
|
|
791
|
-
usage = event.usage;
|
|
929
|
+
if (event.type === "message_update") {
|
|
930
|
+
if (event.usage) usage = event.usage;
|
|
931
|
+
if (event.message?.role === "assistant") {
|
|
932
|
+
const partial = extractAssistantText(event.message);
|
|
933
|
+
if (partial) output = partial;
|
|
934
|
+
seenModel = event.message.model || seenModel;
|
|
935
|
+
}
|
|
792
936
|
}
|
|
793
937
|
if (event.type === "message_end" && event.message) {
|
|
794
938
|
const text = extractAssistantText(event.message);
|
|
@@ -897,9 +1041,22 @@ async function runAgentRpc(
|
|
|
897
1041
|
compactToolOutput?: boolean;
|
|
898
1042
|
toolOutputLimit?: number;
|
|
899
1043
|
verificationTimeoutSec?: number;
|
|
1044
|
+
hardTimeoutMs?: number;
|
|
1045
|
+
absoluteHardTimeoutMs?: number;
|
|
1046
|
+
activityExtensionMs?: number;
|
|
1047
|
+
activityWindowMs?: number;
|
|
1048
|
+
idleTimeoutMs?: number;
|
|
1049
|
+
postToolErrorIdleTimeoutMs?: number;
|
|
1050
|
+
allowLocalEnvWrite?: boolean;
|
|
900
1051
|
} = {},
|
|
901
1052
|
): Promise<RunResult> {
|
|
902
1053
|
const config = AGENTS[agent];
|
|
1054
|
+
const hardTimeoutMs = Number(runtimeOptions.hardTimeoutMs || CHILD_HARD_TIMEOUT_MS);
|
|
1055
|
+
const absoluteHardTimeoutMs = Number(runtimeOptions.absoluteHardTimeoutMs || hardTimeoutMs);
|
|
1056
|
+
const activityExtensionMs = Number(runtimeOptions.activityExtensionMs || 0);
|
|
1057
|
+
const activityWindowMs = Number(runtimeOptions.activityWindowMs || Math.max(5_000, Math.min(activityExtensionMs || hardTimeoutMs, 30_000)));
|
|
1058
|
+
const idleTimeoutMs = Number(runtimeOptions.idleTimeoutMs || CHILD_IDLE_TIMEOUT_MS);
|
|
1059
|
+
const postToolErrorIdleTimeoutMs = Number(runtimeOptions.postToolErrorIdleTimeoutMs || POST_TOOL_ERROR_IDLE_TIMEOUT_MS);
|
|
903
1060
|
const args: string[] = [
|
|
904
1061
|
"--mode", "rpc", "--no-session",
|
|
905
1062
|
"--no-skills", "--no-prompt-templates", "--no-context-files",
|
|
@@ -907,8 +1064,13 @@ async function runAgentRpc(
|
|
|
907
1064
|
];
|
|
908
1065
|
if (model) args.push("--model", model);
|
|
909
1066
|
if (thinkingLevel) args.push("--thinking", thinkingLevel);
|
|
1067
|
+
const codeIntelligenceTools = WRITE_AGENTS.has(agent)
|
|
1068
|
+
? ["ues_code", "ues_code_edit"]
|
|
1069
|
+
: ["ues_code"];
|
|
910
1070
|
const allowedTools = [...new Set([
|
|
911
1071
|
...config.tools,
|
|
1072
|
+
...codeIntelligenceTools,
|
|
1073
|
+
"ues_service",
|
|
912
1074
|
...extraTools,
|
|
913
1075
|
...(runtimeOptions.compactToolOutput ? ["ues_evidence_get"] : []),
|
|
914
1076
|
])];
|
|
@@ -924,6 +1086,7 @@ async function runAgentRpc(
|
|
|
924
1086
|
Boolean(runtimeOptions.compactToolOutput),
|
|
925
1087
|
Number(runtimeOptions.toolOutputLimit || 0),
|
|
926
1088
|
Number(runtimeOptions.verificationTimeoutSec || 0),
|
|
1089
|
+
Boolean(runtimeOptions.allowLocalEnvWrite),
|
|
927
1090
|
]);
|
|
928
1091
|
const taskInput = `Task: ${task}\n`;
|
|
929
1092
|
const startedAt = Date.now();
|
|
@@ -960,17 +1123,22 @@ async function runAgentRpc(
|
|
|
960
1123
|
env: {
|
|
961
1124
|
...process.env,
|
|
962
1125
|
UES_CHILD_PROCESS: "1",
|
|
1126
|
+
UES_CHILD_AGENT: agent,
|
|
963
1127
|
UES_CHILD_TOOL_COMPACTION: runtimeOptions.compactToolOutput ? "1" : "0",
|
|
964
1128
|
UES_CHILD_TOOL_OUTPUT_LIMIT: String(runtimeOptions.toolOutputLimit || 24 * 1024),
|
|
965
1129
|
UES_CHILD_VERIFICATION_TIMEOUT_SEC: String(runtimeOptions.verificationTimeoutSec || 300),
|
|
1130
|
+
UES_CHILD_ALLOW_LOCAL_ENV_WRITE: runtimeOptions.allowLocalEnvWrite ? "1" : "0",
|
|
966
1131
|
},
|
|
967
1132
|
},
|
|
968
1133
|
taskInput,
|
|
969
1134
|
{
|
|
970
1135
|
signal,
|
|
971
|
-
hardTimeoutMs
|
|
972
|
-
|
|
973
|
-
|
|
1136
|
+
hardTimeoutMs,
|
|
1137
|
+
absoluteHardTimeoutMs,
|
|
1138
|
+
activityExtensionMs,
|
|
1139
|
+
activityWindowMs,
|
|
1140
|
+
idleTimeoutMs,
|
|
1141
|
+
postToolErrorIdleTimeoutMs,
|
|
974
1142
|
onEvent: (event: any) => {
|
|
975
1143
|
lastActivityAt = Date.now();
|
|
976
1144
|
if (event.type === "tool_execution_start") {
|
|
@@ -1072,12 +1240,16 @@ async function runAgentRpc(
|
|
|
1072
1240
|
if ((error as any)?.uesRpcPhase === "runtime") {
|
|
1073
1241
|
const timeout = /hard-timeout|idle-timeout/i.test(message);
|
|
1074
1242
|
const toolStall = /post-tool-error-stall/i.test(message);
|
|
1243
|
+
const partialOutput = extractAssistantText((error as any)?.partialMessage);
|
|
1244
|
+
const recoveredOutput = partialOutput
|
|
1245
|
+
? partialOutput + "\n\n[UES transport note: " + message + "]"
|
|
1246
|
+
: message;
|
|
1075
1247
|
return {
|
|
1076
1248
|
agent,
|
|
1077
1249
|
task,
|
|
1078
1250
|
cwd,
|
|
1079
1251
|
exitCode: timeout ? 124 : toolStall ? 125 : 1,
|
|
1080
|
-
output:
|
|
1252
|
+
output: recoveredOutput,
|
|
1081
1253
|
stderr: message,
|
|
1082
1254
|
model,
|
|
1083
1255
|
stopReason: timeout ? "timeout" : toolStall ? "tool-error-stall" : "rpc-runtime-error",
|
|
@@ -1109,6 +1281,13 @@ async function runAgent(
|
|
|
1109
1281
|
compactToolOutput?: boolean;
|
|
1110
1282
|
toolOutputLimit?: number;
|
|
1111
1283
|
verificationTimeoutSec?: number;
|
|
1284
|
+
hardTimeoutMs?: number;
|
|
1285
|
+
absoluteHardTimeoutMs?: number;
|
|
1286
|
+
activityExtensionMs?: number;
|
|
1287
|
+
activityWindowMs?: number;
|
|
1288
|
+
idleTimeoutMs?: number;
|
|
1289
|
+
postToolErrorIdleTimeoutMs?: number;
|
|
1290
|
+
allowLocalEnvWrite?: boolean;
|
|
1112
1291
|
} = {},
|
|
1113
1292
|
): Promise<RunResult> {
|
|
1114
1293
|
if (CHILD_RUNTIME !== "cli") {
|
|
@@ -1251,9 +1430,37 @@ function cachedContextKey(
|
|
|
1251
1430
|
role: string,
|
|
1252
1431
|
budget: number,
|
|
1253
1432
|
fingerprint = "unknown",
|
|
1433
|
+
namespace = cwd,
|
|
1254
1434
|
) {
|
|
1255
1435
|
const auxStamp = runtimeContextAuxStamp(cwd);
|
|
1256
|
-
return [
|
|
1436
|
+
return [path.resolve(namespace), fingerprint, auxStamp, role, String(budget), task].join("\u0000");
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
function deepExplorationContract(role: string) {
|
|
1440
|
+
const common = [
|
|
1441
|
+
"## UES DEEP bounded exploration",
|
|
1442
|
+
"Quality gates remain unchanged. Reduce latency by avoiding duplicate discovery, not by skipping evidence.",
|
|
1443
|
+
"Use the supplied runtime context pack, hierarchy, ranked references and declared files before broad repository exploration.",
|
|
1444
|
+
"Prefer targeted ues_code/direct reads. Do not inventory the repository or repeat unchanged reads unless a named evidence gap requires it.",
|
|
1445
|
+
];
|
|
1446
|
+
if (role === "architect") {
|
|
1447
|
+
common.push(
|
|
1448
|
+
"Stop exploring once every planned task has exact repository-relative scope, dependency ordering, observable acceptance, concrete verification, and risk/rollback coverage.",
|
|
1449
|
+
);
|
|
1450
|
+
} else if (role === "plan-checker") {
|
|
1451
|
+
common.push(
|
|
1452
|
+
"Inspect only disputed or unproven plan properties; once each blocking property is proven or rejected, return PASS/REVISE immediately.",
|
|
1453
|
+
);
|
|
1454
|
+
} else if (role === "executor") {
|
|
1455
|
+
common.push(
|
|
1456
|
+
"Start with declared task files and direct consumers/tests only. Once the safe minimal edit is supported by evidence, implement and spend remaining effort on fresh verification rather than more discovery.",
|
|
1457
|
+
);
|
|
1458
|
+
} else if (role === "debugger") {
|
|
1459
|
+
common.push(
|
|
1460
|
+
"Investigate only the concrete failure path and nearest evidence needed to reject/confirm the active hypothesis; avoid general repository surveys.",
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
return common.join("\n");
|
|
1257
1464
|
}
|
|
1258
1465
|
|
|
1259
1466
|
function compactContextPack(pack: any, recentFailure?: string) {
|
|
@@ -1304,20 +1511,39 @@ async function runRoutedAgent(
|
|
|
1304
1511
|
signal?: AbortSignal,
|
|
1305
1512
|
onProgress?: Parameters<typeof runAgent>[6],
|
|
1306
1513
|
traceID?: string,
|
|
1514
|
+
taskPolicyOverride?: any,
|
|
1515
|
+
contextCacheNamespace?: string,
|
|
1307
1516
|
): Promise<RunResult> {
|
|
1517
|
+
const routedStartedAt = Date.now();
|
|
1308
1518
|
const role = roleForAgent(agent);
|
|
1519
|
+
const traceRoot = requireGitWorkspaceRoot(cwd, "UES specialist");
|
|
1520
|
+
const workspaceSnapshotStartedAt = Date.now();
|
|
1521
|
+
let agentWorkspaceState: any = null;
|
|
1522
|
+
let workspaceState: any = {
|
|
1523
|
+
cacheable: false,
|
|
1524
|
+
fingerprint: "unknown",
|
|
1525
|
+
changedFiles: [],
|
|
1526
|
+
};
|
|
1527
|
+
try {
|
|
1528
|
+
agentWorkspaceState = captureWorkspaceStateV2(cwd);
|
|
1529
|
+
workspaceState = runtimeWorkspaceSnapshot(cwd, { workspaceState: agentWorkspaceState });
|
|
1530
|
+
} catch {}
|
|
1531
|
+
const agentHygieneBaseline = captureWorkspaceHygieneBaseline(cwd, {
|
|
1532
|
+
workspaceState: agentWorkspaceState,
|
|
1533
|
+
});
|
|
1534
|
+
const workspaceSnapshotMs = Date.now() - workspaceSnapshotStartedAt;
|
|
1309
1535
|
const browserRequested = browserEvidenceNeeded(task, role);
|
|
1310
1536
|
const browserTools = browserRequested
|
|
1311
1537
|
? selectBrowserToolsForTask(HOST_BROWSER_TOOL_NAMES, task, role)
|
|
1312
1538
|
: [];
|
|
1313
|
-
const taskPolicy = classifyEngineeringTask(task);
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1539
|
+
const taskPolicy = taskPolicyOverride || classifyEngineeringTask(task);
|
|
1540
|
+
const turboFast = turboFastPathDecision(taskPolicy, {
|
|
1541
|
+
role,
|
|
1542
|
+
attempt,
|
|
1543
|
+
browserRequested,
|
|
1544
|
+
visualRequired: visualEvidenceNeeded(task),
|
|
1545
|
+
});
|
|
1546
|
+
const fastBoundedContext = turboFast.eligible;
|
|
1321
1547
|
const budgetDecision = adaptiveContextBudget(taskPolicy, role, attempt, {
|
|
1322
1548
|
disabled: !ADAPTIVE_CONTEXT_ENABLED,
|
|
1323
1549
|
});
|
|
@@ -1332,7 +1558,7 @@ async function runRoutedAgent(
|
|
|
1332
1558
|
const modelPolicy = await readModelPolicy(getUesConfigDir());
|
|
1333
1559
|
const selection = resolveCapabilityModel(role, attempt, task, taskPolicy, modelPolicy);
|
|
1334
1560
|
if (traceID) {
|
|
1335
|
-
await appendTrajectoryEvent(
|
|
1561
|
+
await appendTrajectoryEvent(traceRoot, traceID, "agent.started", {
|
|
1336
1562
|
agent,
|
|
1337
1563
|
role,
|
|
1338
1564
|
attempt,
|
|
@@ -1367,14 +1593,6 @@ async function runRoutedAgent(
|
|
|
1367
1593
|
? undefined
|
|
1368
1594
|
: inheritedThinking;
|
|
1369
1595
|
|
|
1370
|
-
let workspaceState: any = {
|
|
1371
|
-
cacheable: false,
|
|
1372
|
-
fingerprint: "unknown",
|
|
1373
|
-
changedFiles: [],
|
|
1374
|
-
};
|
|
1375
|
-
try {
|
|
1376
|
-
workspaceState = runtimeWorkspaceSnapshot(cwd);
|
|
1377
|
-
} catch {}
|
|
1378
1596
|
const workspaceFingerprint = String(workspaceState.fingerprint || "unknown");
|
|
1379
1597
|
|
|
1380
1598
|
let enrichedTask = task;
|
|
@@ -1384,6 +1602,8 @@ async function runRoutedAgent(
|
|
|
1384
1602
|
let affectedTests: any = null;
|
|
1385
1603
|
let reusableVerification: any = null;
|
|
1386
1604
|
let contextCacheHit = false;
|
|
1605
|
+
let contextPerformance: any = null;
|
|
1606
|
+
const contextBuildStartedAt = Date.now();
|
|
1387
1607
|
try {
|
|
1388
1608
|
if (fastBoundedContext) {
|
|
1389
1609
|
contextQuality = { schemaVersion: 1, profile: "fast-bounded", bounded: true };
|
|
@@ -1397,12 +1617,17 @@ async function runRoutedAgent(
|
|
|
1397
1617
|
: "Verify every explicit acceptance clause independently. Read the final diff/target file, then run a narrow behavioral check. Compilation or syntax alone is not proof. If any enumerated edge/error/idempotency/non-mutation case lacks fresh executable evidence, return FAIL.",
|
|
1398
1618
|
].join("\n");
|
|
1399
1619
|
} else {
|
|
1620
|
+
const cacheBudgetKey =
|
|
1621
|
+
["architect", "plan-checker"].includes(role)
|
|
1622
|
+
? Number(budgetDecision.baseBudget || budgetDecision.budget)
|
|
1623
|
+
: budgetDecision.budget;
|
|
1400
1624
|
const cacheKey = cachedContextKey(
|
|
1401
1625
|
cwd,
|
|
1402
1626
|
task,
|
|
1403
1627
|
role,
|
|
1404
|
-
|
|
1628
|
+
cacheBudgetKey,
|
|
1405
1629
|
workspaceFingerprint,
|
|
1630
|
+
contextCacheNamespace || cwd,
|
|
1406
1631
|
);
|
|
1407
1632
|
const cacheableContext =
|
|
1408
1633
|
workspaceState.cacheable === true &&
|
|
@@ -1431,8 +1656,9 @@ async function runRoutedAgent(
|
|
|
1431
1656
|
cwd,
|
|
1432
1657
|
task,
|
|
1433
1658
|
role,
|
|
1434
|
-
|
|
1659
|
+
cacheBudgetKey,
|
|
1435
1660
|
workspaceFingerprint,
|
|
1661
|
+
contextCacheNamespace || cwd,
|
|
1436
1662
|
);
|
|
1437
1663
|
if (postBuildKey !== cacheKey) rememberContextPack(postBuildKey, pack);
|
|
1438
1664
|
}
|
|
@@ -1473,6 +1699,7 @@ async function runRoutedAgent(
|
|
|
1473
1699
|
}
|
|
1474
1700
|
|
|
1475
1701
|
contextQuality = pack.contextQuality;
|
|
1702
|
+
contextPerformance = pack.performance || null;
|
|
1476
1703
|
enrichedTask = [
|
|
1477
1704
|
task,
|
|
1478
1705
|
"",
|
|
@@ -1499,6 +1726,10 @@ async function runRoutedAgent(
|
|
|
1499
1726
|
row.stdoutPreview ? ` preview: ${cap(String(row.stdoutPreview).replace(/\s+/g, " "), 900)}` : "",
|
|
1500
1727
|
].filter(Boolean).join("\n")).join("\n")
|
|
1501
1728
|
: "",
|
|
1729
|
+
taskPolicy.executionProfile === "deep" &&
|
|
1730
|
+
["architect", "plan-checker", "executor", "debugger"].includes(role)
|
|
1731
|
+
? "\n" + deepExplorationContract(role)
|
|
1732
|
+
: "",
|
|
1502
1733
|
].filter(Boolean).join("\n");
|
|
1503
1734
|
}
|
|
1504
1735
|
} catch (error) {
|
|
@@ -1507,6 +1738,7 @@ async function runRoutedAgent(
|
|
|
1507
1738
|
enrichedTask += "\n\n## Previous failed verification\n" + cap(recentFailure, 5000);
|
|
1508
1739
|
}
|
|
1509
1740
|
}
|
|
1741
|
+
const contextBuildMs = Date.now() - contextBuildStartedAt;
|
|
1510
1742
|
|
|
1511
1743
|
if (browserRequested) {
|
|
1512
1744
|
enrichedTask += browserTools.length
|
|
@@ -1525,8 +1757,40 @@ async function runRoutedAgent(
|
|
|
1525
1757
|
].join("\n");
|
|
1526
1758
|
}
|
|
1527
1759
|
|
|
1760
|
+
const planningBudget = planningRuntimeBudget(role, attempt, {
|
|
1761
|
+
executionProfile: taskPolicy.executionProfile,
|
|
1762
|
+
risk: taskPolicy.risk,
|
|
1763
|
+
taskChars: task.length,
|
|
1764
|
+
});
|
|
1528
1765
|
const startedAt = Date.now();
|
|
1529
|
-
const
|
|
1766
|
+
const artifactRoot = await taskSandboxOwnerRoot(cwd).catch(() => null) || traceRoot;
|
|
1767
|
+
const childArtifact = await createSubagentArtifact(artifactRoot, {
|
|
1768
|
+
agent,
|
|
1769
|
+
role,
|
|
1770
|
+
attempt,
|
|
1771
|
+
task,
|
|
1772
|
+
traceID,
|
|
1773
|
+
model: selectedModel,
|
|
1774
|
+
modelTier: selection.tier,
|
|
1775
|
+
workspaceFingerprint,
|
|
1776
|
+
}).catch(() => null);
|
|
1777
|
+
if (childArtifact?.handle) {
|
|
1778
|
+
try {
|
|
1779
|
+
onProgress?.({
|
|
1780
|
+
agent,
|
|
1781
|
+
elapsedMs: 0,
|
|
1782
|
+
idleMs: 0,
|
|
1783
|
+
toolCalls: 0,
|
|
1784
|
+
model: selectedModel,
|
|
1785
|
+
phase: "running",
|
|
1786
|
+
note: `subagent handle ${childArtifact.handle}; durable status ${childArtifact.file}`,
|
|
1787
|
+
});
|
|
1788
|
+
} catch {}
|
|
1789
|
+
}
|
|
1790
|
+
let result: RunResult;
|
|
1791
|
+
const modelRunStartedAt = Date.now();
|
|
1792
|
+
try {
|
|
1793
|
+
result = await runAgent(
|
|
1530
1794
|
agent,
|
|
1531
1795
|
enrichedTask,
|
|
1532
1796
|
cwd,
|
|
@@ -1546,15 +1810,90 @@ async function runRoutedAgent(
|
|
|
1546
1810
|
? 24 * 1024
|
|
1547
1811
|
: 48 * 1024,
|
|
1548
1812
|
verificationTimeoutSec:
|
|
1549
|
-
|
|
1550
|
-
?
|
|
1551
|
-
: taskPolicy.
|
|
1552
|
-
?
|
|
1553
|
-
: taskPolicy.executionProfile === "
|
|
1554
|
-
?
|
|
1555
|
-
:
|
|
1556
|
-
|
|
1557
|
-
|
|
1813
|
+
turboFast.eligible
|
|
1814
|
+
? TURBO_FAST_TIMEOUTS.verificationTimeoutSec
|
|
1815
|
+
: taskPolicy.risk === "high"
|
|
1816
|
+
? 900
|
|
1817
|
+
: taskPolicy.executionProfile === "fast"
|
|
1818
|
+
? 120
|
|
1819
|
+
: taskPolicy.executionProfile === "standard"
|
|
1820
|
+
? 300
|
|
1821
|
+
: 600,
|
|
1822
|
+
hardTimeoutMs:
|
|
1823
|
+
turboFast.eligible
|
|
1824
|
+
? TURBO_FAST_TIMEOUTS.hardTimeoutMs
|
|
1825
|
+
: planningBudget?.hardTimeoutMs || CHILD_HARD_TIMEOUT_MS,
|
|
1826
|
+
absoluteHardTimeoutMs:
|
|
1827
|
+
turboFast.eligible
|
|
1828
|
+
? TURBO_FAST_TIMEOUTS.hardTimeoutMs
|
|
1829
|
+
: planningBudget?.absoluteHardTimeoutMs || CHILD_HARD_TIMEOUT_MS,
|
|
1830
|
+
activityExtensionMs:
|
|
1831
|
+
turboFast.eligible ? 0 : planningBudget?.activityExtensionMs || 0,
|
|
1832
|
+
activityWindowMs:
|
|
1833
|
+
turboFast.eligible ? 0 : planningBudget?.activityWindowMs || 0,
|
|
1834
|
+
idleTimeoutMs:
|
|
1835
|
+
turboFast.eligible
|
|
1836
|
+
? TURBO_FAST_TIMEOUTS.idleTimeoutMs
|
|
1837
|
+
: planningBudget?.idleTimeoutMs || CHILD_IDLE_TIMEOUT_MS,
|
|
1838
|
+
postToolErrorIdleTimeoutMs:
|
|
1839
|
+
turboFast.eligible
|
|
1840
|
+
? TURBO_FAST_TIMEOUTS.postToolErrorIdleTimeoutMs
|
|
1841
|
+
: planningBudget?.idleTimeoutMs
|
|
1842
|
+
? Math.min(POST_TOOL_ERROR_IDLE_TIMEOUT_MS, planningBudget.idleTimeoutMs)
|
|
1843
|
+
: POST_TOOL_ERROR_IDLE_TIMEOUT_MS,
|
|
1844
|
+
allowLocalEnvWrite:
|
|
1845
|
+
taskPolicy.localEnvWriteExplicitlyAllowed === true ||
|
|
1846
|
+
taskExplicitlyAllowsLocalEnvWrite(task),
|
|
1847
|
+
},
|
|
1848
|
+
);
|
|
1849
|
+
} catch (error) {
|
|
1850
|
+
if (childArtifact?.handle) {
|
|
1851
|
+
await failSubagentArtifact(artifactRoot, childArtifact.handle, error).catch(() => null);
|
|
1852
|
+
}
|
|
1853
|
+
throw error;
|
|
1854
|
+
}
|
|
1855
|
+
const modelRunMs = Date.now() - modelRunStartedAt;
|
|
1856
|
+
const hygieneStartedAt = Date.now();
|
|
1857
|
+
const postRunHygiene = await postRunFileHygiene(cwd, {
|
|
1858
|
+
baseline: agentHygieneBaseline,
|
|
1859
|
+
taskText: task,
|
|
1860
|
+
allowSourceMutations: WRITE_AGENTS.has(agent),
|
|
1861
|
+
autoClean: true,
|
|
1862
|
+
}).catch((error) => ({
|
|
1863
|
+
schemaVersion: 1,
|
|
1864
|
+
safe: false,
|
|
1865
|
+
changed: [],
|
|
1866
|
+
created: [],
|
|
1867
|
+
removed: [],
|
|
1868
|
+
findings: [{
|
|
1869
|
+
kind: "hygiene-audit-error",
|
|
1870
|
+
file: ".",
|
|
1871
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1872
|
+
}],
|
|
1873
|
+
summary: "workspace hygiene audit failed: " + (error instanceof Error ? error.message : String(error)),
|
|
1874
|
+
}));
|
|
1875
|
+
if (!postRunHygiene.safe) {
|
|
1876
|
+
const hygieneText = [
|
|
1877
|
+
"UES post-run workspace hygiene guard rejected this child run.",
|
|
1878
|
+
postRunHygiene.summary || "workspace hygiene failed",
|
|
1879
|
+
].join("\n");
|
|
1880
|
+
result = {
|
|
1881
|
+
...result,
|
|
1882
|
+
exitCode: result.exitCode === 0 ? 2 : result.exitCode,
|
|
1883
|
+
stopReason: "error",
|
|
1884
|
+
errorMessage: hygieneText,
|
|
1885
|
+
output: [result.output, "", hygieneText].filter(Boolean).join("\n"),
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
const hygieneMs = Date.now() - hygieneStartedAt;
|
|
1890
|
+
const finalizedChildArtifact = childArtifact?.handle
|
|
1891
|
+
? await finalizeSubagentArtifact(artifactRoot, childArtifact.handle, {
|
|
1892
|
+
...result,
|
|
1893
|
+
durationMs: Date.now() - startedAt,
|
|
1894
|
+
verdict: verdictFromOutput(result.output),
|
|
1895
|
+
}).catch(() => null)
|
|
1896
|
+
: null;
|
|
1558
1897
|
const enrichedResult: RunResult = {
|
|
1559
1898
|
...result,
|
|
1560
1899
|
task,
|
|
@@ -1581,13 +1920,39 @@ async function runRoutedAgent(
|
|
|
1581
1920
|
!["high", "critical"].includes(String(taskPolicy.risk || "").toLowerCase()),
|
|
1582
1921
|
runtimeContextBudget: budgetDecision.budget,
|
|
1583
1922
|
baseContextBudget: budgetDecision.baseBudget,
|
|
1923
|
+
turboFastPath: turboFast.eligible,
|
|
1924
|
+
turboFastStrategy: turboFast.strategy,
|
|
1925
|
+
turboFastTimeouts: turboFast.eligible ? TURBO_FAST_TIMEOUTS : null,
|
|
1926
|
+
planningRuntimeBudget: planningBudget,
|
|
1927
|
+
affectedTestInventorySource: affectedTests?.inventorySource || null,
|
|
1928
|
+
contextPerformance,
|
|
1929
|
+
latencyMs: {
|
|
1930
|
+
workspaceSnapshot: workspaceSnapshotMs,
|
|
1931
|
+
contextBuild: contextBuildMs,
|
|
1932
|
+
modelRun: modelRunMs,
|
|
1933
|
+
hygiene: hygieneMs,
|
|
1934
|
+
total: Date.now() - routedStartedAt,
|
|
1935
|
+
},
|
|
1584
1936
|
},
|
|
1585
1937
|
verdict: verdictFromOutput(result.output),
|
|
1586
1938
|
report: parseStructuredReport(result.output),
|
|
1587
1939
|
durationMs: Date.now() - startedAt,
|
|
1940
|
+
subagentArtifact: finalizedChildArtifact ? {
|
|
1941
|
+
handle: finalizedChildArtifact.handle,
|
|
1942
|
+
status: finalizedChildArtifact.status,
|
|
1943
|
+
file: finalizedChildArtifact.file,
|
|
1944
|
+
taskRef: finalizedChildArtifact.taskRef,
|
|
1945
|
+
outputRef: finalizedChildArtifact.result?.outputRef || null,
|
|
1946
|
+
resume: finalizedChildArtifact.resume || null,
|
|
1947
|
+
} : childArtifact ? {
|
|
1948
|
+
handle: childArtifact.handle,
|
|
1949
|
+
status: childArtifact.status,
|
|
1950
|
+
file: childArtifact.file,
|
|
1951
|
+
taskRef: childArtifact.taskRef,
|
|
1952
|
+
} : null,
|
|
1588
1953
|
};
|
|
1589
1954
|
if (traceID) {
|
|
1590
|
-
await appendTrajectoryEvent(
|
|
1955
|
+
await appendTrajectoryEvent(traceRoot, traceID, "agent.completed", {
|
|
1591
1956
|
agent,
|
|
1592
1957
|
role,
|
|
1593
1958
|
attempt,
|
|
@@ -1683,19 +2048,64 @@ function extractMarkedJson(output: string, marker: string) {
|
|
|
1683
2048
|
return null;
|
|
1684
2049
|
}
|
|
1685
2050
|
|
|
1686
|
-
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1689
|
-
|
|
2051
|
+
function sandboxSourceIntentBatches(values: string[]) {
|
|
2052
|
+
const batches: string[][] = [];
|
|
2053
|
+
let batch: string[] = [];
|
|
2054
|
+
let chars = 0;
|
|
2055
|
+
for (const value of sourceFacingPaths(values)) {
|
|
2056
|
+
const cost = value.length + 3;
|
|
2057
|
+
if (batch.length && (batch.length >= 64 || chars + cost > 12_000)) {
|
|
2058
|
+
batches.push(batch);
|
|
2059
|
+
batch = [];
|
|
2060
|
+
chars = 0;
|
|
2061
|
+
}
|
|
2062
|
+
batch.push(value);
|
|
2063
|
+
chars += cost;
|
|
2064
|
+
}
|
|
2065
|
+
if (batch.length) batches.push(batch);
|
|
2066
|
+
return batches;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
async function sandboxIntentToAddSourceFiles(dir: string, signal?: AbortSignal) {
|
|
2070
|
+
const pathspecs = sourceGitPathspecs();
|
|
2071
|
+
const untracked = await runProcess(
|
|
2072
|
+
"git",
|
|
2073
|
+
["ls-files", "--others", "--exclude-standard", "-z", "--", ...pathspecs],
|
|
2074
|
+
dir,
|
|
2075
|
+
signal,
|
|
2076
|
+
);
|
|
2077
|
+
if (untracked.exitCode !== 0) {
|
|
2078
|
+
throw new Error(untracked.stderr || untracked.stdout || "git ls-files for intent-to-add failed");
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
const candidates = sourceFacingPaths(
|
|
2082
|
+
untracked.stdout
|
|
2083
|
+
.split("\0")
|
|
2084
|
+
.map((value) => value.replaceAll("\\", "/"))
|
|
2085
|
+
.filter(Boolean),
|
|
2086
|
+
);
|
|
2087
|
+
for (const batch of sandboxSourceIntentBatches(candidates)) {
|
|
2088
|
+
const intent = await runProcess("git", ["add", "-N", "--", ...batch], dir, signal);
|
|
2089
|
+
if (intent.exitCode !== 0) {
|
|
2090
|
+
throw new Error(intent.stderr || intent.stdout || "git add -N failed");
|
|
2091
|
+
}
|
|
1690
2092
|
}
|
|
1691
|
-
|
|
2093
|
+
return candidates;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
async function sandboxChangedFiles(dir: string, base: string, signal?: AbortSignal) {
|
|
2097
|
+
const pathspecs = sourceGitPathspecs();
|
|
2098
|
+
await sandboxIntentToAddSourceFiles(dir, signal);
|
|
2099
|
+
const diff = await runProcess("git", ["diff", "--name-only", base, "--", ...pathspecs], dir, signal);
|
|
1692
2100
|
if (diff.exitCode !== 0) {
|
|
1693
2101
|
throw new Error(diff.stderr || diff.stdout || "git diff --name-only failed");
|
|
1694
2102
|
}
|
|
1695
|
-
return
|
|
1696
|
-
.
|
|
1697
|
-
|
|
1698
|
-
|
|
2103
|
+
return sourceFacingPaths(
|
|
2104
|
+
diff.stdout
|
|
2105
|
+
.split(/\r?\n/)
|
|
2106
|
+
.map((value) => value.trim().replaceAll("\\", "/"))
|
|
2107
|
+
.filter(Boolean),
|
|
2108
|
+
);
|
|
1699
2109
|
}
|
|
1700
2110
|
|
|
1701
2111
|
async function cleanupSandboxes(
|
|
@@ -1704,11 +2114,30 @@ async function cleanupSandboxes(
|
|
|
1704
2114
|
) {
|
|
1705
2115
|
for (const item of prepared) {
|
|
1706
2116
|
if (!item.sandbox?.dir) continue;
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
2117
|
+
const resolved = path.resolve(item.sandbox.dir);
|
|
2118
|
+
try {
|
|
2119
|
+
await removeTaskSandbox(root, resolved, {
|
|
2120
|
+
force: true,
|
|
2121
|
+
deleteBranch: true,
|
|
2122
|
+
});
|
|
2123
|
+
ACTIVE_TASK_SANDBOXES.delete(resolved);
|
|
2124
|
+
} catch {}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
async function cleanupTraceSandboxes(root: string, traceID: string) {
|
|
2129
|
+
const targets = [...ACTIVE_TASK_SANDBOXES.entries()]
|
|
2130
|
+
.filter(([, ownerTrace]) => ownerTrace === traceID)
|
|
2131
|
+
.map(([dir]) => dir);
|
|
2132
|
+
let removed = 0;
|
|
2133
|
+
for (const dir of targets) {
|
|
2134
|
+
try {
|
|
2135
|
+
await removeTaskSandbox(root, dir, { force: true, deleteBranch: true });
|
|
2136
|
+
ACTIVE_TASK_SANDBOXES.delete(dir);
|
|
2137
|
+
removed += 1;
|
|
2138
|
+
} catch {}
|
|
1711
2139
|
}
|
|
2140
|
+
return removed;
|
|
1712
2141
|
}
|
|
1713
2142
|
|
|
1714
2143
|
async function executeStructuredPlan(input: {
|
|
@@ -1717,6 +2146,7 @@ async function executeStructuredPlan(input: {
|
|
|
1717
2146
|
inheritedModel?: string;
|
|
1718
2147
|
inheritedThinking?: string;
|
|
1719
2148
|
maxAttempts: number;
|
|
2149
|
+
rootPolicy?: any;
|
|
1720
2150
|
durableSlug?: string;
|
|
1721
2151
|
traceID?: string;
|
|
1722
2152
|
signal?: AbortSignal;
|
|
@@ -1789,6 +2219,7 @@ async function executeStructuredPlan(input: {
|
|
|
1789
2219
|
for (let waveIndex = 0; waveIndex < safe.waves.length; waveIndex++) {
|
|
1790
2220
|
const ids = safe.waves[waveIndex];
|
|
1791
2221
|
let lastWaveFailure = "";
|
|
2222
|
+
const failureByTask = new Map<string, string>();
|
|
1792
2223
|
|
|
1793
2224
|
for (let attempt = 1; attempt <= input.maxAttempts; attempt++) {
|
|
1794
2225
|
const prepared: Array<{
|
|
@@ -1807,12 +2238,13 @@ async function executeStructuredPlan(input: {
|
|
|
1807
2238
|
let cwd = input.root;
|
|
1808
2239
|
let sandbox: any = undefined;
|
|
1809
2240
|
|
|
1810
|
-
if (gitCapable) {
|
|
2241
|
+
if (gitCapable && writeFiles.length > 0) {
|
|
1811
2242
|
const slug = "runtime-" + randomUUID().slice(0, 8) + "-w" + waveIndex + "-a" + attempt;
|
|
1812
2243
|
sandbox = await createTaskSandbox(input.root, slug, id, {
|
|
1813
2244
|
inheritDirtyRoot: true,
|
|
1814
2245
|
});
|
|
1815
2246
|
cwd = sandbox.dir;
|
|
2247
|
+
ACTIVE_TASK_SANDBOXES.set(path.resolve(sandbox.dir), String(input.traceID || "structured"));
|
|
1816
2248
|
}
|
|
1817
2249
|
|
|
1818
2250
|
prepared.push({ task, cwd, sandbox, writeFiles });
|
|
@@ -1830,22 +2262,37 @@ async function executeStructuredPlan(input: {
|
|
|
1830
2262
|
}
|
|
1831
2263
|
|
|
1832
2264
|
let completed = 0;
|
|
2265
|
+
const writerCount = prepared.filter((item) => item.writeFiles.length > 0).length;
|
|
2266
|
+
const waveConcurrency = !gitCapable
|
|
2267
|
+
? 1
|
|
2268
|
+
: writerCount > 0
|
|
2269
|
+
? Math.min(MAX_CONCURRENCY, MAX_WRITER_CONCURRENCY)
|
|
2270
|
+
: MAX_CONCURRENCY;
|
|
1833
2271
|
const waveResults = await mapLimit(
|
|
1834
2272
|
prepared,
|
|
1835
|
-
|
|
2273
|
+
waveConcurrency,
|
|
1836
2274
|
async (item) => {
|
|
1837
2275
|
const taskText = [
|
|
1838
2276
|
"Execute exactly this structured plan task.",
|
|
1839
2277
|
"Do not broaden file scope. If the declared write file list is empty, do not edit files.",
|
|
2278
|
+
"Start with the declared files/interfaces and supplied context. Do not inventory the repository. Use targeted symbol/path search only for a concrete unresolved acceptance or dependency gap; once the safe edit is understood, implement and verify instead of continuing discovery.",
|
|
1840
2279
|
"",
|
|
1841
2280
|
JSON.stringify(item.task, null, 2),
|
|
1842
2281
|
"",
|
|
1843
|
-
"
|
|
1844
|
-
String(input.plan.goal || ""),
|
|
1845
|
-
|
|
1846
|
-
? "\nFresh failure evidence from the previous wave attempt:\n" + cap(lastWaveFailure, 7000)
|
|
1847
|
-
: "",
|
|
2282
|
+
"Parent goal (context only; never broaden this leaf task):",
|
|
2283
|
+
cap(String(input.plan.goal || ""), 700),
|
|
2284
|
+
input.rootPolicy?.executionContractPrompt || "",
|
|
1848
2285
|
].filter(Boolean).join("\n");
|
|
2286
|
+
const leafPolicy = leafTaskPolicy(item.task, input.rootPolicy || {});
|
|
2287
|
+
const taskFailure = failureByTask.get(String(item.task.id)) || "";
|
|
2288
|
+
const leafVisualRequired = visualEvidenceNeeded(taskText);
|
|
2289
|
+
const leafFastDecision = turboFastPathDecision(leafPolicy, {
|
|
2290
|
+
role: "executor",
|
|
2291
|
+
attempt,
|
|
2292
|
+
browserRequested: browserEvidenceNeeded(taskText, "executor"),
|
|
2293
|
+
visualRequired: leafVisualRequired,
|
|
2294
|
+
});
|
|
2295
|
+
const leafAttemptStartedAt = Date.now();
|
|
1849
2296
|
|
|
1850
2297
|
const plannedExecution: any = dynamicTaskByID.get(item.task.id);
|
|
1851
2298
|
const deterministicReadOnly =
|
|
@@ -1978,8 +2425,8 @@ async function executeStructuredPlan(input: {
|
|
|
1978
2425
|
"",
|
|
1979
2426
|
"Overall goal:",
|
|
1980
2427
|
String(input.plan.goal || ""),
|
|
1981
|
-
|
|
1982
|
-
? "\nFresh failure
|
|
2428
|
+
taskFailure
|
|
2429
|
+
? "\nFresh task-local failure delta from the previous attempt:\n" + cap(taskFailure, 4200)
|
|
1983
2430
|
: "",
|
|
1984
2431
|
].filter(Boolean).join("\n"),
|
|
1985
2432
|
item.cwd,
|
|
@@ -1990,6 +2437,8 @@ async function executeStructuredPlan(input: {
|
|
|
1990
2437
|
input.signal,
|
|
1991
2438
|
undefined,
|
|
1992
2439
|
input.traceID,
|
|
2440
|
+
leafPolicy,
|
|
2441
|
+
input.root,
|
|
1993
2442
|
);
|
|
1994
2443
|
results.push({
|
|
1995
2444
|
wave: waveIndex,
|
|
@@ -2027,8 +2476,8 @@ async function executeStructuredPlan(input: {
|
|
|
2027
2476
|
};
|
|
2028
2477
|
}
|
|
2029
2478
|
|
|
2030
|
-
let focusedFailure =
|
|
2031
|
-
if (attempt > 1 && runtimeFailureNeedsDiagnosis(
|
|
2479
|
+
let focusedFailure = taskFailure;
|
|
2480
|
+
if (attempt > 1 && runtimeFailureNeedsDiagnosis(taskFailure)) {
|
|
2032
2481
|
const diagnosis = await runRoutedAgent(
|
|
2033
2482
|
"ues-debugger",
|
|
2034
2483
|
[
|
|
@@ -2038,16 +2487,18 @@ async function executeStructuredPlan(input: {
|
|
|
2038
2487
|
JSON.stringify(item.task, null, 2),
|
|
2039
2488
|
"",
|
|
2040
2489
|
"Previous runtime failure:",
|
|
2041
|
-
cap(
|
|
2490
|
+
cap(taskFailure, 4200),
|
|
2042
2491
|
].join("\n"),
|
|
2043
2492
|
item.cwd,
|
|
2044
2493
|
input.inheritedModel,
|
|
2045
2494
|
input.inheritedThinking,
|
|
2046
2495
|
attempt,
|
|
2047
|
-
|
|
2496
|
+
taskFailure,
|
|
2048
2497
|
input.signal,
|
|
2049
2498
|
undefined,
|
|
2050
2499
|
input.traceID,
|
|
2500
|
+
leafPolicy,
|
|
2501
|
+
input.root,
|
|
2051
2502
|
);
|
|
2052
2503
|
results.push({
|
|
2053
2504
|
wave: waveIndex,
|
|
@@ -2070,16 +2521,15 @@ async function executeStructuredPlan(input: {
|
|
|
2070
2521
|
}
|
|
2071
2522
|
}
|
|
2072
2523
|
|
|
2524
|
+
const retryDelta = failureDelta(focusedFailure || taskFailure);
|
|
2073
2525
|
const implementation = await runRoutedAgent(
|
|
2074
2526
|
"ues-executor",
|
|
2075
|
-
taskText
|
|
2076
|
-
? "\n\nFocused diagnosis before retry:\n" + cap(focusedFailure, 7000)
|
|
2077
|
-
: ""),
|
|
2527
|
+
taskText,
|
|
2078
2528
|
item.cwd,
|
|
2079
2529
|
input.inheritedModel,
|
|
2080
2530
|
input.inheritedThinking,
|
|
2081
2531
|
attempt,
|
|
2082
|
-
|
|
2532
|
+
retryDelta || undefined,
|
|
2083
2533
|
input.signal,
|
|
2084
2534
|
(progress) => {
|
|
2085
2535
|
input.onUpdate?.({
|
|
@@ -2093,8 +2543,10 @@ async function executeStructuredPlan(input: {
|
|
|
2093
2543
|
});
|
|
2094
2544
|
},
|
|
2095
2545
|
input.traceID,
|
|
2546
|
+
leafPolicy,
|
|
2547
|
+
input.root,
|
|
2096
2548
|
);
|
|
2097
|
-
results.push({ wave: waveIndex, attempt, task: item.task.id, phase: "execute", ...implementation });
|
|
2549
|
+
results.push({ wave: waveIndex, attempt, task: item.task.id, phase: "execute", leafPolicy, ...implementation });
|
|
2098
2550
|
|
|
2099
2551
|
if (implementation.exitCode !== 0 || implementation.stopReason === "error") {
|
|
2100
2552
|
if (!isAbortedRun(implementation)) {
|
|
@@ -2114,7 +2566,75 @@ async function executeStructuredPlan(input: {
|
|
|
2114
2566
|
};
|
|
2115
2567
|
}
|
|
2116
2568
|
|
|
2117
|
-
|
|
2569
|
+
let verification: RunResult;
|
|
2570
|
+
let leafFastGate: any = null;
|
|
2571
|
+
if (leafFastDecision.eligible) {
|
|
2572
|
+
const snapshot = runtimeWorkspaceSnapshot(item.cwd);
|
|
2573
|
+
const receipts = await listReusableVerification(item.cwd, {
|
|
2574
|
+
limit: 12,
|
|
2575
|
+
maxAgeMs: 10 * 60_000,
|
|
2576
|
+
previewBytes: 2200,
|
|
2577
|
+
...(snapshot.cacheable === true && snapshot.fingerprint
|
|
2578
|
+
? { workspaceFingerprint: snapshot.fingerprint }
|
|
2579
|
+
: {}),
|
|
2580
|
+
}).catch(() => ({ results: [] }));
|
|
2581
|
+
leafFastGate = evaluateFastVerificationGate({
|
|
2582
|
+
policy: leafPolicy,
|
|
2583
|
+
implementation,
|
|
2584
|
+
receipts: receipts?.results || [],
|
|
2585
|
+
attemptStartedAtMs: leafAttemptStartedAt,
|
|
2586
|
+
visualRequired: leafVisualRequired,
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
if (leafFastGate?.passed === true) {
|
|
2591
|
+
verification = {
|
|
2592
|
+
agent: "ues-deterministic-verifier",
|
|
2593
|
+
task: taskText,
|
|
2594
|
+
cwd: item.cwd,
|
|
2595
|
+
exitCode: 0,
|
|
2596
|
+
output: [
|
|
2597
|
+
"Per-leaf Turbo verification reused fresh behavioral evidence captured after this leaf implementation.",
|
|
2598
|
+
...leafFastGate.behavioralReceipts.map((entry: any) => "- " + entry.command),
|
|
2599
|
+
"",
|
|
2600
|
+
"UES_VERDICT: PASS",
|
|
2601
|
+
].join("\n"),
|
|
2602
|
+
stderr: "",
|
|
2603
|
+
verdict: "PASS",
|
|
2604
|
+
durationMs: 0,
|
|
2605
|
+
toolCalls: 0,
|
|
2606
|
+
toolNames: [],
|
|
2607
|
+
report: {
|
|
2608
|
+
schemaVersion: 1,
|
|
2609
|
+
valid: true,
|
|
2610
|
+
verdict: "PASS",
|
|
2611
|
+
sections: {
|
|
2612
|
+
"checks-run": leafFastGate.behavioralReceipts.map((entry: any) => entry.command).join("\n"),
|
|
2613
|
+
"acceptance-criteria-proven": "Fresh behavioral receipt(s) exist at the post-implementation workspace fingerprint.",
|
|
2614
|
+
"completion-evidence": "Deterministic per-leaf PASS after this implementation attempt.",
|
|
2615
|
+
},
|
|
2616
|
+
},
|
|
2617
|
+
optimizations: {
|
|
2618
|
+
perLeafTurbo: true,
|
|
2619
|
+
behavioralReceiptCount: leafFastGate.behavioralReceipts.length,
|
|
2620
|
+
},
|
|
2621
|
+
};
|
|
2622
|
+
input.onUpdate?.({
|
|
2623
|
+
content: [{
|
|
2624
|
+
type: "text",
|
|
2625
|
+
text: `UES Per-Leaf Turbo: ${item.task.id} reused ${leafFastGate.behavioralReceipts.length} fresh behavioral receipt(s); skipped verifier model turn`,
|
|
2626
|
+
}],
|
|
2627
|
+
details: {
|
|
2628
|
+
wave: waveIndex,
|
|
2629
|
+
attempt,
|
|
2630
|
+
task: item.task.id,
|
|
2631
|
+
phase: "leaf-turbo-verified",
|
|
2632
|
+
leafPolicy,
|
|
2633
|
+
leafFastGate,
|
|
2634
|
+
},
|
|
2635
|
+
});
|
|
2636
|
+
} else {
|
|
2637
|
+
verification = await runRoutedAgent(
|
|
2118
2638
|
"ues-verifier",
|
|
2119
2639
|
[
|
|
2120
2640
|
"Verify exactly this structured plan task in the current isolated worktree.",
|
|
@@ -2141,8 +2661,11 @@ async function executeStructuredPlan(input: {
|
|
|
2141
2661
|
});
|
|
2142
2662
|
},
|
|
2143
2663
|
input.traceID,
|
|
2664
|
+
leafPolicy,
|
|
2665
|
+
input.root,
|
|
2144
2666
|
);
|
|
2145
|
-
|
|
2667
|
+
}
|
|
2668
|
+
results.push({ wave: waveIndex, attempt, task: item.task.id, phase: "verify", leafPolicy, leafFastGate, ...verification });
|
|
2146
2669
|
const passed = verification.exitCode === 0 && verification.verdict === "PASS";
|
|
2147
2670
|
if (!isAbortedRun(verification)) {
|
|
2148
2671
|
await recordRuntimeOutcome(implementation, taskText, passed, attempt - 1);
|
|
@@ -2188,8 +2711,12 @@ async function executeStructuredPlan(input: {
|
|
|
2188
2711
|
|
|
2189
2712
|
const failed = waveResults.filter((item) => !item.passed);
|
|
2190
2713
|
if (failed.length) {
|
|
2714
|
+
for (const row of failed) {
|
|
2715
|
+
const rawFailure = row.verification?.output || row.implementation?.output || "unknown failure";
|
|
2716
|
+
failureByTask.set(String(row.item?.task?.id || "unknown"), failureDelta(rawFailure));
|
|
2717
|
+
}
|
|
2191
2718
|
lastWaveFailure = failed
|
|
2192
|
-
.map((item) => item.
|
|
2719
|
+
.map((item) => failureByTask.get(String(item.item?.task?.id || "unknown")) || "unknown failure")
|
|
2193
2720
|
.join("\n\n---\n\n");
|
|
2194
2721
|
await failDurablePrepared(prepared, lastWaveFailure);
|
|
2195
2722
|
await cleanupSandboxes(input.root, prepared);
|
|
@@ -2207,9 +2734,9 @@ async function executeStructuredPlan(input: {
|
|
|
2207
2734
|
};
|
|
2208
2735
|
}
|
|
2209
2736
|
|
|
2210
|
-
if (!gitCapable) {
|
|
2211
|
-
//
|
|
2212
|
-
//
|
|
2737
|
+
if (!gitCapable || prepared.every((item) => !item.sandbox?.dir)) {
|
|
2738
|
+
// Read-only waves do not need a duplicate Git worktree. Their fresh
|
|
2739
|
+
// verifier evidence is enough as long as the workspace stays unchanged.
|
|
2213
2740
|
await completeDurableWave(waveResults);
|
|
2214
2741
|
break;
|
|
2215
2742
|
}
|
|
@@ -2217,13 +2744,21 @@ async function executeStructuredPlan(input: {
|
|
|
2217
2744
|
const actualByTask = new Map<string, string[]>();
|
|
2218
2745
|
let scopeFailure = "";
|
|
2219
2746
|
for (const item of prepared) {
|
|
2747
|
+
if (!item.sandbox?.dir) {
|
|
2748
|
+
actualByTask.set(item.task.id, []);
|
|
2749
|
+
continue;
|
|
2750
|
+
}
|
|
2220
2751
|
const changed = await sandboxChangedFiles(item.sandbox.dir, item.sandbox.integrationBase, input.signal);
|
|
2221
2752
|
actualByTask.set(item.task.id, changed);
|
|
2222
2753
|
const allowed = new Set(item.writeFiles);
|
|
2223
2754
|
const unexpected = changed.filter((file) => !allowed.has(file));
|
|
2224
2755
|
if (unexpected.length) {
|
|
2225
|
-
|
|
2226
|
-
`${item.task.id}: changed files outside declared write scope: ${unexpected.join(", ")}
|
|
2756
|
+
const scopeMessage =
|
|
2757
|
+
`${item.task.id}: changed real source files outside declared write scope: ${unexpected.join(", ")}. ` +
|
|
2758
|
+
"On retry, revert or avoid these files and satisfy the task strictly within its declared write scope. " +
|
|
2759
|
+
"Do not silently broaden scope.";
|
|
2760
|
+
failureByTask.set(String(item.task.id), failureDelta(scopeMessage, { maxChars: 2200 }));
|
|
2761
|
+
scopeFailure += scopeMessage + "\n";
|
|
2227
2762
|
}
|
|
2228
2763
|
}
|
|
2229
2764
|
|
|
@@ -2232,7 +2767,12 @@ async function executeStructuredPlan(input: {
|
|
|
2232
2767
|
for (const file of changed) {
|
|
2233
2768
|
const previous = changedOwners.get(file);
|
|
2234
2769
|
if (previous && previous !== taskID) {
|
|
2235
|
-
|
|
2770
|
+
const conflictMessage = `wave conflict: ${previous} and ${taskID} both changed real source file ${file}`;
|
|
2771
|
+
const previousFailure = failureByTask.get(previous) || "";
|
|
2772
|
+
const currentFailure = failureByTask.get(taskID) || "";
|
|
2773
|
+
failureByTask.set(previous, failureDelta([previousFailure, conflictMessage].filter(Boolean).join("\n"), { maxChars: 2200 }));
|
|
2774
|
+
failureByTask.set(taskID, failureDelta([currentFailure, conflictMessage].filter(Boolean).join("\n"), { maxChars: 2200 }));
|
|
2775
|
+
scopeFailure += conflictMessage + "\n";
|
|
2236
2776
|
} else {
|
|
2237
2777
|
changedOwners.set(file, taskID);
|
|
2238
2778
|
}
|
|
@@ -2260,6 +2800,7 @@ async function executeStructuredPlan(input: {
|
|
|
2260
2800
|
const integrated: Array<{ item: any; receipt: any }> = [];
|
|
2261
2801
|
try {
|
|
2262
2802
|
for (const item of prepared) {
|
|
2803
|
+
if (!item.sandbox?.dir) continue;
|
|
2263
2804
|
const receipt = await integrateTaskSandbox(input.root, item.sandbox.dir, { keep: true });
|
|
2264
2805
|
integrated.push({ item, receipt });
|
|
2265
2806
|
integrations.push({ wave: waveIndex, task: item.task.id, ...receipt });
|
|
@@ -2379,7 +2920,86 @@ export default function (pi: ExtensionAPI) {
|
|
|
2379
2920
|
// controller inside specialist workers.
|
|
2380
2921
|
if (process.env.UES_CHILD_PROCESS === "1") return;
|
|
2381
2922
|
|
|
2923
|
+
let directControllerAbort: AbortController | null = null;
|
|
2924
|
+
let promptUesActive = false;
|
|
2925
|
+
let directControllerRunner: ((task: string, ctx: any, admission: "command" | "automatic", admissionDecision?: any) => Promise<void>) | null = null;
|
|
2926
|
+
const AUTO_ADMISSION_ENABLED = !["0", "false", "off"].includes(
|
|
2927
|
+
String(process.env.UES_AUTO_ADMIT || "1").trim().toLowerCase(),
|
|
2928
|
+
);
|
|
2929
|
+
const UES_PARENT_TOOL_NAMES = new Set(["ues_cli", "ues_execute", "ues_service", "ues_dispatch"]);
|
|
2930
|
+
let normalActiveTools: string[] | null = null;
|
|
2931
|
+
|
|
2932
|
+
const currentNonUesTools = () =>
|
|
2933
|
+
pi.getActiveTools().filter((name) => !UES_PARENT_TOOL_NAMES.has(String(name)));
|
|
2934
|
+
|
|
2935
|
+
const deactivateParentUesTools = () => {
|
|
2936
|
+
const normal = currentNonUesTools();
|
|
2937
|
+
normalActiveTools = normal;
|
|
2938
|
+
try { pi.setActiveTools(normal); } catch {}
|
|
2939
|
+
};
|
|
2940
|
+
|
|
2941
|
+
const activateParentUesTools = () => {
|
|
2942
|
+
const normal = normalActiveTools || currentNonUesTools();
|
|
2943
|
+
const registered = new Set(
|
|
2944
|
+
(typeof (pi as any).getAllTools === "function" ? (pi as any).getAllTools() : [])
|
|
2945
|
+
.map((tool: any) => String(tool?.name || "")),
|
|
2946
|
+
);
|
|
2947
|
+
const uesTools = [...UES_PARENT_TOOL_NAMES].filter((name) => registered.has(name));
|
|
2948
|
+
try { pi.setActiveTools([...new Set([...normal, ...uesTools])]); } catch {}
|
|
2949
|
+
};
|
|
2950
|
+
|
|
2951
|
+
const uesModeActive = () =>
|
|
2952
|
+
promptUesActive || Boolean(directControllerAbort && !directControllerAbort.signal.aborted);
|
|
2953
|
+
|
|
2954
|
+
const syncSessionIdentity = (name: string, ctx: any) => {
|
|
2955
|
+
if (!name) return;
|
|
2956
|
+
try { pi.setSessionName(name); } catch {}
|
|
2957
|
+
try { ctx?.ui?.setTitle?.(name); } catch {}
|
|
2958
|
+
};
|
|
2959
|
+
|
|
2960
|
+
pi.on("session_start", async () => {
|
|
2961
|
+
deactivateParentUesTools();
|
|
2962
|
+
});
|
|
2963
|
+
|
|
2964
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
2965
|
+
const root = resolveGitWorkspaceRoot(ctx.cwd || "");
|
|
2966
|
+
if (!root) return undefined;
|
|
2967
|
+
await checkpointDurableWorkBeforeCompaction(root, {
|
|
2968
|
+
reason: event.reason,
|
|
2969
|
+
maxWorkspaces: 3,
|
|
2970
|
+
}).catch(() => []);
|
|
2971
|
+
return undefined;
|
|
2972
|
+
});
|
|
2973
|
+
|
|
2974
|
+
pi.on("session_compact", async (event, ctx) => {
|
|
2975
|
+
const root = resolveGitWorkspaceRoot(ctx.cwd || "");
|
|
2976
|
+
if (!root) return;
|
|
2977
|
+
const packet = await buildCompactionResumeGuard(root, {
|
|
2978
|
+
reason: event.reason,
|
|
2979
|
+
maxWorkspaces: 3,
|
|
2980
|
+
}).catch(() => null);
|
|
2981
|
+
if (!packet?.workspaces?.length) return;
|
|
2982
|
+
const content = renderCompactionResumeGuard(packet);
|
|
2983
|
+
if (!content) return;
|
|
2984
|
+
pi.sendMessage({
|
|
2985
|
+
customType: "ues-durable-resume-guard",
|
|
2986
|
+
content,
|
|
2987
|
+
display: false,
|
|
2988
|
+
details: {
|
|
2989
|
+
schemaVersion: 1,
|
|
2990
|
+
reason: event.reason,
|
|
2991
|
+
willRetry: event.willRetry,
|
|
2992
|
+
workspaceCount: packet.workspaceCount,
|
|
2993
|
+
source: packet.source,
|
|
2994
|
+
},
|
|
2995
|
+
}, { triggerTurn: false });
|
|
2996
|
+
});
|
|
2997
|
+
|
|
2382
2998
|
pi.on("session_shutdown", async () => {
|
|
2999
|
+
directControllerAbort?.abort();
|
|
3000
|
+
directControllerAbort = null;
|
|
3001
|
+
promptUesActive = false;
|
|
3002
|
+
normalActiveTools = null;
|
|
2383
3003
|
CONTEXT_PACK_CACHE.clear();
|
|
2384
3004
|
clearSkillCompilerCache();
|
|
2385
3005
|
clearAffectedTestCache();
|
|
@@ -2387,29 +3007,44 @@ export default function (pi: ExtensionAPI) {
|
|
|
2387
3007
|
clearSemanticIndexRuntimeCache();
|
|
2388
3008
|
MCP_HEALTH.clear();
|
|
2389
3009
|
abortActiveCliChildren();
|
|
3010
|
+
await stopAllServices().catch(() => []);
|
|
2390
3011
|
await RPC_POOL.stopAll().catch(() => {});
|
|
3012
|
+
for (const dir of [...ACTIVE_TASK_SANDBOXES.keys()]) {
|
|
3013
|
+
const ownerRoot = await taskSandboxOwnerRoot(dir).catch(() => null);
|
|
3014
|
+
if (!ownerRoot) continue;
|
|
3015
|
+
await removeTaskSandbox(ownerRoot, dir, { force: true, deleteBranch: true }).catch(() => {});
|
|
3016
|
+
ACTIVE_TASK_SANDBOXES.delete(dir);
|
|
3017
|
+
}
|
|
2391
3018
|
});
|
|
2392
3019
|
|
|
2393
3020
|
pi.on("input", async (event, ctx) => {
|
|
2394
3021
|
if (process.env.UES_CHILD_PROCESS === "1") return { action: "continue" };
|
|
2395
|
-
if (
|
|
2396
|
-
event.source !== "interactive" ||
|
|
2397
|
-
!["steer", "followUp"].includes(String(event.streamingBehavior || ""))
|
|
2398
|
-
) {
|
|
2399
|
-
return { action: "continue" };
|
|
2400
|
-
}
|
|
3022
|
+
if (event.source !== "interactive") return { action: "continue" };
|
|
2401
3023
|
|
|
2402
3024
|
const text = String(event.text || "").trim();
|
|
2403
3025
|
if (!text) return { action: "continue" };
|
|
2404
3026
|
|
|
3027
|
+
const sessionName = sessionNameFromUesInput(text, ctx.cwd || "");
|
|
3028
|
+
if (sessionName) syncSessionIdentity(sessionName, ctx);
|
|
3029
|
+
if (/^\/ues-(?:resume|fix|feature|debug|review|audit|plan|research|critique|verify)(?:\s|$)/i.test(text)) {
|
|
3030
|
+
promptUesActive = true;
|
|
3031
|
+
activateParentUesTools();
|
|
3032
|
+
}
|
|
3033
|
+
|
|
2405
3034
|
if (/^(?:stop|cancel|abort|dừng|dung|hủy|huy)(?:\s|$)/i.test(text)) {
|
|
3035
|
+
if (!uesModeActive()) return { action: "continue" };
|
|
3036
|
+
let directAborted = 0;
|
|
3037
|
+
if (directControllerAbort && !directControllerAbort.signal.aborted) {
|
|
3038
|
+
directControllerAbort.abort();
|
|
3039
|
+
directAborted = 1;
|
|
3040
|
+
}
|
|
2406
3041
|
const result = await RPC_POOL.abortActive();
|
|
2407
3042
|
const cliAborted = abortActiveCliChildren();
|
|
2408
|
-
const total = result.aborted + cliAborted;
|
|
3043
|
+
const total = directAborted + result.aborted + cliAborted;
|
|
2409
3044
|
if (total > 0) {
|
|
2410
3045
|
try {
|
|
2411
3046
|
ctx.ui.notify(
|
|
2412
|
-
`UES: aborted ${total} active child worker(s)` +
|
|
3047
|
+
`UES: aborted ${total} active controller/child worker(s)` +
|
|
2413
3048
|
(cliAborted ? ` (${cliAborted} CLI fallback)` : ""),
|
|
2414
3049
|
"warning",
|
|
2415
3050
|
);
|
|
@@ -2419,6 +3054,65 @@ export default function (pi: ExtensionAPI) {
|
|
|
2419
3054
|
return { action: "continue" };
|
|
2420
3055
|
}
|
|
2421
3056
|
|
|
3057
|
+
if (!["steer", "followUp"].includes(String(event.streamingBehavior || ""))) {
|
|
3058
|
+
if (directControllerAbort && !directControllerAbort.signal.aborted) {
|
|
3059
|
+
const continuation = automaticUesContinuation(text, { activeRun: true });
|
|
3060
|
+
if (continuation.forward) {
|
|
3061
|
+
const forwarded = await RPC_POOL.steerActive(text).catch(() => ({
|
|
3062
|
+
accepted: false,
|
|
3063
|
+
reason: "steer-failed",
|
|
3064
|
+
active: 0,
|
|
3065
|
+
}));
|
|
3066
|
+
if (forwarded.accepted) {
|
|
3067
|
+
try { ctx.ui.notify("UES: natural continuation forwarded to the active child", "info"); } catch {}
|
|
3068
|
+
return { action: "handled" };
|
|
3069
|
+
}
|
|
3070
|
+
try {
|
|
3071
|
+
ctx.ui.notify(
|
|
3072
|
+
"UES is already running; continuation could not be targeted safely, so the prompt was not swallowed.",
|
|
3073
|
+
"warning",
|
|
3074
|
+
);
|
|
3075
|
+
} catch {}
|
|
3076
|
+
}
|
|
3077
|
+
return { action: "continue" };
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
if (
|
|
3081
|
+
AUTO_ADMISSION_ENABLED &&
|
|
3082
|
+
directControllerRunner &&
|
|
3083
|
+
!(Array.isArray((event as any).images) && (event as any).images.length > 0)
|
|
3084
|
+
) {
|
|
3085
|
+
const preliminaryAdmission = automaticUesAdmission(text);
|
|
3086
|
+
const workspaceRoot = preliminaryAdmission.admit === true
|
|
3087
|
+
? resolveGitWorkspaceRoot(ctx.cwd || "")
|
|
3088
|
+
: null;
|
|
3089
|
+
const admission = workspaceRoot
|
|
3090
|
+
? automaticUesAdmission(text, { inGitWorkspace: true })
|
|
3091
|
+
: preliminaryAdmission;
|
|
3092
|
+
if (workspaceRoot && admission.admit === true) {
|
|
3093
|
+
try {
|
|
3094
|
+
ctx.ui.notify(
|
|
3095
|
+
"UES " + PACKAGE_VERSION + ": " +
|
|
3096
|
+
(admission.route === "guarded" ? "high-risk engineering task admitted" : "engineering task auto-admitted") +
|
|
3097
|
+
" (" + admission.reason + ", confidence " + admission.confidence + ")",
|
|
3098
|
+
admission.route === "guarded" ? "warning" : "info",
|
|
3099
|
+
);
|
|
3100
|
+
} catch {}
|
|
3101
|
+
void directControllerRunner(text, ctx, "automatic", admission).catch((error) => {
|
|
3102
|
+
try {
|
|
3103
|
+
ctx.ui.notify(
|
|
3104
|
+
"UES automatic controller failed to start: " +
|
|
3105
|
+
(error instanceof Error ? error.message : String(error)),
|
|
3106
|
+
"error",
|
|
3107
|
+
);
|
|
3108
|
+
} catch {}
|
|
3109
|
+
});
|
|
3110
|
+
return { action: "handled" };
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
return { action: "continue" };
|
|
3114
|
+
}
|
|
3115
|
+
|
|
2422
3116
|
const steered = await RPC_POOL.steerActive(text).catch(() => ({
|
|
2423
3117
|
accepted: false,
|
|
2424
3118
|
reason: "steer-failed",
|
|
@@ -2434,8 +3128,27 @@ export default function (pi: ExtensionAPI) {
|
|
|
2434
3128
|
return { action: "continue" };
|
|
2435
3129
|
});
|
|
2436
3130
|
|
|
3131
|
+
pi.on("before_agent_start", async () => {
|
|
3132
|
+
if (uesModeActive()) activateParentUesTools();
|
|
3133
|
+
else deactivateParentUesTools();
|
|
3134
|
+
});
|
|
3135
|
+
|
|
3136
|
+
pi.on("agent_end", async () => {
|
|
3137
|
+
promptUesActive = false;
|
|
3138
|
+
deactivateParentUesTools();
|
|
3139
|
+
});
|
|
3140
|
+
|
|
2437
3141
|
pi.on("tool_call", async (event, ctx) => {
|
|
2438
3142
|
const toolName = String(event.toolName || "");
|
|
3143
|
+
if (!uesModeActive()) {
|
|
3144
|
+
if (toolName.startsWith("ues_")) {
|
|
3145
|
+
return {
|
|
3146
|
+
block: true,
|
|
3147
|
+
reason: "UES parent tools are hidden outside admitted UES runs. Submit a normal engineering task or start an explicit /ues-* command.",
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3150
|
+
return undefined;
|
|
3151
|
+
}
|
|
2439
3152
|
if (toolName !== "bash" && toolName !== "powershell") {
|
|
2440
3153
|
const allTools = typeof (pi as any).getAllTools === "function" ? (pi as any).getAllTools() : [];
|
|
2441
3154
|
const descriptor = allTools.find((tool: any) => String(tool?.name || "") === toolName);
|
|
@@ -2449,6 +3162,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
2449
3162
|
}
|
|
2450
3163
|
|
|
2451
3164
|
const command = String((event.input as any)?.command || "");
|
|
3165
|
+
if (looksLikeLongRunningServiceCommand(command)) {
|
|
3166
|
+
return {
|
|
3167
|
+
block: true,
|
|
3168
|
+
reason:
|
|
3169
|
+
"UES detected a likely long-running foreground service command. " +
|
|
3170
|
+
"Use ues_service start/wait-ready/logs/stop so Pi can continue without blocking on the server process.",
|
|
3171
|
+
};
|
|
3172
|
+
}
|
|
2452
3173
|
const risk = destructiveShellRisk(command);
|
|
2453
3174
|
if (!risk.risky) return undefined;
|
|
2454
3175
|
const allowed = await approveRisk(ctx, command, risk.id || "destructive");
|
|
@@ -2457,6 +3178,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2457
3178
|
});
|
|
2458
3179
|
|
|
2459
3180
|
pi.on("tool_result", async (event) => {
|
|
3181
|
+
if (!uesModeActive()) return undefined;
|
|
2460
3182
|
const toolName = String((event as any).toolName || "");
|
|
2461
3183
|
if (!toolName || toolName === "bash" || toolName === "powershell") return undefined;
|
|
2462
3184
|
MCP_HEALTH.finish(
|
|
@@ -2479,7 +3201,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2479
3201
|
cwd: Type.Optional(Type.String({ description: "Working directory; defaults to the current Pi cwd" })),
|
|
2480
3202
|
}),
|
|
2481
3203
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
2482
|
-
const cwd =
|
|
3204
|
+
const cwd = requireGitWorkspaceRoot(params.cwd || ctx.cwd, "ues_cli");
|
|
2483
3205
|
const separator = params.args.indexOf("--");
|
|
2484
3206
|
if (separator >= 0 && separator < params.args.length - 1) {
|
|
2485
3207
|
const command = params.args.slice(separator + 1).join(" ");
|
|
@@ -2538,6 +3260,91 @@ export default function (pi: ExtensionAPI) {
|
|
|
2538
3260
|
});
|
|
2539
3261
|
|
|
2540
3262
|
pi.registerTool({
|
|
3263
|
+
name: "ues_service",
|
|
3264
|
+
label: "UES Managed Service",
|
|
3265
|
+
description:
|
|
3266
|
+
"Start and manage long-running dev servers/watchers without blocking Pi. Uses shell-free execution, bounded logs, readiness probes, evidence snapshots, and session cleanup. For start/restart, command is the executable only (for example node or npm); put every argument in args.",
|
|
3267
|
+
parameters: Type.Object({
|
|
3268
|
+
action: Type.Union([
|
|
3269
|
+
Type.Literal("start"),
|
|
3270
|
+
Type.Literal("wait-ready"),
|
|
3271
|
+
Type.Literal("status"),
|
|
3272
|
+
Type.Literal("logs"),
|
|
3273
|
+
Type.Literal("stop"),
|
|
3274
|
+
Type.Literal("restart"),
|
|
3275
|
+
]),
|
|
3276
|
+
name: Type.String({ minLength: 1, maxLength: 64 }),
|
|
3277
|
+
command: Type.Optional(Type.String({ minLength: 1 })),
|
|
3278
|
+
args: Type.Optional(Type.Array(Type.String(), { maxItems: 128 })),
|
|
3279
|
+
cwd: Type.Optional(Type.String()),
|
|
3280
|
+
readyPort: Type.Optional(Type.Number({ minimum: 1, maximum: 65535 })),
|
|
3281
|
+
readyHost: Type.Optional(Type.String({ maxLength: 255 })),
|
|
3282
|
+
readyLog: Type.Optional(Type.String({ maxLength: 512 })),
|
|
3283
|
+
timeoutMs: Type.Optional(Type.Number({ minimum: 100, maximum: 600000 })),
|
|
3284
|
+
lifetimeMs: Type.Optional(Type.Number({ minimum: 10000, maximum: 7200000 })),
|
|
3285
|
+
idleTimeoutMs: Type.Optional(Type.Number({ minimum: 5000, maximum: 3600000 })),
|
|
3286
|
+
maxChars: Type.Optional(Type.Number({ minimum: 256, maximum: 128000 })),
|
|
3287
|
+
}),
|
|
3288
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
3289
|
+
try {
|
|
3290
|
+
const hostCwd = path.resolve(ctx.cwd);
|
|
3291
|
+
const serviceRoot = requireGitWorkspaceRoot(hostCwd, "ues_service");
|
|
3292
|
+
const requestedServiceCwd = params.cwd
|
|
3293
|
+
? path.relative(serviceRoot, path.resolve(hostCwd, params.cwd)) || "."
|
|
3294
|
+
: path.relative(serviceRoot, hostCwd) || ".";
|
|
3295
|
+
let result: any;
|
|
3296
|
+
if (params.action === "start") {
|
|
3297
|
+
if (!params.command) throw new Error("ues_service start requires command");
|
|
3298
|
+
const riskText = [params.command, ...(params.args || [])].join(" ");
|
|
3299
|
+
const risk = destructiveShellRisk(riskText);
|
|
3300
|
+
if (risk.risky) throw new Error(`UES service safety blocked ${risk.id || "destructive"} command`);
|
|
3301
|
+
result = await startService(serviceRoot, {
|
|
3302
|
+
name: params.name,
|
|
3303
|
+
command: params.command,
|
|
3304
|
+
args: params.args || [],
|
|
3305
|
+
cwd: requestedServiceCwd,
|
|
3306
|
+
readyPort: params.readyPort,
|
|
3307
|
+
readyHost: params.readyHost,
|
|
3308
|
+
readyLog: params.readyLog,
|
|
3309
|
+
timeoutMs: params.timeoutMs,
|
|
3310
|
+
lifetimeMs: params.lifetimeMs,
|
|
3311
|
+
idleTimeoutMs: params.idleTimeoutMs,
|
|
3312
|
+
});
|
|
3313
|
+
} else if (params.action === "wait-ready") {
|
|
3314
|
+
result = await waitForService(serviceRoot, params.name, { timeoutMs: params.timeoutMs });
|
|
3315
|
+
} else if (params.action === "status") {
|
|
3316
|
+
result = await serviceStatus(serviceRoot, params.name);
|
|
3317
|
+
} else if (params.action === "logs") {
|
|
3318
|
+
result = await serviceLogs(serviceRoot, params.name, { maxChars: params.maxChars, evidence: true });
|
|
3319
|
+
} else if (params.action === "stop") {
|
|
3320
|
+
result = await stopService(serviceRoot, params.name, { timeoutMs: params.timeoutMs });
|
|
3321
|
+
} else if (params.action === "restart") {
|
|
3322
|
+
result = await restartService(serviceRoot, params.name, {
|
|
3323
|
+
timeoutMs: params.timeoutMs,
|
|
3324
|
+
lifetimeMs: params.lifetimeMs,
|
|
3325
|
+
idleTimeoutMs: params.idleTimeoutMs,
|
|
3326
|
+
});
|
|
3327
|
+
} else {
|
|
3328
|
+
throw new Error("Unknown ues_service action");
|
|
3329
|
+
}
|
|
3330
|
+
const failed =
|
|
3331
|
+
(params.action === "start" || params.action === "wait-ready") && result?.ready !== true;
|
|
3332
|
+
return {
|
|
3333
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
3334
|
+
details: result,
|
|
3335
|
+
isError: failed,
|
|
3336
|
+
};
|
|
3337
|
+
} catch (error) {
|
|
3338
|
+
return {
|
|
3339
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
3340
|
+
details: { action: params.action, name: params.name },
|
|
3341
|
+
isError: true,
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
},
|
|
3345
|
+
});
|
|
3346
|
+
|
|
3347
|
+
const uesExecuteTool: any = {
|
|
2541
3348
|
name: "ues_execute",
|
|
2542
3349
|
label: "UES Execute",
|
|
2543
3350
|
description:
|
|
@@ -2549,16 +3356,61 @@ export default function (pi: ExtensionAPI) {
|
|
|
2549
3356
|
}),
|
|
2550
3357
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
2551
3358
|
const hostBrowserTools = refreshHostBrowserToolNames(pi);
|
|
2552
|
-
const cwd =
|
|
3359
|
+
const cwd = requireGitWorkspaceRoot(params.cwd || ctx.cwd, "ues_execute");
|
|
2553
3360
|
const inheritedModel = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
2554
3361
|
const inheritedThinking = ctx.thinkingLevel as string | undefined;
|
|
2555
|
-
const
|
|
2556
|
-
const
|
|
3362
|
+
const controllerWorkspaceState = captureWorkspaceStateV2(cwd);
|
|
3363
|
+
const inheritedDirty = captureInheritedDirtyState(cwd, {
|
|
3364
|
+
workspaceState: controllerWorkspaceState,
|
|
3365
|
+
});
|
|
3366
|
+
const controllerHygieneBaseline = captureWorkspaceHygieneBaseline(cwd, {
|
|
3367
|
+
workspaceState: controllerWorkspaceState,
|
|
3368
|
+
});
|
|
3369
|
+
const executionContract = buildExecutionContract(params.task, inheritedDirty);
|
|
3370
|
+
const contractPrompt = executionContractPrompt(executionContract);
|
|
3371
|
+
const suppliedPolicy = (params as any).__taskPolicy;
|
|
3372
|
+
const basePolicy =
|
|
3373
|
+
suppliedPolicy && typeof suppliedPolicy === "object"
|
|
3374
|
+
? suppliedPolicy
|
|
3375
|
+
: classifyEngineeringTask(params.task);
|
|
3376
|
+
const policy = {
|
|
3377
|
+
...basePolicy,
|
|
3378
|
+
localEnvWriteExplicitlyAllowed: executionContract.localEnvWriteExplicitlyAllowed === true,
|
|
3379
|
+
executionContractPrompt: contractPrompt,
|
|
3380
|
+
};
|
|
3381
|
+
const traceID = String((params as any).__traceID || createTraceID("ues-execute"));
|
|
3382
|
+
const orphanCleanup = await pruneOrphanTaskSandboxes(cwd, {
|
|
3383
|
+
minAgeMs: 5 * 60_000,
|
|
3384
|
+
legacyMinAgeMs: 30 * 60_000,
|
|
3385
|
+
ownedMinAgeMs: 60_000,
|
|
3386
|
+
reclaimOwnerPid: process.pid,
|
|
3387
|
+
protectedDirs: [...ACTIVE_TASK_SANDBOXES.keys()],
|
|
3388
|
+
}).catch(() => ({ removed: [], skipped: [], sidecarsRemoved: [] }));
|
|
3389
|
+
if (orphanCleanup.removed?.length) {
|
|
3390
|
+
onUpdate?.({
|
|
3391
|
+
content: [{ type: "text", text: `UES sandbox cleanup: removed ${orphanCleanup.removed.length} orphan worktree(s)` }],
|
|
3392
|
+
details: { mode: "execute", phase: "sandbox-cleanup", orphanCleanup },
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
2557
3395
|
await appendTrajectoryEvent(cwd, traceID, "controller.started", {
|
|
2558
3396
|
profile: policy.executionProfile,
|
|
2559
3397
|
risk: policy.risk,
|
|
2560
3398
|
mode: policy.mode,
|
|
3399
|
+
inheritedDirtyCount: executionContract.inheritedDirty?.paths?.length || 0,
|
|
3400
|
+
explicitPhaseCount: executionContract.phases?.length || 0,
|
|
3401
|
+
localEnvWriteExplicitlyAllowed: executionContract.localEnvWriteExplicitlyAllowed === true,
|
|
2561
3402
|
}).catch(() => {});
|
|
3403
|
+
if ((executionContract.inheritedDirty?.paths?.length || 0) > 0 || (executionContract.phases?.length || 0) > 0) {
|
|
3404
|
+
onUpdate?.({
|
|
3405
|
+
content: [{
|
|
3406
|
+
type: "text",
|
|
3407
|
+
text:
|
|
3408
|
+
`UES contract: inherited dirty ${executionContract.inheritedDirty?.paths?.length || 0}; ` +
|
|
3409
|
+
`explicit phases ${executionContract.phases?.length || 0}; local .env write ${executionContract.localEnvWriteExplicitlyAllowed ? "authorized" : "blocked"}`,
|
|
3410
|
+
}],
|
|
3411
|
+
details: { mode: "execute", phase: "execution-contract", executionContract, traceID },
|
|
3412
|
+
});
|
|
3413
|
+
}
|
|
2562
3414
|
const browserLaneRequested =
|
|
2563
3415
|
browserEvidenceNeeded(params.task, "executor") || visualEvidenceNeeded(params.task);
|
|
2564
3416
|
if (browserLaneRequested) {
|
|
@@ -2603,9 +3455,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
2603
3455
|
});
|
|
2604
3456
|
|
|
2605
3457
|
const run = async (agent: AgentName, task: string, attempt = 1, failure?: string) => {
|
|
3458
|
+
const planningBudget = planningRuntimeBudget(roleForAgent(agent), attempt, {
|
|
3459
|
+
executionProfile: policy.executionProfile,
|
|
3460
|
+
risk: policy.risk,
|
|
3461
|
+
taskChars: task.length,
|
|
3462
|
+
});
|
|
3463
|
+
let softSteerSent = false;
|
|
3464
|
+
const governedTask = [task, "", contractPrompt].filter(Boolean).join("\n");
|
|
2606
3465
|
const result = await runRoutedAgent(
|
|
2607
3466
|
agent,
|
|
2608
|
-
|
|
3467
|
+
governedTask,
|
|
2609
3468
|
cwd,
|
|
2610
3469
|
inheritedModel,
|
|
2611
3470
|
inheritedThinking,
|
|
@@ -2613,6 +3472,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
2613
3472
|
failure,
|
|
2614
3473
|
signal,
|
|
2615
3474
|
(progress) => {
|
|
3475
|
+
if (
|
|
3476
|
+
!softSteerSent &&
|
|
3477
|
+
(
|
|
3478
|
+
(agent === "ues-architect" && shouldSoftSteerArchitect(progress, planningBudget)) ||
|
|
3479
|
+
(agent === "ues-plan-checker" && shouldSoftSteerPlanningRole(progress, planningBudget))
|
|
3480
|
+
)
|
|
3481
|
+
) {
|
|
3482
|
+
softSteerSent = true;
|
|
3483
|
+
const steerText =
|
|
3484
|
+
agent === "ues-architect"
|
|
3485
|
+
? "Stop repository exploration now. Use the evidence already gathered and emit the required UES_PLAN_JSON object immediately as the next substantive output. Do not start new broad searches or delay the JSON behind prose."
|
|
3486
|
+
: "Stop broad validation now. Check only unresolved declared paths and dependencies, then return the verdict immediately. End with exactly UES_VERDICT: PASS or UES_VERDICT: REVISE.";
|
|
3487
|
+
void RPC_POOL.steerActive(steerText).catch(() => ({ accepted: false }));
|
|
3488
|
+
onUpdate?.({
|
|
3489
|
+
content: [{
|
|
3490
|
+
type: "text",
|
|
3491
|
+
text:
|
|
3492
|
+
agent === "ues-architect"
|
|
3493
|
+
? "UES planning fast-stop: architect evidence budget reached; requesting immediate plan emission"
|
|
3494
|
+
: "UES planning fast-stop: plan-checker budget reached; requesting immediate verdict",
|
|
3495
|
+
}],
|
|
3496
|
+
details: { mode: "execute", phase: "planning-soft-steer", policy, progress, planningBudget, traceID },
|
|
3497
|
+
});
|
|
3498
|
+
}
|
|
2616
3499
|
onUpdate?.({
|
|
2617
3500
|
content: [{
|
|
2618
3501
|
type: "text",
|
|
@@ -2627,6 +3510,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2627
3510
|
});
|
|
2628
3511
|
},
|
|
2629
3512
|
traceID,
|
|
3513
|
+
policy,
|
|
2630
3514
|
);
|
|
2631
3515
|
steps.push(result);
|
|
2632
3516
|
onUpdate?.({
|
|
@@ -2639,27 +3523,202 @@ export default function (pi: ExtensionAPI) {
|
|
|
2639
3523
|
return result;
|
|
2640
3524
|
};
|
|
2641
3525
|
|
|
2642
|
-
if (
|
|
2643
|
-
const
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
3526
|
+
if (policy.readOnly === true) {
|
|
3527
|
+
const before = runtimeWorkspaceSnapshot(cwd);
|
|
3528
|
+
const deterministicChecks = deterministicReadOnlyGitCommands(params.task);
|
|
3529
|
+
let inspection: any;
|
|
3530
|
+
|
|
3531
|
+
if (deterministicChecks.length > 0) {
|
|
3532
|
+
const rows: any[] = [];
|
|
3533
|
+
for (const spec of deterministicChecks) {
|
|
3534
|
+
const result = await runProcess(spec.command, spec.args, cwd, signal);
|
|
3535
|
+
if (result.exitCode === 130 || result.stopReason === "aborted") {
|
|
3536
|
+
const aborted: any = {
|
|
3537
|
+
agent: "ues-deterministic-read-only",
|
|
3538
|
+
task: params.task,
|
|
3539
|
+
cwd,
|
|
3540
|
+
exitCode: 130,
|
|
3541
|
+
output: "Deterministic read-only command aborted: " + spec.label,
|
|
3542
|
+
stderr: result.stderr || "",
|
|
3543
|
+
verdict: null,
|
|
3544
|
+
durationMs: result.durationMs || 0,
|
|
3545
|
+
toolCalls: 0,
|
|
3546
|
+
toolNames: [],
|
|
3547
|
+
report: parseStructuredReport(""),
|
|
3548
|
+
stopReason: "aborted",
|
|
3549
|
+
};
|
|
3550
|
+
steps.push(aborted);
|
|
3551
|
+
return abortedResponse(aborted, "read-only-deterministic-check");
|
|
3552
|
+
}
|
|
3553
|
+
rows.push({
|
|
3554
|
+
...spec,
|
|
3555
|
+
exitCode: result.exitCode,
|
|
3556
|
+
stdout: cap(result.stdout || "", 8000),
|
|
3557
|
+
stderr: cap(result.stderr || "", 4000),
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
2654
3560
|
|
|
2655
|
-
|
|
2656
|
-
|
|
3561
|
+
const afterChecks = runtimeWorkspaceSnapshot(cwd);
|
|
3562
|
+
const unchangedAfterChecks =
|
|
3563
|
+
before.cacheable === true &&
|
|
3564
|
+
afterChecks.cacheable === true &&
|
|
3565
|
+
before.fingerprint === afterChecks.fingerprint;
|
|
3566
|
+
const failedRows = rows.filter((row) => Number(row.exitCode) !== 0);
|
|
3567
|
+
const deterministicPass = failedRows.length === 0 && unchangedAfterChecks;
|
|
3568
|
+
const checksText = rows.map((row) => {
|
|
3569
|
+
const payload = [row.stdout, row.stderr].filter(Boolean).join("\n").trim() || "(no output)";
|
|
3570
|
+
return [
|
|
3571
|
+
row.label + " — exit " + row.exitCode,
|
|
3572
|
+
payload,
|
|
3573
|
+
].join("\n");
|
|
3574
|
+
}).join("\n\n");
|
|
3575
|
+
const failureText = [
|
|
3576
|
+
...failedRows.map((row) => row.label + " exited " + row.exitCode),
|
|
3577
|
+
...(unchangedAfterChecks ? [] : ["read-only workspace fingerprint changed"]),
|
|
3578
|
+
].join("\n") || "None.";
|
|
3579
|
+
|
|
3580
|
+
const output = [
|
|
3581
|
+
"## Checks run",
|
|
3582
|
+
checksText,
|
|
3583
|
+
"",
|
|
3584
|
+
"## Acceptance criteria proven",
|
|
3585
|
+
deterministicPass
|
|
3586
|
+
? "All explicitly requested whitelisted Git inspection commands completed successfully."
|
|
3587
|
+
: "The requested deterministic inspection did not fully pass.",
|
|
3588
|
+
"",
|
|
3589
|
+
"## Failures",
|
|
3590
|
+
failureText,
|
|
3591
|
+
"",
|
|
3592
|
+
"## Unresolved gaps",
|
|
3593
|
+
"None.",
|
|
3594
|
+
"",
|
|
3595
|
+
"## Checks not run",
|
|
3596
|
+
"None.",
|
|
3597
|
+
"",
|
|
3598
|
+
"## Completion evidence",
|
|
3599
|
+
"UES executed " + rows.length + " command-only read-only Git check(s) directly and compared the source workspace fingerprint before and after.",
|
|
3600
|
+
"",
|
|
3601
|
+
"UES_VERDICT: " + (deterministicPass ? "PASS" : "FAIL"),
|
|
3602
|
+
].join("\n");
|
|
3603
|
+
|
|
3604
|
+
inspection = {
|
|
3605
|
+
agent: "ues-deterministic-read-only",
|
|
3606
|
+
task: params.task,
|
|
3607
|
+
cwd,
|
|
3608
|
+
exitCode: deterministicPass ? 0 : 1,
|
|
3609
|
+
output,
|
|
3610
|
+
stderr: "",
|
|
3611
|
+
verdict: deterministicPass ? "PASS" : "FAIL",
|
|
3612
|
+
durationMs: 0,
|
|
3613
|
+
toolCalls: rows.length,
|
|
3614
|
+
toolNames: rows.map((row) => row.label),
|
|
3615
|
+
report: parseStructuredReport(output),
|
|
3616
|
+
optimizations: {
|
|
3617
|
+
deterministicReadOnlyGit: true,
|
|
3618
|
+
checkCount: rows.length,
|
|
3619
|
+
},
|
|
3620
|
+
};
|
|
3621
|
+
steps.push(inspection);
|
|
3622
|
+
onUpdate?.({
|
|
3623
|
+
content: [{
|
|
3624
|
+
type: "text",
|
|
3625
|
+
text: "UES read-only fast path: executed " + rows.length + " whitelisted Git check(s) deterministically; skipped verifier model turn",
|
|
3626
|
+
}],
|
|
3627
|
+
details: { mode: "execute", phase: "read-only-deterministic", policy, traceID, checks: rows.map((row) => row.label) },
|
|
3628
|
+
});
|
|
3629
|
+
} else {
|
|
3630
|
+
inspection = await run(
|
|
3631
|
+
"ues-verifier",
|
|
3632
|
+
[
|
|
3633
|
+
"READ-ONLY INSPECTION MODE. Do not edit, create, delete, stage, commit, install dependencies, or start persistent services.",
|
|
3634
|
+
"Use only the narrowest read-only commands and source inspection needed to answer the request.",
|
|
3635
|
+
"Fresh command output is valid completion evidence for an inspection task; do not require behavioral tests when the user did not request a code change.",
|
|
3636
|
+
"Return exactly these H2 sections: ## Checks run, ## Acceptance criteria proven, ## Failures, ## Unresolved gaps, ## Checks not run, ## Completion evidence.",
|
|
3637
|
+
"Write exactly None in Failures and Unresolved gaps when there is no real requested failure or gap.",
|
|
3638
|
+
"End with exactly UES_VERDICT: PASS or UES_VERDICT: FAIL.",
|
|
3639
|
+
"Explicitly verify that source-facing workspace state is unchanged before returning PASS.",
|
|
3640
|
+
"",
|
|
3641
|
+
"Inspection request:",
|
|
3642
|
+
params.task,
|
|
3643
|
+
].join("\n"),
|
|
3644
|
+
1,
|
|
3645
|
+
);
|
|
3646
|
+
if (isAbortedRun(inspection)) return abortedResponse(inspection, "read-only-inspection");
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
const after = runtimeWorkspaceSnapshot(cwd);
|
|
3650
|
+
const unchanged =
|
|
3651
|
+
before.cacheable === true &&
|
|
3652
|
+
after.cacheable === true &&
|
|
3653
|
+
before.fingerprint === after.fingerprint;
|
|
3654
|
+
const completionAudit = auditCompletion({
|
|
3655
|
+
verification: inspection,
|
|
3656
|
+
requireIntegration: false,
|
|
3657
|
+
requireVisual: false,
|
|
3658
|
+
workspaceSnapshot: after,
|
|
3659
|
+
behavioralReceipts: [],
|
|
3660
|
+
requireBehavioralReceipt: false,
|
|
3661
|
+
});
|
|
3662
|
+
if (!unchanged) {
|
|
3663
|
+
completionAudit.passed = false;
|
|
3664
|
+
completionAudit.failures = [...new Set([
|
|
3665
|
+
...(completionAudit.failures || []),
|
|
3666
|
+
"read-only-workspace-mutated",
|
|
3667
|
+
])];
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
if (!completionAudit.passed) {
|
|
3671
|
+
return {
|
|
3672
|
+
content: [{
|
|
3673
|
+
type: "text",
|
|
3674
|
+
text: "UES read-only inspection did not pass: " + completionAudit.failures.join(", ") +
|
|
3675
|
+
"\n\n" + inspection.output,
|
|
3676
|
+
}],
|
|
3677
|
+
details: { mode: "execute", policy, steps, completionAudit, traceID },
|
|
3678
|
+
isError: true,
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
return {
|
|
3683
|
+
content: [{
|
|
3684
|
+
type: "text",
|
|
3685
|
+
text: [
|
|
3686
|
+
"UES read-only inspection PASS.",
|
|
3687
|
+
"Workspace source fingerprint remained unchanged.",
|
|
3688
|
+
"",
|
|
3689
|
+
inspection.output,
|
|
3690
|
+
].join("\n"),
|
|
3691
|
+
}],
|
|
3692
|
+
details: { mode: "execute", policy, steps, completionAudit, traceID },
|
|
3693
|
+
};
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
if (shouldRunDedicatedDiagnosis(policy, 1)) {
|
|
3697
|
+
const diagnosis = await run("ues-debugger", params.task, 1);
|
|
3698
|
+
if (isAbortedRun(diagnosis)) return abortedResponse(diagnosis, "diagnosis");
|
|
3699
|
+
if (diagnosis.exitCode !== 0 || diagnosis.stopReason === "error") {
|
|
3700
|
+
return {
|
|
3701
|
+
content: [{ type: "text", text: `Diagnosis failed:\n\n${diagnosis.output}` }],
|
|
3702
|
+
details: { mode: "execute", policy, steps },
|
|
3703
|
+
isError: true,
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
recentFailure = diagnosis.output;
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
if (policy.requirePlanCheck) {
|
|
3710
|
+
const planInstruction = [
|
|
2657
3711
|
params.task,
|
|
2658
3712
|
"",
|
|
2659
3713
|
"Produce an implementation plan grounded in the current repository. Include exact files/interfaces, dependencies, risk controls, rollback notes, acceptance criteria and verification commands.",
|
|
2660
|
-
"For deterministic scheduling,
|
|
3714
|
+
"For deterministic scheduling, emit UES_PLAN_JSON: followed by one valid JSON object with schemaVersion=1, goal, and tasks as the first substantive output. Do not delay the JSON behind long prose.",
|
|
2661
3715
|
"Each task must have id, title, summary, dependsOn, files ({create,modify,test,delete,read}), acceptance, verification, and risk.",
|
|
3716
|
+
executionContract.phases?.length
|
|
3717
|
+
? "PHASE CONTRACT: every execution task must include an integer phase matching one explicit execution PHASE number from the user request. Constraint/guardrail-only phases are invariants, not fake tasks. Do not omit, merge away, or invent execution phases. UES will add deterministic previous-phase barriers after validation."
|
|
3718
|
+
: "",
|
|
3719
|
+
"STRICT JSON CONTRACT: acceptance and verification are non-empty arrays of strings. risk is exactly one of low|medium|high|critical. Put descriptive risk prose in riskNotes. verificationCommands is optional and does not replace verification.",
|
|
2662
3720
|
"Declare every file a task may write. Do not invent files: inspect the repository first.",
|
|
3721
|
+
"DEEP efficiency rule: use the supplied runtime context/ranked references first; do not inventory the whole repository or re-read unchanged files. You have a bounded planning budget: prefer at most one targeted lookup per unresolved boundary, then emit the plan. Stop exploration once exact task scope, dependencies, acceptance, verification, and risk/rollback are grounded.",
|
|
2663
3722
|
].join("\n");
|
|
2664
3723
|
|
|
2665
3724
|
let architect = await run(
|
|
@@ -2669,70 +3728,237 @@ export default function (pi: ExtensionAPI) {
|
|
|
2669
3728
|
recentFailure || undefined,
|
|
2670
3729
|
);
|
|
2671
3730
|
if (isAbortedRun(architect)) return abortedResponse(architect, "planning");
|
|
2672
|
-
if (architect.exitCode !== 0 || architect.stopReason === "error") {
|
|
2673
|
-
return {
|
|
2674
|
-
content: [{ type: "text", text: `Architecture pass failed:\n\n${architect.output}` }],
|
|
2675
|
-
details: { mode: "execute", policy, steps },
|
|
2676
|
-
isError: true,
|
|
2677
|
-
};
|
|
2678
|
-
}
|
|
2679
3731
|
|
|
2680
|
-
|
|
2681
|
-
|
|
3732
|
+
let planCandidate = extractValidatedPlan(architect.output);
|
|
3733
|
+
structuredPlan = planCandidate.plan;
|
|
3734
|
+
let structuredValidation = planCandidate.validation;
|
|
2682
3735
|
|
|
2683
|
-
if (
|
|
2684
|
-
(
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
3736
|
+
if (planCandidate.salvaged && structuredValidation?.valid === true) {
|
|
3737
|
+
onUpdate?.({
|
|
3738
|
+
content: [{
|
|
3739
|
+
type: "text",
|
|
3740
|
+
text: `UES planning salvage: recovered a valid plan from ${planCandidate.source} architect output`,
|
|
3741
|
+
}],
|
|
3742
|
+
details: {
|
|
3743
|
+
mode: "execute",
|
|
3744
|
+
phase: "planning-salvage",
|
|
3745
|
+
source: planCandidate.source,
|
|
3746
|
+
traceID,
|
|
3747
|
+
},
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3751
|
+
const firstPlanValid =
|
|
3752
|
+
structuredPlan &&
|
|
3753
|
+
structuredValidation?.valid === true;
|
|
3754
|
+
|
|
3755
|
+
if (!firstPlanValid) {
|
|
3756
|
+
const recoveryEvidence = failureDelta([
|
|
3757
|
+
architect.exitCode !== 0 || architect.stopReason
|
|
3758
|
+
? "Previous architect pass stopped before a valid plan: " + String(architect.stopReason || architect.exitCode)
|
|
3759
|
+
: "Previous architect pass returned an invalid plan.",
|
|
3760
|
+
structuredValidation
|
|
3761
|
+
? JSON.stringify(structuredValidation, null, 2)
|
|
3762
|
+
: "UES_PLAN_JSON marker or valid JSON object was missing.",
|
|
3763
|
+
"RECOVERY RULE: do not restart repository exploration. Reuse the existing context/evidence, perform at most one targeted lookup for any blocking gap, then return the corrected plan immediately.",
|
|
3764
|
+
"acceptance and verification must be non-empty string arrays; risk must be low|medium|high|critical; descriptive prose belongs in riskNotes.",
|
|
3765
|
+
].join("\n"), { maxChars: 4200 });
|
|
3766
|
+
|
|
3767
|
+
architect = await run("ues-architect", planInstruction, 2, recoveryEvidence);
|
|
3768
|
+
if (isAbortedRun(architect)) return abortedResponse(architect, "plan-recovery");
|
|
3769
|
+
planCandidate = extractValidatedPlan(architect.output);
|
|
3770
|
+
structuredPlan = planCandidate.plan;
|
|
3771
|
+
structuredValidation = planCandidate.validation;
|
|
3772
|
+
if (planCandidate.salvaged && structuredValidation?.valid === true) {
|
|
3773
|
+
onUpdate?.({
|
|
2703
3774
|
content: [{
|
|
2704
3775
|
type: "text",
|
|
2705
|
-
text:
|
|
2706
|
-
(structuredValidation ? JSON.stringify(structuredValidation, null, 2) : architect.output),
|
|
3776
|
+
text: `UES planning salvage: recovery produced a valid ${planCandidate.source} plan`,
|
|
2707
3777
|
}],
|
|
2708
|
-
details: {
|
|
2709
|
-
|
|
2710
|
-
|
|
3778
|
+
details: {
|
|
3779
|
+
mode: "execute",
|
|
3780
|
+
phase: "planning-salvage",
|
|
3781
|
+
source: planCandidate.source,
|
|
3782
|
+
traceID,
|
|
3783
|
+
},
|
|
3784
|
+
});
|
|
2711
3785
|
}
|
|
2712
3786
|
}
|
|
2713
3787
|
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
3788
|
+
if (
|
|
3789
|
+
!structuredPlan ||
|
|
3790
|
+
structuredValidation?.valid !== true
|
|
3791
|
+
) {
|
|
3792
|
+
return {
|
|
3793
|
+
content: [{
|
|
3794
|
+
type: "text",
|
|
3795
|
+
text: "Long-horizon planning exhausted its bounded fast-planning recovery without a valid deterministic task graph.\n\n" +
|
|
3796
|
+
(structuredValidation ? JSON.stringify(structuredValidation, null, 2) : architect.output),
|
|
3797
|
+
}],
|
|
3798
|
+
details: { mode: "execute", policy, steps, structuredPlan, structuredValidation },
|
|
3799
|
+
isError: true,
|
|
3800
|
+
};
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
let phaseGate = enforcePhaseGates(structuredPlan, executionContract);
|
|
3804
|
+
if (phaseGate.valid !== true) {
|
|
3805
|
+
const phaseRevisionEvidence = failureDelta([
|
|
3806
|
+
"The deterministic explicit-phase gate rejected the plan.",
|
|
3807
|
+
"Do not rescan the repository. Revise only phase assignment/coverage/dependencies.",
|
|
3808
|
+
"Every execution task must include an integer phase. Every explicit execution PHASE must have at least one task. Constraint-only phases are invariants, not tasks.",
|
|
3809
|
+
"Phase gate errors:",
|
|
3810
|
+
phaseGate.errors.join("\n"),
|
|
3811
|
+
"Current plan:",
|
|
3812
|
+
JSON.stringify(structuredPlan, null, 2),
|
|
3813
|
+
].join("\n"), { maxChars: 6200 });
|
|
3814
|
+
const phaseRevisedArchitect = await run(
|
|
3815
|
+
"ues-architect",
|
|
3816
|
+
planInstruction,
|
|
3817
|
+
2,
|
|
3818
|
+
phaseRevisionEvidence,
|
|
3819
|
+
);
|
|
3820
|
+
if (isAbortedRun(phaseRevisedArchitect)) {
|
|
3821
|
+
return abortedResponse(phaseRevisedArchitect, "phase-plan-auto-revise");
|
|
3822
|
+
}
|
|
3823
|
+
const phaseRevisedCandidate = extractValidatedPlan(phaseRevisedArchitect.output);
|
|
3824
|
+
if (phaseRevisedCandidate.plan && phaseRevisedCandidate.validation?.valid === true) {
|
|
3825
|
+
const revisedPhaseGate = enforcePhaseGates(
|
|
3826
|
+
phaseRevisedCandidate.plan,
|
|
3827
|
+
executionContract,
|
|
3828
|
+
);
|
|
3829
|
+
if (revisedPhaseGate.valid === true) {
|
|
3830
|
+
architect = phaseRevisedArchitect;
|
|
3831
|
+
structuredPlan = revisedPhaseGate.plan;
|
|
3832
|
+
structuredValidation = phaseRevisedCandidate.validation;
|
|
3833
|
+
phaseGate = revisedPhaseGate;
|
|
3834
|
+
onUpdate?.({
|
|
3835
|
+
content: [{
|
|
3836
|
+
type: "text",
|
|
3837
|
+
text: "UES phase gate: auto-revised phase coverage/dependencies once and recovered a valid gated plan",
|
|
3838
|
+
}],
|
|
3839
|
+
details: { mode: "execute", phase: "phase-plan-auto-revise", phaseGate, traceID },
|
|
3840
|
+
});
|
|
3841
|
+
} else {
|
|
3842
|
+
phaseGate = revisedPhaseGate;
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
if (phaseGate.valid !== true) {
|
|
3847
|
+
return {
|
|
3848
|
+
content: [{
|
|
3849
|
+
type: "text",
|
|
3850
|
+
text:
|
|
3851
|
+
"Explicit phase contract gate did not pass after bounded auto-recovery. UES will not flatten or skip user-declared phases.\n\n" +
|
|
3852
|
+
phaseGate.errors.join("\n"),
|
|
3853
|
+
}],
|
|
3854
|
+
details: { mode: "execute", policy, steps, structuredPlan, phaseGate, executionContract },
|
|
3855
|
+
isError: true,
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3858
|
+
structuredPlan = phaseGate.plan;
|
|
3859
|
+
|
|
3860
|
+
const buildPlanCheckTask = () => [
|
|
3861
|
+
"Validate the following inline plan against the current repository. If persistent SPEC/PLAN files do not exist yet, evaluate this inline plan directly instead of failing only because those files are absent.",
|
|
3862
|
+
"Use declared files and dependencies first. Do not inventory the repository. Resolve only concrete grounding gaps, then return the verdict.",
|
|
3863
|
+
"",
|
|
3864
|
+
"Original task:",
|
|
3865
|
+
params.task,
|
|
3866
|
+
"",
|
|
3867
|
+
"Inline plan:",
|
|
3868
|
+
structuredPlan ? JSON.stringify(structuredPlan, null, 2) : architect.output,
|
|
3869
|
+
].join("\n");
|
|
3870
|
+
|
|
3871
|
+
let planCheckTask = buildPlanCheckTask();
|
|
3872
|
+
let planCheck = await run("ues-plan-checker", planCheckTask, 1);
|
|
2727
3873
|
if (isAbortedRun(planCheck)) return abortedResponse(planCheck, "plan-verification");
|
|
3874
|
+
|
|
3875
|
+
const planCheckTransportFailure =
|
|
3876
|
+
planCheck.verdict !== "PASS" &&
|
|
3877
|
+
planCheck.verdict !== "REVISE" &&
|
|
3878
|
+
(
|
|
3879
|
+
planCheck.exitCode !== 0 ||
|
|
3880
|
+
/hard-timeout|idle-timeout|UES RPC hard-timeout|UES RPC idle-timeout/i.test(
|
|
3881
|
+
String(planCheck.output || "") + "\n" +
|
|
3882
|
+
String(planCheck.stderr || "") + "\n" +
|
|
3883
|
+
String(planCheck.stopReason || ""),
|
|
3884
|
+
)
|
|
3885
|
+
);
|
|
3886
|
+
|
|
3887
|
+
if (planCheckTransportFailure) {
|
|
3888
|
+
const planCheckRecoveryEvidence = failureDelta([
|
|
3889
|
+
"Previous plan-checker runtime failure:",
|
|
3890
|
+
String(planCheck.output || planCheck.stderr || planCheck.stopReason || "unknown"),
|
|
3891
|
+
"Reuse warm context. Do not scan broadly. Check only unresolved declared paths/dependencies and return PASS or REVISE.",
|
|
3892
|
+
].join("\n"), { maxChars: 2600 });
|
|
3893
|
+
|
|
3894
|
+
planCheck = await run(
|
|
3895
|
+
"ues-plan-checker",
|
|
3896
|
+
planCheckTask,
|
|
3897
|
+
2,
|
|
3898
|
+
planCheckRecoveryEvidence,
|
|
3899
|
+
);
|
|
3900
|
+
if (isAbortedRun(planCheck)) return abortedResponse(planCheck, "plan-verification-recovery");
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
if (planCheck.verdict === "REVISE") {
|
|
3904
|
+
const revisionEvidence = failureDelta([
|
|
3905
|
+
"The independent plan-checker requested a bounded revision.",
|
|
3906
|
+
"Do not restart broad repository exploration. Change only the rejected plan claims/scope.",
|
|
3907
|
+
"Plan-checker feedback:",
|
|
3908
|
+
String(planCheck.output || ""),
|
|
3909
|
+
"Current plan:",
|
|
3910
|
+
JSON.stringify(structuredPlan, null, 2),
|
|
3911
|
+
].join("\n"), { maxChars: 5200 });
|
|
3912
|
+
|
|
3913
|
+
const revisedArchitect = await run(
|
|
3914
|
+
"ues-architect",
|
|
3915
|
+
planInstruction,
|
|
3916
|
+
2,
|
|
3917
|
+
revisionEvidence,
|
|
3918
|
+
);
|
|
3919
|
+
if (isAbortedRun(revisedArchitect)) return abortedResponse(revisedArchitect, "plan-auto-revise");
|
|
3920
|
+
const revisedCandidate = extractValidatedPlan(revisedArchitect.output);
|
|
3921
|
+
if (revisedCandidate.plan && revisedCandidate.validation?.valid === true) {
|
|
3922
|
+
architect = revisedArchitect;
|
|
3923
|
+
structuredPlan = revisedCandidate.plan;
|
|
3924
|
+
structuredValidation = revisedCandidate.validation;
|
|
3925
|
+
planCheckTask = buildPlanCheckTask();
|
|
3926
|
+
onUpdate?.({
|
|
3927
|
+
content: [{ type: "text", text: "UES plan gate: auto-revised the rejected plan; re-checking once" }],
|
|
3928
|
+
details: { mode: "execute", phase: "plan-auto-revise", policy, traceID },
|
|
3929
|
+
});
|
|
3930
|
+
planCheck = await run(
|
|
3931
|
+
"ues-plan-checker",
|
|
3932
|
+
planCheckTask,
|
|
3933
|
+
2,
|
|
3934
|
+
failureDelta(String(planCheck.output || ""), { maxChars: 2600 }),
|
|
3935
|
+
);
|
|
3936
|
+
if (isAbortedRun(planCheck)) return abortedResponse(planCheck, "plan-auto-revise-check");
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
const finalPhaseGate = enforcePhaseGates(structuredPlan, executionContract);
|
|
3941
|
+
if (finalPhaseGate.valid !== true) {
|
|
3942
|
+
return {
|
|
3943
|
+
content: [{
|
|
3944
|
+
type: "text",
|
|
3945
|
+
text:
|
|
3946
|
+
"Plan revision lost the deterministic explicit-phase contract. UES will not execute it.\n\n" +
|
|
3947
|
+
finalPhaseGate.errors.join("\n"),
|
|
3948
|
+
}],
|
|
3949
|
+
details: { mode: "execute", policy, steps, structuredPlan, finalPhaseGate, executionContract },
|
|
3950
|
+
isError: true,
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
structuredPlan = finalPhaseGate.plan;
|
|
3954
|
+
|
|
2728
3955
|
if (planCheck.exitCode !== 0 || planCheck.verdict !== "PASS") {
|
|
2729
3956
|
return {
|
|
2730
|
-
content: [{ type: "text", text: `Plan gate did not pass:\n\n${planCheck.output}` }],
|
|
3957
|
+
content: [{ type: "text", text: `Plan gate did not pass after bounded auto-recovery:\n\n${planCheck.output}` }],
|
|
2731
3958
|
details: { mode: "execute", policy, steps, structuredPlan },
|
|
2732
3959
|
isError: true,
|
|
2733
3960
|
};
|
|
2734
3961
|
}
|
|
2735
|
-
|
|
2736
3962
|
const durableRequested =
|
|
2737
3963
|
policy.mode === "long-horizon" || policy.profile?.durableState === true;
|
|
2738
3964
|
if (durableRequested) {
|
|
@@ -2754,6 +3980,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
2754
3980
|
planCheck.output,
|
|
2755
3981
|
signal,
|
|
2756
3982
|
);
|
|
3983
|
+
durableWork.executionArtifacts = await persistExecutionContractArtifacts(
|
|
3984
|
+
durableWork.dir,
|
|
3985
|
+
executionContract,
|
|
3986
|
+
);
|
|
2757
3987
|
onUpdate?.({
|
|
2758
3988
|
content: [{
|
|
2759
3989
|
type: "text",
|
|
@@ -2787,6 +4017,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2787
4017
|
inheritedModel,
|
|
2788
4018
|
inheritedThinking,
|
|
2789
4019
|
maxAttempts,
|
|
4020
|
+
rootPolicy: policy,
|
|
2790
4021
|
durableSlug: durableWork?.slug,
|
|
2791
4022
|
traceID,
|
|
2792
4023
|
signal,
|
|
@@ -2893,12 +4124,131 @@ export default function (pi: ExtensionAPI) {
|
|
|
2893
4124
|
}
|
|
2894
4125
|
}
|
|
2895
4126
|
|
|
4127
|
+
const verdictMatrix = buildFinalVerdictMatrix(params.task, {
|
|
4128
|
+
contract: executionContract,
|
|
4129
|
+
primaryPass: true,
|
|
4130
|
+
integrationPass: true,
|
|
4131
|
+
primaryOutput: (scheduled.results || [])
|
|
4132
|
+
.map((row: any) => row?.verification?.output || row?.implementation?.output || "")
|
|
4133
|
+
.filter(Boolean)
|
|
4134
|
+
.join("\n"),
|
|
4135
|
+
integrationOutput: integration.output,
|
|
4136
|
+
visualOutput: visualResult?.output || "",
|
|
4137
|
+
primaryChecks: (scheduled.results || [])
|
|
4138
|
+
.flatMap((row: any) => Object.values(row?.verification?.report?.sections || {}))
|
|
4139
|
+
.filter(Boolean)
|
|
4140
|
+
.join("\n"),
|
|
4141
|
+
integrationChecks: integration.report?.sections?.["checks-run"] || "",
|
|
4142
|
+
visualChecks: visualResult?.report?.sections?.["checks-run"] || "",
|
|
4143
|
+
});
|
|
4144
|
+
|
|
4145
|
+
const finalEvidenceForContract = [
|
|
4146
|
+
integration.output,
|
|
4147
|
+
visualResult?.output || "",
|
|
4148
|
+
].filter(Boolean).join("\n\n--- VISUAL ---\n\n");
|
|
4149
|
+
|
|
4150
|
+
if (verdictMatrix.final !== "PASS") {
|
|
4151
|
+
if (durableWork) {
|
|
4152
|
+
await durableRecordIntegration(
|
|
4153
|
+
cwd,
|
|
4154
|
+
durableWork.slug,
|
|
4155
|
+
"PARTIAL",
|
|
4156
|
+
finalEvidenceForContract,
|
|
4157
|
+
signal,
|
|
4158
|
+
).catch(() => {});
|
|
4159
|
+
await finalizeExecutionContractArtifacts(
|
|
4160
|
+
durableWork.dir,
|
|
4161
|
+
executionContract,
|
|
4162
|
+
verdictMatrix,
|
|
4163
|
+
finalEvidenceForContract,
|
|
4164
|
+
).catch(() => null);
|
|
4165
|
+
}
|
|
4166
|
+
const deviceOnlyPending =
|
|
4167
|
+
verdictMatrix.final === "SOURCE_RUNTIME_PASS_DEVICE_NOT_VERIFIED";
|
|
4168
|
+
return {
|
|
4169
|
+
content: [{
|
|
4170
|
+
type: "text",
|
|
4171
|
+
text: [
|
|
4172
|
+
deviceOnlyPending
|
|
4173
|
+
? "UES source/runtime verification passed, but requested real-device verification is still pending."
|
|
4174
|
+
: "UES deterministic final gate is not fully verified.",
|
|
4175
|
+
"",
|
|
4176
|
+
verdictMatrix.source,
|
|
4177
|
+
verdictMatrix.runtime,
|
|
4178
|
+
verdictMatrix.dbClean,
|
|
4179
|
+
verdictMatrix.device,
|
|
4180
|
+
"",
|
|
4181
|
+
integration.output,
|
|
4182
|
+
].join("\n"),
|
|
4183
|
+
}],
|
|
4184
|
+
details: {
|
|
4185
|
+
mode: "execute",
|
|
4186
|
+
policy,
|
|
4187
|
+
steps,
|
|
4188
|
+
structuredPlan,
|
|
4189
|
+
scheduled,
|
|
4190
|
+
durableWork,
|
|
4191
|
+
verdictMatrix,
|
|
4192
|
+
executionContract,
|
|
4193
|
+
traceID,
|
|
4194
|
+
},
|
|
4195
|
+
isError: !deviceOnlyPending,
|
|
4196
|
+
};
|
|
4197
|
+
}
|
|
4198
|
+
|
|
4199
|
+
const finalWriteScope = [...new Set(
|
|
4200
|
+
structuredPlan.tasks.flatMap((task: any) => taskWriteFiles(task)),
|
|
4201
|
+
)];
|
|
4202
|
+
const preFinalAudit = await preFinalWorkspaceAudit(cwd, {
|
|
4203
|
+
baseline: controllerHygieneBaseline,
|
|
4204
|
+
taskText: params.task,
|
|
4205
|
+
allowedPaths: finalWriteScope,
|
|
4206
|
+
strictScope: true,
|
|
4207
|
+
allowSourceMutations: true,
|
|
4208
|
+
}).catch((error) => ({
|
|
4209
|
+
safe: false,
|
|
4210
|
+
summary: "pre-final workspace audit failed: " + (error instanceof Error ? error.message : String(error)),
|
|
4211
|
+
findings: [],
|
|
4212
|
+
}));
|
|
4213
|
+
if (!preFinalAudit.safe) {
|
|
4214
|
+
if (durableWork) {
|
|
4215
|
+
await durableRecordIntegration(
|
|
4216
|
+
cwd,
|
|
4217
|
+
durableWork.slug,
|
|
4218
|
+
"PARTIAL",
|
|
4219
|
+
preFinalAudit.summary || "pre-final workspace hygiene failed",
|
|
4220
|
+
signal,
|
|
4221
|
+
).catch(() => {});
|
|
4222
|
+
}
|
|
4223
|
+
return {
|
|
4224
|
+
content: [{
|
|
4225
|
+
type: "text",
|
|
4226
|
+
text: [
|
|
4227
|
+
"UES pre-final workspace audit blocked PASS.",
|
|
4228
|
+
"The final repository contains an undeclared, transient, or Unicode-unsafe change.",
|
|
4229
|
+
"",
|
|
4230
|
+
preFinalAudit.summary || "workspace hygiene failed",
|
|
4231
|
+
].join("\n"),
|
|
4232
|
+
}],
|
|
4233
|
+
details: {
|
|
4234
|
+
mode: "execute",
|
|
4235
|
+
policy,
|
|
4236
|
+
steps,
|
|
4237
|
+
structuredPlan,
|
|
4238
|
+
scheduled,
|
|
4239
|
+
durableWork,
|
|
4240
|
+
verdictMatrix,
|
|
4241
|
+
executionContract,
|
|
4242
|
+
preFinalAudit,
|
|
4243
|
+
traceID,
|
|
4244
|
+
},
|
|
4245
|
+
isError: true,
|
|
4246
|
+
};
|
|
4247
|
+
}
|
|
4248
|
+
|
|
2896
4249
|
let durableFinalization: any = null;
|
|
2897
4250
|
if (durableWork) {
|
|
2898
|
-
const finalEvidence =
|
|
2899
|
-
integration.output,
|
|
2900
|
-
visualResult?.output || "",
|
|
2901
|
-
].filter(Boolean).join("\n\n--- VISUAL ---\n\n");
|
|
4251
|
+
const finalEvidence = finalEvidenceForContract;
|
|
2902
4252
|
try {
|
|
2903
4253
|
durableFinalization = await durableRecordIntegration(
|
|
2904
4254
|
cwd,
|
|
@@ -2921,6 +4271,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
2921
4271
|
}
|
|
2922
4272
|
}
|
|
2923
4273
|
|
|
4274
|
+
if (durableWork) {
|
|
4275
|
+
await finalizeExecutionContractArtifacts(
|
|
4276
|
+
durableWork.dir,
|
|
4277
|
+
executionContract,
|
|
4278
|
+
verdictMatrix,
|
|
4279
|
+
finalEvidenceForContract,
|
|
4280
|
+
).catch(() => null);
|
|
4281
|
+
}
|
|
4282
|
+
|
|
2924
4283
|
const memoryFiles = [...new Set(structuredPlan.tasks.flatMap((task: any) => taskWriteFiles(task)))];
|
|
2925
4284
|
const memory = durableWork
|
|
2926
4285
|
? durableFinalization?.finalized?.memory || null
|
|
@@ -2932,6 +4291,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
2932
4291
|
`UES scheduled execution PASS across ${structuredPlan.tasks.length} task(s).`,
|
|
2933
4292
|
`Safe waves: ${scheduled.schedule?.safeWaves?.length || 0}; integrations: ${scheduled.integrations?.length || 0}.`,
|
|
2934
4293
|
durableWork ? `Durable state: .ues-work/${durableWork.slug} finalized with fresh integration receipt.` : "",
|
|
4294
|
+
verdictMatrix.source,
|
|
4295
|
+
verdictMatrix.runtime,
|
|
4296
|
+
verdictMatrix.dbClean,
|
|
4297
|
+
verdictMatrix.device,
|
|
2935
4298
|
"",
|
|
2936
4299
|
integration.output,
|
|
2937
4300
|
].join("\n"),
|
|
@@ -2946,6 +4309,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
2946
4309
|
memory,
|
|
2947
4310
|
durableWork,
|
|
2948
4311
|
durableFinalization,
|
|
4312
|
+
verdictMatrix,
|
|
4313
|
+
executionContract,
|
|
2949
4314
|
traceID,
|
|
2950
4315
|
},
|
|
2951
4316
|
};
|
|
@@ -2979,18 +4344,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
2979
4344
|
recentFailure = diagnosis.output;
|
|
2980
4345
|
}
|
|
2981
4346
|
|
|
2982
|
-
const
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
4347
|
+
const fastDecision = turboFastPathDecision(policy, {
|
|
4348
|
+
role: "executor",
|
|
4349
|
+
attempt,
|
|
4350
|
+
browserRequested: browserEvidenceNeeded(params.task, "executor"),
|
|
4351
|
+
visualRequired: visualEvidenceNeeded(params.task),
|
|
4352
|
+
});
|
|
4353
|
+
const fastBoundedLane = fastDecision.eligible;
|
|
2988
4354
|
const fastAttemptStartedAt = Date.now();
|
|
2989
4355
|
const executorTask = [
|
|
2990
4356
|
params.task,
|
|
2991
4357
|
recentFailure ? "\nEvidence from diagnosis/previous failed verification:\n" + cap(recentFailure, 7000) : "",
|
|
2992
4358
|
fastBoundedLane
|
|
2993
|
-
? "\nFAST bounded rule: stay on the named file
|
|
4359
|
+
? "\nFAST bounded rule: stay on the named target file. Read that file first; do not inventory the repository or run broad searches unless the target is missing or direct evidence proves wider scope. Make the smallest edit, then run one focused behavioral check and stop once a fresh PASS receipt covers every explicit branch. If no project-native JS/TS test exists, use one temporary .ues-cache/fast-acceptance.test.mjs probe, run node --test .ues-cache/fast-acceptance.test.mjs, and remove only that probe afterwards. Syntax/build alone is insufficient."
|
|
2994
4360
|
: "",
|
|
2995
4361
|
"\nImplement the smallest coherent change. Do not push, publish, deploy, rewrite history, or broaden scope without evidence.",
|
|
2996
4362
|
].filter(Boolean).join("\n");
|
|
@@ -3008,6 +4374,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
3008
4374
|
continue;
|
|
3009
4375
|
}
|
|
3010
4376
|
|
|
4377
|
+
const inheritedDirtyCheck = detectInheritedDirtyViolations(
|
|
4378
|
+
cwd,
|
|
4379
|
+
executionContract.inheritedDirty,
|
|
4380
|
+
executionContract.approvedInheritedDirtyPaths || [],
|
|
4381
|
+
);
|
|
4382
|
+
if (!inheritedDirtyCheck.safe) {
|
|
4383
|
+
await recordRuntimeOutcome(implementation, params.task, false, attempt - 1);
|
|
4384
|
+
return {
|
|
4385
|
+
content: [{
|
|
4386
|
+
type: "text",
|
|
4387
|
+
text: [
|
|
4388
|
+
"UES inherited dirty-work guard stopped completion.",
|
|
4389
|
+
"The implementation changed pre-existing dirty source work that was not explicitly authorized by the original task.",
|
|
4390
|
+
"UES will not auto-restore or overwrite these files.",
|
|
4391
|
+
"",
|
|
4392
|
+
...inheritedDirtyCheck.violations.map((item: any) => "- " + item.path),
|
|
4393
|
+
].join("\n"),
|
|
4394
|
+
}],
|
|
4395
|
+
details: {
|
|
4396
|
+
mode: "execute",
|
|
4397
|
+
policy,
|
|
4398
|
+
steps,
|
|
4399
|
+
attempts: attempt,
|
|
4400
|
+
executionContract,
|
|
4401
|
+
inheritedDirtyCheck,
|
|
4402
|
+
traceID,
|
|
4403
|
+
},
|
|
4404
|
+
isError: true,
|
|
4405
|
+
};
|
|
4406
|
+
}
|
|
4407
|
+
|
|
3011
4408
|
let verification: RunResult;
|
|
3012
4409
|
let fastGate: any = null;
|
|
3013
4410
|
if (fastBoundedLane) {
|
|
@@ -3029,7 +4426,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
3029
4426
|
optimizations: { fastDeterministicVerification: true, behavioralReceiptCount: fastGate.behavioralReceipts.length },
|
|
3030
4427
|
};
|
|
3031
4428
|
steps.push(verification);
|
|
3032
|
-
onUpdate?.({
|
|
4429
|
+
onUpdate?.({
|
|
4430
|
+
content: [{ type: "text", text: "UES Turbo Fast Path: reused " + fastGate.behavioralReceipts.length + " fresh behavioral receipt(s); skipped verifier model turn" }],
|
|
4431
|
+
details: { mode: "execute", phase: "turbo-fast-verified", policy, fastGate, fastDecision, traceID },
|
|
4432
|
+
});
|
|
3033
4433
|
} else {
|
|
3034
4434
|
verification = await run(
|
|
3035
4435
|
"ues-verifier",
|
|
@@ -3125,7 +4525,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3125
4525
|
requireVisual: visualEvidenceNeeded(params.task),
|
|
3126
4526
|
workspaceSnapshot: completionSnapshot,
|
|
3127
4527
|
behavioralReceipts: freshReceipts,
|
|
3128
|
-
requireBehavioralReceipt:
|
|
4528
|
+
requireBehavioralReceipt: policy.requireBehavioralReceipt !== false,
|
|
3129
4529
|
});
|
|
3130
4530
|
if (!completionAudit.passed) {
|
|
3131
4531
|
recentFailure = "Completion auditor rejected PASS: " + completionAudit.failures.join(", ");
|
|
@@ -3138,6 +4538,83 @@ export default function (pi: ExtensionAPI) {
|
|
|
3138
4538
|
};
|
|
3139
4539
|
}
|
|
3140
4540
|
|
|
4541
|
+
const verdictMatrix = buildFinalVerdictMatrix(params.task, {
|
|
4542
|
+
contract: executionContract,
|
|
4543
|
+
primaryPass: true,
|
|
4544
|
+
integrationPass: integrationResult ? true : undefined,
|
|
4545
|
+
primaryOutput: verification.output,
|
|
4546
|
+
integrationOutput: integrationResult?.output || "",
|
|
4547
|
+
visualOutput: visualResult?.output || "",
|
|
4548
|
+
primaryChecks: verification.report?.sections?.["checks-run"] || "",
|
|
4549
|
+
integrationChecks: integrationResult?.report?.sections?.["checks-run"] || "",
|
|
4550
|
+
visualChecks: visualResult?.report?.sections?.["checks-run"] || "",
|
|
4551
|
+
});
|
|
4552
|
+
|
|
4553
|
+
if (verdictMatrix.final !== "PASS") {
|
|
4554
|
+
await recordRuntimeOutcome(implementation, params.task, false, attempt - 1);
|
|
4555
|
+
const deviceOnlyPending =
|
|
4556
|
+
verdictMatrix.final === "SOURCE_RUNTIME_PASS_DEVICE_NOT_VERIFIED";
|
|
4557
|
+
return {
|
|
4558
|
+
content: [{
|
|
4559
|
+
type: "text",
|
|
4560
|
+
text: [
|
|
4561
|
+
deviceOnlyPending
|
|
4562
|
+
? "UES source/runtime verification passed, but requested real-device verification is still pending."
|
|
4563
|
+
: "UES deterministic final gate is not fully verified.",
|
|
4564
|
+
"",
|
|
4565
|
+
verdictMatrix.source,
|
|
4566
|
+
verdictMatrix.runtime,
|
|
4567
|
+
verdictMatrix.dbClean,
|
|
4568
|
+
verdictMatrix.device,
|
|
4569
|
+
"",
|
|
4570
|
+
integrationResult?.output || verification.output,
|
|
4571
|
+
].join("\n"),
|
|
4572
|
+
}],
|
|
4573
|
+
details: {
|
|
4574
|
+
mode: "execute",
|
|
4575
|
+
policy,
|
|
4576
|
+
steps,
|
|
4577
|
+
attempts: attempt,
|
|
4578
|
+
completionAudit,
|
|
4579
|
+
verdictMatrix,
|
|
4580
|
+
executionContract,
|
|
4581
|
+
traceID,
|
|
4582
|
+
},
|
|
4583
|
+
isError: !deviceOnlyPending,
|
|
4584
|
+
};
|
|
4585
|
+
}
|
|
4586
|
+
|
|
4587
|
+
const preFinalAudit = await preFinalWorkspaceAudit(cwd, {
|
|
4588
|
+
baseline: controllerHygieneBaseline,
|
|
4589
|
+
taskText: params.task,
|
|
4590
|
+
strictScope: false,
|
|
4591
|
+
allowSourceMutations: true,
|
|
4592
|
+
}).catch((error) => ({
|
|
4593
|
+
safe: false,
|
|
4594
|
+
summary: "pre-final workspace audit failed: " + (error instanceof Error ? error.message : String(error)),
|
|
4595
|
+
findings: [],
|
|
4596
|
+
}));
|
|
4597
|
+
if (!preFinalAudit.safe) {
|
|
4598
|
+
recentFailure = "Pre-final workspace audit rejected PASS: " + (preFinalAudit.summary || "workspace hygiene failed");
|
|
4599
|
+
await recordRuntimeOutcome(implementation, params.task, false, attempt - 1);
|
|
4600
|
+
if (attempt < maxAttempts) continue;
|
|
4601
|
+
return {
|
|
4602
|
+
content: [{ type: "text", text: recentFailure }],
|
|
4603
|
+
details: {
|
|
4604
|
+
mode: "execute",
|
|
4605
|
+
policy,
|
|
4606
|
+
steps,
|
|
4607
|
+
attempts: attempt,
|
|
4608
|
+
completionAudit,
|
|
4609
|
+
verdictMatrix,
|
|
4610
|
+
executionContract,
|
|
4611
|
+
preFinalAudit,
|
|
4612
|
+
traceID,
|
|
4613
|
+
},
|
|
4614
|
+
isError: true,
|
|
4615
|
+
};
|
|
4616
|
+
}
|
|
4617
|
+
|
|
3141
4618
|
await recordRuntimeOutcome(implementation, params.task, true, attempt - 1);
|
|
3142
4619
|
const memory = await rememberVerifiedTask(cwd, params.task, verification, integrationResult);
|
|
3143
4620
|
const final = steps.at(-1);
|
|
@@ -3147,11 +4624,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
3147
4624
|
text: [
|
|
3148
4625
|
`UES execution PASS after ${attempt} attempt(s).`,
|
|
3149
4626
|
`Policy: ${policy.executionProfile}/${policy.risk}; model tier: ${implementation.modelTier || "default"}.`,
|
|
4627
|
+
verdictMatrix.source,
|
|
4628
|
+
verdictMatrix.runtime,
|
|
4629
|
+
verdictMatrix.dbClean,
|
|
4630
|
+
verdictMatrix.device,
|
|
3150
4631
|
"",
|
|
3151
4632
|
final?.output || verification.output,
|
|
3152
4633
|
].join("\n"),
|
|
3153
4634
|
}],
|
|
3154
|
-
details: { mode: "execute", policy, steps, attempts: attempt, memory, completionAudit, traceID },
|
|
4635
|
+
details: { mode: "execute", policy, steps, attempts: attempt, memory, completionAudit, verdictMatrix, executionContract, traceID },
|
|
3155
4636
|
};
|
|
3156
4637
|
}
|
|
3157
4638
|
|
|
@@ -3164,15 +4645,373 @@ export default function (pi: ExtensionAPI) {
|
|
|
3164
4645
|
isError: true,
|
|
3165
4646
|
};
|
|
3166
4647
|
},
|
|
4648
|
+
};
|
|
4649
|
+
pi.registerTool(uesExecuteTool);
|
|
4650
|
+
|
|
4651
|
+
// Extension commands are resolved before prompt templates in Pi. Registering
|
|
4652
|
+
// /ues-run here makes controller admission deterministic: weak models never
|
|
4653
|
+
// have to remember to call ues_execute themselves.
|
|
4654
|
+
pi.registerCommand("ues-status", {
|
|
4655
|
+
description: "Show the loaded UES runtime version and current session policy basics",
|
|
4656
|
+
handler: async (_args, ctx) => {
|
|
4657
|
+
const status = [
|
|
4658
|
+
"UES runtime: " + PACKAGE_VERSION,
|
|
4659
|
+
"Package root: " + PACKAGE_ROOT,
|
|
4660
|
+
"Child runtime: " + CHILD_RUNTIME,
|
|
4661
|
+
"Adaptive context: " + (ADAPTIVE_CONTEXT_ENABLED ? "on" : "off"),
|
|
4662
|
+
"Unified workspace snapshot V2: on",
|
|
4663
|
+
"Parallel context preparation: on",
|
|
4664
|
+
"Git-index affected-test inventory: on",
|
|
4665
|
+
"Parallel verification evidence I/O: on",
|
|
4666
|
+
"Micro skills: " + (MICRO_SKILLS_ENABLED ? "on" : "off"),
|
|
4667
|
+
"Turbo Fast Path: on",
|
|
4668
|
+
"Parent UES tools hidden outside UES runs: on",
|
|
4669
|
+
"Zero-friction engineering admission: " + (AUTO_ADMISSION_ENABLED ? "on (native / auto / high-risk + safe continuation)" : "off"),
|
|
4670
|
+
"Git-root artifact guard: on",
|
|
4671
|
+
"Inherited dirty-work guard: on",
|
|
4672
|
+
"Local .env mutation guard: on",
|
|
4673
|
+
"Portable temp-path guard: on",
|
|
4674
|
+
"Explicit phase barriers: on",
|
|
4675
|
+
"Independent final verdict matrix: on",
|
|
4676
|
+
"Unicode source hygiene: blocking bidi/zero-width/control/homoglyph audit",
|
|
4677
|
+
"Post-run file hygiene: transient cleanup + read-only mutation guard",
|
|
4678
|
+
"Pre-final workspace audit: on",
|
|
4679
|
+
"Disk hygiene: bounded + auto-clean",
|
|
4680
|
+
"Writer concurrency: " + MAX_WRITER_CONCURRENCY,
|
|
4681
|
+
].join("\n");
|
|
4682
|
+
pi.sendMessage({
|
|
4683
|
+
customType: "ues-runtime-status",
|
|
4684
|
+
content: status,
|
|
4685
|
+
display: true,
|
|
4686
|
+
details: { version: PACKAGE_VERSION, packageRoot: PACKAGE_ROOT, childRuntime: CHILD_RUNTIME },
|
|
4687
|
+
}, { triggerTurn: false });
|
|
4688
|
+
try { ctx.ui.notify("UES runtime " + PACKAGE_VERSION + " loaded", "info"); } catch {}
|
|
4689
|
+
},
|
|
4690
|
+
});
|
|
4691
|
+
|
|
4692
|
+
pi.registerCommand("ues-clean", {
|
|
4693
|
+
description: "Safely remove stale UES task sandboxes and orphan metadata for the current repository",
|
|
4694
|
+
handler: async (_args, ctx) => {
|
|
4695
|
+
if (uesModeActive()) {
|
|
4696
|
+
try { ctx.ui.notify("UES cleanup refused while a UES task is active", "warning"); } catch {}
|
|
4697
|
+
return;
|
|
4698
|
+
}
|
|
4699
|
+
let workspaceRoot: string;
|
|
4700
|
+
try {
|
|
4701
|
+
workspaceRoot = requireGitWorkspaceRoot(ctx.cwd, "/ues-clean");
|
|
4702
|
+
} catch (error) {
|
|
4703
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4704
|
+
pi.sendMessage({
|
|
4705
|
+
customType: "ues-cleanup-result",
|
|
4706
|
+
content: "UES cleanup refused unsafe workspace.\n" + message,
|
|
4707
|
+
display: true,
|
|
4708
|
+
details: { error: message },
|
|
4709
|
+
}, { triggerTurn: false });
|
|
4710
|
+
try { ctx.ui.notify(message, "error"); } catch {}
|
|
4711
|
+
return;
|
|
4712
|
+
}
|
|
4713
|
+
const cleanup = await pruneOrphanTaskSandboxes(workspaceRoot, {
|
|
4714
|
+
minAgeMs: 5 * 60_000,
|
|
4715
|
+
legacyMinAgeMs: 30 * 60_000,
|
|
4716
|
+
ownedMinAgeMs: 0,
|
|
4717
|
+
reclaimOwnerPid: process.pid,
|
|
4718
|
+
protectedDirs: [...ACTIVE_TASK_SANDBOXES.keys()],
|
|
4719
|
+
}).catch((error) => ({
|
|
4720
|
+
removed: [],
|
|
4721
|
+
skipped: [],
|
|
4722
|
+
sidecarsRemoved: [],
|
|
4723
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4724
|
+
}));
|
|
4725
|
+
const removed = Number(cleanup?.removed?.length || 0);
|
|
4726
|
+
const sidecars = Number(cleanup?.sidecarsRemoved?.length || 0);
|
|
4727
|
+
const skipped = Number(cleanup?.skipped?.length || 0);
|
|
4728
|
+
await stopAllServices(workspaceRoot).catch(() => []);
|
|
4729
|
+
const transientDirs = [".ues-traces", ".ues-services", ".ues-dashboard"];
|
|
4730
|
+
const removedRuntimeDirs: string[] = [];
|
|
4731
|
+
for (const name of transientDirs) {
|
|
4732
|
+
const target = path.join(workspaceRoot, name);
|
|
4733
|
+
if (!fs.existsSync(target)) continue;
|
|
4734
|
+
await fs.promises.rm(target, { recursive: true, force: true }).catch(() => {});
|
|
4735
|
+
if (!fs.existsSync(target)) removedRuntimeDirs.push(name);
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4738
|
+
const cacheDir = path.join(workspaceRoot, ".ues-cache");
|
|
4739
|
+
const removedCacheEntries: string[] = [];
|
|
4740
|
+
if (fs.existsSync(cacheDir)) {
|
|
4741
|
+
const entries = await fs.promises.readdir(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
4742
|
+
for (const entry of entries) {
|
|
4743
|
+
if (entry.name === "evidence-v1") continue;
|
|
4744
|
+
if (
|
|
4745
|
+
entry.name === "semantic-index-v1.json" ||
|
|
4746
|
+
entry.name.startsWith("semantic-index-v1.json.") ||
|
|
4747
|
+
entry.name === "verification-broker-v1.json" ||
|
|
4748
|
+
entry.name.startsWith("verification-broker-v1.json.")
|
|
4749
|
+
) {
|
|
4750
|
+
await fs.promises.rm(path.join(cacheDir, entry.name), { recursive: true, force: true }).catch(() => {});
|
|
4751
|
+
removedCacheEntries.push(entry.name);
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4755
|
+
const evidenceGc = await gcEvidenceStore(workspaceRoot, {
|
|
4756
|
+
maxBytes: 96 * 1024 * 1024,
|
|
4757
|
+
maxEntries: 600,
|
|
4758
|
+
maxAgeDays: 14,
|
|
4759
|
+
}).catch((error) => ({
|
|
4760
|
+
removed: [],
|
|
4761
|
+
removedCount: 0,
|
|
4762
|
+
protectedEntries: 0,
|
|
4763
|
+
protectedBytes: 0,
|
|
4764
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4765
|
+
}));
|
|
4766
|
+
|
|
4767
|
+
if (fs.existsSync(cacheDir)) {
|
|
4768
|
+
const remaining = await fs.promises.readdir(cacheDir).catch(() => ["unknown"]);
|
|
4769
|
+
if (remaining.length === 0) await fs.promises.rm(cacheDir, { recursive: true, force: true }).catch(() => {});
|
|
4770
|
+
}
|
|
4771
|
+
|
|
4772
|
+
const text = [
|
|
4773
|
+
"UES cleanup complete.",
|
|
4774
|
+
"Removed sandboxes: " + removed,
|
|
4775
|
+
"Removed orphan metadata: " + sidecars,
|
|
4776
|
+
"Removed transient runtime dirs: " + (removedRuntimeDirs.join(", ") || "none"),
|
|
4777
|
+
"Removed rebuildable cache entries: " + (removedCacheEntries.length || 0),
|
|
4778
|
+
"Pruned evidence blobs: " + Number(evidenceGc?.removedCount || 0),
|
|
4779
|
+
"Protected verified-memory evidence: " + Number(evidenceGc?.protectedEntries || 0),
|
|
4780
|
+
"Protected/recent entries kept: " + skipped,
|
|
4781
|
+
"Preserved durable state: .ues-work, .ues-memory, .ues-learning, .ues-evals and verified-memory evidence",
|
|
4782
|
+
cleanup?.baseRemoved ? "Sandbox base directory removed because it is empty." : "",
|
|
4783
|
+
cleanup?.error ? "Error: " + cleanup.error : "",
|
|
4784
|
+
].filter(Boolean).join("\n");
|
|
4785
|
+
pi.sendMessage({
|
|
4786
|
+
customType: "ues-cleanup-result",
|
|
4787
|
+
content: text,
|
|
4788
|
+
display: true,
|
|
4789
|
+
details: { ...cleanup, removedRuntimeDirs, removedCacheEntries, evidenceGc },
|
|
4790
|
+
}, { triggerTurn: false });
|
|
4791
|
+
try {
|
|
4792
|
+
ctx.ui.notify(
|
|
4793
|
+
"UES cleanup removed/pruned " +
|
|
4794
|
+
(removed + sidecars + removedRuntimeDirs.length + removedCacheEntries.length + Number(evidenceGc?.removedCount || 0)) +
|
|
4795
|
+
" stale/transient artifact(s)",
|
|
4796
|
+
"info",
|
|
4797
|
+
);
|
|
4798
|
+
} catch {}
|
|
4799
|
+
},
|
|
4800
|
+
});
|
|
4801
|
+
|
|
4802
|
+
directControllerRunner = async (task, ctx, admission, admissionDecision) => {
|
|
4803
|
+
if (directControllerAbort && !directControllerAbort.signal.aborted) {
|
|
4804
|
+
try { ctx.ui.notify("UES controller is already running in this session", "warning"); } catch {}
|
|
4805
|
+
return;
|
|
4806
|
+
}
|
|
4807
|
+
|
|
4808
|
+
let workspaceRoot: string;
|
|
4809
|
+
try {
|
|
4810
|
+
workspaceRoot = requireGitWorkspaceRoot(ctx.cwd, admission === "automatic" ? "automatic UES admission" : "/ues-run");
|
|
4811
|
+
} catch (error) {
|
|
4812
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4813
|
+
pi.sendMessage({
|
|
4814
|
+
customType: "ues-controller-result",
|
|
4815
|
+
content: message,
|
|
4816
|
+
display: true,
|
|
4817
|
+
details: { controllerUsed: false, controllerPass: false, reason: "unsafe-workspace-root" },
|
|
4818
|
+
}, { triggerTurn: false });
|
|
4819
|
+
try { ctx.ui.notify(message, "error"); } catch {}
|
|
4820
|
+
return;
|
|
4821
|
+
}
|
|
4822
|
+
|
|
4823
|
+
const abort = new AbortController();
|
|
4824
|
+
directControllerAbort = abort;
|
|
4825
|
+
const directTraceID = createTraceID(admission === "automatic" ? "ues-auto" : "ues-run");
|
|
4826
|
+
syncSessionIdentity(uesSessionName("run", task, workspaceRoot), ctx);
|
|
4827
|
+
let result: any;
|
|
4828
|
+
let lastProgressNoticeAt = 0;
|
|
4829
|
+
let lastProgressKey = "";
|
|
4830
|
+
try {
|
|
4831
|
+
const directPolicy =
|
|
4832
|
+
admissionDecision?.policy && typeof admissionDecision.policy === "object"
|
|
4833
|
+
? admissionDecision.policy
|
|
4834
|
+
: classifyEngineeringTask(task);
|
|
4835
|
+
const routeLabel =
|
|
4836
|
+
admission === "automatic"
|
|
4837
|
+
? admissionDecision?.route === "guarded"
|
|
4838
|
+
? "high-risk auto controller started ("
|
|
4839
|
+
: "auto controller started ("
|
|
4840
|
+
: "controller started (";
|
|
4841
|
+
try {
|
|
4842
|
+
ctx.ui.notify(
|
|
4843
|
+
"UES " + PACKAGE_VERSION + ": " + routeLabel +
|
|
4844
|
+
String(directPolicy.executionProfile || directPolicy.mode || "unknown") +
|
|
4845
|
+
"/" + String(directPolicy.risk || "unknown") + ")",
|
|
4846
|
+
admissionDecision?.route === "guarded" ? "warning" : "info",
|
|
4847
|
+
);
|
|
4848
|
+
} catch {}
|
|
4849
|
+
result = await uesExecuteTool.execute(
|
|
4850
|
+
`ues-run-${randomUUID()}`,
|
|
4851
|
+
{ task, cwd: workspaceRoot, __traceID: directTraceID, __taskPolicy: directPolicy },
|
|
4852
|
+
abort.signal,
|
|
4853
|
+
(update: any) => {
|
|
4854
|
+
const text = (update?.content || [])
|
|
4855
|
+
.filter((part: any) => part?.type === "text" && typeof part.text === "string")
|
|
4856
|
+
.map((part: any) => part.text)
|
|
4857
|
+
.join("\n")
|
|
4858
|
+
.trim();
|
|
4859
|
+
if (text) {
|
|
4860
|
+
try { ctx.ui.setStatus("ues-run", cap(text, 180)); } catch {}
|
|
4861
|
+
}
|
|
4862
|
+
|
|
4863
|
+
const progress = update?.details?.progress;
|
|
4864
|
+
if (progress) {
|
|
4865
|
+
const progressKey = String(progress.agent || "") + ":" + String(update?.details?.phase || "");
|
|
4866
|
+
const now = Date.now();
|
|
4867
|
+
const phaseChanged = progressKey && progressKey !== lastProgressKey;
|
|
4868
|
+
const heartbeatDue = now - lastProgressNoticeAt >= 60_000;
|
|
4869
|
+
if (phaseChanged || heartbeatDue) {
|
|
4870
|
+
const message =
|
|
4871
|
+
"UES: " + String(progress.agent || "worker") +
|
|
4872
|
+
" running " + Math.round(Number(progress.elapsedMs || 0) / 1000) + "s" +
|
|
4873
|
+
" (idle " + Math.round(Number(progress.idleMs || 0) / 1000) + "s, tools " +
|
|
4874
|
+
Number(progress.toolCalls || 0) +
|
|
4875
|
+
(progress.activeTool ? ", " + String(progress.activeTool) : "") + ")";
|
|
4876
|
+
try {
|
|
4877
|
+
ctx.ui.notify(
|
|
4878
|
+
message,
|
|
4879
|
+
Number(progress.idleMs || 0) >= 45_000 ? "warning" : "info",
|
|
4880
|
+
);
|
|
4881
|
+
} catch {}
|
|
4882
|
+
lastProgressNoticeAt = now;
|
|
4883
|
+
lastProgressKey = progressKey;
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
|
|
4887
|
+
if (process.env.UES_EVAL_DIRECT_TELEMETRY === "1") {
|
|
4888
|
+
process.stderr.write(JSON.stringify({
|
|
4889
|
+
type: "ues_controller_progress",
|
|
4890
|
+
phase: update?.details?.phase || null,
|
|
4891
|
+
task: update?.details?.task || null,
|
|
4892
|
+
agent: progress?.agent || null,
|
|
4893
|
+
elapsedMs: Number(progress?.elapsedMs || 0),
|
|
4894
|
+
idleMs: Number(progress?.idleMs || 0),
|
|
4895
|
+
toolCalls: Number(progress?.toolCalls || 0),
|
|
4896
|
+
activeTool: progress?.activeTool || null,
|
|
4897
|
+
note: progress?.note || null,
|
|
4898
|
+
text: text ? cap(text, 240) : null,
|
|
4899
|
+
}) + "\n");
|
|
4900
|
+
}
|
|
4901
|
+
},
|
|
4902
|
+
ctx,
|
|
4903
|
+
);
|
|
4904
|
+
} catch (error) {
|
|
4905
|
+
result = {
|
|
4906
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
4907
|
+
details: { mode: "execute", reason: "direct-controller-exception" },
|
|
4908
|
+
isError: true,
|
|
4909
|
+
};
|
|
4910
|
+
} finally {
|
|
4911
|
+
const traceRemoved = await cleanupTraceSandboxes(workspaceRoot, directTraceID).catch(() => 0);
|
|
4912
|
+
const staleCleanup = await pruneOrphanTaskSandboxes(workspaceRoot, {
|
|
4913
|
+
minAgeMs: 5 * 60_000,
|
|
4914
|
+
legacyMinAgeMs: 30 * 60_000,
|
|
4915
|
+
ownedMinAgeMs: 0,
|
|
4916
|
+
reclaimOwnerPid: process.pid,
|
|
4917
|
+
protectedDirs: [...ACTIVE_TASK_SANDBOXES.keys()],
|
|
4918
|
+
}).catch(() => ({ removed: [], skipped: [], sidecarsRemoved: [] }));
|
|
4919
|
+
const cleaned =
|
|
4920
|
+
Number(traceRemoved || 0) +
|
|
4921
|
+
Number(staleCleanup?.removed?.length || 0) +
|
|
4922
|
+
Number(staleCleanup?.sidecarsRemoved?.length || 0);
|
|
4923
|
+
if (cleaned > 0) {
|
|
4924
|
+
try { ctx.ui.notify("UES cleanup: removed " + cleaned + " stale sandbox artifact(s)", "info"); } catch {}
|
|
4925
|
+
}
|
|
4926
|
+
if (directControllerAbort === abort) directControllerAbort = null;
|
|
4927
|
+
try { ctx.ui.setStatus("ues-run", undefined); } catch {}
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
const content = (result?.content || [])
|
|
4931
|
+
.filter((part: any) => part?.type === "text" && typeof part.text === "string")
|
|
4932
|
+
.map((part: any) => part.text)
|
|
4933
|
+
.join("\n")
|
|
4934
|
+
.trim() || "(UES controller returned no text)";
|
|
4935
|
+
const controllerPass = result?.isError !== true;
|
|
4936
|
+
|
|
4937
|
+
if (process.env.UES_EVAL_DIRECT_TELEMETRY === "1") {
|
|
4938
|
+
const details = result?.details || null;
|
|
4939
|
+
const telemetryDetails = details
|
|
4940
|
+
? {
|
|
4941
|
+
mode: details.mode,
|
|
4942
|
+
attempts: details.attempts,
|
|
4943
|
+
policy: details.policy
|
|
4944
|
+
? {
|
|
4945
|
+
mode: details.policy.mode,
|
|
4946
|
+
executionProfile: details.policy.executionProfile,
|
|
4947
|
+
risk: details.policy.risk,
|
|
4948
|
+
}
|
|
4949
|
+
: null,
|
|
4950
|
+
steps: Array.isArray(details.steps)
|
|
4951
|
+
? details.steps.map((step: any) => ({
|
|
4952
|
+
agent: step?.agent,
|
|
4953
|
+
exitCode: step?.exitCode,
|
|
4954
|
+
optimizations: step?.optimizations || null,
|
|
4955
|
+
usage: step?.usage || null,
|
|
4956
|
+
toolCalls: Number(step?.toolCalls || 0),
|
|
4957
|
+
toolNames: Array.isArray(step?.toolNames) ? step.toolNames : [],
|
|
4958
|
+
}))
|
|
4959
|
+
: [],
|
|
4960
|
+
}
|
|
4961
|
+
: null;
|
|
4962
|
+
process.stderr.write(JSON.stringify({
|
|
4963
|
+
type: "ues_controller_direct",
|
|
4964
|
+
controllerUsed: true,
|
|
4965
|
+
controllerPass,
|
|
4966
|
+
details: telemetryDetails,
|
|
4967
|
+
}) + "\n");
|
|
4968
|
+
}
|
|
4969
|
+
|
|
4970
|
+
pi.sendMessage({
|
|
4971
|
+
customType: "ues-controller-result",
|
|
4972
|
+
content,
|
|
4973
|
+
display: true,
|
|
4974
|
+
details: {
|
|
4975
|
+
controllerUsed: true,
|
|
4976
|
+
controllerPass,
|
|
4977
|
+
admission,
|
|
4978
|
+
admissionRoute: admissionDecision?.route || (admission === "command" ? "command" : "auto"),
|
|
4979
|
+
admissionConfidence: admissionDecision?.confidence || null,
|
|
4980
|
+
admissionReason: admissionDecision?.reason || null,
|
|
4981
|
+
...(result?.details || {}),
|
|
4982
|
+
},
|
|
4983
|
+
});
|
|
4984
|
+
|
|
4985
|
+
try {
|
|
4986
|
+
ctx.ui.notify(
|
|
4987
|
+
controllerPass ? "UES " + PACKAGE_VERSION + ": verified controller run completed" : "UES " + PACKAGE_VERSION + ": controller run failed verification",
|
|
4988
|
+
controllerPass ? "info" : "error",
|
|
4989
|
+
);
|
|
4990
|
+
} catch {}
|
|
4991
|
+
|
|
4992
|
+
};
|
|
4993
|
+
|
|
4994
|
+
pi.registerCommand("ues-run", {
|
|
4995
|
+
description: "Run an engineering task directly through the deterministic UES controller",
|
|
4996
|
+
handler: async (args, ctx) => {
|
|
4997
|
+
const task = String(args || "").trim();
|
|
4998
|
+
if (!task) {
|
|
4999
|
+
try { ctx.ui.notify("Usage: /ues-run <engineering task>", "warning"); } catch {}
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
5002
|
+
await directControllerRunner?.(task, ctx, "command");
|
|
5003
|
+
},
|
|
3167
5004
|
});
|
|
3168
5005
|
|
|
3169
5006
|
pi.registerTool({
|
|
3170
5007
|
name: "ues_dispatch",
|
|
3171
5008
|
label: "UES Dispatch",
|
|
3172
5009
|
description:
|
|
3173
|
-
"Run bundled UES specialist agents in isolated child Pi processes. Supports single, parallel, or chain mode. Parallel writer agents fail closed unless each writer has an explicit distinct cwd/worktree. Available agents: " +
|
|
5010
|
+
"Run bundled UES specialist agents in isolated child Pi processes, or inspect durable child handles with action=status/list. Every child run writes a small artifact with exact task/output evidence references. Supports single, parallel, or chain mode. Parallel writer agents fail closed unless each writer has an explicit distinct cwd/worktree. Available agents: " +
|
|
3174
5011
|
Object.keys(AGENTS).join(", "),
|
|
3175
5012
|
parameters: Type.Object({
|
|
5013
|
+
action: Type.Optional(Type.Union([Type.Literal("run"), Type.Literal("status"), Type.Literal("list")])),
|
|
5014
|
+
handle: Type.Optional(Type.String({ description: "Subagent artifact handle for status action" })),
|
|
3176
5015
|
agent: Type.Optional(Type.String({ description: "Agent name for single mode" })),
|
|
3177
5016
|
task: Type.Optional(Type.String({ description: "Task for single mode" })),
|
|
3178
5017
|
cwd: Type.Optional(Type.String({ description: "Working directory for single mode" })),
|
|
@@ -3181,6 +5020,39 @@ export default function (pi: ExtensionAPI) {
|
|
|
3181
5020
|
}),
|
|
3182
5021
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
3183
5022
|
refreshHostBrowserToolNames(pi);
|
|
5023
|
+
const action = String(params.action || "run");
|
|
5024
|
+
const baseCwd = ctx.cwd;
|
|
5025
|
+
if (action === "status") {
|
|
5026
|
+
if (!params.handle) {
|
|
5027
|
+
return {
|
|
5028
|
+
content: [{ type: "text", text: "ues_dispatch action=status requires handle." }],
|
|
5029
|
+
details: { mode: "status" },
|
|
5030
|
+
isError: true,
|
|
5031
|
+
};
|
|
5032
|
+
}
|
|
5033
|
+
try {
|
|
5034
|
+
const root = requireGitWorkspaceRoot(baseCwd, "ues_dispatch status");
|
|
5035
|
+
const artifact = await readSubagentArtifact(root, params.handle);
|
|
5036
|
+
return {
|
|
5037
|
+
content: [{ type: "text", text: JSON.stringify(artifact, null, 2) }],
|
|
5038
|
+
details: { mode: "status", artifact },
|
|
5039
|
+
};
|
|
5040
|
+
} catch (error) {
|
|
5041
|
+
return {
|
|
5042
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
5043
|
+
details: { mode: "status", handle: params.handle },
|
|
5044
|
+
isError: true,
|
|
5045
|
+
};
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
if (action === "list") {
|
|
5049
|
+
const root = requireGitWorkspaceRoot(baseCwd, "ues_dispatch list");
|
|
5050
|
+
const artifacts = await listSubagentArtifacts(root, { limit: 20 });
|
|
5051
|
+
return {
|
|
5052
|
+
content: [{ type: "text", text: JSON.stringify(artifacts, null, 2) }],
|
|
5053
|
+
details: { mode: "list", artifacts },
|
|
5054
|
+
};
|
|
5055
|
+
}
|
|
3184
5056
|
const hasSingle = Boolean(params.agent && params.task);
|
|
3185
5057
|
const hasParallel = Boolean(params.tasks?.length);
|
|
3186
5058
|
const hasChain = Boolean(params.chain?.length);
|
|
@@ -3195,7 +5067,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
3195
5067
|
const validateAgent = (name: string): name is AgentName => name in AGENTS;
|
|
3196
5068
|
const model = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
3197
5069
|
const thinking = ctx.thinkingLevel as string | undefined;
|
|
3198
|
-
const baseCwd = ctx.cwd;
|
|
3199
5070
|
|
|
3200
5071
|
if (hasSingle) {
|
|
3201
5072
|
if (!validateAgent(params.agent!)) {
|
|
@@ -3293,7 +5164,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
3293
5164
|
}
|
|
3294
5165
|
|
|
3295
5166
|
let completed = 0;
|
|
3296
|
-
const
|
|
5167
|
+
const dispatchHasWriters = tasks.some((item) => WRITE_AGENTS.has(item.agent));
|
|
5168
|
+
const dispatchConcurrency = dispatchHasWriters
|
|
5169
|
+
? Math.min(MAX_CONCURRENCY, MAX_WRITER_CONCURRENCY)
|
|
5170
|
+
: MAX_CONCURRENCY;
|
|
5171
|
+
const results = await mapLimit(tasks, dispatchConcurrency, async (item) => {
|
|
3297
5172
|
const result = await runRoutedAgent(
|
|
3298
5173
|
item.agent as AgentName,
|
|
3299
5174
|
item.task,
|