@nghyane/arcane 0.1.13 → 0.1.15
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 +28 -0
- package/package.json +21 -70
- package/scripts/format-prompts.ts +1 -3
- package/src/cli/args.ts +2 -7
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli.ts +0 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/grep.ts +1 -1
- package/src/commands/jupyter.ts +1 -1
- package/src/commands/plugin.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/shell.ts +1 -1
- package/src/commands/ssh.ts +1 -1
- package/src/commands/stats.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config/model-registry.ts +3 -4
- package/src/config/model-resolver.ts +36 -9
- package/src/config/prompt-templates.ts +1 -9
- package/src/config/settings-schema.ts +32 -88
- package/src/config/settings.ts +3 -4
- package/src/debug/index.ts +1 -1
- package/src/debug/log-formatting.ts +1 -1
- package/src/debug/log-viewer.ts +2 -2
- package/src/discovery/helpers.ts +13 -3
- package/src/exa/index.ts +1 -35
- package/src/exa/render.ts +30 -190
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +5 -1
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/extensions/wrapper.ts +7 -15
- package/src/extensibility/hooks/runner.ts +1 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/index.ts +13 -13
- package/src/lsp/index.ts +77 -24
- package/src/lsp/render.ts +34 -583
- package/src/lsp/types.ts +3 -3
- package/src/lsp/utils.ts +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -24
- package/src/modes/components/assistant-message.ts +7 -7
- package/src/modes/components/bash-execution.ts +50 -112
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/branch-summary-message.ts +16 -10
- package/src/modes/components/compaction-summary-message.ts +20 -12
- package/src/modes/components/context-group.ts +106 -0
- package/src/modes/components/custom-message.ts +4 -5
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +2 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-message.ts +4 -5
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/components/index.ts +0 -2
- package/src/modes/components/keybinding-hints.ts +1 -1
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/oauth-selector.ts +1 -1
- package/src/modes/components/plugin-settings.ts +1 -1
- package/src/modes/components/python-execution.ts +51 -91
- package/src/modes/components/queue-mode-selector.ts +1 -1
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-defs.ts +5 -10
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/show-images-selector.ts +1 -1
- package/src/modes/components/skill-message.ts +4 -4
- package/src/modes/components/status-line/segments.ts +2 -2
- package/src/modes/components/status-line/separators.ts +1 -1
- package/src/modes/components/status-line-segment-editor.ts +1 -1
- package/src/modes/components/status-line.ts +1 -1
- package/src/modes/components/theme-selector.ts +1 -1
- package/src/modes/components/thinking-selector.ts +1 -1
- package/src/modes/components/todo-display.ts +2 -4
- package/src/modes/components/todo-reminder.ts +4 -4
- package/src/modes/components/tool-execution.ts +118 -440
- package/src/modes/components/tool-image-display.ts +107 -0
- package/src/modes/components/tree-selector.ts +2 -2
- package/src/modes/components/ttsr-notification.ts +4 -17
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +9 -10
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +58 -187
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +3 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +3 -26
- package/src/modes/controllers/ssh-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +3 -7
- package/src/modes/print-mode.ts +5 -5
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/types.ts +1 -2
- package/src/modes/utils/ui-helpers.ts +34 -32
- package/src/patch/edit-tool.ts +742 -0
- package/src/patch/index.ts +32 -898
- package/src/patch/schemas.ts +208 -0
- package/src/patch/shared.ts +83 -151
- package/src/prompts/agents/explore.md +22 -37
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/agents/librarian.md +29 -20
- package/src/prompts/agents/oracle.md +9 -2
- package/src/prompts/agents/reviewer.md +14 -48
- package/src/prompts/agents/task.md +16 -8
- package/src/prompts/compaction/branch-summary.md +4 -1
- package/src/prompts/compaction/compaction-summary.md +4 -1
- package/src/prompts/system/subagent-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +162 -178
- package/src/prompts/system/verification-reminder.md +6 -0
- package/src/sdk.ts +0 -9
- package/src/session/agent-session.ts +244 -1459
- package/src/session/model-controller.ts +406 -0
- package/src/session/retry-utils.ts +71 -0
- package/src/session/session-manager.ts +22 -186
- package/src/session/session-types.ts +312 -0
- package/src/session/stats.ts +387 -0
- package/src/session/streaming-edit.ts +258 -0
- package/src/session/ttsr.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +0 -8
- package/src/stt/recorder.ts +2 -2
- package/src/system-prompt.ts +1 -14
- package/src/task/agents.ts +7 -33
- package/src/task/executor.ts +50 -438
- package/src/task/index.ts +104 -71
- package/src/task/progress-tracker.ts +390 -0
- package/src/task/render.ts +371 -187
- package/src/task/subprocess-tool-registry.ts +1 -1
- package/src/task/types.ts +14 -47
- package/src/tools/ask.ts +31 -42
- package/src/tools/bash-interactive.ts +2 -2
- package/src/tools/bash-interceptor.ts +2 -2
- package/src/tools/bash-normalize.ts +1 -1
- package/src/tools/bash-skill-urls.ts +2 -2
- package/src/tools/bash.ts +87 -136
- package/src/tools/browser.ts +54 -84
- package/src/tools/create-tools.ts +186 -0
- package/src/tools/default-renderer.ts +104 -0
- package/src/tools/explore.ts +11 -10
- package/src/tools/fetch.ts +24 -114
- package/src/tools/find.ts +48 -132
- package/src/tools/gemini-image.ts +5 -15
- package/src/tools/github.ts +450 -0
- package/src/tools/grep.ts +43 -179
- package/src/tools/index.ts +35 -198
- package/src/tools/json-tree.ts +3 -3
- package/src/tools/librarian.ts +18 -18
- package/src/tools/list-limit.ts +2 -2
- package/src/tools/notebook.ts +35 -87
- package/src/tools/oracle.ts +25 -25
- package/src/tools/output-meta.ts +89 -4
- package/src/tools/output-utils.ts +2 -2
- package/src/tools/python.ts +86 -637
- package/src/tools/read.ts +36 -119
- package/src/tools/reviewer-tool.ts +19 -21
- package/src/tools/search-code.ts +128 -0
- package/src/tools/ssh.ts +67 -126
- package/src/tools/subagent-tool.ts +197 -123
- package/src/tools/todo-write.ts +15 -31
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/undo-edit.ts +30 -67
- package/src/tools/write.ts +78 -127
- package/src/tui/code-cell.ts +4 -4
- package/src/tui/file-list.ts +2 -2
- package/src/tui/output-block.ts +1 -1
- package/src/tui/status-line.ts +1 -1
- package/src/tui/tree-list.ts +2 -2
- package/src/tui/types.ts +1 -1
- package/src/tui/utils.ts +1 -1
- package/src/{tools → ui}/render-utils.ts +87 -126
- package/src/utils/external-editor.ts +4 -4
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/index.ts +30 -0
- package/src/utils/tools-manager.ts +9 -19
- package/src/web/github-client.ts +290 -0
- package/src/web/scrapers/github.ts +11 -62
- package/src/web/search/auth.ts +1 -3
- package/src/web/search/index.ts +82 -46
- package/src/web/search/provider.ts +11 -16
- package/src/web/search/providers/grep.ts +160 -0
- package/src/web/search/render.ts +48 -235
- package/src/web/search/types.ts +1 -1
- package/src/commands/commit.ts +0 -36
- package/src/commit/agentic/agent.ts +0 -311
- package/src/commit/agentic/fallback.ts +0 -96
- package/src/commit/agentic/index.ts +0 -359
- package/src/commit/agentic/prompts/analyze-file.md +0 -22
- package/src/commit/agentic/prompts/session-user.md +0 -25
- package/src/commit/agentic/prompts/split-confirm.md +0 -1
- package/src/commit/agentic/prompts/system.md +0 -38
- package/src/commit/agentic/state.ts +0 -69
- package/src/commit/agentic/tools/analyze-file.ts +0 -118
- package/src/commit/agentic/tools/git-file-diff.ts +0 -194
- package/src/commit/agentic/tools/git-hunk.ts +0 -50
- package/src/commit/agentic/tools/git-overview.ts +0 -84
- package/src/commit/agentic/tools/index.ts +0 -56
- package/src/commit/agentic/tools/propose-changelog.ts +0 -128
- package/src/commit/agentic/tools/propose-commit.ts +0 -154
- package/src/commit/agentic/tools/recent-commits.ts +0 -81
- package/src/commit/agentic/tools/split-commit.ts +0 -280
- package/src/commit/agentic/topo-sort.ts +0 -44
- package/src/commit/agentic/trivial.ts +0 -51
- package/src/commit/agentic/validation.ts +0 -200
- package/src/commit/analysis/conventional.ts +0 -165
- package/src/commit/analysis/index.ts +0 -4
- package/src/commit/analysis/scope.ts +0 -242
- package/src/commit/analysis/summary.ts +0 -112
- package/src/commit/analysis/validation.ts +0 -66
- package/src/commit/changelog/detect.ts +0 -37
- package/src/commit/changelog/generate.ts +0 -110
- package/src/commit/changelog/index.ts +0 -234
- package/src/commit/changelog/parse.ts +0 -44
- package/src/commit/cli.ts +0 -93
- package/src/commit/git/diff.ts +0 -148
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -211
- package/src/commit/git/operations.ts +0 -54
- package/src/commit/index.ts +0 -5
- package/src/commit/map-reduce/index.ts +0 -64
- package/src/commit/map-reduce/map-phase.ts +0 -178
- package/src/commit/map-reduce/reduce-phase.ts +0 -145
- package/src/commit/map-reduce/utils.ts +0 -9
- package/src/commit/message.ts +0 -11
- package/src/commit/model-selection.ts +0 -69
- package/src/commit/pipeline.ts +0 -243
- package/src/commit/prompts/analysis-system.md +0 -148
- package/src/commit/prompts/analysis-user.md +0 -38
- package/src/commit/prompts/changelog-system.md +0 -50
- package/src/commit/prompts/changelog-user.md +0 -18
- package/src/commit/prompts/file-observer-system.md +0 -24
- package/src/commit/prompts/file-observer-user.md +0 -8
- package/src/commit/prompts/reduce-system.md +0 -50
- package/src/commit/prompts/reduce-user.md +0 -17
- package/src/commit/prompts/summary-retry.md +0 -3
- package/src/commit/prompts/summary-system.md +0 -38
- package/src/commit/prompts/summary-user.md +0 -13
- package/src/commit/prompts/types-description.md +0 -2
- package/src/commit/types.ts +0 -109
- package/src/commit/utils/exclusions.ts +0 -42
- package/src/mcp/render.ts +0 -123
- package/src/modes/components/agent-dashboard.ts +0 -1130
- package/src/modes/components/codemode-group.ts +0 -369
- package/src/modes/components/read-tool-group.ts +0 -119
- package/src/modes/components/visual-truncate.ts +0 -63
- package/src/prompts/system/subagent-user-prompt.md +0 -8
- package/src/prompts/tools/ask.md +0 -44
- package/src/prompts/tools/bash.md +0 -24
- package/src/prompts/tools/browser.md +0 -33
- package/src/prompts/tools/calculator.md +0 -12
- package/src/prompts/tools/explore.md +0 -29
- package/src/prompts/tools/fetch.md +0 -16
- package/src/prompts/tools/find.md +0 -18
- package/src/prompts/tools/gemini-image.md +0 -23
- package/src/prompts/tools/grep.md +0 -28
- package/src/prompts/tools/hashline.md +0 -232
- package/src/prompts/tools/librarian.md +0 -24
- package/src/prompts/tools/lsp.md +0 -28
- package/src/prompts/tools/oracle.md +0 -26
- package/src/prompts/tools/patch.md +0 -74
- package/src/prompts/tools/python.md +0 -66
- package/src/prompts/tools/read.md +0 -36
- package/src/prompts/tools/replace.md +0 -38
- package/src/prompts/tools/reviewer.md +0 -41
- package/src/prompts/tools/ssh.md +0 -51
- package/src/prompts/tools/task-summary.md +0 -28
- package/src/prompts/tools/task.md +0 -146
- package/src/prompts/tools/todo-write.md +0 -65
- package/src/prompts/tools/undo-edit.md +0 -7
- package/src/prompts/tools/web-search.md +0 -19
- package/src/prompts/tools/write.md +0 -18
- package/src/task/batch.ts +0 -102
- package/src/task/discovery.ts +0 -126
- package/src/task/parallel.ts +0 -84
- package/src/task/template.ts +0 -32
- package/src/tools/calculator.ts +0 -537
- package/src/tools/jtd-to-typescript.ts +0 -198
- package/src/tools/renderers.ts +0 -60
- package/src/tools/tool-result.ts +0 -86
- /package/src/{modes/theme → theme}/dark.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
- /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
- /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
- /package/src/{modes/theme → theme}/light.json +0 -0
- /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
- /package/src/{modes/theme → theme}/theme-schema.json +0 -0
- /package/src/{modes/theme → theme}/theme.ts +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
|
|
3
|
+
import type { Agent, AgentEvent } from "@nghyane/arcane-agent";
|
|
4
|
+
import type { AssistantMessage, ToolCall } from "@nghyane/arcane-ai";
|
|
5
|
+
import { isEnoent, logger } from "@nghyane/arcane-utils";
|
|
6
|
+
import type { Settings } from "../config/settings";
|
|
7
|
+
import { normalizeDiff, normalizeToLF, ParseError, previewPatch, stripBom } from "../patch";
|
|
8
|
+
import type { SecretObfuscator } from "../secrets/obfuscator";
|
|
9
|
+
import { resolveToCwd } from "../tools/path-utils";
|
|
10
|
+
import type { SessionManager } from "./session-manager";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Mutable state for streaming edit abort detection.
|
|
14
|
+
*/
|
|
15
|
+
export interface StreamingEditState {
|
|
16
|
+
abortTriggered: boolean;
|
|
17
|
+
checkedLineCounts: Map<string, number>;
|
|
18
|
+
fileCache: Map<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createStreamingEditState(): StreamingEditState {
|
|
22
|
+
return {
|
|
23
|
+
abortTriggered: false,
|
|
24
|
+
checkedLineCounts: new Map(),
|
|
25
|
+
fileCache: new Map(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resetStreamingEditState(state: StreamingEditState): void {
|
|
30
|
+
state.abortTriggered = false;
|
|
31
|
+
state.checkedLineCounts.clear();
|
|
32
|
+
state.fileCache.clear();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function preCacheStreamingEditFile(
|
|
36
|
+
event: AgentEvent,
|
|
37
|
+
state: StreamingEditState,
|
|
38
|
+
settings: Settings,
|
|
39
|
+
cwd: string,
|
|
40
|
+
): Promise<void> {
|
|
41
|
+
if (!settings.get("edit.streamingAbort")) return;
|
|
42
|
+
if (event.type !== "message_update") return;
|
|
43
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
44
|
+
if (assistantEvent.type !== "toolcall_start") return;
|
|
45
|
+
if (event.message.role !== "assistant") return;
|
|
46
|
+
|
|
47
|
+
const contentIndex = assistantEvent.contentIndex;
|
|
48
|
+
const messageContent = event.message.content;
|
|
49
|
+
if (!Array.isArray(messageContent) || contentIndex >= messageContent.length) return;
|
|
50
|
+
const toolCall = messageContent[contentIndex] as ToolCall;
|
|
51
|
+
if (toolCall.name !== "edit") return;
|
|
52
|
+
|
|
53
|
+
const args = toolCall.arguments;
|
|
54
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return;
|
|
55
|
+
if ("old_text" in args || "new_text" in args) return;
|
|
56
|
+
|
|
57
|
+
const path = typeof args.path === "string" ? args.path : undefined;
|
|
58
|
+
if (!path) return;
|
|
59
|
+
|
|
60
|
+
const resolvedPath = resolveToCwd(path, cwd);
|
|
61
|
+
ensureFileCache(state, resolvedPath);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function ensureFileCache(state: StreamingEditState, resolvedPath: string): void {
|
|
65
|
+
if (state.fileCache.has(resolvedPath)) return;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const rawText = fs.readFileSync(resolvedPath, "utf-8");
|
|
69
|
+
const { text } = stripBom(rawText);
|
|
70
|
+
state.fileCache.set(resolvedPath, normalizeToLF(text));
|
|
71
|
+
} catch {
|
|
72
|
+
// Don't cache on read errors (including ENOENT) - let the edit tool handle them
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function invalidateFileCacheForPath(state: StreamingEditState, path: string, cwd: string): void {
|
|
77
|
+
const resolvedPath = resolveToCwd(path, cwd);
|
|
78
|
+
state.fileCache.delete(resolvedPath);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function maybeAbortStreamingEdit(
|
|
82
|
+
event: AgentEvent,
|
|
83
|
+
state: StreamingEditState,
|
|
84
|
+
settings: Settings,
|
|
85
|
+
agent: Agent,
|
|
86
|
+
cwd: string,
|
|
87
|
+
obfuscator: SecretObfuscator | undefined,
|
|
88
|
+
): void {
|
|
89
|
+
if (!settings.get("edit.streamingAbort")) return;
|
|
90
|
+
if (state.abortTriggered) return;
|
|
91
|
+
if (event.type !== "message_update") return;
|
|
92
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
93
|
+
if (assistantEvent.type !== "toolcall_end" && assistantEvent.type !== "toolcall_delta") return;
|
|
94
|
+
if (event.message.role !== "assistant") return;
|
|
95
|
+
|
|
96
|
+
const contentIndex = assistantEvent.contentIndex;
|
|
97
|
+
const messageContent = event.message.content;
|
|
98
|
+
if (!Array.isArray(messageContent) || contentIndex >= messageContent.length) return;
|
|
99
|
+
const toolCall = messageContent[contentIndex] as ToolCall;
|
|
100
|
+
if (toolCall.name !== "edit" || !toolCall.id) return;
|
|
101
|
+
|
|
102
|
+
const args = toolCall.arguments;
|
|
103
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return;
|
|
104
|
+
if ("old_text" in args || "new_text" in args) return;
|
|
105
|
+
|
|
106
|
+
const path = typeof args.path === "string" ? args.path : undefined;
|
|
107
|
+
const diff = typeof args.diff === "string" ? args.diff : undefined;
|
|
108
|
+
const op = typeof args.op === "string" ? args.op : undefined;
|
|
109
|
+
if (!path || !diff) return;
|
|
110
|
+
if (op && op !== "update") return;
|
|
111
|
+
|
|
112
|
+
if (!diff.includes("\n")) return;
|
|
113
|
+
const lastNewlineIndex = diff.lastIndexOf("\n");
|
|
114
|
+
if (lastNewlineIndex < 0) return;
|
|
115
|
+
const diffForCheck = diff.endsWith("\n") ? diff : diff.slice(0, lastNewlineIndex + 1);
|
|
116
|
+
if (diffForCheck.trim().length === 0) return;
|
|
117
|
+
|
|
118
|
+
let normalizedDiffResult = normalizeDiff(diffForCheck.replace(/\r/g, ""));
|
|
119
|
+
if (!normalizedDiffResult) return;
|
|
120
|
+
if (obfuscator) normalizedDiffResult = obfuscator.deobfuscate(normalizedDiffResult);
|
|
121
|
+
if (!normalizedDiffResult) return;
|
|
122
|
+
const lines = normalizedDiffResult.split("\n");
|
|
123
|
+
const hasChangeLine = lines.some(line => line.startsWith("+") || line.startsWith("-"));
|
|
124
|
+
if (!hasChangeLine) return;
|
|
125
|
+
|
|
126
|
+
const lineCount = lines.length;
|
|
127
|
+
const lastChecked = state.checkedLineCounts.get(toolCall.id);
|
|
128
|
+
if (lastChecked !== undefined && lineCount <= lastChecked) return;
|
|
129
|
+
state.checkedLineCounts.set(toolCall.id, lineCount);
|
|
130
|
+
|
|
131
|
+
const rename = typeof args.rename === "string" ? args.rename : undefined;
|
|
132
|
+
|
|
133
|
+
const removedLines = lines
|
|
134
|
+
.filter(line => line.startsWith("-") && !line.startsWith("--- "))
|
|
135
|
+
.map(line => line.slice(1));
|
|
136
|
+
if (removedLines.length > 0) {
|
|
137
|
+
const resolvedPath = resolveToCwd(path, cwd);
|
|
138
|
+
let cachedContent = state.fileCache.get(resolvedPath);
|
|
139
|
+
if (cachedContent === undefined) {
|
|
140
|
+
ensureFileCache(state, resolvedPath);
|
|
141
|
+
cachedContent = state.fileCache.get(resolvedPath);
|
|
142
|
+
}
|
|
143
|
+
if (cachedContent !== undefined) {
|
|
144
|
+
const missing = removedLines.find(line => !cachedContent.includes(normalizeToLF(line)));
|
|
145
|
+
if (missing) {
|
|
146
|
+
state.abortTriggered = true;
|
|
147
|
+
logger.warn("Streaming edit aborted due to patch preview failure", {
|
|
148
|
+
toolCallId: toolCall.id,
|
|
149
|
+
path,
|
|
150
|
+
error: `Failed to find expected lines in ${path}:\n${missing}`,
|
|
151
|
+
});
|
|
152
|
+
agent.abort();
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (assistantEvent.type === "toolcall_delta") return;
|
|
157
|
+
void checkRemovedLinesAsync(state, agent, toolCall.id, path, resolvedPath, removedLines);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (assistantEvent.type === "toolcall_delta") return;
|
|
162
|
+
void checkPreviewPatchAsync(state, agent, settings, cwd, toolCall.id, path, rename, normalizedDiffResult);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function checkRemovedLinesAsync(
|
|
166
|
+
state: StreamingEditState,
|
|
167
|
+
agent: Agent,
|
|
168
|
+
toolCallId: string,
|
|
169
|
+
path: string,
|
|
170
|
+
resolvedPath: string,
|
|
171
|
+
removedLines: string[],
|
|
172
|
+
): Promise<void> {
|
|
173
|
+
if (state.abortTriggered) return;
|
|
174
|
+
try {
|
|
175
|
+
const { text } = stripBom(await Bun.file(resolvedPath).text());
|
|
176
|
+
const normalizedContent = normalizeToLF(text);
|
|
177
|
+
const missing = removedLines.find(line => !normalizedContent.includes(normalizeToLF(line)));
|
|
178
|
+
if (missing) {
|
|
179
|
+
state.abortTriggered = true;
|
|
180
|
+
logger.warn("Streaming edit aborted due to patch preview failure", {
|
|
181
|
+
toolCallId,
|
|
182
|
+
path,
|
|
183
|
+
error: `Failed to find expected lines in ${path}:\n${missing}`,
|
|
184
|
+
});
|
|
185
|
+
agent.abort();
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
if (!isEnoent(err)) {
|
|
189
|
+
// Log unexpected errors but don't abort
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function checkPreviewPatchAsync(
|
|
195
|
+
state: StreamingEditState,
|
|
196
|
+
agent: Agent,
|
|
197
|
+
settings: Settings,
|
|
198
|
+
cwd: string,
|
|
199
|
+
toolCallId: string,
|
|
200
|
+
path: string,
|
|
201
|
+
rename: string | undefined,
|
|
202
|
+
normalizedDiff: string,
|
|
203
|
+
): Promise<void> {
|
|
204
|
+
if (state.abortTriggered) return;
|
|
205
|
+
try {
|
|
206
|
+
await previewPatch(
|
|
207
|
+
{ path, op: "update", rename, diff: normalizedDiff },
|
|
208
|
+
{
|
|
209
|
+
cwd,
|
|
210
|
+
allowFuzzy: settings.get("edit.fuzzyMatch"),
|
|
211
|
+
fuzzyThreshold: settings.get("edit.fuzzyThreshold"),
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (error instanceof ParseError) return;
|
|
216
|
+
state.abortTriggered = true;
|
|
217
|
+
logger.warn("Streaming edit aborted due to patch preview failure", {
|
|
218
|
+
toolCallId,
|
|
219
|
+
path,
|
|
220
|
+
error: error instanceof Error ? error.message : String(error),
|
|
221
|
+
});
|
|
222
|
+
agent.abort();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Rewrite tool call arguments in agent state and persisted session history.
|
|
228
|
+
*/
|
|
229
|
+
export async function rewriteToolCallArgs(
|
|
230
|
+
agent: Agent,
|
|
231
|
+
sessionManager: SessionManager,
|
|
232
|
+
toolCallId: string,
|
|
233
|
+
args: Record<string, unknown>,
|
|
234
|
+
): Promise<void> {
|
|
235
|
+
let updated = false;
|
|
236
|
+
const messages = agent.state.messages;
|
|
237
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
238
|
+
const msg = messages[i];
|
|
239
|
+
if (msg.role !== "assistant") continue;
|
|
240
|
+
const assistantMsg = msg as AssistantMessage;
|
|
241
|
+
if (!Array.isArray(assistantMsg.content)) continue;
|
|
242
|
+
for (const block of assistantMsg.content) {
|
|
243
|
+
if (typeof block !== "object" || block === null) continue;
|
|
244
|
+
if (!("type" in block) || (block as { type?: string }).type !== "toolCall") continue;
|
|
245
|
+
const toolCall = block as { id?: string; arguments?: Record<string, unknown> };
|
|
246
|
+
if (toolCall.id === toolCallId) {
|
|
247
|
+
toolCall.arguments = args;
|
|
248
|
+
updated = true;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (updated) break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (updated) {
|
|
256
|
+
await sessionManager.rewriteAssistantToolCallArgs(toolCallId, args);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
3
|
+
import type { Agent, AgentMessage } from "@nghyane/arcane-agent";
|
|
4
|
+
import type { AssistantMessage, ToolCall } from "@nghyane/arcane-ai";
|
|
5
|
+
import type { Rule } from "../capability/rule";
|
|
6
|
+
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
7
|
+
import type { TtsrManager, TtsrMatchContext } from "../export/ttsr";
|
|
8
|
+
import ttsrInterruptTemplate from "../prompts/system/ttsr-interrupt.md" with { type: "text" };
|
|
9
|
+
import type { SessionManager } from "./session-manager";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Mutable state for TTSR (time-traveling stream rules).
|
|
13
|
+
*/
|
|
14
|
+
export interface TtsrState {
|
|
15
|
+
pendingInjections: Rule[];
|
|
16
|
+
abortPending: boolean;
|
|
17
|
+
retryToken: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createTtsrState(): TtsrState {
|
|
21
|
+
return {
|
|
22
|
+
pendingInjections: [],
|
|
23
|
+
abortPending: false,
|
|
24
|
+
retryToken: 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Get TTSR injection payload and clear pending injections. */
|
|
29
|
+
export function getTtsrInjectionContent(state: TtsrState): { content: string; rules: Rule[] } | undefined {
|
|
30
|
+
if (state.pendingInjections.length === 0) return undefined;
|
|
31
|
+
const rules = state.pendingInjections;
|
|
32
|
+
const content = rules
|
|
33
|
+
.map(r => renderPromptTemplate(ttsrInterruptTemplate, { name: r.name, path: r.path, content: r.content }))
|
|
34
|
+
.join("\n\n");
|
|
35
|
+
state.pendingInjections = [];
|
|
36
|
+
return { content, rules };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function addPendingTtsrInjections(state: TtsrState, rules: Rule[]): void {
|
|
40
|
+
const seen = new Set(state.pendingInjections.map(rule => rule.name));
|
|
41
|
+
for (const rule of rules) {
|
|
42
|
+
if (seen.has(rule.name)) continue;
|
|
43
|
+
state.pendingInjections.push(rule);
|
|
44
|
+
seen.add(rule.name);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function extractTtsrRuleNames(details: unknown): string[] {
|
|
49
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const rules = (details as { rules?: unknown }).rules;
|
|
53
|
+
if (!Array.isArray(rules)) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
return rules.filter((ruleName): ruleName is string => typeof ruleName === "string");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function markTtsrInjected(
|
|
60
|
+
ttsrManager: TtsrManager | undefined,
|
|
61
|
+
sessionManager: SessionManager,
|
|
62
|
+
ruleNames: string[],
|
|
63
|
+
): void {
|
|
64
|
+
const uniqueRuleNames = Array.from(
|
|
65
|
+
new Set(ruleNames.map(ruleName => ruleName.trim()).filter(ruleName => ruleName.length > 0)),
|
|
66
|
+
);
|
|
67
|
+
if (uniqueRuleNames.length === 0) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
ttsrManager?.markInjectedByNames(uniqueRuleNames);
|
|
71
|
+
sessionManager.appendTtsrInjection(uniqueRuleNames);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function findTtsrAssistantIndex(messages: AgentMessage[], targetTimestamp: number | undefined): number {
|
|
75
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
76
|
+
const message = messages[i];
|
|
77
|
+
if (message.role !== "assistant") {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (targetTimestamp === undefined || message.timestamp === targetTimestamp) {
|
|
81
|
+
return i;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return -1;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function shouldInterruptForTtsrMatch(
|
|
88
|
+
ttsrManager: TtsrManager | undefined,
|
|
89
|
+
matchContext: TtsrMatchContext,
|
|
90
|
+
): boolean {
|
|
91
|
+
const mode = ttsrManager?.getSettings().interruptMode ?? "always";
|
|
92
|
+
if (mode === "never") {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (mode === "prose-only") {
|
|
96
|
+
return matchContext.source === "text" || matchContext.source === "thinking";
|
|
97
|
+
}
|
|
98
|
+
if (mode === "tool-only") {
|
|
99
|
+
return matchContext.source === "tool";
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function queueDeferredTtsrInjectionIfNeeded(
|
|
105
|
+
state: TtsrState,
|
|
106
|
+
agent: Agent,
|
|
107
|
+
assistantMsg: AssistantMessage,
|
|
108
|
+
): void {
|
|
109
|
+
if (state.abortPending || state.pendingInjections.length === 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (assistantMsg.stopReason === "aborted" || assistantMsg.stopReason === "error") {
|
|
113
|
+
state.pendingInjections = [];
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const injection = getTtsrInjectionContent(state);
|
|
118
|
+
if (!injection) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
agent.followUp({
|
|
122
|
+
role: "custom",
|
|
123
|
+
customType: "ttsr-injection",
|
|
124
|
+
content: injection.content,
|
|
125
|
+
display: false,
|
|
126
|
+
details: { rules: injection.rules.map(rule => rule.name) },
|
|
127
|
+
timestamp: Date.now(),
|
|
128
|
+
});
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
if (agent.state.isStreaming || !agent.hasQueuedMessages()) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
agent.continue().catch(() => {});
|
|
134
|
+
}, 0);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Build TTSR match context for tool call argument deltas. */
|
|
138
|
+
export function getTtsrToolMatchContext(message: AgentMessage, contentIndex: number, cwd: string): TtsrMatchContext {
|
|
139
|
+
const context: TtsrMatchContext = { source: "tool" };
|
|
140
|
+
if (message.role !== "assistant") {
|
|
141
|
+
return context;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const content = message.content;
|
|
145
|
+
if (!Array.isArray(content) || contentIndex < 0 || contentIndex >= content.length) {
|
|
146
|
+
return context;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const block = content[contentIndex];
|
|
150
|
+
if (!block || typeof block !== "object" || block.type !== "toolCall") {
|
|
151
|
+
return context;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const toolCall = block as ToolCall;
|
|
155
|
+
context.toolName = toolCall.name;
|
|
156
|
+
context.streamKey = toolCall.id ? `toolcall:${toolCall.id}` : `tool:${toolCall.name}:${contentIndex}`;
|
|
157
|
+
context.filePaths = extractTtsrFilePathsFromArgs(toolCall.arguments, cwd);
|
|
158
|
+
return context;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Extract path-like arguments from tool call payload for TTSR glob matching. */
|
|
162
|
+
export function extractTtsrFilePathsFromArgs(args: unknown, cwd: string): string[] | undefined {
|
|
163
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const rawPaths: string[] = [];
|
|
168
|
+
for (const [key, value] of Object.entries(args)) {
|
|
169
|
+
const normalizedKey = key.toLowerCase();
|
|
170
|
+
if (typeof value === "string" && (normalizedKey === "path" || normalizedKey.endsWith("path"))) {
|
|
171
|
+
rawPaths.push(value);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(value) && (normalizedKey === "paths" || normalizedKey.endsWith("paths"))) {
|
|
175
|
+
for (const candidate of value) {
|
|
176
|
+
if (typeof candidate === "string") {
|
|
177
|
+
rawPaths.push(candidate);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const normalizedPaths = rawPaths.flatMap(pathValue => normalizeTtsrPathCandidates(pathValue, cwd));
|
|
184
|
+
if (normalizedPaths.length === 0) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return Array.from(new Set(normalizedPaths));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Convert a path argument into stable relative/absolute candidates for glob checks. */
|
|
192
|
+
export function normalizeTtsrPathCandidates(rawPath: string, cwd: string): string[] {
|
|
193
|
+
const trimmed = rawPath.trim();
|
|
194
|
+
if (trimmed.length === 0) {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const normalizedInput = trimmed.replaceAll("\\", "/");
|
|
199
|
+
const candidates = new Set<string>([normalizedInput]);
|
|
200
|
+
if (normalizedInput.startsWith("./")) {
|
|
201
|
+
candidates.add(normalizedInput.slice(2));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const absolutePath = path.isAbsolute(trimmed) ? path.normalize(trimmed) : path.resolve(cwd, trimmed);
|
|
205
|
+
candidates.add(absolutePath.replaceAll("\\", "/"));
|
|
206
|
+
|
|
207
|
+
const relativePath = path.relative(cwd, absolutePath).replaceAll("\\", "/");
|
|
208
|
+
if (relativePath && relativePath !== "." && !relativePath.startsWith("../") && relativePath !== "..") {
|
|
209
|
+
candidates.add(relativePath);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return Array.from(candidates);
|
|
213
|
+
}
|
|
@@ -204,14 +204,6 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
204
204
|
runtime.ctx.editor.setText("");
|
|
205
205
|
},
|
|
206
206
|
},
|
|
207
|
-
{
|
|
208
|
-
name: "agents",
|
|
209
|
-
description: "Open Agent Control Center dashboard",
|
|
210
|
-
handle: (_command, runtime) => {
|
|
211
|
-
runtime.ctx.showAgentsDashboard();
|
|
212
|
-
runtime.ctx.editor.setText("");
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
207
|
{
|
|
216
208
|
name: "branch",
|
|
217
209
|
description: "Create a new branch from a previous message",
|
package/src/stt/recorder.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs/promises";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { logger, Snowflake } from "@nghyane/arcane-utils";
|
|
5
|
-
import {
|
|
5
|
+
import { $, type Subprocess } from "bun";
|
|
6
6
|
|
|
7
7
|
export interface RecordingHandle {
|
|
8
8
|
stop(): Promise<void>;
|
|
@@ -277,7 +277,7 @@ async function startPowerShellRecording(outputPath: string): Promise<RecordingHa
|
|
|
277
277
|
|
|
278
278
|
// ── Health check ───────────────────────────────────────────────────
|
|
279
279
|
|
|
280
|
-
async function verifyProcessAlive(proc:
|
|
280
|
+
async function verifyProcessAlive(proc: Subprocess, tool: string): Promise<void> {
|
|
281
281
|
await Bun.sleep(300);
|
|
282
282
|
|
|
283
283
|
const exited = await Promise.race([proc.exited.then(code => code), Bun.sleep(0).then(() => "running" as const)]);
|
package/src/system-prompt.ts
CHANGED
|
@@ -334,9 +334,7 @@ function getSystemInfoCachePath(): string {
|
|
|
334
334
|
async function loadGpuCache(): Promise<GpuCache | null> {
|
|
335
335
|
try {
|
|
336
336
|
const cachePath = getSystemInfoCachePath();
|
|
337
|
-
const
|
|
338
|
-
if (!(await file.exists())) return null;
|
|
339
|
-
const content = await file.json();
|
|
337
|
+
const content = await Bun.file(cachePath).json();
|
|
340
338
|
return content as GpuCache;
|
|
341
339
|
} catch {
|
|
342
340
|
return null;
|
|
@@ -470,8 +468,6 @@ export interface BuildSystemPromptOptions {
|
|
|
470
468
|
toolNames?: string[];
|
|
471
469
|
/** Text to append to system prompt. */
|
|
472
470
|
appendSystemPrompt?: string;
|
|
473
|
-
/** Repeat full tool descriptions in system prompt. Default: false */
|
|
474
|
-
repeatToolDescriptions?: boolean;
|
|
475
471
|
/** Skills settings for discovery. */
|
|
476
472
|
skillsSettings?: SkillsSettings;
|
|
477
473
|
/** Working directory. Default: getProjectDir() */
|
|
@@ -496,7 +492,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
496
492
|
customPrompt,
|
|
497
493
|
tools,
|
|
498
494
|
appendSystemPrompt,
|
|
499
|
-
repeatToolDescriptions = false,
|
|
500
495
|
skillsSettings,
|
|
501
496
|
toolNames,
|
|
502
497
|
cwd,
|
|
@@ -636,12 +631,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
636
631
|
toolNamesArray = defaultToolNames;
|
|
637
632
|
}
|
|
638
633
|
|
|
639
|
-
// Build tool descriptions for system prompt rendering
|
|
640
|
-
const toolDescriptions = toolNamesArray.map(name => ({
|
|
641
|
-
name,
|
|
642
|
-
description: tools?.get(name)?.description ?? "",
|
|
643
|
-
}));
|
|
644
|
-
|
|
645
634
|
// Filter skills to only include those with read tool
|
|
646
635
|
const hasRead = tools?.has("read");
|
|
647
636
|
const filteredSkills = preloadedSkills === undefined && hasRead ? skills : [];
|
|
@@ -667,8 +656,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
667
656
|
time("getEnvironmentInfo");
|
|
668
657
|
return renderPromptTemplate(systemPromptTemplate, {
|
|
669
658
|
tools: toolNamesArray,
|
|
670
|
-
toolDescriptions,
|
|
671
|
-
repeatToolDescriptions,
|
|
672
659
|
environment,
|
|
673
660
|
systemPromptCustomization: systemPromptCustomization ?? "",
|
|
674
661
|
contextFiles,
|
package/src/task/agents.ts
CHANGED
|
@@ -7,55 +7,28 @@ import { renderPromptTemplate } from "../config/prompt-templates";
|
|
|
7
7
|
import { parseAgentFields } from "../discovery/helpers";
|
|
8
8
|
import exploreMd from "../prompts/agents/explore.md" with { type: "text" };
|
|
9
9
|
// Embed agent markdown files at build time
|
|
10
|
-
import
|
|
10
|
+
import librarianMd from "../prompts/agents/librarian.md" with { type: "text" };
|
|
11
|
+
import oracleMd from "../prompts/agents/oracle.md" with { type: "text" };
|
|
11
12
|
import reviewerMd from "../prompts/agents/reviewer.md" with { type: "text" };
|
|
12
13
|
import taskMd from "../prompts/agents/task.md" with { type: "text" };
|
|
13
14
|
import { parseFrontmatter } from "../utils/frontmatter";
|
|
14
15
|
import type { AgentDefinition, AgentSource } from "./types";
|
|
15
16
|
|
|
16
|
-
interface AgentFrontmatter {
|
|
17
|
-
name: string;
|
|
18
|
-
description: string;
|
|
19
|
-
tools?: string[];
|
|
20
|
-
model?: string | string[];
|
|
21
|
-
thinkingLevel?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
17
|
interface EmbeddedAgentDef {
|
|
25
18
|
fileName: string;
|
|
26
|
-
frontmatter?: AgentFrontmatter;
|
|
27
19
|
template: string;
|
|
28
20
|
}
|
|
29
21
|
|
|
30
22
|
function buildAgentContent(def: EmbeddedAgentDef): string {
|
|
31
|
-
|
|
32
|
-
if (!def.frontmatter) return body;
|
|
33
|
-
return renderPromptTemplate(agentFrontmatterTemplate, { ...def.frontmatter, body });
|
|
23
|
+
return renderPromptTemplate(def.template);
|
|
34
24
|
}
|
|
35
25
|
|
|
36
26
|
const EMBEDDED_AGENT_DEFS: EmbeddedAgentDef[] = [
|
|
37
27
|
{ fileName: "explore.md", template: exploreMd },
|
|
28
|
+
{ fileName: "librarian.md", template: librarianMd },
|
|
29
|
+
{ fileName: "oracle.md", template: oracleMd },
|
|
38
30
|
{ fileName: "reviewer.md", template: reviewerMd },
|
|
39
|
-
{
|
|
40
|
-
fileName: "task.md",
|
|
41
|
-
frontmatter: {
|
|
42
|
-
name: "task",
|
|
43
|
-
description: "General-purpose subagent with full capabilities for delegated multi-step tasks",
|
|
44
|
-
model: "default",
|
|
45
|
-
thinkingLevel: "medium",
|
|
46
|
-
},
|
|
47
|
-
template: taskMd,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
fileName: "quick_task.md",
|
|
51
|
-
frontmatter: {
|
|
52
|
-
name: "quick_task",
|
|
53
|
-
description: "Low-reasoning agent for strictly mechanical updates or data collection only",
|
|
54
|
-
model: "arcane/fast",
|
|
55
|
-
thinkingLevel: "minimal",
|
|
56
|
-
},
|
|
57
|
-
template: taskMd,
|
|
58
|
-
},
|
|
31
|
+
{ fileName: "task.md", template: taskMd },
|
|
59
32
|
];
|
|
60
33
|
|
|
61
34
|
const EMBEDDED_AGENTS: { name: string; content: string }[] = EMBEDDED_AGENT_DEFS.map(def => ({
|
|
@@ -105,6 +78,7 @@ export function parseAgent(
|
|
|
105
78
|
}
|
|
106
79
|
return {
|
|
107
80
|
...fields,
|
|
81
|
+
kind: fields.kind ?? "hybrid",
|
|
108
82
|
systemPrompt: body,
|
|
109
83
|
source,
|
|
110
84
|
filePath,
|