@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.
- package/README.md +2 -2
- package/bin/plain +1 -1
- package/config/config.predefined.json +1 -1
- package/config/prompts.predefined/shortcuts/configure.md +1 -1
- package/dist/main.mjs +473 -0
- package/dist/main.mjs.map +7 -0
- package/package.json +5 -7
- package/src/agent.d.ts +0 -52
- package/src/agent.mjs +0 -204
- package/src/agentLoop.mjs +0 -419
- package/src/agentState.mjs +0 -41
- package/src/claudeCodePlugin.mjs +0 -164
- package/src/cliArgs.mjs +0 -175
- package/src/cliBatch.mjs +0 -147
- package/src/cliCommands.mjs +0 -283
- package/src/cliCompleter.mjs +0 -227
- package/src/cliCost.mjs +0 -309
- package/src/cliFormatter.mjs +0 -413
- package/src/cliInteractive.mjs +0 -529
- package/src/cliInterruptTransform.mjs +0 -51
- package/src/cliMuteTransform.mjs +0 -26
- package/src/cliPasteTransform.mjs +0 -183
- package/src/config.d.ts +0 -36
- package/src/config.mjs +0 -197
- package/src/context/loadAgentRoles.mjs +0 -283
- package/src/context/loadPrompts.mjs +0 -324
- package/src/context/loadUserMessageContext.mjs +0 -147
- package/src/costTracker.mjs +0 -210
- package/src/env.mjs +0 -44
- package/src/main.mjs +0 -279
- package/src/mcpClient.mjs +0 -351
- package/src/mcpIntegration.mjs +0 -160
- package/src/model.d.ts +0 -109
- package/src/modelCaller.mjs +0 -32
- package/src/modelDefinition.d.ts +0 -92
- package/src/prompt.mjs +0 -138
- package/src/providers/anthropic.d.ts +0 -248
- package/src/providers/anthropic.mjs +0 -587
- package/src/providers/bedrock.d.ts +0 -249
- package/src/providers/bedrock.mjs +0 -700
- package/src/providers/gemini.d.ts +0 -208
- package/src/providers/gemini.mjs +0 -754
- package/src/providers/openai.d.ts +0 -281
- package/src/providers/openai.mjs +0 -544
- package/src/providers/openaiCompatible.d.ts +0 -147
- package/src/providers/openaiCompatible.mjs +0 -652
- package/src/providers/platform/awsSigV4.mjs +0 -184
- package/src/providers/platform/azure.mjs +0 -42
- package/src/providers/platform/bedrock.mjs +0 -78
- package/src/providers/platform/googleCloud.mjs +0 -34
- package/src/subagent.mjs +0 -265
- package/src/tmpfile.mjs +0 -27
- package/src/tool.d.ts +0 -74
- package/src/toolExecutor.mjs +0 -236
- package/src/toolInputValidator.mjs +0 -183
- package/src/toolUseApprover.mjs +0 -99
- package/src/tools/askURL.mjs +0 -209
- package/src/tools/askWeb.mjs +0 -208
- package/src/tools/compactContext.d.ts +0 -4
- package/src/tools/compactContext.mjs +0 -87
- package/src/tools/delegateToSubagent.d.ts +0 -4
- package/src/tools/delegateToSubagent.mjs +0 -48
- package/src/tools/execCommand.d.ts +0 -22
- package/src/tools/execCommand.mjs +0 -200
- package/src/tools/patchFile.d.ts +0 -4
- package/src/tools/patchFile.mjs +0 -133
- package/src/tools/reportAsSubagent.d.ts +0 -3
- package/src/tools/reportAsSubagent.mjs +0 -44
- package/src/tools/tmuxCommand.d.ts +0 -14
- package/src/tools/tmuxCommand.mjs +0 -194
- package/src/tools/writeFile.d.ts +0 -4
- package/src/tools/writeFile.mjs +0 -56
- package/src/usageStore.mjs +0 -167
- package/src/utils/evalJSONConfig.mjs +0 -72
- package/src/utils/matchValue.d.ts +0 -6
- package/src/utils/matchValue.mjs +0 -40
- package/src/utils/noThrow.mjs +0 -31
- package/src/utils/notify.mjs +0 -29
- package/src/utils/parseFileRange.mjs +0 -18
- package/src/utils/readFileRange.mjs +0 -33
- package/src/utils/retryOnError.mjs +0 -41
- package/src/voiceInput.mjs +0 -61
- package/src/voiceInputGemini.mjs +0 -105
- package/src/voiceInputOpenAI.mjs +0 -104
- package/src/voiceInputSession.mjs +0 -543
- package/src/voiceToggleKey.mjs +0 -62
package/src/agentState.mjs
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @import { Message } from "./model"
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {ReturnType<typeof createStateManager>} StateManager
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Object} StateEventHandlers
|
|
11
|
-
* @property {(messages: Message[]) => void} onMessagesAppended
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Creates a state manager for message handling.
|
|
16
|
-
* @param {Message[]} initialMessages
|
|
17
|
-
* @param {StateEventHandlers} handlers
|
|
18
|
-
*/
|
|
19
|
-
export function createStateManager(initialMessages, handlers) {
|
|
20
|
-
/** @type {Message[]} */
|
|
21
|
-
let messages = [...initialMessages];
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
/** Get all messages (immutable copy) */
|
|
25
|
-
getMessages: () => [...messages],
|
|
26
|
-
|
|
27
|
-
/** Get message at specific index (supports -1 for last) */
|
|
28
|
-
getMessageAt: /** @param {number} index */ (index) => messages.at(index),
|
|
29
|
-
|
|
30
|
-
/** Append messages */
|
|
31
|
-
appendMessages: /** @param {Message[]} newMessages */ (newMessages) => {
|
|
32
|
-
messages = [...messages, ...newMessages];
|
|
33
|
-
handlers.onMessagesAppended(newMessages);
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
/** Replace all messages */
|
|
37
|
-
setMessages: /** @param {Message[]} newMessages */ (newMessages) => {
|
|
38
|
-
messages = [...newMessages];
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
package/src/claudeCodePlugin.mjs
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { loadAppConfig } from "./config.mjs";
|
|
5
|
-
import { CLAUDE_CODE_PLUGIN_DIR } from "./env.mjs";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} ClaudeCodePluginRepo
|
|
9
|
-
* @property {string} source
|
|
10
|
-
* @property {Array<{name: string, path: string, only?: string}>} plugins
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {Object} ClaudeCodePlugin
|
|
15
|
-
* @property {string} name
|
|
16
|
-
* @property {string} path
|
|
17
|
-
* @property {RegExp} [only]
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Resolve plugin paths from hierarchical config structure.
|
|
22
|
-
* Converts {source, plugins} to flat {name, path, only} with full paths.
|
|
23
|
-
* @param {ClaudeCodePluginRepo[]} repos
|
|
24
|
-
* @returns {ClaudeCodePlugin[]}
|
|
25
|
-
*/
|
|
26
|
-
export function resolvePluginPaths(repos) {
|
|
27
|
-
if (!repos) return [];
|
|
28
|
-
|
|
29
|
-
/** @type {ClaudeCodePlugin[]} */
|
|
30
|
-
const result = [];
|
|
31
|
-
|
|
32
|
-
for (const repo of repos) {
|
|
33
|
-
const ownerRepo = extractOwnerRepo(repo.source);
|
|
34
|
-
if (!ownerRepo) {
|
|
35
|
-
console.warn(`Invalid source URL: ${repo.source}`);
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
for (const plugin of repo.plugins) {
|
|
40
|
-
// Compile only pattern to RegExp
|
|
41
|
-
let only;
|
|
42
|
-
if (plugin.only) {
|
|
43
|
-
try {
|
|
44
|
-
only = new RegExp(plugin.only);
|
|
45
|
-
} catch (err) {
|
|
46
|
-
console.warn(
|
|
47
|
-
`Invalid regex pattern "${plugin.only}" for plugin "${plugin.name}":`,
|
|
48
|
-
err instanceof Error ? err.message : String(err),
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
result.push({
|
|
54
|
-
name: plugin.name,
|
|
55
|
-
path: path.join(CLAUDE_CODE_PLUGIN_DIR, ownerRepo, plugin.path),
|
|
56
|
-
only,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Install Claude Code plugins by cloning repositories.
|
|
66
|
-
*/
|
|
67
|
-
export async function installClaudeCodePlugins() {
|
|
68
|
-
const { appConfig } = await loadAppConfig({ skipTrustCheck: true });
|
|
69
|
-
const repos = appConfig.claudeCodePlugins ?? [];
|
|
70
|
-
|
|
71
|
-
if (repos.length === 0) {
|
|
72
|
-
console.log("No plugins configured.");
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let installed = 0;
|
|
77
|
-
let skipped = 0;
|
|
78
|
-
let failed = 0;
|
|
79
|
-
|
|
80
|
-
// Track paths for summary
|
|
81
|
-
/** @type {string[]} */
|
|
82
|
-
const installedPaths = [];
|
|
83
|
-
/** @type {string[]} */
|
|
84
|
-
const skippedPaths = [];
|
|
85
|
-
|
|
86
|
-
// Ensure plugin directory exists
|
|
87
|
-
await fs.mkdir(CLAUDE_CODE_PLUGIN_DIR, { recursive: true });
|
|
88
|
-
|
|
89
|
-
for (const repo of repos) {
|
|
90
|
-
const ownerRepo = extractOwnerRepo(repo.source);
|
|
91
|
-
if (!ownerRepo) {
|
|
92
|
-
console.error(`❌ Invalid source URL: ${repo.source}`);
|
|
93
|
-
failed++;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const destPath = path.join(CLAUDE_CODE_PLUGIN_DIR, ownerRepo);
|
|
98
|
-
|
|
99
|
-
// Check if already exists
|
|
100
|
-
const exists = await fs
|
|
101
|
-
.access(destPath)
|
|
102
|
-
.then(() => true)
|
|
103
|
-
.catch(() => false);
|
|
104
|
-
if (exists) {
|
|
105
|
-
console.log(`⏭️ Skipping ${repo.source} → ${destPath}: already installed`);
|
|
106
|
-
skippedPaths.push(destPath);
|
|
107
|
-
skipped++;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Clone the repository
|
|
112
|
-
console.log(`📥 Installing ${repo.source}...`);
|
|
113
|
-
try {
|
|
114
|
-
await new Promise((resolve, reject) => {
|
|
115
|
-
execFile(
|
|
116
|
-
"git",
|
|
117
|
-
["clone", "--depth", "1", repo.source, destPath],
|
|
118
|
-
(err) => {
|
|
119
|
-
if (err) reject(err);
|
|
120
|
-
else resolve(undefined);
|
|
121
|
-
},
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
console.log(`✅ Installed to ${destPath}`);
|
|
125
|
-
installedPaths.push(destPath);
|
|
126
|
-
installed++;
|
|
127
|
-
} catch (error) {
|
|
128
|
-
console.error(
|
|
129
|
-
`❌ Failed to install: ${error instanceof Error ? error.message : String(error)}`,
|
|
130
|
-
);
|
|
131
|
-
failed++;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.log(
|
|
136
|
-
`\n📊 Summary: ${installed} installed, ${skipped} skipped, ${failed} failed`,
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
if (installedPaths.length > 0) {
|
|
140
|
-
console.log("\nInstalled:");
|
|
141
|
-
for (const p of installedPaths) {
|
|
142
|
-
console.log(` • ${p}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (skippedPaths.length > 0) {
|
|
147
|
-
console.log("\nSkipped:");
|
|
148
|
-
for (const p of skippedPaths) {
|
|
149
|
-
console.log(` • ${p}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Extract owner/repo from source URL.
|
|
156
|
-
* @param {string} source
|
|
157
|
-
* @returns {string|null}
|
|
158
|
-
*/
|
|
159
|
-
function extractOwnerRepo(source) {
|
|
160
|
-
// Handle: https://github.com/owner/repo
|
|
161
|
-
// Handle: git@github.com:owner/repo.git
|
|
162
|
-
const match = source.match(/[:/]([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
163
|
-
return match ? match[1] : null;
|
|
164
|
-
}
|
package/src/cliArgs.mjs
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {HelpSubcommand | InteractiveSubcommand | BatchSubcommand | ListModelsSubcommand | InstallClaudeCodePluginsSubcommand | CostSubcommand} Subcommand
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {{ type: 'help' }} HelpSubcommand
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {{ type: 'interactive', config: string[], model: string | null }} InteractiveSubcommand
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {{ type: 'batch', task: string, config: string[], model: string | null }} BatchSubcommand
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {{ type: 'list-models' }} ListModelsSubcommand
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @typedef {{ type: 'install-claude-code-plugins' }} InstallClaudeCodePluginsSubcommand
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {{ type: 'cost', from: string | null, to: string | null }} CostSubcommand
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @typedef {Object} CliArgs
|
|
31
|
-
* @property {Subcommand} subcommand - The subcommand to execute
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Parse command-line arguments.
|
|
36
|
-
* @param {string[]} argv - process.argv or similar
|
|
37
|
-
* @returns {CliArgs}
|
|
38
|
-
*/
|
|
39
|
-
export function parseCliArgs(argv) {
|
|
40
|
-
const args = argv.slice(2);
|
|
41
|
-
const subcommandName = args[0];
|
|
42
|
-
|
|
43
|
-
if (["-h", "--help", "help"].includes(subcommandName)) {
|
|
44
|
-
return {
|
|
45
|
-
subcommand: { type: "help" },
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!subcommandName || subcommandName.startsWith("-")) {
|
|
50
|
-
// Interactive mode (default)
|
|
51
|
-
const config = [];
|
|
52
|
-
let model = null;
|
|
53
|
-
|
|
54
|
-
for (let i = 0; i < args.length; i++) {
|
|
55
|
-
if (args[i] === "-m" || args[i] === "--model") {
|
|
56
|
-
if (args[i + 1]) {
|
|
57
|
-
model = args[i + 1];
|
|
58
|
-
i++;
|
|
59
|
-
}
|
|
60
|
-
} else if (args[i] === "-c" || args[i] === "--config") {
|
|
61
|
-
if (args[i + 1]) {
|
|
62
|
-
config.push(args[i + 1]);
|
|
63
|
-
i++;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
subcommand: { type: "interactive", config, model },
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (subcommandName === "batch") {
|
|
74
|
-
const batchArgs = args.slice(1);
|
|
75
|
-
|
|
76
|
-
let task = null;
|
|
77
|
-
let model = null;
|
|
78
|
-
const config = [];
|
|
79
|
-
|
|
80
|
-
for (let i = 0; i < batchArgs.length; i++) {
|
|
81
|
-
if (batchArgs[i] === "-m" || batchArgs[i] === "--model") {
|
|
82
|
-
if (batchArgs[i + 1]) {
|
|
83
|
-
model = batchArgs[i + 1];
|
|
84
|
-
i++;
|
|
85
|
-
}
|
|
86
|
-
} else if (batchArgs[i] === "-c" || batchArgs[i] === "--config") {
|
|
87
|
-
if (batchArgs[i + 1]) {
|
|
88
|
-
config.push(batchArgs[i + 1]);
|
|
89
|
-
i++;
|
|
90
|
-
}
|
|
91
|
-
} else if (!batchArgs[i].startsWith("-") && !task) {
|
|
92
|
-
task = batchArgs[i];
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
subcommand: { type: "batch", task: task || "", config, model },
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (subcommandName === "list-models") {
|
|
102
|
-
return {
|
|
103
|
-
subcommand: { type: "list-models" },
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (subcommandName === "install-claude-code-plugins") {
|
|
108
|
-
return {
|
|
109
|
-
subcommand: { type: "install-claude-code-plugins" },
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (subcommandName === "cost") {
|
|
114
|
-
const costArgs = args.slice(1);
|
|
115
|
-
let from = null;
|
|
116
|
-
let to = null;
|
|
117
|
-
for (let i = 0; i < costArgs.length; i++) {
|
|
118
|
-
if (costArgs[i] === "--from") {
|
|
119
|
-
if (costArgs[i + 1]) {
|
|
120
|
-
from = costArgs[i + 1];
|
|
121
|
-
i++;
|
|
122
|
-
}
|
|
123
|
-
} else if (costArgs[i] === "--to") {
|
|
124
|
-
if (costArgs[i + 1]) {
|
|
125
|
-
to = costArgs[i + 1];
|
|
126
|
-
i++;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
subcommand: { type: "cost", from, to },
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
subcommand: { type: "help" },
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Print help message and exit.
|
|
142
|
-
* @param {number} [exitCode] - Exit code (default: 0)
|
|
143
|
-
*/
|
|
144
|
-
export function printHelp(exitCode = 0) {
|
|
145
|
-
console.log(`
|
|
146
|
-
Usage: plain [options]
|
|
147
|
-
plain batch [options] <task>
|
|
148
|
-
plain cost [--from YYYY-MM-DD] [--to YYYY-MM-DD]
|
|
149
|
-
plain list-models
|
|
150
|
-
plain install-claude-code-plugins
|
|
151
|
-
|
|
152
|
-
Options:
|
|
153
|
-
-m, --model <model+variant> Model to use
|
|
154
|
-
-h, --help Show this help message
|
|
155
|
-
-c, --config <file> Config file to load (repeatable)
|
|
156
|
-
|
|
157
|
-
Subcommands:
|
|
158
|
-
batch <task> Run in batch mode with the given task instruction.
|
|
159
|
-
Config files are NOT auto-loaded in batch mode;
|
|
160
|
-
use -c to specify config files explicitly.
|
|
161
|
-
cost Show aggregated token cost per day for a period.
|
|
162
|
-
Defaults to the first day of the current month
|
|
163
|
-
through today.
|
|
164
|
-
list-models List available models
|
|
165
|
-
install-claude-code-plugins Install Claude Code plugins
|
|
166
|
-
|
|
167
|
-
Examples:
|
|
168
|
-
plain -m gpt-5.4+thinking-medium
|
|
169
|
-
plain batch \\
|
|
170
|
-
-c ~/.config/plain-agent/config.local.json \\
|
|
171
|
-
-c .plain-agent/config.json \\
|
|
172
|
-
"Add tests for ..."
|
|
173
|
-
`);
|
|
174
|
-
process.exit(exitCode);
|
|
175
|
-
}
|
package/src/cliBatch.mjs
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @import { UserEventEmitter, AgentEventEmitter, AgentCommands } from "./agent"
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { formatCostForBatch } from "./cliFormatter.mjs";
|
|
6
|
-
import { appendUsageRecord, buildUsageRecord } from "./usageStore.mjs";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {object} BatchSessionOptions
|
|
10
|
-
* @property {UserEventEmitter} userEventEmitter
|
|
11
|
-
* @property {AgentEventEmitter} agentEventEmitter
|
|
12
|
-
* @property {AgentCommands} agentCommands
|
|
13
|
-
* @property {string} task - Task instruction to execute
|
|
14
|
-
* @property {string} sessionId
|
|
15
|
-
* @property {string} modelName
|
|
16
|
-
* @property {boolean} sandbox
|
|
17
|
-
* @property {Date} startTime
|
|
18
|
-
* @property {() => Promise<void>} onStop
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Start a batch session and execute the task.
|
|
23
|
-
* Events are output as JSON Lines (1 line = 1 JSON object).
|
|
24
|
-
*
|
|
25
|
-
* @param {BatchSessionOptions} options
|
|
26
|
-
* @returns {Promise<void>}
|
|
27
|
-
*/
|
|
28
|
-
export async function startBatchSession({
|
|
29
|
-
userEventEmitter,
|
|
30
|
-
agentEventEmitter,
|
|
31
|
-
agentCommands,
|
|
32
|
-
task,
|
|
33
|
-
sessionId,
|
|
34
|
-
modelName,
|
|
35
|
-
sandbox,
|
|
36
|
-
startTime,
|
|
37
|
-
onStop,
|
|
38
|
-
}) {
|
|
39
|
-
setupEventHandlers(agentEventEmitter, { sessionId, modelName, sandbox });
|
|
40
|
-
|
|
41
|
-
userEventEmitter.emit("userInput", [{ type: "text", text: task }]);
|
|
42
|
-
|
|
43
|
-
await new Promise((/** @type {(value?: void) => void} */ resolve) => {
|
|
44
|
-
agentEventEmitter.on("turnEnd", async () => {
|
|
45
|
-
const costSummary = agentCommands.getCostSummary();
|
|
46
|
-
|
|
47
|
-
outputEvent({
|
|
48
|
-
type: "session_end",
|
|
49
|
-
timestamp: new Date().toISOString(),
|
|
50
|
-
cost: formatCostForBatch(costSummary),
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const record = buildUsageRecord({
|
|
55
|
-
sessionId,
|
|
56
|
-
mode: "batch",
|
|
57
|
-
modelName,
|
|
58
|
-
workingDir: process.cwd(),
|
|
59
|
-
costSummary,
|
|
60
|
-
now: startTime,
|
|
61
|
-
});
|
|
62
|
-
if (record) {
|
|
63
|
-
await appendUsageRecord(record);
|
|
64
|
-
}
|
|
65
|
-
} catch (err) {
|
|
66
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
67
|
-
outputEvent({
|
|
68
|
-
type: "error",
|
|
69
|
-
error: { message: `failed to record usage: ${message}` },
|
|
70
|
-
timestamp: new Date().toISOString(),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
await onStop();
|
|
75
|
-
resolve();
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
process.exit(0);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Setup event handlers for batch mode.
|
|
84
|
-
* Output events as JSON Lines.
|
|
85
|
-
*
|
|
86
|
-
* @param {AgentEventEmitter} agentEventEmitter
|
|
87
|
-
* @param {{ sessionId: string, modelName: string, sandbox: boolean }} meta
|
|
88
|
-
*/
|
|
89
|
-
function setupEventHandlers(
|
|
90
|
-
agentEventEmitter,
|
|
91
|
-
{ sessionId, modelName, sandbox },
|
|
92
|
-
) {
|
|
93
|
-
outputEvent({
|
|
94
|
-
type: "session_start",
|
|
95
|
-
sessionId,
|
|
96
|
-
modelName,
|
|
97
|
-
sandbox,
|
|
98
|
-
timestamp: new Date().toISOString(),
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
agentEventEmitter.on("message", (message) => {
|
|
102
|
-
outputEvent({
|
|
103
|
-
type: "message",
|
|
104
|
-
message,
|
|
105
|
-
timestamp: new Date().toISOString(),
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
agentEventEmitter.on("error", (error) => {
|
|
110
|
-
outputEvent({
|
|
111
|
-
type: "error",
|
|
112
|
-
error: {
|
|
113
|
-
message: error.message,
|
|
114
|
-
stack: error.stack,
|
|
115
|
-
},
|
|
116
|
-
timestamp: new Date().toISOString(),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
process.exit(1);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
agentEventEmitter.on("subagentSwitched", (subagent) => {
|
|
123
|
-
outputEvent({
|
|
124
|
-
type: "subagent_switched",
|
|
125
|
-
subagent,
|
|
126
|
-
timestamp: new Date().toISOString(),
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
agentEventEmitter.on("providerTokenUsage", (usage) => {
|
|
131
|
-
outputEvent({
|
|
132
|
-
type: "token_usage",
|
|
133
|
-
usage,
|
|
134
|
-
timestamp: new Date().toISOString(),
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Output an event as JSON Lines format.
|
|
141
|
-
* Each event is a single line of JSON.
|
|
142
|
-
*
|
|
143
|
-
* @param {object} event
|
|
144
|
-
*/
|
|
145
|
-
function outputEvent(event) {
|
|
146
|
-
console.log(JSON.stringify(event));
|
|
147
|
-
}
|