billion-context-dsh 0.2.1 → 0.2.2
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/README.en.md +1 -1
- package/README.md +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +226 -14
- package/dist/index.js.map +1 -1
- package/dist/region.d.ts +44 -0
- package/dist/tools.d.ts +7 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[English](./README.en.md) | [中文](./README.md)
|
|
4
4
|
|
|
5
5
|
> **⚠️ Beta notice — not for production use**
|
|
6
|
-
> This project (**v0.2.
|
|
6
|
+
> This project (**v0.2.2**) is a work-in-progress beta. The [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) itself is also in **public beta**. **Do not use either in engineering / production environments** — expect breaking changes and rough edges.
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
9
|
<strong>Built with gratitude on top of these projects</strong> — please give them a ⭐:
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[中文](./README.md) | [English](./README.en.md)
|
|
4
4
|
|
|
5
5
|
> **⚠️ 测试版声明——请勿用于生产环境**
|
|
6
|
-
> 本项目(**v0.2.
|
|
6
|
+
> 本项目(**v0.2.2**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
9
|
<strong>衷心感谢以下项目——请给它们一个 ⭐:</strong>
|
package/dist/index.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export { makeTools, type ToolEnvironment } from './tools.ts';
|
|
|
40
40
|
export { acpCommand } from './commands.ts';
|
|
41
41
|
export { buildNudge, resolveTokenCount, type NudgeEnvironment, type NudgeOutcome } from './nudge.ts';
|
|
42
42
|
export { DEFAULT_CONTEXT_WINDOW, detectContextWindow, windowSourceLabel, type AcpWindow, } from './window.ts';
|
|
43
|
-
export { AlreadyCompressedRangeError, rebuildBlockLedger, resolveSurfaceRange, runCompactionTransaction, shadowedSeqsOf, findOpenTurn, assertNoActiveCompaction, blockRegistry, blockRefForSummarySeq, compactionIdsOfKernelBlocks, summarySeqOfKernelBlock, expandShadowedSeqs, type AcpBlockLedgerEntry, type CompactionTransactionInput, type ResolvedSurfaceRange, } from './region.ts';
|
|
43
|
+
export { AlreadyCompressedRangeError, rebuildBlockLedger, resolveSurfaceRange, runCompactionTransaction, shadowedSeqsOf, findOpenTurn, assertNoActiveCompaction, blockRegistry, blockRefForSummarySeq, compactionIdsOfKernelBlocks, summarySeqOfKernelBlock, expandShadowedSeqs, hideCompressToolPair, stripOrphanedSurfaceToolMessages, type AcpBlockLedgerEntry, type CompactionTransactionInput, type ResolvedSurfaceRange, } from './region.ts';
|
|
44
44
|
export { eventsToCoreMessages, projectEvent, surfaceEventsOf, extractEventText } from './messages.ts';
|
|
45
45
|
export interface AcpConfig {
|
|
46
46
|
/**
|
|
@@ -108,6 +108,8 @@ export declare class AcpCompactionEngine extends CompactionEngine {
|
|
|
108
108
|
/** Resolved prompt templates (validated at construction — fail-fast on template typos). */
|
|
109
109
|
readonly prompts: ResolvedPrompts;
|
|
110
110
|
private readonly lastNudgeTurn;
|
|
111
|
+
/** Successful compress call ids awaiting their tool/result so the pair can be hidden. */
|
|
112
|
+
private readonly compressCallIdsToHide;
|
|
111
113
|
/** Per provider/model route the resolved window (probe failures cached too). */
|
|
112
114
|
private readonly windowCache;
|
|
113
115
|
constructor(ctx: Context, config?: Partial<AcpConfig>);
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
toolPairingBalancedAfter,
|
|
17
17
|
toolPairingBalancedBefore
|
|
18
18
|
} from "@deepseek-ai/dsh-compaction";
|
|
19
|
-
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
19
|
+
import { createAssistantMessage, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
20
20
|
import { defaultCountTokens } from "acp-kernel";
|
|
21
21
|
|
|
22
22
|
// src/messages.ts
|
|
@@ -347,7 +347,198 @@ function isCheckpointNode(event) {
|
|
|
347
347
|
const source = event.data.source;
|
|
348
348
|
return source?.plugin === "compact";
|
|
349
349
|
}
|
|
350
|
+
function toolCallIdsOfEvent(event) {
|
|
351
|
+
if (event.type !== "assistant/message") return [];
|
|
352
|
+
const content = event.data.message?.content;
|
|
353
|
+
if (!Array.isArray(content)) return [];
|
|
354
|
+
const ids = [];
|
|
355
|
+
for (const block of content) {
|
|
356
|
+
if (block === null || typeof block !== "object") continue;
|
|
357
|
+
const b = block;
|
|
358
|
+
if (b.type === "tool-call" && typeof b.id === "string") ids.push(b.id);
|
|
359
|
+
}
|
|
360
|
+
return ids;
|
|
361
|
+
}
|
|
362
|
+
function toolCallIdOfResultEvent(event) {
|
|
363
|
+
if (event.type !== "tool/result") return null;
|
|
364
|
+
const message = event.data.message;
|
|
365
|
+
const block = Array.isArray(message?.content) ? message.content.find((candidate) => candidate?.type === "tool-result") : void 0;
|
|
366
|
+
const id = block?.toolCallId ?? message?.source?.callId;
|
|
367
|
+
return typeof id === "string" ? id : null;
|
|
368
|
+
}
|
|
369
|
+
function assistantProviderModel(event) {
|
|
370
|
+
if (event.type === "assistant/message") {
|
|
371
|
+
const message = event.data.message;
|
|
372
|
+
return {
|
|
373
|
+
provider: typeof message?.source?.provider === "string" ? message.source.provider : "billion-context-dsh",
|
|
374
|
+
model: typeof message?.source?.model === "string" ? message.source.model : "surface-prune"
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return { provider: "billion-context-dsh", model: "surface-prune" };
|
|
378
|
+
}
|
|
379
|
+
function hideSurfaceSeqs(session, seqs, provider, model, text) {
|
|
380
|
+
if (seqs.length === 0) return;
|
|
381
|
+
const start = seqs[0];
|
|
382
|
+
const end = seqs[seqs.length - 1];
|
|
383
|
+
let shadowedTokenCount = 0;
|
|
384
|
+
for (const seq of seqs) {
|
|
385
|
+
const event = session.events[seq];
|
|
386
|
+
if (event !== void 0) shadowedTokenCount += defaultCountTokens(extractEventText(event));
|
|
387
|
+
}
|
|
388
|
+
session.append("compaction/prune", {
|
|
389
|
+
shadowedRange: { start, end },
|
|
390
|
+
shadowedSeqs: [...seqs],
|
|
391
|
+
shadowedTokenCount
|
|
392
|
+
});
|
|
393
|
+
if (text !== void 0) {
|
|
394
|
+
session.append("user/message", createUserMessage({
|
|
395
|
+
content: [{ type: "text", text }],
|
|
396
|
+
source: { kind: "plugin", plugin: "billion-context-dsh" }
|
|
397
|
+
}), {
|
|
398
|
+
surfaceOp: { op: "replace", start, end },
|
|
399
|
+
sourceEventSeqs: [...seqs]
|
|
400
|
+
});
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
session.append("assistant/message", {
|
|
404
|
+
turn: findOpenTurn(session.events) ?? 0,
|
|
405
|
+
step: 0,
|
|
406
|
+
message: createAssistantMessage({ content: [], source: { provider, model } })
|
|
407
|
+
}, {
|
|
408
|
+
surfaceOp: { op: "replace", start, end },
|
|
409
|
+
sourceEventSeqs: [...seqs]
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
function hideCompressToolPair(session, callId, resultSeq) {
|
|
413
|
+
let callSeq = null;
|
|
414
|
+
for (const event of session.events) {
|
|
415
|
+
if (event.type !== "assistant/message") continue;
|
|
416
|
+
if (toolCallIdsOfEvent(event).includes(callId)) {
|
|
417
|
+
callSeq = event.seq;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (callSeq === null) return false;
|
|
422
|
+
const callNodeIds = toolCallIdsOfEvent(session.events[callSeq]);
|
|
423
|
+
if (callNodeIds.length !== 1 || callNodeIds[0] !== callId) return false;
|
|
424
|
+
let resolvedResultSeq = resultSeq ?? null;
|
|
425
|
+
if (resolvedResultSeq === null) {
|
|
426
|
+
for (const event of session.events) {
|
|
427
|
+
if (event.type === "tool/result" && toolCallIdOfResultEvent(event) === callId) {
|
|
428
|
+
resolvedResultSeq = event.seq;
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (resolvedResultSeq === null) return false;
|
|
434
|
+
const nodes = session.surface.nodes;
|
|
435
|
+
const startIdx = nodes.indexOf(callSeq);
|
|
436
|
+
const endIdx = nodes.indexOf(resolvedResultSeq);
|
|
437
|
+
if (startIdx < 0 || endIdx < 0 || endIdx - startIdx !== 1) return false;
|
|
438
|
+
const { provider, model } = assistantProviderModel(session.events[callSeq]);
|
|
439
|
+
const resultEvent = session.events[resolvedResultSeq];
|
|
440
|
+
const resultText = resultEvent === void 0 ? "" : extractEventText(resultEvent);
|
|
441
|
+
hideSurfaceSeqs(session, [callSeq, resolvedResultSeq], provider, model, resultText.trim().length > 0 ? resultText : void 0);
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
function stripOrphanedSurfaceToolMessages(session, inFlightCallIds = /* @__PURE__ */ new Set()) {
|
|
445
|
+
const nodes = session.surface.nodes;
|
|
446
|
+
const callIdsBySeq = /* @__PURE__ */ new Map();
|
|
447
|
+
const open = /* @__PURE__ */ new Map();
|
|
448
|
+
const orphanResultSeqs = [];
|
|
449
|
+
const brokenResults = /* @__PURE__ */ new Map();
|
|
450
|
+
for (let index = 0; index < nodes.length; index += 1) {
|
|
451
|
+
const seq = nodes[index];
|
|
452
|
+
const event = session.events[seq];
|
|
453
|
+
if (event === void 0) continue;
|
|
454
|
+
if (event.type === "assistant/message") {
|
|
455
|
+
const ids = toolCallIdsOfEvent(event);
|
|
456
|
+
if (ids.length === 0) continue;
|
|
457
|
+
callIdsBySeq.set(seq, ids);
|
|
458
|
+
for (const id of ids) {
|
|
459
|
+
if (!open.has(id)) open.set(id, { seq, index });
|
|
460
|
+
}
|
|
461
|
+
} else if (event.type === "tool/result") {
|
|
462
|
+
const id = toolCallIdOfResultEvent(event);
|
|
463
|
+
if (id === null) continue;
|
|
464
|
+
const call = open.get(id);
|
|
465
|
+
if (call === void 0) {
|
|
466
|
+
orphanResultSeqs.push(seq);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const callNodeIds = callIdsBySeq.get(call.seq);
|
|
470
|
+
let adjacent = false;
|
|
471
|
+
if (callNodeIds !== void 0) {
|
|
472
|
+
adjacent = true;
|
|
473
|
+
for (let mid = call.index + 1; mid < index; mid += 1) {
|
|
474
|
+
const midEvent = session.events[nodes[mid]];
|
|
475
|
+
if (midEvent === void 0 || midEvent.type !== "tool/result") {
|
|
476
|
+
adjacent = false;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
const midId = toolCallIdOfResultEvent(midEvent);
|
|
480
|
+
if (midId === null || !callNodeIds.includes(midId)) {
|
|
481
|
+
adjacent = false;
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
open.delete(id);
|
|
487
|
+
if (!adjacent) brokenResults.set(seq, call.seq);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const brokenIdsByCallSeq = /* @__PURE__ */ new Map();
|
|
491
|
+
for (const [resultSeq, callSeq] of brokenResults) {
|
|
492
|
+
const id = toolCallIdOfResultEvent(session.events[resultSeq]);
|
|
493
|
+
if (id !== null) {
|
|
494
|
+
const list = brokenIdsByCallSeq.get(callSeq) ?? [];
|
|
495
|
+
list.push(id);
|
|
496
|
+
brokenIdsByCallSeq.set(callSeq, list);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const hiddenSet = new Set(orphanResultSeqs);
|
|
500
|
+
for (const resultSeq of brokenResults.keys()) hiddenSet.add(resultSeq);
|
|
501
|
+
for (const [callSeq, ids] of callIdsBySeq) {
|
|
502
|
+
const brokenIds = brokenIdsByCallSeq.get(callSeq);
|
|
503
|
+
const allUnpaired = !ids.some((candidate) => inFlightCallIds.has(candidate)) && ids.every((candidate) => open.has(candidate) || brokenIds?.includes(candidate) === true);
|
|
504
|
+
if (allUnpaired) hiddenSet.add(callSeq);
|
|
505
|
+
}
|
|
506
|
+
const hidden = [...hiddenSet].sort((a, b) => a - b);
|
|
507
|
+
let count = 0;
|
|
508
|
+
for (const seq of hidden) {
|
|
509
|
+
const event = session.events[seq];
|
|
510
|
+
if (event === void 0) continue;
|
|
511
|
+
const { provider, model } = assistantProviderModel(event);
|
|
512
|
+
hideSurfaceSeqs(session, [seq], provider, model);
|
|
513
|
+
count += 1;
|
|
514
|
+
}
|
|
515
|
+
return count;
|
|
516
|
+
}
|
|
517
|
+
function openToolCallIds(session) {
|
|
518
|
+
const open = /* @__PURE__ */ new Set();
|
|
519
|
+
for (const seq of session.surface.nodes) {
|
|
520
|
+
const event = session.events[seq];
|
|
521
|
+
if (event === void 0) continue;
|
|
522
|
+
if (event.type === "assistant/message") {
|
|
523
|
+
for (const id of toolCallIdsOfEvent(event)) open.add(id);
|
|
524
|
+
} else if (event.type === "tool/result") {
|
|
525
|
+
const id = toolCallIdOfResultEvent(event);
|
|
526
|
+
if (id !== null) open.delete(id);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return open;
|
|
530
|
+
}
|
|
531
|
+
function deferCompressPairHide(session, callId, resultSeq, onError) {
|
|
532
|
+
queueMicrotask(() => {
|
|
533
|
+
try {
|
|
534
|
+
hideCompressToolPair(session, callId, resultSeq);
|
|
535
|
+
} catch (error) {
|
|
536
|
+
onError?.(error);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
}
|
|
350
540
|
function buildCompressibleSeqRanges(session, opts = {}) {
|
|
541
|
+
stripOrphanedSurfaceToolMessages(session);
|
|
351
542
|
const nodes = session.surface.nodes;
|
|
352
543
|
const preserve = opts.preserveRecent ?? 5;
|
|
353
544
|
const protectedSeqs = /* @__PURE__ */ new Set();
|
|
@@ -989,6 +1180,7 @@ function unwrapCompressArgs(args) {
|
|
|
989
1180
|
async function handleCompress(env, args, exec) {
|
|
990
1181
|
const agent = requireAgent(exec);
|
|
991
1182
|
const session = agent.session;
|
|
1183
|
+
stripOrphanedSurfaceToolMessages(session, openToolCallIds(session));
|
|
992
1184
|
const state = env.store.stateFor(session);
|
|
993
1185
|
const coreMessages = allLogMessages(session);
|
|
994
1186
|
const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session));
|
|
@@ -1059,10 +1251,13 @@ async function handleCompress(env, args, exec) {
|
|
|
1059
1251
|
// non-block-covered messages as the visible feed, so default behavior is
|
|
1060
1252
|
// preserved. Any 'Excluded N protected message(s)' warning is surfaced.
|
|
1061
1253
|
});
|
|
1062
|
-
if (applied.result.errors.length > 0) {
|
|
1254
|
+
if (applied.result.errors.length > 0 && applied.result.blocksCreated === 0) {
|
|
1063
1255
|
return { text: `compress failed: ${applied.result.errors.join("; ")}` };
|
|
1064
1256
|
}
|
|
1065
1257
|
env.store.set(session, applied.state);
|
|
1258
|
+
if (applied.result.blocksCreated > 0) {
|
|
1259
|
+
env.compressCallIdsToHide?.add(exec.callId);
|
|
1260
|
+
}
|
|
1066
1261
|
const previousIds = new Set(turn.state.blocks.map((block) => block.blockId));
|
|
1067
1262
|
const newBlocks = applied.state.blocks.filter((block) => !previousIds.has(block.blockId));
|
|
1068
1263
|
const blockByRangeKey = new Map(newBlocks.map((block) => [`${block.startRef}::${block.endRef}`, block]));
|
|
@@ -1125,8 +1320,9 @@ async function handleCompress(env, args, exec) {
|
|
|
1125
1320
|
}
|
|
1126
1321
|
const summaryLine = `Compressed ${applied.result.blocksCreated} block(s), ~${applied.result.tokensCompressed} tokens reclaimed.`;
|
|
1127
1322
|
const totalSkipped = skippedRanges + alreadyCompressedNotes.length;
|
|
1128
|
-
const
|
|
1129
|
-
const
|
|
1323
|
+
const failedLines = applied.result.errors.map((error) => ` ${error}`);
|
|
1324
|
+
const warningLines = [...freeWarnings.map((warning) => ` ${warning}`), ...failedLines, ...alreadyCompressedNotes, ...lines];
|
|
1325
|
+
const footer = totalSkipped > 0 ? ` (${totalSkipped} range(s) skipped or failed \u2014 see above)` : "";
|
|
1130
1326
|
return { text: `${summaryLine}
|
|
1131
1327
|
${[...warningLines, footer].filter((line) => line !== "").join("\n")}` };
|
|
1132
1328
|
}
|
|
@@ -1360,6 +1556,8 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
1360
1556
|
/** Resolved prompt templates (validated at construction — fail-fast on template typos). */
|
|
1361
1557
|
prompts;
|
|
1362
1558
|
lastNudgeTurn = /* @__PURE__ */ new Map();
|
|
1559
|
+
/** Successful compress call ids awaiting their tool/result so the pair can be hidden. */
|
|
1560
|
+
compressCallIdsToHide = /* @__PURE__ */ new Set();
|
|
1363
1561
|
/** Per provider/model route the resolved window (probe failures cached too). */
|
|
1364
1562
|
windowCache = /* @__PURE__ */ new Map();
|
|
1365
1563
|
constructor(ctx, config = {}) {
|
|
@@ -1379,7 +1577,8 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
1379
1577
|
nudgeEmergencyThresholdPct: this.config.nudgeEmergencyThresholdPct,
|
|
1380
1578
|
coreOverrides: this.config.coreOverrides,
|
|
1381
1579
|
windowFor: (agent) => this.windowFor(agent),
|
|
1382
|
-
prompts: this.prompts
|
|
1580
|
+
prompts: this.prompts,
|
|
1581
|
+
compressCallIdsToHide: this.compressCallIdsToHide
|
|
1383
1582
|
};
|
|
1384
1583
|
const tools = ctx.get("tools");
|
|
1385
1584
|
if (tools !== void 0) {
|
|
@@ -1413,16 +1612,27 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
1413
1612
|
if (name === "commands") registerCommand();
|
|
1414
1613
|
});
|
|
1415
1614
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1615
|
+
ctx.on("session/event", (session, event) => {
|
|
1616
|
+
if (event.type !== "tool/result") return;
|
|
1617
|
+
const message = event.data.message;
|
|
1618
|
+
const block = message.content[0];
|
|
1619
|
+
const callId = block?.toolCallId ?? message.source.callId;
|
|
1620
|
+
if (typeof callId !== "string" || !this.compressCallIdsToHide.has(callId)) return;
|
|
1621
|
+
this.compressCallIdsToHide.delete(callId);
|
|
1622
|
+
deferCompressPairHide(session, callId, event.seq, (error) => {
|
|
1623
|
+
ctx.logger.warn(`billion-context-dsh: hide compress call/result pair failed: ${String(error)}`);
|
|
1424
1624
|
});
|
|
1425
|
-
}
|
|
1625
|
+
});
|
|
1626
|
+
ctx.on("agent/pre-step", async (payload, next) => {
|
|
1627
|
+
stripOrphanedSurfaceToolMessages(payload.agent.session);
|
|
1628
|
+
if (!this.config.autoNudge) return next();
|
|
1629
|
+
const decision = await next();
|
|
1630
|
+
if (decision.kind === "reject") return decision;
|
|
1631
|
+
const window = await this.windowFor(payload.agent);
|
|
1632
|
+
const outcome = buildNudge(payload.agent, { ...env, modelContextLimit: window.limit }, this.lastNudgeTurn);
|
|
1633
|
+
if (outcome === null) return decision;
|
|
1634
|
+
return { kind: "enter", messages: [...decision.messages, outcome.message] };
|
|
1635
|
+
});
|
|
1426
1636
|
const systemPrompt = ctx.get("systemPrompt");
|
|
1427
1637
|
if (systemPrompt !== void 0) {
|
|
1428
1638
|
systemPrompt.section({
|
|
@@ -1519,6 +1729,7 @@ export {
|
|
|
1519
1729
|
expandShadowedSeqs,
|
|
1520
1730
|
extractEventText,
|
|
1521
1731
|
findOpenTurn,
|
|
1732
|
+
hideCompressToolPair,
|
|
1522
1733
|
kernelConfigFor,
|
|
1523
1734
|
makeTools,
|
|
1524
1735
|
projectEvent,
|
|
@@ -1531,6 +1742,7 @@ export {
|
|
|
1531
1742
|
resolveTokenCount,
|
|
1532
1743
|
runCompactionTransaction,
|
|
1533
1744
|
shadowedSeqsOf,
|
|
1745
|
+
stripOrphanedSurfaceToolMessages,
|
|
1534
1746
|
summarySeqOfKernelBlock,
|
|
1535
1747
|
surfaceEventsOf,
|
|
1536
1748
|
windowSourceLabel
|