@intentic/sandbox-contract 1.344.0 → 1.345.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 (307) hide show
  1. package/README.md +6 -0
  2. package/dist/contracts/accounts.contract.d.ts +24 -0
  3. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  4. package/dist/contracts/agent.contract.d.ts +214 -6
  5. package/dist/contracts/agent.contract.d.ts.map +1 -1
  6. package/dist/contracts/agent.contract.js +13 -2
  7. package/dist/contracts/agent.contract.js.map +1 -1
  8. package/dist/contracts/agents.contract.d.ts +656 -0
  9. package/dist/contracts/agents.contract.d.ts.map +1 -1
  10. package/dist/contracts/agents.contract.js +5 -3
  11. package/dist/contracts/agents.contract.js.map +1 -1
  12. package/dist/contracts/device.contract.d.ts +242 -0
  13. package/dist/contracts/device.contract.d.ts.map +1 -1
  14. package/dist/contracts/device.contract.js +5 -1
  15. package/dist/contracts/device.contract.js.map +1 -1
  16. package/dist/contracts/endpoints.contract.d.ts +2 -0
  17. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  18. package/dist/contracts/endpoints.contract.js +1 -0
  19. package/dist/contracts/endpoints.contract.js.map +1 -1
  20. package/dist/contracts/push.contract.d.ts +11 -0
  21. package/dist/contracts/push.contract.d.ts.map +1 -1
  22. package/dist/contracts/push.contract.js +10 -1
  23. package/dist/contracts/push.contract.js.map +1 -1
  24. package/dist/contracts/runner.contract.d.ts +15 -0
  25. package/dist/contracts/runner.contract.d.ts.map +1 -1
  26. package/dist/contracts/secrets.contract.d.ts +169 -0
  27. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  28. package/dist/contracts/secrets.contract.js +35 -0
  29. package/dist/contracts/secrets.contract.js.map +1 -1
  30. package/dist/contracts/sessions.contract.d.ts +2 -0
  31. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  32. package/dist/contracts/settings.contract.d.ts +33 -0
  33. package/dist/contracts/settings.contract.d.ts.map +1 -1
  34. package/dist/contracts/system.contract.d.ts +133 -30
  35. package/dist/contracts/system.contract.d.ts.map +1 -1
  36. package/dist/contracts/translator.contract.d.ts +24 -0
  37. package/dist/contracts/translator.contract.d.ts.map +1 -1
  38. package/dist/contracts/workspace.contract.d.ts +11 -0
  39. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  40. package/dist/contracts/workspace.contract.js +11 -1
  41. package/dist/contracts/workspace.contract.js.map +1 -1
  42. package/dist/events/agent-events.d.ts +17 -0
  43. package/dist/events/agent-events.d.ts.map +1 -1
  44. package/dist/events/agent-events.js +2 -1
  45. package/dist/events/agent-events.js.map +1 -1
  46. package/dist/events/agent-transcript.d.ts +727 -0
  47. package/dist/events/agent-transcript.d.ts.map +1 -0
  48. package/dist/events/agent-transcript.js +34 -0
  49. package/dist/events/agent-transcript.js.map +1 -0
  50. package/dist/events/agent-words.d.ts +5 -0
  51. package/dist/events/agent-words.d.ts.map +1 -1
  52. package/dist/events/agent-words.js +9 -0
  53. package/dist/events/agent-words.js.map +1 -1
  54. package/dist/events/requests.d.ts +143 -0
  55. package/dist/events/requests.d.ts.map +1 -1
  56. package/dist/events/requests.js +35 -1
  57. package/dist/events/requests.js.map +1 -1
  58. package/dist/events/sandbox-notice.d.ts +7 -0
  59. package/dist/events/sandbox-notice.d.ts.map +1 -1
  60. package/dist/events/sandbox-notice.js +1 -0
  61. package/dist/events/sandbox-notice.js.map +1 -1
  62. package/dist/events/system-events.d.ts +88 -0
  63. package/dist/events/system-events.d.ts.map +1 -1
  64. package/dist/events/transcript.d.ts +20 -720
  65. package/dist/events/transcript.d.ts.map +1 -1
  66. package/dist/events/transcript.js +17 -19
  67. package/dist/events/transcript.js.map +1 -1
  68. package/dist/events/undelivered.d.ts +4 -0
  69. package/dist/events/undelivered.d.ts.map +1 -1
  70. package/dist/events/undelivered.js +13 -0
  71. package/dist/events/undelivered.js.map +1 -1
  72. package/dist/ids/conversation-ids.d.ts +2 -0
  73. package/dist/ids/conversation-ids.d.ts.map +1 -1
  74. package/dist/ids/conversation-ids.js +2 -0
  75. package/dist/ids/conversation-ids.js.map +1 -1
  76. package/dist/ids/row-ids.d.ts +3 -0
  77. package/dist/ids/row-ids.d.ts.map +1 -0
  78. package/dist/ids/row-ids.js +11 -0
  79. package/dist/ids/row-ids.js.map +1 -0
  80. package/dist/index.d.ts +1281 -39
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/models/agent-lanes.d.ts.map +1 -1
  83. package/dist/models/agent-lanes.js +3 -2
  84. package/dist/models/agent-lanes.js.map +1 -1
  85. package/dist/models/agent-standing.d.ts +4 -0
  86. package/dist/models/agent-standing.d.ts.map +1 -1
  87. package/dist/models/agent-standing.js +2 -1
  88. package/dist/models/agent-standing.js.map +1 -1
  89. package/dist/models/plan-pools.d.ts +5 -1
  90. package/dist/models/plan-pools.d.ts.map +1 -1
  91. package/dist/models/plan-pools.js +57 -21
  92. package/dist/models/plan-pools.js.map +1 -1
  93. package/dist/netd/generated/wire.d.ts +5 -0
  94. package/dist/netd/generated/wire.d.ts.map +1 -1
  95. package/dist/policy/command-classes.d.ts +1 -1
  96. package/dist/policy/command-classes.d.ts.map +1 -1
  97. package/dist/policy/command-classes.js +2 -2
  98. package/dist/policy/command-classes.js.map +1 -1
  99. package/dist/policy/keyring-origins.d.ts +27 -0
  100. package/dist/policy/keyring-origins.d.ts.map +1 -0
  101. package/dist/policy/keyring-origins.js +213 -0
  102. package/dist/policy/keyring-origins.js.map +1 -0
  103. package/dist/policy/keyring-seal.d.ts +9 -0
  104. package/dist/policy/keyring-seal.d.ts.map +1 -1
  105. package/dist/policy/keyring-seal.js +18 -6
  106. package/dist/policy/keyring-seal.js.map +1 -1
  107. package/dist/policy/safety-policy.d.ts +1 -1
  108. package/dist/policy/safety-policy.d.ts.map +1 -1
  109. package/dist/policy/safety-policy.js +1 -1
  110. package/dist/policy/safety-policy.js.map +1 -1
  111. package/dist/policy/turned-away.d.ts +1 -0
  112. package/dist/policy/turned-away.d.ts.map +1 -1
  113. package/dist/policy/turned-away.js +10 -0
  114. package/dist/policy/turned-away.js.map +1 -1
  115. package/dist/protocol/doors/host-protocol.d.ts +1 -0
  116. package/dist/protocol/doors/host-protocol.d.ts.map +1 -1
  117. package/dist/protocol/doors/host-protocol.js +1 -0
  118. package/dist/protocol/doors/host-protocol.js.map +1 -1
  119. package/dist/protocol/doors/webext-links.d.ts +1 -0
  120. package/dist/protocol/doors/webext-links.d.ts.map +1 -1
  121. package/dist/protocol/doors/webext-links.js +1 -0
  122. package/dist/protocol/doors/webext-links.js.map +1 -1
  123. package/dist/protocol/peer-dial.d.ts +2 -0
  124. package/dist/protocol/peer-dial.d.ts.map +1 -1
  125. package/dist/protocol/peer-dial.js +62 -29
  126. package/dist/protocol/peer-dial.js.map +1 -1
  127. package/dist/protocol/raw/raw-json-routes.d.ts +176 -0
  128. package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
  129. package/dist/protocol/raw/raw-json-routes.js +5 -0
  130. package/dist/protocol/raw/raw-json-routes.js.map +1 -1
  131. package/dist/protocol/raw/raw-routes.d.ts +31 -0
  132. package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
  133. package/dist/protocol/raw/raw-routes.js +8 -0
  134. package/dist/protocol/raw/raw-routes.js.map +1 -1
  135. package/dist/schemas/agent-place.d.ts +10 -0
  136. package/dist/schemas/agent-place.d.ts.map +1 -0
  137. package/dist/schemas/agent-place.js +15 -0
  138. package/dist/schemas/agent-place.js.map +1 -0
  139. package/dist/schemas/agent.d.ts +116 -83
  140. package/dist/schemas/agent.d.ts.map +1 -1
  141. package/dist/schemas/agent.js +26 -39
  142. package/dist/schemas/agent.js.map +1 -1
  143. package/dist/schemas/agents-list.d.ts +38 -0
  144. package/dist/schemas/agents-list.d.ts.map +1 -1
  145. package/dist/schemas/agents.d.ts +117 -16
  146. package/dist/schemas/agents.d.ts.map +1 -1
  147. package/dist/schemas/agents.js +19 -21
  148. package/dist/schemas/agents.js.map +1 -1
  149. package/dist/schemas/attach.d.ts +8 -0
  150. package/dist/schemas/attach.d.ts.map +1 -0
  151. package/dist/schemas/attach.js +14 -0
  152. package/dist/schemas/attach.js.map +1 -0
  153. package/dist/schemas/command-admission.d.ts +71 -0
  154. package/dist/schemas/command-admission.d.ts.map +1 -0
  155. package/dist/schemas/command-admission.js +27 -0
  156. package/dist/schemas/command-admission.js.map +1 -0
  157. package/dist/schemas/conversation-links.d.ts +44 -0
  158. package/dist/schemas/conversation-links.d.ts.map +1 -0
  159. package/dist/schemas/conversation-links.js +35 -0
  160. package/dist/schemas/conversation-links.js.map +1 -0
  161. package/dist/schemas/data-protection.d.ts +1 -1
  162. package/dist/schemas/data-protection.d.ts.map +1 -1
  163. package/dist/schemas/devices.d.ts +4 -1
  164. package/dist/schemas/devices.d.ts.map +1 -1
  165. package/dist/schemas/devices.js +1 -0
  166. package/dist/schemas/devices.js.map +1 -1
  167. package/dist/schemas/hosts.d.ts +2 -0
  168. package/dist/schemas/hosts.d.ts.map +1 -1
  169. package/dist/schemas/hosts.js +2 -0
  170. package/dist/schemas/hosts.js.map +1 -1
  171. package/dist/schemas/keyring-native.d.ts +137 -0
  172. package/dist/schemas/keyring-native.d.ts.map +1 -0
  173. package/dist/schemas/keyring-native.js +69 -0
  174. package/dist/schemas/keyring-native.js.map +1 -0
  175. package/dist/schemas/keyring.d.ts +413 -0
  176. package/dist/schemas/keyring.d.ts.map +1 -1
  177. package/dist/schemas/keyring.js +139 -4
  178. package/dist/schemas/keyring.js.map +1 -1
  179. package/dist/schemas/phone-passwords.d.ts +186 -0
  180. package/dist/schemas/phone-passwords.d.ts.map +1 -0
  181. package/dist/schemas/phone-passwords.js +25 -0
  182. package/dist/schemas/phone-passwords.js.map +1 -0
  183. package/dist/schemas/providers/plan-limits.d.ts +74 -0
  184. package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
  185. package/dist/schemas/providers/plan-limits.js +31 -0
  186. package/dist/schemas/providers/plan-limits.js.map +1 -1
  187. package/dist/schemas/providers/provider-oauth.d.ts +24 -0
  188. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  189. package/dist/schemas/providers/usage.d.ts +1 -0
  190. package/dist/schemas/providers/usage.d.ts.map +1 -1
  191. package/dist/schemas/providers/usage.js +1 -0
  192. package/dist/schemas/providers/usage.js.map +1 -1
  193. package/dist/schemas/push.d.ts +13 -0
  194. package/dist/schemas/push.d.ts.map +1 -1
  195. package/dist/schemas/push.js +11 -1
  196. package/dist/schemas/push.js.map +1 -1
  197. package/dist/schemas/sandbox-passwords.d.ts +288 -0
  198. package/dist/schemas/sandbox-passwords.d.ts.map +1 -0
  199. package/dist/schemas/sandbox-passwords.js +66 -0
  200. package/dist/schemas/sandbox-passwords.js.map +1 -0
  201. package/dist/schemas/secrets.d.ts +2 -0
  202. package/dist/schemas/secrets.d.ts.map +1 -1
  203. package/dist/schemas/secrets.js +4 -0
  204. package/dist/schemas/secrets.js.map +1 -1
  205. package/dist/schemas/settings-savings.d.ts +709 -0
  206. package/dist/schemas/settings-savings.d.ts.map +1 -0
  207. package/dist/schemas/settings-savings.js +53 -0
  208. package/dist/schemas/settings-savings.js.map +1 -0
  209. package/dist/schemas/settings.d.ts +3 -671
  210. package/dist/schemas/settings.d.ts.map +1 -1
  211. package/dist/schemas/settings.js +3 -50
  212. package/dist/schemas/settings.js.map +1 -1
  213. package/dist/schemas/terminal.d.ts +5 -0
  214. package/dist/schemas/terminal.d.ts.map +1 -1
  215. package/dist/schemas/terminal.js +4 -0
  216. package/dist/schemas/terminal.js.map +1 -1
  217. package/dist/schemas/work-assurance.d.ts +24 -0
  218. package/dist/schemas/work-assurance.d.ts.map +1 -0
  219. package/dist/schemas/work-assurance.js +23 -0
  220. package/dist/schemas/work-assurance.js.map +1 -0
  221. package/dist/schemas/workspace/workspace-tree.d.ts +23 -0
  222. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  223. package/dist/schemas/workspace/workspace-tree.js +20 -0
  224. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  225. package/dist/state/auth-state.d.ts +4 -0
  226. package/dist/state/auth-state.d.ts.map +1 -0
  227. package/dist/state/auth-state.js +37 -0
  228. package/dist/state/auth-state.js.map +1 -0
  229. package/dist/state/definition.d.ts +4 -0
  230. package/dist/state/definition.d.ts.map +1 -1
  231. package/dist/state/history-state.d.ts.map +1 -1
  232. package/dist/state/history-state.js +17 -1
  233. package/dist/state/history-state.js.map +1 -1
  234. package/dist/state/runtime-state.d.ts.map +1 -1
  235. package/dist/state/runtime-state.js.map +1 -1
  236. package/dist/text/transcript-fold.js +5 -5
  237. package/dist/text/transcript-fold.js.map +1 -1
  238. package/dist/webext/index.d.ts +1 -0
  239. package/dist/webext/index.d.ts.map +1 -1
  240. package/dist/webext/index.js +1 -0
  241. package/dist/webext/index.js.map +1 -1
  242. package/package.json +6 -5
  243. package/src/contracts/agent.contract.ts +13 -1
  244. package/src/contracts/agents.contract.ts +4 -3
  245. package/src/contracts/device.contract.ts +16 -0
  246. package/src/contracts/endpoints.contract.ts +3 -0
  247. package/src/contracts/push.contract.ts +23 -1
  248. package/src/contracts/secrets.contract.ts +39 -0
  249. package/src/contracts/workspace.contract.ts +16 -0
  250. package/src/events/agent-events.ts +4 -1
  251. package/src/events/agent-transcript.ts +48 -0
  252. package/src/events/agent-words.ts +13 -0
  253. package/src/events/requests.ts +41 -3
  254. package/src/events/sandbox-notice.ts +3 -0
  255. package/src/events/transcript.ts +30 -28
  256. package/src/events/undelivered.ts +18 -0
  257. package/src/ids/conversation-ids.ts +7 -0
  258. package/src/ids/row-ids.ts +20 -0
  259. package/src/models/agent-lanes.ts +9 -3
  260. package/src/models/agent-standing.ts +9 -1
  261. package/src/models/plan-pools.test.ts +128 -20
  262. package/src/models/plan-pools.ts +114 -31
  263. package/src/netd/generated/netd-wire.json +25 -0
  264. package/src/netd/generated/wire.ts +1 -1
  265. package/src/policy/command-classes.ts +2 -2
  266. package/src/policy/keyring-origins.test.ts +132 -0
  267. package/src/policy/keyring-origins.ts +287 -0
  268. package/src/policy/keyring-seal.test.ts +37 -2
  269. package/src/policy/keyring-seal.ts +33 -7
  270. package/src/policy/safety-policy.ts +1 -1
  271. package/src/policy/tests/safety-policy.test.ts +1 -1
  272. package/src/policy/turned-away.ts +15 -0
  273. package/src/protocol/doors/host-protocol.ts +3 -0
  274. package/src/protocol/doors/webext-links.ts +3 -0
  275. package/src/protocol/peer-dial.test.ts +90 -4
  276. package/src/protocol/peer-dial.ts +83 -39
  277. package/src/protocol/raw/raw-json-routes.ts +13 -0
  278. package/src/protocol/raw/raw-routes.ts +12 -1
  279. package/src/schemas/agent-place.ts +22 -0
  280. package/src/schemas/agent.ts +42 -78
  281. package/src/schemas/agents.ts +41 -46
  282. package/src/schemas/attach.ts +23 -0
  283. package/src/schemas/command-admission.ts +57 -0
  284. package/src/schemas/conversation-links.ts +62 -0
  285. package/src/schemas/data-protection.ts +1 -1
  286. package/src/schemas/devices.ts +4 -4
  287. package/src/schemas/hosts.ts +4 -0
  288. package/src/schemas/keyring-native.test.ts +13 -0
  289. package/src/schemas/keyring-native.ts +101 -0
  290. package/src/schemas/keyring.ts +202 -4
  291. package/src/schemas/phone-passwords.ts +51 -0
  292. package/src/schemas/providers/plan-limits.ts +49 -2
  293. package/src/schemas/providers/usage.ts +2 -0
  294. package/src/schemas/push.ts +19 -3
  295. package/src/schemas/sandbox-passwords.ts +88 -0
  296. package/src/schemas/secrets.ts +4 -0
  297. package/src/schemas/settings-savings.ts +125 -0
  298. package/src/schemas/settings.ts +8 -116
  299. package/src/schemas/terminal.ts +8 -0
  300. package/src/schemas/work-assurance.ts +36 -0
  301. package/src/schemas/workspace/workspace-tree.ts +32 -0
  302. package/src/state/auth-state.ts +89 -0
  303. package/src/state/history-state.ts +44 -1
  304. package/src/state/runtime-state.ts +5 -4
  305. package/src/text/transcript-fold.test.ts +67 -0
  306. package/src/text/transcript-fold.ts +6 -6
  307. package/src/webext/index.ts +2 -0
@@ -58,8 +58,9 @@ export interface PeerDialSpec<S extends SocketLike> {
58
58
  readonly hello: () => Promise<Record<string, unknown>> | Record<string, unknown>;
59
59
  // Hand the open socket to the oRPC handler serving the door's contract.
60
60
  readonly attach: (socket: S) => void;
61
- // The retry ladder: how long to wait after a drop, given how long the socket had held.
62
- readonly backoff: { readonly next: (heldMs: number) => number };
61
+ // The retry ladder: how long to wait after a drop, given how long the socket had held; `reset` puts it back on its
62
+ // floor (`retryNow`).
63
+ readonly backoff: { readonly next: (heldMs: number) => number; readonly reset: () => void };
63
64
  /* How long this socket may hear nothing before the link is presumed dead: `peerLinkSilenceMs` of the heartbeat the door's own hub pings on. */
64
65
  readonly silenceMs: number;
65
66
  readonly log: (message: string) => void;
@@ -86,8 +87,71 @@ export interface PeerLink {
86
87
  readonly stop: (reason?: string) => void;
87
88
  readonly state: () => PeerLinkState;
88
89
  readonly outage: () => PeerOutage | undefined;
90
+ /* SOMEBODY KNOWS THE FAR END IS BACK, so the link need not wait to find out (2026-10-19). A machine agent that has
91
+ just woken a sleeping sandbox knows it is starting, but the link to it had spent the sleep on the ladder: past
92
+ LONG_OUTAGE_ATTEMPTS each try rests a quarter of an hour, and on rog a sandbox woken at 16:48 had its link back at
93
+ 17:12, 24 minutes with no device tools. This ends a wait on the ladder (the long rest included) and dials now,
94
+ and puts the outage behind it as an answer would (`failures`, the quiet rule, its `since`, the ladder's rung), so
95
+ a dial that finds the sandbox still booting retries on the fast floor. A dial in flight is left to finish and
96
+ only its ladder is reset, so a failure of it retries fast too; an open or stopped link is untouched. */
97
+ readonly retryNow: () => void;
89
98
  }
90
99
 
100
+ /* ONE OUTAGE'S BOOKKEEPING: the attempts that have failed since the link was last open, when the first of them was, and
101
+ what each may say (the quiet rule above). An answer clears it, and so does `retryNow`. */
102
+ const failureRun = (log: (message: string) => void) => {
103
+ let failures = 0;
104
+ // When the loop last complained out loud, and when the CURRENT outage began.
105
+ let quietSince = 0;
106
+ let since: number | undefined;
107
+ return {
108
+ count: (): number => failures,
109
+ // One more failed attempt; answers how many that makes.
110
+ fail: (): number => {
111
+ failures += 1;
112
+ since ??= Date.now();
113
+ return failures;
114
+ },
115
+ /* What ONE failed attempt is allowed to say. */
116
+ complain: (said: string, delay: number): void => {
117
+ const every = `retrying every ${Math.round(delay / 1000)}s`;
118
+ if (failures <= LOUD_ATTEMPTS) {
119
+ log(`${said}; reconnecting in ${Math.round(delay / 1000)}s`);
120
+ return;
121
+ }
122
+ if (failures === LOUD_ATTEMPTS + 1) {
123
+ quietSince = Date.now();
124
+ log(`${said}; still nothing after ${failures} attempts — ${every}, and saying so at most every ${Math.round(QUIET_LOG_MS / 60_000)} minutes from here`);
125
+ return;
126
+ }
127
+ if (Date.now() - quietSince >= QUIET_LOG_MS) {
128
+ quietSince = Date.now();
129
+ log(`${said}; ${failures} failed attempts, ${every}`);
130
+ }
131
+ },
132
+ clear: (): void => {
133
+ failures = 0;
134
+ quietSince = 0;
135
+ since = undefined;
136
+ },
137
+ reading: (): PeerOutage | undefined => (since === undefined ? undefined : { failures, since }),
138
+ };
139
+ };
140
+
141
+ /* THE WAIT ON THE LADDER hangs off the loop's stop, so a link stopped during it is done at once and never dials again,
142
+ and off a controller of its own, which `retryNow` aborts to dial at once. Cutting a wait already over does nothing. */
143
+ const ladderWaits = (stopping: AbortSignal) => {
144
+ let resting: AbortController | undefined;
145
+ return {
146
+ wait: (delay: number, dial: () => Promise<void>): void => {
147
+ const rest = new AbortController();
148
+ resting = rest;
149
+ void sleep(delay, { signal: anySignal(stopping, rest.signal) }).then(() => (stopping.aborted ? undefined : dial()));
150
+ },
151
+ cut: (): void => resting?.abort(),
152
+ };
153
+ };
154
+
91
155
  export const dialPeer = <S extends SocketLike>(spec: PeerDialSpec<S>): PeerLink => {
92
156
  // The loop's whole life: every way it ends (a stop, a revocation, a pairing gone) aborts this, and everything the
93
157
  // loop arms hangs off it, so nothing it left behind can dial, wait or fire once it is over. A stopped link that
@@ -96,32 +160,8 @@ export const dialPeer = <S extends SocketLike>(spec: PeerDialSpec<S>): PeerLink
96
160
  const done = new Promise<void>((resolve) => void whenAborted(stopping.signal, () => resolve()));
97
161
  let socket: S | undefined;
98
162
  let openedAt: number | undefined;
99
- // Consecutive attempts that have failed since this link was last open, and when the loop last complained out
100
- // loud: between them they are the whole of the quiet rule above.
101
- let failures = 0;
102
- let quietSince = 0;
103
- // When the CURRENT outage began, as opposed to when this link was last complained about.
104
- let failingSince: number | undefined;
105
-
106
- /* What ONE failed attempt is allowed to say. */
107
- const complain = (said: string, delay: number): void => {
108
- const every = `retrying every ${Math.round(delay / 1000)}s`;
109
- if (failures <= LOUD_ATTEMPTS) {
110
- spec.log(`${said}; reconnecting in ${Math.round(delay / 1000)}s`);
111
- return;
112
- }
113
- if (failures === LOUD_ATTEMPTS + 1) {
114
- quietSince = Date.now();
115
- spec.log(
116
- `${said}; still nothing after ${failures} attempts — ${every}, and saying so at most every ${Math.round(QUIET_LOG_MS / 60_000)} minutes from here`,
117
- );
118
- return;
119
- }
120
- if (Date.now() - quietSince >= QUIET_LOG_MS) {
121
- quietSince = Date.now();
122
- spec.log(`${said}; ${failures} failed attempts, ${every}`);
123
- }
124
- };
163
+ const outage = failureRun(spec.log);
164
+ const ladder = ladderWaits(stopping.signal);
125
165
 
126
166
  const open = async (): Promise<void> => {
127
167
  const attempt = await spec.open(stopping.signal);
@@ -155,14 +195,11 @@ export const dialPeer = <S extends SocketLike>(spec: PeerDialSpec<S>): PeerLink
155
195
  }
156
196
  const rung = spec.backoff.next(openedAt === undefined ? 0 : Date.now() - openedAt);
157
197
  openedAt = undefined;
158
- failures += 1;
159
- failingSince ??= Date.now();
160
198
  // The rest is added to the ladder's draw rather than replacing it: a flat fifteen minutes would bring back in
161
199
  // lockstep every link that entered the long outage together.
162
- const delay = failures >= LONG_OUTAGE_ATTEMPTS ? LONG_OUTAGE_MS + rung : rung;
163
- complain(said, delay);
164
- // The wait hangs off the stop: a link stopped during it is done at once and never dials again.
165
- void sleep(delay, { signal: stopping.signal }).then(() => (stopping.signal.aborted ? undefined : open()));
200
+ const delay = outage.fail() >= LONG_OUTAGE_ATTEMPTS ? LONG_OUTAGE_MS + rung : rung;
201
+ outage.complain(said, delay);
202
+ ladder.wait(delay, open);
166
203
  };
167
204
 
168
205
  const arm = (): void => {
@@ -189,9 +226,7 @@ export const dialPeer = <S extends SocketLike>(spec: PeerDialSpec<S>): PeerLink
189
226
  openedAt = Date.now();
190
227
  // A link that is up owes nothing to the failures behind it: the next outage is news again, and the
191
228
  // "connected to …" line this open is about to log is what reports the recovery.
192
- failures = 0;
193
- quietSince = 0;
194
- failingSince = undefined;
229
+ outage.clear();
195
230
  arm();
196
231
  spec.attach(ws);
197
232
  const send = (hello: Record<string, unknown>): void => {
@@ -230,7 +265,7 @@ export const dialPeer = <S extends SocketLike>(spec: PeerDialSpec<S>): PeerLink
230
265
 
231
266
  /* Socket errors record a cause; the close event owns retry scheduling. */
232
267
  ws.addEventListener("error", () => {
233
- if (failures < LOUD_ATTEMPTS) {
268
+ if (outage.count() < LOUD_ATTEMPTS) {
234
269
  spec.log("connection error");
235
270
  }
236
271
  });
@@ -251,6 +286,15 @@ export const dialPeer = <S extends SocketLike>(spec: PeerDialSpec<S>): PeerLink
251
286
  // ladder) and a finished one is closed: read off the loop itself, not a flag kept beside it that a redial
252
287
  // firing after the stop could set back.
253
288
  state: () => (socket?.readyState === OPEN ? "open" : stopping.signal.aborted ? "closed" : "connecting"),
254
- outage: () => (failingSince === undefined ? undefined : { failures, since: failingSince }),
289
+ outage: outage.reading,
290
+ retryNow: () => {
291
+ if (stopping.signal.aborted || socket?.readyState === OPEN) {
292
+ return;
293
+ }
294
+ outage.clear();
295
+ spec.backoff.reset();
296
+ // Waiting on the ladder: the wait ends now and its dial follows. A dial in flight has no wait to end.
297
+ ladder.cut();
298
+ },
255
299
  };
256
300
  };
@@ -17,6 +17,15 @@ import { HostsListSchema } from "../../schemas/hosts.js";
17
17
  import { VoicesViewSchema } from "../../schemas/voice.js";
18
18
  import { MemberEmailSchema, MemberGrantSchema, MemberSelfSchema, MembersListSchema, MembersRosterSchema } from "../../schemas/members.js";
19
19
  import { PhonesListSchema, PhoneWakeRegistrationSchema } from "../../schemas/phone.js";
20
+ import {
21
+ PhoneKeyringsResultSchema,
22
+ PhonePasswordsCaptureResultSchema,
23
+ PhonePasswordsCaptureSchema,
24
+ PhonePasswordsFillResultSchema,
25
+ PhonePasswordsFillSchema,
26
+ PhonePasswordsPairResultSchema,
27
+ PhonePasswordsPairSchema,
28
+ } from "../../schemas/phone-passwords.js";
20
29
  import { OkSchema } from "../../schemas/shared.js";
21
30
  import { EnrollCodeAnswerSchema, EnrollCodeRequestSchema, PeerPairingAnswerSchema, SyncPairAnswerSchema, SyncPairRequestSchema } from "../../schemas/short-codes.js";
22
31
  import { SpeechStatusSchema, SpeechTranscriptSchema, SpeechWakeClaimSchema, SpeechWakeGrantSchema } from "../../schemas/speech.js";
@@ -133,6 +142,10 @@ export const RAW_JSON_ROUTES = {
133
142
  "POST /system/phones/pair": { output: PeerPairingAnswerSchema },
134
143
  "DELETE /system/phones/{id}": { output: OkSchema },
135
144
  "POST /system/phones/{id}/wake": { input: PhoneWakeRegistrationSchema, output: OkSchema },
145
+ "GET /system/phones/passwords/keyrings": { output: PhoneKeyringsResultSchema },
146
+ "POST /system/phones/passwords/pair": { input: PhonePasswordsPairSchema, output: PhonePasswordsPairResultSchema },
147
+ "POST /system/phones/passwords/capture": { input: PhonePasswordsCaptureSchema, output: PhonePasswordsCaptureResultSchema },
148
+ "POST /system/phones/passwords/fill": { input: PhonePasswordsFillSchema, output: PhonePasswordsFillResultSchema },
136
149
  "GET /system/runners": { output: RunnersListSchema },
137
150
  "DELETE /system/runners/{id}": { output: OkSchema },
138
151
  "POST /system/runners/{id}/definition/sync": { output: RunnerDefinitionAppliedSchema },
@@ -13,6 +13,8 @@ export const RAW_ROUTES = {
13
13
  // A person opening this sandbox's own address in a browser: sent to the app's /open on it, the address being what
14
14
  // they may still hold when the platform has lost the rest. Anything else gets the same 401 as before it existed.
15
15
  "GET /": { auth: "door", beforeBoot: true },
16
+ // The tab icon a browser asks for on that address: the lotus in the sandbox green, the same bytes for everyone.
17
+ "GET /favicon.ico": { auth: "door", beforeBoot: true },
16
18
  "GET /diff/raw": { lane: "bulk" },
17
19
  // Arming the dictation model is a read any tier makes; dictating is writing a message, the collaborator's grant.
18
20
  "GET /speech/status": { guest: true },
@@ -149,9 +151,11 @@ export const RAW_ROUTES = {
149
151
  "POST /children/delegate": { agent: true, control: "never" },
150
152
  "GET /children/links": { agent: true, control: "never" },
151
153
  "GET /children": { agent: true, control: "never" },
152
- // The fleet reads, and the one write: words in front of another conversation, which a person can do by typing.
154
+ // The fleet reads, and its two writes: words in front of another conversation, which a person can do by typing, and
155
+ // the Continue a person can press on one a spent allowance or a stop held (`agents resume`).
153
156
  "GET /fleet": { agent: true },
154
157
  "POST /fleet/message": { agent: true },
158
+ "POST /fleet/resume": { agent: true },
155
159
  "GET /fleet/{handle}": { agent: true },
156
160
  // The `devices push` CLI: a program the agent built, carried to one of the owner's computers in chunks; held to the
157
161
  // devices the calling conversation's turn mounts, and refused by the machine itself unless "Run programs" is on.
@@ -192,6 +196,7 @@ export const RAW_ROUTES = {
192
196
  "DELETE /system/hosts/{id}": { control: "never" },
193
197
  // A peer's socket reconnects on its own backoff, so it answers before boot.
194
198
  "GET /system/hosts/connect": { auth: "door", beforeBoot: true, control: "never" },
199
+ "POST /system/hosts/keyring-changed": { auth: "door", control: "never" },
195
200
  "POST /system/webext/pair": { control: "never" },
196
201
  "POST /system/webext/enroll": { auth: "door", control: "never" },
197
202
  "GET /system/webext": { control: "never" },
@@ -204,6 +209,11 @@ export const RAW_ROUTES = {
204
209
  "GET /system/phones/connect": { auth: "door", beforeBoot: true, control: "never" },
205
210
  // The editor hands over the push-relay channel it registered for a phone, so the sandbox can wake it.
206
211
  "POST /system/phones/{id}/wake": { control: "never" },
212
+ // The phone as Android's autofill service (schemas/phone-passwords.ts): each checks the phone's enrollment token itself.
213
+ "GET /system/phones/passwords/keyrings": { auth: "door", control: "never" },
214
+ "POST /system/phones/passwords/pair": { auth: "door", control: "never" },
215
+ "POST /system/phones/passwords/capture": { auth: "door", control: "never" },
216
+ "POST /system/phones/passwords/fill": { auth: "door", control: "never" },
207
217
  "POST /system/runners/pair": { control: "never" },
208
218
  "POST /system/runners/enroll": { auth: "door", control: "never" },
209
219
  "GET /system/runners": { control: "never" },
@@ -219,6 +229,7 @@ export const RAW_ROUTES = {
219
229
  // A secret the browser types for the agent (`type_secret` aimed at it): one value for one ticket the daemon minted
220
230
  // for this browser and one host, handed back on the browser's own request rather than over the tool socket.
221
231
  "POST /system/webext/secret": { auth: "door", control: "never" },
232
+ "POST /system/webext/grants-changed": { auth: "door", control: "never" },
222
233
  // This sandbox's keyring keys (schemas/keyring.ts), public halves only: a keyring fetches them here over TLS, from the
223
234
  // sandbox's own address, before it shares anything with it, rather than take them from whoever relays the share.
224
235
  "GET /keyring/keys": { auth: "door", control: "never" },
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+
3
+ // Words put in an agent's mouth (`agents.place`): what is asked, and what the record answers.
4
+
5
+ // Bounded just above the handoff's per-message render cap, so a line too long to carry whole doesn't reach the agent
6
+ // truncated.
7
+ export const AgentPlaceSchema = z.object({
8
+ id: z.string().min(1).describe("Which conversation."),
9
+ text: z
10
+ .string()
11
+ .trim()
12
+ .min(1)
13
+ .max(8_000)
14
+ .describe(
15
+ "The words to put in the agent's mouth. Bounded just above what the next turn can carry whole, because a line too long to be handed over intact would reach the agent truncated and quietly break the very thing this is for.",
16
+ ),
17
+ });
18
+ // The placed line's own id, so the window that placed it holds the same row its next read of the record brings.
19
+ export const AgentPlacedSchema = z.object({
20
+ ok: z.literal(true).describe("Always true: the line is on the record."),
21
+ rowId: z.string().optional().describe("The `rowId` the line was recorded under. Absent from a sandbox older than row ids."),
22
+ });
@@ -1,12 +1,13 @@
1
1
  import { TurnErrandSchema } from "./speaker.js";
2
2
  import { z } from "zod";
3
- import { CONVERSATION_ID } from "../ids/conversation-ids.js";
3
+ import { CONVERSATION_ID, CONVERSATION_TITLE_MAX } from "../ids/conversation-ids.js";
4
4
  import { canonicalHarness } from "../models/agent-catalog.js";
5
5
  import { ModelRoleSchema } from "../models/model-roles.js";
6
6
  import { NATIVE_PROVIDERS } from "../models/provider-specs.js";
7
7
  import { AgentPlacementSchema } from "../protocol/doors/runner-protocol.js";
8
8
  import { MENTION_LIMIT } from "../text/mentions.js";
9
9
  import { entryId } from "./internal.js";
10
+ import { FRESH_OF_DESCRIPTION, FreshOfSchema, freshOfIssues } from "./conversation-links.js";
10
11
  // Agent runtimes the daemon can serve: native providers have dedicated adapters, `endpoint/<id>` names an installed
11
12
  // endpoint capability, anything else is an ACP agent capability id. A bare string, not an enum, so an unknown id is a
12
13
  // clean error, not a contract change.
@@ -66,76 +67,14 @@ export const AgentOriginSchema = z.object({
66
67
  author: z.string().optional(),
67
68
  });
68
69
  export type AgentOrigin = z.infer<typeof AgentOriginSchema>;
69
- // The class of thing asking to start a session, the admission policy's key space; derived daemon-side, never sent by a
70
- // client. Chat and loops are absent since both begin with the owner's own click.
71
- export const WakeSourceSchema = z.enum(["schedule", "event", "listener", "webchat", "issues", "workspace", "workflow"]);
72
- export type WakeSource = z.infer<typeof WakeSourceSchema>;
73
- // One admission verdict the owner can configure: let it run, hold it for approval, or refuse it outright.
74
- export const AdmissionRuleSchema = z.enum(["allow", "hold", "deny"]);
75
- export type AdmissionRule = z.infer<typeof AdmissionRuleSchema>;
76
- // Where a command would run; the same string means different things on each machine (rm -rf /usr is recoverable here,
77
- // fatal on a laptop). Read with a locus (command-classes.ts CommandContext); callers must state one rather than
78
- // default.
79
- export const CommandLocusSchema = z.enum([
80
- // This sandbox's own shell: a disposable container, /work a git worktree, /history every other agent's.
81
- "sandbox",
82
- // One of the owner's own computers, reached through the machine agent. Nothing here is disposable.
83
- "device",
84
- ]);
85
- export type CommandLocus = z.infer<typeof CommandLocusSchema>;
86
- // What kind of thing a shell command is, the command gate's key space under the admission floor; gates one command
87
- // inside an already-running session. Six classes, chosen because each is hard or impossible to undo somewhere.
88
- export const CommandClassSchema = z.enum([
89
- // Rewrites or discards committed work: force-push, hard reset, force-delete a branch, clean -f, filter-branch.
90
- "git.destructive",
91
- // Moves a checkout to another branch. Only ever matches in a tree the conversation SHARES with its owner: inside
92
- // its own copy a conversation may stand where it likes, and the drift that causes is reported, not refused.
93
- "git.branch-switch",
94
- // Recursive-force deletion (`rm -rf`), and its spelling in a script (`fs.rm(p, { recursive: true })`).
95
- "files.destructive",
96
- // State nothing recovers at this locus; which roots count (/, /history in the sandbox) depends on the locus.
97
- "system.destructive",
98
- // A container volume or its data; the sandbox's blast radius (nested engine) differs sharply from a device's.
99
- "container.state",
100
- // Reads credential material: a secret reference, or a file confirmed to actually hold one.
101
- "secrets.access",
102
- // Publishes outward and irreversibly: npm/pnpm/yarn/cargo publish, gh release create, docker push.
103
- "package.publish",
104
- // A network program (curl, nc, ssh, scp, …) or interpreter one-liner aimed at a non-loopback or run-time-built
105
- // destination, the general exfiltration channel under the per-provider actionRules.
106
- "network.outbound",
107
- ]);
108
- export type CommandClass = z.infer<typeof CommandClassSchema>;
109
- // Workspace-wide admission floor per wake source, consulted on every outside-driven wake; composes with per-automation
110
- // overrides most-restrictive-wins. `workflow` is allow|deny only, since a hold there is indistinguishable from a CI
111
- // runner's timeout.
112
- export const AdmissionPolicySchema = z.object({
113
- schedule: AdmissionRuleSchema.default("allow"),
114
- event: AdmissionRuleSchema.default("allow"),
115
- listener: AdmissionRuleSchema.default("allow"),
116
- webchat: AdmissionRuleSchema.default("allow"),
117
- // Defaults to hold, unlike the others: a bug-report wake has full repo powers and a stranger's stack trace as its
118
- // brief.
119
- issues: AdmissionRuleSchema.default("hold"),
120
- workspace: AdmissionRuleSchema.default("allow"),
121
- workflow: z.enum(["allow", "deny"]).default("allow"),
122
- });
123
- export type AdmissionPolicy = z.infer<typeof AdmissionPolicySchema>;
124
70
  // How tool calls are gated, the Claude Agent SDK's PermissionMode narrowed to the three the composer offers. Both a
125
71
  // turn input and the payload of the `mode` frame, since the agent can move itself between them mid-turn. The SDK's
126
72
  // acceptEdits is not one of them: only `default` asks a person here, so it would differ from bypassPermissions in name
127
73
  // alone.
128
74
  export const PermissionModeSchema = z.enum(["default", "plan", "bypassPermissions"]);
129
75
  export type PermissionMode = z.infer<typeof PermissionModeSchema>;
130
- // Where a conversation was cut from, the durable half of a fork, carried on the registry entry. `index` is the message
131
- // the cut sat above in the source, so its transcript can put the mark back.
132
- export const ForkedFromSchema = z.object({
133
- conversationId: ConversationIdSchema,
134
- index: z.number().int().nonnegative(),
135
- // Which files this fork opened on; kept so the fork can still say it later, not only right after the click.
136
- files: z.enum(["then", "now"]),
137
- });
138
- export type ForkedFrom = z.infer<typeof ForkedFromSchema>;
76
+ // How one conversation relates to another (a fork's cut, a fresh session's origin): conversation-links.ts.
77
+ export * from "./conversation-links.js";
139
78
  // The turn's fields before the cross-field refinements below, so a subset (TurnProfileSchema) can be picked from them.
140
79
  // How many files one turn may carry. A batch of a person's queued messages joins their files, so the admission splits a
141
80
  // batch rather than build a turn past this (turn-admission.ts, together): a turn the daemon makes must parse back.
@@ -158,7 +97,7 @@ const AgentTurnFieldsSchema = z.object({
158
97
  // Seeds a fresh registry entry's title; an existing entry's title always wins over this.
159
98
  title: z
160
99
  .string()
161
- .max(80)
100
+ .max(CONVERSATION_TITLE_MAX)
162
101
  .optional()
163
102
  .describe("A title for a conversation this turn is opening. Ignored for a conversation that already has one."),
164
103
  // Already uploaded via /workspace/upload; Claude reads them via Read, Codex gets images as native input.
@@ -262,6 +201,8 @@ const AgentTurnFieldsSchema = z.object({
262
201
  })
263
202
  .optional()
264
203
  .describe("Where this conversation was cut from, on its first turn only. Only the client knows this, so only the client can say it."),
204
+ // A fresh session's origin, on its first turn only; requires an isolated conversationId the registry has never seen.
205
+ freshOf: FreshOfSchema.optional().describe(FRESH_OF_DESCRIPTION),
265
206
  // The browser sends the chosen model per turn; the provider token is the sandbox's own stored credential.
266
207
  model: z.string().optional().describe("Which model to use. Leave it out for the provider's default."),
267
208
  // Audience only, never model routing: `runRole` is what fills a model in. A run somebody pressed and is
@@ -327,6 +268,15 @@ const AgentTurnFieldsSchema = z.object({
327
268
  .describe(
328
269
  "Hold this message until then (epoch milliseconds) instead of starting a turn now: a time you chose, or the reopen of an allowance you know is spent. It waits in the conversation's queue, where it can be sent early, reworded, rescheduled or removed, and goes by itself at that instant, even for a conversation this message opens or one whose turn is running now. Ignored when already past; at most a month ahead.",
329
270
  ),
271
+ // Why `sendAt` is the instant it is, when it is not a time somebody chose: the composer booked the message for the
272
+ // spent allowance's reopen. Kept with the booking (`reopenOf` on the queue), so every window can tell an allowance
273
+ // wait from an appointment without matching instants against whatever the usage feed says now.
274
+ sendAtReopen: z
275
+ .literal(true)
276
+ .optional()
277
+ .describe(
278
+ "Says `sendAt` is the reopen of the allowance this message would be refused by, not a time you chose. Recorded with the booking (the queue's `reopenOf` names the account it waits for), so every window can say the message waits on an allowance, and see when the conversation has moved somewhere with room. Only with `sendAt`.",
279
+ ),
330
280
  // The other scheduled send: work that builds on another agent's, held until that agent's work is in the workspace.
331
281
  // Held in the queue like `sendAt`; the queue's `after` is this. Never both: a hold waits on exactly one thing.
332
282
  sendAfter: ConversationIdSchema.optional().describe(
@@ -362,6 +312,7 @@ export const AgentTurnSchema = AgentTurnFieldsSchema
362
312
  .refine((turn) => turn.forkOf?.files !== "then" || turn.isolated === true, {
363
313
  message: 'forkOf.files "then" requires isolated',
364
314
  })
315
+ .superRefine(freshOfIssues)
365
316
  .refine((turn) => turn.sendAt === undefined || turn.sendAfter === undefined, {
366
317
  message: "sendAt and sendAfter are two different holds: name one",
367
318
  })
@@ -514,6 +465,25 @@ export type MessageReceipt = z.infer<typeof MessageReceiptSchema>;
514
465
  // a job that ended, a land that broke something), or another agent (a child reporting back).
515
466
  export const MessageVoiceSchema = z.enum(["person", "sandbox", "agent"]);
516
467
  export type MessageVoice = z.infer<typeof MessageVoiceSchema>;
468
+ // The account a message booked for an allowance's reopen waits on: what it waits out, which the booking's instant alone
469
+ // cannot say once the chat has moved somewhere with room.
470
+ export const QueuedReopenSchema = z.object({
471
+ agent: AgentProviderSchema.describe("The provider whose allowance it waits out."),
472
+ account: z
473
+ .string()
474
+ .optional()
475
+ .describe("Which of its accounts. Absent when none was named, and every account of the provider read spent."),
476
+ });
477
+ export type QueuedReopen = z.infer<typeof QueuedReopenSchema>;
478
+ // Who a person's waiting message goes on, as its request names it now.
479
+ export const QueuedServingSchema = z.object({
480
+ agent: AgentProviderSchema.describe("The provider."),
481
+ harness: AgentHarnessSchema.describe("The agentic loop."),
482
+ model: z.string().optional().describe("The model. Absent when it named none, and takes the conversation's own."),
483
+ effort: z.string().optional().describe("How hard it thinks, where the model offers a choice."),
484
+ account: z.string().optional().describe("The account that pays for it. Absent when it named none, and goes on the conversation's own."),
485
+ });
486
+ export type QueuedServing = z.infer<typeof QueuedServingSchema>;
517
487
  // One message waiting for the conversation's next turn, as every window shows it.
518
488
  export const QueuedMessageSchema = z.object({
519
489
  id: z.string().describe("The message's id: what its sender named it, or what the sandbox did."),
@@ -533,6 +503,12 @@ export const QueuedMessageSchema = z.object({
533
503
  after: ConversationIdSchema.optional().describe(
534
504
  "The conversation whose finished work must land before this message goes out by itself, in place of `until`.",
535
505
  ),
506
+ reopenOf: QueuedReopenSchema.optional().describe(
507
+ "The allowance this message's `until` waits out: it was booked for that account's reopen, by the composer or by the sandbox holding words it would not send into a spent allowance, rather than for a time somebody chose. Absent on any other booking, and from sandboxes older than the field.",
508
+ ),
509
+ serves: QueuedServingSchema.optional().describe(
510
+ "Who serves the turn this message starts: the provider, loop, model, effort and account it was sent under, or was moved to since (`agent.queueReroute`, an account switch). Only on a person's words; the sandbox's and an agent's own go on wherever the conversation runs when they go. Absent from sandboxes older than the field.",
511
+ ),
536
512
  continues: z
537
513
  .literal(true)
538
514
  .optional()
@@ -586,15 +562,3 @@ export const QueueResumedSchema = z.object({
586
562
  ),
587
563
  });
588
564
  export type QueueResumed = z.infer<typeof QueueResumedSchema>;
589
- // Attaches to a conversation's newest run (live, or finished within retention); no cursor to resume from, the head
590
- // carries all rows. `run` names what the client was watching, so a newer run's head id tells it to catch up.
591
- export const AttachTurnSchema = z.object({
592
- conversationId: ConversationIdSchema.describe("Which conversation to watch."),
593
- run: z
594
- .string()
595
- .optional()
596
- .describe(
597
- "The run you were watching. The stream opens on the conversation's newest run whatever you name: if a newer turn has started since, the head names that one instead, and its rows are that turn's.",
598
- ),
599
- });
600
- export type AttachTurn = z.infer<typeof AttachTurnSchema>;