@moda-ai/cli 1.22.1 → 1.23.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.
Files changed (118) hide show
  1. package/dist/cli-c4se7a5d.js +762 -0
  2. package/dist/cli-mnqa4rp6.js +1563 -0
  3. package/dist/cli-stmjdgvy.js +2842 -0
  4. package/dist/cli-y55bgg14.js +6980 -0
  5. package/dist/cli-ysegdrm5.js +286 -0
  6. package/dist/cli.js +4779 -1159
  7. package/dist/harness-github-actions-7t3cncnt.js +95 -0
  8. package/dist/index-4ser01zr.js +2769 -0
  9. package/dist/provision-hm1ppwj6.js +74 -0
  10. package/package.json +3 -2
  11. package/skills/integration/index.json +2 -2
  12. package/dist/agent-schemas.d.ts +0 -3
  13. package/dist/agent-schemas.js +0 -358
  14. package/dist/agent.d.ts +0 -5
  15. package/dist/agent.js +0 -477
  16. package/dist/analyst-tui.d.ts +0 -81
  17. package/dist/analyst-tui.js +0 -314
  18. package/dist/api-client.d.ts +0 -46
  19. package/dist/api-client.js +0 -120
  20. package/dist/ask-stream.d.ts +0 -146
  21. package/dist/ask-stream.js +0 -473
  22. package/dist/auth-storage.d.ts +0 -35
  23. package/dist/auth-storage.js +0 -159
  24. package/dist/auth.d.ts +0 -50
  25. package/dist/auth.js +0 -694
  26. package/dist/cli-session.d.ts +0 -26
  27. package/dist/cli-session.js +0 -169
  28. package/dist/cli.d.ts +0 -44
  29. package/dist/command-context.d.ts +0 -62
  30. package/dist/command-context.js +0 -57
  31. package/dist/command-registry.d.ts +0 -22
  32. package/dist/command-registry.js +0 -15
  33. package/dist/config.d.ts +0 -60
  34. package/dist/config.js +0 -110
  35. package/dist/doctor.d.ts +0 -79
  36. package/dist/doctor.js +0 -1182
  37. package/dist/errors.d.ts +0 -50
  38. package/dist/errors.js +0 -64
  39. package/dist/exit-funnel.d.ts +0 -14
  40. package/dist/exit-funnel.js +0 -53
  41. package/dist/harness-artifact-bodies.d.ts +0 -28
  42. package/dist/harness-artifact-bodies.js +0 -276
  43. package/dist/harness-candidates.d.ts +0 -77
  44. package/dist/harness-candidates.js +0 -482
  45. package/dist/harness-remote.d.ts +0 -135
  46. package/dist/harness-remote.js +0 -540
  47. package/dist/harness-report.d.ts +0 -145
  48. package/dist/harness-report.js +0 -1579
  49. package/dist/harness-sync-limits.d.ts +0 -8
  50. package/dist/harness-sync-limits.js +0 -21
  51. package/dist/harness.d.ts +0 -463
  52. package/dist/harness.js +0 -4009
  53. package/dist/init/index.d.ts +0 -78
  54. package/dist/init/index.js +0 -1442
  55. package/dist/init/mcp.d.ts +0 -2
  56. package/dist/init/mcp.js +0 -27
  57. package/dist/init/progress-board.d.ts +0 -80
  58. package/dist/init/progress-board.js +0 -225
  59. package/dist/init/rules.d.ts +0 -6
  60. package/dist/init/rules.js +0 -171
  61. package/dist/init/sdk-catalog.d.ts +0 -10
  62. package/dist/init/sdk-catalog.js +0 -80
  63. package/dist/init/sdk-guide.d.ts +0 -19
  64. package/dist/init/sdk-guide.js +0 -185
  65. package/dist/init/sdk-integration.d.ts +0 -53
  66. package/dist/init/sdk-integration.js +0 -835
  67. package/dist/init/sdk-protocol.d.ts +0 -135
  68. package/dist/init/sdk-protocol.js +0 -152
  69. package/dist/init/sdk-verification.d.ts +0 -27
  70. package/dist/init/sdk-verification.js +0 -123
  71. package/dist/init/tenant.d.ts +0 -67
  72. package/dist/init/tenant.js +0 -421
  73. package/dist/intelligence-schema.d.ts +0 -349
  74. package/dist/intelligence-schema.js +0 -174
  75. package/dist/keys.d.ts +0 -47
  76. package/dist/keys.js +0 -136
  77. package/dist/manifest.d.ts +0 -5
  78. package/dist/manifest.js +0 -114
  79. package/dist/markdown-terminal.d.ts +0 -49
  80. package/dist/markdown-terminal.js +0 -212
  81. package/dist/output.d.ts +0 -191
  82. package/dist/output.js +0 -624
  83. package/dist/production-intelligence.d.ts +0 -175
  84. package/dist/production-intelligence.js +0 -1430
  85. package/dist/profile-commands.d.ts +0 -10
  86. package/dist/profile-commands.js +0 -342
  87. package/dist/profiles.d.ts +0 -112
  88. package/dist/profiles.js +0 -573
  89. package/dist/prompts-ab.d.ts +0 -71
  90. package/dist/prompts-ab.js +0 -313
  91. package/dist/prompts-propose.d.ts +0 -15
  92. package/dist/prompts-propose.js +0 -212
  93. package/dist/prompts.d.ts +0 -58
  94. package/dist/prompts.js +0 -418
  95. package/dist/provision.d.ts +0 -28
  96. package/dist/provision.js +0 -104
  97. package/dist/redact.d.ts +0 -2
  98. package/dist/redact.js +0 -90
  99. package/dist/schemas.d.ts +0 -225
  100. package/dist/schemas.js +0 -126
  101. package/dist/skills-bundled.d.ts +0 -35
  102. package/dist/skills-bundled.js +0 -157
  103. package/dist/skills-manifest.d.ts +0 -33
  104. package/dist/skills-manifest.js +0 -244
  105. package/dist/skills.d.ts +0 -57
  106. package/dist/skills.js +0 -783
  107. package/dist/sync.d.ts +0 -5
  108. package/dist/sync.js +0 -36
  109. package/dist/telemetry.d.ts +0 -56
  110. package/dist/telemetry.js +0 -167
  111. package/dist/transforms.d.ts +0 -25
  112. package/dist/transforms.js +0 -102
  113. package/dist/tui.d.ts +0 -48
  114. package/dist/tui.js +0 -393
  115. package/dist/update-check.d.ts +0 -31
  116. package/dist/update-check.js +0 -141
  117. package/dist/workspace.d.ts +0 -12
  118. package/dist/workspace.js +0 -1294
package/dist/output.js DELETED
@@ -1,624 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { CliAccessError, CliAuthError, CliCancellationError, CliInputError, CliInputRequiredError, isApiLikeError, isNetworkLikeError, } from './errors.js';
3
- import { redactDeep } from './redact.js';
4
- export const AGENT_SCHEMA_VERSION = 'moda.agent.v1';
5
- export const AGENT_EVENT_SCHEMA_VERSION = 'moda.agent_event.v1';
6
- /**
7
- * One-line feedback nudge stamped into every non-feedback success envelope's
8
- * `meta.tip` (agents are the CLI's most common callers, and the envelope is
9
- * where they actually look). Deliberately conditional wording so agents flag
10
- * genuine observations instead of habitually running the command. The
11
- * human-TTY twin is printed on stderr by the dispatcher in cli.ts.
12
- */
13
- export const FEEDBACK_TIP = 'Help us improve! If a result in this response looks wrong or is missing data, flag it: moda feedback "<describe what looked wrong>" [--category=...] [--conversation-id=... | --cluster-id=... | --tool-name=...]. Only submit genuine observations.';
14
- /**
15
- * Stable process exit codes that an external agent can branch on:
16
- * 0 ok — grounded answer from Moda Cloud intelligence.
17
- * 3 degraded — answer produced from a local/degraded fallback (lower trust).
18
- * 1 error — command/input/unexpected failure (see envelope `errors[]`).
19
- * 4 auth — login or token renewal required before retrying.
20
- * 5 input — non-interactive caller must choose and re-invoke.
21
- * 130 cancel — interrupted or the output consumer closed the pipe.
22
- * `EXIT_DEGRADED` is intentionally distinct so a caller can tell a degraded
23
- * answer apart from both success and hard failure without parsing the payload.
24
- */
25
- export const EXIT_OK = 0;
26
- export const EXIT_ERROR = 1;
27
- export const EXIT_DEGRADED = 3;
28
- export const EXIT_AUTH_REQUIRED = 4;
29
- export const EXIT_INPUT_REQUIRED = 5;
30
- export const EXIT_CANCELLED = 130;
31
- export const AGENT_ERROR_CODES = [
32
- 'input_error',
33
- 'api_error',
34
- 'network_error',
35
- 'auth_required',
36
- 'input_required',
37
- 'cancelled',
38
- 'command_error',
39
- 'access_denied',
40
- 'unknown_error',
41
- 'cloud_unavailable',
42
- ];
43
- const FALLBACK_RUN_ID = randomUUID();
44
- let fallbackSeq = 0;
45
- let stdoutClosed = false;
46
- let stderrClosed = false;
47
- process.stdout.on('error', (error) => {
48
- if (error.code === 'EPIPE') {
49
- stdoutClosed = true;
50
- process.exitCode = EXIT_CANCELLED;
51
- return;
52
- }
53
- throw error;
54
- });
55
- process.stderr.on('error', (error) => {
56
- if (error.code === 'EPIPE') {
57
- stderrClosed = true;
58
- process.exitCode = EXIT_CANCELLED;
59
- return;
60
- }
61
- throw error;
62
- });
63
- /**
64
- * True once an EPIPE from a closed stdout/stderr consumer has been observed.
65
- * The stream error listeners above own this state; expose it so callers can
66
- * detect consumer-cancelled runs explicitly instead of sniffing the global
67
- * `process.exitCode`.
68
- */
69
- export function wasOutputCancelled() {
70
- return stdoutClosed || stderrClosed;
71
- }
72
- export function safeWriteStdout(text) {
73
- if (stdoutClosed)
74
- return false;
75
- try {
76
- if (process.env.NODE_ENV === 'test') {
77
- console.log(text.endsWith('\n') ? text.slice(0, -1) : text);
78
- }
79
- else {
80
- process.stdout.write(text);
81
- }
82
- return true;
83
- }
84
- catch (error) {
85
- if (error.code === 'EPIPE') {
86
- stdoutClosed = true;
87
- process.exitCode = EXIT_CANCELLED;
88
- return false;
89
- }
90
- throw error;
91
- }
92
- }
93
- export function safeWriteStderr(text) {
94
- if (stderrClosed)
95
- return false;
96
- try {
97
- if (process.env.NODE_ENV === 'test') {
98
- console.error(text.endsWith('\n') ? text.slice(0, -1) : text);
99
- }
100
- else {
101
- process.stderr.write(text);
102
- }
103
- return true;
104
- }
105
- catch (error) {
106
- if (error.code === 'EPIPE') {
107
- stderrClosed = true;
108
- process.exitCode = EXIT_CANCELLED;
109
- return false;
110
- }
111
- throw error;
112
- }
113
- }
114
- /**
115
- * Output mode requested by explicit flags only, or `undefined` when the caller
116
- * did not pick one. Explicit flags always win over auto-detection.
117
- */
118
- function explicitModeFromFlags(flags) {
119
- if (flags.agent === 'true' && (flags.stream === 'true' || flags.format === 'ndjson'))
120
- return 'agent-stream';
121
- if (flags.agent === 'true')
122
- return 'agent';
123
- if (flags.json === 'true')
124
- return 'json';
125
- if (flags.human === 'true' || flags.prose === 'true')
126
- return 'human';
127
- return undefined;
128
- }
129
- /** Flag-only resolution (default human). Kept for callers that only have flags. */
130
- export function outputModeFromFlags(flags) {
131
- return explicitModeFromFlags(flags) ?? 'human';
132
- }
133
- function outputModeFromEnv(env) {
134
- const fmt = (env.MODA_FORMAT ?? '').trim().toLowerCase();
135
- if (fmt === 'human' || fmt === 'json' || fmt === 'agent')
136
- return fmt;
137
- if (fmt === 'agent-stream' || fmt === 'ndjson')
138
- return 'agent-stream';
139
- return undefined;
140
- }
141
- function truthyEnv(value) {
142
- return value != null && value !== '' && value !== '0' && value.toLowerCase() !== 'false';
143
- }
144
- /**
145
- * True when the process appears to be driven by an agent / automation rather
146
- * than an interactive human (Claude Code, Cursor, generic CI, or an explicit
147
- * `MODA_AGENT` opt-in). Such callers get machine-parseable output by default.
148
- */
149
- function isAgentEnvironment(env) {
150
- return (truthyEnv(env.CLAUDECODE) ||
151
- truthyEnv(env.CURSOR) ||
152
- truthyEnv(env.CI) ||
153
- truthyEnv(env.MODA_AGENT));
154
- }
155
- /**
156
- * Resolve the effective output mode for a real CLI invocation, in priority
157
- * order: explicit flags -> `MODA_FORMAT` -> agent-environment detection ->
158
- * TTY detection (piped/redirected => agent JSON) -> human prose. This is what
159
- * makes the CLI agent-safe: a piped/automated caller gets structured output
160
- * with zero flags, while a human at a terminal still gets prose.
161
- */
162
- export function resolveOutputMode(flags, opts = {}) {
163
- const explicit = explicitModeFromFlags(flags);
164
- if (explicit)
165
- return explicit;
166
- const env = opts.env ?? process.env;
167
- const fromEnv = outputModeFromEnv(env);
168
- if (fromEnv)
169
- return fromEnv;
170
- if (isAgentEnvironment(env))
171
- return 'agent';
172
- const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);
173
- if (!isTTY)
174
- return 'agent';
175
- return 'human';
176
- }
177
- export function createCliOutput(options) {
178
- const state = {
179
- runId: options.runId ?? randomUUID(),
180
- seq: 0,
181
- started: false,
182
- terminal: false,
183
- startedAt: Date.now(),
184
- lastEventAt: Date.now(),
185
- };
186
- const emitEvent = (event) => {
187
- if (options.quiet || options.mode !== 'agent-stream')
188
- return;
189
- if (state.terminal)
190
- return;
191
- if (event.event === 'started' && state.started)
192
- return;
193
- if (event.event === 'started')
194
- state.started = true;
195
- if (isTerminalEvent(event.event)) {
196
- state.terminal = true;
197
- clearHeartbeat(state);
198
- }
199
- state.lastEventAt = Date.now();
200
- safeWriteStdout(`${JSON.stringify(createAgentEvent(options.command, event, state))}\n`);
201
- };
202
- const ensureStarted = () => {
203
- if (options.mode !== 'agent-stream' || state.started || options.quiet)
204
- return;
205
- startHeartbeat(options.command, state);
206
- emitEvent({
207
- event: 'started',
208
- phase: options.command,
209
- message: `Starting ${options.command}.`,
210
- elapsed_ms: 0,
211
- });
212
- };
213
- const writeEnvelope = (data, writeOptions = {}) => {
214
- return createAgentEnvelope({
215
- command: options.command,
216
- status: writeOptions.status === 'error'
217
- ? 'error'
218
- : writeOptions.status === 'degraded'
219
- ? 'degraded'
220
- : 'ok',
221
- data,
222
- meta: {
223
- ...(writeOptions.meta ?? {}),
224
- run_id: state.runId,
225
- // No tip on the feedback command itself; it would point at the
226
- // command that just ran.
227
- ...(options.command === 'feedback' ? {} : { tip: FEEDBACK_TIP }),
228
- },
229
- summary: writeOptions.summary,
230
- findings: writeOptions.findings,
231
- evidenceRefs: writeOptions.evidenceRefs,
232
- recommendedActions: writeOptions.recommendedActions,
233
- nextCommands: writeOptions.nextCommands,
234
- artifacts: writeOptions.artifacts,
235
- warnings: writeOptions.warnings,
236
- errors: writeOptions.errors,
237
- });
238
- };
239
- const output = {
240
- mode: options.mode,
241
- quiet: options.quiet === true,
242
- writeData(data, writeOptions = {}) {
243
- if (options.quiet)
244
- return;
245
- if (options.mode === 'agent' || options.mode === 'agent-stream') {
246
- if (state.terminal)
247
- return;
248
- const envelope = writeEnvelope(data, writeOptions);
249
- if (options.mode === 'agent-stream') {
250
- ensureStarted();
251
- emitEvent({
252
- event: envelope.status === 'error' ? 'error' : 'completed',
253
- status: envelope.status,
254
- result: envelope,
255
- ...(envelope.errors[0] ? { error: envelope.errors[0] } : {}),
256
- });
257
- }
258
- else {
259
- state.terminal = true;
260
- safeWriteStdout(`${JSON.stringify(envelope)}\n`);
261
- }
262
- return;
263
- }
264
- safeWriteStdout(`${JSON.stringify(redactDeep(data), null, 2)}\n`);
265
- },
266
- writeEvent(event) {
267
- if (options.quiet)
268
- return;
269
- if (options.mode !== 'agent-stream')
270
- return;
271
- if (event.event === 'started') {
272
- startHeartbeat(options.command, state);
273
- emitEvent(event);
274
- return;
275
- }
276
- ensureStarted();
277
- emitEvent(event);
278
- },
279
- writeError(error) {
280
- if (options.quiet || state.terminal)
281
- return;
282
- const agentError = errorToAgentError(error);
283
- const envelope = createAgentErrorEnvelope(options.command || 'unknown', agentError, state.runId);
284
- if (options.mode === 'agent-stream') {
285
- ensureStarted();
286
- if (error instanceof CliInputRequiredError) {
287
- emitEvent({
288
- event: 'needs_input',
289
- status: 'error',
290
- message: error.message,
291
- error: agentError,
292
- input_request: error.input_request,
293
- result: envelope,
294
- });
295
- return;
296
- }
297
- if (error instanceof CliAuthError) {
298
- emitEvent({
299
- event: 'auth_required',
300
- status: 'error',
301
- message: error.message,
302
- error: agentError,
303
- auth: error.auth,
304
- result: envelope,
305
- });
306
- return;
307
- }
308
- if (error instanceof CliCancellationError) {
309
- emitEvent({
310
- event: 'cancelled',
311
- status: 'error',
312
- message: error.message,
313
- error: agentError,
314
- result: envelope,
315
- });
316
- return;
317
- }
318
- emitEvent({
319
- event: 'error',
320
- status: 'error',
321
- message: agentError.message,
322
- error: agentError,
323
- result: envelope,
324
- });
325
- return;
326
- }
327
- if (options.mode === 'agent' || options.mode === 'json') {
328
- state.terminal = true;
329
- safeWriteStdout(`${JSON.stringify(envelope)}\n`);
330
- }
331
- },
332
- writeCancelled(reason = 'Command cancelled.') {
333
- output.writeError(new CliCancellationError(reason));
334
- },
335
- hasTerminalOutput() {
336
- return state.terminal;
337
- },
338
- close() {
339
- clearHeartbeat(state);
340
- },
341
- };
342
- return output;
343
- }
344
- export function createAgentEnvelope(options) {
345
- const safeData = redactDeep(options.data);
346
- const bounded = boundForAgent(safeData);
347
- const envelope = {
348
- schema_version: AGENT_SCHEMA_VERSION,
349
- command: options.command,
350
- status: options.status,
351
- generated_at: new Date().toISOString(),
352
- ...(options.profile ? { profile: options.profile } : {}),
353
- summary: normalizeSummary(options.summary, options.command, options.status, safeData),
354
- findings: options.findings ?? [],
355
- evidence_refs: options.evidenceRefs ?? [],
356
- recommended_actions: options.recommendedActions ?? [],
357
- next_commands: normalizeNextCommands(options.nextCommands ?? []),
358
- artifacts: options.artifacts ?? [],
359
- warnings: options.warnings ?? inferWarnings(safeData),
360
- errors: options.errors ?? [],
361
- data: bounded.data,
362
- ...(options.meta ? { meta: options.meta } : {}),
363
- ...(bounded.truncation ? { truncation: bounded.truncation } : {}),
364
- };
365
- return redactDeep(envelope);
366
- }
367
- export function createAgentErrorEnvelope(command, error, runId) {
368
- return createAgentEnvelope({
369
- command,
370
- status: 'error',
371
- data: null,
372
- meta: runId ? { run_id: runId } : undefined,
373
- summary: {
374
- text: error.message,
375
- confidence: 'high',
376
- },
377
- errors: [error],
378
- nextCommands: (error.suggested_commands ?? []).map((commandText) => ({
379
- command: commandText,
380
- purpose: 'Suggested recovery command.',
381
- mutability: commandText.includes(' sync') || commandText.startsWith('moda sync') ? 'sync' : 'read',
382
- requires_approval: commandText.includes(' --yes') || commandText.includes(' sync'),
383
- })),
384
- });
385
- }
386
- export function createAgentEvent(command, event, state) {
387
- const stamped = {
388
- schema_version: AGENT_EVENT_SCHEMA_VERSION,
389
- command,
390
- generated_at: new Date().toISOString(),
391
- run_id: state?.runId ?? FALLBACK_RUN_ID,
392
- seq: state ? ++state.seq : ++fallbackSeq,
393
- event_version: event.event_version ?? 1,
394
- ...event,
395
- };
396
- return redactDeep(stamped);
397
- }
398
- export function errorToAgentError(error) {
399
- if (error instanceof CliInputRequiredError) {
400
- return {
401
- code: 'input_required',
402
- message: error.message,
403
- cause: error.usage,
404
- suggested_commands: error.input_request.resume_command
405
- ? [error.input_request.resume_command.join(' ')]
406
- : undefined,
407
- input_request: error.input_request,
408
- blocking: true,
409
- };
410
- }
411
- if (error instanceof CliAuthError) {
412
- const suggested = [error.auth.login_command.join(' ')];
413
- if (error.auth.resume_command)
414
- suggested.push(error.auth.resume_command.join(' '));
415
- return {
416
- code: 'auth_required',
417
- message: error.message,
418
- cause: error.usage,
419
- suggested_commands: suggested,
420
- auth: error.auth,
421
- blocking: true,
422
- };
423
- }
424
- if (error instanceof CliCancellationError) {
425
- return {
426
- code: 'cancelled',
427
- message: error.message,
428
- blocking: true,
429
- };
430
- }
431
- if (error instanceof CliAccessError) {
432
- // Access problem (missing organization or insufficient role), not
433
- // auth-missing: exit stays EXIT_ERROR and no login command is suggested.
434
- return {
435
- code: 'access_denied',
436
- message: error.message,
437
- cause: error.guidance,
438
- blocking: true,
439
- };
440
- }
441
- if (error instanceof Error) {
442
- if (error instanceof CliInputError || error.name === 'ZodError') {
443
- return {
444
- code: 'input_error',
445
- message: error.message,
446
- cause: 'usage' in error && typeof error.usage === 'string' ? error.usage : undefined,
447
- suggested_commands: 'usage' in error && typeof error.usage === 'string'
448
- ? extractSuggestedCommands(error.usage)
449
- : undefined,
450
- blocking: true,
451
- };
452
- }
453
- if (isApiLikeError(error)) {
454
- const statusCode = 'statusCode' in error ? error.statusCode : undefined;
455
- return {
456
- code: 'api_error',
457
- message: error.message,
458
- cause: typeof statusCode === 'number' ? `HTTP ${statusCode}` : undefined,
459
- blocking: true,
460
- };
461
- }
462
- if (isNetworkLikeError(error)) {
463
- return {
464
- code: 'network_error',
465
- message: error.message,
466
- blocking: true,
467
- };
468
- }
469
- return {
470
- code: 'command_error',
471
- message: error.message,
472
- cause: 'usage' in error && typeof error.usage === 'string' ? error.usage : undefined,
473
- suggested_commands: 'usage' in error && typeof error.usage === 'string'
474
- ? extractSuggestedCommands(error.usage)
475
- : undefined,
476
- blocking: true,
477
- };
478
- }
479
- return {
480
- code: 'unknown_error',
481
- message: String(error),
482
- blocking: true,
483
- };
484
- }
485
- function isTerminalEvent(event) {
486
- return event === 'completed' ||
487
- event === 'error' ||
488
- event === 'cancelled' ||
489
- event === 'needs_input' ||
490
- event === 'auth_required';
491
- }
492
- function startHeartbeat(command, state) {
493
- if (state.heartbeat)
494
- return;
495
- const intervalMs = Number(process.env.MODA_HEARTBEAT_MS ?? 10_000);
496
- if (!Number.isFinite(intervalMs) || intervalMs <= 0)
497
- return;
498
- state.heartbeat = setInterval(() => {
499
- if (state.terminal || stdoutClosed)
500
- return;
501
- const now = Date.now();
502
- if (now - state.lastEventAt < intervalMs)
503
- return;
504
- state.lastEventAt = now;
505
- safeWriteStdout(`${JSON.stringify(createAgentEvent(command, {
506
- event: 'heartbeat',
507
- elapsed_ms: now - state.startedAt,
508
- }, state))}\n`);
509
- }, intervalMs);
510
- state.heartbeat.unref();
511
- }
512
- function clearHeartbeat(state) {
513
- if (!state.heartbeat)
514
- return;
515
- clearInterval(state.heartbeat);
516
- state.heartbeat = undefined;
517
- }
518
- function normalizeSummary(summary, command, status, data) {
519
- if (typeof summary === 'string') {
520
- return { text: summary, confidence: 'high' };
521
- }
522
- if (summary?.text) {
523
- return {
524
- text: summary.text,
525
- confidence: summary.confidence ?? 'high',
526
- };
527
- }
528
- return inferSummary(command, status, data);
529
- }
530
- function inferSummary(command, status, data) {
531
- const record = asRecord(data);
532
- if (status === 'error') {
533
- return { text: `${command} failed.`, confidence: 'high' };
534
- }
535
- const summary = asRecord(record?.summary);
536
- if (summary) {
537
- const statusValue = typeof summary.status === 'string' ? summary.status : undefined;
538
- const fail = typeof summary.fail === 'number' ? summary.fail : undefined;
539
- const warning = typeof summary.warning === 'number' ? summary.warning : undefined;
540
- if (statusValue && fail !== undefined && warning !== undefined) {
541
- return {
542
- text: `${command} completed with status ${statusValue}: ${fail} failing checks and ${warning} warnings.`,
543
- confidence: 'high',
544
- };
545
- }
546
- const runtimeAgentCount = typeof summary.runtimeAgentCount === 'number'
547
- ? summary.runtimeAgentCount
548
- : undefined;
549
- if (runtimeAgentCount !== undefined) {
550
- return {
551
- text: `${command} completed. Harness summary includes ${runtimeAgentCount} runtime agent(s).`,
552
- confidence: 'high',
553
- };
554
- }
555
- }
556
- if (typeof record?.status === 'string') {
557
- return { text: `${command} completed with status ${record.status}.`, confidence: 'high' };
558
- }
559
- return { text: `${command} completed.`, confidence: 'medium' };
560
- }
561
- function normalizeNextCommands(commands) {
562
- return commands.map((command) => {
563
- if (typeof command !== 'string')
564
- return command;
565
- return {
566
- command,
567
- purpose: 'Suggested next Moda command.',
568
- mutability: inferMutability(command),
569
- requires_approval: command.includes(' --yes') || command.includes(' sync'),
570
- };
571
- });
572
- }
573
- function inferMutability(command) {
574
- if (/\b(sync|approve|init|install|pull|gen|remove|promote)\b/.test(command)) {
575
- if (/\bsync\b/.test(command))
576
- return 'sync';
577
- return 'write';
578
- }
579
- return 'read';
580
- }
581
- function inferWarnings(data) {
582
- const record = asRecord(data);
583
- const direct = record?.warnings;
584
- if (Array.isArray(direct))
585
- return direct.filter((item) => typeof item === 'string');
586
- const validation = asRecord(record?.validation);
587
- if (Array.isArray(validation?.warnings)) {
588
- return validation.warnings.filter((item) => typeof item === 'string');
589
- }
590
- return [];
591
- }
592
- function boundForAgent(data) {
593
- const maxBytes = Number(process.env.MODA_AGENT_MAX_OUTPUT_BYTES ?? 200_000);
594
- const serialized = JSON.stringify(data);
595
- const bytes = Buffer.byteLength(serialized, 'utf8');
596
- if (bytes <= maxBytes)
597
- return { data };
598
- const preview = serialized.slice(0, Math.max(0, maxBytes - 256));
599
- return {
600
- data: {
601
- truncated: true,
602
- original_bytes: bytes,
603
- max_bytes: maxBytes,
604
- preview,
605
- },
606
- truncation: {
607
- data: {
608
- original_bytes: bytes,
609
- max_bytes: maxBytes,
610
- reason: 'agent_output_byte_limit',
611
- },
612
- },
613
- };
614
- }
615
- function asRecord(value) {
616
- return value && typeof value === 'object' && !Array.isArray(value)
617
- ? value
618
- : undefined;
619
- }
620
- function extractSuggestedCommands(text) {
621
- const commands = [...text.matchAll(/`(moda [^`]+)`/g)].map((match) => match[1]);
622
- return commands.length > 0 ? commands : undefined;
623
- }
624
- //# sourceMappingURL=output.js.map