@pasko70/pibo 2.2.4 → 2.3.0

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 (61) hide show
  1. package/dist/agent-runtime/capabilities.js +9 -1
  2. package/dist/agent-runtime/context-build.js +5 -2
  3. package/dist/agent-runtimes/codex-native/adapter.js +5 -0
  4. package/dist/agent-runtimes/omp/adapter.js +5 -0
  5. package/dist/agent-runtimes/omp/turn.js +2 -0
  6. package/dist/agent-runtimes/pi/adapter.js +28 -7
  7. package/dist/agent-runtimes/pi/intent-tracing.js +120 -0
  8. package/dist/agent-runtimes/pi/routed-session.js +25 -8
  9. package/dist/agent-runtimes/pi/runtime.js +28 -16
  10. package/dist/apps/chat/chat-settings-routes.js +44 -3
  11. package/dist/apps/chat/chat-transcription.js +85 -0
  12. package/dist/apps/chat/data/chat-data-mappers.js +4 -4
  13. package/dist/apps/chat/stream.js +23 -9
  14. package/dist/apps/chat/trace-v2.js +1 -0
  15. package/dist/apps/chat/web-app.js +49 -0
  16. package/dist/apps/chat-ui/assets/{dist-luJVFsmq.js → dist-Byygd1lH.js} +1 -1
  17. package/dist/apps/chat-ui/assets/{dist-ltfz1vFX.js → dist-C9BrS7sL.js} +1 -1
  18. package/dist/apps/chat-ui/assets/{dist-rf4HYdoa.js → dist-CUcAofmV.js} +1 -1
  19. package/dist/apps/chat-ui/assets/{dist-CImWCF2M.js → dist-D4RU6xu3.js} +1 -1
  20. package/dist/apps/chat-ui/assets/{dist-BFwTuvyi.js → dist-DusFwy0L.js} +1 -1
  21. package/dist/apps/chat-ui/assets/{index-nZQXUpxE.css → index-BJ56TREg.css} +1 -1
  22. package/dist/apps/chat-ui/assets/index-Bifi_kjN.js +228 -0
  23. package/dist/apps/chat-ui/index.html +2 -2
  24. package/dist/apps/chat-vscode-web/assets/index-WsLm1mo3.js +43 -0
  25. package/dist/apps/chat-vscode-web/index.html +1 -1
  26. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  27. package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.3.0.vsix +0 -0
  28. package/dist/core/codex-compat.js +1 -3
  29. package/dist/core/context-build.js +16 -7
  30. package/dist/core/gateway-resource-guard.js +35 -14
  31. package/dist/core/gateway-settings.js +64 -0
  32. package/dist/core/session-router.js +226 -46
  33. package/dist/core/user-settings.js +20 -0
  34. package/dist/data/ingest-service.js +14 -2
  35. package/dist/debug/agents.js +391 -0
  36. package/dist/debug/index.js +14 -2
  37. package/dist/gateway/server.js +2 -0
  38. package/dist/index.js +6 -1
  39. package/dist/plugins/builtin.js +4 -2
  40. package/dist/plugins/openai-chatgpt-transcription.js +12 -0
  41. package/dist/plugins/openai-transcription.js +12 -0
  42. package/dist/plugins/registry.js +23 -0
  43. package/dist/session-ui/delegation.js +4 -2
  44. package/dist/session-ui/terminalRows.js +51 -1
  45. package/dist/shared/trace-async-agent-runs.js +4 -2
  46. package/dist/shared/trace-event-projection.js +12 -2
  47. package/dist/shared/trace-live-reducer.js +4 -0
  48. package/dist/shared/trace-patch-nodes.js +1 -0
  49. package/dist/shared/trace-subagent-links.js +19 -6
  50. package/dist/subagents/observations.js +149 -0
  51. package/dist/subagents/tool.js +177 -46
  52. package/dist/tools/session-service.js +1 -0
  53. package/dist/tools/session-tool-set.js +9 -5
  54. package/dist/transcription/openai-chatgpt.js +148 -0
  55. package/dist/transcription/openai.js +72 -0
  56. package/dist/transcription/types.js +8 -0
  57. package/npm-shrinkwrap.json +2 -2
  58. package/package.json +1 -1
  59. package/dist/apps/chat-ui/assets/index-D2Maa2v1.js +0 -226
  60. package/dist/apps/chat-vscode-web/assets/index-BpurWVnX.js +0 -41
  61. package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.2.4.vsix +0 -0
@@ -18,7 +18,55 @@ export function buildCompactTerminalRows(traceView, options) {
18
18
  .filter((item) => item.node.type !== "agent.turn" && (options.showThinking || item.node.type !== "model.reasoning"));
19
19
  const candidates = syncThinkingToolRows(flatNodes.map((item) => createRowCandidate(item.node, item.turnId)));
20
20
  applyCompletedTurnTiming(candidates, turnById);
21
- return groupRelatedToolCandidates(reconcileConceptualRowCandidates(candidates)).map((candidate) => candidate.row);
21
+ const reconciled = reconcileConceptualRowCandidates(candidates);
22
+ const rows = (options.toolDisplayMode ?? "default") === "default"
23
+ ? groupRelatedToolCandidates(reconciled).map((candidate) => candidate.row)
24
+ : reconciled.map((candidate) => candidate.row);
25
+ return applyToolDisplayMode(rows, options.toolDisplayMode ?? "default");
26
+ }
27
+ function applyToolDisplayMode(rows, mode) {
28
+ if (mode === "default")
29
+ return rows;
30
+ if (mode === "hide")
31
+ return rows.filter((row) => !isToolDisplayRow(row));
32
+ return rows.flatMap((row) => {
33
+ if (!isToolDisplayRow(row))
34
+ return [row];
35
+ const intent = row.intent?.trim();
36
+ if (mode === "intent" && !intent)
37
+ return [];
38
+ const slimRow = {
39
+ ...row,
40
+ lines: row.lines.slice(0, 1),
41
+ input: undefined,
42
+ output: undefined,
43
+ error: undefined,
44
+ markdown: undefined,
45
+ expandable: false,
46
+ singleLine: true,
47
+ previewOmission: undefined,
48
+ detailItems: undefined,
49
+ };
50
+ if (mode !== "intent")
51
+ return [slimRow];
52
+ return [{
53
+ ...slimRow,
54
+ title: undefined,
55
+ summary: undefined,
56
+ lines: [{
57
+ prefix: "bullet",
58
+ tokens: [token(intent, row.status === "error" ? "red" : row.status === "done" ? "green" : "cyan", "semibold")],
59
+ }],
60
+ }];
61
+ });
62
+ }
63
+ function isToolDisplayRow(row) {
64
+ return row.id.startsWith("terminal:tool:")
65
+ || row.kind === "tool.call"
66
+ || row.kind === "tool.image"
67
+ || row.kind === "tool.group.exploring"
68
+ || row.kind === "tool.group.images"
69
+ || row.kind === "agent.delegation";
22
70
  }
23
71
  export function findActiveTurnStartedAt(traceView) {
24
72
  if (!traceView)
@@ -112,6 +160,7 @@ function createRowCandidate(node, turnId) {
112
160
  row: {
113
161
  ...candidate.row,
114
162
  id: compactTerminalRowIdentity(node),
163
+ intent: node.intent,
115
164
  ...debugFields(node),
116
165
  },
117
166
  };
@@ -164,6 +213,7 @@ function reconcileConceptualRowCandidates(candidates) {
164
213
  ...existing.row,
165
214
  ...candidate.row,
166
215
  sourceNodeIds: [...new Set([...existing.row.sourceNodeIds, ...candidate.row.sourceNodeIds])],
216
+ intent: candidate.row.intent ?? existing.row.intent,
167
217
  input: candidate.row.input ?? existing.row.input,
168
218
  output: candidate.row.output ?? existing.row.output,
169
219
  error: candidate.row.error ?? existing.row.error,
@@ -51,10 +51,12 @@ function createAsyncAgentRunNode(parent, piboSessionId, startedAt, delegation) {
51
51
  const toolName = stringValue(run?.toolName) ?? stringValue(input.toolName) ?? delegation?.title;
52
52
  if (!toolName || !isSubagentToolName(toolName))
53
53
  return undefined;
54
- const subagentName = stringValue(delegation?.summary) ?? subagentNameFromToolName(toolName);
54
+ const delegatedArguments = input.arguments;
55
+ const subagentName = stringValue(delegation?.summary)
56
+ ?? (isRecord(delegatedArguments) ? stringValue(delegatedArguments.name) : undefined)
57
+ ?? subagentNameFromToolName(toolName);
55
58
  const runId = stringValue(run?.runId);
56
59
  const runStatus = stringValue(run?.status);
57
- const delegatedArguments = input.arguments;
58
60
  const completionPolicy = stringValue(run?.completionPolicy) ?? stringValue(input.completionPolicy);
59
61
  return {
60
62
  id: `${parent.id}:async-agent`,
@@ -465,6 +465,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
465
465
  id: `tool:${event.toolCallId}`,
466
466
  parentId: turnParentId,
467
467
  toolCallId: event.toolCallId,
468
+ intent: event.intent,
468
469
  type: subagentTool ? "agent.delegation" : "tool.call",
469
470
  title: event.toolName,
470
471
  status: event.type === "tool_execution_finished"
@@ -685,6 +686,8 @@ function isNativeHistoryToolEchoEvent(event, coverage, sessionStatus) {
685
686
  event.type !== "tool_execution_updated" &&
686
687
  event.type !== "tool_execution_finished")
687
688
  return false;
689
+ if (typeof event.intent === "string" && event.intent.trim())
690
+ return false;
688
691
  return coverage.toolCallIds.has(event.toolCallId) || historyCoversEvent(event, coverage);
689
692
  }
690
693
  export function mergeMessageTurnTimings(...groups) {
@@ -917,6 +920,7 @@ function thinkingEventNodeId(event) {
917
920
  }
918
921
  function mergeToolEvent(target, update) {
919
922
  target.status = update.status;
923
+ target.intent = update.intent ?? target.intent;
920
924
  target.summary = update.summary ?? target.summary;
921
925
  target.input = mergeDelegationInput(target, update);
922
926
  target.output = update.output ?? target.output;
@@ -943,8 +947,14 @@ function findLegacySubagentLinkTarget(nodes, update) {
943
947
  }
944
948
  function delegationAgentName(node) {
945
949
  const input = isObjectRecord(node.input) ? node.input : undefined;
946
- const value = typeof input?.subagentName === "string" ? input.subagentName : node.summary ?? node.title;
947
- return typeof value === "string" ? value.replace(/^pibo_subagent_/, "").trim().toLowerCase() || undefined : undefined;
950
+ const value = typeof input?.name === "string"
951
+ ? input.name
952
+ : typeof input?.subagentName === "string"
953
+ ? input.subagentName
954
+ : node.summary ?? node.title;
955
+ return typeof value === "string"
956
+ ? value.replace(/^pibo_agents_send_message$/, "agent").replace(/^pibo_subagent_/, "").trim().toLowerCase() || undefined
957
+ : undefined;
948
958
  }
949
959
  function delegationThreadKey(value) {
950
960
  if (!isObjectRecord(value) || typeof value.threadKey !== "string")
@@ -80,6 +80,7 @@ function storedEventFromStreamEvent(event, piboSessionId, nextSequence, now) {
80
80
  toolCallId: event.toolCallId,
81
81
  toolName: event.toolName,
82
82
  args: event.args,
83
+ ...(event.intent ? { intent: event.intent } : {}),
83
84
  };
84
85
  return makeStored(event, piboSessionId, "tool_execution_started", payload, nextSequence, now);
85
86
  }
@@ -95,6 +96,7 @@ function storedEventFromStreamEvent(event, piboSessionId, nextSequence, now) {
95
96
  toolName: event.toolName,
96
97
  args: event.args,
97
98
  partialResult: event.partialResult,
99
+ ...(event.intent ? { intent: event.intent } : {}),
98
100
  }
99
101
  : {
100
102
  type: "tool_call",
@@ -104,6 +106,7 @@ function storedEventFromStreamEvent(event, piboSessionId, nextSequence, now) {
104
106
  toolName: event.toolName,
105
107
  args: event.args,
106
108
  argsComplete: Boolean(event.argsComplete),
109
+ ...(event.intent ? { intent: event.intent } : {}),
107
110
  };
108
111
  return makeStored(event, piboSessionId, sourceEventType, payload, nextSequence, now);
109
112
  }
@@ -116,6 +119,7 @@ function storedEventFromStreamEvent(event, piboSessionId, nextSequence, now) {
116
119
  toolName: event.toolName,
117
120
  result: event.result,
118
121
  isError: Boolean(event.isError),
122
+ ...(event.intent ? { intent: event.intent } : {}),
119
123
  };
120
124
  return makeStored(event, piboSessionId, "tool_execution_finished", payload, nextSequence, now);
121
125
  }
@@ -52,6 +52,7 @@ function traceNodeShallowEqual(left, right) {
52
52
  left.eventId === right.eventId &&
53
53
  left.toolCallId === right.toolCallId &&
54
54
  left.runId === right.runId &&
55
+ left.intent === right.intent &&
55
56
  left.type === right.type &&
56
57
  left.title === right.title &&
57
58
  left.status === right.status &&
@@ -22,9 +22,16 @@ export function mapTraceSubagentSessionLinks(events) {
22
22
  export function findLikelyTraceChildSession(piboSessionId, toolName, event, childByParent) {
23
23
  if (!isSubagentToolName(toolName))
24
24
  return undefined;
25
+ const agentName = toolEventAgentName(event);
25
26
  const candidates = childByParent
26
27
  .get(piboSessionId)
27
- ?.filter((session) => session.metadata?.subagentToolName === toolName) ?? [];
28
+ ?.filter((session) => {
29
+ if (toolName === "pibo_agents_send_message") {
30
+ return session.metadata?.subagentToolName === toolName
31
+ && (!agentName || session.metadata?.subagentName === agentName);
32
+ }
33
+ return session.metadata?.subagentToolName === toolName;
34
+ }) ?? [];
28
35
  const threadKey = toolEventThreadKey(event);
29
36
  if (threadKey) {
30
37
  return candidates.find((session) => session.metadata?.threadKey === threadKey)?.id;
@@ -32,15 +39,21 @@ export function findLikelyTraceChildSession(piboSessionId, toolName, event, chil
32
39
  return candidates.length === 1 ? candidates[0].id : undefined;
33
40
  }
34
41
  export function isSubagentToolName(name) {
35
- return name.startsWith("pibo_subagent_");
42
+ return name === "pibo_agents_send_message" || name.startsWith("pibo_subagent_");
36
43
  }
37
44
  export function subagentNameFromToolName(toolName) {
38
- return toolName.slice("pibo_subagent_".length);
45
+ return toolName === "pibo_agents_send_message" ? "agent" : toolName.slice("pibo_subagent_".length);
39
46
  }
40
- function toolEventThreadKey(event) {
41
- const args = "args" in event && event.args && typeof event.args === "object" && !Array.isArray(event.args)
47
+ function toolEventArguments(event) {
48
+ return "args" in event && event.args && typeof event.args === "object" && !Array.isArray(event.args)
42
49
  ? event.args
43
50
  : undefined;
44
- const threadKey = args && "threadKey" in args ? args.threadKey : undefined;
51
+ }
52
+ function toolEventAgentName(event) {
53
+ const name = toolEventArguments(event)?.name;
54
+ return typeof name === "string" && name.trim() ? name.trim() : undefined;
55
+ }
56
+ function toolEventThreadKey(event) {
57
+ const threadKey = toolEventArguments(event)?.threadKey;
45
58
  return typeof threadKey === "string" && threadKey.trim() ? threadKey.trim() : undefined;
46
59
  }
@@ -0,0 +1,149 @@
1
+ export const PIBO_AGENT_OBSERVATION_TEXT_MAX_BYTES = 4 * 1024;
2
+ export const PIBO_AGENT_OBSERVATION_DETAILS_MAX_BYTES = 32 * 1024;
3
+ export const PIBO_AGENT_OBSERVATION_DEFAULT_LIMIT = 50;
4
+ export const PIBO_AGENT_OBSERVATION_MAX_LIMIT = 200;
5
+ export function piboAgentObservationSourceFromEvent(event) {
6
+ const source = {
7
+ eventType: event.type,
8
+ fallbackText: "toolName" in event && typeof event.toolName === "string" ? event.toolName : event.type,
9
+ };
10
+ if ("source" in event && typeof event.source === "string")
11
+ source.source = event.source;
12
+ if ("text" in event)
13
+ source.text = event.text;
14
+ if (event.type === "session_error")
15
+ source.error = event.error;
16
+ if (event.type === "tool_call" || event.type === "tool_execution_started")
17
+ source.args = event.args;
18
+ if (event.type === "tool_execution_updated")
19
+ source.partialResult = event.partialResult;
20
+ if (event.type === "tool_execution_finished" || event.type === "execution_result" || event.type === "compaction_end")
21
+ source.result = event.result;
22
+ if (event.type === "execution_result")
23
+ source.action = event.action;
24
+ if (event.type === "compaction_start" || event.type === "compaction_end")
25
+ source.reason = event.reason;
26
+ if (event.type === "subagent_session")
27
+ source.subagentName = event.subagentName;
28
+ return source;
29
+ }
30
+ export function piboAgentObservationKind(eventType) {
31
+ if (["message_queued", "message_steered", "message_started", "assistant_delta", "assistant_message", "message_finished"].includes(eventType))
32
+ return "message";
33
+ if (eventType.startsWith("thinking_"))
34
+ return "thinking";
35
+ if (eventType.startsWith("tool_") || eventType === "subagent_session")
36
+ return "tool";
37
+ if (eventType === "session_error")
38
+ return "error";
39
+ if (eventType === "execution_result" || eventType.startsWith("compaction_"))
40
+ return "lifecycle";
41
+ return "event";
42
+ }
43
+ export function piboAgentObservationRole(source) {
44
+ const eventType = source.eventType;
45
+ if (eventType === "assistant_message" || eventType === "assistant_delta" || eventType.startsWith("thinking_"))
46
+ return "assistant";
47
+ if (eventType === "message_queued" || eventType === "message_steered" || eventType === "message_started" || eventType === "message_finished")
48
+ return source.source ?? "actor";
49
+ if (eventType.startsWith("tool_"))
50
+ return "tool";
51
+ if (eventType === "subagent_session")
52
+ return "agent";
53
+ if (eventType === "session_error" || eventType === "execution_result" || eventType.startsWith("compaction_"))
54
+ return "system";
55
+ return undefined;
56
+ }
57
+ export function piboAgentObservationText(source) {
58
+ if (typeof source.text === "string")
59
+ return boundPiboAgentObservationText(source.text);
60
+ if (typeof source.error === "string")
61
+ return boundPiboAgentObservationText(source.error);
62
+ if (source.eventType === "tool_call" || source.eventType === "tool_execution_started") {
63
+ return stringifyPiboAgentObservationValue(source.args);
64
+ }
65
+ if (source.eventType === "tool_execution_updated")
66
+ return stringifyPiboAgentObservationValue(source.partialResult);
67
+ if (source.eventType === "tool_execution_finished")
68
+ return stringifyPiboAgentObservationValue(source.result);
69
+ if (source.eventType === "subagent_session" && typeof source.subagentName === "string") {
70
+ return boundPiboAgentObservationText(source.subagentName);
71
+ }
72
+ if (source.eventType === "execution_result") {
73
+ return stringifyPiboAgentObservationValue(source.result) ?? stringifyPiboAgentObservationValue(source.action);
74
+ }
75
+ if ((source.eventType === "compaction_start" || source.eventType === "compaction_end") && typeof source.reason === "string") {
76
+ return boundPiboAgentObservationText(source.reason);
77
+ }
78
+ return stringifyPiboAgentObservationValue(source.fallbackText);
79
+ }
80
+ export function boundPiboAgentObservationText(value) {
81
+ if (Buffer.byteLength(value, "utf8") <= PIBO_AGENT_OBSERVATION_TEXT_MAX_BYTES)
82
+ return value;
83
+ const suffix = "…";
84
+ let end = Math.min(value.length, PIBO_AGENT_OBSERVATION_TEXT_MAX_BYTES);
85
+ while (end > 0 && Buffer.byteLength(`${value.slice(0, end)}${suffix}`, "utf8") > PIBO_AGENT_OBSERVATION_TEXT_MAX_BYTES)
86
+ end -= 1;
87
+ return `${value.slice(0, end)}${suffix}`;
88
+ }
89
+ export function stringifyPiboAgentObservationValue(value) {
90
+ if (typeof value === "string")
91
+ return boundPiboAgentObservationText(value);
92
+ if (value === undefined)
93
+ return undefined;
94
+ try {
95
+ return boundPiboAgentObservationText(JSON.stringify(value));
96
+ }
97
+ catch {
98
+ return boundPiboAgentObservationText(String(value));
99
+ }
100
+ }
101
+ export function piboAgentObservationDetails(value) {
102
+ let serialized;
103
+ try {
104
+ serialized = JSON.stringify(value) ?? "null";
105
+ }
106
+ catch {
107
+ return { truncated: true, preview: boundPiboAgentObservationText(String(value)) };
108
+ }
109
+ if (Buffer.byteLength(serialized, "utf8") <= PIBO_AGENT_OBSERVATION_DETAILS_MAX_BYTES) {
110
+ return JSON.parse(serialized);
111
+ }
112
+ return {
113
+ truncated: true,
114
+ preview: boundPiboAgentObservationText(serialized),
115
+ };
116
+ }
117
+ export function parsePiboAgentObservationTimestamp(value, label) {
118
+ if (value === undefined)
119
+ return undefined;
120
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/.test(value)) {
121
+ throw new Error(`Agent observation ${label} must be a valid ISO-8601 timestamp.`);
122
+ }
123
+ const timestamp = Date.parse(value);
124
+ if (!Number.isFinite(timestamp))
125
+ throw new Error(`Agent observation ${label} must be a valid ISO-8601 timestamp.`);
126
+ return timestamp;
127
+ }
128
+ export function normalizePiboAgentObservationOrder(value) {
129
+ if (value === undefined)
130
+ return "asc";
131
+ if (value !== "asc" && value !== "desc")
132
+ throw new Error(`Agent observation order must be "asc" or "desc".`);
133
+ return value;
134
+ }
135
+ export function normalizePiboAgentObservationLimit(value) {
136
+ if (value === undefined)
137
+ return PIBO_AGENT_OBSERVATION_DEFAULT_LIMIT;
138
+ if (!Number.isInteger(value) || value < 1 || value > PIBO_AGENT_OBSERVATION_MAX_LIMIT) {
139
+ throw new Error(`Agent observation limit must be an integer from 1 to ${PIBO_AGENT_OBSERVATION_MAX_LIMIT}.`);
140
+ }
141
+ return value;
142
+ }
143
+ export function normalizePiboAgentObservationCursor(value) {
144
+ if (value === undefined)
145
+ return undefined;
146
+ if (!Number.isInteger(value) || value < 0)
147
+ throw new Error("Agent observation afterSequence must be a non-negative integer.");
148
+ return value;
149
+ }
@@ -1,16 +1,168 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { Type } from "typebox";
3
+ import { piboStringEnum } from "../tools/schema.js";
3
4
  import { definePiboTool } from "../tools/contract.js";
4
- function hashPart(value) {
5
- return createHash("sha256").update(value).digest("hex").slice(0, 12);
5
+ export const PIBO_AGENT_TOOL_NAMES = [
6
+ "pibo_agents_send_message",
7
+ "pibo_agents_list_agents",
8
+ "pibo_agents_observe",
9
+ "pibo_agents_kill",
10
+ ];
11
+ function availableAgentDescription(subagent) {
12
+ return subagent.description?.trim() || `Targets profile ${subagent.targetProfile}.`;
13
+ }
14
+ export function listAvailableAgents(subagents) {
15
+ return subagents
16
+ .filter((subagent) => subagent.enabled !== false)
17
+ .map((subagent) => ({
18
+ name: subagent.name,
19
+ description: availableAgentDescription(subagent),
20
+ profile: subagent.targetProfile,
21
+ ...(subagent.model ? { model: { ...subagent.model } } : {}),
22
+ ...(subagent.thinkingLevel ? { thinkingLevel: subagent.thinkingLevel } : {}),
23
+ }));
6
24
  }
7
- function toolNamePart(value) {
8
- const normalized = value.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
9
- return normalized || `subagent_${hashPart(value)}`;
25
+ export function formatAvailableAgentsForPrompt(subagents) {
26
+ return listAvailableAgents(subagents)
27
+ .map((agent) => `- ${agent.name}: ${agent.description}`)
28
+ .join("\n");
10
29
  }
30
+ function resultText(prefix, value) {
31
+ return `${prefix}\n${JSON.stringify(value, null, 2)}`;
32
+ }
33
+ export function createAgentToolDefinitions(subagents, controller) {
34
+ const enabled = subagents.filter((subagent) => subagent.enabled !== false);
35
+ if (enabled.length === 0)
36
+ return [];
37
+ const byName = new Map();
38
+ for (const subagent of enabled) {
39
+ if (byName.has(subagent.name))
40
+ throw new Error(`Duplicate agent name "${subagent.name}"`);
41
+ byName.set(subagent.name, subagent);
42
+ }
43
+ const names = [...byName.keys()];
44
+ const availableAgents = listAvailableAgents(enabled);
45
+ const catalog = formatAvailableAgentsForPrompt(enabled);
46
+ return [
47
+ definePiboTool({
48
+ name: "pibo_agents_send_message",
49
+ title: "Pibo Agents Send Message",
50
+ description: [
51
+ "Send a message to an available delegated agent. Foreground execution waits for the reply; use pibo_run_start for asynchronous delegation.",
52
+ "Available agents:",
53
+ catalog,
54
+ ].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.",
56
+ executionMode: "parallel",
57
+ inputSchema: Type.Object({
58
+ name: piboStringEnum(names, { description: "Available delegated agent name" }),
59
+ message: Type.String({ description: "Message to send to the delegated agent" }),
60
+ threadKey: Type.Optional(Type.String({
61
+ description: "Stable key for continuing one delegated-agent conversation. Omit it to create a new child session.",
62
+ maxLength: 256,
63
+ })),
64
+ }),
65
+ async execute(toolCallId, params, signal) {
66
+ const subagent = byName.get(params.name);
67
+ if (!subagent)
68
+ throw new Error(`Unknown delegated agent "${params.name}"`);
69
+ const result = await controller.sendMessage({
70
+ subagent,
71
+ message: params.message,
72
+ threadKey: params.threadKey,
73
+ toolCallId,
74
+ signal,
75
+ });
76
+ return {
77
+ content: [{
78
+ type: "text",
79
+ text: `Agent ${result.name} (${result.agentId}, thread ${result.threadKey}) replied:\n${result.reply.text}`,
80
+ }],
81
+ details: result,
82
+ };
83
+ },
84
+ }),
85
+ definePiboTool({
86
+ name: "pibo_agents_list_agents",
87
+ title: "Pibo Agents List Agents",
88
+ description: "List available delegated-agent profiles and child agent instances owned by this session.",
89
+ promptSnippet: "List available delegated agents and existing child instances with their agentId, thread, profile, and running, idle, or killed status.",
90
+ executionMode: "parallel",
91
+ annotations: { readOnly: true },
92
+ inputSchema: Type.Object({}),
93
+ async execute() {
94
+ const result = { availableAgents, agents: controller.listAgents() };
95
+ return {
96
+ content: [{ type: "text", text: resultText("Delegated agents:", result) }],
97
+ details: result,
98
+ };
99
+ },
100
+ }),
101
+ definePiboTool({
102
+ name: "pibo_agents_observe",
103
+ title: "Pibo Agents Observe",
104
+ description: "Read bounded delegated-agent observations with exact agent, thread, event, kind, time, text, cursor, order, and limit filters.",
105
+ promptSnippet: "Observe child-agent activity. Array filters use OR within a field and different fields combine with AND. For cursor polling, pass afterSequence from the previous result; pages consume the oldest unseen observations even when order is desc.",
106
+ executionMode: "parallel",
107
+ annotations: { readOnly: true },
108
+ inputSchema: Type.Object({
109
+ agentIds: Type.Optional(Type.Array(Type.String({ description: "Owned child agentId" }), { maxItems: 50 })),
110
+ names: Type.Optional(Type.Array(piboStringEnum(names), { maxItems: 50 })),
111
+ threadKeys: Type.Optional(Type.Array(Type.String(), { maxItems: 50 })),
112
+ eventTypes: Type.Optional(Type.Array(Type.String({ description: "Exact Pibo output event type" }), { maxItems: 50 })),
113
+ kinds: Type.Optional(Type.Array(piboStringEnum(["message", "thinking", "tool", "error", "lifecycle", "event"]), { maxItems: 6 })),
114
+ since: Type.Optional(Type.String({ description: "Inclusive ISO-8601 lower timestamp bound" })),
115
+ until: Type.Optional(Type.String({ description: "Inclusive ISO-8601 upper timestamp bound" })),
116
+ textContains: Type.Optional(Type.String({ description: "Case-insensitive substring match against normalized observation text" })),
117
+ afterSequence: Type.Optional(Type.Integer({ description: "Exclusive live observation cursor. Cursor pages consume the oldest unseen observations; desc reverses only the returned page.", minimum: 0 })),
118
+ order: Type.Optional(piboStringEnum(["asc", "desc"], { default: "asc" })),
119
+ limit: Type.Optional(Type.Integer({ description: "Maximum observations to return", minimum: 1, maximum: 200, default: 50 })),
120
+ includeDetails: Type.Optional(Type.Boolean({ description: "Include the normalized source event in each observation" })),
121
+ }),
122
+ async execute(_toolCallId, params) {
123
+ const result = controller.observe(params);
124
+ return {
125
+ content: [{ type: "text", text: resultText("Agent observations:", result) }],
126
+ details: result,
127
+ };
128
+ },
129
+ }),
130
+ definePiboTool({
131
+ name: "pibo_agents_kill",
132
+ title: "Pibo Agents Kill",
133
+ description: "Terminate one owned child agent session subtree and cancel its yielded runs.",
134
+ promptSnippet: "Kill an owned child agent by agentId when its work is no longer needed. Use pibo_agents_list_agents to find the exact agentId.",
135
+ executionMode: "parallel",
136
+ annotations: { destructive: true, idempotent: true },
137
+ inputSchema: Type.Object({
138
+ agentId: Type.String({ description: "Exact child agentId returned by send_message or list_agents" }),
139
+ }),
140
+ async execute(_toolCallId, params) {
141
+ const result = await controller.killAgent(params.agentId);
142
+ return {
143
+ content: [{ type: "text", text: resultText(`Killed delegated agent ${params.agentId}.`, result) }],
144
+ details: result,
145
+ };
146
+ },
147
+ }),
148
+ ];
149
+ }
150
+ function legacySubagentToolHash(value) {
151
+ return createHash("sha256").update(value).digest("hex").slice(0, 12);
152
+ }
153
+ /**
154
+ * @deprecated Runtime-generated per-agent tools are no longer assembled by Pibo runtimes.
155
+ * This helper remains source-compatible for integrations migrating to PIBO_AGENT_TOOL_NAMES.
156
+ */
11
157
  export function createSubagentToolName(subagentName) {
12
- return `pibo_subagent_${toolNamePart(subagentName)}`;
158
+ const normalized = subagentName.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
159
+ return `pibo_subagent_${normalized || `subagent_${legacySubagentToolHash(subagentName)}`}`;
13
160
  }
161
+ /**
162
+ * @deprecated Use createAgentToolDefinitions with a PiboAgentsController.
163
+ * Pibo runtimes expose only the four pibo_agents_* tools, but this legacy factory remains available
164
+ * for external callers during migration.
165
+ */
14
166
  export function createSubagentToolDefinitions(subagents, runner) {
15
167
  const seen = new Set();
16
168
  const definitions = [];
@@ -18,48 +170,27 @@ export function createSubagentToolDefinitions(subagents, runner) {
18
170
  if (subagent.enabled === false)
19
171
  continue;
20
172
  const toolName = createSubagentToolName(subagent.name);
21
- if (seen.has(toolName)) {
173
+ if (seen.has(toolName))
22
174
  throw new Error(`Duplicate subagent tool name "${toolName}"`);
23
- }
24
175
  seen.add(toolName);
25
- definitions.push(createSubagentToolDefinition(subagent, runner));
176
+ definitions.push(definePiboTool({
177
+ name: toolName,
178
+ title: `Pibo Subagent ${subagent.name}`,
179
+ description: subagent.description ?? `Send a message to the ${subagent.name} subagent. Use threadKey to continue the same subagent session.`,
180
+ promptSnippet: subagent.description ?? `Send a message to the ${subagent.name} subagent. Pass the same threadKey when you want to continue the same subagent session.`,
181
+ executionMode: "parallel",
182
+ inputSchema: Type.Object({
183
+ message: Type.String({ description: "Message to send to the subagent" }),
184
+ threadKey: Type.Optional(Type.String({
185
+ description: "Stable key for continuing a previous subagent conversation. Omit it to create a new subagent session.",
186
+ maxLength: 256,
187
+ })),
188
+ }),
189
+ async execute(toolCallId, params, signal) {
190
+ const result = await runner.runSubagent({ subagent, message: params.message, threadKey: params.threadKey, toolCallId, signal });
191
+ return { content: [{ type: "text", text: result.reply.text }], details: result };
192
+ },
193
+ }));
26
194
  }
27
195
  return definitions;
28
196
  }
29
- function createSubagentToolDefinition(subagent, runner) {
30
- const name = createSubagentToolName(subagent.name);
31
- return definePiboTool({
32
- name,
33
- title: `Pibo Subagent ${subagent.name}`,
34
- description: subagent.description ??
35
- `Send a message to the ${subagent.name} subagent. Use threadKey to continue the same subagent session.`,
36
- promptSnippet: subagent.description ??
37
- `Send a message to the ${subagent.name} subagent. Pass the same threadKey when you want to continue the same subagent session.`,
38
- executionMode: "parallel",
39
- inputSchema: Type.Object({
40
- message: Type.String({ description: "Message to send to the subagent" }),
41
- threadKey: Type.Optional(Type.String({
42
- description: "Stable key for continuing a previous subagent conversation. Omit it to create a new subagent session.",
43
- maxLength: 256,
44
- })),
45
- }),
46
- async execute(toolCallId, params, signal) {
47
- const result = await runner.runSubagent({
48
- subagent,
49
- message: params.message,
50
- threadKey: params.threadKey,
51
- toolCallId,
52
- signal,
53
- });
54
- return {
55
- content: [
56
- {
57
- type: "text",
58
- text: result.reply.text,
59
- },
60
- ],
61
- details: result,
62
- };
63
- },
64
- });
65
- }
@@ -53,6 +53,7 @@ export class PiboPortableToolService {
53
53
  cwd,
54
54
  },
55
55
  controllers: {
56
+ agentsController: input.agentsController,
56
57
  subagentRunner: input.subagentRunner,
57
58
  runToolController: input.runToolController,
58
59
  runtimeToolController: input.runtimeToolController,