@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/format.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { renderOutputForLLM } from "./spill.ts";
|
|
5
|
+
import { getOutputSpillThreshold, getOutputSpillTail } from "./config.ts";
|
|
5
6
|
import type {
|
|
6
7
|
ResolvedTask,
|
|
7
8
|
TaskProgress,
|
|
@@ -355,7 +356,13 @@ function isResumableSessionFile(sessionFile: string): boolean {
|
|
|
355
356
|
* notice instead of a retry hint — so the parent model is told to re-dispatch
|
|
356
357
|
* fresh rather than left to fabricate a path or chase a dead resume.
|
|
357
358
|
*/
|
|
358
|
-
export function formatFailedTask(
|
|
359
|
+
export function formatFailedTask(
|
|
360
|
+
r: TaskResult,
|
|
361
|
+
cwd?: string,
|
|
362
|
+
/** Dispatch-scoped snapshot for output-spill bounds; falls back to live
|
|
363
|
+
* config when unset (direct callers, legacy tests). */
|
|
364
|
+
config?: import("./config.ts").DelegateConfig,
|
|
365
|
+
): string[] {
|
|
359
366
|
const parts: string[] = [];
|
|
360
367
|
const isAbort = r.error === "Aborted";
|
|
361
368
|
// Empty string is falsy but not nullish — `||` covers both undefined and "".
|
|
@@ -366,8 +373,16 @@ export function formatFailedTask(r: TaskResult, cwd?: string): string[] {
|
|
|
366
373
|
parts.push(`[${isAbort ? "ABORTED" : "FAILED"}: ${failParts.join(" · ")}]`);
|
|
367
374
|
|
|
368
375
|
// Surface partial assistant output even when the task did not complete.
|
|
376
|
+
// Pass the dispatch-scoped spill bounds so a mid-task config change cannot
|
|
377
|
+
// widen (or narrow) the partial output the parent sees — the bounds captured
|
|
378
|
+
// when the task started are the ones the caller committed to.
|
|
369
379
|
if (r.output && r.output !== "(no output)") {
|
|
370
|
-
parts.push(
|
|
380
|
+
parts.push(
|
|
381
|
+
renderOutputForLLM(r.output, r.agent, {
|
|
382
|
+
thresholdChars: getOutputSpillThreshold(config),
|
|
383
|
+
tailChars: getOutputSpillTail(config),
|
|
384
|
+
}),
|
|
385
|
+
);
|
|
371
386
|
}
|
|
372
387
|
|
|
373
388
|
if (r.sessionFile && isResumableSessionFile(r.sessionFile)) {
|
|
@@ -414,6 +429,8 @@ export function formatFailedTask(r: TaskResult, cwd?: string): string[] {
|
|
|
414
429
|
export function formatCompletedTask(
|
|
415
430
|
task: ResolvedTask,
|
|
416
431
|
result: TaskResult,
|
|
432
|
+
/** Dispatch-scoped snapshot for output-spill bounds. */
|
|
433
|
+
config?: import("./config.ts").DelegateConfig,
|
|
417
434
|
): string[] {
|
|
418
435
|
const parts: string[] = [];
|
|
419
436
|
// `|| task.sessionAction` covers action-only tasks (close/list/...) where prompt is
|
|
@@ -425,7 +442,7 @@ export function formatCompletedTask(
|
|
|
425
442
|
for (const w of task.warnings) parts.push(`[WARNING: ${w}]`);
|
|
426
443
|
}
|
|
427
444
|
if (result.error) {
|
|
428
|
-
parts.push(...formatFailedTask(result, task.cwd));
|
|
445
|
+
parts.push(...formatFailedTask(result, task.cwd, config));
|
|
429
446
|
} else {
|
|
430
447
|
const meta = [
|
|
431
448
|
`OK | ${fmtDuration(result.durationMs)} | ${fmtTokens(result.tokens)} tokens`,
|
|
@@ -434,8 +451,37 @@ export function formatCompletedTask(
|
|
|
434
451
|
const touched = relativeTouchedSummary(result.touchedFiles, task.cwd);
|
|
435
452
|
if (touched) meta.push(`touched (best-effort): ${touched}`);
|
|
436
453
|
parts.push(
|
|
437
|
-
`[${meta.join(" · ")}]\n\n${renderOutputForLLM(
|
|
454
|
+
`[${meta.join(" · ")}]\n\n${renderOutputForLLM(
|
|
455
|
+
result.output,
|
|
456
|
+
result.agent,
|
|
457
|
+
{
|
|
458
|
+
thresholdChars: getOutputSpillThreshold(config),
|
|
459
|
+
tailChars: getOutputSpillTail(config),
|
|
460
|
+
},
|
|
461
|
+
)}`,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
if (result.integration) {
|
|
465
|
+
const integration = result.integration;
|
|
466
|
+
parts.push(
|
|
467
|
+
`[INTEGRATION: ${integration.status} · proposed ${integration.proposedFiles.length} file(s) · applied ${integration.appliedFiles.length} file(s)]`,
|
|
438
468
|
);
|
|
469
|
+
if (integration.baselineRef)
|
|
470
|
+
parts.push(`baseline ref: ${integration.baselineRef}`);
|
|
471
|
+
if (integration.proposalRef)
|
|
472
|
+
parts.push(`proposal ref: ${integration.proposalRef}`);
|
|
473
|
+
if (integration.patchPath)
|
|
474
|
+
parts.push(`full patch: ${integration.patchPath}`);
|
|
475
|
+
if (integration.worktreePath)
|
|
476
|
+
parts.push(`conflict worktree: ${integration.worktreePath}`);
|
|
477
|
+
for (const conflict of integration.conflicts ?? []) {
|
|
478
|
+
parts.push(`conflict: ${conflict.path}: ${conflict.reason}`);
|
|
479
|
+
}
|
|
480
|
+
if (integration.status === "applied_unverified") {
|
|
481
|
+
parts.push(
|
|
482
|
+
'Changes were applied but not verified. Suggested next call: delegate({ tasks: [{ agent: "reviewer", workspace: "scratch", prompt: "Review the applied isolated changes and run the relevant tests." }] })',
|
|
483
|
+
);
|
|
484
|
+
}
|
|
439
485
|
}
|
|
440
486
|
return parts;
|
|
441
487
|
}
|
package/host.ts
CHANGED
|
@@ -89,6 +89,8 @@ export interface HostDepsOptions {
|
|
|
89
89
|
* tasks (no named agent) pass undefined to use the discovered prompt.
|
|
90
90
|
*/
|
|
91
91
|
systemPrompt?: string;
|
|
92
|
+
/** Dispatch-scoped delegate.json snapshot for the provider-extension allowlist. */
|
|
93
|
+
delegateConfig?: import("./config.ts").DelegateConfig;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
const hostDepsCache = new GenerationCache<HostDeps>();
|
|
@@ -163,6 +165,7 @@ export async function getHostDeps(options: HostDepsOptions): Promise<HostDeps> {
|
|
|
163
165
|
options.modelProvider,
|
|
164
166
|
options.cwd,
|
|
165
167
|
agentDir,
|
|
168
|
+
options.delegateConfig,
|
|
166
169
|
);
|
|
167
170
|
|
|
168
171
|
// Provider configs may contain functions (custom stream/OAuth handlers), so a
|
|
@@ -199,7 +202,9 @@ export async function getHostDeps(options: HostDepsOptions): Promise<HostDeps> {
|
|
|
199
202
|
* allowlist, since editing it changes what a subsequent build would inject.
|
|
200
203
|
*/
|
|
201
204
|
function hostDepsCacheKey(options: HostDepsOptions, agentDir: string): string {
|
|
202
|
-
const providerExtensions = getSubagentProviderExtensionMap(
|
|
205
|
+
const providerExtensions = getSubagentProviderExtensionMap(
|
|
206
|
+
options.delegateConfig,
|
|
207
|
+
);
|
|
203
208
|
return JSON.stringify({
|
|
204
209
|
agentDir,
|
|
205
210
|
cwd: options.cwd,
|