@wjarka/cezarion 0.11.1 → 0.11.2-pr50.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/automations/task-template.js +1 -0
  2. package/dist/automations/task-template.js.map +1 -1
  3. package/dist/automations/types.d.ts +2 -0
  4. package/dist/automations/types.js +1 -0
  5. package/dist/automations/types.js.map +1 -1
  6. package/dist/contract/automations.d.ts +8 -0
  7. package/dist/contract/effort.d.ts +18 -0
  8. package/dist/contract/index.d.ts +1 -0
  9. package/dist/contract/index.js +919 -898
  10. package/dist/contract/runs.d.ts +7 -0
  11. package/dist/contract/skills.d.ts +1 -0
  12. package/dist/core/agent-runner.d.ts +4 -0
  13. package/dist/core/agent-runner.js.map +1 -1
  14. package/dist/core/claude-cli-runner.js +4 -0
  15. package/dist/core/claude-cli-runner.js.map +1 -1
  16. package/dist/core/codex-app-server-runner.d.ts +9 -0
  17. package/dist/core/codex-app-server-runner.js +14 -1
  18. package/dist/core/codex-app-server-runner.js.map +1 -1
  19. package/dist/core/opencode-server-runner.js +4 -0
  20. package/dist/core/opencode-server-runner.js.map +1 -1
  21. package/dist/core/pi-runner.js +4 -0
  22. package/dist/core/pi-runner.js.map +1 -1
  23. package/dist/runs/store.d.ts +3 -0
  24. package/dist/runs/store.js +4 -0
  25. package/dist/runs/store.js.map +1 -1
  26. package/dist/server/server.d.ts +68 -0
  27. package/dist/server/server.js +14 -3
  28. package/dist/server/server.js.map +1 -1
  29. package/dist/workflows/run.d.ts +7 -0
  30. package/dist/workflows/run.js +109 -52
  31. package/dist/workflows/run.js.map +1 -1
  32. package/package.json +3 -3
  33. package/scripts/mock-claude.mjs +72 -2
  34. package/web/dist/assets/{alert-dialog-B4Ofsgv7.js → alert-dialog-g-GVThSw.js} +1 -1
  35. package/web/dist/assets/arrow-down-BzmL0uj0.js +1 -0
  36. package/web/dist/assets/arrow-left-DtUM7GFU.js +1 -0
  37. package/web/dist/assets/{centered-state-duPW0phQ.js → centered-state-DdbE_pNl.js} +2 -2
  38. package/web/dist/assets/chevron-right-DnLh0uM2.js +1 -0
  39. package/web/dist/assets/{collapsible-DdcNBdSk.js → collapsible-R1r7zUOB.js} +1 -1
  40. package/web/dist/assets/{commit-list-CgvN9vec.js → commit-list-Bgb1DHBX.js} +1 -1
  41. package/web/dist/assets/{compare-variants-CQA2nd65.js → compare-variants-UA_0n_5a.js} +1 -1
  42. package/web/dist/assets/{diff-B6Agp5Hl.js → diff-Cv3z9esx.js} +2 -2
  43. package/web/dist/assets/{diff-stat-BoXuGkgq.js → diff-stat-ydfu0_5T.js} +1 -1
  44. package/web/dist/assets/{diff-view-CD8XI-fj.js → diff-view-DnPdkmk2.js} +1 -1
  45. package/web/dist/assets/{dropdown-menu-B3SYw-k5.js → dropdown-menu-CPP9gEmn.js} +1 -1
  46. package/web/dist/assets/{editable-title-B-hjp5wl.js → editable-title-D_bWdmY0.js} +1 -1
  47. package/web/dist/assets/{ellipsis-vertical-BYwzw0xl.js → ellipsis-vertical-WNkaehnw.js} +1 -1
  48. package/web/dist/assets/{file-BRj5eISx.js → file-RvhJlchx.js} +1 -1
  49. package/web/dist/assets/{folder-z4oT5981.js → folder-Dz-weqBo.js} +1 -1
  50. package/web/dist/assets/{git-toolbar-owSEkYld.js → git-toolbar-BEKuAjHk.js} +1 -1
  51. package/web/dist/assets/github-CmFHDNje.js +1 -0
  52. package/web/dist/assets/{image-preview-h3WFluuy.js → image-preview-DYiOSYoY.js} +1 -1
  53. package/web/dist/assets/index-BQTfjxFm.js +7 -0
  54. package/web/dist/assets/{markdown-DqBKYPiB.js → markdown-DQHRRO9e.js} +1 -1
  55. package/web/dist/assets/new-task-form-D2KlpqYC.js +1 -0
  56. package/web/dist/assets/{pill-BQfmfpYp.js → pill-FAFXjmY6.js} +1 -1
  57. package/web/dist/assets/{project-router-DxgFZY1l.js → project-router-Dzc0JK2j.js} +1 -1
  58. package/web/dist/assets/{prompt-templates-B6m16gq-.js → prompt-templates-DmqtzSPL.js} +1 -1
  59. package/web/dist/assets/{refresh-cw-mhSpxf2a.js → refresh-cw-BP4TkA4M.js} +1 -1
  60. package/web/dist/assets/{repo-git-CUNRxR3_.js → repo-git-C20pc5tT.js} +1 -1
  61. package/web/dist/assets/{run-diff-DsmmDVfx.js → run-diff-CvcsWwew.js} +1 -1
  62. package/web/dist/assets/run-header-xFlbebSJ.js +1 -0
  63. package/web/dist/assets/{search-x-BqDMB2g8.js → search-x-Drnmsmd0.js} +1 -1
  64. package/web/dist/assets/{skill-empty-hint-BWEvTggc.js → skill-empty-hint-D2l_OqZ5.js} +1 -1
  65. package/web/dist/assets/{skills-DwIxnnWO.js → skills-Cn2ppPow.js} +1 -1
  66. package/web/dist/assets/{sparkles-DIoTQpyx.js → sparkles-B9ja4FHw.js} +1 -1
  67. package/web/dist/assets/{square-terminal-Cc7CuQxc.js → square-terminal-CB2du-YG.js} +1 -1
  68. package/web/dist/assets/{tab-link-D1fQgpmO.js → tab-link-C5FWJ66Y.js} +1 -1
  69. package/web/dist/assets/{task-changes-BKeLlDpK.js → task-changes-CuUmQArS.js} +1 -1
  70. package/web/dist/assets/{task-commits-gcEFZIa5.js → task-commits-DgIWIhW9.js} +1 -1
  71. package/web/dist/assets/{task-files-BGAqoXTW.js → task-files-BeDXPyz5.js} +1 -1
  72. package/web/dist/assets/task-thread-2nmi0vcz.js +9 -0
  73. package/web/dist/assets/{textarea-BOVbfy5C.js → textarea-Sm6doUPO.js} +1 -1
  74. package/web/dist/assets/{thread-loading-CypW73KD.js → thread-loading-DojNeADh.js} +1 -1
  75. package/web/dist/assets/{trash-2-CJq6LHWn.js → trash-2-C_lGBXG2.js} +1 -1
  76. package/web/dist/assets/{triangle-alert-CjDFvU27.js → triangle-alert-Brk_ldFl.js} +1 -1
  77. package/web/dist/assets/{upload-CPDZlJeK.js → upload-Cxs1UROt.js} +1 -1
  78. package/web/dist/assets/{use-desktop-5JsLmi0a.js → use-desktop-Cmrz3npD.js} +1 -1
  79. package/web/dist/assets/{use-submit-shortcut-CHVFevty.js → use-submit-shortcut-DzeqN3GK.js} +1 -1
  80. package/web/dist/assets/{utils-DiO6zyJd.js → utils-Unt0mZvU.js} +1 -1
  81. package/web/dist/assets/{workflows-rQDFzWRt.js → workflows-D_GyBbup.js} +1 -1
  82. package/web/dist/assets/{zoomable-image-COWqb11v.js → zoomable-image-ByySCppM.js} +1 -1
  83. package/web/dist/index.html +22 -22
  84. package/web/dist/assets/arrow-down-Dy8dup7j.js +0 -1
  85. package/web/dist/assets/arrow-left-BJaucbhn.js +0 -1
  86. package/web/dist/assets/chevron-right-Be4A5TCS.js +0 -1
  87. package/web/dist/assets/github-Bz2VgRX3.js +0 -1
  88. package/web/dist/assets/index-EtQQQ6Kj.js +0 -7
  89. package/web/dist/assets/new-task-form-BdJKRBJX.js +0 -1
  90. package/web/dist/assets/run-header-BkhrseLJ.js +0 -1
  91. package/web/dist/assets/task-thread-DT8aUYTS.js +0 -9
@@ -73,6 +73,9 @@ export declare function runAccountKey(run: Pick<RunRecord, 'runner' | 'agentProf
73
73
  export interface StartRunInput {
74
74
  task: string;
75
75
  model?: string;
76
+ /** Reasoning-effort pin (#45). Canonical `low`/`medium`/`high`/`xhigh`/`max`.
77
+ * Unset = harness default. Same class of override as `model`. */
78
+ effort?: string;
76
79
  /** Agent backend chosen for this task (GUI). Unset = the config default. */
77
80
  runner?: RunnerId;
78
81
  /** Agent account for this task (spec 2026-07-29-agent-profiles), applying to steps that run
@@ -591,6 +594,8 @@ export declare class RunManager {
591
594
  images?: ContentBlock[];
592
595
  runner?: RunnerId;
593
596
  model?: string;
597
+ /** Reasoning-effort pin (#45). Omitted keeps the run's pin; empty string clears it. */
598
+ effort?: string;
594
599
  /** Agent account for the reopened session (spec 2026-07-29-agent-profiles). Omitted = the
595
600
  * account the run is already on. */
596
601
  agentProfile?: string;
@@ -615,6 +620,8 @@ export declare class RunManager {
615
620
  * item-shape cache are session-scoped, like the mapper state feeding them.
616
621
  */
617
622
  private makeUiSink;
623
+ /** Continue autonomous runs and markerless tool turns through one bounded path. */
624
+ private tryAutoContinueTurn;
618
625
  /** Native backend asks arrive before turn-end. Persist and park immediately
619
626
  * so the cockpit shows attention and the run releases its workspace slot. */
620
627
  private handleRunnerUiEvent;
@@ -55,6 +55,13 @@ const DONE_MARKER_RE = /CEZ:DONE\s*$/;
55
55
  * backends can't split the marker across text events.
56
56
  */
57
57
  const MONITORING_MARKER_RE = /CEZ:MONITORING\s*$/;
58
+ /** Claude's native scheduler is the backend-level equivalent of the textual
59
+ * monitoring marker. Keep this recognition here, at the workflow boundary,
60
+ * so the v1 event protocol stays unchanged and other backends do not acquire
61
+ * semantics from a coincidentally named tool (#46). */
62
+ function isClaudeScheduleWakeup(event, backend) {
63
+ return backend === 'claude' && event.type === 'tool-call' && event.tool === 'ScheduleWakeup';
64
+ }
58
65
  /**
59
66
  * Preserve boundaries between complete assistant text blocks while a turn is
60
67
  * accumulated for marker parsing. The runners join these same v1 blocks with
@@ -122,6 +129,17 @@ const REPOSITORY_ROOT_LOCK_DISABLED_NOTE = 'repository-root lock disabled by CEZ
122
129
  const MAX_AUTO_CONTINUES = 40;
123
130
  const AUTONOMOUS_NUDGE = 'Continue working autonomously until the task is fully complete. Do not ask me for confirmation or clarification — make reasonable assumptions and proceed. When everything is done, end the session with your done signal.';
124
131
  const MONITORING_WAKE_NUDGE = 'Re-check the downstream work you were monitoring. Continue toward the task goal; emit CEZ:MONITORING again only if it is still pending.';
132
+ /** A question at the handoff boundary may be followed by Markdown answer choices. */
133
+ function hasUserQuestion(text) {
134
+ const lines = stripTaskMarkers(text).trimEnd().split('\n');
135
+ while (lines.length > 0) {
136
+ const line = lines.pop().trim();
137
+ if (!line || /^(?:[-*+]|\d+[.)])\s+\S/.test(line))
138
+ continue;
139
+ return /\?[*_~`]*(?:\s*\([^()]*\))?$/.test(line);
140
+ }
141
+ return false;
142
+ }
125
143
  /**
126
144
  * Auto-resume after a provider usage limit (spec 2026-08-03-auto-resume-after-usage-limit).
127
145
  *
@@ -540,13 +558,14 @@ export class RunManager {
540
558
  // Sanitize at the manager boundary so CLI runs, workflows, variants, and
541
559
  // direct callers cannot bypass the HTTP policy.
542
560
  const effectiveInput = agentModelsLocked(this.repoRoot)
543
- ? { ...input, model: undefined }
561
+ ? { ...input, model: undefined, effort: undefined }
544
562
  : input;
545
563
  const run = this.store.createRun({
546
564
  title: makeRunTitle(input.task, workflow) + (group ? ` (${group.variant})` : ''),
547
565
  workflow: workflow.name,
548
566
  task: input.task,
549
567
  model: effectiveInput.model,
568
+ effort: effectiveInput.effort,
550
569
  runner: input.runner,
551
570
  // The composer's per-task account (spec 2026-07-29-agent-profiles). Persisted at creation
552
571
  // so a queued run picks it up at dequeue and every later resume reads the same answer.
@@ -849,6 +868,7 @@ export class RunManager {
849
868
  input: this.hydrateQueuedInput(run.id, {
850
869
  task: run.task,
851
870
  model: run.model,
871
+ effort: run.effort,
852
872
  runner: run.runner,
853
873
  generateFollowups,
854
874
  // Re-thread autonomy (#489): the rebuilt input feeds `execute`, whose mid-run auto-nudge
@@ -1769,7 +1789,7 @@ export class RunManager {
1769
1789
  /** Restart recovery may discover several interrupted tasks at once. Those
1770
1790
  * continuations are queued; an explicit user Continue remains immediate. */
1771
1791
  deferForCapacity = false) {
1772
- if (agentModelsLocked(this.repoRoot) && opts.model?.trim()) {
1792
+ if (agentModelsLocked(this.repoRoot) && (opts.model?.trim() || opts.effort?.trim())) {
1773
1793
  return { ok: false, error: AGENT_MODELS_LOCKED_ERROR };
1774
1794
  }
1775
1795
  if (this.active.has(runId))
@@ -1804,7 +1824,10 @@ export class RunManager {
1804
1824
  // run's current backend — `runContinuation` reads it off the record, later continuations
1805
1825
  // default to it, and the header reflects the active engine. An empty model ('') clears the
1806
1826
  // pin, letting the runner pick the model (auto).
1807
- if (opts.runner !== undefined || opts.model !== undefined || opts.agentProfile !== undefined) {
1827
+ if (opts.runner !== undefined ||
1828
+ opts.model !== undefined ||
1829
+ opts.effort !== undefined ||
1830
+ opts.agentProfile !== undefined) {
1808
1831
  // Guard the pairing before persisting anything: the model override applies to the runner
1809
1832
  // this continuation will actually use (`opts.runner ?? record.runner ?? 'claude'` — the
1810
1833
  // same resolution `runContinuation` reads off the record). A model that is recognizably
@@ -1842,6 +1865,9 @@ export class RunManager {
1842
1865
  : inheritedAccountIsForeign
1843
1866
  ? { agentProfile: undefined }
1844
1867
  : {}),
1868
+ // Effort is shared vocabulary, not per-backend, so a runner switch keeps the pin.
1869
+ // Empty string clears it the same way `model: ''` clears auto.
1870
+ ...(opts.effort !== undefined ? { effort: opts.effort === '' ? undefined : opts.effort } : {}),
1845
1871
  });
1846
1872
  }
1847
1873
  // Everything that could refuse this continuation has now passed, so a pending usage-limit
@@ -1976,6 +2002,8 @@ export class RunManager {
1976
2002
  });
1977
2003
  let stepCost = 0;
1978
2004
  let turnText = '';
2005
+ let sawClaudeScheduleWakeup = false;
2006
+ let sawToolUse = false;
1979
2007
  let sessionError;
1980
2008
  const sink = this.makeUiSink(runId, stepId);
1981
2009
  const onEvent = (event) => {
@@ -2010,6 +2038,10 @@ export class RunManager {
2010
2038
  stepCost += event.usd;
2011
2039
  this.store.updateStep(runId, stepId, { costUsd: stepCost });
2012
2040
  }
2041
+ if (event.type === 'tool-call')
2042
+ sawToolUse = true;
2043
+ if (isClaudeScheduleWakeup(event, backend))
2044
+ sawClaudeScheduleWakeup = true;
2013
2045
  if (event.type === 'turn-end') {
2014
2046
  // Belt-and-braces: v2 `turn.completed` already flushed the delta
2015
2047
  // coalescers; the v1 turn boundary flushes again (idempotent) so no
@@ -2023,8 +2055,14 @@ export class RunManager {
2023
2055
  const askResult = sessionOpen && !done ? parseAskMarkerResult(turnText) : undefined;
2024
2056
  const ask = askResult?.kind === 'valid' ? askResult.request : null;
2025
2057
  const askRejection = askResult ? askMarkerRejection(askResult) : undefined;
2026
- const monitoring = sessionOpen && !done && !ask && MONITORING_MARKER_RE.test(turnText.trimEnd());
2058
+ const monitoring = sessionOpen &&
2059
+ !done &&
2060
+ !ask &&
2061
+ (MONITORING_MARKER_RE.test(turnText.trimEnd()) || sawClaudeScheduleWakeup);
2062
+ const markerlessMidWork = askResult?.kind === 'none' && !monitoring && sawToolUse && !hasUserQuestion(turnText);
2027
2063
  turnText = '';
2064
+ sawClaudeScheduleWakeup = false;
2065
+ sawToolUse = false;
2028
2066
  if (askRejection)
2029
2067
  this.store.appendEvent(runId, { type: 'note', message: askRejection, stepId });
2030
2068
  if (done) {
@@ -2034,29 +2072,15 @@ export class RunManager {
2034
2072
  state.session?.end();
2035
2073
  return;
2036
2074
  }
2075
+ let autoContinued = false;
2037
2076
  if (sessionOpen) {
2038
- // Autonomous (#autonomous): never hand the ball back to the user. Nudge the agent to
2039
- // keep going (bounded by MAX_AUTO_CONTINUES) instead of parking at `waiting`.
2040
- const autoContinued = state.autonomous &&
2041
- (state.autoContinues ?? 0) < MAX_AUTO_CONTINUES &&
2042
- !state.cancelled &&
2043
- (() => {
2044
- const sent = state.session?.sendMessage([{ type: 'text', text: AUTONOMOUS_NUDGE }]);
2045
- if (!sent)
2046
- return false;
2047
- state.autoContinues = (state.autoContinues ?? 0) + 1;
2048
- this.store.appendEvent(runId, {
2049
- type: 'note',
2050
- message: `autonomous — continuing without pausing (${state.autoContinues}/${MAX_AUTO_CONTINUES})`,
2051
- });
2052
- return true;
2053
- })();
2077
+ autoContinued = this.tryAutoContinueTurn(runId, state, state.autonomous === true || markerlessMidWork);
2054
2078
  if (!autoContinued) {
2055
2079
  // `CEZ:ASK` → park `waiting` (attention) AND surface the structured
2056
- // question as an ask card (#473). `CEZ:MONITORING` → non-attention
2057
- // `running`/`activity:'monitoring'` (#490). Both share the waiting
2058
- // lifecycle (free the slot, keep the idle timer); the autonomous
2059
- // nudge above still wins over either.
2080
+ // question as an ask card (#473). `CEZ:MONITORING` or Claude's native
2081
+ // `ScheduleWakeup` → non-attention `running`/`activity:'monitoring'`
2082
+ // (#490, #46). Both free the slot; only genuine user waits keep the
2083
+ // idle timer. The autonomous nudge above still wins over either.
2060
2084
  if (ask)
2061
2085
  emitAskRequested(sink, ask);
2062
2086
  if (monitoring) {
@@ -2086,7 +2110,7 @@ export class RunManager {
2086
2110
  if (this.store.getRun(runId)?.autoResumeAttempts !== undefined) {
2087
2111
  this.store.updateRun(runId, { autoResumeAttempts: undefined });
2088
2112
  }
2089
- appendHandoffHeartbeat(this.dataDir, runId, `turn complete — status=${monitoring ? 'monitoring' : sessionOpen ? 'waiting' : 'running'}`);
2113
+ appendHandoffHeartbeat(this.dataDir, runId, `turn complete — status=${autoContinued ? 'running' : monitoring ? 'monitoring' : sessionOpen ? 'waiting' : 'running'}`);
2090
2114
  }
2091
2115
  };
2092
2116
  // Backend + model come off the record: the run's current backend by default, or the
@@ -2196,6 +2220,7 @@ export class RunManager {
2196
2220
  additionalDirectories: agentDirectories(join(this.dataDir, 'runs'), continueProfile.env),
2197
2221
  env: continueProfile.env,
2198
2222
  model: continueModel,
2223
+ effort: agentModelsLocked(this.repoRoot) ? undefined : record?.effort,
2199
2224
  sessionId,
2200
2225
  resume: sessionId !== undefined,
2201
2226
  timeoutMs: 0,
@@ -2587,6 +2612,8 @@ export class RunManager {
2587
2612
  const startTokens = stepRecord?.tokensUsed ?? 0;
2588
2613
  let stepCost = stepRecord?.costUsd ?? 0;
2589
2614
  let turnText = '';
2615
+ let sawClaudeScheduleWakeup = false;
2616
+ let sawToolUse = false;
2590
2617
  let sessionError;
2591
2618
  const sink = this.makeUiSink(runId, step.id);
2592
2619
  const onEvent = (event) => {
@@ -2622,6 +2649,10 @@ export class RunManager {
2622
2649
  stepCost += event.usd;
2623
2650
  this.store.updateStep(runId, step.id, { costUsd: stepCost });
2624
2651
  }
2652
+ if (event.type === 'tool-call')
2653
+ sawToolUse = true;
2654
+ if (isClaudeScheduleWakeup(event, backend))
2655
+ sawClaudeScheduleWakeup = true;
2625
2656
  if (event.type === 'turn-end') {
2626
2657
  // v2 `turn.completed` already flushed the coalescers; the v1 turn
2627
2658
  // boundary flushes again (idempotent) as a backstop.
@@ -2638,8 +2669,11 @@ export class RunManager {
2638
2669
  sessionOpen &&
2639
2670
  !done &&
2640
2671
  !ask &&
2641
- MONITORING_MARKER_RE.test(turnText.trimEnd());
2672
+ (MONITORING_MARKER_RE.test(turnText.trimEnd()) || sawClaudeScheduleWakeup);
2673
+ const markerlessMidWork = askResult?.kind === 'none' && !monitoring && sawToolUse && !hasUserQuestion(turnText);
2642
2674
  turnText = '';
2675
+ sawClaudeScheduleWakeup = false;
2676
+ sawToolUse = false;
2643
2677
  if (askRejection)
2644
2678
  emit({ type: 'note', stepId: step.id, message: askRejection });
2645
2679
  if (done) {
@@ -2651,33 +2685,36 @@ export class RunManager {
2651
2685
  return;
2652
2686
  }
2653
2687
  const waiting = interactive && sessionOpen;
2688
+ let autoContinued = false;
2654
2689
  if (waiting) {
2655
- // Turn over, session open. Either the ball is in the user's court
2656
- // (`waiting`) — optionally with a structured `CEZ:ASK` question the
2657
- // cockpit renders as an ask card (#473) — or the agent declared it is
2658
- // still working on its own downstream work with `CEZ:MONITORING`, which
2659
- // parks as `running`/`activity:'monitoring'`, a non-attention state,
2660
- // instead of raising "needs you" (#490). Lifecycle is identical: the
2661
- // run frees its slot and keeps the idle timer.
2662
- if (ask)
2663
- emitAskRequested(sink, ask);
2664
- if (monitoring) {
2665
- this.store.updateRun(runId, { status: 'running', activity: 'monitoring' });
2666
- this.store.updateStep(runId, step.id, { status: 'running' });
2667
- this.monitoring.add(runId);
2668
- this.clearIdleTimer(state);
2669
- this.armMonitoringWakeTimer(runId, state);
2670
- }
2671
- else {
2672
- this.store.updateRun(runId, { status: 'waiting', activity: undefined });
2673
- this.store.updateStep(runId, step.id, { status: 'waiting' });
2674
- this.monitoring.delete(runId);
2675
- this.clearMonitoringWakeTimer(state, runId);
2690
+ autoContinued = this.tryAutoContinueTurn(runId, state, !ask && (state.autonomous === true || markerlessMidWork));
2691
+ if (!autoContinued) {
2692
+ // Turn over, session open. Either the ball is in the user's court
2693
+ // (`waiting`) — optionally with a structured `CEZ:ASK` question the
2694
+ // cockpit renders as an ask card (#473) — or the agent declared it is
2695
+ // still working through `CEZ:MONITORING` or Claude's native
2696
+ // `ScheduleWakeup`. Monitoring parks as `running` with no user-wait
2697
+ // idle timer, so the cockpit stays non-attention (#490, #46).
2698
+ if (ask)
2699
+ emitAskRequested(sink, ask);
2700
+ if (monitoring) {
2701
+ this.store.updateRun(runId, { status: 'running', activity: 'monitoring' });
2702
+ this.store.updateStep(runId, step.id, { status: 'running' });
2703
+ this.monitoring.add(runId);
2704
+ this.clearIdleTimer(state);
2705
+ this.armMonitoringWakeTimer(runId, state);
2706
+ }
2707
+ else {
2708
+ this.store.updateRun(runId, { status: 'waiting', activity: undefined });
2709
+ this.store.updateStep(runId, step.id, { status: 'waiting' });
2710
+ this.monitoring.delete(runId);
2711
+ this.clearMonitoringWakeTimer(state, runId);
2712
+ }
2713
+ this.waiting.add(runId);
2714
+ if (!monitoring)
2715
+ this.armIdleTimer(runId, state);
2716
+ this.releaseSlot(); // the freed slot can start a queued run right away — in any project
2676
2717
  }
2677
- this.waiting.add(runId);
2678
- if (!monitoring)
2679
- this.armIdleTimer(runId, state);
2680
- this.releaseSlot(); // the freed slot can start a queued run right away — in any project
2681
2718
  }
2682
2719
  // The window is proven open — see the twin in `runContinuation`.
2683
2720
  if (this.store.getRun(runId)?.autoResumeAttempts !== undefined) {
@@ -2685,7 +2722,7 @@ export class RunManager {
2685
2722
  }
2686
2723
  // Cez's own heartbeat — the handoff stays current even when the
2687
2724
  // agent forgets to write (spec 007).
2688
- appendHandoffHeartbeat(this.dataDir, runId, `turn complete — status=${monitoring ? 'monitoring' : waiting ? 'waiting' : 'running'}`);
2725
+ appendHandoffHeartbeat(this.dataDir, runId, `turn complete — status=${autoContinued ? 'running' : monitoring ? 'monitoring' : waiting ? 'waiting' : 'running'}`);
2689
2726
  }
2690
2727
  };
2691
2728
  const stepBackend = step.runner ?? taskBackend;
@@ -2747,6 +2784,9 @@ export class RunManager {
2747
2784
  additionalDirectories: agentDirectories(join(this.dataDir, 'runs'), stepProfile.env),
2748
2785
  env: stepProfile.env,
2749
2786
  model: backendModel,
2787
+ effort: agentModelsLocked(this.repoRoot)
2788
+ ? undefined
2789
+ : this.store.getRun(runId)?.effort ?? input.effort,
2750
2790
  sessionId,
2751
2791
  // Interactive sessions have no wall clock — the idle timer rules.
2752
2792
  timeoutMs: interactive ? 0 : undefined,
@@ -2809,6 +2849,23 @@ export class RunManager {
2809
2849
  emitLive: (event) => this.store.emitEphemeral(runId, { ...event, stepId }),
2810
2850
  });
2811
2851
  }
2852
+ /** Continue autonomous runs and markerless tool turns through one bounded path. */
2853
+ tryAutoContinueTurn(runId, state, shouldContinue) {
2854
+ if (!shouldContinue || (state.autoContinues ?? 0) >= MAX_AUTO_CONTINUES) {
2855
+ return false;
2856
+ }
2857
+ if (state.cancelled)
2858
+ return false;
2859
+ const sent = state.session?.sendMessage([{ type: 'text', text: AUTONOMOUS_NUDGE }]);
2860
+ if (!sent)
2861
+ return false;
2862
+ state.autoContinues = (state.autoContinues ?? 0) + 1;
2863
+ this.store.appendEvent(runId, {
2864
+ type: 'note',
2865
+ message: `${state.autonomous ? 'autonomous' : 'work still in progress'} — continuing without pausing (${state.autoContinues}/${MAX_AUTO_CONTINUES})`,
2866
+ });
2867
+ return true;
2868
+ }
2812
2869
  /** Native backend asks arrive before turn-end. Persist and park immediately
2813
2870
  * so the cockpit shows attention and the run releases its workspace slot. */
2814
2871
  handleRunnerUiEvent(runId, state, sink, event) {