@gajae-code/coding-agent 0.11.10 → 0.12.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 +86 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-profile-contract.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +86 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -1
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/manager.d.ts +8 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +7 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +18 -2
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +23 -3
- package/dist/types/sdk/host/control/operations.d.ts +2 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/startup-capability.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +51 -19
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +27 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +59 -13
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/skill-state/active-state.d.ts +0 -4
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/types.d.ts +36 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/package.json +9 -13
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +8 -0
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +49 -2
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +91 -7
- package/src/commands/team.ts +1 -1
- package/src/config/model-profile-activation.ts +1 -0
- package/src/config/model-profile-contract.ts +13 -0
- package/src/config/model-profiles.ts +24 -0
- package/src/config/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +84 -3
- package/src/config/settings.ts +254 -108
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +229 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +78 -26
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +26 -16
- package/src/eval/py/executor.ts +7 -2
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -68
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -921
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +979 -71
- package/src/gjc-runtime/state-runtime.ts +50 -12
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +34 -5
- package/src/gjc-runtime/team-runtime.ts +970 -33
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +13 -7
- package/src/gjc-runtime/workflow-manifest.generated.json +97 -350
- package/src/gjc-runtime/workflow-manifest.ts +34 -179
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +10 -11
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +47 -0
- package/src/modes/acp/acp-agent.ts +597 -67
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +76 -5
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +1 -4
- package/src/modes/components/welcome.ts +18 -3
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +62 -6
- package/src/modes/controllers/selector-controller.ts +83 -37
- package/src/modes/interactive-mode.ts +127 -29
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/manager.ts +26 -6
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/lifecycle.ts +107 -7
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/config.ts +1 -1
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +503 -60
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -4
- package/src/sdk/bus/telegram-daemon.ts +531 -53
- package/src/sdk/host/control/dispatch.ts +7 -1
- package/src/sdk/host/control/operations.ts +2 -1
- package/src/sdk/host/host.ts +25 -3
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/protocol/operation-inventory.generated.json +76 -0
- package/src/sdk/protocol/operation-registry.ts +8 -2
- package/src/sdk/session.ts +25 -2
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +720 -190
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +40 -10
- package/src/session/internal/native-publish-outcome.ts +8 -3
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +1372 -42
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +0 -10
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +65 -7
- package/src/task/executor.ts +11 -3
- package/src/task/index.ts +2 -0
- package/src/task/types.ts +22 -11
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +89 -69
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1542 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/tool-result.ts +14 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
getPluginsCacheDir,
|
|
31
31
|
MarketplaceManager,
|
|
32
32
|
} from "../../extensibility/plugins/marketplace";
|
|
33
|
+
import { INTERACTIVE_SELECTOR_RESUME_ORIGIN } from "../../extensibility/shared-events";
|
|
33
34
|
import {
|
|
34
35
|
getAvailableThemes,
|
|
35
36
|
getCurrentThemeName,
|
|
@@ -165,6 +166,7 @@ import type { JobsObserver } from "../jobs-observer";
|
|
|
165
166
|
import type { SessionObserverRegistry } from "../session-observer-registry";
|
|
166
167
|
import type { TasksAggregator } from "../tasks-aggregator";
|
|
167
168
|
import type { TranscriptItemRegistry } from "../transcript-item-registry";
|
|
169
|
+
import { acquireResumeProgressLease } from "../utils/ui-helpers";
|
|
168
170
|
|
|
169
171
|
const CALLBACK_SERVER_PROVIDERS = new Set<string>([
|
|
170
172
|
"anthropic",
|
|
@@ -1151,6 +1153,12 @@ export class SelectorController {
|
|
|
1151
1153
|
);
|
|
1152
1154
|
if (scope === undefined) return;
|
|
1153
1155
|
const persistDefault = scope.trim().toLowerCase() === "default";
|
|
1156
|
+
if (persistDefault && !this.ctx.settings.canWriteDurableConfig()) {
|
|
1157
|
+
this.ctx.showError(
|
|
1158
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
1159
|
+
);
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1154
1162
|
|
|
1155
1163
|
const imageProvider = normalized as
|
|
1156
1164
|
| "auto"
|
|
@@ -1224,13 +1232,18 @@ export class SelectorController {
|
|
|
1224
1232
|
const selector = new ThinkingSelectorComponent(
|
|
1225
1233
|
this.ctx.session.thinkingLevel,
|
|
1226
1234
|
availableLevels,
|
|
1227
|
-
selection => {
|
|
1228
|
-
done();
|
|
1229
|
-
|
|
1235
|
+
async selection => {
|
|
1230
1236
|
const { level, persistDefault } = selection;
|
|
1231
1237
|
const configuredDefault = this.ctx.settings.get("defaultThinkingLevel");
|
|
1232
1238
|
const levelToApply = level === ThinkingLevel.Inherit ? configuredDefault : level;
|
|
1233
|
-
|
|
1239
|
+
try {
|
|
1240
|
+
await this.ctx.session.setThinkingLevelForControl(level, persistDefault);
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
this.ctx.showError(error instanceof Error ? error.message : String(error));
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
done();
|
|
1246
|
+
|
|
1234
1247
|
const effectiveLevel = this.ctx.session.thinkingLevel ?? ThinkingLevel.Off;
|
|
1235
1248
|
const requestedLabel =
|
|
1236
1249
|
level === ThinkingLevel.Inherit ? `${level} (configured default: ${configuredDefault})` : level;
|
|
@@ -1270,6 +1283,7 @@ export class SelectorController {
|
|
|
1270
1283
|
},
|
|
1271
1284
|
{
|
|
1272
1285
|
onChange: (id, value) => this.handleSettingChange(id, value),
|
|
1286
|
+
onError: message => this.ctx.showError(message),
|
|
1273
1287
|
onThemePreview: themeName => {
|
|
1274
1288
|
return previewTheme(themeName).then(result => {
|
|
1275
1289
|
if (!result.success && result.error && !isThemePreviewSuperseded(result)) {
|
|
@@ -1289,6 +1303,7 @@ export class SelectorController {
|
|
|
1289
1303
|
onPetPreview: mode => {
|
|
1290
1304
|
this.ctx.previewPetMode(mode as PetMode);
|
|
1291
1305
|
},
|
|
1306
|
+
onPetCommit: mode => this.ctx.commitPetPreviewMode(mode as PetMode),
|
|
1292
1307
|
onStatusLinePreview: previewSettings => {
|
|
1293
1308
|
// Update status line with preview settings
|
|
1294
1309
|
this.ctx.statusLine.updateSettings({
|
|
@@ -1332,24 +1347,40 @@ export class SelectorController {
|
|
|
1332
1347
|
getAvailableThemes().then(availableThemes => {
|
|
1333
1348
|
const initialTheme = getCurrentThemeName() ?? "red-claw";
|
|
1334
1349
|
this.showSelector(done => {
|
|
1350
|
+
const restoreAndClose = () => {
|
|
1351
|
+
void restoreThemePreview(initialTheme).then(result => {
|
|
1352
|
+
if (!result.success && result.error) {
|
|
1353
|
+
this.ctx.showError(`Failed to restore theme preview: ${result.error}`);
|
|
1354
|
+
}
|
|
1355
|
+
this.#refreshThemeUi();
|
|
1356
|
+
});
|
|
1357
|
+
done();
|
|
1358
|
+
};
|
|
1335
1359
|
const selector = new ThemeSelectorComponent(
|
|
1336
1360
|
initialTheme,
|
|
1337
1361
|
availableThemes,
|
|
1338
1362
|
themeName => {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1363
|
+
if (!settings.canWriteDurableConfig()) {
|
|
1364
|
+
this.ctx.showError(
|
|
1365
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
1366
|
+
);
|
|
1367
|
+
restoreAndClose();
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
try {
|
|
1371
|
+
settings.set(getDetectedThemeSettingsPath(), themeName);
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
if (!settings.canWriteDurableConfig()) {
|
|
1374
|
+
this.ctx.showError(error instanceof Error ? error.message : String(error));
|
|
1375
|
+
restoreAndClose();
|
|
1376
|
+
return;
|
|
1348
1377
|
}
|
|
1349
|
-
|
|
1350
|
-
}
|
|
1378
|
+
throw error;
|
|
1379
|
+
}
|
|
1380
|
+
this.#refreshThemeUi();
|
|
1351
1381
|
done();
|
|
1352
1382
|
},
|
|
1383
|
+
restoreAndClose,
|
|
1353
1384
|
themeName => {
|
|
1354
1385
|
void previewTheme(themeName).then(result => {
|
|
1355
1386
|
if (!result.success && result.error) {
|
|
@@ -1373,8 +1404,9 @@ export class SelectorController {
|
|
|
1373
1404
|
const selector = new PetSelectorComponent(
|
|
1374
1405
|
initial,
|
|
1375
1406
|
mode => {
|
|
1376
|
-
this.ctx.setPetMode(mode)
|
|
1377
|
-
|
|
1407
|
+
if (this.ctx.setPetMode(mode)) {
|
|
1408
|
+
done();
|
|
1409
|
+
}
|
|
1378
1410
|
},
|
|
1379
1411
|
() => {
|
|
1380
1412
|
this.ctx.previewPetMode(initial);
|
|
@@ -2262,28 +2294,42 @@ export class SelectorController {
|
|
|
2262
2294
|
async handleResumeSession(sessionPath: string): Promise<void> {
|
|
2263
2295
|
const previousSessionId = this.ctx.sessionManager.getSessionId();
|
|
2264
2296
|
this.#clearTransientSessionUi();
|
|
2265
|
-
const
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
sessionPath
|
|
2273
|
-
|
|
2274
|
-
|
|
2297
|
+
const progressLease = acquireResumeProgressLease(this.ctx);
|
|
2298
|
+
try {
|
|
2299
|
+
await progressLease.committed;
|
|
2300
|
+
const migrationPolicy =
|
|
2301
|
+
this.ctx.settings?.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
|
|
2302
|
+
let writableSessionPath = sessionPath;
|
|
2303
|
+
if (this.ctx.sessionManager.isManagedDestination()) {
|
|
2304
|
+
const inspection = await SessionManager.inspectSessionTailReadOnly(sessionPath);
|
|
2305
|
+
if (inspection.kind === "error")
|
|
2306
|
+
throw new Error(`Could not inspect selected session: ${inspection.reason}`);
|
|
2307
|
+
writableSessionPath = await this.ctx.sessionManager.prepareManagedCandidateForStrictAdoption(
|
|
2308
|
+
sessionPath,
|
|
2309
|
+
migrationPolicy,
|
|
2310
|
+
inspection.identity,
|
|
2311
|
+
);
|
|
2312
|
+
}
|
|
2313
|
+
// Switch session via AgentSession (emits hook and tool session events)
|
|
2314
|
+
if (
|
|
2315
|
+
!(await this.ctx.session.switchSession(writableSessionPath, {
|
|
2316
|
+
transition: { origin: INTERACTIVE_SELECTOR_RESUME_ORIGIN },
|
|
2317
|
+
}))
|
|
2318
|
+
)
|
|
2319
|
+
return;
|
|
2320
|
+
const switchingToDifferentSession = previousSessionId !== this.ctx.sessionManager.getSessionId();
|
|
2321
|
+
if (switchingToDifferentSession) this.ctx.resetIrcSidebarSession();
|
|
2322
|
+
this.#refreshSessionTerminalTitle();
|
|
2323
|
+
this.ctx.updateEditorBorderColor();
|
|
2324
|
+
|
|
2325
|
+
this.ctx.rebuildInitialMessages(
|
|
2326
|
+
switchingToDifferentSession ? "replace-identity" : "reconcile-same-transcript",
|
|
2275
2327
|
);
|
|
2328
|
+
await this.ctx.reloadTodos();
|
|
2329
|
+
this.ctx.showStatus("Resumed session");
|
|
2330
|
+
} finally {
|
|
2331
|
+
progressLease.clear();
|
|
2276
2332
|
}
|
|
2277
|
-
// Switch session via AgentSession (emits hook and tool session events)
|
|
2278
|
-
if (!(await this.ctx.session.switchSession(writableSessionPath))) return;
|
|
2279
|
-
const switchingToDifferentSession = previousSessionId !== this.ctx.sessionManager.getSessionId();
|
|
2280
|
-
if (switchingToDifferentSession) this.ctx.resetIrcSidebarSession();
|
|
2281
|
-
this.#refreshSessionTerminalTitle();
|
|
2282
|
-
this.ctx.updateEditorBorderColor();
|
|
2283
|
-
|
|
2284
|
-
this.ctx.rebuildInitialMessages(switchingToDifferentSession ? "replace-identity" : "reconcile-same-transcript");
|
|
2285
|
-
await this.ctx.reloadTodos();
|
|
2286
|
-
this.ctx.showStatus("Resumed session");
|
|
2287
2333
|
}
|
|
2288
2334
|
|
|
2289
2335
|
async handleSessionDeleteCommand(): Promise<void> {
|
|
@@ -18,6 +18,7 @@ import chalk from "chalk";
|
|
|
18
18
|
import { AsyncJobManager } from "../async";
|
|
19
19
|
import {
|
|
20
20
|
type AppKeybinding,
|
|
21
|
+
defaultMessageQueueKeysForPlatform,
|
|
21
22
|
formatKeyHint,
|
|
22
23
|
formatKeyHints,
|
|
23
24
|
KeybindingsManager,
|
|
@@ -47,6 +48,7 @@ import type { SessionContext, SessionManager } from "../session/session-manager"
|
|
|
47
48
|
import { getRecentSessions, getSessionMessageEntryId } from "../session/session-manager";
|
|
48
49
|
import type { LspStartupServerInfo } from "../tools";
|
|
49
50
|
import { formatPhaseDisplayName } from "../tools/todo-write";
|
|
51
|
+
import { copyToClipboard } from "../utils/clipboard";
|
|
50
52
|
import type { EventBus } from "../utils/event-bus";
|
|
51
53
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
|
|
52
54
|
import { popTerminalTitle, pushTerminalTitle, setSessionTerminalTitle } from "../utils/title-generator";
|
|
@@ -59,7 +61,7 @@ import { GajaePetWidget, type PetMode } from "./components/gajae-pet-widget";
|
|
|
59
61
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
60
62
|
import type { HookInputComponent } from "./components/hook-input";
|
|
61
63
|
import type { HookSelectorComponent } from "./components/hook-selector";
|
|
62
|
-
import { IrcSplitViewComponent } from "./components/irc-sidebar";
|
|
64
|
+
import { computeIrcSplitWidths, getIrcSidebarSemanticToken, IrcSplitViewComponent } from "./components/irc-sidebar";
|
|
63
65
|
import {
|
|
64
66
|
getPetUnavailableWarning,
|
|
65
67
|
isPetAvailable,
|
|
@@ -113,16 +115,60 @@ import {
|
|
|
113
115
|
import type { ParsedIrcMessage } from "./utils/irc-message";
|
|
114
116
|
import { addChatChild, prepareTranscriptRebuild, UiHelpers } from "./utils/ui-helpers";
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
function buildComposerPlaceholder(
|
|
119
|
+
keybindings: Pick<KeybindingsManager, "getDisplayString">,
|
|
120
|
+
context: KeyDisplayContext,
|
|
121
|
+
options: { readonly busy: boolean; readonly busyPromptMode: "steer" | "queue" },
|
|
122
|
+
): string {
|
|
123
|
+
const parts: string[] = [];
|
|
124
|
+
const submitKey = options.busy ? keybindings.getDisplayString("tui.input.submit", context) : "";
|
|
125
|
+
if (submitKey) {
|
|
126
|
+
const submitAction = options.busyPromptMode === "steer" ? "Steer" : "Queue";
|
|
127
|
+
parts.push(`${submitKey}: ${submitAction}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const queueKey = keybindings.getDisplayString("app.message.queue", context);
|
|
131
|
+
const submitQueues = options.busy && options.busyPromptMode === "queue" && submitKey;
|
|
132
|
+
if (queueKey && !submitQueues) parts.push(`${queueKey}: ${options.busy ? "Queue" : "Queue (busy)"}`);
|
|
133
|
+
|
|
134
|
+
const actionHints = [
|
|
135
|
+
["app.thinking.cycle", "Thinking"],
|
|
136
|
+
["app.model.select", "Model"],
|
|
137
|
+
["app.history.search", "History"],
|
|
138
|
+
] as const;
|
|
139
|
+
for (const [id, label] of actionHints) {
|
|
140
|
+
const key = keybindings.getDisplayString(id, context);
|
|
141
|
+
if (key) parts.push(`${key}: ${label}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
117
144
|
const newlineKeys = context.platform === "win32" ? ["alt+enter", "ctrl+j"] : ["shift+enter", "ctrl+j"];
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
145
|
+
parts.push(`${formatKeyHints(newlineKeys, context)}: New line`, `${formatKeyHint("ctrl+c", context)}: Clear`);
|
|
146
|
+
return `Type your message... ${parts.join(" · ")}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function getDefaultComposerPlaceholder(
|
|
150
|
+
context: KeyDisplayContext = { platform: process.platform },
|
|
151
|
+
keybindings?: Pick<KeybindingsManager, "getDisplayString">,
|
|
152
|
+
): string {
|
|
153
|
+
const effectiveKeybindings =
|
|
154
|
+
keybindings ??
|
|
155
|
+
KeybindingsManager.inMemory({
|
|
156
|
+
"app.message.queue": defaultMessageQueueKeysForPlatform(context.platform),
|
|
157
|
+
});
|
|
158
|
+
return buildComposerPlaceholder(effectiveKeybindings, context, { busy: false, busyPromptMode: "steer" });
|
|
122
159
|
}
|
|
123
160
|
|
|
124
161
|
export const DEFAULT_COMPOSER_PLACEHOLDER = getDefaultComposerPlaceholder();
|
|
162
|
+
|
|
163
|
+
export function getComposerPlaceholder(
|
|
164
|
+
keybindings: Pick<KeybindingsManager, "getDisplayString">,
|
|
165
|
+
context: KeyDisplayContext,
|
|
166
|
+
options: { readonly busy: boolean; readonly busyPromptMode: "steer" | "queue" },
|
|
167
|
+
): string {
|
|
168
|
+
return buildComposerPlaceholder(keybindings, context, options);
|
|
169
|
+
}
|
|
125
170
|
const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;
|
|
171
|
+
const COMPOSER_RIGHT_GUTTER_WIDTH = 1;
|
|
126
172
|
|
|
127
173
|
const IRC_SIDEBAR_TOGGLE_SHADOWING_ACTIONS: readonly AppKeybinding[] = [
|
|
128
174
|
"app.plan.toggle",
|
|
@@ -169,7 +215,7 @@ function configureDefaultComposerChrome(editor: CustomEditor): void {
|
|
|
169
215
|
editor.setInputPrefix(getDefaultInputPrefix());
|
|
170
216
|
editor.setPlaceholder(getDefaultComposerPlaceholder());
|
|
171
217
|
editor.setPaddingX(1);
|
|
172
|
-
editor.setRightGutterWidth(
|
|
218
|
+
editor.setRightGutterWidth(COMPOSER_RIGHT_GUTTER_WIDTH);
|
|
173
219
|
editor.setTopBorder(undefined);
|
|
174
220
|
}
|
|
175
221
|
|
|
@@ -370,6 +416,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
370
416
|
#sttController: SttModeController | undefined;
|
|
371
417
|
#resizeHandler?: () => void;
|
|
372
418
|
#observerRegistry: SessionObserverRegistry;
|
|
419
|
+
#viewportOutputRevision = 0n;
|
|
420
|
+
#viewportOutputIdentity: string;
|
|
421
|
+
|
|
373
422
|
#transcriptRegistry = new TranscriptItemRegistry();
|
|
374
423
|
|
|
375
424
|
/** Direct controller capabilities for consumers that coordinate mode transitions. */
|
|
@@ -438,6 +487,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
438
487
|
this.mcpManager = mcpManager;
|
|
439
488
|
this.#eventBus = eventBus;
|
|
440
489
|
this.#keyDisplayContext = keyDisplayContext;
|
|
490
|
+
this.#viewportOutputIdentity = `session:${this.sessionManager.getSessionId()}`;
|
|
491
|
+
|
|
441
492
|
this.keybindings.setDisplayContext(this.#keyDisplayContext);
|
|
442
493
|
const thisMode = this;
|
|
443
494
|
this.#goalModeController = new GoalModeController({
|
|
@@ -510,6 +561,14 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
510
561
|
|
|
511
562
|
this.ui = new TUI(new ProcessTerminal(), settings.get("showHardwareCursor"), {
|
|
512
563
|
enableMouse: settings.get("mouse.enabled"),
|
|
564
|
+
copySelection: async text => {
|
|
565
|
+
try {
|
|
566
|
+
await copyToClipboard(text);
|
|
567
|
+
this.showStatus("Selection copied to clipboard");
|
|
568
|
+
} catch (error) {
|
|
569
|
+
this.showError(`Failed to copy selection: ${error instanceof Error ? error.message : String(error)}`);
|
|
570
|
+
}
|
|
571
|
+
},
|
|
513
572
|
});
|
|
514
573
|
this.ui.setClearOnShrink(settings.get("clearOnShrink"));
|
|
515
574
|
this.chatContainer = new Container();
|
|
@@ -660,6 +719,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
660
719
|
getViewportRows: () => this.ui.terminal.rows,
|
|
661
720
|
getReservedBottomRows: getWelcomeReservedBottomRows,
|
|
662
721
|
changelogMarkdown: this.#changelogMarkdown,
|
|
722
|
+
rightGutterWidth: COMPOSER_RIGHT_GUTTER_WIDTH,
|
|
663
723
|
collapseChangelog: settings.get("collapseChangelog"),
|
|
664
724
|
keyDisplayContext: this.#keyDisplayContext,
|
|
665
725
|
},
|
|
@@ -683,6 +743,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
683
743
|
this.ui.addChild(this.hookWidgetContainerBelow);
|
|
684
744
|
this.ui.setBottomPinnedComponent(this.statusLine);
|
|
685
745
|
this.ui.setFocus(this.editor);
|
|
746
|
+
this.ui.setViewportOutputSource({
|
|
747
|
+
identity: this.#viewportOutputIdentity,
|
|
748
|
+
revision: this.#viewportOutputRevision,
|
|
749
|
+
});
|
|
750
|
+
|
|
686
751
|
this.petWidget?.dispose();
|
|
687
752
|
this.petWidget = this.#createPetWidget(this.editor);
|
|
688
753
|
const configuredPetMode = settings.get("pet.mode");
|
|
@@ -715,6 +780,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
715
780
|
|
|
716
781
|
this.#inputController.setupKeyHandlers();
|
|
717
782
|
this.#inputController.setupEditorSubmitHandler();
|
|
783
|
+
this.editor.onViewportPageScroll = direction => {
|
|
784
|
+
this.ui.scrollViewportPages(direction);
|
|
785
|
+
};
|
|
786
|
+
this.editor.onViewportFollowLive = () => {
|
|
787
|
+
this.ui.followLiveViewport();
|
|
788
|
+
};
|
|
718
789
|
|
|
719
790
|
// Wire observer registry to EventBus
|
|
720
791
|
if (this.#eventBus) {
|
|
@@ -836,6 +907,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
836
907
|
// Set up theme file watcher
|
|
837
908
|
onThemeChange(() => {
|
|
838
909
|
clearRenderCache();
|
|
910
|
+
this.#ircSplitView.invalidateTheme();
|
|
839
911
|
configureDefaultComposerChrome(this.editor);
|
|
840
912
|
this.editor.setPlaceholder(this.#getComposerPlaceholder());
|
|
841
913
|
this.ui.invalidate();
|
|
@@ -1056,27 +1128,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1056
1128
|
return this.session.isStreaming || this.session.isCompacting;
|
|
1057
1129
|
}
|
|
1058
1130
|
|
|
1059
|
-
#getFirstKeyForAction(action: AppKeybinding): string | undefined {
|
|
1060
|
-
return this.keybindings.getKeys(action)[0];
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
#getMessageQueueShortcut(): string | undefined {
|
|
1064
|
-
const preferredAction: AppKeybinding =
|
|
1065
|
-
process.platform === "darwin" ? "app.message.followUp" : "app.message.queue";
|
|
1066
|
-
const fallbackAction: AppKeybinding =
|
|
1067
|
-
process.platform === "darwin" ? "app.message.queue" : "app.message.followUp";
|
|
1068
|
-
return this.#getFirstKeyForAction(preferredAction) ?? this.#getFirstKeyForAction(fallbackAction);
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
1131
|
#getComposerPlaceholder(): string {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
const parts = submitKey ? [`${submitKey}: ${submitAction}`] : [];
|
|
1077
|
-
const queueKey = this.#getMessageQueueShortcut();
|
|
1078
|
-
if (queueKey) parts.push(`${formatKeyHint(queueKey, this.#keyDisplayContext)}: Queue`);
|
|
1079
|
-
return parts.length > 0 ? `${defaultPlaceholder} · ${parts.join(" · ")}` : defaultPlaceholder;
|
|
1132
|
+
return getComposerPlaceholder(this.keybindings, this.#keyDisplayContext, {
|
|
1133
|
+
busy: this.#isPromptDeliveryBusy(),
|
|
1134
|
+
busyPromptMode: this.settings.get("busyPromptMode"),
|
|
1135
|
+
});
|
|
1080
1136
|
}
|
|
1081
1137
|
|
|
1082
1138
|
#getWelcomeReservedRows(width: number): number {
|
|
@@ -1093,6 +1149,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1093
1149
|
this.statusLine,
|
|
1094
1150
|
this.hookWidgetContainerAbove,
|
|
1095
1151
|
this.editorContainer,
|
|
1152
|
+
this.petFloorContainer,
|
|
1096
1153
|
this.hookWidgetContainerBelow,
|
|
1097
1154
|
].reduce((rows, component) => rows + component.render(width).length, 0);
|
|
1098
1155
|
|
|
@@ -1160,8 +1217,22 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1160
1217
|
this.ui.requestRender();
|
|
1161
1218
|
return false;
|
|
1162
1219
|
}
|
|
1220
|
+
if (!settings.canWriteDurableConfig()) {
|
|
1221
|
+
this.showError(
|
|
1222
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
1223
|
+
);
|
|
1224
|
+
return false;
|
|
1225
|
+
}
|
|
1226
|
+
try {
|
|
1227
|
+
settings.set("pet.mode", mode);
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
if (!settings.canWriteDurableConfig()) {
|
|
1230
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
throw error;
|
|
1234
|
+
}
|
|
1163
1235
|
apply(mode);
|
|
1164
|
-
settings.set("pet.mode", mode);
|
|
1165
1236
|
this.ui.requestRender();
|
|
1166
1237
|
return true;
|
|
1167
1238
|
}
|
|
@@ -1460,6 +1531,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1460
1531
|
|
|
1461
1532
|
this.#inputController.setupKeyHandlers();
|
|
1462
1533
|
this.#inputController.setupEditorSubmitHandler();
|
|
1534
|
+
this.editor.onViewportPageScroll = direction => {
|
|
1535
|
+
this.ui.scrollViewportPages(direction);
|
|
1536
|
+
};
|
|
1537
|
+
this.editor.onViewportFollowLive = () => {
|
|
1538
|
+
this.ui.followLiveViewport();
|
|
1539
|
+
};
|
|
1463
1540
|
|
|
1464
1541
|
void this.refreshSlashCommandState().catch(error => {
|
|
1465
1542
|
logger.warn("Failed to refresh slash command state for custom editor", { error: String(error) });
|
|
@@ -1610,6 +1687,21 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1610
1687
|
return this.#uiHelpers.isKnownSlashCommand(text);
|
|
1611
1688
|
}
|
|
1612
1689
|
|
|
1690
|
+
/** Advances the sticky-viewport source only for semantic transcript output. */
|
|
1691
|
+
recordVisibleTranscriptMutation(): void {
|
|
1692
|
+
const identity = `session:${this.sessionManager.getSessionId()}`;
|
|
1693
|
+
if (identity !== this.#viewportOutputIdentity) {
|
|
1694
|
+
this.#viewportOutputIdentity = identity;
|
|
1695
|
+
this.#viewportOutputRevision = 0n;
|
|
1696
|
+
} else {
|
|
1697
|
+
this.#viewportOutputRevision += 1n;
|
|
1698
|
+
}
|
|
1699
|
+
this.ui.setViewportOutputSource({
|
|
1700
|
+
identity: this.#viewportOutputIdentity,
|
|
1701
|
+
revision: this.#viewportOutputRevision,
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1613
1705
|
addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): Component[] {
|
|
1614
1706
|
return this.#uiHelpers.addMessageToChat(message, options);
|
|
1615
1707
|
}
|
|
@@ -2137,12 +2229,18 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2137
2229
|
}
|
|
2138
2230
|
|
|
2139
2231
|
resetIrcSidebarSession(): void {
|
|
2140
|
-
this.
|
|
2232
|
+
const sidebarVisible = this.#ircSplitView.effectiveSidebarVisible(this.ui.terminal.columns);
|
|
2233
|
+
const rightWidth = computeIrcSplitWidths(this.ui.terminal.columns).rightWidth;
|
|
2234
|
+
const beforeToken = sidebarVisible ? getIrcSidebarSemanticToken(this.ircLedger, rightWidth) : "";
|
|
2235
|
+
this.ircLedger.reset({ retireCurrentSessionIdentities: true });
|
|
2236
|
+
this.#ircSplitView.resetSource();
|
|
2237
|
+
const afterToken = sidebarVisible ? getIrcSidebarSemanticToken(this.ircLedger, rightWidth) : "";
|
|
2141
2238
|
this.#eventController.resetIrcObservations();
|
|
2142
2239
|
this.#ircSidebarRequestedVisible = false;
|
|
2143
2240
|
this.#ircSplitView.setVisible(false);
|
|
2144
2241
|
this.#uiHelpers.resetIrcSidebarHint();
|
|
2145
2242
|
this.#syncIrcSidebarAvailabilityFromSettings();
|
|
2243
|
+
if (sidebarVisible && beforeToken !== afterToken) this.recordVisibleTranscriptMutation();
|
|
2146
2244
|
}
|
|
2147
2245
|
|
|
2148
2246
|
#invalidateIrcSidebarRender(): void {
|
|
@@ -4,9 +4,26 @@ import type { ParsedIrcMessage } from "./utils/irc-message";
|
|
|
4
4
|
|
|
5
5
|
type InlineMode = "persistent" | "ephemeral";
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
export const IRC_OBSERVATION_LEDGER_MAX_RECORDS = 10_000;
|
|
8
|
+
export const IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES = 16 * 1024 * 1024;
|
|
9
|
+
export const IRC_OBSERVATION_LEDGER_MAX_SEEN_IDENTITIES = 100_000;
|
|
10
|
+
|
|
11
|
+
export const __ircLedgerPerfCounters = {
|
|
12
|
+
enabled: false,
|
|
13
|
+
epochAdvances: 0,
|
|
14
|
+
snapshot() {
|
|
15
|
+
return { epochAdvances: this.epochAdvances };
|
|
16
|
+
},
|
|
17
|
+
enable(): void {
|
|
18
|
+
this.enabled = true;
|
|
19
|
+
},
|
|
20
|
+
disable(): void {
|
|
21
|
+
this.enabled = false;
|
|
22
|
+
},
|
|
23
|
+
reset(): void {
|
|
24
|
+
this.epochAdvances = 0;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
10
27
|
|
|
11
28
|
export type IrcObservationRecord = Readonly<
|
|
12
29
|
ParsedIrcMessage & {
|
|
@@ -22,7 +39,7 @@ function measureRetainedUtf8Bytes(message: ParsedIrcMessage): number {
|
|
|
22
39
|
let bytes = 4;
|
|
23
40
|
for (const value of [message.observationId, message.from, message.to, message.text, message.kind]) {
|
|
24
41
|
bytes += Buffer.byteLength(value, "utf8");
|
|
25
|
-
if (bytes >
|
|
42
|
+
if (bytes > IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES) return bytes;
|
|
26
43
|
}
|
|
27
44
|
return bytes;
|
|
28
45
|
}
|
|
@@ -43,12 +60,28 @@ export class IrcObservationLedger {
|
|
|
43
60
|
#nextSequence = 0;
|
|
44
61
|
#seenObservationIdentities = new Set<string>();
|
|
45
62
|
#identityCapacityExhausted = false;
|
|
63
|
+
#retiredSessionIdentities = new Set<string>();
|
|
46
64
|
#evictedObservationIds = new Set<string>();
|
|
65
|
+
#mutationEpoch = 0;
|
|
66
|
+
|
|
67
|
+
get mutationEpoch(): number {
|
|
68
|
+
return this.#mutationEpoch;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#advanceMutationEpoch(): void {
|
|
72
|
+
this.#mutationEpoch++;
|
|
73
|
+
if (__ircLedgerPerfCounters.enabled) __ircLedgerPerfCounters.epochAdvances++;
|
|
74
|
+
}
|
|
47
75
|
|
|
48
76
|
#rememberObservationIdentity(observationId: string): boolean {
|
|
49
77
|
const identity = tombstoneIdentity(observationId);
|
|
50
|
-
if (
|
|
51
|
-
|
|
78
|
+
if (
|
|
79
|
+
this.#seenObservationIdentities.has(identity) ||
|
|
80
|
+
this.#retiredSessionIdentities.has(identity) ||
|
|
81
|
+
this.#identityCapacityExhausted
|
|
82
|
+
)
|
|
83
|
+
return false;
|
|
84
|
+
if (this.#seenObservationIdentities.size >= IRC_OBSERVATION_LEDGER_MAX_SEEN_IDENTITIES) {
|
|
52
85
|
this.#identityCapacityExhausted = true;
|
|
53
86
|
return false;
|
|
54
87
|
}
|
|
@@ -56,6 +89,18 @@ export class IrcObservationLedger {
|
|
|
56
89
|
return true;
|
|
57
90
|
}
|
|
58
91
|
|
|
92
|
+
#retireCurrentSessionIdentities(): void {
|
|
93
|
+
for (const identity of this.#seenObservationIdentities) {
|
|
94
|
+
this.#retiredSessionIdentities.delete(identity);
|
|
95
|
+
this.#retiredSessionIdentities.add(identity);
|
|
96
|
+
while (this.#retiredSessionIdentities.size > IRC_OBSERVATION_LEDGER_MAX_SEEN_IDENTITIES) {
|
|
97
|
+
const oldest = this.#retiredSessionIdentities.values().next().value;
|
|
98
|
+
if (oldest === undefined) break;
|
|
99
|
+
this.#retiredSessionIdentities.delete(oldest);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
59
104
|
#evict(observationId: string): void {
|
|
60
105
|
const record = this.#records.get(observationId);
|
|
61
106
|
if (!record) return;
|
|
@@ -65,7 +110,10 @@ export class IrcObservationLedger {
|
|
|
65
110
|
}
|
|
66
111
|
|
|
67
112
|
#enforceBounds(): void {
|
|
68
|
-
while (
|
|
113
|
+
while (
|
|
114
|
+
this.#records.size > IRC_OBSERVATION_LEDGER_MAX_RECORDS ||
|
|
115
|
+
this.#retainedUtf8Bytes > IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES
|
|
116
|
+
) {
|
|
69
117
|
const oldestObservationId = this.#records.keys().next().value;
|
|
70
118
|
if (oldestObservationId === undefined) return;
|
|
71
119
|
this.#evict(oldestObservationId);
|
|
@@ -76,10 +124,9 @@ export class IrcObservationLedger {
|
|
|
76
124
|
observe(message: ParsedIrcMessage, panelVisibleAtObservation: boolean): IrcObservationRecord | undefined {
|
|
77
125
|
const existing = this.#records.get(message.observationId);
|
|
78
126
|
if (existing) return existing;
|
|
79
|
-
if (!this.#rememberObservationIdentity(message.observationId)) return undefined;
|
|
80
|
-
|
|
81
127
|
const estimatedBytes = measureRetainedUtf8Bytes(message);
|
|
82
|
-
if (estimatedBytes >
|
|
128
|
+
if (estimatedBytes > IRC_OBSERVATION_LEDGER_MAX_RETAINED_UTF8_BYTES) return undefined;
|
|
129
|
+
if (!this.#rememberObservationIdentity(message.observationId)) return undefined;
|
|
83
130
|
|
|
84
131
|
const observedAt = Date.now();
|
|
85
132
|
const mode: InlineMode = panelVisibleAtObservation ? "ephemeral" : "persistent";
|
|
@@ -94,6 +141,7 @@ export class IrcObservationLedger {
|
|
|
94
141
|
this.#records.set(record.observationId, record);
|
|
95
142
|
this.#retainedUtf8Bytes += estimatedBytes;
|
|
96
143
|
this.#enforceBounds();
|
|
144
|
+
this.#advanceMutationEpoch();
|
|
97
145
|
return this.#records.get(record.observationId);
|
|
98
146
|
}
|
|
99
147
|
|
|
@@ -116,12 +164,20 @@ export class IrcObservationLedger {
|
|
|
116
164
|
return observationIds;
|
|
117
165
|
}
|
|
118
166
|
|
|
119
|
-
reset(): void {
|
|
120
|
-
|
|
121
|
-
|
|
167
|
+
reset(options?: { retireCurrentSessionIdentities?: boolean }): void {
|
|
168
|
+
const retainedRecordsChanged = this.#records.size > 0;
|
|
169
|
+
if (options?.retireCurrentSessionIdentities) this.#retireCurrentSessionIdentities();
|
|
170
|
+
else this.#retiredSessionIdentities.clear();
|
|
171
|
+
if (retainedRecordsChanged) {
|
|
172
|
+
for (const observationId of this.#records.keys()) {
|
|
173
|
+
this.#evictedObservationIds.add(observationId);
|
|
174
|
+
}
|
|
175
|
+
this.#records.clear();
|
|
176
|
+
this.#retainedUtf8Bytes = 0;
|
|
122
177
|
}
|
|
123
|
-
this.#records.clear();
|
|
124
|
-
this.#retainedUtf8Bytes = 0;
|
|
125
178
|
this.#nextSequence = 0;
|
|
179
|
+
this.#seenObservationIdentities.clear();
|
|
180
|
+
this.#identityCapacityExhausted = false;
|
|
181
|
+
if (retainedRecordsChanged) this.#advanceMutationEpoch();
|
|
126
182
|
}
|
|
127
183
|
}
|
|
@@ -115,6 +115,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
115
115
|
compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
|
|
116
116
|
clearContext: () => session.clearContext(),
|
|
117
117
|
cycleModel: () => session.cycleModel(),
|
|
118
|
+
setModelProfile: name => session.activateModelProfileForControl(name),
|
|
118
119
|
cycleThinkingLevel: () => session.cycleThinkingLevel(),
|
|
119
120
|
setQueueMode: (kind, mode) => {
|
|
120
121
|
if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
|
|
@@ -131,7 +132,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
131
132
|
}
|
|
132
133
|
return false;
|
|
133
134
|
},
|
|
134
|
-
invokeSkill: (name, args) => session.invokeSkill(name, args),
|
|
135
|
+
invokeSkill: (name, args, options) => session.invokeSkill(name, args, options),
|
|
135
136
|
setPlanMode: on => session.setSdkPlanMode(on),
|
|
136
137
|
operateGoal: (op, objective) => session.operateGoal(op, objective),
|
|
137
138
|
getSkillState: () => session.skills.map(skill => ({ name: skill.name, description: skill.description })),
|
|
@@ -379,7 +380,24 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
379
380
|
const disabled = [...(session.settings.get("disabledExtensions") ?? [])];
|
|
380
381
|
const on = input.on === true;
|
|
381
382
|
const next = on ? disabled.filter(value => value !== id) : [...new Set([...disabled, id])];
|
|
382
|
-
session.settings.
|
|
383
|
+
if (!session.settings.canWriteDurableConfig()) {
|
|
384
|
+
throw Object.assign(
|
|
385
|
+
new Error(
|
|
386
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
387
|
+
),
|
|
388
|
+
{ code: "invalid_request" },
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
session.settings.set("disabledExtensions", next);
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (!session.settings.canWriteDurableConfig()) {
|
|
395
|
+
throw Object.assign(new Error(error instanceof Error ? error.message : String(error)), {
|
|
396
|
+
code: "invalid_request",
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
383
401
|
return { changed: true, enabled: on };
|
|
384
402
|
}
|
|
385
403
|
case "session.delete":
|
package/src/modes/types.ts
CHANGED
|
@@ -248,6 +248,8 @@ export interface InteractiveModeContext {
|
|
|
248
248
|
getAssistantViewportAnchorId?(message: AssistantMessage): string;
|
|
249
249
|
findLastAssistantMessage(): AssistantMessage | undefined;
|
|
250
250
|
extractAssistantText(message: AssistantMessage): string;
|
|
251
|
+
/** Records one semantic visible-transcript mutation for the sticky viewport. */
|
|
252
|
+
recordVisibleTranscriptMutation?(): void;
|
|
251
253
|
updateEditorTopBorder(): void;
|
|
252
254
|
updateEditorBorderColor(): void;
|
|
253
255
|
rebuildChatFromMessages(policy: TranscriptRebuildPolicy): void;
|