@pellux/goodvibes-daemon-sdk 0.18.3 → 0.30.1

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 (82) hide show
  1. package/README.md +13 -7
  2. package/dist/api-router.d.ts +8 -1
  3. package/dist/api-router.d.ts.map +1 -1
  4. package/dist/api-router.js +12 -3
  5. package/dist/artifact-upload.d.ts +26 -0
  6. package/dist/artifact-upload.d.ts.map +1 -0
  7. package/dist/artifact-upload.js +535 -0
  8. package/dist/automation.d.ts +2 -2
  9. package/dist/automation.d.ts.map +1 -1
  10. package/dist/channel-route-types.d.ts.map +1 -1
  11. package/dist/channel-routes.d.ts +2 -2
  12. package/dist/channel-routes.d.ts.map +1 -1
  13. package/dist/channel-routes.js +28 -3
  14. package/dist/context.d.ts +151 -51
  15. package/dist/context.d.ts.map +1 -1
  16. package/dist/control-routes.d.ts +2 -2
  17. package/dist/control-routes.d.ts.map +1 -1
  18. package/dist/error-response.d.ts.map +1 -1
  19. package/dist/error-response.js +172 -12
  20. package/dist/http-policy.d.ts +1 -1
  21. package/dist/http-policy.d.ts.map +1 -1
  22. package/dist/http-policy.js +0 -1
  23. package/dist/index.d.ts +8 -4
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -1
  26. package/dist/integration-route-types.d.ts +3 -1
  27. package/dist/integration-route-types.d.ts.map +1 -1
  28. package/dist/integration-routes.d.ts +2 -2
  29. package/dist/integration-routes.d.ts.map +1 -1
  30. package/dist/integration-routes.js +3 -0
  31. package/dist/knowledge-refinement-routes.d.ts +4 -0
  32. package/dist/knowledge-refinement-routes.d.ts.map +1 -0
  33. package/dist/knowledge-refinement-routes.js +58 -0
  34. package/dist/knowledge-route-types.d.ts +11 -1
  35. package/dist/knowledge-route-types.d.ts.map +1 -1
  36. package/dist/knowledge-routes.d.ts +2 -2
  37. package/dist/knowledge-routes.d.ts.map +1 -1
  38. package/dist/knowledge-routes.js +156 -13
  39. package/dist/media-route-types.d.ts +10 -0
  40. package/dist/media-route-types.d.ts.map +1 -1
  41. package/dist/media-routes.d.ts +2 -2
  42. package/dist/media-routes.d.ts.map +1 -1
  43. package/dist/media-routes.js +134 -15
  44. package/dist/operator.d.ts +2 -2
  45. package/dist/operator.d.ts.map +1 -1
  46. package/dist/operator.js +49 -15
  47. package/dist/otlp-protobuf.d.ts +3 -0
  48. package/dist/otlp-protobuf.d.ts.map +1 -0
  49. package/dist/otlp-protobuf.js +977 -0
  50. package/dist/remote-routes.d.ts +2 -2
  51. package/dist/remote-routes.d.ts.map +1 -1
  52. package/dist/remote-routes.js +146 -13
  53. package/dist/remote.d.ts +2 -2
  54. package/dist/remote.d.ts.map +1 -1
  55. package/dist/route-helpers.d.ts +8 -0
  56. package/dist/route-helpers.d.ts.map +1 -1
  57. package/dist/route-helpers.js +24 -0
  58. package/dist/runtime-automation-routes.d.ts +2 -2
  59. package/dist/runtime-automation-routes.d.ts.map +1 -1
  60. package/dist/runtime-automation-routes.js +4 -1
  61. package/dist/runtime-route-types.d.ts +46 -3
  62. package/dist/runtime-route-types.d.ts.map +1 -1
  63. package/dist/runtime-routes.d.ts +2 -2
  64. package/dist/runtime-routes.d.ts.map +1 -1
  65. package/dist/runtime-routes.js +1 -0
  66. package/dist/runtime-session-routes.d.ts +13 -3
  67. package/dist/runtime-session-routes.d.ts.map +1 -1
  68. package/dist/runtime-session-routes.js +102 -10
  69. package/dist/sessions.d.ts +2 -2
  70. package/dist/sessions.d.ts.map +1 -1
  71. package/dist/sessions.js +3 -0
  72. package/dist/system-route-types.d.ts +19 -0
  73. package/dist/system-route-types.d.ts.map +1 -1
  74. package/dist/system-routes.d.ts +2 -2
  75. package/dist/system-routes.d.ts.map +1 -1
  76. package/dist/system-routes.js +18 -0
  77. package/dist/tasks.d.ts +2 -2
  78. package/dist/tasks.d.ts.map +1 -1
  79. package/dist/telemetry-routes.d.ts +25 -2
  80. package/dist/telemetry-routes.d.ts.map +1 -1
  81. package/dist/telemetry-routes.js +131 -15
  82. package/package.json +128 -5
@@ -1,5 +1,9 @@
1
1
  import { buildMissingScopeBody, resolvePrivateHostFetchOptions, } from './http-policy.js';
2
+ import { GoodVibesSdkError, DaemonErrorCategory } from '@pellux/goodvibes-errors';
2
3
  import { jsonErrorResponse, summarizeErrorForRecord } from './error-response.js';
4
+ import { createArtifactFromUploadRequest, isArtifactUploadRequest } from './artifact-upload.js';
5
+ import { readBoundedPositiveInteger, readOptionalBoundedInteger } from './route-helpers.js';
6
+ import { createDaemonKnowledgeRefinementRouteHandlers } from './knowledge-refinement-routes.js';
3
7
  export function createDaemonKnowledgeRouteHandlers(context) {
4
8
  return {
5
9
  getKnowledgeStatus: async () => Response.json(await context.knowledgeService.getStatus()),
@@ -26,6 +30,13 @@ export function createDaemonKnowledgeRouteHandlers(context) {
26
30
  : Response.json({ error: 'Unknown knowledge connector' }, { status: 404 });
27
31
  },
28
32
  getKnowledgeProjectionTargets: async (url) => Response.json({ targets: await context.knowledgeService.listProjectionTargets(readLimit(url, 25)) }),
33
+ getKnowledgeMap: async (url) => Response.json(await context.knowledgeService.map({
34
+ limit: readLimit(url, 500),
35
+ includeSources: readBooleanQuery(url, 'includeSources'),
36
+ includeIssues: readBooleanQuery(url, 'includeIssues'),
37
+ includeGenerated: readBooleanQuery(url, 'includeGenerated'),
38
+ ...readKnowledgeMapFilters(url),
39
+ })),
29
40
  getKnowledgeGraphqlSchema: () => Response.json({
30
41
  language: 'graphql',
31
42
  domain: 'knowledge',
@@ -95,6 +106,7 @@ export function createDaemonKnowledgeRouteHandlers(context) {
95
106
  const jobId = url.searchParams.get('jobId') ?? undefined;
96
107
  return Response.json({ runs: context.knowledgeService.listJobRuns(readLimit(url, 25), jobId) });
97
108
  },
109
+ ...createDaemonKnowledgeRefinementRouteHandlers(context),
98
110
  getKnowledgeSchedules: (url) => Response.json({ schedules: context.knowledgeService.listSchedules(readLimit(url, 100)) }),
99
111
  getKnowledgeSchedule: (id) => {
100
112
  const schedule = context.knowledgeService.getSchedule(id);
@@ -104,11 +116,14 @@ export function createDaemonKnowledgeRouteHandlers(context) {
104
116
  },
105
117
  postKnowledgeIngestUrl: async (request) => handleKnowledgeIngestUrl(context, request),
106
118
  postKnowledgeIngestArtifact: async (request) => handleKnowledgeIngestArtifact(context, request),
119
+ postKnowledgeSyncBrowserHistory: async (request) => handleKnowledgeSyncBrowserHistory(context, request),
107
120
  postKnowledgeImportBookmarks: async (request) => handleKnowledgeImportBookmarks(context, request),
108
121
  postKnowledgeImportUrls: async (request) => handleKnowledgeImportUrls(context, request),
109
122
  postKnowledgeIngestConnector: async (request) => handleKnowledgeIngestConnector(context, request),
110
123
  postKnowledgeSearch: async (request) => handleKnowledgeSearch(context, request),
124
+ postKnowledgeAsk: async (request) => handleKnowledgeAsk(context, request),
111
125
  postKnowledgePacket: async (request) => handleKnowledgePacket(context, request),
126
+ postKnowledgeReviewIssue: async (id, request) => handleKnowledgeReviewIssue(context, id, request),
112
127
  postKnowledgeDecideCandidate: async (id, request) => handleKnowledgeDecideCandidate(context, id, request),
113
128
  postKnowledgeRunJob: async (jobId, request) => handleKnowledgeRunJob(context, jobId, request),
114
129
  postKnowledgeLint: async (request) => {
@@ -140,7 +155,39 @@ export function createDaemonKnowledgeRouteHandlers(context) {
140
155
  };
141
156
  }
142
157
  function readLimit(url, fallback) {
143
- return Math.max(1, Number(url.searchParams.get('limit') ?? fallback) || fallback);
158
+ return readBoundedPositiveInteger(url.searchParams.get('limit'), fallback, 1_000);
159
+ }
160
+ function readBooleanQuery(url, key) {
161
+ const raw = url.searchParams.get(key);
162
+ if (raw === null || raw.trim() === '')
163
+ return undefined;
164
+ return raw === '1' || raw.toLowerCase() === 'true' || raw.toLowerCase() === 'yes';
165
+ }
166
+ function readKnowledgeMapFilters(url) {
167
+ const minConfidence = readOptionalBoundedInteger(url.searchParams.get('minConfidence'), 0, 100);
168
+ return {
169
+ ...(url.searchParams.get('query') ? { query: url.searchParams.get('query') } : {}),
170
+ ...(Number.isFinite(minConfidence) ? { minConfidence } : {}),
171
+ recordKinds: readStringList(url, 'recordKinds', 'recordKind'),
172
+ ids: readStringList(url, 'ids', 'id'),
173
+ linkedToIds: readStringList(url, 'linkedToIds', 'linkedToId'),
174
+ nodeKinds: readStringList(url, 'nodeKinds', 'nodeKind'),
175
+ sourceTypes: readStringList(url, 'sourceTypes', 'sourceType'),
176
+ sourceStatuses: readStringList(url, 'sourceStatuses', 'sourceStatus'),
177
+ nodeStatuses: readStringList(url, 'nodeStatuses', 'nodeStatus'),
178
+ issueCodes: readStringList(url, 'issueCodes', 'issueCode'),
179
+ issueStatuses: readStringList(url, 'issueStatuses', 'issueStatus'),
180
+ issueSeverities: readStringList(url, 'issueSeverities', 'issueSeverity'),
181
+ edgeRelations: readStringList(url, 'edgeRelations', 'edgeRelation'),
182
+ tags: readStringList(url, 'tags', 'tag'),
183
+ };
184
+ }
185
+ function readStringList(url, ...names) {
186
+ return names
187
+ .flatMap((name) => url.searchParams.getAll(name))
188
+ .flatMap((value) => value.split(','))
189
+ .map((value) => value.trim())
190
+ .filter(Boolean);
144
191
  }
145
192
  function readKnowledgeProjectionRequest(body) {
146
193
  const rawKind = typeof body.kind === 'string' ? body.kind.trim().toLowerCase() : '';
@@ -148,11 +195,15 @@ function readKnowledgeProjectionRequest(body) {
148
195
  && rawKind !== 'bundle'
149
196
  && rawKind !== 'source'
150
197
  && rawKind !== 'node'
151
- && rawKind !== 'issue') {
152
- return Response.json({ error: 'Projection kind must be one of overview, bundle, source, node, or issue.' }, { status: 400 });
198
+ && rawKind !== 'issue'
199
+ && rawKind !== 'dashboard'
200
+ && rawKind !== 'rollup') {
201
+ return Response.json({
202
+ error: 'Projection kind must be one of overview, bundle, source, node, issue, dashboard, or rollup.',
203
+ }, { status: 400 });
153
204
  }
154
205
  const id = typeof body.id === 'string' ? body.id.trim() : '';
155
- if ((rawKind === 'source' || rawKind === 'node' || rawKind === 'issue') && !id) {
206
+ if ((rawKind === 'source' || rawKind === 'node' || rawKind === 'issue' || rawKind === 'rollup') && !id) {
156
207
  return Response.json({ error: `Projection kind ${rawKind} requires id.` }, { status: 400 });
157
208
  }
158
209
  return {
@@ -176,18 +227,18 @@ function readKnowledgeSchedule(context, value) {
176
227
  if (typeof schedule.interval === 'string') {
177
228
  return context.normalizeEverySchedule(schedule.interval, typeof schedule.anchorAt === 'number' ? schedule.anchorAt : undefined);
178
229
  }
179
- throw new Error('Every schedule requires intervalMs or interval.');
230
+ throw new GoodVibesSdkError('Invalid schedule: every schedule requires intervalMs or an interval object. Set schedule.intervalMs (number, in milliseconds) or schedule.interval.', { category: DaemonErrorCategory.BAD_REQUEST, source: 'contract', recoverable: false });
180
231
  case 'cron':
181
232
  if (typeof schedule.expression !== 'string' || !schedule.expression.trim()) {
182
- throw new Error('Cron schedule requires expression.');
233
+ throw new GoodVibesSdkError('Invalid schedule: cron schedule requires a cron expression string in schedule.expression (e.g. "0 9 * * 1-5").', { category: DaemonErrorCategory.BAD_REQUEST, source: 'contract', recoverable: false });
183
234
  }
184
235
  return context.normalizeCronSchedule(schedule.expression, typeof schedule.timezone === 'string' ? schedule.timezone : undefined, schedule.staggerMs);
185
236
  case 'at':
186
237
  if (typeof schedule.at !== 'number')
187
- throw new Error('At schedule requires at.');
238
+ throw new GoodVibesSdkError('Invalid schedule: at schedule requires schedule.at as a Unix timestamp in milliseconds.', { category: DaemonErrorCategory.BAD_REQUEST, source: 'contract', recoverable: false });
188
239
  return context.normalizeAtSchedule(schedule.at);
189
240
  default:
190
- throw new Error('Schedule kind must be at, every, or cron.');
241
+ throw new GoodVibesSdkError('Invalid schedule kind. Expected schedule.kind to be one of: "at", "every", or "cron".', { category: DaemonErrorCategory.BAD_REQUEST, source: 'contract', recoverable: false });
191
242
  }
192
243
  }
193
244
  catch (error) {
@@ -311,7 +362,7 @@ async function handleKnowledgeIngestUrl(context, request) {
311
362
  ...(typeof body.sessionId === 'string' ? { sessionId: body.sessionId } : {}),
312
363
  ...(typeof body.sourceType === 'string' ? { sourceType: body.sourceType } : {}),
313
364
  ...(typeof body.connectorId === 'string' ? { connectorId: body.connectorId } : {}),
314
- ...privateHostFetchOptions,
365
+ ...(privateHostFetchOptions ?? {}),
315
366
  ...(typeof body.metadata === 'object' && body.metadata !== null ? { metadata: body.metadata } : {}),
316
367
  }), { status: 201 });
317
368
  }
@@ -323,6 +374,20 @@ async function handleKnowledgeIngestArtifact(context, request) {
323
374
  const admin = context.requireAdmin(request);
324
375
  if (admin)
325
376
  return admin;
377
+ if (isArtifactUploadRequest(request)) {
378
+ const uploaded = await createArtifactFromUploadRequest(context.artifactStore, request);
379
+ if (uploaded instanceof Response)
380
+ return uploaded;
381
+ try {
382
+ return Response.json(await context.knowledgeService.ingestArtifact({
383
+ ...uploaded.fields,
384
+ artifactId: uploaded.artifactId,
385
+ }), { status: 201 });
386
+ }
387
+ catch (error) {
388
+ return jsonErrorResponse(error, { status: 400 });
389
+ }
390
+ }
326
391
  const body = await context.parseJsonBody(request);
327
392
  if (body instanceof Response)
328
393
  return body;
@@ -340,7 +405,7 @@ async function handleKnowledgeIngestArtifact(context, request) {
340
405
  ...(typeof body.sessionId === 'string' ? { sessionId: body.sessionId } : {}),
341
406
  ...(typeof body.sourceType === 'string' ? { sourceType: body.sourceType } : {}),
342
407
  ...(typeof body.connectorId === 'string' ? { connectorId: body.connectorId } : {}),
343
- ...privateHostFetchOptions,
408
+ ...(privateHostFetchOptions ?? {}),
344
409
  ...(typeof body.metadata === 'object' && body.metadata !== null ? { metadata: body.metadata } : {}),
345
410
  }), { status: 201 });
346
411
  }
@@ -348,6 +413,35 @@ async function handleKnowledgeIngestArtifact(context, request) {
348
413
  return jsonErrorResponse(error, { status: 400 });
349
414
  }
350
415
  }
416
+ async function handleKnowledgeSyncBrowserHistory(context, request) {
417
+ const admin = context.requireAdmin(request);
418
+ if (admin)
419
+ return admin;
420
+ const body = await context.parseOptionalJsonBody(request);
421
+ if (body instanceof Response)
422
+ return body;
423
+ const input = body ?? {};
424
+ const sourceKinds = Array.isArray(input.sourceKinds)
425
+ ? input.sourceKinds.filter((entry) => entry === 'history' || entry === 'bookmark')
426
+ : undefined;
427
+ const browsers = Array.isArray(input.browsers)
428
+ ? input.browsers.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
429
+ : undefined;
430
+ try {
431
+ return Response.json(await context.knowledgeService.syncBrowserHistory({
432
+ ...(typeof input.limit === 'number' ? { limit: Math.max(1, input.limit) } : {}),
433
+ ...(typeof input.sinceMs === 'number' ? { sinceMs: input.sinceMs } : {}),
434
+ ...(typeof input.homeOverride === 'string' ? { homeOverride: input.homeOverride } : {}),
435
+ ...(typeof input.sessionId === 'string' ? { sessionId: input.sessionId } : {}),
436
+ ...(typeof input.connectorId === 'string' ? { connectorId: input.connectorId } : {}),
437
+ ...(browsers?.length ? { browsers } : {}),
438
+ ...(sourceKinds?.length ? { sourceKinds } : {}),
439
+ }), { status: 201 });
440
+ }
441
+ catch (error) {
442
+ return jsonErrorResponse(error, { status: 400 });
443
+ }
444
+ }
351
445
  async function handleKnowledgeImportBookmarks(context, request) {
352
446
  const admin = context.requireAdmin(request);
353
447
  if (admin)
@@ -365,7 +459,7 @@ async function handleKnowledgeImportBookmarks(context, request) {
365
459
  return Response.json(await context.knowledgeService.importBookmarksFromFile({
366
460
  path,
367
461
  ...(typeof body.sessionId === 'string' ? { sessionId: body.sessionId } : {}),
368
- ...privateHostFetchOptions,
462
+ ...(privateHostFetchOptions ?? {}),
369
463
  }), { status: 201 });
370
464
  }
371
465
  catch (error) {
@@ -389,7 +483,7 @@ async function handleKnowledgeImportUrls(context, request) {
389
483
  return Response.json(await context.knowledgeService.importUrlsFromFile({
390
484
  path,
391
485
  ...(typeof body.sessionId === 'string' ? { sessionId: body.sessionId } : {}),
392
- ...privateHostFetchOptions,
486
+ ...(privateHostFetchOptions ?? {}),
393
487
  }), { status: 201 });
394
488
  }
395
489
  catch (error) {
@@ -416,7 +510,7 @@ async function handleKnowledgeIngestConnector(context, request) {
416
510
  ...(typeof body.content === 'string' ? { content: body.content } : {}),
417
511
  ...(typeof body.path === 'string' ? { path: body.path } : {}),
418
512
  ...(typeof body.sessionId === 'string' ? { sessionId: body.sessionId } : {}),
419
- ...privateHostFetchOptions,
513
+ ...(privateHostFetchOptions ?? {}),
420
514
  }), { status: 201 });
421
515
  }
422
516
  catch (error) {
@@ -433,6 +527,26 @@ async function handleKnowledgeSearch(context, request) {
433
527
  const limit = typeof body.limit === 'number' ? body.limit : 10;
434
528
  return Response.json({ results: context.knowledgeService.search(query, limit) });
435
529
  }
530
+ async function handleKnowledgeAsk(context, request) {
531
+ const body = await context.parseJsonBody(request);
532
+ if (body instanceof Response)
533
+ return body;
534
+ const query = typeof body.query === 'string' ? body.query.trim() : '';
535
+ if (!query)
536
+ return Response.json({ error: 'Missing query' }, { status: 400 });
537
+ return Response.json(await context.knowledgeService.ask({
538
+ query,
539
+ ...(typeof body.knowledgeSpaceId === 'string' ? { knowledgeSpaceId: body.knowledgeSpaceId } : {}),
540
+ ...(typeof body.limit === 'number' ? { limit: Math.max(1, body.limit) } : {}),
541
+ ...(typeof body.mode === 'string' && ['concise', 'standard', 'detailed'].includes(body.mode) ? { mode: body.mode } : {}),
542
+ ...(typeof body.includeSources === 'boolean' ? { includeSources: body.includeSources } : {}),
543
+ ...(typeof body.includeConfidence === 'boolean' ? { includeConfidence: body.includeConfidence } : {}),
544
+ ...(typeof body.includeLinkedObjects === 'boolean' ? { includeLinkedObjects: body.includeLinkedObjects } : {}),
545
+ ...(Array.isArray(body.candidateSourceIds) ? { candidateSourceIds: body.candidateSourceIds.filter(isString) } : {}),
546
+ ...(Array.isArray(body.candidateNodeIds) ? { candidateNodeIds: body.candidateNodeIds.filter(isString) } : {}),
547
+ ...(typeof body.strictCandidates === 'boolean' ? { strictCandidates: body.strictCandidates } : {}),
548
+ }));
549
+ }
436
550
  async function handleKnowledgePacket(context, request) {
437
551
  const body = await context.parseJsonBody(request);
438
552
  if (body instanceof Response)
@@ -451,6 +565,32 @@ async function handleKnowledgePacket(context, request) {
451
565
  ...(typeof budgetLimit === 'number' ? { budgetLimit } : {}),
452
566
  }));
453
567
  }
568
+ async function handleKnowledgeReviewIssue(context, id, request) {
569
+ const admin = context.requireAdmin(request);
570
+ if (admin)
571
+ return admin;
572
+ const body = await context.parseOptionalJsonBody(request);
573
+ if (body instanceof Response)
574
+ return body;
575
+ const action = typeof body?.action === 'string' ? body.action.trim().toLowerCase() : '';
576
+ if (!['accept', 'reject', 'resolve', 'reopen', 'edit', 'forget'].includes(action)) {
577
+ return Response.json({ error: 'Action must be accept, reject, resolve, reopen, edit, or forget.' }, { status: 400 });
578
+ }
579
+ try {
580
+ return Response.json(await context.knowledgeService.reviewIssue({
581
+ issueId: id,
582
+ action,
583
+ ...(typeof body?.reviewer === 'string' ? { reviewer: body.reviewer } : {}),
584
+ ...(body?.value && typeof body.value === 'object' && !Array.isArray(body.value) ? { value: body.value } : {}),
585
+ }));
586
+ }
587
+ catch (error) {
588
+ const message = summarizeErrorForRecord(error);
589
+ return jsonErrorResponse(error, {
590
+ status: message.startsWith('Unknown knowledge issue:') ? 404 : 400,
591
+ });
592
+ }
593
+ }
454
594
  async function handleKnowledgeDecideCandidate(context, id, request) {
455
595
  const admin = context.requireAdmin(request);
456
596
  if (admin)
@@ -580,3 +720,6 @@ async function handleKnowledgeMaterializeProjection(context, request) {
580
720
  return jsonErrorResponse(error, { status: 400 });
581
721
  }
582
722
  }
723
+ function isString(value) {
724
+ return typeof value === 'string' && value.trim().length > 0;
725
+ }
@@ -4,6 +4,15 @@ export type MediaArtifact = Record<string, unknown>;
4
4
  export type MultimodalAnalysisResult = unknown;
5
5
  export type MultimodalDetail = string;
6
6
  export type VoiceAudioArtifact = Record<string, unknown>;
7
+ export interface VoiceSynthesisStreamLike {
8
+ readonly providerId: string;
9
+ readonly mimeType: string;
10
+ readonly format: string;
11
+ readonly chunks: AsyncIterable<{
12
+ readonly data: Uint8Array;
13
+ }>;
14
+ readonly metadata: Record<string, unknown>;
15
+ }
7
16
  export type WebSearchSafeSearch = string;
8
17
  export type WebSearchTimeRange = string;
9
18
  export type WebSearchVerbosity = string;
@@ -16,6 +25,7 @@ export interface VoiceServiceLike {
16
25
  }>;
17
26
  listVoices(providerId?: string): Promise<readonly unknown[]>;
18
27
  synthesize(providerId: string | undefined, input: Record<string, unknown>): Promise<unknown>;
28
+ synthesizeStream(providerId: string | undefined, input: Record<string, unknown>): Promise<VoiceSynthesisStreamLike>;
19
29
  transcribe(providerId: string | undefined, input: Record<string, unknown>): Promise<unknown>;
20
30
  openRealtimeSession(providerId: string | undefined, input: Record<string, unknown>): Promise<unknown>;
21
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"media-route-types.d.ts","sourceRoot":"","sources":["../src/media-route-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"media-route-types.d.ts","sourceRoot":"","sources":["../src/media-route-types.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { DaemonApiRouteHandlers } from './context.js';
1
+ import type { DaemonMediaRouteHandlers } from './context.js';
2
2
  import type { DaemonMediaRouteContext } from './media-route-types.js';
3
- export declare function createDaemonMediaRouteHandlers(context: DaemonMediaRouteContext): Pick<DaemonApiRouteHandlers, 'getVoiceStatus' | 'getVoiceProviders' | 'getVoiceVoices' | 'postVoiceTts' | 'postVoiceStt' | 'postVoiceRealtimeSession' | 'getWebSearchProviders' | 'postWebSearch' | 'getArtifacts' | 'postArtifact' | 'getArtifact' | 'getArtifactContent' | 'getMediaProviders' | 'postMediaAnalyze' | 'postMediaTransform' | 'postMediaGenerate' | 'getMultimodalStatus' | 'getMultimodalProviders' | 'postMultimodalAnalyze' | 'postMultimodalPacket' | 'postMultimodalWriteback'>;
3
+ export declare function createDaemonMediaRouteHandlers(context: DaemonMediaRouteContext): DaemonMediaRouteHandlers;
4
4
  //# sourceMappingURL=media-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"media-routes.d.ts","sourceRoot":"","sources":["../src/media-routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,KAAK,EAEV,uBAAuB,EASxB,MAAM,wBAAwB,CAAC;AAIhC,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,uBAAuB,GAC/B,IAAI,CACL,sBAAsB,EACpB,gBAAgB,GAChB,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,cAAc,GACd,0BAA0B,GAC1B,uBAAuB,GACvB,eAAe,GACf,cAAc,GACd,cAAc,GACd,aAAa,GACb,oBAAoB,GACpB,mBAAmB,GACnB,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,qBAAqB,GACrB,wBAAwB,GACxB,uBAAuB,GACvB,sBAAsB,GACtB,yBAAyB,CAC5B,CA+BA"}
1
+ {"version":3,"file":"media-routes.d.ts","sourceRoot":"","sources":["../src/media-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAK7D,OAAO,KAAK,EAEV,uBAAuB,EAUxB,MAAM,wBAAwB,CAAC;AA2BhC,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,uBAAuB,GAC/B,wBAAwB,CAgC1B"}
@@ -1,5 +1,28 @@
1
1
  import { resolvePrivateHostFetchOptions } from './http-policy.js';
2
2
  import { jsonErrorResponse } from './error-response.js';
3
+ import { DaemonErrorCategory } from '@pellux/goodvibes-errors';
4
+ import { createArtifactFromUploadRequest, isArtifactUploadRequest } from './artifact-upload.js';
5
+ function readErrorMessage(error) {
6
+ if (typeof error === 'string')
7
+ return error;
8
+ if (error instanceof Error)
9
+ return error.message;
10
+ if (error && typeof error === 'object' && typeof error.message === 'string') {
11
+ return error.message;
12
+ }
13
+ return 'Unknown error';
14
+ }
15
+ function isProviderNotConfiguredError(error) {
16
+ const msg = readErrorMessage(error).toLowerCase();
17
+ return (msg.includes('not configured')
18
+ || msg.includes('no provider')
19
+ || msg.includes('api key')
20
+ || msg.includes('api_key')
21
+ || msg.includes('missing key')
22
+ || msg.includes('no api')
23
+ || msg.includes('provider not')
24
+ || msg.includes('unconfigured'));
25
+ }
3
26
  export function createDaemonMediaRouteHandlers(context) {
4
27
  return {
5
28
  getVoiceStatus: async () => Response.json(await context.voiceService.getStatus(Boolean(context.configManager.get('ui.voiceEnabled')))),
@@ -8,6 +31,7 @@ export function createDaemonMediaRouteHandlers(context) {
8
31
  }),
9
32
  getVoiceVoices: async (url) => Response.json({ voices: await context.voiceService.listVoices(url.searchParams.get('providerId') ?? undefined) }),
10
33
  postVoiceTts: async (request) => handleVoiceTts(context, request),
34
+ postVoiceTtsStream: async (request) => handleVoiceTtsStream(context, request),
11
35
  postVoiceStt: async (request) => handleVoiceStt(context, request),
12
36
  postVoiceRealtimeSession: async (request) => handleVoiceRealtimeSession(context, request),
13
37
  getWebSearchProviders: async () => Response.json({ providers: await context.webSearchService.getStatus().then((status) => status.providers) }),
@@ -32,27 +56,110 @@ export function createDaemonMediaRouteHandlers(context) {
32
56
  postMultimodalWriteback: async (request) => handleMultimodalWriteback(context, request),
33
57
  };
34
58
  }
59
+ function readOptionalConfigString(context, key) {
60
+ const value = context.configManager.get(key);
61
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
62
+ }
63
+ function readVoiceSynthesisRequest(body, context) {
64
+ const providerId = typeof body.providerId === 'string'
65
+ ? body.providerId
66
+ : context
67
+ ? readOptionalConfigString(context, 'tts.provider')
68
+ : undefined;
69
+ const voiceId = typeof body.voiceId === 'string'
70
+ ? body.voiceId
71
+ : context
72
+ ? readOptionalConfigString(context, 'tts.voice')
73
+ : undefined;
74
+ const modelId = typeof body.modelId === 'string' ? body.modelId : undefined;
75
+ const format = typeof body.format === 'string' ? body.format : undefined;
76
+ const speed = typeof body.speed === 'number' ? body.speed : undefined;
77
+ const input = {
78
+ text: typeof body.text === 'string' ? body.text : '',
79
+ metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
80
+ };
81
+ if (voiceId !== undefined)
82
+ input.voiceId = voiceId;
83
+ if (modelId !== undefined)
84
+ input.modelId = modelId;
85
+ if (format !== undefined)
86
+ input.format = format;
87
+ if (speed !== undefined)
88
+ input.speed = speed;
89
+ return {
90
+ ...(providerId !== undefined ? { providerId } : {}),
91
+ input,
92
+ };
93
+ }
35
94
  async function handleVoiceTts(context, req) {
36
95
  const body = await context.parseJsonBody(req);
37
96
  if (body instanceof Response)
38
97
  return body;
39
- const text = typeof body.text === 'string' ? body.text : '';
40
- if (!text.trim())
98
+ const { providerId, input } = readVoiceSynthesisRequest(body);
99
+ if (!input.text.trim())
41
100
  return Response.json({ error: 'Missing text' }, { status: 400 });
42
101
  try {
43
- const result = await context.voiceService.synthesize(typeof body.providerId === 'string' ? body.providerId : undefined, {
44
- text,
45
- voiceId: typeof body.voiceId === 'string' ? body.voiceId : undefined,
46
- modelId: typeof body.modelId === 'string' ? body.modelId : undefined,
47
- format: typeof body.format === 'string' ? body.format : undefined,
48
- speed: typeof body.speed === 'number' ? body.speed : undefined,
49
- metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
50
- });
102
+ const result = await context.voiceService.synthesize(providerId, input);
51
103
  return Response.json(result);
52
104
  }
53
105
  catch (error) {
54
- return jsonErrorResponse(error, { status: 404 });
106
+ if (isProviderNotConfiguredError(error)) {
107
+ return Response.json({ code: 'PROVIDER_NOT_CONFIGURED', error: readErrorMessage(error), category: DaemonErrorCategory.CONFIG, source: 'provider', recoverable: false, hint: 'Configure the voice provider API key or service credentials.' }, { status: 409 });
108
+ }
109
+ return jsonErrorResponse(error, { status: 400 });
110
+ }
111
+ }
112
+ async function handleVoiceTtsStream(context, req) {
113
+ const body = await context.parseJsonBody(req);
114
+ if (body instanceof Response)
115
+ return body;
116
+ const { providerId, input } = readVoiceSynthesisRequest(body, context);
117
+ if (!input.text.trim())
118
+ return Response.json({ error: 'Missing text' }, { status: 400 });
119
+ try {
120
+ const result = await context.voiceService.synthesizeStream(providerId, { ...input, signal: req.signal });
121
+ return voiceStreamResponse(result);
55
122
  }
123
+ catch (error) {
124
+ if (isProviderNotConfiguredError(error)) {
125
+ return Response.json({ code: 'PROVIDER_NOT_CONFIGURED', error: readErrorMessage(error), category: DaemonErrorCategory.CONFIG, source: 'provider', recoverable: false, hint: 'Configure the streaming TTS provider API key or service credentials.' }, { status: 409 });
126
+ }
127
+ return jsonErrorResponse(error, { status: 400 });
128
+ }
129
+ }
130
+ function voiceStreamResponse(result) {
131
+ const iterator = result.chunks[Symbol.asyncIterator]();
132
+ const stream = new ReadableStream({
133
+ async pull(controller) {
134
+ try {
135
+ while (true) {
136
+ const { done, value } = await iterator.next();
137
+ if (done) {
138
+ controller.close();
139
+ return;
140
+ }
141
+ if (value.data.byteLength > 0) {
142
+ controller.enqueue(value.data);
143
+ return;
144
+ }
145
+ }
146
+ }
147
+ catch (error) {
148
+ controller.error(error);
149
+ }
150
+ },
151
+ async cancel(reason) {
152
+ await iterator.return?.(reason);
153
+ },
154
+ });
155
+ return new Response(stream, {
156
+ headers: {
157
+ 'Content-Type': result.mimeType,
158
+ 'Cache-Control': 'no-store',
159
+ 'X-GoodVibes-Voice-Provider': result.providerId,
160
+ 'X-GoodVibes-Audio-Format': result.format,
161
+ },
162
+ });
56
163
  }
57
164
  async function handleVoiceStt(context, req) {
58
165
  const body = await context.parseJsonBody(req);
@@ -72,7 +179,10 @@ async function handleVoiceStt(context, req) {
72
179
  return Response.json(result);
73
180
  }
74
181
  catch (error) {
75
- return jsonErrorResponse(error, { status: 404 });
182
+ if (isProviderNotConfiguredError(error)) {
183
+ return Response.json({ code: 'PROVIDER_NOT_CONFIGURED', error: readErrorMessage(error), category: DaemonErrorCategory.CONFIG, source: 'provider', recoverable: false, hint: 'Configure the voice provider API key or service credentials.' }, { status: 409 });
184
+ }
185
+ return jsonErrorResponse(error, { status: 400 });
76
186
  }
77
187
  }
78
188
  async function handleVoiceRealtimeSession(context, req) {
@@ -91,7 +201,10 @@ async function handleVoiceRealtimeSession(context, req) {
91
201
  return Response.json(result, { status: 201 });
92
202
  }
93
203
  catch (error) {
94
- return jsonErrorResponse(error, { status: 404 });
204
+ if (isProviderNotConfiguredError(error)) {
205
+ return Response.json({ code: 'PROVIDER_NOT_CONFIGURED', error: readErrorMessage(error), category: DaemonErrorCategory.CONFIG, source: 'provider', recoverable: false, hint: 'Configure the voice provider API key or service credentials.' }, { status: 409 });
206
+ }
207
+ return jsonErrorResponse(error, { status: 400 });
95
208
  }
96
209
  }
97
210
  async function handleMediaAnalyze(context, req) {
@@ -121,6 +234,12 @@ async function handleMediaAnalyze(context, req) {
121
234
  }));
122
235
  }
123
236
  async function handleArtifactCreate(context, req) {
237
+ if (isArtifactUploadRequest(req)) {
238
+ const uploaded = await createArtifactFromUploadRequest(context.artifactStore, req);
239
+ if (uploaded instanceof Response)
240
+ return uploaded;
241
+ return Response.json({ artifact: uploaded.artifact }, { status: 201 });
242
+ }
124
243
  const body = await context.parseJsonBody(req);
125
244
  if (body instanceof Response)
126
245
  return body;
@@ -140,7 +259,7 @@ async function handleArtifactCreate(context, req) {
140
259
  ...(typeof body.text === 'string' ? { text: body.text } : {}),
141
260
  ...(typeof body.path === 'string' ? { path: body.path } : {}),
142
261
  ...(typeof body.uri === 'string' ? { uri: body.uri } : {}),
143
- ...privateHostFetchOptions,
262
+ ...(privateHostFetchOptions ?? {}),
144
263
  ...(typeof body.retentionMs === 'number' ? { retentionMs: body.retentionMs } : {}),
145
264
  ...(typeof body.metadata === 'object' && body.metadata !== null ? { metadata: body.metadata } : {}),
146
265
  });
@@ -254,7 +373,7 @@ async function handleMultimodalAnalyze(context, req) {
254
373
  ...(requestedArtifact ? {
255
374
  artifact: {
256
375
  ...requestedArtifact,
257
- ...privateHostFetchOptions,
376
+ ...(privateHostFetchOptions ?? {}),
258
377
  },
259
378
  } : {}),
260
379
  ...(typeof body.prompt === 'string' ? { prompt: body.prompt } : {}),
@@ -1,3 +1,3 @@
1
- import type { DaemonApiRouteHandlers } from './context.js';
2
- export declare function dispatchOperatorRoutes(req: Request, handlers: Pick<DaemonApiRouteHandlers, 'getStatus' | 'getCurrentAuth' | 'getControlPlaneSnapshot' | 'getOperatorContract' | 'getControlPlaneWeb' | 'getControlPlaneRecentEvents' | 'getControlPlaneMessages' | 'getControlPlaneClients' | 'getTelemetrySnapshot' | 'getTelemetryEvents' | 'getTelemetryErrors' | 'getTelemetryTraces' | 'getTelemetryMetrics' | 'createTelemetryEventStream' | 'getTelemetryOtlpTraces' | 'getTelemetryOtlpLogs' | 'getTelemetryOtlpMetrics' | 'getGatewayMethods' | 'getGatewayEvents' | 'getGatewayMethod' | 'invokeGatewayMethod' | 'createControlPlaneEventStream' | 'getRoutesSnapshot' | 'getSurfaces' | 'getChannelAccounts' | 'getChannelSurfaceAccounts' | 'getChannelAccount' | 'postChannelAccountAction' | 'getChannelSetupSchema' | 'getChannelDoctor' | 'getChannelRepairActions' | 'getChannelLifecycle' | 'postChannelLifecycleMigrate' | 'getChannelCapabilities' | 'getChannelSurfaceCapabilities' | 'getChannelTools' | 'getChannelSurfaceTools' | 'getChannelAgentTools' | 'getChannelSurfaceAgentTools' | 'postChannelTool' | 'getChannelActions' | 'getChannelSurfaceActions' | 'postChannelAction' | 'postChannelResolveTarget' | 'postChannelAuthorize' | 'postChannelAllowlistResolve' | 'postChannelAllowlistEdit' | 'getChannelPolicies' | 'postChannelPolicy' | 'getChannelPolicyAudit' | 'getChannelStatus' | 'getChannelDirectory' | 'getWatchers' | 'postWatcher' | 'patchWatcher' | 'watcherAction' | 'deleteWatcher' | 'getServiceStatus' | 'installService' | 'startService' | 'stopService' | 'restartService' | 'uninstallService' | 'getRouteBindings' | 'postRouteBinding' | 'patchRouteBinding' | 'deleteRouteBinding' | 'getApprovals' | 'approvalAction' | 'getRemote' | 'getHealth' | 'getAccounts' | 'getProviders' | 'getProvider' | 'getProviderUsage' | 'getSettings' | 'getContinuity' | 'getWorktrees' | 'getIntelligence' | 'getLocalAuth' | 'postLocalAuthUser' | 'deleteLocalAuthUser' | 'postLocalAuthPassword' | 'deleteLocalAuthSession' | 'deleteBootstrapFile' | 'getPanels' | 'postPanelOpen' | 'getEvents' | 'getConfig' | 'postConfig' | 'getReview' | 'getIntegrationSession' | 'getIntegrationTasks' | 'getIntegrationAutomation' | 'getIntegrationSessions' | 'getAutomationHeartbeat' | 'postAutomationHeartbeat' | 'getMemoryDoctor' | 'getMemoryVectorStats' | 'postMemoryVectorRebuild' | 'postMemoryEmbeddingDefault' | 'getKnowledgeStatus' | 'getKnowledgeSources' | 'getKnowledgeNodes' | 'getKnowledgeIssues' | 'getKnowledgeItem' | 'getKnowledgeConnectors' | 'getKnowledgeConnector' | 'getKnowledgeConnectorDoctor' | 'getKnowledgeProjectionTargets' | 'getKnowledgeGraphqlSchema' | 'getKnowledgeExtractions' | 'getKnowledgeUsage' | 'getKnowledgeCandidates' | 'getKnowledgeCandidate' | 'getKnowledgeReports' | 'getKnowledgeReport' | 'getKnowledgeExtraction' | 'getKnowledgeSourceExtraction' | 'getKnowledgeJobs' | 'getKnowledgeJob' | 'getKnowledgeJobRuns' | 'getKnowledgeSchedules' | 'getKnowledgeSchedule' | 'postKnowledgeIngestUrl' | 'postKnowledgeIngestArtifact' | 'postKnowledgeImportBookmarks' | 'postKnowledgeImportUrls' | 'postKnowledgeIngestConnector' | 'postKnowledgeSearch' | 'postKnowledgePacket' | 'postKnowledgeDecideCandidate' | 'postKnowledgeRunJob' | 'postKnowledgeLint' | 'postKnowledgeReindex' | 'postKnowledgeSaveSchedule' | 'deleteKnowledgeSchedule' | 'postKnowledgeSetScheduleEnabled' | 'postKnowledgeRenderProjection' | 'postKnowledgeMaterializeProjection' | 'executeKnowledgeGraphql' | 'getVoiceStatus' | 'getVoiceProviders' | 'getVoiceVoices' | 'postVoiceTts' | 'postVoiceStt' | 'postVoiceRealtimeSession' | 'getWebSearchProviders' | 'postWebSearch' | 'getArtifacts' | 'postArtifact' | 'getArtifact' | 'getArtifactContent' | 'getMediaProviders' | 'postMediaAnalyze' | 'postMediaTransform' | 'postMediaGenerate' | 'getMultimodalStatus' | 'getMultimodalProviders' | 'postMultimodalAnalyze' | 'postMultimodalPacket' | 'postMultimodalWriteback' | 'getRemoteNodeHostContract'>): Promise<Response | null>;
1
+ import type { DaemonOperatorRouteHandlers } from './context.js';
2
+ export declare function dispatchOperatorRoutes(req: Request, handlers: DaemonOperatorRouteHandlers): Promise<Response | null>;
3
3
  //# sourceMappingURL=operator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAGhE,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,2BAA2B,GACpC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA8S1B"}