opencode-agent-skill 14.4.0 → 15.0.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 +33 -15
- 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 +1 -1
- package/lib/capability-fabric.mjs +17 -0
- package/lib/completion-auditor.mjs +25 -5
- package/lib/evidence-store.mjs +92 -8
- package/lib/execution-contract.mjs +579 -0
- package/lib/leaf-runtime-optimizer.mjs +76 -0
- 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/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/workspace-fingerprint.mjs +2 -11
- 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 +145 -2
- package/pi/extensions/ues.ts +1760 -160
- 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,11 +25,21 @@ 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 { buildExecutionContract, buildFinalVerdictMatrix, captureInheritedDirtyState, detectInheritedDirtyViolations, enforcePhaseGates, executionContractPrompt, phaseArtifactPayloads, taskExplicitlyAllowsLocalEnvWrite } from "../../lib/execution-contract.mjs";
|
|
39
|
+
import { sessionNameFromUesInput, uesSessionName } from "../../lib/session-display.mjs";
|
|
40
|
+
import { requireGitWorkspaceRoot, resolveGitWorkspaceRoot } from "../../lib/workspace-root.mjs";
|
|
41
|
+
import { createAdaptiveDeadline } from "../../lib/activity-deadline.mjs";
|
|
42
|
+
import { extractValidatedPlan } from "../../lib/plan-salvage.mjs";
|
|
32
43
|
import { auditCompletion } from "../../lib/completion-auditor.mjs";
|
|
33
44
|
import { mcpExecutionPolicy } from "../../lib/mcp-tool-policy.mjs";
|
|
34
45
|
import { McpHealthTracker } from "../../lib/mcp-health.mjs";
|
|
@@ -47,14 +58,37 @@ import {
|
|
|
47
58
|
integrateTaskSandbox,
|
|
48
59
|
removeTaskSandbox,
|
|
49
60
|
rollbackTaskSandbox,
|
|
61
|
+
pruneOrphanTaskSandboxes,
|
|
62
|
+
taskSandboxOwnerRoot,
|
|
50
63
|
} from "../../lib/worktree-sandbox.mjs";
|
|
64
|
+
import {
|
|
65
|
+
looksLikeLongRunningServiceCommand,
|
|
66
|
+
restartService,
|
|
67
|
+
serviceLogs,
|
|
68
|
+
serviceStatus,
|
|
69
|
+
startService,
|
|
70
|
+
stopAllServices,
|
|
71
|
+
stopService,
|
|
72
|
+
waitForService,
|
|
73
|
+
} from "../../lib/service-manager.mjs";
|
|
51
74
|
|
|
52
75
|
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
76
|
+
const PACKAGE_VERSION = (() => {
|
|
77
|
+
try {
|
|
78
|
+
return String(JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"))?.version || "unknown");
|
|
79
|
+
} catch {
|
|
80
|
+
return "unknown";
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
53
83
|
const OCSKILL_BIN = path.join(PACKAGE_ROOT, "bin", "ocskill.mjs");
|
|
54
84
|
const CHILD_RUNTIME_EXTENSION = path.join(PACKAGE_ROOT, "pi", "extensions", "ues-child-runtime.ts");
|
|
55
85
|
const AGENT_DIR = path.join(PACKAGE_ROOT, "global-config", "agents");
|
|
56
86
|
const MAX_PARALLEL_TASKS = 8;
|
|
57
87
|
const MAX_CONCURRENCY = 4;
|
|
88
|
+
const MAX_WRITER_CONCURRENCY = Math.max(
|
|
89
|
+
1,
|
|
90
|
+
Math.min(4, Number(process.env.UES_MAX_WRITER_CONCURRENCY || (process.platform === "win32" ? 2 : 3))),
|
|
91
|
+
);
|
|
58
92
|
const OUTPUT_LIMIT = 512 * 1024;
|
|
59
93
|
|
|
60
94
|
function configuredDuration(name: string, fallback: number, min: number, max: number) {
|
|
@@ -93,6 +127,12 @@ const POST_TOOL_ERROR_IDLE_TIMEOUT_MS = configuredDuration(
|
|
|
93
127
|
5_000,
|
|
94
128
|
5 * 60_000,
|
|
95
129
|
);
|
|
130
|
+
const TURBO_FAST_TIMEOUTS = turboFastTimeoutBudget({
|
|
131
|
+
hardTimeoutMs: configuredDuration("UES_FAST_CHILD_HARD_TIMEOUT_MS", 180_000, 30_000, 10 * 60_000),
|
|
132
|
+
idleTimeoutMs: configuredDuration("UES_FAST_CHILD_IDLE_TIMEOUT_MS", 60_000, 20_000, 5 * 60_000),
|
|
133
|
+
postToolErrorIdleTimeoutMs: configuredDuration("UES_FAST_POST_TOOL_ERROR_IDLE_TIMEOUT_MS", 30_000, 5_000, 2 * 60_000),
|
|
134
|
+
verificationTimeoutSec: configuredDuration("UES_FAST_VERIFICATION_TIMEOUT_SEC", 90, 30, 300),
|
|
135
|
+
});
|
|
96
136
|
const MODEL_VISIBLE_OUTPUT_LIMIT = configuredDuration(
|
|
97
137
|
"UES_MODEL_VISIBLE_OUTPUT_LIMIT",
|
|
98
138
|
64 * 1024,
|
|
@@ -160,6 +200,7 @@ function abortActiveCliChildren() {
|
|
|
160
200
|
|
|
161
201
|
let HOST_BROWSER_TOOL_NAMES: string[] = [];
|
|
162
202
|
const CONTEXT_PACK_CACHE = new Map<string, any>();
|
|
203
|
+
const ACTIVE_TASK_SANDBOXES = new Map<string, string>();
|
|
163
204
|
|
|
164
205
|
function configuredBrowserToolNames() {
|
|
165
206
|
return String(process.env.UES_BROWSER_MCP_TOOL_NAMES || "")
|
|
@@ -257,6 +298,8 @@ function getAgentPrompt(agent: AgentName) {
|
|
|
257
298
|
"- Do not call OpenCode-only dispatch tools such as \`ues.dispatch_task\` or \`ues.dispatch_parallel\`.",
|
|
258
299
|
"- Respect the original role's edit/read-only boundary and return evidence to the parent Pi session.",
|
|
259
300
|
"- 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.",
|
|
301
|
+
"- 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.",
|
|
302
|
+
"- 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
303
|
"- 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
304
|
"",
|
|
262
305
|
].join("\n");
|
|
@@ -272,16 +315,8 @@ function getAgentPrompt(agent: AgentName) {
|
|
|
272
315
|
}
|
|
273
316
|
|
|
274
317
|
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 };
|
|
318
|
+
const invocation = resolvePiChildInvocation(args);
|
|
319
|
+
return { command: invocation.command, args: invocation.args };
|
|
285
320
|
}
|
|
286
321
|
|
|
287
322
|
function extractAssistantText(message: any): string {
|
|
@@ -345,6 +380,83 @@ async function runOcskillJson(args: string[], cwd: string, signal?: AbortSignal)
|
|
|
345
380
|
}
|
|
346
381
|
}
|
|
347
382
|
|
|
383
|
+
async function persistExecutionContractArtifacts(dir: string, contract: any) {
|
|
384
|
+
if (!contract || !dir) return null;
|
|
385
|
+
const phasesDir = path.join(dir, "phases");
|
|
386
|
+
await fs.promises.mkdir(phasesDir, { recursive: true });
|
|
387
|
+
await fs.promises.writeFile(
|
|
388
|
+
path.join(dir, "EXECUTION_CONTRACT.json"),
|
|
389
|
+
JSON.stringify(contract, null, 2) + "\n",
|
|
390
|
+
"utf8",
|
|
391
|
+
);
|
|
392
|
+
await fs.promises.writeFile(
|
|
393
|
+
path.join(phasesDir, "MANIFEST.json"),
|
|
394
|
+
JSON.stringify({
|
|
395
|
+
schemaVersion: 1,
|
|
396
|
+
taskHash: contract.taskHash,
|
|
397
|
+
phaseCount: contract.phases?.length || 0,
|
|
398
|
+
artifacts: phaseArtifactPayloads(contract).map((item: any) => item.file),
|
|
399
|
+
}, null, 2) + "\n",
|
|
400
|
+
"utf8",
|
|
401
|
+
);
|
|
402
|
+
for (const item of phaseArtifactPayloads(contract)) {
|
|
403
|
+
const target = path.join(dir, item.file);
|
|
404
|
+
await fs.promises.mkdir(path.dirname(target), { recursive: true });
|
|
405
|
+
await fs.promises.writeFile(target, JSON.stringify(item.value, null, 2) + "\n", "utf8");
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
contract: path.join(dir, "EXECUTION_CONTRACT.json"),
|
|
409
|
+
manifest: path.join(phasesDir, "MANIFEST.json"),
|
|
410
|
+
phaseCount: contract.phases?.length || 0,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function finalizeExecutionContractArtifacts(
|
|
415
|
+
dir: string,
|
|
416
|
+
contract: any,
|
|
417
|
+
verdictMatrix: any,
|
|
418
|
+
evidence: string,
|
|
419
|
+
) {
|
|
420
|
+
if (!dir || !contract) return null;
|
|
421
|
+
const payload = {
|
|
422
|
+
schemaVersion: 1,
|
|
423
|
+
verdictMatrix,
|
|
424
|
+
evidence: cap(evidence || "", 12000),
|
|
425
|
+
finalizedAt: new Date().toISOString(),
|
|
426
|
+
};
|
|
427
|
+
await fs.promises.writeFile(
|
|
428
|
+
path.join(dir, "FINAL_VERDICTS.json"),
|
|
429
|
+
JSON.stringify(payload, null, 2) + "\n",
|
|
430
|
+
"utf8",
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
for (const item of phaseArtifactPayloads(contract)) {
|
|
434
|
+
const phaseText = String(item.value.title || "") + "\n" + String(item.value.sourceBody || "");
|
|
435
|
+
let status = verdictMatrix?.source === "SOURCE_PASS" ? "VERIFIED" : "NOT_VERIFIED";
|
|
436
|
+
if (/device|expo go|thiết bị/i.test(phaseText) && verdictMatrix?.device !== "DEVICE_PASS") {
|
|
437
|
+
status = "NOT_VERIFIED";
|
|
438
|
+
}
|
|
439
|
+
if (/(?:cleanup|fixture|database|db|dữ liệu)/i.test(phaseText) && contract.gates?.dbClean === true && verdictMatrix?.dbClean !== "DB_CLEAN_PASS") {
|
|
440
|
+
status = "NOT_VERIFIED";
|
|
441
|
+
}
|
|
442
|
+
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") {
|
|
443
|
+
status = "NOT_VERIFIED";
|
|
444
|
+
}
|
|
445
|
+
const target = path.join(dir, item.file);
|
|
446
|
+
await fs.promises.writeFile(
|
|
447
|
+
target,
|
|
448
|
+
JSON.stringify({
|
|
449
|
+
...item.value,
|
|
450
|
+
status,
|
|
451
|
+
finalVerdicts: verdictMatrix,
|
|
452
|
+
evidence: [cap(evidence || "", 6000)],
|
|
453
|
+
}, null, 2) + "\n",
|
|
454
|
+
"utf8",
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
return payload;
|
|
458
|
+
}
|
|
459
|
+
|
|
348
460
|
async function initializeDurableControllerWork(
|
|
349
461
|
root: string,
|
|
350
462
|
originalTask: string,
|
|
@@ -540,9 +652,22 @@ async function runAgentCli(
|
|
|
540
652
|
compactToolOutput?: boolean;
|
|
541
653
|
toolOutputLimit?: number;
|
|
542
654
|
verificationTimeoutSec?: number;
|
|
655
|
+
hardTimeoutMs?: number;
|
|
656
|
+
absoluteHardTimeoutMs?: number;
|
|
657
|
+
activityExtensionMs?: number;
|
|
658
|
+
activityWindowMs?: number;
|
|
659
|
+
idleTimeoutMs?: number;
|
|
660
|
+
postToolErrorIdleTimeoutMs?: number;
|
|
661
|
+
allowLocalEnvWrite?: boolean;
|
|
543
662
|
} = {},
|
|
544
663
|
): Promise<RunResult> {
|
|
545
664
|
const config = AGENTS[agent];
|
|
665
|
+
const hardTimeoutMs = Number(runtimeOptions.hardTimeoutMs || CHILD_HARD_TIMEOUT_MS);
|
|
666
|
+
const absoluteHardTimeoutMs = Number(runtimeOptions.absoluteHardTimeoutMs || hardTimeoutMs);
|
|
667
|
+
const activityExtensionMs = Number(runtimeOptions.activityExtensionMs || 0);
|
|
668
|
+
const activityWindowMs = Number(runtimeOptions.activityWindowMs || Math.max(5_000, Math.min(activityExtensionMs || hardTimeoutMs, 30_000)));
|
|
669
|
+
const idleTimeoutMs = Number(runtimeOptions.idleTimeoutMs || CHILD_IDLE_TIMEOUT_MS);
|
|
670
|
+
const postToolErrorIdleTimeoutMs = Number(runtimeOptions.postToolErrorIdleTimeoutMs || POST_TOOL_ERROR_IDLE_TIMEOUT_MS);
|
|
546
671
|
const args: string[] = [
|
|
547
672
|
"--mode", "json", "-p", "--no-session",
|
|
548
673
|
// Keep extension discovery enabled so custom model providers (for example
|
|
@@ -559,6 +684,7 @@ async function runAgentCli(
|
|
|
559
684
|
const allowedTools = [...new Set([
|
|
560
685
|
...config.tools,
|
|
561
686
|
...codeIntelligenceTools,
|
|
687
|
+
"ues_service",
|
|
562
688
|
...extraTools,
|
|
563
689
|
...(runtimeOptions.compactToolOutput ? ["ues_evidence_get"] : []),
|
|
564
690
|
])];
|
|
@@ -588,9 +714,11 @@ async function runAgentCli(
|
|
|
588
714
|
env: {
|
|
589
715
|
...process.env,
|
|
590
716
|
UES_CHILD_PROCESS: "1",
|
|
717
|
+
UES_CHILD_AGENT: agent,
|
|
591
718
|
UES_CHILD_TOOL_COMPACTION: runtimeOptions.compactToolOutput ? "1" : "0",
|
|
592
719
|
UES_CHILD_TOOL_OUTPUT_LIMIT: String(runtimeOptions.toolOutputLimit || 24 * 1024),
|
|
593
720
|
UES_CHILD_VERIFICATION_TIMEOUT_SEC: String(runtimeOptions.verificationTimeoutSec || 300),
|
|
721
|
+
UES_CHILD_ALLOW_LOCAL_ENV_WRITE: runtimeOptions.allowLocalEnvWrite ? "1" : "0",
|
|
594
722
|
},
|
|
595
723
|
shell: false,
|
|
596
724
|
detached: process.platform !== "win32",
|
|
@@ -599,6 +727,12 @@ async function runAgentCli(
|
|
|
599
727
|
});
|
|
600
728
|
const childStartedAt = Date.now();
|
|
601
729
|
let lastActivityAt = childStartedAt;
|
|
730
|
+
const adaptiveDeadline = createAdaptiveDeadline({
|
|
731
|
+
hardTimeoutMs,
|
|
732
|
+
absoluteHardTimeoutMs,
|
|
733
|
+
activityExtensionMs,
|
|
734
|
+
activityWindowMs,
|
|
735
|
+
}, childStartedAt);
|
|
602
736
|
let buffer = "";
|
|
603
737
|
let settled = false;
|
|
604
738
|
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
@@ -722,19 +856,20 @@ async function runAgentCli(
|
|
|
722
856
|
heartbeatTimer.unref?.();
|
|
723
857
|
watchdogTimer = setInterval(() => {
|
|
724
858
|
const now = Date.now();
|
|
725
|
-
|
|
859
|
+
const deadline = adaptiveDeadline.shouldAbort(now, lastActivityAt);
|
|
860
|
+
if (deadline.abort) {
|
|
726
861
|
terminateForTimeout("hard");
|
|
727
862
|
return;
|
|
728
863
|
}
|
|
729
864
|
if (
|
|
730
865
|
lastToolErrorAt > 0 &&
|
|
731
|
-
now - lastToolErrorAt >=
|
|
732
|
-
now - lastActivityAt >=
|
|
866
|
+
now - lastToolErrorAt >= postToolErrorIdleTimeoutMs &&
|
|
867
|
+
now - lastActivityAt >= postToolErrorIdleTimeoutMs
|
|
733
868
|
) {
|
|
734
869
|
terminateForTimeout("post-tool-error");
|
|
735
870
|
return;
|
|
736
871
|
}
|
|
737
|
-
if (now - lastActivityAt >=
|
|
872
|
+
if (now - lastActivityAt >= idleTimeoutMs) {
|
|
738
873
|
terminateForTimeout("idle");
|
|
739
874
|
}
|
|
740
875
|
}, 1000);
|
|
@@ -787,8 +922,13 @@ async function runAgentCli(
|
|
|
787
922
|
lastToolErrorEvidence = "";
|
|
788
923
|
}
|
|
789
924
|
}
|
|
790
|
-
if (event.type === "message_update"
|
|
791
|
-
usage = event.usage;
|
|
925
|
+
if (event.type === "message_update") {
|
|
926
|
+
if (event.usage) usage = event.usage;
|
|
927
|
+
if (event.message?.role === "assistant") {
|
|
928
|
+
const partial = extractAssistantText(event.message);
|
|
929
|
+
if (partial) output = partial;
|
|
930
|
+
seenModel = event.message.model || seenModel;
|
|
931
|
+
}
|
|
792
932
|
}
|
|
793
933
|
if (event.type === "message_end" && event.message) {
|
|
794
934
|
const text = extractAssistantText(event.message);
|
|
@@ -897,9 +1037,22 @@ async function runAgentRpc(
|
|
|
897
1037
|
compactToolOutput?: boolean;
|
|
898
1038
|
toolOutputLimit?: number;
|
|
899
1039
|
verificationTimeoutSec?: number;
|
|
1040
|
+
hardTimeoutMs?: number;
|
|
1041
|
+
absoluteHardTimeoutMs?: number;
|
|
1042
|
+
activityExtensionMs?: number;
|
|
1043
|
+
activityWindowMs?: number;
|
|
1044
|
+
idleTimeoutMs?: number;
|
|
1045
|
+
postToolErrorIdleTimeoutMs?: number;
|
|
1046
|
+
allowLocalEnvWrite?: boolean;
|
|
900
1047
|
} = {},
|
|
901
1048
|
): Promise<RunResult> {
|
|
902
1049
|
const config = AGENTS[agent];
|
|
1050
|
+
const hardTimeoutMs = Number(runtimeOptions.hardTimeoutMs || CHILD_HARD_TIMEOUT_MS);
|
|
1051
|
+
const absoluteHardTimeoutMs = Number(runtimeOptions.absoluteHardTimeoutMs || hardTimeoutMs);
|
|
1052
|
+
const activityExtensionMs = Number(runtimeOptions.activityExtensionMs || 0);
|
|
1053
|
+
const activityWindowMs = Number(runtimeOptions.activityWindowMs || Math.max(5_000, Math.min(activityExtensionMs || hardTimeoutMs, 30_000)));
|
|
1054
|
+
const idleTimeoutMs = Number(runtimeOptions.idleTimeoutMs || CHILD_IDLE_TIMEOUT_MS);
|
|
1055
|
+
const postToolErrorIdleTimeoutMs = Number(runtimeOptions.postToolErrorIdleTimeoutMs || POST_TOOL_ERROR_IDLE_TIMEOUT_MS);
|
|
903
1056
|
const args: string[] = [
|
|
904
1057
|
"--mode", "rpc", "--no-session",
|
|
905
1058
|
"--no-skills", "--no-prompt-templates", "--no-context-files",
|
|
@@ -907,8 +1060,13 @@ async function runAgentRpc(
|
|
|
907
1060
|
];
|
|
908
1061
|
if (model) args.push("--model", model);
|
|
909
1062
|
if (thinkingLevel) args.push("--thinking", thinkingLevel);
|
|
1063
|
+
const codeIntelligenceTools = WRITE_AGENTS.has(agent)
|
|
1064
|
+
? ["ues_code", "ues_code_edit"]
|
|
1065
|
+
: ["ues_code"];
|
|
910
1066
|
const allowedTools = [...new Set([
|
|
911
1067
|
...config.tools,
|
|
1068
|
+
...codeIntelligenceTools,
|
|
1069
|
+
"ues_service",
|
|
912
1070
|
...extraTools,
|
|
913
1071
|
...(runtimeOptions.compactToolOutput ? ["ues_evidence_get"] : []),
|
|
914
1072
|
])];
|
|
@@ -924,6 +1082,7 @@ async function runAgentRpc(
|
|
|
924
1082
|
Boolean(runtimeOptions.compactToolOutput),
|
|
925
1083
|
Number(runtimeOptions.toolOutputLimit || 0),
|
|
926
1084
|
Number(runtimeOptions.verificationTimeoutSec || 0),
|
|
1085
|
+
Boolean(runtimeOptions.allowLocalEnvWrite),
|
|
927
1086
|
]);
|
|
928
1087
|
const taskInput = `Task: ${task}\n`;
|
|
929
1088
|
const startedAt = Date.now();
|
|
@@ -960,17 +1119,22 @@ async function runAgentRpc(
|
|
|
960
1119
|
env: {
|
|
961
1120
|
...process.env,
|
|
962
1121
|
UES_CHILD_PROCESS: "1",
|
|
1122
|
+
UES_CHILD_AGENT: agent,
|
|
963
1123
|
UES_CHILD_TOOL_COMPACTION: runtimeOptions.compactToolOutput ? "1" : "0",
|
|
964
1124
|
UES_CHILD_TOOL_OUTPUT_LIMIT: String(runtimeOptions.toolOutputLimit || 24 * 1024),
|
|
965
1125
|
UES_CHILD_VERIFICATION_TIMEOUT_SEC: String(runtimeOptions.verificationTimeoutSec || 300),
|
|
1126
|
+
UES_CHILD_ALLOW_LOCAL_ENV_WRITE: runtimeOptions.allowLocalEnvWrite ? "1" : "0",
|
|
966
1127
|
},
|
|
967
1128
|
},
|
|
968
1129
|
taskInput,
|
|
969
1130
|
{
|
|
970
1131
|
signal,
|
|
971
|
-
hardTimeoutMs
|
|
972
|
-
|
|
973
|
-
|
|
1132
|
+
hardTimeoutMs,
|
|
1133
|
+
absoluteHardTimeoutMs,
|
|
1134
|
+
activityExtensionMs,
|
|
1135
|
+
activityWindowMs,
|
|
1136
|
+
idleTimeoutMs,
|
|
1137
|
+
postToolErrorIdleTimeoutMs,
|
|
974
1138
|
onEvent: (event: any) => {
|
|
975
1139
|
lastActivityAt = Date.now();
|
|
976
1140
|
if (event.type === "tool_execution_start") {
|
|
@@ -1072,12 +1236,16 @@ async function runAgentRpc(
|
|
|
1072
1236
|
if ((error as any)?.uesRpcPhase === "runtime") {
|
|
1073
1237
|
const timeout = /hard-timeout|idle-timeout/i.test(message);
|
|
1074
1238
|
const toolStall = /post-tool-error-stall/i.test(message);
|
|
1239
|
+
const partialOutput = extractAssistantText((error as any)?.partialMessage);
|
|
1240
|
+
const recoveredOutput = partialOutput
|
|
1241
|
+
? partialOutput + "\n\n[UES transport note: " + message + "]"
|
|
1242
|
+
: message;
|
|
1075
1243
|
return {
|
|
1076
1244
|
agent,
|
|
1077
1245
|
task,
|
|
1078
1246
|
cwd,
|
|
1079
1247
|
exitCode: timeout ? 124 : toolStall ? 125 : 1,
|
|
1080
|
-
output:
|
|
1248
|
+
output: recoveredOutput,
|
|
1081
1249
|
stderr: message,
|
|
1082
1250
|
model,
|
|
1083
1251
|
stopReason: timeout ? "timeout" : toolStall ? "tool-error-stall" : "rpc-runtime-error",
|
|
@@ -1109,6 +1277,13 @@ async function runAgent(
|
|
|
1109
1277
|
compactToolOutput?: boolean;
|
|
1110
1278
|
toolOutputLimit?: number;
|
|
1111
1279
|
verificationTimeoutSec?: number;
|
|
1280
|
+
hardTimeoutMs?: number;
|
|
1281
|
+
absoluteHardTimeoutMs?: number;
|
|
1282
|
+
activityExtensionMs?: number;
|
|
1283
|
+
activityWindowMs?: number;
|
|
1284
|
+
idleTimeoutMs?: number;
|
|
1285
|
+
postToolErrorIdleTimeoutMs?: number;
|
|
1286
|
+
allowLocalEnvWrite?: boolean;
|
|
1112
1287
|
} = {},
|
|
1113
1288
|
): Promise<RunResult> {
|
|
1114
1289
|
if (CHILD_RUNTIME !== "cli") {
|
|
@@ -1251,9 +1426,37 @@ function cachedContextKey(
|
|
|
1251
1426
|
role: string,
|
|
1252
1427
|
budget: number,
|
|
1253
1428
|
fingerprint = "unknown",
|
|
1429
|
+
namespace = cwd,
|
|
1254
1430
|
) {
|
|
1255
1431
|
const auxStamp = runtimeContextAuxStamp(cwd);
|
|
1256
|
-
return [
|
|
1432
|
+
return [path.resolve(namespace), fingerprint, auxStamp, role, String(budget), task].join("\u0000");
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
function deepExplorationContract(role: string) {
|
|
1436
|
+
const common = [
|
|
1437
|
+
"## UES DEEP bounded exploration",
|
|
1438
|
+
"Quality gates remain unchanged. Reduce latency by avoiding duplicate discovery, not by skipping evidence.",
|
|
1439
|
+
"Use the supplied runtime context pack, hierarchy, ranked references and declared files before broad repository exploration.",
|
|
1440
|
+
"Prefer targeted ues_code/direct reads. Do not inventory the repository or repeat unchanged reads unless a named evidence gap requires it.",
|
|
1441
|
+
];
|
|
1442
|
+
if (role === "architect") {
|
|
1443
|
+
common.push(
|
|
1444
|
+
"Stop exploring once every planned task has exact repository-relative scope, dependency ordering, observable acceptance, concrete verification, and risk/rollback coverage.",
|
|
1445
|
+
);
|
|
1446
|
+
} else if (role === "plan-checker") {
|
|
1447
|
+
common.push(
|
|
1448
|
+
"Inspect only disputed or unproven plan properties; once each blocking property is proven or rejected, return PASS/REVISE immediately.",
|
|
1449
|
+
);
|
|
1450
|
+
} else if (role === "executor") {
|
|
1451
|
+
common.push(
|
|
1452
|
+
"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.",
|
|
1453
|
+
);
|
|
1454
|
+
} else if (role === "debugger") {
|
|
1455
|
+
common.push(
|
|
1456
|
+
"Investigate only the concrete failure path and nearest evidence needed to reject/confirm the active hypothesis; avoid general repository surveys.",
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
return common.join("\n");
|
|
1257
1460
|
}
|
|
1258
1461
|
|
|
1259
1462
|
function compactContextPack(pack: any, recentFailure?: string) {
|
|
@@ -1304,20 +1507,23 @@ async function runRoutedAgent(
|
|
|
1304
1507
|
signal?: AbortSignal,
|
|
1305
1508
|
onProgress?: Parameters<typeof runAgent>[6],
|
|
1306
1509
|
traceID?: string,
|
|
1510
|
+
taskPolicyOverride?: any,
|
|
1511
|
+
contextCacheNamespace?: string,
|
|
1307
1512
|
): Promise<RunResult> {
|
|
1308
1513
|
const role = roleForAgent(agent);
|
|
1514
|
+
const traceRoot = requireGitWorkspaceRoot(cwd, "UES specialist");
|
|
1309
1515
|
const browserRequested = browserEvidenceNeeded(task, role);
|
|
1310
1516
|
const browserTools = browserRequested
|
|
1311
1517
|
? selectBrowserToolsForTask(HOST_BROWSER_TOOL_NAMES, task, role)
|
|
1312
1518
|
: [];
|
|
1313
|
-
const taskPolicy = classifyEngineeringTask(task);
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1519
|
+
const taskPolicy = taskPolicyOverride || classifyEngineeringTask(task);
|
|
1520
|
+
const turboFast = turboFastPathDecision(taskPolicy, {
|
|
1521
|
+
role,
|
|
1522
|
+
attempt,
|
|
1523
|
+
browserRequested,
|
|
1524
|
+
visualRequired: visualEvidenceNeeded(task),
|
|
1525
|
+
});
|
|
1526
|
+
const fastBoundedContext = turboFast.eligible;
|
|
1321
1527
|
const budgetDecision = adaptiveContextBudget(taskPolicy, role, attempt, {
|
|
1322
1528
|
disabled: !ADAPTIVE_CONTEXT_ENABLED,
|
|
1323
1529
|
});
|
|
@@ -1332,7 +1538,7 @@ async function runRoutedAgent(
|
|
|
1332
1538
|
const modelPolicy = await readModelPolicy(getUesConfigDir());
|
|
1333
1539
|
const selection = resolveCapabilityModel(role, attempt, task, taskPolicy, modelPolicy);
|
|
1334
1540
|
if (traceID) {
|
|
1335
|
-
await appendTrajectoryEvent(
|
|
1541
|
+
await appendTrajectoryEvent(traceRoot, traceID, "agent.started", {
|
|
1336
1542
|
agent,
|
|
1337
1543
|
role,
|
|
1338
1544
|
attempt,
|
|
@@ -1397,12 +1603,17 @@ async function runRoutedAgent(
|
|
|
1397
1603
|
: "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
1604
|
].join("\n");
|
|
1399
1605
|
} else {
|
|
1606
|
+
const cacheBudgetKey =
|
|
1607
|
+
["architect", "plan-checker"].includes(role)
|
|
1608
|
+
? Number(budgetDecision.baseBudget || budgetDecision.budget)
|
|
1609
|
+
: budgetDecision.budget;
|
|
1400
1610
|
const cacheKey = cachedContextKey(
|
|
1401
1611
|
cwd,
|
|
1402
1612
|
task,
|
|
1403
1613
|
role,
|
|
1404
|
-
|
|
1614
|
+
cacheBudgetKey,
|
|
1405
1615
|
workspaceFingerprint,
|
|
1616
|
+
contextCacheNamespace || cwd,
|
|
1406
1617
|
);
|
|
1407
1618
|
const cacheableContext =
|
|
1408
1619
|
workspaceState.cacheable === true &&
|
|
@@ -1431,8 +1642,9 @@ async function runRoutedAgent(
|
|
|
1431
1642
|
cwd,
|
|
1432
1643
|
task,
|
|
1433
1644
|
role,
|
|
1434
|
-
|
|
1645
|
+
cacheBudgetKey,
|
|
1435
1646
|
workspaceFingerprint,
|
|
1647
|
+
contextCacheNamespace || cwd,
|
|
1436
1648
|
);
|
|
1437
1649
|
if (postBuildKey !== cacheKey) rememberContextPack(postBuildKey, pack);
|
|
1438
1650
|
}
|
|
@@ -1499,6 +1711,10 @@ async function runRoutedAgent(
|
|
|
1499
1711
|
row.stdoutPreview ? ` preview: ${cap(String(row.stdoutPreview).replace(/\s+/g, " "), 900)}` : "",
|
|
1500
1712
|
].filter(Boolean).join("\n")).join("\n")
|
|
1501
1713
|
: "",
|
|
1714
|
+
taskPolicy.executionProfile === "deep" &&
|
|
1715
|
+
["architect", "plan-checker", "executor", "debugger"].includes(role)
|
|
1716
|
+
? "\n" + deepExplorationContract(role)
|
|
1717
|
+
: "",
|
|
1502
1718
|
].filter(Boolean).join("\n");
|
|
1503
1719
|
}
|
|
1504
1720
|
} catch (error) {
|
|
@@ -1525,6 +1741,11 @@ async function runRoutedAgent(
|
|
|
1525
1741
|
].join("\n");
|
|
1526
1742
|
}
|
|
1527
1743
|
|
|
1744
|
+
const planningBudget = planningRuntimeBudget(role, attempt, {
|
|
1745
|
+
executionProfile: taskPolicy.executionProfile,
|
|
1746
|
+
risk: taskPolicy.risk,
|
|
1747
|
+
taskChars: task.length,
|
|
1748
|
+
});
|
|
1528
1749
|
const startedAt = Date.now();
|
|
1529
1750
|
const result = await runAgent(
|
|
1530
1751
|
agent,
|
|
@@ -1546,13 +1767,40 @@ async function runRoutedAgent(
|
|
|
1546
1767
|
? 24 * 1024
|
|
1547
1768
|
: 48 * 1024,
|
|
1548
1769
|
verificationTimeoutSec:
|
|
1549
|
-
|
|
1550
|
-
?
|
|
1551
|
-
: taskPolicy.
|
|
1552
|
-
?
|
|
1553
|
-
: taskPolicy.executionProfile === "
|
|
1554
|
-
?
|
|
1555
|
-
:
|
|
1770
|
+
turboFast.eligible
|
|
1771
|
+
? TURBO_FAST_TIMEOUTS.verificationTimeoutSec
|
|
1772
|
+
: taskPolicy.risk === "high"
|
|
1773
|
+
? 900
|
|
1774
|
+
: taskPolicy.executionProfile === "fast"
|
|
1775
|
+
? 120
|
|
1776
|
+
: taskPolicy.executionProfile === "standard"
|
|
1777
|
+
? 300
|
|
1778
|
+
: 600,
|
|
1779
|
+
hardTimeoutMs:
|
|
1780
|
+
turboFast.eligible
|
|
1781
|
+
? TURBO_FAST_TIMEOUTS.hardTimeoutMs
|
|
1782
|
+
: planningBudget?.hardTimeoutMs || CHILD_HARD_TIMEOUT_MS,
|
|
1783
|
+
absoluteHardTimeoutMs:
|
|
1784
|
+
turboFast.eligible
|
|
1785
|
+
? TURBO_FAST_TIMEOUTS.hardTimeoutMs
|
|
1786
|
+
: planningBudget?.absoluteHardTimeoutMs || CHILD_HARD_TIMEOUT_MS,
|
|
1787
|
+
activityExtensionMs:
|
|
1788
|
+
turboFast.eligible ? 0 : planningBudget?.activityExtensionMs || 0,
|
|
1789
|
+
activityWindowMs:
|
|
1790
|
+
turboFast.eligible ? 0 : planningBudget?.activityWindowMs || 0,
|
|
1791
|
+
idleTimeoutMs:
|
|
1792
|
+
turboFast.eligible
|
|
1793
|
+
? TURBO_FAST_TIMEOUTS.idleTimeoutMs
|
|
1794
|
+
: planningBudget?.idleTimeoutMs || CHILD_IDLE_TIMEOUT_MS,
|
|
1795
|
+
postToolErrorIdleTimeoutMs:
|
|
1796
|
+
turboFast.eligible
|
|
1797
|
+
? TURBO_FAST_TIMEOUTS.postToolErrorIdleTimeoutMs
|
|
1798
|
+
: planningBudget?.idleTimeoutMs
|
|
1799
|
+
? Math.min(POST_TOOL_ERROR_IDLE_TIMEOUT_MS, planningBudget.idleTimeoutMs)
|
|
1800
|
+
: POST_TOOL_ERROR_IDLE_TIMEOUT_MS,
|
|
1801
|
+
allowLocalEnvWrite:
|
|
1802
|
+
taskPolicy.localEnvWriteExplicitlyAllowed === true ||
|
|
1803
|
+
taskExplicitlyAllowsLocalEnvWrite(task),
|
|
1556
1804
|
},
|
|
1557
1805
|
);
|
|
1558
1806
|
const enrichedResult: RunResult = {
|
|
@@ -1581,13 +1829,17 @@ async function runRoutedAgent(
|
|
|
1581
1829
|
!["high", "critical"].includes(String(taskPolicy.risk || "").toLowerCase()),
|
|
1582
1830
|
runtimeContextBudget: budgetDecision.budget,
|
|
1583
1831
|
baseContextBudget: budgetDecision.baseBudget,
|
|
1832
|
+
turboFastPath: turboFast.eligible,
|
|
1833
|
+
turboFastStrategy: turboFast.strategy,
|
|
1834
|
+
turboFastTimeouts: turboFast.eligible ? TURBO_FAST_TIMEOUTS : null,
|
|
1835
|
+
planningRuntimeBudget: planningBudget,
|
|
1584
1836
|
},
|
|
1585
1837
|
verdict: verdictFromOutput(result.output),
|
|
1586
1838
|
report: parseStructuredReport(result.output),
|
|
1587
1839
|
durationMs: Date.now() - startedAt,
|
|
1588
1840
|
};
|
|
1589
1841
|
if (traceID) {
|
|
1590
|
-
await appendTrajectoryEvent(
|
|
1842
|
+
await appendTrajectoryEvent(traceRoot, traceID, "agent.completed", {
|
|
1591
1843
|
agent,
|
|
1592
1844
|
role,
|
|
1593
1845
|
attempt,
|
|
@@ -1683,19 +1935,64 @@ function extractMarkedJson(output: string, marker: string) {
|
|
|
1683
1935
|
return null;
|
|
1684
1936
|
}
|
|
1685
1937
|
|
|
1686
|
-
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1689
|
-
|
|
1938
|
+
function sandboxSourceIntentBatches(values: string[]) {
|
|
1939
|
+
const batches: string[][] = [];
|
|
1940
|
+
let batch: string[] = [];
|
|
1941
|
+
let chars = 0;
|
|
1942
|
+
for (const value of sourceFacingPaths(values)) {
|
|
1943
|
+
const cost = value.length + 3;
|
|
1944
|
+
if (batch.length && (batch.length >= 64 || chars + cost > 12_000)) {
|
|
1945
|
+
batches.push(batch);
|
|
1946
|
+
batch = [];
|
|
1947
|
+
chars = 0;
|
|
1948
|
+
}
|
|
1949
|
+
batch.push(value);
|
|
1950
|
+
chars += cost;
|
|
1690
1951
|
}
|
|
1691
|
-
|
|
1952
|
+
if (batch.length) batches.push(batch);
|
|
1953
|
+
return batches;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
async function sandboxIntentToAddSourceFiles(dir: string, signal?: AbortSignal) {
|
|
1957
|
+
const pathspecs = sourceGitPathspecs();
|
|
1958
|
+
const untracked = await runProcess(
|
|
1959
|
+
"git",
|
|
1960
|
+
["ls-files", "--others", "--exclude-standard", "-z", "--", ...pathspecs],
|
|
1961
|
+
dir,
|
|
1962
|
+
signal,
|
|
1963
|
+
);
|
|
1964
|
+
if (untracked.exitCode !== 0) {
|
|
1965
|
+
throw new Error(untracked.stderr || untracked.stdout || "git ls-files for intent-to-add failed");
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
const candidates = sourceFacingPaths(
|
|
1969
|
+
untracked.stdout
|
|
1970
|
+
.split("\0")
|
|
1971
|
+
.map((value) => value.replaceAll("\\", "/"))
|
|
1972
|
+
.filter(Boolean),
|
|
1973
|
+
);
|
|
1974
|
+
for (const batch of sandboxSourceIntentBatches(candidates)) {
|
|
1975
|
+
const intent = await runProcess("git", ["add", "-N", "--", ...batch], dir, signal);
|
|
1976
|
+
if (intent.exitCode !== 0) {
|
|
1977
|
+
throw new Error(intent.stderr || intent.stdout || "git add -N failed");
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return candidates;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
async function sandboxChangedFiles(dir: string, base: string, signal?: AbortSignal) {
|
|
1984
|
+
const pathspecs = sourceGitPathspecs();
|
|
1985
|
+
await sandboxIntentToAddSourceFiles(dir, signal);
|
|
1986
|
+
const diff = await runProcess("git", ["diff", "--name-only", base, "--", ...pathspecs], dir, signal);
|
|
1692
1987
|
if (diff.exitCode !== 0) {
|
|
1693
1988
|
throw new Error(diff.stderr || diff.stdout || "git diff --name-only failed");
|
|
1694
1989
|
}
|
|
1695
|
-
return
|
|
1696
|
-
.
|
|
1697
|
-
|
|
1698
|
-
|
|
1990
|
+
return sourceFacingPaths(
|
|
1991
|
+
diff.stdout
|
|
1992
|
+
.split(/\r?\n/)
|
|
1993
|
+
.map((value) => value.trim().replaceAll("\\", "/"))
|
|
1994
|
+
.filter(Boolean),
|
|
1995
|
+
);
|
|
1699
1996
|
}
|
|
1700
1997
|
|
|
1701
1998
|
async function cleanupSandboxes(
|
|
@@ -1704,19 +2001,39 @@ async function cleanupSandboxes(
|
|
|
1704
2001
|
) {
|
|
1705
2002
|
for (const item of prepared) {
|
|
1706
2003
|
if (!item.sandbox?.dir) continue;
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
2004
|
+
const resolved = path.resolve(item.sandbox.dir);
|
|
2005
|
+
try {
|
|
2006
|
+
await removeTaskSandbox(root, resolved, {
|
|
2007
|
+
force: true,
|
|
2008
|
+
deleteBranch: true,
|
|
2009
|
+
});
|
|
2010
|
+
ACTIVE_TASK_SANDBOXES.delete(resolved);
|
|
2011
|
+
} catch {}
|
|
1711
2012
|
}
|
|
1712
2013
|
}
|
|
1713
2014
|
|
|
2015
|
+
async function cleanupTraceSandboxes(root: string, traceID: string) {
|
|
2016
|
+
const targets = [...ACTIVE_TASK_SANDBOXES.entries()]
|
|
2017
|
+
.filter(([, ownerTrace]) => ownerTrace === traceID)
|
|
2018
|
+
.map(([dir]) => dir);
|
|
2019
|
+
let removed = 0;
|
|
2020
|
+
for (const dir of targets) {
|
|
2021
|
+
try {
|
|
2022
|
+
await removeTaskSandbox(root, dir, { force: true, deleteBranch: true });
|
|
2023
|
+
ACTIVE_TASK_SANDBOXES.delete(dir);
|
|
2024
|
+
removed += 1;
|
|
2025
|
+
} catch {}
|
|
2026
|
+
}
|
|
2027
|
+
return removed;
|
|
2028
|
+
}
|
|
2029
|
+
|
|
1714
2030
|
async function executeStructuredPlan(input: {
|
|
1715
2031
|
plan: any;
|
|
1716
2032
|
root: string;
|
|
1717
2033
|
inheritedModel?: string;
|
|
1718
2034
|
inheritedThinking?: string;
|
|
1719
2035
|
maxAttempts: number;
|
|
2036
|
+
rootPolicy?: any;
|
|
1720
2037
|
durableSlug?: string;
|
|
1721
2038
|
traceID?: string;
|
|
1722
2039
|
signal?: AbortSignal;
|
|
@@ -1789,6 +2106,7 @@ async function executeStructuredPlan(input: {
|
|
|
1789
2106
|
for (let waveIndex = 0; waveIndex < safe.waves.length; waveIndex++) {
|
|
1790
2107
|
const ids = safe.waves[waveIndex];
|
|
1791
2108
|
let lastWaveFailure = "";
|
|
2109
|
+
const failureByTask = new Map<string, string>();
|
|
1792
2110
|
|
|
1793
2111
|
for (let attempt = 1; attempt <= input.maxAttempts; attempt++) {
|
|
1794
2112
|
const prepared: Array<{
|
|
@@ -1807,12 +2125,13 @@ async function executeStructuredPlan(input: {
|
|
|
1807
2125
|
let cwd = input.root;
|
|
1808
2126
|
let sandbox: any = undefined;
|
|
1809
2127
|
|
|
1810
|
-
if (gitCapable) {
|
|
2128
|
+
if (gitCapable && writeFiles.length > 0) {
|
|
1811
2129
|
const slug = "runtime-" + randomUUID().slice(0, 8) + "-w" + waveIndex + "-a" + attempt;
|
|
1812
2130
|
sandbox = await createTaskSandbox(input.root, slug, id, {
|
|
1813
2131
|
inheritDirtyRoot: true,
|
|
1814
2132
|
});
|
|
1815
2133
|
cwd = sandbox.dir;
|
|
2134
|
+
ACTIVE_TASK_SANDBOXES.set(path.resolve(sandbox.dir), String(input.traceID || "structured"));
|
|
1816
2135
|
}
|
|
1817
2136
|
|
|
1818
2137
|
prepared.push({ task, cwd, sandbox, writeFiles });
|
|
@@ -1830,22 +2149,37 @@ async function executeStructuredPlan(input: {
|
|
|
1830
2149
|
}
|
|
1831
2150
|
|
|
1832
2151
|
let completed = 0;
|
|
2152
|
+
const writerCount = prepared.filter((item) => item.writeFiles.length > 0).length;
|
|
2153
|
+
const waveConcurrency = !gitCapable
|
|
2154
|
+
? 1
|
|
2155
|
+
: writerCount > 0
|
|
2156
|
+
? Math.min(MAX_CONCURRENCY, MAX_WRITER_CONCURRENCY)
|
|
2157
|
+
: MAX_CONCURRENCY;
|
|
1833
2158
|
const waveResults = await mapLimit(
|
|
1834
2159
|
prepared,
|
|
1835
|
-
|
|
2160
|
+
waveConcurrency,
|
|
1836
2161
|
async (item) => {
|
|
1837
2162
|
const taskText = [
|
|
1838
2163
|
"Execute exactly this structured plan task.",
|
|
1839
2164
|
"Do not broaden file scope. If the declared write file list is empty, do not edit files.",
|
|
2165
|
+
"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
2166
|
"",
|
|
1841
2167
|
JSON.stringify(item.task, null, 2),
|
|
1842
2168
|
"",
|
|
1843
|
-
"
|
|
1844
|
-
String(input.plan.goal || ""),
|
|
1845
|
-
|
|
1846
|
-
? "\nFresh failure evidence from the previous wave attempt:\n" + cap(lastWaveFailure, 7000)
|
|
1847
|
-
: "",
|
|
2169
|
+
"Parent goal (context only; never broaden this leaf task):",
|
|
2170
|
+
cap(String(input.plan.goal || ""), 700),
|
|
2171
|
+
input.rootPolicy?.executionContractPrompt || "",
|
|
1848
2172
|
].filter(Boolean).join("\n");
|
|
2173
|
+
const leafPolicy = leafTaskPolicy(item.task, input.rootPolicy || {});
|
|
2174
|
+
const taskFailure = failureByTask.get(String(item.task.id)) || "";
|
|
2175
|
+
const leafVisualRequired = visualEvidenceNeeded(taskText);
|
|
2176
|
+
const leafFastDecision = turboFastPathDecision(leafPolicy, {
|
|
2177
|
+
role: "executor",
|
|
2178
|
+
attempt,
|
|
2179
|
+
browserRequested: browserEvidenceNeeded(taskText, "executor"),
|
|
2180
|
+
visualRequired: leafVisualRequired,
|
|
2181
|
+
});
|
|
2182
|
+
const leafAttemptStartedAt = Date.now();
|
|
1849
2183
|
|
|
1850
2184
|
const plannedExecution: any = dynamicTaskByID.get(item.task.id);
|
|
1851
2185
|
const deterministicReadOnly =
|
|
@@ -1978,8 +2312,8 @@ async function executeStructuredPlan(input: {
|
|
|
1978
2312
|
"",
|
|
1979
2313
|
"Overall goal:",
|
|
1980
2314
|
String(input.plan.goal || ""),
|
|
1981
|
-
|
|
1982
|
-
? "\nFresh failure
|
|
2315
|
+
taskFailure
|
|
2316
|
+
? "\nFresh task-local failure delta from the previous attempt:\n" + cap(taskFailure, 4200)
|
|
1983
2317
|
: "",
|
|
1984
2318
|
].filter(Boolean).join("\n"),
|
|
1985
2319
|
item.cwd,
|
|
@@ -1990,6 +2324,8 @@ async function executeStructuredPlan(input: {
|
|
|
1990
2324
|
input.signal,
|
|
1991
2325
|
undefined,
|
|
1992
2326
|
input.traceID,
|
|
2327
|
+
leafPolicy,
|
|
2328
|
+
input.root,
|
|
1993
2329
|
);
|
|
1994
2330
|
results.push({
|
|
1995
2331
|
wave: waveIndex,
|
|
@@ -2027,8 +2363,8 @@ async function executeStructuredPlan(input: {
|
|
|
2027
2363
|
};
|
|
2028
2364
|
}
|
|
2029
2365
|
|
|
2030
|
-
let focusedFailure =
|
|
2031
|
-
if (attempt > 1 && runtimeFailureNeedsDiagnosis(
|
|
2366
|
+
let focusedFailure = taskFailure;
|
|
2367
|
+
if (attempt > 1 && runtimeFailureNeedsDiagnosis(taskFailure)) {
|
|
2032
2368
|
const diagnosis = await runRoutedAgent(
|
|
2033
2369
|
"ues-debugger",
|
|
2034
2370
|
[
|
|
@@ -2038,16 +2374,18 @@ async function executeStructuredPlan(input: {
|
|
|
2038
2374
|
JSON.stringify(item.task, null, 2),
|
|
2039
2375
|
"",
|
|
2040
2376
|
"Previous runtime failure:",
|
|
2041
|
-
cap(
|
|
2377
|
+
cap(taskFailure, 4200),
|
|
2042
2378
|
].join("\n"),
|
|
2043
2379
|
item.cwd,
|
|
2044
2380
|
input.inheritedModel,
|
|
2045
2381
|
input.inheritedThinking,
|
|
2046
2382
|
attempt,
|
|
2047
|
-
|
|
2383
|
+
taskFailure,
|
|
2048
2384
|
input.signal,
|
|
2049
2385
|
undefined,
|
|
2050
2386
|
input.traceID,
|
|
2387
|
+
leafPolicy,
|
|
2388
|
+
input.root,
|
|
2051
2389
|
);
|
|
2052
2390
|
results.push({
|
|
2053
2391
|
wave: waveIndex,
|
|
@@ -2070,16 +2408,15 @@ async function executeStructuredPlan(input: {
|
|
|
2070
2408
|
}
|
|
2071
2409
|
}
|
|
2072
2410
|
|
|
2411
|
+
const retryDelta = failureDelta(focusedFailure || taskFailure);
|
|
2073
2412
|
const implementation = await runRoutedAgent(
|
|
2074
2413
|
"ues-executor",
|
|
2075
|
-
taskText
|
|
2076
|
-
? "\n\nFocused diagnosis before retry:\n" + cap(focusedFailure, 7000)
|
|
2077
|
-
: ""),
|
|
2414
|
+
taskText,
|
|
2078
2415
|
item.cwd,
|
|
2079
2416
|
input.inheritedModel,
|
|
2080
2417
|
input.inheritedThinking,
|
|
2081
2418
|
attempt,
|
|
2082
|
-
|
|
2419
|
+
retryDelta || undefined,
|
|
2083
2420
|
input.signal,
|
|
2084
2421
|
(progress) => {
|
|
2085
2422
|
input.onUpdate?.({
|
|
@@ -2093,8 +2430,10 @@ async function executeStructuredPlan(input: {
|
|
|
2093
2430
|
});
|
|
2094
2431
|
},
|
|
2095
2432
|
input.traceID,
|
|
2433
|
+
leafPolicy,
|
|
2434
|
+
input.root,
|
|
2096
2435
|
);
|
|
2097
|
-
results.push({ wave: waveIndex, attempt, task: item.task.id, phase: "execute", ...implementation });
|
|
2436
|
+
results.push({ wave: waveIndex, attempt, task: item.task.id, phase: "execute", leafPolicy, ...implementation });
|
|
2098
2437
|
|
|
2099
2438
|
if (implementation.exitCode !== 0 || implementation.stopReason === "error") {
|
|
2100
2439
|
if (!isAbortedRun(implementation)) {
|
|
@@ -2114,7 +2453,75 @@ async function executeStructuredPlan(input: {
|
|
|
2114
2453
|
};
|
|
2115
2454
|
}
|
|
2116
2455
|
|
|
2117
|
-
|
|
2456
|
+
let verification: RunResult;
|
|
2457
|
+
let leafFastGate: any = null;
|
|
2458
|
+
if (leafFastDecision.eligible) {
|
|
2459
|
+
const snapshot = runtimeWorkspaceSnapshot(item.cwd);
|
|
2460
|
+
const receipts = await listReusableVerification(item.cwd, {
|
|
2461
|
+
limit: 12,
|
|
2462
|
+
maxAgeMs: 10 * 60_000,
|
|
2463
|
+
previewBytes: 2200,
|
|
2464
|
+
...(snapshot.cacheable === true && snapshot.fingerprint
|
|
2465
|
+
? { workspaceFingerprint: snapshot.fingerprint }
|
|
2466
|
+
: {}),
|
|
2467
|
+
}).catch(() => ({ results: [] }));
|
|
2468
|
+
leafFastGate = evaluateFastVerificationGate({
|
|
2469
|
+
policy: leafPolicy,
|
|
2470
|
+
implementation,
|
|
2471
|
+
receipts: receipts?.results || [],
|
|
2472
|
+
attemptStartedAtMs: leafAttemptStartedAt,
|
|
2473
|
+
visualRequired: leafVisualRequired,
|
|
2474
|
+
});
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
if (leafFastGate?.passed === true) {
|
|
2478
|
+
verification = {
|
|
2479
|
+
agent: "ues-deterministic-verifier",
|
|
2480
|
+
task: taskText,
|
|
2481
|
+
cwd: item.cwd,
|
|
2482
|
+
exitCode: 0,
|
|
2483
|
+
output: [
|
|
2484
|
+
"Per-leaf Turbo verification reused fresh behavioral evidence captured after this leaf implementation.",
|
|
2485
|
+
...leafFastGate.behavioralReceipts.map((entry: any) => "- " + entry.command),
|
|
2486
|
+
"",
|
|
2487
|
+
"UES_VERDICT: PASS",
|
|
2488
|
+
].join("\n"),
|
|
2489
|
+
stderr: "",
|
|
2490
|
+
verdict: "PASS",
|
|
2491
|
+
durationMs: 0,
|
|
2492
|
+
toolCalls: 0,
|
|
2493
|
+
toolNames: [],
|
|
2494
|
+
report: {
|
|
2495
|
+
schemaVersion: 1,
|
|
2496
|
+
valid: true,
|
|
2497
|
+
verdict: "PASS",
|
|
2498
|
+
sections: {
|
|
2499
|
+
"checks-run": leafFastGate.behavioralReceipts.map((entry: any) => entry.command).join("\n"),
|
|
2500
|
+
"acceptance-criteria-proven": "Fresh behavioral receipt(s) exist at the post-implementation workspace fingerprint.",
|
|
2501
|
+
"completion-evidence": "Deterministic per-leaf PASS after this implementation attempt.",
|
|
2502
|
+
},
|
|
2503
|
+
},
|
|
2504
|
+
optimizations: {
|
|
2505
|
+
perLeafTurbo: true,
|
|
2506
|
+
behavioralReceiptCount: leafFastGate.behavioralReceipts.length,
|
|
2507
|
+
},
|
|
2508
|
+
};
|
|
2509
|
+
input.onUpdate?.({
|
|
2510
|
+
content: [{
|
|
2511
|
+
type: "text",
|
|
2512
|
+
text: `UES Per-Leaf Turbo: ${item.task.id} reused ${leafFastGate.behavioralReceipts.length} fresh behavioral receipt(s); skipped verifier model turn`,
|
|
2513
|
+
}],
|
|
2514
|
+
details: {
|
|
2515
|
+
wave: waveIndex,
|
|
2516
|
+
attempt,
|
|
2517
|
+
task: item.task.id,
|
|
2518
|
+
phase: "leaf-turbo-verified",
|
|
2519
|
+
leafPolicy,
|
|
2520
|
+
leafFastGate,
|
|
2521
|
+
},
|
|
2522
|
+
});
|
|
2523
|
+
} else {
|
|
2524
|
+
verification = await runRoutedAgent(
|
|
2118
2525
|
"ues-verifier",
|
|
2119
2526
|
[
|
|
2120
2527
|
"Verify exactly this structured plan task in the current isolated worktree.",
|
|
@@ -2141,8 +2548,11 @@ async function executeStructuredPlan(input: {
|
|
|
2141
2548
|
});
|
|
2142
2549
|
},
|
|
2143
2550
|
input.traceID,
|
|
2551
|
+
leafPolicy,
|
|
2552
|
+
input.root,
|
|
2144
2553
|
);
|
|
2145
|
-
|
|
2554
|
+
}
|
|
2555
|
+
results.push({ wave: waveIndex, attempt, task: item.task.id, phase: "verify", leafPolicy, leafFastGate, ...verification });
|
|
2146
2556
|
const passed = verification.exitCode === 0 && verification.verdict === "PASS";
|
|
2147
2557
|
if (!isAbortedRun(verification)) {
|
|
2148
2558
|
await recordRuntimeOutcome(implementation, taskText, passed, attempt - 1);
|
|
@@ -2188,8 +2598,12 @@ async function executeStructuredPlan(input: {
|
|
|
2188
2598
|
|
|
2189
2599
|
const failed = waveResults.filter((item) => !item.passed);
|
|
2190
2600
|
if (failed.length) {
|
|
2601
|
+
for (const row of failed) {
|
|
2602
|
+
const rawFailure = row.verification?.output || row.implementation?.output || "unknown failure";
|
|
2603
|
+
failureByTask.set(String(row.item?.task?.id || "unknown"), failureDelta(rawFailure));
|
|
2604
|
+
}
|
|
2191
2605
|
lastWaveFailure = failed
|
|
2192
|
-
.map((item) => item.
|
|
2606
|
+
.map((item) => failureByTask.get(String(item.item?.task?.id || "unknown")) || "unknown failure")
|
|
2193
2607
|
.join("\n\n---\n\n");
|
|
2194
2608
|
await failDurablePrepared(prepared, lastWaveFailure);
|
|
2195
2609
|
await cleanupSandboxes(input.root, prepared);
|
|
@@ -2207,9 +2621,9 @@ async function executeStructuredPlan(input: {
|
|
|
2207
2621
|
};
|
|
2208
2622
|
}
|
|
2209
2623
|
|
|
2210
|
-
if (!gitCapable) {
|
|
2211
|
-
//
|
|
2212
|
-
//
|
|
2624
|
+
if (!gitCapable || prepared.every((item) => !item.sandbox?.dir)) {
|
|
2625
|
+
// Read-only waves do not need a duplicate Git worktree. Their fresh
|
|
2626
|
+
// verifier evidence is enough as long as the workspace stays unchanged.
|
|
2213
2627
|
await completeDurableWave(waveResults);
|
|
2214
2628
|
break;
|
|
2215
2629
|
}
|
|
@@ -2217,13 +2631,21 @@ async function executeStructuredPlan(input: {
|
|
|
2217
2631
|
const actualByTask = new Map<string, string[]>();
|
|
2218
2632
|
let scopeFailure = "";
|
|
2219
2633
|
for (const item of prepared) {
|
|
2634
|
+
if (!item.sandbox?.dir) {
|
|
2635
|
+
actualByTask.set(item.task.id, []);
|
|
2636
|
+
continue;
|
|
2637
|
+
}
|
|
2220
2638
|
const changed = await sandboxChangedFiles(item.sandbox.dir, item.sandbox.integrationBase, input.signal);
|
|
2221
2639
|
actualByTask.set(item.task.id, changed);
|
|
2222
2640
|
const allowed = new Set(item.writeFiles);
|
|
2223
2641
|
const unexpected = changed.filter((file) => !allowed.has(file));
|
|
2224
2642
|
if (unexpected.length) {
|
|
2225
|
-
|
|
2226
|
-
`${item.task.id}: changed files outside declared write scope: ${unexpected.join(", ")}
|
|
2643
|
+
const scopeMessage =
|
|
2644
|
+
`${item.task.id}: changed real source files outside declared write scope: ${unexpected.join(", ")}. ` +
|
|
2645
|
+
"On retry, revert or avoid these files and satisfy the task strictly within its declared write scope. " +
|
|
2646
|
+
"Do not silently broaden scope.";
|
|
2647
|
+
failureByTask.set(String(item.task.id), failureDelta(scopeMessage, { maxChars: 2200 }));
|
|
2648
|
+
scopeFailure += scopeMessage + "\n";
|
|
2227
2649
|
}
|
|
2228
2650
|
}
|
|
2229
2651
|
|
|
@@ -2232,7 +2654,12 @@ async function executeStructuredPlan(input: {
|
|
|
2232
2654
|
for (const file of changed) {
|
|
2233
2655
|
const previous = changedOwners.get(file);
|
|
2234
2656
|
if (previous && previous !== taskID) {
|
|
2235
|
-
|
|
2657
|
+
const conflictMessage = `wave conflict: ${previous} and ${taskID} both changed real source file ${file}`;
|
|
2658
|
+
const previousFailure = failureByTask.get(previous) || "";
|
|
2659
|
+
const currentFailure = failureByTask.get(taskID) || "";
|
|
2660
|
+
failureByTask.set(previous, failureDelta([previousFailure, conflictMessage].filter(Boolean).join("\n"), { maxChars: 2200 }));
|
|
2661
|
+
failureByTask.set(taskID, failureDelta([currentFailure, conflictMessage].filter(Boolean).join("\n"), { maxChars: 2200 }));
|
|
2662
|
+
scopeFailure += conflictMessage + "\n";
|
|
2236
2663
|
} else {
|
|
2237
2664
|
changedOwners.set(file, taskID);
|
|
2238
2665
|
}
|
|
@@ -2260,6 +2687,7 @@ async function executeStructuredPlan(input: {
|
|
|
2260
2687
|
const integrated: Array<{ item: any; receipt: any }> = [];
|
|
2261
2688
|
try {
|
|
2262
2689
|
for (const item of prepared) {
|
|
2690
|
+
if (!item.sandbox?.dir) continue;
|
|
2263
2691
|
const receipt = await integrateTaskSandbox(input.root, item.sandbox.dir, { keep: true });
|
|
2264
2692
|
integrated.push({ item, receipt });
|
|
2265
2693
|
integrations.push({ wave: waveIndex, task: item.task.id, ...receipt });
|
|
@@ -2379,7 +2807,52 @@ export default function (pi: ExtensionAPI) {
|
|
|
2379
2807
|
// controller inside specialist workers.
|
|
2380
2808
|
if (process.env.UES_CHILD_PROCESS === "1") return;
|
|
2381
2809
|
|
|
2810
|
+
let directControllerAbort: AbortController | null = null;
|
|
2811
|
+
let promptUesActive = false;
|
|
2812
|
+
let directControllerRunner: ((task: string, ctx: any, admission: "command" | "automatic", admissionDecision?: any) => Promise<void>) | null = null;
|
|
2813
|
+
const AUTO_ADMISSION_ENABLED = !["0", "false", "off"].includes(
|
|
2814
|
+
String(process.env.UES_AUTO_ADMIT || "1").trim().toLowerCase(),
|
|
2815
|
+
);
|
|
2816
|
+
const UES_PARENT_TOOL_NAMES = new Set(["ues_cli", "ues_execute", "ues_service", "ues_dispatch"]);
|
|
2817
|
+
let normalActiveTools: string[] | null = null;
|
|
2818
|
+
|
|
2819
|
+
const currentNonUesTools = () =>
|
|
2820
|
+
pi.getActiveTools().filter((name) => !UES_PARENT_TOOL_NAMES.has(String(name)));
|
|
2821
|
+
|
|
2822
|
+
const deactivateParentUesTools = () => {
|
|
2823
|
+
const normal = currentNonUesTools();
|
|
2824
|
+
normalActiveTools = normal;
|
|
2825
|
+
try { pi.setActiveTools(normal); } catch {}
|
|
2826
|
+
};
|
|
2827
|
+
|
|
2828
|
+
const activateParentUesTools = () => {
|
|
2829
|
+
const normal = normalActiveTools || currentNonUesTools();
|
|
2830
|
+
const registered = new Set(
|
|
2831
|
+
(typeof (pi as any).getAllTools === "function" ? (pi as any).getAllTools() : [])
|
|
2832
|
+
.map((tool: any) => String(tool?.name || "")),
|
|
2833
|
+
);
|
|
2834
|
+
const uesTools = [...UES_PARENT_TOOL_NAMES].filter((name) => registered.has(name));
|
|
2835
|
+
try { pi.setActiveTools([...new Set([...normal, ...uesTools])]); } catch {}
|
|
2836
|
+
};
|
|
2837
|
+
|
|
2838
|
+
const uesModeActive = () =>
|
|
2839
|
+
promptUesActive || Boolean(directControllerAbort && !directControllerAbort.signal.aborted);
|
|
2840
|
+
|
|
2841
|
+
const syncSessionIdentity = (name: string, ctx: any) => {
|
|
2842
|
+
if (!name) return;
|
|
2843
|
+
try { pi.setSessionName(name); } catch {}
|
|
2844
|
+
try { ctx?.ui?.setTitle?.(name); } catch {}
|
|
2845
|
+
};
|
|
2846
|
+
|
|
2847
|
+
pi.on("session_start", async () => {
|
|
2848
|
+
deactivateParentUesTools();
|
|
2849
|
+
});
|
|
2850
|
+
|
|
2382
2851
|
pi.on("session_shutdown", async () => {
|
|
2852
|
+
directControllerAbort?.abort();
|
|
2853
|
+
directControllerAbort = null;
|
|
2854
|
+
promptUesActive = false;
|
|
2855
|
+
normalActiveTools = null;
|
|
2383
2856
|
CONTEXT_PACK_CACHE.clear();
|
|
2384
2857
|
clearSkillCompilerCache();
|
|
2385
2858
|
clearAffectedTestCache();
|
|
@@ -2387,29 +2860,44 @@ export default function (pi: ExtensionAPI) {
|
|
|
2387
2860
|
clearSemanticIndexRuntimeCache();
|
|
2388
2861
|
MCP_HEALTH.clear();
|
|
2389
2862
|
abortActiveCliChildren();
|
|
2863
|
+
await stopAllServices().catch(() => []);
|
|
2390
2864
|
await RPC_POOL.stopAll().catch(() => {});
|
|
2865
|
+
for (const dir of [...ACTIVE_TASK_SANDBOXES.keys()]) {
|
|
2866
|
+
const ownerRoot = await taskSandboxOwnerRoot(dir).catch(() => null);
|
|
2867
|
+
if (!ownerRoot) continue;
|
|
2868
|
+
await removeTaskSandbox(ownerRoot, dir, { force: true, deleteBranch: true }).catch(() => {});
|
|
2869
|
+
ACTIVE_TASK_SANDBOXES.delete(dir);
|
|
2870
|
+
}
|
|
2391
2871
|
});
|
|
2392
2872
|
|
|
2393
2873
|
pi.on("input", async (event, ctx) => {
|
|
2394
2874
|
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
|
-
}
|
|
2875
|
+
if (event.source !== "interactive") return { action: "continue" };
|
|
2401
2876
|
|
|
2402
2877
|
const text = String(event.text || "").trim();
|
|
2403
2878
|
if (!text) return { action: "continue" };
|
|
2404
2879
|
|
|
2880
|
+
const sessionName = sessionNameFromUesInput(text, ctx.cwd || "");
|
|
2881
|
+
if (sessionName) syncSessionIdentity(sessionName, ctx);
|
|
2882
|
+
if (/^\/ues-(?:resume|fix|feature|debug|review|audit|plan|research|critique|verify)(?:\s|$)/i.test(text)) {
|
|
2883
|
+
promptUesActive = true;
|
|
2884
|
+
activateParentUesTools();
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2405
2887
|
if (/^(?:stop|cancel|abort|dừng|dung|hủy|huy)(?:\s|$)/i.test(text)) {
|
|
2888
|
+
if (!uesModeActive()) return { action: "continue" };
|
|
2889
|
+
let directAborted = 0;
|
|
2890
|
+
if (directControllerAbort && !directControllerAbort.signal.aborted) {
|
|
2891
|
+
directControllerAbort.abort();
|
|
2892
|
+
directAborted = 1;
|
|
2893
|
+
}
|
|
2406
2894
|
const result = await RPC_POOL.abortActive();
|
|
2407
2895
|
const cliAborted = abortActiveCliChildren();
|
|
2408
|
-
const total = result.aborted + cliAborted;
|
|
2896
|
+
const total = directAborted + result.aborted + cliAborted;
|
|
2409
2897
|
if (total > 0) {
|
|
2410
2898
|
try {
|
|
2411
2899
|
ctx.ui.notify(
|
|
2412
|
-
`UES: aborted ${total} active child worker(s)` +
|
|
2900
|
+
`UES: aborted ${total} active controller/child worker(s)` +
|
|
2413
2901
|
(cliAborted ? ` (${cliAborted} CLI fallback)` : ""),
|
|
2414
2902
|
"warning",
|
|
2415
2903
|
);
|
|
@@ -2419,6 +2907,65 @@ export default function (pi: ExtensionAPI) {
|
|
|
2419
2907
|
return { action: "continue" };
|
|
2420
2908
|
}
|
|
2421
2909
|
|
|
2910
|
+
if (!["steer", "followUp"].includes(String(event.streamingBehavior || ""))) {
|
|
2911
|
+
if (directControllerAbort && !directControllerAbort.signal.aborted) {
|
|
2912
|
+
const continuation = automaticUesContinuation(text, { activeRun: true });
|
|
2913
|
+
if (continuation.forward) {
|
|
2914
|
+
const forwarded = await RPC_POOL.steerActive(text).catch(() => ({
|
|
2915
|
+
accepted: false,
|
|
2916
|
+
reason: "steer-failed",
|
|
2917
|
+
active: 0,
|
|
2918
|
+
}));
|
|
2919
|
+
if (forwarded.accepted) {
|
|
2920
|
+
try { ctx.ui.notify("UES: natural continuation forwarded to the active child", "info"); } catch {}
|
|
2921
|
+
return { action: "handled" };
|
|
2922
|
+
}
|
|
2923
|
+
try {
|
|
2924
|
+
ctx.ui.notify(
|
|
2925
|
+
"UES is already running; continuation could not be targeted safely, so the prompt was not swallowed.",
|
|
2926
|
+
"warning",
|
|
2927
|
+
);
|
|
2928
|
+
} catch {}
|
|
2929
|
+
}
|
|
2930
|
+
return { action: "continue" };
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
if (
|
|
2934
|
+
AUTO_ADMISSION_ENABLED &&
|
|
2935
|
+
directControllerRunner &&
|
|
2936
|
+
!(Array.isArray((event as any).images) && (event as any).images.length > 0)
|
|
2937
|
+
) {
|
|
2938
|
+
const preliminaryAdmission = automaticUesAdmission(text);
|
|
2939
|
+
const workspaceRoot = preliminaryAdmission.admit === true
|
|
2940
|
+
? resolveGitWorkspaceRoot(ctx.cwd || "")
|
|
2941
|
+
: null;
|
|
2942
|
+
const admission = workspaceRoot
|
|
2943
|
+
? automaticUesAdmission(text, { inGitWorkspace: true })
|
|
2944
|
+
: preliminaryAdmission;
|
|
2945
|
+
if (workspaceRoot && admission.admit === true) {
|
|
2946
|
+
try {
|
|
2947
|
+
ctx.ui.notify(
|
|
2948
|
+
"UES " + PACKAGE_VERSION + ": " +
|
|
2949
|
+
(admission.route === "guarded" ? "high-risk engineering task admitted" : "engineering task auto-admitted") +
|
|
2950
|
+
" (" + admission.reason + ", confidence " + admission.confidence + ")",
|
|
2951
|
+
admission.route === "guarded" ? "warning" : "info",
|
|
2952
|
+
);
|
|
2953
|
+
} catch {}
|
|
2954
|
+
void directControllerRunner(text, ctx, "automatic", admission).catch((error) => {
|
|
2955
|
+
try {
|
|
2956
|
+
ctx.ui.notify(
|
|
2957
|
+
"UES automatic controller failed to start: " +
|
|
2958
|
+
(error instanceof Error ? error.message : String(error)),
|
|
2959
|
+
"error",
|
|
2960
|
+
);
|
|
2961
|
+
} catch {}
|
|
2962
|
+
});
|
|
2963
|
+
return { action: "handled" };
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
return { action: "continue" };
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2422
2969
|
const steered = await RPC_POOL.steerActive(text).catch(() => ({
|
|
2423
2970
|
accepted: false,
|
|
2424
2971
|
reason: "steer-failed",
|
|
@@ -2434,8 +2981,27 @@ export default function (pi: ExtensionAPI) {
|
|
|
2434
2981
|
return { action: "continue" };
|
|
2435
2982
|
});
|
|
2436
2983
|
|
|
2984
|
+
pi.on("before_agent_start", async () => {
|
|
2985
|
+
if (uesModeActive()) activateParentUesTools();
|
|
2986
|
+
else deactivateParentUesTools();
|
|
2987
|
+
});
|
|
2988
|
+
|
|
2989
|
+
pi.on("agent_end", async () => {
|
|
2990
|
+
promptUesActive = false;
|
|
2991
|
+
deactivateParentUesTools();
|
|
2992
|
+
});
|
|
2993
|
+
|
|
2437
2994
|
pi.on("tool_call", async (event, ctx) => {
|
|
2438
2995
|
const toolName = String(event.toolName || "");
|
|
2996
|
+
if (!uesModeActive()) {
|
|
2997
|
+
if (toolName.startsWith("ues_")) {
|
|
2998
|
+
return {
|
|
2999
|
+
block: true,
|
|
3000
|
+
reason: "UES parent tools are hidden outside admitted UES runs. Submit a normal engineering task or start an explicit /ues-* command.",
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
return undefined;
|
|
3004
|
+
}
|
|
2439
3005
|
if (toolName !== "bash" && toolName !== "powershell") {
|
|
2440
3006
|
const allTools = typeof (pi as any).getAllTools === "function" ? (pi as any).getAllTools() : [];
|
|
2441
3007
|
const descriptor = allTools.find((tool: any) => String(tool?.name || "") === toolName);
|
|
@@ -2449,6 +3015,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
2449
3015
|
}
|
|
2450
3016
|
|
|
2451
3017
|
const command = String((event.input as any)?.command || "");
|
|
3018
|
+
if (looksLikeLongRunningServiceCommand(command)) {
|
|
3019
|
+
return {
|
|
3020
|
+
block: true,
|
|
3021
|
+
reason:
|
|
3022
|
+
"UES detected a likely long-running foreground service command. " +
|
|
3023
|
+
"Use ues_service start/wait-ready/logs/stop so Pi can continue without blocking on the server process.",
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
2452
3026
|
const risk = destructiveShellRisk(command);
|
|
2453
3027
|
if (!risk.risky) return undefined;
|
|
2454
3028
|
const allowed = await approveRisk(ctx, command, risk.id || "destructive");
|
|
@@ -2457,6 +3031,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2457
3031
|
});
|
|
2458
3032
|
|
|
2459
3033
|
pi.on("tool_result", async (event) => {
|
|
3034
|
+
if (!uesModeActive()) return undefined;
|
|
2460
3035
|
const toolName = String((event as any).toolName || "");
|
|
2461
3036
|
if (!toolName || toolName === "bash" || toolName === "powershell") return undefined;
|
|
2462
3037
|
MCP_HEALTH.finish(
|
|
@@ -2479,7 +3054,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2479
3054
|
cwd: Type.Optional(Type.String({ description: "Working directory; defaults to the current Pi cwd" })),
|
|
2480
3055
|
}),
|
|
2481
3056
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
2482
|
-
const cwd =
|
|
3057
|
+
const cwd = requireGitWorkspaceRoot(params.cwd || ctx.cwd, "ues_cli");
|
|
2483
3058
|
const separator = params.args.indexOf("--");
|
|
2484
3059
|
if (separator >= 0 && separator < params.args.length - 1) {
|
|
2485
3060
|
const command = params.args.slice(separator + 1).join(" ");
|
|
@@ -2538,6 +3113,91 @@ export default function (pi: ExtensionAPI) {
|
|
|
2538
3113
|
});
|
|
2539
3114
|
|
|
2540
3115
|
pi.registerTool({
|
|
3116
|
+
name: "ues_service",
|
|
3117
|
+
label: "UES Managed Service",
|
|
3118
|
+
description:
|
|
3119
|
+
"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.",
|
|
3120
|
+
parameters: Type.Object({
|
|
3121
|
+
action: Type.Union([
|
|
3122
|
+
Type.Literal("start"),
|
|
3123
|
+
Type.Literal("wait-ready"),
|
|
3124
|
+
Type.Literal("status"),
|
|
3125
|
+
Type.Literal("logs"),
|
|
3126
|
+
Type.Literal("stop"),
|
|
3127
|
+
Type.Literal("restart"),
|
|
3128
|
+
]),
|
|
3129
|
+
name: Type.String({ minLength: 1, maxLength: 64 }),
|
|
3130
|
+
command: Type.Optional(Type.String({ minLength: 1 })),
|
|
3131
|
+
args: Type.Optional(Type.Array(Type.String(), { maxItems: 128 })),
|
|
3132
|
+
cwd: Type.Optional(Type.String()),
|
|
3133
|
+
readyPort: Type.Optional(Type.Number({ minimum: 1, maximum: 65535 })),
|
|
3134
|
+
readyHost: Type.Optional(Type.String({ maxLength: 255 })),
|
|
3135
|
+
readyLog: Type.Optional(Type.String({ maxLength: 512 })),
|
|
3136
|
+
timeoutMs: Type.Optional(Type.Number({ minimum: 100, maximum: 600000 })),
|
|
3137
|
+
lifetimeMs: Type.Optional(Type.Number({ minimum: 10000, maximum: 7200000 })),
|
|
3138
|
+
idleTimeoutMs: Type.Optional(Type.Number({ minimum: 5000, maximum: 3600000 })),
|
|
3139
|
+
maxChars: Type.Optional(Type.Number({ minimum: 256, maximum: 128000 })),
|
|
3140
|
+
}),
|
|
3141
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
3142
|
+
try {
|
|
3143
|
+
const hostCwd = path.resolve(ctx.cwd);
|
|
3144
|
+
const serviceRoot = requireGitWorkspaceRoot(hostCwd, "ues_service");
|
|
3145
|
+
const requestedServiceCwd = params.cwd
|
|
3146
|
+
? path.relative(serviceRoot, path.resolve(hostCwd, params.cwd)) || "."
|
|
3147
|
+
: path.relative(serviceRoot, hostCwd) || ".";
|
|
3148
|
+
let result: any;
|
|
3149
|
+
if (params.action === "start") {
|
|
3150
|
+
if (!params.command) throw new Error("ues_service start requires command");
|
|
3151
|
+
const riskText = [params.command, ...(params.args || [])].join(" ");
|
|
3152
|
+
const risk = destructiveShellRisk(riskText);
|
|
3153
|
+
if (risk.risky) throw new Error(`UES service safety blocked ${risk.id || "destructive"} command`);
|
|
3154
|
+
result = await startService(serviceRoot, {
|
|
3155
|
+
name: params.name,
|
|
3156
|
+
command: params.command,
|
|
3157
|
+
args: params.args || [],
|
|
3158
|
+
cwd: requestedServiceCwd,
|
|
3159
|
+
readyPort: params.readyPort,
|
|
3160
|
+
readyHost: params.readyHost,
|
|
3161
|
+
readyLog: params.readyLog,
|
|
3162
|
+
timeoutMs: params.timeoutMs,
|
|
3163
|
+
lifetimeMs: params.lifetimeMs,
|
|
3164
|
+
idleTimeoutMs: params.idleTimeoutMs,
|
|
3165
|
+
});
|
|
3166
|
+
} else if (params.action === "wait-ready") {
|
|
3167
|
+
result = await waitForService(serviceRoot, params.name, { timeoutMs: params.timeoutMs });
|
|
3168
|
+
} else if (params.action === "status") {
|
|
3169
|
+
result = await serviceStatus(serviceRoot, params.name);
|
|
3170
|
+
} else if (params.action === "logs") {
|
|
3171
|
+
result = await serviceLogs(serviceRoot, params.name, { maxChars: params.maxChars, evidence: true });
|
|
3172
|
+
} else if (params.action === "stop") {
|
|
3173
|
+
result = await stopService(serviceRoot, params.name, { timeoutMs: params.timeoutMs });
|
|
3174
|
+
} else if (params.action === "restart") {
|
|
3175
|
+
result = await restartService(serviceRoot, params.name, {
|
|
3176
|
+
timeoutMs: params.timeoutMs,
|
|
3177
|
+
lifetimeMs: params.lifetimeMs,
|
|
3178
|
+
idleTimeoutMs: params.idleTimeoutMs,
|
|
3179
|
+
});
|
|
3180
|
+
} else {
|
|
3181
|
+
throw new Error("Unknown ues_service action");
|
|
3182
|
+
}
|
|
3183
|
+
const failed =
|
|
3184
|
+
(params.action === "start" || params.action === "wait-ready") && result?.ready !== true;
|
|
3185
|
+
return {
|
|
3186
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
3187
|
+
details: result,
|
|
3188
|
+
isError: failed,
|
|
3189
|
+
};
|
|
3190
|
+
} catch (error) {
|
|
3191
|
+
return {
|
|
3192
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
3193
|
+
details: { action: params.action, name: params.name },
|
|
3194
|
+
isError: true,
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
},
|
|
3198
|
+
});
|
|
3199
|
+
|
|
3200
|
+
const uesExecuteTool: any = {
|
|
2541
3201
|
name: "ues_execute",
|
|
2542
3202
|
label: "UES Execute",
|
|
2543
3203
|
description:
|
|
@@ -2549,16 +3209,55 @@ export default function (pi: ExtensionAPI) {
|
|
|
2549
3209
|
}),
|
|
2550
3210
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
2551
3211
|
const hostBrowserTools = refreshHostBrowserToolNames(pi);
|
|
2552
|
-
const cwd =
|
|
3212
|
+
const cwd = requireGitWorkspaceRoot(params.cwd || ctx.cwd, "ues_execute");
|
|
2553
3213
|
const inheritedModel = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
2554
3214
|
const inheritedThinking = ctx.thinkingLevel as string | undefined;
|
|
2555
|
-
const
|
|
2556
|
-
const
|
|
3215
|
+
const inheritedDirty = captureInheritedDirtyState(cwd);
|
|
3216
|
+
const executionContract = buildExecutionContract(params.task, inheritedDirty);
|
|
3217
|
+
const contractPrompt = executionContractPrompt(executionContract);
|
|
3218
|
+
const suppliedPolicy = (params as any).__taskPolicy;
|
|
3219
|
+
const basePolicy =
|
|
3220
|
+
suppliedPolicy && typeof suppliedPolicy === "object"
|
|
3221
|
+
? suppliedPolicy
|
|
3222
|
+
: classifyEngineeringTask(params.task);
|
|
3223
|
+
const policy = {
|
|
3224
|
+
...basePolicy,
|
|
3225
|
+
localEnvWriteExplicitlyAllowed: executionContract.localEnvWriteExplicitlyAllowed === true,
|
|
3226
|
+
executionContractPrompt: contractPrompt,
|
|
3227
|
+
};
|
|
3228
|
+
const traceID = String((params as any).__traceID || createTraceID("ues-execute"));
|
|
3229
|
+
const orphanCleanup = await pruneOrphanTaskSandboxes(cwd, {
|
|
3230
|
+
minAgeMs: 5 * 60_000,
|
|
3231
|
+
legacyMinAgeMs: 30 * 60_000,
|
|
3232
|
+
ownedMinAgeMs: 60_000,
|
|
3233
|
+
reclaimOwnerPid: process.pid,
|
|
3234
|
+
protectedDirs: [...ACTIVE_TASK_SANDBOXES.keys()],
|
|
3235
|
+
}).catch(() => ({ removed: [], skipped: [], sidecarsRemoved: [] }));
|
|
3236
|
+
if (orphanCleanup.removed?.length) {
|
|
3237
|
+
onUpdate?.({
|
|
3238
|
+
content: [{ type: "text", text: `UES sandbox cleanup: removed ${orphanCleanup.removed.length} orphan worktree(s)` }],
|
|
3239
|
+
details: { mode: "execute", phase: "sandbox-cleanup", orphanCleanup },
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
2557
3242
|
await appendTrajectoryEvent(cwd, traceID, "controller.started", {
|
|
2558
3243
|
profile: policy.executionProfile,
|
|
2559
3244
|
risk: policy.risk,
|
|
2560
3245
|
mode: policy.mode,
|
|
3246
|
+
inheritedDirtyCount: executionContract.inheritedDirty?.paths?.length || 0,
|
|
3247
|
+
explicitPhaseCount: executionContract.phases?.length || 0,
|
|
3248
|
+
localEnvWriteExplicitlyAllowed: executionContract.localEnvWriteExplicitlyAllowed === true,
|
|
2561
3249
|
}).catch(() => {});
|
|
3250
|
+
if ((executionContract.inheritedDirty?.paths?.length || 0) > 0 || (executionContract.phases?.length || 0) > 0) {
|
|
3251
|
+
onUpdate?.({
|
|
3252
|
+
content: [{
|
|
3253
|
+
type: "text",
|
|
3254
|
+
text:
|
|
3255
|
+
`UES contract: inherited dirty ${executionContract.inheritedDirty?.paths?.length || 0}; ` +
|
|
3256
|
+
`explicit phases ${executionContract.phases?.length || 0}; local .env write ${executionContract.localEnvWriteExplicitlyAllowed ? "authorized" : "blocked"}`,
|
|
3257
|
+
}],
|
|
3258
|
+
details: { mode: "execute", phase: "execution-contract", executionContract, traceID },
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
2562
3261
|
const browserLaneRequested =
|
|
2563
3262
|
browserEvidenceNeeded(params.task, "executor") || visualEvidenceNeeded(params.task);
|
|
2564
3263
|
if (browserLaneRequested) {
|
|
@@ -2603,9 +3302,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
2603
3302
|
});
|
|
2604
3303
|
|
|
2605
3304
|
const run = async (agent: AgentName, task: string, attempt = 1, failure?: string) => {
|
|
3305
|
+
const planningBudget = planningRuntimeBudget(roleForAgent(agent), attempt, {
|
|
3306
|
+
executionProfile: policy.executionProfile,
|
|
3307
|
+
risk: policy.risk,
|
|
3308
|
+
taskChars: task.length,
|
|
3309
|
+
});
|
|
3310
|
+
let softSteerSent = false;
|
|
3311
|
+
const governedTask = [task, "", contractPrompt].filter(Boolean).join("\n");
|
|
2606
3312
|
const result = await runRoutedAgent(
|
|
2607
3313
|
agent,
|
|
2608
|
-
|
|
3314
|
+
governedTask,
|
|
2609
3315
|
cwd,
|
|
2610
3316
|
inheritedModel,
|
|
2611
3317
|
inheritedThinking,
|
|
@@ -2613,6 +3319,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
2613
3319
|
failure,
|
|
2614
3320
|
signal,
|
|
2615
3321
|
(progress) => {
|
|
3322
|
+
if (
|
|
3323
|
+
!softSteerSent &&
|
|
3324
|
+
(
|
|
3325
|
+
(agent === "ues-architect" && shouldSoftSteerArchitect(progress, planningBudget)) ||
|
|
3326
|
+
(agent === "ues-plan-checker" && shouldSoftSteerPlanningRole(progress, planningBudget))
|
|
3327
|
+
)
|
|
3328
|
+
) {
|
|
3329
|
+
softSteerSent = true;
|
|
3330
|
+
const steerText =
|
|
3331
|
+
agent === "ues-architect"
|
|
3332
|
+
? "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."
|
|
3333
|
+
: "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.";
|
|
3334
|
+
void RPC_POOL.steerActive(steerText).catch(() => ({ accepted: false }));
|
|
3335
|
+
onUpdate?.({
|
|
3336
|
+
content: [{
|
|
3337
|
+
type: "text",
|
|
3338
|
+
text:
|
|
3339
|
+
agent === "ues-architect"
|
|
3340
|
+
? "UES planning fast-stop: architect evidence budget reached; requesting immediate plan emission"
|
|
3341
|
+
: "UES planning fast-stop: plan-checker budget reached; requesting immediate verdict",
|
|
3342
|
+
}],
|
|
3343
|
+
details: { mode: "execute", phase: "planning-soft-steer", policy, progress, planningBudget, traceID },
|
|
3344
|
+
});
|
|
3345
|
+
}
|
|
2616
3346
|
onUpdate?.({
|
|
2617
3347
|
content: [{
|
|
2618
3348
|
type: "text",
|
|
@@ -2627,6 +3357,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2627
3357
|
});
|
|
2628
3358
|
},
|
|
2629
3359
|
traceID,
|
|
3360
|
+
policy,
|
|
2630
3361
|
);
|
|
2631
3362
|
steps.push(result);
|
|
2632
3363
|
onUpdate?.({
|
|
@@ -2639,6 +3370,176 @@ export default function (pi: ExtensionAPI) {
|
|
|
2639
3370
|
return result;
|
|
2640
3371
|
};
|
|
2641
3372
|
|
|
3373
|
+
if (policy.readOnly === true) {
|
|
3374
|
+
const before = runtimeWorkspaceSnapshot(cwd);
|
|
3375
|
+
const deterministicChecks = deterministicReadOnlyGitCommands(params.task);
|
|
3376
|
+
let inspection: any;
|
|
3377
|
+
|
|
3378
|
+
if (deterministicChecks.length > 0) {
|
|
3379
|
+
const rows: any[] = [];
|
|
3380
|
+
for (const spec of deterministicChecks) {
|
|
3381
|
+
const result = await runProcess(spec.command, spec.args, cwd, signal);
|
|
3382
|
+
if (result.exitCode === 130 || result.stopReason === "aborted") {
|
|
3383
|
+
const aborted: any = {
|
|
3384
|
+
agent: "ues-deterministic-read-only",
|
|
3385
|
+
task: params.task,
|
|
3386
|
+
cwd,
|
|
3387
|
+
exitCode: 130,
|
|
3388
|
+
output: "Deterministic read-only command aborted: " + spec.label,
|
|
3389
|
+
stderr: result.stderr || "",
|
|
3390
|
+
verdict: null,
|
|
3391
|
+
durationMs: result.durationMs || 0,
|
|
3392
|
+
toolCalls: 0,
|
|
3393
|
+
toolNames: [],
|
|
3394
|
+
report: parseStructuredReport(""),
|
|
3395
|
+
stopReason: "aborted",
|
|
3396
|
+
};
|
|
3397
|
+
steps.push(aborted);
|
|
3398
|
+
return abortedResponse(aborted, "read-only-deterministic-check");
|
|
3399
|
+
}
|
|
3400
|
+
rows.push({
|
|
3401
|
+
...spec,
|
|
3402
|
+
exitCode: result.exitCode,
|
|
3403
|
+
stdout: cap(result.stdout || "", 8000),
|
|
3404
|
+
stderr: cap(result.stderr || "", 4000),
|
|
3405
|
+
});
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
const afterChecks = runtimeWorkspaceSnapshot(cwd);
|
|
3409
|
+
const unchangedAfterChecks =
|
|
3410
|
+
before.cacheable === true &&
|
|
3411
|
+
afterChecks.cacheable === true &&
|
|
3412
|
+
before.fingerprint === afterChecks.fingerprint;
|
|
3413
|
+
const failedRows = rows.filter((row) => Number(row.exitCode) !== 0);
|
|
3414
|
+
const deterministicPass = failedRows.length === 0 && unchangedAfterChecks;
|
|
3415
|
+
const checksText = rows.map((row) => {
|
|
3416
|
+
const payload = [row.stdout, row.stderr].filter(Boolean).join("\n").trim() || "(no output)";
|
|
3417
|
+
return [
|
|
3418
|
+
row.label + " — exit " + row.exitCode,
|
|
3419
|
+
payload,
|
|
3420
|
+
].join("\n");
|
|
3421
|
+
}).join("\n\n");
|
|
3422
|
+
const failureText = [
|
|
3423
|
+
...failedRows.map((row) => row.label + " exited " + row.exitCode),
|
|
3424
|
+
...(unchangedAfterChecks ? [] : ["read-only workspace fingerprint changed"]),
|
|
3425
|
+
].join("\n") || "None.";
|
|
3426
|
+
|
|
3427
|
+
const output = [
|
|
3428
|
+
"## Checks run",
|
|
3429
|
+
checksText,
|
|
3430
|
+
"",
|
|
3431
|
+
"## Acceptance criteria proven",
|
|
3432
|
+
deterministicPass
|
|
3433
|
+
? "All explicitly requested whitelisted Git inspection commands completed successfully."
|
|
3434
|
+
: "The requested deterministic inspection did not fully pass.",
|
|
3435
|
+
"",
|
|
3436
|
+
"## Failures",
|
|
3437
|
+
failureText,
|
|
3438
|
+
"",
|
|
3439
|
+
"## Unresolved gaps",
|
|
3440
|
+
"None.",
|
|
3441
|
+
"",
|
|
3442
|
+
"## Checks not run",
|
|
3443
|
+
"None.",
|
|
3444
|
+
"",
|
|
3445
|
+
"## Completion evidence",
|
|
3446
|
+
"UES executed " + rows.length + " command-only read-only Git check(s) directly and compared the source workspace fingerprint before and after.",
|
|
3447
|
+
"",
|
|
3448
|
+
"UES_VERDICT: " + (deterministicPass ? "PASS" : "FAIL"),
|
|
3449
|
+
].join("\n");
|
|
3450
|
+
|
|
3451
|
+
inspection = {
|
|
3452
|
+
agent: "ues-deterministic-read-only",
|
|
3453
|
+
task: params.task,
|
|
3454
|
+
cwd,
|
|
3455
|
+
exitCode: deterministicPass ? 0 : 1,
|
|
3456
|
+
output,
|
|
3457
|
+
stderr: "",
|
|
3458
|
+
verdict: deterministicPass ? "PASS" : "FAIL",
|
|
3459
|
+
durationMs: 0,
|
|
3460
|
+
toolCalls: rows.length,
|
|
3461
|
+
toolNames: rows.map((row) => row.label),
|
|
3462
|
+
report: parseStructuredReport(output),
|
|
3463
|
+
optimizations: {
|
|
3464
|
+
deterministicReadOnlyGit: true,
|
|
3465
|
+
checkCount: rows.length,
|
|
3466
|
+
},
|
|
3467
|
+
};
|
|
3468
|
+
steps.push(inspection);
|
|
3469
|
+
onUpdate?.({
|
|
3470
|
+
content: [{
|
|
3471
|
+
type: "text",
|
|
3472
|
+
text: "UES read-only fast path: executed " + rows.length + " whitelisted Git check(s) deterministically; skipped verifier model turn",
|
|
3473
|
+
}],
|
|
3474
|
+
details: { mode: "execute", phase: "read-only-deterministic", policy, traceID, checks: rows.map((row) => row.label) },
|
|
3475
|
+
});
|
|
3476
|
+
} else {
|
|
3477
|
+
inspection = await run(
|
|
3478
|
+
"ues-verifier",
|
|
3479
|
+
[
|
|
3480
|
+
"READ-ONLY INSPECTION MODE. Do not edit, create, delete, stage, commit, install dependencies, or start persistent services.",
|
|
3481
|
+
"Use only the narrowest read-only commands and source inspection needed to answer the request.",
|
|
3482
|
+
"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.",
|
|
3483
|
+
"Return exactly these H2 sections: ## Checks run, ## Acceptance criteria proven, ## Failures, ## Unresolved gaps, ## Checks not run, ## Completion evidence.",
|
|
3484
|
+
"Write exactly None in Failures and Unresolved gaps when there is no real requested failure or gap.",
|
|
3485
|
+
"End with exactly UES_VERDICT: PASS or UES_VERDICT: FAIL.",
|
|
3486
|
+
"Explicitly verify that source-facing workspace state is unchanged before returning PASS.",
|
|
3487
|
+
"",
|
|
3488
|
+
"Inspection request:",
|
|
3489
|
+
params.task,
|
|
3490
|
+
].join("\n"),
|
|
3491
|
+
1,
|
|
3492
|
+
);
|
|
3493
|
+
if (isAbortedRun(inspection)) return abortedResponse(inspection, "read-only-inspection");
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
const after = runtimeWorkspaceSnapshot(cwd);
|
|
3497
|
+
const unchanged =
|
|
3498
|
+
before.cacheable === true &&
|
|
3499
|
+
after.cacheable === true &&
|
|
3500
|
+
before.fingerprint === after.fingerprint;
|
|
3501
|
+
const completionAudit = auditCompletion({
|
|
3502
|
+
verification: inspection,
|
|
3503
|
+
requireIntegration: false,
|
|
3504
|
+
requireVisual: false,
|
|
3505
|
+
workspaceSnapshot: after,
|
|
3506
|
+
behavioralReceipts: [],
|
|
3507
|
+
requireBehavioralReceipt: false,
|
|
3508
|
+
});
|
|
3509
|
+
if (!unchanged) {
|
|
3510
|
+
completionAudit.passed = false;
|
|
3511
|
+
completionAudit.failures = [...new Set([
|
|
3512
|
+
...(completionAudit.failures || []),
|
|
3513
|
+
"read-only-workspace-mutated",
|
|
3514
|
+
])];
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
if (!completionAudit.passed) {
|
|
3518
|
+
return {
|
|
3519
|
+
content: [{
|
|
3520
|
+
type: "text",
|
|
3521
|
+
text: "UES read-only inspection did not pass: " + completionAudit.failures.join(", ") +
|
|
3522
|
+
"\n\n" + inspection.output,
|
|
3523
|
+
}],
|
|
3524
|
+
details: { mode: "execute", policy, steps, completionAudit, traceID },
|
|
3525
|
+
isError: true,
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3529
|
+
return {
|
|
3530
|
+
content: [{
|
|
3531
|
+
type: "text",
|
|
3532
|
+
text: [
|
|
3533
|
+
"UES read-only inspection PASS.",
|
|
3534
|
+
"Workspace source fingerprint remained unchanged.",
|
|
3535
|
+
"",
|
|
3536
|
+
inspection.output,
|
|
3537
|
+
].join("\n"),
|
|
3538
|
+
}],
|
|
3539
|
+
details: { mode: "execute", policy, steps, completionAudit, traceID },
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
|
|
2642
3543
|
if (shouldRunDedicatedDiagnosis(policy, 1)) {
|
|
2643
3544
|
const diagnosis = await run("ues-debugger", params.task, 1);
|
|
2644
3545
|
if (isAbortedRun(diagnosis)) return abortedResponse(diagnosis, "diagnosis");
|
|
@@ -2657,9 +3558,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
2657
3558
|
params.task,
|
|
2658
3559
|
"",
|
|
2659
3560
|
"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,
|
|
3561
|
+
"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
3562
|
"Each task must have id, title, summary, dependsOn, files ({create,modify,test,delete,read}), acceptance, verification, and risk.",
|
|
3563
|
+
executionContract.phases?.length
|
|
3564
|
+
? "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."
|
|
3565
|
+
: "",
|
|
3566
|
+
"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
3567
|
"Declare every file a task may write. Do not invent files: inspect the repository first.",
|
|
3568
|
+
"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
3569
|
].join("\n");
|
|
2664
3570
|
|
|
2665
3571
|
let architect = await run(
|
|
@@ -2669,70 +3575,237 @@ export default function (pi: ExtensionAPI) {
|
|
|
2669
3575
|
recentFailure || undefined,
|
|
2670
3576
|
);
|
|
2671
3577
|
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
3578
|
|
|
2680
|
-
|
|
2681
|
-
|
|
3579
|
+
let planCandidate = extractValidatedPlan(architect.output);
|
|
3580
|
+
structuredPlan = planCandidate.plan;
|
|
3581
|
+
let structuredValidation = planCandidate.validation;
|
|
2682
3582
|
|
|
2683
|
-
if (
|
|
2684
|
-
(
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
3583
|
+
if (planCandidate.salvaged && structuredValidation?.valid === true) {
|
|
3584
|
+
onUpdate?.({
|
|
3585
|
+
content: [{
|
|
3586
|
+
type: "text",
|
|
3587
|
+
text: `UES planning salvage: recovered a valid plan from ${planCandidate.source} architect output`,
|
|
3588
|
+
}],
|
|
3589
|
+
details: {
|
|
3590
|
+
mode: "execute",
|
|
3591
|
+
phase: "planning-salvage",
|
|
3592
|
+
source: planCandidate.source,
|
|
3593
|
+
traceID,
|
|
3594
|
+
},
|
|
3595
|
+
});
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
const firstPlanValid =
|
|
3599
|
+
structuredPlan &&
|
|
3600
|
+
structuredValidation?.valid === true;
|
|
3601
|
+
|
|
3602
|
+
if (!firstPlanValid) {
|
|
3603
|
+
const recoveryEvidence = failureDelta([
|
|
3604
|
+
architect.exitCode !== 0 || architect.stopReason
|
|
3605
|
+
? "Previous architect pass stopped before a valid plan: " + String(architect.stopReason || architect.exitCode)
|
|
3606
|
+
: "Previous architect pass returned an invalid plan.",
|
|
3607
|
+
structuredValidation
|
|
3608
|
+
? JSON.stringify(structuredValidation, null, 2)
|
|
3609
|
+
: "UES_PLAN_JSON marker or valid JSON object was missing.",
|
|
3610
|
+
"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.",
|
|
3611
|
+
"acceptance and verification must be non-empty string arrays; risk must be low|medium|high|critical; descriptive prose belongs in riskNotes.",
|
|
3612
|
+
].join("\n"), { maxChars: 4200 });
|
|
3613
|
+
|
|
3614
|
+
architect = await run("ues-architect", planInstruction, 2, recoveryEvidence);
|
|
3615
|
+
if (isAbortedRun(architect)) return abortedResponse(architect, "plan-recovery");
|
|
3616
|
+
planCandidate = extractValidatedPlan(architect.output);
|
|
3617
|
+
structuredPlan = planCandidate.plan;
|
|
3618
|
+
structuredValidation = planCandidate.validation;
|
|
3619
|
+
if (planCandidate.salvaged && structuredValidation?.valid === true) {
|
|
3620
|
+
onUpdate?.({
|
|
2703
3621
|
content: [{
|
|
2704
3622
|
type: "text",
|
|
2705
|
-
text:
|
|
2706
|
-
(structuredValidation ? JSON.stringify(structuredValidation, null, 2) : architect.output),
|
|
3623
|
+
text: `UES planning salvage: recovery produced a valid ${planCandidate.source} plan`,
|
|
2707
3624
|
}],
|
|
2708
|
-
details: {
|
|
2709
|
-
|
|
2710
|
-
|
|
3625
|
+
details: {
|
|
3626
|
+
mode: "execute",
|
|
3627
|
+
phase: "planning-salvage",
|
|
3628
|
+
source: planCandidate.source,
|
|
3629
|
+
traceID,
|
|
3630
|
+
},
|
|
3631
|
+
});
|
|
2711
3632
|
}
|
|
2712
3633
|
}
|
|
2713
3634
|
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
3635
|
+
if (
|
|
3636
|
+
!structuredPlan ||
|
|
3637
|
+
structuredValidation?.valid !== true
|
|
3638
|
+
) {
|
|
3639
|
+
return {
|
|
3640
|
+
content: [{
|
|
3641
|
+
type: "text",
|
|
3642
|
+
text: "Long-horizon planning exhausted its bounded fast-planning recovery without a valid deterministic task graph.\n\n" +
|
|
3643
|
+
(structuredValidation ? JSON.stringify(structuredValidation, null, 2) : architect.output),
|
|
3644
|
+
}],
|
|
3645
|
+
details: { mode: "execute", policy, steps, structuredPlan, structuredValidation },
|
|
3646
|
+
isError: true,
|
|
3647
|
+
};
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
let phaseGate = enforcePhaseGates(structuredPlan, executionContract);
|
|
3651
|
+
if (phaseGate.valid !== true) {
|
|
3652
|
+
const phaseRevisionEvidence = failureDelta([
|
|
3653
|
+
"The deterministic explicit-phase gate rejected the plan.",
|
|
3654
|
+
"Do not rescan the repository. Revise only phase assignment/coverage/dependencies.",
|
|
3655
|
+
"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.",
|
|
3656
|
+
"Phase gate errors:",
|
|
3657
|
+
phaseGate.errors.join("\n"),
|
|
3658
|
+
"Current plan:",
|
|
3659
|
+
JSON.stringify(structuredPlan, null, 2),
|
|
3660
|
+
].join("\n"), { maxChars: 6200 });
|
|
3661
|
+
const phaseRevisedArchitect = await run(
|
|
3662
|
+
"ues-architect",
|
|
3663
|
+
planInstruction,
|
|
3664
|
+
2,
|
|
3665
|
+
phaseRevisionEvidence,
|
|
3666
|
+
);
|
|
3667
|
+
if (isAbortedRun(phaseRevisedArchitect)) {
|
|
3668
|
+
return abortedResponse(phaseRevisedArchitect, "phase-plan-auto-revise");
|
|
3669
|
+
}
|
|
3670
|
+
const phaseRevisedCandidate = extractValidatedPlan(phaseRevisedArchitect.output);
|
|
3671
|
+
if (phaseRevisedCandidate.plan && phaseRevisedCandidate.validation?.valid === true) {
|
|
3672
|
+
const revisedPhaseGate = enforcePhaseGates(
|
|
3673
|
+
phaseRevisedCandidate.plan,
|
|
3674
|
+
executionContract,
|
|
3675
|
+
);
|
|
3676
|
+
if (revisedPhaseGate.valid === true) {
|
|
3677
|
+
architect = phaseRevisedArchitect;
|
|
3678
|
+
structuredPlan = revisedPhaseGate.plan;
|
|
3679
|
+
structuredValidation = phaseRevisedCandidate.validation;
|
|
3680
|
+
phaseGate = revisedPhaseGate;
|
|
3681
|
+
onUpdate?.({
|
|
3682
|
+
content: [{
|
|
3683
|
+
type: "text",
|
|
3684
|
+
text: "UES phase gate: auto-revised phase coverage/dependencies once and recovered a valid gated plan",
|
|
3685
|
+
}],
|
|
3686
|
+
details: { mode: "execute", phase: "phase-plan-auto-revise", phaseGate, traceID },
|
|
3687
|
+
});
|
|
3688
|
+
} else {
|
|
3689
|
+
phaseGate = revisedPhaseGate;
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
if (phaseGate.valid !== true) {
|
|
3694
|
+
return {
|
|
3695
|
+
content: [{
|
|
3696
|
+
type: "text",
|
|
3697
|
+
text:
|
|
3698
|
+
"Explicit phase contract gate did not pass after bounded auto-recovery. UES will not flatten or skip user-declared phases.\n\n" +
|
|
3699
|
+
phaseGate.errors.join("\n"),
|
|
3700
|
+
}],
|
|
3701
|
+
details: { mode: "execute", policy, steps, structuredPlan, phaseGate, executionContract },
|
|
3702
|
+
isError: true,
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
structuredPlan = phaseGate.plan;
|
|
3706
|
+
|
|
3707
|
+
const buildPlanCheckTask = () => [
|
|
3708
|
+
"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.",
|
|
3709
|
+
"Use declared files and dependencies first. Do not inventory the repository. Resolve only concrete grounding gaps, then return the verdict.",
|
|
3710
|
+
"",
|
|
3711
|
+
"Original task:",
|
|
3712
|
+
params.task,
|
|
3713
|
+
"",
|
|
3714
|
+
"Inline plan:",
|
|
3715
|
+
structuredPlan ? JSON.stringify(structuredPlan, null, 2) : architect.output,
|
|
3716
|
+
].join("\n");
|
|
3717
|
+
|
|
3718
|
+
let planCheckTask = buildPlanCheckTask();
|
|
3719
|
+
let planCheck = await run("ues-plan-checker", planCheckTask, 1);
|
|
2727
3720
|
if (isAbortedRun(planCheck)) return abortedResponse(planCheck, "plan-verification");
|
|
3721
|
+
|
|
3722
|
+
const planCheckTransportFailure =
|
|
3723
|
+
planCheck.verdict !== "PASS" &&
|
|
3724
|
+
planCheck.verdict !== "REVISE" &&
|
|
3725
|
+
(
|
|
3726
|
+
planCheck.exitCode !== 0 ||
|
|
3727
|
+
/hard-timeout|idle-timeout|UES RPC hard-timeout|UES RPC idle-timeout/i.test(
|
|
3728
|
+
String(planCheck.output || "") + "\n" +
|
|
3729
|
+
String(planCheck.stderr || "") + "\n" +
|
|
3730
|
+
String(planCheck.stopReason || ""),
|
|
3731
|
+
)
|
|
3732
|
+
);
|
|
3733
|
+
|
|
3734
|
+
if (planCheckTransportFailure) {
|
|
3735
|
+
const planCheckRecoveryEvidence = failureDelta([
|
|
3736
|
+
"Previous plan-checker runtime failure:",
|
|
3737
|
+
String(planCheck.output || planCheck.stderr || planCheck.stopReason || "unknown"),
|
|
3738
|
+
"Reuse warm context. Do not scan broadly. Check only unresolved declared paths/dependencies and return PASS or REVISE.",
|
|
3739
|
+
].join("\n"), { maxChars: 2600 });
|
|
3740
|
+
|
|
3741
|
+
planCheck = await run(
|
|
3742
|
+
"ues-plan-checker",
|
|
3743
|
+
planCheckTask,
|
|
3744
|
+
2,
|
|
3745
|
+
planCheckRecoveryEvidence,
|
|
3746
|
+
);
|
|
3747
|
+
if (isAbortedRun(planCheck)) return abortedResponse(planCheck, "plan-verification-recovery");
|
|
3748
|
+
}
|
|
3749
|
+
|
|
3750
|
+
if (planCheck.verdict === "REVISE") {
|
|
3751
|
+
const revisionEvidence = failureDelta([
|
|
3752
|
+
"The independent plan-checker requested a bounded revision.",
|
|
3753
|
+
"Do not restart broad repository exploration. Change only the rejected plan claims/scope.",
|
|
3754
|
+
"Plan-checker feedback:",
|
|
3755
|
+
String(planCheck.output || ""),
|
|
3756
|
+
"Current plan:",
|
|
3757
|
+
JSON.stringify(structuredPlan, null, 2),
|
|
3758
|
+
].join("\n"), { maxChars: 5200 });
|
|
3759
|
+
|
|
3760
|
+
const revisedArchitect = await run(
|
|
3761
|
+
"ues-architect",
|
|
3762
|
+
planInstruction,
|
|
3763
|
+
2,
|
|
3764
|
+
revisionEvidence,
|
|
3765
|
+
);
|
|
3766
|
+
if (isAbortedRun(revisedArchitect)) return abortedResponse(revisedArchitect, "plan-auto-revise");
|
|
3767
|
+
const revisedCandidate = extractValidatedPlan(revisedArchitect.output);
|
|
3768
|
+
if (revisedCandidate.plan && revisedCandidate.validation?.valid === true) {
|
|
3769
|
+
architect = revisedArchitect;
|
|
3770
|
+
structuredPlan = revisedCandidate.plan;
|
|
3771
|
+
structuredValidation = revisedCandidate.validation;
|
|
3772
|
+
planCheckTask = buildPlanCheckTask();
|
|
3773
|
+
onUpdate?.({
|
|
3774
|
+
content: [{ type: "text", text: "UES plan gate: auto-revised the rejected plan; re-checking once" }],
|
|
3775
|
+
details: { mode: "execute", phase: "plan-auto-revise", policy, traceID },
|
|
3776
|
+
});
|
|
3777
|
+
planCheck = await run(
|
|
3778
|
+
"ues-plan-checker",
|
|
3779
|
+
planCheckTask,
|
|
3780
|
+
2,
|
|
3781
|
+
failureDelta(String(planCheck.output || ""), { maxChars: 2600 }),
|
|
3782
|
+
);
|
|
3783
|
+
if (isAbortedRun(planCheck)) return abortedResponse(planCheck, "plan-auto-revise-check");
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
const finalPhaseGate = enforcePhaseGates(structuredPlan, executionContract);
|
|
3788
|
+
if (finalPhaseGate.valid !== true) {
|
|
3789
|
+
return {
|
|
3790
|
+
content: [{
|
|
3791
|
+
type: "text",
|
|
3792
|
+
text:
|
|
3793
|
+
"Plan revision lost the deterministic explicit-phase contract. UES will not execute it.\n\n" +
|
|
3794
|
+
finalPhaseGate.errors.join("\n"),
|
|
3795
|
+
}],
|
|
3796
|
+
details: { mode: "execute", policy, steps, structuredPlan, finalPhaseGate, executionContract },
|
|
3797
|
+
isError: true,
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
structuredPlan = finalPhaseGate.plan;
|
|
3801
|
+
|
|
2728
3802
|
if (planCheck.exitCode !== 0 || planCheck.verdict !== "PASS") {
|
|
2729
3803
|
return {
|
|
2730
|
-
content: [{ type: "text", text: `Plan gate did not pass:\n\n${planCheck.output}` }],
|
|
3804
|
+
content: [{ type: "text", text: `Plan gate did not pass after bounded auto-recovery:\n\n${planCheck.output}` }],
|
|
2731
3805
|
details: { mode: "execute", policy, steps, structuredPlan },
|
|
2732
3806
|
isError: true,
|
|
2733
3807
|
};
|
|
2734
3808
|
}
|
|
2735
|
-
|
|
2736
3809
|
const durableRequested =
|
|
2737
3810
|
policy.mode === "long-horizon" || policy.profile?.durableState === true;
|
|
2738
3811
|
if (durableRequested) {
|
|
@@ -2754,6 +3827,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
2754
3827
|
planCheck.output,
|
|
2755
3828
|
signal,
|
|
2756
3829
|
);
|
|
3830
|
+
durableWork.executionArtifacts = await persistExecutionContractArtifacts(
|
|
3831
|
+
durableWork.dir,
|
|
3832
|
+
executionContract,
|
|
3833
|
+
);
|
|
2757
3834
|
onUpdate?.({
|
|
2758
3835
|
content: [{
|
|
2759
3836
|
type: "text",
|
|
@@ -2787,6 +3864,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2787
3864
|
inheritedModel,
|
|
2788
3865
|
inheritedThinking,
|
|
2789
3866
|
maxAttempts,
|
|
3867
|
+
rootPolicy: policy,
|
|
2790
3868
|
durableSlug: durableWork?.slug,
|
|
2791
3869
|
traceID,
|
|
2792
3870
|
signal,
|
|
@@ -2893,12 +3971,81 @@ export default function (pi: ExtensionAPI) {
|
|
|
2893
3971
|
}
|
|
2894
3972
|
}
|
|
2895
3973
|
|
|
3974
|
+
const verdictMatrix = buildFinalVerdictMatrix(params.task, {
|
|
3975
|
+
contract: executionContract,
|
|
3976
|
+
primaryPass: true,
|
|
3977
|
+
integrationPass: true,
|
|
3978
|
+
primaryOutput: (scheduled.results || [])
|
|
3979
|
+
.map((row: any) => row?.verification?.output || row?.implementation?.output || "")
|
|
3980
|
+
.filter(Boolean)
|
|
3981
|
+
.join("\n"),
|
|
3982
|
+
integrationOutput: integration.output,
|
|
3983
|
+
visualOutput: visualResult?.output || "",
|
|
3984
|
+
primaryChecks: (scheduled.results || [])
|
|
3985
|
+
.flatMap((row: any) => Object.values(row?.verification?.report?.sections || {}))
|
|
3986
|
+
.filter(Boolean)
|
|
3987
|
+
.join("\n"),
|
|
3988
|
+
integrationChecks: integration.report?.sections?.["checks-run"] || "",
|
|
3989
|
+
visualChecks: visualResult?.report?.sections?.["checks-run"] || "",
|
|
3990
|
+
});
|
|
3991
|
+
|
|
3992
|
+
const finalEvidenceForContract = [
|
|
3993
|
+
integration.output,
|
|
3994
|
+
visualResult?.output || "",
|
|
3995
|
+
].filter(Boolean).join("\n\n--- VISUAL ---\n\n");
|
|
3996
|
+
|
|
3997
|
+
if (verdictMatrix.final !== "PASS") {
|
|
3998
|
+
if (durableWork) {
|
|
3999
|
+
await durableRecordIntegration(
|
|
4000
|
+
cwd,
|
|
4001
|
+
durableWork.slug,
|
|
4002
|
+
"PARTIAL",
|
|
4003
|
+
finalEvidenceForContract,
|
|
4004
|
+
signal,
|
|
4005
|
+
).catch(() => {});
|
|
4006
|
+
await finalizeExecutionContractArtifacts(
|
|
4007
|
+
durableWork.dir,
|
|
4008
|
+
executionContract,
|
|
4009
|
+
verdictMatrix,
|
|
4010
|
+
finalEvidenceForContract,
|
|
4011
|
+
).catch(() => null);
|
|
4012
|
+
}
|
|
4013
|
+
const deviceOnlyPending =
|
|
4014
|
+
verdictMatrix.final === "SOURCE_RUNTIME_PASS_DEVICE_NOT_VERIFIED";
|
|
4015
|
+
return {
|
|
4016
|
+
content: [{
|
|
4017
|
+
type: "text",
|
|
4018
|
+
text: [
|
|
4019
|
+
deviceOnlyPending
|
|
4020
|
+
? "UES source/runtime verification passed, but requested real-device verification is still pending."
|
|
4021
|
+
: "UES deterministic final gate is not fully verified.",
|
|
4022
|
+
"",
|
|
4023
|
+
verdictMatrix.source,
|
|
4024
|
+
verdictMatrix.runtime,
|
|
4025
|
+
verdictMatrix.dbClean,
|
|
4026
|
+
verdictMatrix.device,
|
|
4027
|
+
"",
|
|
4028
|
+
integration.output,
|
|
4029
|
+
].join("\n"),
|
|
4030
|
+
}],
|
|
4031
|
+
details: {
|
|
4032
|
+
mode: "execute",
|
|
4033
|
+
policy,
|
|
4034
|
+
steps,
|
|
4035
|
+
structuredPlan,
|
|
4036
|
+
scheduled,
|
|
4037
|
+
durableWork,
|
|
4038
|
+
verdictMatrix,
|
|
4039
|
+
executionContract,
|
|
4040
|
+
traceID,
|
|
4041
|
+
},
|
|
4042
|
+
isError: !deviceOnlyPending,
|
|
4043
|
+
};
|
|
4044
|
+
}
|
|
4045
|
+
|
|
2896
4046
|
let durableFinalization: any = null;
|
|
2897
4047
|
if (durableWork) {
|
|
2898
|
-
const finalEvidence =
|
|
2899
|
-
integration.output,
|
|
2900
|
-
visualResult?.output || "",
|
|
2901
|
-
].filter(Boolean).join("\n\n--- VISUAL ---\n\n");
|
|
4048
|
+
const finalEvidence = finalEvidenceForContract;
|
|
2902
4049
|
try {
|
|
2903
4050
|
durableFinalization = await durableRecordIntegration(
|
|
2904
4051
|
cwd,
|
|
@@ -2921,6 +4068,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
2921
4068
|
}
|
|
2922
4069
|
}
|
|
2923
4070
|
|
|
4071
|
+
if (durableWork) {
|
|
4072
|
+
await finalizeExecutionContractArtifacts(
|
|
4073
|
+
durableWork.dir,
|
|
4074
|
+
executionContract,
|
|
4075
|
+
verdictMatrix,
|
|
4076
|
+
finalEvidenceForContract,
|
|
4077
|
+
).catch(() => null);
|
|
4078
|
+
}
|
|
4079
|
+
|
|
2924
4080
|
const memoryFiles = [...new Set(structuredPlan.tasks.flatMap((task: any) => taskWriteFiles(task)))];
|
|
2925
4081
|
const memory = durableWork
|
|
2926
4082
|
? durableFinalization?.finalized?.memory || null
|
|
@@ -2932,6 +4088,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
2932
4088
|
`UES scheduled execution PASS across ${structuredPlan.tasks.length} task(s).`,
|
|
2933
4089
|
`Safe waves: ${scheduled.schedule?.safeWaves?.length || 0}; integrations: ${scheduled.integrations?.length || 0}.`,
|
|
2934
4090
|
durableWork ? `Durable state: .ues-work/${durableWork.slug} finalized with fresh integration receipt.` : "",
|
|
4091
|
+
verdictMatrix.source,
|
|
4092
|
+
verdictMatrix.runtime,
|
|
4093
|
+
verdictMatrix.dbClean,
|
|
4094
|
+
verdictMatrix.device,
|
|
2935
4095
|
"",
|
|
2936
4096
|
integration.output,
|
|
2937
4097
|
].join("\n"),
|
|
@@ -2946,6 +4106,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
2946
4106
|
memory,
|
|
2947
4107
|
durableWork,
|
|
2948
4108
|
durableFinalization,
|
|
4109
|
+
verdictMatrix,
|
|
4110
|
+
executionContract,
|
|
2949
4111
|
traceID,
|
|
2950
4112
|
},
|
|
2951
4113
|
};
|
|
@@ -2979,18 +4141,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
2979
4141
|
recentFailure = diagnosis.output;
|
|
2980
4142
|
}
|
|
2981
4143
|
|
|
2982
|
-
const
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
4144
|
+
const fastDecision = turboFastPathDecision(policy, {
|
|
4145
|
+
role: "executor",
|
|
4146
|
+
attempt,
|
|
4147
|
+
browserRequested: browserEvidenceNeeded(params.task, "executor"),
|
|
4148
|
+
visualRequired: visualEvidenceNeeded(params.task),
|
|
4149
|
+
});
|
|
4150
|
+
const fastBoundedLane = fastDecision.eligible;
|
|
2988
4151
|
const fastAttemptStartedAt = Date.now();
|
|
2989
4152
|
const executorTask = [
|
|
2990
4153
|
params.task,
|
|
2991
4154
|
recentFailure ? "\nEvidence from diagnosis/previous failed verification:\n" + cap(recentFailure, 7000) : "",
|
|
2992
4155
|
fastBoundedLane
|
|
2993
|
-
? "\nFAST bounded rule: stay on the named file
|
|
4156
|
+
? "\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
4157
|
: "",
|
|
2995
4158
|
"\nImplement the smallest coherent change. Do not push, publish, deploy, rewrite history, or broaden scope without evidence.",
|
|
2996
4159
|
].filter(Boolean).join("\n");
|
|
@@ -3008,6 +4171,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
3008
4171
|
continue;
|
|
3009
4172
|
}
|
|
3010
4173
|
|
|
4174
|
+
const inheritedDirtyCheck = detectInheritedDirtyViolations(
|
|
4175
|
+
cwd,
|
|
4176
|
+
executionContract.inheritedDirty,
|
|
4177
|
+
executionContract.approvedInheritedDirtyPaths || [],
|
|
4178
|
+
);
|
|
4179
|
+
if (!inheritedDirtyCheck.safe) {
|
|
4180
|
+
await recordRuntimeOutcome(implementation, params.task, false, attempt - 1);
|
|
4181
|
+
return {
|
|
4182
|
+
content: [{
|
|
4183
|
+
type: "text",
|
|
4184
|
+
text: [
|
|
4185
|
+
"UES inherited dirty-work guard stopped completion.",
|
|
4186
|
+
"The implementation changed pre-existing dirty source work that was not explicitly authorized by the original task.",
|
|
4187
|
+
"UES will not auto-restore or overwrite these files.",
|
|
4188
|
+
"",
|
|
4189
|
+
...inheritedDirtyCheck.violations.map((item: any) => "- " + item.path),
|
|
4190
|
+
].join("\n"),
|
|
4191
|
+
}],
|
|
4192
|
+
details: {
|
|
4193
|
+
mode: "execute",
|
|
4194
|
+
policy,
|
|
4195
|
+
steps,
|
|
4196
|
+
attempts: attempt,
|
|
4197
|
+
executionContract,
|
|
4198
|
+
inheritedDirtyCheck,
|
|
4199
|
+
traceID,
|
|
4200
|
+
},
|
|
4201
|
+
isError: true,
|
|
4202
|
+
};
|
|
4203
|
+
}
|
|
4204
|
+
|
|
3011
4205
|
let verification: RunResult;
|
|
3012
4206
|
let fastGate: any = null;
|
|
3013
4207
|
if (fastBoundedLane) {
|
|
@@ -3029,7 +4223,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
3029
4223
|
optimizations: { fastDeterministicVerification: true, behavioralReceiptCount: fastGate.behavioralReceipts.length },
|
|
3030
4224
|
};
|
|
3031
4225
|
steps.push(verification);
|
|
3032
|
-
onUpdate?.({
|
|
4226
|
+
onUpdate?.({
|
|
4227
|
+
content: [{ type: "text", text: "UES Turbo Fast Path: reused " + fastGate.behavioralReceipts.length + " fresh behavioral receipt(s); skipped verifier model turn" }],
|
|
4228
|
+
details: { mode: "execute", phase: "turbo-fast-verified", policy, fastGate, fastDecision, traceID },
|
|
4229
|
+
});
|
|
3033
4230
|
} else {
|
|
3034
4231
|
verification = await run(
|
|
3035
4232
|
"ues-verifier",
|
|
@@ -3125,7 +4322,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3125
4322
|
requireVisual: visualEvidenceNeeded(params.task),
|
|
3126
4323
|
workspaceSnapshot: completionSnapshot,
|
|
3127
4324
|
behavioralReceipts: freshReceipts,
|
|
3128
|
-
requireBehavioralReceipt:
|
|
4325
|
+
requireBehavioralReceipt: policy.requireBehavioralReceipt !== false,
|
|
3129
4326
|
});
|
|
3130
4327
|
if (!completionAudit.passed) {
|
|
3131
4328
|
recentFailure = "Completion auditor rejected PASS: " + completionAudit.failures.join(", ");
|
|
@@ -3138,6 +4335,52 @@ export default function (pi: ExtensionAPI) {
|
|
|
3138
4335
|
};
|
|
3139
4336
|
}
|
|
3140
4337
|
|
|
4338
|
+
const verdictMatrix = buildFinalVerdictMatrix(params.task, {
|
|
4339
|
+
contract: executionContract,
|
|
4340
|
+
primaryPass: true,
|
|
4341
|
+
integrationPass: integrationResult ? true : undefined,
|
|
4342
|
+
primaryOutput: verification.output,
|
|
4343
|
+
integrationOutput: integrationResult?.output || "",
|
|
4344
|
+
visualOutput: visualResult?.output || "",
|
|
4345
|
+
primaryChecks: verification.report?.sections?.["checks-run"] || "",
|
|
4346
|
+
integrationChecks: integrationResult?.report?.sections?.["checks-run"] || "",
|
|
4347
|
+
visualChecks: visualResult?.report?.sections?.["checks-run"] || "",
|
|
4348
|
+
});
|
|
4349
|
+
|
|
4350
|
+
if (verdictMatrix.final !== "PASS") {
|
|
4351
|
+
await recordRuntimeOutcome(implementation, params.task, false, attempt - 1);
|
|
4352
|
+
const deviceOnlyPending =
|
|
4353
|
+
verdictMatrix.final === "SOURCE_RUNTIME_PASS_DEVICE_NOT_VERIFIED";
|
|
4354
|
+
return {
|
|
4355
|
+
content: [{
|
|
4356
|
+
type: "text",
|
|
4357
|
+
text: [
|
|
4358
|
+
deviceOnlyPending
|
|
4359
|
+
? "UES source/runtime verification passed, but requested real-device verification is still pending."
|
|
4360
|
+
: "UES deterministic final gate is not fully verified.",
|
|
4361
|
+
"",
|
|
4362
|
+
verdictMatrix.source,
|
|
4363
|
+
verdictMatrix.runtime,
|
|
4364
|
+
verdictMatrix.dbClean,
|
|
4365
|
+
verdictMatrix.device,
|
|
4366
|
+
"",
|
|
4367
|
+
integrationResult?.output || verification.output,
|
|
4368
|
+
].join("\n"),
|
|
4369
|
+
}],
|
|
4370
|
+
details: {
|
|
4371
|
+
mode: "execute",
|
|
4372
|
+
policy,
|
|
4373
|
+
steps,
|
|
4374
|
+
attempts: attempt,
|
|
4375
|
+
completionAudit,
|
|
4376
|
+
verdictMatrix,
|
|
4377
|
+
executionContract,
|
|
4378
|
+
traceID,
|
|
4379
|
+
},
|
|
4380
|
+
isError: !deviceOnlyPending,
|
|
4381
|
+
};
|
|
4382
|
+
}
|
|
4383
|
+
|
|
3141
4384
|
await recordRuntimeOutcome(implementation, params.task, true, attempt - 1);
|
|
3142
4385
|
const memory = await rememberVerifiedTask(cwd, params.task, verification, integrationResult);
|
|
3143
4386
|
const final = steps.at(-1);
|
|
@@ -3147,11 +4390,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
3147
4390
|
text: [
|
|
3148
4391
|
`UES execution PASS after ${attempt} attempt(s).`,
|
|
3149
4392
|
`Policy: ${policy.executionProfile}/${policy.risk}; model tier: ${implementation.modelTier || "default"}.`,
|
|
4393
|
+
verdictMatrix.source,
|
|
4394
|
+
verdictMatrix.runtime,
|
|
4395
|
+
verdictMatrix.dbClean,
|
|
4396
|
+
verdictMatrix.device,
|
|
3150
4397
|
"",
|
|
3151
4398
|
final?.output || verification.output,
|
|
3152
4399
|
].join("\n"),
|
|
3153
4400
|
}],
|
|
3154
|
-
details: { mode: "execute", policy, steps, attempts: attempt, memory, completionAudit, traceID },
|
|
4401
|
+
details: { mode: "execute", policy, steps, attempts: attempt, memory, completionAudit, verdictMatrix, executionContract, traceID },
|
|
3155
4402
|
};
|
|
3156
4403
|
}
|
|
3157
4404
|
|
|
@@ -3164,6 +4411,355 @@ export default function (pi: ExtensionAPI) {
|
|
|
3164
4411
|
isError: true,
|
|
3165
4412
|
};
|
|
3166
4413
|
},
|
|
4414
|
+
};
|
|
4415
|
+
pi.registerTool(uesExecuteTool);
|
|
4416
|
+
|
|
4417
|
+
// Extension commands are resolved before prompt templates in Pi. Registering
|
|
4418
|
+
// /ues-run here makes controller admission deterministic: weak models never
|
|
4419
|
+
// have to remember to call ues_execute themselves.
|
|
4420
|
+
pi.registerCommand("ues-status", {
|
|
4421
|
+
description: "Show the loaded UES runtime version and current session policy basics",
|
|
4422
|
+
handler: async (_args, ctx) => {
|
|
4423
|
+
const status = [
|
|
4424
|
+
"UES runtime: " + PACKAGE_VERSION,
|
|
4425
|
+
"Package root: " + PACKAGE_ROOT,
|
|
4426
|
+
"Child runtime: " + CHILD_RUNTIME,
|
|
4427
|
+
"Adaptive context: " + (ADAPTIVE_CONTEXT_ENABLED ? "on" : "off"),
|
|
4428
|
+
"Micro skills: " + (MICRO_SKILLS_ENABLED ? "on" : "off"),
|
|
4429
|
+
"Turbo Fast Path: on",
|
|
4430
|
+
"Parent UES tools hidden outside UES runs: on",
|
|
4431
|
+
"Zero-friction engineering admission: " + (AUTO_ADMISSION_ENABLED ? "on (native / auto / high-risk + safe continuation)" : "off"),
|
|
4432
|
+
"Git-root artifact guard: on",
|
|
4433
|
+
"Inherited dirty-work guard: on",
|
|
4434
|
+
"Local .env mutation guard: on",
|
|
4435
|
+
"Portable temp-path guard: on",
|
|
4436
|
+
"Explicit phase barriers: on",
|
|
4437
|
+
"Independent final verdict matrix: on",
|
|
4438
|
+
"Disk hygiene: bounded + auto-clean",
|
|
4439
|
+
"Writer concurrency: " + MAX_WRITER_CONCURRENCY,
|
|
4440
|
+
].join("\n");
|
|
4441
|
+
pi.sendMessage({
|
|
4442
|
+
customType: "ues-runtime-status",
|
|
4443
|
+
content: status,
|
|
4444
|
+
display: true,
|
|
4445
|
+
details: { version: PACKAGE_VERSION, packageRoot: PACKAGE_ROOT, childRuntime: CHILD_RUNTIME },
|
|
4446
|
+
}, { triggerTurn: false });
|
|
4447
|
+
try { ctx.ui.notify("UES runtime " + PACKAGE_VERSION + " loaded", "info"); } catch {}
|
|
4448
|
+
},
|
|
4449
|
+
});
|
|
4450
|
+
|
|
4451
|
+
pi.registerCommand("ues-clean", {
|
|
4452
|
+
description: "Safely remove stale UES task sandboxes and orphan metadata for the current repository",
|
|
4453
|
+
handler: async (_args, ctx) => {
|
|
4454
|
+
if (uesModeActive()) {
|
|
4455
|
+
try { ctx.ui.notify("UES cleanup refused while a UES task is active", "warning"); } catch {}
|
|
4456
|
+
return;
|
|
4457
|
+
}
|
|
4458
|
+
let workspaceRoot: string;
|
|
4459
|
+
try {
|
|
4460
|
+
workspaceRoot = requireGitWorkspaceRoot(ctx.cwd, "/ues-clean");
|
|
4461
|
+
} catch (error) {
|
|
4462
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4463
|
+
pi.sendMessage({
|
|
4464
|
+
customType: "ues-cleanup-result",
|
|
4465
|
+
content: "UES cleanup refused unsafe workspace.\n" + message,
|
|
4466
|
+
display: true,
|
|
4467
|
+
details: { error: message },
|
|
4468
|
+
}, { triggerTurn: false });
|
|
4469
|
+
try { ctx.ui.notify(message, "error"); } catch {}
|
|
4470
|
+
return;
|
|
4471
|
+
}
|
|
4472
|
+
const cleanup = await pruneOrphanTaskSandboxes(workspaceRoot, {
|
|
4473
|
+
minAgeMs: 5 * 60_000,
|
|
4474
|
+
legacyMinAgeMs: 30 * 60_000,
|
|
4475
|
+
ownedMinAgeMs: 0,
|
|
4476
|
+
reclaimOwnerPid: process.pid,
|
|
4477
|
+
protectedDirs: [...ACTIVE_TASK_SANDBOXES.keys()],
|
|
4478
|
+
}).catch((error) => ({
|
|
4479
|
+
removed: [],
|
|
4480
|
+
skipped: [],
|
|
4481
|
+
sidecarsRemoved: [],
|
|
4482
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4483
|
+
}));
|
|
4484
|
+
const removed = Number(cleanup?.removed?.length || 0);
|
|
4485
|
+
const sidecars = Number(cleanup?.sidecarsRemoved?.length || 0);
|
|
4486
|
+
const skipped = Number(cleanup?.skipped?.length || 0);
|
|
4487
|
+
await stopAllServices(workspaceRoot).catch(() => []);
|
|
4488
|
+
const transientDirs = [".ues-traces", ".ues-services", ".ues-dashboard"];
|
|
4489
|
+
const removedRuntimeDirs: string[] = [];
|
|
4490
|
+
for (const name of transientDirs) {
|
|
4491
|
+
const target = path.join(workspaceRoot, name);
|
|
4492
|
+
if (!fs.existsSync(target)) continue;
|
|
4493
|
+
await fs.promises.rm(target, { recursive: true, force: true }).catch(() => {});
|
|
4494
|
+
if (!fs.existsSync(target)) removedRuntimeDirs.push(name);
|
|
4495
|
+
}
|
|
4496
|
+
|
|
4497
|
+
const cacheDir = path.join(workspaceRoot, ".ues-cache");
|
|
4498
|
+
const removedCacheEntries: string[] = [];
|
|
4499
|
+
if (fs.existsSync(cacheDir)) {
|
|
4500
|
+
const entries = await fs.promises.readdir(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
4501
|
+
for (const entry of entries) {
|
|
4502
|
+
if (entry.name === "evidence-v1") continue;
|
|
4503
|
+
if (
|
|
4504
|
+
entry.name === "semantic-index-v1.json" ||
|
|
4505
|
+
entry.name.startsWith("semantic-index-v1.json.") ||
|
|
4506
|
+
entry.name === "verification-broker-v1.json" ||
|
|
4507
|
+
entry.name.startsWith("verification-broker-v1.json.")
|
|
4508
|
+
) {
|
|
4509
|
+
await fs.promises.rm(path.join(cacheDir, entry.name), { recursive: true, force: true }).catch(() => {});
|
|
4510
|
+
removedCacheEntries.push(entry.name);
|
|
4511
|
+
}
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
const evidenceGc = await gcEvidenceStore(workspaceRoot, {
|
|
4515
|
+
maxBytes: 96 * 1024 * 1024,
|
|
4516
|
+
maxEntries: 600,
|
|
4517
|
+
maxAgeDays: 14,
|
|
4518
|
+
}).catch((error) => ({
|
|
4519
|
+
removed: [],
|
|
4520
|
+
removedCount: 0,
|
|
4521
|
+
protectedEntries: 0,
|
|
4522
|
+
protectedBytes: 0,
|
|
4523
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4524
|
+
}));
|
|
4525
|
+
|
|
4526
|
+
if (fs.existsSync(cacheDir)) {
|
|
4527
|
+
const remaining = await fs.promises.readdir(cacheDir).catch(() => ["unknown"]);
|
|
4528
|
+
if (remaining.length === 0) await fs.promises.rm(cacheDir, { recursive: true, force: true }).catch(() => {});
|
|
4529
|
+
}
|
|
4530
|
+
|
|
4531
|
+
const text = [
|
|
4532
|
+
"UES cleanup complete.",
|
|
4533
|
+
"Removed sandboxes: " + removed,
|
|
4534
|
+
"Removed orphan metadata: " + sidecars,
|
|
4535
|
+
"Removed transient runtime dirs: " + (removedRuntimeDirs.join(", ") || "none"),
|
|
4536
|
+
"Removed rebuildable cache entries: " + (removedCacheEntries.length || 0),
|
|
4537
|
+
"Pruned evidence blobs: " + Number(evidenceGc?.removedCount || 0),
|
|
4538
|
+
"Protected verified-memory evidence: " + Number(evidenceGc?.protectedEntries || 0),
|
|
4539
|
+
"Protected/recent entries kept: " + skipped,
|
|
4540
|
+
"Preserved durable state: .ues-work, .ues-memory, .ues-learning, .ues-evals and verified-memory evidence",
|
|
4541
|
+
cleanup?.baseRemoved ? "Sandbox base directory removed because it is empty." : "",
|
|
4542
|
+
cleanup?.error ? "Error: " + cleanup.error : "",
|
|
4543
|
+
].filter(Boolean).join("\n");
|
|
4544
|
+
pi.sendMessage({
|
|
4545
|
+
customType: "ues-cleanup-result",
|
|
4546
|
+
content: text,
|
|
4547
|
+
display: true,
|
|
4548
|
+
details: { ...cleanup, removedRuntimeDirs, removedCacheEntries, evidenceGc },
|
|
4549
|
+
}, { triggerTurn: false });
|
|
4550
|
+
try {
|
|
4551
|
+
ctx.ui.notify(
|
|
4552
|
+
"UES cleanup removed/pruned " +
|
|
4553
|
+
(removed + sidecars + removedRuntimeDirs.length + removedCacheEntries.length + Number(evidenceGc?.removedCount || 0)) +
|
|
4554
|
+
" stale/transient artifact(s)",
|
|
4555
|
+
"info",
|
|
4556
|
+
);
|
|
4557
|
+
} catch {}
|
|
4558
|
+
},
|
|
4559
|
+
});
|
|
4560
|
+
|
|
4561
|
+
directControllerRunner = async (task, ctx, admission, admissionDecision) => {
|
|
4562
|
+
if (directControllerAbort && !directControllerAbort.signal.aborted) {
|
|
4563
|
+
try { ctx.ui.notify("UES controller is already running in this session", "warning"); } catch {}
|
|
4564
|
+
return;
|
|
4565
|
+
}
|
|
4566
|
+
|
|
4567
|
+
let workspaceRoot: string;
|
|
4568
|
+
try {
|
|
4569
|
+
workspaceRoot = requireGitWorkspaceRoot(ctx.cwd, admission === "automatic" ? "automatic UES admission" : "/ues-run");
|
|
4570
|
+
} catch (error) {
|
|
4571
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4572
|
+
pi.sendMessage({
|
|
4573
|
+
customType: "ues-controller-result",
|
|
4574
|
+
content: message,
|
|
4575
|
+
display: true,
|
|
4576
|
+
details: { controllerUsed: false, controllerPass: false, reason: "unsafe-workspace-root" },
|
|
4577
|
+
}, { triggerTurn: false });
|
|
4578
|
+
try { ctx.ui.notify(message, "error"); } catch {}
|
|
4579
|
+
return;
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4582
|
+
const abort = new AbortController();
|
|
4583
|
+
directControllerAbort = abort;
|
|
4584
|
+
const directTraceID = createTraceID(admission === "automatic" ? "ues-auto" : "ues-run");
|
|
4585
|
+
syncSessionIdentity(uesSessionName("run", task, workspaceRoot), ctx);
|
|
4586
|
+
let result: any;
|
|
4587
|
+
let lastProgressNoticeAt = 0;
|
|
4588
|
+
let lastProgressKey = "";
|
|
4589
|
+
try {
|
|
4590
|
+
const directPolicy =
|
|
4591
|
+
admissionDecision?.policy && typeof admissionDecision.policy === "object"
|
|
4592
|
+
? admissionDecision.policy
|
|
4593
|
+
: classifyEngineeringTask(task);
|
|
4594
|
+
const routeLabel =
|
|
4595
|
+
admission === "automatic"
|
|
4596
|
+
? admissionDecision?.route === "guarded"
|
|
4597
|
+
? "high-risk auto controller started ("
|
|
4598
|
+
: "auto controller started ("
|
|
4599
|
+
: "controller started (";
|
|
4600
|
+
try {
|
|
4601
|
+
ctx.ui.notify(
|
|
4602
|
+
"UES " + PACKAGE_VERSION + ": " + routeLabel +
|
|
4603
|
+
String(directPolicy.executionProfile || directPolicy.mode || "unknown") +
|
|
4604
|
+
"/" + String(directPolicy.risk || "unknown") + ")",
|
|
4605
|
+
admissionDecision?.route === "guarded" ? "warning" : "info",
|
|
4606
|
+
);
|
|
4607
|
+
} catch {}
|
|
4608
|
+
result = await uesExecuteTool.execute(
|
|
4609
|
+
`ues-run-${randomUUID()}`,
|
|
4610
|
+
{ task, cwd: workspaceRoot, __traceID: directTraceID, __taskPolicy: directPolicy },
|
|
4611
|
+
abort.signal,
|
|
4612
|
+
(update: any) => {
|
|
4613
|
+
const text = (update?.content || [])
|
|
4614
|
+
.filter((part: any) => part?.type === "text" && typeof part.text === "string")
|
|
4615
|
+
.map((part: any) => part.text)
|
|
4616
|
+
.join("\n")
|
|
4617
|
+
.trim();
|
|
4618
|
+
if (text) {
|
|
4619
|
+
try { ctx.ui.setStatus("ues-run", cap(text, 180)); } catch {}
|
|
4620
|
+
}
|
|
4621
|
+
|
|
4622
|
+
const progress = update?.details?.progress;
|
|
4623
|
+
if (progress) {
|
|
4624
|
+
const progressKey = String(progress.agent || "") + ":" + String(update?.details?.phase || "");
|
|
4625
|
+
const now = Date.now();
|
|
4626
|
+
const phaseChanged = progressKey && progressKey !== lastProgressKey;
|
|
4627
|
+
const heartbeatDue = now - lastProgressNoticeAt >= 60_000;
|
|
4628
|
+
if (phaseChanged || heartbeatDue) {
|
|
4629
|
+
const message =
|
|
4630
|
+
"UES: " + String(progress.agent || "worker") +
|
|
4631
|
+
" running " + Math.round(Number(progress.elapsedMs || 0) / 1000) + "s" +
|
|
4632
|
+
" (idle " + Math.round(Number(progress.idleMs || 0) / 1000) + "s, tools " +
|
|
4633
|
+
Number(progress.toolCalls || 0) +
|
|
4634
|
+
(progress.activeTool ? ", " + String(progress.activeTool) : "") + ")";
|
|
4635
|
+
try {
|
|
4636
|
+
ctx.ui.notify(
|
|
4637
|
+
message,
|
|
4638
|
+
Number(progress.idleMs || 0) >= 45_000 ? "warning" : "info",
|
|
4639
|
+
);
|
|
4640
|
+
} catch {}
|
|
4641
|
+
lastProgressNoticeAt = now;
|
|
4642
|
+
lastProgressKey = progressKey;
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4645
|
+
|
|
4646
|
+
if (process.env.UES_EVAL_DIRECT_TELEMETRY === "1") {
|
|
4647
|
+
process.stderr.write(JSON.stringify({
|
|
4648
|
+
type: "ues_controller_progress",
|
|
4649
|
+
phase: update?.details?.phase || null,
|
|
4650
|
+
task: update?.details?.task || null,
|
|
4651
|
+
agent: progress?.agent || null,
|
|
4652
|
+
elapsedMs: Number(progress?.elapsedMs || 0),
|
|
4653
|
+
idleMs: Number(progress?.idleMs || 0),
|
|
4654
|
+
toolCalls: Number(progress?.toolCalls || 0),
|
|
4655
|
+
activeTool: progress?.activeTool || null,
|
|
4656
|
+
note: progress?.note || null,
|
|
4657
|
+
text: text ? cap(text, 240) : null,
|
|
4658
|
+
}) + "\n");
|
|
4659
|
+
}
|
|
4660
|
+
},
|
|
4661
|
+
ctx,
|
|
4662
|
+
);
|
|
4663
|
+
} catch (error) {
|
|
4664
|
+
result = {
|
|
4665
|
+
content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
|
|
4666
|
+
details: { mode: "execute", reason: "direct-controller-exception" },
|
|
4667
|
+
isError: true,
|
|
4668
|
+
};
|
|
4669
|
+
} finally {
|
|
4670
|
+
const traceRemoved = await cleanupTraceSandboxes(workspaceRoot, directTraceID).catch(() => 0);
|
|
4671
|
+
const staleCleanup = await pruneOrphanTaskSandboxes(workspaceRoot, {
|
|
4672
|
+
minAgeMs: 5 * 60_000,
|
|
4673
|
+
legacyMinAgeMs: 30 * 60_000,
|
|
4674
|
+
ownedMinAgeMs: 0,
|
|
4675
|
+
reclaimOwnerPid: process.pid,
|
|
4676
|
+
protectedDirs: [...ACTIVE_TASK_SANDBOXES.keys()],
|
|
4677
|
+
}).catch(() => ({ removed: [], skipped: [], sidecarsRemoved: [] }));
|
|
4678
|
+
const cleaned =
|
|
4679
|
+
Number(traceRemoved || 0) +
|
|
4680
|
+
Number(staleCleanup?.removed?.length || 0) +
|
|
4681
|
+
Number(staleCleanup?.sidecarsRemoved?.length || 0);
|
|
4682
|
+
if (cleaned > 0) {
|
|
4683
|
+
try { ctx.ui.notify("UES cleanup: removed " + cleaned + " stale sandbox artifact(s)", "info"); } catch {}
|
|
4684
|
+
}
|
|
4685
|
+
if (directControllerAbort === abort) directControllerAbort = null;
|
|
4686
|
+
try { ctx.ui.setStatus("ues-run", undefined); } catch {}
|
|
4687
|
+
}
|
|
4688
|
+
|
|
4689
|
+
const content = (result?.content || [])
|
|
4690
|
+
.filter((part: any) => part?.type === "text" && typeof part.text === "string")
|
|
4691
|
+
.map((part: any) => part.text)
|
|
4692
|
+
.join("\n")
|
|
4693
|
+
.trim() || "(UES controller returned no text)";
|
|
4694
|
+
const controllerPass = result?.isError !== true;
|
|
4695
|
+
|
|
4696
|
+
if (process.env.UES_EVAL_DIRECT_TELEMETRY === "1") {
|
|
4697
|
+
const details = result?.details || null;
|
|
4698
|
+
const telemetryDetails = details
|
|
4699
|
+
? {
|
|
4700
|
+
mode: details.mode,
|
|
4701
|
+
attempts: details.attempts,
|
|
4702
|
+
policy: details.policy
|
|
4703
|
+
? {
|
|
4704
|
+
mode: details.policy.mode,
|
|
4705
|
+
executionProfile: details.policy.executionProfile,
|
|
4706
|
+
risk: details.policy.risk,
|
|
4707
|
+
}
|
|
4708
|
+
: null,
|
|
4709
|
+
steps: Array.isArray(details.steps)
|
|
4710
|
+
? details.steps.map((step: any) => ({
|
|
4711
|
+
agent: step?.agent,
|
|
4712
|
+
exitCode: step?.exitCode,
|
|
4713
|
+
optimizations: step?.optimizations || null,
|
|
4714
|
+
usage: step?.usage || null,
|
|
4715
|
+
toolCalls: Number(step?.toolCalls || 0),
|
|
4716
|
+
toolNames: Array.isArray(step?.toolNames) ? step.toolNames : [],
|
|
4717
|
+
}))
|
|
4718
|
+
: [],
|
|
4719
|
+
}
|
|
4720
|
+
: null;
|
|
4721
|
+
process.stderr.write(JSON.stringify({
|
|
4722
|
+
type: "ues_controller_direct",
|
|
4723
|
+
controllerUsed: true,
|
|
4724
|
+
controllerPass,
|
|
4725
|
+
details: telemetryDetails,
|
|
4726
|
+
}) + "\n");
|
|
4727
|
+
}
|
|
4728
|
+
|
|
4729
|
+
pi.sendMessage({
|
|
4730
|
+
customType: "ues-controller-result",
|
|
4731
|
+
content,
|
|
4732
|
+
display: true,
|
|
4733
|
+
details: {
|
|
4734
|
+
controllerUsed: true,
|
|
4735
|
+
controllerPass,
|
|
4736
|
+
admission,
|
|
4737
|
+
admissionRoute: admissionDecision?.route || (admission === "command" ? "command" : "auto"),
|
|
4738
|
+
admissionConfidence: admissionDecision?.confidence || null,
|
|
4739
|
+
admissionReason: admissionDecision?.reason || null,
|
|
4740
|
+
...(result?.details || {}),
|
|
4741
|
+
},
|
|
4742
|
+
});
|
|
4743
|
+
|
|
4744
|
+
try {
|
|
4745
|
+
ctx.ui.notify(
|
|
4746
|
+
controllerPass ? "UES " + PACKAGE_VERSION + ": verified controller run completed" : "UES " + PACKAGE_VERSION + ": controller run failed verification",
|
|
4747
|
+
controllerPass ? "info" : "error",
|
|
4748
|
+
);
|
|
4749
|
+
} catch {}
|
|
4750
|
+
|
|
4751
|
+
};
|
|
4752
|
+
|
|
4753
|
+
pi.registerCommand("ues-run", {
|
|
4754
|
+
description: "Run an engineering task directly through the deterministic UES controller",
|
|
4755
|
+
handler: async (args, ctx) => {
|
|
4756
|
+
const task = String(args || "").trim();
|
|
4757
|
+
if (!task) {
|
|
4758
|
+
try { ctx.ui.notify("Usage: /ues-run <engineering task>", "warning"); } catch {}
|
|
4759
|
+
return;
|
|
4760
|
+
}
|
|
4761
|
+
await directControllerRunner?.(task, ctx, "command");
|
|
4762
|
+
},
|
|
3167
4763
|
});
|
|
3168
4764
|
|
|
3169
4765
|
pi.registerTool({
|
|
@@ -3293,7 +4889,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
3293
4889
|
}
|
|
3294
4890
|
|
|
3295
4891
|
let completed = 0;
|
|
3296
|
-
const
|
|
4892
|
+
const dispatchHasWriters = tasks.some((item) => WRITE_AGENTS.has(item.agent));
|
|
4893
|
+
const dispatchConcurrency = dispatchHasWriters
|
|
4894
|
+
? Math.min(MAX_CONCURRENCY, MAX_WRITER_CONCURRENCY)
|
|
4895
|
+
: MAX_CONCURRENCY;
|
|
4896
|
+
const results = await mapLimit(tasks, dispatchConcurrency, async (item) => {
|
|
3297
4897
|
const result = await runRoutedAgent(
|
|
3298
4898
|
item.agent as AgentName,
|
|
3299
4899
|
item.task,
|