@drisp/cli 0.7.13 → 0.7.14

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/cli.js CHANGED
@@ -9,6 +9,10 @@ import {
9
9
  RUN_EXIT_CODE,
10
10
  ServiceNotEnabledError,
11
11
  approvalAnswer,
12
+ assignmentPageSchema,
13
+ assignmentReportSchema,
14
+ assignmentRevisionSchema,
15
+ assignmentSchema,
12
16
  attachRuntimeEventLoop,
13
17
  attemptForecast,
14
18
  behindMain,
@@ -20,18 +24,30 @@ import {
20
24
  buildPostByToolUseId,
21
25
  buildProbeConfigs,
22
26
  calibrationSchema,
27
+ cancelProgress,
23
28
  centerAnsi,
24
29
  childWorktreePath,
25
30
  classifyFailure,
31
+ commandProgress,
26
32
  commandSchema,
27
33
  commitBuildFiles,
28
34
  compactText,
35
+ completionPackageSchema,
29
36
  computeInputRows,
30
37
  connectAlreadyConnectedSchema,
31
38
  connectCodeResponseSchema,
32
39
  connectPendingResponseSchema,
33
40
  connectRedeemResponseSchema,
34
41
  connectStartResponseSchema,
42
+ controlProgress,
43
+ controlRequestSchema,
44
+ coordinationActivityPageSchema,
45
+ coordinationBriefSchema,
46
+ coordinationCommandResultSchema,
47
+ coordinationCommandSchema,
48
+ coordinationInboxEventSchema,
49
+ coordinationSpendSchema,
50
+ coordinatorSchema,
35
51
  createFeedMapper,
36
52
  createLocalFeedSink,
37
53
  createRuntime,
@@ -39,10 +55,26 @@ import {
39
55
  credentialHelperKey,
40
56
  cursorToVisualPosition,
41
57
  defaultWorktreesRoot,
58
+ describeChecks,
42
59
  detectClaudeVersion,
43
60
  eligibilityReason,
44
61
  ensureBuildWorktree,
45
62
  ensureChildWorktree,
63
+ executionActivationSchema,
64
+ executionCancellationSchema,
65
+ executionControlState,
66
+ executionHierarchySchema,
67
+ executionMessageSchema,
68
+ executionOutcomeSchema,
69
+ executionPageSchema,
70
+ executionQuestionPageSchema,
71
+ executionQuestionSchema,
72
+ executionSchema,
73
+ executionStepsSchema,
74
+ executionWorkflowPageSchema,
75
+ executionWorkflowRevisionPageSchema,
76
+ executionWorkflowRevisionSchema,
77
+ executionWorkflowSchema,
46
78
  expandHome,
47
79
  extractFriendlyServerName,
48
80
  extractPermissionSnapshot,
@@ -71,10 +103,12 @@ import {
71
103
  historyRecordSchema,
72
104
  hubHttpUrl,
73
105
  identityHubOrigin,
106
+ inboxCursorSchema,
74
107
  isBashToolResponse,
75
108
  isCommandPrefix,
76
109
  isGitHubRepository,
77
110
  isScopedPermissionsRequest,
111
+ judgeChecks,
78
112
  lastCommitTouching,
79
113
  listAwaitingAttentionRuns,
80
114
  listHarnessAdapters,
@@ -85,9 +119,11 @@ import {
85
119
  lookupCredential,
86
120
  machineInstallationId,
87
121
  makeSkippedProbe,
122
+ maxClientCancelBoundaryGraceMs,
88
123
  mergeFeedItems,
89
124
  mergeGuidance,
90
125
  messageGlyphs,
126
+ messageProgress,
91
127
  overriddenIdentityPath,
92
128
  parseFrontmatter,
93
129
  parseToolName,
@@ -97,6 +133,9 @@ import {
97
133
  probeSkipReason,
98
134
  processRegistry,
99
135
  progressGlyphs,
136
+ qaRoundPageSchema,
137
+ readCheckList,
138
+ recoveryEpisodeSchema,
100
139
  register,
101
140
  registerCleanupOnExit,
102
141
  releaseMcpAsset,
@@ -112,8 +151,12 @@ import {
112
151
  resolveHookForwarderCommand,
113
152
  resolveRuntimeAuthOverlay,
114
153
  resolveToolColumn,
154
+ reviewRoundPageSchema,
115
155
  runClaudeAuthStatus,
116
156
  runConfiguredRunner,
157
+ runControlCommandResultSchema,
158
+ runControlCommandSchema,
159
+ runControlRecordSchema,
117
160
  runExec,
118
161
  runMachineRunners,
119
162
  runProbe,
@@ -130,12 +173,14 @@ import {
130
173
  runnerSoftwareVersion,
131
174
  runnerUpdateRequestSchema,
132
175
  scopeFingerprint,
176
+ selectWorkPath,
133
177
  serviceInstallationId,
134
178
  sessionsDir,
135
179
  sliceAnsi,
136
180
  spaces,
137
181
  startDashboardDecisionDrain,
138
182
  startWorkflowExecution,
183
+ supportedOperations,
139
184
  supportsSessionApproval,
140
185
  teamDefaultRequestSchema,
141
186
  teamDefaultSchema,
@@ -146,9 +191,113 @@ import {
146
191
  trackerOperationSchema,
147
192
  trackerSnapshotSchema,
148
193
  visualPositionToOffset,
194
+ workActivityPageSchema,
195
+ workAttachmentCheckPageSchema,
196
+ workAttachmentPageSchema,
197
+ workAttachmentSchema,
198
+ workAttentionActivityPageSchema,
199
+ workAttentionCommandResultSchema,
200
+ workAttentionCommandSchema,
201
+ workAttentionRequestPageSchema,
202
+ workAttentionRequestSchema,
203
+ workAuthoritySchema,
204
+ workCandidatePageSchema,
205
+ workCandidateSchema,
206
+ workCandidateSelectionSchema,
207
+ workClientContract,
208
+ workClientContractHeader,
209
+ workClientUnsupported,
210
+ workCommandResultSchema,
211
+ workCommandSchema,
212
+ workCommentPageSchema,
213
+ workCommentRevisionPageSchema,
214
+ workCommentSchema,
215
+ workDeliveryMigrationPageSchema,
216
+ workDeliveryWorkflowPageSchema,
217
+ workDeliveryWorkflowRevisionPageSchema,
218
+ workDeliveryWorkflowRevisionSchema,
219
+ workDeliveryWorkflowSchema,
220
+ workDependencyGateSchema,
221
+ workDeploymentActivityPageSchema,
222
+ workDeploymentCommandResultSchema,
223
+ workDeploymentCommandSchema,
224
+ workDeploymentPageSchema,
225
+ workDeploymentSchema,
226
+ workDiscussionActivityPageSchema,
227
+ workDiscussionCommandResultSchema,
228
+ workDiscussionCommandSchema,
229
+ workEvidenceActivityPageSchema,
230
+ workEvidenceCommandResultSchema,
231
+ workEvidenceCommandSchema,
232
+ workEvidencePageSchema,
233
+ workEvidenceSchema,
234
+ workExecutionBindingPageSchema,
235
+ workExecutionBindingSchema,
236
+ workExecutionPolicySchema,
237
+ workExecutionReservationPageSchema,
238
+ workExecutionReservationSchema,
239
+ workImportBatchPageSchema,
240
+ workImportBatchSchema,
241
+ workImportCommandResultSchema,
242
+ workImportCommandSchema,
243
+ workImportComponentPageSchema,
244
+ workImportItemPageSchema,
245
+ workImportOutcomePageSchema,
246
+ workImportSnapshotSchema,
247
+ workIssueAssigneePageSchema,
248
+ workIssueLabelPageSchema,
249
+ workIssueMembershipSchema,
250
+ workIssuePageSchema,
251
+ workIssueRelationPageSchema,
252
+ workIssueRelationSchema,
253
+ workIssueReservationSchema,
254
+ workIssueSchema,
255
+ workIssueTransitionsSchema,
256
+ workLabelIssuePageSchema,
257
+ workLabelPageSchema,
258
+ workLabelSchema,
259
+ workLegacyAliasSchema,
260
+ workLifecycleEventPageSchema,
261
+ workMembershipActivityPageSchema,
262
+ workMembershipCommandResultSchema,
263
+ workMembershipCommandSchema,
264
+ workPageSchema,
265
+ workPendingRelationActivityPageSchema,
266
+ workPendingRelationCommandResultSchema,
267
+ workPendingRelationCommandSchema,
268
+ workPendingRelationPageSchema,
269
+ workPendingRelationSchema,
270
+ workPlanPageSchema,
271
+ workPlanSchema,
272
+ workProductionTrailPageSchema,
273
+ workRelationActivityPageSchema,
274
+ workRelationCommandResultSchema,
275
+ workRelationCommandSchema,
276
+ workReservationActivityPageSchema,
277
+ workReservationCommandResultSchema,
278
+ workReservationCommandSchema,
279
+ workScopeRevisionPageSchema,
280
+ workSourceActivityPageSchema,
281
+ workSourceAliasPageSchema,
282
+ workSourceAssociationPageSchema,
283
+ workSourceCommandResultSchema,
284
+ workSourceCommandSchema,
285
+ workSourceKeyLifecycleSchema,
286
+ workSourcePrincipalPageSchema,
287
+ workSourcePrincipalSchema,
288
+ workSourceReferencePageSchema,
289
+ workSourceReferenceSchema,
290
+ workSourceSpacePageSchema,
291
+ workSourceSpaceSchema,
292
+ workThreadSchema,
293
+ workTranscriptHistorySchema,
294
+ workTranscriptLinkPageSchema,
295
+ workTranscriptLinkSchema,
296
+ workVerificationAttemptPageSchema,
297
+ workVerificationAttemptSchema,
149
298
  wrapText,
150
299
  writeIdentityOnce
151
- } from "./chunk-5TM2ARN3.js";
300
+ } from "./chunk-HCTFAAGU.js";
152
301
  import {
153
302
  generateId as generateId2
154
303
  } from "./chunk-PQIB66SG.js";
@@ -166,6 +315,7 @@ import {
166
315
  CANNOT_PASS_TEMPLATE,
167
316
  DESCRIPTION_TEMPLATE,
168
317
  GATES,
318
+ GATE_NAMES,
169
319
  LAYOUT_MARKERS,
170
320
  MERGE_SIGN_OFF,
171
321
  NO_GATE_TEXT_TEMPLATE,
@@ -176,6 +326,7 @@ import {
176
326
  WORKFLOW_MD_TEMPLATE,
177
327
  collectMcpServersWithOptions,
178
328
  createSteerQueue,
329
+ describeIgnoredGate,
179
330
  deserializeRunMemory,
180
331
  external_exports,
181
332
  findMarketplaceRepoDir,
@@ -189,6 +340,7 @@ import {
189
340
  listMarketplaceWorkflows,
190
341
  listMarketplaceWorkflowsFromRepo,
191
342
  listWorkflows,
343
+ parseGateFlags,
192
344
  parseWorkflowConfig,
193
345
  projectConfigPath,
194
346
  pullMarketplaceRepo,
@@ -200,6 +352,7 @@ import {
200
352
  resolveWorkflow,
201
353
  resolveWorkflowInstall,
202
354
  resolveWorkflowMarketplaceSource,
355
+ tightenGates,
203
356
  updateWorkflow,
204
357
  updateWorkflows,
205
358
  verifyRendered,
@@ -207,7 +360,7 @@ import {
207
360
  workflowRegistryDir,
208
361
  writeGlobalConfig,
209
362
  writeProjectConfig
210
- } from "./chunk-SACVOS6T.js";
363
+ } from "./chunk-GGAPZULS.js";
211
364
  import {
212
365
  __toESM
213
366
  } from "./chunk-G3PMV62Z.js";
@@ -215,10 +368,10 @@ import {
215
368
  // src/app/entry/cli.tsx
216
369
  import { render } from "ink";
217
370
  import meow from "meow";
218
- import fs13 from "fs";
371
+ import fs15 from "fs";
219
372
  import { createRequire } from "module";
220
- import os5 from "os";
221
- import path13 from "path";
373
+ import os6 from "os";
374
+ import path16 from "path";
222
375
  import { fileURLToPath } from "url";
223
376
 
224
377
  // src/app/shell/sessionUiState.ts
@@ -642,7 +795,7 @@ function reduceSessionUiState(state, action, ctx) {
642
795
 
643
796
  // src/app/shell/sessionUiController.ts
644
797
  function createSessionUiController(update, context) {
645
- const dispatch = (...actions) => update((state) => {
798
+ const dispatch2 = (...actions) => update((state) => {
646
799
  const ctx = context();
647
800
  return resolveSessionUiState(
648
801
  actions.reduce(
@@ -653,61 +806,61 @@ function createSessionUiController(update, context) {
653
806
  );
654
807
  });
655
808
  return {
656
- focus: (focusMode) => dispatch({ type: "set_focus_mode", focusMode }),
657
- cycleFocus: () => dispatch({ type: "cycle_focus" }),
809
+ focus: (focusMode) => dispatch2({ type: "set_focus_mode", focusMode }),
810
+ cycleFocus: () => dispatch2({ type: "cycle_focus" }),
658
811
  input: (mode) => {
659
812
  switch (mode) {
660
813
  case "cancel":
661
- dispatch({ type: "cancel_input" });
814
+ dispatch2({ type: "cancel_input" });
662
815
  break;
663
816
  case "command-open":
664
- dispatch({ type: "open_command_input" });
817
+ dispatch2({ type: "open_command_input" });
665
818
  break;
666
819
  case "search-open":
667
- dispatch({ type: "open_search_input" });
820
+ dispatch2({ type: "open_search_input" });
668
821
  break;
669
822
  case "normal-open":
670
- dispatch({ type: "open_normal_input" });
823
+ dispatch2({ type: "open_normal_input" });
671
824
  break;
672
825
  case "search":
673
826
  case "command":
674
827
  case "normal":
675
- dispatch({ type: "set_input_mode", inputMode: mode });
828
+ dispatch2({ type: "set_input_mode", inputMode: mode });
676
829
  }
677
830
  },
678
- cycleHints: () => dispatch({ type: "cycle_hints_forced" }),
679
- hideOverlay: () => dispatch({ type: "set_show_run_overlay", show: false }),
831
+ cycleHints: () => dispatch2({ type: "cycle_hints_forced" }),
832
+ hideOverlay: () => dispatch2({ type: "set_show_run_overlay", show: false }),
680
833
  search: {
681
- edit: (query) => dispatch({ type: "set_search_query", query }),
682
- submit: (query, firstMatchIndex) => dispatch({ type: "submit_search_query", query, firstMatchIndex }),
683
- step: (direction, matches) => dispatch({ type: "step_search_match", direction, matches }),
684
- clear: () => dispatch({ type: "clear_search_and_jump_tail" })
834
+ edit: (query) => dispatch2({ type: "set_search_query", query }),
835
+ submit: (query, firstMatchIndex) => dispatch2({ type: "submit_search_query", query, firstMatchIndex }),
836
+ step: (direction, matches) => dispatch2({ type: "step_search_match", direction, matches }),
837
+ clear: () => dispatch2({ type: "clear_search_and_jump_tail" })
685
838
  },
686
839
  navigate: (panel, destination) => {
687
840
  if (typeof destination === "number")
688
- dispatch(
841
+ dispatch2(
689
842
  panel === "feed" ? { type: "move_feed_cursor", delta: destination } : { type: "scroll_message_viewport", delta: destination }
690
843
  );
691
844
  else if (panel === "feed")
692
- dispatch({
845
+ dispatch2({
693
846
  type: destination === "top" ? "jump_feed_top" : "jump_feed_tail"
694
847
  });
695
848
  else
696
- dispatch({
849
+ dispatch2({
697
850
  type: destination === "top" ? "jump_message_top" : "jump_message_tail"
698
851
  });
699
852
  },
700
853
  // Wheel navigation selects its panel and moves it in the same update.
701
- scrollPanel: (panel, delta) => dispatch(
854
+ scrollPanel: (panel, delta) => dispatch2(
702
855
  { type: "set_focus_mode", focusMode: panel },
703
856
  panel === "feed" ? { type: "move_feed_cursor", delta } : { type: "scroll_message_viewport", delta }
704
857
  ),
705
- reveal: (cursor) => dispatch({ type: "reveal_feed_entry", cursor }),
706
- messageTab: (tab) => dispatch({ type: "set_message_tab", tab }),
858
+ reveal: (cursor) => dispatch2({ type: "reveal_feed_entry", cursor }),
859
+ messageTab: (tab) => dispatch2({ type: "set_message_tab", tab }),
707
860
  todo: {
708
- toggle: () => dispatch({ type: "toggle_todo_visible" }),
709
- move: (delta) => dispatch({ type: "move_todo_cursor", delta }),
710
- show: () => dispatch({ type: "set_todo_visible", visible: true })
861
+ toggle: () => dispatch2({ type: "toggle_todo_visible" }),
862
+ move: (delta) => dispatch2({ type: "move_todo_cursor", delta }),
863
+ show: () => dispatch2({ type: "set_todo_visible", visible: true })
711
864
  }
712
865
  };
713
866
  }
@@ -2811,7 +2964,7 @@ function useWorkflowSessionController(base, input) {
2811
2964
  }
2812
2965
  setIsRunning(false);
2813
2966
  }, [base, cancelCurrentRun]);
2814
- const spawn = useCallback6(
2967
+ const spawn2 = useCallback6(
2815
2968
  async (prompt, continuation, _configOverride) => {
2816
2969
  await cancelCurrentRun();
2817
2970
  const latest = input.store?.getLatestRun();
@@ -2883,7 +3036,7 @@ function useWorkflowSessionController(base, input) {
2883
3036
  }, []);
2884
3037
  return {
2885
3038
  ...base,
2886
- startTurn: spawn,
3039
+ startTurn: spawn2,
2887
3040
  isRunning,
2888
3041
  interrupt,
2889
3042
  kill,
@@ -3791,7 +3944,7 @@ function textInputReducer(state, action) {
3791
3944
  }
3792
3945
  function useTextInput(options = {}) {
3793
3946
  const { onChange, onSubmit, isActive = true } = options;
3794
- const [state, dispatch] = useReducer(textInputReducer, {
3947
+ const [state, dispatch2] = useReducer(textInputReducer, {
3795
3948
  value: "",
3796
3949
  cursorOffset: 0
3797
3950
  });
@@ -3810,7 +3963,7 @@ function useTextInput(options = {}) {
3810
3963
  onChangeRef.current?.(state.value);
3811
3964
  }, [state.value]);
3812
3965
  const setValue = useCallback7((newValue) => {
3813
- dispatch({ type: "set-value", value: newValue });
3966
+ dispatch2({ type: "set-value", value: newValue });
3814
3967
  }, []);
3815
3968
  const { stdin } = useStdin();
3816
3969
  const isForwardDeleteRef = useRef5(false);
@@ -3850,47 +4003,47 @@ function useTextInput(options = {}) {
3850
4003
  if (key.return) {
3851
4004
  const { value: val, cursorOffset: cur } = stateRef.current;
3852
4005
  if (cur > 0 && val[cur - 1] === "\\") {
3853
- dispatch({ type: "newline-escape" });
4006
+ dispatch2({ type: "newline-escape" });
3854
4007
  } else {
3855
4008
  onSubmitRef.current?.(val);
3856
4009
  }
3857
4010
  return;
3858
4011
  }
3859
4012
  if (key.leftArrow) {
3860
- dispatch(key.ctrl ? { type: "move-home" } : { type: "move-left" });
4013
+ dispatch2(key.ctrl ? { type: "move-home" } : { type: "move-left" });
3861
4014
  return;
3862
4015
  }
3863
4016
  if (key.rightArrow) {
3864
- dispatch(key.ctrl ? { type: "move-end" } : { type: "move-right" });
4017
+ dispatch2(key.ctrl ? { type: "move-end" } : { type: "move-right" });
3865
4018
  return;
3866
4019
  }
3867
4020
  if (key.home) {
3868
- dispatch({ type: "move-home" });
4021
+ dispatch2({ type: "move-home" });
3869
4022
  return;
3870
4023
  }
3871
4024
  if (key.end) {
3872
- dispatch({ type: "move-end" });
4025
+ dispatch2({ type: "move-end" });
3873
4026
  return;
3874
4027
  }
3875
4028
  if (key.ctrl) {
3876
- if (input === "a") dispatch({ type: "move-home" });
3877
- else if (input === "e") dispatch({ type: "move-end" });
3878
- else if (input === "w") dispatch({ type: "delete-word-back" });
3879
- else if (input === "u") dispatch({ type: "clear-line" });
3880
- else if (input === "d") dispatch({ type: "delete-forward" });
4029
+ if (input === "a") dispatch2({ type: "move-home" });
4030
+ else if (input === "e") dispatch2({ type: "move-end" });
4031
+ else if (input === "w") dispatch2({ type: "delete-word-back" });
4032
+ else if (input === "u") dispatch2({ type: "clear-line" });
4033
+ else if (input === "d") dispatch2({ type: "delete-forward" });
3881
4034
  return;
3882
4035
  }
3883
4036
  if (key.backspace || key.delete) {
3884
4037
  if (isForwardDeleteRef.current) {
3885
4038
  isForwardDeleteRef.current = false;
3886
- dispatch({ type: "delete-forward" });
4039
+ dispatch2({ type: "delete-forward" });
3887
4040
  } else {
3888
- dispatch({ type: "backspace" });
4041
+ dispatch2({ type: "backspace" });
3889
4042
  }
3890
4043
  return;
3891
4044
  }
3892
4045
  if (input && !key.meta) {
3893
- dispatch({ type: "insert", char: input });
4046
+ dispatch2({ type: "insert", char: input });
3894
4047
  }
3895
4048
  } finally {
3896
4049
  done();
@@ -3902,7 +4055,7 @@ function useTextInput(options = {}) {
3902
4055
  value: state.value,
3903
4056
  cursorOffset: state.cursorOffset,
3904
4057
  setValue,
3905
- dispatch
4058
+ dispatch: dispatch2
3906
4059
  };
3907
4060
  }
3908
4061
 
@@ -4007,7 +4160,7 @@ var ShellInputImpl = forwardRef(function ShellInput({
4007
4160
  commandSuggestionsEnabled,
4008
4161
  wrapSuggestionLine
4009
4162
  }, ref) {
4010
- const { value, cursorOffset, setValue, dispatch } = useTextInput({
4163
+ const { value, cursorOffset, setValue, dispatch: dispatch2 } = useTextInput({
4011
4164
  onChange,
4012
4165
  onSubmit,
4013
4166
  isActive: isInputActive
@@ -4132,7 +4285,7 @@ var ShellInputImpl = forwardRef(function ShellInput({
4132
4285
  const recalled = historyBackRef.current?.(currentValue);
4133
4286
  if (recalled !== void 0) applyProgrammaticValue(recalled);
4134
4287
  } else {
4135
- dispatch({ type: "move-up", width: inputContentWidth });
4288
+ dispatch2({ type: "move-up", width: inputContentWidth });
4136
4289
  }
4137
4290
  return;
4138
4291
  }
@@ -4146,11 +4299,11 @@ var ShellInputImpl = forwardRef(function ShellInput({
4146
4299
  const recalled = historyForwardRef.current?.();
4147
4300
  if (recalled !== void 0) applyProgrammaticValue(recalled);
4148
4301
  } else {
4149
- dispatch({ type: "move-down", width: inputContentWidth });
4302
+ dispatch2({ type: "move-down", width: inputContentWidth });
4150
4303
  }
4151
4304
  }
4152
4305
  },
4153
- [applyProgrammaticValue, dispatch, inputContentWidth, showSuggestions]
4306
+ [applyProgrammaticValue, dispatch2, inputContentWidth, showSuggestions]
4154
4307
  );
4155
4308
  useInput7(handleArrows, { isActive: isInputActive });
4156
4309
  const lines = renderInputLines(
@@ -5337,14 +5490,14 @@ function extractWebSearch(_input, response) {
5337
5490
  return { type: "text", content: extractTextContent(response), maxLines: 10 };
5338
5491
  }
5339
5492
  function extractNotebookEdit(input, _response) {
5340
- const path14 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5493
+ const path17 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5341
5494
  const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
5342
5495
  const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
5343
- if (!source) return { type: "text", content: `${mode} cell in ${path14}` };
5496
+ if (!source) return { type: "text", content: `${mode} cell in ${path17}` };
5344
5497
  return {
5345
5498
  type: "code",
5346
5499
  content: source,
5347
- language: detectLanguage(path14),
5500
+ language: detectLanguage(path17),
5348
5501
  maxLines: 10
5349
5502
  };
5350
5503
  }
@@ -5870,10 +6023,10 @@ function extractToolSubject(toolName, toolInput) {
5870
6023
  const parts = [];
5871
6024
  const pattern = str(input, "pattern");
5872
6025
  const glob = str(input, "glob");
5873
- const path14 = str(input, "path");
6026
+ const path17 = str(input, "path");
5874
6027
  if (pattern) parts.push(`"${pattern}"`);
5875
6028
  if (glob) parts.push(glob);
5876
- if (path14) parts.push(`in ${path14}`);
6029
+ if (path17) parts.push(`in ${path17}`);
5877
6030
  return parts.length > 0 ? parts.join(" ") : void 0;
5878
6031
  }
5879
6032
  case "Skill":
@@ -5906,9 +6059,13 @@ function eventLabel(event) {
5906
6059
  return event.data.scope === "subagent" ? "Subagent" : "Agent";
5907
6060
  case "user.prompt":
5908
6061
  return "User";
6062
+ case "system.wake":
6063
+ return "Wake";
5909
6064
  case "subagent.start":
5910
6065
  case "subagent.stop":
5911
6066
  return `Subagent \xB7 ${event.data.agent_type}`;
6067
+ case "subagent.task":
6068
+ return `Subagent \xB7 ${event.data.subagent_type ?? event.data.task_id}`;
5912
6069
  case "run.start":
5913
6070
  return "Run Start";
5914
6071
  case "run.end":
@@ -6018,11 +6175,17 @@ function renderDetailLines(event, width, pairedPostEvent, theme = darkTheme) {
6018
6175
  const content = renderMarkdownToLines(event.data.message, cw);
6019
6176
  return buildResult(header, content, false);
6020
6177
  }
6021
- case "user.prompt": {
6178
+ case "user.prompt":
6179
+ case "system.wake": {
6022
6180
  const header = buildCompactHeader(event, width, { theme });
6023
6181
  const content = renderMarkdownToLines(event.data.prompt, cw);
6024
6182
  return buildResult(header, content, false);
6025
6183
  }
6184
+ case "subagent.task": {
6185
+ const header = buildCompactHeader(event, width, { theme });
6186
+ const json2 = JSON.stringify(event.data, null, 2);
6187
+ return buildResult(header, highlightCode(json2, cw, "json"), true);
6188
+ }
6026
6189
  case "tool.post":
6027
6190
  case "tool.delta":
6028
6191
  case "tool.failure":
@@ -8444,8 +8607,8 @@ function formatStatsSnapshot(snapshot) {
8444
8607
  function truncateSessionId(id, maxWidth) {
8445
8608
  if (id.length <= maxWidth) return id;
8446
8609
  if (maxWidth >= 12) {
8447
- const tail = id.slice(-6);
8448
- return id.slice(0, maxWidth - 7) + "\u2026" + tail;
8610
+ const tail2 = id.slice(-6);
8611
+ return id.slice(0, maxWidth - 7) + "\u2026" + tail2;
8449
8612
  }
8450
8613
  const alphanumeric = id.replace(/[^a-zA-Z0-9]/g, "").slice(-4);
8451
8614
  return "S" + (alphanumeric || "\u2013");
@@ -9955,10 +10118,11 @@ function deriveStartupTimeoutFailure(args) {
9955
10118
 
9956
10119
  // src/app/shell/resolveAllowedTools.ts
9957
10120
  var BASELINE_ALLOWED_TOOLS = ["mcp__*"];
9958
- function resolveAllowedTools(harness, allowedTools) {
10121
+ function resolveAllowedTools(harness, allowedTools, workflowAllowedTools = []) {
9959
10122
  const extras = [
9960
10123
  ...BASELINE_ALLOWED_TOOLS,
9961
- ...resolveHarnessAdapter(harness).capabilities.extraAllowedTools
10124
+ ...resolveHarnessAdapter(harness).capabilities.extraAllowedTools,
10125
+ ...workflowAllowedTools
9962
10126
  ];
9963
10127
  const merged = [...allowedTools ?? []];
9964
10128
  for (const tool of extras) {
@@ -11780,7 +11944,8 @@ function App({
11780
11944
  workflow: runtimeState.workflow,
11781
11945
  allowedTools: resolveAllowedTools(
11782
11946
  runtimeState.harness,
11783
- runtimeState.isolation?.allowedTools
11947
+ runtimeState.isolation?.allowedTools,
11948
+ runtimeState.workflow?.allowedTools
11784
11949
  ),
11785
11950
  athenaSessionId,
11786
11951
  children: /* @__PURE__ */ jsx23(
@@ -12210,6 +12375,19 @@ async function runExecCommand(input, deps = {}) {
12210
12375
  );
12211
12376
  return RUN_EXIT_CODE.USAGE;
12212
12377
  }
12378
+ let buildGates;
12379
+ try {
12380
+ buildGates = parseGateFlags(input.flags.gates);
12381
+ } catch (error) {
12382
+ logError(`Error: ${error.message}`);
12383
+ return RUN_EXIT_CODE.USAGE;
12384
+ }
12385
+ const gates = tightenGates(input.runtimeConfig.gates, buildGates);
12386
+ for (const gate of gates.ignored) {
12387
+ logError(
12388
+ `Warning: ${describeIgnoredGate({ gate, source: "--gate", floor: "configured" })}`
12389
+ );
12390
+ }
12213
12391
  let continueResolution;
12214
12392
  try {
12215
12393
  continueResolution = resolveResumeTarget({
@@ -12262,6 +12440,16 @@ async function runExecCommand(input, deps = {}) {
12262
12440
  ephemeral: input.flags.ephemeral,
12263
12441
  timeoutMs: input.flags.timeoutMs,
12264
12442
  permissionGraceMs: graceMs ?? input.runtimeConfig.permissionGraceMs,
12443
+ gates: gates.gates,
12444
+ gateProvenance: {
12445
+ ...input.runtimeConfig.settingsProvenance?.gates,
12446
+ // A gate --gate tightened was set by this Build.
12447
+ ...Object.fromEntries(
12448
+ GATE_NAMES.filter(
12449
+ (name) => gates.gates[name] !== input.runtimeConfig.gates[name]
12450
+ ).map((name) => [name, "build"])
12451
+ )
12452
+ },
12265
12453
  ...answer !== void 0 ? { storedAnswer: localAnswerDecision(answer) } : {},
12266
12454
  // Reporting-only summary: strip to name + source layer so secret-bearing
12267
12455
  // MCP env/command/args and skill paths never reach the startup notice or
@@ -13104,7 +13292,7 @@ var thisMachine = (installationId) => ({
13104
13292
  os: `${platform()} ${release()}`.slice(0, 64),
13105
13293
  softwareVersion: runnerSoftwareVersion
13106
13294
  });
13107
- var post = (origin, path14, body) => fetch(new URL(path14, origin), {
13295
+ var post = (origin, path17, body) => fetch(new URL(path17, origin), {
13108
13296
  method: "POST",
13109
13297
  headers: { "Content-Type": "application/json" },
13110
13298
  body: JSON.stringify(body)
@@ -13123,8 +13311,8 @@ async function alreadyConnected(existing, { forTeam, ownIdentity }) {
13123
13311
  const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
13124
13312
  const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
13125
13313
  if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
13126
- const path14 = (own ?? forTeam(existing.orgId)[0])?.path;
13127
- return { status: "identity_unusable", ...runner, problem, ...path14 ? { path: path14 } : {} };
13314
+ const path17 = (own ?? forTeam(existing.orgId)[0])?.path;
13315
+ return { status: "identity_unusable", ...runner, problem, ...path17 ? { path: path17 } : {} };
13128
13316
  }
13129
13317
  async function connectWithCode({
13130
13318
  code,
@@ -13196,20 +13384,20 @@ async function saveIdentity(origin, credential, { single, forTeam }, directory)
13196
13384
  name: credential.name
13197
13385
  });
13198
13386
  const replaced = [];
13199
- for (const { path: path15 } of forTeam(credential.orgId)) {
13200
- const aside = `${path15}.replaced-${Date.now()}`;
13201
- await rename(path15, aside);
13387
+ for (const { path: path18 } of forTeam(credential.orgId)) {
13388
+ const aside = `${path18}.replaced-${Date.now()}`;
13389
+ await rename(path18, aside);
13202
13390
  replaced.push(aside);
13203
13391
  }
13204
- const path14 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13392
+ const path17 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13205
13393
  try {
13206
- await writeIdentityOnce(path14, JSON.stringify(config, null, 2) + "\n");
13394
+ await writeIdentityOnce(path17, JSON.stringify(config, null, 2) + "\n");
13207
13395
  } catch (error) {
13208
13396
  if (error.code === "EEXIST")
13209
- throw new Error(`Another Connect wrote a Runner identity for this Team at ${path14} meanwhile; it was left unchanged.`);
13397
+ throw new Error(`Another Connect wrote a Runner identity for this Team at ${path17} meanwhile; it was left unchanged.`);
13210
13398
  throw error;
13211
13399
  }
13212
- return { status: "connected", config, path: path14, replaced };
13400
+ return { status: "connected", config, path: path17, replaced };
13213
13401
  }
13214
13402
  function pathSegment(value) {
13215
13403
  const segment = encodeURIComponent(value);
@@ -13249,7 +13437,7 @@ async function refuseExisting(target) {
13249
13437
  throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
13250
13438
  }
13251
13439
  async function disconnectRunner({
13252
- identity: { path: path14, config },
13440
+ identity: { path: path17, config },
13253
13441
  serviceManager,
13254
13442
  othersRemain
13255
13443
  }) {
@@ -13282,7 +13470,7 @@ async function disconnectRunner({
13282
13470
  await restore();
13283
13471
  throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
13284
13472
  }
13285
- await forgetRunner(path14);
13473
+ await forgetRunner(path17);
13286
13474
  if (othersRemain) await restore();
13287
13475
  else await serviceManager?.remove();
13288
13476
  return result;
@@ -13435,18 +13623,18 @@ async function runRunnerCommandV2(input, options = {}) {
13435
13623
  `This machine is already connected to this Team as ${outcome.name}, but ${why}. ` + (code ? `The code was not used: connect a new Runner in its place with ${retry}. ` : `Connect a new Runner in its place with a Connect code from Settings \u2192 Runners: ${retry}. `) + `The old ${outcome.name} stays listed in Settings \u2192 Runners, offline.`
13436
13624
  );
13437
13625
  }
13438
- const { config, path: path14, replaced } = outcome;
13626
+ const { config, path: path17, replaced } = outcome;
13439
13627
  for (const aside of replaced)
13440
13628
  process.stdout.write(
13441
13629
  `The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
13442
13630
  `
13443
13631
  );
13444
13632
  process.stdout.write(
13445
- `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path14}.
13633
+ `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path17}.
13446
13634
  `
13447
13635
  );
13448
13636
  if (services) {
13449
- const service = await ensureService(services, config, path14);
13637
+ const service = await ensureService(services, config, path17);
13450
13638
  process.stdout.write(`${serviceStarted(service)}
13451
13639
  `);
13452
13640
  return 0;
@@ -13455,7 +13643,7 @@ async function runRunnerCommandV2(input, options = {}) {
13455
13643
  process.stdout.write(
13456
13644
  "No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
13457
13645
  );
13458
- await runConfiguredRunner(path14, options.signal);
13646
+ await runConfiguredRunner(path17, options.signal);
13459
13647
  return 0;
13460
13648
  }
13461
13649
  case "disconnect": {
@@ -13526,9 +13714,9 @@ async function runRunnerCommandV2(input, options = {}) {
13526
13714
  configured: true,
13527
13715
  // One entry per Team this machine serves; credentials are never printed.
13528
13716
  teams: identities.map(
13529
- ({ path: path14, config: { token: _token, ...config } }) => ({
13717
+ ({ path: path17, config: { token: _token, ...config } }) => ({
13530
13718
  ...config,
13531
- path: path14
13719
+ path: path17
13532
13720
  })
13533
13721
  ),
13534
13722
  service: service ? {
@@ -13836,7 +14024,368 @@ import { randomUUID as randomUUID2 } from "crypto";
13836
14024
  import fs7 from "fs";
13837
14025
  import path7 from "path";
13838
14026
 
14027
+ // ../../packages/client/src/work-flows.ts
14028
+ var WorkFlowError = class extends Error {
14029
+ constructor(code, details = {}) {
14030
+ super(code);
14031
+ this.code = code;
14032
+ this.details = details;
14033
+ }
14034
+ code;
14035
+ details;
14036
+ };
14037
+ var execution = (result) => {
14038
+ const o = result.outcome;
14039
+ if (o.kind === "execution" || o.kind === "assignment" || o.kind === "package" || o.kind === "effect") return o.execution;
14040
+ throw new WorkFlowError("unexpected_outcome", { kind: o.kind });
14041
+ };
14042
+ var WorkFlows = class {
14043
+ constructor(client2, teamId, commandId = () => crypto.randomUUID(), pageSize = 20) {
14044
+ this.client = client2;
14045
+ this.teamId = teamId;
14046
+ this.commandId = commandId;
14047
+ this.pageSize = pageSize;
14048
+ }
14049
+ client;
14050
+ teamId;
14051
+ commandId;
14052
+ pageSize;
14053
+ /**
14054
+ * The Team's path. A Hub without the authority route (`not_found` with HTTP 404) predates this contract and has no
14055
+ * execution switch, so it is `legacy`; every other failure is thrown.
14056
+ */
14057
+ async path() {
14058
+ try {
14059
+ return selectWorkPath(await this.client.workAuthority());
14060
+ } catch (error) {
14061
+ if (error instanceof ClientError && error.code === "not_found" && error.status === 404) return selectWorkPath(null);
14062
+ throw error;
14063
+ }
14064
+ }
14065
+ async operations() {
14066
+ return supportedOperations(await this.path());
14067
+ }
14068
+ /** Refuses (before any write) an operation the Team's path does not give canonical clients. */
14069
+ async require(operation) {
14070
+ const path17 = await this.path();
14071
+ if (path17.kind === "unsupported") throw new WorkFlowError(path17.code, { minimum: path17.minimum, current: path17.current });
14072
+ if (!supportedOperations(path17).includes(operation))
14073
+ throw new WorkFlowError(path17.execution === "legacy" && path17.work === "core" && !operation.startsWith("work.") && operation !== "history.read" ? "execution_authority_legacy" : "work_authority_legacy", { path: path17.kind, operation });
14074
+ }
14075
+ /** Names an operation the clients deliberately do not offer, with the reason; `undefined` when it is offered. */
14076
+ static unsupported(type) {
14077
+ return workClientUnsupported[type];
14078
+ }
14079
+ async send(body, commandId) {
14080
+ return this.client.coordinationCommand({ version: 1, teamId: this.teamId, commandId: commandId ?? this.commandId(), ...body });
14081
+ }
14082
+ // ── Reads: canonical Work records, one page of each collection ──
14083
+ async readIssue(id) {
14084
+ await this.require("work.read");
14085
+ const page = { limit: this.pageSize };
14086
+ const issue = await this.client.workIssue(id);
14087
+ const [occupancy, executions, attention, evidence, history, transcripts] = await Promise.all([
14088
+ this.client.workIssueReservation(id),
14089
+ this.client.workIssueExecutions(id, page),
14090
+ this.client.workIssueAttention(id, page),
14091
+ this.client.workIssueEvidence(id, page),
14092
+ this.client.workIssueActivity(id, page),
14093
+ this.client.workThreadTranscriptLinks(issue.threadId, page)
14094
+ ]);
14095
+ return { issue, occupancy, executions, attention, evidence, history, transcripts };
14096
+ }
14097
+ async readPlan(id) {
14098
+ await this.require("work.read");
14099
+ const page = { limit: this.pageSize };
14100
+ const [plan, issues, executions, history] = await Promise.all([
14101
+ this.client.workPlan(id),
14102
+ this.client.workIssues({ ...page, planId: id }),
14103
+ this.client.workPlanExecutions(id, page),
14104
+ this.client.workPlanActivity(id, page)
14105
+ ]);
14106
+ return { plan, issues, executions, history };
14107
+ }
14108
+ // ── Start, follow-up, inspect ──
14109
+ /**
14110
+ * Starts a canonical execution of an Issue (through its reservation) or a Plan. Never a legacy Build: under legacy
14111
+ * execution authority this refuses before writing. An occupied Issue answers with its owner.
14112
+ */
14113
+ async start(scope, options = {}) {
14114
+ await this.require("execution.start");
14115
+ try {
14116
+ const result = await this.send({ type: "execution.start", target: scope, payload: {
14117
+ ...options.contract ? { contract: options.contract } : {},
14118
+ ...options.limits ? { limits: options.limits } : {},
14119
+ ...options.activation ? { activation: options.activation } : {},
14120
+ ...options.expectedScopeRevision ? { expectedScopeRevision: options.expectedScopeRevision } : {}
14121
+ } }, options.commandId);
14122
+ return { kind: "started", command: commandProgress(result), replayed: result.replayed, execution: execution(result) };
14123
+ } catch (error) {
14124
+ if (error instanceof ClientError && error.code === "issue_run_live")
14125
+ return { kind: "occupied", owner: error.details.owner ?? {}, reservation: error.details.reservation ?? null };
14126
+ throw error;
14127
+ }
14128
+ }
14129
+ /** The open execution that owns a scope now: the Issue's reservation owner, or the Plan's newest open execution. */
14130
+ async owner(scope) {
14131
+ if (scope.kind === "issue") {
14132
+ const occupancy = await this.client.workIssueReservation(scope.id);
14133
+ const owner = occupancy.reservation?.owner;
14134
+ if (!occupancy.occupied || !owner) throw new WorkFlowError("not_occupied", { issueId: scope.id });
14135
+ if (owner.kind !== "execution") throw new WorkFlowError("legacy_owner", { runId: owner.runId });
14136
+ return this.client.workExecution(owner.executionId);
14137
+ }
14138
+ let after = null;
14139
+ let open;
14140
+ do {
14141
+ const page = await this.client.workPlanExecutions(scope.id, { after, limit: 100 });
14142
+ open = page.items.filter((e) => e.closure === null).at(-1) ?? open;
14143
+ after = page.nextCursor;
14144
+ } while (after);
14145
+ if (!open) throw new WorkFlowError("not_occupied", { planId: scope.id });
14146
+ return open;
14147
+ }
14148
+ /** Durable input for the coordinator of the scope's (or the named) open execution, recorded in its inbox. */
14149
+ async followUp(target, body, commandId) {
14150
+ await this.require("execution.follow_up");
14151
+ const e = target.kind === "execution" ? await this.client.workExecution(target.id) : await this.owner(target);
14152
+ const result = await this.send({
14153
+ type: "execution.message",
14154
+ target: { kind: "execution", id: e.id },
14155
+ expectedGeneration: e.generation,
14156
+ payload: { body }
14157
+ }, commandId);
14158
+ return { command: commandProgress(result), replayed: result.replayed, executionId: e.id, threadId: e.threadId };
14159
+ }
14160
+ /** Progress, outcomes, controls, questions, messages, specialists and their transcripts: the first page of each. */
14161
+ async inspect(executionId) {
14162
+ await this.require("execution.inspect");
14163
+ const page = { limit: this.pageSize };
14164
+ const e = await this.client.workExecution(executionId);
14165
+ const [outcome, controls, questions, messages, assignments, packages, reports, children, history, transcripts, recoveries] = await Promise.all([
14166
+ this.client.workExecutionOutcome(e.id),
14167
+ this.client.workExecutionControls(e.id, page),
14168
+ this.client.workExecutionQuestions(e.id, page),
14169
+ this.client.workExecutionMessages(e.id, page),
14170
+ this.client.workExecutionAssignments(e.id, page),
14171
+ this.client.workExecutionPackages(e.id, page),
14172
+ this.client.workExecutionReports(e.id, page),
14173
+ this.client.workExecutionChildren(e.id, page),
14174
+ this.client.workExecutionActivity(e.id, page),
14175
+ this.client.workThreadTranscriptLinks(e.threadId, page),
14176
+ this.client.workExecutionRecoveries(e.id, page)
14177
+ ]);
14178
+ const cancellation = await this.cancellation(e.id);
14179
+ return {
14180
+ execution: e,
14181
+ outcome,
14182
+ recoveries,
14183
+ cancellation,
14184
+ cancel: cancellation ? cancelProgress(e, cancellation) : null,
14185
+ controls: { items: controls.items.map(controlProgress), nextCursor: controls.nextCursor },
14186
+ questions,
14187
+ messages: { items: messages.items.map((m) => ({ ...m, progress: messageProgress(m) })), nextCursor: messages.nextCursor },
14188
+ assignments,
14189
+ packages,
14190
+ reports,
14191
+ children,
14192
+ history,
14193
+ transcripts
14194
+ };
14195
+ }
14196
+ /** The newest cancellation, or null when the execution was never cancelled (the read's own `not_found`). */
14197
+ async cancellation(executionId) {
14198
+ try {
14199
+ return await this.client.workExecutionCancellation(executionId);
14200
+ } catch (error) {
14201
+ if (error instanceof ClientError && error.code === "not_found") return null;
14202
+ throw error;
14203
+ }
14204
+ }
14205
+ // ── Controls: requested versus effective ──
14206
+ /**
14207
+ * Holds, releases, pauses, resumes or cancels an execution at the generation the caller observed (read now when not
14208
+ * given). The outcome is `requested` until the Hub records the control effective.
14209
+ */
14210
+ async control(executionId, control, options = {}) {
14211
+ await this.require(`execution.${control}`);
14212
+ const generation = options.expectedGeneration ?? (await this.client.workExecution(executionId)).generation;
14213
+ const reason = options.reason ?? null;
14214
+ if (options.boundaryGraceMs !== void 0 && !(Number.isInteger(options.boundaryGraceMs) && options.boundaryGraceMs >= 0 && options.boundaryGraceMs <= maxClientCancelBoundaryGraceMs))
14215
+ throw new WorkFlowError("invalid_request", { field: "boundaryGraceMs", max: maxClientCancelBoundaryGraceMs });
14216
+ const payload = control === "cancel" ? { reason: reason ?? "Cancelled by a member", ...options.boundaryGraceMs !== void 0 ? { boundaryGraceMs: options.boundaryGraceMs } : {} } : { reason };
14217
+ const result = await this.send({
14218
+ type: `execution.${control}`,
14219
+ target: { kind: "execution", id: executionId },
14220
+ expectedGeneration: generation,
14221
+ payload
14222
+ }, options.commandId);
14223
+ const e = execution(result);
14224
+ const kind = control === "unhold" ? "unhold" : control;
14225
+ const requests = await this.client.workExecutionControls(e.id, { limit: this.pageSize });
14226
+ const request = requests.items.filter((r) => r.control === kind && r.commandId === result.commandId).at(-1);
14227
+ const outcome = {
14228
+ control,
14229
+ command: commandProgress(result),
14230
+ replayed: result.replayed,
14231
+ state: executionControlState(e, control),
14232
+ execution: e,
14233
+ request: request ? controlProgress(request) : null
14234
+ };
14235
+ if (control === "cancel") {
14236
+ const cancellation = await this.cancellation(e.id);
14237
+ outcome.cancellation = cancellation;
14238
+ outcome.cancel = cancelProgress(e, cancellation);
14239
+ }
14240
+ return outcome;
14241
+ }
14242
+ /** Publishes the next version of a specialist assignment (`expectedVersion` its current one). */
14243
+ async revise(assignmentId, changes, reason, options = {}) {
14244
+ await this.require("assignment.revise");
14245
+ const a = await this.client.workAssignment(assignmentId);
14246
+ const result = await this.send({
14247
+ type: "assignment.revise",
14248
+ target: { kind: "assignment", id: assignmentId },
14249
+ expectedGeneration: a.generation,
14250
+ expectedVersion: options.expectedVersion ?? a.assignmentVersion,
14251
+ payload: { changes, reason }
14252
+ }, options.commandId);
14253
+ return result;
14254
+ }
14255
+ // ── Recovery of interrupted work (DRI-325) ──
14256
+ /** Settles an effect of uncertain outcome by inspection, naming what was inspected. Never repeats the effect. */
14257
+ async reconcile(effectId, outcome, evidence, options) {
14258
+ await this.require("effect.reconcile");
14259
+ const result = await this.send({
14260
+ type: "effect.reconcile",
14261
+ target: { kind: "effect", id: effectId },
14262
+ expectedGeneration: options.expectedGeneration,
14263
+ payload: { outcome, evidence }
14264
+ }, options.commandId);
14265
+ return { command: commandProgress(result), replayed: result.replayed, execution: execution(result) };
14266
+ }
14267
+ /** Replaces an interrupted specialist Run of an open assignment with `runId`, once the old one is proven stopped. */
14268
+ async recover(assignmentId, input) {
14269
+ await this.require("assignment.recover");
14270
+ const a = await this.client.workAssignment(assignmentId);
14271
+ const { commandId, runId, ...rest } = input;
14272
+ const result = await this.send({
14273
+ type: "assignment.recover",
14274
+ target: { kind: "assignment", id: assignmentId },
14275
+ expectedGeneration: a.generation,
14276
+ payload: { runId: runId ?? crypto.randomUUID(), ...rest }
14277
+ }, commandId);
14278
+ return result;
14279
+ }
14280
+ /**
14281
+ * A member closes a cancelling execution as cancelled (or an escalated one as failed) once every Run is stopped,
14282
+ * acknowledging exactly the effects still outstanding (the cancellation's `blockers.effectIds`); the acknowledgement
14283
+ * waives their reconciliation, never the proof of stop. `effect.reconcile` settles one by inspection instead.
14284
+ */
14285
+ async close(executionId, outcome, options = {}) {
14286
+ await this.require("execution.close");
14287
+ const generation = options.expectedGeneration ?? (await this.client.workExecution(executionId)).generation;
14288
+ const acknowledgement = options.acknowledge?.length ? { reason: options.reason ?? "Acknowledged by a member", effectIds: options.acknowledge } : null;
14289
+ const result = await this.send({
14290
+ type: "execution.close",
14291
+ target: { kind: "execution", id: executionId },
14292
+ expectedGeneration: generation,
14293
+ payload: { outcome, acknowledgement }
14294
+ }, options.commandId);
14295
+ const e = execution(result);
14296
+ const cancellation = outcome === "cancelled" ? await this.cancellation(e.id) : null;
14297
+ return {
14298
+ command: commandProgress(result),
14299
+ replayed: result.replayed,
14300
+ execution: e,
14301
+ ...outcome === "cancelled" ? { cancel: cancelProgress(e, cancellation), cancellation } : {}
14302
+ };
14303
+ }
14304
+ // ── Questions and acceptance ──
14305
+ /** A member answers any question; a coordinator's credential answers a technical one addressed to it (version from its brief). */
14306
+ async answer(questionId, answer, options = {}) {
14307
+ await this.require("question.answer");
14308
+ const version2 = options.expectedVersion ?? (options.asRun ? (await this.client.workBrief()).questions?.find((q) => q.id === questionId)?.version : (await this.client.workQuestion(questionId)).version);
14309
+ if (version2 === void 0) throw new WorkFlowError("question_not_addressed", { questionId });
14310
+ const result = await this.send({
14311
+ type: "question.answer",
14312
+ target: { kind: "question", id: questionId },
14313
+ expectedVersion: version2,
14314
+ payload: { answer }
14315
+ }, options.commandId);
14316
+ if (result.outcome.kind !== "question") throw new WorkFlowError("unexpected_outcome", { kind: result.outcome.kind });
14317
+ return { command: commandProgress(result), replayed: result.replayed, question: result.outcome.question, delivery: result.outcome.question.answer?.delivery ?? null };
14318
+ }
14319
+ /** Forwards a question one level up: the addressed coordinator's own credential only (a member's is `forbidden`). */
14320
+ async forward(questionId, note, options = {}) {
14321
+ await this.require("question.forward");
14322
+ const version2 = options.expectedVersion ?? (await this.client.workBrief()).questions?.find((q) => q.id === questionId)?.version;
14323
+ if (version2 === void 0) throw new WorkFlowError("question_not_addressed", { questionId });
14324
+ return this.send({ type: "question.forward", target: { kind: "question", id: questionId }, expectedVersion: version2, payload: { note } }, options.commandId);
14325
+ }
14326
+ /** A member accepts or rejects a root execution's package (its contract names the authority). */
14327
+ async decide(executionId, packageId, decision, reason, options = {}) {
14328
+ await this.require("execution.decide");
14329
+ const generation = options.expectedGeneration ?? (await this.client.workExecution(executionId)).generation;
14330
+ const result = await this.send({
14331
+ type: "execution.decide",
14332
+ target: { kind: "execution", id: executionId },
14333
+ expectedGeneration: generation,
14334
+ payload: { packageId, decision, reason }
14335
+ }, options.commandId);
14336
+ return { command: commandProgress(result), replayed: result.replayed, execution: execution(result), outcome: await this.client.workExecutionOutcome(executionId) };
14337
+ }
14338
+ // ── A coordinated Run's own credential ──
14339
+ async brief() {
14340
+ await this.require("run.brief");
14341
+ return this.client.workBrief();
14342
+ }
14343
+ /**
14344
+ * The specialist acknowledges a revision once its own in-flight work is reconciled. `toolUseId` is the harness tool
14345
+ * call carrying this command (the Runner's PreToolUse context): the Hub excludes it from the in-flight check.
14346
+ */
14347
+ async incorporate(assignmentId, version2, options) {
14348
+ await this.require("assignment.incorporate");
14349
+ return this.send({
14350
+ type: "assignment.incorporate",
14351
+ target: { kind: "assignment", id: assignmentId },
14352
+ expectedGeneration: options.generation,
14353
+ payload: { version: version2, ...options.toolUseId ? { toolUseId: options.toolUseId } : {} }
14354
+ }, options.commandId);
14355
+ }
14356
+ };
14357
+
13839
14358
  // ../../packages/client/src/index.ts
14359
+ function discussionQuery(page) {
14360
+ const query = new URLSearchParams(workQuery(page).slice(1));
14361
+ if (page.parentId) query.set("parentId", page.parentId);
14362
+ if (page.commentId) query.set("commentId", page.commentId);
14363
+ const text = query.toString();
14364
+ return text ? `?${text}` : "";
14365
+ }
14366
+ function relationQuery(page) {
14367
+ const query = new URLSearchParams(workQuery(page).slice(1));
14368
+ if (page.kind) query.set("kind", page.kind);
14369
+ if (page.state) query.set("state", page.state);
14370
+ const text = query.toString();
14371
+ return text ? `?${text}` : "";
14372
+ }
14373
+ function workQuery(page = {}) {
14374
+ const query = new URLSearchParams();
14375
+ if (page.after) query.set("after", page.after);
14376
+ if (page.limit !== void 0) query.set("limit", String(page.limit));
14377
+ if (page.planId) query.set("planId", page.planId);
14378
+ if (page.lifecycle) query.set("lifecycle", page.lifecycle);
14379
+ const text = query.toString();
14380
+ return text ? `?${text}` : "";
14381
+ }
14382
+ function sourceQuery(page) {
14383
+ const query = new URLSearchParams();
14384
+ for (const [key, value] of Object.entries(page))
14385
+ if (value !== void 0 && value !== null && value !== "") query.set(key, String(value));
14386
+ const text = query.toString();
14387
+ return text ? `?${text}` : "";
14388
+ }
13840
14389
  var runArtifactSchema = external_exports.object({ name: external_exports.string(), seq: external_exports.number().int(), json: external_exports.json() });
13841
14390
  var githubRepositorySchema = external_exports.object({
13842
14391
  installationId: external_exports.number().int().positive(),
@@ -13846,13 +14395,15 @@ var githubRepositorySchema = external_exports.object({
13846
14395
  defaultBranch: external_exports.string()
13847
14396
  });
13848
14397
  var ClientError = class extends Error {
13849
- constructor(code, status) {
14398
+ constructor(code, status, details = {}) {
13850
14399
  super(code);
13851
14400
  this.code = code;
13852
14401
  this.status = status;
14402
+ this.details = details;
13853
14403
  }
13854
14404
  code;
13855
14405
  status;
14406
+ details;
13856
14407
  };
13857
14408
  var DrispClient = class _DrispClient {
13858
14409
  constructor(baseUrl, orgId, token) {
@@ -13877,21 +14428,25 @@ var DrispClient = class _DrispClient {
13877
14428
  if (!match) throw new Error("invalid_hub_url");
13878
14429
  return new _DrispClient(url.origin, decodeURIComponent(match[1]), async () => token);
13879
14430
  }
13880
- async request(path14, body) {
14431
+ async request(path17, body) {
13881
14432
  const response = await fetch(
13882
- `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path14}`,
14433
+ `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path17}`,
13883
14434
  {
13884
14435
  method: body === void 0 ? "GET" : "POST",
13885
14436
  headers: {
13886
14437
  Authorization: `Bearer ${await this.token()}`,
13887
- "Content-Type": "application/json"
14438
+ "Content-Type": "application/json",
14439
+ // DRI-342: the client contract this build speaks; a Hub refuses one below its minimum (`client_upgrade_required`).
14440
+ [workClientContractHeader]: String(workClientContract)
13888
14441
  },
13889
14442
  ...body === void 0 ? {} : { body: JSON.stringify(body) }
13890
14443
  }
13891
14444
  );
13892
14445
  const result = await response.json();
13893
- if (!response.ok)
13894
- throw new ClientError(result.error ?? `HTTP ${response.status}`, response.status);
14446
+ if (!response.ok) {
14447
+ const { error, ...details } = result;
14448
+ throw new ClientError(error ?? `HTTP ${response.status}`, response.status, details);
14449
+ }
13895
14450
  return result;
13896
14451
  }
13897
14452
  submit(command) {
@@ -13915,6 +14470,668 @@ var DrispClient = class _DrispClient {
13915
14470
  startIssue(id, command) {
13916
14471
  return this.request(`/tracker/issues/${encodeURIComponent(id)}/start`, commandSchema.parse(command));
13917
14472
  }
14473
+ // ── Attention requests and execution policy (DRI-300) ──
14474
+ /**
14475
+ * Sends one attention or execution-policy command (`attention.request`, `attention.resolve`, `attention.sync_run`,
14476
+ * `execution_policy.hold`, `execution_policy.resume`, `execution_policy.set_agent_eligibility`) on the Work
14477
+ * command route, with its receipts and replay. Resolving attention never answers a Run: answer through `submit`.
14478
+ */
14479
+ async workAttentionCommand(command) {
14480
+ return workAttentionCommandResultSchema.parse(await this.request("/work/commands", workAttentionCommandSchema.parse(command)));
14481
+ }
14482
+ /** An Issue's attention requests, oldest first; `state` narrows to open (its Needs Human) or resolved ones. */
14483
+ async workIssueAttention(id, page = {}) {
14484
+ const query = workQuery(page);
14485
+ const state = page.state ? `${query ? "&" : "?"}state=${page.state}` : "";
14486
+ return workAttentionRequestPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/attention${query}${state}`));
14487
+ }
14488
+ async workAttentionRequest(id) {
14489
+ return workAttentionRequestSchema.parse(await this.request(`/work/attention-requests/${encodeURIComponent(id)}`));
14490
+ }
14491
+ async workAttentionActivity(id, page = {}) {
14492
+ return workAttentionActivityPageSchema.parse(
14493
+ await this.request(`/work/attention-requests/${encodeURIComponent(id)}/activity${workQuery(page)}`)
14494
+ );
14495
+ }
14496
+ async workExecutionPolicy(issueId) {
14497
+ return workExecutionPolicySchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/execution-policy`));
14498
+ }
14499
+ async workExecutionPolicyActivity(issueId, page = {}) {
14500
+ return workAttentionActivityPageSchema.parse(
14501
+ await this.request(`/work/issues/${encodeURIComponent(issueId)}/execution-policy/activity${workQuery(page)}`)
14502
+ );
14503
+ }
14504
+ // ── end DRI-300 ──
14505
+ /**
14506
+ * Sends one versioned Team command to the Work core (DRI-294). The caller retains `commandId` for retries: an
14507
+ * identical retry returns the original result with `replayed: true`; a changed payload is `idempotency_conflict`.
14508
+ */
14509
+ async workCommand(command) {
14510
+ return workCommandResultSchema.parse(await this.request("/work/commands", workCommandSchema.parse(command)));
14511
+ }
14512
+ async workIssue(id) {
14513
+ return workIssueSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}`));
14514
+ }
14515
+ /** Issues by number: active only unless `lifecycle` asks for `archived`, `deleted` or `all` (DRI-304). */
14516
+ async workIssues(page = {}) {
14517
+ return workIssuePageSchema.parse(await this.request(`/work/issues${workQuery(page)}`));
14518
+ }
14519
+ async workIssueActivity(id, page = {}) {
14520
+ return workActivityPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/activity${workQuery(page)}`));
14521
+ }
14522
+ async workIssueScopeRevisions(id, page = {}) {
14523
+ return workScopeRevisionPageSchema.parse(
14524
+ await this.request(`/work/issues/${encodeURIComponent(id)}/scope-revisions${workQuery(page)}`)
14525
+ );
14526
+ }
14527
+ async workPlan(id) {
14528
+ return workPlanSchema.parse(await this.request(`/work/plans/${encodeURIComponent(id)}`));
14529
+ }
14530
+ /** Plans by number (DRI-296): active only unless `lifecycle` asks for `archived`, `deleted` or `all` (DRI-304). */
14531
+ async workPlans(page = {}) {
14532
+ return workPlanPageSchema.parse(await this.request(`/work/plans${workQuery(page)}`));
14533
+ }
14534
+ async workPlanActivity(id, page = {}) {
14535
+ return workActivityPageSchema.parse(await this.request(`/work/plans/${encodeURIComponent(id)}/activity${workQuery(page)}`));
14536
+ }
14537
+ // ── Sub-Issue trees (DRI-296); `plan.archive`, `plan.unarchive`, `issue.set_plan`, `issue.set_parent` go through `workCommand` ──
14538
+ /** An Issue's direct children, by number. */
14539
+ async workIssueChildren(id, page = {}) {
14540
+ return workIssuePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/children${workQuery(page)}`));
14541
+ }
14542
+ /** An Issue's ancestors, nearest first; each page continues from the last ancestor returned. */
14543
+ async workIssueAncestors(id, page = {}) {
14544
+ return workIssuePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/ancestors${workQuery(page)}`));
14545
+ }
14546
+ // ── end DRI-296 ──
14547
+ // ── Lifecycle (DRI-304); `issue.archive`, `issue.delete`, `issue.restore`, `plan.delete`, `plan.restore` go through `workCommand` ──
14548
+ /** An Issue's lifecycle events (completion, cancellation, reopening, archival, tombstone, restoration), oldest first. */
14549
+ async workIssueLifecycle(id, page = {}) {
14550
+ return workLifecycleEventPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/lifecycle${workQuery(page)}`));
14551
+ }
14552
+ async workPlanLifecycle(id, page = {}) {
14553
+ return workLifecycleEventPageSchema.parse(await this.request(`/work/plans/${encodeURIComponent(id)}/lifecycle${workQuery(page)}`));
14554
+ }
14555
+ /** Who holds a complete source key and whether that holder is tombstoned: the contract imports consume. */
14556
+ async workSourceKeyLifecycle(key) {
14557
+ return workSourceKeyLifecycleSchema.parse(await this.request(`/work/source-references/lifecycle?${new URLSearchParams(key)}`));
14558
+ }
14559
+ // ── end DRI-304 ──
14560
+ /**
14561
+ * Sends one delivery workflow command (`workflow.create`, `workflow.publish`, `workflow.activate`; DRI-297). Team
14562
+ * admin authority; the same envelope, receipts and replay as `workCommand`.
14563
+ */
14564
+ async workflowCommand(command) {
14565
+ return workCommandResultSchema.parse(await this.request("/work/delivery-workflows/commands", workCommandSchema.parse(command)));
14566
+ }
14567
+ async deliveryWorkflows(page = {}) {
14568
+ return workDeliveryWorkflowPageSchema.parse(await this.request(`/work/delivery-workflows${workQuery(page)}`));
14569
+ }
14570
+ /** One delivery workflow; `active` names the Team's active one. */
14571
+ async deliveryWorkflow(id = "active") {
14572
+ return workDeliveryWorkflowSchema.parse(await this.request(`/work/delivery-workflows/${encodeURIComponent(id)}`));
14573
+ }
14574
+ async deliveryWorkflowRevision(id, revision) {
14575
+ return workDeliveryWorkflowRevisionSchema.parse(
14576
+ await this.request(`/work/delivery-workflows/${encodeURIComponent(id)}/revisions/${revision}`)
14577
+ );
14578
+ }
14579
+ async deliveryWorkflowRevisions(id, page = {}) {
14580
+ return workDeliveryWorkflowRevisionPageSchema.parse(
14581
+ await this.request(`/work/delivery-workflows/${encodeURIComponent(id)}/revisions${workQuery(page)}`)
14582
+ );
14583
+ }
14584
+ async deliveryMigrations(page = {}) {
14585
+ return workDeliveryMigrationPageSchema.parse(await this.request(`/work/delivery-migrations${workQuery(page)}`));
14586
+ }
14587
+ /** The moves out of an Issue's current status, each evaluated for the caller by the Hub's gate evaluator. */
14588
+ async workIssueTransitions(id) {
14589
+ return workIssueTransitionsSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/transitions`));
14590
+ }
14591
+ // ── Source identities and attribution (DRI-302) ──
14592
+ /**
14593
+ * Sends one source command (`source_space.register`, `source_principal.record`, `source_principal.map`,
14594
+ * `source_reference.record`, `observe`, `associate`, `alias_key`) on the Work command route, with its receipts.
14595
+ */
14596
+ async workSourceCommand(command) {
14597
+ return workSourceCommandResultSchema.parse(await this.request("/work/commands", workSourceCommandSchema.parse(command)));
14598
+ }
14599
+ async workSourceSpaces(page = {}) {
14600
+ return workSourceSpacePageSchema.parse(await this.request(`/work/source-spaces${workQuery(page)}`));
14601
+ }
14602
+ async workSourceSpace(id) {
14603
+ return workSourceSpaceSchema.parse(await this.request(`/work/source-spaces/${encodeURIComponent(id)}`));
14604
+ }
14605
+ async workSourcePrincipals(page = {}) {
14606
+ return workSourcePrincipalPageSchema.parse(await this.request(`/work/source-principals${sourceQuery(page)}`));
14607
+ }
14608
+ async workSourcePrincipal(id) {
14609
+ return workSourcePrincipalSchema.parse(await this.request(`/work/source-principals/${encodeURIComponent(id)}`));
14610
+ }
14611
+ /** References, optionally of one space or carrying a readable identifier now or formerly (an alias, not identity). */
14612
+ async workSourceReferences(page = {}) {
14613
+ return workSourceReferencePageSchema.parse(await this.request(`/work/source-references${sourceQuery(page)}`));
14614
+ }
14615
+ async workSourceReference(id) {
14616
+ return workSourceReferenceSchema.parse(await this.request(`/work/source-references/${encodeURIComponent(id)}`));
14617
+ }
14618
+ /** The reference holding a complete source key (canonical or alias) in this Team; `not_found` otherwise. */
14619
+ async workResolveSource(key) {
14620
+ return workSourceReferenceSchema.parse(await this.request(`/work/source-references/resolve?${new URLSearchParams(key)}`));
14621
+ }
14622
+ async workSourceAliases(id, page = {}) {
14623
+ return workSourceAliasPageSchema.parse(await this.request(`/work/source-references/${encodeURIComponent(id)}/aliases${workQuery(page)}`));
14624
+ }
14625
+ async workSourceAssociations(id, page = {}) {
14626
+ return workSourceAssociationPageSchema.parse(
14627
+ await this.request(`/work/source-references/${encodeURIComponent(id)}/associations${workQuery(page)}`)
14628
+ );
14629
+ }
14630
+ async workSourceActivity(subject, page = {}) {
14631
+ const collection = { source_space: "source-spaces", source_principal: "source-principals", source_reference: "source-references" }[subject.kind];
14632
+ return workSourceActivityPageSchema.parse(
14633
+ await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
14634
+ );
14635
+ }
14636
+ /** The source references associated with an Issue or Plan now. */
14637
+ async workOwnerSourceReferences(owner, page = {}) {
14638
+ return workSourceReferencePageSchema.parse(
14639
+ await this.request(`/work/${owner.kind}s/${encodeURIComponent(owner.id)}/source-references${workQuery(page)}`)
14640
+ );
14641
+ }
14642
+ /** Every association change to or from an Issue or Plan, including references since moved elsewhere. */
14643
+ async workOwnerSourceHistory(owner, page = {}) {
14644
+ return workSourceAssociationPageSchema.parse(
14645
+ await this.request(`/work/${owner.kind}s/${encodeURIComponent(owner.id)}/source-history${workQuery(page)}`)
14646
+ );
14647
+ }
14648
+ // ── end DRI-302 ──
14649
+ // ── Assignees and Team labels (DRI-295) ──
14650
+ /**
14651
+ * Sends one membership or label command (`issue_membership.assign`, `issue_membership.unassign`,
14652
+ * `issue_membership.add_label`, `issue_membership.remove_label` at the Issue's membership version; `label.create`,
14653
+ * `label.edit`) on the Work command route, with its receipts and replay.
14654
+ */
14655
+ async workMembershipCommand(command) {
14656
+ return workMembershipCommandResultSchema.parse(await this.request("/work/commands", workMembershipCommandSchema.parse(command)));
14657
+ }
14658
+ /** The Issue's membership version (what a membership command names) and its current counts. */
14659
+ async workIssueMembership(id) {
14660
+ return workIssueMembershipSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/membership`));
14661
+ }
14662
+ async workIssueMembershipActivity(id, page = {}) {
14663
+ return workMembershipActivityPageSchema.parse(
14664
+ await this.request(`/work/issues/${encodeURIComponent(id)}/membership/activity${workQuery(page)}`)
14665
+ );
14666
+ }
14667
+ /** An Issue's current assignees, oldest first; `state: 'all'` includes removed periods. */
14668
+ async workIssueAssignees(id, page = {}) {
14669
+ const query = workQuery(page);
14670
+ const state = page.state ? `${query ? "&" : "?"}state=${page.state}` : "";
14671
+ return workIssueAssigneePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/assignees${query}${state}`));
14672
+ }
14673
+ /** An Issue's label memberships, each with the label's current record; `state: 'all'` includes removed ones. */
14674
+ async workIssueLabels(id, page = {}) {
14675
+ const query = workQuery(page);
14676
+ const state = page.state ? `${query ? "&" : "?"}state=${page.state}` : "";
14677
+ return workIssueLabelPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/labels${query}${state}`));
14678
+ }
14679
+ async workLabels(page = {}) {
14680
+ return workLabelPageSchema.parse(await this.request(`/work/labels${workQuery(page)}`));
14681
+ }
14682
+ async workLabel(id) {
14683
+ return workLabelSchema.parse(await this.request(`/work/labels/${encodeURIComponent(id)}`));
14684
+ }
14685
+ /** Issues currently carrying the label, by number. */
14686
+ async workLabelIssues(id, page = {}) {
14687
+ return workLabelIssuePageSchema.parse(await this.request(`/work/labels/${encodeURIComponent(id)}/issues${workQuery(page)}`));
14688
+ }
14689
+ async workLabelActivity(id, page = {}) {
14690
+ return workMembershipActivityPageSchema.parse(
14691
+ await this.request(`/work/labels/${encodeURIComponent(id)}/activity${workQuery(page)}`)
14692
+ );
14693
+ }
14694
+ // ── end DRI-295 ──
14695
+ // ── Delivery candidates and evidence (DRI-298) ──
14696
+ /**
14697
+ * Sends one candidate or evidence command (`candidate.propose`, `candidate.select` at the selection's version,
14698
+ * `evidence.record`, `evidence.retract` and `evidence.accept` at the evidence's version) on the Work command route,
14699
+ * with its receipts and replay. Recorded evidence may advance the Issue automatically; the result says how.
14700
+ */
14701
+ async workEvidenceCommand(command) {
14702
+ return workEvidenceCommandResultSchema.parse(await this.request("/work/commands", workEvidenceCommandSchema.parse(command)));
14703
+ }
14704
+ async workIssueCandidates(id, page = {}) {
14705
+ return workCandidatePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/candidates${workQuery(page)}`));
14706
+ }
14707
+ /** The Issue's current-candidate selection and its version. */
14708
+ async workIssueCandidate(id) {
14709
+ return workCandidateSelectionSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/candidate`));
14710
+ }
14711
+ async workIssueCandidateActivity(id, page = {}) {
14712
+ return workEvidenceActivityPageSchema.parse(
14713
+ await this.request(`/work/issues/${encodeURIComponent(id)}/candidate/activity${workQuery(page)}`)
14714
+ );
14715
+ }
14716
+ /** An Issue's evidence, oldest first, each with its applicability now; narrow by candidate or kind. */
14717
+ async workIssueEvidence(id, page = {}) {
14718
+ const query = new URLSearchParams(workQuery(page).slice(1));
14719
+ if (page.candidateId) query.set("candidateId", page.candidateId);
14720
+ if (page.kind) query.set("kind", page.kind);
14721
+ const text = query.toString();
14722
+ return workEvidencePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/evidence${text ? `?${text}` : ""}`));
14723
+ }
14724
+ async workCandidate(id) {
14725
+ return workCandidateSchema.parse(await this.request(`/work/candidates/${encodeURIComponent(id)}`));
14726
+ }
14727
+ async workCandidateEvidence(id, page = {}) {
14728
+ return workEvidencePageSchema.parse(await this.request(`/work/candidates/${encodeURIComponent(id)}/evidence${workQuery(page)}`));
14729
+ }
14730
+ async workEvidence(id) {
14731
+ return workEvidenceSchema.parse(await this.request(`/work/evidence/${encodeURIComponent(id)}`));
14732
+ }
14733
+ /** A candidate's or an evidence record's history, oldest first. */
14734
+ async workEvidenceActivity(subject, page = {}) {
14735
+ const collection = subject.kind === "candidate" ? "candidates" : "evidence";
14736
+ return workEvidenceActivityPageSchema.parse(
14737
+ await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
14738
+ );
14739
+ }
14740
+ // ── end DRI-298 ──
14741
+ // ── Deployments, verification attempts and production confirmation (DRI-299) ──
14742
+ /**
14743
+ * Sends one deployment or verification command (`deployment.record`, `deployment.retire` at the deployment's
14744
+ * version, `verification.start`) on the Work command route. QA results and production confirmation are evidence
14745
+ * commands (`workEvidenceCommand` with `evidence.record` naming the attempt, or `production.confirm`).
14746
+ */
14747
+ async workDeploymentCommand(command) {
14748
+ return workDeploymentCommandResultSchema.parse(await this.request("/work/commands", workDeploymentCommandSchema.parse(command)));
14749
+ }
14750
+ /** An Issue's deployments in recording order; narrow by candidate, environment or lifecycle. */
14751
+ async workIssueDeployments(id, page = {}) {
14752
+ const query = new URLSearchParams(workQuery(page).slice(1));
14753
+ for (const key of ["candidateId", "environment", "lifecycle"]) if (page[key]) query.set(key, page[key]);
14754
+ const text = query.toString();
14755
+ return workDeploymentPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/deployments${text ? `?${text}` : ""}`));
14756
+ }
14757
+ /** An Issue's verification attempts in start order, each with its outcome and standing now. */
14758
+ async workIssueVerificationAttempts(id, page = {}) {
14759
+ const query = new URLSearchParams(workQuery(page).slice(1));
14760
+ if (page.deploymentId) query.set("deploymentId", page.deploymentId);
14761
+ const text = query.toString();
14762
+ return workVerificationAttemptPageSchema.parse(
14763
+ await this.request(`/work/issues/${encodeURIComponent(id)}/verification-attempts${text ? `?${text}` : ""}`)
14764
+ );
14765
+ }
14766
+ /** An Issue's staging-to-production trail: its production confirmations with the records they name. */
14767
+ async workIssueProductionTrail(id, page = {}) {
14768
+ return workProductionTrailPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/production-trail${workQuery(page)}`));
14769
+ }
14770
+ async workDeployment(id) {
14771
+ return workDeploymentSchema.parse(await this.request(`/work/deployments/${encodeURIComponent(id)}`));
14772
+ }
14773
+ async workDeploymentAttempts(id, page = {}) {
14774
+ return workVerificationAttemptPageSchema.parse(await this.request(`/work/deployments/${encodeURIComponent(id)}/attempts${workQuery(page)}`));
14775
+ }
14776
+ async workVerificationAttempt(id) {
14777
+ return workVerificationAttemptSchema.parse(await this.request(`/work/verification-attempts/${encodeURIComponent(id)}`));
14778
+ }
14779
+ /** A deployment's or a verification attempt's history, oldest first. */
14780
+ async workDeploymentActivity(subject, page = {}) {
14781
+ const collection = subject.kind === "deployment" ? "deployments" : "verification-attempts";
14782
+ return workDeploymentActivityPageSchema.parse(
14783
+ await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
14784
+ );
14785
+ }
14786
+ // ── end DRI-299 ──
14787
+ // ── Dependencies, duplicates and related Issues (DRI-301) ──
14788
+ /** Sends one relation command (`issue_relation.add`, `issue_relation.remove`) on the Work command route. */
14789
+ async workRelationCommand(command) {
14790
+ return workRelationCommandResultSchema.parse(await this.request("/work/commands", workRelationCommandSchema.parse(command)));
14791
+ }
14792
+ /**
14793
+ * An Issue's own relations, oldest first: its prerequisites, its canonical Issue and its related links (either side);
14794
+ * `kind` narrows to one, `state: 'all'` includes removed periods.
14795
+ */
14796
+ async workIssueRelations(id, page = {}) {
14797
+ return workIssueRelationPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/relations${relationQuery(page)}`));
14798
+ }
14799
+ /** The Issues blocked by this one (`dependents`) or marked duplicates of it (`duplicates`), oldest first. */
14800
+ async workIssueInverseRelations(id, kind, page = {}) {
14801
+ return workIssueRelationPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/${kind}${relationQuery(page)}`));
14802
+ }
14803
+ /** Whether every prerequisite of the Issue is satisfied now, and a page of the unsatisfied ones. */
14804
+ async workDependencyGate(id, page = {}) {
14805
+ return workDependencyGateSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/dependency-gate${workQuery(page)}`));
14806
+ }
14807
+ async workRelation(id) {
14808
+ return workIssueRelationSchema.parse(await this.request(`/work/relations/${encodeURIComponent(id)}`));
14809
+ }
14810
+ async workRelationActivity(id, page = {}) {
14811
+ return workRelationActivityPageSchema.parse(await this.request(`/work/relations/${encodeURIComponent(id)}/activity${workQuery(page)}`));
14812
+ }
14813
+ // ── end DRI-301 ──
14814
+ // ── Pending imported relations (DRI-308) ──
14815
+ /** Sends `pending_relation.dismiss` or `pending_relation.resolve` on the Work command route. */
14816
+ async workPendingRelationCommand(command) {
14817
+ return workPendingRelationCommandResultSchema.parse(await this.request("/work/commands", workPendingRelationCommandSchema.parse(command)));
14818
+ }
14819
+ /** The pending imported relations naming an Issue (`issue`) or recorded by an import batch (`import`), oldest first. */
14820
+ async workPendingRelations(of, page = {}) {
14821
+ const query = new URLSearchParams(workQuery(page).slice(1));
14822
+ if (page.disposition) query.set("disposition", page.disposition);
14823
+ const text = query.toString();
14824
+ return workPendingRelationPageSchema.parse(await this.request(
14825
+ `/work/${of.kind === "issue" ? "issues" : "imports"}/${encodeURIComponent(of.id)}/pending-relations${text ? `?${text}` : ""}`
14826
+ ));
14827
+ }
14828
+ async workPendingRelation(id) {
14829
+ return workPendingRelationSchema.parse(await this.request(`/work/pending-relations/${encodeURIComponent(id)}`));
14830
+ }
14831
+ async workPendingRelationActivity(id, page = {}) {
14832
+ return workPendingRelationActivityPageSchema.parse(await this.request(`/work/pending-relations/${encodeURIComponent(id)}/activity${workQuery(page)}`));
14833
+ }
14834
+ // ── end DRI-308 ──
14835
+ // ── Execution reservations and canonical admission (DRI-310) ──
14836
+ /**
14837
+ * Sends one reservation command (`legacy_alias.record`, Team admin) on the Work command route, with its receipts
14838
+ * and replay. DRI-314 retired the provisional lifecycle commands: Coordination's own (`coordinationCommand`) start,
14839
+ * bind and close executions.
14840
+ */
14841
+ async workReservationCommand(command) {
14842
+ return workReservationCommandResultSchema.parse(await this.request("/work/commands", workReservationCommandSchema.parse(command)));
14843
+ }
14844
+ /** An Issue's occupancy: its ownership generation and held reservation, if any. */
14845
+ async workIssueReservation(issueId) {
14846
+ return workIssueReservationSchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/reservation`));
14847
+ }
14848
+ /** Every reservation an Issue has had, oldest first. */
14849
+ async workIssueReservations(issueId, page = {}) {
14850
+ return workExecutionReservationPageSchema.parse(
14851
+ await this.request(`/work/issues/${encodeURIComponent(issueId)}/reservations${workQuery(page)}`)
14852
+ );
14853
+ }
14854
+ async workReservation(id) {
14855
+ return workExecutionReservationSchema.parse(await this.request(`/work/reservations/${encodeURIComponent(id)}`));
14856
+ }
14857
+ async workReservationBindings(id, page = {}) {
14858
+ return workExecutionBindingPageSchema.parse(await this.request(`/work/reservations/${encodeURIComponent(id)}/bindings${workQuery(page)}`));
14859
+ }
14860
+ async workReservationActivity(id, page = {}) {
14861
+ return workReservationActivityPageSchema.parse(await this.request(`/work/reservations/${encodeURIComponent(id)}/activity${workQuery(page)}`));
14862
+ }
14863
+ /** A Run's one authoritative binding; `not_found` for a Run admitted outside any canonical Issue. */
14864
+ async workRunBinding(runId) {
14865
+ return workExecutionBindingSchema.parse(await this.request(`/work/runs/${encodeURIComponent(runId)}/binding`));
14866
+ }
14867
+ /** Run control's record of a dispatched coordinated Run: grants, fencing refusal, receipts and physical stop (DRI-340). */
14868
+ async workRunControl(runId) {
14869
+ return runControlRecordSchema.parse(await this.request(`/work/runs/${encodeURIComponent(runId)}/control`));
14870
+ }
14871
+ /** `run_control.acknowledge_lost` (Team admin): a Run whose stop is unproven is accepted as no longer running (DRI-340). */
14872
+ async runControlCommand(command) {
14873
+ return runControlCommandResultSchema.parse(await this.request("/work/commands", runControlCommandSchema.parse(command)));
14874
+ }
14875
+ /** The Issue an exact legacy reference resolves to; anything else is `not_found`. */
14876
+ async workResolveLegacyAlias(alias) {
14877
+ return workLegacyAliasSchema.parse(await this.request(`/work/legacy-aliases/resolve?${new URLSearchParams({ alias })}`));
14878
+ }
14879
+ // ── end DRI-310 ──
14880
+ // ── Coordination (DRI-314) ──
14881
+ /**
14882
+ * Sends one Coordination command on the Work command route, with its receipts and replay: a member's
14883
+ * `execution.start`, `.control`, `.cancel`, `.close`, `.decide`, `.amend`, `.message`, or the provisional synthetic
14884
+ * driver's `coordinator.*` and `run.fact` (Team admin). A stale generation is `stale_generation`.
14885
+ */
14886
+ async coordinationCommand(command) {
14887
+ return coordinationCommandResultSchema.parse(await this.request("/work/commands", coordinationCommandSchema.parse(command)));
14888
+ }
14889
+ async workExecution(id) {
14890
+ return executionSchema.parse(await this.request(`/work/executions/${encodeURIComponent(id)}`));
14891
+ }
14892
+ /** Every execution of an Issue, oldest first: its predecessors and the current one. */
14893
+ async workIssueExecutions(issueId, page = {}) {
14894
+ return executionPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/executions${workQuery(page)}`));
14895
+ }
14896
+ async workCoordinator(id) {
14897
+ return coordinatorSchema.parse(await this.request(`/work/coordinators/${encodeURIComponent(id)}`));
14898
+ }
14899
+ /** Measured spend across every execution of an Issue. */
14900
+ async workIssueSpend(issueId) {
14901
+ return coordinationSpendSchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/spend`));
14902
+ }
14903
+ // ── Coordinator activation and specialist assignment (DRI-317) ──
14904
+ /**
14905
+ * With a coordinated Run's scoped credential (its grant's token as the bearer): what that Run may read about its own
14906
+ * work. The coordinator sees its execution's assignments, packages and reports; a specialist its own assignment.
14907
+ * A member's token is `forbidden`. Commands go through `coordinationCommand` with the same credential.
14908
+ */
14909
+ async workBrief() {
14910
+ return coordinationBriefSchema.parse(await this.request("/work/brief"));
14911
+ }
14912
+ /** An execution's coordinator activation: its Run, binding and coordinator Workflow. */
14913
+ async workExecutionActivation(executionId) {
14914
+ return executionActivationSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/activation`));
14915
+ }
14916
+ /** The specialist reports exposed on an execution's canonical Thread, oldest first. */
14917
+ async workExecutionReports(executionId, page = {}) {
14918
+ return workPageSchema(assignmentReportSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/reports${workQuery(page)}`));
14919
+ }
14920
+ // ── end DRI-317 ──
14921
+ // ── Coordinator wake-up (DRI-318) ──
14922
+ /** An execution's coordinator inbox, every event oldest first, with each event's acknowledgement. */
14923
+ async workExecutionInbox(executionId, page = {}) {
14924
+ return workPageSchema(coordinationInboxEventSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/inbox${workQuery(page)}`));
14925
+ }
14926
+ /** An execution's InboxCursor: acknowledged through, newest event, wake-ups, the activation asleep. */
14927
+ async workExecutionCursor(executionId) {
14928
+ return inboxCursorSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/cursor`));
14929
+ }
14930
+ // ── end DRI-318 ──
14931
+ // ── Recovery of interrupted work (DRI-325) ──
14932
+ /** An execution's recovery episodes, oldest first, each with its append-only steps. */
14933
+ async workExecutionRecoveries(executionId, page = {}) {
14934
+ return workPageSchema(recoveryEpisodeSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/recoveries${workQuery(page)}`));
14935
+ }
14936
+ // ── end DRI-325 ──
14937
+ // ── Hold, pause and revise (DRI-323) ──
14938
+ /** An execution's control requests, oldest first: requested versus effective, each specialist Run's part, a hold's wait. */
14939
+ async workExecutionControls(executionId, page = {}) {
14940
+ return workPageSchema(controlRequestSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/controls${workQuery(page)}`));
14941
+ }
14942
+ /** An assignment's published revisions, oldest first, each with its message and incorporation. */
14943
+ async workAssignmentRevisions(assignmentId, page = {}) {
14944
+ return workPageSchema(assignmentRevisionSchema).parse(await this.request(`/work/assignments/${encodeURIComponent(assignmentId)}/revisions${workQuery(page)}`));
14945
+ }
14946
+ // ── end DRI-323 ──
14947
+ // ── DRI-324: an execution's newest cancellation ──
14948
+ /** Each admitted Run's tool-boundary hold and stop, every effect issued (finished, unknown), what remains and what blocks closure. */
14949
+ async workExecutionCancellation(executionId) {
14950
+ return executionCancellationSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/cancellation`));
14951
+ }
14952
+ // ── Execution workflows (DRI-319): the Team library and a pinned execution's steps ──
14953
+ /** Publishes an execution workflow revision (Team admin) on its own route, with the Work receipts and replay. */
14954
+ async executionWorkflowCommand(command) {
14955
+ return coordinationCommandResultSchema.parse(await this.request("/work/execution-workflows/commands", coordinationCommandSchema.parse(command)));
14956
+ }
14957
+ async executionWorkflows(page = {}) {
14958
+ return executionWorkflowPageSchema.parse(await this.request(`/work/execution-workflows${workQuery(page)}`));
14959
+ }
14960
+ async executionWorkflow(id) {
14961
+ return executionWorkflowSchema.parse(await this.request(`/work/execution-workflows/${encodeURIComponent(id)}`));
14962
+ }
14963
+ async executionWorkflowRevisions(id, page = {}) {
14964
+ return executionWorkflowRevisionPageSchema.parse(await this.request(`/work/execution-workflows/${encodeURIComponent(id)}/revisions${workQuery(page)}`));
14965
+ }
14966
+ async executionWorkflowRevision(id, revision) {
14967
+ return executionWorkflowRevisionSchema.parse(await this.request(`/work/execution-workflows/${encodeURIComponent(id)}/revisions/${revision}`));
14968
+ }
14969
+ /** The pinned workflow revision of an execution and every step's state. */
14970
+ async workExecutionSteps(executionId) {
14971
+ return executionStepsSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/steps`));
14972
+ }
14973
+ // ── end DRI-319 ──
14974
+ // ── Review and repair loop (DRI-320) ──
14975
+ /** An execution's review rounds, oldest first: each verdict, the candidate it evaluated, the findings. */
14976
+ async workExecutionReviews(executionId, page = {}) {
14977
+ return reviewRoundPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/reviews${workQuery(page)}`));
14978
+ }
14979
+ // ── end DRI-320 ──
14980
+ // ── Messages and receipts (DRI-322) ──
14981
+ /**
14982
+ * An execution's messages, oldest first: a specialist's with its target and every receipt (received, delivered,
14983
+ * incorporated, rejected, unconfirmed), never relabelled. Send with `coordinationCommand` (`message.send`).
14984
+ */
14985
+ async workExecutionMessages(executionId, page = {}) {
14986
+ return workPageSchema(executionMessageSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/messages${workQuery(page)}`));
14987
+ }
14988
+ // ── end DRI-322 ──
14989
+ // ── DRI-329: an execution's place in the delegation hierarchy ──
14990
+ /** Depth below its root, remaining delegation depth, subtree spend and a page of child executions with state and spend. */
14991
+ async workExecutionHierarchy(executionId, page = {}) {
14992
+ return executionHierarchySchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/hierarchy${workQuery(page)}`));
14993
+ }
14994
+ /** The child executions an execution's assignments control, oldest first. */
14995
+ async workExecutionChildren(executionId, page = {}) {
14996
+ return executionPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/children${workQuery(page)}`));
14997
+ }
14998
+ // ── end DRI-329 ──
14999
+ // ── QA steps and repair (DRI-321) ──
15000
+ /** An execution's QA rounds, oldest first: attempt, deployment, candidate, outcome, reproduction. */
15001
+ async workExecutionQa(executionId, page = {}) {
15002
+ return qaRoundPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/qa${workQuery(page)}`));
15003
+ }
15004
+ // ── end DRI-321 ──
15005
+ // ── Questions routed to their authority (DRI-330) ──
15006
+ /** Every question visible on an execution (raised in it or routed through it), in the order it reached it. Answer with `coordinationCommand`. */
15007
+ async workExecutionQuestions(executionId, page = {}) {
15008
+ return executionQuestionPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/questions${workQuery(page)}`));
15009
+ }
15010
+ /** One question: its authority, addressee, route, answer and its delivery, resolution. */
15011
+ async workQuestion(questionId) {
15012
+ return executionQuestionSchema.parse(await this.request(`/work/questions/${encodeURIComponent(questionId)}`));
15013
+ }
15014
+ // ── end DRI-330 ──
15015
+ // ── Child acceptance and delivery readiness (DRI-335) ──
15016
+ /** An execution's outcomes as distinct facts: finished, awaiting acceptance, accepted, closure, and Work's delivery readiness. */
15017
+ async workExecutionOutcome(executionId) {
15018
+ return executionOutcomeSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/outcome`));
15019
+ }
15020
+ /** An execution's completion packages, oldest first, each with the versioned assignment contract it answers. */
15021
+ async workExecutionPackages(executionId, page = {}) {
15022
+ return workPageSchema(completionPackageSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/packages${workQuery(page)}`));
15023
+ }
15024
+ // ── end DRI-335 ──
15025
+ // ── Client integration (DRI-342) ──
15026
+ /** Every execution of a Plan, oldest first. */
15027
+ async workPlanExecutions(planId, page = {}) {
15028
+ return executionPageSchema.parse(await this.request(`/work/plans/${encodeURIComponent(planId)}/executions${workQuery(page)}`));
15029
+ }
15030
+ /** One assignment. */
15031
+ async workAssignment(id) {
15032
+ return assignmentSchema.parse(await this.request(`/work/assignments/${encodeURIComponent(id)}`));
15033
+ }
15034
+ /** A page of an execution's assignments, oldest first. */
15035
+ async workExecutionAssignments(executionId, page = {}) {
15036
+ return assignmentPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/assignments${workQuery(page)}`));
15037
+ }
15038
+ /** A page of an execution's Activity, oldest first: its durable history. */
15039
+ async workExecutionActivity(executionId, page = {}) {
15040
+ return coordinationActivityPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/activity${workQuery(page)}`));
15041
+ }
15042
+ /**
15043
+ * The Team's authority record: who owns work-domain writes and starts, and the client contract the Hub serves.
15044
+ * Readable whether or not the Work core is on. A Hub that predates it answers `not_found`; see `WorkFlows.path`.
15045
+ */
15046
+ async workAuthority() {
15047
+ return workAuthoritySchema.parse(await this.request("/work/authority"));
15048
+ }
15049
+ // ── end DRI-342 ──
15050
+ // ── end DRI-314 ──
15051
+ // ── Discussion on Work Threads (DRI-303) ──
15052
+ /**
15053
+ * Sends one discussion command (`comment.add`, `comment.edit`, `comment.delete`, `attachment.link`,
15054
+ * `attachment.check`, `attachment.remove`, `transcript.link`) on the Work command route, with its receipts and replay.
15055
+ */
15056
+ async workDiscussionCommand(command) {
15057
+ return workDiscussionCommandResultSchema.parse(await this.request("/work/commands", workDiscussionCommandSchema.parse(command)));
15058
+ }
15059
+ /** The Thread's binding: the one Issue or Plan it belongs to. */
15060
+ async workThread(id) {
15061
+ return workThreadSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}`));
15062
+ }
15063
+ /** A Thread's comments, oldest first, deleted ones as tombstones; `parentId` pages the replies of one comment. */
15064
+ async workThreadComments(id, page = {}) {
15065
+ return workCommentPageSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}/comments${discussionQuery(page)}`));
15066
+ }
15067
+ /** A Thread's link-only attachments, oldest first; `commentId` narrows to one comment's. */
15068
+ async workThreadAttachments(id, page = {}) {
15069
+ return workAttachmentPageSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}/attachments${discussionQuery(page)}`));
15070
+ }
15071
+ /** A Thread's transcript links, each `visible` or `restricted` by the caller's access to its Run. */
15072
+ async workThreadTranscriptLinks(id, page = {}) {
15073
+ return workTranscriptLinkPageSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}/transcript-links${workQuery(page)}`));
15074
+ }
15075
+ async workComment(id) {
15076
+ return workCommentSchema.parse(await this.request(`/work/comments/${encodeURIComponent(id)}`));
15077
+ }
15078
+ async workCommentRevisions(id, page = {}) {
15079
+ return workCommentRevisionPageSchema.parse(await this.request(`/work/comments/${encodeURIComponent(id)}/revisions${workQuery(page)}`));
15080
+ }
15081
+ async workAttachment(id) {
15082
+ return workAttachmentSchema.parse(await this.request(`/work/attachments/${encodeURIComponent(id)}`));
15083
+ }
15084
+ async workAttachmentChecks(id, page = {}) {
15085
+ return workAttachmentCheckPageSchema.parse(await this.request(`/work/attachments/${encodeURIComponent(id)}/checks${workQuery(page)}`));
15086
+ }
15087
+ async workTranscriptLink(id) {
15088
+ return workTranscriptLinkSchema.parse(await this.request(`/work/transcript-links/${encodeURIComponent(id)}`));
15089
+ }
15090
+ /** The linked range of the Run's history, under the Run's own visibility; `after` is the last sequence read. */
15091
+ async workTranscriptHistory(id, after) {
15092
+ return workTranscriptHistorySchema.parse(await this.request(
15093
+ `/work/transcript-links/${encodeURIComponent(id)}/history${after === void 0 ? "" : `?after=${after}`}`
15094
+ ));
15095
+ }
15096
+ async workDiscussionActivity(subject, page = {}) {
15097
+ const collection = { comment: "comments", attachment: "attachments", transcript_link: "transcript-links" }[subject.kind];
15098
+ return workDiscussionActivityPageSchema.parse(
15099
+ await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
15100
+ );
15101
+ }
15102
+ // ── end DRI-303 ──
15103
+ // ── Normalized import preview (DRI-305) ──
15104
+ /** Previews one normalized import (`import.preview`, Team admin) on its own route, with the Work receipts. */
15105
+ async workImportCommand(command) {
15106
+ return workImportCommandResultSchema.parse(await this.request("/work/imports/commands", workImportCommandSchema.parse(command)));
15107
+ }
15108
+ async workImports(page = {}) {
15109
+ return workImportBatchPageSchema.parse(await this.request(`/work/imports${workQuery(page)}`));
15110
+ }
15111
+ async workImport(id) {
15112
+ return workImportBatchSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}`));
15113
+ }
15114
+ /** A batch's previewed items, optionally of one collection or one disposition. */
15115
+ async workImportItems(id, page = {}) {
15116
+ return workImportItemPageSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/items${sourceQuery(page)}`));
15117
+ }
15118
+ /** The frozen envelope and mapping, exactly as previewed, with their digests. */
15119
+ async workImportSnapshot(id) {
15120
+ return workImportSnapshotSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/snapshot`));
15121
+ }
15122
+ // ── end DRI-305 ──
15123
+ // ── Import commit (DRI-306): `import.commit` goes through `workImportCommand` ──
15124
+ /** What the commit did with a batch, item by item and part by part, optionally filtered. */
15125
+ async workImportOutcomes(id, page = {}) {
15126
+ return workImportOutcomePageSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/outcomes${sourceQuery(page)}`));
15127
+ }
15128
+ // ── end DRI-306 ──
15129
+ // ── Import completion (DRI-307): `import.complete` goes through `workImportCommand` ──
15130
+ /** A committed batch's discussion components and their current states, optionally of one collection or state. */
15131
+ async workImportComponents(id, page = {}) {
15132
+ return workImportComponentPageSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/components${sourceQuery(page)}`));
15133
+ }
15134
+ // ── end DRI-307 ──
13918
15135
  /** The recent Runs; with a filter, every Run carrying that key or every child of that Run, newest first. */
13919
15136
  async runs(filter) {
13920
15137
  const query = !filter ? "" : "key" in filter ? `?key=${encodeURIComponent(filter.key)}` : `?parent=${encodeURIComponent(filter.parent)}`;
@@ -15607,51 +16824,1383 @@ ${USAGE3}
15607
16824
  }
15608
16825
  }
15609
16826
 
15610
- // src/app/entry/trackerCommand.ts
15611
- import { readFile as readFile2 } from "fs/promises";
15612
- async function runTrackerCommand(args, env2 = process.env) {
15613
- const usage2 = "Usage: drisp tracker get <issue-id> | apply <operation.json>\nOperations: issue.revision, issue.comment, issue.attention, issue.evidence. Read the Issue first for its current version. Evidence includes revision, reference, kind and runId; staging/QA also require deploymentId. Production release confirmation is a human action.";
16827
+ // src/app/entry/driveCommand.ts
16828
+ import path14 from "path";
16829
+
16830
+ // src/core/drive/merge.ts
16831
+ var DEFAULT_TIMEOUT_MS = 9 * 6e4;
16832
+ var DEFAULT_POLL_MS = 15e3;
16833
+ var DEFAULT_NO_CHECKS_GRACE_MS = 45e3;
16834
+ var DEFAULT_SETTLE_MS = 2e4;
16835
+ var DEFAULT_LOCK_WAIT_MS = 6e4;
16836
+ var MAX_ROUNDS = 2;
16837
+ var DEPENDENCY_FILES = [
16838
+ "package.json",
16839
+ "package-lock.json",
16840
+ "npm-shrinkwrap.json",
16841
+ "pnpm-lock.yaml",
16842
+ "yarn.lock",
16843
+ "bun.lock",
16844
+ "bun.lockb"
16845
+ ];
16846
+ var OUTPUT_TAIL = 4e3;
16847
+ var SCRATCH_PREFIX = "drisp-drive-merge-";
16848
+ var MERGED_REFS = "refs/drisp/merged";
16849
+ var TESTED_REFS = "refs/drisp/tested";
16850
+ function tail(text) {
16851
+ const trimmed = text.trim();
16852
+ return trimmed.length > OUTPUT_TAIL ? `\u2026${trimmed.slice(-OUTPUT_TAIL)}` : trimmed;
16853
+ }
16854
+ function both(result) {
16855
+ return tail([result.stdout, result.stderr].filter(Boolean).join("\n"));
16856
+ }
16857
+ var Stop = class extends Error {
16858
+ constructor(result) {
16859
+ super(result.detail);
16860
+ this.result = result;
16861
+ }
16862
+ result;
16863
+ };
16864
+ function stop(outcome, detail, output) {
16865
+ throw new Stop({ outcome, detail, ...output ? { output } : {} });
16866
+ }
16867
+ function refuse(detail, output) {
16868
+ return stop("refused", detail, output);
16869
+ }
16870
+ function baseKeptMoving(defaultBranch) {
16871
+ return `base kept moving: ${defaultBranch} moved after each of ${MAX_ROUNDS} rebase-and-check rounds; run the command again`;
16872
+ }
16873
+ async function driveMerge(input, deps) {
15614
16874
  try {
15615
- if (!env2.DRISP_HUB_URL || !env2.DRISP_RUN_TOKEN)
15616
- throw new Error(
15617
- "This command requires a native Issue Run with DRISP_HUB_URL and DRISP_RUN_TOKEN."
15618
- );
15619
- const client2 = DrispClient.delegated(
15620
- env2.DRISP_HUB_URL,
15621
- env2.DRISP_RUN_TOKEN
15622
- );
15623
- const [action, argument, ...extra] = args;
15624
- if (!argument || extra.length) throw new Error(usage2);
15625
- if (action === "get")
15626
- process.stdout.write(
15627
- JSON.stringify(await client2.trackerIssue(argument)) + "\n"
15628
- );
15629
- else if (action === "apply") {
15630
- const operation = trackerOperationSchema.parse(
15631
- JSON.parse(await readFile2(argument, "utf8"))
15632
- );
15633
- process.stdout.write(
15634
- JSON.stringify(await client2.trackerOperation(operation)) + "\n"
15635
- );
15636
- } else throw new Error(usage2);
15637
- return 0;
16875
+ return await new DriveMerge(input, deps).run();
15638
16876
  } catch (error) {
15639
- process.stderr.write(
15640
- (error instanceof Error ? error.message : String(error)) + "\n"
15641
- );
15642
- return 2;
16877
+ if (error instanceof Stop) return error.result;
16878
+ throw error;
15643
16879
  }
15644
16880
  }
15645
-
15646
- // src/app/entry/doctorCommand.ts
15647
- import process7 from "process";
15648
-
15649
- // src/harnesses/claude/system/doctorEnvironment.ts
15650
- import { execFileSync as execFileSync3 } from "child_process";
15651
- import * as fs12 from "fs";
15652
- import * as os4 from "os";
15653
- import * as path12 from "path";
15654
- var MANAGED_POLICY_KEYS = [
16881
+ function judge(checks, scope) {
16882
+ const { failing, unfinished } = judgeChecks(checks);
16883
+ if (failing.length > 0)
16884
+ return {
16885
+ kind: "failed",
16886
+ detail: `${scope} failed: ${failing.map((check) => check.name).join(", ")}`
16887
+ };
16888
+ const cancelled = unfinished.filter((check) => check.bucket === "cancel");
16889
+ if (cancelled.length > 0)
16890
+ return {
16891
+ kind: "failed",
16892
+ detail: `${scope} cancelled: ${cancelled.map((check) => check.name).join(", ")}`
16893
+ };
16894
+ if (unfinished.length > 0)
16895
+ return {
16896
+ kind: "waiting",
16897
+ detail: `${scope} not finished: ${describeChecks(unfinished)}`
16898
+ };
16899
+ return {
16900
+ kind: "passed",
16901
+ scope,
16902
+ detail: `${scope} passed (${checks.map((check) => check.name).join(", ")})`
16903
+ };
16904
+ }
16905
+ var DriveMerge = class {
16906
+ constructor(input, deps) {
16907
+ this.input = input;
16908
+ this.deps = deps;
16909
+ this.timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
16910
+ this.pollMs = input.pollMs ?? DEFAULT_POLL_MS;
16911
+ this.graceMs = input.noChecksGraceMs ?? DEFAULT_NO_CHECKS_GRACE_MS;
16912
+ this.settleMs = input.settleMs ?? DEFAULT_SETTLE_MS;
16913
+ }
16914
+ input;
16915
+ deps;
16916
+ timeoutMs;
16917
+ /** Set when the command starts; every stage takes what is left of it. */
16918
+ deadline = 0;
16919
+ pollMs;
16920
+ graceMs;
16921
+ settleMs;
16922
+ left() {
16923
+ return this.deadline - this.deps.now();
16924
+ }
16925
+ timedOut(what) {
16926
+ return stop(
16927
+ "checks_timeout",
16928
+ `${what}: the command's ${Math.round(this.timeoutMs / 1e3)} s deadline passed; run the command again`
16929
+ );
16930
+ }
16931
+ git(cwd, ...args) {
16932
+ return this.deps.git(cwd, args);
16933
+ }
16934
+ /** stdout of a git call that must succeed, else `refused` naming it. */
16935
+ must(cwd, what, ...args) {
16936
+ const result = this.git(cwd, ...args);
16937
+ if (result.code !== 0) refuse(`${what} failed`, both(result));
16938
+ return result.stdout.trim();
16939
+ }
16940
+ sha(cwd, ref) {
16941
+ const result = this.git(cwd, "rev-parse", "--verify", "--quiet", ref);
16942
+ return result.code === 0 ? result.stdout.trim() : null;
16943
+ }
16944
+ isAncestor(cwd, commit, of) {
16945
+ return this.git(cwd, "merge-base", "--is-ancestor", commit, of).code === 0;
16946
+ }
16947
+ checkedOut(cwd) {
16948
+ return this.git(
16949
+ cwd,
16950
+ "symbolic-ref",
16951
+ "--quiet",
16952
+ "--short",
16953
+ "HEAD"
16954
+ ).stdout.trim();
16955
+ }
16956
+ rebaseInProgress(worktreeDir) {
16957
+ return ["rebase-merge", "rebase-apply"].some((state) => {
16958
+ const at = this.git(
16959
+ worktreeDir,
16960
+ "rev-parse",
16961
+ "--path-format=absolute",
16962
+ "--git-path",
16963
+ state
16964
+ ).stdout.trim();
16965
+ return at !== "" && this.deps.exists(at);
16966
+ });
16967
+ }
16968
+ async run() {
16969
+ const { worktree, item } = this.input;
16970
+ this.deadline = this.deps.now() + this.timeoutMs;
16971
+ const top = this.git(worktree, "rev-parse", "--show-toplevel");
16972
+ if (top.code !== 0) refuse(`${worktree} is not a git worktree`, both(top));
16973
+ const worktreeDir = top.stdout.trim();
16974
+ if (this.git(worktreeDir, "check-ref-format", `${MERGED_REFS}/${item}`).code !== 0)
16975
+ refuse(`"${item}" cannot name a git ref; use the item's identifier`);
16976
+ const commonDir = this.must(
16977
+ worktreeDir,
16978
+ "reading the git common dir",
16979
+ "rev-parse",
16980
+ "--path-format=absolute",
16981
+ "--git-common-dir"
16982
+ );
16983
+ const release2 = await this.deps.lock(
16984
+ commonDir,
16985
+ Math.min(this.input.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS, this.left())
16986
+ );
16987
+ if (!release2)
16988
+ refuse(
16989
+ "another merge in progress: another drisp drive merge holds the lock in this repository; run the command again once it finishes"
16990
+ );
16991
+ try {
16992
+ this.removeStaleScratch(worktreeDir);
16993
+ return await this.locked(worktreeDir);
16994
+ } finally {
16995
+ release2();
16996
+ }
16997
+ }
16998
+ /** Temporary checkouts a killed run left behind (they live under `drisp-drive-merge-*`). */
16999
+ removeStaleScratch(worktreeDir) {
17000
+ const stale = this.git(worktreeDir, "worktree", "list", "--porcelain").stdout.split("\n").filter(
17001
+ (line) => line.startsWith("worktree ") && line.includes(SCRATCH_PREFIX)
17002
+ ).map((line) => line.slice("worktree ".length));
17003
+ for (const dir of stale)
17004
+ this.git(worktreeDir, "worktree", "remove", "--force", dir);
17005
+ this.git(worktreeDir, "worktree", "prune");
17006
+ }
17007
+ async locked(worktreeDir) {
17008
+ const { item } = this.input;
17009
+ if (this.rebaseInProgress(worktreeDir))
17010
+ refuse(
17011
+ "a rebase is in progress in the worktree; run `git rebase --abort` there (or finish it), then merge again"
17012
+ );
17013
+ const mainCheckout = this.mainCheckout(worktreeDir);
17014
+ const current = this.checkedOut(worktreeDir);
17015
+ const branch = this.input.branch ?? current;
17016
+ if (!branch)
17017
+ refuse("the worktree has a detached HEAD; check out the item branch");
17018
+ if (current !== branch)
17019
+ refuse(
17020
+ `the worktree has ${current || "a detached HEAD"} checked out, not ${branch}`
17021
+ );
17022
+ const originUrl = this.git(
17023
+ mainCheckout,
17024
+ "config",
17025
+ "--get",
17026
+ "remote.origin.url"
17027
+ );
17028
+ const hasOrigin = originUrl.code === 0;
17029
+ const defaultBranch = this.defaultBranch(mainCheckout, hasOrigin);
17030
+ if (branch === defaultBranch)
17031
+ refuse(`${branch} is the default branch; name the item's branch`);
17032
+ const dirty = this.uncommitted(worktreeDir);
17033
+ if (dirty.length > 0)
17034
+ stop(
17035
+ "conflict",
17036
+ "the worktree has uncommitted changes or untracked files; commit them (or remove or ignore them), then merge again",
17037
+ dirty.join("\n")
17038
+ );
17039
+ const github = /github\.com[:/]/i.test(originUrl.stdout);
17040
+ this.deps.log(
17041
+ `drive merge ${item}: ${branch} \u2192 ${defaultBranch} (${github ? "GitHub" : hasOrigin ? "git origin" : "local"})`
17042
+ );
17043
+ const ctx = { worktreeDir, mainCheckout, branch, defaultBranch, hasOrigin };
17044
+ return github ? this.viaGitHub(ctx) : this.viaGit(ctx);
17045
+ }
17046
+ /** The main checkout: the first entry of `git worktree list`. */
17047
+ mainCheckout(worktreeDir) {
17048
+ const list = this.must(
17049
+ worktreeDir,
17050
+ "git worktree list",
17051
+ "worktree",
17052
+ "list",
17053
+ "--porcelain"
17054
+ );
17055
+ const first = list.split("\n").find((line) => line.startsWith("worktree "))?.slice("worktree ".length);
17056
+ if (!first) refuse("could not find the main checkout");
17057
+ return first;
17058
+ }
17059
+ /** origin's HEAD, else the main checkout's `main`/`master`. */
17060
+ defaultBranch(mainCheckout, hasOrigin) {
17061
+ const head = this.git(
17062
+ mainCheckout,
17063
+ "symbolic-ref",
17064
+ "--quiet",
17065
+ "--short",
17066
+ "refs/remotes/origin/HEAD"
17067
+ ).stdout.trim();
17068
+ if (head.startsWith("origin/")) return head.slice("origin/".length);
17069
+ if (hasOrigin) {
17070
+ const remote = this.git(
17071
+ mainCheckout,
17072
+ "ls-remote",
17073
+ "--symref",
17074
+ "origin",
17075
+ "HEAD"
17076
+ ).stdout.match(/^ref: refs\/heads\/(\S+)\s+HEAD/m);
17077
+ if (remote?.[1]) return remote[1];
17078
+ }
17079
+ for (const name of ["main", "master"]) {
17080
+ if (this.sha(mainCheckout, `refs/heads/${name}`)) return name;
17081
+ }
17082
+ return refuse("could not tell the default branch (no main or master)");
17083
+ }
17084
+ status(cwd, untracked) {
17085
+ return this.git(
17086
+ cwd,
17087
+ "status",
17088
+ "--porcelain",
17089
+ `--untracked-files=${untracked}`
17090
+ ).stdout.split("\n").map((line) => line.slice(3).trim()).filter(Boolean);
17091
+ }
17092
+ /** Changes to tracked files and untracked, non-ignored files: neither may ride along unseen. */
17093
+ uncommitted(cwd) {
17094
+ return this.status(cwd, "all");
17095
+ }
17096
+ /** Rebase the worktree onto `onto`; a conflict is aborted and reported. Returns the rebased HEAD. */
17097
+ rebase(worktreeDir, onto) {
17098
+ const rebased = this.git(worktreeDir, "rebase", onto);
17099
+ if (rebased.code !== 0) {
17100
+ const files = this.git(
17101
+ worktreeDir,
17102
+ "diff",
17103
+ "--name-only",
17104
+ "--diff-filter=U"
17105
+ ).stdout.split("\n").filter(Boolean);
17106
+ if (this.rebaseInProgress(worktreeDir))
17107
+ this.git(worktreeDir, "rebase", "--abort");
17108
+ if (files.length === 0)
17109
+ refuse(`git rebase ${onto} failed (not a conflict)`, both(rebased));
17110
+ stop(
17111
+ "conflict",
17112
+ `rebasing onto ${onto} conflicts in ${files.join(", ")}; the rebase was aborted`,
17113
+ files.join("\n")
17114
+ );
17115
+ }
17116
+ return this.must(worktreeDir, "reading HEAD", "rev-parse", "HEAD");
17117
+ }
17118
+ /** The repository's own checks: `.athena/config.json` `checks`, else package.json's `test` script. */
17119
+ repoChecks(mainCheckout, worktreeDir) {
17120
+ const configured = this.deps.configuredChecks(mainCheckout);
17121
+ if (configured && configured.length > 0) return configured;
17122
+ if (this.deps.packageTestScript(worktreeDir) !== void 0)
17123
+ return ["npm test"];
17124
+ return refuse(
17125
+ 'no checks to prove the merge: set "checks" in .athena/config.json or a package.json test script'
17126
+ );
17127
+ }
17128
+ /** The manifests and lockfiles whose change means the worktree's installed dependencies may not match. */
17129
+ dependenciesChanged(cwd, base, sha) {
17130
+ const changed = this.git(
17131
+ cwd,
17132
+ "diff",
17133
+ "--name-only",
17134
+ base,
17135
+ sha,
17136
+ "--",
17137
+ ...DEPENDENCY_FILES.map((name) => `:(glob)**/${name}`)
17138
+ ).stdout.trim();
17139
+ return changed !== "";
17140
+ }
17141
+ /** The install the checkout's lockfile calls for, scripts off. */
17142
+ installCommand(dir) {
17143
+ const has = (name) => this.deps.exists(`${dir}/${name}`);
17144
+ if (has("pnpm-lock.yaml"))
17145
+ return "pnpm install --frozen-lockfile --ignore-scripts";
17146
+ if (has("yarn.lock"))
17147
+ return "yarn install --frozen-lockfile --ignore-scripts";
17148
+ if (has("bun.lock") || has("bun.lockb"))
17149
+ return "bun install --frozen-lockfile --ignore-scripts";
17150
+ if (has("package-lock.json") || has("npm-shrinkwrap.json"))
17151
+ return "npm ci --ignore-scripts --no-audit --no-fund";
17152
+ return "npm install --ignore-scripts --no-audit --no-fund";
17153
+ }
17154
+ /**
17155
+ * Run each check in a clean, detached checkout of `sha` (never the worktree, whose files may differ from the
17156
+ * commit); the first non-zero exit fails the merge, and the deadline is `checks_timeout`. The worktree's
17157
+ * `node_modules` is reused only when no manifest or lockfile changed since `base`; otherwise the lockfile's
17158
+ * install runs in the checkout first.
17159
+ */
17160
+ async runRepoChecks(mainCheckout, worktreeDir, base, sha) {
17161
+ const checks = this.repoChecks(mainCheckout, worktreeDir);
17162
+ const scratch = this.deps.scratchDir();
17163
+ const added = this.git(
17164
+ worktreeDir,
17165
+ "worktree",
17166
+ "add",
17167
+ "--quiet",
17168
+ "--detach",
17169
+ scratch.dir,
17170
+ sha
17171
+ );
17172
+ if (added.code !== 0) {
17173
+ scratch.cleanup();
17174
+ refuse("could not check out the tested commit", both(added));
17175
+ }
17176
+ const run = async (command, what) => {
17177
+ if (this.left() <= 0) this.timedOut(`before ${what}`);
17178
+ const result = await this.deps.runCheck(
17179
+ scratch.dir,
17180
+ command,
17181
+ this.left()
17182
+ );
17183
+ if (result.timedOut)
17184
+ stop(
17185
+ "checks_timeout",
17186
+ `${what} was killed at the command's ${Math.round(this.timeoutMs / 1e3)} s deadline; run the command again`,
17187
+ both(result)
17188
+ );
17189
+ if (result.code !== 0)
17190
+ stop("checks_failed", `${what} exited ${result.code}`, both(result));
17191
+ };
17192
+ try {
17193
+ if (this.dependenciesChanged(worktreeDir, base, sha)) {
17194
+ const install = this.installCommand(scratch.dir);
17195
+ this.deps.log(`dependencies changed; installing: ${install}`);
17196
+ await run(install, `the dependency install \`${install}\``);
17197
+ } else {
17198
+ this.deps.linkDependencies(worktreeDir, scratch.dir);
17199
+ }
17200
+ for (const check of checks) {
17201
+ this.deps.log(`check (${sha.slice(0, 7)}): ${check}`);
17202
+ await run(check, `\`${check}\``);
17203
+ }
17204
+ return checks;
17205
+ } finally {
17206
+ this.git(worktreeDir, "worktree", "remove", "--force", scratch.dir);
17207
+ scratch.cleanup();
17208
+ this.git(worktreeDir, "worktree", "prune");
17209
+ }
17210
+ }
17211
+ // ── recorded merges ───────────────────────────────────────────────
17212
+ recordedMerge(cwd) {
17213
+ return this.sha(cwd, `${MERGED_REFS}/${this.input.item}`);
17214
+ }
17215
+ forgetMerge(cwd) {
17216
+ this.git(cwd, "update-ref", "-d", `${MERGED_REFS}/${this.input.item}`);
17217
+ this.git(cwd, "update-ref", "-d", `${TESTED_REFS}/${this.input.item}`);
17218
+ }
17219
+ /**
17220
+ * The default branch's commits origin lacks, along its first parents, are pushed only when each is a merge this
17221
+ * command recorded; anything else on it was not merged here and is refused, so a local `git merge` is never
17222
+ * laundered through a push. Returns a note for the detail.
17223
+ */
17224
+ pushRecordedOnly(mainCheckout, defaultBranch) {
17225
+ const remote = `refs/remotes/origin/${defaultBranch}`;
17226
+ const ahead = this.sha(mainCheckout, remote) ? this.git(
17227
+ mainCheckout,
17228
+ "rev-list",
17229
+ "--first-parent",
17230
+ `${remote}..refs/heads/${defaultBranch}`
17231
+ ).stdout.split("\n").filter(Boolean) : [];
17232
+ if (ahead.length === 0) return "";
17233
+ const recorded = new Set(
17234
+ this.git(
17235
+ mainCheckout,
17236
+ "for-each-ref",
17237
+ "--format=%(objectname)",
17238
+ MERGED_REFS
17239
+ ).stdout.split("\n").filter(Boolean)
17240
+ );
17241
+ const foreign = ahead.filter((commit) => !recorded.has(commit));
17242
+ if (foreign.length > 0)
17243
+ refuse(
17244
+ `${defaultBranch} has commits drisp drive merge did not make, so it pushes nothing; a member must remove them (or push them) first`,
17245
+ this.git(
17246
+ mainCheckout,
17247
+ "log",
17248
+ "--oneline",
17249
+ "--no-walk",
17250
+ ...foreign
17251
+ ).stdout.trim()
17252
+ );
17253
+ const pushed = this.git(
17254
+ mainCheckout,
17255
+ "push",
17256
+ "--quiet",
17257
+ "origin",
17258
+ defaultBranch
17259
+ );
17260
+ if (pushed.code !== 0)
17261
+ refuse(
17262
+ `${defaultBranch} holds a recorded merge that origin lacks, but pushing it failed; run the command again`,
17263
+ both(pushed)
17264
+ );
17265
+ return `; pushed ${defaultBranch} to origin`;
17266
+ }
17267
+ /** Fast-forward the default branch to origin's when origin moved on; refuse when they diverged. */
17268
+ catchUpWithOrigin(mainCheckout, defaultBranch) {
17269
+ this.must(mainCheckout, "git fetch origin", "fetch", "--quiet", "origin");
17270
+ const remote = `refs/remotes/origin/${defaultBranch}`;
17271
+ if (!this.sha(mainCheckout, remote)) return;
17272
+ if (this.isAncestor(mainCheckout, remote, `refs/heads/${defaultBranch}`))
17273
+ return;
17274
+ const ff = this.git(mainCheckout, "merge", "--ff-only", "--quiet", remote);
17275
+ if (ff.code !== 0)
17276
+ refuse(
17277
+ `${defaultBranch} and origin/${defaultBranch} have diverged`,
17278
+ both(ff)
17279
+ );
17280
+ }
17281
+ // ── a git origin, or none ─────────────────────────────────────────
17282
+ async viaGit(ctx) {
17283
+ const { worktreeDir, mainCheckout, branch, defaultBranch, hasOrigin } = ctx;
17284
+ const { item } = this.input;
17285
+ const defaultRef = `refs/heads/${defaultBranch}`;
17286
+ if (hasOrigin)
17287
+ this.must(mainCheckout, "git fetch origin", "fetch", "--quiet", "origin");
17288
+ const tip = this.must(
17289
+ worktreeDir,
17290
+ `reading ${branch}`,
17291
+ "rev-parse",
17292
+ `refs/heads/${branch}`
17293
+ );
17294
+ let recorded = this.recordedMerge(mainCheckout);
17295
+ if (recorded && !(this.isAncestor(mainCheckout, recorded, defaultRef) && (this.isAncestor(mainCheckout, tip, defaultRef) || this.sha(mainCheckout, `${TESTED_REFS}/${item}`) === tip))) {
17296
+ this.forgetMerge(mainCheckout);
17297
+ recorded = null;
17298
+ }
17299
+ if (recorded) {
17300
+ const pushed = hasOrigin ? this.pushRecordedOnly(mainCheckout, defaultBranch) : "";
17301
+ if (hasOrigin) this.deleteRemoteBranch(mainCheckout, branch);
17302
+ return {
17303
+ outcome: "already_merged",
17304
+ detail: `${item} was merged at ${recorded.slice(0, 7)}${pushed}`
17305
+ };
17306
+ }
17307
+ if (this.isAncestor(mainCheckout, tip, defaultRef))
17308
+ refuse(
17309
+ `no commits to merge: ${defaultBranch} already holds every commit of ${branch}, and drisp drive merge did not merge it (a merge it did not record does not count)`
17310
+ );
17311
+ const checkedOut = this.checkedOut(mainCheckout);
17312
+ if (checkedOut !== defaultBranch)
17313
+ refuse(
17314
+ `the main checkout has ${checkedOut || "a detached HEAD"} checked out, not ${defaultBranch}`
17315
+ );
17316
+ const dirty = this.status(mainCheckout, "no");
17317
+ if (dirty.length > 0)
17318
+ refuse(
17319
+ "the main checkout has uncommitted changes to tracked files",
17320
+ dirty.join("\n")
17321
+ );
17322
+ for (let round = 1; round <= MAX_ROUNDS; round++) {
17323
+ if (hasOrigin) {
17324
+ this.pushRecordedOnly(mainCheckout, defaultBranch);
17325
+ this.catchUpWithOrigin(mainCheckout, defaultBranch);
17326
+ }
17327
+ const before = this.must(
17328
+ mainCheckout,
17329
+ "reading the base",
17330
+ "rev-parse",
17331
+ defaultRef
17332
+ );
17333
+ const sha = this.rebase(worktreeDir, defaultRef);
17334
+ const checks = await this.runRepoChecks(
17335
+ mainCheckout,
17336
+ worktreeDir,
17337
+ before,
17338
+ sha
17339
+ );
17340
+ if (hasOrigin) this.catchUpWithOrigin(mainCheckout, defaultBranch);
17341
+ const base = this.must(
17342
+ mainCheckout,
17343
+ "reading the base",
17344
+ "rev-parse",
17345
+ defaultRef
17346
+ );
17347
+ if (!this.isAncestor(mainCheckout, base, sha)) {
17348
+ this.deps.log(
17349
+ `${defaultBranch} moved to ${base.slice(0, 7)} during the checks; rebasing again`
17350
+ );
17351
+ continue;
17352
+ }
17353
+ const merge = this.must(
17354
+ mainCheckout,
17355
+ "creating the merge commit",
17356
+ "commit-tree",
17357
+ `${sha}^{tree}`,
17358
+ "-p",
17359
+ base,
17360
+ "-p",
17361
+ sha,
17362
+ "-m",
17363
+ `Merge ${item} (${branch})`
17364
+ );
17365
+ this.must(
17366
+ mainCheckout,
17367
+ "recording the merge",
17368
+ "update-ref",
17369
+ `${MERGED_REFS}/${item}`,
17370
+ merge
17371
+ );
17372
+ this.must(
17373
+ mainCheckout,
17374
+ "recording the merge",
17375
+ "update-ref",
17376
+ `${TESTED_REFS}/${item}`,
17377
+ sha
17378
+ );
17379
+ const advanced = this.advance(
17380
+ ctx,
17381
+ merge,
17382
+ ` after ${checks.map((check) => `\`${check}\``).join(", ")} passed on ${sha.slice(0, 7)}`
17383
+ );
17384
+ if (advanced) return advanced;
17385
+ }
17386
+ return stop("checks_failed", baseKeptMoving(defaultBranch));
17387
+ }
17388
+ /**
17389
+ * Fast-forward the default branch to the recorded merge and push it. Returns null when origin moved under the
17390
+ * push, after undoing the local merge, so the caller rebases and checks again.
17391
+ */
17392
+ advance(ctx, merge, evidence) {
17393
+ const { mainCheckout, branch, defaultBranch, hasOrigin } = ctx;
17394
+ const before = this.must(
17395
+ mainCheckout,
17396
+ "reading the base",
17397
+ "rev-parse",
17398
+ `refs/heads/${defaultBranch}`
17399
+ );
17400
+ const ff = this.git(mainCheckout, "merge", "--ff-only", "--quiet", merge);
17401
+ if (ff.code !== 0) {
17402
+ this.forgetMerge(mainCheckout);
17403
+ refuse(
17404
+ `fast-forwarding ${defaultBranch} in the main checkout failed`,
17405
+ both(ff)
17406
+ );
17407
+ }
17408
+ let note = "";
17409
+ if (hasOrigin) {
17410
+ const pushed = this.git(
17411
+ mainCheckout,
17412
+ "push",
17413
+ "--quiet",
17414
+ "origin",
17415
+ defaultBranch
17416
+ );
17417
+ if (pushed.code !== 0) {
17418
+ this.git(mainCheckout, "fetch", "--quiet", "origin");
17419
+ const remote = this.sha(
17420
+ mainCheckout,
17421
+ `refs/remotes/origin/${defaultBranch}`
17422
+ );
17423
+ if (remote && !this.isAncestor(mainCheckout, remote, merge)) {
17424
+ this.git(mainCheckout, "reset", "--keep", "--quiet", before);
17425
+ this.forgetMerge(mainCheckout);
17426
+ this.deps.log(
17427
+ `origin/${defaultBranch} moved during the merge; rebasing again`
17428
+ );
17429
+ return null;
17430
+ }
17431
+ refuse(
17432
+ `merged ${branch} into ${defaultBranch} locally (recorded), but pushing ${defaultBranch} failed; run the command again to push`,
17433
+ both(pushed)
17434
+ );
17435
+ }
17436
+ this.deleteRemoteBranch(mainCheckout, branch);
17437
+ note = "; pushed to origin";
17438
+ }
17439
+ return {
17440
+ outcome: "merged",
17441
+ detail: `${branch} merged into ${defaultBranch} at ${merge.slice(0, 7)}${evidence}${note}`
17442
+ };
17443
+ }
17444
+ // ── GitHub ────────────────────────────────────────────────────────
17445
+ async viewPullRequest(worktreeDir, selector) {
17446
+ const view = await this.deps.gh(worktreeDir, [
17447
+ "pr",
17448
+ "view",
17449
+ selector,
17450
+ "--json",
17451
+ "number,state,url,headRefName,headRefOid,baseRefName,mergedAt"
17452
+ ]);
17453
+ if (view.code !== 0)
17454
+ refuse(
17455
+ `no pull request found for ${selector}; the subagent opens one before the merge`,
17456
+ both(view)
17457
+ );
17458
+ try {
17459
+ return JSON.parse(view.stdout);
17460
+ } catch {
17461
+ return refuse("could not read gh pr view", both(view));
17462
+ }
17463
+ }
17464
+ async viaGitHub(ctx) {
17465
+ const { worktreeDir, mainCheckout, branch, defaultBranch } = ctx;
17466
+ let pr = await this.viewPullRequest(worktreeDir, this.input.pr ?? branch);
17467
+ if (pr.headRefName !== branch)
17468
+ refuse(
17469
+ `pull request #${pr.number} is for ${pr.headRefName}, not ${branch}`
17470
+ );
17471
+ if (pr.state === "MERGED") {
17472
+ this.pullMainCheckout(mainCheckout, defaultBranch);
17473
+ this.deleteRemoteBranch(mainCheckout, branch);
17474
+ return {
17475
+ outcome: "already_merged",
17476
+ detail: `pull request #${pr.number} was merged at ${pr.mergedAt ?? "an earlier time"}`
17477
+ };
17478
+ }
17479
+ if (pr.state !== "OPEN")
17480
+ refuse(`pull request #${pr.number} is ${pr.state.toLowerCase()}`);
17481
+ if (pr.baseRefName !== defaultBranch)
17482
+ refuse(
17483
+ `pull request #${pr.number} targets ${pr.baseRefName}, not ${defaultBranch}`
17484
+ );
17485
+ const base = `refs/remotes/origin/${defaultBranch}`;
17486
+ const fetchBase = () => this.must(
17487
+ worktreeDir,
17488
+ "git fetch origin",
17489
+ "fetch",
17490
+ "--quiet",
17491
+ "origin",
17492
+ defaultBranch
17493
+ );
17494
+ fetchBase();
17495
+ if (this.isAncestor(worktreeDir, `refs/heads/${branch}`, base))
17496
+ refuse(
17497
+ `no commits to merge: origin/${defaultBranch} already holds every commit of ${branch}`
17498
+ );
17499
+ for (let round = 1; round <= MAX_ROUNDS; round++) {
17500
+ if (round > 1) {
17501
+ pr = await this.viewPullRequest(worktreeDir, String(pr.number));
17502
+ fetchBase();
17503
+ }
17504
+ const baseSha = this.must(
17505
+ worktreeDir,
17506
+ "reading the base",
17507
+ "rev-parse",
17508
+ base
17509
+ );
17510
+ const sha = this.rebase(worktreeDir, base);
17511
+ this.pushBranch(worktreeDir, branch, pr.headRefOid, sha);
17512
+ const proof = await this.awaitChecks(worktreeDir, pr.number, sha);
17513
+ const ran = proof === "none" ? await this.runRepoChecks(mainCheckout, worktreeDir, baseSha, sha) : null;
17514
+ fetchBase();
17515
+ if (!this.isAncestor(worktreeDir, base, sha)) {
17516
+ this.deps.log(
17517
+ `origin/${defaultBranch} moved during the checks; rebasing again`
17518
+ );
17519
+ continue;
17520
+ }
17521
+ const merged = await this.deps.gh(worktreeDir, [
17522
+ "pr",
17523
+ "merge",
17524
+ String(pr.number),
17525
+ "--squash",
17526
+ "--match-head-commit",
17527
+ sha
17528
+ ]);
17529
+ if (merged.code !== 0) {
17530
+ const after = await this.viewPullRequest(
17531
+ worktreeDir,
17532
+ String(pr.number)
17533
+ );
17534
+ if (after.state !== "MERGED")
17535
+ refuse(`gh pr merge #${pr.number} failed`, both(merged));
17536
+ }
17537
+ this.pullMainCheckout(mainCheckout, defaultBranch);
17538
+ this.deleteRemoteBranch(mainCheckout, branch);
17539
+ return {
17540
+ outcome: "merged",
17541
+ detail: `pull request #${pr.number} squash-merged at ${sha.slice(0, 7)} after ${ran ? `no GitHub checks; ${ran.map((check) => `\`${check}\``).join(", ")} passed` : proof}`
17542
+ };
17543
+ }
17544
+ return stop("checks_failed", baseKeptMoving(defaultBranch));
17545
+ }
17546
+ /**
17547
+ * Push the rebased branch, leased on the head GitHub reported, so a commit pushed meanwhile is never overwritten.
17548
+ * When the lease fails it reads origin again: already at `sha` is fine; anything else is `refused`.
17549
+ */
17550
+ pushBranch(worktreeDir, branch, lease, sha) {
17551
+ const pushed = this.git(
17552
+ worktreeDir,
17553
+ "push",
17554
+ "--quiet",
17555
+ `--force-with-lease=refs/heads/${branch}:${lease}`,
17556
+ "origin",
17557
+ `${sha}:refs/heads/${branch}`
17558
+ );
17559
+ if (pushed.code === 0) return;
17560
+ this.git(worktreeDir, "fetch", "--quiet", "origin", branch);
17561
+ const remote = this.sha(worktreeDir, `refs/remotes/origin/${branch}`);
17562
+ if (remote === sha) return;
17563
+ refuse(
17564
+ `origin's ${branch} is at ${remote?.slice(0, 7) ?? "an unknown commit"}, not the ${lease.slice(0, 7)} the pull request reported, so the push was refused; the subagent pulls those commits into its worktree, then merge again`,
17565
+ both(pushed)
17566
+ );
17567
+ }
17568
+ /**
17569
+ * Wait until GitHub reports the pushed commit's checks done. Required checks decide; with none required, all
17570
+ * checks, which must hold for a settle period after GitHub first reports the commit; with no checks at all
17571
+ * after the grace period (also counted from then), `none`. A failed or cancelled check is `checks_failed`; the
17572
+ * bound is `checks_timeout`.
17573
+ */
17574
+ async awaitChecks(worktreeDir, pr, sha) {
17575
+ let seenAt = null;
17576
+ for (; ; ) {
17577
+ const view = await this.viewPullRequest(worktreeDir, String(pr));
17578
+ let verdict;
17579
+ if (view.headRefOid !== sha) {
17580
+ verdict = {
17581
+ kind: "waiting",
17582
+ detail: `GitHub has not seen ${sha.slice(0, 7)} yet`
17583
+ };
17584
+ } else {
17585
+ seenAt ??= this.deps.now();
17586
+ const since = this.deps.now() - seenAt;
17587
+ verdict = await this.readChecks(worktreeDir, pr);
17588
+ if (verdict.kind === "none" && since >= this.graceMs) return "none";
17589
+ if (verdict.kind === "passed" && verdict.scope === "checks" && since < this.settleMs)
17590
+ verdict = {
17591
+ kind: "waiting",
17592
+ detail: "checks passed; waiting for late checks to register"
17593
+ };
17594
+ }
17595
+ if (verdict.kind === "passed") return verdict.detail;
17596
+ if (verdict.kind === "failed") stop("checks_failed", verdict.detail);
17597
+ if (this.left() < this.pollMs)
17598
+ stop(
17599
+ "checks_timeout",
17600
+ `the command's ${Math.round(this.timeoutMs / 1e3)} s deadline passed waiting for checks (${verdict.detail}); run the command again to keep waiting`
17601
+ );
17602
+ this.deps.log(`waiting for checks: ${verdict.detail}`);
17603
+ await this.deps.sleep(this.pollMs);
17604
+ }
17605
+ }
17606
+ async readChecks(worktreeDir, pr) {
17607
+ const read = async (required) => {
17608
+ const result = await this.deps.gh(worktreeDir, [
17609
+ "pr",
17610
+ "checks",
17611
+ String(pr),
17612
+ ...required ? ["--required"] : [],
17613
+ "--json",
17614
+ "name,bucket"
17615
+ ]);
17616
+ return readCheckList(result.stdout, result.stderr);
17617
+ };
17618
+ const empty = (list2) => list2.kind === "none" || list2.kind === "checks" && list2.checks.length === 0;
17619
+ let list = await read(true);
17620
+ let scope = "required checks";
17621
+ if (empty(list)) {
17622
+ list = await read(false);
17623
+ scope = "checks";
17624
+ }
17625
+ if (empty(list)) return { kind: "none", detail: "no checks reported" };
17626
+ if (list.kind === "unreadable")
17627
+ return { kind: "waiting", detail: list.detail };
17628
+ return list.kind === "checks" ? judge(list.checks, scope) : { kind: "none", detail: "no checks reported" };
17629
+ }
17630
+ /**
17631
+ * Delete the merged item's branch on origin, so clean-up needs no push of its own (in Run D of the sandbox a
17632
+ * clean-up loop's `git push origin --delete $b` parked the Run: its target is a variable). Best effort: a branch
17633
+ * that was never pushed, or one GitHub already deleted, is fine.
17634
+ */
17635
+ deleteRemoteBranch(mainCheckout, branch) {
17636
+ if (this.git(
17637
+ mainCheckout,
17638
+ "ls-remote",
17639
+ "--exit-code",
17640
+ "--heads",
17641
+ "origin",
17642
+ branch
17643
+ ).code !== 0)
17644
+ return;
17645
+ this.git(mainCheckout, "push", "--quiet", "origin", "--delete", branch);
17646
+ }
17647
+ /** Bring the main checkout up to date after a merge on GitHub, so the next worktree starts from it. Best effort. */
17648
+ pullMainCheckout(mainCheckout, defaultBranch) {
17649
+ if (this.checkedOut(mainCheckout) !== defaultBranch) return;
17650
+ this.git(
17651
+ mainCheckout,
17652
+ "pull",
17653
+ "--quiet",
17654
+ "--ff-only",
17655
+ "origin",
17656
+ defaultBranch
17657
+ );
17658
+ }
17659
+ };
17660
+
17661
+ // src/core/drive/processes.ts
17662
+ import { spawn, spawnSync as spawnSync2 } from "child_process";
17663
+ import fs13 from "fs";
17664
+ import os4 from "os";
17665
+ import path13 from "path";
17666
+
17667
+ // src/core/drive/lock.ts
17668
+ import fs12 from "fs";
17669
+ import path12 from "path";
17670
+ var LOCK_FILE = "drisp-drive-merge.lock";
17671
+ function alive(pid) {
17672
+ try {
17673
+ process.kill(pid, 0);
17674
+ return true;
17675
+ } catch (error) {
17676
+ return error.code === "EPERM";
17677
+ }
17678
+ }
17679
+ var TAKEOVER_STALE_MS = 1e4;
17680
+ function ownerOf(file) {
17681
+ try {
17682
+ const owner = Number(fs12.readFileSync(file, "utf8"));
17683
+ return Number.isInteger(owner) && owner > 0 ? owner : null;
17684
+ } catch {
17685
+ return null;
17686
+ }
17687
+ }
17688
+ function takeOverStale(file, deadOwner) {
17689
+ const marker = `${file}.takeover`;
17690
+ try {
17691
+ fs12.writeFileSync(marker, String(process.pid), { flag: "wx" });
17692
+ } catch {
17693
+ try {
17694
+ if (Date.now() - fs12.statSync(marker).mtimeMs > TAKEOVER_STALE_MS)
17695
+ fs12.unlinkSync(marker);
17696
+ } catch {
17697
+ }
17698
+ return;
17699
+ }
17700
+ try {
17701
+ if (ownerOf(file) === deadOwner) fs12.unlinkSync(file);
17702
+ } catch {
17703
+ } finally {
17704
+ fs12.unlinkSync(marker);
17705
+ }
17706
+ }
17707
+ async function acquireLock(commonDir, waitMs) {
17708
+ const file = path12.join(commonDir, LOCK_FILE);
17709
+ const deadline = Date.now() + waitMs;
17710
+ for (; ; ) {
17711
+ try {
17712
+ fs12.writeFileSync(file, String(process.pid), { flag: "wx" });
17713
+ return () => {
17714
+ if (ownerOf(file) === process.pid) {
17715
+ try {
17716
+ fs12.unlinkSync(file);
17717
+ } catch {
17718
+ }
17719
+ }
17720
+ };
17721
+ } catch (error) {
17722
+ if (error.code !== "EEXIST") throw error;
17723
+ }
17724
+ const owner = ownerOf(file);
17725
+ if (owner !== null && !alive(owner)) {
17726
+ takeOverStale(file, owner);
17727
+ continue;
17728
+ }
17729
+ if (Date.now() >= deadline) return null;
17730
+ await new Promise((resolve) => setTimeout(resolve, 100));
17731
+ }
17732
+ }
17733
+
17734
+ // src/core/drive/processes.ts
17735
+ var OUTPUT_CAP = 1024 * 1024;
17736
+ function runGit(cwd, args) {
17737
+ const result = spawnSync2("git", ["-C", cwd, ...args], {
17738
+ encoding: "utf8",
17739
+ stdio: ["ignore", "pipe", "pipe"],
17740
+ maxBuffer: 64 * 1024 * 1024
17741
+ });
17742
+ return {
17743
+ code: result.error ? 127 : result.status ?? 1,
17744
+ stdout: result.stdout,
17745
+ stderr: result.error ? result.error.message : result.stderr
17746
+ };
17747
+ }
17748
+ function runProcess(program, args, cwd, timeoutMs) {
17749
+ return new Promise((resolve) => {
17750
+ let stdout = "";
17751
+ let stderr = "";
17752
+ let timedOut = false;
17753
+ const child = spawn(program, args, {
17754
+ cwd,
17755
+ stdio: ["ignore", "pipe", "pipe"],
17756
+ detached: true
17757
+ });
17758
+ const keep = (text, chunk) => (text + chunk.toString()).slice(-OUTPUT_CAP);
17759
+ child.stdout.on("data", (chunk) => {
17760
+ stdout = keep(stdout, chunk);
17761
+ });
17762
+ child.stderr.on("data", (chunk) => {
17763
+ stderr = keep(stderr, chunk);
17764
+ });
17765
+ const timer = setTimeout(() => {
17766
+ timedOut = true;
17767
+ try {
17768
+ if (child.pid) process.kill(-child.pid, "SIGKILL");
17769
+ } catch {
17770
+ child.kill("SIGKILL");
17771
+ }
17772
+ }, timeoutMs);
17773
+ child.on("error", (error) => {
17774
+ clearTimeout(timer);
17775
+ resolve({ code: 127, stdout, stderr: error.message, timedOut });
17776
+ });
17777
+ child.on("close", (code, signal) => {
17778
+ clearTimeout(timer);
17779
+ resolve({
17780
+ code: code ?? (signal ? 128 : 1),
17781
+ stdout,
17782
+ stderr,
17783
+ timedOut
17784
+ });
17785
+ });
17786
+ });
17787
+ }
17788
+ var GH_TIMEOUT_MS = 6e4;
17789
+ function runCheck(cwd, command, timeoutMs) {
17790
+ return runProcess(
17791
+ "bash",
17792
+ ["-e", "-o", "pipefail", "-c", command],
17793
+ cwd,
17794
+ timeoutMs
17795
+ ).then((result) => ({
17796
+ ...result,
17797
+ // One stream reads better in a Report: the check's own interleaving is lost either way.
17798
+ stdout: [result.stdout, result.stderr].filter(Boolean).join("\n"),
17799
+ stderr: ""
17800
+ }));
17801
+ }
17802
+ function packageTestScript(dir) {
17803
+ try {
17804
+ const manifest = JSON.parse(
17805
+ fs13.readFileSync(path13.join(dir, "package.json"), "utf8")
17806
+ );
17807
+ const test = manifest.scripts?.["test"];
17808
+ return typeof test === "string" && test.trim() ? test : void 0;
17809
+ } catch {
17810
+ return void 0;
17811
+ }
17812
+ }
17813
+ function scratchDir() {
17814
+ const parent = fs13.mkdtempSync(path13.join(os4.tmpdir(), SCRATCH_PREFIX));
17815
+ return {
17816
+ dir: path13.join(parent, "checkout"),
17817
+ cleanup: () => fs13.rmSync(parent, { recursive: true, force: true })
17818
+ };
17819
+ }
17820
+ function linkDependencies(fromDir, toDir) {
17821
+ const modules = path13.join(fromDir, "node_modules");
17822
+ if (fs13.existsSync(modules) && !fs13.existsSync(path13.join(toDir, "node_modules")))
17823
+ fs13.symlinkSync(modules, path13.join(toDir, "node_modules"), "dir");
17824
+ }
17825
+ function processDeps(configuredChecks, log) {
17826
+ return {
17827
+ git: runGit,
17828
+ gh: (cwd, args) => runProcess("gh", args, cwd, GH_TIMEOUT_MS).then(
17829
+ ({ timedOut, ...result }) => timedOut ? {
17830
+ ...result,
17831
+ code: result.code || 124,
17832
+ stderr: `${result.stderr}
17833
+ gh timed out`
17834
+ } : result
17835
+ ),
17836
+ runCheck,
17837
+ configuredChecks,
17838
+ packageTestScript,
17839
+ lock: acquireLock,
17840
+ scratchDir,
17841
+ linkDependencies,
17842
+ exists: (target) => fs13.existsSync(target),
17843
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
17844
+ now: () => Date.now(),
17845
+ log
17846
+ };
17847
+ }
17848
+
17849
+ // src/app/entry/driveCommand.ts
17850
+ var USAGE4 = `Usage: drisp drive merge <item> --worktree <path> [--branch <branch>] [--pr <number|url>] [--timeout-ms <ms>]
17851
+ Merges a drive item: already merged \u2192 already_merged; rebase onto the default branch (conflict); on GitHub push with
17852
+ --force-with-lease, wait for the required checks (all checks when none is required; the repository's checks when
17853
+ there are none) and gh pr merge; otherwise run the repository's checks (.athena/config.json "checks", else the
17854
+ package.json test script) and merge --no-ff into the default branch, then push it. Prints {outcome, detail, output?}
17855
+ with outcome merged | already_merged | conflict | checks_failed | checks_timeout | refused.`;
17856
+ async function runDriveCommand(input) {
17857
+ const stdout = input.stdout ?? process.stdout;
17858
+ const stderr = input.stderr ?? process.stderr;
17859
+ const [item, ...extra] = input.positional;
17860
+ if (input.subcommand !== "merge" || !item || extra.length > 0 || !input.flags.worktree || input.flags.timeoutMs !== void 0 && !(Number.isFinite(input.flags.timeoutMs) && input.flags.timeoutMs > 0)) {
17861
+ stderr.write(`${USAGE4}
17862
+ `);
17863
+ return 2;
17864
+ }
17865
+ const deps = input.deps ?? processDeps(
17866
+ (dir) => readConfig(dir).checks,
17867
+ (line) => stderr.write(`${line}
17868
+ `)
17869
+ );
17870
+ let result;
17871
+ try {
17872
+ result = await driveMerge(
17873
+ {
17874
+ item,
17875
+ worktree: path14.resolve(input.cwd, input.flags.worktree),
17876
+ ...input.flags.branch ? { branch: input.flags.branch } : {},
17877
+ ...input.flags.pr ? { pr: input.flags.pr } : {},
17878
+ ...input.flags.timeoutMs !== void 0 ? { timeoutMs: input.flags.timeoutMs } : {}
17879
+ },
17880
+ deps
17881
+ );
17882
+ } catch (error) {
17883
+ result = {
17884
+ outcome: "refused",
17885
+ detail: `drive merge failed unexpectedly: ${error instanceof Error ? error.message : String(error)}`
17886
+ };
17887
+ }
17888
+ stdout.write(`${JSON.stringify({ item, ...result })}
17889
+ `);
17890
+ return result.outcome === "merged" || result.outcome === "already_merged" ? 0 : 1;
17891
+ }
17892
+
17893
+ // src/app/entry/trackerCommand.ts
17894
+ import { readFile as readFile2 } from "fs/promises";
17895
+ async function runTrackerCommand(args, env2 = process.env) {
17896
+ const usage2 = "Usage: drisp tracker get <issue-id> | apply <operation.json>\nOperations: issue.revision, issue.comment, issue.attention, issue.evidence. Read the Issue first for its current version. Evidence includes revision, reference, kind and runId; staging/QA also require deploymentId. Production release confirmation is a human action.";
17897
+ try {
17898
+ if (!env2.DRISP_HUB_URL || !env2.DRISP_RUN_TOKEN)
17899
+ throw new Error(
17900
+ "This command requires a native Issue Run with DRISP_HUB_URL and DRISP_RUN_TOKEN."
17901
+ );
17902
+ const client2 = DrispClient.delegated(
17903
+ env2.DRISP_HUB_URL,
17904
+ env2.DRISP_RUN_TOKEN
17905
+ );
17906
+ const [action, argument, ...extra] = args;
17907
+ if (!argument || extra.length) throw new Error(usage2);
17908
+ if (action === "get")
17909
+ process.stdout.write(
17910
+ JSON.stringify(await client2.trackerIssue(argument)) + "\n"
17911
+ );
17912
+ else if (action === "apply") {
17913
+ const operation = trackerOperationSchema.parse(
17914
+ JSON.parse(await readFile2(argument, "utf8"))
17915
+ );
17916
+ process.stdout.write(
17917
+ JSON.stringify(await client2.trackerOperation(operation)) + "\n"
17918
+ );
17919
+ } else throw new Error(usage2);
17920
+ return 0;
17921
+ } catch (error) {
17922
+ process.stderr.write(
17923
+ (error instanceof Error ? error.message : String(error)) + "\n"
17924
+ );
17925
+ return 2;
17926
+ }
17927
+ }
17928
+
17929
+ // src/app/entry/workCommand.ts
17930
+ import { readFile as readFile3 } from "fs/promises";
17931
+ var WORK_USAGE = `Usage: drisp work authority
17932
+ drisp work issue <issue-id> | plan <plan-id>
17933
+ drisp work start issue|plan <id> [--activation <file.json>] [--command-id <id>]
17934
+ drisp work follow-up issue|plan|execution <id> "<text>" [--command-id <id>]
17935
+ drisp work inspect <execution-id>
17936
+ drisp work hold|unhold|pause|resume <execution-id> [--reason <text>] [--generation <n>] [--command-id <id>]
17937
+ drisp work cancel <execution-id> [--reason <text>] [--grace-ms <0-590000>] [--generation <n>] [--command-id <id>]
17938
+ drisp work revise <assignment-id> --changes <file.json> --reason <text> [--command-id <id>]
17939
+ drisp work reconcile <effect-id> succeeded|failed --evidence <file.json> --generation <n>
17940
+ drisp work recover <assignment-id> --reason <text> [--session auto|original|fresh] [--runner-id <id>]
17941
+ drisp work close <execution-id> cancelled|failed [--acknowledge <effect-id,...>] [--reason <text>]
17942
+ drisp work answer <question-id> "<text>" | --decision allow|deny [--command-id <id>]
17943
+ drisp work accept|reject <execution-id> <package-id> "<reason>" [--generation <n>]
17944
+ drisp work brief
17945
+ drisp work forward <question-id> "<note>"
17946
+ drisp work incorporate <assignment-id> <version> --generation <n> [--tool-use-id <id>]
17947
+ DRISP_HUB_URL is the Team's Hub base (https://<hub>/v2/orgs/<team>); DRISP_HUB_TOKEN a member's token, DRISP_RUN_TOKEN a
17948
+ coordinated Run's credential (brief, incorporate, forward, and answer as that Run). incorporate reads
17949
+ DRISP_TOOL_USE_ID when set; the Runner does not set it yet (DRI-353). A control prints "requested" until the
17950
+ Hub records it effective; a cancel reads requested, then the cancellation's state (stopping, waiting_on_children,
17951
+ effects_outstanding, runs_lost), and effective only once the execution closed as cancelled, with cleanStop as the Hub
17952
+ recorded it. Nothing here starts a legacy Build: under legacy execution authority starts and controls are
17953
+ refused (execution_authority_legacy) and the Build commands stay the legacy path.`;
17954
+ function workCommandFlags(flags) {
17955
+ const result = {};
17956
+ for (const name of [
17957
+ "reason",
17958
+ "activation",
17959
+ "commandId",
17960
+ "toolUseId",
17961
+ "decision",
17962
+ "changes",
17963
+ "evidence",
17964
+ "session",
17965
+ "runnerId",
17966
+ "acknowledge"
17967
+ ])
17968
+ if (typeof flags[name] === "string") result[name] = flags[name];
17969
+ for (const name of ["generation", "graceMs"])
17970
+ if (typeof flags[name] === "number") result[name] = flags[name];
17971
+ return result;
17972
+ }
17973
+ var UsageError = class extends Error {
17974
+ };
17975
+ var UNSUPPORTED = {
17976
+ control: "execution.control",
17977
+ amend: "execution.amend",
17978
+ activate: "execution.activate",
17979
+ message: "message.send",
17980
+ "record-effect": "effect.record",
17981
+ "decide-child": "coordinator.decide",
17982
+ build: "legacy.build_start"
17983
+ };
17984
+ var RUN_CREDENTIAL = /* @__PURE__ */ new Set(["brief", "incorporate", "forward"]);
17985
+ async function runWorkCommand(args, flags = {}, env2 = process.env, io = {
17986
+ out: (line) => process.stdout.write(line + "\n"),
17987
+ err: (line) => process.stderr.write(line + "\n")
17988
+ }) {
17989
+ const [sub = "", ...rest] = args;
17990
+ try {
17991
+ if (!sub || sub === "help") throw new UsageError(WORK_USAGE);
17992
+ const asRun = RUN_CREDENTIAL.has(sub);
17993
+ const token = asRun ? env2.DRISP_RUN_TOKEN ?? env2.DRISP_HUB_TOKEN : env2.DRISP_HUB_TOKEN ?? env2.DRISP_RUN_TOKEN;
17994
+ if (!env2.DRISP_HUB_URL || !token)
17995
+ throw new UsageError(
17996
+ `drisp work needs DRISP_HUB_URL and ${asRun ? "DRISP_RUN_TOKEN" : "DRISP_HUB_TOKEN"}.`
17997
+ );
17998
+ if (flags.generation !== void 0 && !(Number.isInteger(flags.generation) && flags.generation > 0))
17999
+ throw new UsageError("--generation must be a positive whole number.");
18000
+ const team = /\/v2\/orgs\/([^/]+)\/?$/.exec(
18001
+ new URL(env2.DRISP_HUB_URL).pathname
18002
+ );
18003
+ if (!team)
18004
+ throw new UsageError(
18005
+ "DRISP_HUB_URL must be the Team base, https://<hub>/v2/orgs/<team>."
18006
+ );
18007
+ const client2 = DrispClient.delegated(env2.DRISP_HUB_URL, token);
18008
+ const teamId = decodeURIComponent(team[1]);
18009
+ const flows = new WorkFlows(client2, teamId);
18010
+ const result = await dispatch(
18011
+ flows,
18012
+ sub,
18013
+ rest,
18014
+ flags,
18015
+ env2,
18016
+ io.readFile ?? ((path17) => readFile3(path17, "utf8"))
18017
+ );
18018
+ io.out(JSON.stringify(result));
18019
+ return 0;
18020
+ } catch (error) {
18021
+ if (error instanceof UsageError) {
18022
+ io.err(error.message);
18023
+ return 2;
18024
+ }
18025
+ if (error instanceof WorkFlowError || error instanceof ClientError) {
18026
+ io.out(
18027
+ JSON.stringify({
18028
+ error: error.code,
18029
+ ...error instanceof ClientError ? { status: error.status } : {},
18030
+ ...error.details
18031
+ })
18032
+ );
18033
+ return 1;
18034
+ }
18035
+ io.out(
18036
+ JSON.stringify({
18037
+ error: "request_failed",
18038
+ message: error instanceof Error ? error.message : String(error)
18039
+ })
18040
+ );
18041
+ return 1;
18042
+ }
18043
+ }
18044
+ function need(values, count) {
18045
+ if (values.length !== count) throw new UsageError(WORK_USAGE);
18046
+ return values;
18047
+ }
18048
+ function scopeOf(kind) {
18049
+ if (kind !== "issue" && kind !== "plan") throw new UsageError(WORK_USAGE);
18050
+ return kind;
18051
+ }
18052
+ async function dispatch(flows, sub, rest, flags, env2, read) {
18053
+ const commandId = flags.commandId;
18054
+ switch (sub) {
18055
+ case "authority": {
18056
+ need(rest, 0);
18057
+ const path17 = await flows.path();
18058
+ return { path: path17, operations: await flows.operations() };
18059
+ }
18060
+ case "issue": {
18061
+ const [id] = need(rest, 1);
18062
+ return flows.readIssue(id);
18063
+ }
18064
+ case "plan": {
18065
+ const [id] = need(rest, 1);
18066
+ return flows.readPlan(id);
18067
+ }
18068
+ case "start": {
18069
+ const [kind, id] = need(rest, 2);
18070
+ const activation = flags.activation ? JSON.parse(await read(flags.activation)) : void 0;
18071
+ if (activation && typeof activation.runId !== "string")
18072
+ activation.runId = crypto.randomUUID();
18073
+ return flows.start(
18074
+ { kind: scopeOf(kind), id },
18075
+ {
18076
+ ...activation ? { activation } : {},
18077
+ ...commandId ? { commandId } : {}
18078
+ }
18079
+ );
18080
+ }
18081
+ case "follow-up": {
18082
+ const [kind, id, text] = need(rest, 3);
18083
+ const target = kind === "execution" ? { kind: "execution", id } : { kind: scopeOf(kind), id };
18084
+ return flows.followUp(target, text, commandId);
18085
+ }
18086
+ case "inspect": {
18087
+ const [id] = need(rest, 1);
18088
+ return flows.inspect(id);
18089
+ }
18090
+ case "hold":
18091
+ case "unhold":
18092
+ case "pause":
18093
+ case "resume":
18094
+ case "cancel": {
18095
+ const [id] = need(rest, 1);
18096
+ if (sub === "cancel" && flags.graceMs !== void 0 && !(Number.isInteger(flags.graceMs) && flags.graceMs >= 0 && flags.graceMs <= maxClientCancelBoundaryGraceMs))
18097
+ throw new UsageError(
18098
+ `--grace-ms must be a whole number of milliseconds from 0 to ${maxClientCancelBoundaryGraceMs}.`
18099
+ );
18100
+ return flows.control(id, sub, {
18101
+ ...flags.reason ? { reason: flags.reason } : {},
18102
+ ...flags.generation !== void 0 ? { expectedGeneration: flags.generation } : {},
18103
+ ...sub === "cancel" && flags.graceMs !== void 0 ? { boundaryGraceMs: flags.graceMs } : {},
18104
+ ...commandId ? { commandId } : {}
18105
+ });
18106
+ }
18107
+ case "revise": {
18108
+ const [id] = need(rest, 1);
18109
+ if (!flags.changes || !flags.reason) throw new UsageError(WORK_USAGE);
18110
+ const changes = JSON.parse(await read(flags.changes));
18111
+ return flows.revise(id, changes, flags.reason, {
18112
+ ...commandId ? { commandId } : {}
18113
+ });
18114
+ }
18115
+ case "reconcile": {
18116
+ const [id, outcome] = need(rest, 2);
18117
+ if (outcome !== "succeeded" && outcome !== "failed" || !flags.evidence || flags.generation === void 0)
18118
+ throw new UsageError(WORK_USAGE);
18119
+ const evidence = JSON.parse(await read(flags.evidence));
18120
+ return flows.reconcile(id, outcome, evidence, {
18121
+ expectedGeneration: flags.generation,
18122
+ ...commandId ? { commandId } : {}
18123
+ });
18124
+ }
18125
+ case "recover": {
18126
+ const [id] = need(rest, 1);
18127
+ const session = flags.session ?? "auto";
18128
+ if (!flags.reason || session !== "auto" && session !== "original" && session !== "fresh")
18129
+ throw new UsageError(WORK_USAGE);
18130
+ return flows.recover(id, {
18131
+ reason: flags.reason,
18132
+ session,
18133
+ ...flags.runnerId ? { runnerId: flags.runnerId } : {},
18134
+ ...commandId ? { commandId } : {}
18135
+ });
18136
+ }
18137
+ case "close": {
18138
+ const [id, outcome] = need(rest, 2);
18139
+ if (outcome !== "cancelled" && outcome !== "failed")
18140
+ throw new UsageError(WORK_USAGE);
18141
+ return flows.close(id, outcome, {
18142
+ ...flags.reason ? { reason: flags.reason } : {},
18143
+ ...flags.acknowledge ? { acknowledge: flags.acknowledge.split(",").filter(Boolean) } : {},
18144
+ ...flags.generation !== void 0 ? { expectedGeneration: flags.generation } : {},
18145
+ ...commandId ? { commandId } : {}
18146
+ });
18147
+ }
18148
+ case "answer": {
18149
+ const [id, text] = rest;
18150
+ if (!id || rest.length > 2) throw new UsageError(WORK_USAGE);
18151
+ const decision = flags.decision;
18152
+ const answer = decision === "allow" || decision === "deny" ? { kind: "permission", decision } : text ? { kind: "text", text } : void 0;
18153
+ if (!answer || text && flags.decision) throw new UsageError(WORK_USAGE);
18154
+ return flows.answer(id, answer, {
18155
+ ...commandId ? { commandId } : {},
18156
+ ...env2.DRISP_HUB_TOKEN ? {} : { asRun: true }
18157
+ });
18158
+ }
18159
+ case "accept":
18160
+ case "reject": {
18161
+ const [id, packageId, reason] = need(rest, 3);
18162
+ return flows.decide(id, packageId, sub, reason, {
18163
+ ...flags.generation !== void 0 ? { expectedGeneration: flags.generation } : {},
18164
+ ...commandId ? { commandId } : {}
18165
+ });
18166
+ }
18167
+ case "brief":
18168
+ need(rest, 0);
18169
+ return flows.brief();
18170
+ case "forward": {
18171
+ const [id, note] = need(rest, 2);
18172
+ return flows.forward(id, note, commandId ? { commandId } : {});
18173
+ }
18174
+ case "incorporate": {
18175
+ const [id, version2] = need(rest, 2);
18176
+ if (flags.generation === void 0 || !/^[1-9][0-9]*$/.test(version2))
18177
+ throw new UsageError(WORK_USAGE);
18178
+ const toolUseId = flags.toolUseId ?? env2.DRISP_TOOL_USE_ID;
18179
+ return flows.incorporate(id, Number(version2), {
18180
+ generation: flags.generation,
18181
+ ...toolUseId ? { toolUseId } : {},
18182
+ ...commandId ? { commandId } : {}
18183
+ });
18184
+ }
18185
+ default: {
18186
+ const operation = UNSUPPORTED[sub];
18187
+ const reason = operation ? WorkFlows.unsupported(operation) : void 0;
18188
+ if (operation && reason)
18189
+ throw new WorkFlowError("unsupported_operation", { operation, reason });
18190
+ throw new UsageError(WORK_USAGE);
18191
+ }
18192
+ }
18193
+ }
18194
+
18195
+ // src/app/entry/doctorCommand.ts
18196
+ import process7 from "process";
18197
+
18198
+ // src/harnesses/claude/system/doctorEnvironment.ts
18199
+ import { execFileSync as execFileSync3 } from "child_process";
18200
+ import * as fs14 from "fs";
18201
+ import * as os5 from "os";
18202
+ import * as path15 from "path";
18203
+ var MANAGED_POLICY_KEYS = [
15655
18204
  "forceLoginMethod",
15656
18205
  "forceLoginOrgUUID",
15657
18206
  "allowManagedHooksOnly",
@@ -15748,14 +18297,14 @@ function tryRichAuth(claudeBinary) {
15748
18297
  function collectEnvironment(options = {}) {
15749
18298
  const platform2 = options.platform ?? process.platform;
15750
18299
  const env2 = options.env ?? process.env;
15751
- const homeDir = options.homeDir ?? os4.homedir();
18300
+ const homeDir = options.homeDir ?? os5.homedir();
15752
18301
  const cwd = options.cwd ?? process.cwd();
15753
18302
  const resolveBinary = options.resolveClaudeBinaryFn ?? resolveClaudeBinary;
15754
18303
  const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
15755
18304
  const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
15756
18305
  const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
15757
- const statFn = options.statFn ?? ((p) => fs12.statSync(p));
15758
- const readFileFn = options.readFileFn ?? ((p) => fs12.readFileSync(p, "utf8"));
18306
+ const statFn = options.statFn ?? ((p) => fs14.statSync(p));
18307
+ const readFileFn = options.readFileFn ?? ((p) => fs14.readFileSync(p, "utf8"));
15759
18308
  const settingsPaths = resolveClaudeSettingsSurfacePaths(
15760
18309
  homeDir,
15761
18310
  cwd,
@@ -15800,7 +18349,7 @@ function collectEnvironment(options = {}) {
15800
18349
  });
15801
18350
  let globalConfigPresent = false;
15802
18351
  try {
15803
- statFn(path12.join(homeDir, ".claude.json"));
18352
+ statFn(path15.join(homeDir, ".claude.json"));
15804
18353
  globalConfigPresent = true;
15805
18354
  } catch {
15806
18355
  globalConfigPresent = false;
@@ -16280,13 +18829,13 @@ function runRunsCommand(input) {
16280
18829
  // src/app/entry/cli.tsx
16281
18830
  import { jsx as jsx24 } from "react/jsx-runtime";
16282
18831
  function resolvePackageJsonPath(entryUrl) {
16283
- let currentDir = path13.dirname(fileURLToPath(entryUrl));
18832
+ let currentDir = path16.dirname(fileURLToPath(entryUrl));
16284
18833
  for (; ; ) {
16285
- const candidatePath = path13.join(currentDir, "package.json");
16286
- if (fs13.existsSync(candidatePath)) {
18834
+ const candidatePath = path16.join(currentDir, "package.json");
18835
+ if (fs15.existsSync(candidatePath)) {
16287
18836
  return candidatePath;
16288
18837
  }
16289
- const parentDir = path13.dirname(currentDir);
18838
+ const parentDir = path16.dirname(currentDir);
16290
18839
  if (parentDir === currentDir) {
16291
18840
  throw new Error("Could not locate package.json for CLI metadata.");
16292
18841
  }
@@ -16297,6 +18846,7 @@ var require2 = createRequire(import.meta.url);
16297
18846
  var { version } = require2(resolvePackageJsonPath(import.meta.url));
16298
18847
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
16299
18848
  "tracker",
18849
+ "work",
16300
18850
  "setup",
16301
18851
  "sessions",
16302
18852
  "resume",
@@ -16310,6 +18860,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
16310
18860
  "marketplace",
16311
18861
  "runner",
16312
18862
  "build",
18863
+ "drive",
16313
18864
  // Deprecated alias of `runner` (#188); removed in 0.7.0.
16314
18865
  "dashboard",
16315
18866
  "telemetry",
@@ -16336,7 +18887,7 @@ async function flushTelemetryOptOut(config) {
16336
18887
  deviceId: deviceId2,
16337
18888
  telemetryEnabled: config.telemetry,
16338
18889
  appVersion: version,
16339
- os: `${os5.platform()}-${os5.arch()}`
18890
+ os: `${os6.platform()}-${os6.arch()}`
16340
18891
  });
16341
18892
  trackTelemetryOptedOut();
16342
18893
  await shutdownTelemetry();
@@ -16367,6 +18918,11 @@ function printExecDryRunSummary(runtimeConfig, context) {
16367
18918
  lines.push(
16368
18919
  ` model: ${runtimeConfig.modelName ?? "(default)"} [${runtimeConfig.settingsProvenance?.model ?? "resolved"}]`
16369
18920
  );
18921
+ lines.push(
18922
+ ` gates: ${Object.entries(runtimeConfig.gates).map(
18923
+ ([gate, mode]) => `${gate}=${mode} [${runtimeConfig.settingsProvenance?.gates[gate] ?? "resolved"}]`
18924
+ ).join(", ")}`
18925
+ );
16370
18926
  lines.push(
16371
18927
  ` permission grace: ${runtimeConfig.permissionGraceMs}ms [${runtimeConfig.settingsProvenance?.permissionGraceMs ?? "resolved"}]`
16372
18928
  );
@@ -16459,6 +19015,9 @@ var cli = meow(
16459
19015
  build plan --input <path> Plan a Build inside an Orchestrator Run (prints one JSON object)
16460
19016
  tracker get <issue> Read the current native Drisp Issue
16461
19017
  tracker apply <json> Apply a scoped native Issue operation from a JSON file
19018
+ work <sub> Canonical Work and coordinated executions (authority, issue, plan, start,
19019
+ follow-up, inspect, hold, unhold, pause, resume, cancel, close, revise, reconcile, recover, answer, accept, reject,
19020
+ brief, forward, incorporate); drisp work help for usage
16462
19021
  build children List this Build's children and their interruptions
16463
19022
  build inspect <run> [cursor] Read a child's state and paged history
16464
19023
  build guide <run> "<text>" Guide a child or answer its delegated technical question
@@ -16468,6 +19027,8 @@ var cli = meow(
16468
19027
  build step <n> Report the Run's current step: writes the step run artifact ([--qa none|web|macos|both], --profile, --merge you, --sign-off <sha>, --complete --evidence, --verify)
16469
19028
  build check <name> -- <cmd> Run a command and record its outcome: writes the checks run artifact
16470
19029
  build merged Record the merged pull request: writes the pull_request run artifact
19030
+ drive merge <item> --worktree <path> [--branch <b>] [--pr <n>] [--timeout-ms <ms>]
19031
+ Merge a drive item: rebase, prove the checks, merge (prints one JSON outcome)
16471
19032
  telemetry [action] Manage anonymous telemetry (enable/disable/status)
16472
19033
  doctor Diagnose Claude headless setup (use with --harness=claude)
16473
19034
 
@@ -16488,6 +19049,9 @@ var cli = meow(
16488
19049
  --steer Queue a human steer for the head of the next Turn's prompt (repeatable; run mode)
16489
19050
  --answer allow | deny: answer the permission a parked run is waiting on; replayed
16490
19051
  into the re-issued call on --continue (run mode)
19052
+ --gate <gate>=ask|auto for this run only (repeatable; run mode). Gates:
19053
+ planApproval, merge, budget, irreversible. Only tightens: =auto has an
19054
+ effect only where the configured floor is already auto (docs/human-gates.md)
16491
19055
  --permission-grace-ms How long a permission no rule answers is held for an attached
16492
19056
  hub before it is deferred and the run parks (default 60000; run mode)
16493
19057
  --json Emit JSONL events to stdout (run mode)
@@ -16575,6 +19139,10 @@ var cli = meow(
16575
19139
  answer: {
16576
19140
  type: "string"
16577
19141
  },
19142
+ gate: {
19143
+ type: "string",
19144
+ isMultiple: true
19145
+ },
16578
19146
  permissionGraceMs: {
16579
19147
  type: "number"
16580
19148
  },
@@ -16606,6 +19174,22 @@ var cli = meow(
16606
19174
  isMultiple: true
16607
19175
  },
16608
19176
  profile: { type: "string" },
19177
+ // drisp drive merge (DRI-368)
19178
+ worktree: { type: "string" },
19179
+ branch: { type: "string" },
19180
+ pr: { type: "string" },
19181
+ // drisp work (DRI-342)
19182
+ reason: { type: "string" },
19183
+ generation: { type: "number" },
19184
+ activation: { type: "string" },
19185
+ commandId: { type: "string" },
19186
+ graceMs: { type: "number" },
19187
+ toolUseId: { type: "string" },
19188
+ decision: { type: "string" },
19189
+ changes: { type: "string" },
19190
+ session: { type: "string" },
19191
+ runnerId: { type: "string" },
19192
+ acknowledge: { type: "string" },
16609
19193
  testFirst: { type: "string" },
16610
19194
  review: { type: "string" },
16611
19195
  merge: { type: "string" },
@@ -16655,7 +19239,7 @@ var cli = meow(
16655
19239
  }
16656
19240
  );
16657
19241
  async function main() {
16658
- const projectDir = path13.resolve(cli.flags.projectDir);
19242
+ const projectDir = path16.resolve(cli.flags.projectDir);
16659
19243
  const [command, ...commandArgs] = cli.input;
16660
19244
  const isRunCommand = command === "run" || command === "exec";
16661
19245
  if (command && !KNOWN_COMMANDS.has(command)) {
@@ -16709,7 +19293,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16709
19293
  await exitWith(1);
16710
19294
  return;
16711
19295
  }
16712
- const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-VPBOZJY5.js");
19296
+ const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-MVTO43MW.js");
16713
19297
  const { waitUntilExit } = render(
16714
19298
  /* @__PURE__ */ jsx24(
16715
19299
  WorkflowInstallWizard,
@@ -16791,6 +19375,32 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16791
19375
  await exitWith(await runTrackerCommand(commandArgs));
16792
19376
  return;
16793
19377
  }
19378
+ if (command === "work") {
19379
+ await exitWith(
19380
+ await runWorkCommand(
19381
+ commandArgs,
19382
+ workCommandFlags(cli.flags)
19383
+ )
19384
+ );
19385
+ return;
19386
+ }
19387
+ if (command === "drive") {
19388
+ const [subcommand = "", ...positional] = commandArgs;
19389
+ await exitWith(
19390
+ await runDriveCommand({
19391
+ subcommand,
19392
+ positional,
19393
+ flags: {
19394
+ ...typeof cli.flags.worktree === "string" ? { worktree: cli.flags.worktree } : {},
19395
+ ...typeof cli.flags.branch === "string" ? { branch: cli.flags.branch } : {},
19396
+ ...typeof cli.flags.pr === "string" ? { pr: cli.flags.pr } : {},
19397
+ ...typeof cli.flags.timeoutMs === "number" ? { timeoutMs: cli.flags.timeoutMs } : {}
19398
+ },
19399
+ cwd: process.cwd()
19400
+ })
19401
+ );
19402
+ return;
19403
+ }
16794
19404
  if (command === "build") {
16795
19405
  const rawArgs = process.argv.slice(2);
16796
19406
  const dashIdx = rawArgs.indexOf("--");
@@ -16893,8 +19503,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16893
19503
  const showSetup = isRunCommand ? false : shouldShowSetup({
16894
19504
  cliInput: cli.input,
16895
19505
  setupComplete: globalConfig.setupComplete,
16896
- globalConfigExists: fs13.existsSync(
16897
- path13.join(os5.homedir(), ".config", "athena", "config.json")
19506
+ globalConfigExists: fs15.existsSync(
19507
+ path16.join(os6.homedir(), ".config", "athena", "config.json")
16898
19508
  )
16899
19509
  });
16900
19510
  let runtimeConfig;
@@ -16926,6 +19536,11 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16926
19536
  await exitWith(RUN_EXIT_CODE.USAGE);
16927
19537
  return;
16928
19538
  }
19539
+ if ((cli.flags.gate ?? []).length > 0 && !isRunCommand) {
19540
+ console.error("Error: --gate is only supported in run mode.");
19541
+ await exitWith(RUN_EXIT_CODE.USAGE);
19542
+ return;
19543
+ }
16929
19544
  if (cli.flags.dryRun) {
16930
19545
  if (!isRunCommand) {
16931
19546
  console.error("Error: --dry-run is only supported in exec mode.");
@@ -16951,7 +19566,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16951
19566
  deviceId: resolvedDeviceId,
16952
19567
  telemetryEnabled: globalConfig.telemetry,
16953
19568
  appVersion: version,
16954
- os: `${os5.platform()}-${os5.arch()}`
19569
+ os: `${os6.platform()}-${os6.arch()}`
16955
19570
  });
16956
19571
  trackAppLaunched({ version, harness: runtimeConfig.harness });
16957
19572
  if (!isRunCommand && isFirstRun && globalConfig.telemetry !== false) {
@@ -16972,7 +19587,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16972
19587
  timeoutMs: cli.flags.timeoutMs,
16973
19588
  verbose: cli.flags.verbose,
16974
19589
  permissionGraceMs: cli.flags.permissionGraceMs,
16975
- answer: cli.flags.answer
19590
+ answer: cli.flags.answer,
19591
+ gates: cli.flags.gate
16976
19592
  },
16977
19593
  runtimeConfig
16978
19594
  });