pi-extended-teams 2.2.10 → 2.3.1

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 (58) hide show
  1. package/README.md +182 -2
  2. package/extensions/agents/read-agent-report.ts +11 -2
  3. package/extensions/agents/read-agent-session-lifecycle.ts +25 -10
  4. package/extensions/agents/read-agent.ts +387 -65
  5. package/extensions/agents/write-agent.ts +3 -0
  6. package/extensions/events/register-events.ts +37 -21
  7. package/extensions/index.ts +257 -60
  8. package/extensions/internal/pi-check-operations.ts +24 -0
  9. package/extensions/internal/session-files.ts +10 -2
  10. package/extensions/runtime/lead-attachment.ts +91 -0
  11. package/extensions/runtime/reload-owner.ts +107 -0
  12. package/extensions/runtime/team-host.ts +124 -0
  13. package/extensions/runtime/teammate-interrupt.ts +11 -5
  14. package/extensions/runtime/types.ts +4 -0
  15. package/extensions/team/lifecycle.ts +26 -0
  16. package/extensions/team/session-cost.ts +225 -0
  17. package/extensions/tools/agent-communication-tools.ts +24 -8
  18. package/extensions/tools/agent-status-tool.ts +50 -7
  19. package/extensions/tools/coordination-tools.ts +78 -8
  20. package/extensions/tools/public-team-tools.ts +51 -0
  21. package/extensions/tools/task-runtime-tools.ts +23 -4
  22. package/extensions/tools/team-tools.ts +365 -107
  23. package/extensions/ui/activity-colors.ts +50 -0
  24. package/extensions/ui/agent-follow-view.ts +15 -11
  25. package/extensions/ui/agent-navigation.ts +35 -1
  26. package/extensions/ui/checkpoints-command.ts +28 -0
  27. package/extensions/ui/onboarding-command.ts +218 -0
  28. package/extensions/ui/status-widget.ts +22 -14
  29. package/package.json +1 -1
  30. package/skills/teams.md +16 -0
  31. package/src/orchestration/index.ts +19 -1
  32. package/src/orchestration/status-projection.ts +2 -3
  33. package/src/orchestration/types.ts +9 -1
  34. package/src/results/check-journal.ts +149 -0
  35. package/src/results/check-policy.ts +86 -0
  36. package/src/results/check-runner.ts +110 -0
  37. package/src/results/checkpoint-assignment.ts +60 -0
  38. package/src/results/checkpoint-policy.ts +20 -0
  39. package/src/results/checkpoint-report.ts +109 -0
  40. package/src/results/checkpoint-retention.ts +15 -0
  41. package/src/results/completion-group-delivery.ts +66 -0
  42. package/src/results/completion-group-recovery.ts +61 -0
  43. package/src/results/completion-group-wake.ts +62 -0
  44. package/src/results/completion-group.ts +290 -0
  45. package/src/results/continuation-recipient.ts +37 -0
  46. package/src/results/durable-json.ts +29 -0
  47. package/src/results/repair-policy.ts +24 -0
  48. package/src/results/report-result.ts +77 -0
  49. package/src/results/source-identity.ts +115 -0
  50. package/src/results/specialist-checkpoint.ts +227 -0
  51. package/src/results/verification-controller.ts +308 -0
  52. package/src/utils/messaging.ts +47 -10
  53. package/src/utils/models.ts +13 -0
  54. package/src/utils/paths.ts +4 -0
  55. package/src/utils/report-events.ts +95 -10
  56. package/src/utils/settings.ts +24 -0
  57. package/src/utils/teams.ts +3 -1
  58. package/src/utils/write-queue.ts +3 -0
@@ -0,0 +1,51 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { StringEnum } from "../internal/schema";
3
+ import { ACCEPTED_FAVORITE_MODEL_SLOTS } from "../../src/utils/settings";
4
+ import { CheckPolicySchema } from "../../src/results/check-policy";
5
+ import { RepairPolicySchema } from "../../src/results/repair-policy";
6
+ import { CheckpointPolicySchema } from "../../src/results/checkpoint-policy";
7
+ import { CompletionGroupPolicySchema } from "../../src/results/completion-group";
8
+
9
+ const levelDescription = "Required intent tier. Configured favorites take priority; an unconfigured tier inherits the current lead model and thinking. Read tiers: read-collect gathers bounded facts without owning the conclusion; read-review is the normal default for focused review, verification, and bounded synthesis; read-analyze explains behavior or root cause across connected evidence; read-critical is only for irreducible high-stakes security, architecture, concurrency, migration, or data-correctness reasoning. Write tiers: write-patch makes a narrow localized change; write-feature implements a bounded feature with a known design; write-system owns a cross-cutting integration or refactor within explicitly claimed files; write-critical is only for high-risk security, concurrency, recovery, migration, or data-integrity changes. Prefer canonical tiers; legacy reading-*/writing-* aliases remain accepted for this minor release. Do not pass role, model, or thinking directly; see README.md.";
10
+ const base = {
11
+ name: Type.Optional(Type.String({ description: "Stable display name. Defaults to a generated agent name." })),
12
+ prompt: Type.String({ description: "The agent's assignment, relevant prior context, evidence already gathered, constraints, and report shape." }),
13
+ cwd: Type.Optional(Type.String({ description: "Working directory. Defaults to the lead session cwd." })),
14
+ checks: Type.Optional(CheckPolicySchema),
15
+ repair: Type.Optional(RepairPolicySchema),
16
+ checkpoint: Type.Optional(CheckpointPolicySchema),
17
+ continue_from: Type.Optional(Type.String({ description: "Continue this checkpoint as a fresh recipient/run with the current assignment and tier. name is a prefix, not a recipient to reuse." })),
18
+ session_context: Type.Optional(StringEnum(["none", "lazy"] as const, { description: "Optional filtered snapshot of the lead's active session branch. Use lazy only when omitted session history may materially affect the lane; the child reads it on demand rather than receiving transcript content in its prompt.", default: "none" })),
19
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Any())),
20
+ allow_nested_read_agents: Type.Optional(Type.Boolean({ description: "Opt in eligible depth-0 write-feature/write-critical agents to restricted read-only child spawning.", default: false })),
21
+ };
22
+
23
+ // Metadata is available before session_start without creating a second queue,
24
+ // lifecycle controller, or execution owner during host reload.
25
+ export const publicTeamToolMetadata = {
26
+ get_agent_status: {
27
+ name: "get_agent_status", label: "Get Agent Status",
28
+ description: "Get one read-only snapshot of one or all agents owned by this parent. Omit agent_name to inspect all. This call is allowed when current status is needed; it never waits, polls, stops, or changes agents. Do not call it repeatedly. Final reports arrive automatically and resume this agent.",
29
+ parameters: Type.Object({ agent_name: Type.Optional(Type.String({ description: "One owned agent to inspect. Omit to inspect all owned agents." })) }),
30
+ },
31
+ spawn_agent: {
32
+ name: "spawn_agent", label: "Spawn Agent",
33
+ description: "Spawn one agent by intent tier only. Configured favorites take priority; an unconfigured tier inherits the current lead model and thinking. Give it the relevant goal, decisions, prior attempts, inspected evidence, constraints, and expected delta rather than a context-free task. Use session_context=lazy only as an on-demand fallback when omitted session history may materially matter; it never replaces a good mission prompt. read-review is the normal read default; use read-collect for bounded fact gathering, read-analyze for connected explanation/root cause, and read-critical only for irreducible high-stakes reasoning. For edits, choose write-patch, write-feature, write-system, or the rare high-risk write-critical by scope and risk. After spawning, do not duplicate or take over its lane; work only on unrelated work, then end the turn so the automatic report can resume you. One get_agent_status snapshot is allowed when current status is needed; never sleep, busy-wait, repeatedly read inbox/status, or treat healthy silence as failure. Wait for the actual report before synthesizing; intervene only on a reported blocker/error, actual health failure, or explicit user cancellation. model_slot selects behavior, model, and thinking; do not pass role, model, or thinking directly.",
34
+ parameters: Type.Object({ ...base, model_slot: StringEnum(ACCEPTED_FAVORITE_MODEL_SLOTS, { description: levelDescription, default: "read-review" }) }),
35
+ },
36
+ spawn_swarm_agents: {
37
+ name: "spawn_swarm_agents", label: "Spawn Swarm Agents",
38
+ description: "Spawn a batch by intent tier only. Configured favorites take priority; unconfigured tiers inherit the current lead model and thinking. Give each lane the relevant goal, decisions, prior attempts, inspected evidence, constraints, and expected delta. Use session_context=lazy selectively as an on-demand fallback, never instead of a good mission prompt. Use read-review as the normal default, read-collect for bounded collection lanes, read-analyze for connected explanation, and read-critical only for irreducible high-stakes reasoning; choose write-patch/feature/system/critical by edit scope and risk. Each spawned lane is delegation-locked: do not duplicate or take it over. After unrelated work is done, end the turn so automatic reports can resume you. One get_agent_status snapshot is allowed when current status is needed; never sleep, busy-wait, repeatedly read inbox/status, or intervene early. Synthesize only after actual reports; intervene only on blocker/error, actual failure, or explicit cancellation. Each agent gets model_slot directly or from defaults; do not pass role, model, or thinking directly.",
39
+ parameters: Type.Object({
40
+ defaults: Type.Optional(Type.Object({
41
+ cwd: Type.Optional(Type.String()), checks: Type.Optional(CheckPolicySchema), repair: Type.Optional(RepairPolicySchema),
42
+ checkpoint: Type.Optional(CheckpointPolicySchema), model_slot: Type.Optional(StringEnum(ACCEPTED_FAVORITE_MODEL_SLOTS, { description: levelDescription })),
43
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Any())),
44
+ session_context: Type.Optional(StringEnum(["none", "lazy"] as const, { description: "Shared lazy session-reference policy.", default: "none" })),
45
+ allow_nested_read_agents: Type.Optional(Type.Boolean({ description: "Shared opt-in for eligible depth-0 write-feature/write-critical agents.", default: false })),
46
+ })),
47
+ completion_group: Type.Optional(CompletionGroupPolicySchema),
48
+ agents: Type.Array(Type.Object({ ...base, model_slot: Type.Optional(StringEnum(ACCEPTED_FAVORITE_MODEL_SLOTS, { description: levelDescription })) }), { description: "Agents to spawn as one batch. Each one must have model_slot directly or inherit it from defaults." }),
49
+ }),
50
+ },
51
+ };
@@ -11,6 +11,7 @@ import type { ReadAgentTeardownResult } from "../agents/read-agent-session-lifec
11
11
  import type { ShutdownTeammateOptions } from "../team/lifecycle";
12
12
  import { readLifecycleTombstone } from "../../src/utils/lifecycle-tombstone";
13
13
  import type { TeammateInterruptResult } from "../runtime/teammate-interrupt";
14
+ import { effectiveTaskOutcome } from "../../src/results/report-result";
14
15
 
15
16
  export interface TaskRuntimeToolsOptions {
16
17
  isTeammate: boolean;
@@ -18,7 +19,7 @@ export interface TaskRuntimeToolsOptions {
18
19
  runningReadAgents: Map<string, RunningReadAgent>;
19
20
  readAgentKey(teamName: string, agentName: string): string;
20
21
  interruptTeammate?(agentName: string): Promise<TeammateInterruptResult>;
21
- cancelQueuedAgent?(teamName: string, agentName: string): boolean;
22
+ cancelQueuedAgent?(teamName: string, agentName: string): boolean | Promise<boolean>;
22
23
  shutdownTeammate(teamName: string, member: Member, options?: ShutdownTeammateOptions): Promise<ReadAgentTeardownResult>;
23
24
  getTeamName(): string | null | undefined;
24
25
  }
@@ -56,7 +57,7 @@ export function registerTaskRuntimeTools(pi: any, options: TaskRuntimeToolsOptio
56
57
  const teamName = options.getTeamName();
57
58
  if (!teamName) throw new Error("No active agent session. Spawn an agent first.");
58
59
 
59
- if (options.cancelQueuedAgent?.(teamName, params.agent_name)) {
60
+ if (await options.cancelQueuedAgent?.(teamName, params.agent_name)) {
60
61
  return {
61
62
  content: [{ type: "text", text: `Cancelled queued agent ${params.agent_name}.` }],
62
63
  details: { session: teamName, agentName: params.agent_name, stopped: true, queued: true, reason: params.reason },
@@ -147,6 +148,10 @@ export function registerTaskRuntimeTools(pi: any, options: TaskRuntimeToolsOptio
147
148
  if (!member) {
148
149
  const fence = await readLifecycleTombstone(teamName, params.agent_name);
149
150
  if (fence.status !== "absent") {
151
+ const completedReport = !options.isTeammate && fence.status === "occupied"
152
+ ? (await reportEvents.listTeamReportEvents(teamName, { agentName: params.agent_name }))
153
+ .filter(report => report.result?.runId === fence.tombstone.runId && report.result.repair).at(-1)
154
+ : undefined;
150
155
  const details = {
151
156
  agentName: params.agent_name,
152
157
  alive: false,
@@ -157,12 +162,17 @@ export function registerTaskRuntimeTools(pi: any, options: TaskRuntimeToolsOptio
157
162
  startupStalled: false,
158
163
  runtime: await runtime.readRuntimeStatus(teamName, params.agent_name).catch(() => null),
159
164
  teardownState: "quarantined",
165
+ ...(completedReport ? { completedReport } : {}),
160
166
  removedMember: false,
161
167
  releasedClaims: [],
162
168
  tombstone: fence.status === "occupied" ? fence.tombstone : undefined,
163
169
  error: fence.status === "corrupt" ? fence.error : undefined,
164
170
  };
165
- return { content: [{ type: "text", text: formatTeammateStatusForModel(params.agent_name, details) }], details };
171
+ const text = [formatTeammateStatusForModel(params.agent_name, details),
172
+ completedReport ? `Full report: ${completedReport.reportPath}; repair ledger: ${completedReport.result!.repair!.journalPath}` : undefined,
173
+ ...(completedReport?.checks?.map(check => `Check ${check.checkId}: ${check.state}; full log: ${check.logPath}`) ?? []),
174
+ ].filter(Boolean).join("\n\n");
175
+ return { content: [{ type: "text", text }], details };
166
176
  }
167
177
  if (options.isTeammate) throw new Error(`Agent ${params.agent_name} not found`);
168
178
  const latestReport = (await reportEvents.listTeamReportEvents(teamName, {
@@ -177,6 +187,14 @@ export function registerTaskRuntimeTools(pi: any, options: TaskRuntimeToolsOptio
177
187
  : undefined;
178
188
  const text = [
179
189
  `Agent ${params.agent_name} is no longer active. Recovered its latest persisted ${latestReport.status} report from ${reportPath}.`,
190
+ latestReport.result
191
+ ? `Task: ${latestReport.result.outcome ?? "unspecified"}; verification: ${latestReport.result.verification.state}; acceptance: ${latestReport.result.acceptance.state}. Report ID: ${latestReport.result.reportId}.`
192
+ : undefined,
193
+ latestReport.result?.repair
194
+ ? `Repair: ${latestReport.result.repair.state}; effective task: ${effectiveTaskOutcome(latestReport.result) ?? "unspecified"}; ledger: ${latestReport.result.repair.journalPath}.`
195
+ : undefined,
196
+ latestReport.checks?.map(check => `Check ${check.checkId}: ${check.state}; exit ${check.exitCode ?? "unknown"}; full log: ${check.logPath}`).join("\n"),
197
+ latestReport.result?.verification.error,
180
198
  recoverySessionFile
181
199
  ? `Its durable child transcript is available at ${recoverySessionFile}. Use the read tool on that path if the report is incomplete.`
182
200
  : undefined,
@@ -246,7 +264,8 @@ export function registerTaskRuntimeTools(pi: any, options: TaskRuntimeToolsOptio
246
264
  removedMember: teardown?.removedMember ?? false,
247
265
  error: teardown?.error ?? lifecycleError,
248
266
  };
249
- return { content: [{ type: "text", text: formatTeammateStatusForModel(params.agent_name, details) }], details };
267
+ const text = [formatTeammateStatusForModel(params.agent_name, details), details.error].filter(Boolean).join("\n\n");
268
+ return { content: [{ type: "text", text }], details };
250
269
  },
251
270
  renderResult(result: any, { expanded }: any, theme: any) {
252
271
  return renderTeammateStatus(result, expanded, theme);