@oh-my-pi/pi-agent-core 14.5.0 → 14.5.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-agent-core",
4
- "version": "14.5.0",
4
+ "version": "14.5.2",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -35,13 +35,13 @@
35
35
  "fmt": "biome format --write ."
36
36
  },
37
37
  "dependencies": {
38
- "@oh-my-pi/pi-ai": "14.5.0",
39
- "@oh-my-pi/pi-natives": "14.5.0",
40
- "@oh-my-pi/pi-utils": "14.5.0"
38
+ "@oh-my-pi/pi-ai": "14.5.2",
39
+ "@oh-my-pi/pi-natives": "14.5.2",
40
+ "@oh-my-pi/pi-utils": "14.5.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@sinclair/typebox": "^0.34.49",
44
- "@types/bun": "^1.3.13"
44
+ "@types/bun": "^1.3"
45
45
  },
46
46
  "engines": {
47
47
  "bun": ">=1.3.7"
package/src/agent-loop.ts CHANGED
@@ -126,7 +126,7 @@ function normalizeMessagesForProvider(
126
126
 
127
127
  export const INTENT_FIELD = "_i";
128
128
 
129
- function injectIntentIntoSchema(schema: unknown): unknown {
129
+ function injectIntentIntoSchema(schema: unknown, mode: "require" | "optional" = "require"): unknown {
130
130
  if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
131
131
  const schemaRecord = schema as Record<string, unknown>;
132
132
  const propertiesValue = schemaRecord.properties;
@@ -141,7 +141,7 @@ function injectIntentIntoSchema(schema: unknown): unknown {
141
141
  if (INTENT_FIELD in properties) {
142
142
  const { [INTENT_FIELD]: intentProp, ...rest } = properties;
143
143
  const needsReorder = Object.keys(properties)[0] !== INTENT_FIELD;
144
- const needsRequired = !required.includes(INTENT_FIELD);
144
+ const needsRequired = mode === "require" && !required.includes(INTENT_FIELD);
145
145
  if (!needsReorder && !needsRequired) return schema;
146
146
  return {
147
147
  ...schemaRecord,
@@ -157,16 +157,27 @@ function injectIntentIntoSchema(schema: unknown): unknown {
157
157
  },
158
158
  ...properties,
159
159
  },
160
- required: [...required, INTENT_FIELD],
160
+ ...(mode === "require" ? { required: [...required, INTENT_FIELD] } : {}),
161
161
  };
162
162
  }
163
163
 
164
- function normalizeTools(tools: Context["tools"], injectIntent: boolean): Context["tools"] {
165
- return tools?.map(tool => ({
166
- ...tool,
167
- description: tool.description || "",
168
- ...(injectIntent && { parameters: injectIntentIntoSchema(tool.parameters) as typeof tool.parameters }),
169
- }));
164
+ function normalizeTools(tools: AgentContext["tools"], injectIntent: boolean): Context["tools"] {
165
+ injectIntent = injectIntent && Bun.env.PI_NO_INTENT !== "1";
166
+ return tools?.map(t => {
167
+ const intentMode = resolveIntentMode(t.intent);
168
+ const parameters =
169
+ injectIntent && intentMode !== "omit"
170
+ ? (injectIntentIntoSchema(t.parameters, intentMode) as typeof t.parameters)
171
+ : t.parameters;
172
+ const description = t.description ?? "";
173
+ return { ...t, parameters, description };
174
+ });
175
+ }
176
+
177
+ function resolveIntentMode(intent: AgentTool["intent"]): "require" | "optional" | "omit" {
178
+ if (typeof intent === "function") return "omit";
179
+ if (intent === "optional" || intent === "omit") return intent;
180
+ return "require";
170
181
  }
171
182
 
172
183
  function extractIntent(args: Record<string, unknown>): { intent?: string; strippedArgs: Record<string, unknown> } {
@@ -542,6 +553,15 @@ async function executeToolCalls(
542
553
  argsForExecution = strippedArgs;
543
554
  if (intent) {
544
555
  toolCall.intent = intent;
556
+ } else if (typeof tool?.intent === "function") {
557
+ try {
558
+ const derived = tool.intent(strippedArgs as never)?.trim();
559
+ if (derived) {
560
+ toolCall.intent = derived;
561
+ }
562
+ } catch {
563
+ // intent function must never break tool execution
564
+ }
545
565
  }
546
566
  }
547
567
  record.args = argsForExecution;
package/src/types.ts CHANGED
@@ -250,6 +250,16 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
250
250
  concurrency?: "shared" | "exclusive";
251
251
  /** If true, argument validation errors are non-fatal: raw args are passed to execute() instead of returning an error to the LLM. */
252
252
  lenientArgValidation?: boolean;
253
+ /**
254
+ * Controls how the INTENT_FIELD (`_i`) is handled for this tool.
255
+ * - `"require"` (default): `_i` is injected and required in the parameter schema.
256
+ * - `"optional"`: `_i` is injected as an optional/nullable field.
257
+ * - `"omit"`: `_i` is NOT injected. Use for tools where intent is obvious (yield, resolve, todo_write, …).
258
+ * - function: `_i` is NOT injected; intent is derived dynamically from (potentially partial / streaming) args.
259
+ */
260
+ intent?: "omit" | "optional" | "require" | ((args: Partial<Static<TParameters>>) => string | undefined);
261
+
262
+ /** The main execution callback for this tool. */
253
263
  execute: AgentToolExecFn<TParameters, TDetails, TTheme>;
254
264
 
255
265
  /** Optional custom rendering for tool call display (returns UI component) */