@pellux/goodvibes-daemon-sdk 0.38.0 → 1.1.0
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/artifact-upload.d.ts.map +1 -1
- package/dist/artifact-upload.js +53 -2
- package/dist/context.d.ts +17 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/control-routes.d.ts.map +1 -1
- package/dist/control-routes.js +12 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integration-route-types.d.ts +62 -0
- package/dist/integration-route-types.d.ts.map +1 -1
- package/dist/integration-routes.d.ts.map +1 -1
- package/dist/integration-routes.js +45 -0
- package/dist/memory-record-body.d.ts +22 -0
- package/dist/memory-record-body.d.ts.map +1 -0
- package/dist/memory-record-body.js +110 -0
- package/dist/operator.js +15 -0
- package/dist/runtime-route-types.d.ts +46 -3
- package/dist/runtime-route-types.d.ts.map +1 -1
- package/dist/runtime-session-lifecycle-routes.d.ts +14 -0
- package/dist/runtime-session-lifecycle-routes.d.ts.map +1 -0
- package/dist/runtime-session-lifecycle-routes.js +68 -0
- package/dist/runtime-session-register.d.ts +9 -0
- package/dist/runtime-session-register.d.ts.map +1 -0
- package/dist/runtime-session-register.js +56 -0
- package/dist/runtime-session-routes.d.ts +40 -8
- package/dist/runtime-session-routes.d.ts.map +1 -1
- package/dist/runtime-session-routes.js +100 -65
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +16 -5
- package/dist/system-route-types.d.ts +48 -0
- package/dist/system-route-types.d.ts.map +1 -1
- package/dist/system-routes.d.ts.map +1 -1
- package/dist/system-routes.js +59 -7
- package/package.json +3 -3
|
@@ -1,16 +1,33 @@
|
|
|
1
|
+
import { handleRegisterSharedSession } from './runtime-session-register.js';
|
|
2
|
+
import { handleDeleteSharedSession, handleGetSharedSession, handleSharedSessionDetach, handleSharedSessionLifecycle, } from './runtime-session-lifecycle-routes.js';
|
|
1
3
|
import { withAdmin } from './auth-helpers.js';
|
|
2
4
|
import { randomUUID } from 'node:crypto';
|
|
3
5
|
import { jsonErrorResponse } from './error-response.js';
|
|
6
|
+
import { SDKErrorCodes } from '@pellux/goodvibes-errors';
|
|
4
7
|
import { createRouteBodySchema, createRouteBodySchemaRegistry, isJsonRecord, readBoundedPositiveInteger, readOptionalStringField, readStringArrayField, } from './route-helpers.js';
|
|
5
8
|
const DEFAULT_LIST_LIMIT = 100;
|
|
6
9
|
const MAX_LIST_LIMIT = 500;
|
|
7
10
|
const MAX_SESSION_TOOL_NAMES = 64;
|
|
8
|
-
const SHARED_SESSION_KINDS = new Set(['tui', 'companion-task', 'companion-chat']);
|
|
11
|
+
export const SHARED_SESSION_KINDS = new Set(['tui', 'agent', 'webui', 'companion-task', 'companion-chat', 'automation']);
|
|
9
12
|
const SHARED_SESSION_STATUSES = new Set(['active', 'closed']);
|
|
10
13
|
function readBoundedLimit(url, key = 'limit') {
|
|
11
14
|
return readBoundedPositiveInteger(url.searchParams.get(key), DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT);
|
|
12
15
|
}
|
|
13
|
-
|
|
16
|
+
/** Runs a submit/steer/follow-up broker call, converting its closed-session
|
|
17
|
+
* guard throw ({ code: SDKErrorCodes.SESSION_CLOSED, status: 409 }, thrown
|
|
18
|
+
* before any mutation) into the same structured 409 other routes return. */
|
|
19
|
+
async function callOrSessionClosed(fn) {
|
|
20
|
+
try {
|
|
21
|
+
return await fn();
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
const closed = err;
|
|
25
|
+
if (closed.code !== SDKErrorCodes.SESSION_CLOSED)
|
|
26
|
+
throw err;
|
|
27
|
+
return jsonErrorResponse({ error: 'Session is closed', code: SDKErrorCodes.SESSION_CLOSED }, { status: closed.status ?? 409 });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function toSharedSessionRecordResponse(sessionId, session, options = {}) {
|
|
14
31
|
const record = isJsonRecord(session) ? session : {};
|
|
15
32
|
const id = readNonEmptyString(record.id) ?? sessionId;
|
|
16
33
|
const now = Date.now();
|
|
@@ -24,9 +41,12 @@ function toSharedSessionRecordResponse(sessionId, session, options = {}) {
|
|
|
24
41
|
const activeAgentId = readNonEmptyString(record.activeAgentId);
|
|
25
42
|
const lastAgentId = readNonEmptyString(record.lastAgentId);
|
|
26
43
|
const lastError = readNonEmptyString(record.lastError);
|
|
44
|
+
const messageCount = Math.max(readFiniteNumber(record.messageCount) ?? 0, options.messageCount ?? 0);
|
|
45
|
+
const retained = readFiniteNumber(record.retainedMessageCount);
|
|
27
46
|
return {
|
|
28
47
|
id,
|
|
29
48
|
kind,
|
|
49
|
+
project: readNonEmptyString(record.project) ?? 'unknown',
|
|
30
50
|
title: readNonEmptyString(record.title) ?? `Session ${id}`,
|
|
31
51
|
status,
|
|
32
52
|
createdAt,
|
|
@@ -34,7 +54,8 @@ function toSharedSessionRecordResponse(sessionId, session, options = {}) {
|
|
|
34
54
|
...(lastMessageAt !== undefined ? { lastMessageAt } : {}),
|
|
35
55
|
...(closedAt !== undefined ? { closedAt } : {}),
|
|
36
56
|
lastActivityAt,
|
|
37
|
-
messageCount
|
|
57
|
+
messageCount,
|
|
58
|
+
...(retained !== undefined && retained < messageCount ? { retainedMessageCount: retained } : {}),
|
|
38
59
|
pendingInputCount: Math.max(readFiniteNumber(record.pendingInputCount) ?? 0, options.pendingInputCount ?? 0),
|
|
39
60
|
routeIds: readStringArray(record.routeIds),
|
|
40
61
|
surfaceKinds: readStringArray(record.surfaceKinds),
|
|
@@ -97,16 +118,20 @@ function readParticipants(value) {
|
|
|
97
118
|
export function createDaemonRuntimeSessionRouteHandlers(context) {
|
|
98
119
|
return {
|
|
99
120
|
createSharedSession: async (request) => withAdmin(context, request, () => handleCreateSharedSession(context, request)),
|
|
121
|
+
registerSharedSession: async (request) => withAdmin(context, request, () => handleRegisterSharedSession(context, request)),
|
|
100
122
|
postTask: async (request) => withAdmin(context, request, () => handlePostTask(context, request)),
|
|
101
123
|
getSharedSession: async (sessionId) => handleGetSharedSession(context, sessionId),
|
|
102
124
|
closeSharedSession: (sessionId, request) => withAdmin(context, request, () => handleSharedSessionLifecycle(context, sessionId, 'close')),
|
|
103
125
|
reopenSharedSession: (sessionId, request) => withAdmin(context, request, () => handleSharedSessionLifecycle(context, sessionId, 'reopen')),
|
|
126
|
+
detachSharedSession: (sessionId, request) => withAdmin(context, request, () => handleSharedSessionDetach(context, sessionId, request)),
|
|
127
|
+
deleteSharedSession: (sessionId, request) => withAdmin(context, request, () => handleDeleteSharedSession(context, sessionId)),
|
|
104
128
|
getSharedSessionMessages: async (sessionId, url) => handleGetSharedSessionMessages(context, sessionId, url),
|
|
105
129
|
getSharedSessionInputs: async (sessionId, url) => handleGetSharedSessionInputs(context, sessionId, url),
|
|
106
130
|
postSharedSessionMessage: (sessionId, request) => withAdmin(context, request, () => handlePostSharedSessionMessage(context, sessionId, request)),
|
|
107
131
|
postSharedSessionSteer: (sessionId, request) => withAdmin(context, request, () => handlePostSharedSessionSteer(context, sessionId, request)),
|
|
108
132
|
postSharedSessionFollowUp: (sessionId, request) => withAdmin(context, request, () => handlePostSharedSessionFollowUp(context, sessionId, request)),
|
|
109
133
|
cancelSharedSessionInput: (sessionId, inputId, request) => withAdmin(context, request, () => handleCancelSharedSessionInput(context, sessionId, inputId)),
|
|
134
|
+
deliverSharedSessionInput: (sessionId, inputId, request) => withAdmin(context, request, () => handleDeliverSharedSessionInput(context, sessionId, inputId, request)),
|
|
110
135
|
getRuntimeTask: (taskId) => handleGetRuntimeTask(context, taskId),
|
|
111
136
|
runtimeTaskAction: (taskId, action, request) => withAdmin(context, request, () => handleRuntimeTaskAction(context, taskId, action, request)),
|
|
112
137
|
getTaskStatus: (agentId) => handleGetTaskStatus(context, agentId),
|
|
@@ -169,7 +194,7 @@ async function handlePostTask(context, req) {
|
|
|
169
194
|
return input;
|
|
170
195
|
const wantsSharedSession = typeof body.sessionId === 'string' || typeof body.routeId === 'string' || typeof body.surfaceKind === 'string';
|
|
171
196
|
if (wantsSharedSession) {
|
|
172
|
-
const submission = await context.sessionBroker.submitMessage({
|
|
197
|
+
const submission = await callOrSessionClosed(() => context.sessionBroker.submitMessage({
|
|
173
198
|
sessionId: typeof body.sessionId === 'string' ? body.sessionId : undefined,
|
|
174
199
|
routeId: typeof body.routeId === 'string' ? body.routeId : undefined,
|
|
175
200
|
surfaceKind: typeof body.surfaceKind === 'string' ? body.surfaceKind : 'web',
|
|
@@ -182,7 +207,9 @@ async function handlePostTask(context, req) {
|
|
|
182
207
|
body: input.task,
|
|
183
208
|
metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
|
|
184
209
|
...(input.routing ? { routing: input.routing } : {}),
|
|
185
|
-
});
|
|
210
|
+
}));
|
|
211
|
+
if (submission instanceof Response)
|
|
212
|
+
return submission;
|
|
186
213
|
if (submission.mode === 'continued-live') {
|
|
187
214
|
return context.recordApiResponse(req, '/task', Response.json({
|
|
188
215
|
acknowledged: true,
|
|
@@ -258,27 +285,6 @@ async function handlePostTask(context, req) {
|
|
|
258
285
|
tools: spawnResult.tools,
|
|
259
286
|
}, { status: 202 }));
|
|
260
287
|
}
|
|
261
|
-
async function handleGetSharedSession(context, sessionId) {
|
|
262
|
-
await context.sessionBroker.start();
|
|
263
|
-
const session = context.sessionBroker.getSession(sessionId);
|
|
264
|
-
if (!session) {
|
|
265
|
-
return jsonErrorResponse({ error: 'Unknown shared session' }, { status: 404 });
|
|
266
|
-
}
|
|
267
|
-
const messages = context.sessionBroker.getMessages(sessionId, 100);
|
|
268
|
-
return Response.json({
|
|
269
|
-
session: toSharedSessionRecordResponse(sessionId, session, { messageCount: messages.length }),
|
|
270
|
-
messages,
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
async function handleSharedSessionLifecycle(context, sessionId, action) {
|
|
274
|
-
await context.sessionBroker.start();
|
|
275
|
-
const session = action === 'close'
|
|
276
|
-
? await context.sessionBroker.closeSession(sessionId)
|
|
277
|
-
: await context.sessionBroker.reopenSession(sessionId);
|
|
278
|
-
return session
|
|
279
|
-
? Response.json({ session: toSharedSessionRecordResponse(sessionId, session, { status: action === 'close' ? 'closed' : 'active' }) })
|
|
280
|
-
: jsonErrorResponse({ error: 'Unknown shared session' }, { status: 404 });
|
|
281
|
-
}
|
|
282
288
|
async function handleGetSharedSessionMessages(context, sessionId, url) {
|
|
283
289
|
await context.sessionBroker.start();
|
|
284
290
|
const session = context.sessionBroker.getSession(sessionId);
|
|
@@ -299,23 +305,54 @@ async function handleGetSharedSessionInputs(context, sessionId, url) {
|
|
|
299
305
|
return jsonErrorResponse({ error: 'Unknown shared session' }, { status: 404 });
|
|
300
306
|
}
|
|
301
307
|
const limit = readBoundedLimit(url);
|
|
302
|
-
|
|
308
|
+
// Collection cursor for live surfaces: filter by state (e.g. 'queued') and/or a
|
|
309
|
+
// `since` createdAt cursor (exclusive) so a surface drains only inputs it has
|
|
310
|
+
// not collected yet. Absent params preserve the legacy "last N inputs" behavior.
|
|
311
|
+
const stateParam = readSessionInputState(url.searchParams.get('state'));
|
|
312
|
+
const sinceParam = readPositiveInt(url.searchParams.get('since'));
|
|
313
|
+
const inputs = (stateParam !== undefined || sinceParam !== undefined)
|
|
314
|
+
? context.sessionBroker.getInputsSince(sessionId, {
|
|
315
|
+
...(stateParam !== undefined ? { state: stateParam } : {}),
|
|
316
|
+
...(sinceParam !== undefined ? { since: sinceParam } : {}),
|
|
317
|
+
limit,
|
|
318
|
+
})
|
|
319
|
+
: context.sessionBroker.getInputs(sessionId, limit);
|
|
303
320
|
return Response.json({
|
|
304
321
|
session: toSharedSessionRecordResponse(sessionId, session, { pendingInputCount: inputs.length }),
|
|
305
322
|
inputs,
|
|
306
323
|
});
|
|
307
324
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
325
|
+
const SHARED_SESSION_INPUT_STATES = new Set([
|
|
326
|
+
'queued', 'delivered', 'spawned', 'completed', 'cancelled', 'failed', 'rejected',
|
|
327
|
+
]);
|
|
328
|
+
function readSessionInputState(value) {
|
|
329
|
+
return value !== null && SHARED_SESSION_INPUT_STATES.has(value) ? value : undefined;
|
|
330
|
+
}
|
|
331
|
+
function readPositiveInt(value) {
|
|
332
|
+
if (value === null)
|
|
333
|
+
return undefined;
|
|
334
|
+
const parsed = Number(value);
|
|
335
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
|
|
336
|
+
}
|
|
337
|
+
/** Handle POST /api/sessions/:sessionId/inputs/:inputId/deliver — a live surface reports
|
|
338
|
+
* collection (`{consumed:true}` = completed, else delivered); optional body, null/bare = consumed:false. */
|
|
339
|
+
async function handleDeliverSharedSessionInput(context, sessionId, inputId, req) {
|
|
340
|
+
const body = await context.parseOptionalJsonBody(req);
|
|
341
|
+
if (body instanceof Response)
|
|
342
|
+
return body;
|
|
343
|
+
const consumed = body?.consumed === true;
|
|
344
|
+
const input = await context.sessionBroker.markInputDelivered(sessionId, inputId, { consumed });
|
|
345
|
+
if (!input) {
|
|
346
|
+
return jsonErrorResponse({ error: 'Unknown shared session input' }, { status: 404 });
|
|
347
|
+
}
|
|
348
|
+
return context.recordApiResponse(req, `/api/sessions/${sessionId}/inputs/${inputId}/deliver`, Response.json({ input }, { status: 200 }));
|
|
349
|
+
}
|
|
350
|
+
/** Handle POST /api/sessions/:sessionId/messages. Accepts `{body}`; 400 when absent/empty. */
|
|
313
351
|
async function handlePostSharedSessionMessage(context, sessionId, req) {
|
|
314
352
|
const body = await context.parseJsonBody(req);
|
|
315
353
|
if (body instanceof Response)
|
|
316
354
|
return body;
|
|
317
|
-
//
|
|
318
|
-
// routing; callers must explicitly opt into kind='task' for agent/WRFC work.
|
|
355
|
+
// Ordinary session messages default to conversation routing; callers must opt into kind='task' for agent/WRFC work.
|
|
319
356
|
const kind = body.kind === undefined ? 'message' : body.kind;
|
|
320
357
|
if (kind !== 'task' && kind !== 'message' && kind !== 'followup') {
|
|
321
358
|
return jsonErrorResponse({ error: `Invalid kind '${String(kind)}'. Accepted values: 'task' | 'message' | 'followup'`, code: 'INVALID_KIND' }, { status: 400 });
|
|
@@ -327,7 +364,9 @@ async function handlePostSharedSessionMessage(context, sessionId, req) {
|
|
|
327
364
|
const input = buildSharedSessionMessageInput(sessionId, body, message);
|
|
328
365
|
// kind='followup' — always queues/spawns a follow-up turn via followUpMessage()
|
|
329
366
|
if (kind === 'followup') {
|
|
330
|
-
const followUpSubmission = await context.sessionBroker.followUpMessage(input);
|
|
367
|
+
const followUpSubmission = await callOrSessionClosed(() => context.sessionBroker.followUpMessage(input));
|
|
368
|
+
if (followUpSubmission instanceof Response)
|
|
369
|
+
return followUpSubmission;
|
|
331
370
|
return await respondToSessionSubmission(context, req, followUpSubmission, message, `/api/sessions/${sessionId}/messages`, 'DaemonServer.handlePostSharedSessionMessage.followup', {
|
|
332
371
|
context: `shared-session:${followUpSubmission.session.id}`,
|
|
333
372
|
});
|
|
@@ -337,24 +376,22 @@ async function handlePostSharedSessionMessage(context, sessionId, req) {
|
|
|
337
376
|
if (kind === 'message') {
|
|
338
377
|
return handleCompanionMessageKind(context, sessionId, req, input);
|
|
339
378
|
}
|
|
340
|
-
const submission = await context.sessionBroker.submitMessage(input);
|
|
379
|
+
const submission = await callOrSessionClosed(() => context.sessionBroker.submitMessage(input));
|
|
380
|
+
if (submission instanceof Response)
|
|
381
|
+
return submission;
|
|
341
382
|
return await respondToSessionSubmission(context, req, submission, message, `/api/sessions/${sessionId}/messages`, 'DaemonServer.handlePostSharedSessionMessage', {
|
|
342
383
|
context: `shared-session:${submission.session.id}`,
|
|
343
384
|
});
|
|
344
385
|
}
|
|
345
|
-
/**
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
* Short-circuits before sessionBroker.submitMessage() so conversation messages
|
|
349
|
-
* are routed to the existing session instead of spawning continuation work.
|
|
350
|
-
*/
|
|
386
|
+
/** Handles kind='message' companion main-chat messages — short-circuits before
|
|
387
|
+
* sessionBroker.submitMessage() so conversation messages route to the existing session instead of spawning continuation work. */
|
|
351
388
|
async function handleCompanionMessageKind(context, sessionId, req, input) {
|
|
352
389
|
const session = context.sessionBroker.getSession(sessionId);
|
|
353
390
|
if (!session) {
|
|
354
391
|
return jsonErrorResponse({ error: 'Unknown shared session', code: 'SESSION_NOT_FOUND' }, { status: 404 });
|
|
355
392
|
}
|
|
356
393
|
if (session.status === 'closed') {
|
|
357
|
-
return jsonErrorResponse({ error: 'Session is closed', code:
|
|
394
|
+
return jsonErrorResponse({ error: 'Session is closed', code: SDKErrorCodes.SESSION_CLOSED }, { status: 409 });
|
|
358
395
|
}
|
|
359
396
|
const messageId = `companion-${randomUUID()}`;
|
|
360
397
|
const timestamp = Date.now();
|
|
@@ -368,9 +405,8 @@ async function handleCompanionMessageKind(context, sessionId, req, input) {
|
|
|
368
405
|
timestamp,
|
|
369
406
|
...(metadata ? { metadata } : {}),
|
|
370
407
|
});
|
|
371
|
-
// Notify in-process subscribers via the conversation follow-up event
|
|
372
|
-
// runtime subscriber turns
|
|
373
|
-
// turn whose events stream to both local and remote clients.
|
|
408
|
+
// Notify in-process subscribers via the conversation follow-up event (the
|
|
409
|
+
// runtime subscriber turns it into a normal conversation turn that streams out).
|
|
374
410
|
context.publishConversationFollowup(sessionId, {
|
|
375
411
|
messageId,
|
|
376
412
|
body: input.body,
|
|
@@ -378,8 +414,7 @@ async function handleCompanionMessageKind(context, sessionId, req, input) {
|
|
|
378
414
|
timestamp,
|
|
379
415
|
...(metadata ? { metadata } : {}),
|
|
380
416
|
});
|
|
381
|
-
//
|
|
382
|
-
// was received and persisted (isConversationRouteResult check).
|
|
417
|
+
// { routedTo: 'conversation' } signals the companion app the message was persisted.
|
|
383
418
|
return context.recordApiResponse(req, `/api/sessions/${sessionId}/messages`, Response.json({
|
|
384
419
|
messageId,
|
|
385
420
|
routedTo: 'conversation',
|
|
@@ -408,11 +443,8 @@ function buildCompanionMessageMetadata(input) {
|
|
|
408
443
|
metadata.displayName = input.displayName;
|
|
409
444
|
return Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
410
445
|
}
|
|
411
|
-
/**
|
|
412
|
-
*
|
|
413
|
-
* for the companion app to receive turn events (STREAM_DELTA, TURN_COMPLETED,
|
|
414
|
-
* etc.) and agent events from the shared session.
|
|
415
|
-
*/
|
|
446
|
+
/** Handle GET /api/sessions/:id/events — a session-scoped SSE stream for turn events
|
|
447
|
+
* (STREAM_DELTA, TURN_COMPLETED, etc.) and agent events. */
|
|
416
448
|
async function handleGetSharedSessionEvents(context, sessionId, req) {
|
|
417
449
|
await context.sessionBroker.start();
|
|
418
450
|
const session = context.sessionBroker.getSession(sessionId);
|
|
@@ -431,10 +463,12 @@ async function handlePostSharedSessionSteer(context, sessionId, req) {
|
|
|
431
463
|
if (!message) {
|
|
432
464
|
return jsonErrorResponse({ error: 'Missing shared session steer body' }, { status: 400 });
|
|
433
465
|
}
|
|
434
|
-
const submission = await context.sessionBroker.steerMessage({
|
|
466
|
+
const submission = await callOrSessionClosed(() => context.sessionBroker.steerMessage({
|
|
435
467
|
...buildSharedSessionMessageInput(sessionId, body, message),
|
|
436
468
|
...(body.allowSpawnFallback === true ? { allowSpawnFallback: true } : {}),
|
|
437
|
-
});
|
|
469
|
+
}));
|
|
470
|
+
if (submission instanceof Response)
|
|
471
|
+
return submission;
|
|
438
472
|
return await respondToSessionSubmission(context, req, submission, message, `/api/sessions/${sessionId}/steer`, 'DaemonServer.handlePostSharedSessionSteer', {
|
|
439
473
|
context: `shared-session:${submission.session.id}`,
|
|
440
474
|
});
|
|
@@ -447,7 +481,9 @@ async function handlePostSharedSessionFollowUp(context, sessionId, req) {
|
|
|
447
481
|
if (!message) {
|
|
448
482
|
return jsonErrorResponse({ error: 'Missing shared session follow-up body' }, { status: 400 });
|
|
449
483
|
}
|
|
450
|
-
const submission = await context.sessionBroker.followUpMessage(buildSharedSessionMessageInput(sessionId, body, message));
|
|
484
|
+
const submission = await callOrSessionClosed(() => context.sessionBroker.followUpMessage(buildSharedSessionMessageInput(sessionId, body, message)));
|
|
485
|
+
if (submission instanceof Response)
|
|
486
|
+
return submission;
|
|
451
487
|
return await respondToSessionSubmission(context, req, submission, message, `/api/sessions/${sessionId}/follow-up`, 'DaemonServer.handlePostSharedSessionFollowUp', {
|
|
452
488
|
context: `shared-session:${submission.session.id}`,
|
|
453
489
|
});
|
|
@@ -477,14 +513,8 @@ function handleGetRuntimeTask(context, taskId) {
|
|
|
477
513
|
return Response.json({ task });
|
|
478
514
|
}
|
|
479
515
|
/**
|
|
480
|
-
* Extract the
|
|
481
|
-
*
|
|
482
|
-
* Accepts the canonical `body` field from the request envelope.
|
|
483
|
-
* Returns an empty string when the field is absent or not a string; the
|
|
484
|
-
* caller must check for an empty result and return 400.
|
|
485
|
-
*
|
|
486
|
-
* @param body - Parsed JSON body from the request.
|
|
487
|
-
* @returns Trimmed message string, or '' if none of the accepted fields are present.
|
|
516
|
+
* Extract the canonical `body` field from an incoming POST envelope. Returns ''
|
|
517
|
+
* when absent or non-string; the caller must check for '' and return 400.
|
|
488
518
|
*/
|
|
489
519
|
export function readSharedSessionMessageBody(body) {
|
|
490
520
|
return typeof body.body === 'string' ? body.body.trim() : '';
|
|
@@ -551,7 +581,12 @@ function readHelperModel(value) {
|
|
|
551
581
|
: undefined;
|
|
552
582
|
}
|
|
553
583
|
async function respondToSessionSubmission(context, req, submission, taskText, path, logLabel, spawnOptions = {}) {
|
|
554
|
-
if (submission.mode === 'continued-live' ||
|
|
584
|
+
if (submission.mode === 'continued-live' ||
|
|
585
|
+
submission.mode === 'queued-follow-up' ||
|
|
586
|
+
submission.mode === 'queued-for-surface') {
|
|
587
|
+
// queued-for-surface: the input is queued for a live registered surface to
|
|
588
|
+
// collect (sessions.inputs.list) and deliver (sessions.inputs.deliver). No
|
|
589
|
+
// daemon executor is spawned — the surface owns turn execution.
|
|
555
590
|
return context.recordApiResponse(req, path, Response.json({
|
|
556
591
|
session: toSharedSessionRecordResponse(submission.session.id, submission.session),
|
|
557
592
|
message: submission.userMessage ?? null,
|
package/dist/sessions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAE/D,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,0BAA0B,GACnC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAE/D,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,0BAA0B,GACnC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAgD1B"}
|
package/dist/sessions.js
CHANGED
|
@@ -4,16 +4,23 @@ export async function dispatchSessionRoutes(req, handlers) {
|
|
|
4
4
|
const method = req.method;
|
|
5
5
|
if (pathname === '/api/sessions' && method === 'GET')
|
|
6
6
|
return handlers.getIntegrationSessions();
|
|
7
|
+
if (pathname === '/api/sessions/register' && method === 'POST')
|
|
8
|
+
return handlers.registerSharedSession(req);
|
|
7
9
|
if (pathname === '/api/sessions' && method === 'POST')
|
|
8
10
|
return handlers.createSharedSession(req);
|
|
9
11
|
const sharedSessionMatch = pathname.match(/^\/api\/sessions\/([^/]+)$/);
|
|
10
12
|
if (sharedSessionMatch && method === 'GET')
|
|
11
13
|
return handlers.getSharedSession(sharedSessionMatch[1]);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (sharedSessionMatch && method === 'DELETE')
|
|
15
|
+
return handlers.deleteSharedSession(sharedSessionMatch[1], req);
|
|
16
|
+
const sharedSessionLifecycleMatch = pathname.match(/^\/api\/sessions\/([^/]+)\/(close|reopen|detach)$/);
|
|
17
|
+
if (sharedSessionLifecycleMatch && method === 'POST') {
|
|
18
|
+
const [, sessionId, action] = sharedSessionLifecycleMatch;
|
|
19
|
+
if (action === 'close')
|
|
20
|
+
return handlers.closeSharedSession(sessionId, req);
|
|
21
|
+
if (action === 'reopen')
|
|
22
|
+
return handlers.reopenSharedSession(sessionId, req);
|
|
23
|
+
return handlers.detachSharedSession(sessionId, req);
|
|
17
24
|
}
|
|
18
25
|
const sharedSessionMessagesMatch = pathname.match(/^\/api\/sessions\/([^/]+)\/messages$/);
|
|
19
26
|
if (sharedSessionMessagesMatch && method === 'GET')
|
|
@@ -33,6 +40,10 @@ export async function dispatchSessionRoutes(req, handlers) {
|
|
|
33
40
|
if (sharedSessionCancelInputMatch && method === 'POST') {
|
|
34
41
|
return handlers.cancelSharedSessionInput(sharedSessionCancelInputMatch[1], sharedSessionCancelInputMatch[2], req);
|
|
35
42
|
}
|
|
43
|
+
const sharedSessionDeliverInputMatch = pathname.match(/^\/api\/sessions\/([^/]+)\/inputs\/([^/]+)\/deliver$/);
|
|
44
|
+
if (sharedSessionDeliverInputMatch && method === 'POST') {
|
|
45
|
+
return handlers.deliverSharedSessionInput(sharedSessionDeliverInputMatch[1], sharedSessionDeliverInputMatch[2], req);
|
|
46
|
+
}
|
|
36
47
|
const sharedSessionEventsMatch = pathname.match(/^\/api\/sessions\/([^/]+)\/events$/);
|
|
37
48
|
if (sharedSessionEventsMatch && method === 'GET')
|
|
38
49
|
return handlers.getSharedSessionEvents(sharedSessionEventsMatch[1], req);
|
|
@@ -101,6 +101,13 @@ export interface ApprovalBrokerLike {
|
|
|
101
101
|
readonly actor: string;
|
|
102
102
|
readonly actorSurface: string;
|
|
103
103
|
readonly note?: string | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Optional per-hunk selection (edit-tool approvals only). The broker
|
|
106
|
+
* filters the approval's own edit list to these indices server-side. An
|
|
107
|
+
* out-of-range index or a non-edit approval throws a 400-tagged error the
|
|
108
|
+
* route layer converts to an HTTP 400.
|
|
109
|
+
*/
|
|
110
|
+
readonly selectedHunks?: readonly number[] | undefined;
|
|
104
111
|
}): Promise<unknown | null>;
|
|
105
112
|
}
|
|
106
113
|
export interface WorkspaceSwapManagerLike {
|
|
@@ -120,9 +127,50 @@ export interface WorkspaceSwapManagerLike {
|
|
|
120
127
|
reason: string;
|
|
121
128
|
}>;
|
|
122
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Secret-free status metadata for a single credential held in the daemon's
|
|
132
|
+
* shared store (or overridden by an environment variable). This is the ONLY
|
|
133
|
+
* shape the credential-read wire method (`credentials.get`) ever returns — the
|
|
134
|
+
* plaintext secret value never crosses the wire. `usable` reflects a real
|
|
135
|
+
* in-process resolution attempt, so a configured-but-unresolvable reference
|
|
136
|
+
* (e.g. a broken `op://` ref) reports `configured: true, usable: false`.
|
|
137
|
+
*/
|
|
138
|
+
export interface CredentialStatusRecord {
|
|
139
|
+
readonly key: string;
|
|
140
|
+
/** A value exists in the shared store (or, for a named probe, in env). */
|
|
141
|
+
readonly configured: boolean;
|
|
142
|
+
/** The value/reference resolved to non-empty plaintext in-process. */
|
|
143
|
+
readonly usable: boolean;
|
|
144
|
+
/** Where the value was found: 'env' | 'user-secure' | 'project-secure' | … */
|
|
145
|
+
readonly source: string;
|
|
146
|
+
/** 'user' | 'project' | 'env'. */
|
|
147
|
+
readonly scope: string;
|
|
148
|
+
/** Backed by an encrypted store (vs plaintext). */
|
|
149
|
+
readonly secure: boolean;
|
|
150
|
+
/** A same-named environment variable overrides the stored value. */
|
|
151
|
+
readonly overriddenByEnv: boolean;
|
|
152
|
+
/** External-reference provider, when the stored value is a secret ref. */
|
|
153
|
+
readonly refSource?: string | undefined;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Reads credential STATUS (never plaintext) from the daemon's shared secret
|
|
157
|
+
* store. Enumeration (`list`) is over stored keys only — never `process.env` —
|
|
158
|
+
* so it cannot leak the names of unrelated environment variables. A named probe
|
|
159
|
+
* (`get`) may consult env for that one caller-named key.
|
|
160
|
+
*/
|
|
161
|
+
export interface CredentialStatusProviderLike {
|
|
162
|
+
list(): Promise<readonly CredentialStatusRecord[]>;
|
|
163
|
+
get(key: string): Promise<CredentialStatusRecord | null>;
|
|
164
|
+
}
|
|
123
165
|
export interface DaemonSystemRouteContext {
|
|
124
166
|
readonly approvalBroker: ApprovalBrokerLike;
|
|
125
167
|
readonly configManager: ConfigManagerLike;
|
|
168
|
+
/**
|
|
169
|
+
* Secret-free credential-status source for `credentials.get`. Null when the
|
|
170
|
+
* daemon host wires no shared secret store — the handler then reports an
|
|
171
|
+
* honest 503 rather than a false "no credentials".
|
|
172
|
+
*/
|
|
173
|
+
readonly credentialStatus: CredentialStatusProviderLike | null;
|
|
126
174
|
readonly integrationHelpers: IntegrationApprovalSnapshotSourceLike | null;
|
|
127
175
|
readonly inspectInboundTls: (surface: 'controlPlane' | 'httpListener') => unknown;
|
|
128
176
|
readonly inspectOutboundTls: () => unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-route-types.d.ts","sourceRoot":"","sources":["../src/system-route-types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"system-route-types.d.ts","sourceRoot":"","sources":["../src/system-route-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-routes.d.ts","sourceRoot":"","sources":["../src/system-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAU9D,OAAO,KAAK,EAMV,wBAAwB,EAEzB,MAAM,yBAAyB,CAAC;AA4CjC,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,wBAAwB,GAChC,yBAAyB,
|
|
1
|
+
{"version":3,"file":"system-routes.d.ts","sourceRoot":"","sources":["../src/system-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAU9D,OAAO,KAAK,EAMV,wBAAwB,EAEzB,MAAM,yBAAyB,CAAC;AA4CjC,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,wBAAwB,GAChC,yBAAyB,CAkL3B"}
|
package/dist/system-routes.js
CHANGED
|
@@ -182,6 +182,28 @@ export function createDaemonSystemRouteHandlers(context) {
|
|
|
182
182
|
return admin;
|
|
183
183
|
return Response.json(context.configManager.getAll());
|
|
184
184
|
},
|
|
185
|
+
getCredentials: async (req) => {
|
|
186
|
+
const admin = context.requireAdmin(req);
|
|
187
|
+
if (admin)
|
|
188
|
+
return admin;
|
|
189
|
+
if (!context.credentialStatus) {
|
|
190
|
+
// Honest degraded state: no shared secret store is wired, so we cannot
|
|
191
|
+
// truthfully report credential status. Never fabricate an empty "no
|
|
192
|
+
// credentials" answer that a caller would read as "nothing configured".
|
|
193
|
+
return jsonErrorResponse({ error: 'Shared credential store unavailable', code: 'CREDENTIAL_STORE_UNAVAILABLE' }, { status: 503 });
|
|
194
|
+
}
|
|
195
|
+
const key = new URL(req.url).searchParams.get('key');
|
|
196
|
+
if (key !== null) {
|
|
197
|
+
const trimmed = key.trim();
|
|
198
|
+
if (!trimmed) {
|
|
199
|
+
return jsonErrorResponse({ error: 'Missing or invalid key' }, { status: 400 });
|
|
200
|
+
}
|
|
201
|
+
const record = await context.credentialStatus.get(trimmed);
|
|
202
|
+
return Response.json({ available: true, credentials: record ? [record] : [] });
|
|
203
|
+
}
|
|
204
|
+
const credentials = await context.credentialStatus.list();
|
|
205
|
+
return Response.json({ available: true, credentials });
|
|
206
|
+
},
|
|
185
207
|
postConfig: async (req) => {
|
|
186
208
|
const admin = context.requireAdmin(req);
|
|
187
209
|
if (admin)
|
|
@@ -366,14 +388,44 @@ async function handleApprovalAction(context, approvalId, action, req) {
|
|
|
366
388
|
? context.recordApiResponse(req, `/api/approvals/${approvalId}/${action}`, Response.json({ approval }))
|
|
367
389
|
: context.recordApiResponse(req, `/api/approvals/${approvalId}/${action}`, jsonErrorResponse({ error: 'Unknown approval' }, { status: 404 }));
|
|
368
390
|
}
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
391
|
+
const selectedHunks = action === 'approve' ? readSelectedHunks(payload.selectedHunks) : undefined;
|
|
392
|
+
if (selectedHunks instanceof Response) {
|
|
393
|
+
return context.recordApiResponse(req, `/api/approvals/${approvalId}/${action}`, selectedHunks);
|
|
394
|
+
}
|
|
395
|
+
let approval;
|
|
396
|
+
try {
|
|
397
|
+
approval = await context.approvalBroker.resolveApproval(approvalId, {
|
|
398
|
+
approved: action === 'approve',
|
|
399
|
+
remember: typeof payload.remember === 'boolean' ? payload.remember : false,
|
|
400
|
+
actor,
|
|
401
|
+
actorSurface: 'web',
|
|
402
|
+
note,
|
|
403
|
+
...(selectedHunks !== undefined ? { selectedHunks } : {}),
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
// The broker throws a 400-tagged error for an out-of-range or non-edit
|
|
408
|
+
// per-hunk selection. Surface it as an honest HTTP 400, not a 500.
|
|
409
|
+
const status = typeof error?.status === 'number' ? error.status : 500;
|
|
410
|
+
const message = error instanceof Error ? error.message : 'Approval resolution failed.';
|
|
411
|
+
return context.recordApiResponse(req, `/api/approvals/${approvalId}/${action}`, jsonErrorResponse({ error: message }, { status }));
|
|
412
|
+
}
|
|
376
413
|
return approval
|
|
377
414
|
? context.recordApiResponse(req, `/api/approvals/${approvalId}/${action}`, Response.json({ approval }))
|
|
378
415
|
: context.recordApiResponse(req, `/api/approvals/${approvalId}/${action}`, jsonErrorResponse({ error: 'Unknown approval' }, { status: 404 }));
|
|
379
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Read an optional selectedHunks array off the request payload. Returns the
|
|
419
|
+
* validated number[] when present and well-formed, undefined when absent, or a
|
|
420
|
+
* 400 Response when present but malformed (non-array, or an entry that is not a
|
|
421
|
+
* finite integer). Range validation against the specific approval's hunk count
|
|
422
|
+
* is the broker's job (it owns the pending edit list).
|
|
423
|
+
*/
|
|
424
|
+
function readSelectedHunks(value) {
|
|
425
|
+
if (value === undefined || value === null)
|
|
426
|
+
return undefined;
|
|
427
|
+
if (!Array.isArray(value) || !value.every((entry) => Number.isInteger(entry))) {
|
|
428
|
+
return jsonErrorResponse({ error: 'selectedHunks must be an array of integer hunk indices.' }, { status: 400 });
|
|
429
|
+
}
|
|
430
|
+
return value;
|
|
431
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-daemon-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"bun": "1.3.10",
|
|
6
6
|
"node": ">=22.0.0"
|
|
@@ -157,8 +157,8 @@
|
|
|
157
157
|
"control-plane"
|
|
158
158
|
],
|
|
159
159
|
"dependencies": {
|
|
160
|
-
"@pellux/goodvibes-contracts": "
|
|
161
|
-
"@pellux/goodvibes-errors": "
|
|
160
|
+
"@pellux/goodvibes-contracts": "1.1.0",
|
|
161
|
+
"@pellux/goodvibes-errors": "1.1.0"
|
|
162
162
|
},
|
|
163
163
|
"publishConfig": {
|
|
164
164
|
"access": "public"
|