@geminixiang/mikan 1.0.0-beta.87 → 1.0.0-beta.89

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 (249) hide show
  1. package/CHANGELOG.md +44 -1370
  2. package/README.md +2 -2
  3. package/dist/adapters/commands/session-view.d.ts.map +1 -1
  4. package/dist/adapters/commands/session-view.js +4 -4
  5. package/dist/adapters/commands/session-view.js.map +1 -1
  6. package/dist/adapters/discord/bot.d.ts +4 -1
  7. package/dist/adapters/discord/bot.d.ts.map +1 -1
  8. package/dist/adapters/discord/bot.js +6 -3
  9. package/dist/adapters/discord/bot.js.map +1 -1
  10. package/dist/adapters/discord/context.d.ts.map +1 -1
  11. package/dist/adapters/discord/context.js +3 -1
  12. package/dist/adapters/discord/context.js.map +1 -1
  13. package/dist/adapters/discord/types.d.ts +1 -0
  14. package/dist/adapters/discord/types.d.ts.map +1 -1
  15. package/dist/adapters/discord/types.js.map +1 -1
  16. package/dist/adapters/github/activity.d.ts.map +1 -1
  17. package/dist/adapters/github/activity.js +7 -13
  18. package/dist/adapters/github/activity.js.map +1 -1
  19. package/dist/adapters/github/bot.d.ts +4 -1
  20. package/dist/adapters/github/bot.d.ts.map +1 -1
  21. package/dist/adapters/github/bot.js +3 -3
  22. package/dist/adapters/github/bot.js.map +1 -1
  23. package/dist/adapters/github/context.d.ts.map +1 -1
  24. package/dist/adapters/github/context.js +3 -1
  25. package/dist/adapters/github/context.js.map +1 -1
  26. package/dist/adapters/github/types.d.ts +0 -3
  27. package/dist/adapters/github/types.d.ts.map +1 -1
  28. package/dist/adapters/github/types.js.map +1 -1
  29. package/dist/adapters/intake.d.ts.map +1 -1
  30. package/dist/adapters/intake.js +12 -2
  31. package/dist/adapters/intake.js.map +1 -1
  32. package/dist/adapters/shared.d.ts +2 -1
  33. package/dist/adapters/shared.d.ts.map +1 -1
  34. package/dist/adapters/shared.js +7 -2
  35. package/dist/adapters/shared.js.map +1 -1
  36. package/dist/adapters/slack/assistant.d.ts +2 -4
  37. package/dist/adapters/slack/assistant.d.ts.map +1 -1
  38. package/dist/adapters/slack/assistant.js +10 -16
  39. package/dist/adapters/slack/assistant.js.map +1 -1
  40. package/dist/adapters/slack/bot.d.ts +5 -1
  41. package/dist/adapters/slack/bot.d.ts.map +1 -1
  42. package/dist/adapters/slack/bot.js +15 -11
  43. package/dist/adapters/slack/bot.js.map +1 -1
  44. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  45. package/dist/adapters/slack/response-lifecycle.js +1 -1
  46. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  47. package/dist/adapters/slack/task-status.d.ts.map +1 -1
  48. package/dist/adapters/slack/task-status.js +1 -2
  49. package/dist/adapters/slack/task-status.js.map +1 -1
  50. package/dist/adapters/telegram/bot.d.ts +4 -1
  51. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  52. package/dist/adapters/telegram/bot.js +3 -3
  53. package/dist/adapters/telegram/bot.js.map +1 -1
  54. package/dist/adapters/telegram/context.d.ts.map +1 -1
  55. package/dist/adapters/telegram/context.js +3 -1
  56. package/dist/adapters/telegram/context.js.map +1 -1
  57. package/dist/adapters/types.d.ts +3 -1
  58. package/dist/adapters/types.d.ts.map +1 -1
  59. package/dist/adapters/types.js.map +1 -1
  60. package/dist/adapters/web/admin/client-assets.js +1 -1
  61. package/dist/adapters/web/admin/client-assets.js.map +1 -1
  62. package/dist/adapters/web/admin/portal.d.ts +0 -1
  63. package/dist/adapters/web/admin/portal.d.ts.map +1 -1
  64. package/dist/adapters/web/admin/portal.js +26 -42
  65. package/dist/adapters/web/admin/portal.js.map +1 -1
  66. package/dist/adapters/web/admin/types.d.ts +0 -2
  67. package/dist/adapters/web/admin/types.d.ts.map +1 -1
  68. package/dist/adapters/web/admin/types.js.map +1 -1
  69. package/dist/adapters/web/login/portal.d.ts.map +1 -1
  70. package/dist/adapters/web/login/portal.js +0 -8
  71. package/dist/adapters/web/login/portal.js.map +1 -1
  72. package/dist/adapters/web/server.d.ts.map +1 -1
  73. package/dist/adapters/web/server.js +0 -1
  74. package/dist/adapters/web/server.js.map +1 -1
  75. package/dist/adapters/web/session-view/portal.d.ts +1 -2
  76. package/dist/adapters/web/session-view/portal.d.ts.map +1 -1
  77. package/dist/adapters/web/session-view/portal.js +38 -328
  78. package/dist/adapters/web/session-view/portal.js.map +1 -1
  79. package/dist/adapters/web/session-view/types.d.ts +4 -6
  80. package/dist/adapters/web/session-view/types.d.ts.map +1 -1
  81. package/dist/adapters/web/session-view/types.js.map +1 -1
  82. package/dist/env-manifest.d.ts +0 -4
  83. package/dist/env-manifest.d.ts.map +1 -1
  84. package/dist/env-manifest.js +5 -8
  85. package/dist/env-manifest.js.map +1 -1
  86. package/dist/events/index.d.ts +2 -9
  87. package/dist/events/index.d.ts.map +1 -1
  88. package/dist/events/index.js +0 -1
  89. package/dist/events/index.js.map +1 -1
  90. package/dist/events/scheduler.d.ts +0 -1
  91. package/dist/events/scheduler.d.ts.map +1 -1
  92. package/dist/events/scheduler.js +2 -7
  93. package/dist/events/scheduler.js.map +1 -1
  94. package/dist/harness/execution-env.d.ts +1 -1
  95. package/dist/harness/execution-env.d.ts.map +1 -1
  96. package/dist/harness/execution-env.js +32 -32
  97. package/dist/harness/execution-env.js.map +1 -1
  98. package/dist/harness/index.d.ts +2 -3
  99. package/dist/harness/index.d.ts.map +1 -1
  100. package/dist/harness/index.js +0 -1
  101. package/dist/harness/index.js.map +1 -1
  102. package/dist/harness/mcp-result.d.ts +4 -3
  103. package/dist/harness/mcp-result.d.ts.map +1 -1
  104. package/dist/harness/mcp-result.js +27 -13
  105. package/dist/harness/mcp-result.js.map +1 -1
  106. package/dist/harness/mcp.js +4 -4
  107. package/dist/harness/mcp.js.map +1 -1
  108. package/dist/harness/presenter.d.ts.map +1 -1
  109. package/dist/harness/presenter.js +20 -25
  110. package/dist/harness/presenter.js.map +1 -1
  111. package/dist/harness/prompt.d.ts +0 -1
  112. package/dist/harness/prompt.d.ts.map +1 -1
  113. package/dist/harness/prompt.js +3 -32
  114. package/dist/harness/prompt.js.map +1 -1
  115. package/dist/harness/run-events.d.ts.map +1 -1
  116. package/dist/harness/run-events.js +4 -12
  117. package/dist/harness/run-events.js.map +1 -1
  118. package/dist/harness/runner.d.ts.map +1 -1
  119. package/dist/harness/runner.js +35 -38
  120. package/dist/harness/runner.js.map +1 -1
  121. package/dist/harness/session.d.ts +37 -38
  122. package/dist/harness/session.d.ts.map +1 -1
  123. package/dist/harness/session.js +387 -388
  124. package/dist/harness/session.js.map +1 -1
  125. package/dist/harness/subagent.d.ts +5 -5
  126. package/dist/harness/subagent.d.ts.map +1 -1
  127. package/dist/harness/subagent.js +40 -45
  128. package/dist/harness/subagent.js.map +1 -1
  129. package/dist/harness/tools/codemode.d.ts +3 -0
  130. package/dist/harness/tools/codemode.d.ts.map +1 -0
  131. package/dist/harness/tools/codemode.js +173 -0
  132. package/dist/harness/tools/codemode.js.map +1 -0
  133. package/dist/harness/tools/history.d.ts +20 -0
  134. package/dist/harness/tools/history.d.ts.map +1 -0
  135. package/dist/harness/tools/history.js +293 -0
  136. package/dist/harness/tools/history.js.map +1 -0
  137. package/dist/harness/tools/pi-tools.d.ts +1 -1
  138. package/dist/harness/tools/pi-tools.d.ts.map +1 -1
  139. package/dist/harness/tools/pi-tools.js +47 -21
  140. package/dist/harness/tools/pi-tools.js.map +1 -1
  141. package/dist/harness/tools/secret-redaction.d.ts.map +1 -1
  142. package/dist/harness/tools/secret-redaction.js +46 -2
  143. package/dist/harness/tools/secret-redaction.js.map +1 -1
  144. package/dist/harness/tools/tool-search.d.ts +5 -0
  145. package/dist/harness/tools/tool-search.d.ts.map +1 -0
  146. package/dist/harness/tools/tool-search.js +84 -0
  147. package/dist/harness/tools/tool-search.js.map +1 -0
  148. package/dist/harness/types.d.ts +75 -15
  149. package/dist/harness/types.d.ts.map +1 -1
  150. package/dist/harness/types.js.map +1 -1
  151. package/dist/index.d.ts +3 -7
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.js +0 -3
  154. package/dist/index.js.map +1 -1
  155. package/dist/log.d.ts +0 -3
  156. package/dist/log.d.ts.map +1 -1
  157. package/dist/log.js +0 -15
  158. package/dist/log.js.map +1 -1
  159. package/dist/main.js +2 -2
  160. package/dist/main.js.map +1 -1
  161. package/dist/memory-capture/index.js +0 -1
  162. package/dist/memory-capture/index.js.map +1 -1
  163. package/dist/migrations/index.d.ts.map +1 -1
  164. package/dist/migrations/index.js +2 -0
  165. package/dist/migrations/index.js.map +1 -1
  166. package/dist/migrations/session-files.d.ts +3 -2
  167. package/dist/migrations/session-files.d.ts.map +1 -1
  168. package/dist/migrations/session-files.js.map +1 -1
  169. package/dist/migrations/session-v4.d.ts +77 -0
  170. package/dist/migrations/session-v4.d.ts.map +1 -0
  171. package/dist/migrations/session-v4.js +177 -0
  172. package/dist/migrations/session-v4.js.map +1 -0
  173. package/dist/migrations/sessions-sqlite.d.ts +3 -0
  174. package/dist/migrations/sessions-sqlite.d.ts.map +1 -0
  175. package/dist/migrations/sessions-sqlite.js +215 -0
  176. package/dist/migrations/sessions-sqlite.js.map +1 -0
  177. package/dist/migrations/sessions-v3.d.ts +2 -2
  178. package/dist/migrations/sessions-v3.d.ts.map +1 -1
  179. package/dist/migrations/sessions-v3.js +8 -19
  180. package/dist/migrations/sessions-v3.js.map +1 -1
  181. package/dist/migrations/types.d.ts +0 -5
  182. package/dist/migrations/types.d.ts.map +1 -1
  183. package/dist/migrations/types.js.map +1 -1
  184. package/dist/observability/sentry.d.ts +2 -9
  185. package/dist/observability/sentry.d.ts.map +1 -1
  186. package/dist/observability/sentry.js +12 -64
  187. package/dist/observability/sentry.js.map +1 -1
  188. package/dist/observability/types.d.ts +0 -16
  189. package/dist/observability/types.d.ts.map +1 -1
  190. package/dist/observability/types.js.map +1 -1
  191. package/dist/office/index.d.ts +1 -4
  192. package/dist/office/index.d.ts.map +1 -1
  193. package/dist/office/index.js +2 -14
  194. package/dist/office/index.js.map +1 -1
  195. package/dist/office/projection.d.ts.map +1 -1
  196. package/dist/office/projection.js +0 -1
  197. package/dist/office/projection.js.map +1 -1
  198. package/dist/office/types.d.ts +1 -2
  199. package/dist/office/types.d.ts.map +1 -1
  200. package/dist/office/types.js.map +1 -1
  201. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  202. package/dist/runtime/conversation-runtime.js +2 -16
  203. package/dist/runtime/conversation-runtime.js.map +1 -1
  204. package/dist/runtime/session-lifecycle.d.ts +1 -2
  205. package/dist/runtime/session-lifecycle.d.ts.map +1 -1
  206. package/dist/runtime/session-lifecycle.js +7 -16
  207. package/dist/runtime/session-lifecycle.js.map +1 -1
  208. package/dist/runtime/types.d.ts +0 -1
  209. package/dist/runtime/types.d.ts.map +1 -1
  210. package/dist/runtime/types.js.map +1 -1
  211. package/dist/sandbox/provisioner.d.ts.map +1 -1
  212. package/dist/sandbox/provisioner.js +3 -4
  213. package/dist/sandbox/provisioner.js.map +1 -1
  214. package/dist/sessions/chat-history-sync.d.ts +8 -6
  215. package/dist/sessions/chat-history-sync.d.ts.map +1 -1
  216. package/dist/sessions/chat-history-sync.js +70 -143
  217. package/dist/sessions/chat-history-sync.js.map +1 -1
  218. package/dist/sessions/compaction-summary.d.ts +4 -0
  219. package/dist/sessions/compaction-summary.d.ts.map +1 -0
  220. package/dist/sessions/compaction-summary.js +16 -0
  221. package/dist/sessions/compaction-summary.js.map +1 -0
  222. package/dist/sessions/session-key.d.ts +1 -0
  223. package/dist/sessions/session-key.d.ts.map +1 -1
  224. package/dist/sessions/session-key.js +3 -0
  225. package/dist/sessions/session-key.js.map +1 -1
  226. package/dist/sessions/session-store.d.ts +47 -29
  227. package/dist/sessions/session-store.d.ts.map +1 -1
  228. package/dist/sessions/session-store.js +578 -484
  229. package/dist/sessions/session-store.js.map +1 -1
  230. package/dist/sessions/types.d.ts +60 -46
  231. package/dist/sessions/types.d.ts.map +1 -1
  232. package/dist/sessions/types.js +1 -1
  233. package/dist/sessions/types.js.map +1 -1
  234. package/dist/settings/apply.js +2 -2
  235. package/dist/settings/apply.js.map +1 -1
  236. package/dist/types.d.ts +10 -3
  237. package/dist/types.d.ts.map +1 -1
  238. package/dist/types.js.map +1 -1
  239. package/dist/vault/index.d.ts.map +1 -1
  240. package/dist/vault/index.js +0 -3
  241. package/dist/vault/index.js.map +1 -1
  242. package/dist/vault/types.d.ts +0 -3
  243. package/dist/vault/types.d.ts.map +1 -1
  244. package/dist/vault/types.js.map +1 -1
  245. package/package.json +8 -5
  246. package/dist/sessions/store.d.ts +0 -17
  247. package/dist/sessions/store.d.ts.map +0 -1
  248. package/dist/sessions/store.js +0 -211
  249. package/dist/sessions/store.js.map +0 -1
@@ -0,0 +1,293 @@
1
+ import { contentText } from "@earendil-works/pi-ai";
2
+ import { Type } from "typebox";
3
+ import { readConversationLog } from "../../sessions/chat-history-sync.js";
4
+ import { formatHistoryLine, formatLocalTimestamp } from "../../sessions/history-line.js";
5
+ import { SessionStore, isEarlierSessionKey } from "../../sessions/session-store.js";
6
+ import { isThreadSessionKey, threadSuffixOf } from "../../sessions/session-key.js";
7
+ import { LABEL_PARAMETER } from "./host-fn-tool.js";
8
+ const HISTORY_TOOL = "history";
9
+ const DEFAULT_LIMIT = 20;
10
+ const MAX_LIMIT = 200;
11
+ const MAX_ENTRY_CHARS = 2000;
12
+ const MAX_OUTPUT_CHARS = 30_000;
13
+ const SESSION_REF_MIN_CHARS = 8;
14
+ const historySchema = Type.Object({
15
+ label: LABEL_PARAMETER,
16
+ action: Type.Union([Type.Literal("sessions"), Type.Literal("search"), Type.Literal("read"), Type.Literal("chat")], {
17
+ description: "sessions: list this conversation's sessions. search: find text in session entries, including tool calls and output. read: read one session in order. chat: search the chat log, including messages you never answered.",
18
+ }),
19
+ query: Type.Optional(Type.String({
20
+ description: "Case-insensitive words that must all appear in an entry; a Chinese or Japanese word also matches when most of its character pairs appear (search and chat; optional for chat)",
21
+ })),
22
+ session: Type.Optional(Type.String({
23
+ description: `Session id from the sessions action (first ${SESSION_REF_MIN_CHARS}+ characters are enough), or "current". read defaults to current; search defaults to every session.`,
24
+ })),
25
+ limit: Type.Optional(Type.Integer({
26
+ minimum: 1,
27
+ maximum: MAX_LIMIT,
28
+ description: `How many of the newest results to return (default ${DEFAULT_LIMIT}; sessions lists up to ${MAX_LIMIT})`,
29
+ })),
30
+ skip: Type.Optional(Type.Integer({
31
+ minimum: 0,
32
+ description: "Newest results to skip, to page back to older ones",
33
+ })),
34
+ });
35
+ function isScopeSession(info, scope) {
36
+ return info.key === scope.sessionKey;
37
+ }
38
+ async function listOfficeSessions(office) {
39
+ return (await SessionStore.list(office)).toSorted((a, b) => b.createdAt - a.createdAt);
40
+ }
41
+ function clip(text, max = MAX_ENTRY_CHARS) {
42
+ return text.length <= max ? text : `${text.slice(0, max)}… (${text.length - max} more chars)`;
43
+ }
44
+ function formatTime(ms) {
45
+ return formatLocalTimestamp(new Date(ms)) ?? String(ms);
46
+ }
47
+ function recordOf(entryId) {
48
+ return entryId.slice(entryId.lastIndexOf(":") + 1);
49
+ }
50
+ function toolResultText(message) {
51
+ return `[${message.toolName}${message.isError ? " error" : ""}] ${contentText(message.content, "\n")}`;
52
+ }
53
+ function messageText(message, results) {
54
+ switch (message.role) {
55
+ case "user":
56
+ return `[user] ${contentText(message.content, "\n")}`;
57
+ case "assistant": {
58
+ const parts = message.content.flatMap((part) => {
59
+ if (part.type === "text")
60
+ return [part.text];
61
+ if (part.type !== "toolCall" || part.name === HISTORY_TOOL)
62
+ return [];
63
+ const call = `→ ${part.name} ${JSON.stringify(part.arguments)}`;
64
+ const result = results.get(part.id);
65
+ return [result ? `${call}\n← ${toolResultText(result)}` : call];
66
+ });
67
+ return parts.length > 0 ? `[assistant] ${parts.join("\n")}` : undefined;
68
+ }
69
+ case "toolResult":
70
+ return message.toolName === HISTORY_TOOL ? undefined : toolResultText(message);
71
+ default:
72
+ return undefined;
73
+ }
74
+ }
75
+ function historyEntries(entries) {
76
+ const calls = new Set();
77
+ const results = new Map();
78
+ for (const entry of entries) {
79
+ if (entry.type !== "message")
80
+ continue;
81
+ if (entry.message.role === "toolResult")
82
+ results.set(entry.message.toolCallId, entry.message);
83
+ if (entry.message.role !== "assistant")
84
+ continue;
85
+ for (const part of entry.message.content)
86
+ if (part.type === "toolCall")
87
+ calls.add(part.id);
88
+ }
89
+ return entries.flatMap((entry) => {
90
+ switch (entry.type) {
91
+ case "message": {
92
+ const { message } = entry;
93
+ if (message.role === "toolResult" && calls.has(message.toolCallId))
94
+ return [];
95
+ const text = messageText(message, results);
96
+ return text ? [{ record: recordOf(entry.id), timestamp: entry.timestamp, text }] : [];
97
+ }
98
+ case "compaction":
99
+ return [
100
+ {
101
+ record: recordOf(entry.id),
102
+ timestamp: entry.timestamp,
103
+ text: `[summary] ${entry.summary}`,
104
+ },
105
+ ];
106
+ case "custom":
107
+ return [];
108
+ }
109
+ });
110
+ }
111
+ const CJK_CHARACTER = /[\u3040-\u30ff\u3400-\u9fff\uac00-\ud7af]/;
112
+ function wordMatches(text, word) {
113
+ if (text.includes(word))
114
+ return true;
115
+ const characters = Array.from(word);
116
+ if (characters.length < 3 || !CJK_CHARACTER.test(word))
117
+ return false;
118
+ const pairs = characters.slice(1).map((character, index) => `${characters[index]}${character}`);
119
+ return pairs.filter((pair) => text.includes(pair)).length * 2 >= pairs.length;
120
+ }
121
+ function matches(text, query) {
122
+ if (!query)
123
+ return true;
124
+ const lower = text.toLowerCase();
125
+ return query.split(/\s+/).every((word) => wordMatches(lower, word));
126
+ }
127
+ function entryLine(entry) {
128
+ return `(${formatTime(entry.timestamp)}) ${clip(entry.text)}`;
129
+ }
130
+ function newestPage(items, args) {
131
+ const end = Math.max(items.length - (args.skip ?? 0), 0);
132
+ return items.slice(Math.max(end - (args.limit ?? DEFAULT_LIMIT), 0), end);
133
+ }
134
+ function bounded(header, lines, skip) {
135
+ const kept = [];
136
+ let length = header.length;
137
+ for (const line of lines.toReversed()) {
138
+ if (length + line.length > MAX_OUTPUT_CHARS)
139
+ break;
140
+ kept.unshift(line);
141
+ length += line.length;
142
+ }
143
+ const dropped = lines.length - kept.length;
144
+ const note = dropped > 0
145
+ ? [`(${dropped} older results omitted by the output limit; use skip=${skip + kept.length})`]
146
+ : [];
147
+ return [header, ...note, ...kept].join("\n\n");
148
+ }
149
+ function sessionKind(info) {
150
+ if (info.root)
151
+ return "main, current";
152
+ return isEarlierSessionKey(info.key) ? "earlier session" : "thread";
153
+ }
154
+ function sessionLabel(info, titles) {
155
+ const suffix = isThreadSessionKey(info.key) ? threadSuffixOf(info.key) : undefined;
156
+ const title = suffix ? titles.get(suffix) : undefined;
157
+ const parent = info.parentSessionId
158
+ ? ` parent=${info.parentSessionId.slice(0, SESSION_REF_MIN_CHARS)}`
159
+ : "";
160
+ return `${info.id} (${sessionKind(info)}, ${formatTime(info.createdAt)}${parent})${title ? ` ${clip(title, 120)}` : ""}`;
161
+ }
162
+ function scopeTitles(office) {
163
+ const titles = new Map();
164
+ for (const { message } of readConversationLog(office)) {
165
+ const firstLine = message.text
166
+ ?.split("\n")
167
+ .find((line) => line.trim())
168
+ ?.trim();
169
+ if (message.ts && firstLine) {
170
+ titles.set(message.ts, `[${message.userName ?? message.user ?? "unknown"}]: ${firstLine}`);
171
+ }
172
+ }
173
+ return titles;
174
+ }
175
+ function resolveSession(sessions, scope, ref) {
176
+ if (ref === "current") {
177
+ return (sessions.find((info) => isScopeSession(info, scope)) ??
178
+ "The current session has not started yet.");
179
+ }
180
+ if (ref.length < SESSION_REF_MIN_CHARS) {
181
+ return `Use at least ${SESSION_REF_MIN_CHARS} characters of a session id.`;
182
+ }
183
+ const found = sessions.filter((info) => info.id.startsWith(ref));
184
+ const [only] = found;
185
+ if (found.length === 1 && only)
186
+ return only;
187
+ return found.length === 0
188
+ ? `No session in this conversation starts with "${ref}". Use the sessions action to list them.`
189
+ : `"${ref}" matches ${found.length} sessions; use more characters.`;
190
+ }
191
+ async function listSessions(scope, args) {
192
+ const sessions = await listOfficeSessions(scope.office);
193
+ if (sessions.length === 0)
194
+ return "This conversation has no sessions yet.";
195
+ const titles = scopeTitles(scope.office);
196
+ const lines = sessions
197
+ .map((info) => `${isScopeSession(info, scope) ? "* " : ""}${sessionLabel(info, titles)}`)
198
+ .toReversed();
199
+ return bounded(`${sessions.length} sessions in this conversation, oldest first; * marks this one.`, newestPage(lines, { ...args, limit: args.limit ?? MAX_LIMIT }), args.skip ?? 0);
200
+ }
201
+ async function sessionEntries(office, info) {
202
+ const inspection = await SessionStore.inspect(office, info.key);
203
+ return inspection ? historyEntries(await inspection.getEntries()) : [];
204
+ }
205
+ async function readSession(scope, args) {
206
+ const sessions = await listOfficeSessions(scope.office);
207
+ const info = resolveSession(sessions, scope, args.session ?? "current");
208
+ if (typeof info === "string")
209
+ return info;
210
+ const query = args.query?.trim().toLowerCase();
211
+ const found = (await sessionEntries(scope.office, info)).filter((entry) => matches(entry.text, query));
212
+ if (found.length === 0) {
213
+ return query ? `No entries match "${args.query}".` : "The session is empty.";
214
+ }
215
+ const page = newestPage(found, args);
216
+ return bounded(`Session ${sessionLabel(info, scopeTitles(scope.office))}: ${found.length} entries; ${page.length} newest shown, oldest first.`, page.map(entryLine), args.skip ?? 0);
217
+ }
218
+ async function searchSessions(scope, args) {
219
+ const query = args.query?.trim().toLowerCase();
220
+ if (!query)
221
+ return "search needs a query; use read to page through a session.";
222
+ const sessions = await listOfficeSessions(scope.office);
223
+ const selected = args.session ? resolveSession(sessions, scope, args.session) : undefined;
224
+ if (typeof selected === "string")
225
+ return selected;
226
+ const titles = scopeTitles(scope.office);
227
+ const results = [];
228
+ const seen = new Set();
229
+ for (const info of selected ? [selected] : sessions.toReversed()) {
230
+ for (const entry of await sessionEntries(scope.office, info)) {
231
+ if (seen.has(entry.record))
232
+ continue;
233
+ seen.add(entry.record);
234
+ if (matches(entry.text, query))
235
+ results.push({ ...entry, session: info });
236
+ }
237
+ }
238
+ if (results.length === 0)
239
+ return `No session entries match "${args.query}".`;
240
+ const page = newestPage(results.toSorted((a, b) => a.timestamp - b.timestamp), args);
241
+ const lines = page.map((result, index) => {
242
+ const line = entryLine(result);
243
+ return page[index - 1]?.session === result.session
244
+ ? line
245
+ : `## session ${sessionLabel(result.session, titles)}\n${line}`;
246
+ });
247
+ const sessionCount = new Set(results.map((result) => result.session)).size;
248
+ return bounded(`${results.length} matching entries in ${sessionCount} sessions; ${page.length} newest shown, oldest first, grouped by session.`, lines, args.skip ?? 0);
249
+ }
250
+ function searchChat(scope, args) {
251
+ const query = args.query?.trim().toLowerCase();
252
+ const lines = readConversationLog(scope.office)
253
+ .map(({ message }) => formatHistoryLine({
254
+ date: message.date ? new Date(message.date) : undefined,
255
+ userName: message.userName ?? message.user,
256
+ threadTs: message.threadTs,
257
+ text: message.text ?? "",
258
+ }))
259
+ .filter((line) => matches(line, query));
260
+ if (lines.length === 0) {
261
+ return query ? `No chat messages match "${args.query}".` : "The chat log is empty.";
262
+ }
263
+ const page = newestPage(lines, args);
264
+ return bounded(`${lines.length} ${query ? "matching " : ""}chat messages; ${page.length} newest shown, oldest first.`, page.map((line) => clip(line)), args.skip ?? 0);
265
+ }
266
+ export async function runHistory(scope, args) {
267
+ switch (args.action) {
268
+ case "sessions":
269
+ return listSessions(scope, args);
270
+ case "search":
271
+ return searchSessions(scope, args);
272
+ case "read":
273
+ return readSession(scope, args);
274
+ case "chat":
275
+ return searchChat(scope, args);
276
+ }
277
+ }
278
+ export function createHistoryTool(scope) {
279
+ return {
280
+ name: HISTORY_TOOL,
281
+ label: HISTORY_TOOL,
282
+ description: "Look back through this conversation's history beyond your context: its sessions (the channel or DM session, earlier ones replaced by /new, and its threads), their messages, your tool calls and their output, and the chat log. Read an earlier result here instead of rerunning its command. Other conversations are not reachable.",
283
+ parameters: historySchema,
284
+ execute: async (_toolCallId, { label: _label, ...args }, signal) => {
285
+ signal?.throwIfAborted();
286
+ return {
287
+ content: [{ type: "text", text: await runHistory(scope, args) }],
288
+ details: undefined,
289
+ };
290
+ },
291
+ };
292
+ }
293
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../../src/harness/tools/history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage, AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { contentText, type ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { Type, type Static } from \"typebox\";\nimport type { Office } from \"../../office/types.js\";\nimport { readConversationLog } from \"../../sessions/chat-history-sync.js\";\nimport { formatHistoryLine, formatLocalTimestamp } from \"../../sessions/history-line.js\";\nimport { SessionStore, isEarlierSessionKey } from \"../../sessions/session-store.js\";\nimport { isThreadSessionKey, threadSuffixOf } from \"../../sessions/session-key.js\";\nimport type { SessionEntry, SessionListing } from \"../../sessions/types.js\";\nimport { LABEL_PARAMETER } from \"./host-fn-tool.js\";\n\nconst HISTORY_TOOL = \"history\";\nconst DEFAULT_LIMIT = 20;\nconst MAX_LIMIT = 200;\nconst MAX_ENTRY_CHARS = 2000;\nconst MAX_OUTPUT_CHARS = 30_000;\nconst SESSION_REF_MIN_CHARS = 8;\n\nconst historySchema = Type.Object({\n label: LABEL_PARAMETER,\n action: Type.Union(\n [Type.Literal(\"sessions\"), Type.Literal(\"search\"), Type.Literal(\"read\"), Type.Literal(\"chat\")],\n {\n description:\n \"sessions: list this conversation's sessions. search: find text in session entries, including tool calls and output. read: read one session in order. chat: search the chat log, including messages you never answered.\",\n },\n ),\n query: Type.Optional(\n Type.String({\n description:\n \"Case-insensitive words that must all appear in an entry; a Chinese or Japanese word also matches when most of its character pairs appear (search and chat; optional for chat)\",\n }),\n ),\n session: Type.Optional(\n Type.String({\n description: `Session id from the sessions action (first ${SESSION_REF_MIN_CHARS}+ characters are enough), or \"current\". read defaults to current; search defaults to every session.`,\n }),\n ),\n limit: Type.Optional(\n Type.Integer({\n minimum: 1,\n maximum: MAX_LIMIT,\n description: `How many of the newest results to return (default ${DEFAULT_LIMIT}; sessions lists up to ${MAX_LIMIT})`,\n }),\n ),\n skip: Type.Optional(\n Type.Integer({\n minimum: 0,\n description: \"Newest results to skip, to page back to older ones\",\n }),\n ),\n});\n\ntype HistoryArgs = Omit<Static<typeof historySchema>, \"label\">;\n\ninterface HistoryScope {\n office: Office;\n sessionKey: string;\n}\n\nfunction isScopeSession(info: SessionListing, scope: HistoryScope): boolean {\n return info.key === scope.sessionKey;\n}\n\nasync function listOfficeSessions(office: Office): Promise<SessionListing[]> {\n return (await SessionStore.list(office)).toSorted((a, b) => b.createdAt - a.createdAt);\n}\n\nfunction clip(text: string, max = MAX_ENTRY_CHARS): string {\n return text.length <= max ? text : `${text.slice(0, max)}… (${text.length - max} more chars)`;\n}\n\nfunction formatTime(ms: number): string {\n return formatLocalTimestamp(new Date(ms)) ?? String(ms);\n}\n\ninterface HistoryEntry {\n record: string;\n timestamp: number;\n text: string;\n}\n\nfunction recordOf(entryId: string): string {\n return entryId.slice(entryId.lastIndexOf(\":\") + 1);\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n return `[${message.toolName}${message.isError ? \" error\" : \"\"}] ${contentText(message.content, \"\\n\")}`;\n}\n\nfunction messageText(\n message: AgentMessage,\n results: ReadonlyMap<string, ToolResultMessage>,\n): string | undefined {\n switch (message.role) {\n case \"user\":\n return `[user] ${contentText(message.content, \"\\n\")}`;\n case \"assistant\": {\n const parts = message.content.flatMap((part) => {\n if (part.type === \"text\") return [part.text];\n if (part.type !== \"toolCall\" || part.name === HISTORY_TOOL) return [];\n const call = `→ ${part.name} ${JSON.stringify(part.arguments)}`;\n const result = results.get(part.id);\n return [result ? `${call}\\n← ${toolResultText(result)}` : call];\n });\n return parts.length > 0 ? `[assistant] ${parts.join(\"\\n\")}` : undefined;\n }\n case \"toolResult\":\n return message.toolName === HISTORY_TOOL ? undefined : toolResultText(message);\n default:\n return undefined;\n }\n}\n\nfunction historyEntries(entries: readonly SessionEntry[]): HistoryEntry[] {\n const calls = new Set<string>();\n const results = new Map<string, ToolResultMessage>();\n for (const entry of entries) {\n if (entry.type !== \"message\") continue;\n if (entry.message.role === \"toolResult\") results.set(entry.message.toolCallId, entry.message);\n if (entry.message.role !== \"assistant\") continue;\n for (const part of entry.message.content) if (part.type === \"toolCall\") calls.add(part.id);\n }\n return entries.flatMap((entry) => {\n switch (entry.type) {\n case \"message\": {\n const { message } = entry;\n if (message.role === \"toolResult\" && calls.has(message.toolCallId)) return [];\n const text = messageText(message, results);\n return text ? [{ record: recordOf(entry.id), timestamp: entry.timestamp, text }] : [];\n }\n case \"compaction\":\n return [\n {\n record: recordOf(entry.id),\n timestamp: entry.timestamp,\n text: `[summary] ${entry.summary}`,\n },\n ];\n case \"custom\":\n return [];\n }\n });\n}\n\nconst CJK_CHARACTER = /[\\u3040-\\u30ff\\u3400-\\u9fff\\uac00-\\ud7af]/;\n\nfunction wordMatches(text: string, word: string): boolean {\n if (text.includes(word)) return true;\n const characters = Array.from(word);\n if (characters.length < 3 || !CJK_CHARACTER.test(word)) return false;\n const pairs = characters.slice(1).map((character, index) => `${characters[index]}${character}`);\n return pairs.filter((pair) => text.includes(pair)).length * 2 >= pairs.length;\n}\n\nfunction matches(text: string, query: string | undefined): boolean {\n if (!query) return true;\n const lower = text.toLowerCase();\n return query.split(/\\s+/).every((word) => wordMatches(lower, word));\n}\n\nfunction entryLine(entry: HistoryEntry): string {\n return `(${formatTime(entry.timestamp)}) ${clip(entry.text)}`;\n}\n\nfunction newestPage<T>(items: readonly T[], args: HistoryArgs): T[] {\n const end = Math.max(items.length - (args.skip ?? 0), 0);\n return items.slice(Math.max(end - (args.limit ?? DEFAULT_LIMIT), 0), end);\n}\n\nfunction bounded(header: string, lines: readonly string[], skip: number): string {\n const kept: string[] = [];\n let length = header.length;\n for (const line of lines.toReversed()) {\n if (length + line.length > MAX_OUTPUT_CHARS) break;\n kept.unshift(line);\n length += line.length;\n }\n const dropped = lines.length - kept.length;\n const note =\n dropped > 0\n ? [`(${dropped} older results omitted by the output limit; use skip=${skip + kept.length})`]\n : [];\n return [header, ...note, ...kept].join(\"\\n\\n\");\n}\n\nfunction sessionKind(info: SessionListing): string {\n if (info.root) return \"main, current\";\n return isEarlierSessionKey(info.key) ? \"earlier session\" : \"thread\";\n}\n\nfunction sessionLabel(info: SessionListing, titles: ReadonlyMap<string, string>): string {\n const suffix = isThreadSessionKey(info.key) ? threadSuffixOf(info.key) : undefined;\n const title = suffix ? titles.get(suffix) : undefined;\n const parent = info.parentSessionId\n ? ` parent=${info.parentSessionId.slice(0, SESSION_REF_MIN_CHARS)}`\n : \"\";\n return `${info.id} (${sessionKind(info)}, ${formatTime(info.createdAt)}${parent})${title ? ` ${clip(title, 120)}` : \"\"}`;\n}\n\nfunction scopeTitles(office: Office): Map<string, string> {\n const titles = new Map<string, string>();\n for (const { message } of readConversationLog(office)) {\n const firstLine = message.text\n ?.split(\"\\n\")\n .find((line) => line.trim())\n ?.trim();\n if (message.ts && firstLine) {\n titles.set(message.ts, `[${message.userName ?? message.user ?? \"unknown\"}]: ${firstLine}`);\n }\n }\n return titles;\n}\n\nfunction resolveSession(\n sessions: readonly SessionListing[],\n scope: HistoryScope,\n ref: string,\n): SessionListing | string {\n if (ref === \"current\") {\n return (\n sessions.find((info) => isScopeSession(info, scope)) ??\n \"The current session has not started yet.\"\n );\n }\n if (ref.length < SESSION_REF_MIN_CHARS) {\n return `Use at least ${SESSION_REF_MIN_CHARS} characters of a session id.`;\n }\n const found = sessions.filter((info) => info.id.startsWith(ref));\n const [only] = found;\n if (found.length === 1 && only) return only;\n return found.length === 0\n ? `No session in this conversation starts with \"${ref}\". Use the sessions action to list them.`\n : `\"${ref}\" matches ${found.length} sessions; use more characters.`;\n}\n\nasync function listSessions(scope: HistoryScope, args: HistoryArgs): Promise<string> {\n const sessions = await listOfficeSessions(scope.office);\n if (sessions.length === 0) return \"This conversation has no sessions yet.\";\n const titles = scopeTitles(scope.office);\n const lines = sessions\n .map((info) => `${isScopeSession(info, scope) ? \"* \" : \"\"}${sessionLabel(info, titles)}`)\n .toReversed();\n return bounded(\n `${sessions.length} sessions in this conversation, oldest first; * marks this one.`,\n newestPage(lines, { ...args, limit: args.limit ?? MAX_LIMIT }),\n args.skip ?? 0,\n );\n}\n\nasync function sessionEntries(office: Office, info: SessionListing): Promise<HistoryEntry[]> {\n const inspection = await SessionStore.inspect(office, info.key);\n return inspection ? historyEntries(await inspection.getEntries()) : [];\n}\n\nasync function readSession(scope: HistoryScope, args: HistoryArgs): Promise<string> {\n const sessions = await listOfficeSessions(scope.office);\n const info = resolveSession(sessions, scope, args.session ?? \"current\");\n if (typeof info === \"string\") return info;\n const query = args.query?.trim().toLowerCase();\n const found = (await sessionEntries(scope.office, info)).filter((entry) =>\n matches(entry.text, query),\n );\n if (found.length === 0) {\n return query ? `No entries match \"${args.query}\".` : \"The session is empty.\";\n }\n const page = newestPage(found, args);\n return bounded(\n `Session ${sessionLabel(info, scopeTitles(scope.office))}: ${found.length} entries; ${page.length} newest shown, oldest first.`,\n page.map(entryLine),\n args.skip ?? 0,\n );\n}\n\nasync function searchSessions(scope: HistoryScope, args: HistoryArgs): Promise<string> {\n const query = args.query?.trim().toLowerCase();\n if (!query) return \"search needs a query; use read to page through a session.\";\n const sessions = await listOfficeSessions(scope.office);\n const selected = args.session ? resolveSession(sessions, scope, args.session) : undefined;\n if (typeof selected === \"string\") return selected;\n const titles = scopeTitles(scope.office);\n const results: Array<HistoryEntry & { session: SessionListing }> = [];\n const seen = new Set<string>();\n for (const info of selected ? [selected] : sessions.toReversed()) {\n for (const entry of await sessionEntries(scope.office, info)) {\n if (seen.has(entry.record)) continue;\n seen.add(entry.record);\n if (matches(entry.text, query)) results.push({ ...entry, session: info });\n }\n }\n if (results.length === 0) return `No session entries match \"${args.query}\".`;\n const page = newestPage(\n results.toSorted((a, b) => a.timestamp - b.timestamp),\n args,\n );\n const lines = page.map((result, index) => {\n const line = entryLine(result);\n return page[index - 1]?.session === result.session\n ? line\n : `## session ${sessionLabel(result.session, titles)}\\n${line}`;\n });\n const sessionCount = new Set(results.map((result) => result.session)).size;\n return bounded(\n `${results.length} matching entries in ${sessionCount} sessions; ${page.length} newest shown, oldest first, grouped by session.`,\n lines,\n args.skip ?? 0,\n );\n}\n\nfunction searchChat(scope: HistoryScope, args: HistoryArgs): string {\n const query = args.query?.trim().toLowerCase();\n const lines = readConversationLog(scope.office)\n .map(({ message }) =>\n formatHistoryLine({\n date: message.date ? new Date(message.date) : undefined,\n userName: message.userName ?? message.user,\n threadTs: message.threadTs,\n text: message.text ?? \"\",\n }),\n )\n .filter((line) => matches(line, query));\n if (lines.length === 0) {\n return query ? `No chat messages match \"${args.query}\".` : \"The chat log is empty.\";\n }\n const page = newestPage(lines, args);\n return bounded(\n `${lines.length} ${query ? \"matching \" : \"\"}chat messages; ${page.length} newest shown, oldest first.`,\n page.map((line) => clip(line)),\n args.skip ?? 0,\n );\n}\n\nexport async function runHistory(scope: HistoryScope, args: HistoryArgs): Promise<string> {\n switch (args.action) {\n case \"sessions\":\n return listSessions(scope, args);\n case \"search\":\n return searchSessions(scope, args);\n case \"read\":\n return readSession(scope, args);\n case \"chat\":\n return searchChat(scope, args);\n }\n}\n\nexport function createHistoryTool(scope: HistoryScope): AgentTool<typeof historySchema> {\n return {\n name: HISTORY_TOOL,\n label: HISTORY_TOOL,\n description:\n \"Look back through this conversation's history beyond your context: its sessions (the channel or DM session, earlier ones replaced by /new, and its threads), their messages, your tool calls and their output, and the chat log. Read an earlier result here instead of rerunning its command. Other conversations are not reachable.\",\n parameters: historySchema,\n execute: async (_toolCallId, { label: _label, ...args }, signal) => {\n signal?.throwIfAborted();\n return {\n content: [{ type: \"text\", text: await runHistory(scope, args) }],\n details: undefined,\n };\n },\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { type AgentTool } from "@earendil-works/pi-agent-core";
1
+ import type { AgentTool } from "@earendil-works/pi-agent-core";
2
2
  import type { MikanHarnessTool } from "../types.js";
3
3
  export type { MikanHarnessTool };
4
4
  export declare function isHarnessTool(tool: unknown): tool is MikanHarnessTool;
@@ -1 +1 @@
1
- {"version":3,"file":"pi-tools.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/pi-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAIjC,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,gBAAgB,CAErE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CAGvE;AAmCD,wBAAgB,kBAAkB,IAAI,gBAAgB,EAAE,CAYvD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,gBAAgB,CAQhE"}
1
+ {"version":3,"file":"pi-tools.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/pi-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAU/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAIjC,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,gBAAgB,CAErE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CAGvE;AA0BD,wBAAgB,kBAAkB,IAAI,gBAAgB,EAAE,CAYvD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,gBAAgB,CAgChE"}
@@ -1,4 +1,4 @@
1
- import { createBashTool, createEditTool, createReadTool, createWriteTool, } from "@earendil-works/pi-agent-core";
1
+ import { createBashTool, createEditTool, createReadTool, createWriteTool, } from "@earendil-works/pi-durable/tools";
2
2
  import { LABEL_PARAMETER } from "./host-fn-tool.js";
3
3
  const HARNESS_TOOL = Symbol.for("mikan.harnessTool");
4
4
  export function isHarnessTool(tool) {
@@ -8,32 +8,34 @@ export function tagHarnessTool(tool) {
8
8
  Object.defineProperty(tool, HARNESS_TOOL, { value: true });
9
9
  return tool;
10
10
  }
11
+ function toJsonValue(input) {
12
+ if (input === undefined)
13
+ return undefined;
14
+ const copy = JSON.parse(JSON.stringify(input));
15
+ return copy;
16
+ }
11
17
  function withLabel(tool) {
12
18
  const schema = tool.parameters;
13
- const parameters = {
14
- ...schema,
15
- properties: {
16
- ...schema.properties,
17
- label: LABEL_PARAMETER,
18
- },
19
- };
20
19
  return tagHarnessTool({
21
20
  ...tool,
22
- parameters,
23
- execute: async (...args) => {
24
- const [toolCallId, params, onUpdate, toolContext, invocation, context] = args;
25
- if (!toolContext?.env) {
26
- throw new Error(`Tool ${tool.name} requires toolContext.env; supply an authorized execution env.`);
21
+ parameters: {
22
+ ...schema,
23
+ properties: { ...schema.properties, label: LABEL_PARAMETER },
24
+ },
25
+ execute: async (args, api, context) => {
26
+ if (!api.env) {
27
+ throw new Error(`Tool ${tool.name} requires an execution env; supply an authorized one.`);
27
28
  }
28
- const { label: _label, ...rest } = params;
29
- return tool.execute(toolCallId, rest, onUpdate, toolContext, invocation, context);
29
+ const { label: _label, ...rest } = args;
30
+ return tool.execute(rest, api, context);
30
31
  },
31
32
  });
32
33
  }
33
34
  export function createSandboxTools() {
34
35
  const bash = createBashTool({
35
- prepare: (execution, toolContext) => {
36
- execution.cwd = toolContext.env.cwd;
36
+ prepare: (execution, api) => {
37
+ if (api.env)
38
+ execution.cwd = api.env.cwd;
37
39
  },
38
40
  });
39
41
  return [
@@ -44,11 +46,35 @@ export function createSandboxTools() {
44
46
  ];
45
47
  }
46
48
  export function adaptAgentTool(tool) {
49
+ const exposure = "exposure" in tool && tool.exposure === "deferred" ? "deferred" : undefined;
50
+ const namespace = "namespace" in tool && typeof tool.namespace === "string" ? tool.namespace : undefined;
47
51
  return tagHarnessTool({
48
- ...tool,
49
- execute: (...args) => {
50
- const [toolCallId, params, onUpdate, , , context] = args;
51
- return tool.execute(toolCallId, params, context.abortSignal, onUpdate);
52
+ name: tool.name,
53
+ description: tool.description,
54
+ parameters: tool.parameters,
55
+ outputSchema: tool.outputSchema,
56
+ prepareArguments: tool.prepareArguments,
57
+ executionMode: tool.executionMode,
58
+ exposure,
59
+ namespace,
60
+ replay: tool.replay === "safe" ? "safe" : "unsafe",
61
+ execute: async (args, api, context) => {
62
+ let updates = Promise.resolve();
63
+ const result = await tool.execute(api.callId, args, context.abortSignal, (partial) => {
64
+ const details = toJsonValue(partial.details);
65
+ if (details === undefined)
66
+ return;
67
+ updates = updates.then(() => api.details(details, context)).catch(() => undefined);
68
+ });
69
+ await updates;
70
+ return {
71
+ content: result.content,
72
+ details: toJsonValue(result.details),
73
+ isError: result.isError,
74
+ structuredContent: result.structuredContent,
75
+ usage: result.usage,
76
+ control: result.terminate ? { terminate: true } : undefined,
77
+ };
52
78
  },
53
79
  });
54
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pi-tools.js","sourceRoot":"","sources":["../../../src/harness/tools/pi-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,GAEhB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAErD,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,OAAO,OAAO,CAAC,IAAI,IAAK,IAAqC,CAAC,YAAY,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAsB;IACnD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,IAAsB;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,UAAqB,CAAC;IAC1C,MAAM,UAAU,GAAG;QACjB,GAAG,MAAM;QACT,UAAU,EAAE;YACV,GAAG,MAAM,CAAC,UAAU;YACpB,KAAK,EAAE,eAAe;SACvB;KACF,CAAC;IAEF,OAAO,cAAc,CAAC;QACpB,GAAG,IAAI;QACP,UAAU;QACV,OAAO,EAAE,KAAK,EAAE,GAAG,IAA6C,EAAE,EAAE;YAClE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;YAC9E,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CACb,QAAQ,IAAI,CAAC,IAAI,gEAAgE,CAClF,CAAC;YACJ,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,MAAiC,CAAC;YACrE,OAAO,IAAI,CAAC,OAAO,CACjB,UAAU,EACV,IAAqB,EACrB,QAAQ,EACR,WAAW,EACX,UAAU,EACV,OAAO,CACR,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,IAAI,GAAG,cAAc,CAAC;QAC1B,OAAO,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,EAAE;YAClC,SAAS,CAAC,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;QACtC,CAAC;KACF,CAAC,CAAC;IACH,OAAO;QACL,SAAS,CAAC,cAAc,EAAsB,CAAC;QAC/C,SAAS,CAAC,eAAe,EAAsB,CAAC;QAChD,SAAS,CAAC,cAAc,EAAsB,CAAC;QAC/C,SAAS,CAAC,IAAwB,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAe;IAC5C,OAAO,cAAc,CAAC;QACpB,GAAG,IAAI;QACP,OAAO,EAAE,CAAC,GAAG,IAA6C,EAAE,EAAE;YAC5D,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,AAAD,EAAG,AAAD,EAAG,OAAO,CAAC,GAAG,IAAI,CAAC;YACzD,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACzE,CAAC;KACkB,CAAC,CAAC;AACzB,CAAC","sourcesContent":["import {\n createBashTool,\n createEditTool,\n createReadTool,\n createWriteTool,\n type AgentTool,\n} from \"@earendil-works/pi-agent-core\";\nimport type { TObject } from \"typebox\";\nimport type { MikanHarnessTool } from \"../types.js\";\nimport { LABEL_PARAMETER } from \"./host-fn-tool.js\";\n\nexport type { MikanHarnessTool };\n\nconst HARNESS_TOOL = Symbol.for(\"mikan.harnessTool\");\n\nexport function isHarnessTool(tool: unknown): tool is MikanHarnessTool {\n return Boolean(tool && (tool as Record<PropertyKey, unknown>)[HARNESS_TOOL]);\n}\n\nexport function tagHarnessTool(tool: MikanHarnessTool): MikanHarnessTool {\n Object.defineProperty(tool, HARNESS_TOOL, { value: true });\n return tool;\n}\n\nfunction withLabel(tool: MikanHarnessTool): MikanHarnessTool {\n const schema = tool.parameters as TObject;\n const parameters = {\n ...schema,\n properties: {\n ...schema.properties,\n label: LABEL_PARAMETER,\n },\n };\n\n return tagHarnessTool({\n ...tool,\n parameters,\n execute: async (...args: Parameters<MikanHarnessTool[\"execute\"]>) => {\n const [toolCallId, params, onUpdate, toolContext, invocation, context] = args;\n if (!toolContext?.env) {\n throw new Error(\n `Tool ${tool.name} requires toolContext.env; supply an authorized execution env.`,\n );\n }\n const { label: _label, ...rest } = params as Record<string, unknown>;\n return tool.execute(\n toolCallId,\n rest as typeof params,\n onUpdate,\n toolContext,\n invocation,\n context,\n );\n },\n });\n}\n\nexport function createSandboxTools(): MikanHarnessTool[] {\n const bash = createBashTool({\n prepare: (execution, toolContext) => {\n execution.cwd = toolContext.env.cwd;\n },\n });\n return [\n withLabel(createReadTool() as MikanHarnessTool),\n withLabel(createWriteTool() as MikanHarnessTool),\n withLabel(createEditTool() as MikanHarnessTool),\n withLabel(bash as MikanHarnessTool),\n ];\n}\n\nexport function adaptAgentTool(tool: AgentTool): MikanHarnessTool {\n return tagHarnessTool({\n ...tool,\n execute: (...args: Parameters<MikanHarnessTool[\"execute\"]>) => {\n const [toolCallId, params, onUpdate, , , context] = args;\n return tool.execute(toolCallId, params, context.abortSignal, onUpdate);\n },\n } as MikanHarnessTool);\n}\n"]}
1
+ {"version":3,"file":"pi-tools.js","sourceRoot":"","sources":["../../../src/harness/tools/pi-tools.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { JsonValue } from \"@earendil-works/chord\";\nimport type { ToolRegistration } from \"@earendil-works/pi-durable\";\nimport {\n createBashTool,\n createEditTool,\n createReadTool,\n createWriteTool,\n} from \"@earendil-works/pi-durable/tools\";\nimport type { TObject } from \"typebox\";\nimport type { MikanHarnessTool } from \"../types.js\";\nimport { LABEL_PARAMETER } from \"./host-fn-tool.js\";\n\nexport type { MikanHarnessTool };\n\nconst HARNESS_TOOL = Symbol.for(\"mikan.harnessTool\");\n\nexport function isHarnessTool(tool: unknown): tool is MikanHarnessTool {\n return Boolean(tool && (tool as Record<PropertyKey, unknown>)[HARNESS_TOOL]);\n}\n\nexport function tagHarnessTool(tool: MikanHarnessTool): MikanHarnessTool {\n Object.defineProperty(tool, HARNESS_TOOL, { value: true });\n return tool;\n}\n\nfunction toJsonValue(input: unknown): JsonValue | undefined {\n if (input === undefined) return undefined;\n const copy: JsonValue = JSON.parse(JSON.stringify(input));\n return copy;\n}\n\nfunction withLabel(tool: ToolRegistration): MikanHarnessTool {\n const schema = tool.parameters as TObject;\n return tagHarnessTool({\n ...tool,\n parameters: {\n ...schema,\n properties: { ...schema.properties, label: LABEL_PARAMETER },\n },\n execute: async (args, api, context) => {\n if (!api.env) {\n throw new Error(`Tool ${tool.name} requires an execution env; supply an authorized one.`);\n }\n const { label: _label, ...rest } = args as Record<string, unknown>;\n return tool.execute(rest, api, context);\n },\n });\n}\n\nexport function createSandboxTools(): MikanHarnessTool[] {\n const bash = createBashTool({\n prepare: (execution, api) => {\n if (api.env) execution.cwd = api.env.cwd;\n },\n });\n return [\n withLabel(createReadTool() as ToolRegistration),\n withLabel(createWriteTool() as ToolRegistration),\n withLabel(createEditTool() as ToolRegistration),\n withLabel(bash as ToolRegistration),\n ];\n}\n\nexport function adaptAgentTool(tool: AgentTool): MikanHarnessTool {\n const exposure = \"exposure\" in tool && tool.exposure === \"deferred\" ? \"deferred\" : undefined;\n const namespace =\n \"namespace\" in tool && typeof tool.namespace === \"string\" ? tool.namespace : undefined;\n return tagHarnessTool({\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n outputSchema: tool.outputSchema,\n prepareArguments: tool.prepareArguments,\n executionMode: tool.executionMode,\n exposure,\n namespace,\n replay: tool.replay === \"safe\" ? \"safe\" : \"unsafe\",\n execute: async (args, api, context) => {\n let updates = Promise.resolve();\n const result = await tool.execute(api.callId, args, context.abortSignal, (partial) => {\n const details = toJsonValue(partial.details);\n if (details === undefined) return;\n updates = updates.then(() => api.details(details, context)).catch(() => undefined);\n });\n await updates;\n return {\n content: result.content,\n details: toJsonValue(result.details),\n isError: result.isError,\n structuredContent: result.structuredContent,\n usage: result.usage,\n control: result.terminate ? { terminate: true } : undefined,\n };\n },\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"secret-redaction.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/secret-redaction.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAeD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAW,EAA2B,GAC9C,MAAM,CAQR;AAED,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CAgB5E"}
1
+ {"version":3,"file":"secret-redaction.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/secret-redaction.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAeD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAW,EAA2B,GAC9C,MAAM,CAQR;AAED,YAAY,EAAE,WAAW,EAAE,CAAC;AAkC5B,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CA6B5E"}
@@ -25,12 +25,56 @@ export function redactSecrets(text, secrets = collectSecretEntries()) {
25
25
  }
26
26
  return result;
27
27
  }
28
+ function redactingApi(api, secrets) {
29
+ const keep = Math.max(...secrets.map((secret) => secret.value.length)) - 1;
30
+ const decoder = new TextDecoder();
31
+ let pending = "";
32
+ return {
33
+ api: {
34
+ ...api,
35
+ output: (chunk) => {
36
+ pending = redactSecrets(pending + (typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true })), secrets);
37
+ const cut = pending.length - keep;
38
+ if (cut <= 0)
39
+ return;
40
+ api.output(pending.slice(0, cut));
41
+ pending = pending.slice(cut);
42
+ },
43
+ diagnostic: (diagnostic) => {
44
+ api.diagnostic({ ...diagnostic, message: redactSecrets(diagnostic.message, secrets) });
45
+ },
46
+ },
47
+ flush: () => {
48
+ const rest = redactSecrets(pending + decoder.decode(), secrets);
49
+ pending = "";
50
+ if (rest)
51
+ api.output(rest);
52
+ },
53
+ };
54
+ }
28
55
  export function withSecretRedaction(tool) {
29
56
  const original = tool.execute.bind(tool);
30
- tool.execute = async (...args) => {
31
- const result = await original(...args);
57
+ tool.execute = async (args, api, context) => {
32
58
  const secrets = collectSecretEntries();
33
59
  if (secrets.length === 0)
60
+ return original(args, api, context);
61
+ const redacting = redactingApi(api, secrets);
62
+ let result;
63
+ try {
64
+ result = await original(args, redacting.api, context);
65
+ }
66
+ catch (error) {
67
+ if (error instanceof Error) {
68
+ error.message = redactSecrets(error.message, secrets);
69
+ if (error.stack)
70
+ error.stack = redactSecrets(error.stack, secrets);
71
+ }
72
+ throw error;
73
+ }
74
+ finally {
75
+ redacting.flush();
76
+ }
77
+ if (result.content === undefined)
34
78
  return result;
35
79
  return {
36
80
  ...result,
@@ -1 +1 @@
1
- {"version":3,"file":"secret-redaction.js","sourceRoot":"","sources":["../../../src/harness/tools/secret-redaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAG9D,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAO5B,SAAS,oBAAoB;IAC3B,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,SAAS;YAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,iBAAiB;gBAAE,SAAS;YACzD,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,OAAO,GAAkB,oBAAoB,EAAE;IAE/C,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACtC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAID,MAAM,UAAU,mBAAmB,CAAC,IAAsB;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO,GAAG,KAAK,EAAE,GAAG,IAA6C,EAAE,EAAE;QACxE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC;QACvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QACxC,OAAO;YACL,GAAG,MAAM;YACT,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAA8B,EAAE,CAC/D,IAAI,CAAC,IAAI,KAAK,MAAM;gBAClB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;gBACtE,CAAC,CAAC,IAAI,CACT;SACF,CAAC;IACJ,CAAC,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { ENV_MANIFEST, readEnv } from \"../../env-manifest.js\";\nimport type { MikanHarnessTool } from \"../types.js\";\n\nconst MIN_SECRET_LENGTH = 8;\n\ninterface SecretEntry {\n label: string;\n value: string;\n}\n\nfunction collectSecretEntries(): SecretEntry[] {\n const entries: SecretEntry[] = [];\n for (const group of ENV_MANIFEST) {\n for (const spec of group.vars) {\n if (!spec.secret) continue;\n const value = readEnv(spec.name);\n if (!value || value.length < MIN_SECRET_LENGTH) continue;\n entries.push({ label: spec.name, value });\n }\n }\n return entries.toSorted((a, b) => b.value.length - a.value.length);\n}\n\nexport function redactSecrets(\n text: string,\n secrets: SecretEntry[] = collectSecretEntries(),\n): string {\n if (!text || secrets.length === 0) return text;\n let result = text;\n for (const { label, value } of secrets) {\n if (!result.includes(value)) continue;\n result = result.split(value).join(`[SECRET:${label}]`);\n }\n return result;\n}\n\nexport type { SecretEntry };\n\nexport function withSecretRedaction(tool: MikanHarnessTool): MikanHarnessTool {\n const original = tool.execute.bind(tool);\n tool.execute = async (...args: Parameters<MikanHarnessTool[\"execute\"]>) => {\n const result = await original(...args);\n const secrets = collectSecretEntries();\n if (secrets.length === 0) return result;\n return {\n ...result,\n content: result.content.map((part): TextContent | ImageContent =>\n part.type === \"text\"\n ? Object.assign({}, part, { text: redactSecrets(part.text, secrets) })\n : part,\n ),\n };\n };\n return tool;\n}\n"]}
1
+ {"version":3,"file":"secret-redaction.js","sourceRoot":"","sources":["../../../src/harness/tools/secret-redaction.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { ENV_MANIFEST, readEnv } from \"../../env-manifest.js\";\nimport type { ToolExecutionApi } from \"@earendil-works/pi-durable\";\nimport type { MikanHarnessTool } from \"../types.js\";\n\nconst MIN_SECRET_LENGTH = 8;\n\ninterface SecretEntry {\n label: string;\n value: string;\n}\n\nfunction collectSecretEntries(): SecretEntry[] {\n const entries: SecretEntry[] = [];\n for (const group of ENV_MANIFEST) {\n for (const spec of group.vars) {\n if (!spec.secret) continue;\n const value = readEnv(spec.name);\n if (!value || value.length < MIN_SECRET_LENGTH) continue;\n entries.push({ label: spec.name, value });\n }\n }\n return entries.toSorted((a, b) => b.value.length - a.value.length);\n}\n\nexport function redactSecrets(\n text: string,\n secrets: SecretEntry[] = collectSecretEntries(),\n): string {\n if (!text || secrets.length === 0) return text;\n let result = text;\n for (const { label, value } of secrets) {\n if (!result.includes(value)) continue;\n result = result.split(value).join(`[SECRET:${label}]`);\n }\n return result;\n}\n\nexport type { SecretEntry };\n\nfunction redactingApi(\n api: ToolExecutionApi,\n secrets: SecretEntry[],\n): { api: ToolExecutionApi; flush: () => void } {\n const keep = Math.max(...secrets.map((secret) => secret.value.length)) - 1;\n const decoder = new TextDecoder();\n let pending = \"\";\n return {\n api: {\n ...api,\n output: (chunk) => {\n pending = redactSecrets(\n pending + (typeof chunk === \"string\" ? chunk : decoder.decode(chunk, { stream: true })),\n secrets,\n );\n const cut = pending.length - keep;\n if (cut <= 0) return;\n api.output(pending.slice(0, cut));\n pending = pending.slice(cut);\n },\n diagnostic: (diagnostic) => {\n api.diagnostic({ ...diagnostic, message: redactSecrets(diagnostic.message, secrets) });\n },\n },\n flush: () => {\n const rest = redactSecrets(pending + decoder.decode(), secrets);\n pending = \"\";\n if (rest) api.output(rest);\n },\n };\n}\n\nexport function withSecretRedaction(tool: MikanHarnessTool): MikanHarnessTool {\n const original = tool.execute.bind(tool);\n tool.execute = async (args, api, context) => {\n const secrets = collectSecretEntries();\n if (secrets.length === 0) return original(args, api, context);\n const redacting = redactingApi(api, secrets);\n let result: Awaited<ReturnType<MikanHarnessTool[\"execute\"]>>;\n try {\n result = await original(args, redacting.api, context);\n } catch (error) {\n if (error instanceof Error) {\n error.message = redactSecrets(error.message, secrets);\n if (error.stack) error.stack = redactSecrets(error.stack, secrets);\n }\n throw error;\n } finally {\n redacting.flush();\n }\n if (result.content === undefined) return result;\n return {\n ...result,\n content: result.content.map((part): TextContent | ImageContent =>\n part.type === \"text\"\n ? Object.assign({}, part, { text: redactSecrets(part.text, secrets) })\n : part,\n ),\n };\n };\n return tool;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { MikanHarnessTool, ToolSearchOptions, ToolSearchToolOptions } from "../types.js";
2
+ export declare const TOOL_SEARCH_TOOL = "tool_search";
3
+ export declare function searchTools(options: ToolSearchOptions): MikanHarnessTool[];
4
+ export declare function createToolSearchTool(options: ToolSearchToolOptions): MikanHarnessTool;
5
+ //# sourceMappingURL=tool-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-search.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/tool-search.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAI9F,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAoB9C,wBAAgB,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,EAAE,CA2B1E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,gBAAgB,CAyCrF"}