@amaster.ai/employee-runtime-connector 0.1.0-beta.49 → 0.1.0-beta.50
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/dist/amaster-runtime-daemon.mjs +1853 -327
- package/dist/amaster-runtime.mjs +27 -2
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
// src/amaster-runtime-daemon.mjs
|
|
5
5
|
import { createHash as createHash12 } from "node:crypto";
|
|
6
|
-
import { chmodSync as chmodSync6, copyFileSync as copyFileSync3, existsSync as
|
|
6
|
+
import { chmodSync as chmodSync6, copyFileSync as copyFileSync3, existsSync as existsSync14, lstatSync as lstatSync7, mkdirSync as mkdirSync9, readFileSync as readFileSync11, readdirSync as readdirSync9, realpathSync as realpathSync5, renameSync as renameSync6, rmSync as rmSync7, statSync as statSync7, symlinkSync as symlinkSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
|
|
7
7
|
import { arch as arch3, homedir as homedir3, hostname as hostname2, platform as platform3 } from "node:os";
|
|
8
|
-
import { basename as basename6, delimiter as delimiter2, dirname as dirname9, extname as extname2, isAbsolute as isAbsolute8, join as
|
|
8
|
+
import { basename as basename6, delimiter as delimiter2, dirname as dirname9, extname as extname2, isAbsolute as isAbsolute8, join as join15, relative as relative9, resolve as resolve12 } from "node:path";
|
|
9
9
|
import { spawn, spawnSync as spawnSync6 } from "node:child_process";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
|
|
@@ -59,7 +59,7 @@ function quoteShell(value) {
|
|
|
59
59
|
|
|
60
60
|
// src/amaster-runtime-daemon/codex-managed-mcp-profile.mjs
|
|
61
61
|
import { createHash } from "node:crypto";
|
|
62
|
-
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
62
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
63
63
|
import { arch, platform } from "node:os";
|
|
64
64
|
import { basename, dirname, isAbsolute, join as join2, relative, resolve } from "node:path";
|
|
65
65
|
import { spawnSync } from "node:child_process";
|
|
@@ -1489,6 +1489,7 @@ function cleanupManagedCodexMcpProfile(profile, owner) {
|
|
|
1489
1489
|
function reconcileManagedCodexMcpProfiles(rootPath, options = {}) {
|
|
1490
1490
|
const root = resolve(rootPath);
|
|
1491
1491
|
if (!existsSync(root)) return { scanned: 0, removed: 0, removedProfiles: 0, removedRollouts: 0, failed: 0, failures: [] };
|
|
1492
|
+
const canonicalRoot = realpathSync(root);
|
|
1492
1493
|
const profileMarkers = [];
|
|
1493
1494
|
const rolloutMarkers = [];
|
|
1494
1495
|
const pending = [root];
|
|
@@ -1504,16 +1505,23 @@ function reconcileManagedCodexMcpProfiles(rootPath, options = {}) {
|
|
|
1504
1505
|
}
|
|
1505
1506
|
}
|
|
1506
1507
|
let removedProfiles = 0;
|
|
1508
|
+
let preservedProfiles = 0;
|
|
1507
1509
|
let removedRollouts = 0;
|
|
1508
1510
|
const failures = [];
|
|
1511
|
+
const protectedCommandIds = new Set(Array.isArray(options.protectedCommandIds) ? options.protectedCommandIds.filter((value) => typeof value === "string" && value) : []);
|
|
1509
1512
|
for (const markerPath of profileMarkers) {
|
|
1510
1513
|
const profileRoot = dirname(markerPath);
|
|
1511
1514
|
try {
|
|
1512
1515
|
const marker = JSON.parse(readFileSync(markerPath, "utf8"));
|
|
1513
|
-
|
|
1516
|
+
const markerProfileRoot = typeof marker?.profileRoot === "string" ? resolve(marker.profileRoot) : null;
|
|
1517
|
+
if (!markerProfileRoot || !existsSync(markerProfileRoot) || !within(realpathSync(profileRoot), canonicalRoot) || realpathSync(markerProfileRoot) !== realpathSync(profileRoot)) {
|
|
1514
1518
|
throw new Error("ownership marker root mismatch");
|
|
1515
1519
|
}
|
|
1516
|
-
|
|
1520
|
+
if (protectedCommandIds.has(marker?.commandId)) {
|
|
1521
|
+
preservedProfiles += 1;
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
cleanupManagedCodexMcpProfile({ profileRoot: markerProfileRoot }, { commandId: marker.commandId, runId: marker.runId });
|
|
1517
1525
|
removedProfiles += 1;
|
|
1518
1526
|
} catch (error) {
|
|
1519
1527
|
failures.push({ profileRoot, error: error instanceof Error ? error.message : String(error) });
|
|
@@ -1527,7 +1535,8 @@ function reconcileManagedCodexMcpProfiles(rootPath, options = {}) {
|
|
|
1527
1535
|
const markerStat = lstatSync(markerPath);
|
|
1528
1536
|
const cacheStat = lstatSync(cacheRoot);
|
|
1529
1537
|
const marker = JSON.parse(readFileSync(markerPath, "utf8"));
|
|
1530
|
-
|
|
1538
|
+
const markerCacheRoot = typeof marker?.cacheRoot === "string" ? resolve(marker.cacheRoot) : null;
|
|
1539
|
+
if (!within(realpathSync(cacheRoot), canonicalRoot) || basename(cacheRoot) !== "session-rollout" || markerStat.isSymbolicLink() || !markerStat.isFile() || cacheStat.isSymbolicLink() || !cacheStat.isDirectory() || marker?.version !== 1 || typeof marker?.companyId !== "string" || typeof marker?.agentId !== "string" || typeof marker?.sourceRunId !== "string" || typeof marker?.sourceCommandId !== "string" || typeof marker?.sessionId !== "string" || markerCacheRoot != null && (!existsSync(markerCacheRoot) || realpathSync(markerCacheRoot) !== realpathSync(cacheRoot))) {
|
|
1531
1540
|
throw new Error("session rollout ownership marker mismatch");
|
|
1532
1541
|
}
|
|
1533
1542
|
const preservedAtMs = typeof marker.preservedAt === "string" ? Date.parse(marker.preservedAt) : Number.NaN;
|
|
@@ -1544,6 +1553,7 @@ function reconcileManagedCodexMcpProfiles(rootPath, options = {}) {
|
|
|
1544
1553
|
scanned: profileMarkers.length + rolloutMarkers.length,
|
|
1545
1554
|
removed: removedProfiles + removedRollouts,
|
|
1546
1555
|
removedProfiles,
|
|
1556
|
+
preservedProfiles,
|
|
1547
1557
|
removedRollouts,
|
|
1548
1558
|
failed: failures.length,
|
|
1549
1559
|
failures
|
|
@@ -1559,7 +1569,7 @@ import {
|
|
|
1559
1569
|
lstatSync as lstatSync2,
|
|
1560
1570
|
mkdirSync as mkdirSync3,
|
|
1561
1571
|
readFileSync as readFileSync3,
|
|
1562
|
-
realpathSync,
|
|
1572
|
+
realpathSync as realpathSync2,
|
|
1563
1573
|
readdirSync as readdirSync2,
|
|
1564
1574
|
rmSync as rmSync2,
|
|
1565
1575
|
statSync as statSync2,
|
|
@@ -1886,7 +1896,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
1886
1896
|
const PI_ATTESTATION_MAX_ATTEMPTS = 2;
|
|
1887
1897
|
const PI_ATTESTATION_RECENT_LIVE_TTL_MS = Number.isFinite(options.recentLiveTtlMs) && options.recentLiveTtlMs > 0 ? options.recentLiveTtlMs : 30 * 60 * 1e3;
|
|
1888
1898
|
const recentLivePiVersionProbes = /* @__PURE__ */ new Map();
|
|
1889
|
-
function
|
|
1899
|
+
function record5(value) {
|
|
1890
1900
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1891
1901
|
}
|
|
1892
1902
|
function npmPackageName(source) {
|
|
@@ -1942,7 +1952,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
1942
1952
|
return matches[0];
|
|
1943
1953
|
}
|
|
1944
1954
|
function restoreManagedPiSessionRollout(input, sessionsRoot, runDir, executorHome, authority) {
|
|
1945
|
-
const session =
|
|
1955
|
+
const session = record5(input.nativeSession);
|
|
1946
1956
|
if (session.mode !== "governed_action_approval" || session.required !== true) return null;
|
|
1947
1957
|
const sessionId = nonEmpty2(session.sessionId, "nativeSession.sessionId");
|
|
1948
1958
|
const sourceRunId = nonEmpty2(session.sourceRunId, "nativeSession.sourceRunId");
|
|
@@ -1998,7 +2008,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
1998
2008
|
}
|
|
1999
2009
|
function piExecutableIdentity(executorCommand) {
|
|
2000
2010
|
try {
|
|
2001
|
-
const realPath =
|
|
2011
|
+
const realPath = realpathSync2(executorCommand);
|
|
2002
2012
|
const executableStat = lstatSync2(realPath);
|
|
2003
2013
|
if (!executableStat.isFile() || executableStat.isSymbolicLink()) {
|
|
2004
2014
|
throw Object.assign(new Error("Pi executable is not a regular file"), { code: "EUNSAFE" });
|
|
@@ -2019,8 +2029,8 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2019
2029
|
}
|
|
2020
2030
|
}
|
|
2021
2031
|
function validateAuthority2(input) {
|
|
2022
|
-
const runtimeAuth =
|
|
2023
|
-
const gateway =
|
|
2032
|
+
const runtimeAuth = record5(input.runtimeAuth);
|
|
2033
|
+
const gateway = record5(runtimeAuth.governedMcp);
|
|
2024
2034
|
const runId = nonEmpty2(input.runId, "runId");
|
|
2025
2035
|
if (runtimeAuth.runId !== runId) throw new Error("pi_managed_mcp_owner_mismatch: command runId does not match runtime authority");
|
|
2026
2036
|
if (gateway.schemaVersion !== SUPPORTED_SCHEMA_VERSION2) throw new Error("pi_managed_mcp_invalid: unsupported schema version");
|
|
@@ -2036,7 +2046,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2036
2046
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() + 3e4) {
|
|
2037
2047
|
throw new Error("pi_managed_mcp_session_expired: Gateway session must remain valid through spawn preflight");
|
|
2038
2048
|
}
|
|
2039
|
-
const headers =
|
|
2049
|
+
const headers = record5(gateway.headers);
|
|
2040
2050
|
for (const name of REQUIRED_AUTHORITY_HEADERS2) nonEmpty2(headers[name], `headers.${name}`);
|
|
2041
2051
|
const allowedHeaderNames = /* @__PURE__ */ new Set([...REQUIRED_AUTHORITY_HEADERS2, ...runtimeAuth.issueId ? ["x-amaster-issue-id"] : []]);
|
|
2042
2052
|
if (Object.keys(headers).some((name) => !allowedHeaderNames.has(name)) || Object.keys(headers).length !== allowedHeaderNames.size) {
|
|
@@ -2065,7 +2075,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2065
2075
|
return output;
|
|
2066
2076
|
}
|
|
2067
2077
|
function assertInvocationIsolation2(input) {
|
|
2068
|
-
for (const name of Object.keys(
|
|
2078
|
+
for (const name of Object.keys(record5(input.commandEnv))) {
|
|
2069
2079
|
if (FORBIDDEN_COMMAND_ENV2.has(name)) throw new Error(`pi_managed_mcp_env_override_blocked: ${name}`);
|
|
2070
2080
|
if (!ALLOWED_COMMAND_ENV2.has(name)) throw new Error(`pi_managed_mcp_env_injection_blocked: ${name}`);
|
|
2071
2081
|
}
|
|
@@ -2097,7 +2107,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2097
2107
|
const value = safeProxyValue2(baseEnv?.[name], name);
|
|
2098
2108
|
if (value) env[name] = value;
|
|
2099
2109
|
}
|
|
2100
|
-
return { ...env, ...
|
|
2110
|
+
return { ...env, ...record5(commandEnv) };
|
|
2101
2111
|
}
|
|
2102
2112
|
function projectMcpConfigHasContent(filePath) {
|
|
2103
2113
|
if (!existsSync3(filePath)) return false;
|
|
@@ -2107,8 +2117,8 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2107
2117
|
} catch {
|
|
2108
2118
|
return true;
|
|
2109
2119
|
}
|
|
2110
|
-
const config =
|
|
2111
|
-
return Object.keys(
|
|
2120
|
+
const config = record5(parsed);
|
|
2121
|
+
return Object.keys(record5(config.mcpServers)).length > 0 || Object.keys(record5(config.servers)).length > 0 || Array.isArray(config.imports) && config.imports.length > 0;
|
|
2112
2122
|
}
|
|
2113
2123
|
function assertNoProjectMcpOverride(cwd, runDir) {
|
|
2114
2124
|
let cursor = resolve2(cwd);
|
|
@@ -2124,7 +2134,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2124
2134
|
}
|
|
2125
2135
|
}
|
|
2126
2136
|
function selectManagedBrowserUse(sourceSettings, npmSource) {
|
|
2127
|
-
const plugin =
|
|
2137
|
+
const plugin = record5(record5(sourceSettings.plugins)[MANAGED_BROWSER_USE_PLUGIN]);
|
|
2128
2138
|
if (plugin.enabled !== true || plugin.package !== MANAGED_BROWSER_USE_PACKAGE) return null;
|
|
2129
2139
|
const packageSpec = Array.isArray(sourceSettings.packages) ? sourceSettings.packages.find((entry) => npmPackageName(entry) === MANAGED_BROWSER_USE_PACKAGE) : null;
|
|
2130
2140
|
if (typeof packageSpec !== "string") return null;
|
|
@@ -2141,7 +2151,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2141
2151
|
if (packageMetadata.name !== MANAGED_BROWSER_USE_PACKAGE) {
|
|
2142
2152
|
throw new Error(`pi_managed_mcp_attestation_failed: enabled ${MANAGED_BROWSER_USE_PACKAGE} package identity mismatch`);
|
|
2143
2153
|
}
|
|
2144
|
-
const sourceConfig =
|
|
2154
|
+
const sourceConfig = record5(sourceSettings["pi-browser-use"]);
|
|
2145
2155
|
const config = {};
|
|
2146
2156
|
for (const key of MANAGED_BROWSER_USE_BOOLEAN_SETTINGS) {
|
|
2147
2157
|
if (typeof sourceConfig[key] === "boolean") config[key] = sourceConfig[key];
|
|
@@ -2185,7 +2195,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2185
2195
|
}
|
|
2186
2196
|
return {
|
|
2187
2197
|
packageSpec,
|
|
2188
|
-
config:
|
|
2198
|
+
config: record5(sourceSettings["pi-telemetry"])
|
|
2189
2199
|
};
|
|
2190
2200
|
}
|
|
2191
2201
|
function seedPiRuntime(sourceHome, agentDir) {
|
|
@@ -2206,7 +2216,7 @@ function createManagedPiMcpProfileApi(options = {}) {
|
|
|
2206
2216
|
let sourceSettings = {};
|
|
2207
2217
|
if (existsSync3(settingsSource)) {
|
|
2208
2218
|
try {
|
|
2209
|
-
sourceSettings =
|
|
2219
|
+
sourceSettings = record5(JSON.parse(readFileSync3(settingsSource, "utf8")));
|
|
2210
2220
|
} catch {
|
|
2211
2221
|
throw new Error("pi_managed_mcp_attestation_failed: source Pi settings are invalid");
|
|
2212
2222
|
}
|
|
@@ -2337,7 +2347,7 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2337
2347
|
const owner = Object.freeze({ commandId: input.commandId, runId });
|
|
2338
2348
|
writePrivateFile2(markerPath, `${JSON.stringify({ version: 1, ...owner, profileRoot })}
|
|
2339
2349
|
`);
|
|
2340
|
-
const runtimeAuth =
|
|
2350
|
+
const runtimeAuth = record5(input.runtimeAuth);
|
|
2341
2351
|
const authority = Object.freeze({
|
|
2342
2352
|
companyId: nonEmpty2(runtimeAuth.companyId, "runtimeAuth.companyId"),
|
|
2343
2353
|
agentId: nonEmpty2(runtimeAuth.agentId, "runtimeAuth.agentId"),
|
|
@@ -2472,6 +2482,7 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2472
2482
|
function reconcileManagedPiMcpProfiles2(rootPath, options2 = {}) {
|
|
2473
2483
|
const root = resolve2(rootPath);
|
|
2474
2484
|
if (!existsSync3(root)) return { scanned: 0, removed: 0, removedProfiles: 0, removedRollouts: 0, failed: 0, failures: [] };
|
|
2485
|
+
const canonicalRoot = realpathSync2(root);
|
|
2475
2486
|
const profileMarkers = [];
|
|
2476
2487
|
const rolloutMarkers = [];
|
|
2477
2488
|
const pending = [root];
|
|
@@ -2487,14 +2498,21 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2487
2498
|
}
|
|
2488
2499
|
}
|
|
2489
2500
|
let removedProfiles = 0;
|
|
2501
|
+
let preservedProfiles = 0;
|
|
2490
2502
|
let removedRollouts = 0;
|
|
2491
2503
|
const failures = [];
|
|
2504
|
+
const protectedCommandIds = new Set(Array.isArray(options2.protectedCommandIds) ? options2.protectedCommandIds.filter((value) => typeof value === "string" && value) : []);
|
|
2492
2505
|
for (const markerPath of profileMarkers) {
|
|
2493
2506
|
const profileRoot = dirname3(markerPath);
|
|
2494
2507
|
try {
|
|
2495
2508
|
const marker = JSON.parse(readFileSync3(markerPath, "utf8"));
|
|
2496
|
-
|
|
2497
|
-
|
|
2509
|
+
const markerProfileRoot = typeof marker?.profileRoot === "string" ? resolve2(marker.profileRoot) : null;
|
|
2510
|
+
if (!markerProfileRoot || !existsSync3(markerProfileRoot) || !within4(realpathSync2(profileRoot), canonicalRoot) || realpathSync2(markerProfileRoot) !== realpathSync2(profileRoot)) throw new Error("ownership marker root mismatch");
|
|
2511
|
+
if (protectedCommandIds.has(marker?.commandId)) {
|
|
2512
|
+
preservedProfiles += 1;
|
|
2513
|
+
continue;
|
|
2514
|
+
}
|
|
2515
|
+
cleanupManagedPiMcpProfile2({ profileRoot: markerProfileRoot }, { commandId: marker.commandId, runId: marker.runId });
|
|
2498
2516
|
removedProfiles += 1;
|
|
2499
2517
|
} catch (error) {
|
|
2500
2518
|
failures.push({ profileRoot, error: error instanceof Error ? error.message : String(error) });
|
|
@@ -2508,7 +2526,8 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2508
2526
|
const markerStat = lstatSync2(markerPath);
|
|
2509
2527
|
const cacheStat = lstatSync2(cacheRoot);
|
|
2510
2528
|
const marker = JSON.parse(readFileSync3(markerPath, "utf8"));
|
|
2511
|
-
|
|
2529
|
+
const markerCacheRoot = typeof marker?.cacheRoot === "string" ? resolve2(marker.cacheRoot) : null;
|
|
2530
|
+
if (!within4(realpathSync2(cacheRoot), canonicalRoot) || basename2(cacheRoot) !== "session-rollout" || markerStat.isSymbolicLink() || !markerStat.isFile() || cacheStat.isSymbolicLink() || !cacheStat.isDirectory() || marker?.version !== 1 || typeof marker?.companyId !== "string" || typeof marker?.agentId !== "string" || typeof marker?.sourceRunId !== "string" || typeof marker?.sourceCommandId !== "string" || typeof marker?.sessionId !== "string" || !markerCacheRoot || !existsSync3(markerCacheRoot) || realpathSync2(markerCacheRoot) !== realpathSync2(cacheRoot)) throw new Error("session rollout ownership marker mismatch");
|
|
2512
2531
|
const preservedAtMs = typeof marker.preservedAt === "string" ? Date.parse(marker.preservedAt) : Number.NaN;
|
|
2513
2532
|
const markerAgeMs = nowMs - (Number.isFinite(preservedAtMs) ? preservedAtMs : markerStat.mtimeMs);
|
|
2514
2533
|
if (markerAgeMs < rolloutTtlMs) continue;
|
|
@@ -2523,6 +2542,7 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2523
2542
|
scanned: profileMarkers.length + rolloutMarkers.length,
|
|
2524
2543
|
removed: removedProfiles + removedRollouts,
|
|
2525
2544
|
removedProfiles,
|
|
2545
|
+
preservedProfiles,
|
|
2526
2546
|
removedRollouts,
|
|
2527
2547
|
failed: failures.length,
|
|
2528
2548
|
failures
|
|
@@ -2631,8 +2651,8 @@ function resultOutboxFileName(commandId, now = Date.now()) {
|
|
|
2631
2651
|
return `${now}-${safeCommandId}.json`;
|
|
2632
2652
|
}
|
|
2633
2653
|
function isValidResultOutboxEntry(entry) {
|
|
2634
|
-
const
|
|
2635
|
-
return
|
|
2654
|
+
const record5 = asRecord(entry);
|
|
2655
|
+
return record5.version === 1 && Boolean(readString(record5.path)) && Object.keys(asRecord(record5.payload)).length > 0;
|
|
2636
2656
|
}
|
|
2637
2657
|
function resultOutboxEntryAgeMs(entry, nowMs) {
|
|
2638
2658
|
const createdAt = Date.parse(readString(entry.createdAt) ?? "");
|
|
@@ -2642,6 +2662,247 @@ function isTerminalResultOutboxStatus(status) {
|
|
|
2642
2662
|
return typeof status === "number" && status >= 400 && status < 500;
|
|
2643
2663
|
}
|
|
2644
2664
|
|
|
2665
|
+
// src/amaster-runtime-daemon/run-completion-state.mjs
|
|
2666
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, readdirSync as readdirSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2667
|
+
import { join as join5 } from "node:path";
|
|
2668
|
+
var RUN_COMPLETION_STATE_VERSION = 1;
|
|
2669
|
+
var RUN_COMPLETION_USAGE_SCHEMA_VERSION = "runtime-command-usage-v1";
|
|
2670
|
+
var RUN_COMPLETION_PHASES = /* @__PURE__ */ new Set([
|
|
2671
|
+
"executing",
|
|
2672
|
+
"usage_snapshot_persisted",
|
|
2673
|
+
"completion_check_pending",
|
|
2674
|
+
"check_failed_retry",
|
|
2675
|
+
"completion_check_outbox",
|
|
2676
|
+
"disposition_required",
|
|
2677
|
+
"closure_executing",
|
|
2678
|
+
"ready_to_terminalize",
|
|
2679
|
+
"cost_ingest_pending",
|
|
2680
|
+
"profile_cleanup",
|
|
2681
|
+
"result_post_pending",
|
|
2682
|
+
"terminal"
|
|
2683
|
+
]);
|
|
2684
|
+
var PHASE_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
2685
|
+
["executing:turn_recorded", "usage_snapshot_persisted"],
|
|
2686
|
+
["closure_executing:turn_recorded", "usage_snapshot_persisted"],
|
|
2687
|
+
["closure_executing:completion_ready", "ready_to_terminalize"],
|
|
2688
|
+
["closure_executing:completion_disposition_required", "disposition_required"],
|
|
2689
|
+
["closure_executing:completion_check_failed", "check_failed_retry"],
|
|
2690
|
+
["closure_executing:completion_authority_rejected", "ready_to_terminalize"],
|
|
2691
|
+
["usage_snapshot_persisted:completion_check_requested", "completion_check_pending"],
|
|
2692
|
+
["completion_check_pending:completion_ready", "ready_to_terminalize"],
|
|
2693
|
+
["completion_check_pending:completion_disposition_required", "disposition_required"],
|
|
2694
|
+
["completion_check_pending:completion_check_failed", "check_failed_retry"],
|
|
2695
|
+
["completion_check_pending:completion_authority_rejected", "ready_to_terminalize"],
|
|
2696
|
+
["check_failed_retry:completion_retry_scheduled", "completion_check_pending"],
|
|
2697
|
+
["check_failed_retry:completion_retry_exhausted", "completion_check_outbox"],
|
|
2698
|
+
["check_failed_retry:completion_retry_abandoned", "ready_to_terminalize"],
|
|
2699
|
+
["completion_check_outbox:completion_retry_scheduled", "completion_check_pending"],
|
|
2700
|
+
["disposition_required:closure_started", "closure_executing"],
|
|
2701
|
+
["disposition_required:closure_exhausted", "ready_to_terminalize"],
|
|
2702
|
+
["ready_to_terminalize:cost_ingest_started", "cost_ingest_pending"],
|
|
2703
|
+
["cost_ingest_pending:cost_ingest_delivered", "profile_cleanup"],
|
|
2704
|
+
["cost_ingest_pending:cost_ingest_abandoned", "profile_cleanup"],
|
|
2705
|
+
["profile_cleanup:profile_cleaned", "result_post_pending"],
|
|
2706
|
+
["profile_cleanup:profile_cleanup_abandoned", "result_post_pending"],
|
|
2707
|
+
["result_post_pending:result_post_delivered", "terminal"]
|
|
2708
|
+
]);
|
|
2709
|
+
function record3(value) {
|
|
2710
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2711
|
+
}
|
|
2712
|
+
function nonEmptyString(value, label) {
|
|
2713
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
2714
|
+
throw new Error(`run_completion_state_invalid:${label}`);
|
|
2715
|
+
}
|
|
2716
|
+
return value.trim();
|
|
2717
|
+
}
|
|
2718
|
+
function nonNegativeInteger(value) {
|
|
2719
|
+
const numeric = Number(value ?? 0);
|
|
2720
|
+
return Number.isInteger(numeric) && numeric >= 0 ? numeric : 0;
|
|
2721
|
+
}
|
|
2722
|
+
function nonNegativeNumber(value) {
|
|
2723
|
+
const numeric = Number(value ?? 0);
|
|
2724
|
+
return Number.isFinite(numeric) && numeric >= 0 ? numeric : 0;
|
|
2725
|
+
}
|
|
2726
|
+
function runtimeCommandCostIdempotencyKey(commandId) {
|
|
2727
|
+
return `runtime-command-cost:${nonEmptyString(commandId, "commandId")}`;
|
|
2728
|
+
}
|
|
2729
|
+
function runCompletionStateFileName(commandId) {
|
|
2730
|
+
const safeCommandId = nonEmptyString(commandId, "commandId").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
2731
|
+
return `${safeCommandId}.json`;
|
|
2732
|
+
}
|
|
2733
|
+
function normalizeRunTurnUsage(turn) {
|
|
2734
|
+
const source = record3(turn);
|
|
2735
|
+
const nestedUsage = record3(source.usage);
|
|
2736
|
+
const usage = Object.keys(nestedUsage).length > 0 ? nestedUsage : source;
|
|
2737
|
+
return {
|
|
2738
|
+
turn: source.turn === "closure" ? "closure" : "primary",
|
|
2739
|
+
attempt: source.turn === "closure" ? 1 : 0,
|
|
2740
|
+
inputTokens: nonNegativeInteger(usage.inputTokens),
|
|
2741
|
+
cachedInputTokens: nonNegativeInteger(usage.cachedInputTokens),
|
|
2742
|
+
outputTokens: nonNegativeInteger(usage.outputTokens),
|
|
2743
|
+
costUsd: nonNegativeNumber(usage.costUsd),
|
|
2744
|
+
recordedAt: typeof source.recordedAt === "string" && source.recordedAt ? source.recordedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
function aggregateRunTurnUsage(turns) {
|
|
2748
|
+
const normalized = Array.isArray(turns) ? turns.map(normalizeRunTurnUsage) : [];
|
|
2749
|
+
const aggregate = normalized.reduce((total, turn) => ({
|
|
2750
|
+
inputTokens: total.inputTokens + turn.inputTokens,
|
|
2751
|
+
cachedInputTokens: total.cachedInputTokens + turn.cachedInputTokens,
|
|
2752
|
+
outputTokens: total.outputTokens + turn.outputTokens,
|
|
2753
|
+
costUsd: total.costUsd + turn.costUsd
|
|
2754
|
+
}), {
|
|
2755
|
+
inputTokens: 0,
|
|
2756
|
+
cachedInputTokens: 0,
|
|
2757
|
+
outputTokens: 0,
|
|
2758
|
+
costUsd: 0
|
|
2759
|
+
});
|
|
2760
|
+
const costUsd = Number(aggregate.costUsd.toFixed(12));
|
|
2761
|
+
return {
|
|
2762
|
+
...aggregate,
|
|
2763
|
+
costUsd,
|
|
2764
|
+
costCents: costUsd > 0 ? Math.max(0, Math.round(costUsd * 100)) : 0
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
function createRunCompletionState(input) {
|
|
2768
|
+
const command = record3(input.command);
|
|
2769
|
+
const commandId = nonEmptyString(command.commandId ?? command.id, "command.commandId");
|
|
2770
|
+
const connectorId = nonEmptyString(input.connectorId ?? command.connectorId, "connectorId");
|
|
2771
|
+
const now = typeof input.now === "string" && input.now ? input.now : (/* @__PURE__ */ new Date()).toISOString();
|
|
2772
|
+
const firstTurn = normalizeRunTurnUsage({ ...record3(input.turn), turn: "primary", recordedAt: now });
|
|
2773
|
+
return {
|
|
2774
|
+
version: RUN_COMPLETION_STATE_VERSION,
|
|
2775
|
+
commandId,
|
|
2776
|
+
connectorId,
|
|
2777
|
+
phase: "usage_snapshot_persisted",
|
|
2778
|
+
closureAttempt: 0,
|
|
2779
|
+
command,
|
|
2780
|
+
executor: record3(input.executor),
|
|
2781
|
+
completionRequest: record3(input.completionRequest),
|
|
2782
|
+
candidateStatus: nonEmptyString(input.candidateStatus, "candidateStatus"),
|
|
2783
|
+
candidateResult: record3(input.candidateResult),
|
|
2784
|
+
...typeof input.candidateError === "string" && input.candidateError ? { candidateError: input.candidateError } : {},
|
|
2785
|
+
usageRecord: {
|
|
2786
|
+
schemaVersion: RUN_COMPLETION_USAGE_SCHEMA_VERSION,
|
|
2787
|
+
idempotencyKey: runtimeCommandCostIdempotencyKey(commandId),
|
|
2788
|
+
commandId,
|
|
2789
|
+
turns: [firstTurn],
|
|
2790
|
+
aggregate: aggregateRunTurnUsage([firstTurn]),
|
|
2791
|
+
ingestStatus: "pending"
|
|
2792
|
+
},
|
|
2793
|
+
createdAt: now,
|
|
2794
|
+
updatedAt: now
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
function appendRunCompletionUsageTurn(state, input) {
|
|
2798
|
+
const current = assertValidRunCompletionState(state);
|
|
2799
|
+
const turn = normalizeRunTurnUsage(input);
|
|
2800
|
+
const priorTurns = current.usageRecord.turns.filter((entry) => entry.turn !== turn.turn);
|
|
2801
|
+
const turns = [...priorTurns, turn].sort((left, right) => left.attempt - right.attempt);
|
|
2802
|
+
return {
|
|
2803
|
+
...current,
|
|
2804
|
+
closureAttempt: turn.turn === "closure" ? 1 : current.closureAttempt,
|
|
2805
|
+
phase: "usage_snapshot_persisted",
|
|
2806
|
+
usageRecord: {
|
|
2807
|
+
...current.usageRecord,
|
|
2808
|
+
turns,
|
|
2809
|
+
aggregate: aggregateRunTurnUsage(turns),
|
|
2810
|
+
ingestStatus: "pending"
|
|
2811
|
+
},
|
|
2812
|
+
updatedAt: turn.recordedAt
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
2815
|
+
function transitionRunCompletionState(state, event, patch = {}, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2816
|
+
const current = assertValidRunCompletionState(state);
|
|
2817
|
+
const nextPhase = PHASE_TRANSITIONS.get(`${current.phase}:${event}`);
|
|
2818
|
+
if (!nextPhase) {
|
|
2819
|
+
throw new Error(`run_completion_state_transition_invalid:${current.phase}:${event}`);
|
|
2820
|
+
}
|
|
2821
|
+
return assertValidRunCompletionState({
|
|
2822
|
+
...current,
|
|
2823
|
+
...record3(patch),
|
|
2824
|
+
phase: nextPhase,
|
|
2825
|
+
updatedAt: now
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
function markRunCompletionCostDelivered(state, receipt, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2829
|
+
const current = assertValidRunCompletionState(state);
|
|
2830
|
+
return transitionRunCompletionState({
|
|
2831
|
+
...current,
|
|
2832
|
+
usageRecord: {
|
|
2833
|
+
...current.usageRecord,
|
|
2834
|
+
ingestStatus: "delivered",
|
|
2835
|
+
deliveredAt: now,
|
|
2836
|
+
receipt: record3(receipt)
|
|
2837
|
+
}
|
|
2838
|
+
}, "cost_ingest_delivered", {}, now);
|
|
2839
|
+
}
|
|
2840
|
+
function assertValidRunCompletionState(value) {
|
|
2841
|
+
const state = record3(value);
|
|
2842
|
+
if (state.version !== RUN_COMPLETION_STATE_VERSION) throw new Error("run_completion_state_invalid:version");
|
|
2843
|
+
const commandId = nonEmptyString(state.commandId, "commandId");
|
|
2844
|
+
const connectorId = nonEmptyString(state.connectorId, "connectorId");
|
|
2845
|
+
if (!RUN_COMPLETION_PHASES.has(state.phase)) throw new Error("run_completion_state_invalid:phase");
|
|
2846
|
+
const command = record3(state.command);
|
|
2847
|
+
if (nonEmptyString(command.commandId ?? command.id, "command.commandId") !== commandId) {
|
|
2848
|
+
throw new Error("run_completion_state_invalid:command_binding");
|
|
2849
|
+
}
|
|
2850
|
+
if (nonEmptyString(command.connectorId, "command.connectorId") !== connectorId) {
|
|
2851
|
+
throw new Error("run_completion_state_invalid:connector_binding");
|
|
2852
|
+
}
|
|
2853
|
+
const usageRecord = record3(state.usageRecord);
|
|
2854
|
+
if (usageRecord.schemaVersion !== RUN_COMPLETION_USAGE_SCHEMA_VERSION) {
|
|
2855
|
+
throw new Error("run_completion_state_invalid:usage_schema");
|
|
2856
|
+
}
|
|
2857
|
+
if (usageRecord.commandId !== commandId) throw new Error("run_completion_state_invalid:usage_command_binding");
|
|
2858
|
+
if (usageRecord.idempotencyKey !== runtimeCommandCostIdempotencyKey(commandId)) {
|
|
2859
|
+
throw new Error("run_completion_state_invalid:usage_idempotency_key");
|
|
2860
|
+
}
|
|
2861
|
+
const turns = Array.isArray(usageRecord.turns) ? usageRecord.turns.map(normalizeRunTurnUsage) : [];
|
|
2862
|
+
if (turns.length < 1 || turns.length > 2) throw new Error("run_completion_state_invalid:usage_turns");
|
|
2863
|
+
const kinds = new Set(turns.map((turn) => turn.turn));
|
|
2864
|
+
if (kinds.size !== turns.length || !kinds.has("primary")) {
|
|
2865
|
+
throw new Error("run_completion_state_invalid:usage_turn_identity");
|
|
2866
|
+
}
|
|
2867
|
+
const aggregate = aggregateRunTurnUsage(turns);
|
|
2868
|
+
return {
|
|
2869
|
+
...state,
|
|
2870
|
+
commandId,
|
|
2871
|
+
connectorId,
|
|
2872
|
+
command,
|
|
2873
|
+
executor: record3(state.executor),
|
|
2874
|
+
completionRequest: record3(state.completionRequest),
|
|
2875
|
+
candidateResult: record3(state.candidateResult),
|
|
2876
|
+
usageRecord: {
|
|
2877
|
+
...usageRecord,
|
|
2878
|
+
turns,
|
|
2879
|
+
aggregate,
|
|
2880
|
+
ingestStatus: ["delivered", "failed"].includes(usageRecord.ingestStatus) ? usageRecord.ingestStatus : "pending"
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
function writeRunCompletionState(directory, state) {
|
|
2885
|
+
const valid = assertValidRunCompletionState(state);
|
|
2886
|
+
mkdirSync4(directory, { recursive: true, mode: 448 });
|
|
2887
|
+
const filePath = join5(directory, runCompletionStateFileName(valid.commandId));
|
|
2888
|
+
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
2889
|
+
writeFileSync4(temporaryPath, `${JSON.stringify(valid, null, 2)}
|
|
2890
|
+
`, { mode: 384 });
|
|
2891
|
+
renameSync2(temporaryPath, filePath);
|
|
2892
|
+
return filePath;
|
|
2893
|
+
}
|
|
2894
|
+
function readRunCompletionState(filePath) {
|
|
2895
|
+
return assertValidRunCompletionState(JSON.parse(readFileSync4(filePath, "utf8")));
|
|
2896
|
+
}
|
|
2897
|
+
function listRunCompletionStateFiles(directory) {
|
|
2898
|
+
if (!existsSync4(directory)) return [];
|
|
2899
|
+
return readdirSync3(directory).filter((name) => name.endsWith(".json")).sort().map((name) => join5(directory, name));
|
|
2900
|
+
}
|
|
2901
|
+
function removeRunCompletionState(directory, commandId) {
|
|
2902
|
+
const filePath = join5(directory, runCompletionStateFileName(commandId));
|
|
2903
|
+
if (existsSync4(filePath)) unlinkSync(filePath);
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2645
2906
|
// src/amaster-runtime-daemon/prompt-compiler.mjs
|
|
2646
2907
|
var DEFAULT_PROMPT_BUDGET_CHARS = 32e3;
|
|
2647
2908
|
var DEADLINE_POSTURE_GUARD = "Named-window:skip_this_window_and_continue; no lower-quality/approval-bypass/fabrication/whole-task-stop; deadline_posture_receipt=targetMilestoneRef,posture,onMiss,taskContinuation,next owner/action; not Server proof";
|
|
@@ -2720,8 +2981,8 @@ function governedReadSection(context) {
|
|
|
2720
2981
|
const reads = Array.isArray(context.governedReadResults) ? context.governedReadResults : [];
|
|
2721
2982
|
if (reads.length === 0) return { content: "", provenance: [] };
|
|
2722
2983
|
const normalized = reads.map((entry, index) => {
|
|
2723
|
-
const
|
|
2724
|
-
const receipt = asRecord(
|
|
2984
|
+
const record5 = asRecord(entry);
|
|
2985
|
+
const receipt = asRecord(record5.receipt);
|
|
2725
2986
|
const provider = readString(receipt.provider) ?? readString(receipt.transport);
|
|
2726
2987
|
const operation = readString(receipt.operation);
|
|
2727
2988
|
const observedAt = readString(receipt.retrievedAt) ?? readString(receipt.observedAt);
|
|
@@ -2731,7 +2992,7 @@ function governedReadSection(context) {
|
|
|
2731
2992
|
throw new Error(`governed_read_context_provenance_invalid: entry ${index} requires provider, operation, observedAt, and source`);
|
|
2732
2993
|
}
|
|
2733
2994
|
return {
|
|
2734
|
-
content:
|
|
2995
|
+
content: record5.content,
|
|
2735
2996
|
receipt: {
|
|
2736
2997
|
provider,
|
|
2737
2998
|
operation,
|
|
@@ -2754,6 +3015,146 @@ function governedReadSection(context) {
|
|
|
2754
3015
|
}))
|
|
2755
3016
|
};
|
|
2756
3017
|
}
|
|
3018
|
+
function canonicalJsonValue(value) {
|
|
3019
|
+
if (Array.isArray(value)) return value.map(canonicalJsonValue);
|
|
3020
|
+
const record5 = asRecord(value);
|
|
3021
|
+
if (Object.keys(record5).length === 0) return value;
|
|
3022
|
+
return Object.fromEntries(
|
|
3023
|
+
Object.keys(record5).sort().map((key) => [key, canonicalJsonValue(record5[key])])
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
3026
|
+
function resolvedDependencySections(context, input, contextScope, snapshotFreshness) {
|
|
3027
|
+
const wake = asRecord(context.paperclipWake);
|
|
3028
|
+
const hasContextCopy = Object.prototype.hasOwnProperty.call(context, "resolvedBlockerSummaries");
|
|
3029
|
+
const hasWakeCopy = Object.prototype.hasOwnProperty.call(wake, "resolvedBlockerSummaries");
|
|
3030
|
+
if (hasContextCopy && !Array.isArray(context.resolvedBlockerSummaries)) {
|
|
3031
|
+
throw new Error("Resolved dependency context invalid: resolvedBlockerSummaries must be an array");
|
|
3032
|
+
}
|
|
3033
|
+
if (hasWakeCopy && !Array.isArray(wake.resolvedBlockerSummaries)) {
|
|
3034
|
+
throw new Error("Resolved dependency wake context invalid: resolvedBlockerSummaries must be an array");
|
|
3035
|
+
}
|
|
3036
|
+
const contextCopy = hasContextCopy ? context.resolvedBlockerSummaries : null;
|
|
3037
|
+
const wakeCopy = hasWakeCopy ? wake.resolvedBlockerSummaries : null;
|
|
3038
|
+
if (contextCopy && wakeCopy && JSON.stringify(canonicalJsonValue(contextCopy)) !== JSON.stringify(canonicalJsonValue(wakeCopy))) {
|
|
3039
|
+
throw new Error("Resolved dependency snapshots diverge between contextSnapshot and paperclipWake");
|
|
3040
|
+
}
|
|
3041
|
+
const summaries = contextCopy ?? wakeCopy ?? [];
|
|
3042
|
+
const tuples = [];
|
|
3043
|
+
const seenTuples = /* @__PURE__ */ new Set();
|
|
3044
|
+
const detailLines = [];
|
|
3045
|
+
const detailSourceRefs = [];
|
|
3046
|
+
const detailObservedAt = [];
|
|
3047
|
+
for (let blockerIndex = 0; blockerIndex < summaries.length; blockerIndex += 1) {
|
|
3048
|
+
const blocker = asRecord(summaries[blockerIndex]);
|
|
3049
|
+
const blockerId = readString(blocker.id);
|
|
3050
|
+
const blockerIdentifier = readString(blocker.identifier);
|
|
3051
|
+
const blockerSelector = blockerIdentifier ?? blockerId;
|
|
3052
|
+
const documents = Array.isArray(blocker.documents) ? blocker.documents : [];
|
|
3053
|
+
const blockerObservedAt = readString(blocker.completedAt) ?? readString(blocker.updatedAt);
|
|
3054
|
+
if (!blockerSelector && documents.length > 0) {
|
|
3055
|
+
throw new Error(
|
|
3056
|
+
`Resolved dependency context invalid: blocker ${blockerIndex} requires an issue id or identifier`
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
3059
|
+
if (blockerSelector) {
|
|
3060
|
+
detailLines.push(
|
|
3061
|
+
`- ${blockerSelector}${readString(blocker.title) ? ` ${readString(blocker.title)}` : ""}${readString(blocker.status) ? ` (${readString(blocker.status)})` : ""}`
|
|
3062
|
+
);
|
|
3063
|
+
const summary = readString(blocker.summary);
|
|
3064
|
+
if (summary) detailLines.push(` Summary: ${summary}`);
|
|
3065
|
+
detailSourceRefs.push(`issue:${blockerId ?? blockerSelector}`);
|
|
3066
|
+
detailObservedAt.push(blockerObservedAt);
|
|
3067
|
+
}
|
|
3068
|
+
for (let documentIndex = 0; documentIndex < documents.length; documentIndex += 1) {
|
|
3069
|
+
const document = asRecord(documents[documentIndex]);
|
|
3070
|
+
const documentId = readString(document.documentId);
|
|
3071
|
+
const key = readString(document.key);
|
|
3072
|
+
const expectedLatestRevisionId = readString(document.latestRevisionId);
|
|
3073
|
+
if (!blockerSelector || !documentId || !key || !expectedLatestRevisionId) {
|
|
3074
|
+
throw new Error(
|
|
3075
|
+
`Resolved dependency context invalid: blocker ${blockerIndex} document ${documentIndex} requires blocker issue selector, documentId, key, and latestRevisionId`
|
|
3076
|
+
);
|
|
3077
|
+
}
|
|
3078
|
+
const latestRevisionNumber = document.latestRevisionNumber;
|
|
3079
|
+
if (latestRevisionNumber != null && (!Number.isInteger(latestRevisionNumber) || latestRevisionNumber < 1)) {
|
|
3080
|
+
throw new Error(
|
|
3081
|
+
`Resolved dependency context invalid: blocker ${blockerIndex} document ${documentIndex} latestRevisionNumber must be a positive integer`
|
|
3082
|
+
);
|
|
3083
|
+
}
|
|
3084
|
+
const tupleKey = JSON.stringify([
|
|
3085
|
+
blockerId,
|
|
3086
|
+
blockerSelector,
|
|
3087
|
+
documentId,
|
|
3088
|
+
key,
|
|
3089
|
+
expectedLatestRevisionId
|
|
3090
|
+
]);
|
|
3091
|
+
if (seenTuples.has(tupleKey)) continue;
|
|
3092
|
+
seenTuples.add(tupleKey);
|
|
3093
|
+
tuples.push({
|
|
3094
|
+
blockerId,
|
|
3095
|
+
blockerSelector,
|
|
3096
|
+
documentId,
|
|
3097
|
+
key,
|
|
3098
|
+
expectedLatestRevisionId,
|
|
3099
|
+
expectedLatestRevisionNumber: latestRevisionNumber ?? null,
|
|
3100
|
+
observedAt: readString(document.updatedAt) ?? blockerObservedAt
|
|
3101
|
+
});
|
|
3102
|
+
const documentTitle = readString(document.title);
|
|
3103
|
+
detailLines.push(
|
|
3104
|
+
` Document: ${key}${documentTitle ? ` (${documentTitle})` : ""}${latestRevisionNumber != null ? ` revision ${latestRevisionNumber}` : ""}`
|
|
3105
|
+
);
|
|
3106
|
+
}
|
|
3107
|
+
const workProducts = Array.isArray(blocker.workProducts) ? blocker.workProducts : [];
|
|
3108
|
+
for (const rawWorkProduct of workProducts) {
|
|
3109
|
+
const workProduct = asRecord(rawWorkProduct);
|
|
3110
|
+
const workProductId = readString(workProduct.id);
|
|
3111
|
+
const workProductTitle = readString(workProduct.title);
|
|
3112
|
+
if (!workProductId && !workProductTitle) continue;
|
|
3113
|
+
detailLines.push(
|
|
3114
|
+
` Work product: ${workProductId ?? workProductTitle}${workProductTitle && workProductId ? ` (${workProductTitle})` : ""}`
|
|
3115
|
+
);
|
|
3116
|
+
const workProductSummary = readString(workProduct.summary);
|
|
3117
|
+
if (workProductSummary) detailLines.push(` ${workProductSummary}`);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
const requiredLines = tuples.length > 0 ? [
|
|
3121
|
+
"Accepted predecessor outputs are binding inputs unless this issue explicitly requests reconsideration.",
|
|
3122
|
+
"Before any downstream document/artifact write, review request, status mutation, or completion, perform every Required read.",
|
|
3123
|
+
"Current-issue documents, parent-issue documents, summaries, and workspace files are not substitutes for these predecessor documents.",
|
|
3124
|
+
"Compare each returned latestRevisionId with the expected value. On revision mismatch, do not perform downstream writes; report it.",
|
|
3125
|
+
...tuples.flatMap((tuple, index) => {
|
|
3126
|
+
const readArguments = { issueId: tuple.blockerSelector, key: tuple.key };
|
|
3127
|
+
const call = input.executorKind === "pi" && input.managedMcpToolMode === "proxy_only" ? {
|
|
3128
|
+
server: "amaster",
|
|
3129
|
+
tool: "amaster.read_issue_document",
|
|
3130
|
+
args: JSON.stringify(readArguments)
|
|
3131
|
+
} : readArguments;
|
|
3132
|
+
return [
|
|
3133
|
+
`- Required read ${index + 1}: issue ${JSON.stringify(tuple.blockerSelector)}${tuple.blockerId ? ` (id ${JSON.stringify(tuple.blockerId)})` : ""}; document ${JSON.stringify(tuple.documentId)}; key ${JSON.stringify(tuple.key)}; expected latestRevisionId ${JSON.stringify(tuple.expectedLatestRevisionId)}${tuple.expectedLatestRevisionNumber != null ? `; revisionNumber ${tuple.expectedLatestRevisionNumber}` : ""}.`,
|
|
3134
|
+
input.executorKind === "pi" && input.managedMcpToolMode === "proxy_only" ? ` Exact Pi \`mcp\` arguments: ${JSON.stringify(call)}` : ` Call amaster.read_issue_document with these exact arguments: ${JSON.stringify(call)}`
|
|
3135
|
+
];
|
|
3136
|
+
})
|
|
3137
|
+
].join("\n") : "";
|
|
3138
|
+
return {
|
|
3139
|
+
required: {
|
|
3140
|
+
content: requiredLines,
|
|
3141
|
+
sourceRef: tuples.map(
|
|
3142
|
+
(tuple) => `issue:${tuple.blockerId ?? tuple.blockerSelector}/document:${tuple.documentId}@${tuple.expectedLatestRevisionId}`
|
|
3143
|
+
),
|
|
3144
|
+
observedAt: tuples.map((tuple) => tuple.observedAt),
|
|
3145
|
+
freshness: tuples.map(() => snapshotFreshness),
|
|
3146
|
+
scope: tuples.map(() => contextScope),
|
|
3147
|
+
tupleCount: tuples.length
|
|
3148
|
+
},
|
|
3149
|
+
details: {
|
|
3150
|
+
content: detailLines.join("\n"),
|
|
3151
|
+
sourceRef: detailSourceRefs,
|
|
3152
|
+
observedAt: detailObservedAt,
|
|
3153
|
+
freshness: detailSourceRefs.map(() => snapshotFreshness),
|
|
3154
|
+
scope: detailSourceRefs.map(() => contextScope)
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
2757
3158
|
function fixedRules(input, includeIssueLine) {
|
|
2758
3159
|
return [
|
|
2759
3160
|
"## AMaster Runtime Connector Task",
|
|
@@ -2790,9 +3191,15 @@ function interactionResolutionText(context) {
|
|
|
2790
3191
|
const wakeResolution = asRecord(asRecord(context.paperclipWake).interactionResolution);
|
|
2791
3192
|
const resolution = Object.keys(direct).length > 0 ? direct : wakeResolution;
|
|
2792
3193
|
if (Object.keys(resolution).length === 0) return "";
|
|
3194
|
+
const target = asRecord(resolution.target);
|
|
3195
|
+
const exactDocumentRevisionDirective = readString(resolution.status) === "changes_requested" && readString(target.type) === "issue_document" && readString(target.issueId) && readString(target.documentId) && readString(target.key) && readString(target.revisionId) ? [
|
|
3196
|
+
"The review target is an exact issue_document revision. Revise that same document with upsert_document_revision: copy target.issueId, target.documentId, and target.key, and use target.revisionId as baseRevisionId.",
|
|
3197
|
+
"Do not copy the reviewed document into the current issue and do not restart its revision lineage at 1."
|
|
3198
|
+
].join(" ") : "";
|
|
2793
3199
|
return [
|
|
2794
3200
|
"Treat this resolved interaction as the authoritative delta for this run.",
|
|
2795
3201
|
readString(resolution.status) === "changes_requested" ? "Apply every requested change before creating a replacement review." : "",
|
|
3202
|
+
exactDocumentRevisionDirective,
|
|
2796
3203
|
stringifyBoundedJson(resolution, 8e3)
|
|
2797
3204
|
].filter(Boolean).join("\n");
|
|
2798
3205
|
}
|
|
@@ -2928,12 +3335,36 @@ function runtimeDecompositionRequirementText(context) {
|
|
|
2928
3335
|
`Requirement source: ${sourceType}:${sourceId}@${sourceRevision}`
|
|
2929
3336
|
].join("\n");
|
|
2930
3337
|
}
|
|
3338
|
+
function piMcpProxyExamplesText(input) {
|
|
3339
|
+
if (!input.hasGovernedMcp || input.executorKind !== "pi" || input.managedMcpToolMode !== "proxy_only" || isRecoveryWakeReason(input.wakeReason)) return "";
|
|
3340
|
+
const proxy = (tool, args) => JSON.stringify({
|
|
3341
|
+
server: "amaster",
|
|
3342
|
+
tool,
|
|
3343
|
+
args: JSON.stringify(args)
|
|
3344
|
+
});
|
|
3345
|
+
return [
|
|
3346
|
+
"The Pi native tool is the outer `mcp` proxy. Emit these as actual tool calls; `args` is the stringified inner canonical object:",
|
|
3347
|
+
"Keep review payload strings short. Reference the exact document key and revision in `target`; do not duplicate the reviewed document body in `prompt` or `detailsMarkdown`.",
|
|
3348
|
+
`- describe: ${proxy("runtime_action.describe", { schemaVersion: "runtime-action-v1", actionType: "update_parent" })}`,
|
|
3349
|
+
`- submit: ${proxy("runtime_action.submit", { schemaVersion: "runtime-action-v1", idempotencyKey: "task-123-progress", action: { type: "update_parent", status: "in_progress", comment: "Continue the remaining work." } })}`,
|
|
3350
|
+
`- submit review interaction: ${proxy("runtime_action.submit", { schemaVersion: "runtime-action-v1", idempotencyKey: "task-123-review", action: { type: "create_interaction", kind: "request_confirmation", continuationPolicy: "wake_assignee_on_accept", payload: { version: 1, resolutionMode: "review", prompt: "Review `plan` revision 1.", target: { type: "issue_document", issueId: "11111111-1111-4111-8111-111111111111", documentId: "33333333-3333-4333-8333-333333333333", key: "plan", revisionId: "22222222-2222-4222-8222-222222222222", revisionNumber: 1 } } } })}`,
|
|
3351
|
+
`- plan: ${proxy("runtime_action.plan", { schemaVersion: "runtime-action-v1", idempotencyKey: "task-123-plan", actions: [{ type: "update_parent", status: "in_progress", comment: "Continue the remaining work." }] })}`,
|
|
3352
|
+
`- commit the returned plan revision: ${proxy("runtime_action.commit", { schemaVersion: "runtime-action-v1", planId: "11111111-1111-4111-8111-111111111111", revision: "plan-revision-1" })}`,
|
|
3353
|
+
`- read document: ${proxy("amaster.read_issue_document", { issueId: "AM-123", key: "plan" })}`
|
|
3354
|
+
].join("\n");
|
|
3355
|
+
}
|
|
2931
3356
|
function sectionText(section) {
|
|
2932
3357
|
if (!section.content) return "";
|
|
2933
3358
|
return section.title ? `## ${section.title}
|
|
2934
3359
|
${section.content}` : section.content;
|
|
2935
3360
|
}
|
|
2936
3361
|
function truncateSection(section, targetChars) {
|
|
3362
|
+
const mandatoryContent = section.mandatoryContent ?? "";
|
|
3363
|
+
if (targetChars <= mandatoryContent.length) {
|
|
3364
|
+
section.content = mandatoryContent;
|
|
3365
|
+
section.truncationReason = "unified_context_budget";
|
|
3366
|
+
return;
|
|
3367
|
+
}
|
|
2937
3368
|
if (targetChars <= 0) {
|
|
2938
3369
|
section.content = "";
|
|
2939
3370
|
section.truncationReason = "unified_context_budget";
|
|
@@ -3015,12 +3446,33 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
3015
3446
|
issueId: input.issueId ?? null
|
|
3016
3447
|
};
|
|
3017
3448
|
const snapshotFreshness = { kind: "run_snapshot" };
|
|
3449
|
+
const resolvedDependencies = resolvedDependencySections(
|
|
3450
|
+
context,
|
|
3451
|
+
input,
|
|
3452
|
+
contextScope,
|
|
3453
|
+
snapshotFreshness
|
|
3454
|
+
);
|
|
3455
|
+
const resolvedDependencyContent = resolvedDependencies.required.content ? [
|
|
3456
|
+
resolvedDependencies.required.content,
|
|
3457
|
+
resolvedDependencies.details.content ? `Auxiliary predecessor context (may be truncated):
|
|
3458
|
+
${resolvedDependencies.details.content}` : ""
|
|
3459
|
+
].filter(Boolean).join("\n\n") : "";
|
|
3460
|
+
const piMcpProxyExamples = resolvedDependencies.required.content ? "" : piMcpProxyExamplesText(input);
|
|
3018
3461
|
const rawSections = [
|
|
3019
3462
|
{ name: "runtime_rules", title: "", priority: 100, sourceRef: `command:${input.commandId}`, content: fixedRules(input, !hasTask) },
|
|
3463
|
+
...resolvedDependencyContent ? [{
|
|
3464
|
+
name: "resolved_dependencies",
|
|
3465
|
+
title: "Resolved Dependency Outputs",
|
|
3466
|
+
priority: 99,
|
|
3467
|
+
...resolvedDependencies.required,
|
|
3468
|
+
content: resolvedDependencyContent,
|
|
3469
|
+
mandatoryContent: resolvedDependencies.required.content
|
|
3470
|
+
}] : [],
|
|
3020
3471
|
{ name: "recovery_instruction", title: "Recovery Instruction", priority: 100, sourceRef: `run:${input.runId ?? "unknown"}`, content: completeRecoveryInstruction, truncationReason: completeRecoveryInstruction ? null : "mode_selection" },
|
|
3021
3472
|
{ name: "approval_continuation", title: "Approved Runtime Action Continuation", priority: 99, sourceRef: `run:${input.runId ?? "unknown"}`, content: approvalContinuationText(input), truncationReason: mode === "continuation" ? null : "mode_selection" },
|
|
3022
3473
|
{ name: "runtime_authorization", title: "Runtime Action Contract", priority: 98, sourceRef: `run:${input.runId ?? "unknown"}`, content: runtimeAuthorizationText(context, mode, { suppressOrdinaryCompletionContract }) },
|
|
3023
3474
|
{ name: "runtime_decomposition_requirement", title: "Required Task Decomposition", priority: 98, sourceRef: `issue:${input.issueId ?? "unknown"}`, content: runtimeDecompositionRequirementText(context) },
|
|
3475
|
+
...piMcpProxyExamples ? [{ name: "pi_mcp_proxy_examples", title: "Pi MCP Proxy Examples", priority: 96, sourceRef: "amaster_governed_mcp_proxy_contract", content: piMcpProxyExamples }] : [],
|
|
3024
3476
|
{ name: "continuation_summary", title: "Continuation Summary", priority: 97, sourceRef: readString(asRecord(context.paperclipContinuationSummary).key) ?? `issue:${input.issueId ?? "unknown"}`, observedAt: readString(asRecord(context.paperclipContinuationSummary).updatedAt), originalContent: continuationSummary, content: mode === "cold" ? "" : continuationSummary, truncationReason: mode === "cold" ? "mode_selection" : null },
|
|
3025
3477
|
{ name: "wake_comments", title: wakeDeltaTitle, priority: interactionResolution ? 99 : 95, sourceRef: wakeDeltaSourceRefs, originalContent: wakeDeltaOriginal, content: wakeDeltaContent, truncationReason: commentsSelected ? null : "duplicate_task_context" },
|
|
3026
3478
|
{ name: "task", title: "Task Context", priority: 90, sourceRef: `issue:${input.issueId ?? "unknown"}`, originalContent: taskText, content: includeTask ? taskText : "", truncationReason: includeTask ? null : "mode_selection" },
|
|
@@ -3061,7 +3513,9 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
3061
3513
|
}
|
|
3062
3514
|
if (prompt.length <= maxChars && manifest.budget.usedChars === prompt.length) break;
|
|
3063
3515
|
const overflow = Math.max(1, prompt.length - maxChars);
|
|
3064
|
-
const candidate = [...sections].filter(
|
|
3516
|
+
const candidate = [...sections].filter(
|
|
3517
|
+
(section) => section.priority < 100 && section.content.length > (section.mandatoryContent?.length ?? 0)
|
|
3518
|
+
).sort((left, right) => left.priority - right.priority)[0];
|
|
3065
3519
|
if (!candidate) {
|
|
3066
3520
|
if (!compactManifest) {
|
|
3067
3521
|
compactManifest = true;
|
|
@@ -3070,6 +3524,11 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
3070
3524
|
}
|
|
3071
3525
|
const fixedChars = sections.filter((section) => section.priority === 100).map(sectionText).join("\n").length;
|
|
3072
3526
|
const manifestChars = JSON.stringify(manifest).length;
|
|
3527
|
+
if (resolvedDependencies.required.tupleCount > 0) {
|
|
3528
|
+
throw new Error(
|
|
3529
|
+
`Resolved dependency mandatory read tuples exceed the unified ${maxChars}-character prompt budget: tupleCount=${resolvedDependencies.required.tupleCount} promptChars=${prompt.length} fixedChars=${fixedChars} manifestChars=${manifestChars}`
|
|
3530
|
+
);
|
|
3531
|
+
}
|
|
3073
3532
|
throw new Error(`Prompt fixed sections exceed the unified ${maxChars}-character budget: promptChars=${prompt.length} fixedChars=${fixedChars} manifestChars=${manifestChars}`);
|
|
3074
3533
|
}
|
|
3075
3534
|
truncateSection(candidate, Math.max(0, candidate.content.length - overflow - 32));
|
|
@@ -3082,9 +3541,9 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
3082
3541
|
}
|
|
3083
3542
|
|
|
3084
3543
|
// src/amaster-runtime-daemon/config-state.mjs
|
|
3085
|
-
import { existsSync as
|
|
3544
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3086
3545
|
import { homedir as homedir2, hostname } from "node:os";
|
|
3087
|
-
import { dirname as dirname4, join as
|
|
3546
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
3088
3547
|
|
|
3089
3548
|
// src/amaster-runtime-daemon/executor-discovery.mjs
|
|
3090
3549
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -3200,26 +3659,26 @@ function defaultMachineId() {
|
|
|
3200
3659
|
}
|
|
3201
3660
|
function runtimeHome(env) {
|
|
3202
3661
|
return expandHomePath(String(
|
|
3203
|
-
env.AMASTER_RUNTIME_STATE_HOME || env.AMASTER_RUNTIME_HOME ||
|
|
3662
|
+
env.AMASTER_RUNTIME_STATE_HOME || env.AMASTER_RUNTIME_HOME || join6(homedir2(), ".amaster-employee")
|
|
3204
3663
|
));
|
|
3205
3664
|
}
|
|
3206
3665
|
function stateFilePath(env) {
|
|
3207
|
-
return env.AMASTER_DAEMON_STATE_FILE ? expandHomePath(String(env.AMASTER_DAEMON_STATE_FILE)) :
|
|
3666
|
+
return env.AMASTER_DAEMON_STATE_FILE ? expandHomePath(String(env.AMASTER_DAEMON_STATE_FILE)) : join6(runtimeHome(env), "runtime-connector-state.json");
|
|
3208
3667
|
}
|
|
3209
3668
|
function corruptStatePath(path) {
|
|
3210
3669
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
3211
3670
|
const base = `${path}.corrupt-${timestamp2}Z`;
|
|
3212
3671
|
let candidate = base;
|
|
3213
|
-
for (let suffix = 1;
|
|
3672
|
+
for (let suffix = 1; existsSync5(candidate); suffix += 1) {
|
|
3214
3673
|
candidate = `${base}.${suffix}`;
|
|
3215
3674
|
}
|
|
3216
3675
|
return candidate;
|
|
3217
3676
|
}
|
|
3218
3677
|
function readState(env) {
|
|
3219
3678
|
const path = stateFilePath(env);
|
|
3220
|
-
if (!
|
|
3679
|
+
if (!existsSync5(path)) return {};
|
|
3221
3680
|
try {
|
|
3222
|
-
const state = JSON.parse(
|
|
3681
|
+
const state = JSON.parse(readFileSync5(path, "utf8"));
|
|
3223
3682
|
if (!state || typeof state !== "object" || Array.isArray(state)) {
|
|
3224
3683
|
throw new TypeError("runtime connector state must be a JSON object");
|
|
3225
3684
|
}
|
|
@@ -3227,7 +3686,7 @@ function readState(env) {
|
|
|
3227
3686
|
} catch {
|
|
3228
3687
|
const quarantinePath = corruptStatePath(path);
|
|
3229
3688
|
try {
|
|
3230
|
-
|
|
3689
|
+
renameSync3(path, quarantinePath);
|
|
3231
3690
|
process.stderr.write(`AMaster runtime state was invalid and quarantined at ${quarantinePath}
|
|
3232
3691
|
`);
|
|
3233
3692
|
} catch (error) {
|
|
@@ -3241,15 +3700,15 @@ function readState(env) {
|
|
|
3241
3700
|
}
|
|
3242
3701
|
function writeState(env, state) {
|
|
3243
3702
|
const path = stateFilePath(env);
|
|
3244
|
-
|
|
3703
|
+
mkdirSync5(dirname4(path), { recursive: true });
|
|
3245
3704
|
const temporaryPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
3246
3705
|
try {
|
|
3247
|
-
|
|
3706
|
+
writeFileSync5(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
3248
3707
|
`, {
|
|
3249
3708
|
flag: "wx",
|
|
3250
3709
|
mode: 384
|
|
3251
3710
|
});
|
|
3252
|
-
|
|
3711
|
+
renameSync3(temporaryPath, path);
|
|
3253
3712
|
} finally {
|
|
3254
3713
|
rmSync3(temporaryPath, { force: true });
|
|
3255
3714
|
}
|
|
@@ -3263,15 +3722,15 @@ function buildConfig(env = process.env, flags = {}) {
|
|
|
3263
3722
|
const authHeader = String(flags.authHeader ?? env.AMASTER_CONNECTOR_AUTH_HEADER ?? "");
|
|
3264
3723
|
const home = runtimeHome(env);
|
|
3265
3724
|
const runtimeWorkspacesRoot = expandHomePath(
|
|
3266
|
-
String(flags.runtimeWorkspacesRoot ?? env.AMASTER_RUNTIME_WORKSPACES_ROOT ??
|
|
3725
|
+
String(flags.runtimeWorkspacesRoot ?? env.AMASTER_RUNTIME_WORKSPACES_ROOT ?? join6(home, "workspaces"))
|
|
3267
3726
|
);
|
|
3268
3727
|
const browserSessionStateRoot = expandHomePath(String(
|
|
3269
|
-
flags.browserSessionStateRoot ?? env.AMASTER_BROWSER_SESSION_STATE_ROOT ??
|
|
3728
|
+
flags.browserSessionStateRoot ?? env.AMASTER_BROWSER_SESSION_STATE_ROOT ?? join6(home, "browser-auth")
|
|
3270
3729
|
));
|
|
3271
3730
|
const browserExecutablePath = String(
|
|
3272
3731
|
flags.browserExecutablePath ?? env.AMASTER_BROWSER_EXECUTABLE_PATH ?? ""
|
|
3273
3732
|
).trim();
|
|
3274
|
-
|
|
3733
|
+
mkdirSync5(runtimeWorkspacesRoot, { recursive: true });
|
|
3275
3734
|
const workspaceBindings = splitList(
|
|
3276
3735
|
flags.workspaceAllowlist ?? env.AMASTER_WORKSPACE_ALLOWLIST ?? env.AMASTER_WORKSPACE_BINDINGS ?? runtimeWorkspacesRoot
|
|
3277
3736
|
).map(expandHomePath);
|
|
@@ -3418,10 +3877,10 @@ function compactExecutorJsonlForTranscript(event) {
|
|
|
3418
3877
|
const text = JSON.stringify(compactExecutorJsonValue(event, stringMaxChars));
|
|
3419
3878
|
if (text.length <= 15e3) return text;
|
|
3420
3879
|
}
|
|
3421
|
-
const
|
|
3422
|
-
const item = tcAsRecord(
|
|
3880
|
+
const record5 = tcAsRecord(event);
|
|
3881
|
+
const item = tcAsRecord(record5.item);
|
|
3423
3882
|
return JSON.stringify({
|
|
3424
|
-
type: tcReadString(
|
|
3883
|
+
type: tcReadString(record5.type) ?? "executor.event",
|
|
3425
3884
|
item: item.type ? {
|
|
3426
3885
|
id: tcReadString(item.id),
|
|
3427
3886
|
type: tcReadString(item.type),
|
|
@@ -3434,10 +3893,10 @@ function compactExecutorJsonlForTranscript(event) {
|
|
|
3434
3893
|
});
|
|
3435
3894
|
}
|
|
3436
3895
|
function summarizeTokenUsage(usage) {
|
|
3437
|
-
const
|
|
3438
|
-
const inputTokens = tcReadNumber(
|
|
3439
|
-
const cachedInputTokens = tcReadNumber(
|
|
3440
|
-
const outputTokens = tcReadNumber(
|
|
3896
|
+
const record5 = tcAsRecord(usage);
|
|
3897
|
+
const inputTokens = tcReadNumber(record5.input_tokens ?? record5.inputTokens, 0);
|
|
3898
|
+
const cachedInputTokens = tcReadNumber(record5.cached_input_tokens ?? record5.cachedInputTokens, 0);
|
|
3899
|
+
const outputTokens = tcReadNumber(record5.output_tokens ?? record5.outputTokens, 0);
|
|
3441
3900
|
const parts = [];
|
|
3442
3901
|
if (inputTokens > 0) parts.push(`\u8F93\u5165 ${inputTokens}`);
|
|
3443
3902
|
if (cachedInputTokens > 0) parts.push(`\u7F13\u5B58 ${cachedInputTokens}`);
|
|
@@ -3692,8 +4151,8 @@ function summarizeClaudeEvent(event) {
|
|
|
3692
4151
|
return null;
|
|
3693
4152
|
}
|
|
3694
4153
|
function tcPiMessageText(message) {
|
|
3695
|
-
const
|
|
3696
|
-
const content =
|
|
4154
|
+
const record5 = tcAsRecord(message);
|
|
4155
|
+
const content = record5.content;
|
|
3697
4156
|
if (typeof content === "string" && content.trim().length > 0) return content.trim();
|
|
3698
4157
|
if (!Array.isArray(content)) return "";
|
|
3699
4158
|
return content.map((entry) => {
|
|
@@ -3707,14 +4166,14 @@ function tcPiAssistantMessageEventText(assistantMessageEvent) {
|
|
|
3707
4166
|
return tcReadString(event.content) ?? "";
|
|
3708
4167
|
}
|
|
3709
4168
|
function tcPiMessageStopReason(message) {
|
|
3710
|
-
const
|
|
3711
|
-
return tcReadString(
|
|
4169
|
+
const record5 = tcAsRecord(message);
|
|
4170
|
+
return tcReadString(record5.stopReason ?? record5.stop_reason);
|
|
3712
4171
|
}
|
|
3713
4172
|
function tcPiMessageErrorText(message) {
|
|
3714
|
-
const
|
|
3715
|
-
const explicitError = tcReadString(
|
|
4173
|
+
const record5 = tcAsRecord(message);
|
|
4174
|
+
const explicitError = tcReadString(record5.errorMessage ?? record5.error_message)?.trim();
|
|
3716
4175
|
if (explicitError) return explicitError;
|
|
3717
|
-
const stopReason = tcPiMessageStopReason(
|
|
4176
|
+
const stopReason = tcPiMessageStopReason(record5);
|
|
3718
4177
|
if (stopReason === "error" || stopReason === "failed" || stopReason === "exception") {
|
|
3719
4178
|
return `Pi Agent message ended with stopReason=${stopReason}`;
|
|
3720
4179
|
}
|
|
@@ -3927,6 +4386,36 @@ function governedMcpToolResult(structuredContent) {
|
|
|
3927
4386
|
...runtimeAction ? { runtimeAction } : {}
|
|
3928
4387
|
};
|
|
3929
4388
|
}
|
|
4389
|
+
function dedupeGovernedMcpToolResults(results) {
|
|
4390
|
+
const unique = [];
|
|
4391
|
+
const indexByInvocationId = /* @__PURE__ */ new Map();
|
|
4392
|
+
for (const rawResult of Array.isArray(results) ? results : []) {
|
|
4393
|
+
const result3 = asRecord(rawResult);
|
|
4394
|
+
const invocationId = readString(result3.invocationId);
|
|
4395
|
+
if (!invocationId) {
|
|
4396
|
+
unique.push(result3);
|
|
4397
|
+
continue;
|
|
4398
|
+
}
|
|
4399
|
+
const existingIndex = indexByInvocationId.get(invocationId);
|
|
4400
|
+
if (existingIndex === void 0) {
|
|
4401
|
+
indexByInvocationId.set(invocationId, unique.length);
|
|
4402
|
+
unique.push(result3);
|
|
4403
|
+
continue;
|
|
4404
|
+
}
|
|
4405
|
+
const existing = asRecord(unique[existingIndex]);
|
|
4406
|
+
const existingRuntimeAction = asRecord(existing.runtimeAction);
|
|
4407
|
+
const runtimeAction = asRecord(result3.runtimeAction);
|
|
4408
|
+
const existingArtifactIntent = asRecord(existing.artifactIntent);
|
|
4409
|
+
const artifactIntent = asRecord(result3.artifactIntent);
|
|
4410
|
+
unique[existingIndex] = {
|
|
4411
|
+
...existing,
|
|
4412
|
+
...result3,
|
|
4413
|
+
...Object.keys(existingRuntimeAction).length > 0 || Object.keys(runtimeAction).length > 0 ? { runtimeAction: { ...existingRuntimeAction, ...runtimeAction } } : {},
|
|
4414
|
+
...Object.keys(existingArtifactIntent).length > 0 || Object.keys(artifactIntent).length > 0 ? { artifactIntent: { ...existingArtifactIntent, ...artifactIntent } } : {}
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
return unique;
|
|
4418
|
+
}
|
|
3930
4419
|
function durablePiRuntimeActionEvidence(results) {
|
|
3931
4420
|
const normalizedResults = (Array.isArray(results) ? results : []).map(asRecord);
|
|
3932
4421
|
const writeCallIds = /* @__PURE__ */ new Set();
|
|
@@ -4051,12 +4540,13 @@ function parseCodexJsonl(stdout) {
|
|
|
4051
4540
|
errorMessage = readString(error.message) ?? errorMessage;
|
|
4052
4541
|
}
|
|
4053
4542
|
}
|
|
4543
|
+
const uniqueMcpToolResults = dedupeGovernedMcpToolResults(mcpToolResults);
|
|
4054
4544
|
return {
|
|
4055
4545
|
sessionId,
|
|
4056
4546
|
summary,
|
|
4057
4547
|
usage,
|
|
4058
4548
|
errorMessage: terminalEvent === "completed" ? null : errorMessage,
|
|
4059
|
-
...
|
|
4549
|
+
...uniqueMcpToolResults.length > 0 ? { mcpToolResults: uniqueMcpToolResults } : {}
|
|
4060
4550
|
};
|
|
4061
4551
|
}
|
|
4062
4552
|
function buildCodexErrorHaystack(input) {
|
|
@@ -4257,13 +4747,13 @@ function parseClaudeStreamJson(stdout) {
|
|
|
4257
4747
|
}
|
|
4258
4748
|
function openCodeErrorText(value) {
|
|
4259
4749
|
if (typeof value === "string") return value;
|
|
4260
|
-
const
|
|
4261
|
-
const message = readString(
|
|
4750
|
+
const record5 = asRecord(value);
|
|
4751
|
+
const message = readString(record5.message);
|
|
4262
4752
|
if (message) return message;
|
|
4263
|
-
const data = asRecord(
|
|
4753
|
+
const data = asRecord(record5.data);
|
|
4264
4754
|
const nestedMessage = readString(data.message);
|
|
4265
4755
|
if (nestedMessage) return nestedMessage;
|
|
4266
|
-
return readString(
|
|
4756
|
+
return readString(record5.name) ?? readString(record5.code) ?? "";
|
|
4267
4757
|
}
|
|
4268
4758
|
function parseOpenCodeJsonl(stdout) {
|
|
4269
4759
|
let sessionId = null;
|
|
@@ -4301,8 +4791,8 @@ function parseOpenCodeJsonl(stdout) {
|
|
|
4301
4791
|
return { sessionId, summary, usage, errorMessage };
|
|
4302
4792
|
}
|
|
4303
4793
|
function piMessageText(message) {
|
|
4304
|
-
const
|
|
4305
|
-
const content =
|
|
4794
|
+
const record5 = asRecord(message);
|
|
4795
|
+
const content = record5.content;
|
|
4306
4796
|
if (typeof content === "string") return content.trim();
|
|
4307
4797
|
if (!Array.isArray(content)) return "";
|
|
4308
4798
|
return content.map((entry) => {
|
|
@@ -4327,10 +4817,10 @@ function piTextValue(value) {
|
|
|
4327
4817
|
return typeof value === "string" && value.length > 0 ? value : "";
|
|
4328
4818
|
}
|
|
4329
4819
|
function piAssistantEventText(assistantEvent) {
|
|
4330
|
-
const
|
|
4331
|
-
const type = readString(
|
|
4332
|
-
if (type === "text_delta") return piTextValue(
|
|
4333
|
-
if (type === "text_end") return piTextValue(
|
|
4820
|
+
const record5 = asRecord(assistantEvent);
|
|
4821
|
+
const type = readString(record5.type);
|
|
4822
|
+
if (type === "text_delta") return piTextValue(record5.delta);
|
|
4823
|
+
if (type === "text_end") return piTextValue(record5.content);
|
|
4334
4824
|
return "";
|
|
4335
4825
|
}
|
|
4336
4826
|
function piMessageUsage(message) {
|
|
@@ -4351,14 +4841,14 @@ function assignPiUsage(target, source) {
|
|
|
4351
4841
|
if (source.costUsd > 0) target.costUsd = source.costUsd;
|
|
4352
4842
|
}
|
|
4353
4843
|
function piMessageStopReason(message) {
|
|
4354
|
-
const
|
|
4355
|
-
return readString(
|
|
4844
|
+
const record5 = asRecord(message);
|
|
4845
|
+
return readString(record5.stopReason ?? record5.stop_reason);
|
|
4356
4846
|
}
|
|
4357
4847
|
function piMessageErrorText(message) {
|
|
4358
|
-
const
|
|
4359
|
-
const explicitError = readString(
|
|
4848
|
+
const record5 = asRecord(message);
|
|
4849
|
+
const explicitError = readString(record5.errorMessage ?? record5.error_message)?.trim();
|
|
4360
4850
|
if (explicitError) return explicitError;
|
|
4361
|
-
const stopReason = piMessageStopReason(
|
|
4851
|
+
const stopReason = piMessageStopReason(record5);
|
|
4362
4852
|
if (stopReason === "error" || stopReason === "failed" || stopReason === "exception") {
|
|
4363
4853
|
return `Pi Agent message ended with stopReason=${stopReason}`;
|
|
4364
4854
|
}
|
|
@@ -4493,6 +4983,7 @@ function parsePiJsonl(stdout) {
|
|
|
4493
4983
|
}
|
|
4494
4984
|
}
|
|
4495
4985
|
}
|
|
4986
|
+
const uniqueMcpToolResults = dedupeGovernedMcpToolResults(mcpToolResults);
|
|
4496
4987
|
return {
|
|
4497
4988
|
sessionId,
|
|
4498
4989
|
summary: messages.at(-1) ?? "",
|
|
@@ -4504,7 +4995,7 @@ function parsePiJsonl(stdout) {
|
|
|
4504
4995
|
errorMessage,
|
|
4505
4996
|
...nonCleanupErrorCount > 0 ? { nonCleanupErrorCount } : {},
|
|
4506
4997
|
...cleanupDiagnostics.length > 0 ? { cleanupDiagnostics } : {},
|
|
4507
|
-
...
|
|
4998
|
+
...uniqueMcpToolResults.length > 0 ? { mcpToolResults: uniqueMcpToolResults } : {}
|
|
4508
4999
|
};
|
|
4509
5000
|
}
|
|
4510
5001
|
function parseGenericOutput(stdout, stderr) {
|
|
@@ -4615,6 +5106,12 @@ function parseRuntimeConnectorResponse(res, method, path, text) {
|
|
|
4615
5106
|
const error = new Error(`${method} ${path} returned HTTP ${res.status}: ${responseDescription}`);
|
|
4616
5107
|
error.httpStatus = res.status;
|
|
4617
5108
|
error.responseContentType = responseContentType;
|
|
5109
|
+
if (responseContentType?.includes("application/json") && text) {
|
|
5110
|
+
try {
|
|
5111
|
+
error.responseBody = JSON.parse(text);
|
|
5112
|
+
} catch {
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
4618
5115
|
throw error;
|
|
4619
5116
|
}
|
|
4620
5117
|
if (!text) return null;
|
|
@@ -4698,7 +5195,7 @@ var bestEffortPostJson = bestEffortPostRuntimeConnectorJson;
|
|
|
4698
5195
|
|
|
4699
5196
|
// src/amaster-runtime-daemon/runtime-artifact-upload.mjs
|
|
4700
5197
|
import { createHash as createHash3 } from "node:crypto";
|
|
4701
|
-
import { lstatSync as lstatSync3, readFileSync as
|
|
5198
|
+
import { lstatSync as lstatSync3, readFileSync as readFileSync6, realpathSync as realpathSync3 } from "node:fs";
|
|
4702
5199
|
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 } from "node:path";
|
|
4703
5200
|
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
4704
5201
|
function requiredString(value, name) {
|
|
@@ -4715,7 +5212,7 @@ function pathWithin(candidate, root) {
|
|
|
4715
5212
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
4716
5213
|
}
|
|
4717
5214
|
function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
|
|
4718
|
-
const root =
|
|
5215
|
+
const root = realpathSync3(resolve3(cwd));
|
|
4719
5216
|
const uploads = /* @__PURE__ */ new Map();
|
|
4720
5217
|
for (const result3 of Array.isArray(mcpToolResults) ? mcpToolResults : []) {
|
|
4721
5218
|
const intent = result3 && typeof result3 === "object" && !Array.isArray(result3) ? result3.artifactIntent : null;
|
|
@@ -4736,10 +5233,10 @@ function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
|
|
|
4736
5233
|
}
|
|
4737
5234
|
const sourcePath = resolve3(root, sourceRelativePath);
|
|
4738
5235
|
const stat = lstatSync3(sourcePath);
|
|
4739
|
-
if (!stat.isFile() || stat.isSymbolicLink() || !pathWithin(
|
|
5236
|
+
if (!stat.isFile() || stat.isSymbolicLink() || !pathWithin(realpathSync3(sourcePath), root)) {
|
|
4740
5237
|
throw new Error(`Runtime Artifact ${intentId} source is not an owned regular file`);
|
|
4741
5238
|
}
|
|
4742
|
-
const body =
|
|
5239
|
+
const body = readFileSync6(sourcePath);
|
|
4743
5240
|
const actualSha256 = createHash3("sha256").update(body).digest("hex");
|
|
4744
5241
|
if (body.length !== expectedByteSize || actualSha256 !== expectedSha256) {
|
|
4745
5242
|
throw new Error(`Runtime Artifact ${intentId} bytes do not match the governed ownership manifest`);
|
|
@@ -4771,21 +5268,39 @@ var RuntimeArtifactIngestAggregateError = class extends AggregateError {
|
|
|
4771
5268
|
};
|
|
4772
5269
|
function createRuntimeArtifactIngestQueue({ ingest, onError }) {
|
|
4773
5270
|
const handledIntentIds = /* @__PURE__ */ new Set();
|
|
5271
|
+
const artifactIdentityByIntentId = /* @__PURE__ */ new Map();
|
|
5272
|
+
const finalizedArtifactIdentities = /* @__PURE__ */ new Set();
|
|
4774
5273
|
const artifacts = [];
|
|
4775
5274
|
const errors = [];
|
|
4776
5275
|
let queue = Promise.resolve();
|
|
5276
|
+
const artifactIdentity = (intent) => {
|
|
5277
|
+
const sourceRelativePath = readString(asRecord(intent).sourceRelativePath);
|
|
5278
|
+
const sha2563 = readString(asRecord(intent).sha256);
|
|
5279
|
+
const byteSize = asRecord(intent).byteSize;
|
|
5280
|
+
return sourceRelativePath && sha2563 && Number.isSafeInteger(byteSize) && byteSize > 0 ? `${sourceRelativePath}\0${sha2563}\0${byteSize}` : null;
|
|
5281
|
+
};
|
|
5282
|
+
const retainReceipts = (receipts) => {
|
|
5283
|
+
artifacts.push(...receipts);
|
|
5284
|
+
for (const receipt of receipts) {
|
|
5285
|
+
const intentId = readString(asRecord(receipt).intentId);
|
|
5286
|
+
const identity2 = intentId ? artifactIdentityByIntentId.get(intentId) : null;
|
|
5287
|
+
if (identity2) finalizedArtifactIdentities.add(identity2);
|
|
5288
|
+
}
|
|
5289
|
+
};
|
|
4777
5290
|
return {
|
|
4778
5291
|
enqueue(results) {
|
|
4779
5292
|
const pending = results.filter((result3) => {
|
|
4780
5293
|
const intentId = readString(asRecord(result3).artifactIntent?.intentId);
|
|
4781
5294
|
if (!intentId || handledIntentIds.has(intentId)) return false;
|
|
4782
5295
|
handledIntentIds.add(intentId);
|
|
5296
|
+
const identity2 = artifactIdentity(asRecord(result3).artifactIntent);
|
|
5297
|
+
if (identity2) artifactIdentityByIntentId.set(intentId, identity2);
|
|
4783
5298
|
return true;
|
|
4784
5299
|
});
|
|
4785
5300
|
if (pending.length === 0) return;
|
|
4786
|
-
queue = queue.then(async () =>
|
|
5301
|
+
queue = queue.then(async () => retainReceipts(await ingest(pending))).catch((caught) => {
|
|
4787
5302
|
const aggregate = caught instanceof RuntimeArtifactIngestAggregateError ? caught : null;
|
|
4788
|
-
|
|
5303
|
+
retainReceipts(aggregate?.receipts ?? []);
|
|
4789
5304
|
for (const error of aggregate?.failures ?? [normalizedError(caught)]) {
|
|
4790
5305
|
errors.push(error);
|
|
4791
5306
|
onError?.(error);
|
|
@@ -4797,8 +5312,14 @@ function createRuntimeArtifactIngestQueue({ ingest, onError }) {
|
|
|
4797
5312
|
},
|
|
4798
5313
|
async flush() {
|
|
4799
5314
|
await queue;
|
|
4800
|
-
|
|
4801
|
-
|
|
5315
|
+
const unresolvedErrors = errors.filter((error) => {
|
|
5316
|
+
if (error.code === "runtime_artifact_rejected") return true;
|
|
5317
|
+
const intentId = readString(error.intentId);
|
|
5318
|
+
const identity2 = intentId ? artifactIdentityByIntentId.get(intentId) : null;
|
|
5319
|
+
return !identity2 || !finalizedArtifactIdentities.has(identity2);
|
|
5320
|
+
});
|
|
5321
|
+
if (unresolvedErrors.length > 0) {
|
|
5322
|
+
throw new RuntimeArtifactIngestAggregateError(unresolvedErrors, artifacts);
|
|
4802
5323
|
}
|
|
4803
5324
|
return [...artifacts];
|
|
4804
5325
|
}
|
|
@@ -4807,40 +5328,40 @@ function createRuntimeArtifactIngestQueue({ ingest, onError }) {
|
|
|
4807
5328
|
|
|
4808
5329
|
// src/amaster-runtime-daemon/workspace-guard.mjs
|
|
4809
5330
|
import { createHash as createHash4 } from "node:crypto";
|
|
4810
|
-
import { existsSync as
|
|
4811
|
-
import { basename as basename4, join as
|
|
5331
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, realpathSync as realpathSync4, statSync as statSync3 } from "node:fs";
|
|
5332
|
+
import { basename as basename4, join as join8, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4 } from "node:path";
|
|
4812
5333
|
|
|
4813
5334
|
// src/amaster-runtime-daemon/workspace-manifest.mjs
|
|
4814
|
-
import { existsSync as
|
|
4815
|
-
import { basename as basename3, dirname as dirname5, join as
|
|
5335
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
5336
|
+
import { basename as basename3, dirname as dirname5, join as join7 } from "node:path";
|
|
4816
5337
|
var WORKSPACE_MANIFEST_FILENAME = ".amaster-runtime.json";
|
|
4817
5338
|
function nowIso() {
|
|
4818
5339
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4819
5340
|
}
|
|
4820
5341
|
function workspaceManifestPath(workspaceOrCwd) {
|
|
4821
5342
|
if (!workspaceOrCwd) return null;
|
|
4822
|
-
if (typeof workspaceOrCwd === "string") return
|
|
4823
|
-
if (typeof workspaceOrCwd.cwd === "string") return
|
|
5343
|
+
if (typeof workspaceOrCwd === "string") return join7(workspaceOrCwd, WORKSPACE_MANIFEST_FILENAME);
|
|
5344
|
+
if (typeof workspaceOrCwd.cwd === "string") return join7(workspaceOrCwd.cwd, WORKSPACE_MANIFEST_FILENAME);
|
|
4824
5345
|
return null;
|
|
4825
5346
|
}
|
|
4826
5347
|
function readWorkspaceManifest(manifestPath) {
|
|
4827
|
-
if (!manifestPath || !
|
|
5348
|
+
if (!manifestPath || !existsSync6(manifestPath)) return null;
|
|
4828
5349
|
try {
|
|
4829
|
-
const parsed = JSON.parse(
|
|
5350
|
+
const parsed = JSON.parse(readFileSync7(manifestPath, "utf8"));
|
|
4830
5351
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
4831
5352
|
} catch {
|
|
4832
5353
|
return null;
|
|
4833
5354
|
}
|
|
4834
5355
|
}
|
|
4835
5356
|
function writeWorkspaceManifest(manifestPath, manifest) {
|
|
4836
|
-
const tempPath =
|
|
5357
|
+
const tempPath = join7(
|
|
4837
5358
|
dirname5(manifestPath),
|
|
4838
5359
|
`.${basename3(manifestPath)}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
4839
5360
|
);
|
|
4840
5361
|
try {
|
|
4841
|
-
|
|
5362
|
+
writeFileSync6(tempPath, `${JSON.stringify(manifest, null, 2)}
|
|
4842
5363
|
`, { mode: 384 });
|
|
4843
|
-
|
|
5364
|
+
renameSync4(tempPath, manifestPath);
|
|
4844
5365
|
} catch (err) {
|
|
4845
5366
|
rmSync4(tempPath, { force: true });
|
|
4846
5367
|
throw err;
|
|
@@ -4899,10 +5420,10 @@ function resolveWorkspaceCwd(config, command) {
|
|
|
4899
5420
|
const payload = asRecord(command.payload);
|
|
4900
5421
|
const requested = readString(payload.workspacePath);
|
|
4901
5422
|
const fallback = config.workspaceBindings[0] ?? process.cwd();
|
|
4902
|
-
const cwd =
|
|
5423
|
+
const cwd = realpathSync4(resolve4(expandHomePath(requested ?? fallback)));
|
|
4903
5424
|
const allowlist = config.workspaceBindings.flatMap((entry) => {
|
|
4904
5425
|
try {
|
|
4905
|
-
return [
|
|
5426
|
+
return [realpathSync4(resolve4(expandHomePath(entry)))];
|
|
4906
5427
|
} catch {
|
|
4907
5428
|
return [];
|
|
4908
5429
|
}
|
|
@@ -4911,7 +5432,7 @@ function resolveWorkspaceCwd(config, command) {
|
|
|
4911
5432
|
if (!allowed) {
|
|
4912
5433
|
throw new Error(`Workspace path is outside AMASTER_WORKSPACE_ALLOWLIST: ${cwd}`);
|
|
4913
5434
|
}
|
|
4914
|
-
if (!
|
|
5435
|
+
if (!existsSync7(cwd) || !statSync3(cwd).isDirectory()) {
|
|
4915
5436
|
throw new Error(`Workspace path does not exist or is not a directory: ${cwd}`);
|
|
4916
5437
|
}
|
|
4917
5438
|
return cwd;
|
|
@@ -4942,8 +5463,8 @@ function workspaceLabel(sourceWorkspacePath, payload) {
|
|
|
4942
5463
|
}
|
|
4943
5464
|
function workspacesRoot(config) {
|
|
4944
5465
|
const root = resolve4(expandHomePath(config.runtimeWorkspacesRoot ?? "~/.amaster-employee/workspaces"));
|
|
4945
|
-
|
|
4946
|
-
return
|
|
5466
|
+
mkdirSync6(root, { recursive: true });
|
|
5467
|
+
return realpathSync4(root);
|
|
4947
5468
|
}
|
|
4948
5469
|
function resolveExecutionWorkspace(config, command, opts = {}) {
|
|
4949
5470
|
const sourceWorkspacePath = resolveWorkspaceCwd(config, command);
|
|
@@ -4955,11 +5476,11 @@ function resolveExecutionWorkspace(config, command, opts = {}) {
|
|
|
4955
5476
|
const workspaceKey = `${safeSegment(workspaceLabel(sourceWorkspacePath, payload), "workspace")}-${shortHash(sourceWorkspacePath)}`;
|
|
4956
5477
|
const issueKey = `${safeSegment(issueLabel(command, payload), "issue")}-${shortHash(issueId, 8)}`;
|
|
4957
5478
|
const runKey = `${safeSegment(runId, "run")}-${shortHash(runId, 8)}`;
|
|
4958
|
-
const runDir =
|
|
4959
|
-
const cwd =
|
|
4960
|
-
const executorHome =
|
|
4961
|
-
|
|
4962
|
-
|
|
5479
|
+
const runDir = join8(root, workspaceKey, issueKey, runKey);
|
|
5480
|
+
const cwd = join8(runDir, "workdir");
|
|
5481
|
+
const executorHome = join8(runDir, "executors", safeSegment(executorKind, "executor"));
|
|
5482
|
+
mkdirSync6(cwd, { recursive: true });
|
|
5483
|
+
mkdirSync6(executorHome, { recursive: true });
|
|
4963
5484
|
const workspace = {
|
|
4964
5485
|
managed: true,
|
|
4965
5486
|
cwd,
|
|
@@ -4981,8 +5502,8 @@ function resolveExecutionWorkspace(config, command, opts = {}) {
|
|
|
4981
5502
|
}
|
|
4982
5503
|
|
|
4983
5504
|
// src/amaster-runtime-daemon/workspace-gc.mjs
|
|
4984
|
-
import { existsSync as
|
|
4985
|
-
import { join as
|
|
5505
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
|
|
5506
|
+
import { join as join9, resolve as resolve5 } from "node:path";
|
|
4986
5507
|
function readIsoTime(value) {
|
|
4987
5508
|
if (typeof value !== "string" || !value.trim()) return null;
|
|
4988
5509
|
const time = new Date(value).getTime();
|
|
@@ -4999,14 +5520,14 @@ function walkWorkdirs(root) {
|
|
|
4999
5520
|
if (!current) continue;
|
|
5000
5521
|
let entries = [];
|
|
5001
5522
|
try {
|
|
5002
|
-
entries =
|
|
5523
|
+
entries = readdirSync4(current, { withFileTypes: true });
|
|
5003
5524
|
} catch {
|
|
5004
5525
|
continue;
|
|
5005
5526
|
}
|
|
5006
5527
|
for (const entry of entries) {
|
|
5007
5528
|
if (!entry.isDirectory()) continue;
|
|
5008
|
-
const fullPath =
|
|
5009
|
-
if (entry.name === "workdir" &&
|
|
5529
|
+
const fullPath = join9(current, entry.name);
|
|
5530
|
+
if (entry.name === "workdir" && existsSync8(workspaceManifestPath(fullPath))) {
|
|
5010
5531
|
workdirs.push(fullPath);
|
|
5011
5532
|
continue;
|
|
5012
5533
|
}
|
|
@@ -5032,12 +5553,12 @@ function directorySizeBytes(path) {
|
|
|
5032
5553
|
if (stat.isDirectory()) {
|
|
5033
5554
|
let entries = [];
|
|
5034
5555
|
try {
|
|
5035
|
-
entries =
|
|
5556
|
+
entries = readdirSync4(current, { withFileTypes: true });
|
|
5036
5557
|
} catch {
|
|
5037
5558
|
continue;
|
|
5038
5559
|
}
|
|
5039
5560
|
for (const entry of entries) {
|
|
5040
|
-
stack.push(
|
|
5561
|
+
stack.push(join9(current, entry.name));
|
|
5041
5562
|
}
|
|
5042
5563
|
continue;
|
|
5043
5564
|
}
|
|
@@ -5081,7 +5602,7 @@ function planManagedWorkspaceGcDryRun(input) {
|
|
|
5081
5602
|
candidates: [],
|
|
5082
5603
|
protected: []
|
|
5083
5604
|
};
|
|
5084
|
-
if (!root || !
|
|
5605
|
+
if (!root || !existsSync8(root)) return result3;
|
|
5085
5606
|
for (const workdir of walkWorkdirs(root)) {
|
|
5086
5607
|
const manifest = readWorkspaceManifest(workspaceManifestPath(workdir));
|
|
5087
5608
|
if (!manifest || manifest.managed !== true) continue;
|
|
@@ -5109,11 +5630,11 @@ function planManagedWorkspaceGcDryRun(input) {
|
|
|
5109
5630
|
}
|
|
5110
5631
|
|
|
5111
5632
|
// src/amaster-runtime-daemon/runtime-status-summary.mjs
|
|
5112
|
-
import { existsSync as
|
|
5113
|
-
import { dirname as dirname6, join as
|
|
5633
|
+
import { existsSync as existsSync9, readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
|
|
5634
|
+
import { dirname as dirname6, join as join10, resolve as resolve6 } from "node:path";
|
|
5114
5635
|
function runtimeStatusDirectoryEntries(path) {
|
|
5115
5636
|
try {
|
|
5116
|
-
return
|
|
5637
|
+
return readdirSync5(path, { withFileTypes: true });
|
|
5117
5638
|
} catch {
|
|
5118
5639
|
return [];
|
|
5119
5640
|
}
|
|
@@ -5133,7 +5654,7 @@ function runtimeStatusDirectorySizeBytes(path) {
|
|
|
5133
5654
|
if (!stat || stat.isSymbolicLink()) continue;
|
|
5134
5655
|
if (stat.isDirectory()) {
|
|
5135
5656
|
for (const entry of runtimeStatusDirectoryEntries(current)) {
|
|
5136
|
-
stack.push(
|
|
5657
|
+
stack.push(join10(current, entry.name));
|
|
5137
5658
|
}
|
|
5138
5659
|
continue;
|
|
5139
5660
|
}
|
|
@@ -5143,15 +5664,15 @@ function runtimeStatusDirectorySizeBytes(path) {
|
|
|
5143
5664
|
}
|
|
5144
5665
|
function walkRuntimeStatusManagedWorkdirs(root) {
|
|
5145
5666
|
const workdirs = [];
|
|
5146
|
-
if (!root || !
|
|
5667
|
+
if (!root || !existsSync9(root)) return workdirs;
|
|
5147
5668
|
const stack = [root];
|
|
5148
5669
|
while (stack.length > 0) {
|
|
5149
5670
|
const current = stack.pop();
|
|
5150
5671
|
if (!current) continue;
|
|
5151
5672
|
for (const entry of runtimeStatusDirectoryEntries(current)) {
|
|
5152
5673
|
if (!entry.isDirectory()) continue;
|
|
5153
|
-
const fullPath =
|
|
5154
|
-
if (entry.name === "workdir" &&
|
|
5674
|
+
const fullPath = join10(current, entry.name);
|
|
5675
|
+
if (entry.name === "workdir" && existsSync9(workspaceManifestPath(fullPath))) {
|
|
5155
5676
|
workdirs.push(fullPath);
|
|
5156
5677
|
continue;
|
|
5157
5678
|
}
|
|
@@ -5192,12 +5713,12 @@ function summarizeRuntimeStatusRecentRun(workdir, manifest) {
|
|
|
5192
5713
|
}
|
|
5193
5714
|
function runtimeStatusOutboxDir(config) {
|
|
5194
5715
|
const statePath = config.AMASTER_DAEMON_STATE_FILE ?? config.daemonStateFile;
|
|
5195
|
-
return
|
|
5716
|
+
return join10(dirname6(stateFilePath({
|
|
5196
5717
|
AMASTER_DAEMON_STATE_FILE: statePath
|
|
5197
5718
|
})), "result-outbox");
|
|
5198
5719
|
}
|
|
5199
5720
|
function countRuntimeStatusJsonEntries(dir) {
|
|
5200
|
-
if (!
|
|
5721
|
+
if (!existsSync9(dir)) return 0;
|
|
5201
5722
|
return runtimeStatusDirectoryEntries(dir).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).length;
|
|
5202
5723
|
}
|
|
5203
5724
|
function summarizeRuntimeLocalState(input) {
|
|
@@ -5229,7 +5750,7 @@ function summarizeRuntimeLocalState(input) {
|
|
|
5229
5750
|
}
|
|
5230
5751
|
}
|
|
5231
5752
|
const pendingOutboxDir = runtimeStatusOutboxDir(config);
|
|
5232
|
-
const invalidOutboxDir =
|
|
5753
|
+
const invalidOutboxDir = join10(pendingOutboxDir, "invalid");
|
|
5233
5754
|
return {
|
|
5234
5755
|
managedWorkspacesRoot: root,
|
|
5235
5756
|
managedWorkdirCount: workdirs.length,
|
|
@@ -5355,10 +5876,10 @@ import {
|
|
|
5355
5876
|
chownSync,
|
|
5356
5877
|
lchownSync,
|
|
5357
5878
|
lstatSync as lstatSync4,
|
|
5358
|
-
readdirSync as
|
|
5879
|
+
readdirSync as readdirSync6
|
|
5359
5880
|
} from "node:fs";
|
|
5360
5881
|
import { resolve as resolve7, sep } from "node:path";
|
|
5361
|
-
var defaultFs = { chmodSync: chmodSync3, chownSync, lchownSync, lstatSync: lstatSync4, readdirSync:
|
|
5882
|
+
var defaultFs = { chmodSync: chmodSync3, chownSync, lchownSync, lstatSync: lstatSync4, readdirSync: readdirSync6 };
|
|
5362
5883
|
function defaultHashRunId(runId) {
|
|
5363
5884
|
return Number.parseInt(createHash5("sha256").update(runId).digest("hex").slice(0, 8), 16);
|
|
5364
5885
|
}
|
|
@@ -5488,17 +6009,17 @@ import { createHash as createHash6 } from "node:crypto";
|
|
|
5488
6009
|
import {
|
|
5489
6010
|
chmodSync as chmodSync4,
|
|
5490
6011
|
chownSync as chownSync2,
|
|
5491
|
-
existsSync as
|
|
6012
|
+
existsSync as existsSync10,
|
|
5492
6013
|
lchownSync as lchownSync2,
|
|
5493
6014
|
lstatSync as lstatSync5,
|
|
5494
|
-
mkdirSync as
|
|
5495
|
-
readFileSync as
|
|
5496
|
-
renameSync as
|
|
6015
|
+
mkdirSync as mkdirSync7,
|
|
6016
|
+
readFileSync as readFileSync8,
|
|
6017
|
+
renameSync as renameSync5,
|
|
5497
6018
|
rmSync as rmSync5,
|
|
5498
6019
|
symlinkSync as symlinkSync2,
|
|
5499
|
-
writeFileSync as
|
|
6020
|
+
writeFileSync as writeFileSync7
|
|
5500
6021
|
} from "node:fs";
|
|
5501
|
-
import { dirname as dirname7, isAbsolute as isAbsolute5, join as
|
|
6022
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join11, relative as relative5, resolve as resolve8 } from "node:path";
|
|
5502
6023
|
var REGISTRY_FILENAME = ".amaster-company-memory-groups.json";
|
|
5503
6024
|
var COMPANY_MARKER_FILENAME = ".amaster-company-memory.json";
|
|
5504
6025
|
var REGISTRY_VERSION = 1;
|
|
@@ -5508,15 +6029,15 @@ var DEFAULT_GID_SPAN = 1e6;
|
|
|
5508
6029
|
var defaultFs2 = {
|
|
5509
6030
|
chmodSync: chmodSync4,
|
|
5510
6031
|
chownSync: chownSync2,
|
|
5511
|
-
existsSync:
|
|
6032
|
+
existsSync: existsSync10,
|
|
5512
6033
|
lchownSync: lchownSync2,
|
|
5513
6034
|
lstatSync: lstatSync5,
|
|
5514
|
-
mkdirSync:
|
|
5515
|
-
readFileSync:
|
|
5516
|
-
renameSync:
|
|
6035
|
+
mkdirSync: mkdirSync7,
|
|
6036
|
+
readFileSync: readFileSync8,
|
|
6037
|
+
renameSync: renameSync5,
|
|
5517
6038
|
rmSync: rmSync5,
|
|
5518
6039
|
symlinkSync: symlinkSync2,
|
|
5519
|
-
writeFileSync:
|
|
6040
|
+
writeFileSync: writeFileSync7
|
|
5520
6041
|
};
|
|
5521
6042
|
function requiredString2(value, label) {
|
|
5522
6043
|
if (typeof value !== "string" || value.trim() === "") {
|
|
@@ -5580,7 +6101,7 @@ function ensureMemoryRoot(root, fs) {
|
|
|
5580
6101
|
fs.chmodSync(root, 457);
|
|
5581
6102
|
}
|
|
5582
6103
|
function readGroupRegistry(root, fs) {
|
|
5583
|
-
const path =
|
|
6104
|
+
const path = join11(root, REGISTRY_FILENAME);
|
|
5584
6105
|
if (!fs.existsSync(path)) {
|
|
5585
6106
|
return { path, groups: {} };
|
|
5586
6107
|
}
|
|
@@ -5662,11 +6183,11 @@ function prepareCompanyPiMemory(input, fs = defaultFs2) {
|
|
|
5662
6183
|
if (input.gid !== void 0 && positiveInteger2(input.gid, "gid") !== gid) {
|
|
5663
6184
|
throw new Error("pi_company_memory_gid_mismatch");
|
|
5664
6185
|
}
|
|
5665
|
-
const companyDir =
|
|
5666
|
-
const companyPiHome =
|
|
5667
|
-
const memoryDir =
|
|
6186
|
+
const companyDir = join11(root, safeCompanyPiHomeSegment(companyId));
|
|
6187
|
+
const companyPiHome = join11(companyDir, ".pi");
|
|
6188
|
+
const memoryDir = join11(companyPiHome, "memories");
|
|
5668
6189
|
ensurePrivateDirectory(companyDir, 456, 0, gid, fs);
|
|
5669
|
-
const markerPath =
|
|
6190
|
+
const markerPath = join11(companyDir, COMPANY_MARKER_FILENAME);
|
|
5670
6191
|
if (fs.existsSync(markerPath)) {
|
|
5671
6192
|
const markerStat = fs.lstatSync(markerPath);
|
|
5672
6193
|
if (!markerStat.isFile() || markerStat.isSymbolicLink()) {
|
|
@@ -5691,7 +6212,7 @@ function prepareCompanyPiMemory(input, fs = defaultFs2) {
|
|
|
5691
6212
|
assertDirectory(memoryDir, "memories", fs);
|
|
5692
6213
|
fs.chownSync(memoryDir, 0, gid);
|
|
5693
6214
|
fs.chmodSync(memoryDir, 1528);
|
|
5694
|
-
const memoryLink =
|
|
6215
|
+
const memoryLink = join11(profileHome, "memories");
|
|
5695
6216
|
if (fs.existsSync(memoryLink)) {
|
|
5696
6217
|
throw new Error("pi_company_memory_profile_link_exists");
|
|
5697
6218
|
}
|
|
@@ -5715,16 +6236,16 @@ import { createHash as createHash7 } from "node:crypto";
|
|
|
5715
6236
|
import {
|
|
5716
6237
|
chmodSync as chmodSync5,
|
|
5717
6238
|
copyFileSync as copyFileSync2,
|
|
5718
|
-
existsSync as
|
|
6239
|
+
existsSync as existsSync11,
|
|
5719
6240
|
lstatSync as lstatSync6,
|
|
5720
|
-
mkdirSync as
|
|
5721
|
-
readFileSync as
|
|
5722
|
-
readdirSync as
|
|
6241
|
+
mkdirSync as mkdirSync8,
|
|
6242
|
+
readFileSync as readFileSync9,
|
|
6243
|
+
readdirSync as readdirSync7,
|
|
5723
6244
|
rmSync as rmSync6,
|
|
5724
6245
|
symlinkSync as symlinkSync3,
|
|
5725
|
-
writeFileSync as
|
|
6246
|
+
writeFileSync as writeFileSync8
|
|
5726
6247
|
} from "node:fs";
|
|
5727
|
-
import { dirname as dirname8, isAbsolute as isAbsolute6, join as
|
|
6248
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join12, relative as relative6, resolve as resolve9 } from "node:path";
|
|
5728
6249
|
var ASSERTION_VERSION = "2026-07-25.v1";
|
|
5729
6250
|
var SHA256 = /^[a-f0-9]{64}$/;
|
|
5730
6251
|
var COPY_ENTRIES = ["SYSTEM.md", "policy", "skills", "agents", "bundles", "extensions"];
|
|
@@ -5749,7 +6270,7 @@ function sha256File(path, label) {
|
|
|
5749
6270
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
5750
6271
|
throw new Error(`pi_trusted_runtime_source_unsafe:${label}`);
|
|
5751
6272
|
}
|
|
5752
|
-
return createHash7("sha256").update(
|
|
6273
|
+
return createHash7("sha256").update(readFileSync9(path)).digest("hex");
|
|
5753
6274
|
}
|
|
5754
6275
|
function verifyDeclaredFiles(manifest, root, label, ignoredPaths = [], enforceComplete = true) {
|
|
5755
6276
|
if (!Array.isArray(manifest.files) || manifest.files.length === 0) {
|
|
@@ -5771,8 +6292,8 @@ function verifyDeclaredFiles(manifest, root, label, ignoredPaths = [], enforceCo
|
|
|
5771
6292
|
if (!enforceComplete) return;
|
|
5772
6293
|
const ignored = ignoredPaths.map((path) => path.split("\\").join("/"));
|
|
5773
6294
|
const visit = (directory) => {
|
|
5774
|
-
for (const entry of
|
|
5775
|
-
const filePath =
|
|
6295
|
+
for (const entry of readdirSync7(directory)) {
|
|
6296
|
+
const filePath = join12(directory, entry);
|
|
5776
6297
|
const relativePath = relative6(root, filePath).split("\\").join("/");
|
|
5777
6298
|
const stat = lstatSync6(filePath);
|
|
5778
6299
|
if (stat.isSymbolicLink()) {
|
|
@@ -5790,7 +6311,7 @@ function verifyDeclaredFiles(manifest, root, label, ignoredPaths = [], enforceCo
|
|
|
5790
6311
|
};
|
|
5791
6312
|
visit(root);
|
|
5792
6313
|
}
|
|
5793
|
-
function
|
|
6314
|
+
function record4(value) {
|
|
5794
6315
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5795
6316
|
}
|
|
5796
6317
|
function within3(candidate, root) {
|
|
@@ -5798,13 +6319,13 @@ function within3(candidate, root) {
|
|
|
5798
6319
|
return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
|
|
5799
6320
|
}
|
|
5800
6321
|
function readJsonFile2(path, label, fallback = {}) {
|
|
5801
|
-
if (!
|
|
6322
|
+
if (!existsSync11(path)) return fallback;
|
|
5802
6323
|
const stat = lstatSync6(path);
|
|
5803
6324
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
5804
6325
|
throw new Error(`pi_trusted_runtime_source_unsafe:${label}`);
|
|
5805
6326
|
}
|
|
5806
6327
|
try {
|
|
5807
|
-
return JSON.parse(
|
|
6328
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
5808
6329
|
} catch {
|
|
5809
6330
|
throw new Error(`pi_trusted_runtime_source_invalid:${label}`);
|
|
5810
6331
|
}
|
|
@@ -5835,29 +6356,29 @@ function copyTreeNoLinks(source, target) {
|
|
|
5835
6356
|
const stat = lstatSync6(source);
|
|
5836
6357
|
if (stat.isSymbolicLink()) throw new Error("pi_trusted_runtime_source_symlink_blocked");
|
|
5837
6358
|
if (stat.isDirectory()) {
|
|
5838
|
-
|
|
6359
|
+
mkdirSync8(target, { recursive: true, mode: 448 });
|
|
5839
6360
|
chmodSync5(target, 448);
|
|
5840
|
-
for (const entry of
|
|
5841
|
-
copyTreeNoLinks(
|
|
6361
|
+
for (const entry of readdirSync7(source)) {
|
|
6362
|
+
copyTreeNoLinks(join12(source, entry), join12(target, entry));
|
|
5842
6363
|
}
|
|
5843
6364
|
return;
|
|
5844
6365
|
}
|
|
5845
6366
|
if (!stat.isFile()) throw new Error("pi_trusted_runtime_source_type_blocked");
|
|
5846
|
-
|
|
6367
|
+
mkdirSync8(dirname8(target), { recursive: true, mode: 448 });
|
|
5847
6368
|
copyFileSync2(source, target);
|
|
5848
6369
|
chmodSync5(target, 384 | stat.mode & 73);
|
|
5849
6370
|
}
|
|
5850
6371
|
function mergeMcp(seedRoot, overlayRoot, governedConfig) {
|
|
5851
|
-
const seed =
|
|
5852
|
-
const overlay =
|
|
6372
|
+
const seed = record4(readJsonFile2(join12(seedRoot, "mcp.json"), "seed_mcp", { mcpServers: {} }));
|
|
6373
|
+
const overlay = record4(readJsonFile2(join12(overlayRoot, "mcp.json"), "overlay_mcp", { mcpServers: {} }));
|
|
5853
6374
|
assertNoPersistentSecrets(seed, ["seed", "mcp.json"]);
|
|
5854
6375
|
assertNoPersistentSecrets(overlay, ["overlay", "mcp.json"]);
|
|
5855
|
-
const seedServers =
|
|
5856
|
-
const overlayServers =
|
|
6376
|
+
const seedServers = record4(seed.mcpServers);
|
|
6377
|
+
const overlayServers = record4(overlay.mcpServers);
|
|
5857
6378
|
if ("amaster" in seedServers || "amaster" in overlayServers) {
|
|
5858
6379
|
throw new Error("pi_trusted_runtime_reserved_mcp_override:amaster");
|
|
5859
6380
|
}
|
|
5860
|
-
const governed =
|
|
6381
|
+
const governed = record4(record4(governedConfig).mcpServers).amaster;
|
|
5861
6382
|
if (!governed) throw new Error("pi_trusted_runtime_governed_mcp_missing");
|
|
5862
6383
|
return {
|
|
5863
6384
|
...deepMerge(seed, overlay),
|
|
@@ -5877,14 +6398,14 @@ function assertEnabledPackagesAvailable(settings, npmRoot) {
|
|
|
5877
6398
|
const requiredNames = new Set(
|
|
5878
6399
|
(Array.isArray(settings.packages) ? settings.packages : []).map(packageName).filter(Boolean)
|
|
5879
6400
|
);
|
|
5880
|
-
for (const plugin of Object.values(
|
|
5881
|
-
const config =
|
|
6401
|
+
for (const plugin of Object.values(record4(settings.plugins))) {
|
|
6402
|
+
const config = record4(plugin);
|
|
5882
6403
|
if (config.enabled === true && typeof config.package === "string") {
|
|
5883
6404
|
requiredNames.add(config.package);
|
|
5884
6405
|
}
|
|
5885
6406
|
}
|
|
5886
6407
|
for (const name of requiredNames) {
|
|
5887
|
-
const metadataPath =
|
|
6408
|
+
const metadataPath = join12(npmRoot, "node_modules", ...name.split("/"), "package.json");
|
|
5888
6409
|
const metadata = readJsonFile2(metadataPath, `package:${name}`, null);
|
|
5889
6410
|
if (!metadata || metadata.name !== name) {
|
|
5890
6411
|
throw new Error(`pi_trusted_runtime_package_unavailable:${name}`);
|
|
@@ -5904,44 +6425,44 @@ function materializeTrustedPiRuntimeProfile(input) {
|
|
|
5904
6425
|
}
|
|
5905
6426
|
}
|
|
5906
6427
|
for (const entry of COPY_ENTRIES) {
|
|
5907
|
-
const target =
|
|
6428
|
+
const target = join12(agentDir, entry);
|
|
5908
6429
|
rmSync6(target, { recursive: true, force: true });
|
|
5909
6430
|
for (const sourceRoot of [seedRoot, overlayRoot]) {
|
|
5910
|
-
const source =
|
|
5911
|
-
if (
|
|
6431
|
+
const source = join12(sourceRoot, entry);
|
|
6432
|
+
if (existsSync11(source)) copyTreeNoLinks(source, target);
|
|
5912
6433
|
}
|
|
5913
6434
|
}
|
|
5914
6435
|
const mergedJson = {};
|
|
5915
6436
|
for (const entry of JSON_ENTRIES) {
|
|
5916
|
-
const seed = readJsonFile2(
|
|
5917
|
-
const overlay = readJsonFile2(
|
|
6437
|
+
const seed = readJsonFile2(join12(seedRoot, entry), `seed_${entry}`, {});
|
|
6438
|
+
const overlay = readJsonFile2(join12(overlayRoot, entry), `overlay_${entry}`, {});
|
|
5918
6439
|
const merged = deepMerge(seed, overlay);
|
|
5919
6440
|
if (entry === "settings.json") {
|
|
5920
6441
|
merged["pi-security"] = {
|
|
5921
|
-
...
|
|
6442
|
+
...record4(merged["pi-security"]),
|
|
5922
6443
|
enabled: true,
|
|
5923
6444
|
approvals: {
|
|
5924
|
-
...
|
|
6445
|
+
...record4(record4(merged["pi-security"]).approvals),
|
|
5925
6446
|
allowSessionGrants: false
|
|
5926
6447
|
}
|
|
5927
6448
|
};
|
|
5928
6449
|
}
|
|
5929
6450
|
assertNoPersistentSecrets(merged, [entry]);
|
|
5930
|
-
|
|
6451
|
+
writeFileSync8(join12(agentDir, entry), `${JSON.stringify(merged, null, 2)}
|
|
5931
6452
|
`, {
|
|
5932
6453
|
mode: 384
|
|
5933
6454
|
});
|
|
5934
|
-
chmodSync5(
|
|
6455
|
+
chmodSync5(join12(agentDir, entry), 384);
|
|
5935
6456
|
mergedJson[entry] = merged;
|
|
5936
6457
|
}
|
|
5937
6458
|
const governedConfig = readJsonFile2(input.governedMcpConfigPath, "governed_mcp");
|
|
5938
6459
|
const mcp = mergeMcp(seedRoot, overlayRoot, governedConfig);
|
|
5939
|
-
|
|
6460
|
+
writeFileSync8(input.governedMcpConfigPath, `${JSON.stringify(mcp, null, 2)}
|
|
5940
6461
|
`, { mode: 384 });
|
|
5941
6462
|
chmodSync5(input.governedMcpConfigPath, 384);
|
|
5942
|
-
const npmRoot =
|
|
6463
|
+
const npmRoot = join12(seedRoot, "npm");
|
|
5943
6464
|
assertEnabledPackagesAvailable(mergedJson["settings.json"], npmRoot);
|
|
5944
|
-
const npmTarget =
|
|
6465
|
+
const npmTarget = join12(agentDir, "npm");
|
|
5945
6466
|
rmSync6(npmTarget, { recursive: true, force: true });
|
|
5946
6467
|
const npmStat = lstatSync6(npmRoot);
|
|
5947
6468
|
if (!npmStat.isDirectory() || npmStat.isSymbolicLink()) {
|
|
@@ -5986,13 +6507,13 @@ function readTrustedPiRuntimeAudit(input) {
|
|
|
5986
6507
|
const profileRoot = resolve9(requiredString3(input.profileRoot, "profileRoot"));
|
|
5987
6508
|
const auditFile = resolve9(requiredString3(input.auditFile, "auditFile"));
|
|
5988
6509
|
if (!within3(auditFile, profileRoot)) throw new Error("pi_trusted_runtime_audit_path_escape");
|
|
5989
|
-
if (!
|
|
6510
|
+
if (!existsSync11(auditFile)) throw new Error("pi_trusted_runtime_audit_startup_missing");
|
|
5990
6511
|
const stat = lstatSync6(auditFile);
|
|
5991
6512
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
5992
6513
|
throw new Error("pi_trusted_runtime_audit_unsafe");
|
|
5993
6514
|
}
|
|
5994
6515
|
if (stat.size > MAX_AUDIT_BYTES) throw new Error("pi_trusted_runtime_audit_too_large");
|
|
5995
|
-
const lines =
|
|
6516
|
+
const lines = readFileSync9(auditFile, "utf8").split("\n").filter(Boolean);
|
|
5996
6517
|
if (lines.length === 0 || lines.length > MAX_AUDIT_EVENTS) {
|
|
5997
6518
|
throw new Error("pi_trusted_runtime_audit_event_count_invalid");
|
|
5998
6519
|
}
|
|
@@ -6049,11 +6570,11 @@ function readTrustedPiRuntimeLocalDigests(input) {
|
|
|
6049
6570
|
throw new Error(`pi_trusted_runtime_source_unsafe:${label}`);
|
|
6050
6571
|
}
|
|
6051
6572
|
}
|
|
6052
|
-
const seedManifestFile =
|
|
6053
|
-
const overlayManifestFile =
|
|
6054
|
-
const seedManifest = JSON.parse(
|
|
6055
|
-
const overlayManifest = JSON.parse(
|
|
6056
|
-
const policyManifest = JSON.parse(
|
|
6573
|
+
const seedManifestFile = join12(seedRoot, "seed-manifest.json");
|
|
6574
|
+
const overlayManifestFile = join12(overlayRoot, "runtime-overlay-manifest.json");
|
|
6575
|
+
const seedManifest = JSON.parse(readFileSync9(seedManifestFile, "utf8"));
|
|
6576
|
+
const overlayManifest = JSON.parse(readFileSync9(overlayManifestFile, "utf8"));
|
|
6577
|
+
const policyManifest = JSON.parse(readFileSync9(policyFile, "utf8"));
|
|
6057
6578
|
verifyDeclaredFiles(seedManifest, seedRoot, "seed", [
|
|
6058
6579
|
"seed-manifest.json",
|
|
6059
6580
|
"npm/package-lock.json",
|
|
@@ -6157,8 +6678,8 @@ function verifyTrustedPiRuntimeAssertion(input) {
|
|
|
6157
6678
|
// src/amaster-runtime-daemon/workspace-status.mjs
|
|
6158
6679
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
6159
6680
|
import { createHash as createHash8 } from "node:crypto";
|
|
6160
|
-
import { existsSync as
|
|
6161
|
-
import { basename as basename5, extname, isAbsolute as isAbsolute7, join as
|
|
6681
|
+
import { existsSync as existsSync12, readdirSync as readdirSync8, readFileSync as readFileSync10, statSync as statSync6 } from "node:fs";
|
|
6682
|
+
import { basename as basename5, extname, isAbsolute as isAbsolute7, join as join13, relative as relative7, resolve as resolve10 } from "node:path";
|
|
6162
6683
|
var WORKSPACE_RUNTIME_SERVICES_FILENAME = ".amaster-runtime-services.json";
|
|
6163
6684
|
var ARTIFACT_EXTENSIONS = /* @__PURE__ */ new Map([
|
|
6164
6685
|
[".md", "markdown"],
|
|
@@ -6227,7 +6748,7 @@ function sanitizeTrackedChange(line) {
|
|
|
6227
6748
|
return isSafeRelativePath(path) ? line : null;
|
|
6228
6749
|
}
|
|
6229
6750
|
function sha256File2(filePath) {
|
|
6230
|
-
return createHash8("sha256").update(
|
|
6751
|
+
return createHash8("sha256").update(readFileSync10(filePath)).digest("hex");
|
|
6231
6752
|
}
|
|
6232
6753
|
function artifactHashCacheKey(relativePath, stat) {
|
|
6233
6754
|
return `${relativePath}\0${stat.size}\0${stat.mtimeMs}`;
|
|
@@ -6265,7 +6786,7 @@ function scanArtifactCandidates(cwd, opts = {}) {
|
|
|
6265
6786
|
const current = stack.pop();
|
|
6266
6787
|
let entries;
|
|
6267
6788
|
try {
|
|
6268
|
-
entries =
|
|
6789
|
+
entries = readdirSync8(current, { withFileTypes: true });
|
|
6269
6790
|
} catch {
|
|
6270
6791
|
continue;
|
|
6271
6792
|
}
|
|
@@ -6276,7 +6797,7 @@ function scanArtifactCandidates(cwd, opts = {}) {
|
|
|
6276
6797
|
if (entry.name.startsWith(".") && entry.name !== ".amaster-runtime.json") {
|
|
6277
6798
|
if (entry.name === ".git") continue;
|
|
6278
6799
|
}
|
|
6279
|
-
const fullPath =
|
|
6800
|
+
const fullPath = join13(current, entry.name);
|
|
6280
6801
|
const relativePath = normalizeRelativePath(root, fullPath);
|
|
6281
6802
|
if (!isSafeRelativePath(relativePath)) continue;
|
|
6282
6803
|
if (basename5(relativePath) === ".amaster-runtime.json") continue;
|
|
@@ -6355,10 +6876,10 @@ function sanitizeRuntimeService(entry) {
|
|
|
6355
6876
|
};
|
|
6356
6877
|
}
|
|
6357
6878
|
function readRuntimeServicesSnapshot(cwd) {
|
|
6358
|
-
const snapshotPath =
|
|
6359
|
-
if (!
|
|
6879
|
+
const snapshotPath = join13(resolve10(cwd), WORKSPACE_RUNTIME_SERVICES_FILENAME);
|
|
6880
|
+
if (!existsSync12(snapshotPath)) return [];
|
|
6360
6881
|
try {
|
|
6361
|
-
const parsed = JSON.parse(
|
|
6882
|
+
const parsed = JSON.parse(readFileSync10(snapshotPath, "utf8"));
|
|
6362
6883
|
const rawServices = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.services) ? parsed.services : [];
|
|
6363
6884
|
return rawServices.map((entry) => sanitizeRuntimeService(entry)).filter((entry) => entry !== null).slice(0, 50);
|
|
6364
6885
|
} catch {
|
|
@@ -7199,7 +7720,7 @@ async function executeSourceReadCommand(commandInput, dependencies = {}) {
|
|
|
7199
7720
|
// src/amaster-runtime-daemon/browser-session-broker.mjs
|
|
7200
7721
|
import { createHash as createHash11 } from "node:crypto";
|
|
7201
7722
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
7202
|
-
import { existsSync as
|
|
7723
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
7203
7724
|
import {
|
|
7204
7725
|
chmod,
|
|
7205
7726
|
lstat,
|
|
@@ -7211,7 +7732,7 @@ import {
|
|
|
7211
7732
|
rm,
|
|
7212
7733
|
writeFile
|
|
7213
7734
|
} from "node:fs/promises";
|
|
7214
|
-
import { join as
|
|
7735
|
+
import { join as join14, relative as relative8, resolve as resolve11, sep as sep2 } from "node:path";
|
|
7215
7736
|
|
|
7216
7737
|
// src/amaster-runtime-daemon/playwright-source-browser.mjs
|
|
7217
7738
|
var LOGIN_CONTROL_SELECTORS = Object.freeze([
|
|
@@ -7403,7 +7924,7 @@ function sha2562(value) {
|
|
|
7403
7924
|
return createHash11("sha256").update(value, "utf8").digest("hex");
|
|
7404
7925
|
}
|
|
7405
7926
|
function configuredBrowserExecutableReady(executablePath) {
|
|
7406
|
-
if (!executablePath || !
|
|
7927
|
+
if (!executablePath || !existsSync13(executablePath)) return false;
|
|
7407
7928
|
const probe = spawnSync5(executablePath, ["--version"], {
|
|
7408
7929
|
encoding: "utf8",
|
|
7409
7930
|
env: { PATH: process.env.PATH ?? "" },
|
|
@@ -7462,7 +7983,7 @@ function assertWithinRoot(root, target) {
|
|
|
7462
7983
|
}
|
|
7463
7984
|
}
|
|
7464
7985
|
async function readMarker(profilePath) {
|
|
7465
|
-
const markerPath =
|
|
7986
|
+
const markerPath = join14(profilePath, MARKER_NAME);
|
|
7466
7987
|
const metadata = await pathStat(markerPath, true);
|
|
7467
7988
|
if (!metadata || !metadata.isFile() || metadata.isSymbolicLink()) {
|
|
7468
7989
|
fail3("browser_session_profile_owner_mismatch");
|
|
@@ -7489,7 +8010,7 @@ async function verifyOwnedProfile(root, profilePath, identity2) {
|
|
|
7489
8010
|
return true;
|
|
7490
8011
|
}
|
|
7491
8012
|
async function writeMarker(profilePath, identity2) {
|
|
7492
|
-
const markerPath =
|
|
8013
|
+
const markerPath = join14(profilePath, MARKER_NAME);
|
|
7493
8014
|
const temporaryPath = `${markerPath}.tmp-${process.pid}-${Date.now()}`;
|
|
7494
8015
|
try {
|
|
7495
8016
|
await writeFile(temporaryPath, `${JSON.stringify(expectedMarker(identity2))}
|
|
@@ -7503,7 +8024,7 @@ async function writeMarker(profilePath, identity2) {
|
|
|
7503
8024
|
}
|
|
7504
8025
|
}
|
|
7505
8026
|
async function ensureOwnedProfile(root, identity2) {
|
|
7506
|
-
const profilePath =
|
|
8027
|
+
const profilePath = join14(root, profileName(identity2));
|
|
7507
8028
|
const exists = await verifyOwnedProfile(root, profilePath, identity2);
|
|
7508
8029
|
if (exists) return { profilePath, reusedProfile: true };
|
|
7509
8030
|
await mkdir(profilePath, { mode: 448 });
|
|
@@ -7514,13 +8035,13 @@ async function ensureOwnedProfile(root, identity2) {
|
|
|
7514
8035
|
}
|
|
7515
8036
|
async function assertNoSymlinks(path) {
|
|
7516
8037
|
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
7517
|
-
const entryPath =
|
|
8038
|
+
const entryPath = join14(path, entry.name);
|
|
7518
8039
|
if (entry.isSymbolicLink()) fail3("browser_session_profile_symlink_forbidden");
|
|
7519
8040
|
if (entry.isDirectory()) await assertNoSymlinks(entryPath);
|
|
7520
8041
|
}
|
|
7521
8042
|
}
|
|
7522
8043
|
async function removeOwnedProfile(root, identity2) {
|
|
7523
|
-
const profilePath =
|
|
8044
|
+
const profilePath = join14(root, profileName(identity2));
|
|
7524
8045
|
if (!await verifyOwnedProfile(root, profilePath, identity2)) return false;
|
|
7525
8046
|
await assertNoSymlinks(profilePath);
|
|
7526
8047
|
await rm(profilePath, { recursive: true });
|
|
@@ -7569,7 +8090,7 @@ function createBrowserSessionBroker(options) {
|
|
|
7569
8090
|
const cancelTimeout = typeof options.clearTimeout === "function" ? options.clearTimeout : clearTimeout;
|
|
7570
8091
|
const rootPromise = (async () => {
|
|
7571
8092
|
const stateRoot = await ensureProtectedDirectory(resolve11(options.stateRoot));
|
|
7572
|
-
return ensureProtectedDirectory(
|
|
8093
|
+
return ensureProtectedDirectory(join14(stateRoot, "browser-sessions"));
|
|
7573
8094
|
})();
|
|
7574
8095
|
const sessions = /* @__PURE__ */ new Map();
|
|
7575
8096
|
async function closeBinding(bindingId) {
|
|
@@ -7793,7 +8314,7 @@ function createBrowserSessionBroker(options) {
|
|
|
7793
8314
|
}
|
|
7794
8315
|
} else {
|
|
7795
8316
|
const root = await rootPromise;
|
|
7796
|
-
const profilePath =
|
|
8317
|
+
const profilePath = join14(root, profileName(input));
|
|
7797
8318
|
if (!await verifyOwnedProfile(root, profilePath, input)) {
|
|
7798
8319
|
fail3("browser_session_profile_missing");
|
|
7799
8320
|
}
|
|
@@ -7862,7 +8383,7 @@ function createBrowserSessionBroker(options) {
|
|
|
7862
8383
|
}
|
|
7863
8384
|
if (!browserExecutablePath && typeof playwright.chromium.executablePath === "function") {
|
|
7864
8385
|
const executablePath = playwright.chromium.executablePath();
|
|
7865
|
-
if (!executablePath || !
|
|
8386
|
+
if (!executablePath || !existsSync13(executablePath)) {
|
|
7866
8387
|
return { ready: false, reason: "chrome_unavailable" };
|
|
7867
8388
|
}
|
|
7868
8389
|
}
|
|
@@ -8145,7 +8666,7 @@ function createPublicNetworkScope(options = {}) {
|
|
|
8145
8666
|
}
|
|
8146
8667
|
|
|
8147
8668
|
// src/amaster-runtime-daemon.mjs
|
|
8148
|
-
var CONNECTOR_VERSION = "0.1.0-beta.
|
|
8669
|
+
var CONNECTOR_VERSION = "0.1.0-beta.50";
|
|
8149
8670
|
var CONNECTOR_CONTRACT_VERSION = "2026-06-04.v1";
|
|
8150
8671
|
var MAX_CHECKPOINT_BYTES = 20 * 1024 * 1024;
|
|
8151
8672
|
var CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -8160,6 +8681,8 @@ var PI_COMPLETION_OUTPUT_TYPES = /* @__PURE__ */ new Set(["agent_end", "approval
|
|
|
8160
8681
|
var PROCESS_GROUP_RSS_SAMPLE_MIN_INTERVAL_MS = 5e3;
|
|
8161
8682
|
var activeRunCommands = /* @__PURE__ */ new Map();
|
|
8162
8683
|
var pendingResultOutboxRunCommands = /* @__PURE__ */ new Map();
|
|
8684
|
+
var pendingRunCompletionCommands = /* @__PURE__ */ new Map();
|
|
8685
|
+
var runCompletionFlights = /* @__PURE__ */ new Map();
|
|
8163
8686
|
var localDispatchCommandIds = /* @__PURE__ */ new Set();
|
|
8164
8687
|
var workspaceStatusHashCache = /* @__PURE__ */ new Map();
|
|
8165
8688
|
var processGroupRssSampleCache = null;
|
|
@@ -8194,9 +8717,9 @@ function configuredBrowserExecutablePath(config) {
|
|
|
8194
8717
|
process.platform === "linux" ? "/usr/bin/google-chrome" : null,
|
|
8195
8718
|
process.platform === "linux" ? "/usr/bin/chromium" : null,
|
|
8196
8719
|
process.platform === "linux" ? "/usr/bin/chromium-browser" : null,
|
|
8197
|
-
process.platform === "win32" && process.env.PROGRAMFILES ?
|
|
8720
|
+
process.platform === "win32" && process.env.PROGRAMFILES ? join15(process.env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : null
|
|
8198
8721
|
].filter(Boolean);
|
|
8199
|
-
return candidates.find((candidate) =>
|
|
8722
|
+
return candidates.find((candidate) => existsSync14(candidate)) ?? explicit ?? null;
|
|
8200
8723
|
}
|
|
8201
8724
|
function runtimeBrowserSessionBroker(config) {
|
|
8202
8725
|
const executablePath = configuredBrowserExecutablePath(config);
|
|
@@ -8243,11 +8766,11 @@ function updateActiveRunWorkspaceManifest(commandId, manifestPath, patch = {}) {
|
|
|
8243
8766
|
}
|
|
8244
8767
|
function resultOutboxPendingCount(config) {
|
|
8245
8768
|
const dir = resultOutboxDir(config);
|
|
8246
|
-
if (!
|
|
8769
|
+
if (!existsSync14(dir)) return 0;
|
|
8247
8770
|
try {
|
|
8248
8771
|
let pending = 0;
|
|
8249
|
-
for (const file of
|
|
8250
|
-
if (readValidResultOutboxEntryOrQuarantine(config, file,
|
|
8772
|
+
for (const file of readdirSync9(dir).filter((name) => name.endsWith(".json"))) {
|
|
8773
|
+
if (readValidResultOutboxEntryOrQuarantine(config, file, join15(dir, file))) {
|
|
8251
8774
|
pending += 1;
|
|
8252
8775
|
}
|
|
8253
8776
|
}
|
|
@@ -8256,6 +8779,63 @@ function resultOutboxPendingCount(config) {
|
|
|
8256
8779
|
return 0;
|
|
8257
8780
|
}
|
|
8258
8781
|
}
|
|
8782
|
+
function runCompletionStateDir(config) {
|
|
8783
|
+
const explicit = readString(process.env.AMASTER_RUN_COMPLETION_STATE_DIR);
|
|
8784
|
+
if (explicit) return resolve12(expandHomePath(explicit));
|
|
8785
|
+
return join15(dirname9(stateFilePath(process.env)), "run-completion-state");
|
|
8786
|
+
}
|
|
8787
|
+
function runCompletionStateInvalidDir(config) {
|
|
8788
|
+
return join15(runCompletionStateDir(config), "invalid");
|
|
8789
|
+
}
|
|
8790
|
+
function quarantineInvalidRunCompletionState(config, filePath, reason) {
|
|
8791
|
+
const invalidDir = runCompletionStateInvalidDir(config);
|
|
8792
|
+
mkdirSync9(invalidDir, { recursive: true, mode: 448 });
|
|
8793
|
+
const destination = join15(invalidDir, basename6(filePath));
|
|
8794
|
+
try {
|
|
8795
|
+
renameSync6(filePath, destination);
|
|
8796
|
+
} catch {
|
|
8797
|
+
copyFileSync3(filePath, destination);
|
|
8798
|
+
unlinkSync2(filePath);
|
|
8799
|
+
}
|
|
8800
|
+
process.stderr.write(`AMaster daemon quarantined invalid run completion state ${basename6(filePath)}: ${reason}
|
|
8801
|
+
`);
|
|
8802
|
+
}
|
|
8803
|
+
function readValidRunCompletionStateOrQuarantine(config, filePath) {
|
|
8804
|
+
try {
|
|
8805
|
+
return readRunCompletionState(filePath);
|
|
8806
|
+
} catch (error) {
|
|
8807
|
+
quarantineInvalidRunCompletionState(
|
|
8808
|
+
config,
|
|
8809
|
+
filePath,
|
|
8810
|
+
error instanceof Error ? error.message : String(error)
|
|
8811
|
+
);
|
|
8812
|
+
return null;
|
|
8813
|
+
}
|
|
8814
|
+
}
|
|
8815
|
+
function runCompletionStateEntries(config) {
|
|
8816
|
+
return listRunCompletionStateFiles(runCompletionStateDir(config)).map((filePath) => ({ filePath, state: readValidRunCompletionStateOrQuarantine(config, filePath) })).filter((entry) => entry.state !== null);
|
|
8817
|
+
}
|
|
8818
|
+
function runCompletionStatePendingCount(config) {
|
|
8819
|
+
return runCompletionStateEntries(config).length;
|
|
8820
|
+
}
|
|
8821
|
+
function runCompletionStateActiveRunCommands(config) {
|
|
8822
|
+
const pending = runCompletionStatePendingCount(config);
|
|
8823
|
+
return runCompletionStateEntries(config).map(({ state }) => {
|
|
8824
|
+
const executor = asRecord(state.executor);
|
|
8825
|
+
const execution = asRecord(executor.closureExecution);
|
|
8826
|
+
const command = asRecord(state.command);
|
|
8827
|
+
return {
|
|
8828
|
+
commandId: state.commandId,
|
|
8829
|
+
...readString(commandRunId(command)) ? { runId: readString(commandRunId(command)) } : {},
|
|
8830
|
+
...readString(commandIssueId(command)) ? { issueId: readString(commandIssueId(command)) } : {},
|
|
8831
|
+
...readString(executor.kind) ? { executorKind: readString(executor.kind) } : {},
|
|
8832
|
+
...readString(execution.cwd) ? { workspacePath: readString(execution.cwd) } : {},
|
|
8833
|
+
phase: state.phase,
|
|
8834
|
+
outboxPending: pending,
|
|
8835
|
+
...readString(state.updatedAt) ? { executorCompletedAt: readString(state.updatedAt) } : {}
|
|
8836
|
+
};
|
|
8837
|
+
});
|
|
8838
|
+
}
|
|
8259
8839
|
function piCompletionOutputType(event) {
|
|
8260
8840
|
const type = readString(asRecord(event).type);
|
|
8261
8841
|
if (PI_COMPLETION_OUTPUT_TYPES.has(type ?? "")) return type;
|
|
@@ -8263,11 +8843,11 @@ function piCompletionOutputType(event) {
|
|
|
8263
8843
|
}
|
|
8264
8844
|
function resultOutboxActiveRunCommands(config) {
|
|
8265
8845
|
const dir = resultOutboxDir(config);
|
|
8266
|
-
if (!
|
|
8846
|
+
if (!existsSync14(dir)) return [];
|
|
8267
8847
|
const outboxPending = resultOutboxPendingCount(config);
|
|
8268
8848
|
const entries = [];
|
|
8269
|
-
for (const file of
|
|
8270
|
-
const entry = readValidResultOutboxEntryOrQuarantine(config, file,
|
|
8849
|
+
for (const file of readdirSync9(dir).filter((name) => name.endsWith(".json")).sort()) {
|
|
8850
|
+
const entry = readValidResultOutboxEntryOrQuarantine(config, file, join15(dir, file));
|
|
8271
8851
|
if (!entry) continue;
|
|
8272
8852
|
const activeRun = asRecord(entry.activeRun);
|
|
8273
8853
|
const commandId = readString(activeRun.commandId) ?? readString(entry.commandId);
|
|
@@ -8297,12 +8877,12 @@ function resultOutboxActiveRunCommands(config) {
|
|
|
8297
8877
|
}
|
|
8298
8878
|
function resultOutboxFailedRunCommands(config) {
|
|
8299
8879
|
const dir = resultOutboxInvalidDir(config);
|
|
8300
|
-
if (!
|
|
8880
|
+
if (!existsSync14(dir)) return [];
|
|
8301
8881
|
const entries = [];
|
|
8302
|
-
for (const file of
|
|
8882
|
+
for (const file of readdirSync9(dir).filter((name) => name.endsWith(".json")).sort().slice(-100)) {
|
|
8303
8883
|
let entry;
|
|
8304
8884
|
try {
|
|
8305
|
-
entry = asRecord(JSON.parse(
|
|
8885
|
+
entry = asRecord(JSON.parse(readFileSync11(join15(dir, file), "utf8")));
|
|
8306
8886
|
} catch {
|
|
8307
8887
|
continue;
|
|
8308
8888
|
}
|
|
@@ -8366,7 +8946,7 @@ function safeExpandPath(value) {
|
|
|
8366
8946
|
}
|
|
8367
8947
|
function safeJsonObjectFromFile(filePath) {
|
|
8368
8948
|
try {
|
|
8369
|
-
const parsed = JSON.parse(
|
|
8949
|
+
const parsed = JSON.parse(readFileSync11(filePath, "utf8"));
|
|
8370
8950
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
8371
8951
|
} catch {
|
|
8372
8952
|
return null;
|
|
@@ -8383,7 +8963,7 @@ function safeDirectorySummary(pathValue) {
|
|
|
8383
8963
|
}
|
|
8384
8964
|
let entryCount = 0;
|
|
8385
8965
|
let truncated = false;
|
|
8386
|
-
for (const name of
|
|
8966
|
+
for (const name of readdirSync9(pathValue)) {
|
|
8387
8967
|
if (name.startsWith(".")) continue;
|
|
8388
8968
|
entryCount += 1;
|
|
8389
8969
|
if (entryCount >= MAX_PI_CAPABILITY_SOURCE_ENTRIES) {
|
|
@@ -8407,12 +8987,12 @@ function safeSkillRootSummary(kind, source, pathValue) {
|
|
|
8407
8987
|
let truncated = false;
|
|
8408
8988
|
if (directory.available && pathValue) {
|
|
8409
8989
|
try {
|
|
8410
|
-
for (const name of
|
|
8990
|
+
for (const name of readdirSync9(pathValue)) {
|
|
8411
8991
|
if (name.startsWith(".")) continue;
|
|
8412
|
-
const skillDir =
|
|
8992
|
+
const skillDir = join15(pathValue, name);
|
|
8413
8993
|
try {
|
|
8414
8994
|
if (!statSync7(skillDir).isDirectory()) continue;
|
|
8415
|
-
if (!
|
|
8995
|
+
if (!existsSync14(join15(skillDir, "SKILL.md"))) continue;
|
|
8416
8996
|
skillCount += 1;
|
|
8417
8997
|
if (skillCount >= MAX_PI_CAPABILITY_SOURCE_ENTRIES) {
|
|
8418
8998
|
truncated = true;
|
|
@@ -8463,7 +9043,7 @@ function objectKeyCount(value) {
|
|
|
8463
9043
|
return value && typeof value === "object" && !Array.isArray(value) ? Object.keys(value).length : 0;
|
|
8464
9044
|
}
|
|
8465
9045
|
function defaultPiCodingAgentDir() {
|
|
8466
|
-
return
|
|
9046
|
+
return join15(homedir3(), ".pi", "agent");
|
|
8467
9047
|
}
|
|
8468
9048
|
function piCapabilitySourcesDiagnostics() {
|
|
8469
9049
|
const configuredPiCodingAgentDir = safeExpandPath(process.env.PI_CODING_AGENT_DIR);
|
|
@@ -8472,14 +9052,14 @@ function piCapabilitySourcesDiagnostics() {
|
|
|
8472
9052
|
const configuredPiAgentHome = safeExpandPath(process.env.PI_AGENT_HOME);
|
|
8473
9053
|
const piAgentHome = configuredPiAgentHome ?? piCodingAgentDir;
|
|
8474
9054
|
const piAgentHomeSource = configuredPiAgentHome ? "PI_AGENT_HOME" : piCodingAgentDirSource;
|
|
8475
|
-
const userSkillsPath = piAgentHome ?
|
|
9055
|
+
const userSkillsPath = piAgentHome ? join15(piAgentHome, "skills") : null;
|
|
8476
9056
|
const configuredMarketplaceSkillsPath = safeExpandPath(
|
|
8477
9057
|
process.env.PI_AGENT_MARKETPLACE_SKILLS_DIR
|
|
8478
9058
|
);
|
|
8479
|
-
const marketplaceSkillsPath = configuredMarketplaceSkillsPath ?? (piAgentHome ?
|
|
9059
|
+
const marketplaceSkillsPath = configuredMarketplaceSkillsPath ?? (piAgentHome ? join15(piAgentHome, "marketplace", "skills") : null);
|
|
8480
9060
|
const builtinSkillsPath = safeExpandPath(process.env.PI_AGENT_BUILTIN_SKILLS_DIR) ?? safeExpandPath(process.env.AMASTER_BUILTIN_SKILLS);
|
|
8481
|
-
const mcpConfigPath = safeExpandPath(process.env.PI_AGENT_MCP_SERVERS_FILE) ?? (piAgentHome ?
|
|
8482
|
-
const settingsConfigPath = piCodingAgentDir ?
|
|
9061
|
+
const mcpConfigPath = safeExpandPath(process.env.PI_AGENT_MCP_SERVERS_FILE) ?? (piAgentHome ? join15(piAgentHome, "mcp.json") : null);
|
|
9062
|
+
const settingsConfigPath = piCodingAgentDir ? join15(piCodingAgentDir, "settings.json") : null;
|
|
8483
9063
|
const skillRoots = [
|
|
8484
9064
|
safeSkillRootSummary("user", `${piAgentHomeSource}/skills`, userSkillsPath),
|
|
8485
9065
|
safeSkillRootSummary(
|
|
@@ -8664,6 +9244,8 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
8664
9244
|
...Array.from(localDispatchCommandIds, (commandId) => ({ commandId, phase: "preparing" })),
|
|
8665
9245
|
...Array.from(activeRunCommands.values()),
|
|
8666
9246
|
...Array.from(pendingResultOutboxRunCommands.values()),
|
|
9247
|
+
...Array.from(pendingRunCompletionCommands.values()),
|
|
9248
|
+
...runCompletionStateActiveRunCommands(config),
|
|
8667
9249
|
...resultOutboxFailedRunCommands(config),
|
|
8668
9250
|
...resultOutboxActiveRunCommands(config)
|
|
8669
9251
|
]) {
|
|
@@ -8725,10 +9307,10 @@ function piAgentSystemDataDir(config) {
|
|
|
8725
9307
|
return configured ? resolve12(expandHomePath(configured)) : null;
|
|
8726
9308
|
}
|
|
8727
9309
|
function readPiAgentLocalPlatformCredential(credentialsDir) {
|
|
8728
|
-
const pointer = readJsonFile3(
|
|
9310
|
+
const pointer = readJsonFile3(join15(credentialsDir, "latest.json"));
|
|
8729
9311
|
const credentialRef = readString(pointer.credentialRef);
|
|
8730
9312
|
if (!credentialRef || !/^[a-f0-9]{64}$/i.test(credentialRef)) return null;
|
|
8731
|
-
const credential = readJsonFile3(
|
|
9313
|
+
const credential = readJsonFile3(join15(credentialsDir, `${credentialRef}.json`));
|
|
8732
9314
|
const organizationId = readString(credential.organizationId);
|
|
8733
9315
|
const apiKey = readString(credential.apiKey);
|
|
8734
9316
|
if (credential.version !== 1 || !organizationId || !apiKey) return null;
|
|
@@ -8744,17 +9326,17 @@ function piAgentLocalPlatformCredentials(config) {
|
|
|
8744
9326
|
if (!piAgentLocalPlatformRunnerEnabled(config)) return [];
|
|
8745
9327
|
const systemDataDir = piAgentSystemDataDir(config);
|
|
8746
9328
|
if (!systemDataDir) return [];
|
|
8747
|
-
const companiesDir =
|
|
9329
|
+
const companiesDir = join15(systemDataDir, "companies");
|
|
8748
9330
|
let entries = [];
|
|
8749
9331
|
try {
|
|
8750
|
-
entries =
|
|
9332
|
+
entries = readdirSync9(companiesDir, { withFileTypes: true });
|
|
8751
9333
|
} catch {
|
|
8752
9334
|
return [];
|
|
8753
9335
|
}
|
|
8754
9336
|
const credentialsByOrganizationId = /* @__PURE__ */ new Map();
|
|
8755
9337
|
for (const entry of entries) {
|
|
8756
9338
|
if (!entry.isDirectory()) continue;
|
|
8757
|
-
const credential = readPiAgentLocalPlatformCredential(
|
|
9339
|
+
const credential = readPiAgentLocalPlatformCredential(join15(companiesDir, entry.name, "model-credentials"));
|
|
8758
9340
|
if (credential) credentialsByOrganizationId.set(credential.organizationId, credential);
|
|
8759
9341
|
}
|
|
8760
9342
|
return [...credentialsByOrganizationId.values()];
|
|
@@ -8928,7 +9510,7 @@ AMASTER_WORKSPACE_ALLOWLIST=${quoteShell(config.workspaceBindings.join(","))}
|
|
|
8928
9510
|
AMASTER_EXECUTORS=${quoteShell(executorEnv)}
|
|
8929
9511
|
AMASTER_CAPABILITIES=${quoteShell(config.capabilities.join(","))}
|
|
8930
9512
|
AMASTER_NETWORK_DOMAINS=${quoteShell(config.networkDomains.join(","))}
|
|
8931
|
-
AMASTER_DAEMON_STATE_FILE=${quoteShell(
|
|
9513
|
+
AMASTER_DAEMON_STATE_FILE=${quoteShell(join15(homedir3(), ".amaster-employee", "runtime-connector-state.json"))}
|
|
8932
9514
|
EOF
|
|
8933
9515
|
|
|
8934
9516
|
set -a
|
|
@@ -8982,17 +9564,19 @@ function maxConcurrentCommands(config) {
|
|
|
8982
9564
|
const configured = Number(config.maxConcurrentCommands);
|
|
8983
9565
|
return Number.isInteger(configured) && configured > 0 ? Math.min(configured, 50) : 1;
|
|
8984
9566
|
}
|
|
8985
|
-
function activeRunCommandCount(options = {}) {
|
|
9567
|
+
function activeRunCommandCount(config, options = {}) {
|
|
8986
9568
|
const excludeCommandIds = new Set(readStringArray(options.excludeCommandIds));
|
|
8987
9569
|
const commandIds = new Set([
|
|
8988
9570
|
...activeRunCommands.keys(),
|
|
8989
9571
|
...pendingResultOutboxRunCommands.keys(),
|
|
9572
|
+
...pendingRunCompletionCommands.keys(),
|
|
9573
|
+
...runCompletionStateEntries(config).map(({ state }) => state.commandId),
|
|
8990
9574
|
...localDispatchCommandIds
|
|
8991
9575
|
].filter((commandId) => !excludeCommandIds.has(commandId)));
|
|
8992
9576
|
return commandIds.size;
|
|
8993
9577
|
}
|
|
8994
9578
|
function availableCommandSlots(config) {
|
|
8995
|
-
return Math.max(0, maxConcurrentCommands(config) - activeRunCommandCount());
|
|
9579
|
+
return Math.max(0, maxConcurrentCommands(config) - activeRunCommandCount(config));
|
|
8996
9580
|
}
|
|
8997
9581
|
function rememberActiveRunCommand(command, metadata) {
|
|
8998
9582
|
const commandId = readString(command.commandId) ?? readString(command.id);
|
|
@@ -9116,10 +9700,10 @@ function buildActiveRunCommandStatus(config, entry) {
|
|
|
9116
9700
|
const base = {
|
|
9117
9701
|
...entry,
|
|
9118
9702
|
phase: readString(entry.phase) ?? "executing",
|
|
9119
|
-
managedWorkdirPresent: entry.workspacePath ?
|
|
9703
|
+
managedWorkdirPresent: entry.workspacePath ? existsSync14(entry.workspacePath) : false,
|
|
9120
9704
|
outboxPending: resultOutboxPendingCount(config)
|
|
9121
9705
|
};
|
|
9122
|
-
if (!entry.workspacePath || !
|
|
9706
|
+
if (!entry.workspacePath || !existsSync14(entry.workspacePath)) return base;
|
|
9123
9707
|
const manifestPath = entry.manifestPath ?? workspaceManifestPath(entry.workspacePath);
|
|
9124
9708
|
const status = readWorkspaceStatus(entry.workspacePath, { hashCache: workspaceStatusHashCache });
|
|
9125
9709
|
const artifactCandidates = status.artifacts.slice(0, 20);
|
|
@@ -9300,7 +9884,7 @@ function trustedPiRuntimeSources(config) {
|
|
|
9300
9884
|
}
|
|
9301
9885
|
function readJsonFile3(filePath) {
|
|
9302
9886
|
try {
|
|
9303
|
-
const parsed = JSON.parse(
|
|
9887
|
+
const parsed = JSON.parse(readFileSync11(filePath, "utf8"));
|
|
9304
9888
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
9305
9889
|
} catch {
|
|
9306
9890
|
return {};
|
|
@@ -9387,8 +9971,8 @@ async function materializeAgentInstructionsBundle(config, command, workspace) {
|
|
|
9387
9971
|
if (!content) continue;
|
|
9388
9972
|
const target = safeAgentInstructionMaterializationTarget(workspace, filePath);
|
|
9389
9973
|
if (!target) continue;
|
|
9390
|
-
|
|
9391
|
-
|
|
9974
|
+
mkdirSync9(dirname9(target.targetPath), { recursive: true });
|
|
9975
|
+
writeFileSync9(target.targetPath, content, "utf8");
|
|
9392
9976
|
materialized.push({
|
|
9393
9977
|
path: target.relativePath,
|
|
9394
9978
|
byteSize: Buffer.byteLength(content, "utf8")
|
|
@@ -9459,14 +10043,14 @@ function companyPiHomeRoot(baseEnv) {
|
|
|
9459
10043
|
const explicitRoot = readString(baseEnv.AMASTER_COMPANY_PI_HOME_ROOT);
|
|
9460
10044
|
if (explicitRoot) return resolve12(expandHomePath(explicitRoot));
|
|
9461
10045
|
const configuredPiHome = readString(baseEnv.PI_AGENT_HOME) ?? readString(baseEnv.PI_CODING_AGENT_DIR);
|
|
9462
|
-
if (configuredPiHome) return
|
|
9463
|
-
return
|
|
10046
|
+
if (configuredPiHome) return join15(dirname9(resolve12(expandHomePath(configuredPiHome))), "companies");
|
|
10047
|
+
return join15(homedir3(), ".amaster-employee", "companies");
|
|
9464
10048
|
}
|
|
9465
10049
|
function companyPiAgentHome(baseEnv, companyId) {
|
|
9466
10050
|
const rawCompanyId = readString(companyId);
|
|
9467
10051
|
if (!rawCompanyId) return null;
|
|
9468
10052
|
const segment = safeCompanyPiHomeSegment(rawCompanyId);
|
|
9469
|
-
return
|
|
10053
|
+
return join15(companyPiHomeRoot(baseEnv), segment, ".pi");
|
|
9470
10054
|
}
|
|
9471
10055
|
function commandUsesPiExecutor(command) {
|
|
9472
10056
|
return readString(asRecord(command.payload).executorKind) === "pi";
|
|
@@ -9534,7 +10118,7 @@ function resolveNativeSessionRequest(command, workspace) {
|
|
|
9534
10118
|
let cwdMatched = false;
|
|
9535
10119
|
if (requestedCwd && sourceWorkspacePath) {
|
|
9536
10120
|
try {
|
|
9537
|
-
cwdMatched =
|
|
10121
|
+
cwdMatched = realpathSync5(requestedCwd) === realpathSync5(sourceWorkspacePath);
|
|
9538
10122
|
} catch {
|
|
9539
10123
|
cwdMatched = resolve12(requestedCwd) === resolve12(sourceWorkspacePath);
|
|
9540
10124
|
}
|
|
@@ -9979,16 +10563,16 @@ function sampleProcessGroupRssBytes(processGroupId) {
|
|
|
9979
10563
|
}
|
|
9980
10564
|
function realOrResolvedPath(value) {
|
|
9981
10565
|
try {
|
|
9982
|
-
return
|
|
10566
|
+
return realpathSync5(value);
|
|
9983
10567
|
} catch {
|
|
9984
10568
|
return resolve12(value);
|
|
9985
10569
|
}
|
|
9986
10570
|
}
|
|
9987
|
-
var LSOF_COMMAND = process.platform === "darwin" &&
|
|
10571
|
+
var LSOF_COMMAND = process.platform === "darwin" && existsSync14("/usr/sbin/lsof") ? "/usr/sbin/lsof" : "lsof";
|
|
9988
10572
|
function processCwdForPid(pid) {
|
|
9989
10573
|
if (process.platform === "linux") {
|
|
9990
10574
|
try {
|
|
9991
|
-
return
|
|
10575
|
+
return realpathSync5(`/proc/${pid}/cwd`);
|
|
9992
10576
|
} catch {
|
|
9993
10577
|
return null;
|
|
9994
10578
|
}
|
|
@@ -10010,7 +10594,7 @@ function allProcessCwdsByPid() {
|
|
|
10010
10594
|
if (process.platform === "linux") {
|
|
10011
10595
|
const procEntries = (() => {
|
|
10012
10596
|
try {
|
|
10013
|
-
return
|
|
10597
|
+
return readdirSync9("/proc", { withFileTypes: true });
|
|
10014
10598
|
} catch {
|
|
10015
10599
|
return [];
|
|
10016
10600
|
}
|
|
@@ -10106,21 +10690,21 @@ function killWorkspaceResidentProcesses(cwd, processGroupId, options = {}) {
|
|
|
10106
10690
|
}
|
|
10107
10691
|
function walkManagedWorkdirs(root) {
|
|
10108
10692
|
const workdirs = [];
|
|
10109
|
-
if (!root || !
|
|
10693
|
+
if (!root || !existsSync14(root)) return workdirs;
|
|
10110
10694
|
const stack = [root];
|
|
10111
10695
|
while (stack.length > 0) {
|
|
10112
10696
|
const current = stack.pop();
|
|
10113
10697
|
if (!current) continue;
|
|
10114
10698
|
let entries = [];
|
|
10115
10699
|
try {
|
|
10116
|
-
entries =
|
|
10700
|
+
entries = readdirSync9(current, { withFileTypes: true });
|
|
10117
10701
|
} catch {
|
|
10118
10702
|
continue;
|
|
10119
10703
|
}
|
|
10120
10704
|
for (const entry of entries) {
|
|
10121
10705
|
if (!entry.isDirectory()) continue;
|
|
10122
|
-
const fullPath =
|
|
10123
|
-
if (entry.name === "workdir" &&
|
|
10706
|
+
const fullPath = join15(current, entry.name);
|
|
10707
|
+
if (entry.name === "workdir" && existsSync14(workspaceManifestPath(fullPath))) {
|
|
10124
10708
|
workdirs.push(fullPath);
|
|
10125
10709
|
continue;
|
|
10126
10710
|
}
|
|
@@ -10525,6 +11109,874 @@ async function ingestCost(config, command, executor, parsed) {
|
|
|
10525
11109
|
costCents: parsedUsageCostCents(parsed.usage)
|
|
10526
11110
|
});
|
|
10527
11111
|
}
|
|
11112
|
+
function pendingRunCompletionSnapshot(config, state) {
|
|
11113
|
+
const command = asRecord(state.command);
|
|
11114
|
+
const executor = asRecord(state.executor);
|
|
11115
|
+
const execution = asRecord(executor.closureExecution);
|
|
11116
|
+
return {
|
|
11117
|
+
commandId: state.commandId,
|
|
11118
|
+
...readString(commandRunId(command)) ? { runId: readString(commandRunId(command)) } : {},
|
|
11119
|
+
...readString(commandIssueId(command)) ? { issueId: readString(commandIssueId(command)) } : {},
|
|
11120
|
+
...readString(executor.kind) ? { executorKind: readString(executor.kind) } : {},
|
|
11121
|
+
...readString(execution.cwd) ? { workspacePath: readString(execution.cwd) } : {},
|
|
11122
|
+
phase: state.phase,
|
|
11123
|
+
outboxPending: runCompletionStatePendingCount(config),
|
|
11124
|
+
...readString(state.updatedAt) ? { executorCompletedAt: readString(state.updatedAt) } : {}
|
|
11125
|
+
};
|
|
11126
|
+
}
|
|
11127
|
+
function persistPendingRunCompletion(config, state) {
|
|
11128
|
+
writeRunCompletionState(runCompletionStateDir(config), state);
|
|
11129
|
+
pendingRunCompletionCommands.set(state.commandId, pendingRunCompletionSnapshot(config, state));
|
|
11130
|
+
return state;
|
|
11131
|
+
}
|
|
11132
|
+
function clearPendingRunCompletion(config, commandId) {
|
|
11133
|
+
removeRunCompletionState(runCompletionStateDir(config), commandId);
|
|
11134
|
+
pendingRunCompletionCommands.delete(commandId);
|
|
11135
|
+
}
|
|
11136
|
+
function completionCheckRetryable(error) {
|
|
11137
|
+
const status = Number(error?.httpStatus);
|
|
11138
|
+
const code = runtimeConnectorErrorCode(error) ?? readString(error?.code);
|
|
11139
|
+
if (code === "runtime_completion_check_response_invalid") return false;
|
|
11140
|
+
if (!Number.isInteger(status)) return true;
|
|
11141
|
+
if (status === 401 || status === 408 || status === 429 || status >= 500) return true;
|
|
11142
|
+
if (status !== 409) return false;
|
|
11143
|
+
return !code || [
|
|
11144
|
+
"runtime_completion_check_command_race",
|
|
11145
|
+
"runtime_completion_check_command_not_acknowledged"
|
|
11146
|
+
].includes(code);
|
|
11147
|
+
}
|
|
11148
|
+
function runtimeConnectorErrorCode(error) {
|
|
11149
|
+
const body = asRecord(error?.responseBody);
|
|
11150
|
+
const details = asRecord(body.details);
|
|
11151
|
+
return readString(details.code) ?? readString(body.code);
|
|
11152
|
+
}
|
|
11153
|
+
function terminalCommandStatusFromCompletionCheckError(error) {
|
|
11154
|
+
if (runtimeConnectorErrorCode(error) !== "runtime_completion_check_command_not_acknowledged") {
|
|
11155
|
+
return null;
|
|
11156
|
+
}
|
|
11157
|
+
const details = asRecord(asRecord(error?.responseBody).details);
|
|
11158
|
+
const status = readString(details.status);
|
|
11159
|
+
return ["succeeded", "failed", "cancelled"].includes(status ?? "") ? status : null;
|
|
11160
|
+
}
|
|
11161
|
+
function managedMcpCleanupOwnerMismatch(error) {
|
|
11162
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11163
|
+
return /^(?:pi|codex)_managed_mcp_cleanup_owner_mismatch:/.test(message);
|
|
11164
|
+
}
|
|
11165
|
+
async function probeTerminalCommandAfterUnsafeCleanup(config, state) {
|
|
11166
|
+
const path = `/api/amaster/runtime-connectors/${state.connectorId}/commands/${state.commandId}/completion-check`;
|
|
11167
|
+
try {
|
|
11168
|
+
await postJson(config, path, runCompletionCheckRequest(state));
|
|
11169
|
+
return null;
|
|
11170
|
+
} catch (error) {
|
|
11171
|
+
const terminalStatus = terminalCommandStatusFromCompletionCheckError(error);
|
|
11172
|
+
if (terminalStatus) return terminalStatus;
|
|
11173
|
+
throw error;
|
|
11174
|
+
}
|
|
11175
|
+
}
|
|
11176
|
+
function runCompletionCheckRequest(state) {
|
|
11177
|
+
const prior = asRecord(state.completionRequest);
|
|
11178
|
+
return {
|
|
11179
|
+
contractVersion: "amaster.runtime-connector.completion-check.v1",
|
|
11180
|
+
...readString(asRecord(state.command).leaseId) ? { leaseId: readString(asRecord(state.command).leaseId) } : {},
|
|
11181
|
+
proposedStatus: state.candidateStatus,
|
|
11182
|
+
resultSignals: {
|
|
11183
|
+
productiveSuccessfulRun: prior.resultSignals?.productiveSuccessfulRun === true,
|
|
11184
|
+
executorTurnCount: state.closureAttempt === 1 ? 2 : 1,
|
|
11185
|
+
closureAttempt: state.closureAttempt === 1 ? 1 : 0
|
|
11186
|
+
}
|
|
11187
|
+
};
|
|
11188
|
+
}
|
|
11189
|
+
function runCompletionCheckMaxAttempts() {
|
|
11190
|
+
return Math.max(1, Math.min(5, parsePositiveInteger(
|
|
11191
|
+
process.env.AMASTER_COMPLETION_CHECK_MAX_ATTEMPTS,
|
|
11192
|
+
3
|
|
11193
|
+
)));
|
|
11194
|
+
}
|
|
11195
|
+
function runCompletionCheckRetryDelayMs() {
|
|
11196
|
+
return Math.max(0, Math.min(5e3, parseNonNegativeInteger(
|
|
11197
|
+
process.env.AMASTER_COMPLETION_CHECK_RETRY_DELAY_MS,
|
|
11198
|
+
100
|
|
11199
|
+
)));
|
|
11200
|
+
}
|
|
11201
|
+
function runCompletionPhaseMaxAttempts() {
|
|
11202
|
+
return Math.max(1, Math.min(100, parsePositiveInteger(
|
|
11203
|
+
process.env.AMASTER_RUN_COMPLETION_PHASE_MAX_ATTEMPTS,
|
|
11204
|
+
10
|
|
11205
|
+
)));
|
|
11206
|
+
}
|
|
11207
|
+
function runCompletionMaxAgeMs() {
|
|
11208
|
+
return parsePositiveInteger(
|
|
11209
|
+
process.env.AMASTER_RUN_COMPLETION_MAX_AGE_SECONDS,
|
|
11210
|
+
7 * 24 * 60 * 60
|
|
11211
|
+
) * 1e3;
|
|
11212
|
+
}
|
|
11213
|
+
function runCompletionStateAgeExceeded(state, nowMs = Date.now()) {
|
|
11214
|
+
const createdAtMs = Date.parse(readString(state.createdAt) ?? "");
|
|
11215
|
+
return Number.isFinite(createdAtMs) && nowMs - createdAtMs > runCompletionMaxAgeMs();
|
|
11216
|
+
}
|
|
11217
|
+
function runCompletionPhaseExhausted(state, nextAttemptCount) {
|
|
11218
|
+
return nextAttemptCount >= runCompletionPhaseMaxAttempts() || runCompletionStateAgeExceeded(state);
|
|
11219
|
+
}
|
|
11220
|
+
function failedRunCompletionCandidate(state, errorCode, message, evidence = {}) {
|
|
11221
|
+
return {
|
|
11222
|
+
candidateStatus: "failed",
|
|
11223
|
+
candidateError: message,
|
|
11224
|
+
candidateResult: {
|
|
11225
|
+
...asRecord(state.candidateResult),
|
|
11226
|
+
errorCode,
|
|
11227
|
+
completionDeliveryFailure: {
|
|
11228
|
+
phase: state.phase,
|
|
11229
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11230
|
+
...evidence
|
|
11231
|
+
}
|
|
11232
|
+
}
|
|
11233
|
+
};
|
|
11234
|
+
}
|
|
11235
|
+
async function requestRunCompletionCheck(config, inputState) {
|
|
11236
|
+
let state = inputState;
|
|
11237
|
+
if (state.phase === "usage_snapshot_persisted") {
|
|
11238
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11239
|
+
state,
|
|
11240
|
+
"completion_check_requested",
|
|
11241
|
+
{ completionRequest: runCompletionCheckRequest(state) }
|
|
11242
|
+
));
|
|
11243
|
+
} else if (state.phase === "completion_check_outbox" || state.phase === "check_failed_retry") {
|
|
11244
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11245
|
+
state,
|
|
11246
|
+
"completion_retry_scheduled",
|
|
11247
|
+
{ completionRequest: runCompletionCheckRequest(state) }
|
|
11248
|
+
));
|
|
11249
|
+
} else if (state.phase === "closure_executing") {
|
|
11250
|
+
state = persistPendingRunCompletion(config, {
|
|
11251
|
+
...state,
|
|
11252
|
+
completionRequest: runCompletionCheckRequest(state),
|
|
11253
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11254
|
+
});
|
|
11255
|
+
}
|
|
11256
|
+
if (state.phase !== "completion_check_pending" && state.phase !== "closure_executing") {
|
|
11257
|
+
throw new Error(`run_completion_check_phase_invalid:${state.phase}`);
|
|
11258
|
+
}
|
|
11259
|
+
const path = `/api/amaster/runtime-connectors/${state.connectorId}/commands/${state.commandId}/completion-check`;
|
|
11260
|
+
const maxAttempts = runCompletionCheckMaxAttempts();
|
|
11261
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
11262
|
+
try {
|
|
11263
|
+
const response = asRecord(await postJson(config, path, runCompletionCheckRequest(state)));
|
|
11264
|
+
if (response.contractVersion !== "amaster.runtime-connector.completion-check.v1" || !["ready_to_terminalize", "disposition_required"].includes(response.outcome) || !["observe", "enforce"].includes(response.enforcementMode)) {
|
|
11265
|
+
const error = new Error("runtime_completion_check_response_invalid");
|
|
11266
|
+
error.code = "runtime_completion_check_response_invalid";
|
|
11267
|
+
throw error;
|
|
11268
|
+
}
|
|
11269
|
+
const event = response.outcome === "disposition_required" && response.enforcementMode === "enforce" ? "completion_disposition_required" : "completion_ready";
|
|
11270
|
+
state = transitionRunCompletionState(state, event, {
|
|
11271
|
+
completionResponse: response,
|
|
11272
|
+
candidateResult: {
|
|
11273
|
+
...asRecord(state.candidateResult),
|
|
11274
|
+
completionObservation: {
|
|
11275
|
+
outcome: response.outcome,
|
|
11276
|
+
enforcementMode: response.enforcementMode,
|
|
11277
|
+
snapshotHash: readString(response.snapshotHash),
|
|
11278
|
+
...readString(response.reasonCode) ? { reasonCode: readString(response.reasonCode) } : {}
|
|
11279
|
+
}
|
|
11280
|
+
}
|
|
11281
|
+
});
|
|
11282
|
+
return { pending: false, state: persistPendingRunCompletion(config, state) };
|
|
11283
|
+
} catch (error) {
|
|
11284
|
+
const terminalStatus = terminalCommandStatusFromCompletionCheckError(error);
|
|
11285
|
+
if (terminalStatus) {
|
|
11286
|
+
clearPendingRunCompletion(config, state.commandId);
|
|
11287
|
+
return { pending: false, state, serverTerminalStatus: terminalStatus };
|
|
11288
|
+
}
|
|
11289
|
+
if (!completionCheckRetryable(error)) {
|
|
11290
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
11291
|
+
state = transitionRunCompletionState(state, "completion_authority_rejected", {
|
|
11292
|
+
candidateStatus: "failed",
|
|
11293
|
+
candidateError: message2,
|
|
11294
|
+
candidateResult: {
|
|
11295
|
+
...asRecord(state.candidateResult),
|
|
11296
|
+
errorCode: runtimeConnectorErrorCode(error) ?? readString(error?.code) ?? "runtime_completion_check_authority_rejected",
|
|
11297
|
+
completionCheckAbort: {
|
|
11298
|
+
httpStatus: Number(error?.httpStatus) || null,
|
|
11299
|
+
reasonCode: runtimeConnectorErrorCode(error)
|
|
11300
|
+
}
|
|
11301
|
+
}
|
|
11302
|
+
});
|
|
11303
|
+
return { pending: false, state: persistPendingRunCompletion(config, state) };
|
|
11304
|
+
}
|
|
11305
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11306
|
+
const completionCheckAttemptCount = readNumber(state.completionCheckAttemptCount, 0) + 1;
|
|
11307
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11308
|
+
state,
|
|
11309
|
+
"completion_check_failed",
|
|
11310
|
+
{
|
|
11311
|
+
lastCompletionCheckError: truncateText(message, 1e3),
|
|
11312
|
+
completionCheckAttemptCount
|
|
11313
|
+
}
|
|
11314
|
+
));
|
|
11315
|
+
if (runCompletionPhaseExhausted(state, completionCheckAttemptCount)) {
|
|
11316
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11317
|
+
state,
|
|
11318
|
+
"completion_retry_abandoned",
|
|
11319
|
+
{
|
|
11320
|
+
...failedRunCompletionCandidate(
|
|
11321
|
+
state,
|
|
11322
|
+
"runtime_completion_check_retry_exhausted",
|
|
11323
|
+
`Completion check retry boundary exhausted: ${message}`,
|
|
11324
|
+
{ attemptCount: completionCheckAttemptCount }
|
|
11325
|
+
),
|
|
11326
|
+
completionCheckAbandonedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11327
|
+
}
|
|
11328
|
+
));
|
|
11329
|
+
process.stderr.write(`AMaster daemon abandoned completion check ${state.commandId} after ${completionCheckAttemptCount} durable attempt(s); terminalizing failed.
|
|
11330
|
+
`);
|
|
11331
|
+
return { pending: false, state };
|
|
11332
|
+
}
|
|
11333
|
+
if (attempt >= maxAttempts) {
|
|
11334
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11335
|
+
state,
|
|
11336
|
+
"completion_retry_exhausted",
|
|
11337
|
+
{ completionCheckOutboxQueuedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
11338
|
+
));
|
|
11339
|
+
process.stderr.write(`AMaster daemon queued completion check ${state.commandId} for durable retry after ${attempt} attempt(s): ${message}
|
|
11340
|
+
`);
|
|
11341
|
+
return { pending: true, state };
|
|
11342
|
+
}
|
|
11343
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11344
|
+
state,
|
|
11345
|
+
"completion_retry_scheduled"
|
|
11346
|
+
));
|
|
11347
|
+
const delayMs = runCompletionCheckRetryDelayMs();
|
|
11348
|
+
if (delayMs > 0) await new Promise((resolveDelay) => setTimeout(resolveDelay, delayMs));
|
|
11349
|
+
}
|
|
11350
|
+
}
|
|
11351
|
+
return { pending: true, state };
|
|
11352
|
+
}
|
|
11353
|
+
function runExitClosurePrompt(state) {
|
|
11354
|
+
const command = asRecord(state.command);
|
|
11355
|
+
const response = asRecord(state.completionResponse);
|
|
11356
|
+
const summary = truncateText(readString(asRecord(state.candidateResult).summary) ?? "", 1200);
|
|
11357
|
+
const issueId = commandIssueId(command) ?? "unknown";
|
|
11358
|
+
return [
|
|
11359
|
+
"# AMaster Run-Exit Disposition Closure",
|
|
11360
|
+
"This is a bounded status-only closure turn for the same command and heartbeat run.",
|
|
11361
|
+
`Issue: ${issueId}`,
|
|
11362
|
+
`Completion reason: ${readString(response.reasonCode) ?? "successful_run_missing_state"}`,
|
|
11363
|
+
summary ? `Primary turn summary: ${summary}` : null,
|
|
11364
|
+
"Do not perform more business work, edit files, create artifacts/documents/children/plans, or mutate company/profile/milestone state.",
|
|
11365
|
+
"Use exactly one runtime_action.submit mutation. Choose update_parent, create_interaction (not suggest_tasks), or add_comment only when a comment is truly the intended exit path.",
|
|
11366
|
+
"A comment alone does not establish completion. Prefer update_parent with the truthful done, blocked, review/input, or todo-continuation state.",
|
|
11367
|
+
`The idempotencyKey must be runtime-command-closure:${state.commandId}.`,
|
|
11368
|
+
"Do not call runtime_action.plan or runtime_action.commit. Stop immediately after the one allowed mutation returns."
|
|
11369
|
+
].filter(Boolean).join("\n\n");
|
|
11370
|
+
}
|
|
11371
|
+
function parseExecutorTurnOutput(executorKind, execution, liveOutputLogger, hasOutputFlood) {
|
|
11372
|
+
const parsed = hasOutputFlood ? {
|
|
11373
|
+
sessionId: null,
|
|
11374
|
+
summary: "",
|
|
11375
|
+
usage: { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 },
|
|
11376
|
+
errorMessage: null
|
|
11377
|
+
} : executorKind === "codex" ? parseCodexJsonl(execution.stdout) : executorKind === "claude" ? parseClaudeStreamJson(execution.stdout) : executorKind === "opencode" ? parseOpenCodeJsonl(execution.stdout) : executorKind === "pi" ? parsePiJsonl(execution.stdout) : parseGenericOutput(execution.stdout, execution.stderr);
|
|
11378
|
+
parsed.sessionId ??= liveOutputLogger.sessionId();
|
|
11379
|
+
const liveTerminalOutput = executorKind === "pi" && !hasOutputFlood ? liveOutputLogger.terminalOutput() : null;
|
|
11380
|
+
if (liveTerminalOutput) {
|
|
11381
|
+
const parsedUsage = asRecord(parsed.usage);
|
|
11382
|
+
const liveUsage = asRecord(liveTerminalOutput.usage);
|
|
11383
|
+
parsed.summary = liveTerminalOutput.summary;
|
|
11384
|
+
parsed.hasAssistantOutput = true;
|
|
11385
|
+
parsed.usage = {
|
|
11386
|
+
inputTokens: readNumber(liveUsage.inputTokens, 0) || readNumber(parsedUsage.inputTokens, 0),
|
|
11387
|
+
cachedInputTokens: readNumber(liveUsage.cachedInputTokens, 0) || readNumber(parsedUsage.cachedInputTokens, 0),
|
|
11388
|
+
outputTokens: readNumber(liveUsage.outputTokens, 0) || readNumber(parsedUsage.outputTokens, 0),
|
|
11389
|
+
...readNumber(liveUsage.costUsd, 0) > 0 || readNumber(parsedUsage.costUsd, 0) > 0 ? { costUsd: readNumber(liveUsage.costUsd, 0) || readNumber(parsedUsage.costUsd, 0) } : {}
|
|
11390
|
+
};
|
|
11391
|
+
}
|
|
11392
|
+
return parsed;
|
|
11393
|
+
}
|
|
11394
|
+
async function executeRunExitClosureTurn(config, inputState, options = {}) {
|
|
11395
|
+
const state = inputState;
|
|
11396
|
+
const command = asRecord(state.command);
|
|
11397
|
+
const executor = asRecord(state.executor);
|
|
11398
|
+
const executionConfig = asRecord(executor.closureExecution);
|
|
11399
|
+
const executorKind = readString(executor.kind);
|
|
11400
|
+
const executable = readString(executionConfig.command);
|
|
11401
|
+
const cwd = readString(executionConfig.cwd);
|
|
11402
|
+
if (!executorKind || !executable || !cwd) {
|
|
11403
|
+
throw new Error("run_exit_closure_execution_context_missing");
|
|
11404
|
+
}
|
|
11405
|
+
const args = Array.isArray(executionConfig.args) ? executionConfig.args.map(String) : [];
|
|
11406
|
+
const env = Object.fromEntries(Object.entries(asRecord(executionConfig.env)).map(([key, value]) => [key, String(value)]));
|
|
11407
|
+
const protectedValues = Array.isArray(executionConfig.protectedValues) ? executionConfig.protectedValues.filter((value) => typeof value === "string" && value) : [];
|
|
11408
|
+
const storedSpawnIdentity = asRecord(executionConfig.spawnIdentity);
|
|
11409
|
+
let reservedPiAssignment = false;
|
|
11410
|
+
const piAllocator = executorKind === "pi" && readNumber(storedSpawnIdentity.uid, 0) > 0 ? configuredPiChildIdentityAllocator(config) : null;
|
|
11411
|
+
if (piAllocator) {
|
|
11412
|
+
const assignment = piAllocator.acquire(commandRunId(command), {
|
|
11413
|
+
...readNumber(storedSpawnIdentity.gid, 0) > 0 ? { gid: readNumber(storedSpawnIdentity.gid, 0) } : {}
|
|
11414
|
+
});
|
|
11415
|
+
if (assignment.uid !== readNumber(storedSpawnIdentity.uid, 0)) {
|
|
11416
|
+
piAllocator.release(commandRunId(command));
|
|
11417
|
+
throw new Error("run_exit_closure_pi_identity_changed");
|
|
11418
|
+
}
|
|
11419
|
+
reservedPiAssignment = true;
|
|
11420
|
+
}
|
|
11421
|
+
let forgetTrackedRun = () => {
|
|
11422
|
+
};
|
|
11423
|
+
let stopTrackedHeartbeats = () => {
|
|
11424
|
+
};
|
|
11425
|
+
let signal = options.signal;
|
|
11426
|
+
if (!signal) {
|
|
11427
|
+
forgetTrackedRun = rememberActiveRunCommand(command, {
|
|
11428
|
+
executorKind,
|
|
11429
|
+
workspacePath: cwd,
|
|
11430
|
+
sourceWorkspacePath: readString(executionConfig.sourceWorkspacePath),
|
|
11431
|
+
managedWorkdir: executionConfig.managedWorkdir === true,
|
|
11432
|
+
manifestPath: readString(executionConfig.manifestPath)
|
|
11433
|
+
});
|
|
11434
|
+
const controller = new AbortController();
|
|
11435
|
+
signal = controller.signal;
|
|
11436
|
+
stopTrackedHeartbeats = startActiveRunHeartbeats(config, command, controller);
|
|
11437
|
+
}
|
|
11438
|
+
const liveOutputLogger = createLiveOutputLogger(
|
|
11439
|
+
config,
|
|
11440
|
+
command,
|
|
11441
|
+
executorKind,
|
|
11442
|
+
protectedValues
|
|
11443
|
+
);
|
|
11444
|
+
try {
|
|
11445
|
+
await ingestLog(config, command, "system", "info", "Starting bounded run-exit disposition closure turn", {
|
|
11446
|
+
presentationKind: "run_exit_disposition_closure",
|
|
11447
|
+
commandId: state.commandId,
|
|
11448
|
+
closureAttempt: 1
|
|
11449
|
+
});
|
|
11450
|
+
let execution;
|
|
11451
|
+
try {
|
|
11452
|
+
execution = await runExecutor(executable, args, {
|
|
11453
|
+
cwd,
|
|
11454
|
+
env,
|
|
11455
|
+
stdin: runExitClosurePrompt(state),
|
|
11456
|
+
timeoutSeconds: Math.max(1, readNumber(executionConfig.timeoutSeconds, config.executorTimeoutSeconds)),
|
|
11457
|
+
maxOutputBytes: Math.max(1, readNumber(executionConfig.maxOutputBytes, config.executorMaxOutputBytes)),
|
|
11458
|
+
maxRssMb: Math.max(0, readNumber(executionConfig.maxRssMb, config.executorMaxRssMb)),
|
|
11459
|
+
signal,
|
|
11460
|
+
executorKind,
|
|
11461
|
+
...readNumber(storedSpawnIdentity.uid, 0) > 0 ? { spawnIdentity: storedSpawnIdentity } : {},
|
|
11462
|
+
...readString(executionConfig.managedInput) ? { managedInput: readString(executionConfig.managedInput) } : {},
|
|
11463
|
+
onOutput: (stream, chunk, rawBytes) => {
|
|
11464
|
+
noteActiveRunOutput(command, stream, chunk, rawBytes);
|
|
11465
|
+
liveOutputLogger.write(stream, chunk);
|
|
11466
|
+
}
|
|
11467
|
+
});
|
|
11468
|
+
} finally {
|
|
11469
|
+
await liveOutputLogger.flush();
|
|
11470
|
+
}
|
|
11471
|
+
execution.stdout = redactProtectedText(execution.stdout, protectedValues);
|
|
11472
|
+
execution.stderr = redactProtectedText(execution.stderr, protectedValues);
|
|
11473
|
+
const outputFlood = asRecord(execution.outputFlood);
|
|
11474
|
+
const hasOutputFlood = Boolean(readString(outputFlood.stream) && readNumber(outputFlood.bytes, 0) > 0);
|
|
11475
|
+
const memoryLimit = asRecord(execution.memoryLimit);
|
|
11476
|
+
const hasMemoryLimit = readNumber(memoryLimit.rssBytes, 0) > 0;
|
|
11477
|
+
const parsed = parseExecutorTurnOutput(executorKind, execution, liveOutputLogger, hasOutputFlood);
|
|
11478
|
+
const outputTelemetry = liveOutputLogger.snapshot({
|
|
11479
|
+
outputBytes: execution.outputBytes,
|
|
11480
|
+
floodLimitBytes: Math.max(1, readNumber(executionConfig.maxOutputBytes, config.executorMaxOutputBytes)),
|
|
11481
|
+
outputTokens: readNumber(parsed.usage?.outputTokens, 0)
|
|
11482
|
+
});
|
|
11483
|
+
const completionOutputStopped = executorKind === "pi" && piCompletionOutputStopped(parsed, execution);
|
|
11484
|
+
const cleanupDisposition = executorKind === "pi" && execution.cancelled !== true && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError && execution.signal === null ? classifyPiTerminalCleanupDisposition(parsed, []) : null;
|
|
11485
|
+
const parsedForValidation = cleanupDisposition ? { ...parsed, errorMessage: null } : parsed;
|
|
11486
|
+
const piInvalidOutputError = executorKind === "pi" ? piOutputValidationError(parsedForValidation, {
|
|
11487
|
+
allowMissingTurnEnd: completionOutputStopped || Boolean(cleanupDisposition),
|
|
11488
|
+
allowMissingAssistantOutput: false
|
|
11489
|
+
}) : null;
|
|
11490
|
+
const parsedError = hasOutputFlood ? "Run-exit closure output exceeded the configured limit" : hasMemoryLimit ? "Run-exit closure exceeded the configured memory limit" : execution.timedOut ? "Run-exit closure timed out" : execution.spawnError ?? piInvalidOutputError ?? parsedForValidation.errorMessage ?? ((execution.exitCode ?? 0) === 0 ? null : `Run-exit closure exited with code ${execution.exitCode ?? "unknown"}`);
|
|
11491
|
+
const succeeded = execution.cancelled !== true && !hasOutputFlood && !hasMemoryLimit && (execution.exitCode === 0 || completionOutputStopped || Boolean(cleanupDisposition)) && !execution.timedOut && !execution.spawnError && !parsedError;
|
|
11492
|
+
const mcpToolResults = dedupeGovernedMcpToolResults([
|
|
11493
|
+
...Array.isArray(parsed.mcpToolResults) ? parsed.mcpToolResults.map(asRecord) : [],
|
|
11494
|
+
...liveOutputLogger.mcpToolResults().map(asRecord)
|
|
11495
|
+
]);
|
|
11496
|
+
await ingestLog(config, command, "system", succeeded ? "info" : "error", succeeded ? "Run-exit disposition closure turn completed" : `Run-exit disposition closure turn failed: ${parsedError}`, {
|
|
11497
|
+
presentationKind: "run_exit_disposition_closure",
|
|
11498
|
+
closureAttempt: 1,
|
|
11499
|
+
succeeded,
|
|
11500
|
+
outputTelemetry,
|
|
11501
|
+
toolResultCount: mcpToolResults.length
|
|
11502
|
+
});
|
|
11503
|
+
return {
|
|
11504
|
+
succeeded,
|
|
11505
|
+
error: parsedError,
|
|
11506
|
+
usage: asRecord(parsed.usage),
|
|
11507
|
+
audit: {
|
|
11508
|
+
schemaVersion: "run-exit-disposition-closure-v1",
|
|
11509
|
+
attempt: 1,
|
|
11510
|
+
succeeded,
|
|
11511
|
+
exitCode: execution.exitCode,
|
|
11512
|
+
signal: execution.signal,
|
|
11513
|
+
timedOut: execution.timedOut,
|
|
11514
|
+
summary: truncateText(readString(parsed.summary) ?? "", 2e3),
|
|
11515
|
+
outputTelemetry,
|
|
11516
|
+
toolResults: mcpToolResults.slice(0, 10).map((result3) => ({
|
|
11517
|
+
status: readString(result3.status),
|
|
11518
|
+
toolName: readString(result3.toolName) ?? readString(result3.name) ?? readString(asRecord(result3.runtimeAction).toolName),
|
|
11519
|
+
invocationId: readString(result3.invocationId)
|
|
11520
|
+
})),
|
|
11521
|
+
...parsedError ? { error: truncateText(parsedError, 1e3) } : {}
|
|
11522
|
+
}
|
|
11523
|
+
};
|
|
11524
|
+
} finally {
|
|
11525
|
+
stopTrackedHeartbeats();
|
|
11526
|
+
forgetTrackedRun();
|
|
11527
|
+
if (reservedPiAssignment) piAllocator.release(commandRunId(command));
|
|
11528
|
+
}
|
|
11529
|
+
}
|
|
11530
|
+
function runCompletionUsageResult(state) {
|
|
11531
|
+
const aggregate = asRecord(asRecord(state.usageRecord).aggregate);
|
|
11532
|
+
const costUsd = readNumber(aggregate.costUsd, 0);
|
|
11533
|
+
return {
|
|
11534
|
+
inputTokens: readNumber(aggregate.inputTokens, 0),
|
|
11535
|
+
cachedInputTokens: readNumber(aggregate.cachedInputTokens, 0),
|
|
11536
|
+
outputTokens: readNumber(aggregate.outputTokens, 0),
|
|
11537
|
+
...costUsd > 0 ? { costUsd } : {}
|
|
11538
|
+
};
|
|
11539
|
+
}
|
|
11540
|
+
async function ingestRunCompletionAggregateCost(config, state) {
|
|
11541
|
+
const command = asRecord(state.command);
|
|
11542
|
+
const payload = asRecord(command.payload);
|
|
11543
|
+
const companyId = readString(payload.companyId);
|
|
11544
|
+
const agentId = readString(payload.agentId);
|
|
11545
|
+
if (!companyId || !agentId) {
|
|
11546
|
+
return { accepted: true, skipped: true, reason: "cost_attribution_missing" };
|
|
11547
|
+
}
|
|
11548
|
+
const executor = asRecord(state.executor);
|
|
11549
|
+
const aggregate = asRecord(asRecord(state.usageRecord).aggregate);
|
|
11550
|
+
const response = asRecord(await postJson(
|
|
11551
|
+
config,
|
|
11552
|
+
`/api/amaster/runtime-connectors/${state.connectorId}/ingest/cost`,
|
|
11553
|
+
{
|
|
11554
|
+
commandId: state.commandId,
|
|
11555
|
+
idempotencyKey: readString(asRecord(state.usageRecord).idempotencyKey),
|
|
11556
|
+
companyId,
|
|
11557
|
+
agentId,
|
|
11558
|
+
issueId: commandIssueId(command) ?? void 0,
|
|
11559
|
+
runId: commandRunId(command) ?? void 0,
|
|
11560
|
+
provider: readString(executor.kind) ?? "runtime_connector",
|
|
11561
|
+
biller: readString(executor.kind) === "codex" ? "chatgpt" : "runtime_connector",
|
|
11562
|
+
billingType: "unknown",
|
|
11563
|
+
model: readString(executor.model) ?? readString(executor.kind) ?? "runtime_connector",
|
|
11564
|
+
inputTokens: readNumber(aggregate.inputTokens, 0),
|
|
11565
|
+
cachedInputTokens: readNumber(aggregate.cachedInputTokens, 0),
|
|
11566
|
+
outputTokens: readNumber(aggregate.outputTokens, 0),
|
|
11567
|
+
costCents: readNumber(aggregate.costCents, 0),
|
|
11568
|
+
occurredAt: readString(state.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
11569
|
+
}
|
|
11570
|
+
));
|
|
11571
|
+
if (response.accepted !== true || !readString(response.costEventId)) {
|
|
11572
|
+
throw new Error("runtime_completion_cost_receipt_invalid");
|
|
11573
|
+
}
|
|
11574
|
+
return response;
|
|
11575
|
+
}
|
|
11576
|
+
async function cleanupRunCompletionProfile(config, state) {
|
|
11577
|
+
const command = asRecord(state.command);
|
|
11578
|
+
const executor = asRecord(state.executor);
|
|
11579
|
+
const managedMcp = asRecord(executor.managedMcp);
|
|
11580
|
+
const profileRoot = readString(managedMcp.profileRoot);
|
|
11581
|
+
if (!profileRoot) return { status: "not_configured" };
|
|
11582
|
+
const cleanup = readString(executor.kind) === "pi" ? cleanupManagedPiMcpProfile : cleanupManagedCodexMcpProfile;
|
|
11583
|
+
const result3 = cleanup({ profileRoot }, {
|
|
11584
|
+
commandId: state.commandId,
|
|
11585
|
+
runId: commandRunId(command)
|
|
11586
|
+
});
|
|
11587
|
+
await ingestLog(config, command, "system", "info", "Cleaned managed MCP profile after run completion gate", {
|
|
11588
|
+
presentationKind: "managed_mcp_cleanup",
|
|
11589
|
+
status: readString(result3?.status) ?? "removed"
|
|
11590
|
+
});
|
|
11591
|
+
return result3;
|
|
11592
|
+
}
|
|
11593
|
+
function queueRunCompletionResultOutbox(config, state, payload, error) {
|
|
11594
|
+
const path = `/api/amaster/runtime-connectors/${state.connectorId}/commands/${state.commandId}/result`;
|
|
11595
|
+
writeResultOutboxEntry(config, {
|
|
11596
|
+
connectorId: state.connectorId,
|
|
11597
|
+
commandId: state.commandId,
|
|
11598
|
+
path,
|
|
11599
|
+
payload,
|
|
11600
|
+
activeRun: pendingRunCompletionSnapshot(config, state)
|
|
11601
|
+
});
|
|
11602
|
+
pendingResultOutboxRunCommands.set(state.commandId, {
|
|
11603
|
+
...pendingRunCompletionSnapshot(config, state),
|
|
11604
|
+
phase: "result_outbox",
|
|
11605
|
+
outboxQueuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11606
|
+
});
|
|
11607
|
+
clearPendingRunCompletion(config, state.commandId);
|
|
11608
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11609
|
+
process.stderr.write(`AMaster daemon queued terminal result ${state.commandId} after result POST failed: ${message}
|
|
11610
|
+
`);
|
|
11611
|
+
}
|
|
11612
|
+
async function finalizeRunCompletionState(config, inputState) {
|
|
11613
|
+
let state = inputState;
|
|
11614
|
+
if (state.phase === "ready_to_terminalize") {
|
|
11615
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11616
|
+
state,
|
|
11617
|
+
"cost_ingest_started"
|
|
11618
|
+
));
|
|
11619
|
+
}
|
|
11620
|
+
if (state.phase === "cost_ingest_pending") {
|
|
11621
|
+
try {
|
|
11622
|
+
const receipt = await ingestRunCompletionAggregateCost(config, state);
|
|
11623
|
+
const aggregateUsage = runCompletionUsageResult(state);
|
|
11624
|
+
const aggregate = asRecord(asRecord(state.usageRecord).aggregate);
|
|
11625
|
+
state = markRunCompletionCostDelivered({
|
|
11626
|
+
...state,
|
|
11627
|
+
candidateResult: {
|
|
11628
|
+
...asRecord(state.candidateResult),
|
|
11629
|
+
usage: aggregateUsage,
|
|
11630
|
+
usageBreakdown: Array.isArray(asRecord(state.usageRecord).turns) ? asRecord(state.usageRecord).turns : [],
|
|
11631
|
+
costUsage: {
|
|
11632
|
+
idempotencyKey: readString(asRecord(state.usageRecord).idempotencyKey),
|
|
11633
|
+
...readString(receipt.costEventId) ? { costEventId: readString(receipt.costEventId) } : {},
|
|
11634
|
+
...aggregateUsage,
|
|
11635
|
+
costCents: readNumber(aggregate.costCents, 0)
|
|
11636
|
+
}
|
|
11637
|
+
}
|
|
11638
|
+
}, receipt);
|
|
11639
|
+
state = persistPendingRunCompletion(config, state);
|
|
11640
|
+
} catch (error) {
|
|
11641
|
+
const message = truncateText(error instanceof Error ? error.message : String(error), 1e3);
|
|
11642
|
+
const costIngestAttemptCount = readNumber(state.costIngestAttemptCount, 0) + 1;
|
|
11643
|
+
if (!runCompletionPhaseExhausted(state, costIngestAttemptCount)) {
|
|
11644
|
+
state = persistPendingRunCompletion(config, {
|
|
11645
|
+
...state,
|
|
11646
|
+
costIngestAttemptCount,
|
|
11647
|
+
lastCostIngestError: message,
|
|
11648
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11649
|
+
});
|
|
11650
|
+
process.stderr.write(`AMaster daemon retained cost_ingest_pending state for ${state.commandId}: ${state.lastCostIngestError}
|
|
11651
|
+
`);
|
|
11652
|
+
return { pending: true, resultDelivered: false, state };
|
|
11653
|
+
}
|
|
11654
|
+
const usageRecord = asRecord(state.usageRecord);
|
|
11655
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11656
|
+
{
|
|
11657
|
+
...state,
|
|
11658
|
+
usageRecord: {
|
|
11659
|
+
...usageRecord,
|
|
11660
|
+
ingestStatus: "failed",
|
|
11661
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11662
|
+
failure: { message, attemptCount: costIngestAttemptCount }
|
|
11663
|
+
}
|
|
11664
|
+
},
|
|
11665
|
+
"cost_ingest_abandoned",
|
|
11666
|
+
{
|
|
11667
|
+
...failedRunCompletionCandidate(
|
|
11668
|
+
state,
|
|
11669
|
+
"runtime_completion_cost_retry_exhausted",
|
|
11670
|
+
`Cost ingestion retry boundary exhausted: ${message}`,
|
|
11671
|
+
{ attemptCount: costIngestAttemptCount }
|
|
11672
|
+
),
|
|
11673
|
+
costIngestAttemptCount,
|
|
11674
|
+
lastCostIngestError: message
|
|
11675
|
+
}
|
|
11676
|
+
));
|
|
11677
|
+
process.stderr.write(`AMaster daemon abandoned cost ingestion ${state.commandId} after ${costIngestAttemptCount} attempt(s); continuing failed terminalization.
|
|
11678
|
+
`);
|
|
11679
|
+
}
|
|
11680
|
+
}
|
|
11681
|
+
if (state.phase === "profile_cleanup") {
|
|
11682
|
+
try {
|
|
11683
|
+
const cleanup = await cleanupRunCompletionProfile(config, state);
|
|
11684
|
+
state = transitionRunCompletionState(state, "profile_cleaned", {
|
|
11685
|
+
managedMcpCleanup: cleanup,
|
|
11686
|
+
candidateResult: {
|
|
11687
|
+
...asRecord(state.candidateResult),
|
|
11688
|
+
...Object.keys(asRecord(asRecord(state.candidateResult).managedMcp)).length > 0 ? {
|
|
11689
|
+
managedMcp: {
|
|
11690
|
+
...asRecord(asRecord(state.candidateResult).managedMcp),
|
|
11691
|
+
cleanup
|
|
11692
|
+
}
|
|
11693
|
+
} : {}
|
|
11694
|
+
}
|
|
11695
|
+
});
|
|
11696
|
+
state = persistPendingRunCompletion(config, state);
|
|
11697
|
+
} catch (error) {
|
|
11698
|
+
const cleanupError = truncateText(error instanceof Error ? error.message : String(error), 1e3);
|
|
11699
|
+
if (managedMcpCleanupOwnerMismatch(error)) {
|
|
11700
|
+
try {
|
|
11701
|
+
const terminalStatus = await probeTerminalCommandAfterUnsafeCleanup(config, state);
|
|
11702
|
+
if (terminalStatus) {
|
|
11703
|
+
const cleanup2 = {
|
|
11704
|
+
status: "unsafe_residual_preserved",
|
|
11705
|
+
reason: "server_command_already_terminal",
|
|
11706
|
+
serverCommandStatus: terminalStatus
|
|
11707
|
+
};
|
|
11708
|
+
await ingestLog(
|
|
11709
|
+
config,
|
|
11710
|
+
asRecord(state.command),
|
|
11711
|
+
"system",
|
|
11712
|
+
"warn",
|
|
11713
|
+
"Preserved unsafe managed MCP residual after server terminalization",
|
|
11714
|
+
{
|
|
11715
|
+
presentationKind: "managed_mcp_cleanup",
|
|
11716
|
+
status: cleanup2.status,
|
|
11717
|
+
serverCommandStatus: terminalStatus,
|
|
11718
|
+
cleanupError
|
|
11719
|
+
}
|
|
11720
|
+
);
|
|
11721
|
+
process.stderr.write(`AMaster daemon preserved unsafe managed MCP residual after server terminalization for ${state.commandId}; durable completion state was released without deleting the unowned path.
|
|
11722
|
+
`);
|
|
11723
|
+
clearPendingRunCompletion(config, state.commandId);
|
|
11724
|
+
return {
|
|
11725
|
+
pending: false,
|
|
11726
|
+
resultDelivered: true,
|
|
11727
|
+
state: {
|
|
11728
|
+
...state,
|
|
11729
|
+
lastProfileCleanupError: cleanupError,
|
|
11730
|
+
managedMcpCleanup: cleanup2
|
|
11731
|
+
},
|
|
11732
|
+
managedMcpCleanup: cleanup2
|
|
11733
|
+
};
|
|
11734
|
+
}
|
|
11735
|
+
} catch (probeError) {
|
|
11736
|
+
state = {
|
|
11737
|
+
...state,
|
|
11738
|
+
lastProfileCleanupProbeError: truncateText(
|
|
11739
|
+
probeError instanceof Error ? probeError.message : String(probeError),
|
|
11740
|
+
1e3
|
|
11741
|
+
)
|
|
11742
|
+
};
|
|
11743
|
+
}
|
|
11744
|
+
}
|
|
11745
|
+
const profileCleanupAttemptCount = readNumber(state.profileCleanupAttemptCount, 0) + 1;
|
|
11746
|
+
if (!runCompletionPhaseExhausted(state, profileCleanupAttemptCount)) {
|
|
11747
|
+
state = persistPendingRunCompletion(config, {
|
|
11748
|
+
...state,
|
|
11749
|
+
profileCleanupAttemptCount,
|
|
11750
|
+
lastProfileCleanupError: cleanupError,
|
|
11751
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11752
|
+
});
|
|
11753
|
+
process.stderr.write(`AMaster daemon retained profile_cleanup state for ${state.commandId}: ${state.lastProfileCleanupError}
|
|
11754
|
+
`);
|
|
11755
|
+
return { pending: true, resultDelivered: false, state };
|
|
11756
|
+
}
|
|
11757
|
+
const cleanup = {
|
|
11758
|
+
status: "unsafe_residual_preserved",
|
|
11759
|
+
reason: "cleanup_retry_exhausted",
|
|
11760
|
+
attemptCount: profileCleanupAttemptCount,
|
|
11761
|
+
error: cleanupError
|
|
11762
|
+
};
|
|
11763
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11764
|
+
state,
|
|
11765
|
+
"profile_cleanup_abandoned",
|
|
11766
|
+
{
|
|
11767
|
+
...failedRunCompletionCandidate(
|
|
11768
|
+
state,
|
|
11769
|
+
"runtime_completion_profile_cleanup_retry_exhausted",
|
|
11770
|
+
`Managed MCP cleanup retry boundary exhausted: ${cleanupError}`,
|
|
11771
|
+
{ attemptCount: profileCleanupAttemptCount, residualPreserved: true }
|
|
11772
|
+
),
|
|
11773
|
+
profileCleanupAttemptCount,
|
|
11774
|
+
lastProfileCleanupError: cleanupError,
|
|
11775
|
+
managedMcpCleanup: cleanup
|
|
11776
|
+
}
|
|
11777
|
+
));
|
|
11778
|
+
process.stderr.write(`AMaster daemon preserved managed MCP residual for ${state.commandId} after ${profileCleanupAttemptCount} failed cleanup attempt(s); continuing failed terminalization.
|
|
11779
|
+
`);
|
|
11780
|
+
}
|
|
11781
|
+
}
|
|
11782
|
+
if (state.phase !== "result_post_pending") {
|
|
11783
|
+
throw new Error(`run_completion_finalize_phase_invalid:${state.phase}`);
|
|
11784
|
+
}
|
|
11785
|
+
const path = `/api/amaster/runtime-connectors/${state.connectorId}/commands/${state.commandId}/result`;
|
|
11786
|
+
const leaseId = readString(asRecord(state.command).leaseId);
|
|
11787
|
+
let payload = {
|
|
11788
|
+
...leaseId ? { leaseId } : {},
|
|
11789
|
+
status: state.candidateStatus,
|
|
11790
|
+
result: asRecord(state.candidateResult),
|
|
11791
|
+
...readString(state.candidateError) ? { error: truncateText(state.candidateError, 4e3) } : {}
|
|
11792
|
+
};
|
|
11793
|
+
try {
|
|
11794
|
+
await postJson(config, path, payload);
|
|
11795
|
+
} catch (error) {
|
|
11796
|
+
if (runtimeConnectorErrorCode(error) === "runtime_completion_disposition_required") {
|
|
11797
|
+
const raceMessage = "Run-exit disposition changed after profile cleanup; command failed closed";
|
|
11798
|
+
payload = {
|
|
11799
|
+
...payload,
|
|
11800
|
+
status: "failed",
|
|
11801
|
+
error: raceMessage,
|
|
11802
|
+
result: {
|
|
11803
|
+
...payload.result,
|
|
11804
|
+
errorCode: "runtime_completion_terminalization_race",
|
|
11805
|
+
completionGateRace: {
|
|
11806
|
+
failedClosed: true,
|
|
11807
|
+
detectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11808
|
+
}
|
|
11809
|
+
}
|
|
11810
|
+
};
|
|
11811
|
+
try {
|
|
11812
|
+
await postJson(config, path, payload);
|
|
11813
|
+
} catch (abortError) {
|
|
11814
|
+
queueRunCompletionResultOutbox(config, state, payload, abortError);
|
|
11815
|
+
return { pending: false, resultDelivered: false, outboxed: true, state };
|
|
11816
|
+
}
|
|
11817
|
+
} else {
|
|
11818
|
+
queueRunCompletionResultOutbox(config, state, payload, error);
|
|
11819
|
+
return { pending: false, resultDelivered: false, outboxed: true, state };
|
|
11820
|
+
}
|
|
11821
|
+
}
|
|
11822
|
+
state = transitionRunCompletionState(state, "result_post_delivered");
|
|
11823
|
+
clearPendingRunCompletion(config, state.commandId);
|
|
11824
|
+
return {
|
|
11825
|
+
pending: false,
|
|
11826
|
+
resultDelivered: true,
|
|
11827
|
+
state,
|
|
11828
|
+
managedMcpCleanup: asRecord(state.managedMcpCleanup)
|
|
11829
|
+
};
|
|
11830
|
+
}
|
|
11831
|
+
async function coordinateRunCompletion(config, inputState, options = {}) {
|
|
11832
|
+
let state = inputState;
|
|
11833
|
+
if (["ready_to_terminalize", "cost_ingest_pending", "profile_cleanup", "result_post_pending"].includes(state.phase)) {
|
|
11834
|
+
return finalizeRunCompletionState(config, state);
|
|
11835
|
+
}
|
|
11836
|
+
const recoveringClosure = state.phase === "closure_executing";
|
|
11837
|
+
let checked = state.phase === "disposition_required" ? { pending: false, state } : await requestRunCompletionCheck(config, state);
|
|
11838
|
+
state = checked.state;
|
|
11839
|
+
if (checked.serverTerminalStatus) {
|
|
11840
|
+
return {
|
|
11841
|
+
pending: false,
|
|
11842
|
+
resultDelivered: true,
|
|
11843
|
+
state,
|
|
11844
|
+
serverTerminalStatus: checked.serverTerminalStatus
|
|
11845
|
+
};
|
|
11846
|
+
}
|
|
11847
|
+
if (checked.pending) return { pending: true, resultDelivered: false, state };
|
|
11848
|
+
if (state.phase === "disposition_required") {
|
|
11849
|
+
const closureAlreadyRecorded = state.closureTurnRecorded === true;
|
|
11850
|
+
const recoveryCount = readNumber(state.closureRecoveryCount, 0);
|
|
11851
|
+
const mayResumeUncertainClosure = recoveringClosure && !closureAlreadyRecorded && recoveryCount < 1;
|
|
11852
|
+
if (state.closureAttempt === 0 || mayResumeUncertainClosure) {
|
|
11853
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11854
|
+
state,
|
|
11855
|
+
"closure_started",
|
|
11856
|
+
{
|
|
11857
|
+
closureAttempt: 1,
|
|
11858
|
+
...mayResumeUncertainClosure ? { closureRecoveryCount: recoveryCount + 1 } : {}
|
|
11859
|
+
}
|
|
11860
|
+
));
|
|
11861
|
+
let closure;
|
|
11862
|
+
try {
|
|
11863
|
+
closure = await executeRunExitClosureTurn(config, state, options);
|
|
11864
|
+
} catch (error) {
|
|
11865
|
+
closure = {
|
|
11866
|
+
succeeded: false,
|
|
11867
|
+
error: error instanceof Error ? error.message : String(error),
|
|
11868
|
+
usage: {},
|
|
11869
|
+
audit: {
|
|
11870
|
+
schemaVersion: "run-exit-disposition-closure-v1",
|
|
11871
|
+
attempt: 1,
|
|
11872
|
+
succeeded: false,
|
|
11873
|
+
error: truncateText(error instanceof Error ? error.message : String(error), 1e3)
|
|
11874
|
+
}
|
|
11875
|
+
};
|
|
11876
|
+
}
|
|
11877
|
+
state = appendRunCompletionUsageTurn({
|
|
11878
|
+
...state,
|
|
11879
|
+
closureTurnRecorded: true,
|
|
11880
|
+
candidateResult: {
|
|
11881
|
+
...asRecord(state.candidateResult),
|
|
11882
|
+
closureTurn: closure.audit
|
|
11883
|
+
}
|
|
11884
|
+
}, {
|
|
11885
|
+
turn: "closure",
|
|
11886
|
+
usage: closure.usage,
|
|
11887
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11888
|
+
});
|
|
11889
|
+
state = persistPendingRunCompletion(config, {
|
|
11890
|
+
...state,
|
|
11891
|
+
completionRequest: {
|
|
11892
|
+
...asRecord(state.completionRequest),
|
|
11893
|
+
resultSignals: {
|
|
11894
|
+
productiveSuccessfulRun: asRecord(asRecord(state.completionRequest).resultSignals).productiveSuccessfulRun === true,
|
|
11895
|
+
executorTurnCount: 2,
|
|
11896
|
+
closureAttempt: 1
|
|
11897
|
+
}
|
|
11898
|
+
}
|
|
11899
|
+
});
|
|
11900
|
+
checked = await requestRunCompletionCheck(config, state);
|
|
11901
|
+
state = checked.state;
|
|
11902
|
+
if (checked.serverTerminalStatus) {
|
|
11903
|
+
return {
|
|
11904
|
+
pending: false,
|
|
11905
|
+
resultDelivered: true,
|
|
11906
|
+
state,
|
|
11907
|
+
serverTerminalStatus: checked.serverTerminalStatus
|
|
11908
|
+
};
|
|
11909
|
+
}
|
|
11910
|
+
if (checked.pending) return { pending: true, resultDelivered: false, state };
|
|
11911
|
+
}
|
|
11912
|
+
}
|
|
11913
|
+
if (state.phase === "disposition_required") {
|
|
11914
|
+
const command = asRecord(state.command);
|
|
11915
|
+
const candidateResult = asRecord(state.candidateResult);
|
|
11916
|
+
const closureTurn = asRecord(candidateResult.closureTurn);
|
|
11917
|
+
state = persistPendingRunCompletion(config, transitionRunCompletionState(
|
|
11918
|
+
state,
|
|
11919
|
+
"closure_exhausted",
|
|
11920
|
+
{
|
|
11921
|
+
candidateResult: {
|
|
11922
|
+
...candidateResult,
|
|
11923
|
+
...Object.keys(closureTurn).length > 0 ? {} : {
|
|
11924
|
+
closureTurn: {
|
|
11925
|
+
schemaVersion: "run-exit-disposition-closure-v1",
|
|
11926
|
+
attempt: 1,
|
|
11927
|
+
succeeded: false,
|
|
11928
|
+
error: "closure_recovery_exhausted"
|
|
11929
|
+
}
|
|
11930
|
+
},
|
|
11931
|
+
closureAttemptExhausted: true,
|
|
11932
|
+
closureFallback: {
|
|
11933
|
+
schemaVersion: "run-exit-disposition-closure-fallback-v1",
|
|
11934
|
+
attempt: 1,
|
|
11935
|
+
reasonCode: readString(asRecord(state.completionResponse).reasonCode) ?? "disposition_still_required",
|
|
11936
|
+
commandId: state.commandId,
|
|
11937
|
+
runId: commandRunId(command)
|
|
11938
|
+
}
|
|
11939
|
+
}
|
|
11940
|
+
}
|
|
11941
|
+
));
|
|
11942
|
+
}
|
|
11943
|
+
return finalizeRunCompletionState(config, state);
|
|
11944
|
+
}
|
|
11945
|
+
function coordinateRunCompletionSingleFlight(config, inputState, options = {}) {
|
|
11946
|
+
const commandId = readString(inputState?.commandId);
|
|
11947
|
+
if (!commandId) return Promise.reject(new Error("run_completion_command_id_missing"));
|
|
11948
|
+
const activeFlight = runCompletionFlights.get(commandId);
|
|
11949
|
+
if (activeFlight) return activeFlight;
|
|
11950
|
+
const flight = coordinateRunCompletion(config, inputState, options);
|
|
11951
|
+
runCompletionFlights.set(commandId, flight);
|
|
11952
|
+
const release = () => {
|
|
11953
|
+
if (runCompletionFlights.get(commandId) === flight) runCompletionFlights.delete(commandId);
|
|
11954
|
+
};
|
|
11955
|
+
void flight.then(release, release);
|
|
11956
|
+
return flight;
|
|
11957
|
+
}
|
|
11958
|
+
async function flushRunCompletionStates(config) {
|
|
11959
|
+
let completed = 0;
|
|
11960
|
+
for (const { filePath, state: listedState } of runCompletionStateEntries(config)) {
|
|
11961
|
+
try {
|
|
11962
|
+
if (runCompletionFlights.has(listedState.commandId)) continue;
|
|
11963
|
+
if (!existsSync14(filePath)) continue;
|
|
11964
|
+
const state = readValidRunCompletionStateOrQuarantine(config, filePath);
|
|
11965
|
+
if (!state) continue;
|
|
11966
|
+
const outcome = await coordinateRunCompletionSingleFlight(config, state);
|
|
11967
|
+
if (!outcome.pending) completed += 1;
|
|
11968
|
+
} catch (error) {
|
|
11969
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11970
|
+
process.stderr.write(`AMaster daemon run completion recovery failed for ${listedState.commandId}: ${message}
|
|
11971
|
+
`);
|
|
11972
|
+
}
|
|
11973
|
+
}
|
|
11974
|
+
if (completed > 0) {
|
|
11975
|
+
process.stderr.write(`AMaster daemon resumed ${completed} durable run completion state(s).
|
|
11976
|
+
`);
|
|
11977
|
+
}
|
|
11978
|
+
return completed;
|
|
11979
|
+
}
|
|
10528
11980
|
async function ingestWorkspaceStatus(config, command, cwd) {
|
|
10529
11981
|
const connectorId = requireConnectorId(config);
|
|
10530
11982
|
const status = readWorkspaceStatus(cwd, { hashCache: workspaceStatusHashCache });
|
|
@@ -10587,6 +12039,7 @@ async function ingestRuntimeArtifacts(config, command, cwd, mcpToolResults) {
|
|
|
10587
12039
|
} catch (err) {
|
|
10588
12040
|
const message = `Runtime Artifact ${upload.intentId} ingest failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
10589
12041
|
const failure = new Error(message, err instanceof Error ? { cause: err } : void 0);
|
|
12042
|
+
failure.intentId = upload.intentId;
|
|
10590
12043
|
if (err instanceof Error && err.code === "runtime_artifact_rejected") {
|
|
10591
12044
|
failure.code = "runtime_artifact_rejected";
|
|
10592
12045
|
}
|
|
@@ -10659,14 +12112,14 @@ async function completeCommand(config, command, status, result3, error) {
|
|
|
10659
12112
|
function resultOutboxDir(config) {
|
|
10660
12113
|
const explicit = readString(process.env.AMASTER_RESULT_OUTBOX_DIR);
|
|
10661
12114
|
if (explicit) return resolve12(expandHomePath(explicit));
|
|
10662
|
-
return
|
|
12115
|
+
return join15(dirname9(stateFilePath(process.env)), "result-outbox");
|
|
10663
12116
|
}
|
|
10664
12117
|
function resultOutboxInvalidDir(config) {
|
|
10665
|
-
return
|
|
12118
|
+
return join15(resultOutboxDir(config), "invalid");
|
|
10666
12119
|
}
|
|
10667
12120
|
function writeResultOutboxEntry(config, entry) {
|
|
10668
12121
|
const dir = resultOutboxDir(config);
|
|
10669
|
-
|
|
12122
|
+
mkdirSync9(dir, { recursive: true });
|
|
10670
12123
|
const body = {
|
|
10671
12124
|
version: 1,
|
|
10672
12125
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10674,19 +12127,19 @@ function writeResultOutboxEntry(config, entry) {
|
|
|
10674
12127
|
lastAttemptAt: null,
|
|
10675
12128
|
...entry
|
|
10676
12129
|
};
|
|
10677
|
-
|
|
12130
|
+
writeFileSync9(join15(dir, resultOutboxFileName(entry.commandId)), `${JSON.stringify(body, null, 2)}
|
|
10678
12131
|
`, { mode: 384 });
|
|
10679
12132
|
}
|
|
10680
12133
|
function moveResultOutboxEntryToInvalid(config, file, fullPath, reason, detail, original) {
|
|
10681
12134
|
const invalidDir = resultOutboxInvalidDir(config);
|
|
10682
|
-
|
|
10683
|
-
const invalidPath =
|
|
12135
|
+
mkdirSync9(invalidDir, { recursive: true });
|
|
12136
|
+
const invalidPath = join15(invalidDir, file);
|
|
10684
12137
|
if (original === void 0) {
|
|
10685
12138
|
try {
|
|
10686
|
-
|
|
12139
|
+
renameSync6(fullPath, invalidPath);
|
|
10687
12140
|
} catch {
|
|
10688
12141
|
copyFileSync3(fullPath, invalidPath);
|
|
10689
|
-
|
|
12142
|
+
unlinkSync2(fullPath);
|
|
10690
12143
|
}
|
|
10691
12144
|
return;
|
|
10692
12145
|
}
|
|
@@ -10696,14 +12149,14 @@ function moveResultOutboxEntryToInvalid(config, file, fullPath, reason, detail,
|
|
|
10696
12149
|
...detail ? { detail: truncateText(detail, 1e3) } : {},
|
|
10697
12150
|
original
|
|
10698
12151
|
};
|
|
10699
|
-
|
|
12152
|
+
writeFileSync9(invalidPath, `${JSON.stringify(evidence, null, 2)}
|
|
10700
12153
|
`, { mode: 384 });
|
|
10701
|
-
|
|
12154
|
+
unlinkSync2(fullPath);
|
|
10702
12155
|
}
|
|
10703
12156
|
function readValidResultOutboxEntryOrQuarantine(config, file, fullPath) {
|
|
10704
12157
|
let entry;
|
|
10705
12158
|
try {
|
|
10706
|
-
entry = JSON.parse(
|
|
12159
|
+
entry = JSON.parse(readFileSync11(fullPath, "utf8"));
|
|
10707
12160
|
} catch (err) {
|
|
10708
12161
|
const message = err instanceof Error ? err.message : String(err);
|
|
10709
12162
|
moveResultOutboxEntryToInvalid(config, file, fullPath, "malformed_result_outbox_json", message);
|
|
@@ -10733,13 +12186,13 @@ function updateResultOutboxAttempt(fullPath, entry, err) {
|
|
|
10733
12186
|
...typeof err?.httpStatus === "number" ? { httpStatus: err.httpStatus } : {},
|
|
10734
12187
|
lastError: truncateText(err instanceof Error ? err.message : String(err), 1e3)
|
|
10735
12188
|
};
|
|
10736
|
-
|
|
12189
|
+
writeFileSync9(fullPath, `${JSON.stringify(next, null, 2)}
|
|
10737
12190
|
`, { mode: 384 });
|
|
10738
12191
|
return next;
|
|
10739
12192
|
}
|
|
10740
12193
|
function quarantineResultOutboxEntry(config, fullPath, file, entry, reason, err) {
|
|
10741
12194
|
const invalidDir = resultOutboxInvalidDir(config);
|
|
10742
|
-
|
|
12195
|
+
mkdirSync9(invalidDir, { recursive: true });
|
|
10743
12196
|
const body = {
|
|
10744
12197
|
...entry,
|
|
10745
12198
|
invalidReason: reason,
|
|
@@ -10747,30 +12200,31 @@ function quarantineResultOutboxEntry(config, fullPath, file, entry, reason, err)
|
|
|
10747
12200
|
...typeof err?.httpStatus === "number" ? { httpStatus: err.httpStatus } : {},
|
|
10748
12201
|
lastError: truncateText(err instanceof Error ? err.message : String(err), 1e3)
|
|
10749
12202
|
};
|
|
10750
|
-
const invalidPath =
|
|
10751
|
-
|
|
12203
|
+
const invalidPath = join15(invalidDir, file);
|
|
12204
|
+
writeFileSync9(invalidPath, `${JSON.stringify(body, null, 2)}
|
|
10752
12205
|
`, { mode: 384 });
|
|
10753
12206
|
try {
|
|
10754
|
-
|
|
12207
|
+
unlinkSync2(fullPath);
|
|
10755
12208
|
} catch (unlinkErr) {
|
|
10756
12209
|
const message = unlinkErr instanceof Error ? unlinkErr.message : String(unlinkErr);
|
|
10757
12210
|
process.stderr.write(`AMaster daemon could not remove quarantined result outbox entry ${file}: ${message}
|
|
10758
12211
|
`);
|
|
10759
12212
|
}
|
|
12213
|
+
clearDeliveredResultOutboxCommand(config, entry);
|
|
10760
12214
|
return invalidPath;
|
|
10761
12215
|
}
|
|
10762
12216
|
async function flushResultOutbox(config) {
|
|
10763
12217
|
const dir = resultOutboxDir(config);
|
|
10764
|
-
if (!
|
|
10765
|
-
const files =
|
|
12218
|
+
if (!existsSync14(dir)) return { attempted: 0, completed: 0 };
|
|
12219
|
+
const files = readdirSync9(dir).filter((name) => name.endsWith(".json")).sort();
|
|
10766
12220
|
let completed = 0;
|
|
10767
12221
|
for (const file of files) {
|
|
10768
|
-
const fullPath =
|
|
12222
|
+
const fullPath = join15(dir, file);
|
|
10769
12223
|
const entry = readValidResultOutboxEntryOrQuarantine(config, file, fullPath);
|
|
10770
12224
|
if (!entry) continue;
|
|
10771
12225
|
try {
|
|
10772
12226
|
await postJson(config, String(entry.path), entry.payload);
|
|
10773
|
-
|
|
12227
|
+
unlinkSync2(fullPath);
|
|
10774
12228
|
clearDeliveredResultOutboxCommand(config, entry);
|
|
10775
12229
|
completed += 1;
|
|
10776
12230
|
} catch (err) {
|
|
@@ -10962,8 +12416,8 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
10962
12416
|
runtimeAuth,
|
|
10963
12417
|
issueId
|
|
10964
12418
|
);
|
|
10965
|
-
const targetDir =
|
|
10966
|
-
|
|
12419
|
+
const targetDir = join15(workspace.cwd, "input-attachments");
|
|
12420
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
10967
12421
|
const usedFilenames = /* @__PURE__ */ new Set();
|
|
10968
12422
|
const materialized = [];
|
|
10969
12423
|
for (const [index, rawAttachment] of attachments.entries()) {
|
|
@@ -10977,9 +12431,9 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
10977
12431
|
filename = `${stem}-${index + 1}${ext}`;
|
|
10978
12432
|
}
|
|
10979
12433
|
usedFilenames.add(filename);
|
|
10980
|
-
const targetPath =
|
|
12434
|
+
const targetPath = join15(targetDir, filename);
|
|
10981
12435
|
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
10982
|
-
|
|
12436
|
+
writeFileSync9(targetPath, body);
|
|
10983
12437
|
const attachmentId = readString(attachment.id);
|
|
10984
12438
|
const actualSha256 = createHash12("sha256").update(body).digest("hex");
|
|
10985
12439
|
const lineageCandidates = lineageCandidatesByAttachmentId.get(attachmentId) ?? [];
|
|
@@ -11053,9 +12507,9 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
11053
12507
|
if (manifest.version !== 1 || !Array.isArray(manifest.entries)) {
|
|
11054
12508
|
throw new Error("artifact_input_manifest_invalid: expected version 1 entries array");
|
|
11055
12509
|
}
|
|
11056
|
-
const targetRoot =
|
|
12510
|
+
const targetRoot = join15(workspace.cwd, "input-artifacts");
|
|
11057
12511
|
rmSync7(targetRoot, { recursive: true, force: true });
|
|
11058
|
-
|
|
12512
|
+
mkdirSync9(targetRoot, { recursive: true });
|
|
11059
12513
|
const usedPaths = /* @__PURE__ */ new Set();
|
|
11060
12514
|
const materialized = [];
|
|
11061
12515
|
for (const [index, rawEntry] of manifest.entries.entries()) {
|
|
@@ -11084,17 +12538,17 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
11084
12538
|
id: attachmentId,
|
|
11085
12539
|
originalFilename: readString(entry.originalFilename)
|
|
11086
12540
|
}, index);
|
|
11087
|
-
let relativePath =
|
|
12541
|
+
let relativePath = join15("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
11088
12542
|
if (usedPaths.has(relativePath)) {
|
|
11089
12543
|
const ext = extname2(filename);
|
|
11090
12544
|
const stem = ext ? filename.slice(0, -ext.length) : filename;
|
|
11091
12545
|
filename = `${stem}-${index + 1}${ext}`;
|
|
11092
|
-
relativePath =
|
|
12546
|
+
relativePath = join15("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
11093
12547
|
}
|
|
11094
12548
|
usedPaths.add(relativePath);
|
|
11095
|
-
const targetPath =
|
|
11096
|
-
|
|
11097
|
-
|
|
12549
|
+
const targetPath = join15(workspace.cwd, relativePath);
|
|
12550
|
+
mkdirSync9(dirname9(targetPath), { recursive: true });
|
|
12551
|
+
writeFileSync9(targetPath, body);
|
|
11098
12552
|
chmodSync6(targetPath, 292);
|
|
11099
12553
|
materialized.push({
|
|
11100
12554
|
id: attachmentId,
|
|
@@ -11114,8 +12568,8 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
11114
12568
|
version: 1,
|
|
11115
12569
|
entries: materialized.map(({ path: _path, relativePath, ...entry }) => ({ ...entry, path: relativePath }))
|
|
11116
12570
|
};
|
|
11117
|
-
const manifestPath =
|
|
11118
|
-
|
|
12571
|
+
const manifestPath = join15(targetRoot, "artifact-input-manifest.json");
|
|
12572
|
+
writeFileSync9(manifestPath, JSON.stringify(localManifest, null, 2) + "\n");
|
|
11119
12573
|
chmodSync6(manifestPath, 292);
|
|
11120
12574
|
updateWorkspaceManifest(workspaceManifestPath(workspace), { artifactInputManifest: localManifest });
|
|
11121
12575
|
await ingestLog(config, command, "system", "info", `Materialized ${materialized.length} required artifact input(s) into the execution workspace`, {
|
|
@@ -11125,11 +12579,11 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
11125
12579
|
return materialized;
|
|
11126
12580
|
}
|
|
11127
12581
|
function issueCheckpointDir(workspace) {
|
|
11128
|
-
return
|
|
12582
|
+
return join15(dirname9(workspace.runDir), "checkpoint");
|
|
11129
12583
|
}
|
|
11130
12584
|
async function clearIssueCheckpoint(config, command, workspace, reason) {
|
|
11131
12585
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
11132
|
-
if (!
|
|
12586
|
+
if (!existsSync14(checkpointDir)) return false;
|
|
11133
12587
|
rmSync7(checkpointDir, { recursive: true, force: true });
|
|
11134
12588
|
await ingestLog(config, command, "system", "info", "Cleared issue continuation checkpoint", { reason });
|
|
11135
12589
|
return true;
|
|
@@ -11143,15 +12597,15 @@ function safeCheckpointRelativePath(rawPath) {
|
|
|
11143
12597
|
return normalized;
|
|
11144
12598
|
}
|
|
11145
12599
|
function hashFileSha256(filePath) {
|
|
11146
|
-
return createHash12("sha256").update(
|
|
12600
|
+
return createHash12("sha256").update(readFileSync11(filePath)).digest("hex");
|
|
11147
12601
|
}
|
|
11148
12602
|
async function materializeIssueCheckpoint(config, command, workspace) {
|
|
11149
12603
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
11150
|
-
const manifestPath =
|
|
11151
|
-
if (!
|
|
12604
|
+
const manifestPath = join15(checkpointDir, "manifest.json");
|
|
12605
|
+
if (!existsSync14(manifestPath)) return [];
|
|
11152
12606
|
let manifest;
|
|
11153
12607
|
try {
|
|
11154
|
-
manifest = asRecord(JSON.parse(
|
|
12608
|
+
manifest = asRecord(JSON.parse(readFileSync11(manifestPath, "utf8")));
|
|
11155
12609
|
} catch (err) {
|
|
11156
12610
|
rmSync7(checkpointDir, { recursive: true, force: true });
|
|
11157
12611
|
throw new Error(`invalid issue checkpoint manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -11164,8 +12618,8 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
11164
12618
|
try {
|
|
11165
12619
|
const files = Array.isArray(manifest.files) ? manifest.files : [];
|
|
11166
12620
|
if (files.length > 20) throw new Error("issue checkpoint manifest exceeds the limit of 20 files");
|
|
11167
|
-
const filesRoot =
|
|
11168
|
-
const workspaceRoot =
|
|
12621
|
+
const filesRoot = realpathSync5(join15(checkpointDir, "files"));
|
|
12622
|
+
const workspaceRoot = realpathSync5(workspace.cwd);
|
|
11169
12623
|
const validated = [];
|
|
11170
12624
|
let totalBytes = 0;
|
|
11171
12625
|
for (const rawFile of files) {
|
|
@@ -11177,8 +12631,8 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
11177
12631
|
if (!pathWithin2(sourceCandidate, filesRoot) || !pathWithin2(target, workspaceRoot)) {
|
|
11178
12632
|
throw new Error(`issue checkpoint path escapes its workspace: ${relativePath}`);
|
|
11179
12633
|
}
|
|
11180
|
-
if (!
|
|
11181
|
-
const source =
|
|
12634
|
+
if (!existsSync14(sourceCandidate)) throw new Error(`issue checkpoint file is missing: ${relativePath}`);
|
|
12635
|
+
const source = realpathSync5(sourceCandidate);
|
|
11182
12636
|
if (!pathWithin2(source, filesRoot) || !statSync7(source).isFile()) {
|
|
11183
12637
|
throw new Error(`issue checkpoint source escapes its storage root: ${relativePath}`);
|
|
11184
12638
|
}
|
|
@@ -11202,8 +12656,8 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
11202
12656
|
} catch (err) {
|
|
11203
12657
|
if (err?.code !== "ENOENT") throw err;
|
|
11204
12658
|
}
|
|
11205
|
-
|
|
11206
|
-
const targetParent =
|
|
12659
|
+
mkdirSync9(dirname9(target), { recursive: true });
|
|
12660
|
+
const targetParent = realpathSync5(dirname9(target));
|
|
11207
12661
|
if (!pathWithin2(targetParent, workspaceRoot)) {
|
|
11208
12662
|
throw new Error(`issue checkpoint target escapes its workspace: ${relativePath}`);
|
|
11209
12663
|
}
|
|
@@ -11231,17 +12685,17 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
11231
12685
|
}
|
|
11232
12686
|
async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
11233
12687
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
11234
|
-
const filesDir =
|
|
12688
|
+
const filesDir = join15(checkpointDir, "files");
|
|
11235
12689
|
rmSync7(checkpointDir, { recursive: true, force: true });
|
|
11236
|
-
|
|
12690
|
+
mkdirSync9(filesDir, { recursive: true });
|
|
11237
12691
|
const files = [];
|
|
11238
12692
|
let totalBytes = 0;
|
|
11239
|
-
const workspaceRoot =
|
|
12693
|
+
const workspaceRoot = realpathSync5(workspace.cwd);
|
|
11240
12694
|
for (const candidate of candidates.slice(0, 20)) {
|
|
11241
12695
|
const relativePath = safeCheckpointRelativePath(candidate.rawPath);
|
|
11242
12696
|
const source = readString(candidate.filePath);
|
|
11243
|
-
if (!relativePath || !source || !
|
|
11244
|
-
const ownedSource =
|
|
12697
|
+
if (!relativePath || !source || !existsSync14(source) || !statSync7(source).isFile()) continue;
|
|
12698
|
+
const ownedSource = realpathSync5(source);
|
|
11245
12699
|
if (!pathWithin2(ownedSource, workspaceRoot)) {
|
|
11246
12700
|
throw new Error(`issue checkpoint source escapes its execution workspace: ${relativePath}`);
|
|
11247
12701
|
}
|
|
@@ -11249,7 +12703,7 @@ async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
|
11249
12703
|
if (byteSize <= 0 || totalBytes + byteSize > MAX_CHECKPOINT_BYTES) continue;
|
|
11250
12704
|
const target = resolve12(filesDir, relativePath);
|
|
11251
12705
|
if (!pathWithin2(target, filesDir)) continue;
|
|
11252
|
-
|
|
12706
|
+
mkdirSync9(dirname9(target), { recursive: true });
|
|
11253
12707
|
copyFileSync3(ownedSource, target);
|
|
11254
12708
|
totalBytes += byteSize;
|
|
11255
12709
|
files.push({ path: relativePath, byteSize, sha256: hashFileSha256(ownedSource) });
|
|
@@ -11267,7 +12721,7 @@ async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
|
11267
12721
|
totalBytes,
|
|
11268
12722
|
files
|
|
11269
12723
|
};
|
|
11270
|
-
|
|
12724
|
+
writeFileSync9(join15(checkpointDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
11271
12725
|
`);
|
|
11272
12726
|
await ingestLog(config, command, "system", "warn", `Saved ${files.length} required deliverable(s) for continuation recovery`, manifest);
|
|
11273
12727
|
return manifest;
|
|
@@ -11353,6 +12807,7 @@ async function executeRunCommand(config, command) {
|
|
|
11353
12807
|
};
|
|
11354
12808
|
let stopActiveRunHeartbeats = () => {
|
|
11355
12809
|
};
|
|
12810
|
+
let completionOwnsManagedMcpProfile = false;
|
|
11356
12811
|
try {
|
|
11357
12812
|
if (executor.kind === "codex" && Object.keys(governedMcp).length > 0) {
|
|
11358
12813
|
managedMcpProfile = prepareManagedCodexMcpProfile({
|
|
@@ -11447,7 +12902,7 @@ async function executeRunCommand(config, command) {
|
|
|
11447
12902
|
executorEnv = {
|
|
11448
12903
|
...executorEnv,
|
|
11449
12904
|
AMASTER_MANAGED_RUNTIME_ASSERTION_FD: "3",
|
|
11450
|
-
AMASTER_MANAGED_RUNTIME_AUDIT_FILE:
|
|
12905
|
+
AMASTER_MANAGED_RUNTIME_AUDIT_FILE: join15(
|
|
11451
12906
|
managedMcpProfile.env.HOME,
|
|
11452
12907
|
".amaster-managed-runtime-audit.jsonl"
|
|
11453
12908
|
),
|
|
@@ -11683,16 +13138,15 @@ async function executeRunCommand(config, command) {
|
|
|
11683
13138
|
outputTelemetry
|
|
11684
13139
|
}
|
|
11685
13140
|
);
|
|
11686
|
-
await ingestCost(config, command, executor, parsed);
|
|
11687
13141
|
const workspaceStatus = await ingestWorkspaceStatus(config, command, cwd);
|
|
11688
13142
|
let nativeSessionRollout = null;
|
|
11689
13143
|
let nativeSessionRolloutError = null;
|
|
11690
13144
|
let nativeSessionRolloutCleanup = null;
|
|
11691
13145
|
let nativeSessionRolloutCleanupError = null;
|
|
11692
|
-
const mcpToolResults = [
|
|
13146
|
+
const mcpToolResults = dedupeGovernedMcpToolResults([
|
|
11693
13147
|
...Array.isArray(parsed.mcpToolResults) ? parsed.mcpToolResults.map(asRecord) : [],
|
|
11694
13148
|
...liveOutputLogger.mcpToolResults().map(asRecord)
|
|
11695
|
-
];
|
|
13149
|
+
]);
|
|
11696
13150
|
runtimeArtifacts.push(...await ingestRuntimeArtifacts(
|
|
11697
13151
|
config,
|
|
11698
13152
|
command,
|
|
@@ -11795,23 +13249,7 @@ async function executeRunCommand(config, command) {
|
|
|
11795
13249
|
const resultStderr = filterExecutionStderrForResult(executor.kind, execution.stderr);
|
|
11796
13250
|
const error = execution.timedOut ? `Executor timed out after ${config.executorTimeoutSeconds}s` : cancelled ? "Executor cancelled by AMaster control plane" : execution.spawnError ?? parsedErrorMessage ?? (succeeded ? null : `Executor exited with code ${execution.exitCode ?? "unknown"}`);
|
|
11797
13251
|
const costUsage = parsedCostUsage(parsed.usage);
|
|
11798
|
-
|
|
11799
|
-
managedMcpCleanup = cleanupManagedMcpProfile(managedMcpProfile, {
|
|
11800
|
-
commandId: command.commandId,
|
|
11801
|
-
runId: commandRunId(command)
|
|
11802
|
-
});
|
|
11803
|
-
patchActiveRunCommand(command, {
|
|
11804
|
-
managedMcpCleanup: managedMcpCleanup.status,
|
|
11805
|
-
managedMcpCleanedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11806
|
-
});
|
|
11807
|
-
await ingestLog(config, command, "system", "info", `Cleaned isolated ${executor.kind} managed MCP profile`, {
|
|
11808
|
-
presentationKind: "managed_mcp_cleanup",
|
|
11809
|
-
attestationId: managedMcpProfile.attestation.attestationId,
|
|
11810
|
-
sessionId: governedMcp.sessionId,
|
|
11811
|
-
status: managedMcpCleanup.status
|
|
11812
|
-
});
|
|
11813
|
-
}
|
|
11814
|
-
const result3 = {
|
|
13252
|
+
let result3 = {
|
|
11815
13253
|
evidenceContract: { version: 1 },
|
|
11816
13254
|
executorKind: executor.kind,
|
|
11817
13255
|
command: invocation.command,
|
|
@@ -11900,6 +13338,90 @@ async function executeRunCommand(config, command) {
|
|
|
11900
13338
|
}
|
|
11901
13339
|
} : {}
|
|
11902
13340
|
};
|
|
13341
|
+
if (succeeded && managedMcpProfile && Array.isArray(command.requiredCapabilities) && command.requiredCapabilities.includes("runtime_actions_v2")) {
|
|
13342
|
+
const productiveSuccessfulRun = outputTelemetry.meaningfulProgressEventCount > 0 || runtimeArtifacts.length > 0 || mcpToolResults.some((toolResult) => ["succeeded", "approval_required"].includes(readString(toolResult.status)));
|
|
13343
|
+
let completionState = createRunCompletionState({
|
|
13344
|
+
connectorId: requireConnectorId(config),
|
|
13345
|
+
command,
|
|
13346
|
+
executor: {
|
|
13347
|
+
kind: executor.kind,
|
|
13348
|
+
model: process.env.AMASTER_CODEX_MODEL || executor.kind,
|
|
13349
|
+
...managedMcpProfile ? {
|
|
13350
|
+
managedMcp: {
|
|
13351
|
+
profileRoot: managedMcpProfile.profileRoot,
|
|
13352
|
+
attestation: managedMcpProfile.attestation
|
|
13353
|
+
}
|
|
13354
|
+
} : {},
|
|
13355
|
+
closureExecution: {
|
|
13356
|
+
command: invocation.command,
|
|
13357
|
+
args: invocation.args,
|
|
13358
|
+
cwd,
|
|
13359
|
+
sourceWorkspacePath: workspace.sourceWorkspacePath,
|
|
13360
|
+
managedWorkdir: workspace.managed,
|
|
13361
|
+
manifestPath: workspaceManifestPath(workspace),
|
|
13362
|
+
env: executorEnv,
|
|
13363
|
+
protectedValues: protectedExecutorValues,
|
|
13364
|
+
timeoutSeconds: config.executorTimeoutSeconds,
|
|
13365
|
+
maxOutputBytes: config.executorMaxOutputBytes,
|
|
13366
|
+
maxRssMb: config.executorMaxRssMb,
|
|
13367
|
+
...piChildIsolation ? { spawnIdentity: piChildIsolation.spawn } : {},
|
|
13368
|
+
...trustedPiRuntime ? { managedInput: `${JSON.stringify(trustedPiRuntimeAssertion)}
|
|
13369
|
+
` } : {}
|
|
13370
|
+
}
|
|
13371
|
+
},
|
|
13372
|
+
completionRequest: {
|
|
13373
|
+
contractVersion: "amaster.runtime-connector.completion-check.v1",
|
|
13374
|
+
leaseId: command.leaseId,
|
|
13375
|
+
proposedStatus: "succeeded",
|
|
13376
|
+
resultSignals: {
|
|
13377
|
+
productiveSuccessfulRun,
|
|
13378
|
+
executorTurnCount: 1,
|
|
13379
|
+
closureAttempt: 0
|
|
13380
|
+
}
|
|
13381
|
+
},
|
|
13382
|
+
candidateStatus: "succeeded",
|
|
13383
|
+
candidateResult: result3,
|
|
13384
|
+
turn: { usage: parsed.usage }
|
|
13385
|
+
});
|
|
13386
|
+
completionState = persistPendingRunCompletion(config, completionState);
|
|
13387
|
+
completionOwnsManagedMcpProfile = true;
|
|
13388
|
+
const completionOutcome = await coordinateRunCompletionSingleFlight(config, completionState, {
|
|
13389
|
+
signal: abortController.signal
|
|
13390
|
+
});
|
|
13391
|
+
managedMcpCleanup = Object.keys(asRecord(completionOutcome.managedMcpCleanup)).length > 0 ? completionOutcome.managedMcpCleanup : managedMcpCleanup;
|
|
13392
|
+
if (completionOutcome.pending) {
|
|
13393
|
+
await heartbeat(config, { logMode: "compact" }).catch((heartbeatError) => {
|
|
13394
|
+
const message = heartbeatError instanceof Error ? heartbeatError.message : String(heartbeatError);
|
|
13395
|
+
process.stderr.write(`AMaster daemon completion-state heartbeat failed; durable state will retry: ${message}
|
|
13396
|
+
`);
|
|
13397
|
+
});
|
|
13398
|
+
}
|
|
13399
|
+
return { resultDelivered: Boolean(completionOutcome.resultDelivered) };
|
|
13400
|
+
}
|
|
13401
|
+
await ingestCost(config, command, executor, parsed);
|
|
13402
|
+
if (managedMcpProfile) {
|
|
13403
|
+
managedMcpCleanup = cleanupManagedMcpProfile(managedMcpProfile, {
|
|
13404
|
+
commandId: command.commandId,
|
|
13405
|
+
runId: commandRunId(command)
|
|
13406
|
+
});
|
|
13407
|
+
patchActiveRunCommand(command, {
|
|
13408
|
+
managedMcpCleanup: managedMcpCleanup.status,
|
|
13409
|
+
managedMcpCleanedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13410
|
+
});
|
|
13411
|
+
await ingestLog(config, command, "system", "info", `Cleaned isolated ${executor.kind} managed MCP profile`, {
|
|
13412
|
+
presentationKind: "managed_mcp_cleanup",
|
|
13413
|
+
attestationId: managedMcpProfile.attestation.attestationId,
|
|
13414
|
+
sessionId: governedMcp.sessionId,
|
|
13415
|
+
status: managedMcpCleanup.status
|
|
13416
|
+
});
|
|
13417
|
+
result3 = {
|
|
13418
|
+
...result3,
|
|
13419
|
+
managedMcp: {
|
|
13420
|
+
...asRecord(result3.managedMcp),
|
|
13421
|
+
cleanup: managedMcpCleanup
|
|
13422
|
+
}
|
|
13423
|
+
};
|
|
13424
|
+
}
|
|
11903
13425
|
const completion = await completeCommand(config, command, cancelled ? "cancelled" : succeeded ? "succeeded" : "failed", result3, error ?? void 0);
|
|
11904
13426
|
if (!completion) {
|
|
11905
13427
|
rememberPendingResultOutboxRunCommand(command, {
|
|
@@ -11914,7 +13436,7 @@ async function executeRunCommand(config, command) {
|
|
|
11914
13436
|
}
|
|
11915
13437
|
return { resultDelivered: Boolean(completion) };
|
|
11916
13438
|
} finally {
|
|
11917
|
-
if (managedMcpProfile && !managedMcpCleanup) {
|
|
13439
|
+
if (managedMcpProfile && !managedMcpCleanup && !completionOwnsManagedMcpProfile) {
|
|
11918
13440
|
managedMcpCleanup = cleanupManagedMcpProfile(managedMcpProfile, {
|
|
11919
13441
|
commandId: command.commandId,
|
|
11920
13442
|
runId: commandRunId(command)
|
|
@@ -12024,13 +13546,15 @@ function isConnectorAuthFailure(err) {
|
|
|
12024
13546
|
return err && (err.httpStatus === 401 || err.httpStatus === 403);
|
|
12025
13547
|
}
|
|
12026
13548
|
function reconcileStaleManagedMcpProfiles(config) {
|
|
13549
|
+
const protectedCommandIds = runCompletionStateEntries(config).map(({ state }) => state.commandId);
|
|
12027
13550
|
for (const [executorKind, reconcile] of [
|
|
12028
13551
|
["Codex", reconcileManagedCodexMcpProfiles],
|
|
12029
13552
|
["Pi", reconcileManagedPiMcpProfiles]
|
|
12030
13553
|
]) {
|
|
12031
|
-
const result3 = reconcile(config.runtimeWorkspacesRoot);
|
|
13554
|
+
const result3 = reconcile(config.runtimeWorkspacesRoot, { protectedCommandIds });
|
|
12032
13555
|
if (result3.failed > 0) {
|
|
12033
|
-
|
|
13556
|
+
const reasons = Array.isArray(result3.failures) ? result3.failures.slice(0, 3).map((failure) => readString(failure?.error) ?? "unknown").join("; ") : "unknown";
|
|
13557
|
+
throw new Error(`${executorKind.toLowerCase()}_managed_mcp_restart_cleanup_failed: ${result3.failed}/${result3.scanned} owned profile(s) could not be reconciled: ${reasons}`);
|
|
12034
13558
|
}
|
|
12035
13559
|
if (result3.removed > 0) {
|
|
12036
13560
|
process.stderr.write(`AMaster daemon removed ${result3.removed} stale marker-owned ${executorKind} managed MCP artifact(s) during startup reconciliation.
|
|
@@ -12054,6 +13578,7 @@ async function runOnce(config) {
|
|
|
12054
13578
|
}
|
|
12055
13579
|
}
|
|
12056
13580
|
await flushResultOutbox(config);
|
|
13581
|
+
await flushRunCompletionStates(config);
|
|
12057
13582
|
const availableSlots = availableCommandSlots(config);
|
|
12058
13583
|
if (availableSlots <= 0) return 0;
|
|
12059
13584
|
const leaseId = `amaster-${Date.now()}`;
|
|
@@ -12085,6 +13610,7 @@ async function dispatchAvailableCommands(config, options = {}) {
|
|
|
12085
13610
|
}
|
|
12086
13611
|
}
|
|
12087
13612
|
await flushResultOutbox(config);
|
|
13613
|
+
await flushRunCompletionStates(config);
|
|
12088
13614
|
const availableSlots = availableCommandSlots(config);
|
|
12089
13615
|
if (availableSlots <= 0) return 0;
|
|
12090
13616
|
const leaseId = `amaster-${Date.now()}`;
|