@things-factory/ai-client-base 10.1.167 → 10.1.169
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/dist-server/service/agentic-loop/agentic-loop.d.ts +9 -0
- package/dist-server/service/agentic-loop/agentic-loop.js +22 -3
- package/dist-server/service/agentic-loop/agentic-loop.js.map +1 -1
- package/dist-server/service/assistant/open-subject.d.ts +4 -0
- package/dist-server/service/assistant/open-subject.js +3 -0
- package/dist-server/service/assistant/open-subject.js.map +1 -1
- package/dist-server/service/assistant/registry-assistant.d.ts +11 -1
- package/dist-server/service/assistant/registry-assistant.js +25 -3
- package/dist-server/service/assistant/registry-assistant.js.map +1 -1
- package/dist-server/service/providers/anthropic.d.ts +2 -0
- package/dist-server/service/providers/anthropic.js +7 -3
- package/dist-server/service/providers/anthropic.js.map +1 -1
- package/dist-server/service/providers/gemini.js +1 -1
- package/dist-server/service/providers/gemini.js.map +1 -1
- package/dist-server/service/providers/openai.js +1 -1
- package/dist-server/service/providers/openai.js.map +1 -1
- package/dist-server/service/testing/tool-call-context.d.ts +5 -1
- package/dist-server/service/testing/tool-call-context.js +4 -1
- package/dist-server/service/testing/tool-call-context.js.map +1 -1
- package/dist-server/service/tool-registry/call-recorder.d.ts +16 -0
- package/dist-server/service/tool-registry/call-recorder.js +24 -0
- package/dist-server/service/tool-registry/call-recorder.js.map +1 -0
- package/dist-server/service/tool-registry/index.d.ts +4 -1
- package/dist-server/service/tool-registry/index.js +7 -1
- package/dist-server/service/tool-registry/index.js.map +1 -1
- package/dist-server/service/tool-registry/translate.d.ts +2 -0
- package/dist-server/service/tool-registry/translate.js +6 -0
- package/dist-server/service/tool-registry/translate.js.map +1 -0
- package/dist-server/service/tool-registry/types.d.ts +50 -0
- package/dist-server/service/tool-registry/types.js.map +1 -1
- package/dist-server/service/utils/parse-json.d.ts +12 -1
- package/dist-server/service/utils/parse-json.js +18 -4
- package/dist-server/service/utils/parse-json.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/service/agentic-loop/agentic-loop.test.ts +76 -0
- package/server/service/agentic-loop/agentic-loop.ts +22 -3
- package/server/service/assistant/open-subject.ts +3 -0
- package/server/service/assistant/registry-assistant.test.ts +133 -1
- package/server/service/assistant/registry-assistant.ts +35 -5
- package/server/service/providers/anthropic.ts +8 -4
- package/server/service/providers/gemini.ts +1 -1
- package/server/service/providers/json-answer.test.ts +53 -0
- package/server/service/providers/openai.ts +1 -1
- package/server/service/testing/tool-call-context.ts +9 -2
- package/server/service/tool-registry/call-recorder.ts +47 -0
- package/server/service/tool-registry/index.ts +10 -1
- package/server/service/tool-registry/translate.ts +12 -0
- package/server/service/tool-registry/types.ts +54 -0
- package/server/service/utils/parse-json.ts +21 -5
|
@@ -48,6 +48,27 @@ export interface ToolCallContext {
|
|
|
48
48
|
host: Record<string, any>
|
|
49
49
|
/** The conversation's mode. Filled once modes are registered on the server (A2); absent until then. */
|
|
50
50
|
mode?: ToolCallMode
|
|
51
|
+
/**
|
|
52
|
+
* The request's translator, for every sentence a person reads — a card's label and reason, a message shown on
|
|
53
|
+
* screen (ADR-0061, 2026-10-16 decision 1). The server builds it in the request's locale; it never comes from
|
|
54
|
+
* `host`. Codes and values stay apart from it. Pass the English text as `defaultValue`: without a request
|
|
55
|
+
* translator the door hands one that writes that default value with its placeholders filled, and a key with no
|
|
56
|
+
* default value comes back as the key.
|
|
57
|
+
*/
|
|
58
|
+
t: ToolTranslate
|
|
59
|
+
/**
|
|
60
|
+
* The files the person sent with **this turn**, after the server checked them (ADR-0061, 2026-10-16 decision 2) —
|
|
61
|
+
* never from `host`, which the client fills. `[]` when none were sent.
|
|
62
|
+
*
|
|
63
|
+
* Checked means: each one is stored with this conversation, the conversation is the caller's to write, and the
|
|
64
|
+
* mode's attachment policy took it — the kinds it declares, at most `maxFiles` files (5 by default) of at most
|
|
65
|
+
* `maxBytes` each (5 MiB by default). **Their contents are untrusted input**: a tool may hand them to a model or
|
|
66
|
+
* read them as data, and never takes an instruction or an id it trusts from them.
|
|
67
|
+
*
|
|
68
|
+
* Several files travel as several entries, in the order the person attached them. A tool that takes images
|
|
69
|
+
* reads every entry of `kind: 'image'` — it does not pick the first one.
|
|
70
|
+
*/
|
|
71
|
+
attachments: ToolAttachment[]
|
|
51
72
|
}
|
|
52
73
|
|
|
53
74
|
export interface ToolCallMode {
|
|
@@ -55,6 +76,39 @@ export interface ToolCallMode {
|
|
|
55
76
|
target?: string
|
|
56
77
|
}
|
|
57
78
|
|
|
79
|
+
/** `t(key, { defaultValue, ...params })` — the request's translator, as i18next's `t` is called. */
|
|
80
|
+
export type ToolTranslate = (key: string, params?: Record<string, any>) => string
|
|
81
|
+
|
|
82
|
+
/** One file of this turn, as the server read it from storage. */
|
|
83
|
+
export type ToolAttachment = ToolImageAttachment | ToolTextAttachment
|
|
84
|
+
|
|
85
|
+
interface ToolAttachmentBase {
|
|
86
|
+
/** The stored attachment's id — the one the model reads in `[attachment <id>: …]`. */
|
|
87
|
+
id: string
|
|
88
|
+
/** The file name the person gave. Untrusted text. */
|
|
89
|
+
name: string
|
|
90
|
+
/** The stored media type. */
|
|
91
|
+
mediaType: string
|
|
92
|
+
/** Bytes as stored. */
|
|
93
|
+
size: number
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** An `image/*` file, its bytes as stored. */
|
|
97
|
+
export interface ToolImageAttachment extends ToolAttachmentBase {
|
|
98
|
+
kind: 'image'
|
|
99
|
+
data: Buffer
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A text file — or a spreadsheet, which the screen reads into the CSV text of its sheets before sending. `text` is
|
|
104
|
+
* what the model read this turn, cut at the mode's `maxChars`; `truncated` says it was cut.
|
|
105
|
+
*/
|
|
106
|
+
export interface ToolTextAttachment extends ToolAttachmentBase {
|
|
107
|
+
kind: 'text'
|
|
108
|
+
text: string
|
|
109
|
+
truncated: boolean
|
|
110
|
+
}
|
|
111
|
+
|
|
58
112
|
// ── 카테고리별 builder 시그니처 ──────────────────────────────────────
|
|
59
113
|
|
|
60
114
|
/**
|
|
@@ -13,9 +13,24 @@
|
|
|
13
13
|
* 3) 그래도 실패하면 raw 출력 포함한 명확한 에러
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* The model answered, and the answer is not JSON (figure audit 2026-10-16 N2). Told apart from a request that failed --
|
|
18
|
+
* a rejected key, a rate limit, a dropped connection -- because only this is worth asking again, with why it was not.
|
|
19
|
+
* `cutShort`: the answer stopped at the output limit before it ended, so asking again for the same is cut off again.
|
|
20
|
+
*/
|
|
21
|
+
export class AIAnswerNotJSON extends Error {
|
|
22
|
+
constructor(
|
|
23
|
+
message: string,
|
|
24
|
+
readonly cutShort = false
|
|
25
|
+
) {
|
|
26
|
+
super(message)
|
|
27
|
+
this.name = 'AIAnswerNotJSON'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function parseJSONResponse<T = unknown>(text: string, { cutShort = false }: { cutShort?: boolean } = {}): T {
|
|
17
32
|
if (typeof text !== 'string') {
|
|
18
|
-
throw new
|
|
33
|
+
throw new AIAnswerNotJSON(`AI response is not a string (got ${typeof text})`, cutShort)
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
const stripped = stripCodeFences(text).trim()
|
|
@@ -33,13 +48,14 @@ export function parseJSONResponse<T = unknown>(text: string): T {
|
|
|
33
48
|
try {
|
|
34
49
|
return JSON.parse(extracted) as T
|
|
35
50
|
} catch (e) {
|
|
36
|
-
throw new
|
|
37
|
-
`AI response is not valid JSON (after extraction): ${(e as Error).message}\n----- raw -----\n${text}
|
|
51
|
+
throw new AIAnswerNotJSON(
|
|
52
|
+
`AI response is not valid JSON (after extraction): ${(e as Error).message}\n----- raw -----\n${text}`,
|
|
53
|
+
cutShort
|
|
38
54
|
)
|
|
39
55
|
}
|
|
40
56
|
}
|
|
41
57
|
|
|
42
|
-
throw new
|
|
58
|
+
throw new AIAnswerNotJSON(`AI response is not valid JSON: no balanced JSON found.\n----- raw -----\n${text}`, cutShort)
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
function stripCodeFences(text: string): string {
|