blun-king-cli 9.1.471 → 9.1.473

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.
@@ -1,4 +1,5 @@
1
1
  const AGENT_TURNS_PATH = '/v1/agent/turns';
2
+ const AGENT_SESSIONS_PATH = '/v1/agent/sessions';
2
3
  const PUBLIC_MODEL_ID = 'king';
3
4
  const PUBLIC_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/u;
4
5
 
@@ -70,9 +71,26 @@ function decodeRequestId(value) {
70
71
  return requirePublicId(decoded, 'request ID');
71
72
  }
72
73
 
74
+ function decodeSessionId(value) {
75
+ let decoded;
76
+ try {
77
+ decoded = decodeURIComponent(value);
78
+ } catch {
79
+ throw adapterError('AGENT_API_INVALID_ID', 'session ID is not valid URL encoding');
80
+ }
81
+ return requirePublicId(decoded, 'session ID');
82
+ }
83
+
73
84
  function routeFor(method, rawPath) {
74
85
  const path = String(rawPath ?? '').split('?', 1)[0];
75
86
  if (method === 'POST' && path === AGENT_TURNS_PATH) return { kind: 'start' };
87
+ if (path.startsWith(`${AGENT_SESSIONS_PATH}/`)) {
88
+ const tail = path.slice(AGENT_SESSIONS_PATH.length + 1);
89
+ if (method === 'DELETE' && !tail.includes('/')) {
90
+ return { kind: 'closeSession', sessionId: decodeSessionId(tail) };
91
+ }
92
+ return { kind: 'unknown' };
93
+ }
76
94
  if (!path.startsWith(`${AGENT_TURNS_PATH}/`)) {
77
95
  if (path.startsWith('/v1/agent/')) return { kind: 'unknown' };
78
96
  return null;
@@ -207,8 +225,19 @@ function createAgentApiHttpAdapter(options = {}) {
207
225
  if (typeof options.resolveSession !== 'function') {
208
226
  throw new TypeError('resolveSession must be a function');
209
227
  }
228
+ if (typeof options.closeSession !== 'function') {
229
+ throw new TypeError('closeSession must be a function');
230
+ }
210
231
  const owners = new Map();
211
232
 
233
+ function hasActiveOwner(principalId, sessionId) {
234
+ const prefix = `${sessionId}\0`;
235
+ for (const [key, scopeOwners] of owners) {
236
+ if (key.startsWith(prefix) && scopeOwners.has(principalId)) return true;
237
+ }
238
+ return false;
239
+ }
240
+
212
241
  function requireOwner(principalId, sessionId, requestId) {
213
242
  const key = scopeKey(sessionId, requestId);
214
243
  const scopeOwners = owners.get(key);
@@ -242,6 +271,12 @@ function createAgentApiHttpAdapter(options = {}) {
242
271
  if (scopeOwners?.size === 0) owners.delete(key);
243
272
  }
244
273
 
274
+ function trackRunningScope(key, principalId, internalSessionId) {
275
+ const scopeOwners = owners.get(key) ?? new Map();
276
+ scopeOwners.set(principalId, internalSessionId);
277
+ owners.set(key, scopeOwners);
278
+ }
279
+
245
280
  async function handleStart(input, principalId) {
246
281
  const body = requireBody(input.body);
247
282
  requireKing(body.model);
@@ -266,20 +301,24 @@ function createAgentApiHttpAdapter(options = {}) {
266
301
  );
267
302
  }
268
303
  const internalSessionId = session.id.trim();
269
- const response = publicCompletion(await controller.startTurn({
270
- session,
271
- publicSessionId: sessionId,
272
- requestId,
273
- prompt: normalizePrompt(body.input),
274
- tools: normalizeTools(body.tools),
275
- }));
276
- trackScope(
277
- scopeKey(sessionId, requestId),
278
- principalId,
279
- internalSessionId,
280
- response,
281
- );
282
- return jsonResponse(200, response);
304
+ const prompt = normalizePrompt(body.input);
305
+ const tools = normalizeTools(body.tools);
306
+ const key = scopeKey(sessionId, requestId);
307
+ trackRunningScope(key, principalId, internalSessionId);
308
+ try {
309
+ const response = publicCompletion(await controller.startTurn({
310
+ session,
311
+ publicSessionId: sessionId,
312
+ requestId,
313
+ prompt,
314
+ tools,
315
+ }));
316
+ trackScope(key, principalId, internalSessionId, response);
317
+ return jsonResponse(200, response);
318
+ } catch (error) {
319
+ trackScope(key, principalId, internalSessionId, null);
320
+ throw error;
321
+ }
283
322
  }
284
323
 
285
324
  async function handleContinue(input, principalId, requestId) {
@@ -313,6 +352,31 @@ function createAgentApiHttpAdapter(options = {}) {
313
352
  });
314
353
  }
315
354
 
355
+ async function handleCloseSession(principalId, sessionId) {
356
+ if (hasActiveOwner(principalId, sessionId)) {
357
+ throw adapterError(
358
+ 'AGENT_API_SESSION_BUSY',
359
+ 'Agent session has an active turn.',
360
+ 409,
361
+ );
362
+ }
363
+ const closed = await options.closeSession({ principalId, sessionId });
364
+ if (closed !== true) {
365
+ throw adapterError(
366
+ 'AGENT_API_SESSION_NOT_FOUND',
367
+ 'Agent session was not found.',
368
+ 404,
369
+ );
370
+ }
371
+ return jsonResponse(200, {
372
+ id: `agent-session-${sessionId}`,
373
+ object: 'agent.session.closed',
374
+ model: PUBLIC_MODEL_ID,
375
+ session_id: sessionId,
376
+ closed: true,
377
+ });
378
+ }
379
+
316
380
  async function handleRequest(input) {
317
381
  if (input === null || typeof input !== 'object') return null;
318
382
  const method = String(input.method ?? '').toUpperCase();
@@ -331,6 +395,9 @@ function createAgentApiHttpAdapter(options = {}) {
331
395
  }
332
396
  const principalId = requireTrustedId(input.principalId, 'principalId');
333
397
  if (route.kind === 'start') return await handleStart(input, principalId);
398
+ if (route.kind === 'closeSession') {
399
+ return await handleCloseSession(principalId, route.sessionId);
400
+ }
334
401
  if (route.kind === 'continue') {
335
402
  return await handleContinue(input, principalId, route.requestId);
336
403
  }
@@ -344,6 +411,7 @@ function createAgentApiHttpAdapter(options = {}) {
344
411
  }
345
412
 
346
413
  export {
414
+ AGENT_SESSIONS_PATH,
347
415
  AGENT_TURNS_PATH,
348
416
  AgentApiHttpAdapterError,
349
417
  PUBLIC_MODEL_ID,
@@ -125,6 +125,7 @@ function createAgentApiServiceHost(options = {}) {
125
125
  const adapter = createAgentApiHttpAdapter({
126
126
  controller,
127
127
  resolveSession: sessionRegistry.resolveSession,
128
+ closeSession: sessionRegistry.closeSession,
128
129
  });
129
130
  const privateServer = createAgentApiPrivateHttpServer({
130
131
  adapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.471",
3
+ "version": "9.1.473",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {