@gajae-code/coding-agent 0.6.3 → 0.6.5
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 +50 -0
- package/README.md +73 -1
- package/dist/types/cli/migrate-cli.d.ts +20 -0
- package/dist/types/commands/migrate.d.ts +33 -0
- package/dist/types/config/keybindings.d.ts +4 -0
- package/dist/types/config/settings-schema.d.ts +27 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -2
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/session-layout.d.ts +59 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +47 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +1 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +5 -4
- package/dist/types/gjc-runtime/state-schema.d.ts +2 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +36 -7
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +2 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +7 -4
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +1 -1
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +1 -1
- package/dist/types/harness-control-plane/storage.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +12 -4
- package/dist/types/migrate/action-planner.d.ts +11 -0
- package/dist/types/migrate/adapters/claude-code.d.ts +2 -0
- package/dist/types/migrate/adapters/codex.d.ts +5 -0
- package/dist/types/migrate/adapters/index.d.ts +45 -0
- package/dist/types/migrate/adapters/opencode.d.ts +2 -0
- package/dist/types/migrate/executor.d.ts +2 -0
- package/dist/types/migrate/mcp-mapper.d.ts +20 -0
- package/dist/types/migrate/report.d.ts +18 -0
- package/dist/types/migrate/skill-normalizer.d.ts +27 -0
- package/dist/types/migrate/types.d.ts +126 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -0
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +1 -1
- package/dist/types/research-plan/index.d.ts +1 -0
- package/dist/types/research-plan/ledger.d.ts +33 -0
- package/dist/types/rlm/artifacts.d.ts +1 -1
- package/dist/types/runtime-mcp/config-writer.d.ts +26 -0
- package/dist/types/skill-state/active-state.d.ts +6 -11
- package/dist/types/skill-state/canonical-skills.d.ts +3 -0
- package/dist/types/skill-state/workflow-hud.d.ts +2 -0
- package/dist/types/task/spawn-gate.d.ts +1 -10
- package/package.json +7 -7
- package/src/cli/migrate-cli.ts +106 -0
- package/src/cli/setup-cli.ts +14 -1
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +2 -2
- package/src/commands/launch.ts +1 -1
- package/src/commands/migrate.ts +46 -0
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +7 -3
- package/src/config/model-registry.ts +9 -2
- package/src/config/model-resolver.ts +13 -2
- package/src/config/settings-schema.ts +17 -0
- package/src/coordinator-mcp/policy.ts +10 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +0 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +28 -24
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +51 -47
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +17 -13
- package/src/exec/bash-executor.ts +3 -1
- package/src/extensibility/custom-commands/loader.ts +0 -7
- package/src/extensibility/gjc-plugins/injection.ts +23 -4
- package/src/extensibility/gjc-plugins/state.ts +16 -1
- package/src/gjc-runtime/deep-interview-recorder.ts +43 -18
- package/src/gjc-runtime/deep-interview-runtime.ts +49 -23
- package/src/gjc-runtime/goal-mode-request.ts +26 -11
- package/src/gjc-runtime/launch-tmux.ts +68 -15
- package/src/gjc-runtime/ralplan-runtime.ts +79 -50
- package/src/gjc-runtime/session-layout.ts +180 -0
- package/src/gjc-runtime/session-resolution.ts +217 -0
- package/src/gjc-runtime/state-graph.ts +1 -2
- package/src/gjc-runtime/state-migrations.ts +1 -0
- package/src/gjc-runtime/state-runtime.ts +230 -121
- package/src/gjc-runtime/state-schema.ts +2 -0
- package/src/gjc-runtime/state-writer.ts +289 -41
- package/src/gjc-runtime/team-runtime.ts +43 -19
- package/src/gjc-runtime/tmux-sessions.ts +43 -2
- package/src/gjc-runtime/ultragoal-guard.ts +45 -2
- package/src/gjc-runtime/ultragoal-runtime.ts +121 -41
- package/src/gjc-runtime/workflow-command-ref.ts +1 -2
- package/src/gjc-runtime/workflow-manifest.ts +1 -2
- package/src/harness-control-plane/storage.ts +14 -4
- package/src/hooks/native-skill-hook.ts +38 -12
- package/src/hooks/skill-state.ts +178 -83
- package/src/internal-urls/docs-index.generated.ts +9 -6
- package/src/migrate/action-planner.ts +318 -0
- package/src/migrate/adapters/claude-code.ts +39 -0
- package/src/migrate/adapters/codex.ts +70 -0
- package/src/migrate/adapters/index.ts +277 -0
- package/src/migrate/adapters/opencode.ts +52 -0
- package/src/migrate/executor.ts +81 -0
- package/src/migrate/mcp-mapper.ts +152 -0
- package/src/migrate/report.ts +104 -0
- package/src/migrate/skill-normalizer.ts +80 -0
- package/src/migrate/types.ts +163 -0
- package/src/modes/bridge/bridge-mode.ts +2 -2
- package/src/modes/components/custom-editor.ts +30 -20
- package/src/modes/components/welcome.ts +42 -9
- package/src/modes/controllers/input-controller.ts +21 -3
- package/src/modes/interactive-mode.ts +22 -1
- package/src/modes/prompt-action-autocomplete.ts +11 -1
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/shared/agent-wire/unattended-audit.ts +3 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +1 -2
- package/src/research-plan/index.ts +1 -0
- package/src/research-plan/ledger.ts +177 -0
- package/src/rlm/artifacts.ts +12 -3
- package/src/rlm/index.ts +7 -0
- package/src/runtime-mcp/config-writer.ts +46 -0
- package/src/session/agent-session.ts +15 -21
- package/src/session/session-manager.ts +19 -2
- package/src/setup/hermes/templates/operator-instructions.v1.md +8 -0
- package/src/setup/hermes-setup.ts +1 -1
- package/src/skill-state/active-state.ts +72 -108
- package/src/skill-state/canonical-skills.ts +4 -0
- package/src/skill-state/deep-interview-mutation-guard.ts +28 -109
- package/src/skill-state/workflow-hud.ts +4 -2
- package/src/skill-state/workflow-state-contract.ts +3 -3
- package/src/slash-commands/builtin-registry.ts +8 -4
- package/src/system-prompt.ts +11 -9
- package/src/task/agents.ts +1 -22
- package/src/task/index.ts +1 -41
- package/src/task/spawn-gate.ts +1 -38
- package/src/task/types.ts +1 -1
- package/src/tools/ask.ts +34 -12
- package/src/tools/computer.ts +58 -4
- package/dist/types/extensibility/custom-commands/bundled/review/index.d.ts +0 -10
- package/src/extensibility/custom-commands/bundled/review/index.ts +0 -456
- package/src/prompts/agents/explore.md +0 -58
- package/src/prompts/agents/plan.md +0 -49
- package/src/prompts/agents/reviewer.md +0 -141
- package/src/prompts/agents/task.md +0 -16
- package/src/prompts/review-request.md +0 -70
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Editor, type KeyId, matchesKey, parseKittySequence } from "@gajae-code/tui";
|
|
2
2
|
import { BracketedPasteHandler } from "@gajae-code/tui/bracketed-paste";
|
|
3
|
-
import type
|
|
3
|
+
import { type AppKeybinding, KEYBINDINGS } from "../../config/keybindings";
|
|
4
4
|
|
|
5
5
|
type ConfigurableEditorAction = Extract<
|
|
6
6
|
AppKeybinding,
|
|
@@ -23,25 +23,35 @@ type ConfigurableEditorAction = Extract<
|
|
|
23
23
|
| "app.clipboard.copyPrompt"
|
|
24
24
|
>;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"app.
|
|
31
|
-
"app.
|
|
32
|
-
"app.
|
|
33
|
-
"app.
|
|
34
|
-
"app.
|
|
35
|
-
"app.model.
|
|
36
|
-
"app.
|
|
37
|
-
"app.
|
|
38
|
-
"app.
|
|
39
|
-
"app.
|
|
40
|
-
"app.
|
|
41
|
-
"app.
|
|
42
|
-
"app.
|
|
43
|
-
"app.
|
|
44
|
-
|
|
26
|
+
// Editor-configurable app actions. Defaults are derived from the central
|
|
27
|
+
// KEYBINDINGS registry so there is a single source of truth (e.g. the
|
|
28
|
+
// platform-aware app.clipboard.pasteImage default is not duplicated here).
|
|
29
|
+
const CONFIGURABLE_EDITOR_ACTIONS = [
|
|
30
|
+
"app.interrupt",
|
|
31
|
+
"app.clear",
|
|
32
|
+
"app.exit",
|
|
33
|
+
"app.suspend",
|
|
34
|
+
"app.thinking.cycle",
|
|
35
|
+
"app.model.cycleForward",
|
|
36
|
+
"app.model.cycleBackward",
|
|
37
|
+
"app.model.select",
|
|
38
|
+
"app.model.selectTemporary",
|
|
39
|
+
"app.tools.expand",
|
|
40
|
+
"app.thinking.toggle",
|
|
41
|
+
"app.editor.external",
|
|
42
|
+
"app.history.search",
|
|
43
|
+
"app.message.queue",
|
|
44
|
+
"app.message.dequeue",
|
|
45
|
+
"app.clipboard.pasteImage",
|
|
46
|
+
"app.clipboard.copyPrompt",
|
|
47
|
+
] as const satisfies readonly ConfigurableEditorAction[];
|
|
48
|
+
|
|
49
|
+
const DEFAULT_ACTION_KEYS = Object.fromEntries(
|
|
50
|
+
CONFIGURABLE_EDITOR_ACTIONS.map(action => {
|
|
51
|
+
const defaultKeys = KEYBINDINGS[action].defaultKeys;
|
|
52
|
+
return [action, Array.isArray(defaultKeys) ? [...defaultKeys] : [defaultKeys]];
|
|
53
|
+
}),
|
|
54
|
+
) as Record<ConfigurableEditorAction, KeyId[]>;
|
|
45
55
|
|
|
46
56
|
const PASTE_DECISION_TIMEOUT_MS = 5_000;
|
|
47
57
|
const PENDING_PASTE_INPUT_MAX = 64;
|
|
@@ -13,6 +13,8 @@ export interface LspServerInfo {
|
|
|
13
13
|
fileTypes: string[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
export type WelcomeLogoMode = "unicode" | "square" | "ascii";
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* GJC-native launch surface with compact command affordances, project
|
|
18
20
|
* signals, and a claw/talon mark without copying another agent shell.
|
|
@@ -27,6 +29,7 @@ export class WelcomeComponent implements Component {
|
|
|
27
29
|
private providerName: string,
|
|
28
30
|
private recentSessions: RecentSession[] = [],
|
|
29
31
|
private lspServers: LspServerInfo[] = [],
|
|
32
|
+
private readonly logoMode: WelcomeLogoMode = "unicode",
|
|
30
33
|
) {}
|
|
31
34
|
|
|
32
35
|
invalidate(): void {}
|
|
@@ -83,7 +86,8 @@ export class WelcomeComponent implements Component {
|
|
|
83
86
|
const minRightCol = 20;
|
|
84
87
|
const modelPill = this.#pill(theme.icon.model || "model", this.modelName, "statusLineModel");
|
|
85
88
|
const providerPill = this.#pill(theme.icon.package || "provider", this.providerName, "statusLinePath");
|
|
86
|
-
const
|
|
89
|
+
const logoLines = this.#logoLines();
|
|
90
|
+
const logoMinWidth = Math.max(...logoLines.map(line => visibleWidth(line)));
|
|
87
91
|
const leftMinContentWidth = Math.max(
|
|
88
92
|
minLeftCol,
|
|
89
93
|
logoMinWidth,
|
|
@@ -102,8 +106,7 @@ export class WelcomeComponent implements Component {
|
|
|
102
106
|
const leftCol = showRightColumn ? dualLeftCol : boxWidth - 2;
|
|
103
107
|
const rightCol = showRightColumn ? dualRightCol : 0;
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
const logoColored = this.#currentLogoFrame();
|
|
109
|
+
const logoColored = this.#currentLogoFrame(logoLines);
|
|
107
110
|
|
|
108
111
|
// Left column - centered content
|
|
109
112
|
const leftLines = [
|
|
@@ -258,10 +261,10 @@ export class WelcomeComponent implements Component {
|
|
|
258
261
|
}
|
|
259
262
|
|
|
260
263
|
/** Pick the logo frame for the current intro phase, or the resting frame. */
|
|
261
|
-
#currentLogoFrame(): readonly string[] {
|
|
262
|
-
if (this.#animStart == null) return
|
|
264
|
+
#currentLogoFrame(logoLines: readonly string[]): readonly string[] {
|
|
265
|
+
if (this.#animStart == null) return REST_FRAMES[this.logoMode];
|
|
263
266
|
const elapsed = performance.now() - this.#animStart;
|
|
264
|
-
if (elapsed >= INTRO_MS) return
|
|
267
|
+
if (elapsed >= INTRO_MS) return REST_FRAMES[this.logoMode];
|
|
265
268
|
// Ease-out cubic so the spin decelerates into the resting state.
|
|
266
269
|
const progress = elapsed / INTRO_MS;
|
|
267
270
|
const eased = 1 - (1 - progress) ** 3;
|
|
@@ -273,7 +276,13 @@ export class WelcomeComponent implements Component {
|
|
|
273
276
|
// the same ease-out curve so the highlight is gone by the resting frame.
|
|
274
277
|
const shinePos = (((progress * INTRO_SHINE_TRAVERSALS) % 1) + 1) % 1;
|
|
275
278
|
const shineStrength = (1 - eased) ** 1.5;
|
|
276
|
-
return gradientLogo(
|
|
279
|
+
return gradientLogo(logoLines, phase, { strength: shineStrength, pos: shinePos });
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
#logoLines(): readonly string[] {
|
|
283
|
+
if (this.logoMode === "ascii") return ASCII_CLAW_LOGO;
|
|
284
|
+
if (this.logoMode === "square") return SQUARE_CLAW_LOGO;
|
|
285
|
+
return RED_CLAW_LOGO;
|
|
277
286
|
}
|
|
278
287
|
}
|
|
279
288
|
|
|
@@ -287,6 +296,26 @@ const RED_CLAW_LOGO = [
|
|
|
287
296
|
"╰────────────────╯ ╰────────╯",
|
|
288
297
|
];
|
|
289
298
|
|
|
299
|
+
// biome-ignore format: preserve ASCII art layout
|
|
300
|
+
const SQUARE_CLAW_LOGO = [
|
|
301
|
+
"┌────────────────┐ ┌────────┐",
|
|
302
|
+
"└──────┐ ┌──┘ ┌──┘ ┌─────┘",
|
|
303
|
+
" └──────┘ ┌───┘ ┌──┘ ",
|
|
304
|
+
" ┌──────┐ └───┐ └──┐ ",
|
|
305
|
+
"┌──────┘ └──┐ └──┐ └─────┐",
|
|
306
|
+
"└────────────────┘ └────────┘",
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
// biome-ignore format: preserve ASCII art layout
|
|
310
|
+
const ASCII_CLAW_LOGO = [
|
|
311
|
+
"+----------------+ +--------+",
|
|
312
|
+
"+------+ +--+ +--+ +-----+",
|
|
313
|
+
" +------+ +---+ +--+ ",
|
|
314
|
+
" +------+ +---+ +--+ ",
|
|
315
|
+
"+------+ +--+ +--+ +-----+",
|
|
316
|
+
"+----------------+ +--------+",
|
|
317
|
+
];
|
|
318
|
+
|
|
290
319
|
/** Multi-stop palette for the red-claw diagonal gradient. */
|
|
291
320
|
const GRADIENT_STOPS: ReadonlyArray<readonly [number, number, number]> = [
|
|
292
321
|
[127, 29, 29], // deep shell red
|
|
@@ -385,5 +414,9 @@ const INTRO_SWEEPS = 2.5;
|
|
|
385
414
|
/** Number of times the shine highlight crosses the diagonal across the intro. */
|
|
386
415
|
const INTRO_SHINE_TRAVERSALS = 3;
|
|
387
416
|
|
|
388
|
-
/** Resting gradient
|
|
389
|
-
const
|
|
417
|
+
/** Resting gradient frames, cached for re-renders outside of the intro. */
|
|
418
|
+
const REST_FRAMES: Record<WelcomeLogoMode, readonly string[]> = {
|
|
419
|
+
unicode: gradientLogo(RED_CLAW_LOGO, 0),
|
|
420
|
+
square: gradientLogo(SQUARE_CLAW_LOGO, 0),
|
|
421
|
+
ascii: gradientLogo(ASCII_CLAW_LOGO, 0),
|
|
422
|
+
};
|
|
@@ -199,6 +199,9 @@ export class InputController {
|
|
|
199
199
|
this.ctx.editor.onDequeue = () => this.handleDequeue();
|
|
200
200
|
this.ctx.editor.setActionKeys("app.message.queue", this.ctx.keybindings.getKeys("app.message.queue"));
|
|
201
201
|
this.ctx.editor.onQueue = () => void this.handleQueueSubmit();
|
|
202
|
+
this.ctx.editor.onTabDeclined = () => {
|
|
203
|
+
if (this.ctx.session.isStreaming) void this.handleQueueSubmit();
|
|
204
|
+
};
|
|
202
205
|
|
|
203
206
|
this.ctx.editor.clearCustomKeyHandlers();
|
|
204
207
|
// Wire up extension shortcuts
|
|
@@ -228,7 +231,11 @@ export class InputController {
|
|
|
228
231
|
});
|
|
229
232
|
}
|
|
230
233
|
for (const key of this.ctx.keybindings.getKeys("app.message.followUp")) {
|
|
231
|
-
this.ctx.editor.setCustomKeyHandler(key, () =>
|
|
234
|
+
this.ctx.editor.setCustomKeyHandler(key, () => {
|
|
235
|
+
if (!this.#isFollowUpShortcutActive()) return false;
|
|
236
|
+
void this.handleFollowUp();
|
|
237
|
+
return true;
|
|
238
|
+
});
|
|
232
239
|
}
|
|
233
240
|
for (const key of this.ctx.keybindings.getKeys("app.stt.toggle")) {
|
|
234
241
|
this.ctx.editor.setCustomKeyHandler(key, () => void this.ctx.handleSTTToggle());
|
|
@@ -503,6 +510,15 @@ export class InputController {
|
|
|
503
510
|
return this.ctx.settings.get("busyPromptMode") === "steer" ? "steer" : "followUp";
|
|
504
511
|
}
|
|
505
512
|
|
|
513
|
+
#isFollowUpShortcutActive(): boolean {
|
|
514
|
+
return (
|
|
515
|
+
this.ctx.session.isStreaming ||
|
|
516
|
+
this.ctx.session.isCompacting ||
|
|
517
|
+
this.ctx.session.isBashRunning ||
|
|
518
|
+
this.ctx.session.isEvalRunning
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
506
522
|
/**
|
|
507
523
|
* Dispatch skill slash invocation(s) (`/skill:<name>`) through custom messages
|
|
508
524
|
* using the supplied `streamingBehavior`. Returns true if the text was a
|
|
@@ -824,8 +840,9 @@ export class InputController {
|
|
|
824
840
|
this.ctx.ui.requestRender();
|
|
825
841
|
return true;
|
|
826
842
|
}
|
|
827
|
-
|
|
828
|
-
|
|
843
|
+
this.ctx.showStatus(
|
|
844
|
+
"No image in clipboard. Use #paste-image, paste a copied image, or attach an image file with @path/to/image.png.",
|
|
845
|
+
);
|
|
829
846
|
return false;
|
|
830
847
|
} catch {
|
|
831
848
|
this.ctx.showStatus("Failed to read clipboard");
|
|
@@ -840,6 +857,7 @@ export class InputController {
|
|
|
840
857
|
keybindings: this.ctx.keybindings,
|
|
841
858
|
copyCurrentLine: () => this.handleCopyCurrentLine(),
|
|
842
859
|
copyPrompt: () => this.handleCopyPrompt(),
|
|
860
|
+
pasteImage: () => void this.handleImagePaste(),
|
|
843
861
|
undo: prefix => this.ctx.editor.undoPastTransientText(prefix),
|
|
844
862
|
moveCursorToMessageEnd: () => this.ctx.editor.moveToMessageEnd(),
|
|
845
863
|
moveCursorToMessageStart: () => this.ctx.editor.moveToMessageStart(),
|
|
@@ -86,7 +86,11 @@ import type { HookInputComponent } from "./components/hook-input";
|
|
|
86
86
|
import type { HookSelectorComponent } from "./components/hook-selector";
|
|
87
87
|
import { StatusLineComponent } from "./components/status-line";
|
|
88
88
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
89
|
-
import {
|
|
89
|
+
import {
|
|
90
|
+
WelcomeComponent,
|
|
91
|
+
type WelcomeLogoMode,
|
|
92
|
+
type LspServerInfo as WelcomeLspServerInfo,
|
|
93
|
+
} from "./components/welcome";
|
|
90
94
|
import { BtwController } from "./controllers/btw-controller";
|
|
91
95
|
import { CommandController } from "./controllers/command-controller";
|
|
92
96
|
import { EventController } from "./controllers/event-controller";
|
|
@@ -218,6 +222,21 @@ function parseGoalSubcommand(args: string): { sub: GoalSubcommand | undefined; r
|
|
|
218
222
|
return { sub: undefined, rest: trimmed };
|
|
219
223
|
}
|
|
220
224
|
|
|
225
|
+
export type WelcomeBannerSettingMode = "auto" | "unicode" | "square" | "ascii";
|
|
226
|
+
|
|
227
|
+
export function resolveWelcomeLogoMode(
|
|
228
|
+
mode: WelcomeBannerSettingMode,
|
|
229
|
+
env: Record<string, string | undefined> = Bun.env,
|
|
230
|
+
platform: NodeJS.Platform = process.platform,
|
|
231
|
+
): WelcomeLogoMode {
|
|
232
|
+
void env;
|
|
233
|
+
void platform;
|
|
234
|
+
if (mode === "unicode") return "unicode";
|
|
235
|
+
if (mode === "square") return "square";
|
|
236
|
+
if (mode === "ascii") return "ascii";
|
|
237
|
+
return "unicode";
|
|
238
|
+
}
|
|
239
|
+
|
|
221
240
|
/** Options for creating an InteractiveMode instance (for future API use) */
|
|
222
241
|
export interface InteractiveModeOptions {
|
|
223
242
|
/** Providers that were migrated during startup */
|
|
@@ -479,6 +498,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
479
498
|
);
|
|
480
499
|
|
|
481
500
|
const startupQuiet = settings.get("startup.quiet");
|
|
501
|
+
const welcomeLogoMode = resolveWelcomeLogoMode(settings.get("startup.welcomeBannerMode"));
|
|
482
502
|
this.#welcomeComponent = undefined;
|
|
483
503
|
|
|
484
504
|
for (const warning of this.session.configWarnings) {
|
|
@@ -494,6 +514,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
494
514
|
providerName,
|
|
495
515
|
recentSessions,
|
|
496
516
|
this.#getWelcomeLspServers(),
|
|
517
|
+
welcomeLogoMode,
|
|
497
518
|
);
|
|
498
519
|
|
|
499
520
|
// Setup UI layout
|
|
@@ -28,6 +28,7 @@ interface PromptActionAutocompleteOptions {
|
|
|
28
28
|
keybindings: KeybindingsManager;
|
|
29
29
|
copyCurrentLine: () => void;
|
|
30
30
|
copyPrompt: () => void;
|
|
31
|
+
pasteImage: () => void;
|
|
31
32
|
undo: (prefix: string) => void;
|
|
32
33
|
moveCursorToMessageEnd: () => void;
|
|
33
34
|
moveCursorToMessageStart: () => void;
|
|
@@ -190,7 +191,9 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
190
191
|
const query = promptActionPrefix.slice(1).toLowerCase();
|
|
191
192
|
const items = this.#actions
|
|
192
193
|
.map(action => {
|
|
193
|
-
const searchable = [action.label, action.description, ...action.keywords]
|
|
194
|
+
const searchable = [action.id, action.label, action.description, ...action.keywords]
|
|
195
|
+
.join(" ")
|
|
196
|
+
.toLowerCase();
|
|
194
197
|
if (!fuzzyMatch(query, searchable)) return null;
|
|
195
198
|
return {
|
|
196
199
|
value: action.label,
|
|
@@ -368,6 +371,13 @@ export function createPromptActionAutocompleteProvider(
|
|
|
368
371
|
keywords: ["copy", "prompt", "clipboard", "message"],
|
|
369
372
|
execute: options.copyPrompt,
|
|
370
373
|
},
|
|
374
|
+
{
|
|
375
|
+
id: "paste-image",
|
|
376
|
+
label: "Paste image from clipboard",
|
|
377
|
+
description: formatKeyHints(options.keybindings.getKeys("app.clipboard.pasteImage")),
|
|
378
|
+
keywords: ["paste", "image", "clipboard", "screenshot", "attach", "vision"],
|
|
379
|
+
execute: options.pasteImage,
|
|
380
|
+
},
|
|
371
381
|
{
|
|
372
382
|
id: "undo",
|
|
373
383
|
label: "Undo",
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
* - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import * as path from "node:path";
|
|
15
14
|
import { $pickenv, logger, readLines, Snowflake } from "@gajae-code/utils";
|
|
16
15
|
import type {
|
|
17
16
|
ExtensionUIContext,
|
|
18
17
|
ExtensionUIDialogOptions,
|
|
19
18
|
ExtensionWidgetOptions,
|
|
20
19
|
} from "../../extensibility/extensions";
|
|
20
|
+
import { workflowGatePath } from "../../gjc-runtime/session-layout";
|
|
21
21
|
import { type Theme, theme } from "../../modes/theme/theme";
|
|
22
22
|
import type { AgentSession } from "../../session/agent-session";
|
|
23
23
|
import { initializeExtensions } from "../runtime-init";
|
|
@@ -336,7 +336,7 @@ export async function runRpcMode(
|
|
|
336
336
|
// Unattended control plane (#318/#319/#323/G011): routes negotiate_unattended +
|
|
337
337
|
// workflow_gate_response and lets skill runtimes emit gates over RPC.
|
|
338
338
|
const gateStore = new FileGateStore(
|
|
339
|
-
|
|
339
|
+
workflowGatePath(session.sessionManager.getCwd(), session.sessionId, session.sessionId),
|
|
340
340
|
);
|
|
341
341
|
const unattendedControlPlane = new UnattendedSessionControlPlane({
|
|
342
342
|
runId: session.sessionId,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, writeSync } from "node:fs";
|
|
14
14
|
import * as path from "node:path";
|
|
15
|
+
import { sessionAuditDir } from "../../../gjc-runtime/session-layout";
|
|
15
16
|
import type { RpcBudgetExceeded, RpcWorkflowGateKind, RpcWorkflowStage } from "../../rpc/rpc-types";
|
|
16
17
|
import { answerHashOf } from "./workflow-gate-schema";
|
|
17
18
|
|
|
@@ -69,9 +70,9 @@ function defaultId(): string {
|
|
|
69
70
|
return `ae_${Date.now().toString(36)}_${idCounter.toString(36)}`;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
export function defaultAuditPath(runId: string, root = process.cwd()): string {
|
|
73
|
+
export function defaultAuditPath(runId: string, root = process.cwd(), gjcSessionId = runId): string {
|
|
73
74
|
const safe = runId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
74
|
-
return path.join(root,
|
|
75
|
+
return path.join(sessionAuditDir(root, gjcSessionId), "unattended", `${safe}.jsonl`);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
/** Append-only audit log writer + reader for one unattended run. */
|
|
@@ -5,7 +5,7 @@ thinking-level: medium
|
|
|
5
5
|
hide: true
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Generate AGENTS.md by launching multiple
|
|
8
|
+
Generate AGENTS.md by launching multiple canonical role agents in parallel (via `task` tool, usually `planner` or `architect`) scanning different areas (core src, tests, configs/build, scripts/docs), then synthesize findings into a single file.
|
|
9
9
|
|
|
10
10
|
<structure>
|
|
11
11
|
- **Project Overview**: Brief description of project purpose
|
|
@@ -82,7 +82,7 @@ The plan MUST be scannable yet detailed enough to execute.
|
|
|
82
82
|
|
|
83
83
|
<procedure>
|
|
84
84
|
### Phase 1: Understand
|
|
85
|
-
You MUST focus on the request and associated code. You SHOULD launch parallel
|
|
85
|
+
You MUST focus on the request and associated code. You SHOULD launch parallel canonical role agents (`planner` or `architect`) when scope spans multiple areas.
|
|
86
86
|
|
|
87
87
|
### Phase 2: Design
|
|
88
88
|
You MUST draft an approach based on exploration. You MUST consider trade-offs briefly, then choose.
|
|
@@ -38,5 +38,5 @@ Performs structural code search using AST matching via native ast-grep.
|
|
|
38
38
|
<critical>
|
|
39
39
|
- Avoid repo-root scans — narrow `paths` first
|
|
40
40
|
- Parse issues are query failure, not evidence of absence: repair the pattern or tighten `paths` before concluding "no matches"
|
|
41
|
-
- For broad/open-ended
|
|
41
|
+
- For broad/open-ended inspection across subsystems, delegate a bounded fact-finding task to an appropriate canonical role agent (`planner` or `architect`) first
|
|
42
42
|
</critical>
|
|
@@ -21,5 +21,5 @@ Searches files using powerful regex matching.
|
|
|
21
21
|
- You MUST use the built-in `search` tool for any content search. NEVER shell out to `grep`, `rg`, `ripgrep`, `ag`, `ack`, `git grep`, `awk`, `sed`-for-search, or any other CLI search via Bash — even for a single match, even "just to check quickly", even piped through other commands.
|
|
22
22
|
- Bash `grep`/`rg` loses `.gitignore` semantics, bypasses result limits, and wastes tokens. The `search` tool is faster, structured, and already wired into the workspace — there is no scenario where Bash search is preferable.
|
|
23
23
|
- If you catch yourself typing `grep`, `rg`, or `| grep` in a Bash command, stop and re-issue the lookup through the `search` tool instead.
|
|
24
|
-
- If the search is open-ended
|
|
24
|
+
- If the search is open-ended and requires multiple rounds across subsystems, delegate a bounded fact-finding task to an appropriate canonical role agent (`planner` for sequencing/context maps or `architect` for read-only architecture assessment) instead of chaining broad `search` calls yourself.
|
|
25
25
|
</critical>
|
|
@@ -28,13 +28,12 @@ Subagents have no conversation history. Every fact, file path, and direction the
|
|
|
28
28
|
{{/if}}
|
|
29
29
|
{{#if independentMode}}- `.inheritContext`: independent mode cannot inherit parent conversation. Omit it or set `"none"`; any non-`none` value is rejected before scheduling.{{/if}}
|
|
30
30
|
{{#if customSchemaEnabled}}- `schema`: JTD schema for expected structured output (do not put format rules in assignments){{/if}}
|
|
31
|
-
- `spawnPlan` (optional): required before any batch with more than 4 tasks
|
|
31
|
+
- `spawnPlan` (optional): required before any batch with more than 4 tasks; include whyParallel, whyNotLocal, independence, expectedReceiptShape, and maxInlineTokens.
|
|
32
32
|
{{#if isolationEnabled}}- `isolated`: run in isolated env; use when tasks edit overlapping files{{/if}}
|
|
33
33
|
</parameters>
|
|
34
34
|
|
|
35
35
|
<rules>
|
|
36
36
|
- HARD runtime gate: calls with more than 4 tasks are rejected before any child launches unless `spawnPlan` is complete.
|
|
37
|
-
- Reviewer->explore gate: a `reviewer` spawning `explore` is rejected before launch unless `spawnPlan` is complete, even for a single task.
|
|
38
37
|
- NEVER assign tasks to run project-wide build/test/lint. Caller verifies after the batch.
|
|
39
38
|
- **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates and formatters. You run them once at the end across the union of changed files — avoids redundant runs and racing formatter passes.
|
|
40
39
|
{{#if ircEnabled}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ledger";
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
export type ResearchPlanConfidence = "low" | "medium" | "high";
|
|
2
|
+
|
|
3
|
+
export type ResearchEvidenceVerdict = "support" | "contradict" | "uncertain";
|
|
4
|
+
|
|
5
|
+
export interface ResearchPlanItem {
|
|
6
|
+
claim: string;
|
|
7
|
+
confidence: ResearchPlanConfidence;
|
|
8
|
+
unknowns: string[];
|
|
9
|
+
evidenceNeeded: string[];
|
|
10
|
+
counterexampleQueries: string[];
|
|
11
|
+
sourceConflictPolicy: string;
|
|
12
|
+
dropCondition: string;
|
|
13
|
+
verifierChecks: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ResearchEvidenceEntry {
|
|
17
|
+
claim: string;
|
|
18
|
+
source: string;
|
|
19
|
+
confidence: ResearchPlanConfidence;
|
|
20
|
+
verdict: ResearchEvidenceVerdict;
|
|
21
|
+
notes?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ResearchLedgerVerdict {
|
|
25
|
+
claim: string;
|
|
26
|
+
finalVerdict: "accepted" | "rejected" | "uncertain";
|
|
27
|
+
survivingSources: ResearchEvidenceEntry[];
|
|
28
|
+
rejectReason?: string;
|
|
29
|
+
unresolvedUnknowns: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ResearchPlanValidationResult {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
errors: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const CONFIDENCE_VALUES = new Set<ResearchPlanConfidence>(["low", "medium", "high"]);
|
|
38
|
+
const EVIDENCE_VERDICTS = new Set<ResearchEvidenceVerdict>(["support", "contradict", "uncertain"]);
|
|
39
|
+
|
|
40
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
41
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateStringArray(value: unknown, field: string, minLength = 1): string[] {
|
|
45
|
+
if (!Array.isArray(value)) return [`${field} must be an array`];
|
|
46
|
+
if (value.length < minLength) return [`${field} must contain at least ${minLength} item(s)`];
|
|
47
|
+
return value.flatMap((item, index) =>
|
|
48
|
+
isNonEmptyString(item) ? [] : [`${field}[${index}] must be a non-empty string`],
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function validateResearchPlanItem(item: Partial<ResearchPlanItem>): ResearchPlanValidationResult {
|
|
53
|
+
const errors: string[] = [];
|
|
54
|
+
if (!isNonEmptyString(item.claim)) errors.push("claim must be a non-empty string");
|
|
55
|
+
if (!item.confidence || !CONFIDENCE_VALUES.has(item.confidence)) {
|
|
56
|
+
errors.push("confidence must be one of: low, medium, high");
|
|
57
|
+
}
|
|
58
|
+
errors.push(...validateStringArray(item.unknowns, "unknowns", 0));
|
|
59
|
+
errors.push(...validateStringArray(item.evidenceNeeded, "evidenceNeeded"));
|
|
60
|
+
errors.push(...validateStringArray(item.counterexampleQueries, "counterexampleQueries"));
|
|
61
|
+
if (!isNonEmptyString(item.sourceConflictPolicy)) errors.push("sourceConflictPolicy must be a non-empty string");
|
|
62
|
+
if (!isNonEmptyString(item.dropCondition)) errors.push("dropCondition must be a non-empty string");
|
|
63
|
+
errors.push(...validateStringArray(item.verifierChecks, "verifierChecks"));
|
|
64
|
+
return { valid: errors.length === 0, errors };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function validateResearchEvidenceEntry(entry: Partial<ResearchEvidenceEntry>): ResearchPlanValidationResult {
|
|
68
|
+
const errors: string[] = [];
|
|
69
|
+
if (!isNonEmptyString(entry.claim)) errors.push("claim must be a non-empty string");
|
|
70
|
+
if (!isNonEmptyString(entry.source)) errors.push("source must be a non-empty string");
|
|
71
|
+
if (!entry.confidence || !CONFIDENCE_VALUES.has(entry.confidence)) {
|
|
72
|
+
errors.push("confidence must be one of: low, medium, high");
|
|
73
|
+
}
|
|
74
|
+
if (!entry.verdict || !EVIDENCE_VERDICTS.has(entry.verdict)) {
|
|
75
|
+
errors.push("verdict must be one of: support, contradict, uncertain");
|
|
76
|
+
}
|
|
77
|
+
return { valid: errors.length === 0, errors };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function lower(value: string): string {
|
|
81
|
+
return value.toLowerCase();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function matchesDropCondition(item: ResearchPlanItem, evidence: ResearchEvidenceEntry[]): string | undefined {
|
|
85
|
+
const condition = lower(item.dropCondition);
|
|
86
|
+
const contradiction = evidence.find(entry => entry.verdict === "contradict");
|
|
87
|
+
if (contradiction && /(counterexample|contradict|conflict|falsif)/.test(condition)) {
|
|
88
|
+
return `dropCondition matched by contradictory source: ${contradiction.source}`;
|
|
89
|
+
}
|
|
90
|
+
const unresolved = evidence.find(entry => entry.verdict === "uncertain");
|
|
91
|
+
if (unresolved && /(unknown|unresolved|uncertain)/.test(condition)) {
|
|
92
|
+
return `dropCondition matched by unresolved evidence: ${unresolved.source}`;
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function sourceConflictReason(item: ResearchPlanItem, evidence: ResearchEvidenceEntry[]): string | undefined {
|
|
98
|
+
const supporting = evidence.filter(entry => entry.verdict === "support");
|
|
99
|
+
const contradicting = evidence.filter(entry => entry.verdict === "contradict");
|
|
100
|
+
if (supporting.length === 0 || contradicting.length === 0) return undefined;
|
|
101
|
+
const policy = lower(item.sourceConflictPolicy);
|
|
102
|
+
if (/(reject|drop|do not accept|prefer contradiction|requires resolution)/.test(policy)) {
|
|
103
|
+
return `sourceConflictPolicy rejected mixed support/contradiction (${supporting.length} support, ${contradicting.length} contradict)`;
|
|
104
|
+
}
|
|
105
|
+
return "source conflict remains unresolved";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function evaluateResearchLedger(
|
|
109
|
+
item: ResearchPlanItem,
|
|
110
|
+
evidence: readonly ResearchEvidenceEntry[],
|
|
111
|
+
): ResearchLedgerVerdict {
|
|
112
|
+
const relevantEvidence = evidence.filter(entry => entry.claim === item.claim);
|
|
113
|
+
const invalidItem = validateResearchPlanItem(item);
|
|
114
|
+
if (!invalidItem.valid) {
|
|
115
|
+
return {
|
|
116
|
+
claim: item.claim,
|
|
117
|
+
finalVerdict: "rejected",
|
|
118
|
+
survivingSources: [],
|
|
119
|
+
rejectReason: `invalid research plan item: ${invalidItem.errors.join("; ")}`,
|
|
120
|
+
unresolvedUnknowns: item.unknowns,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const invalidEvidence = relevantEvidence.flatMap(entry => validateResearchEvidenceEntry(entry).errors);
|
|
124
|
+
if (invalidEvidence.length > 0) {
|
|
125
|
+
return {
|
|
126
|
+
claim: item.claim,
|
|
127
|
+
finalVerdict: "rejected",
|
|
128
|
+
survivingSources: [],
|
|
129
|
+
rejectReason: `invalid evidence entry: ${invalidEvidence.join("; ")}`,
|
|
130
|
+
unresolvedUnknowns: item.unknowns,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (relevantEvidence.length === 0) {
|
|
134
|
+
return {
|
|
135
|
+
claim: item.claim,
|
|
136
|
+
finalVerdict: "uncertain",
|
|
137
|
+
survivingSources: [],
|
|
138
|
+
rejectReason: "no evidence collected for claim",
|
|
139
|
+
unresolvedUnknowns: item.unknowns,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const supporting = relevantEvidence.filter(entry => entry.verdict === "support");
|
|
143
|
+
const firstContradiction = relevantEvidence.find(entry => entry.verdict === "contradict");
|
|
144
|
+
let dropReason = matchesDropCondition(item, relevantEvidence) ?? sourceConflictReason(item, relevantEvidence);
|
|
145
|
+
// A counterexample with no surviving support falsifies the claim regardless of how the
|
|
146
|
+
// dropCondition / sourceConflictPolicy prose is worded. Without this, a purely contradicted
|
|
147
|
+
// claim would slip through as "uncertain" and reopen the hallucination survival path the
|
|
148
|
+
// evidence ledger exists to close (a contested claim already rejects via sourceConflictReason).
|
|
149
|
+
if (!dropReason && firstContradiction && supporting.length === 0) {
|
|
150
|
+
dropReason = `claim contradicted by counterexample with no supporting evidence: ${firstContradiction.source}`;
|
|
151
|
+
}
|
|
152
|
+
if (dropReason) {
|
|
153
|
+
return {
|
|
154
|
+
claim: item.claim,
|
|
155
|
+
finalVerdict: "rejected",
|
|
156
|
+
survivingSources: supporting,
|
|
157
|
+
rejectReason: dropReason,
|
|
158
|
+
unresolvedUnknowns: item.unknowns,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const uncertain = relevantEvidence.some(entry => entry.verdict === "uncertain");
|
|
162
|
+
if (uncertain || supporting.length === 0) {
|
|
163
|
+
return {
|
|
164
|
+
claim: item.claim,
|
|
165
|
+
finalVerdict: "uncertain",
|
|
166
|
+
survivingSources: supporting,
|
|
167
|
+
rejectReason: uncertain ? "unresolved uncertainty remains" : "no supporting evidence survived verification",
|
|
168
|
+
unresolvedUnknowns: item.unknowns,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
claim: item.claim,
|
|
173
|
+
finalVerdict: "accepted",
|
|
174
|
+
survivingSources: supporting,
|
|
175
|
+
unresolvedUnknowns: [],
|
|
176
|
+
};
|
|
177
|
+
}
|
package/src/rlm/artifacts.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* RLM session artifact layout under <cwd>/.gjc/rlm/<
|
|
2
|
+
* RLM session artifact layout under <cwd>/.gjc/_session-{gjcSessionId}/rlm/<rlmSessionId>/.
|
|
3
|
+
*
|
|
4
|
+
* The GJC session id (process boundary) scopes the directory; the RLM session id
|
|
5
|
+
* names the individual research run within it. The two ids are kept distinct.
|
|
3
6
|
*/
|
|
4
7
|
import * as fs from "node:fs/promises";
|
|
5
8
|
import * as path from "node:path";
|
|
6
9
|
import { readNotebookDocument } from "../edit/notebook";
|
|
10
|
+
import { rlmArtifactRoot } from "../gjc-runtime/session-layout";
|
|
11
|
+
import { resolveGjcSessionForWrite } from "../gjc-runtime/session-resolution";
|
|
7
12
|
import type { RlmArtifactPaths } from "./types";
|
|
8
13
|
|
|
9
|
-
export const RLM_DIR_SEGMENT =
|
|
14
|
+
export const RLM_DIR_SEGMENT = "rlm";
|
|
10
15
|
|
|
11
16
|
const SESSION_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
12
17
|
|
|
@@ -25,7 +30,11 @@ export function resolveRlmArtifactPaths(cwd: string, sessionId: string): RlmArti
|
|
|
25
30
|
if (!isValidRlmSessionId(sessionId)) {
|
|
26
31
|
throw new Error(`Invalid RLM session id: ${JSON.stringify(sessionId)}`);
|
|
27
32
|
}
|
|
28
|
-
const dir =
|
|
33
|
+
const dir = rlmArtifactRoot(
|
|
34
|
+
cwd,
|
|
35
|
+
resolveGjcSessionForWrite(cwd, { envSessionId: process.env.GJC_SESSION_ID }).gjcSessionId,
|
|
36
|
+
sessionId,
|
|
37
|
+
);
|
|
29
38
|
return {
|
|
30
39
|
dir,
|
|
31
40
|
notebookPath: path.join(dir, "notebook.ipynb"),
|
package/src/rlm/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { getProjectDir } from "@gajae-code/utils";
|
|
|
11
11
|
import { type Args, parseArgs } from "../cli/args";
|
|
12
12
|
import { disposeKernelSessionsByOwner } from "../eval/py/executor";
|
|
13
13
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
14
|
+
import { resolveSessionIdFromSources, writeSessionActivityMarker } from "../gjc-runtime/session-resolution";
|
|
14
15
|
import { type RlmPreset, runRootCommand } from "../main";
|
|
15
16
|
import rlmReportCommandPrompt from "../prompts/system/rlm-report-command.md" with { type: "text" };
|
|
16
17
|
import type { CreateAgentSessionOptions } from "../sdk";
|
|
@@ -231,6 +232,12 @@ async function writeRlmMetadata(input: {
|
|
|
231
232
|
successfulRuns: input.successfulRuns,
|
|
232
233
|
};
|
|
233
234
|
await Bun.write(input.paths.metadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
|
|
235
|
+
// Best-effort: update the per-session activity marker so latest-session auto-detect
|
|
236
|
+
// accounts for RLM-only generated output (AC2). Never let marker failure break RLM.
|
|
237
|
+
const gjcSessionId = resolveSessionIdFromSources({ envSessionId: process.env.GJC_SESSION_ID })?.gjcSessionId;
|
|
238
|
+
if (gjcSessionId) {
|
|
239
|
+
await writeSessionActivityMarker(input.cwd, gjcSessionId, { writer: "rlm" }).catch(() => {});
|
|
240
|
+
}
|
|
234
241
|
}
|
|
235
242
|
|
|
236
243
|
export async function runRlmCommand(argv: string[]): Promise<void> {
|