@things-factory/ai-client-base 10.1.28 → 10.1.31
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.js +13 -1
- package/dist-server/service/agentic-loop/agentic-loop.js.map +1 -1
- package/dist-server/service/assistant/registry-assistant.d.ts +11 -0
- package/dist-server/service/assistant/registry-assistant.js +5 -1
- package/dist-server/service/assistant/registry-assistant.js.map +1 -1
- package/dist-server/service/assistant/tool-scope.d.ts +3 -3
- package/dist-server/service/assistant/tool-scope.js +32 -15
- package/dist-server/service/assistant/tool-scope.js.map +1 -1
- package/dist-server/service/tool-registry/index.d.ts +1 -1
- package/dist-server/service/tool-registry/index.js.map +1 -1
- package/dist-server/service/tool-registry/registry.d.ts +9 -8
- package/dist-server/service/tool-registry/registry.js +26 -38
- package/dist-server/service/tool-registry/registry.js.map +1 -1
- package/dist-server/service/tool-registry/types.d.ts +16 -31
- package/dist-server/service/tool-registry/types.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/service/agentic-loop/agentic-loop.ts +14 -1
- package/server/service/agentic-loop/proposal-key.test.ts +31 -0
- package/server/service/assistant/registry-assistant.test.ts +65 -15
- package/server/service/assistant/registry-assistant.ts +13 -1
- package/server/service/assistant/tool-scope.test.ts +123 -0
- package/server/service/assistant/tool-scope.ts +34 -17
- package/server/service/tool-registry/index.ts +0 -1
- package/server/service/tool-registry/registry.test.ts +34 -66
- package/server/service/tool-registry/registry.ts +35 -46
- package/server/service/tool-registry/types.ts +16 -33
|
@@ -32,13 +32,15 @@ const categories: Map<string, ToolCategory> = new Map()
|
|
|
32
32
|
/** A registration that was accepted but is not yet what the contract requires. Code + params, not prose. */
|
|
33
33
|
export interface ToolRegistryWarning {
|
|
34
34
|
/**
|
|
35
|
-
* `tool-
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
* `tool-doors-undeclared` — nothing said which doors this tool reaches, so the gate leaves it out
|
|
36
|
+
* of every conversation (`resolveToolScope`) and this names it.
|
|
37
|
+
*
|
|
38
|
+
* What those doors require is **not** asked here. Registration runs while modules load, before the
|
|
39
|
+
* schema stands, so AUTH's field table is still empty; `ai-assistant`'s boot report asks the
|
|
40
|
+
* schema after it is built (`tool-door-not-in-schema` · `tool-door-has-no-privilege`).
|
|
39
41
|
*/
|
|
40
|
-
code: 'tool-
|
|
41
|
-
params: { category: string; tool: string; kind: ToolSpec['kind']
|
|
42
|
+
code: 'tool-doors-undeclared'
|
|
43
|
+
params: { category: string; tool: string; kind: ToolSpec['kind'] }
|
|
42
44
|
/** For a person reading it; the code and params are what callers act on. */
|
|
43
45
|
message: string
|
|
44
46
|
}
|
|
@@ -46,8 +48,8 @@ export interface ToolRegistryWarning {
|
|
|
46
48
|
const warnings: ToolRegistryWarning[] = []
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
|
-
* What
|
|
50
|
-
*
|
|
51
|
+
* What registered without saying which doors it reaches. A host prints this at boot; the gate has
|
|
52
|
+
* already left each of these tools out of every conversation.
|
|
51
53
|
*/
|
|
52
54
|
export function getToolRegistryWarnings(): ReadonlyArray<ToolRegistryWarning> {
|
|
53
55
|
return warnings.slice()
|
|
@@ -80,53 +82,40 @@ export function registerToolCategory(category: ToolCategory): void {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
/*
|
|
83
|
-
* A tool
|
|
85
|
+
* A tool says which doors it reaches (ADR-0071 decision 2).
|
|
84
86
|
*
|
|
85
|
-
* The conversation door checks one privilege — may this person talk to the assistant.
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* Board.
|
|
87
|
+
* The conversation door checks one privilege — may this person talk to the assistant. On its own
|
|
88
|
+
* that single check opened every tool behind it: a user holding only `ai-assistant:mutation` could
|
|
89
|
+
* reach board-import's `materializeImportSession` and create a Board.
|
|
89
90
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* tool
|
|
94
|
-
*
|
|
95
|
-
* is empty across the apps, this becomes a refusal.
|
|
91
|
+
* Registration does not refuse a tool that says nothing; it records the omission as a code
|
|
92
|
+
* (`getToolRegistryWarnings`), not a log line — a warning only a log carries is not
|
|
93
|
+
* distinguishable from no warning. The refusal happens where it costs nothing to be late: the gate
|
|
94
|
+
* leaves an undeclared tool out of every conversation, so booting stays possible while the list is
|
|
95
|
+
* worked down, and no undeclared tool is reachable in the meantime.
|
|
96
96
|
*/
|
|
97
97
|
warnings.splice(0, warnings.length, ...warnings.filter(w => w.params.category !== category.name))
|
|
98
|
-
for (const spec of category.specs) {
|
|
99
|
-
const declared = Boolean(spec.privilege?.category && spec.privilege?.privilege)
|
|
100
|
-
if (declared) continue
|
|
101
|
-
|
|
102
|
-
/*
|
|
103
|
-
* An empty list says this tool reaches no door — the ones that hand back what the surface sent
|
|
104
|
-
* are that. Nothing to gate, nothing to report.
|
|
105
|
-
*/
|
|
106
|
-
if (Array.isArray(spec.doors) && spec.doors.length === 0) continue
|
|
107
98
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
})
|
|
121
|
-
continue
|
|
122
|
-
}
|
|
99
|
+
/*
|
|
100
|
+
* ── 문을 안 적은 도구는 그 답을 **아무도 못 읽게 만든다** ─────────────────
|
|
101
|
+
*
|
|
102
|
+
* ADR-0073 뒤로 어시스턴트의 답 행에는 그 답을 만든 도구들이 지난 문이 적힌다. 선언이 없는 도구는
|
|
103
|
+
* `unresolved:<도구>` 로 적히고 그 이름은 아무도 못 지난다 — **답을 쓴 본인도** 새로 고치면
|
|
104
|
+
* 「이 답을 볼 권한이 없습니다」를 본다. 2026-09-18 트윈 모델러에서 그렇게 나왔다.
|
|
105
|
+
*
|
|
106
|
+
* 2026-09-18 부터 이 선언은 관문이기도 하다(ADR-0071 결정 2 보탬). 문을 안 적은 도구는 어느 대화에도
|
|
107
|
+
* 나가지 않으므로, 이 목록이 「무엇이 빠져 있나」를 말하는 유일한 자리다.
|
|
108
|
+
*/
|
|
109
|
+
for (const spec of category.specs) {
|
|
110
|
+
if (Array.isArray(spec.doors)) continue
|
|
123
111
|
|
|
124
112
|
warnings.push({
|
|
125
|
-
code: 'tool-
|
|
113
|
+
code: 'tool-doors-undeclared',
|
|
126
114
|
params: { category: category.name, tool: spec.name, kind: spec.kind },
|
|
127
115
|
message:
|
|
128
|
-
`tool '${spec.name}' in category '${category.name}'
|
|
129
|
-
|
|
116
|
+
`tool '${spec.name}' in category '${category.name}' declares no doors, so it is offered to no ` +
|
|
117
|
+
'conversation and answers built with it would be hidden from every reader including their ' +
|
|
118
|
+
'author — declare `doors`, or `doors: []` when it reaches none'
|
|
130
119
|
})
|
|
131
120
|
}
|
|
132
121
|
|
|
@@ -119,23 +119,6 @@ export interface ExternalToolContext {
|
|
|
119
119
|
}
|
|
120
120
|
// ── ToolSpec — 카테고리별 변형 ──────────────────────────────────────
|
|
121
121
|
|
|
122
|
-
/**
|
|
123
|
-
* The privilege a tool requires — the same `category`/`privilege` the tool's GraphQL door declares
|
|
124
|
-
* (`@privilege(category, privilege, owner?, super?)`), so the fields mirror `PrivilegeObject`.
|
|
125
|
-
*
|
|
126
|
-
* A tool is not a new door; it is another way into a door that already exists. A tool that creates
|
|
127
|
-
* a Board goes through the privilege that creating a Board already needs. Minting an AI-only
|
|
128
|
-
* privilege name would put two doors on one effect, and one of them would end up looser.
|
|
129
|
-
*/
|
|
130
|
-
export interface ToolPrivilege {
|
|
131
|
-
category: string
|
|
132
|
-
privilege: string
|
|
133
|
-
/** Domain owners pass without the privilege — mirror the door's `domainOwnerGranted`. */
|
|
134
|
-
owner?: boolean
|
|
135
|
-
/** Super users pass without the privilege — mirror the door's `superUserGranted`. */
|
|
136
|
-
super?: boolean
|
|
137
|
-
}
|
|
138
|
-
|
|
139
122
|
/**
|
|
140
123
|
* 모든 ToolSpec 공통 메타데이터.
|
|
141
124
|
*/
|
|
@@ -151,14 +134,6 @@ export interface ToolSpecBase {
|
|
|
151
134
|
themeNote?: string
|
|
152
135
|
/** JSON Schema for parameters — LLM tool schema 에 그대로 노출. */
|
|
153
136
|
schema: any
|
|
154
|
-
/**
|
|
155
|
-
* The privilege a caller needs before this tool is offered or run. See `ToolPrivilege`.
|
|
156
|
-
*
|
|
157
|
-
* Expected for `write`, `action` and `external` — registration records a
|
|
158
|
-
* `tool-privilege-undeclared` warning for one without it (A1 stage one).
|
|
159
|
-
*/
|
|
160
|
-
privilege?: ToolPrivilege
|
|
161
|
-
|
|
162
137
|
/**
|
|
163
138
|
* Which GraphQL doors this tool reaches — `['Query.twinNow']`,
|
|
164
139
|
* `['Query.productionSummary', 'Query.pendingSummary']`.
|
|
@@ -173,15 +148,23 @@ export interface ToolSpecBase {
|
|
|
173
148
|
* plant's factory overview answers from two, and asking for only one of them would hand back
|
|
174
149
|
* half of what the caller may not see.
|
|
175
150
|
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
151
|
+
* **This list is the gate** (ADR-0071 decision 2, added to 2026-09-18). A tool used to carry its
|
|
152
|
+
* own copy of what its door asks; the copy went stale the day a door changed its declaration, and
|
|
153
|
+
* the looser of the two won. Now the gate reads the door's own declaration from AUTH's field
|
|
154
|
+
* table (`privilegeOfField`) and asks `checkPermission` with it — the same question, the same
|
|
155
|
+
* answer, one declaration.
|
|
156
|
+
*
|
|
157
|
+
* Three states, and they are not the same thing:
|
|
158
|
+
* - **doors named** — the caller passes each of them, or the tool is neither offered nor run.
|
|
159
|
+
* - **`doors: []`** — this tool reaches no door at all. The ones that hand back what the surface
|
|
160
|
+
* sent (`getOpenScene`) are this: there is nothing behind them to gate.
|
|
161
|
+
* - **no `doors` key** — nothing was said. This is **not** an open tool: it is left out of every
|
|
162
|
+
* conversation and reported by name at boot (`tool-doors-undeclared`). An answer built with it
|
|
163
|
+
* would be hidden from every reader including its author (ADR-0073), so opening it would
|
|
164
|
+
* produce a tool whose answers no one can read.
|
|
182
165
|
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
166
|
+
* A door that declares nothing of its own opens as far as that door opens — a tool is never
|
|
167
|
+
* stricter than the door it reaches, and never softer.
|
|
185
168
|
*/
|
|
186
169
|
doors?: string[]
|
|
187
170
|
}
|