stitchkit 0.70.1 → 0.70.2

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 (48) hide show
  1. package/dist/agent-runtime/harness-control.d.ts +2 -0
  2. package/dist/agent-runtime/harness-control.d.ts.map +1 -1
  3. package/dist/agent-runtime/history-chronology.d.ts +19 -0
  4. package/dist/agent-runtime/history-chronology.d.ts.map +1 -0
  5. package/dist/agent-runtime/history.d.ts.map +1 -1
  6. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  7. package/dist/agent-runtime/terminal-status.d.ts.map +1 -1
  8. package/dist/agent-runtime-browser.js +51 -51
  9. package/dist/agent-runtime-coding-tools.js +2 -2
  10. package/dist/agent-runtime-harness.js +60 -16
  11. package/dist/agent-runtime-openrouter.js +2 -2
  12. package/dist/agent-runtime-sqlite-bun.js +5 -5
  13. package/dist/agent-runtime-sqlite-node.js +5 -5
  14. package/dist/agent-runtime.js +110 -110
  15. package/dist/application-grammy.js +2 -2
  16. package/dist/application.js +63 -63
  17. package/dist/browser/live-state.d.ts +126 -0
  18. package/dist/browser/live-state.d.ts.map +1 -0
  19. package/dist/cli.js +5 -5
  20. package/dist/contract/index.js +20 -20
  21. package/dist/declaration.d.ts +4 -0
  22. package/dist/declaration.d.ts.map +1 -1
  23. package/dist/declaration.js +22 -22
  24. package/dist/files.js +3 -3
  25. package/dist/{index-vy5bjy07.js → index-4qfqy0m6.js} +21 -31
  26. package/dist/{index-10gbbbaa.js → index-9sx8tbz2.js} +1 -1
  27. package/dist/{index-4g196jmf.js → index-a01jky8m.js} +69 -7
  28. package/dist/{index-hsabxjz0.js → index-jw81xr75.js} +1 -1
  29. package/dist/index-y2s6h5bf.js +83 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +667 -56
  33. package/dist/node.js +26 -26
  34. package/dist/observability/index.js +22 -22
  35. package/dist/react.js +2 -2
  36. package/dist/realtime/contract.d.ts +30 -11
  37. package/dist/realtime/contract.d.ts.map +1 -1
  38. package/dist/realtime/index.d.ts +1 -1
  39. package/dist/realtime/index.d.ts.map +1 -1
  40. package/dist/server/index.js +79 -79
  41. package/dist/server/multipart.d.ts.map +1 -1
  42. package/dist/testing/surface-conformance.d.ts +4 -2
  43. package/dist/testing/surface-conformance.d.ts.map +1 -1
  44. package/dist/testing.js +122 -111
  45. package/dist/tools.js +57 -57
  46. package/llms-full.txt +224 -4
  47. package/package.json +1 -1
  48. package/dist/index-es0h4w26.js +0 -63
package/llms-full.txt CHANGED
@@ -1779,6 +1779,14 @@ for a custom raw transport. It uses the same descriptor and returns
1779
1779
  `{ files, fields, rollback }`; contract endpoints should prefer the automatic
1780
1780
  dispatcher path.
1781
1781
 
1782
+ Both delivery modes preserve UTF-8 `filename` metadata, including browser `FormData` names. For
1783
+ interoperability, a UTF-8 percent-encoded `filename*` parameter takes precedence over `filename`;
1784
+ unsupported charsets or malformed encoding are rejected rather than falling back silently. Literal
1785
+ percent sequences in ordinary `filename` are not decoded. Filenames remain untrusted metadata,
1786
+ not safe filesystem paths. Part headers have a 64 KiB byte limit, are case-insensitive and reject
1787
+ duplicates, folding, invalid header names and control characters. Disposition parameters reject
1788
+ duplicates and unsafe field names; declared part sizes must be nonnegative safe decimal integers.
1789
+
1782
1790
  ### Rate limiting
1783
1791
 
1784
1792
  ```ts
@@ -4068,6 +4076,12 @@ revalidates the signature/tool call/input before the original direct tool reache
4068
4076
  fence. Reconnect and SQLite reopen require no promise registry. Remembered policy and a stronger
4069
4077
  cross-crash exactly-once guarantee remain application concerns.
4070
4078
 
4079
+ Approval requests suspend a call; they do not settle its result. History carries exact call and
4080
+ approval identity across assistant/tool records, so an approved result can precede the next signed
4081
+ request in a successor run. Automatic decisions follow the same chronology. Unknown/duplicate
4082
+ responses and results with a different call or tool name are invalid; dropping an invalid active
4083
+ approval input fails the run with a private diagnostic rather than starting a fresh model turn.
4084
+
4071
4085
  `stitchkit/agent-runtime/coding-tools` returns ordinary direct runtime tools named `read_file`,
4072
4086
  `write_file`, `search_files`, `apply_patch`, `run_command` and optional `read_output`. Every call passes a
4073
4087
  required host authorization callback. File paths are relative, bounded and contained after
@@ -6332,6 +6346,171 @@ The bound handle intentionally has no `connect()` or `disconnect()`. Its
6332
6346
  `on`/`emit`/`request`, rejection and timeout semantics are exactly the path used
6333
6347
  by `createRealtimeClient`; only transport construction/lifecycle differs.
6334
6348
 
6349
+ ## Snapshot + event state synchronization
6350
+
6351
+ `createLiveStateController` is the optional browser-safe state machine between a
6352
+ validated transport binding and any renderer. It solves one problem: install a
6353
+ snapshot and every event after that snapshot's consistency point without a race.
6354
+ It does not create a socket, choose a cursor, retry a transport, store history or
6355
+ invent ordering for the application.
6356
+
6357
+ ```ts
6358
+ import {
6359
+ createLiveStateController,
6360
+ type LiveStateEventDecision,
6361
+ type LiveStateSource,
6362
+ } from 'stitchkit'
6363
+
6364
+ type View = { revision: number; rows: readonly Row[] }
6365
+ type Change = { revision: number; row: Row }
6366
+
6367
+ const applyChange = (state: View, event: Change): LiveStateEventDecision<View> => {
6368
+ if (event.revision <= state.revision) return { outcome: 'duplicate' }
6369
+ if (event.revision !== state.revision + 1) return { outcome: 'gap' }
6370
+ return {
6371
+ outcome: 'applied',
6372
+ state: { revision: event.revision, rows: [...state.rows, event.row] },
6373
+ }
6374
+ }
6375
+
6376
+ const live = createLiveStateController({
6377
+ source,
6378
+ applyEvent: applyChange,
6379
+ maxBufferedEvents: 128,
6380
+ maxBufferedBytes: 256 * 1024,
6381
+ sizeOfEvent: encodedChangeBytes,
6382
+ })
6383
+
6384
+ const unsubscribe = live.subscribe(render)
6385
+ await live.start()
6386
+ // On a gap/overflow or source loss, choose when the UI should resync.
6387
+ const status = live.getSnapshot()
6388
+ if (status.phase === 'resync-required' || status.phase === 'unavailable') {
6389
+ await live.resync()
6390
+ }
6391
+
6392
+ unsubscribe()
6393
+ await live.close()
6394
+ ```
6395
+
6396
+ The source boundary is the important part:
6397
+
6398
+ ```ts
6399
+ interface LiveStateSource<State, Event> {
6400
+ open(input: {
6401
+ signal: AbortSignal
6402
+ onEvent(event: Event): void
6403
+ onUnavailable(): void
6404
+ }): Promise<{ snapshot: State; close(): void | Promise<void> }>
6405
+ }
6406
+ ```
6407
+
6408
+ `onEvent` is available before `open()` begins asynchronous work. By the time
6409
+ `open()` resolves, the source guarantees that every event after the returned
6410
+ snapshot's consistency point has already been or will be passed to that callback.
6411
+ The controller buffers early events within both explicit limits, installs the
6412
+ snapshot, drains in order, then becomes `live`. A late result from an earlier
6413
+ `resync()` generation is fenced. Non-cooperative caller-owned cleanup is asked to
6414
+ stop but cannot hold controller settlement.
6415
+
6416
+ `subscribe()` listeners are synchronous external-store notifications: read the
6417
+ published snapshot and schedule rendering elsewhere. A listener that returns a
6418
+ Promise is removed after its first call, preventing unresolved UI work from
6419
+ accumulating per event. Source `close()` must be idempotent because an abort-aware
6420
+ binding may have started cleanup before the controller calls it.
6421
+
6422
+ At most two physical source `open()` / `close()` operations in total may remain
6423
+ unsettled. If a caller-owned source ignores cancellation beyond that operation
6424
+ bound, `resync()` returns
6425
+ `unavailable/controller-capacity` without opening another generation. When a slot
6426
+ settles, the controller publishes `resync-required/controller-capacity`; the host
6427
+ may retry explicitly. This bounds controller-retained work without inventing a
6428
+ transport retry loop.
6429
+
6430
+ ### Socket.IO binding
6431
+
6432
+ Use an acknowledged operation whose server handler establishes the subscription
6433
+ before it captures/returns the snapshot. For example, the server can join the
6434
+ socket to the resource room, capture revision `N`, then acknowledge that snapshot;
6435
+ ordered Socket.IO frames after that point reach the already-installed handler:
6436
+
6437
+ ```ts
6438
+ const source: LiveStateSource<View, Change> = {
6439
+ async open({ signal, onEvent, onUnavailable }) {
6440
+ const offEvent = socket.on('view:changed', onEvent)
6441
+ const offConnection = socket.onConnectionChange((connected) => {
6442
+ if (!connected) onUnavailable()
6443
+ })
6444
+ let closed = false
6445
+ const close = () => {
6446
+ if (closed) return
6447
+ closed = true
6448
+ offEvent()
6449
+ offConnection()
6450
+ }
6451
+ signal.addEventListener('abort', close, { once: true })
6452
+
6453
+ try {
6454
+ const snapshot = await socket.request('view:open', { timeoutMs: 5_000 })
6455
+ return { snapshot, close }
6456
+ } catch (error) {
6457
+ close()
6458
+ throw error
6459
+ }
6460
+ },
6461
+ }
6462
+ ```
6463
+
6464
+ Socket.IO still owns physical reconnect. A reconnected transport only means the
6465
+ connection is open; call `resync()` when application state needs a fresh
6466
+ generation. If the application has replay, its source may resume from its opaque
6467
+ cursor and return an accepted consistency point. If history expired or a cursor is
6468
+ incompatible, the source must acquire a fresh authoritative snapshot or reject the
6469
+ open; the controller does not classify or compare opaque cursors itself.
6470
+
6471
+ ### One-way HTTP stream binding
6472
+
6473
+ NDJSON/SSE can use the same receiver semantics when **one response generation**
6474
+ starts with a schema-validated snapshot frame and every later frame is a validated
6475
+ event. Attach `parseNDJSON` or the typed contract-stream reader, parse the first
6476
+ frame before resolving `open()`, and pump remaining frames into `onEvent`. Abort
6477
+ that response in `close()`.
6478
+
6479
+ A separate `GET /snapshot` followed by `GET /events` is not this boundary: a
6480
+ change can land between the two requests and disappear unless the application
6481
+ supplies a watermark/replay protocol. The controller intentionally cannot make
6482
+ that uncoordinated recipe safe.
6483
+
6484
+ ### Rendering, cache and process lifecycle
6485
+
6486
+ For replaceable progress, let the reducer replace the absolute view at each
6487
+ accepted revision. For ordered records, append only the exact next revision and
6488
+ return `gap` otherwise. Both use the same controller; their ordering policy stays
6489
+ in their reducers. `getSnapshot()` + `subscribe()` works headlessly and with
6490
+ `useSyncExternalStore`. A React Query application can update its existing query
6491
+ cache from a subscriber after `phase === 'live'`; no second hook or store adapter
6492
+ is required. Cache `markFresh` windows suppress local echoes, while revision/cursor
6493
+ classification detects duplicates—those are different policies.
6494
+
6495
+ A server process that owns such a receiver can place `start()` and `close()` in
6496
+ an existing `defineManagedResource` and include it in `createApplication`.
6497
+ Readiness follows a successful `live` snapshot; shutdown calls `close()`. Stitchkit
6498
+ does not add another supervisor, reconnect loop or durable event database.
6499
+
6500
+ When migrating a hand-written receiver, remove only the superseded attach/snapshot
6501
+ race loop, retry timer and listener bookkeeping. Keep the application's schemas,
6502
+ authorization, reducer, cursor/replay policy and durable storage. Development
6503
+ proxying and Vite HMR remain frontend tooling; they are described in
6504
+ [frontend integrations](./frontend-integrations.md) and never travel through live
6505
+ application event envelopes.
6506
+
6507
+ The Agent harness control server follows the same ordering: it installs the
6508
+ conversation attachment before awaiting the authoritative snapshot and rolls the
6509
+ attachment back if that read fails. A host adapter installs its delivery callback,
6510
+ issues `attach`, and supplies the returned snapshot through its live-state source;
6511
+ the existing Agent cursor and view reducers still own runtime epochs, durable
6512
+ versions and transcript projection.
6513
+
6335
6514
  ### Request-response over realtime
6336
6515
 
6337
6516
  For an event with an `ack` schema, `request()` is the Promise form of the same
@@ -11271,7 +11450,7 @@ description: One machine-readable statement a repository makes about itself —
11271
11450
  type: architecture
11272
11451
  status: active
11273
11452
  created: 2026-08-25
11274
- updated: 2026-08-25
11453
+ updated: 2026-08-30
11275
11454
  ---
11276
11455
 
11277
11456
  # Project declaration
@@ -11306,6 +11485,32 @@ mode that produces a running, wrong deployment rather than an error.
11306
11485
 
11307
11486
  ## Why declare yourself
11308
11487
 
11488
+ ### Identity is not product membership
11489
+
11490
+ The exported names `ProjectDeclaration` and `ProjectIdentity` describe the buildable source/artifact
11491
+ declared by a repository. A singular `identity` does **not** make a product project and a repository
11492
+ the same entity, nor does it identify a local checkout or a harness session.
11493
+
11494
+ | Entity | Meaning and owner |
11495
+ | --- | --- |
11496
+ | Product project | A product boundary whose repository membership is explicitly maintained outside this declaration |
11497
+ | Repository | Versioned source; its declaration describes roles, build outputs and release requirements |
11498
+ | Checkout | A local working copy of a repository revision; local paths and credentials belong to its host |
11499
+ | Harness workspace | The host-selected working scope for a session, not an implied product or membership registry |
11500
+
11501
+ Membership is many-to-many. For example, an external registry may declare product A includes
11502
+ repositories `service-a` and `shared-library`, while product B includes `service-b` and the same
11503
+ `shared-library`. Both products can read the library's unchanged declaration. Installing that library
11504
+ as a dependency, placing a checkout beside another, or naming a harness workspace does not create
11505
+ membership. The embedding product/registry owns these explicit relationships and their access policy.
11506
+
11507
+ A private companion repository can be part of a product without becoming a separate product. Its
11508
+ relationship and working context stay in an authorized private registry, never in a potentially public
11509
+ library declaration. No registry or membership fields are required here; the declaration remains
11510
+ optional. Existing exports and schema version 1 are unchanged.
11511
+
11512
+ ### One statement, several readers
11513
+
11309
11514
  Because the statements exist either way, and without a schema they exist three
11310
11515
  times. A repository already says how many roles it runs (in a process file),
11311
11516
  which variables it needs (in a Zod schema), what it builds (in a script) and
@@ -11501,6 +11706,16 @@ The browser-and-server entrypoint. Re-exports everything from
11501
11706
  | `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
11502
11707
  | `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
11503
11708
  | `bindRealtimeClient` | function | bind contract validation and typed acknowledgements to an existing Stitchkit client transport without owning its lifecycle |
11709
+ | `createLiveStateController` | function | keep typed application state current across one source-owned snapshot/event generation with finite pre-snapshot buffering, generation fencing and explicit resync — [guide](../guide/realtime.md#snapshot--event-state-synchronization) |
11710
+ | `LiveStateController` | _type_ | renderer-neutral `start` / `resync` / `getSnapshot` / `subscribe` / `close` handle |
11711
+ | `LiveStateControllerConfig` | _type_ | typed source, reducer, explicit event/byte bounds, event sizing and isolated error hooks |
11712
+ | `LiveStateControllerSnapshot` / `LiveStateControllerStatus` | _types_ | current value plus phase, generation, buffer and application/duplicate/gap/refusal counters |
11713
+ | `LiveStateControllerStatusSchema` | schema | strict runtime validation for controller status metadata |
11714
+ | `LiveStatePhaseSchema` / `LiveStatePhase` | schema / _type_ | `idle`, `opening`, `live`, `resync-required`, `unavailable` or `closed` |
11715
+ | `LiveStateStopReasonSchema` / `LiveStateStopReason` | schema / _type_ | explicit gap, overflow, source loss, controller failure and bounded `controller-capacity` reasons |
11716
+ | `LiveStateEventDecision` | _type_ | provider-owned reducer result: applied state, duplicate or gap |
11717
+ | `LiveStateSource` / `LiveStateSourceOpenInput` / `LiveStateSourceOpenResult` | _types_ | host binding for one continuous snapshot/event boundary; transport retry and cursor semantics remain host-owned |
11718
+ | `LiveStateControllerError` / `LiveStateSubscriberError` | _types_ | isolated observer failure payloads that do not change source or subscriber truth |
11504
11719
  | `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
11505
11720
  | `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
11506
11721
  | `parseNDJSON` | function | parse bounded fatal-UTF-8 NDJSON; blank keep-alives are skipped and `finalLine: 'require-newline'` can make the delimiter mandatory — [guide](../guide/client.md#ndjson) |
@@ -11540,7 +11755,7 @@ The browser-and-server entrypoint. Re-exports everything from
11540
11755
  | `RealtimeEmitArguments` | _type_ | emit arguments including an inferred acknowledgement callback |
11541
11756
  | `RealtimeEventHandler` | _type_ | handler inferred from an event definition |
11542
11757
  | `InferRealtimeEventMap` | _type_ | inferred Socket.IO-compatible event map |
11543
- | `RealtimeRejectDirection` | _type_ | server/client inbound/outbound rejection direction |
11758
+ | `RealtimeRejectDirection` / `RealtimeRejectPhase` / `RealtimeRejectReason` / `RealtimeRejectFault` | _types_ | canonical inferred rejection direction, validation phase, reason and fault classification |
11544
11759
  | `RealtimeRejectedEvent` | _type_ | structured rejected event with event, direction, phase, reason and fault |
11545
11760
  | `RealtimeRejectedEventHook` | _type_ | sync/async observer for structured realtime rejections |
11546
11761
  | `ValidatedRealtimeSocket` | _type_ | runtime-validating `on`/`emit` surface inferred from registries; `emit` returns "accepted by the transport" (`false` only for a client-side disconnected drop) |
@@ -12238,7 +12453,7 @@ and introduces no store, queue or model-provider implementation of its own.
12238
12453
  | `AgentHarnessFileResources` | _type_ | loader plus direct `read_resource` definition for lazy exact content |
12239
12454
  | `createAgentHarnessControlServer` | function | transport-neutral correlated requests, observer attachments and exclusive controller leases |
12240
12455
  | `AgentHarnessControlServer` / `AgentHarnessControlConnection` | _type_ | host server and detachable connection lifecycle; `deliver` is serialized, while required out-of-band `onOverflow` closes/aborts a slow transport before reconnect |
12241
- | `AgentHarnessControlServerConfig` | _type_ | explicit per-connection pending-event bound for failure-isolated control delivery |
12456
+ | `AgentHarnessControlServerConfig` | _type_ | explicit per-connection pending-event and server-wide concurrent attachment-snapshot bounds for failure-isolated control delivery |
12242
12457
  | `AgentHarnessPendingApproval` / `AgentHarnessApprovalDecision` | _type_ | exact durable pending request and allow/deny successor input |
12243
12458
 
12244
12459
  Resources default to at most 64 entries, 1 MiB of total UTF-8 text and 128 diagnostics. Duplicate
@@ -12733,7 +12948,7 @@ handler pipeline without opening a TCP port.
12733
12948
  | `DefineRealtimeProbeConfig` | _type_ | name, canonical scenario, explicit fixture and expected realtime outcome |
12734
12949
  | `CreateRealtimeProbeDriverConfig` | _type_ | per-scenario foreign-transport binder and optional handler-call counter |
12735
12950
  | `RealtimeProbeAdapter` | _type_ | connected-state observation, scenario invocation and subscription-only cleanup |
12736
- | `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/invalid/disconnect/timeout scenario vocabulary |
12951
+ | `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/local-invalid/peer-refusal/disconnect/timeout scenario vocabulary |
12737
12952
  | `RealtimeRejectionObservation` | _type_ | parsed structured realtime rejection observation |
12738
12953
  | `RealtimeDisconnectObservation` | _type_ | normalized physical timing of a realtime disconnect |
12739
12954
  | `TransportObservation` | _type_ | validated normalized driver result |
@@ -12784,6 +12999,11 @@ by the scaffolder that writes the first copy, and by whatever builds a source an
12784
12999
  binds the artifact into a deployment. It ships from the framework so those
12785
13000
  readers cannot hold different copies of the same schema.
12786
13001
 
13002
+ `identity` identifies the repository-local buildable source/artifact, not a product project, checkout
13003
+ or harness workspace. Product↔repository membership is explicit and many-to-many, owned by an
13004
+ external registry; dependency edges do not establish membership. Private companion context is never
13005
+ required in this public schema. See [identity boundaries](../guide/declaration.md#identity-is-not-product-membership).
13006
+
12787
13007
  **Declaring yourself is optional.** A project with no `project.json` is a
12788
13008
  complete project: nothing else in the framework imports this entrypoint, no
12789
13009
  build, test or start path looks for a declaration, and its absence is never an
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.70.1",
3
+ "version": "0.70.2",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -1,63 +0,0 @@
1
- // src/agent-runtime/terminal-status.ts
2
- function isSpeakableAssistantStatus(status) {
3
- return status === "completed" || status === "interrupted" || status === "committed";
4
- }
5
- function isAssistantHistoryEvidence(status, policy) {
6
- return isSpeakableAssistantStatus(status) || status === "failed" && policy?.failedAssistant === "assistant-marked";
7
- }
8
- function isCompleteAgentHistoryTurn(messages, policy) {
9
- if (messages[0]?.role !== "user")
10
- return false;
11
- const calls = new Set;
12
- const completed = new Set;
13
- const approvals = new Map;
14
- let assistantCount = 0;
15
- for (const message of messages) {
16
- if (message.role === "assistant") {
17
- if (!isAssistantHistoryEvidence(message.status, policy))
18
- return false;
19
- assistantCount += 1;
20
- }
21
- for (const part of message.parts) {
22
- if (part.type === "tool-call") {
23
- if (calls.has(part.callId))
24
- return false;
25
- calls.add(part.callId);
26
- } else if (part.type === "tool-approval-request") {
27
- if (!calls.has(part.callId) || completed.has(part.callId) || approvals.has(part.approvalId))
28
- return false;
29
- if ([...approvals.values()].some((approval) => approval.callId === part.callId))
30
- return false;
31
- approvals.set(part.approvalId, { callId: part.callId, answered: false });
32
- } else if (part.type === "tool-approval-response") {
33
- const approval = approvals.get(part.approvalId);
34
- if (!approval || approval.answered)
35
- return false;
36
- approval.answered = true;
37
- } else if (part.type === "tool-result") {
38
- if (!calls.has(part.callId) || completed.has(part.callId))
39
- return false;
40
- const approval = [...approvals.values()].find((entry) => entry.callId === part.callId);
41
- if (approval && !approval.answered)
42
- return false;
43
- completed.add(part.callId);
44
- }
45
- }
46
- }
47
- return assistantCount > 0 && calls.size === completed.size && [...approvals.values()].every((approval) => approval.answered);
48
- }
49
- function assistantStatus(reason) {
50
- if (reason === "success" || reason === "policy_stop" || reason === "provider_stop") {
51
- return "completed";
52
- }
53
- if (reason === "superseded")
54
- return "superseded";
55
- if (reason === "absorbed")
56
- return "superseded";
57
- if (reason === "interrupted" || reason === "cancelled" || reason === "shutdown") {
58
- return "interrupted";
59
- }
60
- return "failed";
61
- }
62
-
63
- export { isAssistantHistoryEvidence, isCompleteAgentHistoryTurn, assistantStatus };