@lythos/agent-adapter-deepseek-serve 0.16.0 → 0.17.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lythos/agent-adapter-deepseek-serve",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "bun test src/ --pass-with-no-tests",
@@ -22,6 +22,6 @@
22
22
  "serve-mode"
23
23
  ],
24
24
  "dependencies": {
25
- "@lythos/agent-adapter": "^0.16.0"
25
+ "@lythos/agent-adapter": "^0.17.0"
26
26
  }
27
27
  }
@@ -3,7 +3,7 @@
3
3
  * No real serve process needed — pure state machine + injectable fs.
4
4
  */
5
5
 
6
- import { describe, test, expect, beforeEach, afterEach } from 'bun:test'
6
+ import { describe, it, expect, beforeEach, afterEach } from 'bun:test'
7
7
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
8
8
  import { tmpdir } from 'node:os'
9
9
  import { join } from 'node:path'
@@ -30,14 +30,14 @@ describe('ServeLock FSM — lock file lifecycle', () => {
30
30
  beforeEach(() => { lockDir = tempLockDir() })
31
31
  afterEach(() => { try { rmSync(lockDir, { recursive: true, force: true }) } catch {} })
32
32
 
33
- test('FSM: no lock → null', () => {
33
+ it('FSM: no lock → null', () => {
34
34
  const lockPath = join(lockDir, 'deepseek-serve.json')
35
35
  // inline logic
36
36
  // Simulate readLock logic
37
37
  expect(existsSync(lockPath)).toBe(false)
38
38
  })
39
39
 
40
- test('FSM: write lock → read back', () => {
40
+ it('FSM: write lock → read back', () => {
41
41
  const lock = { pid: 12345, port: 17878, version: '0.8.14', startedAt: new Date().toISOString(), threads: {} }
42
42
  writeTestLock(lockDir, lock)
43
43
  const content = readFileSync(join(lockDir, 'deepseek-serve.json'), 'utf-8')
@@ -48,7 +48,7 @@ describe('ServeLock FSM — lock file lifecycle', () => {
48
48
  expect(parsed.threads).toEqual({})
49
49
  })
50
50
 
51
- test('FSM: corrupt lock → null (not crash)', () => {
51
+ it('FSM: corrupt lock → null (not crash)', () => {
52
52
  writeFileSync(join(lockDir, 'deepseek-serve.json'), 'not valid json {{{')
53
53
  // readLock should return null, not throw
54
54
  try {
@@ -62,7 +62,7 @@ describe('ServeLock FSM — lock file lifecycle', () => {
62
62
  }
63
63
  })
64
64
 
65
- test('FSM: updateThreadMapping adds entry', () => {
65
+ it('FSM: updateThreadMapping adds entry', () => {
66
66
  const lock = { pid: 12345, port: 17878, version: '0.8.14', startedAt: new Date().toISOString(), threads: {} as Record<string, string> }
67
67
  writeTestLock(lockDir, lock)
68
68
  // Simulate update
@@ -74,7 +74,7 @@ describe('ServeLock FSM — lock file lifecycle', () => {
74
74
  expect(Object.keys(parsed.threads).length).toBe(1)
75
75
  })
76
76
 
77
- test('FSM: multiple sessions map to different threads', () => {
77
+ it('FSM: multiple sessions map to different threads', () => {
78
78
  const lock = { pid: 12345, port: 17878, version: '0.8.14', startedAt: new Date().toISOString(), threads: {} as Record<string, string> }
79
79
  lock.threads['arena-20260508-001'] = 'thr_aaa'
80
80
  lock.threads['arena-20260508-002'] = 'thr_bbb'
@@ -90,14 +90,14 @@ describe('ServeLock FSM — lock file lifecycle', () => {
90
90
  // ── PID detection ───────────────────────────────────────────────────────────
91
91
 
92
92
  describe('isProcessAlive — PID signal', () => {
93
- test('current process PID is alive', () => {
93
+ it('current process PID is alive', () => {
94
94
  const alive = (() => {
95
95
  try { process.kill(process.pid, 0); return true } catch { return false }
96
96
  })()
97
97
  expect(alive).toBe(true)
98
98
  })
99
99
 
100
- test('impossible PID is dead', () => {
100
+ it('impossible PID is dead', () => {
101
101
  // PID 99999 is extremely unlikely to exist
102
102
  const alive = (() => {
103
103
  try { process.kill(99999, 0); return true } catch { return false }
@@ -105,7 +105,7 @@ describe('isProcessAlive — PID signal', () => {
105
105
  expect(alive).toBe(false)
106
106
  })
107
107
 
108
- test('PID 0 is alive (self)', () => {
108
+ it('PID 0 is alive (self)', () => {
109
109
  // process.kill(0, 0) signals the whole process group — always succeeds
110
110
  const alive = (() => {
111
111
  try { process.kill(0, 0); return true } catch { return false }
@@ -117,30 +117,30 @@ describe('isProcessAlive — PID signal', () => {
117
117
  // ── Version parsing ─────────────────────────────────────────────────────────
118
118
 
119
119
  describe('getVersion — parse deepseek --version', () => {
120
- test('parses standard version format', () => {
120
+ it('parses standard version format', () => {
121
121
  const out = 'deepseek 0.8.14\n'
122
122
  const match = out.match(/(\d+\.\d+\.\d+)/)
123
123
  expect(match?.[1]).toBe('0.8.14')
124
124
  })
125
125
 
126
- test('parses version from mixed output', () => {
126
+ it('parses version from mixed output', () => {
127
127
  const out = 'DeepSeek TUI v0.8.14 (abc1234)\nRuntime: Rust 1.80\n'
128
128
  const match = out.match(/(\d+\.\d+\.\d+)/)
129
129
  expect(match?.[1]).toBe('0.8.14')
130
130
  })
131
131
 
132
- test('returns null for no version', () => {
132
+ it('returns null for no version', () => {
133
133
  const out = 'command not found\n'
134
134
  const match = out.match(/(\d+\.\d+\.\d+)/)
135
135
  expect(match).toBe(null)
136
136
  })
137
137
 
138
- test('0.8.x passes version check', () => {
138
+ it('0.8.x passes version check', () => {
139
139
  const version = '0.8.14'
140
140
  expect(version.startsWith('0.8.')).toBe(true)
141
141
  })
142
142
 
143
- test('0.9.x still works (with warning)', () => {
143
+ it('0.9.x still works (with warning)', () => {
144
144
  const version = '0.9.0'
145
145
  expect(version.startsWith('0.8.')).toBe(false)
146
146
  // Should warn but continue
@@ -150,7 +150,7 @@ describe('getVersion — parse deepseek --version', () => {
150
150
  // ── Session ID format ───────────────────────────────────────────────────────
151
151
 
152
152
  describe('nextSessionId — format', () => {
153
- test('starts with arena- prefix', () => {
153
+ it('starts with arena- prefix', () => {
154
154
  let counter = 0
155
155
  const ts = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 15)
156
156
  counter++
@@ -158,7 +158,7 @@ describe('nextSessionId — format', () => {
158
158
  expect(id.startsWith('arena-')).toBe(true)
159
159
  })
160
160
 
161
- test('counter increments', () => {
161
+ it('counter increments', () => {
162
162
  let counter = 0
163
163
  const ts = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 15)
164
164
  const ids: string[] = []
@@ -176,12 +176,12 @@ describe('nextSessionId — format', () => {
176
176
  // ── Adapter registration ────────────────────────────────────────────────────
177
177
 
178
178
  describe('adapter registration', () => {
179
- test('adapter is registered under name "deepseek"', async () => {
179
+ it('adapter is registered under name "deepseek"', async () => {
180
180
  const { deepseekServeAdapter } = await import('./deepseek-serve')
181
181
  expect(deepseekServeAdapter.name).toBe('deepseek')
182
182
  })
183
183
 
184
- test('adapter has spawn method', async () => {
184
+ it('adapter has spawn method', async () => {
185
185
  const { deepseekServeAdapter } = await import('./deepseek-serve')
186
186
  expect(typeof deepseekServeAdapter.spawn).toBe('function')
187
187
  })
@@ -190,7 +190,7 @@ describe('adapter registration', () => {
190
190
  // ── Actor FSM: state transitions (conceptual) ───────────────────────────────
191
191
 
192
192
  describe('Actor FSM — state transitions', () => {
193
- test('state: cold start → serve running', () => {
193
+ it('state: cold start → serve running', () => {
194
194
  // Given: no lock file, serve not running
195
195
  // When: ensureServeRunning()
196
196
  // Then: findFreePort → spawn serve → health check → writeLock → return port
@@ -200,7 +200,7 @@ describe('Actor FSM — state transitions', () => {
200
200
  expect(states).toContain('error')
201
201
  })
202
202
 
203
- test('state: warm reuse → skip start', () => {
203
+ it('state: warm reuse → skip start', () => {
204
204
  // Given: lock exists, PID alive, health check passes
205
205
  // When: ensureServeRunning()
206
206
  // Then: cachedPort → health check → return (no new process)
@@ -208,7 +208,7 @@ describe('Actor FSM — state transitions', () => {
208
208
  expect(expectedPath.length).toBe(3)
209
209
  })
210
210
 
211
- test('state: dead PID → restart', () => {
211
+ it('state: dead PID → restart', () => {
212
212
  // Given: lock exists, PID dead
213
213
  // When: ensureServeRunning()
214
214
  // Then: isProcessAlive → false → findFreePort → spawn → health → writeLock
@@ -216,7 +216,7 @@ describe('Actor FSM — state transitions', () => {
216
216
  expect(expectedPath.length).toBe(7)
217
217
  })
218
218
 
219
- test('state: port occupied → increment', () => {
219
+ it('state: port occupied → increment', () => {
220
220
  // Given: base port 17878 occupied
221
221
  // When: findFreePort(17878)
222
222
  // Then: try 17879, 17880... until free
@@ -232,26 +232,26 @@ describe('Thread API — request paths', () => {
232
232
  const PORT = 17878
233
233
  const THREAD_ID = 'thr_test123'
234
234
 
235
- test('POST /v1/threads — create thread', () => {
235
+ it('POST /v1/threads — create thread', () => {
236
236
  const path = `/v1/threads`
237
237
  const url = `http://127.0.0.1:${PORT}${path}`
238
238
  expect(path).toBe('/v1/threads')
239
239
  expect(url).toContain(':17878')
240
240
  })
241
241
 
242
- test('POST /v1/threads/{id}/turns — send turn', () => {
242
+ it('POST /v1/threads/{id}/turns — send turn', () => {
243
243
  const path = `/v1/threads/${THREAD_ID}/turns`
244
244
  expect(path).toContain(THREAD_ID)
245
245
  expect(path).toContain('/turns')
246
246
  })
247
247
 
248
- test('GET /v1/threads/{id}/events — SSE stream', () => {
248
+ it('GET /v1/threads/{id}/events — SSE stream', () => {
249
249
  const path = `/v1/threads/${THREAD_ID}/events`
250
250
  const url = `http://127.0.0.1:${PORT}${path}?since_seq=0`
251
251
  expect(url).toContain('since_seq=0')
252
252
  })
253
253
 
254
- test('GET /health — health check', () => {
254
+ it('GET /health — health check', () => {
255
255
  const url = `http://127.0.0.1:${PORT}/health`
256
256
  expect(url.endsWith('/health')).toBe(true)
257
257
  })
@@ -260,7 +260,7 @@ describe('Thread API — request paths', () => {
260
260
  // ── Lock file schema ────────────────────────────────────────────────────────
261
261
 
262
262
  describe('ServeLock schema', () => {
263
- test('valid lock matches expected shape', () => {
263
+ it('valid lock matches expected shape', () => {
264
264
  const lock = {
265
265
  pid: 12345,
266
266
  port: 17878,
@@ -275,7 +275,7 @@ describe('ServeLock schema', () => {
275
275
  expect(typeof lock.threads).toBe('object')
276
276
  })
277
277
 
278
- test('threads must be string→string map', () => {
278
+ it('threads must be string→string map', () => {
279
279
  const threads: Record<string, string> = {}
280
280
  threads['session-a'] = 'thr_111'
281
281
  threads['session-b'] = 'thr_222'