@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.
@@ -2,11 +2,12 @@
2
2
  // MirrorX runtime connector daemon bundle.
3
3
 
4
4
  // src/amaster-runtime-daemon.mjs
5
- import { createHash as createHash9 } from "node:crypto";
6
- import { chmodSync as chmodSync6, copyFileSync as copyFileSync3, existsSync as existsSync12, lstatSync as lstatSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync10, readdirSync as readdirSync8, realpathSync as realpathSync3, renameSync as renameSync5, rmSync as rmSync7, statSync as statSync7, symlinkSync as symlinkSync4, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
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 join13, relative as relative8, resolve as resolve11 } from "node:path";
9
- import { spawn, spawnSync as spawnSync5 } from "node:child_process";
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, sep2, end, banNewLines = false) {
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 === sep2) {
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 piManagedMcpProfileApi = (() => {
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 result2 = spawnSync2(executorCommand, ["--version"], {
2214
- cwd: env.AMASTER_RUNTIME_EXECUTION_WORKDIR,
2215
- env,
2216
- encoding: "utf8",
2217
- timeout: 1e4,
2218
- maxBuffer: 1024 * 1024
2219
- });
2220
- if (result2.error || result2.status !== 0) {
2221
- throw new Error(`pi_managed_mcp_attestation_failed: --version exit=${result2.status ?? "spawn_error"}`);
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
- return executorVersion;
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 executorVersion = attestPi(nonEmpty2(input.executorCommand, "executorCommand"), env, configPath, config);
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
- executorVersion,
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, options = {}) {
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(options.nowMs) ? options.nowMs : Date.now();
2419
- const rolloutTtlMs = Number.isFinite(options.sessionRolloutTtlMs) && options.sessionRolloutTtlMs > 0 ? options.sessionRolloutTtlMs : DEFAULT_SESSION_ROLLOUT_TTL_MS2;
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 identity = record2(requestedIdentity);
2520
- const childUmask = identity.umask;
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 = { ...identity };
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, call `update_parent` with `status: todo`, preserve the current agent owner, and record the concrete next action.",
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
- "- blocked with a named unblock owner and action;",
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 runtimeAuthorizationText(context) {
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
- if (missing.length === 0) return "";
2746
- return [
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 tasks before continuing substantial source work.",
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: 99, sourceRef: `run:${input.runId ?? "unknown"}`, content: recoveryInstructionText(input), truncationReason: isRecoveryWakeReason(input.wakeReason) ? null : "mode_selection" },
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 Authorization", priority: 98, sourceRef: `run:${input.runId ?? "unknown"}`, content: runtimeAuthorizationText(context) },
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 = jsonText(manifest).length;
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 result2 = spawnSync3("sh", ["-lc", `command -v ${quoteShell(command)} >/dev/null 2>&1`], {
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 result2.status === 0;
3114
+ return result3.status === 0;
2950
3115
  }
2951
3116
  function commandVersion(command, env = process.env) {
2952
3117
  const commandEnv = executorDiscoveryEnv(env);
2953
- const result2 = spawnSync3(command, ["--version"], {
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 (result2.status !== 0) return void 0;
2960
- return result2.stdout.trim().split(/\r?\n/)[0]?.slice(0, 120) || void 0;
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 timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
3045
- const base = `${path}.corrupt-${timestamp}Z`;
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 result2 = kept.join("\n");
3302
- return hadTrailingNewline && result2 ? `${result2}
3303
- ` : result2;
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 result2 = asRecord(rawResult);
3710
- return readString(result2.invocationId) === approvedInvocationId && readString(result2.status) === "succeeded" && !["rejected", "blocked"].includes(readString(result2.providerStatus) ?? "");
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 sha256 = readString(effectResult.sha256);
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(sha256 ?? "") && Number.isSafeInteger(byteSize) && byteSize > 0 ? { intentId, manifestId, sourceRelativePath, sha256, byteSize } : null;
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 result2 of normalizedResults) {
3747
- const runtimeAction = asRecord(result2.runtimeAction);
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(result2.status);
3753
- const providerStatus = readString(result2.providerStatus);
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(result2.invocationId) ? { invocationId: readString(result2.invocationId) } : {},
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 result2 = asRecord(item.result);
3801
- let structuredContent = asRecord(result2.structuredContent);
3802
- if (Object.keys(structuredContent).length === 0 && Array.isArray(result2.content)) {
3803
- for (const part of result2.content) {
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((resolve12) => setTimeout(resolve12, delayMs));
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 = realpathSync(resolve3(cwd));
4703
+ const root = realpathSync2(resolve3(cwd));
4521
4704
  const uploads = /* @__PURE__ */ new Map();
4522
- for (const result2 of Array.isArray(mcpToolResults) ? mcpToolResults : []) {
4523
- const intent = result2 && typeof result2 === "object" && !Array.isArray(result2) ? result2.artifactIntent : null;
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(realpathSync(sourcePath), root)) {
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((result2) => {
4582
- const intentId = readString(asRecord(result2).artifactIntent?.intentId);
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 realpathSync2, statSync as statSync3 } from "node:fs";
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 = realpathSync2(resolve4(expandHomePath(requested ?? fallback)));
4887
+ const cwd = realpathSync3(resolve4(expandHomePath(requested ?? fallback)));
4705
4888
  const allowlist = config.workspaceBindings.flatMap((entry) => {
4706
4889
  try {
4707
- return [realpathSync2(resolve4(expandHomePath(entry)))];
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 realpathSync2(root);
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 result2 = {
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 result2;
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
- result2.totalWorkdirs += 1;
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
- result2.protected.push({ ...summary, reason: "active_run_or_command" });
5080
+ result3.protected.push({ ...summary, reason: "active_run_or_command" });
4898
5081
  continue;
4899
5082
  }
4900
5083
  if (lastTouchedTime !== null && nowMs - lastTouchedTime < ttlMs) {
4901
- result2.protected.push({ ...summary, reason: "within_ttl" });
5084
+ result3.protected.push({ ...summary, reason: "within_ttl" });
4902
5085
  continue;
4903
5086
  }
4904
- result2.totalCandidateBytes += summary.sizeBytes;
4905
- result2.candidates.push({
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 result2;
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.45";
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 (!existsSync12(dir)) return 0;
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, join13(dir, 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((result2) => result2.status === "approval_required") ? "approval_required" : null;
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 (!existsSync12(dir)) return [];
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, join13(dir, 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 (!existsSync12(dir)) return [];
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(join13(dir, file), "utf8")));
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 ? resolve11(expandHomePath(text)) : null;
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 = join13(pathValue, name);
8397
+ const skillDir = join14(pathValue, name);
6449
8398
  try {
6450
8399
  if (!statSync7(skillDir).isDirectory()) continue;
6451
- if (!existsSync12(join13(skillDir, "SKILL.md"))) continue;
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 join13(homedir3(), ".pi", "agent");
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 ? join13(piAgentHome, "skills") : null;
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 ? join13(piAgentHome, "marketplace", "skills") : null);
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 ? join13(piAgentHome, "mcp.json") : null);
6518
- const settingsConfigPath = piCodingAgentDir ? join13(piCodingAgentDir, "settings.json") : null;
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 ? resolve11(expandHomePath(configured)) : null;
8710
+ return configured ? resolve12(expandHomePath(configured)) : null;
6752
8711
  }
6753
8712
  function readPiAgentLocalPlatformCredential(credentialsDir) {
6754
- const pointer = readJsonFile3(join13(credentialsDir, "latest.json"));
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(join13(credentialsDir, `${credentialRef}.json`));
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 = join13(systemDataDir, "companies");
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(join13(companiesDir, entry.name, "model-credentials"));
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(join13(homedir3(), ".amaster-employee", "runtime-connector-state.json"))}
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 ? existsSync12(entry.workspacePath) : false,
9104
+ managedWorkdirPresent: entry.workspacePath ? existsSync13(entry.workspacePath) : false,
7146
9105
  outboxPending: resultOutboxPendingCount(config)
7147
9106
  };
7148
- if (!entry.workspacePath || !existsSync12(entry.workspacePath)) return base;
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 = resolve11(workspace.cwd, relativePath);
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 resolve11(expandHomePath(explicitRoot));
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 join13(dirname9(resolve11(expandHomePath(configuredPiHome))), "companies");
7487
- return join13(homedir3(), ".amaster-employee", "companies");
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 join13(companyPiHomeRoot(baseEnv), segment, ".pi");
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 = realpathSync3(requestedCwd) === realpathSync3(sourceWorkspacePath);
9521
+ cwdMatched = realpathSync4(requestedCwd) === realpathSync4(sourceWorkspacePath);
7561
9522
  } catch {
7562
- cwdMatched = resolve11(requestedCwd) === resolve11(sourceWorkspacePath);
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 result2 = spawnSync5("ps", ["-axo", "pgid=,rss="], {
9945
+ const result3 = spawnSync6("ps", ["-axo", "pgid=,rss="], {
7985
9946
  encoding: "utf8",
7986
9947
  stdio: ["ignore", "pipe", "ignore"]
7987
9948
  });
7988
- if (result2.status !== 0) return null;
9949
+ if (result3.status !== 0) return null;
7989
9950
  const rssKbByProcessGroup = /* @__PURE__ */ new Map();
7990
- for (const line of String(result2.stdout ?? "").split(/\r?\n/)) {
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 realpathSync3(value);
9966
+ return realpathSync4(value);
8006
9967
  } catch {
8007
- return resolve11(value);
9968
+ return resolve12(value);
8008
9969
  }
8009
9970
  }
8010
- var LSOF_COMMAND = process.platform === "darwin" && existsSync12("/usr/sbin/lsof") ? "/usr/sbin/lsof" : "lsof";
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 realpathSync3(`/proc/${pid}/cwd`);
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 result2 = spawnSync5(LSOF_COMMAND, ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
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 (result2.status !== 0) return null;
8025
- const cwdLine = String(result2.stdout ?? "").split(/\r?\n/).find((line) => line.startsWith("n"));
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 result2 = spawnSync5(LSOF_COMMAND, ["-nP", "-d", "cwd", "-Fp", "-Fn"], {
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 (result2.status !== 0) return cwds;
10015
+ if (result3.status !== 0) return cwds;
8055
10016
  let currentPid = null;
8056
- for (const line of String(result2.stdout ?? "").split(/\r?\n/)) {
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 result2 = spawnSync5("ps", ["-axo", "pid=,pgid=,command="], {
10032
+ const result3 = spawnSync6("ps", ["-axo", "pid=,pgid=,command="], {
8072
10033
  encoding: "utf8",
8073
10034
  stdio: ["ignore", "pipe", "ignore"]
8074
10035
  });
8075
- if (result2.status !== 0) return [];
10036
+ if (result3.status !== 0) return [];
8076
10037
  const rows = [];
8077
- for (const line of String(result2.stdout ?? "").split(/\r?\n/)) {
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 || !existsSync12(root)) return workdirs;
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 = join13(current, entry.name);
8146
- if (entry.name === "workdir" && existsSync12(workspaceManifestPath(fullPath))) {
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 = relative8(root, workdir);
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 = (result2) => {
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
- ...result2
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, result2, error) {
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: result2,
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 resolve11(expandHomePath(explicit));
8685
- return join13(dirname9(stateFilePath(process.env)), "result-outbox");
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 join13(resultOutboxDir(config), "invalid");
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(join13(dir, resultOutboxFileName(entry.commandId)), `${JSON.stringify(body, null, 2)}
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 = join13(invalidDir, file);
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 = join13(invalidDir, file);
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 (!existsSync12(dir)) return { attempted: 0, completed: 0 };
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 = join13(dir, file);
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 = join13(workspace.cwd, "input-attachments");
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 = join13(targetDir, filename);
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 = createHash9("sha256").update(body).digest("hex");
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: relative8(workspace.cwd, targetPath),
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 = join13(workspace.cwd, "input-artifacts");
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 sha256 = readString(entry.sha256);
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(sha256 ?? "") || !contentPath || byteSize === null) {
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 = createHash9("sha256").update(body).digest("hex");
9100
- if (body.byteLength !== byteSize || actualSha256 !== sha256) {
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=${sha256} actualSha256=${actualSha256}`
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 = join13("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
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 = join13("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
11076
+ relativePath = join14("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
9116
11077
  }
9117
11078
  usedPaths.add(relativePath);
9118
- const targetPath = join13(workspace.cwd, relativePath);
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 = join13(targetRoot, "artifact-input-manifest.json");
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 join13(dirname9(workspace.runDir), "checkpoint");
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 (!existsSync12(checkpointDir)) return false;
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 createHash9("sha256").update(readFileSync10(filePath)).digest("hex");
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 = join13(checkpointDir, "manifest.json");
9174
- if (!existsSync12(manifestPath)) return [];
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 = realpathSync3(join13(checkpointDir, "files"));
9191
- const workspaceRoot = realpathSync3(workspace.cwd);
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 = resolve11(filesRoot, relativePath);
9199
- const target = resolve11(workspaceRoot, relativePath);
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 (!existsSync12(sourceCandidate)) throw new Error(`issue checkpoint file is missing: ${relativePath}`);
9204
- const source = realpathSync3(sourceCandidate);
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 = realpathSync3(dirname9(target));
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 = join13(checkpointDir, "files");
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 = realpathSync3(workspace.cwd);
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 || !existsSync12(source) || !statSync7(source).isFile()) continue;
9267
- const ownedSource = realpathSync3(source);
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 = resolve11(filesDir, relativePath);
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(join13(checkpointDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
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: join13(
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((result3) => {
9723
- const intentId = readString(asRecord(result3).artifactIntent?.intentId);
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((result3) => readString(result3.status) === "approval_required");
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: resolve11(cwd, artifact.relativePath)
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 result2 = {
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", result2, error ?? void 0);
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 result2 = reconcile(config.runtimeWorkspacesRoot);
10002
- if (result2.failed > 0) {
10003
- throw new Error(`${executorKind.toLowerCase()}_managed_mcp_restart_cleanup_failed: ${result2.failed}/${result2.scanned} owned profile(s) could not be reconciled`);
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 (result2.removed > 0) {
10006
- process.stderr.write(`AMaster daemon removed ${result2.removed} stale marker-owned ${executorKind} managed MCP artifact(s) during startup reconciliation.
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 result(config, flags) {
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((resolve12) => setTimeout(resolve12, config.pollIntervalSeconds * 1e3));
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 result(config, flags);
12223
+ await result2(config, flags);
10210
12224
  break;
10211
12225
  case "run-once":
10212
12226
  await runOnce(config);