@namzu/sdk 53.2.2 → 54.0.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.
- package/CHANGELOG.md +24 -0
- package/dist/compaction/context-breakdown.d.ts +85 -0
- package/dist/compaction/context-breakdown.d.ts.map +1 -0
- package/dist/compaction/context-breakdown.js +149 -0
- package/dist/compaction/context-breakdown.js.map +1 -0
- package/dist/compaction/token-estimate.d.ts +2 -0
- package/dist/compaction/token-estimate.d.ts.map +1 -1
- package/dist/compaction/token-estimate.js +4 -0
- package/dist/compaction/token-estimate.js.map +1 -1
- package/dist/public-runtime.d.ts +7 -2
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +6 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/runtime/cache-clock.d.ts +39 -0
- package/dist/runtime/cache-clock.d.ts.map +1 -0
- package/dist/runtime/cache-clock.js +48 -0
- package/dist/runtime/cache-clock.js.map +1 -0
- package/dist/runtime/query/compact-session.d.ts +37 -2
- package/dist/runtime/query/compact-session.d.ts.map +1 -1
- package/dist/runtime/query/compact-session.js +77 -3
- package/dist/runtime/query/compact-session.js.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +3 -0
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +8 -0
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/store/budget/disk.js +4 -1
- package/dist/store/budget/disk.js.map +1 -1
- package/dist/tools/admit-facts.d.ts.map +1 -1
- package/dist/tools/admit-facts.js +6 -0
- package/dist/tools/admit-facts.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +23 -9
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/edit-apply.d.ts +7 -4
- package/dist/tools/builtins/edit-apply.d.ts.map +1 -1
- package/dist/tools/builtins/edit-apply.js +3 -0
- package/dist/tools/builtins/edit-apply.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +27 -7
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/exit-semantics.d.ts +31 -0
- package/dist/tools/builtins/exit-semantics.d.ts.map +1 -0
- package/dist/tools/builtins/exit-semantics.js +198 -0
- package/dist/tools/builtins/exit-semantics.js.map +1 -0
- package/dist/tools/builtins/write-file.js +7 -2
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/memory/delete.js +1 -1
- package/dist/tools/memory/delete.js.map +1 -1
- package/dist/tools/memory/read.js +1 -1
- package/dist/tools/memory/read.js.map +1 -1
- package/dist/tools/memory/save.d.ts.map +1 -1
- package/dist/tools/memory/save.js +4 -3
- package/dist/tools/memory/save.js.map +1 -1
- package/dist/tools/memory/search.js +1 -1
- package/dist/tools/memory/search.js.map +1 -1
- package/dist/tools/memory/update.js +1 -1
- package/dist/tools/memory/update.js.map +1 -1
- package/dist/toolsets/manager.d.ts.map +1 -1
- package/dist/toolsets/manager.js +6 -1
- package/dist/toolsets/manager.js.map +1 -1
- package/dist/types/common/index.d.ts +19 -0
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +9 -0
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/provider/chat.d.ts +8 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +6 -0
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/session/checkpoint.d.ts +52 -0
- package/dist/types/session/checkpoint.d.ts.map +1 -1
- package/dist/types/session/checkpoint.js +5 -0
- package/dist/types/session/checkpoint.js.map +1 -1
- package/dist/types/session/events.d.ts +9 -0
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +181 -6
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +1 -1
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/tool/presentation.d.ts +17 -1
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/compaction/context-breakdown.ts +228 -0
- package/src/compaction/token-estimate.ts +5 -0
- package/src/public-runtime.ts +23 -2
- package/src/runtime/cache-clock.ts +67 -0
- package/src/runtime/query/compact-session.ts +101 -3
- package/src/runtime/query/executor/tool-call-admission.ts +3 -0
- package/src/runtime/query/iteration/index.ts +8 -0
- package/src/store/budget/disk.ts +4 -1
- package/src/tools/admit-facts.ts +6 -0
- package/src/tools/builtins/bash.ts +25 -9
- package/src/tools/builtins/edit-apply.ts +12 -2
- package/src/tools/builtins/edit.ts +34 -8
- package/src/tools/builtins/exit-semantics.ts +198 -0
- package/src/tools/builtins/write-file.ts +7 -2
- package/src/tools/memory/delete.ts +1 -1
- package/src/tools/memory/read.ts +1 -1
- package/src/tools/memory/save.ts +4 -3
- package/src/tools/memory/search.ts +1 -1
- package/src/tools/memory/update.ts +1 -1
- package/src/toolsets/manager.ts +6 -1
- package/src/types/common/index.ts +27 -0
- package/src/types/provider/chat.ts +8 -0
- package/src/types/provider/config.ts +6 -0
- package/src/types/session/checkpoint.ts +5 -0
- package/src/types/session/events.ts +6 -0
- package/src/types/session/records.ts +1 -1
- package/src/types/tool/presentation.ts +16 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation.d.ts","sourceRoot":"","sources":["../../../src/types/tool/presentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY;AACvB,4EAA4E;AAC1E;IACA,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,UAAU,CAAA;IAClC,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAA;IACjC,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAA;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAA;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CACzB;AACH;;;;GAIG;GACD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACrB;AACH,qCAAqC;GACnC;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpF;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,gDAAgD;IAChD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAC1B,WAAW,GACX,mBAAmB,GACnB,YAAY,GACZ,gBAAgB,GAChB,SAAS,GACT,qBAAqB,GACrB,cAAc,GACd,WAAW,GACX,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"presentation.d.ts","sourceRoot":"","sources":["../../../src/types/tool/presentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY;AACvB,4EAA4E;AAC1E;IACA,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,UAAU,CAAA;IAClC,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAA;IACjC,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAA;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAA;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CACzB;AACH;;;;GAIG;GACD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACrB;AACH,qCAAqC;GACnC;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpF;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,gDAAgD;IAChD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAC1B,WAAW,GACX,mBAAmB,GACnB,YAAY,GACZ,gBAAgB,GAChB,SAAS,GACT,qBAAqB,GACrB,cAAc,GACd,WAAW,GACX,eAAe;AACjB,8DAA8D;GAC5D,UAAU;AACZ,uEAAuE;GACrE,iBAAiB;AACnB,6CAA6C;GAC3C,gBAAgB;AAClB,iDAAiD;GAC/C,YAAY;AACd,uFAAuF;GACrF,mBAAmB,CAAA;AAEtB;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GAClB;IACA,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,qCAAqC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAA;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAA;IAChC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5C,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CACzB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oFAAoF;IACpF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CACzB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAC3B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAC3B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IACxC,+FAA+F;IAC/F,QAAQ,CAAC,cAAc,CAAC,EAAE,IAAI,CAAA;CAC7B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAA;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,oGAAoG;IACpG,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,IAAI,CAAA;CAC7B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;IAClC,wEAAwE;IACxE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAEJ,iDAAiD;AACjD,MAAM,MAAM,cAAc,GAAG,YAAY,CAAA;AAEzC;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB,CAAC,MAAM,GAAG,OAAO;IACjD,WAAW,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAA;IACrD,aAAa,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,cAAc,GAAG,SAAS,CAAA;CAC7E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namzu/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "54.0.0",
|
|
4
4
|
"description": "Open-source AI kernel for TypeScript: runs an agent the way an operating system runs a process, with an identity and a budget, scheduled tool calls and checkpointed turns.",
|
|
5
5
|
"license": "FSL-1.1-MIT",
|
|
6
6
|
"engines": {
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the context window goes: the arithmetic behind a `/context` view.
|
|
3
|
+
*
|
|
4
|
+
* Pure. A host hands in what it knows (sizes of the system prompt, the tool
|
|
5
|
+
* catalogue, instruction files, skills, agents, and the conversation) and gets
|
|
6
|
+
* the categories back in the order they are drawn, with the free room and the
|
|
7
|
+
* compaction buffer accounted for. Nothing here reads a provider or a file.
|
|
8
|
+
*
|
|
9
|
+
* The conversation's own share is never counted separately: it is whatever the
|
|
10
|
+
* measured prompt size is once the fixed parts are taken out, so the categories
|
|
11
|
+
* always add up to the figure the provider reported.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { CHARS_PER_TOKEN } from '../constants/limits.js'
|
|
15
|
+
import type { Message } from '../types/message/index.js'
|
|
16
|
+
import type { LLMToolSchema } from '../types/tool/index.js'
|
|
17
|
+
import { TRIGGER_BUFFER_TOKENS, TRIGGER_OUTPUT_RESERVE_CAP_TOKENS } from './context-window.js'
|
|
18
|
+
import { estimateMessageTokens, estimateTextTokens } from './token-estimate.js'
|
|
19
|
+
|
|
20
|
+
/** The categories, in the order they are laid out. */
|
|
21
|
+
export const CONTEXT_CATEGORY_ORDER = [
|
|
22
|
+
'system-prompt',
|
|
23
|
+
'system-tools',
|
|
24
|
+
'mcp-tools',
|
|
25
|
+
'mcp-instructions',
|
|
26
|
+
'agents',
|
|
27
|
+
'memory',
|
|
28
|
+
'skills',
|
|
29
|
+
'messages',
|
|
30
|
+
] as const
|
|
31
|
+
|
|
32
|
+
export type ContextCategoryId = (typeof CONTEXT_CATEGORY_ORDER)[number] | 'buffer' | 'free'
|
|
33
|
+
export type ContextCategoryKind = 'used' | 'free' | 'buffer'
|
|
34
|
+
|
|
35
|
+
export interface ContextCategory {
|
|
36
|
+
readonly id: ContextCategoryId
|
|
37
|
+
readonly name: string
|
|
38
|
+
readonly tokens: number
|
|
39
|
+
readonly kind: ContextCategoryKind
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const NAMES: Record<(typeof CONTEXT_CATEGORY_ORDER)[number], string> = {
|
|
43
|
+
'system-prompt': 'System prompt',
|
|
44
|
+
'system-tools': 'System tools',
|
|
45
|
+
'mcp-tools': 'MCP tools',
|
|
46
|
+
'mcp-instructions': 'MCP server instructions',
|
|
47
|
+
agents: 'Custom agents',
|
|
48
|
+
memory: 'Memory files',
|
|
49
|
+
skills: 'Skills',
|
|
50
|
+
messages: 'Messages',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ContextUsageInput {
|
|
54
|
+
readonly windowTokens: number
|
|
55
|
+
/**
|
|
56
|
+
* `auto` when the window is what the provider or model table declares.
|
|
57
|
+
* Anything else (`config`, `default`) means the figure was overridden or
|
|
58
|
+
* guessed, and the compaction buffer is then drawn.
|
|
59
|
+
*/
|
|
60
|
+
readonly windowSource: 'auto' | 'config' | 'default'
|
|
61
|
+
/** What the model may write in one reply; absent reserves the full cap. */
|
|
62
|
+
readonly maxOutputTokens?: number
|
|
63
|
+
/** Whether the session summarises on its own. */
|
|
64
|
+
readonly autoCompact: boolean
|
|
65
|
+
/**
|
|
66
|
+
* The size of everything being sent: counted by the provider, or estimated.
|
|
67
|
+
* Absent, the conversation is estimated from `messages`.
|
|
68
|
+
*/
|
|
69
|
+
readonly contextTokens?: number
|
|
70
|
+
readonly systemPromptTokens?: number
|
|
71
|
+
/** Tokens of the built-in tool definitions. */
|
|
72
|
+
readonly systemToolTokens?: number
|
|
73
|
+
readonly mcpToolTokens?: number
|
|
74
|
+
readonly mcpInstructionTokens?: number
|
|
75
|
+
readonly agentTokens?: number
|
|
76
|
+
readonly memoryTokens?: number
|
|
77
|
+
readonly skillTokens?: number
|
|
78
|
+
/** The conversation, for the estimate and for the per-tool breakdown. */
|
|
79
|
+
readonly messages?: readonly Message[]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ContextMessageBreakdown {
|
|
83
|
+
/** Tokens of the calls the model wrote, by tool name. */
|
|
84
|
+
readonly toolCallsByType: Readonly<Record<string, number>>
|
|
85
|
+
/** Tokens of the results that came back, by the name of the tool called. */
|
|
86
|
+
readonly toolResultsByType: Readonly<Record<string, number>>
|
|
87
|
+
readonly assistantTokens: number
|
|
88
|
+
readonly userTokens: number
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ContextBreakdown {
|
|
92
|
+
readonly categories: readonly ContextCategory[]
|
|
93
|
+
readonly windowTokens: number
|
|
94
|
+
/** Everything in use, which can exceed the window. */
|
|
95
|
+
readonly totalTokens: number
|
|
96
|
+
/** Whole percent of the window in use. */
|
|
97
|
+
readonly percentage: number
|
|
98
|
+
/** The compaction buffer, 0 when it is not drawn. */
|
|
99
|
+
readonly bufferTokens: number
|
|
100
|
+
/** Whether the session summarises on its own; names the buffer. */
|
|
101
|
+
readonly autoCompact: boolean
|
|
102
|
+
/** How far past the window the context is; 0 while it fits. */
|
|
103
|
+
readonly overflowTokens: number
|
|
104
|
+
readonly messageBreakdown: ContextMessageBreakdown
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Per-tool size of a catalogue already rendered the way it is sent. */
|
|
108
|
+
export function describeToolCatalogTokens(
|
|
109
|
+
tools: readonly LLMToolSchema[],
|
|
110
|
+
): { name: string; tokens: number }[] {
|
|
111
|
+
return tools.map((tool) => {
|
|
112
|
+
let length = 0
|
|
113
|
+
try {
|
|
114
|
+
length = JSON.stringify(tool).length
|
|
115
|
+
} catch {
|
|
116
|
+
// A tool that cannot render is the model call's problem to report.
|
|
117
|
+
}
|
|
118
|
+
return { name: tool.function.name, tokens: Math.ceil(length / CHARS_PER_TOKEN) }
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function nonNegative(value: number | undefined): number {
|
|
123
|
+
return value !== undefined && Number.isFinite(value) && value > 0 ? Math.round(value) : 0
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function bufferFor(input: ContextUsageInput): number {
|
|
127
|
+
if (input.autoCompact) {
|
|
128
|
+
// Drawn only when the window was overridden: on a declared window the
|
|
129
|
+
// room kept for a reply is not a thing the person chose.
|
|
130
|
+
if (input.windowSource === 'auto') return 0
|
|
131
|
+
const reserve = Math.min(
|
|
132
|
+
input.maxOutputTokens !== undefined && input.maxOutputTokens > 0
|
|
133
|
+
? input.maxOutputTokens
|
|
134
|
+
: TRIGGER_OUTPUT_RESERVE_CAP_TOKENS,
|
|
135
|
+
TRIGGER_OUTPUT_RESERVE_CAP_TOKENS,
|
|
136
|
+
)
|
|
137
|
+
return reserve + TRIGGER_BUFFER_TOKENS
|
|
138
|
+
}
|
|
139
|
+
return COMPACT_BUFFER_TOKENS
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Room kept for a manual compaction when nothing summarises on its own. */
|
|
143
|
+
export const COMPACT_BUFFER_TOKENS = 3_000
|
|
144
|
+
|
|
145
|
+
function tokensOfContent(message: Message): number {
|
|
146
|
+
return estimateMessageTokens(message)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function breakdownMessages(messages: readonly Message[]): ContextMessageBreakdown {
|
|
150
|
+
const toolCallsByType: Record<string, number> = {}
|
|
151
|
+
const toolResultsByType: Record<string, number> = {}
|
|
152
|
+
const nameOfCall = new Map<string, string>()
|
|
153
|
+
let assistantTokens = 0
|
|
154
|
+
let userTokens = 0
|
|
155
|
+
for (const message of messages) {
|
|
156
|
+
if (message.role === 'assistant') {
|
|
157
|
+
let calls = 0
|
|
158
|
+
for (const call of message.toolCalls ?? []) {
|
|
159
|
+
nameOfCall.set(call.id, call.function.name)
|
|
160
|
+
const tokens = estimateTextTokens(call.function.name + call.function.arguments)
|
|
161
|
+
toolCallsByType[call.function.name] = (toolCallsByType[call.function.name] ?? 0) + tokens
|
|
162
|
+
calls += tokens
|
|
163
|
+
}
|
|
164
|
+
assistantTokens += Math.max(0, tokensOfContent(message) - calls)
|
|
165
|
+
} else if (message.role === 'tool') {
|
|
166
|
+
const name = nameOfCall.get(message.toolCallId) ?? 'unknown'
|
|
167
|
+
toolResultsByType[name] = (toolResultsByType[name] ?? 0) + tokensOfContent(message)
|
|
168
|
+
} else if (message.role === 'user') {
|
|
169
|
+
userTokens += tokensOfContent(message)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { toolCallsByType, toolResultsByType, assistantTokens, userTokens }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Split a window into the parts that fill it. */
|
|
176
|
+
export function analyzeContextUsage(input: ContextUsageInput): ContextBreakdown {
|
|
177
|
+
const windowTokens = Math.max(0, Math.round(input.windowTokens))
|
|
178
|
+
const fixed: Record<(typeof CONTEXT_CATEGORY_ORDER)[number], number> = {
|
|
179
|
+
'system-prompt': nonNegative(input.systemPromptTokens),
|
|
180
|
+
'system-tools': nonNegative(input.systemToolTokens),
|
|
181
|
+
'mcp-tools': nonNegative(input.mcpToolTokens),
|
|
182
|
+
'mcp-instructions': nonNegative(input.mcpInstructionTokens),
|
|
183
|
+
agents: nonNegative(input.agentTokens),
|
|
184
|
+
memory: nonNegative(input.memoryTokens),
|
|
185
|
+
skills: nonNegative(input.skillTokens),
|
|
186
|
+
messages: 0,
|
|
187
|
+
}
|
|
188
|
+
const fixedTotal = Object.values(fixed).reduce((sum, tokens) => sum + tokens, 0)
|
|
189
|
+
const messages = input.messages ?? []
|
|
190
|
+
const bufferTokens = Math.min(bufferFor(input), windowTokens)
|
|
191
|
+
|
|
192
|
+
// The conversation is what the measured size leaves once the fixed parts
|
|
193
|
+
// are taken out; without a measurement it is counted from the messages.
|
|
194
|
+
const measured = nonNegative(input.contextTokens)
|
|
195
|
+
const rawMessages =
|
|
196
|
+
measured > 0
|
|
197
|
+
? Math.max(0, measured - fixedTotal)
|
|
198
|
+
: messages.reduce((sum, message) => sum + tokensOfContent(message), 0)
|
|
199
|
+
fixed.messages = Math.min(rawMessages, Math.max(0, windowTokens - fixedTotal - bufferTokens))
|
|
200
|
+
|
|
201
|
+
const totalTokens = measured > 0 ? Math.max(measured, fixedTotal) : fixedTotal + rawMessages
|
|
202
|
+
const used = fixedTotal + fixed.messages
|
|
203
|
+
const categories: ContextCategory[] = []
|
|
204
|
+
for (const id of CONTEXT_CATEGORY_ORDER) {
|
|
205
|
+
if (fixed[id] > 0) categories.push({ id, name: NAMES[id], tokens: fixed[id], kind: 'used' })
|
|
206
|
+
}
|
|
207
|
+
const free = Math.max(0, windowTokens - used - bufferTokens)
|
|
208
|
+
if (bufferTokens > 0) {
|
|
209
|
+
categories.push({
|
|
210
|
+
id: 'buffer',
|
|
211
|
+
name: input.autoCompact ? 'Autocompact buffer' : 'Compact buffer',
|
|
212
|
+
tokens: bufferTokens,
|
|
213
|
+
kind: 'buffer',
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
categories.push({ id: 'free', name: 'Free space', tokens: free, kind: 'free' })
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
categories,
|
|
220
|
+
windowTokens,
|
|
221
|
+
totalTokens,
|
|
222
|
+
percentage: windowTokens > 0 ? Math.round((totalTokens / windowTokens) * 100) : 0,
|
|
223
|
+
bufferTokens,
|
|
224
|
+
autoCompact: input.autoCompact,
|
|
225
|
+
overflowTokens: Math.max(0, totalTokens - windowTokens),
|
|
226
|
+
messageBreakdown: breakdownMessages(messages),
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -52,6 +52,11 @@ export function estimateMessageTokens(message: Message): number {
|
|
|
52
52
|
return Math.ceil(chars / CHARS_PER_TOKEN) + rich
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/** Tokens in a piece of text by the same characters-per-token approximation. */
|
|
56
|
+
export function estimateTextTokens(text: string): number {
|
|
57
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN)
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
export function estimateMessagesTokens(messages: readonly Message[]): number {
|
|
56
61
|
return messages.reduce((total, message) => total + estimateMessageTokens(message), 0)
|
|
57
62
|
}
|
package/src/public-runtime.ts
CHANGED
|
@@ -404,8 +404,13 @@ export type {
|
|
|
404
404
|
// session between turns.
|
|
405
405
|
export { abandonTurn } from './runtime/query/abandon-turn.js'
|
|
406
406
|
export type { AbandonTurnOptions, SessionLocatorOptions } from './runtime/query/abandon-turn.js'
|
|
407
|
-
export {
|
|
408
|
-
export type {
|
|
407
|
+
export { CacheClock } from './runtime/cache-clock.js'
|
|
408
|
+
export type { CacheClockState } from './runtime/cache-clock.js'
|
|
409
|
+
export { compactSession, compactSessionRange } from './runtime/query/compact-session.js'
|
|
410
|
+
export type {
|
|
411
|
+
CompactSessionParams,
|
|
412
|
+
CompactSessionRangeParams,
|
|
413
|
+
} from './runtime/query/compact-session.js'
|
|
409
414
|
export { prepareForkState } from './runtime/query/fork/prepare.js'
|
|
410
415
|
export { listCheckpoints } from './runtime/query/fork/list.js'
|
|
411
416
|
export { DecisionParser, FallbackResolver } from './runtime/decision/index.js'
|
|
@@ -1563,6 +1568,22 @@ export {
|
|
|
1563
1568
|
// The gate is the reason this is a list rather than a fourth accident.
|
|
1564
1569
|
export type { ResolvedContextWindow } from './compaction/context-window.js'
|
|
1565
1570
|
export { resolveTriggerThreshold } from './compaction/context-window.js'
|
|
1571
|
+
// What the context window is made of, for a host that draws the answer to
|
|
1572
|
+
// "where did my context go".
|
|
1573
|
+
export {
|
|
1574
|
+
analyzeContextUsage,
|
|
1575
|
+
describeToolCatalogTokens,
|
|
1576
|
+
CONTEXT_CATEGORY_ORDER,
|
|
1577
|
+
} from './compaction/context-breakdown.js'
|
|
1578
|
+
export type {
|
|
1579
|
+
ContextBreakdown,
|
|
1580
|
+
ContextCategory,
|
|
1581
|
+
ContextCategoryId,
|
|
1582
|
+
ContextCategoryKind,
|
|
1583
|
+
ContextMessageBreakdown,
|
|
1584
|
+
ContextUsageInput,
|
|
1585
|
+
} from './compaction/context-breakdown.js'
|
|
1586
|
+
export { estimateTextTokens } from './compaction/token-estimate.js'
|
|
1566
1587
|
export type { CompactRegionInput } from './compaction/manual.js'
|
|
1567
1588
|
export type {
|
|
1568
1589
|
CompactionVerificationOptions,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { TokenUsage } from '../types/common/index.js'
|
|
2
|
+
|
|
3
|
+
/** What the clock knows about the prompt cache after the latest model call. */
|
|
4
|
+
export interface CacheClockState {
|
|
5
|
+
/** When the last call that touched the cache finished (ms since epoch). */
|
|
6
|
+
readonly lastActivityAt: number
|
|
7
|
+
/** How long the provider keeps the cache, as that call reported it. */
|
|
8
|
+
readonly ttlMs: number
|
|
9
|
+
/**
|
|
10
|
+
* When the cache lapses: `lastActivityAt + ttlMs`. Only set when the call
|
|
11
|
+
* actually read or wrote cache; a call that touched none leaves the cache
|
|
12
|
+
* (and so its expiry) unknown rather than assumed.
|
|
13
|
+
*/
|
|
14
|
+
readonly expiresAt: number | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Tracks when a conversation's prompt cache will lapse.
|
|
19
|
+
*
|
|
20
|
+
* Fed by each completed model call. The lifetime is never guessed: a call whose
|
|
21
|
+
* usage carries no `cacheTtlMs` (a driver that does not report one) clears the
|
|
22
|
+
* clock, so nothing downstream can act on a lifetime nobody stated. Pure of
|
|
23
|
+
* timers: the caller passes `now`.
|
|
24
|
+
*/
|
|
25
|
+
export class CacheClock {
|
|
26
|
+
#state: CacheClockState | null = null
|
|
27
|
+
|
|
28
|
+
/** Record a finished call. `usage` is that call's own usage, not a turn total. */
|
|
29
|
+
record(usage: TokenUsage, at: number): void {
|
|
30
|
+
if (usage.cacheTtlMs === undefined || !(usage.cacheTtlMs > 0)) {
|
|
31
|
+
this.#state = null
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
const touched = usage.cachedTokens + usage.cacheWriteTokens > 0
|
|
35
|
+
this.#state = {
|
|
36
|
+
lastActivityAt: at,
|
|
37
|
+
ttlMs: usage.cacheTtlMs,
|
|
38
|
+
expiresAt: touched ? at + usage.cacheTtlMs : null,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Forget everything (the conversation was replaced or its context rewritten). */
|
|
43
|
+
reset(): void {
|
|
44
|
+
this.#state = null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get state(): CacheClockState | null {
|
|
48
|
+
return this.#state
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Whether the cache is still alive at `now`. False when unknown. */
|
|
52
|
+
warm(now: number): boolean {
|
|
53
|
+
const state = this.#state
|
|
54
|
+
return state !== null && state.expiresAt !== null && now < state.expiresAt
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The moment `fraction` of the cache's remaining life since the last
|
|
59
|
+
* activity has passed: `lastActivityAt + (expiresAt - lastActivityAt) * fraction`.
|
|
60
|
+
* `null` when the expiry is unknown.
|
|
61
|
+
*/
|
|
62
|
+
dueAt(fraction: number): number | null {
|
|
63
|
+
const state = this.#state
|
|
64
|
+
if (state === null || state.expiresAt === null) return null
|
|
65
|
+
return state.lastActivityAt + (state.expiresAt - state.lastActivityAt) * fraction
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type CompactNowInput,
|
|
3
|
+
type CompactionResult,
|
|
4
|
+
compactNow,
|
|
5
|
+
compactRegion,
|
|
6
|
+
} from '../../compaction/manual.js'
|
|
2
7
|
import { readFoldedHistory } from '../../manager/session/turn-recorder.js'
|
|
3
8
|
import { NamzuError } from '../../types/errors/index.js'
|
|
4
9
|
import type { MessageId, SessionId } from '../../types/ids/index.js'
|
|
@@ -10,13 +15,19 @@ import { type SessionLocatorOptions, locateSessionLog, withSessionLease } from '
|
|
|
10
15
|
/** A compaction a host asks for between turns. */
|
|
11
16
|
export interface CompactSessionParams extends Omit<CompactNowInput, 'messages' | 'onShed'> {
|
|
12
17
|
readonly sessionId: SessionId
|
|
18
|
+
/**
|
|
19
|
+
* Who asked. `'manual'` (the default) is a person's request; `'idle'` is a
|
|
20
|
+
* host compacting a conversation nobody is using before its prompt cache
|
|
21
|
+
* lapses. Recorded on the `compaction` record.
|
|
22
|
+
*/
|
|
23
|
+
readonly trigger?: 'manual' | 'idle'
|
|
13
24
|
/** Where to find the session's log, and the lease to append under. */
|
|
14
25
|
readonly locator?: SessionLocatorOptions
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
/**
|
|
18
29
|
* Compact a session outside any turn. Reads the session's context from its
|
|
19
|
-
* log, compacts it, and appends a `compaction{ trigger
|
|
30
|
+
* log, compacts it, and appends a `compaction{ trigger }` record (`'manual'` unless the host says `'idle'`)
|
|
20
31
|
* with no `turnId`: the messages the pass kept are named by id, the summary
|
|
21
32
|
* is recorded in full, and the shed messages stay in the log for audit.
|
|
22
33
|
*
|
|
@@ -26,7 +37,7 @@ export interface CompactSessionParams extends Omit<CompactNowInput, 'messages' |
|
|
|
26
37
|
export async function compactSession(
|
|
27
38
|
params: CompactSessionParams,
|
|
28
39
|
): Promise<CompactionResult | null> {
|
|
29
|
-
const { sessionId, locator = {}, ...input } = params
|
|
40
|
+
const { sessionId, locator = {}, trigger = 'manual', ...input } = params
|
|
30
41
|
const log = await locateSessionLog(sessionId, locator)
|
|
31
42
|
return withSessionLease(log, locator.lease, async (lease) => {
|
|
32
43
|
const active = await log.activeTurn({ lease })
|
|
@@ -47,6 +58,93 @@ export async function compactSession(
|
|
|
47
58
|
})
|
|
48
59
|
}
|
|
49
60
|
const { summary, keptMessageIds } = compactionShape(result.messages, ids)
|
|
61
|
+
await log.append(lease, {
|
|
62
|
+
type: 'compaction',
|
|
63
|
+
compactionId: generateMessageId(),
|
|
64
|
+
strategy: input.config.strategy,
|
|
65
|
+
trigger,
|
|
66
|
+
replacesSeqRange: [1, head.pointer.seq],
|
|
67
|
+
summary,
|
|
68
|
+
keptMessageIds,
|
|
69
|
+
tokensBefore: 0,
|
|
70
|
+
tokensAfter: 0,
|
|
71
|
+
})
|
|
72
|
+
return result
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A compaction of one span of a session's context, asked for between turns. */
|
|
77
|
+
export interface CompactSessionRangeParams extends CompactSessionParams {
|
|
78
|
+
/**
|
|
79
|
+
* The first message to summarise, by the id the log recorded it under.
|
|
80
|
+
* Omitted: the span starts at the beginning of the context.
|
|
81
|
+
*/
|
|
82
|
+
readonly from?: MessageId
|
|
83
|
+
/**
|
|
84
|
+
* The message the span stops before; it and everything after it stay as
|
|
85
|
+
* they are. Omitted: the span runs to the end of the context.
|
|
86
|
+
*/
|
|
87
|
+
readonly until?: MessageId
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Compact one span of a session's context outside any turn.
|
|
92
|
+
*
|
|
93
|
+
* Like {@link compactSession}, but only the messages from `from` up to (not
|
|
94
|
+
* including) `until` are summarised; the rest of the context stays verbatim.
|
|
95
|
+
* A host uses it to fold away "everything before this message" or "this
|
|
96
|
+
* message and everything after it" without touching the other side. Pinned
|
|
97
|
+
* (`retain`) messages inside the span stay as they are.
|
|
98
|
+
*
|
|
99
|
+
* Throws `not_found` when `from` or `until` is not in the session's current
|
|
100
|
+
* context (a compaction may already have folded it away), and
|
|
101
|
+
* `invalid_config` when a boundary would split a tool call from its result.
|
|
102
|
+
* `null` when the span holds nothing worth summarising; nothing is appended
|
|
103
|
+
* then. Refused while a turn is active (`TurnInProgressError`).
|
|
104
|
+
*/
|
|
105
|
+
export async function compactSessionRange(
|
|
106
|
+
params: CompactSessionRangeParams,
|
|
107
|
+
): Promise<CompactionResult | null> {
|
|
108
|
+
const { sessionId, locator = {}, from, until, ...input } = params
|
|
109
|
+
const log = await locateSessionLog(sessionId, locator)
|
|
110
|
+
return withSessionLease(log, locator.lease, async (lease) => {
|
|
111
|
+
const active = await log.activeTurn({ lease })
|
|
112
|
+
if (active) {
|
|
113
|
+
throw new TurnInProgressError({ sessionId, activeTurnId: active.turnId, state: active.state })
|
|
114
|
+
}
|
|
115
|
+
const history = await readFoldedHistory(log)
|
|
116
|
+
const position = (id: MessageId | undefined, fallback: number): number => {
|
|
117
|
+
if (id === undefined) return fallback
|
|
118
|
+
const at = history.findIndex((entry) => entry.messageId === id)
|
|
119
|
+
if (at < 0) {
|
|
120
|
+
throw new NamzuError({
|
|
121
|
+
code: 'not_found',
|
|
122
|
+
message: `Message ${id} is not in session ${sessionId}'s current context; it may already have been summarised.`,
|
|
123
|
+
details: { sessionId, messageId: id },
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
return at
|
|
127
|
+
}
|
|
128
|
+
const start = position(from, 0)
|
|
129
|
+
const end = position(until, history.length)
|
|
130
|
+
const ids = new Map<Message, MessageId>()
|
|
131
|
+
for (const entry of history) if (entry.messageId) ids.set(entry.message, entry.messageId)
|
|
132
|
+
const result = await compactRegion({
|
|
133
|
+
...input,
|
|
134
|
+
messages: history.map((entry) => entry.message),
|
|
135
|
+
start,
|
|
136
|
+
end,
|
|
137
|
+
})
|
|
138
|
+
if (!result) return null
|
|
139
|
+
const head = await log.head()
|
|
140
|
+
if (!head) {
|
|
141
|
+
throw new NamzuError({
|
|
142
|
+
code: 'not_found',
|
|
143
|
+
message: `Session ${sessionId} has an empty log; there is nothing to compact.`,
|
|
144
|
+
details: { sessionId },
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
const { summary, keptMessageIds } = compactionShape(result.messages, ids)
|
|
50
148
|
await log.append(lease, {
|
|
51
149
|
type: 'compaction',
|
|
52
150
|
compactionId: generateMessageId(),
|
|
@@ -290,6 +290,9 @@ export async function prepareDirectCall(
|
|
|
290
290
|
...(providerInput && !repairUsed && preparation.inputFailure
|
|
291
291
|
? { inputFailure: preparation.inputFailure }
|
|
292
292
|
: {}),
|
|
293
|
+
// The registry's typed reason (e.g. invalid arguments) is what a
|
|
294
|
+
// host keys its short label on; the message alone is wording.
|
|
295
|
+
...(preparation.result.facts ? { facts: preparation.result.facts } : {}),
|
|
293
296
|
}
|
|
294
297
|
}
|
|
295
298
|
|
|
@@ -1201,6 +1201,14 @@ export class IterationOrchestrator {
|
|
|
1201
1201
|
usage: recorder.tokenUsage,
|
|
1202
1202
|
budget: recorder.budget?.summary(),
|
|
1203
1203
|
cost: recorder.costInfo,
|
|
1204
|
+
...(response.usage.cacheTtlMs !== undefined
|
|
1205
|
+
? {
|
|
1206
|
+
cache: {
|
|
1207
|
+
ttlMs: response.usage.cacheTtlMs,
|
|
1208
|
+
touched: response.usage.cachedTokens + response.usage.cacheWriteTokens > 0,
|
|
1209
|
+
},
|
|
1210
|
+
}
|
|
1211
|
+
: {}),
|
|
1204
1212
|
...contextFigures,
|
|
1205
1213
|
})
|
|
1206
1214
|
|
package/src/store/budget/disk.ts
CHANGED
|
@@ -238,6 +238,9 @@ function usageAtLeast(current: TokenUsage | undefined, previous: TokenUsage | un
|
|
|
238
238
|
if (current === undefined) return false
|
|
239
239
|
return Object.entries(previous).every(
|
|
240
240
|
([field, amount]) =>
|
|
241
|
-
typeof amount !== 'number' ||
|
|
241
|
+
typeof amount !== 'number' ||
|
|
242
|
+
field === 'cacheTtlMs' ||
|
|
243
|
+
// `cacheTtlMs` is a per-call lifetime, not a running count: it has no order.
|
|
244
|
+
((current as unknown as Record<string, number | undefined>)[field] ?? 0) >= amount,
|
|
242
245
|
)
|
|
243
246
|
}
|
package/src/tools/admit-facts.ts
CHANGED
|
@@ -10,6 +10,11 @@ const FAILURE_REASONS: readonly ToolFailureReason[] = [
|
|
|
10
10
|
'unknown_tool',
|
|
11
11
|
'timed_out',
|
|
12
12
|
'invalid_input',
|
|
13
|
+
'not_read',
|
|
14
|
+
'changed_on_disk',
|
|
15
|
+
'text_not_found',
|
|
16
|
+
'not_unique',
|
|
17
|
+
'invalid_arguments',
|
|
13
18
|
]
|
|
14
19
|
|
|
15
20
|
const WRITE_OPERATIONS = ['created', 'updated', 'unchanged', 'wrote'] as const
|
|
@@ -105,6 +110,7 @@ export function admitToolFacts(value: unknown): ToolFacts | undefined {
|
|
|
105
110
|
typeof value.exitCode === 'number' && Number.isSafeInteger(value.exitCode)
|
|
106
111
|
? value.exitCode
|
|
107
112
|
: undefined,
|
|
113
|
+
exitNote: text(value.exitNote),
|
|
108
114
|
timedOut: flag(value.timedOut),
|
|
109
115
|
signal: text(value.signal),
|
|
110
116
|
binary:
|