@gajae-code/agent-core 0.11.0 → 0.11.2

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/src/proxy.ts CHANGED
@@ -30,6 +30,33 @@ class ProxyMessageEventStream extends EventStream<AssistantMessageEvent, Assista
30
30
  }
31
31
  }
32
32
 
33
+ interface ReasoningBuffers {
34
+ summary: string;
35
+ raw: string;
36
+ }
37
+
38
+ const reasoningBuffers = new WeakMap<object, ReasoningBuffers>();
39
+
40
+ function materializeReasoningProvenance(
41
+ content: Extract<AssistantMessage["content"][number], { type: "thinking" }>,
42
+ ): void {
43
+ const buffers = reasoningBuffers.get(content);
44
+ if (!buffers) return;
45
+ const mutable = content as { provenance?: "summary" | "raw" | "mixed"; summaryText?: string; rawText?: string };
46
+ if (mutable.provenance === undefined) {
47
+ if (mutable.summaryText === undefined && buffers.summary) mutable.summaryText = buffers.summary;
48
+ if (mutable.rawText === undefined && buffers.raw) mutable.rawText = buffers.raw;
49
+ mutable.provenance =
50
+ buffers.summary && buffers.raw ? "mixed" : buffers.summary ? "summary" : buffers.raw ? "raw" : undefined;
51
+ }
52
+ // Finalized display string must exclude raw CoT when a summary exists (parity with
53
+ // the Responses/Codex decoders): summary/mixed -> summary only; raw-only -> raw. The
54
+ // raw text stays available separately via rawText for explicit consumers.
55
+ const effSummary = mutable.summaryText ?? buffers.summary;
56
+ const effRaw = mutable.rawText ?? buffers.raw;
57
+ content.thinking = mutable.provenance === "raw" ? effRaw : effSummary || effRaw;
58
+ }
59
+
33
60
  /**
34
61
  * Proxy event types - server sends these with partial field stripped to reduce bandwidth.
35
62
  */
@@ -41,6 +68,9 @@ export type ProxyAssistantMessageEvent =
41
68
  | { type: "thinking_start"; contentIndex: number }
42
69
  | { type: "thinking_delta"; contentIndex: number; delta: string }
43
70
  | { type: "thinking_end"; contentIndex: number; contentSignature?: string }
71
+ | { type: "reasoning_summary_start"; contentIndex: number }
72
+ | { type: "reasoning_summary_delta"; contentIndex: number; delta: string }
73
+ | { type: "reasoning_summary_end"; contentIndex: number; content?: string }
44
74
  | { type: "toolcall_start"; contentIndex: number; id: string; toolName: string }
45
75
  | { type: "toolcall_delta"; contentIndex: number; delta: string }
46
76
  | { type: "toolcall_end"; contentIndex: number }
@@ -238,14 +268,22 @@ function processProxyEvent(
238
268
  throw new Error("Received text_end for non-text content");
239
269
  }
240
270
 
241
- case "thinking_start":
242
- partial.content[proxyEvent.contentIndex] = { type: "thinking", thinking: "" };
271
+ case "thinking_start": {
272
+ const content = { type: "thinking", thinking: "" } as Extract<
273
+ AssistantMessage["content"][number],
274
+ { type: "thinking" }
275
+ >;
276
+ partial.content[proxyEvent.contentIndex] = content;
277
+ reasoningBuffers.set(content, { summary: "", raw: "" });
243
278
  return { type: "thinking_start", contentIndex: proxyEvent.contentIndex, partial };
279
+ }
244
280
 
245
281
  case "thinking_delta": {
246
282
  const content = partial.content[proxyEvent.contentIndex];
247
283
  if (content?.type === "thinking") {
248
284
  content.thinking += proxyEvent.delta;
285
+ const buffers = reasoningBuffers.get(content);
286
+ if (buffers) buffers.raw += proxyEvent.delta;
249
287
  return {
250
288
  type: "thinking_delta",
251
289
  contentIndex: proxyEvent.contentIndex,
@@ -256,10 +294,52 @@ function processProxyEvent(
256
294
  throw new Error("Received thinking_delta for non-thinking content");
257
295
  }
258
296
 
297
+ case "reasoning_summary_start":
298
+ return { type: "reasoning_summary_start", contentIndex: proxyEvent.contentIndex, partial };
299
+
300
+ case "reasoning_summary_delta": {
301
+ const content = partial.content[proxyEvent.contentIndex];
302
+ if (content?.type === "thinking") {
303
+ content.thinking += proxyEvent.delta;
304
+ const buffers = reasoningBuffers.get(content);
305
+ if (buffers) buffers.summary += proxyEvent.delta;
306
+ return {
307
+ type: "reasoning_summary_delta",
308
+ contentIndex: proxyEvent.contentIndex,
309
+ delta: proxyEvent.delta,
310
+ partial,
311
+ };
312
+ }
313
+ throw new Error("Received reasoning_summary_delta for non-thinking content");
314
+ }
315
+
316
+ case "reasoning_summary_end": {
317
+ const content = partial.content[proxyEvent.contentIndex];
318
+ if (content?.type === "thinking") {
319
+ const buffers = reasoningBuffers.get(content);
320
+ // Final-only summaries arrive with the text on the end event and no summary
321
+ // deltas, so the accumulated buffer is empty. Prefer the end event's content
322
+ // so the summary survives the proxy and materializes as provenance "summary".
323
+ if (buffers && !buffers.summary.trim() && proxyEvent.content) {
324
+ buffers.summary = proxyEvent.content;
325
+ if (!content.thinking.trim()) content.thinking = proxyEvent.content;
326
+ }
327
+ materializeReasoningProvenance(content);
328
+ return {
329
+ type: "reasoning_summary_end",
330
+ contentIndex: proxyEvent.contentIndex,
331
+ content: buffers?.summary || proxyEvent.content || "",
332
+ partial,
333
+ };
334
+ }
335
+ throw new Error("Received reasoning_summary_end for non-thinking content");
336
+ }
337
+
259
338
  case "thinking_end": {
260
339
  const content = partial.content[proxyEvent.contentIndex];
261
340
  if (content?.type === "thinking") {
262
341
  content.thinkingSignature = proxyEvent.contentSignature;
342
+ materializeReasoningProvenance(content);
263
343
  return {
264
344
  type: "thinking_end",
265
345
  contentIndex: proxyEvent.contentIndex,
package/src/types.ts CHANGED
@@ -13,6 +13,7 @@ import type {
13
13
  Tool,
14
14
  ToolChoice,
15
15
  ToolResultMessage,
16
+ TransportFailureFacts,
16
17
  TSchema,
17
18
  } from "@gajae-code/ai";
18
19
  import type { AppendOnlyContextManager } from "./append-only-context";
@@ -58,6 +59,7 @@ export type ManagedAttemptContinuation = (ownership: ManagedAttemptContinuationO
58
59
  /** Decision returned by managed fallback policy for one provisional attempt. */
59
60
  export type ManagedAttemptDecision =
60
61
  | { type: "retry"; continuation: ManagedAttemptContinuation }
62
+ | { type: "maintenance"; continuation: ManagedAttemptContinuation }
61
63
  | { type: "terminal"; terminal: RunTerminalRequest };
62
64
 
63
65
  /** Structured result for one managed upstream invocation. */
@@ -67,9 +69,10 @@ export type ManagedAttemptOutcome =
67
69
  failure: {
68
70
  message: AssistantMessage;
69
71
  /** Exact provider transport facts, including retry headers, for fallback policy. */
70
- transportFailure?: import("@gajae-code/ai").TransportFailureFacts;
72
+ transportFailure?: TransportFailureFacts;
71
73
  };
72
74
  }
75
+ | { type: "context_overflow_discarded"; message: AssistantMessage }
73
76
  | { type: "run_terminal"; reason: "cancelled" | "error" | "exhausted" };
74
77
 
75
78
  export type ManagedAttemptOutcomeHandler = (