@ai-matrx/chat 0.7.6 → 0.7.8

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 (65) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/agents/api/fetch-pending-calls.js +4 -3
  3. package/dist/agents/api/submit-tool-results.js +3 -6
  4. package/dist/agents/components/chat/begin-fresh-chat.js +2 -2
  5. package/dist/agents/components/inputs/smart-input/InputActionButtons.d.ts +2 -2
  6. package/dist/agents/components/inputs/smart-input/InputActionButtons.js +3 -1
  7. package/dist/agents/components/inputs/smart-input/SmartAgentInputStacked.js +8 -3
  8. package/dist/agents/components/inputs/smart-input/composer/ComposerSplash.js +2 -3
  9. package/dist/agents/components/inputs/smart-input/composer/useComposerMode.js +4 -2
  10. package/dist/agents/components/live-run/LiveRunDisplay.d.ts +7 -1
  11. package/dist/agents/components/live-run/LiveRunDisplay.js +3 -1
  12. package/dist/agents/components/messages-display/assistant/AssistantMessageFooter.js +4 -2
  13. package/dist/agents/components/shared/transcript-layout.d.ts +9 -8
  14. package/dist/agents/components/shared/transcript-layout.js +2 -2
  15. package/dist/agents/redux/agent-definition/agent-cache-bust-request.js +7 -7
  16. package/dist/agents/redux/execution-system/context-rules/receipt-check.js +2 -2
  17. package/dist/agents/redux/execution-system/inbox/inbox.thunks.d.ts +2 -2
  18. package/dist/agents/redux/execution-system/inbox/inbox.thunks.js +7 -15
  19. package/dist/agents/redux/execution-system/observational-memory/fetch-memory-cost.thunk.d.ts +1 -1
  20. package/dist/agents/redux/execution-system/observational-memory/fetch-memory-cost.thunk.js +7 -4
  21. package/dist/agents/redux/execution-system/selectors/aggregate.selectors.d.ts +1 -114
  22. package/dist/agents/redux/execution-system/selectors/aggregate.selectors.js +28 -43
  23. package/dist/agents/redux/execution-system/thunks/call-conversation-api.d.ts +44 -0
  24. package/dist/agents/redux/execution-system/thunks/call-conversation-api.js +78 -0
  25. package/dist/agents/redux/execution-system/thunks/continue-agent-json.d.ts +41 -0
  26. package/dist/agents/redux/execution-system/thunks/continue-agent-json.js +66 -0
  27. package/dist/agents/redux/execution-system/thunks/execute-instance.thunk.d.ts +20 -0
  28. package/dist/agents/redux/execution-system/thunks/execute-instance.thunk.js +9 -2
  29. package/dist/agents/redux/execution-system/thunks/launch-agent-execution.thunk.js +4 -6
  30. package/dist/agents/redux/execution-system/thunks/notify-and-play-dispatcher.d.ts +1 -5
  31. package/dist/agents/redux/execution-system/thunks/notify-and-play-dispatcher.js +2 -1
  32. package/dist/agents/redux/execution-system/thunks/resolve-base-url.js +7 -17
  33. package/dist/agents/redux/execution-system/thunks/settle-after-stop.thunk.js +2 -2
  34. package/dist/agents/redux/execution-system/thunks/smart-execute.thunk.js +3 -3
  35. package/dist/agents/redux/execution-system/utils/continuation-body.d.ts +8 -2
  36. package/dist/agents/redux/execution-system/utils/continuation-body.js +3 -3
  37. package/dist/agents/redux/execution-system/utils/required-organization.d.ts +18 -0
  38. package/dist/agents/redux/execution-system/utils/required-organization.js +14 -10
  39. package/dist/agents/services/mcp-connections.service.js +5 -8
  40. package/dist/agents/ui-first-tools/dispatcher/dispatch-ui-first-tool.thunk.js +2 -1
  41. package/dist/host/defaults/files-host.js +11 -1
  42. package/dist/host/defaults/server-api.d.ts +1 -15
  43. package/dist/host/defaults/server-api.js +0 -21
  44. package/dist/host/defaults/server.d.ts +2 -1
  45. package/dist/host/defaults/server.js +8 -6
  46. package/dist/host/request-headers.d.ts +26 -0
  47. package/dist/host/request-headers.js +17 -0
  48. package/dist/host/server/call-api.d.ts +0 -13
  49. package/dist/host/server/call-api.js +0 -2
  50. package/dist/host/server/matrx-transport.d.ts +0 -1
  51. package/dist/host/server/matrx-transport.js +0 -2
  52. package/dist/store/root-state.d.ts +4 -21
  53. package/dist/surfaces/hooks/useSurfaceConfig.js +2 -1
  54. package/dist/surfaces/manifests/agent-builder.manifest.d.ts +8 -0
  55. package/dist/surfaces/manifests/agent-builder.manifest.js +62 -2
  56. package/dist/surfaces/runtime/surface-feedback.js +2 -2
  57. package/dist/ui/mandates-storage/mandateStorage.d.ts +36 -0
  58. package/dist/ui/mandates-storage/shortcutStorage.d.ts +9 -0
  59. package/dist/ui/markdown-stream/EnhancedChatMarkdown.js +22 -0
  60. package/dist/ui/markdown-stream/MarkdownStreamImpl.js +3 -3
  61. package/dist/ui/markdown-stream/StreamTextFallback.d.ts +6 -0
  62. package/dist/ui/markdown-stream/StreamTextFallback.js +23 -0
  63. package/dist/ui/markdown-stream/merge-thinking-across-hidden.d.ts +19 -0
  64. package/dist/ui/markdown-stream/merge-thinking-across-hidden.js +21 -0
  65. package/package.json +16 -11
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @ai-matrx/chat
2
2
 
3
+ ## 0.7.8
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/chat/v0.7.7..npm/chat/v0.7.8 -- apps/shared/chat`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.7.7
10
+ ### Unreleased
11
+
12
+ - **`continueAgentJson`** (`agents/redux/execution-system/thunks/continue-agent-json`): the NEXT turn of a
13
+ conversation whose first turn ran headlessly, waited on like the first (`adoptHeadlessAgentJson`). A turn is
14
+ the person's words (`userInput`) or a turn the HOST writes (`hostTurn` — sent as `host_turn`, never
15
+ `user_input`); per-turn structured content rides `context` (`null` removes a key). A conversation not in the
16
+ tab yet is read first. `executeInstance` takes `hostTurn` (continuations only; the empty-turn rule lets it
17
+ through). Lane F6b: the Applet builder's build, fix rounds, changes and replies are one conversation. Test:
18
+ `a-host-turn-continues-the-conversation.test.ts`. **Consumer action:** none.
19
+ - `MarkdownStream`'s stand-in (shown while the app's lazy rich-content host loads, and after a render error)
20
+ reads the request's own answer text when no `content` was passed, and says "Loading…" while it has none —
21
+ it used to print "No content available" over a finished answer, so an Applet run read "Done — No content
22
+ available" on a slow network (lane F8, live Applet audit 2026-10-09). `StreamTextFallback`. Test:
23
+ `the-fallback-never-hides-a-finished-answer.test.tsx` (red on the old stand-in). **Consumer action:** none.
24
+ - Where machine frames are hidden (an Expert, an Applet visitor), thinking runs separated only by settled
25
+ tool calls render as ONE thinking block — live slots and reloaded segments alike
26
+ (`mergeThinkingAcrossHidden`). A research run used to stack ~40 identical "Thought process" rows above the
27
+ answer (live Applet audit, 2026-10-09). Test: `merge-thinking-across-hidden.test.ts`.
28
+ - `LiveRunDisplay` takes `errorText`: the host's own sentence for a failed run, shown instead of the
29
+ request's (an Applet shows visitors a plain sentence; the server's words stay in the error inspector).
30
+ **Consumer action:** none required; Applet hosts pass it.
31
+
3
32
  ## 0.7.6
4
33
  ### Unreleased
5
34
 
@@ -1,13 +1,14 @@
1
1
  import { callApi } from "../../host/server/call-api.js";
2
+ import { callConversationApi } from "../redux/execution-system/thunks/call-conversation-api.js";
2
3
  const inFlightConversationReads = /* @__PURE__ */ new Map();
3
4
  function readConversationPendingCalls(conversationId, dispatch) {
4
5
  const joined = inFlightConversationReads.get(conversationId);
5
6
  if (joined) return joined;
6
7
  const tracked = dispatch(
7
- callApi({
8
+ // THE CONVERSATION DOOR: the turn's own server, credential and organization.
9
+ callConversationApi(conversationId, {
8
10
  path: "/ai/conversation/{conversation_id}/pending_calls",
9
- method: "GET",
10
- pathParams: { conversation_id: conversationId }
11
+ method: "GET"
11
12
  })
12
13
  ).finally(() => {
13
14
  if (inFlightConversationReads.get(conversationId) === tracked) {
@@ -1,6 +1,6 @@
1
1
  import { selectHostConversation } from "../redux/execution-system/host-conversations/host-conversations.selectors.js";
2
2
  import { answerHostAsk } from "../redux/execution-system/host-conversations/host-conversation.thunks.js";
3
- import { callApi } from "../../host/server/call-api.js";
3
+ import { callConversationApi } from "../redux/execution-system/thunks/call-conversation-api.js";
4
4
  import { toast } from "../../host/notify.js";
5
5
  import { formatDurationMs } from "@ai-matrx/kit/format";
6
6
  import { setInstanceStatus } from "../redux/execution-system/conversations/conversations.slice.js";
@@ -92,17 +92,14 @@ function isRetryableError(err) {
92
92
  function postToolResults(conversationId, results) {
93
93
  return async (dispatch, getState) => {
94
94
  const callIds = results.map((r) => r.call_id).join(", ");
95
- const conversationOrganizationId = getState().conversations?.byConversationId?.[conversationId]?.organizationId ?? void 0;
96
95
  let lastError = null;
97
96
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
98
97
  try {
99
98
  const result = await dispatch(
100
- callApi({
99
+ callConversationApi(conversationId, {
101
100
  path: "/ai/conversations/{conversation_id}/tool_results",
102
101
  method: "POST",
103
- pathParams: { conversation_id: conversationId },
104
- body: { results },
105
- scopeOverrides: conversationOrganizationId ? { organization_id: conversationOrganizationId } : void 0
102
+ body: { results }
106
103
  })
107
104
  );
108
105
  if (result.error) {
@@ -8,6 +8,7 @@ import {
8
8
  stageDraftHandoff
9
9
  } from "../../redux/chat/chat-route.slice.js";
10
10
  import { DEFAULT_NEW_CHAT_MANDATE_KEY } from "./chat-quick-actions.config.js";
11
+ import { selectIsAuthenticated } from "../../../host/identity.js";
11
12
  function parseChatPath(pathname) {
12
13
  const agentMatch = pathname.match(/^\/chat\/a\/([^/]+)/);
13
14
  if (agentMatch) {
@@ -116,8 +117,7 @@ async function beginFreshChat({
116
117
  }) {
117
118
  const state = getState();
118
119
  let defaultAgentId = null;
119
- const auth = state.userAuth;
120
- if (auth && auth.id !== null && !auth.isAnonymous) {
120
+ if (selectIsAuthenticated(state)) {
121
121
  try {
122
122
  defaultAgentId = await resolveDefaultAgentBounded();
123
123
  } catch (error) {
@@ -58,9 +58,9 @@ interface InputActionButtonsProps {
58
58
  * audio) live in the + menu; Output and Effort in the agent pill's menu.
59
59
  */
60
60
  folded?: boolean;
61
- /** Compact's row under the card: after + · mic (· live audio). */
61
+ /** Scope · values: after + · mic (· live audio) in Compact's row, after + in the card's own bar. */
62
62
  leading?: React.ReactNode;
63
- /** Compact's row under the card: the right end (agent · output · effort). */
63
+ /** Agent · output · effort: the right end of Compact's row, or just left of the mic in the card's own bar. */
64
64
  trailing?: React.ReactNode;
65
65
  /**
66
66
  * Which part this instance draws. Compact mounts two: `"send"` (↵, inside
@@ -202,9 +202,11 @@ function InputActionButtons({
202
202
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
203
203
  plusMenu,
204
204
  database ? /* @__PURE__ */ jsx(DesktopPresenceIndicator, { conversationId }) : null,
205
- variablesToggle
205
+ variablesToggle,
206
+ composer.leading
206
207
  ] }),
207
208
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
209
+ composer.trailing,
208
210
  extraRightControls,
209
211
  micGroup,
210
212
  liveAudioButton,
@@ -20,7 +20,7 @@ import { Button } from "@ai-matrx/design-system";
20
20
  import { cn } from "@ai-matrx/design-system";
21
21
  import { composerShows } from "./composer/composer-mode-visibility.js";
22
22
  import { ComposerChipsRow } from "./composer/ComposerChipsRow.js";
23
- import { ComposerMetaRow, ComposerPills, ComposerScopeCluster, ComposerValueGroupChip } from "./composer/ComposerMetaRow.js";
23
+ import { ComposerPills, ComposerScopeCluster, ComposerValueGroupChip } from "./composer/ComposerMetaRow.js";
24
24
  import { useComposerFold } from "./composer/useComposerFold.js";
25
25
  import { useTouchOnlyDevice } from "../../../../ui/composer/useTouchOnlyDevice.js";
26
26
  function SmartAgentInputStacked({
@@ -323,7 +323,12 @@ function SmartAgentInputStacked({
323
323
  onVoiceBusyChange: setVoiceBusy,
324
324
  extraRightControls,
325
325
  onRequestInputExpand: () => setExpandRequestKey((key) => key + 1),
326
- composer: composerParts
326
+ composer: {
327
+ ...composerParts,
328
+ // The pills live in the card's own bar: no strip under the card (rule: existing bars).
329
+ leading: composer.meta === "none" ? void 0 : /* @__PURE__ */ jsx(ComposerScopeCluster, { conversationId, composer, folded }),
330
+ trailing: composer.meta === "none" ? void 0 : /* @__PURE__ */ jsx(ComposerPills, { conversationId, composer, menuSide, folded })
331
+ }
327
332
  }
328
333
  )
329
334
  ] })
@@ -344,7 +349,7 @@ function SmartAgentInputStacked({
344
349
  trailing: composer.meta === "none" ? void 0 : /* @__PURE__ */ jsx(ComposerPills, { conversationId, composer, menuSide, folded })
345
350
  }
346
351
  }
347
- ) : composer.meta === "none" ? null : /* @__PURE__ */ jsx(ComposerMetaRow, { conversationId, composer, menuSide, folded })
352
+ ) : null
348
353
  ]
349
354
  }
350
355
  );
@@ -15,6 +15,7 @@ import { useSessionKnob } from "../../../../../host/prefs-react.js";
15
15
  import { cn } from "@ai-matrx/design-system";
16
16
  import { COMPOSER_KNOBS } from "./composer-mode-cookie.js";
17
17
  import { Button } from "@ai-matrx/design-system/controls";
18
+ import { selectOrganizationId } from "../../../../../host/org.js";
18
19
  function timeOfDayGreeting(hour) {
19
20
  if (hour < 5) return "Evening";
20
21
  if (hour < 12) return "Morning";
@@ -69,9 +70,7 @@ function ComposerQuickActions({
69
70
  className
70
71
  }) {
71
72
  const knob = useSessionKnob(COMPOSER_KNOBS.quickActions);
72
- const organizationId = useAppSelector(
73
- (state) => state.appContext?.organization_id ?? null
74
- );
73
+ const organizationId = useAppSelector(selectOrganizationId);
75
74
  const actions = parseQuickActions(knob);
76
75
  const quickActionKeys = actions.map((a) => a.mandateKey);
77
76
  const mandates = useMandateSet(quickActionKeys, {
@@ -13,13 +13,15 @@ import {
13
13
  readComposerModeCookieClient,
14
14
  writeComposerModeCookie
15
15
  } from "./composer-mode-cookie.js";
16
+ import { selectUserId } from "../../../../../host/identity.js";
17
+ import { selectOrganizationId } from "../../../../../host/org.js";
16
18
  let knobsAppliedThisTab = false;
17
19
  let choseModeThisTab = false;
18
20
  function useComposerMode(initialMode, { enabled = true } = {}) {
19
21
  const dispatch = useAppDispatch();
20
22
  const stored = useAppSelector(selectComposerMode);
21
- const organizationId = useAppSelector((s) => s.appContext?.organization_id ?? null);
22
- const userId = useAppSelector((s) => s.userAuth?.id ?? null);
23
+ const organizationId = useAppSelector(selectOrganizationId);
24
+ const userId = useAppSelector(selectUserId);
23
25
  const knobOrganization = enabled ? organizationId : null;
24
26
  const defaultModeKnob = useEffectiveKnob(knobOrganization, userId, COMPOSER_KNOBS.defaultMode);
25
27
  const rememberKnob = useEffectiveKnob(knobOrganization, userId, COMPOSER_KNOBS.rememberLastMode);
@@ -25,6 +25,12 @@ export interface LiveRunDisplayProps {
25
25
  * so a refused launch never reads as a run still on its way.
26
26
  */
27
27
  failure?: string | null;
28
+ /**
29
+ * The host's own sentence for a failed run, shown INSTEAD of the request's. A host whose viewers did not
30
+ * build the agent (an Applet's visitors) shows a plain sentence; the server's words stay in the error
31
+ * inspector (Applet audit 2026-10-09).
32
+ */
33
+ errorText?: string | null;
28
34
  /** Renders a dismiss (X) affordance; caller tears the run down. */
29
35
  onDismiss?: () => void;
30
36
  className?: string;
@@ -42,4 +48,4 @@ export interface LiveRunDisplayProps {
42
48
  */
43
49
  variant?: "card" | "bare";
44
50
  }
45
- export declare function LiveRunDisplay({ conversationId, requestId: requestIdProp, label, pending, failure, onDismiss, className, bodyClassName, variant, }: LiveRunDisplayProps): import("react").JSX.Element | null;
51
+ export declare function LiveRunDisplay({ conversationId, requestId: requestIdProp, label, pending, failure, errorText, onDismiss, className, bodyClassName, variant, }: LiveRunDisplayProps): import("react").JSX.Element | null;
@@ -21,6 +21,7 @@ function LiveRunDisplay({
21
21
  label,
22
22
  pending = false,
23
23
  failure = null,
24
+ errorText = null,
24
25
  onDismiss,
25
26
  className,
26
27
  bodyClassName,
@@ -32,7 +33,8 @@ function LiveRunDisplay({
32
33
  pending && !failure
33
34
  );
34
35
  const { requestId, isActive, statusText, chunkCount } = status;
35
- const errorMessage = status.errorMessage ?? (failure || null);
36
+ const rawError = status.errorMessage ?? (failure || null);
37
+ const errorMessage = rawError && errorText ? errorText : rawError;
36
38
  const conversationMessages = useAppSelector(
37
39
  conversationId ? selectConversationMessages(conversationId) : selectNoConversationMessages
38
40
  );
@@ -165,7 +165,7 @@ function AssistantMessageFooter(props) {
165
165
  isLatestAssistant
166
166
  } = useAssistantMessageActions(props);
167
167
  const answerActions = useTranscriptLayout().answerActions;
168
- const isHoverOnly = answerActions === "hover" || density === "compact" && !isLatestAssistant;
168
+ const isHoverOnly = answerActions === "hover" || answerActions === "latest" && !isLatestAssistant || density === "compact" && !isLatestAssistant;
169
169
  const rowRef = useRef(null);
170
170
  useEffect(() => {
171
171
  const row = rowRef.current;
@@ -183,7 +183,9 @@ function AssistantMessageFooter(props) {
183
183
  {
184
184
  className: cn(
185
185
  "transition-opacity",
186
- isHoverOnly && "opacity-0 group-hover/assistant-msg:opacity-100 focus-within:opacity-100"
186
+ isHoverOnly && // Hidden by opacity, never display: the row keeps its height, so revealing it shifts
187
+ // nothing. A touch screen has no hover, so it is always visible there.
188
+ "opacity-0 group-hover/assistant-msg:opacity-100 focus-within:opacity-100 [@media(hover:none)]:opacity-100"
187
189
  ),
188
190
  children: [
189
191
  /* @__PURE__ */ jsxs(
@@ -1,24 +1,25 @@
1
1
  /**
2
2
  * How a host lays the ONE conversation column out (HOST-OWNED-SOURCE-PLAN P28e look bar).
3
3
  *
4
- * Knobs, never forks: every value has today's web default, and a host that says nothing gets
5
- * exactly the web chat. Matrx 2 (desktop) fills its pane, starts at the left edge and draws the
4
+ * Knobs, never forks: a host that says nothing gets the default look (flat compact user turn,
5
+ * actions on hover). Matrx 2 (desktop) fills its pane, starts at the left edge and draws the
6
6
  * person's turn as a compact block — the look its coding screen had before the column.
7
7
  *
8
8
  * - `width`: "reading" (the canonical `max-w-3xl` reading column) | "fill" (the host's pane).
9
9
  * - `align`: "center" | "start" — where a reading-width column sits in a wider pane.
10
- * - `userMessage`: "card" (the bordered card that collapses a long message behind a chevron) |
11
- * "compact" (a tight block; only a genuinely long message collapses).
12
- * - `answerActions`: "shown" (the answer's action row always visible on the latest answer) |
13
- * "hover" (revealed on hover / keyboard focus — a finished answer ends on its last word, not on
14
- * a lone ⋯ that reads as "still writing" in a host with few actions).
10
+ * - `userMessage`: "compact" (DEFAULT: a flat tight block; only a genuinely long message
11
+ * collapses) | "card" (the bordered card that collapses a long message behind a chevron).
12
+ * - `answerActions`: "hover" (DEFAULT: the action row is revealed on hover / keyboard focus, and
13
+ * always on touch — its height is reserved, so revealing it moves nothing; a finished answer ends
14
+ * on its last word) | "latest" (the latest answer's row is always visible, older ones on hover) |
15
+ * "shown" (every answer's row always visible).
15
16
  */
16
17
  import { type ReactNode } from "react";
17
18
  export interface TranscriptLayout {
18
19
  width?: "reading" | "fill";
19
20
  align?: "center" | "start";
20
21
  userMessage?: "card" | "compact";
21
- answerActions?: "shown" | "hover";
22
+ answerActions?: "shown" | "latest" | "hover";
22
23
  }
23
24
  export declare const DEFAULT_TRANSCRIPT_LAYOUT: Required<TranscriptLayout>;
24
25
  export declare function resolveTranscriptLayout(layout?: TranscriptLayout): Required<TranscriptLayout>;
@@ -6,8 +6,8 @@ import {
6
6
  const DEFAULT_TRANSCRIPT_LAYOUT = {
7
7
  width: "reading",
8
8
  align: "center",
9
- userMessage: "card",
10
- answerActions: "shown"
9
+ userMessage: "compact",
10
+ answerActions: "hover"
11
11
  };
12
12
  const TranscriptLayoutContext = createContext(DEFAULT_TRANSCRIPT_LAYOUT);
13
13
  function resolveTranscriptLayout(layout) {
@@ -1,4 +1,4 @@
1
- import { applyOrganizationContextHeader } from "../../../host/server/organization-context.js";
1
+ import { chatRequestHeaders } from "../../../host/request-headers.js";
2
2
  import { selectResolvedBaseUrl } from "../../../host/server/api-config.js";
3
3
  import { selectAccessToken, selectFingerprintId } from "../../../host/identity.js";
4
4
  import { selectOrganizationId } from "../../../host/org.js";
@@ -17,7 +17,6 @@ function resolveAgentCacheBustBackend(state) {
17
17
  const accessToken = selectAccessToken(state);
18
18
  const fingerprintId = selectFingerprintId(state);
19
19
  if (accessToken) {
20
- headers["Authorization"] = `Bearer ${accessToken}`;
21
20
  const organizationId = selectOrganizationId(state);
22
21
  if (!organizationId) {
23
22
  console.warn(
@@ -27,13 +26,14 @@ function resolveAgentCacheBustBackend(state) {
27
26
  }
28
27
  return {
29
28
  baseUrl: trimmedBase,
30
- headers: applyOrganizationContextHeader(headers, organizationId)
29
+ // THE ONE HEADER BUILDER (host/request-headers.ts).
30
+ headers: { ...headers, ...chatRequestHeaders({ accessToken, fingerprintId: null }, organizationId) }
31
31
  };
32
32
  }
33
- if (fingerprintId) {
34
- headers["X-Fingerprint-ID"] = fingerprintId;
35
- }
36
- return { baseUrl: trimmedBase, headers };
33
+ return {
34
+ baseUrl: trimmedBase,
35
+ headers: { ...headers, ...chatRequestHeaders({ accessToken: null, fingerprintId }, null) }
36
+ };
37
37
  }
38
38
  async function postInvalidateAgentCache(baseUrl, agentId, headers, options) {
39
39
  const response = await sendMatrxRequest(
@@ -5,6 +5,7 @@ import {
5
5
  import { setContextReceipt } from "../instance-context/instance-context.slice.js";
6
6
  import { captureError } from "../../../../host/diagnostics.js";
7
7
  import { toast } from "../../../../host/notify.js";
8
+ import { selectIsAdmin } from "../../../../host/identity.js";
8
9
  function toContextReceiptRow(row) {
9
10
  const out = {
10
11
  key: row.key,
@@ -101,8 +102,7 @@ function recordContextReceipt(dispatch, getState, args) {
101
102
  requestId,
102
103
  level: "high"
103
104
  });
104
- const auth = getState().userAuth;
105
- if (auth?.isAdmin && auth.adminLaneOpen) {
105
+ if (selectIsAdmin(getState())) {
106
106
  console.error("[context-rules] receipt mismatch", { conversationId, requestId, mismatches, receipt });
107
107
  toast.warning(`Context mismatch: ${summary}`.slice(0, 140));
108
108
  }
@@ -10,8 +10,8 @@
10
10
  * anywhere else; bypassing the funnel forfeits the optimistic card +
11
11
  * `injection_consumed` delivery handshake.
12
12
  *
13
- * All four calls ride `callApi` (auth, base-url resolution, org/project
14
- * scope injection, error capture) against the generated endpoint types.
13
+ * All four calls ride THE CONVERSATION DOOR (`callConversationApi`): the turn's own server,
14
+ * credential and organization, so a mid-run message can never go where the run is not.
15
15
  */
16
16
  import type { ChatDispatch, ChatRootState } from "../../../../store/root-state.js";
17
17
  import { type InboxItemKind, type InboxItemMode } from "./inbox.slice.js";
@@ -1,5 +1,5 @@
1
1
  import { createAsyncThunk } from "@reduxjs/toolkit";
2
- import { callApi } from "../../../../host/server/call-api.js";
2
+ import { callConversationApi } from "../thunks/call-conversation-api.js";
3
3
  import { toast } from "../../../../host/notify.js";
4
4
  import {
5
5
  addInboxItem,
@@ -55,10 +55,9 @@ const enqueueInboxMessage = createAsyncThunk(
55
55
  })
56
56
  );
57
57
  const result = await dispatch(
58
- callApi({
58
+ callConversationApi(conversationId, {
59
59
  path: "/ai/conversations/{conversation_id}/inbox",
60
60
  method: "POST",
61
- pathParams: { conversation_id: conversationId },
62
61
  expectedErrorStatuses: [404],
63
62
  body: {
64
63
  kind,
@@ -116,13 +115,10 @@ const retractInboxItem = createAsyncThunk(
116
115
  return "retracted";
117
116
  }
118
117
  const result = await dispatch(
119
- callApi({
118
+ callConversationApi(conversationId, {
120
119
  path: "/ai/conversations/{conversation_id}/inbox/{injection_id}",
121
120
  method: "DELETE",
122
- pathParams: {
123
- conversation_id: conversationId,
124
- injection_id: injectionId
125
- }
121
+ pathParams: { injection_id: injectionId }
126
122
  })
127
123
  );
128
124
  if (result.error && result.error.status === 409) {
@@ -150,13 +146,10 @@ const editInboxItem = createAsyncThunk(
150
146
  return;
151
147
  }
152
148
  const result = await dispatch(
153
- callApi({
149
+ callConversationApi(conversationId, {
154
150
  path: "/ai/conversations/{conversation_id}/inbox/{injection_id}",
155
151
  method: "PATCH",
156
- pathParams: {
157
- conversation_id: conversationId,
158
- injection_id: injectionId
159
- },
152
+ pathParams: { injection_id: injectionId },
160
153
  body: { text }
161
154
  })
162
155
  );
@@ -178,10 +171,9 @@ const editInboxItem = createAsyncThunk(
178
171
  );
179
172
  const hydrateInbox = createAsyncThunk("conversationInbox/hydrate", async ({ conversationId }, { dispatch }) => {
180
173
  const result = await dispatch(
181
- callApi({
174
+ callConversationApi(conversationId, {
182
175
  path: "/ai/conversations/{conversation_id}/inbox",
183
176
  method: "GET",
184
- pathParams: { conversation_id: conversationId },
185
177
  queryParams: { status: "pending" }
186
178
  })
187
179
  );
@@ -13,7 +13,7 @@
13
13
  * captures that as a typed error on the slice and never throws.
14
14
  */
15
15
  import type { ChatRootState } from "../../../../store/root-state.js";
16
- import { type MemoryCostSummary } from "../../../../host/server/call-api.js";
16
+ import type { MemoryCostSummary } from "../../../../host/server/call-api.js";
17
17
  interface FetchMemoryCostArgs {
18
18
  conversationId: string;
19
19
  signal?: AbortSignal;
@@ -1,7 +1,5 @@
1
1
  import { createAsyncThunk } from "@reduxjs/toolkit";
2
- import {
3
- callConversationMemoryCost
4
- } from "../../../../host/server/call-api.js";
2
+ import { callConversationApi } from "../thunks/call-conversation-api.js";
5
3
  import {
6
4
  setCostFetchStatus,
7
5
  setCostSummary
@@ -11,7 +9,12 @@ const fetchMemoryCost = createAsyncThunk(
11
9
  async ({ conversationId, signal }, { dispatch, rejectWithValue }) => {
12
10
  dispatch(setCostFetchStatus({ conversationId, status: "loading" }));
13
11
  const result = await dispatch(
14
- callConversationMemoryCost(conversationId, { signal })
12
+ // THE CONVERSATION DOOR: the turn's own server, credential and organization.
13
+ callConversationApi(conversationId, {
14
+ path: "/ai/conversations/{conversation_id}/memory_cost",
15
+ method: "GET",
16
+ ...signal ? { signal } : {}
17
+ })
15
18
  );
16
19
  if (result.error || !result.data) {
17
20
  const message = result.error?.message ?? "Failed to fetch memory cost.";
@@ -362,120 +362,7 @@ export declare const selectIsInstanceReady: (conversationId: string) => ((state:
362
362
  * Usage — stabilize selector identity with `useMemo`:
363
363
  * `useMemo(() => makeSelectAssembledRequest(conversationId), [conversationId])`.
364
364
  */
365
- export declare function makeSelectAssembledRequest(conversationId: string): ((state: {
366
- voiceAgent: import("../../../../voice-agent/types.js").VoiceAgentState;
367
- agentDefinition: import("../../../types/agent-definition.types.js").AgentDefinitionSliceState;
368
- conversationList: import("../../conversation-list/conversation-list.types.js").ConversationListState;
369
- conversationHistory: import("../../conversation-history/types.js").ConversationHistoryState;
370
- agentShortcut: import("../../agent-shortcuts/types.js").AgentShortcutSliceState;
371
- agentShortcutCategory: import("../../agent-shortcut-categories/types.js").AgentShortcutCategorySliceState;
372
- surfaceUserState: import("../../../../surfaces/redux/userStateSlice.js").SurfaceUserStateSlice;
373
- conversations: import("../conversations/conversations.slice.js").ConversationsState;
374
- chatIncognito: import("../../chat/chat-incognito.slice.js").ChatIncognitoState;
375
- chatRoute: import("../../chat/chat-route.slice.js").ChatRouteState;
376
- instanceModelOverrides: import("../instance-model-overrides/instance-model-overrides.slice.js").InstanceModelOverridesState;
377
- instanceInputCapabilities: import("../instance-input-capabilities/instance-input-capabilities.slice.js").InstanceInputCapabilitiesState;
378
- instanceVariableValues: import("../instance-variable-values/instance-variable-values.slice.js").InstanceVariableValuesState;
379
- instanceResources: import("../instance-resources/instance-resources.slice.js").InstanceResourcesState;
380
- instanceContext: import("../instance-context/instance-context.slice.js").InstanceContextState;
381
- instanceWorkingDocument: import("../instance-working-document/instance-working-document.slice.js").InstanceWorkingDocumentSliceState;
382
- instanceUserInput: import("../instance-user-input/instance-user-input.slice.js").InstanceUserInputSliceState;
383
- instanceClientTools: import("../instance-client-tools/instance-client-tools.slice.js").InstanceClientToolsState;
384
- pendingAsks: import("../../../ui-first-tools/redux/pending-asks.slice.js").PendingAsksState;
385
- conversationInbox: import("../inbox/inbox.slice.js").ConversationInboxState;
386
- agentLists: import("../../../ui-first-tools/redux/agent-lists.slice.js").AgentListsState;
387
- instanceUIState: import("../instance-ui-state/instance-ui-state.slice.js").InstanceUIStateSlice;
388
- activeTools: import("../active-tools/active-tools.slice.js").ActiveToolsState;
389
- activeRequests: import("../active-requests/active-requests.slice.js").ActiveRequestsState;
390
- runSets: import("../run-sets/run-sets.slice.js").RunSetsState;
391
- messages: import("../messages/messages.slice.js").MessagesState;
392
- observability: import("../observability/observability.slice.js").ObservabilityState;
393
- contextState: import("../context-state/context-state.slice.js").ContextStateSliceState;
394
- observationalMemory: import("../observational-memory/observational-memory.slice.js").ObservationalMemoryState;
395
- cacheBypass: import("../message-crud/cache-bypass.slice.js").CacheBypassState;
396
- conversationFocus: import("../conversation-focus/conversation-focus.slice.js").ConversationFocusState;
397
- surfaces: import("../../surfaces/surfaces.slice.js").SurfacesState;
398
- surfacesCatalog: import("../../../../surfaces/redux/surfacesCatalogSlice.js").SurfacesCatalogSliceState;
399
- agentSurfaceBindings: import("../../../../surfaces/redux/agentSurfaceBindingsSlice.js").AgentSurfaceBindingsSliceState;
400
- surfaceConfig: import("../../../../surfaces/redux/surfaceConfigSlice.js").SurfaceConfigSliceState;
401
- agentAssistantMarkdownDraft: import("../../agent-assistant-markdown-draft.slice.js").AgentAssistantMarkdownDraftState;
402
- mcp: import("../../mcp/mcp.slice.js").McpSliceState;
403
- proposedDirectives: import("../../proposed-directives/proposedDirectivesSlice.js").ProposedDirectivesState;
404
- conversationAttachments: import("../../../connectors/attachments.slice.js").AttachmentsSliceState;
405
- chatHost: import("../../../../store/chat-host.slice.js").ChatHostState;
406
- hostConversations: import("../host-conversations/host-conversations.slice.js").HostConversationsState;
407
- } & import("../../../../store/root-state.js").ChatHostReadSlices) => AssembledAgentStartRequest | null) & {
408
- clearCache: () => void;
409
- resultsCount: () => number;
410
- resetResultsCount: () => void;
411
- } & {
412
- resultFunc: (resultFuncArgs_0: import("../conversations/conversations.slice.js").ConversationsState, resultFuncArgs_1: import("../instance-ui-state/instance-ui-state.slice.js").InstanceUIStateSlice, resultFuncArgs_2: import("../instance-user-input/instance-user-input.slice.js").InstanceUserInputSliceState, resultFuncArgs_3: import("../instance-resources/instance-resources.slice.js").InstanceResourcesState, resultFuncArgs_4: import("../instance-variable-values/instance-variable-values.slice.js").InstanceVariableValuesState, resultFuncArgs_5: import("../instance-model-overrides/instance-model-overrides.slice.js").InstanceModelOverridesState, resultFuncArgs_6: import("../instance-context/instance-context.slice.js").InstanceContextState, resultFuncArgs_7: import("../instance-client-tools/instance-client-tools.slice.js").InstanceClientToolsState, resultFuncArgs_8: {
413
- organization_id?: string | null;
414
- } | undefined, resultFuncArgs_9: import("../../../../store/chat-host.slice.js").ChatHostState, resultFuncArgs_10: import("../../../types/agent-definition.types.js").AgentDefinitionSliceState, resultFuncArgs_11: import("../../../../surfaces/redux/userStateSlice.js").SurfaceUserStateSlice, resultFuncArgs_12: import("../messages/messages.slice.js").MessagesState, resultFuncArgs_13: {
415
- id: string | null;
416
- email?: string | null;
417
- isAnonymous?: boolean;
418
- accessToken?: string | null;
419
- isAdmin?: boolean;
420
- adminLaneOpen?: boolean;
421
- adminLevel?: string | null;
422
- } | undefined, resultFuncArgs_14: {
423
- fingerprintId?: string | null;
424
- userMetadata?: {
425
- fullName?: string | null;
426
- name?: string | null;
427
- preferredUsername?: string | null;
428
- } | null;
429
- } | undefined) => AssembledAgentStartRequest | null;
430
- memoizedResultFunc: ((resultFuncArgs_0: import("../conversations/conversations.slice.js").ConversationsState, resultFuncArgs_1: import("../instance-ui-state/instance-ui-state.slice.js").InstanceUIStateSlice, resultFuncArgs_2: import("../instance-user-input/instance-user-input.slice.js").InstanceUserInputSliceState, resultFuncArgs_3: import("../instance-resources/instance-resources.slice.js").InstanceResourcesState, resultFuncArgs_4: import("../instance-variable-values/instance-variable-values.slice.js").InstanceVariableValuesState, resultFuncArgs_5: import("../instance-model-overrides/instance-model-overrides.slice.js").InstanceModelOverridesState, resultFuncArgs_6: import("../instance-context/instance-context.slice.js").InstanceContextState, resultFuncArgs_7: import("../instance-client-tools/instance-client-tools.slice.js").InstanceClientToolsState, resultFuncArgs_8: {
431
- organization_id?: string | null;
432
- } | undefined, resultFuncArgs_9: import("../../../../store/chat-host.slice.js").ChatHostState, resultFuncArgs_10: import("../../../types/agent-definition.types.js").AgentDefinitionSliceState, resultFuncArgs_11: import("../../../../surfaces/redux/userStateSlice.js").SurfaceUserStateSlice, resultFuncArgs_12: import("../messages/messages.slice.js").MessagesState, resultFuncArgs_13: {
433
- id: string | null;
434
- email?: string | null;
435
- isAnonymous?: boolean;
436
- accessToken?: string | null;
437
- isAdmin?: boolean;
438
- adminLaneOpen?: boolean;
439
- adminLevel?: string | null;
440
- } | undefined, resultFuncArgs_14: {
441
- fingerprintId?: string | null;
442
- userMetadata?: {
443
- fullName?: string | null;
444
- name?: string | null;
445
- preferredUsername?: string | null;
446
- } | null;
447
- } | undefined) => AssembledAgentStartRequest | null) & {
448
- clearCache: () => void;
449
- resultsCount: () => number;
450
- resetResultsCount: () => void;
451
- };
452
- lastResult: () => AssembledAgentStartRequest | null;
453
- dependencies: [(s: ChatRootState) => import("../conversations/conversations.slice.js").ConversationsState, (s: ChatRootState) => import("../instance-ui-state/instance-ui-state.slice.js").InstanceUIStateSlice, (s: ChatRootState) => import("../instance-user-input/instance-user-input.slice.js").InstanceUserInputSliceState, (s: ChatRootState) => import("../instance-resources/instance-resources.slice.js").InstanceResourcesState, (s: ChatRootState) => import("../instance-variable-values/instance-variable-values.slice.js").InstanceVariableValuesState, (s: ChatRootState) => import("../instance-model-overrides/instance-model-overrides.slice.js").InstanceModelOverridesState, (s: ChatRootState) => import("../instance-context/instance-context.slice.js").InstanceContextState, (s: ChatRootState) => import("../instance-client-tools/instance-client-tools.slice.js").InstanceClientToolsState, (s: ChatRootState) => {
454
- organization_id?: string | null;
455
- } | undefined, (s: ChatRootState) => import("../../../../store/chat-host.slice.js").ChatHostState, (s: ChatRootState) => import("../../../types/agent-definition.types.js").AgentDefinitionSliceState, (s: ChatRootState) => import("../../../../surfaces/redux/userStateSlice.js").SurfaceUserStateSlice, (s: ChatRootState) => import("../messages/messages.slice.js").MessagesState, (s: ChatRootState) => {
456
- id: string | null;
457
- email?: string | null;
458
- isAnonymous?: boolean;
459
- accessToken?: string | null;
460
- isAdmin?: boolean;
461
- adminLaneOpen?: boolean;
462
- adminLevel?: string | null;
463
- } | undefined, (s: ChatRootState) => {
464
- fingerprintId?: string | null;
465
- userMetadata?: {
466
- fullName?: string | null;
467
- name?: string | null;
468
- preferredUsername?: string | null;
469
- } | null;
470
- } | undefined];
471
- recomputations: () => number;
472
- resetRecomputations: () => void;
473
- dependencyRecomputations: () => number;
474
- resetDependencyRecomputations: () => void;
475
- } & {
476
- memoize: typeof import("reselect").weakMapMemoize;
477
- argsMemoize: typeof import("reselect").weakMapMemoize;
478
- };
365
+ export declare function makeSelectAssembledRequest(conversationId: string): (state: ChatRootState) => AssembledAgentStartRequest | null;
479
366
  /** @deprecated Use {@link makeSelectAssembledRequest}; same underlying factory (alias). */
480
367
  export declare const selectAssembledRequest: typeof makeSelectAssembledRequest;
481
368
  /**