@amaster.ai/employee-runtime-connector 0.1.0-beta.46 → 0.1.0-beta.47

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