@nghyane/arcane 0.1.12 → 0.1.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/CHANGELOG.md +28 -0
- package/package.json +21 -70
- package/scripts/format-prompts.ts +1 -3
- package/src/cli/args.ts +2 -7
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli.ts +0 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/grep.ts +1 -1
- package/src/commands/jupyter.ts +1 -1
- package/src/commands/plugin.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/shell.ts +1 -1
- package/src/commands/ssh.ts +1 -1
- package/src/commands/stats.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config/model-registry.ts +3 -4
- package/src/config/model-resolver.ts +36 -9
- package/src/config/prompt-templates.ts +1 -9
- package/src/config/settings-schema.ts +32 -88
- package/src/config/settings.ts +3 -4
- package/src/debug/index.ts +1 -1
- package/src/debug/log-formatting.ts +1 -1
- package/src/debug/log-viewer.ts +2 -2
- package/src/discovery/helpers.ts +13 -3
- package/src/exa/company.ts +2 -7
- package/src/exa/index.ts +1 -35
- package/src/exa/linkedin.ts +2 -7
- package/src/exa/mcp-client.ts +21 -11
- package/src/exa/render.ts +30 -190
- package/src/exa/researcher.ts +2 -12
- package/src/exa/search.ts +5 -25
- package/src/exa/types.ts +3 -3
- package/src/exec/bash-executor.ts +2 -1
- package/src/exec/non-interactive-env.ts +43 -0
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +5 -1
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/extensions/wrapper.ts +7 -15
- package/src/extensibility/hooks/runner.ts +1 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/index.ts +13 -13
- package/src/lsp/index.ts +77 -24
- package/src/lsp/render.ts +34 -583
- package/src/lsp/types.ts +3 -3
- package/src/lsp/utils.ts +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -24
- package/src/modes/components/assistant-message.ts +7 -7
- package/src/modes/components/bash-execution.ts +48 -113
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/branch-summary-message.ts +13 -10
- package/src/modes/components/compaction-summary-message.ts +14 -13
- package/src/modes/components/context-group.ts +106 -0
- package/src/modes/components/custom-message.ts +4 -5
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +8 -3
- package/src/modes/components/footer.ts +2 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-message.ts +4 -5
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/components/index.ts +0 -2
- package/src/modes/components/keybinding-hints.ts +1 -1
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/oauth-selector.ts +1 -1
- package/src/modes/components/plugin-settings.ts +1 -1
- package/src/modes/components/python-execution.ts +49 -92
- package/src/modes/components/queue-mode-selector.ts +1 -1
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-defs.ts +5 -10
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/show-images-selector.ts +1 -1
- package/src/modes/components/skill-message.ts +4 -4
- package/src/modes/components/status-line/segments.ts +2 -2
- package/src/modes/components/status-line/separators.ts +1 -1
- package/src/modes/components/status-line-segment-editor.ts +1 -1
- package/src/modes/components/status-line.ts +1 -1
- package/src/modes/components/theme-selector.ts +1 -1
- package/src/modes/components/thinking-selector.ts +1 -1
- package/src/modes/components/todo-display.ts +2 -4
- package/src/modes/components/todo-reminder.ts +4 -4
- package/src/modes/components/tool-execution.ts +118 -440
- package/src/modes/components/tool-image-display.ts +107 -0
- package/src/modes/components/tree-selector.ts +2 -2
- package/src/modes/components/ttsr-notification.ts +4 -17
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +9 -10
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +58 -187
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +3 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +3 -26
- package/src/modes/controllers/ssh-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +3 -7
- package/src/modes/print-mode.ts +5 -5
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/types.ts +1 -2
- package/src/modes/utils/ui-helpers.ts +34 -32
- package/src/patch/edit-tool.ts +742 -0
- package/src/patch/index.ts +32 -898
- package/src/patch/schemas.ts +208 -0
- package/src/patch/shared.ts +83 -151
- package/src/prompts/agents/explore.md +22 -37
- package/src/prompts/agents/frontmatter.md +1 -1
- package/src/prompts/agents/init.md +2 -2
- package/src/prompts/agents/librarian.md +30 -21
- package/src/prompts/agents/oracle.md +9 -2
- package/src/prompts/agents/reviewer.md +15 -49
- package/src/prompts/agents/task.md +17 -9
- package/src/prompts/compaction/branch-summary-context.md +1 -1
- package/src/prompts/compaction/branch-summary-preamble.md +1 -1
- package/src/prompts/compaction/branch-summary.md +4 -1
- package/src/prompts/compaction/compaction-short-summary.md +1 -1
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +4 -1
- package/src/prompts/compaction/compaction-turn-prefix.md +1 -1
- package/src/prompts/compaction/compaction-update-summary.md +1 -1
- package/src/prompts/memories/consolidation.md +1 -1
- package/src/prompts/memories/read_path.md +1 -1
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +1 -1
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +1 -1
- package/src/prompts/system/agent-creation-user.md +1 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/file-operations.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +2 -2
- package/src/prompts/system/summarization-system.md +1 -1
- package/src/prompts/system/system-prompt.md +163 -178
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/system/verification-reminder.md +6 -0
- package/src/prompts/system/web-search.md +1 -1
- package/src/sdk.ts +0 -9
- package/src/session/agent-session.ts +244 -1459
- package/src/session/auth-storage.ts +5 -0
- package/src/session/model-controller.ts +406 -0
- package/src/session/retry-utils.ts +71 -0
- package/src/session/session-manager.ts +22 -186
- package/src/session/session-types.ts +312 -0
- package/src/session/stats.ts +387 -0
- package/src/session/streaming-edit.ts +258 -0
- package/src/session/ttsr.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +0 -8
- package/src/ssh/connection-manager.ts +1 -0
- package/src/stt/recorder.ts +2 -2
- package/src/system-prompt.ts +1 -14
- package/src/task/agents.ts +7 -33
- package/src/task/executor.ts +50 -438
- package/src/task/index.ts +104 -71
- package/src/task/progress-tracker.ts +390 -0
- package/src/task/render.ts +371 -187
- package/src/task/subprocess-tool-registry.ts +1 -1
- package/src/task/types.ts +14 -47
- package/src/tools/ask.ts +31 -42
- package/src/tools/bash-interactive.ts +4 -47
- package/src/tools/bash-interceptor.ts +2 -2
- package/src/tools/bash-normalize.ts +1 -1
- package/src/tools/bash-skill-urls.ts +2 -2
- package/src/tools/bash.ts +87 -136
- package/src/tools/browser.ts +54 -84
- package/src/tools/create-tools.ts +186 -0
- package/src/tools/default-renderer.ts +104 -0
- package/src/tools/explore.ts +11 -10
- package/src/tools/fetch.ts +24 -114
- package/src/tools/find.ts +48 -132
- package/src/tools/gemini-image.ts +5 -15
- package/src/tools/github.ts +450 -0
- package/src/tools/grep.ts +43 -179
- package/src/tools/index.ts +35 -198
- package/src/tools/json-tree.ts +3 -3
- package/src/tools/librarian.ts +18 -18
- package/src/tools/list-limit.ts +2 -2
- package/src/tools/notebook.ts +35 -87
- package/src/tools/oracle.ts +25 -25
- package/src/tools/output-meta.ts +89 -4
- package/src/tools/output-utils.ts +2 -2
- package/src/tools/python.ts +86 -637
- package/src/tools/read.ts +36 -119
- package/src/tools/reviewer-tool.ts +19 -21
- package/src/tools/search-code.ts +128 -0
- package/src/tools/ssh.ts +67 -126
- package/src/tools/subagent-tool.ts +197 -123
- package/src/tools/todo-write.ts +15 -31
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/undo-edit.ts +30 -67
- package/src/tools/write.ts +78 -127
- package/src/tui/code-cell.ts +4 -4
- package/src/tui/file-list.ts +2 -2
- package/src/tui/output-block.ts +1 -1
- package/src/tui/status-line.ts +1 -1
- package/src/tui/tree-list.ts +2 -2
- package/src/tui/types.ts +1 -1
- package/src/tui/utils.ts +1 -1
- package/src/{tools → ui}/render-utils.ts +87 -126
- package/src/utils/external-editor.ts +4 -4
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/index.ts +30 -0
- package/src/utils/tools-manager.ts +9 -19
- package/src/web/github-client.ts +290 -0
- package/src/web/scrapers/github.ts +11 -62
- package/src/web/search/auth.ts +1 -3
- package/src/web/search/index.ts +85 -49
- package/src/web/search/provider.ts +11 -16
- package/src/web/search/providers/grep.ts +160 -0
- package/src/web/search/render.ts +48 -235
- package/src/web/search/types.ts +1 -1
- package/src/commands/commit.ts +0 -36
- package/src/commit/agentic/agent.ts +0 -311
- package/src/commit/agentic/fallback.ts +0 -96
- package/src/commit/agentic/index.ts +0 -359
- package/src/commit/agentic/prompts/analyze-file.md +0 -22
- package/src/commit/agentic/prompts/session-user.md +0 -25
- package/src/commit/agentic/prompts/split-confirm.md +0 -1
- package/src/commit/agentic/prompts/system.md +0 -38
- package/src/commit/agentic/state.ts +0 -69
- package/src/commit/agentic/tools/analyze-file.ts +0 -118
- package/src/commit/agentic/tools/git-file-diff.ts +0 -194
- package/src/commit/agentic/tools/git-hunk.ts +0 -50
- package/src/commit/agentic/tools/git-overview.ts +0 -84
- package/src/commit/agentic/tools/index.ts +0 -56
- package/src/commit/agentic/tools/propose-changelog.ts +0 -128
- package/src/commit/agentic/tools/propose-commit.ts +0 -154
- package/src/commit/agentic/tools/recent-commits.ts +0 -81
- package/src/commit/agentic/tools/split-commit.ts +0 -280
- package/src/commit/agentic/topo-sort.ts +0 -44
- package/src/commit/agentic/trivial.ts +0 -51
- package/src/commit/agentic/validation.ts +0 -200
- package/src/commit/analysis/conventional.ts +0 -165
- package/src/commit/analysis/index.ts +0 -4
- package/src/commit/analysis/scope.ts +0 -242
- package/src/commit/analysis/summary.ts +0 -112
- package/src/commit/analysis/validation.ts +0 -66
- package/src/commit/changelog/detect.ts +0 -37
- package/src/commit/changelog/generate.ts +0 -110
- package/src/commit/changelog/index.ts +0 -234
- package/src/commit/changelog/parse.ts +0 -44
- package/src/commit/cli.ts +0 -93
- package/src/commit/git/diff.ts +0 -148
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -211
- package/src/commit/git/operations.ts +0 -54
- package/src/commit/index.ts +0 -5
- package/src/commit/map-reduce/index.ts +0 -64
- package/src/commit/map-reduce/map-phase.ts +0 -178
- package/src/commit/map-reduce/reduce-phase.ts +0 -145
- package/src/commit/map-reduce/utils.ts +0 -9
- package/src/commit/message.ts +0 -11
- package/src/commit/model-selection.ts +0 -69
- package/src/commit/pipeline.ts +0 -243
- package/src/commit/prompts/analysis-system.md +0 -148
- package/src/commit/prompts/analysis-user.md +0 -38
- package/src/commit/prompts/changelog-system.md +0 -50
- package/src/commit/prompts/changelog-user.md +0 -18
- package/src/commit/prompts/file-observer-system.md +0 -24
- package/src/commit/prompts/file-observer-user.md +0 -8
- package/src/commit/prompts/reduce-system.md +0 -50
- package/src/commit/prompts/reduce-user.md +0 -17
- package/src/commit/prompts/summary-retry.md +0 -3
- package/src/commit/prompts/summary-system.md +0 -38
- package/src/commit/prompts/summary-user.md +0 -13
- package/src/commit/prompts/types-description.md +0 -2
- package/src/commit/types.ts +0 -109
- package/src/commit/utils/exclusions.ts +0 -42
- package/src/mcp/render.ts +0 -123
- package/src/modes/components/agent-dashboard.ts +0 -1130
- package/src/modes/components/codemode-group.ts +0 -369
- package/src/modes/components/read-tool-group.ts +0 -119
- package/src/modes/components/visual-truncate.ts +0 -63
- package/src/prompts/system/subagent-user-prompt.md +0 -8
- package/src/prompts/tools/ask.md +0 -44
- package/src/prompts/tools/bash.md +0 -24
- package/src/prompts/tools/browser.md +0 -33
- package/src/prompts/tools/calculator.md +0 -12
- package/src/prompts/tools/explore.md +0 -29
- package/src/prompts/tools/fetch.md +0 -16
- package/src/prompts/tools/find.md +0 -18
- package/src/prompts/tools/gemini-image.md +0 -23
- package/src/prompts/tools/grep.md +0 -28
- package/src/prompts/tools/hashline.md +0 -232
- package/src/prompts/tools/librarian.md +0 -24
- package/src/prompts/tools/lsp.md +0 -28
- package/src/prompts/tools/oracle.md +0 -26
- package/src/prompts/tools/patch.md +0 -74
- package/src/prompts/tools/python.md +0 -66
- package/src/prompts/tools/read.md +0 -36
- package/src/prompts/tools/replace.md +0 -38
- package/src/prompts/tools/reviewer.md +0 -41
- package/src/prompts/tools/ssh.md +0 -51
- package/src/prompts/tools/task-summary.md +0 -28
- package/src/prompts/tools/task.md +0 -146
- package/src/prompts/tools/todo-write.md +0 -65
- package/src/prompts/tools/undo-edit.md +0 -7
- package/src/prompts/tools/web-search.md +0 -19
- package/src/prompts/tools/write.md +0 -18
- package/src/task/batch.ts +0 -102
- package/src/task/discovery.ts +0 -126
- package/src/task/parallel.ts +0 -84
- package/src/task/template.ts +0 -32
- package/src/tools/calculator.ts +0 -537
- package/src/tools/jtd-to-typescript.ts +0 -198
- package/src/tools/renderers.ts +0 -60
- package/src/tools/tool-result.ts +0 -86
- /package/src/{modes/theme → theme}/dark.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
- /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
- /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
- /package/src/{modes/theme → theme}/light.json +0 -0
- /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
- /package/src/{modes/theme → theme}/theme-schema.json +0 -0
- /package/src/{modes/theme → theme}/theme.ts +0 -0
|
@@ -663,6 +663,11 @@ export class AuthStorage {
|
|
|
663
663
|
let credentials: OAuthCredentials;
|
|
664
664
|
const saveApiKeyCredential = async (apiKey: string): Promise<void> => {
|
|
665
665
|
const newCredential: ApiKeyCredential = { type: "api_key", key: apiKey };
|
|
666
|
+
const shouldReplaceExisting = provider === "minimax-code" || provider === "minimax-code-cn";
|
|
667
|
+
if (shouldReplaceExisting) {
|
|
668
|
+
await this.set(provider, newCredential);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
666
671
|
const existing = this.#getCredentialsForProvider(provider);
|
|
667
672
|
if (existing.length === 0) {
|
|
668
673
|
await this.set(provider, newCredential);
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import type { Agent, ThinkingLevel } from "@nghyane/arcane-agent";
|
|
2
|
+
import type { Model, ProviderSessionState } from "@nghyane/arcane-ai";
|
|
3
|
+
import { modelsAreEqual, supportsXhigh } from "@nghyane/arcane-ai";
|
|
4
|
+
import { logger } from "@nghyane/arcane-utils";
|
|
5
|
+
import { MODEL_ROLE_IDS, type ModelRegistry, type ModelRole } from "../config/model-registry";
|
|
6
|
+
import { expandRoleAlias, parseModelString } from "../config/model-resolver";
|
|
7
|
+
import type { Settings } from "../config/settings";
|
|
8
|
+
import type { SessionManager } from "./session-manager";
|
|
9
|
+
import type { ModelCycleResult, RoleModelCycleResult } from "./session-types";
|
|
10
|
+
|
|
11
|
+
/** Standard thinking levels */
|
|
12
|
+
const THINKING_LEVELS: ThinkingLevel[] = ["off", "minimal", "low", "medium", "high"];
|
|
13
|
+
|
|
14
|
+
/** Thinking levels including xhigh (for supported models) */
|
|
15
|
+
const THINKING_LEVELS_WITH_XHIGH: ThinkingLevel[] = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Owns model selection, thinking level, and scoped model cycling.
|
|
19
|
+
*
|
|
20
|
+
* AgentSession delegates all model/thinking operations here.
|
|
21
|
+
* The controller mutates agent state (model, thinking level) and
|
|
22
|
+
* persists changes to sessionManager and settings.
|
|
23
|
+
*/
|
|
24
|
+
export class ModelController {
|
|
25
|
+
#agent: Agent;
|
|
26
|
+
#settings: Settings;
|
|
27
|
+
#sessionManager: SessionManager;
|
|
28
|
+
#modelRegistry: ModelRegistry;
|
|
29
|
+
#scopedModels: Array<{ model: Model; thinkingLevel: ThinkingLevel }>;
|
|
30
|
+
#forceCopilotAgentInitiator: boolean;
|
|
31
|
+
#providerSessionState = new Map<string, ProviderSessionState>();
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
agent: Agent,
|
|
35
|
+
settings: Settings,
|
|
36
|
+
sessionManager: SessionManager,
|
|
37
|
+
modelRegistry: ModelRegistry,
|
|
38
|
+
options: {
|
|
39
|
+
scopedModels?: Array<{ model: Model; thinkingLevel: ThinkingLevel }>;
|
|
40
|
+
forceCopilotAgentInitiator?: boolean;
|
|
41
|
+
},
|
|
42
|
+
) {
|
|
43
|
+
this.#agent = agent;
|
|
44
|
+
this.#settings = settings;
|
|
45
|
+
this.#sessionManager = sessionManager;
|
|
46
|
+
this.#modelRegistry = modelRegistry;
|
|
47
|
+
this.#scopedModels = options.scopedModels ? [...options.scopedModels] : [];
|
|
48
|
+
this.#forceCopilotAgentInitiator = options.forceCopilotAgentInitiator ?? false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get registry(): ModelRegistry {
|
|
52
|
+
return this.#modelRegistry;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get model(): Model | undefined {
|
|
56
|
+
return this.#agent.state.model;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get thinkingLevel(): ThinkingLevel {
|
|
60
|
+
return this.#agent.state.thinkingLevel;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get scopedModels(): ReadonlyArray<{ model: Model; thinkingLevel: ThinkingLevel }> {
|
|
64
|
+
return this.#scopedModels;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get providerSessionState(): Map<string, ProviderSessionState> {
|
|
68
|
+
return this.#providerSessionState;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
applySessionModelOverrides(model: Model): Model {
|
|
72
|
+
if (!this.#forceCopilotAgentInitiator || model.provider !== "github-copilot") {
|
|
73
|
+
return model;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
...model,
|
|
77
|
+
headers: {
|
|
78
|
+
...model.headers,
|
|
79
|
+
"X-Initiator": "agent",
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setModelDirect(model: Model): void {
|
|
85
|
+
const currentModel = this.model;
|
|
86
|
+
if (currentModel) {
|
|
87
|
+
this.#closeProviderSessionsForModelSwitch(currentModel, model);
|
|
88
|
+
}
|
|
89
|
+
this.#agent.setModel(this.applySessionModelOverrides(model));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async setModel(model: Model, role: ModelRole = "default"): Promise<void> {
|
|
93
|
+
const apiKey = await this.#modelRegistry.getApiKey(model, this.#sessionId);
|
|
94
|
+
if (!apiKey) {
|
|
95
|
+
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.setModelDirect(model);
|
|
99
|
+
this.#sessionManager.appendModelChange(`${model.provider}/${model.id}`, role);
|
|
100
|
+
this.#settings.setModelRole(role, `${model.provider}/${model.id}`);
|
|
101
|
+
this.#settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async setModelTemporary(model: Model): Promise<void> {
|
|
105
|
+
const apiKey = await this.#modelRegistry.getApiKey(model, this.#sessionId);
|
|
106
|
+
if (!apiKey) {
|
|
107
|
+
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.setModelDirect(model);
|
|
111
|
+
this.#sessionManager.appendModelChange(`${model.provider}/${model.id}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async cycleModel(direction: "forward" | "backward" = "forward"): Promise<ModelCycleResult | undefined> {
|
|
115
|
+
if (this.#scopedModels.length > 0) {
|
|
116
|
+
return this.#cycleScopedModel(direction);
|
|
117
|
+
}
|
|
118
|
+
return this.#cycleAvailableModel(direction);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async cycleRoleModels(
|
|
122
|
+
roleOrder: readonly ModelRole[],
|
|
123
|
+
options?: { temporary?: boolean },
|
|
124
|
+
): Promise<RoleModelCycleResult | undefined> {
|
|
125
|
+
const availableModels = this.#modelRegistry.getAvailable();
|
|
126
|
+
if (availableModels.length === 0) return undefined;
|
|
127
|
+
|
|
128
|
+
const currentModel = this.model;
|
|
129
|
+
if (!currentModel) return undefined;
|
|
130
|
+
const roleModels: Array<{ role: ModelRole; model: Model }> = [];
|
|
131
|
+
|
|
132
|
+
for (const role of roleOrder) {
|
|
133
|
+
const roleModelStr =
|
|
134
|
+
role === "default"
|
|
135
|
+
? (this.#settings.getModelRole("default") ?? `${currentModel.provider}/${currentModel.id}`)
|
|
136
|
+
: this.#settings.getModelRole(role);
|
|
137
|
+
if (!roleModelStr) continue;
|
|
138
|
+
|
|
139
|
+
const expandedRoleModelStr = expandRoleAlias(roleModelStr, this.#settings);
|
|
140
|
+
const parsed = parseModelString(expandedRoleModelStr);
|
|
141
|
+
let match: Model | undefined;
|
|
142
|
+
if (parsed) {
|
|
143
|
+
match = availableModels.find(m => m.provider === parsed.provider && m.id === parsed.id);
|
|
144
|
+
}
|
|
145
|
+
if (!match) {
|
|
146
|
+
match = availableModels.find(m => m.id.toLowerCase() === expandedRoleModelStr.toLowerCase());
|
|
147
|
+
}
|
|
148
|
+
if (!match) continue;
|
|
149
|
+
|
|
150
|
+
roleModels.push({ role, model: match });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (roleModels.length <= 1) return undefined;
|
|
154
|
+
|
|
155
|
+
const lastRole = this.#sessionManager.getLastModelChangeRole();
|
|
156
|
+
let currentIndex = lastRole
|
|
157
|
+
? roleModels.findIndex(entry => entry.role === lastRole)
|
|
158
|
+
: roleModels.findIndex(entry => modelsAreEqual(entry.model, currentModel));
|
|
159
|
+
if (currentIndex === -1) currentIndex = 0;
|
|
160
|
+
|
|
161
|
+
const nextIndex = (currentIndex + 1) % roleModels.length;
|
|
162
|
+
const next = roleModels[nextIndex];
|
|
163
|
+
|
|
164
|
+
if (options?.temporary) {
|
|
165
|
+
await this.setModelTemporary(next.model);
|
|
166
|
+
} else {
|
|
167
|
+
await this.setModel(next.model, next.role);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { model: next.model, thinkingLevel: this.thinkingLevel, role: next.role };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
resolveRoleModel(role: ModelRole): Model | undefined {
|
|
174
|
+
return this.#resolveRoleModel(role, this.#modelRegistry.getAvailable(), this.model);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getAvailableModels(): Model[] {
|
|
178
|
+
return this.#modelRegistry.getAvailable();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
setThinkingLevel(level: ThinkingLevel, persist = false): void {
|
|
182
|
+
const availableLevels = this.getAvailableThinkingLevels();
|
|
183
|
+
const effectiveLevel = availableLevels.includes(level) ? level : this.clampThinkingLevel(level, availableLevels);
|
|
184
|
+
|
|
185
|
+
const isChanging = effectiveLevel !== this.#agent.state.thinkingLevel;
|
|
186
|
+
|
|
187
|
+
this.#agent.setThinkingLevel(effectiveLevel);
|
|
188
|
+
|
|
189
|
+
if (isChanging) {
|
|
190
|
+
this.#sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
191
|
+
if (persist) {
|
|
192
|
+
this.#settings.set("defaultThinkingLevel", effectiveLevel);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
cycleThinkingLevel(): ThinkingLevel | undefined {
|
|
198
|
+
if (!this.supportsThinking()) return undefined;
|
|
199
|
+
|
|
200
|
+
const levels = this.getAvailableThinkingLevels();
|
|
201
|
+
const currentIndex = levels.indexOf(this.thinkingLevel);
|
|
202
|
+
const nextIndex = (currentIndex + 1) % levels.length;
|
|
203
|
+
const nextLevel = levels[nextIndex];
|
|
204
|
+
|
|
205
|
+
this.setThinkingLevel(nextLevel);
|
|
206
|
+
return nextLevel;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
getAvailableThinkingLevels(): ThinkingLevel[] {
|
|
210
|
+
if (!this.supportsThinking()) return ["off"];
|
|
211
|
+
return this.supportsXhighThinking() ? THINKING_LEVELS_WITH_XHIGH : THINKING_LEVELS;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
supportsXhighThinking(): boolean {
|
|
215
|
+
return this.model ? supportsXhigh(this.model) : false;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
supportsThinking(): boolean {
|
|
219
|
+
return !!this.model?.reasoning;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
getCompactionModelCandidates(availableModels: Model[]): Model[] {
|
|
223
|
+
const candidates: Model[] = [];
|
|
224
|
+
const seen = new Set<string>();
|
|
225
|
+
|
|
226
|
+
const addCandidate = (model: Model | undefined): void => {
|
|
227
|
+
if (!model) return;
|
|
228
|
+
const key = this.getModelKey(model);
|
|
229
|
+
if (seen.has(key)) return;
|
|
230
|
+
seen.add(key);
|
|
231
|
+
candidates.push(model);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const currentModel = this.model;
|
|
235
|
+
for (const role of MODEL_ROLE_IDS) {
|
|
236
|
+
addCandidate(this.#resolveRoleModel(role, availableModels, currentModel));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const sortedByContext = [...availableModels].sort((a, b) => b.contextWindow - a.contextWindow);
|
|
240
|
+
for (const model of sortedByContext) {
|
|
241
|
+
addCandidate(model);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return candidates;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
resolveContextPromotionTarget(currentModel: Model, availableModels: Model[]): Model | undefined {
|
|
248
|
+
return this.#resolveContextPromotionConfiguredTarget(currentModel, availableModels);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
closeCodexProviderSessionsForHistoryRewrite(): void {
|
|
252
|
+
const currentModel = this.model;
|
|
253
|
+
if (!currentModel || currentModel.api !== "openai-codex-responses") return;
|
|
254
|
+
this.#closeProviderSessionsForModelSwitch(currentModel, currentModel);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
get #sessionId(): string {
|
|
258
|
+
return this.#sessionManager.getSessionId();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// =========================================================================
|
|
262
|
+
// Private
|
|
263
|
+
// =========================================================================
|
|
264
|
+
|
|
265
|
+
async #getScopedModelsWithApiKey(): Promise<Array<{ model: Model; thinkingLevel: ThinkingLevel }>> {
|
|
266
|
+
const apiKeysByProvider = new Map<string, string | undefined>();
|
|
267
|
+
const result: Array<{ model: Model; thinkingLevel: ThinkingLevel }> = [];
|
|
268
|
+
|
|
269
|
+
for (const scoped of this.#scopedModels) {
|
|
270
|
+
const provider = scoped.model.provider;
|
|
271
|
+
let apiKey: string | undefined;
|
|
272
|
+
if (apiKeysByProvider.has(provider)) {
|
|
273
|
+
apiKey = apiKeysByProvider.get(provider);
|
|
274
|
+
} else {
|
|
275
|
+
apiKey = await this.#modelRegistry.getApiKeyForProvider(provider, this.#sessionId);
|
|
276
|
+
apiKeysByProvider.set(provider, apiKey);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (apiKey) {
|
|
280
|
+
result.push(scoped);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async #cycleScopedModel(direction: "forward" | "backward"): Promise<ModelCycleResult | undefined> {
|
|
288
|
+
const scopedModels = await this.#getScopedModelsWithApiKey();
|
|
289
|
+
if (scopedModels.length <= 1) return undefined;
|
|
290
|
+
|
|
291
|
+
const currentModel = this.model;
|
|
292
|
+
let currentIndex = scopedModels.findIndex(sm => modelsAreEqual(sm.model, currentModel));
|
|
293
|
+
|
|
294
|
+
if (currentIndex === -1) currentIndex = 0;
|
|
295
|
+
const len = scopedModels.length;
|
|
296
|
+
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
297
|
+
const next = scopedModels[nextIndex];
|
|
298
|
+
|
|
299
|
+
this.setModelDirect(next.model);
|
|
300
|
+
this.#sessionManager.appendModelChange(`${next.model.provider}/${next.model.id}`);
|
|
301
|
+
this.#settings.setModelRole("default", `${next.model.provider}/${next.model.id}`);
|
|
302
|
+
this.#settings.getStorage()?.recordModelUsage(`${next.model.provider}/${next.model.id}`);
|
|
303
|
+
|
|
304
|
+
this.setThinkingLevel(next.thinkingLevel);
|
|
305
|
+
|
|
306
|
+
return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async #cycleAvailableModel(direction: "forward" | "backward"): Promise<ModelCycleResult | undefined> {
|
|
310
|
+
const availableModels = this.#modelRegistry.getAvailable();
|
|
311
|
+
if (availableModels.length <= 1) return undefined;
|
|
312
|
+
|
|
313
|
+
const currentModel = this.model;
|
|
314
|
+
let currentIndex = availableModels.findIndex(m => modelsAreEqual(m, currentModel));
|
|
315
|
+
|
|
316
|
+
if (currentIndex === -1) currentIndex = 0;
|
|
317
|
+
const len = availableModels.length;
|
|
318
|
+
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
319
|
+
const nextModel = availableModels[nextIndex];
|
|
320
|
+
|
|
321
|
+
const apiKey = await this.#modelRegistry.getApiKey(nextModel, this.#sessionId);
|
|
322
|
+
if (!apiKey) {
|
|
323
|
+
throw new Error(`No API key for ${nextModel.provider}/${nextModel.id}`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.setModelDirect(nextModel);
|
|
327
|
+
this.#sessionManager.appendModelChange(`${nextModel.provider}/${nextModel.id}`);
|
|
328
|
+
this.#settings.setModelRole("default", `${nextModel.provider}/${nextModel.id}`);
|
|
329
|
+
this.#settings.getStorage()?.recordModelUsage(`${nextModel.provider}/${nextModel.id}`);
|
|
330
|
+
|
|
331
|
+
this.setThinkingLevel(this.thinkingLevel);
|
|
332
|
+
|
|
333
|
+
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
clampThinkingLevel(level: ThinkingLevel, availableLevels: ThinkingLevel[]): ThinkingLevel {
|
|
337
|
+
const ordered = THINKING_LEVELS_WITH_XHIGH;
|
|
338
|
+
const available = new Set(availableLevels);
|
|
339
|
+
const requestedIndex = ordered.indexOf(level);
|
|
340
|
+
if (requestedIndex === -1) {
|
|
341
|
+
return availableLevels[0] ?? "off";
|
|
342
|
+
}
|
|
343
|
+
for (let i = requestedIndex; i < ordered.length; i++) {
|
|
344
|
+
const candidate = ordered[i];
|
|
345
|
+
if (available.has(candidate)) return candidate;
|
|
346
|
+
}
|
|
347
|
+
for (let i = requestedIndex - 1; i >= 0; i--) {
|
|
348
|
+
const candidate = ordered[i];
|
|
349
|
+
if (available.has(candidate)) return candidate;
|
|
350
|
+
}
|
|
351
|
+
return availableLevels[0] ?? "off";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#closeProviderSessionsForModelSwitch(currentModel: Model, nextModel: Model): void {
|
|
355
|
+
if (currentModel.api !== "openai-codex-responses" && nextModel.api !== "openai-codex-responses") return;
|
|
356
|
+
|
|
357
|
+
const providerKey = "openai-codex-responses";
|
|
358
|
+
const state = this.#providerSessionState.get(providerKey);
|
|
359
|
+
if (!state) return;
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
state.close();
|
|
363
|
+
} catch (error) {
|
|
364
|
+
logger.warn("Failed to close provider session state during model switch", {
|
|
365
|
+
providerKey,
|
|
366
|
+
error: String(error),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
this.#providerSessionState.delete(providerKey);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
getModelKey(model: Model): string {
|
|
374
|
+
return `${model.provider}/${model.id}`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
#resolveContextPromotionConfiguredTarget(currentModel: Model, availableModels: Model[]): Model | undefined {
|
|
378
|
+
const configuredTarget = currentModel.contextPromotionTarget?.trim();
|
|
379
|
+
if (!configuredTarget) return undefined;
|
|
380
|
+
|
|
381
|
+
const parsed = parseModelString(configuredTarget);
|
|
382
|
+
if (parsed) {
|
|
383
|
+
const explicitModel = availableModels.find(m => m.provider === parsed.provider && m.id === parsed.id);
|
|
384
|
+
if (explicitModel) return explicitModel;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return availableModels.find(m => m.provider === currentModel.provider && m.id === configuredTarget);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
#resolveRoleModel(role: ModelRole, availableModels: Model[], currentModel: Model | undefined): Model | undefined {
|
|
391
|
+
const roleModelStr =
|
|
392
|
+
role === "default"
|
|
393
|
+
? (this.#settings.getModelRole("default") ??
|
|
394
|
+
(currentModel ? `${currentModel.provider}/${currentModel.id}` : undefined))
|
|
395
|
+
: this.#settings.getModelRole(role);
|
|
396
|
+
|
|
397
|
+
if (!roleModelStr) return undefined;
|
|
398
|
+
|
|
399
|
+
const parsed = parseModelString(roleModelStr);
|
|
400
|
+
if (parsed) {
|
|
401
|
+
return availableModels.find(m => m.provider === parsed.provider && m.id === parsed.id);
|
|
402
|
+
}
|
|
403
|
+
const roleLower = roleModelStr.toLowerCase();
|
|
404
|
+
return availableModels.find(m => m.id.toLowerCase() === roleLower);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure utility functions for retry logic.
|
|
3
|
+
* Stateless — no class dependencies, no side effects.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if an error message indicates a retryable API error.
|
|
8
|
+
* Matches: overloaded, rate limit, usage limit, 429, 5xx, connection errors.
|
|
9
|
+
*/
|
|
10
|
+
export function isRetryableErrorMessage(errorMessage: string): boolean {
|
|
11
|
+
return /overloaded|rate.?limit|usage.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|unable to connect|fetch failed|retry delay/i.test(
|
|
12
|
+
errorMessage,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if an error message indicates a usage/billing limit (non-transient).
|
|
18
|
+
*/
|
|
19
|
+
export function isUsageLimitErrorMessage(errorMessage: string): boolean {
|
|
20
|
+
return /usage.?limit|usage_limit_reached|limit_reached/i.test(errorMessage);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse retry-after delay from an error message.
|
|
25
|
+
* Supports: retry-after-ms, retry-after (seconds or HTTP-date), x-ratelimit-reset-ms, x-ratelimit-reset.
|
|
26
|
+
* Returns delay in milliseconds, or undefined if no header found.
|
|
27
|
+
*/
|
|
28
|
+
export function parseRetryAfterMs(errorMessage: string): number | undefined {
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
const retryAfterMsMatch = /retry-after-ms\s*[:=]\s*(\d+)/i.exec(errorMessage);
|
|
31
|
+
if (retryAfterMsMatch) {
|
|
32
|
+
return Math.max(0, Number(retryAfterMsMatch[1]));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const retryAfterMatch = /retry-after\s*[:=]\s*([^\s,;]+)/i.exec(errorMessage);
|
|
36
|
+
if (retryAfterMatch) {
|
|
37
|
+
const value = retryAfterMatch[1];
|
|
38
|
+
const seconds = Number(value);
|
|
39
|
+
if (!Number.isNaN(seconds)) {
|
|
40
|
+
return Math.max(0, seconds * 1000);
|
|
41
|
+
}
|
|
42
|
+
const dateMs = Date.parse(value);
|
|
43
|
+
if (!Number.isNaN(dateMs)) {
|
|
44
|
+
return Math.max(0, dateMs - now);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resetMsMatch = /x-ratelimit-reset-ms\s*[:=]\s*(\d+)/i.exec(errorMessage);
|
|
49
|
+
if (resetMsMatch) {
|
|
50
|
+
const resetMs = Number(resetMsMatch[1]);
|
|
51
|
+
if (!Number.isNaN(resetMs)) {
|
|
52
|
+
if (resetMs > 1_000_000_000_000) {
|
|
53
|
+
return Math.max(0, resetMs - now);
|
|
54
|
+
}
|
|
55
|
+
return Math.max(0, resetMs);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const resetMatch = /x-ratelimit-reset\s*[:=]\s*(\d+)/i.exec(errorMessage);
|
|
60
|
+
if (resetMatch) {
|
|
61
|
+
const resetSeconds = Number(resetMatch[1]);
|
|
62
|
+
if (!Number.isNaN(resetSeconds)) {
|
|
63
|
+
if (resetSeconds > 1_000_000_000) {
|
|
64
|
+
return Math.max(0, resetSeconds * 1000 - now);
|
|
65
|
+
}
|
|
66
|
+
return Math.max(0, resetSeconds * 1000);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
@@ -20,193 +20,29 @@ import {
|
|
|
20
20
|
import type { SessionStorage, SessionStorageWriter } from "./session-storage";
|
|
21
21
|
import { FileSessionStorage, MemorySessionStorage } from "./session-storage";
|
|
22
22
|
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
export interface SessionHeader {
|
|
26
|
-
type: "session";
|
|
27
|
-
version?: number; // v1 sessions don't have this
|
|
28
|
-
id: string;
|
|
29
|
-
title?: string; // Auto-generated title from first message
|
|
30
|
-
timestamp: string;
|
|
31
|
-
cwd: string;
|
|
32
|
-
parentSession?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface NewSessionOptions {
|
|
36
|
-
parentSession?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface SessionEntryBase {
|
|
40
|
-
type: string;
|
|
41
|
-
id: string;
|
|
42
|
-
parentId: string | null;
|
|
43
|
-
timestamp: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface SessionMessageEntry extends SessionEntryBase {
|
|
47
|
-
type: "message";
|
|
48
|
-
message: AgentMessage;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface ThinkingLevelChangeEntry extends SessionEntryBase {
|
|
52
|
-
type: "thinking_level_change";
|
|
53
|
-
thinkingLevel: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface ModelChangeEntry extends SessionEntryBase {
|
|
57
|
-
type: "model_change";
|
|
58
|
-
/** Model in "provider/modelId" format */
|
|
59
|
-
model: string;
|
|
60
|
-
/** Role: "default", "fast", "oracle", etc. Undefined treated as "default" */
|
|
61
|
-
role?: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface CompactionEntry<T = unknown> extends SessionEntryBase {
|
|
65
|
-
type: "compaction";
|
|
66
|
-
summary: string;
|
|
67
|
-
shortSummary?: string;
|
|
68
|
-
firstKeptEntryId: string;
|
|
69
|
-
tokensBefore: number;
|
|
70
|
-
/** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
|
|
71
|
-
details?: T;
|
|
72
|
-
/** Hook-provided data to persist across compaction */
|
|
73
|
-
preserveData?: Record<string, unknown>;
|
|
74
|
-
/** True if generated by an extension, undefined/false if pi-generated (backward compatible) */
|
|
75
|
-
fromExtension?: boolean;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {
|
|
79
|
-
type: "branch_summary";
|
|
80
|
-
fromId: string;
|
|
81
|
-
summary: string;
|
|
82
|
-
/** Extension-specific data (not sent to LLM) */
|
|
83
|
-
details?: T;
|
|
84
|
-
/** True if generated by an extension, false if pi-generated */
|
|
85
|
-
fromExtension?: boolean;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Custom entry for extensions to store extension-specific data in the session.
|
|
90
|
-
* Use customType to identify your extension's entries.
|
|
91
|
-
*
|
|
92
|
-
* Purpose: Persist extension state across session reloads. On reload, extensions can
|
|
93
|
-
* scan entries for their customType and reconstruct internal state.
|
|
94
|
-
*
|
|
95
|
-
* Does NOT participate in LLM context (ignored by buildSessionContext).
|
|
96
|
-
* For injecting content into context, see CustomMessageEntry.
|
|
97
|
-
*/
|
|
98
|
-
export interface CustomEntry<T = unknown> extends SessionEntryBase {
|
|
99
|
-
type: "custom";
|
|
100
|
-
customType: string;
|
|
101
|
-
data?: T;
|
|
102
|
-
}
|
|
23
|
+
export * from "./session-types";
|
|
103
24
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
type
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
type
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
type
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
/** Tools available to the agent */
|
|
126
|
-
tools: string[];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Mode change entry - tracks agent mode transitions. */
|
|
130
|
-
export interface ModeChangeEntry extends SessionEntryBase {
|
|
131
|
-
type: "mode_change";
|
|
132
|
-
/** Current mode name, or "none" when exiting a mode */
|
|
133
|
-
mode: string;
|
|
134
|
-
/** Optional mode-specific data (e.g. plan file path) */
|
|
135
|
-
data?: Record<string, unknown>;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Custom message entry for extensions to inject messages into LLM context.
|
|
140
|
-
* Use customType to identify your extension's entries.
|
|
141
|
-
*
|
|
142
|
-
* Unlike CustomEntry, this DOES participate in LLM context.
|
|
143
|
-
* The content is converted to a user message in buildSessionContext().
|
|
144
|
-
* Use details for extension-specific metadata (not sent to LLM).
|
|
145
|
-
*
|
|
146
|
-
* display controls TUI rendering:
|
|
147
|
-
* - false: hidden entirely
|
|
148
|
-
* - true: rendered with distinct styling (different from user messages)
|
|
149
|
-
*/
|
|
150
|
-
export interface CustomMessageEntry<T = unknown> extends SessionEntryBase {
|
|
151
|
-
type: "custom_message";
|
|
152
|
-
customType: string;
|
|
153
|
-
content: string | (TextContent | ImageContent)[];
|
|
154
|
-
details?: T;
|
|
155
|
-
display: boolean;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/** Session entry - has id/parentId for tree structure (returned by "read" methods in SessionManager) */
|
|
159
|
-
export type SessionEntry =
|
|
160
|
-
| SessionMessageEntry
|
|
161
|
-
| ThinkingLevelChangeEntry
|
|
162
|
-
| ModelChangeEntry
|
|
163
|
-
| CompactionEntry
|
|
164
|
-
| BranchSummaryEntry
|
|
165
|
-
| CustomEntry
|
|
166
|
-
| CustomMessageEntry
|
|
167
|
-
| LabelEntry
|
|
168
|
-
| TtsrInjectionEntry
|
|
169
|
-
| SessionInitEntry
|
|
170
|
-
| ModeChangeEntry;
|
|
171
|
-
|
|
172
|
-
/** Raw file entry (includes header) */
|
|
173
|
-
export type FileEntry = SessionHeader | SessionEntry;
|
|
174
|
-
|
|
175
|
-
/** Tree node for getTree() - defensive copy of session structure */
|
|
176
|
-
export interface SessionTreeNode {
|
|
177
|
-
entry: SessionEntry;
|
|
178
|
-
children: SessionTreeNode[];
|
|
179
|
-
/** Resolved label for this entry, if any */
|
|
180
|
-
label?: string;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export interface SessionContext {
|
|
184
|
-
messages: AgentMessage[];
|
|
185
|
-
thinkingLevel: string;
|
|
186
|
-
/** Model roles: { default: "provider/modelId", small: "provider/modelId", ... } */
|
|
187
|
-
models: Record<string, string>;
|
|
188
|
-
/** Names of TTSR rules that have been injected this session */
|
|
189
|
-
injectedTtsrRules: string[];
|
|
190
|
-
/** Active mode (e.g. "plan") or "none" if no special mode is active */
|
|
191
|
-
mode: string;
|
|
192
|
-
/** Mode-specific data from the last mode_change entry */
|
|
193
|
-
modeData?: Record<string, unknown>;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export interface SessionInfo {
|
|
197
|
-
path: string;
|
|
198
|
-
id: string;
|
|
199
|
-
/** Working directory where the session was started. Empty string for old sessions. */
|
|
200
|
-
cwd: string;
|
|
201
|
-
title?: string;
|
|
202
|
-
/** Path to the parent session (if this session was forked). */
|
|
203
|
-
parentSessionPath?: string;
|
|
204
|
-
created: Date;
|
|
205
|
-
modified: Date;
|
|
206
|
-
messageCount: number;
|
|
207
|
-
firstMessage: string;
|
|
208
|
-
allMessagesText: string;
|
|
209
|
-
}
|
|
25
|
+
import {
|
|
26
|
+
type BranchSummaryEntry,
|
|
27
|
+
type CompactionEntry,
|
|
28
|
+
CURRENT_SESSION_VERSION,
|
|
29
|
+
type CustomEntry,
|
|
30
|
+
type CustomMessageEntry,
|
|
31
|
+
type FileEntry,
|
|
32
|
+
type LabelEntry,
|
|
33
|
+
type ModeChangeEntry,
|
|
34
|
+
type ModelChangeEntry,
|
|
35
|
+
type NewSessionOptions,
|
|
36
|
+
type SessionContext,
|
|
37
|
+
type SessionEntry,
|
|
38
|
+
type SessionHeader,
|
|
39
|
+
type SessionInfo,
|
|
40
|
+
type SessionInitEntry,
|
|
41
|
+
type SessionMessageEntry,
|
|
42
|
+
type SessionTreeNode,
|
|
43
|
+
type ThinkingLevelChangeEntry,
|
|
44
|
+
type TtsrInjectionEntry,
|
|
45
|
+
} from "./session-types";
|
|
210
46
|
|
|
211
47
|
export type ReadonlySessionManager = Pick<
|
|
212
48
|
SessionManager,
|