makaron-cli 0.7.9 → 0.7.10
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 +35 -4
- package/SKILL.md +35 -4
- package/bin/makaron.mjs +105 -20
- package/package.json +1 -1
- package/skills/makaron/SKILL.md +35 -4
package/README.md
CHANGED
|
@@ -136,11 +136,13 @@ Outputs one JSON per line as artifacts appear:
|
|
|
136
136
|
|
|
137
137
|
### Dialogue events for external Agents
|
|
138
138
|
|
|
139
|
-
Use this when another Agent needs to read what Makaron said,
|
|
139
|
+
Use this when another Agent needs to read what Makaron said, handle text checkpoints, and relay artifacts without inventing customer-service wording.
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
142
|
npx makaron-cli responses events <runId> --jsonl
|
|
143
143
|
# alias: npx makaron-cli responses timeline <runId> --jsonl
|
|
144
|
+
# compact view: npx makaron-cli responses timeline <runId> --jsonl --compact
|
|
145
|
+
# pure Q&A view: npx makaron-cli responses timeline <runId> --jsonl --checkpoint-mode off
|
|
144
146
|
```
|
|
145
147
|
|
|
146
148
|
Events use only three factual types:
|
|
@@ -151,7 +153,7 @@ Events use only three factual types:
|
|
|
151
153
|
{"type":"artifact","kind":"image","status":"completed","url":"https://..."}
|
|
152
154
|
```
|
|
153
155
|
|
|
154
|
-
If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
156
|
+
Makaron uses a conservative checkpoint rule for creative/service Agents: if a run stops with substantive text, has no image/video/music/design artifact, and has no continuing execution action such as a tool call, the message is marked `requires_approval: true`. Pure status text such as queued, rendering, uploading, or completed is not treated as a checkpoint. Pure Q&A flows should pass `--checkpoint-mode off`. If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
155
157
|
|
|
156
158
|
```bash
|
|
157
159
|
npx makaron-cli responses approve msg_1 --run <runId> --note "Proceed."
|
|
@@ -163,10 +165,12 @@ npx makaron-cli responses continue msg_1 --run <runId>
|
|
|
163
165
|
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
166
|
|
|
165
167
|
```bash
|
|
166
|
-
npx makaron-cli responses events <runId> --jsonl
|
|
167
|
-
# alias: npx makaron-cli responses timeline <runId> --jsonl --fail-on-unapproved
|
|
168
|
+
npx makaron-cli responses events <runId> --jsonl --checkpoint-mode service --fail-on-unapproved
|
|
169
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl --compact --fail-on-unapproved
|
|
168
170
|
```
|
|
169
171
|
|
|
172
|
+
Use `--compact` when relaying to another Agent or chat system; it merges consecutive Makaron content chunks into a single readable message.
|
|
173
|
+
|
|
170
174
|
### Extract specific results
|
|
171
175
|
|
|
172
176
|
```bash
|
|
@@ -296,6 +300,33 @@ type MakaronOutput =
|
|
|
296
300
|
| Motion design | "create an Instagram story with animated text" |
|
|
297
301
|
| Multi-step | "edit the photo then make a video from it" |
|
|
298
302
|
|
|
303
|
+
## Minimal Agent Wrapper
|
|
304
|
+
|
|
305
|
+
A new Agent can use this minimal flow:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
RUN_JSON=$(npx makaron-cli chat --project auto --json -b "$USER_PROMPT")
|
|
309
|
+
RUN_ID=$(echo "$RUN_JSON" | jq -r .runId)
|
|
310
|
+
PROJECT_URL=$(echo "$RUN_JSON" | jq -r .projectUrl)
|
|
311
|
+
send_message "Project created: $PROJECT_URL"
|
|
312
|
+
|
|
313
|
+
if ! npx makaron-cli responses timeline "$RUN_ID" --jsonl --compact --checkpoint-mode service --fail-on-unapproved > /tmp/makaron-events.jsonl; then
|
|
314
|
+
npx makaron-cli responses timeline "$RUN_ID" --jsonl --compact | while read -r event; do
|
|
315
|
+
TYPE=$(echo "$event" | jq -r .type)
|
|
316
|
+
REQUIRES=$(echo "$event" | jq -r ".requires_approval // false")
|
|
317
|
+
TEXT=$(echo "$event" | jq -r ".text // empty")
|
|
318
|
+
MSG_ID=$(echo "$event" | jq -r ".id // empty")
|
|
319
|
+
if [ "$TYPE" = "message" ] && [ "$REQUIRES" = "true" ]; then
|
|
320
|
+
send_message "$TEXT"
|
|
321
|
+
npx makaron-cli responses ask-user "$MSG_ID" --run "$RUN_ID"
|
|
322
|
+
exit 3
|
|
323
|
+
fi
|
|
324
|
+
done
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
RESULT=$(npx makaron-cli responses get "$RUN_ID" --wait --json)
|
|
328
|
+
```
|
|
329
|
+
|
|
299
330
|
## Recommended Pattern: Service Flow (Feishu/OpenClaw/Group Chat)
|
|
300
331
|
|
|
301
332
|
When serving end-users in a chat environment (Feishu, Slack, Discord), use this proactive message pattern:
|
package/SKILL.md
CHANGED
|
@@ -128,11 +128,13 @@ Outputs one JSON per line as artifacts appear:
|
|
|
128
128
|
|
|
129
129
|
### Dialogue events for external Agents
|
|
130
130
|
|
|
131
|
-
Use this when another Agent needs to read what Makaron said,
|
|
131
|
+
Use this when another Agent needs to read what Makaron said, handle text checkpoints, and relay artifacts without inventing customer-service wording.
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
134
|
npx makaron-cli responses events <runId> --jsonl
|
|
135
135
|
# alias: npx makaron-cli responses timeline <runId> --jsonl
|
|
136
|
+
# compact view: npx makaron-cli responses timeline <runId> --jsonl --compact
|
|
137
|
+
# pure Q&A view: npx makaron-cli responses timeline <runId> --jsonl --checkpoint-mode off
|
|
136
138
|
```
|
|
137
139
|
|
|
138
140
|
Events use only three factual types:
|
|
@@ -143,7 +145,7 @@ Events use only three factual types:
|
|
|
143
145
|
{"type":"artifact","kind":"image","status":"completed","url":"https://..."}
|
|
144
146
|
```
|
|
145
147
|
|
|
146
|
-
If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
148
|
+
Makaron uses a conservative checkpoint rule for creative/service Agents: if a run stops with substantive text, has no image/video/music/design artifact, and has no continuing execution action such as a tool call, the message is marked `requires_approval: true`. Pure status text such as queued, rendering, uploading, or completed is not treated as a checkpoint. Pure Q&A flows should pass `--checkpoint-mode off`. If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
147
149
|
|
|
148
150
|
```bash
|
|
149
151
|
npx makaron-cli responses approve msg_1 --run <runId> --note "Proceed."
|
|
@@ -155,10 +157,12 @@ npx makaron-cli responses continue msg_1 --run <runId>
|
|
|
155
157
|
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
158
|
|
|
157
159
|
```bash
|
|
158
|
-
npx makaron-cli responses events <runId> --jsonl
|
|
159
|
-
# alias: npx makaron-cli responses timeline <runId> --jsonl --fail-on-unapproved
|
|
160
|
+
npx makaron-cli responses events <runId> --jsonl --checkpoint-mode service --fail-on-unapproved
|
|
161
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl --compact --fail-on-unapproved
|
|
160
162
|
```
|
|
161
163
|
|
|
164
|
+
Use `--compact` when relaying to another Agent or chat system; it merges consecutive Makaron content chunks into a single readable message.
|
|
165
|
+
|
|
162
166
|
### Extract specific results
|
|
163
167
|
|
|
164
168
|
```bash
|
|
@@ -285,6 +289,33 @@ type MakaronOutput =
|
|
|
285
289
|
| Motion design | "create an Instagram story with animated text" |
|
|
286
290
|
| Multi-step | "edit the photo then make a video from it" |
|
|
287
291
|
|
|
292
|
+
## Minimal Agent Wrapper
|
|
293
|
+
|
|
294
|
+
A new Agent can use this minimal flow:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
RUN_JSON=$(npx makaron-cli chat --project auto --json -b "$USER_PROMPT")
|
|
298
|
+
RUN_ID=$(echo "$RUN_JSON" | jq -r .runId)
|
|
299
|
+
PROJECT_URL=$(echo "$RUN_JSON" | jq -r .projectUrl)
|
|
300
|
+
send_message "Project created: $PROJECT_URL"
|
|
301
|
+
|
|
302
|
+
if ! npx makaron-cli responses timeline "$RUN_ID" --jsonl --compact --checkpoint-mode service --fail-on-unapproved > /tmp/makaron-events.jsonl; then
|
|
303
|
+
npx makaron-cli responses timeline "$RUN_ID" --jsonl --compact | while read -r event; do
|
|
304
|
+
TYPE=$(echo "$event" | jq -r .type)
|
|
305
|
+
REQUIRES=$(echo "$event" | jq -r ".requires_approval // false")
|
|
306
|
+
TEXT=$(echo "$event" | jq -r ".text // empty")
|
|
307
|
+
MSG_ID=$(echo "$event" | jq -r ".id // empty")
|
|
308
|
+
if [ "$TYPE" = "message" ] && [ "$REQUIRES" = "true" ]; then
|
|
309
|
+
send_message "$TEXT"
|
|
310
|
+
npx makaron-cli responses ask-user "$MSG_ID" --run "$RUN_ID"
|
|
311
|
+
exit 3
|
|
312
|
+
fi
|
|
313
|
+
done
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
RESULT=$(npx makaron-cli responses get "$RUN_ID" --wait --json)
|
|
317
|
+
```
|
|
318
|
+
|
|
288
319
|
## Recommended Pattern: Service Flow (Feishu/OpenClaw/Group Chat)
|
|
289
320
|
|
|
290
321
|
When serving end-users in a chat environment (Feishu, Slack, Discord), use this proactive message pattern:
|
package/bin/makaron.mjs
CHANGED
|
@@ -447,25 +447,61 @@ function stableEventId(prefix, runId, seq, fallback) {
|
|
|
447
447
|
return fallback || `${prefix}_${runId}_${seq ?? Date.now()}`;
|
|
448
448
|
}
|
|
449
449
|
|
|
450
|
-
function
|
|
450
|
+
function inferApprovalIntent(text) {
|
|
451
451
|
if (!text) return false;
|
|
452
452
|
const normalized = text.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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));
|
|
453
|
+
const hasEnglishApprovalIntent = /\b(shall i|should i|do you want me to|confirm|approve|approval|permission)\b/.test(normalized);
|
|
454
|
+
const hasEnglishAction = /\b(go ahead|proceed|continue|generate|create|submit|start|run|render)\b/.test(normalized);
|
|
455
|
+
const hasChineseApprovalIntent = /(是否|要不要|是否要|需要我|请确认|确认|同意|批准|可以吗|是否可以)/.test(normalized);
|
|
456
|
+
const hasChineseAction = /(继续|开始|生成|创建|提交|执行|渲染|出图|出视频|制作)/.test(normalized);
|
|
457
|
+
return (hasEnglishApprovalIntent && hasEnglishAction) || (hasChineseApprovalIntent && hasChineseAction);
|
|
462
458
|
}
|
|
463
459
|
|
|
464
|
-
function
|
|
460
|
+
function isPureStatusMessage(text, sourceType, status) {
|
|
461
|
+
const normalized = String(text || '').toLowerCase().replace(/\s+/g, ' ').trim();
|
|
462
|
+
if (sourceType === 'status') return true;
|
|
463
|
+
if (status && !normalized) return true;
|
|
464
|
+
if (!normalized) return false;
|
|
465
|
+
return /^(queued|running|rendering|uploading|processing|generating|completed|complete|done|failed|aborted|started|submitted|waiting|polling)(\.|…|\.\.\.)?$/.test(normalized)
|
|
466
|
+
|| /^(排队中|队列中|运行中|渲染中|上传中|处理中|生成中|已完成|完成|失败|已失败|已提交|等待中)$/.test(normalized);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function hasExplicitApprovalRequirement(data, text) {
|
|
470
|
+
return Boolean(
|
|
471
|
+
data.requires_approval
|
|
472
|
+
|| data.requiresApproval
|
|
473
|
+
|| data.action_required
|
|
474
|
+
|| data.actionRequired
|
|
475
|
+
|| data.proposal
|
|
476
|
+
|| inferApprovalIntent(text)
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function isPureQaRun(data) {
|
|
481
|
+
const raw = [
|
|
482
|
+
data.intent,
|
|
483
|
+
data.mode,
|
|
484
|
+
data.workflow,
|
|
485
|
+
data.workflow_type,
|
|
486
|
+
data.workflowType,
|
|
487
|
+
data.run_type,
|
|
488
|
+
data.runType,
|
|
489
|
+
data.kind,
|
|
490
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
491
|
+
return /\b(qa|q&a|question_answer|question-answer|answer|pure_qa|pure-qa)\b/.test(raw);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function normalizeDialogueMessage(runId, projectId, ev, context = {}) {
|
|
465
495
|
const data = ev.data || ev;
|
|
466
496
|
const text = data.text || data.message || data.content || data.statusText || '';
|
|
467
497
|
if (!text && ev.type !== 'tool_call') return null;
|
|
468
|
-
const
|
|
498
|
+
const explicitApproval = hasExplicitApprovalRequirement(data, text);
|
|
499
|
+
const textOnlyCheckpoint = Boolean(
|
|
500
|
+
context.stoppedWithoutArtifacts
|
|
501
|
+
&& !explicitApproval
|
|
502
|
+
&& !isPureStatusMessage(text, ev.type, data.status)
|
|
503
|
+
);
|
|
504
|
+
const requiresApproval = explicitApproval || textOnlyCheckpoint;
|
|
469
505
|
const message = {
|
|
470
506
|
type: 'message',
|
|
471
507
|
id: stableEventId('msg', runId, ev.seq, data.id || ev.id),
|
|
@@ -479,6 +515,7 @@ function normalizeDialogueMessage(runId, projectId, ev) {
|
|
|
479
515
|
if (requiresApproval) {
|
|
480
516
|
message.requires_approval = true;
|
|
481
517
|
message.approval_options = data.approval_options || data.approvalOptions || ['approve', 'revise', 'ask_user', 'continue'];
|
|
518
|
+
if (textOnlyCheckpoint) message.approval_reason = 'text_only_checkpoint';
|
|
482
519
|
}
|
|
483
520
|
if (data.proposal) message.proposal = data.proposal;
|
|
484
521
|
return message;
|
|
@@ -488,6 +525,7 @@ function normalizeDialogueArtifact(runId, projectId, item, seq) {
|
|
|
488
525
|
if (!item) return null;
|
|
489
526
|
const kind = item.type || item.kind || (item.imageUrl ? 'image' : item.videoUrl ? 'video' : undefined);
|
|
490
527
|
if (!kind) return null;
|
|
528
|
+
if (!['image', 'video', 'design', 'music', 'audio', 'file'].includes(kind)) return null;
|
|
491
529
|
const artifact = {
|
|
492
530
|
type: 'artifact',
|
|
493
531
|
id: stableEventId('artifact', runId, seq, item.id || item.snapshotId || item.taskId),
|
|
@@ -505,7 +543,7 @@ function normalizeDialogueArtifact(runId, projectId, item, seq) {
|
|
|
505
543
|
return artifact;
|
|
506
544
|
}
|
|
507
545
|
|
|
508
|
-
function normalizeDialogueEvent(runId, projectId, ev) {
|
|
546
|
+
function normalizeDialogueEvent(runId, projectId, ev, context = {}) {
|
|
509
547
|
const data = ev.data || {};
|
|
510
548
|
if (ev.type === 'message' || ev.type === 'approval' || ev.type === 'artifact') {
|
|
511
549
|
return { ...data, ...ev, runId: ev.runId || runId, projectId: ev.projectId || projectId };
|
|
@@ -515,7 +553,7 @@ function normalizeDialogueEvent(runId, projectId, ev) {
|
|
|
515
553
|
case 'status':
|
|
516
554
|
case 'tool_call':
|
|
517
555
|
case 'error':
|
|
518
|
-
return normalizeDialogueMessage(runId, projectId, ev);
|
|
556
|
+
return normalizeDialogueMessage(runId, projectId, ev, context);
|
|
519
557
|
case 'image':
|
|
520
558
|
return normalizeDialogueArtifact(runId, projectId, { type: 'image', status: data.imageUrl ? 'completed' : 'running', imageUrl: data.imageUrl, snapshotId: data.snapshotId }, ev.seq);
|
|
521
559
|
case 'render':
|
|
@@ -533,8 +571,22 @@ function normalizeDialogueEvent(runId, projectId, ev) {
|
|
|
533
571
|
function buildDialogueEvents(runId, data) {
|
|
534
572
|
const projectId = data.projectId || data.project_id;
|
|
535
573
|
const events = [];
|
|
574
|
+
const hasArtifacts = Boolean(
|
|
575
|
+
(data.output || []).some(item => normalizeDialogueArtifact(runId, projectId, item, item.seq))
|
|
576
|
+
|| (data.events || []).some(ev => normalizeDialogueEvent(runId, projectId, ev, { stoppedWithoutArtifacts: false })?.type === 'artifact')
|
|
577
|
+
);
|
|
578
|
+
const hasContinuationAction = (data.events || []).some(ev => ['tool_call', 'image', 'render', 'animation_task', 'video_snapshot', 'music_task'].includes(ev.type));
|
|
579
|
+
const checkpointMode = data.checkpointMode || 'service';
|
|
580
|
+
const context = {
|
|
581
|
+
stoppedWithoutArtifacts: checkpointMode !== 'off'
|
|
582
|
+
&& !isPureQaRun(data)
|
|
583
|
+
&& !hasArtifacts
|
|
584
|
+
&& !hasContinuationAction
|
|
585
|
+
&& !data.incomplete
|
|
586
|
+
&& ['completed', 'failed', 'aborted', 'waiting', 'needs_input'].includes(data.status),
|
|
587
|
+
};
|
|
536
588
|
for (const ev of data.events || []) {
|
|
537
|
-
const normalized = normalizeDialogueEvent(runId, projectId, ev);
|
|
589
|
+
const normalized = normalizeDialogueEvent(runId, projectId, ev, context);
|
|
538
590
|
if (normalized) events.push(normalized);
|
|
539
591
|
}
|
|
540
592
|
for (const item of data.output || []) {
|
|
@@ -547,6 +599,30 @@ function buildDialogueEvents(runId, data) {
|
|
|
547
599
|
return events;
|
|
548
600
|
}
|
|
549
601
|
|
|
602
|
+
function compactDialogueEvents(events) {
|
|
603
|
+
const compacted = [];
|
|
604
|
+
for (const ev of events) {
|
|
605
|
+
const prev = compacted[compacted.length - 1];
|
|
606
|
+
const canMerge = prev
|
|
607
|
+
&& ev.type === 'message'
|
|
608
|
+
&& prev.type === 'message'
|
|
609
|
+
&& ev.source_type === 'content'
|
|
610
|
+
&& prev.source_type === 'content'
|
|
611
|
+
&& !ev.requires_approval
|
|
612
|
+
&& !prev.requires_approval
|
|
613
|
+
&& !ev.proposal
|
|
614
|
+
&& !prev.proposal;
|
|
615
|
+
if (canMerge) {
|
|
616
|
+
prev.text = `${prev.text}${ev.text}`;
|
|
617
|
+
prev.id = `${prev.id}+${ev.id}`;
|
|
618
|
+
prev.seq_end = ev.seq;
|
|
619
|
+
} else {
|
|
620
|
+
compacted.push({ ...ev });
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return compacted;
|
|
624
|
+
}
|
|
625
|
+
|
|
550
626
|
function findUnhandledApprovalMessages(events) {
|
|
551
627
|
const approved = new Set(events.filter(ev => ev.type === 'approval').map(ev => ev.messageId || ev.message_id));
|
|
552
628
|
return events.filter(ev => ev.type === 'message' && ev.requires_approval && !approved.has(ev.id));
|
|
@@ -562,12 +638,13 @@ async function fetchRun(baseUrl, headers, runId, opts = {}) {
|
|
|
562
638
|
}
|
|
563
639
|
|
|
564
640
|
async function printDialogueEvents(baseUrl, headers, runId, opts = {}) {
|
|
565
|
-
const { jsonl = false, failOnUnapproved = false, follow = false, interval = 5000 } = opts;
|
|
641
|
+
const { jsonl = false, failOnUnapproved = false, follow = false, interval = 5000, compact = false, checkpointMode = 'service' } = opts;
|
|
566
642
|
const printed = new Set();
|
|
567
643
|
|
|
568
644
|
while (true) {
|
|
569
645
|
const data = await fetchRun(baseUrl, headers, runId, { events: true });
|
|
570
|
-
const
|
|
646
|
+
const dialogueEvents = buildDialogueEvents(runId, { ...data, checkpointMode });
|
|
647
|
+
const events = compact ? compactDialogueEvents(dialogueEvents) : dialogueEvents;
|
|
571
648
|
const unhandled = findUnhandledApprovalMessages(events);
|
|
572
649
|
if (failOnUnapproved && unhandled.length) {
|
|
573
650
|
process.stderr.write(`Unhandled Makaron message requires approval: ${unhandled.map(ev => ev.id).join(', ')}\n`);
|
|
@@ -1242,15 +1319,21 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1242
1319
|
|
|
1243
1320
|
} else if (sub === 'events' || sub === 'timeline') {
|
|
1244
1321
|
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;
|
|
1322
|
+
if (!runId) { console.error(`Usage: makaron responses ${sub} <runId> [--jsonl] [--compact] [--checkpoint-mode service|off] [--follow] [--interval <ms>] [--fail-on-unapproved]`); process.exit(1); }
|
|
1323
|
+
let interval = 5000, jsonl = false, follow = false, failOnUnapproved = false, compact = false, checkpointMode = 'service';
|
|
1247
1324
|
for (let i = 3; i < args.length; i++) {
|
|
1248
1325
|
if (args[i] === '--jsonl') jsonl = true;
|
|
1326
|
+
else if (args[i] === '--compact') compact = true;
|
|
1327
|
+
else if (args[i] === '--checkpoint-mode' && args[i + 1]) checkpointMode = args[++i];
|
|
1249
1328
|
else if (args[i] === '--follow') follow = true;
|
|
1250
1329
|
else if (args[i] === '--fail-on-unapproved') failOnUnapproved = true;
|
|
1251
1330
|
else if (args[i] === '--interval' && args[i + 1]) interval = parseInt(args[++i]);
|
|
1252
1331
|
}
|
|
1253
|
-
|
|
1332
|
+
if (!['service', 'off'].includes(checkpointMode)) {
|
|
1333
|
+
console.error('--checkpoint-mode must be service or off');
|
|
1334
|
+
process.exit(1);
|
|
1335
|
+
}
|
|
1336
|
+
await printDialogueEvents(baseUrl, headers, runId, { interval, jsonl, follow, failOnUnapproved, compact, checkpointMode });
|
|
1254
1337
|
|
|
1255
1338
|
} else if (['approve', 'revise', 'ask-user', 'continue'].includes(sub)) {
|
|
1256
1339
|
const messageId = args[2];
|
|
@@ -1291,6 +1374,7 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1291
1374
|
responses get <runId> --pick <field> Extract: first_image_url, first_video_url, project_url, output
|
|
1292
1375
|
responses events <runId> --jsonl Emit message/approval/artifact events for external Agents
|
|
1293
1376
|
responses timeline <runId> --jsonl Alias for responses events
|
|
1377
|
+
responses timeline <runId> --checkpoint-mode off Disable text-only checkpoints for pure Q&A
|
|
1294
1378
|
responses approve <messageId> --run <runId> Record approval for a Makaron message
|
|
1295
1379
|
responses revise <messageId> --run <runId> Record revision request for a Makaron message
|
|
1296
1380
|
responses ask-user <messageId> --run <runId> Record that the user must decide
|
|
@@ -1733,6 +1817,7 @@ Commands:
|
|
|
1733
1817
|
responses get <runId> Get run status and results
|
|
1734
1818
|
responses get <runId> --wait Poll until completed
|
|
1735
1819
|
responses events <runId> --jsonl Emit message/approval/artifact events
|
|
1820
|
+
responses timeline <runId> --checkpoint-mode off Disable text-only checkpoints for pure Q&A
|
|
1736
1821
|
responses list --project <id> List runs for a project
|
|
1737
1822
|
abort <runId> Abort a running Agent
|
|
1738
1823
|
|
package/package.json
CHANGED
package/skills/makaron/SKILL.md
CHANGED
|
@@ -128,11 +128,13 @@ Outputs one JSON per line as artifacts appear:
|
|
|
128
128
|
|
|
129
129
|
### Dialogue events for external Agents
|
|
130
130
|
|
|
131
|
-
Use this when another Agent needs to read what Makaron said,
|
|
131
|
+
Use this when another Agent needs to read what Makaron said, handle text checkpoints, and relay artifacts without inventing customer-service wording.
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
134
|
npx makaron-cli responses events <runId> --jsonl
|
|
135
135
|
# alias: npx makaron-cli responses timeline <runId> --jsonl
|
|
136
|
+
# compact view: npx makaron-cli responses timeline <runId> --jsonl --compact
|
|
137
|
+
# pure Q&A view: npx makaron-cli responses timeline <runId> --jsonl --checkpoint-mode off
|
|
136
138
|
```
|
|
137
139
|
|
|
138
140
|
Events use only three factual types:
|
|
@@ -143,7 +145,7 @@ Events use only three factual types:
|
|
|
143
145
|
{"type":"artifact","kind":"image","status":"completed","url":"https://..."}
|
|
144
146
|
```
|
|
145
147
|
|
|
146
|
-
If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
148
|
+
Makaron uses a conservative checkpoint rule for creative/service Agents: if a run stops with substantive text, has no image/video/music/design artifact, and has no continuing execution action such as a tool call, the message is marked `requires_approval: true`. Pure status text such as queued, rendering, uploading, or completed is not treated as a checkpoint. Pure Q&A flows should pass `--checkpoint-mode off`. If a message has `requires_approval: true`, the external Agent must record an approval before continuing to wait for artifacts or claiming completion:
|
|
147
149
|
|
|
148
150
|
```bash
|
|
149
151
|
npx makaron-cli responses approve msg_1 --run <runId> --note "Proceed."
|
|
@@ -155,10 +157,12 @@ npx makaron-cli responses continue msg_1 --run <runId>
|
|
|
155
157
|
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
158
|
|
|
157
159
|
```bash
|
|
158
|
-
npx makaron-cli responses events <runId> --jsonl
|
|
159
|
-
# alias: npx makaron-cli responses timeline <runId> --jsonl --fail-on-unapproved
|
|
160
|
+
npx makaron-cli responses events <runId> --jsonl --checkpoint-mode service --fail-on-unapproved
|
|
161
|
+
# alias: npx makaron-cli responses timeline <runId> --jsonl --compact --fail-on-unapproved
|
|
160
162
|
```
|
|
161
163
|
|
|
164
|
+
Use `--compact` when relaying to another Agent or chat system; it merges consecutive Makaron content chunks into a single readable message.
|
|
165
|
+
|
|
162
166
|
### Extract specific results
|
|
163
167
|
|
|
164
168
|
```bash
|
|
@@ -285,6 +289,33 @@ type MakaronOutput =
|
|
|
285
289
|
| Motion design | "create an Instagram story with animated text" |
|
|
286
290
|
| Multi-step | "edit the photo then make a video from it" |
|
|
287
291
|
|
|
292
|
+
## Minimal Agent Wrapper
|
|
293
|
+
|
|
294
|
+
A new Agent can use this minimal flow:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
RUN_JSON=$(npx makaron-cli chat --project auto --json -b "$USER_PROMPT")
|
|
298
|
+
RUN_ID=$(echo "$RUN_JSON" | jq -r .runId)
|
|
299
|
+
PROJECT_URL=$(echo "$RUN_JSON" | jq -r .projectUrl)
|
|
300
|
+
send_message "Project created: $PROJECT_URL"
|
|
301
|
+
|
|
302
|
+
if ! npx makaron-cli responses timeline "$RUN_ID" --jsonl --compact --checkpoint-mode service --fail-on-unapproved > /tmp/makaron-events.jsonl; then
|
|
303
|
+
npx makaron-cli responses timeline "$RUN_ID" --jsonl --compact | while read -r event; do
|
|
304
|
+
TYPE=$(echo "$event" | jq -r .type)
|
|
305
|
+
REQUIRES=$(echo "$event" | jq -r ".requires_approval // false")
|
|
306
|
+
TEXT=$(echo "$event" | jq -r ".text // empty")
|
|
307
|
+
MSG_ID=$(echo "$event" | jq -r ".id // empty")
|
|
308
|
+
if [ "$TYPE" = "message" ] && [ "$REQUIRES" = "true" ]; then
|
|
309
|
+
send_message "$TEXT"
|
|
310
|
+
npx makaron-cli responses ask-user "$MSG_ID" --run "$RUN_ID"
|
|
311
|
+
exit 3
|
|
312
|
+
fi
|
|
313
|
+
done
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
RESULT=$(npx makaron-cli responses get "$RUN_ID" --wait --json)
|
|
317
|
+
```
|
|
318
|
+
|
|
288
319
|
## Recommended Pattern: Service Flow (Feishu/OpenClaw/Group Chat)
|
|
289
320
|
|
|
290
321
|
When serving end-users in a chat environment (Feishu, Slack, Discord), use this proactive message pattern:
|