From d182f20de150cc0e388a660a96509851b3154d07 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 30 Aug 2026 10:03:45 -0700 Subject: [PATCH 1/4] Add tests for correlating question answers back to their gate Proves the block's own id (the same id postQuestion mints as questionId) rides an answer's mail as its correlationId end to end through the real HTTP route, including when batching would otherwise strand it: a batch whose last queued message isn't the answer itself must still carry the answer's correlationId, not drop it because the last message has none. --- .../chat/test/block-responses-routes.test.ts | 111 ++++++++++++++++++ packages/chat/test/test-support.ts | 10 +- 2 files changed, 119 insertions(+), 2 deletions(-) diff --git a/packages/chat/test/block-responses-routes.test.ts b/packages/chat/test/block-responses-routes.test.ts index 327dc07de..93c31b257 100644 --- a/packages/chat/test/block-responses-routes.test.ts +++ b/packages/chat/test/block-responses-routes.test.ts @@ -14,6 +14,7 @@ import { createWorkbench, fakePlatform, mountAs, + settleFanout, TENANT, timelineEvents, timelineOf, @@ -306,6 +307,116 @@ describe("block response routes — question answers", () => { }); }); + test("answering a question stamps the answer's mail with the block's own id as its correlationId", async () => { + const platform = fakePlatform(); + const deps = buildDeps({ + platform, + blockResponses: createInMemoryBlockResponseStore(), + }); + const app = mountAs(createChatRoutes(deps), "prn_alice"); + const { body: workbench } = await createWorkbench(app, { + kind: "workbench", + participants: ["ins_echo1@acme.example"], + }); + + const post = await app.request( + responsesUrl(workbench.id, "m1", "blk_question1"), + { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ kind: "question", answer: "Staging" }), + }, + ); + expect(post.status).toBe(200); + await settleFanout(); + + // The block's own id (`blk_question1`, the same id `postQuestion` + // mints as `questionId`) rides as the answer mail's correlationId — + // the exact id `tryCorrelate` (`vendor/intx/inference/src/reactor.ts`) + // needs to resolve the `message_response` gate this answers, rather + // than "whichever gate is next". + expect(platform.sentMail).toHaveLength(1); + expect(platform.sentMail[0]?.correlationId).toBe("blk_question1"); + }); + + test("a question's correlationId survives batching even when it isn't the batch's last queued message", async () => { + // A held first dispatch forces the answer (queued 2nd) and a further + // plain follow-up (queued 3rd, landing last) into one batched turn — + // proving the batch's correlationId comes from whichever queued turn + // carries one, not from `batch[batch.length - 1]`, which here is the + // follow-up and carries none. + let releaseHold: () => void = () => {}; + const held = new Promise((resolve) => { + releaseHold = resolve; + }); + let resolveFirstDispatchStarted: () => void = () => {}; + const firstDispatchStarted = new Promise((resolve) => { + resolveFirstDispatchStarted = resolve; + }); + let holdConsumed = false; + + const platform = fakePlatform(); + const deliverMail = platform.sendMail.bind(platform); + platform.sendMail = async (input) => { + if (!holdConsumed) { + holdConsumed = true; + resolveFirstDispatchStarted(); + await held; + } + return deliverMail(input); + }; + + const deps = buildDeps({ + platform, + blockResponses: createInMemoryBlockResponseStore(), + }); + const app = mountAs(createChatRoutes(deps), "prn_alice"); + const { body: workbench } = await createWorkbench(app, { + kind: "workbench", + participants: ["ins_echo1@acme.example"], + }); + + // Message 1 dispatches immediately and is held open. + const first = await app.request(`/workbenches/${workbench.id}/messages`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ parts: [{ kind: "text", text: "hello" }] }), + }); + expect(first.status).toBe(201); + await firstDispatchStarted; + + // Queued 2nd, while the first dispatch is still held: the answer, + // carrying the block's id as its correlationId. + const post = await app.request( + responsesUrl(workbench.id, "m1", "blk_question1"), + { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ kind: "question", answer: "Staging" }), + }, + ); + expect(post.status).toBe(200); + + // Queued 3rd, landing last in the batch: an unrelated follow-up with + // no correlationId of its own. + const third = await app.request(`/workbenches/${workbench.id}/messages`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + parts: [{ kind: "text", text: "also, one more thing" }], + }), + }); + expect(third.status).toBe(201); + + releaseHold(); + await settleFanout(); + + // First dispatch (message 1), then one batched dispatch covering + // both the answer and the follow-up. + expect(platform.sentMail).toHaveLength(2); + expect(platform.sentMail[1]?.correlationId).toBe("blk_question1"); + }); + test("a question response without answer text is rejected", async () => { const deps = buildDeps({ blockResponses: createInMemoryBlockResponseStore(), diff --git a/packages/chat/test/test-support.ts b/packages/chat/test/test-support.ts index cf2b8c438..930a29649 100644 --- a/packages/chat/test/test-support.ts +++ b/packages/chat/test/test-support.ts @@ -83,6 +83,7 @@ export function fakePlatform( principalId?: string; content: MailContent; fromWorkbenchId?: string; + correlationId?: string; }[]; launchInviteCalls: { tenantId: string; @@ -96,6 +97,7 @@ export function fakePlatform( principalId?: string; content: MailContent; fromWorkbenchId?: string; + correlationId?: string; }[] = []; const launchInviteCalls: { tenantId: string; @@ -172,10 +174,14 @@ export function fakePlatform( input.principalId !== undefined ? { ...sentMailEntryBase, principalId: input.principalId } : sentMailEntryBase; - sentMail.push( + const withFromWorkbench = input.fromWorkbenchId !== undefined ? { ...withPrincipal, fromWorkbenchId: input.fromWorkbenchId } - : withPrincipal, + : withPrincipal; + sentMail.push( + input.correlationId !== undefined + ? { ...withFromWorkbench, correlationId: input.correlationId } + : withFromWorkbench, ); const id = `mail_${++mailCounter}`; const createdAt = new Date().toISOString(); From f3e3ef84b2637e8a99b3161ec829e1db618a7050 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 30 Aug 2026 10:04:01 -0700 Subject: [PATCH 2/4] Correlate question answers back to the question that asked them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit postQuestion minted a stable questionId that runAskUser discarded, and the question-response route set no interchangeCorrelationId on the relayed answer, so nothing tied answer N to question N beyond arrival order — two questions asked before either was answered would resolve on a guess, not a match. ask_user's beforeToolExtension (CL-7190, stacked below this) already reuses questionId as its message_response gate's correlationId; this wires the other half of the round trip. The blocks/responses route now passes the answered block's own id (which IS its questionId, the same way a poll's block id is its pollId) as correlationId when relaying a question's answer. That id threads through the plain-message mail path — SendWorkbenchMessageInput -> QueuedTurn -> dispatchTurnBatch -> DispatchTurnInput/dispatchTurn -> WorkbenchMail.sendMail -> platform-adapter -> SendFoldedMailParams/deliverFoldedMailMIME -> UserMessageParams/sendUserMessage -- landing as headers.interchangeCorrelationId on the InboundMessage the reactor's pre-existing, unmodified tryCorrelate already reads generically. dispatchTurnBatch takes whichever queued turn in a batch carries a correlationId, not only the batch's last message, since principalId legitimately tracks "whoever sent last" but a gate answer does not. --- packages/chat/src/platform-adapter.ts | 3 ++ packages/chat/src/platform-port.ts | 7 ++++ packages/chat/src/routes.ts | 7 ++++ packages/chat/src/turn-queue.ts | 4 ++ packages/chat/src/workbench-service.ts | 40 +++++++++++++++++++ packages/folded-runs/src/mail.ts | 6 +++ packages/interaction-tools/src/client.ts | 16 +++++--- .../intx/hub-sessions/src/session-service.ts | 8 +++- 8 files changed, 84 insertions(+), 7 deletions(-) diff --git a/packages/chat/src/platform-adapter.ts b/packages/chat/src/platform-adapter.ts index 8f15235bc..6989bd365 100644 --- a/packages/chat/src/platform-adapter.ts +++ b/packages/chat/src/platform-adapter.ts @@ -1214,6 +1214,9 @@ export function createHubChatPlatform( domain, content: input.content.content, cryptoProvider, + ...(input.correlationId !== undefined + ? { correlationId: input.correlationId } + : {}), }; const withAttachments = attachments !== undefined diff --git a/packages/chat/src/platform-port.ts b/packages/chat/src/platform-port.ts index ecf525a75..a05df780a 100644 --- a/packages/chat/src/platform-port.ts +++ b/packages/chat/src/platform-port.ts @@ -174,6 +174,13 @@ export interface WorkbenchMail { * a principal address has no mailbox: a reply to it vanishes. */ readonly fromWorkbenchId?: string; + /** + * The MIME `Interchange-Correlation-ID` header to stamp on this mail + * (`vendor/intx/mime/src/mail-builder.ts` already accepts this as + * `opts.correlationId`) — set when this message is an answer that must + * resolve a specific parked `message_response` gate, absent otherwise. + */ + readonly correlationId?: string; }): Promise; fetchBlob(workbenchId: string, blobId: string): Promise; diff --git a/packages/chat/src/routes.ts b/packages/chat/src/routes.ts index 94656ae36..d38fed2cb 100644 --- a/packages/chat/src/routes.ts +++ b/packages/chat/src/routes.ts @@ -2269,6 +2269,13 @@ export function createChatRoutes(deps: CreateChatRoutesDeps): Hono { senderAddress: senderAddressOf(c), workbenchId, messageParts: [{ kind: "text", text: payload.answer }], + // A question block's `blockId` IS its `questionId` + // (`ask_user`'s `postQuestion` mints one id and reuses it as + // both), which is also the `message_response` gate's own + // correlationId (`beforeAskUser`, `@corbits/interaction-tools`) + // — so this is the exact id that resolves the gate this answer + // is for, not merely "whichever gate is next". + correlationId: blockId, }, ); deps.onMessageFanout?.(answer.fanoutDelivered); diff --git a/packages/chat/src/turn-queue.ts b/packages/chat/src/turn-queue.ts index e362b6301..d5b921fbf 100644 --- a/packages/chat/src/turn-queue.ts +++ b/packages/chat/src/turn-queue.ts @@ -41,6 +41,10 @@ export type QueuedTurn = { readonly principalId: string; readonly recipients: readonly string[]; readonly parts: readonly PartType[]; + /** The id an answer must carry to resolve the `message_response` gate it + * answers (see `SendWorkbenchMessageInput.correlationId` in + * `./workbench-service.ts`). Absent for every ordinary message. */ + readonly correlationId?: string; }; export type WorkbenchTurnQueueDeps = { diff --git a/packages/chat/src/workbench-service.ts b/packages/chat/src/workbench-service.ts index 31a2c613f..899bf0f32 100644 --- a/packages/chat/src/workbench-service.ts +++ b/packages/chat/src/workbench-service.ts @@ -1381,6 +1381,16 @@ export type SendWorkbenchMessageInput = { * into the run the room already has. */ readonly forcedRecipientAddress?: string; + /** + * The id an answer must carry to resolve the gate it answers — a + * `message_response` gate's own correlationId (`@corbits/interaction-tools`'s + * `beforeAskUser`), threaded down to `headers.interchangeCorrelationId` + * on the InboundMessage the reactor's `tryCorrelate` reads. Sourced from + * the answered block's own id for a question response + * (`packages/chat/src/routes.ts`'s blocks/responses route); absent for + * every ordinary message, which carries no correlation at all. + */ + readonly correlationId?: string; }; export type SendWorkbenchMessageResult = { @@ -1620,6 +1630,9 @@ async function routeToRecipients( principalId: input.principalId, recipients, parts: turnParts, + ...(input.correlationId !== undefined + ? { correlationId: input.correlationId } + : {}), }, (batch) => dispatchTurnBatch(deps, input.tenantId, input.workbenchId, batch), @@ -1664,6 +1677,18 @@ async function dispatchTurnBatch( const last = batch[batch.length - 1]; if (last === undefined) return; const messageIds = batch.map((turn) => turn.messageId); + // An answer's correlationId must survive batching regardless of where in + // the batch it landed — unlike `principalId`, which is legitimately + // "whoever sent last," a gate answer is a specific message a specific + // queued turn carries, not necessarily the batch's final one (a further + // unrelated message queued behind the answer, before this batch drains, + // would otherwise make `last.correlationId` undefined and silently strand + // the gate on its timeout instead of resolving it). At most one queued + // turn in a batch carries a correlationId in practice — a batch answering + // more than one live question is not a shape this dispatch produces. + const batchCorrelationId = batch.find( + (turn) => turn.correlationId !== undefined, + )?.correlationId; // CL-6644: unconditional entry marker — see the matching note on the // caller's own recipient-resolution log. This is the one line that @@ -1730,6 +1755,15 @@ async function dispatchTurnBatch( agentAddress, parts, requestMessageIds: messageIds, + // A batch concatenating more than one queued message's parts + // still stamps the whole combined body as the answer when any + // one of them carries a correlationId — acceptable (the user + // did answer), but a batch mixing the actual answer with an + // unrelated follow-up hands the gate the whole blob, not just + // the answer. + ...(batchCorrelationId !== undefined + ? { correlationId: batchCorrelationId } + : {}), }), turnDispatchTimeoutMs, turnDispatchTimeoutMessage(agentAddress, turnDispatchTimeoutMs), @@ -1774,6 +1808,9 @@ export type DispatchTurnInput = { readonly parts: PartType[]; /** The room messages this turn answers, in arrival order. */ readonly requestMessageIds: readonly string[]; + /** See `SendWorkbenchMessageInput.correlationId`; threaded straight + * through to `WorkbenchMail.sendMail`. */ + readonly correlationId?: string; }; /** @@ -1812,6 +1849,9 @@ export async function dispatchTurn( principalId: input.principalId, content: encodeParts(input.parts, { replyTo: input.workbenchId }), fromWorkbenchId: input.workbenchId, + ...(input.correlationId !== undefined + ? { correlationId: input.correlationId } + : {}), }); } catch (err) { if (turn !== undefined) { diff --git a/packages/folded-runs/src/mail.ts b/packages/folded-runs/src/mail.ts index d1e583944..a6cb1d259 100644 --- a/packages/folded-runs/src/mail.ts +++ b/packages/folded-runs/src/mail.ts @@ -25,6 +25,9 @@ export type SendFoldedMailParams = { attachments?: MessageAttachment[]; replyTo?: string; cryptoProvider: CryptoProvider; + /** The MIME `Interchange-Correlation-ID` header — set when this message + * answers a specific parked `message_response` gate, absent otherwise. */ + correlationId?: string; }; /** @@ -50,6 +53,9 @@ async function deliverFoldedMailMIME( sessionId: params.sessionId, tenantId: params.tenantId, cryptoProvider: params.cryptoProvider, + ...(params.correlationId !== undefined + ? { correlationId: params.correlationId } + : {}), }; const withAttachments = params.attachments !== undefined diff --git a/packages/interaction-tools/src/client.ts b/packages/interaction-tools/src/client.ts index e88eb75c8..20695654a 100644 --- a/packages/interaction-tools/src/client.ts +++ b/packages/interaction-tools/src/client.ts @@ -57,12 +57,16 @@ const PostedMessageResponse = type({ id: "string", createdAt: "string" }); /** * Posts a `question` block into the caller's own channel. Mints the * block's `questionId` here (never trusts the model to supply a stable, - * collision-free id) and returns it, since `ask_user`'s tool result names - * it so a caller can correlate a later answer, and the route persists - * responses keyed by `(messageId, blockId)`. `@intx/hub-common`'s - * `generateId` is a closed enum of platform id kinds (vendored, read-only - * source) with no "question" entry, so this mints its own `q_`-prefixed - * id the same way `packages/chat/src/threads.ts`'s `thr_` ids do. + * collision-free id) and returns it: `beforeAskUser` (`./tool.ts`) reuses + * it verbatim as the `message_response` gate's own `correlationId`, and + * the block-response route persists (and later relays) an answer keyed on + * this same id as `blockId` — a question block's `blockId` IS its + * `questionId`, the same way a poll's is its `pollId` + * (`packages/chat/src/schema.ts`'s `block_responses` table comment). + * `@intx/hub-common`'s `generateId` is a closed enum of platform id kinds + * (vendored, read-only source) with no "question" entry, so this mints its + * own `q_`-prefixed id the same way `packages/chat/src/threads.ts`'s + * `thr_` ids do. */ export async function postQuestion( config: AskUserClientConfig, diff --git a/vendor/intx/hub-sessions/src/session-service.ts b/vendor/intx/hub-sessions/src/session-service.ts index 455bcf79a..03860dfd0 100644 --- a/vendor/intx/hub-sessions/src/session-service.ts +++ b/vendor/intx/hub-sessions/src/session-service.ts @@ -334,6 +334,11 @@ export type UserMessageParams = { sessionId: string; tenantId: string; cryptoProvider: CryptoProvider; + /** Stamped as the MIME `Interchange-Correlation-ID` header (CL-7191) so + * the reactor's `tryCorrelate` can resolve a parked `message_response` + * gate against this message rather than only ordering. Absent for every + * ordinary message. */ + correlationId?: string; }; export type SessionServiceDeps = { @@ -2281,6 +2286,7 @@ export function createSessionService( sessionId, tenantId, cryptoProvider, + correlationId, } = params; const headers: MessageHeaders = { @@ -2294,7 +2300,7 @@ export function createSessionService( references, mimeVersion: "1.0", interchangeType: "conversation.message", - interchangeCorrelationId: undefined, + interchangeCorrelationId: correlationId, interchangeTenantId: tenantId, interchangeAgentId: undefined, interchangeSessionId: sessionId, From f8a1363c89219bbbdd16fb7f08c8556ef07d3346 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 30 Aug 2026 10:04:11 -0700 Subject: [PATCH 3/4] Update docs: hub-sessions correlationId delta Records the UserMessageParams.correlationId delta in VENDORED.md and vendor/intx/hub-sessions/VENDORED-FROM, with a matching tree-hash update in scripts/checks/kill-dates.txt. --- VENDORED.md | 34 +++++++++++++------------- scripts/checks/kill-dates.txt | 2 +- vendor/intx/hub-sessions/VENDORED-FROM | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/VENDORED.md b/VENDORED.md index 907185f7a..262cc7782 100644 --- a/VENDORED.md +++ b/VENDORED.md @@ -22,23 +22,23 @@ never a convenience. ## Ledger -| Vendored path | What was copied | Upstream repo @ commit | Why not a published package | Owner | Kill date | Kill-date test | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ----------------- | -| `apps/sidecar` | Derived from upstream's own `apps/sidecar`: six modules byte-identical (`default-harness.ts`, `signing-keypair.ts`, `source-asset-delivery.ts`, `workflow-closure-apply.ts`, `workflow-probe-handler.ts`, `workflow-run-pack-restore.ts`), three near-verbatim (`atomic-write.ts` differs only by the repo-wide logger namespace), the rest (`index.ts`, `config.ts`, `tool-materialization.ts`, `step-agent-tools.ts`, `workflow-host-wiring/`, `workflow-substrate-factory/`, …) substantially rewritten, plus workbench-only modules. A living fork, not a frozen copy, so this row carries no tree hash. | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | An app is never npm-published, so no publish can cover the execution host; retired by consuming an upstream-published host, or by renewing this row deliberately | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/agent` | `@intx/agent` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the operator-configurable doom-loop threshold (`afd0c82b`, `c421c092`) the re-vendored `workflow-host` configures; one local delta (CL-7190): `ToolBundle.beforeToolExtension` (`tool.ts`) and its composition into `ResolvedTools.beforeToolExtensions` (`agent.ts`), so a tool package can contribute its own suspend-capable extension without the reactor or director special-casing it by name; retired by the next `@intx/agent` publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/db` | `@intx/db` source (`src/`, `migrations/`, drizzle config, manifest, tsconfigs) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the `wire_projection` column/loader delta (CL-6324) or the `workflow_definition.origin` column separating a definition from the per-run record of one folded run's deploy (CL-6452), shipped as migrations `0086`/`0087` behind upstream's `0085_add_approval_run_idx`, plus `0088` rewriting the retired `onBodyFailure: "continue"` literal to upstream's `"tolerate"` in stored wire projections; retired when upstream absorbs the deltas | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/harness` | `@intx/harness` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the connector reply drain (`driveConnectorReplies`, `ConnectorReplyDrain`, `AgentEventStream`; `11590e66`) the sidecar's warm mail loop drives; no local delta; retired by the next `@intx/harness` publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/hub-agent` | `@intx/hub-agent` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the `agentDir` path export (`927556de`) the sidecar's deploy-tree lookup uses, and its own `@intx/mail-memory`/`@intx/harness` pins must resolve the vendored copies; no local delta; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/hub-api` | `@intx/hub-api` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the null-principal `resolveApproval` for policy-resolved decisions (CL-6345) or the bearer-authenticated workflow-deploy mirror (`middleware/workflow-run-deploy-auth.ts`, CL-workflow-deploy-bearer); retired when upstream absorbs the deltas | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/hub-sessions` | `@intx/hub-sessions` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the pack-acceptance fixes (`ownsWorkflowRunRepo`, `anchorAddressForPackSource`, `decideTerminalRunFlip`), the adopted deploy front + `sourceRef` (CL-6324), the wire-projection writer (CL-6324), malformed tool-call-name sanitization (CL-6478), the sealed-run terminal-status backfill (CL-6595), or the CL-7190 `registerSignalCorrelation` approval-only guard (fails loud on a future `SignalKind` this RPC has no persistence for, rather than silently mis-persisting one); retired when upstream absorbs the deltas | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/inference` | `@intx/inference` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates doom-loop detection (`8da4c827`, `afd0c82b`, `c421c092`); local deltas: `providers/google-genai-files.ts` builds its upload body as `new Uint8Array(bytes)` because TS 6's lib.dom `BodyInit` rejects `Uint8Array` (upstream compiles ESNext-only under TS 5.9); and CL-7190's `message_response` resume branch in `reactor.ts`'s `resumePendingOperation`/`timeoutMessageFor`, plus its `reactor.test.ts`/`testing/fakes.ts` regression harness (this package previously had zero tests); retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/mail-memory` | `@intx/mail-memory` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the `@intx/mailbox` extraction (`af03bb90`), on-demand body reads (`54f7c239`) and `expunge` returning the swept uids (`bcabb1f8`) that the re-vendored `workflow-host` binds against; no local delta; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/mailbox` | `@intx/mailbox` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | Never published: a new package at the target pin (`af03bb90`) that `workflow-host`'s substrate mailbox store and supervisor-backed transport import; no local delta; retired by its first publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/mime` | `@intx/mime` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the non-RFC message-id guard `isMessageId` (`d97e1832`), the full `References` chain (`65c6fe70`) and the lossless `decodeMail` decoder (`3b6d06b2`) that `mailbox`/`mail-memory` at the same pin import; no local delta; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/types` | `@intx/types` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the type surface the re-vendored trees compile against: `expunge` returning `expungedUids` (`bcabb1f8`), plain-string `PackRejectReason` (`7b42f405`), the run authorization/approvals REST types (`71ad6c08`), the decoded-mail `Mail`/`MailPartReader` model (`3b6d06b2`) and the `interchange.actions`/`loops` package-json refs (`3bd5b837`, `1ea2f39b`); one local delta (CL-7190): `"message_response"` added to `signals.ts`'s `signalKinds`, alongside `signalKindToGateType`; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/workflow` | `@intx/workflow` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | No local delta: npm 0.3.0 predates the `onBodyFailure: "tolerate"` section policy (`b977ade6`) that `@corbits/agent-runtime` authors and the action/loop primitives (`3bd5b837`, `1ea2f39b`) the re-vendored `workflow-host` runs; retired by the next `@intx/workflow` publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/workflow-deploy` | `@intx/workflow-deploy` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | No local delta: npm 0.3.0 predates `inertLoopBody` and the loop-body source pin (`1ea2f39b`) that the re-vendored `hub-sessions` imports; retired by the next `@intx/workflow-deploy` publish | sawyer | 2026-10-26 | `check:killdates` | -| `vendor/intx/workflow-host` | `@intx/workflow-host` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the body-spawn authorize/credential/mail-part-reader threading and grants head-collapse (CL-6448) that let the fork run tool-bearing onTrigger bodies; retired when upstream absorbs the delta | sawyer | 2026-10-26 | `check:killdates` | +| Vendored path | What was copied | Upstream repo @ commit | Why not a published package | Owner | Kill date | Kill-date test | +| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ----------------- | +| `apps/sidecar` | Derived from upstream's own `apps/sidecar`: six modules byte-identical (`default-harness.ts`, `signing-keypair.ts`, `source-asset-delivery.ts`, `workflow-closure-apply.ts`, `workflow-probe-handler.ts`, `workflow-run-pack-restore.ts`), three near-verbatim (`atomic-write.ts` differs only by the repo-wide logger namespace), the rest (`index.ts`, `config.ts`, `tool-materialization.ts`, `step-agent-tools.ts`, `workflow-host-wiring/`, `workflow-substrate-factory/`, …) substantially rewritten, plus workbench-only modules. A living fork, not a frozen copy, so this row carries no tree hash. | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | An app is never npm-published, so no publish can cover the execution host; retired by consuming an upstream-published host, or by renewing this row deliberately | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/agent` | `@intx/agent` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the operator-configurable doom-loop threshold (`afd0c82b`, `c421c092`) the re-vendored `workflow-host` configures; one local delta (CL-7190): `ToolBundle.beforeToolExtension` (`tool.ts`) and its composition into `ResolvedTools.beforeToolExtensions` (`agent.ts`), so a tool package can contribute its own suspend-capable extension without the reactor or director special-casing it by name; retired by the next `@intx/agent` publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/db` | `@intx/db` source (`src/`, `migrations/`, drizzle config, manifest, tsconfigs) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the `wire_projection` column/loader delta (CL-6324) or the `workflow_definition.origin` column separating a definition from the per-run record of one folded run's deploy (CL-6452), shipped as migrations `0086`/`0087` behind upstream's `0085_add_approval_run_idx`, plus `0088` rewriting the retired `onBodyFailure: "continue"` literal to upstream's `"tolerate"` in stored wire projections; retired when upstream absorbs the deltas | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/harness` | `@intx/harness` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the connector reply drain (`driveConnectorReplies`, `ConnectorReplyDrain`, `AgentEventStream`; `11590e66`) the sidecar's warm mail loop drives; no local delta; retired by the next `@intx/harness` publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/hub-agent` | `@intx/hub-agent` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the `agentDir` path export (`927556de`) the sidecar's deploy-tree lookup uses, and its own `@intx/mail-memory`/`@intx/harness` pins must resolve the vendored copies; no local delta; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/hub-api` | `@intx/hub-api` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the null-principal `resolveApproval` for policy-resolved decisions (CL-6345) or the bearer-authenticated workflow-deploy mirror (`middleware/workflow-run-deploy-auth.ts`, CL-workflow-deploy-bearer); retired when upstream absorbs the deltas | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/hub-sessions` | `@intx/hub-sessions` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the pack-acceptance fixes (`ownsWorkflowRunRepo`, `anchorAddressForPackSource`, `decideTerminalRunFlip`), the adopted deploy front + `sourceRef` (CL-6324), the wire-projection writer (CL-6324), malformed tool-call-name sanitization (CL-6478), the sealed-run terminal-status backfill (CL-6595), the CL-7190 `registerSignalCorrelation` approval-only guard (fails loud on a future `SignalKind` this RPC has no persistence for, rather than silently mis-persisting one), or the CL-7191 `UserMessageParams.correlationId` plumbing into `sendUserMessage`'s `headers.interchangeCorrelationId` (a plain chat message can now resolve a parked `message_response` gate); retired when upstream absorbs the deltas | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/inference` | `@intx/inference` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates doom-loop detection (`8da4c827`, `afd0c82b`, `c421c092`); local deltas: `providers/google-genai-files.ts` builds its upload body as `new Uint8Array(bytes)` because TS 6's lib.dom `BodyInit` rejects `Uint8Array` (upstream compiles ESNext-only under TS 5.9); and CL-7190's `message_response` resume branch in `reactor.ts`'s `resumePendingOperation`/`timeoutMessageFor`, plus its `reactor.test.ts`/`testing/fakes.ts` regression harness (this package previously had zero tests); retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/mail-memory` | `@intx/mail-memory` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the `@intx/mailbox` extraction (`af03bb90`), on-demand body reads (`54f7c239`) and `expunge` returning the swept uids (`bcabb1f8`) that the re-vendored `workflow-host` binds against; no local delta; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/mailbox` | `@intx/mailbox` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | Never published: a new package at the target pin (`af03bb90`) that `workflow-host`'s substrate mailbox store and supervisor-backed transport import; no local delta; retired by its first publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/mime` | `@intx/mime` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the non-RFC message-id guard `isMessageId` (`d97e1832`), the full `References` chain (`65c6fe70`) and the lossless `decodeMail` decoder (`3b6d06b2`) that `mailbox`/`mail-memory` at the same pin import; no local delta; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/types` | `@intx/types` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 predates the type surface the re-vendored trees compile against: `expunge` returning `expungedUids` (`bcabb1f8`), plain-string `PackRejectReason` (`7b42f405`), the run authorization/approvals REST types (`71ad6c08`), the decoded-mail `Mail`/`MailPartReader` model (`3b6d06b2`) and the `interchange.actions`/`loops` package-json refs (`3bd5b837`, `1ea2f39b`); one local delta (CL-7190): `"message_response"` added to `signals.ts`'s `signalKinds`, alongside `signalKindToGateType`; retired by the next publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/workflow` | `@intx/workflow` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | No local delta: npm 0.3.0 predates the `onBodyFailure: "tolerate"` section policy (`b977ade6`) that `@corbits/agent-runtime` authors and the action/loop primitives (`3bd5b837`, `1ea2f39b`) the re-vendored `workflow-host` runs; retired by the next `@intx/workflow` publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/workflow-deploy` | `@intx/workflow-deploy` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | No local delta: npm 0.3.0 predates `inertLoopBody` and the loop-body source pin (`1ea2f39b`) that the re-vendored `hub-sessions` imports; retired by the next `@intx/workflow-deploy` publish | sawyer | 2026-10-26 | `check:killdates` | +| `vendor/intx/workflow-host` | `@intx/workflow-host` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `a8bc06ae` (origin/main, 2026-08-27) | npm 0.3.0 covers the base package but not the body-spawn authorize/credential/mail-part-reader threading and grants head-collapse (CL-6448) that let the fork run tool-bearing onTrigger bodies; retired when upstream absorbs the delta | sawyer | 2026-10-26 | `check:killdates` | The pin is `a8bc06ae` (upstream `origin/main`, 2026-08-27), 72 commits past the `v0.3.0` release tag `b5580a02`. npm is still `0.3.0`, so every tree a diff --git a/scripts/checks/kill-dates.txt b/scripts/checks/kill-dates.txt index 5740abcdd..bc46993fd 100644 --- a/scripts/checks/kill-dates.txt +++ b/scripts/checks/kill-dates.txt @@ -19,7 +19,7 @@ vendor/intx/db | sawyer | 2026-10-26 | 0a4cdb9a8a6ff19d5d4713cbc4f5cc9257aad839b vendor/intx/harness | sawyer | 2026-10-26 | 867f2b0eb4a360c68bf552d5b95a9530411e2c1a2d046d1f5ce718d4a9be36a8 vendor/intx/hub-agent | sawyer | 2026-10-26 | 30d5050511f22bc73b3c5d34728dfdf5b791de203452b83d4333a1dc762afceb vendor/intx/hub-api | sawyer | 2026-10-26 | 42ee33e027559b236065382cb94f393bbcfee69625615894f82be778f34f7aa1 -vendor/intx/hub-sessions | sawyer | 2026-10-26 | 19fcc2c0bf6af1dd9a1b267513bea777968c454a323cc402fc5ec1cca8097523 +vendor/intx/hub-sessions | sawyer | 2026-10-26 | 79a6f534a5166dfc5904d1b82214d1a0f857f2b2bde0b845115e0618f84745f2 vendor/intx/inference | sawyer | 2026-10-26 | f32792856f555b0fed40ce75badde7630a45a6452ad20cfc61058192f8376f11 vendor/intx/mail-memory | sawyer | 2026-10-26 | 9f3601a7fb22e2d1c63daa976f3afccbd79af2187c155a0080c0d60c82450b92 vendor/intx/mailbox | sawyer | 2026-10-26 | d36d7ffcc32018571276e4922a8c2714b7ee0bb5deb80b01e73859245975d4c6 diff --git a/vendor/intx/hub-sessions/VENDORED-FROM b/vendor/intx/hub-sessions/VENDORED-FROM index 0f7d6a4aa..d36678b34 100644 --- a/vendor/intx/hub-sessions/VENDORED-FROM +++ b/vendor/intx/hub-sessions/VENDORED-FROM @@ -1,4 +1,4 @@ Source: https://github.com/faremeter/interchange (packages/hub-sessions) Commit: a8bc06ae38661c5e0ed91ded8559bf09f502213d (origin/main, 2026-08-27) License: LGPL-2.1-only (see vendor/intx/LICENSE) -Local modifications: exports map repointed from the upstream intx-src condition to direct TypeScript source resolution (types/default -> ./src/...); dist references removed. Terminal-anchor pack acceptance: hub-session-lookups.ts's receiveWorkflowRunPack gates the anchor lookup on the exported pure helper ownsWorkflowRunRepo (self-anchored row with a routable address, no liveness requirement) instead of upstream's `status in (deployed, running)`, so a terminal run can still land the inbox-enqueue and markConsumed-rejection packs that retire mail arriving in its teardown window; CL-6361 widens the same lookup to peel a per-step pack source address back to its base run's anchor via anchorAddressForPackSource; CL-6379 classifies an accepted pack's newly-terminal runs through decideTerminalRunFlip so a section occurrence's repo-local child run (turn__) is skipped quietly. CL-6324: a third code-sourced deploy front, deployAdoptedCodeSourcedWorkflow (plus the deployAdoptedWorkflowFromSource service method and its AdoptingWorkflowDeployer / DeployAdoptedWorkflowFromSourceParams types), deploys onto shared capacity while ADOPTING an anchor workflow_run row the caller already owns; it composes upstream's emitSourceRefDeployFrame and a guarded UPDATE. DeployWorkflowFromSourceParams / DeployPreparedCodeSourcedWorkflowParams gain an optional sourceRef threaded through bindAssetAttachmentResolver / bindSourceAttachmentResolver so a per-run source tree on refs/heads/runs/ packs the pinned commit. CL-6324: workflow-probe-gate.ts's PersistFrozenApprovalFn carries the inert projection and createDbFrozenApprovalWriter stamps workflow_definition_version.wire_projection in the same transaction as approved_wire_hash. CL-6478: sanitize-tool-name.ts + event-collector.ts's tool_call case persist only a tool-call name encodeToolName can re-invert (anything else collapses to MALFORMED_TOOL_NAME), so one bad name fails its turn instead of wedging the room; @intx/inference is a dependency for this. CL-6595: workflow-run-kind.ts's validatePush also reports a run sealed from birth (combined events.jsonl with no per-event blobs) as newly terminal, and the new readCommittedWorkflowRunTerminalStatus export backs a same-push markTerminal backfill in hub-session-lookups.ts; both classify through upstream's classifyTerminalEvent. Retired at this pin: the per-event inference.usage forward (upstream fab86ca9 emits TurnUsage once per turn), the collector-level event serialization (upstream a1d419c3 serializes at the registry) and the anchor-before-frame ordering with DeployFrameNotSentError (upstream a203a057 + 9e11829f, isDeployFrameFailure). CL-7190: registerSignalCorrelation (hub-session-lookups.ts) now throws for any signal kind other than "approval", failing loud instead of silently mis-persisting a future SignalKind it has no co-write for. +Local modifications: exports map repointed from the upstream intx-src condition to direct TypeScript source resolution (types/default -> ./src/...); dist references removed. Terminal-anchor pack acceptance: hub-session-lookups.ts's receiveWorkflowRunPack gates the anchor lookup on the exported pure helper ownsWorkflowRunRepo (self-anchored row with a routable address, no liveness requirement) instead of upstream's `status in (deployed, running)`, so a terminal run can still land the inbox-enqueue and markConsumed-rejection packs that retire mail arriving in its teardown window; CL-6361 widens the same lookup to peel a per-step pack source address back to its base run's anchor via anchorAddressForPackSource; CL-6379 classifies an accepted pack's newly-terminal runs through decideTerminalRunFlip so a section occurrence's repo-local child run (turn__) is skipped quietly. CL-6324: a third code-sourced deploy front, deployAdoptedCodeSourcedWorkflow (plus the deployAdoptedWorkflowFromSource service method and its AdoptingWorkflowDeployer / DeployAdoptedWorkflowFromSourceParams types), deploys onto shared capacity while ADOPTING an anchor workflow_run row the caller already owns; it composes upstream's emitSourceRefDeployFrame and a guarded UPDATE. DeployWorkflowFromSourceParams / DeployPreparedCodeSourcedWorkflowParams gain an optional sourceRef threaded through bindAssetAttachmentResolver / bindSourceAttachmentResolver so a per-run source tree on refs/heads/runs/ packs the pinned commit. CL-6324: workflow-probe-gate.ts's PersistFrozenApprovalFn carries the inert projection and createDbFrozenApprovalWriter stamps workflow_definition_version.wire_projection in the same transaction as approved_wire_hash. CL-6478: sanitize-tool-name.ts + event-collector.ts's tool_call case persist only a tool-call name encodeToolName can re-invert (anything else collapses to MALFORMED_TOOL_NAME), so one bad name fails its turn instead of wedging the room; @intx/inference is a dependency for this. CL-6595: workflow-run-kind.ts's validatePush also reports a run sealed from birth (combined events.jsonl with no per-event blobs) as newly terminal, and the new readCommittedWorkflowRunTerminalStatus export backs a same-push markTerminal backfill in hub-session-lookups.ts; both classify through upstream's classifyTerminalEvent. Retired at this pin: the per-event inference.usage forward (upstream fab86ca9 emits TurnUsage once per turn), the collector-level event serialization (upstream a1d419c3 serializes at the registry) and the anchor-before-frame ordering with DeployFrameNotSentError (upstream a203a057 + 9e11829f, isDeployFrameFailure). CL-7190: registerSignalCorrelation (hub-session-lookups.ts) now throws for any signal kind other than "approval", failing loud instead of silently mis-persisting a future SignalKind it has no co-write for. CL-7191: UserMessageParams gains an optional correlationId (session-service.ts) stamped as sendUserMessage's headers.interchangeCorrelationId, so a plain chat message can resolve a parked message_response gate. From c33f258baa97bf6ab18d46939351c2d297b21fb4 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 30 Aug 2026 10:41:49 -0700 Subject: [PATCH 4/4] Bump @corbits/interaction-tools to 0.0.4 for its correlation-id source change --- packages/interaction-tools/package.json | 2 +- workflows/assistant/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/interaction-tools/package.json b/packages/interaction-tools/package.json index 8223d7b2f..d52be8cc1 100644 --- a/packages/interaction-tools/package.json +++ b/packages/interaction-tools/package.json @@ -2,7 +2,7 @@ "name": "@corbits/interaction-tools", "private": true, "description": "The ask_user tool: an @intx/agent bundle that poses an interview question in-thread as a question block and parks the turn on a message_response gate until the user answers", - "version": "0.0.3", + "version": "0.0.4", "license": "LGPL-2.1-or-later", "type": "module", "exports": { diff --git a/workflows/assistant/src/index.ts b/workflows/assistant/src/index.ts index 9174e295d..d9a6a36c4 100644 --- a/workflows/assistant/src/index.ts +++ b/workflows/assistant/src/index.ts @@ -52,7 +52,7 @@ export const ASSISTANT_TOOL_PACKAGE_PINS: readonly ToolPackagePin[] = [ { name: "@corbits/catalog-tools", version: "0.0.1" }, { name: "@corbits/skills-tools", version: "0.0.6" }, { name: "@corbits/mcp-tools", version: "0.0.10" }, - { name: "@corbits/interaction-tools", version: "0.0.3" }, + { name: "@corbits/interaction-tools", version: "0.0.4" }, { name: "@corbits/manus-tools", version: "0.0.11" }, ];