@siduri-x/api 2.0.2 → 2.0.3
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +15 -15
- package/dist/app.js +20 -1
- package/dist/context-mapper.js +2 -0
- package/dist/context-mapper.test.js +35 -0
- package/dist/index.test.js +14 -0
- package/dist/teach-mode.test.js +67 -0
- package/package.json +3 -3
- package/siduri.sqlite +0 -0
- package/src/app.ts +22 -1
- package/src/context-mapper.test.ts +39 -0
- package/src/context-mapper.ts +2 -0
- package/src/index.test.ts +20 -0
- package/src/teach-mode.test.ts +77 -0
package/.turbo/turbo-build.log
CHANGED
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @siduri-x/api@2.0.
|
|
2
|
+
> @siduri-x/api@2.0.3 test /home/zagin/Projects/vxnus-studio/projects/siduri-x/apps/api
|
|
3
3
|
> jest --config jest.config.json
|
|
4
4
|
|
|
5
|
-
PASS src/
|
|
6
|
-
PASS src/
|
|
7
|
-
PASS src/t7-release.test.ts (12.705 s)
|
|
8
|
-
PASS src/knowledge.test.ts (12.82 s)
|
|
9
|
-
PASS src/context-mapper.test.ts
|
|
10
|
-
PASS src/runtime.test.ts
|
|
5
|
+
PASS src/context-mapper.test.ts (17.122 s)
|
|
6
|
+
PASS src/runtime.test.ts (17.986 s)
|
|
11
7
|
● Console
|
|
12
8
|
|
|
13
9
|
console.error
|
|
@@ -28,16 +24,20 @@ PASS src/runtime.test.ts
|
|
|
28
24
|
at PerceptionPipeline.execute (../../packages/core/dist/perception-pipeline.js:21:34)
|
|
29
25
|
at Object.<anonymous> (src/runtime.test.ts:74:22)
|
|
30
26
|
|
|
31
|
-
PASS src/
|
|
27
|
+
PASS src/t5-experience.test.ts (19.901 s)
|
|
28
|
+
PASS src/t4-gating.test.ts (19.152 s)
|
|
29
|
+
PASS src/index.test.ts (19.326 s)
|
|
30
|
+
PASS src/teach-mode.test.ts (20.058 s)
|
|
31
|
+
PASS src/t7-release.test.ts
|
|
32
32
|
PASS src/smoke.test.ts
|
|
33
|
-
PASS src/
|
|
34
|
-
PASS src/
|
|
35
|
-
PASS src/
|
|
36
|
-
PASS src/
|
|
37
|
-
PASS src/
|
|
33
|
+
PASS src/auth.test.ts
|
|
34
|
+
PASS src/t6-security.test.ts (19.898 s)
|
|
35
|
+
PASS src/boot.test.ts
|
|
36
|
+
PASS src/b0-b6.test.ts
|
|
37
|
+
PASS src/knowledge.test.ts
|
|
38
38
|
[999D[K
|
|
39
39
|
Test Suites: 13 passed, 13 total
|
|
40
|
-
Tests:
|
|
40
|
+
Tests: 90 passed, 90 total
|
|
41
41
|
Snapshots: 0 total
|
|
42
|
-
Time:
|
|
42
|
+
Time: 23.887 s
|
|
43
43
|
Ran all test suites.
|
package/dist/app.js
CHANGED
|
@@ -114,12 +114,31 @@ function createApp(runtimes = new Map()) {
|
|
|
114
114
|
companionId,
|
|
115
115
|
name: manifest.identity.name,
|
|
116
116
|
archetype: manifest.identity.archetype,
|
|
117
|
+
origin: manifest.identity.origin,
|
|
118
|
+
ethos: manifest.identity.ethos,
|
|
117
119
|
version: manifest.version || '1.0.0',
|
|
118
120
|
updatedAt: new Date().toISOString(),
|
|
119
121
|
});
|
|
120
122
|
if (manifest.personality) {
|
|
121
123
|
await repo.setPersonality(companionId, manifest.personality);
|
|
122
124
|
}
|
|
125
|
+
if (Array.isArray(manifest.relationships)) {
|
|
126
|
+
for (const rel of manifest.relationships) {
|
|
127
|
+
if (rel && rel.entityId) {
|
|
128
|
+
await repo.updateRelationship(companionId, {
|
|
129
|
+
companionId,
|
|
130
|
+
entityId: rel.entityId,
|
|
131
|
+
entityType: 'human',
|
|
132
|
+
role: rel.role || 'user',
|
|
133
|
+
stance: rel.stance || 'neutral',
|
|
134
|
+
interactionConventions: rel.conventions || [],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (Array.isArray(manifest.dialogueExamples) && repo.setExemplars) {
|
|
140
|
+
await repo.setExemplars(companionId, manifest.dialogueExamples);
|
|
141
|
+
}
|
|
123
142
|
if (directivesToCommit.length > 0) {
|
|
124
143
|
await repo.commitDirectives(companionId, directivesToCommit);
|
|
125
144
|
}
|
|
@@ -225,7 +244,7 @@ function createApp(runtimes = new Map()) {
|
|
|
225
244
|
medium: 'web',
|
|
226
245
|
signal: abortController.signal,
|
|
227
246
|
});
|
|
228
|
-
res.write(`event: staged\ndata: ${JSON.stringify({ response_id: response.response_id, correlation_id: response.correlation_id, status: response.status })}\n\n`);
|
|
247
|
+
res.write(`event: staged\ndata: ${JSON.stringify({ response_id: response.response_id, correlation_id: response.correlation_id, status: response.status, mode: response.metadata?.mode })}\n\n`);
|
|
229
248
|
const avatarEvent = response.metadata?.events?.find((e) => (e.kind === 'avatar' || e.kind === 'body') && (e.approval === 'APPROVED' || !e.approval));
|
|
230
249
|
if (avatarEvent) {
|
|
231
250
|
res.write(`event: avatar\ndata: ${JSON.stringify(avatarEvent)}\n\n`);
|
package/dist/context-mapper.js
CHANGED
|
@@ -115,6 +115,7 @@ function mapRequestContext(input, options = {}) {
|
|
|
115
115
|
},
|
|
116
116
|
source: input.source || rawCtx.source || (isAuthenticated ? 'local' : 'external'),
|
|
117
117
|
subject: rawCtx.subject,
|
|
118
|
+
mode: rawCtx.mode || input.mode,
|
|
118
119
|
};
|
|
119
120
|
const validated = (0, core_1.validateRequestContext)(constructed);
|
|
120
121
|
if (!validated.accepted) {
|
|
@@ -218,6 +219,7 @@ function mapRequestContext(input, options = {}) {
|
|
|
218
219
|
},
|
|
219
220
|
source: input.source || (isAuthenticated ? 'local' : 'external'),
|
|
220
221
|
subject: input.subject,
|
|
222
|
+
mode: input.mode || input.conversation?.mode,
|
|
221
223
|
};
|
|
222
224
|
const validated = (0, core_1.validateRequestContext)(mappedContext);
|
|
223
225
|
if (!validated.accepted) {
|
|
@@ -249,4 +249,39 @@ describe('API Request Context Mapper (Single-Owner, Single-Machine)', () => {
|
|
|
249
249
|
expect(attenuatedResult.context?.actor.authorizationRole).toBe('viewer');
|
|
250
250
|
expect(attenuatedResult.diagnostics).not.toContain('role_escalation_attempt_suppressed');
|
|
251
251
|
});
|
|
252
|
+
describe('Mode Mapping (Casual, Teach, Hybrid)', () => {
|
|
253
|
+
test('maps explicit mode parameter to RequestContext', () => {
|
|
254
|
+
const casualResult = (0, context_mapper_1.mapRequestContext)({
|
|
255
|
+
companionId: 'comp-1',
|
|
256
|
+
mode: 'casual',
|
|
257
|
+
generateCorrelationId: true,
|
|
258
|
+
});
|
|
259
|
+
expect(casualResult.accepted).toBe(true);
|
|
260
|
+
expect(casualResult.context?.mode).toBe('casual');
|
|
261
|
+
const teachResult = (0, context_mapper_1.mapRequestContext)({
|
|
262
|
+
companionId: 'comp-1',
|
|
263
|
+
mode: 'teach',
|
|
264
|
+
generateCorrelationId: true,
|
|
265
|
+
});
|
|
266
|
+
expect(teachResult.accepted).toBe(true);
|
|
267
|
+
expect(teachResult.context?.mode).toBe('teach');
|
|
268
|
+
const hybridResult = (0, context_mapper_1.mapRequestContext)({
|
|
269
|
+
companionId: 'comp-1',
|
|
270
|
+
mode: 'hybrid',
|
|
271
|
+
generateCorrelationId: true,
|
|
272
|
+
});
|
|
273
|
+
expect(hybridResult.accepted).toBe(true);
|
|
274
|
+
expect(hybridResult.context?.mode).toBe('hybrid');
|
|
275
|
+
});
|
|
276
|
+
test('rejects invalid interaction mode', () => {
|
|
277
|
+
const invalidResult = (0, context_mapper_1.mapRequestContext)({
|
|
278
|
+
companionId: 'comp-1',
|
|
279
|
+
mode: 'super_turbo',
|
|
280
|
+
generateCorrelationId: true,
|
|
281
|
+
});
|
|
282
|
+
expect(invalidResult.accepted).toBe(false);
|
|
283
|
+
expect(invalidResult.error?.code).toBe('INVALID_CONTEXT');
|
|
284
|
+
expect(invalidResult.error?.field).toBe('mode');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
252
287
|
});
|
package/dist/index.test.js
CHANGED
|
@@ -134,4 +134,18 @@ describe('API Boundary Context Validation (P2 Route Integration)', () => {
|
|
|
134
134
|
expect(res.body.interrupted).toBe(true);
|
|
135
135
|
expect(fakeRuntime.mouth.interrupt).toHaveBeenCalledWith('user_barge_in');
|
|
136
136
|
});
|
|
137
|
+
test('POST /chat passes explicit interaction mode (casual, teach, hybrid) to runtime', async () => {
|
|
138
|
+
const res = await (0, supertest_1.default)(app)
|
|
139
|
+
.post('/chat')
|
|
140
|
+
.send({
|
|
141
|
+
id: 'companion-a',
|
|
142
|
+
message: 'Casual banter',
|
|
143
|
+
mode: 'casual',
|
|
144
|
+
});
|
|
145
|
+
expect(res.status).toBe(200);
|
|
146
|
+
expect(fakeRuntime.handleUserMessage).toHaveBeenCalledWith('Casual banter', expect.objectContaining({
|
|
147
|
+
companionId: 'companion-a',
|
|
148
|
+
mode: 'casual',
|
|
149
|
+
}), []);
|
|
150
|
+
});
|
|
137
151
|
});
|
package/dist/teach-mode.test.js
CHANGED
|
@@ -14,6 +14,8 @@ jest.mock('@siduri-x/self', () => {
|
|
|
14
14
|
SqliteSelfRepository: jest.fn().mockImplementation(() => ({
|
|
15
15
|
setIdentity: jest.fn().mockResolvedValue(undefined),
|
|
16
16
|
setPersonality: jest.fn().mockResolvedValue(undefined),
|
|
17
|
+
updateRelationship: jest.fn().mockResolvedValue(undefined),
|
|
18
|
+
setExemplars: jest.fn().mockResolvedValue(undefined),
|
|
17
19
|
commitDirectives: jest.fn().mockResolvedValue(undefined),
|
|
18
20
|
close: jest.fn(),
|
|
19
21
|
})),
|
|
@@ -133,4 +135,69 @@ kind: "other"
|
|
|
133
135
|
const MockRepo = self_1.SqliteSelfRepository;
|
|
134
136
|
expect(MockRepo.mock.instances.length).toBe(0);
|
|
135
137
|
});
|
|
138
|
+
it('POST /teach/install-self installs v2.0 manifest with ethos, relationships, and exemplars', async () => {
|
|
139
|
+
const v2Manifest = {
|
|
140
|
+
specVersion: '2.0.0',
|
|
141
|
+
identity: {
|
|
142
|
+
name: 'Siduri',
|
|
143
|
+
archetype: 'System Sentinel',
|
|
144
|
+
origin: 'Ancient mythos',
|
|
145
|
+
ethos: 'Protector of the realm and loyal companion to creator',
|
|
146
|
+
},
|
|
147
|
+
version: '2.0.0',
|
|
148
|
+
relationships: [
|
|
149
|
+
{
|
|
150
|
+
entityId: 'actor:zagin',
|
|
151
|
+
role: 'creator',
|
|
152
|
+
stance: 'familiar_loyal',
|
|
153
|
+
conventions: ['Direct communication', 'Highest administrative trust'],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
dialogueExamples: [
|
|
157
|
+
{
|
|
158
|
+
user: 'Deploy current branch',
|
|
159
|
+
assistant: 'Deploying to staging now, boss.',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
directives: [
|
|
163
|
+
{ id: 'dir-rel-1', directive: 'Honor creator root privileges' },
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
const res = await (0, supertest_1.default)(app)
|
|
167
|
+
.post('/teach/install-self')
|
|
168
|
+
.set(mockAuthHeader)
|
|
169
|
+
.send({
|
|
170
|
+
companionId: 'comp-v2',
|
|
171
|
+
manifest: v2Manifest,
|
|
172
|
+
approvedDirectiveIds: ['dir-rel-1'],
|
|
173
|
+
});
|
|
174
|
+
expect(res.status).toBe(200);
|
|
175
|
+
expect(res.body.success).toBe(true);
|
|
176
|
+
const MockRepo = self_1.SqliteSelfRepository;
|
|
177
|
+
const repoInstance = MockRepo.mock.results[MockRepo.mock.results.length - 1].value;
|
|
178
|
+
expect(repoInstance.setIdentity).toHaveBeenCalledWith(expect.objectContaining({
|
|
179
|
+
companionId: 'comp-v2',
|
|
180
|
+
name: 'Siduri',
|
|
181
|
+
archetype: 'System Sentinel',
|
|
182
|
+
origin: 'Ancient mythos',
|
|
183
|
+
ethos: 'Protector of the realm and loyal companion to creator',
|
|
184
|
+
}));
|
|
185
|
+
expect(repoInstance.updateRelationship).toHaveBeenCalledWith('comp-v2', {
|
|
186
|
+
companionId: 'comp-v2',
|
|
187
|
+
entityId: 'actor:zagin',
|
|
188
|
+
entityType: 'human',
|
|
189
|
+
role: 'creator',
|
|
190
|
+
stance: 'familiar_loyal',
|
|
191
|
+
interactionConventions: ['Direct communication', 'Highest administrative trust'],
|
|
192
|
+
});
|
|
193
|
+
expect(repoInstance.setExemplars).toHaveBeenCalledWith('comp-v2', [
|
|
194
|
+
{
|
|
195
|
+
user: 'Deploy current branch',
|
|
196
|
+
assistant: 'Deploying to staging now, boss.',
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
expect(repoInstance.commitDirectives).toHaveBeenCalledWith('comp-v2', [
|
|
200
|
+
{ id: 'dir-rel-1', directive: 'Honor creator root privileges' },
|
|
201
|
+
]);
|
|
202
|
+
});
|
|
136
203
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siduri-x/api",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"dotenv": "^17.4.2",
|
|
14
14
|
"express": "^5.2.1",
|
|
15
15
|
"@siduri-x/body": "2.0.1",
|
|
16
|
+
"@siduri-x/core": "2.0.3",
|
|
16
17
|
"@siduri-x/brain": "2.0.1",
|
|
17
|
-
"@siduri-x/core": "2.0.2",
|
|
18
18
|
"@siduri-x/ear": "2.0.1",
|
|
19
19
|
"@siduri-x/eknowledge": "2.0.1",
|
|
20
20
|
"@siduri-x/hands": "2.0.1",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@siduri-x/memory": "2.0.1",
|
|
23
23
|
"@siduri-x/mouth": "2.0.1",
|
|
24
24
|
"@siduri-x/observation": "2.0.1",
|
|
25
|
-
"@siduri-x/self": "2.0.
|
|
25
|
+
"@siduri-x/self": "2.0.2",
|
|
26
26
|
"@siduri-x/vision": "2.0.1",
|
|
27
27
|
"@siduri-x/voice": "2.0.1"
|
|
28
28
|
},
|
package/siduri.sqlite
ADDED
|
Binary file
|
package/src/app.ts
CHANGED
|
@@ -136,6 +136,8 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
136
136
|
companionId,
|
|
137
137
|
name: manifest.identity.name,
|
|
138
138
|
archetype: manifest.identity.archetype,
|
|
139
|
+
origin: manifest.identity.origin,
|
|
140
|
+
ethos: manifest.identity.ethos,
|
|
139
141
|
version: manifest.version || '1.0.0',
|
|
140
142
|
updatedAt: new Date().toISOString(),
|
|
141
143
|
});
|
|
@@ -144,6 +146,25 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
144
146
|
await repo.setPersonality(companionId, manifest.personality);
|
|
145
147
|
}
|
|
146
148
|
|
|
149
|
+
if (Array.isArray(manifest.relationships)) {
|
|
150
|
+
for (const rel of manifest.relationships) {
|
|
151
|
+
if (rel && rel.entityId) {
|
|
152
|
+
await repo.updateRelationship(companionId, {
|
|
153
|
+
companionId,
|
|
154
|
+
entityId: rel.entityId,
|
|
155
|
+
entityType: 'human',
|
|
156
|
+
role: rel.role || 'user',
|
|
157
|
+
stance: rel.stance || 'neutral',
|
|
158
|
+
interactionConventions: rel.conventions || [],
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (Array.isArray(manifest.dialogueExamples) && repo.setExemplars) {
|
|
165
|
+
await repo.setExemplars(companionId, manifest.dialogueExamples);
|
|
166
|
+
}
|
|
167
|
+
|
|
147
168
|
if (directivesToCommit.length > 0) {
|
|
148
169
|
await repo.commitDirectives(companionId, directivesToCommit);
|
|
149
170
|
}
|
|
@@ -260,7 +281,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
260
281
|
signal: abortController.signal,
|
|
261
282
|
});
|
|
262
283
|
|
|
263
|
-
res.write(`event: staged\ndata: ${JSON.stringify({ response_id: response.response_id, correlation_id: response.correlation_id, status: response.status })}\n\n`);
|
|
284
|
+
res.write(`event: staged\ndata: ${JSON.stringify({ response_id: response.response_id, correlation_id: response.correlation_id, status: response.status, mode: response.metadata?.mode })}\n\n`);
|
|
264
285
|
|
|
265
286
|
const avatarEvent = response.metadata?.events?.find(
|
|
266
287
|
(e: any) => (e.kind === 'avatar' || e.kind === 'body') && (e.approval === 'APPROVED' || !e.approval)
|
|
@@ -277,4 +277,43 @@ describe('API Request Context Mapper (Single-Owner, Single-Machine)', () => {
|
|
|
277
277
|
expect(attenuatedResult.context?.actor.authorizationRole).toBe('viewer');
|
|
278
278
|
expect(attenuatedResult.diagnostics).not.toContain('role_escalation_attempt_suppressed');
|
|
279
279
|
});
|
|
280
|
+
|
|
281
|
+
describe('Mode Mapping (Casual, Teach, Hybrid)', () => {
|
|
282
|
+
test('maps explicit mode parameter to RequestContext', () => {
|
|
283
|
+
const casualResult = mapRequestContext({
|
|
284
|
+
companionId: 'comp-1',
|
|
285
|
+
mode: 'casual',
|
|
286
|
+
generateCorrelationId: true,
|
|
287
|
+
});
|
|
288
|
+
expect(casualResult.accepted).toBe(true);
|
|
289
|
+
expect(casualResult.context?.mode).toBe('casual');
|
|
290
|
+
|
|
291
|
+
const teachResult = mapRequestContext({
|
|
292
|
+
companionId: 'comp-1',
|
|
293
|
+
mode: 'teach',
|
|
294
|
+
generateCorrelationId: true,
|
|
295
|
+
});
|
|
296
|
+
expect(teachResult.accepted).toBe(true);
|
|
297
|
+
expect(teachResult.context?.mode).toBe('teach');
|
|
298
|
+
|
|
299
|
+
const hybridResult = mapRequestContext({
|
|
300
|
+
companionId: 'comp-1',
|
|
301
|
+
mode: 'hybrid',
|
|
302
|
+
generateCorrelationId: true,
|
|
303
|
+
});
|
|
304
|
+
expect(hybridResult.accepted).toBe(true);
|
|
305
|
+
expect(hybridResult.context?.mode).toBe('hybrid');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('rejects invalid interaction mode', () => {
|
|
309
|
+
const invalidResult = mapRequestContext({
|
|
310
|
+
companionId: 'comp-1',
|
|
311
|
+
mode: 'super_turbo',
|
|
312
|
+
generateCorrelationId: true,
|
|
313
|
+
});
|
|
314
|
+
expect(invalidResult.accepted).toBe(false);
|
|
315
|
+
expect(invalidResult.error?.code).toBe('INVALID_CONTEXT');
|
|
316
|
+
expect(invalidResult.error?.field).toBe('mode');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
280
319
|
});
|
package/src/context-mapper.ts
CHANGED
|
@@ -145,6 +145,7 @@ export function mapRequestContext(
|
|
|
145
145
|
},
|
|
146
146
|
source: input.source || rawCtx.source || (isAuthenticated ? 'local' : 'external'),
|
|
147
147
|
subject: rawCtx.subject,
|
|
148
|
+
mode: rawCtx.mode || input.mode,
|
|
148
149
|
};
|
|
149
150
|
|
|
150
151
|
const validated = validateRequestContext(constructed);
|
|
@@ -262,6 +263,7 @@ export function mapRequestContext(
|
|
|
262
263
|
},
|
|
263
264
|
source: input.source || (isAuthenticated ? 'local' : 'external'),
|
|
264
265
|
subject: input.subject,
|
|
266
|
+
mode: input.mode || input.conversation?.mode,
|
|
265
267
|
};
|
|
266
268
|
|
|
267
269
|
const validated = validateRequestContext(mappedContext);
|
package/src/index.test.ts
CHANGED
|
@@ -155,4 +155,24 @@ describe('API Boundary Context Validation (P2 Route Integration)', () => {
|
|
|
155
155
|
expect(res.body.interrupted).toBe(true);
|
|
156
156
|
expect(fakeRuntime.mouth.interrupt).toHaveBeenCalledWith('user_barge_in');
|
|
157
157
|
});
|
|
158
|
+
|
|
159
|
+
test('POST /chat passes explicit interaction mode (casual, teach, hybrid) to runtime', async () => {
|
|
160
|
+
const res = await request(app)
|
|
161
|
+
.post('/chat')
|
|
162
|
+
.send({
|
|
163
|
+
id: 'companion-a',
|
|
164
|
+
message: 'Casual banter',
|
|
165
|
+
mode: 'casual',
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(res.status).toBe(200);
|
|
169
|
+
expect(fakeRuntime.handleUserMessage).toHaveBeenCalledWith(
|
|
170
|
+
'Casual banter',
|
|
171
|
+
expect.objectContaining({
|
|
172
|
+
companionId: 'companion-a',
|
|
173
|
+
mode: 'casual',
|
|
174
|
+
}),
|
|
175
|
+
[]
|
|
176
|
+
);
|
|
177
|
+
});
|
|
158
178
|
});
|
package/src/teach-mode.test.ts
CHANGED
|
@@ -10,6 +10,8 @@ jest.mock('@siduri-x/self', () => {
|
|
|
10
10
|
SqliteSelfRepository: jest.fn().mockImplementation(() => ({
|
|
11
11
|
setIdentity: jest.fn().mockResolvedValue(undefined),
|
|
12
12
|
setPersonality: jest.fn().mockResolvedValue(undefined),
|
|
13
|
+
updateRelationship: jest.fn().mockResolvedValue(undefined),
|
|
14
|
+
setExemplars: jest.fn().mockResolvedValue(undefined),
|
|
13
15
|
commitDirectives: jest.fn().mockResolvedValue(undefined),
|
|
14
16
|
close: jest.fn(),
|
|
15
17
|
})),
|
|
@@ -149,4 +151,79 @@ kind: "other"
|
|
|
149
151
|
const MockRepo = SqliteSelfRepository as jest.MockedClass<typeof SqliteSelfRepository>;
|
|
150
152
|
expect(MockRepo.mock.instances.length).toBe(0);
|
|
151
153
|
});
|
|
154
|
+
|
|
155
|
+
it('POST /teach/install-self installs v2.0 manifest with ethos, relationships, and exemplars', async () => {
|
|
156
|
+
const v2Manifest = {
|
|
157
|
+
specVersion: '2.0.0',
|
|
158
|
+
identity: {
|
|
159
|
+
name: 'Siduri',
|
|
160
|
+
archetype: 'System Sentinel',
|
|
161
|
+
origin: 'Ancient mythos',
|
|
162
|
+
ethos: 'Protector of the realm and loyal companion to creator',
|
|
163
|
+
},
|
|
164
|
+
version: '2.0.0',
|
|
165
|
+
relationships: [
|
|
166
|
+
{
|
|
167
|
+
entityId: 'actor:zagin',
|
|
168
|
+
role: 'creator',
|
|
169
|
+
stance: 'familiar_loyal',
|
|
170
|
+
conventions: ['Direct communication', 'Highest administrative trust'],
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
dialogueExamples: [
|
|
174
|
+
{
|
|
175
|
+
user: 'Deploy current branch',
|
|
176
|
+
assistant: 'Deploying to staging now, boss.',
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
directives: [
|
|
180
|
+
{ id: 'dir-rel-1', directive: 'Honor creator root privileges' },
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const res = await request(app)
|
|
185
|
+
.post('/teach/install-self')
|
|
186
|
+
.set(mockAuthHeader)
|
|
187
|
+
.send({
|
|
188
|
+
companionId: 'comp-v2',
|
|
189
|
+
manifest: v2Manifest,
|
|
190
|
+
approvedDirectiveIds: ['dir-rel-1'],
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(res.status).toBe(200);
|
|
194
|
+
expect(res.body.success).toBe(true);
|
|
195
|
+
|
|
196
|
+
const MockRepo = SqliteSelfRepository as jest.MockedClass<typeof SqliteSelfRepository>;
|
|
197
|
+
const repoInstance = MockRepo.mock.results[MockRepo.mock.results.length - 1].value;
|
|
198
|
+
|
|
199
|
+
expect(repoInstance.setIdentity).toHaveBeenCalledWith(
|
|
200
|
+
expect.objectContaining({
|
|
201
|
+
companionId: 'comp-v2',
|
|
202
|
+
name: 'Siduri',
|
|
203
|
+
archetype: 'System Sentinel',
|
|
204
|
+
origin: 'Ancient mythos',
|
|
205
|
+
ethos: 'Protector of the realm and loyal companion to creator',
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
expect(repoInstance.updateRelationship).toHaveBeenCalledWith('comp-v2', {
|
|
210
|
+
companionId: 'comp-v2',
|
|
211
|
+
entityId: 'actor:zagin',
|
|
212
|
+
entityType: 'human',
|
|
213
|
+
role: 'creator',
|
|
214
|
+
stance: 'familiar_loyal',
|
|
215
|
+
interactionConventions: ['Direct communication', 'Highest administrative trust'],
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(repoInstance.setExemplars).toHaveBeenCalledWith('comp-v2', [
|
|
219
|
+
{
|
|
220
|
+
user: 'Deploy current branch',
|
|
221
|
+
assistant: 'Deploying to staging now, boss.',
|
|
222
|
+
},
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
expect(repoInstance.commitDirectives).toHaveBeenCalledWith('comp-v2', [
|
|
226
|
+
{ id: 'dir-rel-1', directive: 'Honor creator root privileges' },
|
|
227
|
+
]);
|
|
228
|
+
});
|
|
152
229
|
});
|