fix: address codex review round 1
- spill-policy validates maxInlineBytes as a non-negative integer at LOAD, so a bad config fails the deployment instead of letting a negative value reach TextRetainer and turn every oversized-result call into an isError. - Document the spill seam vocabulary in docs/core-data-structures/spill.md (SaveTextSpill/SpillOwner/SpillSource/SpillRef/SpillPath, verbatim + type-equiv gated) and index it from core.md, matching the other capability seams.
This commit is contained in:
@@ -8,7 +8,7 @@ This plugin registers **no service** and owns no storage or preview mechanics: p
|
||||
|
||||
| Key | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `maxInlineBytes` | *(omitted)* | Model-facing context cap for a plain-text result, in UTF-8 bytes. **Omitted disables the policy entirely** (the plugin registers nothing). When set, a larger result is spilled and replaced with a preview derived from the same budget (head/tail split). |
|
||||
| `maxInlineBytes` | *(omitted)* | Model-facing context cap for a plain-text result, in UTF-8 bytes (a non-negative integer; validated at load). **Omitted disables the policy entirely** (the plugin registers nothing). When set, a larger result is spilled and replaced with a preview derived from the same budget (head/tail split). |
|
||||
|
||||
## Behavior
|
||||
|
||||
|
||||
@@ -101,6 +101,12 @@ export function apply(ctx: Context, config: Config): void {
|
||||
const maxInlineBytes = config.maxInlineBytes
|
||||
// Omitted ⇒ no automatic spill policy: register nothing at all.
|
||||
if (maxInlineBytes === undefined) return
|
||||
// Validate at LOAD, not per call: a negative/fractional cap would reach
|
||||
// TextRetainer's assertBudget and throw, turning every oversized-result call
|
||||
// into an isError. A bad config must fail the deployment, not the tool.
|
||||
if (!Number.isInteger(maxInlineBytes) || maxInlineBytes < 0) {
|
||||
throw new Error(`spill-policy: maxInlineBytes must be a non-negative integer (got ${maxInlineBytes})`)
|
||||
}
|
||||
|
||||
ctx.on('tools/post-execute', async (exec, result, next): Promise<PostToolDecision> => {
|
||||
// Delegate first so a downstream listener (e.g. a hook) settles the result;
|
||||
|
||||
@@ -82,6 +82,16 @@ describe('disabled mode', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('config validation', () => {
|
||||
it('rejects a negative maxInlineBytes at load', async () => {
|
||||
await expect(setup({ maxInlineBytes: -1 })).rejects.toThrow(/non-negative integer/)
|
||||
})
|
||||
|
||||
it('rejects a fractional maxInlineBytes at load', async () => {
|
||||
await expect(setup({ maxInlineBytes: 1.5 })).rejects.toThrow(/non-negative integer/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('oversized plain-text replacement', () => {
|
||||
it('spills the full text and replaces the result with a preview + path', async () => {
|
||||
const { ctx, spill } = await setup({ maxInlineBytes: 20 })
|
||||
|
||||
Reference in New Issue
Block a user