@gajae-code/coding-agent 0.9.2 → 0.9.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 +12 -0
- package/dist/types/capability/mcp.d.ts +3 -1
- package/dist/types/commands/mcp.d.ts +1 -1
- package/dist/types/modes/components/status-line/types.d.ts +6 -0
- package/dist/types/modes/components/status-line.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +12 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +4 -3
- package/dist/types/runtime-mcp/types.d.ts +5 -4
- package/dist/types/session/agent-session.d.ts +16 -0
- package/dist/types/session/session-manager.d.ts +1 -0
- package/dist/types/tools/skill.d.ts +9 -8
- package/dist/types/utils/linkedom.d.ts +4 -0
- package/dist/types/utils/pasted-image-path.d.ts +5 -3
- package/package.json +11 -7
- package/scripts/build-binary.ts +2 -40
- package/scripts/compile-args.ts +91 -0
- package/scripts/generate-docs-index.ts +1 -0
- package/src/capability/mcp.ts +3 -1
- package/src/cli/mcp-cli.ts +108 -26
- package/src/cli.ts +21 -0
- package/src/commands/mcp.ts +8 -8
- package/src/commands/stats.ts +6 -2
- package/src/config/mcp-schema.json +21 -17
- package/src/coordinator-mcp/server.ts +7 -4
- package/src/discovery/builtin.ts +14 -0
- package/src/discovery/mcp-json.ts +14 -0
- package/src/gjc-runtime/state-runtime.ts +105 -12
- package/src/gjc-runtime/workflow-manifest.generated.json +4 -28
- package/src/gjc-runtime/workflow-manifest.ts +1 -7
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/internal-urls/gjc-protocol.ts +14 -1
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/status-line/presets.ts +5 -7
- package/src/modes/components/status-line/segments.ts +14 -0
- package/src/modes/components/status-line/types.ts +6 -0
- package/src/modes/components/status-line.ts +7 -1
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/controllers/event-controller.ts +9 -9
- package/src/modes/controllers/input-controller.ts +2 -2
- package/src/modes/interactive-mode.ts +12 -4
- package/src/modes/utils/ui-helpers.ts +154 -32
- package/src/prompts/tools/skill.md +2 -2
- package/src/runtime-mcp/config-writer.ts +4 -3
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/types.ts +5 -4
- package/src/session/agent-session.ts +117 -3
- package/src/session/session-manager.ts +68 -40
- package/src/tools/bash-interactive.ts +9 -4
- package/src/tools/browser/readable.ts +2 -2
- package/src/tools/fetch.ts +5 -5
- package/src/tools/skill.ts +14 -11
- package/src/utils/linkedom.ts +14 -0
- package/src/utils/pasted-image-path.ts +23 -9
- package/src/web/scrapers/arxiv.ts +2 -2
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/iacr.ts +2 -2
- package/src/web/scrapers/readthedocs.ts +2 -2
- package/src/web/scrapers/twitter.ts +3 -2
- package/src/web/scrapers/wikipedia.ts +2 -2
- package/src/export/html/template.css +0 -1060
- package/src/export/html/template.html +0 -47
- package/src/export/html/template.js +0 -2314
- package/vendor/insane-search/engine/tests/test_hardening.py +0 -57
- package/vendor/insane-search/engine/tests/test_smoke.py +0 -152
- package/vendor/insane-search/engine/tests/test_u1.py +0 -200
- package/vendor/insane-search/engine/tests/test_u4.py +0 -131
- package/vendor/insane-search/engine/tests/test_u5.py +0 -163
- package/vendor/insane-search/engine/tests/test_u7.py +0 -124
- package/vendor/insane-search/engine/tests/test_u8.py +0 -216
|
@@ -26,7 +26,7 @@ import { interruptHint } from "../shared";
|
|
|
26
26
|
import { buildAbortDisplayMessage } from "../utils/abort-message";
|
|
27
27
|
import { consumeInjectedOptimisticSignature } from "../utils/injected-user-submission";
|
|
28
28
|
import { ringTerminalBell } from "../utils/terminal-bell";
|
|
29
|
-
import { argsWithPartialJson } from "../utils/ui-helpers";
|
|
29
|
+
import { addChatChild, argsWithPartialJson } from "../utils/ui-helpers";
|
|
30
30
|
|
|
31
31
|
type AgentSessionEventKind = AgentSessionEvent["type"];
|
|
32
32
|
|
|
@@ -169,12 +169,12 @@ export class EventController {
|
|
|
169
169
|
|
|
170
170
|
#getReadGroup(): ReadToolGroupComponent {
|
|
171
171
|
if (!this.#lastReadGroup) {
|
|
172
|
-
this.ctx
|
|
172
|
+
addChatChild(this.ctx, new Text("", 0, 0));
|
|
173
173
|
const group = new ReadToolGroupComponent({
|
|
174
174
|
showContentPreview: this.ctx.settings.get("read.toolResultPreview"),
|
|
175
175
|
});
|
|
176
176
|
group.setExpanded(this.ctx.toolOutputExpanded);
|
|
177
|
-
this.ctx
|
|
177
|
+
addChatChild(this.ctx, group);
|
|
178
178
|
this.#lastReadGroup = group;
|
|
179
179
|
}
|
|
180
180
|
return this.#lastReadGroup;
|
|
@@ -332,7 +332,7 @@ export class EventController {
|
|
|
332
332
|
this.ctx.ui.requestRender(),
|
|
333
333
|
);
|
|
334
334
|
this.ctx.streamingMessage = event.message;
|
|
335
|
-
this.ctx
|
|
335
|
+
addChatChild(this.ctx, this.ctx.streamingComponent);
|
|
336
336
|
this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage, { streaming: true });
|
|
337
337
|
this.ctx.ui.requestRender();
|
|
338
338
|
}
|
|
@@ -445,7 +445,7 @@ export class EventController {
|
|
|
445
445
|
);
|
|
446
446
|
if (!this.ctx.pendingTools.has(content.id)) {
|
|
447
447
|
this.#resetReadGroup();
|
|
448
|
-
this.ctx
|
|
448
|
+
addChatChild(this.ctx, new Text("", 0, 0));
|
|
449
449
|
const tool = this.ctx.session.getToolByName(content.name);
|
|
450
450
|
const component = new ToolExecutionComponent(
|
|
451
451
|
content.name,
|
|
@@ -462,8 +462,8 @@ export class EventController {
|
|
|
462
462
|
content.id,
|
|
463
463
|
);
|
|
464
464
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
465
|
-
this.ctx.chatContainer.addChild(component);
|
|
466
465
|
this.ctx.pendingTools.set(content.id, component);
|
|
466
|
+
addChatChild(this.ctx, component);
|
|
467
467
|
} else {
|
|
468
468
|
this.ctx.pendingTools.get(content.id)?.updateArgs(renderArgs, content.id);
|
|
469
469
|
}
|
|
@@ -575,8 +575,8 @@ export class EventController {
|
|
|
575
575
|
event.toolCallId,
|
|
576
576
|
);
|
|
577
577
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
578
|
-
this.ctx.chatContainer.addChild(component);
|
|
579
578
|
this.ctx.pendingTools.set(event.toolCallId, component);
|
|
579
|
+
addChatChild(this.ctx, component);
|
|
580
580
|
this.ctx.ui.requestRender();
|
|
581
581
|
}
|
|
582
582
|
}
|
|
@@ -869,13 +869,13 @@ export class EventController {
|
|
|
869
869
|
async #handleTtsrTriggered(event: Extract<AgentSessionEvent, { type: "ttsr_triggered" }>): Promise<void> {
|
|
870
870
|
const component = new TtsrNotificationComponent(event.rules);
|
|
871
871
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
872
|
-
this.ctx
|
|
872
|
+
addChatChild(this.ctx, component);
|
|
873
873
|
this.ctx.ui.requestRender();
|
|
874
874
|
}
|
|
875
875
|
|
|
876
876
|
async #handleTodoReminder(event: Extract<AgentSessionEvent, { type: "todo_reminder" }>): Promise<void> {
|
|
877
877
|
const component = new TodoReminderComponent(event.todos, event.attempt, event.maxAttempts);
|
|
878
|
-
this.ctx
|
|
878
|
+
addChatChild(this.ctx, component);
|
|
879
879
|
this.ctx.ui.requestRender();
|
|
880
880
|
}
|
|
881
881
|
|
|
@@ -18,7 +18,7 @@ import { copyToClipboard, readImageFromClipboard } from "../../utils/clipboard";
|
|
|
18
18
|
import { getEditorCommand, openInEditor } from "../../utils/external-editor";
|
|
19
19
|
import { ensureSupportedImageInput, ImageInputTooLargeError, loadImageInput } from "../../utils/image-loading";
|
|
20
20
|
import { resizeImage } from "../../utils/image-resize";
|
|
21
|
-
import { resolvePastedImagePath } from "../../utils/pasted-image-path";
|
|
21
|
+
import { formatPastedImageReference, resolvePastedImagePath } from "../../utils/pasted-image-path";
|
|
22
22
|
import { generateSessionTitle, setSessionTerminalTitle } from "../../utils/title-generator";
|
|
23
23
|
import { type QueuedMessageMoveDirection, QueuedMessageSelectorComponent } from "../components/queued-message-selector";
|
|
24
24
|
|
|
@@ -1096,7 +1096,7 @@ export class InputController {
|
|
|
1096
1096
|
data: image.data,
|
|
1097
1097
|
mimeType: image.mimeType,
|
|
1098
1098
|
});
|
|
1099
|
-
this.ctx.editor.insertText(`${this.#nextImagePlaceholder()} `);
|
|
1099
|
+
this.ctx.editor.insertText(`${formatPastedImageReference(this.#nextImagePlaceholder(), image.resolvedPath)} `);
|
|
1100
1100
|
this.ctx.showStatus(`Attached image: ${path.basename(image.resolvedPath)}`, { dim: true });
|
|
1101
1101
|
this.ctx.ui.requestRender();
|
|
1102
1102
|
return true;
|
|
@@ -18,6 +18,7 @@ import type { Component, EditorTheme, SlashCommand } from "@gajae-code/tui";
|
|
|
18
18
|
import {
|
|
19
19
|
Container,
|
|
20
20
|
clearRenderCache,
|
|
21
|
+
getRenderCacheRetainedBytes,
|
|
21
22
|
Loader,
|
|
22
23
|
Markdown,
|
|
23
24
|
ProcessTerminal,
|
|
@@ -114,7 +115,7 @@ import {
|
|
|
114
115
|
theme,
|
|
115
116
|
} from "./theme/theme";
|
|
116
117
|
import type { CompactionQueuedMessage, InteractiveModeContext, SubmittedUserInput, TodoItem, TodoPhase } from "./types";
|
|
117
|
-
import { UiHelpers } from "./utils/ui-helpers";
|
|
118
|
+
import { addChatChild, UiHelpers } from "./utils/ui-helpers";
|
|
118
119
|
|
|
119
120
|
const INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS = 5_000;
|
|
120
121
|
const COMPOSER_NEWLINE_HINT = process.platform === "win32" ? "Alt+Enter/Ctrl+J" : "Shift+Enter/Ctrl+J";
|
|
@@ -398,6 +399,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
398
399
|
) {
|
|
399
400
|
this.session = session;
|
|
400
401
|
this.sessionManager = session.sessionManager;
|
|
402
|
+
this.session.setRetainedMemorySampler(() => ({
|
|
403
|
+
tuiChatChildren: this.chatContainer.children.length,
|
|
404
|
+
tuiCachedRenderBytes: getRenderCacheRetainedBytes(),
|
|
405
|
+
}));
|
|
401
406
|
this.settings = session.settings;
|
|
402
407
|
this.keybindings = KeybindingsManager.inMemory();
|
|
403
408
|
this.agent = session.agent;
|
|
@@ -1514,8 +1519,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1514
1519
|
|
|
1515
1520
|
#renderPlanPreview(planContent: string, options?: { append?: boolean }): void {
|
|
1516
1521
|
const existingContainer = this.#planReviewContainer;
|
|
1517
|
-
|
|
1518
|
-
|
|
1522
|
+
// Only reuse the existing preview when it is still attached to the chat container;
|
|
1523
|
+
// a collapsed/disposed container must be re-added, not mutated in place.
|
|
1524
|
+
const stillAttached = existingContainer !== undefined && this.chatContainer.children.includes(existingContainer);
|
|
1525
|
+
const replaceExisting = options?.append !== true && stillAttached;
|
|
1526
|
+
const planReviewContainer = replaceExisting && existingContainer ? existingContainer : new Container();
|
|
1519
1527
|
planReviewContainer.clear();
|
|
1520
1528
|
planReviewContainer.addChild(new Spacer(1));
|
|
1521
1529
|
planReviewContainer.addChild(new DynamicBorder());
|
|
@@ -1524,7 +1532,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1524
1532
|
planReviewContainer.addChild(new Markdown(planContent, 1, 1, getMarkdownTheme()));
|
|
1525
1533
|
planReviewContainer.addChild(new DynamicBorder());
|
|
1526
1534
|
if (!replaceExisting) {
|
|
1527
|
-
this
|
|
1535
|
+
addChatChild(this, planReviewContainer);
|
|
1528
1536
|
}
|
|
1529
1537
|
this.#planReviewContainer = planReviewContainer;
|
|
1530
1538
|
this.ui.requestRender();
|
|
@@ -64,6 +64,127 @@ type QueuedMessages = {
|
|
|
64
64
|
followUp: string[];
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
const CHAT_CHILD_CAP = 400;
|
|
68
|
+
const CHAT_COLLAPSE_BATCH_SIZE = 300;
|
|
69
|
+
|
|
70
|
+
const chatChildAddedAt = new WeakMap<Component, number>();
|
|
71
|
+
|
|
72
|
+
export class CollapsedChatHistoryComponent implements Component {
|
|
73
|
+
readonly isCollapsedChatHistory = true;
|
|
74
|
+
count: number;
|
|
75
|
+
startTime: number;
|
|
76
|
+
endTime: number;
|
|
77
|
+
|
|
78
|
+
constructor(count: number, startTime: number, endTime: number) {
|
|
79
|
+
this.count = count;
|
|
80
|
+
this.startTime = startTime;
|
|
81
|
+
this.endTime = endTime;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
merge(count: number, startTime: number, endTime: number): void {
|
|
85
|
+
this.count += count;
|
|
86
|
+
this.startTime = Math.min(this.startTime, startTime);
|
|
87
|
+
this.endTime = Math.max(this.endTime, endTime);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
render(_width: number): string[] {
|
|
91
|
+
const label = `[${this.count} earlier messages collapsed — ${formatChatCollapseTime(this.startTime)} to ${formatChatCollapseTime(this.endTime)}]`;
|
|
92
|
+
return [theme.fg("dim", label)];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
invalidate(): void {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function formatChatCollapseTime(timestamp: number): string {
|
|
99
|
+
return new Date(timestamp).toLocaleTimeString(undefined, {
|
|
100
|
+
hour: "2-digit",
|
|
101
|
+
minute: "2-digit",
|
|
102
|
+
hour12: false,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isCollapsedChatHistory(component: Component): component is CollapsedChatHistoryComponent {
|
|
107
|
+
return component instanceof CollapsedChatHistoryComponent;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isActiveChatChild(ctx: InteractiveModeContext, component: Component): boolean {
|
|
111
|
+
if ([...ctx.pendingTools.values()].includes(component as never)) return true;
|
|
112
|
+
if (ctx.pendingBashComponents.includes(component as never)) return true;
|
|
113
|
+
if (ctx.pendingPythonComponents.includes(component as never)) return true;
|
|
114
|
+
return component === ctx.bashComponent || component === ctx.pythonComponent || component === ctx.streamingComponent;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getChatChildTime(component: Component): number {
|
|
118
|
+
return chatChildAddedAt.get(component) ?? Date.now();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function addChatChild(ctx: InteractiveModeContext, component: Component): void {
|
|
122
|
+
ctx.chatContainer.addChild(component);
|
|
123
|
+
chatChildAddedAt.set(component, Date.now());
|
|
124
|
+
trimChatChildren(ctx);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function trimChatChildren(ctx: InteractiveModeContext): void {
|
|
128
|
+
const children = ctx.chatContainer.children;
|
|
129
|
+
|
|
130
|
+
// Collapse the oldest completed children into history placeholders until the cap is
|
|
131
|
+
// satisfied or no further reduction is structurally possible. Active (pending/running)
|
|
132
|
+
// children and existing placeholders are never collapsed, so a conversation whose
|
|
133
|
+
// top-level children are dominated by active components may legitimately exceed the cap.
|
|
134
|
+
let scanFrom = 0;
|
|
135
|
+
while (children.length > CHAT_CHILD_CAP) {
|
|
136
|
+
// Advance to the oldest collapsible completed child, skipping active children and
|
|
137
|
+
// existing placeholders so the cap is enforced even when the oldest child is active.
|
|
138
|
+
while (
|
|
139
|
+
scanFrom < children.length &&
|
|
140
|
+
(isActiveChatChild(ctx, children[scanFrom]) || isCollapsedChatHistory(children[scanFrom]))
|
|
141
|
+
) {
|
|
142
|
+
scanFrom++;
|
|
143
|
+
}
|
|
144
|
+
if (scanFrom >= children.length) return; // only active/placeholder children remain
|
|
145
|
+
|
|
146
|
+
// Extend over the oldest contiguous run of completed children (up to the batch size).
|
|
147
|
+
let collapseEndExclusive = scanFrom;
|
|
148
|
+
const maxEnd = Math.min(children.length, scanFrom + CHAT_COLLAPSE_BATCH_SIZE);
|
|
149
|
+
while (
|
|
150
|
+
collapseEndExclusive < maxEnd &&
|
|
151
|
+
!isActiveChatChild(ctx, children[collapseEndExclusive]) &&
|
|
152
|
+
!isCollapsedChatHistory(children[collapseEndExclusive])
|
|
153
|
+
) {
|
|
154
|
+
collapseEndExclusive++;
|
|
155
|
+
}
|
|
156
|
+
const collapseCount = collapseEndExclusive - scanFrom;
|
|
157
|
+
|
|
158
|
+
const prev = scanFrom > 0 ? children[scanFrom - 1] : undefined;
|
|
159
|
+
const mergeTarget = prev && isCollapsedChatHistory(prev) ? prev : undefined;
|
|
160
|
+
// A lone completed child with no adjacent placeholder to merge into cannot reduce
|
|
161
|
+
// the child count (collapsing it yields a 1-count placeholder). Skip past it.
|
|
162
|
+
if (!mergeTarget && collapseCount <= 1) {
|
|
163
|
+
scanFrom += Math.max(collapseCount, 1);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const collapsed = children.slice(scanFrom, collapseEndExclusive);
|
|
168
|
+
const startTime = getChatChildTime(collapsed[0]);
|
|
169
|
+
const endTime = getChatChildTime(collapsed[collapsed.length - 1]);
|
|
170
|
+
for (const child of collapsed) {
|
|
171
|
+
child.dispose?.();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (mergeTarget) {
|
|
175
|
+
mergeTarget.merge(collapseCount, startTime, endTime);
|
|
176
|
+
children.splice(scanFrom, collapseCount);
|
|
177
|
+
chatChildAddedAt.set(mergeTarget, mergeTarget.startTime);
|
|
178
|
+
// scanFrom stays; whatever follows the merged run is next.
|
|
179
|
+
} else {
|
|
180
|
+
const placeholder = new CollapsedChatHistoryComponent(collapseCount, startTime, endTime);
|
|
181
|
+
children.splice(scanFrom, collapseCount, placeholder);
|
|
182
|
+
chatChildAddedAt.set(placeholder, startTime);
|
|
183
|
+
scanFrom++; // step past the freshly created placeholder
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
67
188
|
export class UiHelpers {
|
|
68
189
|
constructor(private ctx: InteractiveModeContext) {}
|
|
69
190
|
|
|
@@ -101,8 +222,8 @@ export class UiHelpers {
|
|
|
101
222
|
|
|
102
223
|
const spacer = new Spacer(1);
|
|
103
224
|
const text = new Text(rendered, 1, 0);
|
|
104
|
-
this.ctx
|
|
105
|
-
this.ctx
|
|
225
|
+
addChatChild(this.ctx, spacer);
|
|
226
|
+
addChatChild(this.ctx, text);
|
|
106
227
|
this.ctx.lastStatusSpacer = spacer;
|
|
107
228
|
this.ctx.lastStatusText = text;
|
|
108
229
|
this.ctx.ui.requestRender();
|
|
@@ -118,7 +239,7 @@ export class UiHelpers {
|
|
|
118
239
|
component.setComplete(message.exitCode, message.cancelled, {
|
|
119
240
|
truncation: message.meta?.truncation,
|
|
120
241
|
});
|
|
121
|
-
this.ctx
|
|
242
|
+
addChatChild(this.ctx, component);
|
|
122
243
|
break;
|
|
123
244
|
}
|
|
124
245
|
case "pythonExecution": {
|
|
@@ -129,7 +250,7 @@ export class UiHelpers {
|
|
|
129
250
|
component.setComplete(message.exitCode, message.cancelled, {
|
|
130
251
|
truncation: message.meta?.truncation,
|
|
131
252
|
});
|
|
132
|
-
this.ctx
|
|
253
|
+
addChatChild(this.ctx, component);
|
|
133
254
|
break;
|
|
134
255
|
}
|
|
135
256
|
case "hookMessage":
|
|
@@ -173,14 +294,14 @@ export class UiHelpers {
|
|
|
173
294
|
]
|
|
174
295
|
.filter(Boolean)
|
|
175
296
|
.join(" ");
|
|
176
|
-
this.ctx
|
|
297
|
+
addChatChild(this.ctx, new Text(line, 1, 0));
|
|
177
298
|
}
|
|
178
299
|
break;
|
|
179
300
|
}
|
|
180
301
|
if (message.customType === SKILL_PROMPT_MESSAGE_TYPE) {
|
|
181
302
|
const component = new SkillMessageComponent(message as CustomMessage<SkillPromptDetails>);
|
|
182
303
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
183
|
-
this.ctx
|
|
304
|
+
addChatChild(this.ctx, component);
|
|
184
305
|
break;
|
|
185
306
|
}
|
|
186
307
|
if (
|
|
@@ -217,12 +338,12 @@ export class UiHelpers {
|
|
|
217
338
|
const components: Component[] = [];
|
|
218
339
|
const header = `${theme.fg("accent", `[IRC] ${arrow}`)}`;
|
|
219
340
|
const headerComponent = new Text(header, 1, 0);
|
|
220
|
-
this.ctx
|
|
341
|
+
addChatChild(this.ctx, headerComponent);
|
|
221
342
|
components.push(headerComponent);
|
|
222
343
|
if (body) {
|
|
223
344
|
for (const line of body.split("\n")) {
|
|
224
345
|
const lineComponent = new Text(theme.fg("muted", ` ${line}`), 0, 0);
|
|
225
|
-
this.ctx
|
|
346
|
+
addChatChild(this.ctx, lineComponent);
|
|
226
347
|
components.push(lineComponent);
|
|
227
348
|
}
|
|
228
349
|
}
|
|
@@ -240,12 +361,12 @@ export class UiHelpers {
|
|
|
240
361
|
const components: Component[] = [];
|
|
241
362
|
const header = `${theme.fg("accent", `[Steer ${details?.state ?? "queued"}] ${details?.from ?? "?"} ⇨ ${details?.to ?? "?"}`)}`;
|
|
242
363
|
const headerComponent = new Text(header, 1, 0);
|
|
243
|
-
this.ctx
|
|
364
|
+
addChatChild(this.ctx, headerComponent);
|
|
244
365
|
components.push(headerComponent);
|
|
245
366
|
if (details?.body) {
|
|
246
367
|
for (const line of details.body.split("\n")) {
|
|
247
368
|
const lineComponent = new Text(theme.fg("muted", ` ${line}`), 0, 0);
|
|
248
|
-
this.ctx
|
|
369
|
+
addChatChild(this.ctx, lineComponent);
|
|
249
370
|
components.push(lineComponent);
|
|
250
371
|
}
|
|
251
372
|
}
|
|
@@ -255,22 +376,22 @@ export class UiHelpers {
|
|
|
255
376
|
// Both HookMessage and CustomMessage have the same structure, cast for compatibility
|
|
256
377
|
const component = new CustomMessageComponent(message as CustomMessage<unknown>, renderer);
|
|
257
378
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
258
|
-
this.ctx
|
|
379
|
+
addChatChild(this.ctx, component);
|
|
259
380
|
}
|
|
260
381
|
break;
|
|
261
382
|
}
|
|
262
383
|
case "compactionSummary": {
|
|
263
|
-
this.ctx
|
|
384
|
+
addChatChild(this.ctx, new Spacer(1));
|
|
264
385
|
const component = new CompactionSummaryMessageComponent(message);
|
|
265
386
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
266
|
-
this.ctx
|
|
387
|
+
addChatChild(this.ctx, component);
|
|
267
388
|
break;
|
|
268
389
|
}
|
|
269
390
|
case "branchSummary": {
|
|
270
|
-
this.ctx
|
|
391
|
+
addChatChild(this.ctx, new Spacer(1));
|
|
271
392
|
const component = new BranchSummaryMessageComponent(message);
|
|
272
393
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
273
|
-
this.ctx
|
|
394
|
+
addChatChild(this.ctx, component);
|
|
274
395
|
break;
|
|
275
396
|
}
|
|
276
397
|
case "fileMention": {
|
|
@@ -291,7 +412,7 @@ export class UiHelpers {
|
|
|
291
412
|
"accent",
|
|
292
413
|
file.path,
|
|
293
414
|
)} ${theme.fg("dim", suffix)}`;
|
|
294
|
-
this.ctx
|
|
415
|
+
addChatChild(this.ctx, new Text(text, 0, 0));
|
|
295
416
|
}
|
|
296
417
|
break;
|
|
297
418
|
}
|
|
@@ -301,7 +422,7 @@ export class UiHelpers {
|
|
|
301
422
|
if (textContent) {
|
|
302
423
|
const isSynthetic = message.role === "developer" ? true : (message.synthetic ?? false);
|
|
303
424
|
const userComponent = new UserMessageComponent(textContent, isSynthetic);
|
|
304
|
-
this.ctx
|
|
425
|
+
addChatChild(this.ctx, userComponent);
|
|
305
426
|
if (options?.populateHistory && message.role === "user" && !isSynthetic) {
|
|
306
427
|
this.ctx.editor.addToHistory(textContent);
|
|
307
428
|
}
|
|
@@ -312,7 +433,7 @@ export class UiHelpers {
|
|
|
312
433
|
const assistantComponent = new AssistantMessageComponent(message, this.ctx.hideThinkingBlock, () =>
|
|
313
434
|
this.ctx.ui.requestRender(),
|
|
314
435
|
);
|
|
315
|
-
this.ctx
|
|
436
|
+
addChatChild(this.ctx, assistantComponent);
|
|
316
437
|
break;
|
|
317
438
|
}
|
|
318
439
|
case "toolResult": {
|
|
@@ -392,7 +513,7 @@ export class UiHelpers {
|
|
|
392
513
|
showContentPreview: this.ctx.settings.get("read.toolResultPreview"),
|
|
393
514
|
});
|
|
394
515
|
readGroup.setExpanded(this.ctx.toolOutputExpanded);
|
|
395
|
-
this.ctx
|
|
516
|
+
addChatChild(this.ctx, readGroup);
|
|
396
517
|
}
|
|
397
518
|
readGroup.updateArgs(content.arguments, content.id);
|
|
398
519
|
readGroup.updateResult(
|
|
@@ -434,7 +555,7 @@ export class UiHelpers {
|
|
|
434
555
|
content.id,
|
|
435
556
|
);
|
|
436
557
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
437
|
-
this.ctx
|
|
558
|
+
addChatChild(this.ctx, component);
|
|
438
559
|
|
|
439
560
|
if (hasErrorStop && errorMessage) {
|
|
440
561
|
component.updateResult(
|
|
@@ -472,7 +593,7 @@ export class UiHelpers {
|
|
|
472
593
|
showContentPreview: this.ctx.settings.get("read.toolResultPreview"),
|
|
473
594
|
});
|
|
474
595
|
readGroup.setExpanded(this.ctx.toolOutputExpanded);
|
|
475
|
-
this.ctx
|
|
596
|
+
addChatChild(this.ctx, readGroup);
|
|
476
597
|
}
|
|
477
598
|
const args = readToolCallArgs.get(message.toolCallId);
|
|
478
599
|
if (args) {
|
|
@@ -539,7 +660,7 @@ export class UiHelpers {
|
|
|
539
660
|
}
|
|
540
661
|
if (preservedChatChildren && preservedChatChildren.length > 0) {
|
|
541
662
|
for (const child of preservedChatChildren) {
|
|
542
|
-
this.ctx
|
|
663
|
+
addChatChild(this.ctx, child);
|
|
543
664
|
}
|
|
544
665
|
this.ctx.ui.requestRender();
|
|
545
666
|
}
|
|
@@ -559,8 +680,8 @@ export class UiHelpers {
|
|
|
559
680
|
process.stderr.write(`Error: ${errorMessage}\n`);
|
|
560
681
|
return;
|
|
561
682
|
}
|
|
562
|
-
this.ctx
|
|
563
|
-
this.ctx
|
|
683
|
+
addChatChild(this.ctx, new Spacer(1));
|
|
684
|
+
addChatChild(this.ctx, new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0));
|
|
564
685
|
this.ctx.ui.requestRender();
|
|
565
686
|
}
|
|
566
687
|
|
|
@@ -569,15 +690,16 @@ export class UiHelpers {
|
|
|
569
690
|
process.stderr.write(`Warning: ${warningMessage}\n`);
|
|
570
691
|
return;
|
|
571
692
|
}
|
|
572
|
-
this.ctx
|
|
573
|
-
this.ctx
|
|
693
|
+
addChatChild(this.ctx, new Spacer(1));
|
|
694
|
+
addChatChild(this.ctx, new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
|
|
574
695
|
this.ctx.ui.requestRender();
|
|
575
696
|
}
|
|
576
697
|
|
|
577
698
|
showNewVersionNotification(newVersion: string): void {
|
|
578
|
-
this.ctx
|
|
579
|
-
this.ctx
|
|
580
|
-
|
|
699
|
+
addChatChild(this.ctx, new Spacer(1));
|
|
700
|
+
addChatChild(this.ctx, new DynamicBorder(text => theme.fg("warning", text)));
|
|
701
|
+
addChatChild(
|
|
702
|
+
this.ctx,
|
|
581
703
|
new Text(
|
|
582
704
|
theme.bold(theme.fg("warning", "Update Available")) +
|
|
583
705
|
"\n" +
|
|
@@ -587,7 +709,7 @@ export class UiHelpers {
|
|
|
587
709
|
0,
|
|
588
710
|
),
|
|
589
711
|
);
|
|
590
|
-
this.ctx
|
|
712
|
+
addChatChild(this.ctx, new DynamicBorder(text => theme.fg("warning", text)));
|
|
591
713
|
this.ctx.ui.requestRender();
|
|
592
714
|
}
|
|
593
715
|
|
|
@@ -853,12 +975,12 @@ export class UiHelpers {
|
|
|
853
975
|
// removeChild() would tear them down before re-adding.
|
|
854
976
|
for (const component of this.ctx.pendingBashComponents) {
|
|
855
977
|
this.ctx.pendingMessagesContainer.detachChild(component);
|
|
856
|
-
this.ctx
|
|
978
|
+
addChatChild(this.ctx, component);
|
|
857
979
|
}
|
|
858
980
|
this.ctx.pendingBashComponents = [];
|
|
859
981
|
for (const component of this.ctx.pendingPythonComponents) {
|
|
860
982
|
this.ctx.pendingMessagesContainer.detachChild(component);
|
|
861
|
-
this.ctx
|
|
983
|
+
addChatChild(this.ctx, component);
|
|
862
984
|
}
|
|
863
985
|
this.ctx.pendingPythonComponents = [];
|
|
864
986
|
}
|
|
@@ -8,8 +8,8 @@ Invoke another available skill in the current turn.
|
|
|
8
8
|
<instruction>
|
|
9
9
|
- `name` is the skill name as it appears in `/skill:<name>` (e.g. `ralplan`, `ultragoal`, `team`, `deep-interview`)
|
|
10
10
|
- `args` is the free-form argument string the skill would receive after `/skill:<name>` on the command line
|
|
11
|
-
- The tool loads the callee's SKILL.md into the current turn and handles
|
|
12
|
-
- The chain is refused while
|
|
11
|
+
- The tool loads the callee's SKILL.md into the current turn and handles native workflow caller→callee state handoff when the caller is one of the built-in GJC workflows.
|
|
12
|
+
- The chain is refused while a native workflow caller is still active. If your current skill is one of `deep-interview`, `ralplan`, `ultragoal`, or `team` and has not yet reached a terminal phase, prepare it first with `gjc state <skill> write --input '{"current_phase":"handoff"}' --json`; no other handoff command is needed. Runtime project/user skills do not use `gjc state <skill>`.
|
|
13
13
|
- Call once per chain step. To chain `A → B → C`, A calls `skill(B)`; B's next agent turn calls `skill(C)`.
|
|
14
14
|
</instruction>
|
|
15
15
|
|
|
@@ -238,10 +238,11 @@ export async function listMCPServers(filePath: string): Promise<string[]> {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/**
|
|
241
|
-
* Set a server's autoload flag
|
|
241
|
+
* Set a server's autoload flag for explicit runtime MCP consumers.
|
|
242
242
|
*
|
|
243
|
-
* Autoload defaults to true, so turning it on
|
|
244
|
-
* config file free of redundant fields; turning it
|
|
243
|
+
* Autoload defaults to true for consumers that honor it, so turning it on
|
|
244
|
+
* removes the key to keep the config file free of redundant fields; turning it
|
|
245
|
+
* off writes `autoload: false`.
|
|
245
246
|
*
|
|
246
247
|
* @throws Error if the server doesn't exist
|
|
247
248
|
*/
|
|
@@ -56,6 +56,7 @@ function convertToLegacyConfig(server: MCPServer): MCPServerConfig {
|
|
|
56
56
|
};
|
|
57
57
|
if (server.args) config.args = server.args;
|
|
58
58
|
if (server.env) config.env = server.env;
|
|
59
|
+
if (server.noInheritEnv !== undefined) config.noInheritEnv = server.noInheritEnv;
|
|
59
60
|
if (server.cwd) config.cwd = server.cwd;
|
|
60
61
|
return config;
|
|
61
62
|
}
|
package/src/runtime-mcp/types.ts
CHANGED
|
@@ -62,9 +62,10 @@ interface MCPServerConfigBase {
|
|
|
62
62
|
/** Whether this server is enabled (default: true) */
|
|
63
63
|
enabled?: boolean;
|
|
64
64
|
/**
|
|
65
|
-
* Whether
|
|
66
|
-
*
|
|
67
|
-
*
|
|
65
|
+
* Whether an explicit runtime MCP consumer should connect this server
|
|
66
|
+
* automatically when that consumer starts (default: true). Normal standalone
|
|
67
|
+
* `gjc`, `gjc --tmux`, and print-mode sessions do not consume `gjc mcp`
|
|
68
|
+
* registrations today.
|
|
68
69
|
*/
|
|
69
70
|
autoload?: boolean;
|
|
70
71
|
/** Connection timeout in milliseconds (default: 30000) */
|
|
@@ -114,7 +115,7 @@ export interface MCPSseServerConfig extends MCPServerConfigBase {
|
|
|
114
115
|
export type MCPServerConfig = MCPStdioServerConfig | MCPHttpServerConfig | MCPSseServerConfig;
|
|
115
116
|
|
|
116
117
|
export const MCP_CONFIG_SCHEMA_URL =
|
|
117
|
-
"https://raw.githubusercontent.com/
|
|
118
|
+
"https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/main/packages/coding-agent/src/config/mcp-schema.json";
|
|
118
119
|
|
|
119
120
|
/** Root mcp.json/.mcp.json file structure */
|
|
120
121
|
export interface MCPConfigFile {
|