tinker-agent 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +64 -6
  3. package/package.json +1 -1
  4. package/src/agent/loop.ts +17 -0
  5. package/src/agent/runtime-session.ts +341 -1
  6. package/src/agent/session-ledger.ts +100 -3
  7. package/src/cli/config.ts +11 -2
  8. package/src/cli/model-profiles.ts +58 -0
  9. package/src/cli/public-config-contract.ts +73 -7
  10. package/src/cli/run-runner.ts +4 -1
  11. package/src/cli/runner-dependencies.ts +28 -4
  12. package/src/cli/tui-memory.ts +4 -0
  13. package/src/cli/tui-runner.tsx +8 -1
  14. package/src/context/context-automation-policy.ts +22 -21
  15. package/src/context/context-manager.ts +91 -15
  16. package/src/context/context-policy.ts +0 -2
  17. package/src/context/context-swap-renderer.ts +1 -1
  18. package/src/context/prefix-retirement-planner.ts +58 -8
  19. package/src/context/recall-retirement-contract.ts +5 -4
  20. package/src/context/swap-planner.ts +33 -27
  21. package/src/events/observation-text-log.ts +4 -0
  22. package/src/events/stdout-event-printer.ts +5 -0
  23. package/src/events/types.ts +5 -1
  24. package/src/model/fake-model-client.ts +55 -16
  25. package/src/model/model-api.ts +12 -0
  26. package/src/model/model-client.ts +9 -1
  27. package/src/model/moonshot-input-token-estimator.ts +5 -1
  28. package/src/model/openai-chat-mapping.ts +2 -24
  29. package/src/model/openai-chat-model-client.ts +18 -294
  30. package/src/model/openai-image-mapping.ts +20 -0
  31. package/src/model/openai-model-utils.ts +304 -0
  32. package/src/model/openai-responses-mapping.ts +532 -0
  33. package/src/model/openai-responses-model-client.ts +295 -0
  34. package/src/model/openai-responses-stream.ts +96 -0
  35. package/src/model/openai-responses-token-estimator.ts +155 -0
  36. package/src/model/reasoning-effort.ts +60 -0
  37. package/src/session/session-catalog.ts +2 -2
  38. package/src/session/session-history-reader.ts +6 -1
  39. package/src/session/session-schema.ts +268 -4
  40. package/src/session/session-store.ts +134 -26
  41. package/src/skills/skill-context.ts +2 -2
  42. package/src/tools/bounded-output-preview.ts +276 -0
  43. package/src/tools/recall.ts +67 -36
  44. package/src/tools/registry.ts +7 -2
  45. package/src/tools/task-output-snapshot.ts +6 -22
  46. package/src/tools/task-output.ts +23 -27
  47. package/src/tui/app.tsx +153 -11
  48. package/src/tui/components/footer.tsx +6 -1
  49. package/src/tui/components/prompt-input.tsx +9 -1
  50. package/src/tui/event-store.ts +15 -0
  51. package/src/tui/slash-commands.ts +20 -0
  52. package/src/tui/tui-session-controller.ts +14 -0
package/src/tui/app.tsx CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  } from "../context/context-manager";
18
18
  import { ContextBudgetExceededError } from "../model/model-request-preflight";
19
19
  import { ModelRequestMediaAggregateError } from "../model/model-client";
20
+ import type { ReasoningEffortSnapshot } from "../model/reasoning-effort";
20
21
  import type { SessionId } from "../ids/runtime-id";
21
22
  import { readLastAssistantResponse } from "../session/session-last-response-reader";
22
23
  import type { PromptHistory } from "./prompt-history";
@@ -107,6 +108,10 @@ const STATIC_HEADER = Symbol("tui-static-header");
107
108
  const LIVE_TIMELINE_MAX_ROWS = 8;
108
109
  const LIVE_TIMELINE_WITH_TASKS_MAX_ROWS = 3;
109
110
  const BACKGROUND_TASKS_MAX_ROWS = 12;
111
+ const IDLE_PROMPT_SCHEDULER = Object.freeze({
112
+ state: "idle" as const,
113
+ pendingCount: 0,
114
+ });
110
115
 
111
116
  export function App(props: AppProps) {
112
117
  const { exit } = useApp();
@@ -136,11 +141,23 @@ export function App(props: AppProps) {
136
141
  () => binding.bashGuard(),
137
142
  () => binding.bashGuard(),
138
143
  );
144
+ const promptScheduler = useSyncExternalStore(
145
+ (listener) => binding.subscribePromptScheduler?.(listener) ?? (() => undefined),
146
+ () => binding.promptScheduler?.() ?? IDLE_PROMPT_SCHEDULER,
147
+ () => binding.promptScheduler?.() ?? IDLE_PROMPT_SCHEDULER,
148
+ );
139
149
  const [isRunning, setIsRunning] = useState(false);
150
+ const executionRunning = isRunning || promptScheduler.state === "running";
140
151
  const [isSessionOperation, setIsSessionOperation] = useState(false);
141
152
  const [isCopying, setIsCopying] = useState(false);
142
153
  const [isCancelling, setIsCancelling] = useState(false);
143
154
  const [notice, setNotice] = useState<string | undefined>(props.initialNotice);
155
+ const currentQueuedFollowUpNotice = `Follow-up queued for the active turn (${promptScheduler.pendingCount} pending).`;
156
+ const visibleNotice =
157
+ notice?.startsWith("Follow-up queued for the active turn (") === true &&
158
+ notice !== currentQueuedFollowUpNotice
159
+ ? undefined
160
+ : notice;
144
161
  const [showStatus, setShowStatus] = useState(false);
145
162
  const [showSkills, setShowSkills] = useState(false);
146
163
  const [showMcp, setShowMcp] = useState(false);
@@ -180,15 +197,43 @@ export function App(props: AppProps) {
180
197
  state.activeTurn === undefined &&
181
198
  props.profiles !== undefined &&
182
199
  props.profiles.profiles.size > 1;
200
+ const reasoningEffort = binding.reasoningEffort?.();
201
+ const hasReasoningEffort = reasoningEffort !== undefined;
183
202
  const activeResumePicker =
184
203
  resumePicker?.ownerSessionId === binding.sessionId ? resumePicker : undefined;
185
204
 
186
- const builtInCommands = canSwitchModel
187
- ? SLASH_COMMANDS
188
- : SLASH_COMMANDS.filter((command) => command.name !== "model");
205
+ const builtInCommands = SLASH_COMMANDS.filter(
206
+ (command) =>
207
+ (command.name !== "model" || canSwitchModel) &&
208
+ (command.name !== "reasoning" || hasReasoningEffort),
209
+ );
189
210
  const availableCommands = [...builtInCommands, ...(props.projectSlashCommands ?? [])];
190
211
 
191
212
  const profileList = props.profiles ? [...props.profiles.profiles.values()] : [];
213
+ const cycleReasoningEffort = () => {
214
+ const current = binding.reasoningEffort?.();
215
+ if (current === undefined) {
216
+ return;
217
+ }
218
+ const currentIndex = current.supportedEfforts.indexOf(current.effort);
219
+ const nextIndex =
220
+ currentIndex < 0 ? 0 : (currentIndex + 1) % current.supportedEfforts.length;
221
+ const nextEffort = current.supportedEfforts[nextIndex];
222
+ if (nextEffort === undefined) {
223
+ return;
224
+ }
225
+ try {
226
+ const updated = binding.setReasoningEffort?.(nextEffort);
227
+ if (updated === undefined) {
228
+ throw new Error("Reasoning effort control is unavailable.");
229
+ }
230
+ setNotice(
231
+ `Reasoning effort cycled from ${JSON.stringify(current.effort)} to ${JSON.stringify(updated.effort)} (Ctrl+R).`,
232
+ );
233
+ } catch (error) {
234
+ setNotice(errorMessage(error));
235
+ }
236
+ };
192
237
 
193
238
  useEffect(() => {
194
239
  if (readGitBranch === undefined) {
@@ -230,7 +275,7 @@ export function App(props: AppProps) {
230
275
  setIsCancelling(true);
231
276
  setNotice("Cancelling current turn...");
232
277
  },
233
- { isActive: isRunning },
278
+ { isActive: executionRunning },
234
279
  );
235
280
 
236
281
  const closeResumePicker = () => {
@@ -420,6 +465,32 @@ export function App(props: AppProps) {
420
465
  submission: PromptSubmission,
421
466
  admissionSignal: AbortSignal,
422
467
  ): Promise<PromptSubmissionOutcome> => {
468
+ if (promptScheduler.state === "running") {
469
+ if (submission.userMessage.attachments !== undefined) {
470
+ setNotice("Active-turn follow-ups do not support image attachments.");
471
+ return false;
472
+ }
473
+ if (submission.userMessage.content.trimStart().startsWith("/")) {
474
+ setNotice("Slash commands cannot be queued while a turn is running.");
475
+ return false;
476
+ }
477
+ try {
478
+ const queued = binding.queueFollowUp?.(submission.userMessage);
479
+ if (queued === undefined) {
480
+ throw new Error("TUI session binding does not support follow-up queuing.");
481
+ }
482
+ void props.history?.append(submission.draft).catch((error: unknown) => {
483
+ setNotice(`Prompt history write failed: ${errorMessage(error)}`);
484
+ });
485
+ setNotice(
486
+ `Follow-up queued for the active turn (${queued.pendingCount} pending).`,
487
+ );
488
+ return true;
489
+ } catch (error) {
490
+ setNotice(`Follow-up was not queued: ${errorMessage(error)}`);
491
+ return false;
492
+ }
493
+ }
423
494
  if (isRunning) return false;
424
495
  setNotice(undefined);
425
496
  setIsCancelling(false);
@@ -528,6 +599,10 @@ export function App(props: AppProps) {
528
599
  restoreStaticViewport();
529
600
  }
530
601
 
602
+ if (promptScheduler.state === "running") {
603
+ return submitAgentPrompt(submission, signal);
604
+ }
605
+
531
606
  if (userMessage.attachments === undefined && trimmed.startsWith("/")) {
532
607
  try {
533
608
  const projectCommand = resolveProjectSlashCommand(
@@ -660,6 +735,42 @@ export function App(props: AppProps) {
660
735
  exit();
661
736
  return true;
662
737
  }
738
+ if (
739
+ command.type === "reasoning_status" ||
740
+ command.type === "reasoning_reset" ||
741
+ command.type === "reasoning_set"
742
+ ) {
743
+ const current = binding.reasoningEffort?.();
744
+ if (current === undefined) {
745
+ setNotice("Current model profile does not configure reasoning effort.");
746
+ return false;
747
+ }
748
+ try {
749
+ if (command.type === "reasoning_status") {
750
+ setNotice(formatReasoningEffortStatus(current));
751
+ } else if (command.type === "reasoning_reset") {
752
+ const reset = binding.resetReasoningEffort?.();
753
+ if (reset === undefined) {
754
+ throw new Error("Reasoning effort control is unavailable.");
755
+ }
756
+ setNotice(
757
+ `Reasoning effort reset to profile default ${JSON.stringify(reset.defaultEffort)}.`,
758
+ );
759
+ } else {
760
+ const updated = binding.setReasoningEffort?.(command.effort);
761
+ if (updated === undefined) {
762
+ throw new Error("Reasoning effort control is unavailable.");
763
+ }
764
+ setNotice(
765
+ `Reasoning effort set to ${JSON.stringify(updated.effort)} for this session runtime (profile default: ${JSON.stringify(updated.defaultEffort)}).`,
766
+ );
767
+ }
768
+ } catch (error) {
769
+ setNotice(errorMessage(error));
770
+ return false;
771
+ }
772
+ return true;
773
+ }
663
774
  if (command.type === "model" || command.type === "model_switch") {
664
775
  if (!canSwitchModel) {
665
776
  setNotice(
@@ -791,9 +902,16 @@ export function App(props: AppProps) {
791
902
  </Box>
792
903
  <Box marginTop={1} flexShrink={0}>
793
904
  <Footer
794
- status={isCancelling ? "cancelling" : state.status}
905
+ status={
906
+ isCancelling
907
+ ? "cancelling"
908
+ : executionRunning
909
+ ? "running"
910
+ : state.status
911
+ }
795
912
  workedForMs={state.workedForMs}
796
913
  yolo={bashGuard.mode === "yolo"}
914
+ pendingFollowUps={promptScheduler.pendingCount}
797
915
  />
798
916
  </Box>
799
917
  <Box marginTop={1} flexDirection="column" flexShrink={0}>
@@ -821,27 +939,43 @@ export function App(props: AppProps) {
821
939
  ) : (
822
940
  <PromptInput
823
941
  modelName={binding.modelName}
942
+ reasoningEffort={reasoningEffort?.effort}
824
943
  workspaceRoot={binding.workspaceRoot}
825
944
  gitBranch={gitBranch}
826
945
  contextUsage={state.contextUsage}
827
946
  isDisabled={
828
- isRunning ||
829
947
  isSessionOperation ||
830
948
  isCopying ||
949
+ isCancelling ||
831
950
  bashGuard.pending !== undefined
832
951
  }
833
952
  history={props.history}
834
953
  commands={availableCommands}
835
954
  fileLister={props.fileLister}
836
- importImage={binding.importImage}
955
+ importImage={
956
+ promptScheduler.state === "running"
957
+ ? undefined
958
+ : binding.importImage
959
+ }
837
960
  verifyImageAssets={binding.verifyImageAssets}
961
+ onCycleReasoningEffort={
962
+ hasReasoningEffort && promptScheduler.state !== "running"
963
+ ? cycleReasoningEffort
964
+ : undefined
965
+ }
838
966
  onSubmit={onSubmit}
839
967
  onMaintenance={onMaintenance}
840
- placeholder='Enter a coding request, or "/" for commands'
968
+ placeholder={
969
+ promptScheduler.state === "running"
970
+ ? "Send a follow-up for the active turn…"
971
+ : 'Enter a coding request, or "/" for commands'
972
+ }
841
973
  />
842
974
  )}
843
975
  {viewError === undefined ? null : <Text color="red">{viewError}</Text>}
844
- {notice === undefined ? null : <Text color="yellow">{notice}</Text>}
976
+ {visibleNotice === undefined ? null : (
977
+ <Text color="yellow">{visibleNotice}</Text>
978
+ )}
845
979
  </Box>
846
980
  </Box>
847
981
  )}
@@ -850,6 +984,14 @@ export function App(props: AppProps) {
850
984
  );
851
985
  }
852
986
 
987
+ function formatReasoningEffortStatus(snapshot: ReasoningEffortSnapshot): string {
988
+ const source =
989
+ snapshot.source === "profile_default"
990
+ ? "profile default"
991
+ : `session override; profile default: ${snapshot.defaultEffort}`;
992
+ return `Reasoning effort: ${snapshot.effort} (${source}). Available: ${snapshot.supportedEfforts.join(", ")}.`;
993
+ }
994
+
853
995
  function errorMessage(error: unknown): string {
854
996
  return error instanceof Error ? error.message : String(error);
855
997
  }
@@ -947,7 +1089,7 @@ export function formatContextRetirementNotice(result: ContextRetirementResult):
947
1089
  const before = result.guardedTokensBefore.toLocaleString("en-US");
948
1090
  const after = result.guardedTokensAfter.toLocaleString("en-US");
949
1091
  if (result.outcome === "retirement_floor") {
950
- return `Context prefix retired: revision ${result.previousRevisionNumber} -> ${result.revisionNumber}, ${result.retiredTurnCount} turns removed from the active request, ${before} -> ${after} estimated tokens; target ${result.targetTokens.toLocaleString("en-US")} was not reached. Run /compact first when retained tool output is still eligible. Older history remains available through Recall.`;
1092
+ return `Context prefix retired: revision ${result.previousRevisionNumber} -> ${result.revisionNumber}, ${result.retiredTurnCount} turns removed from the active request, ${before} -> ${after} estimated tokens; target ${result.targetTokens.toLocaleString("en-US")} was not reached. Run /compact first when retained tool output is still eligible. Older history remains available through RecallSearch and RecallGet.`;
951
1093
  }
952
1094
  const reduction =
953
1095
  result.guardedTokensBefore === 0
@@ -957,7 +1099,7 @@ export function formatContextRetirementNotice(result: ContextRetirementResult):
957
1099
  result.guardedTokensBefore) *
958
1100
  100
959
1101
  ).toFixed(1);
960
- return `Context prefix retired: revision ${result.previousRevisionNumber} -> ${result.revisionNumber}, ${result.retiredTurnCount} turns removed from the active request, ${before} -> ${after} estimated tokens (-${reduction}%). Older history remains available through Recall.`;
1102
+ return `Context prefix retired: revision ${result.previousRevisionNumber} -> ${result.revisionNumber}, ${result.retiredTurnCount} turns removed from the active request, ${before} -> ${after} estimated tokens (-${reduction}%). Older history remains available through RecallSearch and RecallGet.`;
961
1103
  }
962
1104
 
963
1105
  export function formatContextRetirementFailureNotice(error: unknown): string {
@@ -4,6 +4,7 @@ export type FooterProps = {
4
4
  status: "idle" | "running" | "cancelling" | "cancelled" | "done" | "failed";
5
5
  workedForMs?: number;
6
6
  yolo?: boolean;
7
+ pendingFollowUps?: number;
7
8
  };
8
9
 
9
10
  export function Footer(props: FooterProps) {
@@ -26,7 +27,11 @@ export function Footer(props: FooterProps) {
26
27
  }
27
28
 
28
29
  if (props.status === "running") {
29
- return <Spinner label={`Running${suffix}`} />;
30
+ const queued =
31
+ props.pendingFollowUps === undefined || props.pendingFollowUps === 0
32
+ ? ""
33
+ : ` · ${props.pendingFollowUps} follow-up${props.pendingFollowUps === 1 ? "" : "s"} queued`;
34
+ return <Spinner label={`Running${queued}${suffix}`} />;
30
35
  }
31
36
 
32
37
  if (props.status === "cancelling") {
@@ -60,6 +60,7 @@ export type PromptSubmissionOutcome =
60
60
 
61
61
  export type PromptInputProps = {
62
62
  modelName: string;
63
+ reasoningEffort?: string;
63
64
  workspaceRoot: string;
64
65
  gitBranch?: string;
65
66
  contextUsage?: ContextUsageSnapshot;
@@ -81,6 +82,7 @@ export type PromptInputProps = {
81
82
  assets: readonly ImageAssetRef[],
82
83
  signal: AbortSignal,
83
84
  ) => Promise<void>;
85
+ onCycleReasoningEffort?: () => void;
84
86
  onSubmit: (
85
87
  submission: PromptSubmission,
86
88
  signal: AbortSignal,
@@ -605,6 +607,10 @@ export function PromptInput(props: PromptInputProps) {
605
607
  }
606
608
  return;
607
609
  }
610
+ if (key.ctrl && input === "r") {
611
+ props.onCycleReasoningEffort?.();
612
+ return;
613
+ }
608
614
  const selectedFile = fileMatches[selectedIndex];
609
615
  const selectedCommand = suggestions[selectedIndex];
610
616
  if (key.return) {
@@ -736,7 +742,9 @@ export function PromptInput(props: PromptInputProps) {
736
742
  {showSuggestions ? null : (
737
743
  <Box>
738
744
  <Text dimColor>
739
- {props.modelName} · {formatWorkspacePath(props.workspaceRoot)}
745
+ {props.modelName}
746
+ {props.reasoningEffort === undefined ? null : ` ${props.reasoningEffort}`} ·{" "}
747
+ {formatWorkspacePath(props.workspaceRoot)}
740
748
  {props.gitBranch === undefined ? null : ` · ${props.gitBranch}`}
741
749
  {state.phase.kind === "idle" ? null : ` · ${phaseLabel(state.phase)}`}
742
750
  </Text>
@@ -155,6 +155,21 @@ export function reduceTuiProjection(
155
155
  },
156
156
  };
157
157
  }
158
+ case "turn.steering.applied": {
159
+ const userPrompt = truncateUserPromptProjection(
160
+ event.data.userPrompt,
161
+ MAX_TIMELINE_PROMPT_CODE_POINTS,
162
+ );
163
+ return updateActiveTurn(state, event, policy, (turn) =>
164
+ appendTurnItem(turn, {
165
+ id: `turn-${event.turnId}-steering-${event.eventSequence}`,
166
+ label: "follow-up",
167
+ text: userPrompt.text,
168
+ userPrompt,
169
+ status: "text",
170
+ }),
171
+ );
172
+ }
158
173
  case "model.request.started":
159
174
  return updateActiveTurn(state, event, policy, (turn) =>
160
175
  event.data.attemptNumber === 1
@@ -65,6 +65,11 @@ export const SLASH_COMMANDS: readonly BuiltInSlashCommand[] = [
65
65
  usage: "/model [profile-name]",
66
66
  description: "Switch model profile (new session)",
67
67
  },
68
+ {
69
+ name: "reasoning",
70
+ usage: "/reasoning [effort|reset]",
71
+ description: "Show or change reasoning effort for this session runtime",
72
+ },
68
73
  {
69
74
  name: "resume",
70
75
  usage: "/resume [session-id]",
@@ -95,6 +100,9 @@ export type ParsedSlashCommand =
95
100
  | { type: "quit" }
96
101
  | { type: "model" }
97
102
  | { type: "model_switch"; profileName: string }
103
+ | { type: "reasoning_status" }
104
+ | { type: "reasoning_reset" }
105
+ | { type: "reasoning_set"; effort: string }
98
106
  | { type: "resume_list" }
99
107
  | { type: "resume"; sessionId: SessionId }
100
108
  | { type: "session_delete"; sessionId: SessionId };
@@ -193,6 +201,18 @@ export function parseSlashCommand(input: string): ParsedSlashCommand {
193
201
  }
194
202
  throw slashCommandUsageError("model");
195
203
  }
204
+ if (command === "/reasoning") {
205
+ if (tokens.length === 1) {
206
+ return { type: "reasoning_status" };
207
+ }
208
+ if (tokens.length === 2 && tokens[1] === "reset") {
209
+ return { type: "reasoning_reset" };
210
+ }
211
+ if (tokens.length === 2) {
212
+ return { type: "reasoning_set", effort: tokens[1] };
213
+ }
214
+ throw slashCommandUsageError("reasoning");
215
+ }
196
216
  if (command === "/resume") {
197
217
  if (tokens.length === 1) {
198
218
  return { type: "resume_list" };
@@ -18,6 +18,7 @@ import type { SessionId } from "../ids/runtime-id";
18
18
  import { createUuidV7 } from "../ids/uuid-v7";
19
19
  import type { ModelProfile } from "../cli/model-profiles";
20
20
  import type { McpInventorySnapshot } from "../mcp/mcp-manager";
21
+ import type { ReasoningEffortSnapshot } from "../model/reasoning-effort";
21
22
  import { SessionCatalog, type SessionSummary } from "../session/session-catalog";
22
23
  import type { TuiProjectionStore } from "./tui-projection-store";
23
24
 
@@ -29,6 +30,9 @@ export type TuiSessionBinding = {
29
30
  projectionStore: TuiProjectionStore;
30
31
  skills(): RuntimeSkillsSnapshot;
31
32
  mcp(): McpInventorySnapshot;
33
+ reasoningEffort?: () => ReasoningEffortSnapshot | undefined;
34
+ setReasoningEffort?: (effort: string) => ReasoningEffortSnapshot;
35
+ resetReasoningEffort?: () => ReasoningEffortSnapshot;
32
36
  supportsImageInput?: () => boolean;
33
37
  importImage?: (
34
38
  sourcePath: string,
@@ -41,6 +45,9 @@ export type TuiSessionBinding = {
41
45
  ) => Promise<void>;
42
46
  admitTurn?: (userMessage: UserMessage, signal: AbortSignal) => Promise<AcceptedTurn>;
43
47
  executeTurn(userMessage: UserMessage, signal: AbortSignal): Promise<RunAgentResult>;
48
+ promptScheduler?: RuntimeSession["promptScheduler"];
49
+ subscribePromptScheduler?: RuntimeSession["subscribePromptScheduler"];
50
+ queueFollowUp?: RuntimeSession["queueFollowUp"];
44
51
  bashGuard(): BashGuardSnapshot;
45
52
  subscribeBashGuard(listener: () => void): () => void;
46
53
  setYoloMode(enabled: boolean): void;
@@ -227,6 +234,9 @@ export function managedTuiBinding(input: {
227
234
  runtimeSession: input.runtimeSession,
228
235
  skills: () => input.runtimeSession.skills(),
229
236
  mcp: () => input.runtimeSession.mcp(),
237
+ reasoningEffort: () => input.runtimeSession.reasoningEffort(),
238
+ setReasoningEffort: (effort) => input.runtimeSession.setReasoningEffort(effort),
239
+ resetReasoningEffort: () => input.runtimeSession.resetReasoningEffort(),
230
240
  supportsImageInput: () => input.runtimeSession.supportsImageInput(),
231
241
  importImage: (sourcePath, signal, prospectiveMessageImageCount) =>
232
242
  input.runtimeSession.importImage(
@@ -243,6 +253,10 @@ export function managedTuiBinding(input: {
243
253
  userMessage,
244
254
  signal,
245
255
  } satisfies ExecuteTurnInput),
256
+ promptScheduler: () => input.runtimeSession.promptScheduler(),
257
+ subscribePromptScheduler: (listener) =>
258
+ input.runtimeSession.subscribePromptScheduler(listener),
259
+ queueFollowUp: (userMessage) => input.runtimeSession.queueFollowUp(userMessage),
246
260
  bashGuard: () => input.runtimeSession.bashGuard(),
247
261
  subscribeBashGuard: (listener) => input.runtimeSession.subscribeBashGuard(listener),
248
262
  setYoloMode: (enabled) => input.runtimeSession.setYoloMode(enabled),