acp-kernel 0.0.94-pr.438.290 → 0.0.94
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/DESIGN.md +137 -158
- package/PROVENANCE.md +40 -44
- package/README.md +30 -43
- package/dist/cache-report.d.ts.map +1 -1
- package/dist/{chunk-WR7OPWNT.js → chunk-NUQZKPQS.js} +3 -7
- package/dist/chunk-NUQZKPQS.js.map +1 -0
- package/dist/compress-tools.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/decompress.d.ts.map +1 -1
- package/dist/filter/apply.d.ts.map +1 -1
- package/dist/filter/index.d.ts +1 -1
- package/dist/filter/index.d.ts.map +1 -1
- package/dist/filter/types.d.ts.map +1 -1
- package/dist/handoff.d.ts.map +1 -1
- package/dist/hide-consumed.d.ts.map +1 -1
- package/dist/image-compress.d.ts.map +1 -1
- package/dist/index.js +75 -254
- package/dist/index.js.map +1 -1
- package/dist/message-kind.d.ts.map +1 -1
- package/dist/nudge-text.d.ts.map +1 -1
- package/dist/packs.d.ts.map +1 -1
- package/dist/panel/cache.d.ts.map +1 -1
- package/dist/panel/index.js +10 -29
- package/dist/panel/index.js.map +1 -1
- package/dist/panel/panel.d.ts.map +1 -1
- package/dist/panel/topic.d.ts.map +1 -1
- package/dist/parse-compress-input.d.ts.map +1 -1
- package/dist/persist/index.d.ts +1 -1
- package/dist/persist/index.d.ts.map +1 -1
- package/dist/persist/index.js +12 -49
- package/dist/persist/index.js.map +1 -1
- package/dist/persist/state-merge.d.ts.map +1 -1
- package/dist/persist/store.d.ts.map +1 -1
- package/dist/protected.d.ts.map +1 -1
- package/dist/rebuild.d.ts.map +1 -1
- package/dist/recommend.d.ts.map +1 -1
- package/dist/render-refs.d.ts.map +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/search/algorithms/bm25.d.ts.map +1 -1
- package/dist/search/algorithms/fuzzy.d.ts.map +1 -1
- package/dist/search/algorithms/hybrid.d.ts.map +1 -1
- package/dist/search/algorithms/semantic.d.ts.map +1 -1
- package/dist/search/algorithms/substring.d.ts.map +1 -1
- package/dist/search/doc-cache.d.ts.map +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/registry.d.ts.map +1 -1
- package/dist/search/stemmer.d.ts.map +1 -1
- package/dist/search/tokenizer.d.ts.map +1 -1
- package/dist/search/types.d.ts.map +1 -1
- package/dist/search.d.ts +3 -3
- package/dist/search.d.ts.map +1 -1
- package/dist/surface-config.d.ts.map +1 -1
- package/dist/tool-pairs.d.ts.map +1 -1
- package/dist/truncate-tools.d.ts.map +1 -1
- package/dist/truncate.d.ts.map +1 -1
- package/dist/turn-integrity.d.ts.map +1 -1
- package/dist/wire/anthropic.d.ts.map +1 -1
- package/dist/wire/bili-message.d.ts.map +1 -1
- package/dist/wire/compress-detect.d.ts.map +1 -1
- package/dist/wire/demoted-thinking.d.ts.map +1 -1
- package/dist/wire/formats.d.ts.map +1 -1
- package/dist/wire/index.js +97 -403
- package/dist/wire/index.js.map +1 -1
- package/dist/wire/message-id.d.ts.map +1 -1
- package/dist/wire/mirror.d.ts.map +1 -1
- package/dist/wire/openai.d.ts.map +1 -1
- package/dist/wire/responses.d.ts.map +1 -1
- package/dist/wire/strip-images.d.ts.map +1 -1
- package/dist/wire/util.d.ts.map +1 -1
- package/package.json +66 -66
- package/dist/chunk-WR7OPWNT.js.map +0 -1
package/DESIGN.md
CHANGED
|
@@ -9,7 +9,7 @@ Framework-agnostic, model-driven context-compression engine. This document is th
|
|
|
9
9
|
ACP-style compression is **not** like zip in one essential way: **the model writes the summaries; this library orchestrates everything around them.**
|
|
10
10
|
|
|
11
11
|
- **zip**: computes the compressed output itself (`bytes → bytes`).
|
|
12
|
-
- **acp-kernel**: the
|
|
12
|
+
- **acp-kernel**: the *summary text* is produced externally (by an LLM) and passed in as an argument. The core decides *when* to compress, *what range* to compress, tracks *state*, applies a compress *decision*, prunes ranges, and supports decompress/search. **It never calls a model.**
|
|
13
13
|
|
|
14
14
|
This is precisely why the core can be a pure library: the one external dependency (the summarizer) is reduced to a string input.
|
|
15
15
|
|
|
@@ -48,60 +48,60 @@ Portable, host-agnostic. Adapters translate host-native messages into `CoreMessa
|
|
|
48
48
|
|
|
49
49
|
```ts
|
|
50
50
|
type CoreMessage = {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
id: string;
|
|
52
|
+
role: "user" | "assistant" | "system" | "tool";
|
|
53
|
+
contentType: "text" | "tool-call" | "tool-result" | "reasoning";
|
|
54
|
+
text?: string;
|
|
55
|
+
toolName?: string; // for protected-tool filtering
|
|
56
|
+
toolCallId?: string; // links tool-call ↔ tool-result
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
type CompressionBlock = {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
blockId: string; // "b0", "b1", ...
|
|
61
|
+
runId: string;
|
|
62
|
+
tier: 1 | 2 | 3;
|
|
63
|
+
topic?: string;
|
|
64
|
+
summary: string; // produced by the model
|
|
65
|
+
directMessageIds: string[];
|
|
66
|
+
effectiveMessageIds: string[];
|
|
67
|
+
directBlockIds: string[]; // nested blocks consumed
|
|
68
|
+
createdAt: number;
|
|
69
|
+
survivedCount: number;
|
|
70
|
+
generation: "young" | "old";
|
|
71
|
+
active: boolean;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
type CompressionState = {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
blocks: CompressionBlock[];
|
|
76
|
+
messageRefs: { byRaw: Record<string, string>; byRef: Record<string, string> }; // raw ↔ mNNNNN
|
|
77
|
+
nudge: {
|
|
78
|
+
lastPerMessageNudgeTokens: number;
|
|
79
|
+
lastNudgeShownTokens: number;
|
|
80
|
+
baselineTokens: number;
|
|
81
|
+
anchors: Record<string, unknown>;
|
|
82
|
+
};
|
|
83
|
+
stats: { tokensCompressed: number; compressionCount: number };
|
|
84
|
+
nextBlockId: number;
|
|
85
|
+
nextRunId: number;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
type Config = {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
89
|
+
tiers: { enabled: boolean; tier2Trigger: number; tier3Trigger: number };
|
|
90
|
+
nudge: {
|
|
91
|
+
maxContextLimitPct: number; // e.g. 0.55 (currently advisory; threshold gate uses minContextLimitPct)
|
|
92
|
+
minContextLimitPct: number; // e.g. 0.45 — nudge threshold gate
|
|
93
|
+
frequency: number; // advisory (reserved for future turn-frequency gating)
|
|
94
|
+
iterationThreshold: number; // advisory (reserved for future iteration gating)
|
|
95
|
+
force: "soft" | "strong";
|
|
96
|
+
};
|
|
97
|
+
// young→old promotion after N survivals (drives the merge-blocks node).
|
|
98
|
+
// NOTE: there is no GC — no age-based deactivation, no summary truncation.
|
|
99
|
+
promotionThreshold: number;
|
|
100
|
+
truncate: { threshold: number }; // emergency tool-output truncation node (LAST safety valve); 1.0 = 100%
|
|
101
|
+
merge: { maxSummaryLength: number; minOldGenBlocks: number }; // batch-merge old-gen blocks into one summary
|
|
102
|
+
protectedTools: string[];
|
|
103
|
+
preserveRecentMessages: number;
|
|
104
|
+
modelContextLimit: number;
|
|
105
105
|
};
|
|
106
106
|
```
|
|
107
107
|
|
|
@@ -111,99 +111,78 @@ type Config = {
|
|
|
111
111
|
|
|
112
112
|
```ts
|
|
113
113
|
interface CompressionCore {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// When the model calls acp_retrieve. Pure lookup over the host-provided store
|
|
137
|
-
// plus an ephemeral injection message (id prefix acp_retrieved_*) that consumes
|
|
138
|
-
// no ref and never enters fold space. Hallucinated refs → not-found ack.
|
|
139
|
-
retrieve(store: MessageContentStore, ref: string): ApplyRetrieveResult;
|
|
140
|
-
|
|
141
|
-
// When the model calls compress. `ranges[].summary` is model-produced text.
|
|
142
|
-
// Allocates block(s), deactivates consumed blocks, updates indices, resets the
|
|
143
|
-
// nudge growth baseline on success (§5.7 feedback-loop fix).
|
|
144
|
-
applyCompression(input: {
|
|
145
|
-
ranges: {
|
|
146
|
-
startRef: string;
|
|
147
|
-
endRef: string;
|
|
148
|
-
summary: string;
|
|
149
|
-
topic?: string;
|
|
150
|
-
}[];
|
|
151
|
-
messages: CoreMessage[];
|
|
152
|
-
state: CompressionState;
|
|
153
|
-
config: Config;
|
|
154
|
-
}): {
|
|
155
|
-
state: CompressionState;
|
|
156
|
-
result: {
|
|
157
|
-
blocksCreated: number;
|
|
158
|
-
tokensCompressed: number;
|
|
159
|
-
errors: string[];
|
|
114
|
+
// Per-turn node pipeline (replaces the message-transform hook's algorithm part).
|
|
115
|
+
// Runs every turn (canonical order): assign-refs → sync-blocks → merge-blocks →
|
|
116
|
+
// prune → ccr-store → filter → hide-compress-calls → nudge-inject →
|
|
117
|
+
// emergency-truncate → render-refs. Survives/promotes blocks via advanceSurvival
|
|
118
|
+
// (no age-based deactivation). Returns transformed messages + updated state +
|
|
119
|
+
// nudge decision + the (possibly grown) content store.
|
|
120
|
+
processTurn(input: {
|
|
121
|
+
messages: CoreMessage[];
|
|
122
|
+
state: CompressionState;
|
|
123
|
+
config: Config;
|
|
124
|
+
tokenCount: number;
|
|
125
|
+
/** Per-session CCR content store from the previous turn (optional; an
|
|
126
|
+
* empty store is created when omitted). */
|
|
127
|
+
contentStore?: MessageContentStore;
|
|
128
|
+
}): {
|
|
129
|
+
messages: CoreMessage[];
|
|
130
|
+
state: CompressionState;
|
|
131
|
+
nudge?: NudgeDecision;
|
|
132
|
+
/** Always present; pass back as input.contentStore next turn. */
|
|
133
|
+
contentStore: MessageContentStore;
|
|
160
134
|
};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
135
|
+
|
|
136
|
+
// When the model calls acp_retrieve. Pure lookup over the host-provided store
|
|
137
|
+
// plus an ephemeral injection message (id prefix acp_retrieved_*) that consumes
|
|
138
|
+
// no ref and never enters fold space. Hallucinated refs → not-found ack.
|
|
139
|
+
retrieve(store: MessageContentStore, ref: string): ApplyRetrieveResult;
|
|
140
|
+
|
|
141
|
+
// When the model calls compress. `ranges[].summary` is model-produced text.
|
|
142
|
+
// Allocates block(s), deactivates consumed blocks, updates indices, resets the
|
|
143
|
+
// nudge growth baseline on success (§5.7 feedback-loop fix).
|
|
144
|
+
applyCompression(input: {
|
|
145
|
+
ranges: { startRef: string; endRef: string; summary: string; topic?: string }[];
|
|
146
|
+
messages: CoreMessage[];
|
|
147
|
+
state: CompressionState;
|
|
148
|
+
config: Config;
|
|
149
|
+
}): {
|
|
150
|
+
state: CompressionState;
|
|
151
|
+
result: { blocksCreated: number; tokensCompressed: number; errors: string[] };
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
resolveBoundaries(input: {
|
|
155
|
+
startRef: string;
|
|
156
|
+
endRef: string;
|
|
157
|
+
messages: CoreMessage[];
|
|
158
|
+
state: CompressionState;
|
|
159
|
+
}): { startIndex: number; endIndex: number; protectedGaps: number[] };
|
|
160
|
+
// protectedGaps is reserved (currently always []); protected-tool hard-exclusion
|
|
161
|
+
// is intentionally NOT implemented in the core — see README "Known limitation".
|
|
162
|
+
|
|
163
|
+
decompress(blockId: string, state: CompressionState): CompressionBlock | undefined;
|
|
164
|
+
|
|
165
|
+
search(query: string, state: CompressionState): CompressionBlock[];
|
|
166
|
+
|
|
167
|
+
status(state: CompressionState, tokenCount: number, config: Config): StatusReport;
|
|
168
|
+
|
|
169
|
+
// No gc(): age-based deactivation was removed (it caused memory-loss upstream).
|
|
170
|
+
// Block promotion (young→old) still happens via advanceSurvival in sync-blocks,
|
|
171
|
+
// and old-gen blocks are batch-merged by the merge-blocks node — not dropped.
|
|
188
172
|
}
|
|
189
173
|
|
|
190
174
|
type CompressCall = {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
startRef: string;
|
|
194
|
-
endRef: string;
|
|
195
|
-
summary: string;
|
|
196
|
-
topic?: string;
|
|
197
|
-
}[];
|
|
175
|
+
mode: "range" | "message";
|
|
176
|
+
ranges: { startRef: string; endRef: string; summary: string; topic?: string }[];
|
|
198
177
|
};
|
|
199
178
|
|
|
200
179
|
type NudgeDecision = {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
180
|
+
shouldInject: boolean;
|
|
181
|
+
reason: string;
|
|
182
|
+
compressibleRanges: { startRef: string; endRef: string; tokens: number }[];
|
|
183
|
+
contextUsage: number; // 0..1
|
|
184
|
+
tier: 1 | 2 | 3 | null; // multi-tier trigger, if any
|
|
185
|
+
breakdown: Record<string, number>;
|
|
207
186
|
};
|
|
208
187
|
```
|
|
209
188
|
|
|
@@ -215,9 +194,9 @@ The core needs two capabilities from the host, both injected (the core never imp
|
|
|
215
194
|
|
|
216
195
|
```ts
|
|
217
196
|
interface Ports {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
197
|
+
countTokens(text: string): number; // default impl ships in core
|
|
198
|
+
// messages are PASSED IN to each call (never fetched) → no MessageStore port
|
|
199
|
+
// state persistence is the host's job (state is plain data) → no storage port
|
|
221
200
|
}
|
|
222
201
|
```
|
|
223
202
|
|
|
@@ -227,32 +206,32 @@ A default `countTokens` ships with the core (word-level + unicode CJK tokenizer,
|
|
|
227
206
|
|
|
228
207
|
## 6. Concept Mapping (nothing is lost)
|
|
229
208
|
|
|
230
|
-
| ACP concept
|
|
231
|
-
|
|
232
|
-
| message-id ↔ ref mapping
|
|
233
|
-
| prune (range → summary block)
|
|
234
|
-
| boundary resolution / search
|
|
235
|
-
| block allocation / state mutation / tiers
|
|
236
|
-
| compress **argument parsing** (lenient: fences, trailing commas, truncated-array salvage; field-name variants) | **core** `parseCompressArgs` (pure; diagnostics are data — adapters emit them)
|
|
237
|
-
| young→old promotion / batch merge
|
|
238
|
-
| emergency truncation (context near full)
|
|
239
|
-
| protected-tools filtering logic
|
|
240
|
-
| `inject` **decision** (shouldNudge / growth / threshold)
|
|
241
|
-
| `inject` **text rendering** (nudge → message string)
|
|
242
|
-
| prompts (system / nudge text templates)
|
|
243
|
-
| compress/decompress/search/status **tool registration**
|
|
244
|
-
| `/acp` commands
|
|
245
|
-
| opencode hooks
|
|
246
|
-
| output-steering **decisions** (turn classification / verbosity level / effort clamp, #355)
|
|
247
|
-
| output-steering **landing** (wire field write-back, system-prompt carrier selection, numeric budget floors)
|
|
248
|
-
| config three-layer merge
|
|
249
|
-
| logger / auth / persistence / update
|
|
209
|
+
| ACP concept | Destination in acp-kernel |
|
|
210
|
+
|---|---|
|
|
211
|
+
| message-id ↔ ref mapping | **core** `processTurn` (pure) |
|
|
212
|
+
| prune (range → summary block) | **core** `processTurn` (pure) |
|
|
213
|
+
| boundary resolution / search | **core** `resolveBoundaries` (pure) |
|
|
214
|
+
| block allocation / state mutation / tiers | **core** `applyCompression` (pure) |
|
|
215
|
+
| compress **argument parsing** (lenient: fences, trailing commas, truncated-array salvage; field-name variants) | **core** `parseCompressArgs` (pure; diagnostics are data — adapters emit them) |
|
|
216
|
+
| young→old promotion / batch merge | **core** `sync-blocks` node (`advanceSurvival`) + `merge-blocks` node (pure) |
|
|
217
|
+
| emergency truncation (context near full) | **core** `emergency-truncate` node — the LAST safety valve; no age-based GC |
|
|
218
|
+
| protected-tools filtering logic | **core** (pure: message + config → bool) |
|
|
219
|
+
| `inject` **decision** (shouldNudge / growth / threshold) | **core** `decideNudge` (pure) |
|
|
220
|
+
| `inject` **text rendering** (nudge → message string) | **adapter** (host message format) |
|
|
221
|
+
| prompts (system / nudge text templates) | rules as **structured data** in core; text rendering in **adapter** |
|
|
222
|
+
| compress/decompress/search/status **tool registration** | **adapter** (calls core pure fns) |
|
|
223
|
+
| `/acp` commands | **adapter** |
|
|
224
|
+
| opencode hooks | **OpenCode adapter** |
|
|
225
|
+
| output-steering **decisions** (turn classification / verbosity level / effort clamp, #355) | **core** `decideOutputSteering` (pure; operates on a structural summary — role + block kinds, no content) |
|
|
226
|
+
| output-steering **landing** (wire field write-back, system-prompt carrier selection, numeric budget floors) | **adapter** |
|
|
227
|
+
| config three-layer merge | **adapter** (core only consumes its own `Config`) |
|
|
228
|
+
| logger / auth / persistence / update | **adapter** (core does zero I/O) |
|
|
250
229
|
|
|
251
230
|
---
|
|
252
231
|
|
|
253
232
|
## 7. Why the algorithm, but not the DCP-derived code, comes here
|
|
254
233
|
|
|
255
|
-
Copyright protects
|
|
234
|
+
Copyright protects *expression*, not ideas, methods, or algorithms (17 USC §102(b)). The compression *methods* (3-tier, growth cadence, protected filtering) are the author's. This core reimplements them in **fresh expression** — it is not a copy or refactor of DCP-derived files. See [PROVENANCE.md](./PROVENANCE.md) for the per-module origin classification (original-bring / DCP-derived-reimplement / adapter-only).
|
|
256
235
|
|
|
257
236
|
---
|
|
258
237
|
|
|
@@ -262,10 +241,10 @@ Copyright protects _expression_, not ideas, methods, or algorithms (17 USC §102
|
|
|
262
241
|
|
|
263
242
|
### 8.1 First-user-message pin
|
|
264
243
|
|
|
265
|
-
The session's **first user message survives prune unconditionally** — even when it is covered by an active block. In `rebuildMessages` (`src/prune.ts`) the pin check runs
|
|
244
|
+
The session's **first user message survives prune unconditionally** — even when it is covered by an active block. In `rebuildMessages` (`src/prune.ts`) the pin check runs *before* the covered-by-active-block check, so after compressing a range that includes the first user message, the rebuilt wire contains **both** the rendered summary **and** the first user message verbatim; all other covered messages drop as usual. Only the *first* user message is pinned — later covered user messages are pruned normally.
|
|
266
245
|
|
|
267
246
|
- **Why:** strict providers reject conversations with no user message (e.g., Anthropic requires the conversation to start from the user role). Pinning guarantees the rebuilt wire retains at least one user message whenever the input had one. Deliberate since prune's first implementation (v0.0.2) — the ordering of the two checks is part of the contract, not incidental. Regression-tested by `tests/state-prune.test.ts` ("prune preserves first user message even when covered") and `tests/orphan-fixes.test.ts` ("prune: first user message pruned when covered (no duplication)").
|
|
268
247
|
- **Consequences for consumers:**
|
|
269
248
|
- Consumers cannot assert byte-level disappearance of the first user message after compression; post-compress wire assertions must expect the rendered summary **plus** the pinned message verbatim.
|
|
270
249
|
- Token-size and prefix-cache stability estimates must account for the pinned message staying verbatim (it is often the task description and can be long).
|
|
271
|
-
- Boundary resolution compensates: `blockVisibleInRange` (`src/boundaries.ts`) treats a block as present in a range via its rendered summary **or** its earliest surviving raw — required precisely because the summary anchors
|
|
250
|
+
- Boundary resolution compensates: `blockVisibleInRange` (`src/boundaries.ts`) treats a block as present in a range via its rendered summary **or** its earliest surviving raw — required precisely because the summary anchors *before* the pinned raw.
|
package/PROVENANCE.md
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
This document classifies **every** source file in `opencode-acp/lib/` (the donor) into one of three buckets, to determine what can be carried into the MIT `acp-kernel` and what must be reimplemented in fresh expression.
|
|
4
4
|
|
|
5
|
-
**Method**: exact path comparison of `opencode-acp` (the ACP fork, 82 `lib/*.ts` files) against the upstream DCP repository (66 `lib/*.ts` files). A file that has **no DCP equivalent** is original work (bucket A). A file with a **DCP equivalent at the same path** is a DCP derivative — its
|
|
5
|
+
**Method**: exact path comparison of `opencode-acp` (the ACP fork, 82 `lib/*.ts` files) against the upstream DCP repository (66 `lib/*.ts` files). A file that has **no DCP equivalent** is original work (bucket A). A file with a **DCP equivalent at the same path** is a DCP derivative — its *expression* is AGPL-bound regardless of how much it was later changed (derivation is judged by whether a file was created by transforming the original, not by % changed; per copyright law expression is protected, not ideas/methods/algorithms).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## Legend
|
|
10
10
|
|
|
11
|
-
| Bucket
|
|
12
|
-
|
|
13
|
-
| **A**
|
|
14
|
-
| **B**
|
|
15
|
-
| **N/A** | Adapter-only (framework-specific: opencode hooks, I/O, auth, commands, UI)
|
|
11
|
+
| Bucket | Meaning | Action in acp-kernel |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **A** | No DCP equivalent → original work of ranxianglei | **Bring verbatim** (the author's own code, MIT-safe). License header rewritten to MIT. |
|
|
14
|
+
| **B** | DCP-derived file (same path exists upstream) → derivative expression is AGPL | **Reimplement in fresh expression** using the author's algorithm; do NOT copy/refactor the file's code. |
|
|
15
|
+
| **N/A** | Adapter-only (framework-specific: opencode hooks, I/O, auth, commands, UI) | **Excluded** from the pure core. Stays in host adapters. Origin irrelevant. |
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -21,28 +21,26 @@ This document classifies **every** source file in `opencode-acp/lib/` (the donor
|
|
|
21
21
|
Quality gate intentionally omitted from acp-kernel (may be added later if needed).
|
|
22
22
|
|
|
23
23
|
### `compress/`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
|
27
|
-
| `compress/decompress.ts`
|
|
28
|
-
| `compress/
|
|
29
|
-
| `compress/hide-
|
|
30
|
-
| `compress/
|
|
31
|
-
| `compress/
|
|
32
|
-
| `compress/
|
|
33
|
-
| `compress/
|
|
34
|
-
| `compress/status.ts` | ACP-original (v1.11+) |
|
|
24
|
+
| File | Notes |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `compress/decompress.ts` | ACP-original (v1.11+); DCP has no decompress |
|
|
27
|
+
| `compress/decompress-logic.ts` | ACP-original |
|
|
28
|
+
| `compress/hide-consumed.ts` | ACP-original (v1.14+) |
|
|
29
|
+
| `compress/hide-failed.ts` | ACP-original |
|
|
30
|
+
| `compress/keep-markers.ts` | ACP-original (v1.12+) |
|
|
31
|
+
| `compress/parts.ts` | ACP-original |
|
|
32
|
+
| `compress/recap.ts` | ACP-original (v1.12.1) |
|
|
33
|
+
| `compress/status.ts` | ACP-original (v1.11+) |
|
|
35
34
|
|
|
36
35
|
### other
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `messages/
|
|
43
|
-
| `messages/filter/*` (9 files) | ACP-original filtering subsystem |
|
|
36
|
+
| File | Notes |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `config-validation.ts` | ACP-extracted for testability; no DCP equivalent |
|
|
39
|
+
| `gc/merge.ts` | ACP-original (DCP has no `gc/` dir) |
|
|
40
|
+
| `messages/truncate-tools.ts` | ACP-original (v1.14.5, replaced DCP's `gc/truncate.ts`) |
|
|
41
|
+
| `messages/filter/*` (9 files) | ACP-original filtering subsystem |
|
|
44
42
|
| `messages/inject/policy/*` (3 files) | ACP-original (v1.13.1) — inject **policy** logic, not rendering |
|
|
45
|
-
| `state/rebuild.ts`
|
|
43
|
+
| `state/rebuild.ts` | ACP-original (v1.11+) |
|
|
46
44
|
|
|
47
45
|
---
|
|
48
46
|
|
|
@@ -51,25 +49,22 @@ Quality gate intentionally omitted from acp-kernel (may be added later if needed
|
|
|
51
49
|
Only the **algorithmic** subset is reimplemented into the pure core; the rest are adapter-only (persistence, config-merge, prompts-rendering) and excluded from the core. Listed by what they become.
|
|
52
50
|
|
|
53
51
|
### Reimplemented into acp-kernel (fresh expression, ~9 substantial files)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
| `compress/
|
|
58
|
-
| `compress/
|
|
59
|
-
| `compress/
|
|
60
|
-
| `
|
|
61
|
-
| `messages/
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `config.ts` (core subset) | `core/Config` defaults | defaults + validation (use A-class `config-validation.ts`) |
|
|
52
|
+
| DCP-derived file | Becomes | What to reimplement |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `compress/range.ts` | `core/applyCompression` (range mode) | block allocation, nested-block handling, boundary resolution |
|
|
55
|
+
| `compress/search.ts` | `core/resolveBoundaries` | ref→index mapping, reversed-boundary swap, protected-gap detection |
|
|
56
|
+
| `compress/state.ts` | `core/state` (mutation) | block id/run allocation, deactivation, byMessageId index |
|
|
57
|
+
| `compress/pipeline.ts` | `core/processTurn` prep/finalize | permission (host-side), fetch (host-side), state wrap |
|
|
58
|
+
| `messages/prune.ts` | `core/prune` | replace compressed ranges with summary blocks |
|
|
59
|
+
| `messages/sync.ts` | `core/sync` | deactivate orphaned blocks when messages deleted |
|
|
60
|
+
| `message-ids.ts` | `core/refs` | raw↔mNNNNN bidirectional map |
|
|
61
|
+
| `messages/inject/inject.ts` + `inject/utils.ts` | `core/decideNudge` | **decision only** — shouldNudge, growth baseline, threshold, compressible ranges |
|
|
62
|
+
| `config.ts` (core subset) | `core/Config` defaults | defaults + validation (use A-class `config-validation.ts`) |
|
|
66
63
|
|
|
67
64
|
### Supporting types/barrels (trivial, write fresh)
|
|
68
|
-
|
|
69
65
|
`compress/{index,types,timing,range-utils}.ts`, `messages/{index,priority,query,reasoning-strip,shape,utils}.ts`, `state/{index,types,utils}.ts`, `token-utils.ts` (wrap `cc-alg` tokenizer), `protected-patterns.ts`, `compress/protected-content.ts`.
|
|
70
66
|
|
|
71
67
|
### Excluded from core (adapter-only despite B lineage)
|
|
72
|
-
|
|
73
68
|
`state/persistence.ts` (filesystem I/O), `prompts/*` (text rendering → adapter), `compress-permission.ts` (permission = host concern).
|
|
74
69
|
|
|
75
70
|
---
|
|
@@ -84,13 +79,14 @@ All framework-specific; irrelevant to the pure core. Stay in the OpenCode adapte
|
|
|
84
79
|
|
|
85
80
|
## Summary
|
|
86
81
|
|
|
87
|
-
| Bucket
|
|
88
|
-
|
|
89
|
-
| **B** DCP-derived | 40
|
|
90
|
-
| **N/A** adapter
|
|
82
|
+
| Bucket | Files | Disposition |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| **B** DCP-derived | 40 | ~9 substantial algorithms reimplemented fresh + ~12 types/barrels rewritten fresh; rest excluded (adapter) |
|
|
85
|
+
| **N/A** adapter | 12 | excluded from core |
|
|
86
|
+
|
|
91
87
|
|
|
92
88
|
---
|
|
93
89
|
|
|
94
90
|
## Compliance note
|
|
95
91
|
|
|
96
|
-
This audit exists to ensure acp-kernel is **genuinely MIT**, not "MIT-labeled but AGPL-tainted." The rule applied throughout: an idea/algorithm is free regardless of source; a
|
|
92
|
+
This audit exists to ensure acp-kernel is **genuinely MIT**, not "MIT-labeled but AGPL-tainted." The rule applied throughout: an idea/algorithm is free regardless of source; a *file's code expression* is bound to the license of the file it descends from. A-class files descend from no DCP file. B-class files' code is NOT carried — the algorithms are reimplemented in new expression. Should any contributor question a classification, the comparison data above (and the upstream DCP tree) allow independent verification.
|