@h-rig/contracts 0.0.6-alpha.103 → 0.0.6-alpha.119

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -40,9 +40,22 @@ var __export = (target, all) => {
40
40
  // packages/contracts/src/index.ts
41
41
  var exports_src = {};
42
42
  __export(exports_src, {
43
+ timelineEntriesFromCustomEntries: () => timelineEntriesFromCustomEntries,
43
44
  sessionIdFromSessionFile: () => sessionIdFromSessionFile,
44
45
  reduceRunJournal: () => reduceRunJournal,
46
+ projectWorkflowEntries: () => projectWorkflowEntries,
47
+ projectCollabWorkflowMarker: () => projectCollabWorkflowMarker,
48
+ parseWorkflowTaskSelected: () => parseWorkflowTaskSelected,
49
+ parseWorkflowTargetSelected: () => parseWorkflowTargetSelected,
50
+ parseWorkflowStatusChanged: () => parseWorkflowStatusChanged,
51
+ parseWorkflowStarted: () => parseWorkflowStarted,
52
+ parseWorkflowOperatorNote: () => parseWorkflowOperatorNote,
53
+ parseWorkflowInboxResolved: () => parseWorkflowInboxResolved,
54
+ parseWorkflowInboxRequested: () => parseWorkflowInboxRequested,
45
55
  parseStopSentinel: () => parseStopSentinel,
56
+ parseResumeSentinel: () => parseResumeSentinel,
57
+ parsePauseSentinel: () => parsePauseSentinel,
58
+ parseInboxResolutionSentinel: () => parseInboxResolutionSentinel,
46
59
  normalizeRunStatusToken: () => normalizeRunStatusToken,
47
60
  isTerminalRunStatus: () => isTerminalRunStatus,
48
61
  isRunSessionCustomType: () => isRunSessionCustomType,
@@ -51,8 +64,20 @@ __export(exports_src, {
51
64
  helpCatalog: () => helpCatalog,
52
65
  foldRunSessionEntries: () => foldRunSessionEntries,
53
66
  decodeRunJournalEvent: () => decodeRunJournalEvent,
67
+ createWorkflowTaskSelected: () => createWorkflowTaskSelected,
68
+ createWorkflowTargetSelected: () => createWorkflowTargetSelected,
69
+ createWorkflowStatusChanged: () => createWorkflowStatusChanged,
70
+ createWorkflowStarted: () => createWorkflowStarted,
71
+ createWorkflowOperatorNote: () => createWorkflowOperatorNote,
72
+ createWorkflowInboxResolved: () => createWorkflowInboxResolved,
73
+ createWorkflowInboxRequested: () => createWorkflowInboxRequested,
74
+ collectResolvedInboxRequests: () => collectResolvedInboxRequests,
75
+ collectPendingInboxRequests: () => collectPendingInboxRequests,
54
76
  canTransitionRunStatus: () => canTransitionRunStatus,
55
77
  buildStopSentinel: () => buildStopSentinel,
78
+ buildResumeSentinel: () => buildResumeSentinel,
79
+ buildPauseSentinel: () => buildPauseSentinel,
80
+ buildInboxResolutionSentinel: () => buildInboxResolutionSentinel,
56
81
  assertRunStatusTransition: () => assertRunStatusTransition,
57
82
  WsWelcomePayload: () => WsWelcomePayload,
58
83
  WsResponse: () => WsResponse,
@@ -330,6 +355,13 @@ __export(exports_src, {
330
355
  RUN_STATUS_TRANSITIONS: () => RUN_STATUS_TRANSITIONS,
331
356
  RIG_WS_METHODS: () => RIG_WS_METHODS,
332
357
  RIG_WS_CHANNELS: () => RIG_WS_CHANNELS,
358
+ RIG_WORKFLOW_TASK_SELECTED: () => RIG_WORKFLOW_TASK_SELECTED,
359
+ RIG_WORKFLOW_TARGET_SELECTED: () => RIG_WORKFLOW_TARGET_SELECTED,
360
+ RIG_WORKFLOW_STATUS_CHANGED: () => RIG_WORKFLOW_STATUS_CHANGED,
361
+ RIG_WORKFLOW_STARTED: () => RIG_WORKFLOW_STARTED,
362
+ RIG_WORKFLOW_OPERATOR_NOTE: () => RIG_WORKFLOW_OPERATOR_NOTE,
363
+ RIG_WORKFLOW_INBOX_RESOLVED: () => RIG_WORKFLOW_INBOX_RESOLVED,
364
+ RIG_WORKFLOW_INBOX_REQUESTED: () => RIG_WORKFLOW_INBOX_REQUESTED,
333
365
  RIG_STOP_SENTINEL_END: () => RIG_STOP_SENTINEL_END,
334
366
  RIG_STOP_SENTINEL: () => RIG_STOP_SENTINEL,
335
367
  RIG_RUN_TIMELINE_ENTRY: () => RIG_RUN_TIMELINE_ENTRY,
@@ -344,7 +376,11 @@ __export(exports_src, {
344
376
  RIG_RUN_APPROVAL_RESOLVED: () => RIG_RUN_APPROVAL_RESOLVED,
345
377
  RIG_RUN_APPROVAL_REQUESTED: () => RIG_RUN_APPROVAL_REQUESTED,
346
378
  RIG_RUN_ADOPTED: () => RIG_RUN_ADOPTED,
379
+ RIG_RESUME_SENTINEL: () => RIG_RESUME_SENTINEL,
347
380
  RIG_PROTOCOL_VERSION: () => RIG_PROTOCOL_VERSION,
381
+ RIG_PAUSE_SENTINEL: () => RIG_PAUSE_SENTINEL,
382
+ RIG_INBOX_RESOLUTION_SENTINEL: () => RIG_INBOX_RESOLUTION_SENTINEL,
383
+ RIG_CONTROL_SENTINEL_END: () => RIG_CONTROL_SENTINEL_END,
348
384
  REASONING_EFFORT_OPTIONS_BY_PROVIDER: () => REASONING_EFFORT_OPTIONS_BY_PROVIDER,
349
385
  QueueEntry: () => QueueEntry,
350
386
  PullRequestConfig: () => PullRequestConfig,
@@ -2295,6 +2331,85 @@ function parseStopSentinel(text, expectedRunId) {
2295
2331
  const reason = body.match(/(?:^|\s)reason=(.+)$/)?.[1]?.trim() ?? null;
2296
2332
  return { reason };
2297
2333
  }
2334
+ var RIG_PAUSE_SENTINEL = "<<RIG_PAUSE";
2335
+ var RIG_RESUME_SENTINEL = "<<RIG_RESUME";
2336
+ var RIG_CONTROL_SENTINEL_END = ">>";
2337
+ function buildControlSentinel(marker, runId, requestedBy) {
2338
+ return `${marker} runId=${runId} requestedBy=${requestedBy}${RIG_CONTROL_SENTINEL_END}`;
2339
+ }
2340
+ function parseControlSentinel(marker, text, expectedRunId) {
2341
+ const start = text.indexOf(marker);
2342
+ if (start < 0)
2343
+ return null;
2344
+ const end = text.indexOf(RIG_CONTROL_SENTINEL_END, start);
2345
+ const body = text.slice(start + marker.length, end >= 0 ? end : undefined).trim();
2346
+ const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
2347
+ if (runId && runId !== expectedRunId)
2348
+ return null;
2349
+ const requestedBy = body.match(/(?:^|\s)requestedBy=([^\s>]+)/)?.[1] ?? null;
2350
+ return { requestedBy };
2351
+ }
2352
+ function buildPauseSentinel(runId, requestedBy = "rig") {
2353
+ return buildControlSentinel(RIG_PAUSE_SENTINEL, runId, requestedBy);
2354
+ }
2355
+ function parsePauseSentinel(text, expectedRunId) {
2356
+ return parseControlSentinel(RIG_PAUSE_SENTINEL, text, expectedRunId);
2357
+ }
2358
+ function buildResumeSentinel(runId, requestedBy = "rig") {
2359
+ return buildControlSentinel(RIG_RESUME_SENTINEL, runId, requestedBy);
2360
+ }
2361
+ function parseResumeSentinel(text, expectedRunId) {
2362
+ return parseControlSentinel(RIG_RESUME_SENTINEL, text, expectedRunId);
2363
+ }
2364
+ var RIG_INBOX_RESOLUTION_SENTINEL = "<<RIG_INBOX_RESOLUTION";
2365
+ function encodeInboxResolutionPayload(payload) {
2366
+ return encodeURIComponent(JSON.stringify(payload));
2367
+ }
2368
+ function decodeInboxResolutionPayload(payload) {
2369
+ try {
2370
+ const parsed = JSON.parse(decodeURIComponent(payload));
2371
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
2372
+ return null;
2373
+ const record = parsed;
2374
+ if (record.kind === "approval") {
2375
+ const requestId = typeof record.requestId === "string" ? record.requestId.trim() : "";
2376
+ const decision = record.decision === "approve" || record.decision === "reject" ? record.decision : null;
2377
+ const note = typeof record.note === "string" ? record.note : record.note === null ? null : undefined;
2378
+ if (!requestId || !decision)
2379
+ return null;
2380
+ return { kind: "approval", requestId, decision, ...note !== undefined ? { note } : {} };
2381
+ }
2382
+ if (record.kind === "input") {
2383
+ const requestId = typeof record.requestId === "string" ? record.requestId.trim() : "";
2384
+ const answers = record.answers && typeof record.answers === "object" && !Array.isArray(record.answers) ? Object.fromEntries(Object.entries(record.answers).filter((entry) => typeof entry[1] === "string")) : null;
2385
+ if (!requestId || !answers)
2386
+ return null;
2387
+ return { kind: "input", requestId, answers };
2388
+ }
2389
+ } catch {
2390
+ return null;
2391
+ }
2392
+ return null;
2393
+ }
2394
+ function buildInboxResolutionSentinel(runId, resolution, requestedBy = "rig") {
2395
+ return `${RIG_INBOX_RESOLUTION_SENTINEL} runId=${runId} requestedBy=${requestedBy} data=${encodeInboxResolutionPayload(resolution)}${RIG_CONTROL_SENTINEL_END}`;
2396
+ }
2397
+ function parseInboxResolutionSentinel(text, expectedRunId) {
2398
+ const start = text.indexOf(RIG_INBOX_RESOLUTION_SENTINEL);
2399
+ if (start < 0)
2400
+ return null;
2401
+ const end = text.indexOf(RIG_CONTROL_SENTINEL_END, start);
2402
+ const body = text.slice(start + RIG_INBOX_RESOLUTION_SENTINEL.length, end >= 0 ? end : undefined).trim();
2403
+ const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
2404
+ if (runId && runId !== expectedRunId)
2405
+ return null;
2406
+ const requestedBy = body.match(/(?:^|\s)requestedBy=([^\s>]+)/)?.[1] ?? null;
2407
+ const payload = body.match(/(?:^|\s)data=([^\s>]+)/)?.[1] ?? "";
2408
+ const decoded = decodeInboxResolutionPayload(payload);
2409
+ if (!decoded)
2410
+ return null;
2411
+ return { ...decoded, requestedBy };
2412
+ }
2298
2413
  function sessionIdFromSessionFile(sessionPath) {
2299
2414
  if (!sessionPath)
2300
2415
  return null;
@@ -2353,6 +2468,384 @@ function foldRunSessionEntries(entries, runId) {
2353
2468
  });
2354
2469
  return reduceRunJournal(events);
2355
2470
  }
2471
+ // packages/contracts/src/workflow-journal.ts
2472
+ var RIG_WORKFLOW_STARTED = "rig.workflow.started";
2473
+ var RIG_WORKFLOW_TARGET_SELECTED = "rig.workflow.target.selected";
2474
+ var RIG_WORKFLOW_TASK_SELECTED = "rig.workflow.task.selected";
2475
+ var RIG_WORKFLOW_STATUS_CHANGED = "rig.workflow.status.changed";
2476
+ var RIG_WORKFLOW_OPERATOR_NOTE = "rig.workflow.operator.note";
2477
+ var RIG_WORKFLOW_INBOX_REQUESTED = "rig.workflow.inbox.requested";
2478
+ var RIG_WORKFLOW_INBOX_RESOLVED = "rig.workflow.inbox.resolved";
2479
+ var EMPTY_PROJECTION = {
2480
+ started: null,
2481
+ target: null,
2482
+ task: null,
2483
+ status: null,
2484
+ notes: [],
2485
+ inbox: [],
2486
+ resolvedInbox: [],
2487
+ updatedAt: null
2488
+ };
2489
+ function asRecord(value) {
2490
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2491
+ }
2492
+ function optionalString(value) {
2493
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
2494
+ }
2495
+ function requiredString(value) {
2496
+ const text = optionalString(value);
2497
+ return text ?? null;
2498
+ }
2499
+ function timestamp(value) {
2500
+ const text = requiredString(value);
2501
+ if (!text)
2502
+ return null;
2503
+ const millis = Date.parse(text);
2504
+ return Number.isFinite(millis) ? text : null;
2505
+ }
2506
+ function newer(left, right) {
2507
+ if (!right)
2508
+ return false;
2509
+ if (!left)
2510
+ return true;
2511
+ return Date.parse(right) >= Date.parse(left);
2512
+ }
2513
+ function isWorkflowTarget(value) {
2514
+ return value === "local" || value === "remote";
2515
+ }
2516
+ function isWorkflowStatus(value) {
2517
+ return value === "starting" || value === "running" || value === "waiting-approval" || value === "waiting-input" || value === "completed" || value === "failed" || value === "stopped";
2518
+ }
2519
+ function isInboxKind(value) {
2520
+ return value === "approval" || value === "input";
2521
+ }
2522
+ function isInboxDecision(value) {
2523
+ return value === "approved" || value === "rejected" || value === "answered";
2524
+ }
2525
+ function parseOwner(value) {
2526
+ const record = asRecord(value);
2527
+ if (!record)
2528
+ return null;
2529
+ const githubUserId = requiredString(record.githubUserId);
2530
+ const login = requiredString(record.login);
2531
+ const namespaceKey = requiredString(record.namespaceKey);
2532
+ return githubUserId && login && namespaceKey ? { githubUserId, login, namespaceKey } : null;
2533
+ }
2534
+ function parseStringArray(value) {
2535
+ if (!Array.isArray(value))
2536
+ return;
2537
+ const options = value.map(optionalString).filter((option) => option !== undefined);
2538
+ return options.length > 0 ? options : undefined;
2539
+ }
2540
+ function hasOwn(record, key) {
2541
+ return Object.prototype.hasOwnProperty.call(record, key);
2542
+ }
2543
+ function entryUpdatedAt(entry) {
2544
+ if ("createdAt" in entry)
2545
+ return entry.createdAt;
2546
+ if ("selectedAt" in entry)
2547
+ return entry.selectedAt;
2548
+ if ("changedAt" in entry)
2549
+ return entry.changedAt;
2550
+ if ("notedAt" in entry)
2551
+ return entry.notedAt;
2552
+ if ("requestedAt" in entry)
2553
+ return entry.requestedAt;
2554
+ return entry.resolvedAt;
2555
+ }
2556
+ function createWorkflowStarted(input) {
2557
+ return {
2558
+ schemaVersion: 1,
2559
+ workflowId: input.workflowId,
2560
+ target: input.target,
2561
+ selectedRepo: input.selectedRepo,
2562
+ owner: input.owner,
2563
+ createdAt: input.createdAt ?? new Date().toISOString()
2564
+ };
2565
+ }
2566
+ function createWorkflowTargetSelected(input) {
2567
+ return {
2568
+ schemaVersion: 1,
2569
+ target: input.target,
2570
+ ...input.reason ? { reason: input.reason } : {},
2571
+ selectedAt: input.selectedAt ?? new Date().toISOString()
2572
+ };
2573
+ }
2574
+ function createWorkflowTaskSelected(input) {
2575
+ return {
2576
+ schemaVersion: 1,
2577
+ taskId: input.taskId,
2578
+ ...input.title ? { title: input.title } : {},
2579
+ selectedAt: input.selectedAt ?? new Date().toISOString()
2580
+ };
2581
+ }
2582
+ function createWorkflowStatusChanged(input) {
2583
+ return {
2584
+ schemaVersion: 1,
2585
+ status: input.status,
2586
+ ...input.detail ? { detail: input.detail } : {},
2587
+ changedAt: input.changedAt ?? new Date().toISOString()
2588
+ };
2589
+ }
2590
+ function createWorkflowOperatorNote(input) {
2591
+ return {
2592
+ schemaVersion: 1,
2593
+ ...input.noteId ? { noteId: input.noteId } : {},
2594
+ note: input.note,
2595
+ notedAt: input.notedAt ?? new Date().toISOString()
2596
+ };
2597
+ }
2598
+ function createWorkflowInboxRequested(input) {
2599
+ return {
2600
+ schemaVersion: 1,
2601
+ requestId: input.requestId,
2602
+ kind: input.kind,
2603
+ title: input.title,
2604
+ ...input.body ? { body: input.body } : {},
2605
+ ...input.options && input.options.length > 0 ? { options: input.options } : {},
2606
+ requestedAt: input.requestedAt ?? new Date().toISOString()
2607
+ };
2608
+ }
2609
+ function createWorkflowInboxResolved(input) {
2610
+ const base = {
2611
+ schemaVersion: 1,
2612
+ requestId: input.requestId,
2613
+ decision: input.decision,
2614
+ resolvedAt: input.resolvedAt ?? new Date().toISOString()
2615
+ };
2616
+ return hasOwn(input, "answer") ? { ...base, answer: input.answer } : base;
2617
+ }
2618
+ function parseWorkflowStarted(data) {
2619
+ const record = asRecord(data);
2620
+ if (!record || record.schemaVersion !== 1)
2621
+ return null;
2622
+ const workflowId = requiredString(record.workflowId);
2623
+ const target = optionalString(record.target);
2624
+ const selectedRepo = requiredString(record.selectedRepo);
2625
+ const owner = parseOwner(record.owner);
2626
+ const createdAt = timestamp(record.createdAt);
2627
+ if (!workflowId || !isWorkflowTarget(target) || !selectedRepo || !owner || !createdAt)
2628
+ return null;
2629
+ return {
2630
+ schemaVersion: 1,
2631
+ workflowId,
2632
+ target,
2633
+ selectedRepo,
2634
+ owner,
2635
+ createdAt
2636
+ };
2637
+ }
2638
+ function parseWorkflowTargetSelected(data) {
2639
+ const record = asRecord(data);
2640
+ if (!record || record.schemaVersion !== 1)
2641
+ return null;
2642
+ const target = optionalString(record.target);
2643
+ const selectedAt = timestamp(record.selectedAt);
2644
+ if (!isWorkflowTarget(target) || !selectedAt)
2645
+ return null;
2646
+ const reason = optionalString(record.reason);
2647
+ return {
2648
+ schemaVersion: 1,
2649
+ target,
2650
+ ...reason ? { reason } : {},
2651
+ selectedAt
2652
+ };
2653
+ }
2654
+ function parseWorkflowTaskSelected(data) {
2655
+ const record = asRecord(data);
2656
+ if (!record || record.schemaVersion !== 1)
2657
+ return null;
2658
+ const taskId = requiredString(record.taskId);
2659
+ const selectedAt = timestamp(record.selectedAt);
2660
+ if (!taskId || !selectedAt)
2661
+ return null;
2662
+ const title = optionalString(record.title);
2663
+ return { schemaVersion: 1, taskId, ...title ? { title } : {}, selectedAt };
2664
+ }
2665
+ function parseWorkflowStatusChanged(data) {
2666
+ const record = asRecord(data);
2667
+ if (!record || record.schemaVersion !== 1)
2668
+ return null;
2669
+ const status = optionalString(record.status);
2670
+ const changedAt = timestamp(record.changedAt);
2671
+ if (!changedAt || !isWorkflowStatus(status))
2672
+ return null;
2673
+ const detail = optionalString(record.detail);
2674
+ return { schemaVersion: 1, status, ...detail ? { detail } : {}, changedAt };
2675
+ }
2676
+ function parseWorkflowOperatorNote(data) {
2677
+ const record = asRecord(data);
2678
+ if (!record || record.schemaVersion !== 1)
2679
+ return null;
2680
+ const note = requiredString(record.note);
2681
+ const notedAt = timestamp(record.notedAt);
2682
+ if (!note || !notedAt)
2683
+ return null;
2684
+ const noteId = optionalString(record.noteId);
2685
+ return { schemaVersion: 1, ...noteId ? { noteId } : {}, note, notedAt };
2686
+ }
2687
+ function parseWorkflowInboxRequested(data) {
2688
+ const record = asRecord(data);
2689
+ if (!record || record.schemaVersion !== 1)
2690
+ return null;
2691
+ const requestId = requiredString(record.requestId);
2692
+ const kind = optionalString(record.kind);
2693
+ const title = requiredString(record.title);
2694
+ const requestedAt = timestamp(record.requestedAt);
2695
+ if (!requestId || !isInboxKind(kind) || !title || !requestedAt)
2696
+ return null;
2697
+ const body = optionalString(record.body);
2698
+ const options = parseStringArray(record.options);
2699
+ return {
2700
+ schemaVersion: 1,
2701
+ requestId,
2702
+ kind,
2703
+ title,
2704
+ ...body ? { body } : {},
2705
+ ...options ? { options } : {},
2706
+ requestedAt
2707
+ };
2708
+ }
2709
+ function parseWorkflowInboxResolved(data) {
2710
+ const record = asRecord(data);
2711
+ if (!record || record.schemaVersion !== 1)
2712
+ return null;
2713
+ const requestId = requiredString(record.requestId);
2714
+ const decision = optionalString(record.decision);
2715
+ const resolvedAt = timestamp(record.resolvedAt);
2716
+ if (!requestId || !isInboxDecision(decision) || !resolvedAt)
2717
+ return null;
2718
+ const base = { schemaVersion: 1, requestId, decision, resolvedAt };
2719
+ return hasOwn(record, "answer") ? { ...base, answer: record.answer } : base;
2720
+ }
2721
+ function collectPendingInboxRequests(entries) {
2722
+ const requested = new Map;
2723
+ const resolved = new Map;
2724
+ for (const entry of entries) {
2725
+ if (entry.type !== "custom")
2726
+ continue;
2727
+ if (entry.customType === RIG_WORKFLOW_INBOX_REQUESTED) {
2728
+ const request = parseWorkflowInboxRequested(entry.data);
2729
+ if (request && newer(requested.get(request.requestId)?.requestedAt, request.requestedAt))
2730
+ requested.set(request.requestId, request);
2731
+ } else if (entry.customType === RIG_WORKFLOW_INBOX_RESOLVED) {
2732
+ const resolution = parseWorkflowInboxResolved(entry.data);
2733
+ if (resolution && newer(resolved.get(resolution.requestId), resolution.resolvedAt))
2734
+ resolved.set(resolution.requestId, resolution.resolvedAt);
2735
+ }
2736
+ }
2737
+ for (const [requestId, request] of requested) {
2738
+ const resolvedAt = resolved.get(requestId);
2739
+ if (resolvedAt && Date.parse(resolvedAt) >= Date.parse(request.requestedAt))
2740
+ requested.delete(requestId);
2741
+ }
2742
+ return Array.from(requested.values()).sort((a, b) => Date.parse(b.requestedAt) - Date.parse(a.requestedAt));
2743
+ }
2744
+ function collectResolvedInboxRequests(entries) {
2745
+ const requested = new Map;
2746
+ const resolved = new Map;
2747
+ for (const entry of entries) {
2748
+ if (entry.type !== "custom")
2749
+ continue;
2750
+ if (entry.customType === RIG_WORKFLOW_INBOX_REQUESTED) {
2751
+ const request = parseWorkflowInboxRequested(entry.data);
2752
+ if (request && newer(requested.get(request.requestId), request.requestedAt))
2753
+ requested.set(request.requestId, request.requestedAt);
2754
+ } else if (entry.customType === RIG_WORKFLOW_INBOX_RESOLVED) {
2755
+ const resolution = parseWorkflowInboxResolved(entry.data);
2756
+ if (resolution && newer(resolved.get(resolution.requestId)?.resolvedAt, resolution.resolvedAt)) {
2757
+ resolved.set(resolution.requestId, resolution);
2758
+ }
2759
+ }
2760
+ }
2761
+ for (const [requestId, resolution] of resolved) {
2762
+ const requestedAt = requested.get(requestId);
2763
+ if (requestedAt && Date.parse(requestedAt) > Date.parse(resolution.resolvedAt))
2764
+ resolved.delete(requestId);
2765
+ }
2766
+ return Array.from(resolved.values()).sort((a, b) => Date.parse(b.resolvedAt) - Date.parse(a.resolvedAt));
2767
+ }
2768
+ function projectCollabWorkflowMarker(collab) {
2769
+ const status = createWorkflowStatusChanged({
2770
+ status: collab.stale ? "stopped" : "running",
2771
+ detail: collab.stale ? "Stale OMP collab session discovered from the sanctioned collab registry." : "Live OMP collab session discovered from the sanctioned collab registry.",
2772
+ changedAt: collab.updatedAt
2773
+ });
2774
+ return {
2775
+ started: null,
2776
+ target: null,
2777
+ task: null,
2778
+ status,
2779
+ notes: [],
2780
+ inbox: [],
2781
+ resolvedInbox: [],
2782
+ updatedAt: newer(collab.startedAt, collab.updatedAt) ? collab.updatedAt : collab.startedAt
2783
+ };
2784
+ }
2785
+ function projectWorkflowEntries(entries) {
2786
+ let started = null;
2787
+ let target = null;
2788
+ let task = null;
2789
+ let status = null;
2790
+ const notes = [];
2791
+ let updatedAt = null;
2792
+ for (const entry of entries) {
2793
+ if (entry.type !== "custom")
2794
+ continue;
2795
+ if (entry.customType === RIG_WORKFLOW_STARTED) {
2796
+ const parsed = parseWorkflowStarted(entry.data);
2797
+ if (parsed && newer(started?.createdAt, parsed.createdAt))
2798
+ started = parsed;
2799
+ } else if (entry.customType === RIG_WORKFLOW_TARGET_SELECTED) {
2800
+ const parsed = parseWorkflowTargetSelected(entry.data);
2801
+ if (parsed && newer(target?.selectedAt, parsed.selectedAt))
2802
+ target = parsed;
2803
+ } else if (entry.customType === RIG_WORKFLOW_TASK_SELECTED) {
2804
+ const parsed = parseWorkflowTaskSelected(entry.data);
2805
+ if (parsed && newer(task?.selectedAt, parsed.selectedAt))
2806
+ task = parsed;
2807
+ } else if (entry.customType === RIG_WORKFLOW_STATUS_CHANGED) {
2808
+ const parsed = parseWorkflowStatusChanged(entry.data);
2809
+ if (parsed && newer(status?.changedAt, parsed.changedAt))
2810
+ status = parsed;
2811
+ } else if (entry.customType === RIG_WORKFLOW_OPERATOR_NOTE) {
2812
+ const parsed = parseWorkflowOperatorNote(entry.data);
2813
+ if (parsed)
2814
+ notes.push(parsed);
2815
+ }
2816
+ }
2817
+ notes.sort((a, b) => Date.parse(b.notedAt) - Date.parse(a.notedAt));
2818
+ for (const value of [started, target, task, status, ...notes].map((entry) => entry ? entryUpdatedAt(entry) : null)) {
2819
+ if (newer(updatedAt, value))
2820
+ updatedAt = value;
2821
+ }
2822
+ const inbox = collectPendingInboxRequests(entries);
2823
+ const resolvedInbox = collectResolvedInboxRequests(entries);
2824
+ for (const entry of [...inbox, ...resolvedInbox]) {
2825
+ if (newer(updatedAt, entryUpdatedAt(entry)))
2826
+ updatedAt = entryUpdatedAt(entry);
2827
+ }
2828
+ if (!started && !target && !task && !status && notes.length === 0 && inbox.length === 0 && resolvedInbox.length === 0 && !updatedAt)
2829
+ return EMPTY_PROJECTION;
2830
+ return { started, target, task, status, notes, inbox, resolvedInbox, updatedAt };
2831
+ }
2832
+ // packages/contracts/src/run-timeline.ts
2833
+ function isRecord(value) {
2834
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2835
+ }
2836
+ function timelineEntriesFromCustomEntries(entries) {
2837
+ return entries.flatMap((entry) => {
2838
+ if (entry.customType !== CUSTOM_TYPE_FOR["timeline-entry"] || !isRecord(entry.data))
2839
+ return [];
2840
+ const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
2841
+ const type = typeof payload.type === "string" ? payload.type : "timeline";
2842
+ const stage = typeof payload.stage === "string" ? payload.stage : typeof payload.name === "string" ? payload.name : null;
2843
+ const status = typeof payload.status === "string" ? payload.status : typeof payload.outcome === "string" ? payload.outcome : null;
2844
+ const detail = typeof payload.detail === "string" ? payload.detail : typeof payload.message === "string" ? payload.message : null;
2845
+ const at = typeof entry.data.at === "string" ? entry.data.at : typeof payload.at === "string" ? payload.at : null;
2846
+ return [{ at, type, stage, status, detail }];
2847
+ });
2848
+ }
2356
2849
  // packages/contracts/src/run-status.ts
2357
2850
  var OPERATOR_INACTIVE_RUN_STATUSES = new Set([
2358
2851
  "completed",
@@ -5540,14 +6033,15 @@ var TOP_LEVEL_SECTIONS = [
5540
6033
  subtitle: "every run is an isolated, collaborative OMP session on the selected target",
5541
6034
  commands: [
5542
6035
  {
5543
- command: "rig run start <task|#issue|--next>",
6036
+ command: "rig run [start] <task|#issue|--next>",
5544
6037
  description: "Dispatch a run for a task (refuses if one is already active; --force overrides).",
5545
6038
  usecase: "Kick off work on a specific issue, or let Rig pick the next ready one.",
5546
6039
  examples: [
5547
- "rig run start 204 # by task/issue id",
5548
- "rig run start --next # next ready task by deps/priority",
6040
+ "rig run 204 # by task/issue id",
6041
+ "rig run --next # next ready task by deps/priority",
6042
+ "rig run --title 'Fix API' 204 # title override before the task ref",
5549
6043
  "rig run start 204 --force # dispatch even if a run is already active",
5550
- "rig run start 204 --model opus-4.8 # override the agent model"
6044
+ "rig run start 204 --model opus-4.8 --prompt 'focus tests'"
5551
6045
  ]
5552
6046
  },
5553
6047
  {
@@ -5592,15 +6086,27 @@ var TOP_LEVEL_SECTIONS = [
5592
6086
  },
5593
6087
  {
5594
6088
  command: "rig run stop <id> [--reason <text>]",
5595
- description: "Request a graceful stop of a run.",
6089
+ description: "Request a graceful stop of a run (terminal).",
5596
6090
  usecase: "Halt a run that's off-track or no longer needed.",
5597
6091
  examples: ["rig run stop 019ee11f", "rig run stop 019ee11f --reason 'superseded by #210'"]
5598
6092
  },
5599
6093
  {
5600
- command: "rig run resume|restart <id>",
5601
- description: "Re-dispatch a task (detached runs start fresh).",
6094
+ command: "rig run pause <id>",
6095
+ description: "Park a live run (running \u2192 paused) without finalizing; the session stays attachable.",
6096
+ usecase: "Hold an autonomous run mid-flight \u2014 free the agent without losing its place.",
6097
+ examples: ["rig run pause 019ee11f"]
6098
+ },
6099
+ {
6100
+ command: "rig run resume <id>",
6101
+ description: "Resume a paused run (paused \u2192 running); the agent continues. A dead/stale run is re-dispatched fresh instead.",
6102
+ usecase: "Un-pause a parked run, or retry a finished one.",
6103
+ examples: ["rig run resume 019ee11f"]
6104
+ },
6105
+ {
6106
+ command: "rig run restart <id>",
6107
+ description: "Re-dispatch a task as a fresh run (new runId, clean runtime).",
5602
6108
  usecase: "Retry a failed/stopped run from a clean slate.",
5603
- examples: ["rig run restart 019ee11f", "rig run resume 019ee11f"]
6109
+ examples: ["rig run restart 019ee11f"]
5604
6110
  },
5605
6111
  {
5606
6112
  command: "rig run timeline|replay <id>",
@@ -5616,12 +6122,12 @@ var TOP_LEVEL_SECTIONS = [
5616
6122
  subtitle: "github-issues / files task sources feed dispatch",
5617
6123
  commands: [
5618
6124
  {
5619
- command: "rig task list [--search <t>] [--status <s>]",
6125
+ command: "rig task list [--search <t>] [--state <open|closed>]",
5620
6126
  description: "List tasks from the configured source, with filters.",
5621
6127
  usecase: "Find what's available to dispatch; filter by text or lifecycle status.",
5622
6128
  examples: [
5623
6129
  "rig task list",
5624
- "rig task list --status open",
6130
+ "rig task list --state open",
5625
6131
  "rig task list --search 'auth'"
5626
6132
  ]
5627
6133
  },
@@ -5633,10 +6139,10 @@ var TOP_LEVEL_SECTIONS = [
5633
6139
  examples: ["rig task show 204", "rig task info 204"]
5634
6140
  },
5635
6141
  {
5636
- command: "rig task scope|deps|status <id>",
5637
- description: "Inspect a task's scope globs, dependencies, or lifecycle status.",
6142
+ command: "rig task scope|deps --task <id>; rig task status",
6143
+ description: "Inspect a task's scope globs/dependencies, or show project task status.",
5638
6144
  usecase: "Check what files a run may touch / what's blocking it.",
5639
- examples: ["rig task scope 204", "rig task deps 204", "rig task status 204"]
6145
+ examples: ["rig task scope --task 204", "rig task deps --task 204", "rig task status"]
5640
6146
  },
5641
6147
  { command: "rig task lookup <term>", description: "Find a task by id/title substring.", examples: ["rig task lookup revoke-invite"] },
5642
6148
  { command: "rig task record \u2026", description: "Write task state/metadata back to the source.", usecase: "Programmatically update task lifecycle from scripts/CI." },
@@ -5689,20 +6195,19 @@ var TOP_LEVEL_SECTIONS = [
5689
6195
  title: "Placement & target \u2014 local vs remote execution",
5690
6196
  subtitle: "where runs execute; the backbone relay/registry is shared",
5691
6197
  commands: [
5692
- { command: "rig server status", description: "Show the selected placement (local/remote) and project-root link.", examples: ["rig server status"] },
6198
+ { command: "rig server status", description: "Show the selected placement (local/remote) and task source.", examples: ["rig server status"] },
5693
6199
  { command: "rig server list", description: "List configured server targets.", examples: ["rig server list"] },
5694
6200
  {
5695
- command: "rig server use <alias>",
6201
+ command: "rig server use <local|alias>",
5696
6202
  description: "Select a server target (execution placement).",
5697
6203
  usecase: "Switch a repo between local and a remote host for run execution.",
5698
6204
  examples: ["rig server use local", "rig server use prod-box"]
5699
6205
  },
5700
6206
  {
5701
- command: "rig server add|remove <alias>",
6207
+ command: "rig server add --alias <alias> --host <host> [--port <n>] | remove <alias>",
5702
6208
  description: "Configure or remove a remote server target.",
5703
- examples: ["rig server add prod-box ubuntu@host", "rig server remove prod-box"]
6209
+ examples: ["rig server add --alias prod-box --host ubuntu@host", "rig server remove prod-box"]
5704
6210
  },
5705
- { command: "rig server repair-link", description: "Repair the project-root link for the selected target.", usecase: "Fix a remote that shows unavailable after a host/checkout change.", examples: ["rig server repair-link"] },
5706
6211
  {
5707
6212
  command: "rig remote <list|add|test|status|\u2026>",
5708
6213
  description: "Lower-level remote endpoint management + run control over a remote.",
@@ -5721,10 +6226,10 @@ var TOP_LEVEL_SECTIONS = [
5721
6226
  examples: ["rig inbox approvals", "rig inbox inputs"]
5722
6227
  },
5723
6228
  {
5724
- command: "rig inbox approve <id> | respond <id> <text>",
5725
- description: "Approve an entry or answer a user-input prompt.",
5726
- usecase: "Unblock a paused run from the CLI.",
5727
- examples: ["rig inbox approve abc123", "rig inbox respond abc123 'use option B'"]
6229
+ command: "rig inbox approve --run <id> --request <id> --decision approve|reject | respond --run <id> --request <id> --text <answer>",
6230
+ description: "Attempt to resolve an approval or answer a user-input prompt; attach if one-shot delivery is unavailable.",
6231
+ usecase: "Resolve pending requests from automation when the live run accepts out-of-band resolution.",
6232
+ examples: ["rig inbox approve --run run1 --request abc123 --decision approve", "rig inbox respond --run run1 --request abc123 --text 'use option B'"]
5728
6233
  },
5729
6234
  { command: "rig inbox watch", description: "Stream inbox changes live.", examples: ["rig inbox watch"] },
5730
6235
  { command: "rig doctor", description: "Cheap health checks: session discovery, registry, selected target, workflow entries.", usecase: "First thing to run when something seems off.", examples: ["rig doctor"] }
@@ -5735,14 +6240,14 @@ var TOP_LEVEL_SECTIONS = [
5735
6240
  subtitle: "logs, artifacts, diffs, failure analysis",
5736
6241
  commands: [
5737
6242
  {
5738
- command: "rig inspect run-logs|logs --run <id>",
5739
- description: "Tail a run's process / session logs.",
6243
+ command: "rig inspect run-logs --run <id> | logs --task <id>",
6244
+ description: "Print run/session log lines.",
5740
6245
  usecase: "Debug why a run failed or stalled.",
5741
- examples: ["rig inspect run-logs --run 019ee11f", "rig inspect logs --run 019ee11f"]
6246
+ examples: ["rig inspect run-logs --run 019ee11f", "rig inspect logs --task 204"]
5742
6247
  },
5743
6248
  { command: "rig inspect runs", description: "List runs from on-disk runtime state.", examples: ["rig inspect runs"] },
5744
- { command: "rig inspect artifact(s) --task <id>", description: "Inspect a task/run's artifacts.", examples: ["rig inspect artifacts --task 204"] },
5745
- { command: "rig inspect diff|graph", description: "Run diff / task dependency graph.", examples: ["rig inspect diff", "rig inspect graph"] },
6249
+ { command: "rig inspect artifact --task <id> --file <name> | artifacts --task <id>", description: "Inspect a task/run's artifacts.", examples: ["rig inspect artifacts --task 204", "rig inspect artifact --task 204 --file task-result.json"] },
6250
+ { command: "rig inspect diff --task <id> | graph [--task <id>]", description: "Run diff / task dependency graph.", examples: ["rig inspect diff --task 204", "rig inspect graph --task 204"] },
5746
6251
  { command: "rig inspect failures --task <id>", description: "Classified failure diagnostics for a task's runs.", usecase: "Triage repeated failures on one task.", examples: ["rig inspect failures --task 204"] },
5747
6252
  { command: "rig stats", description: "Aggregate run stats over a window.", examples: ["rig stats", "rig stats --since 7d"] }
5748
6253
  ]
@@ -5783,15 +6288,14 @@ var PRIMARY_GROUPS = [
5783
6288
  {
5784
6289
  name: "server",
5785
6290
  summary: "Compatibility server selector; the product Server target screen lives in Rig Cockpit.",
5786
- usage: ["rig server <status|list|add|use|repair-link|start> [options]"],
6291
+ usage: ["rig server <status|list|add|use|remove> [options]"],
5787
6292
  commands: [
5788
- { command: "status", description: "Legacy automation-only: show the selected server and project-root link.", primary: true },
6293
+ { command: "status", description: "Legacy automation-only: show the selected placement and task source.", primary: true },
5789
6294
  { command: "use local", description: "Legacy automation-only: switch this repo to local execution placement.", primary: true },
5790
- { command: "add <alias> <url>", description: "Legacy automation-only: save a remote server URL.", primary: true },
6295
+ { command: "add --alias <alias> --host <host> [--port <n>]", description: "Legacy automation-only: save a remote execution target.", primary: true },
5791
6296
  { command: "use <alias>", description: "Legacy automation-only: select a remote execution target alias.", primary: true },
5792
- { command: "repair-link [--prepare|--backfill-only] [--repo owner/repo]", description: "Legacy automation-only: repair a server-host checkout link.", primary: true },
5793
- { command: "list", description: "Legacy automation-only: list saved local/remote server aliases.", primary: true },
5794
- { command: "start [--host <host>] [--port <n>]", description: "Legacy diagnostic-only: start a local rig-server process." }
6297
+ { command: "remove <alias>", description: "Legacy automation-only: remove a saved remote execution target.", primary: true },
6298
+ { command: "list", description: "Legacy automation-only: list saved local/remote server aliases.", primary: true }
5795
6299
  ],
5796
6300
  examples: [
5797
6301
  "rig server status # legacy automation only",
@@ -5807,9 +6311,9 @@ var PRIMARY_GROUPS = [
5807
6311
  { command: "list [--assignee <login|me|@me>] [--state open|closed]", description: "Legacy automation-only: list tasks from the configured source.", primary: true },
5808
6312
  { command: "next [filters]", description: "Legacy automation-only: render the next matching task card.", primary: true },
5809
6313
  { command: "show <id>|--task <id> [--raw]", description: "Legacy automation-only: show a task payload for scripts.", primary: true },
5810
- { command: "run [#<issue>|<task-id>|--task <id>]", description: "Legacy/fenced: dispatch through the old task-run CLI path; not the task-detail dispatch path.", primary: true },
6314
+ { command: "run [#<issue>|<task-id>|--task <id>] [--title <t>] [--model <m>] [--prompt <p>|--initial-prompt <p>]", description: "Legacy/fenced: dispatch through the CLI run path; not the task-detail dispatch path.", primary: true },
5811
6315
  { command: "ready", description: "Legacy automation-only: list task IDs that old dispatch can run now." },
5812
- { command: "validate|verify [--task <id>]", description: "Legacy automation-only: run configured task checks/review gates." },
6316
+ { command: "validate|verify [--task <id>]", description: "Legacy automation-only: run configured task checks/review gates; failing checks exit nonzero." },
5813
6317
  { command: "details --task <id>", description: "Legacy automation-only: show full task info from the configured source." },
5814
6318
  { command: "reopen [--task <id> | --all] [--reason <text>]", description: "Legacy automation-only: reopen closed task(s) in the configured source." },
5815
6319
  { command: "artifacts|artifact-dir|artifact-write", description: "Legacy automation-only: inspect or write task artifacts." }
@@ -5817,30 +6321,30 @@ var PRIMARY_GROUPS = [
5817
6321
  examples: [
5818
6322
  "rig task list --assignee @me --limit 20 # legacy automation only",
5819
6323
  "rig task show 123 --raw # legacy automation only",
5820
- "rig task run #123 --runtime-adapter pi # explicit legacy dispatch only"
6324
+ "rig task run #123 --model opus-4.8 --prompt 'focus tests'"
5821
6325
  ],
5822
6326
  next: ["For normal UX, run bare `rig`, use Cockpit Server/Tasks, then dispatch from Task detail."]
5823
6327
  },
5824
6328
  {
5825
6329
  name: "run",
5826
6330
  summary: "Legacy automation-only run-record inspection; OMP Runs/collab is the live surface.",
5827
- usage: ["rig run <list|status|show|steer|stop|resume|restart> [options]"],
6331
+ usage: ["rig run [start] <task|#issue|--next> [--title <t>] [--model <m>] [--prompt <p>|--initial-prompt <p>]", "rig run <list|status|show|steer|stop|resume|restart> [options]"],
5828
6332
  commands: [
6333
+ { command: "[start] <task|#issue|--next> [--title <t>] [--model <m>] [--prompt <p>|--initial-prompt <p>]", description: "Legacy automation-only: dispatch a task run through the unified run launcher.", primary: true },
5829
6334
  { command: "list", description: "Legacy automation-only: list old run records from selected server/local state.", primary: true },
5830
6335
  { command: "status", description: "Legacy automation-only: render old active/recent run-record groups; not live OMP status.", primary: true },
5831
6336
  { command: "show <id>|--run <id> [--raw]", description: "Legacy automation-only: show an old run-record payload for scripts.", primary: true },
5832
6337
  { command: "attach <run-id>|--run <id>", description: "Legacy/fenced: not the Rig Cockpit attach path; use OMP Runs or `rig join <link>`.", primary: true },
5833
6338
  { command: "steer <run-id> --message <text>", description: "Legacy automation-only: queue steering into an old live worker.", primary: true },
5834
- { command: "stop [<run-id>|--run <id>]", description: "Legacy automation-only: request stop for old run records.", primary: true },
5835
- { command: "resume [<run-id>]", description: "Legacy automation-only: resume an interrupted old run record." },
5836
- { command: "restart [<run-id>]", description: "Legacy automation-only: re-dispatch an old run from a clean runtime." },
5837
- { command: "timeline --run <id> [--follow]", description: "Legacy automation-only: stream raw old timeline events." },
5838
- { command: "replay <run-id>|--run <id> [--with-session]", description: "Legacy automation-only: print an old consolidated run timeline." },
5839
- { command: "delete|cleanup", description: "Legacy automation-only: remove completed old run records/artifacts." }
6339
+ { command: "stop <run-id>|--run <id> [--reason <text>]", description: "Legacy automation-only: request stop for old run records.", primary: true },
6340
+ { command: "resume <run-id>|--run <id>", description: "Legacy automation-only: resume an interrupted old run record." },
6341
+ { command: "restart <run-id>|--run <id>", description: "Legacy automation-only: re-dispatch an old run from a clean runtime." },
6342
+ { command: "timeline|replay <run-id>|--run <id> [--with-session]", description: "Legacy automation-only: print an old consolidated run timeline." },
6343
+ { command: "delete|cleanup [<run-id>|--run <id>]", description: "Legacy automation-only: remove stale live-registry entries; journals are retained." }
5840
6344
  ],
5841
6345
  examples: [
5842
- "rig run list # legacy automation only",
5843
- "rig run show <run-id> # legacy automation only",
6346
+ "rig run 204 # dispatch task 204",
6347
+ "rig run --title 'Fix API' 204",
5844
6348
  "rig run stop <run-id> # legacy automation only"
5845
6349
  ],
5846
6350
  next: [
@@ -5856,7 +6360,7 @@ var PRIMARY_GROUPS = [
5856
6360
  { command: "approvals [--run <id>] [--task <id>]", description: "Legacy automation-only: list pending approval records.", primary: true },
5857
6361
  { command: "inputs [--run <id>] [--task <id>]", description: "Legacy automation-only: list pending user-input records.", primary: true },
5858
6362
  { command: "approve --run <id> --request <id> --decision approve|reject", description: "Legacy automation-only: resolve an approval record." },
5859
- { command: "respond --run <id> --request <id> --answer key=value", description: "Legacy automation-only: answer a user-input record." }
6363
+ { command: "respond --run <id> --request <id> --text <answer> | --answer <answer>", description: "Legacy automation-only: answer a user-input record." }
5860
6364
  ],
5861
6365
  examples: [
5862
6366
  "rig inbox approvals # legacy automation only"
@@ -5878,15 +6382,17 @@ var PRIMARY_GROUPS = [
5878
6382
  {
5879
6383
  name: "inspect",
5880
6384
  summary: "Legacy automation-only artifact/log inspection; normal UX is OMP session history.",
5881
- usage: ["rig inspect <logs|artifacts|failures|graph|audit> --task <id>"],
6385
+ usage: ["rig inspect <logs|artifact|artifacts|run-logs|runs|failures|graph|diff> [options]"],
5882
6386
  commands: [
5883
6387
  { command: "logs --task <id>", description: "Legacy automation-only: latest old run log for a task.", primary: true },
6388
+ { command: "run-logs --run <id>", description: "Legacy automation-only: log lines for a specific run.", primary: true },
6389
+ { command: "runs", description: "Legacy automation-only: list projected runs." },
6390
+ { command: "artifact --task <id> --file <name>", description: "Legacy automation-only: preview one completion artifact." },
5884
6391
  { command: "artifacts --task <id>", description: "Legacy automation-only: list completion artifacts.", primary: true },
6392
+ { command: "diff --task <id>", description: "Legacy automation-only: list task changed files." },
5885
6393
  { command: "failures --task <id>", description: "Legacy automation-only: recorded failures for a task.", primary: true },
5886
- { command: "graph", description: "Legacy automation-only: task dependency graph." },
5887
- { command: "audit", description: "Legacy automation-only: controlled-command audit trail." }
6394
+ { command: "graph [--task <id>]", description: "Legacy automation-only: task dependency graph." }
5888
6395
  ],
5889
- examples: ["rig inspect logs --task <id> # legacy automation only"],
5890
6396
  next: ["For normal UX, use the OMP Runs screen and OMP session history."]
5891
6397
  },
5892
6398
  {