@ai-matrx/chat 0.2.1 → 0.2.3
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.
- package/CHANGELOG.md +17 -0
- package/dist/agents/components/inputs/smart-input/InputActionButtons.js +1 -1
- package/dist/agents/components/inputs/smart-input/SmartAgentInputStacked.js +10 -5
- package/dist/agents/components/inputs/smart-input/composer/ComposerMetaRow.js +1 -1
- package/dist/agents/components/inputs/smart-input/composer/useComposerFold.d.ts +3 -2
- package/dist/agents/components/inputs/smart-input/composer/useComposerFold.js +30 -5
- package/dist/agents/components/messages-display/MessageTimestamp.d.ts +8 -1
- package/dist/agents/components/messages-display/MessageTimestamp.js +3 -4
- package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +5 -3
- package/dist/agents/components/messages-display/assistant/AssistantMessageFooter.d.ts +5 -0
- package/dist/agents/components/messages-display/assistant/AssistantMessageFooter.js +36 -13
- package/dist/agents/components/messages-display/assistant/AssistantTurnGroup.js +3 -1
- package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.d.ts +10 -1
- package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +12 -11
- package/package.json +14 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.2.3
|
|
4
|
+
### The frontend's last in-flight chat edit, carried at the P27 cutover
|
|
5
|
+
|
|
6
|
+
- `run-headless-agent-json.ts`: an edit another lane had uncommitted in matrx-frontend's
|
|
7
|
+
`packages/chat` when that copy was deleted, carried here verbatim.
|
|
8
|
+
- Consumer action: none.
|
|
9
|
+
|
|
10
|
+
## 0.2.2
|
|
11
|
+
### Last re-sync before matrx-frontend consumes the published package (P27)
|
|
12
|
+
|
|
13
|
+
- Carries the frontend's latest `packages/chat` changes: the composer's run-controls row folds when
|
|
14
|
+
it overflows (`useComposerFold`, `ComposerMetaRow`), message timestamp and assistant footer/turn
|
|
15
|
+
group updates, input action buttons and stacked input.
|
|
16
|
+
- The module tree's suites run here under Jest (`pnpm test:modules`); the host-tie ratchet moved
|
|
17
|
+
in from matrx-frontend (`pnpm check:host-boundary`, budget 0).
|
|
18
|
+
- Consumer action: none.
|
|
19
|
+
|
|
3
20
|
## 0.2.1
|
|
4
21
|
### Re-sync with matrx-frontend's packages/chat (P27)
|
|
5
22
|
|
|
@@ -172,7 +172,7 @@ function InputActionButtons({
|
|
|
172
172
|
] });
|
|
173
173
|
}
|
|
174
174
|
if (composer.part === "controls") {
|
|
175
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-1.5 shrink-0", children: [
|
|
175
|
+
return /* @__PURE__ */ jsxs("div", { "data-composer-row": "", className: "flex min-w-0 items-center justify-between gap-1.5 shrink-0", children: [
|
|
176
176
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-1.5", children: [
|
|
177
177
|
plusMenu,
|
|
178
178
|
/* @__PURE__ */ jsx(DesktopPresenceIndicator, { conversationId }),
|
|
@@ -22,6 +22,7 @@ import { composerShows } from "./composer/composer-mode-visibility.js";
|
|
|
22
22
|
import { ComposerChipsRow } from "./composer/ComposerChipsRow.js";
|
|
23
23
|
import { ComposerMetaRow, ComposerPills, ComposerScopeCluster, ComposerValueGroupChip } from "./composer/ComposerMetaRow.js";
|
|
24
24
|
import { useComposerFold } from "./composer/useComposerFold.js";
|
|
25
|
+
import { useTouchOnlyDevice } from "../../../../ui/composer/useTouchOnlyDevice.js";
|
|
25
26
|
function SmartAgentInputStacked({
|
|
26
27
|
conversationId,
|
|
27
28
|
uploadRoot = "userContent",
|
|
@@ -44,6 +45,7 @@ function SmartAgentInputStacked({
|
|
|
44
45
|
const [voiceBusy, setVoiceBusy] = useState(false);
|
|
45
46
|
const [expandRequestKey, setExpandRequestKey] = useState(0);
|
|
46
47
|
const { ref: composerRootRef, folded } = useComposerFold();
|
|
48
|
+
const touchOnly = useTouchOnlyDevice();
|
|
47
49
|
const showFreeformInput = useAppSelector(
|
|
48
50
|
selectShowFreeformInput(conversationId ?? "")
|
|
49
51
|
);
|
|
@@ -65,7 +67,8 @@ function SmartAgentInputStacked({
|
|
|
65
67
|
"div",
|
|
66
68
|
{
|
|
67
69
|
className: cn(
|
|
68
|
-
"matrx-touch-targets
|
|
70
|
+
touchOnly && "matrx-touch-targets",
|
|
71
|
+
"mx-auto flex w-full min-w-0 shrink-0 flex-col",
|
|
69
72
|
formCompact ? void 0 : "max-w-[768px]"
|
|
70
73
|
),
|
|
71
74
|
"data-composer-size": composer.size,
|
|
@@ -182,7 +185,7 @@ function SmartAgentInputStacked({
|
|
|
182
185
|
"div",
|
|
183
186
|
{
|
|
184
187
|
ref: composerRootRef,
|
|
185
|
-
className: "matrx-touch-targets mx-auto flex w-full min-w-0 max-w-[420px] shrink-0 flex-col",
|
|
188
|
+
className: cn(touchOnly && "matrx-touch-targets", "mx-auto flex w-full min-w-0 max-w-[420px] shrink-0 flex-col"),
|
|
186
189
|
"data-composer-size": composer.size,
|
|
187
190
|
"data-composer-mode": composer.mode,
|
|
188
191
|
"data-assist-dock-avoid": "",
|
|
@@ -263,9 +266,11 @@ function SmartAgentInputStacked({
|
|
|
263
266
|
{
|
|
264
267
|
ref: composerRootRef,
|
|
265
268
|
className: cn(
|
|
266
|
-
// THE 44px FLOOR
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
+
// THE 44px FLOOR on a TOUCH device only. The shared class also fires
|
|
270
|
+
// on any viewport under 1024px, which stretched every button and pill
|
|
271
|
+
// of a narrowed desktop window to 44px (2026-10-06).
|
|
272
|
+
touchOnly && "matrx-touch-targets",
|
|
273
|
+
"mx-auto flex w-full min-w-0 shrink-0 flex-col gap-1",
|
|
269
274
|
isCompact ? void 0 : "max-w-[768px]"
|
|
270
275
|
),
|
|
271
276
|
"data-composer-size": composer.size,
|
|
@@ -89,7 +89,7 @@ function ComposerMetaRow({
|
|
|
89
89
|
return (
|
|
90
90
|
// One line, always: a narrow column scrolls the row sideways, never wraps it.
|
|
91
91
|
// Every gap is the tap-target 6px; the clusters sit at the two ends.
|
|
92
|
-
/* @__PURE__ */ jsxs("div", { className: cn(COMPOSER_ROW_CLASS, "justify-between gap-1.5 px-1"), children: [
|
|
92
|
+
/* @__PURE__ */ jsxs("div", { "data-composer-row": "", className: cn(COMPOSER_ROW_CLASS, "justify-between gap-1.5 px-1"), children: [
|
|
93
93
|
/* @__PURE__ */ jsx(ComposerScopeCluster, { conversationId, composer, folded }),
|
|
94
94
|
/* @__PURE__ */ jsx(ComposerPills, { conversationId, composer, menuSide, folded })
|
|
95
95
|
] })
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const COMPOSER_FOLD_WIDTH_PX = 480;
|
|
7
7
|
/**
|
|
8
|
-
* Watches the composer's own width and
|
|
9
|
-
* fold
|
|
8
|
+
* Watches the composer's own width — and whether a row under the card
|
|
9
|
+
* overflows — and re-renders only when the fold flips, never per pixel while
|
|
10
|
+
* a panel is dragged.
|
|
10
11
|
*/
|
|
11
12
|
export declare function useComposerFold(): {
|
|
12
13
|
ref: import("react").Dispatch<import("react").SetStateAction<HTMLDivElement | null>>;
|
|
@@ -6,15 +6,40 @@ function useComposerFold() {
|
|
|
6
6
|
const [folded, setFolded] = useState(false);
|
|
7
7
|
useEffect(() => {
|
|
8
8
|
if (!el || typeof ResizeObserver === "undefined") return void 0;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
let foldedAt = 0;
|
|
10
|
+
const evaluate = () => {
|
|
11
|
+
const width = el.getBoundingClientRect().width;
|
|
12
|
+
if (width === 0) return;
|
|
13
|
+
const rows = Array.from(el.querySelectorAll("[data-composer-row]"));
|
|
14
|
+
const overflowing = rows.some((row) => row.scrollWidth > row.clientWidth + 1);
|
|
15
|
+
setFolded((was) => {
|
|
16
|
+
if (width < COMPOSER_FOLD_WIDTH_PX) {
|
|
17
|
+
foldedAt = Math.max(foldedAt, width);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (!was && overflowing) {
|
|
21
|
+
foldedAt = width;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (was && width > foldedAt + UNFOLD_MARGIN_PX && !overflowing) {
|
|
25
|
+
foldedAt = 0;
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return was;
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const observer = new ResizeObserver(evaluate);
|
|
13
32
|
observer.observe(el);
|
|
14
|
-
|
|
33
|
+
const mutations = new MutationObserver(() => requestAnimationFrame(evaluate));
|
|
34
|
+
mutations.observe(el, { subtree: true, childList: true, characterData: true });
|
|
35
|
+
return () => {
|
|
36
|
+
observer.disconnect();
|
|
37
|
+
mutations.disconnect();
|
|
38
|
+
};
|
|
15
39
|
}, [el]);
|
|
16
40
|
return { ref, folded };
|
|
17
41
|
}
|
|
42
|
+
const UNFOLD_MARGIN_PX = 80;
|
|
18
43
|
export {
|
|
19
44
|
COMPOSER_FOLD_WIDTH_PX,
|
|
20
45
|
useComposerFold
|
|
@@ -9,6 +9,13 @@ interface TimestampDisplay {
|
|
|
9
9
|
compact: string;
|
|
10
10
|
}
|
|
11
11
|
export declare function formatTimestampDisplay(timestamp: TimestampInput): TimestampDisplay | null;
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The time at the END of the action row. It always occupies its space — it is
|
|
14
|
+
* revealed by opacity on hover/focus, never inserted — so showing it shifts
|
|
15
|
+
* nothing. It never wraps: a narrow row gets the time alone ("2:23 PM"), a
|
|
16
|
+
* wider one the date too ("Oct 6, 2:23 PM"). The exact date, seconds and zone
|
|
17
|
+
* stay in the tooltip and accessible name. A touch screen has no hover to
|
|
18
|
+
* reveal it, so there it takes no space at all and the actions get the row.
|
|
19
|
+
*/
|
|
13
20
|
export declare function MessageTimestamp({ timestamp }: MessageTimestampProps): import("react").JSX.Element | null;
|
|
14
21
|
export {};
|
|
@@ -50,11 +50,10 @@ function MessageTimestamp({ timestamp }) {
|
|
|
50
50
|
tabIndex: 0,
|
|
51
51
|
"aria-label": `${display.absolute} (${display.relative})`,
|
|
52
52
|
title: `${display.absolute} (${display.relative})`,
|
|
53
|
-
|
|
54
|
-
className: "sr-only text-[10px] font-normal text-muted-foreground/65 focus:not-sr-only [@media(hover:hover)]:group-hover/assistant-msg:not-sr-only [@media(hover:hover)]:group-focus-within/assistant-msg:not-sr-only",
|
|
53
|
+
className: "shrink-0 whitespace-nowrap [@media(hover:none)]:hidden text-[10px] font-normal text-muted-foreground/65 opacity-0 transition-opacity focus:opacity-100 [@media(hover:hover)]:group-hover/assistant-msg:opacity-100 [@media(hover:hover)]:group-focus-within/assistant-msg:opacity-100",
|
|
55
54
|
children: [
|
|
56
|
-
/* @__PURE__ */ jsx("span", { className: "@min-[
|
|
57
|
-
/* @__PURE__ */ jsx("span", { className: "hidden @min-[
|
|
55
|
+
/* @__PURE__ */ jsx("span", { className: "@min-[420px]/message-footer:hidden", children: display.compact }),
|
|
56
|
+
/* @__PURE__ */ jsx("span", { className: "hidden @min-[420px]/message-footer:inline", children: display.short })
|
|
58
57
|
]
|
|
59
58
|
}
|
|
60
59
|
);
|
|
@@ -52,7 +52,8 @@ import { AssistantWarning } from "../../run/AssistantWarning.js";
|
|
|
52
52
|
import { BreathingOrb } from "./BreathingOrb.js";
|
|
53
53
|
import {
|
|
54
54
|
AssistantMessageContextMenu,
|
|
55
|
-
AssistantMessageFooter
|
|
55
|
+
AssistantMessageFooter,
|
|
56
|
+
AssistantMessageFooterPlaceholder
|
|
56
57
|
} from "./AssistantMessageFooter.js";
|
|
57
58
|
import { AssistantNoAnswer } from "./AssistantNoAnswer.js";
|
|
58
59
|
import { selectInstanceStatus } from "../../../redux/execution-system/conversations/conversations.selectors.js";
|
|
@@ -442,7 +443,7 @@ function AgentAssistantMessage({
|
|
|
442
443
|
!isClosedStreamSegment && displaySources.length > 0 && /* @__PURE__ */ jsx(MessageSourcesRow, { sources: displaySources, className: "mt-2" }),
|
|
443
444
|
!isStreamActive && /* @__PURE__ */ jsx(PrefillNote, { metadata: record?.metadata }),
|
|
444
445
|
!isStreamActive && /* @__PURE__ */ jsx(StoppedNote, { metadata: record?.metadata, requestStatus }),
|
|
445
|
-
isStreamActive && !failed && (phase === "text_streaming" || phase === "interstitial") && /* @__PURE__ */ jsx(BreathingOrb, { className: "
|
|
446
|
+
isStreamActive && !failed && (phase === "text_streaming" || phase === "interstitial") && /* @__PURE__ */ jsx("div", { className: "relative h-0 overflow-visible", children: /* @__PURE__ */ jsx(BreathingOrb, { className: "absolute left-0 top-[3px]", size: 40 }) })
|
|
446
447
|
] }),
|
|
447
448
|
!isClosedStreamSegment && visibleWarnings?.map((warning, index) => /* @__PURE__ */ jsx(
|
|
448
449
|
AssistantWarning,
|
|
@@ -475,7 +476,8 @@ function AgentAssistantMessage({
|
|
|
475
476
|
isCapturing,
|
|
476
477
|
surfaceKey
|
|
477
478
|
}
|
|
478
|
-
)
|
|
479
|
+
),
|
|
480
|
+
!hideActionBar && (isStreamActive || !messageId) && !failed && !editingInPlace && /* @__PURE__ */ jsx(AssistantMessageFooterPlaceholder, {})
|
|
479
481
|
]
|
|
480
482
|
}
|
|
481
483
|
);
|
|
@@ -51,6 +51,11 @@ export declare function useAssistantMessageActions({ messageId, conversationId,
|
|
|
51
51
|
showOptions: boolean;
|
|
52
52
|
isLatestAssistant: boolean;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Holds the action row's space while a reply streams (the real footer needs
|
|
56
|
+
* the saved message id), so nothing below jumps when the actions arrive.
|
|
57
|
+
*/
|
|
58
|
+
export declare function AssistantMessageFooterPlaceholder(): React.JSX.Element;
|
|
54
59
|
export declare function AssistantMessageFooter(props: AssistantMessageFooterProps): React.JSX.Element;
|
|
55
60
|
export default AssistantMessageFooter;
|
|
56
61
|
/** Agent-menu placements over a rendered answer (never "Content Blocks"). */
|
|
@@ -146,6 +146,10 @@ function useAssistantMessageActions({
|
|
|
146
146
|
isLatestAssistant
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
+
let lastFooterRowHeight = 46;
|
|
150
|
+
function AssistantMessageFooterPlaceholder() {
|
|
151
|
+
return /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: lastFooterRowHeight } });
|
|
152
|
+
}
|
|
149
153
|
function AssistantMessageFooter(props) {
|
|
150
154
|
const { messageId, conversationId, surfaceKey } = props;
|
|
151
155
|
const {
|
|
@@ -162,6 +166,17 @@ function AssistantMessageFooter(props) {
|
|
|
162
166
|
isLatestAssistant
|
|
163
167
|
} = useAssistantMessageActions(props);
|
|
164
168
|
const isHoverOnly = density === "compact" && !isLatestAssistant;
|
|
169
|
+
const rowRef = useRef(null);
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
const row = rowRef.current;
|
|
172
|
+
if (!row) return;
|
|
173
|
+
const observer = new ResizeObserver(() => {
|
|
174
|
+
const height = row.getBoundingClientRect().height;
|
|
175
|
+
if (height > 0) lastFooterRowHeight = height;
|
|
176
|
+
});
|
|
177
|
+
observer.observe(row);
|
|
178
|
+
return () => observer.disconnect();
|
|
179
|
+
}, []);
|
|
165
180
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
166
181
|
/* @__PURE__ */ jsxs(
|
|
167
182
|
"div",
|
|
@@ -171,19 +186,26 @@ function AssistantMessageFooter(props) {
|
|
|
171
186
|
isHoverOnly && "opacity-0 group-hover/assistant-msg:opacity-100 focus-within:opacity-100"
|
|
172
187
|
),
|
|
173
188
|
children: [
|
|
174
|
-
/* @__PURE__ */ jsxs(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
189
|
+
/* @__PURE__ */ jsxs(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
ref: rowRef,
|
|
193
|
+
className: "@container/message-footer flex w-full min-w-0 items-center gap-2",
|
|
194
|
+
children: [
|
|
195
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
196
|
+
RichDocumentActions,
|
|
197
|
+
{
|
|
198
|
+
content: config.content,
|
|
199
|
+
source: config.source,
|
|
200
|
+
actions: config.actions,
|
|
201
|
+
hideOverflow: !showOptions,
|
|
202
|
+
className: "px-0"
|
|
203
|
+
}
|
|
204
|
+
) }),
|
|
205
|
+
/* @__PURE__ */ jsx(MessageTimestamp, { timestamp: record?.createdAt })
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
),
|
|
187
209
|
/* @__PURE__ */ jsx(
|
|
188
210
|
NegativeVerdictFollowUp,
|
|
189
211
|
{
|
|
@@ -243,6 +265,7 @@ export {
|
|
|
243
265
|
ASSISTANT_ANSWER_PLACEMENT_MODE,
|
|
244
266
|
AssistantMessageContextMenu,
|
|
245
267
|
AssistantMessageFooter,
|
|
268
|
+
AssistantMessageFooterPlaceholder,
|
|
246
269
|
AssistantMessageFooter_default as default,
|
|
247
270
|
useAssistantMessageActions
|
|
248
271
|
};
|
|
@@ -5,7 +5,8 @@ import { useDomCapturePrint } from "../../../../conversation/hooks/useDomCapture
|
|
|
5
5
|
import { AgentAssistantMessage } from "./AgentAssistantMessage.js";
|
|
6
6
|
import {
|
|
7
7
|
AssistantMessageContextMenu,
|
|
8
|
-
AssistantMessageFooter
|
|
8
|
+
AssistantMessageFooter,
|
|
9
|
+
AssistantMessageFooterPlaceholder
|
|
9
10
|
} from "./AssistantMessageFooter.js";
|
|
10
11
|
import {
|
|
11
12
|
membersForRender,
|
|
@@ -111,6 +112,7 @@ function AssistantTurnGroup({
|
|
|
111
112
|
},
|
|
112
113
|
`files-${m.key}`
|
|
113
114
|
)),
|
|
115
|
+
lastMember && !showBar && /* @__PURE__ */ jsx(AssistantMessageFooterPlaceholder, {}),
|
|
114
116
|
showBar && anchorMessageId && // The group's bar sits BESIDE its members, so it carries the turn's
|
|
115
117
|
// one menu itself — its ⋯ opens the same menu the answer's
|
|
116
118
|
// right-click does (RC-B6), never a second drawn menu.
|
|
@@ -28,12 +28,21 @@
|
|
|
28
28
|
import type { ChatDispatch, ChatRootState } from "../../../../store/root-state.js";
|
|
29
29
|
import type { MessagePart } from "@ai-matrx/agents/generated/stream-events";
|
|
30
30
|
import type { ContextAnchor, SourceFeature, RequestInitiation } from "../../../types/instance.types.js";
|
|
31
|
+
import type { ApplicationScope } from "../../../types/scope.types.js";
|
|
31
32
|
import type { AnyMandateKey } from "@ai-matrx/agents/mandates";
|
|
32
33
|
export interface HeadlessAgentJsonOptions {
|
|
33
|
-
/** Exact agent to run. Mutually exclusive with mandateKey. */
|
|
34
|
+
/** Exact agent to run. Mutually exclusive with mandateKey and shortcutId. */
|
|
34
35
|
agentId?: string;
|
|
35
36
|
/** Swappable mandate to resolve inside the canonical launcher, preserving config_overrides. */
|
|
36
37
|
mandateKey?: AnyMandateKey;
|
|
38
|
+
/**
|
|
39
|
+
* A saved shortcut to run: the launcher resolves its agent, config and scope
|
|
40
|
+
* mappings exactly as a menu click would. Mutually exclusive with agentId and
|
|
41
|
+
* mandateKey. Pair with `applicationScope` for the shortcut's mapped values.
|
|
42
|
+
*/
|
|
43
|
+
shortcutId?: string;
|
|
44
|
+
/** UI-captured scope values a shortcut's scopeMappings read (selection, content, …). */
|
|
45
|
+
applicationScope?: ApplicationScope;
|
|
37
46
|
/** Stable surface key for telemetry + the focus registry. */
|
|
38
47
|
surfaceKey: string;
|
|
39
48
|
/** UI feature that triggered the run. */
|
|
@@ -176,7 +176,7 @@ async function runHeadlessAgentJson(dispatch, getState, opts) {
|
|
|
176
176
|
{
|
|
177
177
|
onResult: opts.onResult,
|
|
178
178
|
surfaceKey: opts.surfaceKey,
|
|
179
|
-
agentRef: opts.agentId ?? opts.mandateKey ?? "unknown"
|
|
179
|
+
agentRef: opts.agentId ?? opts.mandateKey ?? opts.shortcutId ?? "unknown"
|
|
180
180
|
},
|
|
181
181
|
result
|
|
182
182
|
);
|
|
@@ -244,20 +244,20 @@ async function launchAndWait(dispatch, getState, opts) {
|
|
|
244
244
|
};
|
|
245
245
|
try {
|
|
246
246
|
let executionIdentity;
|
|
247
|
+
const identities = [opts.agentId, opts.mandateKey, opts.shortcutId].filter((v) => v !== void 0);
|
|
248
|
+
if (identities.length !== 1) {
|
|
249
|
+
throw new Error("runHeadlessAgentJson requires exactly one of agentId, mandateKey or shortcutId");
|
|
250
|
+
}
|
|
247
251
|
if (opts.mandateKey !== void 0) {
|
|
248
|
-
if (opts.agentId !== void 0) {
|
|
249
|
-
throw new Error("runHeadlessAgentJson accepts agentId or mandateKey, never both");
|
|
250
|
-
}
|
|
251
252
|
executionIdentity = { mandateKey: opts.mandateKey };
|
|
253
|
+
} else if (opts.shortcutId !== void 0) {
|
|
254
|
+
executionIdentity = { shortcutId: opts.shortcutId };
|
|
252
255
|
} else {
|
|
253
|
-
if (opts.agentId === void 0) {
|
|
254
|
-
throw new Error("runHeadlessAgentJson requires agentId or mandateKey");
|
|
255
|
-
}
|
|
256
256
|
executionIdentity = { agentId: opts.agentId };
|
|
257
257
|
}
|
|
258
258
|
screamIfDeclaredFlattening(
|
|
259
259
|
opts,
|
|
260
|
-
opts.agentId ?? opts.mandateKey ?? "unknown"
|
|
260
|
+
opts.agentId ?? opts.mandateKey ?? opts.shortcutId ?? "unknown"
|
|
261
261
|
);
|
|
262
262
|
const launch = await dispatch(
|
|
263
263
|
launchAgentExecution({
|
|
@@ -282,7 +282,8 @@ async function launchAndWait(dispatch, getState, opts) {
|
|
|
282
282
|
runtime: {
|
|
283
283
|
...opts.surfaceName !== void 0 ? { surfaceName: opts.surfaceName } : {},
|
|
284
284
|
...opts.variables ? { variables: opts.variables } : {},
|
|
285
|
-
...opts.userInput !== void 0 ? { userInput: opts.userInput } : {}
|
|
285
|
+
...opts.userInput !== void 0 ? { userInput: opts.userInput } : {},
|
|
286
|
+
...opts.applicationScope ? { applicationScope: opts.applicationScope } : {}
|
|
286
287
|
},
|
|
287
288
|
// `callerExecutes` is the deliberate half of `autoRun: !twoStep`. A
|
|
288
289
|
// headless launch normally IGNORES autoRun (no interface = nothing to
|
|
@@ -335,7 +336,7 @@ async function launchAndWait(dispatch, getState, opts) {
|
|
|
335
336
|
pollMs,
|
|
336
337
|
settleMs,
|
|
337
338
|
surfaceKey: opts.surfaceKey,
|
|
338
|
-
agentRef: opts.agentId ?? opts.mandateKey ?? "unknown",
|
|
339
|
+
agentRef: opts.agentId ?? opts.mandateKey ?? opts.shortcutId ?? "unknown",
|
|
339
340
|
msgs,
|
|
340
341
|
expect: opts.expect ?? "json",
|
|
341
342
|
...opts.signal ? { signal: opts.signal } : {},
|
|
@@ -351,7 +352,7 @@ async function launchAndWait(dispatch, getState, opts) {
|
|
|
351
352
|
conversationId: conversationId ?? void 0,
|
|
352
353
|
raw: {
|
|
353
354
|
surfaceKey: opts.surfaceKey,
|
|
354
|
-
agent: opts.agentId ?? opts.mandateKey ?? "unknown",
|
|
355
|
+
agent: opts.agentId ?? opts.mandateKey ?? opts.shortcutId ?? "unknown",
|
|
355
356
|
detail,
|
|
356
357
|
// Only when nothing readable came out: the raw shape, so a throw this
|
|
357
358
|
// helper cannot read is a bug report rather than a shrug.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "THE one AI Matrx person-to-agent chat system: the full chat (running agents, conversations, tool calls, streaming, context, surfaces, voice) as per-module subpaths @ai-matrx/chat/<domain>/<module> over a host contract (/host, /host/react) whose only required value is a db; plus the kernel compatibility entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) until P28.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -202,40 +202,44 @@
|
|
|
202
202
|
"uuid": "^14.0.2",
|
|
203
203
|
"zod": "^4.6.5",
|
|
204
204
|
"@ai-matrx/alchemy": "latest",
|
|
205
|
-
"@ai-matrx/associations": "latest",
|
|
206
205
|
"@ai-matrx/browser-audio": "latest",
|
|
206
|
+
"@ai-matrx/associations": "latest",
|
|
207
207
|
"@ai-matrx/canvas": "latest",
|
|
208
208
|
"@ai-matrx/content-ir": "latest",
|
|
209
|
-
"@ai-matrx/agents": "latest",
|
|
210
209
|
"@ai-matrx/content-ir-react": "latest",
|
|
211
|
-
"@ai-matrx/data": "latest",
|
|
212
210
|
"@ai-matrx/design-system": "latest",
|
|
211
|
+
"@ai-matrx/diff": "latest",
|
|
213
212
|
"@ai-matrx/icons": "latest",
|
|
214
213
|
"@ai-matrx/kit": "latest",
|
|
215
214
|
"@ai-matrx/media": "latest",
|
|
216
|
-
"@ai-matrx/diff": "latest",
|
|
217
215
|
"@ai-matrx/print": "latest",
|
|
218
216
|
"@ai-matrx/realtime": "latest",
|
|
219
217
|
"@ai-matrx/records": "latest",
|
|
220
218
|
"@ai-matrx/records-ui": "latest",
|
|
221
|
-
"@ai-matrx/tap-target": "latest"
|
|
219
|
+
"@ai-matrx/tap-target": "latest",
|
|
220
|
+
"@ai-matrx/agents": "latest",
|
|
221
|
+
"@ai-matrx/data": "latest"
|
|
222
222
|
},
|
|
223
223
|
"devDependencies": {
|
|
224
224
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
225
225
|
"@reduxjs/toolkit": "^2.12.0",
|
|
226
226
|
"@testing-library/react": "^16.3.3",
|
|
227
|
+
"@types/jest": "^30.0.0",
|
|
227
228
|
"@types/lodash": "^4.17.25",
|
|
228
229
|
"@types/node": "^26.6.2",
|
|
229
230
|
"@types/react": "19.3.0",
|
|
230
231
|
"@types/react-dom": "19.3.0",
|
|
231
232
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
232
233
|
"immer": "^11.1.18",
|
|
234
|
+
"jest": "^30.5.2",
|
|
235
|
+
"jest-environment-jsdom": "^30.5.2",
|
|
233
236
|
"jsdom": "^30.1.1",
|
|
234
237
|
"next": "16.4.0-canary.39",
|
|
235
238
|
"publint": "^0.3.24",
|
|
236
239
|
"react": "^19.3.0",
|
|
237
240
|
"react-dom": "^19.3.0",
|
|
238
241
|
"react-redux": "^9.3.0",
|
|
242
|
+
"ts-jest": "^29.4.14",
|
|
239
243
|
"tsup": "^8.5.1",
|
|
240
244
|
"typescript": "^6.0.3",
|
|
241
245
|
"vitest": "^5.0.1"
|
|
@@ -275,9 +279,11 @@
|
|
|
275
279
|
"scripts": {
|
|
276
280
|
"build": "pnpm clean && tsup && node ./scripts/copy-css.mjs && node ./scripts/tsc-own.mjs -p tsconfig.build.json && node ./scripts/finish-build.mjs",
|
|
277
281
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
278
|
-
"check:package": "pnpm build && node ../scripts/check-exports-agreement.mjs && pnpm check:canonical-pickers && publint && pnpm verify:tarball && pnpm verify:modules-tarball && pnpm check:front-door:self-test",
|
|
282
|
+
"check:package": "pnpm build && node ../scripts/check-exports-agreement.mjs && pnpm check:canonical-pickers && publint && pnpm verify:tarball && pnpm verify:modules-tarball && pnpm check:front-door:self-test && pnpm check:host-boundary",
|
|
283
|
+
"check:host-boundary": "node ./scripts/check-host-boundary.mjs && node ./scripts/check-host-boundary.mjs --self-test",
|
|
279
284
|
"typecheck": "node ./node_modules/typescript/bin/tsc -p tsconfig.json --noEmit && node ./scripts/tsc-own.mjs -p tsconfig.modules.json",
|
|
280
|
-
"test": "vitest run",
|
|
285
|
+
"test": "vitest run && pnpm test:modules",
|
|
286
|
+
"test:modules": "jest -c jest.config.mjs",
|
|
281
287
|
"test:watch": "vitest",
|
|
282
288
|
"test:falsifiable": "node ./scripts/prove-falsifiable.mjs",
|
|
283
289
|
"verify:tarball": "node ./scripts/verify-tarball.mjs",
|