@ai-matrx/chat 0.4.7 → 0.4.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.
- package/CHANGELOG.md +6 -0
- package/dist/agents/redux/execution-system/thunks/process-stream.js +24 -11
- package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.d.ts +2 -0
- package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +25 -4
- package/dist/agents/runtime-reconnect/reconnect-server-operation.thunk.js +10 -6
- package/dist/host/defaults/prefs.d.ts +13 -5
- package/dist/host/defaults/prefs.js +11 -2
- package/dist/host/index.d.ts +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.4.8
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/chat/v0.4.7..npm/chat/v0.4.8 -- apps/shared/chat`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
3
9
|
## 0.4.7
|
|
4
10
|
|
|
5
11
|
- The host-source column test gets an explicit 120s budget: the column now renders the real text engine
|
|
@@ -5,7 +5,11 @@ import {
|
|
|
5
5
|
classifyUsageRefusal
|
|
6
6
|
} from "../../../../host/usage-gate.js";
|
|
7
7
|
import { parseNdjsonStream } from "../../../../host/server/stream-parser.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
isStreamTransportLost,
|
|
10
|
+
readStreamCursor,
|
|
11
|
+
StreamTransportError
|
|
12
|
+
} from "@ai-matrx/agents/matrx";
|
|
9
13
|
import { monitorStream } from "@ai-matrx/data/net";
|
|
10
14
|
import { mintClientTempId } from "@ai-matrx/kit/ids";
|
|
11
15
|
import { withPerformedScript } from "../../../speech-script/types.js";
|
|
@@ -139,15 +143,7 @@ import { getCapabilitiesForConversation } from "../../../runtime/get-model-capab
|
|
|
139
143
|
import { toast } from "../../../../host/notify.js";
|
|
140
144
|
import { isDirectiveApplyEvent } from "@ai-matrx/agents/envelope";
|
|
141
145
|
import { proposeDirective } from "../../proposed-directives/proposedDirectivesSlice.js";
|
|
142
|
-
|
|
143
|
-
if (typeof event !== "object" || event === null || !("stream_seq" in event) || typeof event.stream_seq !== "number" || !Number.isSafeInteger(event.stream_seq) || event.stream_seq <= 0) {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
return {
|
|
147
|
-
streamId: "stream_id" in event && typeof event.stream_id === "string" && event.stream_id.length > 0 ? event.stream_id : null,
|
|
148
|
-
streamSeq: event.stream_seq
|
|
149
|
-
};
|
|
150
|
-
}
|
|
146
|
+
const readTransportCursor = readStreamCursor;
|
|
151
147
|
function renderBlockToContentType(type) {
|
|
152
148
|
if (type === "image" || type === "image_output") return "image";
|
|
153
149
|
if (type === "audio_output") return "audio";
|
|
@@ -349,6 +345,7 @@ async function processStream({
|
|
|
349
345
|
let pendingTransportCursor = null;
|
|
350
346
|
const batchedDispatch = (action) => dispatch(autoBatched(action));
|
|
351
347
|
let reasoningLeakReported = false;
|
|
348
|
+
let serverStructuredData = null;
|
|
352
349
|
const runJsonExtraction = (isFinal) => {
|
|
353
350
|
if (!jsonTracker) return;
|
|
354
351
|
const req = getState().activeRequests.byRequestId[requestId];
|
|
@@ -367,10 +364,22 @@ async function processStream({
|
|
|
367
364
|
const jsonState = isFinal ? jsonTracker.finalize() : jsonTracker.getState();
|
|
368
365
|
if (isFinal || jsonState.revision !== lastJsonRevision) {
|
|
369
366
|
lastJsonRevision = jsonState.revision;
|
|
367
|
+
const clientResults = jsonState.results.map(toSnapshot);
|
|
368
|
+
const results = serverStructuredData ? [
|
|
369
|
+
toSnapshot({
|
|
370
|
+
value: serverStructuredData,
|
|
371
|
+
type: Array.isArray(serverStructuredData) ? "array" : "object",
|
|
372
|
+
source: "whole-string",
|
|
373
|
+
isComplete: true,
|
|
374
|
+
repairApplied: false,
|
|
375
|
+
warnings: ["Server structured_output payload"]
|
|
376
|
+
}),
|
|
377
|
+
...clientResults
|
|
378
|
+
] : clientResults;
|
|
370
379
|
batchedDispatch(
|
|
371
380
|
updateExtractedJson({
|
|
372
381
|
requestId,
|
|
373
|
-
results
|
|
382
|
+
results,
|
|
374
383
|
revision: jsonState.revision,
|
|
375
384
|
isComplete: isFinal
|
|
376
385
|
})
|
|
@@ -1920,6 +1929,10 @@ async function processStream({
|
|
|
1920
1929
|
);
|
|
1921
1930
|
} else if (isStructuredOutputEvent(event)) {
|
|
1922
1931
|
otherEvents++;
|
|
1932
|
+
if (event.data.success && event.data.data != null && typeof event.data.data === "object") {
|
|
1933
|
+
serverStructuredData = event.data.data;
|
|
1934
|
+
runJsonExtraction(false);
|
|
1935
|
+
}
|
|
1923
1936
|
dispatch(
|
|
1924
1937
|
appendTimeline({
|
|
1925
1938
|
requestId,
|
|
@@ -276,6 +276,8 @@ export interface HeadlessAgentJsonResult {
|
|
|
276
276
|
* result passes through untouched.
|
|
277
277
|
*/
|
|
278
278
|
export declare function failWarnedOutputMissingKeys(getState: () => ChatRootState, opts: Pick<HeadlessAgentJsonOptions, "mandateKey" | "surfaceKey" | "expect">, result: HeadlessAgentJsonResult): Promise<HeadlessAgentJsonResult>;
|
|
279
|
+
/** The first JSON object in `text` that holds EVERY required key, or null. */
|
|
280
|
+
export declare function recoverByRequiredKeys(text: string | undefined, required: readonly string[]): Record<string, unknown> | null;
|
|
279
281
|
/**
|
|
280
282
|
* The plain sentence of a warned-holder failure (`failWarnedOutputMissingKeys`),
|
|
281
283
|
* or `null` for any other result. A lane that swallows failures to `null`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractFirstJson } from "@ai-matrx/kit/json-extract";
|
|
1
|
+
import { extractAllJson, extractFirstJson } from "@ai-matrx/kit/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";
|
|
@@ -129,13 +129,18 @@ async function failWarnedOutputMissingKeys(getState, opts, result) {
|
|
|
129
129
|
if (required.length === 0) return result;
|
|
130
130
|
let missing;
|
|
131
131
|
if (result.success && isRecord) {
|
|
132
|
-
|
|
133
|
-
const answers = typeof record.answers === "object" && record.answers !== null && !Array.isArray(record.answers) ? record.answers : null;
|
|
134
|
-
missing = required.filter((key) => !(key in record) && !(answers && key in answers));
|
|
132
|
+
missing = missingRequiredKeys(data, required);
|
|
135
133
|
} else {
|
|
136
134
|
missing = [...required];
|
|
137
135
|
}
|
|
138
136
|
if (missing.length === 0) return result;
|
|
137
|
+
const recovered = recoverByRequiredKeys(result.fullResponse, required);
|
|
138
|
+
if (recovered) {
|
|
139
|
+
console.warn(
|
|
140
|
+
`[runHeadlessAgentJson] ${opts.surfaceKey}: the first JSON read lacked ${missing.join(", ")}; a candidate in the answer holding every required key was used instead.`
|
|
141
|
+
);
|
|
142
|
+
return { ...result, success: true, data: recovered, noResult: false, error: void 0, errorDetail: void 0 };
|
|
143
|
+
}
|
|
139
144
|
const warning = resolved.outputWarnings[0];
|
|
140
145
|
const holderId = warning.holderId ?? resolved.agentId;
|
|
141
146
|
let holderName;
|
|
@@ -174,6 +179,21 @@ async function failWarnedOutputMissingKeys(getState, opts, result) {
|
|
|
174
179
|
errorDetail: `mandate_output_unusable: missing ${keys}`
|
|
175
180
|
};
|
|
176
181
|
}
|
|
182
|
+
function missingRequiredKeys(record, required) {
|
|
183
|
+
const answers = typeof record.answers === "object" && record.answers !== null && !Array.isArray(record.answers) ? record.answers : null;
|
|
184
|
+
return required.filter((key) => !(key in record) && !(answers && key in answers));
|
|
185
|
+
}
|
|
186
|
+
function recoverByRequiredKeys(text, required) {
|
|
187
|
+
if (!text || required.length === 0) return null;
|
|
188
|
+
for (const candidate of extractAllJson(text, { allowFuzzy: true })) {
|
|
189
|
+
const value = candidate.value;
|
|
190
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
|
|
191
|
+
if (missingRequiredKeys(value, required).length === 0) {
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
177
197
|
function mandateOutputUnusableSentence(result) {
|
|
178
198
|
if (result.success) return null;
|
|
179
199
|
if (!result.errorDetail?.startsWith("mandate_output_unusable")) return null;
|
|
@@ -565,5 +585,6 @@ export {
|
|
|
565
585
|
failWarnedOutputMissingKeys,
|
|
566
586
|
livePosture,
|
|
567
587
|
mandateOutputUnusableSentence,
|
|
588
|
+
recoverByRequiredKeys,
|
|
568
589
|
runHeadlessAgentJson
|
|
569
590
|
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { retryReconnect } from "./retry-reconnect.js";
|
|
2
2
|
import { discardRetainedTransportConsumer } from "../redux/execution-system/thunks/process-stream.js";
|
|
3
3
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
pickServerJobOperation,
|
|
6
|
+
runtimeOperationRejoinPath,
|
|
7
|
+
settleRunPickup
|
|
8
|
+
} from "@ai-matrx/agents/matrx";
|
|
5
9
|
import { toast } from "../../host/notify.js";
|
|
6
10
|
import { captureError } from "../../host/diagnostics.js";
|
|
7
11
|
import { setRequestStatus } from "../redux/execution-system/active-requests/active-requests.slice.js";
|
|
@@ -77,8 +81,9 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
77
81
|
clearFollower();
|
|
78
82
|
return noFollow;
|
|
79
83
|
}
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const picked = pickServerJobOperation(byLink?.operations);
|
|
85
|
+
op = picked && !picked.is_terminal ? picked : null;
|
|
86
|
+
settledWhileDisconnected = Boolean(picked?.is_terminal);
|
|
82
87
|
} catch (err) {
|
|
83
88
|
const superseded = ctrl.signal.aborted || !stillOwner();
|
|
84
89
|
clearFollower();
|
|
@@ -263,9 +268,8 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
263
268
|
clearFollower();
|
|
264
269
|
return noFollow;
|
|
265
270
|
}
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
);
|
|
271
|
+
const latest = pickServerJobOperation(refreshed?.operations);
|
|
272
|
+
const stillLive = latest && !latest.is_terminal ? latest : null;
|
|
269
273
|
if (stillLive) {
|
|
270
274
|
if (followers.get(conversationId) === ctrl) {
|
|
271
275
|
followers.delete(conversationId);
|
|
@@ -23,14 +23,22 @@ export declare const PREFERENCE_FIELDS: Readonly<Record<string, keyof ChatPrefer
|
|
|
23
23
|
export declare function createUnhostedKnobs(): ChatKnobsPort;
|
|
24
24
|
export declare const CHAT_PREFS_PREFIX = "ai-matrx-chat:";
|
|
25
25
|
export declare function createWebPrefs(): ChatPrefsPort;
|
|
26
|
+
/** A host-kept prefs port, plus the late arrival of the host's saved values. */
|
|
27
|
+
export interface HostKeptPrefs extends ChatPrefsPort {
|
|
28
|
+
/**
|
|
29
|
+
* The host's saved values arrived after the port was made (read over a socket or IPC). A key
|
|
30
|
+
* already written this session keeps the newer value; every other key is set and announced.
|
|
31
|
+
*/
|
|
32
|
+
load(values: Readonly<Record<string, string>>): void;
|
|
33
|
+
}
|
|
26
34
|
/**
|
|
27
35
|
* A prefs port the HOST keeps (HOST-OWNED-SOURCE-PLAN §2.2 "Drafts"): a page whose origin
|
|
28
36
|
* changes between launches (a local host on a new port, a desktop window) cannot keep drafts in
|
|
29
|
-
* web storage. The host
|
|
30
|
-
* (`persist`, `null` = removed) to write where it keeps them — a file,
|
|
31
|
-
*
|
|
37
|
+
* web storage. The host hands its saved values in (`initial`, or `load` when they arrive later)
|
|
38
|
+
* and receives every change (`persist`, `null` = removed) to write where it keeps them — a file,
|
|
39
|
+
* an IPC store. Reads are synchronous from memory.
|
|
32
40
|
*/
|
|
33
41
|
export declare function createHostKeptPrefs(options: {
|
|
34
|
-
initial
|
|
42
|
+
initial?: Readonly<Record<string, string>>;
|
|
35
43
|
persist: (key: string, value: string | null) => void;
|
|
36
|
-
}):
|
|
44
|
+
}): HostKeptPrefs;
|
|
@@ -169,12 +169,14 @@ function createWebPrefs() {
|
|
|
169
169
|
return port;
|
|
170
170
|
}
|
|
171
171
|
function createHostKeptPrefs(options) {
|
|
172
|
-
const memory = new Map(Object.entries(options.initial));
|
|
172
|
+
const memory = new Map(Object.entries(options.initial ?? {}));
|
|
173
|
+
const written = /* @__PURE__ */ new Set();
|
|
173
174
|
const listeners = /* @__PURE__ */ new Set();
|
|
174
175
|
const emit = (key) => {
|
|
175
176
|
for (const listener of listeners) listener(key);
|
|
176
177
|
};
|
|
177
178
|
const write = (key, value) => {
|
|
179
|
+
written.add(key);
|
|
178
180
|
try {
|
|
179
181
|
options.persist(key, value);
|
|
180
182
|
} catch (error) {
|
|
@@ -206,7 +208,14 @@ function createHostKeptPrefs(options) {
|
|
|
206
208
|
},
|
|
207
209
|
knob: (key, fallback) => parseKnob(memory.get(key) ?? null, fallback),
|
|
208
210
|
knobs: createUnhostedKnobs(),
|
|
209
|
-
snapshot: () => Object.fromEntries(memory)
|
|
211
|
+
snapshot: () => Object.fromEntries(memory),
|
|
212
|
+
load(values) {
|
|
213
|
+
for (const [key, value] of Object.entries(values)) {
|
|
214
|
+
if (written.has(key) || memory.get(key) === value) continue;
|
|
215
|
+
memory.set(key, value);
|
|
216
|
+
emit(key);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
210
219
|
};
|
|
211
220
|
return port;
|
|
212
221
|
}
|
package/dist/host/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { createDbIdentity, SIGNED_OUT_IDENTITY } from "./defaults/identity.js";
|
|
|
10
10
|
export { createNoPickerOrg, createStoredOrg, CHAT_ACTIVE_ORG_STORAGE_KEY } from "./defaults/org.js";
|
|
11
11
|
export { createDefaultServer, DEFAULT_CHAT_SERVER_URL, } from "./defaults/server.js";
|
|
12
12
|
export { createDomNotifier } from "./defaults/notify.js";
|
|
13
|
-
export { createWebPrefs, createHostKeptPrefs, CHAT_PREFS_PREFIX } from "./defaults/prefs.js";
|
|
13
|
+
export { createWebPrefs, createHostKeptPrefs, CHAT_PREFS_PREFIX, type HostKeptPrefs } from "./defaults/prefs.js";
|
|
14
14
|
export { createWindowNavigation } from "./defaults/navigation.js";
|
|
15
15
|
export { createUnhostedWindows } from "./defaults/windows.js";
|
|
16
16
|
export { CHAT_WINDOWS, CHAT_WINDOW_IDS, DEFAULT_WINDOW_INSTANCE_ID, isChatWindowId, type ChatWindowId, } from "./windows.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
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",
|
|
@@ -226,17 +226,14 @@
|
|
|
226
226
|
"server-only": "^0.0.1",
|
|
227
227
|
"uuid": "^14.0.2",
|
|
228
228
|
"zod": "^4.6.5",
|
|
229
|
+
"@ai-matrx/agents": "latest",
|
|
229
230
|
"@ai-matrx/alchemy": "latest",
|
|
230
|
-
"@ai-matrx/associations": "latest",
|
|
231
231
|
"@ai-matrx/browser-audio": "latest",
|
|
232
|
-
"@ai-matrx/canvas": "latest",
|
|
233
|
-
"@ai-matrx/agents": "latest",
|
|
234
232
|
"@ai-matrx/content-ir": "latest",
|
|
235
|
-
"@ai-matrx/
|
|
236
|
-
"@ai-matrx/
|
|
233
|
+
"@ai-matrx/associations": "latest",
|
|
234
|
+
"@ai-matrx/canvas": "latest",
|
|
237
235
|
"@ai-matrx/design-system": "latest",
|
|
238
236
|
"@ai-matrx/diff": "latest",
|
|
239
|
-
"@ai-matrx/icons": "latest",
|
|
240
237
|
"@ai-matrx/kit": "latest",
|
|
241
238
|
"@ai-matrx/media": "latest",
|
|
242
239
|
"@ai-matrx/print": "latest",
|
|
@@ -245,6 +242,9 @@
|
|
|
245
242
|
"@ai-matrx/records-ui": "latest",
|
|
246
243
|
"@ai-matrx/rich-content": "latest",
|
|
247
244
|
"@ai-matrx/rich-editor": "latest",
|
|
245
|
+
"@ai-matrx/data": "latest",
|
|
246
|
+
"@ai-matrx/content-ir-react": "latest",
|
|
247
|
+
"@ai-matrx/icons": "latest",
|
|
248
248
|
"@ai-matrx/tap-target": "latest"
|
|
249
249
|
},
|
|
250
250
|
"devDependencies": {
|