dsh-better-tips 0.12.1 → 0.13.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 +23 -0
- package/README.md +54 -122
- package/client.js +268 -124
- package/index.js +202 -12
- package/package.json +1 -1
- package/test/client.test.mjs +677 -188
- package/test/host.test.mjs +417 -3
package/test/host.test.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import assert from 'node:assert/strict'
|
|
|
3
3
|
import fs, { readFile } from 'node:fs/promises'
|
|
4
4
|
import { syncBuiltinESMExports } from 'node:module'
|
|
5
5
|
import { join } from 'node:path'
|
|
6
|
+
import { tmpdir } from 'node:os'
|
|
7
|
+
import { pathToFileURL } from 'node:url'
|
|
6
8
|
import { apply } from '../index.js'
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -70,7 +72,7 @@ test('the detail route mounts when connection arrives after plugin activation an
|
|
|
70
72
|
const { routes, connect, disposers } = harness({ extraRetries: 0 }, { connectionReady: false })
|
|
71
73
|
assert.equal(routes.length, 0)
|
|
72
74
|
connect()
|
|
73
|
-
assert.equal(routes.length,
|
|
75
|
+
assert.equal(routes.length, 4, 'late service activation must install all four routes')
|
|
74
76
|
const detail = routes.find((entry) => entry.path === '/api/better-tips/details')
|
|
75
77
|
const result = await detail.fetch(new Request('http://localhost/api/better-tips/details?sessionId=session-1&turn=1'))
|
|
76
78
|
assert.equal(result.status, 200)
|
|
@@ -78,6 +80,221 @@ test('the detail route mounts when connection arrives after plugin activation an
|
|
|
78
80
|
assert.equal(routes.length, 0, 'the routes belong to the dependency lifetime')
|
|
79
81
|
})
|
|
80
82
|
|
|
83
|
+
async function reportEnvironment (h, signal) {
|
|
84
|
+
const route = h.routes.find((entry) => entry.path === '/api/better-tips/environment')
|
|
85
|
+
return route.fetch(new Request('http://localhost/api/better-tips/environment', { signal }))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function withInventoryManifests (run) {
|
|
89
|
+
const root = await fs.mkdtemp(join(tmpdir(), 'better-tips-inventory-'))
|
|
90
|
+
const manifests = new Map()
|
|
91
|
+
const reads = new Map()
|
|
92
|
+
const originalRead = fs.readFile
|
|
93
|
+
try {
|
|
94
|
+
const put = async (name, version, suffix = '') => {
|
|
95
|
+
const directory = join(root, name.replace(/[^A-Za-z0-9]/g, '_') + suffix)
|
|
96
|
+
await fs.mkdir(directory, { recursive: true })
|
|
97
|
+
const manifestPath = join(directory, 'package.json')
|
|
98
|
+
await fs.writeFile(manifestPath, JSON.stringify({ name, ...(version === undefined ? {} : { version }), privateConfig: 'DO_NOT_EXPORT_CONFIG' }))
|
|
99
|
+
manifests.set(name + suffix, manifestPath)
|
|
100
|
+
return manifestPath
|
|
101
|
+
}
|
|
102
|
+
fs.readFile = async (path, ...args) => { const key = String(path); reads.set(key, (reads.get(key) ?? 0) + 1); return originalRead(path, ...args) }
|
|
103
|
+
syncBuiltinESMExports()
|
|
104
|
+
await run({ put, manifests, reads, root })
|
|
105
|
+
} finally {
|
|
106
|
+
fs.readFile = originalRead; syncBuiltinESMExports()
|
|
107
|
+
// root is the exact directory returned by mkdtemp above, owned by this fixture.
|
|
108
|
+
await fs.rm(root, { recursive: true, force: true })
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
test('report environment collects enabled host entries and bundles using exact Loader anchors', async () => {
|
|
113
|
+
await withInventoryManifests(async ({ put, reads }) => {
|
|
114
|
+
const plugin = await put('@fixture/plugin', '1.2.3')
|
|
115
|
+
const otherVersion = await put('@fixture/plugin', '2.0.0', '-other')
|
|
116
|
+
const core = await put('@deepseek-ai/core', '0.2.2-rc.2')
|
|
117
|
+
const baseA = pathToFileURL(join(tmpdir(), 'tree-a.yml')).href
|
|
118
|
+
const baseB = pathToFileURL(join(tmpdir(), 'tree-b.yml')).href
|
|
119
|
+
const entries = [
|
|
120
|
+
{ options: { name: '@fixture/plugin/client' }, disabled: false, parent: { tree: { ctx: { baseUrl: baseA } } }, fiber: { state: 'failed' } },
|
|
121
|
+
{ options: { name: '@fixture/plugin/tools' }, disabled: false, parent: { tree: { ctx: { baseUrl: baseA } } } },
|
|
122
|
+
{ options: { name: '@fixture/plugin' }, disabled: false, parent: { tree: { ctx: { baseUrl: baseB } } } },
|
|
123
|
+
{ options: { name: '@deepseek-ai/core' }, disabled: false, parent: { tree: { ctx: { baseUrl: baseA } } } },
|
|
124
|
+
{ options: { name: 'official-addon/runtime' }, disabled: false, parent: { tree: { ctx: { baseUrl: baseA } } } },
|
|
125
|
+
{ options: { name: 'disabled-secret' }, disabled: true },
|
|
126
|
+
{ options: { name: 'group-secret', group: true }, disabled: false },
|
|
127
|
+
]
|
|
128
|
+
const resolutions = []
|
|
129
|
+
const h = harness({}, { services: {
|
|
130
|
+
loader: { entries: () => entries },
|
|
131
|
+
pluginPackages: { packageOf (name, anchor) { resolutions.push([name, anchor]); return { manifestPath: name === '@deepseek-ai/core' ? core : anchor === baseB ? otherVersion : plugin } } },
|
|
132
|
+
pluginManager: { listBundles: async () => [
|
|
133
|
+
{ name: '@deepseek-ai/dsh-base', version: '0.2.2-rc.2', enabled: true, availability: 'installation', official: false, rows: [{ moduleName: '@deepseek-ai/core' }] },
|
|
134
|
+
{ name: 'official-addon', version: '1.0.0', enabled: true, availability: 'profile', official: true, rows: [{ moduleName: 'official-addon/runtime' }] },
|
|
135
|
+
{ name: '@deepseek-ai/third-party-fixture', version: '7.0.0', enabled: true, availability: 'profile', official: false },
|
|
136
|
+
{ name: '@fixture/plugin', version: '1.2.3', enabled: true, source: 'DO_NOT_EXPORT_SOURCE' },
|
|
137
|
+
{ name: '@fixture/config-preset', version: '3.4.5', enabled: true, rows: [] },
|
|
138
|
+
{ name: 'disabled-bundle', version: '9', enabled: false },
|
|
139
|
+
] },
|
|
140
|
+
} })
|
|
141
|
+
try {
|
|
142
|
+
const response = await reportEnvironment(h)
|
|
143
|
+
assert.equal(response.status, 200)
|
|
144
|
+
const body = await response.json()
|
|
145
|
+
assert.equal(body.runtime.pluginsStatus, 'complete')
|
|
146
|
+
assert.deepEqual(body.runtime.enabledPlugins, [
|
|
147
|
+
{ name: '@deepseek-ai/third-party-fixture', version: '7.0.0' },
|
|
148
|
+
{ name: '@fixture/config-preset', version: '3.4.5' },
|
|
149
|
+
{ name: '@fixture/plugin', version: '1.2.3' },
|
|
150
|
+
{ name: '@fixture/plugin', version: '2.0.0' },
|
|
151
|
+
])
|
|
152
|
+
assert.ok(resolutions.some(([name, anchor]) => name === '@fixture/plugin' && anchor === baseB))
|
|
153
|
+
assert.equal(reads.get(plugin), 1, 'one manifest read per package path within this request')
|
|
154
|
+
assert.doesNotMatch(JSON.stringify(body), /DO_NOT_EXPORT|tree-a|tree-b|disabled-|group-secret|manifestPath|"config":|"source":/)
|
|
155
|
+
assert.ok(body.runtime.enabledPlugins.every((entry) => Object.keys(entry).sort().join(',') === 'name,version'))
|
|
156
|
+
} finally { await disposeHarness(h) }
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('report environment excludes native repository packages without relying on package-name prefix', async () => {
|
|
161
|
+
await withInventoryManifests(async ({ put }) => {
|
|
162
|
+
const native = await put('native-unscoped-plugin', '0.2.2-rc.2')
|
|
163
|
+
await fs.writeFile(native, JSON.stringify({ name: 'native-unscoped-plugin', version: '0.2.2-rc.2', repository: { url: 'git+https://github.com/deepseek-ai/deepseek-harness.git' } }))
|
|
164
|
+
const thirdParty = await put('@deepseek-ai/not-shipped-fixture', '9.0.0')
|
|
165
|
+
const h = harness({}, { services: {
|
|
166
|
+
loader: { entries: () => [native, thirdParty].map((path, index) => ({ disabled: false, options: { name: index ? '@deepseek-ai/not-shipped-fixture' : 'native-unscoped-plugin' } })) },
|
|
167
|
+
pluginPackages: { packageOf: (name) => ({ manifestPath: name === 'native-unscoped-plugin' ? native : thirdParty }) },
|
|
168
|
+
pluginManager: { listBundles: async () => [{ name: 'native-unscoped-plugin', version: '0.2.2-rc.2', enabled: true, availability: 'profile', official: false }] },
|
|
169
|
+
agentPresets: {
|
|
170
|
+
inspectCompositions: () => [],
|
|
171
|
+
compositionInventory: async () => [{ id: 'fixture', rows: [
|
|
172
|
+
{ moduleName: 'native-unscoped-plugin', enabled: true },
|
|
173
|
+
{ moduleName: 'native-unscoped-plugin', enabled: 'conditional' },
|
|
174
|
+
{ moduleName: 'cordis:group', enabled: true },
|
|
175
|
+
{ moduleName: 'not-enabled-conditional', enabled: 'conditional' },
|
|
176
|
+
] }],
|
|
177
|
+
},
|
|
178
|
+
} })
|
|
179
|
+
try {
|
|
180
|
+
const body = await (await reportEnvironment(h)).json()
|
|
181
|
+
assert.deepEqual(body.runtime.enabledPlugins, [{ name: '@deepseek-ai/not-shipped-fixture', version: '9.0.0' }])
|
|
182
|
+
assert.equal(body.runtime.pluginsStatus, 'complete')
|
|
183
|
+
} finally { await disposeHarness(h) }
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('report environment recollects every request and rereads versions rather than caching the old roster', async () => {
|
|
188
|
+
await withInventoryManifests(async ({ put }) => {
|
|
189
|
+
const path = await put('fixture-changing', '1.0.0')
|
|
190
|
+
let plugins = [{ moduleName: 'fixture-changing/client', enabled: true, fiberPhase: 'failed', config: 'DO_NOT_EXPORT' }]
|
|
191
|
+
let bundles = [{ name: 'fixture-changing', version: '1.0.0', enabled: true }]
|
|
192
|
+
let pluginCalls = 0
|
|
193
|
+
let bundleCalls = 0
|
|
194
|
+
const h = harness({}, { services: {
|
|
195
|
+
pluginPackages: { packageOf: () => ({ manifestPath: path }) },
|
|
196
|
+
pluginManager: { listPlugins: async () => { pluginCalls++; return plugins }, listBundles: async () => { bundleCalls++; return bundles } },
|
|
197
|
+
} })
|
|
198
|
+
try {
|
|
199
|
+
const first = await (await reportEnvironment(h)).json()
|
|
200
|
+
assert.deepEqual(first.runtime.enabledPlugins, [{ name: 'fixture-changing', version: '1.0.0' }])
|
|
201
|
+
await fs.writeFile(path, JSON.stringify({ name: 'fixture-changing', version: '1.1.0' }))
|
|
202
|
+
bundles = [{ name: 'fixture-changing', version: '1.1.0', enabled: true }]
|
|
203
|
+
const second = await (await reportEnvironment(h)).json()
|
|
204
|
+
assert.deepEqual(second.runtime.enabledPlugins, [{ name: 'fixture-changing', version: '1.1.0' }])
|
|
205
|
+
plugins = [{ moduleName: 'fixture-changing', enabled: false }]
|
|
206
|
+
bundles = []
|
|
207
|
+
const third = await (await reportEnvironment(h)).json()
|
|
208
|
+
assert.deepEqual(third.runtime.enabledPlugins, [])
|
|
209
|
+
assert.equal(third.runtime.pluginsStatus, 'complete')
|
|
210
|
+
assert.equal(pluginCalls, 3)
|
|
211
|
+
assert.equal(bundleCalls, 3)
|
|
212
|
+
} finally { await disposeHarness(h) }
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('report environment keeps large lists complete and marks missing or invalid versions honestly', async () => {
|
|
217
|
+
await withInventoryManifests(async ({ put }) => {
|
|
218
|
+
const missingVersion = await put('fixture-no-version', undefined)
|
|
219
|
+
const h = harness({}, { services: {
|
|
220
|
+
pluginPackages: { packageOf (name) { return name === 'fixture-no-version' ? { manifestPath: missingVersion } : undefined } },
|
|
221
|
+
pluginManager: { listPlugins: async () => [
|
|
222
|
+
{ moduleName: 'fixture-no-version/subpath', enabled: true }, { moduleName: 'fixture-unresolved', enabled: true },
|
|
223
|
+
], listBundles: async () => [
|
|
224
|
+
...Array.from({ length: 137 }, (_, index) => ({ name: 'fixture-bundle-' + index, version: '1.0.0', enabled: true })),
|
|
225
|
+
{ name: 'fixture-bad-version', version: 'D:\\PRIVATE\\token=secret', enabled: true },
|
|
226
|
+
] },
|
|
227
|
+
} })
|
|
228
|
+
try {
|
|
229
|
+
const body = await (await reportEnvironment(h)).json()
|
|
230
|
+
assert.equal(body.runtime.enabledPlugins.length, 140)
|
|
231
|
+
assert.equal(body.runtime.pluginsStatus, 'partial')
|
|
232
|
+
for (const name of ['fixture-no-version', 'fixture-unresolved', 'fixture-bad-version']) {
|
|
233
|
+
assert.deepEqual(body.runtime.enabledPlugins.find((entry) => entry.name === name), { name, version: null })
|
|
234
|
+
}
|
|
235
|
+
assert.doesNotMatch(JSON.stringify(body), /PRIVATE|token=|manifestPath/)
|
|
236
|
+
} finally { await disposeHarness(h) }
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test('report environment reports unavailable or partial service inventories without leaking errors', async () => {
|
|
241
|
+
for (const [services, expectedStatus, expectedNames] of [
|
|
242
|
+
[{}, 'unavailable', []],
|
|
243
|
+
[{ pluginManager: { listPlugins: async () => { throw Error('PRIVATE_PATH_AND_TOKEN') }, listBundles: async () => [{ name: 'fixture-config', version: '1', enabled: true }] } }, 'partial', ['fixture-config']],
|
|
244
|
+
[{ loader: { entries: () => { throw Error('PRIVATE_PATH_AND_TOKEN') } } }, 'unavailable', []],
|
|
245
|
+
]) {
|
|
246
|
+
const h = harness({}, { services })
|
|
247
|
+
try {
|
|
248
|
+
const body = await (await reportEnvironment(h)).json()
|
|
249
|
+
assert.equal(body.runtime.pluginsStatus, expectedStatus)
|
|
250
|
+
assert.deepEqual(body.runtime.enabledPlugins.map((entry) => entry.name), expectedNames)
|
|
251
|
+
assert.doesNotMatch(JSON.stringify(body), /PRIVATE_PATH_AND_TOKEN/)
|
|
252
|
+
} finally { await disposeHarness(h) }
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test('report environment includes preset composition packages without exporting preset internals', async () => {
|
|
257
|
+
await withInventoryManifests(async ({ put }) => {
|
|
258
|
+
const path = await put('fixture-preset-tool', '4.5.6')
|
|
259
|
+
const anchor = pathToFileURL(join(tmpdir(), 'private-preset.yml')).href
|
|
260
|
+
const h = harness({}, { services: {
|
|
261
|
+
loader: { entries: () => [] },
|
|
262
|
+
pluginManager: { listBundles: async () => [] },
|
|
263
|
+
pluginPackages: { packageOf (name, base) { assert.equal(name, 'fixture-preset-tool'); assert.equal(base, anchor); return { manifestPath: path } } },
|
|
264
|
+
agentPresets: {
|
|
265
|
+
inspectCompositions: () => [{ id: 'private-preset-id', modules: [{ moduleName: 'fixture-preset-tool/tools', baseUrl: anchor, useHostBase: false }], leakedServices: ['DO_NOT_EXPORT'] }],
|
|
266
|
+
compositionInventory: async () => [{ id: 'private-preset-id', rows: [
|
|
267
|
+
{ moduleName: 'fixture-preset-tool/tools', enabled: true },
|
|
268
|
+
{ moduleName: 'fixture-failed-preset-entry', enabled: true, fiberState: 'failed' },
|
|
269
|
+
{ moduleName: 'fixture-disabled-preset-entry', enabled: false },
|
|
270
|
+
{ moduleName: 'fixture-conditional-preset-entry', enabled: 'conditional', condition: 'PRIVATE_CONDITION' },
|
|
271
|
+
] }],
|
|
272
|
+
},
|
|
273
|
+
} })
|
|
274
|
+
try {
|
|
275
|
+
const body = await (await reportEnvironment(h)).json()
|
|
276
|
+
assert.equal(body.runtime.pluginsStatus, 'partial')
|
|
277
|
+
assert.deepEqual(body.runtime.enabledPlugins, [{ name: 'fixture-failed-preset-entry', version: null }, { name: 'fixture-preset-tool', version: '4.5.6' }])
|
|
278
|
+
assert.doesNotMatch(JSON.stringify(body), /private-preset|DO_NOT_EXPORT|PRIVATE_CONDITION|disabled-preset|conditional-preset|baseUrl/)
|
|
279
|
+
} finally { await disposeHarness(h) }
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('report environment requires authenticated owner before collecting inventory and honors cancellation', async () => {
|
|
284
|
+
for (const [admission, status] of [[{ rejection: 401 }, 401], [{ peer: { principal: { kind: 'guest' } } }, 403]]) {
|
|
285
|
+
let reads = 0
|
|
286
|
+
const h = harness({}, { admission, services: { pluginManager: { listPlugins () { reads++; return [] }, listBundles () { reads++; return [] } } } })
|
|
287
|
+
try {
|
|
288
|
+
const response = await reportEnvironment(h)
|
|
289
|
+
assert.equal(response.status, status)
|
|
290
|
+
assert.equal(reads, 0)
|
|
291
|
+
} finally { await disposeHarness(h) }
|
|
292
|
+
}
|
|
293
|
+
const controller = new AbortController()
|
|
294
|
+
const h = harness({}, { services: { pluginManager: { listBundles: async () => { controller.abort(); return [] }, listPlugins: async () => { assert.fail('must stop after cancellation') } } } })
|
|
295
|
+
try { assert.equal((await reportEnvironment(h, controller.signal)).status, 499) } finally { await disposeHarness(h) }
|
|
296
|
+
})
|
|
297
|
+
|
|
81
298
|
test('diagnostic routes share cached versions from the actual launch and plugin manifests', async () => {
|
|
82
299
|
const originalRead = fs.readFile
|
|
83
300
|
const oldInstall = process.env.DSH_INSTALL_DIR
|
|
@@ -505,9 +722,9 @@ function runtimeHarness (h, adapter, middleware = []) {
|
|
|
505
722
|
}
|
|
506
723
|
|
|
507
724
|
async function collect (stream) { const chunks = []; for await (const chunk of stream) chunks.push(chunk); return chunks }
|
|
508
|
-
async function compactionRecords (h) {
|
|
725
|
+
async function compactionRecords (h, sessionId = 'session-1') {
|
|
509
726
|
const route = h.routes.find((entry) => entry.path === '/api/better-tips/compaction')
|
|
510
|
-
return (await route.fetch(new Request(
|
|
727
|
+
return (await route.fetch(new Request(`http://localhost/api/better-tips/compaction?sessionId=${encodeURIComponent(sessionId)}`))).json()
|
|
511
728
|
}
|
|
512
729
|
|
|
513
730
|
test('compaction retries re-dispatch every attempt through two downstream middleware layers', async () => {
|
|
@@ -527,6 +744,8 @@ test('compaction retries re-dispatch every attempt through two downstream middle
|
|
|
527
744
|
assert.equal(record.attempts.length, 2)
|
|
528
745
|
assert.equal(record.delays.length, 1)
|
|
529
746
|
assert.equal(record.attempts[0].failure.code, 'SERVER')
|
|
747
|
+
assert.equal(record.attempts[0].finishReason, 'error')
|
|
748
|
+
assert.equal(record.attempts[1].finishReason, 'stop')
|
|
530
749
|
assert.equal(record.attempts[0].failure.status, 502)
|
|
531
750
|
assert.ok(Number.isFinite(record.durationMs))
|
|
532
751
|
await disposeHarness(h)
|
|
@@ -658,6 +877,137 @@ test('compaction capture is bound at failure and cannot change after unrelated l
|
|
|
658
877
|
} finally { await disposeHarness(h); globalThis.fetch = originalFetch }
|
|
659
878
|
})
|
|
660
879
|
|
|
880
|
+
test('compaction HTTP and network failures retain scoped capture when iterator throws the original error', async () => {
|
|
881
|
+
const originalFetch = globalThis.fetch
|
|
882
|
+
try {
|
|
883
|
+
for (const kind of ['response', 'network']) {
|
|
884
|
+
const networkError = Object.assign(new Error('synthetic socket failed'), { code: 'ECONNRESET' })
|
|
885
|
+
const thrown = kind === 'network' ? networkError : Object.assign(new Error('adapter failed after HTTP response'), { code: 'STREAM_CLOSED' })
|
|
886
|
+
globalThis.fetch = async () => {
|
|
887
|
+
if (kind === 'network') throw networkError
|
|
888
|
+
return new Response('synthetic upstream body', { status: 502, headers: { 'x-request-id': 'throw-http-request' } })
|
|
889
|
+
}
|
|
890
|
+
const h = harness({ captureDetails: true, compactionRetries: 3, initialDelayMs: 0 })
|
|
891
|
+
try {
|
|
892
|
+
const { runtime, stats } = runtimeHarness(h, () => (async function * () {
|
|
893
|
+
const response = await fetch(`https://${kind}.example/v1/chat`)
|
|
894
|
+
await response.text()
|
|
895
|
+
throw thrown
|
|
896
|
+
})())
|
|
897
|
+
await assert.rejects(collect(runtime.stream({ ...COMPACTION_OPTIONS })), error => error === thrown)
|
|
898
|
+
const body = await compactionRecords(h)
|
|
899
|
+
const record = body.records[0]
|
|
900
|
+
const attempt = record.attempts[0]
|
|
901
|
+
assert.equal(record.status, 'thrown')
|
|
902
|
+
assert.equal(record.attempts.length, 1)
|
|
903
|
+
assert.equal(stats.adapterCalls, 1, 'throw handling does not introduce retries')
|
|
904
|
+
assert.equal(body.capture.records, 1)
|
|
905
|
+
assert.equal(attempt.provenance, 'request-context')
|
|
906
|
+
assert.ok(attempt.attemptId)
|
|
907
|
+
assert.equal(attempt.detail.context.attemptId, attempt.attemptId)
|
|
908
|
+
assert.equal(attempt.detail.context.sessionId, COMPACTION_OPTIONS.sessionId)
|
|
909
|
+
assert.equal(attempt.detail.host, `${kind}.example`)
|
|
910
|
+
assert.equal(attempt.thrown.message, thrown.message)
|
|
911
|
+
if (kind === 'response') {
|
|
912
|
+
assert.equal(attempt.detail.status, 502)
|
|
913
|
+
assert.equal(attempt.detail.headers['x-request-id'], 'throw-http-request')
|
|
914
|
+
} else assert.equal(attempt.detail.error.code, 'ECONNRESET')
|
|
915
|
+
} finally { await disposeHarness(h) }
|
|
916
|
+
}
|
|
917
|
+
} finally { globalThis.fetch = originalFetch }
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
test('synchronous compaction dispatch throws retain identity without borrowing an earlier capture', async () => {
|
|
921
|
+
const originalFetch = globalThis.fetch
|
|
922
|
+
globalThis.fetch = async () => new Response('old capture', { status: 502 })
|
|
923
|
+
const h = harness({ captureDetails: true, compactionRetries: 3 })
|
|
924
|
+
try {
|
|
925
|
+
await fetch('https://unrelated.example/v1/chat')
|
|
926
|
+
const thrown = new Error('synchronous adapter refused dispatch')
|
|
927
|
+
const { runtime, stats } = runtimeHarness(h, () => { throw thrown })
|
|
928
|
+
await assert.rejects(collect(runtime.stream({ ...COMPACTION_OPTIONS })), error => error === thrown)
|
|
929
|
+
const body = await compactionRecords(h)
|
|
930
|
+
const attempt = body.records[0].attempts[0]
|
|
931
|
+
assert.equal(stats.adapterCalls, 1)
|
|
932
|
+
assert.equal(body.capture.records, 1)
|
|
933
|
+
assert.equal(body.records[0].status, 'thrown')
|
|
934
|
+
assert.ok(attempt.attemptId, 'identity is assigned before the dispatch can throw')
|
|
935
|
+
assert.equal(attempt.provenance, 'request-context')
|
|
936
|
+
assert.equal(attempt.detail, undefined)
|
|
937
|
+
assert.equal(attempt.finishReason, undefined)
|
|
938
|
+
assert.equal(attempt.thrown.message, thrown.message)
|
|
939
|
+
} finally { await disposeHarness(h); globalThis.fetch = originalFetch }
|
|
940
|
+
})
|
|
941
|
+
|
|
942
|
+
test('concurrent thrown compactions keep evidence inside their own session and attempt', async () => {
|
|
943
|
+
const originalFetch = globalThis.fetch
|
|
944
|
+
globalThis.fetch = async (url) => new Response(String(url), { status: 502 })
|
|
945
|
+
const h = harness({ captureDetails: true, compactionRetries: 0 })
|
|
946
|
+
try {
|
|
947
|
+
const waiting = new Map()
|
|
948
|
+
const errors = { 'session-A': new Error('throw A'), 'session-B': new Error('throw B') }
|
|
949
|
+
const { runtime } = runtimeHarness(h, (options) => (async function * () {
|
|
950
|
+
await fetch(`https://${options.sessionId.toLowerCase()}.example/v1/chat`)
|
|
951
|
+
await new Promise(resolve => waiting.set(options.sessionId, resolve))
|
|
952
|
+
throw errors[options.sessionId]
|
|
953
|
+
})())
|
|
954
|
+
const rejected = Object.fromEntries(Object.keys(errors).map(sessionId => [sessionId,
|
|
955
|
+
assert.rejects(collect(runtime.stream({ ...COMPACTION_OPTIONS, sessionId })), error => error === errors[sessionId])]))
|
|
956
|
+
for (let pass = 0; pass < 50 && waiting.size < 2; pass++) await new Promise(resolve => setImmediate(resolve))
|
|
957
|
+
assert.equal(waiting.size, 2)
|
|
958
|
+
waiting.get('session-B')()
|
|
959
|
+
await rejected['session-B']
|
|
960
|
+
waiting.get('session-A')()
|
|
961
|
+
await rejected['session-A']
|
|
962
|
+
const a = (await compactionRecords(h, 'session-A')).records[0].attempts[0]
|
|
963
|
+
const b = (await compactionRecords(h, 'session-B')).records[0].attempts[0]
|
|
964
|
+
assert.notEqual(a.attemptId, b.attemptId)
|
|
965
|
+
for (const [attempt, sessionId] of [[a, 'session-A'], [b, 'session-B']]) {
|
|
966
|
+
assert.equal(attempt.detail.context.sessionId, sessionId)
|
|
967
|
+
assert.equal(attempt.detail.context.attemptId, attempt.attemptId)
|
|
968
|
+
assert.equal(attempt.detail.host, `${sessionId.toLowerCase()}.example`)
|
|
969
|
+
}
|
|
970
|
+
} finally { await disposeHarness(h); globalThis.fetch = originalFetch }
|
|
971
|
+
})
|
|
972
|
+
|
|
973
|
+
test('caller cancellation during compaction preserves original thrown identity and starts no retry', async () => {
|
|
974
|
+
const originalFetch = globalThis.fetch
|
|
975
|
+
globalThis.fetch = async () => new Response('failed before cancellation', { status: 502 })
|
|
976
|
+
const h = harness({ captureDetails: true, compactionRetries: 3 })
|
|
977
|
+
const caller = new AbortController()
|
|
978
|
+
const abort = Object.assign(new Error('caller cancelled inside adapter'), { code: 'ABORTED' })
|
|
979
|
+
try {
|
|
980
|
+
const { runtime, stats } = runtimeHarness(h, () => (async function * () {
|
|
981
|
+
await fetch('https://cancelled.example/v1/chat')
|
|
982
|
+
caller.abort(abort)
|
|
983
|
+
throw abort
|
|
984
|
+
})())
|
|
985
|
+
await assert.rejects(collect(runtime.stream({ ...COMPACTION_OPTIONS, signal: caller.signal })), error => error === abort)
|
|
986
|
+
const record = (await compactionRecords(h)).records[0]
|
|
987
|
+
assert.equal(record.status, 'aborted')
|
|
988
|
+
assert.equal(record.attempts[0].thrown.code, 'ABORTED')
|
|
989
|
+
assert.equal(record.attempts[0].detail.host, 'cancelled.example')
|
|
990
|
+
assert.equal(stats.adapterCalls, 1)
|
|
991
|
+
} finally { await disposeHarness(h); globalThis.fetch = originalFetch }
|
|
992
|
+
})
|
|
993
|
+
|
|
994
|
+
test('compaction terminal finish kind is exposed and max-tokens is never recorded as ok', async () => {
|
|
995
|
+
const h = harness({ compactionRetries: 3 })
|
|
996
|
+
try {
|
|
997
|
+
for (const [kind, expected] of [['stop', 'ok'], ['tool-calls', 'ok'], ['max-tokens', 'max-tokens'], ['aborted', 'aborted'], ['unexpected-kind', 'incomplete']]) {
|
|
998
|
+
const chunks = [{ type: 'finish', reason: { kind } }]
|
|
999
|
+
const { runtime, stats } = runtimeHarness(h, () => streamOf(chunks))
|
|
1000
|
+
assert.deepEqual(await collect(runtime.stream({ ...COMPACTION_OPTIONS })), chunks)
|
|
1001
|
+
const record = (await compactionRecords(h)).records.at(-1)
|
|
1002
|
+
assert.equal(record.status, expected, kind)
|
|
1003
|
+
assert.equal(record.attempts[0].finishReason, kind)
|
|
1004
|
+
assert.ok(record.attempts[0].attemptId)
|
|
1005
|
+
assert.equal(record.attempts[0].detail, undefined)
|
|
1006
|
+
assert.equal(stats.adapterCalls, 1, 'terminal-state recording does not change retry policy')
|
|
1007
|
+
}
|
|
1008
|
+
} finally { await disposeHarness(h) }
|
|
1009
|
+
})
|
|
1010
|
+
|
|
661
1011
|
test('successful compaction attempts never inherit a neighboring failure capture', async () => {
|
|
662
1012
|
const originalFetch = globalThis.fetch
|
|
663
1013
|
globalThis.fetch = async () => new Response('', { status: 502 })
|
|
@@ -853,6 +1203,70 @@ test('AUTH compaction route suppresses credential echoes in both failure and cor
|
|
|
853
1203
|
} finally { await disposeHarness(h); globalThis.fetch = originalFetch }
|
|
854
1204
|
})
|
|
855
1205
|
|
|
1206
|
+
test('AUTH thrown compactions protect arbitrary text and body through cause and aggregate errors', async () => {
|
|
1207
|
+
const originalFetch = globalThis.fetch
|
|
1208
|
+
const unrecognized = 'arbitraryVendorCredentialWithoutKnownShape'
|
|
1209
|
+
try {
|
|
1210
|
+
for (const wrapped of ['direct', 'cause', 'aggregate']) {
|
|
1211
|
+
globalThis.fetch = async () => new Response(`${unrecognized} ${PRIVATE_KEY} ${PRIVATE_BEARER}`, {
|
|
1212
|
+
status: 401, statusText: unrecognized, headers: { 'x-request-id': 'auth-throw-request', 'cf-ray': PRIVATE_KEY },
|
|
1213
|
+
})
|
|
1214
|
+
const auth = Object.assign(new Error(unrecognized), { code: 'AUTH', errno: unrecognized })
|
|
1215
|
+
const thrown = wrapped === 'cause' ? Object.assign(new Error(unrecognized), { code: 'WRAPPER', cause: auth })
|
|
1216
|
+
: wrapped === 'aggregate' ? new AggregateError([new Error('unrelated'), new AggregateError([auth], unrecognized)], unrecognized) : auth
|
|
1217
|
+
if (wrapped === 'aggregate') thrown.errors.unshift(thrown)
|
|
1218
|
+
const h = harness({ captureDetails: true, compactionRetries: 0 })
|
|
1219
|
+
try {
|
|
1220
|
+
const { runtime } = runtimeHarness(h, () => (async function * () {
|
|
1221
|
+
await (await fetch('https://auth-throw.example/v1/chat')).text()
|
|
1222
|
+
throw thrown
|
|
1223
|
+
})())
|
|
1224
|
+
await assert.rejects(collect(runtime.stream({ ...COMPACTION_OPTIONS })), error => error === thrown)
|
|
1225
|
+
const body = await compactionRecords(h)
|
|
1226
|
+
assertNoCredential(body)
|
|
1227
|
+
assert.equal(JSON.stringify(body).includes(unrecognized), false)
|
|
1228
|
+
const attempt = body.records[0].attempts[0]
|
|
1229
|
+
assert.deepEqual(attempt.thrown, { code: 'AUTH', message: '', protected: true })
|
|
1230
|
+
assert.ok(attempt.attemptId)
|
|
1231
|
+
assert.equal(attempt.detail.context.attemptId, attempt.attemptId)
|
|
1232
|
+
assert.equal(attempt.detail.status, 401)
|
|
1233
|
+
assert.equal(attempt.detail.headers['x-request-id'], 'auth-throw-request')
|
|
1234
|
+
assert.equal(attempt.detail.statusText, undefined)
|
|
1235
|
+
assert.equal(attempt.detail.body, undefined)
|
|
1236
|
+
assert.equal(attempt.detail.error, undefined)
|
|
1237
|
+
assert.equal(attempt.detail.bodyReason, 'credential-protection')
|
|
1238
|
+
assert.equal(attempt.detail.protected, true)
|
|
1239
|
+
assert.equal(thrown.message, unrecognized, 'original thrown value is untouched')
|
|
1240
|
+
} finally { await disposeHarness(h) }
|
|
1241
|
+
}
|
|
1242
|
+
} finally { globalThis.fetch = originalFetch }
|
|
1243
|
+
})
|
|
1244
|
+
|
|
1245
|
+
test('thrown compaction network detail redacts nested credentials without changing the source error', async () => {
|
|
1246
|
+
const originalFetch = globalThis.fetch
|
|
1247
|
+
const thrown = Object.assign(new Error(`network ${PRIVATE_KEY}`), {
|
|
1248
|
+
code: 'ECONNRESET', errno: PRIVATE_KEY, syscall: PRIVATE_BEARER, address: PRIVATE_KEY,
|
|
1249
|
+
cause: Object.assign(new Error(PRIVATE_BEARER), { code: `ERR_${PRIVATE_KEY}` }),
|
|
1250
|
+
})
|
|
1251
|
+
globalThis.fetch = async () => { throw thrown }
|
|
1252
|
+
const h = harness({ captureDetails: true, compactionRetries: 0 })
|
|
1253
|
+
try {
|
|
1254
|
+
const { runtime } = runtimeHarness(h, () => (async function * () { await fetch('https://nested-throw.example/v1/chat') })())
|
|
1255
|
+
await assert.rejects(collect(runtime.stream({ ...COMPACTION_OPTIONS })), error => error === thrown)
|
|
1256
|
+
const body = await compactionRecords(h)
|
|
1257
|
+
assertNoCredential(body)
|
|
1258
|
+
const attempt = body.records[0].attempts[0]
|
|
1259
|
+
assert.equal(attempt.detail.error.errno, 'sk-[redacted]')
|
|
1260
|
+
assert.equal(attempt.detail.error.syscall, 'Bearer [redacted]')
|
|
1261
|
+
assert.equal(attempt.detail.error.address, 'sk-[redacted]')
|
|
1262
|
+
assert.equal(attempt.detail.error.cause.message, 'Bearer [redacted]')
|
|
1263
|
+
assert.equal(attempt.detail.error.cause.code, 'ERR_sk-[redacted]')
|
|
1264
|
+
assert.equal(attempt.thrown.errno, 'sk-[redacted]')
|
|
1265
|
+
assert.equal(thrown.errno, PRIVATE_KEY)
|
|
1266
|
+
assert.equal(thrown.cause.message, PRIVATE_BEARER)
|
|
1267
|
+
} finally { await disposeHarness(h); globalThis.fetch = originalFetch }
|
|
1268
|
+
})
|
|
1269
|
+
|
|
856
1270
|
test('metadata error responses redact native exception text and never export AUTH durable event text', async () => {
|
|
857
1271
|
const failure = Object.freeze({ code: 'AUTH', message: `unrecognized-provider-credential ${PRIVATE_KEY} ${PRIVATE_BEARER}` })
|
|
858
1272
|
const event = Object.freeze({ seq: 7, type: 'llm/retry', data: Object.freeze({ failure }) })
|