@bermudi/pi-delegate 0.1.11 → 0.1.13
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 +57 -4
- package/concurrency.ts +55 -16
- package/config.ts +584 -50
- package/delegate.ts +8 -1
- package/dispatch.ts +410 -38
- package/extension.ts +14 -0
- package/format.ts +50 -4
- package/host.ts +6 -1
- package/isolated-workspace.ts +857 -0
- package/lifecycle.ts +50 -16
- package/manual.ts +13 -9
- package/package.json +1 -1
- package/pool.ts +23 -1
- package/provider-extensions.ts +11 -2
- package/render-branches.ts +30 -0
- package/render-result.ts +8 -5
- package/runner.ts +3 -1
- package/schema.ts +58 -51
- package/settings.ts +202 -84
- package/shared-write-safety.ts +273 -0
- package/task-resolution.ts +87 -16
- package/telemetry.ts +135 -68
- package/ticket-format.ts +13 -5
- package/tickets.ts +23 -11
- package/types.ts +79 -0
package/tickets.ts
CHANGED
|
@@ -169,6 +169,7 @@ export function cancelTicketForShutdown(ticket: AsyncTicket): void {
|
|
|
169
169
|
total_cost: totalCost,
|
|
170
170
|
},
|
|
171
171
|
ticket.telemetryGeneration,
|
|
172
|
+
ticket.telemetryConfig,
|
|
172
173
|
);
|
|
173
174
|
}
|
|
174
175
|
}
|
|
@@ -238,6 +239,9 @@ export function formatCompletedTicket(
|
|
|
238
239
|
parts.push(
|
|
239
240
|
`${statusTag}${succeeded}/${ticket.results.length} ${completionLabel} · ${fmtDuration(elapsedTotal)} wall time\n`,
|
|
240
241
|
);
|
|
242
|
+
if (ticket.dispatchWarning) {
|
|
243
|
+
parts.push(`WARNING: ${ticket.dispatchWarning}`);
|
|
244
|
+
}
|
|
241
245
|
|
|
242
246
|
const pendingLabelFor = (index: number): string => {
|
|
243
247
|
if (ticket.status !== "cancelled") {
|
|
@@ -258,7 +262,7 @@ export function formatCompletedTicket(
|
|
|
258
262
|
parts.push(`[${pendingLabelFor(i)}]`);
|
|
259
263
|
continue;
|
|
260
264
|
}
|
|
261
|
-
parts.push(...formatCompletedTask(t, r));
|
|
265
|
+
parts.push(...formatCompletedTask(t, r, ticket.config));
|
|
262
266
|
}
|
|
263
267
|
|
|
264
268
|
const completedResults = ticket.results.filter(
|
|
@@ -294,6 +298,7 @@ export function formatCompletedTicket(
|
|
|
294
298
|
ticketId: ticket.id,
|
|
295
299
|
status: ticket.status,
|
|
296
300
|
overlapWarning: overlapWarning || undefined,
|
|
301
|
+
dispatchWarning: ticket.dispatchWarning,
|
|
297
302
|
},
|
|
298
303
|
};
|
|
299
304
|
}
|
|
@@ -336,9 +341,21 @@ function buildWaitDetails(ticket: AsyncTicket): DelegateDetails {
|
|
|
336
341
|
ticketId: ticket.id,
|
|
337
342
|
status: ticket.status,
|
|
338
343
|
overlapWarning: overlapWarning || undefined,
|
|
344
|
+
dispatchWarning: ticket.dispatchWarning,
|
|
339
345
|
};
|
|
340
346
|
}
|
|
341
347
|
|
|
348
|
+
function appendDispatchWarnings(
|
|
349
|
+
base: string,
|
|
350
|
+
details: DelegateDetails,
|
|
351
|
+
): string {
|
|
352
|
+
const warnings = [
|
|
353
|
+
details.dispatchWarning ? `WARNING: ${details.dispatchWarning}` : undefined,
|
|
354
|
+
details.overlapWarning,
|
|
355
|
+
].filter((warning): warning is string => warning !== undefined);
|
|
356
|
+
return warnings.length ? `${base}\n\n${warnings.join("\n\n")}` : base;
|
|
357
|
+
}
|
|
358
|
+
|
|
342
359
|
function buildWaitRunningUpdate(
|
|
343
360
|
ticket: AsyncTicket,
|
|
344
361
|
): AgentToolResult<DelegateDetails> {
|
|
@@ -358,9 +375,7 @@ function buildWaitRunningUpdate(
|
|
|
358
375
|
if (pending > 0) parts.push(`${pending} queued`);
|
|
359
376
|
|
|
360
377
|
const details = buildWaitDetails(ticket);
|
|
361
|
-
const text =
|
|
362
|
-
parts.join(" · ") +
|
|
363
|
-
(details.overlapWarning ? `\n\n${details.overlapWarning}` : "");
|
|
378
|
+
const text = appendDispatchWarnings(parts.join(" · "), details);
|
|
364
379
|
return {
|
|
365
380
|
content: [{ type: "text", text }],
|
|
366
381
|
details,
|
|
@@ -398,8 +413,7 @@ function buildWaitAbortResult(
|
|
|
398
413
|
): AgentToolResult<DelegateDetails> {
|
|
399
414
|
const details = buildWaitDetails(ticket);
|
|
400
415
|
const base = `Wait for ticket ${ticket.id} aborted · ticket continues ${ticket.status} in the background`;
|
|
401
|
-
const text =
|
|
402
|
-
base + (details.overlapWarning ? `\n\n${details.overlapWarning}` : "");
|
|
416
|
+
const text = appendDispatchWarnings(base, details);
|
|
403
417
|
return {
|
|
404
418
|
content: [{ type: "text", text }],
|
|
405
419
|
details,
|
|
@@ -613,6 +627,7 @@ export function handlePoll(
|
|
|
613
627
|
ticketId: ticket.id,
|
|
614
628
|
status: ticket.status,
|
|
615
629
|
overlapWarning: snapshot.overlapWarning || undefined,
|
|
630
|
+
dispatchWarning: ticket.dispatchWarning,
|
|
616
631
|
},
|
|
617
632
|
};
|
|
618
633
|
}
|
|
@@ -656,9 +671,7 @@ export function handleCancel(params: {
|
|
|
656
671
|
}
|
|
657
672
|
if (!params.force) {
|
|
658
673
|
const details = buildWaitDetails(ticket);
|
|
659
|
-
const text =
|
|
660
|
-
formatCancelPreview(ticket) +
|
|
661
|
-
(details.overlapWarning ? `\n\n${details.overlapWarning}` : "");
|
|
674
|
+
const text = appendDispatchWarnings(formatCancelPreview(ticket), details);
|
|
662
675
|
return {
|
|
663
676
|
content: [{ type: "text", text }],
|
|
664
677
|
details,
|
|
@@ -667,8 +680,7 @@ export function handleCancel(params: {
|
|
|
667
680
|
requestTicketCancel(ticket);
|
|
668
681
|
const details = buildWaitDetails(ticket);
|
|
669
682
|
const base = `Ticket '${ticketId}' is cancelling; workers are settling. Poll for final status.`;
|
|
670
|
-
const text =
|
|
671
|
-
base + (details.overlapWarning ? `\n\n${details.overlapWarning}` : "");
|
|
683
|
+
const text = appendDispatchWarnings(base, details);
|
|
672
684
|
return {
|
|
673
685
|
content: [{ type: "text", text }],
|
|
674
686
|
details,
|
package/types.ts
CHANGED
|
@@ -99,8 +99,19 @@ export interface AsyncTicket {
|
|
|
99
99
|
callRecord?: CallRecord;
|
|
100
100
|
/** Runtime generation for rejecting shutdown writes from stale tickets. */
|
|
101
101
|
telemetryGeneration?: number;
|
|
102
|
+
/** Telemetry config captured at dispatch; binds shutdown aggregate rows to the
|
|
103
|
+
* same backend the call span wrote to. */
|
|
104
|
+
telemetryConfig?: import("./config.ts").TelemetryConfig;
|
|
102
105
|
/** Resolves after every async worker has settled, including shutdown aborts. */
|
|
103
106
|
completion?: Promise<void>;
|
|
107
|
+
/** False from admission until every worker has quiesced. Unlike `status`,
|
|
108
|
+
* this remains false during shutdown's early terminal transition. */
|
|
109
|
+
workersSettled?: boolean;
|
|
110
|
+
/** Batch-level warning attached to this dispatch, not to any one task. */
|
|
111
|
+
dispatchWarning?: string;
|
|
112
|
+
/** Immutable dispatch-scoped delegate.json snapshot used by async workers and
|
|
113
|
+
* later result formatting. */
|
|
114
|
+
config?: import("./config.ts").DelegateConfig;
|
|
104
115
|
}
|
|
105
116
|
|
|
106
117
|
/** Live parent settings captured when a delegate call starts. The built-in
|
|
@@ -138,6 +149,11 @@ export interface ResolvedTask {
|
|
|
138
149
|
warnings: string[];
|
|
139
150
|
/** Explicit settings that must match a live pooled session on reuse. */
|
|
140
151
|
reuseIntent?: ReuseIntent;
|
|
152
|
+
/** Stable signature of the provider-scoped extension allowlist for this task's
|
|
153
|
+
* model provider. Pool reuse compares this to the frozen session value so a
|
|
154
|
+
* revoked or reconfigured extension cannot continue executing in a reused
|
|
155
|
+
* session. */
|
|
156
|
+
providerExtensionSources?: string;
|
|
141
157
|
}
|
|
142
158
|
|
|
143
159
|
export interface ToolActivity {
|
|
@@ -195,6 +211,8 @@ export interface DelegateDetails {
|
|
|
195
211
|
/** Global overlap warning derived from result.attributedFiles, surfaced in both
|
|
196
212
|
* the textual content and the custom TUI. */
|
|
197
213
|
overlapWarning?: string;
|
|
214
|
+
/** Warning that applies to the whole dispatch rather than an individual task. */
|
|
215
|
+
dispatchWarning?: string;
|
|
198
216
|
}
|
|
199
217
|
|
|
200
218
|
export interface TaskResult {
|
|
@@ -227,8 +245,58 @@ export interface TaskResult {
|
|
|
227
245
|
* for overlap detection so concurrent tasks in the same repo do not
|
|
228
246
|
* fabricate false conflicts from shared git snapshots. */
|
|
229
247
|
attributedFiles?: string[];
|
|
248
|
+
/** Git-native proposal/reconciliation outcome for workspace:"isolated". */
|
|
249
|
+
integration?: TaskIntegration;
|
|
230
250
|
}
|
|
231
251
|
|
|
252
|
+
export type TaskIntegrationStatus =
|
|
253
|
+
| "applied_unverified"
|
|
254
|
+
| "no_changes"
|
|
255
|
+
| "conflict"
|
|
256
|
+
| "discarded"
|
|
257
|
+
| "apply_failed";
|
|
258
|
+
|
|
259
|
+
interface TaskIntegrationFiles {
|
|
260
|
+
proposedFiles: string[];
|
|
261
|
+
appliedFiles: string[];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface TaskIntegrationWithoutRecovery extends TaskIntegrationFiles {
|
|
265
|
+
conflicts?: never;
|
|
266
|
+
baselineRef?: never;
|
|
267
|
+
proposalRef?: never;
|
|
268
|
+
patchPath?: never;
|
|
269
|
+
worktreePath?: never;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export type TaskIntegration =
|
|
273
|
+
| (TaskIntegrationWithoutRecovery & {
|
|
274
|
+
status: "applied_unverified";
|
|
275
|
+
})
|
|
276
|
+
| (TaskIntegrationWithoutRecovery & {
|
|
277
|
+
status: "no_changes";
|
|
278
|
+
})
|
|
279
|
+
| (TaskIntegrationWithoutRecovery & {
|
|
280
|
+
status: "discarded";
|
|
281
|
+
})
|
|
282
|
+
| (TaskIntegrationFiles & {
|
|
283
|
+
status: "conflict";
|
|
284
|
+
conflicts: Array<{ path: string; reason: string }>;
|
|
285
|
+
baselineRef: string;
|
|
286
|
+
proposalRef: string;
|
|
287
|
+
patchPath: string;
|
|
288
|
+
worktreePath: string;
|
|
289
|
+
})
|
|
290
|
+
| (TaskIntegrationFiles & {
|
|
291
|
+
status: "apply_failed";
|
|
292
|
+
conflicts: Array<{ path: string; reason: string }>;
|
|
293
|
+
/** Recovery metadata exists when reconciliation reached a proposal. */
|
|
294
|
+
baselineRef?: string;
|
|
295
|
+
proposalRef?: string;
|
|
296
|
+
patchPath?: string;
|
|
297
|
+
worktreePath?: string;
|
|
298
|
+
});
|
|
299
|
+
|
|
232
300
|
/** Single source of truth for a subagent's runtime configuration.
|
|
233
301
|
* Passed to `createAgentSession` as `model` / `thinkingLevel` / `tools` / `cwd`. */
|
|
234
302
|
export interface AgentRunConfig {
|
|
@@ -265,8 +333,15 @@ export interface TaskRunEnv {
|
|
|
265
333
|
telemetryCallId?: string;
|
|
266
334
|
/** Runtime generation for rejecting writes from a stale shutdown worker. */
|
|
267
335
|
telemetryGeneration?: number;
|
|
336
|
+
/** Telemetry config captured at dispatch; binds task rows to the same backend
|
|
337
|
+
* as the call span. */
|
|
338
|
+
telemetryConfig?: import("./config.ts").TelemetryConfig;
|
|
268
339
|
/** Whether this task is part of an async ticket. */
|
|
269
340
|
async?: boolean;
|
|
341
|
+
/** Immutable dispatch-scoped delegate.json snapshot. Long-lived async workers
|
|
342
|
+
* use this instead of the live singleton so retry/stall/output/provider
|
|
343
|
+
* settings stay stable for the ticket's lifetime. */
|
|
344
|
+
config?: import("./config.ts").DelegateConfig;
|
|
270
345
|
}
|
|
271
346
|
|
|
272
347
|
/** Structural subset of Pi's `ExtensionContext` used by delegate's
|
|
@@ -288,6 +363,10 @@ export interface DelegateToolCtx {
|
|
|
288
363
|
| undefined;
|
|
289
364
|
/** Optional hook Pi exposes for extensions to read the live system prompt. */
|
|
290
365
|
getSystemPrompt?: () => string | undefined;
|
|
366
|
+
/** Optional TUI notice boundary used for migration warnings. */
|
|
367
|
+
ui?: {
|
|
368
|
+
notify(message: string, level?: "info" | "warning" | "error"): void;
|
|
369
|
+
};
|
|
291
370
|
}
|
|
292
371
|
|
|
293
372
|
/** Shape returned by the delegate tool's `execute`. Mirrors Pi's
|