pi-crew 0.1.45 → 0.1.46
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 +5 -5
- package/agents/analyst.md +1 -1
- package/agents/critic.md +1 -1
- package/agents/executor.md +1 -1
- package/agents/explorer.md +1 -1
- package/agents/planner.md +1 -1
- package/agents/reviewer.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/test-engineer.md +1 -1
- package/agents/verifier.md +1 -1
- package/agents/writer.md +1 -1
- package/docs/next-upgrade-roadmap.md +733 -0
- package/docs/refactor-tasks-phase3.md +394 -394
- package/docs/refactor-tasks-phase4.md +564 -564
- package/docs/refactor-tasks-phase5.md +402 -402
- package/docs/refactor-tasks-phase6.md +662 -662
- package/docs/research-awesome-agent-skills-distillation.md +100 -0
- package/docs/research-extension-examples.md +297 -297
- package/docs/research-extension-system.md +324 -324
- package/docs/research-oh-my-pi-distillation.md +322 -0
- package/docs/research-optimization-plan.md +548 -548
- package/docs/research-phase10-distillation.md +198 -198
- package/docs/research-phase11-distillation.md +201 -201
- package/docs/research-pi-coding-agent.md +357 -357
- package/docs/research-source-pi-crew-reference.md +174 -174
- package/docs/runtime-flow.md +148 -148
- package/docs/source-runtime-refactor-map.md +107 -83
- package/docs/usage.md +3 -3
- package/index.ts +6 -6
- package/install.mjs +52 -8
- package/package.json +1 -1
- package/schema.json +2 -1
- package/skills/async-worker-recovery/SKILL.md +42 -0
- package/skills/context-artifact-hygiene/SKILL.md +52 -0
- package/skills/delegation-patterns/SKILL.md +54 -0
- package/skills/mailbox-interactive/SKILL.md +40 -0
- package/skills/model-routing-context/SKILL.md +39 -0
- package/skills/multi-perspective-review/SKILL.md +58 -0
- package/skills/observability-reliability/SKILL.md +41 -0
- package/skills/ownership-session-security/SKILL.md +41 -0
- package/skills/pi-extension-lifecycle/SKILL.md +39 -0
- package/skills/requirements-to-task-packet/SKILL.md +63 -0
- package/skills/resource-discovery-config/SKILL.md +41 -0
- package/skills/runtime-state-reader/SKILL.md +44 -0
- package/skills/secure-agent-orchestration-review/SKILL.md +45 -0
- package/skills/state-mutation-locking/SKILL.md +42 -0
- package/skills/systematic-debugging/SKILL.md +67 -0
- package/skills/ui-render-performance/SKILL.md +39 -0
- package/skills/verification-before-done/SKILL.md +57 -0
- package/skills/worktree-isolation/SKILL.md +39 -0
- package/src/agents/agent-serializer.ts +34 -34
- package/src/agents/discover-agents.ts +12 -11
- package/src/config/config.ts +48 -24
- package/src/config/defaults.ts +14 -0
- package/src/extension/cross-extension-rpc.ts +82 -82
- package/src/extension/project-init.ts +62 -2
- package/src/extension/register.ts +11 -9
- package/src/extension/registration/commands.ts +32 -25
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-helpers.ts +8 -0
- package/src/extension/registration/subagent-tools.ts +149 -148
- package/src/extension/registration/team-tool.ts +8 -6
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-index.ts +13 -5
- package/src/extension/run-maintenance.ts +62 -43
- package/src/extension/team-tool/api.ts +25 -8
- package/src/extension/team-tool/cancel.ts +33 -4
- package/src/extension/team-tool/context.ts +5 -0
- package/src/extension/team-tool/handle-settings.ts +188 -188
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +91 -79
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +37 -17
- package/src/extension/team-tool/run.ts +52 -10
- package/src/extension/team-tool/status.ts +12 -1
- package/src/extension/team-tool-types.ts +2 -0
- package/src/extension/team-tool.ts +32 -11
- package/src/i18n.ts +184 -184
- package/src/observability/event-to-metric.ts +8 -1
- package/src/observability/exporters/otlp-exporter.ts +77 -77
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/agent-control.ts +63 -63
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/background-runner.ts +59 -53
- package/src/runtime/cancellation.ts +51 -0
- package/src/runtime/child-pi.ts +457 -444
- package/src/runtime/completion-guard.ts +190 -190
- package/src/runtime/crash-recovery.ts +1 -0
- package/src/runtime/crew-agent-records.ts +38 -6
- package/src/runtime/deadletter.ts +1 -0
- package/src/runtime/delivery-coordinator.ts +46 -25
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/effectiveness.ts +76 -0
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +106 -106
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +124 -124
- package/src/runtime/live-agent-control.ts +88 -87
- package/src/runtime/live-agent-manager.ts +103 -85
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +309 -305
- package/src/runtime/manifest-cache.ts +17 -2
- package/src/runtime/model-fallback.ts +6 -4
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/pi-args.ts +18 -3
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/process-status.ts +5 -1
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +81 -64
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/runtime-resolver.ts +22 -6
- package/src/runtime/session-resources.ts +25 -25
- package/src/runtime/session-snapshot.ts +59 -59
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/skill-instructions.ts +222 -0
- package/src/runtime/stale-reconciler.ts +4 -14
- package/src/runtime/subagent-manager.ts +3 -0
- package/src/runtime/supervisor-contact.ts +59 -59
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-output-context.ts +127 -127
- package/src/runtime/task-runner/capabilities.ts +78 -0
- package/src/runtime/task-runner/live-executor.ts +105 -101
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +3 -1
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -0
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/task-runner.ts +44 -5
- package/src/runtime/team-runner.ts +78 -15
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/schema/config-schema.ts +1 -0
- package/src/schema/team-tool-schema.ts +3 -3
- package/src/state/active-run-registry.ts +165 -0
- package/src/state/contracts.ts +1 -1
- package/src/state/mailbox.ts +44 -4
- package/src/state/state-store.ts +8 -1
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +44 -2
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/team-config.ts +1 -0
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/crew-widget.ts +4 -3
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dashboard-panes/progress-pane.ts +2 -0
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/loaders.ts +158 -158
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/render-scheduler.ts +143 -143
- package/src/ui/run-snapshot-cache.ts +10 -2
- package/src/ui/snapshot-types.ts +2 -0
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +58 -58
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/utils/atomic-write.ts +33 -33
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +12 -12
- package/src/utils/names.ts +27 -27
- package/src/utils/paths.ts +4 -2
- package/src/utils/redaction.ts +44 -44
- package/src/utils/safe-paths.ts +47 -47
- package/src/utils/sleep.ts +32 -32
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/workflows/workflow-config.ts +1 -0
- package/src/worktree/branch-freshness.ts +45 -45
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +29 -29
- package/workflows/fast-fix.workflow.md +22 -22
- package/workflows/implementation.workflow.md +38 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
|
@@ -1,43 +1,62 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
fs.
|
|
39
|
-
fs.
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
+
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
5
|
+
import { projectCrewRoot } from "../utils/paths.ts";
|
|
6
|
+
import { listRuns } from "./run-index.ts";
|
|
7
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
8
|
+
import { redactSecrets } from "../utils/redaction.ts";
|
|
9
|
+
|
|
10
|
+
export interface PruneRunsResult {
|
|
11
|
+
kept: string[];
|
|
12
|
+
removed: string[];
|
|
13
|
+
auditPath?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PruneRunsOptions {
|
|
17
|
+
intent?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isFinished(run: TeamRunManifest): boolean {
|
|
21
|
+
return run.status === "completed" || run.status === "failed" || run.status === "cancelled" || run.status === "blocked";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isSafeToPrune(cwd: string, run: TeamRunManifest): boolean {
|
|
25
|
+
try {
|
|
26
|
+
const crewRoot = projectCrewRoot(cwd);
|
|
27
|
+
resolveRealContainedPath(crewRoot, run.stateRoot);
|
|
28
|
+
resolveRealContainedPath(crewRoot, run.artifactsRoot);
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function appendPruneAudit(cwd: string, payload: Record<string, unknown>): string | undefined {
|
|
36
|
+
try {
|
|
37
|
+
const filePath = path.join(projectCrewRoot(cwd), "audit", "prune.jsonl");
|
|
38
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
39
|
+
fs.appendFileSync(filePath, `${JSON.stringify(redactSecrets({ ...payload, auditedAt: new Date().toISOString() }))}\n`, "utf-8");
|
|
40
|
+
return filePath;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logInternalError("prune.audit-write", error, `cwd=${cwd}`);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function pruneFinishedRuns(cwd: string, keep: number, options: PruneRunsOptions = {}): PruneRunsResult {
|
|
48
|
+
const finished = listRuns(cwd).filter((run) => run.cwd === cwd && isFinished(run)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
49
|
+
const kept = finished.slice(0, keep).map((run) => run.runId);
|
|
50
|
+
const removed: string[] = [];
|
|
51
|
+
for (const run of finished.slice(keep)) {
|
|
52
|
+
if (!isSafeToPrune(cwd, run)) {
|
|
53
|
+
logInternalError("prune.path-unsafe", new Error(`Skipping unsafe prune: stateRoot=${run.stateRoot}, artifactsRoot=${run.artifactsRoot}`), `runId=${run.runId}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
fs.rmSync(run.stateRoot, { recursive: true, force: true });
|
|
57
|
+
fs.rmSync(run.artifactsRoot, { recursive: true, force: true });
|
|
58
|
+
removed.push(run.runId);
|
|
59
|
+
}
|
|
60
|
+
const auditPath = appendPruneAudit(cwd, { action: "prune", keep, intent: options.intent, kept, removed });
|
|
61
|
+
return { kept, removed, auditPath };
|
|
62
|
+
}
|
|
@@ -5,7 +5,7 @@ import { loadRunManifestById, saveRunManifest, saveRunTasks, updateRunStatus } f
|
|
|
5
5
|
import { withRunLockSync } from "../../state/locks.ts";
|
|
6
6
|
import { canTransitionTaskStatus, isTeamTaskStatus } from "../../state/contracts.ts";
|
|
7
7
|
import { claimTask, releaseTaskClaim, transitionClaimedTaskStatus } from "../../state/task-claims.ts";
|
|
8
|
-
import { acknowledgeMailboxMessage, appendMailboxMessage, readDeliveryState, readMailbox, readMailboxMessage, validateMailbox, type MailboxDirection } from "../../state/mailbox.ts";
|
|
8
|
+
import { acknowledgeMailboxMessage, appendFollowUpMessage, appendMailboxMessage, appendSteeringMessage, readDeliveryState, readMailbox, readMailboxMessage, validateMailbox, type MailboxDirection } from "../../state/mailbox.ts";
|
|
9
9
|
import { appendEvent, readEvents, readEventsCursor } from "../../state/event-log.ts";
|
|
10
10
|
import { resolveCrewRuntime } from "../../runtime/runtime-resolver.ts";
|
|
11
11
|
import { probeLiveSessionRuntime } from "../../subagents/live/session-runtime.ts";
|
|
@@ -14,7 +14,7 @@ import { touchWorkerHeartbeat } from "../../runtime/worker-heartbeat.ts";
|
|
|
14
14
|
import { agentOutputPath, readCrewAgentEventsCursor, readCrewAgentStatus, readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
15
15
|
import { buildAgentDashboard, readAgentOutput } from "../../runtime/agent-observability.ts";
|
|
16
16
|
import { readForegroundControlStatus, writeForegroundInterruptRequest } from "../../runtime/foreground-control.ts";
|
|
17
|
-
import { getLiveAgent, listLiveAgents, resumeLiveAgent, steerLiveAgent, stopLiveAgent } from "../../subagents/live/manager.ts";
|
|
17
|
+
import { followUpLiveAgent, getLiveAgent, listLiveAgents, resumeLiveAgent, steerLiveAgent, stopLiveAgent } from "../../subagents/live/manager.ts";
|
|
18
18
|
import { appendLiveAgentControlRequest } from "../../subagents/live/control.ts";
|
|
19
19
|
import { liveControlRealtimeMessage, publishLiveControlRealtime } from "../../subagents/live/realtime.ts";
|
|
20
20
|
import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
@@ -212,7 +212,7 @@ export async function handleApi(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
212
212
|
const agent = readCrewAgents(loaded.manifest).find((item) => item.id === agentId || item.taskId === agentId);
|
|
213
213
|
if (!agent) return result("API nudge-agent requires config.agentId matching an agent id or task id.", { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
214
214
|
const messageText = typeof cfg.message === "string" && cfg.message.trim() ? cfg.message.trim() : "Please report your current status, blocker, or smallest next step.";
|
|
215
|
-
const message =
|
|
215
|
+
const message = appendSteeringMessage(loaded.manifest, { taskId: agent.taskId, to: agent.taskId, body: messageText, priority: "normal", data: { source: "nudge-agent" } });
|
|
216
216
|
appendEvent(loaded.manifest.eventsPath, { type: "agent.nudged", runId: loaded.manifest.runId, taskId: agent.taskId, message: messageText, data: { agentId: agent.id, mailboxMessageId: message.id } });
|
|
217
217
|
ctx.events?.emit?.("crew.mailbox.message", { runId: loaded.manifest.runId, id: message.id, direction: message.direction, from: message.from, to: message.to, taskId: message.taskId, source: "nudge-agent" });
|
|
218
218
|
return result(JSON.stringify({ agentId: agent.id, mailboxMessage: message }, null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
@@ -220,7 +220,7 @@ export async function handleApi(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
220
220
|
if (operation === "list-live-agents") {
|
|
221
221
|
return result(JSON.stringify(listLiveAgents().filter((agent) => agent.runId === loaded.manifest.runId), null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
222
222
|
}
|
|
223
|
-
if (operation === "steer-agent" || operation === "stop-agent" || operation === "resume-agent" || operation === "interrupt-agent") {
|
|
223
|
+
if (operation === "steer-agent" || operation === "follow-up-agent" || operation === "stop-agent" || operation === "resume-agent" || operation === "interrupt-agent") {
|
|
224
224
|
const agentId = typeof cfg.agentId === "string" ? cfg.agentId : undefined;
|
|
225
225
|
if (!agentId) return result(`API ${operation} requires config.agentId.`, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
226
226
|
const message = typeof cfg.message === "string" && cfg.message.trim() ? cfg.message.trim() : undefined;
|
|
@@ -228,7 +228,22 @@ export async function handleApi(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
228
228
|
try {
|
|
229
229
|
const live = getLiveAgent(agentId);
|
|
230
230
|
if (live && live.runId !== loaded.manifest.runId) return result(`Live agent '${agentId}' does not belong to run ${loaded.manifest.runId}.`, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
231
|
-
if (operation === "steer-agent"
|
|
231
|
+
if (!live && (operation === "steer-agent" || operation === "follow-up-agent")) throw new Error(`Live agent '${agentId}' not found.`);
|
|
232
|
+
const liveTaskId = live?.taskId;
|
|
233
|
+
if ((operation === "steer-agent" || operation === "follow-up-agent") && !liveTaskId) throw new Error(`Live agent '${agentId}' not found.`);
|
|
234
|
+
const targetTaskId = liveTaskId ?? agentId;
|
|
235
|
+
if (operation === "steer-agent") {
|
|
236
|
+
const text = message ?? "Please report current status and wrap up if possible.";
|
|
237
|
+
const realtime = await steerLiveAgent(agentId, text);
|
|
238
|
+
const mailboxMessage = appendSteeringMessage(loaded.manifest, { taskId: targetTaskId, body: text, status: "delivered", data: { source: "steer-agent", realtime: true } });
|
|
239
|
+
return result(JSON.stringify({ realtime, mailboxMessage }, null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
240
|
+
}
|
|
241
|
+
if (operation === "follow-up-agent") {
|
|
242
|
+
if (!prompt) return result("API follow-up-agent requires config.prompt or config.message.", { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
243
|
+
const realtime = await followUpLiveAgent(agentId, prompt);
|
|
244
|
+
const mailboxMessage = appendFollowUpMessage(loaded.manifest, { taskId: targetTaskId, body: prompt, status: "delivered", data: { source: "follow-up-agent", realtime: true } });
|
|
245
|
+
return result(JSON.stringify({ realtime, mailboxMessage }, null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
246
|
+
}
|
|
232
247
|
if (operation === "resume-agent") {
|
|
233
248
|
if (!prompt) return result("API resume-agent requires config.prompt or config.message.", { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
234
249
|
return result(JSON.stringify(await resumeLiveAgent(agentId, prompt), null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
@@ -243,12 +258,14 @@ export async function handleApi(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
243
258
|
const task = loaded.tasks.find((item) => item.id === agent.taskId);
|
|
244
259
|
if (!task) return result(`API ${operation} agent '${agentId}' does not match a run task.`, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
245
260
|
if (operation === "resume-agent" && !prompt) return result("API resume-agent requires config.prompt or config.message.", { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
261
|
+
if (operation === "follow-up-agent" && !prompt) return result("API follow-up-agent requires config.prompt or config.message.", { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
246
262
|
try {
|
|
247
|
-
const request = appendLiveAgentControlRequest(loaded.manifest, { taskId: task.id, agentId: agent.id, operation: operation === "resume-agent" ? "resume" : operation === "steer-agent" ? "steer" : "stop", message: operation === "resume-agent" ? prompt : message });
|
|
263
|
+
const request = appendLiveAgentControlRequest(loaded.manifest, { taskId: task.id, agentId: agent.id, operation: operation === "resume-agent" ? "resume" : operation === "follow-up-agent" ? "follow-up" : operation === "steer-agent" ? "steer" : "stop", message: operation === "resume-agent" || operation === "follow-up-agent" ? prompt : message });
|
|
264
|
+
const mailboxMessage = operation === "steer-agent" ? appendSteeringMessage(loaded.manifest, { taskId: task.id, to: agent.id, body: message ?? "Please report current status and wrap up if possible.", status: "delivered", data: { source: "steer-agent", liveControlRequestId: request.id } }) : operation === "follow-up-agent" && prompt ? appendFollowUpMessage(loaded.manifest, { taskId: task.id, to: agent.id, body: prompt, status: "delivered", data: { source: "follow-up-agent", liveControlRequestId: request.id } }) : undefined;
|
|
248
265
|
publishLiveControlRealtime(request);
|
|
249
266
|
ctx.events?.emit?.("pi-crew:live-control", liveControlRealtimeMessage(request));
|
|
250
|
-
appendEvent(loaded.manifest.eventsPath, { type: "agent.control.queued", runId: loaded.manifest.runId, taskId: agent.taskId, message: `Queued ${request.operation} control request for live agent.`, data: { request, realtime: true } });
|
|
251
|
-
return result(JSON.stringify({ queued: true, request }, null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
267
|
+
appendEvent(loaded.manifest.eventsPath, { type: "agent.control.queued", runId: loaded.manifest.runId, taskId: agent.taskId, message: `Queued ${request.operation} control request for live agent.`, data: { request, mailboxMessageId: mailboxMessage?.id, realtime: true } });
|
|
268
|
+
return result(JSON.stringify({ queued: true, request, mailboxMessage }, null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
252
269
|
} catch (queueError) {
|
|
253
270
|
const message = queueError instanceof Error ? queueError.message : String(queueError);
|
|
254
271
|
return result(message, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
@@ -3,6 +3,8 @@ import { withRunLockSync } from "../../state/locks.ts";
|
|
|
3
3
|
import { loadRunManifestById, saveRunTasks, updateRunStatus } from "../../state/state-store.ts";
|
|
4
4
|
import { saveCrewAgents, recordFromTask } from "../../runtime/crew-agent-records.ts";
|
|
5
5
|
import { writeForegroundInterruptRequest } from "../../runtime/foreground-control.ts";
|
|
6
|
+
import { cancellationReasonFromUnknown } from "../../runtime/cancellation.ts";
|
|
7
|
+
import { appendEvent } from "../../state/event-log.ts";
|
|
6
8
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
7
9
|
import type { PiTeamsToolResult } from "../tool-result.ts";
|
|
8
10
|
import { result, type TeamContext } from "./context.ts";
|
|
@@ -55,6 +57,25 @@ export function abortOwned(
|
|
|
55
57
|
return result;
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
function configFromParams(params: TeamToolParamsValue): Record<string, unknown> | undefined {
|
|
61
|
+
return params.config && typeof params.config === "object" && !Array.isArray(params.config) ? params.config : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function cancelReasonFromParams(params: TeamToolParamsValue): { code: string; message: string } {
|
|
65
|
+
const config = configFromParams(params);
|
|
66
|
+
const rawReason = config?.reason ?? config?.cancelReason;
|
|
67
|
+
const reason = rawReason === undefined ? { code: "caller_cancelled" as const, message: "Run cancelled by user request." } : cancellationReasonFromUnknown(rawReason);
|
|
68
|
+
return { code: reason.code, message: reason.message };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function intentFromParams(params: TeamToolParamsValue): string | undefined {
|
|
72
|
+
const config = configFromParams(params);
|
|
73
|
+
const rawIntent = config?.intent ?? config?._intent;
|
|
74
|
+
if (typeof rawIntent !== "string") return undefined;
|
|
75
|
+
const intent = rawIntent.replace(/\s+/g, " ").trim();
|
|
76
|
+
return intent ? intent.slice(0, 500) : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
58
79
|
export function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
|
|
59
80
|
if (!params.runId) return result("Cancel requires runId.", { action: "cancel", status: "error" }, true);
|
|
60
81
|
const loaded = loadRunManifestById(ctx.cwd, params.runId);
|
|
@@ -65,13 +86,17 @@ export function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): PiT
|
|
|
65
86
|
// Classify tasks for foreign-aware cancellation
|
|
66
87
|
const abortResult = abortOwned(loaded.manifest.runId, undefined, ctx);
|
|
67
88
|
if (abortResult.abortedIds.length === 0 && abortResult.foreignIds.length > 0) {
|
|
68
|
-
return result(`Run ${loaded.manifest.runId} belongs to another session; not cancelled.`, { action: "cancel", status: "error", runId: loaded.manifest.runId, foreignIds: abortResult.foreignIds }
|
|
89
|
+
return result(`Run ${loaded.manifest.runId} belongs to another session; not cancelled.`, { action: "cancel", status: "error", runId: loaded.manifest.runId, foreignIds: abortResult.foreignIds }, true);
|
|
69
90
|
}
|
|
70
91
|
const cancellableIds = new Set(abortResult.abortedIds);
|
|
92
|
+
const cancelReason = cancelReasonFromParams(params);
|
|
93
|
+
const cancelIntent = intentFromParams(params);
|
|
94
|
+
const cancelData = cancelIntent ? { reason: cancelReason.code, intent: cancelIntent } : { reason: cancelReason.code };
|
|
95
|
+
const cancelMessage = `${cancelReason.message} (${cancelReason.code})`;
|
|
71
96
|
|
|
72
97
|
const tasks = loaded.tasks.map((task) => {
|
|
73
98
|
if (cancellableIds.has(task.id) && (task.status === "queued" || task.status === "running" || task.status === "waiting")) {
|
|
74
|
-
return { ...task, status: "cancelled" as const, finishedAt: new Date().toISOString(), error:
|
|
99
|
+
return { ...task, status: "cancelled" as const, finishedAt: new Date().toISOString(), error: cancelMessage };
|
|
75
100
|
}
|
|
76
101
|
return task;
|
|
77
102
|
});
|
|
@@ -82,11 +107,14 @@ export function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): PiT
|
|
|
82
107
|
logInternalError("team-tool.handleCancel.crewAgents", error, `runId=${loaded.manifest.runId}`);
|
|
83
108
|
}
|
|
84
109
|
try {
|
|
85
|
-
writeForegroundInterruptRequest(loaded.manifest,
|
|
110
|
+
writeForegroundInterruptRequest(loaded.manifest, cancelMessage);
|
|
86
111
|
} catch (error) {
|
|
87
112
|
logInternalError("team-tool.handleCancel.interruptRequest", error, `runId=${loaded.manifest.runId}`);
|
|
88
113
|
}
|
|
89
|
-
|
|
114
|
+
for (const taskId of abortResult.abortedIds) {
|
|
115
|
+
appendEvent(loaded.manifest.eventsPath, { type: "task.cancelled", runId: loaded.manifest.runId, taskId, message: cancelMessage, data: cancelData });
|
|
116
|
+
}
|
|
117
|
+
const updated = updateRunStatus(loaded.manifest, "cancelled", `${cancelMessage} Already-finished worker processes are not retroactively changed.`, { data: cancelData });
|
|
90
118
|
|
|
91
119
|
// Build descriptive message including foreign/missing info
|
|
92
120
|
const parts = [`Cancelled run ${updated.runId}.`];
|
|
@@ -101,6 +129,7 @@ export function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): PiT
|
|
|
101
129
|
abortedIds: abortResult.abortedIds,
|
|
102
130
|
missingIds: abortResult.missingIds,
|
|
103
131
|
foreignIds: abortResult.foreignIds,
|
|
132
|
+
intent: cancelIntent,
|
|
104
133
|
});
|
|
105
134
|
});
|
|
106
135
|
}
|
|
@@ -15,6 +15,11 @@ export type TeamContext = Pick<ExtensionContext, "cwd"> & Partial<Pick<Extension
|
|
|
15
15
|
onJsonEvent?: (taskId: string, runId: string, event: unknown) => void;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
export function withSessionId<T extends Pick<ExtensionContext, "sessionManager">>(ctx: T): T & { sessionId?: string } {
|
|
19
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
20
|
+
return sessionId ? { ...ctx, sessionId } : { ...ctx };
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
export function result(text: string, details: TeamToolDetails, isError = false): PiTeamsToolResult {
|
|
19
24
|
return toolResult(text, details, isError);
|
|
20
25
|
}
|