@bermudi/pi-delegate 0.1.1 → 0.1.3

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/types.ts CHANGED
@@ -12,6 +12,7 @@ import type {
12
12
  } from "@earendil-works/pi-coding-agent";
13
13
  import type { Static } from "@sinclair/typebox";
14
14
  import type { delegateArgumentsSchema } from "./schema.ts";
15
+ import type { CallRecord } from "./telemetry.ts";
15
16
 
16
17
  export interface AgentConfig {
17
18
  name: string;
@@ -25,18 +26,37 @@ export interface AgentConfig {
25
26
  }
26
27
 
27
28
  // ── Tool parameter types — derived from the TypeBox schema ────────────────
28
- // `delegateArgumentsSchema` in schema.ts is the single source of truth; these are
29
- // projections of it, so schema and types cannot drift. Field semantics live
30
- // in the schema's `description`s (which the calling model also sees).
31
- // The import is type-only, so the schema.ts ↔ types.ts cycle is erased at
32
- // compile time.
33
-
34
- export type DelegateArguments = Static<typeof delegateArgumentsSchema>;
35
- export type TaskDef = NonNullable<DelegateArguments["tasks"]>[number];
29
+ // `delegateArgumentsSchema` in schema.ts is the canonical provider-visible
30
+ // shape. The public types add deprecated `action` aliases so existing TypeScript
31
+ // callers remain source-compatible without advertising the overloaded fields to
32
+ // models. The import is type-only, so the schema.ts ↔ types.ts cycle is erased
33
+ // at compile time.
34
+
35
+ type CanonicalDelegateArguments = Static<typeof delegateArgumentsSchema>;
36
+ type CanonicalTaskDef = NonNullable<
37
+ CanonicalDelegateArguments["tasks"]
38
+ >[number];
39
+
36
40
  /** Top-level async ticket action: "poll" | "cancel" | "wait". */
37
- export type DelegateAction = NonNullable<DelegateArguments["action"]>;
41
+ export type TicketAction = NonNullable<
42
+ CanonicalDelegateArguments["ticketAction"]
43
+ >;
38
44
  /** Per-task session action: "prompt" | "close" | "list". */
39
- export type SessionAction = NonNullable<TaskDef["action"]>;
45
+ export type SessionAction = NonNullable<CanonicalTaskDef["sessionAction"]>;
46
+
47
+ export type TaskDef = CanonicalTaskDef & {
48
+ /** @deprecated Use `sessionAction` instead. Runtime normalization still accepts this alias. */
49
+ action?: SessionAction;
50
+ };
51
+
52
+ export type DelegateArguments = Omit<CanonicalDelegateArguments, "tasks"> & {
53
+ /** @deprecated Use `ticketAction` instead. Runtime normalization still accepts this alias. */
54
+ action?: TicketAction;
55
+ tasks?: TaskDef[];
56
+ };
57
+
58
+ /** @deprecated Use `TicketAction` instead. */
59
+ export type DelegateAction = TicketAction;
40
60
 
41
61
  // ── Async Ticket Types ─────────────────────────────────────────────────────
42
62
 
@@ -61,8 +81,22 @@ export interface AsyncTicket {
61
81
  controller: AbortController;
62
82
  error?: string;
63
83
  parentModelId?: string;
84
+ /** Session-tree leaf active when the ticket was spawned (see leaf.ts).
85
+ * `undefined` = the leaf the session opened on. Compared at delivery time
86
+ * so results are not used to wake the agent on a foreign branch. */
87
+ spawnLeafId?: string | null;
64
88
  /** Active blocking waiters. Resolved by terminal delivery or timeout/abort. */
65
89
  waiters?: TicketWaiter[];
90
+ /** Telemetry call span id attached to this async ticket. */
91
+ callId?: string;
92
+ /** Telemetry call span start timestamp for accurate wall-time on cancellation. */
93
+ callStartedAt?: number;
94
+ /** Snapshot of the call row at spawn, used to write the cancelled/settled row. */
95
+ callRecord?: CallRecord;
96
+ /** Runtime generation for rejecting shutdown writes from stale tickets. */
97
+ telemetryGeneration?: number;
98
+ /** Resolves after every async worker has settled, including shutdown aborts. */
99
+ completion?: Promise<void>;
66
100
  }
67
101
 
68
102
  /** Live parent settings captured when a delegate call starts. The built-in
@@ -81,6 +115,7 @@ export interface ReuseIntent {
81
115
  }
82
116
 
83
117
  export interface ResolvedTask {
118
+ id?: string;
84
119
  prompt: string;
85
120
  agent?: string;
86
121
  model: Model<Api>;
@@ -90,8 +125,10 @@ export interface ResolvedTask {
90
125
  cwd: string;
91
126
  context?: "fresh" | "with-parent-transcript";
92
127
  sessionId?: string;
93
- action?: SessionAction;
128
+ sessionAction?: SessionAction;
94
129
  resumeFrom?: string;
130
+ /** Hard wall-clock budget in milliseconds, measured from task start. */
131
+ deadlineMs?: number;
95
132
  agentName: string;
96
133
  warnings: string[];
97
134
  /** Explicit settings that must match a live pooled session on reuse. */
@@ -117,10 +154,14 @@ export interface ToolActivity {
117
154
  * - `model_error`: the failure is attributable to the resolved model/provider
118
155
  * (account usage limit, quota exhausted, auth lost) — not transient for that
119
156
  * model, so same-model retry is pointless. The parent should resume with a
120
- * different `model` (see `resumeFrom` + `model`). */
121
- export type TaskFailureKind = "stalled" | "model_error";
157
+ * different `model` (see `resumeFrom` + `model`).
158
+ * - `deadline_exceeded`: the task's `deadlineMs` wall-clock budget expired
159
+ * (measured from when the task left the concurrency queue). The prompt was
160
+ * cooperatively aborted; completed side effects are not rolled back. */
161
+ export type TaskFailureKind = "stalled" | "model_error" | "deadline_exceeded";
122
162
 
123
163
  export interface TaskProgress {
164
+ id?: string;
124
165
  index: number;
125
166
  agent: string;
126
167
  task: string;
@@ -146,9 +187,13 @@ export interface DelegateDetails {
146
187
  ticketId?: string;
147
188
  /** Terminal/live ticket status when this result comes from an async ticket. */
148
189
  status?: AsyncTicket["status"];
190
+ /** Global overlap warning derived from result.attributedFiles, surfaced in both
191
+ * the textual content and the custom TUI. */
192
+ overlapWarning?: string;
149
193
  }
150
194
 
151
195
  export interface TaskResult {
196
+ id?: string;
152
197
  agent: string;
153
198
  output: string;
154
199
  error?: string;
@@ -167,7 +212,14 @@ export interface TaskResult {
167
212
  * Pi sums `cost.total` for nested usage anyway. */
168
213
  usage: Usage;
169
214
  sessionFile?: string;
215
+ /** All files the subagent is known to have touched, including bash mutations
216
+ * captured via git diff and other tasks' concurrent git changes. This is a
217
+ * best-effort, repository-wide list for display, not for attribution. */
170
218
  touchedFiles: string[];
219
+ /** Files directly attributable to this task's edit/write tool calls. Used
220
+ * for overlap detection so concurrent tasks in the same repo do not
221
+ * fabricate false conflicts from shared git snapshots. */
222
+ attributedFiles?: string[];
171
223
  }
172
224
 
173
225
  /** Single source of truth for a subagent's runtime configuration.
@@ -204,6 +256,12 @@ export interface TaskRunEnv {
204
256
  onProgress: (p: TaskProgress, u: AgentProgressUpdate) => void;
205
257
  /** Called after every TaskProgress mutation (early-returns, completion). Sync uses this to fire onUpdate. */
206
258
  onStatusChange?: () => void;
259
+ /** Telemetry call id for this dispatch. undefined when telemetry is disabled or not started. */
260
+ telemetryCallId?: string;
261
+ /** Runtime generation for rejecting writes from a stale shutdown worker. */
262
+ telemetryGeneration?: number;
263
+ /** Whether this task is part of an async ticket. */
264
+ async?: boolean;
207
265
  }
208
266
 
209
267
  /** Structural subset of Pi's `ExtensionContext` used by delegate's
package/usage.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AgentSession } from "@earendil-works/pi-coding-agent";
2
2
  import type { Usage } from "@earendil-works/pi-ai";
3
+ import type { TaskResult } from "./types.ts";
3
4
 
4
5
  /** Snapshot of the cumulative session usage fields we read for delta accounting. */
5
6
  export interface SessionUsageSnapshot {
@@ -119,3 +120,21 @@ export function sumUsage(usages: readonly (Usage | undefined)[]): Usage {
119
120
  emptyUsage(),
120
121
  );
121
122
  }
123
+
124
+ /** Aggregate the completed result rows used by async call telemetry. */
125
+ export function aggregateTaskResults(
126
+ results: readonly (TaskResult | undefined)[],
127
+ ): { totalTokens: number; totalCost: number } {
128
+ return results
129
+ .filter(
130
+ (result): result is TaskResult =>
131
+ result !== undefined && "touchedFiles" in result,
132
+ )
133
+ .reduce(
134
+ (total, result) => ({
135
+ totalTokens: total.totalTokens + result.tokens,
136
+ totalCost: total.totalCost + (result.usage?.cost?.total ?? 0),
137
+ }),
138
+ { totalTokens: 0, totalCost: 0 },
139
+ );
140
+ }