@nuvin/session 0.2.0-rc.9 → 0.2.1-rc.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.
@@ -1,5 +1,127 @@
1
- // src/state/tool-preview.ts
1
+ // src/state/file-edit-preview.ts
2
+ function asObj(value) {
3
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
4
+ return value;
5
+ }
2
6
  function str(obj, key) {
7
+ if (!obj) return void 0;
8
+ const value = obj[key];
9
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
10
+ }
11
+ function rawString(obj, key) {
12
+ if (!obj) return void 0;
13
+ const value = obj[key];
14
+ return typeof value === "string" ? value : void 0;
15
+ }
16
+ function bool(obj, key) {
17
+ if (!obj) return void 0;
18
+ const value = obj[key];
19
+ return typeof value === "boolean" ? value : void 0;
20
+ }
21
+ var LINE_NUMBER_KEYS = [
22
+ "oldStartLine",
23
+ "oldEndLine",
24
+ "newStartLine",
25
+ "newEndLine",
26
+ "oldLineCount",
27
+ "newLineCount"
28
+ ];
29
+ function lineNumbers(value) {
30
+ const obj = asObj(value);
31
+ if (!obj) return void 0;
32
+ const candidate = {};
33
+ for (const key of LINE_NUMBER_KEYS) {
34
+ const number = obj[key];
35
+ if (typeof number !== "number" || !Number.isSafeInteger(number)) return void 0;
36
+ candidate[key] = number;
37
+ }
38
+ return candidate;
39
+ }
40
+ var ANCHORED_FILE_ERROR_CODES = [
41
+ "invalid_patch",
42
+ "unknown_snapshot",
43
+ "ambiguous_snapshot_tag",
44
+ "stale_snapshot",
45
+ "unseen_lines",
46
+ "invalid_range",
47
+ "overlapping_operations",
48
+ "no_change",
49
+ "write_failed",
50
+ "post_write_drift"
51
+ ];
52
+ function errorCode(obj) {
53
+ if (!obj) return void 0;
54
+ const value = obj.error;
55
+ return typeof value === "string" && ANCHORED_FILE_ERROR_CODES.includes(value) ? value : void 0;
56
+ }
57
+ var ANCHORED_MODES = ["anchored-json", "hashline"];
58
+ function canonicalMode(value) {
59
+ return typeof value === "string" && ANCHORED_MODES.includes(value) ? value : void 0;
60
+ }
61
+ function canonicalBlock(value) {
62
+ const obj = asObj(value);
63
+ if (!obj) return void 0;
64
+ const oldText = rawString(obj, "oldText");
65
+ const newText = rawString(obj, "newText");
66
+ if (oldText === void 0 || newText === void 0) return void 0;
67
+ const blockLineNumbers = lineNumbers(obj.lineNumbers);
68
+ return blockLineNumbers === void 0 ? { oldText, newText } : { oldText, newText, lineNumbers: blockLineNumbers };
69
+ }
70
+ function canonicalPreview(value) {
71
+ const obj = asObj(value);
72
+ if (!obj) return void 0;
73
+ const mode = canonicalMode(obj.mode);
74
+ const filePath = str(obj, "filePath");
75
+ if (mode === void 0 || filePath === void 0) return void 0;
76
+ if (str(obj, "canonicalPath") === void 0 || str(obj, "tag") === void 0 || str(obj, "digest") === void 0 || typeof obj.operationCount !== "number" || !Array.isArray(obj.operations) || !Array.isArray(obj.blocks)) {
77
+ return void 0;
78
+ }
79
+ const blocks = [];
80
+ for (const block of obj.blocks) {
81
+ const parsed = canonicalBlock(block);
82
+ if (parsed !== void 0) blocks.push(parsed);
83
+ }
84
+ if (blocks.length === 0) return void 0;
85
+ return { mode, filePath, blocks };
86
+ }
87
+ function withMetadata(base, input, structured) {
88
+ const description = str(input, "description");
89
+ const dryRun = bool(structured, "dryRun") ?? bool(input, "dryRun");
90
+ const code = errorCode(structured);
91
+ return {
92
+ ...base,
93
+ ...description === void 0 ? {} : { description },
94
+ ...dryRun === void 0 ? {} : { dryRun },
95
+ ...code === void 0 ? {} : { errorCode: code }
96
+ };
97
+ }
98
+ function exactPreview(input, structured) {
99
+ const oldText = rawString(input, "oldText");
100
+ const newText = rawString(input, "newText");
101
+ if (oldText === void 0 || newText === void 0) return void 0;
102
+ const exactLineNumbers = lineNumbers(structured?.lineNumbers) ?? lineNumbers(input?.lineNumbers);
103
+ const block = exactLineNumbers === void 0 ? { oldText, newText } : { oldText, newText, lineNumbers: exactLineNumbers };
104
+ const filePath = str(input, "filePath");
105
+ const base = { mode: "exact", blocks: [block] };
106
+ if (filePath !== void 0) base.filePath = filePath;
107
+ return withMetadata(base, input, structured);
108
+ }
109
+ function extractFileEditPreview(input, structured, options) {
110
+ const inputObj = asObj(input);
111
+ const structuredObj = asObj(structured);
112
+ const exact = exactPreview(inputObj, structuredObj);
113
+ if (exact !== void 0) return exact;
114
+ const completed = canonicalPreview(structuredObj?.preview);
115
+ if (completed !== void 0) return withMetadata(completed, inputObj, structuredObj);
116
+ if (options?.allowApprovalInputPreview === true) {
117
+ const approval = canonicalPreview(inputObj?.preview);
118
+ if (approval !== void 0) return withMetadata(approval, inputObj, structuredObj);
119
+ }
120
+ return void 0;
121
+ }
122
+
123
+ // src/state/tool-preview.ts
124
+ function str2(obj, key) {
3
125
  if (!obj) return void 0;
4
126
  const v = obj[key];
5
127
  return typeof v === "string" && v.trim().length > 0 ? v.trim() : void 0;
@@ -9,17 +131,21 @@ function num(obj, key) {
9
131
  const v = obj[key];
10
132
  return typeof v === "number" ? v : void 0;
11
133
  }
12
- function bool(obj, key) {
134
+ function bool2(obj, key) {
13
135
  return obj?.[key] === true;
14
136
  }
15
- function asObj(value) {
137
+ function asObj2(value) {
16
138
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
17
139
  return value;
18
140
  }
141
+ function getMemoryToolInput(value) {
142
+ const outer = asObj2(value);
143
+ return asObj2(outer?.input) ?? outer;
144
+ }
19
145
  function parseMcpToolIdentity(toolName, structured) {
20
- const obj = asObj(structured);
21
- const structuredServer = str(obj, "serverName");
22
- const structuredTool = str(obj, "toolName");
146
+ const obj = asObj2(structured);
147
+ const structuredServer = str2(obj, "serverName");
148
+ const structuredTool = str2(obj, "toolName");
23
149
  if (structuredServer && structuredTool) {
24
150
  return { serverName: structuredServer, toolName: structuredTool };
25
151
  }
@@ -45,9 +171,9 @@ function parseMcpToolIdentity(toolName, structured) {
45
171
  };
46
172
  }
47
173
  var Bash_fmt = (msg) => {
48
- const input = asObj(msg.input);
49
- const command = str(input, "command") ?? msg.summary;
50
- const cwd = str(input, "cwd");
174
+ const input = asObj2(msg.input);
175
+ const command = str2(input, "command") ?? msg.summary;
176
+ const cwd = str2(input, "cwd");
51
177
  const mainArg = cwd ? `${command} at ${cwd}` : command;
52
178
  let phrase = "Ran command";
53
179
  if (msg.status === "pending") phrase = "Waiting to run command";
@@ -58,9 +184,9 @@ var Bash_fmt = (msg) => {
58
184
  return { mainArg, phrase };
59
185
  };
60
186
  var WebSearch_fmt = (msg) => {
61
- const input = asObj(msg.input);
62
- const structured = asObj(msg.structured);
63
- const query = str(input, "query") ?? msg.summary;
187
+ const input = asObj2(msg.input);
188
+ const structured = asObj2(msg.structured);
189
+ const query = str2(input, "query") ?? msg.summary;
64
190
  let phrase;
65
191
  if (msg.status === "pending") phrase = "Waiting to search web";
66
192
  else if (msg.status === "running" || msg.status === "approved") phrase = "Searching web";
@@ -73,23 +199,23 @@ var WebSearch_fmt = (msg) => {
73
199
  return { mainArg: query, phrase };
74
200
  };
75
201
  var WebFetch_fmt = (msg) => {
76
- const input = asObj(msg.input);
77
- const structured = asObj(msg.structured);
78
- const url = str(input, "url") ?? msg.summary;
202
+ const input = asObj2(msg.input);
203
+ const structured = asObj2(msg.structured);
204
+ const url = str2(input, "url") ?? msg.summary;
79
205
  let phrase;
80
206
  if (msg.status === "pending") phrase = "Waiting to fetch URL";
81
207
  else if (msg.status === "running" || msg.status === "approved") phrase = "Fetching URL";
82
208
  else if (msg.status === "rejected") phrase = "Skipped fetch";
83
209
  else if (msg.status === "error") phrase = "Fetch failed";
84
210
  else {
85
- const format = str(structured, "format");
211
+ const format = str2(structured, "format");
86
212
  phrase = format ? `Fetched ${format}` : "Fetched";
87
213
  }
88
214
  return { mainArg: url, phrase };
89
215
  };
90
216
  var FileRead_fmt = (msg) => {
91
- const input = asObj(msg.input);
92
- const filePath = str(input, "path") ?? msg.summary;
217
+ const input = asObj2(msg.input);
218
+ const filePath = str2(input, "path") ?? msg.summary;
93
219
  const lineStart = num(input, "lineStart");
94
220
  const lineEnd = num(input, "lineEnd");
95
221
  let mainArg = filePath;
@@ -104,9 +230,10 @@ var FileRead_fmt = (msg) => {
104
230
  return { mainArg, phrase };
105
231
  };
106
232
  var FileEdit_fmt = (msg) => {
107
- const input = asObj(msg.input);
108
- const structured = asObj(msg.structured);
109
- const filePath = str(structured, "filePath") ?? str(input, "filePath") ?? msg.summary;
233
+ const input = asObj2(msg.input);
234
+ const structured = asObj2(msg.structured);
235
+ const preview = extractFileEditPreview(msg.input, msg.structured);
236
+ const filePath = preview?.filePath ?? str2(structured, "filePath") ?? str2(input, "filePath") ?? msg.summary;
110
237
  const bytesWritten = num(structured, "bytesWritten");
111
238
  let phrase;
112
239
  if (msg.status === "error") phrase = "Edit failed";
@@ -117,9 +244,9 @@ var FileEdit_fmt = (msg) => {
117
244
  return { mainArg: filePath, phrase };
118
245
  };
119
246
  var FileNew_fmt = (msg) => {
120
- const input = asObj(msg.input);
121
- const structured = asObj(msg.structured);
122
- const filePath = str(structured, "filePath") ?? str(input, "filePath") ?? msg.summary;
247
+ const input = asObj2(msg.input);
248
+ const structured = asObj2(msg.structured);
249
+ const filePath = str2(structured, "filePath") ?? str2(input, "filePath") ?? msg.summary;
123
250
  const lines = num(structured, "lines");
124
251
  const bytes = num(structured, "bytes");
125
252
  let phrase = "Created file";
@@ -134,10 +261,10 @@ var FileNew_fmt = (msg) => {
134
261
  return { mainArg: filePath, phrase };
135
262
  };
136
263
  var Grep_fmt = (msg) => {
137
- const input = asObj(msg.input);
138
- const structured = asObj(msg.structured);
139
- const pattern = str(input, "pattern") ?? "";
140
- const searchPath = str(input, "path");
264
+ const input = asObj2(msg.input);
265
+ const structured = asObj2(msg.structured);
266
+ const pattern = str2(input, "pattern") ?? "";
267
+ const searchPath = str2(input, "path");
141
268
  const mainArg = searchPath ? `${pattern} at ${searchPath}` : pattern || msg.summary;
142
269
  let phrase;
143
270
  if (msg.status === "pending") phrase = "Waiting to search";
@@ -147,7 +274,7 @@ var Grep_fmt = (msg) => {
147
274
  else {
148
275
  const matchCount = num(structured, "matchCount");
149
276
  const fileCount = num(structured, "fileCount");
150
- const truncated = bool(structured, "truncated");
277
+ const truncated = bool2(structured, "truncated");
151
278
  if (matchCount === 0) phrase = "Not found";
152
279
  else if (matchCount === void 0) phrase = "Search complete";
153
280
  else {
@@ -159,9 +286,9 @@ var Grep_fmt = (msg) => {
159
286
  return { mainArg, phrase };
160
287
  };
161
288
  var Glob_fmt = (msg) => {
162
- const input = asObj(msg.input);
163
- const structured = asObj(msg.structured);
164
- const pattern = str(input, "pattern") ?? msg.summary;
289
+ const input = asObj2(msg.input);
290
+ const structured = asObj2(msg.structured);
291
+ const pattern = str2(input, "pattern") ?? msg.summary;
165
292
  let phrase;
166
293
  if (msg.status === "pending") phrase = "Waiting to find files";
167
294
  else if (msg.status === "running" || msg.status === "approved") phrase = "Finding files";
@@ -169,16 +296,16 @@ var Glob_fmt = (msg) => {
169
296
  else if (msg.status === "error") phrase = "Search failed";
170
297
  else {
171
298
  const count = num(structured, "count");
172
- const truncated = bool(structured, "truncated");
299
+ const truncated = bool2(structured, "truncated");
173
300
  phrase = count !== void 0 ? `Found ${count} file${count === 1 ? "" : "s"}` : "Search complete";
174
301
  if (truncated) phrase += " (truncated)";
175
302
  }
176
303
  return { mainArg: pattern, phrase };
177
304
  };
178
305
  var Ls_fmt = (msg) => {
179
- const input = asObj(msg.input);
180
- const structured = asObj(msg.structured);
181
- const directoryPath = str(input, "path") ?? ".";
306
+ const input = asObj2(msg.input);
307
+ const structured = asObj2(msg.structured);
308
+ const directoryPath = str2(input, "path") ?? ".";
182
309
  let phrase;
183
310
  if (msg.status === "pending") phrase = "Waiting to list directory";
184
311
  else if (msg.status === "running" || msg.status === "approved") phrase = "Listing directory";
@@ -186,18 +313,18 @@ var Ls_fmt = (msg) => {
186
313
  else if (msg.status === "error") phrase = "Listing failed";
187
314
  else {
188
315
  const total = num(structured, "total");
189
- const truncated = bool(structured, "truncated");
316
+ const truncated = bool2(structured, "truncated");
190
317
  phrase = total !== void 0 ? `Listed ${total} entries` : "Listed";
191
318
  if (truncated) phrase += " (truncated)";
192
319
  }
193
320
  return { mainArg: directoryPath, phrase };
194
321
  };
195
322
  var Lsp_fmt = (msg) => {
196
- const input = asObj(msg.input);
197
- const filePath = str(input, "filePath") ?? msg.summary;
323
+ const input = asObj2(msg.input);
324
+ const filePath = str2(input, "filePath") ?? msg.summary;
198
325
  const line = num(input, "line");
199
326
  const character = num(input, "character");
200
- const operation = str(input, "operation") ?? "lsp";
327
+ const operation = str2(input, "operation") ?? "lsp";
201
328
  let mainArg = filePath;
202
329
  if (line !== void 0 && character !== void 0) mainArg = `${filePath}:${line}:${character}`;
203
330
  else if (line !== void 0) mainArg = `${filePath}:${line}`;
@@ -210,8 +337,8 @@ var Lsp_fmt = (msg) => {
210
337
  return { mainArg, phrase };
211
338
  };
212
339
  var LoadSkill_fmt = (msg) => {
213
- const input = asObj(msg.input);
214
- const name = str(input, "name") ?? msg.summary;
340
+ const input = asObj2(msg.input);
341
+ const name = str2(input, "name") ?? msg.summary;
215
342
  let phrase;
216
343
  if (msg.status === "pending") phrase = "Waiting to load skill";
217
344
  else if (msg.status === "running" || msg.status === "approved") phrase = "Loading skill";
@@ -221,8 +348,8 @@ var LoadSkill_fmt = (msg) => {
221
348
  return { mainArg: name, phrase };
222
349
  };
223
350
  var ViewFile_fmt = (msg) => {
224
- const input = asObj(msg.input);
225
- const filePath = str(input, "path") ?? msg.summary;
351
+ const input = asObj2(msg.input);
352
+ const filePath = str2(input, "path") ?? msg.summary;
226
353
  let phrase;
227
354
  if (msg.status === "pending") phrase = "Waiting to view image";
228
355
  else if (msg.status === "running" || msg.status === "approved") phrase = "Viewing image";
@@ -232,8 +359,8 @@ var ViewFile_fmt = (msg) => {
232
359
  return { mainArg: filePath, phrase };
233
360
  };
234
361
  var TodoWrite_fmt = (msg) => {
235
- const input = asObj(msg.input);
236
- const structured = asObj(msg.structured);
362
+ const input = asObj2(msg.input);
363
+ const structured = asObj2(msg.structured);
237
364
  const todos = input?.todos ?? input?.items;
238
365
  const count = Array.isArray(todos) ? todos.length : void 0;
239
366
  const mainArg = count === void 0 ? msg.summary : `${count} item${count === 1 ? "" : "s"}`;
@@ -243,18 +370,18 @@ var TodoWrite_fmt = (msg) => {
243
370
  else if (msg.status === "rejected") phrase = "Skipped todo update";
244
371
  else if (msg.status === "error") phrase = "Todo update failed";
245
372
  else {
246
- const stats = asObj(structured?.stats);
373
+ const stats = asObj2(structured?.stats);
247
374
  const completed = num(stats, "completed");
248
375
  const total = num(stats, "total");
249
- const progress = str(structured, "progress");
376
+ const progress = str2(structured, "progress");
250
377
  phrase = completed !== void 0 && total !== void 0 && progress ? `Updated todos (${completed}/${total} - ${progress})` : "Updated todos";
251
378
  }
252
379
  return { mainArg, phrase };
253
380
  };
254
381
  var UpdateTopic_fmt = (msg) => {
255
- const input = asObj(msg.input);
256
- const structured = asObj(msg.structured);
257
- const mainArg = str(structured, "topic") ?? str(input, "topic") ?? (msg.summary || void 0);
382
+ const input = asObj2(msg.input);
383
+ const structured = asObj2(msg.structured);
384
+ const mainArg = str2(structured, "topic") ?? str2(input, "topic") ?? (msg.summary || void 0);
258
385
  let phrase = "Updated topic";
259
386
  if (msg.status === "pending") phrase = "Waiting to update topic";
260
387
  else if (msg.status === "running" || msg.status === "approved") phrase = "Updating topic";
@@ -263,10 +390,10 @@ var UpdateTopic_fmt = (msg) => {
263
390
  return { mainArg, phrase };
264
391
  };
265
392
  var InvokeCommand_fmt = (msg) => {
266
- const input = asObj(msg.input);
267
- const structured = asObj(msg.structured);
268
- const name = str(structured, "name") ?? str(input, "name");
269
- const args = str(input, "args");
393
+ const input = asObj2(msg.input);
394
+ const structured = asObj2(msg.structured);
395
+ const name = str2(structured, "name") ?? str2(input, "name");
396
+ const args = str2(input, "args");
270
397
  const slash = name ? `/${name}` : void 0;
271
398
  const mainArg = slash && args ? `${slash} with ${args}` : slash;
272
399
  let phrase = "Invoked";
@@ -277,10 +404,10 @@ var InvokeCommand_fmt = (msg) => {
277
404
  return { mainArg, phrase };
278
405
  };
279
406
  var Memory_fmt = (msg) => {
280
- const input = asObj(msg.input);
281
- const structured = asObj(msg.structured);
282
- const operation = str(input, "operation") ?? "update";
283
- const mainArg = str(structured, "slug") ?? str(input, "slug") ?? str(input, "title");
407
+ const input = getMemoryToolInput(msg.input);
408
+ const structured = asObj2(msg.structured);
409
+ const operation = str2(input, "operation") ?? "update";
410
+ const mainArg = str2(structured, "slug") ?? str2(input, "slug") ?? str2(input, "title");
284
411
  const verb = operation === "create" ? "create" : operation === "remove" ? "remove" : "edit";
285
412
  let phrase = verb === "create" ? "Created memory" : verb === "remove" ? "Removed memory" : "Edited memory";
286
413
  if (msg.status === "pending") phrase = `Waiting to ${verb} memory`;
@@ -291,10 +418,10 @@ var Memory_fmt = (msg) => {
291
418
  return { mainArg, phrase };
292
419
  };
293
420
  var MemoryRead_fmt = (msg) => {
294
- const input = asObj(msg.input);
295
- const structured = asObj(msg.structured);
296
- const slug = str(input, "slug");
297
- const pattern = str(input, "pattern");
421
+ const input = getMemoryToolInput(msg.input);
422
+ const structured = asObj2(msg.structured);
423
+ const slug = str2(input, "slug");
424
+ const pattern = str2(input, "pattern");
298
425
  const mainArg = slug ?? pattern ?? "all";
299
426
  let phrase;
300
427
  if (msg.status === "pending") phrase = "Waiting to read memory";
@@ -408,7 +535,9 @@ function getTodoItems(value) {
408
535
  });
409
536
  }
410
537
  export {
538
+ extractFileEditPreview,
411
539
  formatToolPreviewLine,
540
+ getMemoryToolInput,
412
541
  getTodoItems,
413
542
  getToolPreview,
414
543
  getToolStatusGlyph,
@@ -1,12 +1,12 @@
1
1
  import "../chunk-7LEIACBZ.js";
2
2
  import {
3
3
  PROTOCOL_VERSION
4
- } from "../chunk-O3UBTUIO.js";
4
+ } from "../chunk-FAYWOY6R.js";
5
5
  import {
6
6
  generateGrantKeyPair,
7
7
  signGrant
8
- } from "../chunk-TUNCGICQ.js";
9
- import "../chunk-HJLRL64D.js";
8
+ } from "../chunk-5XT7AOI2.js";
9
+ import "../chunk-4UBSHJWS.js";
10
10
 
11
11
  // src/test-utils/fake-relay.ts
12
12
  import { randomUUID } from "crypto";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuvin/session",
3
- "version": "0.2.0-rc.9",
3
+ "version": "0.2.1-rc.0",
4
4
  "private": false,
5
5
  "description": "Headless session layer for Nuvin: state reducers, wire protocol, session controller",
6
6
  "license": "Apache-2.0",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "ws": "^8.20.0",
53
- "@nuvin/agent-core": "^0.2.0-rc.6"
53
+ "@nuvin/agent-core": "^0.2.1-rc.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/node": "^22.0.0",