@amaster.ai/employee-runtime-connector 0.1.0-beta.45 → 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 +2212 -198
- 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];
|
|
@@ -1876,6 +1881,10 @@ var piManagedMcpProfileApi = (() => {
|
|
|
1876
1881
|
const PROFILE_MARKER2 = ".amaster-managed-pi-profile.json";
|
|
1877
1882
|
const SESSION_ROLLOUT_MARKER2 = ".amaster-pi-session-rollout.json";
|
|
1878
1883
|
const DEFAULT_SESSION_ROLLOUT_TTL_MS2 = 24 * 60 * 60 * 1e3;
|
|
1884
|
+
const PI_ATTESTATION_TIMEOUT_MS = 1e4;
|
|
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();
|
|
1879
1888
|
function record4(value) {
|
|
1880
1889
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1881
1890
|
}
|
|
@@ -1981,6 +1990,33 @@ var piManagedMcpProfileApi = (() => {
|
|
|
1981
1990
|
}
|
|
1982
1991
|
return tuple.join(".");
|
|
1983
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
|
+
}
|
|
1984
2020
|
function validateAuthority2(input) {
|
|
1985
2021
|
const runtimeAuth = record4(input.runtimeAuth);
|
|
1986
2022
|
const gateway = record4(runtimeAuth.governedMcp);
|
|
@@ -2210,18 +2246,52 @@ var piManagedMcpProfileApi = (() => {
|
|
|
2210
2246
|
return { adapterVersion, protectedValues };
|
|
2211
2247
|
}
|
|
2212
2248
|
function attestPi(executorCommand, env, configPath, expectedConfig) {
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2249
|
+
const executorIdentity = piExecutableIdentity(executorCommand);
|
|
2250
|
+
let result3;
|
|
2251
|
+
for (let attempt = 1; attempt <= PI_ATTESTATION_MAX_ATTEMPTS; attempt += 1) {
|
|
2252
|
+
result3 = spawnSyncImpl(executorCommand, ["--version"], {
|
|
2253
|
+
cwd: env.AMASTER_RUNTIME_EXECUTION_WORKDIR,
|
|
2254
|
+
env,
|
|
2255
|
+
encoding: "utf8",
|
|
2256
|
+
timeout: PI_ATTESTATION_TIMEOUT_MS,
|
|
2257
|
+
killSignal: "SIGKILL",
|
|
2258
|
+
maxBuffer: 1024 * 1024
|
|
2259
|
+
});
|
|
2260
|
+
if (result3.error?.code === "ETIMEDOUT" && attempt < PI_ATTESTATION_MAX_ATTEMPTS) continue;
|
|
2261
|
+
break;
|
|
2262
|
+
}
|
|
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";
|
|
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;
|
|
2222
2294
|
}
|
|
2223
|
-
const executorVersion = parseVersion(`${result2.stdout ?? ""}
|
|
2224
|
-
${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
2225
2295
|
let effectiveConfig;
|
|
2226
2296
|
try {
|
|
2227
2297
|
effectiveConfig = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
@@ -2231,7 +2301,20 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2231
2301
|
if (JSON.stringify(effectiveConfig) !== JSON.stringify(expectedConfig) || env.PI_AGENT_MCP_SERVERS_FILE !== configPath) {
|
|
2232
2302
|
throw new Error("pi_managed_mcp_attestation_failed: effective config does not match managed amaster Gateway");
|
|
2233
2303
|
}
|
|
2234
|
-
|
|
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
|
+
};
|
|
2235
2318
|
}
|
|
2236
2319
|
function prepareManagedPiMcpProfile2(input) {
|
|
2237
2320
|
assertInvocationIsolation2(input);
|
|
@@ -2288,12 +2371,13 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2288
2371
|
PI_AGENT_MCP_SERVERS_FILE: configPath,
|
|
2289
2372
|
TMPDIR: tmp
|
|
2290
2373
|
};
|
|
2291
|
-
const
|
|
2374
|
+
const executorAttestation = attestPi(nonEmpty2(input.executorCommand, "executorCommand"), env, configPath, config);
|
|
2292
2375
|
const attestationFacts = {
|
|
2293
2376
|
connectorVersion: nonEmpty2(input.connectorVersion, "connectorVersion"),
|
|
2294
2377
|
executorKind: "pi",
|
|
2295
|
-
|
|
2378
|
+
...executorAttestation,
|
|
2296
2379
|
mcpAdapterVersion: seededRuntime.adapterVersion,
|
|
2380
|
+
mcpToolMode: MANAGED_PI_MCP_TOOL_MODE,
|
|
2297
2381
|
configMode: "isolated_home_run_scoped_mcp_file",
|
|
2298
2382
|
namespace: SUPPORTED_SERVER_NAME2,
|
|
2299
2383
|
schemaVersion: SUPPORTED_SCHEMA_VERSION2,
|
|
@@ -2384,7 +2468,7 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2384
2468
|
if (existsSync3(profileRoot)) throw new Error("pi_managed_mcp_cleanup_failed: profile root still exists");
|
|
2385
2469
|
return { status: "removed", profileRoot };
|
|
2386
2470
|
}
|
|
2387
|
-
function reconcileManagedPiMcpProfiles2(rootPath,
|
|
2471
|
+
function reconcileManagedPiMcpProfiles2(rootPath, options2 = {}) {
|
|
2388
2472
|
const root = resolve2(rootPath);
|
|
2389
2473
|
if (!existsSync3(root)) return { scanned: 0, removed: 0, removedProfiles: 0, removedRollouts: 0, failed: 0, failures: [] };
|
|
2390
2474
|
const profileMarkers = [];
|
|
@@ -2415,8 +2499,8 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2415
2499
|
failures.push({ profileRoot, error: error instanceof Error ? error.message : String(error) });
|
|
2416
2500
|
}
|
|
2417
2501
|
}
|
|
2418
|
-
const nowMs = Number.isFinite(
|
|
2419
|
-
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;
|
|
2420
2504
|
for (const markerPath of rolloutMarkers) {
|
|
2421
2505
|
const cacheRoot = dirname3(markerPath);
|
|
2422
2506
|
try {
|
|
@@ -2444,7 +2528,8 @@ ${result2.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
|
|
|
2444
2528
|
};
|
|
2445
2529
|
}
|
|
2446
2530
|
return { prepareManagedPiMcpProfile: prepareManagedPiMcpProfile2, preserveManagedPiSessionRollout: preserveManagedPiSessionRollout2, cleanupManagedPiMcpProfile: cleanupManagedPiMcpProfile2, reconcileManagedPiMcpProfiles: reconcileManagedPiMcpProfiles2 };
|
|
2447
|
-
}
|
|
2531
|
+
}
|
|
2532
|
+
var piManagedMcpProfileApi = createManagedPiMcpProfileApi();
|
|
2448
2533
|
var prepareManagedPiMcpProfile = piManagedMcpProfileApi.prepareManagedPiMcpProfile;
|
|
2449
2534
|
var preserveManagedPiSessionRollout = piManagedMcpProfileApi.preserveManagedPiSessionRollout;
|
|
2450
2535
|
var cleanupManagedPiMcpProfile = piManagedMcpProfileApi.cleanupManagedPiMcpProfile;
|
|
@@ -2516,12 +2601,12 @@ function record2(value) {
|
|
|
2516
2601
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2517
2602
|
}
|
|
2518
2603
|
function managedChildSpawnInvocation(command, args, requestedIdentity) {
|
|
2519
|
-
const
|
|
2520
|
-
const childUmask =
|
|
2604
|
+
const identity2 = record2(requestedIdentity);
|
|
2605
|
+
const childUmask = identity2.umask;
|
|
2521
2606
|
if (childUmask !== void 0 && (!Number.isInteger(childUmask) || childUmask < 0 || childUmask > 511)) {
|
|
2522
2607
|
throw new Error("executor_spawn_umask_invalid");
|
|
2523
2608
|
}
|
|
2524
|
-
const spawnIdentity = { ...
|
|
2609
|
+
const spawnIdentity = { ...identity2 };
|
|
2525
2610
|
delete spawnIdentity.umask;
|
|
2526
2611
|
if (childUmask === void 0) {
|
|
2527
2612
|
return { command, args, spawnIdentity };
|
|
@@ -2685,7 +2770,10 @@ function fixedRules(input, includeIssueLine) {
|
|
|
2685
2770
|
input.managed ? `- source workspace: ${input.sourceWorkspacePath}` : "",
|
|
2686
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." : "",
|
|
2687
2772
|
input.wakeReason ? `- wake reason: ${input.wakeReason}` : "",
|
|
2688
|
-
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." : ""
|
|
2689
2777
|
].filter(Boolean).join("\n");
|
|
2690
2778
|
}
|
|
2691
2779
|
function approvalContinuationText(input) {
|
|
@@ -2714,25 +2802,79 @@ function recoveryInstructionText(input) {
|
|
|
2714
2802
|
"The prior successful run did not leave a terminal task disposition.",
|
|
2715
2803
|
"Do not repeat the original source work or create or revise deliverables.",
|
|
2716
2804
|
"Inspect the existing task and run evidence, then choose exactly one explicit disposition: done, in_review, input, blocked, delegated, or queued.",
|
|
2717
|
-
"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.",
|
|
2718
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.",
|
|
2719
2807
|
"Record the disposition with its concrete owner or next action and update the task accordingly."
|
|
2720
2808
|
].join("\n");
|
|
2721
2809
|
}
|
|
2722
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");
|
|
2723
2817
|
return [
|
|
2724
2818
|
"This is a status-only source recovery. Do not repeat the original source work or create or revise deliverables.",
|
|
2725
2819
|
"Inspect the existing task and run evidence, then choose exactly one explicit disposition using task-governance actions:",
|
|
2726
2820
|
"- done only when the existing evidence already satisfies acceptance;",
|
|
2727
2821
|
"- in_review or input when a specific human review or answer is required;",
|
|
2728
|
-
|
|
2822
|
+
blockedDisposition,
|
|
2729
2823
|
"- delegated or queued only with a concrete continuation path.",
|
|
2730
2824
|
"Record the disposition in a comment and update the parent task accordingly."
|
|
2731
2825
|
].join("\n");
|
|
2732
2826
|
}
|
|
2733
2827
|
return "";
|
|
2734
2828
|
}
|
|
2735
|
-
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 } = {}) {
|
|
2736
2878
|
const envelope = asRecord(context.authorizationEnvelope);
|
|
2737
2879
|
const allowed = new Set(
|
|
2738
2880
|
Array.isArray(envelope.allowedActionClasses) ? envelope.allowedActionClasses.filter((value) => typeof value === "string") : []
|
|
@@ -2742,13 +2884,23 @@ function runtimeAuthorizationText(context) {
|
|
|
2742
2884
|
authorizationClass,
|
|
2743
2885
|
optionId: readString(optionId)
|
|
2744
2886
|
})).filter((entry) => entry.optionId && !allowed.has(entry.authorizationClass));
|
|
2745
|
-
|
|
2746
|
-
|
|
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 ? [
|
|
2747
2898
|
`Current allowed action classes: ${[...allowed].join(", ") || "task_governance"}.`,
|
|
2748
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:",
|
|
2749
2900
|
...missing.map((entry) => `- ${entry.authorizationClass}: ${entry.optionId}`),
|
|
2750
2901
|
"Never use request_confirmation to authorize a runtime action class."
|
|
2751
|
-
].join("\n");
|
|
2902
|
+
].join("\n") : "";
|
|
2903
|
+
return [completionContract, authorizationContract].filter(Boolean).join("\n");
|
|
2752
2904
|
}
|
|
2753
2905
|
function runtimeDecompositionRequirementText(context) {
|
|
2754
2906
|
const issue = asRecord(context.paperclipIssue);
|
|
@@ -2766,7 +2918,10 @@ function runtimeDecompositionRequirementText(context) {
|
|
|
2766
2918
|
}
|
|
2767
2919
|
return [
|
|
2768
2920
|
"This issue has a server-enforced typed decomposition requirement.",
|
|
2769
|
-
"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.",
|
|
2770
2925
|
"Each executable child must have an owner, dependencies where needed, and acceptance criteria. Do not create probe or test children.",
|
|
2771
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.",
|
|
2772
2927
|
`Requirement source: ${sourceType}:${sourceId}@${sourceRevision}`
|
|
@@ -2804,12 +2959,12 @@ function manifestEntry(section) {
|
|
|
2804
2959
|
truncationReason: section.truncationReason ?? null
|
|
2805
2960
|
};
|
|
2806
2961
|
}
|
|
2807
|
-
function renderPrompt(sections, manifest) {
|
|
2962
|
+
function renderPrompt(sections, manifest, compactManifest = false) {
|
|
2808
2963
|
return [
|
|
2809
2964
|
...sections.map(sectionText).filter(Boolean),
|
|
2810
2965
|
"## Context Manifest",
|
|
2811
2966
|
"```json",
|
|
2812
|
-
jsonText(manifest),
|
|
2967
|
+
compactManifest ? JSON.stringify(manifest) : jsonText(manifest),
|
|
2813
2968
|
"```"
|
|
2814
2969
|
].join("\n");
|
|
2815
2970
|
}
|
|
@@ -2833,6 +2988,10 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2833
2988
|
}
|
|
2834
2989
|
const context = asRecord(input.context);
|
|
2835
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;
|
|
2836
2995
|
const governedReads = governedReadSection(context);
|
|
2837
2996
|
const hasTask = Boolean(readString(input.taskMarkdown));
|
|
2838
2997
|
const taskText = readString(input.taskMarkdown) ?? "";
|
|
@@ -2857,9 +3016,9 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2857
3016
|
const snapshotFreshness = { kind: "run_snapshot" };
|
|
2858
3017
|
const rawSections = [
|
|
2859
3018
|
{ name: "runtime_rules", title: "", priority: 100, sourceRef: `command:${input.commandId}`, content: fixedRules(input, !hasTask) },
|
|
2860
|
-
{ 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" },
|
|
2861
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" },
|
|
2862
|
-
{ 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 }) },
|
|
2863
3022
|
{ name: "runtime_decomposition_requirement", title: "Required Task Decomposition", priority: 98, sourceRef: `issue:${input.issueId ?? "unknown"}`, content: runtimeDecompositionRequirementText(context) },
|
|
2864
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 },
|
|
2865
3024
|
{ name: "wake_comments", title: wakeDeltaTitle, priority: interactionResolution ? 99 : 95, sourceRef: wakeDeltaSourceRefs, originalContent: wakeDeltaOriginal, content: wakeDeltaContent, truncationReason: commentsSelected ? null : "duplicate_task_context" },
|
|
@@ -2889,12 +3048,13 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2889
3048
|
truncationReason: section.content ? section.truncationReason : section.truncationReason ?? "source_absent"
|
|
2890
3049
|
}));
|
|
2891
3050
|
let prompt = "";
|
|
3051
|
+
let compactManifest = false;
|
|
2892
3052
|
let manifest = buildManifest(mode, maxChars, sections, governedReads.provenance, 0);
|
|
2893
3053
|
for (let pass = 0; pass < 20; pass += 1) {
|
|
2894
3054
|
let usedChars = 0;
|
|
2895
3055
|
for (let telemetryPass = 0; telemetryPass < 3; telemetryPass += 1) {
|
|
2896
3056
|
manifest = buildManifest(mode, maxChars, sections, governedReads.provenance, usedChars);
|
|
2897
|
-
prompt = renderPrompt(sections, manifest);
|
|
3057
|
+
prompt = renderPrompt(sections, manifest, compactManifest);
|
|
2898
3058
|
if (prompt.length === usedChars) break;
|
|
2899
3059
|
usedChars = prompt.length;
|
|
2900
3060
|
}
|
|
@@ -2902,8 +3062,13 @@ function compileCommandPromptWithManifest(input, options = {}) {
|
|
|
2902
3062
|
const overflow = Math.max(1, prompt.length - maxChars);
|
|
2903
3063
|
const candidate = [...sections].filter((section) => section.priority < 100 && section.content.length > 0).sort((left, right) => left.priority - right.priority)[0];
|
|
2904
3064
|
if (!candidate) {
|
|
3065
|
+
if (!compactManifest) {
|
|
3066
|
+
compactManifest = true;
|
|
3067
|
+
manifest = buildManifest(mode, maxChars, sections, governedReads.provenance, 0);
|
|
3068
|
+
continue;
|
|
3069
|
+
}
|
|
2905
3070
|
const fixedChars = sections.filter((section) => section.priority === 100).map(sectionText).join("\n").length;
|
|
2906
|
-
const manifestChars =
|
|
3071
|
+
const manifestChars = JSON.stringify(manifest).length;
|
|
2907
3072
|
throw new Error(`Prompt fixed sections exceed the unified ${maxChars}-character budget: promptChars=${prompt.length} fixedChars=${fixedChars} manifestChars=${manifestChars}`);
|
|
2908
3073
|
}
|
|
2909
3074
|
truncateSection(candidate, Math.max(0, candidate.content.length - overflow - 32));
|
|
@@ -2942,22 +3107,22 @@ function executorDiscoveryEnv(env = process.env) {
|
|
|
2942
3107
|
}
|
|
2943
3108
|
function commandExists(command, env = process.env) {
|
|
2944
3109
|
const commandEnv = executorDiscoveryEnv(env);
|
|
2945
|
-
const
|
|
3110
|
+
const result3 = spawnSync3("sh", ["-lc", `command -v ${quoteShell(command)} >/dev/null 2>&1`], {
|
|
2946
3111
|
env: commandEnv,
|
|
2947
3112
|
stdio: "ignore"
|
|
2948
3113
|
});
|
|
2949
|
-
return
|
|
3114
|
+
return result3.status === 0;
|
|
2950
3115
|
}
|
|
2951
3116
|
function commandVersion(command, env = process.env) {
|
|
2952
3117
|
const commandEnv = executorDiscoveryEnv(env);
|
|
2953
|
-
const
|
|
3118
|
+
const result3 = spawnSync3(command, ["--version"], {
|
|
2954
3119
|
env: commandEnv,
|
|
2955
3120
|
encoding: "utf8",
|
|
2956
3121
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2957
3122
|
timeout: 3e3
|
|
2958
3123
|
});
|
|
2959
|
-
if (
|
|
2960
|
-
return
|
|
3124
|
+
if (result3.status !== 0) return void 0;
|
|
3125
|
+
return result3.stdout.trim().split(/\r?\n/)[0]?.slice(0, 120) || void 0;
|
|
2961
3126
|
}
|
|
2962
3127
|
function discoverExecutors(options = {}) {
|
|
2963
3128
|
const knownExecutors = options.knownExecutors ?? KNOWN_EXECUTORS;
|
|
@@ -3041,8 +3206,8 @@ function stateFilePath(env) {
|
|
|
3041
3206
|
return env.AMASTER_DAEMON_STATE_FILE ? expandHomePath(String(env.AMASTER_DAEMON_STATE_FILE)) : join5(runtimeHome(env), "runtime-connector-state.json");
|
|
3042
3207
|
}
|
|
3043
3208
|
function corruptStatePath(path) {
|
|
3044
|
-
const
|
|
3045
|
-
const base = `${path}.corrupt-${
|
|
3209
|
+
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
|
|
3210
|
+
const base = `${path}.corrupt-${timestamp2}Z`;
|
|
3046
3211
|
let candidate = base;
|
|
3047
3212
|
for (let suffix = 1; existsSync4(candidate); suffix += 1) {
|
|
3048
3213
|
candidate = `${base}.${suffix}`;
|
|
@@ -3099,6 +3264,12 @@ function buildConfig(env = process.env, flags = {}) {
|
|
|
3099
3264
|
const runtimeWorkspacesRoot = expandHomePath(
|
|
3100
3265
|
String(flags.runtimeWorkspacesRoot ?? env.AMASTER_RUNTIME_WORKSPACES_ROOT ?? join5(home, "workspaces"))
|
|
3101
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();
|
|
3102
3273
|
mkdirSync4(runtimeWorkspacesRoot, { recursive: true });
|
|
3103
3274
|
const workspaceBindings = splitList(
|
|
3104
3275
|
flags.workspaceAllowlist ?? env.AMASTER_WORKSPACE_ALLOWLIST ?? env.AMASTER_WORKSPACE_BINDINGS ?? runtimeWorkspacesRoot
|
|
@@ -3165,6 +3336,8 @@ function buildConfig(env = process.env, flags = {}) {
|
|
|
3165
3336
|
flags.orphanReaperIntervalSeconds ?? env.AMASTER_RUNTIME_ORPHAN_REAPER_INTERVAL_SECONDS,
|
|
3166
3337
|
300
|
|
3167
3338
|
),
|
|
3339
|
+
browserSessionStateRoot,
|
|
3340
|
+
browserExecutablePath: browserExecutablePath ? expandHomePath(browserExecutablePath) : "",
|
|
3168
3341
|
runtimeWorkspacesRoot,
|
|
3169
3342
|
state
|
|
3170
3343
|
};
|
|
@@ -3298,9 +3471,9 @@ function filterExecutionStderrForResult(executorKind, stderr) {
|
|
|
3298
3471
|
const trimmed = line.trim();
|
|
3299
3472
|
return !trimmed || !isNoise(trimmed);
|
|
3300
3473
|
});
|
|
3301
|
-
const
|
|
3302
|
-
return hadTrailingNewline &&
|
|
3303
|
-
` :
|
|
3474
|
+
const result3 = kept.join("\n");
|
|
3475
|
+
return hadTrailingNewline && result3 ? `${result3}
|
|
3476
|
+
` : result3;
|
|
3304
3477
|
}
|
|
3305
3478
|
function summarizeCodexEvent(event) {
|
|
3306
3479
|
if (isProviderBookkeepingEvent(event)) return null;
|
|
@@ -3706,8 +3879,8 @@ var PI_TERMINAL_RUNTIME_ACTION_STATUSES = /* @__PURE__ */ new Set([
|
|
|
3706
3879
|
function approvedMcpInvocationSucceeded(results, invocationId) {
|
|
3707
3880
|
const approvedInvocationId = readString(invocationId);
|
|
3708
3881
|
return Boolean(approvedInvocationId && (Array.isArray(results) ? results : []).some((rawResult) => {
|
|
3709
|
-
const
|
|
3710
|
-
return readString(
|
|
3882
|
+
const result3 = asRecord(rawResult);
|
|
3883
|
+
return readString(result3.invocationId) === approvedInvocationId && readString(result3.status) === "succeeded" && !["rejected", "blocked"].includes(readString(result3.providerStatus) ?? "");
|
|
3711
3884
|
}));
|
|
3712
3885
|
}
|
|
3713
3886
|
function governedMcpToolResult(structuredContent) {
|
|
@@ -3721,9 +3894,9 @@ function governedMcpToolResult(structuredContent) {
|
|
|
3721
3894
|
const intentId = readString(effectResult.artifactIntentId);
|
|
3722
3895
|
const manifestId = readString(effectResult.manifestId);
|
|
3723
3896
|
const sourceRelativePath = readString(effectResult.sourceRelativePath);
|
|
3724
|
-
const
|
|
3897
|
+
const sha2563 = readString(effectResult.sha256);
|
|
3725
3898
|
const byteSize = readNumber(effectResult.byteSize, 0);
|
|
3726
|
-
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;
|
|
3727
3900
|
const runtimeActionToolName = readString(providerContent.toolName);
|
|
3728
3901
|
const runtimeAction = runtimeActionToolName?.startsWith("runtime_action.") ? {
|
|
3729
3902
|
toolName: runtimeActionToolName,
|
|
@@ -3743,21 +3916,21 @@ function durablePiRuntimeActionEvidence(results) {
|
|
|
3743
3916
|
const normalizedResults = (Array.isArray(results) ? results : []).map(asRecord);
|
|
3744
3917
|
const writeCallIds = /* @__PURE__ */ new Set();
|
|
3745
3918
|
const writePlanIds = /* @__PURE__ */ new Set();
|
|
3746
|
-
for (const
|
|
3747
|
-
const runtimeAction = asRecord(
|
|
3919
|
+
for (const result3 of normalizedResults) {
|
|
3920
|
+
const runtimeAction = asRecord(result3.runtimeAction);
|
|
3748
3921
|
const toolName = readString(runtimeAction.toolName);
|
|
3749
3922
|
const callId = readString(runtimeAction.callId);
|
|
3750
3923
|
const planId = readString(runtimeAction.planId);
|
|
3751
3924
|
const resultStatus = readString(runtimeAction.resultStatus);
|
|
3752
|
-
const status = readString(
|
|
3753
|
-
const providerStatus = readString(
|
|
3925
|
+
const status = readString(result3.status);
|
|
3926
|
+
const providerStatus = readString(result3.providerStatus);
|
|
3754
3927
|
if (status === "succeeded" && providerStatus === "accepted" && toolName && resultStatus && PI_TERMINAL_RUNTIME_ACTION_STATUSES.has(resultStatus)) {
|
|
3755
3928
|
const isLinkedStatus = toolName === "runtime_action.status" && (callId && writeCallIds.has(callId) || planId && writePlanIds.has(planId));
|
|
3756
3929
|
const isDurableWrite = toolName === "runtime_action.submit" ? Boolean(callId) : toolName === "runtime_action.commit" && Boolean(planId || callId);
|
|
3757
3930
|
if (isLinkedStatus || isDurableWrite) {
|
|
3758
3931
|
return {
|
|
3759
3932
|
kind: "runtime_action",
|
|
3760
|
-
...readString(
|
|
3933
|
+
...readString(result3.invocationId) ? { invocationId: readString(result3.invocationId) } : {},
|
|
3761
3934
|
toolName,
|
|
3762
3935
|
...callId ? { callId } : {},
|
|
3763
3936
|
...planId ? { planId } : {}
|
|
@@ -3793,14 +3966,24 @@ function classifyPiTerminalCleanupDisposition(parsed, runtimeArtifacts) {
|
|
|
3793
3966
|
diagnostics
|
|
3794
3967
|
};
|
|
3795
3968
|
}
|
|
3969
|
+
function classifyPiTurnLimitResult(parsed) {
|
|
3970
|
+
const stopReason = readString(parsed?.stopReason);
|
|
3971
|
+
if (parsed?.terminalEventType !== "agent_end" || !["toolUse", "tool_use"].includes(stopReason ?? "")) return null;
|
|
3972
|
+
return {
|
|
3973
|
+
errorCode: "max_turns_exhausted",
|
|
3974
|
+
errorFamily: "execution_limit",
|
|
3975
|
+
stopReason: "max_turns_exhausted",
|
|
3976
|
+
message: "Pi Agent exhausted its turn limit while waiting to continue tool work"
|
|
3977
|
+
};
|
|
3978
|
+
}
|
|
3796
3979
|
function codexMcpToolResults(event) {
|
|
3797
3980
|
if (event?.type !== "item.completed") return [];
|
|
3798
3981
|
const item = asRecord(event.item);
|
|
3799
3982
|
if (item.type !== "mcp_tool_call") return [];
|
|
3800
|
-
const
|
|
3801
|
-
let structuredContent = asRecord(
|
|
3802
|
-
if (Object.keys(structuredContent).length === 0 && Array.isArray(
|
|
3803
|
-
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) {
|
|
3804
3987
|
const text = readString(asRecord(part).text);
|
|
3805
3988
|
if (!text) continue;
|
|
3806
3989
|
try {
|
|
@@ -4475,7 +4658,7 @@ async function postRuntimeConnectorJsonWithRetry(config, path, payload, options
|
|
|
4475
4658
|
} catch (error) {
|
|
4476
4659
|
lastError = error;
|
|
4477
4660
|
if (attempt >= maxAttempts || !retryableRuntimeConnectorPost(error)) throw error;
|
|
4478
|
-
if (delayMs > 0) await new Promise((
|
|
4661
|
+
if (delayMs > 0) await new Promise((resolve13) => setTimeout(resolve13, delayMs));
|
|
4479
4662
|
}
|
|
4480
4663
|
}
|
|
4481
4664
|
throw lastError;
|
|
@@ -4500,7 +4683,7 @@ var bestEffortPostJson = bestEffortPostRuntimeConnectorJson;
|
|
|
4500
4683
|
|
|
4501
4684
|
// src/amaster-runtime-daemon/runtime-artifact-upload.mjs
|
|
4502
4685
|
import { createHash as createHash3 } from "node:crypto";
|
|
4503
|
-
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";
|
|
4504
4687
|
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 } from "node:path";
|
|
4505
4688
|
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
4506
4689
|
function requiredString(value, name) {
|
|
@@ -4517,10 +4700,10 @@ function pathWithin(candidate, root) {
|
|
|
4517
4700
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
4518
4701
|
}
|
|
4519
4702
|
function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
|
|
4520
|
-
const root =
|
|
4703
|
+
const root = realpathSync2(resolve3(cwd));
|
|
4521
4704
|
const uploads = /* @__PURE__ */ new Map();
|
|
4522
|
-
for (const
|
|
4523
|
-
const intent =
|
|
4705
|
+
for (const result3 of Array.isArray(mcpToolResults) ? mcpToolResults : []) {
|
|
4706
|
+
const intent = result3 && typeof result3 === "object" && !Array.isArray(result3) ? result3.artifactIntent : null;
|
|
4524
4707
|
if (!intent || typeof intent !== "object" || Array.isArray(intent)) continue;
|
|
4525
4708
|
const intentId = requiredString(intent.intentId, "intentId");
|
|
4526
4709
|
const manifestId = requiredString(intent.manifestId, "manifestId");
|
|
@@ -4538,7 +4721,7 @@ function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
|
|
|
4538
4721
|
}
|
|
4539
4722
|
const sourcePath = resolve3(root, sourceRelativePath);
|
|
4540
4723
|
const stat = lstatSync3(sourcePath);
|
|
4541
|
-
if (!stat.isFile() || stat.isSymbolicLink() || !pathWithin(
|
|
4724
|
+
if (!stat.isFile() || stat.isSymbolicLink() || !pathWithin(realpathSync2(sourcePath), root)) {
|
|
4542
4725
|
throw new Error(`Runtime Artifact ${intentId} source is not an owned regular file`);
|
|
4543
4726
|
}
|
|
4544
4727
|
const body = readFileSync5(sourcePath);
|
|
@@ -4578,8 +4761,8 @@ function createRuntimeArtifactIngestQueue({ ingest, onError }) {
|
|
|
4578
4761
|
let queue = Promise.resolve();
|
|
4579
4762
|
return {
|
|
4580
4763
|
enqueue(results) {
|
|
4581
|
-
const pending = results.filter((
|
|
4582
|
-
const intentId = readString(asRecord(
|
|
4764
|
+
const pending = results.filter((result3) => {
|
|
4765
|
+
const intentId = readString(asRecord(result3).artifactIntent?.intentId);
|
|
4583
4766
|
if (!intentId || handledIntentIds.has(intentId)) return false;
|
|
4584
4767
|
handledIntentIds.add(intentId);
|
|
4585
4768
|
return true;
|
|
@@ -4609,7 +4792,7 @@ function createRuntimeArtifactIngestQueue({ ingest, onError }) {
|
|
|
4609
4792
|
|
|
4610
4793
|
// src/amaster-runtime-daemon/workspace-guard.mjs
|
|
4611
4794
|
import { createHash as createHash4 } from "node:crypto";
|
|
4612
|
-
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";
|
|
4613
4796
|
import { basename as basename4, join as join7, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4 } from "node:path";
|
|
4614
4797
|
|
|
4615
4798
|
// src/amaster-runtime-daemon/workspace-manifest.mjs
|
|
@@ -4701,10 +4884,10 @@ function resolveWorkspaceCwd(config, command) {
|
|
|
4701
4884
|
const payload = asRecord(command.payload);
|
|
4702
4885
|
const requested = readString(payload.workspacePath);
|
|
4703
4886
|
const fallback = config.workspaceBindings[0] ?? process.cwd();
|
|
4704
|
-
const cwd =
|
|
4887
|
+
const cwd = realpathSync3(resolve4(expandHomePath(requested ?? fallback)));
|
|
4705
4888
|
const allowlist = config.workspaceBindings.flatMap((entry) => {
|
|
4706
4889
|
try {
|
|
4707
|
-
return [
|
|
4890
|
+
return [realpathSync3(resolve4(expandHomePath(entry)))];
|
|
4708
4891
|
} catch {
|
|
4709
4892
|
return [];
|
|
4710
4893
|
}
|
|
@@ -4745,7 +4928,7 @@ function workspaceLabel(sourceWorkspacePath, payload) {
|
|
|
4745
4928
|
function workspacesRoot(config) {
|
|
4746
4929
|
const root = resolve4(expandHomePath(config.runtimeWorkspacesRoot ?? "~/.amaster-employee/workspaces"));
|
|
4747
4930
|
mkdirSync5(root, { recursive: true });
|
|
4748
|
-
return
|
|
4931
|
+
return realpathSync3(root);
|
|
4749
4932
|
}
|
|
4750
4933
|
function resolveExecutionWorkspace(config, command, opts = {}) {
|
|
4751
4934
|
const sourceWorkspacePath = resolveWorkspaceCwd(config, command);
|
|
@@ -4873,7 +5056,7 @@ function planManagedWorkspaceGcDryRun(input) {
|
|
|
4873
5056
|
const ttlMs = ttlHours * 60 * 60 * 1e3;
|
|
4874
5057
|
const activeRunIds = new Set(input.activeRunIds ?? []);
|
|
4875
5058
|
const activeCommandIds = new Set(input.activeCommandIds ?? []);
|
|
4876
|
-
const
|
|
5059
|
+
const result3 = {
|
|
4877
5060
|
dryRun: true,
|
|
4878
5061
|
root,
|
|
4879
5062
|
generatedAt: now.toISOString(),
|
|
@@ -4883,31 +5066,31 @@ function planManagedWorkspaceGcDryRun(input) {
|
|
|
4883
5066
|
candidates: [],
|
|
4884
5067
|
protected: []
|
|
4885
5068
|
};
|
|
4886
|
-
if (!root || !existsSync7(root)) return
|
|
5069
|
+
if (!root || !existsSync7(root)) return result3;
|
|
4887
5070
|
for (const workdir of walkWorkdirs(root)) {
|
|
4888
5071
|
const manifest = readWorkspaceManifest(workspaceManifestPath(workdir));
|
|
4889
5072
|
if (!manifest || manifest.managed !== true) continue;
|
|
4890
5073
|
const summary = summarizeWorkdir(workdir, manifest, nowMs);
|
|
4891
|
-
|
|
5074
|
+
result3.totalWorkdirs += 1;
|
|
4892
5075
|
const lastTouchedTime = readIsoTime(summary.lastTouchedAt);
|
|
4893
5076
|
const active = Boolean(
|
|
4894
5077
|
summary.runId && activeRunIds.has(summary.runId) || summary.commandId && activeCommandIds.has(summary.commandId)
|
|
4895
5078
|
);
|
|
4896
5079
|
if (active) {
|
|
4897
|
-
|
|
5080
|
+
result3.protected.push({ ...summary, reason: "active_run_or_command" });
|
|
4898
5081
|
continue;
|
|
4899
5082
|
}
|
|
4900
5083
|
if (lastTouchedTime !== null && nowMs - lastTouchedTime < ttlMs) {
|
|
4901
|
-
|
|
5084
|
+
result3.protected.push({ ...summary, reason: "within_ttl" });
|
|
4902
5085
|
continue;
|
|
4903
5086
|
}
|
|
4904
|
-
|
|
4905
|
-
|
|
5087
|
+
result3.totalCandidateBytes += summary.sizeBytes;
|
|
5088
|
+
result3.candidates.push({
|
|
4906
5089
|
...summary,
|
|
4907
5090
|
reason: lastTouchedTime === null ? "missing_last_touched_at" : "older_than_ttl"
|
|
4908
5091
|
});
|
|
4909
5092
|
}
|
|
4910
|
-
return
|
|
5093
|
+
return result3;
|
|
4911
5094
|
}
|
|
4912
5095
|
|
|
4913
5096
|
// src/amaster-runtime-daemon/runtime-status-summary.mjs
|
|
@@ -6230,8 +6413,1724 @@ function readWorkspaceStatus(cwd, opts = {}) {
|
|
|
6230
6413
|
};
|
|
6231
6414
|
}
|
|
6232
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
|
+
|
|
6233
8132
|
// src/amaster-runtime-daemon.mjs
|
|
6234
|
-
var CONNECTOR_VERSION = "0.1.0-beta.
|
|
8133
|
+
var CONNECTOR_VERSION = "0.1.0-beta.47";
|
|
6235
8134
|
var CONNECTOR_CONTRACT_VERSION = "2026-06-04.v1";
|
|
6236
8135
|
var MAX_CHECKPOINT_BYTES = 20 * 1024 * 1024;
|
|
6237
8136
|
var CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -6256,6 +8155,56 @@ var piChildIdentityAllocator = null;
|
|
|
6256
8155
|
var piChildIdentityAllocatorKey = null;
|
|
6257
8156
|
var trustedPiRuntimeProvenanceCache = createTrustedPiRuntimeProvenanceCache();
|
|
6258
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
|
+
}
|
|
6259
8208
|
function configuredPiChildIdentityAllocator(config) {
|
|
6260
8209
|
if (!(config.piChildUidBase > 0)) return null;
|
|
6261
8210
|
const key = `${config.piChildUidBase}:${config.piChildUidSpan}`;
|
|
@@ -6279,11 +8228,11 @@ function updateActiveRunWorkspaceManifest(commandId, manifestPath, patch = {}) {
|
|
|
6279
8228
|
}
|
|
6280
8229
|
function resultOutboxPendingCount(config) {
|
|
6281
8230
|
const dir = resultOutboxDir(config);
|
|
6282
|
-
if (!
|
|
8231
|
+
if (!existsSync13(dir)) return 0;
|
|
6283
8232
|
try {
|
|
6284
8233
|
let pending = 0;
|
|
6285
8234
|
for (const file of readdirSync8(dir).filter((name) => name.endsWith(".json"))) {
|
|
6286
|
-
if (readValidResultOutboxEntryOrQuarantine(config, file,
|
|
8235
|
+
if (readValidResultOutboxEntryOrQuarantine(config, file, join14(dir, file))) {
|
|
6287
8236
|
pending += 1;
|
|
6288
8237
|
}
|
|
6289
8238
|
}
|
|
@@ -6295,15 +8244,15 @@ function resultOutboxPendingCount(config) {
|
|
|
6295
8244
|
function piCompletionOutputType(event) {
|
|
6296
8245
|
const type = readString(asRecord(event).type);
|
|
6297
8246
|
if (PI_COMPLETION_OUTPUT_TYPES.has(type ?? "")) return type;
|
|
6298
|
-
return piMcpToolResults(event).some((
|
|
8247
|
+
return piMcpToolResults(event).some((result3) => result3.status === "approval_required") ? "approval_required" : null;
|
|
6299
8248
|
}
|
|
6300
8249
|
function resultOutboxActiveRunCommands(config) {
|
|
6301
8250
|
const dir = resultOutboxDir(config);
|
|
6302
|
-
if (!
|
|
8251
|
+
if (!existsSync13(dir)) return [];
|
|
6303
8252
|
const outboxPending = resultOutboxPendingCount(config);
|
|
6304
8253
|
const entries = [];
|
|
6305
8254
|
for (const file of readdirSync8(dir).filter((name) => name.endsWith(".json")).sort()) {
|
|
6306
|
-
const entry = readValidResultOutboxEntryOrQuarantine(config, file,
|
|
8255
|
+
const entry = readValidResultOutboxEntryOrQuarantine(config, file, join14(dir, file));
|
|
6307
8256
|
if (!entry) continue;
|
|
6308
8257
|
const activeRun = asRecord(entry.activeRun);
|
|
6309
8258
|
const commandId = readString(activeRun.commandId) ?? readString(entry.commandId);
|
|
@@ -6333,12 +8282,12 @@ function resultOutboxActiveRunCommands(config) {
|
|
|
6333
8282
|
}
|
|
6334
8283
|
function resultOutboxFailedRunCommands(config) {
|
|
6335
8284
|
const dir = resultOutboxInvalidDir(config);
|
|
6336
|
-
if (!
|
|
8285
|
+
if (!existsSync13(dir)) return [];
|
|
6337
8286
|
const entries = [];
|
|
6338
8287
|
for (const file of readdirSync8(dir).filter((name) => name.endsWith(".json")).sort().slice(-100)) {
|
|
6339
8288
|
let entry;
|
|
6340
8289
|
try {
|
|
6341
|
-
entry = asRecord(JSON.parse(readFileSync10(
|
|
8290
|
+
entry = asRecord(JSON.parse(readFileSync10(join14(dir, file), "utf8")));
|
|
6342
8291
|
} catch {
|
|
6343
8292
|
continue;
|
|
6344
8293
|
}
|
|
@@ -6398,7 +8347,7 @@ function piExtraArgsDiagnostics(value) {
|
|
|
6398
8347
|
}
|
|
6399
8348
|
function safeExpandPath(value) {
|
|
6400
8349
|
const text = readString(value);
|
|
6401
|
-
return text ?
|
|
8350
|
+
return text ? resolve12(expandHomePath(text)) : null;
|
|
6402
8351
|
}
|
|
6403
8352
|
function safeJsonObjectFromFile(filePath) {
|
|
6404
8353
|
try {
|
|
@@ -6445,10 +8394,10 @@ function safeSkillRootSummary(kind, source, pathValue) {
|
|
|
6445
8394
|
try {
|
|
6446
8395
|
for (const name of readdirSync8(pathValue)) {
|
|
6447
8396
|
if (name.startsWith(".")) continue;
|
|
6448
|
-
const skillDir =
|
|
8397
|
+
const skillDir = join14(pathValue, name);
|
|
6449
8398
|
try {
|
|
6450
8399
|
if (!statSync7(skillDir).isDirectory()) continue;
|
|
6451
|
-
if (!
|
|
8400
|
+
if (!existsSync13(join14(skillDir, "SKILL.md"))) continue;
|
|
6452
8401
|
skillCount += 1;
|
|
6453
8402
|
if (skillCount >= MAX_PI_CAPABILITY_SOURCE_ENTRIES) {
|
|
6454
8403
|
truncated = true;
|
|
@@ -6499,7 +8448,7 @@ function objectKeyCount(value) {
|
|
|
6499
8448
|
return value && typeof value === "object" && !Array.isArray(value) ? Object.keys(value).length : 0;
|
|
6500
8449
|
}
|
|
6501
8450
|
function defaultPiCodingAgentDir() {
|
|
6502
|
-
return
|
|
8451
|
+
return join14(homedir3(), ".pi", "agent");
|
|
6503
8452
|
}
|
|
6504
8453
|
function piCapabilitySourcesDiagnostics() {
|
|
6505
8454
|
const configuredPiCodingAgentDir = safeExpandPath(process.env.PI_CODING_AGENT_DIR);
|
|
@@ -6508,14 +8457,14 @@ function piCapabilitySourcesDiagnostics() {
|
|
|
6508
8457
|
const configuredPiAgentHome = safeExpandPath(process.env.PI_AGENT_HOME);
|
|
6509
8458
|
const piAgentHome = configuredPiAgentHome ?? piCodingAgentDir;
|
|
6510
8459
|
const piAgentHomeSource = configuredPiAgentHome ? "PI_AGENT_HOME" : piCodingAgentDirSource;
|
|
6511
|
-
const userSkillsPath = piAgentHome ?
|
|
8460
|
+
const userSkillsPath = piAgentHome ? join14(piAgentHome, "skills") : null;
|
|
6512
8461
|
const configuredMarketplaceSkillsPath = safeExpandPath(
|
|
6513
8462
|
process.env.PI_AGENT_MARKETPLACE_SKILLS_DIR
|
|
6514
8463
|
);
|
|
6515
|
-
const marketplaceSkillsPath = configuredMarketplaceSkillsPath ?? (piAgentHome ?
|
|
8464
|
+
const marketplaceSkillsPath = configuredMarketplaceSkillsPath ?? (piAgentHome ? join14(piAgentHome, "marketplace", "skills") : null);
|
|
6516
8465
|
const builtinSkillsPath = safeExpandPath(process.env.PI_AGENT_BUILTIN_SKILLS_DIR) ?? safeExpandPath(process.env.AMASTER_BUILTIN_SKILLS);
|
|
6517
|
-
const mcpConfigPath = safeExpandPath(process.env.PI_AGENT_MCP_SERVERS_FILE) ?? (piAgentHome ?
|
|
6518
|
-
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;
|
|
6519
8468
|
const skillRoots = [
|
|
6520
8469
|
safeSkillRootSummary("user", `${piAgentHomeSource}/skills`, userSkillsPath),
|
|
6521
8470
|
safeSkillRootSummary(
|
|
@@ -6709,6 +8658,8 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
6709
8658
|
const activeRunCommandList = Array.from(activeRunCommandById.values()).map((entry) => buildActiveRunCommandStatus(config, entry));
|
|
6710
8659
|
const executorReadiness = buildExecutorReadiness(config);
|
|
6711
8660
|
const platformCredential = piAgentLocalPlatformCredentialStatus(config);
|
|
8661
|
+
const headedBrowserAuth = browserCapabilityStatus(config, "headed_browser_auth");
|
|
8662
|
+
const constrainedSourceRead = browserCapabilityStatus(config, "constrained_source_read");
|
|
6712
8663
|
return {
|
|
6713
8664
|
status: "online",
|
|
6714
8665
|
workspaceBindings: config.workspaceBindings,
|
|
@@ -6735,6 +8686,14 @@ function buildHeartbeatPayload(config, options = {}) {
|
|
|
6735
8686
|
stateFile: stateFilePath(process.env),
|
|
6736
8687
|
...executorReadiness.length > 0 ? { executorReadiness } : {},
|
|
6737
8688
|
...platformCredential ? { platformCredential } : {},
|
|
8689
|
+
constrainedSourceReader: {
|
|
8690
|
+
capability: "constrained_source_read",
|
|
8691
|
+
...constrainedSourceRead
|
|
8692
|
+
},
|
|
8693
|
+
browserSessionBroker: {
|
|
8694
|
+
capability: "headed_browser_auth",
|
|
8695
|
+
...headedBrowserAuth
|
|
8696
|
+
},
|
|
6738
8697
|
...versionDrift.versionDrift || versionDrift.bundleDrift ? versionDrift : {}
|
|
6739
8698
|
},
|
|
6740
8699
|
metadata: {
|
|
@@ -6748,13 +8707,13 @@ function piAgentLocalPlatformRunnerEnabled(config) {
|
|
|
6748
8707
|
}
|
|
6749
8708
|
function piAgentSystemDataDir(config) {
|
|
6750
8709
|
const configured = readString(config.AMASTER_PI_AGENT_SYSTEM_DATA_DIR ?? process.env.AMASTER_PI_AGENT_SYSTEM_DATA_DIR);
|
|
6751
|
-
return configured ?
|
|
8710
|
+
return configured ? resolve12(expandHomePath(configured)) : null;
|
|
6752
8711
|
}
|
|
6753
8712
|
function readPiAgentLocalPlatformCredential(credentialsDir) {
|
|
6754
|
-
const pointer = readJsonFile3(
|
|
8713
|
+
const pointer = readJsonFile3(join14(credentialsDir, "latest.json"));
|
|
6755
8714
|
const credentialRef = readString(pointer.credentialRef);
|
|
6756
8715
|
if (!credentialRef || !/^[a-f0-9]{64}$/i.test(credentialRef)) return null;
|
|
6757
|
-
const credential = readJsonFile3(
|
|
8716
|
+
const credential = readJsonFile3(join14(credentialsDir, `${credentialRef}.json`));
|
|
6758
8717
|
const organizationId = readString(credential.organizationId);
|
|
6759
8718
|
const apiKey = readString(credential.apiKey);
|
|
6760
8719
|
if (credential.version !== 1 || !organizationId || !apiKey) return null;
|
|
@@ -6770,7 +8729,7 @@ function piAgentLocalPlatformCredentials(config) {
|
|
|
6770
8729
|
if (!piAgentLocalPlatformRunnerEnabled(config)) return [];
|
|
6771
8730
|
const systemDataDir = piAgentSystemDataDir(config);
|
|
6772
8731
|
if (!systemDataDir) return [];
|
|
6773
|
-
const companiesDir =
|
|
8732
|
+
const companiesDir = join14(systemDataDir, "companies");
|
|
6774
8733
|
let entries = [];
|
|
6775
8734
|
try {
|
|
6776
8735
|
entries = readdirSync8(companiesDir, { withFileTypes: true });
|
|
@@ -6780,7 +8739,7 @@ function piAgentLocalPlatformCredentials(config) {
|
|
|
6780
8739
|
const credentialsByOrganizationId = /* @__PURE__ */ new Map();
|
|
6781
8740
|
for (const entry of entries) {
|
|
6782
8741
|
if (!entry.isDirectory()) continue;
|
|
6783
|
-
const credential = readPiAgentLocalPlatformCredential(
|
|
8742
|
+
const credential = readPiAgentLocalPlatformCredential(join14(companiesDir, entry.name, "model-credentials"));
|
|
6784
8743
|
if (credential) credentialsByOrganizationId.set(credential.organizationId, credential);
|
|
6785
8744
|
}
|
|
6786
8745
|
return [...credentialsByOrganizationId.values()];
|
|
@@ -6954,7 +8913,7 @@ AMASTER_WORKSPACE_ALLOWLIST=${quoteShell(config.workspaceBindings.join(","))}
|
|
|
6954
8913
|
AMASTER_EXECUTORS=${quoteShell(executorEnv)}
|
|
6955
8914
|
AMASTER_CAPABILITIES=${quoteShell(config.capabilities.join(","))}
|
|
6956
8915
|
AMASTER_NETWORK_DOMAINS=${quoteShell(config.networkDomains.join(","))}
|
|
6957
|
-
AMASTER_DAEMON_STATE_FILE=${quoteShell(
|
|
8916
|
+
AMASTER_DAEMON_STATE_FILE=${quoteShell(join14(homedir3(), ".amaster-employee", "runtime-connector-state.json"))}
|
|
6958
8917
|
EOF
|
|
6959
8918
|
|
|
6960
8919
|
set -a
|
|
@@ -7142,10 +9101,10 @@ function buildActiveRunCommandStatus(config, entry) {
|
|
|
7142
9101
|
const base = {
|
|
7143
9102
|
...entry,
|
|
7144
9103
|
phase: readString(entry.phase) ?? "executing",
|
|
7145
|
-
managedWorkdirPresent: entry.workspacePath ?
|
|
9104
|
+
managedWorkdirPresent: entry.workspacePath ? existsSync13(entry.workspacePath) : false,
|
|
7146
9105
|
outboxPending: resultOutboxPendingCount(config)
|
|
7147
9106
|
};
|
|
7148
|
-
if (!entry.workspacePath || !
|
|
9107
|
+
if (!entry.workspacePath || !existsSync13(entry.workspacePath)) return base;
|
|
7149
9108
|
const manifestPath = entry.manifestPath ?? workspaceManifestPath(entry.workspacePath);
|
|
7150
9109
|
const status = readWorkspaceStatus(entry.workspacePath, { hashCache: workspaceStatusHashCache });
|
|
7151
9110
|
const artifactCandidates = status.artifacts.slice(0, 20);
|
|
@@ -7399,7 +9358,7 @@ function safeAgentInstructionMaterializationTarget(workspace, filePath) {
|
|
|
7399
9358
|
const segments = normalized.split("/").filter(Boolean);
|
|
7400
9359
|
if (segments.length === 0 || segments.some((segment) => segment === "." || segment === "..")) return null;
|
|
7401
9360
|
const relativePath = segments.join("/");
|
|
7402
|
-
const targetPath =
|
|
9361
|
+
const targetPath = resolve12(workspace.cwd, relativePath);
|
|
7403
9362
|
if (!pathWithin2(targetPath, workspace.cwd)) return null;
|
|
7404
9363
|
return { relativePath, targetPath };
|
|
7405
9364
|
}
|
|
@@ -7471,6 +9430,8 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
7471
9430
|
sourceWorkspacePath: workspaceContext.sourceWorkspacePath,
|
|
7472
9431
|
wakeReason: readString(context.wakeReason),
|
|
7473
9432
|
hasGovernedMcp,
|
|
9433
|
+
executorKind: options.executorKind,
|
|
9434
|
+
managedMcpToolMode: options.managedMcpToolMode,
|
|
7474
9435
|
agentInstructions,
|
|
7475
9436
|
taskMarkdown,
|
|
7476
9437
|
attachmentsText,
|
|
@@ -7481,16 +9442,16 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
7481
9442
|
}
|
|
7482
9443
|
function companyPiHomeRoot(baseEnv) {
|
|
7483
9444
|
const explicitRoot = readString(baseEnv.AMASTER_COMPANY_PI_HOME_ROOT);
|
|
7484
|
-
if (explicitRoot) return
|
|
9445
|
+
if (explicitRoot) return resolve12(expandHomePath(explicitRoot));
|
|
7485
9446
|
const configuredPiHome = readString(baseEnv.PI_AGENT_HOME) ?? readString(baseEnv.PI_CODING_AGENT_DIR);
|
|
7486
|
-
if (configuredPiHome) return
|
|
7487
|
-
return
|
|
9447
|
+
if (configuredPiHome) return join14(dirname9(resolve12(expandHomePath(configuredPiHome))), "companies");
|
|
9448
|
+
return join14(homedir3(), ".amaster-employee", "companies");
|
|
7488
9449
|
}
|
|
7489
9450
|
function companyPiAgentHome(baseEnv, companyId) {
|
|
7490
9451
|
const rawCompanyId = readString(companyId);
|
|
7491
9452
|
if (!rawCompanyId) return null;
|
|
7492
9453
|
const segment = safeCompanyPiHomeSegment(rawCompanyId);
|
|
7493
|
-
return
|
|
9454
|
+
return join14(companyPiHomeRoot(baseEnv), segment, ".pi");
|
|
7494
9455
|
}
|
|
7495
9456
|
function commandUsesPiExecutor(command) {
|
|
7496
9457
|
return readString(asRecord(command.payload).executorKind) === "pi";
|
|
@@ -7557,9 +9518,9 @@ function resolveNativeSessionRequest(command, workspace) {
|
|
|
7557
9518
|
let cwdMatched = false;
|
|
7558
9519
|
if (requestedCwd && sourceWorkspacePath) {
|
|
7559
9520
|
try {
|
|
7560
|
-
cwdMatched =
|
|
9521
|
+
cwdMatched = realpathSync4(requestedCwd) === realpathSync4(sourceWorkspacePath);
|
|
7561
9522
|
} catch {
|
|
7562
|
-
cwdMatched =
|
|
9523
|
+
cwdMatched = resolve12(requestedCwd) === resolve12(sourceWorkspacePath);
|
|
7563
9524
|
}
|
|
7564
9525
|
}
|
|
7565
9526
|
const used = Boolean(enabled && requested && sessionId && requestedCwd && cwdMatched);
|
|
@@ -7981,13 +9942,13 @@ function sampleProcessGroupRssBytes(processGroupId) {
|
|
|
7981
9942
|
if (process.platform === "win32" || processGroupId === null) return null;
|
|
7982
9943
|
const now = Date.now();
|
|
7983
9944
|
if (!processGroupRssSampleCache || now - processGroupRssSampleCache.sampledAtMs >= PROCESS_GROUP_RSS_SAMPLE_MIN_INTERVAL_MS) {
|
|
7984
|
-
const
|
|
9945
|
+
const result3 = spawnSync6("ps", ["-axo", "pgid=,rss="], {
|
|
7985
9946
|
encoding: "utf8",
|
|
7986
9947
|
stdio: ["ignore", "pipe", "ignore"]
|
|
7987
9948
|
});
|
|
7988
|
-
if (
|
|
9949
|
+
if (result3.status !== 0) return null;
|
|
7989
9950
|
const rssKbByProcessGroup = /* @__PURE__ */ new Map();
|
|
7990
|
-
for (const line of String(
|
|
9951
|
+
for (const line of String(result3.stdout ?? "").split(/\r?\n/)) {
|
|
7991
9952
|
const match = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
7992
9953
|
if (!match) continue;
|
|
7993
9954
|
const pgid = Number.parseInt(match[1], 10);
|
|
@@ -8002,27 +9963,27 @@ function sampleProcessGroupRssBytes(processGroupId) {
|
|
|
8002
9963
|
}
|
|
8003
9964
|
function realOrResolvedPath(value) {
|
|
8004
9965
|
try {
|
|
8005
|
-
return
|
|
9966
|
+
return realpathSync4(value);
|
|
8006
9967
|
} catch {
|
|
8007
|
-
return
|
|
9968
|
+
return resolve12(value);
|
|
8008
9969
|
}
|
|
8009
9970
|
}
|
|
8010
|
-
var LSOF_COMMAND = process.platform === "darwin" &&
|
|
9971
|
+
var LSOF_COMMAND = process.platform === "darwin" && existsSync13("/usr/sbin/lsof") ? "/usr/sbin/lsof" : "lsof";
|
|
8011
9972
|
function processCwdForPid(pid) {
|
|
8012
9973
|
if (process.platform === "linux") {
|
|
8013
9974
|
try {
|
|
8014
|
-
return
|
|
9975
|
+
return realpathSync4(`/proc/${pid}/cwd`);
|
|
8015
9976
|
} catch {
|
|
8016
9977
|
return null;
|
|
8017
9978
|
}
|
|
8018
9979
|
}
|
|
8019
9980
|
if (process.platform === "darwin") {
|
|
8020
|
-
const
|
|
9981
|
+
const result3 = spawnSync6(LSOF_COMMAND, ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
|
|
8021
9982
|
encoding: "utf8",
|
|
8022
9983
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8023
9984
|
});
|
|
8024
|
-
if (
|
|
8025
|
-
const cwdLine = String(
|
|
9985
|
+
if (result3.status !== 0) return null;
|
|
9986
|
+
const cwdLine = String(result3.stdout ?? "").split(/\r?\n/).find((line) => line.startsWith("n"));
|
|
8026
9987
|
const cwd = cwdLine?.slice(1);
|
|
8027
9988
|
return cwd ? realOrResolvedPath(cwd) : null;
|
|
8028
9989
|
}
|
|
@@ -8047,13 +10008,13 @@ function allProcessCwdsByPid() {
|
|
|
8047
10008
|
return cwds;
|
|
8048
10009
|
}
|
|
8049
10010
|
if (process.platform === "darwin") {
|
|
8050
|
-
const
|
|
10011
|
+
const result3 = spawnSync6(LSOF_COMMAND, ["-nP", "-d", "cwd", "-Fp", "-Fn"], {
|
|
8051
10012
|
encoding: "utf8",
|
|
8052
10013
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8053
10014
|
});
|
|
8054
|
-
if (
|
|
10015
|
+
if (result3.status !== 0) return cwds;
|
|
8055
10016
|
let currentPid = null;
|
|
8056
|
-
for (const line of String(
|
|
10017
|
+
for (const line of String(result3.stdout ?? "").split(/\r?\n/)) {
|
|
8057
10018
|
if (line.startsWith("p")) {
|
|
8058
10019
|
const parsed = Number.parseInt(line.slice(1), 10);
|
|
8059
10020
|
currentPid = Number.isFinite(parsed) ? parsed : null;
|
|
@@ -8068,13 +10029,13 @@ function allProcessCwdsByPid() {
|
|
|
8068
10029
|
}
|
|
8069
10030
|
function allProcessRows() {
|
|
8070
10031
|
if (process.platform === "win32") return [];
|
|
8071
|
-
const
|
|
10032
|
+
const result3 = spawnSync6("ps", ["-axo", "pid=,pgid=,command="], {
|
|
8072
10033
|
encoding: "utf8",
|
|
8073
10034
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8074
10035
|
});
|
|
8075
|
-
if (
|
|
10036
|
+
if (result3.status !== 0) return [];
|
|
8076
10037
|
const rows = [];
|
|
8077
|
-
for (const line of String(
|
|
10038
|
+
for (const line of String(result3.stdout ?? "").split(/\r?\n/)) {
|
|
8078
10039
|
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
8079
10040
|
if (!match) continue;
|
|
8080
10041
|
const pid = Number.parseInt(match[1], 10);
|
|
@@ -8129,7 +10090,7 @@ function killWorkspaceResidentProcesses(cwd, processGroupId, options = {}) {
|
|
|
8129
10090
|
}
|
|
8130
10091
|
function walkManagedWorkdirs(root) {
|
|
8131
10092
|
const workdirs = [];
|
|
8132
|
-
if (!root || !
|
|
10093
|
+
if (!root || !existsSync13(root)) return workdirs;
|
|
8133
10094
|
const stack = [root];
|
|
8134
10095
|
while (stack.length > 0) {
|
|
8135
10096
|
const current = stack.pop();
|
|
@@ -8142,8 +10103,8 @@ function walkManagedWorkdirs(root) {
|
|
|
8142
10103
|
}
|
|
8143
10104
|
for (const entry of entries) {
|
|
8144
10105
|
if (!entry.isDirectory()) continue;
|
|
8145
|
-
const fullPath =
|
|
8146
|
-
if (entry.name === "workdir" &&
|
|
10106
|
+
const fullPath = join14(current, entry.name);
|
|
10107
|
+
if (entry.name === "workdir" && existsSync13(workspaceManifestPath(fullPath))) {
|
|
8147
10108
|
workdirs.push(fullPath);
|
|
8148
10109
|
continue;
|
|
8149
10110
|
}
|
|
@@ -8181,7 +10142,7 @@ function manifestMatchesActiveRuntimeRef(manifest, refs, workdir = null) {
|
|
|
8181
10142
|
}
|
|
8182
10143
|
function buildOrphanReaperSample(root, workdir, manifest, residents) {
|
|
8183
10144
|
const relativeWorkdir = (() => {
|
|
8184
|
-
const value =
|
|
10145
|
+
const value = relative9(root, workdir);
|
|
8185
10146
|
return value && !value.startsWith("..") && !isAbsolute8(value) ? value : basename6(workdir);
|
|
8186
10147
|
})();
|
|
8187
10148
|
return {
|
|
@@ -8294,7 +10255,7 @@ function runExecutor(command, args, options) {
|
|
|
8294
10255
|
...spawnInvocation.spawnIdentity
|
|
8295
10256
|
});
|
|
8296
10257
|
const processGroupId = processGroupIdForChild(child);
|
|
8297
|
-
const finish = (
|
|
10258
|
+
const finish = (result3) => {
|
|
8298
10259
|
if (settled) return;
|
|
8299
10260
|
settled = true;
|
|
8300
10261
|
if (timer) clearTimeout(timer);
|
|
@@ -8313,7 +10274,7 @@ function runExecutor(command, args, options) {
|
|
|
8313
10274
|
completionOutputType,
|
|
8314
10275
|
killedWorkspaceResidents,
|
|
8315
10276
|
...outputDrainForcedClosed ? { outputDrainForcedClosed: true } : {},
|
|
8316
|
-
...
|
|
10277
|
+
...result3
|
|
8317
10278
|
});
|
|
8318
10279
|
};
|
|
8319
10280
|
const scheduleStopKill = () => {
|
|
@@ -8654,12 +10615,12 @@ function resultOutboxActiveRunSnapshot(command) {
|
|
|
8654
10615
|
...readString(current.childExitSignal) ? { childExitSignal: readString(current.childExitSignal) } : {}
|
|
8655
10616
|
};
|
|
8656
10617
|
}
|
|
8657
|
-
async function completeCommand(config, command, status,
|
|
10618
|
+
async function completeCommand(config, command, status, result3, error) {
|
|
8658
10619
|
const connectorId = requireConnectorId(config);
|
|
8659
10620
|
const payload = {
|
|
8660
10621
|
leaseId: command.leaseId,
|
|
8661
10622
|
status,
|
|
8662
|
-
result:
|
|
10623
|
+
result: result3,
|
|
8663
10624
|
...error ? { error: truncateText(error, 4e3) } : {}
|
|
8664
10625
|
};
|
|
8665
10626
|
const path = `/api/amaster/runtime-connectors/${connectorId}/commands/${command.commandId}/result`;
|
|
@@ -8681,11 +10642,11 @@ async function completeCommand(config, command, status, result2, error) {
|
|
|
8681
10642
|
}
|
|
8682
10643
|
function resultOutboxDir(config) {
|
|
8683
10644
|
const explicit = readString(process.env.AMASTER_RESULT_OUTBOX_DIR);
|
|
8684
|
-
if (explicit) return
|
|
8685
|
-
return
|
|
10645
|
+
if (explicit) return resolve12(expandHomePath(explicit));
|
|
10646
|
+
return join14(dirname9(stateFilePath(process.env)), "result-outbox");
|
|
8686
10647
|
}
|
|
8687
10648
|
function resultOutboxInvalidDir(config) {
|
|
8688
|
-
return
|
|
10649
|
+
return join14(resultOutboxDir(config), "invalid");
|
|
8689
10650
|
}
|
|
8690
10651
|
function writeResultOutboxEntry(config, entry) {
|
|
8691
10652
|
const dir = resultOutboxDir(config);
|
|
@@ -8697,13 +10658,13 @@ function writeResultOutboxEntry(config, entry) {
|
|
|
8697
10658
|
lastAttemptAt: null,
|
|
8698
10659
|
...entry
|
|
8699
10660
|
};
|
|
8700
|
-
writeFileSync8(
|
|
10661
|
+
writeFileSync8(join14(dir, resultOutboxFileName(entry.commandId)), `${JSON.stringify(body, null, 2)}
|
|
8701
10662
|
`, { mode: 384 });
|
|
8702
10663
|
}
|
|
8703
10664
|
function moveResultOutboxEntryToInvalid(config, file, fullPath, reason, detail, original) {
|
|
8704
10665
|
const invalidDir = resultOutboxInvalidDir(config);
|
|
8705
10666
|
mkdirSync8(invalidDir, { recursive: true });
|
|
8706
|
-
const invalidPath =
|
|
10667
|
+
const invalidPath = join14(invalidDir, file);
|
|
8707
10668
|
if (original === void 0) {
|
|
8708
10669
|
try {
|
|
8709
10670
|
renameSync5(fullPath, invalidPath);
|
|
@@ -8770,7 +10731,7 @@ function quarantineResultOutboxEntry(config, fullPath, file, entry, reason, err)
|
|
|
8770
10731
|
...typeof err?.httpStatus === "number" ? { httpStatus: err.httpStatus } : {},
|
|
8771
10732
|
lastError: truncateText(err instanceof Error ? err.message : String(err), 1e3)
|
|
8772
10733
|
};
|
|
8773
|
-
const invalidPath =
|
|
10734
|
+
const invalidPath = join14(invalidDir, file);
|
|
8774
10735
|
writeFileSync8(invalidPath, `${JSON.stringify(body, null, 2)}
|
|
8775
10736
|
`, { mode: 384 });
|
|
8776
10737
|
try {
|
|
@@ -8784,11 +10745,11 @@ function quarantineResultOutboxEntry(config, fullPath, file, entry, reason, err)
|
|
|
8784
10745
|
}
|
|
8785
10746
|
async function flushResultOutbox(config) {
|
|
8786
10747
|
const dir = resultOutboxDir(config);
|
|
8787
|
-
if (!
|
|
10748
|
+
if (!existsSync13(dir)) return { attempted: 0, completed: 0 };
|
|
8788
10749
|
const files = readdirSync8(dir).filter((name) => name.endsWith(".json")).sort();
|
|
8789
10750
|
let completed = 0;
|
|
8790
10751
|
for (const file of files) {
|
|
8791
|
-
const fullPath =
|
|
10752
|
+
const fullPath = join14(dir, file);
|
|
8792
10753
|
const entry = readValidResultOutboxEntryOrQuarantine(config, file, fullPath);
|
|
8793
10754
|
if (!entry) continue;
|
|
8794
10755
|
try {
|
|
@@ -8985,7 +10946,7 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
8985
10946
|
runtimeAuth,
|
|
8986
10947
|
issueId
|
|
8987
10948
|
);
|
|
8988
|
-
const targetDir =
|
|
10949
|
+
const targetDir = join14(workspace.cwd, "input-attachments");
|
|
8989
10950
|
mkdirSync8(targetDir, { recursive: true });
|
|
8990
10951
|
const usedFilenames = /* @__PURE__ */ new Set();
|
|
8991
10952
|
const materialized = [];
|
|
@@ -9000,11 +10961,11 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
9000
10961
|
filename = `${stem}-${index + 1}${ext}`;
|
|
9001
10962
|
}
|
|
9002
10963
|
usedFilenames.add(filename);
|
|
9003
|
-
const targetPath =
|
|
10964
|
+
const targetPath = join14(targetDir, filename);
|
|
9004
10965
|
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
9005
10966
|
writeFileSync8(targetPath, body);
|
|
9006
10967
|
const attachmentId = readString(attachment.id);
|
|
9007
|
-
const actualSha256 =
|
|
10968
|
+
const actualSha256 = createHash12("sha256").update(body).digest("hex");
|
|
9008
10969
|
const lineageCandidates = lineageCandidatesByAttachmentId.get(attachmentId) ?? [];
|
|
9009
10970
|
const lineage = selectAttachmentLineage(lineageCandidates, actualSha256);
|
|
9010
10971
|
if (lineageCandidates.length > 0 && !lineage) {
|
|
@@ -9027,7 +10988,7 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
9027
10988
|
id: attachmentId,
|
|
9028
10989
|
name: readString(attachment.originalFilename) ?? filename,
|
|
9029
10990
|
path: targetPath,
|
|
9030
|
-
relativePath:
|
|
10991
|
+
relativePath: relative9(workspace.cwd, targetPath),
|
|
9031
10992
|
contentType: readString(attachment.contentType),
|
|
9032
10993
|
byteSize: body.byteLength,
|
|
9033
10994
|
contentPath,
|
|
@@ -9076,7 +11037,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9076
11037
|
if (manifest.version !== 1 || !Array.isArray(manifest.entries)) {
|
|
9077
11038
|
throw new Error("artifact_input_manifest_invalid: expected version 1 entries array");
|
|
9078
11039
|
}
|
|
9079
|
-
const targetRoot =
|
|
11040
|
+
const targetRoot = join14(workspace.cwd, "input-artifacts");
|
|
9080
11041
|
rmSync7(targetRoot, { recursive: true, force: true });
|
|
9081
11042
|
mkdirSync8(targetRoot, { recursive: true });
|
|
9082
11043
|
const usedPaths = /* @__PURE__ */ new Set();
|
|
@@ -9085,10 +11046,10 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9085
11046
|
const entry = asRecord(rawEntry);
|
|
9086
11047
|
const workProductId = readString(entry.workProductId);
|
|
9087
11048
|
const attachmentId = readString(entry.attachmentId);
|
|
9088
|
-
const
|
|
11049
|
+
const sha2563 = readString(entry.sha256);
|
|
9089
11050
|
const contentPath = readString(entry.contentPath);
|
|
9090
11051
|
const byteSize = readNumber(entry.byteSize, null);
|
|
9091
|
-
if (!workProductId || !attachmentId || !/^[a-f0-9]{64}$/i.test(
|
|
11052
|
+
if (!workProductId || !attachmentId || !/^[a-f0-9]{64}$/i.test(sha2563 ?? "") || !contentPath || byteSize === null) {
|
|
9092
11053
|
throw new Error(`artifact_input_manifest_invalid: entry ${index} is incomplete`);
|
|
9093
11054
|
}
|
|
9094
11055
|
const expectedContentPath = `/api/attachments/${attachmentId}/content`;
|
|
@@ -9096,10 +11057,10 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9096
11057
|
throw new Error(`artifact_input_manifest_invalid: entry ${index} contentPath does not match attachmentId`);
|
|
9097
11058
|
}
|
|
9098
11059
|
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
9099
|
-
const actualSha256 =
|
|
9100
|
-
if (body.byteLength !== byteSize || actualSha256 !==
|
|
11060
|
+
const actualSha256 = createHash12("sha256").update(body).digest("hex");
|
|
11061
|
+
if (body.byteLength !== byteSize || actualSha256 !== sha2563) {
|
|
9101
11062
|
throw new Error(
|
|
9102
|
-
`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}`
|
|
9103
11064
|
);
|
|
9104
11065
|
}
|
|
9105
11066
|
const sourceDir = safeArtifactInputSourceDir(entry, index);
|
|
@@ -9107,15 +11068,15 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9107
11068
|
id: attachmentId,
|
|
9108
11069
|
originalFilename: readString(entry.originalFilename)
|
|
9109
11070
|
}, index);
|
|
9110
|
-
let relativePath =
|
|
11071
|
+
let relativePath = join14("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
9111
11072
|
if (usedPaths.has(relativePath)) {
|
|
9112
11073
|
const ext = extname2(filename);
|
|
9113
11074
|
const stem = ext ? filename.slice(0, -ext.length) : filename;
|
|
9114
11075
|
filename = `${stem}-${index + 1}${ext}`;
|
|
9115
|
-
relativePath =
|
|
11076
|
+
relativePath = join14("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
9116
11077
|
}
|
|
9117
11078
|
usedPaths.add(relativePath);
|
|
9118
|
-
const targetPath =
|
|
11079
|
+
const targetPath = join14(workspace.cwd, relativePath);
|
|
9119
11080
|
mkdirSync8(dirname9(targetPath), { recursive: true });
|
|
9120
11081
|
writeFileSync8(targetPath, body);
|
|
9121
11082
|
chmodSync6(targetPath, 292);
|
|
@@ -9129,7 +11090,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9129
11090
|
relativePath,
|
|
9130
11091
|
contentType: readString(entry.contentType),
|
|
9131
11092
|
byteSize: body.byteLength,
|
|
9132
|
-
sha256,
|
|
11093
|
+
sha256: sha2563,
|
|
9133
11094
|
contentPath
|
|
9134
11095
|
});
|
|
9135
11096
|
}
|
|
@@ -9137,7 +11098,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9137
11098
|
version: 1,
|
|
9138
11099
|
entries: materialized.map(({ path: _path, relativePath, ...entry }) => ({ ...entry, path: relativePath }))
|
|
9139
11100
|
};
|
|
9140
|
-
const manifestPath =
|
|
11101
|
+
const manifestPath = join14(targetRoot, "artifact-input-manifest.json");
|
|
9141
11102
|
writeFileSync8(manifestPath, JSON.stringify(localManifest, null, 2) + "\n");
|
|
9142
11103
|
chmodSync6(manifestPath, 292);
|
|
9143
11104
|
updateWorkspaceManifest(workspaceManifestPath(workspace), { artifactInputManifest: localManifest });
|
|
@@ -9148,11 +11109,11 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
|
9148
11109
|
return materialized;
|
|
9149
11110
|
}
|
|
9150
11111
|
function issueCheckpointDir(workspace) {
|
|
9151
|
-
return
|
|
11112
|
+
return join14(dirname9(workspace.runDir), "checkpoint");
|
|
9152
11113
|
}
|
|
9153
11114
|
async function clearIssueCheckpoint(config, command, workspace, reason) {
|
|
9154
11115
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
9155
|
-
if (!
|
|
11116
|
+
if (!existsSync13(checkpointDir)) return false;
|
|
9156
11117
|
rmSync7(checkpointDir, { recursive: true, force: true });
|
|
9157
11118
|
await ingestLog(config, command, "system", "info", "Cleared issue continuation checkpoint", { reason });
|
|
9158
11119
|
return true;
|
|
@@ -9166,12 +11127,12 @@ function safeCheckpointRelativePath(rawPath) {
|
|
|
9166
11127
|
return normalized;
|
|
9167
11128
|
}
|
|
9168
11129
|
function hashFileSha256(filePath) {
|
|
9169
|
-
return
|
|
11130
|
+
return createHash12("sha256").update(readFileSync10(filePath)).digest("hex");
|
|
9170
11131
|
}
|
|
9171
11132
|
async function materializeIssueCheckpoint(config, command, workspace) {
|
|
9172
11133
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
9173
|
-
const manifestPath =
|
|
9174
|
-
if (!
|
|
11134
|
+
const manifestPath = join14(checkpointDir, "manifest.json");
|
|
11135
|
+
if (!existsSync13(manifestPath)) return [];
|
|
9175
11136
|
let manifest;
|
|
9176
11137
|
try {
|
|
9177
11138
|
manifest = asRecord(JSON.parse(readFileSync10(manifestPath, "utf8")));
|
|
@@ -9187,21 +11148,21 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
9187
11148
|
try {
|
|
9188
11149
|
const files = Array.isArray(manifest.files) ? manifest.files : [];
|
|
9189
11150
|
if (files.length > 20) throw new Error("issue checkpoint manifest exceeds the limit of 20 files");
|
|
9190
|
-
const filesRoot =
|
|
9191
|
-
const workspaceRoot =
|
|
11151
|
+
const filesRoot = realpathSync4(join14(checkpointDir, "files"));
|
|
11152
|
+
const workspaceRoot = realpathSync4(workspace.cwd);
|
|
9192
11153
|
const validated = [];
|
|
9193
11154
|
let totalBytes = 0;
|
|
9194
11155
|
for (const rawFile of files) {
|
|
9195
11156
|
const file = asRecord(rawFile);
|
|
9196
11157
|
const relativePath = safeCheckpointRelativePath(readString(file.path));
|
|
9197
11158
|
if (!relativePath) throw new Error("issue checkpoint manifest contains an invalid file path");
|
|
9198
|
-
const sourceCandidate =
|
|
9199
|
-
const target =
|
|
11159
|
+
const sourceCandidate = resolve12(filesRoot, relativePath);
|
|
11160
|
+
const target = resolve12(workspaceRoot, relativePath);
|
|
9200
11161
|
if (!pathWithin2(sourceCandidate, filesRoot) || !pathWithin2(target, workspaceRoot)) {
|
|
9201
11162
|
throw new Error(`issue checkpoint path escapes its workspace: ${relativePath}`);
|
|
9202
11163
|
}
|
|
9203
|
-
if (!
|
|
9204
|
-
const source =
|
|
11164
|
+
if (!existsSync13(sourceCandidate)) throw new Error(`issue checkpoint file is missing: ${relativePath}`);
|
|
11165
|
+
const source = realpathSync4(sourceCandidate);
|
|
9205
11166
|
if (!pathWithin2(source, filesRoot) || !statSync7(source).isFile()) {
|
|
9206
11167
|
throw new Error(`issue checkpoint source escapes its storage root: ${relativePath}`);
|
|
9207
11168
|
}
|
|
@@ -9226,7 +11187,7 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
9226
11187
|
if (err?.code !== "ENOENT") throw err;
|
|
9227
11188
|
}
|
|
9228
11189
|
mkdirSync8(dirname9(target), { recursive: true });
|
|
9229
|
-
const targetParent =
|
|
11190
|
+
const targetParent = realpathSync4(dirname9(target));
|
|
9230
11191
|
if (!pathWithin2(targetParent, workspaceRoot)) {
|
|
9231
11192
|
throw new Error(`issue checkpoint target escapes its workspace: ${relativePath}`);
|
|
9232
11193
|
}
|
|
@@ -9254,23 +11215,23 @@ async function materializeIssueCheckpoint(config, command, workspace) {
|
|
|
9254
11215
|
}
|
|
9255
11216
|
async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
9256
11217
|
const checkpointDir = issueCheckpointDir(workspace);
|
|
9257
|
-
const filesDir =
|
|
11218
|
+
const filesDir = join14(checkpointDir, "files");
|
|
9258
11219
|
rmSync7(checkpointDir, { recursive: true, force: true });
|
|
9259
11220
|
mkdirSync8(filesDir, { recursive: true });
|
|
9260
11221
|
const files = [];
|
|
9261
11222
|
let totalBytes = 0;
|
|
9262
|
-
const workspaceRoot =
|
|
11223
|
+
const workspaceRoot = realpathSync4(workspace.cwd);
|
|
9263
11224
|
for (const candidate of candidates.slice(0, 20)) {
|
|
9264
11225
|
const relativePath = safeCheckpointRelativePath(candidate.rawPath);
|
|
9265
11226
|
const source = readString(candidate.filePath);
|
|
9266
|
-
if (!relativePath || !source || !
|
|
9267
|
-
const ownedSource =
|
|
11227
|
+
if (!relativePath || !source || !existsSync13(source) || !statSync7(source).isFile()) continue;
|
|
11228
|
+
const ownedSource = realpathSync4(source);
|
|
9268
11229
|
if (!pathWithin2(ownedSource, workspaceRoot)) {
|
|
9269
11230
|
throw new Error(`issue checkpoint source escapes its execution workspace: ${relativePath}`);
|
|
9270
11231
|
}
|
|
9271
11232
|
const byteSize = statSync7(ownedSource).size;
|
|
9272
11233
|
if (byteSize <= 0 || totalBytes + byteSize > MAX_CHECKPOINT_BYTES) continue;
|
|
9273
|
-
const target =
|
|
11234
|
+
const target = resolve12(filesDir, relativePath);
|
|
9274
11235
|
if (!pathWithin2(target, filesDir)) continue;
|
|
9275
11236
|
mkdirSync8(dirname9(target), { recursive: true });
|
|
9276
11237
|
copyFileSync3(ownedSource, target);
|
|
@@ -9290,7 +11251,7 @@ async function saveIssueCheckpoint(config, command, workspace, candidates) {
|
|
|
9290
11251
|
totalBytes,
|
|
9291
11252
|
files
|
|
9292
11253
|
};
|
|
9293
|
-
writeFileSync8(
|
|
11254
|
+
writeFileSync8(join14(checkpointDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
9294
11255
|
`);
|
|
9295
11256
|
await ingestLog(config, command, "system", "warn", `Saved ${files.length} required deliverable(s) for continuation recovery`, manifest);
|
|
9296
11257
|
return manifest;
|
|
@@ -9352,6 +11313,7 @@ async function executeRunCommand(config, command) {
|
|
|
9352
11313
|
await ingestWorkspaceStatus(config, command, cwd);
|
|
9353
11314
|
const promptCompilation = buildCommandPrompt(command, workspace, materializedAttachments, {
|
|
9354
11315
|
executorKind: executor.kind,
|
|
11316
|
+
managedMcpToolMode: executor.kind === "pi" && Object.keys(governedMcp).length > 0 ? MANAGED_PI_MCP_TOOL_MODE : null,
|
|
9355
11317
|
artifactVerifierCommands: config.artifactVerifierCommands
|
|
9356
11318
|
});
|
|
9357
11319
|
const nativeSessionRequest = asRecord(asRecord(command.payload).nativeSession);
|
|
@@ -9469,7 +11431,7 @@ async function executeRunCommand(config, command) {
|
|
|
9469
11431
|
executorEnv = {
|
|
9470
11432
|
...executorEnv,
|
|
9471
11433
|
AMASTER_MANAGED_RUNTIME_ASSERTION_FD: "3",
|
|
9472
|
-
AMASTER_MANAGED_RUNTIME_AUDIT_FILE:
|
|
11434
|
+
AMASTER_MANAGED_RUNTIME_AUDIT_FILE: join14(
|
|
9473
11435
|
managedMcpProfile.env.HOME,
|
|
9474
11436
|
".amaster-managed-runtime-audit.jsonl"
|
|
9475
11437
|
),
|
|
@@ -9719,12 +11681,12 @@ async function executeRunCommand(config, command) {
|
|
|
9719
11681
|
config,
|
|
9720
11682
|
command,
|
|
9721
11683
|
cwd,
|
|
9722
|
-
mcpToolResults.filter((
|
|
9723
|
-
const intentId = readString(asRecord(
|
|
11684
|
+
mcpToolResults.filter((result4) => {
|
|
11685
|
+
const intentId = readString(asRecord(result4).artifactIntent?.intentId);
|
|
9724
11686
|
return !intentId || !runtimeArtifactIngest.hasHandled(intentId);
|
|
9725
11687
|
})
|
|
9726
11688
|
));
|
|
9727
|
-
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");
|
|
9728
11690
|
if (shouldPreserveNativeSession) {
|
|
9729
11691
|
try {
|
|
9730
11692
|
const preserveSessionRollout = executor.kind === "pi" ? preserveManagedPiSessionRollout : preserveManagedCodexSessionRollout;
|
|
@@ -9738,7 +11700,7 @@ async function executeRunCommand(config, command) {
|
|
|
9738
11700
|
workspace,
|
|
9739
11701
|
workspaceStatus.artifacts.map((artifact) => ({
|
|
9740
11702
|
rawPath: artifact.relativePath,
|
|
9741
|
-
filePath:
|
|
11703
|
+
filePath: resolve12(cwd, artifact.relativePath)
|
|
9742
11704
|
}))
|
|
9743
11705
|
);
|
|
9744
11706
|
nativeSessionRollout = {
|
|
@@ -9801,12 +11763,13 @@ async function executeRunCommand(config, command) {
|
|
|
9801
11763
|
);
|
|
9802
11764
|
}
|
|
9803
11765
|
const parsedForValidation = cleanupDisposition ? { ...parsed, errorMessage: null } : parsed;
|
|
11766
|
+
const piTurnLimitFailure = executor.kind === "pi" && !cancelled && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError ? classifyPiTurnLimitResult(parsedForValidation) : null;
|
|
9804
11767
|
const piInvalidOutputError = executor.kind === "pi" ? piOutputValidationError(parsedForValidation, {
|
|
9805
11768
|
allowMissingTurnEnd: completionOutputStopped || Boolean(cleanupDisposition),
|
|
9806
11769
|
allowMissingAssistantOutput: execution.completionOutputType === "approval_required"
|
|
9807
11770
|
}) : null;
|
|
9808
11771
|
const piProviderFailure = executor.kind === "pi" && !cancelled && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError ? classifyPiProviderError(parsedForValidation) : null;
|
|
9809
|
-
const parsedErrorMessage = outputFloodError ?? memoryLimitError ?? piInvalidOutputError ?? nativeSessionRolloutError ?? nativeSessionRolloutCleanupError ?? parsedForValidation.errorMessage;
|
|
11772
|
+
const parsedErrorMessage = outputFloodError ?? memoryLimitError ?? piTurnLimitFailure?.message ?? piInvalidOutputError ?? nativeSessionRolloutError ?? nativeSessionRolloutCleanupError ?? parsedForValidation.errorMessage;
|
|
9810
11773
|
const codexTransientFailure = executor.kind === "codex" && (execution.exitCode ?? 0) !== 0 ? classifyCodexTransientUpstreamError({
|
|
9811
11774
|
stdout: execution.stdout,
|
|
9812
11775
|
stderr: execution.stderr,
|
|
@@ -9832,7 +11795,7 @@ async function executeRunCommand(config, command) {
|
|
|
9832
11795
|
status: managedMcpCleanup.status
|
|
9833
11796
|
});
|
|
9834
11797
|
}
|
|
9835
|
-
const
|
|
11798
|
+
const result3 = {
|
|
9836
11799
|
evidenceContract: { version: 1 },
|
|
9837
11800
|
executorKind: executor.kind,
|
|
9838
11801
|
command: invocation.command,
|
|
@@ -9882,10 +11845,15 @@ async function executeRunCommand(config, command) {
|
|
|
9882
11845
|
...Array.isArray(execution.killedWorkspaceResidents) && execution.killedWorkspaceResidents.length > 0 ? {
|
|
9883
11846
|
killedWorkspaceResidents: execution.killedWorkspaceResidents
|
|
9884
11847
|
} : {},
|
|
9885
|
-
...piInvalidOutputError && !cancelled && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError && !piProviderFailure ? {
|
|
11848
|
+
...piInvalidOutputError && !cancelled && !hasOutputFlood && !hasMemoryLimit && execution.timedOut !== true && !execution.spawnError && !piTurnLimitFailure && !piProviderFailure ? {
|
|
9886
11849
|
errorCode: "pi_executor_invalid_output",
|
|
9887
11850
|
errorFamily: "validation"
|
|
9888
11851
|
} : {},
|
|
11852
|
+
...piTurnLimitFailure ? {
|
|
11853
|
+
errorCode: piTurnLimitFailure.errorCode,
|
|
11854
|
+
errorFamily: piTurnLimitFailure.errorFamily,
|
|
11855
|
+
stopReason: piTurnLimitFailure.stopReason
|
|
11856
|
+
} : {},
|
|
9889
11857
|
...piProviderFailure ? piProviderFailure : {},
|
|
9890
11858
|
...codexTransientFailure ? codexTransientFailure : {},
|
|
9891
11859
|
...piUsageDiagnostic && !piInvalidOutputError ? {
|
|
@@ -9916,7 +11884,7 @@ async function executeRunCommand(config, command) {
|
|
|
9916
11884
|
}
|
|
9917
11885
|
} : {}
|
|
9918
11886
|
};
|
|
9919
|
-
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);
|
|
9920
11888
|
if (!completion) {
|
|
9921
11889
|
rememberPendingResultOutboxRunCommand(command, {
|
|
9922
11890
|
phase: "result_outbox",
|
|
@@ -9962,6 +11930,52 @@ async function processCommand(config, command) {
|
|
|
9962
11930
|
await executeTrackedModelCallCommand(config, command);
|
|
9963
11931
|
return;
|
|
9964
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
|
+
}
|
|
9965
11979
|
await completeCommand(config, command, "succeeded", {
|
|
9966
11980
|
summary: `Command ${command.commandType} acknowledged by amaster-runtime-daemon.`,
|
|
9967
11981
|
commandType: command.commandType
|
|
@@ -9998,12 +12012,12 @@ function reconcileStaleManagedMcpProfiles(config) {
|
|
|
9998
12012
|
["Codex", reconcileManagedCodexMcpProfiles],
|
|
9999
12013
|
["Pi", reconcileManagedPiMcpProfiles]
|
|
10000
12014
|
]) {
|
|
10001
|
-
const
|
|
10002
|
-
if (
|
|
10003
|
-
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`);
|
|
10004
12018
|
}
|
|
10005
|
-
if (
|
|
10006
|
-
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.
|
|
10007
12021
|
`);
|
|
10008
12022
|
}
|
|
10009
12023
|
}
|
|
@@ -10090,7 +12104,7 @@ async function ack(config, flags) {
|
|
|
10090
12104
|
process.stdout.write(`${JSON.stringify(body, null, 2)}
|
|
10091
12105
|
`);
|
|
10092
12106
|
}
|
|
10093
|
-
async function
|
|
12107
|
+
async function result2(config, flags) {
|
|
10094
12108
|
const connectorId = requireConnectorId(config);
|
|
10095
12109
|
const commandId = flags.commandId ?? process.env.AMASTER_COMMAND_ID;
|
|
10096
12110
|
if (!commandId) throw new Error("--command-id or AMASTER_COMMAND_ID is required");
|
|
@@ -10146,7 +12160,7 @@ async function runLoop(config) {
|
|
|
10146
12160
|
${message}
|
|
10147
12161
|
`);
|
|
10148
12162
|
}
|
|
10149
|
-
await new Promise((
|
|
12163
|
+
await new Promise((resolve13) => setTimeout(resolve13, config.pollIntervalSeconds * 1e3));
|
|
10150
12164
|
}
|
|
10151
12165
|
}
|
|
10152
12166
|
function help() {
|
|
@@ -10206,7 +12220,7 @@ async function main() {
|
|
|
10206
12220
|
await ack(config, flags);
|
|
10207
12221
|
break;
|
|
10208
12222
|
case "result":
|
|
10209
|
-
await
|
|
12223
|
+
await result2(config, flags);
|
|
10210
12224
|
break;
|
|
10211
12225
|
case "run-once":
|
|
10212
12226
|
await runOnce(config);
|