totemlayer 0.1.5 → 0.1.11
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 +26 -44
- package/bin/agent-profile.mjs +18 -2
- package/bin/agent-runner.mjs +20 -10
- package/bin/agent-setup.mjs +141 -55
- package/bin/automatic-runtime.mjs +131 -0
- package/bin/cloud-bootstrap.mjs +64 -0
- package/bin/collaboration-listener.mjs +83 -83
- package/bin/connected-agent-service.mjs +84 -0
- package/bin/device-login.mjs +168 -0
- package/bin/docker-test-sandbox.mjs +1 -1
- package/bin/installation.mjs +160 -0
- package/bin/mcp-install.mjs +74 -18
- package/bin/mcp.mjs +56 -1
- package/bin/mission-agent-service.mjs +40 -2
- package/bin/mission-infrastructure.mjs +609 -0
- package/bin/mission-worker.mjs +259 -49
- package/bin/model-adapter.mjs +164 -0
- package/bin/network-runtime.mjs +58 -3
- package/bin/platform-client.mjs +3 -0
- package/bin/provider-detection.mjs +94 -0
- package/bin/runtime-installation.mjs +305 -0
- package/bin/sdk/index.js +5 -0
- package/bin/totem.mjs +13 -3
- package/bin/worker-core/adapters.mjs +279 -0
- package/bin/worker-core/errors.mjs +60 -0
- package/package.json +2 -1
- package/scripts/prepare.mjs +5 -0
package/bin/mission-worker.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { runInfrastructureWorkerCycle } from "./mission-infrastructure.mjs";
|
|
1
2
|
import { spawn } from "node:child_process";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
import {
|
|
@@ -20,6 +21,7 @@ import { setTimeout as sleep } from "node:timers/promises";
|
|
|
20
21
|
import { inspectLocalRepository } from "./repository.mjs";
|
|
21
22
|
import { localClientEnvironment } from "./platform-client.mjs";
|
|
22
23
|
import { localModelEndpoint } from "./local-model-provider.mjs";
|
|
24
|
+
import { externalModelReply, isExternalModelProvider } from "./model-adapter.mjs";
|
|
23
25
|
import { runDockerNodeTests } from "./docker-test-sandbox.mjs";
|
|
24
26
|
import { acquireMissionAgentSession, assertMissionAgentSession } from "./mission-agent-session.mjs";
|
|
25
27
|
|
|
@@ -52,7 +54,10 @@ const SOURCE_CHANGE = {
|
|
|
52
54
|
...FILE_CHANGE,
|
|
53
55
|
properties: {
|
|
54
56
|
...FILE_CHANGE.properties,
|
|
55
|
-
path: {
|
|
57
|
+
path: {
|
|
58
|
+
...CODE_PATH,
|
|
59
|
+
pattern: CODE_PATH.pattern.replace("^(src|test|tests)/", "^src/"),
|
|
60
|
+
},
|
|
56
61
|
},
|
|
57
62
|
};
|
|
58
63
|
const TEST_CHANGE = {
|
|
@@ -98,7 +103,10 @@ const OUTPUT_SCHEMAS = {
|
|
|
98
103
|
},
|
|
99
104
|
engineering: {
|
|
100
105
|
summary: TEXT,
|
|
101
|
-
verdict: {
|
|
106
|
+
verdict: {
|
|
107
|
+
type: "string",
|
|
108
|
+
enum: ["feasible", "changes_required", "rejected"],
|
|
109
|
+
},
|
|
102
110
|
findings: STRINGS,
|
|
103
111
|
spec: TEXT,
|
|
104
112
|
candidateTitle: TEXT,
|
|
@@ -113,11 +121,19 @@ const OUTPUT_SCHEMAS = {
|
|
|
113
121
|
code: {
|
|
114
122
|
summary: TEXT,
|
|
115
123
|
changes: { type: "array", items: SOURCE_CHANGE, minItems: 1, maxItems: 3 },
|
|
116
|
-
testChanges: {
|
|
124
|
+
testChanges: {
|
|
125
|
+
type: "array",
|
|
126
|
+
items: TEST_CHANGE,
|
|
127
|
+
minItems: 1,
|
|
128
|
+
maxItems: 1,
|
|
129
|
+
},
|
|
117
130
|
},
|
|
118
131
|
review: {
|
|
119
132
|
summary: TEXT,
|
|
120
|
-
verdict: {
|
|
133
|
+
verdict: {
|
|
134
|
+
type: "string",
|
|
135
|
+
enum: ["approved", "changes_required", "rejected"],
|
|
136
|
+
},
|
|
121
137
|
findings: STRINGS,
|
|
122
138
|
probes: { type: "array", items: REVIEW_PROBE, maxItems: 3 },
|
|
123
139
|
},
|
|
@@ -189,6 +205,7 @@ function localOllamaBaseUrl(value = "http://127.0.0.1:11434") {
|
|
|
189
205
|
}
|
|
190
206
|
|
|
191
207
|
export function missionModelEndpoint(provider = "qwen-local", { ollamaUrl, qwenUrl, nimUrl } = {}) {
|
|
208
|
+
if (isExternalModelProvider(provider)) return null;
|
|
192
209
|
if (!["qwen-local", "ollama-local", "nim-local"].includes(provider))
|
|
193
210
|
throw new Error("Choose qwen-local, ollama-local, or nim-local.");
|
|
194
211
|
return localModelEndpoint(provider, { ollamaUrl, qwenUrl, nimUrl });
|
|
@@ -252,7 +269,12 @@ function rejectedResponseEvidence(response) {
|
|
|
252
269
|
} catch {
|
|
253
270
|
// Malformed text cannot be safely interpreted as credential-bearing fields.
|
|
254
271
|
// Preserve a fingerprint and length without storing its raw fragments.
|
|
255
|
-
return {
|
|
272
|
+
return {
|
|
273
|
+
...metadata,
|
|
274
|
+
response: null,
|
|
275
|
+
responseOmitted: "malformed_json",
|
|
276
|
+
truncated: false,
|
|
277
|
+
};
|
|
256
278
|
}
|
|
257
279
|
}
|
|
258
280
|
|
|
@@ -449,7 +471,9 @@ function structuredEvent(stage, response) {
|
|
|
449
471
|
outcome: output.outcome.slice(0, 4000),
|
|
450
472
|
selected: output.selected,
|
|
451
473
|
...(typeof output.selectedCandidateTitle === "string"
|
|
452
|
-
? {
|
|
474
|
+
? {
|
|
475
|
+
selectedCandidateTitle: output.selectedCandidateTitle.slice(0, 160),
|
|
476
|
+
}
|
|
453
477
|
: {}),
|
|
454
478
|
...(typeof output.rationale === "string"
|
|
455
479
|
? { rationale: output.rationale.slice(0, 4000) }
|
|
@@ -461,7 +485,10 @@ function structuredEvent(stage, response) {
|
|
|
461
485
|
break;
|
|
462
486
|
return {
|
|
463
487
|
type: "completion",
|
|
464
|
-
payload: {
|
|
488
|
+
payload: {
|
|
489
|
+
summary: output.summary.slice(0, 4000),
|
|
490
|
+
result: output.result,
|
|
491
|
+
},
|
|
465
492
|
};
|
|
466
493
|
}
|
|
467
494
|
throw new Error("The agent returned a malformed stage result.");
|
|
@@ -498,6 +525,23 @@ export function verifiedHandoff(mission, events, response) {
|
|
|
498
525
|
} catch {
|
|
499
526
|
/* no structured review */
|
|
500
527
|
}
|
|
528
|
+
const infrastructure = artifacts.filter((artifact) => artifact.kind === "infrastructure");
|
|
529
|
+
const localDeployment = [...infrastructure]
|
|
530
|
+
.reverse()
|
|
531
|
+
.find(
|
|
532
|
+
(artifact) =>
|
|
533
|
+
artifact.metadata?.receipt?.provider === "local-static" &&
|
|
534
|
+
!artifact.metadata.receipt.deploymentEffectId,
|
|
535
|
+
);
|
|
536
|
+
const localVerification = [...infrastructure]
|
|
537
|
+
.reverse()
|
|
538
|
+
.find(
|
|
539
|
+
(artifact) =>
|
|
540
|
+
artifact.metadata?.receipt?.deploymentEffectId ===
|
|
541
|
+
localDeployment?.metadata?.receipt?.operationId,
|
|
542
|
+
);
|
|
543
|
+
if (mission.infrastructure && !localVerification && model.result === "completed")
|
|
544
|
+
throw new Error("Complete the infrastructure verification before a successful handoff.");
|
|
501
545
|
const branch = code?.metadata?.branch;
|
|
502
546
|
const headCommit = code?.metadata?.headCommit;
|
|
503
547
|
const changedFiles = Array.isArray(code?.metadata?.changedFiles)
|
|
@@ -532,7 +576,15 @@ export function verifiedHandoff(mission, events, response) {
|
|
|
532
576
|
agentReportedLimitations: Array.isArray(model.limitations) ? model.limitations : [],
|
|
533
577
|
pullRequestOpened: Boolean(code?.metadata?.pullRequestUrl),
|
|
534
578
|
pullRequestUrl: code?.metadata?.pullRequestUrl ?? null,
|
|
535
|
-
deployed:
|
|
579
|
+
deployed: Boolean(localVerification),
|
|
580
|
+
deploymentScope: localVerification ? "local" : null,
|
|
581
|
+
infrastructure: localDeployment
|
|
582
|
+
? {
|
|
583
|
+
deployment: localDeployment.metadata.receipt,
|
|
584
|
+
verification: localVerification?.metadata?.receipt ?? null,
|
|
585
|
+
availability: "Available on the deployment agent's computer while it is connected.",
|
|
586
|
+
}
|
|
587
|
+
: null,
|
|
536
588
|
},
|
|
537
589
|
};
|
|
538
590
|
}
|
|
@@ -540,13 +592,22 @@ export function verifiedHandoff(mission, events, response) {
|
|
|
540
592
|
export function runFile(
|
|
541
593
|
command,
|
|
542
594
|
args,
|
|
543
|
-
{
|
|
595
|
+
{
|
|
596
|
+
cwd,
|
|
597
|
+
input,
|
|
598
|
+
signal,
|
|
599
|
+
timeoutMs = 15 * 60_000,
|
|
600
|
+
env,
|
|
601
|
+
acceptNonZero = false,
|
|
602
|
+
platform = process.platform,
|
|
603
|
+
spawnProcess = spawn,
|
|
604
|
+
} = {},
|
|
544
605
|
) {
|
|
545
606
|
if (signal?.aborted)
|
|
546
607
|
return Promise.reject(signal.reason ?? new Error("The command was interrupted."));
|
|
547
608
|
return new Promise((resolveRun, rejectRun) => {
|
|
548
|
-
const grouped =
|
|
549
|
-
const child =
|
|
609
|
+
const grouped = platform !== "win32";
|
|
610
|
+
const child = spawnProcess(command, args, {
|
|
550
611
|
cwd,
|
|
551
612
|
env,
|
|
552
613
|
shell: false,
|
|
@@ -597,13 +658,25 @@ export function runFile(
|
|
|
597
658
|
if (!grouped) {
|
|
598
659
|
// Windows has no POSIX process groups; taskkill /T includes descendants.
|
|
599
660
|
await new Promise((resolveKill, rejectKill) => {
|
|
600
|
-
const killer =
|
|
661
|
+
const killer = spawnProcess("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
|
|
601
662
|
shell: false,
|
|
602
663
|
stdio: "ignore",
|
|
603
664
|
windowsHide: true,
|
|
604
665
|
});
|
|
605
|
-
|
|
606
|
-
|
|
666
|
+
const killTimer = setTimeout(() => {
|
|
667
|
+
killer.kill("SIGKILL");
|
|
668
|
+
rejectKill(new Error("Windows process-tree cleanup timed out."));
|
|
669
|
+
}, 5_000);
|
|
670
|
+
killer.once("error", (error) => {
|
|
671
|
+
clearTimeout(killTimer);
|
|
672
|
+
rejectKill(error);
|
|
673
|
+
});
|
|
674
|
+
killer.once("close", (code) => {
|
|
675
|
+
clearTimeout(killTimer);
|
|
676
|
+
if (code === 0) resolveKill();
|
|
677
|
+
else
|
|
678
|
+
rejectKill(new Error(`Windows process-tree cleanup exited with status ${code}.`));
|
|
679
|
+
});
|
|
607
680
|
});
|
|
608
681
|
return;
|
|
609
682
|
}
|
|
@@ -622,6 +695,8 @@ export function runFile(
|
|
|
622
695
|
// Give the kernel time to reap killed descendants before checkout cleanup.
|
|
623
696
|
for (let wait = 0; wait < 20 && groupExists(); wait += 1) await sleep(25);
|
|
624
697
|
})().catch((cleanupError) => {
|
|
698
|
+
// Report an uncertain cleanup failure even if the client never closes.
|
|
699
|
+
closed = true;
|
|
625
700
|
stopError = new Error(
|
|
626
701
|
`${command} was interrupted but process cleanup failed: ${cleanupError.message}`,
|
|
627
702
|
);
|
|
@@ -1021,7 +1096,15 @@ function testSourceTokens(source) {
|
|
|
1021
1096
|
}
|
|
1022
1097
|
if (char === "\\") {
|
|
1023
1098
|
const escaped = source[index++];
|
|
1024
|
-
const simple = {
|
|
1099
|
+
const simple = {
|
|
1100
|
+
n: "\n",
|
|
1101
|
+
r: "\r",
|
|
1102
|
+
t: "\t",
|
|
1103
|
+
b: "\b",
|
|
1104
|
+
f: "\f",
|
|
1105
|
+
v: "\v",
|
|
1106
|
+
0: "\0",
|
|
1107
|
+
};
|
|
1025
1108
|
if (escaped === "\n") continue;
|
|
1026
1109
|
if (escaped === "\r") {
|
|
1027
1110
|
if (source[index] === "\n") index += 1;
|
|
@@ -1294,7 +1377,9 @@ async function existingNodeTestTitles(checkout) {
|
|
|
1294
1377
|
let entriesSeen = 0;
|
|
1295
1378
|
let bytes = 0;
|
|
1296
1379
|
const visit = async (relative = "") => {
|
|
1297
|
-
for (const entry of await readdir(join(checkout, relative), {
|
|
1380
|
+
for (const entry of await readdir(join(checkout, relative), {
|
|
1381
|
+
withFileTypes: true,
|
|
1382
|
+
})) {
|
|
1298
1383
|
if ([".git", "node_modules"].includes(entry.name)) continue;
|
|
1299
1384
|
if (++entriesSeen > 5000)
|
|
1300
1385
|
throw new Error(
|
|
@@ -1483,6 +1568,7 @@ async function runStructuredMissionAgent({
|
|
|
1483
1568
|
repair = false,
|
|
1484
1569
|
reasoning = false,
|
|
1485
1570
|
rejectionEvidenceDirectory,
|
|
1571
|
+
adapterSettings = {},
|
|
1486
1572
|
}) {
|
|
1487
1573
|
const regressionFile =
|
|
1488
1574
|
stage === "code" && !repair ? await selectMissionRegressionTestFile(checkout) : null;
|
|
@@ -1498,7 +1584,10 @@ async function runStructuredMissionAgent({
|
|
|
1498
1584
|
...TEST_CHANGE,
|
|
1499
1585
|
properties: {
|
|
1500
1586
|
...TEST_CHANGE.properties,
|
|
1501
|
-
path: {
|
|
1587
|
+
path: {
|
|
1588
|
+
...TEST_CHANGE.properties.path,
|
|
1589
|
+
const: regressionFile.path,
|
|
1590
|
+
},
|
|
1502
1591
|
},
|
|
1503
1592
|
},
|
|
1504
1593
|
},
|
|
@@ -1511,7 +1600,26 @@ async function runStructuredMissionAgent({
|
|
|
1511
1600
|
required: Object.keys(properties),
|
|
1512
1601
|
additionalProperties: false,
|
|
1513
1602
|
};
|
|
1514
|
-
const
|
|
1603
|
+
const requestModel = isExternalModelProvider(provider)
|
|
1604
|
+
? async (_url, options) => {
|
|
1605
|
+
const body = JSON.parse(options.body);
|
|
1606
|
+
const content = await externalModelReply({
|
|
1607
|
+
...adapterSettings,
|
|
1608
|
+
provider,
|
|
1609
|
+
model,
|
|
1610
|
+
messages: body.messages,
|
|
1611
|
+
schema: body.response_format?.json_schema?.schema,
|
|
1612
|
+
signal: options.signal,
|
|
1613
|
+
timeoutMs,
|
|
1614
|
+
});
|
|
1615
|
+
return { choices: [{ message: { content } }] };
|
|
1616
|
+
}
|
|
1617
|
+
: requestLocalModel;
|
|
1618
|
+
const endpoint = missionModelEndpoint(provider, {
|
|
1619
|
+
ollamaUrl,
|
|
1620
|
+
qwenUrl,
|
|
1621
|
+
nimUrl,
|
|
1622
|
+
});
|
|
1515
1623
|
model ??=
|
|
1516
1624
|
provider === "qwen-local"
|
|
1517
1625
|
? "qwen3.5-9b"
|
|
@@ -1533,7 +1641,7 @@ async function runStructuredMissionAgent({
|
|
|
1533
1641
|
const useReasoning = reasoning === true && ["code", "review"].includes(stage);
|
|
1534
1642
|
for (let attempt = 0; attempt < (stage === "code" ? 2 : 1); attempt += 1) {
|
|
1535
1643
|
const generationLimit = useReasoning ? 8192 : stage === "code" ? 4096 : 2048;
|
|
1536
|
-
const data = await
|
|
1644
|
+
const data = await requestModel(endpoint, {
|
|
1537
1645
|
method: "POST",
|
|
1538
1646
|
headers: { "content-type": "application/json" },
|
|
1539
1647
|
signal: combined,
|
|
@@ -1556,7 +1664,11 @@ async function runStructuredMissionAgent({
|
|
|
1556
1664
|
: {
|
|
1557
1665
|
think: useReasoning,
|
|
1558
1666
|
format: schema,
|
|
1559
|
-
options: {
|
|
1667
|
+
options: {
|
|
1668
|
+
num_ctx: 16_384,
|
|
1669
|
+
num_predict: generationLimit,
|
|
1670
|
+
temperature: 0.1,
|
|
1671
|
+
},
|
|
1560
1672
|
}),
|
|
1561
1673
|
messages: [
|
|
1562
1674
|
{
|
|
@@ -1585,7 +1697,10 @@ async function runStructuredMissionAgent({
|
|
|
1585
1697
|
});
|
|
1586
1698
|
} catch (error) {
|
|
1587
1699
|
if (rejectionEvidenceDirectory) {
|
|
1588
|
-
await mkdir(rejectionEvidenceDirectory, {
|
|
1700
|
+
await mkdir(rejectionEvidenceDirectory, {
|
|
1701
|
+
recursive: true,
|
|
1702
|
+
mode: 0o700,
|
|
1703
|
+
});
|
|
1589
1704
|
await chmod(rejectionEvidenceDirectory, 0o700);
|
|
1590
1705
|
await saveJournal(join(rejectionEvidenceDirectory, `response-${attempt + 1}.json`), {
|
|
1591
1706
|
capturedAt: new Date().toISOString(),
|
|
@@ -1654,7 +1769,9 @@ export async function preflightMissionRepository(mission, repositoryRoot) {
|
|
|
1654
1769
|
const base = mission.repository?.baseRevision;
|
|
1655
1770
|
if (!mission.repository || !COMMIT.test(base ?? ""))
|
|
1656
1771
|
throw new Error("The mission needs a selected repository with a full commit revision.");
|
|
1657
|
-
const local = await inspectLocalRepository({
|
|
1772
|
+
const local = await inspectLocalRepository({
|
|
1773
|
+
TOTEM_REPOSITORY_ROOT: repositoryRoot,
|
|
1774
|
+
});
|
|
1658
1775
|
if (local.repository !== mission.repository.url)
|
|
1659
1776
|
throw new Error("The configured local repository does not match the mission repository.");
|
|
1660
1777
|
if ((await git(repositoryRoot, "cat-file", "-t", base)) !== "commit")
|
|
@@ -1676,7 +1793,9 @@ export async function prepareMissionCheckout(
|
|
|
1676
1793
|
throw new Error("Mission code work needs an immutable repository revision and work item ID.");
|
|
1677
1794
|
if (retryAttempt !== undefined && (!Number.isInteger(retryAttempt) || retryAttempt < 2))
|
|
1678
1795
|
throw new Error("A retry checkout needs a later server work-item attempt.");
|
|
1679
|
-
const local = await inspectLocalRepository({
|
|
1796
|
+
const local = await inspectLocalRepository({
|
|
1797
|
+
TOTEM_REPOSITORY_ROOT: repositoryRoot,
|
|
1798
|
+
});
|
|
1680
1799
|
if (local.repository !== mission.repository.url)
|
|
1681
1800
|
throw new Error("The selected local checkout does not match the mission repository.");
|
|
1682
1801
|
const base = await git(repositoryRoot, "rev-parse", mission.repository.baseRevision);
|
|
@@ -1714,7 +1833,10 @@ export async function prepareMissionCheckout(
|
|
|
1714
1833
|
} catch (error) {
|
|
1715
1834
|
if (error.code !== "ENOENT") throw error;
|
|
1716
1835
|
}
|
|
1717
|
-
await mkdir(join(stateDirectory, "checkouts"), {
|
|
1836
|
+
await mkdir(join(stateDirectory, "checkouts"), {
|
|
1837
|
+
recursive: true,
|
|
1838
|
+
mode: 0o700,
|
|
1839
|
+
});
|
|
1718
1840
|
await git(repositoryRoot, "worktree", "add", "-b", branch, checkout, startRevision);
|
|
1719
1841
|
return { checkout, branch, baseRevision: base, startRevision };
|
|
1720
1842
|
}
|
|
@@ -1732,7 +1854,9 @@ export async function prepareReviewCheckout(mission, workItem, repositoryRoot, s
|
|
|
1732
1854
|
!UUID.test(workItem.id)
|
|
1733
1855
|
)
|
|
1734
1856
|
throw new Error("Review requires a visible local code artifact with full Git revisions.");
|
|
1735
|
-
const local = await inspectLocalRepository({
|
|
1857
|
+
const local = await inspectLocalRepository({
|
|
1858
|
+
TOTEM_REPOSITORY_ROOT: repositoryRoot,
|
|
1859
|
+
});
|
|
1736
1860
|
if (local.repository !== mission.repository.url)
|
|
1737
1861
|
throw new Error("The selected checkout does not match the mission repository.");
|
|
1738
1862
|
const [resolvedHead, mergeBase] = await Promise.all([
|
|
@@ -1753,16 +1877,26 @@ export async function prepareReviewCheckout(mission, workItem, repositoryRoot, s
|
|
|
1753
1877
|
} catch (error) {
|
|
1754
1878
|
if (error.code !== "ENOENT") throw error;
|
|
1755
1879
|
}
|
|
1756
|
-
await mkdir(join(stateDirectory, "checkouts"), {
|
|
1880
|
+
await mkdir(join(stateDirectory, "checkouts"), {
|
|
1881
|
+
recursive: true,
|
|
1882
|
+
mode: 0o700,
|
|
1883
|
+
});
|
|
1757
1884
|
await git(repositoryRoot, "worktree", "add", "--detach", checkout, head);
|
|
1758
|
-
return {
|
|
1885
|
+
return {
|
|
1886
|
+
checkout,
|
|
1887
|
+
baseRevision: base,
|
|
1888
|
+
headCommit: head,
|
|
1889
|
+
branch: codeArtifact.metadata?.branch,
|
|
1890
|
+
};
|
|
1759
1891
|
}
|
|
1760
1892
|
|
|
1761
1893
|
export async function prepareBaseReadCheckout(mission, workItem, repositoryRoot, stateDirectory) {
|
|
1762
1894
|
const base = mission.repository?.baseRevision;
|
|
1763
1895
|
if (!mission.repository || !COMMIT.test(base ?? "") || !UUID.test(workItem.id))
|
|
1764
1896
|
throw new Error("Engineering needs a selected immutable repository revision.");
|
|
1765
|
-
const local = await inspectLocalRepository({
|
|
1897
|
+
const local = await inspectLocalRepository({
|
|
1898
|
+
TOTEM_REPOSITORY_ROOT: repositoryRoot,
|
|
1899
|
+
});
|
|
1766
1900
|
if (local.repository !== mission.repository.url)
|
|
1767
1901
|
throw new Error("The local repository does not match the mission selection.");
|
|
1768
1902
|
const resolved = await git(repositoryRoot, "rev-parse", base);
|
|
@@ -1777,7 +1911,10 @@ export async function prepareBaseReadCheckout(mission, workItem, repositoryRoot,
|
|
|
1777
1911
|
} catch (error) {
|
|
1778
1912
|
if (error.code !== "ENOENT") throw error;
|
|
1779
1913
|
}
|
|
1780
|
-
await mkdir(join(stateDirectory, "checkouts"), {
|
|
1914
|
+
await mkdir(join(stateDirectory, "checkouts"), {
|
|
1915
|
+
recursive: true,
|
|
1916
|
+
mode: 0o700,
|
|
1917
|
+
});
|
|
1781
1918
|
await git(repositoryRoot, "worktree", "add", "--detach", checkout, base);
|
|
1782
1919
|
return { checkout, baseRevision: base };
|
|
1783
1920
|
}
|
|
@@ -1950,7 +2087,10 @@ export async function runIndependentReviewProbes(checkout, changedFiles, probes,
|
|
|
1950
2087
|
try {
|
|
1951
2088
|
await writeFile(probePath, contents, { flag: "wx", mode: 0o600 });
|
|
1952
2089
|
const check = await runIsolatedNodeTests(checkoutPath, signal);
|
|
1953
|
-
return {
|
|
2090
|
+
return {
|
|
2091
|
+
...check,
|
|
2092
|
+
command: `${check.command}; independent reviewer probes`,
|
|
2093
|
+
};
|
|
1954
2094
|
} finally {
|
|
1955
2095
|
await rm(probePath, { force: true });
|
|
1956
2096
|
}
|
|
@@ -2106,7 +2246,9 @@ async function settleKnownLocalTestFailure(
|
|
|
2106
2246
|
checkoutInfo: record.checkoutInfo,
|
|
2107
2247
|
failure: record.failure,
|
|
2108
2248
|
});
|
|
2109
|
-
const remote = await client.missions.listEffects(missionId, workItemId, {
|
|
2249
|
+
const remote = await client.missions.listEffects(missionId, workItemId, {
|
|
2250
|
+
signal,
|
|
2251
|
+
});
|
|
2110
2252
|
for (const [index, effectId] of record.effectIds.entries()) {
|
|
2111
2253
|
const effect = remote.find((item) => item.id === effectId);
|
|
2112
2254
|
if (
|
|
@@ -2171,7 +2313,11 @@ async function recoverKnownLocalTestFailures(
|
|
|
2171
2313
|
);
|
|
2172
2314
|
} catch (error) {
|
|
2173
2315
|
if (error.status === 401 || error.status === 403) throw error;
|
|
2174
|
-
emit({
|
|
2316
|
+
emit({
|
|
2317
|
+
type: "recovery_waiting",
|
|
2318
|
+
workItemId,
|
|
2319
|
+
message: String(error.message ?? error),
|
|
2320
|
+
});
|
|
2175
2321
|
}
|
|
2176
2322
|
}
|
|
2177
2323
|
return handled;
|
|
@@ -2207,7 +2353,9 @@ async function settleCompletedResult(client, missionId, workItemId, path, record
|
|
|
2207
2353
|
throw new CompletedResultReconciliationFailed(
|
|
2208
2354
|
"The completed local result cannot be safely reconciled.",
|
|
2209
2355
|
);
|
|
2210
|
-
const remote = await client.missions.listEffects(missionId, workItemId, {
|
|
2356
|
+
const remote = await client.missions.listEffects(missionId, workItemId, {
|
|
2357
|
+
signal,
|
|
2358
|
+
});
|
|
2211
2359
|
const effects = record.effectIds.map((id, index) => {
|
|
2212
2360
|
const effect = remote.find((entry) => entry.id === id);
|
|
2213
2361
|
if (
|
|
@@ -2284,7 +2432,10 @@ async function recoverReadyCompletions(
|
|
|
2284
2432
|
"Review the saved result and its lease or assignment before continuing. The worker will not repeat completed actions.",
|
|
2285
2433
|
},
|
|
2286
2434
|
},
|
|
2287
|
-
{
|
|
2435
|
+
{
|
|
2436
|
+
idempotencyKey: key(agentId, workItemId, "completion-blocker"),
|
|
2437
|
+
signal,
|
|
2438
|
+
},
|
|
2288
2439
|
);
|
|
2289
2440
|
await saveJournal(path, { ...blocked, blockerRecorded: true });
|
|
2290
2441
|
};
|
|
@@ -2301,7 +2452,11 @@ async function recoverReadyCompletions(
|
|
|
2301
2452
|
completionInput(workItemId, record.leaseToken, record.result),
|
|
2302
2453
|
{ idempotencyKey: key(agentId, workItemId, "complete"), signal },
|
|
2303
2454
|
);
|
|
2304
|
-
await saveJournal(path, {
|
|
2455
|
+
await saveJournal(path, {
|
|
2456
|
+
state: "done",
|
|
2457
|
+
stage: record.stage,
|
|
2458
|
+
result: record.result,
|
|
2459
|
+
});
|
|
2305
2460
|
emit({ type: "work_recovered", workItemId });
|
|
2306
2461
|
} catch (error) {
|
|
2307
2462
|
if (error.status === 401 || error.status === 403) throw error;
|
|
@@ -2323,13 +2478,23 @@ async function recoverReadyCompletions(
|
|
|
2323
2478
|
}
|
|
2324
2479
|
continue;
|
|
2325
2480
|
}
|
|
2326
|
-
emit({
|
|
2481
|
+
emit({
|
|
2482
|
+
type: "recovery_waiting",
|
|
2483
|
+
workItemId,
|
|
2484
|
+
message: String(error.message ?? error),
|
|
2485
|
+
});
|
|
2327
2486
|
}
|
|
2328
2487
|
}
|
|
2329
2488
|
return handled;
|
|
2330
2489
|
}
|
|
2331
2490
|
|
|
2332
2491
|
export async function runMissionWorkerCycle(options) {
|
|
2492
|
+
if (
|
|
2493
|
+
(options.recoveryOnly ||
|
|
2494
|
+
options.candidateWorkItem?.metadata?.kind?.startsWith("infrastructure_")) &&
|
|
2495
|
+
(await runInfrastructureWorkerCycle(options))
|
|
2496
|
+
)
|
|
2497
|
+
return;
|
|
2333
2498
|
return runMissionWorker({ ...options, once: true, _singleCycle: true });
|
|
2334
2499
|
}
|
|
2335
2500
|
|
|
@@ -2355,6 +2520,7 @@ export async function runMissionWorker({
|
|
|
2355
2520
|
signal,
|
|
2356
2521
|
emit = () => {},
|
|
2357
2522
|
runAgent = runLocalMissionAgent,
|
|
2523
|
+
adapterSettings = {},
|
|
2358
2524
|
}) {
|
|
2359
2525
|
if (!UUID.test(missionId)) throw new Error("Provide a valid mission ID.");
|
|
2360
2526
|
missionModelEndpoint(provider, { ollamaUrl, qwenUrl, nimUrl });
|
|
@@ -2509,7 +2675,10 @@ export async function runMissionWorker({
|
|
|
2509
2675
|
"Assign a runtime that supports this work item, or set its stage to a supported kind.",
|
|
2510
2676
|
},
|
|
2511
2677
|
},
|
|
2512
|
-
{
|
|
2678
|
+
{
|
|
2679
|
+
idempotencyKey: key(agent.id, workItem.id, "unsupported-stage"),
|
|
2680
|
+
signal,
|
|
2681
|
+
},
|
|
2513
2682
|
)
|
|
2514
2683
|
.catch(() => {});
|
|
2515
2684
|
emit({
|
|
@@ -2525,7 +2694,11 @@ export async function runMissionWorker({
|
|
|
2525
2694
|
if (prior?.state === "claim_blocked") {
|
|
2526
2695
|
if (!reportedAttention.has(workItem.id)) {
|
|
2527
2696
|
await recordRecoveryBlocker(prior.pendingWorkItem, prior.message);
|
|
2528
|
-
emit({
|
|
2697
|
+
emit({
|
|
2698
|
+
type: "needs_attention",
|
|
2699
|
+
workItemId: workItem.id,
|
|
2700
|
+
message: prior.message,
|
|
2701
|
+
});
|
|
2529
2702
|
}
|
|
2530
2703
|
continue;
|
|
2531
2704
|
}
|
|
@@ -2745,7 +2918,10 @@ export async function runMissionWorker({
|
|
|
2745
2918
|
// preceded the request, so another worker's later effects cannot
|
|
2746
2919
|
// be bypassed by a claim key which never owned that attempt.
|
|
2747
2920
|
if (claimRecord.previousState) {
|
|
2748
|
-
const restored = {
|
|
2921
|
+
const restored = {
|
|
2922
|
+
...claimRecord,
|
|
2923
|
+
state: claimRecord.previousState,
|
|
2924
|
+
};
|
|
2749
2925
|
for (const field of [
|
|
2750
2926
|
"claimKey",
|
|
2751
2927
|
"expectedPriorAttempt",
|
|
@@ -2769,7 +2945,11 @@ export async function runMissionWorker({
|
|
|
2769
2945
|
};
|
|
2770
2946
|
await saveJournal(journalPath, blocked);
|
|
2771
2947
|
await recordRecoveryBlocker(claimRecord.pendingWorkItem, blocked.message);
|
|
2772
|
-
emit({
|
|
2948
|
+
emit({
|
|
2949
|
+
type: "needs_attention",
|
|
2950
|
+
workItemId: workItem.id,
|
|
2951
|
+
message: blocked.message,
|
|
2952
|
+
});
|
|
2773
2953
|
continue;
|
|
2774
2954
|
}
|
|
2775
2955
|
// Another worker may have claimed a stale listing, or the mission may
|
|
@@ -2810,7 +2990,9 @@ export async function runMissionWorker({
|
|
|
2810
2990
|
}, 10_000);
|
|
2811
2991
|
const renew = setInterval(() => {
|
|
2812
2992
|
void client.missions
|
|
2813
|
-
.renew(missionId, workItem.id, claim.leaseToken, {
|
|
2993
|
+
.renew(missionId, workItem.id, claim.leaseToken, {
|
|
2994
|
+
signal: combined,
|
|
2995
|
+
})
|
|
2814
2996
|
.catch((error) => {
|
|
2815
2997
|
renewError = error;
|
|
2816
2998
|
workSignal.abort();
|
|
@@ -2909,7 +3091,11 @@ export async function runMissionWorker({
|
|
|
2909
3091
|
});
|
|
2910
3092
|
}
|
|
2911
3093
|
if (effectIds.length) {
|
|
2912
|
-
await saveJournal(journalPath, {
|
|
3094
|
+
await saveJournal(journalPath, {
|
|
3095
|
+
state: "dispatching",
|
|
3096
|
+
stage,
|
|
3097
|
+
effectIds,
|
|
3098
|
+
});
|
|
2913
3099
|
for (const effectId of effectIds) {
|
|
2914
3100
|
const dispatch = await client.missions.dispatchEffect(
|
|
2915
3101
|
missionId,
|
|
@@ -2984,6 +3170,7 @@ export async function runMissionWorker({
|
|
|
2984
3170
|
write: stage === "code",
|
|
2985
3171
|
model,
|
|
2986
3172
|
provider,
|
|
3173
|
+
adapterSettings,
|
|
2987
3174
|
ollamaUrl,
|
|
2988
3175
|
qwenUrl,
|
|
2989
3176
|
nimUrl,
|
|
@@ -3008,6 +3195,7 @@ export async function runMissionWorker({
|
|
|
3008
3195
|
write: false,
|
|
3009
3196
|
model,
|
|
3010
3197
|
provider,
|
|
3198
|
+
adapterSettings,
|
|
3011
3199
|
ollamaUrl,
|
|
3012
3200
|
qwenUrl,
|
|
3013
3201
|
nimUrl,
|
|
@@ -3029,7 +3217,9 @@ export async function runMissionWorker({
|
|
|
3029
3217
|
}
|
|
3030
3218
|
for (const event of promptEvents(events).filter((item) => UUID.test(item.id ?? ""))) {
|
|
3031
3219
|
try {
|
|
3032
|
-
await client.missions.events.ack(missionId, event.id, {
|
|
3220
|
+
await client.missions.events.ack(missionId, event.id, {
|
|
3221
|
+
signal: combined,
|
|
3222
|
+
});
|
|
3033
3223
|
} catch (error) {
|
|
3034
3224
|
emit({
|
|
3035
3225
|
type: "event_ack_pending",
|
|
@@ -3046,7 +3236,10 @@ export async function runMissionWorker({
|
|
|
3046
3236
|
for (let repair = 0; repair < 3; repair += 1) {
|
|
3047
3237
|
await authorizeLocalLaunch();
|
|
3048
3238
|
const check = await runIsolatedNodeTests(checkout, combined);
|
|
3049
|
-
commands.push({
|
|
3239
|
+
commands.push({
|
|
3240
|
+
command: check.command,
|
|
3241
|
+
exitCode: check.exitCode,
|
|
3242
|
+
});
|
|
3050
3243
|
localTestRuns.push(check);
|
|
3051
3244
|
if (check.exitCode === 0) break;
|
|
3052
3245
|
if (repair === 2) throw new LocalCodeTestsFailed(localTestRuns);
|
|
@@ -3065,6 +3258,7 @@ export async function runMissionWorker({
|
|
|
3065
3258
|
write: true,
|
|
3066
3259
|
model,
|
|
3067
3260
|
provider,
|
|
3261
|
+
adapterSettings,
|
|
3068
3262
|
ollamaUrl,
|
|
3069
3263
|
qwenUrl,
|
|
3070
3264
|
nimUrl,
|
|
@@ -3131,6 +3325,7 @@ export async function runMissionWorker({
|
|
|
3131
3325
|
write: false,
|
|
3132
3326
|
model,
|
|
3133
3327
|
provider,
|
|
3328
|
+
adapterSettings,
|
|
3134
3329
|
ollamaUrl,
|
|
3135
3330
|
qwenUrl,
|
|
3136
3331
|
nimUrl,
|
|
@@ -3186,12 +3381,18 @@ export async function runMissionWorker({
|
|
|
3186
3381
|
if (reviewCheck)
|
|
3187
3382
|
generated.commands = [
|
|
3188
3383
|
...(generated.commands ?? []),
|
|
3189
|
-
{
|
|
3384
|
+
{
|
|
3385
|
+
command: reviewCheck.command,
|
|
3386
|
+
exitCode: reviewCheck.exitCode,
|
|
3387
|
+
},
|
|
3190
3388
|
];
|
|
3191
3389
|
if (reviewProbeCheck)
|
|
3192
3390
|
generated.commands = [
|
|
3193
3391
|
...(generated.commands ?? []),
|
|
3194
|
-
{
|
|
3392
|
+
{
|
|
3393
|
+
command: reviewProbeCheck.command,
|
|
3394
|
+
exitCode: reviewProbeCheck.exitCode,
|
|
3395
|
+
},
|
|
3195
3396
|
];
|
|
3196
3397
|
const resultSummary = handoff?.summary ?? generated.text.slice(0, 2000);
|
|
3197
3398
|
result = {
|
|
@@ -3224,7 +3425,10 @@ export async function runMissionWorker({
|
|
|
3224
3425
|
contentText: handoff
|
|
3225
3426
|
? JSON.stringify(handoff.detail, null, 2).slice(0, 32000)
|
|
3226
3427
|
: generated.text.slice(0, 32000),
|
|
3227
|
-
metadata: {
|
|
3428
|
+
metadata: {
|
|
3429
|
+
...(evidence ?? {}),
|
|
3430
|
+
commands: generated.commands ?? [],
|
|
3431
|
+
},
|
|
3228
3432
|
},
|
|
3229
3433
|
};
|
|
3230
3434
|
if (effectIds.length) {
|
|
@@ -3260,7 +3464,10 @@ export async function runMissionWorker({
|
|
|
3260
3464
|
missionId,
|
|
3261
3465
|
workItem.id,
|
|
3262
3466
|
completionInput(workItem.id, claim.leaseToken, result),
|
|
3263
|
-
{
|
|
3467
|
+
{
|
|
3468
|
+
idempotencyKey: key(agent.id, workItem.id, "complete"),
|
|
3469
|
+
signal: combined,
|
|
3470
|
+
},
|
|
3264
3471
|
);
|
|
3265
3472
|
await saveJournal(journalPath, { state: "done", stage, result });
|
|
3266
3473
|
reportedAttention.delete(workItem.id);
|
|
@@ -3301,7 +3508,10 @@ export async function runMissionWorker({
|
|
|
3301
3508
|
prior?.state === "local_tests_failed"
|
|
3302
3509
|
? (prior.verifiedFailedEffectIds ?? prior.effectIds ?? [])
|
|
3303
3510
|
: [],
|
|
3304
|
-
failure: {
|
|
3511
|
+
failure: {
|
|
3512
|
+
checks: error.checks,
|
|
3513
|
+
completedAt: new Date().toISOString(),
|
|
3514
|
+
},
|
|
3305
3515
|
};
|
|
3306
3516
|
await saveJournal(journalPath, record);
|
|
3307
3517
|
knownLocalTestFailure = true;
|