@nexrall/code-core 1.4.12 → 1.4.14

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.
@@ -10,9 +10,13 @@ export interface SlashCommand {
10
10
  export declare function loadSlashCommands(workDir: string): SlashCommand[];
11
11
  export declare function findSlashCommand(cmds: SlashCommand[], name: string): SlashCommand | undefined;
12
12
  /**
13
- * Expand a command body into a final prompt: argument substitution, @file
14
- * inlining, and !`cmd` shell capture. Shell/file errors are inlined as notes
15
- * rather than thrown so the prompt is always produced.
13
+ * Expand a command/skill body into a final prompt: argument substitution,
14
+ * @file inlining, and !`cmd` shell capture. Shell/file errors are inlined as
15
+ * notes rather than thrown so the prompt is always produced. Shared by both
16
+ * SlashCommand (this file) and Skill (agent/skills.ts) — they have the same
17
+ * `body` shape, just a richer frontmatter set on the Skill side.
16
18
  */
19
+ export declare function expandBody(body: string, argString: string, workDir: string): string;
20
+ /** @deprecated kept for backward compatibility — use expandBody(cmd.body, ...) directly. */
17
21
  export declare function expandCommand(cmd: SlashCommand, argString: string, workDir: string): string;
18
22
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/commands/loader.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;CACrD;AAiFD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CASjE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAG7F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAuC3F"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/commands/loader.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;CACrD;AAiFD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CASjE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAG7F;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAuCnF;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3F"}
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.loadSlashCommands = loadSlashCommands;
37
37
  exports.findSlashCommand = findSlashCommand;
38
+ exports.expandBody = expandBody;
38
39
  exports.expandCommand = expandCommand;
39
40
  const fs = __importStar(require("fs"));
40
41
  const path = __importStar(require("path"));
@@ -140,14 +141,16 @@ function findSlashCommand(cmds, name) {
140
141
  return cmds.find((c) => c.name === want);
141
142
  }
142
143
  /**
143
- * Expand a command body into a final prompt: argument substitution, @file
144
- * inlining, and !`cmd` shell capture. Shell/file errors are inlined as notes
145
- * rather than thrown so the prompt is always produced.
144
+ * Expand a command/skill body into a final prompt: argument substitution,
145
+ * @file inlining, and !`cmd` shell capture. Shell/file errors are inlined as
146
+ * notes rather than thrown so the prompt is always produced. Shared by both
147
+ * SlashCommand (this file) and Skill (agent/skills.ts) — they have the same
148
+ * `body` shape, just a richer frontmatter set on the Skill side.
146
149
  */
147
- function expandCommand(cmd, argString, workDir) {
150
+ function expandBody(body, argString, workDir) {
148
151
  const args = argString.trim();
149
152
  const positional = args ? args.split(/\s+/) : [];
150
- let out = cmd.body;
153
+ let out = body;
151
154
  // !`shell command` → captured stdout
152
155
  out = out.replace(/!`([^`]+)`/g, (_m, c) => {
153
156
  try {
@@ -175,9 +178,13 @@ function expandCommand(cmd, argString, workDir) {
175
178
  const hadArgsToken = /\$ARGUMENTS/.test(out);
176
179
  out = out.replace(/\$ARGUMENTS/g, args);
177
180
  // If the template never referenced its args, append them so they aren't lost.
178
- if (!hadArgsToken && !/\$\d+/.test(cmd.body) && args) {
181
+ if (!hadArgsToken && !/\$\d+/.test(body) && args) {
179
182
  out = `${out}\n\n${args}`;
180
183
  }
181
184
  return out.trim();
182
185
  }
186
+ /** @deprecated kept for backward compatibility — use expandBody(cmd.body, ...) directly. */
187
+ function expandCommand(cmd, argString, workDir) {
188
+ return expandBody(cmd.body, argString, workDir);
189
+ }
183
190
  //# sourceMappingURL=loader.js.map
package/dist/index.d.ts CHANGED
@@ -8,6 +8,8 @@ export * from './agent/editCompleteness';
8
8
  export * from './agent/crossFile';
9
9
  export * from './agent/flaky';
10
10
  export * from './agent/claimEvidence';
11
+ export * from './agent/memory';
12
+ export * from './agent/skills';
11
13
  export * from './mcp/client';
12
14
  export * from './mcp/httpClient';
13
15
  export * from './mcp/manager';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -24,6 +24,8 @@ __exportStar(require("./agent/editCompleteness"), exports);
24
24
  __exportStar(require("./agent/crossFile"), exports);
25
25
  __exportStar(require("./agent/flaky"), exports);
26
26
  __exportStar(require("./agent/claimEvidence"), exports);
27
+ __exportStar(require("./agent/memory"), exports);
28
+ __exportStar(require("./agent/skills"), exports);
27
29
  __exportStar(require("./mcp/client"), exports);
28
30
  __exportStar(require("./mcp/httpClient"), exports);
29
31
  __exportStar(require("./mcp/manager"), exports);
@@ -10,7 +10,7 @@ export interface PluginInfo {
10
10
  /** Discover installed plugins (project scope shadows global on name clash). */
11
11
  export declare function loadPlugins(workDir: string): PluginInfo[];
12
12
  /** Subdirectories of every installed plugin that hold `kind` assets (existing only). */
13
- export declare function pluginAssetDirs(workDir: string, kind: 'commands' | 'agents'): string[];
13
+ export declare function pluginAssetDirs(workDir: string, kind: 'commands' | 'agents' | 'skills'): string[];
14
14
  /** Merge hooks.json from every plugin into a single hooks config (arrays concatenated). */
15
15
  export declare function pluginHooks(workDir: string): Record<string, unknown[]>;
16
16
  /** MCP server maps declared by plugins: { serverName → config } (first plugin wins on clash). */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC7B;AAsCD,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAKzD;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,EAAE,CAMtF;AAED,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAetE;AAED,iGAAiG;AACjG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC7B;AAsCD,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAKzD;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,EAAE,CAMjG;AAED,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAetE;AAED,iGAAiG;AACjG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazE"}
@@ -1 +1 @@
1
- {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/tools/executor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AA6iEtE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,WAAW,CAAC,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EAClC,OAAO,CAAC,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,CAAC,CAiBrB"}
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/tools/executor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAkkEtE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,WAAW,CAAC,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EAClC,OAAO,CAAC,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,CAAC,CAiBrB"}
@@ -44,6 +44,8 @@ const child_process_1 = require("child_process");
44
44
  const sandbox_1 = require("./sandbox");
45
45
  const auth_1 = require("../auth");
46
46
  const editCompleteness_1 = require("../agent/editCompleteness");
47
+ const memory_1 = require("../agent/memory");
48
+ const skills_1 = require("../agent/skills");
47
49
  const testIntegrity_1 = require("../agent/testIntegrity");
48
50
  const crossFile_1 = require("../agent/crossFile");
49
51
  const client_1 = require("../api/client");
@@ -2028,39 +2030,58 @@ async function stockPhoto(input, workDir) {
2028
2030
  return { output: `Found ${photos.length} photo(s) for "${query}":\n${lines.join('\n')}` };
2029
2031
  }
2030
2032
  // ─── Memory tools ─────────────────────────────────────────────────────────────
2031
- const MEMORY_FILE = path.join(os.homedir(), '.nexrall', 'memory.md');
2032
- async function memoryWrite(input) {
2033
+ // Implementation lives in agent/memory.ts (project vs global scope, size caps,
2034
+ // LLM-summarizing compaction, file locking) — this is just the tool-call glue.
2035
+ function memoryScopeOf(input) {
2036
+ return input.scope === 'global' ? 'global' : 'project';
2037
+ }
2038
+ async function memoryWrite(input, workDir) {
2033
2039
  const content = typeof input.content === 'string' ? input.content.trim() : '';
2034
2040
  if (!content)
2035
2041
  return { error: 'content is required' };
2036
- const dir = path.dirname(MEMORY_FILE);
2037
- fs.mkdirSync(dir, { recursive: true });
2038
- // Dedup: skip if an entry with the same first 60 chars already exists.
2039
- // The old check used full substring match which blocked "React + TypeScript"
2040
- // if "React" was already stored. Now we only deduplicate near-identical entries.
2041
- if (fs.existsSync(MEMORY_FILE)) {
2042
- const existing = fs.readFileSync(MEMORY_FILE, 'utf-8').toLowerCase();
2043
- const fingerprint = content.toLowerCase().slice(0, 60);
2044
- if (existing.includes(fingerprint)) {
2045
- return { output: `Memory already recorded (skipped duplicate): ${content}` };
2046
- }
2047
- }
2048
- const timestamp = new Date().toISOString().slice(0, 10);
2049
- const entry = `\n- [${timestamp}] ${content}`;
2050
- fs.appendFileSync(MEMORY_FILE, entry, 'utf-8');
2051
- return { output: `Memory saved: ${content}` };
2042
+ const scope = memoryScopeOf(input);
2043
+ const r = await (0, memory_1.writeMemory)(content, scope, workDir);
2044
+ if (!r.ok)
2045
+ return { error: 'failed to save memory' };
2046
+ return { output: r.already ? `Memory already recorded (skipped duplicate): ${content}` : `Memory saved (${scope}): ${content}` };
2052
2047
  }
2053
- async function memoryRead(_input) {
2048
+ async function memoryRead(input, workDir) {
2054
2049
  try {
2055
- if (!fs.existsSync(MEMORY_FILE))
2056
- return { output: 'No memories saved yet.' };
2057
- const content = fs.readFileSync(MEMORY_FILE, 'utf-8').trim();
2058
- return { output: content || 'No memories saved yet.' };
2050
+ // No explicit scope requested → merge both (what the model actually needs
2051
+ // when re-orienting mid-conversation); an explicit scope narrows to just that file.
2052
+ if (input && (input.scope === 'project' || input.scope === 'global')) {
2053
+ const mem = (0, memory_1.readMemory)(input.scope, workDir);
2054
+ return { output: mem || 'No memories saved yet.' };
2055
+ }
2056
+ const mem = (0, memory_1.readAllMemory)(workDir);
2057
+ return { output: mem || 'No memories saved yet.' };
2059
2058
  }
2060
2059
  catch (err) {
2061
2060
  return { error: err.message };
2062
2061
  }
2063
2062
  }
2063
+ // ─── Skills ───────────────────────────────────────────────────────────────────
2064
+ //
2065
+ // use_skill loads a skill's playbook into context so the model can follow it —
2066
+ // same idea as memory_read returning saved notes, just for a named, reusable
2067
+ // procedure instead of accumulated learnings. The expanded body (with
2068
+ // $ARGUMENTS/@file/!`cmd` substitution already applied) comes back as the tool
2069
+ // result; the model then proceeds using its normal tools to carry it out.
2070
+ async function useSkill(input, workDir) {
2071
+ const name = typeof input.name === 'string' ? input.name.trim() : '';
2072
+ if (!name)
2073
+ return { error: 'name is required (the skill to invoke)' };
2074
+ const dir = workDir ?? process.cwd();
2075
+ const skills = (0, skills_1.loadSkills)(dir);
2076
+ const skill = (0, skills_1.findSkill)(skills, name);
2077
+ if (!skill) {
2078
+ const known = skills.map((s) => s.name).join(', ') || '(none defined)';
2079
+ return { error: `Unknown skill "${name}". Available: ${known}.` };
2080
+ }
2081
+ const args = typeof input.args === 'string' ? input.args : '';
2082
+ const expanded = (0, skills_1.expandSkill)(skill, args, dir);
2083
+ return { output: `[Skill "${skill.name}" loaded — follow these instructions:]\n\n${expanded}` };
2084
+ }
2064
2085
  function semanticPreamble(input, workDir) {
2065
2086
  const p = typeof input.path === 'string' ? input.path : '';
2066
2087
  const line = typeof input.line === 'number' ? Math.floor(input.line) : 0;
@@ -2123,6 +2144,7 @@ const TOOL_MAP = {
2123
2144
  notebook_edit: notebookEdit,
2124
2145
  memory_write: memoryWrite,
2125
2146
  memory_read: memoryRead,
2147
+ use_skill: useSkill,
2126
2148
  bash_output: bashOutput,
2127
2149
  kill_shell: killShell,
2128
2150
  // LSP-lite fallback (regex-based). In VS Code these names are intercepted by
@@ -2144,6 +2166,7 @@ const WORKDIR_TOOLS = new Set([
2144
2166
  'generate_image', 'stock_photo',
2145
2167
  'get_symbols', 'get_workspace_symbols',
2146
2168
  'go_to_definition', 'find_references', 'get_hover',
2169
+ 'memory_write', 'memory_read', 'use_skill',
2147
2170
  ]);
2148
2171
  async function executeTool(name, input, abortSignal, sandbox, workDir, agentScope) {
2149
2172
  if (!TOOL_MAP[name])
package/dist/types.d.ts CHANGED
@@ -46,6 +46,24 @@ export interface SSEMessageCompleteEvent {
46
46
  export interface SSEUsageEvent {
47
47
  type: 'usage';
48
48
  usage: UsageInfo;
49
+ /**
50
+ * True when these tokens belong to an attempt that was CUT SHORT (the client
51
+ * disconnected or the turn was restarted) rather than to the turn's completed
52
+ * response. They were still billed — the upstream vendor charged for every token
53
+ * generated before the cut — so this is real spend, but a UI that shows "this
54
+ * turn's output" should not add it to the successful attempt's count or it will
55
+ * appear to double-report. Sum it for COST, keep it out of a single turn's token
56
+ * total. Absent/false for an ordinary completed turn.
57
+ */
58
+ partial?: boolean;
59
+ /**
60
+ * True when this usage is a RE-REPORT of a turn that was already billed. The previous
61
+ * attempt completed and was charged, but its `done` never reached the client, so the
62
+ * client retried and the backend served the stored answer from its turn-idempotency
63
+ * cache instead of re-invoking the model. No new charge occurred — a cost display must
64
+ * not add these tokens again. Mutually exclusive with `partial`.
65
+ */
66
+ replayed?: boolean;
49
67
  }
50
68
  export interface SSEDoneEvent {
51
69
  type: 'done';
@@ -97,7 +115,40 @@ export interface SSEBalanceStatusEvent {
97
115
  balance: number;
98
116
  zero: boolean;
99
117
  }
100
- export type SSEEvent = SSETextEvent | SSEToolUseEvent | SSEMessageCompleteEvent | SSEUsageEvent | SSEDoneEvent | SSEErrorEvent | SSEThinkingEvent | SSEThinkingProgressEvent | SSEThinkingDeltaEvent | SSERetryEvent | SSERetryResolvedEvent | SSEBalanceStatusEvent;
118
+ /**
119
+ * Emitted when a stream attempt died AFTER partial output had already been
120
+ * rendered, and the network layer is restarting the SAME turn from scratch.
121
+ *
122
+ * Unlike `retry` (which only ever fires when nothing was shown yet, so the UI
123
+ * merely needs a "reconnecting…" hint), this one carries a hard requirement:
124
+ * the consumer MUST discard the partial assistant output it has rendered for
125
+ * this turn — the text/thinking streamed so far will be re-sent in full by the
126
+ * new attempt, so keeping it would duplicate it on screen.
127
+ *
128
+ * LOCAL side effects are safe: tool calls are dispatched by the agent loop from
129
+ * the COMPLETED message, after `streamChat` returns, and a restart means it threw
130
+ * instead — so no tool ran, no file changed, no checkpoint or hook fired.
131
+ *
132
+ * BILLING IS NOT ROLLED BACK, and callers should not assume otherwise. The
133
+ * backend charges whatever tokens the model produced before the cut (its stream
134
+ * `abort` handler), because the upstream vendor charged us for them. A restarted
135
+ * turn therefore costs more than one attempt's worth of tokens — bounded by the
136
+ * reconnect budget, but real. The backend emits a `usage` event tagged
137
+ * `partial: true` for the discarded attempt so a cost display can stay accurate.
138
+ */
139
+ export interface SSEStreamRestartEvent {
140
+ type: 'stream_restart';
141
+ /** Human-readable cause, for an optional dim status line. */
142
+ reason: string;
143
+ /**
144
+ * How many characters of rendered output this attempt had already emitted — counting
145
+ * both visible text AND streamed thinking, since consumers display both and both must
146
+ * be discarded. Only ever 0 when the attempt rendered nothing at all, in which case a
147
+ * plain `retry` is emitted instead of this event.
148
+ */
149
+ discardedChars: number;
150
+ }
151
+ export type SSEEvent = SSETextEvent | SSEToolUseEvent | SSEMessageCompleteEvent | SSEUsageEvent | SSEDoneEvent | SSEErrorEvent | SSEThinkingEvent | SSEThinkingProgressEvent | SSEThinkingDeltaEvent | SSERetryEvent | SSERetryResolvedEvent | SSEStreamRestartEvent | SSEBalanceStatusEvent;
101
152
  export interface UsageInfo {
102
153
  input_tokens: number;
103
154
  output_tokens: number;
@@ -173,7 +224,13 @@ export interface AgentLoopOptions {
173
224
  onThinkingProgress?: (tokens: number) => void;
174
225
  onToolUse: (name: string, input: Record<string, unknown>, isSubTask?: boolean) => void;
175
226
  onToolResult: (name: string, result: ToolResult, isSubTask?: boolean) => void;
176
- onUsage: (u: UsageInfo) => void;
227
+ /**
228
+ * Token usage for a model call. `partial` marks tokens from an attempt that was cut
229
+ * short and restarted — real, already-billed spend that should be counted toward
230
+ * COST, but kept out of any "tokens this turn produced" display (see SSEUsageEvent).
231
+ * Optional second argument, so existing implementations keep compiling unchanged.
232
+ */
233
+ onUsage: (u: UsageInfo, partial?: boolean) => void;
177
234
  /**
178
235
  * Fired when the network layer is about to transparently retry a turn after
179
236
  * a transient disconnect (network drop, machine sleep/wake, overloaded
@@ -184,6 +241,17 @@ export interface AgentLoopOptions {
184
241
  onRetry?: (attempt: number, maxAttempts: number, reason: string) => void;
185
242
  /** Fired once a retried attempt starts receiving data again — clear any "reconnecting…" UI. */
186
243
  onRetryResolved?: () => void;
244
+ /**
245
+ * Fired when a stream died mid-render and the SAME turn is being restarted.
246
+ * The consumer MUST discard whatever partial assistant text/thinking it has
247
+ * already displayed for this turn — the new attempt re-sends it in full, so
248
+ * keeping the old fragment would duplicate it. No tool has executed at this
249
+ * point (tools only run after `message_complete`), so nothing beyond the
250
+ * rendered output needs undoing. If a caller does not implement this, the
251
+ * loop falls back to NOT retrying post-render failures, preserving the old
252
+ * (safe but turn-killing) behaviour.
253
+ */
254
+ onStreamRestart?: (reason: string, discardedChars: number) => void;
187
255
  /**
188
256
  * Fired when the backend reports the wallet balance dropped under the
189
257
  * low-balance threshold ($5) after billing a turn, or when a turn couldn't
@@ -270,4 +338,45 @@ export interface AgentLoopOptions {
270
338
  */
271
339
  _agentScope?: string;
272
340
  }
341
+ /**
342
+ * Thrown by `runAgentLoop` when a turn cannot continue (stream died, upstream
343
+ * error, etc.) but real work HAS already been completed in this turn.
344
+ *
345
+ * Why this exists: the loop operates on its own copy of the history
346
+ * (`[...initialMessages]`), so a plain `throw` used to leave the CALLER's array
347
+ * still holding only the original user message. An agent that had already run
348
+ * 25 tool calls over eight minutes would lose every one of them — the user was
349
+ * billed for the work, then had to start from scratch because "continue" had no
350
+ * memory of it. Carrying the partial history on the error lets the caller
351
+ * salvage it, so the very next "continue" resumes exactly where it stopped.
352
+ *
353
+ * `messages` is always a VALID history to resend: the loop only ever appends in
354
+ * complete (assistant → tool_result) pairs, so it never ends on an assistant
355
+ * turn with unanswered tool_use blocks.
356
+ */
357
+ export declare class AgentTurnError extends Error {
358
+ /** The turn's history up to the point of failure — safe to resend to the API. */
359
+ readonly messages: Message[];
360
+ /**
361
+ * How many turn steps completed before the failure (0 = nothing salvageable).
362
+ *
363
+ * Supplied by the loop as an explicit counter rather than derived from array
364
+ * lengths: auto-compaction SPLICES the history down mid-turn, so a long run that
365
+ * compacted and then died can end up SHORTER than it started. A length delta would
366
+ * read as "no progress" and discard exactly the expensive, long-running sessions
367
+ * this class exists to protect.
368
+ */
369
+ readonly progressCount: number;
370
+ /** The original underlying error, if any. */
371
+ readonly cause?: unknown;
372
+ constructor(message: string, messages: Message[], progressCount: number, cause?: unknown);
373
+ }
374
+ /** Type guard — narrows an unknown catch binding to an AgentTurnError. */
375
+ export declare function isAgentTurnError(err: unknown): err is AgentTurnError;
376
+ /**
377
+ * Salvage a partial history out of a failed turn. Returns the partial history
378
+ * when the error carried one AND real progress was made, otherwise `null`.
379
+ * Callers use it as: `messages = salvageHistory(err) ?? messages;`
380
+ */
381
+ export declare function salvageHistory(err: unknown): Message[] | null;
273
382
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -1,4 +1,48 @@
1
1
  "use strict";
2
2
  // ─── Content Blocks ───────────────────────────────────────────────────────────
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.AgentTurnError = void 0;
5
+ exports.isAgentTurnError = isAgentTurnError;
6
+ exports.salvageHistory = salvageHistory;
7
+ // ─── Turn failure that preserves work already done ───────────────────────────
8
+ /**
9
+ * Thrown by `runAgentLoop` when a turn cannot continue (stream died, upstream
10
+ * error, etc.) but real work HAS already been completed in this turn.
11
+ *
12
+ * Why this exists: the loop operates on its own copy of the history
13
+ * (`[...initialMessages]`), so a plain `throw` used to leave the CALLER's array
14
+ * still holding only the original user message. An agent that had already run
15
+ * 25 tool calls over eight minutes would lose every one of them — the user was
16
+ * billed for the work, then had to start from scratch because "continue" had no
17
+ * memory of it. Carrying the partial history on the error lets the caller
18
+ * salvage it, so the very next "continue" resumes exactly where it stopped.
19
+ *
20
+ * `messages` is always a VALID history to resend: the loop only ever appends in
21
+ * complete (assistant → tool_result) pairs, so it never ends on an assistant
22
+ * turn with unanswered tool_use blocks.
23
+ */
24
+ class AgentTurnError extends Error {
25
+ constructor(message, messages, progressCount, cause) {
26
+ super(message);
27
+ this.name = 'AgentTurnError';
28
+ this.messages = messages;
29
+ this.progressCount = Math.max(0, progressCount);
30
+ this.cause = cause;
31
+ }
32
+ }
33
+ exports.AgentTurnError = AgentTurnError;
34
+ /** Type guard — narrows an unknown catch binding to an AgentTurnError. */
35
+ function isAgentTurnError(err) {
36
+ return err instanceof AgentTurnError;
37
+ }
38
+ /**
39
+ * Salvage a partial history out of a failed turn. Returns the partial history
40
+ * when the error carried one AND real progress was made, otherwise `null`.
41
+ * Callers use it as: `messages = salvageHistory(err) ?? messages;`
42
+ */
43
+ function salvageHistory(err) {
44
+ if (!isAgentTurnError(err))
45
+ return null;
46
+ return err.progressCount > 0 ? err.messages : null;
47
+ }
4
48
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexrall/code-core",
3
- "version": "1.4.12",
3
+ "version": "1.4.14",
4
4
  "description": "Core agent loop, tools, and extension primitives for Nexrall Code — embed an AI coding agent in any Node.js application.",
5
5
  "license": "MIT",
6
6
  "author": "Nexrall <support@nexrall.com> (https://nexrall.com)",