@pasko70/pibo 2.4.1 → 2.4.3

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.
Files changed (46) hide show
  1. package/dist/agent-runtime/context-build.js +100 -11
  2. package/dist/agent-runtime/profile-validation.js +3 -0
  3. package/dist/agent-runtime/resource-service.js +16 -0
  4. package/dist/agent-runtime/routed-session.js +180 -30
  5. package/dist/agent-runtime/testing/fake-adapter.js +7 -0
  6. package/dist/agent-runtimes/pi/adapter.js +2 -0
  7. package/dist/agent-runtimes/pi/routed-session.js +104 -43
  8. package/dist/agent-runtimes/pi/runtime.js +8 -5
  9. package/dist/apps/chat/web-app.js +21 -6
  10. package/dist/apps/chat-ui/assets/{dist-Cw9po47P.js → dist-C4JGcQjh.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-BeqHbnGN.js → dist-CF92Lv76.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{dist-DTRjeLwO.js → dist-CJ0JS-bE.js} +1 -1
  13. package/dist/apps/chat-ui/assets/{dist-3YG57JXi.js → dist-DrgKyb9n.js} +1 -1
  14. package/dist/apps/chat-ui/assets/{dist-CrDtveZB.js → dist-VoMyV-PE.js} +1 -1
  15. package/dist/apps/chat-ui/assets/index-0WZI2phJ.css +1 -0
  16. package/dist/apps/chat-ui/assets/index-DhX1_aRM.js +228 -0
  17. package/dist/apps/chat-ui/index.html +2 -2
  18. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  19. package/dist/apps/vscode-artifacts/{pibo-vscode-ext-2.4.1.vsix → pibo-vscode-ext-2.4.3.vsix} +0 -0
  20. package/dist/cli.js +16 -6
  21. package/dist/core/context-build.js +66 -6
  22. package/dist/core/model-defaults.js +11 -3
  23. package/dist/core/session-router.js +285 -112
  24. package/dist/gateway/server.js +1 -0
  25. package/dist/loops/accounting.js +80 -0
  26. package/dist/loops/service.js +52 -13
  27. package/dist/loops/store.js +67 -19
  28. package/dist/loops/tools.js +3 -1
  29. package/dist/reliability/store.js +11 -6
  30. package/dist/runs/lifecycle.js +38 -1
  31. package/dist/runs/registry.js +46 -45
  32. package/dist/runs/tools.js +34 -24
  33. package/dist/subagents/context.js +48 -0
  34. package/dist/subagents/runtime-selection.js +28 -0
  35. package/dist/subagents/tool.js +28 -6
  36. package/dist/tools/codex-compat.js +1 -0
  37. package/dist/tools/contract.js +10 -0
  38. package/dist/tools/mcp-bridge.js +4 -2
  39. package/dist/tools/runtime/node-backend.js +8 -2
  40. package/dist/tools/runtime/python-backend.js +8 -2
  41. package/dist/tools/runtime/tool.js +1 -0
  42. package/dist/tools/session-tool-set.js +19 -12
  43. package/npm-shrinkwrap.json +2 -2
  44. package/package.json +1 -1
  45. package/dist/apps/chat-ui/assets/index-AjnP3ci-.js +0 -228
  46. package/dist/apps/chat-ui/assets/index-BJ56TREg.css +0 -1
@@ -1,8 +1,17 @@
1
1
  import { Type } from "typebox";
2
2
  import { piboStringEnum } from "../tools/schema.js";
3
- import { definePiboTool } from "../tools/contract.js";
4
- import { foregroundServiceWarning, hasMeaningfulTimeoutOutput, isConfiguredTimeoutError, PiboRunExecutionTimeoutError, resolveRunTimeoutMs } from "./lifecycle.js";
3
+ import { definePiboTool, piboToolTerminalStatus, piboToolTimeoutPhase } from "../tools/contract.js";
4
+ import { foregroundServiceWarning, hasMeaningfulTimeoutOutput, isConfiguredTimeoutError, PiboRunCancellationError, PiboRunCancelledError, PiboRunExecutionTimeoutError, resolveRunTimeoutMs, waitForRunCancellationSettlement } from "./lifecycle.js";
5
5
  import { PiboRunResourceLimitError, prepareYieldedRunExecution } from "./resource-isolation.js";
6
+ export const PIBO_RUN_TOOL_NAMES = [
7
+ "pibo_run_start",
8
+ "pibo_run_list",
9
+ "pibo_run_status",
10
+ "pibo_run_wait",
11
+ "pibo_run_read",
12
+ "pibo_run_cancel",
13
+ "pibo_run_ack",
14
+ ];
6
15
  function resultText(prefix, value) {
7
16
  return `${prefix}\n${JSON.stringify(value, null, 2)}`;
8
17
  }
@@ -27,22 +36,6 @@ function requireTool(tools, name) {
27
36
  }
28
37
  return tool;
29
38
  }
30
- async function waitForRunCancellationSettlement(settled, timeoutMs = 15_000) {
31
- let timer;
32
- try {
33
- await Promise.race([
34
- settled,
35
- new Promise((_resolve, reject) => {
36
- timer = setTimeout(() => reject(new Error(`Yielded run did not settle within ${timeoutMs}ms after cancellation.`)), timeoutMs);
37
- timer.unref?.();
38
- }),
39
- ]);
40
- }
41
- finally {
42
- if (timer)
43
- clearTimeout(timer);
44
- }
45
- }
46
39
  export function createRunToolDefinitions(yieldableTools, controller) {
47
40
  const toolNames = yieldableTools.map((tool) => tool.name);
48
41
  return [
@@ -73,6 +66,7 @@ export function createRunToolDefinitions(yieldableTools, controller) {
73
66
  resolveExecutionSettled = resolve;
74
67
  });
75
68
  let observedOutput = false;
69
+ let cancellationFailure;
76
70
  const run = controller.startToolRun({
77
71
  toolName: tool.name,
78
72
  params: params.arguments,
@@ -95,26 +89,38 @@ export function createRunToolDefinitions(yieldableTools, controller) {
95
89
  throw processCancellationError;
96
90
  if (executionStarted)
97
91
  await waitForRunCancellationSettlement(executionSettled);
92
+ if (cancellationFailure)
93
+ throw cancellationFailure;
98
94
  },
99
- async execute() {
95
+ async execute(runId) {
100
96
  executionStarted = true;
101
97
  try {
102
98
  const result = await prepared.execute(() => tool.execute(toolCallId, prepared.params, runSignal, (update) => {
103
99
  observedOutput ||= hasMeaningfulTimeoutOutput(update);
104
100
  onUpdate?.(update);
105
- }, ctx));
101
+ }, { ...ctx, yieldedRunId: runId }));
106
102
  const resultObject = result;
107
103
  const text = textFromToolResult(resultObject);
108
104
  if (resultObject.isError === true) {
109
- if (timeoutMs !== undefined && isConfiguredTimeoutError(text ?? ""))
105
+ const structuredTimeout = piboToolTerminalStatus(resultObject) === "timed_out";
106
+ if (structuredTimeout) {
107
+ throw new PiboRunExecutionTimeoutError(text ?? `${tool.name} timed out.`, piboToolTimeoutPhase(resultObject) ?? (observedOutput || hasMeaningfulTimeoutOutput(text) ? "lifetime" : "startup"));
108
+ }
109
+ if (timeoutMs !== undefined && isConfiguredTimeoutError(text ?? "")) {
110
110
  throw new PiboRunExecutionTimeoutError(text ?? `${tool.name} timed out.`, observedOutput || hasMeaningfulTimeoutOutput(text) ? "lifetime" : "startup");
111
+ }
111
112
  throw new Error(text ?? `${tool.name} returned an error result.`);
112
113
  }
113
114
  return { text, details: resultObject.details ?? result };
114
115
  }
115
116
  catch (error) {
116
- if (error instanceof PiboRunExecutionTimeoutError || error instanceof PiboRunResourceLimitError)
117
+ if (error instanceof PiboRunCancellationError)
118
+ cancellationFailure = error;
119
+ if (error instanceof PiboRunExecutionTimeoutError || error instanceof PiboRunResourceLimitError || error instanceof PiboRunCancellationError)
117
120
  throw error;
121
+ if (runAbortController.signal.aborted) {
122
+ throw new PiboRunCancelledError("Yielded run was cancelled; execution ended after cancellation.", { cause: error });
123
+ }
118
124
  if (timeoutMs !== undefined && isConfiguredTimeoutError(error))
119
125
  throw new PiboRunExecutionTimeoutError(error instanceof Error ? error.message : String(error), observedOutput ? "lifetime" : "startup");
120
126
  throw error;
@@ -223,8 +229,11 @@ export function createRunToolDefinitions(yieldableTools, controller) {
223
229
  }),
224
230
  async execute(_toolCallId, params) {
225
231
  const run = await controller.cancelRun(params.runId);
232
+ const prefix = run.status === "cancelled"
233
+ ? `Cancelled run ${run.runId}.`
234
+ : `Run ${run.runId} reached ${run.status} before cancellation completed.`;
226
235
  return {
227
- content: [{ type: "text", text: resultText(`Cancelled run ${run.runId}.`, run) }],
236
+ content: [{ type: "text", text: resultText(prefix, run) }],
228
237
  details: run,
229
238
  };
230
239
  },
@@ -240,8 +249,9 @@ export function createRunToolDefinitions(yieldableTools, controller) {
240
249
  }),
241
250
  async execute(_toolCallId, params) {
242
251
  const run = controller.ackRun(params.runId);
252
+ const prefix = run.changed ? `Acknowledged run ${run.runId}.` : `Run ${run.runId} was already acknowledged in state ${run.status}; no state changed.`;
243
253
  return {
244
- content: [{ type: "text", text: resultText(`Acknowledged run ${run.runId}.`, run) }],
254
+ content: [{ type: "text", text: resultText(prefix, run) }],
245
255
  details: run,
246
256
  };
247
257
  },
@@ -0,0 +1,48 @@
1
+ import { listAvailableAgents } from "./tool.js";
2
+ export const PIBO_DELEGATED_AGENT_CONTEXT_PATH = "pibo://runtime/delegated-agents.md";
3
+ export function getDelegatedAgentContextFile(subagents) {
4
+ const agents = listAvailableAgents(subagents);
5
+ if (agents.length === 0)
6
+ return undefined;
7
+ const catalog = agents.map((agent) => {
8
+ const runtime = [
9
+ agent.model ? `${agent.model.provider}/${agent.model.id}` : undefined,
10
+ agent.thinkingLevel ? `thinking ${agent.thinkingLevel}` : undefined,
11
+ ].filter(Boolean).join(", ");
12
+ return `- \`${agent.name}\` → \`${agent.profile}\`${runtime ? ` (${runtime})` : ""}: ${agent.description}`;
13
+ }).join("\n");
14
+ return {
15
+ path: PIBO_DELEGATED_AGENT_CONTEXT_PATH,
16
+ content: [
17
+ "# Delegated Agent Management",
18
+ "",
19
+ "This session has Pibo-managed delegated agents. Dispatch is yielded-only: never call `pibo_agents_send_message` directly. Start it through `pibo_run_start`, then manage the returned run ID.",
20
+ "",
21
+ "## Available agents",
22
+ "",
23
+ catalog,
24
+ "",
25
+ "## Required workflow",
26
+ "",
27
+ "```text",
28
+ "pibo_run_start({",
29
+ " toolName: \"pibo_agents_send_message\",",
30
+ " arguments: { name, message, threadKey? },",
31
+ " completionPolicy?: \"tracked\" | \"detached\"",
32
+ "}) -> { runId }",
33
+ "",
34
+ "pibo_run_wait({ runId, timeoutMs? }) # bounded wait only; expiry does not stop the child",
35
+ "pibo_run_status({ runId }) # compact lifecycle state",
36
+ "pibo_agents_observe({ requestIds?: [runId], agentIds?, names?, threadKeys?, kinds?, roles?, ... })",
37
+ "pibo_run_read({ runId }) # terminal result, including the complete final agent message",
38
+ "pibo_run_cancel({ runId }) # explicit request cancellation",
39
+ "pibo_agents_list_agents({}) # available definitions and persistent child instances",
40
+ "pibo_agents_kill({ agentId }) # terminate one persistent child session subtree",
41
+ "```",
42
+ "",
43
+ "Reuse a stable `threadKey` to continue the same child Pibo Session. A wait timeout is only an orchestrator wake-up. Observe progress and decide whether to continue waiting, steer through a new message after the current turn, cancel the request, or kill the child session.",
44
+ "",
45
+ "For substantial reports, ask the child to persist a Markdown artifact and include its path in the complete final message.",
46
+ ].join("\n"),
47
+ };
48
+ }
@@ -0,0 +1,28 @@
1
+ import { selectRequestedSubagentModelProfile, selectRequestedSubagentThinkingLevel, } from "../core/model-defaults.js";
2
+ export function resolvePiboSubagentRuntimeSelection(subagent, targetProfile, modelDefaults = {}) {
3
+ const effectiveModel = subagent.model ?? selectRequestedSubagentModelProfile(targetProfile, modelDefaults);
4
+ const effectiveThinkingLevel = subagent.thinkingLevel ?? selectRequestedSubagentThinkingLevel(targetProfile, modelDefaults);
5
+ return {
6
+ ...(subagent.model ? { configuredModel: { ...subagent.model } } : {}),
7
+ ...(effectiveModel ? { effectiveModel: { ...effectiveModel } } : {}),
8
+ ...(subagent.thinkingLevel ? { configuredThinkingLevel: subagent.thinkingLevel } : {}),
9
+ ...(effectiveThinkingLevel ? { effectiveThinkingLevel } : {}),
10
+ };
11
+ }
12
+ export function resolvePiboSubagentRuntimeSelections(subagents, targetProfileResolver, modelDefaults = {}) {
13
+ return subagents.map((subagent) => {
14
+ const targetProfile = targetProfileResolver?.(subagent.targetProfile);
15
+ const selection = targetProfile
16
+ ? resolvePiboSubagentRuntimeSelection(subagent, targetProfile, modelDefaults)
17
+ : {
18
+ ...(subagent.model ? { configuredModel: { ...subagent.model }, effectiveModel: { ...subagent.model } } : {}),
19
+ ...(subagent.thinkingLevel ? { configuredThinkingLevel: subagent.thinkingLevel, effectiveThinkingLevel: subagent.thinkingLevel } : {}),
20
+ };
21
+ return {
22
+ name: subagent.name,
23
+ targetProfile: subagent.targetProfile,
24
+ enabled: subagent.enabled !== false,
25
+ ...selection,
26
+ };
27
+ });
28
+ }
@@ -30,6 +30,13 @@ export function formatAvailableAgentsForPrompt(subagents) {
30
30
  function resultText(prefix, value) {
31
31
  return `${prefix}\n${JSON.stringify(value, null, 2)}`;
32
32
  }
33
+ function normalizeAgentSendMessageResult(result, fallbackRequestId) {
34
+ return {
35
+ ...result,
36
+ requestId: result.requestId?.trim() || fallbackRequestId,
37
+ finalMessage: typeof result.finalMessage === "string" ? result.finalMessage : result.reply.text,
38
+ };
39
+ }
33
40
  export function createAgentToolDefinitions(subagents, controller) {
34
41
  const enabled = subagents.filter((subagent) => subagent.enabled !== false);
35
42
  if (enabled.length === 0)
@@ -48,11 +55,11 @@ export function createAgentToolDefinitions(subagents, controller) {
48
55
  name: "pibo_agents_send_message",
49
56
  title: "Pibo Agents Send Message",
50
57
  description: [
51
- "Send a message to an available delegated agent. Foreground execution waits for the reply; use pibo_run_start for asynchronous delegation.",
58
+ "Yielded-only delegated send. Start this tool through pibo_run_start; bounded waits do not limit the child lifetime.",
52
59
  "Available agents:",
53
60
  catalog,
54
61
  ].join("\n"),
55
- promptSnippet: "Send work to an available delegated agent by name. Reuse threadKey to continue its child session. Use pibo_run_start with this tool for asynchronous work. The tool definition lists the available names and parent-visible descriptions.",
62
+ promptSnippet: "Start pibo_agents_send_message through pibo_run_start. Reuse threadKey to continue its child session, and use run wait/status/read/cancel plus agent observe for lifecycle control.",
56
63
  executionMode: "parallel",
57
64
  inputSchema: Type.Object({
58
65
  name: piboStringEnum(names, { description: "Available delegated agent name" }),
@@ -62,22 +69,35 @@ export function createAgentToolDefinitions(subagents, controller) {
62
69
  maxLength: 256,
63
70
  })),
64
71
  }),
65
- async execute(toolCallId, params, signal) {
72
+ async execute(toolCallId, params, signal, _onUpdate, context) {
66
73
  const subagent = byName.get(params.name);
67
74
  if (!subagent)
68
75
  throw new Error(`Unknown delegated agent "${params.name}"`);
69
- const result = await controller.sendMessage({
76
+ if (!context.yieldedRunId) {
77
+ throw new Error("pibo_agents_send_message is yielded-only. Start it through pibo_run_start.");
78
+ }
79
+ const result = normalizeAgentSendMessageResult(await controller.sendMessage({
70
80
  subagent,
71
81
  message: params.message,
72
82
  threadKey: params.threadKey,
73
83
  toolCallId,
84
+ requestId: context.yieldedRunId,
85
+ parentProvenance: context.getActiveMessage?.()?.provenance,
74
86
  signal,
75
- });
87
+ }), context.yieldedRunId);
76
88
  return {
77
89
  content: [{
78
90
  type: "text",
79
- text: `Agent ${result.name} (${result.agentId}, thread ${result.threadKey}) replied:\n${result.reply.text}`,
91
+ text: `Agent request ${result.requestId} completed (${result.name}, ${result.agentId}, thread ${result.threadKey}).\n\n${result.finalMessage}`,
80
92
  }],
93
+ structuredContent: {
94
+ status: "completed",
95
+ requestId: result.requestId,
96
+ agentId: result.agentId,
97
+ threadKey: result.threadKey,
98
+ eventId: result.eventId,
99
+ finalMessage: result.finalMessage,
100
+ },
81
101
  details: result,
82
102
  };
83
103
  },
@@ -106,11 +126,13 @@ export function createAgentToolDefinitions(subagents, controller) {
106
126
  executionMode: "parallel",
107
127
  annotations: { readOnly: true },
108
128
  inputSchema: Type.Object({
129
+ requestIds: Type.Optional(Type.Array(Type.String({ description: "Exact yielded run/request ID" }), { maxItems: 50 })),
109
130
  agentIds: Type.Optional(Type.Array(Type.String({ description: "Owned child agentId" }), { maxItems: 50 })),
110
131
  names: Type.Optional(Type.Array(piboStringEnum(names), { maxItems: 50 })),
111
132
  threadKeys: Type.Optional(Type.Array(Type.String(), { maxItems: 50 })),
112
133
  eventTypes: Type.Optional(Type.Array(Type.String({ description: "Exact Pibo output event type" }), { maxItems: 50 })),
113
134
  kinds: Type.Optional(Type.Array(piboStringEnum(["message", "thinking", "tool", "error", "lifecycle", "event"]), { maxItems: 6 })),
135
+ roles: Type.Optional(Type.Array(Type.String({ description: "Exact normalized role, for example assistant" }), { maxItems: 20 })),
114
136
  since: Type.Optional(Type.String({ description: "Inclusive ISO-8601 lower timestamp bound" })),
115
137
  until: Type.Optional(Type.String({ description: "Inclusive ISO-8601 upper timestamp bound" })),
116
138
  textContains: Type.Optional(Type.String({ description: "Case-insensitive substring match against normalized observation text" })),
@@ -4,6 +4,7 @@ import { extname, isAbsolute, resolve } from "node:path";
4
4
  import { Type } from "typebox";
5
5
  import { piboStringEnum } from "./schema.js";
6
6
  import { definePiboTool } from "./contract.js";
7
+ export const CODEX_COMPAT_TOOL_NAMES = ["apply_patch", "view_image"];
7
8
  function resolveCwd(baseCwd, workdir) {
8
9
  if (!workdir || workdir.trim().length === 0)
9
10
  return baseCwd;
@@ -1,3 +1,10 @@
1
+ export function piboToolTerminalStatus(result) {
2
+ return result.metadata?.piboTerminalStatus === "timed_out" ? "timed_out" : undefined;
3
+ }
4
+ export function piboToolTimeoutPhase(result) {
5
+ const phase = result.metadata?.piboTimeoutPhase;
6
+ return phase === "startup" || phase === "lifetime" ? phase : undefined;
7
+ }
1
8
  /** Identity helper that preserves schema-derived input types. */
2
9
  export function definePiboTool(definition) {
3
10
  definition.label ??= definition.title;
@@ -14,8 +21,11 @@ export function isPiboToolDefinition(value) {
14
21
  export function normalizePiboToolResult(result) {
15
22
  return {
16
23
  content: result.content.map((content) => ({ ...content })),
24
+ ...("structuredContent" in result && result.structuredContent !== undefined ? { structuredContent: result.structuredContent } : {}),
17
25
  ...(result.details !== undefined ? { details: result.details } : {}),
18
26
  ...(result.isError !== undefined ? { isError: result.isError } : {}),
27
+ ...("payloadRefs" in result && result.payloadRefs !== undefined ? { payloadRefs: [...result.payloadRefs] } : {}),
28
+ ...("metadata" in result && result.metadata !== undefined ? { metadata: { ...result.metadata } } : {}),
19
29
  };
20
30
  }
21
31
  /** Convert a legacy Pi-shaped definition without leaking Pi types into generic code. */
@@ -166,10 +166,12 @@ async function convertContent(items, options) {
166
166
  return { content, payloadRefs };
167
167
  }
168
168
  async function piboResultToMcp(result, options) {
169
- const converted = await convertContent(result.content, options);
169
+ const preserveCompleteRunRead = options.tool.name === "pibo_run_read";
170
+ const conversionOptions = preserveCompleteRunRead ? { ...options, writer: undefined } : options;
171
+ const converted = await convertContent(result.content, conversionOptions);
170
172
  const payloadRefs = [...new Set([...(result.payloadRefs ?? []), ...converted.payloadRefs])];
171
173
  let structuredContent = result.structuredContent ?? toJsonValue(result.details);
172
- if (structuredContent !== undefined && options.writer) {
174
+ if (structuredContent !== undefined && options.writer && !preserveCompleteRunRead) {
173
175
  const encoded = JSON.stringify(structuredContent);
174
176
  if (Buffer.byteLength(encoded, "utf8") > options.threshold) {
175
177
  const stored = await storeLargeContent({
@@ -68,8 +68,14 @@ export class NodeRuntimeBackend {
68
68
  static async start(baseCwd, input) {
69
69
  const target = input.target ?? {};
70
70
  const backend = new NodeRuntimeBackend(resolveCwd(baseCwd, target.cwd), target.executable ?? process.execPath, target.args ?? [], target.env);
71
- await backend.waitReady(input.timeoutMs ?? 10000);
72
- return backend;
71
+ try {
72
+ await backend.waitReady(input.timeoutMs ?? 10000);
73
+ return backend;
74
+ }
75
+ catch (error) {
76
+ await backend.close(true).catch(() => undefined);
77
+ throw error;
78
+ }
73
79
  }
74
80
  isAlive() {
75
81
  return this.alive && !this.child.killed;
@@ -68,8 +68,14 @@ export class PythonRuntimeBackend {
68
68
  static async start(baseCwd, input) {
69
69
  const target = input.target ?? {};
70
70
  const backend = new PythonRuntimeBackend(resolveCwd(baseCwd, target.cwd), target.executable ?? (process.platform === "win32" ? "python" : "python3"), target.args ?? [], target.env);
71
- await backend.waitReady(input.timeoutMs ?? 10000);
72
- return backend;
71
+ try {
72
+ await backend.waitReady(input.timeoutMs ?? 10000);
73
+ return backend;
74
+ }
75
+ catch (error) {
76
+ await backend.close(true).catch(() => undefined);
77
+ throw error;
78
+ }
73
79
  }
74
80
  isAlive() {
75
81
  return this.alive && !this.child.killed;
@@ -163,6 +163,7 @@ export function createRuntimeToolDefinition(controller) {
163
163
  content: [{ type: "text", text: formatRuntimeResult(result) }],
164
164
  details: result,
165
165
  isError: isErrorStatus(status),
166
+ ...(status === "timeout" ? { metadata: { piboTerminalStatus: "timed_out", piboTimeoutPhase: "startup" } } : {}),
166
167
  };
167
168
  },
168
169
  });
@@ -31,6 +31,12 @@ function getToolDefinition(tool, context = {}) {
31
31
  return tool.definition;
32
32
  return tool.createDefinition(context);
33
33
  }
34
+ export function materializePiboProfileTools(profile, context = {}) {
35
+ return profile.tools
36
+ .filter((tool) => !isRuntimeToolProfile(tool) && !isCodexBrowserToolProfile(tool))
37
+ .filter(hasEnabledToolDefinition)
38
+ .map((tool) => ({ profile: tool, definition: getToolDefinition(tool, context) }));
39
+ }
34
40
  /** Assemble the selected Pibo-managed tool set without importing any harness package. */
35
41
  export function createPiboSessionToolDefinitions(options) {
36
42
  const { profile } = options;
@@ -47,10 +53,8 @@ export function createPiboSessionToolDefinitions(options) {
47
53
  const codexBrowserTools = options.codexBrowserController
48
54
  ? createCodexBrowserToolDefinitions(options.codexBrowserController, selectedCodexBrowserToolNames)
49
55
  : [];
50
- const profileTools = profile.tools
51
- .filter((tool) => !isRuntimeToolProfile(tool) && !isCodexBrowserToolProfile(tool))
52
- .filter(hasEnabledToolDefinition);
53
- const profileToolDefinitions = profileTools.map((tool) => getToolDefinition(tool, options.toolContext));
56
+ const materializedProfileTools = materializePiboProfileTools(profile, options.toolContext);
57
+ const profileToolDefinitions = materializedProfileTools.map((tool) => tool.definition);
54
58
  const codexCompatTools = profile.toolPackages.codexCompat === true
55
59
  ? createCodexCompatToolDefinitions()
56
60
  : [];
@@ -60,28 +64,31 @@ export function createPiboSessionToolDefinitions(options) {
60
64
  const agentTools = options.agentsController
61
65
  ? createAgentToolDefinitions(profile.subagents, options.agentsController)
62
66
  : [];
67
+ const delegatedSendTool = agentTools.find((tool) => tool.name === "pibo_agents_send_message");
68
+ const directAgentTools = agentTools.filter((tool) => tool !== delegatedSendTool);
63
69
  const nativeYieldableTools = [...(options.nativeYieldableTools ?? [])];
64
70
  const yieldableTools = [
65
71
  ...nativeYieldableTools,
66
- ...profileTools
67
- .filter((tool) => tool.yieldable !== false)
68
- .map((tool) => getToolDefinition(tool, options.toolContext)),
72
+ ...materializedProfileTools
73
+ .filter((tool) => tool.profile.yieldable !== false)
74
+ .map((tool) => tool.definition),
69
75
  ...(runtimeTool && runtimeProfileTool?.yieldable !== false ? [runtimeTool] : []),
70
76
  ...codexBrowserTools.filter((definition) => profile.tools.find((tool) => tool.name === definition.name)?.yieldable !== false),
71
77
  ...agentTools,
72
78
  ...codexCompatTools,
73
79
  ];
74
- const runTools = profile.toolPackages.runControl === true
75
- && options.runToolController
76
- && yieldableTools.length > 0
77
- ? createRunToolDefinitions(yieldableTools, options.runToolController)
80
+ const runControlYieldableTools = profile.toolPackages.runControl === true
81
+ ? yieldableTools
82
+ : delegatedSendTool ? [delegatedSendTool] : [];
83
+ const runTools = options.runToolController && runControlYieldableTools.length > 0
84
+ ? createRunToolDefinitions(runControlYieldableTools, options.runToolController)
78
85
  : [];
79
86
  return [
80
87
  ...nativeYieldableTools,
81
88
  ...profileToolDefinitions,
82
89
  ...(runtimeTool ? [runtimeTool] : []),
83
90
  ...codexBrowserTools,
84
- ...agentTools,
91
+ ...directAgentTools,
85
92
  ...codexCompatTools,
86
93
  ...goalTools,
87
94
  ...runTools,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@pasko70/pibo",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@pasko70/pibo",
9
- "version": "2.4.1",
9
+ "version": "2.4.3",
10
10
  "workspaces": [
11
11
  "packages/workflows"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasko70/pibo",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  "packages/workflows"