toolroll 0.9.67 → 0.9.69

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.
Files changed (250) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/acp-client.js +1 -3
  3. package/dist/agent-board.d.ts +0 -1
  4. package/dist/agent-board.js +0 -1
  5. package/dist/agent-fence.d.ts +58 -0
  6. package/dist/agent-fence.js +214 -19
  7. package/dist/agentconfig.d.ts +1 -1
  8. package/dist/agentconfig.js +6 -2
  9. package/dist/batch-checks.js +14 -26
  10. package/dist/browser/workspace.css +1 -1
  11. package/dist/browser/workspace.js +31 -29
  12. package/dist/builder.d.ts +9 -0
  13. package/dist/builder.js +30 -27
  14. package/dist/canonical-json.d.ts +14 -0
  15. package/dist/canonical-json.js +36 -0
  16. package/dist/chat-actions.js +1 -2
  17. package/dist/chat-admission.d.ts +19 -0
  18. package/dist/chat-admission.js +22 -0
  19. package/dist/chat-channel.js +1 -1
  20. package/dist/chat-delivery.js +0 -1
  21. package/dist/chat-follow.d.ts +17 -0
  22. package/dist/chat-follow.js +32 -0
  23. package/dist/chat-messages.js +0 -1
  24. package/dist/chat-settings-page.d.ts +23 -0
  25. package/dist/chat-settings-page.js +41 -0
  26. package/dist/check-access.d.ts +54 -0
  27. package/dist/check-access.js +80 -0
  28. package/dist/claim.js +3 -6
  29. package/dist/claude-print.d.ts +12 -0
  30. package/dist/claude-print.js +34 -0
  31. package/dist/cli-http.d.ts +0 -7
  32. package/dist/cli-http.js +0 -21
  33. package/dist/cli.d.ts +3 -10
  34. package/dist/cli.js +15 -24
  35. package/dist/coding-provider.js +2 -1
  36. package/dist/coding-workspace.js +1 -1
  37. package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
  38. package/dist/connect-cli.js +331 -0
  39. package/dist/containment.d.ts +0 -2
  40. package/dist/containment.js +2 -6
  41. package/dist/contracts/chat-actions.d.ts +0 -1
  42. package/dist/contracts/chat-content.d.ts +0 -4
  43. package/dist/contracts/chat-content.js +0 -2
  44. package/dist/contracts/chat-tables.d.ts +0 -1
  45. package/dist/contracts/cli.d.ts +0 -62
  46. package/dist/contracts/cli.js +0 -4
  47. package/dist/contracts/coding-workspace.js +1 -1
  48. package/dist/contracts/console-api.d.ts +1 -0
  49. package/dist/contracts/console-api.js +1 -0
  50. package/dist/contracts/contract.js +1 -1
  51. package/dist/contracts/flow-card.d.ts +0 -1
  52. package/dist/contracts/flow-propose.d.ts +0 -1
  53. package/dist/contracts/flow-send.js +1 -1
  54. package/dist/contracts/gateway-tools.d.ts +0 -1
  55. package/dist/contracts/integration-metadata.js +1 -1
  56. package/dist/contracts/lead-tools.d.ts +0 -1
  57. package/dist/contracts/memory-pass.d.ts +0 -1
  58. package/dist/contracts/memory-pass.js +1 -1
  59. package/dist/contracts/plan.js +1 -1
  60. package/dist/contracts/project-knowledge.js +1 -1
  61. package/dist/contracts/project-skills.d.ts +0 -1
  62. package/dist/contracts/proof.js +1 -1
  63. package/dist/contracts/recipes.js +1 -1
  64. package/dist/contracts/scope.js +1 -1
  65. package/dist/contracts/scout-report.js +1 -1
  66. package/dist/contracts/teams-callback.d.ts +0 -1
  67. package/dist/converse.d.ts +0 -6
  68. package/dist/converse.js +0 -19
  69. package/dist/daemon.js +3 -2
  70. package/dist/decision.d.ts +1 -3
  71. package/dist/deprecations.d.ts +12 -4
  72. package/dist/deprecations.js +27 -4
  73. package/dist/desktop-update-recovery.js +1 -1
  74. package/dist/desktop-update.js +3 -2
  75. package/dist/discord-chat.js +4 -15
  76. package/dist/discord-settings.d.ts +2 -6
  77. package/dist/discord-settings.js +10 -38
  78. package/dist/discord.js +12 -23
  79. package/dist/evidence.d.ts +0 -1
  80. package/dist/evidence.js +0 -3
  81. package/dist/exec.d.ts +0 -9
  82. package/dist/exec.js +3 -81
  83. package/dist/flow-actions.js +0 -3
  84. package/dist/flow-draft.d.ts +1 -3
  85. package/dist/flow-draft.js +15 -28
  86. package/dist/flow-scripts.d.ts +1 -8
  87. package/dist/flow-scripts.js +0 -13
  88. package/dist/flow-send.js +1 -1
  89. package/dist/flow-share.d.ts +0 -2
  90. package/dist/flow-share.js +1 -6
  91. package/dist/flow-steps.js +1 -1
  92. package/dist/flow-triggers.d.ts +0 -1
  93. package/dist/flow-triggers.js +1 -2
  94. package/dist/flow-unattended.js +1 -12
  95. package/dist/flows.js +1 -1
  96. package/dist/health-cli.d.ts +2 -2
  97. package/dist/health-cli.js +1 -1
  98. package/dist/invoke.d.ts +39 -5
  99. package/dist/invoke.js +181 -24
  100. package/dist/is-record.d.ts +2 -0
  101. package/dist/is-record.js +2 -0
  102. package/dist/lead-doors.d.ts +1 -5
  103. package/dist/lead-doors.js +0 -4
  104. package/dist/lead-tools.js +3 -6
  105. package/dist/live-detail.d.ts +0 -1
  106. package/dist/live-detail.js +0 -6
  107. package/dist/live-terminal.js +2 -1
  108. package/dist/live.d.ts +0 -1
  109. package/dist/live.js +1 -2
  110. package/dist/mcp-core.js +0 -2
  111. package/dist/memory-pass.js +1 -1
  112. package/dist/model-catalog.js +1 -1
  113. package/dist/modes.js +1 -12
  114. package/dist/operate.d.ts +4 -0
  115. package/dist/operate.js +456 -750
  116. package/dist/password-bearer.d.ts +0 -2
  117. package/dist/password-bearer.js +0 -4
  118. package/dist/peek-cli.js +2 -1
  119. package/dist/peek.d.ts +2 -26
  120. package/dist/peek.js +2 -168
  121. package/dist/phase-routing.d.ts +0 -4
  122. package/dist/phase-routing.js +2 -21
  123. package/dist/planner-source.d.ts +2 -1
  124. package/dist/planner-source.js +2 -12
  125. package/dist/planner.js +2 -14
  126. package/dist/process-tree.d.ts +0 -2
  127. package/dist/process-tree.js +0 -10
  128. package/dist/project-delete-ui.d.ts +16 -0
  129. package/dist/project-delete-ui.js +60 -1
  130. package/dist/project-delete.d.ts +0 -2
  131. package/dist/project-delete.js +1 -3
  132. package/dist/project-learning.d.ts +0 -14
  133. package/dist/project-skills.d.ts +0 -1
  134. package/dist/project-skills.js +0 -14
  135. package/dist/project-tools.js +2 -10
  136. package/dist/provider.d.ts +0 -17
  137. package/dist/provider.js +0 -26
  138. package/dist/pulls.d.ts +0 -2
  139. package/dist/pulls.js +0 -9
  140. package/dist/release-adapters.d.ts +3 -0
  141. package/dist/release-adapters.js +15 -5
  142. package/dist/remote-exec.d.ts +3 -2
  143. package/dist/remote-exec.js +3 -2
  144. package/dist/repos.d.ts +0 -5
  145. package/dist/repos.js +2 -10
  146. package/dist/repository-context.d.ts +0 -2
  147. package/dist/repository-context.js +0 -4
  148. package/dist/restart-certification.d.ts +0 -2
  149. package/dist/restart-certification.js +1 -5
  150. package/dist/result-follow-ups.d.ts +1 -4
  151. package/dist/result-follow-ups.js +11 -21
  152. package/dist/result-review.d.ts +0 -2
  153. package/dist/result-review.js +0 -2
  154. package/dist/retention.d.ts +0 -1
  155. package/dist/retention.js +0 -1
  156. package/dist/review-context.d.ts +0 -1
  157. package/dist/review-context.js +1 -12
  158. package/dist/reviewer.d.ts +0 -16
  159. package/dist/reviewer.js +0 -18
  160. package/dist/run-closeout.js +2 -1
  161. package/dist/scope.d.ts +9 -0
  162. package/dist/scope.js +13 -18
  163. package/dist/scout-browser.js +2 -1
  164. package/dist/serve.js +11 -2
  165. package/dist/server/console.css +5 -133
  166. package/dist/server/flows.js +2 -2
  167. package/dist/server/http.d.ts +4 -0
  168. package/dist/server/http.js +5 -0
  169. package/dist/server/pages.js +2 -2
  170. package/dist/server/people-tokens.js +3 -4
  171. package/dist/server/remote.js +1 -6
  172. package/dist/server/render-chat.d.ts +9 -2
  173. package/dist/server/render-chat.js +53 -7
  174. package/dist/server/render-results.d.ts +3 -6
  175. package/dist/server/render-results.js +4 -9
  176. package/dist/server/render-tasks.js +6 -6
  177. package/dist/server/route-table.d.ts +0 -4
  178. package/dist/server/route-table.js +1 -6
  179. package/dist/server/settings.js +60 -23
  180. package/dist/server/task-views.js +1 -1
  181. package/dist/server/tasks.js +5 -3
  182. package/dist/setup-guide.d.ts +2 -2
  183. package/dist/skills.d.ts +0 -6
  184. package/dist/skills.js +0 -8
  185. package/dist/slack-chat.d.ts +1 -7
  186. package/dist/slack-chat.js +5 -18
  187. package/dist/slack-settings.d.ts +2 -6
  188. package/dist/slack-settings.js +10 -38
  189. package/dist/slack.js +12 -23
  190. package/dist/sleep.d.ts +2 -0
  191. package/dist/sleep.js +2 -0
  192. package/dist/store.d.ts +29 -75
  193. package/dist/store.js +89 -180
  194. package/dist/structured-output.d.ts +0 -1
  195. package/dist/structured-output.js +0 -1
  196. package/dist/subagent-work.d.ts +0 -2
  197. package/dist/subagent-work.js +0 -2
  198. package/dist/subagents.d.ts +1 -3
  199. package/dist/subagents.js +20 -33
  200. package/dist/subscription-chat.d.ts +1 -3
  201. package/dist/subscription-chat.js +2 -3
  202. package/dist/surface.js +2 -1
  203. package/dist/task-sizing.d.ts +1 -4
  204. package/dist/task-sizing.js +3 -10
  205. package/dist/teams-chat.js +4 -11
  206. package/dist/teams.js +12 -19
  207. package/dist/telegram-lead.js +2 -2
  208. package/dist/telegram.js +1 -1
  209. package/dist/token-notices.d.ts +0 -11
  210. package/dist/token-notices.js +1 -5
  211. package/dist/tool-secret-store.d.ts +0 -3
  212. package/dist/tool-secret-store.js +2 -11
  213. package/dist/toolroll-update.d.ts +0 -2
  214. package/dist/toolroll-update.js +2 -2
  215. package/dist/verification-evidence.d.ts +0 -1
  216. package/dist/verification-evidence.js +0 -1
  217. package/dist/webhooks.d.ts +0 -1
  218. package/dist/webhooks.js +0 -1
  219. package/dist/workspace-ui.d.ts +0 -8
  220. package/dist/workspace-ui.js +1 -6
  221. package/package.json +1 -1
  222. package/dist/contracts/native-census.d.ts +0 -42
  223. package/dist/contracts/native-census.js +0 -36
  224. package/dist/process-recovery-anchor.d.ts +0 -75
  225. package/dist/process-recovery-anchor.js +0 -188
  226. package/dist/process-recovery-coalition.d.ts +0 -25
  227. package/dist/process-recovery-coalition.js +0 -105
  228. package/dist/process-recovery-eligibility.d.ts +0 -179
  229. package/dist/process-recovery-eligibility.js +0 -174
  230. package/dist/process-recovery-native.d.ts +0 -161
  231. package/dist/process-recovery-native.js +0 -595
  232. package/dist/process-recovery-provenance.d.ts +0 -163
  233. package/dist/process-recovery-provenance.js +0 -292
  234. package/dist/process-recovery-services.d.ts +0 -56
  235. package/dist/process-recovery-services.js +0 -191
  236. package/dist/process-recovery-settlement.d.ts +0 -32
  237. package/dist/process-recovery-settlement.js +0 -133
  238. package/dist/process-recovery.d.ts +0 -41
  239. package/dist/process-recovery.js +0 -91
  240. package/dist/session-brief.d.ts +0 -8
  241. package/dist/session-brief.js +0 -24
  242. package/dist/session-cli.d.ts +0 -42
  243. package/dist/session-cli.js +0 -271
  244. package/dist/session-contract.d.ts +0 -183
  245. package/dist/session-contract.js +0 -122
  246. package/dist/session-remote.d.ts +0 -19
  247. package/dist/session-remote.js +0 -86
  248. package/dist/session-service.d.ts +0 -22
  249. package/dist/session-service.js +0 -157
  250. package/dist/team-cli.js +0 -692
package/dist/invoke.d.ts CHANGED
@@ -21,12 +21,11 @@ import { type VersionProbe } from "./attest.js";
21
21
  import type { Store } from "./store.js";
22
22
  import type { RunOptions } from "./exec.js";
23
23
  import type { Runner } from "./worktree.js";
24
+ import { type NetworkFence, type SignInGrants } from "./agent-fence.js";
25
+ import { type CheckAccess } from "./check-access.js";
24
26
  import { type WritesMode } from "./agent-fence.js";
25
27
  import { type NetworkProfile } from "./egress.js";
26
28
  export type { ProviderRunner } from "./provider.js";
27
- /** The claude binary name — kept for the legacy quota rows and tests that
28
- * describe history; new code carries a resolved AgentSpec instead. */
29
- export declare const PROVIDER_BINARY = "claude";
30
29
  export type ProviderUsage = {
31
30
  tokensIn: number | null;
32
31
  tokensOut: number | null;
@@ -120,11 +119,46 @@ export declare function invokeAgent(store: Store, runId: number, spec: AgentSpec
120
119
  * Their network stays as it was. A machine without an OS fence runs them as before; a fence that can't be prepared
121
120
  * (a checkout whose binding no longer proves) runs nothing.
122
121
  */
123
- export declare function fencedCommands(store: Store, runId: number, writes: WritesMode, inner: Runner, available?: () => boolean): Runner;
122
+ export declare function fencedCommands(store: Store, runId: number, writes: WritesMode, inner: Runner, available?: () => boolean, access?: CheckAccess, network?: NetworkProfile): Runner;
124
123
  /**
125
124
  * A project's checks run in a checkout of a result's commit made for them (result-follow-ups.ts, batch-checks.ts), in
126
125
  * the same fence as a build's commands: Toolroll's state, credentials, other projects and runs denied; under the
127
126
  * project's writes setting, only that checkout, its Git data, its own temp folder, the project's caches and its check
128
127
  * database writable. Where nothing can confine them, they run only when an instance operator allowed unconfined agents.
129
128
  */
130
- export declare function fencedCheckoutCommands(store: Store, repo: string, checkout: string, inner: Runner, available?: () => boolean): Runner;
129
+ export declare function fencedCheckoutCommands(store: Store, repo: string, checkout: string, inner: Runner, available?: () => boolean, access?: CheckAccess, network?: NetworkProfile): Runner;
130
+ /**
131
+ * A `sign-ins` check's launch (check-access.ts): its exact sign-in files and its own transcript folder
132
+ * (agent-fence.ts signInGrants), Codex's config and sign-in copied into a home of the launch's own (private, gone with
133
+ * the launch; ~/.codex itself stays fenced), gh pointed at its own folder, and the network the check runs under:
134
+ * open, or its allowlist through a proxy of its own (on Linux, carried in by a bridge). Nothing about it is started
135
+ * unless all of it is.
136
+ */
137
+ export declare function signInsLaunch(store: Store, own: string, temp: string, checkout: string, policy: NetworkProfile, gitPaths: readonly string[], platform?: NodeJS.Platform): Promise<{
138
+ grants: SignInGrants;
139
+ network: NetworkFence;
140
+ env: Record<string, string>;
141
+ close(): Promise<void>;
142
+ } | {
143
+ problem: string;
144
+ }>;
145
+ /** How a check at `access` actually runs here: unsandboxed where no OS fence exists (and an operator allowed that), else
146
+ * as asked. Recorded on every check's receipt, so a check that ran without a sandbox is never shown as sandboxed. */
147
+ export declare function actualCheckAccess(store: Store, access: CheckAccess): CheckAccess;
148
+ /**
149
+ * A check of saved results, in their own checkout (batch-checks.ts, result-follow-ups.ts), worked out right before each
150
+ * command: never wider than any of those results' own checks ran, nor than the project allows now; under sign-ins, the
151
+ * narrowest of their sealed check networks and the project's current one. A result with no sealed network runs it
152
+ * sandboxed.
153
+ */
154
+ export declare function checkoutLaunchOf(store: Store, repo: string, runIds: readonly number[], platform?: NodeJS.Platform): {
155
+ access: CheckAccess;
156
+ network: NetworkProfile | undefined;
157
+ };
158
+ /** The widest level a check's commands actually ran at (what its receipt says). */
159
+ export declare const widestRan: (levels: readonly CheckAccess[]) => CheckAccess;
160
+ /** A check log's line for how its commands ran, when wider than sandboxed (shown with the log). */
161
+ export declare const ranAtLine: (level: CheckAccess) => string | null;
162
+ /** A saved result's check command runner (batch-checks.ts, result-follow-ups.ts): its level and network worked out right
163
+ * now (checkoutLaunchOf), and how it actually runs told to `ran`. */
164
+ export declare function checkoutRunner(store: Store, repo: string, checkout: string, inner: Runner, runIds: readonly number[], ran: (level: CheckAccess) => void, platform?: NodeJS.Platform): Runner;
package/dist/invoke.js CHANGED
@@ -29,19 +29,17 @@ import { witnessedRunner } from "./process-custody.js";
29
29
  import { underStopWatch } from "./task-control.js";
30
30
  import { childDatabaseEnv, isolatedChildDatabase, removeChildDatabase as removeAgentDatabase } from "./child-database.js";
31
31
  import { RunSignInError, noToolsArgs, prepareRunTools } from "./project-tools.js";
32
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
32
+ import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
33
33
  import { homedir, tmpdir } from "node:os";
34
34
  import { dirname, join } from "node:path";
35
35
  import { poolRootOf } from "./worktree.js";
36
- import { BRIDGE_DIR } from "./agent-fence.js";
36
+ import { BRIDGE_DIR, checkLaunchRoot, readGrantedFile, SIGN_IN_COPIES_DIR, signInCopiesRoot, signInGrants } from "./agent-fence.js";
37
+ import { CHECK_ACCESS_DEFAULT, checkAccessHere, narrowestAccess, narrowestNetwork, signInsAvailable } from "./check-access.js";
37
38
  import { agentFence, buildCacheEnv, buildCacheFolder, grantFolder, writeAllowlist, WRITES_DEFAULT, CheckoutBindingError, trustedWorktreeBinding, checkoutGitEnv, HARDENED_GIT_ARGS, codexFenceArgv, fenceGrants, fencedGitEnv, linuxFenceAvailable, linuxFenced, macosFenceAvailable, macosFenced, NO_GRANTS, runFolders, taskBoundary } from "./agent-fence.js";
38
- import { proxyEnv, providerOnlyNetwork, startEgressProxy } from "./egress.js";
39
+ import { proxyEnv, providerOnlyNetwork, repositoryEcosystems, resolveNetwork, startEgressProxy } from "./egress.js";
39
40
  import { bridgeFits, startEgressBridge } from "./egress-bridge.js";
40
41
  import { claudeConfigDir, claudeDefaults, codexDefaults, codexHomeDir, isolatedCodexHome } from "./provider-home.js";
41
42
  import { billingOf, claudeBillingFrom, seenBilling } from "./spend.js";
42
- /** The claude binary name — kept for the legacy quota rows and tests that
43
- * describe history; new code carries a resolved AgentSpec instead. */
44
- export const PROVIDER_BINARY = "claude";
45
43
  /** "Within seconds": how soon a silent failed run must end for its stderr to
46
44
  * be read for the sign-in signal. */
47
45
  const AUTH_EARLY_EXIT_MS = 30_000;
@@ -64,6 +62,11 @@ function claudeSettingsAuthProblem(configDir) {
64
62
  catch (error) {
65
63
  if (error.code === "ENOENT")
66
64
  return null;
65
+ // A sandbox around the plane (a project check's fence, say) may hide the file while the CLI still signs in from its
66
+ // keychain. Isolated builds never load it, and a login that needed it fails within seconds and is reported as one.
67
+ const code = error.code;
68
+ if (code === "EPERM" || code === "EACCES")
69
+ return null;
67
70
  return "Can't read your Claude settings.json to check sign-in. Restore access to that file and try again. Isolated builds do not load it.";
68
71
  }
69
72
  let settings;
@@ -967,21 +970,27 @@ function runFence(store, runId, provider, keyHome, ownTools, writes = WRITES_DEF
967
970
  * Their network stays as it was. A machine without an OS fence runs them as before; a fence that can't be prepared
968
971
  * (a checkout whose binding no longer proves) runs nothing.
969
972
  */
970
- export function fencedCommands(store, runId, writes, inner, available = () => macosFenceAvailable() || linuxFenceAvailable()) {
973
+ export function fencedCommands(store, runId, writes, inner, available = () => macosFenceAvailable() || linuxFenceAvailable(), access = CHECK_ACCESS_DEFAULT, network) {
971
974
  // An injected runner (tests) starts no real command, as an injected agent starts no real agent (invokeAgent).
972
975
  if (inner !== runCommand)
973
976
  return inner;
974
977
  return async (file, args, options) => {
975
- // As a build itself: where nothing can confine them, they run only when an instance operator allowed unconfined agents.
978
+ // As a build itself: where nothing can confine them, they run only when an instance operator allowed unconfined agents
979
+ // (and the run says so: its checks ran without a sandbox, whatever was sealed).
976
980
  if (!available()) {
977
- if (store.unfencedAgentsAllowed())
981
+ if (store.unfencedAgentsAllowed()) {
982
+ store.stampRunCheckAccess(runId, "unsandboxed");
978
983
  return inner(file, args, options);
984
+ }
979
985
  return { code: 1, stdout: "", stderr: "This machine can't confine the build's commands (no macOS sandbox, no working bubblewrap), so they didn't run. Install bubblewrap, or an instance operator allows unconfined agents with `toolroll config unconfined-agents allow`.\n", timedOut: false, notFound: false };
980
986
  }
981
- const own = realpathSync(mkdtempSync(join(tmpdir(), "so-cmd-")));
987
+ const own = realpathSync(mkdtempSync(join(checkLaunchRoot(), "so-cmd-")));
982
988
  try {
983
989
  const temp = join(own, "tmp");
984
990
  mkdirSync(temp, { mode: 0o700 });
991
+ // Not sandboxed (check-access.ts): the project's checks are trusted by the whole instance, as before 0.9.66.
992
+ if (access === "unsandboxed")
993
+ return await inner(file, args, { ...options, env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp } });
985
994
  const fenced = runFence(store, runId, "command", undefined, own, writes);
986
995
  if (fenced.problem !== undefined)
987
996
  return { code: 1, stdout: "", stderr: `${fenced.problem}\n`, timedOut: false, notFound: false };
@@ -991,12 +1000,24 @@ export function fencedCommands(store, runId, writes, inner, available = () => ma
991
1000
  const database = options?.env?.["TOOLROLL_DB"];
992
1001
  const checkData = database === undefined || fenced.grants.writes === undefined ? null : grantFolder(dirname(database), false);
993
1002
  const grants = checkData === null ? fenced.grants : { ...fenced.grants, writes: { ...fenced.grants.writes, subpaths: [...fenced.grants.writes.subpaths, checkData] } };
994
- return await inner(file, args, {
995
- ...options,
996
- fence: fenced.fence,
997
- fenceGrants: grants,
998
- env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp, ...(fenced.cache === undefined || fenced.grants.writes === undefined ? {} : buildCacheEnv(fenced.cache)) },
999
- });
1003
+ const checkout = store.getRun(runId)?.worktree ?? null;
1004
+ const signIns = access !== "sign-ins" ? null : checkout === null ? { problem: "The check's checkout is unknown, so its sign-ins can't be granted." }
1005
+ : network === undefined ? { problem: "A check with sign-ins has no network sealed for it, so nothing ran." }
1006
+ : await signInsLaunch(store, own, temp, checkout, network, fenced.gitPaths ?? []);
1007
+ if (signIns !== null && "problem" in signIns)
1008
+ return { code: 1, stdout: "", stderr: `${signIns.problem}\n`, timedOut: false, notFound: false };
1009
+ try {
1010
+ return await inner(file, args, {
1011
+ ...options,
1012
+ fence: fenced.fence,
1013
+ fenceGrants: signIns === null ? grants : { ...grants, signIns: signIns.grants, network: signIns.network },
1014
+ env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp, ...(fenced.cache === undefined || fenced.grants.writes === undefined ? {} : buildCacheEnv(fenced.cache)), ...(signIns?.env ?? {}) },
1015
+ ...(signIns === null ? {} : { omitEnv: [...(options?.omitEnv ?? []), ...SIGN_INS_OMITTED_ENV] }),
1016
+ });
1017
+ }
1018
+ finally {
1019
+ await signIns?.close();
1020
+ }
1000
1021
  }
1001
1022
  finally {
1002
1023
  rmSync(own, { recursive: true, force: true });
@@ -1009,7 +1030,7 @@ export function fencedCommands(store, runId, writes, inner, available = () => ma
1009
1030
  * project's writes setting, only that checkout, its Git data, its own temp folder, the project's caches and its check
1010
1031
  * database writable. Where nothing can confine them, they run only when an instance operator allowed unconfined agents.
1011
1032
  */
1012
- export function fencedCheckoutCommands(store, repo, checkout, inner, available = () => macosFenceAvailable() || linuxFenceAvailable()) {
1033
+ export function fencedCheckoutCommands(store, repo, checkout, inner, available = () => macosFenceAvailable() || linuxFenceAvailable(), access = CHECK_ACCESS_DEFAULT, network) {
1013
1034
  if (inner !== runCommand)
1014
1035
  return inner;
1015
1036
  return async (file, args, options) => {
@@ -1021,10 +1042,13 @@ export function fencedCheckoutCommands(store, repo, checkout, inner, available =
1021
1042
  const databaseFile = store.databaseFile();
1022
1043
  if (databaseFile === null)
1023
1044
  return inner(file, args, options);
1024
- const own = realpathSync(mkdtempSync(join(tmpdir(), "so-cmd-")));
1045
+ const own = realpathSync(mkdtempSync(join(checkLaunchRoot(), "so-cmd-")));
1025
1046
  try {
1026
1047
  const temp = join(own, "tmp");
1027
1048
  mkdirSync(temp, { mode: 0o700 });
1049
+ // Not sandboxed (check-access.ts): the project's checks are trusted by the whole instance, as before 0.9.66.
1050
+ if (access === "unsandboxed")
1051
+ return await inner(file, args, { ...options, env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp } });
1028
1052
  let binding;
1029
1053
  try {
1030
1054
  binding = trustedWorktreeBinding(repo, checkout);
@@ -1042,12 +1066,24 @@ export function fencedCheckoutCommands(store, repo, checkout, inner, available =
1042
1066
  const base = fenceGrants({ databaseFile, worktree: checkout, boundary, runFolders: folders, linked: linkedFor(store, checkout), binding, poolRoots });
1043
1067
  const grants = allowed === null ? base
1044
1068
  : { ...base, readOnly: [...new Set([...base.readOnly, ...allowed.readOnly])].sort(), writes: { ...allowed.writes, subpaths: [...allowed.writes.subpaths, ...(checkData === null ? [] : [checkData])] } };
1045
- return await inner(file, args, {
1046
- ...options,
1047
- fence: agentFence({ databaseFile, worktree: checkout, boundary, binding, runFolders: folders, extra: allowed?.deny ?? [] }),
1048
- fenceGrants: grants,
1049
- env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp, ...(allowed === null ? {} : buildCacheEnv(cache)) },
1050
- });
1069
+ // With sign-ins, under the network its caller proved for it (the narrowest of its results' sealed check networks and
1070
+ // the project's current one): none given, nothing runs.
1071
+ const signIns = access !== "sign-ins" ? null : network === undefined ? { problem: "A check with sign-ins has no network sealed for it, so nothing ran." }
1072
+ : await signInsLaunch(store, own, temp, checkout, network, [binding.gitdir, join(binding.main, ".git")]);
1073
+ if (signIns !== null && "problem" in signIns)
1074
+ return { code: 1, stdout: "", stderr: `${signIns.problem}\n`, timedOut: false, notFound: false };
1075
+ try {
1076
+ return await inner(file, args, {
1077
+ ...options,
1078
+ fence: agentFence({ databaseFile, worktree: checkout, boundary, binding, runFolders: folders, extra: allowed?.deny ?? [] }),
1079
+ fenceGrants: signIns === null ? grants : { ...grants, signIns: signIns.grants, network: signIns.network },
1080
+ env: { ...(options?.env ?? {}), TMPDIR: temp, TMP: temp, TEMP: temp, ...(allowed === null ? {} : buildCacheEnv(cache)), ...(signIns?.env ?? {}) },
1081
+ ...(signIns === null ? {} : { omitEnv: [...(options?.omitEnv ?? []), ...SIGN_INS_OMITTED_ENV] }),
1082
+ });
1083
+ }
1084
+ finally {
1085
+ await signIns?.close();
1086
+ }
1051
1087
  }
1052
1088
  finally {
1053
1089
  rmSync(own, { recursive: true, force: true });
@@ -1116,3 +1152,124 @@ async function codexLoginBilling(binary, omitEnv) {
1116
1152
  function plainLines(stdout) {
1117
1153
  return stdout.split("\n").filter(line => !line.trimStart().startsWith("{")).join("\n");
1118
1154
  }
1155
+ /** What a `sign-ins` check never inherits: a caller's choice of Claude's config folder (Claude's keychain sign-in is
1156
+ * keyed to its default one; check-access.ts). gh's and Codex's are set to the granted ones instead. */
1157
+ const SIGN_INS_OMITTED_ENV = ["CLAUDE_CONFIG_DIR"];
1158
+ /**
1159
+ * A `sign-ins` check's launch (check-access.ts): its exact sign-in files and its own transcript folder
1160
+ * (agent-fence.ts signInGrants), Codex's config and sign-in copied into a home of the launch's own (private, gone with
1161
+ * the launch; ~/.codex itself stays fenced), gh pointed at its own folder, and the network the check runs under:
1162
+ * open, or its allowlist through a proxy of its own (on Linux, carried in by a bridge). Nothing about it is started
1163
+ * unless all of it is.
1164
+ */
1165
+ export async function signInsLaunch(store, own, temp, checkout, policy, gitPaths, platform = process.platform) {
1166
+ // The belt (check-access.ts signInsAvailable, off for now): no sign-in file is read, copied or granted, whatever level
1167
+ // reached this far. Its callers already run such a check sandboxed; anything else that asks runs nothing.
1168
+ if (!signInsAvailable(platform))
1169
+ return { problem: "Sign-ins can't be shared with checks yet, so nothing ran." };
1170
+ const home = homedir();
1171
+ const grants = signInGrants(home, checkout);
1172
+ const cleanup = [];
1173
+ // Codex's files are copied, each read only as the real file it is (readGrantedFile: no link, here or above it).
1174
+ const codex = join(own, "codex");
1175
+ mkdirSync(codex, { mode: 0o700 });
1176
+ for (const name of ["config.toml", "auth.json"]) {
1177
+ const bytes = readGrantedFile(join(realpathSync(home), ".codex", name));
1178
+ if (bytes !== null)
1179
+ writeFileSync(join(codex, name), bytes, { mode: 0o600 });
1180
+ }
1181
+ // On Linux every granted file is bound from a copy of its own, made the same way (bubblewrap binds a source path, and
1182
+ // a link swapped in after validation must never be what it binds). A Mac's sandbox checks the real path itself.
1183
+ // Not reached for now: the belt above refuses until reading these files is race-free.
1184
+ if (process.platform === "linux") {
1185
+ const copies = {};
1186
+ // Under Toolroll's own state folder, which every fence denies whole (a launch's folder in a shared /tmp isn't masked
1187
+ // from a launch already running): bubblewrap binds from the original root, so only this check sees them.
1188
+ const database = store.databaseFile();
1189
+ if (database === null || database === "" || database === ":memory:")
1190
+ return { problem: "A check with sign-ins needs Toolroll's state folder, so nothing ran." };
1191
+ const state = dirname(realpathSync(database));
1192
+ try {
1193
+ mkdirSync(join(state, SIGN_IN_COPIES_DIR), { mode: 0o700 });
1194
+ }
1195
+ catch { /* there already: checked next */ }
1196
+ // Only the real, private folder Toolroll made (this user's, 0700, no link): never a link planted where it should be.
1197
+ const parent = signInCopiesRoot(state);
1198
+ if (parent === null)
1199
+ return { problem: "The sign-in copies folder isn't a private folder of Toolroll's, so nothing ran." };
1200
+ const folder = mkdtempSync(join(parent, `c-${process.pid}-`));
1201
+ cleanup.push(folder);
1202
+ grants.read.forEach((path, index) => {
1203
+ const bytes = readGrantedFile(path);
1204
+ if (bytes === null)
1205
+ return;
1206
+ const copy = join(folder, String(index));
1207
+ writeFileSync(copy, bytes, { mode: 0o600 });
1208
+ copies[path] = copy;
1209
+ });
1210
+ grants.read = grants.read.filter(path => copies[path] !== undefined);
1211
+ grants.copies = copies;
1212
+ }
1213
+ let egress = null;
1214
+ let bridge = null;
1215
+ const close = async () => { await egress?.close(); await bridge?.close(); for (const one of cleanup)
1216
+ rmSync(one, { recursive: true, force: true }); };
1217
+ try {
1218
+ const env = { CODEX_HOME: codex, GH_CONFIG_DIR: join(home, ".config", "gh"), XDG_CONFIG_HOME: join(home, ".config") };
1219
+ if (policy.mode === "open")
1220
+ return { grants, network: { mode: "open" }, env, close };
1221
+ // Exactly the project's network: the services the sign-ins are for are reached only if the operator allowed their
1222
+ // hosts (check-access.ts signInHostsMissing says which are missing; never added here).
1223
+ egress = await startEgressProxy({ hosts: policy.hosts });
1224
+ const network = { mode: "allowlist", proxyPort: egress.port, brokerPorts: [], loopback: policy.loopback, socketRoots: [temp, realpathSync(checkout)] };
1225
+ if (linuxFenceAvailable()) {
1226
+ const database = store.databaseFile();
1227
+ const fenced = database === null || database === "" || database === ":memory:" ? undefined : join(dirname(realpathSync(database)), BRIDGE_DIR);
1228
+ bridge = await startEgressBridge([egress.port], fenced !== undefined && bridgeFits(fenced) ? fenced : undefined);
1229
+ network.bridge = { folder: realpathSync(bridge.folder), forwarder: realpathSync(bridge.forwarder), node: process.execPath };
1230
+ // Its private /tmp gives back its checkout, its launch folder and its checkout's own Git data (as a build's does).
1231
+ network.keep = [realpathSync(checkout), own, ...gitPaths.filter(path => existsSync(path))];
1232
+ }
1233
+ return { grants, network, env: { ...env, ...proxyEnv(egress, policy.loopback) }, close };
1234
+ }
1235
+ catch (error) {
1236
+ await close();
1237
+ return { problem: `The check's network could not be set up, so nothing ran: ${error instanceof Error ? error.message : String(error)}` };
1238
+ }
1239
+ }
1240
+ /** How a check at `access` actually runs here: unsandboxed where no OS fence exists (and an operator allowed that), else
1241
+ * as asked. Recorded on every check's receipt, so a check that ran without a sandbox is never shown as sandboxed. */
1242
+ export function actualCheckAccess(store, access) {
1243
+ return macosFenceAvailable() || linuxFenceAvailable() || !store.unfencedAgentsAllowed() ? access : "unsandboxed";
1244
+ }
1245
+ /**
1246
+ * A check of saved results, in their own checkout (batch-checks.ts, result-follow-ups.ts), worked out right before each
1247
+ * command: never wider than any of those results' own checks ran, nor than the project allows now; under sign-ins, the
1248
+ * narrowest of their sealed check networks and the project's current one. A result with no sealed network runs it
1249
+ * sandboxed.
1250
+ */
1251
+ export function checkoutLaunchOf(store, repo, runIds, platform = process.platform) {
1252
+ // As checks can get it: sign-ins (off for now; a stamp or setting from before, or another database) run sandboxed.
1253
+ const access = checkAccessHere(narrowestAccess([store.projectCheckAccess(repo), ...runIds.map(id => store.runCheckAccess(id))]), platform);
1254
+ if (access !== "sign-ins")
1255
+ return { access, network: undefined };
1256
+ const sealed = runIds.map(id => store.runCheckNetwork(id));
1257
+ if (sealed.length === 0 || sealed.some(one => one === null))
1258
+ return { access: CHECK_ACCESS_DEFAULT, network: undefined };
1259
+ const live = resolveNetwork("command", store.projectNetwork(repo), repositoryEcosystems(repo));
1260
+ return { access, network: sealed.reduce((low, one) => narrowestNetwork(low, one), live) };
1261
+ }
1262
+ /** The widest level a check's commands actually ran at (what its receipt says). */
1263
+ export const widestRan = (levels) => levels.includes("unsandboxed") ? "unsandboxed" : levels.includes("sign-ins") ? "sign-ins" : CHECK_ACCESS_DEFAULT;
1264
+ /** A check log's line for how its commands ran, when wider than sandboxed (shown with the log). */
1265
+ export const ranAtLine = (level) => level === "sandboxed" ? null
1266
+ : `=== Check access ===\n${level === "unsandboxed" ? "These commands ran without a sandbox." : "These commands ran sandboxed, with model and GitHub sign-ins."}`;
1267
+ /** A saved result's check command runner (batch-checks.ts, result-follow-ups.ts): its level and network worked out right
1268
+ * now (checkoutLaunchOf), and how it actually runs told to `ran`. */
1269
+ export function checkoutRunner(store, repo, checkout, inner, runIds, ran, platform = process.platform) {
1270
+ return (file, args, options) => {
1271
+ const launch = checkoutLaunchOf(store, repo, runIds, platform);
1272
+ ran(actualCheckAccess(store, launch.access));
1273
+ return fencedCheckoutCommands(store, repo, checkout, inner, undefined, launch.access, launch.network)(file, args, options);
1274
+ };
1275
+ }
@@ -0,0 +1,2 @@
1
+ /** A plain object: not null, not an array. */
2
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
@@ -0,0 +1,2 @@
1
+ /** A plain object: not null, not an array. */
2
+ export const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1,5 +1,5 @@
1
1
  import { type SharedActionOptions } from './chat-actions.js';
2
- import { type CoordinatorProposal, type LeadProposal, type Store } from "./store.js";
2
+ import { type LeadProposal, type Store } from "./store.js";
3
3
  import type { VerifiedApprover } from "./principal.js";
4
4
  import { type TeamChatProviderResolver } from './team-chat-authorization.js';
5
5
  export type ProposalKind = LeadProposal["kind"];
@@ -78,7 +78,3 @@ export declare function dismissLeadProposal(store: Store, who: VerifiedApprover,
78
78
  */
79
79
  export declare function confirmCoordinatorProposal(store: Store, who: VerifiedApprover, proposalId: number, now: Date, options: DoorOptions): DoorOutcome;
80
80
  export declare function dismissCoordinatorProposal(store: Store, who: VerifiedApprover, proposalId: number, now: Date): boolean;
81
- /** Which door renders a coordinator proposal for this reader: the repo must be admitted. */
82
- export declare function coordinatorProposalVisible(who: {
83
- repos: readonly string[];
84
- }, proposal: CoordinatorProposal): boolean;
@@ -260,10 +260,6 @@ export function dismissCoordinatorProposal(store, who, proposalId, now) {
260
260
  return store.casCoordinatorProposal(proposalId, "pending", "dismissed", who.name, null, now);
261
261
  });
262
262
  }
263
- /** Which door renders a coordinator proposal for this reader: the repo must be admitted. */
264
- export function coordinatorProposalVisible(who, proposal) {
265
- return who.repos.includes(proposal.repo);
266
- }
267
263
  /**
268
264
  * The shared executor: one kind, one payload, the operator's own act
269
265
  * through the primitive that owns it. Module-private (v3 review, finding
@@ -1,5 +1,4 @@
1
1
  import { sweepLine, sweepTable } from "./lead-sweep.js";
2
- import { TEXT_LIMITS } from "./text-limits.js";
3
2
  import { connectionsOf, oneClickOf } from "./mcp-connect.js";
4
3
  import { checkLevelFromWords, isCheckLevel } from "./check-levels.js";
5
4
  import { policyParts } from "./policy.js";
@@ -39,9 +38,9 @@ import { CHAT_TASK_ACTIONS, chatTaskRun, chatTaskStamp, isChatTaskAction } from
39
38
  import { Buffer } from "node:buffer";
40
39
  import { LEAD_ASK_OTHER } from "./store.js";
41
40
  import { hasDisguisedText, hasForbiddenControls } from "./decision.js";
42
- import { readVerifiedArtifact, readVerifiedReport, scanForSecrets } from "./evidence.js";
41
+ import { readVerifiedArtifact, scanForSecrets } from "./evidence.js";
43
42
  import { parseAcceptanceCriteria } from "./scope.js";
44
- import { diagnoseTaskDispatch, withDispatchDiagnoses } from "./dispatch.js";
43
+ import { diagnoseTaskDispatch } from "./dispatch.js";
45
44
  import { agentChoicesFor, routeOfTask, INSTALLATION_SCOPE } from "./agentconfig.js";
46
45
  import { isNewModel, modelWords, priceWords, runtimeStates, seenModels } from "./model-catalog.js";
47
46
  import { agentsSummary, chosenWords, PHASES, postureWords, routeProblems, sameSpec, sizeSourceWords, sizeWords, specWords } from "./phase-routing.js";
@@ -55,7 +54,7 @@ import { planInChat } from "./chat-decide.js";
55
54
  import { flowDecisionAt } from "./telegram-flow.js";
56
55
  import { flowPersonOf } from "./flow-send.js";
57
56
  import { flowInsights } from "./flow-insights.js";
58
- import { describeTrigger, FLOW_TRIGGER_KINDS, triggerConfigOf } from "./flow-triggers.js";
57
+ import { describeTrigger, triggerConfigOf } from "./flow-triggers.js";
59
58
  export const LEAD_MAX_PROPOSALS_PER_TURN = 5;
60
59
  export { reportSummaryFor } from "./report-summary.js";
61
60
  import { reportSummaryFor } from "./report-summary.js";
@@ -63,8 +62,6 @@ import { labelOf, nameOf, summaryOf, zonesOf } from "./subagent-admin.js";
63
62
  import { SUBAGENT_TEMPLATES, withSection } from "./subagents.js";
64
63
  import { callOutcome, callWords, defaultRule, ruleWords } from "./subagent-tools.js";
65
64
  import { deskOf, routinesOf } from "./subagent-desk.js";
66
- import { KITS } from "./kits.js";
67
- import { STARTER_IDS } from "./flow-starters.js";
68
65
  import { undoFor, weekOf, weekWords } from "./subagent-week.js";
69
66
  import { PERSON_ID, personEntry, teamRoomOf } from "./lead-people.js";
70
67
  import { aboutYouOf, checkAboutYouLine } from "./lead-about.js";
@@ -10,7 +10,6 @@ export type LiveDetailSetting = {
10
10
  };
11
11
  /** Whether this project's live conversations show commands. Unknown or absent is off. */
12
12
  export declare function liveCommandsShown(store: Store, repo: string | null): boolean;
13
- export declare function liveDetailSetting(store: Store, repo: string): LiveDetailSetting | null;
14
13
  /** Save the setting and its ledger line in one transaction. The caller has proved who is asking (and, to turn it on, their password). */
15
14
  export declare function setLiveCommands(store: Store, change: {
16
15
  repo: string;
@@ -26,12 +26,6 @@ export function liveCommandsShown(store, repo) {
26
26
  return false;
27
27
  }
28
28
  }
29
- export function liveDetailSetting(store, repo) {
30
- const row = store.handle.prepare("SELECT repo, commands, updated_by, updated_at FROM live_detail_setting WHERE repo = ?").get(repo);
31
- if (row === undefined)
32
- return null;
33
- return { repo: String(row["repo"]), commands: Number(row["commands"]) === 1, updatedBy: String(row["updated_by"]), updatedAt: String(row["updated_at"]) };
34
- }
35
29
  /** Save the setting and its ledger line in one transaction. The caller has proved who is asking (and, to turn it on, their password). */
36
30
  export function setLiveCommands(store, change, now) {
37
31
  store.transact(() => {
@@ -13,6 +13,7 @@ import { existsSync, mkdirSync, readlinkSync, realpathSync, statSync } from "nod
13
13
  import { delimiter, isAbsolute, join } from "node:path";
14
14
  import { codingEnvironment } from "./coding-provider.js";
15
15
  import { teardownGit, refusal } from "./teardown-git.js";
16
+ import { sleep } from "./sleep.js";
16
17
  export const TERMINAL_PROGRAMS = ["claude", "codex", "shell"];
17
18
  /** At most this many running sessions per console, and this many kept at all (ended ones count until removed). */
18
19
  export const MAX_LIVE_TERMINALS = 8;
@@ -404,7 +405,7 @@ export class TerminalHost {
404
405
  }
405
406
  async sweep(session) {
406
407
  const grace = this.options.closeGraceMs ?? CLOSE_GRACE_MS;
407
- const pause = (ms) => new Promise(resolve => setTimeout(resolve, ms));
408
+ const pause = sleep;
408
409
  const leaderGone = () => session.pty === null;
409
410
  let readable = true;
410
411
  const members = () => {
package/dist/live.d.ts CHANGED
@@ -16,7 +16,6 @@
16
16
  import { type KeyState, type LiveActivityKind, type LiveRecord, type LiveRecorderOptions } from "./live-records.js";
17
17
  export declare const LIVE_LINE_CAP: number;
18
18
  export declare const LIVE_FILE_CAP: number;
19
- export declare const LIVE_CAP_MARKER = "[the live view stopped at its size cap \u2014 the run record continues]";
20
19
  export declare const LIVE_REDACTED_LINE = "[a credential-shaped line was redacted]";
21
20
  /** Finalized runs keep their live file this long for the final drain. */
22
21
  export declare const LIVE_RETAIN_MS: number;
package/dist/live.js CHANGED
@@ -16,10 +16,9 @@
16
16
  import { closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync, readSync, readdirSync, statSync, unlinkSync, writeSync } from "node:fs";
17
17
  import { join } from "node:path";
18
18
  import { scanForSecrets } from "./evidence.js";
19
- import { createLiveRecorder, LIVE_CAP_WORDS, parseLiveRecords, redactLiveLines, projectLiveText, serializeLiveRecord } from "./live-records.js";
19
+ import { createLiveRecorder, parseLiveRecords, redactLiveLines, projectLiveText, serializeLiveRecord } from "./live-records.js";
20
20
  export const LIVE_LINE_CAP = 2 * 1024;
21
21
  export const LIVE_FILE_CAP = 4 * 1024 * 1024;
22
- export const LIVE_CAP_MARKER = LIVE_CAP_WORDS;
23
22
  export const LIVE_REDACTED_LINE = "[a credential-shaped line was redacted]";
24
23
  /** Finalized runs keep their live file this long for the final drain. */
25
24
  export const LIVE_RETAIN_MS = 24 * 60 * 60 * 1000;
package/dist/mcp-core.js CHANGED
@@ -9,13 +9,11 @@
9
9
  * `tools/call` results with `isError: true`.
10
10
  */
11
11
  import { dirname, join } from 'node:path';
12
- import { statSync } from 'node:fs';
13
12
  import { repositoryContextRead } from './repository-context.js';
14
13
  import { ASSIGNMENT_TOOLS, assignmentForCoordinator } from "./assignment-adapters.js";
15
14
  import { parseContract, toModelSchema } from "./contracts/contract.js";
16
15
  import { GATEWAY_TOOL_INPUTS, GATEWAY_TOOL_OUTPUTS } from "./contracts/gateway-tools.js";
17
16
  import { reportToolOutput } from "./contracts/lead-tools.js";
18
- import { PACKAGE_VERSION } from "./version.js";
19
17
  import { authenticateCoordinator, fileCoordinatorProposal, statusFor, listTasksFor, taskDetailFor, } from "./coordinator.js";
20
18
  import { ISO_STAMP_RULE, decisionOver, decisionsOver, labelRepos, queueOver, recapOver } from "./lead-tools.js";
21
19
  import { proposeAsCoordinator } from "./coordinator-proposals.js";
@@ -13,7 +13,7 @@
13
13
  import { createHash } from 'node:crypto';
14
14
  import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
15
15
  import { homedir } from 'node:os';
16
- import { basename, join } from 'node:path';
16
+ import { join } from 'node:path';
17
17
  import { scanForSecrets, readVerifiedArtifact } from './evidence.js';
18
18
  import { changeKnowledge, knowledgeView } from './project-knowledge.js';
19
19
  import { listDecisions, recordDecision } from './project-memory.js';
@@ -14,7 +14,7 @@ import { delimiter, join } from "node:path";
14
14
  import { fetchOpenRouterModels } from "./openrouter-models.js";
15
15
  import { codexModelSchema, codexModelsCacheSchema, npmLatestSchema } from "./contracts/model-catalog.js";
16
16
  import { readCappedBody } from "./converse.js";
17
- import { validModelId } from "./provider.js";
17
+ import {} from "./provider.js";
18
18
  import { activeUpdateWork } from "./desktop-update-gate.js";
19
19
  export const MODELS_SCHEMA = `
20
20
  CREATE TABLE IF NOT EXISTS model_seen (
package/dist/modes.js CHANGED
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import { createHash } from "node:crypto";
17
17
  import { CHAT_PROVIDERS, isChatProvider } from "./contracts/chat-tables.js";
18
+ import { canonicalJson } from "./canonical-json.js";
18
19
  export const MODE_MAX_DAYS = 7;
19
20
  /** The presets are STARTING POINTS the ceremony renders in full — the
20
21
  * signature always covers the resolved terms, never a label. */
@@ -57,18 +58,6 @@ export function presetTerms(name, absoluteExpiry) {
57
58
  absoluteExpiry,
58
59
  };
59
60
  }
60
- function canonicalJson(value) {
61
- if (Array.isArray(value))
62
- return `[${value.map(canonicalJson).join(",")}]`;
63
- if (value !== null && typeof value === "object") {
64
- const entries = Object.entries(value)
65
- .filter(([, v]) => v !== undefined)
66
- .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
67
- .map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`);
68
- return `{${entries.join(",")}}`;
69
- }
70
- return JSON.stringify(value);
71
- }
72
61
  export function modeTermsJson(terms) {
73
62
  return canonicalJson(terms);
74
63
  }
package/dist/operate.d.ts CHANGED
@@ -67,6 +67,10 @@ export type OperateOptions = {
67
67
  flowStepIo?: Partial<StepIo>;
68
68
  /** Injected by tests: how often a worker's flow passes run beside its builds (default FLOW_EVERY_MS). */
69
69
  flowEveryMs?: number;
70
+ /** Injected by tests: the operating system this runs on (check access: no sign-ins on Linux yet). */
71
+ platform?: NodeJS.Platform;
72
+ /** Injected by tests: whether this machine has an OS fence for checks (macOS sandbox or bubblewrap). */
73
+ fenceAvailable?: () => boolean;
70
74
  /** Injected by tests: how `flows import` fetches a flow file's address. */
71
75
  flowFetch?: FetchLike;
72
76
  /** Injected by tests: how `integrations` checks reach services (fetch, gh, sign-in checks, mail servers). */