@pellux/goodvibes-agent 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/dist/package/main.js +19016 -12684
  3. package/docs/README.md +3 -3
  4. package/docs/connected-host.md +2 -2
  5. package/docs/getting-started.md +11 -9
  6. package/docs/tools-and-commands.md +12 -10
  7. package/docs/voice-and-live-tts.md +2 -2
  8. package/package.json +1 -1
  9. package/release/release-notes.md +17 -5
  10. package/release/release-readiness.json +36 -36
  11. package/src/agent/competitive-feature-inventory.ts +42 -44
  12. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  13. package/src/agent/setup-wizard.ts +245 -9
  14. package/src/input/agent-workspace-categories.ts +2 -2
  15. package/src/input/agent-workspace-onboarding-categories.ts +4 -4
  16. package/src/input/agent-workspace-settings.ts +52 -1
  17. package/src/input/agent-workspace-setup-snapshot.ts +20 -1
  18. package/src/input/agent-workspace-setup.ts +32 -5
  19. package/src/input/agent-workspace-snapshot.ts +23 -3
  20. package/src/input/agent-workspace.ts +5 -1
  21. package/src/input/setup-wizard-live-receipts.ts +76 -0
  22. package/src/renderer/agent-workspace-context-lines.ts +21 -6
  23. package/src/renderer/agent-workspace.ts +46 -10
  24. package/src/runtime/tool-permission-safety.ts +1 -1
  25. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  26. package/src/tools/agent-harness-agent-orchestration.ts +216 -128
  27. package/src/tools/agent-harness-autonomy-live-records.ts +154 -0
  28. package/src/tools/agent-harness-autonomy-queue-types.ts +1 -1
  29. package/src/tools/agent-harness-autonomy-queue.ts +19 -8
  30. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  31. package/src/tools/agent-harness-background-processes.ts +8 -4
  32. package/src/tools/agent-harness-browser-cockpit-route.ts +188 -34
  33. package/src/tools/agent-harness-browser-control.ts +12 -4
  34. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  35. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  36. package/src/tools/agent-harness-execution-posture.ts +3 -0
  37. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  38. package/src/tools/agent-harness-local-model-benchmarks.ts +71 -1
  39. package/src/tools/agent-harness-local-model-endpoints.ts +469 -354
  40. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  41. package/src/tools/agent-harness-local-model-url.ts +78 -0
  42. package/src/tools/agent-harness-media-posture.ts +27 -12
  43. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  44. package/src/tools/agent-harness-memory-posture.ts +253 -137
  45. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  46. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  47. package/src/tools/agent-harness-mode-catalog.ts +4 -2
  48. package/src/tools/agent-harness-model-provider-health.ts +139 -42
  49. package/src/tools/agent-harness-model-readiness.ts +31 -5
  50. package/src/tools/agent-harness-model-routing-types.ts +61 -0
  51. package/src/tools/agent-harness-model-routing.ts +31 -6
  52. package/src/tools/agent-harness-pairing-posture.ts +30 -9
  53. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  54. package/src/tools/agent-harness-personal-ops-lanes.ts +81 -15
  55. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  56. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  57. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  58. package/src/tools/agent-harness-personal-ops-records.ts +176 -224
  59. package/src/tools/agent-harness-personal-ops-types.ts +19 -1
  60. package/src/tools/agent-harness-personal-ops.ts +18 -11
  61. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  62. package/src/tools/agent-harness-research-briefing.ts +26 -7
  63. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  64. package/src/tools/agent-harness-research-runs.ts +15 -3
  65. package/src/tools/agent-harness-research-workflow.ts +92 -19
  66. package/src/tools/agent-harness-setup-model-helpers.ts +1 -0
  67. package/src/tools/agent-harness-setup-smoke.ts +26 -1
  68. package/src/tools/agent-harness-tool-schema.ts +23 -1
  69. package/src/tools/agent-harness-tool-types.ts +5 -0
  70. package/src/tools/agent-harness-tool.ts +9 -1
  71. package/src/tools/agent-harness-workspace-actions.ts +1 -1
  72. package/src/tools/agent-memory-tool.ts +40 -1
  73. package/src/tools/agent-model-compare-run.ts +13 -0
  74. package/src/tools/agent-research-runner.ts +367 -0
  75. package/src/tools/agent-research-tool.ts +7 -179
  76. package/src/tools/agent-route-planner-candidates-surfaces.ts +1 -1
  77. package/src/version.ts +1 -1
@@ -1,6 +1,9 @@
1
1
  import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
2
+ import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifacts';
2
3
  import type { CommandContext } from '../input/command-registry.ts';
3
4
  import type { WorkPlanItem } from '../work-plans/work-plan-store.ts';
5
+ import { AGENT_TEMPLATES, AGENT_TOOL_MODES, agentOrchestrationDecisionCards } from './agent-harness-agent-orchestration-policy.ts';
6
+ import { remoteReadModelSnapshot, type RemoteCaptureOutcomeRecord, type RemoteWorkspaceEvidenceRecord } from './agent-harness-remote-read-models.ts';
4
7
  import { previewHarnessText } from './agent-harness-text.ts';
5
8
 
6
9
  interface AgentHarnessAgentOrchestrationArgs {
@@ -19,6 +22,9 @@ interface RemoteRuntimeSnapshot {
19
22
  readonly pools: readonly Record<string, unknown>[];
20
23
  readonly contracts: readonly Record<string, unknown>[];
21
24
  readonly artifacts: readonly Record<string, unknown>[];
25
+ readonly outcomes: readonly RemoteCaptureOutcomeRecord[];
26
+ readonly workspaces: readonly RemoteWorkspaceEvidenceRecord[];
27
+ readonly sourceCounts: Readonly<Record<string, number>>;
22
28
  }
23
29
 
24
30
  export type AgentOrchestrationResolution =
@@ -26,38 +32,49 @@ export type AgentOrchestrationResolution =
26
32
  | { readonly status: 'ambiguous'; readonly input: string; readonly candidates: readonly Record<string, unknown>[] }
27
33
  | { readonly status: 'missing_lookup'; readonly usage: string };
28
34
 
29
- const AGENT_TOOL_MODES = [
30
- 'spawn',
31
- 'batch-spawn',
32
- 'list',
33
- 'templates',
34
- 'status',
35
- 'get',
36
- 'budget',
37
- 'plan',
38
- 'wait',
39
- 'message',
40
- 'cancel',
41
- 'wrfc-chains',
42
- 'wrfc-history',
43
- 'cohort-status',
44
- 'cohort-report',
45
- ] as const;
46
-
47
- const AGENT_TEMPLATES = [
48
- 'orchestrator',
49
- 'engineer',
50
- 'reviewer',
51
- 'tester',
52
- 'researcher',
53
- 'integrator',
54
- 'general',
55
- ] as const;
35
+ const REMOTE_ARTIFACT_RECEIPT_PURPOSES = new Set([
36
+ 'remote-runner-artifact-receipt',
37
+ 'remote-runner-closeout-receipt',
38
+ 'remote-runner-export-receipt',
39
+ 'agent-remote-runner-artifact-receipt',
40
+ 'agent-remote-runner-closeout-receipt',
41
+ 'connected-host-remote-runner-artifact-receipt',
42
+ 'connected-host-remote-runner-closeout-receipt',
43
+ ]);
56
44
 
57
45
  function readString(value: unknown): string {
58
46
  return typeof value === 'string' ? value.trim() : '';
59
47
  }
60
48
 
49
+ function readObject(value: unknown): Record<string, unknown> {
50
+ return value && typeof value === 'object' ? value as Record<string, unknown> : {};
51
+ }
52
+
53
+ function artifactMetadata(record: Record<string, unknown>): Record<string, unknown> {
54
+ return readObject(record.metadata);
55
+ }
56
+
57
+ function metadataString(record: Record<string, unknown>, key: string): string {
58
+ return readString(artifactMetadata(record)[key]);
59
+ }
60
+
61
+ function recordOrMetadataString(record: Record<string, unknown>, key: string): string {
62
+ return readString(record[key]) || metadataString(record, key);
63
+ }
64
+
65
+ function recordOrMetadataNumber(record: Record<string, unknown>, key: string): number {
66
+ const value = record[key] ?? artifactMetadata(record)[key];
67
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
68
+ }
69
+
70
+ function artifactPurpose(record: Record<string, unknown>): string {
71
+ return metadataString(record, 'purpose');
72
+ }
73
+
74
+ function isRemoteArtifactReceipt(record: Record<string, unknown>): boolean {
75
+ return REMOTE_ARTIFACT_RECEIPT_PURPOSES.has(artifactPurpose(record));
76
+ }
77
+
61
78
  function readLimit(value: unknown, fallback: number): number {
62
79
  const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
63
80
  if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
@@ -99,7 +116,7 @@ function remotePools(context: CommandContext): readonly Record<string, unknown>[
99
116
  }
100
117
  }
101
118
 
102
- function remoteArtifacts(context: CommandContext): readonly Record<string, unknown>[] {
119
+ function remoteRuntimeArtifacts(context: CommandContext): readonly Record<string, unknown>[] {
103
120
  try {
104
121
  return context.ops.remoteRuntime?.listArtifacts().map((record) => record as unknown as Record<string, unknown>) ?? [];
105
122
  } catch {
@@ -107,11 +124,44 @@ function remoteArtifacts(context: CommandContext): readonly Record<string, unkno
107
124
  }
108
125
  }
109
126
 
127
+ function artifactCreatedAt(artifact: ArtifactDescriptor): number {
128
+ return typeof artifact.createdAt === 'number' && Number.isFinite(artifact.createdAt) ? artifact.createdAt : 0;
129
+ }
130
+
131
+ function remoteReceiptArtifacts(context: CommandContext): readonly Record<string, unknown>[] {
132
+ const store = context.platform.artifactStore;
133
+ if (!store?.list) return [];
134
+ try {
135
+ return store.list(100)
136
+ .filter((artifact) => isRemoteArtifactReceipt(artifact as unknown as Record<string, unknown>))
137
+ .filter((artifact) => remoteRunnerId(artifact as unknown as Record<string, unknown>))
138
+ .sort((left, right) => artifactCreatedAt(right) - artifactCreatedAt(left))
139
+ .slice(0, 20)
140
+ .map((artifact) => artifact as unknown as Record<string, unknown>);
141
+ } catch {
142
+ return [];
143
+ }
144
+ }
145
+
146
+ function remoteArtifacts(context: CommandContext): readonly Record<string, unknown>[] {
147
+ const runtimeArtifacts = remoteRuntimeArtifacts(context);
148
+ const seen = new Set(runtimeArtifacts.map((artifact) => readString(artifact.id)).filter(Boolean));
149
+ const receiptArtifacts = remoteReceiptArtifacts(context).filter((artifact) => {
150
+ const id = readString(artifact.id);
151
+ return !id || !seen.has(id);
152
+ });
153
+ return [...runtimeArtifacts, ...receiptArtifacts];
154
+ }
155
+
110
156
  function remoteRuntimeSnapshot(context: CommandContext): RemoteRuntimeSnapshot {
157
+ const readModels = remoteReadModelSnapshot(context);
111
158
  return {
112
159
  pools: remotePools(context),
113
160
  contracts: remoteContracts(context),
114
161
  artifacts: remoteArtifacts(context),
162
+ outcomes: readModels.outcomes,
163
+ workspaces: readModels.workspaces,
164
+ sourceCounts: readModels.sourceCounts,
115
165
  };
116
166
  }
117
167
 
@@ -166,7 +216,10 @@ function routeForAgent(mode: string, id: string): string {
166
216
  }
167
217
 
168
218
  function remoteRunnerId(record: Record<string, unknown>): string {
169
- return readString(record.runnerId);
219
+ return recordOrMetadataString(record, 'runnerId')
220
+ || recordOrMetadataString(record, 'agentId')
221
+ || recordOrMetadataString(record, 'linkedAgentId')
222
+ || recordOrMetadataString(record, 'runner');
170
223
  }
171
224
 
172
225
  function transportState(contract: Record<string, unknown>): string {
@@ -207,22 +260,51 @@ function contractSummary(contract: Record<string, unknown>, includeParameters: b
207
260
  }
208
261
 
209
262
  function artifactSummary(artifact: Record<string, unknown>, includeParameters: boolean): Record<string, unknown> {
210
- const task = artifact.task && typeof artifact.task === 'object' ? artifact.task as Record<string, unknown> : {};
211
- const evidence = artifact.evidence && typeof artifact.evidence === 'object' ? artifact.evidence as Record<string, unknown> : {};
263
+ const metadata = artifactMetadata(artifact);
264
+ const task = readObject(artifact.task);
265
+ const evidence = readObject(artifact.evidence);
266
+ const receipt = isRemoteArtifactReceipt(artifact);
267
+ const id = readString(artifact.id);
268
+ const status = readString(task.status)
269
+ || metadataString(artifact, 'status')
270
+ || metadataString(artifact, 'outcome')
271
+ || 'unknown';
272
+ const taskText = readString(task.task)
273
+ || metadataString(artifact, 'task')
274
+ || metadataString(artifact, 'title');
275
+ const summary = readString(task.summary)
276
+ || metadataString(artifact, 'summary')
277
+ || metadataString(artifact, 'description');
278
+ const sourceArtifactId = metadataString(artifact, 'sourceArtifactId')
279
+ || metadataString(artifact, 'remoteArtifactId')
280
+ || metadataString(artifact, 'exportArtifactId');
212
281
  return {
213
- id: readString(artifact.id),
282
+ id,
214
283
  runnerId: remoteRunnerId(artifact),
215
- status: readString(task.status) || 'unknown',
216
- task: previewHarnessText(readString(task.task), includeParameters ? 160 : 72),
217
- summary: previewHarnessText(readString(task.summary), includeParameters ? 220 : 96),
218
- toolCallCount: typeof evidence.toolCallCount === 'number' ? evidence.toolCallCount : 0,
219
- messageCount: typeof evidence.messageCount === 'number' ? evidence.messageCount : 0,
220
- errorCount: typeof evidence.errorCount === 'number' ? evidence.errorCount : 0,
221
- hasKnowledgeInjections: Boolean(evidence.hasKnowledgeInjections),
222
- modelRoute: `remote { mode: "review", artifactId: "${readString(artifact.id)}" }`,
284
+ status,
285
+ task: previewHarnessText(taskText, includeParameters ? 160 : 72),
286
+ summary: previewHarnessText(summary, includeParameters ? 220 : 96),
287
+ toolCallCount: typeof evidence.toolCallCount === 'number' ? evidence.toolCallCount : recordOrMetadataNumber(artifact, 'toolCallCount'),
288
+ messageCount: typeof evidence.messageCount === 'number' ? evidence.messageCount : recordOrMetadataNumber(artifact, 'messageCount'),
289
+ errorCount: typeof evidence.errorCount === 'number' ? evidence.errorCount : recordOrMetadataNumber(artifact, 'errorCount'),
290
+ hasKnowledgeInjections: Boolean(evidence.hasKnowledgeInjections ?? metadata.hasKnowledgeInjections),
291
+ modelRoute: artifactReviewRoute(artifact),
292
+ ...(receipt ? {
293
+ receipt: true,
294
+ purpose: artifactPurpose(artifact),
295
+ sourceArtifactId: sourceArtifactId || null,
296
+ redaction: metadataString(artifact, 'redaction') || metadataString(artifact, 'redactionPolicy') || 'metadata-only',
297
+ } : {}),
223
298
  };
224
299
  }
225
300
 
301
+ function artifactReviewRoute(artifact: Record<string, unknown>): string {
302
+ const id = readString(artifact.id);
303
+ return isRemoteArtifactReceipt(artifact)
304
+ ? `agent_artifacts show artifactId:"${id}" includeContent:false`
305
+ : `remote { mode: "review", artifactId: "${id}" }`;
306
+ }
307
+
226
308
  function agentRoutes(id: string): Record<string, string> {
227
309
  return {
228
310
  inspect: routeForAgent('get', id),
@@ -256,6 +338,26 @@ function artifactsByRunnerId(artifacts: readonly Record<string, unknown>[]): Map
256
338
  return grouped;
257
339
  }
258
340
 
341
+ function outcomesByRunnerId(outcomes: readonly RemoteCaptureOutcomeRecord[]): Map<string, readonly RemoteCaptureOutcomeRecord[]> {
342
+ const grouped = new Map<string, RemoteCaptureOutcomeRecord[]>();
343
+ for (const outcome of outcomes) {
344
+ const entries = grouped.get(outcome.runnerId) ?? [];
345
+ entries.push(outcome);
346
+ grouped.set(outcome.runnerId, entries);
347
+ }
348
+ return grouped;
349
+ }
350
+
351
+ function workspacesByRunnerId(workspaces: readonly RemoteWorkspaceEvidenceRecord[]): Map<string, readonly RemoteWorkspaceEvidenceRecord[]> {
352
+ const grouped = new Map<string, RemoteWorkspaceEvidenceRecord[]>();
353
+ for (const workspace of workspaces) {
354
+ const entries = grouped.get(workspace.runnerId) ?? [];
355
+ entries.push(workspace);
356
+ grouped.set(workspace.runnerId, entries);
357
+ }
358
+ return grouped;
359
+ }
360
+
259
361
  function workPlanItems(context: CommandContext): readonly WorkPlanItem[] {
260
362
  try {
261
363
  return context.workspace.workPlanStore?.listItems() ?? [];
@@ -301,39 +403,45 @@ function workPlanCloseoutItem(item: WorkPlanItem, includeParameters: boolean): R
301
403
  };
302
404
  }
303
405
 
304
- function closeoutReviewRoutes(id: string, items: readonly WorkPlanItem[], artifacts: readonly Record<string, unknown>[]): readonly string[] {
406
+ function closeoutReviewRoutes(id: string, items: readonly WorkPlanItem[], artifacts: readonly Record<string, unknown>[], outcomes: readonly RemoteCaptureOutcomeRecord[] = [], workspaces: readonly RemoteWorkspaceEvidenceRecord[] = []): readonly string[] {
305
407
  return [
306
408
  routeForAgent('get', id),
307
409
  ...items.map((item) => `agent_work_plan action:"get" id:"${item.id}"`),
308
- ...artifacts.map((artifact) => `remote { mode: "review", artifactId: "${readString(artifact.id)}" }`),
410
+ ...artifacts.map((artifact) => artifactReviewRoute(artifact)),
411
+ ...outcomes.map((outcome) => outcome.modelRoute),
412
+ ...workspaces.map((workspace) => workspace.modelRoute),
309
413
  ];
310
414
  }
311
415
 
312
- function closeoutStatus(status: string, items: readonly WorkPlanItem[], artifacts: readonly Record<string, unknown>[]): string {
416
+ function closeoutStatus(status: string, items: readonly WorkPlanItem[], artifacts: readonly Record<string, unknown>[], outcomes: readonly RemoteCaptureOutcomeRecord[] = []): string {
313
417
  if (['pending', 'running'].includes(status)) return 'pending-work';
314
418
  if (status === 'failed') return 'attention';
315
419
  if (items.some((item) => item.status === 'blocked' || item.status === 'failed')) return 'attention';
316
- if (artifacts.length > 0) return 'evidence-ready';
420
+ if (artifacts.length > 0 || outcomes.length > 0) return 'evidence-ready';
317
421
  if (items.length > 0) return 'needs-artifact-evidence';
318
422
  return 'unlinked';
319
423
  }
320
424
 
321
- function closeoutCard(
322
- id: string,
323
- status: string,
324
- items: readonly WorkPlanItem[],
325
- artifacts: readonly Record<string, unknown>[],
326
- includeParameters: boolean,
327
- ): Record<string, unknown> {
425
+ function closeoutCard(id: string, status: string, items: readonly WorkPlanItem[], artifacts: readonly Record<string, unknown>[], outcomes: readonly RemoteCaptureOutcomeRecord[], workspaces: readonly RemoteWorkspaceEvidenceRecord[], includeParameters: boolean): Record<string, unknown> {
426
+ const hasReadModelEvidence = outcomes.length > 0 || workspaces.length > 0;
328
427
  return {
329
- status: closeoutStatus(status, items, artifacts),
428
+ status: closeoutStatus(status, items, artifacts, outcomes),
330
429
  workPlanItemCount: items.length,
331
430
  dispatchReceiptCount: items.reduce((count, item) => count + dispatchReceiptLines(item).length, 0),
332
431
  remoteArtifactCount: artifacts.length,
333
- autoAttachReason: artifacts.length > 0 ? 'remote artifact runnerId matched the visible agent id' : null,
432
+ remoteReceiptCount: artifacts.filter((artifact) => isRemoteArtifactReceipt(artifact)).length,
433
+ remoteOutcomeCount: outcomes.length,
434
+ workspaceEvidenceCount: workspaces.length,
435
+ autoAttachReason: artifacts.length > 0
436
+ ? 'remote artifact or durable receipt runnerId matched the visible agent id'
437
+ : hasReadModelEvidence
438
+ ? 'daemon/SDK remote read-model runnerId matched the visible agent id'
439
+ : null,
334
440
  workPlanItems: items.slice(0, includeParameters ? 8 : 3).map((item) => workPlanCloseoutItem(item, includeParameters)),
335
441
  autoAttachedRemoteArtifacts: artifacts.slice(0, includeParameters ? 8 : 3).map((artifact) => artifactSummary(artifact, includeParameters)),
336
- reviewRoutes: closeoutReviewRoutes(id, items, artifacts).slice(0, includeParameters ? 20 : 8),
442
+ liveRemoteOutcomes: outcomes.slice(0, includeParameters ? 8 : 3),
443
+ workspaceEvidence: workspaces.slice(0, includeParameters ? 8 : 3),
444
+ reviewRoutes: closeoutReviewRoutes(id, items, artifacts, outcomes, workspaces).slice(0, includeParameters ? 24 : 8),
337
445
  updateRoutes: items.slice(0, includeParameters ? 8 : 3).flatMap((item) => [
338
446
  `agent_work_plan action:"set_status" id:"${item.id}" status:"done"`,
339
447
  `agent_work_plan action:"set_status" id:"${item.id}" status:"blocked"`,
@@ -346,6 +454,8 @@ function managedPlanItem(
346
454
  record: AgentRecordView,
347
455
  contract: Record<string, unknown> | undefined,
348
456
  artifacts: readonly Record<string, unknown>[],
457
+ outcomes: readonly RemoteCaptureOutcomeRecord[],
458
+ workspaces: readonly RemoteWorkspaceEvidenceRecord[],
349
459
  linkedWorkItems: readonly WorkPlanItem[],
350
460
  includeParameters: boolean,
351
461
  ): Record<string, unknown> {
@@ -354,12 +464,14 @@ function managedPlanItem(
354
464
  const task = readString(record.task);
355
465
  const progress = readString(record.progress);
356
466
  const hasRemoteContract = Boolean(contract);
467
+ const hasLiveRemoteEvidence = hasRemoteContract || outcomes.length > 0 || workspaces.length > 0;
357
468
  const running = ['pending', 'running'].includes(status);
358
469
  const failed = status === 'failed';
470
+ const hasOutcomeEvidence = artifacts.length > 0 || outcomes.length > 0;
359
471
  return {
360
472
  planItemId: `agent:${id}`,
361
473
  agentId: id,
362
- lane: hasRemoteContract ? 'remote-runner' : 'visible-agent',
474
+ lane: hasLiveRemoteEvidence ? 'remote-runner' : 'visible-agent',
363
475
  milestoneId: running ? 'visible-agent-work' : 'review-and-closeout',
364
476
  status,
365
477
  title: previewHarnessText(task || id, includeParameters ? 160 : 72),
@@ -370,18 +482,26 @@ function managedPlanItem(
370
482
  workPlanLinks: linkedWorkItems.slice(0, includeParameters ? 8 : 3).map((item) => workPlanCloseoutItem(item, includeParameters)),
371
483
  remoteContract: contract ? contractSummary(contract, includeParameters) : null,
372
484
  artifactTrail: artifacts.slice(0, includeParameters ? 8 : 3).map((artifact) => artifactSummary(artifact, includeParameters)),
373
- closeout: closeoutCard(id, status, linkedWorkItems, artifacts, includeParameters),
485
+ liveOutcomeTrail: outcomes.slice(0, includeParameters ? 8 : 3),
486
+ workspaceEvidence: workspaces.slice(0, includeParameters ? 8 : 3),
487
+ closeout: closeoutCard(id, status, linkedWorkItems, artifacts, outcomes, workspaces, includeParameters),
374
488
  reviewGate: {
375
- status: artifacts.length > 0 ? 'artifact-ready' : running ? 'pending-work' : linkedWorkItems.length > 0 ? 'needs-artifact-evidence' : 'needs-link',
376
- requiredEvidence: contract ? stringArray(capabilityCeiling(contract).requiredEvidence).slice(0, includeParameters ? 12 : 5) : [],
377
- modelRoutes: closeoutReviewRoutes(id, linkedWorkItems, artifacts).slice(0, includeParameters ? 20 : 8),
489
+ status: failed ? 'attention' : running ? 'pending-work' : hasOutcomeEvidence ? 'artifact-ready' : workspaces.length > 0 ? 'workspace-evidence-ready' : linkedWorkItems.length > 0 ? 'needs-artifact-evidence' : 'needs-link',
490
+ requiredEvidence: [
491
+ ...(contract ? stringArray(capabilityCeiling(contract).requiredEvidence).slice(0, includeParameters ? 12 : 5) : []),
492
+ ...(workspaces.length > 0 ? ['workspace/worktree isolation evidence'] : []),
493
+ ...(outcomes.length > 0 ? ['live remote capture/export outcome evidence'] : []),
494
+ ],
495
+ modelRoutes: closeoutReviewRoutes(id, linkedWorkItems, artifacts, outcomes, workspaces).slice(0, includeParameters ? 24 : 8),
378
496
  },
379
497
  nextAction: failed
380
498
  ? 'Inspect the failed agent, then decide whether to message, retry through a new visible task, or cancel related work.'
381
499
  : running
382
500
  ? 'Use wait/status for progress, message for guidance, or cancel if the work no longer helps the user.'
383
- : artifacts.length > 0
501
+ : hasOutcomeEvidence
384
502
  ? 'Review the artifact trail before closing the plan.'
503
+ : workspaces.length > 0
504
+ ? 'Confirm the workspace isolation evidence, then attach outcome evidence before closing the plan.'
385
505
  : 'Capture or attach outcome evidence before treating this work as complete.',
386
506
  };
387
507
  }
@@ -402,19 +522,29 @@ function managedExecutionPlan(
402
522
  ): Record<string, unknown> {
403
523
  const agentToolAvailable = agentToolRegistered(toolRegistry);
404
524
  const remoteToolAvailable = remoteToolRegistered(toolRegistry);
405
- const { pools, contracts, artifacts } = remoteSnapshot;
525
+ const { pools, contracts, artifacts, outcomes, workspaces, sourceCounts } = remoteSnapshot;
406
526
  const contractsByRunner = contractByRunnerId(contracts);
407
527
  const artifactsByRunner = artifactsByRunnerId(artifacts);
528
+ const outcomesByRunner = outcomesByRunnerId(outcomes);
529
+ const workspacesByRunner = workspacesByRunnerId(workspaces);
408
530
  const workPlanByAgent = workPlanItemsByAgentId(workPlanItems(context));
409
531
  const items = records.map((record) => {
410
532
  const id = agentId(record);
411
- return managedPlanItem(record, contractsByRunner.get(id), artifactsByRunner.get(id) ?? [], workPlanByAgent.get(id) ?? [], includeParameters);
533
+ return managedPlanItem(
534
+ record,
535
+ contractsByRunner.get(id),
536
+ artifactsByRunner.get(id) ?? [],
537
+ outcomesByRunner.get(id) ?? [],
538
+ workspacesByRunner.get(id) ?? [],
539
+ workPlanByAgent.get(id) ?? [],
540
+ includeParameters,
541
+ );
412
542
  });
413
543
  const status = managedPlanStatus(records, agentToolAvailable);
414
544
  const running = records.filter((record) => ['pending', 'running'].includes(agentStatus(record))).length;
415
545
  const failed = records.filter((record) => agentStatus(record) === 'failed').length;
416
546
  const completed = records.filter((record) => agentStatus(record) === 'completed').length;
417
- const remoteStatus = contracts.length > 0 || artifacts.length > 0 ? 'ready' : context.ops.remoteRuntime ? 'ready' : 'needs-setup';
547
+ const remoteStatus = contracts.length > 0 || artifacts.length > 0 || outcomes.length > 0 || workspaces.length > 0 ? 'ready' : context.ops.remoteRuntime ? 'ready' : 'needs-setup';
418
548
  const linkedWorkPlanItemCount = Array.from(workPlanByAgent.values()).reduce((count, entries) => count + entries.length, 0);
419
549
  const dispatchReceiptCount = Array.from(workPlanByAgent.values()).reduce(
420
550
  (count, entries) => count + entries.reduce((entryCount, item) => entryCount + dispatchReceiptLines(item).length, 0),
@@ -423,7 +553,7 @@ function managedExecutionPlan(
423
553
  return {
424
554
  planId: 'visible-managed-execution',
425
555
  status,
426
- summary: `${records.length} visible agent${records.length === 1 ? '' : 's'}, ${running} active, ${failed} attention, ${artifacts.length} remote artifact${artifacts.length === 1 ? '' : 's'}, ${dispatchReceiptCount} dispatch receipt${dispatchReceiptCount === 1 ? '' : 's'}.`,
556
+ summary: `${records.length} visible agent${records.length === 1 ? '' : 's'}, ${running} active, ${failed} attention, ${artifacts.length} remote artifact${artifacts.length === 1 ? '' : 's'}, ${outcomes.length} live remote outcome${outcomes.length === 1 ? '' : 's'}, ${workspaces.length} workspace evidence record${workspaces.length === 1 ? '' : 's'}, ${dispatchReceiptCount} dispatch receipt${dispatchReceiptCount === 1 ? '' : 's'}.`,
427
557
  milestones: [
428
558
  {
429
559
  id: 'intake-and-lane-selection',
@@ -449,6 +579,8 @@ function managedExecutionPlan(
449
579
  pools: pools.length,
450
580
  contracts: contracts.length,
451
581
  artifacts: artifacts.length,
582
+ readModelOutcomes: outcomes.length,
583
+ workspaceEvidence: workspaces.length,
452
584
  routes: {
453
585
  pools: remoteToolAvailable ? 'remote { mode: "pools", view: "summary" }' : 'delegation action:"route" delegationRouteId:"remote-runner-inspection"',
454
586
  contracts: remoteToolAvailable ? 'remote { mode: "contracts", view: "summary" }' : 'delegation action:"route" delegationRouteId:"remote-runner-inspection"',
@@ -462,7 +594,9 @@ function managedExecutionPlan(
462
594
  linkedWorkPlanItems: linkedWorkPlanItemCount,
463
595
  dispatchReceipts: dispatchReceiptCount,
464
596
  autoAttachedRemoteArtifacts: artifacts.length,
465
- requiredEvidence: ['changed files or artifact', 'test or verification output', 'agent status', 'recovery route when writes happened'],
597
+ liveRemoteOutcomes: outcomes.length,
598
+ workspaceEvidence: workspaces.length,
599
+ requiredEvidence: ['changed files or artifact', 'test or verification output', 'agent status', 'workspace isolation evidence when remote runners are used', 'recovery route when writes happened'],
466
600
  routes: ['agent_harness mode:"agent_orchestration"', 'execution action:"history"', 'execution action:"recovery"', 'delegation action:"status"', 'agent_work_plan action:"list"'],
467
601
  },
468
602
  ],
@@ -476,7 +610,10 @@ function managedExecutionPlan(
476
610
  })),
477
611
  contracts: contracts.slice(0, includeParameters ? 8 : 3).map((contract) => contractSummary(contract, includeParameters)),
478
612
  artifacts: artifacts.slice(0, includeParameters ? 8 : 3).map((artifact) => artifactSummary(artifact, includeParameters)),
479
- policy: 'Remote runner evidence is read-only here; creating pools, assigning runners, importing artifacts, or spawning agents stays on confirmed first-class routes.',
613
+ liveOutcomes: outcomes.slice(0, includeParameters ? 8 : 3),
614
+ workspaceEvidence: workspaces.slice(0, includeParameters ? 8 : 3),
615
+ sourceCounts,
616
+ policy: 'Remote runner evidence is read-only here; creating pools, assigning runners, importing artifacts, accepting workspace isolation, or spawning agents stays on confirmed first-class routes.',
480
617
  },
481
618
  modelAccess: {
482
619
  spawn: 'agent { mode: "spawn" }',
@@ -484,6 +621,7 @@ function managedExecutionPlan(
484
621
  listAgents: 'agent { mode: "list" }',
485
622
  remoteContracts: remoteToolAvailable ? 'remote { mode: "contracts", view: "summary" }' : 'delegation action:"route" delegationRouteId:"remote-runner-inspection"',
486
623
  remoteArtifacts: remoteToolAvailable ? 'remote { mode: "artifacts", view: "summary" }' : 'delegation action:"route" delegationRouteId:"remote-runner-inspection"',
624
+ remoteReadModelOutcomes: 'agent_harness mode:"agent_orchestration" includeParameters:true',
487
625
  },
488
626
  policy: 'Managed execution plans are read-only summaries. Parallel work must remain visible, cancellable, attached to evidence, and justified by user outcome.',
489
627
  };
@@ -494,6 +632,8 @@ function describeAgent(
494
632
  includeParameters: boolean,
495
633
  contract?: Record<string, unknown>,
496
634
  artifacts: readonly Record<string, unknown>[] = [],
635
+ outcomes: readonly RemoteCaptureOutcomeRecord[] = [],
636
+ workspaces: readonly RemoteWorkspaceEvidenceRecord[] = [],
497
637
  linkedWorkItems: readonly WorkPlanItem[] = [],
498
638
  ): Record<string, unknown> {
499
639
  const id = agentId(record);
@@ -510,7 +650,7 @@ function describeAgent(
510
650
  modelRoute: 'agent { mode: "get" }',
511
651
  userRoute: 'Agent Workspace -> Work -> Autonomy queue',
512
652
  routes: agentRoutes(id),
513
- managedPlanCard: managedPlanItem(record, contract, artifacts, linkedWorkItems, includeParameters),
653
+ managedPlanCard: managedPlanItem(record, contract, artifacts, outcomes, workspaces, linkedWorkItems, includeParameters),
514
654
  ...(readString(record.model) ? { model: readString(record.model) } : {}),
515
655
  ...(readString(record.provider) ? { provider: readString(record.provider) } : {}),
516
656
  ...(includeParameters ? {
@@ -523,62 +663,6 @@ function describeAgent(
523
663
  };
524
664
  }
525
665
 
526
- function decisionCards(agentToolAvailable: boolean): readonly Record<string, unknown>[] {
527
- return [
528
- {
529
- id: 'serial-by-default',
530
- label: 'Stay serial by default',
531
- status: 'ready',
532
- chooseWhen: ['Ordinary chat, planning, research, setup, local context, and short current-workspace tool work.'],
533
- route: 'main conversation',
534
- reason: 'Lowest-friction route for the user when parallelism does not improve outcome.',
535
- },
536
- {
537
- id: 'visible-single-agent',
538
- label: 'Spawn one visible agent',
539
- status: agentToolAvailable ? 'ready' : 'unavailable',
540
- chooseWhen: ['A bounded autonomous task can run independently with visible status and cancellation.'],
541
- requiredFields: ['task', 'successCriteria or requiredEvidence when outcome quality matters'],
542
- modelRoute: 'agent { mode: "spawn" }',
543
- inspectRoute: 'agent_harness mode:"agent_orchestration"',
544
- },
545
- {
546
- id: 'visible-batch-spawn',
547
- label: 'Batch-spawn independent agents',
548
- status: agentToolAvailable ? 'ready' : 'unavailable',
549
- chooseWhen: ['Tasks are genuinely independent and parallel work materially improves time-to-result.'],
550
- doNotUseWhen: ['Review/test/verification role fanout for one deliverable; that collapses to one owner chain.'],
551
- requiredFields: ['tasks[]', 'authoritativeTask for the original user ask when applicable'],
552
- modelRoute: 'agent { mode: "batch-spawn" }',
553
- inspectRoute: 'agent_harness mode:"agent_orchestration"',
554
- },
555
- {
556
- id: 'managed-multi-runner-plan',
557
- label: 'Use a managed multi-runner plan',
558
- status: agentToolAvailable ? 'ready' : 'unavailable',
559
- chooseWhen: ['A large task already has approval for parallel work and needs milestones, evidence, and cancellation routes.'],
560
- requiredFields: ['original user ask', 'lane reason', 'success criteria', 'per-runner evidence', 'cancel/recovery route'],
561
- modelRoute: 'agent_work_plan action:"dispatch_agents" ids:["..."] confirm:true explicitUserRequest:"..."',
562
- inspectRoute: 'agent_harness mode:"agent_orchestration"',
563
- policy: 'Read-only plan surface first; approved work-plan dispatch, spawn, message, wait, cancel, or remote mutation stays on confirmed first-class routes.',
564
- },
565
- {
566
- id: 'inspect-or-control-visible-agent',
567
- label: 'Inspect or control a visible agent',
568
- status: agentToolAvailable ? 'ready' : 'unavailable',
569
- chooseWhen: ['The user asks for progress, budget, plan, message, wait, cancel, WRFC chain, or cohort status.'],
570
- modelRoutes: ['agent { mode: "list" }', 'agent { mode: "get" }', 'agent { mode: "message" }', 'agent { mode: "wait" }', 'agent { mode: "cancel" }'],
571
- },
572
- {
573
- id: 'hidden-fanout-blocked',
574
- label: 'Block hidden fanout',
575
- status: 'blocked',
576
- chooseWhen: ['A request implies invisible background agents, unmanaged parallel coding workers, or orphaned jobs.'],
577
- saferRoutes: ['visible work plan', 'research run', 'confirmed schedule', 'agent { mode: "spawn" }', 'delegation action:"status"'],
578
- },
579
- ];
580
- }
581
-
582
666
  export function agentOrchestrationCatalogStatus(context: CommandContext, toolRegistry: ToolRegistry): Record<string, unknown> {
583
667
  const records = exportAgents(context);
584
668
  const counts = statusCounts(records ?? []);
@@ -617,6 +701,8 @@ export function agentOrchestrationSummary(context: CommandContext, toolRegistry:
617
701
  const remoteSnapshot = remoteRuntimeSnapshot(context);
618
702
  const contracts = contractByRunnerId(remoteSnapshot.contracts);
619
703
  const artifacts = artifactsByRunnerId(remoteSnapshot.artifacts);
704
+ const outcomes = outcomesByRunnerId(remoteSnapshot.outcomes);
705
+ const workspaces = workspacesByRunnerId(remoteSnapshot.workspaces);
620
706
  const workPlanByAgent = workPlanItemsByAgentId(workPlanItems(context));
621
707
  return {
622
708
  status: counts.running > 0 || counts.pending > 0 ? 'attention' : toolRegistered ? 'ready' : 'needs-setup',
@@ -635,11 +721,11 @@ export function agentOrchestrationSummary(context: CommandContext, toolRegistry:
635
721
  managedExecutionPlan: managedExecutionPlan(records, context, toolRegistry, remoteSnapshot, includeParameters),
636
722
  agents: filtered.map((record) => {
637
723
  const id = agentId(record);
638
- return describeAgent(record, includeParameters, contracts.get(id), artifacts.get(id) ?? [], workPlanByAgent.get(id) ?? []);
724
+ return describeAgent(record, includeParameters, contracts.get(id), artifacts.get(id) ?? [], outcomes.get(id) ?? [], workspaces.get(id) ?? [], workPlanByAgent.get(id) ?? []);
639
725
  }),
640
726
  returned: filtered.length,
641
727
  total: records.length,
642
- decisionCards: decisionCards(toolRegistered),
728
+ decisionCards: agentOrchestrationDecisionCards(toolRegistered),
643
729
  modes: [...AGENT_TOOL_MODES],
644
730
  templates: [...AGENT_TEMPLATES],
645
731
  modelAccess: {
@@ -674,17 +760,19 @@ export function describeAgentOrchestrationAgent(context: CommandContext, args: A
674
760
  const remoteSnapshot = remoteRuntimeSnapshot(context);
675
761
  const contracts = contractByRunnerId(remoteSnapshot.contracts);
676
762
  const artifacts = artifactsByRunnerId(remoteSnapshot.artifacts);
763
+ const outcomes = outcomesByRunnerId(remoteSnapshot.outcomes);
764
+ const workspaces = workspacesByRunnerId(remoteSnapshot.workspaces);
677
765
  const workPlanByAgent = workPlanItemsByAgentId(workPlanItems(context));
678
766
  if (exact) {
679
767
  const id = agentId(exact);
680
- return { status: 'found', agent: describeAgent(exact, args.includeParameters !== false, contracts.get(id), artifacts.get(id) ?? [], workPlanByAgent.get(id) ?? []) };
768
+ return { status: 'found', agent: describeAgent(exact, args.includeParameters !== false, contracts.get(id), artifacts.get(id) ?? [], outcomes.get(id) ?? [], workspaces.get(id) ?? [], workPlanByAgent.get(id) ?? []) };
681
769
  }
682
770
  const normalized = lookup.input.toLowerCase();
683
771
  const matches = records.filter((record) => agentSearchText(record).includes(normalized));
684
772
  if (matches.length === 1) {
685
773
  const record = matches[0]!;
686
774
  const id = agentId(record);
687
- return { status: 'found', agent: describeAgent(record, args.includeParameters !== false, contracts.get(id), artifacts.get(id) ?? [], workPlanByAgent.get(id) ?? []) };
775
+ return { status: 'found', agent: describeAgent(record, args.includeParameters !== false, contracts.get(id), artifacts.get(id) ?? [], outcomes.get(id) ?? [], workspaces.get(id) ?? [], workPlanByAgent.get(id) ?? []) };
688
776
  }
689
777
  if (matches.length > 1) {
690
778
  return {
@@ -692,7 +780,7 @@ export function describeAgentOrchestrationAgent(context: CommandContext, args: A
692
780
  input: lookup.input,
693
781
  candidates: matches.slice(0, 8).map((record) => {
694
782
  const id = agentId(record);
695
- return describeAgent(record, false, contracts.get(id), artifacts.get(id) ?? [], workPlanByAgent.get(id) ?? []);
783
+ return describeAgent(record, false, contracts.get(id), artifacts.get(id) ?? [], outcomes.get(id) ?? [], workspaces.get(id) ?? [], workPlanByAgent.get(id) ?? []);
696
784
  }),
697
785
  };
698
786
  }