@pellux/goodvibes-agent 1.2.0 → 1.4.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 (85) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/dist/package/{ast-grep-napi.linux-x64-gnu-swtppvy9.node → ast-grep-napi.linux-x64-gnu-mkk8xwww.node} +0 -0
  3. package/dist/package/{ast-grep-napi.linux-x64-musl-ttfcdtap.node → ast-grep-napi.linux-x64-musl-ryqtgdv6.node} +0 -0
  4. package/dist/package/main.js +65740 -57156
  5. package/docs/README.md +3 -3
  6. package/docs/connected-host.md +2 -2
  7. package/docs/getting-started.md +11 -9
  8. package/docs/tools-and-commands.md +12 -10
  9. package/docs/voice-and-live-tts.md +2 -2
  10. package/package.json +1 -1
  11. package/release/release-notes.md +15 -144
  12. package/release/release-readiness.json +36 -36
  13. package/src/agent/competitive-feature-inventory.ts +42 -44
  14. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  15. package/src/agent/setup-wizard.ts +267 -24
  16. package/src/cli/tui-startup.ts +2 -2
  17. package/src/input/agent-workspace-activation.ts +2 -2
  18. package/src/input/agent-workspace-categories.ts +2 -2
  19. package/src/input/agent-workspace-onboarding-categories.ts +4 -4
  20. package/src/input/agent-workspace-onboarding-finish.ts +21 -0
  21. package/src/input/agent-workspace-settings.ts +47 -1
  22. package/src/input/agent-workspace-setup-snapshot.ts +17 -6
  23. package/src/input/agent-workspace-setup.ts +20 -26
  24. package/src/input/agent-workspace-snapshot.ts +16 -3
  25. package/src/input/agent-workspace-types.ts +5 -2
  26. package/src/input/agent-workspace.ts +21 -6
  27. package/src/input/handler.ts +67 -5
  28. package/src/input/setup-wizard-live-receipts.ts +76 -0
  29. package/src/renderer/agent-workspace-context-lines.ts +17 -33
  30. package/src/renderer/agent-workspace.ts +434 -21
  31. package/src/runtime/tool-permission-safety.ts +1 -1
  32. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  33. package/src/tools/agent-harness-agent-orchestration.ts +216 -128
  34. package/src/tools/agent-harness-autonomy-live-records.ts +154 -0
  35. package/src/tools/agent-harness-autonomy-queue-types.ts +1 -1
  36. package/src/tools/agent-harness-autonomy-queue.ts +19 -8
  37. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  38. package/src/tools/agent-harness-background-processes.ts +8 -4
  39. package/src/tools/agent-harness-browser-cockpit-route.ts +188 -34
  40. package/src/tools/agent-harness-browser-control.ts +12 -4
  41. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  42. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  43. package/src/tools/agent-harness-execution-posture.ts +3 -0
  44. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  45. package/src/tools/agent-harness-local-model-benchmarks.ts +71 -1
  46. package/src/tools/agent-harness-local-model-endpoints.ts +469 -354
  47. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  48. package/src/tools/agent-harness-local-model-url.ts +78 -0
  49. package/src/tools/agent-harness-media-posture.ts +27 -12
  50. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  51. package/src/tools/agent-harness-memory-posture.ts +253 -137
  52. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  53. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  54. package/src/tools/agent-harness-mode-catalog.ts +4 -2
  55. package/src/tools/agent-harness-model-provider-health.ts +139 -42
  56. package/src/tools/agent-harness-model-readiness.ts +31 -5
  57. package/src/tools/agent-harness-model-routing-types.ts +61 -0
  58. package/src/tools/agent-harness-model-routing.ts +31 -6
  59. package/src/tools/agent-harness-pairing-posture.ts +30 -9
  60. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  61. package/src/tools/agent-harness-personal-ops-lanes.ts +81 -15
  62. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  63. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  64. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  65. package/src/tools/agent-harness-personal-ops-records.ts +176 -224
  66. package/src/tools/agent-harness-personal-ops-types.ts +19 -1
  67. package/src/tools/agent-harness-personal-ops.ts +18 -11
  68. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  69. package/src/tools/agent-harness-research-briefing.ts +26 -7
  70. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  71. package/src/tools/agent-harness-research-runs.ts +15 -3
  72. package/src/tools/agent-harness-research-workflow.ts +92 -19
  73. package/src/tools/agent-harness-setup-model-helpers.ts +1 -0
  74. package/src/tools/agent-harness-setup-posture.ts +7 -7
  75. package/src/tools/agent-harness-setup-smoke.ts +26 -1
  76. package/src/tools/agent-harness-tool-schema.ts +23 -1
  77. package/src/tools/agent-harness-tool-types.ts +5 -0
  78. package/src/tools/agent-harness-tool.ts +9 -1
  79. package/src/tools/agent-harness-workspace-actions.ts +1 -1
  80. package/src/tools/agent-memory-tool.ts +40 -1
  81. package/src/tools/agent-model-compare-run.ts +13 -0
  82. package/src/tools/agent-research-runner.ts +367 -0
  83. package/src/tools/agent-research-tool.ts +7 -179
  84. package/src/tools/agent-route-planner-candidates-surfaces.ts +1 -1
  85. package/src/version.ts +1 -1
@@ -2,10 +2,12 @@ import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifact
2
2
  import type { CommandContext } from '../input/command-registry.ts';
3
3
  import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
4
4
  import { previewHarnessText } from './agent-harness-text.ts';
5
- import { hasMethod } from './agent-harness-personal-ops-discovery.ts';
6
5
  import { redactedPersonalOpsText } from './agent-harness-personal-ops-runner.ts';
6
+ import { personalOpsRecordCertification } from './agent-harness-personal-ops-certification.ts';
7
7
  import type { PersonalOpsConnectorSignal, PersonalOpsConnectorTool, PersonalOpsLane, PersonalOpsLaneId, PersonalOpsLiveRecord, PersonalOpsRecordFreshness, PersonalOpsStatus, PersonalOpsWorkflow, PersonalOpsWorkflowStatus } from './agent-harness-personal-ops-types.ts';
8
8
 
9
+ type PersonalOpsEffectReceiptLaneId = Extract<PersonalOpsLaneId, 'inbox' | 'calendar' | 'tasks' | 'reminders'>;
10
+
9
11
  export function laneStatusRank(status: PersonalOpsStatus): number {
10
12
  if (status === 'ready') return 4;
11
13
  if (status === 'partial') return 3;
@@ -61,6 +63,15 @@ export function searchText(lane: PersonalOpsLane): string {
61
63
  record.freshness.refreshRoute ?? '',
62
64
  record.freshness.policy,
63
65
  ].join('\n') : '',
66
+ record.certification ? [
67
+ record.certification.schemaStatus,
68
+ record.certification.schemaVersion ?? '',
69
+ record.certification.publicationGuarantee ?? '',
70
+ record.certification.publisher ?? '',
71
+ record.certification.provenance?.join('\n') ?? '',
72
+ record.certification.receiptIds?.join('\n') ?? '',
73
+ record.certification.missingSignals.join('\n'),
74
+ ].join('\n') : '',
64
75
  ]).join('\n') ?? '',
65
76
  ].join('\n').toLowerCase();
66
77
  }
@@ -70,7 +81,7 @@ export function describeLiveRecord(record: PersonalOpsLiveRecord, includeParamet
70
81
  id: record.id,
71
82
  label: record.label,
72
83
  status: record.status,
73
- summary: previewHarnessText(record.summary, includeParameters ? 180 : 96),
84
+ summary: previewHarnessText(record.summary, includeParameters ? 240 : 96),
74
85
  userRoute: previewHarnessText(record.userRoute, includeParameters ? 140 : 96),
75
86
  modelRoute: previewHarnessText(record.modelRoute, includeParameters ? 140 : 96),
76
87
  ...(record.tags && record.tags.length > 0 ? { tags: record.tags.slice(0, includeParameters ? 12 : 4) } : {}),
@@ -85,6 +96,7 @@ export function describeLiveRecord(record: PersonalOpsLiveRecord, includeParamet
85
96
  ...(includeParameters && typeof record.reviewRecordCount === 'number' ? { reviewRecordCount: record.reviewRecordCount } : {}),
86
97
  ...(includeParameters && record.reviewLabels && record.reviewLabels.length > 0 ? { reviewLabels: record.reviewLabels } : {}),
87
98
  ...(includeParameters && record.sourceTool ? { sourceTool: record.sourceTool } : {}),
99
+ ...(includeParameters && record.certification ? { certification: record.certification } : {}),
88
100
  ...(includeParameters && record.freshness ? { freshness: record.freshness } : {}),
89
101
  ...(includeParameters && record.followUpRoutes && record.followUpRoutes.length > 0 ? { followUpRoutes: record.followUpRoutes } : {}),
90
102
  };
@@ -279,6 +291,14 @@ export function artifactMetadataStringArray(artifact: ArtifactDescriptor, key: s
279
291
  return [];
280
292
  }
281
293
 
294
+ const PERSONAL_OPS_EFFECT_RECEIPT_PURPOSES = new Set([
295
+ 'personal-ops-provider-effect-receipt',
296
+ 'personal-ops-effect-receipt',
297
+ 'personal-ops-connector-effect-receipt',
298
+ 'agent-personal-ops-effect-receipt',
299
+ 'connected-host-personal-ops-effect-receipt',
300
+ ]);
301
+
282
302
  export function safeRecordIdPart(value: string): string {
283
303
  return value
284
304
  .toLowerCase()
@@ -287,6 +307,24 @@ export function safeRecordIdPart(value: string): string {
287
307
  .slice(0, 80) || 'item';
288
308
  }
289
309
 
310
+ function artifactCreatedAtIso(artifact: ArtifactDescriptor): string {
311
+ return typeof artifact.createdAt === 'number' && Number.isFinite(artifact.createdAt)
312
+ ? new Date(artifact.createdAt).toISOString()
313
+ : '';
314
+ }
315
+
316
+ function metadataCreatedAtIso(artifact: ArtifactDescriptor): string {
317
+ const explicit = artifactMetadataString(artifact, 'createdAt')
318
+ || artifactMetadataString(artifact, 'recordedAt')
319
+ || artifactMetadataString(artifact, 'completedAt')
320
+ || artifactMetadataString(artifact, 'timestamp');
321
+ if (explicit) {
322
+ const parsed = Date.parse(explicit);
323
+ if (Number.isFinite(parsed)) return new Date(parsed).toISOString();
324
+ }
325
+ return artifactCreatedAtIso(artifact);
326
+ }
327
+
290
328
  export function savedReviewArtifacts(context: CommandContext, laneId: 'inbox' | 'calendar'): readonly ArtifactDescriptor[] {
291
329
  const store = context.platform.artifactStore;
292
330
  if (!store?.list) return [];
@@ -305,6 +343,141 @@ export function savedReviewArtifacts(context: CommandContext, laneId: 'inbox' |
305
343
  }
306
344
  }
307
345
 
346
+ function savedProviderEffectReceiptArtifacts(context: CommandContext, laneId: PersonalOpsEffectReceiptLaneId): readonly ArtifactDescriptor[] {
347
+ const store = context.platform.artifactStore;
348
+ if (!store?.list) return [];
349
+ try {
350
+ return store.list(100)
351
+ .filter((artifact) => PERSONAL_OPS_EFFECT_RECEIPT_PURPOSES.has(artifactMetadataString(artifact, 'purpose')))
352
+ .filter((artifact) => artifactMetadataString(artifact, 'laneId') === laneId)
353
+ .sort((left, right) => {
354
+ const leftCreated = typeof left.createdAt === 'number' ? left.createdAt : 0;
355
+ const rightCreated = typeof right.createdAt === 'number' ? right.createdAt : 0;
356
+ return rightCreated - leftCreated;
357
+ })
358
+ .slice(0, 10);
359
+ } catch {
360
+ return [];
361
+ }
362
+ }
363
+
364
+ function normalizedEffectReceiptStatus(artifact: ArtifactDescriptor): string {
365
+ const raw = (artifactMetadataString(artifact, 'status')
366
+ || artifactMetadataString(artifact, 'outcome')
367
+ || artifactMetadataString(artifact, 'result')).toLowerCase();
368
+ if (!raw) return 'unknown';
369
+ if (['ok', 'ready', 'success', 'succeeded', 'complete', 'completed', 'sent', 'archived', 'labeled', 'updated', 'accepted', 'declined'].includes(raw)) return 'succeeded';
370
+ if (['blocked', 'needs-review', 'needs_setup', 'needs-setup'].includes(raw)) return 'blocked';
371
+ if (['fail', 'failed', 'error', 'errored'].includes(raw)) return 'failed';
372
+ if (['running', 'pending', 'in-progress', 'in_progress'].includes(raw)) return 'running';
373
+ return raw;
374
+ }
375
+
376
+ function providerEffectSubjectId(artifact: ArtifactDescriptor): string {
377
+ return artifactMetadataString(artifact, 'subjectId')
378
+ || artifactMetadataString(artifact, 'threadId')
379
+ || artifactMetadataString(artifact, 'messageId')
380
+ || artifactMetadataString(artifact, 'eventId')
381
+ || artifactMetadataString(artifact, 'taskId')
382
+ || artifactMetadataString(artifact, 'reminderId')
383
+ || artifactMetadataString(artifact, 'scheduleId')
384
+ || artifactMetadataString(artifact, 'providerRecordId')
385
+ || artifactMetadataString(artifact, 'targetId');
386
+ }
387
+
388
+ function effectReceiptLaneLabel(laneId: PersonalOpsEffectReceiptLaneId): string {
389
+ if (laneId === 'inbox') return 'Inbox';
390
+ if (laneId === 'calendar') return 'Calendar';
391
+ if (laneId === 'tasks') return 'Task';
392
+ return 'Reminder';
393
+ }
394
+
395
+ function effectReceiptCapability(laneId: PersonalOpsEffectReceiptLaneId): string {
396
+ if (laneId === 'inbox') return 'inbox-effect-receipt';
397
+ if (laneId === 'calendar') return 'calendar-effect-receipt';
398
+ if (laneId === 'tasks') return 'task-effect-receipt';
399
+ return 'reminder-effect-receipt';
400
+ }
401
+
402
+ export function savedProviderEffectReceiptRecords(
403
+ context: CommandContext,
404
+ laneId: PersonalOpsEffectReceiptLaneId,
405
+ ): readonly PersonalOpsLiveRecord[] {
406
+ return savedProviderEffectReceiptArtifacts(context, laneId).map((artifact) => {
407
+ const laneLabel = effectReceiptLaneLabel(laneId);
408
+ const lowerLaneLabel = laneLabel.toLowerCase();
409
+ const providerId = artifactMetadataString(artifact, 'providerId') || artifactMetadataString(artifact, 'provider') || 'provider';
410
+ const operation = artifactMetadataString(artifact, 'operation') || artifactMetadataString(artifact, 'action') || `${laneId}-effect`;
411
+ const status = normalizedEffectReceiptStatus(artifact);
412
+ const sourceTool = artifactMetadataString(artifact, 'sourceTool') || artifactMetadataString(artifact, 'qualifiedName');
413
+ const subjectId = providerEffectSubjectId(artifact);
414
+ const receiptId = artifactMetadataString(artifact, 'receiptId') || artifact.id;
415
+ const createdAt = metadataCreatedAtIso(artifact);
416
+ const failureReason = artifactMetadataString(artifact, 'failureReason') || artifactMetadataString(artifact, 'error');
417
+ const redaction = artifactMetadataString(artifact, 'redaction') || artifactMetadataString(artifact, 'redactionPolicy') || 'metadata-only';
418
+ const nextRoute = artifactMetadataString(artifact, 'nextRoute')
419
+ || `personal_ops action:"lane" laneId:"${laneId}" includeParameters:true`;
420
+ const artifactRoute = `agent_artifacts show artifactId:"${artifact.id}" includeContent:false`;
421
+ const operationLabel = operation.replace(/[-_]+/g, ' ');
422
+ const certification = personalOpsRecordCertification({
423
+ record: artifactMetadata(artifact),
424
+ sourcePath: sourceTool || artifactRoute,
425
+ durableId: subjectId || receiptId,
426
+ recordKind: `${lowerLaneLabel} provider-effect receipt`,
427
+ hasConfirmedEffectRoute: true,
428
+ requireReceipt: true,
429
+ });
430
+ return {
431
+ id: `provider-effect-receipt:${artifact.id}`,
432
+ label: `${laneLabel} effect receipt: ${operationLabel}`,
433
+ status,
434
+ summary: [
435
+ `Confirmed ${lowerLaneLabel} provider-effect receipt ${receiptId} reports ${operation} ${status}.`,
436
+ providerId ? `Provider ${providerId}.` : '',
437
+ subjectId ? `Subject ${previewHarnessText(redactedPersonalOpsText(subjectId), 96)}.` : '',
438
+ `Redaction ${redaction}.`,
439
+ failureReason ? `Failure ${previewHarnessText(redactedPersonalOpsText(failureReason), 140)}.` : '',
440
+ sourceTool ? `Source ${sourceTool}.` : '',
441
+ createdAt ? `Recorded ${createdAt}.` : '',
442
+ ].filter(Boolean).join(' '),
443
+ userRoute: `Agent Workspace -> Personal Ops -> ${laneLabel} effect receipts`,
444
+ modelRoute: artifactRoute,
445
+ tags: [
446
+ 'provider-effect-receipt',
447
+ 'artifact',
448
+ `${laneId}-effect`,
449
+ operation,
450
+ status,
451
+ providerId,
452
+ ].filter(Boolean),
453
+ effect: 'read-only',
454
+ capability: effectReceiptCapability(laneId),
455
+ confirmationRequired: false,
456
+ artifactId: artifact.id,
457
+ ...(sourceTool ? { sourceTool } : {}),
458
+ certification,
459
+ followUpRoutes: [
460
+ {
461
+ id: 'inspect-effect-receipt',
462
+ label: 'Inspect provider-effect receipt',
463
+ effect: 'read-only',
464
+ modelRoute: artifactRoute,
465
+ requiresConfirmation: false,
466
+ policy: 'Receipt inspection is read-only and uses redacted artifact metadata/content boundaries.',
467
+ },
468
+ {
469
+ id: 'continue-provider-lane',
470
+ label: `Continue ${lowerLaneLabel} lane review`,
471
+ effect: 'read-only',
472
+ modelRoute: nextRoute,
473
+ requiresConfirmation: false,
474
+ policy: 'Use the lane posture to decide whether a fresh connector read or separate confirmed provider effect is appropriate.',
475
+ },
476
+ ],
477
+ };
478
+ });
479
+ }
480
+
308
481
  export function matchingReadTool(
309
482
  connectors: readonly PersonalOpsConnectorSignal[],
310
483
  sourceTool: string,
@@ -508,225 +681,4 @@ export function savedReviewArtifactRecords(
508
681
  });
509
682
  }
510
683
 
511
- export function taskOperationRecords(methodIds: readonly string[]): readonly PersonalOpsLiveRecord[] {
512
- const records: PersonalOpsLiveRecord[] = [
513
- {
514
- id: 'workplan-list',
515
- label: 'Review visible work plan',
516
- status: 'ready',
517
- summary: 'Read Agent-owned work-plan items before starting or switching multi-step work.',
518
- userRoute: 'Agent Workspace -> Work -> Review work plan',
519
- modelRoute: 'agent_work_plan action:"list"',
520
- tags: ['work-plan', 'task-read'],
521
- effect: 'read-only',
522
- capability: 'task-read',
523
- },
524
- {
525
- id: 'workplan-add',
526
- label: 'Add visible work item',
527
- status: 'ready',
528
- summary: 'Create one local Agent work-plan item instead of hiding task state in chat.',
529
- userRoute: 'Agent Workspace -> Personal Ops -> Add work item',
530
- modelRoute: 'agent_work_plan action:"create" title:"..."',
531
- tags: ['work-plan', 'task-write'],
532
- effect: 'confirmed-effect',
533
- capability: 'task-write',
534
- requiredFields: ['title'],
535
- optionalFields: ['detail', 'priority', 'status'],
536
- confirmationRequired: false,
537
- },
538
- {
539
- id: 'workplan-status',
540
- label: 'Update work item status',
541
- status: 'ready',
542
- summary: 'Move one visible work item through pending, active, blocked, done, failed, or cancelled state.',
543
- userRoute: 'Agent Workspace -> Work -> Update work item status',
544
- modelRoute: 'agent_work_plan action:"set_status" id:"..." status:"..."',
545
- tags: ['work-plan', 'task-write'],
546
- effect: 'confirmed-effect',
547
- capability: 'task-write',
548
- requiredFields: ['id', 'status'],
549
- confirmationRequired: false,
550
- },
551
- ];
552
- if (hasMethod(methodIds, 'tasks.list')) {
553
- records.push({
554
- id: 'host-tasks-list',
555
- label: 'List connected-host tasks',
556
- status: 'ready',
557
- summary: 'Inspect connected-host task state without creating, retrying, or mutating host tasks.',
558
- userRoute: 'Agent Workspace -> Work -> Host tasks',
559
- modelRoute: 'workspace action:"action" actionId:"tasks-list"',
560
- tags: ['host-task', 'task-read'],
561
- effect: 'read-only',
562
- capability: 'host-task-read',
563
- });
564
- }
565
- if (hasMethod(methodIds, 'tasks.get') || hasMethod(methodIds, 'tasks.status')) {
566
- records.push({
567
- id: 'host-task-inspect',
568
- label: 'Inspect connected-host task',
569
- status: 'ready',
570
- summary: 'Inspect one exact connected-host task id and output before considering controls.',
571
- userRoute: 'Agent Workspace -> Work -> Inspect host task',
572
- modelRoute: 'workspace action:"action" actionId:"task-show"',
573
- tags: ['host-task', 'task-read'],
574
- effect: 'read-only',
575
- capability: 'host-task-read',
576
- requiredFields: ['taskId'],
577
- });
578
- }
579
- if (hasMethod(methodIds, 'tasks.cancel')) {
580
- records.push({
581
- id: 'host-task-cancel',
582
- label: 'Cancel connected-host task',
583
- status: 'ready',
584
- summary: 'Cancel one exact connected-host task id only when the user authorizes it.',
585
- userRoute: 'Agent Workspace -> Work -> Host task controls',
586
- modelRoute: 'agent_operator_method methodId:"tasks.cancel" input:{"taskId":"..."} confirm:true explicitUserRequest:"..."',
587
- tags: ['host-task', 'task-write'],
588
- effect: 'confirmed-effect',
589
- capability: 'host-task-control',
590
- requiredFields: ['taskId'],
591
- confirmationRequired: true,
592
- });
593
- }
594
- if (hasMethod(methodIds, 'tasks.retry')) {
595
- records.push({
596
- id: 'host-task-retry',
597
- label: 'Retry connected-host task',
598
- status: 'ready',
599
- summary: 'Retry one failed or cancelled connected-host task id only after inspection.',
600
- userRoute: 'Agent Workspace -> Work -> Host task controls',
601
- modelRoute: 'agent_operator_method methodId:"tasks.retry" input:{"taskId":"..."} confirm:true explicitUserRequest:"..."',
602
- tags: ['host-task', 'task-write'],
603
- effect: 'confirmed-effect',
604
- capability: 'host-task-control',
605
- requiredFields: ['taskId'],
606
- confirmationRequired: true,
607
- });
608
- }
609
- return records;
610
- }
611
-
612
- export function reminderOperationRecords(methodIds: readonly string[], deliveryConfigured: boolean): readonly PersonalOpsLiveRecord[] {
613
- const records: PersonalOpsLiveRecord[] = [
614
- {
615
- id: 'reminder-create',
616
- label: 'Create confirmed reminder',
617
- status: hasMethod(methodIds, 'schedules.create') ? deliveryConfigured ? 'ready' : 'attention' : 'needs-setup',
618
- summary: deliveryConfigured
619
- ? 'Create one connected reminder schedule with real timing and a visible delivery path.'
620
- : 'Create one reminder only after confirming timing and delivery scope; no configured delivery target was detected.',
621
- userRoute: 'Agent Workspace -> Personal Ops -> Create reminder',
622
- modelRoute: 'schedule action:"remind" message:"..." scheduleKind:"..." scheduleValue:"..." confirm:true explicitUserRequest:"..."',
623
- tags: ['reminder', 'schedule-write'],
624
- effect: 'confirmed-effect',
625
- capability: 'reminder-create',
626
- requiredFields: ['title', 'scheduleKind', 'scheduleValue'],
627
- optionalFields: ['deliveryTargetId', 'timezone', 'message'],
628
- confirmationRequired: true,
629
- },
630
- {
631
- id: 'autonomous-schedule-create',
632
- label: 'Create autonomous schedule',
633
- status: hasMethod(methodIds, 'schedules.create') ? 'ready' : 'needs-setup',
634
- summary: 'Create one visible autonomous schedule only when task, cadence, success criteria, and user request provenance are explicit.',
635
- userRoute: 'Agent Workspace -> Automation -> Create schedule',
636
- modelRoute: 'schedule action:"create" task:"..." successCriteria:"..." scheduleKind:"..." scheduleValue:"..." confirm:true explicitUserRequest:"..."',
637
- tags: ['autonomy', 'schedule-write'],
638
- effect: 'confirmed-effect',
639
- capability: 'schedule-create',
640
- requiredFields: ['task', 'successCriteria', 'scheduleKind', 'scheduleValue'],
641
- confirmationRequired: true,
642
- },
643
- ];
644
- if (hasMethod(methodIds, 'schedules.list')) {
645
- records.push({
646
- id: 'schedule-list',
647
- label: 'List connected schedules',
648
- status: 'ready',
649
- summary: 'Inspect configured schedules and history before running or mutating one.',
650
- userRoute: 'Agent Workspace -> Automation -> Schedules',
651
- modelRoute: 'workspace action:"action" actionId:"schedule-list"',
652
- tags: ['schedule', 'schedule-read'],
653
- effect: 'read-only',
654
- capability: 'schedule-read',
655
- });
656
- records.push({
657
- id: 'schedule-edit',
658
- label: 'Edit connected schedule',
659
- status: 'ready',
660
- summary: 'Preview and edit one exact connected schedule id with before/after diff context.',
661
- userRoute: 'Agent Workspace -> Automation -> Edit schedule',
662
- modelRoute: 'schedule action:"edit" scheduleId:"..." confirm:true explicitUserRequest:"..."',
663
- tags: ['schedule', 'schedule-write'],
664
- effect: 'confirmed-effect',
665
- capability: 'schedule-control',
666
- requiredFields: ['scheduleId'],
667
- optionalFields: ['name', 'scheduleKind', 'scheduleValue', 'prompt'],
668
- confirmationRequired: true,
669
- });
670
- }
671
- if (hasMethod(methodIds, 'schedules.run')) {
672
- records.push({
673
- id: 'schedule-run-now',
674
- label: 'Run schedule now',
675
- status: 'ready',
676
- summary: 'Run one exact connected schedule id now after the user confirms.',
677
- userRoute: 'Agent Workspace -> Automation -> Run job now',
678
- modelRoute: 'schedule action:"run" scheduleId:"..." confirm:true explicitUserRequest:"..."',
679
- tags: ['schedule', 'schedule-write'],
680
- effect: 'confirmed-effect',
681
- capability: 'schedule-control',
682
- requiredFields: ['scheduleId'],
683
- confirmationRequired: true,
684
- });
685
- }
686
- if (hasMethod(methodIds, 'schedules.disable')) {
687
- records.push({
688
- id: 'schedule-pause',
689
- label: 'Pause connected schedule',
690
- status: 'ready',
691
- summary: 'Disable one exact connected schedule id after reviewing current state.',
692
- userRoute: 'Agent Workspace -> Automation -> Schedule controls',
693
- modelRoute: 'schedule action:"pause" scheduleId:"..." confirm:true explicitUserRequest:"..."',
694
- tags: ['schedule', 'schedule-write'],
695
- effect: 'confirmed-effect',
696
- capability: 'schedule-control',
697
- requiredFields: ['scheduleId'],
698
- confirmationRequired: true,
699
- });
700
- }
701
- if (hasMethod(methodIds, 'schedules.enable')) {
702
- records.push({
703
- id: 'schedule-resume',
704
- label: 'Resume connected schedule',
705
- status: 'ready',
706
- summary: 'Enable one exact connected schedule id after reviewing current state.',
707
- userRoute: 'Agent Workspace -> Automation -> Schedule controls',
708
- modelRoute: 'schedule action:"resume" scheduleId:"..." confirm:true explicitUserRequest:"..."',
709
- tags: ['schedule', 'schedule-write'],
710
- effect: 'confirmed-effect',
711
- capability: 'schedule-control',
712
- requiredFields: ['scheduleId'],
713
- confirmationRequired: true,
714
- });
715
- }
716
- if (hasMethod(methodIds, 'schedules.delete')) {
717
- records.push({
718
- id: 'schedule-delete',
719
- label: 'Delete connected schedule',
720
- status: 'ready',
721
- summary: 'Delete one exact connected schedule id only after explicit user confirmation.',
722
- userRoute: 'Agent Workspace -> Automation -> Schedule controls',
723
- modelRoute: 'schedule action:"delete" scheduleId:"..." confirm:true explicitUserRequest:"..."',
724
- tags: ['schedule', 'schedule-write'],
725
- effect: 'confirmed-effect',
726
- capability: 'schedule-control',
727
- requiredFields: ['scheduleId'],
728
- confirmationRequired: true,
729
- });
730
- }
731
- return records;
732
- }
684
+ export { reminderOperationRecords, taskOperationRecords } from './agent-harness-personal-ops-operations.ts';
@@ -94,10 +94,25 @@ export interface PersonalOpsLiveRecord {
94
94
  readonly reviewRecordCount?: number;
95
95
  readonly reviewLabels?: readonly string[];
96
96
  readonly sourceTool?: string;
97
+ readonly certification?: PersonalOpsRecordCertification;
97
98
  readonly followUpRoutes?: readonly PersonalOpsFollowUpRoute[];
98
99
  readonly freshness?: PersonalOpsRecordFreshness;
99
100
  }
100
101
 
102
+ export interface PersonalOpsRecordCertification {
103
+ readonly schemaStatus: 'certified' | 'legacy';
104
+ readonly schemaVersion?: string;
105
+ readonly publicationGuarantee?: string;
106
+ readonly publisher?: string;
107
+ readonly provenance?: readonly string[];
108
+ readonly receiptId?: string;
109
+ readonly receiptStatus?: string;
110
+ readonly receiptRoute?: string;
111
+ readonly receiptIds?: readonly string[];
112
+ readonly missingSignals: readonly string[];
113
+ readonly policy: string;
114
+ }
115
+
101
116
  export interface PersonalOpsFollowUpRoute {
102
117
  readonly id: string;
103
118
  readonly label: string;
@@ -119,11 +134,12 @@ export interface PersonalOpsRoutePacket {
119
134
  export interface PersonalOpsRecordFreshness {
120
135
  readonly status:
121
136
  | 'fresh-provider-route-ready'
137
+ | 'fresh-provider-record-current'
122
138
  | 'saved-review-refreshable'
123
139
  | 'connector-attention'
124
140
  | 'provider-contract-missing'
125
141
  | 'source-tool-missing';
126
- readonly source: 'connector-read' | 'saved-review-artifact';
142
+ readonly source: 'connector-read' | 'saved-review-artifact' | 'daemon-read-model' | 'sdk-read-model';
127
143
  readonly sourceTool?: string;
128
144
  readonly lastReviewedAt?: string;
129
145
  readonly refreshRoute?: string;
@@ -215,6 +231,8 @@ export const PERSONAL_OPS_READ_CONTROL_FIELDS = new Set(['saveReviewCards', 'sav
215
231
  export const QUEUE_CAPABILITIES = new Set([
216
232
  'inbox-read',
217
233
  'calendar-read',
234
+ 'inbox-provider-thread',
235
+ 'calendar-provider-event',
218
236
  'inbox-thread-review',
219
237
  'calendar-event-review',
220
238
  'inbox-review-artifact',
@@ -52,6 +52,11 @@ function recordCount(lane: PersonalOpsLane, predicate: (record: PersonalOpsLiveR
52
52
  return (lane.liveRecords ?? []).filter(predicate).length;
53
53
  }
54
54
 
55
+ function isFreshProviderRecord(record: PersonalOpsLiveRecord): boolean {
56
+ return record.freshness?.status === 'fresh-provider-route-ready'
57
+ || record.freshness?.status === 'fresh-provider-record-current';
58
+ }
59
+
55
60
  function workflowCount(lane: PersonalOpsLane, status: PersonalOpsWorkflowStatus): number {
56
61
  return (lane.workflows ?? []).filter((workflow) => workflow.status === status).length;
57
62
  }
@@ -67,12 +72,12 @@ function briefingPurpose(lane: PersonalOpsLane): string {
67
72
  }
68
73
 
69
74
  function briefingNext(lane: PersonalOpsLane): string {
70
- const freshProviderReads = recordCount(lane, (record) => record.freshness?.status === 'fresh-provider-route-ready');
75
+ const freshProviderReads = recordCount(lane, isFreshProviderRecord);
71
76
  const refreshableSavedRecords = recordCount(lane, (record) => record.freshness?.status === 'saved-review-refreshable');
72
77
  const savedReviewRecords = recordCount(lane, (record) => record.freshness?.source === 'saved-review-artifact' || typeof record.reviewRecordCount === 'number');
73
78
  const attentionWorkflows = workflowCount(lane, 'attention');
74
79
  if ((lane.id === 'inbox' || lane.id === 'calendar') && freshProviderReads > 0) {
75
- return 'Pick one bounded read-only record and run it with personal_ops action:"read" only when the user asks for live provider data.';
80
+ return 'Inspect one current provider-backed record first; run a bounded refresh/read route only when the returned record exposes one and the user asks for live provider data.';
76
81
  }
77
82
  if ((lane.id === 'inbox' || lane.id === 'calendar') && refreshableSavedRecords > 0) {
78
83
  return 'Recap the saved redacted queue first; refresh a single record only through the returned confirmed read route.';
@@ -89,7 +94,7 @@ function briefingNext(lane: PersonalOpsLane): string {
89
94
  function briefingStepForLane(lane: PersonalOpsLane, includeParameters: boolean): Record<string, unknown> {
90
95
  const records = lane.liveRecords ?? [];
91
96
  const workflows = lane.workflows ?? [];
92
- const freshProviderReads = recordCount(lane, (record) => record.freshness?.status === 'fresh-provider-route-ready');
97
+ const freshProviderReads = recordCount(lane, isFreshProviderRecord);
93
98
  const refreshableSavedRecords = recordCount(lane, (record) => record.freshness?.status === 'saved-review-refreshable');
94
99
  const savedReviewRecords = recordCount(lane, (record) => record.freshness?.source === 'saved-review-artifact' || typeof record.reviewRecordCount === 'number');
95
100
  const readOnlyRecords = recordCount(lane, (record) => record.effect === 'read-only');
@@ -105,7 +110,7 @@ function briefingStepForLane(lane: PersonalOpsLane, includeParameters: boolean):
105
110
  `${records.length} live/operation record(s)`,
106
111
  `${workflowCount(lane, 'ready')} ready workflow(s)`,
107
112
  `${workflowCount(lane, 'attention')} attention workflow(s)`,
108
- `${freshProviderReads} fresh provider read route(s)`,
113
+ `${freshProviderReads} fresh provider read/record route(s)`,
109
114
  `${refreshableSavedRecords} refreshable saved review record(s)`,
110
115
  `${savedReviewRecords} saved review record(s)`,
111
116
  ];
@@ -215,7 +220,7 @@ export async function personalOpsBriefingSummary(context: CommandContext, args:
215
220
  workflows: workflows.length,
216
221
  readyWorkflows: workflows.filter((workflow) => workflow.status === 'ready').length,
217
222
  attentionWorkflows: workflows.filter((workflow) => workflow.status === 'attention').length,
218
- freshProviderReads: liveRecords.filter((record) => record.freshness?.status === 'fresh-provider-route-ready').length,
223
+ freshProviderReads: liveRecords.filter(isFreshProviderRecord).length,
219
224
  refreshableSavedRecords: liveRecords.filter((record) => record.freshness?.status === 'saved-review-refreshable').length,
220
225
  savedReviewRecords: liveRecords.filter((record) => record.freshness?.source === 'saved-review-artifact' || typeof record.reviewRecordCount === 'number').length,
221
226
  connectorSignals: lanes.reduce((sum, lane) => sum + (lane.connectorSignals?.length ?? 0), 0),
@@ -258,7 +263,7 @@ export async function personalOpsSummary(context: CommandContext, args: AgentHar
258
263
  attention: workflows.filter((workflow) => workflow.status === 'attention').length,
259
264
  needsSetup: workflows.filter((workflow) => workflow.status === 'needs-setup').length,
260
265
  },
261
- policy: 'Personal Ops unifies inbox, agenda, notes, tasks, reminders, routines, and delivery. Lanes include live records when Agent owns them and schema-derived connector operation records when MCP schemas are available. Missing email/calendar connectors, messages, and events are reported as setup/data gaps, not faked.',
266
+ policy: 'Personal Ops unifies inbox, agenda, notes, tasks, reminders, routines, and delivery. Lanes include live records when Agent owns them, daemon/SDK provider read-model queue records when published, and schema-derived connector operation records when MCP schemas are available. Missing email/calendar connectors, messages, and events are reported as setup/data gaps, not faked.',
262
267
  nextActions: nextActions(lanes),
263
268
  };
264
269
  }
@@ -268,6 +273,7 @@ function queueRecordType(record: PersonalOpsLiveRecord): string {
268
273
  if (record.capability === 'calendar-event-review') return 'saved-calendar-event';
269
274
  if (record.capability === 'inbox-review-artifact') return 'saved-inbox-review';
270
275
  if (record.capability === 'calendar-review-artifact') return 'saved-calendar-review';
276
+ if (record.freshness?.status === 'fresh-provider-record-current') return 'fresh-provider-record';
271
277
  if (record.freshness?.status === 'fresh-provider-route-ready') return 'fresh-provider-read';
272
278
  if (record.freshness?.status === 'connector-attention') return 'provider-read-attention';
273
279
  return record.capability ?? 'personal-ops-record';
@@ -276,7 +282,7 @@ function queueRecordType(record: PersonalOpsLiveRecord): string {
276
282
  function queueStatusRank(record: PersonalOpsLiveRecord): number {
277
283
  if (record.freshness?.status === 'saved-review-refreshable') return 100;
278
284
  if (record.capability === 'inbox-thread-review' || record.capability === 'calendar-event-review') return 90;
279
- if (record.freshness?.status === 'fresh-provider-route-ready') return 80;
285
+ if (record.freshness?.status === 'fresh-provider-route-ready' || record.freshness?.status === 'fresh-provider-record-current') return 80;
280
286
  if (record.capability === 'inbox-review-artifact' || record.capability === 'calendar-review-artifact') return 70;
281
287
  if (record.freshness?.status === 'connector-attention') return 50;
282
288
  if (record.freshness?.status === 'provider-contract-missing') return 35;
@@ -286,7 +292,7 @@ function queueStatusRank(record: PersonalOpsLiveRecord): number {
286
292
 
287
293
  function isPersonalOpsQueueRecord(record: PersonalOpsLiveRecord): boolean {
288
294
  if (record.freshness?.source === 'saved-review-artifact') return true;
289
- if (record.freshness?.status === 'fresh-provider-route-ready' || record.freshness?.status === 'connector-attention') return true;
295
+ if (isFreshProviderRecord(record) || record.freshness?.status === 'connector-attention') return true;
290
296
  return record.capability ? QUEUE_CAPABILITIES.has(record.capability) : false;
291
297
  }
292
298
 
@@ -323,6 +329,7 @@ function describeQueueItem(lane: PersonalOpsLane, record: PersonalOpsLiveRecord,
323
329
  ...(record.artifactId ? { artifact: `agent_artifacts show artifactId:"${record.artifactId}" includeContent:true` } : {}),
324
330
  },
325
331
  followUpRoutes,
332
+ ...(includeParameters && record.certification ? { certification: record.certification } : {}),
326
333
  ...(includeParameters && record.tags && record.tags.length > 0 ? { tags: record.tags } : {}),
327
334
  ...(includeParameters && record.requiredFields ? { requiredFields: record.requiredFields } : {}),
328
335
  ...(includeParameters && record.sampleInput ? { sampleInput: record.sampleInput } : {}),
@@ -359,7 +366,7 @@ export async function personalOpsQueueSummary(context: CommandContext, args: Age
359
366
  const items = allItems.slice(0, limit).map((item) => describeQueueItem(item.lane, item.record, includeParameters));
360
367
  const readRecords = allItems.filter((item) => item.record.effect === 'read-only');
361
368
  const confirmedFollowUps = allItems.reduce((total, item) => total + (item.record.followUpRoutes ?? []).filter((route) => route.requiresConfirmation).length, 0);
362
- const freshProviderReads = allItems.filter((item) => item.record.freshness?.status === 'fresh-provider-route-ready').length;
369
+ const freshProviderReads = allItems.filter((item) => isFreshProviderRecord(item.record)).length;
363
370
  const refreshableSavedRecords = allItems.filter((item) => item.record.freshness?.status === 'saved-review-refreshable').length;
364
371
  const savedReviewRecords = allItems.filter((item) => item.record.freshness?.source === 'saved-review-artifact' || typeof item.record.reviewRecordCount === 'number').length;
365
372
  const attentionRecords = allItems.filter((item) => item.record.freshness?.status === 'connector-attention' || item.lane.status === 'gap' || item.lane.status === 'needs-setup').length;
@@ -389,14 +396,14 @@ export async function personalOpsQueueSummary(context: CommandContext, args: Age
389
396
  nextActions: allItems.length > 0
390
397
  ? [
391
398
  refreshableSavedRecords > 0 ? 'Refresh one saved queue item only through its returned confirmed read route when the user asks for current provider state.' : '',
392
- freshProviderReads > 0 ? 'Run one fresh provider read at a time, summarize it, and save review cards when the user wants a durable queue.' : '',
399
+ freshProviderReads > 0 ? 'Inspect one current provider-backed queue record at a time; run a refresh route only when the record publishes one and the user asks.' : '',
393
400
  'Use saved redacted queue artifacts for recap or local drafts before any external send, label, archive, edit, RSVP, or delete.',
394
401
  ].filter(Boolean).slice(0, includeParameters ? 5 : 3)
395
402
  : [
396
403
  'Run personal_ops action:"intake" for the user request to find a safe connector route.',
397
404
  'Set up an inbox or calendar connector before promising fresh queue state.',
398
405
  ],
399
- policy: 'Personal Ops queue is read-only. It aggregates existing saved review artifacts and connector read-route records; it does not execute MCP tools, read live provider data, send messages, edit calendar events, create reminders, or mutate artifacts.',
406
+ policy: 'Personal Ops queue is read-only. It aggregates existing saved review artifacts, connector read-route records, and daemon/SDK provider read-model records; it does not execute MCP tools, read live provider data beyond already-published read models, send messages, edit calendar events, create reminders, or mutate artifacts.',
400
407
  };
401
408
  }
402
409