@ottocode/server 0.1.227 → 0.1.228

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": "@ottocode/server",
3
- "version": "0.1.227",
3
+ "version": "0.1.228",
4
4
  "description": "HTTP API server for ottocode",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -49,8 +49,8 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@ottocode/sdk": "0.1.227",
53
- "@ottocode/database": "0.1.227",
52
+ "@ottocode/sdk": "0.1.228",
53
+ "@ottocode/database": "0.1.228",
54
54
  "drizzle-orm": "^0.44.5",
55
55
  "hono": "^4.9.9",
56
56
  "zod": "^4.3.6"
@@ -10,6 +10,7 @@ export type OauthCodexContinuationInput = {
10
10
  firstToolSeen: boolean;
11
11
  hasTrailingAssistantText: boolean;
12
12
  endedWithToolActivity?: boolean;
13
+ lastToolName?: string;
13
14
  droppedPseudoToolText: boolean;
14
15
  lastAssistantText: string;
15
16
  };
@@ -54,7 +55,7 @@ const MAX_UNCLEAN_EOF_RETRIES = 1;
54
55
 
55
56
  function isUncleanEof(input: OauthCodexContinuationInput): boolean {
56
57
  if (input.finishReason && input.finishReason !== 'unknown') return false;
57
- if (input.firstToolSeen) return true;
58
+ if (isMissingAssistantSummary(input)) return true;
58
59
  if (looksLikeIntermediateProgressText(input.lastAssistantText)) return true;
59
60
  return false;
60
61
  }
@@ -82,6 +83,10 @@ export function decideOauthCodexContinuation(
82
83
  return { shouldContinue: true, reason: 'truncated' };
83
84
  }
84
85
 
86
+ if (input.lastToolName === 'finish') {
87
+ return { shouldContinue: false };
88
+ }
89
+
85
90
  if (input.endedWithToolActivity) {
86
91
  return { shouldContinue: true, reason: 'ended-on-tool-activity' };
87
92
  }
@@ -182,12 +182,20 @@ async function runAssistant(opts: RunOpts) {
182
182
  let _finishObserved = false;
183
183
  let _toolActivityObserved = false;
184
184
  let _trailingAssistantTextAfterTool = false;
185
+ let _endedWithToolActivity = false;
186
+ let _lastToolName: string | undefined;
185
187
  let _abortedByUser = false;
186
188
  let titleGenerationTriggered = false;
187
189
  const unsubscribeFinish = subscribe(opts.sessionId, (evt) => {
188
190
  if (evt.type === 'tool.call' || evt.type === 'tool.result') {
189
191
  _toolActivityObserved = true;
190
192
  _trailingAssistantTextAfterTool = false;
193
+ _endedWithToolActivity = true;
194
+ try {
195
+ _lastToolName = (evt.payload as { name?: string } | undefined)?.name;
196
+ } catch {
197
+ _lastToolName = undefined;
198
+ }
191
199
  }
192
200
  if (evt.type === 'tool.call') {
193
201
  triggerTitleGenerationWhenReady();
@@ -327,6 +335,7 @@ async function runAssistant(opts: RunOpts) {
327
335
  (delta.trim().length > 0 && firstToolSeen())
328
336
  ) {
329
337
  _trailingAssistantTextAfterTool = true;
338
+ _endedWithToolActivity = false;
330
339
  }
331
340
 
332
341
  if (!currentPartId && !accumulated.trim()) {
@@ -443,8 +452,6 @@ async function runAssistant(opts: RunOpts) {
443
452
 
444
453
  const MAX_CONTINUATIONS = 6;
445
454
  const continuationCount = opts.continuationCount ?? 0;
446
- const endedWithToolActivity =
447
- _toolActivityObserved && !_trailingAssistantTextAfterTool;
448
455
  const continuationDecision = decideOauthCodexContinuation({
449
456
  provider: opts.provider,
450
457
  isOpenAIOAuth,
@@ -456,7 +463,8 @@ async function runAssistant(opts: RunOpts) {
456
463
  rawFinishReason: streamRawFinishReason,
457
464
  firstToolSeen: fs,
458
465
  hasTrailingAssistantText: _trailingAssistantTextAfterTool,
459
- endedWithToolActivity,
466
+ endedWithToolActivity: _endedWithToolActivity,
467
+ lastToolName: _lastToolName,
460
468
  droppedPseudoToolText: oauthTextGuard?.dropped ?? false,
461
469
  lastAssistantText: latestAssistantText,
462
470
  });