@poncho-ai/cli 0.13.0 → 0.14.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.
- package/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +42 -0
- package/dist/{chunk-CUCEDHME.js → chunk-AIEVSNGF.js} +1994 -467
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-interactive-ink-VZBOYJYS.js → run-interactive-ink-7ULE5JJI.js} +151 -118
- package/package.json +4 -4
- package/src/api-docs.ts +674 -0
- package/src/index.ts +632 -229
- package/src/init-onboarding.ts +14 -1
- package/src/run-interactive-ink.ts +171 -147
- package/src/web-ui.ts +760 -244
package/src/init-onboarding.ts
CHANGED
|
@@ -349,7 +349,20 @@ export const buildConfigFromOnboardingAnswers = (
|
|
|
349
349
|
};
|
|
350
350
|
|
|
351
351
|
if (messagingPlatform !== "none") {
|
|
352
|
-
|
|
352
|
+
const channelConfig: NonNullable<PonchoConfig["messaging"]>[number] = {
|
|
353
|
+
platform: messagingPlatform as "slack" | "resend",
|
|
354
|
+
};
|
|
355
|
+
if (messagingPlatform === "resend") {
|
|
356
|
+
const mode = String(answers["messaging.resend.mode"] ?? "auto-reply");
|
|
357
|
+
if (mode === "tool") {
|
|
358
|
+
channelConfig.mode = "tool";
|
|
359
|
+
}
|
|
360
|
+
const recipientsRaw = String(answers["messaging.resend.allowedRecipients"] ?? "");
|
|
361
|
+
if (recipientsRaw.trim().length > 0) {
|
|
362
|
+
channelConfig.allowedRecipients = recipientsRaw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
config.messaging = [channelConfig];
|
|
353
366
|
}
|
|
354
367
|
|
|
355
368
|
return config;
|
|
@@ -22,14 +22,6 @@ import { consumeFirstRunIntro } from "./init-feature-context.js";
|
|
|
22
22
|
import { resolveHarnessEnvironment } from "./index.js";
|
|
23
23
|
import { getMascotLines } from "./mascot.js";
|
|
24
24
|
|
|
25
|
-
// Re-export types that index.ts references
|
|
26
|
-
export type ApprovalRequest = {
|
|
27
|
-
tool: string;
|
|
28
|
-
input: Record<string, unknown>;
|
|
29
|
-
approvalId: string;
|
|
30
|
-
resolve: (approved: boolean) => void;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
25
|
export type SessionSnapshot = {
|
|
34
26
|
messages: Message[];
|
|
35
27
|
nextTurn: number;
|
|
@@ -374,14 +366,12 @@ export const runInteractiveInk = async ({
|
|
|
374
366
|
workingDir,
|
|
375
367
|
config,
|
|
376
368
|
conversationStore,
|
|
377
|
-
onSetApprovalCallback,
|
|
378
369
|
}: {
|
|
379
370
|
harness: AgentHarness;
|
|
380
371
|
params: Record<string, string>;
|
|
381
372
|
workingDir: string;
|
|
382
373
|
config?: PonchoConfig;
|
|
383
374
|
conversationStore: ConversationStore;
|
|
384
|
-
onSetApprovalCallback?: (cb: (req: ApprovalRequest) => void) => void;
|
|
385
375
|
}): Promise<void> => {
|
|
386
376
|
const metadata = await loadMetadata(workingDir);
|
|
387
377
|
|
|
@@ -391,32 +381,6 @@ export const runInteractiveInk = async ({
|
|
|
391
381
|
terminal: true,
|
|
392
382
|
});
|
|
393
383
|
|
|
394
|
-
// --- Approval bridge -------------------------------------------------------
|
|
395
|
-
// When the harness needs tool approval, it calls the approval handler in
|
|
396
|
-
// index.ts, which creates a pending promise and fires our callback.
|
|
397
|
-
// We use readline to prompt the user for y/n.
|
|
398
|
-
if (onSetApprovalCallback) {
|
|
399
|
-
onSetApprovalCallback((req: ApprovalRequest) => {
|
|
400
|
-
// Print approval prompt — we're mid-turn so stdout might have partial text
|
|
401
|
-
process.stdout.write("\n");
|
|
402
|
-
const preview = compactPreview(req.input, 100);
|
|
403
|
-
rl.question(
|
|
404
|
-
`${C.yellow}${C.bold}Tool "${req.tool}" requires approval${C.reset}\n` +
|
|
405
|
-
`${C.gray}input: ${preview}${C.reset}\n` +
|
|
406
|
-
`${C.yellow}approve? (y/n): ${C.reset}`,
|
|
407
|
-
(answer) => {
|
|
408
|
-
const approved = answer.trim().toLowerCase() === "y";
|
|
409
|
-
console.log(
|
|
410
|
-
approved
|
|
411
|
-
? green(` approved ${req.tool}`)
|
|
412
|
-
: magenta(` denied ${req.tool}`),
|
|
413
|
-
);
|
|
414
|
-
req.resolve(approved);
|
|
415
|
-
},
|
|
416
|
-
);
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
384
|
// --- Print header ----------------------------------------------------------
|
|
421
385
|
|
|
422
386
|
console.log("");
|
|
@@ -555,130 +519,190 @@ export const runInteractiveInk = async ({
|
|
|
555
519
|
pendingFiles = [];
|
|
556
520
|
}
|
|
557
521
|
|
|
522
|
+
type CheckpointEvent = Extract<AgentEvent, { type: "tool:approval:checkpoint" }>;
|
|
523
|
+
let eventSource: AsyncGenerator<AgentEvent> = harness.run({
|
|
524
|
+
task: trimmed,
|
|
525
|
+
parameters: params,
|
|
526
|
+
messages,
|
|
527
|
+
files: turnFiles.length > 0 ? turnFiles : undefined,
|
|
528
|
+
abortSignal: activeRunAbortController.signal,
|
|
529
|
+
});
|
|
530
|
+
|
|
558
531
|
try {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (event.type === "run:started") {
|
|
567
|
-
latestRunId = event.runId;
|
|
568
|
-
}
|
|
569
|
-
if (event.type === "model:chunk") {
|
|
570
|
-
sawChunk = true;
|
|
571
|
-
// If we have tools accumulated and text starts again, push tools as a section
|
|
572
|
-
if (currentTools.length > 0) {
|
|
573
|
-
sections.push({ type: "tools", content: currentTools });
|
|
574
|
-
currentTools = [];
|
|
532
|
+
// eslint-disable-next-line no-constant-condition
|
|
533
|
+
while (true) {
|
|
534
|
+
let checkpointEvent: CheckpointEvent | null = null;
|
|
535
|
+
|
|
536
|
+
for await (const event of eventSource) {
|
|
537
|
+
if (event.type === "run:started") {
|
|
538
|
+
latestRunId = event.runId;
|
|
575
539
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
540
|
+
if (event.type === "model:chunk") {
|
|
541
|
+
sawChunk = true;
|
|
542
|
+
if (currentTools.length > 0) {
|
|
543
|
+
sections.push({ type: "tools", content: currentTools });
|
|
544
|
+
currentTools = [];
|
|
545
|
+
}
|
|
546
|
+
responseText += event.content;
|
|
547
|
+
streamedText += event.content;
|
|
548
|
+
currentText += event.content;
|
|
579
549
|
|
|
580
|
-
|
|
550
|
+
if (!thinkingCleared) {
|
|
551
|
+
clearThinking();
|
|
552
|
+
process.stdout.write(`${C.green}assistant> ${C.reset}`);
|
|
553
|
+
}
|
|
554
|
+
process.stdout.write(event.content);
|
|
555
|
+
} else if (
|
|
556
|
+
event.type === "tool:started" ||
|
|
557
|
+
event.type === "tool:completed" ||
|
|
558
|
+
event.type === "tool:error" ||
|
|
559
|
+
event.type === "tool:approval:required"
|
|
560
|
+
) {
|
|
561
|
+
if (streamedText.length > 0) {
|
|
562
|
+
committedText = true;
|
|
563
|
+
streamedText = "";
|
|
564
|
+
process.stdout.write("\n");
|
|
565
|
+
}
|
|
581
566
|
clearThinking();
|
|
582
|
-
process.stdout.write(`${C.green}assistant> ${C.reset}`);
|
|
583
|
-
}
|
|
584
|
-
// Stream the text directly to stdout
|
|
585
|
-
process.stdout.write(event.content);
|
|
586
|
-
} else if (
|
|
587
|
-
event.type === "tool:started" ||
|
|
588
|
-
event.type === "tool:completed" ||
|
|
589
|
-
event.type === "tool:error" ||
|
|
590
|
-
event.type === "tool:approval:required" ||
|
|
591
|
-
event.type === "tool:approval:granted" ||
|
|
592
|
-
event.type === "tool:approval:denied"
|
|
593
|
-
) {
|
|
594
|
-
// Flush any streaming text before tool output
|
|
595
|
-
if (streamedText.length > 0) {
|
|
596
|
-
committedText = true;
|
|
597
|
-
streamedText = "";
|
|
598
|
-
process.stdout.write("\n");
|
|
599
|
-
}
|
|
600
|
-
clearThinking();
|
|
601
567
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
568
|
+
if (event.type === "tool:started") {
|
|
569
|
+
if (currentText.length > 0) {
|
|
570
|
+
sections.push({ type: "text", content: currentText });
|
|
571
|
+
currentText = "";
|
|
572
|
+
}
|
|
573
|
+
const preview = showToolPayloads
|
|
574
|
+
? compactPreview(event.input, 400)
|
|
575
|
+
: compactPreview(event.input, 100);
|
|
576
|
+
console.log(yellow(`tools> start ${event.tool} input=${preview}`));
|
|
577
|
+
const toolText = `- start \`${event.tool}\``;
|
|
578
|
+
toolTimeline.push(toolText);
|
|
579
|
+
currentTools.push(toolText);
|
|
580
|
+
toolEvents += 1;
|
|
581
|
+
} else if (event.type === "tool:completed") {
|
|
582
|
+
const preview = showToolPayloads
|
|
583
|
+
? compactPreview(event.output, 400)
|
|
584
|
+
: compactPreview(event.output, 100);
|
|
585
|
+
console.log(
|
|
586
|
+
yellow(
|
|
587
|
+
`tools> done ${event.tool} in ${formatDuration(event.duration)}`,
|
|
588
|
+
),
|
|
589
|
+
);
|
|
590
|
+
if (showToolPayloads) {
|
|
591
|
+
console.log(yellow(`tools> output ${preview}`));
|
|
592
|
+
}
|
|
593
|
+
const toolText = `- done \`${event.tool}\` in ${formatDuration(event.duration)}`;
|
|
594
|
+
toolTimeline.push(toolText);
|
|
595
|
+
currentTools.push(toolText);
|
|
596
|
+
} else if (event.type === "tool:error") {
|
|
597
|
+
console.log(
|
|
598
|
+
red(`tools> error ${event.tool}: ${event.error}`),
|
|
599
|
+
);
|
|
600
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
601
|
+
toolTimeline.push(toolText);
|
|
602
|
+
currentTools.push(toolText);
|
|
603
|
+
} else if (event.type === "tool:approval:required") {
|
|
604
|
+
console.log(
|
|
605
|
+
magenta(`tools> approval required for ${event.tool}`),
|
|
606
|
+
);
|
|
607
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
608
|
+
toolTimeline.push(toolText);
|
|
609
|
+
currentTools.push(toolText);
|
|
607
610
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
console.log(yellow(`tools> output ${preview}`));
|
|
611
|
+
} else if (event.type === "tool:approval:checkpoint") {
|
|
612
|
+
checkpointEvent = event as CheckpointEvent;
|
|
613
|
+
} else if (event.type === "run:error") {
|
|
614
|
+
clearThinking();
|
|
615
|
+
runFailed = true;
|
|
616
|
+
console.log(red(`error> ${event.error.message}`));
|
|
617
|
+
} else if (event.type === "run:cancelled") {
|
|
618
|
+
clearThinking();
|
|
619
|
+
runCancelled = true;
|
|
620
|
+
} else if (event.type === "model:response") {
|
|
621
|
+
usage = event.usage;
|
|
622
|
+
} else if (event.type === "run:completed" && !sawChunk) {
|
|
623
|
+
clearThinking();
|
|
624
|
+
responseText = event.result.response ?? "";
|
|
625
|
+
if (responseText.length > 0) {
|
|
626
|
+
process.stdout.write(
|
|
627
|
+
`${C.green}assistant> ${C.reset}${responseText}\n`,
|
|
628
|
+
);
|
|
627
629
|
}
|
|
628
|
-
const toolText = `- done \`${event.tool}\` in ${formatDuration(event.duration)}`;
|
|
629
|
-
toolTimeline.push(toolText);
|
|
630
|
-
currentTools.push(toolText);
|
|
631
|
-
} else if (event.type === "tool:error") {
|
|
632
|
-
console.log(
|
|
633
|
-
red(`tools> error ${event.tool}: ${event.error}`),
|
|
634
|
-
);
|
|
635
|
-
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
636
|
-
toolTimeline.push(toolText);
|
|
637
|
-
currentTools.push(toolText);
|
|
638
|
-
} else if (event.type === "tool:approval:required") {
|
|
639
|
-
console.log(
|
|
640
|
-
magenta(`tools> approval required for ${event.tool}`),
|
|
641
|
-
);
|
|
642
|
-
const toolText = `- approval required \`${event.tool}\``;
|
|
643
|
-
toolTimeline.push(toolText);
|
|
644
|
-
currentTools.push(toolText);
|
|
645
|
-
} else if (event.type === "tool:approval:granted") {
|
|
646
|
-
console.log(
|
|
647
|
-
gray(`tools> approval granted (${event.approvalId})`),
|
|
648
|
-
);
|
|
649
|
-
const toolText = `- approval granted (${event.approvalId})`;
|
|
650
|
-
toolTimeline.push(toolText);
|
|
651
|
-
currentTools.push(toolText);
|
|
652
|
-
} else if (event.type === "tool:approval:denied") {
|
|
653
|
-
console.log(
|
|
654
|
-
magenta(`tools> approval denied (${event.approvalId})`),
|
|
655
|
-
);
|
|
656
|
-
const toolText = `- approval denied (${event.approvalId})`;
|
|
657
|
-
toolTimeline.push(toolText);
|
|
658
|
-
currentTools.push(toolText);
|
|
659
630
|
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (!checkpointEvent) break;
|
|
634
|
+
|
|
635
|
+
// Prompt user for approval
|
|
636
|
+
if (streamedText.length > 0) {
|
|
637
|
+
process.stdout.write("\n");
|
|
638
|
+
streamedText = "";
|
|
639
|
+
}
|
|
640
|
+
clearThinking();
|
|
641
|
+
const preview = compactPreview(checkpointEvent.input, 100);
|
|
642
|
+
const answer = await ask(rl,
|
|
643
|
+
`${C.yellow}${C.bold}Tool "${checkpointEvent.tool}" requires approval${C.reset}\n` +
|
|
644
|
+
`${C.gray}input: ${preview}${C.reset}\n` +
|
|
645
|
+
`${C.yellow}approve? (y/n): ${C.reset}`,
|
|
646
|
+
);
|
|
647
|
+
const approved = answer.trim().toLowerCase() === "y";
|
|
648
|
+
console.log(
|
|
649
|
+
approved
|
|
650
|
+
? green(` approved ${checkpointEvent.tool}`)
|
|
651
|
+
: magenta(` denied ${checkpointEvent.tool}`),
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
const approvalText = approved
|
|
655
|
+
? `- approval granted (${checkpointEvent.approvalId})`
|
|
656
|
+
: `- approval denied (${checkpointEvent.approvalId})`;
|
|
657
|
+
toolTimeline.push(approvalText);
|
|
658
|
+
currentTools.push(approvalText);
|
|
659
|
+
|
|
660
|
+
let toolResults: Array<{ callId: string; toolName: string; result?: unknown; error?: string }>;
|
|
661
|
+
if (approved) {
|
|
662
|
+
const execResults = await harness.executeTools(
|
|
663
|
+
[{ id: checkpointEvent.toolCallId, name: checkpointEvent.tool, input: checkpointEvent.input }],
|
|
664
|
+
{ runId: latestRunId, agentId: "interactive", step: 0, workingDir, parameters: params },
|
|
665
|
+
);
|
|
666
|
+
toolResults = execResults.map(r => ({
|
|
667
|
+
callId: r.callId,
|
|
668
|
+
toolName: r.tool,
|
|
669
|
+
result: r.output,
|
|
670
|
+
error: r.error,
|
|
671
|
+
}));
|
|
672
|
+
for (const r of execResults) {
|
|
673
|
+
if (r.error) {
|
|
674
|
+
console.log(red(`tools> error ${r.tool}: ${r.error}`));
|
|
675
|
+
const toolText = `- error \`${r.tool}\`: ${r.error}`;
|
|
676
|
+
toolTimeline.push(toolText);
|
|
677
|
+
currentTools.push(toolText);
|
|
678
|
+
} else {
|
|
679
|
+
console.log(yellow(`tools> done ${r.tool}`));
|
|
680
|
+
const toolText = `- done \`${r.tool}\``;
|
|
681
|
+
toolTimeline.push(toolText);
|
|
682
|
+
currentTools.push(toolText);
|
|
683
|
+
}
|
|
676
684
|
}
|
|
685
|
+
} else {
|
|
686
|
+
toolResults = [{
|
|
687
|
+
callId: checkpointEvent.toolCallId,
|
|
688
|
+
toolName: checkpointEvent.tool,
|
|
689
|
+
error: "Tool execution denied by user",
|
|
690
|
+
}];
|
|
677
691
|
}
|
|
692
|
+
|
|
693
|
+
const fullMessages = [...messages, ...checkpointEvent.checkpointMessages];
|
|
694
|
+
eventSource = harness.continueFromToolResult({
|
|
695
|
+
messages: fullMessages,
|
|
696
|
+
toolResults,
|
|
697
|
+
abortSignal: activeRunAbortController!.signal,
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
process.stdout.write(gray("thinking..."));
|
|
701
|
+
thinkingCleared = false;
|
|
678
702
|
}
|
|
679
703
|
} catch (error) {
|
|
680
704
|
clearThinking();
|
|
681
|
-
if (activeRunAbortController
|
|
705
|
+
if (activeRunAbortController?.signal.aborted) {
|
|
682
706
|
runCancelled = true;
|
|
683
707
|
} else {
|
|
684
708
|
runFailed = true;
|