akanjs 3.0.0-alpha.37 → 3.0.0-alpha.39

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.
Files changed (43) hide show
  1. package/dictionary/base.dictionary.ts +4 -0
  2. package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
  3. package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
  4. package/package.json +1 -1
  5. package/service/agent.service.ts +48 -2
  6. package/service/predefinedAdaptor/deepseekLlm.ts +13 -1
  7. package/service/predefinedAdaptor/llm.adaptor.ts +31 -0
  8. package/store/agent/AgentPrompts.ts +20 -2
  9. package/store/agent/ScreenReader.ts +14 -4
  10. package/store/agentic/useFieldTool.ts +60 -4
  11. package/store/agentic/useFormTools.ts +13 -2
  12. package/store/formSetterNames.ts +5 -0
  13. package/types/dictionary/base.dictionary.d.ts +1 -1
  14. package/types/dictionary/dictionary.d.ts +8 -8
  15. package/types/service/agent.service.d.ts +13 -1
  16. package/types/service/predefinedAdaptor/deepseekLlm.d.ts +6 -0
  17. package/types/service/predefinedAdaptor/llm.adaptor.d.ts +29 -0
  18. package/types/store/agentic/useFieldTool.d.ts +14 -2
  19. package/types/store/agentic/useFormTools.d.ts +5 -0
  20. package/types/store/formSetterNames.d.ts +5 -0
  21. package/types/ui/Agent/Attach.d.ts +16 -0
  22. package/types/ui/Agent/Chat.d.ts +9 -1
  23. package/types/ui/Agent/attachment.d.ts +23 -0
  24. package/types/ui/Dropdown.d.ts +3 -1
  25. package/types/ui/Field.d.ts +1 -0
  26. package/types/ui/index.d.ts +1 -0
  27. package/types/vendor/use-agentic/types.d.ts +23 -0
  28. package/ui/Agent/Attach.tsx +77 -0
  29. package/ui/Agent/Bubble.tsx +6 -7
  30. package/ui/Agent/Chat.tsx +84 -32
  31. package/ui/Agent/ChatCommands.ts +2 -0
  32. package/ui/Agent/attachment.ts +53 -0
  33. package/ui/Agent/sessionHistory.ts +20 -1
  34. package/ui/DraggableList.tsx +5 -1
  35. package/ui/Dropdown.tsx +37 -7
  36. package/ui/Field.tsx +41 -19
  37. package/ui/Input.tsx +6 -6
  38. package/ui/Select.tsx +1 -1
  39. package/ui/Switch.tsx +1 -1
  40. package/ui/index.ts +1 -0
  41. package/vendor/use-agentic/AgentSession.ts +3 -1
  42. package/vendor/use-agentic/WIRE.md +18 -0
  43. package/vendor/use-agentic/types.ts +24 -0
@@ -54,6 +54,10 @@ export const baseDictionary = serviceDictionary(["en", "ko"])
54
54
  agentClear: ["Clear conversation", "대화 비우기"],
55
55
  agentQuestion: ["The agent needs your decision", "에이전트가 결정을 요청합니다"],
56
56
  agentAnswer: ["Type your answer...", "답변을 입력하세요..."],
57
+ agentAttach: ["Attach a file", "파일 첨부"],
58
+ agentAttachRemove: ["Remove attachment", "첨부 제거"],
59
+ agentAttachTooLarge: ["{name} is too large to attach.", "{name}은(는) 용량이 너무 커서 첨부할 수 없습니다."],
60
+ agentAttachUnsupported: ["{name} cannot be attached here.", "{name}은(는) 여기에 첨부할 수 없습니다."],
57
61
  agentContinue: ["This is taking a while. Keep going?", "시간이 걸리고 있습니다. 계속할까요?"],
58
62
  agentKeepGoing: ["Keep going", "계속하기"],
59
63
  agentCmdNew: ["Start a new conversation", "새 대화 시작"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "3.0.0-alpha.37",
3
+ "version": "3.0.0-alpha.39",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -1,5 +1,10 @@
1
1
  import { Err } from "akanjs/dictionary";
2
- import type { LlmTurnRequest } from "./predefinedAdaptor/llm.adaptor";
2
+ import type {
3
+ AgentWireAttachment,
4
+ AgentWireMessage,
5
+ LlmAccepts,
6
+ LlmTurnRequest,
7
+ } from "./predefinedAdaptor/llm.adaptor";
3
8
  import { LlmAdaptorRole } from "./predefinedAdaptor/role.adaptor";
4
9
  import { serve } from "./serve";
5
10
 
@@ -7,8 +12,49 @@ export class AgentService extends serve("agent" as const, ({ plug }) => ({
7
12
  llm: plug(LlmAdaptorRole),
8
13
  })) {
9
14
  async runTurn(request: LlmTurnRequest, onDelta?: (delta: string) => void) {
10
- const answer = await this.llm.chat(request, onDelta);
15
+ const answer = await this.llm.chat(AgentService.readable(request, this.llm.accepts), onDelta);
11
16
  if (!answer) throw new Err("agent.error.llmUnavailable");
12
17
  return { text: answer.text ?? "", toolCalls: answer.toolCalls ?? [], stop: answer.stop };
13
18
  }
19
+
20
+ /**
21
+ * Replaces every attachment the provider cannot read with a note naming it, so no adaptor has to think about
22
+ * attachments it does not support and none can lose one quietly. The model has to be *told*, not merely spared:
23
+ * a file that vanishes on the way in is one it answers about from the filename, confidently and wrongly.
24
+ *
25
+ * The note rides in the message text because that is the one field every provider mapping already reads.
26
+ */
27
+ static readable(request: LlmTurnRequest, accepts: LlmAccepts | undefined): LlmTurnRequest {
28
+ if (!request.messages.some((message) => message.attachments?.length)) return request;
29
+ const messages = request.messages.map((message) => AgentService.readableMessage(message, accepts ?? {}));
30
+ return { ...request, messages };
31
+ }
32
+
33
+ private static readableMessage(message: AgentWireMessage, accepts: LlmAccepts): AgentWireMessage {
34
+ const { attachments = [], ...rest } = message;
35
+ if (!attachments.length) return message;
36
+ const kept = attachments.filter((attachment) => AgentService.isReadable(attachment, accepts));
37
+ if (kept.length === attachments.length) return message;
38
+ const notes = attachments.filter((attachment) => !kept.includes(attachment)).map(AgentService.note);
39
+ return {
40
+ ...rest,
41
+ ...(kept.length ? { attachments: kept } : {}),
42
+ text: [message.text, ...notes].filter(Boolean).join("\n\n"),
43
+ };
44
+ }
45
+
46
+ /** Extracted text is readable by every model there is; bytes and links need the provider to say so. */
47
+ private static isReadable(attachment: AgentWireAttachment, accepts: LlmAccepts): boolean {
48
+ if (attachment.text) return true;
49
+ if (!attachment.data && !attachment.url) return false;
50
+ return attachment.mimeType.startsWith("image/") ? !!accepts.image : !!accepts.document;
51
+ }
52
+
53
+ private static note(attachment: AgentWireAttachment): string {
54
+ const why =
55
+ attachment.data || attachment.url
56
+ ? "this model cannot read that type"
57
+ : "its content is no longer available, as a reloaded conversation keeps the name and not the bytes";
58
+ return `[Attachment not read: ${attachment.name} (${attachment.mimeType}) — ${why}. Tell the user it was not read instead of guessing what it holds, and ask for the text if the answer needs it.]`;
59
+ }
14
60
  }
@@ -201,7 +201,19 @@ export class DeepseekLlm
201
201
  : {}),
202
202
  },
203
203
  ];
204
- return [{ role: "user" as const, content: message.text ?? "" }];
204
+ return [{ role: "user" as const, content: DeepseekLlm.userContent(message) }];
205
+ }
206
+
207
+ /**
208
+ * `accepts` is left undeclared, so by the time an attachment reaches here `AgentService.readable` has reduced it
209
+ * to its text and turned everything else into a note. Each block is labelled because a model handed two
210
+ * unlabelled documents can no longer cite either one.
211
+ */
212
+ static userContent(message: AgentWireMessage): string {
213
+ const blocks = (message.attachments ?? []).flatMap((attachment) =>
214
+ attachment.text ? [`--- attachment: ${attachment.name} (${attachment.mimeType}) ---\n${attachment.text}`] : [],
215
+ );
216
+ return [message.text, ...blocks].filter(Boolean).join("\n\n");
205
217
  }
206
218
 
207
219
  static turnAnswer(answer: DeepseekAnswer): LlmTurnAnswer {
@@ -12,6 +12,20 @@ export interface AgentWireToolResult {
12
12
  error?: string;
13
13
  }
14
14
 
15
+ /**
16
+ * A file the caller attached to one message. Exactly one carrier reaches the model — `data` as inlined bytes, `url`
17
+ * as something the provider fetches, `text` as content already extracted — and which of them a given provider can
18
+ * read is what `LlmAccepts` answers.
19
+ */
20
+ export interface AgentWireAttachment {
21
+ name: string;
22
+ mimeType: string;
23
+ /** Base64, with no `data:` prefix. */
24
+ data?: string;
25
+ url?: string;
26
+ text?: string;
27
+ }
28
+
15
29
  /**
16
30
  * One transcript message of the in-page agent wire (`use-agentic`'s WIRE.md), typed at both ends independently —
17
31
  * the wire is the contract, so the server never imports the client package.
@@ -19,6 +33,7 @@ export interface AgentWireToolResult {
19
33
  export interface AgentWireMessage {
20
34
  role: "user" | "assistant" | "tool";
21
35
  text?: string;
36
+ attachments?: AgentWireAttachment[];
22
37
  toolCalls?: AgentWireToolCall[];
23
38
  toolResults?: AgentWireToolResult[];
24
39
  error?: string;
@@ -62,6 +77,22 @@ export interface LlmAdaptor {
62
77
  * answer. An adapter may ignore it — the caller treats zero reported deltas as "answered whole".
63
78
  */
64
79
  chat(request: LlmTurnRequest, onDelta?: (delta: string) => void): Promise<LlmTurnAnswer | null>;
80
+ /** Which attachment carriers this provider's model can read. Omitted means text only. */
81
+ readonly accepts?: LlmAccepts;
82
+ }
83
+
84
+ /**
85
+ * What an adaptor's model reads beyond text. Declared rather than defaulted to true, because the failure of
86
+ * guessing wrong is the worst one available: a provider handed bytes it cannot decode either rejects the whole
87
+ * turn or accepts it having seen nothing, and the model then answers confidently about a file it never read.
88
+ * `AgentService` degrades what is not accepted into a note the model can repeat back, so a text-only provider
89
+ * needs no attachment code at all — which is every provider until somebody swaps one in for vision.
90
+ */
91
+ export interface LlmAccepts {
92
+ /** Inlined or linked image bytes. */
93
+ image?: boolean;
94
+ /** Non-image bytes handed over whole — a PDF the model parses itself. */
95
+ document?: boolean;
65
96
  }
66
97
 
67
98
  /**
@@ -1,7 +1,7 @@
1
1
  import { Translator } from "akanjs/client";
2
2
  import { parseAkanI18nEnv } from "akanjs/common";
3
3
  import { FetchClient } from "akanjs/fetch";
4
- import type { PromptContent, PromptResult, SerializedSignal } from "akanjs/signal";
4
+ import type { PromptContent, PromptMessage, PromptResult, SerializedSignal } from "akanjs/signal";
5
5
  import type { ChatMessage } from "../../vendor/use-agentic";
6
6
 
7
7
  export interface AgentPrompt {
@@ -59,7 +59,25 @@ export class AgentPrompts {
59
59
  /** The messages a prompt returns become the user's turn, the way an MCP client sends a `prompts/get` result. */
60
60
  static messagesOf(result: PromptResult): ChatMessage[] {
61
61
  if (typeof result === "string") return [{ role: "user", text: result }];
62
- return result.map((message) => ({ role: message.role, text: AgentPrompts.textOf(message.content) }));
62
+ return result.map((message) => AgentPrompts.#messageOf(message));
63
+ }
64
+
65
+ /**
66
+ * A binary block becomes an attachment. It used to become the string `[image]`, which a model reads as having
67
+ * been shown a picture — so a prompt built with `Msg.imageOf` produced confident answers about bytes that never
68
+ * left the server. The other block types are text already and stay text.
69
+ */
70
+ static #messageOf(message: PromptMessage): ChatMessage {
71
+ const { role, content } = message;
72
+ if (content.type !== "image" && content.type !== "audio") return { role, text: AgentPrompts.textOf(content) };
73
+ const name = AgentPrompts.#binaryName(content.mimeType);
74
+ return { role, attachments: [{ name, mimeType: content.mimeType, data: content.data }] };
75
+ }
76
+
77
+ /** `Msg.image` carries no filename — the protocol has nowhere to put one — so the type is the label. */
78
+ static #binaryName(mimeType: string) {
79
+ const [kind, subtype] = mimeType.split("/");
80
+ return subtype ? `${kind}.${subtype.split("+")[0]}` : mimeType;
63
81
  }
64
82
 
65
83
  static textOf(content: PromptContent): string {
@@ -248,6 +248,15 @@ export class ScreenReader {
248
248
  if (href && href !== "#" && !href.startsWith("javascript:") && text !== href) this.#buffer += ` (${href})`;
249
249
  }
250
250
 
251
+ /**
252
+ * A control the person cannot use publishes no tool, so saying so here is what turns a silent refusal into a
253
+ * fact the agent could have read. It reads `aria-disabled` too: a styled-off div carries no native property.
254
+ */
255
+ static #off(el: HTMLElement) {
256
+ const native = (el as HTMLInputElement | HTMLButtonElement).disabled;
257
+ return native || el.getAttribute("aria-disabled") === "true" ? " (disabled)" : "";
258
+ }
259
+
251
260
  #button(el: HTMLElement) {
252
261
  const before = this.#buffer;
253
262
  this.#walkChildren(el);
@@ -255,7 +264,7 @@ export class ScreenReader {
255
264
  this.#buffer = before;
256
265
  const label = inner || el.getAttribute("aria-label") || "";
257
266
  const action = el.getAttribute("data-akan-action");
258
- if (label || action) this.#buffer += ` [button: ${label}${action ? ` → ${action}` : ""}]`;
267
+ if (label || action) this.#buffer += ` [button${ScreenReader.#off(el)}: ${label}${action ? ` → ${action}` : ""}]`;
259
268
  }
260
269
 
261
270
  #control(el: HTMLElement, tag: string) {
@@ -268,12 +277,13 @@ export class ScreenReader {
268
277
  el.getAttribute("placeholder") ??
269
278
  el.getAttribute("name") ??
270
279
  type;
280
+ const off = ScreenReader.#off(el);
271
281
  if (type === "password") {
272
- this.#buffer += ` [input ${name}]`;
282
+ this.#buffer += ` [input ${name}${off}]`;
273
283
  return;
274
284
  }
275
285
  if (type === "checkbox" || type === "radio") {
276
- this.#buffer += ` [${type} ${name}: ${input.checked ? "on" : "off"}]`;
286
+ this.#buffer += ` [${type} ${name}${off}: ${input.checked ? "on" : "off"}]`;
277
287
  return;
278
288
  }
279
289
  const raw =
@@ -282,7 +292,7 @@ export class ScreenReader {
282
292
  (el as unknown as HTMLSelectElement).value)
283
293
  : input.value;
284
294
  const value = (raw ?? "").replace(/\s+/g, " ").trim().slice(0, 120);
285
- this.#buffer += ` [${tag === "SELECT" ? "select" : "input"} ${name}: ${JSON.stringify(value)}]`;
295
+ this.#buffer += ` [${tag === "SELECT" ? "select" : "input"} ${name}${off}: ${JSON.stringify(value)}]`;
286
296
  }
287
297
 
288
298
  #pre(el: HTMLElement) {
@@ -89,24 +89,77 @@ const rowEntries = (ref: FormFieldRef, arraySchema: JsonSchema): ToolEntry[] =>
89
89
  ];
90
90
  };
91
91
 
92
+ export interface FieldToolOptions {
93
+ /** The control's own normalizer. Applied to the agent's write exactly as it is to the person's typing. */
94
+ transform?: unknown;
95
+ /** True while the person cannot use the control. Publishes nothing, so the agent gets no lever the screen withholds. */
96
+ disabled?: boolean;
97
+ /** The person can drag entries into a new order, so `move<Field>On<Model>` is a lever the screen really has. */
98
+ sortable?: boolean;
99
+ }
100
+
101
+ /**
102
+ * Reorder-by-position for a list the person can drag, beside the whole-array setter.
103
+ *
104
+ * The drag is the lever the screen actually offers, and it changes no row's content — so an agent asked to move one
105
+ * row should not have to retype the nine it is leaving alone, which is the same argument that gives an embedded-row
106
+ * array its `add`/`sub`. There is no store action behind it: reordering *is* a whole-array write, so this splices
107
+ * the live rows and hands them to the setter the drag hands them to, `transform` deliberately not applied — the
108
+ * values are already stored, and normalizing them again is not something dragging does.
109
+ */
110
+ const moveEntry = (ref: FormFieldRef, onChange: () => (value: unknown) => unknown): ToolEntry => {
111
+ const name = formSetterNames(capitalize(ref.refName), ref.key).moveFieldOnModel;
112
+ return {
113
+ name,
114
+ description: `Move one entry of ${ref.key} on the ${ref.refName} form to another position, counting from 0. Reorders only — no entry's content changes.`,
115
+ parameters: {
116
+ type: "object",
117
+ properties: { from: { type: "integer" }, to: { type: "integer" } },
118
+ required: ["from", "to"],
119
+ additionalProperties: false,
120
+ },
121
+ effect: "state",
122
+ guard: (args) => {
123
+ const length = rowsOf(ref).length;
124
+ const outside = ["from", "to"].filter((key) => {
125
+ const idx = args[key];
126
+ return typeof idx !== "number" || !Number.isInteger(idx) || idx < 0 || idx >= length;
127
+ });
128
+ if (!outside.length) return true;
129
+ return `${ref.key} has ${length} ${length === 1 ? "entry" : "entries"}, so ${outside.join(" and ")} is out of range.`;
130
+ },
131
+ run: (args) => {
132
+ const rows = [...rowsOf(ref)];
133
+ const [moved] = rows.splice(args.from as number, 1);
134
+ rows.splice(args.to as number, 0, moved);
135
+ return onChange()(rows);
136
+ },
137
+ };
138
+ };
139
+
92
140
  /**
93
- * Publishes the setter a form control is already holding, for exactly as long as the control is on screen.
141
+ * Publishes the setter a form control is already holding, for exactly as long as the control is usable.
94
142
  *
95
143
  * The control is the declaration — the same rule the rest of the surface follows. A handler passed by reference
96
144
  * (`onChange={st.do.setTitleOnTask}`) names the field it writes, so the tool and the person press one function;
97
145
  * an inline arrow names nothing and publishes nothing, which is the existing `data-akan-action` rule with
98
146
  * consequences. Publishing from the form's subscription instead would offer every field of the model, including
99
147
  * the ones this template draws no control for.
148
+ *
149
+ * `disabled` withdraws the tool for the same reason the whole surface is declaration-only: a field the person
150
+ * cannot type into is not one an agent may write in their place. It also closes the field to `fill<Model>Form`,
151
+ * whose guard offers only what a control published — so one gate covers both writers.
100
152
  */
101
- export const useFieldTool = (onChange: unknown, transform?: unknown) => {
153
+ export const useFieldTool = (onChange: unknown, { transform, disabled, sortable }: FieldToolOptions = {}) => {
102
154
  const surface = useSurface();
103
155
  const scope = useScopePath();
104
156
  const action = actionTagOf(onChange)?.action ?? null;
157
+ const off = !!disabled;
105
158
  const live = useRef({ onChange, transform });
106
159
  live.current = { onChange, transform };
107
160
  const scopeKey = scope.join(".");
108
161
  useEffect(() => {
109
- if (!action) return;
162
+ if (!action || off) return;
110
163
  const ref = FormFields.ref(action);
111
164
  const schema = ref && FormFields.schema(ref.field);
112
165
  if (!ref || !schema) return;
@@ -122,10 +175,13 @@ export const useFieldTool = (onChange: unknown, transform?: unknown) => {
122
175
  },
123
176
  },
124
177
  ...rowEntries(ref, schema),
178
+ ...(sortable && ref.field.arrDepth > 0
179
+ ? [moveEntry(ref, () => live.current.onChange as (value: unknown) => unknown)]
180
+ : []),
125
181
  ];
126
182
  const registered = entries.map((entry) => surface.registerTool(scope, entry));
127
183
  return () => {
128
184
  for (const unregister of registered) unregister();
129
185
  };
130
- }, [surface, scopeKey, action]);
186
+ }, [surface, scopeKey, action, off, !!sortable]);
131
187
  };
@@ -17,6 +17,11 @@ import { FormFields } from "./formFields";
17
17
  * controls will render. The **guard** is where the screen gets its say: a plain field has to have published its
18
18
  * own setter, and a composite is waved through because its rows are written with `writeOn<Model>(path, value)`,
19
19
  * which no control can annotate — so this is the one place an agent can reach a field the screen may not show.
20
+ *
21
+ * Registered `shared`, because the entry is a pure function of `refName`: the schema comes from the model, the
22
+ * guard re-reads the live surface, and every `write` reaches the one store instance. So a form put on screen by a
23
+ * shell that subscribes it (`Model.EditModal`) and by the `Template` inside it registers one declaration twice,
24
+ * which is interchangeable in the exact sense `shared` means — not a clash an app could fix by suppressing one.
20
25
  */
21
26
  export const useFormTools = (refName: string | null, write: (action: string, value: unknown) => void) => {
22
27
  const surface = useSurface();
@@ -44,6 +49,7 @@ export const useFormTools = (refName: string | null, write: (action: string, val
44
49
  additionalProperties: false,
45
50
  },
46
51
  effect: "state",
52
+ shared: true,
47
53
  guard: (args) => {
48
54
  const keys = Object.keys(args);
49
55
  if (!keys.length) return "Name at least one field to fill.";
@@ -59,9 +65,14 @@ export const useFormTools = (refName: string | null, write: (action: string, val
59
65
  const patch = Object.entries(args).map(([key, value]) => {
60
66
  const entry = byKey.get(key);
61
67
  if (!entry) throw new Error(`The ${refName} form has no field "${key}".`);
62
- return [entry.action, FormFields.checked(name, key, entry.field, value)] as const;
68
+ return { entry, value: FormFields.checked(name, key, entry.field, value) };
63
69
  });
64
- for (const [action, value] of patch) live.current(action, value);
70
+
71
+ for (const { entry, value } of patch) {
72
+ const control = surface.tool(AgenticSurface.fullName(scope, entry.action), scope);
73
+ if (control) void control.run({ value });
74
+ else live.current(entry.action, value);
75
+ }
65
76
  },
66
77
  });
67
78
  }, [surface, scopeKey, refName]);
@@ -16,6 +16,11 @@ export const formSetterNames = (className: string, key: string) => {
16
16
  addFieldOnModel: `add${classKeyName}On${className}`,
17
17
  subFieldOnModel: `sub${classKeyName}On${className}`,
18
18
  addOrSubFieldOnModel: `addOrSub${classKeyName}On${className}`,
19
+ /**
20
+ * The agent tool a drag-sortable list publishes. No store action answers to it: reordering *is* the whole-array
21
+ * write the drag already performs, so the tool splices the live rows and hands them to the same setter.
22
+ */
23
+ moveFieldOnModel: `move${classKeyName}On${className}`,
19
24
  uploadFieldOnModel: `upload${classKeyName}On${className}`,
20
25
  /**
21
26
  * The optional hook a store declares to run after this field is written.
@@ -1 +1 @@
1
- export declare const baseDictionary: import("./dictInfo.d.ts").ServiceDictInfo<[string, string], "ping" | "pingBody" | "pingParam" | "pingQuery" | "wsPing" | "pubsubPing", never, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">;
1
+ export declare const baseDictionary: import("./dictInfo.d.ts").ServiceDictInfo<[string, string], "ping" | "pingBody" | "pingParam" | "pingQuery" | "wsPing" | "pubsubPing", never, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">;
@@ -1,13 +1,13 @@
1
1
  import type { AgentEndpoint, AgentTurn, BaseEndpoint } from "akanjs/signal";
2
2
  export declare const dictionary: {
3
- base: import("./locale.d.ts").DictModule<import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">, never>;
3
+ base: import("./locale.d.ts").DictModule<import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view">, never>;
4
4
  agentTurn: import("./locale.d.ts").DictModule<import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc`, never>;
5
5
  agent: import("./locale.d.ts").DictModule<import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, "agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed">;
6
6
  };
7
- export declare const Err: import("./trans.d.ts").ErrConstructor<"agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed">, translate: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja", key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, data?: import("./trans.d.ts").TranslationData) => string, msg: {
8
- info: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
9
- success: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
10
- error: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
11
- warning: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
12
- loading: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
13
- }, getDictionary: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja") => object, getAllDictionary: () => import("./trans.d.ts").RootDictionary, __Dict_Key__: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, __Error_Key__: "agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed";
7
+ export declare const Err: import("./trans.d.ts").ErrConstructor<"agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed">, translate: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja", key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, data?: import("./trans.d.ts").TranslationData) => string, msg: {
8
+ info: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
9
+ success: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
10
+ error: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
11
+ warning: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
12
+ loading: (key: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, option?: import("./trans.d.ts").TransMessageOption) => void;
13
+ }, getDictionary: (lang: "en" | "ko" | "zhChs" | "zhCht" | "ja") => object, getAllDictionary: () => import("./trans.d.ts").RootDictionary, __Dict_Key__: import("./locale.d.ts").ServiceTranslatorKey<"base", BaseEndpoint, "error" | "remove" | "save" | "refresh" | "password" | "stop" | "send" | "skip" | "agent" | "ok" | "connecting" | "new" | "somethingWrong" | "connected" | "serverDisconnected" | "refreshing" | "tryReconnecting" | "serverHasProblem" | "checkServerStatus" | "success" | "failed" | "processing" | "processed" | "noData" | "invalidValueError" | "emailInvalidError" | "phoneInvalidError" | "cancel" | "unauthorized" | "agentIntro" | "agentPlaceholder" | "agentClear" | "agentQuestion" | "agentAnswer" | "agentAttach" | "agentAttachRemove" | "agentAttachTooLarge" | "agentAttachUnsupported" | "agentContinue" | "agentKeepGoing" | "agentCmdNew" | "agentCmdRetry" | "agentCmdCopy" | "agentCmdHelp" | "agentCmdTools" | "agentHelpIntro" | "agentHelpNote" | "agentNothingToRetry" | "agentBusy" | "agentCopied" | "agentCopyFailed" | "agentToolsHead" | "agentToolsState" | "approve" | "decline" | "confirmClose" | "textTooShortError" | "textTooLongError" | "selectTooShortError" | "selectTooLongError" | "numberTooSmallError" | "numberTooBigError" | "passwordNotMatchError" | "selectDateError" | "priceUnit" | "passwordConfirm" | "noOptions" | "addModel" | "createModel" | "createSuccess" | "updateModel" | "removeModel" | "updateSuccess" | "removeSuccess" | "sureToRemove" | "irreversibleOps" | "typeNameToRemove" | "yesRemove" | "removeMsg" | "confirmMsg" | "perPage" | "cardView" | "tableView" | "exportCsv" | "exportJson" | "actions" | "edit" | "view"> | import("./locale.d.ts").ScalarTranslatorKey<"agentTurn", AgentTurn, never> | `agentStop.${string}` | `agentStop.${string}.desc` | import("./locale.d.ts").ServiceTranslatorKey<"agent", AgentEndpoint, never>, __Error_Key__: "agent.error.llmUnavailable" | "agent.error.deepseekRequestFailed";
@@ -1,4 +1,4 @@
1
- import type { LlmTurnRequest } from "./predefinedAdaptor/llm.adaptor";
1
+ import type { LlmAccepts, LlmTurnRequest } from "./predefinedAdaptor/llm.adaptor";
2
2
  declare const AgentService_base: import("./serve.d.ts").ServiceCls<"agent", {}, {
3
3
  llm: import("./injectInfo.d.ts").InjectInfo<"plug", import("./predefinedAdaptor.d.ts").LlmAdaptor, never, never>;
4
4
  }>;
@@ -8,5 +8,17 @@ export declare class AgentService extends AgentService_base {
8
8
  toolCalls: import("./predefinedAdaptor.d.ts").AgentWireToolCall[];
9
9
  stop: "end" | "toolUse";
10
10
  }>;
11
+ /**
12
+ * Replaces every attachment the provider cannot read with a note naming it, so no adaptor has to think about
13
+ * attachments it does not support and none can lose one quietly. The model has to be *told*, not merely spared:
14
+ * a file that vanishes on the way in is one it answers about from the filename, confidently and wrongly.
15
+ *
16
+ * The note rides in the message text because that is the one field every provider mapping already reads.
17
+ */
18
+ static readable(request: LlmTurnRequest, accepts: LlmAccepts | undefined): LlmTurnRequest;
19
+ private static readableMessage;
20
+ /** Extracted text is readable by every model there is; bytes and links need the provider to say so. */
21
+ private static isReadable;
22
+ private static note;
11
23
  }
12
24
  export {};
@@ -57,6 +57,12 @@ export declare class DeepseekLlm extends DeepseekLlm_base implements LlmAdaptor
57
57
  /** Context rides below the instructions framed as data — screen state must never read as directives. */
58
58
  static systemPrompt({ instructions, context }: LlmTurnRequest): string;
59
59
  static providerMessages(message: AgentWireMessage): DeepseekMessage[];
60
+ /**
61
+ * `accepts` is left undeclared, so by the time an attachment reaches here `AgentService.readable` has reduced it
62
+ * to its text and turned everything else into a note. Each block is labelled because a model handed two
63
+ * unlabelled documents can no longer cite either one.
64
+ */
65
+ static userContent(message: AgentWireMessage): string;
60
66
  static turnAnswer(answer: DeepseekAnswer): LlmTurnAnswer;
61
67
  /** The provider sends arguments as a JSON string; an unparsable one becomes an empty call rather than a crash. */
62
68
  static parsedArgs(raw: string | undefined): Record<string, unknown>;
@@ -10,6 +10,19 @@ export interface AgentWireToolResult {
10
10
  changes?: unknown[];
11
11
  error?: string;
12
12
  }
13
+ /**
14
+ * A file the caller attached to one message. Exactly one carrier reaches the model — `data` as inlined bytes, `url`
15
+ * as something the provider fetches, `text` as content already extracted — and which of them a given provider can
16
+ * read is what `LlmAccepts` answers.
17
+ */
18
+ export interface AgentWireAttachment {
19
+ name: string;
20
+ mimeType: string;
21
+ /** Base64, with no `data:` prefix. */
22
+ data?: string;
23
+ url?: string;
24
+ text?: string;
25
+ }
13
26
  /**
14
27
  * One transcript message of the in-page agent wire (`use-agentic`'s WIRE.md), typed at both ends independently —
15
28
  * the wire is the contract, so the server never imports the client package.
@@ -17,6 +30,7 @@ export interface AgentWireToolResult {
17
30
  export interface AgentWireMessage {
18
31
  role: "user" | "assistant" | "tool";
19
32
  text?: string;
33
+ attachments?: AgentWireAttachment[];
20
34
  toolCalls?: AgentWireToolCall[];
21
35
  toolResults?: AgentWireToolResult[];
22
36
  error?: string;
@@ -55,6 +69,21 @@ export interface LlmAdaptor {
55
69
  * answer. An adapter may ignore it — the caller treats zero reported deltas as "answered whole".
56
70
  */
57
71
  chat(request: LlmTurnRequest, onDelta?: (delta: string) => void): Promise<LlmTurnAnswer | null>;
72
+ /** Which attachment carriers this provider's model can read. Omitted means text only. */
73
+ readonly accepts?: LlmAccepts;
74
+ }
75
+ /**
76
+ * What an adaptor's model reads beyond text. Declared rather than defaulted to true, because the failure of
77
+ * guessing wrong is the worst one available: a provider handed bytes it cannot decode either rejects the whole
78
+ * turn or accepts it having seen nothing, and the model then answers confidently about a file it never read.
79
+ * `AgentService` degrades what is not accepted into a note the model can repeat back, so a text-only provider
80
+ * needs no attachment code at all — which is every provider until somebody swaps one in for vision.
81
+ */
82
+ export interface LlmAccepts {
83
+ /** Inlined or linked image bytes. */
84
+ image?: boolean;
85
+ /** Non-image bytes handed over whole — a PDF the model parses itself. */
86
+ document?: boolean;
58
87
  }
59
88
  /**
60
89
  * Settings for whichever adaptor fills `LlmAdaptorRole`, registered with `option.setLlm(...)` and injected as the