@gajae-code/coding-agent 0.11.11 → 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 +49 -3
- package/dist/types/cli/read-cli.d.ts +1 -0
- 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-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 +29 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +22 -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/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +52 -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 +8 -0
- 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/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/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- 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-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +5 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +15 -1
- 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/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -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/session/agent-session.d.ts +44 -10
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +25 -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/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 +4 -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 +2 -0
- 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/read-cli.ts +2 -1
- package/src/cli.ts +1 -0
- 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/team.ts +1 -1
- 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 +23 -1
- package/src/config/settings.ts +246 -108
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +229 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +58 -25
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +25 -15
- package/src/eval/py/executor.ts +7 -2
- package/src/extensibility/extensions/types.ts +28 -4
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -82
- 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 -943
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +742 -85
- package/src/gjc-runtime/state-runtime.ts +2 -1
- 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 +8 -9
- 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 +43 -7
- package/src/modes/acp/acp-event-mapper.ts +14 -7
- 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 +61 -4
- package/src/modes/components/oauth-selector.ts +42 -12
- 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/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +19 -2
- package/src/modes/controllers/input-controller.ts +18 -3
- package/src/modes/controllers/selector-controller.ts +83 -37
- package/src/modes/interactive-mode.ts +62 -3
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +19 -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/system/system-prompt.md +0 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/broker/lifecycle.ts +25 -6
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +341 -38
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/host/control/dispatch.ts +5 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +23 -1
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/protocol/operation-inventory.generated.json +29 -0
- package/src/sdk/protocol/operation-registry.ts +7 -2
- package/src/sdk/session.ts +13 -1
- package/src/session/agent-session.ts +466 -50
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +764 -17
- 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/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 +182 -18
- package/src/tools/tool-result.ts +14 -1
- package/src/web/search/providers/codex.ts +1 -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
|
@@ -281,7 +281,24 @@ export class ExtensionUiController {
|
|
|
281
281
|
const disabled = [...(session.settings.get("disabledExtensions") ?? [])];
|
|
282
282
|
const on = input.on === true;
|
|
283
283
|
const next = on ? disabled.filter(value => value !== id) : [...new Set([...disabled, id])];
|
|
284
|
-
session.settings.
|
|
284
|
+
if (!session.settings.canWriteDurableConfig()) {
|
|
285
|
+
throw Object.assign(
|
|
286
|
+
new Error(
|
|
287
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
288
|
+
),
|
|
289
|
+
{ code: "invalid_request" },
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
session.settings.set("disabledExtensions", next);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (!session.settings.canWriteDurableConfig()) {
|
|
296
|
+
throw Object.assign(new Error(error instanceof Error ? error.message : String(error)), {
|
|
297
|
+
code: "invalid_request",
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
285
302
|
return { changed: true, enabled: on };
|
|
286
303
|
}
|
|
287
304
|
case "session.delete":
|
|
@@ -442,7 +459,7 @@ export class ExtensionUiController {
|
|
|
442
459
|
}
|
|
443
460
|
return false;
|
|
444
461
|
},
|
|
445
|
-
invokeSkill: (name, args) => this.ctx.session.invokeSkill(name, args),
|
|
462
|
+
invokeSkill: (name, args, options) => this.ctx.session.invokeSkill(name, args, options),
|
|
446
463
|
setPlanMode: on => this.ctx.session.setSdkPlanMode(on),
|
|
447
464
|
operateGoal: (op, objective) => this.ctx.session.operateGoal(op, objective),
|
|
448
465
|
getSkillState: () =>
|
|
@@ -2044,9 +2044,24 @@ export class InputController {
|
|
|
2044
2044
|
}
|
|
2045
2045
|
|
|
2046
2046
|
toggleThinkingBlockVisibility(): void {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2047
|
+
if (!settings.canWriteDurableConfig()) {
|
|
2048
|
+
this.ctx.showError(
|
|
2049
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
2050
|
+
);
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
const hideThinkingBlock = !this.ctx.hideThinkingBlock;
|
|
2054
|
+
try {
|
|
2055
|
+
settings.set("hideThinkingBlock", hideThinkingBlock);
|
|
2056
|
+
} catch (error) {
|
|
2057
|
+
if (!settings.canWriteDurableConfig()) {
|
|
2058
|
+
this.ctx.showError(error instanceof Error ? error.message : String(error));
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
throw error;
|
|
2062
|
+
}
|
|
2063
|
+
this.ctx.hideThinkingBlock = hideThinkingBlock;
|
|
2064
|
+
this.ctx.session.setThinkingVisibility(hideThinkingBlock ? "hidden" : "visible");
|
|
2050
2065
|
|
|
2051
2066
|
// Rebuild chat from session messages
|
|
2052
2067
|
// Detach the live streaming component before the disposing clear() so the
|
|
@@ -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> {
|
|
@@ -61,7 +61,7 @@ import { GajaePetWidget, type PetMode } from "./components/gajae-pet-widget";
|
|
|
61
61
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
62
62
|
import type { HookInputComponent } from "./components/hook-input";
|
|
63
63
|
import type { HookSelectorComponent } from "./components/hook-selector";
|
|
64
|
-
import { IrcSplitViewComponent } from "./components/irc-sidebar";
|
|
64
|
+
import { computeIrcSplitWidths, getIrcSidebarSemanticToken, IrcSplitViewComponent } from "./components/irc-sidebar";
|
|
65
65
|
import {
|
|
66
66
|
getPetUnavailableWarning,
|
|
67
67
|
isPetAvailable,
|
|
@@ -416,6 +416,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
416
416
|
#sttController: SttModeController | undefined;
|
|
417
417
|
#resizeHandler?: () => void;
|
|
418
418
|
#observerRegistry: SessionObserverRegistry;
|
|
419
|
+
#viewportOutputRevision = 0n;
|
|
420
|
+
#viewportOutputIdentity: string;
|
|
421
|
+
|
|
419
422
|
#transcriptRegistry = new TranscriptItemRegistry();
|
|
420
423
|
|
|
421
424
|
/** Direct controller capabilities for consumers that coordinate mode transitions. */
|
|
@@ -484,6 +487,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
484
487
|
this.mcpManager = mcpManager;
|
|
485
488
|
this.#eventBus = eventBus;
|
|
486
489
|
this.#keyDisplayContext = keyDisplayContext;
|
|
490
|
+
this.#viewportOutputIdentity = `session:${this.sessionManager.getSessionId()}`;
|
|
491
|
+
|
|
487
492
|
this.keybindings.setDisplayContext(this.#keyDisplayContext);
|
|
488
493
|
const thisMode = this;
|
|
489
494
|
this.#goalModeController = new GoalModeController({
|
|
@@ -738,6 +743,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
738
743
|
this.ui.addChild(this.hookWidgetContainerBelow);
|
|
739
744
|
this.ui.setBottomPinnedComponent(this.statusLine);
|
|
740
745
|
this.ui.setFocus(this.editor);
|
|
746
|
+
this.ui.setViewportOutputSource({
|
|
747
|
+
identity: this.#viewportOutputIdentity,
|
|
748
|
+
revision: this.#viewportOutputRevision,
|
|
749
|
+
});
|
|
750
|
+
|
|
741
751
|
this.petWidget?.dispose();
|
|
742
752
|
this.petWidget = this.#createPetWidget(this.editor);
|
|
743
753
|
const configuredPetMode = settings.get("pet.mode");
|
|
@@ -770,6 +780,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
770
780
|
|
|
771
781
|
this.#inputController.setupKeyHandlers();
|
|
772
782
|
this.#inputController.setupEditorSubmitHandler();
|
|
783
|
+
this.editor.onViewportPageScroll = direction => {
|
|
784
|
+
this.ui.scrollViewportPages(direction);
|
|
785
|
+
};
|
|
786
|
+
this.editor.onViewportFollowLive = () => {
|
|
787
|
+
this.ui.followLiveViewport();
|
|
788
|
+
};
|
|
773
789
|
|
|
774
790
|
// Wire observer registry to EventBus
|
|
775
791
|
if (this.#eventBus) {
|
|
@@ -891,6 +907,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
891
907
|
// Set up theme file watcher
|
|
892
908
|
onThemeChange(() => {
|
|
893
909
|
clearRenderCache();
|
|
910
|
+
this.#ircSplitView.invalidateTheme();
|
|
894
911
|
configureDefaultComposerChrome(this.editor);
|
|
895
912
|
this.editor.setPlaceholder(this.#getComposerPlaceholder());
|
|
896
913
|
this.ui.invalidate();
|
|
@@ -1132,6 +1149,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1132
1149
|
this.statusLine,
|
|
1133
1150
|
this.hookWidgetContainerAbove,
|
|
1134
1151
|
this.editorContainer,
|
|
1152
|
+
this.petFloorContainer,
|
|
1135
1153
|
this.hookWidgetContainerBelow,
|
|
1136
1154
|
].reduce((rows, component) => rows + component.render(width).length, 0);
|
|
1137
1155
|
|
|
@@ -1199,8 +1217,22 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1199
1217
|
this.ui.requestRender();
|
|
1200
1218
|
return false;
|
|
1201
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
|
+
}
|
|
1202
1235
|
apply(mode);
|
|
1203
|
-
settings.set("pet.mode", mode);
|
|
1204
1236
|
this.ui.requestRender();
|
|
1205
1237
|
return true;
|
|
1206
1238
|
}
|
|
@@ -1499,6 +1531,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1499
1531
|
|
|
1500
1532
|
this.#inputController.setupKeyHandlers();
|
|
1501
1533
|
this.#inputController.setupEditorSubmitHandler();
|
|
1534
|
+
this.editor.onViewportPageScroll = direction => {
|
|
1535
|
+
this.ui.scrollViewportPages(direction);
|
|
1536
|
+
};
|
|
1537
|
+
this.editor.onViewportFollowLive = () => {
|
|
1538
|
+
this.ui.followLiveViewport();
|
|
1539
|
+
};
|
|
1502
1540
|
|
|
1503
1541
|
void this.refreshSlashCommandState().catch(error => {
|
|
1504
1542
|
logger.warn("Failed to refresh slash command state for custom editor", { error: String(error) });
|
|
@@ -1649,6 +1687,21 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1649
1687
|
return this.#uiHelpers.isKnownSlashCommand(text);
|
|
1650
1688
|
}
|
|
1651
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
|
+
|
|
1652
1705
|
addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): Component[] {
|
|
1653
1706
|
return this.#uiHelpers.addMessageToChat(message, options);
|
|
1654
1707
|
}
|
|
@@ -2176,12 +2229,18 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2176
2229
|
}
|
|
2177
2230
|
|
|
2178
2231
|
resetIrcSidebarSession(): void {
|
|
2179
|
-
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) : "";
|
|
2180
2238
|
this.#eventController.resetIrcObservations();
|
|
2181
2239
|
this.#ircSidebarRequestedVisible = false;
|
|
2182
2240
|
this.#ircSplitView.setVisible(false);
|
|
2183
2241
|
this.#uiHelpers.resetIrcSidebarHint();
|
|
2184
2242
|
this.#syncIrcSidebarAvailabilityFromSettings();
|
|
2243
|
+
if (sidebarVisible && beforeToken !== afterToken) this.recordVisibleTranscriptMutation();
|
|
2185
2244
|
}
|
|
2186
2245
|
|
|
2187
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
|
}
|
|
@@ -132,7 +132,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
132
132
|
}
|
|
133
133
|
return false;
|
|
134
134
|
},
|
|
135
|
-
invokeSkill: (name, args) => session.invokeSkill(name, args),
|
|
135
|
+
invokeSkill: (name, args, options) => session.invokeSkill(name, args, options),
|
|
136
136
|
setPlanMode: on => session.setSdkPlanMode(on),
|
|
137
137
|
operateGoal: (op, objective) => session.operateGoal(op, objective),
|
|
138
138
|
getSkillState: () => session.skills.map(skill => ({ name: skill.name, description: skill.description })),
|
|
@@ -380,7 +380,24 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
380
380
|
const disabled = [...(session.settings.get("disabledExtensions") ?? [])];
|
|
381
381
|
const on = input.on === true;
|
|
382
382
|
const next = on ? disabled.filter(value => value !== id) : [...new Set([...disabled, id])];
|
|
383
|
-
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
|
+
}
|
|
384
401
|
return { changed: true, enabled: on };
|
|
385
402
|
}
|
|
386
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;
|