@akagilnc/pi-workflow-roles 0.1.3541 → 0.1.3549
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/grok/production-host.js +26 -430
- package/dist/public-cli/main.js +24 -41
- package/package.json +1 -1
- package/src/doctor-evidence.ts +1 -8
- package/src/grok/production-host.ts +13 -216
- package/src/grok/role-turn-host.ts +4 -358
- package/src/host-transition-prior-native.ts +23 -32
- package/src/public-cli/post-admission.ts +0 -1
- package/src/grok/bash-seatbelt.ts +0 -69
package/dist/public-cli/main.js
CHANGED
|
@@ -16863,7 +16863,6 @@ async function discoverCaseFiles(root) {
|
|
|
16863
16863
|
for (const item of await readdir(dir, { withFileTypes: true })) {
|
|
16864
16864
|
const path = resolve5(dir, item.name);
|
|
16865
16865
|
if (item.isDirectory()) {
|
|
16866
|
-
if (item.name === GROK_HOME_DIR_NAME) continue;
|
|
16867
16866
|
await walk(path, depth + 1);
|
|
16868
16867
|
} else if (item.isFile() && (item.name.endsWith(".jsonl") || item.name === "stderr.log" && depth === 1)) found.push(path);
|
|
16869
16868
|
}
|
|
@@ -16992,13 +16991,11 @@ async function loadDoctorCase(runsPath) {
|
|
|
16992
16991
|
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
16992
|
return { version: 1, identity: { issueNumber: Number(match[1]), runsPath: await stableRunsIdentity(root) }, evidence, cost: cost2 };
|
|
16994
16993
|
}
|
|
16995
|
-
var GROK_HOME_DIR_NAME;
|
|
16996
16994
|
var init_doctor_evidence = __esm({
|
|
16997
16995
|
"src/doctor-evidence.ts"() {
|
|
16998
16996
|
"use strict";
|
|
16999
16997
|
init_sha256();
|
|
17000
16998
|
init_terminating_tools();
|
|
17001
|
-
GROK_HOME_DIR_NAME = "grok-home";
|
|
17002
16999
|
}
|
|
17003
17000
|
});
|
|
17004
17001
|
|
|
@@ -27905,7 +27902,7 @@ var init_turn_request = __esm({
|
|
|
27905
27902
|
|
|
27906
27903
|
// src/host-transition-prior-native.ts
|
|
27907
27904
|
import { access as access2, readdir as readdir6 } from "node:fs/promises";
|
|
27908
|
-
import { join as join26 } from "node:path";
|
|
27905
|
+
import { dirname as dirname11, join as join26 } from "node:path";
|
|
27909
27906
|
function isKnownRoleTurnHost(value) {
|
|
27910
27907
|
return KNOWN_ROLE_TURN_HOSTS.includes(value);
|
|
27911
27908
|
}
|
|
@@ -27921,41 +27918,28 @@ async function listPiNativeRecordPaths(sessionFile) {
|
|
|
27921
27918
|
throw error;
|
|
27922
27919
|
}
|
|
27923
27920
|
}
|
|
27924
|
-
async function
|
|
27925
|
-
const
|
|
27926
|
-
let
|
|
27921
|
+
async function listSitianRecordPaths(sessionParent) {
|
|
27922
|
+
const sessionRoot = dirname11(sessionParent);
|
|
27923
|
+
let entries;
|
|
27927
27924
|
try {
|
|
27928
|
-
|
|
27925
|
+
entries = await readdir6(sessionRoot, { withFileTypes: true });
|
|
27929
27926
|
} catch (error) {
|
|
27930
27927
|
if (isEnoent2(error)) return [];
|
|
27931
27928
|
throw error;
|
|
27932
27929
|
}
|
|
27933
|
-
const
|
|
27934
|
-
for (const
|
|
27935
|
-
if (!
|
|
27936
|
-
|
|
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) {
|
|
27930
|
+
const recordPaths = [];
|
|
27931
|
+
for (const entry of entries) {
|
|
27932
|
+
if (!entry.isDirectory()) continue;
|
|
27933
|
+
const recordFile = join26(sessionRoot, entry.name, "records.jsonl");
|
|
27951
27934
|
try {
|
|
27952
|
-
await access2(
|
|
27953
|
-
|
|
27935
|
+
await access2(recordFile);
|
|
27936
|
+
recordPaths.push(recordFile);
|
|
27954
27937
|
} catch (error) {
|
|
27955
27938
|
if (!isEnoent2(error)) throw error;
|
|
27956
27939
|
}
|
|
27957
27940
|
}
|
|
27958
|
-
|
|
27941
|
+
recordPaths.sort();
|
|
27942
|
+
return recordPaths;
|
|
27959
27943
|
}
|
|
27960
27944
|
async function projectHostTransitionPriorNative(input) {
|
|
27961
27945
|
if (input.previousHost === input.liveHost) return void 0;
|
|
@@ -27969,7 +27953,7 @@ async function projectHostTransitionPriorNative(input) {
|
|
|
27969
27953
|
priorNativePaths: paths2
|
|
27970
27954
|
};
|
|
27971
27955
|
}
|
|
27972
|
-
const paths = await
|
|
27956
|
+
const paths = await listSitianRecordPaths(input.piSessionFile);
|
|
27973
27957
|
return {
|
|
27974
27958
|
previousHost: "grok-build",
|
|
27975
27959
|
priorNativePaths: paths
|
|
@@ -28458,7 +28442,6 @@ async function dispatchPostAdmissionTurn(input) {
|
|
|
28458
28442
|
const hostTransition = previousHost !== void 0 && liveHost !== void 0 && principalCoordinates !== void 0 ? await projectHostTransitionPriorNative({
|
|
28459
28443
|
previousHost,
|
|
28460
28444
|
liveHost,
|
|
28461
|
-
runDirectory: admitted.runDirectory,
|
|
28462
28445
|
piSessionFile: principalCoordinates.sessionFile
|
|
28463
28446
|
}) : void 0;
|
|
28464
28447
|
turnRequest = request;
|
|
@@ -30884,9 +30867,9 @@ var init_analyst_book_key = __esm({
|
|
|
30884
30867
|
// src/atomic-write.ts
|
|
30885
30868
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
30886
30869
|
import { rename, rm as rm2, writeFile as writeFile10 } from "node:fs/promises";
|
|
30887
|
-
import { dirname as
|
|
30870
|
+
import { dirname as dirname12, join as join30 } from "node:path";
|
|
30888
30871
|
async function writeFileAtomically(destination, contents) {
|
|
30889
|
-
const parent =
|
|
30872
|
+
const parent = dirname12(destination);
|
|
30890
30873
|
const temporary = join30(parent, `.atomic-write-${randomUUID6()}.tmp`);
|
|
30891
30874
|
try {
|
|
30892
30875
|
await writeFile10(temporary, contents);
|
|
@@ -30904,7 +30887,7 @@ var init_atomic_write = __esm({
|
|
|
30904
30887
|
|
|
30905
30888
|
// src/analyst-index.ts
|
|
30906
30889
|
import { open as open3, readFile as readFile17, unlink as unlink4 } from "node:fs/promises";
|
|
30907
|
-
import { dirname as
|
|
30890
|
+
import { dirname as dirname13, join as join31 } from "node:path";
|
|
30908
30891
|
function sleep(ms) {
|
|
30909
30892
|
return new Promise((resolve12) => {
|
|
30910
30893
|
setTimeout(resolve12, ms);
|
|
@@ -30912,8 +30895,8 @@ function sleep(ms) {
|
|
|
30912
30895
|
}
|
|
30913
30896
|
async function withAnalystLibraryIndexLock(ledgerHome, fn) {
|
|
30914
30897
|
const indexPath = analystLibraryIndexPath(ledgerHome);
|
|
30915
|
-
ensureRealDirectoryTree(ledgerHome,
|
|
30916
|
-
const lockPath = join31(
|
|
30898
|
+
ensureRealDirectoryTree(ledgerHome, dirname13(indexPath));
|
|
30899
|
+
const lockPath = join31(dirname13(indexPath), LIBRARY_INDEX_LOCK_NAME);
|
|
30917
30900
|
assertLedgerFileInsideHome(lockPath, ledgerHome);
|
|
30918
30901
|
const startedAt = Date.now();
|
|
30919
30902
|
while (true) {
|
|
@@ -31049,7 +31032,7 @@ async function readAnalystLibraryIndexPage(ledgerHome) {
|
|
|
31049
31032
|
}
|
|
31050
31033
|
async function writeAnalystLibraryIndexPage(ledgerHome, page) {
|
|
31051
31034
|
const path = analystLibraryIndexPath(ledgerHome);
|
|
31052
|
-
ensureRealDirectoryTree(ledgerHome,
|
|
31035
|
+
ensureRealDirectoryTree(ledgerHome, dirname13(path));
|
|
31053
31036
|
assertLedgerFileInsideHome(path, ledgerHome);
|
|
31054
31037
|
await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
|
|
31055
31038
|
`);
|
|
@@ -32426,7 +32409,7 @@ var init_analyst_metric_family = __esm({
|
|
|
32426
32409
|
|
|
32427
32410
|
// src/analyst-page.ts
|
|
32428
32411
|
import { createHash as createHash7 } from "node:crypto";
|
|
32429
|
-
import { dirname as
|
|
32412
|
+
import { dirname as dirname14, join as join33 } from "node:path";
|
|
32430
32413
|
function analystIssuePageKey(address) {
|
|
32431
32414
|
const parts = ["book", address.bookKey];
|
|
32432
32415
|
if (address.issueNumber !== void 0) {
|
|
@@ -32556,7 +32539,7 @@ async function buildAnalystIssueMetricsPage(input) {
|
|
|
32556
32539
|
}
|
|
32557
32540
|
async function writeAnalystIssueMetricsPage(ledgerHome, page) {
|
|
32558
32541
|
const path = analystIssuePagePath(ledgerHome, analystIssuePageAddressFromPage(page));
|
|
32559
|
-
ensureRealDirectoryTree(ledgerHome,
|
|
32542
|
+
ensureRealDirectoryTree(ledgerHome, dirname14(path));
|
|
32560
32543
|
assertLedgerFileInsideHome(path, ledgerHome);
|
|
32561
32544
|
await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
|
|
32562
32545
|
`);
|
|
@@ -34014,7 +33997,7 @@ var init_cli = __esm({
|
|
|
34014
33997
|
|
|
34015
33998
|
// src/public-cli/main.ts
|
|
34016
33999
|
import { existsSync as existsSync11 } from "node:fs";
|
|
34017
|
-
import { dirname as
|
|
34000
|
+
import { dirname as dirname15, join as join35 } from "node:path";
|
|
34018
34001
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
34019
34002
|
|
|
34020
34003
|
// src/public-cli/host-pi-runtime.ts
|
|
@@ -34101,7 +34084,7 @@ function linkPackage(packageRoot2, name, targetDir) {
|
|
|
34101
34084
|
}
|
|
34102
34085
|
|
|
34103
34086
|
// src/public-cli/main.ts
|
|
34104
|
-
var here =
|
|
34087
|
+
var here = dirname15(fileURLToPath4(import.meta.url));
|
|
34105
34088
|
function resolvePackageRoot(binDir) {
|
|
34106
34089
|
const canonical = join35(binDir, "..", "..");
|
|
34107
34090
|
if (existsSync11(join35(canonical, "package.json"))) {
|
package/package.json
CHANGED
package/src/doctor-evidence.ts
CHANGED
|
@@ -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 / #
|
|
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
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
288
|
-
let turn: ProductionGrokIsolationBinding | undefined;
|
|
289
|
-
let serial = Promise.resolve();
|
|
127
|
+
const env = childEnv(packageRoot);
|
|
290
128
|
|
|
291
|
-
|
|
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:
|
|
134
|
+
binary: resolveGrokBinary(request.home),
|
|
312
135
|
cwd: request.cwd,
|
|
313
|
-
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
|
}
|