@code-yeongyu/senpi 2026.8.28 → 2026.8.29
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 +74 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +5 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +22 -12
- package/dist/core/credential-accounts.js.map +1 -1
- package/dist/core/credential-pool/classify.d.ts +6 -1
- package/dist/core/credential-pool/classify.d.ts.map +1 -1
- package/dist/core/credential-pool/classify.js +22 -5
- package/dist/core/credential-pool/classify.js.map +1 -1
- package/dist/core/credential-pool/failover.d.ts +8 -0
- package/dist/core/credential-pool/failover.d.ts.map +1 -1
- package/dist/core/credential-pool/failover.js +24 -6
- package/dist/core/credential-pool/failover.js.map +1 -1
- package/dist/core/credential-pool/rotation-stream.d.ts +13 -2
- package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
- package/dist/core/credential-pool/rotation-stream.js +97 -10
- package/dist/core/credential-pool/rotation-stream.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +7 -0
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
- package/dist/core/extensions/builtin/service-tier.js +13 -0
- package/dist/core/extensions/builtin/service-tier.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +4 -2
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/model-config-schema.d.ts +4 -4
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +74 -37
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/retry-fallback/chains.d.ts +23 -1
- package/dist/core/retry-fallback/chains.d.ts.map +1 -1
- package/dist/core/retry-fallback/chains.js +59 -3
- package/dist/core/retry-fallback/chains.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/retry-fallback/settings.d.ts.map +1 -1
- package/dist/core/retry-fallback/settings.js +9 -0
- package/dist/core/retry-fallback/settings.js.map +1 -1
- package/dist/core/retry-fallback/validate.js +1 -1
- package/dist/core/retry-fallback/validate.js.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +31 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/main.d.ts +7 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.js +2 -2
- package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +323 -33
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +276 -237
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +133 -24
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +14 -4
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +82 -13
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +85 -28
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +3 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +162 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +22 -0
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +147 -16
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +2 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +4 -0
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +7 -0
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +9 -0
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/docs/rpc.md +37 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +6 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -224,6 +224,19 @@ function isDeadTerminalError(error) {
|
|
|
224
224
|
}
|
|
225
225
|
return false;
|
|
226
226
|
}
|
|
227
|
+
function storageWriteCrashMessage(error) {
|
|
228
|
+
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
const code = error.code;
|
|
232
|
+
if (code === "EDQUOT") {
|
|
233
|
+
return "Disk quota exceeded (EDQUOT). Free space or quota on the filesystem, then retry.";
|
|
234
|
+
}
|
|
235
|
+
if (code === "ENOSPC") {
|
|
236
|
+
return "Disk full (ENOSPC). Free space on the filesystem, then retry.";
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
227
240
|
const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage. Disable this warning in /settings.";
|
|
228
241
|
function isAnthropicSubscriptionAuthKey(apiKey) {
|
|
229
242
|
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|
|
@@ -2987,7 +3000,7 @@ export class InteractiveMode {
|
|
|
2987
3000
|
this.showTreeSelector();
|
|
2988
3001
|
}
|
|
2989
3002
|
else {
|
|
2990
|
-
this.showUserMessageSelector();
|
|
3003
|
+
void this.showUserMessageSelector().catch((error) => this.showError(error instanceof Error ? error.message : String(error)));
|
|
2991
3004
|
}
|
|
2992
3005
|
this.lastEscapeTime = 0;
|
|
2993
3006
|
}
|
|
@@ -3001,7 +3014,9 @@ export class InteractiveMode {
|
|
|
3001
3014
|
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
3002
3015
|
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
3003
3016
|
this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ());
|
|
3004
|
-
this.defaultEditor.onAction("app.thinking.cycle", () =>
|
|
3017
|
+
this.defaultEditor.onAction("app.thinking.cycle", () => {
|
|
3018
|
+
void this.cycleThinkingLevel().catch((error) => this.showError(error instanceof Error ? error.message : String(error)));
|
|
3019
|
+
});
|
|
3005
3020
|
this.defaultEditor.onAction("app.model.cycleForward", () => this.cycleModel("forward"));
|
|
3006
3021
|
this.defaultEditor.onAction("app.model.cycleBackward", () => this.cycleModel("backward"));
|
|
3007
3022
|
// Global debug handler on TUI (works regardless of focus)
|
|
@@ -3023,7 +3038,7 @@ export class InteractiveMode {
|
|
|
3023
3038
|
this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
|
|
3024
3039
|
this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
|
|
3025
3040
|
this.defaultEditor.onAction("app.session.tree", () => this.showTreeSelector());
|
|
3026
|
-
this.defaultEditor.onAction("app.session.fork", () => this.showUserMessageSelector());
|
|
3041
|
+
this.defaultEditor.onAction("app.session.fork", () => void this.showUserMessageSelector().catch((error) => this.showError(error instanceof Error ? error.message : String(error))));
|
|
3027
3042
|
this.defaultEditor.onAction("app.session.resume", () => this.showSessionSelector());
|
|
3028
3043
|
this.defaultEditor.onChange = (text) => {
|
|
3029
3044
|
const wasBashMode = this.isBashMode;
|
|
@@ -3179,197 +3194,158 @@ export class InteractiveMode {
|
|
|
3179
3194
|
}
|
|
3180
3195
|
setupEditorSubmitHandler() {
|
|
3181
3196
|
this.defaultEditor.onSubmit = async (text) => {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
if (text === "/favorite-models") {
|
|
3201
|
-
this.editor.setText("");
|
|
3202
|
-
await this.showFavoriteModelsSelector();
|
|
3203
|
-
return;
|
|
3204
|
-
}
|
|
3205
|
-
if (text === "/scoped-models") {
|
|
3206
|
-
this.editor.setText("");
|
|
3207
|
-
this.showScopedModelsSelector();
|
|
3208
|
-
return;
|
|
3209
|
-
}
|
|
3210
|
-
if (text === "/model" || text.startsWith("/model ")) {
|
|
3211
|
-
const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
|
|
3212
|
-
this.editor.setText("");
|
|
3213
|
-
await this.handleModelCommand(searchTerm);
|
|
3214
|
-
return;
|
|
3215
|
-
}
|
|
3216
|
-
if (text === "/export" || text.startsWith("/export ")) {
|
|
3217
|
-
await this.handleExportCommand(text);
|
|
3218
|
-
this.editor.setText("");
|
|
3219
|
-
return;
|
|
3220
|
-
}
|
|
3221
|
-
if (text === "/import" || text.startsWith("/import ")) {
|
|
3222
|
-
await this.handleImportCommand(text);
|
|
3223
|
-
this.editor.setText("");
|
|
3224
|
-
return;
|
|
3225
|
-
}
|
|
3226
|
-
if (text === "/share") {
|
|
3227
|
-
await this.handleShareCommand();
|
|
3228
|
-
this.editor.setText("");
|
|
3229
|
-
return;
|
|
3230
|
-
}
|
|
3231
|
-
if (text === "/copy") {
|
|
3232
|
-
await this.handleCopyCommand();
|
|
3233
|
-
this.editor.setText("");
|
|
3234
|
-
return;
|
|
3235
|
-
}
|
|
3236
|
-
if (text === "/name" || text.startsWith("/name ")) {
|
|
3237
|
-
this.handleNameCommand(text);
|
|
3238
|
-
this.editor.setText("");
|
|
3239
|
-
return;
|
|
3240
|
-
}
|
|
3241
|
-
if (text === "/session") {
|
|
3242
|
-
this.handleSessionCommand();
|
|
3243
|
-
this.editor.setText("");
|
|
3244
|
-
return;
|
|
3245
|
-
}
|
|
3246
|
-
if (text === "/changelog") {
|
|
3247
|
-
this.handleChangelogCommand();
|
|
3248
|
-
this.editor.setText("");
|
|
3249
|
-
return;
|
|
3250
|
-
}
|
|
3251
|
-
if (text === "/keybindings") {
|
|
3252
|
-
this.editor.setText("");
|
|
3253
|
-
await this.handleKeybindingsCommand();
|
|
3254
|
-
return;
|
|
3255
|
-
}
|
|
3256
|
-
if (text === "/hotkeys") {
|
|
3257
|
-
this.handleHotkeysCommand();
|
|
3258
|
-
this.editor.setText("");
|
|
3259
|
-
return;
|
|
3260
|
-
}
|
|
3261
|
-
if (text === "/fork") {
|
|
3262
|
-
this.showUserMessageSelector();
|
|
3263
|
-
this.editor.setText("");
|
|
3264
|
-
return;
|
|
3265
|
-
}
|
|
3266
|
-
if (text === "/clone") {
|
|
3267
|
-
this.editor.setText("");
|
|
3268
|
-
await this.handleCloneCommand();
|
|
3269
|
-
return;
|
|
3270
|
-
}
|
|
3271
|
-
if (text === "/tree") {
|
|
3272
|
-
this.showTreeSelector();
|
|
3273
|
-
this.editor.setText("");
|
|
3274
|
-
return;
|
|
3275
|
-
}
|
|
3276
|
-
if (text === "/trust") {
|
|
3277
|
-
this.showTrustSelector();
|
|
3278
|
-
this.editor.setText("");
|
|
3279
|
-
return;
|
|
3280
|
-
}
|
|
3281
|
-
if (text === "/login" || text.startsWith("/login ")) {
|
|
3282
|
-
const providerRef = text.startsWith("/login ") ? text.slice(7).trim() : undefined;
|
|
3283
|
-
this.editor.setText("");
|
|
3284
|
-
await this.handleLoginCommand(providerRef);
|
|
3285
|
-
return;
|
|
3286
|
-
}
|
|
3287
|
-
if (text === "/logout") {
|
|
3288
|
-
this.showOAuthSelector("logout");
|
|
3289
|
-
this.editor.setText("");
|
|
3290
|
-
return;
|
|
3291
|
-
}
|
|
3292
|
-
if (text === "/new") {
|
|
3293
|
-
this.editor.setText("");
|
|
3294
|
-
await this.handleClearCommand();
|
|
3295
|
-
return;
|
|
3296
|
-
}
|
|
3297
|
-
if (text === "/compact" || text.startsWith("/compact ")) {
|
|
3298
|
-
const customInstructions = text.startsWith("/compact ") ? text.slice(9).trim() : undefined;
|
|
3299
|
-
this.editor.setText("");
|
|
3300
|
-
await this.handleCompactCommand(customInstructions);
|
|
3301
|
-
return;
|
|
3302
|
-
}
|
|
3303
|
-
if (text === "/reload") {
|
|
3304
|
-
this.editor.setText("");
|
|
3305
|
-
await this.handleReloadCommand();
|
|
3306
|
-
return;
|
|
3307
|
-
}
|
|
3308
|
-
if (text === "/debug") {
|
|
3309
|
-
this.handleDebugCommand();
|
|
3310
|
-
this.editor.setText("");
|
|
3311
|
-
return;
|
|
3312
|
-
}
|
|
3313
|
-
if (text === "/arminsayshi") {
|
|
3314
|
-
this.handleArminSaysHi();
|
|
3315
|
-
this.editor.setText("");
|
|
3316
|
-
return;
|
|
3317
|
-
}
|
|
3318
|
-
if (text === "/dementedelves") {
|
|
3319
|
-
this.handleDementedDelves();
|
|
3320
|
-
this.editor.setText("");
|
|
3321
|
-
return;
|
|
3322
|
-
}
|
|
3323
|
-
if (text === "/resume") {
|
|
3324
|
-
this.showSessionSelector();
|
|
3325
|
-
this.editor.setText("");
|
|
3326
|
-
return;
|
|
3327
|
-
}
|
|
3328
|
-
if (text === "/quit" || text === "/exit") {
|
|
3329
|
-
this.editor.setText("");
|
|
3330
|
-
await this.shutdown();
|
|
3331
|
-
return;
|
|
3332
|
-
}
|
|
3333
|
-
if (this.isExtensionCommand(text)) {
|
|
3334
|
-
this.editor.addToHistory?.(text);
|
|
3335
|
-
this.editor.setText("");
|
|
3336
|
-
const pendingEchoId = this.optimisticUserEchoes.begin(text);
|
|
3337
|
-
try {
|
|
3338
|
-
await this.session.prompt(text, this.optimisticUserEchoes.promptOptions(pendingEchoId));
|
|
3197
|
+
try {
|
|
3198
|
+
// Capture-then-clear BEFORE any branch: handleFollowUp's non-streaming
|
|
3199
|
+
// path pre-resolves images and hands off here, but slash / extension /
|
|
3200
|
+
// bash submissions return before the consuming branches below. Clearing
|
|
3201
|
+
// only after use would leak a stale array into a later ordinary
|
|
3202
|
+
// submission whose text never references it.
|
|
3203
|
+
const preResolvedImages = this.preResolvedSubmissionImages;
|
|
3204
|
+
this.preResolvedSubmissionImages = undefined;
|
|
3205
|
+
this.hideShortcutOverlay();
|
|
3206
|
+
this.lastEditorText = "";
|
|
3207
|
+
text = text.trim();
|
|
3208
|
+
if (!text)
|
|
3209
|
+
return;
|
|
3210
|
+
// Handle commands
|
|
3211
|
+
if (text === "/settings") {
|
|
3212
|
+
await this.showSettingsSelector();
|
|
3213
|
+
this.editor.setText("");
|
|
3214
|
+
return;
|
|
3339
3215
|
}
|
|
3340
|
-
|
|
3341
|
-
this.
|
|
3342
|
-
|
|
3216
|
+
if (text === "/favorite-models") {
|
|
3217
|
+
this.editor.setText("");
|
|
3218
|
+
await this.showFavoriteModelsSelector();
|
|
3219
|
+
return;
|
|
3343
3220
|
}
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
this.
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3221
|
+
if (text === "/scoped-models") {
|
|
3222
|
+
this.editor.setText("");
|
|
3223
|
+
this.showScopedModelsSelector();
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
if (text === "/model" || text.startsWith("/model ")) {
|
|
3227
|
+
const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
|
|
3228
|
+
this.editor.setText("");
|
|
3229
|
+
await this.handleModelCommand(searchTerm);
|
|
3230
|
+
return;
|
|
3231
|
+
}
|
|
3232
|
+
if (text === "/export" || text.startsWith("/export ")) {
|
|
3233
|
+
await this.handleExportCommand(text);
|
|
3234
|
+
this.editor.setText("");
|
|
3235
|
+
return;
|
|
3236
|
+
}
|
|
3237
|
+
if (text === "/import" || text.startsWith("/import ")) {
|
|
3238
|
+
await this.handleImportCommand(text);
|
|
3239
|
+
this.editor.setText("");
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
if (text === "/share") {
|
|
3243
|
+
await this.handleShareCommand();
|
|
3244
|
+
this.editor.setText("");
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3247
|
+
if (text === "/copy") {
|
|
3248
|
+
await this.handleCopyCommand();
|
|
3249
|
+
this.editor.setText("");
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
if (text === "/name" || text.startsWith("/name ")) {
|
|
3253
|
+
await this.handleNameCommand(text);
|
|
3254
|
+
this.editor.setText("");
|
|
3255
|
+
return;
|
|
3256
|
+
}
|
|
3257
|
+
if (text === "/session") {
|
|
3258
|
+
await this.handleSessionCommand();
|
|
3259
|
+
this.editor.setText("");
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3262
|
+
if (text === "/changelog") {
|
|
3263
|
+
this.handleChangelogCommand();
|
|
3264
|
+
this.editor.setText("");
|
|
3265
|
+
return;
|
|
3266
|
+
}
|
|
3267
|
+
if (text === "/keybindings") {
|
|
3268
|
+
this.editor.setText("");
|
|
3269
|
+
await this.handleKeybindingsCommand();
|
|
3270
|
+
return;
|
|
3271
|
+
}
|
|
3272
|
+
if (text === "/hotkeys") {
|
|
3273
|
+
this.handleHotkeysCommand();
|
|
3274
|
+
this.editor.setText("");
|
|
3275
|
+
return;
|
|
3276
|
+
}
|
|
3277
|
+
if (text === "/fork") {
|
|
3278
|
+
await this.showUserMessageSelector();
|
|
3279
|
+
this.editor.setText("");
|
|
3280
|
+
return;
|
|
3281
|
+
}
|
|
3282
|
+
if (text === "/clone") {
|
|
3283
|
+
this.editor.setText("");
|
|
3284
|
+
await this.handleCloneCommand();
|
|
3285
|
+
return;
|
|
3286
|
+
}
|
|
3287
|
+
if (text === "/tree") {
|
|
3288
|
+
this.showTreeSelector();
|
|
3289
|
+
this.editor.setText("");
|
|
3290
|
+
return;
|
|
3291
|
+
}
|
|
3292
|
+
if (text === "/trust") {
|
|
3293
|
+
this.showTrustSelector();
|
|
3294
|
+
this.editor.setText("");
|
|
3295
|
+
return;
|
|
3296
|
+
}
|
|
3297
|
+
if (text === "/login" || text.startsWith("/login ")) {
|
|
3298
|
+
const providerRef = text.startsWith("/login ") ? text.slice(7).trim() : undefined;
|
|
3299
|
+
this.editor.setText("");
|
|
3300
|
+
await this.handleLoginCommand(providerRef);
|
|
3301
|
+
return;
|
|
3302
|
+
}
|
|
3303
|
+
if (text === "/logout") {
|
|
3304
|
+
this.showOAuthSelector("logout");
|
|
3305
|
+
this.editor.setText("");
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3308
|
+
if (text === "/new") {
|
|
3309
|
+
this.editor.setText("");
|
|
3310
|
+
await this.handleClearCommand();
|
|
3311
|
+
return;
|
|
3312
|
+
}
|
|
3313
|
+
if (text === "/compact" || text.startsWith("/compact ")) {
|
|
3314
|
+
const customInstructions = text.startsWith("/compact ") ? text.slice(9).trim() : undefined;
|
|
3315
|
+
this.editor.setText("");
|
|
3316
|
+
await this.handleCompactCommand(customInstructions);
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
if (text === "/reload") {
|
|
3320
|
+
this.editor.setText("");
|
|
3321
|
+
await this.handleReloadCommand();
|
|
3322
|
+
return;
|
|
3323
|
+
}
|
|
3324
|
+
if (text === "/debug") {
|
|
3325
|
+
this.handleDebugCommand();
|
|
3326
|
+
this.editor.setText("");
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
if (text === "/arminsayshi") {
|
|
3330
|
+
this.handleArminSaysHi();
|
|
3331
|
+
this.editor.setText("");
|
|
3332
|
+
return;
|
|
3333
|
+
}
|
|
3334
|
+
if (text === "/dementedelves") {
|
|
3335
|
+
this.handleDementedDelves();
|
|
3336
|
+
this.editor.setText("");
|
|
3337
|
+
return;
|
|
3338
|
+
}
|
|
3339
|
+
if (text === "/resume") {
|
|
3340
|
+
this.showSessionSelector();
|
|
3341
|
+
this.editor.setText("");
|
|
3342
|
+
return;
|
|
3343
|
+
}
|
|
3344
|
+
if (text === "/quit" || text === "/exit") {
|
|
3345
|
+
this.editor.setText("");
|
|
3346
|
+
await this.shutdown();
|
|
3360
3347
|
return;
|
|
3361
3348
|
}
|
|
3362
|
-
}
|
|
3363
|
-
// Queue non-command input during compaction.
|
|
3364
|
-
// Extension commands short-circuit at the isExtensionCommand branch above and
|
|
3365
|
-
// dispatch immediately inside AgentSession.prompt(), so the only text that
|
|
3366
|
-
// reaches this compaction branch is non-command user input, which is queued
|
|
3367
|
-
// for delivery after compaction settles.
|
|
3368
|
-
//
|
|
3369
|
-
// Note: the isExtensionCommand re-check below is already unreachable today
|
|
3370
|
-
// (the branch above returns first) and stays harmless after the
|
|
3371
|
-
// immediate-dispatch hoist in prompt().
|
|
3372
|
-
if (this.session.isCompacting) {
|
|
3373
3349
|
if (this.isExtensionCommand(text)) {
|
|
3374
3350
|
this.editor.addToHistory?.(text);
|
|
3375
3351
|
this.editor.setText("");
|
|
@@ -3381,52 +3357,96 @@ export class InteractiveMode {
|
|
|
3381
3357
|
this.optimisticUserEchoes.reject(pendingEchoId);
|
|
3382
3358
|
throw error;
|
|
3383
3359
|
}
|
|
3360
|
+
return;
|
|
3384
3361
|
}
|
|
3385
|
-
|
|
3386
|
-
|
|
3362
|
+
// Handle bash command (! for normal, !! for excluded from context)
|
|
3363
|
+
if (text.startsWith("!")) {
|
|
3364
|
+
const isExcluded = text.startsWith("!!");
|
|
3365
|
+
const command = isExcluded ? text.slice(2).trim() : text.slice(1).trim();
|
|
3366
|
+
if (command) {
|
|
3367
|
+
if (this.session.isBashRunning) {
|
|
3368
|
+
this.showWarning("A bash command is already running. Press Esc to cancel it first.");
|
|
3369
|
+
this.editor.setText(text);
|
|
3370
|
+
return;
|
|
3371
|
+
}
|
|
3372
|
+
this.editor.addToHistory?.(text);
|
|
3373
|
+
await this.handleBashCommand(command, isExcluded);
|
|
3374
|
+
this.isBashMode = false;
|
|
3375
|
+
this.updateEditorBorderColor();
|
|
3376
|
+
return;
|
|
3377
|
+
}
|
|
3387
3378
|
}
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
//
|
|
3397
|
-
|
|
3379
|
+
// Queue non-command input during compaction.
|
|
3380
|
+
// Extension commands short-circuit at the isExtensionCommand branch above and
|
|
3381
|
+
// dispatch immediately inside AgentSession.prompt(), so the only text that
|
|
3382
|
+
// reaches this compaction branch is non-command user input, which is queued
|
|
3383
|
+
// for delivery after compaction settles.
|
|
3384
|
+
//
|
|
3385
|
+
// Note: the isExtensionCommand re-check below is already unreachable today
|
|
3386
|
+
// (the branch above returns first) and stays harmless after the
|
|
3387
|
+
// immediate-dispatch hoist in prompt().
|
|
3388
|
+
if (this.session.isCompacting) {
|
|
3389
|
+
if (this.isExtensionCommand(text)) {
|
|
3390
|
+
this.editor.addToHistory?.(text);
|
|
3391
|
+
this.editor.setText("");
|
|
3392
|
+
const pendingEchoId = this.optimisticUserEchoes.begin(text);
|
|
3393
|
+
try {
|
|
3394
|
+
await this.session.prompt(text, this.optimisticUserEchoes.promptOptions(pendingEchoId));
|
|
3395
|
+
}
|
|
3396
|
+
catch (error) {
|
|
3397
|
+
this.optimisticUserEchoes.reject(pendingEchoId);
|
|
3398
|
+
throw error;
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
else {
|
|
3402
|
+
this.queueCompactionSubmission(text, "steer");
|
|
3403
|
+
}
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3406
|
+
// If streaming, use prompt() with steer behavior.
|
|
3407
|
+
// Extension commands are dispatched immediately by AgentSession.prompt()
|
|
3408
|
+
// (short-circuited at the isExtensionCommand branch above); the steer
|
|
3409
|
+
// behavior here applies only to ordinary text, prompt template expansion,
|
|
3410
|
+
// and queueing.
|
|
3411
|
+
if (this.session.isStreaming) {
|
|
3412
|
+
// Resolve BEFORE setText(""): the editor's prune chain fires
|
|
3413
|
+
// onImageMarkersChanged([]) and destroys pendingImages.
|
|
3414
|
+
const images = preResolvedImages ?? this.takeSubmissionImages(text);
|
|
3415
|
+
this.editor.addToHistory?.(text);
|
|
3416
|
+
this.editor.setText("");
|
|
3417
|
+
const pendingEchoId = this.optimisticUserEchoes.begin(text);
|
|
3418
|
+
try {
|
|
3419
|
+
await this.session.prompt(text, {
|
|
3420
|
+
streamingBehavior: "steer",
|
|
3421
|
+
...(images.length > 0 ? { images } : {}),
|
|
3422
|
+
...this.optimisticUserEchoes.promptOptions(pendingEchoId),
|
|
3423
|
+
});
|
|
3424
|
+
}
|
|
3425
|
+
catch (error) {
|
|
3426
|
+
this.optimisticUserEchoes.reject(pendingEchoId);
|
|
3427
|
+
throw error;
|
|
3428
|
+
}
|
|
3429
|
+
this.updatePendingMessagesDisplay();
|
|
3430
|
+
this.ui.requestRender();
|
|
3431
|
+
return;
|
|
3432
|
+
}
|
|
3433
|
+
// Normal message submission
|
|
3434
|
+
// First, move any pending bash components to chat
|
|
3435
|
+
this.flushPendingBashComponents();
|
|
3398
3436
|
const images = preResolvedImages ?? this.takeSubmissionImages(text);
|
|
3399
|
-
this.editor.addToHistory?.(text);
|
|
3400
|
-
this.editor.setText("");
|
|
3401
3437
|
const pendingEchoId = this.optimisticUserEchoes.begin(text);
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
...(images.length > 0 ? { images } : {}),
|
|
3406
|
-
...this.optimisticUserEchoes.promptOptions(pendingEchoId),
|
|
3407
|
-
});
|
|
3438
|
+
const submission = images.length > 0 ? { text, images, pendingEchoId } : { text, pendingEchoId };
|
|
3439
|
+
if (this.onInputCallback) {
|
|
3440
|
+
this.onInputCallback(submission);
|
|
3408
3441
|
}
|
|
3409
|
-
|
|
3410
|
-
this.
|
|
3411
|
-
throw error;
|
|
3442
|
+
else {
|
|
3443
|
+
this.pendingUserInputs.push(submission);
|
|
3412
3444
|
}
|
|
3413
|
-
this.
|
|
3414
|
-
this.ui.requestRender();
|
|
3415
|
-
return;
|
|
3416
|
-
}
|
|
3417
|
-
// Normal message submission
|
|
3418
|
-
// First, move any pending bash components to chat
|
|
3419
|
-
this.flushPendingBashComponents();
|
|
3420
|
-
const images = preResolvedImages ?? this.takeSubmissionImages(text);
|
|
3421
|
-
const pendingEchoId = this.optimisticUserEchoes.begin(text);
|
|
3422
|
-
const submission = images.length > 0 ? { text, images, pendingEchoId } : { text, pendingEchoId };
|
|
3423
|
-
if (this.onInputCallback) {
|
|
3424
|
-
this.onInputCallback(submission);
|
|
3445
|
+
this.editor.addToHistory?.(text);
|
|
3425
3446
|
}
|
|
3426
|
-
|
|
3427
|
-
this.
|
|
3447
|
+
catch (error) {
|
|
3448
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
3428
3449
|
}
|
|
3429
|
-
this.editor.addToHistory?.(text);
|
|
3430
3450
|
};
|
|
3431
3451
|
}
|
|
3432
3452
|
subscribeToAgent() {
|
|
@@ -3738,15 +3758,24 @@ export class InteractiveMode {
|
|
|
3738
3758
|
// Keep the structural fallback for focused handler consumers while using
|
|
3739
3759
|
// the session-owned manager on the real TUI path.
|
|
3740
3760
|
const sessionManager = this.session.sessionManager ?? this.sessionManager;
|
|
3741
|
-
|
|
3761
|
+
let entries = sessionManager?.buildContextEntries?.() ?? [];
|
|
3742
3762
|
if (entries[0]?.type !== "compaction") {
|
|
3743
|
-
|
|
3763
|
+
try {
|
|
3764
|
+
sessionManager?.reloadFromDisk?.();
|
|
3765
|
+
entries = sessionManager?.buildContextEntries?.() ?? entries;
|
|
3766
|
+
}
|
|
3767
|
+
catch {
|
|
3768
|
+
// Retain existing entries on reload error
|
|
3769
|
+
}
|
|
3744
3770
|
}
|
|
3745
3771
|
this.chatContainer.clear();
|
|
3746
3772
|
const summaryMessage = createCompactionSummaryMessage(sanitizeTerminalLabel(event.result.summary), event.result.tokensBefore, new Date().toISOString(), event.result.details);
|
|
3747
3773
|
if (typeof this.renderSessionEntries === "function") {
|
|
3748
3774
|
// The latest compaction is prepended for model context; append it below at its chronological position.
|
|
3749
|
-
|
|
3775
|
+
const entriesToRender = entries[0]?.type === "compaction"
|
|
3776
|
+
? entries.slice(1)
|
|
3777
|
+
: entries.filter((e) => e.type !== "compaction");
|
|
3778
|
+
this.renderSessionEntries(entriesToRender);
|
|
3750
3779
|
this.addMessageToChat(summaryMessage);
|
|
3751
3780
|
if (event.result.usage) {
|
|
3752
3781
|
this.addCompactionCostNotice({
|
|
@@ -4441,6 +4470,12 @@ export class InteractiveMode {
|
|
|
4441
4470
|
};
|
|
4442
4471
|
}
|
|
4443
4472
|
rebuildChatFromMessages() {
|
|
4473
|
+
try {
|
|
4474
|
+
this.sessionManager?.reloadFromDisk?.();
|
|
4475
|
+
}
|
|
4476
|
+
catch {
|
|
4477
|
+
// Keep in-memory entries if file reload is unavailable
|
|
4478
|
+
}
|
|
4444
4479
|
this.chatContainer.clear();
|
|
4445
4480
|
this.renderSessionEntries(this.sessionManager.buildContextEntries());
|
|
4446
4481
|
}
|
|
@@ -4561,6 +4596,10 @@ export class InteractiveMode {
|
|
|
4561
4596
|
}
|
|
4562
4597
|
catch { }
|
|
4563
4598
|
restoreInteractiveStderr();
|
|
4599
|
+
const storageMessage = storageWriteCrashMessage(error);
|
|
4600
|
+
if (storageMessage !== undefined) {
|
|
4601
|
+
console.error(storageMessage);
|
|
4602
|
+
}
|
|
4564
4603
|
console.error(`${APP_NAME} exiting due to uncaughtException:`);
|
|
4565
4604
|
console.error(error);
|
|
4566
4605
|
process.exit(1);
|
|
@@ -6617,7 +6656,7 @@ export class InteractiveMode {
|
|
|
6617
6656
|
const outputPath = this.getPathCommandArgument(text, "/export");
|
|
6618
6657
|
try {
|
|
6619
6658
|
if (outputPath?.endsWith(".jsonl")) {
|
|
6620
|
-
const filePath = this.session.exportToJsonl(outputPath);
|
|
6659
|
+
const filePath = await this.session.exportToJsonl(outputPath);
|
|
6621
6660
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
6622
6661
|
}
|
|
6623
6662
|
else {
|
|
@@ -6806,7 +6845,7 @@ export class InteractiveMode {
|
|
|
6806
6845
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
6807
6846
|
}
|
|
6808
6847
|
}
|
|
6809
|
-
handleNameCommand(text) {
|
|
6848
|
+
async handleNameCommand(text) {
|
|
6810
6849
|
const name = text.replace(/^\/name\s*/, "").trim();
|
|
6811
6850
|
if (!name) {
|
|
6812
6851
|
const currentName = this.sessionManager.getSessionName();
|
|
@@ -6820,8 +6859,8 @@ export class InteractiveMode {
|
|
|
6820
6859
|
this.ui.requestRender();
|
|
6821
6860
|
return;
|
|
6822
6861
|
}
|
|
6823
|
-
this.session.setSessionName(name);
|
|
6824
|
-
const sessionName = this.
|
|
6862
|
+
await this.session.setSessionName(name);
|
|
6863
|
+
const sessionName = this.session.sessionName;
|
|
6825
6864
|
if (sessionName !== name) {
|
|
6826
6865
|
this.showWarning(`Session name was normalized from ${JSON.stringify(name)} to ${JSON.stringify(sessionName)}`);
|
|
6827
6866
|
}
|
|
@@ -6832,7 +6871,7 @@ export class InteractiveMode {
|
|
|
6832
6871
|
async handleSessionCommand() {
|
|
6833
6872
|
// Awaited at the boundary: the shared-host proxy answers this over RPC.
|
|
6834
6873
|
const stats = await this.session.getSessionStats();
|
|
6835
|
-
const sessionName = this.
|
|
6874
|
+
const sessionName = this.session.sessionName;
|
|
6836
6875
|
const entries = this.sessionManager.getEntries();
|
|
6837
6876
|
const cacheWaste = computeCacheWaste(entries, this.session.modelRuntime);
|
|
6838
6877
|
// Cost/token totals per provider/model actually used (e.g. OpenRouter `auto`
|
|
@@ -7114,7 +7153,7 @@ export class InteractiveMode {
|
|
|
7114
7153
|
}
|
|
7115
7154
|
this.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated ? { truncated: true, content: result.output } : undefined, result.fullOutputPath);
|
|
7116
7155
|
// Record the result in session
|
|
7117
|
-
this.session.recordBashResult(command, result, { excludeFromContext });
|
|
7156
|
+
void this.session.recordBashResult(command, result, { excludeFromContext });
|
|
7118
7157
|
this.bashComponent = undefined;
|
|
7119
7158
|
this.ui.requestRender();
|
|
7120
7159
|
return;
|