akanjs 3.0.0-beta.5 → 3.0.0-beta.7
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/fetch/client/wsClient.ts +4 -4
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/package.json +1 -1
- package/service/predefinedAdaptor/anthropicLlm.ts +7 -1
- package/service/predefinedAdaptor/openaiDialect.ts +3 -2
- package/types/service/predefinedAdaptor/anthropicLlm.d.ts +6 -0
- package/types/service/predefinedAdaptor/openaiDialect.d.ts +1 -1
- package/types/ui/Agent/Attach.d.ts +1 -1
- package/types/ui/Agent/Chat.d.ts +4 -3
- package/types/ui/index.d.ts +1 -0
- package/ui/Agent/Attach.tsx +3 -3
- package/ui/Agent/Chat.tsx +4 -3
- package/ui/Agent/sessionHistory.ts +5 -2
- package/ui/index.ts +1 -0
- package/vendor/use-agentic/Compaction.ts +3 -1
package/fetch/client/wsClient.ts
CHANGED
|
@@ -171,13 +171,13 @@ export class WsClient {
|
|
|
171
171
|
break;
|
|
172
172
|
}
|
|
173
173
|
} catch (error) {
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
175
|
+
this.logger.warn(`WebSocket message process failed ${errMsg}`);
|
|
176
176
|
}
|
|
177
177
|
};
|
|
178
178
|
this.#ws.onerror = (e) => {
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
180
|
+
this.logger.verbose(`WebSocket error ${errMsg}`);
|
|
181
181
|
};
|
|
182
182
|
this.#ws.onclose = (event) => {
|
|
183
183
|
this.logger.debug(`WebSocket closed: ${event.code} ${event.reason}`);
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -297,9 +297,15 @@ export class AnthropicLlm
|
|
|
297
297
|
return source.type === "base64" ? { ...source, media_type: mimeType } : source;
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Bytes beat an address when a host sent both: it already paid for them on the way in, and the address it also
|
|
302
|
+
* sent is the one it renders — which the default storage backend serves on a path only the app can resolve.
|
|
303
|
+
* Picking that costs a confident answer about a picture nothing fetched; picking the bytes costs one hop that
|
|
304
|
+
* already carries them. A URL the provider really can reach travels alone.
|
|
305
|
+
*/
|
|
300
306
|
static sourceOf(attachment: AgentWireAttachment): AnthropicSource | null {
|
|
301
|
-
if (attachment.url) return { type: "url", url: attachment.url };
|
|
302
307
|
if (attachment.data) return { type: "base64", media_type: attachment.mimeType, data: attachment.data };
|
|
308
|
+
if (attachment.url) return { type: "url", url: attachment.url };
|
|
303
309
|
return null;
|
|
304
310
|
}
|
|
305
311
|
|
|
@@ -122,7 +122,7 @@ export class OpenaiDialect {
|
|
|
122
122
|
* Text attachments are labelled into the message, because a model handed two unlabelled documents can no longer
|
|
123
123
|
* cite either one. Images become their own parts only when the provider said it reads them; the dialect carries
|
|
124
124
|
* one as a `data:` URL, which is the same encoding whether the bytes were inlined or already addressable, so
|
|
125
|
-
* both carriers take one branch.
|
|
125
|
+
* both carriers take one branch — the inlined bytes first, for the reason named at the branch.
|
|
126
126
|
*/
|
|
127
127
|
static userContent(message: AgentWireMessage, accepts?: LlmAccepts): string | OpenaiContentPart[] {
|
|
128
128
|
const attachments = message.attachments ?? [];
|
|
@@ -144,7 +144,8 @@ export class OpenaiDialect {
|
|
|
144
144
|
);
|
|
145
145
|
return [];
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
|
|
148
|
+
const url = attachment.data ? `data:${mimeType};base64,${attachment.data}` : (attachment.url ?? "");
|
|
148
149
|
return url ? [{ type: "image_url" as const, image_url: { url } }] : [];
|
|
149
150
|
});
|
|
150
151
|
const text = [message.text, ...blocks, ...notes].filter(Boolean).join("\n\n");
|
|
@@ -109,6 +109,12 @@ export declare class AnthropicLlm extends AnthropicLlm_base implements LlmAdapto
|
|
|
109
109
|
static userContent(message: AgentWireMessage, accepts?: LlmAccepts): AnthropicBlock[];
|
|
110
110
|
/** The block's `media_type` is the essence, not whatever parameters the browser attached to it. */
|
|
111
111
|
static typed(source: AnthropicSource, mimeType: string): AnthropicSource;
|
|
112
|
+
/**
|
|
113
|
+
* Bytes beat an address when a host sent both: it already paid for them on the way in, and the address it also
|
|
114
|
+
* sent is the one it renders — which the default storage backend serves on a path only the app can resolve.
|
|
115
|
+
* Picking that costs a confident answer about a picture nothing fetched; picking the bytes costs one hop that
|
|
116
|
+
* already carries them. A URL the provider really can reach travels alone.
|
|
117
|
+
*/
|
|
112
118
|
static sourceOf(attachment: AgentWireAttachment): AnthropicSource | null;
|
|
113
119
|
static turnAnswer(answer: AnthropicAnswer): LlmTurnAnswer;
|
|
114
120
|
/** The ceiling wins over the calls that did arrive — see `OpenaiDialect.stopOf` for why. */
|
|
@@ -77,7 +77,7 @@ export declare class OpenaiDialect {
|
|
|
77
77
|
* Text attachments are labelled into the message, because a model handed two unlabelled documents can no longer
|
|
78
78
|
* cite either one. Images become their own parts only when the provider said it reads them; the dialect carries
|
|
79
79
|
* one as a `data:` URL, which is the same encoding whether the bytes were inlined or already addressable, so
|
|
80
|
-
* both carriers take one branch.
|
|
80
|
+
* both carriers take one branch — the inlined bytes first, for the reason named at the branch.
|
|
81
81
|
*/
|
|
82
82
|
static userContent(message: AgentWireMessage, accepts?: LlmAccepts): string | OpenaiContentPart[];
|
|
83
83
|
/**
|
|
@@ -5,7 +5,7 @@ interface AttachProps {
|
|
|
5
5
|
onPick: (files: File[]) => void;
|
|
6
6
|
}
|
|
7
7
|
export declare const Attach: ({ className, label, onPick }: AttachProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
interface ChipsProps {
|
|
8
|
+
export interface ChipsProps {
|
|
9
9
|
className?: string;
|
|
10
10
|
attachments: readonly MessageAttachment[];
|
|
11
11
|
/** Omitted for a sent message: what is already on the wire cannot be taken back. */
|
package/types/ui/Agent/Chat.d.ts
CHANGED
|
@@ -72,9 +72,10 @@ export interface ChatProps {
|
|
|
72
72
|
* the built-in, so it can also replace how an image is prepared.
|
|
73
73
|
*
|
|
74
74
|
* **A `url` is handed to the provider as the address it will fetch**, so answer `data` whenever the provider
|
|
75
|
-
* cannot reach it
|
|
76
|
-
*
|
|
77
|
-
*
|
|
75
|
+
* cannot reach it — the default storage backend serves a path only this app can resolve, and a model handed one
|
|
76
|
+
* answers about a picture it never saw with nothing anywhere reporting a failure. Answering **both** is the
|
|
77
|
+
* shape for that case: bytes are what the provider is given, and the address is what the chip draws, so an
|
|
78
|
+
* uploading reader gets a thumbnail without betting the answer on who can reach its storage.
|
|
78
79
|
*/
|
|
79
80
|
attach?: AttachReader;
|
|
80
81
|
/**
|
package/types/ui/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { AgentProvider, type AgentProviderProps, type AgentRunner, AgentSession, type AgentSessionOptions, type ChatMessage, type CompactOptions, type ContextBlock, httpRunner, type MessageAttachment, type PublishedTool, type RunnerEvent, type RunnerRequest, SessionContext, type SessionHistory, type SurfaceView, useAgent, } from "../vendor/use-agentic.d.ts";
|
|
2
2
|
export { Agent } from "./Agent.d.ts";
|
|
3
3
|
export { type ApprovalProps, DefaultApproval } from "./Agent/Approval.d.ts";
|
|
4
|
+
export { Chips as AgentAttachments, type ChipsProps as AgentAttachmentsProps } from "./Agent/Attach.d.ts";
|
|
4
5
|
export { type AgentSessionSetup, agentSessionOf } from "./Agent/agentSessionOf.d.ts";
|
|
5
6
|
export { type AttachLimits, type AttachReader, maxAttachmentBytes, maxMessageAttachmentBytes, maxMessageAttachments, } from "./Agent/attachment.d.ts";
|
|
6
7
|
export { type BubbleProps, DefaultBubble } from "./Agent/Bubble.d.ts";
|
package/ui/Agent/Attach.tsx
CHANGED
|
@@ -38,7 +38,7 @@ export const Attach = ({ className, label, onPick }: AttachProps) => {
|
|
|
38
38
|
);
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
interface ChipsProps {
|
|
41
|
+
export interface ChipsProps {
|
|
42
42
|
className?: string;
|
|
43
43
|
|
|
44
44
|
attachments: readonly MessageAttachment[];
|
|
@@ -57,11 +57,11 @@ export const Chips = ({ className, attachments, onRemove, removeLabel, pending =
|
|
|
57
57
|
className="flex items-center gap-1 rounded-field bg-muted px-2 py-0.5 text-xs"
|
|
58
58
|
key={`${attachment.name}-${idx}`}
|
|
59
59
|
>
|
|
60
|
-
{attachment.data && attachment.mimeType?.startsWith("image/") ? (
|
|
60
|
+
{(attachment.data || attachment.url) && attachment.mimeType?.startsWith("image/") ? (
|
|
61
61
|
<img
|
|
62
62
|
alt={attachment.name}
|
|
63
63
|
className="size-6 rounded-field object-cover"
|
|
64
|
-
src={`data:${attachment.mimeType};base64,${attachment.data}`}
|
|
64
|
+
src={attachment.data ? `data:${attachment.mimeType};base64,${attachment.data}` : attachment.url}
|
|
65
65
|
/>
|
|
66
66
|
) : null}
|
|
67
67
|
<span className="max-w-32 truncate">{attachment.name}</span>
|
package/ui/Agent/Chat.tsx
CHANGED
|
@@ -111,9 +111,10 @@ export interface ChatProps {
|
|
|
111
111
|
* the built-in, so it can also replace how an image is prepared.
|
|
112
112
|
*
|
|
113
113
|
* **A `url` is handed to the provider as the address it will fetch**, so answer `data` whenever the provider
|
|
114
|
-
* cannot reach it
|
|
115
|
-
*
|
|
116
|
-
*
|
|
114
|
+
* cannot reach it — the default storage backend serves a path only this app can resolve, and a model handed one
|
|
115
|
+
* answers about a picture it never saw with nothing anywhere reporting a failure. Answering **both** is the
|
|
116
|
+
* shape for that case: bytes are what the provider is given, and the address is what the chip draws, so an
|
|
117
|
+
* uploading reader gets a thumbnail without betting the answer on who can reach its storage.
|
|
117
118
|
*/
|
|
118
119
|
attach?: AttachReader;
|
|
119
120
|
/**
|
|
@@ -8,16 +8,19 @@ export type PersistOption = boolean | { storage?: "session" | "local"; key?: str
|
|
|
8
8
|
* chunk of it, and `AgentSession` swallows a failed save — so persisting the bytes would quietly stop persisting
|
|
9
9
|
* the transcript itself. The name and type stay so a restored conversation still reads as what happened, and a
|
|
10
10
|
* `url` stays because a pointer is not content; the server then tells the model the content is gone rather than
|
|
11
|
-
* letting it answer from the filename.
|
|
11
|
+
* letting it answer from the filename. A `ref` stays for the same reason and matters more: it is the host's own
|
|
12
|
+
* handle on the file, so dropping it leaves a restored conversation with nothing but a name and size to guess
|
|
13
|
+
* from — the guess `ref` exists to retire.
|
|
12
14
|
*/
|
|
13
15
|
const withoutContent = (message: ChatMessage): ChatMessage =>
|
|
14
16
|
message.attachments?.length
|
|
15
17
|
? {
|
|
16
18
|
...message,
|
|
17
|
-
attachments: message.attachments.map(({ name, mimeType, url }) => ({
|
|
19
|
+
attachments: message.attachments.map(({ name, mimeType, url, ref }) => ({
|
|
18
20
|
name,
|
|
19
21
|
mimeType,
|
|
20
22
|
...(url ? { url } : {}),
|
|
23
|
+
...(ref ? { ref } : {}),
|
|
21
24
|
})),
|
|
22
25
|
}
|
|
23
26
|
: message;
|
package/ui/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export {
|
|
|
21
21
|
} from "../vendor/use-agentic";
|
|
22
22
|
export { Agent } from "./Agent";
|
|
23
23
|
export { type ApprovalProps, DefaultApproval } from "./Agent/Approval";
|
|
24
|
+
export { Chips as AgentAttachments, type ChipsProps as AgentAttachmentsProps } from "./Agent/Attach";
|
|
24
25
|
export { type AgentSessionSetup, agentSessionOf } from "./Agent/agentSessionOf";
|
|
25
26
|
export {
|
|
26
27
|
type AttachLimits,
|
|
@@ -100,7 +100,9 @@ export class Compaction {
|
|
|
100
100
|
static #line(message: ChatMessage): string {
|
|
101
101
|
const parts: string[] = [];
|
|
102
102
|
if (message.text) parts.push(Compaction.#clip(message.text, 1200));
|
|
103
|
-
|
|
103
|
+
|
|
104
|
+
for (const attachment of message.attachments ?? [])
|
|
105
|
+
parts.push(`[attached ${attachment.name}, content not carried into this summary]`);
|
|
104
106
|
for (const call of message.toolCalls ?? [])
|
|
105
107
|
parts.push(`[called ${call.name} ${Compaction.#clip(JSON.stringify(call.args), 200)}]`);
|
|
106
108
|
for (const result of message.toolResults ?? [])
|