@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
@@ -25,6 +25,8 @@ import {
25
25
  WorkspaceDerivedQuerySchema,
26
26
  WorkspaceDerivedSchema,
27
27
  WorkspaceDeletedSchema,
28
+ WorkspaceDimensionsQuerySchema,
29
+ WorkspaceDimensionsSchema,
28
30
  WorkspaceDirSchema,
29
31
  WorkspaceExtractSchema,
30
32
  WorkspaceRestoredSchema,
@@ -110,6 +112,20 @@ export const workspaceContract = {
110
112
  .meta({ floor: "viewer", guest: true })
111
113
  .input(WorkspaceDerivedQuerySchema)
112
114
  .output(WorkspaceDerivedSchema),
115
+ // A folder's pictures measured at once, so a gallery lays out its rows before the pictures themselves arrive. A body,
116
+ // not a query: the paths are a folder's worth.
117
+ dimensions: procedure
118
+ .route({
119
+ method: "POST",
120
+ path: "/workspace/dimensions",
121
+ summary: "Measure pictures and videos",
122
+ description:
123
+ "The width and height each picture or video is shown at, and a video's running time, for a list of workspace files. Only the files' headers are read, so a folder's worth costs little; a gallery uses it to lay out its rows before any picture has loaded. A file that is not a picture, or cannot be read, is simply left out.",
124
+ })
125
+ // Measuring is reading a file's header, and the answer is a few numbers per file.
126
+ .meta({ floor: "viewer", guest: true })
127
+ .input(WorkspaceDimensionsQuerySchema)
128
+ .output(WorkspaceDimensionsSchema),
113
129
  // Mints the ticket GET /workspace/media (a plain Hono byte-range route, no oRPC shape) requires to stream.
114
130
  mediaTicket: procedure
115
131
  .route({
@@ -8,7 +8,7 @@ import { FastModeStateSchema } from "../schemas/providers/fast-mode.js";
8
8
  import { NeedSchema } from "../schemas/needs.js";
9
9
  import { PromptCacheOpeningSchema, PromptFingerprintSchema } from "../schemas/keep-warm.js";
10
10
  import { AgentReplySchema, UsageWindowSchema } from "../schemas/providers/plan-limits.js";
11
- import { SubagentKindSchema, SubagentStatusSchema, SubagentVerificationSchema } from "../schemas/terminal.js";
11
+ import { SubagentKindSchema, SubagentSdkRecordSchema, SubagentStatusSchema, SubagentVerificationSchema } from "../schemas/terminal.js";
12
12
  import { RetryLadderSchema } from "../schemas/turn-break.js";
13
13
  import { HandedOffSchema, HandoffOfferSchema } from "../schemas/providers/handoff.js";
14
14
  import { AgentCommandSchema, browserHelpRequest, capabilityOfferRequest, CapabilityOutcomeSchema, ContextUsageSchema, credentialOfferRequest, CredentialReceiptSchema, pageAskRequest, PageSchema, paymentOfferRequest, PaymentReceiptSchema, PermissionRequestSchema, PlanRequestSchema, QuestionRequestSchema, terminalHelpRequest, TodoItemSchema, ToolCallContentSchema, ToolCallLocationSchema, ToolCallStatusSchema, ToolKindSchema } from "./requests.js";
@@ -142,6 +142,9 @@ export const AgentEventSchema = z.discriminatedUnion("kind", [
142
142
  error: z.string().optional(),
143
143
  // Whether anything checked the work this report describes; rides the frame that ends the child.
144
144
  verification: SubagentVerificationSchema.optional(),
145
+ // Where the Claude runtime keeps an in-process child's own record, said once the daemon has paired it, so the
146
+ // card the turn settles into can name it after a restart.
147
+ sdk: SubagentSdkRecordSchema.optional(),
145
148
  }),
146
149
  z.object({ kind: z.literal("todos"), items: z.array(TodoItemSchema) }),
147
150
  // The provider's own slash commands, replaced whole each time; the composer's `/` popover lists them, invoking one
@@ -0,0 +1,48 @@
1
+ import { z } from "zod";
2
+ import { AgentHarnessSchema, AgentProviderSchema } from "../schemas/agent.js";
3
+ import { SessionTranscriptSchema, TurnEndingSchema } from "./transcript.js";
4
+
5
+ // The record a reopened tab rebuilds from: messages plus what the session is bound to (provider, runtime, account). A
6
+ // client can't derive these: after a mid-chat switch, its own picks belong to a different session.
7
+ export const AgentTranscriptSchema = SessionTranscriptSchema.extend({
8
+ sessionId: z.string().optional().describe("The provider session behind the last turn, when there is one."),
9
+ provider: AgentProviderSchema.optional().describe("Which provider minted that session."),
10
+ harness: AgentHarnessSchema.optional().describe("Which runtime minted it: a session resumes only on the loop that opened it."),
11
+ account: z
12
+ .string()
13
+ .optional()
14
+ .describe("Which stored account it belongs to, as the daemon resolved it. Absent when no stored account paid for the turn."),
15
+ // How the last turn ended, for endings that leave a live session with just a press to offer; absent otherwise.
16
+ ending: TurnEndingSchema.optional().describe(
17
+ "How the last turn ended, when it left work behind that one press finishes. Absent for a conversation whose last turn ended on its own, and for the failures that name something to repair first.",
18
+ ),
19
+ // `from` offsets every `rewindIndex` here, and is the `before` for the page above; `more` flags older messages.
20
+ from: z
21
+ .number()
22
+ .int()
23
+ .nonnegative()
24
+ .describe("Where the first message sits in the whole record, and the `before` that asks for the page above this one."),
25
+ more: z.boolean().describe("Whether older messages precede this page."),
26
+ // The cursor a reader keeps between reads: `through` (with the id of the row just before it) is the `after` of its
27
+ // next read, which then carries only what the record gained since.
28
+ through: z
29
+ .number()
30
+ .int()
31
+ .nonnegative()
32
+ .optional()
33
+ .describe(
34
+ "The position just past this page's last message: pass it back as `after`, with that message's `rowId` as `afterRow`, to read only what the record gains from here on. Absent from a sandbox older than reading forward.",
35
+ ),
36
+ continuous: z
37
+ .boolean()
38
+ .optional()
39
+ .describe(
40
+ "Answered to a read with `after`: true when these messages carry on exactly from there, to be added to what the reader holds; false when the record no longer runs through that point (a rewind since, or more written than one page carries), so this is its latest page instead, to replace what the reader holds.",
41
+ ),
42
+ live: z
43
+ .string()
44
+ .optional()
45
+ .describe(
46
+ "The run going on in this conversation right now, which the record does not hold yet: attach to it for its rows as they are drawn. Absent when nothing runs.",
47
+ ),
48
+ });
@@ -48,8 +48,20 @@ export interface ChildReportFields {
48
48
  // Where its work went, in the daemon's words: into the parent's own checkout, or held off it by a clash. Undefined
49
49
  // for a child whose work lands as any conversation's does.
50
50
  readonly landing?: string | undefined;
51
+ // For a child spawned with a result schema: the value it handed back, written out as JSON, or why it handed none.
52
+ readonly result?: { readonly json: string } | { readonly missing: string } | undefined;
51
53
  }
52
54
 
55
+ // The result a child spawned with a schema handed back, in words; nothing for a child spawned without one.
56
+ const resultLines = (result: ChildReportFields["result"]): readonly string[] => {
57
+ if (result === undefined) {
58
+ return [];
59
+ }
60
+ return "json" in result
61
+ ? ["", "Result (it matches the schema you gave it):", "```json", result.json, "```"]
62
+ : ["", `Result: none. ${result.missing} Ask it for one with send.`];
63
+ };
64
+
53
65
  // The wait tool's `verification` field, in words.
54
66
  const verificationLine = (verification: SubagentVerification | undefined): string | undefined => {
55
67
  if (verification === undefined) {
@@ -76,6 +88,7 @@ export const childReportPrompt = (fields: ChildReportFields): string => {
76
88
  ...(verification === undefined ? [] : [verification]),
77
89
  "",
78
90
  fields.report.trim() === "" ? "(It ended without a closing report.)" : fields.report.trim(),
91
+ ...resultLines(fields.result),
79
92
  "",
80
93
  `${fields.landing ?? "Its changes are in its own worktree and land the way any agent's do."} Its account of its own work is a claim, not ` +
81
94
  "a result: check what matters before you build on it, and follow up with the subagents tools as you would mid-turn.",
@@ -371,8 +371,10 @@ export const CredentialReceiptSchema = z.object({
371
371
  });
372
372
  export type CredentialReceipt = z.infer<typeof CredentialReceiptSchema>;
373
373
 
374
- // The three requests a turn journal can restore verbatim after a daemon restart. `browser_help`/`terminal_help` are
375
- // excluded: their Chromium/waiting command dies with the container, so those can only be reported, not restored.
374
+ // The requests a turn journal can restore verbatim after a daemon restart, raised by the turn itself or by a gate it
375
+ // called. `browser_help`/`terminal_help` are excluded: their Chromium/waiting command dies with the container, so those
376
+ // can only be reported, not restored. A gate's offer comes back as a card whose answer is told to a turn of its own,
377
+ // since the call that raised it died with the daemon: a yes is the agent's to act on by asking again.
376
378
  export const PlanRequestSchema = z.object({
377
379
  kind: z.literal("plan").describe("The agent has written a plan and is waiting for a yes."),
378
380
  ...planRequest,
@@ -385,5 +387,41 @@ export const PermissionRequestSchema = PermissionAskSchema.extend({
385
387
  kind: z.literal("permission").describe("The agent wants to use a tool it needs permission for."),
386
388
  ...permissionRequest,
387
389
  });
388
- export const ParkedRequestSchema = z.discriminatedUnion("kind", [PlanRequestSchema, QuestionRequestSchema, PermissionRequestSchema]);
390
+ export const PageAskRequestSchema = z.object({
391
+ kind: z.literal("page_ask").describe("The agent has shown a page to be answered on and is waiting."),
392
+ ...pageAskRequest,
393
+ });
394
+ export const CapabilityOfferRequestSchema = z.object({
395
+ kind: z.literal("capability_offer").describe("The agent asks for something to be connected."),
396
+ ...capabilityOfferRequest,
397
+ });
398
+ export const PaymentOfferRequestSchema = z.object({
399
+ kind: z.literal("payment_offer").describe("The agent asks to pay for something."),
400
+ ...paymentOfferRequest,
401
+ });
402
+ export const CredentialOfferRequestSchema = z.object({
403
+ kind: z.literal("credential_offer").describe("The agent asks a named person to release a credential."),
404
+ ...credentialOfferRequest,
405
+ });
406
+ export const ParkedRequestSchema = z.discriminatedUnion("kind", [
407
+ PlanRequestSchema,
408
+ QuestionRequestSchema,
409
+ PermissionRequestSchema,
410
+ PageAskRequestSchema,
411
+ CapabilityOfferRequestSchema,
412
+ PaymentOfferRequestSchema,
413
+ CredentialOfferRequestSchema,
414
+ ]);
415
+ // Whether a frame is a card the journal keeps for a restart to put back; a record, so a kind added above fails to compile
416
+ // until it is named here.
417
+ const RESTORABLE = {
418
+ plan: true,
419
+ question: true,
420
+ permission: true,
421
+ page_ask: true,
422
+ capability_offer: true,
423
+ payment_offer: true,
424
+ credential_offer: true,
425
+ } as const satisfies Record<ParkedRequest["kind"], true>;
426
+ export const isRestorableRequest = (event: { readonly kind: string }): event is ParkedRequest => Object.hasOwn(RESTORABLE, event.kind);
389
427
  export type ParkedRequest = z.infer<typeof ParkedRequestSchema>;
@@ -63,6 +63,9 @@ export const SandboxNoticeSchema = z.discriminatedUnion("code", [
63
63
  z.object({ code: z.literal("renewalWithdrawn"), params: z.object(failure) }),
64
64
  z.object({ code: z.literal("reconnect"), params: z.object(failure) }),
65
65
  z.object({ code: z.literal("undelivered"), params: z.object({ ...failure, unattended: z.boolean().optional() }) }),
66
+ // A door refusal that came after the turn had already run (a trial spent mid-turn): what it did stays, nothing is
67
+ // held, and `switchModel` when another model gets past the refusal.
68
+ z.object({ code: z.literal("stoppedPartway"), params: z.object({ ...failure, switchModel: z.boolean().optional() }) }),
66
69
  // A turn the sandbox kept whole, its message above; `memory` when a memory hold kept it.
67
70
  z.object({ code: z.literal("kept"), params: z.object({ ...failure, memory: z.boolean().optional() }) }),
68
71
  z.object({ code: z.literal("memoryHeld"), params: z.object(failure) }),
@@ -1,8 +1,7 @@
1
1
  import { z } from "zod";
2
- import { AgentHarnessSchema, AgentProviderSchema } from "../schemas/agent.js";
3
2
  import { ShareDetailSchema } from "../schemas/share.js";
4
3
  import { TurnErrandSchema, TurnSpeakerSchema } from "../schemas/speaker.js";
5
- import { SubagentKindSchema, SubagentStatusSchema, SubagentVerificationSchema } from "../schemas/terminal.js";
4
+ import { SubagentKindSchema, SubagentSdkRecordSchema, SubagentStatusSchema, SubagentVerificationSchema } from "../schemas/terminal.js";
6
5
  import { NeedSchema } from "../schemas/needs.js";
7
6
  import { HeldEndingSchema, RetryLadderSchema } from "../schemas/turn-break.js";
8
7
  import { HandoffOfferSchema } from "../schemas/providers/handoff.js";
@@ -172,6 +171,9 @@ export const TranscriptSubagentSchema = z.object({
172
171
  summary: z.string().optional(),
173
172
  error: z.string().optional(),
174
173
  verification: SubagentVerificationSchema.optional(),
174
+ sdk: SubagentSdkRecordSchema.optional().describe(
175
+ "Where the Claude runtime keeps this in-process subagent's own record, so its whole run reads back once nothing in memory remembers it. Absent for every other kind, and until the daemon has paired it.",
176
+ ),
175
177
  });
176
178
  export type TranscriptSubagent = z.infer<typeof TranscriptSubagentSchema>;
177
179
  // Mutable, unlike most of this file: the fold settles a card in place when its result lands turns later, saving a
@@ -269,6 +271,15 @@ export const TranscriptRowSchema = z.object({
269
271
  "Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken.",
270
272
  ),
271
273
  text: z.string().describe("The words."),
274
+ // The row's own name, minted once where the row is made (the live fold, or the record for a row written outside a
275
+ // run) and carried on every copy: the live head's, each patch's, the record's, a client's mirror. Positions move
276
+ // under a rewind and contents grow while a run streams, so this is how a reader says "this row" across all of them.
277
+ rowId: z
278
+ .string()
279
+ .optional()
280
+ .describe(
281
+ "The row's own id: the same on the live turn's copy, every change to it, the record, and a reader's own copy, so a reader updates the row it already holds instead of matching it by content. Rows recorded before rows had ids are named by their position (`@12`). Absent from a sandbox older than ids.",
282
+ ),
272
283
  // Which run produced this row, carried on every copy of it: the live head's, the record's, a client's mirror.
273
284
  // It is how a client says "I am already showing this run" without comparing content, which cannot answer that
274
285
  // while the run's last row is still growing.
@@ -339,9 +350,10 @@ export const TranscriptRowSchema = z.object({
339
350
  ),
340
351
  // The follow-up this notice offers, by name; the chat decides what it does, and whether it stands. `sendAnyway` sends
341
352
  // the held message; `sandboxMemory` offers that and a raise of the sandbox's memory; `sendAgain` sends a turn the
342
- // sandbox kept after a refusal no raise would clear.
353
+ // sandbox kept after a refusal no raise would clear; `switchModel` opens the model list for a turn a model's refusal
354
+ // stopped partway, so the same conversation carries on with another.
343
355
  noticeAction: z
344
- .enum(["landHold", "depsInstall", "watchStop", "sandboxMemory", "sendAnyway", "sendAgain"])
356
+ .enum(["landHold", "depsInstall", "watchStop", "sandboxMemory", "sendAnyway", "sendAgain", "switchModel"])
345
357
  .optional()
346
358
  .describe("A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies."),
347
359
  // Where a refused turn's words wait decides what the notice's press does: the conversation's queue, or the sandbox.
@@ -416,33 +428,45 @@ export const holdsRequest = (row: TranscriptRequests): boolean => REQUEST_FIELDS
416
428
  // Whether a row is holding the turn open on a card nobody has answered.
417
429
  export const isAwaitingDecision = (row: TranscriptRequests): boolean => REQUEST_FIELDS.some((field) => row[field]?.status === "pending");
418
430
 
431
+ // The row a patch changes, by its own id, beside the index older readers count from.
432
+ const patchRowId = z
433
+ .string()
434
+ .optional()
435
+ .describe("The id of the row this changes. A reader holding a row by that id changes it there, wherever it sits; `index` is for one that does not.");
436
+
419
437
  // One change to a run's rows, so a client keeps rows rather than frames. Prose/thinking append to a row or a card; a
420
438
  // `tool` card replaces its own fields by id, never its nested calls or thinking, which arrive by their own patches;
421
- // everything else replaces its row; `drop` removes a row that opened and never wrote.
439
+ // everything else replaces its row; `drop` removes a row that opened and never wrote. Each names its row twice: by
440
+ // `index` from where the run's rows start, and by `rowId`, which needs no counting and survives a row dropped above it.
422
441
  export const TranscriptPatchSchema = z.discriminatedUnion("op", [
423
442
  z.object({ op: z.literal("append").describe("A new row at the end."), row: TranscriptRowSchema }),
424
443
  z.object({
425
444
  op: z.literal("replace").describe("This row, whole, in place of the one at that index."),
426
445
  index: z.number().int().nonnegative(),
446
+ rowId: patchRowId,
427
447
  row: TranscriptRowSchema,
428
448
  }),
429
449
  z.object({
430
450
  op: z.literal("drop").describe("The row at that index is gone: it was opened and never written into."),
431
451
  index: z.number().int().nonnegative(),
452
+ rowId: patchRowId,
432
453
  }),
433
454
  z.object({
434
455
  op: z.literal("text").describe("More of the agent's prose, onto that row's text."),
435
456
  index: z.number().int().nonnegative(),
457
+ rowId: patchRowId,
436
458
  text: z.string(),
437
459
  }),
438
460
  z.object({
439
461
  op: z.literal("thinking").describe("More of the agent's reasoning, onto that row's thinking."),
440
462
  index: z.number().int().nonnegative(),
463
+ rowId: patchRowId,
441
464
  text: z.string(),
442
465
  }),
443
466
  z.object({
444
467
  op: z.literal("toolThinking").describe("More of a delegated subagent's reasoning, onto the thinking of the card that started it."),
445
468
  index: z.number().int().nonnegative(),
469
+ rowId: patchRowId,
446
470
  id: z.string().describe("The card's id, matched wherever it nests."),
447
471
  text: z.string(),
448
472
  }),
@@ -453,6 +477,7 @@ export const TranscriptPatchSchema = z.discriminatedUnion("op", [
453
477
  "A tool card's own fields: new, or the latest state of one already there, matched by id wherever it nests. Carries no `children` or `thinking`; a card already there keeps its own.",
454
478
  ),
455
479
  index: z.number().int().nonnegative(),
480
+ rowId: patchRowId,
456
481
  tool: TranscriptToolSchema,
457
482
  parent: z.string().optional().describe("The card this one nests under, when it is a delegated subagent's own call."),
458
483
  }),
@@ -527,29 +552,6 @@ export const TurnEndingSchema = z.object({
527
552
  });
528
553
  export type TurnEnding = z.infer<typeof TurnEndingSchema>;
529
554
 
530
- // The record a reopened tab rebuilds from: messages plus what the session is bound to (provider, runtime, account). A
531
- // client can't derive these: after a mid-chat switch, its own picks belong to a different session.
532
- export const AgentTranscriptSchema = SessionTranscriptSchema.extend({
533
- sessionId: z.string().optional().describe("The provider session behind the last turn, when there is one."),
534
- provider: AgentProviderSchema.optional().describe("Which provider minted that session."),
535
- harness: AgentHarnessSchema.optional().describe("Which runtime minted it: a session resumes only on the loop that opened it."),
536
- account: z
537
- .string()
538
- .optional()
539
- .describe("Which stored account it belongs to, as the daemon resolved it. Absent when no stored account paid for the turn."),
540
- // How the last turn ended, for endings that leave a live session with just a press to offer; absent otherwise.
541
- ending: TurnEndingSchema.optional().describe(
542
- "How the last turn ended, when it left work behind that one press finishes. Absent for a conversation whose last turn ended on its own, and for the failures that name something to repair first.",
543
- ),
544
- // `from` offsets every `rewindIndex` here, and is the `before` for the page above; `more` flags older messages.
545
- from: z
546
- .number()
547
- .int()
548
- .nonnegative()
549
- .describe("Where the first message sits in the whole record, and the `before` that asks for the page above this one."),
550
- more: z.boolean().describe("Whether older messages precede this page."),
551
- });
552
-
553
555
  // A delegation's own run, fetched when its card opens; empty when the record no longer holds it.
554
556
  export const AgentToolChildrenSchema = z.object({
555
557
  children: z.array(TranscriptToolSchema).describe("The calls the delegated agent made, in the order it made them."),
@@ -6,6 +6,24 @@ import type { TranscriptRow } from "./transcript.js";
6
6
  // automation, a loop, a watch wake) has no typed message and nobody watching, so promising a resend there names a
7
7
  // composer the reader is not looking at. Neither version promises a retry: whether one comes is the scheduler's
8
8
  // decision, not this row's to state. The code carries the same fact, so the editor's localized line agrees.
9
+ // The row the same refusal leaves once the turn had already run: a trial spent, or a model refused, on a later step. The
10
+ // message was read and acted on, so it is neither undelivered nor held (turn-runs keeps only a turn turned away at the
11
+ // door), and saying so invited a resend of something that had already changed files (onboarding review 2026-10-18,
12
+ // F07). What it did stays in the conversation's copy; the press, when another model gets past the refusal, carries on there.
13
+ export const stoppedPartwayRow = (said: { readonly message: string; readonly error?: string }, switchModel: boolean): TranscriptRow => {
14
+ const row: TranscriptRow = {
15
+ role: "notice",
16
+ text: switchModel
17
+ ? `${said.message} The run stopped partway. The changes it made so far are kept: continue with another model to pick up from here.`
18
+ : `${said.message} The run stopped partway. The changes it made so far are kept: send a message to pick up from here.`,
19
+ noticeCode: noticeCode({ code: "stoppedPartway", params: { ...said, switchModel: switchModel || undefined } }),
20
+ };
21
+ if (switchModel) {
22
+ row.noticeAction = "switchModel";
23
+ }
24
+ return row;
25
+ };
26
+
9
27
  export const undeliveredRow = (said: { readonly message: string; readonly error?: string }, unattended: boolean): TranscriptRow => ({
10
28
  role: "notice",
11
29
  text: unattended
@@ -8,6 +8,13 @@ export const CONVERSATION_ID = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
8
8
  // Safe to interpolate into a path or a branch name. `false` for anything else, including the empty string.
9
9
  export const isConversationId = (value: string): boolean => CONVERSATION_ID.test(value);
10
10
 
11
+ // The longest title a conversation opens with (AgentTurn.title). A title made by joining words (a fresh session's
12
+ // "Code review: " and its origin's whole title) runs past it easily, and a turn naming one is refused outright, so a
13
+ // sender clips it first rather than lose the turn to its name.
14
+ export const CONVERSATION_TITLE_MAX = 80;
15
+ export const clipTitle = (title: string): string =>
16
+ title.length <= CONVERSATION_TITLE_MAX ? title : `${title.slice(0, CONVERSATION_TITLE_MAX - 1).trimEnd()}…`;
17
+
11
18
  // Short (nothing over seven letters) and visually distinct: no near-rhymes, no shared first syllable.
12
19
  const ADJECTIVES = [
13
20
  "amber",
@@ -0,0 +1,20 @@
1
+ // A transcript row's own name (TranscriptRow.rowId). Minted once, where the row is made: the live fold for a run's rows,
2
+ // the record for a row written outside a run. Random rather than counted, since two makers (a run and a placed line) can
3
+ // write into one conversation without asking each other, and nothing reads order out of it: position is the record's.
4
+
5
+ const ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
6
+ // Twelve characters of base 36, about 62 bits: unique within any conversation by a wide margin, and short, since every
7
+ // row of every record carries one.
8
+ const LENGTH = 12;
9
+
10
+ export const newRowId = (): string => {
11
+ let id = "";
12
+ for (const byte of globalThis.crypto.getRandomValues(new Uint8Array(LENGTH))) {
13
+ id += ALPHABET[byte % ALPHABET.length];
14
+ }
15
+ return id;
16
+ };
17
+
18
+ // The name of a row recorded before rows had ids: its position, which never moves for a row that stays (a rewind only
19
+ // drops rows after the point it keeps, and what is written after gets minted ids). The `@` keeps it apart from a minted id.
20
+ export const positionRowId = (position: number): string => `@${position}`;
@@ -23,11 +23,15 @@ import {
23
23
  // reader's once it no longer does: nothing running there to hear it and nothing armed to wake it, which is a parent out
24
24
  // of Active. That holds only until the parent moves past it (heardByParent). After that the stop is the family's
25
25
  // history, drawn in the tray, and no longer something the reader owes a press. The sandbox draws the same line for a
26
- // child's land news (subagents/child-lands.ts): the parent's while it has a live turn, the owner's after.
26
+ // child's land news (subagents/child-lands.ts): the parent's while it has a live turn, the owner's after. A fresh
27
+ // session is the exception: its parent never supervises it (nothing it does is reported there), so whatever it stops on
28
+ // is the reader's from the start, through its parent's card, however busy the parent is.
27
29
  export const callsOwner = (
28
30
  child: AgentStanding & { readonly updatedAt: number },
29
31
  family: { readonly child: FleetLane; readonly parent: FleetLane; readonly parentAt: number },
30
- ): boolean => onlyOwnerCanAnswer(child) || (family.child === "attention" && family.parent !== "active" && !heardByParent(child, family.parentAt));
32
+ ): boolean =>
33
+ onlyOwnerCanAnswer(child) ||
34
+ (family.child === "attention" && (family.parent !== "active" || child.fresh !== undefined) && !heardByParent(child, family.parentAt));
31
35
 
32
36
  export type FleetLane = "attention" | "active" | "finished";
33
37
 
@@ -127,7 +131,9 @@ export const attentionCalls = (agents: readonly CallerStanding[]): readonly { re
127
131
  const calls: { readonly id: string; readonly card: string }[] = [];
128
132
  for (const agent of agents) {
129
133
  const parent = parentIn(agent);
130
- if (parent === undefined || agent.unsent === true) {
134
+ // Words left in a child's composer give it a card of its own (childFold.standsAlone), except a fresh session's:
135
+ // its composer is the person's anyway, and it rides its origin's card whatever it holds.
136
+ if (parent === undefined || (agent.unsent === true && agent.fresh === undefined)) {
131
137
  if (laneOf(agent) === "attention") {
132
138
  calls.push({ id: agent.id, card: agent.id });
133
139
  }
@@ -55,6 +55,8 @@ export interface AgentStanding {
55
55
  readonly limitScheduled?: boolean | undefined;
56
56
  /** The account the owner's policy is moving the held turn to, while that move is booked (a scheduled card's sentence). */
57
57
  readonly limitMoving?: string | undefined;
58
+ /** When the booked re-run goes, in epoch SECONDS, while one is booked (limitScheduled): the pass's own instant. */
59
+ readonly limitNextAt?: number | undefined;
58
60
  // A land that broke (AgentSummary.landFailure), standing until one goes through whatever turns run after it. Absent
59
61
  // from a sandbox older than it, where only a turn's own `failure` can say so.
60
62
  readonly landFailure?: LandFailure | undefined;
@@ -62,6 +64,10 @@ export interface AgentStanding {
62
64
  // themselves at a time or once another agent's work lands (`scheduled`), which make the conversation one that runs
63
65
  // again by itself. Never the words.
64
66
  readonly queue?: QueueHold | undefined;
67
+ // The conversation whose work this one was opened on, for a fresh session (AgentSummary.fresh): a person's own
68
+ // review, test or ask, which hangs under that conversation's card and is never reported to it
69
+ // (subagents/child-report.ts), so nothing it stops on is that conversation's news. Absent for every other one.
70
+ readonly fresh?: { readonly of: string } | undefined;
65
71
  }
66
72
 
67
73
  // The part of a conversation's queue the board reads: why it holds, and what a scheduled hold waits for. A queue-level
@@ -282,9 +288,11 @@ export const onlyOwnerCanAnswer = (agent: AgentStanding): boolean =>
282
288
  // an orchestrator that worked around three spent allowances and landed is done, not stuck. Only a turn's ending
283
289
  // counts. A question is still parked and waits for an answer whoever has moved since, and a land refused or broken
284
290
  // leaves work stuck on the child's branch that no report settles. A turn a person ran in the child's own chat is never
285
- // reported to the parent, and it ends after the parent's last move, so it still reads as unheard here.
291
+ // reported to the parent, and it ends after the parent's last move, so it still reads as unheard here. Nor is anything a
292
+ // fresh session does, whenever its origin moved: it is the person's, so its stops are never the origin's to have heard.
286
293
  // `updatedAt` is the clock on both sides: a rename keeps it (agents-registry), and reading does not move it.
287
294
  export const heardByParent = (child: AgentStanding & { readonly updatedAt: number }, parentAt: number): boolean =>
295
+ child.fresh === undefined &&
288
296
  BLOCKING_ENDINGS.has(child.status) &&
289
297
  // Still unwinding: its ending has not reached anyone yet.
290
298
  child.status !== "stopping" &&