@gajae-code/coding-agent 0.13.1 → 0.13.3
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 +41 -0
- package/dist/types/config/model-registry.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +3 -3
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/dynamic-theme-text.d.ts +9 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +5 -5
- package/dist/types/modes/components/pet-capability.d.ts +5 -5
- package/dist/types/modes/components/settings-selector.d.ts +5 -0
- package/dist/types/modes/theme/defaults/index.d.ts +118 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +13 -0
- package/dist/types/session/session-manager.d.ts +7 -0
- package/dist/types/tools/image-gen.d.ts +1 -0
- package/package.json +8 -8
- package/src/config/model-profile-activation.ts +3 -1
- package/src/config/model-profiles.ts +59 -2
- package/src/config/model-registry.ts +16 -1
- package/src/config/settings-schema.ts +1 -1
- package/src/coordinator-mcp/server.ts +3 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +31 -6
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/main.ts +4 -0
- package/src/modes/components/chrome.ts +4 -3
- package/src/modes/components/dynamic-theme-text.ts +22 -0
- package/src/modes/components/gajae-pet-widget.ts +130 -42
- package/src/modes/components/model-selector.ts +1 -6
- package/src/modes/components/pet-capability.ts +10 -5
- package/src/modes/components/settings-selector.ts +20 -5
- package/src/modes/components/status-line/segments.ts +3 -3
- package/src/modes/controllers/selector-controller.ts +61 -5
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/theme/defaults/index.ts +2 -0
- package/src/modes/theme/defaults/ouroboros.json +118 -0
- package/src/sdk/broker/process-incarnation.ts +6 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/bus/telegram-daemon.ts +51 -43
- package/src/session/agent-session.ts +39 -0
- package/src/session/internal/managed-session-storage.ts +162 -40
- package/src/session/session-manager.ts +28 -0
- package/src/slash-commands/builtin-registry.ts +62 -8
- package/src/task/index.ts +5 -2
- package/src/tools/bash.ts +11 -9
- package/src/tools/image-gen.ts +322 -126
- package/src/tools/todo-contract.ts +55 -3
- package/src/tools/todo-write.ts +4 -3
|
@@ -14774,6 +14774,20 @@ export class SessionManager {
|
|
|
14774
14774
|
getSessionFile(): string | undefined {
|
|
14775
14775
|
return this.#sessionFile;
|
|
14776
14776
|
}
|
|
14777
|
+
/**
|
|
14778
|
+
* On-disk transcript file size in bytes. Returns 0 when the file is
|
|
14779
|
+
* unavailable, unreadable, or no session file is set. The managed-storage
|
|
14780
|
+
* path reads through the descriptor (no full-file scan); the plain-file
|
|
14781
|
+
* path uses statSync.
|
|
14782
|
+
*/
|
|
14783
|
+
getTranscriptFileBytes(): number {
|
|
14784
|
+
if (!this.#sessionFile) return 0;
|
|
14785
|
+
try {
|
|
14786
|
+
return this.#statSync(this.#sessionFile).size;
|
|
14787
|
+
} catch {
|
|
14788
|
+
return 0;
|
|
14789
|
+
}
|
|
14790
|
+
}
|
|
14777
14791
|
|
|
14778
14792
|
getSessionMemoryStats(): SessionMemoryStats {
|
|
14779
14793
|
const runtime = this.#sidecarRuntime;
|
|
@@ -15783,6 +15797,20 @@ export class SessionManager {
|
|
|
15783
15797
|
if (publishResumeBreadcrumb && persisted) writeTerminalBreadcrumb(this.cwd, this.#sessionFile);
|
|
15784
15798
|
if (persisted) this.#readOnlyResume = false;
|
|
15785
15799
|
} catch (err) {
|
|
15800
|
+
// content_too_large on the managed append hot path means the append-only
|
|
15801
|
+
// transcript file has reached the per-file storage limit (64 MiB). The
|
|
15802
|
+
// on-disk file is append-only and grew past the limit even though the
|
|
15803
|
+
// in-memory entry list may be much smaller (compaction evicts old
|
|
15804
|
+
// content but the append-only file never shrinks until a full rewrite).
|
|
15805
|
+
// Fall back to a full rewrite (replaceSync) which writes only the live
|
|
15806
|
+
// in-memory entries, shrinking the file below the limit. The entry has
|
|
15807
|
+
// already been added to #fileEntries by #appendEntryWithinPersistenceFence.
|
|
15808
|
+
if (err instanceof Error && err.message === "content_too_large") {
|
|
15809
|
+
this.#rewriteFileSync();
|
|
15810
|
+
if (publishResumeBreadcrumb) writeTerminalBreadcrumb(this.cwd, this.#sessionFile);
|
|
15811
|
+
this.#readOnlyResume = false;
|
|
15812
|
+
return;
|
|
15813
|
+
}
|
|
15786
15814
|
this.#recordPersistError(err);
|
|
15787
15815
|
throw this.#persistError ?? toError(err);
|
|
15788
15816
|
}
|
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
4
4
|
import { type Model, modelsAreEqual } from "@gajae-code/ai/core";
|
|
5
5
|
import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
6
|
-
import { PET_SKINS, type PetMode, Spacer, Text } from "@gajae-code/tui";
|
|
6
|
+
import { PET_SKIN_IDS, PET_SKINS, type PetMode, Spacer, Text } from "@gajae-code/tui";
|
|
7
7
|
import { setProjectDir } from "@gajae-code/utils";
|
|
8
8
|
import { jobElapsedMs } from "../async";
|
|
9
9
|
import { activateModelProfile, materializeActiveModelProfileAssignments } from "../config/model-profile-activation";
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
GJC_MODEL_ASSIGNMENT_TARGET_IDS,
|
|
13
13
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
14
14
|
type GjcModelAssignmentTargetId,
|
|
15
|
+
requiresExplicitThinkingChoice,
|
|
15
16
|
} from "../config/model-registry";
|
|
16
17
|
|
|
17
18
|
import {
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
} from "../config/model-resolver";
|
|
24
25
|
import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../discovery/helpers.js";
|
|
25
26
|
import { DynamicBorder } from "../modes/components/dynamic-border";
|
|
26
|
-
import { theme } from "../modes/theme/theme";
|
|
27
|
+
import { getAvailableThemes, getDetectedThemeSettingsPath, setTheme, theme } from "../modes/theme/theme";
|
|
27
28
|
import {
|
|
28
29
|
type ComposerSubmissionOptions,
|
|
29
30
|
canApplyComposerSubmission,
|
|
@@ -70,8 +71,11 @@ function canClearComposer(runtime: BuiltinSlashCommandRuntime): boolean {
|
|
|
70
71
|
|
|
71
72
|
const PET_COMMAND_OPTIONS: ReadonlyArray<{ name: string; mode: PetMode; description: string }> = [
|
|
72
73
|
{ name: "off", mode: "off", description: "Hide the pet" },
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
...PET_SKIN_IDS.map(id => ({
|
|
75
|
+
name: PET_SKINS[id].label,
|
|
76
|
+
mode: id,
|
|
77
|
+
description: PET_SKINS[id].description,
|
|
78
|
+
})),
|
|
75
79
|
];
|
|
76
80
|
const PET_COMMAND_HINT = `[${PET_COMMAND_OPTIONS.map(option => option.name).join("|")}]`;
|
|
77
81
|
/**
|
|
@@ -709,10 +713,48 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
709
713
|
},
|
|
710
714
|
{
|
|
711
715
|
name: "theme",
|
|
712
|
-
description: "
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
+
description: "Change theme immediately, or open the theme selector without args",
|
|
717
|
+
inlineHint: "[theme]",
|
|
718
|
+
allowArgs: true,
|
|
719
|
+
handleTui: async (command, runtime) => {
|
|
720
|
+
const ctx = runtime.ctx;
|
|
721
|
+
const name = command.args?.trim();
|
|
722
|
+
if (!name) {
|
|
723
|
+
ctx.showThemeSelector();
|
|
724
|
+
ctx.editor.setText("");
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const available = await getAvailableThemes();
|
|
728
|
+
if (!available.includes(name)) {
|
|
729
|
+
ctx.showError(`Unknown theme "${name}". Available themes: ${available.join(", ")}`);
|
|
730
|
+
ctx.editor.setText("");
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (!ctx.settings.canWriteDurableConfig()) {
|
|
734
|
+
ctx.showError(
|
|
735
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
736
|
+
);
|
|
737
|
+
ctx.editor.setText("");
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
ctx.settings.set(getDetectedThemeSettingsPath(), name);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
ctx.showError(error instanceof Error ? error.message : String(error));
|
|
744
|
+
ctx.editor.setText("");
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const result = await setTheme(name, true, { shouldApply: () => !ctx.isStopped?.() });
|
|
748
|
+
if (ctx.isStopped?.()) return;
|
|
749
|
+
ctx.statusLine.invalidate();
|
|
750
|
+
ctx.updateEditorTopBorder();
|
|
751
|
+
ctx.ui.invalidate();
|
|
752
|
+
if (result.success) {
|
|
753
|
+
ctx.showStatus(`Theme changed to ${name}`);
|
|
754
|
+
} else {
|
|
755
|
+
ctx.showError(`Failed to load theme "${name}": ${result.error}\nFell back to dark theme.`);
|
|
756
|
+
}
|
|
757
|
+
ctx.editor.setText("");
|
|
716
758
|
},
|
|
717
759
|
},
|
|
718
760
|
{
|
|
@@ -832,6 +874,18 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
832
874
|
return usage(modelSelectionUsage(runtime, resolution.failure.message), runtime);
|
|
833
875
|
}
|
|
834
876
|
const { selection } = resolution;
|
|
877
|
+
if (
|
|
878
|
+
selection.thinkingLevel === undefined &&
|
|
879
|
+
targetIds.some(targetId => requiresExplicitThinkingChoice(selection.model, targetId))
|
|
880
|
+
) {
|
|
881
|
+
return usage(
|
|
882
|
+
modelSelectionUsage(
|
|
883
|
+
runtime,
|
|
884
|
+
`Model ${selection.model.provider}/${selection.model.id} requires an explicit effort suffix.`,
|
|
885
|
+
),
|
|
886
|
+
runtime,
|
|
887
|
+
);
|
|
888
|
+
}
|
|
835
889
|
try {
|
|
836
890
|
const includesDefault = targetIds.includes("default");
|
|
837
891
|
const includesRoleAgent = targetIds.some(role => role !== "default");
|
package/src/task/index.ts
CHANGED
|
@@ -1900,6 +1900,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1900
1900
|
file => path.basename(file.path).toLowerCase() !== "agents.md",
|
|
1901
1901
|
);
|
|
1902
1902
|
const promptTemplates = this.session.promptTemplates;
|
|
1903
|
+
const parentMcpManager = this.session.getMcpManager?.();
|
|
1904
|
+
// Exact-config tools-only managers belong to the top-level ACP session and cannot be reused by sub-sessions.
|
|
1905
|
+
const reusableParentMcpManager = parentMcpManager?.isToolsOnly() ? undefined : parentMcpManager;
|
|
1903
1906
|
|
|
1904
1907
|
// Initialize progress for all tasks
|
|
1905
1908
|
for (let i = 0; i < tasksWithUniqueIds.length; i++) {
|
|
@@ -2036,7 +2039,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2036
2039
|
parentArtifactManager,
|
|
2037
2040
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
2038
2041
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
2039
|
-
parentMcpManager:
|
|
2042
|
+
parentMcpManager: reusableParentMcpManager,
|
|
2040
2043
|
forkContextSeed,
|
|
2041
2044
|
});
|
|
2042
2045
|
return {
|
|
@@ -2113,7 +2116,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2113
2116
|
parentArtifactManager,
|
|
2114
2117
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
2115
2118
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
2116
|
-
parentMcpManager:
|
|
2119
|
+
parentMcpManager: reusableParentMcpManager,
|
|
2117
2120
|
forkContextSeed,
|
|
2118
2121
|
});
|
|
2119
2122
|
let capturedResult = {
|
package/src/tools/bash.ts
CHANGED
|
@@ -862,26 +862,28 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
862
862
|
return { kind: "aborted" };
|
|
863
863
|
}
|
|
864
864
|
|
|
865
|
+
const threshold = Promise.withResolvers<{ kind: "running" }>();
|
|
866
|
+
const thresholdTimer = setTimeout(() => threshold.resolve({ kind: "running" }), Math.max(0, thresholdMs));
|
|
865
867
|
const waiters: Array<Promise<ManagedBashJobCompletion | { kind: "running" } | { kind: "aborted" }>> = [
|
|
866
868
|
job.completion,
|
|
867
|
-
|
|
869
|
+
threshold.promise,
|
|
868
870
|
];
|
|
869
871
|
if (backgroundRequest) {
|
|
870
872
|
waiters.push(backgroundRequest.then(() => ({ kind: "running" as const })));
|
|
871
873
|
}
|
|
872
874
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
+
let onAbort: (() => void) | undefined;
|
|
876
|
+
if (signal) {
|
|
877
|
+
const aborted = Promise.withResolvers<{ kind: "aborted" }>();
|
|
878
|
+
onAbort = () => aborted.resolve({ kind: "aborted" });
|
|
879
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
880
|
+
waiters.push(aborted.promise);
|
|
875
881
|
}
|
|
876
|
-
|
|
877
|
-
const { promise: abortedPromise, resolve: resolveAborted } = Promise.withResolvers<{ kind: "aborted" }>();
|
|
878
|
-
const onAbort = () => resolveAborted({ kind: "aborted" });
|
|
879
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
880
|
-
waiters.push(abortedPromise);
|
|
881
882
|
try {
|
|
882
883
|
return await Promise.race(waiters);
|
|
883
884
|
} finally {
|
|
884
|
-
|
|
885
|
+
clearTimeout(thresholdTimer);
|
|
886
|
+
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
885
887
|
}
|
|
886
888
|
}
|
|
887
889
|
|