@pellux/goodvibes-sdk 0.18.42 → 0.18.43

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.
Files changed (44) hide show
  1. package/dist/_internal/daemon/runtime-route-types.d.ts +7 -0
  2. package/dist/_internal/daemon/runtime-route-types.d.ts.map +1 -1
  3. package/dist/_internal/daemon/runtime-session-routes.d.ts.map +1 -1
  4. package/dist/_internal/daemon/runtime-session-routes.js +43 -0
  5. package/dist/_internal/platform/companion/companion-chat-manager.d.ts +94 -0
  6. package/dist/_internal/platform/companion/companion-chat-manager.d.ts.map +1 -0
  7. package/dist/_internal/platform/companion/companion-chat-manager.js +291 -0
  8. package/dist/_internal/platform/companion/companion-chat-route-types.d.ts +31 -0
  9. package/dist/_internal/platform/companion/companion-chat-route-types.d.ts.map +1 -0
  10. package/dist/_internal/platform/companion/companion-chat-route-types.js +6 -0
  11. package/dist/_internal/platform/companion/companion-chat-routes.d.ts +23 -0
  12. package/dist/_internal/platform/companion/companion-chat-routes.d.ts.map +1 -0
  13. package/dist/_internal/platform/companion/companion-chat-routes.js +133 -0
  14. package/dist/_internal/platform/companion/companion-chat-types.d.ts +108 -0
  15. package/dist/_internal/platform/companion/companion-chat-types.d.ts.map +1 -0
  16. package/dist/_internal/platform/companion/companion-chat-types.js +11 -0
  17. package/dist/_internal/platform/companion/index.d.ts +6 -0
  18. package/dist/_internal/platform/companion/index.d.ts.map +1 -0
  19. package/dist/_internal/platform/companion/index.js +2 -0
  20. package/dist/_internal/platform/control-plane/conversation-message.d.ts +29 -0
  21. package/dist/_internal/platform/control-plane/conversation-message.d.ts.map +1 -0
  22. package/dist/_internal/platform/control-plane/conversation-message.js +9 -0
  23. package/dist/_internal/platform/control-plane/index.d.ts +1 -0
  24. package/dist/_internal/platform/control-plane/index.d.ts.map +1 -1
  25. package/dist/_internal/platform/control-plane/session-broker.d.ts +7 -0
  26. package/dist/_internal/platform/control-plane/session-broker.d.ts.map +1 -1
  27. package/dist/_internal/platform/control-plane/session-broker.js +102 -11
  28. package/dist/_internal/platform/control-plane/session-types.d.ts +11 -1
  29. package/dist/_internal/platform/control-plane/session-types.d.ts.map +1 -1
  30. package/dist/_internal/platform/daemon/facade-composition.d.ts.map +1 -1
  31. package/dist/_internal/platform/daemon/facade-composition.js +7 -0
  32. package/dist/_internal/platform/daemon/facade.d.ts +2 -0
  33. package/dist/_internal/platform/daemon/facade.d.ts.map +1 -1
  34. package/dist/_internal/platform/daemon/facade.js +17 -0
  35. package/dist/_internal/platform/daemon/http/router.d.ts +7 -0
  36. package/dist/_internal/platform/daemon/http/router.d.ts.map +1 -1
  37. package/dist/_internal/platform/daemon/http/router.js +34 -0
  38. package/dist/_internal/platform/daemon/http/runtime-route-types.d.ts +7 -0
  39. package/dist/_internal/platform/daemon/http/runtime-route-types.d.ts.map +1 -1
  40. package/dist/_internal/platform/runtime/tasks/adapters/agent-adapter.d.ts +12 -0
  41. package/dist/_internal/platform/runtime/tasks/adapters/agent-adapter.d.ts.map +1 -1
  42. package/dist/_internal/platform/runtime/tasks/adapters/agent-adapter.js +48 -0
  43. package/dist/_internal/platform/version.js +1 -1
  44. package/package.json +1 -1
@@ -0,0 +1,133 @@
1
+ /**
2
+ * companion-chat-routes.ts
3
+ *
4
+ * HTTP route handlers for the companion-app chat-mode API.
5
+ *
6
+ * Routes:
7
+ * POST /api/companion/chat/sessions
8
+ * GET /api/companion/chat/sessions/:sessionId
9
+ * DELETE /api/companion/chat/sessions/:sessionId
10
+ * POST /api/companion/chat/sessions/:sessionId/messages
11
+ * GET /api/companion/chat/sessions/:sessionId/events (SSE)
12
+ *
13
+ * All routes require the existing daemon bearer-token auth (enforced by the
14
+ * caller — DaemonHttpRouter.handleRequest already validates auth before
15
+ * dispatching to API routes).
16
+ */
17
+ // ---------------------------------------------------------------------------
18
+ // Route dispatch — called from DaemonHttpRouter.dispatchApiRoutes
19
+ // ---------------------------------------------------------------------------
20
+ /**
21
+ * Try to handle a companion chat route. Returns null if the path/method
22
+ * does not match, so the caller can fall through to other route groups.
23
+ */
24
+ export async function dispatchCompanionChatRoutes(req, context) {
25
+ const url = new URL(req.url);
26
+ const { pathname } = url;
27
+ // POST /api/companion/chat/sessions
28
+ if (pathname === '/api/companion/chat/sessions' && req.method === 'POST') {
29
+ return handleCreateSession(req, context);
30
+ }
31
+ const sessionMatch = pathname.match(/^\/api\/companion\/chat\/sessions\/([^/]+)(\/(.+))?$/);
32
+ if (!sessionMatch)
33
+ return null;
34
+ const sessionId = sessionMatch[1];
35
+ const sub = sessionMatch[3] ?? '';
36
+ // GET /api/companion/chat/sessions/:sessionId
37
+ if (!sub && req.method === 'GET') {
38
+ return handleGetSession(sessionId, context);
39
+ }
40
+ // DELETE /api/companion/chat/sessions/:sessionId
41
+ if (!sub && req.method === 'DELETE') {
42
+ return handleDeleteSession(sessionId, context);
43
+ }
44
+ // POST /api/companion/chat/sessions/:sessionId/messages
45
+ if (sub === 'messages' && req.method === 'POST') {
46
+ return handlePostMessage(req, sessionId, context);
47
+ }
48
+ // GET /api/companion/chat/sessions/:sessionId/events
49
+ if (sub === 'events' && req.method === 'GET') {
50
+ return handleGetEvents(req, sessionId, context);
51
+ }
52
+ return null;
53
+ }
54
+ // ---------------------------------------------------------------------------
55
+ // POST /api/companion/chat/sessions
56
+ // ---------------------------------------------------------------------------
57
+ async function handleCreateSession(req, context) {
58
+ const bodyOrResponse = await context.parseOptionalJsonBody(req);
59
+ if (bodyOrResponse instanceof Response)
60
+ return bodyOrResponse;
61
+ const body = (bodyOrResponse ?? {});
62
+ const input = {
63
+ title: typeof body['title'] === 'string' ? body['title'] : undefined,
64
+ model: typeof body['model'] === 'string' ? body['model'] : undefined,
65
+ provider: typeof body['provider'] === 'string' ? body['provider'] : undefined,
66
+ systemPrompt: typeof body['systemPrompt'] === 'string' ? body['systemPrompt'] : undefined,
67
+ };
68
+ const session = context.chatManager.createSession(input);
69
+ return Response.json({ sessionId: session.id, createdAt: session.createdAt }, { status: 201 });
70
+ }
71
+ // ---------------------------------------------------------------------------
72
+ // GET /api/companion/chat/sessions/:sessionId
73
+ // ---------------------------------------------------------------------------
74
+ async function handleGetSession(sessionId, context) {
75
+ const session = context.chatManager.getSession(sessionId);
76
+ if (!session) {
77
+ return Response.json({ error: 'Session not found', code: 'SESSION_NOT_FOUND' }, { status: 404 });
78
+ }
79
+ const messages = context.chatManager.getMessages(sessionId);
80
+ return Response.json({ session, messages });
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // DELETE /api/companion/chat/sessions/:sessionId
84
+ // ---------------------------------------------------------------------------
85
+ async function handleDeleteSession(sessionId, context) {
86
+ const session = context.chatManager.closeSession(sessionId);
87
+ if (!session) {
88
+ return Response.json({ error: 'Session not found', code: 'SESSION_NOT_FOUND' }, { status: 404 });
89
+ }
90
+ return Response.json({ sessionId: session.id, status: session.status });
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // POST /api/companion/chat/sessions/:sessionId/messages
94
+ // ---------------------------------------------------------------------------
95
+ async function handlePostMessage(req, sessionId, context) {
96
+ const bodyOrResponse = await context.parseJsonBody(req);
97
+ if (bodyOrResponse instanceof Response)
98
+ return bodyOrResponse;
99
+ const body = bodyOrResponse;
100
+ const input = {
101
+ content: typeof body['content'] === 'string' ? body['content'] : '',
102
+ metadata: typeof body['metadata'] === 'object' && body['metadata'] !== null
103
+ ? body['metadata']
104
+ : undefined,
105
+ };
106
+ if (!input.content.trim()) {
107
+ return Response.json({ error: 'content is required and must be a non-empty string', code: 'INVALID_INPUT' }, { status: 400 });
108
+ }
109
+ try {
110
+ const messageId = await context.chatManager.postMessage(sessionId, input.content);
111
+ return Response.json({ messageId }, { status: 202 });
112
+ }
113
+ catch (err) {
114
+ const e = err;
115
+ const status = e.status ?? 500;
116
+ return Response.json({ error: e.message ?? 'Internal error', code: e.code ?? 'INTERNAL_ERROR' }, { status });
117
+ }
118
+ }
119
+ // ---------------------------------------------------------------------------
120
+ // GET /api/companion/chat/sessions/:sessionId/events (SSE)
121
+ // ---------------------------------------------------------------------------
122
+ async function handleGetEvents(req, sessionId, context) {
123
+ const session = context.chatManager.getSession(sessionId);
124
+ if (!session) {
125
+ return Response.json({ error: 'Session not found', code: 'SESSION_NOT_FOUND' }, { status: 404 });
126
+ }
127
+ if (session.status === 'closed') {
128
+ return Response.json({ error: 'Session is closed', code: 'SESSION_CLOSED' }, { status: 410 });
129
+ }
130
+ // Delegate to the caller-provided SSE stream opener which wires up the
131
+ // gateway live-client registration and returns an SSE Response.
132
+ return context.openSessionEventStream(req, sessionId);
133
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * companion-chat-types.ts
3
+ *
4
+ * Types for companion-app chat-mode sessions. These are distinct from the
5
+ * TUI operator session and from task-submit (SharedSessionBroker) sessions.
6
+ * Chat sessions are managed by the CompanionChatManager and live entirely
7
+ * in memory (no persistence across daemon restart — v1 scope).
8
+ *
9
+ * TODO (follow-up): persist chat sessions to disk / database on daemon restart.
10
+ */
11
+ import type { ConversationMessageEnvelope } from '../control-plane/conversation-message.js';
12
+ /**
13
+ * Re-export the shared envelope so chat-mode code can import from one place.
14
+ * The envelope shape is the canonical message unit flowing through
15
+ * the control-plane gateway across both chat-mode and Problem-2 routing.
16
+ */
17
+ export type { ConversationMessageEnvelope } from '../control-plane/conversation-message.js';
18
+ export type CompanionChatSessionKind = 'companion-chat';
19
+ export type CompanionChatSessionStatus = 'active' | 'closed';
20
+ export type CompanionChatMessageRole = 'user' | 'assistant';
21
+ export interface CompanionChatMessage {
22
+ readonly id: string;
23
+ readonly sessionId: string;
24
+ readonly role: CompanionChatMessageRole;
25
+ readonly content: string;
26
+ readonly createdAt: number;
27
+ }
28
+ export interface CompanionChatSession {
29
+ readonly id: string;
30
+ readonly kind: CompanionChatSessionKind;
31
+ readonly title: string;
32
+ readonly model: string | null;
33
+ readonly provider: string | null;
34
+ readonly systemPrompt: string | null;
35
+ readonly status: CompanionChatSessionStatus;
36
+ readonly createdAt: number;
37
+ readonly updatedAt: number;
38
+ readonly closedAt: number | null;
39
+ readonly messageCount: number;
40
+ }
41
+ export interface CreateCompanionChatSessionInput {
42
+ readonly title?: string;
43
+ readonly model?: string;
44
+ readonly provider?: string;
45
+ readonly systemPrompt?: string;
46
+ }
47
+ export interface CreateCompanionChatSessionOutput {
48
+ readonly sessionId: string;
49
+ readonly createdAt: number;
50
+ }
51
+ export interface PostCompanionChatMessageInput {
52
+ readonly content: string;
53
+ readonly metadata?: Record<string, unknown>;
54
+ }
55
+ export interface PostCompanionChatMessageOutput {
56
+ readonly messageId: string;
57
+ }
58
+ export interface GetCompanionChatSessionOutput {
59
+ readonly session: CompanionChatSession;
60
+ readonly messages: CompanionChatMessage[];
61
+ }
62
+ export interface CompanionChatTurnStartedEvent {
63
+ readonly type: 'turn.started';
64
+ readonly sessionId: string;
65
+ readonly messageId: string;
66
+ readonly turnId: string;
67
+ /** Shared envelope for the user message that started this turn. */
68
+ readonly envelope: ConversationMessageEnvelope;
69
+ }
70
+ export interface CompanionChatTurnDeltaEvent {
71
+ readonly type: 'turn.delta';
72
+ readonly sessionId: string;
73
+ readonly turnId: string;
74
+ readonly delta: string;
75
+ }
76
+ export interface CompanionChatTurnToolCallEvent {
77
+ readonly type: 'turn.tool_call';
78
+ readonly sessionId: string;
79
+ readonly turnId: string;
80
+ readonly toolCallId: string;
81
+ readonly toolName: string;
82
+ readonly toolInput: unknown;
83
+ }
84
+ export interface CompanionChatTurnToolResultEvent {
85
+ readonly type: 'turn.tool_result';
86
+ readonly sessionId: string;
87
+ readonly turnId: string;
88
+ readonly toolCallId: string;
89
+ readonly toolName: string;
90
+ readonly result: unknown;
91
+ readonly isError: boolean;
92
+ }
93
+ export interface CompanionChatTurnCompletedEvent {
94
+ readonly type: 'turn.completed';
95
+ readonly sessionId: string;
96
+ readonly turnId: string;
97
+ readonly assistantMessageId: string;
98
+ /** Shared envelope for the assistant message produced by this turn. */
99
+ readonly envelope: ConversationMessageEnvelope;
100
+ }
101
+ export interface CompanionChatTurnErrorEvent {
102
+ readonly type: 'turn.error';
103
+ readonly sessionId: string;
104
+ readonly turnId: string;
105
+ readonly error: string;
106
+ }
107
+ export type CompanionChatTurnEvent = CompanionChatTurnStartedEvent | CompanionChatTurnDeltaEvent | CompanionChatTurnToolCallEvent | CompanionChatTurnToolResultEvent | CompanionChatTurnCompletedEvent | CompanionChatTurnErrorEvent;
108
+ //# sourceMappingURL=companion-chat-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"companion-chat-types.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/companion/companion-chat-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * companion-chat-types.ts
3
+ *
4
+ * Types for companion-app chat-mode sessions. These are distinct from the
5
+ * TUI operator session and from task-submit (SharedSessionBroker) sessions.
6
+ * Chat sessions are managed by the CompanionChatManager and live entirely
7
+ * in memory (no persistence across daemon restart — v1 scope).
8
+ *
9
+ * TODO (follow-up): persist chat sessions to disk / database on daemon restart.
10
+ */
11
+ export {};
@@ -0,0 +1,6 @@
1
+ export type { CompanionChatMessage, CompanionChatSession, CompanionChatSessionKind, CompanionChatSessionStatus, CompanionChatMessageRole, CompanionChatTurnEvent, CompanionChatTurnStartedEvent, CompanionChatTurnDeltaEvent, CompanionChatTurnToolCallEvent, CompanionChatTurnToolResultEvent, CompanionChatTurnCompletedEvent, CompanionChatTurnErrorEvent, CreateCompanionChatSessionInput, CreateCompanionChatSessionOutput, PostCompanionChatMessageInput, PostCompanionChatMessageOutput, GetCompanionChatSessionOutput, ConversationMessageEnvelope, } from './companion-chat-types.js';
2
+ export type { CompanionLLMProvider, CompanionChatEventPublisher, CompanionChatManagerConfig, CompanionProviderMessage, CompanionProviderChunk, } from './companion-chat-manager.js';
3
+ export { CompanionChatManager } from './companion-chat-manager.js';
4
+ export { dispatchCompanionChatRoutes } from './companion-chat-routes.js';
5
+ export type { CompanionChatRouteContext } from './companion-chat-route-types.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/companion/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,EACtB,6BAA6B,EAC7B,2BAA2B,EAC3B,8BAA8B,EAC9B,gCAAgC,EAChC,+BAA+B,EAC/B,2BAA2B,EAC3B,+BAA+B,EAC/B,gCAAgC,EAChC,6BAA6B,EAC7B,8BAA8B,EAC9B,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,oBAAoB,EACpB,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { CompanionChatManager } from './companion-chat-manager.js';
2
+ export { dispatchCompanionChatRoutes } from './companion-chat-routes.js';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * conversation-message.ts
3
+ *
4
+ * Shared envelope types for conversation messages flowing through the
5
+ * control-plane gateway. All consumers — SSE companion-chat streams,
6
+ * TUI-surface follow-up listeners, and web-UI clients — depend on this
7
+ * stable shape.
8
+ */
9
+ /**
10
+ * Provenance of a message/event flowing through a conversation.
11
+ * Used both for chat-mode (where the SDK owns the ConversationManager)
12
+ * and for companion follow-ups (where the TUI client owns it cross-process).
13
+ */
14
+ export type MessageSource = 'operator' | 'companion-chat-user' | 'companion-chat-assistant' | 'companion-followup' | 'system' | 'tool';
15
+ /**
16
+ * Stable envelope shape for any conversation-message-related event published
17
+ * through the control-plane gateway. All consumers (SSE companion chat stream,
18
+ * TUI surface follow-up listener, web-UI clients) can depend on this shape.
19
+ */
20
+ export interface ConversationMessageEnvelope {
21
+ readonly sessionId: string;
22
+ readonly messageId: string;
23
+ readonly body: string;
24
+ readonly source: MessageSource;
25
+ readonly timestamp: number;
26
+ /** Optional metadata for tool info, model id, etc. */
27
+ readonly metadata?: Readonly<Record<string, unknown>>;
28
+ }
29
+ //# sourceMappingURL=conversation-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-message.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/control-plane/conversation-message.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;GAIG;AACH,MAAM,MAAM,aAAa,GACrB,UAAU,GACV,qBAAqB,GACrB,0BAA0B,GAC1B,oBAAoB,GACpB,QAAQ,GACR,MAAM,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * conversation-message.ts
3
+ *
4
+ * Shared envelope types for conversation messages flowing through the
5
+ * control-plane gateway. All consumers — SSE companion-chat streams,
6
+ * TUI-surface follow-up listeners, and web-UI clients — depend on this
7
+ * stable shape.
8
+ */
9
+ export {};
@@ -9,4 +9,5 @@ export { SharedSessionBroker } from './session-broker.js';
9
9
  export type { SharedApprovalRecord, SharedApprovalAuditRecord, SharedApprovalStatus, RequestSharedApprovalInput, } from './approval-broker.js';
10
10
  export { ApprovalBroker } from './approval-broker.js';
11
11
  export type { ControlPlaneAuthMode, ControlPlaneAuthSnapshot } from '@pellux/goodvibes-sdk/platform/control-plane/auth-snapshot';
12
+ export type { MessageSource, ConversationMessageEnvelope } from './conversation-message.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/control-plane/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,oDAAoD,CAAC;AAC5D,YAAY,EAAE,yBAAyB,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvH,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EACL,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,8BAA8B,EAC9B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,EACvB,8BAA8B,EAC9B,+BAA+B,GAChC,MAAM,4DAA4D,CAAC;AACpE,YAAY,EACV,uBAAuB,EACvB,gCAAgC,EAChC,+BAA+B,EAC/B,gCAAgC,EAChC,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,8DAA8D,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/control-plane/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,oDAAoD,CAAC;AAC5D,YAAY,EAAE,yBAAyB,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvH,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EACL,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,8BAA8B,EAC9B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,EACvB,8BAA8B,EAC9B,+BAA+B,GAChC,MAAM,4DAA4D,CAAC;AACpE,YAAY,EACV,uBAAuB,EACvB,gCAAgC,EAChC,+BAA+B,EAC/B,gCAAgC,EAChC,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,8DAA8D,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAC;AACjI,YAAY,EAAE,aAAa,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC"}
@@ -18,6 +18,7 @@ export declare class SharedSessionBroker {
18
18
  private loaded;
19
19
  private _gcInterval;
20
20
  private _busUnsubs;
21
+ private _busAttached;
21
22
  /** Default idle threshold for zero-message sessions (ms). */
22
23
  private readonly _idleEmptyMs;
23
24
  /** Default idle threshold for sessions with content (ms). */
@@ -36,6 +37,11 @@ export declare class SharedSessionBroker {
36
37
  readonly idleLongMs?: number;
37
38
  });
38
39
  setEventPublisher(publisher: SharedSessionEventPublisher | null): void;
40
+ /**
41
+ * M3: Gracefully stop the broker — clears GC interval, tears down bus subscriptions,
42
+ * and persists state. Call from DaemonServer.stop().
43
+ */
44
+ stop(): Promise<void>;
39
45
  /**
40
46
  * Wire the broker to a RuntimeEventBus so agent terminal events automatically
41
47
  * reconcile session inputs and task state.
@@ -93,6 +99,7 @@ export declare class SharedSessionBroker {
93
99
  private claimNextQueuedInput;
94
100
  private finalizeAgentInputs;
95
101
  private runQueuedFollowUp;
102
+ private _touch;
96
103
  private refreshPendingInputCount;
97
104
  /**
98
105
  * Periodic idle-session GC sweep.
@@ -1 +1 @@
1
- {"version":3,"file":"session-broker.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/control-plane/session-broker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-broker.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/control-plane/session-broker.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@ export class SharedSessionBroker {
18
18
  loaded = false;
19
19
  _gcInterval = null;
20
20
  _busUnsubs = [];
21
+ _busAttached = false;
21
22
  /** Default idle threshold for zero-message sessions (ms). */
22
23
  _idleEmptyMs;
23
24
  /** Default idle threshold for sessions with content (ms). */
@@ -41,6 +42,25 @@ export class SharedSessionBroker {
41
42
  setEventPublisher(publisher) {
42
43
  this.eventPublisher = publisher;
43
44
  }
45
+ /**
46
+ * M3: Gracefully stop the broker — clears GC interval, tears down bus subscriptions,
47
+ * and persists state. Call from DaemonServer.stop().
48
+ */
49
+ async stop() {
50
+ if (this._gcInterval) {
51
+ clearInterval(this._gcInterval);
52
+ this._gcInterval = null;
53
+ }
54
+ for (const u of this._busUnsubs) {
55
+ try {
56
+ u();
57
+ }
58
+ catch { }
59
+ }
60
+ this._busUnsubs = [];
61
+ this._busAttached = false;
62
+ await this.persist();
63
+ }
44
64
  /**
45
65
  * Wire the broker to a RuntimeEventBus so agent terminal events automatically
46
66
  * reconcile session inputs and task state.
@@ -53,29 +73,54 @@ export class SharedSessionBroker {
53
73
  * Return `null` when the agent is not associated with a shared session.
54
74
  */
55
75
  attachRuntimeBus(bus, sessionResolver) {
76
+ // m3: idempotent — second call is a no-op with a warning
77
+ if (this._busAttached) {
78
+ console.warn('[SharedSessionBroker] attachRuntimeBus called more than once — ignoring duplicate call');
79
+ return () => { };
80
+ }
81
+ this._busAttached = true;
56
82
  const onCompleted = bus.on('AGENT_COMPLETED', (envelope) => {
83
+ // m2: runtime type guard
84
+ if (typeof envelope.payload?.agentId !== 'string')
85
+ return;
57
86
  const sessionId = sessionResolver(envelope.payload.agentId);
58
87
  if (!sessionId)
59
88
  return;
60
- void this.completeAgent(sessionId, envelope.payload.agentId, envelope.payload.output ?? '', { status: 'completed', durationMs: envelope.payload.durationMs });
89
+ // m1: catch to prevent unhandled rejections
90
+ this.completeAgent(sessionId, envelope.payload.agentId, envelope.payload.output ?? '', { status: 'completed', durationMs: envelope.payload.durationMs }).catch((err) => {
91
+ console.error('[SharedSessionBroker] completeAgent error on AGENT_COMPLETED', err);
92
+ });
61
93
  });
62
94
  const onFailed = bus.on('AGENT_FAILED', (envelope) => {
95
+ // m2: runtime type guard
96
+ if (typeof envelope.payload?.agentId !== 'string')
97
+ return;
63
98
  const sessionId = sessionResolver(envelope.payload.agentId);
64
99
  if (!sessionId)
65
100
  return;
66
- void this.completeAgent(sessionId, envelope.payload.agentId, envelope.payload.error, { status: 'failed', durationMs: envelope.payload.durationMs });
101
+ // m1: catch to prevent unhandled rejections
102
+ this.completeAgent(sessionId, envelope.payload.agentId, envelope.payload.error, { status: 'failed', durationMs: envelope.payload.durationMs }).catch((err) => {
103
+ console.error('[SharedSessionBroker] completeAgent error on AGENT_FAILED', err);
104
+ });
67
105
  });
68
106
  const onCancelled = bus.on('AGENT_CANCELLED', (envelope) => {
107
+ // m2: runtime type guard
108
+ if (typeof envelope.payload?.agentId !== 'string')
109
+ return;
69
110
  const sessionId = sessionResolver(envelope.payload.agentId);
70
111
  if (!sessionId)
71
112
  return;
72
- void this.completeAgent(sessionId, envelope.payload.agentId, envelope.payload.reason ?? 'cancelled', { status: 'cancelled' });
113
+ // m1: catch to prevent unhandled rejections
114
+ this.completeAgent(sessionId, envelope.payload.agentId, envelope.payload.reason ?? 'cancelled', { status: 'cancelled' }).catch((err) => {
115
+ console.error('[SharedSessionBroker] completeAgent error on AGENT_CANCELLED', err);
116
+ });
73
117
  });
74
118
  this._busUnsubs.push(onCompleted, onFailed, onCancelled);
75
119
  return () => {
76
120
  onCompleted();
77
121
  onFailed();
78
122
  onCancelled();
123
+ this._busAttached = false;
79
124
  this._busUnsubs = this._busUnsubs.filter((fn) => fn !== onCompleted && fn !== onFailed && fn !== onCancelled);
80
125
  };
81
126
  }
@@ -100,8 +145,31 @@ export class SharedSessionBroker {
100
145
  this.inputs.set(sessionId, bucket);
101
146
  }
102
147
  this.loaded = true;
148
+ // M2: startup reconciliation — cancel inputs stuck in spawned/delivered from a prior run
149
+ const restartReason = 'daemon restart — agent state unknown';
150
+ for (const [sessionId, bucket] of this.inputs.entries()) {
151
+ let changed = false;
152
+ for (let i = 0; i < bucket.length; i++) {
153
+ const entry = bucket[i];
154
+ if (entry.state === 'spawned' || entry.state === 'delivered') {
155
+ bucket[i] = { ...entry, state: 'cancelled', updatedAt: Date.now(), error: restartReason };
156
+ changed = true;
157
+ }
158
+ }
159
+ if (changed)
160
+ this.refreshPendingInputCount(sessionId);
161
+ }
162
+ for (const [sessionId, session] of this.sessions.entries()) {
163
+ if (session.activeAgentId) {
164
+ this.sessions.set(sessionId, { ...session, activeAgentId: undefined, updatedAt: Date.now() });
165
+ }
166
+ }
167
+ await this.persist();
103
168
  if (!this._gcInterval) {
104
- this._gcInterval = setInterval(() => { this._gcSweep(); }, 60_000);
169
+ // M3: .unref() so the GC interval does not keep the process alive past shutdown
170
+ const iv = setInterval(() => { this._gcSweep(); }, 60_000);
171
+ iv.unref?.();
172
+ this._gcInterval = iv;
105
173
  }
106
174
  }
107
175
  listSessions(limit = 100) {
@@ -159,6 +227,7 @@ export class SharedSessionBroker {
159
227
  const routeIds = input.routeBinding?.id ? [input.routeBinding.id] : [];
160
228
  const session = {
161
229
  id: sessionId,
230
+ kind: input.kind ?? 'tui',
162
231
  title: input.title?.trim() || input.routeBinding?.title || `Session ${sessionId}`,
163
232
  status: 'active',
164
233
  createdAt: now,
@@ -191,12 +260,15 @@ export class SharedSessionBroker {
191
260
  const session = this.sessions.get(sessionId);
192
261
  if (!session)
193
262
  return null;
263
+ this._touch(sessionId); // M4: uniform touch before mutation
264
+ const touched = this.sessions.get(sessionId); // re-fetch after touch
265
+ const now = Date.now();
194
266
  const updated = {
195
- ...session,
267
+ ...touched,
196
268
  status: 'closed',
197
269
  activeAgentId: undefined,
198
- updatedAt: Date.now(),
199
- closedAt: Date.now(),
270
+ updatedAt: now,
271
+ closedAt: now,
200
272
  };
201
273
  this.sessions.set(sessionId, updated);
202
274
  await this.persist();
@@ -271,13 +343,16 @@ export class SharedSessionBroker {
271
343
  agentId,
272
344
  metadata,
273
345
  });
346
+ const now = Date.now();
274
347
  const updated = {
275
348
  ...(this.sessions.get(sessionId) ?? session),
276
349
  activeAgentId: (this.sessions.get(sessionId)?.activeAgentId === agentId) ? undefined : this.sessions.get(sessionId)?.activeAgentId,
277
350
  lastAgentId: agentId,
278
- updatedAt: Date.now(),
351
+ updatedAt: now,
352
+ lastActivityAt: now, // M4: completeAgent explicitly updates lastActivityAt
279
353
  ...(metadata.status === 'failed' ? { lastError: body } : {}),
280
354
  };
355
+ this._touch(sessionId); // M4: touch after mutation
281
356
  this.sessions.set(sessionId, updated);
282
357
  const finalizedInputs = this.finalizeAgentInputs(sessionId, agentId, metadata.status === 'failed' ? 'failed' : metadata.status === 'cancelled' ? 'cancelled' : 'completed', metadata.status === 'failed' ? body : undefined);
283
358
  await this.persist();
@@ -373,6 +448,7 @@ export class SharedSessionBroker {
373
448
  return message;
374
449
  }
375
450
  async attachParticipantAndRoute(session, input, binding) {
451
+ this._touch(session.id); // M4
376
452
  const existing = this.sessions.get(session.id) ?? session;
377
453
  const nextRouteIds = binding?.id
378
454
  ? [...new Set([...existing.routeIds, binding.id])]
@@ -604,6 +680,7 @@ export class SharedSessionBroker {
604
680
  };
605
681
  }
606
682
  recordInput(sessionId, intent, input, routeId, causationId) {
683
+ this._touch(sessionId); // M4
607
684
  const id = `sin-${randomUUID().slice(0, 8)}`;
608
685
  const entry = {
609
686
  id,
@@ -642,6 +719,7 @@ export class SharedSessionBroker {
642
719
  bucket[index] = updated;
643
720
  this.inputs.set(sessionId, bucket);
644
721
  this.refreshPendingInputCount(sessionId);
722
+ this._touch(sessionId); // M4
645
723
  return updated;
646
724
  }
647
725
  claimNextQueuedInput(sessionId, agentId) {
@@ -649,17 +727,20 @@ export class SharedSessionBroker {
649
727
  const next = bucket.find((entry) => entry.state === 'queued');
650
728
  if (!next)
651
729
  return null;
652
- return this.updateInput(sessionId, next.id, (entry) => ({
730
+ const result = this.updateInput(sessionId, next.id, (entry) => ({
653
731
  ...entry,
654
732
  state: 'spawned',
655
733
  activeAgentId: agentId,
656
734
  updatedAt: Date.now(),
657
735
  }));
736
+ this._touch(sessionId); // M4
737
+ return result;
658
738
  }
659
739
  finalizeAgentInputs(sessionId, agentId, nextState, error) {
660
740
  const bucket = this.inputs.get(sessionId);
661
741
  if (!bucket)
662
742
  return [];
743
+ this._touch(sessionId); // M4: ensure direct callers also bump session timestamps
663
744
  let changed = false;
664
745
  const updatedInputs = [];
665
746
  for (let index = 0; index < bucket.length; index += 1) {
@@ -710,6 +791,14 @@ export class SharedSessionBroker {
710
791
  await this.persist();
711
792
  return claimed ? { input: claimed, agentId: spawned.agentId } : null;
712
793
  }
794
+ // M4: touch helper — bumps lastActivityAt + updatedAt on a session
795
+ _touch(sessionId) {
796
+ const s = this.sessions.get(sessionId);
797
+ if (!s)
798
+ return;
799
+ const now = Date.now();
800
+ this.sessions.set(sessionId, { ...s, lastActivityAt: now, updatedAt: now });
801
+ }
713
802
  refreshPendingInputCount(sessionId) {
714
803
  const session = this.sessions.get(sessionId);
715
804
  if (!session)
@@ -735,11 +824,14 @@ export class SharedSessionBroker {
735
824
  */
736
825
  _gcSweep() {
737
826
  const now = Date.now();
827
+ let anyChanged = false; // m4: track changed inline, not a second O(n) scan
738
828
  for (const [sessionId, session] of this.sessions.entries()) {
739
829
  if (session.status !== 'active')
740
830
  continue;
741
831
  if (session.activeAgentId)
742
832
  continue; // live agent — leave it
833
+ if (session.pendingInputCount > 0)
834
+ continue; // M4: never close sessions with pending inputs
743
835
  const idle = now - session.lastActivityAt;
744
836
  let reason = null;
745
837
  if (session.messageCount === 0 && idle >= this._idleEmptyMs) {
@@ -759,9 +851,8 @@ export class SharedSessionBroker {
759
851
  };
760
852
  this.sessions.set(sessionId, closed);
761
853
  this.publishUpdate('session-closed', { ...closed, reason });
854
+ anyChanged = true; // m4: set during loop, not a second scan
762
855
  }
763
- // Persist after a sweep only when something changed.
764
- const anyChanged = [...this.sessions.values()].some((s) => s.status === 'closed' && s.closedAt && now - s.closedAt < 2_000);
765
856
  if (anyChanged) {
766
857
  void this.persist();
767
858
  }