@drisp/cli 0.7.12 → 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,21 +173,131 @@ 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,
142
187
  teamIdentityPath,
143
188
  todoGlyphSet,
189
+ trackerImportPreviewSchema,
190
+ trackerIssueSchema,
191
+ trackerOperationSchema,
192
+ trackerSnapshotSchema,
144
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,
145
298
  wrapText,
146
299
  writeIdentityOnce
147
- } from "./chunk-UUUCDIHI.js";
300
+ } from "./chunk-HCTFAAGU.js";
148
301
  import {
149
302
  generateId as generateId2
150
303
  } from "./chunk-PQIB66SG.js";
@@ -162,6 +315,7 @@ import {
162
315
  CANNOT_PASS_TEMPLATE,
163
316
  DESCRIPTION_TEMPLATE,
164
317
  GATES,
318
+ GATE_NAMES,
165
319
  LAYOUT_MARKERS,
166
320
  MERGE_SIGN_OFF,
167
321
  NO_GATE_TEXT_TEMPLATE,
@@ -172,6 +326,7 @@ import {
172
326
  WORKFLOW_MD_TEMPLATE,
173
327
  collectMcpServersWithOptions,
174
328
  createSteerQueue,
329
+ describeIgnoredGate,
175
330
  deserializeRunMemory,
176
331
  external_exports,
177
332
  findMarketplaceRepoDir,
@@ -185,6 +340,7 @@ import {
185
340
  listMarketplaceWorkflows,
186
341
  listMarketplaceWorkflowsFromRepo,
187
342
  listWorkflows,
343
+ parseGateFlags,
188
344
  parseWorkflowConfig,
189
345
  projectConfigPath,
190
346
  pullMarketplaceRepo,
@@ -196,6 +352,7 @@ import {
196
352
  resolveWorkflow,
197
353
  resolveWorkflowInstall,
198
354
  resolveWorkflowMarketplaceSource,
355
+ tightenGates,
199
356
  updateWorkflow,
200
357
  updateWorkflows,
201
358
  verifyRendered,
@@ -203,7 +360,7 @@ import {
203
360
  workflowRegistryDir,
204
361
  writeGlobalConfig,
205
362
  writeProjectConfig
206
- } from "./chunk-SACVOS6T.js";
363
+ } from "./chunk-GGAPZULS.js";
207
364
  import {
208
365
  __toESM
209
366
  } from "./chunk-G3PMV62Z.js";
@@ -211,10 +368,10 @@ import {
211
368
  // src/app/entry/cli.tsx
212
369
  import { render } from "ink";
213
370
  import meow from "meow";
214
- import fs13 from "fs";
371
+ import fs15 from "fs";
215
372
  import { createRequire } from "module";
216
- import os5 from "os";
217
- import path13 from "path";
373
+ import os6 from "os";
374
+ import path16 from "path";
218
375
  import { fileURLToPath } from "url";
219
376
 
220
377
  // src/app/shell/sessionUiState.ts
@@ -638,7 +795,7 @@ function reduceSessionUiState(state, action, ctx) {
638
795
 
639
796
  // src/app/shell/sessionUiController.ts
640
797
  function createSessionUiController(update, context) {
641
- const dispatch = (...actions) => update((state) => {
798
+ const dispatch2 = (...actions) => update((state) => {
642
799
  const ctx = context();
643
800
  return resolveSessionUiState(
644
801
  actions.reduce(
@@ -649,61 +806,61 @@ function createSessionUiController(update, context) {
649
806
  );
650
807
  });
651
808
  return {
652
- focus: (focusMode) => dispatch({ type: "set_focus_mode", focusMode }),
653
- cycleFocus: () => dispatch({ type: "cycle_focus" }),
809
+ focus: (focusMode) => dispatch2({ type: "set_focus_mode", focusMode }),
810
+ cycleFocus: () => dispatch2({ type: "cycle_focus" }),
654
811
  input: (mode) => {
655
812
  switch (mode) {
656
813
  case "cancel":
657
- dispatch({ type: "cancel_input" });
814
+ dispatch2({ type: "cancel_input" });
658
815
  break;
659
816
  case "command-open":
660
- dispatch({ type: "open_command_input" });
817
+ dispatch2({ type: "open_command_input" });
661
818
  break;
662
819
  case "search-open":
663
- dispatch({ type: "open_search_input" });
820
+ dispatch2({ type: "open_search_input" });
664
821
  break;
665
822
  case "normal-open":
666
- dispatch({ type: "open_normal_input" });
823
+ dispatch2({ type: "open_normal_input" });
667
824
  break;
668
825
  case "search":
669
826
  case "command":
670
827
  case "normal":
671
- dispatch({ type: "set_input_mode", inputMode: mode });
828
+ dispatch2({ type: "set_input_mode", inputMode: mode });
672
829
  }
673
830
  },
674
- cycleHints: () => dispatch({ type: "cycle_hints_forced" }),
675
- 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 }),
676
833
  search: {
677
- edit: (query) => dispatch({ type: "set_search_query", query }),
678
- submit: (query, firstMatchIndex) => dispatch({ type: "submit_search_query", query, firstMatchIndex }),
679
- step: (direction, matches) => dispatch({ type: "step_search_match", direction, matches }),
680
- 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" })
681
838
  },
682
839
  navigate: (panel, destination) => {
683
840
  if (typeof destination === "number")
684
- dispatch(
841
+ dispatch2(
685
842
  panel === "feed" ? { type: "move_feed_cursor", delta: destination } : { type: "scroll_message_viewport", delta: destination }
686
843
  );
687
844
  else if (panel === "feed")
688
- dispatch({
845
+ dispatch2({
689
846
  type: destination === "top" ? "jump_feed_top" : "jump_feed_tail"
690
847
  });
691
848
  else
692
- dispatch({
849
+ dispatch2({
693
850
  type: destination === "top" ? "jump_message_top" : "jump_message_tail"
694
851
  });
695
852
  },
696
853
  // Wheel navigation selects its panel and moves it in the same update.
697
- scrollPanel: (panel, delta) => dispatch(
854
+ scrollPanel: (panel, delta) => dispatch2(
698
855
  { type: "set_focus_mode", focusMode: panel },
699
856
  panel === "feed" ? { type: "move_feed_cursor", delta } : { type: "scroll_message_viewport", delta }
700
857
  ),
701
- reveal: (cursor) => dispatch({ type: "reveal_feed_entry", cursor }),
702
- 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 }),
703
860
  todo: {
704
- toggle: () => dispatch({ type: "toggle_todo_visible" }),
705
- move: (delta) => dispatch({ type: "move_todo_cursor", delta }),
706
- 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 })
707
864
  }
708
865
  };
709
866
  }
@@ -2807,7 +2964,7 @@ function useWorkflowSessionController(base, input) {
2807
2964
  }
2808
2965
  setIsRunning(false);
2809
2966
  }, [base, cancelCurrentRun]);
2810
- const spawn = useCallback6(
2967
+ const spawn2 = useCallback6(
2811
2968
  async (prompt, continuation, _configOverride) => {
2812
2969
  await cancelCurrentRun();
2813
2970
  const latest = input.store?.getLatestRun();
@@ -2879,7 +3036,7 @@ function useWorkflowSessionController(base, input) {
2879
3036
  }, []);
2880
3037
  return {
2881
3038
  ...base,
2882
- startTurn: spawn,
3039
+ startTurn: spawn2,
2883
3040
  isRunning,
2884
3041
  interrupt,
2885
3042
  kill,
@@ -3787,7 +3944,7 @@ function textInputReducer(state, action) {
3787
3944
  }
3788
3945
  function useTextInput(options = {}) {
3789
3946
  const { onChange, onSubmit, isActive = true } = options;
3790
- const [state, dispatch] = useReducer(textInputReducer, {
3947
+ const [state, dispatch2] = useReducer(textInputReducer, {
3791
3948
  value: "",
3792
3949
  cursorOffset: 0
3793
3950
  });
@@ -3806,7 +3963,7 @@ function useTextInput(options = {}) {
3806
3963
  onChangeRef.current?.(state.value);
3807
3964
  }, [state.value]);
3808
3965
  const setValue = useCallback7((newValue) => {
3809
- dispatch({ type: "set-value", value: newValue });
3966
+ dispatch2({ type: "set-value", value: newValue });
3810
3967
  }, []);
3811
3968
  const { stdin } = useStdin();
3812
3969
  const isForwardDeleteRef = useRef5(false);
@@ -3846,47 +4003,47 @@ function useTextInput(options = {}) {
3846
4003
  if (key.return) {
3847
4004
  const { value: val, cursorOffset: cur } = stateRef.current;
3848
4005
  if (cur > 0 && val[cur - 1] === "\\") {
3849
- dispatch({ type: "newline-escape" });
4006
+ dispatch2({ type: "newline-escape" });
3850
4007
  } else {
3851
4008
  onSubmitRef.current?.(val);
3852
4009
  }
3853
4010
  return;
3854
4011
  }
3855
4012
  if (key.leftArrow) {
3856
- dispatch(key.ctrl ? { type: "move-home" } : { type: "move-left" });
4013
+ dispatch2(key.ctrl ? { type: "move-home" } : { type: "move-left" });
3857
4014
  return;
3858
4015
  }
3859
4016
  if (key.rightArrow) {
3860
- dispatch(key.ctrl ? { type: "move-end" } : { type: "move-right" });
4017
+ dispatch2(key.ctrl ? { type: "move-end" } : { type: "move-right" });
3861
4018
  return;
3862
4019
  }
3863
4020
  if (key.home) {
3864
- dispatch({ type: "move-home" });
4021
+ dispatch2({ type: "move-home" });
3865
4022
  return;
3866
4023
  }
3867
4024
  if (key.end) {
3868
- dispatch({ type: "move-end" });
4025
+ dispatch2({ type: "move-end" });
3869
4026
  return;
3870
4027
  }
3871
4028
  if (key.ctrl) {
3872
- if (input === "a") dispatch({ type: "move-home" });
3873
- else if (input === "e") dispatch({ type: "move-end" });
3874
- else if (input === "w") dispatch({ type: "delete-word-back" });
3875
- else if (input === "u") dispatch({ type: "clear-line" });
3876
- 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" });
3877
4034
  return;
3878
4035
  }
3879
4036
  if (key.backspace || key.delete) {
3880
4037
  if (isForwardDeleteRef.current) {
3881
4038
  isForwardDeleteRef.current = false;
3882
- dispatch({ type: "delete-forward" });
4039
+ dispatch2({ type: "delete-forward" });
3883
4040
  } else {
3884
- dispatch({ type: "backspace" });
4041
+ dispatch2({ type: "backspace" });
3885
4042
  }
3886
4043
  return;
3887
4044
  }
3888
4045
  if (input && !key.meta) {
3889
- dispatch({ type: "insert", char: input });
4046
+ dispatch2({ type: "insert", char: input });
3890
4047
  }
3891
4048
  } finally {
3892
4049
  done();
@@ -3898,7 +4055,7 @@ function useTextInput(options = {}) {
3898
4055
  value: state.value,
3899
4056
  cursorOffset: state.cursorOffset,
3900
4057
  setValue,
3901
- dispatch
4058
+ dispatch: dispatch2
3902
4059
  };
3903
4060
  }
3904
4061
 
@@ -4003,7 +4160,7 @@ var ShellInputImpl = forwardRef(function ShellInput({
4003
4160
  commandSuggestionsEnabled,
4004
4161
  wrapSuggestionLine
4005
4162
  }, ref) {
4006
- const { value, cursorOffset, setValue, dispatch } = useTextInput({
4163
+ const { value, cursorOffset, setValue, dispatch: dispatch2 } = useTextInput({
4007
4164
  onChange,
4008
4165
  onSubmit,
4009
4166
  isActive: isInputActive
@@ -4128,7 +4285,7 @@ var ShellInputImpl = forwardRef(function ShellInput({
4128
4285
  const recalled = historyBackRef.current?.(currentValue);
4129
4286
  if (recalled !== void 0) applyProgrammaticValue(recalled);
4130
4287
  } else {
4131
- dispatch({ type: "move-up", width: inputContentWidth });
4288
+ dispatch2({ type: "move-up", width: inputContentWidth });
4132
4289
  }
4133
4290
  return;
4134
4291
  }
@@ -4142,11 +4299,11 @@ var ShellInputImpl = forwardRef(function ShellInput({
4142
4299
  const recalled = historyForwardRef.current?.();
4143
4300
  if (recalled !== void 0) applyProgrammaticValue(recalled);
4144
4301
  } else {
4145
- dispatch({ type: "move-down", width: inputContentWidth });
4302
+ dispatch2({ type: "move-down", width: inputContentWidth });
4146
4303
  }
4147
4304
  }
4148
4305
  },
4149
- [applyProgrammaticValue, dispatch, inputContentWidth, showSuggestions]
4306
+ [applyProgrammaticValue, dispatch2, inputContentWidth, showSuggestions]
4150
4307
  );
4151
4308
  useInput7(handleArrows, { isActive: isInputActive });
4152
4309
  const lines = renderInputLines(
@@ -5333,14 +5490,14 @@ function extractWebSearch(_input, response) {
5333
5490
  return { type: "text", content: extractTextContent(response), maxLines: 10 };
5334
5491
  }
5335
5492
  function extractNotebookEdit(input, _response) {
5336
- const path14 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5493
+ const path17 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5337
5494
  const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
5338
5495
  const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
5339
- if (!source) return { type: "text", content: `${mode} cell in ${path14}` };
5496
+ if (!source) return { type: "text", content: `${mode} cell in ${path17}` };
5340
5497
  return {
5341
5498
  type: "code",
5342
5499
  content: source,
5343
- language: detectLanguage(path14),
5500
+ language: detectLanguage(path17),
5344
5501
  maxLines: 10
5345
5502
  };
5346
5503
  }
@@ -5866,10 +6023,10 @@ function extractToolSubject(toolName, toolInput) {
5866
6023
  const parts = [];
5867
6024
  const pattern = str(input, "pattern");
5868
6025
  const glob = str(input, "glob");
5869
- const path14 = str(input, "path");
6026
+ const path17 = str(input, "path");
5870
6027
  if (pattern) parts.push(`"${pattern}"`);
5871
6028
  if (glob) parts.push(glob);
5872
- if (path14) parts.push(`in ${path14}`);
6029
+ if (path17) parts.push(`in ${path17}`);
5873
6030
  return parts.length > 0 ? parts.join(" ") : void 0;
5874
6031
  }
5875
6032
  case "Skill":
@@ -5902,9 +6059,13 @@ function eventLabel(event) {
5902
6059
  return event.data.scope === "subagent" ? "Subagent" : "Agent";
5903
6060
  case "user.prompt":
5904
6061
  return "User";
6062
+ case "system.wake":
6063
+ return "Wake";
5905
6064
  case "subagent.start":
5906
6065
  case "subagent.stop":
5907
6066
  return `Subagent \xB7 ${event.data.agent_type}`;
6067
+ case "subagent.task":
6068
+ return `Subagent \xB7 ${event.data.subagent_type ?? event.data.task_id}`;
5908
6069
  case "run.start":
5909
6070
  return "Run Start";
5910
6071
  case "run.end":
@@ -6014,11 +6175,17 @@ function renderDetailLines(event, width, pairedPostEvent, theme = darkTheme) {
6014
6175
  const content = renderMarkdownToLines(event.data.message, cw);
6015
6176
  return buildResult(header, content, false);
6016
6177
  }
6017
- case "user.prompt": {
6178
+ case "user.prompt":
6179
+ case "system.wake": {
6018
6180
  const header = buildCompactHeader(event, width, { theme });
6019
6181
  const content = renderMarkdownToLines(event.data.prompt, cw);
6020
6182
  return buildResult(header, content, false);
6021
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
+ }
6022
6189
  case "tool.post":
6023
6190
  case "tool.delta":
6024
6191
  case "tool.failure":
@@ -8440,8 +8607,8 @@ function formatStatsSnapshot(snapshot) {
8440
8607
  function truncateSessionId(id, maxWidth) {
8441
8608
  if (id.length <= maxWidth) return id;
8442
8609
  if (maxWidth >= 12) {
8443
- const tail = id.slice(-6);
8444
- return id.slice(0, maxWidth - 7) + "\u2026" + tail;
8610
+ const tail2 = id.slice(-6);
8611
+ return id.slice(0, maxWidth - 7) + "\u2026" + tail2;
8445
8612
  }
8446
8613
  const alphanumeric = id.replace(/[^a-zA-Z0-9]/g, "").slice(-4);
8447
8614
  return "S" + (alphanumeric || "\u2013");
@@ -9951,10 +10118,11 @@ function deriveStartupTimeoutFailure(args) {
9951
10118
 
9952
10119
  // src/app/shell/resolveAllowedTools.ts
9953
10120
  var BASELINE_ALLOWED_TOOLS = ["mcp__*"];
9954
- function resolveAllowedTools(harness, allowedTools) {
10121
+ function resolveAllowedTools(harness, allowedTools, workflowAllowedTools = []) {
9955
10122
  const extras = [
9956
10123
  ...BASELINE_ALLOWED_TOOLS,
9957
- ...resolveHarnessAdapter(harness).capabilities.extraAllowedTools
10124
+ ...resolveHarnessAdapter(harness).capabilities.extraAllowedTools,
10125
+ ...workflowAllowedTools
9958
10126
  ];
9959
10127
  const merged = [...allowedTools ?? []];
9960
10128
  for (const tool of extras) {
@@ -11776,7 +11944,8 @@ function App({
11776
11944
  workflow: runtimeState.workflow,
11777
11945
  allowedTools: resolveAllowedTools(
11778
11946
  runtimeState.harness,
11779
- runtimeState.isolation?.allowedTools
11947
+ runtimeState.isolation?.allowedTools,
11948
+ runtimeState.workflow?.allowedTools
11780
11949
  ),
11781
11950
  athenaSessionId,
11782
11951
  children: /* @__PURE__ */ jsx23(
@@ -12206,6 +12375,19 @@ async function runExecCommand(input, deps = {}) {
12206
12375
  );
12207
12376
  return RUN_EXIT_CODE.USAGE;
12208
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
+ }
12209
12391
  let continueResolution;
12210
12392
  try {
12211
12393
  continueResolution = resolveResumeTarget({
@@ -12258,6 +12440,16 @@ async function runExecCommand(input, deps = {}) {
12258
12440
  ephemeral: input.flags.ephemeral,
12259
12441
  timeoutMs: input.flags.timeoutMs,
12260
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
+ },
12261
12453
  ...answer !== void 0 ? { storedAnswer: localAnswerDecision(answer) } : {},
12262
12454
  // Reporting-only summary: strip to name + source layer so secret-bearing
12263
12455
  // MCP env/command/args and skill paths never reach the startup notice or
@@ -13100,7 +13292,7 @@ var thisMachine = (installationId) => ({
13100
13292
  os: `${platform()} ${release()}`.slice(0, 64),
13101
13293
  softwareVersion: runnerSoftwareVersion
13102
13294
  });
13103
- var post = (origin, path14, body) => fetch(new URL(path14, origin), {
13295
+ var post = (origin, path17, body) => fetch(new URL(path17, origin), {
13104
13296
  method: "POST",
13105
13297
  headers: { "Content-Type": "application/json" },
13106
13298
  body: JSON.stringify(body)
@@ -13119,8 +13311,8 @@ async function alreadyConnected(existing, { forTeam, ownIdentity }) {
13119
13311
  const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
13120
13312
  const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
13121
13313
  if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
13122
- const path14 = (own ?? forTeam(existing.orgId)[0])?.path;
13123
- 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 } : {} };
13124
13316
  }
13125
13317
  async function connectWithCode({
13126
13318
  code,
@@ -13192,20 +13384,20 @@ async function saveIdentity(origin, credential, { single, forTeam }, directory)
13192
13384
  name: credential.name
13193
13385
  });
13194
13386
  const replaced = [];
13195
- for (const { path: path15 } of forTeam(credential.orgId)) {
13196
- const aside = `${path15}.replaced-${Date.now()}`;
13197
- 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);
13198
13390
  replaced.push(aside);
13199
13391
  }
13200
- const path14 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13392
+ const path17 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13201
13393
  try {
13202
- await writeIdentityOnce(path14, JSON.stringify(config, null, 2) + "\n");
13394
+ await writeIdentityOnce(path17, JSON.stringify(config, null, 2) + "\n");
13203
13395
  } catch (error) {
13204
13396
  if (error.code === "EEXIST")
13205
- 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.`);
13206
13398
  throw error;
13207
13399
  }
13208
- return { status: "connected", config, path: path14, replaced };
13400
+ return { status: "connected", config, path: path17, replaced };
13209
13401
  }
13210
13402
  function pathSegment(value) {
13211
13403
  const segment = encodeURIComponent(value);
@@ -13245,7 +13437,7 @@ async function refuseExisting(target) {
13245
13437
  throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
13246
13438
  }
13247
13439
  async function disconnectRunner({
13248
- identity: { path: path14, config },
13440
+ identity: { path: path17, config },
13249
13441
  serviceManager,
13250
13442
  othersRemain
13251
13443
  }) {
@@ -13278,7 +13470,7 @@ async function disconnectRunner({
13278
13470
  await restore();
13279
13471
  throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
13280
13472
  }
13281
- await forgetRunner(path14);
13473
+ await forgetRunner(path17);
13282
13474
  if (othersRemain) await restore();
13283
13475
  else await serviceManager?.remove();
13284
13476
  return result;
@@ -13431,18 +13623,18 @@ async function runRunnerCommandV2(input, options = {}) {
13431
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.`
13432
13624
  );
13433
13625
  }
13434
- const { config, path: path14, replaced } = outcome;
13626
+ const { config, path: path17, replaced } = outcome;
13435
13627
  for (const aside of replaced)
13436
13628
  process.stdout.write(
13437
13629
  `The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
13438
13630
  `
13439
13631
  );
13440
13632
  process.stdout.write(
13441
- `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path14}.
13633
+ `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path17}.
13442
13634
  `
13443
13635
  );
13444
13636
  if (services) {
13445
- const service = await ensureService(services, config, path14);
13637
+ const service = await ensureService(services, config, path17);
13446
13638
  process.stdout.write(`${serviceStarted(service)}
13447
13639
  `);
13448
13640
  return 0;
@@ -13451,7 +13643,7 @@ async function runRunnerCommandV2(input, options = {}) {
13451
13643
  process.stdout.write(
13452
13644
  "No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
13453
13645
  );
13454
- await runConfiguredRunner(path14, options.signal);
13646
+ await runConfiguredRunner(path17, options.signal);
13455
13647
  return 0;
13456
13648
  }
13457
13649
  case "disconnect": {
@@ -13522,9 +13714,9 @@ async function runRunnerCommandV2(input, options = {}) {
13522
13714
  configured: true,
13523
13715
  // One entry per Team this machine serves; credentials are never printed.
13524
13716
  teams: identities.map(
13525
- ({ path: path14, config: { token: _token, ...config } }) => ({
13717
+ ({ path: path17, config: { token: _token, ...config } }) => ({
13526
13718
  ...config,
13527
- path: path14
13719
+ path: path17
13528
13720
  })
13529
13721
  ),
13530
13722
  service: service ? {
@@ -13832,7 +14024,368 @@ import { randomUUID as randomUUID2 } from "crypto";
13832
14024
  import fs7 from "fs";
13833
14025
  import path7 from "path";
13834
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
+
13835
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
+ }
13836
14389
  var runArtifactSchema = external_exports.object({ name: external_exports.string(), seq: external_exports.number().int(), json: external_exports.json() });
13837
14390
  var githubRepositorySchema = external_exports.object({
13838
14391
  installationId: external_exports.number().int().positive(),
@@ -13842,13 +14395,15 @@ var githubRepositorySchema = external_exports.object({
13842
14395
  defaultBranch: external_exports.string()
13843
14396
  });
13844
14397
  var ClientError = class extends Error {
13845
- constructor(code, status) {
14398
+ constructor(code, status, details = {}) {
13846
14399
  super(code);
13847
14400
  this.code = code;
13848
14401
  this.status = status;
14402
+ this.details = details;
13849
14403
  }
13850
14404
  code;
13851
14405
  status;
14406
+ details;
13852
14407
  };
13853
14408
  var DrispClient = class _DrispClient {
13854
14409
  constructor(baseUrl, orgId, token) {
@@ -13873,26 +14428,710 @@ var DrispClient = class _DrispClient {
13873
14428
  if (!match) throw new Error("invalid_hub_url");
13874
14429
  return new _DrispClient(url.origin, decodeURIComponent(match[1]), async () => token);
13875
14430
  }
13876
- async request(path14, body) {
14431
+ async request(path17, body) {
13877
14432
  const response = await fetch(
13878
- `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path14}`,
14433
+ `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path17}`,
13879
14434
  {
13880
14435
  method: body === void 0 ? "GET" : "POST",
13881
14436
  headers: {
13882
14437
  Authorization: `Bearer ${await this.token()}`,
13883
- "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)
13884
14441
  },
13885
14442
  ...body === void 0 ? {} : { body: JSON.stringify(body) }
13886
14443
  }
13887
14444
  );
13888
14445
  const result = await response.json();
13889
- if (!response.ok)
13890
- 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
+ }
13891
14450
  return result;
13892
14451
  }
13893
14452
  submit(command) {
13894
14453
  return this.request("/commands", commandSchema.parse(command));
13895
14454
  }
14455
+ async tracker() {
14456
+ return trackerSnapshotSchema.parse(await this.request("/tracker"));
14457
+ }
14458
+ async trackerIssue(id) {
14459
+ return trackerIssueSchema.parse(await this.request(`/tracker/issues/${encodeURIComponent(id)}`));
14460
+ }
14461
+ trackerOperation(operation) {
14462
+ return this.request("/tracker", trackerOperationSchema.parse(operation));
14463
+ }
14464
+ async previewLinearImport(input) {
14465
+ return trackerImportPreviewSchema.parse(await this.trackerOperation({ kind: "import.preview", input }));
14466
+ }
14467
+ async commitLinearImport(previewId) {
14468
+ return trackerSnapshotSchema.parse(await this.trackerOperation({ kind: "import.commit", previewId }));
14469
+ }
14470
+ startIssue(id, command) {
14471
+ return this.request(`/tracker/issues/${encodeURIComponent(id)}/start`, commandSchema.parse(command));
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 ──
13896
15135
  /** The recent Runs; with a filter, every Run carrying that key or every child of that Run, newest first. */
13897
15136
  async runs(filter) {
13898
15137
  const query = !filter ? "" : "key" in filter ? `?key=${encodeURIComponent(filter.key)}` : `?parent=${encodeURIComponent(filter.parent)}`;
@@ -15585,39 +16824,1407 @@ ${USAGE3}
15585
16824
  }
15586
16825
  }
15587
16826
 
15588
- // src/app/entry/doctorCommand.ts
15589
- import process7 from "process";
16827
+ // src/app/entry/driveCommand.ts
16828
+ import path14 from "path";
15590
16829
 
15591
- // src/harnesses/claude/system/doctorEnvironment.ts
15592
- import { execFileSync as execFileSync3 } from "child_process";
15593
- import * as fs12 from "fs";
15594
- import * as os4 from "os";
15595
- import * as path12 from "path";
15596
- var MANAGED_POLICY_KEYS = [
15597
- "forceLoginMethod",
15598
- "forceLoginOrgUUID",
15599
- "allowManagedHooksOnly",
15600
- "allowManagedMcpServersOnly",
15601
- "allowManagedPermissionRulesOnly",
15602
- "disableAutoMode",
15603
- "disableBypassPermissionsMode",
15604
- "forceRemoteSettingsRefresh",
15605
- "availableModels",
15606
- "model",
15607
- "apiKeyHelper",
15608
- "awsCredentialExport"
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"
15609
16845
  ];
15610
- function inspectSettingsFile(scope, filePath, statFn, readFileFn) {
15611
- let stat;
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) {
15612
16874
  try {
15613
- stat = statFn(filePath);
15614
- } catch {
15615
- return { scope, path: filePath, present: false };
16875
+ return await new DriveMerge(input, deps).run();
16876
+ } catch (error) {
16877
+ if (error instanceof Stop) return error.result;
16878
+ throw error;
15616
16879
  }
15617
- const info = {
16880
+ }
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",
15618
16901
  scope,
15619
- path: filePath,
15620
- present: true,
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 = [
18204
+ "forceLoginMethod",
18205
+ "forceLoginOrgUUID",
18206
+ "allowManagedHooksOnly",
18207
+ "allowManagedMcpServersOnly",
18208
+ "allowManagedPermissionRulesOnly",
18209
+ "disableAutoMode",
18210
+ "disableBypassPermissionsMode",
18211
+ "forceRemoteSettingsRefresh",
18212
+ "availableModels",
18213
+ "model",
18214
+ "apiKeyHelper",
18215
+ "awsCredentialExport"
18216
+ ];
18217
+ function inspectSettingsFile(scope, filePath, statFn, readFileFn) {
18218
+ let stat;
18219
+ try {
18220
+ stat = statFn(filePath);
18221
+ } catch {
18222
+ return { scope, path: filePath, present: false };
18223
+ }
18224
+ const info = {
18225
+ scope,
18226
+ path: filePath,
18227
+ present: true,
15621
18228
  sizeBytes: stat.size,
15622
18229
  mtime: stat.mtime.toISOString()
15623
18230
  };
@@ -15690,14 +18297,14 @@ function tryRichAuth(claudeBinary) {
15690
18297
  function collectEnvironment(options = {}) {
15691
18298
  const platform2 = options.platform ?? process.platform;
15692
18299
  const env2 = options.env ?? process.env;
15693
- const homeDir = options.homeDir ?? os4.homedir();
18300
+ const homeDir = options.homeDir ?? os5.homedir();
15694
18301
  const cwd = options.cwd ?? process.cwd();
15695
18302
  const resolveBinary = options.resolveClaudeBinaryFn ?? resolveClaudeBinary;
15696
18303
  const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
15697
18304
  const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
15698
18305
  const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
15699
- const statFn = options.statFn ?? ((p) => fs12.statSync(p));
15700
- 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"));
15701
18308
  const settingsPaths = resolveClaudeSettingsSurfacePaths(
15702
18309
  homeDir,
15703
18310
  cwd,
@@ -15742,7 +18349,7 @@ function collectEnvironment(options = {}) {
15742
18349
  });
15743
18350
  let globalConfigPresent = false;
15744
18351
  try {
15745
- statFn(path12.join(homeDir, ".claude.json"));
18352
+ statFn(path15.join(homeDir, ".claude.json"));
15746
18353
  globalConfigPresent = true;
15747
18354
  } catch {
15748
18355
  globalConfigPresent = false;
@@ -16222,13 +18829,13 @@ function runRunsCommand(input) {
16222
18829
  // src/app/entry/cli.tsx
16223
18830
  import { jsx as jsx24 } from "react/jsx-runtime";
16224
18831
  function resolvePackageJsonPath(entryUrl) {
16225
- let currentDir = path13.dirname(fileURLToPath(entryUrl));
18832
+ let currentDir = path16.dirname(fileURLToPath(entryUrl));
16226
18833
  for (; ; ) {
16227
- const candidatePath = path13.join(currentDir, "package.json");
16228
- if (fs13.existsSync(candidatePath)) {
18834
+ const candidatePath = path16.join(currentDir, "package.json");
18835
+ if (fs15.existsSync(candidatePath)) {
16229
18836
  return candidatePath;
16230
18837
  }
16231
- const parentDir = path13.dirname(currentDir);
18838
+ const parentDir = path16.dirname(currentDir);
16232
18839
  if (parentDir === currentDir) {
16233
18840
  throw new Error("Could not locate package.json for CLI metadata.");
16234
18841
  }
@@ -16238,6 +18845,8 @@ function resolvePackageJsonPath(entryUrl) {
16238
18845
  var require2 = createRequire(import.meta.url);
16239
18846
  var { version } = require2(resolvePackageJsonPath(import.meta.url));
16240
18847
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
18848
+ "tracker",
18849
+ "work",
16241
18850
  "setup",
16242
18851
  "sessions",
16243
18852
  "resume",
@@ -16251,6 +18860,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
16251
18860
  "marketplace",
16252
18861
  "runner",
16253
18862
  "build",
18863
+ "drive",
16254
18864
  // Deprecated alias of `runner` (#188); removed in 0.7.0.
16255
18865
  "dashboard",
16256
18866
  "telemetry",
@@ -16277,7 +18887,7 @@ async function flushTelemetryOptOut(config) {
16277
18887
  deviceId: deviceId2,
16278
18888
  telemetryEnabled: config.telemetry,
16279
18889
  appVersion: version,
16280
- os: `${os5.platform()}-${os5.arch()}`
18890
+ os: `${os6.platform()}-${os6.arch()}`
16281
18891
  });
16282
18892
  trackTelemetryOptedOut();
16283
18893
  await shutdownTelemetry();
@@ -16308,6 +18918,11 @@ function printExecDryRunSummary(runtimeConfig, context) {
16308
18918
  lines.push(
16309
18919
  ` model: ${runtimeConfig.modelName ?? "(default)"} [${runtimeConfig.settingsProvenance?.model ?? "resolved"}]`
16310
18920
  );
18921
+ lines.push(
18922
+ ` gates: ${Object.entries(runtimeConfig.gates).map(
18923
+ ([gate, mode]) => `${gate}=${mode} [${runtimeConfig.settingsProvenance?.gates[gate] ?? "resolved"}]`
18924
+ ).join(", ")}`
18925
+ );
16311
18926
  lines.push(
16312
18927
  ` permission grace: ${runtimeConfig.permissionGraceMs}ms [${runtimeConfig.settingsProvenance?.permissionGraceMs ?? "resolved"}]`
16313
18928
  );
@@ -16398,6 +19013,11 @@ var cli = meow(
16398
19013
  runner [sub] Run the runner that connects this machine to a Team and executes its Runs
16399
19014
  (no subcommand: foreground; connect [code] --hub <url> [--replace], disconnect [team], status, restart, recover, help)
16400
19015
  build plan --input <path> Plan a Build inside an Orchestrator Run (prints one JSON object)
19016
+ tracker get <issue> Read the current native Drisp Issue
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
16401
19021
  build children List this Build's children and their interruptions
16402
19022
  build inspect <run> [cursor] Read a child's state and paged history
16403
19023
  build guide <run> "<text>" Guide a child or answer its delegated technical question
@@ -16407,6 +19027,8 @@ var cli = meow(
16407
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)
16408
19028
  build check <name> -- <cmd> Run a command and record its outcome: writes the checks run artifact
16409
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)
16410
19032
  telemetry [action] Manage anonymous telemetry (enable/disable/status)
16411
19033
  doctor Diagnose Claude headless setup (use with --harness=claude)
16412
19034
 
@@ -16427,6 +19049,9 @@ var cli = meow(
16427
19049
  --steer Queue a human steer for the head of the next Turn's prompt (repeatable; run mode)
16428
19050
  --answer allow | deny: answer the permission a parked run is waiting on; replayed
16429
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)
16430
19055
  --permission-grace-ms How long a permission no rule answers is held for an attached
16431
19056
  hub before it is deferred and the run parks (default 60000; run mode)
16432
19057
  --json Emit JSONL events to stdout (run mode)
@@ -16514,6 +19139,10 @@ var cli = meow(
16514
19139
  answer: {
16515
19140
  type: "string"
16516
19141
  },
19142
+ gate: {
19143
+ type: "string",
19144
+ isMultiple: true
19145
+ },
16517
19146
  permissionGraceMs: {
16518
19147
  type: "number"
16519
19148
  },
@@ -16545,6 +19174,22 @@ var cli = meow(
16545
19174
  isMultiple: true
16546
19175
  },
16547
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" },
16548
19193
  testFirst: { type: "string" },
16549
19194
  review: { type: "string" },
16550
19195
  merge: { type: "string" },
@@ -16594,7 +19239,7 @@ var cli = meow(
16594
19239
  }
16595
19240
  );
16596
19241
  async function main() {
16597
- const projectDir = path13.resolve(cli.flags.projectDir);
19242
+ const projectDir = path16.resolve(cli.flags.projectDir);
16598
19243
  const [command, ...commandArgs] = cli.input;
16599
19244
  const isRunCommand = command === "run" || command === "exec";
16600
19245
  if (command && !KNOWN_COMMANDS.has(command)) {
@@ -16648,7 +19293,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16648
19293
  await exitWith(1);
16649
19294
  return;
16650
19295
  }
16651
- const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-VPBOZJY5.js");
19296
+ const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-MVTO43MW.js");
16652
19297
  const { waitUntilExit } = render(
16653
19298
  /* @__PURE__ */ jsx24(
16654
19299
  WorkflowInstallWizard,
@@ -16726,6 +19371,36 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16726
19371
  );
16727
19372
  return;
16728
19373
  }
19374
+ if (command === "tracker") {
19375
+ await exitWith(await runTrackerCommand(commandArgs));
19376
+ return;
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
+ }
16729
19404
  if (command === "build") {
16730
19405
  const rawArgs = process.argv.slice(2);
16731
19406
  const dashIdx = rawArgs.indexOf("--");
@@ -16828,8 +19503,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16828
19503
  const showSetup = isRunCommand ? false : shouldShowSetup({
16829
19504
  cliInput: cli.input,
16830
19505
  setupComplete: globalConfig.setupComplete,
16831
- globalConfigExists: fs13.existsSync(
16832
- path13.join(os5.homedir(), ".config", "athena", "config.json")
19506
+ globalConfigExists: fs15.existsSync(
19507
+ path16.join(os6.homedir(), ".config", "athena", "config.json")
16833
19508
  )
16834
19509
  });
16835
19510
  let runtimeConfig;
@@ -16861,6 +19536,11 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16861
19536
  await exitWith(RUN_EXIT_CODE.USAGE);
16862
19537
  return;
16863
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
+ }
16864
19544
  if (cli.flags.dryRun) {
16865
19545
  if (!isRunCommand) {
16866
19546
  console.error("Error: --dry-run is only supported in exec mode.");
@@ -16886,7 +19566,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16886
19566
  deviceId: resolvedDeviceId,
16887
19567
  telemetryEnabled: globalConfig.telemetry,
16888
19568
  appVersion: version,
16889
- os: `${os5.platform()}-${os5.arch()}`
19569
+ os: `${os6.platform()}-${os6.arch()}`
16890
19570
  });
16891
19571
  trackAppLaunched({ version, harness: runtimeConfig.harness });
16892
19572
  if (!isRunCommand && isFirstRun && globalConfig.telemetry !== false) {
@@ -16907,7 +19587,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16907
19587
  timeoutMs: cli.flags.timeoutMs,
16908
19588
  verbose: cli.flags.verbose,
16909
19589
  permissionGraceMs: cli.flags.permissionGraceMs,
16910
- answer: cli.flags.answer
19590
+ answer: cli.flags.answer,
19591
+ gates: cli.flags.gate
16911
19592
  },
16912
19593
  runtimeConfig
16913
19594
  });