@gajae-code/coding-agent 0.13.2 → 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 +33 -8
- 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/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/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +31 -6
- package/src/internal-urls/docs-index.generated.ts +3 -2
- 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/theme/defaults/index.ts +2 -0
- package/src/modes/theme/defaults/ouroboros.json +118 -0
- package/src/session/agent-session.ts +39 -0
- package/src/session/internal/managed-session-storage.ts +26 -19
- package/src/session/session-manager.ts +28 -0
- package/src/slash-commands/builtin-registry.ts +19 -3
- package/src/tools/image-gen.ts +322 -126
- package/src/tools/todo-contract.ts +55 -3
- package/src/tools/todo-write.ts +4 -3
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/can1357/gajae-code/main/packages/coding-agent/theme-schema.json",
|
|
3
|
+
"name": "ouroboros",
|
|
4
|
+
"vars": {
|
|
5
|
+
"cyan": "#58d5c3",
|
|
6
|
+
"blue": "#7092be",
|
|
7
|
+
"green": "#78c878",
|
|
8
|
+
"red": "#ff667a",
|
|
9
|
+
"yellow": "#f2c94c",
|
|
10
|
+
"gray": "#a9bbb2",
|
|
11
|
+
"dimGray": "#5b756d",
|
|
12
|
+
"darkGray": "#29443f",
|
|
13
|
+
"accent": "#aee80e",
|
|
14
|
+
"selectedBg": "#1b3b3b",
|
|
15
|
+
"userMsgBg": "#102b30",
|
|
16
|
+
"toolPendingBg": "#0d252b",
|
|
17
|
+
"toolSuccessBg": "#163326",
|
|
18
|
+
"toolErrorBg": "#351923",
|
|
19
|
+
"customMsgBg": "#0d2130",
|
|
20
|
+
"abyss": "#050b18",
|
|
21
|
+
"deepNavy": "#0b1630",
|
|
22
|
+
"mantle": "#0d2130",
|
|
23
|
+
"surface": "#102b30",
|
|
24
|
+
"surfaceBright": "#1b3b3b",
|
|
25
|
+
"ouroTeal": "#38bfa7",
|
|
26
|
+
"serpentGreen": "#78c878",
|
|
27
|
+
"petLime": "#aee80e",
|
|
28
|
+
"ritualGold": "#f2c94c",
|
|
29
|
+
"eyeGold": "#ffe786",
|
|
30
|
+
"coolScale": "#7092be",
|
|
31
|
+
"foam": "#e7f1e9",
|
|
32
|
+
"mutedScale": "#a9bbb2",
|
|
33
|
+
"dimScale": "#5b756d",
|
|
34
|
+
"dangerRed": "#ff667a",
|
|
35
|
+
"diffRemovalRed": "#df5b6b"
|
|
36
|
+
},
|
|
37
|
+
"colors": {
|
|
38
|
+
"accent": "petLime",
|
|
39
|
+
"border": "ouroTeal",
|
|
40
|
+
"borderAccent": "ritualGold",
|
|
41
|
+
"borderMuted": "dimScale",
|
|
42
|
+
"success": "serpentGreen",
|
|
43
|
+
"error": "dangerRed",
|
|
44
|
+
"warning": "ritualGold",
|
|
45
|
+
"muted": "mutedScale",
|
|
46
|
+
"dim": "dimScale",
|
|
47
|
+
"text": "foam",
|
|
48
|
+
"thinkingText": "mutedScale",
|
|
49
|
+
"selectedBg": "surfaceBright",
|
|
50
|
+
"userMessageBg": "surface",
|
|
51
|
+
"userMessageText": "foam",
|
|
52
|
+
"customMessageBg": "mantle",
|
|
53
|
+
"customMessageText": "foam",
|
|
54
|
+
"customMessageLabel": "petLime",
|
|
55
|
+
"toolPendingBg": "mantle",
|
|
56
|
+
"toolSuccessBg": "#163326",
|
|
57
|
+
"toolErrorBg": "#351923",
|
|
58
|
+
"toolTitle": "foam",
|
|
59
|
+
"toolOutput": "mutedScale",
|
|
60
|
+
"mdHeading": "petLime",
|
|
61
|
+
"mdLink": "ouroTeal",
|
|
62
|
+
"mdLinkUrl": "dimScale",
|
|
63
|
+
"mdCode": "eyeGold",
|
|
64
|
+
"mdCodeBlock": "serpentGreen",
|
|
65
|
+
"mdCodeBlockBorder": "ouroTeal",
|
|
66
|
+
"mdQuote": "mutedScale",
|
|
67
|
+
"mdQuoteBorder": "ouroTeal",
|
|
68
|
+
"mdHr": "dimScale",
|
|
69
|
+
"mdListBullet": "ritualGold",
|
|
70
|
+
"toolDiffAdded": "serpentGreen",
|
|
71
|
+
"toolDiffRemoved": "diffRemovalRed",
|
|
72
|
+
"toolDiffContext": "mutedScale",
|
|
73
|
+
"syntaxComment": "dimScale",
|
|
74
|
+
"syntaxKeyword": "petLime",
|
|
75
|
+
"syntaxFunction": "eyeGold",
|
|
76
|
+
"syntaxVariable": "foam",
|
|
77
|
+
"syntaxString": "serpentGreen",
|
|
78
|
+
"syntaxNumber": "coolScale",
|
|
79
|
+
"syntaxType": "ouroTeal",
|
|
80
|
+
"syntaxOperator": "ritualGold",
|
|
81
|
+
"syntaxPunctuation": "mutedScale",
|
|
82
|
+
"thinkingOff": "dimScale",
|
|
83
|
+
"thinkingMinimal": "mutedScale",
|
|
84
|
+
"thinkingLow": "coolScale",
|
|
85
|
+
"thinkingMedium": "ouroTeal",
|
|
86
|
+
"thinkingHigh": "petLime",
|
|
87
|
+
"thinkingXhigh": "eyeGold",
|
|
88
|
+
"bashMode": "petLime",
|
|
89
|
+
"statusLineBg": "deepNavy",
|
|
90
|
+
"statusLineSep": "dimScale",
|
|
91
|
+
"statusLineModel": "petLime",
|
|
92
|
+
"statusLinePath": "ouroTeal",
|
|
93
|
+
"statusLineGitClean": "serpentGreen",
|
|
94
|
+
"statusLineGitDirty": "ritualGold",
|
|
95
|
+
"statusLineContext": "coolScale",
|
|
96
|
+
"statusLineSpend": "eyeGold",
|
|
97
|
+
"statusLineStaged": "serpentGreen",
|
|
98
|
+
"statusLineDirty": "ritualGold",
|
|
99
|
+
"statusLineUntracked": "diffRemovalRed",
|
|
100
|
+
"statusLineOutput": "foam",
|
|
101
|
+
"statusLineCost": "eyeGold",
|
|
102
|
+
"statusLineSubagents": "ouroTeal",
|
|
103
|
+
"pythonMode": "ritualGold"
|
|
104
|
+
},
|
|
105
|
+
"export": {
|
|
106
|
+
"pageBg": "#050b18",
|
|
107
|
+
"cardBg": "#0b1630",
|
|
108
|
+
"infoBg": "#102b30"
|
|
109
|
+
},
|
|
110
|
+
"symbols": {
|
|
111
|
+
"preset": "unicode",
|
|
112
|
+
"overrides": {
|
|
113
|
+
"icon.pi": "🐍",
|
|
114
|
+
"status.running": "∞",
|
|
115
|
+
"md.bullet": "◇"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -954,6 +954,13 @@ type RetryErrorClassification =
|
|
|
954
954
|
|
|
955
955
|
const BARE_DEFAULT_WATCHDOG_ERROR =
|
|
956
956
|
/^(?:[A-Za-z][A-Za-z0-9-]*(?: [A-Za-z][A-Za-z0-9-]*){0,3} )stream (?:timed out while waiting for the first event|stalled while waiting for the next event)$/;
|
|
957
|
+
/**
|
|
958
|
+
* Consecutive same-model retries allowed for `escaped_arguments_discarded`
|
|
959
|
+
* managed outcomes before the run terminates. Mirrors the agent loop's
|
|
960
|
+
* escaped-non-ASCII resample budget on dev; enforced session-side here because
|
|
961
|
+
* a managed retry re-enters the loop as a fresh run.
|
|
962
|
+
*/
|
|
963
|
+
const MAX_ESCAPED_ARGUMENT_RETRIES = 2;
|
|
957
964
|
const PROVIDER_FIRST_EVENT_TIMEOUT_ERROR = "Provider stream timed out while waiting for the first event";
|
|
958
965
|
const WRAPPED_PROVIDER_FIRST_EVENT_TIMEOUT_ERROR = `Error: ${PROVIDER_FIRST_EVENT_TIMEOUT_ERROR}`;
|
|
959
966
|
const PROVIDER_FIRST_EVENT_TIMEOUT_WITHOUT_ARTICLE_ERROR = "Provider stream timed out while waiting for first event";
|
|
@@ -2030,6 +2037,7 @@ export class AgentSession {
|
|
|
2030
2037
|
#retryPromise: Promise<void> | undefined = undefined;
|
|
2031
2038
|
#retryResolve: (() => void) | undefined = undefined;
|
|
2032
2039
|
#defaultFallbackController: FallbackChainController | undefined;
|
|
2040
|
+
#consecutiveEscapedArgumentRetries = 0;
|
|
2033
2041
|
#overflowMaintenanceAttempts = 0;
|
|
2034
2042
|
#defaultFallbackExhaustedLastTurn = false;
|
|
2035
2043
|
#fallbackInvocationId = 0;
|
|
@@ -13690,6 +13698,7 @@ export class AgentSession {
|
|
|
13690
13698
|
sessionMemory.metadataResidentBytes,
|
|
13691
13699
|
tuiChatChildren: retainedMemory.tuiChatChildren ?? 0,
|
|
13692
13700
|
tuiCachedRenderBytes: retainedMemory.tuiCachedRenderBytes ?? 0,
|
|
13701
|
+
transcriptFileBytes: this.sessionManager.getTranscriptFileBytes(),
|
|
13693
13702
|
};
|
|
13694
13703
|
}
|
|
13695
13704
|
|
|
@@ -15710,10 +15719,40 @@ export class AgentSession {
|
|
|
15710
15719
|
}
|
|
15711
15720
|
|
|
15712
15721
|
async #handleManagedAttemptOutcome(outcome: ManagedAttemptOutcome): Promise<ManagedAttemptDecision> {
|
|
15722
|
+
if (outcome.type !== "escaped_arguments_discarded") this.#consecutiveEscapedArgumentRetries = 0;
|
|
15713
15723
|
if (outcome.type === "run_terminal") {
|
|
15714
15724
|
this.#defaultFallbackChain().resetAttemptBudget();
|
|
15715
15725
|
return { type: "terminal", terminal: { stopReason: outcome.reason } };
|
|
15716
15726
|
}
|
|
15727
|
+
if (outcome.type === "escaped_arguments_discarded") {
|
|
15728
|
+
// An escaped-non-ASCII wire defect is a sampling accident, not provider
|
|
15729
|
+
// evidence: never charge the attempt, advance the chain, or suppress the
|
|
15730
|
+
// selector. The loop already removed the defective turn from history, so
|
|
15731
|
+
// this decision just re-issues the same request on the same model.
|
|
15732
|
+
//
|
|
15733
|
+
// The retry bound lives HERE, not in the loop: a managed retry re-enters
|
|
15734
|
+
// the loop as a fresh run, so no loop-local budget survives it. After
|
|
15735
|
+
// MAX_ESCAPED_ARGUMENT_RETRIES consecutive escaped-argument discards the
|
|
15736
|
+
// run terminates with an error instead of ping-ponging forever against a
|
|
15737
|
+
// model that deterministically emits escaped arguments. The counter
|
|
15738
|
+
// resets on any other outcome.
|
|
15739
|
+
this.#defaultFallbackChain().discardStartedAttempt();
|
|
15740
|
+
this.#consecutiveEscapedArgumentRetries++;
|
|
15741
|
+
if (this.#consecutiveEscapedArgumentRetries > MAX_ESCAPED_ARGUMENT_RETRIES) {
|
|
15742
|
+
this.#consecutiveEscapedArgumentRetries = 0;
|
|
15743
|
+
return {
|
|
15744
|
+
type: "terminal",
|
|
15745
|
+
terminal: { stopReason: "error", messages: [outcome.message] },
|
|
15746
|
+
};
|
|
15747
|
+
}
|
|
15748
|
+
return {
|
|
15749
|
+
type: "retry",
|
|
15750
|
+
continuation: async ownership => {
|
|
15751
|
+
if (!ownership.isCurrent() || ownership.lease.signal.aborted) return;
|
|
15752
|
+
await this.agent.continue(this.#managedFallbackPromptOptions());
|
|
15753
|
+
},
|
|
15754
|
+
};
|
|
15755
|
+
}
|
|
15717
15756
|
if (outcome.type === "context_overflow_discarded") {
|
|
15718
15757
|
// The provider invocation happened, but overflow is context maintenance rather
|
|
15719
15758
|
// than a fallback-policy failure. Keep the logical run owner and do not charge,
|
|
@@ -1336,25 +1336,32 @@ export class ManagedSessionDescendantStore {
|
|
|
1336
1336
|
fsyncDirectory(this.#baseDir);
|
|
1337
1337
|
return;
|
|
1338
1338
|
}
|
|
1339
|
-
if (outcome.reason === "destination_exists") {
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1339
|
+
if (outcome.reason === "destination_exists" || outcome.reason === "invalid_request") {
|
|
1340
|
+
const captureIfPresent = (pathname: string): ManagedFileSnapshot | undefined => {
|
|
1341
|
+
try {
|
|
1342
|
+
return captureManagedFilePrefixNoFollow(pathname, REPLACEMENT_CLEANUP_RECEIPT_MAX_BYTES);
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
1345
|
+
throw error;
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
const currentPending = captureIfPresent(receiptPath);
|
|
1349
|
+
const existing = captureIfPresent(destination);
|
|
1350
|
+
if (!currentPending && existing) {
|
|
1351
|
+
if (
|
|
1352
|
+
!sameReplacementIdentity(existing.identity, pending.identity) ||
|
|
1353
|
+
existing.identity.sha256 !== pending.identity.sha256
|
|
1354
|
+
)
|
|
1355
|
+
throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
1356
|
+
fsyncDirectory(this.#baseDir);
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
if (currentPending && !existing) throw publishFailure(outcome);
|
|
1360
|
+
// Two names cannot both authoritatively represent the one receipt identity
|
|
1361
|
+
// encoded in the canonical filename. A byte-identical copy has a different
|
|
1362
|
+
// filesystem identity and would make the canonical receipt fail its next
|
|
1363
|
+
// reconciliation, while a hard link violates the single-link capture fence.
|
|
1364
|
+
throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
1358
1365
|
}
|
|
1359
1366
|
throw publishFailure(outcome);
|
|
1360
1367
|
}
|
|
@@ -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 {
|
|
@@ -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
|
/**
|
|
@@ -870,6 +874,18 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
870
874
|
return usage(modelSelectionUsage(runtime, resolution.failure.message), runtime);
|
|
871
875
|
}
|
|
872
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
|
+
}
|
|
873
889
|
try {
|
|
874
890
|
const includesDefault = targetIds.includes("default");
|
|
875
891
|
const includesRoleAgent = targetIds.some(role => role !== "default");
|