@amaster.ai/employee-runtime-connector 0.1.0-beta.46 → 0.1.0-beta.47
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 +2181 -195
- package/dist/amaster-runtime.mjs +1 -1
- package/package.json +4 -1
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// MirrorX runtime connector daemon bundle.
|
|
3
3
|
|
|
4
4
|
// src/amaster-runtime-daemon.mjs
|
|
5
|
-
import { createHash as
|
|
6
|
-
import { chmodSync as chmodSync6, copyFileSync as copyFileSync3, existsSync as
|
|
5
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
6
|
+
import { chmodSync as chmodSync6, copyFileSync as copyFileSync3, existsSync as existsSync13, lstatSync as lstatSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync10, readdirSync as readdirSync8, realpathSync as realpathSync4, renameSync as renameSync5, rmSync as rmSync7, statSync as statSync7, symlinkSync as symlinkSync4, unlinkSync, writeFileSync as writeFileSync8 } 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
|
|
9
|
-
import { spawn, spawnSync as
|
|
8
|
+
import { basename as basename6, delimiter as delimiter2, dirname as dirname9, extname as extname2, isAbsolute as isAbsolute8, join as join14, relative as relative9, resolve as resolve12 } from "node:path";
|
|
9
|
+
import { spawn, spawnSync as spawnSync6 } from "node:child_process";
|
|
10
|
+
import { createRequire } from "node:module";
|
|
10
11
|
|
|
11
12
|
// src/amaster-runtime-daemon/common.mjs
|
|
12
13
|
import { homedir } from "node:os";
|
|
@@ -388,7 +389,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
388
389
|
}
|
|
389
390
|
return ptr;
|
|
390
391
|
}
|
|
391
|
-
function skipUntil(str, ptr,
|
|
392
|
+
function skipUntil(str, ptr, sep3, end, banNewLines = false) {
|
|
392
393
|
if (!end) {
|
|
393
394
|
ptr = indexOfNewline(str, ptr);
|
|
394
395
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -397,7 +398,7 @@ function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
|
397
398
|
let c = str[i];
|
|
398
399
|
if (c === "#") {
|
|
399
400
|
i = indexOfNewline(str, i);
|
|
400
|
-
} else if (c ===
|
|
401
|
+
} else if (c === sep3) {
|
|
401
402
|
return i + 1;
|
|
402
403
|
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
403
404
|
return i;
|
|
@@ -1558,6 +1559,7 @@ import {
|
|
|
1558
1559
|
lstatSync as lstatSync2,
|
|
1559
1560
|
mkdirSync as mkdirSync3,
|
|
1560
1561
|
readFileSync as readFileSync3,
|
|
1562
|
+
realpathSync,
|
|
1561
1563
|
readdirSync as readdirSync2,
|
|
1562
1564
|
rmSync as rmSync2,
|
|
1563
1565
|
statSync as statSync2,
|
|
@@ -1778,7 +1780,10 @@ function syncAmasterProviderFiles(agentDir, executorEnv) {
|
|
|
1778
1780
|
}
|
|
1779
1781
|
|
|
1780
1782
|
// src/amaster-runtime-daemon/pi-managed-mcp-profile.mjs
|
|
1781
|
-
var
|
|
1783
|
+
var MANAGED_PI_MCP_TOOL_MODE = "proxy_only";
|
|
1784
|
+
function createManagedPiMcpProfileApi(options = {}) {
|
|
1785
|
+
const spawnSyncImpl = typeof options.spawnSync === "function" ? options.spawnSync : spawnSync2;
|
|
1786
|
+
const nowImpl = typeof options.now === "function" ? options.now : Date.now;
|
|
1782
1787
|
const SUPPORTED_SCHEMA_VERSION2 = "amaster.governed-mcp.v1";
|
|
1783
1788
|
const SUPPORTED_SERVER_NAME2 = "amaster";
|
|
1784
1789
|
const MINIMUM_PI_VERSION = [0, 73, 1];
|
|
@@ -1878,6 +1883,8 @@ var piManagedMcpProfileApi = (() => {
|
|
|
1878
1883
|
const DEFAULT_SESSION_ROLLOUT_TTL_MS2 = 24 * 60 * 60 * 1e3;
|
|
1879
1884
|
const PI_ATTESTATION_TIMEOUT_MS = 1e4;
|
|
1880
1885
|
const PI_ATTESTATION_MAX_ATTEMPTS = 2;
|
|
1886
|
+
const PI_ATTESTATION_RECENT_LIVE_TTL_MS = Number.isFinite(options.recentLiveTtlMs) && options.recentLiveTtlMs > 0 ? options.recentLiveTtlMs : 30 * 60 * 1e3;
|
|
1887
|
+
const recentLivePiVersionProbes = /* @__PURE__ */ new Map();
|
|
1881
1888
|
function record4(value) {
|
|
1882
1889
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1883
1890
|
}
|
|
@@ -1983,6 +1990,33 @@ var piManagedMcpProfileApi = (() => {
|
|
|
1983
1990
|
}
|
|
1984
1991
|
return tuple.join(".");
|
|
1985
1992
|
}
|
|
1993
|
+
function currentTimeMs() {
|
|
1994
|
+
const value = Number(nowImpl());
|
|
1995
|
+
if (!Number.isFinite(value)) throw new Error("pi_managed_mcp_attestation_failed: invalid attestation clock");
|
|
1996
|
+
return value;
|
|
1997
|
+
}
|
|
1998
|
+
function piExecutableIdentity(executorCommand) {
|
|
1999
|
+
try {
|
|
2000
|
+
const realPath = realpathSync(executorCommand);
|
|
2001
|
+
const executableStat = lstatSync2(realPath);
|
|
2002
|
+
if (!executableStat.isFile() || executableStat.isSymbolicLink()) {
|
|
2003
|
+
throw Object.assign(new Error("Pi executable is not a regular file"), { code: "EUNSAFE" });
|
|
2004
|
+
}
|
|
2005
|
+
const contentSha256 = createHash2("sha256").update(readFileSync3(realPath)).digest("hex");
|
|
2006
|
+
return `sha256:${createHash2("sha256").update(JSON.stringify({ realPath, contentSha256 })).digest("hex")}`;
|
|
2007
|
+
} catch (error) {
|
|
2008
|
+
const errorCode = typeof error?.code === "string" ? error.code : "UNKNOWN";
|
|
2009
|
+
throw new Error(`pi_managed_mcp_attestation_failed: --version error=${errorCode}`);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
function pruneRecentLivePiVersionProbes(nowMs) {
|
|
2013
|
+
for (const [identity2, entry] of recentLivePiVersionProbes) {
|
|
2014
|
+
const ageMs = nowMs - entry.attestedAtMs;
|
|
2015
|
+
if (ageMs < 0 || ageMs > PI_ATTESTATION_RECENT_LIVE_TTL_MS) {
|
|
2016
|
+
recentLivePiVersionProbes.delete(identity2);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
1986
2020
|
function validateAuthority2(input) {
|
|
1987
2021
|
const runtimeAuth = record4(input.runtimeAuth);
|
|
1988
2022
|
const gateway = record4(runtimeAuth.governedMcp);
|
|
@@ -2212,9 +2246,10 @@ var piManagedMcpProfileApi = (() => {
|
|
|
2212
2246
|
return { adapterVersion, protectedValues };
|
|
2213
2247
|
}
|
|
2214
2248
|
function attestPi(executorCommand, env, configPath, expectedConfig) {
|
|
2215
|
-
|
|
2249
|
+
const executorIdentity = piExecutableIdentity(executorCommand);
|
|
2250
|
+
let result3;
|
|
2216
2251
|
for (let attempt = 1; attempt <= PI_ATTESTATION_MAX_ATTEMPTS; attempt += 1) {
|
|
2217
|
-
|
|
2252
|
+
result3 = spawnSyncImpl(executorCommand, ["--version"], {
|
|
2218
2253
|
cwd: env.AMASTER_RUNTIME_EXECUTION_WORKDIR,
|
|
2219
2254
|
env,
|
|
2220
2255
|
encoding: "utf8",
|
|
@@ -2222,18 +2257,41 @@ var piManagedMcpProfileApi = (() => {
|
|
|
2222
2257
|
killSignal: "SIGKILL",
|
|
2223
2258
|
maxBuffer: 1024 * 1024
|
|
2224
2259
|
});
|
|
2225
|
-
if (
|
|
2260
|
+
if (result3.error?.code === "ETIMEDOUT" && attempt < PI_ATTESTATION_MAX_ATTEMPTS) continue;
|
|
2226
2261
|
break;
|
|
2227
2262
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2263
|
+
let executorVersion;
|
|
2264
|
+
let executorVersionSource;
|
|
2265
|
+
let liveProbeAttestedAtMs;
|
|
2266
|
+
let liveProbeAgeMs;
|
|
2267
|
+
if (result3.error?.code === "ETIMEDOUT") {
|
|
2268
|
+
const identityAfterTimeout = piExecutableIdentity(executorCommand);
|
|
2269
|
+
const nowMs = currentTimeMs();
|
|
2270
|
+
pruneRecentLivePiVersionProbes(nowMs);
|
|
2271
|
+
const recentLiveProbe = identityAfterTimeout === executorIdentity ? recentLivePiVersionProbes.get(executorIdentity) : null;
|
|
2272
|
+
const ageMs = recentLiveProbe ? nowMs - recentLiveProbe.attestedAtMs : Number.POSITIVE_INFINITY;
|
|
2273
|
+
if (!recentLiveProbe || ageMs < 0 || ageMs > PI_ATTESTATION_RECENT_LIVE_TTL_MS) {
|
|
2274
|
+
throw new Error("pi_managed_mcp_attestation_failed: --version error=ETIMEDOUT");
|
|
2275
|
+
}
|
|
2276
|
+
executorVersion = recentLiveProbe.executorVersion;
|
|
2277
|
+
executorVersionSource = "recent_live_probe_reuse";
|
|
2278
|
+
liveProbeAttestedAtMs = recentLiveProbe.attestedAtMs;
|
|
2279
|
+
liveProbeAgeMs = ageMs;
|
|
2280
|
+
} else if (result3.error) {
|
|
2281
|
+
const errorCode = typeof result3.error.code === "string" ? result3.error.code : "UNKNOWN";
|
|
2230
2282
|
throw new Error(`pi_managed_mcp_attestation_failed: --version error=${errorCode}`);
|
|
2283
|
+
} else if (result3.status !== 0) {
|
|
2284
|
+
throw new Error(`pi_managed_mcp_attestation_failed: --version exit=${result3.status ?? "unknown"}`);
|
|
2285
|
+
} else {
|
|
2286
|
+
executorVersion = parseVersion(`${result3.stdout ?? ""}
|
|
2287
|
+
${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
2288
|
+
if (piExecutableIdentity(executorCommand) !== executorIdentity) {
|
|
2289
|
+
throw new Error("pi_managed_mcp_attestation_failed: Pi executable changed during --version probe");
|
|
2290
|
+
}
|
|
2291
|
+
executorVersionSource = "live_probe";
|
|
2292
|
+
liveProbeAttestedAtMs = currentTimeMs();
|
|
2293
|
+
liveProbeAgeMs = 0;
|
|
2231
2294
|
}
|
|
2232
|
-
if (result2.status !== 0) {
|
|
2233
|
-
throw new Error(`pi_managed_mcp_attestation_failed: --version exit=${result2.status ?? "unknown"}`);
|
|
2234
|
-
}
|
|
2235
|
-
const executorVersion = parseVersion(`${result2.stdout ?? ""}
|
|
2236
|
-
${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
2237
2295
|
let effectiveConfig;
|
|
2238
2296
|
try {
|
|
2239
2297
|
effectiveConfig = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
@@ -2243,7 +2301,20 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2243
2301
|
if (JSON.stringify(effectiveConfig) !== JSON.stringify(expectedConfig) || env.PI_AGENT_MCP_SERVERS_FILE !== configPath) {
|
|
2244
2302
|
throw new Error("pi_managed_mcp_attestation_failed: effective config does not match managed amaster Gateway");
|
|
2245
2303
|
}
|
|
2246
|
-
|
|
2304
|
+
if (executorVersionSource === "live_probe") {
|
|
2305
|
+
pruneRecentLivePiVersionProbes(liveProbeAttestedAtMs);
|
|
2306
|
+
recentLivePiVersionProbes.set(executorIdentity, {
|
|
2307
|
+
executorVersion,
|
|
2308
|
+
attestedAtMs: liveProbeAttestedAtMs
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
return {
|
|
2312
|
+
executorVersion,
|
|
2313
|
+
executorIdentity,
|
|
2314
|
+
executorVersionSource,
|
|
2315
|
+
liveProbeAttestedAt: new Date(liveProbeAttestedAtMs).toISOString(),
|
|
2316
|
+
liveProbeAgeMs
|
|
2317
|
+
};
|
|
2247
2318
|
}
|
|
2248
2319
|
function prepareManagedPiMcpProfile2(input) {
|
|
2249
2320
|
assertInvocationIsolation2(input);
|
|
@@ -2300,12 +2371,13 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2300
2371
|
PI_AGENT_MCP_SERVERS_FILE: configPath,
|
|
2301
2372
|
TMPDIR: tmp
|
|
2302
2373
|
};
|
|
2303
|
-
const
|
|
2374
|
+
const executorAttestation = attestPi(nonEmpty2(input.executorCommand, "executorCommand"), env, configPath, config);
|
|
2304
2375
|
const attestationFacts = {
|
|
2305
2376
|
connectorVersion: nonEmpty2(input.connectorVersion, "connectorVersion"),
|
|
2306
2377
|
executorKind: "pi",
|
|
2307
|
-
|
|
2378
|
+
...executorAttestation,
|
|
2308
2379
|
mcpAdapterVersion: seededRuntime.adapterVersion,
|
|
2380
|
+
mcpToolMode: MANAGED_PI_MCP_TOOL_MODE,
|
|
2309
2381
|
configMode: "isolated_home_run_scoped_mcp_file",
|
|
2310
2382
|
namespace: SUPPORTED_SERVER_NAME2,
|
|
2311
2383
|
schemaVersion: SUPPORTED_SCHEMA_VERSION2,
|
|
@@ -2396,7 +2468,7 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2396
2468
|
if (existsSync3(profileRoot)) throw new Error("pi_managed_mcp_cleanup_failed: profile root still exists");
|
|
2397
2469
|
return { status: "removed", profileRoot };
|
|
2398
2470
|
}
|
|
2399
|
-
function reconcileManagedPiMcpProfiles2(rootPath,
|
|
2471
|
+
function reconcileManagedPiMcpProfiles2(rootPath, options2 = {}) {
|
|
2400
2472
|
const root = resolve2(rootPath);
|
|
2401
2473
|
if (!existsSync3(root)) return { scanned: 0, removed: 0, removedProfiles: 0, removedRollouts: 0, failed: 0, failures: [] };
|
|
2402
2474
|
const profileMarkers = [];
|
|
@@ -2427,8 +2499,8 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2427
2499
|
failures.push({ profileRoot, error: error instanceof Error ? error.message : String(error) });
|
|
2428
2500
|
}
|
|
2429
2501
|
}
|
|
2430
|
-
const nowMs = Number.isFinite(
|
|
2431
|
-
const rolloutTtlMs = Number.isFinite(
|
|
2502
|
+
const nowMs = Number.isFinite(options2.nowMs) ? options2.nowMs : Date.now();
|
|
2503
|
+
const rolloutTtlMs = Number.isFinite(options2.sessionRolloutTtlMs) && options2.sessionRolloutTtlMs > 0 ? options2.sessionRolloutTtlMs : DEFAULT_SESSION_ROLLOUT_TTL_MS2;
|
|
2432
2504
|
for (const markerPath of rolloutMarkers) {
|
|
2433
2505
|
const cacheRoot = dirname3(markerPath);
|
|
2434
2506
|
try {
|
|
@@ -2456,7 +2528,8 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2456
2528
|
};
|
|
2457
2529
|
}
|
|
2458
2530
|
return { prepareManagedPiMcpProfile: prepareManagedPiMcpProfile2, preserveManagedPiSessionRollout: preserveManagedPiSessionRollout2, cleanupManagedPiMcpProfile: cleanupManagedPiMcpProfile2, reconcileManagedPiMcpProfiles: reconcileManagedPiMcpProfiles2 };
|
|
2459
|
-
}
|
|
2531
|
+
}
|
|
2532
|
+
var piManagedMcpProfileApi = createManagedPiMcpProfileApi();
|
|
2460
2533
|
var prepareManagedPiMcpProfile = piManagedMcpProfileApi.prepareManagedPiMcpProfile;
|
|
2461
2534
|
var preserveManagedPiSessionRollout = piManagedMcpProfileApi.preserveManagedPiSessionRollout;
|
|
2462
2535
|
var cleanupManagedPiMcpProfile = piManagedMcpProfileApi.cleanupManagedPiMcpProfile;
|
|
@@ -2528,12 +2601,12 @@ function record2(value) {
|
|
|
2528
2601
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2529
2602
|
}
|
|
2530
2603
|
function managedChildSpawnInvocation(command, args, requestedIdentity) {
|
|
2531
|
-
const
|
|
2532
|
-
const childUmask =
|
|
2604
|
+
const identity2 = record2(requestedIdentity);
|
|
2605
|
+
const childUmask = identity2.umask;
|
|
2533
2606
|
if (childUmask !== void 0 && (!Number.isInteger(childUmask) || childUmask < 0 || childUmask > 511)) {
|
|
2534
2607
|
throw new Error("executor_spawn_umask_invalid");
|
|
2535
2608
|
}
|
|
2536
|
-
const spawnIdentity = { ...
|
|
2609
|
+
const spawnIdentity = { ...identity2 };
|
|
2537
2610
|
delete spawnIdentity.umask;
|
|
2538
2611
|
if (childUmask === void 0) {
|
|
2539
2612
|
return { command, args, spawnIdentity };
|
|
@@ -2697,7 +2770,10 @@ function fixedRules(input, includeIssueLine) {
|
|
|
2697
2770
|
input.managed ? `- source workspace: ${input.sourceWorkspacePath}` : "",
|
|
2698
2771
|
input.managed ? "Use the execution workspace as cwd. Never recursively scan the source workspace, the user's home directory, or any path outside the execution workspace. Only inspect a specific external source path when the task explicitly requires that exact path." : "",
|
|
2699
2772
|
input.wakeReason ? `- wake reason: ${input.wakeReason}` : "",
|
|
2700
|
-
input.hasGovernedMcp ? "- AMaster mutations are available only through the managed amaster MCP server. Use discovered tools and immediate typed results; never call AMaster mutation REST endpoints directly." : "- No mutation channel is available for this command. Keep the run read-only and report the missing Runtime V2 capability as a blocker."
|
|
2773
|
+
input.hasGovernedMcp ? "- AMaster mutations are available only through the managed amaster MCP server. Use discovered tools and immediate typed results; never call AMaster mutation REST endpoints directly." : "- No mutation channel is available for this command. Keep the run read-only and report the missing Runtime V2 capability as a blocker.",
|
|
2774
|
+
"- Runtime Artifact lineage fields sourceWorkProductId and sourceWorkProductIds accept only current artifact work product ids returned as result.effectResult.workProductId by a succeeded upload_artifact action. Never pass a documentId or revisionId as artifact lineage.",
|
|
2775
|
+
"- When all inputs are issue documents and there is no source artifact work product, persist the derived result with upsert_document and do not call upload_artifact for a derived or review artifact.",
|
|
2776
|
+
input.hasGovernedMcp && input.executorKind === "pi" && input.managedMcpToolMode === "proxy_only" ? "- For proxy-only Pi MCP writes, put the complete target argument object directly in the actual `mcp` call's string `args` field, even for long document bodies. Do not create shell, script, or file intermediates to stage or quote a would-be tool call, and do not narrate or print it as prose. After the needed `runtime_action.describe` result, emit the write call before further planning." : ""
|
|
2701
2777
|
].filter(Boolean).join("\n");
|
|
2702
2778
|
}
|
|
2703
2779
|
function approvalContinuationText(input) {
|
|
@@ -2726,25 +2802,79 @@ function recoveryInstructionText(input) {
|
|
|
2726
2802
|
"The prior successful run did not leave a terminal task disposition.",
|
|
2727
2803
|
"Do not repeat the original source work or create or revise deliverables.",
|
|
2728
2804
|
"Inspect the existing task and run evidence, then choose exactly one explicit disposition: done, in_review, input, blocked, delegated, or queued.",
|
|
2729
|
-
"If more work remains on this issue,
|
|
2805
|
+
"If more work remains on this issue, choose continuation/todo and then execute the Runtime Action Continuation Option rendered below. Do not guess its transport shape.",
|
|
2730
2806
|
"Do not keep or write `in_progress`: only the actual `todo` issue update is a recognized resume disposition and lets the control plane queue a normal-model continuation.",
|
|
2731
2807
|
"Record the disposition with its concrete owner or next action and update the task accordingly."
|
|
2732
2808
|
].join("\n");
|
|
2733
2809
|
}
|
|
2734
2810
|
if (input.wakeReason === "source_scoped_recovery_action") {
|
|
2811
|
+
const wake = asRecord(context.paperclipWake);
|
|
2812
|
+
const unresolvedBlockerIssueIds = Array.isArray(wake.unresolvedBlockerIssueIds) ? wake.unresolvedBlockerIssueIds.map(readString).filter(Boolean) : [];
|
|
2813
|
+
const blockedDisposition = unresolvedBlockerIssueIds.length > 0 ? `- blocked only when the issue already has an unresolved first-class blocker; name the exact blocker and wait condition from: ${unresolvedBlockerIssueIds.join(", ")};` : [
|
|
2814
|
+
"- if source work remains on this issue, choose continuation/todo and then execute the Runtime Action Continuation Option rendered below so the control plane can queue a normal-model continuation;",
|
|
2815
|
+
"- do not set `blocked` without an unresolved first-class blocker; a named owner or proposed next action alone is not a blocker;"
|
|
2816
|
+
].join("\n");
|
|
2735
2817
|
return [
|
|
2736
2818
|
"This is a status-only source recovery. Do not repeat the original source work or create or revise deliverables.",
|
|
2737
2819
|
"Inspect the existing task and run evidence, then choose exactly one explicit disposition using task-governance actions:",
|
|
2738
2820
|
"- done only when the existing evidence already satisfies acceptance;",
|
|
2739
2821
|
"- in_review or input when a specific human review or answer is required;",
|
|
2740
|
-
|
|
2822
|
+
blockedDisposition,
|
|
2741
2823
|
"- delegated or queued only with a concrete continuation path.",
|
|
2742
2824
|
"Record the disposition in a comment and update the parent task accordingly."
|
|
2743
2825
|
].join("\n");
|
|
2744
2826
|
}
|
|
2745
2827
|
return "";
|
|
2746
2828
|
}
|
|
2747
|
-
function
|
|
2829
|
+
function continuationRuntimeActionEnvelope(context) {
|
|
2830
|
+
const envelope = asRecord(context.continuationRuntimeActionEnvelope);
|
|
2831
|
+
const action = asRecord(envelope.action);
|
|
2832
|
+
if (!readString(envelope.schemaVersion) || !readString(envelope.idempotencyKey) || action.type !== "update_parent" || action.status !== "todo" || !readString(action.comment)) return null;
|
|
2833
|
+
return envelope;
|
|
2834
|
+
}
|
|
2835
|
+
function runtimeActionContinuationOptionText(input) {
|
|
2836
|
+
if (!isRecoveryWakeReason(input.wakeReason)) return "";
|
|
2837
|
+
const heading = "### Runtime Action Continuation Option";
|
|
2838
|
+
if (!input.hasGovernedMcp) {
|
|
2839
|
+
return `${heading}
|
|
2840
|
+
Runtime Action continuation is unavailable: managed governed MCP capability is missing. Do not guess a tool call.`;
|
|
2841
|
+
}
|
|
2842
|
+
const envelope = continuationRuntimeActionEnvelope(asRecord(input.context));
|
|
2843
|
+
if (!envelope) {
|
|
2844
|
+
return `${heading}
|
|
2845
|
+
Runtime Action continuation is unavailable: continuationRuntimeActionEnvelope is missing or invalid. Do not guess a tool call.`;
|
|
2846
|
+
}
|
|
2847
|
+
if (input.executorKind === "pi") {
|
|
2848
|
+
if (input.managedMcpToolMode !== "proxy_only") {
|
|
2849
|
+
return `${heading}
|
|
2850
|
+
Runtime Action continuation is unavailable: managed Pi MCP tool mode is not proxy_only. Do not guess a proxy or direct-tool call.`;
|
|
2851
|
+
}
|
|
2852
|
+
const proxyCall = {
|
|
2853
|
+
server: "amaster",
|
|
2854
|
+
tool: "runtime_action.submit",
|
|
2855
|
+
args: JSON.stringify(envelope)
|
|
2856
|
+
};
|
|
2857
|
+
return [
|
|
2858
|
+
heading,
|
|
2859
|
+
"Only after selecting continuation/todo from the disposition menu, emit an actual `mcp` tool call with the exact proxy arguments below. Do not print this JSON as prose and do not add an outer `action` field.",
|
|
2860
|
+
"```json",
|
|
2861
|
+
jsonText(proxyCall),
|
|
2862
|
+
"```"
|
|
2863
|
+
].join("\n");
|
|
2864
|
+
}
|
|
2865
|
+
if (input.executorKind === "codex") {
|
|
2866
|
+
return [
|
|
2867
|
+
heading,
|
|
2868
|
+
"Only after selecting continuation/todo from the disposition menu, emit an actual `runtime_action.submit` tool call with the exact arguments below. Do not print this JSON as prose.",
|
|
2869
|
+
"```json",
|
|
2870
|
+
jsonText(envelope),
|
|
2871
|
+
"```"
|
|
2872
|
+
].join("\n");
|
|
2873
|
+
}
|
|
2874
|
+
return `${heading}
|
|
2875
|
+
Runtime Action continuation is unavailable: executor kind does not support a governed Runtime Action continuation. Do not guess a tool call.`;
|
|
2876
|
+
}
|
|
2877
|
+
function runtimeAuthorizationText(context, mode, { suppressOrdinaryCompletionContract = false } = {}) {
|
|
2748
2878
|
const envelope = asRecord(context.authorizationEnvelope);
|
|
2749
2879
|
const allowed = new Set(
|
|
2750
2880
|
Array.isArray(envelope.allowedActionClasses) ? envelope.allowedActionClasses.filter((value) => typeof value === "string") : []
|
|
@@ -2754,13 +2884,23 @@ function runtimeAuthorizationText(context) {
|
|
|
2754
2884
|
authorizationClass,
|
|
2755
2885
|
optionId: readString(optionId)
|
|
2756
2886
|
})).filter((entry) => entry.optionId && !allowed.has(entry.authorizationClass));
|
|
2757
|
-
|
|
2758
|
-
|
|
2887
|
+
const issue = asRecord(context.paperclipIssue);
|
|
2888
|
+
const completion = asRecord(asRecord(issue.taskRequirements).completion);
|
|
2889
|
+
const completionRole = readString(completion.role);
|
|
2890
|
+
const completionDeliverable = readString(completion.deliverable);
|
|
2891
|
+
const firstDocumentCheckpoint = completionDeliverable === "document_or_artifact" ? mode === "cold" ? "Before the first external browse, search, or research action, call upsert_document with a stable key. Create a checkpoint skeleton containing the acceptance criteria, a source table, and known unknowns; update the same document with each piece of verified evidence instead of waiting until broad research is complete. Immediately after upsert_document, call update_parent with status: in_progress and a concrete next action; complete both control-plane writes before the first external browse, search, or research action." : "Before the first external browse, search, or research action, read the existing stable-key document. Preserve all still-valid verified rows, source URLs, conclusions, and known unknowns; do not replace a populated document with a blank skeleton or less-informative fallback. Only when the document does not exist may you create a checkpoint skeleton. Then call update_parent with status: in_progress and a concrete next action before the first external browse, search, or research action. Update the same document with each piece of verified evidence." : "";
|
|
2892
|
+
const completionContract = !suppressOrdinaryCompletionContract && completionRole && completionDeliverable ? [
|
|
2893
|
+
"Before ending this run, persist durable progress through Runtime Actions and record one explicit task disposition. Browser and tool history alone are not durable progress.",
|
|
2894
|
+
firstDocumentCheckpoint,
|
|
2895
|
+
"If work remains in an ordinary productive run, call update_parent with status: in_progress and a concrete next action so bounded continuation recovery can preserve a live path. Status: todo does not queue a normal continuation from an ordinary productive run. Only a server-issued successful-run handoff recovery may use status: todo, and only according to its exact Recovery Instruction. Otherwise use a supported terminal or waiting disposition."
|
|
2896
|
+
].filter(Boolean).join(" ") : "";
|
|
2897
|
+
const authorizationContract = missing.length > 0 ? [
|
|
2759
2898
|
`Current allowed action classes: ${[...allowed].join(", ") || "task_governance"}.`,
|
|
2760
2899
|
"If the task requires a missing runtime action class, create a request_checkbox_confirmation interaction using the exact option id below and wait for its accepted continuation:",
|
|
2761
2900
|
...missing.map((entry) => `- ${entry.authorizationClass}: ${entry.optionId}`),
|
|
2762
2901
|
"Never use request_confirmation to authorize a runtime action class."
|
|
2763
|
-
].join("\n");
|
|
2902
|
+
].join("\n") : "";
|
|
2903
|
+
return [completionContract, authorizationContract].filter(Boolean).join("\n");
|
|
2764
2904
|
}
|
|
2765
2905
|
function runtimeDecompositionRequirementText(context) {
|
|
2766
2906
|
const issue = asRecord(context.paperclipIssue);
|
|
@@ -2778,7 +2918,10 @@ function runtimeDecompositionRequirementText(context) {
|
|
|
2778
2918
|
}
|
|
2779
2919
|
return [
|
|
2780
2920
|
"This issue has a server-enforced typed decomposition requirement.",
|
|
2781
|
-
"Create real direct child
|
|
2921
|
+
"Create the complete real direct child graph before doing any substantial source work.",
|
|
2922
|
+
"Before browsing, searching, commenting, or doing any source work, call runtime_action.describe for create_child_task, persist the complete child graph with runtime_action.plan, and execute it with runtime_action.commit.",
|
|
2923
|
+
"Once runtime_action.commit succeeds, the committed required child graph is this parent run's durable delegated live disposition.",
|
|
2924
|
+
"After runtime_action.commit succeeds, yield and end the parent run immediately. Do not browse, search, research, or execute any delegated child acceptance scope, and do not poll child runs. Child assignment runs are the sole execution path for delegated child scope.",
|
|
2782
2925
|
"Each executable child must have an owner, dependencies where needed, and acceptance criteria. Do not create probe or test children.",
|
|
2783
2926
|
"Preserve every parent safety and approval boundary in each child title, description, and acceptance criteria. If the parent prohibits registration, payment, booking, or external contact without later explicit authorization, create preparation or approval only children and do not assign the prohibited action.",
|
|
2784
2927
|
`Requirement source: ${sourceType}:${sourceId}@${sourceRevision}`
|
|
@@ -2816,12 +2959,12 @@ function manifestEntry(section) {
|
|
|
2816
2959
|
truncationReason: section.truncationReason ?? null
|
|
2817
2960
|
};
|
|
2818
2961
|
}
|
|
2819
|
-
function renderPrompt(sections, manifest) {
|
|
2962
|
+
function renderPrompt(sections, manifest, compactManifest = false) {
|
|
2820
2963
|
return [
|
|
2821
2964
|
...sections.map(sectionText).filter(Boolean),
|
|
2822
2965
|
"## Context Manifest",
|
|
2823
2966
|
"```json",
|
|
2824
|
-
jsonText(manifest),
|
|
2967
|
+
compactManifest ? JSON.stringify(manifest) : jsonText(manifest),
|
|
2825
2968
|
"```"
|
|
2826
2969
|
].join("\n");
|
|
2827
2970
|
}
|
|
@@ -2845,6 +2988,10 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2845
2988
|
}
|
|
2846
2989
|
const context = asRecord(input.context);
|
|
2847
2990
|
const mode = contextMode(input);
|
|
2991
|
+
const recoveryInstruction = recoveryInstructionText(input);
|
|
2992
|
+
const recoveryContinuationOption = recoveryInstruction ? runtimeActionContinuationOptionText(input) : "";
|
|
2993
|
+
const completeRecoveryInstruction = [recoveryInstruction, recoveryContinuationOption].filter(Boolean).join("\n\n");
|
|
2994
|
+
const suppressOrdinaryCompletionContract = completeRecoveryInstruction.length > 0;
|
|
2848
2995
|
const governedReads = governedReadSection(context);
|
|
2849
2996
|
const hasTask = Boolean(readString(input.taskMarkdown));
|
|
2850
2997
|
const taskText = readString(input.taskMarkdown) ?? "";
|
|
@@ -2869,9 +3016,9 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2869
3016
|
const snapshotFreshness = { kind: "run_snapshot" };
|
|
2870
3017
|
const rawSections = [
|
|
2871
3018
|
{ name: "runtime_rules", title: "", priority: 100, sourceRef: `command:${input.commandId}`, content: fixedRules(input, !hasTask) },
|
|
2872
|
-
{ name: "recovery_instruction", title: "Recovery Instruction", priority:
|
|
3019
|
+
{ name: "recovery_instruction", title: "Recovery Instruction", priority: 100, sourceRef: `run:${input.runId ?? "unknown"}`, content: completeRecoveryInstruction, truncationReason: completeRecoveryInstruction ? null : "mode_selection" },
|
|
2873
3020
|
{ name: "approval_continuation", title: "Approved Runtime Action Continuation", priority: 99, sourceRef: `run:${input.runId ?? "unknown"}`, content: approvalContinuationText(input), truncationReason: mode === "continuation" ? null : "mode_selection" },
|
|
2874
|
-
{ name: "runtime_authorization", title: "Runtime Action
|
|
3021
|
+
{ name: "runtime_authorization", title: "Runtime Action Contract", priority: 98, sourceRef: `run:${input.runId ?? "unknown"}`, content: runtimeAuthorizationText(context, mode, { suppressOrdinaryCompletionContract }) },
|
|
2875
3022
|
{ name: "runtime_decomposition_requirement", title: "Required Task Decomposition", priority: 98, sourceRef: `issue:${input.issueId ?? "unknown"}`, content: runtimeDecompositionRequirementText(context) },
|
|
2876
3023
|
{ 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 },
|
|
2877
3024
|
{ name: "wake_comments", title: wakeDeltaTitle, priority: interactionResolution ? 99 : 95, sourceRef: wakeDeltaSourceRefs, originalContent: wakeDeltaOriginal, content: wakeDeltaContent, truncationReason: commentsSelected ? null : "duplicate_task_context" },
|
|
@@ -2901,12 +3048,13 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2901
3048
|
truncationReason: section.content ? section.truncationReason : section.truncationReason ?? "source_absent"
|
|
2902
3049
|
}));
|
|
2903
3050
|
let prompt = "";
|
|
3051
|
+
let compactManifest = false;
|
|
2904
3052
|
let manifest = buildManifest(mode, maxChars, sections, governedReads.provenance, 0);
|
|
2905
3053
|
for (let pass = 0; pass < 20; pass += 1) {
|
|
2906
3054
|
let usedChars = 0;
|
|
2907
3055
|
for (let telemetryPass = 0; telemetryPass < 3; telemetryPass += 1) {
|
|
2908
3056
|
manifest = buildManifest(mode, maxChars, sections, governedReads.provenance, usedChars);
|
|
2909
|
-
prompt = renderPrompt(sections, manifest);
|
|
3057
|
+
prompt = renderPrompt(sections, manifest, compactManifest);
|
|
2910
3058
|
if (prompt.length === usedChars) break;
|
|
2911
3059
|
usedChars = prompt.length;
|
|
2912
3060
|
}
|
|
@@ -2914,8 +3062,13 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2914
3062
|
const overflow = Math.max(1, prompt.length - maxChars);
|
|
2915
3063
|
const candidate = [...sections].filter((section) => section.priority < 100 && section.content.length > 0).sort((left, right) => left.priority - right.priority)[0];
|
|
2916
3064
|
if (!candidate) {
|
|
3065
|
+
if (!compactManifest) {
|
|
3066
|
+
compactManifest = true;
|
|
3067
|
+
manifest = buildManifest(mode, maxChars, sections, governedReads.provenance, 0);
|
|
3068
|
+
continue;
|
|
3069
|
+
}
|
|
2917
3070
|
const fixedChars = sections.filter((section) => section.priority === 100).map(sectionText).join("\n").length;
|
|
2918
|
-
const manifestChars =
|
|
3071
|
+
const manifestChars = JSON.stringify(manifest).length;
|
|
2919
3072
|
throw new Error(`Prompt fixed sections exceed the unified ${maxChars}-character budget: promptChars=${prompt.length} fixedChars=${fixedChars} manifestChars=${manifestChars}`);
|
|
2920
3073
|
}
|
|
2921
3074
|
truncateSection(candidate, Math.max(0, candidate.content.length - overflow - 32));
|
|
@@ -2954,22 +3107,22 @@ function executorDiscoveryEnv(env = process.env) {
|
|
|
2954
3107
|
}
|
|
2955
3108
|
function commandExists(command, env = process.env) {
|
|
2956
3109
|
const commandEnv = executorDiscoveryEnv(env);
|
|
2957
|
-
const
|
|
3110
|
+
const result3 = spawnSync3("sh", ["-lc", `command -v ${quoteShell(command)} >/dev/null 2>&1`], {
|
|
2958
3111
|
env: commandEnv,
|
|
2959
3112
|
stdio: "ignore"
|
|
2960
3113
|
});
|
|
2961
|
-
return
|
|
3114
|
+
return result3.status === 0;
|
|
2962
3115
|
}
|
|
2963
3116
|
function commandVersion(command, env = process.env) {
|
|
2964
3117
|
const commandEnv = executorDiscoveryEnv(env);
|
|
2965
|
-
const
|
|
3118
|
+
const result3 = spawnSync3(command, ["--version"], {
|
|
2966
3119
|
env: commandEnv,
|
|
2967
3120
|
encoding: "utf8",
|
|
2968
3121
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2969
3122
|
timeout: 3e3
|
|
2970
3123
|
});
|
|
2971
|
-
if (
|
|
2972
|
-
return
|
|
3124
|
+
if (result3.status !== 0) return void 0;
|
|
3125
|
+
return result3.stdout.trim().split(/\r?\n/)[0]?.slice(0, 120) || void 0;
|
|
2973
3126
|
}
|
|
2974
3127
|
function discoverExecutors(options = {}) {
|
|
2975
3128
|
const knownExecutors = options.knownExecutors ?? KNOWN_EXECUTORS;
|
|
@@ -3053,8 +3206,8 @@ function stateFilePath(env) {
|
|
|
3053
3206
|
return env.AMASTER_DAEMON_STATE_FILE ? expandHomePath(String(env.AMASTER_DAEMON_STATE_FILE)) : join5(runtimeHome(env), "runtime-connector-state.json");
|
|
3054
3207
|
}
|
|
3055
3208
|
function corruptStatePath(path) {
|
|
3056
|
-
const
|
|
3057
|
-
const base = `${path}.corrupt-${
|
|
3209
|
+
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
3210
|
+
const base = `${path}.corrupt-${timestamp2}Z`;
|
|
3058
3211
|
let candidate = base;
|
|
3059
3212
|
for (let suffix = 1; existsSync4(candidate); suffix += 1) {
|
|
3060
3213
|
candidate = `${base}.${suffix}`;
|
|
@@ -3111,6 +3264,12 @@ function buildConfig(env = process.env, flags = {}) {
|
|
|
3111
3264
|
const runtimeWorkspacesRoot = expandHomePath(
|
|
3112
3265
|
String(flags.runtimeWorkspacesRoot ?? env.AMASTER_RUNTIME_WORKSPACES_ROOT ?? join5(home, "workspaces"))
|
|
3113
3266
|
);
|
|
3267
|
+
const browserSessionStateRoot = expandHomePath(String(
|
|
3268
|
+
flags.browserSessionStateRoot ?? env.AMASTER_BROWSER_SESSION_STATE_ROOT ?? join5(home, "browser-auth")
|
|
3269
|
+
));
|
|
3270
|
+
const browserExecutablePath = String(
|
|
3271
|
+
flags.browserExecutablePath ?? env.AMASTER_BROWSER_EXECUTABLE_PATH ?? ""
|
|
3272
|
+
).trim();
|
|
3114
3273
|
mkdirSync4(runtimeWorkspacesRoot, { recursive: true });
|
|
3115
3274
|
const workspaceBindings = splitList(
|
|
3116
3275
|
flags.workspaceAllowlist ?? env.AMASTER_WORKSPACE_ALLOWLIST ?? env.AMASTER_WORKSPACE_BINDINGS ?? runtimeWorkspacesRoot
|
|
@@ -3177,6 +3336,8 @@ function buildConfig(env = process.env, flags = {}) {
|
|
|
3177
3336
|
flags.orphanReaperIntervalSeconds ?? env.AMASTER_RUNTIME_ORPHAN_REAPER_INTERVAL_SECONDS,
|
|
3178
3337
|
300
|
|
3179
3338
|
),
|
|
3339
|
+
browserSessionStateRoot,
|
|
3340
|
+
browserExecutablePath: browserExecutablePath ? expandHomePath(browserExecutablePath) : "",
|
|
3180
3341
|
runtimeWorkspacesRoot,
|
|
3181
3342
|
state
|
|
3182
3343
|
};
|
|
@@ -3310,9 +3471,9 @@ function filterExecutionStderrForResult(executorKind, stderr) {
|
|
|
3310
3471
|
const trimmed = line.trim();
|
|
3311
3472
|
return !trimmed || !isNoise(trimmed);
|
|
3312
3473
|
});
|
|
3313
|
-
const
|
|
3314
|
-
return hadTrailingNewline &&
|
|
3315
|
-
` :
|
|
3474
|
+
const result3 = kept.join("\n");
|
|
3475
|
+
return hadTrailingNewline && result3 ? `${result3}
|
|
3476
|
+
` : result3;
|
|
3316
3477
|
}
|
|
3317
3478
|
function summarizeCodexEvent(event) {
|
|
3318
3479
|
if (isProviderBookkeepingEvent(event)) return null;
|
|
@@ -3718,8 +3879,8 @@ var PI_TERMINAL_RUNTIME_ACTION_STATUSES = /* @__PURE__ */ new Set([
|
|
|
3718
3879
|
function approvedMcpInvocationSucceeded(results, invocationId) {
|
|
3719
3880
|
const approvedInvocationId = readString(invocationId);
|
|
3720
3881
|
return Boolean(approvedInvocationId && (Array.isArray(results) ? results : []).some((rawResult) => {
|
|
3721
|
-
const
|
|
3722
|
-
return readString(
|
|
3882
|
+
const result3 = asRecord(rawResult);
|
|
3883
|
+
return readString(result3.invocationId) === approvedInvocationId && readString(result3.status) === "succeeded" && !["rejected", "blocked"].includes(readString(result3.providerStatus) ?? "");
|
|
3723
3884
|
}));
|
|
3724
3885
|
}
|
|
3725
3886
|
function governedMcpToolResult(structuredContent) {
|
|
@@ -3733,9 +3894,9 @@ function governedMcpToolResult(structuredContent) {
|
|
|
3733
3894
|
const intentId = readString(effectResult.artifactIntentId);
|
|
3734
3895
|
const manifestId = readString(effectResult.manifestId);
|
|
3735
3896
|
const sourceRelativePath = readString(effectResult.sourceRelativePath);
|
|
3736
|
-
const
|
|
3897
|
+
const sha2563 = readString(effectResult.sha256);
|
|
3737
3898
|
const byteSize = readNumber(effectResult.byteSize, 0);
|
|
3738
|
-
const artifactIntent = providerStatus === "pending_reconcile" && intentId && manifestId && sourceRelativePath && /^[a-f0-9]{64}$/.test(
|
|
3899
|
+
const artifactIntent = providerStatus === "pending_reconcile" && intentId && manifestId && sourceRelativePath && /^[a-f0-9]{64}$/.test(sha2563 ?? "") && Number.isSafeInteger(byteSize) && byteSize > 0 ? { intentId, manifestId, sourceRelativePath, sha256: sha2563, byteSize } : null;
|
|
3739
3900
|
const runtimeActionToolName = readString(providerContent.toolName);
|
|
3740
3901
|
const runtimeAction = runtimeActionToolName?.startsWith("runtime_action.") ? {
|
|
3741
3902
|
toolName: runtimeActionToolName,
|
|
@@ -3755,21 +3916,21 @@ function durablePiRuntimeActionEvidence(results) {
|
|
|
3755
3916
|
const normalizedResults = (Array.isArray(results) ? results : []).map(asRecord);
|
|
3756
3917
|
const writeCallIds = /* @__PURE__ */ new Set();
|
|
3757
3918
|
const writePlanIds = /* @__PURE__ */ new Set();
|
|
3758
|
-
for (const
|
|
3759
|
-
const runtimeAction = asRecord(
|
|
3919
|
+
for (const result3 of normalizedResults) {
|
|
3920
|
+
const runtimeAction = asRecord(result3.runtimeAction);
|
|
3760
3921
|
const toolName = readString(runtimeAction.toolName);
|
|
3761
3922
|
const callId = readString(runtimeAction.callId);
|
|
3762
3923
|
const planId = readString(runtimeAction.planId);
|
|
3763
3924
|
const resultStatus = readString(runtimeAction.resultStatus);
|
|
3764
|
-
const status = readString(
|
|
3765
|
-
const providerStatus = readString(
|
|
3925
|
+
const status = readString(result3.status);
|
|
3926
|
+
const providerStatus = readString(result3.providerStatus);
|
|
3766
3927
|
if (status === "succeeded" && providerStatus === "accepted" && toolName && resultStatus && PI_TERMINAL_RUNTIME_ACTION_STATUSES.has(resultStatus)) {
|
|
3767
3928
|
const isLinkedStatus = toolName === "runtime_action.status" && (callId && writeCallIds.has(callId) || planId && writePlanIds.has(planId));
|
|
3768
3929
|
const isDurableWrite = toolName === "runtime_action.submit" ? Boolean(callId) : toolName === "runtime_action.commit" && Boolean(planId || callId);
|
|
3769
3930
|
if (isLinkedStatus || isDurableWrite) {
|
|
3770
3931
|
return {
|
|
3771
3932
|
kind: "runtime_action",
|
|
3772
|
-
...readString(
|
|
3933
|
+
...readString(result3.invocationId) ? { invocationId: readString(result3.invocationId) } : {},
|
|
3773
3934
|
toolName,
|
|
3774
3935
|
...callId ? { callId } : {},
|
|
3775
3936
|
...planId ? { planId } : {}
|
|
@@ -3819,10 +3980,10 @@ function codexMcpToolResults(event) {
|
|
|
3819
3980
|
if (event?.type !== "item.completed") return [];
|
|
3820
3981
|
const item = asRecord(event.item);
|
|
3821
3982
|
if (item.type !== "mcp_tool_call") return [];
|
|
3822
|
-
const
|
|
3823
|
-
let structuredContent = asRecord(
|
|
3824
|
-
if (Object.keys(structuredContent).length === 0 && Array.isArray(
|
|
3825
|
-
for (const part of
|
|
3983
|
+
const result3 = asRecord(item.result);
|
|
3984
|
+
let structuredContent = asRecord(result3.structuredContent);
|
|
3985
|
+
if (Object.keys(structuredContent).length === 0 && Array.isArray(result3.content)) {
|
|
3986
|
+
for (const part of result3.content) {
|
|
3826
3987
|
const text = readString(asRecord(part).text);
|
|
3827
3988
|
if (!text) continue;
|
|
3828
3989
|
try {
|
|
@@ -4497,7 +4658,7 @@ async function postRuntimeConnectorJsonWithRetry(config, path, payload, options
|
|
|
4497
4658
|
} catch (error) {
|
|
4498
4659
|
lastError = error;
|
|
4499
4660
|
if (attempt >= maxAttempts || !retryableRuntimeConnectorPost(error)) throw error;
|
|
4500
|
-
if (delayMs > 0) await new Promise((
|
|
4661
|
+
if (delayMs > 0) await new Promise((resolve13) => setTimeout(resolve13, delayMs));
|
|
4501
4662
|
}
|
|
4502
4663
|
}
|
|
4503
4664
|
throw lastError;
|
|
@@ -4522,7 +4683,7 @@ var bestEffortPostJson = bestEffortPostRuntimeConnectorJson;
|
|
|
4522
4683
|
|
|
4523
4684
|
// src/amaster-runtime-daemon/runtime-artifact-upload.mjs
|
|
4524
4685
|
import { createHash as createHash3 } from "node:crypto";
|
|
4525
|
-
import { lstatSync as lstatSync3, readFileSync as readFileSync5, realpathSync } from "node:fs";
|
|
4686
|
+
import { lstatSync as lstatSync3, readFileSync as readFileSync5, realpathSync as realpathSync2 } from "node:fs";
|
|
4526
4687
|
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 } from "node:path";
|
|
4527
4688
|
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
4528
4689
|
function requiredString(value, name) {
|
|
@@ -4539,10 +4700,10 @@ function pathWithin(candidate, root) {
|
|
|
4539
4700
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
4540
4701
|
}
|
|
4541
4702
|
function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
|
|
4542
|
-
const root =
|
|
4703
|
+
const root = realpathSync2(resolve3(cwd));
|
|
4543
4704
|
const uploads = /* @__PURE__ */ new Map();
|
|
4544
|
-
for (const
|
|
4545
|
-
const intent =
|
|
4705
|
+
for (const result3 of Array.isArray(mcpToolResults) ? mcpToolResults : []) {
|
|
4706
|
+
const intent = result3 && typeof result3 === "object" && !Array.isArray(result3) ? result3.artifactIntent : null;
|
|
4546
4707
|
if (!intent || typeof intent !== "object" || Array.isArray(intent)) continue;
|
|
4547
4708
|
const intentId = requiredString(intent.intentId, "intentId");
|
|
4548
4709
|
const manifestId = requiredString(intent.manifestId, "manifestId");
|
|
@@ -4560,7 +4721,7 @@ function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
|
|
|
4560
4721
|
}
|
|
4561
4722
|
const sourcePath = resolve3(root, sourceRelativePath);
|
|
4562
4723
|
const stat = lstatSync3(sourcePath);
|
|
4563
|
-
if (!stat.isFile() || stat.isSymbolicLink() || !pathWithin(
|
|
4724
|
+
if (!stat.isFile() || stat.isSymbolicLink() || !pathWithin(realpathSync2(sourcePath), root)) {
|
|
4564
4725
|
throw new Error(`Runtime Artifact ${intentId} source is not an owned regular file`);
|
|
4565
4726
|
}
|
|
4566
4727
|
const body = readFileSync5(sourcePath);
|
|
@@ -4600,8 +4761,8 @@ function createRuntimeArtifactIngestQueue({ ingest, onError }) {
|
|
|
4600
4761
|
let queue = Promise.resolve();
|
|
4601
4762
|
return {
|
|
4602
4763
|
enqueue(results) {
|
|
4603
|
-
const pending = results.filter((
|
|
4604
|
-
const intentId = readString(asRecord(
|
|
4764
|
+
const pending = results.filter((result3) => {
|
|
4765
|
+
const intentId = readString(asRecord(result3).artifactIntent?.intentId);
|
|
4605
4766
|
if (!intentId || handledIntentIds.has(intentId)) return false;
|
|
4606
4767
|
handledIntentIds.add(intentId);
|
|
4607
4768
|
return true;
|
|
@@ -4631,7 +4792,7 @@ function createRuntimeArtifactIngestQueue({ ingest, onError }) {
|
|
|
4631
4792
|
|
|
4632
4793
|
// src/amaster-runtime-daemon/workspace-guard.mjs
|
|
4633
4794
|
import { createHash as createHash4 } from "node:crypto";
|
|
4634
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync5, realpathSync as
|
|
4795
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
4635
4796
|
import { basename as basename4, join as join7, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4 } from "node:path";
|
|
4636
4797
|
|
|
4637
4798
|
// src/amaster-runtime-daemon/workspace-manifest.mjs
|
|
@@ -4723,10 +4884,10 @@ function resolveWorkspaceCwd(config, command) {
|
|
|
4723
4884
|
const payload = asRecord(command.payload);
|
|
4724
4885
|
const requested = readString(payload.workspacePath);
|
|
4725
4886
|
const fallback = config.workspaceBindings[0] ?? process.cwd();
|
|
4726
|
-
const cwd =
|
|
4887
|
+
const cwd = realpathSync3(resolve4(expandHomePath(requested ?? fallback)));
|
|
4727
4888
|
const allowlist = config.workspaceBindings.flatMap((entry) => {
|
|
4728
4889
|
try {
|
|
4729
|
-
return [
|
|
4890
|
+
return [realpathSync3(resolve4(expandHomePath(entry)))];
|
|
4730
4891
|
} catch {
|
|
4731
4892
|
return [];
|
|
4732
4893
|
}
|
|
@@ -4767,7 +4928,7 @@ function workspaceLabel(sourceWorkspacePath, payload) {
|
|
|
4767
4928
|
function workspacesRoot(config) {
|
|
4768
4929
|
const root = resolve4(expandHomePath(config.runtimeWorkspacesRoot ?? "~/.amaster-employee/workspaces"));
|
|
4769
4930
|
mkdirSync5(root, { recursive: true });
|
|
4770
|
-
return
|
|
4931
|
+
return realpathSync3(root);
|
|
4771
4932
|
}
|
|
4772
4933
|
function resolveExecutionWorkspace(config, command, opts = {}) {
|
|
4773
4934
|
const sourceWorkspacePath = resolveWorkspaceCwd(config, command);
|
|
@@ -4895,7 +5056,7 @@ function planManagedWorkspaceGcDryRun(input) {
|
|
|
4895
5056
|
const ttlMs = ttlHours * 60 * 60 * 1e3;
|
|
4896
5057
|
const activeRunIds = new Set(input.activeRunIds ?? []);
|
|
4897
5058
|
const activeCommandIds = new Set(input.activeCommandIds ?? []);
|
|
4898
|
-
const
|
|
5059
|
+
const result3 = {
|
|
4899
5060
|
dryRun: true,
|
|
4900
5061
|
root,
|
|
4901
5062
|
generatedAt: now.toISOString(),
|
|
@@ -4905,31 +5066,31 @@ function planManagedWorkspaceGcDryRun(input) {
|
|
|
4905
5066
|
candidates: [],
|
|
4906
5067
|
protected: []
|
|
4907
5068
|
};
|
|
4908
|
-
if (!root || !existsSync7(root)) return
|
|
5069
|
+
if (!root || !existsSync7(root)) return result3;
|
|
4909
5070
|
for (const workdir of walkWorkdirs(root)) {
|
|
4910
5071
|
const manifest = readWorkspaceManifest(workspaceManifestPath(workdir));
|
|
4911
5072
|
if (!manifest || manifest.managed !== true) continue;
|
|
4912
5073
|
const summary = summarizeWorkdir(workdir, manifest, nowMs);
|
|
4913
|
-
|
|
5074
|
+
result3.totalWorkdirs += 1;
|
|
4914
5075
|
const lastTouchedTime = readIsoTime(summary.lastTouchedAt);
|
|
4915
5076
|
const active = Boolean(
|
|
4916
5077
|
summary.runId && activeRunIds.has(summary.runId) || summary.commandId && activeCommandIds.has(summary.commandId)
|
|
4917
5078
|
);
|
|
4918
5079
|
if (active) {
|
|
4919
|
-
|
|
5080
|
+
result3.protected.push({ ...summary, reason: "active_run_or_command" });
|
|
4920
5081
|
continue;
|
|
4921
5082
|
}
|
|
4922
5083
|
if (lastTouchedTime !== null && nowMs - lastTouchedTime < ttlMs) {
|
|
4923
|
-
|
|
5084
|
+
result3.protected.push({ ...summary, reason: "within_ttl" });
|
|
4924
5085
|
continue;
|
|
4925
5086
|
}
|
|
4926
|
-
|
|
4927
|
-
|
|
5087
|
+
result3.totalCandidateBytes += summary.sizeBytes;
|
|
5088
|
+
result3.candidates.push({
|
|
4928
5089
|
...summary,
|
|
4929
5090
|
reason: lastTouchedTime === null ? "missing_last_touched_at" : "older_than_ttl"
|
|
4930
5091
|
});
|
|
4931
5092
|
}
|
|
4932
|
-
return
|
|
5093
|
+
return result3;
|
|
4933
5094
|
}
|
|
4934
5095
|
|
|
4935
5096
|
// src/amaster-runtime-daemon/runtime-status-summary.mjs
|
|
@@ -6252,8 +6413,1724 @@ function readWorkspaceStatus(cwd, opts = {}) {
|
|
|
6252
6413
|
};
|
|
6253
6414
|
}
|
|
6254
6415
|
|
|
6416
|
+
// src/amaster-runtime-daemon/source-read-command.mjs
|
|
6417
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
6418
|
+
|
|
6419
|
+
// src/amaster-runtime-daemon/constrained-source-reader.mjs
|
|
6420
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
6421
|
+
var CONSTRAINED_SOURCE_READER_CONTRACT_VERSION = "amaster.constrained-source-reader.v1";
|
|
6422
|
+
var DEFAULT_LIMITS = Object.freeze({
|
|
6423
|
+
maxPages: 10,
|
|
6424
|
+
maxSnapshotChars: 2e5,
|
|
6425
|
+
maxTotalSnapshotChars: 5e5
|
|
6426
|
+
});
|
|
6427
|
+
var REQUEST_FIELDS = /* @__PURE__ */ new Set([
|
|
6428
|
+
"sourceRevisionId",
|
|
6429
|
+
"locator",
|
|
6430
|
+
"declaredPageLocators",
|
|
6431
|
+
"allowedResourceOrigins",
|
|
6432
|
+
"pageId",
|
|
6433
|
+
"limits"
|
|
6434
|
+
]);
|
|
6435
|
+
var LIMIT_FIELDS = new Set(Object.keys(DEFAULT_LIMITS));
|
|
6436
|
+
var ERROR_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
6437
|
+
"sourceRevisionId",
|
|
6438
|
+
"origin",
|
|
6439
|
+
"pathname",
|
|
6440
|
+
"pageIndex",
|
|
6441
|
+
"operation"
|
|
6442
|
+
]);
|
|
6443
|
+
function isRecord2(value) {
|
|
6444
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
6445
|
+
const prototype = Object.getPrototypeOf(value);
|
|
6446
|
+
return prototype === Object.prototype || prototype === null;
|
|
6447
|
+
}
|
|
6448
|
+
function boundedMetadataValue(key, value) {
|
|
6449
|
+
if (key === "pageIndex") return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
6450
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
6451
|
+
return value.slice(0, 512);
|
|
6452
|
+
}
|
|
6453
|
+
function sanitizeErrorMetadata(metadata) {
|
|
6454
|
+
if (!isRecord2(metadata)) return {};
|
|
6455
|
+
const sanitized = {};
|
|
6456
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
6457
|
+
if (!ERROR_METADATA_FIELDS.has(key)) continue;
|
|
6458
|
+
const bounded = boundedMetadataValue(key, value);
|
|
6459
|
+
if (bounded !== void 0) sanitized[key] = bounded;
|
|
6460
|
+
}
|
|
6461
|
+
return sanitized;
|
|
6462
|
+
}
|
|
6463
|
+
var ConstrainedSourceReaderError = class extends Error {
|
|
6464
|
+
constructor(code, metadata = {}) {
|
|
6465
|
+
super(code);
|
|
6466
|
+
this.name = "ConstrainedSourceReaderError";
|
|
6467
|
+
this.code = code;
|
|
6468
|
+
this.metadata = sanitizeErrorMetadata(metadata);
|
|
6469
|
+
}
|
|
6470
|
+
};
|
|
6471
|
+
function fail(code, metadata) {
|
|
6472
|
+
throw new ConstrainedSourceReaderError(code, metadata);
|
|
6473
|
+
}
|
|
6474
|
+
function locatorMetadata(sourceRevisionId, url, pageIndex) {
|
|
6475
|
+
return {
|
|
6476
|
+
sourceRevisionId,
|
|
6477
|
+
origin: url?.origin,
|
|
6478
|
+
pathname: url?.pathname,
|
|
6479
|
+
pageIndex
|
|
6480
|
+
};
|
|
6481
|
+
}
|
|
6482
|
+
function parseHttpLocator(value, { sourceRevisionId, pageIndex, invalidCode }) {
|
|
6483
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
6484
|
+
fail(invalidCode, { sourceRevisionId, pageIndex });
|
|
6485
|
+
}
|
|
6486
|
+
const locator = value.trim();
|
|
6487
|
+
let url;
|
|
6488
|
+
try {
|
|
6489
|
+
url = new URL(locator);
|
|
6490
|
+
} catch {
|
|
6491
|
+
fail(invalidCode, { sourceRevisionId, pageIndex });
|
|
6492
|
+
}
|
|
6493
|
+
if (url.username || url.password) {
|
|
6494
|
+
fail(
|
|
6495
|
+
"source_reader_locator_credentials_forbidden",
|
|
6496
|
+
locatorMetadata(sourceRevisionId, url, pageIndex)
|
|
6497
|
+
);
|
|
6498
|
+
}
|
|
6499
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
6500
|
+
fail(invalidCode, locatorMetadata(sourceRevisionId, url, pageIndex));
|
|
6501
|
+
}
|
|
6502
|
+
return { locator, url };
|
|
6503
|
+
}
|
|
6504
|
+
function normalizeLimits(value, sourceRevisionId) {
|
|
6505
|
+
if (value === void 0) return { ...DEFAULT_LIMITS };
|
|
6506
|
+
if (!isRecord2(value) || Object.keys(value).some((key) => !LIMIT_FIELDS.has(key))) {
|
|
6507
|
+
fail("source_reader_limit_invalid", { sourceRevisionId });
|
|
6508
|
+
}
|
|
6509
|
+
const limits = { ...DEFAULT_LIMITS };
|
|
6510
|
+
for (const [key, ceiling] of Object.entries(DEFAULT_LIMITS)) {
|
|
6511
|
+
if (!(key in value)) continue;
|
|
6512
|
+
const candidate = value[key];
|
|
6513
|
+
if (!Number.isSafeInteger(candidate) || candidate <= 0 || candidate > ceiling) {
|
|
6514
|
+
fail("source_reader_limit_invalid", { sourceRevisionId });
|
|
6515
|
+
}
|
|
6516
|
+
limits[key] = candidate;
|
|
6517
|
+
}
|
|
6518
|
+
return limits;
|
|
6519
|
+
}
|
|
6520
|
+
function normalizeResourceOrigins(value, sourceRevisionId) {
|
|
6521
|
+
if (value === void 0) return [];
|
|
6522
|
+
if (!Array.isArray(value)) {
|
|
6523
|
+
fail("source_reader_request_invalid", { sourceRevisionId });
|
|
6524
|
+
}
|
|
6525
|
+
const normalized = [];
|
|
6526
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6527
|
+
for (const resourceOrigin of value) {
|
|
6528
|
+
const { url } = parseHttpLocator(resourceOrigin, {
|
|
6529
|
+
sourceRevisionId,
|
|
6530
|
+
invalidCode: "source_reader_resource_origin_invalid"
|
|
6531
|
+
});
|
|
6532
|
+
if (url.pathname !== "/" || url.search || url.hash) {
|
|
6533
|
+
fail(
|
|
6534
|
+
"source_reader_resource_origin_invalid",
|
|
6535
|
+
locatorMetadata(sourceRevisionId, url)
|
|
6536
|
+
);
|
|
6537
|
+
}
|
|
6538
|
+
if (!seen.has(url.origin)) {
|
|
6539
|
+
seen.add(url.origin);
|
|
6540
|
+
normalized.push(url.origin);
|
|
6541
|
+
}
|
|
6542
|
+
}
|
|
6543
|
+
return normalized;
|
|
6544
|
+
}
|
|
6545
|
+
function normalizeConstrainedSourceReadRequest(input) {
|
|
6546
|
+
if (!isRecord2(input) || Object.keys(input).some((key) => !REQUEST_FIELDS.has(key))) {
|
|
6547
|
+
fail("source_reader_request_invalid");
|
|
6548
|
+
}
|
|
6549
|
+
const sourceRevisionId = typeof input.sourceRevisionId === "string" ? input.sourceRevisionId.trim() : "";
|
|
6550
|
+
if (!sourceRevisionId) fail("source_reader_request_invalid");
|
|
6551
|
+
if (!Number.isSafeInteger(input.pageId) || input.pageId <= 0) {
|
|
6552
|
+
fail("source_reader_request_invalid", { sourceRevisionId });
|
|
6553
|
+
}
|
|
6554
|
+
if (input.declaredPageLocators !== void 0 && !Array.isArray(input.declaredPageLocators)) {
|
|
6555
|
+
fail("source_reader_request_invalid", { sourceRevisionId });
|
|
6556
|
+
}
|
|
6557
|
+
const limits = normalizeLimits(input.limits, sourceRevisionId);
|
|
6558
|
+
const primary = parseHttpLocator(input.locator, {
|
|
6559
|
+
sourceRevisionId,
|
|
6560
|
+
pageIndex: 0,
|
|
6561
|
+
invalidCode: "source_reader_request_invalid"
|
|
6562
|
+
});
|
|
6563
|
+
const declaredInputs = input.declaredPageLocators ?? [];
|
|
6564
|
+
const declared = declaredInputs.map((locator, index) => parseHttpLocator(locator, {
|
|
6565
|
+
sourceRevisionId,
|
|
6566
|
+
pageIndex: index + 1,
|
|
6567
|
+
invalidCode: "source_reader_request_invalid"
|
|
6568
|
+
}));
|
|
6569
|
+
const locators = [primary, ...declared];
|
|
6570
|
+
if (locators.length > limits.maxPages) {
|
|
6571
|
+
fail("source_reader_limit_invalid", { sourceRevisionId });
|
|
6572
|
+
}
|
|
6573
|
+
const seenLocators = /* @__PURE__ */ new Set();
|
|
6574
|
+
for (const [pageIndex, entry] of locators.entries()) {
|
|
6575
|
+
if (entry.url.origin !== primary.url.origin) {
|
|
6576
|
+
fail(
|
|
6577
|
+
"source_reader_navigation_scope_forbidden",
|
|
6578
|
+
locatorMetadata(sourceRevisionId, entry.url, pageIndex)
|
|
6579
|
+
);
|
|
6580
|
+
}
|
|
6581
|
+
if (seenLocators.has(entry.url.href)) {
|
|
6582
|
+
fail(
|
|
6583
|
+
"source_reader_request_invalid",
|
|
6584
|
+
locatorMetadata(sourceRevisionId, entry.url, pageIndex)
|
|
6585
|
+
);
|
|
6586
|
+
}
|
|
6587
|
+
seenLocators.add(entry.url.href);
|
|
6588
|
+
}
|
|
6589
|
+
return {
|
|
6590
|
+
contractVersion: CONSTRAINED_SOURCE_READER_CONTRACT_VERSION,
|
|
6591
|
+
sourceRevisionId,
|
|
6592
|
+
locator: primary.locator,
|
|
6593
|
+
declaredPageLocators: declared.map((entry) => entry.locator),
|
|
6594
|
+
locators: locators.map((entry) => entry.locator),
|
|
6595
|
+
contentOrigin: primary.url.origin,
|
|
6596
|
+
allowedResourceOrigins: normalizeResourceOrigins(
|
|
6597
|
+
input.allowedResourceOrigins,
|
|
6598
|
+
sourceRevisionId
|
|
6599
|
+
),
|
|
6600
|
+
pageId: input.pageId,
|
|
6601
|
+
limits
|
|
6602
|
+
};
|
|
6603
|
+
}
|
|
6604
|
+
var TRANSPORT_METHODS = Object.freeze([
|
|
6605
|
+
"listPages",
|
|
6606
|
+
"navigatePage",
|
|
6607
|
+
"takeSnapshot"
|
|
6608
|
+
]);
|
|
6609
|
+
var RESULT_OPERATION_NAMES = Object.freeze([
|
|
6610
|
+
"list_pages",
|
|
6611
|
+
"navigate_page",
|
|
6612
|
+
"take_snapshot"
|
|
6613
|
+
]);
|
|
6614
|
+
var UNSUPPORTED_INTERACTIONS = /* @__PURE__ */ new Set([
|
|
6615
|
+
"scroll_required",
|
|
6616
|
+
"click_required",
|
|
6617
|
+
"evaluate_required",
|
|
6618
|
+
"virtualized_content"
|
|
6619
|
+
]);
|
|
6620
|
+
var SAFE_TRANSPORT_FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
6621
|
+
"source_reader_auth_required",
|
|
6622
|
+
"source_reader_no_access",
|
|
6623
|
+
"source_reader_network_scope_forbidden",
|
|
6624
|
+
"source_reader_download_forbidden"
|
|
6625
|
+
]);
|
|
6626
|
+
function transportFunctionNames(transport) {
|
|
6627
|
+
const names = /* @__PURE__ */ new Set();
|
|
6628
|
+
let target = transport;
|
|
6629
|
+
while (target && target !== Object.prototype) {
|
|
6630
|
+
for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(target))) {
|
|
6631
|
+
if (name !== "constructor" && typeof descriptor.value === "function") names.add(name);
|
|
6632
|
+
}
|
|
6633
|
+
target = Object.getPrototypeOf(target);
|
|
6634
|
+
}
|
|
6635
|
+
return [...names].sort();
|
|
6636
|
+
}
|
|
6637
|
+
function assertTransportSurface(transport, sourceRevisionId) {
|
|
6638
|
+
if (!isRecord2(transport) && (typeof transport !== "object" || transport === null)) {
|
|
6639
|
+
fail("source_reader_transport_invalid", { sourceRevisionId });
|
|
6640
|
+
}
|
|
6641
|
+
const methods = transportFunctionNames(transport);
|
|
6642
|
+
if (methods.length !== TRANSPORT_METHODS.length || TRANSPORT_METHODS.some((method) => !methods.includes(method))) {
|
|
6643
|
+
fail("source_reader_transport_invalid", { sourceRevisionId });
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6646
|
+
function readSignal(value) {
|
|
6647
|
+
if (value === void 0) return new AbortController().signal;
|
|
6648
|
+
if (typeof value !== "object" || value === null || typeof value.aborted !== "boolean" || typeof value.addEventListener !== "function") {
|
|
6649
|
+
fail("source_reader_request_invalid");
|
|
6650
|
+
}
|
|
6651
|
+
return value;
|
|
6652
|
+
}
|
|
6653
|
+
function checkAborted(signal, sourceRevisionId, operation) {
|
|
6654
|
+
if (signal.aborted) fail("source_reader_aborted", { sourceRevisionId, operation });
|
|
6655
|
+
}
|
|
6656
|
+
async function callTransport({ sourceRevisionId, signal, operation, action }) {
|
|
6657
|
+
checkAborted(signal, sourceRevisionId, operation);
|
|
6658
|
+
try {
|
|
6659
|
+
const result3 = await action();
|
|
6660
|
+
checkAborted(signal, sourceRevisionId, operation);
|
|
6661
|
+
if (isRecord2(result3) && result3.isError === true) {
|
|
6662
|
+
fail("source_reader_transport_failed", { sourceRevisionId, operation });
|
|
6663
|
+
}
|
|
6664
|
+
return result3;
|
|
6665
|
+
} catch (error) {
|
|
6666
|
+
if (error instanceof ConstrainedSourceReaderError) throw error;
|
|
6667
|
+
if (SAFE_TRANSPORT_FAILURE_CODES.has(error?.code)) {
|
|
6668
|
+
fail(error.code, { sourceRevisionId, operation });
|
|
6669
|
+
}
|
|
6670
|
+
if (signal.aborted || error?.name === "AbortError") {
|
|
6671
|
+
fail("source_reader_aborted", { sourceRevisionId, operation });
|
|
6672
|
+
}
|
|
6673
|
+
fail("source_reader_transport_failed", { sourceRevisionId, operation });
|
|
6674
|
+
}
|
|
6675
|
+
}
|
|
6676
|
+
function normalizePageRows(value, sourceRevisionId) {
|
|
6677
|
+
if (!Array.isArray(value)) {
|
|
6678
|
+
fail("source_reader_transport_invalid", { sourceRevisionId, operation: "list_pages" });
|
|
6679
|
+
}
|
|
6680
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6681
|
+
return value.map((row) => {
|
|
6682
|
+
if (!isRecord2(row) || !Number.isSafeInteger(row.pageId) || row.pageId <= 0 || typeof row.url !== "string" || seen.has(row.pageId)) {
|
|
6683
|
+
fail("source_reader_transport_invalid", { sourceRevisionId, operation: "list_pages" });
|
|
6684
|
+
}
|
|
6685
|
+
seen.add(row.pageId);
|
|
6686
|
+
return { pageId: row.pageId, url: row.url };
|
|
6687
|
+
});
|
|
6688
|
+
}
|
|
6689
|
+
function pageIdSet(rows) {
|
|
6690
|
+
return rows.map((row) => row.pageId).sort((left, right) => left - right);
|
|
6691
|
+
}
|
|
6692
|
+
function assertStableTargetSet(before, after, request) {
|
|
6693
|
+
if (!after.some((row) => row.pageId === request.pageId)) {
|
|
6694
|
+
fail("source_reader_target_missing", { sourceRevisionId: request.sourceRevisionId });
|
|
6695
|
+
}
|
|
6696
|
+
if (before.length !== after.length || pageIdSet(before).some((pageId, index) => pageId !== pageIdSet(after)[index])) {
|
|
6697
|
+
fail("source_reader_target_set_changed", { sourceRevisionId: request.sourceRevisionId });
|
|
6698
|
+
}
|
|
6699
|
+
}
|
|
6700
|
+
function parseObservedUrl(value, request, pageIndex, operation) {
|
|
6701
|
+
const { url } = parseHttpLocator(value, {
|
|
6702
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6703
|
+
pageIndex,
|
|
6704
|
+
invalidCode: "source_reader_transport_invalid"
|
|
6705
|
+
});
|
|
6706
|
+
if (url.origin !== request.contentOrigin) {
|
|
6707
|
+
fail(
|
|
6708
|
+
"source_reader_cross_scope_redirect",
|
|
6709
|
+
{ ...locatorMetadata(request.sourceRevisionId, url, pageIndex), operation }
|
|
6710
|
+
);
|
|
6711
|
+
}
|
|
6712
|
+
return url;
|
|
6713
|
+
}
|
|
6714
|
+
async function assertNetworkAllowed(locator, dependencies, request, pageIndex, operation) {
|
|
6715
|
+
let allowed;
|
|
6716
|
+
try {
|
|
6717
|
+
allowed = await dependencies.assertNetworkAddressAllowed(locator);
|
|
6718
|
+
} catch {
|
|
6719
|
+
const url = (() => {
|
|
6720
|
+
try {
|
|
6721
|
+
return new URL(locator);
|
|
6722
|
+
} catch {
|
|
6723
|
+
return null;
|
|
6724
|
+
}
|
|
6725
|
+
})();
|
|
6726
|
+
fail(
|
|
6727
|
+
"source_reader_network_scope_forbidden",
|
|
6728
|
+
{ ...locatorMetadata(request.sourceRevisionId, url, pageIndex), operation }
|
|
6729
|
+
);
|
|
6730
|
+
}
|
|
6731
|
+
if (allowed === false) {
|
|
6732
|
+
const url = new URL(locator);
|
|
6733
|
+
fail(
|
|
6734
|
+
"source_reader_network_scope_forbidden",
|
|
6735
|
+
{ ...locatorMetadata(request.sourceRevisionId, url, pageIndex), operation }
|
|
6736
|
+
);
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
function normalizeNavigationResult(value, request, pageIndex) {
|
|
6740
|
+
if (!isRecord2(value) || typeof value.finalUrl !== "string" || !Array.isArray(value.redirectChain) || value.redirectChain.some((entry) => typeof entry !== "string") || !Array.isArray(value.resourceOrigins) || value.resourceOrigins.some((entry) => typeof entry !== "string") || typeof value.downloadAttempted !== "boolean") {
|
|
6741
|
+
fail("source_reader_transport_invalid", {
|
|
6742
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6743
|
+
pageIndex,
|
|
6744
|
+
operation: "navigate_page"
|
|
6745
|
+
});
|
|
6746
|
+
}
|
|
6747
|
+
return {
|
|
6748
|
+
finalUrl: value.finalUrl,
|
|
6749
|
+
redirectChain: [...value.redirectChain],
|
|
6750
|
+
resourceOrigins: [...value.resourceOrigins],
|
|
6751
|
+
downloadAttempted: value.downloadAttempted
|
|
6752
|
+
};
|
|
6753
|
+
}
|
|
6754
|
+
function normalizeSnapshotResult(value, request, pageIndex) {
|
|
6755
|
+
if (!isRecord2(value) || typeof value.text !== "string" || typeof value.truncated !== "boolean" || !(value.unsupportedInteraction === null || UNSUPPORTED_INTERACTIONS.has(value.unsupportedInteraction))) {
|
|
6756
|
+
fail("source_reader_transport_invalid", {
|
|
6757
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6758
|
+
pageIndex,
|
|
6759
|
+
operation: "take_snapshot"
|
|
6760
|
+
});
|
|
6761
|
+
}
|
|
6762
|
+
return {
|
|
6763
|
+
text: value.text,
|
|
6764
|
+
truncated: value.truncated,
|
|
6765
|
+
unsupportedInteraction: value.unsupportedInteraction
|
|
6766
|
+
};
|
|
6767
|
+
}
|
|
6768
|
+
function timestamp(now, sourceRevisionId) {
|
|
6769
|
+
const value = now();
|
|
6770
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
6771
|
+
if (!Number.isFinite(date.getTime())) {
|
|
6772
|
+
fail("source_reader_request_invalid", { sourceRevisionId });
|
|
6773
|
+
}
|
|
6774
|
+
return date.toISOString();
|
|
6775
|
+
}
|
|
6776
|
+
async function listPages(dependencies, request, signal) {
|
|
6777
|
+
const value = await callTransport({
|
|
6778
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6779
|
+
signal,
|
|
6780
|
+
operation: "list_pages",
|
|
6781
|
+
action: () => dependencies.transport.listPages({ signal })
|
|
6782
|
+
});
|
|
6783
|
+
return normalizePageRows(value, request.sourceRevisionId);
|
|
6784
|
+
}
|
|
6785
|
+
async function validateNavigationResult(result3, dependencies, request, pageIndex) {
|
|
6786
|
+
if (result3.downloadAttempted) {
|
|
6787
|
+
fail("source_reader_download_forbidden", {
|
|
6788
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6789
|
+
pageIndex,
|
|
6790
|
+
operation: "navigate_page"
|
|
6791
|
+
});
|
|
6792
|
+
}
|
|
6793
|
+
for (const locator of [...result3.redirectChain, result3.finalUrl]) {
|
|
6794
|
+
parseObservedUrl(locator, request, pageIndex, "navigate_page");
|
|
6795
|
+
await assertNetworkAllowed(locator, dependencies, request, pageIndex, "navigate_page");
|
|
6796
|
+
}
|
|
6797
|
+
const allowedOrigins = /* @__PURE__ */ new Set([request.contentOrigin, ...request.allowedResourceOrigins]);
|
|
6798
|
+
for (const resourceOrigin of result3.resourceOrigins) {
|
|
6799
|
+
const { url } = parseHttpLocator(resourceOrigin, {
|
|
6800
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6801
|
+
pageIndex,
|
|
6802
|
+
invalidCode: "source_reader_transport_invalid"
|
|
6803
|
+
});
|
|
6804
|
+
if (url.pathname !== "/" || url.search || url.hash) {
|
|
6805
|
+
fail("source_reader_transport_invalid", {
|
|
6806
|
+
...locatorMetadata(request.sourceRevisionId, url, pageIndex),
|
|
6807
|
+
operation: "navigate_page"
|
|
6808
|
+
});
|
|
6809
|
+
}
|
|
6810
|
+
if (!allowedOrigins.has(url.origin)) {
|
|
6811
|
+
fail("source_reader_resource_origin_forbidden", {
|
|
6812
|
+
...locatorMetadata(request.sourceRevisionId, url, pageIndex),
|
|
6813
|
+
operation: "navigate_page"
|
|
6814
|
+
});
|
|
6815
|
+
}
|
|
6816
|
+
await assertNetworkAllowed(url.origin, dependencies, request, pageIndex, "navigate_page");
|
|
6817
|
+
}
|
|
6818
|
+
}
|
|
6819
|
+
async function readConstrainedSource(input, dependencies) {
|
|
6820
|
+
const request = normalizeConstrainedSourceReadRequest(input);
|
|
6821
|
+
if (!isRecord2(dependencies) || typeof dependencies.assertNetworkAddressAllowed !== "function" || dependencies.now !== void 0 && typeof dependencies.now !== "function") {
|
|
6822
|
+
fail("source_reader_request_invalid", { sourceRevisionId: request.sourceRevisionId });
|
|
6823
|
+
}
|
|
6824
|
+
assertTransportSurface(dependencies.transport, request.sourceRevisionId);
|
|
6825
|
+
const signal = readSignal(dependencies.signal);
|
|
6826
|
+
const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
6827
|
+
const startedAt = timestamp(now, request.sourceRevisionId);
|
|
6828
|
+
const pages = [];
|
|
6829
|
+
let totalSnapshotChars = 0;
|
|
6830
|
+
let coverageGap = null;
|
|
6831
|
+
for (const [pageIndex, locator] of request.locators.entries()) {
|
|
6832
|
+
checkAborted(signal, request.sourceRevisionId, "navigate_page");
|
|
6833
|
+
await assertNetworkAllowed(locator, dependencies, request, pageIndex, "navigate_page");
|
|
6834
|
+
const beforeNavigation = await listPages(dependencies, request, signal);
|
|
6835
|
+
if (!beforeNavigation.some((row) => row.pageId === request.pageId)) {
|
|
6836
|
+
fail("source_reader_target_missing", {
|
|
6837
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6838
|
+
pageIndex,
|
|
6839
|
+
operation: "list_pages"
|
|
6840
|
+
});
|
|
6841
|
+
}
|
|
6842
|
+
const navigationValue = await callTransport({
|
|
6843
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6844
|
+
signal,
|
|
6845
|
+
operation: "navigate_page",
|
|
6846
|
+
action: () => dependencies.transport.navigatePage({
|
|
6847
|
+
pageId: request.pageId,
|
|
6848
|
+
locator,
|
|
6849
|
+
allowedResourceOrigins: [...request.allowedResourceOrigins],
|
|
6850
|
+
signal
|
|
6851
|
+
})
|
|
6852
|
+
});
|
|
6853
|
+
const navigation = normalizeNavigationResult(navigationValue, request, pageIndex);
|
|
6854
|
+
await validateNavigationResult(navigation, dependencies, request, pageIndex);
|
|
6855
|
+
const afterNavigation = await listPages(dependencies, request, signal);
|
|
6856
|
+
assertStableTargetSet(beforeNavigation, afterNavigation, request);
|
|
6857
|
+
const snapshotValue = await callTransport({
|
|
6858
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6859
|
+
signal,
|
|
6860
|
+
operation: "take_snapshot",
|
|
6861
|
+
action: () => dependencies.transport.takeSnapshot({ pageId: request.pageId, signal })
|
|
6862
|
+
});
|
|
6863
|
+
const snapshot = normalizeSnapshotResult(snapshotValue, request, pageIndex);
|
|
6864
|
+
const afterSnapshot = await listPages(dependencies, request, signal);
|
|
6865
|
+
assertStableTargetSet(beforeNavigation, afterSnapshot, request);
|
|
6866
|
+
if (snapshot.text.length > request.limits.maxSnapshotChars || totalSnapshotChars + snapshot.text.length > request.limits.maxTotalSnapshotChars) {
|
|
6867
|
+
fail("source_reader_snapshot_oversized", {
|
|
6868
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6869
|
+
pageIndex,
|
|
6870
|
+
operation: "take_snapshot"
|
|
6871
|
+
});
|
|
6872
|
+
}
|
|
6873
|
+
totalSnapshotChars += snapshot.text.length;
|
|
6874
|
+
pages.push({
|
|
6875
|
+
pageIndex,
|
|
6876
|
+
locator,
|
|
6877
|
+
finalUrl: navigation.finalUrl,
|
|
6878
|
+
capturedAt: timestamp(now, request.sourceRevisionId),
|
|
6879
|
+
text: snapshot.text,
|
|
6880
|
+
charCount: snapshot.text.length,
|
|
6881
|
+
contentHash: createHash9("sha256").update(snapshot.text, "utf8").digest("hex")
|
|
6882
|
+
});
|
|
6883
|
+
if (snapshot.truncated || snapshot.unsupportedInteraction) {
|
|
6884
|
+
coverageGap = {
|
|
6885
|
+
code: "unsupported_interaction",
|
|
6886
|
+
pageIndex,
|
|
6887
|
+
reason: snapshot.unsupportedInteraction ?? "scroll_required"
|
|
6888
|
+
};
|
|
6889
|
+
break;
|
|
6890
|
+
}
|
|
6891
|
+
}
|
|
6892
|
+
const result3 = {
|
|
6893
|
+
contractVersion: CONSTRAINED_SOURCE_READER_CONTRACT_VERSION,
|
|
6894
|
+
sourceRevisionId: request.sourceRevisionId,
|
|
6895
|
+
status: coverageGap ? "partial" : "complete",
|
|
6896
|
+
pages,
|
|
6897
|
+
totalSnapshotChars,
|
|
6898
|
+
operationNames: [...RESULT_OPERATION_NAMES],
|
|
6899
|
+
startedAt,
|
|
6900
|
+
completedAt: timestamp(now, request.sourceRevisionId),
|
|
6901
|
+
passiveEffectsPossible: true
|
|
6902
|
+
};
|
|
6903
|
+
if (coverageGap) result3.coverageGap = coverageGap;
|
|
6904
|
+
return result3;
|
|
6905
|
+
}
|
|
6906
|
+
|
|
6907
|
+
// src/amaster-runtime-daemon/source-read-command.mjs
|
|
6908
|
+
var SOURCE_SNAPSHOT_REDACTION_VERSION = "amaster.source-snapshot-redaction.v1";
|
|
6909
|
+
var COMMAND_PAYLOAD_FIELDS = /* @__PURE__ */ new Set([
|
|
6910
|
+
"contractVersion",
|
|
6911
|
+
"companyId",
|
|
6912
|
+
"sourceId",
|
|
6913
|
+
"sourceRevisionId",
|
|
6914
|
+
"sourceRevision",
|
|
6915
|
+
"locator",
|
|
6916
|
+
"declaredPageLocators",
|
|
6917
|
+
"allowedResourceOrigins",
|
|
6918
|
+
"limits",
|
|
6919
|
+
"authentication"
|
|
6920
|
+
]);
|
|
6921
|
+
var AUTHENTICATION_FIELDS = /* @__PURE__ */ new Set([
|
|
6922
|
+
"bindingId",
|
|
6923
|
+
"actionId",
|
|
6924
|
+
"interactionId",
|
|
6925
|
+
"localOpaqueRef",
|
|
6926
|
+
"provider",
|
|
6927
|
+
"origin",
|
|
6928
|
+
"leaseId",
|
|
6929
|
+
"leaseExpiresAt"
|
|
6930
|
+
]);
|
|
6931
|
+
var RESULT_OPERATION_NAMES2 = Object.freeze([
|
|
6932
|
+
"list_pages",
|
|
6933
|
+
"navigate_page",
|
|
6934
|
+
"take_snapshot"
|
|
6935
|
+
]);
|
|
6936
|
+
var REDACTION_CATEGORIES = Object.freeze([
|
|
6937
|
+
"credential",
|
|
6938
|
+
"token",
|
|
6939
|
+
"privateKey",
|
|
6940
|
+
"urlSecret",
|
|
6941
|
+
"highEntropy"
|
|
6942
|
+
]);
|
|
6943
|
+
var REDACTED = "[REDACTED]";
|
|
6944
|
+
function isRecord3(value) {
|
|
6945
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
6946
|
+
const prototype = Object.getPrototypeOf(value);
|
|
6947
|
+
return prototype === Object.prototype || prototype === null;
|
|
6948
|
+
}
|
|
6949
|
+
function readIdentityString(value) {
|
|
6950
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
6951
|
+
}
|
|
6952
|
+
function normalizeAuthentication(value) {
|
|
6953
|
+
if (value === void 0) return void 0;
|
|
6954
|
+
if (!isRecord3(value) || Object.keys(value).some((key) => !AUTHENTICATION_FIELDS.has(key)) || !readIdentityString(value.bindingId) || !readIdentityString(value.actionId) || !readIdentityString(value.interactionId) || typeof value.localOpaqueRef !== "string" || !/^profile_[a-z0-9]{16,64}$/u.test(value.localOpaqueRef) || !readIdentityString(value.provider) || !readIdentityString(value.origin) || !readIdentityString(value.leaseId) || !readIdentityString(value.leaseExpiresAt) || !Number.isFinite(Date.parse(value.leaseExpiresAt))) {
|
|
6955
|
+
throw Object.assign(new Error("source_reader_command_invalid"), {
|
|
6956
|
+
code: "source_reader_command_invalid"
|
|
6957
|
+
});
|
|
6958
|
+
}
|
|
6959
|
+
return { ...value };
|
|
6960
|
+
}
|
|
6961
|
+
function sha256(value) {
|
|
6962
|
+
return createHash10("sha256").update(value, "utf8").digest("hex");
|
|
6963
|
+
}
|
|
6964
|
+
function safeTimestamp(now) {
|
|
6965
|
+
try {
|
|
6966
|
+
const value = now();
|
|
6967
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
6968
|
+
if (Number.isFinite(date.getTime())) return date.toISOString();
|
|
6969
|
+
} catch {
|
|
6970
|
+
}
|
|
6971
|
+
return "1970-01-01T00:00:00.000Z";
|
|
6972
|
+
}
|
|
6973
|
+
function emptyRedactionSummary() {
|
|
6974
|
+
return {
|
|
6975
|
+
replacements: 0,
|
|
6976
|
+
categories: Object.fromEntries(REDACTION_CATEGORIES.map((category) => [category, 0]))
|
|
6977
|
+
};
|
|
6978
|
+
}
|
|
6979
|
+
function scrubSnapshotText(input) {
|
|
6980
|
+
let text = input;
|
|
6981
|
+
const summary = emptyRedactionSummary();
|
|
6982
|
+
const replace = (category, pattern, replacement) => {
|
|
6983
|
+
text = text.replace(pattern, (...args) => {
|
|
6984
|
+
summary.replacements += 1;
|
|
6985
|
+
summary.categories[category] += 1;
|
|
6986
|
+
return typeof replacement === "function" ? replacement(...args) : replacement;
|
|
6987
|
+
});
|
|
6988
|
+
};
|
|
6989
|
+
replace(
|
|
6990
|
+
"privateKey",
|
|
6991
|
+
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/gu,
|
|
6992
|
+
REDACTED
|
|
6993
|
+
);
|
|
6994
|
+
replace(
|
|
6995
|
+
"urlSecret",
|
|
6996
|
+
/([?&](?:access_token|api[_-]?key|auth|code|credential|key|password|secret|signature|token)=)[^&#\s]+/giu,
|
|
6997
|
+
(_match, prefix) => `${prefix}${REDACTED}`
|
|
6998
|
+
);
|
|
6999
|
+
replace(
|
|
7000
|
+
"token",
|
|
7001
|
+
/\b(?:authorization\s*:\s*)?bearer\s+[A-Za-z0-9._~+/=-]{8,}/giu,
|
|
7002
|
+
REDACTED
|
|
7003
|
+
);
|
|
7004
|
+
replace(
|
|
7005
|
+
"token",
|
|
7006
|
+
/\b(api[_-]?key|access[_-]?token|auth[_-]?token)\s*[:=]\s*[^\s,;]+/giu,
|
|
7007
|
+
(_match, label) => `${label}=${REDACTED}`
|
|
7008
|
+
);
|
|
7009
|
+
replace(
|
|
7010
|
+
"credential",
|
|
7011
|
+
/\b(password|passwd|pwd|credential|client[_-]?secret)\s*[:=]\s*[^\s,;]+/giu,
|
|
7012
|
+
(_match, label) => `${label}=${REDACTED}`
|
|
7013
|
+
);
|
|
7014
|
+
text = text.replace(/[A-Za-z0-9_-]{32,}/gu, (candidate) => {
|
|
7015
|
+
if (!/[A-Za-z]/u.test(candidate) || !/[0-9]/u.test(candidate)) return candidate;
|
|
7016
|
+
summary.replacements += 1;
|
|
7017
|
+
summary.categories.highEntropy += 1;
|
|
7018
|
+
return REDACTED;
|
|
7019
|
+
});
|
|
7020
|
+
return { text, summary };
|
|
7021
|
+
}
|
|
7022
|
+
function normalizeCommand(command) {
|
|
7023
|
+
if (!isRecord3(command) || !isRecord3(command.payload)) throw Object.assign(new Error("source_reader_command_invalid"), { code: "source_reader_command_invalid" });
|
|
7024
|
+
const payload = command.payload;
|
|
7025
|
+
if (Object.keys(payload).some((key) => !COMMAND_PAYLOAD_FIELDS.has(key)) || payload.contractVersion !== CONSTRAINED_SOURCE_READER_CONTRACT_VERSION || !readIdentityString(command.commandId) || !readIdentityString(command.connectorId) || !readIdentityString(payload.companyId) || !readIdentityString(payload.sourceId) || !readIdentityString(payload.sourceRevisionId) || !Number.isSafeInteger(payload.sourceRevision) || payload.sourceRevision <= 0) {
|
|
7026
|
+
throw Object.assign(new Error("source_reader_command_invalid"), { code: "source_reader_command_invalid" });
|
|
7027
|
+
}
|
|
7028
|
+
return {
|
|
7029
|
+
commandId: command.commandId.trim(),
|
|
7030
|
+
connectorId: command.connectorId.trim(),
|
|
7031
|
+
payload: {
|
|
7032
|
+
...payload,
|
|
7033
|
+
...payload.authentication === void 0 ? {} : { authentication: normalizeAuthentication(payload.authentication) }
|
|
7034
|
+
}
|
|
7035
|
+
};
|
|
7036
|
+
}
|
|
7037
|
+
function baseResult(command, startedAt, completedAt) {
|
|
7038
|
+
const payload = command.payload;
|
|
7039
|
+
return {
|
|
7040
|
+
contractVersion: CONSTRAINED_SOURCE_READER_CONTRACT_VERSION,
|
|
7041
|
+
redactionVersion: SOURCE_SNAPSHOT_REDACTION_VERSION,
|
|
7042
|
+
companyId: payload.companyId,
|
|
7043
|
+
sourceId: payload.sourceId,
|
|
7044
|
+
sourceRevisionId: payload.sourceRevisionId,
|
|
7045
|
+
sourceRevision: payload.sourceRevision,
|
|
7046
|
+
connectorId: command.connectorId,
|
|
7047
|
+
commandId: command.commandId,
|
|
7048
|
+
operationNames: [...RESULT_OPERATION_NAMES2],
|
|
7049
|
+
startedAt,
|
|
7050
|
+
completedAt,
|
|
7051
|
+
passiveEffectsPossible: true
|
|
7052
|
+
};
|
|
7053
|
+
}
|
|
7054
|
+
function failureCode(error) {
|
|
7055
|
+
const code = readIdentityString(error?.code);
|
|
7056
|
+
return code && /^source_reader_[a-z0-9_]{1,110}$/u.test(code) ? code : "source_reader_transport_failed";
|
|
7057
|
+
}
|
|
7058
|
+
function failedResult(command, startedAt, completedAt, error) {
|
|
7059
|
+
return {
|
|
7060
|
+
...baseResult(command, startedAt, completedAt),
|
|
7061
|
+
status: "failed",
|
|
7062
|
+
pages: [],
|
|
7063
|
+
totalSnapshotChars: 0,
|
|
7064
|
+
aggregateContentHash: sha256(""),
|
|
7065
|
+
redaction: emptyRedactionSummary(),
|
|
7066
|
+
failureCode: failureCode(error)
|
|
7067
|
+
};
|
|
7068
|
+
}
|
|
7069
|
+
async function unavailableSourceReadBindingResolver() {
|
|
7070
|
+
throw Object.assign(new Error("source_reader_transport_unavailable"), {
|
|
7071
|
+
code: "source_reader_transport_unavailable"
|
|
7072
|
+
});
|
|
7073
|
+
}
|
|
7074
|
+
async function executeSourceReadCommand(commandInput, dependencies = {}) {
|
|
7075
|
+
const now = typeof dependencies.now === "function" ? dependencies.now : () => /* @__PURE__ */ new Date();
|
|
7076
|
+
const startedAt = safeTimestamp(now);
|
|
7077
|
+
let command;
|
|
7078
|
+
let binding;
|
|
7079
|
+
let bindingReleased = false;
|
|
7080
|
+
const releaseBinding = async () => {
|
|
7081
|
+
if (bindingReleased || typeof binding?.release !== "function") return;
|
|
7082
|
+
bindingReleased = true;
|
|
7083
|
+
await binding.release();
|
|
7084
|
+
};
|
|
7085
|
+
try {
|
|
7086
|
+
command = normalizeCommand(commandInput);
|
|
7087
|
+
} catch (error) {
|
|
7088
|
+
const fallback = {
|
|
7089
|
+
commandId: readIdentityString(commandInput?.commandId) ?? "00000000-0000-4000-8000-000000000000",
|
|
7090
|
+
connectorId: readIdentityString(commandInput?.connectorId) ?? "00000000-0000-4000-8000-000000000000",
|
|
7091
|
+
payload: isRecord3(commandInput?.payload) ? commandInput.payload : {}
|
|
7092
|
+
};
|
|
7093
|
+
return failedResult(fallback, startedAt, safeTimestamp(now), error);
|
|
7094
|
+
}
|
|
7095
|
+
try {
|
|
7096
|
+
if (typeof dependencies.resolveBinding !== "function") {
|
|
7097
|
+
throw Object.assign(new Error("source_reader_transport_unavailable"), {
|
|
7098
|
+
code: "source_reader_transport_unavailable"
|
|
7099
|
+
});
|
|
7100
|
+
}
|
|
7101
|
+
const signal = dependencies.signal ?? new AbortController().signal;
|
|
7102
|
+
binding = await dependencies.resolveBinding({
|
|
7103
|
+
companyId: command.payload.companyId,
|
|
7104
|
+
sourceId: command.payload.sourceId,
|
|
7105
|
+
sourceRevisionId: command.payload.sourceRevisionId,
|
|
7106
|
+
sourceRevision: command.payload.sourceRevision,
|
|
7107
|
+
locator: command.payload.locator,
|
|
7108
|
+
declaredPageLocators: command.payload.declaredPageLocators,
|
|
7109
|
+
allowedResourceOrigins: command.payload.allowedResourceOrigins,
|
|
7110
|
+
limits: command.payload.limits,
|
|
7111
|
+
...command.payload.authentication ? { authentication: command.payload.authentication } : {},
|
|
7112
|
+
signal
|
|
7113
|
+
});
|
|
7114
|
+
if (!isRecord3(binding) || !Number.isSafeInteger(binding.pageId) || binding.pageId <= 0 || typeof binding.assertNetworkAddressAllowed !== "function") {
|
|
7115
|
+
throw Object.assign(new Error("source_reader_transport_invalid"), {
|
|
7116
|
+
code: "source_reader_transport_invalid"
|
|
7117
|
+
});
|
|
7118
|
+
}
|
|
7119
|
+
const read = await readConstrainedSource({
|
|
7120
|
+
sourceRevisionId: command.payload.sourceRevisionId,
|
|
7121
|
+
locator: command.payload.locator,
|
|
7122
|
+
declaredPageLocators: command.payload.declaredPageLocators,
|
|
7123
|
+
allowedResourceOrigins: command.payload.allowedResourceOrigins,
|
|
7124
|
+
pageId: binding.pageId,
|
|
7125
|
+
limits: command.payload.limits
|
|
7126
|
+
}, {
|
|
7127
|
+
transport: binding.transport,
|
|
7128
|
+
assertNetworkAddressAllowed: binding.assertNetworkAddressAllowed,
|
|
7129
|
+
signal,
|
|
7130
|
+
now
|
|
7131
|
+
});
|
|
7132
|
+
const redaction = emptyRedactionSummary();
|
|
7133
|
+
const processedPages = read.pages.map((page) => {
|
|
7134
|
+
const scrubbed = scrubSnapshotText(page.text);
|
|
7135
|
+
redaction.replacements += scrubbed.summary.replacements;
|
|
7136
|
+
for (const category of REDACTION_CATEGORIES) {
|
|
7137
|
+
redaction.categories[category] += scrubbed.summary.categories[category];
|
|
7138
|
+
}
|
|
7139
|
+
return {
|
|
7140
|
+
metadata: {
|
|
7141
|
+
pageIndex: page.pageIndex,
|
|
7142
|
+
capturedAt: page.capturedAt,
|
|
7143
|
+
charCount: scrubbed.text.length,
|
|
7144
|
+
contentHash: sha256(scrubbed.text)
|
|
7145
|
+
},
|
|
7146
|
+
content: scrubbed.text
|
|
7147
|
+
};
|
|
7148
|
+
});
|
|
7149
|
+
const pages = processedPages.map((page) => page.metadata);
|
|
7150
|
+
const aggregateContentHash = sha256(pages.map((page) => `${page.pageIndex}:${page.contentHash}:${page.charCount}`).join("\n"));
|
|
7151
|
+
const result3 = {
|
|
7152
|
+
...baseResult(command, read.startedAt, read.completedAt),
|
|
7153
|
+
status: read.status,
|
|
7154
|
+
pages,
|
|
7155
|
+
totalSnapshotChars: pages.reduce((total, page) => total + page.charCount, 0),
|
|
7156
|
+
aggregateContentHash,
|
|
7157
|
+
redaction
|
|
7158
|
+
};
|
|
7159
|
+
if (read.status === "partial") result3.coverageGap = read.coverageGap;
|
|
7160
|
+
if (read.status === "complete") {
|
|
7161
|
+
result3.knowledge = {
|
|
7162
|
+
format: "text",
|
|
7163
|
+
pages: processedPages.map((page) => ({
|
|
7164
|
+
pageIndex: page.metadata.pageIndex,
|
|
7165
|
+
content: page.content
|
|
7166
|
+
}))
|
|
7167
|
+
};
|
|
7168
|
+
}
|
|
7169
|
+
await releaseBinding();
|
|
7170
|
+
return result3;
|
|
7171
|
+
} catch (error) {
|
|
7172
|
+
let terminalError = error;
|
|
7173
|
+
try {
|
|
7174
|
+
await releaseBinding();
|
|
7175
|
+
} catch {
|
|
7176
|
+
terminalError = Object.assign(new Error("source_reader_release_failed"), {
|
|
7177
|
+
code: "source_reader_release_failed"
|
|
7178
|
+
});
|
|
7179
|
+
}
|
|
7180
|
+
return failedResult(command, startedAt, safeTimestamp(now), terminalError);
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
|
|
7184
|
+
// src/amaster-runtime-daemon/browser-session-broker.mjs
|
|
7185
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
7186
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
7187
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
7188
|
+
import {
|
|
7189
|
+
chmod,
|
|
7190
|
+
lstat,
|
|
7191
|
+
mkdir,
|
|
7192
|
+
readFile,
|
|
7193
|
+
readdir,
|
|
7194
|
+
realpath,
|
|
7195
|
+
rename,
|
|
7196
|
+
rm,
|
|
7197
|
+
writeFile
|
|
7198
|
+
} from "node:fs/promises";
|
|
7199
|
+
import { join as join13, relative as relative8, resolve as resolve11, sep as sep2 } from "node:path";
|
|
7200
|
+
|
|
7201
|
+
// src/amaster-runtime-daemon/playwright-source-browser.mjs
|
|
7202
|
+
var LOGIN_CONTROL_SELECTORS = Object.freeze([
|
|
7203
|
+
'input[type="password"]',
|
|
7204
|
+
'input[autocomplete="one-time-code"]',
|
|
7205
|
+
'[data-amaster-auth-required="true"]'
|
|
7206
|
+
]);
|
|
7207
|
+
var ACCESS_DENIED_SELECTOR = '[data-amaster-access-denied="true"]';
|
|
7208
|
+
function fail2(code) {
|
|
7209
|
+
throw Object.assign(new Error(code), { code });
|
|
7210
|
+
}
|
|
7211
|
+
function checkSignal(signal) {
|
|
7212
|
+
if (signal?.aborted) fail2("source_reader_aborted");
|
|
7213
|
+
}
|
|
7214
|
+
function pageIds(context, ids, nextId) {
|
|
7215
|
+
return context.pages().map((page) => {
|
|
7216
|
+
if (!ids.has(page)) ids.set(page, nextId.value++);
|
|
7217
|
+
return { pageId: ids.get(page), url: page.url() };
|
|
7218
|
+
});
|
|
7219
|
+
}
|
|
7220
|
+
function selectedPage(context, ids, pageId) {
|
|
7221
|
+
const page = context.pages().find((candidate) => ids.get(candidate) === pageId);
|
|
7222
|
+
if (!page) fail2("source_reader_target_missing");
|
|
7223
|
+
return page;
|
|
7224
|
+
}
|
|
7225
|
+
function redirectChain(response) {
|
|
7226
|
+
const chain = [];
|
|
7227
|
+
let request = response?.request?.()?.redirectedFrom?.() ?? null;
|
|
7228
|
+
while (request) {
|
|
7229
|
+
const locator = request.url?.();
|
|
7230
|
+
if (typeof locator !== "string") fail2("source_reader_transport_invalid");
|
|
7231
|
+
chain.push(locator);
|
|
7232
|
+
request = request.redirectedFrom?.() ?? null;
|
|
7233
|
+
}
|
|
7234
|
+
return chain.reverse();
|
|
7235
|
+
}
|
|
7236
|
+
function originOf(locator) {
|
|
7237
|
+
try {
|
|
7238
|
+
const url = new URL(locator);
|
|
7239
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return null;
|
|
7240
|
+
return url.origin;
|
|
7241
|
+
} catch {
|
|
7242
|
+
return null;
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
7245
|
+
async function locatorVisible(page, selector) {
|
|
7246
|
+
const locator = page.locator(selector);
|
|
7247
|
+
if (await locator.count() === 0) return false;
|
|
7248
|
+
const target = typeof locator.first === "function" ? locator.first() : locator;
|
|
7249
|
+
return target.isVisible();
|
|
7250
|
+
}
|
|
7251
|
+
async function createPlaywrightSourceBrowser(options) {
|
|
7252
|
+
if (!options?.context || !options?.page || typeof options.assertNetworkAddressAllowed !== "function") {
|
|
7253
|
+
fail2("source_reader_transport_invalid");
|
|
7254
|
+
}
|
|
7255
|
+
const { context, page, assertNetworkAddressAllowed } = options;
|
|
7256
|
+
const maxSnapshotChars = Number.isSafeInteger(options.maxSnapshotChars) && options.maxSnapshotChars > 0 ? options.maxSnapshotChars : 2e5;
|
|
7257
|
+
const ids = /* @__PURE__ */ new WeakMap();
|
|
7258
|
+
const nextId = { value: 1 };
|
|
7259
|
+
ids.set(page, nextId.value++);
|
|
7260
|
+
let networkFailure = null;
|
|
7261
|
+
let allowedOrigins = new Set(Array.isArray(options.allowedOrigins) ? options.allowedOrigins : []);
|
|
7262
|
+
const accessDeniedPages = /* @__PURE__ */ new WeakSet();
|
|
7263
|
+
if (typeof context.route !== "function") {
|
|
7264
|
+
fail2("source_reader_transport_invalid");
|
|
7265
|
+
}
|
|
7266
|
+
await context.route("**/*", async (route) => {
|
|
7267
|
+
try {
|
|
7268
|
+
const locator = route.request().url();
|
|
7269
|
+
const origin = originOf(locator);
|
|
7270
|
+
if (!origin || !allowedOrigins.has(origin)) {
|
|
7271
|
+
networkFailure = "source_reader_resource_origin_forbidden";
|
|
7272
|
+
await route.abort("blockedbyclient");
|
|
7273
|
+
return;
|
|
7274
|
+
}
|
|
7275
|
+
await assertNetworkAddressAllowed(locator);
|
|
7276
|
+
await route.continue();
|
|
7277
|
+
} catch {
|
|
7278
|
+
networkFailure = "source_reader_network_scope_forbidden";
|
|
7279
|
+
await route.abort("blockedbyclient");
|
|
7280
|
+
}
|
|
7281
|
+
});
|
|
7282
|
+
if (typeof context.routeWebSocket !== "function") {
|
|
7283
|
+
fail2("source_reader_transport_invalid");
|
|
7284
|
+
}
|
|
7285
|
+
await context.routeWebSocket("**/*", async (webSocket) => {
|
|
7286
|
+
networkFailure = "source_reader_network_scope_forbidden";
|
|
7287
|
+
await webSocket.close({
|
|
7288
|
+
code: 1008,
|
|
7289
|
+
reason: "source_reader_websocket_forbidden"
|
|
7290
|
+
});
|
|
7291
|
+
});
|
|
7292
|
+
const transport = {
|
|
7293
|
+
async listPages({ signal } = {}) {
|
|
7294
|
+
checkSignal(signal);
|
|
7295
|
+
if (networkFailure) fail2(networkFailure);
|
|
7296
|
+
return pageIds(context, ids, nextId);
|
|
7297
|
+
},
|
|
7298
|
+
async navigatePage({ pageId, locator, allowedResourceOrigins = [], signal } = {}) {
|
|
7299
|
+
checkSignal(signal);
|
|
7300
|
+
const target = selectedPage(context, ids, pageId);
|
|
7301
|
+
await assertNetworkAddressAllowed(locator);
|
|
7302
|
+
const contentOrigin = originOf(locator);
|
|
7303
|
+
if (!contentOrigin || !Array.isArray(allowedResourceOrigins)) {
|
|
7304
|
+
fail2("source_reader_transport_invalid");
|
|
7305
|
+
}
|
|
7306
|
+
allowedOrigins = /* @__PURE__ */ new Set([contentOrigin, ...allowedResourceOrigins]);
|
|
7307
|
+
const resources = /* @__PURE__ */ new Set();
|
|
7308
|
+
const checks = [];
|
|
7309
|
+
let downloadAttempted = false;
|
|
7310
|
+
const onRequest = (request) => {
|
|
7311
|
+
const resourceLocator = request.url();
|
|
7312
|
+
checks.push(Promise.resolve(assertNetworkAddressAllowed(resourceLocator)));
|
|
7313
|
+
try {
|
|
7314
|
+
resources.add(new URL(resourceLocator).origin);
|
|
7315
|
+
} catch {
|
|
7316
|
+
networkFailure = "source_reader_network_scope_forbidden";
|
|
7317
|
+
}
|
|
7318
|
+
};
|
|
7319
|
+
const onDownload = () => {
|
|
7320
|
+
downloadAttempted = true;
|
|
7321
|
+
};
|
|
7322
|
+
target.on("request", onRequest);
|
|
7323
|
+
target.on("download", onDownload);
|
|
7324
|
+
let response;
|
|
7325
|
+
try {
|
|
7326
|
+
response = await target.goto(locator, { waitUntil: "domcontentloaded" });
|
|
7327
|
+
await Promise.all(checks);
|
|
7328
|
+
} catch (error) {
|
|
7329
|
+
if (networkFailure || error?.code === "source_reader_network_scope_forbidden") {
|
|
7330
|
+
fail2("source_reader_network_scope_forbidden");
|
|
7331
|
+
}
|
|
7332
|
+
fail2("source_reader_transport_failed");
|
|
7333
|
+
} finally {
|
|
7334
|
+
target.off("request", onRequest);
|
|
7335
|
+
target.off("download", onDownload);
|
|
7336
|
+
}
|
|
7337
|
+
checkSignal(signal);
|
|
7338
|
+
if (networkFailure) fail2(networkFailure);
|
|
7339
|
+
const status = response?.status?.();
|
|
7340
|
+
if (status === 401 || status === 403) accessDeniedPages.add(target);
|
|
7341
|
+
const finalUrl = response?.url?.() ?? target.url();
|
|
7342
|
+
const redirects = redirectChain(response);
|
|
7343
|
+
for (const redirect of redirects) await assertNetworkAddressAllowed(redirect);
|
|
7344
|
+
await assertNetworkAddressAllowed(finalUrl);
|
|
7345
|
+
return {
|
|
7346
|
+
finalUrl,
|
|
7347
|
+
redirectChain: redirects,
|
|
7348
|
+
resourceOrigins: [...resources].sort(),
|
|
7349
|
+
downloadAttempted
|
|
7350
|
+
};
|
|
7351
|
+
},
|
|
7352
|
+
async takeSnapshot({ pageId, signal } = {}) {
|
|
7353
|
+
checkSignal(signal);
|
|
7354
|
+
if (networkFailure) fail2(networkFailure);
|
|
7355
|
+
const target = selectedPage(context, ids, pageId);
|
|
7356
|
+
if (accessDeniedPages.has(target) || await locatorVisible(target, ACCESS_DENIED_SELECTOR)) {
|
|
7357
|
+
fail2("source_reader_no_access");
|
|
7358
|
+
}
|
|
7359
|
+
for (const selector of LOGIN_CONTROL_SELECTORS) {
|
|
7360
|
+
if (await locatorVisible(target, selector)) fail2("source_reader_auth_required");
|
|
7361
|
+
}
|
|
7362
|
+
const text = await target.locator("body").innerText();
|
|
7363
|
+
if (typeof text !== "string") fail2("source_reader_transport_invalid");
|
|
7364
|
+
checkSignal(signal);
|
|
7365
|
+
if (networkFailure) fail2(networkFailure);
|
|
7366
|
+
return {
|
|
7367
|
+
text: text.slice(0, maxSnapshotChars),
|
|
7368
|
+
truncated: text.length > maxSnapshotChars,
|
|
7369
|
+
unsupportedInteraction: null
|
|
7370
|
+
};
|
|
7371
|
+
}
|
|
7372
|
+
};
|
|
7373
|
+
return Object.freeze({
|
|
7374
|
+
pageId: ids.get(page),
|
|
7375
|
+
transport: Object.freeze(transport),
|
|
7376
|
+
assertNetworkAddressAllowed
|
|
7377
|
+
});
|
|
7378
|
+
}
|
|
7379
|
+
|
|
7380
|
+
// src/amaster-runtime-daemon/browser-session-broker.mjs
|
|
7381
|
+
var MARKER_NAME = ".amaster-browser-session.json";
|
|
7382
|
+
var MARKER_VERSION = 1;
|
|
7383
|
+
var SUPPORTED_BROWSER_VERSION = /(?:Google Chrome|Chromium|Microsoft Edge|Chrome for Testing)/iu;
|
|
7384
|
+
function fail3(code) {
|
|
7385
|
+
throw Object.assign(new Error(code), { code });
|
|
7386
|
+
}
|
|
7387
|
+
function sha2562(value) {
|
|
7388
|
+
return createHash11("sha256").update(value, "utf8").digest("hex");
|
|
7389
|
+
}
|
|
7390
|
+
function configuredBrowserExecutableReady(executablePath) {
|
|
7391
|
+
if (!executablePath || !existsSync12(executablePath)) return false;
|
|
7392
|
+
const probe = spawnSync5(executablePath, ["--version"], {
|
|
7393
|
+
encoding: "utf8",
|
|
7394
|
+
env: { PATH: process.env.PATH ?? "" },
|
|
7395
|
+
maxBuffer: 64 * 1024,
|
|
7396
|
+
timeout: 5e3,
|
|
7397
|
+
windowsHide: true
|
|
7398
|
+
});
|
|
7399
|
+
if (probe.error || probe.signal || probe.status !== 0) return false;
|
|
7400
|
+
const output = `${probe.stdout ?? ""}
|
|
7401
|
+
${probe.stderr ?? ""}`.slice(0, 64 * 1024);
|
|
7402
|
+
return SUPPORTED_BROWSER_VERSION.test(output);
|
|
7403
|
+
}
|
|
7404
|
+
function profileName(identity2) {
|
|
7405
|
+
return sha2562(`${identity2.companyId}\0${identity2.bindingId}\0${identity2.localOpaqueRef}`);
|
|
7406
|
+
}
|
|
7407
|
+
function expectedMarker(identity2) {
|
|
7408
|
+
return {
|
|
7409
|
+
version: MARKER_VERSION,
|
|
7410
|
+
companyId: identity2.companyId,
|
|
7411
|
+
bindingId: identity2.bindingId,
|
|
7412
|
+
localOpaqueRef: identity2.localOpaqueRef
|
|
7413
|
+
};
|
|
7414
|
+
}
|
|
7415
|
+
function markerMatches(marker, identity2) {
|
|
7416
|
+
const expected = expectedMarker(identity2);
|
|
7417
|
+
return marker?.version === expected.version && marker?.companyId === expected.companyId && marker?.bindingId === expected.bindingId && marker?.localOpaqueRef === expected.localOpaqueRef && Object.keys(marker).length === Object.keys(expected).length;
|
|
7418
|
+
}
|
|
7419
|
+
function validLease(input, now) {
|
|
7420
|
+
const expiresAt = Date.parse(input.leaseExpiresAt);
|
|
7421
|
+
if (typeof input.leaseId !== "string" || !input.leaseId || !Number.isFinite(expiresAt) || expiresAt <= now.getTime()) {
|
|
7422
|
+
fail3("browser_session_lease_expired");
|
|
7423
|
+
}
|
|
7424
|
+
return new Date(expiresAt);
|
|
7425
|
+
}
|
|
7426
|
+
async function pathStat(path, missingAllowed = false) {
|
|
7427
|
+
try {
|
|
7428
|
+
return await lstat(path);
|
|
7429
|
+
} catch (error) {
|
|
7430
|
+
if (missingAllowed && error?.code === "ENOENT") return null;
|
|
7431
|
+
throw error;
|
|
7432
|
+
}
|
|
7433
|
+
}
|
|
7434
|
+
async function ensureProtectedDirectory(path) {
|
|
7435
|
+
await mkdir(path, { recursive: true, mode: 448 });
|
|
7436
|
+
const metadata = await lstat(path);
|
|
7437
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
7438
|
+
fail3("browser_session_profile_path_forbidden");
|
|
7439
|
+
}
|
|
7440
|
+
await chmod(path, 448);
|
|
7441
|
+
return realpath(path);
|
|
7442
|
+
}
|
|
7443
|
+
function assertWithinRoot(root, target) {
|
|
7444
|
+
const pathFromRoot = relative8(root, target);
|
|
7445
|
+
if (pathFromRoot === "" || pathFromRoot === ".." || pathFromRoot.startsWith(`..${sep2}`) || resolve11(root, pathFromRoot) !== target) {
|
|
7446
|
+
fail3("browser_session_profile_path_forbidden");
|
|
7447
|
+
}
|
|
7448
|
+
}
|
|
7449
|
+
async function readMarker(profilePath) {
|
|
7450
|
+
const markerPath = join13(profilePath, MARKER_NAME);
|
|
7451
|
+
const metadata = await pathStat(markerPath, true);
|
|
7452
|
+
if (!metadata || !metadata.isFile() || metadata.isSymbolicLink()) {
|
|
7453
|
+
fail3("browser_session_profile_owner_mismatch");
|
|
7454
|
+
}
|
|
7455
|
+
try {
|
|
7456
|
+
const marker = JSON.parse(await readFile(markerPath, "utf8"));
|
|
7457
|
+
if (!marker || typeof marker !== "object" || Array.isArray(marker)) throw new Error();
|
|
7458
|
+
return marker;
|
|
7459
|
+
} catch {
|
|
7460
|
+
fail3("browser_session_profile_owner_mismatch");
|
|
7461
|
+
}
|
|
7462
|
+
}
|
|
7463
|
+
async function verifyOwnedProfile(root, profilePath, identity2) {
|
|
7464
|
+
const metadata = await pathStat(profilePath, true);
|
|
7465
|
+
if (!metadata) return false;
|
|
7466
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
7467
|
+
fail3("browser_session_profile_path_forbidden");
|
|
7468
|
+
}
|
|
7469
|
+
const canonical = await realpath(profilePath);
|
|
7470
|
+
assertWithinRoot(root, canonical);
|
|
7471
|
+
if (!markerMatches(await readMarker(canonical), identity2)) {
|
|
7472
|
+
fail3("browser_session_profile_owner_mismatch");
|
|
7473
|
+
}
|
|
7474
|
+
return true;
|
|
7475
|
+
}
|
|
7476
|
+
async function writeMarker(profilePath, identity2) {
|
|
7477
|
+
const markerPath = join13(profilePath, MARKER_NAME);
|
|
7478
|
+
const temporaryPath = `${markerPath}.tmp-${process.pid}-${Date.now()}`;
|
|
7479
|
+
try {
|
|
7480
|
+
await writeFile(temporaryPath, `${JSON.stringify(expectedMarker(identity2))}
|
|
7481
|
+
`, {
|
|
7482
|
+
flag: "wx",
|
|
7483
|
+
mode: 384
|
|
7484
|
+
});
|
|
7485
|
+
await rename(temporaryPath, markerPath);
|
|
7486
|
+
} finally {
|
|
7487
|
+
await rm(temporaryPath, { force: true });
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
async function ensureOwnedProfile(root, identity2) {
|
|
7491
|
+
const profilePath = join13(root, profileName(identity2));
|
|
7492
|
+
const exists = await verifyOwnedProfile(root, profilePath, identity2);
|
|
7493
|
+
if (exists) return { profilePath, reusedProfile: true };
|
|
7494
|
+
await mkdir(profilePath, { mode: 448 });
|
|
7495
|
+
await chmod(profilePath, 448);
|
|
7496
|
+
await writeMarker(profilePath, identity2);
|
|
7497
|
+
await verifyOwnedProfile(root, profilePath, identity2);
|
|
7498
|
+
return { profilePath, reusedProfile: false };
|
|
7499
|
+
}
|
|
7500
|
+
async function assertNoSymlinks(path) {
|
|
7501
|
+
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
7502
|
+
const entryPath = join13(path, entry.name);
|
|
7503
|
+
if (entry.isSymbolicLink()) fail3("browser_session_profile_symlink_forbidden");
|
|
7504
|
+
if (entry.isDirectory()) await assertNoSymlinks(entryPath);
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
async function removeOwnedProfile(root, identity2) {
|
|
7508
|
+
const profilePath = join13(root, profileName(identity2));
|
|
7509
|
+
if (!await verifyOwnedProfile(root, profilePath, identity2)) return false;
|
|
7510
|
+
await assertNoSymlinks(profilePath);
|
|
7511
|
+
await rm(profilePath, { recursive: true });
|
|
7512
|
+
return true;
|
|
7513
|
+
}
|
|
7514
|
+
async function closeQuietly(context) {
|
|
7515
|
+
try {
|
|
7516
|
+
await context?.close?.();
|
|
7517
|
+
} catch {
|
|
7518
|
+
}
|
|
7519
|
+
}
|
|
7520
|
+
function resolverHost(locator) {
|
|
7521
|
+
try {
|
|
7522
|
+
return new URL(locator).hostname.toLowerCase().replace(/\.$/u, "");
|
|
7523
|
+
} catch {
|
|
7524
|
+
fail3("source_reader_network_scope_forbidden");
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
function resolverAddress(address) {
|
|
7528
|
+
return address.includes(":") ? `[${address}]` : address;
|
|
7529
|
+
}
|
|
7530
|
+
function httpNetworkLocator(locator) {
|
|
7531
|
+
try {
|
|
7532
|
+
const url = new URL(locator);
|
|
7533
|
+
if (url.protocol === "ws:") url.protocol = "http:";
|
|
7534
|
+
else if (url.protocol === "wss:") url.protocol = "https:";
|
|
7535
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
7536
|
+
fail3("browser_session_network_scope_forbidden");
|
|
7537
|
+
}
|
|
7538
|
+
return url.href;
|
|
7539
|
+
} catch (error) {
|
|
7540
|
+
if (error?.code === "browser_session_network_scope_forbidden") throw error;
|
|
7541
|
+
fail3("browser_session_network_scope_forbidden");
|
|
7542
|
+
}
|
|
7543
|
+
}
|
|
7544
|
+
function createBrowserSessionBroker(options) {
|
|
7545
|
+
const now = typeof options?.now === "function" ? options.now : () => /* @__PURE__ */ new Date();
|
|
7546
|
+
const playwright = options?.playwright;
|
|
7547
|
+
const browserExecutablePath = typeof options?.browserExecutablePath === "string" && options.browserExecutablePath.trim() ? resolve11(options.browserExecutablePath) : null;
|
|
7548
|
+
const browserExecutableReady = browserExecutablePath ? configuredBrowserExecutableReady(browserExecutablePath) : null;
|
|
7549
|
+
const networkScope = options?.publicNetworkScope;
|
|
7550
|
+
if (!options?.stateRoot || typeof networkScope?.assertNetworkAddressAllowed !== "function" || typeof networkScope?.resolveNetworkAddresses !== "function") {
|
|
7551
|
+
fail3("browser_session_broker_config_invalid");
|
|
7552
|
+
}
|
|
7553
|
+
const scheduleTimeout = typeof options.setTimeout === "function" ? options.setTimeout : setTimeout;
|
|
7554
|
+
const cancelTimeout = typeof options.clearTimeout === "function" ? options.clearTimeout : clearTimeout;
|
|
7555
|
+
const rootPromise = (async () => {
|
|
7556
|
+
const stateRoot = await ensureProtectedDirectory(resolve11(options.stateRoot));
|
|
7557
|
+
return ensureProtectedDirectory(join13(stateRoot, "browser-sessions"));
|
|
7558
|
+
})();
|
|
7559
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
7560
|
+
async function closeBinding(bindingId) {
|
|
7561
|
+
const active = sessions.get(bindingId);
|
|
7562
|
+
if (!active) return;
|
|
7563
|
+
sessions.delete(bindingId);
|
|
7564
|
+
if (active.leaseTimer) cancelTimeout(active.leaseTimer);
|
|
7565
|
+
active.removeAbortListener?.();
|
|
7566
|
+
await closeQuietly(active.context);
|
|
7567
|
+
}
|
|
7568
|
+
function scheduleLeaseDeadline(session) {
|
|
7569
|
+
if (session.leaseTimer) cancelTimeout(session.leaseTimer);
|
|
7570
|
+
const delay = Math.max(0, session.leaseExpiresAt.getTime() - now().getTime());
|
|
7571
|
+
const timer = scheduleTimeout(async () => {
|
|
7572
|
+
const active = sessions.get(session.bindingId);
|
|
7573
|
+
if (active?.leaseId !== session.leaseId) return;
|
|
7574
|
+
await closeBinding(session.bindingId);
|
|
7575
|
+
}, delay);
|
|
7576
|
+
timer?.unref?.();
|
|
7577
|
+
session.leaseTimer = timer;
|
|
7578
|
+
}
|
|
7579
|
+
function attachAbortSignal(session, signal) {
|
|
7580
|
+
session.removeAbortListener?.();
|
|
7581
|
+
if (!signal || typeof signal.addEventListener !== "function") return;
|
|
7582
|
+
const abort = () => {
|
|
7583
|
+
void closeBinding(session.bindingId);
|
|
7584
|
+
};
|
|
7585
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
7586
|
+
session.removeAbortListener = () => signal.removeEventListener?.("abort", abort);
|
|
7587
|
+
if (signal.aborted) abort();
|
|
7588
|
+
}
|
|
7589
|
+
async function installHumanNetworkGuard(context, pinnedHosts) {
|
|
7590
|
+
if (typeof context?.route !== "function" || typeof context?.routeWebSocket !== "function") {
|
|
7591
|
+
fail3("browser_session_network_guard_unavailable");
|
|
7592
|
+
}
|
|
7593
|
+
let failure = null;
|
|
7594
|
+
await context.route("**/*", async (route) => {
|
|
7595
|
+
try {
|
|
7596
|
+
const locator = httpNetworkLocator(route.request().url());
|
|
7597
|
+
if (!pinnedHosts.has(resolverHost(locator))) {
|
|
7598
|
+
fail3("browser_session_network_scope_forbidden");
|
|
7599
|
+
}
|
|
7600
|
+
await networkScope.assertNetworkAddressAllowed(locator);
|
|
7601
|
+
await route.continue();
|
|
7602
|
+
} catch {
|
|
7603
|
+
failure = "browser_session_network_scope_forbidden";
|
|
7604
|
+
await route.abort("blockedbyclient");
|
|
7605
|
+
}
|
|
7606
|
+
});
|
|
7607
|
+
await context.routeWebSocket("**/*", async (webSocket) => {
|
|
7608
|
+
try {
|
|
7609
|
+
const locator = httpNetworkLocator(webSocket.url());
|
|
7610
|
+
if (!pinnedHosts.has(resolverHost(locator))) {
|
|
7611
|
+
fail3("browser_session_network_scope_forbidden");
|
|
7612
|
+
}
|
|
7613
|
+
await networkScope.assertNetworkAddressAllowed(locator);
|
|
7614
|
+
webSocket.connectToServer();
|
|
7615
|
+
} catch {
|
|
7616
|
+
failure = "browser_session_network_scope_forbidden";
|
|
7617
|
+
await webSocket.close({
|
|
7618
|
+
code: 1008,
|
|
7619
|
+
reason: "browser_session_network_scope_forbidden"
|
|
7620
|
+
});
|
|
7621
|
+
}
|
|
7622
|
+
});
|
|
7623
|
+
return () => {
|
|
7624
|
+
if (failure) fail3(failure);
|
|
7625
|
+
};
|
|
7626
|
+
}
|
|
7627
|
+
async function browserLaunchArgs(input) {
|
|
7628
|
+
const locators = [
|
|
7629
|
+
input.locator,
|
|
7630
|
+
...Array.isArray(input.declaredPageLocators) ? input.declaredPageLocators : [],
|
|
7631
|
+
...Array.isArray(input.allowedResourceOrigins) ? input.allowedResourceOrigins : []
|
|
7632
|
+
];
|
|
7633
|
+
const pinned = /* @__PURE__ */ new Map();
|
|
7634
|
+
for (const locator of locators) {
|
|
7635
|
+
if (typeof locator !== "string" || !locator) continue;
|
|
7636
|
+
const addresses = await networkScope.resolveNetworkAddresses(locator);
|
|
7637
|
+
if (!Array.isArray(addresses) || addresses.length === 0) {
|
|
7638
|
+
fail3("source_reader_network_scope_forbidden");
|
|
7639
|
+
}
|
|
7640
|
+
const host = resolverHost(locator);
|
|
7641
|
+
const address = addresses[0];
|
|
7642
|
+
if (typeof address !== "string" || !address) fail3("source_reader_network_scope_forbidden");
|
|
7643
|
+
pinned.set(host, resolverAddress(address));
|
|
7644
|
+
}
|
|
7645
|
+
const rules = [...pinned.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([host, address]) => `MAP ${host} ${address}`);
|
|
7646
|
+
return rules.length > 0 ? [`--host-resolver-rules=${[...rules, "EXCLUDE localhost"].join(",")}`] : [];
|
|
7647
|
+
}
|
|
7648
|
+
function pinnedNetworkHosts(input) {
|
|
7649
|
+
return new Set([
|
|
7650
|
+
input.locator,
|
|
7651
|
+
...Array.isArray(input.declaredPageLocators) ? input.declaredPageLocators : [],
|
|
7652
|
+
...Array.isArray(input.allowedResourceOrigins) ? input.allowedResourceOrigins : []
|
|
7653
|
+
].filter((locator) => typeof locator === "string" && locator).map(resolverHost));
|
|
7654
|
+
}
|
|
7655
|
+
async function launchPersistent(identity2, leaseKind, leaseId, leaseExpiresAt) {
|
|
7656
|
+
const root = await rootPromise;
|
|
7657
|
+
const profile = await ensureOwnedProfile(root, identity2);
|
|
7658
|
+
if (typeof playwright?.chromium?.launchPersistentContext !== "function") {
|
|
7659
|
+
fail3("browser_session_runtime_unavailable");
|
|
7660
|
+
}
|
|
7661
|
+
let context;
|
|
7662
|
+
try {
|
|
7663
|
+
const args = await browserLaunchArgs(identity2);
|
|
7664
|
+
context = await playwright.chromium.launchPersistentContext(profile.profilePath, {
|
|
7665
|
+
headless: false,
|
|
7666
|
+
acceptDownloads: false,
|
|
7667
|
+
serviceWorkers: "block",
|
|
7668
|
+
args,
|
|
7669
|
+
...browserExecutablePath ? { executablePath: browserExecutablePath } : {}
|
|
7670
|
+
});
|
|
7671
|
+
const page = context.pages()[0] ?? await context.newPage();
|
|
7672
|
+
const session = {
|
|
7673
|
+
...identity2,
|
|
7674
|
+
context,
|
|
7675
|
+
page,
|
|
7676
|
+
leaseKind,
|
|
7677
|
+
leaseId,
|
|
7678
|
+
leaseExpiresAt
|
|
7679
|
+
};
|
|
7680
|
+
sessions.set(identity2.bindingId, session);
|
|
7681
|
+
scheduleLeaseDeadline(session);
|
|
7682
|
+
return { session, reusedProfile: profile.reusedProfile };
|
|
7683
|
+
} catch (error) {
|
|
7684
|
+
await closeQuietly(context);
|
|
7685
|
+
if (error?.code?.startsWith?.("browser_session_")) throw error;
|
|
7686
|
+
fail3("browser_session_launch_failed");
|
|
7687
|
+
}
|
|
7688
|
+
}
|
|
7689
|
+
async function openHumanSession(input) {
|
|
7690
|
+
const expiresAt = validLease(input, now());
|
|
7691
|
+
await networkScope.assertNetworkAddressAllowed(input.locator);
|
|
7692
|
+
const existing = sessions.get(input.bindingId);
|
|
7693
|
+
if (existing && existing.companyId === input.companyId && existing.localOpaqueRef === input.localOpaqueRef && existing.leaseKind === "human" && existing.leaseId === input.leaseId) {
|
|
7694
|
+
return {
|
|
7695
|
+
bindingId: input.bindingId,
|
|
7696
|
+
leaseId: input.leaseId,
|
|
7697
|
+
reusedProfile: true,
|
|
7698
|
+
status: "human_lease"
|
|
7699
|
+
};
|
|
7700
|
+
}
|
|
7701
|
+
await closeBinding(input.bindingId);
|
|
7702
|
+
const { session, reusedProfile } = await launchPersistent(
|
|
7703
|
+
input,
|
|
7704
|
+
"human",
|
|
7705
|
+
input.leaseId,
|
|
7706
|
+
expiresAt
|
|
7707
|
+
);
|
|
7708
|
+
try {
|
|
7709
|
+
const assertNetworkGuardHealthy = await installHumanNetworkGuard(
|
|
7710
|
+
session.context,
|
|
7711
|
+
pinnedNetworkHosts(input)
|
|
7712
|
+
);
|
|
7713
|
+
await session.page.goto(input.locator, { waitUntil: "domcontentloaded" });
|
|
7714
|
+
assertNetworkGuardHealthy();
|
|
7715
|
+
} catch {
|
|
7716
|
+
await closeBinding(input.bindingId);
|
|
7717
|
+
fail3("browser_session_navigation_failed");
|
|
7718
|
+
}
|
|
7719
|
+
return {
|
|
7720
|
+
bindingId: input.bindingId,
|
|
7721
|
+
leaseId: input.leaseId,
|
|
7722
|
+
reusedProfile,
|
|
7723
|
+
status: "human_lease"
|
|
7724
|
+
};
|
|
7725
|
+
}
|
|
7726
|
+
async function switchAccount(input) {
|
|
7727
|
+
validLease(input, now());
|
|
7728
|
+
const previous = input.previousBinding;
|
|
7729
|
+
if (!previous || previous.bindingId === input.bindingId || typeof previous.localOpaqueRef !== "string") {
|
|
7730
|
+
fail3("browser_session_command_invalid");
|
|
7731
|
+
}
|
|
7732
|
+
const previousSession = sessions.get(previous.bindingId);
|
|
7733
|
+
if (previousSession && (previousSession.companyId !== input.companyId || previousSession.localOpaqueRef !== previous.localOpaqueRef)) {
|
|
7734
|
+
fail3("browser_session_profile_owner_mismatch");
|
|
7735
|
+
}
|
|
7736
|
+
await closeBinding(previous.bindingId);
|
|
7737
|
+
await closeBinding(input.bindingId);
|
|
7738
|
+
const root = await rootPromise;
|
|
7739
|
+
await removeOwnedProfile(root, {
|
|
7740
|
+
companyId: input.companyId,
|
|
7741
|
+
bindingId: previous.bindingId,
|
|
7742
|
+
localOpaqueRef: previous.localOpaqueRef
|
|
7743
|
+
});
|
|
7744
|
+
return openHumanSession(input);
|
|
7745
|
+
}
|
|
7746
|
+
async function revokeBinding(input) {
|
|
7747
|
+
await closeBinding(input.bindingId);
|
|
7748
|
+
const root = await rootPromise;
|
|
7749
|
+
await removeOwnedProfile(root, input);
|
|
7750
|
+
return { bindingId: input.bindingId, status: "revoked" };
|
|
7751
|
+
}
|
|
7752
|
+
async function resolveAuthenticatedBinding(input) {
|
|
7753
|
+
const expiresAt = validLease(input, now());
|
|
7754
|
+
await networkScope.assertNetworkAddressAllowed(input.locator);
|
|
7755
|
+
let session = sessions.get(input.bindingId);
|
|
7756
|
+
if (session) {
|
|
7757
|
+
if (session.companyId !== input.companyId || session.localOpaqueRef !== input.localOpaqueRef) {
|
|
7758
|
+
fail3("browser_session_profile_owner_mismatch");
|
|
7759
|
+
}
|
|
7760
|
+
if (session.leaseKind === "human" && session.leaseId === input.leaseId) {
|
|
7761
|
+
fail3("source_reader_human_lease_active");
|
|
7762
|
+
}
|
|
7763
|
+
if (session.leaseKind === "human") {
|
|
7764
|
+
await closeBinding(input.bindingId);
|
|
7765
|
+
session = (await launchPersistent(
|
|
7766
|
+
input,
|
|
7767
|
+
"agent_read",
|
|
7768
|
+
input.leaseId,
|
|
7769
|
+
expiresAt
|
|
7770
|
+
)).session;
|
|
7771
|
+
} else if (session.leaseKind === "agent_read" && session.leaseId !== input.leaseId) {
|
|
7772
|
+
fail3("source_reader_lease_conflict");
|
|
7773
|
+
} else {
|
|
7774
|
+
session.leaseKind = "agent_read";
|
|
7775
|
+
session.leaseId = input.leaseId;
|
|
7776
|
+
session.leaseExpiresAt = expiresAt;
|
|
7777
|
+
scheduleLeaseDeadline(session);
|
|
7778
|
+
}
|
|
7779
|
+
} else {
|
|
7780
|
+
const root = await rootPromise;
|
|
7781
|
+
const profilePath = join13(root, profileName(input));
|
|
7782
|
+
if (!await verifyOwnedProfile(root, profilePath, input)) {
|
|
7783
|
+
fail3("browser_session_profile_missing");
|
|
7784
|
+
}
|
|
7785
|
+
session = (await launchPersistent(
|
|
7786
|
+
input,
|
|
7787
|
+
"agent_read",
|
|
7788
|
+
input.leaseId,
|
|
7789
|
+
expiresAt
|
|
7790
|
+
)).session;
|
|
7791
|
+
}
|
|
7792
|
+
try {
|
|
7793
|
+
const binding = await createPlaywrightSourceBrowser({
|
|
7794
|
+
context: session.context,
|
|
7795
|
+
page: session.page,
|
|
7796
|
+
maxSnapshotChars: input.limits?.maxSnapshotChars,
|
|
7797
|
+
assertNetworkAddressAllowed: networkScope.assertNetworkAddressAllowed
|
|
7798
|
+
});
|
|
7799
|
+
attachAbortSignal(session, input.signal);
|
|
7800
|
+
await binding.transport.takeSnapshot({ pageId: binding.pageId });
|
|
7801
|
+
return {
|
|
7802
|
+
...binding,
|
|
7803
|
+
release: () => closeBinding(input.bindingId)
|
|
7804
|
+
};
|
|
7805
|
+
} catch (error) {
|
|
7806
|
+
await closeBinding(input.bindingId);
|
|
7807
|
+
throw error;
|
|
7808
|
+
}
|
|
7809
|
+
}
|
|
7810
|
+
async function resolvePublicBinding(input) {
|
|
7811
|
+
if (typeof playwright?.chromium?.launch !== "function") {
|
|
7812
|
+
fail3("browser_session_runtime_unavailable");
|
|
7813
|
+
}
|
|
7814
|
+
await networkScope.assertNetworkAddressAllowed(input.locator);
|
|
7815
|
+
const args = await browserLaunchArgs(input);
|
|
7816
|
+
const browser = await playwright.chromium.launch({
|
|
7817
|
+
headless: true,
|
|
7818
|
+
args,
|
|
7819
|
+
...browserExecutablePath ? { executablePath: browserExecutablePath } : {}
|
|
7820
|
+
});
|
|
7821
|
+
const context = await browser.newContext({
|
|
7822
|
+
acceptDownloads: false,
|
|
7823
|
+
serviceWorkers: "block"
|
|
7824
|
+
});
|
|
7825
|
+
const page = await context.newPage();
|
|
7826
|
+
const binding = await createPlaywrightSourceBrowser({
|
|
7827
|
+
context,
|
|
7828
|
+
page,
|
|
7829
|
+
maxSnapshotChars: input.limits?.maxSnapshotChars,
|
|
7830
|
+
assertNetworkAddressAllowed: networkScope.assertNetworkAddressAllowed
|
|
7831
|
+
});
|
|
7832
|
+
return {
|
|
7833
|
+
...binding,
|
|
7834
|
+
release: async () => {
|
|
7835
|
+
await closeQuietly(context);
|
|
7836
|
+
await closeQuietly(browser);
|
|
7837
|
+
}
|
|
7838
|
+
};
|
|
7839
|
+
}
|
|
7840
|
+
return Object.freeze({
|
|
7841
|
+
readiness() {
|
|
7842
|
+
if (typeof playwright?.chromium?.launchPersistentContext !== "function") {
|
|
7843
|
+
return { ready: false, reason: "playwright_unavailable" };
|
|
7844
|
+
}
|
|
7845
|
+
if (browserExecutablePath && !browserExecutableReady) {
|
|
7846
|
+
return { ready: false, reason: "chrome_unavailable" };
|
|
7847
|
+
}
|
|
7848
|
+
if (typeof playwright.chromium.executablePath === "function") {
|
|
7849
|
+
const executablePath = playwright.chromium.executablePath();
|
|
7850
|
+
if (!executablePath || !existsSync12(executablePath)) {
|
|
7851
|
+
return { ready: false, reason: "chrome_unavailable" };
|
|
7852
|
+
}
|
|
7853
|
+
}
|
|
7854
|
+
return { ready: true, reason: null };
|
|
7855
|
+
},
|
|
7856
|
+
openHumanSession,
|
|
7857
|
+
reopenHumanSession: openHumanSession,
|
|
7858
|
+
switchAccount,
|
|
7859
|
+
revokeBinding,
|
|
7860
|
+
resolveAuthenticatedBinding,
|
|
7861
|
+
resolvePublicBinding,
|
|
7862
|
+
async releaseLease({ bindingId, leaseId }) {
|
|
7863
|
+
const session = sessions.get(bindingId);
|
|
7864
|
+
if (!session || session.leaseId !== leaseId) return false;
|
|
7865
|
+
await closeBinding(bindingId);
|
|
7866
|
+
return true;
|
|
7867
|
+
},
|
|
7868
|
+
async shutdown() {
|
|
7869
|
+
await Promise.all([...sessions.keys()].map(closeBinding));
|
|
7870
|
+
}
|
|
7871
|
+
});
|
|
7872
|
+
}
|
|
7873
|
+
|
|
7874
|
+
// src/amaster-runtime-daemon/browser-session-command.mjs
|
|
7875
|
+
var BROWSER_SESSION_CONTRACT_VERSION = "amaster.browser-session.v1";
|
|
7876
|
+
var PAYLOAD_FIELDS = /* @__PURE__ */ new Set([
|
|
7877
|
+
"contractVersion",
|
|
7878
|
+
"actionId",
|
|
7879
|
+
"companyId",
|
|
7880
|
+
"bindingId",
|
|
7881
|
+
"localOpaqueRef",
|
|
7882
|
+
"provider",
|
|
7883
|
+
"origin",
|
|
7884
|
+
"locator",
|
|
7885
|
+
"operation",
|
|
7886
|
+
"previousBinding",
|
|
7887
|
+
"leaseId",
|
|
7888
|
+
"leaseExpiresAt"
|
|
7889
|
+
]);
|
|
7890
|
+
var OPERATIONS = /* @__PURE__ */ new Set(["open", "reopen", "switch_account", "revoke"]);
|
|
7891
|
+
var PROVIDERS = /* @__PURE__ */ new Set([
|
|
7892
|
+
"feishu",
|
|
7893
|
+
"dingtalk",
|
|
7894
|
+
"wechat_work",
|
|
7895
|
+
"notion",
|
|
7896
|
+
"github_gitlab",
|
|
7897
|
+
"crm",
|
|
7898
|
+
"erp",
|
|
7899
|
+
"custom",
|
|
7900
|
+
"other"
|
|
7901
|
+
]);
|
|
7902
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
7903
|
+
var ZERO_UUID = "00000000-0000-4000-8000-000000000000";
|
|
7904
|
+
function isRecord4(value) {
|
|
7905
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7906
|
+
}
|
|
7907
|
+
function identity(value) {
|
|
7908
|
+
return typeof value === "string" && UUID.test(value.trim()) ? value.trim() : null;
|
|
7909
|
+
}
|
|
7910
|
+
function safeTimestamp2(now) {
|
|
7911
|
+
try {
|
|
7912
|
+
const value = now();
|
|
7913
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
7914
|
+
if (Number.isFinite(date.getTime())) return date.toISOString();
|
|
7915
|
+
} catch {
|
|
7916
|
+
}
|
|
7917
|
+
return "1970-01-01T00:00:00.000Z";
|
|
7918
|
+
}
|
|
7919
|
+
function parseLocator(value, exactOrigin = false) {
|
|
7920
|
+
if (typeof value !== "string" || !value.trim() || value.length > 8192) return null;
|
|
7921
|
+
let url;
|
|
7922
|
+
try {
|
|
7923
|
+
url = new URL(value);
|
|
7924
|
+
} catch {
|
|
7925
|
+
return null;
|
|
7926
|
+
}
|
|
7927
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password) return null;
|
|
7928
|
+
if (exactOrigin && (value !== url.origin || url.pathname !== "/" || url.search || url.hash)) {
|
|
7929
|
+
return null;
|
|
7930
|
+
}
|
|
7931
|
+
return value;
|
|
7932
|
+
}
|
|
7933
|
+
function normalizePayload(value) {
|
|
7934
|
+
if (!isRecord4(value) || Object.keys(value).some((field) => !PAYLOAD_FIELDS.has(field)) || value.contractVersion !== BROWSER_SESSION_CONTRACT_VERSION || !identity(value.actionId) || !identity(value.companyId) || !identity(value.bindingId) || typeof value.localOpaqueRef !== "string" || !/^profile_[a-z0-9]{16,64}$/u.test(value.localOpaqueRef) || !PROVIDERS.has(value.provider) || !OPERATIONS.has(value.operation) || typeof value.leaseId !== "string" || !value.leaseId.trim() || value.leaseId.length > 255 || typeof value.leaseExpiresAt !== "string" || !Number.isFinite(Date.parse(value.leaseExpiresAt))) return null;
|
|
7935
|
+
const previousBinding = isRecord4(value.previousBinding) && Object.keys(value.previousBinding).length === 2 && identity(value.previousBinding.bindingId) && /^profile_[a-z0-9]{16,64}$/u.test(value.previousBinding.localOpaqueRef) ? {
|
|
7936
|
+
bindingId: identity(value.previousBinding.bindingId),
|
|
7937
|
+
localOpaqueRef: value.previousBinding.localOpaqueRef
|
|
7938
|
+
} : null;
|
|
7939
|
+
if (value.operation === "switch_account" ? !previousBinding || previousBinding.bindingId === value.bindingId : value.previousBinding !== void 0) return null;
|
|
7940
|
+
const origin = parseLocator(value.origin, true);
|
|
7941
|
+
const locator = parseLocator(value.locator);
|
|
7942
|
+
if (!origin || !locator || new URL(locator).origin !== origin) return null;
|
|
7943
|
+
return {
|
|
7944
|
+
contractVersion: BROWSER_SESSION_CONTRACT_VERSION,
|
|
7945
|
+
actionId: value.actionId,
|
|
7946
|
+
companyId: value.companyId,
|
|
7947
|
+
bindingId: value.bindingId,
|
|
7948
|
+
localOpaqueRef: value.localOpaqueRef,
|
|
7949
|
+
provider: value.provider,
|
|
7950
|
+
origin,
|
|
7951
|
+
locator,
|
|
7952
|
+
operation: value.operation,
|
|
7953
|
+
...previousBinding ? { previousBinding } : {},
|
|
7954
|
+
leaseId: value.leaseId.trim(),
|
|
7955
|
+
leaseExpiresAt: new Date(value.leaseExpiresAt).toISOString()
|
|
7956
|
+
};
|
|
7957
|
+
}
|
|
7958
|
+
function safeContext(command) {
|
|
7959
|
+
const payload = isRecord4(command?.payload) ? command.payload : {};
|
|
7960
|
+
return {
|
|
7961
|
+
actionId: identity(payload.actionId) ?? ZERO_UUID,
|
|
7962
|
+
companyId: identity(payload.companyId) ?? ZERO_UUID,
|
|
7963
|
+
bindingId: identity(payload.bindingId) ?? ZERO_UUID,
|
|
7964
|
+
connectorId: identity(command?.connectorId) ?? ZERO_UUID,
|
|
7965
|
+
commandId: identity(command?.commandId) ?? ZERO_UUID,
|
|
7966
|
+
operation: OPERATIONS.has(payload.operation) ? payload.operation : "open"
|
|
7967
|
+
};
|
|
7968
|
+
}
|
|
7969
|
+
function failureCode2(error) {
|
|
7970
|
+
const code = typeof error?.code === "string" ? error.code : "";
|
|
7971
|
+
if (/^browser_auth_[a-z0-9_]{1,110}$/u.test(code)) return code;
|
|
7972
|
+
if (/^browser_session_[a-z0-9_]{1,102}$/u.test(code)) {
|
|
7973
|
+
return `browser_auth_${code.slice("browser_session_".length)}`;
|
|
7974
|
+
}
|
|
7975
|
+
return "browser_auth_runtime_failed";
|
|
7976
|
+
}
|
|
7977
|
+
function result(context, startedAt, completedAt, outcome, code = null) {
|
|
7978
|
+
return {
|
|
7979
|
+
contractVersion: BROWSER_SESSION_CONTRACT_VERSION,
|
|
7980
|
+
actionId: context.actionId,
|
|
7981
|
+
companyId: context.companyId,
|
|
7982
|
+
bindingId: context.bindingId,
|
|
7983
|
+
connectorId: context.connectorId,
|
|
7984
|
+
commandId: context.commandId,
|
|
7985
|
+
operation: context.operation,
|
|
7986
|
+
outcome,
|
|
7987
|
+
startedAt,
|
|
7988
|
+
completedAt,
|
|
7989
|
+
passiveEffectsPossible: true,
|
|
7990
|
+
failureCode: code
|
|
7991
|
+
};
|
|
7992
|
+
}
|
|
7993
|
+
async function executeBrowserSessionCommand(command, dependencies = {}) {
|
|
7994
|
+
const now = typeof dependencies.now === "function" ? dependencies.now : () => /* @__PURE__ */ new Date();
|
|
7995
|
+
const startedAt = safeTimestamp2(now);
|
|
7996
|
+
const context = safeContext(command);
|
|
7997
|
+
const payload = normalizePayload(command?.payload);
|
|
7998
|
+
if (!payload || !identity(command?.commandId) || !identity(command?.connectorId)) {
|
|
7999
|
+
return result(
|
|
8000
|
+
context,
|
|
8001
|
+
startedAt,
|
|
8002
|
+
safeTimestamp2(now),
|
|
8003
|
+
"failed",
|
|
8004
|
+
"browser_auth_command_invalid"
|
|
8005
|
+
);
|
|
8006
|
+
}
|
|
8007
|
+
const broker = dependencies.broker;
|
|
8008
|
+
const method = payload.operation === "open" ? "openHumanSession" : payload.operation === "reopen" ? "reopenHumanSession" : payload.operation === "switch_account" ? "switchAccount" : "revokeBinding";
|
|
8009
|
+
try {
|
|
8010
|
+
if (typeof broker?.[method] !== "function") {
|
|
8011
|
+
throw Object.assign(new Error("browser_session_runtime_unavailable"), {
|
|
8012
|
+
code: "browser_session_runtime_unavailable"
|
|
8013
|
+
});
|
|
8014
|
+
}
|
|
8015
|
+
await broker[method](payload);
|
|
8016
|
+
return result(
|
|
8017
|
+
context,
|
|
8018
|
+
startedAt,
|
|
8019
|
+
safeTimestamp2(now),
|
|
8020
|
+
payload.operation === "revoke" ? "revoked" : "ready"
|
|
8021
|
+
);
|
|
8022
|
+
} catch (error) {
|
|
8023
|
+
return result(
|
|
8024
|
+
context,
|
|
8025
|
+
startedAt,
|
|
8026
|
+
safeTimestamp2(now),
|
|
8027
|
+
"failed",
|
|
8028
|
+
failureCode2(error)
|
|
8029
|
+
);
|
|
8030
|
+
}
|
|
8031
|
+
}
|
|
8032
|
+
|
|
8033
|
+
// src/amaster-runtime-daemon/public-network-scope.mjs
|
|
8034
|
+
import { lookup as dnsLookup } from "node:dns/promises";
|
|
8035
|
+
import { BlockList, isIP } from "node:net";
|
|
8036
|
+
var blockedV4Addresses = new BlockList();
|
|
8037
|
+
for (const [network, prefix] of [
|
|
8038
|
+
["0.0.0.0", 8],
|
|
8039
|
+
["10.0.0.0", 8],
|
|
8040
|
+
["100.64.0.0", 10],
|
|
8041
|
+
["127.0.0.0", 8],
|
|
8042
|
+
["169.254.0.0", 16],
|
|
8043
|
+
["172.16.0.0", 12],
|
|
8044
|
+
["192.0.0.0", 24],
|
|
8045
|
+
["192.0.2.0", 24],
|
|
8046
|
+
["192.88.99.0", 24],
|
|
8047
|
+
["192.168.0.0", 16],
|
|
8048
|
+
["198.18.0.0", 15],
|
|
8049
|
+
["198.51.100.0", 24],
|
|
8050
|
+
["203.0.113.0", 24],
|
|
8051
|
+
["224.0.0.0", 4],
|
|
8052
|
+
["240.0.0.0", 4]
|
|
8053
|
+
]) {
|
|
8054
|
+
blockedV4Addresses.addSubnet(network, prefix, "ipv4");
|
|
8055
|
+
}
|
|
8056
|
+
var blockedV6Addresses = new BlockList();
|
|
8057
|
+
for (const [network, prefix] of [
|
|
8058
|
+
["::", 128],
|
|
8059
|
+
["::1", 128],
|
|
8060
|
+
["::", 96],
|
|
8061
|
+
["::ffff:0:0", 96],
|
|
8062
|
+
["64:ff9b::", 96],
|
|
8063
|
+
["64:ff9b:1::", 48],
|
|
8064
|
+
["100::", 64],
|
|
8065
|
+
["2001::", 32],
|
|
8066
|
+
["2001:2::", 48],
|
|
8067
|
+
["2001:db8::", 32],
|
|
8068
|
+
["2001:10::", 28],
|
|
8069
|
+
["2001:20::", 28],
|
|
8070
|
+
["2002::", 16],
|
|
8071
|
+
["fc00::", 7],
|
|
8072
|
+
["fe80::", 10],
|
|
8073
|
+
["fec0::", 10],
|
|
8074
|
+
["ff00::", 8]
|
|
8075
|
+
]) {
|
|
8076
|
+
blockedV6Addresses.addSubnet(network, prefix, "ipv6");
|
|
8077
|
+
}
|
|
8078
|
+
function forbidden() {
|
|
8079
|
+
return Object.assign(new Error("source_reader_network_scope_forbidden"), {
|
|
8080
|
+
code: "source_reader_network_scope_forbidden"
|
|
8081
|
+
});
|
|
8082
|
+
}
|
|
8083
|
+
function parsePublicLocator(value) {
|
|
8084
|
+
let url;
|
|
8085
|
+
try {
|
|
8086
|
+
url = new URL(value);
|
|
8087
|
+
} catch {
|
|
8088
|
+
throw forbidden();
|
|
8089
|
+
}
|
|
8090
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !url.hostname) {
|
|
8091
|
+
throw forbidden();
|
|
8092
|
+
}
|
|
8093
|
+
const hostname3 = url.hostname.toLowerCase().replace(/\.$/u, "");
|
|
8094
|
+
if (hostname3 === "localhost" || hostname3.endsWith(".localhost") || hostname3.endsWith(".local")) {
|
|
8095
|
+
throw forbidden();
|
|
8096
|
+
}
|
|
8097
|
+
return { url, hostname: hostname3 };
|
|
8098
|
+
}
|
|
8099
|
+
function addressIsPublic(address) {
|
|
8100
|
+
const family = isIP(address);
|
|
8101
|
+
if (family === 4) return !blockedV4Addresses.check(address, "ipv4");
|
|
8102
|
+
if (family === 6) return !blockedV6Addresses.check(address, "ipv6");
|
|
8103
|
+
return false;
|
|
8104
|
+
}
|
|
8105
|
+
function createPublicNetworkScope(options = {}) {
|
|
8106
|
+
const lookup = options.lookup ?? ((hostname3) => dnsLookup(hostname3, {
|
|
8107
|
+
all: true,
|
|
8108
|
+
verbatim: true
|
|
8109
|
+
}));
|
|
8110
|
+
async function resolveNetworkAddresses(locator) {
|
|
8111
|
+
const { hostname: hostname3 } = parsePublicLocator(locator);
|
|
8112
|
+
let addresses;
|
|
8113
|
+
try {
|
|
8114
|
+
addresses = await lookup(hostname3);
|
|
8115
|
+
} catch {
|
|
8116
|
+
throw forbidden();
|
|
8117
|
+
}
|
|
8118
|
+
if (!Array.isArray(addresses) || addresses.length === 0 || addresses.some((entry) => !entry || typeof entry.address !== "string" || !addressIsPublic(entry.address))) {
|
|
8119
|
+
throw forbidden();
|
|
8120
|
+
}
|
|
8121
|
+
return [...new Set(addresses.map((entry) => entry.address))].sort();
|
|
8122
|
+
}
|
|
8123
|
+
return Object.freeze({
|
|
8124
|
+
resolveNetworkAddresses,
|
|
8125
|
+
async assertNetworkAddressAllowed(locator) {
|
|
8126
|
+
await resolveNetworkAddresses(locator);
|
|
8127
|
+
return true;
|
|
8128
|
+
}
|
|
8129
|
+
});
|
|
8130
|
+
}
|
|
8131
|
+
|
|
6255
8132
|
// src/amaster-runtime-daemon.mjs
|
|
6256
|
-
var CONNECTOR_VERSION = "0.1.0-beta.
|
|
8133
|
+
var CONNECTOR_VERSION = "0.1.0-beta.47";
|
|
6257
8134
|
var CONNECTOR_CONTRACT_VERSION = "2026-06-04.v1";
|
|
6258
8135
|
var MAX_CHECKPOINT_BYTES = 20 * 1024 * 1024;
|
|
6259
8136
|
var CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -6278,6 +8155,56 @@ var piChildIdentityAllocator = null;
|
|
|
6278
8155
|
var piChildIdentityAllocatorKey = null;
|
|
6279
8156
|
var trustedPiRuntimeProvenanceCache = createTrustedPiRuntimeProvenanceCache();
|
|
6280
8157
|
var lastPartialTrustedPiRuntimeSourceWarningKey = null;
|
|
8158
|
+
var browserSessionBroker = null;
|
|
8159
|
+
var browserSessionBrokerKey = null;
|
|
8160
|
+
var playwrightRuntime;
|
|
8161
|
+
function configuredPlaywrightRuntime() {
|
|
8162
|
+
if (playwrightRuntime !== void 0) return playwrightRuntime;
|
|
8163
|
+
try {
|
|
8164
|
+
const loaded = createRequire(import.meta.url)("playwright-core");
|
|
8165
|
+
playwrightRuntime = typeof loaded?.chromium?.launchPersistentContext === "function" ? { chromium: loaded.chromium } : null;
|
|
8166
|
+
} catch {
|
|
8167
|
+
playwrightRuntime = null;
|
|
8168
|
+
}
|
|
8169
|
+
return playwrightRuntime;
|
|
8170
|
+
}
|
|
8171
|
+
function configuredBrowserExecutablePath(config) {
|
|
8172
|
+
const explicit = readString(config.browserExecutablePath);
|
|
8173
|
+
const candidates = [
|
|
8174
|
+
explicit,
|
|
8175
|
+
process.platform === "darwin" ? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" : null,
|
|
8176
|
+
process.platform === "darwin" ? "/Applications/Chromium.app/Contents/MacOS/Chromium" : null,
|
|
8177
|
+
process.platform === "darwin" ? "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" : null,
|
|
8178
|
+
process.platform === "linux" ? "/usr/bin/google-chrome-stable" : null,
|
|
8179
|
+
process.platform === "linux" ? "/usr/bin/google-chrome" : null,
|
|
8180
|
+
process.platform === "linux" ? "/usr/bin/chromium" : null,
|
|
8181
|
+
process.platform === "linux" ? "/usr/bin/chromium-browser" : null,
|
|
8182
|
+
process.platform === "win32" && process.env.PROGRAMFILES ? join14(process.env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : null
|
|
8183
|
+
].filter(Boolean);
|
|
8184
|
+
return candidates.find((candidate) => existsSync13(candidate)) ?? explicit ?? null;
|
|
8185
|
+
}
|
|
8186
|
+
function runtimeBrowserSessionBroker(config) {
|
|
8187
|
+
const executablePath = configuredBrowserExecutablePath(config);
|
|
8188
|
+
const key = `${resolve12(config.browserSessionStateRoot)}:${executablePath ?? "missing"}`;
|
|
8189
|
+
if (!browserSessionBroker || browserSessionBrokerKey !== key) {
|
|
8190
|
+
browserSessionBroker = createBrowserSessionBroker({
|
|
8191
|
+
stateRoot: config.browserSessionStateRoot,
|
|
8192
|
+
playwright: configuredPlaywrightRuntime(),
|
|
8193
|
+
browserExecutablePath: executablePath,
|
|
8194
|
+
publicNetworkScope: createPublicNetworkScope()
|
|
8195
|
+
});
|
|
8196
|
+
browserSessionBrokerKey = key;
|
|
8197
|
+
}
|
|
8198
|
+
return browserSessionBroker;
|
|
8199
|
+
}
|
|
8200
|
+
function browserCapabilityStatus(config, capability) {
|
|
8201
|
+
const advertised = config.capabilities.includes(capability);
|
|
8202
|
+
if (!advertised) {
|
|
8203
|
+
return { advertised, ready: false, reason: "capability_not_advertised" };
|
|
8204
|
+
}
|
|
8205
|
+
const readiness = runtimeBrowserSessionBroker(config).readiness();
|
|
8206
|
+
return { advertised, ...readiness };
|
|
8207
|
+
}
|
|
6281
8208
|
function configuredPiChildIdentityAllocator(config) {
|
|
6282
8209
|
if (!(config.piChildUidBase > 0)) return null;
|
|
6283
8210
|
const key = `${config.piChildUidBase}:${config.piChildUidSpan}`;
|
|
@@ -6301,11 +8228,11 @@ function updateActiveRunWorkspaceManifest(commandId, manifestPath, patch = {}) {
|
|
|
6301
8228
|
}
|
|
6302
8229
|
function resultOutboxPendingCount(config) {
|
|
6303
8230
|
const dir = resultOutboxDir(config);
|
|
6304
|
-
if (!
|
|
8231
|
+
if (!existsSync13(dir)) return 0;
|
|
6305
8232
|
try {
|
|
6306
8233
|
let pending = 0;
|
|
6307
8234
|
for (const file of readdirSync8(dir).filter((name) => name.endsWith(".json"))) {
|
|
6308
|
-
if (readValidResultOutboxEntryOrQuarantine(config, file,
|
|
8235
|
+
if (readValidResultOutboxEntryOrQuarantine(config, file, join14(dir, file))) {
|
|
6309
8236
|
pending += 1;
|
|
6310
8237
|
}
|
|
6311
8238
|
}
|
|
@@ -6317,15 +8244,15 @@ function resultOutboxPendingCount(config) {
|
|
|
6317
8244
|
function piCompletionOutputType(event) {
|
|
6318
8245
|
const type = readString(asRecord(event).type);
|
|
6319
8246
|
if (PI_COMPLETION_OUTPUT_TYPES.has(type ?? "")) return type;
|
|
6320
|
-
return piMcpToolResults(event).some((
|
|
8247
|
+
return piMcpToolResults(event).some((result3) => result3.status === "approval_required") ? "approval_required" : null;
|
|
6321
8248
|
}
|
|
6322
8249
|
function resultOutboxActiveRunCommands(config) {
|
|
6323
8250
|
const dir = resultOutboxDir(config);
|
|
6324
|
-
if (!
|
|
8251
|
+
if (!existsSync13(dir)) return [];
|
|
6325
8252
|
const outboxPending = resultOutboxPendingCount(config);
|
|
6326
8253
|
const entries = [];
|
|
6327
8254
|
for (const file of readdirSync8(dir).filter((name) => name.endsWith(".json")).sort()) {
|
|
6328
|
-
const entry = readValidResultOutboxEntryOrQuarantine(config, file,
|
|
8255
|
+
const entry = readValidResultOutboxEntryOrQuarantine(config, file, join14(dir, file));
|
|
6329
8256
|
if (!entry) continue;
|
|
6330
8257
|
const activeRun = asRecord(entry.activeRun);
|
|
6331
8258
|
const commandId = readString(activeRun.commandId) ?? readString(entry.commandId);
|
|
@@ -6355,12 +8282,12 @@ function resultOutboxActiveRunCommands(config) {
|
|
|
6355
8282
|
}
|
|
6356
8283
|
function resultOutboxFailedRunCommands(config) {
|
|
6357
8284
|
const dir = resultOutboxInvalidDir(config);
|
|
6358
|
-
if (!
|
|
8285
|
+
if (!existsSync13(dir)) return [];
|
|
6359
8286
|
const entries = [];
|
|
6360
8287
|
for (const file of readdirSync8(dir).filter((name) => name.endsWith(".json")).sort().slice(-100)) {
|
|
6361
8288
|
let entry;
|
|
6362
8289
|
try {
|
|
6363
|
-
entry = asRecord(JSON.parse(readFileSync10(
|
|
8290
|
+
entry = asRecord(JSON.parse(readFileSync10(join14(dir, file), "utf8")));
|
|
6364
8291
|
} catch {
|
|
6365
8292
|
continue;
|
|
6366
8293
|
}
|
|
@@ -6420,7 +8347,7 @@ function piExtraArgsDiagnostics(value) {
|
|
|
6420
8347
|
}
|
|
6421
8348
|
function safeExpandPath(value) {
|
|
6422
8349
|
const text = readString(value);
|
|
6423
|
-
return text ?
|
|
8350
|
+
return text ? resolve12(expandHomePath(text)) : null;
|
|
6424
8351
|
}
|
|
6425
8352
|
function safeJsonObjectFromFile(filePath) {
|
|
6426
8353
|
try {
|
|
@@ -6467,10 +8394,10 @@ function safeSkillRootSummary(kind, source, pathValue) {
|
|
|
6467
8394
|
try {
|
|
6468
8395
|
for (const name of readdirSync8(pathValue)) {
|
|
6469
8396
|
if (name.startsWith(".")) continue;
|
|
6470
|
-
const skillDir =
|
|
8397
|
+
const skillDir = join14(pathValue, name);
|
|
6471
8398
|
try {
|
|
6472
8399
|
if (!statSync7(skillDir).isDirectory()) continue;
|
|
6473
|
-
if (!
|
|
8400
|
+
if (!existsSync13(join14(skillDir, "SKILL.md"))) continue;
|
|
6474
8401
|
skillCount += 1;
|
|
6475
8402
|
if (skillCount >= MAX_PI_CAPABILITY_SOURCE_ENTRIES) {
|
|
6476
8403
|
truncated = true;
|
|
@@ -6521,7 +8448,7 @@ function objectKeyCount(value) {
|
|
|
6521
8448
|
return value && typeof value === "object" && !Array.isArray(value) ? Object.keys(value).length : 0;
|
|
6522
8449
|
}
|
|
6523
8450
|
function defaultPiCodingAgentDir() {
|
|
6524
|
-
return
|
|
8451
|
+
return join14(homedir3(), ".pi", "agent");
|
|
6525
8452
|
}
|
|
6526
8453
|
function piCapabilitySourcesDiagnostics() {
|
|
6527
8454
|
const configuredPiCodingAgentDir = safeExpandPath(process.env.PI_CODING_AGENT_DIR);
|
|
@@ -6530,14 +8457,14 @@ function piCapabilitySourcesDiagnostics() {
|
|
|
6530
8457
|
const configuredPiAgentHome = safeExpandPath(process.env.PI_AGENT_HOME);
|
|
6531
8458
|
const piAgentHome = configuredPiAgentHome ?? piCodingAgentDir;
|
|
6532
8459
|
const piAgentHomeSource = configuredPiAgentHome ? "PI_AGENT_HOME" : piCodingAgentDirSource;
|
|
6533
|
-
const userSkillsPath = piAgentHome ?
|
|
8460
|
+
const userSkillsPath = piAgentHome ? join14(piAgentHome, "skills") : null;
|
|
6534
8461
|
const configuredMarketplaceSkillsPath = safeExpandPath(
|
|
6535
8462
|
process.env.PI_AGENT_MARKETPLACE_SKILLS_DIR
|
|
6536
8463
|
);
|
|
6537
|
-
const marketplaceSkillsPath = configuredMarketplaceSkillsPath ?? (piAgentHome ?
|
|
8464
|
+
const marketplaceSkillsPath = configuredMarketplaceSkillsPath ?? (piAgentHome ? join14(piAgentHome, "marketplace", "skills") : null);
|
|
6538
8465
|
const builtinSkillsPath = safeExpandPath(process.env.PI_AGENT_BUILTIN_SKILLS_DIR) ?? safeExpandPath(process.env.AMASTER_BUILTIN_SKILLS);
|
|
6539
|
-
const mcpConfigPath = safeExpandPath(process.env.PI_AGENT_MCP_SERVERS_FILE) ?? (piAgentHome ?
|
|
6540
|
-
const settingsConfigPath = piCodingAgentDir ?
|
|
8466
|
+
const mcpConfigPath = safeExpandPath(process.env.PI_AGENT_MCP_SERVERS_FILE) ?? (piAgentHome ? join14(piAgentHome, "mcp.json") : null);
|
|
8467
|
+
const settingsConfigPath = piCodingAgentDir ? join14(piCodingAgentDir, "settings.json") : null;
|
|
6541
8468
|
const skillRoots = [
|
|
6542
8469
|
safeSkillRootSummary("user", `${piAgentHomeSource}/skills`, userSkillsPath),
|
|
6543
8470
|
safeSkillRootSummary(
|
|
@@ -6731,6 +8658,8 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
6731
8658
|
const activeRunCommandList = Array.from(activeRunCommandById.values()).map((entry) => buildActiveRunCommandStatus(config, entry));
|
|
6732
8659
|
const executorReadiness = buildExecutorReadiness(config);
|
|
6733
8660
|
const platformCredential = piAgentLocalPlatformCredentialStatus(config);
|
|
8661
|
+
const headedBrowserAuth = browserCapabilityStatus(config, "headed_browser_auth");
|
|
8662
|
+
const constrainedSourceRead = browserCapabilityStatus(config, "constrained_source_read");
|
|
6734
8663
|
return {
|
|
6735
8664
|
status: "online",
|
|
6736
8665
|
workspaceBindings: config.workspaceBindings,
|
|
@@ -6757,6 +8686,14 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
6757
8686
|
stateFile: stateFilePath(process.env),
|
|
6758
8687
|
...executorReadiness.length > 0 ? { executorReadiness } : {},
|
|
6759
8688
|
...platformCredential ? { platformCredential } : {},
|
|
8689
|
+
constrainedSourceReader: {
|
|
8690
|
+
capability: "constrained_source_read",
|
|
8691
|
+
...constrainedSourceRead
|
|
8692
|
+
},
|
|
8693
|
+
browserSessionBroker: {
|
|
8694
|
+
capability: "headed_browser_auth",
|
|
8695
|
+
...headedBrowserAuth
|
|
8696
|
+
},
|
|
6760
8697
|
...versionDrift.versionDrift || versionDrift.bundleDrift ? versionDrift : {}
|
|
6761
8698
|
},
|
|
6762
8699
|
metadata: {
|
|
@@ -6770,13 +8707,13 @@ function piAgentLocalPlatformRunnerEnabled(config) {
|
|
|
6770
8707
|
}
|
|
6771
8708
|
function piAgentSystemDataDir(config) {
|
|
6772
8709
|
const configured = readString(config.AMASTER_PI_AGENT_SYSTEM_DATA_DIR ?? process.env.AMASTER_PI_AGENT_SYSTEM_DATA_DIR);
|
|
6773
|
-
return configured ?
|
|
8710
|
+
return configured ? resolve12(expandHomePath(configured)) : null;
|
|
6774
8711
|
}
|
|
6775
8712
|
function readPiAgentLocalPlatformCredential(credentialsDir) {
|
|
6776
|
-
const pointer = readJsonFile3(
|
|
8713
|
+
const pointer = readJsonFile3(join14(credentialsDir, "latest.json"));
|
|
6777
8714
|
const credentialRef = readString(pointer.credentialRef);
|
|
6778
8715
|
if (!credentialRef || !/^[a-f0-9]{64}$/i.test(credentialRef)) return null;
|
|
6779
|
-
const credential = readJsonFile3(
|
|
8716
|
+
const credential = readJsonFile3(join14(credentialsDir, `${credentialRef}.json`));
|
|
6780
8717
|
const organizationId = readString(credential.organizationId);
|
|
6781
8718
|
const apiKey = readString(credential.apiKey);
|
|
6782
8719
|
if (credential.version !== 1 || !organizationId || !apiKey) return null;
|
|
@@ -6792,7 +8729,7 @@ function piAgentLocalPlatformCredentials(config) {
|
|
|
6792
8729
|
if (!piAgentLocalPlatformRunnerEnabled(config)) return [];
|
|
6793
8730
|
const systemDataDir = piAgentSystemDataDir(config);
|
|
6794
8731
|
if (!systemDataDir) return [];
|
|
6795
|
-
const companiesDir =
|
|
8732
|
+
const companiesDir = join14(systemDataDir, "companies");
|
|
6796
8733
|
let entries = [];
|
|
6797
8734
|
try {
|
|
6798
8735
|
entries = readdirSync8(companiesDir, { withFileTypes: true });
|
|
@@ -6802,7 +8739,7 @@ function piAgentLocalPlatformCredentials(config) {
|
|
|
6802
8739
|
const credentialsByOrganizationId = /* @__PURE__ */ new Map();
|
|
6803
8740
|
for (const entry of entries) {
|
|
6804
8741
|
if (!entry.isDirectory()) continue;
|
|
6805
|
-
const credential = readPiAgentLocalPlatformCredential(
|
|
8742
|
+
const credential = readPiAgentLocalPlatformCredential(join14(companiesDir, entry.name, "model-credentials"));
|
|
6806
8743
|
if (credential) credentialsByOrganizationId.set(credential.organizationId, credential);
|
|
6807
8744
|
}
|
|
6808
8745
|
return [...credentialsByOrganizationId.values()];
|
|
@@ -6976,7 +8913,7 @@ AMASTER_WORKSPACE_ALLOWLIST=${quoteShell(config.workspaceBindings.join(","))}
|
|
|
6976
8913
|
AMASTER_EXECUTORS=${quoteShell(executorEnv)}
|
|
6977
8914
|
AMASTER_CAPABILITIES=${quoteShell(config.capabilities.join(","))}
|
|
6978
8915
|
AMASTER_NETWORK_DOMAINS=${quoteShell(config.networkDomains.join(","))}
|
|
6979
|
-
AMASTER_DAEMON_STATE_FILE=${quoteShell(
|
|
8916
|
+
AMASTER_DAEMON_STATE_FILE=${quoteShell(join14(homedir3(), ".amaster-employee", "runtime-connector-state.json"))}
|
|
6980
8917
|
EOF
|
|
6981
8918
|
|
|
6982
8919
|
set -a
|
|
@@ -7164,10 +9101,10 @@ function buildActiveRunCommandStatus(config, entry) {
|
|
|
7164
9101
|
const base = {
|
|
7165
9102
|
...entry,
|
|
7166
9103
|
phase: readString(entry.phase) ?? "executing",
|
|
7167
|
-
managedWorkdirPresent: entry.workspacePath ?
|
|
9104
|
+
managedWorkdirPresent: entry.workspacePath ? existsSync13(entry.workspacePath) : false,
|
|
7168
9105
|
outboxPending: resultOutboxPendingCount(config)
|
|
7169
9106
|
};
|
|
7170
|
-
if (!entry.workspacePath || !
|
|
9107
|
+
if (!entry.workspacePath || !existsSync13(entry.workspacePath)) return base;
|
|
7171
9108
|
const manifestPath = entry.manifestPath ?? workspaceManifestPath(entry.workspacePath);
|
|
7172
9109
|
const status = readWorkspaceStatus(entry.workspacePath, { hashCache: workspaceStatusHashCache });
|
|
7173
9110
|
const artifactCandidates = status.artifacts.slice(0, 20);
|
|
@@ -7421,7 +9358,7 @@ function safeAgentInstructionMaterializationTarget(workspace, filePath) {
|
|
|
7421
9358
|
const segments = normalized.split("/").filter(Boolean);
|
|
7422
9359
|
if (segments.length === 0 || segments.some((segment) => segment === "." || segment === "..")) return null;
|
|
7423
9360
|
const relativePath = segments.join("/");
|
|
7424
|
-
const targetPath =
|
|
9361
|
+
const targetPath = resolve12(workspace.cwd, relativePath);
|
|
7425
9362
|
if (!pathWithin2(targetPath, workspace.cwd)) return null;
|
|
7426
9363
|
return { relativePath, targetPath };
|
|
7427
9364
|
}
|
|
@@ -7493,6 +9430,8 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
7493
9430
|
sourceWorkspacePath: workspaceContext.sourceWorkspacePath,
|
|
7494
9431
|
wakeReason: readString(context.wakeReason),
|
|
7495
9432
|
hasGovernedMcp,
|
|
9433
|
+
executorKind: options.executorKind,
|
|
9434
|
+
managedMcpToolMode: options.managedMcpToolMode,
|
|
7496
9435
|
agentInstructions,
|
|
7497
9436
|
taskMarkdown,
|
|
7498
9437
|
attachmentsText,
|
|
@@ -7503,16 +9442,16 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
7503
9442
|
}
|
|
7504
9443
|
function companyPiHomeRoot(baseEnv) {
|
|
7505
9444
|
const explicitRoot = readString(baseEnv.AMASTER_COMPANY_PI_HOME_ROOT);
|
|
7506
|
-
if (explicitRoot) return
|
|
9445
|
+
if (explicitRoot) return resolve12(expandHomePath(explicitRoot));
|
|
7507
9446
|
const configuredPiHome = readString(baseEnv.PI_AGENT_HOME) ?? readString(baseEnv.PI_CODING_AGENT_DIR);
|
|
7508
|
-
if (configuredPiHome) return
|
|
7509
|
-
return
|
|
9447
|
+
if (configuredPiHome) return join14(dirname9(resolve12(expandHomePath(configuredPiHome))), "companies");
|
|
9448
|
+
return join14(homedir3(), ".amaster-employee", "companies");
|
|
7510
9449
|
}
|
|
7511
9450
|
function companyPiAgentHome(baseEnv, companyId) {
|
|
7512
9451
|
const rawCompanyId = readString(companyId);
|
|
7513
9452
|
if (!rawCompanyId) return null;
|
|
7514
9453
|
const segment = safeCompanyPiHomeSegment(rawCompanyId);
|
|
7515
|
-
return
|
|
9454
|
+
return join14(companyPiHomeRoot(baseEnv), segment, ".pi");
|
|
7516
9455
|
}
|
|
7517
9456
|
function commandUsesPiExecutor(command) {
|
|
7518
9457
|
return readString(asRecord(command.payload).executorKind) === "pi";
|
|
@@ -7579,9 +9518,9 @@ function resolveNativeSessionRequest(command, workspace) {
|
|
|
7579
9518
|
let cwdMatched = false;
|
|
7580
9519
|
if (requestedCwd && sourceWorkspacePath) {
|
|
7581
9520
|
try {
|
|
7582
|
-
cwdMatched =
|
|
9521
|
+
cwdMatched = realpathSync4(requestedCwd) === realpathSync4(sourceWorkspacePath);
|
|
7583
9522
|
} catch {
|
|
7584
|
-
cwdMatched =
|
|
9523
|
+
cwdMatched = resolve12(requestedCwd) === resolve12(sourceWorkspacePath);
|
|
7585
9524
|
}
|
|
7586
9525
|
}
|
|
7587
9526
|
const used = Boolean(enabled && requested && sessionId && requestedCwd && cwdMatched);
|
|
@@ -8003,13 +9942,13 @@ function sampleProcessGroupRssBytes(processGroupId) {
|
|
|
8003
9942
|
if (process.platform === "win32" || processGroupId === null) return null;
|
|
8004
9943
|
const now = Date.now();
|
|
8005
9944
|
if (!processGroupRssSampleCache || now - processGroupRssSampleCache.sampledAtMs >= PROCESS_GROUP_RSS_SAMPLE_MIN_INTERVAL_MS) {
|
|
8006
|
-
const
|
|
9945
|
+
const result3 = spawnSync6("ps", ["-axo", "pgid=,rss="], {
|
|
8007
9946
|
encoding: "utf8",
|
|
8008
9947
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8009
9948
|
});
|
|
8010
|
-
if (
|
|
9949
|
+
if (result3.status !== 0) return null;
|
|
8011
9950
|
const rssKbByProcessGroup = /* @__PURE__ */ new Map();
|
|
8012
|
-
for (const line of String(
|
|
9951
|
+
for (const line of String(result3.stdout ?? "").split(/\r?\n/)) {
|
|
8013
9952
|
const match = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
8014
9953
|
if (!match) continue;
|
|
8015
9954
|
const pgid = Number.parseInt(match[1], 10);
|
|
@@ -8024,27 +9963,27 @@ function sampleProcessGroupRssBytes(processGroupId) {
|
|
|
8024
9963
|
}
|
|
8025
9964
|
function realOrResolvedPath(value) {
|
|
8026
9965
|
try {
|
|
8027
|
-
return
|
|
9966
|
+
return realpathSync4(value);
|
|
8028
9967
|
} catch {
|
|
8029
|
-
return
|
|
9968
|
+
return resolve12(value);
|
|
8030
9969
|
}
|
|
8031
9970
|
}
|
|
8032
|
-
var LSOF_COMMAND = process.platform === "darwin" &&
|
|
9971
|
+
var LSOF_COMMAND = process.platform === "darwin" && existsSync13("/usr/sbin/lsof") ? "/usr/sbin/lsof" : "lsof";
|
|
8033
9972
|
function processCwdForPid(pid) {
|
|
8034
9973
|
if (process.platform === "linux") {
|
|
8035
9974
|
try {
|
|
8036
|
-
return
|
|
9975
|
+
return realpathSync4(`/proc/${pid}/cwd`);
|
|
8037
9976
|
} catch {
|
|
8038
9977
|
return null;
|
|
8039
9978
|
}
|
|
8040
9979
|
}
|
|
8041
9980
|
if (process.platform === "darwin") {
|
|
8042
|
-
const
|
|
9981
|
+
const result3 = spawnSync6(LSOF_COMMAND, ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
|
|
8043
9982
|
encoding: "utf8",
|
|
8044
9983
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8045
9984
|
});
|
|
8046
|
-
if (
|
|
8047
|
-
const cwdLine = String(
|
|
9985
|
+
if (result3.status !== 0) return null;
|
|
9986
|
+
const cwdLine = String(result3.stdout ?? "").split(/\r?\n/).find((line) => line.startsWith("n"));
|
|
8048
9987
|
const cwd = cwdLine?.slice(1);
|
|
8049
9988
|
return cwd ? realOrResolvedPath(cwd) : null;
|
|
8050
9989
|
}
|
|
@@ -8069,13 +10008,13 @@ function allProcessCwdsByPid() {
|
|
|
8069
10008
|
return cwds;
|
|
8070
10009
|
}
|
|
8071
10010
|
if (process.platform === "darwin") {
|
|
8072
|
-
const
|
|
10011
|
+
const result3 = spawnSync6(LSOF_COMMAND, ["-nP", "-d", "cwd", "-Fp", "-Fn"], {
|
|
8073
10012
|
encoding: "utf8",
|
|
8074
10013
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8075
10014
|
});
|
|
8076
|
-
if (
|
|
10015
|
+
if (result3.status !== 0) return cwds;
|
|
8077
10016
|
let currentPid = null;
|
|
8078
|
-
for (const line of String(
|
|
10017
|
+
for (const line of String(result3.stdout ?? "").split(/\r?\n/)) {
|
|
8079
10018
|
if (line.startsWith("p")) {
|
|
8080
10019
|
const parsed = Number.parseInt(line.slice(1), 10);
|
|
8081
10020
|
currentPid = Number.isFinite(parsed) ? parsed : null;
|
|
@@ -8090,13 +10029,13 @@ function allProcessCwdsByPid() {
|
|
|
8090
10029
|
}
|
|
8091
10030
|
function allProcessRows() {
|
|
8092
10031
|
if (process.platform === "win32") return [];
|
|
8093
|
-
const
|
|
10032
|
+
const result3 = spawnSync6("ps", ["-axo", "pid=,pgid=,command="], {
|
|
8094
10033
|
encoding: "utf8",
|
|
8095
10034
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8096
10035
|
});
|
|
8097
|
-
if (
|
|
10036
|
+
if (result3.status !== 0) return [];
|
|
8098
10037
|
const rows = [];
|
|
8099
|
-
for (const line of String(
|
|
10038
|
+
for (const line of String(result3.stdout ?? "").split(/\r?\n/)) {
|
|
8100
10039
|
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
8101
10040
|
if (!match) continue;
|
|
8102
10041
|
const pid = Number.parseInt(match[1], 10);
|
|
@@ -8151,7 +10090,7 @@ function killWorkspaceResidentProcesses(cwd, processGroupId, options = {}) {
|
|
|
8151
10090
|
}
|
|
8152
10091
|
function walkManagedWorkdirs(root) {
|
|
8153
10092
|
const workdirs = [];
|
|
8154
|
-
if (!root || !
|
|
10093
|
+
if (!root || !existsSync13(root)) return workdirs;
|
|
8155
10094
|
const stack = [root];
|
|
8156
10095
|
while (stack.length > 0) {
|
|
8157
10096
|
const current = stack.pop();
|
|
@@ -8164,8 +10103,8 @@ function walkManagedWorkdirs(root) {
|
|
|
8164
10103
|
}
|
|
8165
10104
|
for (const entry of entries) {
|
|
8166
10105
|
if (!entry.isDirectory()) continue;
|
|
8167
|
-
const fullPath =
|
|
8168
|
-
if (entry.name === "workdir" &&
|
|
10106
|
+
const fullPath = join14(current, entry.name);
|
|
10107
|
+
if (entry.name === "workdir" && existsSync13(workspaceManifestPath(fullPath))) {
|
|
8169
10108
|
workdirs.push(fullPath);
|
|
8170
10109
|
continue;
|
|
8171
10110
|
}
|
|
@@ -8203,7 +10142,7 @@ function manifestMatchesActiveRuntimeRef(manifest, refs, workdir = null) {
|
|
|
8203
10142
|
}
|
|
8204
10143
|
function buildOrphanReaperSample(root, workdir, manifest, residents) {
|
|
8205
10144
|
const relativeWorkdir = (() => {
|
|
8206
|
-
const value =
|
|
10145
|
+
const value = relative9(root, workdir);
|
|
8207
10146
|
return value && !value.startsWith("..") && !isAbsolute8(value) ? value : basename6(workdir);
|
|
8208
10147
|
})();
|
|
8209
10148
|
return {
|
|
@@ -8316,7 +10255,7 @@ function runExecutor(command, args, options) {
|
|
|
8316
10255
|
...spawnInvocation.spawnIdentity
|
|
8317
10256
|
});
|
|
8318
10257
|
const processGroupId = processGroupIdForChild(child);
|
|
8319
|
-
const finish = (
|
|
10258
|
+
const finish = (result3) => {
|
|
8320
10259
|
if (settled) return;
|
|
8321
10260
|
settled = true;
|
|
8322
10261
|
if (timer) clearTimeout(timer);
|
|
@@ -8335,7 +10274,7 @@ function runExecutor(command, args, options) {
|
|
|
8335
10274
|
completionOutputType,
|
|
8336
10275
|
killedWorkspaceResidents,
|
|
8337
10276
|
...outputDrainForcedClosed ? { outputDrainForcedClosed: true } : {},
|
|
8338
|
-
...
|
|
10277
|
+
...result3
|
|
8339
10278
|
});
|
|
8340
10279
|
};
|
|
8341
10280
|
const scheduleStopKill = () => {
|
|
@@ -8676,12 +10615,12 @@ function resultOutboxActiveRunSnapshot(command) {
|
|
|
8676
10615
|
...readString(current.childExitSignal) ? { childExitSignal: readString(current.childExitSignal) } : {}
|
|
8677
10616
|
};
|
|
8678
10617
|
}
|
|
8679
|
-
async function completeCommand(config, command, status,
|
|
10618
|
+
async function completeCommand(config, command, status, result3, error) {
|
|
8680
10619
|
const connectorId = requireConnectorId(config);
|
|
8681
10620
|
const payload = {
|
|
8682
10621
|
leaseId: command.leaseId,
|
|
8683
10622
|
status,
|
|
8684
|
-
result:
|
|
10623
|
+
result: result3,
|
|
8685
10624
|
...error ? { error: truncateText(error, 4e3) } : {}
|
|
8686
10625
|
};
|
|
8687
10626
|
const path = `/api/amaster/runtime-connectors/${connectorId}/commands/${command.commandId}/result`;
|
|
@@ -8703,11 +10642,11 @@ async function completeCommand(config, command, status, result2, error) {
|
|
|
8703
10642
|
}
|
|
8704
10643
|
function resultOutboxDir(config) {
|
|
8705
10644
|
const explicit = readString(process.env.AMASTER_RESULT_OUTBOX_DIR);
|
|
8706
|
-
if (explicit) return
|
|
8707
|
-
return
|
|
10645
|
+
if (explicit) return resolve12(expandHomePath(explicit));
|
|
10646
|
+
return join14(dirname9(stateFilePath(process.env)), "result-outbox");
|
|
8708
10647
|
}
|
|
8709
10648
|
function resultOutboxInvalidDir(config) {
|
|
8710
|
-
return
|
|
10649
|
+
return join14(resultOutboxDir(config), "invalid");
|
|
8711
10650
|
}
|
|
8712
10651
|
function writeResultOutboxEntry(config, entry) {
|
|
8713
10652
|
const dir = resultOutboxDir(config);
|
|
@@ -8719,13 +10658,13 @@ function writeResultOutboxEntry(config, entry) {
|
|
|
8719
10658
|
lastAttemptAt: null,
|
|
8720
10659
|
...entry
|
|
8721
10660
|
};
|
|
8722
|
-
writeFileSync8(
|
|
10661
|
+
writeFileSync8(join14(dir, resultOutboxFileName(entry.commandId)), `${JSON.stringify(body, null, 2)}
|
|
8723
10662
|
`, { mode: 384 });
|
|
8724
10663
|
}
|
|
8725
10664
|
function moveResultOutboxEntryToInvalid(config, file, fullPath, reason, detail, original) {
|
|
8726
10665
|
const invalidDir = resultOutboxInvalidDir(config);
|
|
8727
10666
|
mkdirSync8(invalidDir, { recursive: true });
|
|
8728
|
-
const invalidPath =
|
|
10667
|
+
const invalidPath = join14(invalidDir, file);
|
|
8729
10668
|
if (original === void 0) {
|
|
8730
10669
|
try {
|
|
8731
10670
|
renameSync5(fullPath, invalidPath);
|
|
@@ -8792,7 +10731,7 @@ function quarantineResultOutboxEntry(config, fullPath, file, entry, reason, err)
|
|
|
8792
10731
|
...typeof err?.httpStatus === "number" ? { httpStatus: err.httpStatus } : {},
|
|
8793
10732
|
lastError: truncateText(err instanceof Error ? err.message : String(err), 1e3)
|
|
8794
10733
|
};
|
|
8795
|
-
const invalidPath =
|
|
10734
|
+
const invalidPath = join14(invalidDir, file);
|
|
8796
10735
|
writeFileSync8(invalidPath, `${JSON.stringify(body, null, 2)}
|
|
8797
10736
|
`, { mode: 384 });
|
|
8798
10737
|
try {
|
|
@@ -8806,11 +10745,11 @@ function quarantineResultOutboxEntry(config, fullPath, file, entry, reason, err)
|
|
|
8806
10745
|
}
|
|
8807
10746
|
async function flushResultOutbox(config) {
|
|
8808
10747
|
const dir = resultOutboxDir(config);
|
|
8809
|
-
if (!
|
|
10748
|
+
if (!existsSync13(dir)) return { attempted: 0, completed: 0 };
|
|
8810
10749
|
const files = readdirSync8(dir).filter((name) => name.endsWith(".json")).sort();
|
|
8811
10750
|
let completed = 0;
|
|
8812
10751
|
for (const file of files) {
|
|
8813
|
-
const fullPath =
|
|
10752
|
+
const fullPath = join14(dir, file);
|
|
8814
10753
|
const entry = readValidResultOutboxEntryOrQuarantine(config, file, fullPath);
|
|
8815
10754
|
if (!entry) continue;
|
|
8816
10755
|
try {
|
|
@@ -9007,7 +10946,7 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
9007
10946
|
runtimeAuth,
|
|
9008
10947
|
issueId
|
|
9009
10948
|
);
|
|
9010
|
-
const targetDir =
|
|
10949
|
+
const targetDir = join14(workspace.cwd, "input-attachments");
|
|
9011
10950
|
mkdirSync8(targetDir, { recursive: true });
|
|
9012
10951
|
const usedFilenames = /* @__PURE__ */ new Set();
|
|
9013
10952
|
const materialized = [];
|
|
@@ -9022,11 +10961,11 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
9022
10961
|
filename = `${stem}-${index + 1}${ext}`;
|
|
9023
10962
|
}
|
|
9024
10963
|
usedFilenames.add(filename);
|
|
9025
|
-
const targetPath =
|
|
10964
|
+
const targetPath = join14(targetDir, filename);
|
|
9026
10965
|
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
9027
10966
|
writeFileSync8(targetPath, body);
|
|
9028
10967
|
const attachmentId = readString(attachment.id);
|
|
9029
|
-
const actualSha256 =
|
|
10968
|
+
const actualSha256 = createHash12("sha256").update(body).digest("hex");
|
|
9030
10969
|
const lineageCandidates = lineageCandidatesByAttachmentId.get(attachmentId) ?? [];
|
|
9031
10970
|
const lineage = selectAttachmentLineage(lineageCandidates, actualSha256);
|
|
9032
10971
|
if (lineageCandidates.length > 0 && !lineage) {
|
|
@@ -9049,7 +10988,7 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
9049
10988
|
id: attachmentId,
|
|
9050
10989
|
name: readString(attachment.originalFilename) ?? filename,
|
|
9051
10990
|
path: targetPath,
|
|
9052
|
-
relativePath:
|
|
10991
|
+
relativePath: relative9(workspace.cwd, targetPath),
|
|
9053
10992
|
contentType: readString(attachment.contentType),
|
|
9054
10993
|
byteSize: body.byteLength,
|
|
9055
10994
|
contentPath,
|
|
@@ -9098,7 +11037,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9098
11037
|
if (manifest.version !== 1 || !Array.isArray(manifest.entries)) {
|
|
9099
11038
|
throw new Error("artifact_input_manifest_invalid: expected version 1 entries array");
|
|
9100
11039
|
}
|
|
9101
|
-
const targetRoot =
|
|
11040
|
+
const targetRoot = join14(workspace.cwd, "input-artifacts");
|
|
9102
11041
|
rmSync7(targetRoot, { recursive: true, force: true });
|
|
9103
11042
|
mkdirSync8(targetRoot, { recursive: true });
|
|
9104
11043
|
const usedPaths = /* @__PURE__ */ new Set();
|
|
@@ -9107,10 +11046,10 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9107
11046
|
const entry = asRecord(rawEntry);
|
|
9108
11047
|
const workProductId = readString(entry.workProductId);
|
|
9109
11048
|
const attachmentId = readString(entry.attachmentId);
|
|
9110
|
-
const
|
|
11049
|
+
const sha2563 = readString(entry.sha256);
|
|
9111
11050
|
const contentPath = readString(entry.contentPath);
|
|
9112
11051
|
const byteSize = readNumber(entry.byteSize, null);
|
|
9113
|
-
if (!workProductId || !attachmentId || !/^[a-f0-9]{64}$/i.test(
|
|
11052
|
+
if (!workProductId || !attachmentId || !/^[a-f0-9]{64}$/i.test(sha2563 ?? "") || !contentPath || byteSize === null) {
|
|
9114
11053
|
throw new Error(`artifact_input_manifest_invalid: entry ${index} is incomplete`);
|
|
9115
11054
|
}
|
|
9116
11055
|
const expectedContentPath = `/api/attachments/${attachmentId}/content`;
|
|
@@ -9118,10 +11057,10 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9118
11057
|
throw new Error(`artifact_input_manifest_invalid: entry ${index} contentPath does not match attachmentId`);
|
|
9119
11058
|
}
|
|
9120
11059
|
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
9121
|
-
const actualSha256 =
|
|
9122
|
-
if (body.byteLength !== byteSize || actualSha256 !==
|
|
11060
|
+
const actualSha256 = createHash12("sha256").update(body).digest("hex");
|
|
11061
|
+
if (body.byteLength !== byteSize || actualSha256 !== sha2563) {
|
|
9123
11062
|
throw new Error(
|
|
9124
|
-
`artifact_input_integrity_mismatch: workProductId=${workProductId} expectedBytes=${byteSize} actualBytes=${body.byteLength} expectedSha256=${
|
|
11063
|
+
`artifact_input_integrity_mismatch: workProductId=${workProductId} expectedBytes=${byteSize} actualBytes=${body.byteLength} expectedSha256=${sha2563} actualSha256=${actualSha256}`
|
|
9125
11064
|
);
|
|
9126
11065
|
}
|
|
9127
11066
|
const sourceDir = safeArtifactInputSourceDir(entry, index);
|
|
@@ -9129,15 +11068,15 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9129
11068
|
id: attachmentId,
|
|
9130
11069
|
originalFilename: readString(entry.originalFilename)
|
|
9131
11070
|
}, index);
|
|
9132
|
-
let relativePath =
|
|
11071
|
+
let relativePath = join14("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
9133
11072
|
if (usedPaths.has(relativePath)) {
|
|
9134
11073
|
const ext = extname2(filename);
|
|
9135
11074
|
const stem = ext ? filename.slice(0, -ext.length) : filename;
|
|
9136
11075
|
filename = `${stem}-${index + 1}${ext}`;
|
|
9137
|
-
relativePath =
|
|
11076
|
+
relativePath = join14("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
9138
11077
|
}
|
|
9139
11078
|
usedPaths.add(relativePath);
|
|
9140
|
-
const targetPath =
|
|
11079
|
+
const targetPath = join14(workspace.cwd, relativePath);
|
|
9141
11080
|
mkdirSync8(dirname9(targetPath), { recursive: true });
|
|
9142
11081
|
writeFileSync8(targetPath, body);
|
|
9143
11082
|
chmodSync6(targetPath, 292);
|
|
@@ -9151,7 +11090,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9151
11090
|
relativePath,
|
|
9152
11091
|
contentType: readString(entry.contentType),
|
|
9153
11092
|
byteSize: body.byteLength,
|
|
9154
|
-
sha256,
|
|
11093
|
+
sha256: sha2563,
|
|
9155
11094
|
contentPath
|
|
9156
11095
|
});
|
|
9157
11096
|
}
|
|
@@ -9159,7 +11098,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9159
11098
|
version: 1,
|
|
9160
11099
|
entries: materialized.map(({ path: _path, relativePath, ...entry }) => ({ ...entry, path: relativePath }))
|
|
9161
11100
|
};
|
|
9162
|
-
const manifestPath =
|
|
11101
|
+
const manifestPath = join14(targetRoot, "artifact-input-manifest.json");
|
|
9163
11102
|
writeFileSync8(manifestPath, JSON.stringify(localManifest, null, 2) + "\n");
|
|
9164
11103
|
chmodSync6(manifestPath, 292);
|
|
9165
11104
|
updateWorkspaceManifest(workspaceManifestPath(workspace), { artifactInputManifest: localManifest });
|
|
@@ -9170,11 +11109,11 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9170
11109
|
return materialized;
|
|
9171
11110
|
}
|
|
9172
11111
|
function issueCheckpointDir(workspace) {
|
|
9173
|
-
return
|
|
11112
|
+
return join14(dirname9(workspace.runDir), "checkpoint");
|
|
9174
11113
|
}
|
|
9175
11114
|
async function clearIssueCheckpoint(config, command, workspace, reason) {
|
|
9176
11115
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
9177
|
-
if (!
|
|
11116
|
+
if (!existsSync13(checkpointDir)) return false;
|
|
9178
11117
|
rmSync7(checkpointDir, { recursive: true, force: true });
|
|
9179
11118
|
await ingestLog(config, command, "system", "info", "Cleared issue continuation checkpoint", { reason });
|
|
9180
11119
|
return true;
|
|
@@ -9188,12 +11127,12 @@ function safeCheckpointRelativePath(rawPath) {
|
|
|
9188
11127
|
return normalized;
|
|
9189
11128
|
}
|
|
9190
11129
|
function hashFileSha256(filePath) {
|
|
9191
|
-
return
|
|
11130
|
+
return createHash12("sha256").update(readFileSync10(filePath)).digest("hex");
|
|
9192
11131
|
}
|
|
9193
11132
|
async function materializeIssueCheckpoint(config, command, workspace) {
|
|
9194
11133
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
9195
|
-
const manifestPath =
|
|
9196
|
-
if (!
|
|
11134
|
+
const manifestPath = join14(checkpointDir, "manifest.json");
|
|
11135
|
+
if (!existsSync13(manifestPath)) return [];
|
|
9197
11136
|
let manifest;
|
|
9198
11137
|
try {
|
|
9199
11138
|
manifest = asRecord(JSON.parse(readFileSync10(manifestPath, "utf8")));
|
|
@@ -9209,21 +11148,21 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
9209
11148
|
try {
|
|
9210
11149
|
const files = Array.isArray(manifest.files) ? manifest.files : [];
|
|
9211
11150
|
if (files.length > 20) throw new Error("issue checkpoint manifest exceeds the limit of 20 files");
|
|
9212
|
-
const filesRoot =
|
|
9213
|
-
const workspaceRoot =
|
|
11151
|
+
const filesRoot = realpathSync4(join14(checkpointDir, "files"));
|
|
11152
|
+
const workspaceRoot = realpathSync4(workspace.cwd);
|
|
9214
11153
|
const validated = [];
|
|
9215
11154
|
let totalBytes = 0;
|
|
9216
11155
|
for (const rawFile of files) {
|
|
9217
11156
|
const file = asRecord(rawFile);
|
|
9218
11157
|
const relativePath = safeCheckpointRelativePath(readString(file.path));
|
|
9219
11158
|
if (!relativePath) throw new Error("issue checkpoint manifest contains an invalid file path");
|
|
9220
|
-
const sourceCandidate =
|
|
9221
|
-
const target =
|
|
11159
|
+
const sourceCandidate = resolve12(filesRoot, relativePath);
|
|
11160
|
+
const target = resolve12(workspaceRoot, relativePath);
|
|
9222
11161
|
if (!pathWithin2(sourceCandidate, filesRoot) || !pathWithin2(target, workspaceRoot)) {
|
|
9223
11162
|
throw new Error(`issue checkpoint path escapes its workspace: ${relativePath}`);
|
|
9224
11163
|
}
|
|
9225
|
-
if (!
|
|
9226
|
-
const source =
|
|
11164
|
+
if (!existsSync13(sourceCandidate)) throw new Error(`issue checkpoint file is missing: ${relativePath}`);
|
|
11165
|
+
const source = realpathSync4(sourceCandidate);
|
|
9227
11166
|
if (!pathWithin2(source, filesRoot) || !statSync7(source).isFile()) {
|
|
9228
11167
|
throw new Error(`issue checkpoint source escapes its storage root: ${relativePath}`);
|
|
9229
11168
|
}
|
|
@@ -9248,7 +11187,7 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
9248
11187
|
if (err?.code !== "ENOENT") throw err;
|
|
9249
11188
|
}
|
|
9250
11189
|
mkdirSync8(dirname9(target), { recursive: true });
|
|
9251
|
-
const targetParent =
|
|
11190
|
+
const targetParent = realpathSync4(dirname9(target));
|
|
9252
11191
|
if (!pathWithin2(targetParent, workspaceRoot)) {
|
|
9253
11192
|
throw new Error(`issue checkpoint target escapes its workspace: ${relativePath}`);
|
|
9254
11193
|
}
|
|
@@ -9276,23 +11215,23 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
9276
11215
|
}
|
|
9277
11216
|
async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
9278
11217
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
9279
|
-
const filesDir =
|
|
11218
|
+
const filesDir = join14(checkpointDir, "files");
|
|
9280
11219
|
rmSync7(checkpointDir, { recursive: true, force: true });
|
|
9281
11220
|
mkdirSync8(filesDir, { recursive: true });
|
|
9282
11221
|
const files = [];
|
|
9283
11222
|
let totalBytes = 0;
|
|
9284
|
-
const workspaceRoot =
|
|
11223
|
+
const workspaceRoot = realpathSync4(workspace.cwd);
|
|
9285
11224
|
for (const candidate of candidates.slice(0, 20)) {
|
|
9286
11225
|
const relativePath = safeCheckpointRelativePath(candidate.rawPath);
|
|
9287
11226
|
const source = readString(candidate.filePath);
|
|
9288
|
-
if (!relativePath || !source || !
|
|
9289
|
-
const ownedSource =
|
|
11227
|
+
if (!relativePath || !source || !existsSync13(source) || !statSync7(source).isFile()) continue;
|
|
11228
|
+
const ownedSource = realpathSync4(source);
|
|
9290
11229
|
if (!pathWithin2(ownedSource, workspaceRoot)) {
|
|
9291
11230
|
throw new Error(`issue checkpoint source escapes its execution workspace: ${relativePath}`);
|
|
9292
11231
|
}
|
|
9293
11232
|
const byteSize = statSync7(ownedSource).size;
|
|
9294
11233
|
if (byteSize <= 0 || totalBytes + byteSize > MAX_CHECKPOINT_BYTES) continue;
|
|
9295
|
-
const target =
|
|
11234
|
+
const target = resolve12(filesDir, relativePath);
|
|
9296
11235
|
if (!pathWithin2(target, filesDir)) continue;
|
|
9297
11236
|
mkdirSync8(dirname9(target), { recursive: true });
|
|
9298
11237
|
copyFileSync3(ownedSource, target);
|
|
@@ -9312,7 +11251,7 @@ async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
|
9312
11251
|
totalBytes,
|
|
9313
11252
|
files
|
|
9314
11253
|
};
|
|
9315
|
-
writeFileSync8(
|
|
11254
|
+
writeFileSync8(join14(checkpointDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
9316
11255
|
`);
|
|
9317
11256
|
await ingestLog(config, command, "system", "warn", `Saved ${files.length} required deliverable(s) for continuation recovery`, manifest);
|
|
9318
11257
|
return manifest;
|
|
@@ -9374,6 +11313,7 @@ async function executeRunCommand(config, command) {
|
|
|
9374
11313
|
await ingestWorkspaceStatus(config, command, cwd);
|
|
9375
11314
|
const promptCompilation = buildCommandPrompt(command, workspace, materializedAttachments, {
|
|
9376
11315
|
executorKind: executor.kind,
|
|
11316
|
+
managedMcpToolMode: executor.kind === "pi" && Object.keys(governedMcp).length > 0 ? MANAGED_PI_MCP_TOOL_MODE : null,
|
|
9377
11317
|
artifactVerifierCommands: config.artifactVerifierCommands
|
|
9378
11318
|
});
|
|
9379
11319
|
const nativeSessionRequest = asRecord(asRecord(command.payload).nativeSession);
|
|
@@ -9491,7 +11431,7 @@ async function executeRunCommand(config, command) {
|
|
|
9491
11431
|
executorEnv = {
|
|
9492
11432
|
...executorEnv,
|
|
9493
11433
|
AMASTER_MANAGED_RUNTIME_ASSERTION_FD: "3",
|
|
9494
|
-
AMASTER_MANAGED_RUNTIME_AUDIT_FILE:
|
|
11434
|
+
AMASTER_MANAGED_RUNTIME_AUDIT_FILE: join14(
|
|
9495
11435
|
managedMcpProfile.env.HOME,
|
|
9496
11436
|
".amaster-managed-runtime-audit.jsonl"
|
|
9497
11437
|
),
|
|
@@ -9741,12 +11681,12 @@ async function executeRunCommand(config, command) {
|
|
|
9741
11681
|
config,
|
|
9742
11682
|
command,
|
|
9743
11683
|
cwd,
|
|
9744
|
-
mcpToolResults.filter((
|
|
9745
|
-
const intentId = readString(asRecord(
|
|
11684
|
+
mcpToolResults.filter((result4) => {
|
|
11685
|
+
const intentId = readString(asRecord(result4).artifactIntent?.intentId);
|
|
9746
11686
|
return !intentId || !runtimeArtifactIngest.hasHandled(intentId);
|
|
9747
11687
|
})
|
|
9748
11688
|
));
|
|
9749
|
-
const shouldPreserveNativeSession = managedMcpProfile && parsed.sessionId && (execution.exitCode === 0 || execution.completionOutputType === "approval_required") && !execution.timedOut && execution.cancelled !== true && !execution.spawnError && ["codex", "pi"].includes(executor.kind) && mcpToolResults.some((
|
|
11689
|
+
const shouldPreserveNativeSession = managedMcpProfile && parsed.sessionId && (execution.exitCode === 0 || execution.completionOutputType === "approval_required") && !execution.timedOut && execution.cancelled !== true && !execution.spawnError && ["codex", "pi"].includes(executor.kind) && mcpToolResults.some((result4) => readString(result4.status) === "approval_required");
|
|
9750
11690
|
if (shouldPreserveNativeSession) {
|
|
9751
11691
|
try {
|
|
9752
11692
|
const preserveSessionRollout = executor.kind === "pi" ? preserveManagedPiSessionRollout : preserveManagedCodexSessionRollout;
|
|
@@ -9760,7 +11700,7 @@ async function executeRunCommand(config, command) {
|
|
|
9760
11700
|
workspace,
|
|
9761
11701
|
workspaceStatus.artifacts.map((artifact) => ({
|
|
9762
11702
|
rawPath: artifact.relativePath,
|
|
9763
|
-
filePath:
|
|
11703
|
+
filePath: resolve12(cwd, artifact.relativePath)
|
|
9764
11704
|
}))
|
|
9765
11705
|
);
|
|
9766
11706
|
nativeSessionRollout = {
|
|
@@ -9855,7 +11795,7 @@ async function executeRunCommand(config, command) {
|
|
|
9855
11795
|
status: managedMcpCleanup.status
|
|
9856
11796
|
});
|
|
9857
11797
|
}
|
|
9858
|
-
const
|
|
11798
|
+
const result3 = {
|
|
9859
11799
|
evidenceContract: { version: 1 },
|
|
9860
11800
|
executorKind: executor.kind,
|
|
9861
11801
|
command: invocation.command,
|
|
@@ -9944,7 +11884,7 @@ async function executeRunCommand(config, command) {
|
|
|
9944
11884
|
}
|
|
9945
11885
|
} : {}
|
|
9946
11886
|
};
|
|
9947
|
-
const completion = await completeCommand(config, command, cancelled ? "cancelled" : succeeded ? "succeeded" : "failed",
|
|
11887
|
+
const completion = await completeCommand(config, command, cancelled ? "cancelled" : succeeded ? "succeeded" : "failed", result3, error ?? void 0);
|
|
9948
11888
|
if (!completion) {
|
|
9949
11889
|
rememberPendingResultOutboxRunCommand(command, {
|
|
9950
11890
|
phase: "result_outbox",
|
|
@@ -9990,6 +11930,52 @@ async function processCommand(config, command) {
|
|
|
9990
11930
|
await executeTrackedModelCallCommand(config, command);
|
|
9991
11931
|
return;
|
|
9992
11932
|
}
|
|
11933
|
+
if (command.commandType === "source_read") {
|
|
11934
|
+
const sourceReadStatus = browserCapabilityStatus(config, "constrained_source_read");
|
|
11935
|
+
const broker = sourceReadStatus.ready ? runtimeBrowserSessionBroker(config) : null;
|
|
11936
|
+
await ackCommand(config, command, "spawned");
|
|
11937
|
+
const result3 = await executeSourceReadCommand(command, {
|
|
11938
|
+
resolveBinding: broker ? async (input) => input.authentication ? broker.resolveAuthenticatedBinding({
|
|
11939
|
+
companyId: input.companyId,
|
|
11940
|
+
locator: input.locator,
|
|
11941
|
+
declaredPageLocators: input.declaredPageLocators,
|
|
11942
|
+
allowedResourceOrigins: input.allowedResourceOrigins,
|
|
11943
|
+
limits: input.limits,
|
|
11944
|
+
signal: input.signal,
|
|
11945
|
+
...input.authentication
|
|
11946
|
+
}) : broker.resolvePublicBinding({
|
|
11947
|
+
locator: input.locator,
|
|
11948
|
+
declaredPageLocators: input.declaredPageLocators,
|
|
11949
|
+
allowedResourceOrigins: input.allowedResourceOrigins,
|
|
11950
|
+
limits: input.limits
|
|
11951
|
+
}) : unavailableSourceReadBindingResolver
|
|
11952
|
+
});
|
|
11953
|
+
const failed = result3.status === "failed";
|
|
11954
|
+
await completeCommand(
|
|
11955
|
+
config,
|
|
11956
|
+
command,
|
|
11957
|
+
failed ? "failed" : "succeeded",
|
|
11958
|
+
result3,
|
|
11959
|
+
failed ? result3.failureCode : void 0
|
|
11960
|
+
);
|
|
11961
|
+
return;
|
|
11962
|
+
}
|
|
11963
|
+
if (command.commandType === "browser_session") {
|
|
11964
|
+
const status = browserCapabilityStatus(config, "headed_browser_auth");
|
|
11965
|
+
await ackCommand(config, command, "spawned");
|
|
11966
|
+
const result3 = await executeBrowserSessionCommand(command, {
|
|
11967
|
+
broker: status.ready ? runtimeBrowserSessionBroker(config) : null
|
|
11968
|
+
});
|
|
11969
|
+
const failed = result3.outcome === "failed";
|
|
11970
|
+
await completeCommand(
|
|
11971
|
+
config,
|
|
11972
|
+
command,
|
|
11973
|
+
failed ? "failed" : "succeeded",
|
|
11974
|
+
result3,
|
|
11975
|
+
failed ? result3.failureCode : void 0
|
|
11976
|
+
);
|
|
11977
|
+
return;
|
|
11978
|
+
}
|
|
9993
11979
|
await completeCommand(config, command, "succeeded", {
|
|
9994
11980
|
summary: `Command ${command.commandType} acknowledged by amaster-runtime-daemon.`,
|
|
9995
11981
|
commandType: command.commandType
|
|
@@ -10026,12 +12012,12 @@ function reconcileStaleManagedMcpProfiles(config) {
|
|
|
10026
12012
|
["Codex", reconcileManagedCodexMcpProfiles],
|
|
10027
12013
|
["Pi", reconcileManagedPiMcpProfiles]
|
|
10028
12014
|
]) {
|
|
10029
|
-
const
|
|
10030
|
-
if (
|
|
10031
|
-
throw new Error(`${executorKind.toLowerCase()}_managed_mcp_restart_cleanup_failed: ${
|
|
12015
|
+
const result3 = reconcile(config.runtimeWorkspacesRoot);
|
|
12016
|
+
if (result3.failed > 0) {
|
|
12017
|
+
throw new Error(`${executorKind.toLowerCase()}_managed_mcp_restart_cleanup_failed: ${result3.failed}/${result3.scanned} owned profile(s) could not be reconciled`);
|
|
10032
12018
|
}
|
|
10033
|
-
if (
|
|
10034
|
-
process.stderr.write(`AMaster daemon removed ${
|
|
12019
|
+
if (result3.removed > 0) {
|
|
12020
|
+
process.stderr.write(`AMaster daemon removed ${result3.removed} stale marker-owned ${executorKind} managed MCP artifact(s) during startup reconciliation.
|
|
10035
12021
|
`);
|
|
10036
12022
|
}
|
|
10037
12023
|
}
|
|
@@ -10118,7 +12104,7 @@ async function ack(config, flags) {
|
|
|
10118
12104
|
process.stdout.write(`${JSON.stringify(body, null, 2)}
|
|
10119
12105
|
`);
|
|
10120
12106
|
}
|
|
10121
|
-
async function
|
|
12107
|
+
async function result2(config, flags) {
|
|
10122
12108
|
const connectorId = requireConnectorId(config);
|
|
10123
12109
|
const commandId = flags.commandId ?? process.env.AMASTER_COMMAND_ID;
|
|
10124
12110
|
if (!commandId) throw new Error("--command-id or AMASTER_COMMAND_ID is required");
|
|
@@ -10174,7 +12160,7 @@ async function runLoop(config) {
|
|
|
10174
12160
|
${message}
|
|
10175
12161
|
`);
|
|
10176
12162
|
}
|
|
10177
|
-
await new Promise((
|
|
12163
|
+
await new Promise((resolve13) => setTimeout(resolve13, config.pollIntervalSeconds * 1e3));
|
|
10178
12164
|
}
|
|
10179
12165
|
}
|
|
10180
12166
|
function help() {
|
|
@@ -10234,7 +12220,7 @@ async function main() {
|
|
|
10234
12220
|
await ack(config, flags);
|
|
10235
12221
|
break;
|
|
10236
12222
|
case "result":
|
|
10237
|
-
await
|
|
12223
|
+
await result2(config, flags);
|
|
10238
12224
|
break;
|
|
10239
12225
|
case "run-once":
|
|
10240
12226
|
await runOnce(config);
|