akanjs 3.0.0-beta.0 → 3.0.0-beta.2
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/dictionary/agent.dictionary.ts +8 -0
- package/dictionary/agentTurn.dictionary.ts +6 -2
- package/dictionary/base.dictionary.ts +1 -0
- package/fetch/agentTurn.ts +1 -1
- 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 +382 -0
- package/service/predefinedAdaptor/deepseekLlm.ts +21 -211
- package/service/predefinedAdaptor/index.ts +3 -0
- package/service/predefinedAdaptor/llm.adaptor.ts +25 -1
- package/service/predefinedAdaptor/openaiDialect.ts +246 -0
- package/service/predefinedAdaptor/openaiLlm.ts +91 -0
- package/signal/agentTurnStream.ts +5 -2
- package/types/dictionary/agent.dictionary.d.ts +1 -1
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +9 -9
- package/types/fetch/agentTurn.d.ts +3 -3
- package/types/service/agent.service.d.ts +1 -1
- package/types/service/predefinedAdaptor/anthropicLlm.d.ts +112 -0
- package/types/service/predefinedAdaptor/deepseekLlm.d.ts +10 -67
- package/types/service/predefinedAdaptor/index.d.ts +3 -0
- package/types/service/predefinedAdaptor/llm.adaptor.d.ts +25 -1
- package/types/service/predefinedAdaptor/openaiDialect.d.ts +96 -0
- package/types/service/predefinedAdaptor/openaiLlm.d.ts +24 -0
- package/types/signal/agent.signal.d.ts +1 -1
- package/types/signal/agentTurn.d.ts +1 -1
- package/types/signal/agentTurnStream.d.ts +1 -1
- package/types/ui/Agent/Attach.d.ts +4 -1
- package/types/ui/Agent/Composer.d.ts +3 -1
- package/types/ui/Agent/useChatAttachments.d.ts +1 -0
- package/types/vendor/use-agentic/types.d.ts +7 -1
- package/ui/Agent/Attach.tsx +13 -1
- package/ui/Agent/Chat.tsx +1 -0
- package/ui/Agent/Composer.tsx +11 -2
- package/ui/Agent/useChatAttachments.ts +6 -0
- package/vendor/use-agentic/AgentSession.ts +12 -2
- package/vendor/use-agentic/WIRE.md +6 -1
- package/vendor/use-agentic/httpRunner.ts +1 -1
- package/vendor/use-agentic/types.ts +8 -1
|
@@ -5,6 +5,8 @@ export interface ComposerProps {
|
|
|
5
5
|
session: AgentSession;
|
|
6
6
|
draft: string;
|
|
7
7
|
attached: readonly MessageAttachment[];
|
|
8
|
+
/** Files still being read. An `attach` that uploads takes seconds, and a panel that shows nothing looks broken. */
|
|
9
|
+
pending?: number;
|
|
8
10
|
/** Absent when the screen cannot listen — the same rule as publishing no tool for a control that is not drawn. */
|
|
9
11
|
mic?: {
|
|
10
12
|
listening: boolean;
|
|
@@ -19,5 +21,5 @@ export interface ComposerProps {
|
|
|
19
21
|
inputRef?: RefObject<HTMLTextAreaElement | null>;
|
|
20
22
|
}
|
|
21
23
|
/** What the user writes with: the staged files above, and the controls that send or stop below them. */
|
|
22
|
-
export declare const DefaultComposer: ({ className, session, draft, attached, mic, onDraft, onKeyDown, onFiles, onRemoveFile, onSend, onStop, inputRef, }: ComposerProps) => import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export declare const DefaultComposer: ({ className, session, draft, attached, pending, mic, onDraft, onKeyDown, onFiles, onRemoveFile, onSend, onStop, inputRef, }: ComposerProps) => import("react/jsx-runtime").JSX.Element;
|
|
23
25
|
export declare const Composer: import("react").ComponentType<ComposerProps>;
|
|
@@ -14,6 +14,7 @@ interface ChatAttachmentsSetup {
|
|
|
14
14
|
*/
|
|
15
15
|
export declare const useChatAttachments: ({ session, attach, limits, l }: ChatAttachmentsSetup) => {
|
|
16
16
|
attached: MessageAttachment[];
|
|
17
|
+
pending: number;
|
|
17
18
|
dragging: boolean;
|
|
18
19
|
dropProps: {
|
|
19
20
|
[key: string]: DragEventHandler<HTMLElement>;
|
|
@@ -114,6 +114,12 @@ export interface ToolCallResult {
|
|
|
114
114
|
* model cannot read and says so in the transcript, because a silently dropped file is one the model then
|
|
115
115
|
* hallucinates about.
|
|
116
116
|
*/
|
|
117
|
+
/**
|
|
118
|
+
* Why an assistant turn ended. `length` is the provider's own ceiling rather than the model's choice, so the turn
|
|
119
|
+
* is incomplete — a truncated answer and a turn cut off before its tool call finished both arrive this way, and
|
|
120
|
+
* neither is distinguishable from `end` without it.
|
|
121
|
+
*/
|
|
122
|
+
export type TurnStop = "end" | "toolUse" | "length";
|
|
117
123
|
export interface MessageAttachment {
|
|
118
124
|
name: string;
|
|
119
125
|
mimeType: string;
|
|
@@ -165,7 +171,7 @@ export type RunnerEvent = {
|
|
|
165
171
|
args: Record<string, unknown>;
|
|
166
172
|
} | {
|
|
167
173
|
type: "done";
|
|
168
|
-
stop:
|
|
174
|
+
stop: TurnStop;
|
|
169
175
|
}
|
|
170
176
|
/**
|
|
171
177
|
* `data` accompanies a message that is a code rather than a sentence — the values whoever resolves the code
|
package/ui/Agent/Attach.tsx
CHANGED
|
@@ -44,9 +44,12 @@ interface ChipsProps {
|
|
|
44
44
|
/** Omitted for a sent message: what is already on the wire cannot be taken back. */
|
|
45
45
|
onRemove?: (index: number) => void;
|
|
46
46
|
removeLabel?: string;
|
|
47
|
+
/** Files still being read, standing beside the ones that are staged so the panel is never blank mid-drop. */
|
|
48
|
+
pending?: number;
|
|
49
|
+
pendingLabel?: string;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
export const Chips = ({ className, attachments, onRemove, removeLabel }: ChipsProps) => (
|
|
52
|
+
export const Chips = ({ className, attachments, onRemove, removeLabel, pending = 0, pendingLabel }: ChipsProps) => (
|
|
50
53
|
<div className={cn("flex flex-wrap gap-1", className)}>
|
|
51
54
|
{attachments.map((attachment, idx) => (
|
|
52
55
|
<span
|
|
@@ -73,5 +76,14 @@ export const Chips = ({ className, attachments, onRemove, removeLabel }: ChipsPr
|
|
|
73
76
|
) : null}
|
|
74
77
|
</span>
|
|
75
78
|
))}
|
|
79
|
+
{Array.from({ length: pending }, (_, idx) => (
|
|
80
|
+
<span
|
|
81
|
+
className="flex animate-pulse items-center gap-1 rounded-field bg-muted px-2 py-0.5 text-foreground/50 text-xs"
|
|
82
|
+
key={`pending-${idx}`}
|
|
83
|
+
>
|
|
84
|
+
<AiOutlinePaperClip />
|
|
85
|
+
<span className="max-w-32 truncate">{pendingLabel}</span>
|
|
86
|
+
</span>
|
|
87
|
+
))}
|
|
76
88
|
</div>
|
|
77
89
|
);
|
package/ui/Agent/Chat.tsx
CHANGED
|
@@ -498,6 +498,7 @@ export const DefaultChat = ({
|
|
|
498
498
|
<Menu onPick={(row) => row.pick()} rows={menu.rows} selected={menu.selected} />
|
|
499
499
|
<Composer
|
|
500
500
|
attached={files.attached}
|
|
501
|
+
pending={files.pending}
|
|
501
502
|
draft={draft}
|
|
502
503
|
inputRef={inputRef}
|
|
503
504
|
{...(speech.canListen ? { mic: { listening: speech.listening, onToggle: speech.toggle } } : {})}
|
package/ui/Agent/Composer.tsx
CHANGED
|
@@ -13,6 +13,8 @@ export interface ComposerProps {
|
|
|
13
13
|
session: AgentSession;
|
|
14
14
|
draft: string;
|
|
15
15
|
attached: readonly MessageAttachment[];
|
|
16
|
+
/** Files still being read. An `attach` that uploads takes seconds, and a panel that shows nothing looks broken. */
|
|
17
|
+
pending?: number;
|
|
16
18
|
/** Absent when the screen cannot listen — the same rule as publishing no tool for a control that is not drawn. */
|
|
17
19
|
mic?: { listening: boolean; onToggle: () => void };
|
|
18
20
|
onDraft: (text: string) => void;
|
|
@@ -33,6 +35,7 @@ export const DefaultComposer = ({
|
|
|
33
35
|
session,
|
|
34
36
|
draft,
|
|
35
37
|
attached,
|
|
38
|
+
pending = 0,
|
|
36
39
|
mic,
|
|
37
40
|
onDraft,
|
|
38
41
|
onKeyDown,
|
|
@@ -53,8 +56,14 @@ export const DefaultComposer = ({
|
|
|
53
56
|
}, [draft, inputRef]);
|
|
54
57
|
return (
|
|
55
58
|
<div className={cn("flex flex-col gap-2 border-foreground/5 border-t p-3", className)}>
|
|
56
|
-
{attached.length ? (
|
|
57
|
-
<Chips
|
|
59
|
+
{attached.length || pending ? (
|
|
60
|
+
<Chips
|
|
61
|
+
attachments={attached}
|
|
62
|
+
onRemove={onRemoveFile}
|
|
63
|
+
pending={pending}
|
|
64
|
+
pendingLabel={l("base.agentAttachReading")}
|
|
65
|
+
removeLabel={l("base.agentAttachRemove")}
|
|
66
|
+
/>
|
|
58
67
|
) : null}
|
|
59
68
|
<div className="flex items-end gap-2">
|
|
60
69
|
{mic ? (
|
|
@@ -25,6 +25,8 @@ export const useChatAttachments = ({ session, attach, limits = {}, l }: ChatAtta
|
|
|
25
25
|
const count = limits.perMessageCount ?? maxMessageAttachments;
|
|
26
26
|
const messageBytes = limits.perMessageBytes ?? maxMessageAttachmentBytes;
|
|
27
27
|
const [attached, setAttached] = useState<MessageAttachment[]>([]);
|
|
28
|
+
|
|
29
|
+
const [pending, setPending] = useState(0);
|
|
28
30
|
const [dragging, setDragging] = useState(false);
|
|
29
31
|
const staged = useRef<MessageAttachment[]>([]);
|
|
30
32
|
|
|
@@ -45,6 +47,7 @@ export const useChatAttachments = ({ session, attach, limits = {}, l }: ChatAtta
|
|
|
45
47
|
session.note(l("base.agentAttachTooMany", { count }));
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
50
|
+
setPending((waiting) => waiting + 1);
|
|
48
51
|
try {
|
|
49
52
|
const read = await Attachment.read(file, attach, limits);
|
|
50
53
|
if (Attachment.failure(read))
|
|
@@ -58,6 +61,8 @@ export const useChatAttachments = ({ session, attach, limits = {}, l }: ChatAtta
|
|
|
58
61
|
else stage([...staged.current, read]);
|
|
59
62
|
} catch (error) {
|
|
60
63
|
session.report(`${file.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
64
|
+
} finally {
|
|
65
|
+
setPending((waiting) => Math.max(0, waiting - 1));
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
};
|
|
@@ -94,6 +99,7 @@ export const useChatAttachments = ({ session, attach, limits = {}, l }: ChatAtta
|
|
|
94
99
|
};
|
|
95
100
|
return {
|
|
96
101
|
attached,
|
|
102
|
+
pending,
|
|
97
103
|
dragging,
|
|
98
104
|
dropProps,
|
|
99
105
|
add,
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
SurfaceView,
|
|
13
13
|
ToolCallRequest,
|
|
14
14
|
ToolCallResult,
|
|
15
|
+
TurnStop,
|
|
15
16
|
} from "./types";
|
|
16
17
|
|
|
17
18
|
export interface PendingApproval {
|
|
@@ -264,6 +265,15 @@ export class AgentSession {
|
|
|
264
265
|
this.#unanswered(toolCalls);
|
|
265
266
|
return;
|
|
266
267
|
}
|
|
268
|
+
|
|
269
|
+
if (stop === "length") {
|
|
270
|
+
|
|
271
|
+
this.#fail(
|
|
272
|
+
"The model ran out of room mid-answer, so this turn is incomplete. Ask again, or raise the answer limit.",
|
|
273
|
+
);
|
|
274
|
+
if (toolCalls.length) this.#unanswered(toolCalls);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
267
277
|
if (stop !== "toolUse" || !toolCalls.length) return;
|
|
268
278
|
const toolResults: ToolCallResult[] = [];
|
|
269
279
|
for (const call of toolCalls)
|
|
@@ -478,7 +488,7 @@ export class AgentSession {
|
|
|
478
488
|
return text;
|
|
479
489
|
}
|
|
480
490
|
|
|
481
|
-
async #assistantTurn(signal: AbortSignal): Promise<{ toolCalls: ToolCallRequest[]; stop:
|
|
491
|
+
async #assistantTurn(signal: AbortSignal): Promise<{ toolCalls: ToolCallRequest[]; stop: TurnStop }> {
|
|
482
492
|
const { tools, guides } = this.#surface.snapshot();
|
|
483
493
|
const instructions = [this.#options.instructions, ...guides].filter(Boolean).join("\n\n");
|
|
484
494
|
const request: RunnerRequest = {
|
|
@@ -493,7 +503,7 @@ export class AgentSession {
|
|
|
493
503
|
this.#append({ role: "assistant" });
|
|
494
504
|
let text = "";
|
|
495
505
|
const toolCalls: ToolCallRequest[] = [];
|
|
496
|
-
let stop:
|
|
506
|
+
let stop: TurnStop = "end";
|
|
497
507
|
for await (const event of this.#runner.run(request)) {
|
|
498
508
|
if (signal.aborted) break;
|
|
499
509
|
if (event.type === "text") {
|
|
@@ -44,7 +44,7 @@ tools, and the loop stay in the client; the server serves exactly one stateless
|
|
|
44
44
|
## Attachments
|
|
45
45
|
|
|
46
46
|
A message may carry files in `attachments` (`MessageAttachment`, `types.ts`) — one of `data` (base64 bytes), `url`,
|
|
47
|
-
or `text` (already-extracted content), plus `name` and `mimeType`:
|
|
47
|
+
or `text` (already-extracted content), plus `name` and `mimeType`, and an optional `ref`:
|
|
48
48
|
|
|
49
49
|
```jsonc
|
|
50
50
|
{ "role": "user", "text": "What does this chart say?",
|
|
@@ -57,6 +57,11 @@ it with a note in the message text saying which file was not read and why, so th
|
|
|
57
57
|
another form. `text` is readable by every provider by definition, which is what makes an extracted PDF work against
|
|
58
58
|
a text-only model.
|
|
59
59
|
|
|
60
|
+
`ref` is the host's own handle on the file — a file id, a storage key — opaque to everything in between. A backend
|
|
61
|
+
carries it and never reads it; it is not shown to the model, and a tool the host published is what spends it.
|
|
62
|
+
Without one, a host that stores its uploads has to identify a file by name and size, which collides for two crops
|
|
63
|
+
of one export.
|
|
64
|
+
|
|
60
65
|
Nothing here is stored: the wire carries the bytes for exactly one turn's request.
|
|
61
66
|
|
|
62
67
|
## Response
|
|
@@ -10,7 +10,7 @@ export interface HttpRunnerOptions {
|
|
|
10
10
|
interface TurnAnswer {
|
|
11
11
|
text?: string;
|
|
12
12
|
toolCalls?: ToolCallRequest[];
|
|
13
|
-
stop?: "end" | "toolUse";
|
|
13
|
+
stop?: "end" | "toolUse" | "length";
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const eventTypes = new Set(["text", "toolCall", "done", "error"]);
|
|
@@ -131,6 +131,13 @@ export interface ToolCallResult {
|
|
|
131
131
|
* model cannot read and says so in the transcript, because a silently dropped file is one the model then
|
|
132
132
|
* hallucinates about.
|
|
133
133
|
*/
|
|
134
|
+
/**
|
|
135
|
+
* Why an assistant turn ended. `length` is the provider's own ceiling rather than the model's choice, so the turn
|
|
136
|
+
* is incomplete — a truncated answer and a turn cut off before its tool call finished both arrive this way, and
|
|
137
|
+
* neither is distinguishable from `end` without it.
|
|
138
|
+
*/
|
|
139
|
+
export type TurnStop = "end" | "toolUse" | "length";
|
|
140
|
+
|
|
134
141
|
export interface MessageAttachment {
|
|
135
142
|
name: string;
|
|
136
143
|
mimeType: string;
|
|
@@ -178,7 +185,7 @@ export interface ContextBlock {
|
|
|
178
185
|
export type RunnerEvent =
|
|
179
186
|
| { type: "text"; delta: string }
|
|
180
187
|
| { type: "toolCall"; id: string; name: string; args: Record<string, unknown> }
|
|
181
|
-
| { type: "done"; stop:
|
|
188
|
+
| { type: "done"; stop: TurnStop }
|
|
182
189
|
/**
|
|
183
190
|
* `data` accompanies a message that is a code rather than a sentence — the values whoever resolves the code
|
|
184
191
|
* interpolates into its text. A host that does not know the code shows the message as it stands.
|