@things-factory/ai-client-base 10.1.207 → 10.1.208

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/ai-client-base",
3
- "version": "10.1.207",
3
+ "version": "10.1.208",
4
4
  "main": "dist-server/index.js",
5
5
  "things-factory": true,
6
6
  "author": "heartyoh",
@@ -24,11 +24,11 @@
24
24
  "dependencies": {
25
25
  "@anthropic-ai/sdk": "^0.40.0",
26
26
  "@google/generative-ai": "^0.24.0",
27
- "@things-factory/auth-base": "^10.1.206",
27
+ "@things-factory/auth-base": "^10.1.208",
28
28
  "@things-factory/env": "^10.1.149",
29
- "@things-factory/setting-base": "^10.1.206",
30
- "@things-factory/shell": "^10.1.206",
29
+ "@things-factory/setting-base": "^10.1.208",
30
+ "@things-factory/shell": "^10.1.208",
31
31
  "openai": "^4.0.0"
32
32
  },
33
- "gitHead": "e6e3fbf5c353629adbeb8850093150b14254e059"
33
+ "gitHead": "ffc1ceaa9471b9f4a9b86f46c3021f2fed72d17d"
34
34
  }
@@ -136,6 +136,24 @@ describe('a scope larger than one call carries', () => {
136
136
  expect(result.openableTools).not.toContain('readList150')
137
137
  })
138
138
 
139
+ it('★ the list says its tools are the model\'s to use - not something to tell the person is missing', async () => {
140
+ const categories = registerLists(165)
141
+ const { client, seen } = recordingClient()
142
+ await registryAssistantChat(input({ client, toolCategories: categories, front: { categories: ['list-3'] } }))
143
+
144
+ const prompt = seen[0].options.systemPrompt
145
+ expect(prompt).toMatch(/never tell the person a tool on this list is missing/)
146
+ expect(prompt).toMatch(/never ask the person to choose instead of opening it/)
147
+ })
148
+
149
+ it('★ opening a tool that is already offered says so - call it now', async () => {
150
+ const categories = registerLists(165)
151
+ const { client, seen } = recordingClient([reply(undefined, [TC(OPEN_TOOLS, { names: ['readList3', 'readList150'] })]), reply('done')])
152
+ await registryAssistantChat(input({ client, toolCategories: categories, front: { categories: ['list-3'] } }))
153
+
154
+ expect(JSON.parse(seen[1].messages.at(-1).content[0].content)).toMatchObject({ opened: ['readList150'], alreadyOffered: ['readList3'] })
155
+ })
156
+
139
157
  it('★ a tool called before it is opened does not run - the model is told to open it', async () => {
140
158
  const categories = registerLists(165)
141
159
  const { client, seen } = recordingClient([reply(undefined, [TC('readList150')]), reply('done')])
@@ -82,7 +82,14 @@ function oneLine(description: string | undefined): string {
82
82
  export function openableToolCatalog(openable: ReadonlyArray<ToolSpec>): string {
83
83
  if (openable.length === 0) return ''
84
84
  return [
85
- `Tools you can open with ${OPEN_TOOLS} (name: what it does). Open the ones the question needs, then call them:`,
85
+ /*
86
+ * Office's model, offered a bank lines' total it could open, told the person that read was not open and asked them
87
+ * to pick a range (2026-10-22) - an app's prompt said a missing tool means a missing permission. What is on this
88
+ * list is the model's to use.
89
+ */
90
+ `Tools you can open with ${OPEN_TOOLS} (name: what it does). They are yours to use in this conversation, only not loaded yet. ` +
91
+ `When the question needs one, open it with ${OPEN_TOOLS} and call it: never tell the person a tool on this list is missing, ` +
92
+ 'and never ask the person to choose instead of opening it.',
86
93
  ...openable.map(spec => `- ${spec.name}: ${oneLine(spec.description)}`)
87
94
  ].join('\n')
88
95
  }
@@ -119,6 +126,7 @@ export function toolOfferState(scope: ToolScope, front: ToolFront = {}) {
119
126
  return { rejected: true, error: 'tool-not-offered', names: unknown, message: `these are not in the list of tools you can open: ${unknown.join(', ')}. Use the exact names from the list.` }
120
127
  }
121
128
  const fresh = [...new Set(names)].filter(name => !offered.has(name))
129
+ const already = [...new Set(names)].filter(name => offered.has(name) && name !== OPEN_TOOLS)
122
130
  const room = MAX_OFFERED_TOOLS - offered.size
123
131
  if (fresh.length > room) {
124
132
  return {
@@ -130,7 +138,14 @@ export function toolOfferState(scope: ToolScope, front: ToolFront = {}) {
130
138
  }
131
139
  for (const name of fresh) offered.set(name, openable.get(name)!)
132
140
  const guidance = [...new Set(fresh.map(name => categoryOf.get(name)?.guidance).filter(Boolean))]
133
- return { opened: fresh, ...(guidance.length ? { guidance: guidance.join('\n') } : {}), message: 'these tools are open from your next step on. Call them as the question needs.' }
141
+ return {
142
+ opened: fresh,
143
+ ...(already.length ? { alreadyOffered: already } : {}),
144
+ ...(guidance.length ? { guidance: guidance.join('\n') } : {}),
145
+ message: already.length
146
+ ? `${already.join(', ')} ${already.length > 1 ? 'are' : 'is'} already open - call ${already.length > 1 ? 'them' : 'it'} now. ${fresh.length ? 'The others are open from your next step on.' : ''}`.trim()
147
+ : 'these tools are open from your next step on. Call them as the question needs.'
148
+ }
134
149
  }
135
150
 
136
151
  return {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "error.ai-not-configured": "이 사업장에는 AI 가 설정되어 있지 않습니다.",
3
3
  "error.ai-agent-not-available": "선택한 AI 에이전트를 이 사업장에서 사용할 수 없습니다.",
4
- "error.ai-agent-provider-unknown": "\"{provider}\" 은(는) 에이전트가 쓸 수 있는 제공처가 아닙니다.",
4
+ "error.ai-agent-provider-unknown": "\"{provider}\"은(는) 에이전트에서 지원하지 않는 제공자입니다.",
5
5
  "error.ai-agent-key-required": "\"{agent}\" 의 API 키를 입력해 주세요.",
6
6
  "error.ai-agent-name-required": "에이전트마다 이름이 있어야 합니다.",
7
7
  "error.ai-agent-model-required": "\"{agent}\" 의 모델을 선택해 주세요.",