@oh-my-pi/pi-agent-core 18.14.0 → 18.14.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.14.1] - 2026-10-31
6
+
7
+ ### Breaking Changes
8
+
9
+ - `SentToolDefinitions.inactiveFor(messages, tools)` is replaced by `withInactiveTools(context)`, which also clears a stale `inactiveTools`; `transformProviderContext` now receives the request's `inactiveTools` ([#17492](https://github.com/can1357/oh-my-pi/pull/17492))
10
+
11
+ ### Fixed
12
+
13
+ - Long tool-call intents no longer block tool execution when intent tracing is enabled ([#13140](https://github.com/can1357/oh-my-pi/issues/13140)).
14
+
5
15
  ## [18.14.0] - 2026-10-29
6
16
 
7
17
  ### Breaking Changes
@@ -85,6 +85,7 @@ export declare function requestCompactionV2Streaming(model: Model, apiKey: strin
85
85
  providerSessionState?: Map<string, ProviderSessionState>;
86
86
  codexCompaction?: CodexCompactionContext;
87
87
  preferWebsockets?: boolean;
88
+ codexAccessPrograms?: boolean;
88
89
  }): Promise<CompactionV2Response>;
89
90
  /** Build Codex-style V2 replacement history from retention candidates plus compaction output. */
90
91
  export declare function buildCompactionV2ReplacementHistory(input: unknown[], compactionItem: Record<string, unknown>, retainedMessageBudget?: number): {
@@ -256,6 +256,8 @@ export interface SummaryOptions {
256
256
  providerSessionState?: Map<string, ProviderSessionState>;
257
257
  /** Whether Codex remote compaction should prefer the provider WebSocket transport. */
258
258
  preferWebsockets?: boolean;
259
+ /** Daybreak Blue request on Codex remote compaction; see `StreamOptions.codexAccessPrograms`. */
260
+ codexAccessPrograms?: boolean;
259
261
  /** Classification shared by every provider request in this logical compaction. */
260
262
  codexCompaction?: CodexCompactionContext;
261
263
  /** Provider-visible tools for remote compaction transports that replay native tool history. */
@@ -1,4 +1,4 @@
1
- import type { Message, Tool } from "@oh-my-pi/pi-ai";
1
+ import type { Context, Tool } from "@oh-my-pi/pi-ai";
2
2
  /**
3
3
  * Last wire definition this Agent sent for each tool name, so a provider that keeps
4
4
  * withdrawn tools declared (Anthropic `tool_removal`) can re-declare them byte-identically.
@@ -9,9 +9,10 @@ export declare class SentToolDefinitions {
9
9
  /** Remember the definitions a request is about to send. */
10
10
  record(tools: readonly Tool[]): void;
11
11
  /**
12
- * Definitions for names the latest `requestControls.tools.declared` in `messages` holds
13
- * that are not in `active`; undefined when none. Names never sent by this Agent are
14
- * skipped: the provider drops them from the declaration.
12
+ * `context` with {@link Context.inactiveTools} set to the definitions for names the latest
13
+ * `requestControls.tools.declared` in its messages holds that are not in its `tools`; cleared when
14
+ * there are none or the request declares no tools. Names never sent by this Agent are skipped: the
15
+ * provider drops them from the declaration.
15
16
  */
16
- inactiveFor(messages: readonly Message[], active: readonly Tool[]): Tool[] | undefined;
17
+ withInactiveTools(context: Context): Context;
17
18
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-agent-core",
4
- "version": "18.14.0",
4
+ "version": "18.14.1",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -38,16 +38,16 @@
38
38
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
39
39
  },
40
40
  "dependencies": {
41
- "@oh-my-pi/pi-ai": "18.14.0",
42
- "@oh-my-pi/pi-catalog": "18.14.0",
43
- "@oh-my-pi/pi-natives": "18.14.0",
44
- "@oh-my-pi/pi-utils": "18.14.0",
45
- "@oh-my-pi/pi-wire": "18.14.0",
46
- "@oh-my-pi/snapcompact": "18.14.0",
41
+ "@oh-my-pi/pi-ai": "18.14.1",
42
+ "@oh-my-pi/pi-catalog": "18.14.1",
43
+ "@oh-my-pi/pi-natives": "18.14.1",
44
+ "@oh-my-pi/pi-utils": "18.14.1",
45
+ "@oh-my-pi/pi-wire": "18.14.1",
46
+ "@oh-my-pi/snapcompact": "18.14.1",
47
47
  "@opentelemetry/api": "^1.9.1"
48
48
  },
49
49
  "devDependencies": {
50
- "@oh-my-pi/omptype": "18.14.0",
50
+ "@oh-my-pi/omptype": "18.14.1",
51
51
  "@opentelemetry/context-async-hooks": "^2.9.0",
52
52
  "@opentelemetry/sdk-trace-base": "^2.9.0",
53
53
  "@types/bun": "^1.3.14"
package/src/agent-loop.ts CHANGED
@@ -1071,12 +1071,6 @@ function resolveIntentMode(intent: AgentTool["intent"]): "require" | "optional"
1071
1071
  return "require";
1072
1072
  }
1073
1073
 
1074
- /**
1075
- * Longest `i` value accepted as an intent; anything past this is a tool
1076
- * payload the model put in the wrong field, not an intent.
1077
- */
1078
- const MAX_INTENT_LENGTH = 200;
1079
-
1080
1074
  function extractIntent(args: Record<string, unknown>): { intent?: string; strippedArgs: Record<string, unknown> } {
1081
1075
  const { [INTENT_FIELD]: intent, ...strippedArgs } = args;
1082
1076
  if (typeof intent !== "string") {
@@ -1976,6 +1970,9 @@ async function prepareProviderCall(
1976
1970
  }),
1977
1971
  };
1978
1972
  }
1973
+ // Before the transform too, so a request-size clamp in it measures the withdrawn definitions the provider
1974
+ // re-declares; recomputed below against what the transform returns.
1975
+ if (config.sentToolDefinitions) llmContext = config.sentToolDefinitions.withInactiveTools(llmContext);
1979
1976
  if (config.transformProviderContext) {
1980
1977
  llmContext = await config.transformProviderContext(llmContext, model);
1981
1978
  }
@@ -1991,10 +1988,9 @@ async function prepareProviderCall(
1991
1988
  };
1992
1989
  }
1993
1990
  // After `transformProviderContext`, so the recorded definitions are exactly what the provider receives.
1994
- if (config.sentToolDefinitions && llmContext.tools) {
1995
- config.sentToolDefinitions.record(llmContext.tools);
1996
- const inactiveTools = config.sentToolDefinitions.inactiveFor(llmContext.messages, llmContext.tools);
1997
- if (inactiveTools) llmContext = { ...llmContext, inactiveTools };
1991
+ if (config.sentToolDefinitions) {
1992
+ if (llmContext.tools) config.sentToolDefinitions.record(llmContext.tools);
1993
+ llmContext = config.sentToolDefinitions.withInactiveTools(llmContext);
1998
1994
  }
1999
1995
  return { model, context: llmContext, promptToolWireTools, ownedDialect };
2000
1996
  }
@@ -3105,11 +3101,6 @@ async function prepareToolCallDispatch(
3105
3101
  ? { intent: undefined, strippedArgs: argsForExecution }
3106
3102
  : extractIntent(toolCall.arguments);
3107
3103
  argsForExecution = strippedArgs;
3108
- if (intent !== undefined && intent.length > MAX_INTENT_LENGTH && tool) {
3109
- entry.args = strippedArgs;
3110
- entry.validationErrorMessage = `\`${INTENT_FIELD}\` is a short intent label (at most ${MAX_INTENT_LENGTH} chars); the value you sent is ${intent.length} chars. The tool was not run. Put that content in the tool's own parameters and retry with a brief \`${INTENT_FIELD}\`.`;
3111
- continue;
3112
- }
3113
3104
  if (intent) {
3114
3105
  toolCall.intent = intent;
3115
3106
  } else if (typeof tool?.intent === "function") {
package/src/agent.ts CHANGED
@@ -959,12 +959,11 @@ export class Agent {
959
959
  pruneDescriptions: this.#pruneToolDescriptions,
960
960
  }) ?? []);
961
961
  let context: Context = { systemPrompt, messages, tools };
962
+ // Side requests reuse the main loop's sent definitions without recording their own; attached before the
963
+ // transform too, so a request-size clamp in it measures them.
964
+ context = this.#sentToolDefinitions.withInactiveTools(context);
962
965
  if (this.#transformProviderContext) context = await this.#transformProviderContext(context, model);
963
- // Side requests reuse the main loop's sent definitions without recording their own.
964
- if (context.tools?.length) {
965
- const inactiveTools = this.#sentToolDefinitions.inactiveFor(context.messages, context.tools);
966
- if (inactiveTools) context = { ...context, inactiveTools };
967
- }
966
+ context = this.#sentToolDefinitions.withInactiveTools(context);
968
967
  return context;
969
968
  }
970
969
 
@@ -274,6 +274,7 @@ export async function requestCompactionV2Streaming(
274
274
  providerSessionState?: Map<string, ProviderSessionState>;
275
275
  codexCompaction?: CodexCompactionContext;
276
276
  preferWebsockets?: boolean;
277
+ codexAccessPrograms?: boolean;
277
278
  },
278
279
  ): Promise<CompactionV2Response> {
279
280
  let fetchImpl: FetchImpl = options?.fetch ?? globalThis.fetch;
@@ -316,6 +317,7 @@ export async function requestCompactionV2Streaming(
316
317
  providerSessionState: options?.providerSessionState,
317
318
  codexCompaction: options?.codexCompaction,
318
319
  preferWebsockets: options?.preferWebsockets,
320
+ codexAccessPrograms: options?.codexAccessPrograms,
319
321
  });
320
322
  } catch (err) {
321
323
  const error = err instanceof Error ? err : new Error(String(err));
@@ -352,6 +354,7 @@ async function attemptCompactionV2Streaming(
352
354
  providerSessionState?: Map<string, ProviderSessionState>;
353
355
  codexCompaction?: CodexCompactionContext;
354
356
  preferWebsockets?: boolean;
357
+ codexAccessPrograms?: boolean;
355
358
  },
356
359
  ): Promise<CompactionV2Response> {
357
360
  // Faithful to Codex: append the compaction trigger as the final input item
@@ -377,6 +380,7 @@ async function attemptCompactionV2Streaming(
377
380
  sessionId: request.sessionId,
378
381
  providerSessionState: options.providerSessionState,
379
382
  preferWebsockets: options.preferWebsockets,
383
+ codexAccessPrograms: options.codexAccessPrograms,
380
384
  responsesLite: model.useResponsesLite,
381
385
  codexCompaction: createOpenAICodexCompactionRequestContext({
382
386
  context: options.codexCompaction,
@@ -750,6 +750,8 @@ export interface SummaryOptions {
750
750
  providerSessionState?: Map<string, ProviderSessionState>;
751
751
  /** Whether Codex remote compaction should prefer the provider WebSocket transport. */
752
752
  preferWebsockets?: boolean;
753
+ /** Daybreak Blue request on Codex remote compaction; see `StreamOptions.codexAccessPrograms`. */
754
+ codexAccessPrograms?: boolean;
753
755
  /** Classification shared by every provider request in this logical compaction. */
754
756
  codexCompaction?: CodexCompactionContext;
755
757
  /** Provider-visible tools for remote compaction transports that replay native tool history. */
@@ -1765,6 +1767,7 @@ export async function compact(
1765
1767
  : options?.promptCacheKey,
1766
1768
  providerSessionState: options?.providerSessionState,
1767
1769
  preferWebsockets: options?.preferWebsockets,
1770
+ codexAccessPrograms: options?.codexAccessPrograms,
1768
1771
  codexCompaction: options?.codexCompaction,
1769
1772
  tools: options?.tools,
1770
1773
  fetch: options?.fetch,
@@ -2000,6 +2003,7 @@ export async function compact(
2000
2003
  fetch: summaryOptions.fetch,
2001
2004
  providerSessionState: summaryOptions.providerSessionState,
2002
2005
  preferWebsockets: summaryOptions.preferWebsockets,
2006
+ codexAccessPrograms: summaryOptions.codexAccessPrograms,
2003
2007
  codexCompaction: summaryOptions.codexCompaction,
2004
2008
  }),
2005
2009
  { signal },
@@ -1,4 +1,4 @@
1
- import type { Message, Tool } from "@oh-my-pi/pi-ai";
1
+ import type { Context, Message, Tool } from "@oh-my-pi/pi-ai";
2
2
 
3
3
  /**
4
4
  * Last wire definition this Agent sent for each tool name, so a provider that keeps
@@ -14,11 +14,18 @@ export class SentToolDefinitions {
14
14
  }
15
15
 
16
16
  /**
17
- * Definitions for names the latest `requestControls.tools.declared` in `messages` holds
18
- * that are not in `active`; undefined when none. Names never sent by this Agent are
19
- * skipped: the provider drops them from the declaration.
17
+ * `context` with {@link Context.inactiveTools} set to the definitions for names the latest
18
+ * `requestControls.tools.declared` in its messages holds that are not in its `tools`; cleared when
19
+ * there are none or the request declares no tools. Names never sent by this Agent are skipped: the
20
+ * provider drops them from the declaration.
20
21
  */
21
- inactiveFor(messages: readonly Message[], active: readonly Tool[]): Tool[] | undefined {
22
+ withInactiveTools(context: Context): Context {
23
+ const inactiveTools = context.tools?.length ? this.#inactiveFor(context.messages, context.tools) : undefined;
24
+ if (!inactiveTools && !context.inactiveTools) return context;
25
+ return { ...context, inactiveTools };
26
+ }
27
+
28
+ #inactiveFor(messages: readonly Message[], active: readonly Tool[]): Tool[] | undefined {
22
29
  let declared: readonly string[] | undefined;
23
30
  for (let index = messages.length - 1; index >= 0; index--) {
24
31
  const message = messages[index];