@agi-cli/server 0.1.73 → 0.1.74

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": "@agi-cli/server",
3
- "version": "0.1.73",
3
+ "version": "0.1.74",
4
4
  "description": "HTTP API server for AGI CLI",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -29,8 +29,8 @@
29
29
  "typecheck": "tsc --noEmit"
30
30
  },
31
31
  "dependencies": {
32
- "@agi-cli/sdk": "0.1.73",
33
- "@agi-cli/database": "0.1.73",
32
+ "@agi-cli/sdk": "0.1.74",
33
+ "@agi-cli/database": "0.1.74",
34
34
  "drizzle-orm": "^0.44.5",
35
35
  "hono": "^4.9.9",
36
36
  "zod": "^4.1.8"
@@ -99,6 +99,7 @@ export function registerSessionMessagesRoutes(app: Hono) {
99
99
  const modelName = body?.model ?? sess.model ?? cfg.defaults.model;
100
100
  const agent = body?.agent ?? sess.agent ?? cfg.defaults.agent;
101
101
  const content = body?.content ?? '';
102
+ const userContext = body?.userContext;
102
103
 
103
104
  // Validate model capabilities if tools are allowed for this agent
104
105
  const wantsToolCalls = true; // agent toolset may be non-empty
@@ -131,6 +132,7 @@ export function registerSessionMessagesRoutes(app: Hono) {
131
132
  model: modelName,
132
133
  content,
133
134
  oneShot: Boolean(body?.oneShot),
135
+ userContext,
134
136
  });
135
137
  return c.json({ messageId: assistantMessageId }, 202);
136
138
  } catch (error) {
@@ -70,8 +70,14 @@ export function serializeError(err: unknown): APIErrorResponse {
70
70
  const payload = toErrorPayload(err);
71
71
 
72
72
  // Determine HTTP status code
73
- let status = 500;
74
- if (err && typeof err === 'object') {
73
+ // Default to 400 for generic errors (client errors)
74
+ // Only use 500 if explicitly set or for APIError instances without a status
75
+ let status = 400;
76
+
77
+ // Handle APIError instances first
78
+ if (err instanceof APIError) {
79
+ status = err.status;
80
+ } else if (err && typeof err === 'object') {
75
81
  const errObj = err as Record<string, unknown>;
76
82
  if (typeof errObj.status === 'number') {
77
83
  status = errObj.status;
@@ -86,11 +92,6 @@ export function serializeError(err: unknown): APIErrorResponse {
86
92
  }
87
93
  }
88
94
 
89
- // Handle APIError instances
90
- if (err instanceof APIError) {
91
- status = err.status;
92
- }
93
-
94
95
  // Extract code if available
95
96
  let code: string | undefined;
96
97
  if (err && typeof err === 'object') {
@@ -20,13 +20,23 @@ type DispatchOptions = {
20
20
  model: string;
21
21
  content: string;
22
22
  oneShot?: boolean;
23
+ userContext?: string;
23
24
  };
24
25
 
25
26
  export async function dispatchAssistantMessage(
26
27
  options: DispatchOptions,
27
28
  ): Promise<{ assistantMessageId: string }> {
28
- const { cfg, db, session, agent, provider, model, content, oneShot } =
29
- options;
29
+ const {
30
+ cfg,
31
+ db,
32
+ session,
33
+ agent,
34
+ provider,
35
+ model,
36
+ content,
37
+ oneShot,
38
+ userContext,
39
+ } = options;
30
40
  const sessionId = session.id;
31
41
  const now = Date.now();
32
42
  const userMessageId = crypto.randomUUID();
@@ -99,6 +109,7 @@ export async function dispatchAssistantMessage(
99
109
  model,
100
110
  projectRoot: cfg.projectRoot,
101
111
  oneShot: Boolean(oneShot),
112
+ userContext,
102
113
  });
103
114
 
104
115
  void touchSessionLastActive({ db, sessionId });
@@ -385,8 +396,8 @@ function deriveTitle(text: string): string {
385
396
 
386
397
  function sanitizeTitle(s: string): string {
387
398
  let t = s.trim();
388
- t = t.replace(/^['"""''()[\]]+|['"""''()[\]]+$/g, '').trim();
389
- t = t.replace(/[\s\-_:–—]+$/g, '').trim();
399
+ t = t.replace(/^['"""''()[\\]]+|['"""''()[\\]]+$/g, '').trim();
400
+ t = t.replace(/[\\s\\-_:–—]+$/g, '').trim();
390
401
  if (t.length > 64) t = `${t.slice(0, 63).trimEnd()}…`;
391
402
  return t;
392
403
  }
@@ -20,6 +20,7 @@ export async function composeSystemPrompt(options: {
20
20
  spoofPrompt?: string;
21
21
  includeEnvironment?: boolean;
22
22
  includeProjectTree?: boolean;
23
+ userContext?: string;
23
24
  }): Promise<string> {
24
25
  if (options.spoofPrompt) {
25
26
  return options.spoofPrompt.trim();
@@ -61,6 +62,16 @@ export async function composeSystemPrompt(options: {
61
62
  }
62
63
  }
63
64
 
65
+ // Add user-provided context if present
66
+ if (options.userContext?.trim()) {
67
+ const userContextBlock = [
68
+ '<user-provided-state-context>',
69
+ options.userContext.trim(),
70
+ '</user-provided-state-context>',
71
+ ].join('\n');
72
+ parts.push(userContextBlock);
73
+ }
74
+
64
75
  const composed = parts.filter(Boolean).join('\n\n').trim();
65
76
  if (composed) return composed;
66
77
 
@@ -146,6 +146,7 @@ async function runAssistant(opts: RunOpts) {
146
146
  oneShot: opts.oneShot,
147
147
  spoofPrompt: undefined,
148
148
  includeProjectTree: isFirstMessage,
149
+ userContext: opts.userContext,
149
150
  });
150
151
  additionalSystemMessages = [{ role: 'system', content: fullPrompt }];
151
152
  } else {
@@ -157,6 +158,7 @@ async function runAssistant(opts: RunOpts) {
157
158
  oneShot: opts.oneShot,
158
159
  spoofPrompt: undefined,
159
160
  includeProjectTree: isFirstMessage,
161
+ userContext: opts.userContext,
160
162
  });
161
163
  }
162
164
  systemTimer.end();
@@ -9,6 +9,7 @@ export type RunOpts = {
9
9
  model: string;
10
10
  projectRoot: string;
11
11
  oneShot?: boolean;
12
+ userContext?: string;
12
13
  abortSignal?: AbortSignal;
13
14
  };
14
15
 
@@ -39,7 +40,8 @@ export function enqueueAssistantRun(
39
40
  }
40
41
 
41
42
  /**
42
- * Aborts all pending operations for a given session.
43
+ * Signals the abort controller for a session.
44
+ * This will trigger the abortSignal in the streamText call.
43
45
  */
44
46
  export function abortSession(sessionId: string) {
45
47
  const controller = sessionAbortControllers.get(sessionId);
@@ -49,34 +51,25 @@ export function abortSession(sessionId: string) {
49
51
  }
50
52
  }
51
53
 
52
- /**
53
- * Gets the current state of a session's queue.
54
- */
55
54
  export function getRunnerState(sessionId: string): RunnerState | undefined {
56
55
  return runners.get(sessionId);
57
56
  }
58
57
 
59
- /**
60
- * Marks a session queue as running.
61
- */
62
58
  export function setRunning(sessionId: string, running: boolean) {
63
59
  const state = runners.get(sessionId);
64
- if (state) {
65
- state.running = running;
66
- }
60
+ if (state) state.running = running;
67
61
  }
68
62
 
69
- /**
70
- * Dequeues the next job from a session's queue.
71
- */
72
63
  export function dequeueJob(sessionId: string): RunOpts | undefined {
73
64
  const state = runners.get(sessionId);
74
65
  return state?.queue.shift();
75
66
  }
76
67
 
77
- /**
78
- * Cleanup abort controller for a session (called when queue is done).
79
- */
80
68
  export function cleanupSession(sessionId: string) {
81
- sessionAbortControllers.delete(sessionId);
69
+ const state = runners.get(sessionId);
70
+ if (state && state.queue.length === 0 && !state.running) {
71
+ runners.delete(sessionId);
72
+ // Clean up any lingering abort controller
73
+ sessionAbortControllers.delete(sessionId);
74
+ }
82
75
  }