@intentic/sandbox-contract 1.223.0 → 1.225.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 (165) hide show
  1. package/README.md +14 -14
  2. package/dist/agent-catalog.js +3 -3
  3. package/dist/agent-catalog.js.map +1 -1
  4. package/dist/chores/chores.js +43 -43
  5. package/dist/chores/chores.js.map +1 -1
  6. package/dist/chores/extension-update.js +2 -2
  7. package/dist/chores/extension-update.js.map +1 -1
  8. package/dist/chores/fix-deps.js +1 -1
  9. package/dist/chores/fix-deps.js.map +1 -1
  10. package/dist/chores/probes.js +1 -1
  11. package/dist/chores/probes.js.map +1 -1
  12. package/dist/chores/prompt.d.ts.map +1 -1
  13. package/dist/chores/prompt.js +3 -3
  14. package/dist/chores/prompt.js.map +1 -1
  15. package/dist/chores/verdict.js +2 -2
  16. package/dist/chores/verdict.js.map +1 -1
  17. package/dist/contracts/capabilities.contract.d.ts +41 -0
  18. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  19. package/dist/contracts/exit.contract.d.ts +80 -0
  20. package/dist/contracts/exit.contract.d.ts.map +1 -0
  21. package/dist/contracts/exit.contract.js +13 -0
  22. package/dist/contracts/exit.contract.js.map +1 -0
  23. package/dist/contracts/ports.contract.d.ts +11 -0
  24. package/dist/contracts/ports.contract.d.ts.map +1 -1
  25. package/dist/contracts/settings.contract.d.ts +12 -0
  26. package/dist/contracts/settings.contract.d.ts.map +1 -1
  27. package/dist/events.d.ts +4 -4
  28. package/dist/events.d.ts.map +1 -1
  29. package/dist/events.js +5 -5
  30. package/dist/events.js.map +1 -1
  31. package/dist/fast-tier.d.ts +9 -0
  32. package/dist/fast-tier.d.ts.map +1 -0
  33. package/dist/fast-tier.js +19 -0
  34. package/dist/fast-tier.js.map +1 -0
  35. package/dist/history-state.js +3 -3
  36. package/dist/history-state.js.map +1 -1
  37. package/dist/index.d.ts +214 -68
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +5 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/model-order.d.ts +1 -0
  42. package/dist/model-order.d.ts.map +1 -1
  43. package/dist/model-order.js +5 -0
  44. package/dist/model-order.js.map +1 -1
  45. package/dist/output-fields.d.ts.map +1 -1
  46. package/dist/output-fields.js +2 -2
  47. package/dist/output-fields.js.map +1 -1
  48. package/dist/prompt-complexity.d.ts +19 -0
  49. package/dist/prompt-complexity.d.ts.map +1 -0
  50. package/dist/prompt-complexity.js +83 -0
  51. package/dist/prompt-complexity.js.map +1 -0
  52. package/dist/publish-drafts.js +2 -2
  53. package/dist/publish-drafts.js.map +1 -1
  54. package/dist/schemas.d.ts +236 -0
  55. package/dist/schemas.d.ts.map +1 -1
  56. package/dist/schemas.js +116 -2
  57. package/dist/schemas.js.map +1 -1
  58. package/dist/workflow-faults.js +3 -3
  59. package/dist/workflow-faults.js.map +1 -1
  60. package/dist/workspace-state.d.ts +20 -20
  61. package/dist/workspace-state.d.ts.map +1 -1
  62. package/dist/workspace-state.js +20 -20
  63. package/dist/workspace-state.js.map +1 -1
  64. package/package.json +5 -5
  65. package/src/agent-catalog.test.ts +25 -25
  66. package/src/agent-catalog.ts +81 -81
  67. package/src/agent-run-model.test.ts +3 -3
  68. package/src/agent-run-model.ts +8 -8
  69. package/src/capability-env.ts +3 -3
  70. package/src/capability-ledger.test.ts +13 -13
  71. package/src/capability-secrets.ts +5 -5
  72. package/src/chores/chores.test.ts +1 -1
  73. package/src/chores/chores.ts +109 -109
  74. package/src/chores/digest.test.ts +1 -1
  75. package/src/chores/digest.ts +3 -3
  76. package/src/chores/extension-update.ts +5 -5
  77. package/src/chores/fix-deps.ts +4 -4
  78. package/src/chores/probes.test.ts +6 -6
  79. package/src/chores/probes.ts +17 -17
  80. package/src/chores/prompt.ts +9 -9
  81. package/src/chores/stack.test.ts +3 -3
  82. package/src/chores/stack.ts +17 -17
  83. package/src/chores/verdict.test.ts +20 -20
  84. package/src/chores/verdict.ts +25 -25
  85. package/src/contract-lock.test.ts +1 -1
  86. package/src/contract-lock.ts +7 -7
  87. package/src/contracts/activity.contract.ts +1 -1
  88. package/src/contracts/agent.contract.ts +2 -2
  89. package/src/contracts/agents.contract.ts +11 -11
  90. package/src/contracts/automations.contract.ts +5 -5
  91. package/src/contracts/capabilities.contract.ts +7 -7
  92. package/src/contracts/chores.contract.ts +3 -3
  93. package/src/contracts/ci.contract.ts +2 -2
  94. package/src/contracts/claude.contract.ts +3 -3
  95. package/src/contracts/drafts.contract.ts +1 -1
  96. package/src/contracts/endpoints.contract.ts +2 -2
  97. package/src/contracts/exit.contract.ts +42 -0
  98. package/src/contracts/extensions.contract.ts +8 -8
  99. package/src/contracts/git.contract.ts +6 -6
  100. package/src/contracts/grok.contract.ts +4 -4
  101. package/src/contracts/host.contract.ts +5 -5
  102. package/src/contracts/intentic.contract.ts +2 -2
  103. package/src/contracts/logs.contract.ts +1 -1
  104. package/src/contracts/loops.contract.ts +8 -8
  105. package/src/contracts/personas.contract.ts +8 -8
  106. package/src/contracts/ports.contract.ts +1 -1
  107. package/src/contracts/prepush.contract.ts +2 -2
  108. package/src/contracts/providers.contract.ts +4 -4
  109. package/src/contracts/public.contract.ts +1 -1
  110. package/src/contracts/push.contract.ts +2 -2
  111. package/src/contracts/secrets.contract.ts +2 -2
  112. package/src/contracts/sessions.contract.ts +2 -2
  113. package/src/contracts/settings.contract.ts +3 -3
  114. package/src/contracts/share.contract.ts +1 -1
  115. package/src/contracts/skills.contract.ts +2 -2
  116. package/src/contracts/system.contract.ts +9 -9
  117. package/src/contracts/usage.contract.ts +1 -1
  118. package/src/contracts/vpn.contract.ts +5 -5
  119. package/src/contracts/workflows.contract.ts +12 -12
  120. package/src/contracts/workspace.contract.ts +13 -13
  121. package/src/conversation-ids.ts +8 -8
  122. package/src/events.test.ts +3 -3
  123. package/src/events.ts +142 -142
  124. package/src/fast-tier.test.ts +88 -0
  125. package/src/fast-tier.ts +72 -0
  126. package/src/history-state.ts +15 -15
  127. package/src/host-protocol.ts +7 -7
  128. package/src/hostnames.test.ts +1 -1
  129. package/src/hostnames.ts +15 -15
  130. package/src/index.ts +13 -8
  131. package/src/listener-protocol.ts +12 -12
  132. package/src/model-order.test.ts +11 -11
  133. package/src/model-order.ts +52 -30
  134. package/src/output-fields.ts +11 -11
  135. package/src/path-refs.test.ts +4 -4
  136. package/src/path-refs.ts +6 -6
  137. package/src/prompt-complexity.test.ts +160 -0
  138. package/src/prompt-complexity.ts +271 -0
  139. package/src/publish-drafts.ts +8 -8
  140. package/src/quick-model.test.ts +11 -11
  141. package/src/quick-model.ts +15 -15
  142. package/src/routes.test.ts +11 -5
  143. package/src/routes.ts +19 -19
  144. package/src/runtime-state.test.ts +1 -1
  145. package/src/runtime-state.ts +22 -22
  146. package/src/schemas.test.ts +8 -8
  147. package/src/schemas.ts +1238 -921
  148. package/src/search-globs.test.ts +2 -2
  149. package/src/search-globs.ts +6 -6
  150. package/src/session-names.ts +5 -5
  151. package/src/share-paths.test.ts +1 -1
  152. package/src/share-paths.ts +7 -7
  153. package/src/sse.ts +1 -1
  154. package/src/state-portability.ts +8 -8
  155. package/src/terminal-protocol.ts +3 -3
  156. package/src/title.test.ts +9 -9
  157. package/src/title.ts +21 -21
  158. package/src/tunnel-ids.test.ts +3 -3
  159. package/src/tunnel-ids.ts +7 -7
  160. package/src/versions.test.ts +3 -3
  161. package/src/versions.ts +6 -6
  162. package/src/workflow-faults.test.ts +6 -6
  163. package/src/workflow-faults.ts +14 -14
  164. package/src/workspace-state.test.ts +32 -32
  165. package/src/workspace-state.ts +139 -139
package/src/routes.ts CHANGED
@@ -6,18 +6,18 @@ import { z } from "zod";
6
6
  * normal and neither should force an update.
7
7
  *
8
8
  * What must not happen is the failure being SILENT. A route the daemon predates answers 404, which the browser
9
- * has no way to tell apart from "you asked for a file that isn't there" so a missing feature reads as a
9
+ * has no way to tell apart from "you asked for a file that isn't there", so a missing feature reads as a
10
10
  * broken one, and diagnosing it costs an hour of "did the image rebuild?".
11
11
  *
12
12
  * So the daemon ADVERTISES the routes it implements (the /events hello frame) and the browser compares that
13
13
  * against the contract it was itself built with. Everything present on both sides works exactly as before;
14
14
  * anything the daemon lacks is a KNOWN, NAMED gap the UI can gate a feature on or explain in an error, instead
15
- * of a mystery 404. Old daemon + new browser stays fully supported it just stops being confusing.
15
+ * of a mystery 404. Old daemon + new browser stays fully supported, it just stops being confusing.
16
16
  *
17
17
  * Route names are `<group>.<route>` (`vpn.list`, `kimi.models`), derived from the contract object both sides
18
18
  * import. Nothing is generated and nothing is hand-maintained: adding a route to the contract adds it here.
19
19
  *
20
- * Everything here is a pure function of a contract passed in index.ts binds them to `sandboxContract` once it
20
+ * Everything here is a pure function of a contract passed in, index.ts binds them to `sandboxContract` once it
21
21
  * is assembled, which is what keeps this module out of an import cycle with it. */
22
22
 
23
23
  // The shape we read off an oRPC contract procedure. `~orpc.route` is the contract metadata oRPC attaches to
@@ -43,7 +43,7 @@ const procedureRoute = (value: unknown): { method: string; path: string } | unde
43
43
  export interface ContractRoute {
44
44
  readonly name: string;
45
45
  readonly method: string;
46
- // The oRPC path template, with `{param}` placeholders e.g. `/system/terminals/{name}`.
46
+ // The oRPC path template, with `{param}` placeholders, e.g. `/system/terminals/{name}`.
47
47
  readonly path: string;
48
48
  }
49
49
 
@@ -69,7 +69,7 @@ export const contractRoutes = (contract: Record<string, unknown>): ContractRoute
69
69
  *
70
70
  * The route list above answers "does this daemon have it". It cannot answer the other half: a route that
71
71
  * exists on BOTH sides but whose payload changed between the two builds. Names match, so the call goes out,
72
- * and a field the browser expects is simply missing from the answer no 404, no message, nothing to attribute
72
+ * and a field the browser expects is simply missing from the answer, no 404, no message, nothing to attribute
73
73
  * it to. That is the one skew failure the named-gap mechanism still leaves silent.
74
74
  *
75
75
  * So each route also advertises a fingerprint of its wire shape: input and output schema together, reduced to
@@ -82,25 +82,25 @@ export const contractRoutes = (contract: Record<string, unknown>): ContractRoute
82
82
  * "hangs on someone remembering"). Changing a field in a contract schema changes that route's fingerprint by
83
83
  * itself, on both sides, with nobody to remember anything. */
84
84
 
85
- // The schemas oRPC hangs off a contract procedure, read structurally for the same reason as `route` above
85
+ // The schemas oRPC hangs off a contract procedure, read structurally for the same reason as `route` above,
86
86
  // they are internal metadata, not part of its public surface.
87
87
  interface ContractSchemasLike {
88
88
  readonly "~orpc": { readonly inputSchema?: unknown; readonly outputSchema?: unknown };
89
89
  }
90
90
 
91
- /* The JSON Schema keywords whose array value is a SET order carries no meaning, so two schemas that differ
91
+ /* The JSON Schema keywords whose array value is a SET, order carries no meaning, so two schemas that differ
92
92
  * only in it are the same wire shape. `required` is the one that matters in practice: JSON Schema spells an
93
93
  * object's fields twice, once as `properties` (an object, so key-sorting settles it) and once as `required`
94
- * (an array, in declaration order) so without this, moving a field up a `z.object` for readability reads as
94
+ * (an array, in declaration order), so without this, moving a field up a `z.object` for readability reads as
95
95
  * drift on every route that carries it. The union keywords are here for the same reason a step lower down. */
96
96
  const UNORDERED = new Set(["required", "enum", "anyOf", "oneOf", "allOf"]);
97
97
 
98
98
  /* Canonical JSON: object keys sorted, set-valued arrays sorted, everything else left in the order it was
99
- * written. So the fingerprint depends on what the schema SAYS rather than how zod happened to emit it which
99
+ * written. So the fingerprint depends on what the schema SAYS rather than how zod happened to emit it, which
100
100
  * is what keeps a purely cosmetic edit from being reported to a user as a payload that moved.
101
101
  *
102
102
  * Ordered arrays stay ordered: a tuple's `prefixItems` is positional, and sorting it would call two genuinely
103
- * different shapes identical the failure that actually costs something here. */
103
+ * different shapes identical, the failure that actually costs something here. */
104
104
  const canonical = (value: unknown, key?: string): unknown => {
105
105
  if (Array.isArray(value)) {
106
106
  const items = value.map((item) => canonical(item));
@@ -114,7 +114,7 @@ const canonical = (value: unknown, key?: string): unknown => {
114
114
  };
115
115
 
116
116
  /* FNV-1a over the canonical JSON, base36. Deliberately not a cryptographic hash: this is a drift SIGNAL, and
117
- * the cost of a collision is one warning that never appears never a wrong action, never a blocked call. A
117
+ * the cost of a collision is one warning that never appears, never a wrong action, never a blocked call. A
118
118
  * crypto digest would mean a subtle-crypto round trip in the browser (async, and unavailable on insecure
119
119
  * origins) to buy nothing this use has any need of. */
120
120
  const fingerprint = (value: unknown): string => {
@@ -133,8 +133,8 @@ const fingerprint = (value: unknown): string => {
133
133
  * way out, so reading both sides in the same direction would call two genuinely different shapes identical.
134
134
  *
135
135
  * A schema that will not convert yields undefined rather than throwing the walk away. Every such route in the
136
- * contract today is a streaming one oRPC wraps an event iterator's output in an opaque type with no schema
137
- * underneath it and their absence lands them in exactly the right place: a route with no advertised shape is
136
+ * contract today is a streaming one, oRPC wraps an event iterator's output in an opaque type with no schema
137
+ * underneath it, and their absence lands them in exactly the right place: a route with no advertised shape is
138
138
  * assumed compatible, which is the same "positive evidence only" rule the missing-route check already runs on. */
139
139
  const procedureShape = (value: unknown): string | undefined => {
140
140
  if (typeof value !== "object" || value === null || !("~orpc" in value)) {
@@ -174,7 +174,7 @@ export const routeShapes = (contract: Record<string, unknown>): Record<string, s
174
174
  };
175
175
 
176
176
  // Does a concrete request path match this route's template? Segment-wise, with `{param}` matching exactly one
177
- // segment the same shape oRPC mounts, so a template can never match a longer or shorter path.
177
+ // segment, the same shape oRPC mounts, so a template can never match a longer or shorter path.
178
178
  const pathMatches = (template: string, path: string): boolean => {
179
179
  const wanted = template.split("/");
180
180
  const actual = path.split("/");
@@ -185,7 +185,7 @@ const pathMatches = (template: string, path: string): boolean => {
185
185
  };
186
186
 
187
187
  // The contract route a concrete request belongs to, or undefined when the path is not a contract route at all
188
- // (the daemon also serves hand-written Hono routes like /health and /workspace/raw those are never gated).
188
+ // (the daemon also serves hand-written Hono routes like /health and /workspace/raw, those are never gated).
189
189
  // The query string is stripped first; callers pass whatever they handed to fetch.
190
190
  export const routeNameForRequest = (routes: readonly ContractRoute[], method: string, pathWithQuery: string): string | undefined => {
191
191
  const path = pathWithQuery.split("?")[0] ?? pathWithQuery;
@@ -193,18 +193,18 @@ export const routeNameForRequest = (routes: readonly ContractRoute[], method: st
193
193
  return routes.find((route) => route.method.toUpperCase() === upper && pathMatches(route.path, path))?.name;
194
194
  };
195
195
 
196
- // The route a typed client call belongs to. oRPC addresses a procedure by its position in the contract
197
- // `['git','stashApply']` which is the contract's own name for it, so this is a lookup rather than a match.
196
+ // The route a typed client call belongs to. oRPC addresses a procedure by its position in the contract,
197
+ // `['git','stashApply']`, which is the contract's own name for it, so this is a lookup rather than a match.
198
198
  export const routeForProcedure = (routes: readonly ContractRoute[], procedure: readonly string[]): ContractRoute | undefined =>
199
199
  routes.find((route) => route.name === procedure.join("."));
200
200
 
201
- // THE CONCRETE PATH A TYPED CALL WILL CARRY the route template with every `{param}` replaced by the input
201
+ // THE CONCRETE PATH A TYPED CALL WILL CARRY, the route template with every `{param}` replaced by the input
202
202
  // field of the same name, which is how the OpenAPI link fills them.
203
203
  //
204
204
  // This exists so a permission gate can check the string the daemon will actually route on rather than the
205
205
  // template it came from. Checking the template would quietly widen every grant: a wildcard glob matches the
206
206
  // literal `{repo}` braces just as happily as it matches a repo name, so a manifest that narrowed the grant to
207
- // one repo would still admit calls for every other the gate would be comparing two patterns instead of
207
+ // one repo would still admit calls for every other, the gate would be comparing two patterns instead of
208
208
  // testing a value against one.
209
209
  //
210
210
  // A param with no matching input field keeps its placeholder. That cannot be reached through the typed client
@@ -39,7 +39,7 @@ describe(`runtimeBoundQueryKeys`, () => {
39
39
  expect(joined(runtimeBoundQueryKeys())).toEqual(joined(RUNTIME_DOMAIN_BINDINGS.flatMap((binding) => binding.invalidates)));
40
40
  });
41
41
 
42
- it(`leaves no domain declaring no keys a domain nothing renders has no reason to be pushed`, () => {
42
+ it(`leaves no domain declaring no keys: a domain nothing renders has no reason to be pushed`, () => {
43
43
  expect(RUNTIME_DOMAIN_BINDINGS.filter((binding) => binding.invalidates.length === 0)).toEqual([]);
44
44
  });
45
45
  });
@@ -1,9 +1,9 @@
1
- /* WHICH RUNNING THING BACKS WHICH VIEW the twin of workspace-state.ts, for state that never touches a file.
1
+ /* WHICH RUNNING THING BACKS WHICH VIEW, the twin of workspace-state.ts, for state that never touches a file.
2
2
  *
3
3
  * That table answers "the agent wrote a file, so which view is stale". This one answers the same question for
4
4
  * the half of the sandbox that has no file to watch: the tmux sessions, the dev-server panels, the listening
5
5
  * sockets, the agent's Chromiums, the children its turns spawn. None of them are on disk, so no
6
- * `workspaceChanged` batch can ever mention them and for want of a push, every one of those views polled.
6
+ * `workspaceChanged` batch can ever mention them, and for want of a push, every one of those views polled.
7
7
  *
8
8
  * The polls were the tell. Six always-on timers (a 10s terminals relist behind THREE mounted surfaces, a 15s
9
9
  * port scan behind the shell rail, a 4s panel relist for as long as any dev server ran) that each asked a
@@ -14,14 +14,14 @@
14
14
  * told to. The declaration lives HERE for the reason its twin does: the daemon publishes a DOMAIN and the
15
15
  * browser turns it into query keys, and those two facts drift the moment they are written down in two packages.
16
16
  *
17
- * A key belongs to whoever queries it the extension views that render ports and panels ask under the SAME key
17
+ * A key belongs to whoever queries it, the extension views that render ports and panels ask under the SAME key
18
18
  * as core (api.sandbox.key("ports") is sandboxKey("ports")), so one entry here refreshes both surfaces and an
19
19
  * extension needs no declaration of its own. */
20
20
 
21
- /* One query key as the browser files it, a segment per level so a binding can name a NESTED key and not only
21
+ /* One query key as the browser files it, a segment per level, so a binding can name a NESTED key and not only
22
22
  * a top-level one. Most domains here are a single segment and read as they always did; the review is not. Its
23
23
  * key is `["git", "changes"]` (the browser appends the sandbox id, and every open file diff hangs beneath it),
24
- * and a domain that could only say `"git"` would drop the commit log along with it a second read of the graph
24
+ * and a domain that could only say `"git"` would drop the commit log along with it, a second read of the graph
25
25
  * to deliver a sentence that has nothing to do with it. */
26
26
  export type QueryKeyPath = readonly string[];
27
27
 
@@ -29,24 +29,24 @@ export interface RuntimeDomainBinding {
29
29
  // What moved, in the daemon's words. Wire data: it rides the runtimeChanged frame.
30
30
  readonly domain: string;
31
31
  /* The browser query keys this domain's state feeds. Invalidation reaches only queries something is
32
- * OBSERVING, so a domain nobody has on screen costs a frame and no request which is what lets a domain be
32
+ * OBSERVING, so a domain nobody has on screen costs a frame and no request, which is what lets a domain be
33
33
  * pushed eagerly without billing every tab for a view it isn't showing. */
34
34
  readonly invalidates: readonly QueryKeyPath[];
35
35
  }
36
36
 
37
37
  /* Declared `as const` so the domain names survive as literal types (see RuntimeDomain below), then published
38
- * under the interface the same two-binding shape, for the same reason, as WORKSPACE_STATE_FILES. */
38
+ * under the interface, the same two-binding shape, for the same reason, as WORKSPACE_STATE_FILES. */
39
39
  const RUNTIME_DOMAINS = [
40
40
  /* Every attachable tmux session: the terminal panel's tab strip, the rail's activity badge, and the work
41
- * popover's background-process rows all read this one list. Sampled rather than announced a pane dies
42
- * when its command exits and tmux tells nobody but sampled ONCE in the daemon, on a connection the
41
+ * popover's background-process rows all read this one list. Sampled rather than announced, a pane dies
42
+ * when its command exits and tmux tells nobody, but sampled ONCE in the daemon, on a connection the
43
43
  * browsers already hold, instead of once per browser per 10s over the tunnel. */
44
44
  { domain: "terminals", invalidates: [["terminals"]] },
45
45
 
46
46
  /* A repo's dev server: running, healthy, and the preview URL it answers on. Two independent things move it,
47
- * and it needs both the process manager starting or reaping a session (announced), and the server
47
+ * and it needs both, the process manager starting or reaping a session (announced), and the server
48
48
  * actually binding its port some seconds later, which is what flips "starting" to "healthy" (seen by the
49
- * port sampler, since panel health is read off the listening sockets see panels.ts listenersByRepo).
49
+ * port sampler, since panel health is read off the listening sockets, see panels.ts listenersByRepo).
50
50
  *
51
51
  * `apps` rides it because a monorepo's per-app previews ARE managed processes under the same manager
52
52
  * (workspace.routes appsList reads processes.portOf), just listed per repo instead of per repo-root. One
@@ -54,13 +54,13 @@ const RUNTIME_DOMAINS = [
54
54
  { domain: "panels", invalidates: [["panels"], ["apps"]] },
55
55
 
56
56
  /* Every listening TCP socket in the sandbox, and which of them are forwarded to a public preview hostname.
57
- * The daemon runs no port poller for the ANSWER attributing a socket to its process walks every /proc fd
58
- * table, far too much to do on a timer so what is sampled is only the LISTEN set out of /proc/net/tcp:
57
+ * The daemon runs no port poller for the ANSWER, attributing a socket to its process walks every /proc fd
58
+ * table, far too much to do on a timer, so what is sampled is only the LISTEN set out of /proc/net/tcp:
59
59
  * two file reads, enough to know that the answer changed and worth nobody's while to compute until a view
60
60
  * asks. Change detection and payload are deliberately different sizes here. */
61
61
  { domain: "ports", invalidates: [["ports"]] },
62
62
 
63
- // The agent's Chromiums and the pages each holds open daemon-held records, minted from the hooks that see
63
+ // The agent's Chromiums and the pages each holds open, daemon-held records, minted from the hooks that see
64
64
  // the agent's own browser tool calls, so every change to this roster passes through this process.
65
65
  { domain: "browsers", invalidates: [["browsers"]] },
66
66
 
@@ -72,17 +72,17 @@ const RUNTIME_DOMAINS = [
72
72
  /* The post queue, when the DAEMON moves it rather than the owner. Approving is the owner's own mutation and
73
73
  * refetches itself, but everything after that happens while nobody is touching the page: a held post coming
74
74
  * due, a Discord send landing, a publish turn writing back what went out. Those are the moments the row on
75
- * screen stops being true and this queue is watched precisely because its rows go out in public, so it is
75
+ * screen stops being true, and this queue is watched precisely because its rows go out in public, so it is
76
76
  * the last place to leave someone reading a stale one. */
77
77
  { domain: "drafts", invalidates: [["drafts"]] },
78
78
 
79
- /* WHAT A LANDED AGENT'S WORK IS CALLED the commit message drafted from the diff the moment that work
79
+ /* WHAT A LANDED AGENT'S WORK IS CALLED, the commit message drafted from the diff the moment that work
80
80
  * reaches the main tree (agents/landed-subject.ts), which the review's "From" chip files into the commit box.
81
81
  *
82
82
  * It needs a push of its own because it arrives LATE, and alone. The review refreshes when a turn ends; the
83
83
  * sentence is a model call that STARTS there and answers seconds later, so the refresh the landing itself
84
84
  * causes is always too early to carry it. Nothing followed. The message then sat in the daemon, correct and
85
- * unread, until some unrelated write happened to refresh the panel again and a chip clicked in that window
85
+ * unread, until some unrelated write happened to refresh the panel again, and a chip clicked in that window
86
86
  * filed nothing at all, which is indistinguishable from the feature having been removed.
87
87
  *
88
88
  * Neither a file nor a ref, so this is the only feed that could carry it: the entry holding it lives on
@@ -91,23 +91,23 @@ const RUNTIME_DOMAINS = [
91
91
  * Being in this table also puts the review on the reconnect re-ask (runtimeBoundQueryKeys), and that is the
92
92
  * half of the fix nothing else covers: a publish with no browser connected is dropped, so a landing drafted
93
93
  * while the app was closed or the tunnel was down would otherwise stay invisible for as long as the panel's
94
- * snapshot survived which, at staleTime Infinity, is until something unrelated moved. It costs one review
94
+ * snapshot survived, which, at staleTime Infinity, is until something unrelated moved. It costs one review
95
95
  * read per (re)connect, against a chip that files nothing for the rest of the session. */
96
96
  { domain: "landings", invalidates: [["git", "changes"]] },
97
97
  ] as const satisfies readonly RuntimeDomainBinding[];
98
98
 
99
99
  export const RUNTIME_DOMAIN_BINDINGS: readonly RuntimeDomainBinding[] = RUNTIME_DOMAINS;
100
100
 
101
- /* Every domain this table declares, as a type so a publish site names one of THESE and nothing else, and a
101
+ /* Every domain this table declares, as a type, so a publish site names one of THESE and nothing else, and a
102
102
  * renamed domain is a compile error in the daemon rather than a frame the browser silently routes nowhere. */
103
103
  export type RuntimeDomain = (typeof RUNTIME_DOMAINS)[number]["domain"];
104
104
 
105
- /* Distinct key paths, in table order two domains in one frame routinely feed the same view, and a Set cannot
105
+ /* Distinct key paths, in table order, two domains in one frame routinely feed the same view, and a Set cannot
106
106
  * see that because each path is its own array. Compared by their segments joined, which is exact: a segment is
107
107
  * one identifier from this file, never a caller's string, so there is no separator to collide on. */
108
108
  const dedupe = (keys: readonly QueryKeyPath[]): readonly QueryKeyPath[] => [...new Map(keys.map((key) => [key.join(`/`), key])).values()];
109
109
 
110
- /* The query keys a pushed set of domains makes stale, deduped and stable the browser's `/events` handler
110
+ /* The query keys a pushed set of domains makes stale, deduped and stable, the browser's `/events` handler
111
111
  * calls this, exactly as it calls staleQueryKeys for a path batch. Kept here rather than in the web so the rule
112
112
  * is unit-testable without a query client, and so the daemon can assert against the same table.
113
113
  *
@@ -116,7 +116,7 @@ const dedupe = (keys: readonly QueryKeyPath[]): readonly QueryKeyPath[] => [...n
116
116
  export const staleRuntimeQueryKeys = (domains: readonly string[]): readonly QueryKeyPath[] =>
117
117
  dedupe(RUNTIME_DOMAIN_BINDINGS.filter((binding) => domains.includes(binding.domain)).flatMap((binding) => binding.invalidates));
118
118
 
119
- /* Every query key any runtime domain feeds what a NEW /events connection re-asks wholesale, for precisely the
119
+ /* Every query key any runtime domain feeds, what a NEW /events connection re-asks wholesale, for precisely the
120
120
  * reason fileBoundQueryKeys exists: this push is these views' ONLY live feed, and a frame produced while the
121
121
  * stream was down is a frame nobody will resend. A panel that finished starting, a session that exited, a port
122
122
  * that closed while the browser was away would otherwise sit wrong until the next unrelated change. Re-asking
@@ -3,8 +3,8 @@ import { CapabilitiesListSchema, IpsecVpnConfigSchema, SandboxSettingsSchema } f
3
3
 
4
4
  /* The settings shape spans a version seam that really moves: the browser ships with the platform, the daemon
5
5
  * ships inside the user's sandbox image, so a web build routinely parses a payload from an OLDER daemon. These
6
- * tests pin the property that makes that survivable an absent key is that flag's default, not a parse
7
- * failure because failing instead reaches the user as a settings page whose switches silently do nothing. */
6
+ * tests pin the property that makes that survivable: an absent key is that flag's default, not a parse
7
+ * failure, because failing instead reaches the user as a settings page whose switches silently do nothing. */
8
8
 
9
9
  test("a payload from a build that predates a toggle parses, with the new toggle at its default", () => {
10
10
  // What a daemon built before the output-cleaner backend switch answers with: every key it knew, and
@@ -19,25 +19,25 @@ test("a payload from a build that predates a toggle parses, with the new toggle
19
19
  outputHoldout: 0.1,
20
20
  };
21
21
  // The defaults come from the schema, not from a copy of it written here. Transcribing them made every
22
- // setting the product gained land as a failure in this file a diff that only ever said "the list moved",
22
+ // setting the product gained land as a failure in this file: a diff that only ever said "the list moved",
23
23
  // never "tolerance broke", and whose fix was always to paste the new default in. What this test is about
24
24
  // is the seam: what the old build sent survives verbatim, and what it never heard of arrives at default.
25
25
  expect(SandboxSettingsSchema.parse(older)).toEqual({ ...SandboxSettingsSchema.parse({}), ...older });
26
26
  });
27
27
 
28
28
  /* The invariant a fresh sandbox depends on: NO field is required. A settings object is written for the first
29
- * time only when the user changes something, so until then the daemon parses `{}` one field without a
29
+ * time only when the user changes something, so until then the daemon parses `{}`: one field without a
30
30
  * `.default()` turns that into a throw at boot, and the version tolerance above is built on the same property.
31
31
  *
32
32
  * Asserted by shape rather than by value: what each default IS belongs next to the field in schemas.ts, where
33
33
  * the reason it holds is written down. A second copy here proved nothing the schema didn't already say and
34
34
  * failed on every field the product added. */
35
- test("no field is required a workspace that has never written settings parses", () => {
35
+ test("no field is required: a workspace that has never written settings parses", () => {
36
36
  const defaults = SandboxSettingsSchema.parse({});
37
37
  expect(Object.keys(defaults).toSorted()).toEqual(Object.keys(SandboxSettingsSchema.shape).toSorted());
38
38
  });
39
39
 
40
- test("a key of the wrong type is still a parse failure tolerance is for absence, not for garbage", () => {
40
+ test("a key of the wrong type is still a parse failure: tolerance is for absence, not for garbage", () => {
41
41
  expect(SandboxSettingsSchema.safeParse({ iqSearch: "yes" }).success).toBe(false);
42
42
  expect(SandboxSettingsSchema.safeParse({ outputHoldout: 4 }).success).toBe(false);
43
43
  // The prompt cap is a real bound, not advice: the text IS the system prompt, and every turn pays for it.
@@ -46,7 +46,7 @@ test("a key of the wrong type is still a parse failure — tolerance is for abse
46
46
 
47
47
  /* The capability list crosses the same seam, and its failure mode is worse than a dead switch: the browser
48
48
  * parses ONE object for the whole page, so a required key the older daemon never sends takes the Capabilities
49
- * page down entirely to hide an advisory badge. */
49
+ * page down entirely: to hide an advisory badge. */
50
50
 
51
51
  test("a capability list from a daemon that predates recommendations parses, with none recommended", () => {
52
52
  const older = { capabilities: [{ id: "github", kind: "cli", status: { state: "active" }, config: { provider: "github" } }] };
@@ -66,7 +66,7 @@ test("an ipsec tunnel is a full tunnel unless it says otherwise", () => {
66
66
  test("routed networks take a CIDR list and reject what charon could not load", () => {
67
67
  expect(IpsecVpnConfigSchema.parse({ ...ipsec, routedNetworks: "10.0.0.0/8, 192.168.0.0/16" }).routedNetworks).toBe("10.0.0.0/8, 192.168.0.0/16");
68
68
  expect(IpsecVpnConfigSchema.parse({ ...ipsec, routedNetworks: "fd00::/8" }).routedNetworks).toBe("fd00::/8");
69
- // A bare host address is the easy mistake strongSwan wants the prefix, and the message says so.
69
+ // A bare host address is the easy mistake: strongSwan wants the prefix, and the message says so.
70
70
  expect(IpsecVpnConfigSchema.safeParse({ ...ipsec, routedNetworks: "192.168.0.168" }).success).toBe(false);
71
71
  expect(IpsecVpnConfigSchema.safeParse({ ...ipsec, routedNetworks: "10.0.0.0/8,nonsense" }).success).toBe(false);
72
72
  expect(IpsecVpnConfigSchema.safeParse({ ...ipsec, routedNetworks: "" }).success).toBe(false);