@gajae-code/coding-agent 0.7.10 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +2 -2
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/config/keybindings.d.ts +11 -5
- package/dist/types/config/settings-schema.d.ts +25 -2
- package/dist/types/defaults/gjc-defaults.d.ts +7 -0
- package/dist/types/discovery/cursor.d.ts +4 -1
- package/dist/types/discovery/index.d.ts +0 -1
- package/dist/types/discovery/windsurf.d.ts +3 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +3 -2
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +1 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +5 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/skill-message.d.ts +1 -0
- package/dist/types/modes/components/status-line.d.ts +8 -0
- package/dist/types/modes/components/two-column-body.d.ts +9 -0
- package/dist/types/modes/components/welcome.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +5 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/types/modes/slash-command-visibility.d.ts +1 -0
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +9 -0
- package/dist/types/runtime-mcp/config.d.ts +2 -0
- package/dist/types/runtime-mcp/loader.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +8 -5
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +7 -0
- package/dist/types/utils/cmux-workspace.d.ts +30 -1
- package/dist/types/web/search/index.d.ts +3 -1
- package/dist/types/web/search/provider.d.ts +9 -0
- package/dist/types/web/search/providers/utils.d.ts +42 -13
- package/dist/types/workflow/workflow-intent-diff.d.ts +19 -0
- package/package.json +10 -10
- package/src/capability/mcp.ts +2 -0
- package/src/cli/update-cli.ts +24 -0
- package/src/cli/web-search-cli.ts +2 -5
- package/src/cli.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/ralplan.ts +1 -0
- package/src/config/keybindings.ts +14 -5
- package/src/config/mcp-schema.json +5 -1
- package/src/config/model-profiles.ts +16 -0
- package/src/config/settings-schema.ts +18 -2
- package/src/coordinator-mcp/server.ts +15 -3
- package/src/defaults/gjc/skills/ralplan/SKILL.md +16 -14
- package/src/defaults/gjc-defaults.ts +16 -0
- package/src/discovery/builtin.ts +12 -0
- package/src/discovery/cursor.ts +5 -84
- package/src/discovery/gemini.ts +3 -89
- package/src/discovery/index.ts +4 -1
- package/src/discovery/mcp-json.ts +11 -0
- package/src/discovery/opencode.ts +5 -122
- package/src/discovery/windsurf.ts +4 -82
- package/src/edit/read-file.ts +9 -1
- package/src/extensibility/extensions/runner.ts +2 -1
- package/src/gjc-runtime/launch-tmux.ts +164 -42
- package/src/gjc-runtime/ralplan-runtime.ts +22 -6
- package/src/gjc-runtime/restricted-role-agent-bash.ts +1 -0
- package/src/gjc-runtime/session-state-sidecar.ts +167 -24
- package/src/gjc-runtime/state-runtime.ts +8 -5
- package/src/gjc-runtime/state-writer.ts +5 -5
- package/src/gjc-runtime/team-runtime.ts +56 -51
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/main.ts +2 -2
- package/src/migrate/executor.ts +23 -6
- package/src/modes/components/agent-dashboard.ts +1 -34
- package/src/modes/components/custom-editor.ts +26 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -48
- package/src/modes/components/model-selector.ts +64 -4
- package/src/modes/components/session-selector.ts +19 -9
- package/src/modes/components/settings-selector.ts +13 -2
- package/src/modes/components/skill-message.ts +18 -10
- package/src/modes/components/status-line.ts +215 -86
- package/src/modes/components/tree-selector.ts +28 -8
- package/src/modes/components/two-column-body.ts +36 -0
- package/src/modes/components/welcome.ts +171 -35
- package/src/modes/controllers/command-controller.ts +23 -0
- package/src/modes/controllers/event-controller.ts +4 -0
- package/src/modes/controllers/input-controller.ts +192 -39
- package/src/modes/controllers/selector-controller.ts +25 -28
- package/src/modes/interactive-mode.ts +36 -30
- package/src/modes/prompt-action-autocomplete.ts +23 -1
- package/src/modes/rpc/rpc-types.ts +1 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +9 -3
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +20 -0
- package/src/modes/slash-command-visibility.ts +9 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/hotkeys-markdown.ts +5 -2
- package/src/modes/utils/ui-helpers.ts +76 -3
- package/src/notifications/index.ts +20 -1
- package/src/notifications/lifecycle-control-runtime.ts +12 -3
- package/src/notifications/telegram-daemon.ts +4 -4
- package/src/prompts/agents/architect.md +7 -4
- package/src/prompts/agents/critic.md +15 -6
- package/src/prompts/agents/planner.md +8 -3
- package/src/prompts/system/system-prompt.md +15 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/runtime-mcp/config-writer.ts +28 -0
- package/src/runtime-mcp/config.ts +7 -0
- package/src/runtime-mcp/loader.ts +3 -0
- package/src/runtime-mcp/manager.ts +3 -0
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk.ts +7 -6
- package/src/session/agent-session.ts +81 -18
- package/src/session/session-manager.ts +58 -18
- package/src/slash-commands/builtin-registry.ts +30 -10
- package/src/slash-commands/types.ts +2 -0
- package/src/system-prompt.ts +8 -0
- package/src/task/executor.ts +5 -0
- package/src/tools/ast-grep.ts +10 -7
- package/src/tools/bash.ts +39 -14
- package/src/tools/index.ts +8 -1
- package/src/utils/cmux-workspace.ts +134 -7
- package/src/utils/title-generator.ts +1 -1
- package/src/web/kagi.ts +1 -1
- package/src/web/parallel.ts +2 -2
- package/src/web/search/index.ts +118 -45
- package/src/web/search/provider.ts +65 -0
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/web/search/providers/brave.ts +1 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/gemini.ts +7 -3
- package/src/web/search/providers/jina.ts +1 -1
- package/src/web/search/providers/kimi.ts +6 -1
- package/src/web/search/providers/openai-compatible.ts +1 -1
- package/src/web/search/providers/parallel.ts +1 -1
- package/src/web/search/providers/perplexity.ts +2 -2
- package/src/web/search/providers/searxng.ts +1 -1
- package/src/web/search/providers/synthetic.ts +1 -1
- package/src/web/search/providers/tavily.ts +1 -1
- package/src/web/search/providers/utils.ts +67 -17
- package/src/web/search/providers/xai.ts +1 -1
- package/src/web/search/providers/zai.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +199 -0
- package/dist/types/discovery/vscode.d.ts +0 -1
- package/src/discovery/vscode.ts +0 -105
package/src/sdk.ts
CHANGED
|
@@ -119,6 +119,7 @@ import { AgentOutputManager } from "./task/output-manager";
|
|
|
119
119
|
import { parseThinkingLevel, resolveThinkingLevelForModel, toReasoningEffort } from "./thinking";
|
|
120
120
|
import { collectDiscoverableTools, type DiscoverableTool } from "./tool-discovery/tool-index";
|
|
121
121
|
import {
|
|
122
|
+
applyConfiguredSearchTimeout,
|
|
122
123
|
BashTool,
|
|
123
124
|
BUILTIN_TOOLS,
|
|
124
125
|
computeEssentialBuiltinNames,
|
|
@@ -140,7 +141,6 @@ import {
|
|
|
140
141
|
setPreferredImageProvider,
|
|
141
142
|
setPreferredSearchProvider,
|
|
142
143
|
setSearchFallbackProviders,
|
|
143
|
-
setSearchHardTimeoutMs,
|
|
144
144
|
type Tool,
|
|
145
145
|
type ToolSession,
|
|
146
146
|
WebSearchTool,
|
|
@@ -255,7 +255,7 @@ export interface CreateAgentSessionOptions {
|
|
|
255
255
|
thinkingLevel?: ThinkingLevel;
|
|
256
256
|
/** Runtime substitution metadata for the initial model_change session event. */
|
|
257
257
|
modelSubstitution?: { requestedModel: Model; reason: string };
|
|
258
|
-
/** Models available for cycling (
|
|
258
|
+
/** Models available for cycling (Alt+N in interactive mode) */
|
|
259
259
|
scopedModels?: ScopedModelSelection[];
|
|
260
260
|
|
|
261
261
|
/** System prompt blocks. Array replaces default, function receives default blocks and returns final blocks. */
|
|
@@ -940,10 +940,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
940
940
|
webSearchFallback.filter(value => typeof value === "string" && isConfigurableSearchProviderId(value)),
|
|
941
941
|
);
|
|
942
942
|
}
|
|
943
|
-
|
|
944
|
-
if (typeof webSearchTimeout === "number" && Number.isFinite(webSearchTimeout) && webSearchTimeout > 0) {
|
|
945
|
-
setSearchHardTimeoutMs(webSearchTimeout * 1000);
|
|
946
|
-
}
|
|
943
|
+
applyConfiguredSearchTimeout(settings);
|
|
947
944
|
|
|
948
945
|
const imageProvider = settings.get("providers.image");
|
|
949
946
|
if (
|
|
@@ -1819,6 +1816,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1819
1816
|
intentField,
|
|
1820
1817
|
mcpDiscoveryMode: false,
|
|
1821
1818
|
mcpDiscoveryServerSummaries: [],
|
|
1819
|
+
toolDiscoveryActive: effectiveDiscoveryMode === "all",
|
|
1820
|
+
discoverableTools: discoverableBuiltinTools
|
|
1821
|
+
.map(tool => ({ name: tool.name, summary: tool.summary }))
|
|
1822
|
+
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
1822
1823
|
eagerTasks,
|
|
1823
1824
|
secretsEnabled,
|
|
1824
1825
|
workspaceTree: workspaceTreePromise,
|
|
@@ -192,7 +192,10 @@ import {
|
|
|
192
192
|
modeStatePath as sessionModeStatePath,
|
|
193
193
|
sessionStateDir,
|
|
194
194
|
} from "../gjc-runtime/session-layout";
|
|
195
|
-
import {
|
|
195
|
+
import {
|
|
196
|
+
persistCoordinatorRuntimeStateFromEvent,
|
|
197
|
+
registerCoordinatorRuntimeStateFinalizer,
|
|
198
|
+
} from "../gjc-runtime/session-state-sidecar";
|
|
196
199
|
import { writeArtifact } from "../gjc-runtime/state-writer";
|
|
197
200
|
import { requestGjcWorkerIntegrationAttempt } from "../gjc-runtime/team-runtime";
|
|
198
201
|
import { GoalRuntime } from "../goals/runtime";
|
|
@@ -260,6 +263,7 @@ import { type EditMode, resolveEditMode } from "../utils/edit-mode";
|
|
|
260
263
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
261
264
|
import { extractFileMentions, generateFileMentionMessages } from "../utils/file-mentions";
|
|
262
265
|
import { buildNamedToolChoice, buildNamedToolChoiceResult } from "../utils/tool-choice";
|
|
266
|
+
import { buildWorkflowIntentDiff, WORKFLOW_INTENT_DIFF_CUSTOM_TYPE } from "../workflow/workflow-intent-diff";
|
|
263
267
|
import type { AuthStorage } from "./auth-storage";
|
|
264
268
|
import type { ClientBridge, ClientBridgePermissionOption, ClientBridgePermissionOutcome } from "./client-bridge";
|
|
265
269
|
import {
|
|
@@ -353,7 +357,7 @@ export interface AgentSessionConfig {
|
|
|
353
357
|
agent: Agent;
|
|
354
358
|
sessionManager: SessionManager;
|
|
355
359
|
settings: Settings;
|
|
356
|
-
/** Models to cycle through with
|
|
360
|
+
/** Models to cycle through with Alt+N (from --models flag) */
|
|
357
361
|
scopedModels?: ScopedModelSelection[];
|
|
358
362
|
/** Initial session thinking selector. */
|
|
359
363
|
thinkingLevel?: ThinkingLevel;
|
|
@@ -901,8 +905,9 @@ function extractPermissionLocations(
|
|
|
901
905
|
* `tag` is set only by `enqueueCustomMessageDisplay` (used for skill-prompt
|
|
902
906
|
* custom messages queued during streaming) and is matched by the custom-role
|
|
903
907
|
* `message_start` dequeue branch; user-message pushes leave it undefined and
|
|
904
|
-
* rely on the existing text-equality match.
|
|
905
|
-
|
|
908
|
+
* rely on the existing text-equality match. `sequence` preserves cross-queue
|
|
909
|
+
* insertion order for one-at-a-time dequeue/edit UX. */
|
|
910
|
+
type QueuedDisplayEntry = { text: string; tag?: string; sequence: number };
|
|
906
911
|
|
|
907
912
|
/** A custom message contributed at the before-agent-start point. */
|
|
908
913
|
export type BeforeAgentStartInternalMessage = Pick<
|
|
@@ -956,6 +961,7 @@ export class AgentSession {
|
|
|
956
961
|
/** Tracks pending follow-up messages for UI display. Removed when delivered.
|
|
957
962
|
* See `#steeringMessages` for entry shape. */
|
|
958
963
|
#followUpMessages: QueuedDisplayEntry[] = [];
|
|
964
|
+
#queuedDisplaySequence = 0;
|
|
959
965
|
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
960
966
|
#pendingNextTurnMessages: CustomMessage[] = [];
|
|
961
967
|
#scheduledHiddenNextTurnGeneration: number | undefined = undefined;
|
|
@@ -1007,6 +1013,7 @@ export class AgentSession {
|
|
|
1007
1013
|
#evalKernelOwnerId: string;
|
|
1008
1014
|
/** Idempotent unregister handle for this session's resource-GC registration. */
|
|
1009
1015
|
#unregisterResourceGc?: () => void;
|
|
1016
|
+
#unregisterRuntimeStateFinalizer?: () => void;
|
|
1010
1017
|
/**
|
|
1011
1018
|
* AsyncJobManager owned by this session (top-level only). Subagents leave
|
|
1012
1019
|
* this undefined and **MUST NOT** dispose the global instance on teardown.
|
|
@@ -1232,6 +1239,11 @@ export class AgentSession {
|
|
|
1232
1239
|
settings: this.settings,
|
|
1233
1240
|
});
|
|
1234
1241
|
}
|
|
1242
|
+
this.#unregisterRuntimeStateFinalizer = registerCoordinatorRuntimeStateFinalizer({
|
|
1243
|
+
sessionId: this.sessionId,
|
|
1244
|
+
cwd: this.sessionManager.getCwd(),
|
|
1245
|
+
sessionFile: this.sessionManager.getSessionFile(),
|
|
1246
|
+
});
|
|
1235
1247
|
// Power assertions are taken per turn (see #beginInFlight); nothing acquired here.
|
|
1236
1248
|
this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
|
|
1237
1249
|
this.#ownedAsyncJobManager = config.ownedAsyncJobManager;
|
|
@@ -1625,6 +1637,14 @@ export class AgentSession {
|
|
|
1625
1637
|
this.#planCompactAbortPending = false;
|
|
1626
1638
|
}
|
|
1627
1639
|
|
|
1640
|
+
#createQueuedDisplayEntry(text: string, tag?: string): QueuedDisplayEntry {
|
|
1641
|
+
const entry: QueuedDisplayEntry = { text, sequence: ++this.#queuedDisplaySequence };
|
|
1642
|
+
if (tag !== undefined) {
|
|
1643
|
+
entry.tag = tag;
|
|
1644
|
+
}
|
|
1645
|
+
return entry;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1628
1648
|
/** Register a compact display string for a custom message that the caller is
|
|
1629
1649
|
* about to dispatch via `promptCustomMessage` / `sendCustomMessage`.
|
|
1630
1650
|
* Returns a stable tag the caller MUST embed in
|
|
@@ -1638,7 +1658,7 @@ export class AgentSession {
|
|
|
1638
1658
|
const tag = `gjc-cmd-${Date.now()}-${++this.#customDisplayTagCounter}`;
|
|
1639
1659
|
const displayText = text.trim();
|
|
1640
1660
|
if (!displayText) return tag;
|
|
1641
|
-
const entry
|
|
1661
|
+
const entry = this.#createQueuedDisplayEntry(displayText, tag);
|
|
1642
1662
|
if (mode === "steer") {
|
|
1643
1663
|
this.#steeringMessages.push(entry);
|
|
1644
1664
|
} else {
|
|
@@ -3339,6 +3359,8 @@ export class AgentSession {
|
|
|
3339
3359
|
// No-op when this session opened no tabs. Failure is logged, not thrown.
|
|
3340
3360
|
this.#unregisterResourceGc?.();
|
|
3341
3361
|
this.#unregisterResourceGc = undefined;
|
|
3362
|
+
this.#unregisterRuntimeStateFinalizer?.();
|
|
3363
|
+
this.#unregisterRuntimeStateFinalizer = undefined;
|
|
3342
3364
|
await releaseTabsForOwner(this.sessionManager.getSessionId()).catch((error: unknown) =>
|
|
3343
3365
|
logger.warn("session dispose: releaseTabsForOwner failed", { error }),
|
|
3344
3366
|
);
|
|
@@ -4818,6 +4840,7 @@ export class AgentSession {
|
|
|
4818
4840
|
|
|
4819
4841
|
// Expand file-based prompt templates if requested
|
|
4820
4842
|
const expandedText = expandPromptTemplates ? expandPromptTemplate(text, [...this.#promptTemplates]) : text;
|
|
4843
|
+
const workflowIntentDiff = options?.synthetic ? null : buildWorkflowIntentDiff(expandedText);
|
|
4821
4844
|
|
|
4822
4845
|
// If streaming, queue via steer() or followUp() based on option
|
|
4823
4846
|
if (this.isStreaming) {
|
|
@@ -4829,9 +4852,16 @@ export class AgentSession {
|
|
|
4829
4852
|
} else {
|
|
4830
4853
|
await this.#queueSteer(expandedText, options?.images);
|
|
4831
4854
|
}
|
|
4855
|
+
if (workflowIntentDiff) {
|
|
4856
|
+
this.sessionManager.appendCustomEntry(WORKFLOW_INTENT_DIFF_CUSTOM_TYPE, workflowIntentDiff);
|
|
4857
|
+
}
|
|
4832
4858
|
return;
|
|
4833
4859
|
}
|
|
4834
4860
|
|
|
4861
|
+
if (workflowIntentDiff) {
|
|
4862
|
+
this.sessionManager.appendCustomEntry(WORKFLOW_INTENT_DIFF_CUSTOM_TYPE, workflowIntentDiff);
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4835
4865
|
// Skip eager todo prelude when the user has already queued a directive
|
|
4836
4866
|
const hasPendingUserDirective = this.#toolChoiceQueue.inspect().includes("user-force");
|
|
4837
4867
|
const eagerTodoPrelude =
|
|
@@ -5285,7 +5315,7 @@ export class AgentSession {
|
|
|
5285
5315
|
*/
|
|
5286
5316
|
async #queueSteer(text: string, images?: ImageContent[]): Promise<void> {
|
|
5287
5317
|
const displayText = text || (images && images.length > 0 ? "[Image]" : "");
|
|
5288
|
-
this.#steeringMessages.push(
|
|
5318
|
+
this.#steeringMessages.push(this.#createQueuedDisplayEntry(displayText));
|
|
5289
5319
|
const content: (TextContent | ImageContent)[] = [{ type: "text", text }];
|
|
5290
5320
|
if (images && images.length > 0) {
|
|
5291
5321
|
content.push(...images);
|
|
@@ -5316,7 +5346,7 @@ export class AgentSession {
|
|
|
5316
5346
|
*/
|
|
5317
5347
|
async #queueFollowUp(text: string, images?: ImageContent[]): Promise<void> {
|
|
5318
5348
|
const displayText = text || (images && images.length > 0 ? "[Image]" : "");
|
|
5319
|
-
this.#followUpMessages.push(
|
|
5349
|
+
this.#followUpMessages.push(this.#createQueuedDisplayEntry(displayText));
|
|
5320
5350
|
const content: (TextContent | ImageContent)[] = [{ type: "text", text }];
|
|
5321
5351
|
if (images && images.length > 0) {
|
|
5322
5352
|
content.push(...images);
|
|
@@ -5686,24 +5716,27 @@ export class AgentSession {
|
|
|
5686
5716
|
}
|
|
5687
5717
|
|
|
5688
5718
|
/**
|
|
5689
|
-
* Pop the
|
|
5719
|
+
* Pop the newest queued message across steering and follow-up queues.
|
|
5690
5720
|
* Used by dequeue keybinding to restore messages to editor one at a time.
|
|
5691
5721
|
* Returns the popped entry's `.text`; the tag (if any) dies with the
|
|
5692
5722
|
* record — no orphan state can outlive the queue entry.
|
|
5693
5723
|
*/
|
|
5694
5724
|
popLastQueuedMessage(): string | undefined {
|
|
5695
|
-
|
|
5696
|
-
|
|
5725
|
+
const steeringEntry = this.#steeringMessages.at(-1);
|
|
5726
|
+
const followUpEntry = this.#followUpMessages.at(-1);
|
|
5727
|
+
|
|
5728
|
+
if (steeringEntry && (!followUpEntry || steeringEntry.sequence > followUpEntry.sequence)) {
|
|
5697
5729
|
const entry = this.#steeringMessages.pop();
|
|
5698
5730
|
this.agent.popLastSteer();
|
|
5699
5731
|
return entry?.text;
|
|
5700
5732
|
}
|
|
5701
|
-
|
|
5702
|
-
if (
|
|
5733
|
+
|
|
5734
|
+
if (followUpEntry) {
|
|
5703
5735
|
const entry = this.#followUpMessages.pop();
|
|
5704
5736
|
this.agent.popLastFollowUp();
|
|
5705
5737
|
return entry?.text;
|
|
5706
5738
|
}
|
|
5739
|
+
|
|
5707
5740
|
return undefined;
|
|
5708
5741
|
}
|
|
5709
5742
|
|
|
@@ -8932,22 +8965,52 @@ export class AgentSession {
|
|
|
8932
8965
|
setAutoRetryEnabled(enabled: boolean): void {
|
|
8933
8966
|
this.settings.set("retry.enabled", enabled);
|
|
8934
8967
|
}
|
|
8968
|
+
#isInterruptedRetryTail(message: AgentMessage | undefined): boolean {
|
|
8969
|
+
if (!message) return false;
|
|
8970
|
+
return (
|
|
8971
|
+
message.role === "user" ||
|
|
8972
|
+
message.role === "developer" ||
|
|
8973
|
+
message.role === "toolResult" ||
|
|
8974
|
+
message.role === "fileMention" ||
|
|
8975
|
+
message.role === "custom" ||
|
|
8976
|
+
message.role === "hookMessage"
|
|
8977
|
+
);
|
|
8978
|
+
}
|
|
8979
|
+
|
|
8980
|
+
#isUnresolvedToolUseAssistant(message: AssistantMessage): boolean {
|
|
8981
|
+
return message.stopReason === "toolUse" && message.content.some(content => content.type === "toolCall");
|
|
8982
|
+
}
|
|
8983
|
+
|
|
8935
8984
|
/**
|
|
8936
|
-
* Manually retry the last failed assistant turn
|
|
8937
|
-
*
|
|
8938
|
-
*
|
|
8985
|
+
* Manually retry the last failed assistant turn, or resume an interrupted tail
|
|
8986
|
+
* left by a non-graceful process exit after the user/custom/tool-result message
|
|
8987
|
+
* was persisted but before the agent emitted a terminal assistant response.
|
|
8988
|
+
* Removes failed/aborted/unresolved tool-use assistant tails before
|
|
8989
|
+
* re-attempting with a fresh retry budget.
|
|
8990
|
+
* @returns true if retry/resume was initiated, false if no retryable tail exists or agent is busy
|
|
8939
8991
|
*/
|
|
8940
8992
|
async retry(): Promise<boolean> {
|
|
8941
8993
|
if (this.isStreaming || this.isCompacting || this.isRetrying) return false;
|
|
8942
8994
|
|
|
8943
8995
|
const messages = this.agent.state.messages;
|
|
8944
8996
|
const lastMsg = messages[messages.length - 1];
|
|
8945
|
-
if (lastMsg
|
|
8997
|
+
if (!lastMsg) return false;
|
|
8998
|
+
|
|
8999
|
+
if (lastMsg.role !== "assistant") {
|
|
9000
|
+
if (!this.#isInterruptedRetryTail(lastMsg)) return false;
|
|
9001
|
+
this.#retryAttempt = 0;
|
|
9002
|
+
this.#scheduleAgentContinue({ delayMs: 1 });
|
|
9003
|
+
return true;
|
|
9004
|
+
}
|
|
8946
9005
|
|
|
8947
9006
|
const assistantMsg = lastMsg as AssistantMessage;
|
|
8948
|
-
|
|
9007
|
+
const shouldDropAssistant =
|
|
9008
|
+
assistantMsg.stopReason === "error" ||
|
|
9009
|
+
assistantMsg.stopReason === "aborted" ||
|
|
9010
|
+
this.#isUnresolvedToolUseAssistant(assistantMsg);
|
|
9011
|
+
if (!shouldDropAssistant) return false;
|
|
8949
9012
|
|
|
8950
|
-
// Remove the failed/aborted assistant message
|
|
9013
|
+
// Remove the failed/aborted/incomplete assistant message before re-attempting.
|
|
8951
9014
|
this.agent.replaceMessages(messages.slice(0, -1));
|
|
8952
9015
|
|
|
8953
9016
|
// Reset retry budget for a fresh attempt
|
|
@@ -1461,9 +1461,25 @@ function materializeResidentEntrySync<T extends FileEntry | SessionEntry>(
|
|
|
1461
1461
|
function materializeResidentEntriesSync<T extends FileEntry | SessionEntry>(
|
|
1462
1462
|
entries: T[],
|
|
1463
1463
|
stores: ResidentBlobStores,
|
|
1464
|
+
missingPolicy: ResidentBlobMissingPolicy = "throw",
|
|
1464
1465
|
): T[] {
|
|
1465
1466
|
const cache = new Map<string, string>();
|
|
1466
|
-
return entries.map(entry => materializeResidentEntrySync(entry, stores, cache));
|
|
1467
|
+
return entries.map(entry => materializeResidentEntrySync(entry, stores, cache, missingPolicy));
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
function materializeResidentEntryForReadSync<T extends FileEntry | SessionEntry>(
|
|
1471
|
+
entry: T,
|
|
1472
|
+
stores: ResidentBlobStores,
|
|
1473
|
+
cache: Map<string, string>,
|
|
1474
|
+
): T {
|
|
1475
|
+
return materializeResidentEntrySync(entry, stores, cache, "placeholder");
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function materializeResidentEntriesForReadSync<T extends FileEntry | SessionEntry>(
|
|
1479
|
+
entries: T[],
|
|
1480
|
+
stores: ResidentBlobStores,
|
|
1481
|
+
): T[] {
|
|
1482
|
+
return materializeResidentEntriesSync(entries, stores, "placeholder");
|
|
1467
1483
|
}
|
|
1468
1484
|
|
|
1469
1485
|
function materializeResidentEntryForPersistenceSync<T extends FileEntry | SessionEntry>(
|
|
@@ -1520,12 +1536,24 @@ function cloneSessionEntry(entry: SessionEntry): SessionEntry {
|
|
|
1520
1536
|
function materializeProviderVisibleEntrySync(entry: SessionEntry, stores: ResidentBlobStores): SessionEntry {
|
|
1521
1537
|
if (entry.type === "compaction") {
|
|
1522
1538
|
const cache = new Map<string, string>();
|
|
1523
|
-
const summary = materializeResidentValueSync(entry.summary, stores, "summary", cache);
|
|
1524
|
-
const shortSummary = materializeResidentValueSync(
|
|
1539
|
+
const summary = materializeResidentValueSync(entry.summary, stores, "summary", cache, "placeholder");
|
|
1540
|
+
const shortSummary = materializeResidentValueSync(
|
|
1541
|
+
entry.shortSummary,
|
|
1542
|
+
stores,
|
|
1543
|
+
"shortSummary",
|
|
1544
|
+
cache,
|
|
1545
|
+
"placeholder",
|
|
1546
|
+
);
|
|
1525
1547
|
const remote = entry.preserveData?.openaiRemoteCompaction;
|
|
1526
1548
|
const remoteRecord = isRecord(remote) ? remote : undefined;
|
|
1527
1549
|
const replacementHistory = remoteRecord
|
|
1528
|
-
? materializeResidentValueSync(
|
|
1550
|
+
? materializeResidentValueSync(
|
|
1551
|
+
remoteRecord.replacementHistory,
|
|
1552
|
+
stores,
|
|
1553
|
+
"replacementHistory",
|
|
1554
|
+
cache,
|
|
1555
|
+
"placeholder",
|
|
1556
|
+
)
|
|
1529
1557
|
: undefined;
|
|
1530
1558
|
const preserveData =
|
|
1531
1559
|
remoteRecord && replacementHistory !== undefined && replacementHistory !== remoteRecord.replacementHistory
|
|
@@ -1545,7 +1573,13 @@ function materializeProviderVisibleEntrySync(entry: SessionEntry, stores: Reside
|
|
|
1545
1573
|
};
|
|
1546
1574
|
}
|
|
1547
1575
|
if (entry.type === "branch_summary") {
|
|
1548
|
-
const summary = materializeResidentValueSync(
|
|
1576
|
+
const summary = materializeResidentValueSync(
|
|
1577
|
+
entry.summary,
|
|
1578
|
+
stores,
|
|
1579
|
+
"summary",
|
|
1580
|
+
new Map<string, string>(),
|
|
1581
|
+
"placeholder",
|
|
1582
|
+
);
|
|
1549
1583
|
return typeof summary === "string" ? { ...entry, summary } : { ...entry };
|
|
1550
1584
|
}
|
|
1551
1585
|
return cloneSessionEntry(entry);
|
|
@@ -2751,7 +2785,10 @@ export class SessionManager {
|
|
|
2751
2785
|
}
|
|
2752
2786
|
|
|
2753
2787
|
captureState(): SessionManagerStateSnapshot {
|
|
2754
|
-
const materializedFileEntries =
|
|
2788
|
+
const materializedFileEntries = materializeResidentEntriesForReadSync(
|
|
2789
|
+
this.#fileEntries,
|
|
2790
|
+
this.#residentBlobStores(),
|
|
2791
|
+
);
|
|
2755
2792
|
return {
|
|
2756
2793
|
sessionId: this.#sessionId,
|
|
2757
2794
|
sessionName: this.#sessionName,
|
|
@@ -2875,7 +2912,7 @@ export class SessionManager {
|
|
|
2875
2912
|
|
|
2876
2913
|
const oldSessionFile = this.#sessionFile;
|
|
2877
2914
|
const oldSessionId = this.#sessionId;
|
|
2878
|
-
const materializedEntries =
|
|
2915
|
+
const materializedEntries = materializeResidentEntriesForReadSync(this.#fileEntries, this.#residentBlobStores());
|
|
2879
2916
|
|
|
2880
2917
|
// Close the current writer
|
|
2881
2918
|
await this.#closePersistWriter();
|
|
@@ -2947,7 +2984,10 @@ export class SessionManager {
|
|
|
2947
2984
|
hadSessionFile = this.storage.existsSync(oldSessionFile);
|
|
2948
2985
|
let movedSessionFile = false;
|
|
2949
2986
|
let movedArtifactDir = false;
|
|
2950
|
-
const materializedEntries =
|
|
2987
|
+
const materializedEntries = materializeResidentEntriesForReadSync(
|
|
2988
|
+
this.#fileEntries,
|
|
2989
|
+
this.#residentBlobStores(),
|
|
2990
|
+
);
|
|
2951
2991
|
const restoreResidentStateAfterFailure = (): void => {
|
|
2952
2992
|
this.#fileEntries = materializedEntries;
|
|
2953
2993
|
this.#resetResidentTextBlobStore();
|
|
@@ -4006,7 +4046,7 @@ export class SessionManager {
|
|
|
4006
4046
|
getLeafEntry(): SessionEntry | undefined {
|
|
4007
4047
|
if (!this.#leafId) return undefined;
|
|
4008
4048
|
const entry = this.#byId.get(this.#leafId);
|
|
4009
|
-
return entry ?
|
|
4049
|
+
return entry ? materializeResidentEntryForReadSync(entry, this.#residentBlobStores(), new Map()) : undefined;
|
|
4010
4050
|
}
|
|
4011
4051
|
|
|
4012
4052
|
/**
|
|
@@ -4176,7 +4216,7 @@ export class SessionManager {
|
|
|
4176
4216
|
const entry = this.#byId.get(id);
|
|
4177
4217
|
return entry
|
|
4178
4218
|
? rehydrateColdSpillEntry(
|
|
4179
|
-
|
|
4219
|
+
materializeResidentEntryForReadSync(entry, this.#residentBlobStores(), new Map()),
|
|
4180
4220
|
this.#blobStore,
|
|
4181
4221
|
this.#residentBlobStoresForColdRehydrate(),
|
|
4182
4222
|
)
|
|
@@ -4193,7 +4233,7 @@ export class SessionManager {
|
|
|
4193
4233
|
visited.add(current.id);
|
|
4194
4234
|
path.push(
|
|
4195
4235
|
rehydrateColdSpillEntry(
|
|
4196
|
-
|
|
4236
|
+
materializeResidentEntryForReadSync(current, this.#residentBlobStores(), cache),
|
|
4197
4237
|
this.#blobStore,
|
|
4198
4238
|
this.#residentBlobStoresForColdRehydrate(),
|
|
4199
4239
|
),
|
|
@@ -4233,7 +4273,7 @@ export class SessionManager {
|
|
|
4233
4273
|
.filter((entry): entry is SessionEntry => entry.type !== "session")
|
|
4234
4274
|
.map(entry =>
|
|
4235
4275
|
rehydrateColdSpillEntry(
|
|
4236
|
-
|
|
4276
|
+
materializeResidentEntryForReadSync(entry, this.#residentBlobStores(), cache),
|
|
4237
4277
|
this.#blobStore,
|
|
4238
4278
|
this.#residentBlobStoresForColdRehydrate(),
|
|
4239
4279
|
),
|
|
@@ -4244,7 +4284,7 @@ export class SessionManager {
|
|
|
4244
4284
|
this.#publicMaterializerCallCount++;
|
|
4245
4285
|
this.#getEntryMaterializerCallCount++;
|
|
4246
4286
|
const entry = this.#byId.get(id);
|
|
4247
|
-
return entry ?
|
|
4287
|
+
return entry ? materializeResidentEntryForReadSync(entry, this.#residentBlobStores(), new Map()) : undefined;
|
|
4248
4288
|
}
|
|
4249
4289
|
|
|
4250
4290
|
/**
|
|
@@ -4255,7 +4295,7 @@ export class SessionManager {
|
|
|
4255
4295
|
const children: SessionEntry[] = [];
|
|
4256
4296
|
for (const entry of this.#byId.values()) {
|
|
4257
4297
|
if (entry.parentId === parentId) {
|
|
4258
|
-
children.push(
|
|
4298
|
+
children.push(materializeResidentEntryForReadSync(entry, this.#residentBlobStores(), cache));
|
|
4259
4299
|
}
|
|
4260
4300
|
}
|
|
4261
4301
|
return children;
|
|
@@ -4310,7 +4350,7 @@ export class SessionManager {
|
|
|
4310
4350
|
while (current) {
|
|
4311
4351
|
if (visited.has(current.id)) break;
|
|
4312
4352
|
visited.add(current.id);
|
|
4313
|
-
path.push(
|
|
4353
|
+
path.push(materializeResidentEntryForReadSync(current, this.#residentBlobStores(), cache));
|
|
4314
4354
|
current = current.parentId ? this.#byId.get(current.parentId) : undefined;
|
|
4315
4355
|
}
|
|
4316
4356
|
path.reverse();
|
|
@@ -4375,7 +4415,7 @@ export class SessionManager {
|
|
|
4375
4415
|
}
|
|
4376
4416
|
if (entry.type !== "message" && entry.type !== "custom_message")
|
|
4377
4417
|
return materializeProviderVisibleEntrySync(entry, this.#residentBlobStores());
|
|
4378
|
-
const materialized =
|
|
4418
|
+
const materialized = materializeResidentEntryForReadSync(entry, this.#residentBlobStores(), new Map());
|
|
4379
4419
|
const rehydrated = rehydrateColdSpillEntry(
|
|
4380
4420
|
materialized,
|
|
4381
4421
|
this.#blobStore,
|
|
@@ -4434,7 +4474,7 @@ export class SessionManager {
|
|
|
4434
4474
|
const resolvedTextBlobCache = new Map<string, string>();
|
|
4435
4475
|
const materializedEntries = this.#fileEntries
|
|
4436
4476
|
.filter((e): e is SessionEntry => e.type !== "session")
|
|
4437
|
-
.map(entry =>
|
|
4477
|
+
.map(entry => materializeResidentEntryForReadSync(entry, this.#residentBlobStores(), resolvedTextBlobCache));
|
|
4438
4478
|
this.#materializedEntriesCache = materializedEntries;
|
|
4439
4479
|
this.#materializedEntriesRevision = this.#entryRevision;
|
|
4440
4480
|
return materializedEntries;
|
|
@@ -4591,7 +4631,7 @@ export class SessionManager {
|
|
|
4591
4631
|
|
|
4592
4632
|
// Filter out LabelEntry from path - we'll recreate them from the resolved map
|
|
4593
4633
|
const pathWithoutLabels = branchPath.filter(e => e.type !== "label");
|
|
4594
|
-
const materializedPathWithoutLabels =
|
|
4634
|
+
const materializedPathWithoutLabels = materializeResidentEntriesForReadSync(
|
|
4595
4635
|
pathWithoutLabels,
|
|
4596
4636
|
this.#residentBlobStores(),
|
|
4597
4637
|
);
|
|
@@ -322,7 +322,8 @@ const shutdownHandlerTui = (_command: ParsedSlashCommand, runtime: TuiSlashComma
|
|
|
322
322
|
const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
323
323
|
{
|
|
324
324
|
name: "settings",
|
|
325
|
-
|
|
325
|
+
priority: 40,
|
|
326
|
+
description: "Open settings and preferences",
|
|
326
327
|
handleTui: (_command, runtime) => {
|
|
327
328
|
runtime.ctx.showSettingsSelector();
|
|
328
329
|
runtime.ctx.editor.setText("");
|
|
@@ -338,7 +339,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
338
339
|
},
|
|
339
340
|
{
|
|
340
341
|
name: "goal",
|
|
341
|
-
|
|
342
|
+
priority: 84,
|
|
343
|
+
description: "Plan and track an autonomous goal",
|
|
342
344
|
subcommands: [
|
|
343
345
|
{ name: "set", description: "Set or replace the goal", usage: "<objective>" },
|
|
344
346
|
{ name: "show", description: "Show current goal details" },
|
|
@@ -541,7 +543,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
541
543
|
},
|
|
542
544
|
{
|
|
543
545
|
name: "export",
|
|
544
|
-
|
|
546
|
+
priority: 50,
|
|
547
|
+
description: "Export this session to an HTML file",
|
|
545
548
|
inlineHint: "[path]",
|
|
546
549
|
allowArgs: true,
|
|
547
550
|
handle: async (command, runtime) => {
|
|
@@ -568,7 +571,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
568
571
|
},
|
|
569
572
|
{
|
|
570
573
|
name: "copy",
|
|
571
|
-
|
|
574
|
+
priority: 55,
|
|
575
|
+
description: "Copy the last response for review or sharing",
|
|
572
576
|
// Public `/copy` is strict zero-argument, but `allowArgs` lets the
|
|
573
577
|
// TUI dispatcher route `/copy <arg>` here so it can be rejected locally
|
|
574
578
|
// instead of falling through as a model prompt.
|
|
@@ -585,7 +589,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
585
589
|
},
|
|
586
590
|
{
|
|
587
591
|
name: "dump",
|
|
588
|
-
|
|
592
|
+
priority: 54,
|
|
593
|
+
description: "Dump the full transcript for review or sharing",
|
|
589
594
|
acpDescription: "Return full transcript as plain text",
|
|
590
595
|
handle: async (_command, runtime) => {
|
|
591
596
|
const text = runtime.session.formatSessionAsText();
|
|
@@ -599,11 +604,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
599
604
|
},
|
|
600
605
|
{
|
|
601
606
|
name: "session",
|
|
602
|
-
|
|
607
|
+
priority: 88,
|
|
608
|
+
description: "Show current session info or delete current session",
|
|
603
609
|
acpDescription: "Show session information",
|
|
604
610
|
acpInputHint: "info|delete",
|
|
605
611
|
subcommands: [
|
|
606
|
-
{ name: "info", description: "Show session
|
|
612
|
+
{ name: "info", description: "Show current session id, title, and workspace" },
|
|
607
613
|
{ name: "delete", description: "Delete current session and return to selector" },
|
|
608
614
|
],
|
|
609
615
|
allowArgs: true,
|
|
@@ -713,6 +719,15 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
713
719
|
runtime.ctx.editor.setText("");
|
|
714
720
|
},
|
|
715
721
|
},
|
|
722
|
+
{
|
|
723
|
+
name: "help",
|
|
724
|
+
priority: 100,
|
|
725
|
+
description: "Learn commands and beginner workflows",
|
|
726
|
+
handleTui: (_command, runtime) => {
|
|
727
|
+
runtime.ctx.handleHelpCommand();
|
|
728
|
+
runtime.ctx.editor.setText("");
|
|
729
|
+
},
|
|
730
|
+
},
|
|
716
731
|
{
|
|
717
732
|
name: "hotkeys",
|
|
718
733
|
description: "Show all keyboard shortcuts",
|
|
@@ -953,6 +968,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
953
968
|
},
|
|
954
969
|
{
|
|
955
970
|
name: "new",
|
|
971
|
+
priority: 96,
|
|
956
972
|
description: "Start a new session",
|
|
957
973
|
handleTui: async (_command, runtime) => {
|
|
958
974
|
runtime.ctx.editor.setText("");
|
|
@@ -969,7 +985,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
969
985
|
},
|
|
970
986
|
{
|
|
971
987
|
name: "compact",
|
|
972
|
-
|
|
988
|
+
priority: 72,
|
|
989
|
+
description: "Compact context and continue this session",
|
|
973
990
|
acpDescription: "Compact the conversation",
|
|
974
991
|
inlineHint: "[focus instructions]",
|
|
975
992
|
allowArgs: true,
|
|
@@ -1026,7 +1043,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1026
1043
|
},
|
|
1027
1044
|
{
|
|
1028
1045
|
name: "resume",
|
|
1029
|
-
|
|
1046
|
+
priority: 92,
|
|
1047
|
+
description: "Resume a previous session",
|
|
1030
1048
|
handleTui: (_command, runtime) => {
|
|
1031
1049
|
runtime.ctx.showSessionSelector();
|
|
1032
1050
|
runtime.ctx.editor.setText("");
|
|
@@ -1045,7 +1063,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1045
1063
|
},
|
|
1046
1064
|
{
|
|
1047
1065
|
name: "retry",
|
|
1048
|
-
|
|
1066
|
+
priority: 70,
|
|
1067
|
+
description: "Retry or continue the last interrupted turn",
|
|
1049
1068
|
handleTui: async (_command, runtime) => {
|
|
1050
1069
|
const didRetry = await runtime.ctx.session.retry();
|
|
1051
1070
|
if (!didRetry) {
|
|
@@ -1242,6 +1261,7 @@ export const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = AC
|
|
|
1242
1261
|
description: command.description,
|
|
1243
1262
|
subcommands: command.subcommands,
|
|
1244
1263
|
inlineHint: command.inlineHint,
|
|
1264
|
+
priority: command.priority,
|
|
1245
1265
|
}),
|
|
1246
1266
|
);
|
|
1247
1267
|
|
|
@@ -19,6 +19,8 @@ export interface BuiltinSlashCommand {
|
|
|
19
19
|
subcommands?: SubcommandDef[];
|
|
20
20
|
/** Static inline hint when command takes a simple argument (no subcommands). */
|
|
21
21
|
inlineHint?: string;
|
|
22
|
+
/** Higher values sort earlier in TUI slash-command autocomplete. */
|
|
23
|
+
priority?: number;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/** Parsed slash-command text after stripping the leading "/". */
|
package/src/system-prompt.ts
CHANGED
|
@@ -356,6 +356,10 @@ export interface BuildSystemPromptOptions {
|
|
|
356
356
|
mcpDiscoveryMode?: boolean;
|
|
357
357
|
/** Discoverable MCP server summaries to advertise when discovery mode is active. */
|
|
358
358
|
mcpDiscoveryServerSummaries?: string[];
|
|
359
|
+
/** Whether built-in tool discovery ("all" mode) is active; enables the tool-discovery prompt block. */
|
|
360
|
+
toolDiscoveryActive?: boolean;
|
|
361
|
+
/** Hidden discoverable tools to advertise (name + one-line summary) when tool discovery is active. */
|
|
362
|
+
discoverableTools?: Array<{ name: string; summary: string }>;
|
|
359
363
|
/** Encourage the agent to delegate via tasks unless changes are trivial. */
|
|
360
364
|
eagerTasks?: boolean;
|
|
361
365
|
/** Rules with alwaysApply=true — their full content is injected into the prompt. */
|
|
@@ -394,6 +398,8 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
394
398
|
intentField,
|
|
395
399
|
mcpDiscoveryMode = false,
|
|
396
400
|
mcpDiscoveryServerSummaries = [],
|
|
401
|
+
toolDiscoveryActive = false,
|
|
402
|
+
discoverableTools = [],
|
|
397
403
|
eagerTasks = false,
|
|
398
404
|
secretsEnabled = false,
|
|
399
405
|
workspaceTree: providedWorkspaceTree,
|
|
@@ -572,6 +578,8 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
572
578
|
mcpDiscoveryMode,
|
|
573
579
|
hasMCPDiscoveryServers: mcpDiscoveryServerSummaries.length > 0,
|
|
574
580
|
mcpDiscoveryServerSummaries,
|
|
581
|
+
toolDiscoveryActive,
|
|
582
|
+
discoverableTools,
|
|
575
583
|
eagerTasks,
|
|
576
584
|
secretsEnabled,
|
|
577
585
|
};
|
package/src/task/executor.ts
CHANGED
|
@@ -1535,6 +1535,11 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1535
1535
|
);
|
|
1536
1536
|
await awaitAbortable(session.waitForIdle());
|
|
1537
1537
|
} catch (err) {
|
|
1538
|
+
// An abort (parent cancel, session shutdown, timeout) rejects
|
|
1539
|
+
// awaitAbortable with ToolAbortError — a cancellation, not a prompt
|
|
1540
|
+
// failure. Mirror the outer catch's `!abortSignal.aborted` guard so
|
|
1541
|
+
// expected cancellations aren't logged at error level.
|
|
1542
|
+
if (abortSignal.aborted || err instanceof ToolAbortError) break;
|
|
1538
1543
|
logger.error("Subagent prompt failed", {
|
|
1539
1544
|
error: err instanceof Error ? err.message : String(err),
|
|
1540
1545
|
});
|
package/src/tools/ast-grep.ts
CHANGED
|
@@ -223,13 +223,16 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
223
223
|
const modelOut: string[] = [];
|
|
224
224
|
const displayOut: string[] = [];
|
|
225
225
|
const fileMatches = matchesByFile.get(relativePath) ?? [];
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const endLine = match.startLine +
|
|
229
|
-
return
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
226
|
+
const preparedMatches = fileMatches.map(match => {
|
|
227
|
+
const lines = match.text.split("\n");
|
|
228
|
+
const endLine = match.startLine + lines.length - 1;
|
|
229
|
+
return { match, lines, endLine };
|
|
230
|
+
});
|
|
231
|
+
const lineNumberWidth = preparedMatches.reduce(
|
|
232
|
+
(width, entry) => Math.max(width, String(entry.match.startLine).length, String(entry.endLine).length),
|
|
233
|
+
0,
|
|
234
|
+
);
|
|
235
|
+
for (const { match, lines: matchLines } of preparedMatches) {
|
|
233
236
|
for (let index = 0; index < matchLines.length; index++) {
|
|
234
237
|
const lineNumber = match.startLine + index;
|
|
235
238
|
const isMatch = index === 0;
|