@ai-matrx/chat 0.5.6 → 0.5.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 +13 -0
- package/dist/agents/components/context-items/registry.d.ts +1 -0
- package/dist/agents/components/context-items/registry.js +7 -3
- package/dist/agents/components/inputs/resources/attach-resource.d.ts +30 -0
- package/dist/agents/components/inputs/resources/attach-resource.js +11 -2
- package/dist/agents/components/inputs/resources/useAttachRenderedArtifact.d.ts +6 -0
- package/dist/agents/components/inputs/resources/useAttachRenderedArtifact.js +79 -69
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.5.8 — attach from outside React
|
|
4
|
+
|
|
5
|
+
- `attachResourceToConversation(store, conversationId, resource)` — THE attach path without a hook
|
|
6
|
+
(`useAttachResource` is it bound to the app store); `attachRenderedArtifact(store, conversationId,
|
|
7
|
+
args)` — the same for a rendered page, for a canvas "Attach to chat ▸" entry or any host port.
|
|
8
|
+
- Consumer action: none required; a canvas host may wire `attachRenderedArtifact` into
|
|
9
|
+
`CanvasOutputPorts.attachOptions`.
|
|
10
|
+
|
|
11
|
+
## 0.5.7 — a registry/list disagreement no longer crashes the page
|
|
12
|
+
|
|
13
|
+
- The context-items load-time drift check logs a loud console error instead of throwing; the
|
|
14
|
+
unregistered type renders through the generic fallback body. A jest test fails on any disagreement.
|
|
15
|
+
|
|
3
16
|
## 0.5.6 — 0.5.5 threw at load
|
|
4
17
|
|
|
5
18
|
- `CONTEXT_ITEM_BLOCK_TYPES` lists `rendered_artifact`: 0.5.5 registered the def without it, so the
|
|
@@ -17,6 +17,7 @@ import type { ComponentType } from "react";
|
|
|
17
17
|
import { hasContextItemDef } from "./context-item-block-types.js";
|
|
18
18
|
import type { ContextItemBodyProps, ContextItemTitleProps, ContextItemTypeDef } from "./types.js";
|
|
19
19
|
export declare const CONTEXT_ITEM_TYPE_DEFS: ContextItemTypeDef[];
|
|
20
|
+
export declare function findContextItemRegistryDrift(): string[];
|
|
20
21
|
export { hasContextItemDef };
|
|
21
22
|
/** Resolve the registered def for a block type, or a graceful fallback. */
|
|
22
23
|
export declare function resolveContextItemDef(blockType: string): ContextItemTypeDef;
|
|
@@ -339,14 +339,17 @@ const FALLBACK_DEF = {
|
|
|
339
339
|
editable: false,
|
|
340
340
|
Body: GenericBody
|
|
341
341
|
};
|
|
342
|
-
{
|
|
342
|
+
function findContextItemRegistryDrift() {
|
|
343
343
|
const listed = new Set(CONTEXT_ITEM_BLOCK_TYPES);
|
|
344
|
-
|
|
344
|
+
return [
|
|
345
345
|
...[...BY_BLOCK_TYPE.keys()].filter((t) => !listed.has(t)),
|
|
346
346
|
...CONTEXT_ITEM_BLOCK_TYPES.filter((t) => !BY_BLOCK_TYPE.has(t))
|
|
347
347
|
];
|
|
348
|
+
}
|
|
349
|
+
{
|
|
350
|
+
const drift = findContextItemRegistryDrift();
|
|
348
351
|
if (drift.length > 0) {
|
|
349
|
-
|
|
352
|
+
console.error(
|
|
350
353
|
`context-items registry and CONTEXT_ITEM_BLOCK_TYPES disagree: ${drift.join(", ")}`
|
|
351
354
|
);
|
|
352
355
|
}
|
|
@@ -371,6 +374,7 @@ function resolveContextItemTitle(blockType) {
|
|
|
371
374
|
}
|
|
372
375
|
export {
|
|
373
376
|
CONTEXT_ITEM_TYPE_DEFS,
|
|
377
|
+
findContextItemRegistryDrift,
|
|
374
378
|
hasContextItemDef,
|
|
375
379
|
resolveContextItemBody,
|
|
376
380
|
resolveContextItemDef,
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* attach-resource — the single mapping from a picked `Resource` to how it gets
|
|
3
|
+
* attached to a conversation, shared by every surface that attaches resources
|
|
4
|
+
* (SmartAgentResourcePickerButton, the RunControlsMenu Attach tab, …).
|
|
5
|
+
*
|
|
6
|
+
* TWO attach shapes, by kind:
|
|
7
|
+
* - A stored FILE → a DURABLE `platform.associations` edge to the conversation
|
|
8
|
+
* (`file → conversation`). The file ID means its complete existing family,
|
|
9
|
+
* including processed documents and Knowledge. This PERSISTS across turns and reloads; the
|
|
10
|
+
* backend reads the conversation's edges at call time and injects the context.
|
|
11
|
+
* The chip renders from the edge list (see `AttachedDocumentChips`), NOT the
|
|
12
|
+
* ephemeral `instanceResources` slice.
|
|
13
|
+
* - Before the conversation row exists, the same stored FILE identity is held
|
|
14
|
+
* as a `processed_document` instance resource and emitted in request.context.
|
|
15
|
+
* It is never downgraded to a provider-native PDF block.
|
|
16
|
+
* - Everything else (media bytes, notes, tasks, webpages, …) → the per-turn
|
|
17
|
+
* `instanceResources` block. A document with no durable file identity still
|
|
18
|
+
* rides content[] as binary media.
|
|
19
|
+
*/
|
|
20
|
+
import type { Dispatch } from "@reduxjs/toolkit";
|
|
1
21
|
import type { ChatRootState } from "../../../../store/root-state.js";
|
|
2
22
|
import type { Resource } from "../../../resources/types.js";
|
|
3
23
|
import type { ResourceBlockType } from "../../../types/instance.types.js";
|
|
@@ -24,7 +44,17 @@ export declare function findIdenticalResourceId(state: ChatRootState, conversati
|
|
|
24
44
|
* the per-turn binary/instanceResources path unchanged. Closing the hosting
|
|
25
45
|
* popover is the CALLER's job.
|
|
26
46
|
*/
|
|
47
|
+
/** The store handles a non-React caller passes to the attach functions. */
|
|
48
|
+
export interface AttachStore {
|
|
49
|
+
dispatch: Dispatch;
|
|
50
|
+
getState: () => unknown;
|
|
51
|
+
}
|
|
27
52
|
export declare function useAttachResource(conversationId: string): (resource: Resource) => Promise<boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* THE attach path, callable outside React (a canvas menu entry, a host port).
|
|
55
|
+
* `useAttachResource` is this bound to the app store.
|
|
56
|
+
*/
|
|
57
|
+
export declare function attachResourceToConversation(store: AttachStore, conversationId: string, resource: Resource): Promise<boolean>;
|
|
28
58
|
/**
|
|
29
59
|
* Inverse of `useAttachResource` for files toggled in the live picker.
|
|
30
60
|
* Owned media can have both a durable authorization edge and a per-turn media
|
|
@@ -183,7 +183,15 @@ async function attachConversationFileEdge(conversationId, sourceId, fileId, labe
|
|
|
183
183
|
function useAttachResource(conversationId) {
|
|
184
184
|
const dispatch = useAppDispatch();
|
|
185
185
|
const store = useAppStore();
|
|
186
|
-
return
|
|
186
|
+
return (resource) => attachResourceToConversation(
|
|
187
|
+
{ dispatch, getState: store.getState },
|
|
188
|
+
conversationId,
|
|
189
|
+
resource
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
async function attachResourceToConversation(store, conversationId, resource) {
|
|
193
|
+
const dispatch = store.dispatch;
|
|
194
|
+
{
|
|
187
195
|
const getState = () => store.getState();
|
|
188
196
|
const baseBlockType = resourceTypeToBlockType(resource.type);
|
|
189
197
|
const blockType = refineBlockType(baseBlockType, resource.data);
|
|
@@ -298,7 +306,7 @@ function useAttachResource(conversationId) {
|
|
|
298
306
|
resourcePreviewLabel
|
|
299
307
|
);
|
|
300
308
|
return true;
|
|
301
|
-
}
|
|
309
|
+
}
|
|
302
310
|
}
|
|
303
311
|
function useDetachResource(conversationId) {
|
|
304
312
|
const dispatch = useAppDispatch();
|
|
@@ -342,6 +350,7 @@ function useDetachResource(conversationId) {
|
|
|
342
350
|
};
|
|
343
351
|
}
|
|
344
352
|
export {
|
|
353
|
+
attachResourceToConversation,
|
|
345
354
|
findIdenticalResourceId,
|
|
346
355
|
resourceLabel,
|
|
347
356
|
resourceTypeToBlockType,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RenderedArtifactRepresentation } from "../../../types/instance.types.js";
|
|
2
2
|
import { type RenderedArtifactSource } from "../../../utils/renderedArtifactContext.js";
|
|
3
|
+
import { type AttachStore } from "./attach-resource.js";
|
|
3
4
|
/** What a host capture port returns: a stored cloud image, or a local PNG to upload. */
|
|
4
5
|
export type RenderedCaptureResult = {
|
|
5
6
|
kind: "stored";
|
|
@@ -18,3 +19,8 @@ export interface AttachRenderedArtifactArgs {
|
|
|
18
19
|
capture?: () => Promise<RenderedCaptureResult>;
|
|
19
20
|
}
|
|
20
21
|
export declare function useAttachRenderedArtifact(conversationId: string | null | undefined): (args: AttachRenderedArtifactArgs) => Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* The same attach outside React (a canvas "Attach to chat ▸" entry). Without
|
|
24
|
+
* `uploadFiles`, a capture must answer a STORED image (the server engine).
|
|
25
|
+
*/
|
|
26
|
+
export declare function attachRenderedArtifact(store: AttachStore, conversationId: string | null | undefined, { source, representation, capture }: AttachRenderedArtifactArgs, uploadFiles?: (files: File[]) => Promise<void>): Promise<boolean>;
|
|
@@ -11,7 +11,7 @@ import { generateResourceId } from "../../../redux/execution-system/utils/ids.js
|
|
|
11
11
|
import {
|
|
12
12
|
RENDERED_ARTIFACT_BLOCK_TYPE
|
|
13
13
|
} from "../../../utils/renderedArtifactContext.js";
|
|
14
|
-
import {
|
|
14
|
+
import { attachResourceToConversation } from "./attach-resource.js";
|
|
15
15
|
import { useUploadAgentResources } from "./usePasteImageResource.js";
|
|
16
16
|
function screenshotName(title) {
|
|
17
17
|
const base = title.trim().replace(/[^\w\s.-]+/g, "").trim().slice(0, 60) || "page";
|
|
@@ -20,88 +20,98 @@ function screenshotName(title) {
|
|
|
20
20
|
function useAttachRenderedArtifact(conversationId) {
|
|
21
21
|
const dispatch = useAppDispatch();
|
|
22
22
|
const store = useAppStore();
|
|
23
|
-
const attachResource = useAttachResource(conversationId ?? "");
|
|
24
23
|
const uploadResources = useUploadAgentResources(conversationId ?? "", {
|
|
25
24
|
uploadRoot: "attachments",
|
|
26
25
|
uploadPath: "screenshots"
|
|
27
26
|
});
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
return (args) => attachRenderedArtifact(
|
|
28
|
+
{ dispatch, getState: store.getState },
|
|
29
|
+
conversationId,
|
|
30
|
+
args,
|
|
31
|
+
uploadResources
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
async function attachRenderedArtifact(store, conversationId, { source, representation, capture }, uploadFiles) {
|
|
35
|
+
const { dispatch } = store;
|
|
36
|
+
if (!conversationId) {
|
|
37
|
+
toast.error("Open a chat to attach this to.");
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (representation !== "screenshot") {
|
|
41
|
+
const resourceId = generateResourceId();
|
|
42
|
+
dispatch(
|
|
43
|
+
addResource({
|
|
44
|
+
conversationId,
|
|
45
|
+
blockType: RENDERED_ARTIFACT_BLOCK_TYPE,
|
|
46
|
+
resourceId,
|
|
47
|
+
source,
|
|
48
|
+
options: { representation }
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
dispatch(
|
|
52
|
+
setResourcePreview({
|
|
53
|
+
conversationId,
|
|
54
|
+
resourceId,
|
|
55
|
+
preview: `${source.title || "Page"} \xB7 ${representation === "code" ? "Code" : "Text"}`
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (!capture) {
|
|
61
|
+
toast.error("This item can't be captured as an image.");
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const name = screenshotName(source.title);
|
|
65
|
+
const placeholderId = generateResourceId();
|
|
66
|
+
dispatch(
|
|
67
|
+
addResource({
|
|
68
|
+
conversationId,
|
|
69
|
+
blockType: "image",
|
|
70
|
+
resourceId: placeholderId,
|
|
71
|
+
source: { filename: name, mime_type: "image/png" }
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
try {
|
|
75
|
+
const result = await capture();
|
|
76
|
+
const state = store.getState();
|
|
77
|
+
if (state.instanceResources.byConversationId[conversationId]?.[placeholderId] === void 0) {
|
|
31
78
|
return false;
|
|
32
79
|
}
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
dispatch(
|
|
36
|
-
|
|
37
|
-
conversationId,
|
|
38
|
-
blockType: RENDERED_ARTIFACT_BLOCK_TYPE,
|
|
39
|
-
resourceId,
|
|
40
|
-
source,
|
|
41
|
-
options: { representation }
|
|
42
|
-
})
|
|
43
|
-
);
|
|
44
|
-
dispatch(
|
|
45
|
-
setResourcePreview({
|
|
46
|
-
conversationId,
|
|
47
|
-
resourceId,
|
|
48
|
-
preview: `${source.title || "Page"} \xB7 ${representation === "code" ? "Code" : "Text"}`
|
|
49
|
-
})
|
|
50
|
-
);
|
|
80
|
+
if (result.kind === "file") {
|
|
81
|
+
if (!uploadFiles) throw new Error("This capture needs an upload, which is not available here");
|
|
82
|
+
dispatch(removeResource({ conversationId, resourceId: placeholderId }));
|
|
83
|
+
await uploadFiles([result.file]);
|
|
51
84
|
return true;
|
|
52
85
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
86
|
+
const attached = await attachResourceToConversation(store, conversationId, {
|
|
87
|
+
type: "file",
|
|
88
|
+
data: {
|
|
89
|
+
id: result.fileId,
|
|
90
|
+
filename: name,
|
|
91
|
+
url: result.url ?? void 0,
|
|
92
|
+
type: "image/png",
|
|
93
|
+
mime_type: "image/png",
|
|
94
|
+
details: { filename: name }
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
if (!attached) throw new Error("The screenshot could not be attached");
|
|
98
|
+
dispatch(removeResource({ conversationId, resourceId: placeholderId }));
|
|
99
|
+
return true;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const reason = err instanceof Error ? err.message : "Capture failed";
|
|
59
102
|
dispatch(
|
|
60
|
-
|
|
103
|
+
setResourceStatus({
|
|
61
104
|
conversationId,
|
|
62
|
-
blockType: "image",
|
|
63
105
|
resourceId: placeholderId,
|
|
64
|
-
|
|
106
|
+
status: "error",
|
|
107
|
+
errorMessage: reason
|
|
65
108
|
})
|
|
66
109
|
);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!stillAttached) return false;
|
|
71
|
-
if (result.kind === "file") {
|
|
72
|
-
dispatch(removeResource({ conversationId, resourceId: placeholderId }));
|
|
73
|
-
await uploadResources([result.file]);
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
const attached = await attachResource({
|
|
77
|
-
type: "file",
|
|
78
|
-
data: {
|
|
79
|
-
id: result.fileId,
|
|
80
|
-
filename: name,
|
|
81
|
-
url: result.url ?? void 0,
|
|
82
|
-
type: "image/png",
|
|
83
|
-
mime_type: "image/png",
|
|
84
|
-
details: { filename: name }
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
if (!attached) throw new Error("The screenshot could not be attached");
|
|
88
|
-
dispatch(removeResource({ conversationId, resourceId: placeholderId }));
|
|
89
|
-
return true;
|
|
90
|
-
} catch (err) {
|
|
91
|
-
const reason = err instanceof Error ? err.message : "Capture failed";
|
|
92
|
-
dispatch(
|
|
93
|
-
setResourceStatus({
|
|
94
|
-
conversationId,
|
|
95
|
-
resourceId: placeholderId,
|
|
96
|
-
status: "error",
|
|
97
|
-
errorMessage: reason
|
|
98
|
-
})
|
|
99
|
-
);
|
|
100
|
-
toast.error(`Couldn't capture the page: ${reason}`);
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
110
|
+
toast.error(`Couldn't capture the page: ${reason}`);
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
104
113
|
}
|
|
105
114
|
export {
|
|
115
|
+
attachRenderedArtifact,
|
|
106
116
|
useAttachRenderedArtifact
|
|
107
117
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.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. 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",
|
|
@@ -166,25 +166,25 @@
|
|
|
166
166
|
"server-only": "^0.0.1",
|
|
167
167
|
"uuid": "^14.0.2",
|
|
168
168
|
"zod": "^4.6.5",
|
|
169
|
+
"@ai-matrx/agents": "latest",
|
|
169
170
|
"@ai-matrx/alchemy": "latest",
|
|
170
171
|
"@ai-matrx/associations": "latest",
|
|
171
|
-
"@ai-matrx/browser-audio": "latest",
|
|
172
|
-
"@ai-matrx/agents": "latest",
|
|
173
172
|
"@ai-matrx/canvas": "latest",
|
|
174
173
|
"@ai-matrx/content-ir": "latest",
|
|
175
174
|
"@ai-matrx/content-ir-react": "latest",
|
|
176
175
|
"@ai-matrx/data": "latest",
|
|
176
|
+
"@ai-matrx/browser-audio": "latest",
|
|
177
177
|
"@ai-matrx/diff": "latest",
|
|
178
178
|
"@ai-matrx/icons": "latest",
|
|
179
|
-
"@ai-matrx/
|
|
179
|
+
"@ai-matrx/design-system": "latest",
|
|
180
|
+
"@ai-matrx/kit": "latest",
|
|
180
181
|
"@ai-matrx/print": "latest",
|
|
181
182
|
"@ai-matrx/realtime": "latest",
|
|
182
|
-
"@ai-matrx/
|
|
183
|
+
"@ai-matrx/media": "latest",
|
|
183
184
|
"@ai-matrx/records-ui": "latest",
|
|
184
|
-
"@ai-matrx/
|
|
185
|
+
"@ai-matrx/records": "latest",
|
|
185
186
|
"@ai-matrx/rich-editor": "latest",
|
|
186
|
-
"@ai-matrx/
|
|
187
|
-
"@ai-matrx/design-system": "latest"
|
|
187
|
+
"@ai-matrx/rich-content": "latest"
|
|
188
188
|
},
|
|
189
189
|
"devDependencies": {
|
|
190
190
|
"@arethetypeswrong/cli": "^0.18.5",
|