makaron-cli 0.7.7 → 0.7.9
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 +33 -0
- package/SKILL.md +33 -0
- package/bin/makaron.mjs +213 -0
- package/package.json +1 -1
- package/skills/makaron/SKILL.md +33 -0
package/README.md
CHANGED
|
@@ -134,6 +134,39 @@ Outputs one JSON per line as artifacts appear:
|
|
|
134
134
|
{"event":"done","status":"completed"}
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
### Dialogue events for external Agents
|
|
138
|
+
|
|
139
|
+
Use this when another Agent needs to read what Makaron said, detect approval requirements, and relay artifacts without inventing customer-service wording.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npx makaron-cli responses events <runId> --jsonl
|
|
143
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Events use only three factual types:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{"type":"message","id":"msg_1","runId":"run_xxx","seq":1,"text":"Makaron original message","requires_approval":true,"approval_options":["approve","revise","ask_user","continue"]}
|
|
150
|
+
{"type":"approval","messageId":"msg_1","choice":"approve","status":"recorded"}
|
|
151
|
+
{"type":"artifact","kind":"image","status":"completed","url":"https://..."}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npx makaron-cli responses approve msg_1 --run <runId> --note "Proceed."
|
|
158
|
+
npx makaron-cli responses revise msg_1 --run <runId> "make it softer"
|
|
159
|
+
npx makaron-cli responses ask-user msg_1 --run <runId>
|
|
160
|
+
npx makaron-cli responses continue msg_1 --run <runId>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Important: approvals are a local Agent gate in v0. They are recorded by the CLI so wrappers and Skills can fail fast, but they do not pause or resume the remote Makaron runtime yet. Wrappers can enforce the local gate with:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npx makaron-cli responses events <runId> --jsonl
|
|
167
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl --fail-on-unapproved
|
|
168
|
+
```
|
|
169
|
+
|
|
137
170
|
### Extract specific results
|
|
138
171
|
|
|
139
172
|
```bash
|
package/SKILL.md
CHANGED
|
@@ -126,6 +126,39 @@ Outputs one JSON per line as artifacts appear:
|
|
|
126
126
|
{"event":"done","status":"completed"}
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
### Dialogue events for external Agents
|
|
130
|
+
|
|
131
|
+
Use this when another Agent needs to read what Makaron said, detect approval requirements, and relay artifacts without inventing customer-service wording.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx makaron-cli responses events <runId> --jsonl
|
|
135
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Events use only three factual types:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{"type":"message","id":"msg_1","runId":"run_xxx","seq":1,"text":"Makaron original message","requires_approval":true,"approval_options":["approve","revise","ask_user","continue"]}
|
|
142
|
+
{"type":"approval","messageId":"msg_1","choice":"approve","status":"recorded"}
|
|
143
|
+
{"type":"artifact","kind":"image","status":"completed","url":"https://..."}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npx makaron-cli responses approve msg_1 --run <runId> --note "Proceed."
|
|
150
|
+
npx makaron-cli responses revise msg_1 --run <runId> "make it softer"
|
|
151
|
+
npx makaron-cli responses ask-user msg_1 --run <runId>
|
|
152
|
+
npx makaron-cli responses continue msg_1 --run <runId>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Important: approvals are a local Agent gate in v0. They are recorded by the CLI so wrappers and Skills can fail fast, but they do not pause or resume the remote Makaron runtime yet. Wrappers can enforce the local gate with:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npx makaron-cli responses events <runId> --jsonl
|
|
159
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl --fail-on-unapproved
|
|
160
|
+
```
|
|
161
|
+
|
|
129
162
|
### Extract specific results
|
|
130
163
|
|
|
131
164
|
```bash
|
package/bin/makaron.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import { execFileSync } from 'child_process';
|
|
|
19
19
|
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
20
20
|
|
|
21
21
|
const AUTH_FILE = path.join(process.env.HOME || '~', '.makaron', 'auth.json');
|
|
22
|
+
const APPROVALS_FILE = path.join(path.dirname(AUTH_FILE), 'approvals.json');
|
|
22
23
|
const DEFAULT_URL = 'https://www.makaron.app';
|
|
23
24
|
const BASE_URL = process.env.MAKARON_URL || DEFAULT_URL;
|
|
24
25
|
const APP_URL = process.env.MAKARON_APP_URL || DEFAULT_URL;
|
|
@@ -62,6 +63,20 @@ function saveAuth(data) {
|
|
|
62
63
|
fs.writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2));
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
function loadApprovals() {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(fs.readFileSync(APPROVALS_FILE, 'utf-8'));
|
|
69
|
+
} catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function saveApprovals(approvals) {
|
|
75
|
+
const dir = path.dirname(APPROVALS_FILE);
|
|
76
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
77
|
+
fs.writeFileSync(APPROVALS_FILE, JSON.stringify(approvals, null, 2));
|
|
78
|
+
}
|
|
79
|
+
|
|
65
80
|
function buildCookie(tokenJson) {
|
|
66
81
|
const url = tokenJson._supabaseUrl || SUPABASE_URL;
|
|
67
82
|
const ref = url.match(/\/\/([^.]+)\./)?.[1] || '';
|
|
@@ -426,6 +441,171 @@ function applyPick(data, field) {
|
|
|
426
441
|
}
|
|
427
442
|
}
|
|
428
443
|
|
|
444
|
+
// ─── Dialogue Events (message / approval / artifact) ─────────────────────────
|
|
445
|
+
|
|
446
|
+
function stableEventId(prefix, runId, seq, fallback) {
|
|
447
|
+
return fallback || `${prefix}_${runId}_${seq ?? Date.now()}`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function inferRequiresApproval(text) {
|
|
451
|
+
if (!text) return false;
|
|
452
|
+
const normalized = text.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
453
|
+
const approvalPhrases = [
|
|
454
|
+
/\bshall i\b.*\b(go ahead|proceed|continue|generate|create|submit|start)\b/,
|
|
455
|
+
/\bshould i\b.*\b(go ahead|proceed|continue|generate|create|submit|start)\b/,
|
|
456
|
+
/\bdo you want me to\b.*\b(go ahead|proceed|continue|generate|create|submit|start)\b/,
|
|
457
|
+
/\bconfirm\b.*\b(before|to)\b.*\b(generate|create|submit|proceed|continue)\b/,
|
|
458
|
+
/\bapprove\b.*\b(before|to)\b.*\b(generate|create|submit|proceed|continue)\b/,
|
|
459
|
+
/\bplease\b.*\b(confirm|approve)\b.*\b(generate|create|submit|proceed|continue)\b/,
|
|
460
|
+
];
|
|
461
|
+
return approvalPhrases.some(pattern => pattern.test(normalized));
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function normalizeDialogueMessage(runId, projectId, ev) {
|
|
465
|
+
const data = ev.data || ev;
|
|
466
|
+
const text = data.text || data.message || data.content || data.statusText || '';
|
|
467
|
+
if (!text && ev.type !== 'tool_call') return null;
|
|
468
|
+
const requiresApproval = Boolean(data.requires_approval || data.requiresApproval || data.action_required || data.actionRequired || inferRequiresApproval(text));
|
|
469
|
+
const message = {
|
|
470
|
+
type: 'message',
|
|
471
|
+
id: stableEventId('msg', runId, ev.seq, data.id || ev.id),
|
|
472
|
+
runId,
|
|
473
|
+
projectId,
|
|
474
|
+
seq: ev.seq,
|
|
475
|
+
status: data.status || undefined,
|
|
476
|
+
text: text || `${data.tool || 'tool_call'}${data.input?.description ? `: ${data.input.description}` : ''}`,
|
|
477
|
+
};
|
|
478
|
+
if (ev.type) message.source_type = ev.type;
|
|
479
|
+
if (requiresApproval) {
|
|
480
|
+
message.requires_approval = true;
|
|
481
|
+
message.approval_options = data.approval_options || data.approvalOptions || ['approve', 'revise', 'ask_user', 'continue'];
|
|
482
|
+
}
|
|
483
|
+
if (data.proposal) message.proposal = data.proposal;
|
|
484
|
+
return message;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function normalizeDialogueArtifact(runId, projectId, item, seq) {
|
|
488
|
+
if (!item) return null;
|
|
489
|
+
const kind = item.type || item.kind || (item.imageUrl ? 'image' : item.videoUrl ? 'video' : undefined);
|
|
490
|
+
if (!kind) return null;
|
|
491
|
+
const artifact = {
|
|
492
|
+
type: 'artifact',
|
|
493
|
+
id: stableEventId('artifact', runId, seq, item.id || item.snapshotId || item.taskId),
|
|
494
|
+
runId,
|
|
495
|
+
projectId,
|
|
496
|
+
seq,
|
|
497
|
+
kind,
|
|
498
|
+
status: item.status || (item.url || item.imageUrl || item.videoUrl || item.audioUrl ? 'completed' : 'running'),
|
|
499
|
+
};
|
|
500
|
+
const url = item.url || item.imageUrl || item.videoUrl || item.audioUrl;
|
|
501
|
+
if (url) artifact.url = url;
|
|
502
|
+
if (item.error) artifact.error = item.error;
|
|
503
|
+
if (item.taskId) artifact.taskId = item.taskId;
|
|
504
|
+
if (item.snapshotId) artifact.snapshotId = item.snapshotId;
|
|
505
|
+
return artifact;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function normalizeDialogueEvent(runId, projectId, ev) {
|
|
509
|
+
const data = ev.data || {};
|
|
510
|
+
if (ev.type === 'message' || ev.type === 'approval' || ev.type === 'artifact') {
|
|
511
|
+
return { ...data, ...ev, runId: ev.runId || runId, projectId: ev.projectId || projectId };
|
|
512
|
+
}
|
|
513
|
+
switch (ev.type) {
|
|
514
|
+
case 'content':
|
|
515
|
+
case 'status':
|
|
516
|
+
case 'tool_call':
|
|
517
|
+
case 'error':
|
|
518
|
+
return normalizeDialogueMessage(runId, projectId, ev);
|
|
519
|
+
case 'image':
|
|
520
|
+
return normalizeDialogueArtifact(runId, projectId, { type: 'image', status: data.imageUrl ? 'completed' : 'running', imageUrl: data.imageUrl, snapshotId: data.snapshotId }, ev.seq);
|
|
521
|
+
case 'render':
|
|
522
|
+
return normalizeDialogueArtifact(runId, projectId, { type: data.animation ? 'video' : 'design', status: data.published ? 'completed' : 'running', url: data.url, snapshotId: data.snapshotId }, ev.seq);
|
|
523
|
+
case 'animation_task':
|
|
524
|
+
case 'video_snapshot':
|
|
525
|
+
return normalizeDialogueArtifact(runId, projectId, { type: 'video', status: 'running', taskId: data.taskId, snapshotId: data.snapshotId }, ev.seq);
|
|
526
|
+
case 'music_task':
|
|
527
|
+
return normalizeDialogueArtifact(runId, projectId, { type: 'music', status: 'running', taskId: data.taskId }, ev.seq);
|
|
528
|
+
default:
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function buildDialogueEvents(runId, data) {
|
|
534
|
+
const projectId = data.projectId || data.project_id;
|
|
535
|
+
const events = [];
|
|
536
|
+
for (const ev of data.events || []) {
|
|
537
|
+
const normalized = normalizeDialogueEvent(runId, projectId, ev);
|
|
538
|
+
if (normalized) events.push(normalized);
|
|
539
|
+
}
|
|
540
|
+
for (const item of data.output || []) {
|
|
541
|
+
const normalized = normalizeDialogueArtifact(runId, projectId, item, item.seq);
|
|
542
|
+
if (normalized && !events.some(ev => ev.type === 'artifact' && ev.id === normalized.id)) events.push(normalized);
|
|
543
|
+
}
|
|
544
|
+
for (const approval of loadApprovals().filter(item => item.runId === runId)) {
|
|
545
|
+
events.push(approval);
|
|
546
|
+
}
|
|
547
|
+
return events;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function findUnhandledApprovalMessages(events) {
|
|
551
|
+
const approved = new Set(events.filter(ev => ev.type === 'approval').map(ev => ev.messageId || ev.message_id));
|
|
552
|
+
return events.filter(ev => ev.type === 'message' && ev.requires_approval && !approved.has(ev.id));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async function fetchRun(baseUrl, headers, runId, opts = {}) {
|
|
556
|
+
const params = new URLSearchParams();
|
|
557
|
+
if (opts.events) params.set('events', 'true');
|
|
558
|
+
const suffix = params.toString() ? `?${params}` : '';
|
|
559
|
+
const res = await fetch(`${baseUrl}/api/agent/run/${runId}${suffix}`, { headers });
|
|
560
|
+
if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
|
|
561
|
+
return normalizeRunResponse(await res.json());
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async function printDialogueEvents(baseUrl, headers, runId, opts = {}) {
|
|
565
|
+
const { jsonl = false, failOnUnapproved = false, follow = false, interval = 5000 } = opts;
|
|
566
|
+
const printed = new Set();
|
|
567
|
+
|
|
568
|
+
while (true) {
|
|
569
|
+
const data = await fetchRun(baseUrl, headers, runId, { events: true });
|
|
570
|
+
const events = buildDialogueEvents(runId, data);
|
|
571
|
+
const unhandled = findUnhandledApprovalMessages(events);
|
|
572
|
+
if (failOnUnapproved && unhandled.length) {
|
|
573
|
+
process.stderr.write(`Unhandled Makaron message requires approval: ${unhandled.map(ev => ev.id).join(', ')}\n`);
|
|
574
|
+
process.exit(3);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const nextEvents = follow ? events.filter(ev => !printed.has(`${ev.type}:${ev.id || ev.seq}`)) : events;
|
|
578
|
+
for (const ev of nextEvents) {
|
|
579
|
+
printed.add(`${ev.type}:${ev.id || ev.seq}`);
|
|
580
|
+
if (jsonl) console.log(JSON.stringify(ev));
|
|
581
|
+
}
|
|
582
|
+
if (!jsonl) console.log(JSON.stringify(nextEvents, null, 2));
|
|
583
|
+
|
|
584
|
+
if (!follow || (!data.incomplete && ['completed', 'failed', 'aborted'].includes(data.status))) {
|
|
585
|
+
if (data.status === 'failed' || data.status === 'aborted') process.exit(1);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
await new Promise(r => setTimeout(r, data.next_poll_after_ms || interval));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function recordApproval(runId, messageId, choice, note) {
|
|
593
|
+
const approvals = loadApprovals();
|
|
594
|
+
const approval = {
|
|
595
|
+
type: 'approval',
|
|
596
|
+
id: `approval_${Date.now()}`,
|
|
597
|
+
runId,
|
|
598
|
+
messageId,
|
|
599
|
+
choice,
|
|
600
|
+
status: 'recorded',
|
|
601
|
+
createdAt: new Date().toISOString(),
|
|
602
|
+
};
|
|
603
|
+
if (note) approval.note = note;
|
|
604
|
+
approvals.push(approval);
|
|
605
|
+
saveApprovals(approvals);
|
|
606
|
+
console.log(JSON.stringify(approval));
|
|
607
|
+
}
|
|
608
|
+
|
|
429
609
|
// ─── Watch (incremental event stream) ───────────────────────────────────────
|
|
430
610
|
|
|
431
611
|
async function watchRun(baseUrl, headers, runId, opts = {}) {
|
|
@@ -1060,6 +1240,32 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1060
1240
|
}
|
|
1061
1241
|
await watchRun(baseUrl, headers, runId, { interval, jsonl });
|
|
1062
1242
|
|
|
1243
|
+
} else if (sub === 'events' || sub === 'timeline') {
|
|
1244
|
+
const runId = args[2];
|
|
1245
|
+
if (!runId) { console.error(`Usage: makaron responses ${sub} <runId> [--jsonl] [--follow] [--interval <ms>] [--fail-on-unapproved]`); process.exit(1); }
|
|
1246
|
+
let interval = 5000, jsonl = false, follow = false, failOnUnapproved = false;
|
|
1247
|
+
for (let i = 3; i < args.length; i++) {
|
|
1248
|
+
if (args[i] === '--jsonl') jsonl = true;
|
|
1249
|
+
else if (args[i] === '--follow') follow = true;
|
|
1250
|
+
else if (args[i] === '--fail-on-unapproved') failOnUnapproved = true;
|
|
1251
|
+
else if (args[i] === '--interval' && args[i + 1]) interval = parseInt(args[++i]);
|
|
1252
|
+
}
|
|
1253
|
+
await printDialogueEvents(baseUrl, headers, runId, { interval, jsonl, follow, failOnUnapproved });
|
|
1254
|
+
|
|
1255
|
+
} else if (['approve', 'revise', 'ask-user', 'continue'].includes(sub)) {
|
|
1256
|
+
const messageId = args[2];
|
|
1257
|
+
if (!messageId) { console.error(`Usage: makaron responses ${sub} <messageId> --run <runId> [--note <text>]`); process.exit(1); }
|
|
1258
|
+
let runId = null, note = null;
|
|
1259
|
+
const noteParts = [];
|
|
1260
|
+
for (let i = 3; i < args.length; i++) {
|
|
1261
|
+
if (args[i] === '--run' && args[i + 1]) runId = args[++i];
|
|
1262
|
+
else if (args[i] === '--note' && args[i + 1]) note = args[++i];
|
|
1263
|
+
else noteParts.push(args[i]);
|
|
1264
|
+
}
|
|
1265
|
+
if (!runId) { console.error(`Usage: makaron responses ${sub} <messageId> --run <runId> [--note <text>]`); process.exit(1); }
|
|
1266
|
+
if (!note && noteParts.length) note = noteParts.join(' ');
|
|
1267
|
+
recordApproval(runId, messageId, sub === 'ask-user' ? 'ask_user' : sub, note);
|
|
1268
|
+
|
|
1063
1269
|
} else if (sub === 'list') {
|
|
1064
1270
|
let projectId = null;
|
|
1065
1271
|
for (let i = 2; i < args.length; i++) {
|
|
@@ -1083,6 +1289,12 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1083
1289
|
responses get <runId> Get status and output (JSON)
|
|
1084
1290
|
responses get <runId> --wait Poll until completed
|
|
1085
1291
|
responses get <runId> --pick <field> Extract: first_image_url, first_video_url, project_url, output
|
|
1292
|
+
responses events <runId> --jsonl Emit message/approval/artifact events for external Agents
|
|
1293
|
+
responses timeline <runId> --jsonl Alias for responses events
|
|
1294
|
+
responses approve <messageId> --run <runId> Record approval for a Makaron message
|
|
1295
|
+
responses revise <messageId> --run <runId> Record revision request for a Makaron message
|
|
1296
|
+
responses ask-user <messageId> --run <runId> Record that the user must decide
|
|
1297
|
+
responses continue <messageId> --run <runId> Record continue decision
|
|
1086
1298
|
responses watch <runId> --jsonl Watch until done (incremental events)
|
|
1087
1299
|
responses list --project <id> List runs for a project
|
|
1088
1300
|
`);
|
|
@@ -1520,6 +1732,7 @@ Commands:
|
|
|
1520
1732
|
|
|
1521
1733
|
responses get <runId> Get run status and results
|
|
1522
1734
|
responses get <runId> --wait Poll until completed
|
|
1735
|
+
responses events <runId> --jsonl Emit message/approval/artifact events
|
|
1523
1736
|
responses list --project <id> List runs for a project
|
|
1524
1737
|
abort <runId> Abort a running Agent
|
|
1525
1738
|
|
package/package.json
CHANGED
package/skills/makaron/SKILL.md
CHANGED
|
@@ -126,6 +126,39 @@ Outputs one JSON per line as artifacts appear:
|
|
|
126
126
|
{"event":"done","status":"completed"}
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
### Dialogue events for external Agents
|
|
130
|
+
|
|
131
|
+
Use this when another Agent needs to read what Makaron said, detect approval requirements, and relay artifacts without inventing customer-service wording.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx makaron-cli responses events <runId> --jsonl
|
|
135
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Events use only three factual types:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{"type":"message","id":"msg_1","runId":"run_xxx","seq":1,"text":"Makaron original message","requires_approval":true,"approval_options":["approve","revise","ask_user","continue"]}
|
|
142
|
+
{"type":"approval","messageId":"msg_1","choice":"approve","status":"recorded"}
|
|
143
|
+
{"type":"artifact","kind":"image","status":"completed","url":"https://..."}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npx makaron-cli responses approve msg_1 --run <runId> --note "Proceed."
|
|
150
|
+
npx makaron-cli responses revise msg_1 --run <runId> "make it softer"
|
|
151
|
+
npx makaron-cli responses ask-user msg_1 --run <runId>
|
|
152
|
+
npx makaron-cli responses continue msg_1 --run <runId>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Important: approvals are a local Agent gate in v0. They are recorded by the CLI so wrappers and Skills can fail fast, but they do not pause or resume the remote Makaron runtime yet. Wrappers can enforce the local gate with:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npx makaron-cli responses events <runId> --jsonl
|
|
159
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl --fail-on-unapproved
|
|
160
|
+
```
|
|
161
|
+
|
|
129
162
|
### Extract specific results
|
|
130
163
|
|
|
131
164
|
```bash
|