@pantheon.ai/agents 0.3.1 → 0.3.2
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 +8 -0
- package/dist/index.js +238 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,15 +60,19 @@ Example migration added in this version:
|
|
|
60
60
|
|
|
61
61
|
- `src/db/migrations/tidb/20260304_0001_add_task_retry_columns.sql`
|
|
62
62
|
- `src/db/migrations/db9/20260304_0001_add_task_retry_columns.sql`
|
|
63
|
+
- `src/db/migrations/tidb/20260305_0002_add_agent_config_envs.sql`
|
|
64
|
+
- `src/db/migrations/db9/20260305_0002_add_agent_config_envs.sql`
|
|
63
65
|
|
|
64
66
|
Apply manually:
|
|
65
67
|
|
|
66
68
|
```bash
|
|
67
69
|
# TiDB/MySQL
|
|
68
70
|
mysql --host 127.0.0.1 --port 4000 -u root pantheon_agents < src/db/migrations/tidb/20260304_0001_add_task_retry_columns.sql
|
|
71
|
+
mysql --host 127.0.0.1 --port 4000 -u root pantheon_agents < src/db/migrations/tidb/20260305_0002_add_agent_config_envs.sql
|
|
69
72
|
|
|
70
73
|
# db9/PostgreSQL
|
|
71
74
|
psql "$DATABASE_URL" -f src/db/migrations/db9/20260304_0001_add_task_retry_columns.sql
|
|
75
|
+
psql "$DATABASE_URL" -f src/db/migrations/db9/20260305_0002_add_agent_config_envs.sql
|
|
72
76
|
```
|
|
73
77
|
|
|
74
78
|
## Developer Local Setup
|
|
@@ -112,6 +116,9 @@ pantheon-agents config reviewer "Download some files" --project-id 019c0495-f77a
|
|
|
112
116
|
|
|
113
117
|
# update role on later config request
|
|
114
118
|
pantheon-agents config reviewer "Switch to review workflow" --project-id 019c0495-f77a-7b6c-ade0-6b59c6654617 --role reviewer
|
|
119
|
+
|
|
120
|
+
# set per-project branch envs (passed on every task start)
|
|
121
|
+
pantheon-agents config reviewer "Use Anthropic model" --project-id 019c0495-f77a-7b6c-ade0-6b59c6654617 --envs '{"ANTHROPIC_MODEL":"claude-opus-4-5"}'
|
|
115
122
|
```
|
|
116
123
|
|
|
117
124
|
### 2) Add a task
|
|
@@ -159,6 +166,7 @@ pantheon-agents show-tasks --all
|
|
|
159
166
|
pantheon-agents get-task <agent-name> <task-id>
|
|
160
167
|
pantheon-agents cancel-task <agent-name> <task-id> [reason] --yes
|
|
161
168
|
pantheon-agents retry-task <agent-name> <task-id> [reason] --yes
|
|
169
|
+
pantheon-agents kill <agent-name> <task-id> [reason] --yes
|
|
162
170
|
pantheon-agents skill.sh
|
|
163
171
|
pantheon-agents gen-migration-sql --provider tidb --from 0.3.0
|
|
164
172
|
pantheon-agents delete-task <agent-name> <task-id>
|
package/dist/index.js
CHANGED
|
@@ -399,7 +399,7 @@ var require_cli_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
399
399
|
|
|
400
400
|
//#endregion
|
|
401
401
|
//#region package.json
|
|
402
|
-
var version = "0.3.
|
|
402
|
+
var version = "0.3.1";
|
|
403
403
|
|
|
404
404
|
//#endregion
|
|
405
405
|
//#region src/db/db9.ts
|
|
@@ -1664,7 +1664,9 @@ const projectSchema = z.object({
|
|
|
1664
1664
|
created_at: zodJsonDate,
|
|
1665
1665
|
updated_at: zodJsonDate,
|
|
1666
1666
|
branches_total: z.number().int().nullable().optional(),
|
|
1667
|
-
background_tasks_total: z.number().int().nullable().optional()
|
|
1667
|
+
background_tasks_total: z.number().int().nullable().optional(),
|
|
1668
|
+
env_id: z.string().uuid().nullable().optional(),
|
|
1669
|
+
env_display_name: z.string().nullable().optional()
|
|
1668
1670
|
});
|
|
1669
1671
|
const projectMessageSchema = z.object({
|
|
1670
1672
|
id: z.string(),
|
|
@@ -2025,6 +2027,7 @@ const executor = createApiExecutor({
|
|
|
2025
2027
|
headers: { Authorization: `Bearer ${process.env.PANTHEON_API_KEY}` },
|
|
2026
2028
|
validateResponse
|
|
2027
2029
|
});
|
|
2030
|
+
const killProjectBranchExecutionApi = defineApi(post`/api/v1/projects/${"projectId"}/branches/${"branchId"}/kill`, typeOf(), "raw");
|
|
2028
2031
|
async function getPantheonProjectInfo({ projectId }) {
|
|
2029
2032
|
return await executor.execute(getProject, { projectId }, null);
|
|
2030
2033
|
}
|
|
@@ -2082,6 +2085,23 @@ async function executeOnPantheon({ projectId, branchId, prompt, agent }) {
|
|
|
2082
2085
|
parent_branch_id: branchId
|
|
2083
2086
|
})).branches[0];
|
|
2084
2087
|
}
|
|
2088
|
+
function isNotFoundError(error) {
|
|
2089
|
+
if (!(error instanceof Error)) return false;
|
|
2090
|
+
const message = error.message.toLowerCase();
|
|
2091
|
+
return message.includes("404") || message.includes("not found");
|
|
2092
|
+
}
|
|
2093
|
+
async function killPantheonBranchExecution({ projectId, branchId }) {
|
|
2094
|
+
try {
|
|
2095
|
+
await executor.execute(killProjectBranchExecutionApi, {
|
|
2096
|
+
projectId,
|
|
2097
|
+
branchId
|
|
2098
|
+
}, {});
|
|
2099
|
+
return [branchId];
|
|
2100
|
+
} catch (error) {
|
|
2101
|
+
if (isNotFoundError(error)) return [];
|
|
2102
|
+
throw error;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2085
2105
|
|
|
2086
2106
|
//#endregion
|
|
2087
2107
|
//#region src/core/task-list.ts
|
|
@@ -2807,6 +2827,7 @@ var WatchStepAggregator = class {
|
|
|
2807
2827
|
//#region src/core/index.ts
|
|
2808
2828
|
const DEFAULT_MAX_RETRY_ATTEMPTS = 3;
|
|
2809
2829
|
const DEFAULT_RETRY_BACKOFF_SECONDS = 30;
|
|
2830
|
+
const DEFAULT_KILL_REASON = "Killed by user";
|
|
2810
2831
|
async function runAgent(name, options, logger) {
|
|
2811
2832
|
const agentDir = path.join(options.dataDir, "agents", name);
|
|
2812
2833
|
const pidFile = path.join(agentDir, "pid");
|
|
@@ -3064,6 +3085,170 @@ async function retryTask(agentName, taskId, reason) {
|
|
|
3064
3085
|
await provider.close();
|
|
3065
3086
|
}
|
|
3066
3087
|
}
|
|
3088
|
+
function resolveTaskAttemptCount(value) {
|
|
3089
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
|
|
3090
|
+
return 1;
|
|
3091
|
+
}
|
|
3092
|
+
function hasTaskBranchId(task) {
|
|
3093
|
+
return "branch_id" in task && typeof task.branch_id === "string";
|
|
3094
|
+
}
|
|
3095
|
+
function collectKillBranchTargets(tasks, options = {}) {
|
|
3096
|
+
const childrenByParentId = /* @__PURE__ */ new Map();
|
|
3097
|
+
const taskById = /* @__PURE__ */ new Map();
|
|
3098
|
+
for (const task of tasks) {
|
|
3099
|
+
taskById.set(task.id, task);
|
|
3100
|
+
if (!task.parent_task_id) continue;
|
|
3101
|
+
const siblings = childrenByParentId.get(task.parent_task_id);
|
|
3102
|
+
if (siblings) siblings.push(task);
|
|
3103
|
+
else childrenByParentId.set(task.parent_task_id, [task]);
|
|
3104
|
+
}
|
|
3105
|
+
const roots = options.rootTaskIds && options.rootTaskIds.length > 0 ? options.rootTaskIds.map((taskId) => taskById.get(taskId)).filter((task) => task != null) : tasks.filter((task) => task.status === "pending" || task.status === "running");
|
|
3106
|
+
if (roots.length === 0) return [];
|
|
3107
|
+
const maxDepthByTaskId = /* @__PURE__ */ new Map();
|
|
3108
|
+
const stack = roots.map((task) => ({
|
|
3109
|
+
taskId: task.id,
|
|
3110
|
+
depth: 0
|
|
3111
|
+
}));
|
|
3112
|
+
while (stack.length > 0) {
|
|
3113
|
+
const item = stack.pop();
|
|
3114
|
+
if (!item) continue;
|
|
3115
|
+
const previousDepth = maxDepthByTaskId.get(item.taskId);
|
|
3116
|
+
if (previousDepth != null && previousDepth >= item.depth) continue;
|
|
3117
|
+
maxDepthByTaskId.set(item.taskId, item.depth);
|
|
3118
|
+
const children = childrenByParentId.get(item.taskId) ?? [];
|
|
3119
|
+
for (const child of children) stack.push({
|
|
3120
|
+
taskId: child.id,
|
|
3121
|
+
depth: item.depth + 1
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
const scoredTargets = [];
|
|
3125
|
+
for (const [taskId, depth] of maxDepthByTaskId) {
|
|
3126
|
+
const task = taskById.get(taskId);
|
|
3127
|
+
if (!task || !hasTaskBranchId(task)) continue;
|
|
3128
|
+
scoredTargets.push({
|
|
3129
|
+
task_id: task.id,
|
|
3130
|
+
project_id: task.project_id,
|
|
3131
|
+
branch_id: task.branch_id,
|
|
3132
|
+
depth,
|
|
3133
|
+
queued_at_ms: task.queued_at.getTime()
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3136
|
+
scoredTargets.sort((a, b) => {
|
|
3137
|
+
if (a.depth !== b.depth) return b.depth - a.depth;
|
|
3138
|
+
return b.queued_at_ms - a.queued_at_ms;
|
|
3139
|
+
});
|
|
3140
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3141
|
+
const targets = [];
|
|
3142
|
+
for (const target of scoredTargets) {
|
|
3143
|
+
const key = `${target.project_id}:${target.branch_id}`;
|
|
3144
|
+
if (seen.has(key)) continue;
|
|
3145
|
+
seen.add(key);
|
|
3146
|
+
targets.push({
|
|
3147
|
+
task_id: target.task_id,
|
|
3148
|
+
project_id: target.project_id,
|
|
3149
|
+
branch_id: target.branch_id
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3152
|
+
return targets;
|
|
3153
|
+
}
|
|
3154
|
+
function collectTaskSubtree(tasks, rootTaskId) {
|
|
3155
|
+
const childrenByParentId = /* @__PURE__ */ new Map();
|
|
3156
|
+
const taskById = /* @__PURE__ */ new Map();
|
|
3157
|
+
for (const task of tasks) {
|
|
3158
|
+
taskById.set(task.id, task);
|
|
3159
|
+
if (!task.parent_task_id) continue;
|
|
3160
|
+
const siblings = childrenByParentId.get(task.parent_task_id);
|
|
3161
|
+
if (siblings) siblings.push(task);
|
|
3162
|
+
else childrenByParentId.set(task.parent_task_id, [task]);
|
|
3163
|
+
}
|
|
3164
|
+
if (!taskById.has(rootTaskId)) return [];
|
|
3165
|
+
const subtreeIds = /* @__PURE__ */ new Set();
|
|
3166
|
+
const stack = [rootTaskId];
|
|
3167
|
+
while (stack.length > 0) {
|
|
3168
|
+
const taskId = stack.pop();
|
|
3169
|
+
if (!taskId || subtreeIds.has(taskId)) continue;
|
|
3170
|
+
subtreeIds.add(taskId);
|
|
3171
|
+
const children = childrenByParentId.get(taskId) ?? [];
|
|
3172
|
+
for (const child of children) stack.push(child.id);
|
|
3173
|
+
}
|
|
3174
|
+
return tasks.filter((task) => subtreeIds.has(task.id));
|
|
3175
|
+
}
|
|
3176
|
+
function toCancelledTask(task, reason, cancelledAt) {
|
|
3177
|
+
return {
|
|
3178
|
+
status: "cancelled",
|
|
3179
|
+
id: task.id,
|
|
3180
|
+
task: task.task,
|
|
3181
|
+
type: task.type,
|
|
3182
|
+
project_id: task.project_id,
|
|
3183
|
+
base_branch_id: task.base_branch_id,
|
|
3184
|
+
parent_task_id: task.parent_task_id ?? null,
|
|
3185
|
+
config_version: task.config_version ?? null,
|
|
3186
|
+
attempt_count: resolveTaskAttemptCount(task.attempt_count),
|
|
3187
|
+
cancel_reason: reason,
|
|
3188
|
+
queued_at: task.queued_at,
|
|
3189
|
+
cancelled_at: cancelledAt
|
|
3190
|
+
};
|
|
3191
|
+
}
|
|
3192
|
+
async function killAgentTasks(agentName, taskId, reason = DEFAULT_KILL_REASON, deps = {}) {
|
|
3193
|
+
const provider = createTaskListProvider(agentName, pino());
|
|
3194
|
+
try {
|
|
3195
|
+
const tasks = await provider.getTasks({
|
|
3196
|
+
order_by: "queued_at",
|
|
3197
|
+
order_direction: "asc"
|
|
3198
|
+
});
|
|
3199
|
+
const subtreeTasks = collectTaskSubtree(tasks, taskId);
|
|
3200
|
+
if (subtreeTasks.length === 0) return null;
|
|
3201
|
+
const pendingTasks = subtreeTasks.filter((task) => task.status === "pending");
|
|
3202
|
+
const runningTasks = subtreeTasks.filter((task) => task.status === "running");
|
|
3203
|
+
for (const pendingTask of pendingTasks) await provider.cancelTask(pendingTask, reason);
|
|
3204
|
+
const killTargets = collectKillBranchTargets(tasks, { rootTaskIds: [taskId] });
|
|
3205
|
+
const killBranch = deps.killBranch ?? killPantheonBranchExecution;
|
|
3206
|
+
const killedBranches = [];
|
|
3207
|
+
const seenKilledBranchKeys = /* @__PURE__ */ new Set();
|
|
3208
|
+
const failedBranchKills = [];
|
|
3209
|
+
for (const target of killTargets) try {
|
|
3210
|
+
const deletedBranchIds = await killBranch({
|
|
3211
|
+
projectId: target.project_id,
|
|
3212
|
+
branchId: target.branch_id
|
|
3213
|
+
});
|
|
3214
|
+
for (const deletedBranchId of deletedBranchIds) {
|
|
3215
|
+
const branchKey = `${target.project_id}:${deletedBranchId}`;
|
|
3216
|
+
if (seenKilledBranchKeys.has(branchKey)) continue;
|
|
3217
|
+
seenKilledBranchKeys.add(branchKey);
|
|
3218
|
+
killedBranches.push({
|
|
3219
|
+
project_id: target.project_id,
|
|
3220
|
+
branch_id: deletedBranchId
|
|
3221
|
+
});
|
|
3222
|
+
}
|
|
3223
|
+
} catch (error) {
|
|
3224
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3225
|
+
failedBranchKills.push({
|
|
3226
|
+
task_id: target.task_id,
|
|
3227
|
+
project_id: target.project_id,
|
|
3228
|
+
branch_id: target.branch_id,
|
|
3229
|
+
error: message
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
const now = /* @__PURE__ */ new Date();
|
|
3233
|
+
const killedBranchKeys = new Set(killedBranches.map((item) => `${item.project_id}:${item.branch_id}`));
|
|
3234
|
+
let runningCancelledCount = 0;
|
|
3235
|
+
for (const runningTask of runningTasks) {
|
|
3236
|
+
const branchKey = `${runningTask.project_id}:${runningTask.branch_id}`;
|
|
3237
|
+
if (!killedBranchKeys.has(branchKey)) continue;
|
|
3238
|
+
await provider.updateTask(toCancelledTask(runningTask, reason, now));
|
|
3239
|
+
runningCancelledCount++;
|
|
3240
|
+
}
|
|
3241
|
+
return {
|
|
3242
|
+
task_id: taskId,
|
|
3243
|
+
pending_cancelled_count: pendingTasks.length,
|
|
3244
|
+
running_cancelled_count: runningCancelledCount,
|
|
3245
|
+
killed_branches: killedBranches,
|
|
3246
|
+
failed_branch_kills: failedBranchKills
|
|
3247
|
+
};
|
|
3248
|
+
} finally {
|
|
3249
|
+
await provider.close();
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3067
3252
|
async function showAgentConfig(agentName, projectId) {
|
|
3068
3253
|
const provider = createTaskListProvider(agentName, pino());
|
|
3069
3254
|
try {
|
|
@@ -3477,6 +3662,56 @@ function createGetTaskCommand(version, deps = {}) {
|
|
|
3477
3662
|
});
|
|
3478
3663
|
}
|
|
3479
3664
|
|
|
3665
|
+
//#endregion
|
|
3666
|
+
//#region src/cli/commands/kill.ts
|
|
3667
|
+
function createKillCommand(version, deps = {}) {
|
|
3668
|
+
return createCommand("kill").version(version).description("Kill a task and its descendants recursively for an agent").argument("<name>", "The name of the agent.").argument("<task-id>", "Root task id to kill.").argument("[reason]", "Optional kill reason.").option("-y, --yes", "Skip confirmation prompt.").option("-f, --force", "Alias for --yes.").action(async function() {
|
|
3669
|
+
const [name, taskId, reason] = this.args;
|
|
3670
|
+
const options = this.opts();
|
|
3671
|
+
if (!ensureEnv(["DATABASE_URL"])) return;
|
|
3672
|
+
const rl = options.yes || options.force ? null : readline.createInterface({
|
|
3673
|
+
input: process$1.stdin,
|
|
3674
|
+
output: process$1.stdout
|
|
3675
|
+
});
|
|
3676
|
+
try {
|
|
3677
|
+
if (rl) {
|
|
3678
|
+
if ((await rl.question(`Type the agent name (${name}) to confirm kill: `)).trim() !== name) {
|
|
3679
|
+
console.error("Confirmation failed. Agent name did not match.");
|
|
3680
|
+
process$1.exitCode = 1;
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
if ((await rl.question(`Type KILL ${taskId} to stop this task subtree and related branches: `)).trim() !== `KILL ${taskId}`) {
|
|
3684
|
+
console.error("Confirmation failed. Aborting kill.");
|
|
3685
|
+
process$1.exitCode = 1;
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
const result = await (deps.killAgentTasks ?? killAgentTasks)(name, taskId, reason);
|
|
3690
|
+
if (!result) {
|
|
3691
|
+
console.error(`Task ${taskId} not found for agent ${name}.`);
|
|
3692
|
+
process$1.exitCode = 1;
|
|
3693
|
+
return;
|
|
3694
|
+
}
|
|
3695
|
+
const attemptedBranchKills = result.killed_branches.length + result.failed_branch_kills.length;
|
|
3696
|
+
if (result.pending_cancelled_count === 0 && attemptedBranchKills === 0) {
|
|
3697
|
+
console.log(`No actionable work found in task subtree ${taskId}.`);
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3700
|
+
console.log(`Kill result for agent ${name} task ${taskId}: cancelled ${result.pending_cancelled_count} pending task(s), cancelled ${result.running_cancelled_count} running task(s).`);
|
|
3701
|
+
if (result.killed_branches.length > 0) console.log(`Killed ${result.killed_branches.length} branch(es).`);
|
|
3702
|
+
if (result.failed_branch_kills.length > 0) {
|
|
3703
|
+
for (const failed of result.failed_branch_kills) console.error(`Failed to kill branch ${failed.branch_id} in project ${failed.project_id}: ${failed.error}`);
|
|
3704
|
+
process$1.exitCode = 1;
|
|
3705
|
+
}
|
|
3706
|
+
} catch (error) {
|
|
3707
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
3708
|
+
process$1.exitCode = 1;
|
|
3709
|
+
} finally {
|
|
3710
|
+
rl?.close();
|
|
3711
|
+
}
|
|
3712
|
+
});
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3480
3715
|
//#endregion
|
|
3481
3716
|
//#region src/cli/commands/run.ts
|
|
3482
3717
|
function createRunAgentCommand(version) {
|
|
@@ -6239,7 +6474,7 @@ function createWatchStreamCommand(version) {
|
|
|
6239
6474
|
|
|
6240
6475
|
//#endregion
|
|
6241
6476
|
//#region src/cli/index.ts
|
|
6242
|
-
const program = new Command().name("pantheon-agents").description("Pantheon agents CLI").version(version).showHelpAfterError().showSuggestionAfterError().addHelpCommand().addCommand(createAddTaskCommand(version)).addCommand(createConfigAgentCommand(version)).addCommand(createDeleteTaskCommand(version)).addCommand(createCancelTaskCommand(version)).addCommand(createGetTaskCommand(version)).addCommand(createRunAgentCommand(version)).addCommand(createRetryTaskCommand(version)).addCommand(createSkillShCommand(version)).addCommand(createGenMigrationSqlCommand(version)).addCommand(createShowConfigCommand(version)).addCommand(createShowTasksCommand(version)).addCommand(createWatchCommand(version)).addCommand(createWatchStreamCommand(version));
|
|
6477
|
+
const program = new Command().name("pantheon-agents").description("Pantheon agents CLI").version(version).showHelpAfterError().showSuggestionAfterError().addHelpCommand().addCommand(createAddTaskCommand(version)).addCommand(createConfigAgentCommand(version)).addCommand(createDeleteTaskCommand(version)).addCommand(createCancelTaskCommand(version)).addCommand(createGetTaskCommand(version)).addCommand(createKillCommand(version)).addCommand(createRunAgentCommand(version)).addCommand(createRetryTaskCommand(version)).addCommand(createSkillShCommand(version)).addCommand(createGenMigrationSqlCommand(version)).addCommand(createShowConfigCommand(version)).addCommand(createShowTasksCommand(version)).addCommand(createWatchCommand(version)).addCommand(createWatchStreamCommand(version));
|
|
6243
6478
|
async function main() {
|
|
6244
6479
|
if (process$1.argv.length <= 2) {
|
|
6245
6480
|
program.outputHelp();
|