acp-kernel 0.0.96 → 0.0.97-pr.466.328

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/LICENSE CHANGED
@@ -19,3 +19,39 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
23
+ ---
24
+
25
+ Additional Term — Attribution on Product Surfaces
26
+ (在 MIT 之上附加的条款 —— 产品出处标注)
27
+
28
+ In addition to the conditions above, if you use or incorporate the Software
29
+ in a product or service that end users can see or interact with — whether
30
+ commercial or open source — you must make attribution clearly visible in at
31
+ least one of the following places:
32
+
33
+ 1. the product's home page;
34
+ 2. its documentation; or
35
+ 3. its "About" / "Credits" / "Acknowledgements" page.
36
+
37
+ The attribution must state that the product uses acp-kernel (and
38
+ billion-context, where applicable) and include a link to
39
+ https://github.com/ranxianglei/acp-kernel (and
40
+ https://github.com/ranxianglei/billion-context, where applicable).
41
+
42
+ For purely server-side or embedded uses with no user-facing surface, carrying
43
+ these notices in the shipped documentation satisfies this term.
44
+
45
+ 附加条款(中文):
46
+
47
+ 除上述条件外,若将本软件用于任何终端用户可见或可交互的产品或服务(无论商业或开源),
48
+ 须在下述至少一处显著位置标明出处:
49
+
50
+ 1. 产品首页;
51
+ 2. 产品文档;或
52
+ 3. “关于 / 致谢 / Acknowledgements”页面。
53
+
54
+ 署名须说明该产品使用了 acp-kernel(涉及时包括 billion-context),并附指向
55
+ https://github.com/ranxianglei/acp-kernel (及 https://github.com/ranxianglei/billion-context,
56
+ 涉及时)的链接。纯服务端或内嵌、无用户可见界面的用途,随附文档中包含上述声明即可。
57
+
package/README.md CHANGED
@@ -298,6 +298,12 @@ to decide between message-level and wire-level surgery.
298
298
 
299
299
  > **Protected tool messages:** protected tool calls (per `config.protectedTools`) and their paired tool-results are hard-excluded from compression — they are dropped from the compressible set and from the new block's `effectiveMessageIds`, so they stay fully visible and are never folded into a summary. This matches opencode-acp's Bug 39 fix. The soft-protected recent zone (`preserveRecentMessages` / last user message) is handled separately: messages there are excluded from the range but do not fail it (an entirely-protected range fails with a clear error).
300
300
 
301
+ ## Attribution requirement (one term on top of MIT)
302
+
303
+ `acp-kernel` is MIT-licensed **plus one additional term**: any product or service (commercial or open source) whose users can see or interact with it and which uses this kernel must attribute it — stating that it uses acp-kernel with a link back to this repository — on its home page, documentation, or About/Credits page. Pure server-side/embedded use satisfies this via shipped documentation. See the **Additional Term** at the end of [LICENSE](LICENSE).
304
+
305
+ If you build on this kernel, we'd love to hear about it: open an issue (no obligation) so we can track where it's used.
306
+
301
307
  ## License
302
308
 
303
309
  MIT © ranxianglei
package/dist/index.js CHANGED
@@ -290,44 +290,10 @@ function stripOrphanedReasoning(messages) {
290
290
  return messages.filter((_, i) => !drop.has(i));
291
291
  }
292
292
 
293
- // src/instance-reid.ts
294
- var CONTENT_HASH_ROOT = /^h_([0-9a-f]{16})(?:_\d+)?(?:#.*)?$/;
295
- function clusterRoot(id) {
296
- const m = CONTENT_HASH_ROOT.exec(id);
297
- return m ? `h_${m[1]}` : null;
298
- }
299
- function remintCoveredLiveIds(messages, state) {
300
- const covered = /* @__PURE__ */ new Set();
301
- for (const id of coveredMessageIds(state)) covered.add(baseIdOf(id));
302
- if (covered.size === 0) return messages;
303
- const groups = /* @__PURE__ */ new Map();
304
- for (let i = 0; i < messages.length; i++) {
305
- const root = clusterRoot(messages[i].id);
306
- if (root === null) continue;
307
- const idxs = groups.get(root);
308
- if (idxs) idxs.push(i);
309
- else groups.set(root, [i]);
310
- }
311
- const next = [...messages];
312
- let changed = false;
313
- for (const [root, idxs] of groups) {
314
- const conflict = idxs.some((i) => covered.has(baseIdOf(messages[i].id)));
315
- if (!conflict) continue;
316
- let k = 1;
317
- for (const i of idxs) {
318
- while (covered.has(`${root}_${k}`)) k++;
319
- const id = messages[i].id;
320
- const hash = id.indexOf("#");
321
- const tail = hash > 0 ? id.slice(hash) : "";
322
- next[i] = { ...messages[i], id: `${root}_${k}${tail}` };
323
- k++;
324
- changed = true;
325
- }
326
- }
327
- return changed ? next : messages;
328
- }
329
-
330
293
  // src/sync.ts
294
+ function isBlockStillPresent(block, presentBases, presentIds) {
295
+ return block.effectiveMessageIds.some((id) => presentBases.has(baseIdOf(id))) || presentIds.has(summaryMessageId(block.blockId));
296
+ }
331
297
  function syncBlocks(messages, state) {
332
298
  const presentIds = new Set(messages.map((message) => message.id));
333
299
  const presentBases = /* @__PURE__ */ new Set();
@@ -383,8 +349,7 @@ function syncBlocks(messages, state) {
383
349
  continue;
384
350
  }
385
351
  block.active = true;
386
- const stillPresent = block.effectiveMessageIds.some((id) => presentBases.has(baseIdOf(id))) || presentIds.has(summaryMessageId(block.blockId));
387
- if (!stillPresent) {
352
+ if (!isBlockStillPresent(block, presentBases, presentIds)) {
388
353
  block.active = false;
389
354
  deactivated.push(block.blockId);
390
355
  }
@@ -392,6 +357,101 @@ function syncBlocks(messages, state) {
392
357
  return { state: result, deactivated };
393
358
  }
394
359
 
360
+ // src/instance-reid.ts
361
+ var CONTENT_HASH_ROOT = /^h_([0-9a-f]{16})(?:_\d+)?(?:#.*)?$/;
362
+ function clusterRoot(id) {
363
+ const m = CONTENT_HASH_ROOT.exec(id);
364
+ return m ? `h_${m[1]}` : null;
365
+ }
366
+ function instanceNumber(id, root) {
367
+ if (!id.startsWith(`${root}_`)) return null;
368
+ const n = Number(id.slice(root.length + 1));
369
+ return Number.isInteger(n) && n > 0 ? n : null;
370
+ }
371
+ function remintCoveredLiveIds(messages, state) {
372
+ const liveBases = /* @__PURE__ */ new Set();
373
+ const liveIds = /* @__PURE__ */ new Set();
374
+ for (const message of messages) {
375
+ if (typeof message.id !== "string") continue;
376
+ liveIds.add(message.id);
377
+ liveBases.add(baseIdOf(message.id));
378
+ }
379
+ const consumed = /* @__PURE__ */ new Set();
380
+ for (const block of state.blocks) {
381
+ for (const id of block.directBlockIds) consumed.add(id);
382
+ }
383
+ const coverersByBase = /* @__PURE__ */ new Map();
384
+ const coveredBases = /* @__PURE__ */ new Set();
385
+ let anyCoverage = false;
386
+ for (const block of state.blocks) {
387
+ if (consumed.has(block.blockId) || block.expanded) continue;
388
+ if (!isBlockStillPresent(block, liveBases, liveIds)) continue;
389
+ const bases = /* @__PURE__ */ new Set();
390
+ for (const id of block.effectiveMessageIds) bases.add(baseIdOf(id));
391
+ let allPresent = bases.size > 0;
392
+ for (const base of bases) {
393
+ if (!liveBases.has(base)) {
394
+ allPresent = false;
395
+ break;
396
+ }
397
+ }
398
+ const replaying = allPresent && !liveIds.has(summaryMessageId(block.blockId));
399
+ anyCoverage = true;
400
+ for (const base of bases) {
401
+ coveredBases.add(base);
402
+ const flags = coverersByBase.get(base);
403
+ if (flags) flags.push(replaying);
404
+ else coverersByBase.set(base, [replaying]);
405
+ }
406
+ }
407
+ if (!anyCoverage) return messages;
408
+ const groups = /* @__PURE__ */ new Map();
409
+ for (let i = 0; i < messages.length; i++) {
410
+ const id = messages[i].id;
411
+ if (typeof id !== "string") continue;
412
+ const root = clusterRoot(id);
413
+ if (root === null) continue;
414
+ const idxs = groups.get(root);
415
+ if (idxs) idxs.push(i);
416
+ else groups.set(root, [i]);
417
+ }
418
+ const next = [...messages];
419
+ let changed = false;
420
+ for (const [root, idxs] of groups) {
421
+ const remintIdx = [];
422
+ for (const i of idxs) {
423
+ const flags = coverersByBase.get(baseIdOf(messages[i].id));
424
+ if (!flags) continue;
425
+ const positionalReplay = flags.every((replaying) => replaying);
426
+ if (positionalReplay) continue;
427
+ remintIdx.push(i);
428
+ }
429
+ if (remintIdx.length === 0) continue;
430
+ const reminting = new Set(remintIdx);
431
+ const taken = /* @__PURE__ */ new Set();
432
+ for (const base of coveredBases) {
433
+ const n = instanceNumber(base, root);
434
+ if (n !== null) taken.add(n);
435
+ }
436
+ for (const i of idxs) {
437
+ if (reminting.has(i)) continue;
438
+ const n = instanceNumber(baseIdOf(messages[i].id), root);
439
+ if (n !== null) taken.add(n);
440
+ }
441
+ let k = 1;
442
+ for (const i of remintIdx) {
443
+ while (taken.has(k)) k++;
444
+ const id = messages[i].id;
445
+ const hash = id.indexOf("#");
446
+ const tail = hash > 0 ? id.slice(hash) : "";
447
+ next[i] = { ...messages[i], id: `${root}_${k}${tail}` };
448
+ taken.add(k);
449
+ changed = true;
450
+ }
451
+ }
452
+ return changed ? next : messages;
453
+ }
454
+
395
455
  // src/config.ts
396
456
  function defaultConfig(modelContextLimit, overrides = {}) {
397
457
  const base = {