@maestrofrontier/frontier 1.4.5 → 1.6.0
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/.agents/plugins/marketplace.json +21 -21
- package/.codex-plugin/plugin.json +29 -29
- package/.cursorrules +197 -194
- package/AGENTS.md +3 -3
- package/README.md +368 -368
- package/bin/maestro.cjs +75 -75
- package/commands/compress.md +36 -36
- package/commands/frontier.md +124 -124
- package/commands/terse.md +23 -23
- package/docs/codex.md +167 -167
- package/docs/orchestration.md +168 -168
- package/frontier/cli.cjs +279 -252
- package/frontier/config.cjs +468 -468
- package/frontier/dispatch.cjs +267 -255
- package/frontier/judge.cjs +92 -92
- package/frontier/progress.cjs +138 -0
- package/frontier/run.cjs +201 -180
- package/frontier/schema.cjs +112 -112
- package/frontier/semaphore.cjs +49 -49
- package/frontier/synthesize.cjs +79 -79
- package/hooks/frontier-autorun.cjs +135 -120
- package/hooks/hooks.json +103 -103
- package/hooks/maestro-doctrine-guard.cjs +81 -81
- package/hooks/maestro-gate-reminder.cjs +22 -7
- package/hooks/maestro-gate-telemetry.cjs +79 -77
- package/hooks/maestro-phase-scope.cjs +118 -118
- package/hooks/maestro-statusline-sync.cjs +152 -152
- package/hooks/maestro-subagent-guard.cjs +148 -148
- package/hooks/maestro-terse-mode.cjs +189 -189
- package/hooks/maestro-toolbudget-advisory.cjs +127 -127
- package/integrations/README.md +111 -111
- package/integrations/cline/skills/frontier/SKILL.md +75 -75
- package/integrations/codex/prompts/frontier.md +70 -70
- package/integrations/codex/prompts/update.md +39 -39
- package/integrations/codex/skills/maestro-frontier/SKILL.md +122 -122
- package/integrations/codex/skills/maestro-settings/SKILL.md +55 -55
- package/integrations/codex/skills/maestro-terse/SKILL.md +58 -58
- package/integrations/codex/skills/maestro-update/SKILL.md +31 -31
- package/integrations/cursor/commands/frontier.md +63 -63
- package/integrations/cursor/commands/update.md +34 -34
- package/integrations/gemini/commands/frontier.toml +76 -76
- package/integrations/windsurf/workflows/frontier.md +70 -70
- package/package.json +59 -58
- package/scripts/install.cjs +1014 -1014
- package/settings/cli.cjs +140 -140
- package/settings/config.cjs +309 -309
- package/skills/maestro-frontier/SKILL.md +122 -122
- package/skills/maestro-settings/SKILL.md +55 -55
- package/skills/maestro-terse/SKILL.md +58 -58
- package/skills/maestro-update/SKILL.md +31 -31
- package/skills/terse/SKILL.md +74 -74
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Maestro SubagentStop guard. Enforces AGENTS.md S7.3 structurally.
|
|
3
|
-
// - Warn on stop with orphaned background_tasks (all agents)
|
|
4
|
-
// - Warn if a file-modifying agent ran no type-check/lint/test
|
|
5
|
-
// - Warn if a file-modifying agent's final text carries none of the
|
|
6
|
-
// S7.3 status tokens (VERIFIED / PENDING_REVIEW / UNVERIFIED / FAIL)
|
|
7
|
-
//
|
|
8
|
-
// Read-only agents (Explore, Plan, or any agent with no file mutation
|
|
9
|
-
// in its transcript) are exempt from the verification warning:
|
|
10
|
-
// research and audit agents have nothing to verify, and a warning on
|
|
11
|
-
// stop extends the agent's turn so its reply to the warning would
|
|
12
|
-
// displace the final report the orchestrator is waiting for.
|
|
13
|
-
//
|
|
14
|
-
// Fires at most once per agent: decision:block re-prompts the
|
|
15
|
-
// agent, which stops again and re-triggers this hook. Without the
|
|
16
|
-
// once-guard the warning loops and pushes the real report out of the
|
|
17
|
-
// final message entirely. The guard is a marker file in the temp dir
|
|
18
|
-
// keyed by the agent transcript path -- the block reason is injected
|
|
19
|
-
// into the conversation, NOT written to the transcript file, so
|
|
20
|
-
// grepping the transcript for our own warning never matches
|
|
21
|
-
// (observed live 2026-06-10). The transcript check is kept as a
|
|
22
|
-
// secondary guard for harness versions that do persist it.
|
|
23
|
-
//
|
|
24
|
-
// Feedback channel: {"decision":"block","reason":...} -- the only
|
|
25
|
-
// SubagentStop output the harness honors (additionalContext is not
|
|
26
|
-
// supported on this event). Blocks the stop exactly once; the agent
|
|
27
|
-
// is told to restate its final report so the orchestrator still
|
|
28
|
-
// receives it.
|
|
29
|
-
//
|
|
30
|
-
// .cjs so Node treats it as CommonJS regardless of any "type": "module"
|
|
31
|
-
// package.json in a parent directory of the install location.
|
|
32
|
-
//
|
|
33
|
-
// Install: see README "Claude Code: Verification Hook".
|
|
34
|
-
|
|
35
|
-
const fs = require('fs');
|
|
36
|
-
const os = require('os');
|
|
37
|
-
const path = require('path');
|
|
38
|
-
const crypto = require('crypto');
|
|
39
|
-
|
|
40
|
-
let data = {};
|
|
41
|
-
try { data = JSON.parse(fs.readFileSync(0, 'utf8')); } catch { process.exit(0); }
|
|
42
|
-
|
|
43
|
-
// Prefer the subagent's own transcript (agent_transcript_path, since
|
|
44
|
-
// Claude Code 2.0.42) over the session transcript.
|
|
45
|
-
const txPath = data.agent_transcript_path || data.transcript_path;
|
|
46
|
-
let txText = '';
|
|
47
|
-
if (txPath && fs.existsSync(txPath)) {
|
|
48
|
-
try {
|
|
49
|
-
const buf = fs.readFileSync(txPath, 'utf8');
|
|
50
|
-
txText = buf.length > 2000000 ? buf.slice(-2000000) : buf;
|
|
51
|
-
} catch {}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Fire once per agent: marker file keyed by transcript path (or
|
|
55
|
-
// session id). MAESTRO_GUARD_STATE_DIR overrides the marker dir for
|
|
56
|
-
// tests. Transcript check kept as a secondary guard.
|
|
57
|
-
const guardKey = data.agent_transcript_path || data.transcript_path || data.session_id || '';
|
|
58
|
-
const stateDir = process.env.MAESTRO_GUARD_STATE_DIR || os.tmpdir();
|
|
59
|
-
const marker = guardKey
|
|
60
|
-
? path.join(stateDir, 'maestro-guard-' + crypto.createHash('sha1').update(String(guardKey)).digest('hex').slice(0, 16))
|
|
61
|
-
: null;
|
|
62
|
-
if (marker && fs.existsSync(marker)) process.exit(0);
|
|
63
|
-
if (txText.includes('Maestro guard:')) process.exit(0);
|
|
64
|
-
|
|
65
|
-
const warnings = [];
|
|
66
|
-
|
|
67
|
-
// background_tasks in the SubagentStop payload is machine-wide (all
|
|
68
|
-
// sessions), not scoped to this agent (observed live 2026-06-10: a
|
|
69
|
-
// fixture-builder agent was warned about unrelated sessions' tasks).
|
|
70
|
-
// Only warn when the agent's own transcript shows it spawned
|
|
71
|
-
// background work; agents that spawned nothing are exempt.
|
|
72
|
-
const spawnRe = /"run_in_background"\s*:\s*true|"name"\s*:\s*"TaskCreate"/;
|
|
73
|
-
const bg = Array.isArray(data.background_tasks) ? data.background_tasks : [];
|
|
74
|
-
const active = bg.filter(t => t && (t.status === 'running' || t.status === 'pending' || t.status === 'active'));
|
|
75
|
-
if (active.length && spawnRe.test(txText)) {
|
|
76
|
-
warnings.push(`${active.length} background task(s) still active. Wait or stop before declaring complete (AGENTS.md S7.3).`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Read-only exemption: known read-only agent types (agent_type, since
|
|
80
|
-
// Claude Code 2.1.69), or no file-mutating activity in the transcript.
|
|
81
|
-
// Mutation = Edit/Write/NotebookEdit tool calls, plus Bash mutations:
|
|
82
|
-
// redirects, sed -i, tee/mv/cp/rm/mkdir/touch, git commit/apply,
|
|
83
|
-
// migrations, package installs. Bash patterns are tested against the
|
|
84
|
-
// parsed command strings only, never raw transcript text -- arrows
|
|
85
|
-
// (->, =>) and redirect-ish chars in prose must not flip a research
|
|
86
|
-
// agent into the writer path (a false nag eats its final report).
|
|
87
|
-
const READ_ONLY_TYPES = new Set(['explore', 'plan']);
|
|
88
|
-
const agentType = String(data.agent_type || '').toLowerCase();
|
|
89
|
-
const toolMutRe = /"name"\s*:\s*"(Edit|Write|NotebookEdit)"/;
|
|
90
|
-
const bashMutRe = /(?<![-=<>])>{1,2}\s*[^\s&|<>]|(^|[\s;&|(])(sed\s+(-\S+\s+)*-i|tee\s|mv\s|cp\s|rm\s|mkdir\s|touch\s|git\s+(commit|apply)\b|apply_migration|(npm|pnpm|yarn)\s+(i|install|add)\b)/;
|
|
91
|
-
let bashMutation = false;
|
|
92
|
-
for (const line of txText.split(/\r?\n/)) {
|
|
93
|
-
let obj;
|
|
94
|
-
try { obj = JSON.parse(line); } catch { continue; }
|
|
95
|
-
if (!obj || obj.type !== 'assistant' || !obj.message || !Array.isArray(obj.message.content)) continue;
|
|
96
|
-
for (const c of obj.message.content) {
|
|
97
|
-
if (c && c.type === 'tool_use' && c.name === 'Bash' && c.input &&
|
|
98
|
-
typeof c.input.command === 'string' && bashMutRe.test(c.input.command)) {
|
|
99
|
-
bashMutation = true;
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (bashMutation) break;
|
|
104
|
-
}
|
|
105
|
-
const readOnly = READ_ONLY_TYPES.has(agentType) || (txText !== '' && !toolMutRe.test(txText) && !bashMutation);
|
|
106
|
-
|
|
107
|
-
const verifyRe = /(tsc\s+--noEmit|eslint|pytest|jest|vitest|\bgo\s+test\b|\bcargo\s+test\b|npm\s+(?:run\s+)?test|pnpm\s+test|yarn\s+test|ruff\s+check|mypy|prettier\s+--check|biome\s+check)/i;
|
|
108
|
-
if (!readOnly && txText && !verifyRe.test(txText)) {
|
|
109
|
-
warnings.push('No type-check/lint/test detected after file modifications. Verify before complete, or state "no checker configured" (AGENTS.md S7.3).');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// S7.3 status vocabulary: a file-modifying agent's final text must
|
|
113
|
-
// carry one of the four status tokens. Case-sensitive on purpose --
|
|
114
|
-
// the doctrine tokens are uppercase, and lowercase "fail"/"verified"
|
|
115
|
-
// in prose are not status declarations.
|
|
116
|
-
const statusRe = /\b(VERIFIED|PENDING_REVIEW|UNVERIFIED|FAIL)\b/;
|
|
117
|
-
if (!readOnly && txText) {
|
|
118
|
-
let finalText = '';
|
|
119
|
-
for (const line of txText.split(/\r?\n/)) {
|
|
120
|
-
let obj;
|
|
121
|
-
try { obj = JSON.parse(line); } catch { continue; }
|
|
122
|
-
if (obj && obj.type === 'assistant' && obj.message && Array.isArray(obj.message.content)) {
|
|
123
|
-
const t = obj.message.content
|
|
124
|
-
.filter(c => c && c.type === 'text' && typeof c.text === 'string')
|
|
125
|
-
.map(c => c.text).join('\n');
|
|
126
|
-
if (t) finalText = t;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if (finalText && !statusRe.test(finalText)) {
|
|
130
|
-
warnings.push('Final report carries no status token. State exactly one of VERIFIED / PENDING_REVIEW / UNVERIFIED / FAIL, with the named gap if not VERIFIED (AGENTS.md S7.3).');
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (warnings.length) {
|
|
135
|
-
if (marker) { try { fs.writeFileSync(marker, String(Date.now())); } catch {} }
|
|
136
|
-
// SubagentStop supports only decision:block + reason as a feedback
|
|
137
|
-
// channel (additionalContext is not honored on this event). Blocking
|
|
138
|
-
// the stop feeds the reason back to the subagent, which addresses it
|
|
139
|
-
// and stops again; the marker file above keeps that second stop silent.
|
|
140
|
-
const payload = {
|
|
141
|
-
decision: 'block',
|
|
142
|
-
reason: 'Maestro guard:\n- ' + warnings.join('\n- ') +
|
|
143
|
-
'\nAfter addressing this, restate your complete final report. Only your last message is returned to the orchestrator.'
|
|
144
|
-
};
|
|
145
|
-
process.stdout.write(JSON.stringify(payload));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
process.exit(0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Maestro SubagentStop guard. Enforces AGENTS.md S7.3 structurally.
|
|
3
|
+
// - Warn on stop with orphaned background_tasks (all agents)
|
|
4
|
+
// - Warn if a file-modifying agent ran no type-check/lint/test
|
|
5
|
+
// - Warn if a file-modifying agent's final text carries none of the
|
|
6
|
+
// S7.3 status tokens (VERIFIED / PENDING_REVIEW / UNVERIFIED / FAIL)
|
|
7
|
+
//
|
|
8
|
+
// Read-only agents (Explore, Plan, or any agent with no file mutation
|
|
9
|
+
// in its transcript) are exempt from the verification warning:
|
|
10
|
+
// research and audit agents have nothing to verify, and a warning on
|
|
11
|
+
// stop extends the agent's turn so its reply to the warning would
|
|
12
|
+
// displace the final report the orchestrator is waiting for.
|
|
13
|
+
//
|
|
14
|
+
// Fires at most once per agent: decision:block re-prompts the
|
|
15
|
+
// agent, which stops again and re-triggers this hook. Without the
|
|
16
|
+
// once-guard the warning loops and pushes the real report out of the
|
|
17
|
+
// final message entirely. The guard is a marker file in the temp dir
|
|
18
|
+
// keyed by the agent transcript path -- the block reason is injected
|
|
19
|
+
// into the conversation, NOT written to the transcript file, so
|
|
20
|
+
// grepping the transcript for our own warning never matches
|
|
21
|
+
// (observed live 2026-06-10). The transcript check is kept as a
|
|
22
|
+
// secondary guard for harness versions that do persist it.
|
|
23
|
+
//
|
|
24
|
+
// Feedback channel: {"decision":"block","reason":...} -- the only
|
|
25
|
+
// SubagentStop output the harness honors (additionalContext is not
|
|
26
|
+
// supported on this event). Blocks the stop exactly once; the agent
|
|
27
|
+
// is told to restate its final report so the orchestrator still
|
|
28
|
+
// receives it.
|
|
29
|
+
//
|
|
30
|
+
// .cjs so Node treats it as CommonJS regardless of any "type": "module"
|
|
31
|
+
// package.json in a parent directory of the install location.
|
|
32
|
+
//
|
|
33
|
+
// Install: see README "Claude Code: Verification Hook".
|
|
34
|
+
|
|
35
|
+
const fs = require('fs');
|
|
36
|
+
const os = require('os');
|
|
37
|
+
const path = require('path');
|
|
38
|
+
const crypto = require('crypto');
|
|
39
|
+
|
|
40
|
+
let data = {};
|
|
41
|
+
try { data = JSON.parse(fs.readFileSync(0, 'utf8')); } catch { process.exit(0); }
|
|
42
|
+
|
|
43
|
+
// Prefer the subagent's own transcript (agent_transcript_path, since
|
|
44
|
+
// Claude Code 2.0.42) over the session transcript.
|
|
45
|
+
const txPath = data.agent_transcript_path || data.transcript_path;
|
|
46
|
+
let txText = '';
|
|
47
|
+
if (txPath && fs.existsSync(txPath)) {
|
|
48
|
+
try {
|
|
49
|
+
const buf = fs.readFileSync(txPath, 'utf8');
|
|
50
|
+
txText = buf.length > 2000000 ? buf.slice(-2000000) : buf;
|
|
51
|
+
} catch {}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fire once per agent: marker file keyed by transcript path (or
|
|
55
|
+
// session id). MAESTRO_GUARD_STATE_DIR overrides the marker dir for
|
|
56
|
+
// tests. Transcript check kept as a secondary guard.
|
|
57
|
+
const guardKey = data.agent_transcript_path || data.transcript_path || data.session_id || '';
|
|
58
|
+
const stateDir = process.env.MAESTRO_GUARD_STATE_DIR || os.tmpdir();
|
|
59
|
+
const marker = guardKey
|
|
60
|
+
? path.join(stateDir, 'maestro-guard-' + crypto.createHash('sha1').update(String(guardKey)).digest('hex').slice(0, 16))
|
|
61
|
+
: null;
|
|
62
|
+
if (marker && fs.existsSync(marker)) process.exit(0);
|
|
63
|
+
if (txText.includes('Maestro guard:')) process.exit(0);
|
|
64
|
+
|
|
65
|
+
const warnings = [];
|
|
66
|
+
|
|
67
|
+
// background_tasks in the SubagentStop payload is machine-wide (all
|
|
68
|
+
// sessions), not scoped to this agent (observed live 2026-06-10: a
|
|
69
|
+
// fixture-builder agent was warned about unrelated sessions' tasks).
|
|
70
|
+
// Only warn when the agent's own transcript shows it spawned
|
|
71
|
+
// background work; agents that spawned nothing are exempt.
|
|
72
|
+
const spawnRe = /"run_in_background"\s*:\s*true|"name"\s*:\s*"TaskCreate"/;
|
|
73
|
+
const bg = Array.isArray(data.background_tasks) ? data.background_tasks : [];
|
|
74
|
+
const active = bg.filter(t => t && (t.status === 'running' || t.status === 'pending' || t.status === 'active'));
|
|
75
|
+
if (active.length && spawnRe.test(txText)) {
|
|
76
|
+
warnings.push(`${active.length} background task(s) still active. Wait or stop before declaring complete (AGENTS.md S7.3).`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Read-only exemption: known read-only agent types (agent_type, since
|
|
80
|
+
// Claude Code 2.1.69), or no file-mutating activity in the transcript.
|
|
81
|
+
// Mutation = Edit/Write/NotebookEdit tool calls, plus Bash mutations:
|
|
82
|
+
// redirects, sed -i, tee/mv/cp/rm/mkdir/touch, git commit/apply,
|
|
83
|
+
// migrations, package installs. Bash patterns are tested against the
|
|
84
|
+
// parsed command strings only, never raw transcript text -- arrows
|
|
85
|
+
// (->, =>) and redirect-ish chars in prose must not flip a research
|
|
86
|
+
// agent into the writer path (a false nag eats its final report).
|
|
87
|
+
const READ_ONLY_TYPES = new Set(['explore', 'plan']);
|
|
88
|
+
const agentType = String(data.agent_type || '').toLowerCase();
|
|
89
|
+
const toolMutRe = /"name"\s*:\s*"(Edit|Write|NotebookEdit)"/;
|
|
90
|
+
const bashMutRe = /(?<![-=<>])>{1,2}\s*[^\s&|<>]|(^|[\s;&|(])(sed\s+(-\S+\s+)*-i|tee\s|mv\s|cp\s|rm\s|mkdir\s|touch\s|git\s+(commit|apply)\b|apply_migration|(npm|pnpm|yarn)\s+(i|install|add)\b)/;
|
|
91
|
+
let bashMutation = false;
|
|
92
|
+
for (const line of txText.split(/\r?\n/)) {
|
|
93
|
+
let obj;
|
|
94
|
+
try { obj = JSON.parse(line); } catch { continue; }
|
|
95
|
+
if (!obj || obj.type !== 'assistant' || !obj.message || !Array.isArray(obj.message.content)) continue;
|
|
96
|
+
for (const c of obj.message.content) {
|
|
97
|
+
if (c && c.type === 'tool_use' && c.name === 'Bash' && c.input &&
|
|
98
|
+
typeof c.input.command === 'string' && bashMutRe.test(c.input.command)) {
|
|
99
|
+
bashMutation = true;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (bashMutation) break;
|
|
104
|
+
}
|
|
105
|
+
const readOnly = READ_ONLY_TYPES.has(agentType) || (txText !== '' && !toolMutRe.test(txText) && !bashMutation);
|
|
106
|
+
|
|
107
|
+
const verifyRe = /(tsc\s+--noEmit|eslint|pytest|jest|vitest|\bgo\s+test\b|\bcargo\s+test\b|npm\s+(?:run\s+)?test|pnpm\s+test|yarn\s+test|ruff\s+check|mypy|prettier\s+--check|biome\s+check)/i;
|
|
108
|
+
if (!readOnly && txText && !verifyRe.test(txText)) {
|
|
109
|
+
warnings.push('No type-check/lint/test detected after file modifications. Verify before complete, or state "no checker configured" (AGENTS.md S7.3).');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// S7.3 status vocabulary: a file-modifying agent's final text must
|
|
113
|
+
// carry one of the four status tokens. Case-sensitive on purpose --
|
|
114
|
+
// the doctrine tokens are uppercase, and lowercase "fail"/"verified"
|
|
115
|
+
// in prose are not status declarations.
|
|
116
|
+
const statusRe = /\b(VERIFIED|PENDING_REVIEW|UNVERIFIED|FAIL)\b/;
|
|
117
|
+
if (!readOnly && txText) {
|
|
118
|
+
let finalText = '';
|
|
119
|
+
for (const line of txText.split(/\r?\n/)) {
|
|
120
|
+
let obj;
|
|
121
|
+
try { obj = JSON.parse(line); } catch { continue; }
|
|
122
|
+
if (obj && obj.type === 'assistant' && obj.message && Array.isArray(obj.message.content)) {
|
|
123
|
+
const t = obj.message.content
|
|
124
|
+
.filter(c => c && c.type === 'text' && typeof c.text === 'string')
|
|
125
|
+
.map(c => c.text).join('\n');
|
|
126
|
+
if (t) finalText = t;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (finalText && !statusRe.test(finalText)) {
|
|
130
|
+
warnings.push('Final report carries no status token. State exactly one of VERIFIED / PENDING_REVIEW / UNVERIFIED / FAIL, with the named gap if not VERIFIED (AGENTS.md S7.3).');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (warnings.length) {
|
|
135
|
+
if (marker) { try { fs.writeFileSync(marker, String(Date.now())); } catch {} }
|
|
136
|
+
// SubagentStop supports only decision:block + reason as a feedback
|
|
137
|
+
// channel (additionalContext is not honored on this event). Blocking
|
|
138
|
+
// the stop feeds the reason back to the subagent, which addresses it
|
|
139
|
+
// and stops again; the marker file above keeps that second stop silent.
|
|
140
|
+
const payload = {
|
|
141
|
+
decision: 'block',
|
|
142
|
+
reason: 'Maestro guard:\n- ' + warnings.join('\n- ') +
|
|
143
|
+
'\nAfter addressing this, restate your complete final report. Only your last message is returned to the orchestrator.'
|
|
144
|
+
};
|
|
145
|
+
process.stdout.write(JSON.stringify(payload));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
process.exit(0);
|