@intentic/sandbox-contract 1.333.1 → 1.335.0

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 (238) hide show
  1. package/README.md +8 -3
  2. package/dist/contracts/accounts.contract.d.ts +11 -11
  3. package/dist/contracts/activity.contract.d.ts +1 -0
  4. package/dist/contracts/activity.contract.d.ts.map +1 -1
  5. package/dist/contracts/activity.contract.js +1 -1
  6. package/dist/contracts/activity.contract.js.map +1 -1
  7. package/dist/contracts/agent.contract.d.ts +102 -18
  8. package/dist/contracts/agent.contract.d.ts.map +1 -1
  9. package/dist/contracts/agents.contract.d.ts +163 -0
  10. package/dist/contracts/agents.contract.d.ts.map +1 -1
  11. package/dist/contracts/agents.contract.js +10 -1
  12. package/dist/contracts/agents.contract.js.map +1 -1
  13. package/dist/contracts/audit.contract.d.ts +84 -0
  14. package/dist/contracts/audit.contract.d.ts.map +1 -0
  15. package/dist/contracts/audit.contract.js +43 -0
  16. package/dist/contracts/audit.contract.js.map +1 -0
  17. package/dist/contracts/automations.contract.d.ts +15 -0
  18. package/dist/contracts/automations.contract.d.ts.map +1 -1
  19. package/dist/contracts/ci.contract.d.ts +10 -0
  20. package/dist/contracts/ci.contract.d.ts.map +1 -1
  21. package/dist/contracts/compliance.contract.d.ts +163 -0
  22. package/dist/contracts/compliance.contract.d.ts.map +1 -0
  23. package/dist/contracts/compliance.contract.js +41 -0
  24. package/dist/contracts/compliance.contract.js.map +1 -0
  25. package/dist/contracts/device.contract.d.ts +6 -0
  26. package/dist/contracts/device.contract.d.ts.map +1 -1
  27. package/dist/contracts/extensions.contract.d.ts +2 -0
  28. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  29. package/dist/contracts/loops.contract.d.ts +15 -0
  30. package/dist/contracts/loops.contract.d.ts.map +1 -1
  31. package/dist/contracts/needs.contract.d.ts +49 -0
  32. package/dist/contracts/needs.contract.d.ts.map +1 -1
  33. package/dist/contracts/offload.contract.d.ts +5 -0
  34. package/dist/contracts/offload.contract.d.ts.map +1 -1
  35. package/dist/contracts/personas.contract.d.ts +10 -0
  36. package/dist/contracts/personas.contract.d.ts.map +1 -1
  37. package/dist/contracts/providers.contract.d.ts +2 -0
  38. package/dist/contracts/providers.contract.d.ts.map +1 -1
  39. package/dist/contracts/providers.contract.js +4 -0
  40. package/dist/contracts/providers.contract.js.map +1 -1
  41. package/dist/contracts/runner.contract.d.ts +42 -10
  42. package/dist/contracts/runner.contract.d.ts.map +1 -1
  43. package/dist/contracts/sessions.contract.d.ts +17 -0
  44. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  45. package/dist/contracts/settings.contract.d.ts +84 -0
  46. package/dist/contracts/settings.contract.d.ts.map +1 -1
  47. package/dist/contracts/settings.contract.js +7 -0
  48. package/dist/contracts/settings.contract.js.map +1 -1
  49. package/dist/contracts/system.contract.d.ts +16 -2
  50. package/dist/contracts/system.contract.d.ts.map +1 -1
  51. package/dist/contracts/workflows.contract.d.ts +30 -0
  52. package/dist/contracts/workflows.contract.d.ts.map +1 -1
  53. package/dist/contracts/workspace.contract.d.ts +3 -0
  54. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  55. package/dist/events/agent-events.d.ts +74 -0
  56. package/dist/events/agent-events.d.ts.map +1 -1
  57. package/dist/events/agent-events.js +1 -0
  58. package/dist/events/agent-events.js.map +1 -1
  59. package/dist/events/requests.d.ts +45 -0
  60. package/dist/events/requests.d.ts.map +1 -1
  61. package/dist/events/system-events.d.ts +12 -0
  62. package/dist/events/system-events.d.ts.map +1 -1
  63. package/dist/events/transcript.d.ts +117 -0
  64. package/dist/events/transcript.d.ts.map +1 -1
  65. package/dist/index.d.ts +828 -73
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +11 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/models/agent-catalog.d.ts +18 -3
  70. package/dist/models/agent-catalog.d.ts.map +1 -1
  71. package/dist/models/agent-catalog.js +61 -12
  72. package/dist/models/agent-catalog.js.map +1 -1
  73. package/dist/models/agent-runtimes.d.ts +5 -2
  74. package/dist/models/agent-runtimes.d.ts.map +1 -1
  75. package/dist/models/agent-runtimes.js +73 -40
  76. package/dist/models/agent-runtimes.js.map +1 -1
  77. package/dist/models/model-setups.d.ts +5 -0
  78. package/dist/models/model-setups.d.ts.map +1 -1
  79. package/dist/models/provider-specs.d.ts +153 -32
  80. package/dist/models/provider-specs.d.ts.map +1 -1
  81. package/dist/models/provider-specs.js +97 -11
  82. package/dist/models/provider-specs.js.map +1 -1
  83. package/dist/policy/reserved-servers.d.ts.map +1 -1
  84. package/dist/policy/reserved-servers.js +1 -0
  85. package/dist/policy/reserved-servers.js.map +1 -1
  86. package/dist/policy/sandbox-power.d.ts +7 -2
  87. package/dist/policy/sandbox-power.d.ts.map +1 -1
  88. package/dist/policy/sandbox-power.js +34 -1
  89. package/dist/policy/sandbox-power.js.map +1 -1
  90. package/dist/policy/turned-away.d.ts.map +1 -1
  91. package/dist/policy/turned-away.js +1 -0
  92. package/dist/policy/turned-away.js.map +1 -1
  93. package/dist/protocol/doors/listener-protocol.d.ts +43 -1
  94. package/dist/protocol/doors/listener-protocol.d.ts.map +1 -1
  95. package/dist/protocol/doors/listener-protocol.js +10 -0
  96. package/dist/protocol/doors/listener-protocol.js.map +1 -1
  97. package/dist/protocol/raw/raw-json-routes.d.ts +92 -0
  98. package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
  99. package/dist/protocol/raw/raw-json-routes.js +2 -1
  100. package/dist/protocol/raw/raw-json-routes.js.map +1 -1
  101. package/dist/protocol/raw/raw-routes.d.ts +13 -0
  102. package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
  103. package/dist/protocol/raw/raw-routes.js +3 -0
  104. package/dist/protocol/raw/raw-routes.js.map +1 -1
  105. package/dist/schemas/activity.d.ts +2 -0
  106. package/dist/schemas/activity.d.ts.map +1 -1
  107. package/dist/schemas/agent.d.ts +34 -0
  108. package/dist/schemas/agent.d.ts.map +1 -1
  109. package/dist/schemas/agent.js +7 -6
  110. package/dist/schemas/agent.js.map +1 -1
  111. package/dist/schemas/agents-list.d.ts +7 -0
  112. package/dist/schemas/agents-list.d.ts.map +1 -1
  113. package/dist/schemas/agents.d.ts +18 -0
  114. package/dist/schemas/agents.d.ts.map +1 -1
  115. package/dist/schemas/ai-literacy.d.ts +54 -0
  116. package/dist/schemas/ai-literacy.d.ts.map +1 -0
  117. package/dist/schemas/ai-literacy.js +15 -0
  118. package/dist/schemas/ai-literacy.js.map +1 -0
  119. package/dist/schemas/audit.d.ts +166 -0
  120. package/dist/schemas/audit.d.ts.map +1 -0
  121. package/dist/schemas/audit.js +100 -0
  122. package/dist/schemas/audit.js.map +1 -0
  123. package/dist/schemas/automations.d.ts +27 -0
  124. package/dist/schemas/automations.d.ts.map +1 -1
  125. package/dist/schemas/automations.js +11 -0
  126. package/dist/schemas/automations.js.map +1 -1
  127. package/dist/schemas/ci.d.ts +10 -0
  128. package/dist/schemas/ci.d.ts.map +1 -1
  129. package/dist/schemas/compliance.d.ts +423 -0
  130. package/dist/schemas/compliance.d.ts.map +1 -0
  131. package/dist/schemas/compliance.js +135 -0
  132. package/dist/schemas/compliance.js.map +1 -0
  133. package/dist/schemas/data-protection.d.ts +28 -0
  134. package/dist/schemas/data-protection.d.ts.map +1 -0
  135. package/dist/schemas/data-protection.js +49 -0
  136. package/dist/schemas/data-protection.js.map +1 -0
  137. package/dist/schemas/devices.d.ts +12 -0
  138. package/dist/schemas/devices.d.ts.map +1 -1
  139. package/dist/schemas/engines.d.ts +22 -1
  140. package/dist/schemas/engines.d.ts.map +1 -1
  141. package/dist/schemas/engines.js +1 -1
  142. package/dist/schemas/engines.js.map +1 -1
  143. package/dist/schemas/extension-updates.d.ts +4 -0
  144. package/dist/schemas/extension-updates.d.ts.map +1 -1
  145. package/dist/schemas/git/git.d.ts +33 -0
  146. package/dist/schemas/git/git.d.ts.map +1 -1
  147. package/dist/schemas/git/git.js +29 -0
  148. package/dist/schemas/git/git.js.map +1 -1
  149. package/dist/schemas/hosts.d.ts +36 -0
  150. package/dist/schemas/hosts.d.ts.map +1 -1
  151. package/dist/schemas/hosts.js +2 -0
  152. package/dist/schemas/hosts.js.map +1 -1
  153. package/dist/schemas/intentic-os.d.ts +117 -0
  154. package/dist/schemas/intentic-os.d.ts.map +1 -0
  155. package/dist/schemas/intentic-os.js +66 -0
  156. package/dist/schemas/intentic-os.js.map +1 -0
  157. package/dist/schemas/loops.d.ts +15 -0
  158. package/dist/schemas/loops.d.ts.map +1 -1
  159. package/dist/schemas/members.d.ts +90 -0
  160. package/dist/schemas/members.d.ts.map +1 -1
  161. package/dist/schemas/members.js +73 -3
  162. package/dist/schemas/members.js.map +1 -1
  163. package/dist/schemas/needs.d.ts +56 -0
  164. package/dist/schemas/needs.d.ts.map +1 -1
  165. package/dist/schemas/offload.d.ts +5 -0
  166. package/dist/schemas/offload.d.ts.map +1 -1
  167. package/dist/schemas/personas.d.ts +10 -0
  168. package/dist/schemas/personas.d.ts.map +1 -1
  169. package/dist/schemas/providers/plan-limits.d.ts +20 -0
  170. package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
  171. package/dist/schemas/providers/provider-oauth.js +1 -1
  172. package/dist/schemas/providers/provider-oauth.js.map +1 -1
  173. package/dist/schemas/providers/usage.d.ts +1 -0
  174. package/dist/schemas/providers/usage.d.ts.map +1 -1
  175. package/dist/schemas/providers/usage.js +4 -0
  176. package/dist/schemas/providers/usage.js.map +1 -1
  177. package/dist/schemas/settings.d.ts +48 -0
  178. package/dist/schemas/settings.d.ts.map +1 -1
  179. package/dist/schemas/settings.js +14 -0
  180. package/dist/schemas/settings.js.map +1 -1
  181. package/dist/schemas/system.d.ts +8 -0
  182. package/dist/schemas/system.d.ts.map +1 -1
  183. package/dist/schemas/system.js +2 -0
  184. package/dist/schemas/system.js.map +1 -1
  185. package/dist/schemas/workflows.d.ts +50 -0
  186. package/dist/schemas/workflows.d.ts.map +1 -1
  187. package/dist/schemas/workspace/workspace-tree.d.ts +4 -0
  188. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  189. package/dist/schemas/workspace/workspace-tree.js +16 -1
  190. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  191. package/dist/state/definition.d.ts +80 -4
  192. package/dist/state/definition.d.ts.map +1 -1
  193. package/dist/state/history-state.d.ts.map +1 -1
  194. package/dist/state/history-state.js +2 -0
  195. package/dist/state/history-state.js.map +1 -1
  196. package/package.json +5 -5
  197. package/src/contracts/activity.contract.ts +1 -1
  198. package/src/contracts/agents.contract.ts +13 -1
  199. package/src/contracts/audit.contract.ts +48 -0
  200. package/src/contracts/compliance.contract.ts +57 -0
  201. package/src/contracts/providers.contract.ts +9 -0
  202. package/src/contracts/settings.contract.ts +7 -0
  203. package/src/events/agent-events.ts +4 -0
  204. package/src/index.ts +11 -0
  205. package/src/models/agent-catalog.test.ts +317 -53
  206. package/src/models/agent-catalog.ts +115 -22
  207. package/src/models/agent-runtimes.ts +273 -75
  208. package/src/models/capability-ledger.test.ts +12 -1
  209. package/src/models/provider-specs.test.ts +42 -7
  210. package/src/models/provider-specs.ts +160 -20
  211. package/src/policy/reserved-servers.ts +2 -1
  212. package/src/policy/sandbox-power.ts +67 -8
  213. package/src/policy/tests/sandbox-power.test.ts +59 -1
  214. package/src/policy/turned-away.ts +2 -0
  215. package/src/protocol/doors/listener-protocol.ts +74 -6
  216. package/src/protocol/raw/raw-json-routes.ts +2 -1
  217. package/src/protocol/raw/raw-routes.ts +7 -0
  218. package/src/schemas/activity.ts +2 -2
  219. package/src/schemas/agent.ts +22 -9
  220. package/src/schemas/ai-literacy.ts +17 -0
  221. package/src/schemas/audit.ts +131 -0
  222. package/src/schemas/automations.ts +13 -0
  223. package/src/schemas/compliance.ts +195 -0
  224. package/src/schemas/daemon-health.test.ts +2 -0
  225. package/src/schemas/data-protection.ts +99 -0
  226. package/src/schemas/engines.ts +1 -1
  227. package/src/schemas/git/git.ts +46 -0
  228. package/src/schemas/hosts.ts +5 -0
  229. package/src/schemas/intentic-os.test.ts +86 -0
  230. package/src/schemas/intentic-os.ts +130 -0
  231. package/src/schemas/members.test.ts +47 -0
  232. package/src/schemas/members.ts +141 -6
  233. package/src/schemas/providers/provider-oauth.ts +1 -1
  234. package/src/schemas/providers/usage.ts +6 -0
  235. package/src/schemas/settings.ts +32 -0
  236. package/src/schemas/system.ts +2 -0
  237. package/src/schemas/workspace/workspace-tree.ts +26 -1
  238. package/src/state/history-state.ts +7 -0
@@ -1,5 +1,6 @@
1
1
  import type { ProviderBrand } from "@intentic/constants";
2
- import { type AgentCapabilities, CLAUDE_CODE, CODEX, CURSOR, DEVIN, OPENCODE, OPENCODE_GEMINI } from "./agent-runtimes.js";
2
+ import type { AgentLoop } from "../schemas/agent.js";
3
+ import { type AgentCapabilities, CLAUDE_CODE, CODEX, CURSOR, DEVIN, GROK_BUILD, KIMI_CLI, MUSE, OPENCODE, OPENCODE_GEMINI } from "./agent-runtimes.js";
3
4
 
4
5
  // Every fact about a provider more than one surface needs, one row each, replacing lists that drifted out of sync.
5
6
  // `access` (cost) and `auth` (connection) stay separate: conflating them mis-describes Z.ai, a prepaid plan spent
@@ -43,8 +44,47 @@ export interface MintedVariant {
43
44
  readonly flow: "device" | "redirect";
44
45
  // ANTHROPIC_BASE_URL for a turn minted here; no version segment, the harness appends /v1/messages itself.
45
46
  readonly anthropicBase: string;
46
- // Where this estate's catalog is read from: an OpenAI-compatible root, with its version segment.
47
+ // Where this estate's catalog is read from: an OpenAI-compatible root, with its version segment. Also the Chat
48
+ // Completions root a loop speaking that wire (OpenCode) sends its turns to.
47
49
  readonly catalogBase: string;
50
+ // The estate's OpenAI Responses root, with its version segment, for a loop that speaks only that wire (Codex); absent
51
+ // where the vendor publishes none, which keeps such a loop off this estate.
52
+ readonly responsesBase?: string;
53
+ }
54
+
55
+ // What a vendor's own terms say about one loop serving its subscription, when there is something worth saying. Shown
56
+ // quietly (an icon and its hover card, never a banner): the owner keeps these routes by choice, and the note is there so
57
+ // nobody runs one without being able to find out.
58
+ // - unlisted: the vendor names the clients it supports and this loop is not among them; nothing on record forbids it.
59
+ // - against-terms: the vendor's terms forbid it, and have been enforced against accounts.
60
+ export interface LoopTerms {
61
+ readonly status: "unlisted" | "against-terms";
62
+ // The vendor's position in one sentence, for the hover card.
63
+ readonly note: string;
64
+ // Where the vendor says it.
65
+ readonly source: string;
66
+ }
67
+
68
+ // One loop a provider can run on: what that pair can do, and what the vendor says about it when there is anything to say.
69
+ export interface LoopOffer {
70
+ readonly capabilities: AgentCapabilities;
71
+ // Absent where the vendor sanctions this loop or says nothing about it.
72
+ readonly terms?: LoopTerms;
73
+ // What a chip calls this loop when the provider's own name would not say which program runs it: the `vendor` loop of
74
+ // a provider whose other loops also carry its name (Grok Build beside Grok on OpenCode). Absent: loopLabel's own.
75
+ readonly label?: string;
76
+ // The variant the provider's own account door (/accounts/{provider}) signs this loop in under, for a loop whose
77
+ // credential is a sign-in of its own rather than the one the provider's other loops spend: a vendor CLI's login. An
78
+ // account the door lists carries the same word as its `variant`, which is how a picker knows which loop it serves.
79
+ // Absent: the loop spends the provider's default credential.
80
+ readonly door?: string;
81
+ }
82
+
83
+ // The loops a provider runs on. `default` is what `native` (and a turn naming no loop) resolves to, and must be one of
84
+ // `offered`; offering a provider a new loop is one more entry in `offered`, plus the adapter that runs it.
85
+ export interface ProviderLoops {
86
+ readonly default: AgentLoop;
87
+ readonly offered: Partial<Record<AgentLoop, LoopOffer>>;
48
88
  }
49
89
 
50
90
  export interface ProviderSpec {
@@ -63,10 +103,17 @@ export interface ProviderSpec {
63
103
  readonly auth: ProviderAuth;
64
104
  // Whether a plan-limit reading is obtainable, so a blank meter is told apart from a plan that publishes none.
65
105
  readonly planLimits: boolean;
66
- // The two runtimes this provider runs on; equal records here mean the harness is not a real choice for it.
67
- readonly runtimes: { readonly native: AgentCapabilities; readonly claudeCode: AgentCapabilities };
106
+ // The loops it runs on; one offered loop means the loop is not a choice for it.
107
+ readonly loops: ProviderLoops;
68
108
  }
69
109
 
110
+ // Kimi's word on the two loops it documents for an API key, reached here on the translator's sign-in.
111
+ const KIMI_TRANSLATED: LoopTerms = {
112
+ status: "unlisted",
113
+ note: "Kimi documents OpenCode and Codex with a Kimi Code API key. Here they reach your plan through a translator's sign-in instead, which Kimi doesn't list.",
114
+ source: "https://www.kimi.com/en/help/kimi-code/third-party-agents",
115
+ };
116
+
70
117
  // satisfies checks each row's shape; as const keeps ids/auth kinds literal, so NativeProvider stays a union.
71
118
  export const PROVIDER_SPECS = [
72
119
  {
@@ -79,8 +126,7 @@ export const PROVIDER_SPECS = [
79
126
  access: { kind: "subscription", requirement: "Claude subscription", runs: "Claude Code" },
80
127
  auth: { kind: "oauth" },
81
128
  planLimits: true,
82
- // Claude has no second runtime: the harness axis is not a choice here.
83
- runtimes: { native: CLAUDE_CODE, claudeCode: CLAUDE_CODE },
129
+ loops: { default: "claude-code", offered: { "claude-code": { capabilities: CLAUDE_CODE } } },
84
130
  },
85
131
  {
86
132
  id: "codex",
@@ -92,7 +138,29 @@ export const PROVIDER_SPECS = [
92
138
  access: { kind: "subscription", requirement: "ChatGPT subscription", runs: "Codex" },
93
139
  auth: { kind: "translator", cliProxy: "codex" },
94
140
  planLimits: true,
95
- runtimes: { native: CODEX, claudeCode: CLAUDE_CODE },
141
+ loops: {
142
+ default: "codex",
143
+ offered: {
144
+ codex: { capabilities: CODEX },
145
+ "claude-code": {
146
+ capabilities: CLAUDE_CODE,
147
+ terms: {
148
+ status: "unlisted",
149
+ note: "OpenAI lets a ChatGPT plan run in Codex and in the apps it lists for Sign in with ChatGPT (OpenCode, Pi, T3 and others). Claude Code, reached here through a translator, isn't among them.",
150
+ source: "https://help.openai.com/en/articles/20001542",
151
+ },
152
+ },
153
+ // The plan through the bundled translator's Responses surface, not OpenCode's own sign-in.
154
+ opencode: {
155
+ capabilities: OPENCODE,
156
+ terms: {
157
+ status: "unlisted",
158
+ note: "OpenAI lists OpenCode among the apps a ChatGPT plan may sign in to, through OpenCode's own sign-in. Here OpenCode reaches the plan through a translator instead, which OpenAI doesn't list.",
159
+ source: "https://help.openai.com/en/articles/20001542",
160
+ },
161
+ },
162
+ },
163
+ },
96
164
  },
97
165
  {
98
166
  id: "grok",
@@ -105,7 +173,32 @@ export const PROVIDER_SPECS = [
105
173
  // The app says grok, CLIProxyAPI says xai; Grok alone also runs natively, not just through the translator.
106
174
  auth: { kind: "translator", cliProxy: "xai" },
107
175
  planLimits: false,
108
- runtimes: { native: OPENCODE, claudeCode: CLAUDE_CODE },
176
+ loops: {
177
+ default: "opencode",
178
+ offered: {
179
+ opencode: { capabilities: OPENCODE },
180
+ // xAI's own CLI, signed in through its own device login (not OpenCode's), and free to run on a SuperGrok
181
+ // or X Premium plan: it is the client xAI ships for that subscription.
182
+ vendor: { capabilities: GROK_BUILD, label: "Grok Build", door: "grok-build" },
183
+ "claude-code": {
184
+ capabilities: CLAUDE_CODE,
185
+ terms: {
186
+ status: "unlisted",
187
+ note: "xAI turns SuperGrok sign-in on for OpenCode, Hermes, OpenClaw and Kilo Code. Claude Code, reached here through a translator, isn't among them.",
188
+ source: "https://x.ai/news/grok-opencode",
189
+ },
190
+ },
191
+ // The translator's xAI sign-in on its Responses surface, a sign-in of its own beside OpenCode's.
192
+ codex: {
193
+ capabilities: CODEX,
194
+ terms: {
195
+ status: "unlisted",
196
+ note: "xAI turns SuperGrok sign-in on for OpenCode, Hermes, OpenClaw and Kilo Code. Codex, reached here through a translator, isn't among them.",
197
+ source: "https://x.ai/news/grok-opencode",
198
+ },
199
+ },
200
+ },
201
+ },
109
202
  },
110
203
  {
111
204
  id: "kimi",
@@ -117,8 +210,19 @@ export const PROVIDER_SPECS = [
117
210
  access: { kind: "subscription", requirement: "Kimi Code subscription", runs: "Kimi Code" },
118
211
  auth: { kind: "translator", cliProxy: "kimi" },
119
212
  planLimits: true,
120
- // Kimi has no native runtime: it exists only under the Claude Code loop, on both harnesses.
121
- runtimes: { native: CLAUDE_CODE, claudeCode: CLAUDE_CODE },
213
+ // Claude Code stays the default: the translator's subscription is what most sandboxes already hold. Kimi's own CLI signs
214
+ // in with its own device login, Kimi's sanctioned client for the plan. Kimi documents Claude Code, OpenCode and Codex
215
+ // with a Kimi Code API key; Codex and OpenCode here reach the plan through the translator's sign-in instead, which
216
+ // Kimi doesn't list, hence their note. Claude Code predates the note and stays as it was.
217
+ loops: {
218
+ default: "claude-code",
219
+ offered: {
220
+ "claude-code": { capabilities: CLAUDE_CODE },
221
+ vendor: { capabilities: KIMI_CLI, label: "Kimi CLI", door: "kimi-cli" },
222
+ codex: { capabilities: CODEX, terms: KIMI_TRANSLATED },
223
+ opencode: { capabilities: OPENCODE, terms: KIMI_TRANSLATED },
224
+ },
225
+ },
122
226
  },
123
227
  {
124
228
  // Labelled for the account (Google), not the model family: this channel vends Claude and GPT-OSS beside Gemini.
@@ -132,8 +236,20 @@ export const PROVIDER_SPECS = [
132
236
  // antigravity is Google's own agent product, the name CLIProxyAPI files this credential under.
133
237
  auth: { kind: "translator", cliProxy: "antigravity" },
134
238
  planLimits: true,
135
- // The only routed provider that ignores harness: Google refuses any request announcing the Claude Code loop.
136
- runtimes: { native: OPENCODE_GEMINI, claudeCode: OPENCODE_GEMINI },
239
+ // The only routed provider without Claude Code: Google refuses any request announcing that loop.
240
+ loops: {
241
+ default: "opencode",
242
+ offered: {
243
+ opencode: {
244
+ capabilities: OPENCODE_GEMINI,
245
+ terms: {
246
+ status: "against-terms",
247
+ note: "Google's Antigravity terms don't allow its sign-in in third-party clients such as OpenCode, and Google has suspended accounts for it.",
248
+ source: "https://antigravity.google/terms/",
249
+ },
250
+ },
251
+ },
252
+ },
137
253
  },
138
254
  {
139
255
  // Cursor's own runtime via Anysphere's SDK; labelled for the account since it vends several vendors' models.
@@ -147,8 +263,8 @@ export const PROVIDER_SPECS = [
147
263
  access: { kind: "subscription", requirement: "Cursor Pro subscription", runs: "Cursor Agent" },
148
264
  auth: { kind: "oauth" },
149
265
  planLimits: false,
150
- // Cursor ignores harness too: there is no route to it but its own SDK.
151
- runtimes: { native: CURSOR, claudeCode: CURSOR },
266
+ // There is no route to Cursor but its own SDK.
267
+ loops: { default: "vendor", offered: { vendor: { capabilities: CURSOR } } },
152
268
  },
153
269
  {
154
270
  // Cognition's Devin through its own CLI (`devin acp`); labelled for the account, since it vends many vendors'
@@ -165,10 +281,11 @@ export const PROVIDER_SPECS = [
165
281
  auth: { kind: "oauth" },
166
282
  // Devin publishes its quota only on its own pages and in its CLI's interactive view, nothing a key can read.
167
283
  planLimits: false,
168
- // The CLI is the only door, so harness is not a choice: both axes are Devin's own runtime.
169
- runtimes: { native: DEVIN, claudeCode: DEVIN },
284
+ // The CLI is the only door, so the loop is not a choice.
285
+ loops: { default: "vendor", offered: { vendor: { capabilities: DEVIN } } },
170
286
  },
171
- // The two minted providers need no new runtime or translator hop: each has its own Anthropic Messages endpoint.
287
+ // The two minted providers need no translator hop: each has its own endpoint for the minted key. Meta's key is a Muse
288
+ // Code key, so its own loop is Muse Code; Z.ai's runs under Claude Code against its Anthropic Messages endpoint.
172
289
  {
173
290
  id: "meta",
174
291
  label: "Meta",
@@ -177,7 +294,7 @@ export const PROVIDER_SPECS = [
177
294
  destination: "Meta",
178
295
  brand: "meta",
179
296
  // A subscription like the rest: the device sign-in mints a plan key, and the plan is what a turn spends.
180
- access: { kind: "subscription", requirement: "Muse Code subscription", runs: "Muse Spark under Claude Code" },
297
+ access: { kind: "subscription", requirement: "Muse Code subscription", runs: "Muse Spark in Muse Code" },
181
298
  auth: {
182
299
  kind: "minted",
183
300
  // Preserve the minted variant id because stored accounts reference it.
@@ -197,7 +314,21 @@ export const PROVIDER_SPECS = [
197
314
  },
198
315
  // No quota surface a minted key can read: the row shows no meter and says so, not an empty one reading as zero.
199
316
  planLimits: false,
200
- runtimes: { native: CLAUDE_CODE, claudeCode: CLAUDE_CODE },
317
+ // Muse Code is the use Meta's terms sanction for the key, so it says nothing; Claude Code stays on offer, quietly noted.
318
+ loops: {
319
+ default: "muse",
320
+ offered: {
321
+ muse: { capabilities: MUSE },
322
+ "claude-code": {
323
+ capabilities: CLAUDE_CODE,
324
+ terms: {
325
+ status: "against-terms",
326
+ note: "Meta's terms say a Muse Code subscription key is for use with Muse Code only. Here it runs under Claude Code.",
327
+ source: "https://dev.meta.ai/docs/muse-code/subscriptions",
328
+ },
329
+ },
330
+ },
331
+ },
201
332
  },
202
333
  {
203
334
  id: "zai",
@@ -221,6 +352,8 @@ export const PROVIDER_SPECS = [
221
352
  flow: "device",
222
353
  anthropicBase: "https://api.z.ai/api/anthropic",
223
354
  catalogBase: "https://api.z.ai/api/coding/paas/v4",
355
+ // Z.ai's dedicated Responses endpoint for Codex (docs.z.ai/devpack/tool/codex).
356
+ responsesBase: "https://api.z.ai/api/v1",
224
357
  },
225
358
  {
226
359
  id: "bigmodel",
@@ -230,11 +363,18 @@ export const PROVIDER_SPECS = [
230
363
  flow: "redirect",
231
364
  anthropicBase: "https://open.bigmodel.cn/api/anthropic",
232
365
  catalogBase: "https://open.bigmodel.cn/api/coding/paas/v4",
366
+ // BigModel's equivalent (docs.bigmodel.cn/cn/coding-plan/tool/codex).
367
+ responsesBase: "https://open.bigmodel.cn/api/v1",
233
368
  },
234
369
  ],
235
370
  },
236
371
  planLimits: false,
237
- runtimes: { native: CLAUDE_CODE, claudeCode: CLAUDE_CODE },
372
+ // The GLM Coding Plan lists Claude Code, Codex and OpenCode among its tools, each on the minted key aimed at the
373
+ // estate's own endpoints: Anthropic Messages, Responses and Chat Completions respectively.
374
+ loops: {
375
+ default: "claude-code",
376
+ offered: { "claude-code": { capabilities: CLAUDE_CODE }, codex: { capabilities: CODEX }, opencode: { capabilities: OPENCODE } },
377
+ },
238
378
  },
239
379
  ] as const satisfies readonly ProviderSpec[];
240
380
 
@@ -9,7 +9,7 @@ import { contributedServerMintingKinds } from "@intentic/extension-manifest";
9
9
  // - outside: the server carries content from beyond the container (a web page, a provider's verbatim sentence)
10
10
  export type ServerProvenance = "control" | "outside";
11
11
 
12
- // Every server agent/run/agent.ts and agent/run/harness/harness-servers.ts mount themselves, plus the browser router's two
12
+ // Every server agent/run/agent.ts, agent/run/harness/harness-servers.ts and runtimes/daemon-tools/daemon-servers.ts mount themselves, plus the browser router's two
13
13
  // names (web, browser) and the virtual desktop's (desktop), which a turn mounts on its lease at the daemon's MCP door
14
14
  // (browser-tools.ts, desktop/desktop-tools.ts) and which the text scan reads from their constants. A server added anywhere else without a row here fails the conformance test in the sandbox guard package.
15
15
  export const DAEMON_MCP_SERVERS: Readonly<Record<string, ServerProvenance>> = {
@@ -23,6 +23,7 @@ export const DAEMON_MCP_SERVERS: Readonly<Record<string, ServerProvenance>> = {
23
23
  watch: "control", // harness: condition watches
24
24
  deps: "control", // harness: dependency readiness
25
25
  diagnostics: "outside", // harness: two tools relay a provider's own sentence verbatim
26
+ gate: "control", // daemon-servers: the policy gate a runtime's own hooks call through the door (runtimes/daemon-tools/policy-gate-server.ts)
26
27
  web: "outside", // the turn's mounts: the anonymous browser router; the page is the internet
27
28
  browser: "outside", // the turn's mounts: the signed-in browser router; the page is the internet
28
29
  desktop: "outside", // the turn's mounts: the sandbox's own virtual desktop; whatever its windows show is outside content
@@ -8,8 +8,12 @@ import type { CommandSpan } from "./command-classes.js";
8
8
  // working (hosts/host-restart-guard.ts), and the device agent, whose "Manage sandboxes" switch decides whether anything
9
9
  // may do this at all (the machine's tools/shell.ts). The same verbs and the same wording, so a command and the tool
10
10
  // that does the same job cannot drift apart.
11
+ //
12
+ // (2026-10-13) On Intentic OS the machine itself is managed the same way: `ic os update|rollback|reboot|channel`, and the
13
+ // `bootc upgrade|switch|rollback` under it, change what the whole box boots, and the ones that reboot it stop every
14
+ // sandbox on it at once. They are read here as the os_* device tools' twins, under the same switch.
11
15
 
12
- /** The sandbox operations a command can name, in the words `ic sandbox` spells them. */
16
+ /** The sandbox operations a command can name, in the words `ic sandbox` spells them, and the OS ones in `ic os`'s. */
13
17
  export type SandboxPowerOp =
14
18
  | "start"
15
19
  | "stop"
@@ -23,7 +27,12 @@ export type SandboxPowerOp =
23
27
  | "dev"
24
28
  | "resize"
25
29
  | "restore"
26
- | "connect";
30
+ | "connect"
31
+ // The machine's OS (Intentic OS), in `ic os`'s verbs; a bootc spelling reads as the same op.
32
+ | "os-update"
33
+ | "os-rollback"
34
+ | "os-reboot"
35
+ | "os-channel";
27
36
 
28
37
  /** The owner's own word for an operation, as a card or a refusal says it ("<doing> this sandbox now?"). */
29
38
  export const SANDBOX_POWER_DOING = {
@@ -40,17 +49,23 @@ export const SANDBOX_POWER_DOING = {
40
49
  resize: "Restart with new resources",
41
50
  restore: "Restore a backup over",
42
51
  connect: "Reinstall",
52
+ // The OS ops stop a sandbox only when they reboot, which takes every sandbox on the machine with it.
53
+ "os-update": "Update and reboot the machine running",
54
+ "os-rollback": "Roll back and reboot the machine running",
55
+ "os-reboot": "Reboot the machine running",
56
+ "os-channel": "Switch images and reboot the machine running",
43
57
  } as const satisfies Record<SandboxPowerOp, string>;
44
58
 
45
59
  /** One command that manages a sandbox container, and what it aims at. */
46
60
  export interface SandboxPowerCommand {
47
- readonly via: "ic" | "docker";
61
+ /** `ic` for `ic sandbox` and `ic os`, `bootc` for the OS's own tool under `ic os`. */
62
+ readonly via: "ic" | "docker" | "bootc";
48
63
  readonly op: SandboxPowerOp;
49
64
  /** Whether it ends the turns of a sandbox that is running; false for a start, a download, a resource ask saved for later. */
50
65
  readonly stops: boolean;
51
66
  /** The slugs (`ic`) or container names (`docker`) it names, as written; an expansion (`$x`, `$(…)`, `*`) stays as typed. */
52
67
  readonly targets: readonly string[];
53
- /** Names none: `ic sandbox restart` alone (the only sandbox the device runs), `remove --all`, `docker stop` fed from a pipe. */
68
+ /** Names none: `ic sandbox restart` alone (the only sandbox the device runs), `remove --all`, `docker stop` fed from a pipe, and every OS op, which is the whole machine's. */
54
69
  readonly unnamed: boolean;
55
70
  /** The fragment, so a card can point at it. */
56
71
  readonly span: CommandSpan;
@@ -63,6 +78,10 @@ const IC_INVOCATION = /(?<![\w.$-])ic(?:\.exe)?["']?\s+sandbox\s+([a-z][a-z-]*)(
63
78
  // `docker stop`, `docker container rm`, podman alike. A global option between the program and the verb
64
79
  // (`docker -H host stop x`) is not read: the verb then follows an option, and the command is left unrecognised.
65
80
  const DOCKER_INVOCATION = /(?<![\w.$-])(?:docker|podman)(?:\.exe)?["']?\s+(?:container\s+)?(stop|restart|rm|remove|kill)(?![\w-])/gi;
81
+ // `ic os <verb>` and `bootc <verb>`, the machine's OS. Only the verbs that change what it boots; `status`, `health`,
82
+ // `record` and `pair` read or set up and are left alone, as is a `--check` that only asks the registry.
83
+ const IC_OS_INVOCATION = /(?<![\w.$-])ic(?:\.exe)?["']?\s+os\s+([a-z][a-z-]*)(?![\w-])/gi;
84
+ const BOOTC_INVOCATION = /(?<![\w.$-])bootc["']?\s+([a-z][a-z-]*)(?![\w-])/gi;
66
85
 
67
86
  const IC_VERBS = new Map<string, SandboxPowerOp>([
68
87
  ["start", "start"],
@@ -90,6 +109,21 @@ const DOCKER_VERBS = new Map<string, SandboxPowerOp>([
90
109
  ["remove", "remove"],
91
110
  ]);
92
111
 
112
+ const IC_OS_VERBS = new Map<string, SandboxPowerOp>([
113
+ ["update", "os-update"],
114
+ ["rollback", "os-rollback"],
115
+ ["reboot", "os-reboot"],
116
+ ["channel", "os-channel"],
117
+ ]);
118
+
119
+ // `bootc switch` is what `ic os channel` runs, to any image; `update` is read as `upgrade`'s other spelling.
120
+ const BOOTC_VERBS = new Map<string, SandboxPowerOp>([
121
+ ["upgrade", "os-update"],
122
+ ["update", "os-update"],
123
+ ["switch", "os-channel"],
124
+ ["rollback", "os-rollback"],
125
+ ]);
126
+
93
127
  // The flags of each program that take their value as the next word, so the value is not read as a slug.
94
128
  const IC_VALUE_FLAGS: ReadonlySet<string> = new Set([
95
129
  "--channel",
@@ -110,6 +144,7 @@ const IC_VALUE_FLAGS: ReadonlySet<string> = new Set([
110
144
  "--tail",
111
145
  ]);
112
146
  const DOCKER_VALUE_FLAGS: ReadonlySet<string> = new Set(["-t", "--time", "-s", "--signal"]);
147
+ const OS_VALUE_FLAGS: ReadonlySet<string> = new Set(["--wait", "--transport", "--target-imgref"]);
113
148
 
114
149
  // The simple command that starts the text: up to the first `|`, `;`, `&` or newline outside quotes. A `&` right after
115
150
  // `>` or `<` is a redirection (`2>&1`), which ends nothing.
@@ -194,8 +229,27 @@ const icAim = (op: SandboxPowerOp, { positionals, flags }: Operands): Aim => {
194
229
  return { stops: true, targets: positionals, all };
195
230
  };
196
231
 
232
+ // Whether an OS op reboots the machine now, which stops every sandbox on it; undefined for a check that only asks the
233
+ // registry, which manages nothing. A reboot always does; the rest stage for the next boot unless told `--apply`.
234
+ const osStops = (op: SandboxPowerOp, { flags }: Operands): boolean | undefined => {
235
+ if (op === "os-update" && flags.has("--check")) {
236
+ return undefined;
237
+ }
238
+ return op === "os-reboot" || flags.has("--apply");
239
+ };
240
+
241
+ // The OS ops in `command`, through `ic os` or bootc. Each names the whole machine, so none names a target.
242
+ const osPowerCommands = (command: string, via: "ic" | "bootc", regions: readonly CommandSpan[]): SandboxPowerCommand[] =>
243
+ [...command.matchAll(via === "ic" ? IC_OS_INVOCATION : BOOTC_INVOCATION)].flatMap((invocation) => {
244
+ const op = (via === "ic" ? IC_OS_VERBS : BOOTC_VERBS).get((invocation[1] ?? "").toLowerCase());
245
+ const span = { start: invocation.index, end: invocation.index + invocation[0].length };
246
+ const stops = op === undefined ? undefined : osStops(op, operandsOf(wordsOf(command.slice(span.end)), OS_VALUE_FLAGS));
247
+ return op === undefined || stops === undefined ? [] : [{ via, op, stops, targets: [], unnamed: true, span, live: isLive(span, regions) }];
248
+ });
249
+
197
250
  /**
198
- * Every command in `command` that starts, stops or replaces a sandbox container, through `ic sandbox` or `docker`.
251
+ * Every command in `command` that starts, stops or replaces a sandbox container, through `ic sandbox` or `docker`, or
252
+ * changes what an Intentic OS machine boots, through `ic os` or `bootc`.
199
253
  * A scan for the programs by name, not a shell parse: it is meant to over-include, since a caller that mistakes
200
254
  * a mention for a command asks the owner once, and one that misses a command cuts a fleet's turns unasked.
201
255
  */
@@ -210,6 +264,7 @@ export const sandboxPowerCommands = (command: string): SandboxPowerCommand[] =>
210
264
  found.push({ via: "ic", op, stops: aim.stops, targets: aim.targets, unnamed: aim.targets.length === 0 || aim.all, span, live: isLive(span, regions) });
211
265
  }
212
266
  }
267
+ found.push(...osPowerCommands(command, "ic", regions), ...osPowerCommands(command, "bootc", regions));
213
268
  for (const invocation of command.matchAll(DOCKER_INVOCATION)) {
214
269
  const op = DOCKER_VERBS.get((invocation[1] ?? "").toLowerCase());
215
270
  if (op !== undefined) {
@@ -240,8 +295,12 @@ export const mayReachSandbox = (hit: SandboxPowerCommand, own: OwnSandbox): bool
240
295
 
241
296
  /**
242
297
  * Whether `hit` manages a sandbox that a device's "Manage sandboxes" switch covers, whichever sandbox it is: any
243
- * `ic sandbox` verb here, and a `docker` verb aimed at a container whose name starts with `containerPrefix` (a sandbox's
244
- * own, or its tunnel's). A docker target worked out at run time is not read: the device cannot know what it will be.
298
+ * `ic sandbox` or `ic os` verb here and any bootc one (the OS is every sandbox's ground), and a `docker` verb aimed at a
299
+ * container whose name starts with `containerPrefix` (a sandbox's own, or its tunnel's). A docker target worked out at
300
+ * run time is not read: the device cannot know what it will be.
245
301
  */
246
302
  export const managesSandbox = (hit: SandboxPowerCommand, containerPrefix: string): boolean =>
247
- hit.via === "ic" || hit.targets.some((target) => target.startsWith(containerPrefix));
303
+ hit.via !== "docker" || hit.targets.some((target) => target.startsWith(containerPrefix));
304
+
305
+ /** Whether `hit` acts on the machine's OS (`ic os`, `bootc`) rather than on one sandbox. */
306
+ export const isOsPower = (hit: Pick<SandboxPowerCommand, "op">): boolean => hit.op.startsWith("os-");
@@ -1,4 +1,4 @@
1
- import { managesSandbox, mayReachSandbox, SANDBOX_POWER_DOING, type SandboxPowerCommand, sandboxPowerCommands } from "../sandbox-power.js";
1
+ import { isOsPower, managesSandbox, mayReachSandbox, SANDBOX_POWER_DOING, type SandboxPowerCommand, sandboxPowerCommands } from "../sandbox-power.js";
2
2
 
3
3
  const OWN = { slug: "work", container: "intentic-sandbox-work" };
4
4
  const PREFIX = "intentic-sandbox-";
@@ -128,6 +128,63 @@ describe("docker", () => {
128
128
  });
129
129
  });
130
130
 
131
+ // On Intentic OS the machine's own OS is changed through `ic os` (and bootc under it): what reboots it stops every
132
+ // sandbox on it, and what only stages for the next boot stops none, like a sandbox's prepare.
133
+ describe("ic os and bootc", () => {
134
+ const WHOLE_MACHINE = { targets: [], unnamed: true };
135
+
136
+ test("reads every verb that changes what the machine boots, as the whole machine's", () => {
137
+ expect(first("ic os update")).toEqual({ via: "ic", op: "os-update", stops: false, ...WHOLE_MACHINE });
138
+ expect(first("ic os update --apply")).toEqual({ via: "ic", op: "os-update", stops: true, ...WHOLE_MACHINE });
139
+ expect(first("ic os rollback")).toEqual({ via: "ic", op: "os-rollback", stops: false, ...WHOLE_MACHINE });
140
+ expect(first("ic os rollback --apply")).toEqual({ via: "ic", op: "os-rollback", stops: true, ...WHOLE_MACHINE });
141
+ expect(first("ic os reboot")).toEqual({ via: "ic", op: "os-reboot", stops: true, ...WHOLE_MACHINE });
142
+ // The channel is the verb's operand, not a sandbox it names.
143
+ expect(first("ic os channel beta")).toEqual({ via: "ic", op: "os-channel", stops: false, ...WHOLE_MACHINE });
144
+ });
145
+
146
+ test("reads bootc's own spelling of the same ops", () => {
147
+ expect(first("sudo bootc upgrade")).toEqual({ via: "bootc", op: "os-update", stops: false, ...WHOLE_MACHINE });
148
+ expect(first("sudo -n /usr/bin/bootc upgrade --apply")).toEqual({ via: "bootc", op: "os-update", stops: true, ...WHOLE_MACHINE });
149
+ expect(first("bootc switch ghcr.io/intentic/os:beta")).toEqual({ via: "bootc", op: "os-channel", stops: false, ...WHOLE_MACHINE });
150
+ expect(first("bootc switch --apply --transport registry ghcr.io/intentic/os:beta")?.stops).toBe(true);
151
+ expect(first("bootc rollback")).toEqual({ via: "bootc", op: "os-rollback", stops: false, ...WHOLE_MACHINE });
152
+ expect(first("bootc rollback --apply")?.stops).toBe(true);
153
+ });
154
+
155
+ test("leaves alone what only reads or sets up", () => {
156
+ for (const command of [
157
+ "ic os status --json",
158
+ "ic os update --check --json",
159
+ "ic os health --wait 600",
160
+ "ic os record",
161
+ "ic os pair",
162
+ "bootc status --json",
163
+ "bootc upgrade --check",
164
+ "magic os reboot",
165
+ "echo cosmic os update",
166
+ ]) {
167
+ expect(sandboxPowerCommands(command), command).toEqual([]);
168
+ }
169
+ });
170
+
171
+ test("a reboot may reach this sandbox and is the switch's, wherever this sandbox runs", () => {
172
+ const reboot = sandboxPowerCommands("ic os reboot")[0];
173
+ expect(reboot !== undefined && mayReachSandbox(reboot, OWN)).toBe(true);
174
+ expect(reboot !== undefined && managesSandbox(reboot, PREFIX)).toBe(true);
175
+ expect(reboot !== undefined && isOsPower(reboot)).toBe(true);
176
+ const staged = sandboxPowerCommands("sudo bootc upgrade")[0];
177
+ expect(staged !== undefined && managesSandbox(staged, PREFIX)).toBe(true);
178
+ expect(isOsPower({ op: "restart" })).toBe(false);
179
+ });
180
+
181
+ test("is found beside a sandbox verb in the same line, in the order written, and quoted it is not live", () => {
182
+ expect(sandboxPowerCommands("ic sandbox stop work && ic os reboot").map((hit) => hit.op)).toEqual(["stop", "os-reboot"]);
183
+ expect(sandboxPowerCommands("echo 'ic os reboot'")[0]?.live).toBe(false);
184
+ expect(sandboxPowerCommands("ic os reboot")[0]?.span).toEqual({ start: 0, end: "ic os reboot".length });
185
+ });
186
+ });
187
+
131
188
  describe("whether a command may reach this sandbox", () => {
132
189
  const reaches = (command: string): boolean => sandboxPowerCommands(command).some((hit) => mayReachSandbox(hit, OWN));
133
190
 
@@ -178,4 +235,5 @@ test("every operation has the owner's word for it", () => {
178
235
  expect(SANDBOX_POWER_DOING.restart).toBe("Restart");
179
236
  expect(SANDBOX_POWER_DOING.resize).toBe("Restart with new resources");
180
237
  expect(SANDBOX_POWER_DOING.rollback).toBe("Roll back");
238
+ expect(`${SANDBOX_POWER_DOING["os-reboot"]} this sandbox now?`).toBe("Reboot the machine running this sandbox now?");
181
239
  });
@@ -20,6 +20,8 @@ const TURNED_AWAY: ReadonlySet<string> = new Set([
20
20
  // The privacy shield found personal data in instructions a runtime loads itself, where it could not mask them.
21
21
  "privacy-instructions",
22
22
  "sandbox-memory-low",
23
+ // The conversation's own view of the workspace could not be set up in time: nothing was started in it.
24
+ "workspace-setup",
23
25
  "trial-unavailable",
24
26
  "trial-model-unavailable",
25
27
  "trial-exhausted",