@akagilnc/pi-workflow-roles 0.1.3545 → 0.1.3552

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.
@@ -15270,7 +15270,8 @@ var init_registry2 = __esm({
15270
15270
  "roles",
15271
15271
  "config",
15272
15272
  "help",
15273
- "resume"
15273
+ "resume",
15274
+ "new"
15274
15275
  ];
15275
15276
  STARTUP_CANDIDATES = {
15276
15277
  judge: [
@@ -16863,7 +16864,6 @@ async function discoverCaseFiles(root) {
16863
16864
  for (const item of await readdir(dir, { withFileTypes: true })) {
16864
16865
  const path = resolve5(dir, item.name);
16865
16866
  if (item.isDirectory()) {
16866
- if (item.name === GROK_HOME_DIR_NAME) continue;
16867
16867
  await walk(path, depth + 1);
16868
16868
  } else if (item.isFile() && (item.name.endsWith(".jsonl") || item.name === "stderr.log" && depth === 1)) found.push(path);
16869
16869
  }
@@ -16992,13 +16992,11 @@ async function loadDoctorCase(runsPath) {
16992
16992
  const cost2 = { invocations: sourceList(runDirs.length, runDirs), legs: sourceList(legs.length, legs), modelApiTurns: sourceList(turns.count, turns.sources), outputTokens: sourceList(tokens.count, tokens.sources), toolCalls: sourceList(calls.count, calls.sources), retries: { ...sourceList(retryDirs.length, retryDirs), evidence: "literal run-dir naming" }, statuses, commits, sessions, outputBytes: { ...sourceList(rawBytes, legs), payload: "raw JSONL bytes", providerWireBytes: "unavailable" } };
16993
16993
  return { version: 1, identity: { issueNumber: Number(match[1]), runsPath: await stableRunsIdentity(root) }, evidence, cost: cost2 };
16994
16994
  }
16995
- var GROK_HOME_DIR_NAME;
16996
16995
  var init_doctor_evidence = __esm({
16997
16996
  "src/doctor-evidence.ts"() {
16998
16997
  "use strict";
16999
16998
  init_sha256();
17000
16999
  init_terminating_tools();
17001
- GROK_HOME_DIR_NAME = "grok-home";
17002
17000
  }
17003
17001
  });
17004
17002
 
@@ -19359,6 +19357,16 @@ var init_option_definitions = __esm({
19359
19357
  "ak-role --host grok-build resume 01abc\u2026",
19360
19358
  "ak-role --engine agy resume 01abc\u2026"
19361
19359
  ]
19360
+ },
19361
+ new: {
19362
+ command: "new",
19363
+ summary: "Explicit fresh summons: same role options as a direct call, but always mint a new run (skip same-ticket auto-resume). Caller chooses new vs resume; no overflow judgment.",
19364
+ usage: ["ak-role new <role> [options] \u2026"],
19365
+ examples: [
19366
+ 'ak-role new countersign --attach ./ticket.md "\u88C1\uFF1A\u672C\u7968 #708 \u662F\u5426\u8DB3\u4EE5\u5F00\u5DE5\u3002"',
19367
+ "ak-role new notary --source-run 01abc\u2026@countersign",
19368
+ "ak-role --host grok-build new inspector --attach ./change.patch"
19369
+ ]
19362
19370
  }
19363
19371
  };
19364
19372
  PUBLIC_COMMAND_HELP = {
@@ -23708,6 +23716,7 @@ async function resolveSeatTicketBinding(admitted, env = {}) {
23708
23716
  return resolution;
23709
23717
  }
23710
23718
  async function tryResumeSameTicketSeatRun(input) {
23719
+ if (input.freshSummons === true) return void 0;
23711
23720
  const previousRunId = await findLatestRunIdForSeatTicket({
23712
23721
  home: input.home,
23713
23722
  bookKey: resolveBookKeyFromGit(input.projectRoot),
@@ -27905,7 +27914,7 @@ var init_turn_request = __esm({
27905
27914
 
27906
27915
  // src/host-transition-prior-native.ts
27907
27916
  import { access as access2, readdir as readdir6 } from "node:fs/promises";
27908
- import { join as join26 } from "node:path";
27917
+ import { dirname as dirname11, join as join26 } from "node:path";
27909
27918
  function isKnownRoleTurnHost(value) {
27910
27919
  return KNOWN_ROLE_TURN_HOSTS.includes(value);
27911
27920
  }
@@ -27921,41 +27930,28 @@ async function listPiNativeRecordPaths(sessionFile) {
27921
27930
  throw error;
27922
27931
  }
27923
27932
  }
27924
- async function listGrokNativeRecordPaths(runDirectory) {
27925
- const grokSessionsDir = join26(runDirectory, "grok-home", "sessions");
27926
- let encodedCwds;
27933
+ async function listSitianRecordPaths(sessionParent) {
27934
+ const sessionRoot = dirname11(sessionParent);
27935
+ let entries;
27927
27936
  try {
27928
- encodedCwds = await readdir6(grokSessionsDir, { withFileTypes: true });
27937
+ entries = await readdir6(sessionRoot, { withFileTypes: true });
27929
27938
  } catch (error) {
27930
27939
  if (isEnoent2(error)) return [];
27931
27940
  throw error;
27932
27941
  }
27933
- const updatesPaths = [];
27934
- for (const cwdEntry of encodedCwds) {
27935
- if (!cwdEntry.isDirectory()) continue;
27936
- let sessionDirs;
27937
- try {
27938
- sessionDirs = await readdir6(join26(grokSessionsDir, cwdEntry.name), { withFileTypes: true });
27939
- } catch (error) {
27940
- if (isEnoent2(error)) continue;
27941
- throw error;
27942
- }
27943
- for (const sessEntry of sessionDirs) {
27944
- if (!sessEntry.isDirectory()) continue;
27945
- updatesPaths.push(join26(grokSessionsDir, cwdEntry.name, sessEntry.name, "updates.jsonl"));
27946
- }
27947
- }
27948
- updatesPaths.sort();
27949
- const present = [];
27950
- for (const updatesFile of updatesPaths) {
27942
+ const recordPaths = [];
27943
+ for (const entry of entries) {
27944
+ if (!entry.isDirectory()) continue;
27945
+ const recordFile = join26(sessionRoot, entry.name, "records.jsonl");
27951
27946
  try {
27952
- await access2(updatesFile);
27953
- present.push(updatesFile);
27947
+ await access2(recordFile);
27948
+ recordPaths.push(recordFile);
27954
27949
  } catch (error) {
27955
27950
  if (!isEnoent2(error)) throw error;
27956
27951
  }
27957
27952
  }
27958
- return present;
27953
+ recordPaths.sort();
27954
+ return recordPaths;
27959
27955
  }
27960
27956
  async function projectHostTransitionPriorNative(input) {
27961
27957
  if (input.previousHost === input.liveHost) return void 0;
@@ -27969,7 +27965,7 @@ async function projectHostTransitionPriorNative(input) {
27969
27965
  priorNativePaths: paths2
27970
27966
  };
27971
27967
  }
27972
- const paths = await listGrokNativeRecordPaths(input.runDirectory);
27968
+ const paths = await listSitianRecordPaths(input.piSessionFile);
27973
27969
  return {
27974
27970
  previousHost: "grok-build",
27975
27971
  priorNativePaths: paths
@@ -28458,7 +28454,6 @@ async function dispatchPostAdmissionTurn(input) {
28458
28454
  const hostTransition = previousHost !== void 0 && liveHost !== void 0 && principalCoordinates !== void 0 ? await projectHostTransitionPriorNative({
28459
28455
  previousHost,
28460
28456
  liveHost,
28461
- runDirectory: admitted.runDirectory,
28462
28457
  piSessionFile: principalCoordinates.sessionFile
28463
28458
  }) : void 0;
28464
28459
  turnRequest = request;
@@ -29370,6 +29365,7 @@ async function runPublicCountersign(argv, env, io, parseCountersignArgv2) {
29370
29365
  projectRoot,
29371
29366
  role: "countersign",
29372
29367
  ticketNumber: probedTicketNumber,
29368
+ freshSummons: env.freshSummons,
29373
29369
  summons,
29374
29370
  resume: (runId, materials) => runPublicCountersignResume(
29375
29371
  { runId, ...materials === void 0 ? {} : { summons: materials } },
@@ -29995,6 +29991,7 @@ async function runPublicNotary(argv, env, io, parseNotaryArgv2) {
29995
29991
  projectRoot,
29996
29992
  role: "notary",
29997
29993
  ticketNumber,
29994
+ freshSummons: env.freshSummons,
29998
29995
  summons,
29999
29996
  resume: (runId, materials) => runPublicNotaryResume(
30000
29997
  { runId, ...materials === void 0 ? {} : { summons: materials } },
@@ -30150,6 +30147,7 @@ async function runPublicInspector(argv, env, io, parseInspectorArgv2) {
30150
30147
  projectRoot,
30151
30148
  role: "inspector",
30152
30149
  ticketNumber: probedTicketNumber,
30150
+ freshSummons: env.freshSummons,
30153
30151
  summons,
30154
30152
  resume: (runId, materials) => runPublicInspectorResume(
30155
30153
  { runId, ...materials === void 0 ? {} : { summons: materials } },
@@ -30884,9 +30882,9 @@ var init_analyst_book_key = __esm({
30884
30882
  // src/atomic-write.ts
30885
30883
  import { randomUUID as randomUUID6 } from "node:crypto";
30886
30884
  import { rename, rm as rm2, writeFile as writeFile10 } from "node:fs/promises";
30887
- import { dirname as dirname11, join as join30 } from "node:path";
30885
+ import { dirname as dirname12, join as join30 } from "node:path";
30888
30886
  async function writeFileAtomically(destination, contents) {
30889
- const parent = dirname11(destination);
30887
+ const parent = dirname12(destination);
30890
30888
  const temporary = join30(parent, `.atomic-write-${randomUUID6()}.tmp`);
30891
30889
  try {
30892
30890
  await writeFile10(temporary, contents);
@@ -30904,7 +30902,7 @@ var init_atomic_write = __esm({
30904
30902
 
30905
30903
  // src/analyst-index.ts
30906
30904
  import { open as open3, readFile as readFile17, unlink as unlink4 } from "node:fs/promises";
30907
- import { dirname as dirname12, join as join31 } from "node:path";
30905
+ import { dirname as dirname13, join as join31 } from "node:path";
30908
30906
  function sleep(ms) {
30909
30907
  return new Promise((resolve12) => {
30910
30908
  setTimeout(resolve12, ms);
@@ -30912,8 +30910,8 @@ function sleep(ms) {
30912
30910
  }
30913
30911
  async function withAnalystLibraryIndexLock(ledgerHome, fn) {
30914
30912
  const indexPath = analystLibraryIndexPath(ledgerHome);
30915
- ensureRealDirectoryTree(ledgerHome, dirname12(indexPath));
30916
- const lockPath = join31(dirname12(indexPath), LIBRARY_INDEX_LOCK_NAME);
30913
+ ensureRealDirectoryTree(ledgerHome, dirname13(indexPath));
30914
+ const lockPath = join31(dirname13(indexPath), LIBRARY_INDEX_LOCK_NAME);
30917
30915
  assertLedgerFileInsideHome(lockPath, ledgerHome);
30918
30916
  const startedAt = Date.now();
30919
30917
  while (true) {
@@ -31049,7 +31047,7 @@ async function readAnalystLibraryIndexPage(ledgerHome) {
31049
31047
  }
31050
31048
  async function writeAnalystLibraryIndexPage(ledgerHome, page) {
31051
31049
  const path = analystLibraryIndexPath(ledgerHome);
31052
- ensureRealDirectoryTree(ledgerHome, dirname12(path));
31050
+ ensureRealDirectoryTree(ledgerHome, dirname13(path));
31053
31051
  assertLedgerFileInsideHome(path, ledgerHome);
31054
31052
  await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
31055
31053
  `);
@@ -32426,7 +32424,7 @@ var init_analyst_metric_family = __esm({
32426
32424
 
32427
32425
  // src/analyst-page.ts
32428
32426
  import { createHash as createHash7 } from "node:crypto";
32429
- import { dirname as dirname13, join as join33 } from "node:path";
32427
+ import { dirname as dirname14, join as join33 } from "node:path";
32430
32428
  function analystIssuePageKey(address) {
32431
32429
  const parts = ["book", address.bookKey];
32432
32430
  if (address.issueNumber !== void 0) {
@@ -32556,7 +32554,7 @@ async function buildAnalystIssueMetricsPage(input) {
32556
32554
  }
32557
32555
  async function writeAnalystIssueMetricsPage(ledgerHome, page) {
32558
32556
  const path = analystIssuePagePath(ledgerHome, analystIssuePageAddressFromPage(page));
32559
- ensureRealDirectoryTree(ledgerHome, dirname13(path));
32557
+ ensureRealDirectoryTree(ledgerHome, dirname14(path));
32560
32558
  assertLedgerFileInsideHome(path, ledgerHome);
32561
32559
  await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
32562
32560
  `);
@@ -33077,7 +33075,8 @@ function createRoleEnvironment(env, options) {
33077
33075
  ...projectSeatHost(options.seat),
33078
33076
  ...timeoutMs === void 0 ? {} : { timeoutMs },
33079
33077
  ...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
33080
- ...options.config?.autoResumeLimit === void 0 ? {} : { autoResumeLimit: options.config.autoResumeLimit }
33078
+ ...options.config?.autoResumeLimit === void 0 ? {} : { autoResumeLimit: options.config.autoResumeLimit },
33079
+ ...env.freshSummons === true ? { freshSummons: true } : {}
33081
33080
  };
33082
33081
  }
33083
33082
  function cliResultFromRoleRun(result2) {
@@ -33564,8 +33563,8 @@ async function runAkRole(argv, env) {
33564
33563
  packageRoot: await realpath6(env.packageRoot),
33565
33564
  principalAuthority: env.principalAuthority ?? piDurablePrincipalAuthority
33566
33565
  };
33567
- const parsed = parseArgv(argv);
33568
- const acceptsSeatAxes = parsed.command !== void 0 && (isPublicCallableRole(parsed.command) || parsed.command === "resume");
33566
+ let parsed = parseArgv(argv);
33567
+ const acceptsSeatAxes = parsed.command !== void 0 && (isPublicCallableRole(parsed.command) || parsed.command === "resume" || parsed.command === "new");
33569
33568
  if (parsed.host !== void 0 && !parsed.help && parsed.command !== void 0 && parsed.command !== "help" && !acceptsSeatAxes) {
33570
33569
  throw new CliUsageError(`host axis is role commands only; refused command ${parsed.command}`);
33571
33570
  }
@@ -33610,6 +33609,21 @@ async function runAkRole(argv, env) {
33610
33609
  exitCode: await runConfigCommand(parsed.args, home, env.packageRoot, io)
33611
33610
  };
33612
33611
  }
33612
+ if (parsed.command === "new") {
33613
+ const role = parsed.args[0];
33614
+ if (role === void 0) {
33615
+ throw new CliUsageError("usage: ak-role new <role> \u2026");
33616
+ }
33617
+ if (!isPublicCallableRole(role)) {
33618
+ throw new CliUsageError(`usage: ak-role new <role> \u2026; unknown role: ${role}`);
33619
+ }
33620
+ parsed = {
33621
+ ...parsed,
33622
+ command: role,
33623
+ args: parsed.args.slice(1)
33624
+ };
33625
+ env = { ...env, freshSummons: true };
33626
+ }
33613
33627
  if (parsed.command === "resume") {
33614
33628
  const agentDir = resolveAgentDir(env, home);
33615
33629
  const cwd = env.cwd ?? process.cwd();
@@ -33639,7 +33653,7 @@ async function runAkRole(argv, env) {
33639
33653
  );
33640
33654
  return cliResultFromRoleRun(result2);
33641
33655
  }
33642
- if (isPublicCliSupportCommand(parsed.command)) {
33656
+ if (parsed.command !== void 0 && isPublicCliSupportCommand(parsed.command)) {
33643
33657
  throw new CliUsageError(`unhandled support command: ${parsed.command}`);
33644
33658
  }
33645
33659
  if (parsed.command === "judge") {
@@ -34014,7 +34028,7 @@ var init_cli = __esm({
34014
34028
 
34015
34029
  // src/public-cli/main.ts
34016
34030
  import { existsSync as existsSync11 } from "node:fs";
34017
- import { dirname as dirname14, join as join35 } from "node:path";
34031
+ import { dirname as dirname15, join as join35 } from "node:path";
34018
34032
  import { fileURLToPath as fileURLToPath4 } from "node:url";
34019
34033
 
34020
34034
  // src/public-cli/host-pi-runtime.ts
@@ -34101,7 +34115,7 @@ function linkPackage(packageRoot2, name, targetDir) {
34101
34115
  }
34102
34116
 
34103
34117
  // src/public-cli/main.ts
34104
- var here = dirname14(fileURLToPath4(import.meta.url));
34118
+ var here = dirname15(fileURLToPath4(import.meta.url));
34105
34119
  function resolvePackageRoot(binDir) {
34106
34120
  const canonical = join35(binDir, "..", "..");
34107
34121
  if (existsSync11(join35(canonical, "package.json"))) {
@@ -12,7 +12,8 @@ const PUBLIC_CLI_SUPPORT_COMMANDS = [
12
12
  "roles",
13
13
  "config",
14
14
  "help",
15
- "resume"
15
+ "resume",
16
+ "new"
16
17
  ];
17
18
  function seatModelOnly(seat) {
18
19
  if (seat?.provider === void 0 || seat.model === void 0) return void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.3545",
3
+ "version": "0.1.3552",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -5,14 +5,7 @@ import type { DoctorCase, DoctorCaseCost, DoctorCount, DoctorEvidenceEntry } fro
5
5
  import { AcceptedDetailsContractError, acceptedFacts, isTerminatingToolName, validateAcceptedDetails } from "./package-contracts/terminating-tools.ts";
6
6
 
7
7
  function record(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); }
8
- /**
9
- * Grok controlled-home ledger directory under a run (ADR 0077 / #594).
10
- * Native journals (e.g. updates.jsonl) are retained for dossier parity but are
11
- * not Pi session records — walking them into deriveSession yields false incomplete
12
- * legs. Explicit skip is the leave-trace exclusion until a grok parser exists.
13
- */
14
- const GROK_HOME_DIR_NAME = "grok-home";
15
- async function discoverCaseFiles(root: string): Promise<string[]> { const found: string[] = []; async function walk(dir: string, depth: number) { for (const item of await readdir(dir, { withFileTypes: true })) { const path = resolve(dir, item.name); if (item.isDirectory()) { if (item.name === GROK_HOME_DIR_NAME) continue; await walk(path, depth + 1); } else if (item.isFile() && (item.name.endsWith(".jsonl") || (item.name === "stderr.log" && depth === 1))) found.push(path); } } await walk(root, 0); return found.sort(); }
8
+ async function discoverCaseFiles(root: string): Promise<string[]> { const found: string[] = []; async function walk(dir: string, depth: number) { for (const item of await readdir(dir, { withFileTypes: true })) { const path = resolve(dir, item.name); if (item.isDirectory()) { await walk(path, depth + 1); } else if (item.isFile() && (item.name.endsWith(".jsonl") || (item.name === "stderr.log" && depth === 1))) found.push(path); } } await walk(root, 0); return found.sort(); }
16
9
  function sourceList(count: number, sources: string[]) { return { count, sources: [...new Set(sources)].sort() }; }
17
10
  function accumulate(metric: DoctorCount, value: number, source: string) { metric.count += value; if (value) metric.sources.push(source); }
18
11
  function timestamp(row: Record<string, unknown>) { return typeof row.timestamp === "string" && Number.isFinite(Date.parse(row.timestamp)) ? row.timestamp : undefined; }
@@ -1,20 +1,12 @@
1
1
  /**
2
- * Production composition for the grok-build RoleTurnHost adapter (#580 / #522 / #594).
2
+ * Production composition for the grok-build RoleTurnHost adapter (#580 / #522 / #717).
3
3
  * Owns injectables around the S6 true adapter; does not alter S6 adapter behavior.
4
4
  *
5
- * Isolation contract: subprocess GROK_HOME, Fixer seatbelt hang root, and auth
6
- * copy share one directory under the runDirectory ledger (grok-home). The grok
7
- * binary still resolves from the operator home. Callers pass that isolated home
8
- * into S6 as request.home (bash-seatbelt.ts: "callers must pass the isolated home").
9
- * Auth is copied from the operator home into grok-home for the turn and scrubbed
10
- * on settle together with AK seatbelt hook residue; the session dossier directly
11
- * written under grok-home survives settlement (ADR 0048 / ADR 0077 / #594).
12
- *
13
- * Known residual risk: a hard process crash between auth copy and settle can leave
14
- * auth.json in the retained run ledger for one crash window. The next open scrubs
15
- * residual auth before recopying; single-crash-window residue is accepted.
5
+ * Grok CLI uses the operator home (`~/.grok`) and credentials in place. The
6
+ * factory does not create a run-scoped grok home, does not rewrite HOME, and
7
+ * does not copy or scrub auth.json. Sititian records on the run are the dossier.
16
8
  */
17
- import { readFile, rm, writeFile } from "node:fs/promises";
9
+ import { readFile } from "node:fs/promises";
18
10
  import { join } from "node:path";
19
11
  import { fileURLToPath } from "node:url";
20
12
 
@@ -22,7 +14,7 @@ import { loadCanonicalSkillBinding as loadHomeCanonicalSkillBinding } from "../c
22
14
  import { createGhCollectorGitHubTransport, createGhIssueSoftFetcher } from "../collector-github.ts";
23
15
  import { createPiDoctorAuditor } from "../doctor-auditor.ts";
24
16
  import { loadDoctorCase } from "../doctor-evidence.ts";
25
- import type { DurablePrincipalAuthority, RoleTurnHost, RoleTurnRequest } from "../host-contracts.ts";
17
+ import type { DurablePrincipalAuthority, RoleTurnHost } from "../host-contracts.ts";
26
18
  import { createPiJudgeAuditor } from "../judge-auditor.ts";
27
19
  import { createProductionMergerGitState } from "../merger-git-state.ts";
28
20
  import { createNativeNavigatorSessionFactory, createNavigatorAttendance } from "../navigator-attendance.ts";
@@ -35,28 +27,11 @@ import { createReviewerPinnedGitReader } from "../reviewer-pinned-git.ts";
35
27
  import { loadGatekeeperSessionMaterials, loadMainRoleSessionMaterials } from "../session-opening-materials.ts";
36
28
  import { createComposedGrokRoleTurnHost } from "./role-envelope.ts";
37
29
  import {
38
- assertControlledGrokAuthIsNotSymlink,
39
- assertControlledGrokHomeIsRealDirectory,
40
30
  connectGrokAcpStdio,
41
31
  controlledGrokChildEnv,
42
- inspectControlledGrok,
43
- prepareControlledGrokHome,
44
- scrubAkBashSeatbeltHooks,
45
- type GrokCapabilityDeclaration,
46
32
  } from "./role-turn-host.ts";
47
33
  import { createGrokSessionIdentityAuthority } from "./session-identity.ts";
48
34
 
49
- /**
50
- * Authoritative production isolation binding consumed by inspect/connect and by
51
- * the S6 request.home rewrite (controlledHome).
52
- */
53
- export type ProductionGrokIsolationBinding = Readonly<{
54
- operatorHome: string;
55
- controlledHome: string;
56
- binary: string;
57
- env: NodeJS.ProcessEnv;
58
- }>;
59
-
60
35
  export type ProductionGrokHostOptions = Readonly<{
61
36
  packageRoot: string;
62
37
  principalAuthority: DurablePrincipalAuthority;
@@ -66,137 +41,13 @@ function resolveGrokBinary(operatorHome: string): string {
66
41
  return join(operatorHome, ".grok", "bin", "grok");
67
42
  }
68
43
 
69
- /**
70
- * Primary-failure state for cleanup settlement. `undefined` is a legal primary
71
- * value (throw/reject undefined), so presence is an explicit discriminant — never
72
- * inferred from `value !== undefined`.
73
- */
74
- export type ProductionGrokPrimaryFailure =
75
- | Readonly<{ present: false }>
76
- | Readonly<{ present: true; value: unknown }>;
77
-
78
- export const NO_PRODUCTION_GROK_PRIMARY_FAILURE = {
79
- present: false,
80
- } as const satisfies ProductionGrokPrimaryFailure;
81
-
82
- /**
83
- * Sole cleanup settlement for production controlled homes: scrub auth secrets
84
- * (auth.json) and AK seatbelt hook residue while preserving the session dossier
85
- * under the runDirectory ledger. Refuses symlink home/auth so rm never follows a
86
- * redirect (#594 F1/F4). Cleanup failure is never silenced. When a primary failure
87
- * is present and cleanup also fails, both surface as AggregateError (including
88
- * primary value `undefined`); cleanup success rethrows the primary value as-is.
89
- */
90
- export async function settleProductionGrokHomeCleanup(
91
- controlledHome: string,
92
- primaryFailure: ProductionGrokPrimaryFailure,
93
- concurrentMessage: string,
94
- ): Promise<void> {
95
- try {
96
- await assertControlledGrokHomeIsRealDirectory(controlledHome);
97
- const authPath = join(controlledHome, "auth.json");
98
- await assertControlledGrokAuthIsNotSymlink(authPath);
99
- await rm(authPath, { force: true });
100
- await scrubAkBashSeatbeltHooks(controlledHome);
101
- } catch (cleanupFailure) {
102
- if (primaryFailure.present) {
103
- throw new AggregateError([primaryFailure.value, cleanupFailure], concurrentMessage, {
104
- cause: primaryFailure.value,
105
- });
106
- }
107
- throw cleanupFailure;
108
- }
109
- if (primaryFailure.present) {
110
- throw primaryFailure.value;
111
- }
112
- }
113
-
114
- /**
115
- * Open the production isolation root under runDirectory: auth copy into grok-home.
116
- * If auth copy fails after the root is created, auth secrets are scrubbed; open
117
- * failure and cleanup failure both surface (AggregateError when concurrent).
118
- */
119
- export async function openProductionGrokHome(
120
- runDirectory: string,
121
- operatorHome: string,
122
- ): Promise<string> {
123
- const controlledHome = join(runDirectory, "grok-home");
124
- try {
125
- await prepareControlledGrokHome(operatorHome, controlledHome);
126
- return controlledHome;
127
- } catch (error) {
128
- await settleProductionGrokHomeCleanup(
129
- controlledHome,
130
- { present: true, value: error },
131
- "production grok home open failed and its cleanup also failed",
132
- );
133
- // settle always throws when primaryFailure is present.
134
- throw error;
135
- }
136
- }
137
-
138
- function childEnv(controlledHome: string, packageRoot: string): NodeJS.ProcessEnv {
44
+ function childEnv(packageRoot: string): NodeJS.ProcessEnv {
139
45
  return {
140
- ...controlledGrokChildEnv(process.env, controlledHome),
46
+ ...controlledGrokChildEnv(process.env),
141
47
  AK_PACKAGE_ROOT: packageRoot,
142
48
  };
143
49
  }
144
50
 
145
- /**
146
- * Single production isolation binding: auth root, GROK_HOME/HOME, and binary
147
- * resolution. Production executeTurn passes controlledHome as S6 request.home
148
- * (seatbelt hang root proven separately by S6 seatbelt tests).
149
- */
150
- export async function bindProductionGrokIsolation(
151
- runDirectory: string,
152
- operatorHome: string,
153
- packageRoot: string,
154
- ): Promise<ProductionGrokIsolationBinding> {
155
- const controlledHome = await openProductionGrokHome(runDirectory, operatorHome);
156
- return {
157
- operatorHome,
158
- controlledHome,
159
- binary: resolveGrokBinary(operatorHome),
160
- env: childEnv(controlledHome, packageRoot),
161
- };
162
- }
163
-
164
- /**
165
- * Bind isolation under runDirectory, run the turn body, always scrub auth.json
166
- * and AK seatbelt hooks via settleProductionGrokHomeCleanup (no silent catch)
167
- * while preserving the session dossier under runDirectory/grok-home. Success,
168
- * typed-result, and throw paths all clean up; cleanup failure and primary+cleanup
169
- * both surface. Same-run Grok isolation only — no cross-run home override (#637).
170
- */
171
- export async function withProductionGrokIsolation<T>(
172
- runDirectory: string,
173
- operatorHome: string,
174
- packageRoot: string,
175
- run: (binding: ProductionGrokIsolationBinding) => Promise<T>,
176
- ): Promise<T> {
177
- let binding: ProductionGrokIsolationBinding | undefined;
178
- let primaryFailure: ProductionGrokPrimaryFailure = NO_PRODUCTION_GROK_PRIMARY_FAILURE;
179
- let value!: T;
180
- try {
181
- binding = await bindProductionGrokIsolation(runDirectory, operatorHome, packageRoot);
182
- value = await run(binding);
183
- } catch (error) {
184
- primaryFailure = { present: true, value: error };
185
- }
186
-
187
- if (binding !== undefined) {
188
- // No catch: settle throws cleanup failure or AggregateError with primary.
189
- await settleProductionGrokHomeCleanup(
190
- binding.controlledHome,
191
- primaryFailure,
192
- "production grok isolation turn and cleanup failed",
193
- );
194
- }
195
-
196
- if (primaryFailure.present) throw primaryFailure.value;
197
- return value;
198
- }
199
-
200
51
  const navigatorRoutePlaybookPath = fileURLToPath(
201
52
  new URL("../../resources/navigator-route-playbook.md", import.meta.url),
202
53
  );
@@ -267,78 +118,24 @@ export function createGrokRoleRuntimeDependencies(packageRoot: string): RoleRunt
267
118
  };
268
119
  }
269
120
 
270
- async function recordGrokCapabilities(
271
- request: RoleTurnRequest,
272
- declaration: GrokCapabilityDeclaration,
273
- ): Promise<void> {
274
- await writeFile(
275
- join(request.runDirectory, "grok-capabilities.json"),
276
- `${JSON.stringify(declaration)}\n`,
277
- "utf8",
278
- );
279
- }
280
-
281
121
  /**
282
122
  * Assemble the production grok-build RoleTurnHost from the S6 true adapter.
283
- * Isolation always under the live request.runDirectory (same-run resume keeps it).
123
+ * Grok subprocesses inherit the operator home; the run directory is sitian-only.
284
124
  */
285
125
  export function createProductionGrokRoleTurnHost(options: ProductionGrokHostOptions): RoleTurnHost {
286
126
  const { packageRoot, principalAuthority } = options;
287
- // Single-slot turn isolation; outer serial keeps operator/controlled pairing intact.
288
- let turn: ProductionGrokIsolationBinding | undefined;
289
- let serial = Promise.resolve();
127
+ const env = childEnv(packageRoot);
290
128
 
291
- const inner = createComposedGrokRoleTurnHost({
129
+ return createComposedGrokRoleTurnHost({
292
130
  sessionIdentity: createGrokSessionIdentityAuthority(principalAuthority),
293
131
  roleRuntimeDependencies: createGrokRoleRuntimeDependencies(packageRoot),
294
- recordCapabilities: recordGrokCapabilities,
295
- async inspect(request) {
296
- if (turn === undefined) {
297
- throw new Error("production grok inspect requires an active isolated turn");
298
- }
299
- return inspectControlledGrok({
300
- binary: turn.binary,
301
- cwd: request.cwd,
302
- env: turn.env,
303
- packageRoot,
304
- });
305
- },
306
132
  async connect(request) {
307
- if (turn === undefined) {
308
- throw new Error("production grok connect requires an active isolated turn");
309
- }
310
133
  return connectGrokAcpStdio({
311
- binary: turn.binary,
134
+ binary: resolveGrokBinary(request.home),
312
135
  cwd: request.cwd,
313
- env: turn.env,
136
+ env,
314
137
  ...(request.model === undefined ? {} : { model: request.model.model }),
315
138
  });
316
139
  },
317
140
  });
318
-
319
- return {
320
- executeTurn(request) {
321
- const execution = serial.then(() =>
322
- withProductionGrokIsolation(
323
- request.runDirectory,
324
- request.home,
325
- packageRoot,
326
- async (binding) => {
327
- turn = binding;
328
- try {
329
- // S6 seatbelt hangs on request.home — same isolated root as GROK_HOME.
330
- return await inner.executeTurn({ ...request, home: binding.controlledHome });
331
- } finally {
332
- turn = undefined;
333
- }
334
- },
335
- ),
336
- );
337
- serial = execution.then(
338
- () => undefined,
339
- () => undefined,
340
- );
341
- return execution;
342
- },
343
- };
344
141
  }