@amaster.ai/employee-runtime-connector 0.1.1-beta.3 → 0.1.1-beta.5

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,7 +2,7 @@
2
2
  // MirrorX runtime connector daemon bundle.
3
3
 
4
4
  // src/amaster-runtime-daemon.mjs
5
- import { createHash as createHash11 } from "node:crypto";
5
+ import { createHash as createHash12 } from "node:crypto";
6
6
  import { chmodSync as chmodSync6, copyFileSync as copyFileSync3, existsSync as existsSync14, lstatSync as lstatSync7, mkdirSync as mkdirSync9, mkdtempSync, readFileSync as readFileSync11, readdirSync as readdirSync9, realpathSync as realpathSync5, renameSync as renameSync6, rmSync as rmSync7, statSync as statSync7, symlinkSync as symlinkSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
7
7
  import { arch as arch3, homedir as homedir3, hostname as hostname2, platform as platform3, tmpdir } from "node:os";
8
8
  import { basename as basename6, delimiter as delimiter2, dirname as dirname9, extname as extname2, isAbsolute as isAbsolute8, join as join15, relative as relative9, resolve as resolve12 } from "node:path";
@@ -1560,7 +1560,7 @@ function reconcileManagedCodexMcpProfiles(rootPath, options = {}) {
1560
1560
  }
1561
1561
 
1562
1562
  // src/amaster-runtime-daemon/pi-managed-mcp-profile.mjs
1563
- import { createHash as createHash2 } from "node:crypto";
1563
+ import { createHash as createHash3 } from "node:crypto";
1564
1564
  import {
1565
1565
  chmodSync as chmodSync2,
1566
1566
  copyFileSync,
@@ -1965,15 +1965,247 @@ function managedPiMcpArgsNormalizerExtensionSource() {
1965
1965
  ].join("\n\n");
1966
1966
  }
1967
1967
 
1968
+ // src/amaster-runtime-daemon/pi-effective-tools-attestor.mjs
1969
+ import { createHash as createHash2 } from "node:crypto";
1970
+ var MANAGED_PI_EFFECTIVE_TOOLS_ATTESTOR_FILENAME = "amaster-effective-tools-attestor.js";
1971
+ var MANAGED_PI_EFFECTIVE_TOOLS_SCHEMA_VERSION = "amaster.pi-effective-tools.v1";
1972
+ function normalizedSchema(value, adapterNormalized) {
1973
+ const schema = value && typeof value === "object" && !Array.isArray(value) ? value : {};
1974
+ if (!adapterNormalized) return schema;
1975
+ const { $schema: _schema, additionalProperties: _additionalProperties, ...normalized } = schema;
1976
+ return normalized;
1977
+ }
1978
+ function stablePiJson(value) {
1979
+ if (value === null || value === void 0 || typeof value !== "object") {
1980
+ const serialized = JSON.stringify(value);
1981
+ return serialized === void 0 ? "undefined" : serialized;
1982
+ }
1983
+ if (Array.isArray(value)) return `[${value.map(stablePiJson).join(",")}]`;
1984
+ const keys = Object.keys(value).sort();
1985
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stablePiJson(value[key])}`).join(",")}}`;
1986
+ }
1987
+ function stablePiToolSchemaHash(schema, options = {}) {
1988
+ return createHash2("sha256").update(stablePiJson(normalizedSchema(schema, options.adapterNormalized === true))).digest("hex");
1989
+ }
1990
+ function stablePiEffectiveToolSetHash(entries) {
1991
+ const normalized = entries.map((entry) => ({ name: entry.name, schemaHash: entry.schemaHash ?? entry.effectiveSchemaHash })).sort((left, right) => left.name.localeCompare(right.name));
1992
+ return createHash2("sha256").update(stablePiJson(normalized)).digest("hex");
1993
+ }
1994
+ function stablePiDirectCatalogSetHash(entries) {
1995
+ const normalized = entries.map((entry) => ({ name: entry.name, exposedName: entry.exposedName, schemaHash: entry.schemaHash, riskLevel: entry.riskLevel })).sort((left, right) => left.name.localeCompare(right.name));
1996
+ return createHash2("sha256").update(stablePiJson(normalized)).digest("hex");
1997
+ }
1998
+ function managedPiEffectiveToolsAttestorExtensionSource() {
1999
+ return String.raw`import { createHash } from "node:crypto";
2000
+ import { readFileSync, writeFileSync } from "node:fs";
2001
+ import { fileURLToPath } from "node:url";
2002
+ import { Value } from "typebox/value";
2003
+
2004
+ const SCHEMA_VERSION = ${JSON.stringify(MANAGED_PI_EFFECTIVE_TOOLS_SCHEMA_VERSION)};
2005
+
2006
+ function record(value) {
2007
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2008
+ }
2009
+
2010
+ function stableJson(value) {
2011
+ if (value === null || value === undefined || typeof value !== "object") {
2012
+ const serialized = JSON.stringify(value);
2013
+ return serialized === undefined ? "undefined" : serialized;
2014
+ }
2015
+ if (Array.isArray(value)) return "[" + value.map(stableJson).join(",") + "]";
2016
+ const keys = Object.keys(value).sort();
2017
+ return "{" + keys.map((key) => JSON.stringify(key) + ":" + stableJson(value[key])).join(",") + "}";
2018
+ }
2019
+
2020
+ function sha256(value) {
2021
+ return createHash("sha256").update(value).digest("hex");
2022
+ }
2023
+
2024
+ function schemaHash(value) {
2025
+ return sha256(stableJson(record(value)));
2026
+ }
2027
+
2028
+ function toolSetHash(entries) {
2029
+ return sha256(stableJson(entries
2030
+ .map((entry) => ({ name: entry.name, schemaHash: entry.schemaHash }))
2031
+ .sort((left, right) => left.name.localeCompare(right.name))));
2032
+ }
2033
+
2034
+ function requiredPath(name) {
2035
+ const value = process.env[name];
2036
+ if (typeof value !== "string" || value.trim() === "") throw new Error(name + " is required");
2037
+ return value;
2038
+ }
2039
+
2040
+ function sourceIsMcpAdapter(sourceInfo) {
2041
+ return stableJson(sourceInfo).includes("pi-mcp-adapter");
2042
+ }
2043
+
2044
+ function safeWriteReceipt(receiptPath, receipt) {
2045
+ writeFileSync(receiptPath, JSON.stringify(receipt) + "\n", { encoding: "utf8", mode: 0o600 });
2046
+ }
2047
+
2048
+ function validationErrorPath(error) {
2049
+ if (typeof error.path === "string" && error.path) return error.path;
2050
+ const base = typeof error.instancePath === "string" ? error.instancePath : "";
2051
+ const params = record(error.params);
2052
+ const additional = Array.isArray(params.additionalProperties) ? params.additionalProperties[0] : null;
2053
+ if (typeof additional === "string" && additional) return base + "/" + additional;
2054
+ const required = Array.isArray(params.requiredProperties) ? params.requiredProperties[0] : null;
2055
+ if (typeof required === "string" && required) return base + "/" + required;
2056
+ return base || "/";
2057
+ }
2058
+
2059
+ function inputErrorReason(schema, input) {
2060
+ const errors = [...Value.Errors(schema, input)];
2061
+ if (errors.length === 0) return null;
2062
+ return errors.slice(0, 8).map((error) => validationErrorPath(error) + ": " + error.message).join("; ");
2063
+ }
2064
+
2065
+ export default function amasterEffectiveToolsAttestor(pi) {
2066
+ const mode = process.env.AMASTER_PI_EFFECTIVE_TOOLS_MODE;
2067
+ if (mode !== "probe" && mode !== "enforce") return;
2068
+ const manifestPath = requiredPath("AMASTER_PI_EFFECTIVE_TOOLS_MANIFEST");
2069
+ const receiptPath = requiredPath("AMASTER_PI_EFFECTIVE_TOOLS_RECEIPT");
2070
+ const cachePath = requiredPath("AMASTER_PI_EFFECTIVE_TOOLS_CACHE");
2071
+ const configPath = requiredPath("AMASTER_PI_EFFECTIVE_TOOLS_CONFIG");
2072
+
2073
+ pi.on("session_start", async () => {
2074
+ let receipt;
2075
+ try {
2076
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
2077
+ if (manifest.schemaVersion !== SCHEMA_VERSION || manifest.serverName !== "amaster") {
2078
+ throw new Error("effective tool manifest identity mismatch");
2079
+ }
2080
+ const sourceDigest = sha256(readFileSync(fileURLToPath(import.meta.url)));
2081
+ if (sourceDigest !== manifest.attestorSourceSha256) throw new Error("attestor source digest mismatch");
2082
+ const configDigest = sha256(readFileSync(configPath));
2083
+ if (configDigest !== manifest.configSha256) throw new Error("managed MCP config digest mismatch");
2084
+ const cacheDigest = sha256(readFileSync(cachePath));
2085
+ if (cacheDigest !== manifest.cacheSha256) throw new Error("managed MCP cache digest mismatch");
2086
+
2087
+ const expectedEntries = Array.isArray(manifest.entries) ? manifest.entries : [];
2088
+ if (expectedEntries.length === 0) throw new Error("effective tool manifest is empty");
2089
+ const expectedByName = new Map(expectedEntries.map((entry) => [entry.name, entry]));
2090
+ if (expectedByName.size !== expectedEntries.length) throw new Error("effective tool manifest has duplicate names");
2091
+ for (const entry of expectedEntries) {
2092
+ const validatorSelfTest = inputErrorReason(entry.schema, { __amasterUnexpected: true });
2093
+ if (!validatorSelfTest || !validatorSelfTest.includes("/__amasterUnexpected")) {
2094
+ throw new Error("strict input validator self-test failed for " + entry.name);
2095
+ }
2096
+ }
2097
+
2098
+ const allTools = pi.getAllTools();
2099
+ if (allTools.some((tool) => typeof tool.name !== "string" || !tool.name)) {
2100
+ throw new Error("registered tool identity mismatch");
2101
+ }
2102
+ const activeToolNames = pi.getActiveTools();
2103
+ if (activeToolNames.some((name) => typeof name !== "string" || !name)) {
2104
+ throw new Error("active tool identity mismatch");
2105
+ }
2106
+ activeToolNames.sort();
2107
+ const activeToolNameSet = new Set(activeToolNames);
2108
+ const proxyPresent = allTools.some((tool) => tool.name === "mcp");
2109
+ const nonCatalogTools = activeToolNames.filter((name) => !expectedByName.has(name));
2110
+ const adapterTools = allTools.filter((tool) => sourceIsMcpAdapter(tool.sourceInfo));
2111
+ const directTools = adapterTools.filter((tool) => tool.name !== "mcp" && activeToolNameSet.has(tool.name)).map((tool) => ({
2112
+ name: tool.name,
2113
+ schemaHash: schemaHash(tool.parameters),
2114
+ }));
2115
+ const actualByName = new Map(directTools.map((entry) => [entry.name, entry]));
2116
+ const missing = [...expectedByName.keys()].filter((name) => !actualByName.has(name) || !activeToolNameSet.has(name)).sort();
2117
+ const unexpected = adapterTools
2118
+ .map((tool) => tool.name)
2119
+ .filter((name) => name !== "mcp" && !expectedByName.has(name))
2120
+ .sort();
2121
+ const schemaMismatches = [...expectedByName.entries()].flatMap(([name, expected]) => {
2122
+ const actual = actualByName.get(name);
2123
+ return actual && actual.schemaHash !== expected.effectiveSchemaHash
2124
+ ? [{ name, expected: expected.effectiveSchemaHash, actual: actual.schemaHash }]
2125
+ : [];
2126
+ });
2127
+ const effectiveSetHash = toolSetHash(directTools);
2128
+ if (proxyPresent || nonCatalogTools.length > 0 || missing.length > 0 || unexpected.length > 0 || schemaMismatches.length > 0) {
2129
+ throw new Error("effective tool surface mismatch: proxy=" + proxyPresent + " nonCatalog=" + nonCatalogTools.join(",") + " missing=" + missing.join(",") + " unexpected=" + unexpected.join(",") + " schemas=" + schemaMismatches.map((entry) => entry.name).join(","));
2130
+ }
2131
+ if (effectiveSetHash !== manifest.effectiveSetHash) throw new Error("effective tool set hash mismatch");
2132
+ receipt = {
2133
+ schemaVersion: SCHEMA_VERSION,
2134
+ status: "attested",
2135
+ mode,
2136
+ proxyPresent,
2137
+ effectiveSetHash,
2138
+ effectiveTools: directTools.sort((left, right) => left.name.localeCompare(right.name)),
2139
+ effectiveToolBindings: expectedEntries
2140
+ .map((entry) => ({ canonicalName: entry.canonicalName, exposedName: entry.name, effectiveSchemaHash: entry.effectiveSchemaHash }))
2141
+ .sort((left, right) => left.canonicalName.localeCompare(right.canonicalName)),
2142
+ attestorSourceSha256: sourceDigest,
2143
+ configSha256: configDigest,
2144
+ cacheSha256: cacheDigest,
2145
+ strictInputValidatorSelfTest: true,
2146
+ modelInvocationStarted: false,
2147
+ attestedAt: new Date().toISOString(),
2148
+ };
2149
+ safeWriteReceipt(receiptPath, receipt);
2150
+ if (mode === "probe") process.exit(0);
2151
+ } catch (error) {
2152
+ receipt = {
2153
+ schemaVersion: SCHEMA_VERSION,
2154
+ status: "rejected",
2155
+ mode,
2156
+ error: error instanceof Error ? error.message : String(error),
2157
+ modelInvocationStarted: false,
2158
+ attestedAt: new Date().toISOString(),
2159
+ };
2160
+ safeWriteReceipt(receiptPath, receipt);
2161
+ process.exit(78);
2162
+ }
2163
+ });
2164
+
2165
+ pi.on("tool_call", (event) => {
2166
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
2167
+ const entry = Array.isArray(manifest.entries)
2168
+ ? manifest.entries.find((candidate) => candidate.name === event.toolName)
2169
+ : null;
2170
+ if (!entry) return;
2171
+ const reason = inputErrorReason(entry.schema, event.input);
2172
+ if (!reason) return;
2173
+ return { block: true, reason: "Governed tool input rejected: " + reason };
2174
+ });
2175
+ }
2176
+ `;
2177
+ }
2178
+
1968
2179
  // src/amaster-runtime-daemon/pi-managed-mcp-profile.mjs
1969
2180
  var MANAGED_PI_MCP_TOOL_MODE = "proxy_only";
2181
+ var MANAGED_PI_DIRECT_TYPED_MCP_TOOL_MODE = "direct_typed";
1970
2182
  var MANAGED_PI_MCP_ARGS_NORMALIZATION = "json_string_control_characters_v1";
1971
2183
  var MANAGED_PI_MCP_ARGS_NORMALIZER_FILENAME = "amaster-mcp-args-normalizer.js";
2184
+ function applyManagedPiToolAllowlist(args, profile) {
2185
+ const toolAllowlist = Array.isArray(profile?.toolAllowlist) ? profile.toolAllowlist.filter((name) => typeof name === "string" && name) : [];
2186
+ if (toolAllowlist.length === 0) return [...args];
2187
+ const printArgIndex = Math.max(args.lastIndexOf("-p"), args.lastIndexOf("--print"));
2188
+ if (printArgIndex < 0) throw new Error("pi_managed_mcp_invocation_invalid: print mode argument missing");
2189
+ return [
2190
+ ...args.slice(0, printArgIndex),
2191
+ "--tools",
2192
+ toolAllowlist.join(","),
2193
+ ...args.slice(printArgIndex)
2194
+ ];
2195
+ }
1972
2196
  function createManagedPiMcpProfileApi(options = {}) {
1973
2197
  const spawnSyncImpl = typeof options.spawnSync === "function" ? options.spawnSync : spawnSync2;
1974
2198
  const nowImpl = typeof options.now === "function" ? options.now : Date.now;
1975
2199
  const SUPPORTED_SCHEMA_VERSION2 = "amaster.governed-mcp.v1";
1976
2200
  const SUPPORTED_SERVER_NAME2 = "amaster";
2201
+ const DIRECT_CATALOG_SCHEMA_VERSION = "amaster.governed-mcp-direct-catalog.v1";
2202
+ const DIRECT_TYPED_V1_TOOL_NAMES = /* @__PURE__ */ new Set([
2203
+ "runtime_action.submit",
2204
+ "amaster.read_company_diagnosis",
2205
+ "amaster.publish_company_diagnosis_brief"
2206
+ ]);
2207
+ const PROVIDER_SAFE_TOOL_NAME = /^[A-Za-z][A-Za-z0-9_-]*$/;
2208
+ const PI_BUILTIN_TOOL_NAMES = /* @__PURE__ */ new Set(["read", "bash", "edit", "write", "grep", "find", "ls", "mcp"]);
1977
2209
  const MINIMUM_PI_VERSION = [0, 73, 1];
1978
2210
  const MINIMUM_MCP_ADAPTER_VERSION = [2, 6, 1];
1979
2211
  const MANAGED_BROWSER_USE_PACKAGE = "@amaster.ai/pi-browser-use";
@@ -2057,6 +2289,12 @@ function createManagedPiMcpProfileApi(options = {}) {
2057
2289
  ]);
2058
2290
  const FORBIDDEN_ARGV = /* @__PURE__ */ new Set([
2059
2291
  "--no-tools",
2292
+ "--no-builtin-tools",
2293
+ "-nbt",
2294
+ "--tools",
2295
+ "-t",
2296
+ "--exclude-tools",
2297
+ "-xt",
2060
2298
  "--no-extensions",
2061
2299
  "--no-skills",
2062
2300
  "--no-session",
@@ -2140,7 +2378,7 @@ function createManagedPiMcpProfileApi(options = {}) {
2140
2378
  nonEmpty2(session.invocationId, "nativeSession.invocationId");
2141
2379
  const sourceWorkspacePath = nonEmpty2(session.cwd, "nativeSession.cwd");
2142
2380
  const issueRoot = dirname3(runDir);
2143
- const managedSourceRunDirName = `${sourceRunId}-${createHash2("sha256").update(sourceRunId).digest("hex").slice(0, 8)}`;
2381
+ const managedSourceRunDirName = `${sourceRunId}-${createHash3("sha256").update(sourceRunId).digest("hex").slice(0, 8)}`;
2144
2382
  const sourceRunDirs = readdirSync2(issueRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink() && (entry.name === sourceRunId || entry.name === managedSourceRunDirName)).map((entry) => join4(issueRoot, entry.name));
2145
2383
  if (sourceRunDirs.length !== 1) {
2146
2384
  throw new Error(`pi_managed_mcp_session_rollout_missing: expected one source run directory for ${sourceRunId}, received ${sourceRunDirs.length}`);
@@ -2185,6 +2423,82 @@ function createManagedPiMcpProfileApi(options = {}) {
2185
2423
  if (!Number.isFinite(value)) throw new Error("pi_managed_mcp_attestation_failed: invalid attestation clock");
2186
2424
  return value;
2187
2425
  }
2426
+ function sha256(value) {
2427
+ return createHash3("sha256").update(value).digest("hex");
2428
+ }
2429
+ function adapterServerConfigHash(definition) {
2430
+ return sha256(stablePiJson({
2431
+ command: definition.command,
2432
+ args: definition.args,
2433
+ env: definition.env,
2434
+ cwd: definition.cwd,
2435
+ url: definition.url,
2436
+ headers: definition.headers,
2437
+ auth: definition.auth,
2438
+ bearerToken: definition.bearerToken,
2439
+ bearerTokenEnv: definition.bearerTokenEnv,
2440
+ exposeResources: definition.exposeResources,
2441
+ excludeTools: definition.excludeTools
2442
+ }));
2443
+ }
2444
+ function validateDirectCatalog(gateway) {
2445
+ const catalog = record6(gateway.toolCatalog);
2446
+ if (catalog.schemaVersion !== DIRECT_CATALOG_SCHEMA_VERSION) {
2447
+ throw new Error("pi_managed_mcp_invalid: direct tool catalog schema mismatch");
2448
+ }
2449
+ const tools = Array.isArray(catalog.tools) ? catalog.tools.map(record6) : [];
2450
+ if (tools.length !== DIRECT_TYPED_V1_TOOL_NAMES.size) {
2451
+ throw new Error("pi_managed_mcp_invalid: direct tool catalog size mismatch");
2452
+ }
2453
+ const names = /* @__PURE__ */ new Set();
2454
+ const exposedNames = /* @__PURE__ */ new Set();
2455
+ const normalized = tools.map((tool) => {
2456
+ const name = nonEmpty2(tool.name, "toolCatalog.tools.name");
2457
+ if (!DIRECT_TYPED_V1_TOOL_NAMES.has(name) || names.has(name)) {
2458
+ throw new Error("pi_managed_mcp_invalid: direct tool catalog name mismatch");
2459
+ }
2460
+ names.add(name);
2461
+ const exposedName = nonEmpty2(tool.exposedName, `toolCatalog.tools.exposedName:${name}`);
2462
+ if (!PROVIDER_SAFE_TOOL_NAME.test(exposedName) || PI_BUILTIN_TOOL_NAMES.has(exposedName)) {
2463
+ throw new Error(`pi_managed_mcp_invalid: direct tool exposed name is unsafe for ${name}`);
2464
+ }
2465
+ if (exposedNames.has(exposedName)) {
2466
+ throw new Error(`pi_managed_mcp_invalid: direct tool exposed name collision for ${exposedName}`);
2467
+ }
2468
+ exposedNames.add(exposedName);
2469
+ const inputSchema = record6(tool.inputSchema);
2470
+ if (inputSchema.type !== "object" || inputSchema.additionalProperties !== false) {
2471
+ throw new Error(`pi_managed_mcp_invalid: direct tool schema is not strict for ${name}`);
2472
+ }
2473
+ const schemaHash = stablePiToolSchemaHash(inputSchema);
2474
+ if (tool.schemaHash !== schemaHash) {
2475
+ throw new Error(`pi_managed_mcp_invalid: direct tool schema hash mismatch for ${name}`);
2476
+ }
2477
+ const riskLevel = nonEmpty2(tool.riskLevel, `toolCatalog.tools.riskLevel:${name}`);
2478
+ if (!(/* @__PURE__ */ new Set(["read", "write", "destructive"])).has(riskLevel)) {
2479
+ throw new Error(`pi_managed_mcp_invalid: direct tool risk level mismatch for ${name}`);
2480
+ }
2481
+ return {
2482
+ name,
2483
+ exposedName,
2484
+ description: typeof tool.description === "string" ? tool.description : "",
2485
+ inputSchema,
2486
+ schemaHash,
2487
+ riskLevel,
2488
+ effectiveSchemaHash: stablePiToolSchemaHash(inputSchema, { adapterNormalized: true })
2489
+ };
2490
+ });
2491
+ const setHash = stablePiDirectCatalogSetHash(normalized.map((tool) => ({
2492
+ name: tool.name,
2493
+ exposedName: tool.exposedName,
2494
+ schemaHash: tool.schemaHash,
2495
+ riskLevel: tool.riskLevel
2496
+ })));
2497
+ if (catalog.setHash !== setHash) {
2498
+ throw new Error("pi_managed_mcp_invalid: direct tool catalog set hash mismatch");
2499
+ }
2500
+ return { setHash, tools: normalized.sort((left, right) => left.name.localeCompare(right.name)) };
2501
+ }
2188
2502
  function piExecutableIdentity(executorCommand) {
2189
2503
  try {
2190
2504
  const realPath = realpathSync2(executorCommand);
@@ -2192,8 +2506,8 @@ function createManagedPiMcpProfileApi(options = {}) {
2192
2506
  if (!executableStat.isFile() || executableStat.isSymbolicLink()) {
2193
2507
  throw Object.assign(new Error("Pi executable is not a regular file"), { code: "EUNSAFE" });
2194
2508
  }
2195
- const contentSha256 = createHash2("sha256").update(readFileSync3(realPath)).digest("hex");
2196
- return `sha256:${createHash2("sha256").update(JSON.stringify({ realPath, contentSha256 })).digest("hex")}`;
2509
+ const contentSha256 = createHash3("sha256").update(readFileSync3(realPath)).digest("hex");
2510
+ return `sha256:${createHash3("sha256").update(JSON.stringify({ realPath, contentSha256 })).digest("hex")}`;
2197
2511
  } catch (error) {
2198
2512
  const errorCode = typeof error?.code === "string" ? error.code : "UNKNOWN";
2199
2513
  throw new Error(`pi_managed_mcp_attestation_failed: --version error=${errorCode}`);
@@ -2239,7 +2553,15 @@ function createManagedPiMcpProfileApi(options = {}) {
2239
2553
  throw new Error("pi_managed_mcp_invalid: Gateway authority mismatch");
2240
2554
  }
2241
2555
  if ((runtimeAuth.issueId ?? null) !== (headers["x-amaster-issue-id"] ?? null)) throw new Error("pi_managed_mcp_invalid: issue authority mismatch");
2242
- return { gateway, gatewayUrl, sessionToken, headers, runId };
2556
+ const mcpToolMode = gateway.mcpToolMode ?? MANAGED_PI_MCP_TOOL_MODE;
2557
+ if (mcpToolMode !== MANAGED_PI_MCP_TOOL_MODE && mcpToolMode !== MANAGED_PI_DIRECT_TYPED_MCP_TOOL_MODE) {
2558
+ throw new Error("pi_managed_mcp_invalid: unsupported managed Pi MCP tool mode");
2559
+ }
2560
+ const directCatalog = mcpToolMode === MANAGED_PI_DIRECT_TYPED_MCP_TOOL_MODE ? validateDirectCatalog(gateway) : null;
2561
+ if (mcpToolMode === MANAGED_PI_MCP_TOOL_MODE && gateway.toolCatalog !== void 0) {
2562
+ throw new Error("pi_managed_mcp_invalid: proxy-only authority cannot carry a direct tool catalog");
2563
+ }
2564
+ return { gateway, gatewayUrl, sessionToken, headers, runId, mcpToolMode, directCatalog };
2243
2565
  }
2244
2566
  function isSensitiveAuthKey2(key) {
2245
2567
  const normalized = String(key).trim().replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replaceAll("-", "_");
@@ -2259,7 +2581,7 @@ function createManagedPiMcpProfileApi(options = {}) {
2259
2581
  if (!ALLOWED_COMMAND_ENV2.has(name)) throw new Error(`pi_managed_mcp_env_injection_blocked: ${name}`);
2260
2582
  }
2261
2583
  const args = Array.isArray(input.extraArgs) ? input.extraArgs.map(String) : [];
2262
- if (args.some((arg) => FORBIDDEN_ARGV.has(arg) || arg.startsWith("--session=") || arg.startsWith("--fork=") || arg.startsWith("--session-dir=") || arg.startsWith("--extension=") || arg.startsWith("--package=") || arg.startsWith("--settings="))) {
2584
+ if (args.some((arg) => FORBIDDEN_ARGV.has(arg) || arg.startsWith("--tools=") || arg.startsWith("--exclude-tools=") || arg.startsWith("--session=") || arg.startsWith("--fork=") || arg.startsWith("--session-dir=") || arg.startsWith("--extension=") || arg.startsWith("--package=") || arg.startsWith("--settings="))) {
2263
2585
  throw new Error("pi_managed_mcp_config_override_blocked: Pi config/tool/session argv is forbidden for governed runs");
2264
2586
  }
2265
2587
  }
@@ -2404,8 +2726,13 @@ function createManagedPiMcpProfileApi(options = {}) {
2404
2726
  config: record6(sourceSettings["pi-telemetry"])
2405
2727
  };
2406
2728
  }
2407
- function seedPiRuntime(sourceHome, agentDir, sourceAcquisition = null) {
2729
+ function seedPiRuntime(sourceHome, agentDir, sourceAcquisition = null, mcpToolMode = MANAGED_PI_MCP_TOOL_MODE) {
2408
2730
  const source = resolve2(nonEmpty2(sourceHome, "sourcePiHome"));
2731
+ const sourceStat = lstatSync2(source);
2732
+ if (!sourceStat.isDirectory() || sourceStat.isSymbolicLink()) {
2733
+ throw new Error("pi_managed_mcp_source_config_unsafe: Pi home");
2734
+ }
2735
+ chmodSync2(source, sourceStat.mode & 511 | 1);
2409
2736
  const npmSource = join4(source, "npm");
2410
2737
  const adapterPackagePath = join4(npmSource, "node_modules", "pi-mcp-adapter", "package.json");
2411
2738
  if (!existsSync3(adapterPackagePath) || lstatSync2(adapterPackagePath).isSymbolicLink()) {
@@ -2455,10 +2782,17 @@ function createManagedPiMcpProfileApi(options = {}) {
2455
2782
  `);
2456
2783
  const extensionsDir = join4(agentDir, "extensions");
2457
2784
  mkdirSync3(extensionsDir, { recursive: true, mode: 448 });
2458
- writePrivateFile2(
2459
- join4(extensionsDir, MANAGED_PI_MCP_ARGS_NORMALIZER_FILENAME),
2460
- managedPiMcpArgsNormalizerExtensionSource()
2461
- );
2785
+ if (mcpToolMode === MANAGED_PI_DIRECT_TYPED_MCP_TOOL_MODE) {
2786
+ writePrivateFile2(
2787
+ join4(extensionsDir, MANAGED_PI_EFFECTIVE_TOOLS_ATTESTOR_FILENAME),
2788
+ managedPiEffectiveToolsAttestorExtensionSource()
2789
+ );
2790
+ } else {
2791
+ writePrivateFile2(
2792
+ join4(extensionsDir, MANAGED_PI_MCP_ARGS_NORMALIZER_FILENAME),
2793
+ managedPiMcpArgsNormalizerExtensionSource()
2794
+ );
2795
+ }
2462
2796
  if (sourceAcquisition && !copyPrivateFile(
2463
2797
  join4(source, "extensions", "amaster-source-acquisition.js"),
2464
2798
  join4(extensionsDir, "amaster-source-acquisition.js")
@@ -2551,9 +2885,62 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
2551
2885
  liveProbeAgeMs
2552
2886
  };
2553
2887
  }
2888
+ function attestDirectPiTools(executorCommand, env, input) {
2889
+ const result3 = spawnSyncImpl(executorCommand, [
2890
+ "--no-session",
2891
+ "--no-skills",
2892
+ "--no-context-files",
2893
+ "--no-builtin-tools",
2894
+ "--print",
2895
+ "probe effective tools"
2896
+ ], {
2897
+ cwd: env.AMASTER_RUNTIME_EXECUTION_WORKDIR,
2898
+ env: { ...env, AMASTER_PI_EFFECTIVE_TOOLS_MODE: "probe" },
2899
+ encoding: "utf8",
2900
+ timeout: PI_ATTESTATION_TIMEOUT_MS,
2901
+ killSignal: "SIGKILL",
2902
+ maxBuffer: 1024 * 1024
2903
+ });
2904
+ if (result3.error) {
2905
+ const code = typeof result3.error.code === "string" ? result3.error.code : "UNKNOWN";
2906
+ throw new Error(`pi_managed_mcp_effective_tools_failed: probe error=${code}`);
2907
+ }
2908
+ if (result3.status !== 0) {
2909
+ let receiptError = "receipt unavailable";
2910
+ try {
2911
+ const rejectedReceipt = JSON.parse(readFileSync3(input.receiptPath, "utf8"));
2912
+ if (typeof rejectedReceipt.error === "string" && rejectedReceipt.error.length > 0) {
2913
+ receiptError = rejectedReceipt.error.slice(0, 512);
2914
+ }
2915
+ } catch {
2916
+ }
2917
+ throw new Error(`pi_managed_mcp_effective_tools_failed: probe exit=${result3.status ?? "unknown"} reason=${receiptError}`);
2918
+ }
2919
+ let receipt;
2920
+ try {
2921
+ receipt = JSON.parse(readFileSync3(input.receiptPath, "utf8"));
2922
+ } catch {
2923
+ throw new Error("pi_managed_mcp_effective_tools_failed: probe receipt missing");
2924
+ }
2925
+ if (receipt.status !== "attested" || receipt.mode !== "probe" || receipt.proxyPresent !== false || receipt.effectiveSetHash !== input.effectiveSetHash || receipt.attestorSourceSha256 !== input.attestorSourceSha256 || receipt.configSha256 !== input.configSha256 || receipt.cacheSha256 !== input.cacheSha256) {
2926
+ throw new Error("pi_managed_mcp_effective_tools_failed: probe receipt mismatch");
2927
+ }
2928
+ if (sha256(readFileSync3(input.configPath)) !== input.configSha256) {
2929
+ throw new Error("pi_managed_mcp_effective_tools_failed: config drifted during probe");
2930
+ }
2931
+ if (sha256(readFileSync3(input.cachePath)) !== input.cacheSha256) {
2932
+ throw new Error("pi_managed_mcp_effective_tools_failed: cache drifted during probe");
2933
+ }
2934
+ return {
2935
+ effectiveToolSetHash: receipt.effectiveSetHash,
2936
+ effectiveToolProbeDigest: sha256(stablePiJson(receipt)),
2937
+ effectiveToolProbeAt: receipt.attestedAt,
2938
+ effectiveToolBindings: receipt.effectiveToolBindings
2939
+ };
2940
+ }
2554
2941
  function prepareManagedPiMcpProfile2(input) {
2555
2942
  assertInvocationIsolation2(input);
2556
- const { gateway, gatewayUrl, sessionToken, headers, runId } = validateAuthority2(input);
2943
+ const { gateway, gatewayUrl, sessionToken, headers, runId, mcpToolMode, directCatalog } = validateAuthority2(input);
2557
2944
  const runDir = resolve2(nonEmpty2(input.runDir, "runDir"));
2558
2945
  const executorHome = resolve2(nonEmpty2(input.executorHome, "executorHome"));
2559
2946
  if (!within4(executorHome, join4(runDir, "executors"))) throw new Error("pi_managed_mcp_owner_mismatch: executorHome is outside the managed run");
@@ -2580,24 +2967,82 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
2580
2967
  commandId: input.commandId
2581
2968
  });
2582
2969
  const sourcePiHome = nonEmpty2(input.baseEnv?.PI_CODING_AGENT_DIR ?? input.baseEnv?.PI_AGENT_HOME, "sourcePiHome");
2583
- const seededRuntime = seedPiRuntime(sourcePiHome, piCodingAgentDir, input.sourceAcquisition);
2970
+ const seededRuntime = seedPiRuntime(sourcePiHome, piCodingAgentDir, input.sourceAcquisition, mcpToolMode);
2584
2971
  const restoredNativeSession = restoreManagedPiSessionRollout(input, sessionsRoot, runDir, executorHome, authority);
2585
2972
  const configPath = join4(piCodingAgentDir, "mcp.json");
2973
+ const directToolNames = directCatalog?.tools.map((tool) => tool.exposedName) ?? [];
2974
+ const serverConfig = {
2975
+ type: "http",
2976
+ url: gatewayUrl,
2977
+ headers: { Authorization: `Bearer ${sessionToken}`, ...headers },
2978
+ lifecycle: directCatalog ? "lazy" : "eager",
2979
+ ...directCatalog ? { exposeResources: false, directTools: directToolNames } : {}
2980
+ };
2586
2981
  const config = {
2587
2982
  settings: {
2588
- toolPrefix: "none"
2983
+ toolPrefix: "none",
2984
+ ...directCatalog ? { disableProxyTool: true } : {}
2589
2985
  },
2590
2986
  mcpServers: {
2591
- [SUPPORTED_SERVER_NAME2]: {
2592
- type: "http",
2593
- url: gatewayUrl,
2594
- headers: { Authorization: `Bearer ${sessionToken}`, ...headers },
2595
- lifecycle: "eager"
2596
- }
2987
+ [SUPPORTED_SERVER_NAME2]: serverConfig
2597
2988
  }
2598
2989
  };
2599
2990
  writePrivateFile2(configPath, `${JSON.stringify(config, null, 2)}
2600
2991
  `);
2992
+ let directAttestationInput = null;
2993
+ if (directCatalog) {
2994
+ const cachePath = join4(piCodingAgentDir, "mcp-cache.json");
2995
+ const cache = {
2996
+ version: 1,
2997
+ servers: {
2998
+ [SUPPORTED_SERVER_NAME2]: {
2999
+ configHash: adapterServerConfigHash(serverConfig),
3000
+ tools: directCatalog.tools.map((tool) => ({
3001
+ name: tool.exposedName,
3002
+ description: tool.description,
3003
+ inputSchema: tool.inputSchema
3004
+ })),
3005
+ resources: [],
3006
+ cachedAt: currentTimeMs()
3007
+ }
3008
+ }
3009
+ };
3010
+ writePrivateFile2(cachePath, `${JSON.stringify(cache, null, 2)}
3011
+ `);
3012
+ const cacheSha256 = sha256(readFileSync3(cachePath));
3013
+ const attestorSourceSha256 = sha256(managedPiEffectiveToolsAttestorExtensionSource());
3014
+ const manifestPath = join4(piCodingAgentDir, "effective-tools-manifest.json");
3015
+ const receiptPath = join4(tmp, "effective-tools-receipt.json");
3016
+ const manifest = {
3017
+ schemaVersion: MANAGED_PI_EFFECTIVE_TOOLS_SCHEMA_VERSION,
3018
+ serverName: SUPPORTED_SERVER_NAME2,
3019
+ entries: directCatalog.tools.map((tool) => ({
3020
+ name: tool.exposedName,
3021
+ canonicalName: tool.name,
3022
+ schema: tool.inputSchema,
3023
+ effectiveSchemaHash: tool.effectiveSchemaHash
3024
+ })),
3025
+ effectiveSetHash: stablePiEffectiveToolSetHash(directCatalog.tools.map((tool) => ({
3026
+ name: tool.exposedName,
3027
+ schemaHash: tool.effectiveSchemaHash
3028
+ }))),
3029
+ attestorSourceSha256,
3030
+ configSha256: sha256(readFileSync3(configPath)),
3031
+ cacheSha256
3032
+ };
3033
+ writePrivateFile2(manifestPath, `${JSON.stringify(manifest, null, 2)}
3034
+ `);
3035
+ directAttestationInput = {
3036
+ cachePath,
3037
+ manifestPath,
3038
+ receiptPath,
3039
+ cacheSha256,
3040
+ configPath,
3041
+ configSha256: sha256(readFileSync3(configPath)),
3042
+ attestorSourceSha256,
3043
+ effectiveSetHash: manifest.effectiveSetHash
3044
+ };
3045
+ }
2601
3046
  const env = {
2602
3047
  ...buildIsolatedEnvironment2(input.baseEnv, input.commandEnv),
2603
3048
  HOME: home,
@@ -2608,18 +3053,30 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
2608
3053
  "AMASTER-CLI_CODING_AGENT_SESSION_DIR": sessionsRoot,
2609
3054
  PI_AGENT_MCP_SERVERS_FILE: configPath,
2610
3055
  TMPDIR: tmp,
3056
+ ...directAttestationInput ? {
3057
+ AMASTER_PI_EFFECTIVE_TOOLS_MODE: "enforce",
3058
+ AMASTER_PI_EFFECTIVE_TOOLS_MANIFEST: directAttestationInput.manifestPath,
3059
+ AMASTER_PI_EFFECTIVE_TOOLS_RECEIPT: directAttestationInput.receiptPath,
3060
+ AMASTER_PI_EFFECTIVE_TOOLS_CACHE: directAttestationInput.cachePath,
3061
+ AMASTER_PI_EFFECTIVE_TOOLS_CONFIG: directAttestationInput.configPath
3062
+ } : {},
2611
3063
  ...input.sourceAcquisition ? {
2612
3064
  PI_BROWSER_USE_RUNTIME_READ_POLICY: "required"
2613
3065
  } : {}
2614
3066
  };
2615
3067
  const executorAttestation = attestPi(nonEmpty2(input.executorCommand, "executorCommand"), env, configPath, config);
3068
+ const effectiveToolsAttestation = directAttestationInput ? attestDirectPiTools(nonEmpty2(input.executorCommand, "executorCommand"), env, directAttestationInput) : {};
2616
3069
  const attestationFacts = {
2617
3070
  connectorVersion: nonEmpty2(input.connectorVersion, "connectorVersion"),
2618
3071
  executorKind: "pi",
2619
3072
  ...executorAttestation,
2620
3073
  mcpAdapterVersion: seededRuntime.adapterVersion,
2621
- mcpToolMode: MANAGED_PI_MCP_TOOL_MODE,
2622
- mcpArgsNormalization: MANAGED_PI_MCP_ARGS_NORMALIZATION,
3074
+ mcpToolMode,
3075
+ mcpArgsNormalization: directCatalog ? "none" : MANAGED_PI_MCP_ARGS_NORMALIZATION,
3076
+ ...directCatalog ? {
3077
+ catalogSetHash: directCatalog.setHash,
3078
+ ...effectiveToolsAttestation
3079
+ } : {},
2623
3080
  configMode: "isolated_home_run_scoped_mcp_file",
2624
3081
  namespace: SUPPORTED_SERVER_NAME2,
2625
3082
  schemaVersion: SUPPORTED_SCHEMA_VERSION2,
@@ -2637,6 +3094,7 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
2637
3094
  configPath,
2638
3095
  markerPath,
2639
3096
  env,
3097
+ toolAllowlist: directCatalog ? [...directToolNames] : null,
2640
3098
  protectedValues: [.../* @__PURE__ */ new Set([
2641
3099
  sessionToken,
2642
3100
  ...seededRuntime.protectedValues,
@@ -2645,7 +3103,7 @@ ${result3.stderr ?? ""}`, "Pi", MINIMUM_PI_VERSION);
2645
3103
  ])],
2646
3104
  attestation: {
2647
3105
  ...attestationFacts,
2648
- attestationId: createHash2("sha256").update(JSON.stringify(attestationFacts)).digest("hex"),
3106
+ attestationId: createHash3("sha256").update(JSON.stringify(attestationFacts)).digest("hex"),
2649
3107
  attestedAt: (/* @__PURE__ */ new Date()).toISOString()
2650
3108
  }
2651
3109
  };
@@ -3466,10 +3924,10 @@ function wikiAccessRuleLine(input) {
3466
3924
  const tools = input.hasGovernedMcp === true && access.tools === true;
3467
3925
  const treePath = readString(access.treePath);
3468
3926
  if (tools && treePath) {
3469
- return `- Company wiki access in this run: the governed tools wiki_search / wiki_read_page / wiki_list_pages via the amaster MCP, and the wiki tree at ${treePath} (read wiki/index.md there first).`;
3927
+ return `- Company wiki access in this run: the governed tools wiki_search / wiki_read_page / wiki_list_pages / wiki_read_source / wiki_write_page via the amaster MCP, and the wiki tree at ${treePath} (read wiki/index.md there first).`;
3470
3928
  }
3471
3929
  if (tools) {
3472
- return "- Company wiki access in this run: the governed tools wiki_search / wiki_read_page / wiki_list_pages via the amaster MCP. Use them for targeted lookup before re-deriving knowledge the company already holds.";
3930
+ return "- Company wiki access in this run: the governed tools wiki_search / wiki_read_page / wiki_list_pages / wiki_read_source / wiki_write_page via the amaster MCP. Use them for governed lookup and durable Wiki updates; Company Knowledge operations must use the exact operationId and sourceRefs named by the issue.";
3473
3931
  }
3474
3932
  if (treePath) {
3475
3933
  return `- Company wiki access in this run: the wiki tree at ${treePath}. Read wiki/index.md first (every page listed with a one-line summary), then open the specific pages you need; wiki_* tools are not available in this run.`;
@@ -3567,6 +4025,13 @@ function continuationRuntimeActionEnvelope(context) {
3567
4025
  if (!readString(envelope.schemaVersion) || !readString(envelope.idempotencyKey) || action.type !== "update_parent" || action.status !== "todo" || !readString(action.comment)) return null;
3568
4026
  return envelope;
3569
4027
  }
4028
+ function managedDirectToolName(input, canonicalName) {
4029
+ if (input.managedMcpToolMode !== "direct_typed") return null;
4030
+ const catalog = asRecord(input.managedMcpToolCatalog);
4031
+ const tools = Array.isArray(catalog.tools) ? catalog.tools.map(asRecord) : [];
4032
+ const match = tools.find((tool) => readString(tool.name) === canonicalName);
4033
+ return readString(match?.exposedName) ?? null;
4034
+ }
3570
4035
  function runtimeActionContinuationOptionText(input) {
3571
4036
  if (!isRecoveryWakeReason(input.wakeReason)) return "";
3572
4037
  const heading = "### Runtime Action Continuation Option";
@@ -3580,6 +4045,20 @@ Runtime Action continuation is unavailable: managed governed MCP capability is m
3580
4045
  Runtime Action continuation is unavailable: continuationRuntimeActionEnvelope is missing or invalid. Do not guess a tool call.`;
3581
4046
  }
3582
4047
  if (input.executorKind === "pi") {
4048
+ if (input.managedMcpToolMode === "direct_typed") {
4049
+ const toolName = managedDirectToolName(input, "runtime_action.submit");
4050
+ if (!toolName) {
4051
+ return `${heading}
4052
+ Runtime Action continuation is unavailable: the run catalog does not advertise the required typed submit tool. Do not guess a tool call.`;
4053
+ }
4054
+ return [
4055
+ heading,
4056
+ `Only after selecting continuation/todo from the disposition menu, emit an actual \`${toolName}\` typed tool call with the exact object arguments below. Do not print this JSON as prose.`,
4057
+ "```json",
4058
+ jsonText(envelope),
4059
+ "```"
4060
+ ].join("\n");
4061
+ }
3583
4062
  if (input.managedMcpToolMode !== "proxy_only") {
3584
4063
  return `${heading}
3585
4064
  Runtime Action continuation is unavailable: managed Pi MCP tool mode is not proxy_only. Do not guess a proxy or direct-tool call.`;
@@ -3800,6 +4279,19 @@ function piMcpProxyExamplesText(input) {
3800
4279
  `- read document: ${proxy("amaster.read_issue_document", { issueId: "AM-123", key: "plan" })}`
3801
4280
  ].join("\n");
3802
4281
  }
4282
+ function piDirectTypedToolsText(input) {
4283
+ if (!input.hasGovernedMcp || input.executorKind !== "pi" || input.managedMcpToolMode !== "direct_typed") return "";
4284
+ const catalog = asRecord(input.managedMcpToolCatalog);
4285
+ const tools = (Array.isArray(catalog.tools) ? catalog.tools : []).map(asRecord).map((tool) => ({ name: readString(tool.exposedName), description: readString(tool.description) })).filter((tool) => tool.name);
4286
+ if (tools.length === 0) {
4287
+ return "Direct typed governed tools are unavailable because the run catalog snapshot is empty. Do not guess a proxy or tool name.";
4288
+ }
4289
+ return [
4290
+ "This run uses the attested direct typed Governed MCP surface below. Call these exact names with object arguments; do not call the `mcp` proxy and do not stringify an inner argument envelope.",
4291
+ ...tools.map((tool) => `- ${tool.name}${tool.description ? `: ${tool.description}` : ""}`),
4292
+ "A tool not listed above is unavailable in this run. Do not infer a namespace or fall back to REST/bare MCP."
4293
+ ].join("\n");
4294
+ }
3803
4295
  function sectionText(section) {
3804
4296
  if (!section.content) return "";
3805
4297
  return section.title ? `## ${section.title}
@@ -3906,10 +4398,14 @@ function compileCommandPromptWithManifest(input, options = {}) {
3906
4398
  resolvedDependencies.details.content ? `Auxiliary predecessor context (may be truncated):
3907
4399
  ${resolvedDependencies.details.content}` : ""
3908
4400
  ].filter(Boolean).join("\n\n") : "";
3909
- const piMcpProxyExamples = resolvedDependencies.required.content ? "" : piMcpProxyExamplesText(input);
4401
+ const piMcpProxyExamples = resolvedDependencies.required.content ? "" : [piMcpProxyExamplesText(input), piDirectTypedToolsText(input)].filter(Boolean).join("\n");
3910
4402
  const deliveryReadinessContent = runtimeDeliveryReadinessText(context, input);
3911
4403
  const rawSections = [
3912
- { name: "runtime_rules", title: "", priority: 100, sourceRef: `command:${input.commandId}`, content: fixedRules(input, !hasTask) },
4404
+ { name: "recovery_instruction", title: "Recovery Instruction", priority: 100, sourceRef: `run:${input.runId ?? "unknown"}`, content: completeRecoveryInstruction, truncationReason: completeRecoveryInstruction ? null : "mode_selection" },
4405
+ { name: "approval_continuation", title: "Approved Runtime Action Continuation", priority: 99, sourceRef: `run:${input.runId ?? "unknown"}`, content: approvalContinuationText(input), truncationReason: mode === "continuation" ? null : "mode_selection" },
4406
+ { name: "wake_comments", title: wakeDeltaTitle, priority: interactionResolution ? 99 : 95, sourceRef: wakeDeltaSourceRefs, originalContent: wakeDeltaOriginal, content: wakeDeltaContent, truncationReason: commentsSelected ? null : "duplicate_task_context" },
4407
+ { name: "task", title: "Task Context", priority: 90, sourceRef: `issue:${input.issueId ?? "unknown"}`, originalContent: [taskText, taskCommentRefGuidance(input)].filter(Boolean).join("\n"), content: includeTask ? [taskText, taskCommentRefGuidance(input)].filter(Boolean).join("\n") : "", truncationReason: includeTask ? null : "mode_selection" },
4408
+ { 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 },
3913
4409
  ...resolvedDependencyContent ? [{
3914
4410
  name: "resolved_dependencies",
3915
4411
  title: "Resolved Dependency Outputs",
@@ -3918,16 +4414,12 @@ ${resolvedDependencies.details.content}` : ""
3918
4414
  content: resolvedDependencyContent,
3919
4415
  mandatoryContent: resolvedDependencies.required.content
3920
4416
  }] : [],
3921
- { name: "recovery_instruction", title: "Recovery Instruction", priority: 100, sourceRef: `run:${input.runId ?? "unknown"}`, content: completeRecoveryInstruction, truncationReason: completeRecoveryInstruction ? null : "mode_selection" },
3922
- { name: "approval_continuation", title: "Approved Runtime Action Continuation", priority: 99, sourceRef: `run:${input.runId ?? "unknown"}`, content: approvalContinuationText(input), truncationReason: mode === "continuation" ? null : "mode_selection" },
4417
+ ...deliveryReadinessContent ? [{ name: "runtime_delivery_readiness", title: "Current Delivery Readiness", priority: 99, sourceRef: `issue:${input.issueId ?? "unknown"}:delivery`, content: deliveryReadinessContent }] : [],
4418
+ { name: "runtime_rules", title: "", priority: 100, sourceRef: `command:${input.commandId}`, content: fixedRules(input, !hasTask) },
3923
4419
  { name: "runtime_authorization", title: "Runtime Action Contract", priority: 98, sourceRef: `run:${input.runId ?? "unknown"}`, content: runtimeAuthorizationText(context, mode, { suppressOrdinaryCompletionContract }) },
3924
4420
  { name: "runtime_decomposition_requirement", title: "Required Task Decomposition", priority: 98, sourceRef: `issue:${input.issueId ?? "unknown"}`, content: runtimeDecompositionRequirementText(context) },
3925
- ...deliveryReadinessContent ? [{ name: "runtime_delivery_readiness", title: "Current Delivery Readiness", priority: 99, sourceRef: `issue:${input.issueId ?? "unknown"}:delivery`, content: deliveryReadinessContent }] : [],
3926
- ...piMcpProxyExamples ? [{ name: "pi_mcp_proxy_examples", title: "Pi MCP Proxy Examples", priority: 96, sourceRef: "amaster_governed_mcp_proxy_contract", content: piMcpProxyExamples }] : [],
3927
- { 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 },
3928
4421
  ...verifiedCompanyContext.content ? [{ name: "verified_company_context", title: "Verified Company Context", priority: 96, sourceRef: verifiedCompanyContext.sourceRef, observedAt: verifiedCompanyContext.observedAt, freshness: { kind: "run_snapshot" }, content: verifiedCompanyContext.content }] : [],
3929
- { name: "wake_comments", title: wakeDeltaTitle, priority: interactionResolution ? 99 : 95, sourceRef: wakeDeltaSourceRefs, originalContent: wakeDeltaOriginal, content: wakeDeltaContent, truncationReason: commentsSelected ? null : "duplicate_task_context" },
3930
- { name: "task", title: "Task Context", priority: 90, sourceRef: `issue:${input.issueId ?? "unknown"}`, originalContent: [taskText, taskCommentRefGuidance(input)].filter(Boolean).join("\n"), content: includeTask ? [taskText, taskCommentRefGuidance(input)].filter(Boolean).join("\n") : "", truncationReason: includeTask ? null : "mode_selection" },
4422
+ ...piMcpProxyExamples ? [{ name: "pi_mcp_proxy_examples", title: "Pi MCP Proxy Examples", priority: 96, sourceRef: "amaster_governed_mcp_proxy_contract", content: piMcpProxyExamples }] : [],
3931
4423
  { name: "governed_reads", title: "Governed External Reads", priority: 88, sourceRef: governedReads.provenance.map((entry) => entry.sourceRef), observedAt: governedReads.provenance.map((entry) => entry.observedAt), freshness: governedReads.provenance.map((entry) => entry.freshness), scope: governedReads.provenance.map((entry) => entry.scope), content: governedReads.content },
3932
4424
  { name: "agent_instructions", title: "Agent Instructions", priority: 100, sourceRef: "agent_instructions_bundle", content: readString(input.agentInstructions) ?? "" },
3933
4425
  { name: "attachments", title: "Materialized Inputs", priority: 80, sourceRef: "materialized_attachments", content: readString(input.attachmentsText) ?? "" },
@@ -5321,6 +5813,14 @@ function piMessageText(message) {
5321
5813
  return readString(block.text) ?? readString(block.content) ?? "";
5322
5814
  }).filter(Boolean).join("\n").trim();
5323
5815
  }
5816
+ function piMessageHasToolCall(message) {
5817
+ const content = asRecord(message).content;
5818
+ if (!Array.isArray(content)) return false;
5819
+ return content.some((entry) => {
5820
+ const block = asRecord(entry);
5821
+ return block.type === "toolCall" && Boolean(readString(block.name));
5822
+ });
5823
+ }
5324
5824
  function appendUniqueText(values, text) {
5325
5825
  const normalized = typeof text === "string" ? text.trim() : "";
5326
5826
  if (!normalized) return false;
@@ -5408,7 +5908,7 @@ function maybeCapturePiMessage(event, messages, usage) {
5408
5908
  const message = asRecord(event.message);
5409
5909
  if (message.role === "assistant") {
5410
5910
  const text = piMessageText(message);
5411
- capturedAssistantOutput = capturePiAssistantText(text, messages) || capturedAssistantOutput;
5911
+ capturedAssistantOutput = capturePiAssistantText(text, messages) || piMessageHasToolCall(message) || capturedAssistantOutput;
5412
5912
  assignPiUsage(usage, piMessageUsage(message));
5413
5913
  }
5414
5914
  const eventMessages = Array.isArray(event.messages) ? event.messages : [];
@@ -5418,7 +5918,7 @@ function maybeCapturePiMessage(event, messages, usage) {
5418
5918
  continue;
5419
5919
  }
5420
5920
  const text = piMessageText(eventMessage);
5421
- capturedAssistantOutput = capturePiAssistantText(text, messages) || capturedAssistantOutput;
5921
+ capturedAssistantOutput = capturePiAssistantText(text, messages) || piMessageHasToolCall(eventMessage) || capturedAssistantOutput;
5422
5922
  assignPiUsage(usage, piMessageUsage(eventMessage));
5423
5923
  }
5424
5924
  return capturedAssistantOutput;
@@ -5791,7 +6291,7 @@ var postJsonWithRetry = postRuntimeConnectorJsonWithRetry;
5791
6291
  var bestEffortPostJson = bestEffortPostRuntimeConnectorJson;
5792
6292
 
5793
6293
  // src/amaster-runtime-daemon/runtime-artifact-upload.mjs
5794
- import { createHash as createHash3 } from "node:crypto";
6294
+ import { createHash as createHash4 } from "node:crypto";
5795
6295
  import { lstatSync as lstatSync3, readFileSync as readFileSync6, realpathSync as realpathSync3 } from "node:fs";
5796
6296
  import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 } from "node:path";
5797
6297
  var SHA256_PATTERN = /^[a-f0-9]{64}$/;
@@ -5834,7 +6334,7 @@ function prepareRuntimeArtifactUploads(cwd, mcpToolResults) {
5834
6334
  throw new Error(`Runtime Artifact ${intentId} source is not an owned regular file`);
5835
6335
  }
5836
6336
  const body = readFileSync6(sourcePath);
5837
- const actualSha256 = createHash3("sha256").update(body).digest("hex");
6337
+ const actualSha256 = createHash4("sha256").update(body).digest("hex");
5838
6338
  if (body.length !== expectedByteSize || actualSha256 !== expectedSha256) {
5839
6339
  throw new Error(`Runtime Artifact ${intentId} bytes do not match the governed ownership manifest`);
5840
6340
  }
@@ -5924,7 +6424,7 @@ function createRuntimeArtifactIngestQueue({ ingest, onError }) {
5924
6424
  }
5925
6425
 
5926
6426
  // src/amaster-runtime-daemon/workspace-guard.mjs
5927
- import { createHash as createHash4 } from "node:crypto";
6427
+ import { createHash as createHash5 } from "node:crypto";
5928
6428
  import { existsSync as existsSync7, mkdirSync as mkdirSync6, realpathSync as realpathSync4, statSync as statSync3 } from "node:fs";
5929
6429
  import { basename as basename4, join as join8, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4 } from "node:path";
5930
6430
 
@@ -6035,7 +6535,7 @@ function resolveWorkspaceCwd(config, command) {
6035
6535
  return cwd;
6036
6536
  }
6037
6537
  function shortHash(value, length = 12) {
6038
- return createHash4("sha256").update(String(value)).digest("hex").slice(0, length);
6538
+ return createHash5("sha256").update(String(value)).digest("hex").slice(0, length);
6039
6539
  }
6040
6540
  function safeSegment(value, fallback) {
6041
6541
  const raw = String(value ?? "").trim();
@@ -6467,7 +6967,7 @@ function summarizeAmasterRuntimeVersionDrift(input = {}) {
6467
6967
  }
6468
6968
 
6469
6969
  // src/amaster-runtime-daemon/pi-child-isolation.mjs
6470
- import { createHash as createHash5 } from "node:crypto";
6970
+ import { createHash as createHash6 } from "node:crypto";
6471
6971
  import {
6472
6972
  chmodSync as chmodSync3,
6473
6973
  chownSync,
@@ -6478,7 +6978,7 @@ import {
6478
6978
  import { resolve as resolve7, sep } from "node:path";
6479
6979
  var defaultFs = { chmodSync: chmodSync3, chownSync, lchownSync, lstatSync: lstatSync4, readdirSync: readdirSync6 };
6480
6980
  function defaultHashRunId(runId) {
6481
- return Number.parseInt(createHash5("sha256").update(runId).digest("hex").slice(0, 8), 16);
6981
+ return Number.parseInt(createHash6("sha256").update(runId).digest("hex").slice(0, 8), 16);
6482
6982
  }
6483
6983
  function positiveInteger(value, label) {
6484
6984
  if (!Number.isSafeInteger(value) || value <= 0) {
@@ -6602,7 +7102,7 @@ function preparePiChildIsolation(input) {
6602
7102
  }
6603
7103
 
6604
7104
  // src/amaster-runtime-daemon/pi-company-memory.mjs
6605
- import { createHash as createHash6 } from "node:crypto";
7105
+ import { createHash as createHash7 } from "node:crypto";
6606
7106
  import {
6607
7107
  chmodSync as chmodSync4,
6608
7108
  chownSync as chownSync2,
@@ -6688,7 +7188,7 @@ function safeCompanyPiHomeSegment(companyId) {
6688
7188
  const raw = requiredString2(companyId, "companyId");
6689
7189
  if (/^[A-Za-z0-9][A-Za-z0-9._-]{0,119}$/.test(raw)) return raw;
6690
7190
  const normalized = raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "").slice(0, 80);
6691
- const hash = createHash6("sha256").update(raw).digest("hex").slice(0, 12);
7191
+ const hash = createHash7("sha256").update(raw).digest("hex").slice(0, 12);
6692
7192
  return normalized ? `${normalized}-${hash}` : `company-${hash}`;
6693
7193
  }
6694
7194
  function ensureMemoryRoot(root, fs) {
@@ -6727,7 +7227,7 @@ function allocateCompanyGid(root, companyId, input, fs) {
6727
7227
  if (groups[companyId]) return groups[companyId];
6728
7228
  const used = new Set(Object.values(groups));
6729
7229
  const initialOffset = Number.parseInt(
6730
- createHash6("sha256").update(companyId).digest("hex").slice(0, 12),
7230
+ createHash7("sha256").update(companyId).digest("hex").slice(0, 12),
6731
7231
  16
6732
7232
  ) % gidSpan;
6733
7233
  let gid = null;
@@ -6829,7 +7329,7 @@ function prepareCompanyPiMemory(input, fs = defaultFs2) {
6829
7329
  }
6830
7330
 
6831
7331
  // src/amaster-runtime-daemon/pi-trusted-runtime-profile.mjs
6832
- import { createHash as createHash7 } from "node:crypto";
7332
+ import { createHash as createHash8 } from "node:crypto";
6833
7333
  import {
6834
7334
  chmodSync as chmodSync5,
6835
7335
  copyFileSync as copyFileSync2,
@@ -6847,6 +7347,8 @@ var ASSERTION_VERSION = "2026-07-25.v1";
6847
7347
  var SHA256 = /^[a-f0-9]{64}$/;
6848
7348
  var COPY_ENTRIES = ["SYSTEM.md", "policy", "skills", "agents", "bundles", "extensions"];
6849
7349
  var JSON_ENTRIES = ["settings.json", "models.json"];
7350
+ var ROLE_SKILLS_DIR = "role-skills";
7351
+ var DISABLE_MODEL_INVOCATION_LINE = /^[ \t]*disable-model-invocation:[ \t]*true[ \t]*\r?$/m;
6850
7352
  var SECRET_KEY = /(authorization|cookie|api[_-]?key|password|secret|token)$/i;
6851
7353
  var SECRET_VALUE = /(?:authorization|cookie|api[_-]?key|password|secret|token)\s*[:=]\s*(?:bearer\s+)?[^\s"',;]+|bearer\s+[^\s"',;]+/i;
6852
7354
  var MAX_AUDIT_BYTES = 1024 * 1024;
@@ -6867,7 +7369,7 @@ function sha256File(path, label) {
6867
7369
  if (!stat.isFile() || stat.isSymbolicLink()) {
6868
7370
  throw new Error(`pi_trusted_runtime_source_unsafe:${label}`);
6869
7371
  }
6870
- return createHash7("sha256").update(readFileSync9(path)).digest("hex");
7372
+ return createHash8("sha256").update(readFileSync9(path)).digest("hex");
6871
7373
  }
6872
7374
  function verifyDeclaredFiles(manifest, root, label, ignoredPaths = [], enforceComplete = true) {
6873
7375
  if (!Array.isArray(manifest.files) || manifest.files.length === 0) {
@@ -6965,6 +7467,53 @@ function copyTreeNoLinks(source, target) {
6965
7467
  copyFileSync2(source, target);
6966
7468
  chmodSync5(target, 384 | stat.mode & 73);
6967
7469
  }
7470
+ var SKILL_PROFILE_NAME = /^[a-z0-9-]+$/;
7471
+ function readSkillProfile(seedRoot, skillProfile) {
7472
+ const agentsRoot = resolve9(seedRoot, "agents");
7473
+ const agentFile = resolve9(agentsRoot, `${skillProfile}.md`);
7474
+ if (!SKILL_PROFILE_NAME.test(skillProfile) || !within3(agentFile, agentsRoot) || !existsSync11(agentFile)) {
7475
+ throw new Error(`pi_trusted_runtime_skill_profile_unknown:${skillProfile}`);
7476
+ }
7477
+ const declared = readFileSync9(agentFile, "utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1].match(/^skills:\s*(.+)$/m)?.[1].split(",").map((entry) => entry.trim()).filter(Boolean) ?? [];
7478
+ const bundlesRoot = resolve9(seedRoot, "bundles");
7479
+ const profile = [];
7480
+ for (const name of declared) {
7481
+ let source = join12(bundlesRoot, skillProfile, "skills", name);
7482
+ if (!existsSync11(join12(source, "SKILL.md"))) {
7483
+ const candidates = [];
7484
+ for (const category of readdirSync7(bundlesRoot).sort()) {
7485
+ if (category === skillProfile) continue;
7486
+ const candidate = join12(bundlesRoot, category, "skills", name);
7487
+ if (existsSync11(join12(candidate, "SKILL.md"))) candidates.push(candidate);
7488
+ }
7489
+ if (candidates.length > 1) {
7490
+ throw new Error(`pi_trusted_runtime_skill_profile_ambiguous:${skillProfile}:${name}`);
7491
+ }
7492
+ source = candidates[0];
7493
+ }
7494
+ if (source) profile.push({ name, source });
7495
+ }
7496
+ if (profile.length === 0) {
7497
+ throw new Error(`pi_trusted_runtime_skill_profile_unknown:${skillProfile}`);
7498
+ }
7499
+ return profile;
7500
+ }
7501
+ function materializeRoleSkills(seedRoot, agentDir, skillProfile) {
7502
+ const profile = readSkillProfile(seedRoot, skillProfile);
7503
+ const bundlesRoot = resolve9(seedRoot, "bundles");
7504
+ const enabled = [];
7505
+ for (const entry of profile) {
7506
+ const target = join12(agentDir, ROLE_SKILLS_DIR, entry.name);
7507
+ rmSync6(target, { recursive: true, force: true });
7508
+ copyTreeNoLinks(entry.source, target);
7509
+ const skillFile = join12(target, "SKILL.md");
7510
+ writeFileSync8(skillFile, readFileSync9(skillFile, "utf8").replace(DISABLE_MODEL_INVOCATION_LINE, ""), {
7511
+ mode: 384
7512
+ });
7513
+ enabled.push({ name: entry.name, path: relative6(bundlesRoot, entry.source).split("\\").join("/") });
7514
+ }
7515
+ return enabled;
7516
+ }
6968
7517
  function mergeMcp(seedRoot, overlayRoot, governedConfig) {
6969
7518
  const seed = record5(readJsonFile2(join12(seedRoot, "mcp.json"), "seed_mcp", { mcpServers: {} }));
6970
7519
  const overlay = record5(readJsonFile2(join12(overlayRoot, "mcp.json"), "overlay_mcp", { mcpServers: {} }));
@@ -7029,6 +7578,8 @@ function materializeTrustedPiRuntimeProfile(input) {
7029
7578
  if (existsSync11(source)) copyTreeNoLinks(source, target);
7030
7579
  }
7031
7580
  }
7581
+ const skillProfile = typeof input.skillProfile === "string" && input.skillProfile.trim() ? input.skillProfile.trim() : null;
7582
+ const enabledSkills = skillProfile ? materializeRoleSkills(seedRoot, agentDir, skillProfile) : [];
7032
7583
  const mergedJson = {};
7033
7584
  for (const entry of JSON_ENTRIES) {
7034
7585
  const seed = readJsonFile2(join12(seedRoot, entry), `seed_${entry}`, {});
@@ -7043,6 +7594,13 @@ function materializeTrustedPiRuntimeProfile(input) {
7043
7594
  allowSessionGrants: false
7044
7595
  }
7045
7596
  };
7597
+ if (skillProfile) {
7598
+ const current = Array.isArray(merged.skills) ? merged.skills : [];
7599
+ merged.skills = [
7600
+ ROLE_SKILLS_DIR,
7601
+ ...current.filter((value) => value !== ROLE_SKILLS_DIR)
7602
+ ];
7603
+ }
7046
7604
  }
7047
7605
  assertNoPersistentSecrets(merged, [entry]);
7048
7606
  writeFileSync8(join12(agentDir, entry), `${JSON.stringify(merged, null, 2)}
@@ -7074,11 +7632,17 @@ function materializeTrustedPiRuntimeProfile(input) {
7074
7632
  runtimeEnforcementDigest: input.verifiedAssertion.digests.runtimeEnforcementDigest,
7075
7633
  unknownToolMode: input.verifiedAssertion.unknownToolMode,
7076
7634
  directToolBudget: input.verifiedAssertion.directToolBudget,
7077
- inheritedEntries: [...COPY_ENTRIES, ...JSON_ENTRIES, "mcp.json", "npm"]
7635
+ inheritedEntries: [...COPY_ENTRIES, ...JSON_ENTRIES, "mcp.json", "npm"],
7636
+ // What the model can actually see this run: which profile and which exact
7637
+ // bundle skills were enabled. Absent profile => main skills only.
7638
+ ...skillProfile ? {
7639
+ skillProfile,
7640
+ enabledSkillsDigest: createHash8("sha256").update(JSON.stringify(enabledSkills)).digest("hex")
7641
+ } : {}
7078
7642
  };
7079
7643
  return {
7080
7644
  facts,
7081
- attestationId: createHash7("sha256").update(JSON.stringify(facts)).digest("hex")
7645
+ attestationId: createHash8("sha256").update(JSON.stringify(facts)).digest("hex")
7082
7646
  };
7083
7647
  }
7084
7648
  function assertAuditArgsRedacted(value) {
@@ -7274,7 +7838,7 @@ function verifyTrustedPiRuntimeAssertion(input) {
7274
7838
  if (hasSourceAssertion) {
7275
7839
  const exactTools = Array.isArray(record5(sourceProfile.tools).exactAllowlist) ? record5(sourceProfile.tools).exactAllowlist : [];
7276
7840
  const exactActions = Array.isArray(record5(sourceProfile.actions).exactAllowlist) ? record5(sourceProfile.actions).exactAllowlist : [];
7277
- const profileHash = createHash7("sha256").update(JSON.stringify(sourceProfile)).digest("hex");
7841
+ const profileHash = createHash8("sha256").update(JSON.stringify(sourceProfile)).digest("hex");
7278
7842
  if (assertion.unknownToolMode !== "deny" || maxCalls !== 0 || sourceAssertion.profileVersion !== sourceProfile.purpose || sourceAssertion.profileHash !== profileHash || sourceAssertion.retentionVersion !== sourceProfile.retention || JSON.stringify(sourceAssertion.exactTools) !== JSON.stringify(exactTools) || JSON.stringify(sourceAssertion.exactActions) !== JSON.stringify(exactActions) || sourceAssertion.sourceId !== sourceProfile.sourceId || sourceAssertion.sourceRevisionId !== sourceProfile.sourceRevisionId || sourceAssertion.sourceRevision !== sourceProfile.sourceRevision || sourceAssertion.attemptId !== sourceProfile.attemptId || sourceAssertion.epoch !== sourceProfile.epoch) {
7279
7843
  throw new Error("pi_trusted_runtime_assertion_binding_mismatch:sourceAcquisition");
7280
7844
  }
@@ -7303,7 +7867,7 @@ function verifyTrustedPiRuntimeAssertion(input) {
7303
7867
 
7304
7868
  // src/amaster-runtime-daemon/workspace-status.mjs
7305
7869
  import { spawnSync as spawnSync4 } from "node:child_process";
7306
- import { createHash as createHash8 } from "node:crypto";
7870
+ import { createHash as createHash9 } from "node:crypto";
7307
7871
  import { existsSync as existsSync12, readdirSync as readdirSync8, readFileSync as readFileSync10, statSync as statSync6 } from "node:fs";
7308
7872
  import { basename as basename5, extname, isAbsolute as isAbsolute7, join as join13, relative as relative7, resolve as resolve10 } from "node:path";
7309
7873
  var WORKSPACE_RUNTIME_SERVICES_FILENAME = ".amaster-runtime-services.json";
@@ -7375,7 +7939,7 @@ function sanitizeTrackedChange(line) {
7375
7939
  return isSafeRelativePath(path) ? line : null;
7376
7940
  }
7377
7941
  function sha256File2(filePath) {
7378
- return createHash8("sha256").update(readFileSync10(filePath)).digest("hex");
7942
+ return createHash9("sha256").update(readFileSync10(filePath)).digest("hex");
7379
7943
  }
7380
7944
  function artifactHashCacheKey(relativePath, stat) {
7381
7945
  return `${relativePath}\0${stat.size}\0${stat.mtimeMs}`;
@@ -7576,7 +8140,7 @@ function readWorkspaceStatus(cwd, opts = {}) {
7576
8140
  }
7577
8141
 
7578
8142
  // src/amaster-runtime-daemon/pi-browser-session-adapter.mjs
7579
- import { createHash as createHash9 } from "node:crypto";
8143
+ import { createHash as createHash10 } from "node:crypto";
7580
8144
  import { spawn, spawnSync as spawnSync5 } from "node:child_process";
7581
8145
  import { existsSync as existsSync13 } from "node:fs";
7582
8146
  import {
@@ -7699,7 +8263,7 @@ function fail(code) {
7699
8263
  throw Object.assign(new Error(code), { code });
7700
8264
  }
7701
8265
  function profileName(identity2) {
7702
- return createHash9("sha256").update(`${identity2.companyId}\0${identity2.bindingId}\0${identity2.localOpaqueRef}`).digest("hex");
8266
+ return createHash10("sha256").update(`${identity2.companyId}\0${identity2.bindingId}\0${identity2.localOpaqueRef}`).digest("hex");
7703
8267
  }
7704
8268
  function expectedMarker(identity2) {
7705
8269
  return {
@@ -8098,7 +8662,7 @@ async function executeBrowserSessionCommand(command, dependencies = {}) {
8098
8662
  }
8099
8663
 
8100
8664
  // src/amaster-runtime-daemon/source-acquisition-invocation.mjs
8101
- import { createHash as createHash10 } from "node:crypto";
8665
+ import { createHash as createHash11 } from "node:crypto";
8102
8666
  var SOURCE_ACQUISITION_PUBLIC_TOOLS = Object.freeze([
8103
8667
  "source_open",
8104
8668
  "source_snapshot",
@@ -8135,7 +8699,7 @@ function serializeSourceAcquisitionProfile(profile) {
8135
8699
  const input = Buffer.from(JSON.stringify(profile), "utf8");
8136
8700
  return {
8137
8701
  input,
8138
- sha256: createHash10("sha256").update(input).digest("hex")
8702
+ sha256: createHash11("sha256").update(input).digest("hex")
8139
8703
  };
8140
8704
  }
8141
8705
  function sourceAcquisitionManagedInputs(options) {
@@ -8171,7 +8735,7 @@ function assertSourceAcquisitionRuntimeAuthority({
8171
8735
  }
8172
8736
 
8173
8737
  // src/amaster-runtime-daemon.mjs
8174
- var CONNECTOR_VERSION = "0.1.1-beta.3";
8738
+ var CONNECTOR_VERSION = "0.1.1-beta.5";
8175
8739
  var CONNECTOR_CONTRACT_VERSION = "2026-06-04.v1";
8176
8740
  var SOURCE_ACQUISITION_CAPABILITY = "source_acquisition_v1";
8177
8741
  var SOURCE_ACQUISITION_PROFILE_VERSION = "source_acquisition_v1";
@@ -9468,7 +10032,7 @@ function sourceAcquisitionRuntimeProfile(config, command) {
9468
10032
  if (!companyId || !bindingId || !/^profile_[a-z0-9]{16,64}$/i.test(localOpaqueRef ?? "")) {
9469
10033
  throw new Error("source_acquisition_profile_invalid");
9470
10034
  }
9471
- const profileName2 = createHash11("sha256").update(`${companyId}\0${bindingId}\0${localOpaqueRef}`).digest("hex");
10035
+ const profileName2 = createHash12("sha256").update(`${companyId}\0${bindingId}\0${localOpaqueRef}`).digest("hex");
9472
10036
  const stateRoot = resolve12(config.browserSessionStateRoot);
9473
10037
  const userDataDir = resolve12(stateRoot, profileName2);
9474
10038
  if (!pathWithin2(userDataDir, stateRoot)) throw new Error("source_acquisition_browser_profile_invalid");
@@ -9665,6 +10229,7 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
9665
10229
  hasGovernedMcp,
9666
10230
  executorKind: options.executorKind,
9667
10231
  managedMcpToolMode: options.managedMcpToolMode,
10232
+ managedMcpToolCatalog: options.managedMcpToolCatalog,
9668
10233
  agentInstructions,
9669
10234
  taskMarkdown,
9670
10235
  attachmentsText,
@@ -9953,6 +10518,14 @@ async function executeModelCallCommand(config, command, signal) {
9953
10518
  sourceWorkspacePath: process.cwd()
9954
10519
  });
9955
10520
  piModelCallProfile = executor.kind === "pi" ? preparePiModelCallProfile(command.commandId, baseEnv) : null;
10521
+ if (piModelCallProfile) {
10522
+ await syncPiExecutorProviderConfig(
10523
+ config,
10524
+ command,
10525
+ piModelCallProfile.env.PI_CODING_AGENT_DIR,
10526
+ resolvePiExecutorProviderConfig(config, command, baseEnv)
10527
+ );
10528
+ }
9956
10529
  execution = await runExecutor(invocation.command, invocation.args, {
9957
10530
  cwd: process.cwd(),
9958
10531
  env: piModelCallProfile?.env ?? baseEnv,
@@ -12197,7 +12770,7 @@ async function materializeIssueAttachments(config, command, workspace) {
12197
12770
  const body = await runtimeApiBuffer(runtimeAuth, contentPath);
12198
12771
  writeFileSync9(targetPath, body);
12199
12772
  const attachmentId = readString(attachment.id);
12200
- const actualSha256 = createHash11("sha256").update(body).digest("hex");
12773
+ const actualSha256 = createHash12("sha256").update(body).digest("hex");
12201
12774
  const lineageCandidates = lineageCandidatesByAttachmentId.get(attachmentId) ?? [];
12202
12775
  const lineage = selectAttachmentLineage(lineageCandidates, actualSha256);
12203
12776
  if (lineageCandidates.length > 0 && !lineage) {
@@ -12289,7 +12862,7 @@ async function materializeRequiredArtifactInputs(config, command, workspace) {
12289
12862
  throw new Error(`artifact_input_manifest_invalid: entry ${index} contentPath does not match attachmentId`);
12290
12863
  }
12291
12864
  const body = await runtimeApiBuffer(runtimeAuth, contentPath);
12292
- const actualSha256 = createHash11("sha256").update(body).digest("hex");
12865
+ const actualSha256 = createHash12("sha256").update(body).digest("hex");
12293
12866
  if (body.byteLength !== byteSize || actualSha256 !== sha256) {
12294
12867
  throw new Error(
12295
12868
  `artifact_input_integrity_mismatch: workProductId=${workProductId} expectedBytes=${byteSize} actualBytes=${body.byteLength} expectedSha256=${sha256} actualSha256=${actualSha256}`
@@ -12359,7 +12932,7 @@ function safeCheckpointRelativePath(rawPath) {
12359
12932
  return normalized;
12360
12933
  }
12361
12934
  function hashFileSha256(filePath) {
12362
- return createHash11("sha256").update(readFileSync11(filePath)).digest("hex");
12935
+ return createHash12("sha256").update(readFileSync11(filePath)).digest("hex");
12363
12936
  }
12364
12937
  async function materializeIssueCheckpoint(config, command, workspace) {
12365
12938
  const checkpointDir = issueCheckpointDir(workspace);
@@ -12545,7 +13118,8 @@ async function executeRunCommand(config, command) {
12545
13118
  await ingestWorkspaceStatus(config, command, cwd);
12546
13119
  const promptCompilation = buildCommandPrompt(command, workspace, materializedAttachments, {
12547
13120
  executorKind: executor.kind,
12548
- managedMcpToolMode: executor.kind === "pi" && Object.keys(governedMcp).length > 0 ? MANAGED_PI_MCP_TOOL_MODE : null,
13121
+ managedMcpToolMode: executor.kind === "pi" && Object.keys(governedMcp).length > 0 ? readString(governedMcp.mcpToolMode) ?? MANAGED_PI_MCP_TOOL_MODE : null,
13122
+ managedMcpToolCatalog: asRecord(governedMcp.toolCatalog),
12549
13123
  artifactVerifierCommands: config.artifactVerifierCommands,
12550
13124
  workspaceBindings: config.workspaceBindings
12551
13125
  });
@@ -12636,6 +13210,7 @@ async function executeRunCommand(config, command) {
12636
13210
  }
12637
13211
  if (managedMcpProfile) {
12638
13212
  executorEnv = managedMcpProfile.env;
13213
+ if (executor.kind === "pi") invocation.args = applyManagedPiToolAllowlist(invocation.args, managedMcpProfile);
12639
13214
  await ingestLog(config, command, "system", "info", `Attested isolated ${executor.kind} managed MCP profile`, {
12640
13215
  presentationKind: "managed_mcp_attestation",
12641
13216
  ...managedMcpProfile.attestation
@@ -12668,7 +13243,8 @@ async function executeRunCommand(config, command) {
12668
13243
  profileRoot: managedMcpProfile.profileRoot,
12669
13244
  agentDir: managedMcpProfile.env.PI_CODING_AGENT_DIR,
12670
13245
  governedMcpConfigPath: managedMcpProfile.configPath,
12671
- verifiedAssertion: trustedPiRuntime
13246
+ verifiedAssertion: trustedPiRuntime,
13247
+ skillProfile: readString(asRecord(command.payload).skillProfile)
12672
13248
  });
12673
13249
  executorEnv = {
12674
13250
  ...executorEnv,
@@ -5,7 +5,7 @@ import { dirname, join, resolve } from "node:path";
5
5
  import { homedir, hostname } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
- const CONNECTOR_VERSION = "0.1.1-beta.3";
8
+ const CONNECTOR_VERSION = "0.1.1-beta.5";
9
9
 
10
10
  const CAPABILITIES = [
11
11
  "remote_registration",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amaster.ai/employee-runtime-connector",
3
- "version": "0.1.1-beta.3",
3
+ "version": "0.1.1-beta.5",
4
4
  "description": "MirrorX runtime connector CLI and daemon",
5
5
  "license": "MIT",
6
6
  "type": "module",