@orion-agents/orion-code 0.3.25 → 0.3.27

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.
@@ -75,6 +75,31 @@ function parseGitDiffDocument(input) {
75
75
  let additions = 0;
76
76
  let deletions = 0;
77
77
  let current;
78
+ const openHunk = (header, index, oldCursor, newCursor) => {
79
+ const headerMatch = HUNK_HEADER.exec(header);
80
+ if (!headerMatch)
81
+ return;
82
+ const oldStart = Number(headerMatch[1]);
83
+ const newStart = Number(headerMatch[3]);
84
+ current = {
85
+ header,
86
+ oldStart,
87
+ oldLines: headerMatch[2] === undefined ? 1 : Number(headerMatch[2]),
88
+ newStart,
89
+ newLines: headerMatch[4] === undefined ? 1 : Number(headerMatch[4]),
90
+ contextLabel: (headerMatch[5] ?? '').trim(),
91
+ lines: [],
92
+ oldCursor,
93
+ newCursor,
94
+ index,
95
+ };
96
+ };
97
+ // v0.3.26 #275 — a continuation page starts mid-hunk: re-open it from the
98
+ // resume state before the first line is classified. A page that happens to
99
+ // start with its own `@@` header simply ignores the resume state.
100
+ if (input.resume && !HUNK_HEADER.test(input.lines[0] ?? '')) {
101
+ openHunk(input.resume.header, input.resume.index, input.resume.oldCursor, input.resume.newCursor);
102
+ }
78
103
  const closeHunk = () => {
79
104
  if (!current)
80
105
  return;
@@ -134,14 +159,22 @@ function parseGitDiffDocument(input) {
134
159
  }));
135
160
  continue;
136
161
  }
137
- if (isMetaLine(raw)) {
162
+ // v0.3.26 #274 — the body marker decides first. A deletion of a line that
163
+ // itself starts with `-- ` produces the body row `--- old comment`, which a
164
+ // header-first check mistook for the old-file header, closing the hunk and
165
+ // demoting the remaining body to document meta. Genuine document-level lines
166
+ // carry no body marker by construction: context rows start with a space,
167
+ // additions with `+`, deletions with `-`, and file boundaries are announced
168
+ // by a markerless `diff --git` line.
169
+ const marker = raw[0];
170
+ const isBodyRow = marker === '+' || marker === '-' || marker === ' ' || raw === '';
171
+ if (!isBodyRow && isMetaLine(raw)) {
138
172
  // A file boundary inside one page (multi-command reads) closes the current hunk.
139
173
  closeHunk();
140
174
  if (raw)
141
175
  meta.push(raw);
142
176
  continue;
143
177
  }
144
- const marker = raw[0];
145
178
  const text = raw.length > 0 ? raw.slice(1) : '';
146
179
  let lineKind;
147
180
  let oldLineNumber = null;
@@ -185,6 +218,16 @@ function parseGitDiffDocument(input) {
185
218
  raw,
186
219
  }));
187
220
  }
221
+ // v0.3.26 #275 — a page that ends inside a hunk publishes the hunk state for
222
+ // the continuation cursor; a page ending on a boundary has nothing to resume.
223
+ const pendingHunk = input.hasMore && current
224
+ ? Object.freeze({
225
+ header: current.header,
226
+ index: current.index,
227
+ oldCursor: current.oldCursor,
228
+ newCursor: current.newCursor,
229
+ })
230
+ : null;
188
231
  closeHunk();
189
232
  const frozenHunks = Object.freeze(hunks);
190
233
  let completeness = 'complete';
@@ -203,6 +246,7 @@ function parseGitDiffDocument(input) {
203
246
  binary,
204
247
  hunks: frozenHunks,
205
248
  meta: Object.freeze(meta),
249
+ pendingHunk,
206
250
  completeness,
207
251
  additions,
208
252
  deletions,
@@ -236,7 +236,13 @@ class GitReadModelServiceV1 {
236
236
  */
237
237
  async diffDocument(input) {
238
238
  const page = await this.diff(input);
239
- return (0, git_diff_document_1.parseGitDiffDocument)({
239
+ // v0.3.26 #275 — the incoming cursor may carry the hunk state this page
240
+ // continues; diff() already validated the cursor, so decoding it here only
241
+ // reads the resume state.
242
+ const resumeHunk = input.cursor
243
+ ? this.decodeCursor(input.cursor, 'diff', page.repositoryRevision, page.fileId, diffScope(input.ignoreWhitespace === true, input.wordDiff === true)).resumeHunk
244
+ : undefined;
245
+ const document = (0, git_diff_document_1.parseGitDiffDocument)({
240
246
  fileToken: page.fileId,
241
247
  path: page.path,
242
248
  source: page.source,
@@ -244,7 +250,18 @@ class GitReadModelServiceV1 {
244
250
  lines: page.lines,
245
251
  hasMore: page.nextCursor !== null,
246
252
  nextCursor: page.nextCursor,
253
+ ...(resumeHunk ? { resume: resumeHunk } : {}),
247
254
  });
255
+ // A page that ends inside a hunk re-mints the cursor with the hunk state so
256
+ // the next page resumes it instead of demoting the rows to meta.
257
+ if (page.nextCursor && document.pendingHunk) {
258
+ const decoded = this.decodeCursor(page.nextCursor, 'diff', page.repositoryRevision, page.fileId, diffScope(input.ignoreWhitespace === true, input.wordDiff === true));
259
+ return Object.freeze({
260
+ ...document,
261
+ nextCursor: this.encodeCursor({ ...decoded, resumeHunk: document.pendingHunk }),
262
+ });
263
+ }
264
+ return document;
248
265
  }
249
266
  historyReader() {
250
267
  this.historyReaderInstance ?? (this.historyReaderInstance = (0, git_history_service_1.createGitHistoryReader)({
@@ -300,7 +317,8 @@ class GitReadModelServiceV1 {
300
317
  .update(`compare-diff:${input.baseOid}:${input.headOid}:${path}:${snapshot.revision}`)
301
318
  .digest('hex')
302
319
  .slice(0, 24);
303
- const offset = input.cursor ? decodeCommitDiffCursor(input.cursor, margin) : 0;
320
+ const cursorState = input.cursor ? decodeCommitDiffCursor(input.cursor, margin) : null;
321
+ const offset = cursorState?.offset ?? 0;
304
322
  const page = await streamDiffPage({
305
323
  cwd: root,
306
324
  commands: Object.freeze([
@@ -339,9 +357,19 @@ class GitReadModelServiceV1 {
339
357
  nextCursor: page.hasMore
340
358
  ? encodeCommitDiffCursor({ margin, offset: offset + page.returnedLines })
341
359
  : null,
360
+ ...(cursorState?.resumeHunk ? { resume: cursorState.resumeHunk } : {}),
342
361
  });
362
+ // v0.3.26 #275 — re-mint the continuation cursor with the split hunk state.
363
+ const nextCursor = page.hasMore && document.pendingHunk
364
+ ? encodeCommitDiffCursor({
365
+ margin,
366
+ offset: offset + page.returnedLines,
367
+ resumeHunk: document.pendingHunk,
368
+ })
369
+ : document.nextCursor;
343
370
  return Object.freeze({
344
371
  ...document,
372
+ nextCursor,
345
373
  baseOid: input.baseOid,
346
374
  baseLabel: `比较 ${input.baseOid.slice(0, 10)} → ${input.headOid.slice(0, 10)}`,
347
375
  });
@@ -450,7 +478,8 @@ class GitReadModelServiceV1 {
450
478
  .update(`commit-diff:${detail.id}:${detail.baseOid}:${parentIndex}:${snapshot.revision}`)
451
479
  .digest('hex')
452
480
  .slice(0, 24);
453
- const offset = input.cursor ? decodeCommitDiffCursor(input.cursor, margin) : 0;
481
+ const cursorState = input.cursor ? decodeCommitDiffCursor(input.cursor, margin) : null;
482
+ const offset = cursorState?.offset ?? 0;
454
483
  const page = await streamDiffPage({
455
484
  cwd: root,
456
485
  commands: Object.freeze([
@@ -489,8 +518,22 @@ class GitReadModelServiceV1 {
489
518
  nextCursor: page.hasMore
490
519
  ? encodeCommitDiffCursor({ margin, offset: offset + page.returnedLines })
491
520
  : null,
521
+ ...(cursorState?.resumeHunk ? { resume: cursorState.resumeHunk } : {}),
522
+ });
523
+ // v0.3.26 #275 — re-mint the continuation cursor with the split hunk state.
524
+ const nextCursor = page.hasMore && document.pendingHunk
525
+ ? encodeCommitDiffCursor({
526
+ margin,
527
+ offset: offset + page.returnedLines,
528
+ resumeHunk: document.pendingHunk,
529
+ })
530
+ : document.nextCursor;
531
+ return Object.freeze({
532
+ ...document,
533
+ nextCursor,
534
+ baseLabel: detail.baseLabel,
535
+ baseOid: detail.baseOid,
492
536
  });
493
- return Object.freeze({ ...document, baseLabel: detail.baseLabel, baseOid: detail.baseOid });
494
537
  }
495
538
  async capture() {
496
539
  let root;
@@ -1211,6 +1254,9 @@ class GitReadModelServiceV1 {
1211
1254
  parsed.offset < 0) {
1212
1255
  return invalidCursor();
1213
1256
  }
1257
+ if (parsed.resumeHunk !== undefined && !isDiffHunkResume(parsed.resumeHunk)) {
1258
+ return invalidCursor();
1259
+ }
1214
1260
  if (parsed.revision !== revision) {
1215
1261
  throw new errors_1.WebWorkbenchError(409, 'Git cursor revision is stale.', 'git_revision_conflict');
1216
1262
  }
@@ -1224,6 +1270,22 @@ class GitReadModelServiceV1 {
1224
1270
  }
1225
1271
  }
1226
1272
  exports.GitReadModelServiceV1 = GitReadModelServiceV1;
1273
+ /** v0.3.26 #275 — the hunk header shape, mirrored for cursor validation. */
1274
+ const HUNK_HEADER_PATTERN = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/u;
1275
+ /** v0.3.26 #275 — validates a cursor-carried hunk resume state. */
1276
+ function isDiffHunkResume(value) {
1277
+ if (!value || typeof value !== 'object')
1278
+ return false;
1279
+ const candidate = value;
1280
+ return (typeof candidate.header === 'string' &&
1281
+ HUNK_HEADER_PATTERN.test(candidate.header) &&
1282
+ Number.isSafeInteger(candidate.index) &&
1283
+ candidate.index >= 0 &&
1284
+ Number.isSafeInteger(candidate.oldCursor) &&
1285
+ candidate.oldCursor >= 1 &&
1286
+ Number.isSafeInteger(candidate.newCursor) &&
1287
+ candidate.newCursor >= 1);
1288
+ }
1227
1289
  function parsePorcelainV1(output) {
1228
1290
  const fields = output.split('\0');
1229
1291
  const records = [];
@@ -1692,7 +1754,16 @@ function decodeCommitDiffCursor(cursor, margin) {
1692
1754
  candidate.offset < 0) {
1693
1755
  throw new errors_1.WebWorkbenchError(409, 'Diff changed; reload before paginating.', 'git_cursor_invalid');
1694
1756
  }
1695
- return candidate.offset;
1757
+ if (candidate.resumeHunk !== undefined && !isDiffHunkResume(candidate.resumeHunk)) {
1758
+ throw new errors_1.WebWorkbenchError(400, 'Diff cursor is not valid.', 'git_cursor_invalid');
1759
+ }
1760
+ return {
1761
+ margin,
1762
+ offset: candidate.offset,
1763
+ ...(candidate.resumeHunk !== undefined
1764
+ ? { resumeHunk: candidate.resumeHunk }
1765
+ : {}),
1766
+ };
1696
1767
  }
1697
1768
  /**
1698
1769
  * Kills a detached child and everything it spawned.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@orion-agents/orion-code",
3
- "version": "0.3.25",
3
+ "version": "0.3.27",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@orion-agents/orion-code",
9
- "version": "0.3.25",
9
+ "version": "0.3.27",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@clack/prompts": "^0.7.0",
@@ -4293,9 +4293,9 @@
4293
4293
  "license": "ISC"
4294
4294
  },
4295
4295
  "node_modules/follow-redirects": {
4296
- "version": "1.16.0",
4297
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
4298
- "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
4296
+ "version": "1.16.1",
4297
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz",
4298
+ "integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==",
4299
4299
  "funding": [
4300
4300
  {
4301
4301
  "type": "individual",
@@ -4563,13 +4563,13 @@
4563
4563
  "license": "MIT"
4564
4564
  },
4565
4565
  "node_modules/handlebars": {
4566
- "version": "4.7.9",
4567
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
4568
- "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
4566
+ "version": "4.7.10",
4567
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.10.tgz",
4568
+ "integrity": "sha512-P5VJMVM7qgBn6vjXMw8WG9uVI+ncf2pi72j4de4yz5ZULLj2RGqLYaKOYGsgyrViQ0tePOVlN1tDCCXXtFqXKg==",
4569
4569
  "dev": true,
4570
4570
  "license": "MIT",
4571
4571
  "dependencies": {
4572
- "minimist": "^1.2.5",
4572
+ "minimist": "^1.2.8",
4573
4573
  "neo-async": "^2.6.2",
4574
4574
  "source-map": "^0.6.1",
4575
4575
  "wordwrap": "^1.0.0"
@@ -7147,9 +7147,9 @@
7147
7147
  }
7148
7148
  },
7149
7149
  "node_modules/source-map-js": {
7150
- "version": "1.2.1",
7151
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
7152
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
7150
+ "version": "1.2.2",
7151
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
7152
+ "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
7153
7153
  "dev": true,
7154
7154
  "license": "BSD-3-Clause",
7155
7155
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orion-agents/orion-code",
3
- "version": "0.3.25",
3
+ "version": "0.3.27",
4
4
  "description": "Orion Code - local-first goal-driven coding agent for terminal and Web.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",