@saluzi/saluzi-edu 0.4.1 → 0.4.2
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
|
@@ -399,6 +399,91 @@ describe('V1 Session Routes', () => {
|
|
|
399
399
|
body: JSON.stringify({ events: [{ type: 'init', data: 'starting' }] }),
|
|
400
400
|
})
|
|
401
401
|
expect(sessRes.status).toBe(200)
|
|
402
|
+
const { id } = await sessRes.json()
|
|
403
|
+
// Legacy raw shape — published verbatim as before (type from the event,
|
|
404
|
+
// direction outbound).
|
|
405
|
+
const events = getEventBus(id).getEventsSince(0)
|
|
406
|
+
expect(events).toHaveLength(1)
|
|
407
|
+
expect(events[0]?.type).toBe('init')
|
|
408
|
+
expect(events[0]?.direction).toBe('outbound')
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
test('POST /v1/sessions with CCR-wrapped system/init seed — publishes a typed system event the Web UI scan can find', async () => {
|
|
412
|
+
// `slz rcs one`'s headless worker seeds its pre-created session with a
|
|
413
|
+
// system/init so the Web UI has the worker's command/skill tables and
|
|
414
|
+
// capabilities before the first message spawns a --print child. The
|
|
415
|
+
// create route must unwrap { type: 'event', data: SDKMessage } and run
|
|
416
|
+
// the inner message through the standard ingest normalization —
|
|
417
|
+
// publishing the raw wrapper would yield an opaque 'event'-typed entry
|
|
418
|
+
// no consumer (loadHistory worker-info scan, live SSE handler) matches.
|
|
419
|
+
const sessRes = await app.request('/v1/sessions', {
|
|
420
|
+
method: 'POST',
|
|
421
|
+
headers: { ...AUTH_HEADERS, 'Content-Type': 'application/json' },
|
|
422
|
+
body: JSON.stringify({
|
|
423
|
+
events: [
|
|
424
|
+
{
|
|
425
|
+
type: 'event',
|
|
426
|
+
data: {
|
|
427
|
+
type: 'system',
|
|
428
|
+
subtype: 'init',
|
|
429
|
+
uuid: 'seed-init-uuid',
|
|
430
|
+
slash_commands: ['compact'],
|
|
431
|
+
slash_command_details: [
|
|
432
|
+
{
|
|
433
|
+
name: 'compact',
|
|
434
|
+
description: 'Shrink context',
|
|
435
|
+
type: 'local',
|
|
436
|
+
},
|
|
437
|
+
],
|
|
438
|
+
skills: ['code-review'],
|
|
439
|
+
skill_details: [
|
|
440
|
+
{
|
|
441
|
+
name: 'code-review',
|
|
442
|
+
description: 'Review code',
|
|
443
|
+
type: 'prompt',
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
remote_capabilities: {
|
|
447
|
+
file_search: true,
|
|
448
|
+
clear: true,
|
|
449
|
+
set_max_thinking_tokens: true,
|
|
450
|
+
get_context_usage: true,
|
|
451
|
+
stop_task: true,
|
|
452
|
+
set_model: true,
|
|
453
|
+
set_permission_mode: true,
|
|
454
|
+
interrupt: true,
|
|
455
|
+
end_session: true,
|
|
456
|
+
mcp_management: true,
|
|
457
|
+
remote_settings: true,
|
|
458
|
+
rewind: true,
|
|
459
|
+
set_effort: true,
|
|
460
|
+
side_question: true,
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
],
|
|
465
|
+
}),
|
|
466
|
+
})
|
|
467
|
+
expect(sessRes.status).toBe(200)
|
|
468
|
+
const { id } = await sessRes.json()
|
|
469
|
+
|
|
470
|
+
const events = getEventBus(id).getEventsSince(0)
|
|
471
|
+
expect(events).toHaveLength(1)
|
|
472
|
+
expect(events[0]?.type).toBe('system')
|
|
473
|
+
expect(events[0]?.direction).toBe('inbound')
|
|
474
|
+
const payload = events[0]?.payload as Record<string, unknown>
|
|
475
|
+
expect(payload.subtype).toBe('init')
|
|
476
|
+
expect(payload.slash_command_details).toEqual([
|
|
477
|
+
{ name: 'compact', description: 'Shrink context', type: 'local' },
|
|
478
|
+
])
|
|
479
|
+
expect(payload.skill_details).toEqual([
|
|
480
|
+
{ name: 'code-review', description: 'Review code', type: 'prompt' },
|
|
481
|
+
])
|
|
482
|
+
expect(
|
|
483
|
+
(payload.remote_capabilities as Record<string, unknown>).set_model,
|
|
484
|
+
).toBe(true)
|
|
485
|
+
// raw keeps the full SDK message for the adapter's payload.raw fallback.
|
|
486
|
+
expect((payload.raw as Record<string, unknown>).subtype).toBe('init')
|
|
402
487
|
})
|
|
403
488
|
})
|
|
404
489
|
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '../../services/session'
|
|
12
12
|
import { createWorkItem } from '../../services/work-dispatch'
|
|
13
13
|
import { publishSessionEvent } from '../../services/transport'
|
|
14
|
+
import { ingestBridgeMessage } from '../../transport/ws-handler'
|
|
14
15
|
import { loadPersistedEvents } from '../../services/event-persistence'
|
|
15
16
|
import { getEventBus } from '../../transport/event-bus'
|
|
16
17
|
import { touchEnvironmentActivity } from '../../services/environment'
|
|
@@ -225,7 +226,26 @@ app.post('/', acceptCliHeaders, apiKeyAuth, async c => {
|
|
|
225
226
|
// Publish initial events if provided
|
|
226
227
|
if (body.events && Array.isArray(body.events)) {
|
|
227
228
|
for (const evt of body.events) {
|
|
228
|
-
|
|
229
|
+
// CCR wire format { type: 'event', data: SDKMessage } — unwrap and run
|
|
230
|
+
// the inner SDK message through the standard ingest path (event-type
|
|
231
|
+
// derivation + payload normalization, e.g. system/init's
|
|
232
|
+
// slash_command_details / remote_capabilities preservation). Publishing
|
|
233
|
+
// the raw wrapper as-is yields an opaque 'event'-typed entry no
|
|
234
|
+
// consumer recognizes.
|
|
235
|
+
const data =
|
|
236
|
+
evt &&
|
|
237
|
+
typeof evt === 'object' &&
|
|
238
|
+
evt.type === 'event' &&
|
|
239
|
+
evt.data &&
|
|
240
|
+
typeof evt.data === 'object' &&
|
|
241
|
+
typeof evt.data.type === 'string'
|
|
242
|
+
? evt.data
|
|
243
|
+
: null
|
|
244
|
+
if (data) {
|
|
245
|
+
ingestBridgeMessage(session.id, data)
|
|
246
|
+
} else {
|
|
247
|
+
publishSessionEvent(session.id, evt.type || 'init', evt, 'outbound')
|
|
248
|
+
}
|
|
229
249
|
}
|
|
230
250
|
}
|
|
231
251
|
|