@ai-matrx/chat 0.5.16 → 0.5.17

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 CHANGED
@@ -1,9 +1,9 @@
1
1
  # @ai-matrx/chat
2
2
 
3
- ## 0.5.16
3
+ ## 0.5.17
4
4
 
5
5
  Automatic changed-only republish (docs/metadata drift since the last tag — see
6
- `git diff npm/chat/v0.5.15..npm/chat/v0.5.16 -- apps/shared/chat`).
6
+ `git diff npm/chat/v0.5.16..npm/chat/v0.5.17 -- apps/shared/chat`).
7
7
  No source changes intended and no consumer action required.
8
8
 
9
9
  ## 0.5.10 — message Print is the one message print
@@ -66,7 +66,11 @@ import { markResourcesSubmitted } from "../instance-resources/instance-resources
66
66
  import { flushRemarkWrites, hasPendingRemarkWrites } from "../instance-resources/remarks.js";
67
67
  import { withFreshRemarkRefs } from "../instance-resources/remarks-wire.js";
68
68
  import { isOrganizationSelectionCancelled } from "../../../../host/org.js";
69
- import { runBeforeSend } from "../../../../host/conversation-hooks.js";
69
+ import {
70
+ runBeforeSend,
71
+ startHostConversation,
72
+ startsConversationOnFirstSend
73
+ } from "../../../../host/conversation-hooks.js";
70
74
  function hasConversationAtExecutionBoundary(state, conversationId) {
71
75
  return state.conversations.byConversationId[conversationId] !== void 0;
72
76
  }
@@ -154,6 +158,35 @@ function holdBehindInFlightSend(dispatch, getState, args) {
154
158
  const smartExecute = createAsyncThunk(
155
159
  "instances/smartExecute",
156
160
  async ({ conversationId, surfaceKey, whileRunning = "queue", submission: handed }, { getState, dispatch }) => {
161
+ if (!handed && startsConversationOnFirstSend(conversationId)) {
162
+ const draft = captureSubmission(getState(), conversationId);
163
+ if (isEmptySubmission(draft)) return;
164
+ if (draft.resources.length > 0) {
165
+ toast.info("Attachments can't be added yet", {
166
+ description: "Send your first message as text \u2014 attachments work from the next one."
167
+ });
168
+ return;
169
+ }
170
+ detachFromComposer(dispatch, getState, conversationId, draft);
171
+ let realId = null;
172
+ try {
173
+ realId = await startHostConversation(conversationId, { text: draft.text });
174
+ } catch (error) {
175
+ returnToComposer(dispatch, getState, conversationId, draft);
176
+ toast.error("Message not sent", {
177
+ description: error instanceof Error ? error.message : String(error)
178
+ });
179
+ return;
180
+ }
181
+ if (!realId) {
182
+ returnToComposer(dispatch, getState, conversationId, draft);
183
+ toast.error("Message not sent", { description: "The conversation could not be started." });
184
+ return;
185
+ }
186
+ dispatch(setUserInputText({ conversationId: realId, text: draft.text, userValues: draft.userValues }));
187
+ await dispatch(smartExecute({ conversationId: realId, surfaceKey, whileRunning }));
188
+ return;
189
+ }
157
190
  if (!handed && !hasConversationAtExecutionBoundary(getState(), conversationId)) {
158
191
  await awaitLaunchingConversation(getState, conversationId);
159
192
  }
@@ -15,6 +15,13 @@
15
15
  * it. Must not throw (a throw is reported and swallowed).
16
16
  * startConversation the host names the next conversation's id; the client
17
17
  * never mints one.
18
+ * startsOnFirstSend this conversation is a DRAFT: it has no server-side
19
+ * conversation yet (the thing it talks about does not
20
+ * exist). The first send calls `startConversation` — the
21
+ * host creates what it must and names the REAL
22
+ * conversation (instance already in the store) — and the
23
+ * words travel to that one. Text only; the draft is
24
+ * never sent to a server under its own id.
18
25
  * everyTurnThroughMandate
19
26
  * every turn (not only turn 1) POSTs the mandate route,
20
27
  * with the gate's variables + context — for a mandate
@@ -45,7 +52,11 @@ export interface ConversationHostHooks {
45
52
  beforeSend?: (input: HostBeforeSendInput) => Promise<HostTurnExtras | HostSendRefused>;
46
53
  held?: HostHeld;
47
54
  onEvent?: (event: TypedStreamEvent, ctx: HostStreamTapContext) => void;
48
- startConversation?: () => Promise<string>;
55
+ startConversation?: (input?: {
56
+ text: string;
57
+ }) => Promise<string>;
58
+ /** With `startConversation`: this id is a draft; the first send goes through `startConversation`. */
59
+ startsOnFirstSend?: boolean;
49
60
  everyTurnThroughMandate?: boolean;
50
61
  /** How a stored user turn reads on screen (display only; the record is unchanged). */
51
62
  displayUserText?: (text: string) => string;
@@ -81,8 +92,12 @@ export declare function takeTurnExtras(conversationId: string): HostTurnExtras |
81
92
  export declare function tapStreamEvent(conversationId: string, requestId: string, event: unknown): void;
82
93
  /** Should this conversation's later turns also POST the mandate route? */
83
94
  export declare function continuesThroughMandate(conversationId: string): boolean;
95
+ /** Is this id a draft whose first send must first create the real conversation? */
96
+ export declare function startsConversationOnFirstSend(conversationId: string): boolean;
84
97
  /** Ask the conversation's host for the next conversation id; null when it has no say. */
85
- export declare function startHostConversation(conversationId: string): Promise<string | null>;
98
+ export declare function startHostConversation(conversationId: string, input?: {
99
+ text: string;
100
+ }): Promise<string | null>;
86
101
  /**
87
102
  * Subscribe to changes of this conversation's held sentence: the host's own
88
103
  * source, and the registry (a registration swaps which source is listened to).
@@ -58,9 +58,13 @@ function tapStreamEvent(conversationId, requestId, event) {
58
58
  function continuesThroughMandate(conversationId) {
59
59
  return hooksByConversation.get(conversationId)?.everyTurnThroughMandate === true;
60
60
  }
61
- async function startHostConversation(conversationId) {
61
+ function startsConversationOnFirstSend(conversationId) {
62
+ const hooks = hooksByConversation.get(conversationId);
63
+ return hooks?.startsOnFirstSend === true && typeof hooks.startConversation === "function";
64
+ }
65
+ async function startHostConversation(conversationId, input) {
62
66
  const start = hooksByConversation.get(conversationId)?.startConversation;
63
- return start ? start() : null;
67
+ return start ? start(input) : null;
64
68
  }
65
69
  function subscribeConversationHeld(conversationId, listener) {
66
70
  let current;
@@ -93,6 +97,7 @@ export {
93
97
  registerConversationHooks,
94
98
  runBeforeSend,
95
99
  startHostConversation,
100
+ startsConversationOnFirstSend,
96
101
  subscribeConversationHeld,
97
102
  subscribeConversationHooks,
98
103
  takeTurnExtras,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/chat",
3
- "version": "0.5.16",
3
+ "version": "0.5.17",
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. One chat, one renderer: the old kernel entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) are retired.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -167,24 +167,24 @@
167
167
  "uuid": "^14.0.2",
168
168
  "zod": "^4.6.5",
169
169
  "@ai-matrx/agents": "latest",
170
+ "@ai-matrx/alchemy": "latest",
170
171
  "@ai-matrx/associations": "latest",
171
172
  "@ai-matrx/browser-audio": "latest",
172
173
  "@ai-matrx/canvas": "latest",
173
174
  "@ai-matrx/content-ir": "latest",
174
- "@ai-matrx/content-ir-react": "latest",
175
175
  "@ai-matrx/data": "latest",
176
+ "@ai-matrx/content-ir-react": "latest",
177
+ "@ai-matrx/diff": "latest",
176
178
  "@ai-matrx/design-system": "latest",
177
- "@ai-matrx/icons": "latest",
178
179
  "@ai-matrx/kit": "latest",
179
- "@ai-matrx/media": "latest",
180
- "@ai-matrx/print": "latest",
181
180
  "@ai-matrx/realtime": "latest",
182
181
  "@ai-matrx/records": "latest",
183
- "@ai-matrx/records-ui": "latest",
184
182
  "@ai-matrx/rich-content": "latest",
183
+ "@ai-matrx/records-ui": "latest",
185
184
  "@ai-matrx/rich-editor": "latest",
186
- "@ai-matrx/alchemy": "latest",
187
- "@ai-matrx/diff": "latest"
185
+ "@ai-matrx/print": "latest",
186
+ "@ai-matrx/icons": "latest",
187
+ "@ai-matrx/media": "latest"
188
188
  },
189
189
  "devDependencies": {
190
190
  "@arethetypeswrong/cli": "^0.18.5",
@@ -299,5 +299,6 @@
299
299
  "deps:write": "node ./scripts/finish-build.mjs --write-deps",
300
300
  "check:side-panel-bundle": "node ./scripts/check-side-panel-bundle.mjs",
301
301
  "check:side-effects": "node ../scripts/check-side-effects-listed.mjs . && node ../scripts/check-side-effects-listed.mjs --self-test"
302
- }
302
+ },
303
+ "gitHead": "72c166ca9e3b90dc0f6648754257142aba4756b6"
303
304
  }