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.
- package/CHANGELOG.md +36 -1
- package/README.md +64 -6
- package/package.json +1 -1
- package/src/agent/loop.ts +17 -0
- package/src/agent/runtime-session.ts +341 -1
- package/src/agent/session-ledger.ts +100 -3
- package/src/cli/config.ts +11 -2
- package/src/cli/model-profiles.ts +58 -0
- package/src/cli/public-config-contract.ts +73 -7
- package/src/cli/run-runner.ts +4 -1
- package/src/cli/runner-dependencies.ts +28 -4
- package/src/cli/tui-memory.ts +4 -0
- package/src/cli/tui-runner.tsx +8 -1
- package/src/context/context-automation-policy.ts +22 -21
- package/src/context/context-manager.ts +91 -15
- package/src/context/context-policy.ts +0 -2
- package/src/context/context-swap-renderer.ts +1 -1
- package/src/context/prefix-retirement-planner.ts +58 -8
- package/src/context/recall-retirement-contract.ts +5 -4
- package/src/context/swap-planner.ts +33 -27
- package/src/events/observation-text-log.ts +4 -0
- package/src/events/stdout-event-printer.ts +5 -0
- package/src/events/types.ts +5 -1
- package/src/model/fake-model-client.ts +55 -16
- package/src/model/model-api.ts +12 -0
- package/src/model/model-client.ts +9 -1
- package/src/model/moonshot-input-token-estimator.ts +5 -1
- package/src/model/openai-chat-mapping.ts +2 -24
- package/src/model/openai-chat-model-client.ts +18 -294
- package/src/model/openai-image-mapping.ts +20 -0
- package/src/model/openai-model-utils.ts +304 -0
- package/src/model/openai-responses-mapping.ts +532 -0
- package/src/model/openai-responses-model-client.ts +295 -0
- package/src/model/openai-responses-stream.ts +96 -0
- package/src/model/openai-responses-token-estimator.ts +155 -0
- package/src/model/reasoning-effort.ts +60 -0
- package/src/session/session-catalog.ts +2 -2
- package/src/session/session-history-reader.ts +6 -1
- package/src/session/session-schema.ts +268 -4
- package/src/session/session-store.ts +134 -26
- package/src/skills/skill-context.ts +2 -2
- package/src/tools/bounded-output-preview.ts +276 -0
- package/src/tools/recall.ts +67 -36
- package/src/tools/registry.ts +7 -2
- package/src/tools/task-output-snapshot.ts +6 -22
- package/src/tools/task-output.ts +23 -27
- package/src/tui/app.tsx +153 -11
- package/src/tui/components/footer.tsx +6 -1
- package/src/tui/components/prompt-input.tsx +9 -1
- package/src/tui/event-store.ts +15 -0
- package/src/tui/slash-commands.ts +20 -0
- 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 =
|
|
187
|
-
|
|
188
|
-
|
|
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:
|
|
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={
|
|
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={
|
|
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=
|
|
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
|
-
{
|
|
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
|
|
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
|
|
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
|
-
|
|
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}
|
|
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>
|
package/src/tui/event-store.ts
CHANGED
|
@@ -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),
|