@things-factory/ai-assistant 10.1.31 → 10.1.33
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/testing/mode-conformance.js +8 -1
- package/dist-server/service/testing/mode-conformance.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/server/service/testing/mode-conformance.test.ts +48 -0
- package/server/service/testing/mode-conformance.ts +8 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/ai-assistant",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.33",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"things-factory": true,
|
|
6
6
|
"author": "heartyoh",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"@operato/i18n": "^10.0.0",
|
|
31
31
|
"@operato/styles": "^10.0.0",
|
|
32
32
|
"@things-factory/ai-client-base": "^10.1.31",
|
|
33
|
-
"@things-factory/auth-base": "^10.1.
|
|
33
|
+
"@things-factory/auth-base": "^10.1.33",
|
|
34
34
|
"@things-factory/env": "^10.1.20",
|
|
35
|
-
"@things-factory/shell": "^10.1.
|
|
35
|
+
"@things-factory/shell": "^10.1.33",
|
|
36
36
|
"dompurify": "^3.0.0",
|
|
37
37
|
"graphql-scalars": "^1.22.0",
|
|
38
38
|
"graphql-tag": "^2.12.6",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"copyfiles": "^2.4.1",
|
|
45
45
|
"rimraf": "^5.0.0"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "7fb7b6587846b5ff6ee7a4983652d9561b4d6b2c"
|
|
48
48
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* What this guards: the harness an app uses to check its own modes asks about **modes**, not about
|
|
3
|
+
* permission.
|
|
4
|
+
*
|
|
5
|
+
* `runRegisteredMode` answers "which tools does this mode open". Since the gate moved onto `doors`
|
|
6
|
+
* (ADR-0071 decision 2), a tool that names a door is closed to a caller with no access check —
|
|
7
|
+
* which is right in a request and wrong in a conformance run: an app checking that its mode opens
|
|
8
|
+
* four tools would see three, and the one that vanishes is the one that reads real data.
|
|
9
|
+
*
|
|
10
|
+
* So the harness runs as someone who passes every door, and an app that wants the other case names
|
|
11
|
+
* it. The permission axis has its own tests (`tool-access.test.ts` · `tool-scope.test.ts`).
|
|
12
|
+
*/
|
|
13
|
+
import { clearAssistantModes, registerAssistantMode } from '../assistant-mode/registry'
|
|
14
|
+
import { clearToolRegistry, registerToolCategory } from '@things-factory/ai-client-base'
|
|
15
|
+
|
|
16
|
+
import { runRegisteredMode } from './mode-conformance'
|
|
17
|
+
|
|
18
|
+
const client = {
|
|
19
|
+
chatWithTools: async () => ({ text: '한 턴', toolCalls: [], stopReason: 'end_turn' })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
clearAssistantModes()
|
|
24
|
+
clearToolRegistry()
|
|
25
|
+
registerToolCategory({
|
|
26
|
+
name: 'twin-authoring',
|
|
27
|
+
guidance: 'read the scene before proposing an edit',
|
|
28
|
+
specs: [
|
|
29
|
+
/* 화면이 보낸 것을 되돌려 준다 — 저장소에 안 닿는다. */
|
|
30
|
+
{ kind: 'read', name: 'getOpenScene', description: 'the open scene', schema: {}, doors: [], builder: () => ({}) },
|
|
31
|
+
/* 저장소를 읽는다 — 문을 적는다. 관문이 이 자리를 가른다. */
|
|
32
|
+
{ kind: 'read', name: 'getTwinStructure', description: 'the twin structure', schema: {}, doors: ['Query.twinInstances'], builder: () => ({}) }
|
|
33
|
+
]
|
|
34
|
+
} as any)
|
|
35
|
+
registerAssistantMode({ kind: 'twin.space', systemPrompt: 'you answer about one space', toolCategories: ['twin-authoring'] } as any)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('★ a mode opens the tools that read real data too — a conformance run is not a caller without permission', async () => {
|
|
39
|
+
const result = await runRegisteredMode('twin.space', { client })
|
|
40
|
+
|
|
41
|
+
expect(result.offeredTools.sort()).toEqual(['getOpenScene', 'getTwinStructure'])
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('an app that wants to see the other case names it — the door the caller cannot pass', async () => {
|
|
45
|
+
const result = await runRegisteredMode('twin.space', { client, toolAccess: async () => false })
|
|
46
|
+
|
|
47
|
+
expect(result.offeredTools).toEqual(['getOpenScene'])
|
|
48
|
+
})
|
|
@@ -52,7 +52,14 @@ export async function runRegisteredMode(
|
|
|
52
52
|
temperature: settings.temperature,
|
|
53
53
|
maxTokens: settings.maxTokens,
|
|
54
54
|
hostContext: options.hostContext,
|
|
55
|
-
|
|
55
|
+
/*
|
|
56
|
+
* 이 하네스가 묻는 것은 **모드**다 — 「이 모드가 무엇을 여는가」이지 「이 사람이 문을 지나는가」가
|
|
57
|
+
* 아니다. 관문이 `doors` 로 옮겨간 뒤(ADR-0071 결정 2) 검사를 안 넘기면 문을 적은 도구가 전부
|
|
58
|
+
* 닫히고, 그러면 앱의 적합성 시험에서 **실제 자료를 읽는 도구만** 조용히 사라진다. 그래서 기본은
|
|
59
|
+
* 모든 문을 지나는 사람이고, 다른 쪽을 보고 싶은 앱은 `toolAccess` 를 적어 그 경우를 쓴다.
|
|
60
|
+
* 권한 축은 자기 시험이 따로 본다(`tool-access.test.ts` · `tool-scope.test.ts`).
|
|
61
|
+
*/
|
|
62
|
+
toolAccess: options.toolAccess ?? (async () => true),
|
|
56
63
|
state: options.state ?? { domain: { id: 'dom-1' }, user: { id: 'u-1' } }
|
|
57
64
|
})
|
|
58
65
|
|