@ai-matrx/chat 0.4.10 → 0.4.14

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 (139) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/agents/components/context-items/bodies/ContextInputBody.js +1 -1
  3. package/dist/agents/components/context-items/bodies/GenericBody.js +1 -1
  4. package/dist/agents/components/conversation-actions/conversation-verbs.js +1 -1
  5. package/dist/agents/components/inputs/smart-input/RunSkillPicker.js +1 -1
  6. package/dist/agents/components/live-run/LiveRunDisplay.js +1 -1
  7. package/dist/agents/components/live-run/LiveRunProgress.js +1 -1
  8. package/dist/agents/components/live-run/RunSetDisplay.js +1 -1
  9. package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +2 -2
  10. package/dist/agents/components/messages-display/assistant/InPlaceAnswerEditor.js +1 -1
  11. package/dist/agents/components/messages-display/conversation-tools/find-in-conversation.js +1 -1
  12. package/dist/agents/components/messages-display/message-options/buildTaskSeedFromMessage.js +1 -1
  13. package/dist/agents/components/messages-display/user/AgentUserMessage.js +1 -1
  14. package/dist/agents/components/messages-display/user/FirstTurnVariables.js +1 -1
  15. package/dist/agents/components/run/AgentStatusIndicator.js +1 -1
  16. package/dist/agents/components/run/RunJobWorkingLine.js +1 -1
  17. package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
  18. package/dist/agents/components/shared/AgentPlanningIndicator.js +1 -1
  19. package/dist/agents/conversation-export/conversation-transfer.js +1 -1
  20. package/dist/agents/conversation-export/document-markdown.js +1 -1
  21. package/dist/agents/hooks/useConversationRoutePromotion.js +1 -1
  22. package/dist/agents/hooks/useModelControls.js +1 -1
  23. package/dist/agents/redux/agent-definition/converters.js +1 -1
  24. package/dist/agents/redux/agent-definition/slice.js +1 -1
  25. package/dist/agents/redux/conversation-list/conversation-list.thunks.js +1 -1
  26. package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +2 -2
  27. package/dist/agents/redux/execution-system/active-requests/active-requests.slice.d.ts +2 -0
  28. package/dist/agents/redux/execution-system/active-requests/active-requests.slice.js +7 -0
  29. package/dist/agents/redux/execution-system/conversations/conversation-persistence.js +2 -4
  30. package/dist/agents/redux/execution-system/instance-context/sync-canvas-item-context.thunk.js +1 -1
  31. package/dist/agents/redux/execution-system/thunks/dispatch-matrx-extend-tool.thunk.js +3 -1
  32. package/dist/agents/redux/execution-system/thunks/process-stream.js +27 -24
  33. package/dist/agents/redux/execution-system/thunks/run-ai-stream.d.ts +7 -0
  34. package/dist/agents/redux/execution-system/thunks/run-ai-stream.js +13 -0
  35. package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +1 -1
  36. package/dist/agents/redux/execution-system/utils/assemble-cx-content-blocks.js +1 -1
  37. package/dist/agents/redux/execution-system/utils/normalize-content-blocks.js +1 -1
  38. package/dist/agents/redux/execution-system/utils/output-contract-guard.js +1 -1
  39. package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +8 -8
  40. package/dist/agents/runtime-reconnect/api.d.ts +6 -25
  41. package/dist/agents/runtime-reconnect/api.js +7 -13
  42. package/dist/agents/runtime-reconnect/reconnect-server-operation.thunk.js +167 -159
  43. package/dist/agents/runtime-reconnect/types.d.ts +2 -0
  44. package/dist/agents/services/mcp-connections.service.d.ts +12 -0
  45. package/dist/agents/services/mcp-connections.service.js +4 -7
  46. package/dist/agents/types/request.types.d.ts +6 -0
  47. package/dist/agents/utils/canvasItemContext.js +1 -1
  48. package/dist/agents/utils/json-extraction.js +2 -2
  49. package/dist/canvas/workspace/ChatPanelTitleMenu.js +1 -1
  50. package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +1 -1
  51. package/dist/cx-chat/components/SsrSidebarChats.js +1 -1
  52. package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
  53. package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
  54. package/dist/cx-chat/hooks/useInstanceBootstrap.js +1 -1
  55. package/dist/cx-conversation/AssistantMessage.js +1 -1
  56. package/dist/host/configure.js +15 -5
  57. package/dist/host/contract.d.ts +43 -2
  58. package/dist/host/db.js +6 -0
  59. package/dist/host/defaults/diagnostics.d.ts +5 -0
  60. package/dist/host/defaults/diagnostics.js +1 -2
  61. package/dist/host/defaults/identity.d.ts +26 -0
  62. package/dist/host/defaults/identity.js +133 -1
  63. package/dist/host/identity.d.ts +19 -2
  64. package/dist/host/identity.js +18 -10
  65. package/dist/host/session-retry.js +2 -2
  66. package/dist/mandates/llm-params.d.ts +1 -1
  67. package/dist/mandates/output-contract.js +1 -1
  68. package/dist/mandates/service.d.ts +1 -1
  69. package/dist/mandates/service.js +1 -1
  70. package/dist/mandates/test-run-core.js +1 -1
  71. package/dist/public-chat/components/PublicMessageOptionsMenu.js +1 -1
  72. package/dist/surfaces/services/bind-agent-to-surface.service.js +2 -4
  73. package/dist/surfaces/services/surface-config.service.js +1 -1
  74. package/dist/tool-call-visualization/components/ToolCallBatch.js +1 -1
  75. package/dist/tool-call-visualization/components/ToolCallVisualization.js +1 -1
  76. package/dist/tool-call-visualization/renderers/agent-call/AgentCallInline.js +1 -1
  77. package/dist/tool-call-visualization/renderers/agent-call/CollabCallCard.js +2 -2
  78. package/dist/tool-call-visualization/renderers/agent-call/collab-subtitle.js +3 -3
  79. package/dist/tool-call-visualization/renderers/skill/SkillInline.js +1 -1
  80. package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +1 -1
  81. package/dist/tool-call-visualization/result-fields/ResultValue.js +1 -1
  82. package/dist/tool-call-visualization/result-fields/shape.js +1 -1
  83. package/dist/tool-call-visualization/utils/cxToolCallToLifecycleEntry.js +2 -2
  84. package/dist/tool-call-visualization/utils/human-copy.js +3 -3
  85. package/dist/ui/mandates-storage/provision-shapes.d.ts +1 -1
  86. package/dist/ui/mandates-storage/provision-shapes.js +1 -1
  87. package/dist/ui/markdown-stream/EnhancedChatMarkdown.js +1 -1
  88. package/dist/ui/treatment-shape.d.ts +1 -1
  89. package/dist/ui/treatment-shape.js +1 -1
  90. package/dist/voice-agent/relay/useVoiceRelaySession.js +1 -1
  91. package/package.json +24 -6
  92. package/dist/agents/hooks/useWarmAgent.d.ts +0 -8
  93. package/dist/agents/hooks/useWarmAgent.js +0 -25
  94. package/dist/agents/utils/artifactId.d.ts +0 -34
  95. package/dist/agents/utils/artifactId.js +0 -15
  96. package/dist/ui/ShimmerText.d.ts +0 -5
  97. package/dist/ui/ShimmerText.js +0 -21
  98. package/dist/ui/addressWithoutNavigating.d.ts +0 -68
  99. package/dist/ui/addressWithoutNavigating.js +0 -60
  100. package/dist/utils/content-ir/decision-answers-ids.d.ts +0 -3
  101. package/dist/utils/content-ir/decision-answers-ids.js +0 -6
  102. package/dist/utils/content-ir/envelope-parity.d.ts +0 -12
  103. package/dist/utils/content-ir/envelope-parity.js +0 -32
  104. package/dist/utils/content-ir/instance-title.d.ts +0 -26
  105. package/dist/utils/content-ir/instance-title.js +0 -39
  106. package/dist/utils/content-ir/kinds/kind-markdown-utils.d.ts +0 -84
  107. package/dist/utils/content-ir/kinds/kind-markdown-utils.js +0 -233
  108. package/dist/utils/content-ir/kinds/kind-value-markdown.d.ts +0 -5
  109. package/dist/utils/content-ir/kinds/kind-value-markdown.js +0 -17
  110. package/dist/utils/content-ir/legacy-quiz-envelope.d.ts +0 -3
  111. package/dist/utils/content-ir/legacy-quiz-envelope.js +0 -45
  112. package/dist/utils/content-ir/surfaces/embedded-kind-json.d.ts +0 -140
  113. package/dist/utils/content-ir/surfaces/embedded-kind-json.js +0 -527
  114. package/dist/utils/content-ir/surfaces/json-kind-signal.d.ts +0 -307
  115. package/dist/utils/content-ir/surfaces/json-kind-signal.js +0 -945
  116. package/dist/utils/content-ir/surfaces/kind-image-alt.d.ts +0 -27
  117. package/dist/utils/content-ir/surfaces/kind-image-alt.js +0 -117
  118. package/dist/utils/content-ir/surfaces/kind-one-line.d.ts +0 -67
  119. package/dist/utils/content-ir/surfaces/kind-one-line.js +0 -209
  120. package/dist/utils/content-ir/surfaces/kind-text-label.d.ts +0 -20
  121. package/dist/utils/content-ir/surfaces/kind-text-label.js +0 -50
  122. package/dist/utils/content-ir/surfaces/kind-text-to-markdown.d.ts +0 -47
  123. package/dist/utils/content-ir/surfaces/kind-text-to-markdown.js +0 -399
  124. package/dist/utils/content-ir/surfaces/markdown-escaped-kind.d.ts +0 -54
  125. package/dist/utils/content-ir/surfaces/markdown-escaped-kind.js +0 -128
  126. package/dist/utils/content-ir/surfaces/quoted-kind-lift.d.ts +0 -61
  127. package/dist/utils/content-ir/surfaces/quoted-kind-lift.js +0 -215
  128. package/dist/utils/inline-decision/decision-options.d.ts +0 -21
  129. package/dist/utils/inline-decision/decision-options.js +0 -32
  130. package/dist/utils/json.d.ts +0 -63
  131. package/dist/utils/json.js +0 -22
  132. package/dist/utils/markdown/plain-title.d.ts +0 -25
  133. package/dist/utils/markdown/plain-title.js +0 -51
  134. package/dist/utils/rendered-find.d.ts +0 -9
  135. package/dist/utils/rendered-find.js +0 -68
  136. package/dist/utils/rich-editor/core/caret-context.d.ts +0 -24
  137. package/dist/utils/rich-editor/core/caret-context.js +0 -73
  138. package/dist/utils/rich-editor/in-place/caret-handoff.d.ts +0 -3
  139. package/dist/utils/rich-editor/in-place/caret-handoff.js +0 -15
package/CHANGELOG.md CHANGED
@@ -1,5 +1,79 @@
1
1
  # @ai-matrx/chat
2
2
 
3
+ ## 0.4.14 — token-only hosts, on-device tools, and a bundle budget (W0(b), W0(c), W0(e))
4
+
5
+ - **Token-only database clients** (supabase-js `accessToken` option — matrx-local's custodian
6
+ client, where `db.auth` throws): new host field `accessToken: () => Promise<string | null>`; the
7
+ default identity reads the person from that token (admin level through `db`). A token-only `db`
8
+ with no function is signed out and settled, announced once with the remedy. The resolver no
9
+ longer touches `db.auth` on such a client.
10
+ - **One session door:** every package session read goes through the identity port
11
+ (`readSessionToken`, `hasBrowserSession`, `hostSessionSource` in `host/identity`); guard test
12
+ `host/__tests__/session-reads-go-through-the-identity-port.test.ts` fails on a direct auth-js call.
13
+ - **`deviceTools` host port** (`ChatDeviceToolsPort`): a host that IS the device (chat inside Matrx
14
+ Extend, a desktop app) executes delegated device tools; the package keeps the lifecycle, the
15
+ durable result submit and the resume. Absent: unchanged web route through the extension bridge.
16
+ - **Side-panel bundle budget:** `pnpm check:side-panel-bundle` builds the bare Vite canary and fails
17
+ when the first chat screen or the whole output (gzip) passes `side-panel-bundle-budget.json`
18
+ (measured on 0.4.9: 2435 kB / 8197 kB — a ratchet, lower it when it shrinks).
19
+
20
+ Consumer action: none for session (auth-js) hosts. A token-only host passes `accessToken` (the same
21
+ function its Supabase client got). A device host passes `deviceTools`.
22
+
23
+ ## 0.4.14 — reconnect rides the shared follow core
24
+
25
+ - Reconnect (`reconnectServerOperation`) now identifies, rejoins, follows and settles through
26
+ `followServerJob` (@ai-matrx/agents 0.57.0); `processStream` dedupes replay through the shared
27
+ `createStreamReplayCursor` (a high-water mark per `stream_id`, persisted on `ActiveRequest` as
28
+ `transportSeqByStream`). The chat-only follower (`followOperationStream`) is deleted. Fixes
29
+ replayed segment A rendering twice after a live A then B.
30
+
31
+ Consumer action: none.
32
+
33
+ ## 0.4.13 — markdown-source helpers from their one home
34
+
35
+ - `StreamingJsonTracker`, `extractAllJson`, `extractFirstJson`, `findAllFencedBlocks` are read from
36
+ `@ai-matrx/content-ir/json-extract`, and `stripThinkingStreaming` from `@ai-matrx/content-ir/source`
37
+ (kit 0.36.0 no longer carries them).
38
+
39
+ Consumer action: none.
40
+
41
+ ## Unreleased
42
+
43
+ - Chat's own message surfaces import `ui/markdown-stream/MarkdownStream` by its default export, so a host test that mocks that module (default only) replaces it everywhere, as it did when it was a slot.
44
+
45
+ ## 0.4.12 — chat keeps no copy of another package's code
46
+
47
+ - Deleted `src/utils/content-ir/**` (14 modules, 4.4k lines): every import now reads
48
+ `@ai-matrx/content-ir/surfaces`, whose versions are the stricter ones (the kind-value-markdown
49
+ registration lives on `globalThis`, so dual loaders share one registration).
50
+ - Deleted nine modules that were byte-identical to their owner and import the owner instead:
51
+ `agents/utils/artifactId`, `ui/ShimmerText`, `ui/addressWithoutNavigating`,
52
+ `utils/inline-decision/decision-options`, `utils/json`, `utils/markdown/plain-title`,
53
+ `utils/rendered-find` → `@ai-matrx/rich-content/utils/…`; `utils/rich-editor/core/caret-context`,
54
+ `utils/rich-editor/in-place/caret-handoff` → `@ai-matrx/rich-editor/…` (one caret hand-off
55
+ state instead of two).
56
+
57
+ Consumer action: replace these specifiers —
58
+ `@ai-matrx/chat/utils/content-ir/**` → `@ai-matrx/content-ir/surfaces` (re-register
59
+ `registerKindValueMarkdown` from there); `@ai-matrx/chat/agents/utils/artifactId` →
60
+ `@ai-matrx/rich-content/utils/lifted/artifactId`; `@ai-matrx/chat/ui/ShimmerText` →
61
+ `@ai-matrx/rich-content/utils/lifted/ShimmerText`; `@ai-matrx/chat/ui/addressWithoutNavigating` →
62
+ `@ai-matrx/rich-content/utils/addressWithoutNavigating`;
63
+ `@ai-matrx/chat/utils/inline-decision/decision-options` → `@ai-matrx/rich-content/utils/decision-options`;
64
+ `@ai-matrx/chat/utils/json` → `@ai-matrx/rich-content/utils/json`;
65
+ `@ai-matrx/chat/utils/markdown/plain-title` → `@ai-matrx/rich-content/utils/plain-title`;
66
+ `@ai-matrx/chat/utils/rendered-find` → `@ai-matrx/rich-content/utils/rendered-find`.
67
+
68
+ ## 0.4.11 — every AI run request reports its client latency
69
+
70
+ - `runAiStream` sends `X-Matrx-Client-Sent-At` (epoch ms of the frozen submission moment) and
71
+ `X-Matrx-Client-Prep-Ms` (submission to fetch dispatch) on every turn and resume request
72
+ (`buildLatencyHeaders`); the server persists one `platform.ai_request_latency` row per request.
73
+ - Forcing test `agents/redux/execution-system/thunks/__tests__/every-ai-run-request-reports-its-client-latency.test.ts`.
74
+
75
+ Consumer action: none.
76
+
3
77
  ## 0.4.10 — a confidential conversation never reaches a cloud target
4
78
 
5
79
  - THE CONFIDENTIAL-ROUTING RULE moves from the frozen kernel (`src/compat`) into the real chat:
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { humanizeIdentifier } from "@ai-matrx/kit/text-case";
4
4
  import { AnswerValueView } from "../../../../host/ui-slots.js";
5
- import { valueCarriesKind } from "../../../../utils/content-ir/surfaces/json-kind-signal.js";
5
+ import { valueCarriesKind } from "@ai-matrx/content-ir/surfaces";
6
6
  function displayValue(value) {
7
7
  if (typeof value === "string") return value;
8
8
  if (value === null) return "null";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { AnswerValueView } from "../../../../host/ui-slots.js";
4
- import { hasKindKeyAnySpelling, valueCarriesKind } from "../../../../utils/content-ir/surfaces/json-kind-signal.js";
4
+ import { hasKindKeyAnySpelling, valueCarriesKind } from "@ai-matrx/content-ir/surfaces";
5
5
  function carriesKind(value) {
6
6
  return valueCarriesKind(value);
7
7
  }
@@ -7,7 +7,7 @@ import {
7
7
  renameConversation
8
8
  } from "../../redux/conversation-list/conversation-row-actions.thunks.js";
9
9
  import { conversationRenameOpener } from "./rename/conversationRenameOpener.js";
10
- import { conversationTitleText } from "../../../utils/content-ir/surfaces/kind-text-label.js";
10
+ import { conversationTitleText } from "@ai-matrx/content-ir/surfaces";
11
11
  function displayConversationTitle(title) {
12
12
  if (!title) return "Untitled conversation";
13
13
  return title.trim().length > 0 ? conversationTitleText(title) ?? title : "Untitled conversation";
@@ -28,7 +28,7 @@ import {
28
28
  PicksSkeletons
29
29
  } from "./RunPicksSurface.js";
30
30
  import { groupCatalog, toolCategoryLabel } from "./run-tool-catalog.js";
31
- import { catalogProseText } from "../../../../utils/content-ir/surfaces/kind-one-line.js";
31
+ import { catalogProseText } from "@ai-matrx/content-ir/surfaces";
32
32
  function tierForSkill(skillId, config) {
33
33
  if (config.included.includes(skillId)) return "included";
34
34
  if (config.listed.includes(skillId)) return "listed";
@@ -4,7 +4,7 @@ import { useEffect, useRef } from "react";
4
4
  import { Loader2, TriangleAlert, X } from "lucide-react";
5
5
  import { cn } from "@ai-matrx/design-system";
6
6
  import { useAppSelector } from "../../../store/hooks.js";
7
- import { MarkdownStream } from "../../../ui/markdown-stream/MarkdownStream.js";
7
+ import MarkdownStream from "../../../ui/markdown-stream/MarkdownStream.js";
8
8
  import { AgentAssistantMessage } from "../messages-display/assistant/AgentAssistantMessage.js";
9
9
  import { useRetainRequestForViewer } from "../../redux/execution-system/active-requests/useRetainRequestForViewer.js";
10
10
  import {
@@ -5,7 +5,7 @@ import { cn } from "@ai-matrx/design-system";
5
5
  import {
6
6
  honestProgressSummary
7
7
  } from "@ai-matrx/kit/progress";
8
- import { isJsonObject } from "../../../utils/json.js";
8
+ import { isJsonObject } from "@ai-matrx/rich-content/utils/json";
9
9
  const STATUSES = /* @__PURE__ */ new Set([
10
10
  "waiting",
11
11
  "running",
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useAppDispatch, useAppSelector } from "../../../store/hooks.js";
4
- import { MarkdownStream } from "../../../ui/markdown-stream/MarkdownStream.js";
4
+ import MarkdownStream from "../../../ui/markdown-stream/MarkdownStream.js";
5
5
  import { cn } from "@ai-matrx/design-system";
6
6
  import { LiveRunDisplay } from "./LiveRunDisplay.js";
7
7
  import { selectRunSetEntries } from "../../redux/execution-system/run-sets/run-sets.slice.js";
@@ -7,7 +7,7 @@ import {
7
7
  useState,
8
8
  useEffect
9
9
  } from "react";
10
- import { MarkdownStream } from "../../../../ui/markdown-stream/MarkdownStream.js";
10
+ import MarkdownStream from "../../../../ui/markdown-stream/MarkdownStream.js";
11
11
  import "../rich-document/chat-message-source.js";
12
12
  import { RecordAnnotations, annotationRecordOf } from "@ai-matrx/rich-content/host/app-bindings";
13
13
  import { useAppDispatch, useAppSelector } from "../../../../store/hooks.js";
@@ -75,7 +75,7 @@ import { retryConversationTurn } from "../../../redux/execution-system/message-c
75
75
  import { commitInlineContentEdit } from "../../../redux/execution-system/message-crud/commit-inline-edit.thunk.js";
76
76
  import { InPlaceAnswerEditor } from "./InPlaceAnswerEditor.js";
77
77
  import { updateMessageRecord } from "../../../redux/execution-system/messages/messages.slice.js";
78
- import { handInPlaceCaret } from "../../../../utils/rich-editor/in-place/caret-handoff.js";
78
+ import { handInPlaceCaret } from "@ai-matrx/rich-editor/in-place/caret-handoff";
79
79
  import { toast } from "../../../../host/notify.js";
80
80
  import { useDomCapturePrint } from "../../../../conversation/hooks/useDomCapturePrint.js";
81
81
  import { MessageFilesStrip } from "../../../../host/ui-slots.js";
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
4
  import { Loader2 } from "lucide-react";
5
- import { takeInPlaceCaret } from "../../../../utils/rich-editor/in-place/caret-handoff.js";
5
+ import { takeInPlaceCaret } from "@ai-matrx/rich-editor/in-place/caret-handoff";
6
6
  import { useAppDispatch } from "../../../../store/hooks.js";
7
7
  import { updateMessageRecord } from "../../../redux/execution-system/messages/messages.slice.js";
8
8
  import {
@@ -1,4 +1,4 @@
1
- import { collectRenderedFindRanges, findRenderedTextMatches } from "../../../../utils/rendered-find.js";
1
+ import { collectRenderedFindRanges, findRenderedTextMatches } from "@ai-matrx/rich-content/utils/rendered-find";
2
2
  function findTextMatches(text, query) {
3
3
  return findRenderedTextMatches(text, query.trim());
4
4
  }
@@ -1,5 +1,5 @@
1
1
  import { durableRecordId } from "@ai-matrx/kit/ids";
2
- import { plainTitleFromMarkdown } from "../../../../utils/markdown/plain-title.js";
2
+ import { plainTitleFromMarkdown } from "@ai-matrx/rich-content/utils/plain-title";
3
3
  import { cleanMarkdown } from "@ai-matrx/content-ir/source";
4
4
  import { buildConversationMessageTitle } from "../../../utils/conversation-message-title.js";
5
5
  function buildMessagePreviewLabel(content, maxLength = 160) {
@@ -36,7 +36,7 @@ import {
36
36
  } from "../../../redux/execution-system/instance-variable-values/instance-variable-values.selectors.js";
37
37
  import { MessageAttachmentStrip } from "../MessageAttachmentStrip.js";
38
38
  import { isAttachmentMessagePart } from "../../context-items/normalize.js";
39
- import { MarkdownStream } from "../../../../ui/markdown-stream/MarkdownStream.js";
39
+ import MarkdownStream from "../../../../ui/markdown-stream/MarkdownStream.js";
40
40
  import "../rich-document/chat-message-source.js";
41
41
  import { literalUserText } from "./literal-user-text.js";
42
42
  import { buildVariableDisplayLines } from "../../../utils/variable-display-lines.js";
@@ -11,7 +11,7 @@ import { buildVariableDisplayLines } from "../../../utils/variable-display-lines
11
11
  import { EntityRef } from "../../../../host/ui-slots.js";
12
12
  import { useEntityTitles } from "../../../../host/ui-slots.js";
13
13
  import { AnswerValueView } from "../../../../host/ui-slots.js";
14
- import { isKindJsonText, valueCarriesKind } from "../../../../utils/content-ir/surfaces/json-kind-signal.js";
14
+ import { isKindJsonText, valueCarriesKind } from "@ai-matrx/content-ir/surfaces";
15
15
  function kindCarryingValue(value) {
16
16
  if (!valueCarriesKind(value)) return null;
17
17
  if (typeof value === "string" && isKindJsonText(value)) {
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { ShimmerText } from "../../../ui/ShimmerText.js";
3
+ import { ShimmerText } from "@ai-matrx/rich-content/utils/lifted/ShimmerText";
4
4
  function AgentStatusIndicator({
5
5
  message,
6
6
  compact = false
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
- import { ShimmerText } from "../../../ui/ShimmerText.js";
4
+ import { ShimmerText } from "@ai-matrx/rich-content/utils/lifted/ShimmerText";
5
5
  import { useAppSelector } from "../../../store/hooks.js";
6
6
  import {
7
7
  selectRequestGenerationJob,
@@ -6,7 +6,7 @@ import { useAppDispatch, useAppSelector } from "../../../store/hooks.js";
6
6
  import { startNewConversation } from "../../redux/execution-system/thunks/create-instance.thunk.js";
7
7
  import { PlusTapButton } from "@ai-matrx/tap-target/buttons";
8
8
  import { selectFocusedConversation } from "../../redux/execution-system/conversation-focus/conversation-focus.selectors.js";
9
- import { pushAddressWithoutNavigating } from "../../../ui/addressWithoutNavigating.js";
9
+ import { pushAddressWithoutNavigating } from "@ai-matrx/rich-content/utils/addressWithoutNavigating";
10
10
  function AgentNewRunButton({ surfaceKey }) {
11
11
  const conversationId = useAppSelector(selectFocusedConversation(surfaceKey));
12
12
  const dispatch = useAppDispatch();
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { ShimmerText } from "../../../ui/ShimmerText.js";
3
+ import { ShimmerText } from "@ai-matrx/rich-content/utils/lifted/ShimmerText";
4
4
  function AgentPlanningIndicator({
5
5
  compact = false,
6
6
  label = "Planning..."
@@ -1,6 +1,6 @@
1
1
  import { toast } from "../../host/notify.js";
2
2
  import { unwrapKindEnvelopes } from "@ai-matrx/content-ir/source";
3
- import { kindTextToMarkdown } from "../../utils/content-ir/surfaces/kind-text-to-markdown.js";
3
+ import { kindTextToMarkdown } from "@ai-matrx/content-ir/surfaces";
4
4
  import { extractFlatText } from "../redux/execution-system/messages/messages.selectors.js";
5
5
  import { selectConversationTitle } from "../redux/execution-system/conversations/conversations.selectors.js";
6
6
  import { isMessagePinned } from "../message-pins/pinned-messages-store.js";
@@ -1,5 +1,5 @@
1
1
  import { unwrapKindEnvelopes } from "@ai-matrx/content-ir/source";
2
- import { kindTextToMarkdown } from "../../utils/content-ir/surfaces/kind-text-to-markdown.js";
2
+ import { kindTextToMarkdown } from "@ai-matrx/content-ir/surfaces";
3
3
  function documentMarkdown(markdown) {
4
4
  return kindTextToMarkdown(unwrapKindEnvelopes(markdown));
5
5
  }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { useEffect, useEffectEvent, useRef } from "react";
3
3
  import { useRouter } from "../../host/navigation.js";
4
- import { replaceAddressWithoutNavigating } from "../../ui/addressWithoutNavigating.js";
4
+ import { replaceAddressWithoutNavigating } from "@ai-matrx/rich-content/utils/addressWithoutNavigating";
5
5
  import { useAppDispatch, useAppSelector, useAppStore } from "../../store/hooks.js";
6
6
  import { waitForConversationPersisted } from "../redux/execution-system/conversations/conversation-persistence.js";
7
7
  import { selectMessageCount } from "../redux/execution-system/messages/messages.selectors.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { LLM_PARAMS_KEYS } from "@ai-matrx/agents/generated/llm-enums";
3
3
  import { UI_GATE_KEYS } from "../redux/agent-settings/ui-gates.js";
4
- import { isJsonObject } from "../../utils/json.js";
4
+ import { isJsonObject } from "@ai-matrx/rich-content/utils/json";
5
5
  import { outputFormatControlKey } from "@ai-matrx/agents/models";
6
6
  function supportsTools(normalizedControls) {
7
7
  const normalizedTools = normalizedControls?.tools;
@@ -15,7 +15,7 @@ import { sanitizeAgentToolIds } from "./sanitize-tool-ids.js";
15
15
  import { stripNullish } from "../../../ui/payload.js";
16
16
  import { OrganizationContextError } from "@ai-matrx/agents/matrx";
17
17
  import { parseUiGates } from "../agent-settings/ui-gates.js";
18
- import { isJsonObject } from "../../../utils/json.js";
18
+ import { isJsonObject } from "@ai-matrx/rich-content/utils/json";
19
19
  import {
20
20
  recordAgentDataIssue,
21
21
  recoverAgentDataField
@@ -1,5 +1,5 @@
1
1
  import { createSlice } from "@reduxjs/toolkit";
2
- import { isJsonObject } from "../../../utils/json.js";
2
+ import { isJsonObject } from "@ai-matrx/rich-content/utils/json";
3
3
  import {
4
4
  shouldUpgradeFetchStatus
5
5
  } from "../../types/agent-definition.types.js";
@@ -16,7 +16,7 @@ import {
16
16
  import { CONVERSATION_LIST_PAGE_SIZE } from "./conversation-list.types.js";
17
17
  import { excludeHiddenLane } from "../conversation-history/history-filters.js";
18
18
  import { getUserId, hasBrowserSession } from "../../../host/identity.js";
19
- import { conversationTitleText } from "../../../utils/content-ir/surfaces/kind-text-label.js";
19
+ import { conversationTitleText } from "@ai-matrx/content-ir/surfaces";
20
20
  function resolveCanonicalAgentIdForConversationsFetch(input, state) {
21
21
  const versionFilter = input.versionFilter === void 0 ? null : input.versionFilter;
22
22
  let mapKey;
@@ -5,7 +5,7 @@ import {
5
5
  import { createSelector } from "@reduxjs/toolkit";
6
6
  import { isAnswerKeptWarning } from "./answer-kept-warning.js";
7
7
  import { blockMediaFileId } from "../utils/block-media-identity.js";
8
- import { DECISION_ANSWERS_BLOCK_TYPE } from "../../../../utils/content-ir/decision-answers-ids.js";
8
+ import { DECISION_ANSWERS_BLOCK_TYPE } from "@ai-matrx/content-ir/surfaces";
9
9
  import { decisionAnswersText } from "@ai-matrx/agents/presentation/decision-answers";
10
10
  import {
11
11
  readEnvelope,
@@ -17,7 +17,7 @@ import {
17
17
  insertCitationMarkers
18
18
  } from "../messages/message-citations.js";
19
19
  import { soleFence } from "@ai-matrx/content-ir/source";
20
- import { stripThinkingStreaming } from "@ai-matrx/kit/text";
20
+ import { stripThinkingStreaming } from "@ai-matrx/content-ir/source";
21
21
  const EMPTY_REQUEST_IDS = [];
22
22
  const EMPTY_ACTIVE_REQUESTS = [];
23
23
  const selectRequest = (requestId) => (state) => state.activeRequests.byRequestId[requestId];
@@ -66,6 +66,8 @@ export declare const createRequest: import("@reduxjs/toolkit").ActionCreatorWith
66
66
  requestId: string;
67
67
  streamSeq: number;
68
68
  streamId?: string | null;
69
+ /** The cursor's per-segment high-water marks (replay dedup survives A→B). */
70
+ marks?: Record<string, number>;
69
71
  }, "activeRequests/recordTransportSeq">, setRequestStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
70
72
  requestId: string;
71
73
  status: RequestStatus;
@@ -269,6 +269,13 @@ const activeRequestsSlice = createSlice({
269
269
  recordTransportSeq(state, action) {
270
270
  const request = state.byRequestId[action.payload.requestId];
271
271
  if (!request) return;
272
+ if (action.payload.marks) {
273
+ const merged = { ...request.transportSeqByStream ?? {} };
274
+ for (const [id, seq] of Object.entries(action.payload.marks)) {
275
+ merged[id] = Math.max(merged[id] ?? 0, seq);
276
+ }
277
+ request.transportSeqByStream = merged;
278
+ }
272
279
  const streamId = action.payload.streamId ?? null;
273
280
  if (streamId && streamId !== request.transportStreamId) {
274
281
  request.transportStreamId = streamId;
@@ -1,14 +1,12 @@
1
1
  import { supabase } from "../../../../host/db.js";
2
+ import { hasBrowserSession } from "../../../../host/identity.js";
2
3
  async function waitForConversationPersisted(conversationId, {
3
4
  signal,
4
5
  timeoutMs = 18e4,
5
6
  intervalMs = 250,
6
7
  maxIntervalMs = 1500
7
8
  } = {}) {
8
- const {
9
- data: { session }
10
- } = await supabase.auth.getSession();
11
- if (!session) {
9
+ if (!await hasBrowserSession()) {
12
10
  console.info(
13
11
  `[conversation-persistence] Guest session \u2014 skipping persistence poll for ${conversationId} (URL stays on /new; server persists under the guest's anonymous user).`
14
12
  );
@@ -8,7 +8,7 @@ import { applyContextDeltaToContent } from "../instance-working-document/context
8
8
  import { CANVAS_ITEM_UPDATED_EVENT } from "../../../utils/canvas-item-event.js";
9
9
  import { invalidateCanvasItemCache } from "../../../../host/ui-slots.js";
10
10
  import { canvasGetVersionHistory, canvasGetById } from "../../../../host/ui-slots.js";
11
- import { isMaterializedArtifactId } from "../../../utils/artifactId.js";
11
+ import { isMaterializedArtifactId } from "@ai-matrx/rich-content/utils/lifted/artifactId";
12
12
  import { recordUnavailable } from "../../../../host/diagnostics.js";
13
13
  function extractBody(content) {
14
14
  const stored = content;
@@ -2,13 +2,15 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
2
2
  import { submitToolResult } from "../../../api/submit-tool-results.js";
3
3
  import { upsertToolLifecycle } from "../active-requests/active-requests.slice.js";
4
4
  import { invokeMatrxExtendTool } from "../../../../host/server/extension-bridge.js";
5
+ import { getChatHost, isChatHostConfigured } from "../../../../host/configure.js";
5
6
  import { extractErrorMessage } from "@ai-matrx/data/net";
6
7
  const dispatchMatrxExtendTool = createAsyncThunk(
7
8
  "matrxExtendTools/dispatch",
8
9
  async ({ conversationId, requestId, callId, toolName, args }, { dispatch }) => {
9
10
  const startedAt = performance.now();
10
11
  try {
11
- const invocation = await invokeMatrxExtendTool(toolName, args);
12
+ const deviceTools = isChatHostConfigured() ? getChatHost().deviceTools : null;
13
+ const invocation = deviceTools ? await deviceTools.invoke(toolName, args, { conversationId, callId }) : await invokeMatrxExtendTool(toolName, args);
12
14
  if (!invocation.handled) {
13
15
  const message = invocation.reason === "matrx_extend_unavailable" ? `'${toolName}' runs inside the Matrx Extend browser extension, and no Matrx Extend is installed or reachable in this browser right now. Nothing ran and nothing was changed. Tell the user plainly, and ask them to install or enable Matrx Extend and reload this page \u2014 or take a route that does not need the extension.` : `Matrx Extend is installed here, but it does not own a tool named '${toolName}', so nothing ran and nothing was changed. Do not retry the same name: use a tool that is actually offered this turn, or tell the user this capability is not available on this browser.`;
14
16
  dispatch(
@@ -6,6 +6,7 @@ import {
6
6
  } from "../../../../host/usage-gate.js";
7
7
  import { parseNdjsonStream } from "../../../../host/server/stream-parser.js";
8
8
  import {
9
+ createStreamReplayCursor,
9
10
  isStreamTransportLost,
10
11
  readStreamCursor,
11
12
  StreamTransportError
@@ -114,7 +115,7 @@ import {
114
115
  } from "../instance-context/sync-canvas-item-context.thunk.js";
115
116
  import { docKindForContextKey } from "../../../utils/workingDocumentContext.js";
116
117
  import { isCanvasItemContextKey } from "../../../utils/canvasItemContext.js";
117
- import { StreamingJsonTracker } from "@ai-matrx/kit/json-extract";
118
+ import { StreamingJsonTracker } from "@ai-matrx/content-ir/json-extract";
118
119
  import { StreamBlockAccumulator } from "../utils/stream-block-accumulator.js";
119
120
  import { deriveAnswerDataText } from "../active-requests/active-requests.selectors.js";
120
121
  import { captureError } from "../../../../host/diagnostics.js";
@@ -143,7 +144,6 @@ import { getCapabilitiesForConversation } from "../../../runtime/get-model-capab
143
144
  import { toast } from "../../../../host/notify.js";
144
145
  import { isDirectiveApplyEvent } from "@ai-matrx/agents/envelope";
145
146
  import { proposeDirective } from "../../proposed-directives/proposedDirectivesSlice.js";
146
- const readTransportCursor = readStreamCursor;
147
147
  function renderBlockToContentType(type) {
148
148
  if (type === "image" || type === "image_output") return "image";
149
149
  if (type === "audio_output") return "audio";
@@ -187,7 +187,7 @@ import { StreamProfiler } from "../../../../ui/stream-profiler.js";
187
187
  import { makePartialKindStalenessGate } from "@ai-matrx/content-ir/wire";
188
188
  import { prepareInboundRenderBlock } from "../utils/inbound-render-block.js";
189
189
  import { progressDataRenderBlock } from "../utils/progress-data-block.js";
190
- import { DECISION_ANSWERS_BLOCK_TYPE } from "../../../../utils/content-ir/decision-answers-ids.js";
190
+ import { DECISION_ANSWERS_BLOCK_TYPE } from "@ai-matrx/content-ir/surfaces";
191
191
  import { recordContextReceipt } from "../context-rules/receipt-check.js";
192
192
  import { assembleMessageParts } from "../utils/assemble-cx-content-blocks.js";
193
193
  import { materializeMessageArtifacts } from "../../../../host/ui-slots.js";
@@ -395,7 +395,8 @@ async function processStream({
395
395
  recordTransportSeq({
396
396
  requestId,
397
397
  streamSeq: cursor.streamSeq,
398
- streamId: cursor.streamId
398
+ streamId: cursor.streamId,
399
+ marks: cursor.marks
399
400
  })
400
401
  );
401
402
  }
@@ -455,8 +456,22 @@ async function processStream({
455
456
  dispatch(setRequestStatus({ requestId, status: "error", error }));
456
457
  dispatch(setInstanceStatus({ conversationId, status: "error" }));
457
458
  };
458
- let lastTransportSeq = getState().activeRequests.byRequestId[requestId]?.lastTransportSeq ?? 0;
459
- let transportStreamId = getState().activeRequests.byRequestId[requestId]?.transportStreamId ?? null;
459
+ const seededRequest = getState().activeRequests.byRequestId[requestId];
460
+ const transportCursor = createStreamReplayCursor({
461
+ seed: {
462
+ streamId: seededRequest?.transportStreamId ?? null,
463
+ lastSeq: seededRequest?.lastTransportSeq ?? 0,
464
+ marks: seededRequest?.transportSeqByStream ?? {}
465
+ },
466
+ // New emitter segment: retain prior rendered content, but make the next
467
+ // client block begin after it. This mirrors a fresh local resume request
468
+ // without blanking the retained viewer.
469
+ onSegmentChange: () => {
470
+ blockAccumulator.rewindToBlockCount(
471
+ getState().activeRequests.byRequestId[requestId]?.renderBlockOrder.length ?? 0
472
+ );
473
+ }
474
+ });
460
475
  let retainedEntry;
461
476
  const consume = async (nextResponse, nextAbortController, nextOnEvent) => {
462
477
  if (retainedEntry.busy) {
@@ -480,25 +495,13 @@ async function processStream({
480
495
  let sawTerminalEvent = false;
481
496
  try {
482
497
  for await (const event of events) {
483
- const transportCursor = readTransportCursor(event);
484
- if (transportCursor !== null) {
485
- const segmentChanged = transportCursor.streamId !== null && transportStreamId !== null && transportCursor.streamId !== transportStreamId;
486
- if (segmentChanged) {
487
- blockAccumulator.rewindToBlockCount(
488
- getState().activeRequests.byRequestId[requestId]?.renderBlockOrder.length ?? 0
489
- );
490
- lastTransportSeq = 0;
491
- }
492
- if (transportCursor.streamId !== null) {
493
- transportStreamId = transportCursor.streamId;
494
- }
495
- if (transportCursor.streamSeq <= lastTransportSeq) {
496
- continue;
497
- }
498
- lastTransportSeq = transportCursor.streamSeq;
498
+ const stamp = readStreamCursor(event);
499
+ if (stamp !== null) {
500
+ if (!transportCursor.admit(event)) continue;
499
501
  pendingTransportCursor = {
500
- streamSeq: transportCursor.streamSeq,
501
- streamId: transportCursor.streamId
502
+ streamSeq: stamp.streamSeq,
503
+ streamId: stamp.streamId,
504
+ marks: transportCursor.marks
502
505
  };
503
506
  scheduleBatchEvent();
504
507
  }
@@ -201,6 +201,13 @@ export interface UnprocessableErrorClassification {
201
201
  }
202
202
  /** Classify HTTP 422s by server code; message text is legacy fallback only. */
203
203
  export declare function classifyUnprocessableError(errorCode: string | null, serverMessage: string): UnprocessableErrorClassification;
204
+ /**
205
+ * `X-Matrx-Client-Sent-At` (epoch ms of the frozen submission moment) and
206
+ * `X-Matrx-Client-Prep-Ms` (submission → fetch dispatch). `submitAt` is a
207
+ * `performance.now()` stamp, so the epoch time is derived from the monotonic
208
+ * elapsed time — immune to wall-clock steps between submit and dispatch.
209
+ */
210
+ export declare function buildLatencyHeaders(submitAt: number, nowPerf?: number, nowEpoch?: number): Record<string, string>;
204
211
  /**
205
212
  * Retry only after the server explicitly rejected the POST before execution
206
213
  * with `X-Matrx-Drain`. Once that proof exists, brief connection failures are
@@ -180,6 +180,13 @@ function waitForDrainRetry(ms, signal) {
180
180
  signal.addEventListener("abort", onAbort, { once: true });
181
181
  });
182
182
  }
183
+ function buildLatencyHeaders(submitAt, nowPerf = performance.now(), nowEpoch = Date.now()) {
184
+ const prepMs = Math.max(0, nowPerf - submitAt);
185
+ return {
186
+ "X-Matrx-Client-Sent-At": String(Math.round(nowEpoch - prepMs)),
187
+ "X-Matrx-Client-Prep-Ms": String(Math.round(prepMs))
188
+ };
189
+ }
183
190
  async function fetchThroughDeploymentDrain(url, init, signal) {
184
191
  const deadline = Date.now() + DEPLOYMENT_DRAIN_MAX_WAIT_MS;
185
192
  let drainObserved = false;
@@ -299,6 +306,11 @@ async function runAiStreamOnce(args) {
299
306
  );
300
307
  }
301
308
  }
309
+ if (kind !== "rejoin") {
310
+ Object.assign(fetchInit, {
311
+ headers: { ...headers, ...buildLatencyHeaders(submitAt) }
312
+ });
313
+ }
302
314
  response = kind === "rejoin" ? await fetchRejoin(
303
315
  url,
304
316
  fetchInit,
@@ -586,6 +598,7 @@ export {
586
598
  StreamCancelledError,
587
599
  StreamPhaseError,
588
600
  StreamRejoinUnavailableError,
601
+ buildLatencyHeaders,
589
602
  classifyUnprocessableError,
590
603
  fetchThroughDeploymentDrain,
591
604
  parseApiErrorBody,
@@ -1,4 +1,4 @@
1
- import { extractAllJson, extractFirstJson } from "@ai-matrx/kit/json-extract";
1
+ import { extractAllJson, extractFirstJson } from "@ai-matrx/content-ir/json-extract";
2
2
  import { extractErrorMessage } from "@ai-matrx/data/net";
3
3
  import { destroyInstanceIfAllowed } from "../conversations/conversations.thunks.js";
4
4
  import { captureError } from "../../../../host/diagnostics.js";
@@ -9,7 +9,7 @@ import { readEnvelope } from "@ai-matrx/content-ir";
9
9
  import {
10
10
  DECISION_ANSWERS_BLOCK_TYPE,
11
11
  DECISION_ANSWERS_KIND
12
- } from "../../../../utils/content-ir/decision-answers-ids.js";
12
+ } from "@ai-matrx/content-ir/surfaces";
13
13
  function isReasoningEnd(e) {
14
14
  return e.kind === "reasoning_end";
15
15
  }
@@ -4,7 +4,7 @@ import {
4
4
  fromCxAudioPart,
5
5
  fromCxVideoPart
6
6
  } from "@ai-matrx/media/files";
7
- import { DECISION_ANSWERS_BLOCK_TYPE } from "../../../../utils/content-ir/decision-answers-ids.js";
7
+ import { DECISION_ANSWERS_BLOCK_TYPE } from "@ai-matrx/content-ir/surfaces";
8
8
  import { withPerformedScript } from "../../../speech-script/types.js";
9
9
  import { seedPersistedEnvelopeCache } from "@ai-matrx/rich-content/kinds/registry/region-envelope-memo";
10
10
  function normalizeContentBlocks(rawBlocks) {
@@ -3,7 +3,7 @@ import { hasField } from "@ai-matrx/agents/field-flags";
3
3
  import { peekMandateCatalogueEntry } from "../../../../host/ui-slots.js";
4
4
  import { fetchAgentOutputSchemas } from "../../../../mandates/output-contract.js";
5
5
  import { SURFACE_WRITE_TOOL_NAME } from "../../../../surfaces/runtime/surface-writeback.js";
6
- import { isJsonObject } from "../../../../utils/json.js";
6
+ import { isJsonObject } from "@ai-matrx/rich-content/utils/json";
7
7
  import { readAgentName } from "../../../identity/agent-identity.js";
8
8
  function schemaName(schema) {
9
9
  if (!isJsonObject(schema)) return null;