@iinm/plain-agent 1.8.3 → 1.8.4

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 (86) hide show
  1. package/README.md +2 -2
  2. package/bin/plain +1 -1
  3. package/config/config.predefined.json +1 -1
  4. package/config/prompts.predefined/shortcuts/configure.md +1 -1
  5. package/dist/main.mjs +473 -0
  6. package/dist/main.mjs.map +7 -0
  7. package/package.json +5 -7
  8. package/src/agent.d.ts +0 -52
  9. package/src/agent.mjs +0 -204
  10. package/src/agentLoop.mjs +0 -419
  11. package/src/agentState.mjs +0 -41
  12. package/src/claudeCodePlugin.mjs +0 -164
  13. package/src/cliArgs.mjs +0 -175
  14. package/src/cliBatch.mjs +0 -147
  15. package/src/cliCommands.mjs +0 -283
  16. package/src/cliCompleter.mjs +0 -227
  17. package/src/cliCost.mjs +0 -309
  18. package/src/cliFormatter.mjs +0 -413
  19. package/src/cliInteractive.mjs +0 -529
  20. package/src/cliInterruptTransform.mjs +0 -51
  21. package/src/cliMuteTransform.mjs +0 -26
  22. package/src/cliPasteTransform.mjs +0 -183
  23. package/src/config.d.ts +0 -36
  24. package/src/config.mjs +0 -197
  25. package/src/context/loadAgentRoles.mjs +0 -283
  26. package/src/context/loadPrompts.mjs +0 -324
  27. package/src/context/loadUserMessageContext.mjs +0 -147
  28. package/src/costTracker.mjs +0 -210
  29. package/src/env.mjs +0 -44
  30. package/src/main.mjs +0 -279
  31. package/src/mcpClient.mjs +0 -351
  32. package/src/mcpIntegration.mjs +0 -160
  33. package/src/model.d.ts +0 -109
  34. package/src/modelCaller.mjs +0 -32
  35. package/src/modelDefinition.d.ts +0 -92
  36. package/src/prompt.mjs +0 -138
  37. package/src/providers/anthropic.d.ts +0 -248
  38. package/src/providers/anthropic.mjs +0 -587
  39. package/src/providers/bedrock.d.ts +0 -249
  40. package/src/providers/bedrock.mjs +0 -700
  41. package/src/providers/gemini.d.ts +0 -208
  42. package/src/providers/gemini.mjs +0 -754
  43. package/src/providers/openai.d.ts +0 -281
  44. package/src/providers/openai.mjs +0 -544
  45. package/src/providers/openaiCompatible.d.ts +0 -147
  46. package/src/providers/openaiCompatible.mjs +0 -652
  47. package/src/providers/platform/awsSigV4.mjs +0 -184
  48. package/src/providers/platform/azure.mjs +0 -42
  49. package/src/providers/platform/bedrock.mjs +0 -78
  50. package/src/providers/platform/googleCloud.mjs +0 -34
  51. package/src/subagent.mjs +0 -265
  52. package/src/tmpfile.mjs +0 -27
  53. package/src/tool.d.ts +0 -74
  54. package/src/toolExecutor.mjs +0 -236
  55. package/src/toolInputValidator.mjs +0 -183
  56. package/src/toolUseApprover.mjs +0 -99
  57. package/src/tools/askURL.mjs +0 -209
  58. package/src/tools/askWeb.mjs +0 -208
  59. package/src/tools/compactContext.d.ts +0 -4
  60. package/src/tools/compactContext.mjs +0 -87
  61. package/src/tools/delegateToSubagent.d.ts +0 -4
  62. package/src/tools/delegateToSubagent.mjs +0 -48
  63. package/src/tools/execCommand.d.ts +0 -22
  64. package/src/tools/execCommand.mjs +0 -200
  65. package/src/tools/patchFile.d.ts +0 -4
  66. package/src/tools/patchFile.mjs +0 -133
  67. package/src/tools/reportAsSubagent.d.ts +0 -3
  68. package/src/tools/reportAsSubagent.mjs +0 -44
  69. package/src/tools/tmuxCommand.d.ts +0 -14
  70. package/src/tools/tmuxCommand.mjs +0 -194
  71. package/src/tools/writeFile.d.ts +0 -4
  72. package/src/tools/writeFile.mjs +0 -56
  73. package/src/usageStore.mjs +0 -167
  74. package/src/utils/evalJSONConfig.mjs +0 -72
  75. package/src/utils/matchValue.d.ts +0 -6
  76. package/src/utils/matchValue.mjs +0 -40
  77. package/src/utils/noThrow.mjs +0 -31
  78. package/src/utils/notify.mjs +0 -29
  79. package/src/utils/parseFileRange.mjs +0 -18
  80. package/src/utils/readFileRange.mjs +0 -33
  81. package/src/utils/retryOnError.mjs +0 -41
  82. package/src/voiceInput.mjs +0 -61
  83. package/src/voiceInputGemini.mjs +0 -105
  84. package/src/voiceInputOpenAI.mjs +0 -104
  85. package/src/voiceInputSession.mjs +0 -543
  86. package/src/voiceToggleKey.mjs +0 -62
@@ -1,184 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { createHash, createHmac } from "node:crypto";
3
-
4
- /**
5
- * @typedef {{ accessKeyId: string, secretAccessKey: string, sessionToken?: string }} AwsCredentials
6
- */
7
-
8
- /** @type {Map<string, { credentials: AwsCredentials, expiration: Date }>} */
9
- const credentialCache = new Map();
10
-
11
- const EXPIRATION_MARGIN_MS = 60 * 1000;
12
-
13
- /**
14
- * Load AWS credentials for the given profile using the AWS CLI.
15
- * Results are cached and reused until the credentials expire.
16
- * @param {string} profile
17
- * @returns {Promise<AwsCredentials>}
18
- */
19
- export async function loadAwsCredentials(profile) {
20
- const cached = credentialCache.get(profile);
21
- if (
22
- cached &&
23
- Date.now() < cached.expiration.getTime() - EXPIRATION_MARGIN_MS
24
- ) {
25
- return cached.credentials;
26
- }
27
-
28
- /** @type {string} */
29
- const stdout = await new Promise((resolve, reject) => {
30
- execFile(
31
- "aws",
32
- ["configure", "export-credentials", "--profile", profile],
33
- {
34
- shell: false,
35
- timeout: 30 * 1000,
36
- },
37
- (error, stdout, _stderr) => {
38
- if (error) {
39
- reject(error);
40
- return;
41
- }
42
- resolve(stdout.trim());
43
- },
44
- );
45
- });
46
- const parsed = JSON.parse(stdout);
47
- for (const key of ["AccessKeyId", "SecretAccessKey"]) {
48
- if (!parsed[key] || typeof parsed[key] !== "string") {
49
- throw new Error(
50
- `AWS credentials output missing ${key}. Raw output: ${stdout.slice(0, 200)}`,
51
- );
52
- }
53
- }
54
- const credentials = {
55
- accessKeyId: parsed.AccessKeyId,
56
- secretAccessKey: parsed.SecretAccessKey,
57
- ...(parsed.SessionToken && { sessionToken: parsed.SessionToken }),
58
- };
59
-
60
- if (parsed.Expiration) {
61
- const expiration = new Date(parsed.Expiration);
62
- if (!Number.isNaN(expiration.getTime())) {
63
- credentialCache.set(profile, { credentials, expiration });
64
- }
65
- }
66
-
67
- return credentials;
68
- }
69
-
70
- /**
71
- * Sign an HTTP request with AWS Signature V4.
72
- *
73
- * Known limitation: if duplicate header names with different casing exist
74
- * (e.g. `Content-Type` and `content-type`), only the first match is used.
75
- * Per AWS SigV4 spec, values should be combined with commas. Callers should
76
- * ensure header names are unique (case-insensitive) before signing.
77
- *
78
- * @param {{
79
- * method: string,
80
- * hostname: string,
81
- * path: string,
82
- * headers: Record<string, string>,
83
- * body: string,
84
- * }} request
85
- * @param {{
86
- * region: string,
87
- * service: string,
88
- * credentials: AwsCredentials,
89
- * }} options
90
- * @returns {{ method: string, headers: Record<string, string>, body: string }}
91
- */
92
- export function signAwsRequest(request, options) {
93
- const { method, hostname, path, headers, body } = request;
94
- const { region, service, credentials } = options;
95
-
96
- const now = new Date();
97
- const amzDate = now
98
- .toISOString()
99
- .replace(/[-:]/g, "")
100
- .replace(/\.\d{3}/, "");
101
- const dateStamp = amzDate.slice(0, 8);
102
-
103
- /** @type {Record<string, string>} */
104
- const signedHeaders = { ...headers, host: hostname, "x-amz-date": amzDate };
105
- if (credentials.sessionToken) {
106
- signedHeaders["x-amz-security-token"] = credentials.sessionToken;
107
- }
108
-
109
- // Canonical headers: sorted, lowercased, trimmed
110
- const sortedKeys = Object.keys(signedHeaders)
111
- .map((k) => k.toLowerCase())
112
- .sort();
113
- const canonicalHeaders = sortedKeys
114
- .map((k) => {
115
- const original = Object.keys(signedHeaders).find(
116
- (h) => h.toLowerCase() === k,
117
- );
118
- return `${k}:${signedHeaders[/** @type {string} */ (original)].trim()}`;
119
- })
120
- .join("\n");
121
- const signedHeadersList = sortedKeys.join(";");
122
-
123
- const payloadHash = sha256Hex(body || "");
124
-
125
- // AWS SigV4 Canonical URI requires each path segment to be URI-encoded.
126
- // URL.pathname returns a decoded string, so we need to re-encode it.
127
- const canonicalUri = path
128
- .split("/")
129
- .map((segment) => encodeURIComponent(segment))
130
- .join("/");
131
-
132
- const canonicalRequest = [
133
- method,
134
- canonicalUri,
135
- "", // query string (empty for POST)
136
- `${canonicalHeaders}\n`,
137
- signedHeadersList,
138
- payloadHash,
139
- ].join("\n");
140
-
141
- // String to sign
142
- const scope = `${dateStamp}/${region}/${service}/aws4_request`;
143
- const stringToSign = [
144
- "AWS4-HMAC-SHA256",
145
- amzDate,
146
- scope,
147
- sha256Hex(canonicalRequest),
148
- ].join("\n");
149
-
150
- // Signing key
151
- const kDate = hmacSha256(`AWS4${credentials.secretAccessKey}`, dateStamp);
152
- const kRegion = hmacSha256(kDate, region);
153
- const kService = hmacSha256(kRegion, service);
154
- const kSigning = hmacSha256(kService, "aws4_request");
155
-
156
- const signature = createHmac("sha256", kSigning)
157
- .update(stringToSign, "utf-8")
158
- .digest("hex");
159
-
160
- const authorization = `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeadersList}, Signature=${signature}`;
161
-
162
- return {
163
- method,
164
- headers: { ...signedHeaders, Authorization: authorization },
165
- body,
166
- };
167
- }
168
-
169
- /**
170
- * @param {string} data
171
- * @returns {string}
172
- */
173
- function sha256Hex(data) {
174
- return createHash("sha256").update(data, "utf-8").digest("hex");
175
- }
176
-
177
- /**
178
- * @param {string | Buffer} key
179
- * @param {string} data
180
- * @returns {Buffer}
181
- */
182
- function hmacSha256(key, data) {
183
- return createHmac("sha256", key).update(data, "utf-8").digest();
184
- }
@@ -1,42 +0,0 @@
1
- import { execFile } from "node:child_process";
2
-
3
- /**
4
- * @param {{azureConfigDir: string}=} config
5
- * @returns {Promise<string>}
6
- */
7
- export async function getAzureAccessToken(config) {
8
- /** @type {string} */
9
- const stdout = await new Promise((resolve, reject) => {
10
- execFile(
11
- "az",
12
- [
13
- "account",
14
- "get-access-token",
15
- "--resource",
16
- "https://cognitiveservices.azure.com",
17
- "--query",
18
- "accessToken",
19
- "--output",
20
- "tsv",
21
- ],
22
- {
23
- shell: false,
24
- timeout: 10 * 1000,
25
- env: config
26
- ? {
27
- AZURE_CONFIG_DIR: config.azureConfigDir,
28
- }
29
- : undefined,
30
- },
31
- (error, stdout, _stderr) => {
32
- if (error) {
33
- reject(error);
34
- return;
35
- }
36
- resolve(stdout.trim());
37
- },
38
- );
39
- });
40
-
41
- return stdout;
42
- }
@@ -1,78 +0,0 @@
1
- import { styleText } from "node:util";
2
-
3
- /**
4
- * @param {ReadableStreamDefaultReader<Uint8Array>} reader
5
- */
6
- export async function* readBedrockStreamEvents(reader) {
7
- let buffer = new Uint8Array();
8
-
9
- while (true) {
10
- const { done, value } = await reader.read();
11
- if (done) {
12
- break;
13
- }
14
-
15
- const nextBuffer = new Uint8Array(buffer.length + value.length);
16
- nextBuffer.set(buffer);
17
- nextBuffer.set(value, buffer.length);
18
- buffer = nextBuffer;
19
-
20
- // AWS event stream format
21
- // https://github.com/awslabs/aws-c-event-stream/blob/main/docs/images/encoding.png
22
- while (buffer.length >= 12) {
23
- const view = new DataView(
24
- buffer.buffer,
25
- buffer.byteOffset,
26
- buffer.byteLength,
27
- );
28
- const totalLength = view.getUint32(0);
29
- const headersLength = view.getUint32(4);
30
-
31
- if (buffer.length < totalLength) {
32
- break;
33
- }
34
-
35
- const payloadOffset = 12 + headersLength;
36
- // prelude 12 bytes + CRC 4 bytes = 16
37
- const payloadLength = totalLength - headersLength - 16;
38
- const payloadRaw = buffer.slice(
39
- payloadOffset,
40
- payloadOffset + payloadLength,
41
- );
42
-
43
- const payloadDecoded = new TextDecoder().decode(payloadRaw);
44
- try {
45
- const payloadParsed = JSON.parse(payloadDecoded);
46
- if (payloadParsed.bytes) {
47
- // Invoke API format (base64 encoded event)
48
- const event = Buffer.from(payloadParsed.bytes, "base64").toString(
49
- "utf-8",
50
- );
51
- const eventParsed = JSON.parse(event);
52
- yield eventParsed;
53
- } else if (payloadParsed.message) {
54
- console.error(
55
- styleText(
56
- "yellow",
57
- `Bedrock message received: ${JSON.stringify(payloadParsed.message)}`,
58
- ),
59
- );
60
- } else {
61
- // Converse API format (direct event data)
62
- yield payloadParsed;
63
- }
64
- } catch (err) {
65
- if (err instanceof Error) {
66
- console.error(
67
- styleText(
68
- "red",
69
- `Error decoding payload: ${err.message}\nPayload: ${payloadDecoded}`,
70
- ),
71
- );
72
- }
73
- }
74
-
75
- buffer = buffer.slice(totalLength);
76
- }
77
- }
78
- }
@@ -1,34 +0,0 @@
1
- import { execFile } from "node:child_process";
2
-
3
- /**
4
- * @param {string=} account
5
- * @returns {Promise<string>}
6
- */
7
- export async function getGoogleCloudAccessToken(account) {
8
- const accountOption = account?.endsWith(".iam.gserviceaccount.com")
9
- ? ["--impersonate-service-account", account]
10
- : account
11
- ? [account]
12
- : [];
13
-
14
- /** @type {string} */
15
- const stdout = await new Promise((resolve, reject) => {
16
- execFile(
17
- "gcloud",
18
- ["auth", "print-access-token", ...accountOption],
19
- {
20
- shell: false,
21
- timeout: 10 * 1000,
22
- },
23
- (error, stdout, _stderr) => {
24
- if (error) {
25
- reject(error);
26
- return;
27
- }
28
- resolve(stdout.trim());
29
- },
30
- );
31
- });
32
-
33
- return stdout;
34
- }
package/src/subagent.mjs DELETED
@@ -1,265 +0,0 @@
1
- /**
2
- * @import { Message, MessageContentToolResult, MessageContentToolUse } from "./model"
3
- * @import { ReportAsSubagentInput } from "./tools/reportAsSubagent"
4
- * @import { AgentRole } from "./context/loadAgentRoles.mjs"
5
- */
6
-
7
- import fs from "node:fs/promises";
8
- import path from "node:path";
9
- import { AGENT_PROJECT_METADATA_DIR } from "./env.mjs";
10
- import { CLAUDE_CODE_COMPATIBILITY_NOTES } from "./prompt.mjs";
11
- import { reportAsSubagentToolName } from "./tools/reportAsSubagent.mjs";
12
-
13
- /** @typedef {ReturnType<typeof createSubagentManager>} SubagentManager */
14
-
15
- /**
16
- * @typedef {Object} SubagentStateEventHandlers
17
- * @property {(subagent: {name:string} | null) => void} onSubagentSwitched
18
- */
19
-
20
- /**
21
- * Creates a manager for subagent lifecycle and state.
22
- * @param {Map<string, AgentRole>} agentRoles
23
- * @param {SubagentStateEventHandlers} handlers
24
- */
25
- export function createSubagentManager(agentRoles, handlers) {
26
- /** @type {{name: string; goal: string; delegationMessageIndex: number}[]} */
27
- const subagents = [];
28
- let subagentCount = 0;
29
-
30
- /**
31
- * @typedef {DelegateSuccess | DelegateFailure} DelegateResult
32
- */
33
-
34
- /**
35
- * @typedef {Object} DelegateSuccess
36
- * @property {true} success
37
- * @property {string} value
38
- */
39
-
40
- /**
41
- * @typedef {Object} DelegateFailure
42
- * @property {false} success
43
- * @property {string} error
44
- */
45
-
46
- /**
47
- * Delegate a task to a subagent.
48
- * @param {string} name
49
- * @param {string} goal
50
- * @param {number} delegationMessageIndex
51
- * @returns {DelegateResult}
52
- */
53
- function delegateToSubagent(name, goal, delegationMessageIndex) {
54
- if (subagents.length > 0) {
55
- return {
56
- success: false,
57
- error:
58
- "Cannot call delegate_to_subagent while already acting as a subagent.",
59
- };
60
- }
61
-
62
- const isCustomRole = name.startsWith("custom:");
63
- const actualName = isCustomRole ? name.substring(7) : name;
64
-
65
- let roleContent = "";
66
- if (!isCustomRole) {
67
- const role = agentRoles.get(name);
68
- if (!role) {
69
- const availableRoles = Array.from(agentRoles.keys())
70
- .sort()
71
- .map((id) => ` - ${id}`)
72
- .join("\n");
73
- return {
74
- success: false,
75
- error: `Agent role "${name}" not found. Available agent roles:\n${availableRoles}\n\nTo use an ad-hoc role, prefix the name with "custom:" (e.g., "custom:researcher").`,
76
- };
77
- }
78
- roleContent = role.claudeOriginated
79
- ? `${role.content}\n\n---\n\n${CLAUDE_CODE_COMPATIBILITY_NOTES}`
80
- : role.content;
81
- }
82
-
83
- subagentCount++;
84
- const sequenceNumber = String(subagentCount).padStart(2, "0");
85
-
86
- subagents.push({
87
- name: actualName,
88
- goal,
89
- delegationMessageIndex,
90
- });
91
- handlers.onSubagentSwitched({ name: actualName });
92
-
93
- return {
94
- success: true,
95
- value: [
96
- `[SUBAGENT MODE ACTIVATED] You are now operating as the subagent "${actualName}".`,
97
- roleContent
98
- ? `Role: ${actualName}\n---\n${roleContent}\n---`
99
- : `Role: ${actualName}`,
100
- `Your goal: ${goal}`,
101
- `Memory file path format: ${AGENT_PROJECT_METADATA_DIR}/memory/<session-id>--${sequenceNumber}--${actualName.replace("/", "-")}--<kebab-case-title>.md (Replace <kebab-case-title> with a short title describing your own goal)`,
102
- `When finished, call "report_as_subagent" with the memory file path. Start executing your goal now.`,
103
- ].join("\n\n"),
104
- };
105
- }
106
-
107
- /**
108
- * @typedef {ReportSuccess | ReportFailure} ReportResult
109
- */
110
-
111
- /**
112
- * @typedef {Object} ReportSuccess
113
- * @property {true} success
114
- * @property {string} memoryContent
115
- */
116
-
117
- /**
118
- * @typedef {Object} ReportFailure
119
- * @property {false} success
120
- * @property {string} error
121
- */
122
-
123
- /**
124
- * Report as a subagent and read the memory file.
125
- * @param {string} memoryPath
126
- * @returns {Promise<ReportResult>}
127
- */
128
- async function reportAsSubagent(memoryPath) {
129
- if (subagents.length === 0) {
130
- return {
131
- success: false,
132
- error: "Cannot call report_as_subagent from the main agent.",
133
- };
134
- }
135
-
136
- const absolutePath = path.resolve(memoryPath);
137
- const memoryDir = path.resolve(AGENT_PROJECT_METADATA_DIR, "memory");
138
- const relativePath = path.relative(memoryDir, absolutePath);
139
- if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
140
- return {
141
- success: false,
142
- error: `Access denied: memoryPath must be within ${AGENT_PROJECT_METADATA_DIR}/memory`,
143
- };
144
- }
145
-
146
- try {
147
- const memoryContent = await fs.readFile(absolutePath, {
148
- encoding: "utf-8",
149
- });
150
- return {
151
- success: true,
152
- memoryContent: memoryContent,
153
- };
154
- } catch (error) {
155
- return {
156
- success: false,
157
- error: `Failed to read memory file: ${error instanceof Error ? error.message : String(error)}`,
158
- };
159
- }
160
- }
161
-
162
- /**
163
- * Process tool results and update state based on special tools.
164
- * Returns the truncated message history and a new message to add.
165
- * @param {MessageContentToolUse[]} toolUseParts
166
- * @param {MessageContentToolResult[]} toolResults
167
- * @param {Message[]} messages
168
- * @returns {{ messages: Message[], newMessage: Message | null }}
169
- * - messages: The potentially truncated message history (new array)
170
- * - newMessage: The user message to add, or null if tool results should be added directly
171
- */
172
- function processToolResults(toolUseParts, toolResults, messages) {
173
- const reportSubagentToolUse = toolUseParts.find(
174
- (toolUse) => toolUse.toolName === reportAsSubagentToolName,
175
- );
176
-
177
- if (reportSubagentToolUse) {
178
- const reportResult = toolResults.find(
179
- (res) => res.toolUseId === reportSubagentToolUse.toolUseId,
180
- );
181
- if (!reportResult) {
182
- return { messages, newMessage: null };
183
- }
184
- return handleSubagentReport(
185
- reportSubagentToolUse,
186
- reportResult,
187
- messages,
188
- );
189
- }
190
-
191
- return { messages, newMessage: null };
192
- }
193
-
194
- /**
195
- * Handle the result of a subagent reporting back.
196
- * On success, truncates conversation history back to the delegation point
197
- * and converts the report into a standard user message.
198
- * @param {MessageContentToolUse} reportToolUse
199
- * @param {MessageContentToolResult} reportResult
200
- * @param {Message[]} messages
201
- * @returns {{ messages: Message[], newMessage: Message | null }}
202
- * - messages: The truncated message history (new array)
203
- * - newMessage: The user message to add, or null if not handled
204
- */
205
- function handleSubagentReport(reportToolUse, reportResult, messages) {
206
- if (reportResult.isError) {
207
- return { messages, newMessage: null };
208
- }
209
-
210
- const currentSubagent = subagents.pop();
211
- if (!currentSubagent) {
212
- return { messages, newMessage: null };
213
- }
214
-
215
- handlers.onSubagentSwitched(subagents.at(-1) ?? null);
216
-
217
- // Truncate history back to the delegation point
218
- const truncatedMessages = messages.slice(
219
- 0,
220
- currentSubagent.delegationMessageIndex,
221
- );
222
-
223
- // Convert the tool result into a standard user message
224
- const resultText = reportResult.content
225
- .map((c) => (c.type === "text" ? c.text : ""))
226
- .join("\n\n");
227
-
228
- const reportInput = /** @type {ReportAsSubagentInput} */ (
229
- reportToolUse.input
230
- );
231
-
232
- /** @type {import('./model').UserMessage} */
233
- const newMessage = {
234
- role: "user",
235
- content: [
236
- {
237
- type: "text",
238
- text: [
239
- `The subagent "${currentSubagent.name}" has completed the task.`,
240
- `Goal: ${currentSubagent.goal}`,
241
- `Memory file: ${reportInput.memoryPath}`,
242
- `Result:\n${resultText}`,
243
- ].join("\n\n"),
244
- },
245
- ],
246
- };
247
-
248
- return { messages: truncatedMessages, newMessage };
249
- }
250
-
251
- /**
252
- * Whether the main agent is currently running as a subagent.
253
- * @returns {boolean}
254
- */
255
- function isSubagentActive() {
256
- return subagents.length > 0;
257
- }
258
-
259
- return {
260
- delegateToSubagent,
261
- reportAsSubagent,
262
- processToolResults,
263
- isSubagentActive,
264
- };
265
- }
package/src/tmpfile.mjs DELETED
@@ -1,27 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { AGENT_TMP_DIR } from "./env.mjs";
4
-
5
- /**
6
- * Write content to a temporary file and return the file path
7
- * @param {string} content - Content to write
8
- * @param {string} name - File name (e.g., "read_web_page")
9
- * @param {string} extension - File extension (e.g., "md", "txt")
10
- * @returns {Promise<string>} Path to the created temporary file
11
- */
12
- export async function writeTmpFile(content, name, extension = "txt") {
13
- const timestamp = new Date()
14
- .toISOString()
15
- .slice(0, 19)
16
- .replace("T", "-")
17
- .replace(/:/g, "");
18
- const randomSuffix = Math.random().toString(36).substring(2, 8);
19
-
20
- const fileName = `${timestamp}-${randomSuffix}--${name}.${extension}`;
21
- const filePath = path.join(AGENT_TMP_DIR, fileName);
22
-
23
- await fs.mkdir(AGENT_TMP_DIR, { recursive: true });
24
- await fs.writeFile(filePath, content, "utf8");
25
-
26
- return filePath;
27
- }
package/src/tool.d.ts DELETED
@@ -1,74 +0,0 @@
1
- import type { MessageContentToolUse } from "./model";
2
-
3
- export type Tool = {
4
- def: ToolDefinition;
5
- impl: ToolImplementation;
6
- validateInput?: (input: Record<string, unknown>) => Error | undefined;
7
- maskApprovalInput?: (
8
- input: Record<string, unknown>,
9
- ) => Record<string, unknown>;
10
- injectImpl?: (impl: ToolImplementation) => void;
11
- };
12
-
13
- export type ToolDefinition = {
14
- name: string;
15
- description: string;
16
- inputSchema: Record<string, unknown>;
17
- };
18
-
19
- export type ToolImplementation = (
20
- input: Record,
21
- ) => Promise<string | StructuredToolResultContent[] | Error>;
22
-
23
- export type StructuredToolResultContent =
24
- | {
25
- type: "text";
26
- text: string;
27
- }
28
- | {
29
- type: "image";
30
- // base64 encoded
31
- data: string;
32
- // e.g., image/jpeg
33
- mimeType: string;
34
- };
35
-
36
- export type ToolUseApproverConfig = {
37
- patterns: ToolUsePattern[];
38
- maxApprovals: number;
39
- defaultAction: "deny" | "ask";
40
-
41
- /**
42
- * Mask the input before auto-approval checks and recording.
43
- * Return a redacted object (e.g., keep only necessary fields) that will be used for:
44
- * - safety validation via isSafeToolInput
45
- * - storing per-session allowed tool-use patterns
46
- */
47
- maskApprovalInput: (
48
- toolName: string,
49
- input: Record<string, unknown>,
50
- ) => Record<string, unknown>;
51
- };
52
-
53
- export type ToolUseDecision = {
54
- action: "allow" | "deny" | "ask";
55
- reason?: string;
56
- };
57
-
58
- export type ToolUseApprover = {
59
- isAllowedToolUse: (toolUse: MessageContentToolUse) => ToolUseDecision;
60
- allowToolUse: (toolUse: MessageContentToolUse) => void;
61
- resetApprovalCount: () => void;
62
- };
63
-
64
- export type ToolUsePattern = {
65
- toolName: ValuePattern;
66
- input?: ObjectPattern;
67
- action?: "allow" | "deny" | "ask";
68
- reason?: string;
69
- };
70
-
71
- export type ToolUse = {
72
- toolName: string;
73
- input: Record<string, unknown>;
74
- };