@kenkaiiii/ggcoder 5.81.0 → 5.83.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/README.md +1 -1
- package/dist/app-sidecar/app-settings.js +11 -5
- package/dist/app-sidecar/app-settings.js.map +1 -1
- package/dist/app-sidecar/auth-routes.js +8 -3
- package/dist/app-sidecar/auth-routes.js.map +1 -1
- package/dist/app-sidecar/goal-routes.js +7 -0
- package/dist/app-sidecar/goal-routes.js.map +1 -1
- package/dist/app-sidecar/run-control-routes.js +31 -3
- package/dist/app-sidecar/run-control-routes.js.map +1 -1
- package/dist/app-sidecar.js +65 -11
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli/auth.js +22 -1
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/core/agent-notifications.d.ts +18 -0
- package/dist/core/agent-notifications.js +35 -2
- package/dist/core/agent-notifications.js.map +1 -1
- package/dist/core/agent-session.d.ts +71 -1
- package/dist/core/agent-session.js +175 -16
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/policy.d.ts +2 -2
- package/dist/core/compaction/policy.js +86 -43
- package/dist/core/compaction/policy.js.map +1 -1
- package/dist/core/compaction/token-estimator.d.ts +4 -1
- package/dist/core/compaction/token-estimator.js +5 -2
- package/dist/core/compaction/token-estimator.js.map +1 -1
- package/dist/core/idle-wake.d.ts +62 -0
- package/dist/core/idle-wake.js +103 -0
- package/dist/core/idle-wake.js.map +1 -0
- package/dist/core/process-manager.d.ts +51 -11
- package/dist/core/process-manager.js +229 -54
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts +5 -1
- package/dist/core/slash-commands.js +24 -9
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/steering.d.ts +7 -0
- package/dist/core/steering.js +13 -0
- package/dist/core/steering.js.map +1 -1
- package/dist/core/subagent-manager.js +5 -4
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-policy.js +4 -1
- package/dist/core/subagent-policy.js.map +1 -1
- package/dist/goals-agent/goals-agent.js +3 -2
- package/dist/goals-agent/goals-agent.js.map +1 -1
- package/dist/interactive.js +9 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/acp-mode.js +8 -1
- package/dist/modes/acp-mode.js.map +1 -1
- package/dist/modes/agent-home-mode.js +13 -1
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/rpc-mode.d.ts +2 -1
- package/dist/modes/rpc-mode.js +15 -3
- package/dist/modes/rpc-mode.js.map +1 -1
- package/dist/modes/serve-mode.js +13 -1
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/tools/background-watch-lint.d.ts +18 -0
- package/dist/tools/background-watch-lint.js +102 -0
- package/dist/tools/background-watch-lint.js.map +1 -0
- package/dist/tools/bash.js +5 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/subagent-shared.js +4 -1
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/task-output.js +11 -1
- package/dist/tools/task-output.js.map +1 -1
- package/dist/ui/components/Footer.js +4 -0
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/ModelSelector.d.ts +10 -0
- package/dist/ui/components/ModelSelector.js +51 -5
- package/dist/ui/components/ModelSelector.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agentLoop, isAbortError, isUsageLimitError, repairToolPairingAdjacent, } from "@kenkaiiii/gg-agent";
|
|
2
|
-
import { ProviderError, stream, } from "@kenkaiiii/gg-ai";
|
|
2
|
+
import { ProviderError, resolveToolSchema, stream, } from "@kenkaiiii/gg-ai";
|
|
3
3
|
import { EventBus } from "./event-bus.js";
|
|
4
4
|
import { flagUntrustedToolResult } from "./injection-detect.js";
|
|
5
5
|
import { COMPLETION_REVIEW_STATE_KIND, } from "./completion-review.js";
|
|
@@ -53,7 +53,7 @@ import { buildLoopBreakMessage } from "./loop-breaker.js";
|
|
|
53
53
|
import { buildRegroundingMessage, requestTextForRegrounding } from "./regrounding.js";
|
|
54
54
|
import { buildSemanticLoopJudgePrompt, buildSemanticLoopMessage, parseSemanticLoopVerdict, SEMANTIC_LOOP_JUDGE_TIMEOUT_MS, withJudgeTimeout, } from "./semantic-loop-check.js";
|
|
55
55
|
import { buildEnvDeltaMessage, buildLanguageUpdateMessage } from "./env-delta.js";
|
|
56
|
-
import { wrapSteeringText, buildNotificationSteeringText, STEERING_PREFIX } from "./steering.js";
|
|
56
|
+
import { wrapSteeringText, buildNotificationSteeringText, buildIdleWakeNotificationText, STEERING_PREFIX, } from "./steering.js";
|
|
57
57
|
import { AgentNotificationQueue } from "./agent-notifications.js";
|
|
58
58
|
import { VerificationGate, VERIFICATION_STATE_KIND } from "./verification-gate.js";
|
|
59
59
|
import { findUserSessionPrompt, getUserSessionPrompt } from "./session-preview.js";
|
|
@@ -68,6 +68,54 @@ import { RunLoopMonitor } from "./agent-session/run-loop-monitor.js";
|
|
|
68
68
|
import { VerificationTracker } from "./agent-session/verification-tracker.js";
|
|
69
69
|
import { McpToolRegistry } from "./agent-session/mcp-tools.js";
|
|
70
70
|
import { TranscriptMarkers } from "./agent-session/transcript-markers.js";
|
|
71
|
+
// ── Output cap ─────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* The output cap to send on a request. A model's output ceiling can equal its
|
|
74
|
+
* whole window (Kimi K2.7 Code: 262,144 of 262,144), and providers that count
|
|
75
|
+
* prompt + max_tokens against the window reject every request with a real
|
|
76
|
+
* prompt. Cap it at the room the model's TOTAL window leaves once the context
|
|
77
|
+
* reaches this session's compaction trigger — the largest prompt it lets build
|
|
78
|
+
* up. The total window, not the route's: the ChatGPT/Codex route's 272K
|
|
79
|
+
* window is an input budget, and GPT-6's 128K output sits outside it.
|
|
80
|
+
*/
|
|
81
|
+
export function outputCapForRequest(options) {
|
|
82
|
+
const totalWindow = getContextWindow(options.model, { provider: options.provider });
|
|
83
|
+
const { targetTokens } = resolveCompactionPolicy({
|
|
84
|
+
provider: options.provider,
|
|
85
|
+
model: options.model,
|
|
86
|
+
contextWindow: getContextWindow(options.model, {
|
|
87
|
+
provider: options.provider,
|
|
88
|
+
accountId: options.accountId,
|
|
89
|
+
}),
|
|
90
|
+
threshold: options.threshold,
|
|
91
|
+
accountId: options.accountId,
|
|
92
|
+
});
|
|
93
|
+
const room = totalWindow - targetTokens;
|
|
94
|
+
return room > 0 ? Math.min(options.maxTokens, room) : options.maxTokens;
|
|
95
|
+
}
|
|
96
|
+
// ── Paid models ────────────────────────────────────────────
|
|
97
|
+
/**
|
|
98
|
+
* Reply for chat bridges (Telegram, agent home) that have no confirmation step.
|
|
99
|
+
* Generic on purpose: every paid model (Claude fast mode, MiMo UltraSpeed) is
|
|
100
|
+
* refused here, so the reason must not name one of them.
|
|
101
|
+
*/
|
|
102
|
+
export function paidModelRefusal(modelName) {
|
|
103
|
+
return `${modelName} spends real money, so it can't be switched to from chat. Switch to it in the GG Coder app or CLI, where you confirm it.`;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Thrown by {@link AgentSession.switchModel} when the target model spends real
|
|
107
|
+
* money and the caller didn't pass the user's explicit confirmation.
|
|
108
|
+
*/
|
|
109
|
+
export class PaidModelConfirmationError extends Error {
|
|
110
|
+
modelName;
|
|
111
|
+
detail;
|
|
112
|
+
constructor(modelName, detail) {
|
|
113
|
+
super(`${modelName} spends real money. ${detail} Confirm before switching to it.`);
|
|
114
|
+
this.name = "PaidModelConfirmationError";
|
|
115
|
+
this.modelName = modelName;
|
|
116
|
+
this.detail = detail;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
71
119
|
export { resolveSessionToolResultCharLimit, resolveSessionTurnToolResultCharLimit, } from "./agent-session/helpers.js";
|
|
72
120
|
// ── Agent Session ──────────────────────────────────────────
|
|
73
121
|
export class AgentSession {
|
|
@@ -156,6 +204,8 @@ export class AgentSession {
|
|
|
156
204
|
deferredCompactionPending = false;
|
|
157
205
|
/** Latest provider count, anchored to the assistant response it measured. */
|
|
158
206
|
providerContext = null;
|
|
207
|
+
/** See {@link toolDefinitionChars}. */
|
|
208
|
+
toolCharsCache = null;
|
|
159
209
|
originalRequest = "";
|
|
160
210
|
// Messages queued by the user while a run is in flight. Drained at the
|
|
161
211
|
// mid-loop steering boundary (user steering wins over the hooks), mirroring
|
|
@@ -167,6 +217,8 @@ export class AgentSession {
|
|
|
167
217
|
// different message (or past the end) by the time the cancel arrives.
|
|
168
218
|
userQueue = [];
|
|
169
219
|
queueSeq = 0;
|
|
220
|
+
/** Monotonic count of human-originated prompts; idle-wake budgets reset on it. */
|
|
221
|
+
humanPromptSeq = 0;
|
|
170
222
|
/** Instant interrupt: the running loop's preempt listeners, fired on queueMessage. */
|
|
171
223
|
steeringListeners = new Set();
|
|
172
224
|
processManager;
|
|
@@ -296,6 +348,16 @@ export class AgentSession {
|
|
|
296
348
|
}
|
|
297
349
|
return modelInfo?.maxOutputTokens ?? 16384;
|
|
298
350
|
}
|
|
351
|
+
/** {@link outputCapForRequest} for this session's live model and settings. */
|
|
352
|
+
requestMaxTokens(accountId) {
|
|
353
|
+
return outputCapForRequest({
|
|
354
|
+
provider: this.provider,
|
|
355
|
+
model: this.model,
|
|
356
|
+
maxTokens: this.maxTokens,
|
|
357
|
+
threshold: this.settingsManager.get("compactThreshold"),
|
|
358
|
+
accountId,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
299
361
|
async initialize() {
|
|
300
362
|
// Set model for accurate token estimation
|
|
301
363
|
setEstimatorModel(this.model);
|
|
@@ -812,6 +874,8 @@ export class AgentSession {
|
|
|
812
874
|
visibility: "transcript",
|
|
813
875
|
}, options = {}) {
|
|
814
876
|
this.prewarmController?.abort();
|
|
877
|
+
if (provenance.source === "human")
|
|
878
|
+
this.humanPromptSeq += 1;
|
|
815
879
|
await this.settlePostTurnCompaction();
|
|
816
880
|
await this.adoptDeferredCheckpointBeforePrompt();
|
|
817
881
|
const slash = await this.resolveSlashInput(content);
|
|
@@ -850,6 +914,7 @@ export class AgentSession {
|
|
|
850
914
|
*/
|
|
851
915
|
async promptWithAttachments(text, attachments) {
|
|
852
916
|
this.prewarmController?.abort();
|
|
917
|
+
this.humanPromptSeq += 1;
|
|
853
918
|
await this.settlePostTurnCompaction();
|
|
854
919
|
await this.adoptDeferredCheckpointBeforePrompt();
|
|
855
920
|
const parts = this.buildAttachmentParts(text, attachments);
|
|
@@ -1499,7 +1564,10 @@ export class AgentSession {
|
|
|
1499
1564
|
}
|
|
1500
1565
|
// Reset self-correction hook state for this run; pin the latest user message
|
|
1501
1566
|
// as the verbatim original request for post-compaction re-grounding.
|
|
1502
|
-
|
|
1567
|
+
// A status update that started an idle-wake run is not the user's request.
|
|
1568
|
+
const lastUser = [...this.messages]
|
|
1569
|
+
.reverse()
|
|
1570
|
+
.find((m) => m.role === "user" && m.provenance?.kind !== "notification");
|
|
1503
1571
|
this.resetHookState(requestTextForRegrounding(lastUser));
|
|
1504
1572
|
// Resolve OAuth credentials and run agent loop.
|
|
1505
1573
|
// On 401, force-refresh the token and retry once — the provider may have
|
|
@@ -1591,7 +1659,7 @@ export class AgentSession {
|
|
|
1591
1659
|
model: this.model,
|
|
1592
1660
|
tools: options.disableTools ? [] : this.tools,
|
|
1593
1661
|
webSearch: !options.disableTools,
|
|
1594
|
-
maxTokens: this.
|
|
1662
|
+
maxTokens: this.requestMaxTokens(accountId),
|
|
1595
1663
|
maxTurns: this.opts.maxTurns,
|
|
1596
1664
|
maxTurnExtensions: this.opts.maxTurnExtensions,
|
|
1597
1665
|
// Plan mode caps effort at medium (Codex `plan_mode_reasoning_effort`
|
|
@@ -1697,8 +1765,12 @@ export class AgentSession {
|
|
|
1697
1765
|
if (anchor?.role === "assistant") {
|
|
1698
1766
|
this.providerContext = { usage: { ...transformOpts.usage }, anchor };
|
|
1699
1767
|
// Feed the authoritative usage back into the token estimator so
|
|
1700
|
-
// char-based estimates track this session's real tokenizer.
|
|
1701
|
-
|
|
1768
|
+
// char-based estimates track this session's real tokenizer. The
|
|
1769
|
+
// provider's count includes the tool definitions sent with the
|
|
1770
|
+
// request, so their characters must be counted too — without
|
|
1771
|
+
// them a short history reads as ~2 chars/token and drags the
|
|
1772
|
+
// shared ratio down for every session in the process.
|
|
1773
|
+
calibrateEstimatorFromUsage(messages.slice(0, anchorIndex), transformOpts.usage, options.disableTools ? 0 : this.toolDefinitionChars());
|
|
1702
1774
|
}
|
|
1703
1775
|
}
|
|
1704
1776
|
const force = transformOpts.force === true;
|
|
@@ -1749,12 +1821,13 @@ export class AgentSession {
|
|
|
1749
1821
|
compactedToolCalls: String(pruneResult.compactedToolCalls),
|
|
1750
1822
|
freedTokens: String(pruneResult.freedTokens),
|
|
1751
1823
|
});
|
|
1752
|
-
// The
|
|
1753
|
-
//
|
|
1754
|
-
//
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1824
|
+
// The measured size counted the pruned content, so take the
|
|
1825
|
+
// freed tokens off it. Never swap the provider's count for a
|
|
1826
|
+
// whole-history estimate here: that estimate rides a shared
|
|
1827
|
+
// chars-per-token ratio, and it once read a 234K-token MiMo
|
|
1828
|
+
// context (23% of 1M) as ~400K and compacted it against a 340K
|
|
1829
|
+
// trigger.
|
|
1830
|
+
activeTokens = Math.max(0, activeTokens - pruneResult.freedTokens);
|
|
1758
1831
|
}
|
|
1759
1832
|
}
|
|
1760
1833
|
if (Date.now() < this.compactionRetryAfter)
|
|
@@ -1926,7 +1999,17 @@ export class AgentSession {
|
|
|
1926
1999
|
// prompt. Detached by design; the pre-run path above remains the backstop.
|
|
1927
2000
|
this.maybeCompactPostTurn(creds);
|
|
1928
2001
|
}
|
|
1929
|
-
|
|
2002
|
+
/**
|
|
2003
|
+
* Switch the session's model. A model that spends real money (Claude fast
|
|
2004
|
+
* mode bills usage credits) is refused unless `confirmPaid` carries the
|
|
2005
|
+
* user's explicit yes, so no caller can switch onto one by accident: every
|
|
2006
|
+
* path (app, CLI, editors, chat bridges, RPC) fails closed by default.
|
|
2007
|
+
*/
|
|
2008
|
+
async switchModel(provider, model, options = {}) {
|
|
2009
|
+
const target = getModel(model);
|
|
2010
|
+
if (target?.billing?.paid && options.confirmPaid !== true && model !== this.model) {
|
|
2011
|
+
throw new PaidModelConfirmationError(target.name, target.billing.detail);
|
|
2012
|
+
}
|
|
1930
2013
|
// The model-switch note is appended to `this.messages`; settle any
|
|
1931
2014
|
// background compaction first so the note cannot be swapped out.
|
|
1932
2015
|
await this.settlePostTurnCompaction();
|
|
@@ -2713,6 +2796,74 @@ export class AgentSession {
|
|
|
2713
2796
|
notify(id, text) {
|
|
2714
2797
|
this.notifications.enqueue("goal", id, text, { terminal: true });
|
|
2715
2798
|
}
|
|
2799
|
+
/**
|
|
2800
|
+
* Observe urgent background alerts — a background task failed or exited
|
|
2801
|
+
* non-zero, or a child agent finished — as they are queued. Hosts use this to
|
|
2802
|
+
* wake an idle session (see {@link continueFromNotifications}); a live run
|
|
2803
|
+
* already receives them at its next steering boundary. Returns unsubscribe.
|
|
2804
|
+
*/
|
|
2805
|
+
onBackgroundAlert(listener) {
|
|
2806
|
+
return this.notifications.onUrgent(() => listener());
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* Characters of the tool definitions sent with every request (name,
|
|
2810
|
+
* description, JSON schema), for estimator calibration. Cached by tool count:
|
|
2811
|
+
* the tool array is append-only.
|
|
2812
|
+
*/
|
|
2813
|
+
toolDefinitionChars() {
|
|
2814
|
+
if (this.toolCharsCache?.count === this.tools.length)
|
|
2815
|
+
return this.toolCharsCache.chars;
|
|
2816
|
+
let chars = 0;
|
|
2817
|
+
for (const tool of this.tools) {
|
|
2818
|
+
chars += tool.name.length + tool.description.length;
|
|
2819
|
+
try {
|
|
2820
|
+
chars += JSON.stringify(resolveToolSchema(tool)).length;
|
|
2821
|
+
}
|
|
2822
|
+
catch {
|
|
2823
|
+
// A schema that cannot be serialized is not sent either.
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
this.toolCharsCache = { count: this.tools.length, chars };
|
|
2827
|
+
return chars;
|
|
2828
|
+
}
|
|
2829
|
+
/** True while an urgent background alert is waiting to be delivered. */
|
|
2830
|
+
hasPendingBackgroundAlert() {
|
|
2831
|
+
return this.notifications.hasUrgent();
|
|
2832
|
+
}
|
|
2833
|
+
/** Monotonic count of human-originated prompts this session has run. */
|
|
2834
|
+
getHumanPromptSeq() {
|
|
2835
|
+
return this.humanPromptSeq;
|
|
2836
|
+
}
|
|
2837
|
+
/**
|
|
2838
|
+
* Start a run from pending background notifications, for a session that had
|
|
2839
|
+
* already finished its turn. The notifications become one hidden runtime
|
|
2840
|
+
* message (never shown as something the user typed), and the agent decides
|
|
2841
|
+
* whether they need action. Returns false, starting nothing, when no urgent
|
|
2842
|
+
* alert is pending — a live run may have delivered it in the meantime.
|
|
2843
|
+
*/
|
|
2844
|
+
async continueFromNotifications() {
|
|
2845
|
+
if (!this.notifications.hasUrgent())
|
|
2846
|
+
return false;
|
|
2847
|
+
this.prewarmController?.abort();
|
|
2848
|
+
await this.settlePostTurnCompaction();
|
|
2849
|
+
await this.adoptDeferredCheckpointBeforePrompt();
|
|
2850
|
+
const notified = this.notifications.drain();
|
|
2851
|
+
if (notified.length === 0)
|
|
2852
|
+
return false;
|
|
2853
|
+
const message = {
|
|
2854
|
+
role: "user",
|
|
2855
|
+
content: buildIdleWakeNotificationText(notified.map((entry) => entry.text)),
|
|
2856
|
+
provenance: { source: "runtime", kind: "notification", visibility: "hidden" },
|
|
2857
|
+
};
|
|
2858
|
+
log("INFO", "notifications", "Starting a run from background alerts", {
|
|
2859
|
+
count: String(notified.length),
|
|
2860
|
+
});
|
|
2861
|
+
this.messages.push(message);
|
|
2862
|
+
await this.persistMessage(message);
|
|
2863
|
+
this.lastPersistedIndex = this.messages.length;
|
|
2864
|
+
await this.runLoop();
|
|
2865
|
+
return true;
|
|
2866
|
+
}
|
|
2716
2867
|
/** This session's sub-agents (workers), newest-state, including ones restored after a restart. */
|
|
2717
2868
|
listSubAgents() {
|
|
2718
2869
|
return this.subAgentManager?.list() ?? [];
|
|
@@ -3242,10 +3393,18 @@ export class AgentSession {
|
|
|
3242
3393
|
return { enhanced: text, segments: [{ kind: "text", text }] };
|
|
3243
3394
|
// Keep credentials and settings on the model selected at click time, even
|
|
3244
3395
|
// if the user switches models while credential resolution is pending.
|
|
3245
|
-
const { provider, model, thinkingLevel,
|
|
3396
|
+
const { provider, model, thinkingLevel, baseUrl } = this;
|
|
3397
|
+
const threshold = this.settingsManager.get("compactThreshold");
|
|
3246
3398
|
const creds = await this.authStorage.resolveCredentials(provider, {
|
|
3247
3399
|
storageKeys: this.currentAuthStorageKeys(),
|
|
3248
3400
|
});
|
|
3401
|
+
const maxTokens = outputCapForRequest({
|
|
3402
|
+
provider,
|
|
3403
|
+
model,
|
|
3404
|
+
maxTokens: this.maxTokens,
|
|
3405
|
+
threshold,
|
|
3406
|
+
accountId: creds.accountId,
|
|
3407
|
+
});
|
|
3249
3408
|
// Cheap, best-effort stack detection from the project root so terminology is
|
|
3250
3409
|
// idiomatic to the user's stack. Never throws (returns "" on any failure).
|
|
3251
3410
|
let stack = "";
|
|
@@ -3363,7 +3522,7 @@ export class AgentSession {
|
|
|
3363
3522
|
messages,
|
|
3364
3523
|
tools: this.tools,
|
|
3365
3524
|
webSearch: true,
|
|
3366
|
-
maxTokens: this.
|
|
3525
|
+
maxTokens: this.requestMaxTokens(creds.accountId),
|
|
3367
3526
|
thinking: this.planModeRef.current
|
|
3368
3527
|
? clampThinkingForPlanMode(this.thinkingLevel)
|
|
3369
3528
|
: this.thinkingLevel,
|
|
@@ -3675,7 +3834,7 @@ export class AgentSession {
|
|
|
3675
3834
|
}
|
|
3676
3835
|
createSlashCommandContext() {
|
|
3677
3836
|
return {
|
|
3678
|
-
switchModel: (provider, model) => this.switchModel(provider, model),
|
|
3837
|
+
switchModel: (provider, model, options) => this.switchModel(provider, model, options),
|
|
3679
3838
|
compact: (focus) => this.compact(undefined, "manual", focus),
|
|
3680
3839
|
newSession: () => this.newSession(),
|
|
3681
3840
|
listSessions: async () => {
|