@nghyane/arcane 0.1.15 → 0.1.17
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 +21 -0
- package/package.json +7 -15
- package/src/config/keybindings.ts +9 -7
- package/src/config/settings-schema.ts +19 -46
- package/src/config/settings.ts +0 -1
- package/src/exa/mcp-client.ts +57 -2
- package/src/internal-urls/docs-index.generated.ts +1 -2
- package/src/internal-urls/index.ts +2 -4
- package/src/internal-urls/router.ts +2 -2
- package/src/internal-urls/types.ts +2 -2
- package/src/mcp/oauth-flow.ts +1 -1
- package/src/modes/controllers/command-controller.ts +26 -64
- package/src/modes/utils/ui-helpers.ts +2 -1
- package/src/patch/hashline.ts +42 -0
- package/src/prompts/system/system-prompt.md +14 -10
- package/src/prompts/thread-extract.md +16 -0
- package/src/prompts/tools/render-mermaid.md +9 -0
- package/src/sdk.ts +1 -19
- package/src/session/agent-session.ts +4 -3
- package/src/session/retry-utils.ts +1 -1
- package/src/session/session-index.ts +329 -0
- package/src/slash-commands/builtin-registry.ts +0 -16
- package/src/task/index.ts +1 -1
- package/src/tools/ask.ts +9 -6
- package/src/tools/bash-skill-urls.ts +3 -3
- package/src/tools/create-tools.ts +26 -0
- package/src/tools/find-thread.ts +120 -0
- package/src/tools/index.ts +5 -0
- package/src/tools/read-thread.ts +409 -0
- package/src/tools/read.ts +2 -2
- package/src/tools/render-mermaid.ts +68 -0
- package/src/tools/save-memory.ts +182 -0
- package/src/web/search/index.ts +2 -0
- package/src/web/search/provider.ts +3 -0
- package/src/web/search/providers/anthropic.ts +1 -0
- package/src/web/search/providers/gemini.ts +122 -37
- package/src/web/search/providers/kagi.ts +163 -0
- package/src/web/search/types.ts +1 -0
- package/src/internal-urls/memory-protocol.ts +0 -133
- package/src/memories/index.ts +0 -1099
- package/src/memories/storage.ts +0 -563
- package/src/prompts/memories/consolidation.md +0 -30
- package/src/prompts/memories/read_path.md +0 -11
- package/src/prompts/memories/stage_one_input.md +0 -6
- package/src/prompts/memories/stage_one_system.md +0 -21
|
@@ -14,10 +14,10 @@ import { Snowflake } from "@nghyane/arcane-utils";
|
|
|
14
14
|
import { setProjectDir } from "@nghyane/arcane-utils/dirs";
|
|
15
15
|
import { $ } from "bun";
|
|
16
16
|
import { reset as resetCapabilities } from "../../capability";
|
|
17
|
+
import { formatKeyHint, type KeyId } from "../../config/keybindings";
|
|
17
18
|
import { loadCustomShare } from "../../export/custom-share";
|
|
18
19
|
import type { CompactOptions } from "../../extensibility/extensions/types";
|
|
19
20
|
import { getGatewayStatus } from "../../ipy/gateway-coordinator";
|
|
20
|
-
import { buildMemoryToolDeveloperInstructions, clearMemoryData, enqueueMemoryConsolidation } from "../../memories";
|
|
21
21
|
import { BashExecutionComponent } from "../../modes/components/bash-execution";
|
|
22
22
|
import { BorderedLoader } from "../../modes/components/bordered-loader";
|
|
23
23
|
import { DynamicBorder } from "../../modes/components/dynamic-border";
|
|
@@ -362,43 +362,44 @@ export class CommandController {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
handleHotkeysCommand(): void {
|
|
365
|
-
const
|
|
366
|
-
const
|
|
365
|
+
const k = (id: string) => formatKeyHint(id as KeyId);
|
|
366
|
+
const expandToolsKey = this.ctx.keybindings.getDisplayString("expandTools") || k("ctrl+o");
|
|
367
|
+
const sttKey = this.ctx.keybindings.getDisplayString("toggleSTT") || k("alt+h");
|
|
367
368
|
const hotkeys = `
|
|
368
369
|
**Navigation**
|
|
369
370
|
| Key | Action |
|
|
370
371
|
|-----|--------|
|
|
371
372
|
| \`Arrow keys\` | Move cursor / browse history (Up when empty) |
|
|
372
|
-
|
|
|
373
|
-
|
|
|
374
|
-
|
|
|
373
|
+
| \`${k("alt+left")}/${k("alt+right")}\` | Move by word |
|
|
374
|
+
| \`${k("ctrl+a")}\` / \`Home\` | Start of line |
|
|
375
|
+
| \`${k("ctrl+e")}\` / \`End\` | End of line |
|
|
375
376
|
|
|
376
377
|
**Editing**
|
|
377
378
|
| Key | Action |
|
|
378
379
|
|-----|--------|
|
|
379
380
|
| \`Enter\` | Send message |
|
|
380
|
-
|
|
|
381
|
-
|
|
|
382
|
-
|
|
|
383
|
-
|
|
|
381
|
+
| \`${k("shift+enter")}\` / \`${k("alt+enter")}\` | New line |
|
|
382
|
+
| \`${k("ctrl+w")}\` / \`${k("alt+backspace")}\` | Delete word backwards |
|
|
383
|
+
| \`${k("ctrl+u")}\` | Delete to start of line |
|
|
384
|
+
| \`${k("ctrl+k")}\` | Delete to end of line |
|
|
384
385
|
|
|
385
386
|
**Other**
|
|
386
387
|
| Key | Action |
|
|
387
388
|
|-----|--------|
|
|
388
389
|
| \`Tab\` | Path completion / accept autocomplete |
|
|
389
390
|
| \`Escape\` | Cancel autocomplete / abort streaming |
|
|
390
|
-
|
|
|
391
|
-
|
|
|
392
|
-
|
|
|
393
|
-
|
|
|
394
|
-
|
|
|
395
|
-
|
|
|
396
|
-
|
|
|
397
|
-
|
|
|
398
|
-
|
|
|
391
|
+
| \`${k("ctrl+c")}\` | Clear editor (first) / exit (second) |
|
|
392
|
+
| \`${k("ctrl+d")}\` | Exit (when editor is empty) |
|
|
393
|
+
| \`${k("ctrl+z")}\` | Suspend to background |
|
|
394
|
+
| \`${k("shift+tab")}\` | Cycle thinking level |
|
|
395
|
+
| \`${k("ctrl+p")}\` | Cycle role models (slow/default/fast) |
|
|
396
|
+
| \`${k("shift+ctrl+p")}\` | Cycle role models (temporary) |
|
|
397
|
+
| \`${k("alt+p")}\` | Select model (temporary) |
|
|
398
|
+
| \`${k("ctrl+l")}\` | Select model (set roles) |
|
|
399
|
+
| \`${k("ctrl+r")}\` | Search prompt history |
|
|
399
400
|
| \`${expandToolsKey}\` | Toggle tool output expansion |
|
|
400
|
-
|
|
|
401
|
-
|
|
|
401
|
+
| \`${k("ctrl+t")}\` | Toggle todo list expansion |
|
|
402
|
+
| \`${k("ctrl+g")}\` | Edit message in external editor |
|
|
402
403
|
| \`${sttKey}\` | Toggle speech-to-text recording |
|
|
403
404
|
| \`/\` | Slash commands |
|
|
404
405
|
| \`!\` | Run bash command |
|
|
@@ -415,49 +416,10 @@ export class CommandController {
|
|
|
415
416
|
this.ctx.ui.requestRender();
|
|
416
417
|
}
|
|
417
418
|
|
|
418
|
-
async handleMemoryCommand(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (action === "view") {
|
|
424
|
-
const payload = await buildMemoryToolDeveloperInstructions(agentDir, this.ctx.settings);
|
|
425
|
-
if (!payload) {
|
|
426
|
-
this.ctx.showWarning("Memory payload is empty (memories disabled or no memory summary found).");
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
430
|
-
this.ctx.chatContainer.addChild(new DynamicBorder());
|
|
431
|
-
this.ctx.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Memory Injection Payload")), 1, 0));
|
|
432
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
433
|
-
this.ctx.chatContainer.addChild(new Markdown(payload, 1, 1, getMarkdownTheme()));
|
|
434
|
-
this.ctx.chatContainer.addChild(new DynamicBorder());
|
|
435
|
-
this.ctx.ui.requestRender();
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (action === "reset" || action === "clear") {
|
|
440
|
-
try {
|
|
441
|
-
await clearMemoryData(agentDir, this.ctx.sessionManager.getCwd());
|
|
442
|
-
await this.ctx.session.refreshBaseSystemPrompt();
|
|
443
|
-
this.ctx.showStatus("Memory data cleared and system prompt refreshed.");
|
|
444
|
-
} catch (error) {
|
|
445
|
-
this.ctx.showError(`Memory clear failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
446
|
-
}
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
if (action === "enqueue" || action === "rebuild") {
|
|
451
|
-
try {
|
|
452
|
-
enqueueMemoryConsolidation(agentDir);
|
|
453
|
-
this.ctx.showStatus("Memory consolidation enqueued.");
|
|
454
|
-
} catch (error) {
|
|
455
|
-
this.ctx.showError(`Memory enqueue failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
456
|
-
}
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
this.ctx.showError("Usage: /memory <view|clear|reset|enqueue|rebuild>");
|
|
419
|
+
async handleMemoryCommand(_text: string): Promise<void> {
|
|
420
|
+
this.ctx.showWarning(
|
|
421
|
+
"The /memory command has been removed. Use save_memory tool or add facts to AGENTS.md directly.",
|
|
422
|
+
);
|
|
461
423
|
}
|
|
462
424
|
|
|
463
425
|
async handleClearCommand(): Promise<void> {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentMessage } from "@nghyane/arcane-agent";
|
|
2
2
|
import type { AssistantMessage, Message } from "@nghyane/arcane-ai";
|
|
3
3
|
import { Spacer, Text, TruncatedText } from "@nghyane/arcane-tui";
|
|
4
|
+
import { formatKeyHint, type KeyId } from "../../config/keybindings";
|
|
4
5
|
import { settings } from "../../config/settings";
|
|
5
6
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
6
7
|
import { BashExecutionComponent } from "../../modes/components/bash-execution";
|
|
@@ -385,7 +386,7 @@ export class UiHelpers {
|
|
|
385
386
|
const queuedText = theme.fg("dim", `${entry.label}: ${entry.message}`);
|
|
386
387
|
this.ctx.pendingMessagesContainer.addChild(new TruncatedText(queuedText, 1, 0));
|
|
387
388
|
}
|
|
388
|
-
const dequeueKey = this.ctx.keybindings.getDisplayString("dequeue") || "
|
|
389
|
+
const dequeueKey = this.ctx.keybindings.getDisplayString("dequeue") || formatKeyHint("alt+up" as KeyId);
|
|
389
390
|
const hintText = theme.fg("dim", `${theme.tree.hook} ${dequeueKey} to edit`);
|
|
390
391
|
this.ctx.pendingMessagesContainer.addChild(new TruncatedText(hintText, 1, 0));
|
|
391
392
|
}
|
package/src/patch/hashline.ts
CHANGED
|
@@ -566,6 +566,37 @@ export function validateLineRef(ref: { line: number; hash: string }, fileLines:
|
|
|
566
566
|
// Edit Application
|
|
567
567
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
568
568
|
|
|
569
|
+
/**
|
|
570
|
+
* Detect suspicious Unicode escape placeholders in edit lines.
|
|
571
|
+
* LLMs sometimes emit literal `\uDDDD` strings instead of actual Unicode characters.
|
|
572
|
+
* Returns a warning message if detected, undefined otherwise.
|
|
573
|
+
*/
|
|
574
|
+
function detectUnicodeEscapePlaceholders(lines: string[]): string | undefined {
|
|
575
|
+
for (const line of lines) {
|
|
576
|
+
if (/\\u[0-9A-Fa-f]{4}/.test(line)) {
|
|
577
|
+
return "Warning: edit content contains literal Unicode escape sequences (\\uXXXX). These may be intended as actual Unicode characters.";
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Auto-correct escaped tab indentation in edit lines.
|
|
585
|
+
* When enabled via ARCANE_HASHLINE_AUTOCORRECT_ESCAPED_TABS=1, replaces
|
|
586
|
+
* leading `\\t` sequences (literal backslash-t from JSON) with real tab characters.
|
|
587
|
+
*/
|
|
588
|
+
function autocorrectEscapedTabs(lines: string[]): string[] {
|
|
589
|
+
if (Bun.env.ARCANE_HASHLINE_AUTOCORRECT_ESCAPED_TABS !== "1") {
|
|
590
|
+
return lines;
|
|
591
|
+
}
|
|
592
|
+
return lines.map(line => {
|
|
593
|
+
const match = line.match(/^((?:\\t)+)/);
|
|
594
|
+
if (!match) return line;
|
|
595
|
+
const tabCount = match[1].length / 2; // each \\t is 2 chars
|
|
596
|
+
return "\t".repeat(tabCount) + line.slice(match[1].length);
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
569
600
|
/**
|
|
570
601
|
* Apply an array of hashline edits to file content.
|
|
571
602
|
*
|
|
@@ -599,6 +630,16 @@ export function applyHashlineEdits(
|
|
|
599
630
|
|
|
600
631
|
const autocorrect = Bun.env.ARCANE_HL_AUTOCORRECT === "1";
|
|
601
632
|
|
|
633
|
+
// Collect warnings and auto-correct edit content
|
|
634
|
+
const warnings: string[] = [];
|
|
635
|
+
for (const edit of edits) {
|
|
636
|
+
const unicodeWarning = detectUnicodeEscapePlaceholders(edit.content);
|
|
637
|
+
if (unicodeWarning && !warnings.includes(unicodeWarning)) {
|
|
638
|
+
warnings.push(unicodeWarning);
|
|
639
|
+
}
|
|
640
|
+
edit.content = autocorrectEscapedTabs(edit.content);
|
|
641
|
+
}
|
|
642
|
+
|
|
602
643
|
function collectExplicitlyTouchedLines(): Set<number> {
|
|
603
644
|
const touched = new Set<number>();
|
|
604
645
|
for (const edit of edits) {
|
|
@@ -914,6 +955,7 @@ export function applyHashlineEdits(
|
|
|
914
955
|
return {
|
|
915
956
|
content: finalContent,
|
|
916
957
|
firstChangedLine,
|
|
958
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
917
959
|
...(noopEdits.length > 0 ? { noopEdits } : {}),
|
|
918
960
|
};
|
|
919
961
|
|
|
@@ -162,6 +162,20 @@ Best practices:
|
|
|
162
162
|
- Run multiple sub-agents concurrently if tasks are independent with disjoint write targets.
|
|
163
163
|
{{/has}}
|
|
164
164
|
|
|
165
|
+
### Cross-session Knowledge
|
|
166
|
+
|
|
167
|
+
Tools: `find_thread`, `read_thread`, `save_memory`
|
|
168
|
+
**Proactive search triggers** — use `find_thread` when:
|
|
169
|
+
- User mentions past work: "we did this before", "last time", "in a previous session"
|
|
170
|
+
- User asks "what did we do about X" or "how did we solve Y"
|
|
171
|
+
- Task seems related to work that may have been done before
|
|
172
|
+
- Handoff context references a parent thread and you need more detail
|
|
173
|
+
**Do NOT search when:**
|
|
174
|
+
- Question is about current session context
|
|
175
|
+
- Generic coding question with no project-specific history
|
|
176
|
+
- User explicitly provides all needed context
|
|
177
|
+
**save_memory**: only when user says "remember this" or states a clear preference. If unsure, ask.
|
|
178
|
+
|
|
165
179
|
### Verification
|
|
166
180
|
After completing changes, verify using commands from AGENTS.md or the project's config. Format → typecheck/lint → test (if relevant) → build (if required).
|
|
167
181
|
Report evidence concisely: counts, pass/fail, error summary.
|
|
@@ -252,16 +266,6 @@ Scan descriptions vs task domain — read skill if ≥50% likely relevant.
|
|
|
252
266
|
</rules>
|
|
253
267
|
{{/if}}
|
|
254
268
|
|
|
255
|
-
{{#if memories.length}}
|
|
256
|
-
<memories>
|
|
257
|
-
{{#each memories}}
|
|
258
|
-
<memory path="{{path}}">
|
|
259
|
-
{{content}}
|
|
260
|
-
</memory>
|
|
261
|
-
{{/each}}
|
|
262
|
-
</memories>
|
|
263
|
-
{{/if}}
|
|
264
|
-
|
|
265
269
|
{{#if preloadedSkills.length}}
|
|
266
270
|
{{#each preloadedSkills}}
|
|
267
271
|
<skill name="{{name}}">
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
You are helping extract relevant information from a conversation thread based on a goal.
|
|
2
|
+
|
|
3
|
+
## Task
|
|
4
|
+
|
|
5
|
+
I am providing a conversation thread rendered as markdown, along with a goal describing what information to extract.
|
|
6
|
+
|
|
7
|
+
Your job is to:
|
|
8
|
+
1. Analyze the thread content
|
|
9
|
+
2. Identify information that is relevant to the goal
|
|
10
|
+
3. Extract and preserve those relevant parts with full fidelity
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
- Be concise but complete — include all relevant details
|
|
14
|
+
- Preserve code snippets, file paths, commands, and decisions exactly as they appear
|
|
15
|
+
- Omit pleasantries, failed attempts, and thinking-out-loud unless the goal asks for them
|
|
16
|
+
- If nothing relevant is found, say so briefly
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Convert Mermaid graph source into ASCII diagram output.
|
|
2
|
+
|
|
3
|
+
Parameters:
|
|
4
|
+
- `mermaid` (required): Mermaid graph text to render.
|
|
5
|
+
- `config` (optional): JSON render configuration (spacing and layout options).
|
|
6
|
+
Behavior:
|
|
7
|
+
- Returns ASCII diagram text.
|
|
8
|
+
- Saves full ASCII output to an artifact URL (`artifact://<id>`) when artifact storage is available.
|
|
9
|
+
- Returns an error when the Mermaid input is invalid or rendering fails.
|
package/src/sdk.ts
CHANGED
|
@@ -42,13 +42,11 @@ import {
|
|
|
42
42
|
ArtifactProtocolHandler,
|
|
43
43
|
DocsProtocolHandler,
|
|
44
44
|
InternalUrlRouter,
|
|
45
|
-
MemoryProtocolHandler,
|
|
46
45
|
RuleProtocolHandler,
|
|
47
46
|
SkillProtocolHandler,
|
|
48
47
|
} from "./internal-urls";
|
|
49
48
|
import { disposeAllKernelSessions } from "./ipy/executor";
|
|
50
49
|
import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp";
|
|
51
|
-
import { buildMemoryToolDeveloperInstructions, getMemoryRoot, startMemoryStartupTask } from "./memories";
|
|
52
50
|
import { collectEnvSecrets, loadSecrets, obfuscateMessages, SecretObfuscator } from "./secrets";
|
|
53
51
|
import { AgentSession } from "./session/agent-session";
|
|
54
52
|
import { AuthStorage } from "./session/auth-storage";
|
|
@@ -735,7 +733,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
735
733
|
settings,
|
|
736
734
|
};
|
|
737
735
|
|
|
738
|
-
// Initialize internal URL router for internal protocols (agent://, artifact://,
|
|
736
|
+
// Initialize internal URL router for internal protocols (agent://, artifact://, skill://, rule://)
|
|
739
737
|
const internalRouter = new InternalUrlRouter();
|
|
740
738
|
const getArtifactsDir = () => {
|
|
741
739
|
const sessionFile = sessionManager.getSessionFile();
|
|
@@ -743,11 +741,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
743
741
|
};
|
|
744
742
|
internalRouter.register(new AgentProtocolHandler({ getArtifactsDir }));
|
|
745
743
|
internalRouter.register(new ArtifactProtocolHandler({ getArtifactsDir }));
|
|
746
|
-
internalRouter.register(
|
|
747
|
-
new MemoryProtocolHandler({
|
|
748
|
-
getMemoryRoot: () => getMemoryRoot(agentDir, settings.getCwd()),
|
|
749
|
-
}),
|
|
750
|
-
);
|
|
751
744
|
internalRouter.register(
|
|
752
745
|
new SkillProtocolHandler({
|
|
753
746
|
getSkills: () => skills,
|
|
@@ -1027,7 +1020,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1027
1020
|
|
|
1028
1021
|
const rebuildSystemPrompt = async (toolNames: string[], tools: Map<string, AgentTool>): Promise<string> => {
|
|
1029
1022
|
toolContextStore.setToolNames(toolNames);
|
|
1030
|
-
const memoryInstructions = await buildMemoryToolDeveloperInstructions(agentDir, settings);
|
|
1031
1023
|
const defaultPrompt = await buildSystemPromptInternal({
|
|
1032
1024
|
cwd,
|
|
1033
1025
|
skills,
|
|
@@ -1037,7 +1029,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1037
1029
|
toolNames,
|
|
1038
1030
|
rules: rulebookRules,
|
|
1039
1031
|
skillsSettings: settings.getGroup("skills") as SkillsSettings,
|
|
1040
|
-
appendSystemPrompt: memoryInstructions,
|
|
1041
1032
|
});
|
|
1042
1033
|
|
|
1043
1034
|
if (options.systemPrompt === undefined) {
|
|
@@ -1054,7 +1045,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1054
1045
|
rules: rulebookRules,
|
|
1055
1046
|
skillsSettings: settings.getGroup("skills") as SkillsSettings,
|
|
1056
1047
|
customPrompt: options.systemPrompt,
|
|
1057
|
-
appendSystemPrompt: memoryInstructions,
|
|
1058
1048
|
});
|
|
1059
1049
|
}
|
|
1060
1050
|
return options.systemPrompt(defaultPrompt);
|
|
@@ -1258,14 +1248,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1258
1248
|
}
|
|
1259
1249
|
}
|
|
1260
1250
|
|
|
1261
|
-
startMemoryStartupTask({
|
|
1262
|
-
session,
|
|
1263
|
-
settings,
|
|
1264
|
-
modelRegistry,
|
|
1265
|
-
agentDir,
|
|
1266
|
-
isSubagent,
|
|
1267
|
-
});
|
|
1268
|
-
|
|
1269
1251
|
return {
|
|
1270
1252
|
session,
|
|
1271
1253
|
extensionsResult,
|
|
@@ -2067,9 +2067,10 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
2067
2067
|
return undefined;
|
|
2068
2068
|
}
|
|
2069
2069
|
|
|
2070
|
-
// Start a new session
|
|
2070
|
+
// Start a new session with parent reference
|
|
2071
|
+
const parentThreadId = this.sessionManager.getSessionId();
|
|
2071
2072
|
await this.sessionManager.flush();
|
|
2072
|
-
await this.sessionManager.newSession();
|
|
2073
|
+
await this.sessionManager.newSession({ parentSession: parentThreadId });
|
|
2073
2074
|
this.agent.reset();
|
|
2074
2075
|
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
2075
2076
|
this.#steeringMessages = [];
|
|
@@ -2078,7 +2079,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
2078
2079
|
this.#todoReminderCount = 0;
|
|
2079
2080
|
|
|
2080
2081
|
// Inject the handoff document as a custom message
|
|
2081
|
-
const handoffContent = `<handoff-context>\n${handoffText}\n</handoff-context>\n\nThe above is a handoff document from
|
|
2082
|
+
const handoffContent = `<handoff-context thread="${parentThreadId}">\n${handoffText}\n</handoff-context>\n\nThe above is a handoff document from thread \`${parentThreadId}\`. Use this context to continue the work seamlessly. If you need additional details not covered above, use \`read_thread("${parentThreadId}", "your specific question")\` to query the original session.`;
|
|
2082
2083
|
this.sessionManager.appendCustomMessageEntry("handoff", handoffContent, true);
|
|
2083
2084
|
|
|
2084
2085
|
// Rebuild agent messages from session
|
|
@@ -17,7 +17,7 @@ export function isRetryableErrorMessage(errorMessage: string): boolean {
|
|
|
17
17
|
* Check if an error message indicates a usage/billing limit (non-transient).
|
|
18
18
|
*/
|
|
19
19
|
export function isUsageLimitErrorMessage(errorMessage: string): boolean {
|
|
20
|
-
return /usage.?limit|usage_limit_reached|limit_reached/i.test(errorMessage);
|
|
20
|
+
return /usage.?limit|usage_limit_reached|limit_reached|quota.?exhaust/i.test(errorMessage);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|