@rallycry/conveyor-agent 10.13.16 → 10.13.18

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.
@@ -14,8 +14,8 @@ import {
14
14
  } from "./chunk-4VUQ2NPF.js";
15
15
  import {
16
16
  startWorkbenchServer
17
- } from "./chunk-5UYMDSAE.js";
18
- import "./chunk-JG54UJ6V.js";
17
+ } from "./chunk-AGGVNADW.js";
18
+ import "./chunk-72AEN6LB.js";
19
19
  import {
20
20
  DEFAULT_WORKBENCH_PORT
21
21
  } from "./chunk-JIGG755T.js";
@@ -336,6 +336,7 @@ async function runPreflightUpdate(deps) {
336
336
  ],
337
337
  { timeoutMs: NPM_INSTALL_TIMEOUT_MS }
338
338
  );
339
+ await deps.exec("sudo", ["rm", "-rf", NPM_CACHE_DIR], { timeoutMs: NPM_VIEW_TIMEOUT_MS }).catch(() => void 0);
339
340
  return "updated";
340
341
  } catch (err) {
341
342
  deps.log.warn(
@@ -1265,4 +1266,4 @@ export {
1265
1266
  runBoot,
1266
1267
  workbenchBootSteps
1267
1268
  };
1268
- //# sourceMappingURL=boot-NMXFM4GK.js.map
1269
+ //# sourceMappingURL=boot-YZCOIV3Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/boot/index.ts","../src/boot/bundle.ts","../src/boot/graphify.ts","../src/boot/logger.ts","../src/boot/types.ts","../src/boot/pipeline.ts","../src/boot/poll.ts","../src/boot/self-update.ts","../src/boot/supervisor.ts","../src/boot/crash-report.ts","../src/boot/user-home.ts","../src/boot/sshd.ts","../src/boot/workspace-extras.ts","../src/boot/workbench-boot-deps.ts","../src/boot/workbench-boot.ts"],"sourcesContent":["/* oxlint-disable import/max-dependencies -- boot composition module: it wires the Task 1-8 pieces (poll/unpack/self-update/supervisor/user-home/workbench) into the two role pipelines; splitting the wiring would scatter the boot sequence */\n/**\n * `conveyor-agent boot` — the vnext pod boot entry. Composes the tested Task\n * 1-8 pieces into two role pipelines (agent / workbench), the `--preflight-update`\n * self-update, and the legacy-launch tombstone predicate the CLI guards on.\n *\n * The bash entrypoint (runtime/entrypoint.sh) is retired: a split-only pod\n * runs `conveyor-agent boot` in each container, keyed by CONVEYOR_CONTAINER_ROLE.\n * Every network/spawn/fs seam is injected via `BootDeps` (production defaults in\n * `defaultBootDeps`) so the dispatch tests run offline.\n */\nimport { spawn } from \"node:child_process\";\nimport { statSync } from \"node:fs\";\nimport { hostname as osHostname } from \"node:os\";\nimport { readAgentVersion } from \"../runner/session-runner-helpers.js\";\nimport { workbenchPort } from \"../workbench/mode.js\";\nimport { DEFAULT_WORKBENCH_PORT } from \"../workbench/protocol.js\";\nimport { buildChildEnv, decodeJwtClaims } from \"./bundle.js\";\nimport { computeGraphifyEnv } from \"./graphify.js\";\nimport { createBootLogger } from \"./logger.js\";\nimport { runPipeline } from \"./pipeline.js\";\nimport { pollBootstrapBundle } from \"./poll.js\";\nimport { defaultExec, runPreflightUpdate, type PreflightDeps } from \"./self-update.js\";\nimport { superviseRunner } from \"./supervisor.js\";\nimport type { BootContext, BootLogger, BootRole, BootStep } from \"./types.js\";\nimport { reclaimHomeOwnership, wireUserHome } from \"./user-home.js\";\nimport { runWorkbenchBoot } from \"./workbench-boot.js\";\nimport { defaultWorkbenchBootDeps, type WorkbenchBootDeps } from \"./workbench-boot-deps.js\";\n\nconst HOME_DIR = \"/home/conveyor\";\nconst USER_HOME_MOUNT = \"/mnt/conveyor-users\";\n\n// Leaf module so cli.ts's guard doesn't eagerly load the whole boot graph.\nexport { isLegacyEntrypointLaunch, TOMBSTONE_MESSAGE } from \"./tombstone.js\";\n\n/** The single injectable seam bundle — network (fetch/poll), spawn, exec, and\n * identity — so dispatch/pipeline composition runs without side effects. */\nexport interface BootDeps {\n fetchFn: typeof fetch;\n sleepFn: (ms: number) => Promise<void>;\n spawnFn: typeof spawn;\n exec: PreflightDeps[\"exec\"];\n /** Reads the running package's own version (cli.ts:30-36 mechanism). */\n installedVersion: () => Promise<string>;\n dirExists: (path: string) => boolean;\n hostname: string;\n log: BootLogger;\n}\n\nexport function defaultBootDeps(): BootDeps {\n return {\n fetchFn: fetch,\n sleepFn: (ms) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n }),\n spawnFn: spawn,\n exec: defaultExec,\n installedVersion: () => {\n const version = readAgentVersion();\n return version\n ? Promise.resolve(version)\n : Promise.reject(new Error(\"could not read installed agent version\"));\n },\n dirExists: (path) => {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n },\n hostname: osHostname(),\n log: createBootLogger(),\n };\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/** Resolves when `signal` aborts (or is already aborted); never rejects. */\nfunction whenAborted(signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n signal.addEventListener(\"abort\", () => resolve(), { once: true });\n });\n}\n\n/**\n * The agent container's boot: poll bootstrap → unpack the bundle into the\n * runner's child env (with graphify env merged) → reclaim home ownership\n * (best-effort) → wire the persistent user-home. superviseRunner runs after\n * the pipeline — it IS the container's foreground process, not a step. Each\n * post-poll step no-ops when the bundle is absent (SIGTERM aborted standby).\n */\nexport function agentBootSteps(\n deps: BootDeps = defaultBootDeps(),\n signal: AbortSignal = new AbortController().signal,\n): BootStep[] {\n return [\n {\n name: \"pollBootstrap\",\n critical: true,\n run: async (ctx) => {\n const bundle = await pollBootstrapBundle(ctx.apiUrl, ctx.bootstrapToken, {\n fetchFn: deps.fetchFn,\n sleepFn: deps.sleepFn,\n log: ctx.log,\n signal,\n });\n if (bundle) ctx.bundle = bundle;\n },\n },\n {\n name: \"unpackBundle\",\n critical: true,\n run: (ctx) => {\n if (ctx.bundle) {\n const claims = decodeJwtClaims(ctx.bundle.sessionJwt);\n ctx.claims = claims;\n const childEnv = buildChildEnv({\n base: process.env,\n bundle: ctx.bundle,\n claims,\n hostname: ctx.hostname,\n log: ctx.log,\n });\n const graphifyEnv = computeGraphifyEnv(\n process.env,\n ctx.bundle.gitPlan.repoName,\n deps.dirExists,\n );\n ctx.childEnv = { ...childEnv, ...graphifyEnv };\n }\n return Promise.resolve();\n },\n },\n {\n name: \"reclaimHome\",\n critical: false,\n run: async (ctx) => {\n if (!ctx.bundle) return;\n await reclaimHomeOwnership(HOME_DIR, deps.exec, ctx.log);\n },\n },\n {\n name: \"wireUserHome\",\n critical: true,\n run: (ctx) => {\n if (ctx.bundle) {\n wireUserHome({\n home: HOME_DIR,\n userHomeMount: USER_HOME_MOUNT,\n userId: ctx.childEnv?.CONVEYOR_USER_ID,\n projectId: ctx.childEnv?.CONVEYOR_PROJECT_ID ?? ctx.childEnv?.PROJECT_ID,\n sharedDir: process.env.CONVEYOR_SHARED_DIR,\n log: ctx.log,\n });\n }\n return Promise.resolve();\n },\n },\n ];\n}\n\n/**\n * Declarative manifest of the workbench container's critical boot path\n * (poll → unpack → startDaemon), pinned by boot-dispatch.test.ts. The PRODUCTION\n * executor is `runWorkbenchBoot` (Task 7): it runs these three critical\n * operations AND the post-listen deferred jobs (ensure /workspaces/repo, git\n * prep, user-home, sshd, reclaim) that a flat pipeline cannot express. This\n * function documents the contract those three steps must honor; the run bodies\n * delegate to the same deps seam so it stays a faithful (if partial) executor.\n */\nexport function workbenchBootSteps(\n deps: WorkbenchBootDeps = defaultWorkbenchBootDeps(),\n): BootStep[] {\n return [\n {\n name: \"pollBootstrap\",\n critical: true,\n run: async (ctx) => {\n const bundle = await deps.pollBundle(ctx);\n if (bundle) ctx.bundle = bundle;\n },\n },\n {\n name: \"unpackBundle\",\n critical: true,\n run: (ctx) => {\n if (ctx.bundle) ctx.childEnv = deps.unpack(ctx, ctx.bundle);\n return Promise.resolve();\n },\n },\n {\n name: \"startDaemon\",\n critical: true,\n run: async (ctx) => {\n await deps.startDaemon({\n port: workbenchPort() ?? DEFAULT_WORKBENCH_PORT,\n token: ctx.bootstrapToken,\n version: readAgentVersion() ?? \"unknown\",\n // The manifest has no GitPrepJob, so it must never claim readiness —\n // \"pending\" is the safe answer if anyone ever executes these steps\n // directly instead of via runWorkbenchBoot (which serves live state).\n getGitStatus: () => ({ t: \"gitStatus\", state: \"pending\" }),\n });\n },\n },\n ];\n}\n\nasync function runAgentRole(\n ctx: BootContext,\n deps: BootDeps,\n signal: AbortSignal,\n): Promise<number> {\n try {\n await runPipeline(agentBootSteps(deps, signal), ctx);\n } catch (err) {\n // A critical step (incl. the poll's 401 BootstrapUnauthorizedError, wrapped\n // by runPipeline) — the pipeline already logged FATAL. Pod identity invalid.\n ctx.log.error(`[boot] agent boot failed: ${errMsg(err)}`);\n return 1;\n }\n if (signal.aborted || !ctx.bundle) {\n ctx.log.info(\"[boot] SIGTERM during standby — exiting.\");\n return 0;\n }\n // The agent container's /workspaces/repo is a dead overlay (entrypoint.sh:907)\n // — every workspace op is proxied to the workbench daemon; run from ~.\n process.chdir(HOME_DIR);\n // superviseRunner owns LAUNCH_MARKER (touched post-bind, before first spawn)\n // and the SIGTERM drain once the runner is live.\n return superviseRunner({\n childEnv: ctx.childEnv ?? {},\n cwd: HOME_DIR,\n apiUrl: ctx.apiUrl,\n bootstrapToken: ctx.bootstrapToken,\n log: ctx.log,\n spawnFn: deps.spawnFn,\n signal,\n });\n}\n\nasync function runWorkbenchRole(\n ctx: BootContext,\n deps: BootDeps,\n signal: AbortSignal,\n): Promise<number> {\n // runWorkbenchBoot parks forever on success; the race resolves only when the\n // signal aborts (SIGTERM), which is our clean exit. Its internal poll gets the\n // same signal so a standby SIGTERM aborts the in-flight fetch too.\n try {\n await Promise.race([\n runWorkbenchBoot(ctx, {\n pollBundle: (workbenchCtx) =>\n pollBootstrapBundle(workbenchCtx.apiUrl, workbenchCtx.bootstrapToken, {\n fetchFn: deps.fetchFn,\n sleepFn: deps.sleepFn,\n log: workbenchCtx.log,\n signal,\n }),\n }),\n whenAborted(signal),\n ]);\n } catch (err) {\n ctx.log.error(`[boot] workbench boot failed: ${errMsg(err)}`);\n return 1;\n }\n // ctx.bundle distinguishes a serving workbench from one still in standby —\n // pod-death forensics reads this line.\n ctx.log.info(\n ctx.bundle\n ? \"[boot] SIGTERM — workbench shutting down.\"\n : \"[boot] SIGTERM during standby — exiting.\",\n );\n return 0;\n}\n\n/**\n * The `conveyor-agent boot` process. Resolves the pod exit code (cli.ts does\n * `process.exit(await runBoot(...))`).\n *\n * - `--preflight-update` → self-update against npm, always exit 0.\n * - else: CONVEYOR_CONTAINER_ROLE selects the pipeline (fatal-loud on anything\n * but agent/workbench — vnext pods are split-only), after asserting the two\n * required identity env vars are present.\n */\nexport async function runBoot(argv: string[], deps: BootDeps = defaultBootDeps()): Promise<number> {\n const { log } = deps;\n\n if (argv.includes(\"--preflight-update\")) {\n await runPreflightUpdate({\n exec: deps.exec,\n installedVersion: deps.installedVersion,\n log,\n });\n return 0;\n }\n\n const role = process.env.CONVEYOR_CONTAINER_ROLE;\n if (role !== \"agent\" && role !== \"workbench\") {\n log.error(\"[boot] FATAL missing/invalid CONVEYOR_CONTAINER_ROLE — vnext pods are split-only\");\n return 1;\n }\n\n const apiUrl = process.env.CONVEYOR_API_URL;\n const bootstrapToken = process.env.POD_BOOTSTRAP_TOKEN;\n if (!apiUrl || !bootstrapToken) {\n // Mirrors entrypoint.sh:161-162 — no identity, nothing to poll for.\n log.error(\"[boot] FATAL missing required env: CONVEYOR_API_URL + POD_BOOTSTRAP_TOKEN\");\n return 1;\n }\n\n // One controller drives every abort path: standby poll (resolves null →\n // exit 0), the workbench park race, and the live runner drain.\n const controller = new AbortController();\n const abort = (): void => controller.abort();\n process.on(\"SIGTERM\", abort);\n process.on(\"SIGINT\", abort);\n\n const ctx: BootContext = {\n role: role as BootRole,\n apiUrl,\n bootstrapToken,\n hostname: deps.hostname,\n log,\n };\n\n return role === \"workbench\"\n ? runWorkbenchRole(ctx, deps, controller.signal)\n : runAgentRole(ctx, deps, controller.signal);\n}\n","import type { BootstrapBundle, GitPlan } from \"../setup/bootstrap-bundle-types.js\";\nimport type { BootLogger, SessionClaims } from \"./types.js\";\n\n/**\n * Ports entrypoint.sh:222-344 (bundle field extraction, JWT claim decode,\n * mode mapping, env injection + denylist).\n *\n * DELIBERATE DEVIATION from the bash on empty values: the literal entrypoint\n * exported most credential/identity fields UNCONDITIONALLY — `export\n * FOO=$(... | jq -r '.f // empty')` yields an empty-string-PRESENT variable\n * when the field is missing (jq's `// empty` only empties the substitution,\n * the `export` still happens). That covered CONVEYOR_TASK_TOKEN,\n * CONVEYOR_GITHUB_TOKEN/GH_TOKEN, ANTHROPIC_API_KEY,\n * CLOUDSDK_AUTH_ACCESS_TOKEN, REPO_OWNER/REPO_NAME/BRANCH,\n * CONVEYOR_SESSION_ID, and CONVEYOR_WORKSPACE_ID; only CONVEYOR_TASK_ID and\n * CONVEYOR_PROJECT_ID were truly absent-on-empty in bash too (guarded\n * export / explicit unset). vnext instead omits ANY empty-valued key\n * uniformly (`setIf` in buildChildEnv): absent is the safer contract —\n * presence-keyed consumers (gh, anthropic tooling) misread an empty-present\n * GH_TOKEN/ANTHROPIC_API_KEY as \"credentials configured\" — and every known\n * in-repo consumer checks truthiness, so the change is otherwise inert.\n *\n * `parseBundle` throws on a genuinely missing/wrong-type required field (so\n * boot fails loud instead of shipping \"null\" into a git URL or env var) and\n * normalizes `envVars: null` to `{}`. Unknown top-level keys are preserved\n * on the returned object — the bundle wire contract can grow fields this\n * package doesn't know about yet.\n */\nfunction requireString(source: Record<string, unknown>, field: string): string {\n const value = source[field];\n if (typeof value !== \"string\") {\n throw new Error(`bundle missing field: ${field}`);\n }\n return value;\n}\n\nfunction parseGitPlan(raw: unknown): GitPlan {\n if (typeof raw !== \"object\" || raw === null) {\n throw new Error(\"bundle missing field: gitPlan\");\n }\n const obj = raw as Record<string, unknown>;\n const plan: GitPlan = {\n repoOwner: requireString(obj, \"repoOwner\"),\n repoName: requireString(obj, \"repoName\"),\n branch: requireString(obj, \"branch\"),\n baseBranch: requireString(obj, \"baseBranch\"),\n };\n if (typeof obj.checkoutRef === \"string\") {\n plan.checkoutRef = obj.checkoutRef;\n }\n return plan;\n}\n\nexport function parseBundle(text: string): BootstrapBundle {\n const raw = JSON.parse(text) as Record<string, unknown>;\n const envVarsRaw = raw.envVars;\n const envVars: Record<string, string> =\n envVarsRaw !== null && envVarsRaw !== undefined && typeof envVarsRaw === \"object\"\n ? (envVarsRaw as Record<string, string>)\n : {};\n const typed = {\n sessionJwt: requireString(raw, \"sessionJwt\"),\n githubToken: requireString(raw, \"githubToken\"),\n anthropicKey: requireString(raw, \"anthropicKey\"),\n gcpToken: requireString(raw, \"gcpToken\"),\n envVars,\n gitPlan: parseGitPlan(raw.gitPlan),\n };\n return { ...raw, ...typed } as BootstrapBundle;\n}\n\n/**\n * Decodes the non-secret session identity claims out of the task JWT's\n * middle segment — base64url alphabet (`-`/`_`), padding restored before\n * decode. Ports the inline `node -e` one-liner at entrypoint.sh:236. Any\n * failure (too few segments, invalid base64, non-JSON payload) yields `{}`\n * rather than throwing: an unreadable JWT degrades to \"no claims\", it never\n * aborts boot — the bootstrap poll step already validated the token against\n * the API before this runs.\n */\nexport function decodeJwtClaims(jwt: string): SessionClaims {\n try {\n const segment = jwt.split(\".\")[1] ?? \"\";\n const normalized = segment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = normalized + \"=\".repeat((4 - (normalized.length % 4)) % 4);\n const json = Buffer.from(padded, \"base64\").toString(\"utf8\");\n const parsed: unknown = JSON.parse(json);\n if (typeof parsed !== \"object\" || parsed === null) {\n return {};\n }\n return parsed as SessionClaims;\n } catch {\n return {};\n }\n}\n\n// Mode mapping, comments carried from entrypoint.sh:261-287. Order is load-bearing —\n// a wrong mapping here crash-looped pods every 10s (agent CLI exits \"Invalid\n// CONVEYOR_MODE\"). Do not reorder without re-reading the history in that range.\nexport function resolveModeEnv(claims: SessionClaims): Record<string, string> {\n // Review sessions are keyed on the session MODE claim, not role; the runner\n // mode is spelled \"code-review\" — bare \"review\" crash-loops the CLI.\n if (claims.mode === \"review\") return { CONVEYOR_MODE: \"code-review\" };\n if (claims.mode === \"pack\") return { CONVEYOR_MODE: \"pack\" };\n // adhoc is also task-less with a projectId — it must win over the pm branch.\n if (claims.mode === \"adhoc\") return { CONVEYOR_MODE: \"adhoc\" };\n if (!claims.taskId && claims.projectId) return { CONVEYOR_MODE: \"pm\" };\n return {};\n}\n\n// A bundle key that isn't a valid shell/env identifier would make `export`\n// eval arbitrary content in the old bash world; the vnext child-env object\n// has no such injection risk, but we keep the same validation so a garbage\n// key can never silently shadow something unexpected. A handful of names\n// would hijack the boot itself (PATH swaps every binary below; LD_PRELOAD\n// injects code into them) — denylisted regardless of validity.\nexport const ENV_KEY_DENYLIST: ReadonlySet<string> = new Set([\n \"PATH\",\n \"HOME\",\n \"LD_PRELOAD\",\n \"SHELL\",\n]);\nconst IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport interface BuildChildEnvOptions {\n base: NodeJS.ProcessEnv;\n bundle: BootstrapBundle;\n claims: SessionClaims;\n hostname: string;\n log: BootLogger;\n /** default \"/workspaces/repo\" */\n workspaceDir?: string;\n}\n\nexport function buildChildEnv(opts: BuildChildEnvOptions): Record<string, string> {\n const { base, bundle, claims, hostname, log } = opts;\n const child: Record<string, string> = {};\n for (const [k, v] of Object.entries(base)) {\n if (typeof v === \"string\") child[k] = v;\n }\n for (const [key, value] of Object.entries(bundle.envVars ?? {})) {\n if (!IDENTIFIER_RE.test(key)) {\n log.warn(`[boot] WARN skipping invalid env key from bundle: '${key}'`);\n continue;\n }\n if (ENV_KEY_DENYLIST.has(key)) {\n log.warn(`[boot] WARN skipping denylisted env key from bundle: '${key}'`);\n continue;\n }\n child[key] = value;\n }\n const setIf = (k: string, v: string | undefined): void => {\n if (v) child[k] = v;\n };\n setIf(\"CONVEYOR_TASK_TOKEN\", bundle.sessionJwt);\n setIf(\"CONVEYOR_GITHUB_TOKEN\", bundle.githubToken);\n setIf(\"GH_TOKEN\", bundle.githubToken);\n setIf(\"ANTHROPIC_API_KEY\", bundle.anthropicKey);\n setIf(\"CLOUDSDK_AUTH_ACCESS_TOKEN\", bundle.gcpToken);\n setIf(\"REPO_OWNER\", bundle.gitPlan.repoOwner);\n setIf(\"REPO_NAME\", bundle.gitPlan.repoName);\n setIf(\"BRANCH\", bundle.gitPlan.branch);\n // CONVEYOR_TASK_ID is absent (never empty) for task-less sessions.\n setIf(\"CONVEYOR_TASK_ID\", claims.taskId);\n setIf(\"CONVEYOR_PROJECT_ID\", claims.projectId);\n setIf(\"CONVEYOR_SESSION_ID\", claims.sessionId);\n setIf(\"CONVEYOR_WORKSPACE_ID\", claims.workspaceId);\n Object.assign(child, resolveModeEnv(claims));\n child.CLAUDESPACE_NAME = hostname;\n child.CONVEYOR_WORKSPACE = opts.workspaceDir ?? \"/workspaces/repo\";\n child.CONVEYOR_BOOT_VNEXT = \"1\";\n // POD_BOOTSTRAP_TOKEN and CONVEYOR_API_URL are never touched here — they pass\n // through unchanged from `base` (copied above). POD_BOOTSTRAP_TOKEN is the\n // credential-refresh key: the agent re-polls with it to swap in a fresh\n // githubToken/sessionJwt in place (entrypoint.sh:338-340).\n return child;\n}\n","/**\n * Port of entrypoint.sh:494-519 (`configure_graphify_env`). A locally\n * published graph lives in the shared GCS-FUSE user-home bucket at a fixed\n * mount point, under `users/_shared/graphify/<repo>/latest` — this computes\n * the env var contract the rest of boot (and the agent process) consumes,\n * without touching the filesystem itself (that's `dirExists`, injected so\n * this stays pure/testable).\n */\nconst USER_HOME_MOUNT = \"/mnt/conveyor-users\";\n\nexport interface GraphifyEnvInput {\n CONVEYOR_GRAPHIFY_DISABLE?: string;\n CONVEYOR_GRAPHIFY_SLUG?: string;\n CONVEYOR_GRAPHIFY_SHARED_ROOT?: string;\n CONVEYOR_GRAPHIFY_DIR?: string;\n CONVEYOR_GRAPHIFY_GRAPH?: string;\n}\n\nexport function computeGraphifyEnv(\n env: GraphifyEnvInput,\n repoName: string | undefined,\n dirExists: (p: string) => boolean,\n): Record<string, string> {\n if (env.CONVEYOR_GRAPHIFY_DISABLE === \"1\") {\n return {};\n }\n\n const slug = env.CONVEYOR_GRAPHIFY_SLUG || repoName;\n if (!slug) {\n return {};\n }\n\n const primaryRoot = `${USER_HOME_MOUNT}/users/_shared/graphify`;\n const legacyRoot = `${USER_HOME_MOUNT}/_shared/graphify`;\n let sharedRoot = env.CONVEYOR_GRAPHIFY_SHARED_ROOT;\n if (!sharedRoot) {\n // Primary root wins whenever it exists (or legacy doesn't) — legacy is\n // only chosen when primary is missing AND legacy is actually present.\n sharedRoot = dirExists(primaryRoot) || !dirExists(legacyRoot) ? primaryRoot : legacyRoot;\n }\n\n const dir = env.CONVEYOR_GRAPHIFY_DIR || `${sharedRoot}/${slug}/latest`;\n const graph = env.CONVEYOR_GRAPHIFY_GRAPH || `${dir}/graph.json`;\n\n return {\n CONVEYOR_GRAPHIFY_SLUG: slug,\n CONVEYOR_GRAPHIFY_SHARED_ROOT: sharedRoot,\n CONVEYOR_GRAPHIFY_DIR: dir,\n CONVEYOR_GRAPHIFY_GRAPH: graph,\n };\n}\n","import { appendFileSync } from \"node:fs\";\nimport type { BootLogger } from \"./types.js\";\n\nexport const BOOTSTRAP_LOG_PATH = \"/tmp/claudespace-bootstrap.log\";\n\nexport function createBootLogger(filePath: string | undefined = BOOTSTRAP_LOG_PATH): BootLogger {\n const emit = (line: string): void => {\n process.stdout.write(`${line}\\n`);\n if (filePath) {\n try {\n appendFileSync(filePath, `${line}\\n`);\n } catch {\n // The file sink is best-effort; stdout is the durable channel (kubectl logs).\n }\n }\n };\n return { info: emit, warn: emit, error: emit };\n}\n","import type { BootstrapBundle } from \"../setup/bootstrap-bundle-types.js\";\n\nexport type BootRole = \"agent\" | \"workbench\";\n\nexport interface BootLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n}\n\nexport interface SessionClaims {\n taskId?: string;\n projectId?: string;\n sessionId?: string;\n workspaceId?: string;\n mode?: string;\n}\n\nexport interface BootContext {\n role: BootRole;\n apiUrl: string;\n bootstrapToken: string;\n hostname: string;\n log: BootLogger;\n /** Filled by the poll step. */\n bundle?: BootstrapBundle;\n /** Filled by the unpack step. */\n claims?: SessionClaims;\n /** Filled by the unpack step — the env handed to children (runner/daemon jobs). */\n childEnv?: Record<string, string>;\n}\n\nexport interface BootStep {\n name: string;\n /** true → a throw aborts boot (fatal-loud). false → degrade-and-continue (warn). */\n critical: boolean;\n run(ctx: BootContext): Promise<void>;\n}\n\nexport class BootFatalError extends Error {\n constructor(\n public readonly step: string,\n message: string,\n options?: { cause?: unknown },\n ) {\n super(`[boot] FATAL step=${step}: ${message}`, options);\n this.name = \"BootFatalError\";\n }\n}\n","import { BootFatalError, type BootContext, type BootStep } from \"./types.js\";\n\nexport async function runPipeline(steps: BootStep[], ctx: BootContext): Promise<void> {\n const t0 = Date.now();\n const timings: string[] = [];\n for (const step of steps) {\n const s0 = Date.now();\n try {\n await step.run(ctx);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (step.critical) {\n ctx.log.error(`[boot] FATAL step=${step.name}: ${msg}`);\n throw err instanceof BootFatalError\n ? err\n : new BootFatalError(step.name, msg, { cause: err });\n }\n ctx.log.warn(`[boot] WARN step=${step.name} degraded: ${msg}`);\n }\n const ms = Date.now() - s0;\n timings.push(`${step.name}:${ms}`);\n ctx.log.info(`[boot] step=${step.name} ms=${ms}`);\n }\n ctx.log.info(\n `[boot] timeline role=${ctx.role} total=${Date.now() - t0} steps=${timings.join(\",\")}`,\n );\n}\n","import type { BootstrapBundle } from \"../setup/bootstrap-bundle-types.js\";\nimport { parseBundle } from \"./bundle.js\";\nimport type { BootLogger } from \"./types.js\";\n\n/**\n * Ports the standby poll loop (entrypoint.sh:186-220): poll\n * `GET ${apiUrl}/api/v3/pods/bootstrap` until the reconciler binds this pod\n * (200 + bundle body) or the bootstrap token is rejected (401, fatal). 204\n * (still unbound) and any network error both retry after `pollIntervalMs`.\n *\n * DELIBERATELY NOT `setup/bootstrap-poll.ts` (`pollUntilBound`) — that\n * helper has deadline/throw semantics for a different caller. Boot instead\n * needs abort support (SIGTERM during standby → clean exit) and periodic\n * progress logging, so it gets its own poll loop.\n */\nexport class BootstrapUnauthorizedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BootstrapUnauthorizedError\";\n }\n}\n\nexport interface PollDeps {\n fetchFn: typeof fetch;\n sleepFn: (ms: number) => Promise<void>;\n log: BootLogger;\n /** Aborted (e.g. SIGTERM during standby) → resolves null; caller exits 0. */\n signal?: AbortSignal;\n /** default 2000 */\n pollIntervalMs?: number;\n}\n\nconst PROGRESS_LOG_EVERY = 30;\nconst DEFAULT_POLL_INTERVAL_MS = 2_000;\n\n/**\n * Resolves after `ms` via `sleepFn`, or immediately when `signal` aborts —\n * whichever comes first. Without the race, a SIGTERM landing mid-sleep would\n * ride out the full poll interval before the loop-top abort check could run.\n * The abort listener is removed on normal completion so repeated polls don't\n * accumulate listeners on a long-lived signal.\n */\nfunction sleepUnlessAborted(\n sleepFn: (ms: number) => Promise<void>,\n ms: number,\n signal?: AbortSignal,\n): Promise<void> {\n if (!signal) return sleepFn(ms);\n if (signal.aborted) return Promise.resolve();\n return new Promise<void>((resolve) => {\n const onAbort = (): void => {\n resolve();\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n void sleepFn(ms).then(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n });\n });\n}\n\n/**\n * Resolves the parsed bundle once bound, or `null` if `deps.signal` is\n * aborted first. Never logs the response body — it carries credentials.\n */\nexport async function pollBootstrapBundle(\n apiUrl: string,\n token: string,\n deps: PollDeps,\n): Promise<BootstrapBundle | null> {\n const interval = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n let pollCount = 0;\n\n while (true) {\n if (deps.signal?.aborted) return null;\n\n let res: Response | undefined;\n try {\n // The signal is threaded into fetch so a SIGTERM during shutdown can\n // abort a hung request instead of blocking past the grace period.\n res = await deps.fetchFn(`${apiUrl}/api/v3/pods/bootstrap`, {\n headers: { Authorization: `Bearer ${token}` },\n signal: deps.signal,\n });\n } catch {\n // An abort mid-fetch surfaces here as an AbortError rejection —\n // that's shutdown, not a network failure; bail out immediately.\n if (deps.signal?.aborted) return null;\n // Network error — treat the same as a still-unbound (204) poll and retry.\n res = undefined;\n }\n\n if (res?.status === 200) {\n return parseBundle(await res.text());\n }\n if (res?.status === 401) {\n throw new BootstrapUnauthorizedError(\"bootstrap token rejected (401) — pod identity invalid\");\n }\n\n pollCount++;\n if (pollCount % PROGRESS_LOG_EVERY === 0) {\n deps.log.info(`[boot] Still waiting for bind (poll #${pollCount})...`);\n }\n await sleepUnlessAborted(deps.sleepFn, interval, deps.signal);\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { BootLogger } from \"./types.js\";\n\nexport const AGENT_PACKAGE = \"@rallycry/conveyor-agent\";\nexport const NPM_VIEW_TIMEOUT_MS = 3_000;\nexport const NPM_INSTALL_TIMEOUT_MS = 120_000;\nexport const NPM_CACHE_DIR = \"/tmp/npm-cache\";\nconst NPM_REGISTRY_ARG = \"--@rallycry:registry=https://registry.npmjs.org/\";\n\nconst SEMVER_RE = /^\\d+\\.\\d+\\.\\d+([-+][A-Za-z0-9.-]+)?$/;\n\nexport function isValidSemver(value: string): boolean {\n return SEMVER_RE.test(value);\n}\n\n/**\n * sort -V semantics on the numeric triple; prerelease/build metadata ignored for ordering.\n * Precondition: both inputs must be valid semver — call isValidSemver first.\n */\nexport function isStrictlyNewer(candidate: string, installed: string): boolean {\n const num = (v: string): number[] => (v.split(/[-+]/)[0] ?? \"\").split(\".\").map((p) => Number(p));\n const [a, b] = [num(candidate), num(installed)];\n for (let i = 0; i < 3; i++) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n if (av !== bv) return av > bv;\n }\n return false;\n}\n\nconst execFileAsync = promisify(execFile);\n\nexport interface PreflightDeps {\n exec(cmd: string, args: string[], opts: { timeoutMs: number }): Promise<{ stdout: string }>;\n installedVersion(): Promise<string>;\n log: BootLogger;\n}\n\nexport function defaultExec(\n cmd: string,\n args: string[],\n opts: { timeoutMs: number },\n): Promise<{ stdout: string }> {\n return execFileAsync(cmd, args, { timeout: opts.timeoutMs });\n}\n\nexport async function runPreflightUpdate(\n deps: PreflightDeps,\n): Promise<\"updated\" | \"current\" | \"skipped\"> {\n let latest = \"\";\n try {\n const { stdout } = await deps.exec(\n \"npm\",\n [\"view\", \"--cache\", NPM_CACHE_DIR, NPM_REGISTRY_ARG, AGENT_PACKAGE, \"version\"],\n { timeoutMs: NPM_VIEW_TIMEOUT_MS },\n );\n latest = stdout.replace(/\\s+/g, \"\");\n } catch (err) {\n deps.log.warn(\n `[boot] WARN preflight npm view failed (${err instanceof Error ? err.message : err}); keeping baked version`,\n );\n return \"skipped\";\n }\n if (!isValidSemver(latest)) {\n deps.log.warn(\n `[boot] WARN preflight got non-semver '${latest}' from npm; keeping baked version`,\n );\n return \"skipped\";\n }\n let installed = \"\";\n try {\n installed = (await deps.installedVersion()).trim();\n } catch (err) {\n deps.log.warn(\n `[boot] WARN preflight installed-version lookup failed (${err instanceof Error ? err.message : err}); keeping baked version`,\n );\n return \"skipped\";\n }\n if (!isValidSemver(installed) || isStrictlyNewer(latest, installed)) {\n deps.log.info(\n `[boot] preflight updating ${AGENT_PACKAGE} ${installed || \"unknown\"} -> ${latest}`,\n );\n try {\n await deps.exec(\n \"sudo\",\n [\n \"npm\",\n \"install\",\n \"-g\",\n \"--cache\",\n NPM_CACHE_DIR,\n \"--silent\",\n `${AGENT_PACKAGE}@${latest}`,\n ],\n { timeoutMs: NPM_INSTALL_TIMEOUT_MS },\n );\n // The cache is pure disk weight once the install lands, and the\n // controller container's ephemeral-storage budget is tight — leaving\n // the tarballs behind contributed to kubelet evicting freshly booted\n // pods (\"Container agent exceeded its local ephemeral storage limit\").\n // sudo because the install above ran as root and owns the cache dir.\n await deps\n .exec(\"sudo\", [\"rm\", \"-rf\", NPM_CACHE_DIR], { timeoutMs: NPM_VIEW_TIMEOUT_MS })\n .catch(() => undefined);\n return \"updated\";\n } catch (err) {\n deps.log.warn(\n `[boot] WARN preflight install failed (${err instanceof Error ? err.message : err}); keeping baked version`,\n );\n return \"skipped\";\n }\n }\n deps.log.info(`[boot] preflight agent ${installed} is current (registry ${latest})`);\n return \"current\";\n}\n","import { spawn as spawnChildProcess, type ChildProcess } from \"node:child_process\";\nimport { appendFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { reportAgentCrash } from \"./crash-report.js\";\nimport type { BootLogger } from \"./types.js\";\n\n/**\n * Ports the supervised launch loop (entrypoint.sh:901-978): spawn\n * `conveyor-agent`, restart it on a nonzero exit up to `AGENT_CRASH_MAX`\n * times with a backoff between attempts, and report every crash to the API.\n * A clean exit (0) or an aborted (SIGTERM) supervision both return without\n * further restarts.\n *\n * Liveness contract: this supervisor writes the markers\n * `apps/api/.../dockerfiles/liveness.sh` reads — `LAUNCH_MARKER`\n * (/tmp/conveyor-agent-launching) once supervision starts (post-bind, before\n * the first spawn), arming the probe, and `RESTARTING_MARKER` touched before\n * each restart backoff so the probe stays green across backoff sleeps.\n * (Same names the late-era bash entrypoint used, so one liveness.sh serves\n * both worlds during the rollout window.)\n */\n// Mirrors bootstrap-route.ts:35.\nexport const AGENT_CRASH_MAX = 3;\nexport const AGENT_LOG_PATH = \"/tmp/claudespace-agent.log\";\n\nconst LAUNCH_MARKER_NAME = \"conveyor-agent-launching\";\nconst RESTARTING_MARKER_NAME = \"conveyor-agent-restarting\";\nexport const LAUNCH_MARKER = `/tmp/${LAUNCH_MARKER_NAME}`;\nexport const RESTARTING_MARKER = `/tmp/${RESTARTING_MARKER_NAME}`;\n\nconst DEFAULT_CRASH_BACKOFF_MS = 10_000;\n// Under the pod's 60s terminationGracePeriodSeconds.\nconst DEFAULT_DRAIN_TIMEOUT_MS = 45_000;\nconst DEFAULT_MARKER_DIR = \"/tmp\";\n\nexport interface SuperviseOptions {\n childEnv: Record<string, string>;\n cwd: string;\n apiUrl: string;\n bootstrapToken: string;\n log: BootLogger;\n /** Injected in tests to substitute a fake binary for `conveyor-agent`. */\n spawnFn?: typeof spawnChildProcess;\n reportFn?: typeof reportAgentCrash;\n crashBackoffMs?: number;\n drainTimeoutMs?: number;\n /** default \"/tmp\"; tests point this at a tmpdir. */\n markerDir?: string;\n /**\n * Test-visible SIGTERM stand-in. Production wiring (`process.on(\"SIGTERM\",\n * () => controller.abort())`) happens in Task 9 — this module only reacts\n * to the signal, it never installs the process listener itself.\n */\n signal?: AbortSignal;\n}\n\n/** Touch (create-or-refresh) a liveness marker. Best-effort — a marker-dir\n * permission problem must never take down agent supervision. */\nfunction touchMarker(path: string, log: BootLogger): void {\n try {\n writeFileSync(path, \"\");\n } catch (err) {\n log.warn(\n `[boot] WARN failed to write liveness marker ${path}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\n/**\n * Tees the child's stdout/stderr to our own stdout (so pod logs still show\n * it) and appends to AGENT_LOG_PATH. Chunks are written as they arrive — no\n * ordering guarantee is claimed or needed beyond \"as they arrive\". The\n * append is best-effort (`report_agent_crash`-style `|| true`): a full disk\n * or permission error on the log file must never kill supervision.\n */\nfunction teeChildOutput(child: ChildProcess): void {\n const onData = (chunk: Buffer): void => {\n process.stdout.write(chunk);\n try {\n appendFileSync(AGENT_LOG_PATH, chunk);\n } catch {\n // Best-effort log file — see doc comment above.\n }\n };\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n}\n\nfunction spawnAgent(spawnFn: typeof spawnChildProcess, opts: SuperviseOptions): ChildProcess {\n // argv `[]`, PATH-resolved (no shell) so a preflight npm-install update\n // lands on the next spawn without any code here caring about it.\n return spawnFn(\"conveyor-agent\", [], {\n cwd: opts.cwd,\n env: opts.childEnv,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n}\n\n/** Resolves the child's exit code. A signal-kill (code null) counts as\n * nonzero — the only caller that kills the child itself (drain) already\n * short-circuits before this matters for restart bookkeeping. */\nfunction waitForExit(child: ChildProcess): Promise<number> {\n return new Promise((resolve) => {\n child.once(\"exit\", (code, signal) => {\n resolve(code ?? (signal ? 1 : 0));\n });\n child.once(\"error\", () => resolve(1));\n });\n}\n\n/** Never resolves unless `signal` fires (or is already aborted). */\nfunction whenAborted(signal: AbortSignal | undefined): Promise<void> {\n return new Promise((resolve) => {\n if (!signal) return;\n if (signal.aborted) {\n resolve();\n return;\n }\n signal.addEventListener(\"abort\", () => resolve(), { once: true });\n });\n}\n\n/**\n * Sleeps `ms`, resolving early if `signal` aborts — mirrors poll.ts's\n * module-private `sleepUnlessAborted` so a SIGTERM landing mid-backoff\n * doesn't ride out the full `crashBackoffMs` before draining can start.\n */\nfunction sleepUnlessAborted(ms: number, signal?: AbortSignal): Promise<void> {\n if (!signal) {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n }\n if (signal.aborted) return Promise.resolve();\n return new Promise((resolve) => {\n const onAbort = (): void => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Forward SIGTERM to the child, wait up to `drainTimeoutMs` for it to exit\n * on its own, then SIGKILL. Always resolves 0 — draining is a clean\n * shutdown from the supervisor's point of view regardless of the child's\n * final exit code.\n */\nasync function drainChild(\n child: ChildProcess,\n exitPromise: Promise<number>,\n drainTimeoutMs: number,\n log: BootLogger,\n): Promise<number> {\n log.info(\"[boot] SIGTERM received, draining agent...\");\n child.kill(\"SIGTERM\");\n\n const TIMED_OUT = Symbol(\"drain-timeout\");\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<typeof TIMED_OUT>((resolve) => {\n timer = setTimeout(() => resolve(TIMED_OUT), drainTimeoutMs);\n });\n const winner = await Promise.race([exitPromise, timeout]);\n if (timer) clearTimeout(timer);\n\n if (winner === TIMED_OUT) {\n log.warn(`[boot] agent did not exit within drainTimeoutMs=${drainTimeoutMs}ms — SIGKILL`);\n child.kill(\"SIGKILL\");\n await exitPromise;\n }\n return 0;\n}\n\nexport async function superviseRunner(opts: SuperviseOptions): Promise<number> {\n const spawnFn = opts.spawnFn ?? spawnChildProcess;\n const reportFn = opts.reportFn ?? reportAgentCrash;\n const crashBackoffMs = opts.crashBackoffMs ?? DEFAULT_CRASH_BACKOFF_MS;\n const drainTimeoutMs = opts.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;\n const markerDir = opts.markerDir ?? DEFAULT_MARKER_DIR;\n const launchMarkerPath = join(markerDir, LAUNCH_MARKER_NAME);\n const restartingMarkerPath = join(markerDir, RESTARTING_MARKER_NAME);\n\n // Supervision starting (post-bind) arms the liveness probe — see the\n // liveness-contract doc comment above.\n touchMarker(launchMarkerPath, opts.log);\n\n let attempt = 0;\n while (true) {\n attempt++;\n opts.log.info(\"[boot] Launching agent...\");\n const child = spawnAgent(spawnFn, opts);\n teeChildOutput(child);\n const exitPromise = waitForExit(child);\n\n const winner = await Promise.race([\n exitPromise.then((code) => ({ type: \"exit\" as const, code })),\n whenAborted(opts.signal).then(() => ({ type: \"abort\" as const })),\n ]);\n\n if (winner.type === \"abort\") {\n return await drainChild(child, exitPromise, drainTimeoutMs, opts.log);\n }\n\n const exitCode = winner.code;\n if (exitCode === 0) {\n opts.log.info(\"[boot] agent exited cleanly (code 0), shutting down pod.\");\n return 0;\n }\n\n if (attempt >= AGENT_CRASH_MAX) {\n opts.log.error(\n `[boot] agent crashed (code ${exitCode}) — attempt cap ${AGENT_CRASH_MAX} reached, giving up.`,\n );\n await reportFn(opts.apiUrl, opts.bootstrapToken, { exitCode, attempt, final: true });\n return 1;\n }\n\n touchMarker(restartingMarkerPath, opts.log);\n await reportFn(opts.apiUrl, opts.bootstrapToken, { exitCode, attempt, final: false });\n opts.log.warn(\n `[boot] agent crashed (code ${exitCode}), retrying in ${crashBackoffMs}ms (attempt ${attempt}/${AGENT_CRASH_MAX})...`,\n );\n await sleepUnlessAborted(crashBackoffMs, opts.signal);\n if (opts.signal?.aborted) {\n // Aborted during backoff — no child is running; draining means \"stop\n // trying to restart\", nothing to SIGTERM/await here.\n return 0;\n }\n }\n}\n","/**\n * Ports `report_agent_crash` (entrypoint.sh:936-943): the pod-side crash\n * supervisor's best-effort POST to the API so the crash gets posted to the\n * task's activity log (which also keeps the pod alive through recovery) and,\n * on the final attempt, a durable notification. Payload contract matches\n * `bootstrap-route.ts`'s `parseCrashReport`: `{ exitCode, attempt, final }`.\n *\n * Best-effort, mirrors the bash `|| true`: a failed or slow report must never\n * block the supervisor's restart loop, so every path swallows errors and this\n * never throws.\n */\nconst CRASH_REPORT_TIMEOUT_MS = 10_000;\n\nexport interface CrashReport {\n exitCode: number;\n attempt: number;\n final: boolean;\n}\n\nexport async function reportAgentCrash(\n apiUrl: string,\n bootstrapToken: string,\n report: CrashReport,\n fetchFn: typeof fetch = fetch,\n): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), CRASH_REPORT_TIMEOUT_MS);\n try {\n await fetchFn(`${apiUrl}/api/v3/pods/agent-crash`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${bootstrapToken}`,\n },\n body: JSON.stringify(report),\n signal: controller.signal,\n });\n } catch {\n // Best-effort — never let a network failure or timeout block the retry loop.\n } finally {\n clearTimeout(timer);\n }\n}\n","import {\n chmodSync,\n copyFileSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n rmSync,\n statSync,\n symlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { PreflightDeps } from \"./self-update.js\";\nimport type { BootLogger } from \"./types.js\";\n\n/**\n * Ports entrypoint.sh:120-143 (`reclaim_home_ownership`), 346-379\n * (`seed_claude_json`), and 381-487 (mount wiring / pod-local ~/.claude.json /\n * credential symlink / opencode links / no-mount fallback).\n *\n * `.claude/rules/claude-session-persistence.md` invariant carried verbatim:\n * ~/.claude.json is pod-local and NEVER symlinked into the mount — the CLI\n * rewrites it wholesale at startup/during runs, and a burst of concurrent pods\n * for the same user+project racing one shared file produces last-writer-wins\n * lost updates (root cause of a live TUI startup-dialog park and a poisoned\n * customApiKeyResponses write bricking every future pod). ~/.claude itself IS\n * symlinked into the mount when present — it holds the shared, append-mostly\n * transcript/plan/memory state --resume depends on.\n */\n\n// ── seed_claude_json ────────────────────────────────────────────────────────\n\nexport const SEED_GATES: Record<string, unknown> = {\n hasCompletedOnboarding: true,\n bypassPermissionsModeAccepted: true,\n projects: {\n \"/workspaces/repo\": { hasTrustDialogAccepted: true },\n },\n};\n\n// Fable is entitlement-gated in the CLI's /model picker via\n// additionalModelOptionsCache, normally populated by a bootstrap fetch the\n// pods' synthesized credentials don't satisfy — without this row the picker\n// never offers Fable in a pod. The literal value is entitlement-load-bearing;\n// it mirrors FABLE_MODEL (\"claude-fable-5\") in packages/shared/src/constants/\n// models.ts plus the CLI's \"[1m]\" 1M-context variant suffix.\nexport const SEED_FABLE_OPTION: {\n value: \"claude-fable-5[1m]\";\n label: \"Fable\";\n description: string;\n} = {\n value: \"claude-fable-5[1m]\",\n label: \"Fable\",\n description: \"Fable 5 - most capable for your hardest and longest-running tasks\",\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction deepMerge(\n target: Record<string, unknown>,\n patch: Record<string, unknown>,\n): Record<string, unknown> {\n const out = { ...target };\n for (const [k, v] of Object.entries(patch)) {\n const cur = out[k];\n out[k] = isPlainObject(cur) && isPlainObject(v) ? deepMerge(cur, v) : v;\n }\n return out;\n}\n\nfunction hasFableEntry(cache: unknown[]): boolean {\n return cache.some((entry) => {\n if (!isPlainObject(entry)) return false;\n const value = entry.value;\n return typeof value === \"string\" && value.toLowerCase().includes(\"fable\");\n });\n}\n\n/**\n * Pre-answer every interactive first-run gate the CLI shows on a fresh (or\n * partially initialized) config — the onboarding wizard, the\n * bypass-permissions warning, and the folder-trust dialog for the repo\n * workspace — plus append the Fable model-picker entitlement row. Merge via\n * deep-merge, never clobber: CLI-owned keys (cache keys, firstStartTime,\n * other projects' trust entries) survive. A missing or corrupt file becomes a\n * fresh seed (Claude Code treats a 0-byte/unparseable file as fatal, so\n * replacing is a repair, not data loss). Theme only seeds fresh configs — a\n * user-picked theme is never overridden. Never throws: seeding must not kill\n * boot under a single bad stat/write.\n */\nexport function seedClaudeJson(targetPath: string): void {\n try {\n let config: Record<string, unknown> | null = null;\n try {\n const parsed: unknown = JSON.parse(readFileSync(targetPath, \"utf8\"));\n if (isPlainObject(parsed)) config = parsed;\n } catch {\n // Missing or corrupt → fresh seed (repair), not a throw.\n }\n let merged = config ? deepMerge(config, SEED_GATES) : { ...SEED_GATES, theme: \"dark\" };\n const cache = Array.isArray(merged.additionalModelOptionsCache)\n ? merged.additionalModelOptionsCache\n : [];\n if (!hasFableEntry(cache)) {\n merged = { ...merged, additionalModelOptionsCache: [...cache, SEED_FABLE_OPTION] };\n }\n writeFileSync(targetPath, JSON.stringify(merged));\n } catch {\n // Seeding must never kill boot.\n }\n}\n\n// ── wire_user_home ──────────────────────────────────────────────────────────\n\nexport interface WireUserHomeOptions {\n /** \"/home/conveyor\" in prod, a tmpdir in tests. */\n home: string;\n /** \"/mnt/conveyor-users\" — the GCS FUSE CSI mount point. */\n userHomeMount: string;\n userId?: string;\n projectId?: string;\n /** CONVEYOR_SHARED_DIR — always set in split pods. */\n sharedDir?: string;\n log: BootLogger;\n}\n\nfunction isDir(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction isRegularFile(path: string): boolean {\n try {\n return lstatSync(path).isFile();\n } catch {\n return false;\n }\n}\n\nfunction safely(log: BootLogger, label: string, fn: () => void): void {\n try {\n fn();\n } catch (err) {\n log.warn(`[boot] WARN ${label} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\n/** `ln -sfn target link` semantics: replace whatever is at `link`, creating\n * its parent dir first. */\nfunction relink(target: string, link: string): void {\n mkdirSync(dirname(link), { recursive: true });\n rmSync(link, { recursive: true, force: true });\n symlinkSync(target, link);\n}\n\ninterface CredentialWireOptions {\n home: string;\n root: string;\n sharedDir?: string;\n log: BootLogger;\n}\n\n/**\n * Pod-local ~/.claude/.credentials.json: concurrent pods under different keys\n * clobbering one shared file cross-wired usage attribution between accounts\n * (seen live). The shared dir keeps a SYMLINK to an absolute pod-local path,\n * which resolves per-pod; racing pods all write the identical symlink value,\n * so the symlink itself cannot lose data. A pre-existing REGULAR (non-symlink)\n * shared file is legacy — carry its contents into the pod-local target first\n * so auth survives the cutover.\n */\nfunction wireCredentials(opts: CredentialWireOptions): void {\n const { home, root, sharedDir, log } = opts;\n const shared = join(root, \".claude\", \".credentials.json\");\n const podLocal = sharedDir\n ? join(sharedDir, \"claude-credentials.pod.json\")\n : join(home, \".claude-credentials.pod.json\");\n safely(log, \"carry legacy shared credentials\", () => {\n if (isRegularFile(shared)) copyFileSync(shared, podLocal);\n });\n safely(log, \"repoint credentials symlink\", () => relink(podLocal, shared));\n safely(log, \"chmod pod-local credentials\", () => chmodSync(podLocal, 0o600));\n}\n\ninterface MountedWireOptions {\n home: string;\n userHomeMount: string;\n userId: string;\n projectId: string;\n sharedDir?: string;\n log: BootLogger;\n}\n\nfunction wireMountedUserHome(opts: MountedWireOptions): void {\n const { home, userHomeMount, userId, projectId, sharedDir, log } = opts;\n const root = join(userHomeMount, \"users\", userId, projectId);\n safely(log, \"mkdir user-home root\", () => {\n mkdirSync(join(root, \".claude\"), { recursive: true });\n mkdirSync(join(root, \".config\", \"claude\"), { recursive: true });\n });\n safely(log, \"wire ~/.claude symlink\", () => relink(join(root, \".claude\"), join(home, \".claude\")));\n safely(log, \"wire ~/.config/claude symlink\", () =>\n relink(join(root, \".config\", \"claude\"), join(home, \".config\", \"claude\")),\n );\n safely(log, \"seed pod-local ~/.claude.json\", () => {\n rmSync(join(home, \".claude.json\"), { recursive: true, force: true });\n seedClaudeJson(join(home, \".claude.json\"));\n });\n wireCredentials({ home, root, sharedDir, log });\n safely(log, \"mkdir opencode root\", () => {\n mkdirSync(join(root, \".local\", \"share\", \"opencode\"), { recursive: true });\n mkdirSync(join(root, \".config\", \"opencode\"), { recursive: true });\n });\n safely(log, \"wire opencode data symlink\", () =>\n relink(join(root, \".local\", \"share\", \"opencode\"), join(home, \".local\", \"share\", \"opencode\")),\n );\n safely(log, \"wire opencode config symlink\", () =>\n relink(join(root, \".config\", \"opencode\"), join(home, \".config\", \"opencode\")),\n );\n}\n\ninterface NoMountWireOptions {\n home: string;\n sharedDir?: string;\n log: BootLogger;\n}\n\nfunction wireNoMountUserHome(opts: NoMountWireOptions): void {\n const { home, sharedDir, log } = opts;\n // No persistent home — the CLI reads the pod-local config; seed it there so\n // non-FUSE pods get the same first-run gate suppression as the mounted path.\n safely(log, \"seed pod-local ~/.claude.json (no mount)\", () =>\n seedClaudeJson(join(home, \".claude.json\")),\n );\n if (sharedDir) {\n // Split-mode pods without a persistent user-home: ~/.claude must still\n // cross the container boundary (claude writes transcripts in the\n // workbench; the agent's tailer reads them), so it lives on the shared\n // emptyDir.\n safely(log, \"wire ~/.claude to shared emptyDir\", () => {\n const target = join(sharedDir, \"claude-home\", \".claude\");\n mkdirSync(target, { recursive: true });\n relink(target, join(home, \".claude\"));\n });\n }\n}\n\n/**\n * When the pod has the GCS FUSE user-home mount present with a resolvable\n * userId+projectId, symlink ~/.claude, ~/.config/claude, and the opencode\n * dirs into the user's persistent subdir, seed a pod-local ~/.claude.json,\n * and repoint the shared credentials file to a pod-local symlink target.\n * Otherwise fall back to a pod-local-only seed (plus a shared-emptyDir\n * ~/.claude symlink for split pods). Every sub-step degrades independently —\n * one failure never aborts the rest.\n */\nexport function wireUserHome(opts: WireUserHomeOptions): void {\n const { home, userHomeMount, userId, projectId, sharedDir, log } = opts;\n if (userId && projectId && isDir(userHomeMount)) {\n wireMountedUserHome({ home, userHomeMount, userId, projectId, sharedDir, log });\n } else {\n wireNoMountUserHome({ home, sharedDir, log });\n }\n}\n\n// ── reclaim_home_ownership ──────────────────────────────────────────────────\n\nconst RECLAIM_FIND_TIMEOUT_MS = 10_000;\n\n/**\n * Bake steps that run as root while ENV HOME points at the pod home can leave\n * root-owned entries under ~/.config / ~/.local (a uv install receipt once\n * bricked every pod boot fleet-wide). Reclaims them so a baked ownership slip\n * can't EACCES the entrypoint. CONDITIONAL, not a recursive chown: a\n * `find … ! -user conveyor` walk is stat-only when the tree is already clean\n * (the common case), so it triggers no overlayfs copy-up — an unconditional\n * `chown -R` here once cost ~16s of I/O-bound copy-up on every boot even when\n * nothing was root-owned.\n */\nexport async function reclaimHomeOwnership(\n home: string,\n exec: PreflightDeps[\"exec\"],\n log: BootLogger,\n): Promise<void> {\n for (const dir of [join(home, \".config\"), join(home, \".local\")]) {\n if (!isDir(dir)) continue;\n try {\n await exec(\n \"sudo\",\n [\n \"-n\",\n \"find\",\n dir,\n \"!\",\n \"-user\",\n \"conveyor\",\n \"-exec\",\n \"chown\",\n \"conveyor:conveyor\",\n \"{}\",\n \"+\",\n ],\n { timeoutMs: RECLAIM_FIND_TIMEOUT_MS },\n );\n } catch (err) {\n log.warn(\n `[boot] WARN reclaim_home_ownership failed for ${dir}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n}\n","import { chmodSync, closeSync, existsSync, mkdirSync, openSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { PreflightDeps } from \"./self-update.js\";\nimport type { BootLogger } from \"./types.js\";\n\n/**\n * Ports `start_workspace_sshd` (entrypoint.sh:12-31). Warn-only end to end —\n * SSH attach is a convenience surface; its absence must never degrade boot.\n *\n * Binds all interfaces deliberately: the preview-router tunnels in from\n * outside the pod to the pod IP. Access is gated by the attach token + SSH\n * public-key auth, not by network reachability (no public ingress).\n */\nexport async function startWorkspaceSshd(\n exec: PreflightDeps[\"exec\"],\n log: BootLogger,\n port: string = process.env.CONVEYOR_WORKSPACE_SSH_PORT || \"2222\",\n home = \"/home/conveyor\",\n): Promise<void> {\n try {\n const sshDir = join(home, \".ssh\");\n mkdirSync(sshDir, { recursive: true });\n chmodSync(sshDir, 0o700);\n const authorizedKeys = join(sshDir, \"authorized_keys\");\n if (!existsSync(authorizedKeys)) closeSync(openSync(authorizedKeys, \"a\"));\n chmodSync(authorizedKeys, 0o600);\n } catch (err) {\n log.warn(\n `[boot] WARN: workspace SSHD key setup failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n return;\n }\n if (!existsSync(\"/usr/sbin/sshd\")) {\n log.warn(\"[boot] WARN: workspace SSHD unavailable\");\n return;\n }\n try {\n await exec(\"sudo\", [\"mkdir\", \"-p\", \"/run/sshd\"], { timeoutMs: 10_000 });\n await exec(\"sudo\", [\"/usr/sbin/sshd\", \"-o\", \"ListenAddress=0.0.0.0\", \"-o\", `Port=${port}`], {\n timeoutMs: 10_000,\n });\n log.info(`[boot] Workspace SSHD listening on 0.0.0.0:${port}`);\n } catch {\n log.warn(\"[boot] WARN: workspace SSHD failed to start\");\n }\n}\n","import {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n rmSync,\n statSync,\n symlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { BootstrapBundle } from \"../setup/bootstrap-bundle-types.js\";\nimport { redactToken, type GitFn } from \"./git-prep.js\";\nimport { computeGraphifyEnv } from \"./graphify.js\";\nimport type { BootLogger } from \"./types.js\";\n\n/**\n * Ports the best-effort workspace extras that ride the git-ready gate:\n * - `bind_graphify_bundle` (entrypoint.sh:521-562)\n * - `ensure_grimoire_submodule` + `link_grimoire_skills` (entrypoint.sh:576-621)\n * - the reference-repos clone block (entrypoint.sh:793-827)\n *\n * Ordering invariant (mark_git_ready, entrypoint.sh:623-628): graphify bind +\n * grimoire submodule + skill links run BEFORE the ready flip (Claude must not\n * spawn before skills exist — fleet-wide 2026-07-10 incident); reference\n * repos run AFTER ready (supplementary context must never block Claude).\n * GitPrepJob's onReady/afterReady hooks carry that split.\n *\n * Every helper is best-effort: always returns cleanly, never fails the gate.\n */\n\nfunction errText(err: unknown): string {\n return redactToken(err instanceof Error ? err.message : String(err));\n}\n\nfunction isDir(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction isSymlink(path: string): boolean {\n try {\n return lstatSync(path).isSymbolicLink();\n } catch {\n return false;\n }\n}\n\nconst GRAPHIFY_BUNDLE_FILES = [\n \"graph.json\",\n \"GRAPH_REPORT.md\",\n \"manifest.json\",\n \".graphify_analysis.json\",\n \".graphify_labels.json\",\n \"publish-manifest.json\",\n \"cost.json\",\n];\n\n/** Symlink the shared graphify bundle into <workspace>/graphify-out.\n * `graphifyEnv` is computeGraphifyEnv's output — empty (disabled/no slug)\n * makes this a no-op. */\nexport function bindGraphifyBundle(\n workspace: string,\n graphifyEnv: Record<string, string>,\n log: BootLogger,\n): void {\n const slug = graphifyEnv.CONVEYOR_GRAPHIFY_SLUG;\n const sourceDir = graphifyEnv.CONVEYOR_GRAPHIFY_DIR;\n const graphFile = graphifyEnv.CONVEYOR_GRAPHIFY_GRAPH;\n if (!slug || !sourceDir || !graphFile) return;\n if (!existsSync(graphFile)) {\n log.info(`[boot] Graphify bundle not found for '${slug}' at ${sourceDir}`);\n return;\n }\n const targetDir = join(workspace, \"graphify-out\");\n try {\n mkdirSync(targetDir, { recursive: true });\n } catch {\n log.warn(`[boot] WARN: unable to create graphify-out at ${targetDir}`);\n return;\n }\n for (const rel of GRAPHIFY_BUNDLE_FILES) {\n const source = join(sourceDir, rel);\n const target = join(targetDir, rel);\n if (!existsSync(source)) continue;\n // -e || -L in bash: never clobber an existing entry, including a broken\n // symlink (existsSync follows links, hence the extra lstat check).\n if (existsSync(target) || isSymlink(target)) continue;\n try {\n symlinkSync(source, target);\n } catch {\n /* best-effort, bash `|| true` */\n }\n }\n log.info(`[boot] Bound graphify bundle '${slug}' into ${targetDir}`);\n}\n\n/** Materialize the .claude/grimoire submodule when the repo declares it and\n * the bake didn't populate it. Always returns cleanly. */\nexport async function ensureGrimoireSubmodule(\n workspace: string,\n githubToken: string,\n git: GitFn,\n log: BootLogger,\n): Promise<void> {\n try {\n if (!existsSync(join(workspace, \".gitmodules\"))) return;\n let paths = \"\";\n try {\n ({ stdout: paths } = await git(\n [\"config\", \"--file\", \".gitmodules\", \"--get-regexp\", String.raw`submodule\\..*\\.path`],\n { cwd: workspace, timeoutMs: 10_000 },\n ));\n } catch {\n // No submodule entries (config exits nonzero on no match).\n return;\n }\n if (!paths.split(\"\\n\").some((line) => line.trim().endsWith(\".claude/grimoire\"))) return;\n // Already materialized (pod-image bake ran conveyor-prebake successfully).\n if (isDir(join(workspace, \".claude\", \"grimoire\", \"skills\"))) return;\n if (!githubToken) {\n log.warn(\"[boot] WARN: grimoire submodule absent and no token to fetch it\");\n return;\n }\n // insteadOf injects the installation token for the submodule's https URL\n // the same way syncTaskBranchToRepo authenticates the main repo remote.\n try {\n await git(\n [\n \"-c\",\n `url.https://x-access-token:${githubToken}@github.com/.insteadOf=https://github.com/`,\n \"submodule\",\n \"update\",\n \"--init\",\n \".claude/grimoire\",\n ],\n { cwd: workspace, timeoutMs: 300_000 },\n );\n log.info(\"[boot] Initialized grimoire submodule\");\n } catch (err) {\n log.warn(\n `[boot] WARN: grimoire submodule init failed (baked pods may lack rc-* skills): ${errText(err)}`,\n );\n }\n } catch (err) {\n log.warn(`[boot] WARN: grimoire submodule check failed: ${errText(err)}`);\n }\n}\n\n/** Link grimoire skills into the repo's project skill dir (pod-local,\n * .gitignore-covered). Never clobbers a real (repo-tracked) skill dir. */\nexport function linkGrimoireSkills(workspace: string, log: BootLogger): void {\n const sourceDir = join(workspace, \".claude\", \"grimoire\", \"skills\");\n const targetDir = join(workspace, \".claude\", \"skills\");\n if (!isDir(sourceDir)) return;\n try {\n mkdirSync(targetDir, { recursive: true });\n } catch {\n log.warn(`[boot] WARN: unable to create ${targetDir}; skipping grimoire skill links`);\n return;\n }\n let linked = 0;\n let names: string[] = [];\n try {\n names = readdirSync(sourceDir);\n } catch {\n return;\n }\n for (const name of names) {\n if (!isDir(join(sourceDir, name))) continue;\n const target = join(targetDir, name);\n // bash: [ -e ] && [ ! -L ] — a broken symlink is replaceable, a real dir is not.\n if (existsSync(target) && !isSymlink(target)) {\n log.warn(`[boot] WARN: ${target} exists and is not a symlink; skipping`);\n continue;\n }\n try {\n // ln -sfn: replace an existing symlink in place.\n rmSync(target, { force: true });\n symlinkSync(join(\"..\", \"grimoire\", \"skills\", name), target);\n linked++;\n } catch {\n /* best-effort */\n }\n }\n log.info(`[boot] Linked ${linked} grimoire skills into ${targetDir}`);\n}\n\ninterface ReferenceRepo {\n slug: string;\n owner: string;\n name: string;\n branch: string;\n token: string;\n}\n\nfunction parseReferenceRepos(refsJson: string): ReferenceRepo[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(refsJson);\n } catch {\n // bash parity: jq parse errors were sent to /dev/null.\n return [];\n }\n if (!Array.isArray(parsed)) return [];\n const refs: ReferenceRepo[] = [];\n for (const entry of parsed) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const raw = entry as Record<string, unknown>;\n const str = (key: string): string => (typeof raw[key] === \"string\" ? (raw[key] as string) : \"\");\n const ref: ReferenceRepo = {\n slug: str(\"slug\"),\n owner: str(\"owner\"),\n name: str(\"name\"),\n branch: str(\"branch\") || \"main\",\n token: str(\"token\"),\n };\n if (ref.token && ref.slug && ref.owner && ref.name) refs.push(ref);\n }\n return refs;\n}\n\n/**\n * GitPrepJob.onReady production composition — the three pre-ready binds in\n * the pinned mark_git_ready order (graphify bind, grimoire submodule, skill\n * links). Best-effort like its parts; a throw here is downgraded to a warn by\n * GitPrepJob anyway.\n */\nexport async function runPreReadyBinds(\n workspace: string,\n bundle: BootstrapBundle,\n git: GitFn,\n log: BootLogger,\n): Promise<void> {\n const graphifyEnv = computeGraphifyEnv(process.env, bundle.gitPlan.repoName, isDir);\n bindGraphifyBundle(workspace, graphifyEnv, log);\n await ensureGrimoireSubmodule(workspace, bundle.githubToken, git, log);\n linkGrimoireSkills(workspace, log);\n}\n\n/**\n * Shallow-clone reference repos (from REFERENCE_REPOS_JSON in bundle.envVars)\n * as read-only context. Runs strictly AFTER the ready flip. Failures are\n * non-fatal — the task proceeds even when a reference project's GitHub App is\n * uninstalled or its token mint failed.\n */\nexport async function cloneReferenceRepos(\n refsJson: string | undefined,\n git: GitFn,\n log: BootLogger,\n referencesDir = \"/workspaces/references\",\n): Promise<void> {\n if (!refsJson) return;\n const refs = parseReferenceRepos(refsJson);\n if (refs.length === 0) return;\n try {\n mkdirSync(referencesDir, { recursive: true });\n } catch {\n return;\n }\n for (const ref of refs) {\n const dest = join(referencesDir, ref.slug);\n if (existsSync(join(dest, \".git\"))) continue;\n try {\n await git(\n [\n \"clone\",\n \"--depth\",\n \"1\",\n \"--single-branch\",\n \"--branch\",\n ref.branch,\n `https://x-access-token:${ref.token}@github.com/${ref.owner}/${ref.name}.git`,\n dest,\n ],\n { timeoutMs: 600_000 },\n );\n // Strip the token from the cloned remote so it never surfaces via\n // `git remote -v` when the agent inspects the reference repo.\n try {\n await git(\n [\"remote\", \"set-url\", \"origin\", `https://github.com/${ref.owner}/${ref.name}.git`],\n { cwd: dest, timeoutMs: 10_000 },\n );\n } catch {\n /* best-effort */\n }\n log.info(`[boot] cloned reference ${ref.slug} (${ref.owner}/${ref.name}@${ref.branch})`);\n } catch {\n log.warn(`[boot] WARN: reference clone failed: ${ref.slug}`);\n }\n }\n}\n","import type { BootstrapBundle } from \"../setup/bootstrap-bundle-types.js\";\nimport {\n startWorkbenchServer,\n type WorkbenchServerHandle,\n type WorkbenchServerOptions,\n} from \"../workbench/server.js\";\nimport { buildChildEnv, decodeJwtClaims } from \"./bundle.js\";\nimport { defaultGit, ensureDir, type GitPrepDeps } from \"./git-prep.js\";\nimport { pollBootstrapBundle } from \"./poll.js\";\nimport { defaultExec } from \"./self-update.js\";\nimport { startWorkspaceSshd } from \"./sshd.js\";\nimport type { BootContext } from \"./types.js\";\nimport { reclaimHomeOwnership, wireUserHome } from \"./user-home.js\";\nimport { cloneReferenceRepos, runPreReadyBinds } from \"./workspace-extras.js\";\n\n/**\n * The injectable step surface of `runWorkbenchBoot` (workbench-boot.ts) plus\n * its production defaults — split out so tests exercise the boot sequencing\n * without network, sockets, or real git, and so the composition module stays\n * within the dependency budget.\n */\nexport interface WorkbenchBootDeps {\n /** Task 4 poll — throws BootstrapUnauthorizedError on 401 (fatal), resolves\n * null when aborted (SIGTERM during standby). */\n pollBundle(ctx: BootContext): Promise<BootstrapBundle | null>;\n /** Task 3 unpack — claims decode + child env. */\n unpack(ctx: BootContext, bundle: BootstrapBundle): Record<string, string>;\n startDaemon(opts: WorkbenchServerOptions): Promise<WorkbenchServerHandle>;\n gitPrepDeps(ctx: BootContext, bundle: BootstrapBundle): GitPrepDeps;\n /** GitPrepJob.onReady — graphify bind + grimoire submodule + skill links. */\n bindsBeforeReady(ctx: BootContext, bundle: BootstrapBundle): Promise<void>;\n /** GitPrepJob.afterReady — reference repos, never blocking Claude. */\n cloneReferences(ctx: BootContext, bundle: BootstrapBundle): Promise<void>;\n /** mkdir -p /workspaces/repo before git prep — the daemon serves shell cwd\n * from it, and the no-git-plan prep path never creates it (bash did so\n * unconditionally, entrypoint.sh:848). */\n ensureWorkspaceDir(ctx: BootContext): void;\n wireHome(ctx: BootContext): void;\n startSshd(ctx: BootContext): Promise<void>;\n reclaimOwnership(ctx: BootContext): Promise<void>;\n /** Daemon lifetime — resolves never; SIGTERM → exit is Task 9's wiring. */\n park(ctx: BootContext): Promise<never>;\n}\n\nconst WORKSPACE_DIR = \"/workspaces/repo\";\nconst HOME_DIR = \"/home/conveyor\";\nconst USER_HOME_MOUNT = \"/mnt/conveyor-users\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport function defaultWorkbenchBootDeps(): WorkbenchBootDeps {\n return {\n pollBundle: (ctx) =>\n pollBootstrapBundle(ctx.apiUrl, ctx.bootstrapToken, {\n fetchFn: fetch,\n sleepFn: sleep,\n log: ctx.log,\n }),\n unpack: (ctx, bundle) => {\n const claims = decodeJwtClaims(bundle.sessionJwt);\n ctx.claims = claims;\n return buildChildEnv({\n base: process.env,\n bundle,\n claims,\n hostname: ctx.hostname,\n log: ctx.log,\n });\n },\n startDaemon: (opts) => startWorkbenchServer(opts),\n gitPrepDeps: (ctx, bundle) => ({\n git: defaultGit,\n bundle,\n podImage: process.env.CONVEYOR_POD_IMAGE === \"1\",\n log: ctx.log,\n }),\n bindsBeforeReady: (ctx, bundle) => runPreReadyBinds(WORKSPACE_DIR, bundle, defaultGit, ctx.log),\n cloneReferences: (ctx, bundle) =>\n cloneReferenceRepos(bundle.envVars?.REFERENCE_REPOS_JSON, defaultGit, ctx.log),\n ensureWorkspaceDir: (ctx) => {\n ensureDir(WORKSPACE_DIR);\n ctx.log.info(`[boot] ensured workspace dir ${WORKSPACE_DIR}`);\n },\n wireHome: (ctx) => {\n // Same id sources as the bash (entrypoint.sh:342-343, 388): userId from\n // the pod env / bundle envVars, projectId from the claim with a pod-env\n // fallback — all merged into childEnv by the unpack step.\n wireUserHome({\n home: HOME_DIR,\n userHomeMount: USER_HOME_MOUNT,\n userId: ctx.childEnv?.CONVEYOR_USER_ID,\n projectId: ctx.childEnv?.CONVEYOR_PROJECT_ID ?? ctx.childEnv?.PROJECT_ID,\n sharedDir: process.env.CONVEYOR_SHARED_DIR,\n log: ctx.log,\n });\n },\n startSshd: (ctx) => startWorkspaceSshd(defaultExec, ctx.log),\n reclaimOwnership: (ctx) => reclaimHomeOwnership(HOME_DIR, defaultExec, ctx.log),\n park: () =>\n new Promise<never>(() => {\n /* parked for the daemon's lifetime */\n }),\n };\n}\n","import { readAgentVersion } from \"../runner/session-runner-helpers.js\";\nimport { workbenchPort } from \"../workbench/mode.js\";\nimport { DEFAULT_WORKBENCH_PORT, type GitStatusFrame } from \"../workbench/protocol.js\";\nimport { GitPrepJob, type GitPrepState } from \"./git-prep.js\";\nimport type { BootContext } from \"./types.js\";\nimport { defaultWorkbenchBootDeps, type WorkbenchBootDeps } from \"./workbench-boot-deps.js\";\n\n/**\n * The workbench container's vnext boot: acquire the bundle, start the daemon\n * (whose listen socket IS the pod's startupProbe), then run everything else —\n * git prep, user-home wiring, sshd, ownership reclaim — strictly after the\n * probe can pass. Replaces the bash entrypoint's workbench tail\n * (entrypoint.sh:835-899): the marker files become the GitPrepJob state\n * served over the daemon's `gitStatus` op.\n *\n * Task 9 composes this through runPipeline; every step is injectable via the\n * deps object (`workbench-boot-deps.ts` carries the production defaults) so\n * tests run without network, sockets, or real git.\n */\n\nexport type { WorkbenchBootDeps } from \"./workbench-boot-deps.js\";\n\nexport function toGitStatusFrame(state: GitPrepState): GitStatusFrame {\n return state.state === \"failed\"\n ? { t: \"gitStatus\", state: \"failed\", reason: state.reason }\n : { t: \"gitStatus\", state: state.state };\n}\n\nfunction warnErr(ctx: BootContext, label: string): (err: unknown) => void {\n return (err) => {\n ctx.log.warn(\n `[boot] WARN ${label} failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n };\n}\n\nexport async function runWorkbenchBoot(\n ctx: BootContext,\n overrides: Partial<WorkbenchBootDeps> = {},\n): Promise<never> {\n const deps: WorkbenchBootDeps = { ...defaultWorkbenchBootDeps(), ...overrides };\n // ── CRITICAL PATH ──────────────────────────────────────────────────────\n // The daemon's listen socket is the pod's startupProbe — it gates both the\n // agent container start and Provisioning→Running. Nothing may run before\n // it besides bundle acquisition and env unpack.\n //\n // The poll's 401 (BootstrapUnauthorizedError) propagates — fatal-loud.\n const bundle = await deps.pollBundle(ctx);\n if (!bundle) {\n // Aborted during standby (SIGTERM). Task 9's signal wiring owns process\n // exit; parking preserves the Promise<never> contract meanwhile.\n ctx.log.info(\"[boot] standby poll aborted — parking for shutdown\");\n return deps.park(ctx);\n }\n ctx.bundle = bundle;\n ctx.childEnv = deps.unpack(ctx, bundle);\n const job = new GitPrepJob(deps.gitPrepDeps(ctx, bundle), {\n // Order pinned by the old entrypoint-grimoire test: binds BEFORE the\n // ready flip (Claude must not spawn before skills exist), references after.\n onReady: () => deps.bindsBeforeReady(ctx, bundle),\n afterReady: () => deps.cloneReferences(ctx, bundle),\n });\n await deps.startDaemon({\n port: workbenchPort() ?? DEFAULT_WORKBENCH_PORT,\n token: ctx.bootstrapToken,\n version: readAgentVersion() ?? \"unknown\",\n getGitStatus: () => toGitStatusFrame(job.status),\n });\n ctx.log.info(\"[boot] step=startDaemon listen socket up (startupProbe gate open)\");\n\n // ── DEFERRED (post-listen) ─────────────────────────────────────────────\n // Everything below runs after the probe can pass; each degrades alone.\n // Ensure the repo cwd exists BEFORE the git prep job — the no-git-plan path\n // returns ready without creating it, and the daemon serves shell cwd from here.\n try {\n deps.ensureWorkspaceDir(ctx);\n } catch (err) {\n warnErr(ctx, \"ensureWorkspaceDir\")(err);\n }\n job.start();\n try {\n deps.wireHome(ctx);\n } catch (err) {\n warnErr(ctx, \"wireUserHome\")(err);\n }\n void deps.startSshd(ctx).catch(warnErr(ctx, \"startWorkspaceSshd\"));\n void deps.reclaimOwnership(ctx).catch(warnErr(ctx, \"reclaimHomeOwnership\"));\n return deps.park(ctx);\n}\n"],"mappings":"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","names":["statSync","appendFileSync","appendFileSync","sleepUnlessAborted","writeFileSync","join","chmodSync","mkdirSync","join","existsSync","lstatSync","mkdirSync","rmSync","statSync","symlinkSync","join","isDir","statSync","lstatSync","existsSync","join","mkdirSync","symlinkSync","rmSync","USER_HOME_MOUNT","HOME_DIR","USER_HOME_MOUNT","statSync","whenAborted"]}
@@ -31,7 +31,7 @@ import {
31
31
  sleep,
32
32
  transcriptSize,
33
33
  turnOptionsFrom
34
- } from "./chunk-JG54UJ6V.js";
34
+ } from "./chunk-72AEN6LB.js";
35
35
 
36
36
  // src/setup/bootstrap.ts
37
37
  var BOOTSTRAP_TIMEOUT_MS = 3e4;
@@ -909,7 +909,8 @@ var AgentConnection = class _AgentConnection {
909
909
  });
910
910
  worker.unref();
911
911
  worker.on("error", (err) => {
912
- process.stderr.write(`[conveyor-agent] heartbeat worker error: ${err.message}
912
+ const message = err instanceof Error ? err.message : String(err);
913
+ process.stderr.write(`[conveyor-agent] heartbeat worker error: ${message}
913
914
  `);
914
915
  this.heartbeatWorker = null;
915
916
  });
@@ -2313,6 +2314,29 @@ var GetProjectOnboardingStepRequestSchema = z4.object({
2313
2314
  var GetProjectConnectUrlsRequestSchema = z4.object({
2314
2315
  projectId: z4.string()
2315
2316
  });
2317
+ var conveyorCapabilitySchema = z4.enum([
2318
+ "read",
2319
+ "create",
2320
+ "update",
2321
+ "chat",
2322
+ "files",
2323
+ "build"
2324
+ ]);
2325
+ var GetConnectionContextRequestSchema = z4.object({
2326
+ projectId: z4.string(),
2327
+ // Optional board scope (CONVEYOR_SUBPROJECT_ID). Validated to belong to the
2328
+ // project in the handler; an invalid/foreign id is reported, not silently
2329
+ // dropped, so a mis-scoped connection is never presented as board-specific.
2330
+ subProjectId: z4.string().nullable().optional()
2331
+ });
2332
+ var VerifyConnectionRequestSchema = z4.object({
2333
+ projectId: z4.string(),
2334
+ subProjectId: z4.string().nullable().optional(),
2335
+ intendedActions: z4.array(conveyorCapabilitySchema).optional()
2336
+ });
2337
+ var ListAccessibleSubprojectsRequestSchema = z4.object({
2338
+ projectId: z4.string()
2339
+ });
2316
2340
  var CreateProjectTaskRequestSchema = z4.object({
2317
2341
  projectId: z4.string(),
2318
2342
  title: z4.string().min(1),
@@ -6211,21 +6235,37 @@ function formatTagWithContextPaths(tag) {
6211
6235
  }
6212
6236
  return lines;
6213
6237
  }
6238
+ function buildEstimateReassessmentLines(context) {
6239
+ const currentSp = context.taskStoryPointValue ?? "unset";
6240
+ const currentRisk = context.taskRiskLevel ?? "unset";
6241
+ return [
6242
+ `Story points and risk are LIVING estimates, not one-time labels. Reassess them at every phase \u2014 planning, building, review \u2014 against your current understanding, and adjust in EITHER direction: work that looks big early is often small once scoped, and vice versa. Lowering an inflated estimate is as valuable as raising an underestimate; a stale value is worse than a changed one.`,
6243
+ `Current values: story points ${currentSp}, risk ${currentRisk}.`,
6244
+ ``,
6245
+ `Risk levels (how much important surface the change touches):`,
6246
+ `- critical: foundational surface \u2014 auth, billing, data integrity, migrations`,
6247
+ `- high: important surface with broad blast radius`,
6248
+ `- medium: moderate, contained surface area`,
6249
+ `- low: small or isolated change`
6250
+ ];
6251
+ }
6214
6252
  function buildPropertyInstructions(context, runnerMode) {
6215
6253
  const isTask = runnerMode === "task";
6216
6254
  const parts = [];
6217
6255
  parts.push(
6218
6256
  ``,
6219
6257
  `### Proactive Property Management`,
6220
- `As you plan this task, proactively fill in task properties when you have enough context:`,
6221
- `- Use update_task_properties to set any combination of: title, story points, and tags`,
6258
+ `As you work this task, proactively keep task properties accurate:`,
6259
+ `- Use update_task_properties to set any combination of: title, story points, risk, and tags`,
6222
6260
  `- You can update all properties at once or just one at a time as needed`,
6223
6261
  `- Icons are assigned automatically during identification \u2014 do not set icons manually`,
6224
6262
  ``,
6225
- `Don't wait for the user to ask \u2014 fill these in naturally as the plan takes shape.`,
6226
- `If the user adjusts the plan significantly, update the properties to match.`
6263
+ ...buildEstimateReassessmentLines(context),
6264
+ ``,
6265
+ `Don't wait for the user to ask \u2014 keep these accurate as the work takes shape.`,
6266
+ `If scope changes materially at any point, update the properties to match.`
6227
6267
  );
6228
- if (!isTask && context.storyPoints && context.storyPoints.length > 0) {
6268
+ if (context.storyPoints && context.storyPoints.length > 0) {
6229
6269
  parts.push(``, `Available story point tiers:`);
6230
6270
  for (const sp of context.storyPoints) {
6231
6271
  const desc = sp.description ? ` \u2014 ${truncateDescription(sp.description, SP_DESC_MAX_CHARS)}` : "";
@@ -6260,7 +6300,7 @@ function buildPlanDocumentationSection(context) {
6260
6300
  ],
6261
6301
  `- Your goal is to BUILD the change, not to produce a plan. After the plan is posted, actually implement it: edit source files, make the change work, then verify. Do NOT stop, go idle, or open a PR the moment the plan exists.`,
6262
6302
  `- Your pull request MUST contain the actual code implementation. Never open a plan-only or empty-diff PR \u2014 a PR that just records the plan is never the goal of auto mode unless the task explicitly asks ONLY for a plan. (If the task genuinely needs no code changes, don't open a PR at all \u2014 finish per the section below.)`,
6263
- `- Identification auto-fills title, story points, and icon with quick AI guesses. After exploring, refine the title and story points with update_task_properties if they look like placeholders. Icons are automatic \u2014 never set them.`
6303
+ `- Identification auto-fills title, story points, and icon with quick AI guesses. After exploring, refine the title, story points, and risk with update_task_properties whenever they no longer match what the work actually is \u2014 adjust in either direction. Icons are automatic \u2014 never set them.`
6264
6304
  ];
6265
6305
  }
6266
6306
  function buildNoPrWhenNoCodeSection(baseBranch) {
@@ -6316,7 +6356,7 @@ function buildDiscoveryPrompt(context, runnerMode) {
6316
6356
  `1. Read the task description and chat history \u2014 respond to what's been discussed`,
6317
6357
  `2. Investigate the codebase using the methodology below \u2014 search first, read targeted files`,
6318
6358
  `3. Save a detailed plan via \`update_task_plan\``,
6319
- `4. Set story points, tags, and title via \`update_task_properties\` (icon is set automatically)`,
6359
+ `4. Set story points, risk, tags, and title via \`update_task_properties\` (icon is set automatically)`,
6320
6360
  `5. Discuss the plan with the team if they're engaged, incorporate feedback`,
6321
6361
  `6. THEN call ExitPlanMode \u2014 it is the LAST step, not the first`,
6322
6362
  ...buildExplorationMethodology(),
@@ -6325,7 +6365,7 @@ function buildDiscoveryPrompt(context, runnerMode) {
6325
6365
  `### Self-Identification Tools`,
6326
6366
  `Use these MCP tools to set your own task properties:`,
6327
6367
  `- \`update_task_plan\` \u2014 save your plan and description`,
6328
- `- \`update_task_properties\` \u2014 set title, story points, and tags (any combination)`,
6368
+ `- \`update_task_properties\` \u2014 set title, story points, risk, and tags (any combination)`,
6329
6369
  `Note: Icons are assigned automatically during identification after planning is complete.`,
6330
6370
  ``,
6331
6371
  `### Tags & Context`,
@@ -6362,7 +6402,7 @@ function buildDiscoveryPrompt(context, runnerMode) {
6362
6402
  ``,
6363
6403
  `### Completing Planning`,
6364
6404
  `Once ALL checklist items above are done, call the **ExitPlanMode** tool.`,
6365
- `- Required before ExitPlanMode will succeed: **plan** (via update_task_plan), **story points** (via update_task_properties), **title** (via update_task_properties)`,
6405
+ `- Required before ExitPlanMode will succeed: **plan** (via update_task_plan), **story points**, **risk**, and **title** (via update_task_properties)`,
6366
6406
  `- ExitPlanMode validates these properties and marks planning as complete`,
6367
6407
  `- It does NOT start building \u2014 the team controls when to switch to Build mode`,
6368
6408
  `- Do NOT call ExitPlanMode until you have thoroughly explored the codebase and saved a detailed plan`
@@ -6427,12 +6467,14 @@ function buildBuildingPrompt(context) {
6427
6467
  `1. \`bun run check\` \u2014 lint + typecheck (fast; run whenever you changed code)`,
6428
6468
  `2. \`bun run test:affected\` \u2014 runs only the tests your diff can affect (docs-only diffs run nothing; apps/api diffs run unit tests only since CI covers the int shards; shared/db diffs escalate to the full suite automatically)`,
6429
6469
  `Docs/markdown/.claude-only changes need NO local gates \u2014 open the PR and let CI validate.`,
6470
+ `3. Reassess story points and risk against the ACTUAL diff \u2014 the estimate was made at planning time; now you know what the work really was. If it proved bigger or smaller, or touches more or less sensitive surface than expected, correct them with update_task_properties (either direction) before opening the PR.`,
6430
6471
  `If a gate fails, fix it before opening the PR. Do NOT open PRs with known failing gates. Never run the full \`bun run test\` for a diff confined to one package.`,
6431
6472
  `For refactors: also run \`git diff ${context?.baseBranch ?? "dev"}..HEAD\` and confirm the public API surface (exports, function signatures) has no unintended breaking changes.`,
6432
6473
  ...buildNoPrWhenNoCodeSection(context?.baseBranch),
6433
6474
  ...context?.isAuto || !context?.plan?.trim() ? buildPlanDocumentationSection(context) : []
6434
6475
  ]
6435
6476
  ];
6477
+ if (context) parts.push(...buildPropertyInstructions(context));
6436
6478
  return parts.join("\n");
6437
6479
  }
6438
6480
  function buildModePrompt(agentMode, context, runnerMode) {
@@ -6493,7 +6535,8 @@ function buildReviewPrompt(context) {
6493
6535
  `2. Review completed children \u2014 check PRs, run tests if needed`,
6494
6536
  `3. Approve and merge passing PRs`,
6495
6537
  `4. Fire builds for children that are ready`,
6496
- `5. Create follow-up tasks for anything out of scope`
6538
+ `5. Create follow-up tasks for anything out of scope`,
6539
+ `6. As children complete, correct their story points with update_subtask (storyPointValue) when the actual work diverged from the estimate \u2014 either direction`
6497
6540
  );
6498
6541
  } else {
6499
6542
  parts.push(
@@ -6538,6 +6581,9 @@ function buildReviewPrompt(context) {
6538
6581
  `- \`low\`: small or isolated change`,
6539
6582
  `The task may already have a risk level set. If your review makes you disagree with it, set the level you believe is correct \u2014 you have the authority to override it in either direction.`,
6540
6583
  ``,
6584
+ `#### Story points (correct if wrong):`,
6585
+ `You can see the full final diff \u2014 if the story-point estimate no longer matches the actual size of the work, correct it with update_task_properties (storyPointValue), in either direction. The card should record what the work actually was, not what it looked like at planning time.`,
6586
+ ``,
6541
6587
  `### Previous Review Feedback`,
6542
6588
  `If previous review feedback is present in the chat history, verify those specific issues were addressed before raising new concerns.`,
6543
6589
  ``,
@@ -8959,6 +9005,8 @@ function collectMissingProps(taskProps) {
8959
9005
  if (!taskProps.storyPointId) missing.push("story points (use update_task_properties)");
8960
9006
  if (!taskProps.title || taskProps.title === "Untitled")
8961
9007
  missing.push("title (use update_task_properties)");
9008
+ if (!taskProps.riskLevel)
9009
+ missing.push("risk (use update_task_properties \u2014 critical/high/medium/low)");
8962
9010
  return missing;
8963
9011
  }
8964
9012
 
@@ -11181,6 +11229,7 @@ var SessionRunner = class _SessionRunner {
11181
11229
  }
11182
11230
  if (delivery === "prefill") {
11183
11231
  await this.setState("waiting_for_input");
11232
+ this.workspaceCommands?.notifyLoopReady();
11184
11233
  await this.callbacks.onEvent({ type: "execute_mode", mode: effectiveMode, delivery });
11185
11234
  if (this.pendingMessages.length > 0) {
11186
11235
  if (!this.stopped) await this.setState("idle");
@@ -11271,6 +11320,7 @@ var SessionRunner = class _SessionRunner {
11271
11320
  */
11272
11321
  async runPrefilledMessage(msg, effectiveMode) {
11273
11322
  await this.setState("waiting_for_input");
11323
+ this.workspaceCommands?.notifyLoopReady();
11274
11324
  await this.callbacks.onEvent({
11275
11325
  type: "execute_mode",
11276
11326
  mode: effectiveMode,
@@ -11555,6 +11605,7 @@ var SessionRunner = class _SessionRunner {
11555
11605
  return this.callbacks.onStatusChange(status);
11556
11606
  },
11557
11607
  onEvent: (event) => {
11608
+ this.workspaceCommands?.notifyLoopReady();
11558
11609
  if (event.type === "completed") {
11559
11610
  this.completedThisTurn = true;
11560
11611
  void this.connection.sendHeartbeat();
@@ -11810,4 +11861,4 @@ export {
11810
11861
  loadConveyorConfig,
11811
11862
  unshallowRepo
11812
11863
  };
11813
- //# sourceMappingURL=chunk-OW2TIDWZ.js.map
11864
+ //# sourceMappingURL=chunk-3GDXX3OX.js.map