@moxxy/cli 0.7.2 → 0.8.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/dist/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { z as z$1, defineProvider, definePlugin, defineTool, MoxxyError, writeFileAtomic, asTurnId, defineMode, asPluginId, defineChannel, defineTunnelProvider, createMutex, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, moxxyPath, runSingleShotTurn, bearerTokenMatches, resolveChannelToken, rotateChannelToken, estimateContextTokens as estimateContextTokens$1, readRequestBody, MOXXY_WS_SUBPROTOCOL, defineEmbedder, bearerGuard, tokenFromWsProtocolHeader, skillFrontmatterSchema, asSkillId, getInstallHint, moxxyHome, defineTranscriber, summarizeTokensByModel, migrateModeName, createDeferredPermissionResolver, classifyNetworkError, addModelTotals, ISOLATION_RANK, moxxyPackageSchema, defineCommand, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
3
+ import { z as z$1, defineProvider, definePlugin, defineTool, MoxxyError, writeFileAtomic, asTurnId, defineMode, asPluginId, defineChannel, defineTunnelProvider, spawnCliTunnel, isCliTunnelAvailable, createMutex, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, moxxyPath, zodToJsonSchema, runSingleShotTurn, bearerTokenMatches, resolveChannelToken, rotateChannelToken, estimateContextTokens as estimateContextTokens$1, readRequestBody, MOXXY_WS_SUBPROTOCOL, defineEmbedder, bearerGuard, tokenFromWsProtocolHeader, skillFrontmatterSchema, asSkillId, getInstallHint, moxxyHome, defineTranscriber, summarizeTokensByModel, migrateModeName, createDeferredPermissionResolver, classifyNetworkError, addModelTotals, ISOLATION_RANK, moxxyPackageSchema, defineCommand, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
4
4
  import * as fs27 from 'fs';
5
5
  import fs27__default, { existsSync, promises, ReadStream, readFileSync, statSync, readdirSync, mkdirSync, writeFileSync, unlinkSync, renameSync, watch, createReadStream } from 'fs';
6
6
  import * as path3 from 'path';
@@ -20,7 +20,7 @@ import * as z4mini from 'zod/v4-mini';
20
20
  import * as z24 from 'zod/v4';
21
21
  import V3, { stdin, stdout, env, cwd } from 'process';
22
22
  import tty, { ReadStream as ReadStream$1 } from 'tty';
23
- import { once, EventEmitter } from 'events';
23
+ import { EventEmitter } from 'events';
24
24
  import { Buffer as Buffer$1 } from 'buffer';
25
25
  import { readFile, mkdir, open, rm, stat, rename as rename$1, unlink, writeFile } from 'fs/promises';
26
26
  import { spawn, spawnSync } from 'child_process';
@@ -433,18 +433,29 @@ var init_tools = __esm({
433
433
  }
434
434
  });
435
435
 
436
- // ../core/dist/subagents/spawn.js
437
- function createSubagentSpawner(rt3) {
438
- return {
439
- async spawn(spec) {
440
- return runOne(rt3, spec);
441
- },
442
- async spawnAll(specs) {
443
- return Promise.all(specs.map((s2) => runOne(rt3, s2)));
444
- }
445
- };
436
+ // ../core/dist/subagents/registry.js
437
+ function registerRetainedChild(session) {
438
+ retained.set(String(session.childSessionId), session);
439
+ }
440
+ function getRetainedChild(childSessionId) {
441
+ return retained.get(String(childSessionId));
446
442
  }
447
- async function runOne(rt3, spec) {
443
+ function releaseRetainedChild(childSessionId) {
444
+ retained.delete(String(childSessionId));
445
+ }
446
+ function clearRetainedChildren() {
447
+ retained.clear();
448
+ }
449
+ var retained;
450
+ var init_registry = __esm({
451
+ "../core/dist/subagents/registry.js"() {
452
+ retained = /* @__PURE__ */ new Map();
453
+ }
454
+ });
455
+
456
+ // ../core/dist/subagents/run-child.js
457
+ async function runChildTurn(args) {
458
+ const { rt: rt3, spec, retainSession } = args;
448
459
  const { parentSession, parentTurnId } = rt3;
449
460
  const childSessionId = newSessionId();
450
461
  const childTurnId = newTurnId();
@@ -456,6 +467,73 @@ async function runOne(rt3, spec) {
456
467
  const { strategy, strategyName } = resolved;
457
468
  const toolRegistry = spec.allowedTools && spec.allowedTools.length > 0 ? buildFilteredToolRegistry(parentSession.tools, new Set(spec.allowedTools)) : parentSession.tools;
458
469
  const childLog = new EventLog();
470
+ const spawner = createSubagentSpawner(rt3);
471
+ const childCtx = buildChildContext(rt3, spec, childSessionId, childTurnId, toolRegistry, childLog, spawner);
472
+ const capture = await executeChildLoop({
473
+ rt: rt3,
474
+ spec,
475
+ label: label3,
476
+ childSessionId,
477
+ childTurnId,
478
+ childLog,
479
+ childCtx,
480
+ strategy,
481
+ strategyName,
482
+ emitCompleted: !retainSession
483
+ });
484
+ if (retainSession) {
485
+ registerRetainedChild({
486
+ label: label3,
487
+ childSessionId,
488
+ childTurnId,
489
+ childLog,
490
+ childCtx,
491
+ spec,
492
+ strategy,
493
+ strategyName,
494
+ parentSession,
495
+ parentTurnId
496
+ });
497
+ }
498
+ return capture.result;
499
+ }
500
+ async function continueChildTurn(args) {
501
+ const retained2 = getRetainedChild(args.childSessionId);
502
+ if (!retained2) {
503
+ throw new Error(`no retained subagent session for "${String(args.childSessionId)}"`);
504
+ }
505
+ await retained2.childLog.append({
506
+ type: "user_prompt",
507
+ sessionId: retained2.childSessionId,
508
+ turnId: retained2.childTurnId,
509
+ source: "user",
510
+ text: args.prompt
511
+ });
512
+ const rt3 = {
513
+ parentSession: retained2.parentSession,
514
+ parentTurnId: retained2.parentTurnId,
515
+ parentSignal: retained2.childCtx.signal,
516
+ parentModel: retained2.childCtx.model
517
+ };
518
+ const capture = await executeChildLoop({
519
+ rt: rt3,
520
+ spec: retained2.spec,
521
+ label: args.label ?? retained2.label,
522
+ childSessionId: retained2.childSessionId,
523
+ childTurnId: retained2.childTurnId,
524
+ childLog: retained2.childLog,
525
+ childCtx: retained2.childCtx,
526
+ strategy: retained2.strategy,
527
+ strategyName: retained2.strategyName,
528
+ emitCompleted: true,
529
+ skipStartEvent: true
530
+ });
531
+ releaseRetainedChild(args.childSessionId);
532
+ return capture.result;
533
+ }
534
+ async function executeChildLoop(args) {
535
+ const { rt: rt3, spec, label: label3, childSessionId, childTurnId, childLog, childCtx, strategy, strategyName, emitCompleted, skipStartEvent } = args;
536
+ const { parentSession, parentTurnId } = rt3;
459
537
  const capture = { text: "", stopReason: "end_turn", error: null };
460
538
  const unsubCapture = childLog.subscribe((e3) => {
461
539
  if (e3.type === "assistant_message") {
@@ -468,15 +546,16 @@ async function runOne(rt3, spec) {
468
546
  }
469
547
  });
470
548
  const unsubStream = childLog.subscribe((childEvt) => streamChildEventToParent(parentSession, parentTurnId, label3, childSessionId, childEvt));
471
- await emitSubagentStart(parentSession, parentTurnId, label3, childSessionId, spec, strategyName);
472
- await childLog.append({
473
- type: "user_prompt",
474
- sessionId: childSessionId,
475
- turnId: childTurnId,
476
- source: "user",
477
- text: spec.prompt
478
- });
479
- const childCtx = buildChildContext(rt3, spec, childSessionId, childTurnId, toolRegistry, childLog);
549
+ if (!skipStartEvent) {
550
+ await emitSubagentStart(parentSession, parentTurnId, label3, childSessionId, spec, strategyName);
551
+ await childLog.append({
552
+ type: "user_prompt",
553
+ sessionId: childSessionId,
554
+ turnId: childTurnId,
555
+ source: "user",
556
+ text: spec.prompt
557
+ });
558
+ }
480
559
  try {
481
560
  for await (const _2 of strategy.run(childCtx)) {
482
561
  void _2;
@@ -494,8 +573,10 @@ async function runOne(rt3, spec) {
494
573
  stopReason: capture.error ? "error" : capture.stopReason,
495
574
  ...capture.error ? { error: { message: capture.error } } : {}
496
575
  };
497
- await emitSubagentCompleted(parentSession, parentTurnId, label3, childSessionId, capture.text, result.stopReason, capture.error);
498
- return result;
576
+ if (emitCompleted) {
577
+ await emitSubagentCompleted(parentSession, parentTurnId, label3, childSessionId, capture.text, result.stopReason, capture.error);
578
+ }
579
+ return { result };
499
580
  }
500
581
  async function resolveStrategy(parentSession, parentTurnId, label3, childSessionId, spec, requestedStrategy) {
501
582
  const exact = parentSession.modes.list().find((s2) => s2.name === requestedStrategy);
@@ -519,7 +600,7 @@ async function resolveStrategy(parentSession, parentTurnId, label3, childSession
519
600
  }
520
601
  };
521
602
  }
522
- function buildChildContext(rt3, spec, childSessionId, childTurnId, toolRegistry, childLog) {
603
+ function buildChildContext(rt3, spec, childSessionId, childTurnId, toolRegistry, childLog, spawner) {
523
604
  const { parentSession, parentSignal, parentModel } = rt3;
524
605
  return {
525
606
  sessionId: childSessionId,
@@ -543,20 +624,40 @@ function buildChildContext(rt3, spec, childSessionId, childTurnId, toolRegistry,
543
624
  signal: parentSignal,
544
625
  // child cancels when parent cancels
545
626
  maxIterations: spec.maxIterations ?? 50,
546
- subagents: createSubagentSpawner({
547
- ...rt3,
548
- parentTurnId: childTurnId
549
- // grand-children attach to this child's turn
550
- }),
627
+ subagents: spawner,
551
628
  emit: (event) => childLog.append(event)
552
629
  };
553
630
  }
554
- var init_spawn = __esm({
555
- "../core/dist/subagents/spawn.js"() {
631
+ function createSubagentSpawner(rt3) {
632
+ return {
633
+ async spawn(spec) {
634
+ return runChildTurn({ rt: rt3, spec, retainSession: spec.retainSession === true });
635
+ },
636
+ async spawnAll(specs) {
637
+ return Promise.all(specs.map((s2) => runChildTurn({ rt: rt3, spec: s2, retainSession: s2.retainSession === true })));
638
+ },
639
+ async continue(args) {
640
+ return continueChildTurn(args);
641
+ },
642
+ release(childSessionId) {
643
+ releaseRetainedChild(childSessionId);
644
+ }
645
+ };
646
+ }
647
+ var init_run_child = __esm({
648
+ "../core/dist/subagents/run-child.js"() {
556
649
  init_log();
557
650
  init_factory();
558
651
  init_events();
559
652
  init_tools();
653
+ init_registry();
654
+ }
655
+ });
656
+
657
+ // ../core/dist/subagents/spawn.js
658
+ var init_spawn = __esm({
659
+ "../core/dist/subagents/spawn.js"() {
660
+ init_run_child();
560
661
  }
561
662
  });
562
663
 
@@ -564,6 +665,7 @@ var init_spawn = __esm({
564
665
  var init_subagents = __esm({
565
666
  "../core/dist/subagents.js"() {
566
667
  init_spawn();
668
+ init_registry();
567
669
  }
568
670
  });
569
671
 
@@ -2153,8 +2255,8 @@ var init_tools2 = __esm({
2153
2255
  const parseResult = tool.inputSchema.safeParse(input);
2154
2256
  if (!parseResult.success) {
2155
2257
  const issues = parseResult.error.issues.map((iss) => {
2156
- const path60 = iss.path.length ? iss.path.join(".") : "(root)";
2157
- return `${path60}: ${iss.message}`;
2258
+ const path61 = iss.path.length ? iss.path.join(".") : "(root)";
2259
+ return `${path61}: ${iss.message}`;
2158
2260
  }).join("; ");
2159
2261
  throw new Error(`Invalid input for ${name}: ${issues}`);
2160
2262
  }
@@ -2879,6 +2981,7 @@ var init_session = __esm({
2879
2981
  init_engine();
2880
2982
  init_resolvers();
2881
2983
  init_logger();
2984
+ init_registry();
2882
2985
  Session = class {
2883
2986
  id;
2884
2987
  cwd;
@@ -3053,6 +3156,7 @@ var init_session = __esm({
3053
3156
  try {
3054
3157
  await this.dispatcher.dispatchShutdown(this.appContext());
3055
3158
  } finally {
3159
+ clearRetainedChildren();
3056
3160
  this.abort(reason);
3057
3161
  }
3058
3162
  }
@@ -7986,6 +8090,7 @@ __export(dist_exports, {
7986
8090
  autoAllowResolver: () => autoAllowResolver,
7987
8091
  buildSkillIndexPrompt: () => buildSkillIndexPrompt,
7988
8092
  buildSynthesizeSkillPlugin: () => buildSynthesizeSkillPlugin,
8093
+ clearRetainedChildren: () => clearRetainedChildren,
7989
8094
  clearUsageStats: () => clearUsageStats,
7990
8095
  collectTurn: () => collectTurn,
7991
8096
  countNodes: () => countNodes,
@@ -8142,17 +8247,17 @@ var require_visit = __commonJS({
8142
8247
  visit.BREAK = BREAK;
8143
8248
  visit.SKIP = SKIP;
8144
8249
  visit.REMOVE = REMOVE;
8145
- function visit_(key, node, visitor, path60) {
8146
- const ctrl = callVisitor(key, node, visitor, path60);
8250
+ function visit_(key, node, visitor, path61) {
8251
+ const ctrl = callVisitor(key, node, visitor, path61);
8147
8252
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
8148
- replaceNode(key, path60, ctrl);
8149
- return visit_(key, ctrl, visitor, path60);
8253
+ replaceNode(key, path61, ctrl);
8254
+ return visit_(key, ctrl, visitor, path61);
8150
8255
  }
8151
8256
  if (typeof ctrl !== "symbol") {
8152
8257
  if (identity.isCollection(node)) {
8153
- path60 = Object.freeze(path60.concat(node));
8258
+ path61 = Object.freeze(path61.concat(node));
8154
8259
  for (let i2 = 0; i2 < node.items.length; ++i2) {
8155
- const ci = visit_(i2, node.items[i2], visitor, path60);
8260
+ const ci = visit_(i2, node.items[i2], visitor, path61);
8156
8261
  if (typeof ci === "number")
8157
8262
  i2 = ci - 1;
8158
8263
  else if (ci === BREAK)
@@ -8163,13 +8268,13 @@ var require_visit = __commonJS({
8163
8268
  }
8164
8269
  }
8165
8270
  } else if (identity.isPair(node)) {
8166
- path60 = Object.freeze(path60.concat(node));
8167
- const ck = visit_("key", node.key, visitor, path60);
8271
+ path61 = Object.freeze(path61.concat(node));
8272
+ const ck = visit_("key", node.key, visitor, path61);
8168
8273
  if (ck === BREAK)
8169
8274
  return BREAK;
8170
8275
  else if (ck === REMOVE)
8171
8276
  node.key = null;
8172
- const cv = visit_("value", node.value, visitor, path60);
8277
+ const cv = visit_("value", node.value, visitor, path61);
8173
8278
  if (cv === BREAK)
8174
8279
  return BREAK;
8175
8280
  else if (cv === REMOVE)
@@ -8190,17 +8295,17 @@ var require_visit = __commonJS({
8190
8295
  visitAsync.BREAK = BREAK;
8191
8296
  visitAsync.SKIP = SKIP;
8192
8297
  visitAsync.REMOVE = REMOVE;
8193
- async function visitAsync_(key, node, visitor, path60) {
8194
- const ctrl = await callVisitor(key, node, visitor, path60);
8298
+ async function visitAsync_(key, node, visitor, path61) {
8299
+ const ctrl = await callVisitor(key, node, visitor, path61);
8195
8300
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
8196
- replaceNode(key, path60, ctrl);
8197
- return visitAsync_(key, ctrl, visitor, path60);
8301
+ replaceNode(key, path61, ctrl);
8302
+ return visitAsync_(key, ctrl, visitor, path61);
8198
8303
  }
8199
8304
  if (typeof ctrl !== "symbol") {
8200
8305
  if (identity.isCollection(node)) {
8201
- path60 = Object.freeze(path60.concat(node));
8306
+ path61 = Object.freeze(path61.concat(node));
8202
8307
  for (let i2 = 0; i2 < node.items.length; ++i2) {
8203
- const ci = await visitAsync_(i2, node.items[i2], visitor, path60);
8308
+ const ci = await visitAsync_(i2, node.items[i2], visitor, path61);
8204
8309
  if (typeof ci === "number")
8205
8310
  i2 = ci - 1;
8206
8311
  else if (ci === BREAK)
@@ -8211,13 +8316,13 @@ var require_visit = __commonJS({
8211
8316
  }
8212
8317
  }
8213
8318
  } else if (identity.isPair(node)) {
8214
- path60 = Object.freeze(path60.concat(node));
8215
- const ck = await visitAsync_("key", node.key, visitor, path60);
8319
+ path61 = Object.freeze(path61.concat(node));
8320
+ const ck = await visitAsync_("key", node.key, visitor, path61);
8216
8321
  if (ck === BREAK)
8217
8322
  return BREAK;
8218
8323
  else if (ck === REMOVE)
8219
8324
  node.key = null;
8220
- const cv = await visitAsync_("value", node.value, visitor, path60);
8325
+ const cv = await visitAsync_("value", node.value, visitor, path61);
8221
8326
  if (cv === BREAK)
8222
8327
  return BREAK;
8223
8328
  else if (cv === REMOVE)
@@ -8244,23 +8349,23 @@ var require_visit = __commonJS({
8244
8349
  }
8245
8350
  return visitor;
8246
8351
  }
8247
- function callVisitor(key, node, visitor, path60) {
8352
+ function callVisitor(key, node, visitor, path61) {
8248
8353
  if (typeof visitor === "function")
8249
- return visitor(key, node, path60);
8354
+ return visitor(key, node, path61);
8250
8355
  if (identity.isMap(node))
8251
- return visitor.Map?.(key, node, path60);
8356
+ return visitor.Map?.(key, node, path61);
8252
8357
  if (identity.isSeq(node))
8253
- return visitor.Seq?.(key, node, path60);
8358
+ return visitor.Seq?.(key, node, path61);
8254
8359
  if (identity.isPair(node))
8255
- return visitor.Pair?.(key, node, path60);
8360
+ return visitor.Pair?.(key, node, path61);
8256
8361
  if (identity.isScalar(node))
8257
- return visitor.Scalar?.(key, node, path60);
8362
+ return visitor.Scalar?.(key, node, path61);
8258
8363
  if (identity.isAlias(node))
8259
- return visitor.Alias?.(key, node, path60);
8364
+ return visitor.Alias?.(key, node, path61);
8260
8365
  return void 0;
8261
8366
  }
8262
- function replaceNode(key, path60, node) {
8263
- const parent = path60[path60.length - 1];
8367
+ function replaceNode(key, path61, node) {
8368
+ const parent = path61[path61.length - 1];
8264
8369
  if (identity.isCollection(parent)) {
8265
8370
  parent.items[key] = node;
8266
8371
  } else if (identity.isPair(parent)) {
@@ -8861,10 +8966,10 @@ var require_Collection = __commonJS({
8861
8966
  var createNode2 = require_createNode();
8862
8967
  var identity = require_identity();
8863
8968
  var Node = require_Node();
8864
- function collectionFromPath(schema, path60, value) {
8969
+ function collectionFromPath(schema, path61, value) {
8865
8970
  let v3 = value;
8866
- for (let i2 = path60.length - 1; i2 >= 0; --i2) {
8867
- const k3 = path60[i2];
8971
+ for (let i2 = path61.length - 1; i2 >= 0; --i2) {
8972
+ const k3 = path61[i2];
8868
8973
  if (typeof k3 === "number" && Number.isInteger(k3) && k3 >= 0) {
8869
8974
  const a2 = [];
8870
8975
  a2[k3] = v3;
@@ -8883,7 +8988,7 @@ var require_Collection = __commonJS({
8883
8988
  sourceObjects: /* @__PURE__ */ new Map()
8884
8989
  });
8885
8990
  }
8886
- var isEmptyPath = (path60) => path60 == null || typeof path60 === "object" && !!path60[Symbol.iterator]().next().done;
8991
+ var isEmptyPath = (path61) => path61 == null || typeof path61 === "object" && !!path61[Symbol.iterator]().next().done;
8887
8992
  var Collection = class extends Node.NodeBase {
8888
8993
  constructor(type, schema) {
8889
8994
  super(type);
@@ -8913,11 +9018,11 @@ var require_Collection = __commonJS({
8913
9018
  * be a Pair instance or a `{ key, value }` object, which may not have a key
8914
9019
  * that already exists in the map.
8915
9020
  */
8916
- addIn(path60, value) {
8917
- if (isEmptyPath(path60))
9021
+ addIn(path61, value) {
9022
+ if (isEmptyPath(path61))
8918
9023
  this.add(value);
8919
9024
  else {
8920
- const [key, ...rest] = path60;
9025
+ const [key, ...rest] = path61;
8921
9026
  const node = this.get(key, true);
8922
9027
  if (identity.isCollection(node))
8923
9028
  node.addIn(rest, value);
@@ -8931,8 +9036,8 @@ var require_Collection = __commonJS({
8931
9036
  * Removes a value from the collection.
8932
9037
  * @returns `true` if the item was found and removed.
8933
9038
  */
8934
- deleteIn(path60) {
8935
- const [key, ...rest] = path60;
9039
+ deleteIn(path61) {
9040
+ const [key, ...rest] = path61;
8936
9041
  if (rest.length === 0)
8937
9042
  return this.delete(key);
8938
9043
  const node = this.get(key, true);
@@ -8946,8 +9051,8 @@ var require_Collection = __commonJS({
8946
9051
  * scalar values from their surrounding node; to disable set `keepScalar` to
8947
9052
  * `true` (collections are always returned intact).
8948
9053
  */
8949
- getIn(path60, keepScalar) {
8950
- const [key, ...rest] = path60;
9054
+ getIn(path61, keepScalar) {
9055
+ const [key, ...rest] = path61;
8951
9056
  const node = this.get(key, true);
8952
9057
  if (rest.length === 0)
8953
9058
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -8965,8 +9070,8 @@ var require_Collection = __commonJS({
8965
9070
  /**
8966
9071
  * Checks if the collection includes a value with the key `key`.
8967
9072
  */
8968
- hasIn(path60) {
8969
- const [key, ...rest] = path60;
9073
+ hasIn(path61) {
9074
+ const [key, ...rest] = path61;
8970
9075
  if (rest.length === 0)
8971
9076
  return this.has(key);
8972
9077
  const node = this.get(key, true);
@@ -8976,8 +9081,8 @@ var require_Collection = __commonJS({
8976
9081
  * Sets a value in this collection. For `!!set`, `value` needs to be a
8977
9082
  * boolean to add/remove the item from the set.
8978
9083
  */
8979
- setIn(path60, value) {
8980
- const [key, ...rest] = path60;
9084
+ setIn(path61, value) {
9085
+ const [key, ...rest] = path61;
8981
9086
  if (rest.length === 0) {
8982
9087
  this.set(key, value);
8983
9088
  } else {
@@ -11457,9 +11562,9 @@ var require_Document = __commonJS({
11457
11562
  this.contents.add(value);
11458
11563
  }
11459
11564
  /** Adds a value to the document. */
11460
- addIn(path60, value) {
11565
+ addIn(path61, value) {
11461
11566
  if (assertCollection(this.contents))
11462
- this.contents.addIn(path60, value);
11567
+ this.contents.addIn(path61, value);
11463
11568
  }
11464
11569
  /**
11465
11570
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -11534,14 +11639,14 @@ var require_Document = __commonJS({
11534
11639
  * Removes a value from the document.
11535
11640
  * @returns `true` if the item was found and removed.
11536
11641
  */
11537
- deleteIn(path60) {
11538
- if (Collection.isEmptyPath(path60)) {
11642
+ deleteIn(path61) {
11643
+ if (Collection.isEmptyPath(path61)) {
11539
11644
  if (this.contents == null)
11540
11645
  return false;
11541
11646
  this.contents = null;
11542
11647
  return true;
11543
11648
  }
11544
- return assertCollection(this.contents) ? this.contents.deleteIn(path60) : false;
11649
+ return assertCollection(this.contents) ? this.contents.deleteIn(path61) : false;
11545
11650
  }
11546
11651
  /**
11547
11652
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -11556,10 +11661,10 @@ var require_Document = __commonJS({
11556
11661
  * scalar values from their surrounding node; to disable set `keepScalar` to
11557
11662
  * `true` (collections are always returned intact).
11558
11663
  */
11559
- getIn(path60, keepScalar) {
11560
- if (Collection.isEmptyPath(path60))
11664
+ getIn(path61, keepScalar) {
11665
+ if (Collection.isEmptyPath(path61))
11561
11666
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
11562
- return identity.isCollection(this.contents) ? this.contents.getIn(path60, keepScalar) : void 0;
11667
+ return identity.isCollection(this.contents) ? this.contents.getIn(path61, keepScalar) : void 0;
11563
11668
  }
11564
11669
  /**
11565
11670
  * Checks if the document includes a value with the key `key`.
@@ -11570,10 +11675,10 @@ var require_Document = __commonJS({
11570
11675
  /**
11571
11676
  * Checks if the document includes a value at `path`.
11572
11677
  */
11573
- hasIn(path60) {
11574
- if (Collection.isEmptyPath(path60))
11678
+ hasIn(path61) {
11679
+ if (Collection.isEmptyPath(path61))
11575
11680
  return this.contents !== void 0;
11576
- return identity.isCollection(this.contents) ? this.contents.hasIn(path60) : false;
11681
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path61) : false;
11577
11682
  }
11578
11683
  /**
11579
11684
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -11590,13 +11695,13 @@ var require_Document = __commonJS({
11590
11695
  * Sets a value in this document. For `!!set`, `value` needs to be a
11591
11696
  * boolean to add/remove the item from the set.
11592
11697
  */
11593
- setIn(path60, value) {
11594
- if (Collection.isEmptyPath(path60)) {
11698
+ setIn(path61, value) {
11699
+ if (Collection.isEmptyPath(path61)) {
11595
11700
  this.contents = value;
11596
11701
  } else if (this.contents == null) {
11597
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path60), value);
11702
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path61), value);
11598
11703
  } else if (assertCollection(this.contents)) {
11599
- this.contents.setIn(path60, value);
11704
+ this.contents.setIn(path61, value);
11600
11705
  }
11601
11706
  }
11602
11707
  /**
@@ -13536,9 +13641,9 @@ var require_cst_visit = __commonJS({
13536
13641
  visit.BREAK = BREAK;
13537
13642
  visit.SKIP = SKIP;
13538
13643
  visit.REMOVE = REMOVE;
13539
- visit.itemAtPath = (cst, path60) => {
13644
+ visit.itemAtPath = (cst, path61) => {
13540
13645
  let item = cst;
13541
- for (const [field, index] of path60) {
13646
+ for (const [field, index] of path61) {
13542
13647
  const tok = item?.[field];
13543
13648
  if (tok && "items" in tok) {
13544
13649
  item = tok.items[index];
@@ -13547,23 +13652,23 @@ var require_cst_visit = __commonJS({
13547
13652
  }
13548
13653
  return item;
13549
13654
  };
13550
- visit.parentCollection = (cst, path60) => {
13551
- const parent = visit.itemAtPath(cst, path60.slice(0, -1));
13552
- const field = path60[path60.length - 1][0];
13655
+ visit.parentCollection = (cst, path61) => {
13656
+ const parent = visit.itemAtPath(cst, path61.slice(0, -1));
13657
+ const field = path61[path61.length - 1][0];
13553
13658
  const coll = parent?.[field];
13554
13659
  if (coll && "items" in coll)
13555
13660
  return coll;
13556
13661
  throw new Error("Parent collection not found");
13557
13662
  };
13558
- function _visit(path60, item, visitor) {
13559
- let ctrl = visitor(item, path60);
13663
+ function _visit(path61, item, visitor) {
13664
+ let ctrl = visitor(item, path61);
13560
13665
  if (typeof ctrl === "symbol")
13561
13666
  return ctrl;
13562
13667
  for (const field of ["key", "value"]) {
13563
13668
  const token = item[field];
13564
13669
  if (token && "items" in token) {
13565
13670
  for (let i2 = 0; i2 < token.items.length; ++i2) {
13566
- const ci = _visit(Object.freeze(path60.concat([[field, i2]])), token.items[i2], visitor);
13671
+ const ci = _visit(Object.freeze(path61.concat([[field, i2]])), token.items[i2], visitor);
13567
13672
  if (typeof ci === "number")
13568
13673
  i2 = ci - 1;
13569
13674
  else if (ci === BREAK)
@@ -13574,10 +13679,10 @@ var require_cst_visit = __commonJS({
13574
13679
  }
13575
13680
  }
13576
13681
  if (typeof ctrl === "function" && field === "key")
13577
- ctrl = ctrl(item, path60);
13682
+ ctrl = ctrl(item, path61);
13578
13683
  }
13579
13684
  }
13580
- return typeof ctrl === "function" ? ctrl(item, path60) : ctrl;
13685
+ return typeof ctrl === "function" ? ctrl(item, path61) : ctrl;
13581
13686
  }
13582
13687
  exports.visit = visit;
13583
13688
  }
@@ -14875,14 +14980,14 @@ var require_parser = __commonJS({
14875
14980
  case "scalar":
14876
14981
  case "single-quoted-scalar":
14877
14982
  case "double-quoted-scalar": {
14878
- const fs43 = this.flowScalar(this.type);
14983
+ const fs44 = this.flowScalar(this.type);
14879
14984
  if (atNextItem || it4.value) {
14880
- map2.items.push({ start, key: fs43, sep: [] });
14985
+ map2.items.push({ start, key: fs44, sep: [] });
14881
14986
  this.onKeyLine = true;
14882
14987
  } else if (it4.sep) {
14883
- this.stack.push(fs43);
14988
+ this.stack.push(fs44);
14884
14989
  } else {
14885
- Object.assign(it4, { key: fs43, sep: [] });
14990
+ Object.assign(it4, { key: fs44, sep: [] });
14886
14991
  this.onKeyLine = true;
14887
14992
  }
14888
14993
  return;
@@ -15010,13 +15115,13 @@ var require_parser = __commonJS({
15010
15115
  case "scalar":
15011
15116
  case "single-quoted-scalar":
15012
15117
  case "double-quoted-scalar": {
15013
- const fs43 = this.flowScalar(this.type);
15118
+ const fs44 = this.flowScalar(this.type);
15014
15119
  if (!it4 || it4.value)
15015
- fc.items.push({ start: [], key: fs43, sep: [] });
15120
+ fc.items.push({ start: [], key: fs44, sep: [] });
15016
15121
  else if (it4.sep)
15017
- this.stack.push(fs43);
15122
+ this.stack.push(fs44);
15018
15123
  else
15019
- Object.assign(it4, { key: fs43, sep: [] });
15124
+ Object.assign(it4, { key: fs44, sep: [] });
15020
15125
  return;
15021
15126
  }
15022
15127
  case "flow-map-end":
@@ -16299,14 +16404,14 @@ var require_url_state_machine = __commonJS({
16299
16404
  return url2.replace(/\u0009|\u000A|\u000D/g, "");
16300
16405
  }
16301
16406
  function shortenPath(url2) {
16302
- const path60 = url2.path;
16303
- if (path60.length === 0) {
16407
+ const path61 = url2.path;
16408
+ if (path61.length === 0) {
16304
16409
  return;
16305
16410
  }
16306
- if (url2.scheme === "file" && path60.length === 1 && isNormalizedWindowsDriveLetter(path60[0])) {
16411
+ if (url2.scheme === "file" && path61.length === 1 && isNormalizedWindowsDriveLetter(path61[0])) {
16307
16412
  return;
16308
16413
  }
16309
- path60.pop();
16414
+ path61.pop();
16310
16415
  }
16311
16416
  function includesCredentials(url2) {
16312
16417
  return url2.username !== "" || url2.password !== "";
@@ -25529,14 +25634,14 @@ __export(fileFromPath_exports, {
25529
25634
  fileFromPathSync: () => fileFromPathSync,
25530
25635
  isFile: () => isFile
25531
25636
  });
25532
- function createFileFromPath(path60, { mtimeMs, size }, filenameOrOptions, options = {}) {
25637
+ function createFileFromPath(path61, { mtimeMs, size }, filenameOrOptions, options = {}) {
25533
25638
  let filename;
25534
25639
  if (isPlainObject_default2(filenameOrOptions)) {
25535
25640
  [options, filename] = [filenameOrOptions, void 0];
25536
25641
  } else {
25537
25642
  filename = filenameOrOptions;
25538
25643
  }
25539
- const file = new FileFromPath({ path: path60, size, lastModified: mtimeMs });
25644
+ const file = new FileFromPath({ path: path61, size, lastModified: mtimeMs });
25540
25645
  if (!filename) {
25541
25646
  filename = file.name;
25542
25647
  }
@@ -25545,13 +25650,13 @@ function createFileFromPath(path60, { mtimeMs, size }, filenameOrOptions, option
25545
25650
  lastModified: file.lastModified
25546
25651
  });
25547
25652
  }
25548
- function fileFromPathSync(path60, filenameOrOptions, options = {}) {
25549
- const stats = statSync(path60);
25550
- return createFileFromPath(path60, stats, filenameOrOptions, options);
25653
+ function fileFromPathSync(path61, filenameOrOptions, options = {}) {
25654
+ const stats = statSync(path61);
25655
+ return createFileFromPath(path61, stats, filenameOrOptions, options);
25551
25656
  }
25552
- async function fileFromPath2(path60, filenameOrOptions, options) {
25553
- const stats = await promises.stat(path60);
25554
- return createFileFromPath(path60, stats, filenameOrOptions, options);
25657
+ async function fileFromPath2(path61, filenameOrOptions, options) {
25658
+ const stats = await promises.stat(path61);
25659
+ return createFileFromPath(path61, stats, filenameOrOptions, options);
25555
25660
  }
25556
25661
  var import_node_domexception, __classPrivateFieldSet4, __classPrivateFieldGet5, _FileFromPath_path, _FileFromPath_start, MESSAGE, FileFromPath;
25557
25662
  var init_fileFromPath = __esm({
@@ -41683,8 +41788,8 @@ var require_utils2 = __commonJS({
41683
41788
  }
41684
41789
  return ind;
41685
41790
  }
41686
- function removeDotSegments(path60) {
41687
- let input = path60;
41791
+ function removeDotSegments(path61) {
41792
+ let input = path61;
41688
41793
  const output = [];
41689
41794
  let nextSlash = -1;
41690
41795
  let len = 0;
@@ -41935,8 +42040,8 @@ var require_schemes = __commonJS({
41935
42040
  wsComponent.secure = void 0;
41936
42041
  }
41937
42042
  if (wsComponent.resourceName) {
41938
- const [path60, query] = wsComponent.resourceName.split("?");
41939
- wsComponent.path = path60 && path60 !== "/" ? path60 : void 0;
42043
+ const [path61, query] = wsComponent.resourceName.split("?");
42044
+ wsComponent.path = path61 && path61 !== "/" ? path61 : void 0;
41940
42045
  wsComponent.query = query;
41941
42046
  wsComponent.resourceName = void 0;
41942
42047
  }
@@ -45283,12 +45388,12 @@ var require_dist2 = __commonJS({
45283
45388
  throw new Error(`Unknown format "${name}"`);
45284
45389
  return f3;
45285
45390
  };
45286
- function addFormats(ajv, list, fs43, exportName) {
45391
+ function addFormats(ajv, list, fs44, exportName) {
45287
45392
  var _a3;
45288
45393
  var _b;
45289
45394
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
45290
45395
  for (const f3 of list)
45291
- ajv.addFormat(f3, fs43[f3]);
45396
+ ajv.addFormat(f3, fs44[f3]);
45292
45397
  }
45293
45398
  module.exports = exports = formatsPlugin;
45294
45399
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -46093,8 +46198,8 @@ var require_windows = __commonJS({
46093
46198
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js"(exports, module) {
46094
46199
  module.exports = isexe;
46095
46200
  isexe.sync = sync;
46096
- var fs43 = __require("fs");
46097
- function checkPathExt(path60, options) {
46201
+ var fs44 = __require("fs");
46202
+ function checkPathExt(path61, options) {
46098
46203
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
46099
46204
  if (!pathext) {
46100
46205
  return true;
@@ -46105,25 +46210,25 @@ var require_windows = __commonJS({
46105
46210
  }
46106
46211
  for (var i2 = 0; i2 < pathext.length; i2++) {
46107
46212
  var p3 = pathext[i2].toLowerCase();
46108
- if (p3 && path60.substr(-p3.length).toLowerCase() === p3) {
46213
+ if (p3 && path61.substr(-p3.length).toLowerCase() === p3) {
46109
46214
  return true;
46110
46215
  }
46111
46216
  }
46112
46217
  return false;
46113
46218
  }
46114
- function checkStat(stat2, path60, options) {
46219
+ function checkStat(stat2, path61, options) {
46115
46220
  if (!stat2.isSymbolicLink() && !stat2.isFile()) {
46116
46221
  return false;
46117
46222
  }
46118
- return checkPathExt(path60, options);
46223
+ return checkPathExt(path61, options);
46119
46224
  }
46120
- function isexe(path60, options, cb) {
46121
- fs43.stat(path60, function(er2, stat2) {
46122
- cb(er2, er2 ? false : checkStat(stat2, path60, options));
46225
+ function isexe(path61, options, cb) {
46226
+ fs44.stat(path61, function(er2, stat2) {
46227
+ cb(er2, er2 ? false : checkStat(stat2, path61, options));
46123
46228
  });
46124
46229
  }
46125
- function sync(path60, options) {
46126
- return checkStat(fs43.statSync(path60), path60, options);
46230
+ function sync(path61, options) {
46231
+ return checkStat(fs44.statSync(path61), path61, options);
46127
46232
  }
46128
46233
  }
46129
46234
  });
@@ -46133,14 +46238,14 @@ var require_mode = __commonJS({
46133
46238
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js"(exports, module) {
46134
46239
  module.exports = isexe;
46135
46240
  isexe.sync = sync;
46136
- var fs43 = __require("fs");
46137
- function isexe(path60, options, cb) {
46138
- fs43.stat(path60, function(er2, stat2) {
46241
+ var fs44 = __require("fs");
46242
+ function isexe(path61, options, cb) {
46243
+ fs44.stat(path61, function(er2, stat2) {
46139
46244
  cb(er2, er2 ? false : checkStat(stat2, options));
46140
46245
  });
46141
46246
  }
46142
- function sync(path60, options) {
46143
- return checkStat(fs43.statSync(path60), options);
46247
+ function sync(path61, options) {
46248
+ return checkStat(fs44.statSync(path61), options);
46144
46249
  }
46145
46250
  function checkStat(stat2, options) {
46146
46251
  return stat2.isFile() && checkMode(stat2, options);
@@ -46173,7 +46278,7 @@ var require_isexe = __commonJS({
46173
46278
  }
46174
46279
  module.exports = isexe;
46175
46280
  isexe.sync = sync;
46176
- function isexe(path60, options, cb) {
46281
+ function isexe(path61, options, cb) {
46177
46282
  if (typeof options === "function") {
46178
46283
  cb = options;
46179
46284
  options = {};
@@ -46183,7 +46288,7 @@ var require_isexe = __commonJS({
46183
46288
  throw new TypeError("callback not provided");
46184
46289
  }
46185
46290
  return new Promise(function(resolve12, reject) {
46186
- isexe(path60, options || {}, function(er2, is) {
46291
+ isexe(path61, options || {}, function(er2, is) {
46187
46292
  if (er2) {
46188
46293
  reject(er2);
46189
46294
  } else {
@@ -46192,7 +46297,7 @@ var require_isexe = __commonJS({
46192
46297
  });
46193
46298
  });
46194
46299
  }
46195
- core(path60, options || {}, function(er2, is) {
46300
+ core(path61, options || {}, function(er2, is) {
46196
46301
  if (er2) {
46197
46302
  if (er2.code === "EACCES" || options && options.ignoreErrors) {
46198
46303
  er2 = null;
@@ -46202,9 +46307,9 @@ var require_isexe = __commonJS({
46202
46307
  cb(er2, is);
46203
46308
  });
46204
46309
  }
46205
- function sync(path60, options) {
46310
+ function sync(path61, options) {
46206
46311
  try {
46207
- return core.sync(path60, options || {});
46312
+ return core.sync(path61, options || {});
46208
46313
  } catch (er2) {
46209
46314
  if (options && options.ignoreErrors || er2.code === "EACCES") {
46210
46315
  return false;
@@ -46220,7 +46325,7 @@ var require_isexe = __commonJS({
46220
46325
  var require_which = __commonJS({
46221
46326
  "../../node_modules/.pnpm/which@2.0.2/node_modules/which/which.js"(exports, module) {
46222
46327
  var isWindows3 = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
46223
- var path60 = __require("path");
46328
+ var path61 = __require("path");
46224
46329
  var COLON = isWindows3 ? ";" : ":";
46225
46330
  var isexe = require_isexe();
46226
46331
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -46258,7 +46363,7 @@ var require_which = __commonJS({
46258
46363
  return opt.all && found.length ? resolve12(found) : reject(getNotFoundError(cmd));
46259
46364
  const ppRaw = pathEnv[i2];
46260
46365
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
46261
- const pCmd = path60.join(pathPart, cmd);
46366
+ const pCmd = path61.join(pathPart, cmd);
46262
46367
  const p3 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
46263
46368
  resolve12(subStep(p3, i2, 0));
46264
46369
  });
@@ -46285,7 +46390,7 @@ var require_which = __commonJS({
46285
46390
  for (let i2 = 0; i2 < pathEnv.length; i2++) {
46286
46391
  const ppRaw = pathEnv[i2];
46287
46392
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
46288
- const pCmd = path60.join(pathPart, cmd);
46393
+ const pCmd = path61.join(pathPart, cmd);
46289
46394
  const p3 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
46290
46395
  for (let j3 = 0; j3 < pathExt.length; j3++) {
46291
46396
  const cur = p3 + pathExt[j3];
@@ -46331,7 +46436,7 @@ var require_path_key = __commonJS({
46331
46436
  // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js
46332
46437
  var require_resolveCommand = __commonJS({
46333
46438
  "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) {
46334
- var path60 = __require("path");
46439
+ var path61 = __require("path");
46335
46440
  var which = require_which();
46336
46441
  var getPathKey = require_path_key();
46337
46442
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -46349,7 +46454,7 @@ var require_resolveCommand = __commonJS({
46349
46454
  try {
46350
46455
  resolved = which.sync(parsed.command, {
46351
46456
  path: env3[getPathKey({ env: env3 })],
46352
- pathExt: withoutPathExt ? path60.delimiter : void 0
46457
+ pathExt: withoutPathExt ? path61.delimiter : void 0
46353
46458
  });
46354
46459
  } catch (e3) {
46355
46460
  } finally {
@@ -46358,7 +46463,7 @@ var require_resolveCommand = __commonJS({
46358
46463
  }
46359
46464
  }
46360
46465
  if (resolved) {
46361
- resolved = path60.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
46466
+ resolved = path61.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
46362
46467
  }
46363
46468
  return resolved;
46364
46469
  }
@@ -46409,8 +46514,8 @@ var require_shebang_command = __commonJS({
46409
46514
  if (!match) {
46410
46515
  return null;
46411
46516
  }
46412
- const [path60, argument] = match[0].replace(/#! ?/, "").split(" ");
46413
- const binary = path60.split("/").pop();
46517
+ const [path61, argument] = match[0].replace(/#! ?/, "").split(" ");
46518
+ const binary = path61.split("/").pop();
46414
46519
  if (binary === "env") {
46415
46520
  return argument;
46416
46521
  }
@@ -46422,16 +46527,16 @@ var require_shebang_command = __commonJS({
46422
46527
  // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
46423
46528
  var require_readShebang = __commonJS({
46424
46529
  "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js"(exports, module) {
46425
- var fs43 = __require("fs");
46530
+ var fs44 = __require("fs");
46426
46531
  var shebangCommand = require_shebang_command();
46427
46532
  function readShebang(command) {
46428
46533
  const size = 150;
46429
46534
  const buffer = Buffer.alloc(size);
46430
46535
  let fd;
46431
46536
  try {
46432
- fd = fs43.openSync(command, "r");
46433
- fs43.readSync(fd, buffer, 0, size, 0);
46434
- fs43.closeSync(fd);
46537
+ fd = fs44.openSync(command, "r");
46538
+ fs44.readSync(fd, buffer, 0, size, 0);
46539
+ fs44.closeSync(fd);
46435
46540
  } catch (e3) {
46436
46541
  }
46437
46542
  return shebangCommand(buffer.toString());
@@ -46443,7 +46548,7 @@ var require_readShebang = __commonJS({
46443
46548
  // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js
46444
46549
  var require_parse = __commonJS({
46445
46550
  "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js"(exports, module) {
46446
- var path60 = __require("path");
46551
+ var path61 = __require("path");
46447
46552
  var resolveCommand = require_resolveCommand();
46448
46553
  var escape4 = require_escape();
46449
46554
  var readShebang = require_readShebang();
@@ -46468,7 +46573,7 @@ var require_parse = __commonJS({
46468
46573
  const needsShell = !isExecutableRegExp.test(commandFile);
46469
46574
  if (parsed.options.forceShell || needsShell) {
46470
46575
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
46471
- parsed.command = path60.normalize(parsed.command);
46576
+ parsed.command = path61.normalize(parsed.command);
46472
46577
  parsed.command = escape4.command(parsed.command);
46473
46578
  parsed.args = parsed.args.map((arg) => escape4.argument(arg, needsDoubleEscapeMetaChars));
46474
46579
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -46556,7 +46661,7 @@ var require_cross_spawn = __commonJS({
46556
46661
  var cp = __require("child_process");
46557
46662
  var parse2 = require_parse();
46558
46663
  var enoent = require_enoent();
46559
- function spawn17(command, args, options) {
46664
+ function spawn14(command, args, options) {
46560
46665
  const parsed = parse2(command, args, options);
46561
46666
  const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
46562
46667
  enoent.hookChildProcess(spawned, parsed);
@@ -46568,8 +46673,8 @@ var require_cross_spawn = __commonJS({
46568
46673
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
46569
46674
  return result;
46570
46675
  }
46571
- module.exports = spawn17;
46572
- module.exports.spawn = spawn17;
46676
+ module.exports = spawn14;
46677
+ module.exports.spawn = spawn14;
46573
46678
  module.exports.sync = spawnSync5;
46574
46679
  module.exports._parse = parse2;
46575
46680
  module.exports._enoent = enoent;
@@ -74297,10 +74402,10 @@ var require_react_reconciler_development = __commonJS({
74297
74402
  var setErrorHandler = null;
74298
74403
  var setSuspenseHandler = null;
74299
74404
  {
74300
- var copyWithDeleteImpl = function(obj, path60, index2) {
74301
- var key = path60[index2];
74405
+ var copyWithDeleteImpl = function(obj, path61, index2) {
74406
+ var key = path61[index2];
74302
74407
  var updated = isArray(obj) ? obj.slice() : assign({}, obj);
74303
- if (index2 + 1 === path60.length) {
74408
+ if (index2 + 1 === path61.length) {
74304
74409
  if (isArray(updated)) {
74305
74410
  updated.splice(key, 1);
74306
74411
  } else {
@@ -74308,11 +74413,11 @@ var require_react_reconciler_development = __commonJS({
74308
74413
  }
74309
74414
  return updated;
74310
74415
  }
74311
- updated[key] = copyWithDeleteImpl(obj[key], path60, index2 + 1);
74416
+ updated[key] = copyWithDeleteImpl(obj[key], path61, index2 + 1);
74312
74417
  return updated;
74313
74418
  };
74314
- var copyWithDelete = function(obj, path60) {
74315
- return copyWithDeleteImpl(obj, path60, 0);
74419
+ var copyWithDelete = function(obj, path61) {
74420
+ return copyWithDeleteImpl(obj, path61, 0);
74316
74421
  };
74317
74422
  var copyWithRenameImpl = function(obj, oldPath, newPath, index2) {
74318
74423
  var oldKey = oldPath[index2];
@@ -74350,17 +74455,17 @@ var require_react_reconciler_development = __commonJS({
74350
74455
  }
74351
74456
  return copyWithRenameImpl(obj, oldPath, newPath, 0);
74352
74457
  };
74353
- var copyWithSetImpl = function(obj, path60, index2, value) {
74354
- if (index2 >= path60.length) {
74458
+ var copyWithSetImpl = function(obj, path61, index2, value) {
74459
+ if (index2 >= path61.length) {
74355
74460
  return value;
74356
74461
  }
74357
- var key = path60[index2];
74462
+ var key = path61[index2];
74358
74463
  var updated = isArray(obj) ? obj.slice() : assign({}, obj);
74359
- updated[key] = copyWithSetImpl(obj[key], path60, index2 + 1, value);
74464
+ updated[key] = copyWithSetImpl(obj[key], path61, index2 + 1, value);
74360
74465
  return updated;
74361
74466
  };
74362
- var copyWithSet = function(obj, path60, value) {
74363
- return copyWithSetImpl(obj, path60, 0, value);
74467
+ var copyWithSet = function(obj, path61, value) {
74468
+ return copyWithSetImpl(obj, path61, 0, value);
74364
74469
  };
74365
74470
  var findHook = function(fiber, id) {
74366
74471
  var currentHook2 = fiber.memoizedState;
@@ -74370,10 +74475,10 @@ var require_react_reconciler_development = __commonJS({
74370
74475
  }
74371
74476
  return currentHook2;
74372
74477
  };
74373
- overrideHookState = function(fiber, id, path60, value) {
74478
+ overrideHookState = function(fiber, id, path61, value) {
74374
74479
  var hook = findHook(fiber, id);
74375
74480
  if (hook !== null) {
74376
- var newState = copyWithSet(hook.memoizedState, path60, value);
74481
+ var newState = copyWithSet(hook.memoizedState, path61, value);
74377
74482
  hook.memoizedState = newState;
74378
74483
  hook.baseState = newState;
74379
74484
  fiber.memoizedProps = assign({}, fiber.memoizedProps);
@@ -74383,10 +74488,10 @@ var require_react_reconciler_development = __commonJS({
74383
74488
  }
74384
74489
  }
74385
74490
  };
74386
- overrideHookStateDeletePath = function(fiber, id, path60) {
74491
+ overrideHookStateDeletePath = function(fiber, id, path61) {
74387
74492
  var hook = findHook(fiber, id);
74388
74493
  if (hook !== null) {
74389
- var newState = copyWithDelete(hook.memoizedState, path60);
74494
+ var newState = copyWithDelete(hook.memoizedState, path61);
74390
74495
  hook.memoizedState = newState;
74391
74496
  hook.baseState = newState;
74392
74497
  fiber.memoizedProps = assign({}, fiber.memoizedProps);
@@ -74409,8 +74514,8 @@ var require_react_reconciler_development = __commonJS({
74409
74514
  }
74410
74515
  }
74411
74516
  };
74412
- overrideProps = function(fiber, path60, value) {
74413
- fiber.pendingProps = copyWithSet(fiber.memoizedProps, path60, value);
74517
+ overrideProps = function(fiber, path61, value) {
74518
+ fiber.pendingProps = copyWithSet(fiber.memoizedProps, path61, value);
74414
74519
  if (fiber.alternate) {
74415
74520
  fiber.alternate.pendingProps = fiber.pendingProps;
74416
74521
  }
@@ -74419,8 +74524,8 @@ var require_react_reconciler_development = __commonJS({
74419
74524
  scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
74420
74525
  }
74421
74526
  };
74422
- overridePropsDeletePath = function(fiber, path60) {
74423
- fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path60);
74527
+ overridePropsDeletePath = function(fiber, path61) {
74528
+ fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path61);
74424
74529
  if (fiber.alternate) {
74425
74530
  fiber.alternate.pendingProps = fiber.pendingProps;
74426
74531
  }
@@ -81862,8 +81967,8 @@ var init_ErrorOverview = __esm({
81862
81967
  init_dist7();
81863
81968
  init_Box();
81864
81969
  init_Text();
81865
- cleanupPath = (path60) => {
81866
- return path60?.replace(`file://${cwd()}/`, "");
81970
+ cleanupPath = (path61) => {
81971
+ return path61?.replace(`file://${cwd()}/`, "");
81867
81972
  };
81868
81973
  stackUtils = new import_stack_utils.default({
81869
81974
  cwd: cwd(),
@@ -86352,8 +86457,8 @@ function readClipboardImageLinux() {
86352
86457
  if (result.status === 0 && result.stdout && result.stdout.length > 0) {
86353
86458
  const target = nextCachePath();
86354
86459
  try {
86355
- const fs43 = __require("fs");
86356
- fs43.writeFileSync(target, result.stdout);
86460
+ const fs44 = __require("fs");
86461
+ fs44.writeFileSync(target, result.stdout);
86357
86462
  } catch {
86358
86463
  return null;
86359
86464
  }
@@ -88004,6 +88109,8 @@ var init_WorkflowsPanel = __esm({
88004
88109
  const [loading, setLoading] = import_react55.default.useState(true);
88005
88110
  const [busy, setBusy] = import_react55.default.useState(false);
88006
88111
  const [status, setStatus] = import_react55.default.useState(null);
88112
+ const [pending, setPending] = import_react55.default.useState(null);
88113
+ const [reply2, setReply] = import_react55.default.useState("");
88007
88114
  const reload = import_react55.default.useCallback(async () => {
88008
88115
  if (!view) {
88009
88116
  setLoading(false);
@@ -88021,7 +88128,7 @@ var init_WorkflowsPanel = __esm({
88021
88128
  import_react55.default.useEffect(() => {
88022
88129
  void reload();
88023
88130
  }, [reload]);
88024
- const active = !busy && !!view && rows.length > 0;
88131
+ const active = !busy && !pending && !!view && rows.length > 0;
88025
88132
  const run2 = import_react55.default.useCallback(async (wf) => {
88026
88133
  if (!view || busy)
88027
88134
  return;
@@ -88033,6 +88140,17 @@ var init_WorkflowsPanel = __esm({
88033
88140
  setStatus(`running "${wf.name}"\u2026`);
88034
88141
  try {
88035
88142
  const result = await view.run(wf.name);
88143
+ if (result.status === "paused" && result.runId) {
88144
+ const askStep = result.steps.find((s2) => s2.status === "awaiting_input");
88145
+ setPending({
88146
+ runId: result.runId,
88147
+ label: askStep?.id ?? wf.name,
88148
+ prompt: result.output || "The workflow is waiting for your input."
88149
+ });
88150
+ setReply("");
88151
+ setStatus(`\u23F8 "${wf.name}" is waiting for your reply \u2014 type it and press Enter (Esc cancels).`);
88152
+ return;
88153
+ }
88036
88154
  const marks = result.steps.map((s2) => `${stepMark(s2.status)}${s2.id}`).join(" ");
88037
88155
  setStatus(result.ok ? `\u2713 ${wf.name} completed \u2014 ${marks}` : `\u2717 ${wf.name} failed: ${result.error ?? ""} \u2014 ${marks}`);
88038
88156
  } catch (err) {
@@ -88042,6 +88160,35 @@ var init_WorkflowsPanel = __esm({
88042
88160
  void reload();
88043
88161
  }
88044
88162
  }, [view, busy, reload]);
88163
+ const submitReply = import_react55.default.useCallback(async (pendingRun, text) => {
88164
+ if (!view?.resume) {
88165
+ setStatus("resume not supported by this session");
88166
+ return;
88167
+ }
88168
+ const answer = text.trim();
88169
+ if (!answer)
88170
+ return;
88171
+ setPending(null);
88172
+ setReply("");
88173
+ setBusy(true);
88174
+ setStatus(`resuming "${pendingRun.label}"\u2026`);
88175
+ try {
88176
+ const result = await view.resume(pendingRun.runId, answer);
88177
+ if (result.status === "paused" && result.runId) {
88178
+ const askStep = result.steps.find((s2) => s2.status === "awaiting_input");
88179
+ setPending({ runId: result.runId, label: askStep?.id ?? pendingRun.label, prompt: result.output });
88180
+ setStatus("\u23F8 waiting again \u2014 type your reply and press Enter (Esc cancels).");
88181
+ } else {
88182
+ const marks = result.steps.map((s2) => `${stepMark(s2.status)}${s2.id}`).join(" ");
88183
+ setStatus(result.ok ? `\u2713 resumed & completed \u2014 ${marks}` : `\u2717 resume failed: ${result.error ?? ""} \u2014 ${marks}`);
88184
+ }
88185
+ } catch (err) {
88186
+ setStatus(`\u2717 resume errored: ${err instanceof Error ? err.message : String(err)}`);
88187
+ } finally {
88188
+ setBusy(false);
88189
+ void reload();
88190
+ }
88191
+ }, [view, reload]);
88045
88192
  const scroll = useScrollableList({
88046
88193
  total: rows.length,
88047
88194
  windowSize: WINDOW4,
@@ -88067,6 +88214,26 @@ var init_WorkflowsPanel = __esm({
88067
88214
  void run2(wf);
88068
88215
  }
88069
88216
  }, { isActive: active });
88217
+ use_input_default((input, key) => {
88218
+ if (!pending)
88219
+ return;
88220
+ if (key.escape) {
88221
+ setPending(null);
88222
+ setReply("");
88223
+ setStatus("reply cancelled \u2014 the run stays paused (re-run to resume).");
88224
+ return;
88225
+ }
88226
+ if (key.return) {
88227
+ void submitReply(pending, reply2);
88228
+ return;
88229
+ }
88230
+ if (key.backspace || key.delete) {
88231
+ setReply((r2) => r2.slice(0, -1));
88232
+ return;
88233
+ }
88234
+ if (input && !key.ctrl && !key.meta)
88235
+ setReply((r2) => r2 + input);
88236
+ }, { isActive: !busy && !!pending });
88070
88237
  const termWidth = process.stdout.columns ?? 80;
88071
88238
  const descWidth = Math.max(16, termWidth - NAME_COL3 - SCOPE_COL2 - TRIG_COL - 12);
88072
88239
  const slice = rows.slice(scroll.visible.start, scroll.visible.end);
@@ -88076,7 +88243,7 @@ var init_WorkflowsPanel = __esm({
88076
88243
  const absoluteIndex = scroll.visible.start + i2;
88077
88244
  const focused = absoluteIndex === scroll.cursor;
88078
88245
  return (0, import_jsx_runtime27.jsxs)(Box_default, { children: [(0, import_jsx_runtime27.jsx)(Text, { ...focused ? {} : { dimColor: true }, children: focused ? "\u203A " : " " }), (0, import_jsx_runtime27.jsx)(Text, { color: wf.enabled ? Colors.active : void 0, dimColor: !wf.enabled, children: wf.enabled ? "\u25CF " : "\u25CB " }), (0, import_jsx_runtime27.jsx)(Box_default, { width: NAME_COL3, children: (0, import_jsx_runtime27.jsx)(Text, { bold: focused, children: truncate8(wf.name, NAME_COL3 - 1) }) }), (0, import_jsx_runtime27.jsx)(Box_default, { width: SCOPE_COL2, children: (0, import_jsx_runtime27.jsx)(Text, { dimColor: true, children: wf.scope }) }), (0, import_jsx_runtime27.jsx)(Box_default, { width: TRIG_COL, children: (0, import_jsx_runtime27.jsx)(Text, { dimColor: true, wrap: "truncate", children: wf.triggers }) }), (0, import_jsx_runtime27.jsx)(Box_default, { width: descWidth, children: (0, import_jsx_runtime27.jsx)(Text, { dimColor: true, wrap: "truncate", children: oneLine5(wf.description) }) })] }, wf.name);
88079
- }), scroll.canScrollDown ? (0, import_jsx_runtime27.jsx)(Text, { dimColor: true, children: ` \u2193 ${rows.length - scroll.visible.end} more below` }) : null, status ? (0, import_jsx_runtime27.jsx)(Box_default, { marginTop: 1, children: (0, import_jsx_runtime27.jsx)(Text, { wrap: "truncate-end", children: status }) }) : null] });
88246
+ }), scroll.canScrollDown ? (0, import_jsx_runtime27.jsx)(Text, { dimColor: true, children: ` \u2193 ${rows.length - scroll.visible.end} more below` }) : null, pending ? (0, import_jsx_runtime27.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: Colors.active, paddingX: 1, children: [(0, import_jsx_runtime27.jsxs)(Text, { color: Colors.active, children: ["\u23F8 Workflow waiting \xB7 ", pending.label] }), pending.prompt ? (0, import_jsx_runtime27.jsx)(Text, { wrap: "wrap", children: oneLine5(pending.prompt).slice(0, 280) }) : null, (0, import_jsx_runtime27.jsxs)(Box_default, { marginTop: 1, children: [(0, import_jsx_runtime27.jsx)(Text, { children: "reply \u203A " }), (0, import_jsx_runtime27.jsx)(Text, { children: reply2 || " " })] }), (0, import_jsx_runtime27.jsx)(Text, { dimColor: true, children: "Enter send \xB7 Esc cancel" })] }) : null, status ? (0, import_jsx_runtime27.jsx)(Box_default, { marginTop: 1, children: (0, import_jsx_runtime27.jsx)(Text, { wrap: "truncate-end", children: status }) }) : null] });
88080
88247
  };
88081
88248
  }
88082
88249
  });
@@ -90717,10 +90884,10 @@ var require_segments = __commonJS({
90717
90884
  const segs = getSegmentsFromString(data, Utils.isKanjiModeEnabled());
90718
90885
  const nodes = buildNodes(segs);
90719
90886
  const graph = buildGraph(nodes, version);
90720
- const path60 = dijkstra.find_path(graph.map, "start", "end");
90887
+ const path61 = dijkstra.find_path(graph.map, "start", "end");
90721
90888
  const optimizedSegs = [];
90722
- for (let i2 = 1; i2 < path60.length - 1; i2++) {
90723
- optimizedSegs.push(graph.table[path60[i2]].node);
90889
+ for (let i2 = 1; i2 < path61.length - 1; i2++) {
90890
+ optimizedSegs.push(graph.table[path61[i2]].node);
90724
90891
  }
90725
90892
  return exports.fromArray(mergeSegments(optimizedSegs));
90726
90893
  };
@@ -93131,7 +93298,7 @@ var require_utils4 = __commonJS({
93131
93298
  // ../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/png.js
93132
93299
  var require_png2 = __commonJS({
93133
93300
  "../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/png.js"(exports) {
93134
- var fs43 = __require("fs");
93301
+ var fs44 = __require("fs");
93135
93302
  var PNG = require_png().PNG;
93136
93303
  var Utils = require_utils4();
93137
93304
  exports.render = function render2(qrData, options) {
@@ -93172,7 +93339,7 @@ var require_png2 = __commonJS({
93172
93339
  });
93173
93340
  png.pack();
93174
93341
  };
93175
- exports.renderToFile = function renderToFile(path60, qrData, options, cb) {
93342
+ exports.renderToFile = function renderToFile(path61, qrData, options, cb) {
93176
93343
  if (typeof cb === "undefined") {
93177
93344
  cb = options;
93178
93345
  options = void 0;
@@ -93183,7 +93350,7 @@ var require_png2 = __commonJS({
93183
93350
  called = true;
93184
93351
  cb.apply(null, args);
93185
93352
  };
93186
- const stream = fs43.createWriteStream(path60);
93353
+ const stream = fs44.createWriteStream(path61);
93187
93354
  stream.on("error", done);
93188
93355
  stream.on("close", done);
93189
93356
  exports.renderToFileStream(stream, qrData, options);
@@ -93245,14 +93412,14 @@ var require_utf8 = __commonJS({
93245
93412
  }
93246
93413
  return output;
93247
93414
  };
93248
- exports.renderToFile = function renderToFile(path60, qrData, options, cb) {
93415
+ exports.renderToFile = function renderToFile(path61, qrData, options, cb) {
93249
93416
  if (typeof cb === "undefined") {
93250
93417
  cb = options;
93251
93418
  options = void 0;
93252
93419
  }
93253
- const fs43 = __require("fs");
93420
+ const fs44 = __require("fs");
93254
93421
  const utf8 = exports.render(qrData, options);
93255
- fs43.writeFile(path60, utf8, cb);
93422
+ fs44.writeFile(path61, utf8, cb);
93256
93423
  };
93257
93424
  }
93258
93425
  });
@@ -93373,7 +93540,7 @@ var require_svg_tag = __commonJS({
93373
93540
  return str2;
93374
93541
  }
93375
93542
  function qrToPath(data, size, margin) {
93376
- let path60 = "";
93543
+ let path61 = "";
93377
93544
  let moveBy = 0;
93378
93545
  let newRow = false;
93379
93546
  let lineLength = 0;
@@ -93384,19 +93551,19 @@ var require_svg_tag = __commonJS({
93384
93551
  if (data[i2]) {
93385
93552
  lineLength++;
93386
93553
  if (!(i2 > 0 && col > 0 && data[i2 - 1])) {
93387
- path60 += newRow ? svgCmd("M", col + margin, 0.5 + row + margin) : svgCmd("m", moveBy, 0);
93554
+ path61 += newRow ? svgCmd("M", col + margin, 0.5 + row + margin) : svgCmd("m", moveBy, 0);
93388
93555
  moveBy = 0;
93389
93556
  newRow = false;
93390
93557
  }
93391
93558
  if (!(col + 1 < size && data[i2 + 1])) {
93392
- path60 += svgCmd("h", lineLength);
93559
+ path61 += svgCmd("h", lineLength);
93393
93560
  lineLength = 0;
93394
93561
  }
93395
93562
  } else {
93396
93563
  moveBy++;
93397
93564
  }
93398
93565
  }
93399
- return path60;
93566
+ return path61;
93400
93567
  }
93401
93568
  exports.render = function render2(qrData, options, cb) {
93402
93569
  const opts = Utils.getOptions(options);
@@ -93404,10 +93571,10 @@ var require_svg_tag = __commonJS({
93404
93571
  const data = qrData.modules.data;
93405
93572
  const qrcodesize = size + opts.margin * 2;
93406
93573
  const bg = !opts.color.light.a ? "" : "<path " + getColorAttrib(opts.color.light, "fill") + ' d="M0 0h' + qrcodesize + "v" + qrcodesize + 'H0z"/>';
93407
- const path60 = "<path " + getColorAttrib(opts.color.dark, "stroke") + ' d="' + qrToPath(data, size, opts.margin) + '"/>';
93574
+ const path61 = "<path " + getColorAttrib(opts.color.dark, "stroke") + ' d="' + qrToPath(data, size, opts.margin) + '"/>';
93408
93575
  const viewBox = 'viewBox="0 0 ' + qrcodesize + " " + qrcodesize + '"';
93409
93576
  const width = !opts.width ? "" : 'width="' + opts.width + '" height="' + opts.width + '" ';
93410
- const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg + path60 + "</svg>\n";
93577
+ const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg + path61 + "</svg>\n";
93411
93578
  if (typeof cb === "function") {
93412
93579
  cb(null, svgTag);
93413
93580
  }
@@ -93421,15 +93588,15 @@ var require_svg = __commonJS({
93421
93588
  "../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/svg.js"(exports) {
93422
93589
  var svgTagRenderer = require_svg_tag();
93423
93590
  exports.render = svgTagRenderer.render;
93424
- exports.renderToFile = function renderToFile(path60, qrData, options, cb) {
93591
+ exports.renderToFile = function renderToFile(path61, qrData, options, cb) {
93425
93592
  if (typeof cb === "undefined") {
93426
93593
  cb = options;
93427
93594
  options = void 0;
93428
93595
  }
93429
- const fs43 = __require("fs");
93596
+ const fs44 = __require("fs");
93430
93597
  const svgTag = exports.render(qrData, options);
93431
93598
  const xmlStr = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' + svgTag;
93432
- fs43.writeFile(path60, xmlStr, cb);
93599
+ fs44.writeFile(path61, xmlStr, cb);
93433
93600
  };
93434
93601
  }
93435
93602
  });
@@ -93587,8 +93754,8 @@ var require_server = __commonJS({
93587
93754
  cb
93588
93755
  };
93589
93756
  }
93590
- function getTypeFromFilename(path60) {
93591
- return path60.slice((path60.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
93757
+ function getTypeFromFilename(path61) {
93758
+ return path61.slice((path61.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
93592
93759
  }
93593
93760
  function getRendererFromType(type) {
93594
93761
  switch (type) {
@@ -93652,17 +93819,17 @@ var require_server = __commonJS({
93652
93819
  const renderer2 = getRendererFromType(params.opts.type);
93653
93820
  return render2(renderer2.renderToBuffer, text, params);
93654
93821
  };
93655
- exports.toFile = function toFile3(path60, text, opts, cb) {
93656
- if (typeof path60 !== "string" || !(typeof text === "string" || typeof text === "object")) {
93822
+ exports.toFile = function toFile3(path61, text, opts, cb) {
93823
+ if (typeof path61 !== "string" || !(typeof text === "string" || typeof text === "object")) {
93657
93824
  throw new Error("Invalid argument");
93658
93825
  }
93659
93826
  if (arguments.length < 3 && !canPromise()) {
93660
93827
  throw new Error("Too few arguments provided");
93661
93828
  }
93662
93829
  const params = checkParams(text, opts, cb);
93663
- const type = params.opts.type || getTypeFromFilename(path60);
93830
+ const type = params.opts.type || getTypeFromFilename(path61);
93664
93831
  const renderer2 = getRendererFromType(type);
93665
- const renderToFile = renderer2.renderToFile.bind(null, path60);
93832
+ const renderToFile = renderer2.renderToFile.bind(null, path61);
93666
93833
  return render2(renderToFile, text, params);
93667
93834
  };
93668
93835
  exports.toFileStream = function toFileStream(stream, text, opts) {
@@ -94482,14 +94649,14 @@ var require_util2 = __commonJS({
94482
94649
  }
94483
94650
  const port = url2.port != null ? url2.port : url2.protocol === "https:" ? 443 : 80;
94484
94651
  let origin = url2.origin != null ? url2.origin : `${url2.protocol || ""}//${url2.hostname || ""}:${port}`;
94485
- let path60 = url2.path != null ? url2.path : `${url2.pathname || ""}${url2.search || ""}`;
94652
+ let path61 = url2.path != null ? url2.path : `${url2.pathname || ""}${url2.search || ""}`;
94486
94653
  if (origin[origin.length - 1] === "/") {
94487
94654
  origin = origin.slice(0, origin.length - 1);
94488
94655
  }
94489
- if (path60 && path60[0] !== "/") {
94490
- path60 = `/${path60}`;
94656
+ if (path61 && path61[0] !== "/") {
94657
+ path61 = `/${path61}`;
94491
94658
  }
94492
- return new URL(`${origin}${path60}`);
94659
+ return new URL(`${origin}${path61}`);
94493
94660
  }
94494
94661
  if (!isHttpOrHttpsPrefixed(url2.origin || url2.protocol)) {
94495
94662
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -94939,39 +95106,39 @@ var require_diagnostics = __commonJS({
94939
95106
  });
94940
95107
  diagnosticsChannel.channel("undici:client:sendHeaders").subscribe((evt) => {
94941
95108
  const {
94942
- request: { method, path: path60, origin }
95109
+ request: { method, path: path61, origin }
94943
95110
  } = evt;
94944
- debuglog("sending request to %s %s/%s", method, origin, path60);
95111
+ debuglog("sending request to %s %s/%s", method, origin, path61);
94945
95112
  });
94946
95113
  diagnosticsChannel.channel("undici:request:headers").subscribe((evt) => {
94947
95114
  const {
94948
- request: { method, path: path60, origin },
95115
+ request: { method, path: path61, origin },
94949
95116
  response: { statusCode }
94950
95117
  } = evt;
94951
95118
  debuglog(
94952
95119
  "received response to %s %s/%s - HTTP %d",
94953
95120
  method,
94954
95121
  origin,
94955
- path60,
95122
+ path61,
94956
95123
  statusCode
94957
95124
  );
94958
95125
  });
94959
95126
  diagnosticsChannel.channel("undici:request:trailers").subscribe((evt) => {
94960
95127
  const {
94961
- request: { method, path: path60, origin }
95128
+ request: { method, path: path61, origin }
94962
95129
  } = evt;
94963
- debuglog("trailers received from %s %s/%s", method, origin, path60);
95130
+ debuglog("trailers received from %s %s/%s", method, origin, path61);
94964
95131
  });
94965
95132
  diagnosticsChannel.channel("undici:request:error").subscribe((evt) => {
94966
95133
  const {
94967
- request: { method, path: path60, origin },
95134
+ request: { method, path: path61, origin },
94968
95135
  error: error2
94969
95136
  } = evt;
94970
95137
  debuglog(
94971
95138
  "request to %s %s/%s errored - %s",
94972
95139
  method,
94973
95140
  origin,
94974
- path60,
95141
+ path61,
94975
95142
  error2.message
94976
95143
  );
94977
95144
  });
@@ -95020,9 +95187,9 @@ var require_diagnostics = __commonJS({
95020
95187
  });
95021
95188
  diagnosticsChannel.channel("undici:client:sendHeaders").subscribe((evt) => {
95022
95189
  const {
95023
- request: { method, path: path60, origin }
95190
+ request: { method, path: path61, origin }
95024
95191
  } = evt;
95025
- debuglog("sending request to %s %s/%s", method, origin, path60);
95192
+ debuglog("sending request to %s %s/%s", method, origin, path61);
95026
95193
  });
95027
95194
  }
95028
95195
  diagnosticsChannel.channel("undici:websocket:open").subscribe((evt) => {
@@ -95084,7 +95251,7 @@ var require_request = __commonJS({
95084
95251
  var kHandler = /* @__PURE__ */ Symbol("handler");
95085
95252
  var Request4 = class {
95086
95253
  constructor(origin, {
95087
- path: path60,
95254
+ path: path61,
95088
95255
  method,
95089
95256
  body,
95090
95257
  headers,
@@ -95099,11 +95266,11 @@ var require_request = __commonJS({
95099
95266
  expectContinue,
95100
95267
  servername
95101
95268
  }, handler) {
95102
- if (typeof path60 !== "string") {
95269
+ if (typeof path61 !== "string") {
95103
95270
  throw new InvalidArgumentError("path must be a string");
95104
- } else if (path60[0] !== "/" && !(path60.startsWith("http://") || path60.startsWith("https://")) && method !== "CONNECT") {
95271
+ } else if (path61[0] !== "/" && !(path61.startsWith("http://") || path61.startsWith("https://")) && method !== "CONNECT") {
95105
95272
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
95106
- } else if (invalidPathRegex.test(path60)) {
95273
+ } else if (invalidPathRegex.test(path61)) {
95107
95274
  throw new InvalidArgumentError("invalid request path");
95108
95275
  }
95109
95276
  if (typeof method !== "string") {
@@ -95169,7 +95336,7 @@ var require_request = __commonJS({
95169
95336
  this.completed = false;
95170
95337
  this.aborted = false;
95171
95338
  this.upgrade = upgrade || null;
95172
- this.path = query ? buildURL(path60, query) : path60;
95339
+ this.path = query ? buildURL(path61, query) : path61;
95173
95340
  this.origin = origin;
95174
95341
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
95175
95342
  this.blocking = blocking == null ? false : blocking;
@@ -99707,7 +99874,7 @@ var require_client_h1 = __commonJS({
99707
99874
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
99708
99875
  }
99709
99876
  function writeH1(client, request) {
99710
- const { method, path: path60, host, upgrade, blocking, reset } = request;
99877
+ const { method, path: path61, host, upgrade, blocking, reset } = request;
99711
99878
  let { body, headers, contentLength } = request;
99712
99879
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
99713
99880
  if (util.isFormDataLike(body)) {
@@ -99773,7 +99940,7 @@ var require_client_h1 = __commonJS({
99773
99940
  if (blocking) {
99774
99941
  socket[kBlocking] = true;
99775
99942
  }
99776
- let header = `${method} ${path60} HTTP/1.1\r
99943
+ let header = `${method} ${path61} HTTP/1.1\r
99777
99944
  `;
99778
99945
  if (typeof host === "string") {
99779
99946
  header += `host: ${host}\r
@@ -100298,7 +100465,7 @@ var require_client_h2 = __commonJS({
100298
100465
  }
100299
100466
  function writeH2(client, request) {
100300
100467
  const session = client[kHTTP2Session];
100301
- const { method, path: path60, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
100468
+ const { method, path: path61, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
100302
100469
  let { body } = request;
100303
100470
  if (upgrade) {
100304
100471
  util.errorRequest(client, request, new Error("Upgrade not supported for H2"));
@@ -100365,7 +100532,7 @@ var require_client_h2 = __commonJS({
100365
100532
  });
100366
100533
  return true;
100367
100534
  }
100368
- headers[HTTP2_HEADER_PATH] = path60;
100535
+ headers[HTTP2_HEADER_PATH] = path61;
100369
100536
  headers[HTTP2_HEADER_SCHEME] = "https";
100370
100537
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
100371
100538
  if (body && typeof body.read === "function") {
@@ -100717,9 +100884,9 @@ var require_redirect_handler = __commonJS({
100717
100884
  return this.handler.onHeaders(statusCode, headers, resume, statusText);
100718
100885
  }
100719
100886
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
100720
- const path60 = search ? `${pathname}${search}` : pathname;
100887
+ const path61 = search ? `${pathname}${search}` : pathname;
100721
100888
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
100722
- this.opts.path = path60;
100889
+ this.opts.path = path61;
100723
100890
  this.opts.origin = origin;
100724
100891
  this.opts.maxRedirections = 0;
100725
100892
  this.opts.query = null;
@@ -101944,10 +102111,10 @@ var require_proxy_agent = __commonJS({
101944
102111
  };
101945
102112
  const {
101946
102113
  origin,
101947
- path: path60 = "/",
102114
+ path: path61 = "/",
101948
102115
  headers = {}
101949
102116
  } = opts;
101950
- opts.path = origin + path60;
102117
+ opts.path = origin + path61;
101951
102118
  if (!("host" in headers) && !("Host" in headers)) {
101952
102119
  const { host } = new URL3(origin);
101953
102120
  headers.host = host;
@@ -103855,20 +104022,20 @@ var require_mock_utils = __commonJS({
103855
104022
  }
103856
104023
  return true;
103857
104024
  }
103858
- function safeUrl(path60) {
103859
- if (typeof path60 !== "string") {
103860
- return path60;
104025
+ function safeUrl(path61) {
104026
+ if (typeof path61 !== "string") {
104027
+ return path61;
103861
104028
  }
103862
- const pathSegments = path60.split("?");
104029
+ const pathSegments = path61.split("?");
103863
104030
  if (pathSegments.length !== 2) {
103864
- return path60;
104031
+ return path61;
103865
104032
  }
103866
104033
  const qp = new URLSearchParams(pathSegments.pop());
103867
104034
  qp.sort();
103868
104035
  return [...pathSegments, qp.toString()].join("?");
103869
104036
  }
103870
- function matchKey(mockDispatch2, { path: path60, method, body, headers }) {
103871
- const pathMatch = matchValue(mockDispatch2.path, path60);
104037
+ function matchKey(mockDispatch2, { path: path61, method, body, headers }) {
104038
+ const pathMatch = matchValue(mockDispatch2.path, path61);
103872
104039
  const methodMatch = matchValue(mockDispatch2.method, method);
103873
104040
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
103874
104041
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -103890,7 +104057,7 @@ var require_mock_utils = __commonJS({
103890
104057
  function getMockDispatch(mockDispatches, key) {
103891
104058
  const basePath = key.query ? buildURL(key.path, key.query) : key.path;
103892
104059
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
103893
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path60 }) => matchValue(safeUrl(path60), resolvedPath));
104060
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path61 }) => matchValue(safeUrl(path61), resolvedPath));
103894
104061
  if (matchedMockDispatches.length === 0) {
103895
104062
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
103896
104063
  }
@@ -103928,9 +104095,9 @@ var require_mock_utils = __commonJS({
103928
104095
  }
103929
104096
  }
103930
104097
  function buildKey(opts) {
103931
- const { path: path60, method, body, headers, query } = opts;
104098
+ const { path: path61, method, body, headers, query } = opts;
103932
104099
  return {
103933
- path: path60,
104100
+ path: path61,
103934
104101
  method,
103935
104102
  body,
103936
104103
  headers,
@@ -104388,10 +104555,10 @@ var require_pending_interceptors_formatter = __commonJS({
104388
104555
  }
104389
104556
  format(pendingInterceptors) {
104390
104557
  const withPrettyHeaders = pendingInterceptors.map(
104391
- ({ method, path: path60, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
104558
+ ({ method, path: path61, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
104392
104559
  Method: method,
104393
104560
  Origin: origin,
104394
- Path: path60,
104561
+ Path: path61,
104395
104562
  "Status code": statusCode,
104396
104563
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
104397
104564
  Invocations: timesInvoked,
@@ -109230,9 +109397,9 @@ var require_util7 = __commonJS({
109230
109397
  }
109231
109398
  }
109232
109399
  }
109233
- function validateCookiePath(path60) {
109234
- for (let i2 = 0; i2 < path60.length; ++i2) {
109235
- const code = path60.charCodeAt(i2);
109400
+ function validateCookiePath(path61) {
109401
+ for (let i2 = 0; i2 < path61.length; ++i2) {
109402
+ const code = path61.charCodeAt(i2);
109236
109403
  if (code < 32 || // exclude CTLs (0-31)
109237
109404
  code === 127 || // DEL
109238
109405
  code === 59) {
@@ -111892,11 +112059,11 @@ var require_undici = __commonJS({
111892
112059
  if (typeof opts.path !== "string") {
111893
112060
  throw new InvalidArgumentError("invalid opts.path");
111894
112061
  }
111895
- let path60 = opts.path;
112062
+ let path61 = opts.path;
111896
112063
  if (!opts.path.startsWith("/")) {
111897
- path60 = `/${path60}`;
112064
+ path61 = `/${path61}`;
111898
112065
  }
111899
- url2 = new URL(util.parseOrigin(url2).origin + path60);
112066
+ url2 = new URL(util.parseOrigin(url2).origin + path61);
111900
112067
  } else {
111901
112068
  if (!opts) {
111902
112069
  opts = typeof url2 === "object" ? url2 : {};
@@ -113793,7 +113960,12 @@ var VaultStore = class {
113793
113960
  }
113794
113961
  const parsed = JSON.parse(raw);
113795
113962
  if (parsed.version !== 1 || parsed.kdf !== "scrypt") {
113796
- throw new Error(`Unsupported vault file: version=${parsed.version} kdf=${parsed.kdf}`);
113963
+ throw new MoxxyError({
113964
+ code: "VAULT_CORRUPT",
113965
+ message: `Unsupported vault file: version=${parsed.version} kdf=${parsed.kdf}`,
113966
+ hint: `This vault was written by an incompatible version. Back up and remove ${this.filePath} to re-initialize.`,
113967
+ context: { filePath: this.filePath }
113968
+ });
113797
113969
  }
113798
113970
  this.file = parsed;
113799
113971
  const salt = Buffer.from(parsed.salt, "base64");
@@ -113977,8 +114149,6 @@ function firstEntry(entries) {
113977
114149
  }
113978
114150
  return void 0;
113979
114151
  }
113980
-
113981
- // ../plugin-vault/dist/placeholder.js
113982
114152
  var PLACEHOLDER_RE = /\$\{vault:([A-Za-z0-9_.-]+)\}/g;
113983
114153
  async function resolveString(input, vault) {
113984
114154
  PLACEHOLDER_RE.lastIndex = 0;
@@ -113992,8 +114162,14 @@ async function resolveString(input, vault) {
113992
114162
  const values = /* @__PURE__ */ new Map();
113993
114163
  for (const name of names) {
113994
114164
  const value = await vault.get(name);
113995
- if (value === null)
113996
- throw new Error(`vault: missing required entry '${name}' referenced in config`);
114165
+ if (value === null) {
114166
+ throw new MoxxyError({
114167
+ code: "CONFIG_INVALID",
114168
+ message: `vault: missing required entry '${name}' referenced in config`,
114169
+ hint: `Add it with \`/vault set ${name} <value>\` (or the \`vault_set\` tool), then retry.`,
114170
+ context: { name }
114171
+ });
114172
+ }
113997
114173
  values.set(name, value);
113998
114174
  }
113999
114175
  return input.replace(PLACEHOLDER_RE, (_match, name) => values.get(name) ?? "");
@@ -114118,8 +114294,14 @@ ${vaultCommandUsage()}` };
114118
114294
  permission: { action: "prompt" },
114119
114295
  handler: async ({ name }) => {
114120
114296
  const value = await vault.get(name);
114121
- if (value === null)
114122
- throw new Error(`vault: '${name}' not found`);
114297
+ if (value === null) {
114298
+ throw new MoxxyError({
114299
+ code: "TOOL_ERROR",
114300
+ message: `vault: '${name}' not found`,
114301
+ hint: "Use `vault_list` to see stored entry names.",
114302
+ context: { name }
114303
+ });
114304
+ }
114123
114305
  return value;
114124
114306
  }
114125
114307
  }),
@@ -115826,13 +116008,13 @@ var MultipartBody = class {
115826
116008
  }
115827
116009
  };
115828
116010
  var fileFromPathWarned = false;
115829
- async function fileFromPath3(path60, ...args) {
116011
+ async function fileFromPath3(path61, ...args) {
115830
116012
  const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
115831
116013
  if (!fileFromPathWarned) {
115832
- console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path60)}) instead`);
116014
+ console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path61)}) instead`);
115833
116015
  fileFromPathWarned = true;
115834
116016
  }
115835
- return await _fileFromPath(path60, ...args);
116017
+ return await _fileFromPath(path61, ...args);
115836
116018
  }
115837
116019
  var defaultHttpAgent = new import_agentkeepalive.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
115838
116020
  var defaultHttpsAgent = new import_agentkeepalive.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
@@ -116555,29 +116737,29 @@ var APIClient = class {
116555
116737
  defaultIdempotencyKey() {
116556
116738
  return `stainless-node-retry-${uuid4()}`;
116557
116739
  }
116558
- get(path60, opts) {
116559
- return this.methodRequest("get", path60, opts);
116740
+ get(path61, opts) {
116741
+ return this.methodRequest("get", path61, opts);
116560
116742
  }
116561
- post(path60, opts) {
116562
- return this.methodRequest("post", path60, opts);
116743
+ post(path61, opts) {
116744
+ return this.methodRequest("post", path61, opts);
116563
116745
  }
116564
- patch(path60, opts) {
116565
- return this.methodRequest("patch", path60, opts);
116746
+ patch(path61, opts) {
116747
+ return this.methodRequest("patch", path61, opts);
116566
116748
  }
116567
- put(path60, opts) {
116568
- return this.methodRequest("put", path60, opts);
116749
+ put(path61, opts) {
116750
+ return this.methodRequest("put", path61, opts);
116569
116751
  }
116570
- delete(path60, opts) {
116571
- return this.methodRequest("delete", path60, opts);
116752
+ delete(path61, opts) {
116753
+ return this.methodRequest("delete", path61, opts);
116572
116754
  }
116573
- methodRequest(method, path60, opts) {
116755
+ methodRequest(method, path61, opts) {
116574
116756
  return this.request(Promise.resolve(opts).then(async (opts2) => {
116575
116757
  const body = opts2 && isBlobLike(opts2?.body) ? new DataView(await opts2.body.arrayBuffer()) : opts2?.body instanceof DataView ? opts2.body : opts2?.body instanceof ArrayBuffer ? new DataView(opts2.body) : opts2 && ArrayBuffer.isView(opts2?.body) ? new DataView(opts2.body.buffer) : opts2?.body;
116576
- return { method, path: path60, ...opts2, body };
116758
+ return { method, path: path61, ...opts2, body };
116577
116759
  }));
116578
116760
  }
116579
- getAPIList(path60, Page3, opts) {
116580
- return this.requestAPIList(Page3, { method: "get", path: path60, ...opts });
116761
+ getAPIList(path61, Page3, opts) {
116762
+ return this.requestAPIList(Page3, { method: "get", path: path61, ...opts });
116581
116763
  }
116582
116764
  calculateContentLength(body) {
116583
116765
  if (typeof body === "string") {
@@ -116596,10 +116778,10 @@ var APIClient = class {
116596
116778
  }
116597
116779
  buildRequest(inputOptions, { retryCount = 0 } = {}) {
116598
116780
  const options = { ...inputOptions };
116599
- const { method, path: path60, query, headers = {} } = options;
116781
+ const { method, path: path61, query, headers = {} } = options;
116600
116782
  const body = ArrayBuffer.isView(options.body) || options.__binaryRequest && typeof options.body === "string" ? options.body : isMultipartBody(options.body) ? options.body.body : options.body ? JSON.stringify(options.body, null, 2) : null;
116601
116783
  const contentLength = this.calculateContentLength(body);
116602
- const url2 = this.buildURL(path60, query);
116784
+ const url2 = this.buildURL(path61, query);
116603
116785
  if ("timeout" in options)
116604
116786
  validatePositiveInteger("timeout", options.timeout);
116605
116787
  options.timeout = options.timeout ?? this.timeout;
@@ -116723,8 +116905,8 @@ var APIClient = class {
116723
116905
  const request = this.makeRequest(options, null);
116724
116906
  return new PagePromise(this, request, Page3);
116725
116907
  }
116726
- buildURL(path60, query) {
116727
- const url2 = isAbsoluteURL(path60) ? new URL(path60) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path60.startsWith("/") ? path60.slice(1) : path60));
116908
+ buildURL(path61, query) {
116909
+ const url2 = isAbsoluteURL(path61) ? new URL(path61) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path61.startsWith("/") ? path61.slice(1) : path61));
116728
116910
  const defaultQuery = this.defaultQuery();
116729
116911
  if (!isEmptyObj(defaultQuery)) {
116730
116912
  query = { ...defaultQuery, ...query };
@@ -119135,9 +119317,9 @@ function toAnthropicTools(tools, opts = {}) {
119135
119317
 
119136
119318
  // ../plugin-provider-anthropic/dist/provider.js
119137
119319
  var anthropicModels = [
119138
- { id: "claude-opus-4-7", contextWindow: 8e5, maxOutputTokens: 8e3, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true },
119139
- { id: "claude-sonnet-4-6", contextWindow: 2e5, maxOutputTokens: 8e3, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true },
119140
- { id: "claude-haiku-4-5-20251001", contextWindow: 2e5, maxOutputTokens: 8e3, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true }
119320
+ { id: "claude-opus-4-7", contextWindow: 1e6, maxOutputTokens: 128e3, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true },
119321
+ { id: "claude-sonnet-4-6", contextWindow: 1e6, maxOutputTokens: 64e3, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true },
119322
+ { id: "claude-haiku-4-5-20251001", contextWindow: 2e5, maxOutputTokens: 64e3, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true }
119141
119323
  ];
119142
119324
  var AnthropicProvider = class {
119143
119325
  name;
@@ -119396,9 +119578,13 @@ var AnthropicProvider = class {
119396
119578
  try {
119397
119579
  const result = await this.client.messages.countTokens({
119398
119580
  model: req.model || this.defaultModel,
119399
- system: systemForCount,
119581
+ ...systemForCount !== void 0 ? { system: systemForCount } : {},
119582
+ // NARROW cast: our hand-rolled message/tool shapes carry `media_type:
119583
+ // string`, which the SDK narrows to a literal union it can't see we
119584
+ // never violate. The method itself is fully typed (no `as unknown` on
119585
+ // the resource anymore); only these two args need the structural cast.
119400
119586
  messages,
119401
- tools
119587
+ ...tools !== void 0 ? { tools } : {}
119402
119588
  });
119403
119589
  return result.input_tokens;
119404
119590
  } catch {
@@ -119883,13 +120069,13 @@ var MultipartBody2 = class {
119883
120069
  }
119884
120070
  };
119885
120071
  var fileFromPathWarned2 = false;
119886
- async function fileFromPath5(path60, ...args) {
120072
+ async function fileFromPath5(path61, ...args) {
119887
120073
  const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
119888
120074
  if (!fileFromPathWarned2) {
119889
- console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path60)}) instead`);
120075
+ console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path61)}) instead`);
119890
120076
  fileFromPathWarned2 = true;
119891
120077
  }
119892
- return await _fileFromPath(path60, ...args);
120078
+ return await _fileFromPath(path61, ...args);
119893
120079
  }
119894
120080
  var defaultHttpAgent2 = new import_agentkeepalive2.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
119895
120081
  var defaultHttpsAgent2 = new import_agentkeepalive2.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
@@ -120667,29 +120853,29 @@ var APIClient2 = class {
120667
120853
  defaultIdempotencyKey() {
120668
120854
  return `stainless-node-retry-${uuid42()}`;
120669
120855
  }
120670
- get(path60, opts) {
120671
- return this.methodRequest("get", path60, opts);
120856
+ get(path61, opts) {
120857
+ return this.methodRequest("get", path61, opts);
120672
120858
  }
120673
- post(path60, opts) {
120674
- return this.methodRequest("post", path60, opts);
120859
+ post(path61, opts) {
120860
+ return this.methodRequest("post", path61, opts);
120675
120861
  }
120676
- patch(path60, opts) {
120677
- return this.methodRequest("patch", path60, opts);
120862
+ patch(path61, opts) {
120863
+ return this.methodRequest("patch", path61, opts);
120678
120864
  }
120679
- put(path60, opts) {
120680
- return this.methodRequest("put", path60, opts);
120865
+ put(path61, opts) {
120866
+ return this.methodRequest("put", path61, opts);
120681
120867
  }
120682
- delete(path60, opts) {
120683
- return this.methodRequest("delete", path60, opts);
120868
+ delete(path61, opts) {
120869
+ return this.methodRequest("delete", path61, opts);
120684
120870
  }
120685
- methodRequest(method, path60, opts) {
120871
+ methodRequest(method, path61, opts) {
120686
120872
  return this.request(Promise.resolve(opts).then(async (opts2) => {
120687
120873
  const body = opts2 && isBlobLike2(opts2?.body) ? new DataView(await opts2.body.arrayBuffer()) : opts2?.body instanceof DataView ? opts2.body : opts2?.body instanceof ArrayBuffer ? new DataView(opts2.body) : opts2 && ArrayBuffer.isView(opts2?.body) ? new DataView(opts2.body.buffer) : opts2?.body;
120688
- return { method, path: path60, ...opts2, body };
120874
+ return { method, path: path61, ...opts2, body };
120689
120875
  }));
120690
120876
  }
120691
- getAPIList(path60, Page3, opts) {
120692
- return this.requestAPIList(Page3, { method: "get", path: path60, ...opts });
120877
+ getAPIList(path61, Page3, opts) {
120878
+ return this.requestAPIList(Page3, { method: "get", path: path61, ...opts });
120693
120879
  }
120694
120880
  calculateContentLength(body) {
120695
120881
  if (typeof body === "string") {
@@ -120708,10 +120894,10 @@ var APIClient2 = class {
120708
120894
  }
120709
120895
  buildRequest(inputOptions, { retryCount = 0 } = {}) {
120710
120896
  const options = { ...inputOptions };
120711
- const { method, path: path60, query, headers = {} } = options;
120897
+ const { method, path: path61, query, headers = {} } = options;
120712
120898
  const body = ArrayBuffer.isView(options.body) || options.__binaryRequest && typeof options.body === "string" ? options.body : isMultipartBody2(options.body) ? options.body.body : options.body ? JSON.stringify(options.body, null, 2) : null;
120713
120899
  const contentLength = this.calculateContentLength(body);
120714
- const url2 = this.buildURL(path60, query);
120900
+ const url2 = this.buildURL(path61, query);
120715
120901
  if ("timeout" in options)
120716
120902
  validatePositiveInteger2("timeout", options.timeout);
120717
120903
  options.timeout = options.timeout ?? this.timeout;
@@ -120827,8 +121013,8 @@ var APIClient2 = class {
120827
121013
  const request = this.makeRequest(options, null);
120828
121014
  return new PagePromise2(this, request, Page3);
120829
121015
  }
120830
- buildURL(path60, query) {
120831
- const url2 = isAbsoluteURL2(path60) ? new URL(path60) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path60.startsWith("/") ? path60.slice(1) : path60));
121016
+ buildURL(path61, query) {
121017
+ const url2 = isAbsoluteURL2(path61) ? new URL(path61) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path61.startsWith("/") ? path61.slice(1) : path61));
120832
121018
  const defaultQuery = this.defaultQuery();
120833
121019
  if (!isEmptyObj2(defaultQuery)) {
120834
121020
  query = { ...defaultQuery, ...query };
@@ -126216,11 +126402,11 @@ function buildAuthUrl(input) {
126216
126402
  }
126217
126403
  async function runAuthorizationCodeFlow(opts) {
126218
126404
  const port = opts.redirectPort ?? 8765;
126219
- const path60 = opts.redirectPath ?? "/callback";
126405
+ const path61 = opts.redirectPath ?? "/callback";
126220
126406
  const codeVerifier = generateCodeVerifier();
126221
126407
  const codeChallenge = computeCodeChallenge(codeVerifier);
126222
126408
  const state = generateState();
126223
- const redirectUri = `http://localhost:${port}${path60}`;
126409
+ const redirectUri = `http://localhost:${port}${path61}`;
126224
126410
  const authUrl = buildAuthUrl({
126225
126411
  authUrl: opts.authUrl,
126226
126412
  clientId: opts.clientId,
@@ -126232,7 +126418,7 @@ async function runAuthorizationCodeFlow(opts) {
126232
126418
  });
126233
126419
  const codePromise = waitForCallback({
126234
126420
  port,
126235
- path: path60,
126421
+ path: path61,
126236
126422
  expectedState: state,
126237
126423
  timeoutMs: opts.timeoutMs ?? 3e5,
126238
126424
  ...opts.signal ? { signal: opts.signal } : {}
@@ -126546,7 +126732,10 @@ function buildOauthAuthorizeTool(deps) {
126546
126732
  let tokens;
126547
126733
  if (mode === "device") {
126548
126734
  if (!input.deviceUrl) {
126549
- throw new Error('mode="device" requires `deviceUrl` (the provider\'s device-authorization endpoint)');
126735
+ throw new MoxxyError({
126736
+ code: "TOOL_ERROR",
126737
+ message: 'mode="device" requires `deviceUrl` (the provider\'s device-authorization endpoint)'
126738
+ });
126550
126739
  }
126551
126740
  tokens = await runDeviceCodeFlow({
126552
126741
  deviceUrl: input.deviceUrl,
@@ -126572,7 +126761,10 @@ function buildOauthAuthorizeTool(deps) {
126572
126761
  });
126573
126762
  } else {
126574
126763
  if (!input.authUrl) {
126575
- throw new Error('mode="loopback" requires `authUrl` (the provider\'s authorization endpoint)');
126764
+ throw new MoxxyError({
126765
+ code: "TOOL_ERROR",
126766
+ message: 'mode="loopback" requires `authUrl` (the provider\'s authorization endpoint)'
126767
+ });
126576
126768
  }
126577
126769
  if (input.noOpen) {
126578
126770
  const verifier = generateCodeVerifier();
@@ -126580,11 +126772,11 @@ function buildOauthAuthorizeTool(deps) {
126580
126772
  const { computeCodeChallenge: computeCodeChallenge2 } = await Promise.resolve().then(() => (init_pkce(), pkce_exports));
126581
126773
  const challenge = computeCodeChallenge2(verifier);
126582
126774
  const port = input.redirectPort ?? 8765;
126583
- const path60 = input.redirectPath ?? "/callback";
126775
+ const path61 = input.redirectPath ?? "/callback";
126584
126776
  const url2 = buildAuthUrl({
126585
126777
  authUrl: input.authUrl,
126586
126778
  clientId: input.clientId,
126587
- redirectUri: `http://localhost:${port}${path60}`,
126779
+ redirectUri: `http://localhost:${port}${path61}`,
126588
126780
  scopes: input.scopes,
126589
126781
  codeChallenge: challenge,
126590
126782
  state,
@@ -126726,7 +126918,7 @@ async function runOauthLogin(profile, ctx) {
126726
126918
  }
126727
126919
  async function runBrowserFlow(profile, ctx) {
126728
126920
  const port = profile.redirect?.port ?? 8765;
126729
- const path60 = profile.redirect?.path ?? "/callback";
126921
+ const path61 = profile.redirect?.path ?? "/callback";
126730
126922
  const serviceName = profile.displayName ?? profile.id;
126731
126923
  return runAuthorizationCodeFlow({
126732
126924
  authUrl: profile.authUrl,
@@ -126735,7 +126927,7 @@ async function runBrowserFlow(profile, ctx) {
126735
126927
  ...profile.clientSecret ? { clientSecret: profile.clientSecret } : {},
126736
126928
  scopes: profile.scopes,
126737
126929
  redirectPort: port,
126738
- redirectPath: path60,
126930
+ redirectPath: path61,
126739
126931
  ...profile.extraAuthParams ? { extraAuthParams: profile.extraAuthParams } : {},
126740
126932
  timeoutMs: DEFAULT_BROWSER_TIMEOUT_MS,
126741
126933
  ...ctx.signal ? { signal: ctx.signal } : {},
@@ -126747,7 +126939,7 @@ If your browser doesn't open automatically, paste this URL:
126747
126939
 
126748
126940
  ${url2}
126749
126941
 
126750
- Waiting for callback on http://localhost:${port}${path60} (5 min timeout)\u2026
126942
+ Waiting for callback on http://localhost:${port}${path61} (5 min timeout)\u2026
126751
126943
 
126752
126944
  `);
126753
126945
  }
@@ -133523,11 +133715,11 @@ function isAddrInUse(err) {
133523
133715
  return !!err && typeof err === "object" && err.code === "EADDRINUSE";
133524
133716
  }
133525
133717
  async function captureStdout(cmd, args) {
133526
- const { spawn: spawn17 } = await import('child_process');
133718
+ const { spawn: spawn14 } = await import('child_process');
133527
133719
  return await new Promise((resolve12) => {
133528
133720
  let out = "";
133529
133721
  try {
133530
- const child = spawn17(cmd, [...args], { stdio: ["ignore", "pipe", "ignore"] });
133722
+ const child = spawn14(cmd, [...args], { stdio: ["ignore", "pipe", "ignore"] });
133531
133723
  child.stdout.on("data", (b3) => {
133532
133724
  out += b3.toString();
133533
133725
  });
@@ -133903,161 +134095,30 @@ var WebChannel = class {
133903
134095
  }
133904
134096
  }
133905
134097
  };
133906
-
133907
- // ../plugin-channel-web/dist/child-cleanup.js
133908
- var live = /* @__PURE__ */ new Set();
133909
- var hooked = false;
133910
- function killChild(child) {
133911
- if (child.exitCode != null || child.signalCode != null)
133912
- return;
133913
- try {
133914
- child.kill("SIGTERM");
133915
- } catch {
133916
- }
133917
- const t2 = setTimeout(() => {
133918
- try {
133919
- child.kill("SIGKILL");
133920
- } catch {
133921
- }
133922
- }, 2e3);
133923
- t2.unref?.();
133924
- child.once("exit", () => clearTimeout(t2));
133925
- }
133926
- function ensureHook() {
133927
- if (hooked)
133928
- return;
133929
- hooked = true;
133930
- const killAll = () => {
133931
- for (const child of live) {
133932
- try {
133933
- child.kill("SIGKILL");
133934
- } catch {
133935
- }
133936
- }
133937
- live.clear();
133938
- };
133939
- process.once("exit", killAll);
133940
- process.once("SIGINT", killAll);
133941
- process.once("SIGTERM", killAll);
133942
- }
133943
- function trackChild(child) {
133944
- ensureHook();
133945
- live.add(child);
133946
- child.once("exit", () => live.delete(child));
133947
- return () => new Promise((resolve12) => {
133948
- live.delete(child);
133949
- if (child.exitCode != null || child.signalCode != null)
133950
- return resolve12();
133951
- child.once("exit", () => resolve12());
133952
- killChild(child);
133953
- });
133954
- }
133955
-
133956
- // ../plugin-channel-web/dist/cloudflared.js
133957
134098
  var TRYCLOUDFLARE_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
133958
134099
  var URL_TIMEOUT_MS = 3e4;
133959
- function parseTrycloudflareUrl(chunk) {
133960
- return TRYCLOUDFLARE_RE.exec(chunk)?.[0] ?? null;
133961
- }
133962
134100
  var cloudflaredTunnel = defineTunnelProvider({
133963
134101
  name: "cloudflared",
133964
- isAvailable: () => new Promise((resolve12) => {
133965
- const child = spawn("cloudflared", ["--version"]);
133966
- child.on("error", () => resolve12(false));
133967
- child.on("exit", (code) => resolve12(code === 0));
133968
- }),
133969
- open: ({ port, host }) => new Promise((resolve12, reject) => {
133970
- const child = spawn("cloudflared", ["tunnel", "--no-autoupdate", "--url", `http://${host}:${port}`]);
133971
- const untrack = trackChild(child);
133972
- let settled = false;
133973
- const timer = setTimeout(() => {
133974
- if (settled)
133975
- return;
133976
- settled = true;
133977
- void untrack();
133978
- reject(new Error("cloudflared: timed out waiting for the tunnel URL"));
133979
- }, URL_TIMEOUT_MS);
133980
- timer.unref?.();
133981
- const onData = (buf) => {
133982
- if (settled)
133983
- return;
133984
- const url2 = parseTrycloudflareUrl(buf.toString("utf8"));
133985
- if (!url2)
133986
- return;
133987
- settled = true;
133988
- clearTimeout(timer);
133989
- resolve12({ url: url2, close: untrack });
133990
- };
133991
- child.stderr?.on("data", onData);
133992
- child.stdout?.on("data", onData);
133993
- child.on("error", (err) => {
133994
- if (settled)
133995
- return;
133996
- settled = true;
133997
- clearTimeout(timer);
133998
- void untrack();
133999
- reject(err);
134000
- });
134001
- child.on("exit", (code) => {
134002
- if (settled)
134003
- return;
134004
- settled = true;
134005
- clearTimeout(timer);
134006
- reject(new Error(`cloudflared exited (code ${code}) before emitting a URL`));
134007
- });
134102
+ isAvailable: () => isCliTunnelAvailable("cloudflared"),
134103
+ open: ({ port, host }) => spawnCliTunnel({
134104
+ cmd: "cloudflared",
134105
+ args: ["tunnel", "--no-autoupdate", "--url", `http://${host}:${port}`],
134106
+ urlRegex: TRYCLOUDFLARE_RE,
134107
+ timeoutMs: URL_TIMEOUT_MS,
134108
+ name: "cloudflared"
134008
134109
  })
134009
134110
  });
134010
134111
  var NGROK_URL_RE = /https:\/\/[a-z0-9-]+\.ngrok(?:-free)?\.(?:app|io|dev)/i;
134011
134112
  var URL_TIMEOUT_MS2 = 3e4;
134012
- function parseNgrokUrl(chunk) {
134013
- return NGROK_URL_RE.exec(chunk)?.[0] ?? null;
134014
- }
134015
134113
  var ngrokTunnel = defineTunnelProvider({
134016
134114
  name: "ngrok",
134017
- isAvailable: () => new Promise((resolve12) => {
134018
- const child = spawn("ngrok", ["--version"]);
134019
- child.on("error", () => resolve12(false));
134020
- child.on("exit", (code) => resolve12(code === 0));
134021
- }),
134022
- open: ({ port }) => new Promise((resolve12, reject) => {
134023
- const child = spawn("ngrok", ["http", String(port), "--log", "stdout", "--log-format", "json"]);
134024
- const untrack = trackChild(child);
134025
- let settled = false;
134026
- const timer = setTimeout(() => {
134027
- if (settled)
134028
- return;
134029
- settled = true;
134030
- void untrack();
134031
- reject(new Error("ngrok: timed out waiting for the tunnel URL"));
134032
- }, URL_TIMEOUT_MS2);
134033
- timer.unref?.();
134034
- const onData = (buf) => {
134035
- if (settled)
134036
- return;
134037
- const url2 = parseNgrokUrl(buf.toString("utf8"));
134038
- if (!url2)
134039
- return;
134040
- settled = true;
134041
- clearTimeout(timer);
134042
- resolve12({ url: url2, close: untrack });
134043
- };
134044
- child.stdout?.on("data", onData);
134045
- child.stderr?.on("data", onData);
134046
- child.on("error", (err) => {
134047
- if (settled)
134048
- return;
134049
- settled = true;
134050
- clearTimeout(timer);
134051
- void untrack();
134052
- reject(err);
134053
- });
134054
- child.on("exit", (code) => {
134055
- if (settled)
134056
- return;
134057
- settled = true;
134058
- clearTimeout(timer);
134059
- reject(new Error(`ngrok exited (code ${code}) before emitting a URL`));
134060
- });
134115
+ isAvailable: () => isCliTunnelAvailable("ngrok"),
134116
+ open: ({ port }) => spawnCliTunnel({
134117
+ cmd: "ngrok",
134118
+ args: ["http", String(port), "--log", "stdout", "--log-format", "json"],
134119
+ urlRegex: NGROK_URL_RE,
134120
+ timeoutMs: URL_TIMEOUT_MS2,
134121
+ name: "ngrok"
134061
134122
  })
134062
134123
  });
134063
134124
  var webSettingsSchema = z$1.object({ tunnel: z$1.string().optional() });
@@ -134310,6 +134371,9 @@ async function createWebSocketTransportServer(opts) {
134310
134371
  for (const client of wss.clients)
134311
134372
  client.terminate();
134312
134373
  },
134374
+ clientCount() {
134375
+ return connections;
134376
+ },
134313
134377
  close() {
134314
134378
  return new Promise((resolve12) => {
134315
134379
  for (const client of wss.clients)
@@ -134640,7 +134704,8 @@ function isRunnerUp(socketPath = runnerSocketPath()) {
134640
134704
 
134641
134705
  // ../runner/dist/server.js
134642
134706
  init_dist();
134643
- var RUNNER_PROTOCOL_VERSION = 3;
134707
+ var RUNNER_PROTOCOL_VERSION = 5;
134708
+ var MIN_COMPATIBLE_PROTOCOL_VERSION = 1;
134644
134709
  var RunnerMethod = {
134645
134710
  /** client->server: handshake; returns the initial info snapshot. */
134646
134711
  Attach: "attach",
@@ -134684,6 +134749,18 @@ var RunnerMethod = {
134684
134749
  WorkflowSetEnabled: "workflow.setEnabled",
134685
134750
  /** client->server: run a workflow now. */
134686
134751
  WorkflowRun: "workflow.run",
134752
+ /** client->server: validate a draft workflow YAML (builder). */
134753
+ WorkflowValidateDraft: "workflow.validateDraft",
134754
+ /** client->server: persist a workflow from full YAML (builder). */
134755
+ WorkflowSave: "workflow.save",
134756
+ /** client->server: fetch one saved workflow as canonical YAML (builder). */
134757
+ WorkflowGetRun: "workflow.getRun",
134758
+ /**
134759
+ * client->server: answer a paused workflow's `awaitInput` question and resume
134760
+ * the run (human-in-the-loop). v5 — the client gates this on the server's
134761
+ * reported version so an older runner returns an actionable error.
134762
+ */
134763
+ WorkflowResume: "workflow.resume",
134687
134764
  /** server->client: ask this client to decide a tool-call permission. */
134688
134765
  PermissionCheck: "permission.check",
134689
134766
  /** server->client: ask this client to confirm an approval checkpoint. */
@@ -134760,6 +134837,18 @@ var workflowSetEnabledParamsSchema = z.object({
134760
134837
  enabled: z.boolean()
134761
134838
  });
134762
134839
  var workflowRunParamsSchema = z.object({ name: z.string() });
134840
+ var workflowValidateDraftParamsSchema = z.object({
134841
+ yaml: z.string().min(1).max(1e6)
134842
+ });
134843
+ var workflowSaveParamsSchema = z.object({
134844
+ yaml: z.string().min(1).max(1e6),
134845
+ previousName: z.string().min(1).max(120).optional()
134846
+ });
134847
+ var workflowGetRunParamsSchema = z.object({ name: z.string().min(1).max(120) });
134848
+ var workflowResumeParamsSchema = z.object({
134849
+ runId: z.string().min(1).max(120),
134850
+ reply: z.string().min(1).max(1e5)
134851
+ });
134763
134852
 
134764
134853
  // ../runner/dist/server.js
134765
134854
  var RunnerServer = class {
@@ -134836,6 +134925,10 @@ var RunnerServer = class {
134836
134925
  peer.handle(RunnerMethod.WorkflowList, () => this.handleWorkflowList());
134837
134926
  peer.handle(RunnerMethod.WorkflowSetEnabled, (raw) => this.handleWorkflowSetEnabled(raw));
134838
134927
  peer.handle(RunnerMethod.WorkflowRun, (raw) => this.handleWorkflowRun(raw));
134928
+ peer.handle(RunnerMethod.WorkflowValidateDraft, (raw) => this.handleWorkflowValidateDraft(raw));
134929
+ peer.handle(RunnerMethod.WorkflowSave, (raw) => this.handleWorkflowSave(raw));
134930
+ peer.handle(RunnerMethod.WorkflowGetRun, (raw) => this.handleWorkflowGetRun(raw));
134931
+ peer.handle(RunnerMethod.WorkflowResume, (raw) => this.handleWorkflowResume(raw));
134839
134932
  peer.onClose(() => this.onDisconnect(client));
134840
134933
  }
134841
134934
  onDisconnect(client) {
@@ -134847,7 +134940,7 @@ var RunnerServer = class {
134847
134940
  // --- request handlers ----------------------------------------------------
134848
134941
  handleAttach(client, raw) {
134849
134942
  const params = attachParamsSchema.parse(raw);
134850
- if (params.protocolVersion !== RUNNER_PROTOCOL_VERSION) {
134943
+ if (params.protocolVersion < MIN_COMPATIBLE_PROTOCOL_VERSION) {
134851
134944
  throw new Error(`runner protocol mismatch: server v${RUNNER_PROTOCOL_VERSION}, client v${params.protocolVersion}`);
134852
134945
  }
134853
134946
  client.role = params.role;
@@ -135066,6 +135159,40 @@ var RunnerServer = class {
135066
135159
  throw new Error("workflows plugin not loaded");
135067
135160
  return view.run(params.name);
135068
135161
  }
135162
+ // --- Workflows builder (validate / save / getRun) ------------------------
135163
+ // Optional on the view (older hosts / pre-builder plugins lack them), so
135164
+ // feature-check and throw a clear error rather than calling undefined.
135165
+ async handleWorkflowValidateDraft(raw) {
135166
+ const params = workflowValidateDraftParamsSchema.parse(raw);
135167
+ const view = this.session.workflows;
135168
+ if (!view?.validateDraft)
135169
+ throw new Error("workflows builder not supported on this runner");
135170
+ return view.validateDraft(params.yaml);
135171
+ }
135172
+ async handleWorkflowSave(raw) {
135173
+ const params = workflowSaveParamsSchema.parse(raw);
135174
+ const view = this.session.workflows;
135175
+ if (!view?.save)
135176
+ throw new Error("workflows builder not supported on this runner");
135177
+ return view.save(params.yaml, params.previousName);
135178
+ }
135179
+ async handleWorkflowGetRun(raw) {
135180
+ const params = workflowGetRunParamsSchema.parse(raw);
135181
+ const view = this.session.workflows;
135182
+ if (!view?.getRun)
135183
+ throw new Error("workflows builder not supported on this runner");
135184
+ return await view.getRun(params.name) ?? null;
135185
+ }
135186
+ // --- Workflows human-in-the-loop (resume a paused awaitInput run) ---------
135187
+ // v5. Optional on the view (older hosts lack it), so feature-check and throw a
135188
+ // clear error rather than calling undefined.
135189
+ async handleWorkflowResume(raw) {
135190
+ const params = workflowResumeParamsSchema.parse(raw);
135191
+ const view = this.session.workflows;
135192
+ if (!view?.resume)
135193
+ throw new Error("workflow resume not supported on this runner");
135194
+ return view.resume(params.runId, params.reply);
135195
+ }
135069
135196
  broadcastInfo() {
135070
135197
  this.broadcast(RunnerNotification.InfoChanged, { info: this.session.getInfo() });
135071
135198
  }
@@ -135208,6 +135335,14 @@ var RemoteSession = class {
135208
135335
  permissionResolver = null;
135209
135336
  approvalResolver = null;
135210
135337
  info = null;
135338
+ /**
135339
+ * The protocol version the SERVER reported at attach. Defaults to our own
135340
+ * version until the handshake resolves. Version-specific client methods (the
135341
+ * v4 workflow *builder* family) gate on this so a newer client attached to an
135342
+ * older runner degrades with a clear, actionable error instead of a raw
135343
+ * JSON-RPC method-not-found. Null until attached.
135344
+ */
135345
+ serverProtocolVersion = null;
135211
135346
  constructor(transport) {
135212
135347
  this.peer = new JsonRpcPeer(transport);
135213
135348
  this.peer.on(RunnerNotification.Event, (params) => {
@@ -135276,6 +135411,28 @@ var RemoteSession = class {
135276
135411
  sinceSeq
135277
135412
  });
135278
135413
  this.info = result.info;
135414
+ this.serverProtocolVersion = typeof result.protocolVersion === "number" ? result.protocolVersion : RUNNER_PROTOCOL_VERSION;
135415
+ }
135416
+ /**
135417
+ * The protocol version the attached runner speaks (its own, from the
135418
+ * handshake). Lets a capability-detecting caller (e.g. the desktop's visual
135419
+ * builder, see #146) decide whether a version-gated feature is available on
135420
+ * THIS runner before invoking it. Null until attached.
135421
+ */
135422
+ get runnerProtocolVersion() {
135423
+ return this.serverProtocolVersion;
135424
+ }
135425
+ /**
135426
+ * Guard a method that only exists on a server at/after `minVersion`. Throws a
135427
+ * clear, actionable error (not a raw JSON-RPC method-not-found) when the
135428
+ * attached runner is older — the desktop case after a JS hot-update outran
135429
+ * its bundled CLI.
135430
+ */
135431
+ requireServerProtocol(minVersion, feature) {
135432
+ const server = this.serverProtocolVersion;
135433
+ if (server !== null && server < minVersion) {
135434
+ throw new Error(`${feature} is not supported by this runner (runner protocol v${server}, needs v${minVersion}) \u2014 update the moxxy CLI to continue.`);
135435
+ }
135279
135436
  }
135280
135437
  get id() {
135281
135438
  return this.requireInfo().sessionId;
@@ -135508,7 +135665,40 @@ var RemoteSession = class {
135508
135665
  enabled
135509
135666
  });
135510
135667
  },
135511
- run: (name) => this.peer.request(RunnerMethod.WorkflowRun, { name })
135668
+ run: (name) => this.peer.request(RunnerMethod.WorkflowRun, { name }),
135669
+ // Builder methods (protocol v4): forward to the runner so the desktop's
135670
+ // RemoteSession-backed visual builder can validate/save/load drafts.
135671
+ // Gated on the SERVER's reported version so a v4 client on a v3 runner
135672
+ // (a desktop whose JS hot-update outran its bundled CLI) gets a clear
135673
+ // "update the CLI" error instead of a raw method-not-found.
135674
+ validateDraft: async (yaml) => {
135675
+ this.requireServerProtocol(4, "The workflows builder");
135676
+ return this.peer.request(RunnerMethod.WorkflowValidateDraft, {
135677
+ yaml
135678
+ });
135679
+ },
135680
+ save: async (yaml, previousName) => {
135681
+ this.requireServerProtocol(4, "Saving a workflow from the builder");
135682
+ return this.peer.request(RunnerMethod.WorkflowSave, {
135683
+ yaml,
135684
+ ...previousName ? { previousName } : {}
135685
+ });
135686
+ },
135687
+ getRun: async (name) => {
135688
+ this.requireServerProtocol(4, "Loading a workflow into the builder");
135689
+ return this.peer.request(RunnerMethod.WorkflowGetRun, { name });
135690
+ },
135691
+ // Human-in-the-loop resume (protocol v5). Gated on the SERVER's reported
135692
+ // version so a v5 client attached to a v4 runner (a desktop whose JS
135693
+ // hot-update outran its bundled CLI) gets a clear "update the CLI" error
135694
+ // rather than a raw method-not-found.
135695
+ resume: async (runId, reply2) => {
135696
+ this.requireServerProtocol(5, "Resuming a paused workflow");
135697
+ return this.peer.request(RunnerMethod.WorkflowResume, {
135698
+ runId,
135699
+ reply: reply2
135700
+ });
135701
+ }
135512
135702
  };
135513
135703
  }
135514
135704
  };
@@ -135575,11 +135765,14 @@ async function connectRemoteSession(opts = {}) {
135575
135765
  throw err;
135576
135766
  }
135577
135767
  }
135768
+ function isProtocolMismatchError(err) {
135769
+ const msg = err instanceof Error ? err.message : String(err);
135770
+ return /protocol mismatch/i.test(msg);
135771
+ }
135578
135772
  async function maybeRecoverFromMismatch(err, socketPath, opts) {
135579
135773
  if (opts.transport || opts.skipMismatchRecovery)
135580
135774
  return;
135581
- const msg = err instanceof Error ? err.message : String(err);
135582
- if (!/protocol mismatch/i.test(msg))
135775
+ if (!isProtocolMismatchError(err))
135583
135776
  return;
135584
135777
  try {
135585
135778
  await killAndUnlinkRunner(socketPath, [...DEFAULT_RUNNER_PORTS, ...opts.extraPortsToFree ?? []]);
@@ -135614,11 +135807,11 @@ async function isMoxxyProcess(pid) {
135614
135807
  return command.length > 0 && /moxxy/i.test(command);
135615
135808
  }
135616
135809
  async function pidCommand2(pid) {
135617
- const { spawn: spawn17 } = await import('child_process');
135810
+ const { spawn: spawn14 } = await import('child_process');
135618
135811
  return await new Promise((resolve12) => {
135619
135812
  let out = "";
135620
135813
  try {
135621
- const child = spawn17("ps", ["-p", String(pid), "-o", "command="], {
135814
+ const child = spawn14("ps", ["-p", String(pid), "-o", "command="], {
135622
135815
  stdio: ["ignore", "pipe", "ignore"]
135623
135816
  });
135624
135817
  child.stdout.on("data", (b3) => {
@@ -135656,11 +135849,11 @@ async function pidsListeningOnPort(port) {
135656
135849
  return await runLsof(["-t", `-iTCP:${port}`, "-sTCP:LISTEN"]);
135657
135850
  }
135658
135851
  async function runLsof(args) {
135659
- const { spawn: spawn17 } = await import('child_process');
135852
+ const { spawn: spawn14 } = await import('child_process');
135660
135853
  return await new Promise((resolve12) => {
135661
135854
  let out = "";
135662
135855
  try {
135663
- const child = spawn17("lsof", [...args], {
135856
+ const child = spawn14("lsof", [...args], {
135664
135857
  stdio: ["ignore", "pipe", "ignore"]
135665
135858
  });
135666
135859
  child.stdout.on("data", (b3) => {
@@ -135684,8 +135877,8 @@ function parsePids(out) {
135684
135877
  }
135685
135878
  async function unlinkSocket(socketPath) {
135686
135879
  try {
135687
- const fs43 = await import('fs');
135688
- fs43.unlinkSync(socketPath);
135880
+ const fs44 = await import('fs');
135881
+ fs44.unlinkSync(socketPath);
135689
135882
  } catch {
135690
135883
  }
135691
135884
  }
@@ -135704,13 +135897,44 @@ async function connectWithRetry(socketPath, retries) {
135704
135897
  }
135705
135898
 
135706
135899
  // ../desktop-ipc-contract/dist/index.js
135707
- var REMOTE_DISALLOWED_COMMANDS = /* @__PURE__ */ new Set([
135708
- "desks.pickFolder",
135709
- "session.pickAttachment",
135710
- "focus.close",
135711
- "focus.restoreMain",
135712
- "focus.resize",
135713
- "app.relaunch"
135900
+ var REMOTE_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
135901
+ // Answer a permission/approval prompt — RESPOND only. Note that
135902
+ // `session.setAutoApprove` (turn the prompt OFF entirely) is deliberately NOT
135903
+ // here: a remote client may answer the desktop user's prompts, never disable
135904
+ // them and run tools unattended.
135905
+ "ask.respond",
135906
+ // Workspace discovery + reconnect (read-only / non-mutating).
135907
+ "connection.snapshotAll",
135908
+ "connection.activeWorkspace",
135909
+ "connection.retry",
135910
+ // The conversation itself.
135911
+ "session.info",
135912
+ "session.runTurn",
135913
+ "session.abortTurn",
135914
+ "session.setMode",
135915
+ "session.newSession",
135916
+ "session.runCommand",
135917
+ // Voice input (capability-probed; transcribe fails coded without a transcriber).
135918
+ "session.hasTranscriber",
135919
+ "session.transcribe",
135920
+ // Per-workspace transcript log (the mobile ChatStoreBridge persists through
135921
+ // these; they're scoped to a workspace's NDJSON log, not host config).
135922
+ "chat.append",
135923
+ "chat.loadSegment",
135924
+ "chat.clearLog",
135925
+ "chat.migrate",
135926
+ // Workflows: READ + run an existing one only. Authoring (`workflows.save`,
135927
+ // `workflows.validateDraft`, `workflows.setEnabled`) is host-only — a paired
135928
+ // phone must not rewrite or re-enable the host's workflows.
135929
+ "workflows.list",
135930
+ "workflows.run",
135931
+ "workflows.getRun",
135932
+ // Answer a paused workflow's awaitInput question. This is RESPOND-only — like
135933
+ // `ask.respond`, the operator answers a question the WORKFLOW asked (the reply
135934
+ // is fed into the paused step and the run continues); it cannot create or
135935
+ // rewrite a workflow. A mobile user answering "ship it" to their own pipeline
135936
+ // is the canonical human-in-the-loop case, so it belongs on the trust surface.
135937
+ "workflows.resume"
135714
135938
  ]);
135715
135939
  var providerName = z.string().regex(/^[a-z][a-z0-9-]{0,63}$/, "invalid provider name");
135716
135940
  var httpUrl = z.string().refine((s2) => {
@@ -135725,6 +135949,9 @@ var skillName = z.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9 ._/-]*$
135725
135949
  var vaultKeyName = z.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/, "invalid vault key name").refine((s2) => !s2.includes(".."), 'vault key name may not contain ".."');
135726
135950
  var optionalWorkspace = z.string().min(1).max(256).optional();
135727
135951
  var MAX_AUDIO_BASE64 = 4e7;
135952
+ var MAX_INLINE_ATTACHMENT_CONTENT = 12e6;
135953
+ var commandName = z.string().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "invalid command name");
135954
+ var workflowName = z.string().min(1).max(200).refine((s2) => !s2.includes("..") && !s2.includes("/") && !s2.includes("\\"), "invalid workflow name");
135728
135955
  var ipcInputSchemas = {
135729
135956
  // No-arg, but spawns a child process (npm install) — pin the payload to
135730
135957
  // "nothing" so a hostile renderer can't smuggle args across.
@@ -135769,7 +135996,40 @@ var ipcInputSchemas = {
135769
135996
  attachments: z.array(z.object({
135770
135997
  path: z.string().min(1).max(4096),
135771
135998
  name: z.string().min(1).max(1024)
135772
- })).max(64).optional()
135999
+ })).max(64).optional(),
136000
+ // Inline attachments cross the wire as payload (remote/mobile clients) —
136001
+ // bound both the entry count and per-entry content size.
136002
+ inlineAttachments: z.array(z.object({
136003
+ kind: z.enum(["stdin", "file", "image", "document", "audio"]),
136004
+ content: z.string().max(MAX_INLINE_ATTACHMENT_CONTENT),
136005
+ name: z.string().max(1024).optional(),
136006
+ mediaType: z.string().max(128).optional()
136007
+ })).max(8).optional()
136008
+ }),
136009
+ // Runs an arbitrary registered slash command — the audit flagged this as the
136010
+ // one mutating session command without a schema, so lock the name to a
136011
+ // registry slug and bound the free-text args.
136012
+ "session.runCommand": z.object({
136013
+ workspaceId: optionalWorkspace,
136014
+ name: commandName,
136015
+ args: z.string().max(1e4)
136016
+ }),
136017
+ "workflows.run": z.object({ name: workflowName }),
136018
+ "workflows.setEnabled": z.object({ name: workflowName, enabled: z.boolean() }),
136019
+ // Builder commands. validateDraft/save take full YAML — bound the size so a
136020
+ // hostile renderer can't OOM the host; save writes to disk so it's
136021
+ // filesystem-touching and gets a boundary check like the other writers.
136022
+ "workflows.validateDraft": z.object({ yaml: z.string().min(1).max(1e6) }),
136023
+ "workflows.save": z.object({
136024
+ yaml: z.string().min(1).max(1e6),
136025
+ previousName: workflowName.optional()
136026
+ }),
136027
+ "workflows.getRun": z.object({ name: workflowName }),
136028
+ // Human-in-the-loop resume: bound the run id + the operator reply (the reply
136029
+ // is forwarded into the paused step's child agent, so cap it to avoid OOM).
136030
+ "workflows.resume": z.object({
136031
+ runId: z.string().min(1).max(120),
136032
+ reply: z.string().min(1).max(1e5)
135773
136033
  }),
135774
136034
  // Security-sensitive: this bypasses the approval sheet, so validate it at
135775
136035
  // the boundary like the other dangerous commands.
@@ -135795,8 +136055,14 @@ var ipcInputSchemas = {
135795
136055
  onboardingComplete: z.boolean().optional(),
135796
136056
  clerkUserId: z.string().max(256).nullable().optional(),
135797
136057
  clerkDisplayName: z.string().max(256).nullable().optional(),
135798
- signedInAt: z.number().nullable().optional()
136058
+ signedInAt: z.number().nullable().optional(),
136059
+ mobileGatewayEnabled: z.boolean().optional()
135799
136060
  }).strict(),
136061
+ // Mobile-gateway control. Both no-arg variants pin the payload to "nothing"
136062
+ // so a hostile caller can't smuggle args; setEnabled is a strict boolean.
136063
+ "mobileGateway.status": z.undefined(),
136064
+ "mobileGateway.rotateToken": z.undefined(),
136065
+ "mobileGateway.setEnabled": z.object({ enabled: z.boolean() }).strict(),
135800
136066
  "chat.append": z.object({
135801
136067
  workspaceId: z.string().min(1).max(256),
135802
136068
  events: z.array(z.unknown()).max(1e4)
@@ -135877,6 +136143,11 @@ async function dispatch(channel, args, fn) {
135877
136143
  var WebSocketCommandBus = class {
135878
136144
  methods = /* @__PURE__ */ new Map();
135879
136145
  peers = /* @__PURE__ */ new Set();
136146
+ /** Deny-by-default allow-list (null ⇒ no filter; see the constructor). */
136147
+ allowedCommands;
136148
+ constructor(opts = {}) {
136149
+ this.allowedCommands = opts.allowedCommands === void 0 ? REMOTE_ALLOWED_COMMANDS : opts.allowedCommands;
136150
+ }
135880
136151
  handle(channel, fn) {
135881
136152
  this.methods.set(channel, fn);
135882
136153
  }
@@ -135889,7 +136160,7 @@ var WebSocketCommandBus = class {
135889
136160
  const peer = new JsonRpcPeer(transport);
135890
136161
  for (const [channel, fn] of this.methods) {
135891
136162
  peer.handle(channel, async (params) => {
135892
- if (REMOTE_DISALLOWED_COMMANDS.has(channel)) {
136163
+ if (this.allowedCommands && !this.allowedCommands.has(channel)) {
135893
136164
  const message = `command "${channel}" is not available over a remote transport`;
135894
136165
  throw new RpcError(message, { code: "runner-error", message });
135895
136166
  }
@@ -135983,7 +136254,74 @@ var MobileSessionHost = class {
135983
136254
  this.bus.handle("session.setAutoApprove", async ({ enabled }) => {
135984
136255
  this.autoApprove = enabled;
135985
136256
  });
135986
- this.bus.handle("session.hasTranscriber", async () => false);
136257
+ this.bus.handle("session.setMode", async ({ mode }) => {
136258
+ this.session.modes.setActive(mode);
136259
+ this.bus.broadcast("connection.changed", { workspaceId: ws, phase: this.snapshot().phase });
136260
+ });
136261
+ this.bus.handle("session.newSession", async () => {
136262
+ for (const controller of this.turns.values())
136263
+ controller.abort();
136264
+ if (this.session.reset)
136265
+ await this.session.reset();
136266
+ else
136267
+ this.session.log.clear();
136268
+ });
136269
+ this.bus.handle("session.runCommand", async ({ name, args }) => {
136270
+ const def = this.session.commands.get(name);
136271
+ if (!def)
136272
+ return { kind: "error", message: `unknown command: /${name}` };
136273
+ return await def.handler({
136274
+ channel: "mobile",
136275
+ sessionId: this.session.getInfo().sessionId,
136276
+ args,
136277
+ session: this.session
136278
+ });
136279
+ });
136280
+ this.bus.handle("session.hasTranscriber", async () => this.activeTranscriber() != null);
136281
+ this.bus.handle("session.transcribe", async ({ audioBase64, mimeType }) => {
136282
+ const transcriber = this.activeTranscriber();
136283
+ if (!transcriber) {
136284
+ throw new IpcError("not-supported", "no transcriber is active on this session");
136285
+ }
136286
+ const audio = Buffer.from(audioBase64, "base64");
136287
+ const result = await transcriber.transcribe(audio, mimeType ? { mimeType } : void 0);
136288
+ return result.text;
136289
+ });
136290
+ this.bus.handle("workflows.list", async () => this.session.workflows?.list() ?? []);
136291
+ this.bus.handle("workflows.setEnabled", async ({ name, enabled }) => {
136292
+ if (this.session.workflows)
136293
+ await this.session.workflows.setEnabled(name, enabled);
136294
+ });
136295
+ this.bus.handle("workflows.run", async ({ name }) => {
136296
+ if (!this.session.workflows) {
136297
+ throw new IpcError("not-supported", "workflows plugin not loaded on this session");
136298
+ }
136299
+ return await this.session.workflows.run(name);
136300
+ });
136301
+ this.bus.handle("workflows.validateDraft", async ({ yaml }) => {
136302
+ if (!this.session.workflows?.validateDraft) {
136303
+ throw new IpcError("not-supported", "workflows builder not supported on this session");
136304
+ }
136305
+ return await this.session.workflows.validateDraft(yaml);
136306
+ });
136307
+ this.bus.handle("workflows.save", async ({ yaml }) => {
136308
+ if (!this.session.workflows?.save) {
136309
+ throw new IpcError("not-supported", "workflows builder not supported on this session");
136310
+ }
136311
+ return await this.session.workflows.save(yaml);
136312
+ });
136313
+ this.bus.handle("workflows.getRun", async ({ name }) => {
136314
+ if (!this.session.workflows?.getRun) {
136315
+ throw new IpcError("not-supported", "workflows builder not supported on this session");
136316
+ }
136317
+ return await this.session.workflows.getRun(name);
136318
+ });
136319
+ this.bus.handle("workflows.resume", async ({ runId, reply: reply2 }) => {
136320
+ if (!this.session.workflows?.resume) {
136321
+ throw new IpcError("not-supported", "workflow resume not supported on this session");
136322
+ }
136323
+ return await this.session.workflows.resume(runId, reply2);
136324
+ });
135987
136325
  this.bus.handle("ask.respond", async ({ requestId, response }) => {
135988
136326
  this.answerAsk(requestId, response);
135989
136327
  });
@@ -136025,6 +136363,15 @@ var MobileSessionHost = class {
136025
136363
  this.session.setApprovalResolver(null);
136026
136364
  }
136027
136365
  // ---- internals ----------------------------------------------------------
136366
+ /** Active transcriber, or null. Guarded access — a thin/remote session may
136367
+ * leave the registry view undefined (capability-absent ≠ crash). */
136368
+ activeTranscriber() {
136369
+ try {
136370
+ return this.session.transcribers?.tryGetActive() ?? null;
136371
+ } catch {
136372
+ return null;
136373
+ }
136374
+ }
136028
136375
  snapshot() {
136029
136376
  const info = this.session.getInfo();
136030
136377
  const phase = {
@@ -136045,7 +136392,8 @@ var MobileSessionHost = class {
136045
136392
  try {
136046
136393
  for await (const _event of this.session.runTurn(args.prompt, {
136047
136394
  signal: controller.signal,
136048
- ...args.model ? { model: args.model } : {}
136395
+ ...args.model ? { model: args.model } : {},
136396
+ ...args.inlineAttachments && args.inlineAttachments.length > 0 ? { attachments: args.inlineAttachments } : {}
136049
136397
  })) {
136050
136398
  void _event;
136051
136399
  }
@@ -136084,19 +136432,6 @@ function resolveMobileToken(configured) {
136084
136432
  function rotateMobileToken() {
136085
136433
  return rotateChannelToken({ fileName: TOKEN_FILE });
136086
136434
  }
136087
- function normalizeTunnelChoice(raw) {
136088
- const v3 = (process.env.MOXXY_MOBILE_TUNNEL ?? raw ?? "localhost").trim().toLowerCase();
136089
- if (v3 === "cloudflared" || v3 === "ngrok")
136090
- return v3;
136091
- return "localhost";
136092
- }
136093
- function tunnelProviderFor(choice) {
136094
- if (choice === "cloudflared")
136095
- return cloudflaredTunnel;
136096
- if (choice === "ngrok")
136097
- return ngrokTunnel;
136098
- return null;
136099
- }
136100
136435
  function resolveBindHost(configured) {
136101
136436
  const v3 = (process.env.MOXXY_MOBILE_HOST ?? configured ?? "").trim();
136102
136437
  return v3.length > 0 ? v3 : "127.0.0.1";
@@ -136133,6 +136468,21 @@ function buildConnectUrl(opts) {
136133
136468
  return `ws://${opts.localHost}:${opts.port}/?t=${t2}`;
136134
136469
  }
136135
136470
 
136471
+ // ../plugin-channel-mobile/dist/tunnel.js
136472
+ function normalizeTunnelChoice(raw) {
136473
+ const v3 = (process.env.MOXXY_MOBILE_TUNNEL ?? raw ?? "localhost").trim().toLowerCase();
136474
+ if (v3 === "cloudflared" || v3 === "ngrok")
136475
+ return v3;
136476
+ return "localhost";
136477
+ }
136478
+ function tunnelProviderFor(choice) {
136479
+ if (choice === "cloudflared")
136480
+ return cloudflaredTunnel;
136481
+ if (choice === "ngrok")
136482
+ return ngrokTunnel;
136483
+ return null;
136484
+ }
136485
+
136136
136486
  // ../plugin-channel-mobile/dist/qr.js
136137
136487
  var import_qrcode = __toESM(require_lib3());
136138
136488
  async function printConnectInfo(url2, token, hint) {
@@ -136196,7 +136546,7 @@ var MobileChannel = class {
136196
136546
  return this.token;
136197
136547
  }
136198
136548
  async start(startOpts) {
136199
- const bus = new WebSocketCommandBus();
136549
+ const bus = new WebSocketCommandBus({ allowedCommands: null });
136200
136550
  const host = new MobileSessionHost(bus, startOpts.session);
136201
136551
  this.host = host;
136202
136552
  host.register();
@@ -137691,10 +138041,10 @@ function buildUsageStatsPlugin(opts = {}) {
137691
138041
  cursor = ctx.log.length;
137692
138042
  },
137693
138043
  async onShutdown(ctx) {
137694
- const live2 = ctx.log.slice(cursor);
137695
- if (live2.length === 0)
138044
+ const live = ctx.log.slice(cursor);
138045
+ if (live.length === 0)
137696
138046
  return;
137697
- const delta = summarizeTokensByModel(live2);
138047
+ const delta = summarizeTokensByModel(live);
137698
138048
  await mergeUsageStats(delta, opts.statsPath);
137699
138049
  }
137700
138050
  }
@@ -138835,11 +139185,11 @@ var ScheduleStore = class {
138835
139185
  * Used by the workflows integration to mirror a workflow's `on.schedule`
138836
139186
  * into the shared poller without a separate timer.
138837
139187
  */
138838
- async syncWorkflowSchedule(workflowName, entry) {
139188
+ async syncWorkflowSchedule(workflowName2, entry) {
138839
139189
  await this.mutate((schedules) => {
138840
- const filtered = schedules.filter((s2) => !(s2.source === "workflow" && s2.workflowName === workflowName));
139190
+ const filtered = schedules.filter((s2) => !(s2.source === "workflow" && s2.workflowName === workflowName2));
138841
139191
  if (entry) {
138842
- filtered.push(scheduleEntrySchema.parse({ ...entry, source: "workflow", workflowName }));
139192
+ filtered.push(scheduleEntrySchema.parse({ ...entry, source: "workflow", workflowName: workflowName2 }));
138843
139193
  }
138844
139194
  return filtered;
138845
139195
  });
@@ -139282,7 +139632,7 @@ function readHeader(headers, name) {
139282
139632
  return v3[0] ?? null;
139283
139633
  return v3 ?? null;
139284
139634
  }
139285
- function readJsonPath(body, path60) {
139635
+ function readJsonPath(body, path61) {
139286
139636
  let parsed;
139287
139637
  try {
139288
139638
  parsed = JSON.parse(body.toString("utf8"));
@@ -139290,7 +139640,7 @@ function readJsonPath(body, path60) {
139290
139640
  return null;
139291
139641
  }
139292
139642
  let cur = parsed;
139293
- for (const seg of path60.split(".")) {
139643
+ for (const seg of path61.split(".")) {
139294
139644
  if (cur === null || cur === void 0 || typeof cur !== "object")
139295
139645
  return null;
139296
139646
  cur = cur[seg];
@@ -139905,88 +140255,52 @@ function redactVerification(v3) {
139905
140255
  }
139906
140256
  var CLOUDFLARED_URL_RE = /https:\/\/[A-Za-z0-9.-]+\.trycloudflare\.com/;
139907
140257
  var NGROK_URL_RE2 = /https:\/\/[A-Za-z0-9.-]+\.ngrok[a-z.-]*\.app/;
139908
- async function isTunnelCliAvailable(kind3) {
139909
- return new Promise((resolve12) => {
139910
- const child = spawn(kind3, ["--version"], { stdio: "ignore" });
139911
- child.once("error", () => resolve12(false));
139912
- child.once("exit", (code) => resolve12(code === 0));
140258
+ var SPAWN_CONFIG = {
140259
+ cloudflared: {
140260
+ cmd: "cloudflared",
140261
+ urlRegex: CLOUDFLARED_URL_RE,
140262
+ args: (port, host) => ["tunnel", "--no-autoupdate", "--url", `http://${host}:${port}`]
140263
+ },
140264
+ ngrok: {
140265
+ cmd: "ngrok",
140266
+ urlRegex: NGROK_URL_RE2,
140267
+ args: (port) => ["http", String(port), "--log=stdout"]
140268
+ }
140269
+ };
140270
+ function openCliTunnel(kind3, port, host, timeoutMs) {
140271
+ const cfg = SPAWN_CONFIG[kind3];
140272
+ return spawnCliTunnel({
140273
+ cmd: cfg.cmd,
140274
+ args: cfg.args(port, host),
140275
+ urlRegex: cfg.urlRegex,
140276
+ name: kind3,
140277
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
139913
140278
  });
139914
140279
  }
140280
+ var webhookTunnelProviders = {
140281
+ cloudflared: defineTunnelProvider({
140282
+ name: "cloudflared",
140283
+ isAvailable: () => isCliTunnelAvailable("cloudflared"),
140284
+ open: ({ port, host }) => openCliTunnel("cloudflared", port, host)
140285
+ }),
140286
+ ngrok: defineTunnelProvider({
140287
+ name: "ngrok",
140288
+ isAvailable: () => isCliTunnelAvailable("ngrok"),
140289
+ open: ({ port, host }) => openCliTunnel("ngrok", port, host)
140290
+ })
140291
+ };
140292
+ function isTunnelCliAvailable(kind3) {
140293
+ const provider = webhookTunnelProviders[kind3];
140294
+ return provider.isAvailable ? provider.isAvailable() : Promise.resolve(false);
140295
+ }
139915
140296
  async function startTunnel(opts) {
139916
140297
  const host = opts.host ?? "127.0.0.1";
139917
- const target = `http://${host}:${opts.port}`;
139918
- const args = opts.kind === "cloudflared" ? ["tunnel", "--no-autoupdate", "--url", target, ...opts.extraArgs ?? []] : ["http", `${opts.port}`, "--log=stdout", ...opts.extraArgs ?? []];
139919
- const timeoutMs = opts.urlTimeoutMs ?? 3e4;
139920
- let child;
139921
- try {
139922
- child = spawn(opts.kind, args, { stdio: ["ignore", "pipe", "pipe"] });
139923
- } catch (err) {
139924
- throw new Error(`failed to spawn ${opts.kind} \u2014 is it installed? (${err instanceof Error ? err.message : String(err)})`);
139925
- }
139926
- const urlRe = opts.kind === "cloudflared" ? CLOUDFLARED_URL_RE : NGROK_URL_RE2;
139927
- const urlPromise = new Promise((resolve12, reject) => {
139928
- let settled = false;
139929
- const onChunk = (buf) => {
139930
- const match = urlRe.exec(buf.toString("utf8"));
139931
- if (match && !settled) {
139932
- settled = true;
139933
- resolve12(match[0]);
139934
- }
139935
- };
139936
- child.stdout?.on("data", onChunk);
139937
- child.stderr?.on("data", onChunk);
139938
- child.once("error", (err) => {
139939
- if (!settled) {
139940
- settled = true;
139941
- reject(err);
139942
- }
139943
- });
139944
- child.once("exit", (code) => {
139945
- if (!settled) {
139946
- settled = true;
139947
- reject(new Error(`${opts.kind} exited before printing a URL (code=${code ?? "null"})`));
139948
- }
139949
- });
139950
- setTimeout(() => {
139951
- if (!settled) {
139952
- settled = true;
139953
- reject(new Error(`timed out waiting ${timeoutMs}ms for ${opts.kind} URL`));
139954
- }
139955
- }, timeoutMs).unref?.();
139956
- });
139957
- let url2;
139958
- try {
139959
- url2 = await urlPromise;
139960
- } catch (err) {
139961
- try {
139962
- child.kill("SIGTERM");
139963
- } catch {
139964
- }
139965
- throw err;
139966
- }
139967
- const pid = child.pid ?? -1;
140298
+ const handle2 = await openCliTunnel(opts.kind, opts.port, host, opts.urlTimeoutMs);
139968
140299
  return {
139969
140300
  kind: opts.kind,
139970
- url: url2,
139971
- pid,
139972
- stop: async () => {
139973
- if (!child.killed && child.exitCode === null) {
139974
- child.kill("SIGTERM");
139975
- try {
139976
- await Promise.race([
139977
- once(child, "exit"),
139978
- new Promise((r2) => setTimeout(r2, 2e3).unref?.())
139979
- ]);
139980
- } catch {
139981
- }
139982
- if (!child.killed && child.exitCode === null) {
139983
- try {
139984
- child.kill("SIGKILL");
139985
- } catch {
139986
- }
139987
- }
139988
- }
139989
- }
140301
+ url: handle2.url,
140302
+ pid: handle2.pid,
140303
+ stop: () => handle2.close()
139990
140304
  };
139991
140305
  }
139992
140306
  function defaultWebhookSecretsDir() {
@@ -141926,23 +142240,123 @@ function validateCondition(expr) {
141926
142240
  }
141927
142241
 
141928
142242
  // ../plugin-workflows/dist/schema.js
141929
- var ACTION_KEYS = ["skill", "prompt", "tool", "workflow"];
142243
+ var ACTION_KEYS = [
142244
+ "skill",
142245
+ "prompt",
142246
+ "tool",
142247
+ "workflow",
142248
+ "bridge",
142249
+ "condition",
142250
+ "switch",
142251
+ "loop"
142252
+ ];
141930
142253
  var SLUG_RE = /^[a-z0-9][a-z0-9-]*$/i;
141931
142254
  var STEP_ID_RE = /^[a-z0-9][a-z0-9_-]*$/i;
142255
+ var loopActionSchema = z.object({
142256
+ body: z.array(z.string().min(1)).min(1),
142257
+ condition: z.string().min(1),
142258
+ maxIterations: z.number().int().min(1).max(50).default(10)
142259
+ });
141932
142260
  var stepSchema = z.object({
141933
142261
  id: z.string().min(1).max(80).regex(STEP_ID_RE, "step id must be slug-like"),
141934
142262
  skill: z.string().min(1).optional(),
141935
142263
  prompt: z.string().min(1).optional(),
141936
142264
  tool: z.string().min(1).optional(),
141937
142265
  workflow: z.string().min(1).optional(),
142266
+ bridge: z.string().min(1).optional(),
142267
+ condition: z.string().min(1).optional(),
142268
+ then: z.array(z.string().min(1)).optional(),
142269
+ else: z.array(z.string().min(1)).optional(),
142270
+ switch: z.string().min(1).optional(),
142271
+ cases: z.record(z.array(z.string().min(1))).optional(),
142272
+ default: z.array(z.string().min(1)).optional(),
142273
+ loop: loopActionSchema.optional(),
141938
142274
  input: z.string().optional(),
141939
142275
  args: z.record(z.unknown()).optional(),
141940
142276
  needs: z.array(z.string().min(1)).default([]),
141941
142277
  when: z.string().min(1).optional(),
141942
142278
  onError: z.enum(["fail", "continue", "retry"]).default("fail"),
141943
142279
  retries: z.number().int().min(0).max(3).default(0),
141944
- label: z.string().max(60).optional()
142280
+ label: z.string().max(60).optional(),
142281
+ format: z.enum(["json", "plain"]).optional(),
142282
+ awaitInput: z.boolean().optional()
141945
142283
  }).superRefine((step, ctx) => {
142284
+ const isLogic = step.bridge != null || step.condition != null || step.switch != null;
142285
+ if (step.awaitInput && (step.tool != null || step.workflow != null || step.loop != null || isLogic)) {
142286
+ ctx.addIssue({
142287
+ code: z.ZodIssueCode.custom,
142288
+ message: `step "${step.id}": awaitInput is only allowed on prompt or skill steps`,
142289
+ path: ["awaitInput"]
142290
+ });
142291
+ }
142292
+ if (step.format === "plain" && step.bridge == null) {
142293
+ ctx.addIssue({
142294
+ code: z.ZodIssueCode.custom,
142295
+ message: `step "${step.id}": format plain is only allowed on bridge steps`,
142296
+ path: ["format"]
142297
+ });
142298
+ }
142299
+ if (step.condition != null) {
142300
+ if (step.then == null) {
142301
+ ctx.addIssue({
142302
+ code: z.ZodIssueCode.custom,
142303
+ message: `step "${step.id}": condition requires then`,
142304
+ path: ["then"]
142305
+ });
142306
+ }
142307
+ if (step.else == null) {
142308
+ ctx.addIssue({
142309
+ code: z.ZodIssueCode.custom,
142310
+ message: `step "${step.id}": condition requires else`,
142311
+ path: ["else"]
142312
+ });
142313
+ }
142314
+ }
142315
+ if (step.switch != null) {
142316
+ const caseKeys = Object.keys(step.cases ?? {});
142317
+ if (caseKeys.length === 0) {
142318
+ ctx.addIssue({
142319
+ code: z.ZodIssueCode.custom,
142320
+ message: `step "${step.id}": switch requires at least one case`,
142321
+ path: ["cases"]
142322
+ });
142323
+ }
142324
+ }
142325
+ if (step.loop != null && (step.then != null || step.else != null || step.cases != null || step.default != null)) {
142326
+ ctx.addIssue({
142327
+ code: z.ZodIssueCode.custom,
142328
+ message: `step "${step.id}": loop cannot be combined with then/else/cases/default`,
142329
+ path: ["loop"]
142330
+ });
142331
+ }
142332
+ if (step.then != null && step.condition == null) {
142333
+ ctx.addIssue({
142334
+ code: z.ZodIssueCode.custom,
142335
+ message: `step "${step.id}": then is only valid with condition`,
142336
+ path: ["then"]
142337
+ });
142338
+ }
142339
+ if (step.else != null && step.condition == null) {
142340
+ ctx.addIssue({
142341
+ code: z.ZodIssueCode.custom,
142342
+ message: `step "${step.id}": else is only valid with condition`,
142343
+ path: ["else"]
142344
+ });
142345
+ }
142346
+ if (step.cases != null && step.switch == null) {
142347
+ ctx.addIssue({
142348
+ code: z.ZodIssueCode.custom,
142349
+ message: `step "${step.id}": cases is only valid with switch`,
142350
+ path: ["cases"]
142351
+ });
142352
+ }
142353
+ if (step.default != null && step.switch == null) {
142354
+ ctx.addIssue({
142355
+ code: z.ZodIssueCode.custom,
142356
+ message: `step "${step.id}": default branch list is only valid with switch`,
142357
+ path: ["default"]
142358
+ });
142359
+ }
141946
142360
  const present2 = ACTION_KEYS.filter((k3) => step[k3] != null);
141947
142361
  if (present2.length === 0) {
141948
142362
  ctx.addIssue({
@@ -141972,6 +142386,17 @@ var inputSpecSchema = z.object({
141972
142386
  default: z.unknown().optional(),
141973
142387
  description: z.string().optional()
141974
142388
  });
142389
+ var uiLayoutSchema = z.object({
142390
+ nodes: z.record(z.object({
142391
+ x: z.number(),
142392
+ y: z.number()
142393
+ })).default({}),
142394
+ viewport: z.object({
142395
+ x: z.number(),
142396
+ y: z.number(),
142397
+ zoom: z.number().positive()
142398
+ }).optional()
142399
+ });
141975
142400
  var workflowSchema = z.object({
141976
142401
  name: z.string().min(1).max(120).regex(SLUG_RE, "name must be slug-like"),
141977
142402
  description: z.string().min(1),
@@ -141983,6 +142408,9 @@ var workflowSchema = z.object({
141983
142408
  channel: z.string().optional(),
141984
142409
  inbox: z.boolean().default(true)
141985
142410
  }).optional(),
142411
+ ui: z.object({
142412
+ layout: uiLayoutSchema.optional()
142413
+ }).optional(),
141986
142414
  concurrency: z.number().int().min(1).max(8).default(4),
141987
142415
  steps: z.array(stepSchema).min(1).max(40)
141988
142416
  }).superRefine((wf, ctx) => {
@@ -142028,6 +142456,119 @@ var workflowSchema = z.object({
142028
142456
  });
142029
142457
  }
142030
142458
  }
142459
+ for (const step of wf.steps) {
142460
+ const branchIds = [];
142461
+ if (step.then)
142462
+ branchIds.push(...step.then);
142463
+ if (step.else)
142464
+ branchIds.push(...step.else);
142465
+ if (step.cases)
142466
+ for (const list of Object.values(step.cases))
142467
+ branchIds.push(...list);
142468
+ if (step.default)
142469
+ branchIds.push(...step.default);
142470
+ for (const ref of branchIds) {
142471
+ if (!ids.has(ref)) {
142472
+ ctx.addIssue({
142473
+ code: z.ZodIssueCode.custom,
142474
+ message: `step "${step.id}" references unknown branch step "${ref}"`,
142475
+ path: ["steps"]
142476
+ });
142477
+ }
142478
+ if (ref === step.id) {
142479
+ ctx.addIssue({
142480
+ code: z.ZodIssueCode.custom,
142481
+ message: `step "${step.id}" cannot reference itself in then/else/cases/default`,
142482
+ path: ["steps"]
142483
+ });
142484
+ }
142485
+ }
142486
+ }
142487
+ for (const step of wf.steps) {
142488
+ if (step.loop == null)
142489
+ continue;
142490
+ for (const ref of step.loop.body) {
142491
+ if (!ids.has(ref)) {
142492
+ ctx.addIssue({
142493
+ code: z.ZodIssueCode.custom,
142494
+ message: `step "${step.id}" loop references unknown body step "${ref}"`,
142495
+ path: ["steps"]
142496
+ });
142497
+ }
142498
+ }
142499
+ }
142500
+ const stepById = new Map(wf.steps.map((s2) => [s2.id, s2]));
142501
+ const bodyOwner = /* @__PURE__ */ new Map();
142502
+ for (const step of wf.steps) {
142503
+ if (step.loop == null)
142504
+ continue;
142505
+ for (const ref of step.loop.body)
142506
+ bodyOwner.set(ref, step.id);
142507
+ }
142508
+ for (const step of wf.steps) {
142509
+ const owner = bodyOwner.get(step.id);
142510
+ if (owner == null)
142511
+ continue;
142512
+ const body = stepById.get(step.id);
142513
+ if (body.condition != null || body.switch != null) {
142514
+ ctx.addIssue({
142515
+ code: z.ZodIssueCode.custom,
142516
+ message: `step "${step.id}" is a loop body of "${owner}" and cannot be a condition/switch step \u2014 branch routing is not honored inside a loop body. Use a bridge step to set vars and drive the loop's own exit condition instead.`,
142517
+ path: ["steps"]
142518
+ });
142519
+ }
142520
+ if (body.awaitInput) {
142521
+ ctx.addIssue({
142522
+ code: z.ZodIssueCode.custom,
142523
+ message: `step "${step.id}" is a loop body of "${owner}" and cannot use awaitInput \u2014 a loop body cannot pause mid-iteration. Ask the operator before/after the loop instead.`,
142524
+ path: ["steps"]
142525
+ });
142526
+ }
142527
+ if (body.when != null) {
142528
+ ctx.addIssue({
142529
+ code: z.ZodIssueCode.custom,
142530
+ message: `step "${step.id}" is a loop body of "${owner}" and cannot have a \`when\` guard \u2014 loop body steps run unconditionally each iteration. Gate the loop via its \`condition\` (exit) instead.`,
142531
+ path: ["steps"]
142532
+ });
142533
+ }
142534
+ for (const dep of body.needs) {
142535
+ if (dep === owner)
142536
+ continue;
142537
+ if (bodyOwner.get(dep) === owner)
142538
+ continue;
142539
+ ctx.addIssue({
142540
+ code: z.ZodIssueCode.custom,
142541
+ message: `step "${step.id}" is a loop body of "${owner}" and may only \`needs\` its loop step ("${owner}") or a sibling body step of the same loop \u2014 "${dep}" is outside the loop and would never settle for it.`,
142542
+ path: ["steps"]
142543
+ });
142544
+ }
142545
+ }
142546
+ for (const step of wf.steps) {
142547
+ if (bodyOwner.has(step.id))
142548
+ continue;
142549
+ if (step.loop != null) {
142550
+ const ownBody = new Set(step.loop.body);
142551
+ for (const dep of step.needs) {
142552
+ if (bodyOwner.has(dep) && !ownBody.has(dep)) {
142553
+ ctx.addIssue({
142554
+ code: z.ZodIssueCode.custom,
142555
+ message: `step "${step.id}" needs "${dep}", which is a loop body of "${bodyOwner.get(dep)}" \u2014 depend on the loop step "${bodyOwner.get(dep)}" instead.`,
142556
+ path: ["steps"]
142557
+ });
142558
+ }
142559
+ }
142560
+ continue;
142561
+ }
142562
+ for (const dep of step.needs) {
142563
+ if (bodyOwner.has(dep)) {
142564
+ ctx.addIssue({
142565
+ code: z.ZodIssueCode.custom,
142566
+ message: `step "${step.id}" needs "${dep}", which is a loop body of "${bodyOwner.get(dep)}" \u2014 depend on the loop step "${bodyOwner.get(dep)}" instead (loop body steps are owned by their loop and never scheduled in the main DAG).`,
142567
+ path: ["steps"]
142568
+ });
142569
+ }
142570
+ }
142571
+ }
142031
142572
  });
142032
142573
  function findCycle(steps) {
142033
142574
  const byId = new Map(steps.map((s2) => [s2.id, s2]));
@@ -142064,8 +142605,8 @@ function findCycle(steps) {
142064
142605
  }
142065
142606
  function formatIssues(error2) {
142066
142607
  return error2.issues.map((iss) => {
142067
- const path60 = iss.path.join(".") || "(root)";
142068
- return `${path60}: ${iss.message}`;
142608
+ const path61 = iss.path.join(".") || "(root)";
142609
+ return `${path61}: ${iss.message}`;
142069
142610
  });
142070
142611
  }
142071
142612
  function validateWorkflow(raw) {
@@ -142195,9 +142736,18 @@ var WorkflowStore = class {
142195
142736
  /**
142196
142737
  * Replace a workflow with a new definition. In-place rewrite for user/project
142197
142738
  * workflows; a user-scope override for builtin/plugin ones.
142739
+ *
142740
+ * `previousName` supports rename from the builder: when the workflow's name
142741
+ * changed, the old user/project file and its registry entry are removed so a
142742
+ * rename doesn't leave an orphaned duplicate file + stale entry behind.
142198
142743
  */
142199
- async save(workflow) {
142744
+ async save(workflow, previousName) {
142200
142745
  await this.ensureLoaded();
142746
+ const renamed = previousName != null && previousName !== workflow.name ? this.byName.get(previousName) : void 0;
142747
+ if (renamed && (renamed.scope === "user" || renamed.scope === "project")) {
142748
+ await promises.rm(renamed.path, { force: true });
142749
+ this.byName.delete(previousName);
142750
+ }
142201
142751
  const existing = this.byName.get(workflow.name);
142202
142752
  const editable = existing && (existing.scope === "user" || existing.scope === "project");
142203
142753
  if (existing && editable) {
@@ -142206,11 +142756,12 @@ var WorkflowStore = class {
142206
142756
  this.byName.set(workflow.name, entry2);
142207
142757
  return entry2;
142208
142758
  }
142209
- const dir = this.userDir();
142759
+ const scope = renamed?.scope === "project" ? "project" : "user";
142760
+ const dir = scope === "project" ? this.projectDir() : this.userDir();
142210
142761
  await promises.mkdir(dir, { recursive: true });
142211
142762
  const file = await uniqueFilename2(dir, workflow.name);
142212
142763
  await writeFileAtomic(file, serializeWorkflow(workflow));
142213
- const entry = { workflow, path: file, scope: "user" };
142764
+ const entry = { workflow, path: file, scope };
142214
142765
  this.byName.set(workflow.name, entry);
142215
142766
  return entry;
142216
142767
  }
@@ -142257,8 +142808,176 @@ async function exists3(p3) {
142257
142808
  return false;
142258
142809
  }
142259
142810
  }
142811
+
142812
+ // ../plugin-workflows/dist/run-store.js
142813
+ init_index_esm();
142814
+ var WorkflowRunStore = class {
142815
+ dir;
142816
+ constructor(dir = moxxyPath("workflow-runs", "active")) {
142817
+ this.dir = dir;
142818
+ }
142819
+ async save(checkpoint) {
142820
+ const runId = ulid();
142821
+ await promises.mkdir(this.dir, { recursive: true });
142822
+ const file = path3.join(this.dir, `${runId}.json`);
142823
+ const payload = { ...checkpoint, runId };
142824
+ const tmp = `${file}.tmp`;
142825
+ await promises.writeFile(tmp, JSON.stringify(payload), "utf8");
142826
+ await promises.rename(tmp, file);
142827
+ return runId;
142828
+ }
142829
+ async load(runId) {
142830
+ const file = path3.join(this.dir, `${runId}.json`);
142831
+ try {
142832
+ const raw = await promises.readFile(file, "utf8");
142833
+ return JSON.parse(raw);
142834
+ } catch {
142835
+ return null;
142836
+ }
142837
+ }
142838
+ async remove(runId) {
142839
+ const file = path3.join(this.dir, `${runId}.json`);
142840
+ try {
142841
+ await promises.unlink(file);
142842
+ } catch {
142843
+ }
142844
+ }
142845
+ /**
142846
+ * Delete checkpoint files older than `maxAgeMs` (by mtime). Paused runs that
142847
+ * are never resumed (the common case while `awaitInput` is gated — see
142848
+ * schema.ts — and in general after a crash) would otherwise accumulate
142849
+ * `<ulid>.json` files under `active/` forever. Returns the count removed.
142850
+ * Best-effort: a missing dir or an unreadable entry is skipped, not thrown.
142851
+ */
142852
+ async sweepStale(maxAgeMs = DEFAULT_CHECKPOINT_TTL_MS, now = Date.now()) {
142853
+ let removed = 0;
142854
+ let entries;
142855
+ try {
142856
+ entries = await promises.readdir(this.dir);
142857
+ } catch {
142858
+ return 0;
142859
+ }
142860
+ for (const name of entries) {
142861
+ if (!name.endsWith(".json"))
142862
+ continue;
142863
+ const file = path3.join(this.dir, name);
142864
+ try {
142865
+ const st3 = await promises.stat(file);
142866
+ if (now - st3.mtimeMs > maxAgeMs) {
142867
+ await promises.unlink(file);
142868
+ removed += 1;
142869
+ }
142870
+ } catch {
142871
+ }
142872
+ }
142873
+ return removed;
142874
+ }
142875
+ };
142876
+ var DEFAULT_CHECKPOINT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
142877
+ var defaultWorkflowRunStore = new WorkflowRunStore();
142878
+
142879
+ // ../plugin-workflows/dist/logic-response.js
142880
+ var PLAIN_PROMPT_MARKERS = [
142881
+ "odpowiedz wy\u0142\u0105cznie zwyk\u0142ym tekstem",
142882
+ "odpowiedz tylko zwyk\u0142ym tekstem",
142883
+ "without json",
142884
+ "no json",
142885
+ "plain text only"
142886
+ ];
142887
+ var LOGIC_SYSTEM_PROMPT = 'You are a workflow logic step. Do not use tools. Reply with exactly one JSON object. Use keys as needed: "vars" (object, data for downstream templates), "branch" (string, routing decision), "text" (optional human-readable summary). No markdown fences or commentary outside the JSON.';
142888
+ function logicSystemPrompt() {
142889
+ return LOGIC_SYSTEM_PROMPT;
142890
+ }
142891
+ function wantsPlainResponse(step) {
142892
+ if (step.format === "plain")
142893
+ return true;
142894
+ if (step.format === "json")
142895
+ return false;
142896
+ const text = (step.bridge ?? step.condition ?? step.switch ?? "").toLowerCase();
142897
+ return PLAIN_PROMPT_MARKERS.some((m3) => text.includes(m3));
142898
+ }
142899
+ function stripJsonFence(raw) {
142900
+ const trimmed = raw.trim();
142901
+ const fence = /^```(?:json)?\s*([\s\S]*?)```$/i.exec(trimmed);
142902
+ return fence ? fence[1].trim() : trimmed;
142903
+ }
142904
+ function parseJsonObject(raw) {
142905
+ const parsed = JSON.parse(stripJsonFence(raw));
142906
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
142907
+ throw new Error("logic step response must be a JSON object");
142908
+ }
142909
+ return parsed;
142910
+ }
142911
+ function parseLogicResponse(raw, _step, format) {
142912
+ if (format === "plain") {
142913
+ return { output: raw.trim() };
142914
+ }
142915
+ const obj = parseJsonObject(raw);
142916
+ const vars = obj.vars != null && typeof obj.vars === "object" && !Array.isArray(obj.vars) ? obj.vars : void 0;
142917
+ const branch = typeof obj.branch === "string" ? obj.branch.trim() : void 0;
142918
+ const text = typeof obj.text === "string" ? obj.text : void 0;
142919
+ const output = text ?? JSON.stringify(obj);
142920
+ return { output, ...vars ? { vars } : {}, ...branch ? { branch } : {} };
142921
+ }
142922
+ function resolveBranchForCondition(_step, branch) {
142923
+ if (!branch)
142924
+ return void 0;
142925
+ const b3 = branch.toLowerCase();
142926
+ if (b3 === "then")
142927
+ return "then";
142928
+ if (b3 === "else")
142929
+ return "else";
142930
+ return void 0;
142931
+ }
142932
+ function resolveBranchForSwitch(step, branch) {
142933
+ if (!branch)
142934
+ return void 0;
142935
+ const cases = step.cases ?? {};
142936
+ if (branch in cases)
142937
+ return branch;
142938
+ if (step.default && step.default.length > 0)
142939
+ return "__default__";
142940
+ return void 0;
142941
+ }
142942
+ function stepsToSkipForBranch(step, selected) {
142943
+ if (step.condition != null) {
142944
+ const then = step.then ?? [];
142945
+ const els = step.else ?? [];
142946
+ const active = selected === "then" ? then : els;
142947
+ const all = [...then, ...els];
142948
+ return all.filter((id) => !active.includes(id));
142949
+ }
142950
+ if (step.switch != null) {
142951
+ const cases = step.cases ?? {};
142952
+ const all = /* @__PURE__ */ new Set();
142953
+ for (const ids of Object.values(cases))
142954
+ for (const id of ids)
142955
+ all.add(id);
142956
+ for (const id of step.default ?? [])
142957
+ all.add(id);
142958
+ if (selected === "__default__") {
142959
+ const keep2 = new Set(step.default ?? []);
142960
+ return [...all].filter((id) => !keep2.has(id));
142961
+ }
142962
+ const keep = new Set(cases[selected] ?? []);
142963
+ return [...all].filter((id) => !keep.has(id));
142964
+ }
142965
+ return [];
142966
+ }
142967
+
142968
+ // ../plugin-workflows/dist/executor/dag.js
142260
142969
  var DAG_EXECUTOR_NAME = "dag";
142261
142970
  var MAX_NESTING_DEPTH = 5;
142971
+ var FINALIZE_REPLY_SUFFIX = "\n\nFinalize now: consolidate the operator's answers into a clear structured response. Include every field the step instructions require. Do not ask further questions.";
142972
+ function collectLoopBodyIds(workflow) {
142973
+ const ids = /* @__PURE__ */ new Set();
142974
+ for (const step of workflow.steps) {
142975
+ if (step.loop)
142976
+ for (const id of step.loop.body)
142977
+ ids.add(id);
142978
+ }
142979
+ return ids;
142980
+ }
142262
142981
  function nowFn(deps) {
142263
142982
  return deps.now ?? (() => Date.now());
142264
142983
  }
@@ -142274,27 +142993,100 @@ function resolveInputs(workflow, deps) {
142274
142993
  }
142275
142994
  return out;
142276
142995
  }
142277
- function buildScope(states, inputs, deps, nowIso) {
142996
+ function buildScope(ctx, nowIso) {
142278
142997
  const steps = {};
142279
- for (const [id, st3] of states)
142998
+ for (const [id, st3] of ctx.states)
142280
142999
  steps[id] = { output: st3.output };
142281
143000
  return {
142282
143001
  steps,
142283
- inputs,
142284
- ...deps.trigger != null ? { trigger: deps.trigger } : {},
143002
+ inputs: ctx.inputs,
143003
+ vars: ctx.vars,
143004
+ ...ctx.deps.trigger != null ? { trigger: ctx.deps.trigger } : {},
142285
143005
  now: nowIso
142286
143006
  };
142287
143007
  }
142288
- async function runExecutor(workflow, deps) {
142289
- const now = nowFn(deps);
142290
- const inputs = resolveInputs(workflow, deps);
143008
+ var UNSAFE_VAR_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
143009
+ function mergeVars(ctx, vars) {
143010
+ if (!vars)
143011
+ return;
143012
+ for (const [key, value] of Object.entries(vars)) {
143013
+ if (UNSAFE_VAR_KEYS.has(key)) {
143014
+ ctx.deps.logger?.warn?.("workflow vars: dropping prototype-pollution key", { key });
143015
+ continue;
143016
+ }
143017
+ Object.defineProperty(ctx.vars, key, {
143018
+ value,
143019
+ writable: true,
143020
+ enumerable: true,
143021
+ configurable: true
143022
+ });
143023
+ }
143024
+ }
143025
+ async function applyBranchSkips(ctx, gate, selected) {
143026
+ for (const id of stepsToSkipForBranch(gate, selected)) {
143027
+ const st3 = ctx.states.get(id);
143028
+ if (!st3 || st3.status !== "pending")
143029
+ continue;
143030
+ st3.status = "skipped";
143031
+ st3.startedAt = st3.endedAt = ctx.now();
143032
+ await ctx.deps.emit?.("workflow_step_skipped", { id, reason: `branch:${gate.id}` });
143033
+ }
143034
+ }
143035
+ function serializeStates(states) {
143036
+ const out = {};
143037
+ for (const [id, st3] of states) {
143038
+ out[id] = {
143039
+ status: st3.status === "pending" ? "pending" : st3.status,
143040
+ output: st3.output,
143041
+ ...st3.error ? { error: st3.error } : {},
143042
+ startedAt: st3.startedAt,
143043
+ endedAt: st3.endedAt
143044
+ };
143045
+ }
143046
+ return out;
143047
+ }
143048
+ function restoreStates(raw) {
142291
143049
  const states = /* @__PURE__ */ new Map();
142292
- for (const step of workflow.steps) {
142293
- states.set(step.id, { status: "pending", output: "", startedAt: 0, endedAt: 0 });
143050
+ for (const [id, st3] of Object.entries(raw)) {
143051
+ states.set(id, {
143052
+ status: st3.status,
143053
+ output: st3.output,
143054
+ ...st3.error ? { error: st3.error } : {},
143055
+ startedAt: st3.startedAt,
143056
+ endedAt: st3.endedAt
143057
+ });
142294
143058
  }
143059
+ return states;
143060
+ }
143061
+ function buildStepResults(workflow, states) {
143062
+ return workflow.steps.map((step) => {
143063
+ const st3 = states.get(step.id);
143064
+ const status = st3.status === "pending" ? "skipped" : st3.status;
143065
+ return {
143066
+ id: step.id,
143067
+ status,
143068
+ output: st3.output,
143069
+ ...st3.error ? { error: st3.error } : {},
143070
+ startedAt: st3.startedAt,
143071
+ endedAt: st3.endedAt
143072
+ };
143073
+ });
143074
+ }
143075
+ function buildRunResult(ctx, status, ok, extra) {
143076
+ const output = status === "completed" ? sinkOutput(ctx.workflow, ctx.states) : "";
143077
+ return {
143078
+ ok,
143079
+ status,
143080
+ steps: buildStepResults(ctx.workflow, ctx.states),
143081
+ output,
143082
+ ...extra
143083
+ };
143084
+ }
143085
+ async function runExecutorLoop(ctx) {
143086
+ const { workflow, deps } = ctx;
142295
143087
  await deps.emit?.("workflow_started", { name: workflow.name, steps: workflow.steps.length });
142296
143088
  const settled = (id) => {
142297
- const s2 = states.get(id)?.status;
143089
+ const s2 = ctx.states.get(id)?.status;
142298
143090
  return s2 === "completed" || s2 === "skipped" || s2 === "failed";
142299
143091
  };
142300
143092
  let aborted = false;
@@ -142308,21 +143100,23 @@ async function runExecutor(workflow, deps) {
142308
143100
  while (skippedSomething) {
142309
143101
  skippedSomething = false;
142310
143102
  for (const step of workflow.steps) {
142311
- const st3 = states.get(step.id);
143103
+ if (ctx.loopBodyIds.has(step.id))
143104
+ continue;
143105
+ const st3 = ctx.states.get(step.id);
142312
143106
  if (st3.status !== "pending")
142313
143107
  continue;
142314
143108
  if (!step.needs.every(settled))
142315
143109
  continue;
142316
143110
  if (step.when == null)
142317
143111
  continue;
142318
- const scope2 = buildScope(states, inputs, deps, new Date(now()).toISOString());
143112
+ const scope2 = buildScope(ctx, new Date(ctx.now()).toISOString());
142319
143113
  let keep;
142320
143114
  try {
142321
143115
  keep = evalCondition(step.when, scope2);
142322
143116
  } catch (err) {
142323
143117
  st3.status = "failed";
142324
143118
  st3.error = `when: ${err instanceof Error ? err.message : String(err)}`;
142325
- st3.startedAt = st3.endedAt = now();
143119
+ st3.startedAt = st3.endedAt = ctx.now();
142326
143120
  await deps.emit?.("workflow_step_failed", { id: step.id, error: st3.error });
142327
143121
  if (step.onError !== "continue") {
142328
143122
  aborted = true;
@@ -142333,7 +143127,7 @@ async function runExecutor(workflow, deps) {
142333
143127
  }
142334
143128
  if (!keep) {
142335
143129
  st3.status = "skipped";
142336
- st3.startedAt = st3.endedAt = now();
143130
+ st3.startedAt = st3.endedAt = ctx.now();
142337
143131
  await deps.emit?.("workflow_step_skipped", { id: step.id });
142338
143132
  skippedSomething = true;
142339
143133
  }
@@ -142344,11 +143138,13 @@ async function runExecutor(workflow, deps) {
142344
143138
  if (aborted)
142345
143139
  break;
142346
143140
  const ready = workflow.steps.filter((step) => {
142347
- const st3 = states.get(step.id);
143141
+ if (ctx.loopBodyIds.has(step.id))
143142
+ return false;
143143
+ const st3 = ctx.states.get(step.id);
142348
143144
  return st3.status === "pending" && step.needs.every(settled);
142349
143145
  });
142350
143146
  if (ready.length === 0) {
142351
- const anyPending = [...states.values()].some((s2) => s2.status === "pending");
143147
+ const anyPending = [...ctx.states.values()].some((s2) => s2.status === "pending");
142352
143148
  if (anyPending) {
142353
143149
  abortReason = "workflow stalled \u2014 no runnable steps (check needs/when)";
142354
143150
  aborted = true;
@@ -142356,19 +143152,62 @@ async function runExecutor(workflow, deps) {
142356
143152
  break;
142357
143153
  }
142358
143154
  const wave = ready.slice(0, Math.max(1, workflow.concurrency));
142359
- const scope = buildScope(states, inputs, deps, new Date(now()).toISOString());
142360
- await Promise.all(wave.map(async (step) => {
142361
- const st3 = states.get(step.id);
142362
- st3.startedAt = now();
143155
+ const scope = buildScope(ctx, new Date(ctx.now()).toISOString());
143156
+ for (const step of wave) {
143157
+ const st3 = ctx.states.get(step.id);
143158
+ st3.startedAt = ctx.now();
142363
143159
  await deps.emit?.("workflow_step_started", {
142364
143160
  id: step.id,
142365
143161
  label: step.label ?? step.id
142366
143162
  });
142367
- const outcome = await runStep(step, scope, deps);
142368
- st3.endedAt = now();
143163
+ const outcome = await runStep(step, scope, ctx);
143164
+ st3.endedAt = ctx.now();
143165
+ if (outcome.paused) {
143166
+ st3.status = "awaiting_input";
143167
+ st3.output = outcome.output;
143168
+ await deps.emit?.("workflow_step_awaiting_input", {
143169
+ id: step.id,
143170
+ label: step.label ?? step.id,
143171
+ preview: outcome.output.slice(0, 280),
143172
+ childSessionId: outcome.interactionAgentId
143173
+ });
143174
+ const store = deps.runStore ?? defaultWorkflowRunStore;
143175
+ const runId = await store.save({
143176
+ workflow,
143177
+ trigger: deps.trigger ?? "manual",
143178
+ inputs: ctx.inputs,
143179
+ states: serializeStates(ctx.states),
143180
+ // Persist vars set by logic steps that ran before this pause so a
143181
+ // resume restores them (otherwise downstream `{{ vars.x }}` is lost).
143182
+ vars: ctx.vars,
143183
+ pendingStepId: step.id,
143184
+ interactionAgentId: outcome.interactionAgentId,
143185
+ startedAt: ctx.now()
143186
+ });
143187
+ await deps.emit?.("workflow_paused", {
143188
+ runId,
143189
+ stepId: step.id,
143190
+ childSessionId: outcome.interactionAgentId,
143191
+ // Carry the human-facing question so the operator UI is self-contained
143192
+ // (no separate event correlation needed): the workflow name, the step
143193
+ // label, and the prompt/question the paused step asked.
143194
+ workflow: workflow.name,
143195
+ label: step.label ?? step.id,
143196
+ prompt: outcome.output.slice(0, 2e3)
143197
+ });
143198
+ return buildRunResult(ctx, "paused", true, {
143199
+ runId,
143200
+ pendingStepId: step.id,
143201
+ ...outcome.interactionAgentId ? { interactionAgentId: outcome.interactionAgentId } : {}
143202
+ });
143203
+ }
142369
143204
  if (outcome.ok) {
142370
143205
  st3.status = "completed";
142371
143206
  st3.output = outcome.output;
143207
+ mergeVars(ctx, outcome.vars);
143208
+ if (outcome.branchRoute != null) {
143209
+ await applyBranchSkips(ctx, step, outcome.branchRoute);
143210
+ }
142372
143211
  await deps.emit?.("workflow_step_completed", {
142373
143212
  id: step.id,
142374
143213
  preview: outcome.output.slice(0, 280)
@@ -142382,32 +143221,106 @@ async function runExecutor(workflow, deps) {
142382
143221
  abortReason = `step "${step.id}" failed: ${outcome.error}`;
142383
143222
  }
142384
143223
  }
142385
- }));
143224
+ }
142386
143225
  }
142387
- const stepResults = workflow.steps.map((step) => {
142388
- const st3 = states.get(step.id);
142389
- return {
142390
- id: step.id,
142391
- status: st3.status === "pending" ? "skipped" : st3.status,
142392
- output: st3.output,
142393
- ...st3.error ? { error: st3.error } : {},
142394
- startedAt: st3.startedAt,
142395
- endedAt: st3.endedAt
142396
- };
142397
- });
142398
143226
  const ok = !aborted;
142399
- const output = sinkOutput(workflow, states);
143227
+ const output = sinkOutput(workflow, ctx.states);
142400
143228
  if (ok) {
142401
143229
  await deps.emit?.("workflow_completed", { name: workflow.name, output: output.slice(0, 280) });
142402
- } else {
142403
- await deps.emit?.("workflow_failed", { name: workflow.name, error: abortReason });
143230
+ return buildRunResult(ctx, "completed", true, { output });
142404
143231
  }
142405
- return {
142406
- ok,
142407
- steps: stepResults,
143232
+ await deps.emit?.("workflow_failed", { name: workflow.name, error: abortReason });
143233
+ return buildRunResult(ctx, "failed", false, {
142408
143234
  output,
142409
- ...ok ? {} : { error: abortReason ?? "workflow failed" }
143235
+ error: abortReason ?? "workflow failed"
143236
+ });
143237
+ }
143238
+ async function runExecutor(workflow, deps) {
143239
+ const ctx = {
143240
+ workflow,
143241
+ deps,
143242
+ inputs: resolveInputs(workflow, deps),
143243
+ vars: {},
143244
+ states: /* @__PURE__ */ new Map(),
143245
+ now: nowFn(deps),
143246
+ loopBodyIds: collectLoopBodyIds(workflow)
142410
143247
  };
143248
+ for (const step of workflow.steps) {
143249
+ ctx.states.set(step.id, { status: "pending", output: "", startedAt: 0, endedAt: 0 });
143250
+ }
143251
+ return runExecutorLoop(ctx);
143252
+ }
143253
+ async function resumeWorkflowRun(runId, userMessage, deps, store = defaultWorkflowRunStore) {
143254
+ const checkpoint = await store.load(runId);
143255
+ if (!checkpoint) {
143256
+ return {
143257
+ ok: false,
143258
+ status: "failed",
143259
+ steps: [],
143260
+ output: "",
143261
+ error: `no paused workflow run "${runId}"`
143262
+ };
143263
+ }
143264
+ const depsWithStore = { ...deps, runStore: store };
143265
+ const step = checkpoint.workflow.steps.find((s2) => s2.id === checkpoint.pendingStepId);
143266
+ if (!step) {
143267
+ return {
143268
+ ok: false,
143269
+ status: "failed",
143270
+ steps: buildStepResults(checkpoint.workflow, restoreStates(checkpoint.states)),
143271
+ output: "",
143272
+ error: `paused step "${checkpoint.pendingStepId}" not found`
143273
+ };
143274
+ }
143275
+ const restoredVars = {};
143276
+ const ctx = {
143277
+ workflow: checkpoint.workflow,
143278
+ deps: depsWithStore,
143279
+ inputs: checkpoint.inputs,
143280
+ vars: restoredVars,
143281
+ states: restoreStates(checkpoint.states),
143282
+ now: nowFn(deps),
143283
+ loopBodyIds: collectLoopBodyIds(checkpoint.workflow)
143284
+ };
143285
+ mergeVars(ctx, checkpoint.vars);
143286
+ const st3 = ctx.states.get(step.id);
143287
+ await deps.emit?.("workflow_resumed", { runId, stepId: step.id });
143288
+ if (typeof deps.spawner.continue !== "function") {
143289
+ st3.status = "failed";
143290
+ st3.error = "subagent spawner does not support resume (continue)";
143291
+ st3.endedAt = ctx.now();
143292
+ await deps.emit?.("workflow_step_failed", { id: step.id, error: st3.error });
143293
+ await store.remove(runId);
143294
+ return buildRunResult(ctx, "failed", false, { error: st3.error });
143295
+ }
143296
+ const finalizePrompt = `Operator reply:
143297
+ ${userMessage.trim()}${FINALIZE_REPLY_SUFFIX}`;
143298
+ try {
143299
+ const child = await deps.spawner.continue({
143300
+ childSessionId: checkpoint.interactionAgentId,
143301
+ prompt: finalizePrompt,
143302
+ label: step.label ?? step.id
143303
+ });
143304
+ if (child.error)
143305
+ throw new Error(child.error.message);
143306
+ st3.status = "completed";
143307
+ st3.output = child.text;
143308
+ st3.endedAt = ctx.now();
143309
+ await deps.emit?.("workflow_step_completed", {
143310
+ id: step.id,
143311
+ preview: child.text.slice(0, 280)
143312
+ });
143313
+ } catch (err) {
143314
+ const message = err instanceof Error ? err.message : String(err);
143315
+ st3.status = "failed";
143316
+ st3.error = message;
143317
+ st3.endedAt = ctx.now();
143318
+ await deps.emit?.("workflow_step_failed", { id: step.id, error: message });
143319
+ await store.remove(runId);
143320
+ return buildRunResult(ctx, "failed", false, { error: message });
143321
+ }
143322
+ await store.remove(runId);
143323
+ return runExecutorLoop(ctx);
142411
143324
  }
142412
143325
  function sinkOutput(workflow, states) {
142413
143326
  const needed = /* @__PURE__ */ new Set();
@@ -142421,18 +143334,17 @@ function sinkOutput(workflow, states) {
142421
143334
  const lastCompleted = [...states.values()].filter((s2) => s2.status === "completed").pop();
142422
143335
  return lastCompleted?.output ?? "";
142423
143336
  }
142424
- async function runStep(step, scope, deps) {
143337
+ async function runStep(step, scope, ctx) {
142425
143338
  const attempts = 1 + Math.max(0, step.retries);
142426
143339
  let lastError = "";
142427
143340
  for (let attempt = 0; attempt < attempts; attempt++) {
142428
- if (deps.signal.aborted)
143341
+ if (ctx.deps.signal.aborted)
142429
143342
  return { ok: false, output: "", error: "aborted" };
142430
143343
  try {
142431
- const output = await runStepOnce(step, scope, deps);
142432
- return { ok: true, output };
143344
+ return await runStepOnce(step, scope, ctx);
142433
143345
  } catch (err) {
142434
143346
  lastError = err instanceof Error ? err.message : String(err);
142435
- deps.logger?.warn?.("workflow step attempt failed", {
143347
+ ctx.deps.logger?.warn?.("workflow step attempt failed", {
142436
143348
  step: step.id,
142437
143349
  attempt: attempt + 1,
142438
143350
  error: lastError
@@ -142441,39 +143353,237 @@ async function runStep(step, scope, deps) {
142441
143353
  }
142442
143354
  return { ok: false, output: "", error: lastError };
142443
143355
  }
142444
- async function runStepOnce(step, scope, deps) {
143356
+ async function runStepOnce(step, scope, ctx) {
143357
+ const { deps } = ctx;
142445
143358
  const opts = deps.logger ? { logger: deps.logger } : {};
143359
+ if (step.loop != null) {
143360
+ return runLoopStep(step, ctx, opts);
143361
+ }
143362
+ if (step.bridge != null || step.condition != null || step.switch != null) {
143363
+ return runLogicStep(step, scope, ctx, opts);
143364
+ }
142446
143365
  if (step.tool) {
142447
143366
  const args = renderArgs(step.args ?? {}, scope, opts);
142448
143367
  const result = await deps.tools.execute(step.tool, args, deps.signal);
142449
- return typeof result === "string" ? result : JSON.stringify(result ?? "");
143368
+ const output = typeof result === "string" ? result : JSON.stringify(result ?? "");
143369
+ return { ok: true, output };
142450
143370
  }
142451
143371
  if (step.workflow) {
142452
- const nested = deps.lookup.workflow(step.workflow);
142453
- if (!nested)
142454
- throw new Error(`nested workflow "${step.workflow}" not found`);
142455
- const depth = (deps.depth ?? 0) + 1;
142456
- if (depth > MAX_NESTING_DEPTH) {
142457
- throw new Error(`nested workflow depth exceeded ${MAX_NESTING_DEPTH}`);
142458
- }
142459
- const nestedInputs = renderArgs(step.args ?? {}, scope, opts);
142460
- const result = await runExecutor(nested, {
142461
- ...deps,
142462
- inputs: nestedInputs,
142463
- depth,
142464
- trigger: `workflow:${step.workflow}`
142465
- });
142466
- if (!result.ok)
142467
- throw new Error(result.error ?? `nested workflow "${step.workflow}" failed`);
142468
- return result.output;
142469
- }
142470
- const spec = buildSubagentSpec(step, scope, deps, opts);
143372
+ return runNestedWorkflow(step, scope, ctx, opts);
143373
+ }
143374
+ const spec = buildSubagentSpecWithDeps(step, scope, deps, opts);
143375
+ if (step.awaitInput) {
143376
+ const child2 = await deps.spawner.spawn({ ...spec, retainSession: true });
143377
+ if (child2.error)
143378
+ throw new Error(child2.error.message);
143379
+ return {
143380
+ ok: false,
143381
+ output: child2.text,
143382
+ paused: true,
143383
+ interactionAgentId: String(child2.childSessionId)
143384
+ };
143385
+ }
142471
143386
  const child = await deps.spawner.spawn(spec);
142472
143387
  if (child.error)
142473
143388
  throw new Error(child.error.message);
142474
- return child.text;
143389
+ return { ok: true, output: child.text };
143390
+ }
143391
+ async function runNestedWorkflow(step, scope, ctx, opts) {
143392
+ const { deps } = ctx;
143393
+ const nested = deps.lookup.workflow(step.workflow);
143394
+ if (!nested)
143395
+ throw new Error(`nested workflow "${step.workflow}" not found`);
143396
+ const depth = (deps.depth ?? 0) + 1;
143397
+ if (depth > MAX_NESTING_DEPTH) {
143398
+ throw new Error(`nested workflow depth exceeded ${MAX_NESTING_DEPTH}`);
143399
+ }
143400
+ const nestedInputs = renderArgs(step.args ?? {}, scope, opts);
143401
+ const result = await runExecutor(nested, {
143402
+ ...deps,
143403
+ inputs: nestedInputs,
143404
+ depth,
143405
+ trigger: `workflow:${step.workflow}`
143406
+ });
143407
+ if (result.status === "paused") {
143408
+ return {
143409
+ ok: false,
143410
+ output: result.output,
143411
+ paused: true,
143412
+ ...result.interactionAgentId ? { interactionAgentId: result.interactionAgentId } : {}
143413
+ };
143414
+ }
143415
+ if (!result.ok)
143416
+ throw new Error(result.error ?? `nested workflow "${step.workflow}" failed`);
143417
+ return { ok: true, output: result.output };
143418
+ }
143419
+ function buildUpstreamBlock(step, scope) {
143420
+ if (step.needs.length === 0)
143421
+ return "";
143422
+ const parts = step.needs.map((id) => `### ${id}
143423
+ ${scope.steps?.[id]?.output ?? ""}`);
143424
+ return `
143425
+
143426
+ ## Upstream
143427
+ ${parts.join("\n\n")}`;
143428
+ }
143429
+ async function runLogicStep(step, scope, ctx, opts) {
143430
+ const instruction = step.bridge ?? step.condition ?? step.switch ?? "";
143431
+ const format = wantsPlainResponse(step) ? "plain" : "json";
143432
+ if ((step.condition != null || step.switch != null) && format === "plain") {
143433
+ return { ok: false, output: "", error: "condition/switch steps require JSON responses" };
143434
+ }
143435
+ const userPrompt = renderTemplate(instruction, scope, opts) + buildUpstreamBlock(step, scope);
143436
+ const spec = {
143437
+ prompt: userPrompt,
143438
+ ...format === "json" ? { systemPrompt: logicSystemPrompt() } : {},
143439
+ label: step.label ?? step.id,
143440
+ allowedTools: []
143441
+ };
143442
+ const child = await ctx.deps.spawner.spawn(spec);
143443
+ if (child.error)
143444
+ return { ok: false, output: "", error: child.error.message };
143445
+ try {
143446
+ const parsed = parseLogicResponse(child.text, step, format);
143447
+ let branchRoute;
143448
+ if (step.condition != null) {
143449
+ const route = resolveBranchForCondition(step, parsed.branch);
143450
+ if (!route) {
143451
+ return {
143452
+ ok: false,
143453
+ output: child.text,
143454
+ error: `condition step "${step.id}": expected branch "then" or "else", got ${JSON.stringify(parsed.branch)}`
143455
+ };
143456
+ }
143457
+ branchRoute = route;
143458
+ }
143459
+ if (step.switch != null) {
143460
+ const route = resolveBranchForSwitch(step, parsed.branch);
143461
+ if (!route) {
143462
+ return {
143463
+ ok: false,
143464
+ output: child.text,
143465
+ error: `switch step "${step.id}": unknown branch ${JSON.stringify(parsed.branch)}`
143466
+ };
143467
+ }
143468
+ branchRoute = route;
143469
+ }
143470
+ return {
143471
+ ok: true,
143472
+ output: parsed.output,
143473
+ ...parsed.vars ? { vars: parsed.vars } : {},
143474
+ ...branchRoute != null ? { branchRoute } : {}
143475
+ };
143476
+ } catch (err) {
143477
+ const message = err instanceof Error ? err.message : String(err);
143478
+ return { ok: false, output: child.text, error: message };
143479
+ }
143480
+ }
143481
+ async function runLoopStep(step, ctx, opts) {
143482
+ const loop = step.loop;
143483
+ const max = loop.maxIterations;
143484
+ const bodySteps = loop.body.map((id) => ctx.workflow.steps.find((s2) => s2.id === id)).filter((s2) => s2 != null);
143485
+ let iteration = 0;
143486
+ let lastBodyOutput = "";
143487
+ for (; iteration < max; iteration++) {
143488
+ await ctx.deps.emit?.("workflow_step_started", {
143489
+ id: step.id,
143490
+ label: `${step.label ?? step.id} (iteration ${iteration + 1})`
143491
+ });
143492
+ for (const body of bodySteps) {
143493
+ const bst = ctx.states.get(body.id);
143494
+ bst.status = "pending";
143495
+ bst.output = "";
143496
+ delete bst.error;
143497
+ }
143498
+ for (const body of bodySteps) {
143499
+ if (ctx.deps.signal.aborted)
143500
+ return { ok: false, output: lastBodyOutput, error: "aborted" };
143501
+ const bodyScope = buildScope(ctx, new Date(ctx.now()).toISOString());
143502
+ const bst = ctx.states.get(body.id);
143503
+ bst.startedAt = ctx.now();
143504
+ const outcome = await runStep(body, bodyScope, ctx);
143505
+ bst.endedAt = ctx.now();
143506
+ if (outcome.paused) {
143507
+ return {
143508
+ ok: false,
143509
+ output: lastBodyOutput,
143510
+ error: `loop "${step.id}": body step "${body.id}" cannot pause for input`
143511
+ };
143512
+ }
143513
+ if (outcome.ok) {
143514
+ bst.status = "completed";
143515
+ bst.output = outcome.output;
143516
+ lastBodyOutput = outcome.output;
143517
+ mergeVars(ctx, outcome.vars);
143518
+ } else {
143519
+ bst.status = "failed";
143520
+ bst.error = outcome.error;
143521
+ if (body.onError !== "continue") {
143522
+ ctx.deps.logger?.warn?.("workflow loop broke on body error", {
143523
+ step: step.id,
143524
+ body: body.id,
143525
+ error: outcome.error
143526
+ });
143527
+ return {
143528
+ ok: true,
143529
+ output: `loop "${step.id}" broke on error in body step "${body.id}" after ${iteration + 1} iteration(s): ${outcome.error}` + (lastBodyOutput ? `
143530
+
143531
+ ${lastBodyOutput}` : "")
143532
+ };
143533
+ }
143534
+ }
143535
+ }
143536
+ const decision = await evaluateLoopCondition(step, loop.condition, ctx, opts);
143537
+ if (!decision.ok) {
143538
+ return { ok: false, output: lastBodyOutput, error: decision.error };
143539
+ }
143540
+ if (decision.route === "then") {
143541
+ return {
143542
+ ok: true,
143543
+ output: `loop "${step.id}" stopped after ${iteration + 1} iteration(s).
143544
+
143545
+ ${lastBodyOutput}`.trim()
143546
+ };
143547
+ }
143548
+ }
143549
+ ctx.deps.logger?.warn?.("workflow loop hit max iterations", { step: step.id, maxIterations: max });
143550
+ return {
143551
+ ok: true,
143552
+ output: `loop "${step.id}" reached max iterations (${max}); stopping.` + (lastBodyOutput ? `
143553
+
143554
+ ${lastBodyOutput}` : "")
143555
+ };
142475
143556
  }
142476
- function buildSubagentSpec(step, scope, deps, opts) {
143557
+ async function evaluateLoopCondition(step, condition, ctx, opts) {
143558
+ const scope = buildScope(ctx, new Date(ctx.now()).toISOString());
143559
+ const userPrompt = renderTemplate(condition, scope, opts) + `
143560
+
143561
+ This is the loop's EXIT condition. Evaluate whether it is now met. Reply with {"branch":"then"} if the condition IS met (stop the loop and continue to the next step), or {"branch":"else"} if it is NOT yet met (run another iteration).`;
143562
+ const spec = {
143563
+ prompt: userPrompt,
143564
+ systemPrompt: logicSystemPrompt(),
143565
+ label: `${step.label ?? step.id} (condition)`,
143566
+ allowedTools: []
143567
+ };
143568
+ const child = await ctx.deps.spawner.spawn(spec);
143569
+ if (child.error)
143570
+ return { ok: false, error: child.error.message };
143571
+ try {
143572
+ const parsed = parseLogicResponse(child.text, step, "json");
143573
+ const route = resolveBranchForCondition(step, parsed.branch);
143574
+ if (!route) {
143575
+ return {
143576
+ ok: false,
143577
+ error: `loop "${step.id}" condition: expected branch "then" or "else", got ${JSON.stringify(parsed.branch)}`
143578
+ };
143579
+ }
143580
+ mergeVars(ctx, parsed.vars);
143581
+ return { ok: true, route };
143582
+ } catch (err) {
143583
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
143584
+ }
143585
+ }
143586
+ function buildSubagentSpecWithDeps(step, scope, deps, opts) {
142477
143587
  const label3 = step.label ?? step.id;
142478
143588
  const renderedInput = step.input ? renderTemplate(step.input, scope, opts) : "";
142479
143589
  if (step.skill) {
@@ -142536,33 +143646,192 @@ async function writeRunRecord(workflow, result, startedAt, executorName, deps, d
142536
143646
  }
142537
143647
 
142538
143648
  // ../plugin-workflows/dist/draft.js
143649
+ var DEFAULT_MAX_TOKENS = 4096;
142539
143650
  function buildSystemPrompt(opts) {
142540
- const skills = opts.availableSkills?.length ? opts.availableSkills.join(", ") : "(none registered \u2014 prefer `prompt` steps)";
142541
- const tools = opts.availableTools?.length ? opts.availableTools.join(", ") : "(none)";
142542
- return `You are a workflow author for the "moxxy" agent. Output ONLY a YAML document (optionally inside a \`\`\`yaml fence) \u2014 no prose.
143651
+ const skills = formatCatalog(opts.availableSkills, "(none registered \u2014 use `prompt` steps with clear instructions)");
143652
+ const tools = formatCatalog(opts.availableTools, "(none \u2014 use `delivery: { channel: inbox }` for notifications)");
143653
+ return `You are a workflow author for the "moxxy" agent. Output ONLY a YAML document (optionally inside a \`\`\`yaml fence) \u2014 no prose before or after.
142543
143654
 
142544
143655
  A workflow is a DAG of steps. Schema:
142545
143656
  - name: kebab-case slug (lowercase letters/numbers/hyphens, starts with a letter)
142546
- - description: one sentence
143657
+ - description: one clear sentence matching the user's goal (never "A simple Moxxy workflow.")
143658
+ - enabled: true
142547
143659
  - on (optional triggers): { schedule: { cron: "m h dom mon dow", timeZone? }, afterWorkflow?, fileChanged?, webhook? }
142548
- - inputs (optional): { <name>: { default: <value>, description? } }
143660
+ - inputs (optional): { <name>: { default: <value>, description: "..." } } \u2014 use for values the operator supplies at run time (e.g. recipient email, image brief)
142549
143661
  - delivery (optional): { channel?: "inbox", inbox?: true }
142550
143662
  - steps: array of steps, each with:
142551
143663
  - id: slug, unique
142552
- - EXACTLY ONE action: skill: <name> | prompt: <text> | tool: <name> | workflow: <name>
142553
- - input: templated prompt for skill/prompt steps
143664
+ - label: short human title (match the user's language when possible)
143665
+ - EXACTLY ONE action: skill | prompt | tool | workflow | bridge | condition | switch | loop
143666
+ - bridge: instruction \u2014 logic step; agent returns JSON with vars (extract/transform data); optional format: plain
143667
+ - condition: instruction + then: [step ids] + else: [step ids] \u2014 agent returns JSON branch then|else
143668
+ - switch: instruction + cases: { <caseId>: [step ids], ... } + optional default: [step ids] \u2014 agent returns JSON branch matching a case id
143669
+ - loop: { body: [step ids], condition: "<prompt>", maxIterations: 1..50 } \u2014 repeats body in order each iteration. \`condition\` is the loop's EXIT/GOAL condition: after each iteration the agent returns JSON branch then (condition met \u2192 STOP, continue to the next step) | else (not yet met \u2192 run body again). A body step error BREAKS the loop to the next step (use onError: continue on a body step to swallow its error and keep iterating). Always stops at maxIterations (default 10). Body step ids must be real steps; do NOT combine loop with then/else/cases/default.
143670
+ - input: templated instruction for skill steps
143671
+ - prompt: templated instruction for prompt steps (multiline allowed with |)
142554
143672
  - args: templated args object for tool/workflow steps
142555
- - needs: [ <upstream step ids> ] (defines the DAG; omit for sources)
142556
- - when (optional): a condition like '{{ steps.x.output }} is not empty' or '{{ inputs.r }} == "US"' or '... contains "text"', joined by and/or
143673
+ - needs: [ <upstream step ids> ] (defines the DAG; omit only for true sources)
143674
+ - when (optional, legacy): simple guards only \u2014 '{{ steps.x.output }} is not empty'. Do NOT use when for semantic decisions (use condition/switch).
142557
143675
  - onError (optional): fail | continue | retry ; retries (optional, 0-3)
142558
143676
 
142559
- Templating: reference earlier results with {{ steps.<id>.output }}, inputs with {{ inputs.<name> }}, plus {{ trigger }} and {{ now }}.
143677
+ Operator data \u2014 two ways: declare a value the operator can supply UP FRONT as an \`inputs\` field (filled in before Run). To PAUSE mid-run and ask a question whose answer depends on earlier steps, set \`awaitInput: true\` on a prompt or skill step: the workflow pauses, surfaces the step's prompt to the operator, and resumes with their reply once they answer. Prefer \`inputs\` for known-up-front values; use \`awaitInput\` only for genuinely mid-run questions.
143678
+
143679
+ Templating: {{ steps.<id>.output }}, {{ inputs.<name> }}, {{ vars.<name> }}, {{ trigger }}, {{ now }}.
143680
+
143681
+ Logic steps: default response is one JSON object (vars, branch, optional text). Describe semantics in the instruction; do not repeat JSON syntax unless needed.
143682
+
143683
+ Ordering: steps whose \`needs\` are all satisfied run in parallel \u2014 chain with \`needs\` for sequential pipelines. A loop's body steps run only inside the loop (each iteration), so give them \`needs: [<loop step id>]\` and never schedule them elsewhere. A loop body step runs unconditionally each iteration: do NOT put \`when\` on it, do NOT make it a condition/switch step, and only \`needs\` its loop step or a sibling body step of the same loop. No NON-loop step may \`needs\` a loop body step \u2014 depend on the loop step instead.
142560
143684
 
142561
- Steps with all their \`needs\` satisfied run in parallel, so use \`needs\` to express ordering and fan-out/fan-in.
143685
+ Authoring rules:
143686
+ 1. Decompose the intent into concrete steps. Multi-phase requests (collect \u2192 act \u2192 summarize \u2192 deliver) need at least 4 steps with a linear or fan-in \`needs\` chain.
143687
+ 2. Values the operator must supply (search topic, recipient email, brief): declare each as an \`inputs\` field with a clear \`description\` (and a \`default\` when sensible). The operator fills them in before Run; reference them downstream via \`{{ inputs.<name> }}\`.
143688
+ 3. To ask the operator a mid-run question whose answer depends on earlier steps, set \`awaitInput: true\` on a prompt or skill step \u2014 the run pauses, shows that step's prompt to the operator, and continues with their reply (referenced downstream via \`{{ steps.<id>.output }}\`). awaitInput is ONLY valid on prompt/skill steps (never tool/logic/loop or a loop body). Prefer \`inputs\` for values known before Run.
143689
+ 4. Research + report + email intents: typical chain \u2014 \`web-research\` skill (over \`{{ inputs.topic }}\`) \u2192 \`write_report\` \u2192 \`send_email\` tool (to \`{{ inputs.recipient }}\`). Put \`topic\` and \`recipient\` in \`inputs\`.
143690
+ 5. Use ONLY skill/tool names from the catalogs below \u2014 never placeholders like "<< skill-name >>", "TBD", or empty skill/tool fields.
143691
+ 6. Prefer a listed skill when its description fits; otherwise use a detailed \`prompt\` step.
143692
+ 7. For email/notify: use a listed mail/MCP tool if available; else \`delivery: { channel: inbox }\`.
143693
+ 8. For image generation: use a listed image/generation tool if available; else a \`prompt\` step that describes producing the image artifact in text.
143694
+ 9. Later steps must read prior results via \`{{ steps.<id>.output }}\`, extracted fields via \`{{ vars.<name> }}\`, and operator data via \`{{ inputs.<name> }}\` \u2014 never invent example emails or briefs in prompts.
143695
+ 10. Between incompatible steps insert \`bridge\` to extract fields into vars (e.g. an email address from text). Use \`condition\` for if/else routing, \`switch\` for multi-way (e.g. value > 100 \u2192 pies, < 0 \u2192 kot, else nieokreslony).
143696
+ 11. Use \`loop\` for "keep refining until good enough" / "retry up to N times" / "iterate while X holds" intents \u2014 set a sane maxIterations so it always terminates. Prefer bridge + vars over passing raw output to tools.
142562
143697
 
142563
- Available skills: ${skills}
142564
- Available tools: ${tools}
142565
- Prefer a named skill when one fits; otherwise use a \`prompt\` step. For "send/email/notify me" use a connected tool if available, else rely on delivery: { channel: inbox }.`;
143698
+ Available skills (name \u2014 description):
143699
+ ${skills}
143700
+
143701
+ Available tools (name \u2014 description):
143702
+ ${tools}
143703
+
143704
+ Example shape for internet research \u2192 report \u2192 email (operator data via inputs):
143705
+ \`\`\`yaml
143706
+ name: internet-research-report-email
143707
+ description: Research a topic, write a report, and email it to the recipient.
143708
+ enabled: true
143709
+ inputs:
143710
+ topic:
143711
+ description: Temat/zakres wyszukiwania w internecie.
143712
+ recipient:
143713
+ description: Adres e-mail odbiorcy raportu.
143714
+ steps:
143715
+ - id: search_web
143716
+ label: Wyszukaj w internecie
143717
+ skill: web-research
143718
+ input: |
143719
+ Przeprowad\u017A research na temat:
143720
+ {{ inputs.topic }}
143721
+ - id: write_report
143722
+ needs: [search_web]
143723
+ label: Przygotuj raport
143724
+ prompt: |
143725
+ Napisz raport po polsku z wynik\xF3w researchu.
143726
+ Temat: {{ inputs.topic }}
143727
+ Research: {{ steps.search_web.output }}
143728
+ - id: send_email
143729
+ needs: [write_report]
143730
+ label: Wy\u015Blij e-mail
143731
+ tool: gmail_send
143732
+ args:
143733
+ to: ["{{ inputs.recipient }}"]
143734
+ subject: "Raport z researchu"
143735
+ body: "{{ steps.write_report.output }}"
143736
+ \`\`\`
143737
+
143738
+ Example shape for image brief \u2192 generate \u2192 report \u2192 email (operator data via inputs):
143739
+ \`\`\`yaml
143740
+ name: image-report-email
143741
+ description: Generate an image from a brief, write a report, and email it.
143742
+ enabled: true
143743
+ inputs:
143744
+ brief:
143745
+ description: What image to generate (subject, style, format, mood, colors).
143746
+ recipient:
143747
+ description: Recipient email for the report.
143748
+ steps:
143749
+ - id: generate_image
143750
+ label: Generate image
143751
+ prompt: |
143752
+ Generate the image from this brief:
143753
+ {{ inputs.brief }}
143754
+ Return the artifact path or id and short generation notes.
143755
+ - id: write_report
143756
+ needs: [generate_image]
143757
+ label: Write report
143758
+ prompt: |
143759
+ Write a concise report in the operator's language.
143760
+ Brief: {{ inputs.brief }}
143761
+ Generation: {{ steps.generate_image.output }}
143762
+ - id: send_report
143763
+ needs: [write_report]
143764
+ label: Send report
143765
+ tool: gmail_send
143766
+ args:
143767
+ to: ["{{ inputs.recipient }}"]
143768
+ subject: "Image workflow report"
143769
+ body: "{{ steps.write_report.output }}"
143770
+ \`\`\`
143771
+
143772
+ Example shape for an iterative refine-until-good loop:
143773
+ \`\`\`yaml
143774
+ name: refine-draft
143775
+ description: Draft a paragraph, then refine it until it is good enough or 5 tries are used.
143776
+ enabled: true
143777
+ inputs:
143778
+ topic:
143779
+ default: "release notes"
143780
+ steps:
143781
+ - id: first_draft
143782
+ label: First draft
143783
+ prompt: |
143784
+ Write a first draft about {{ inputs.topic }}.
143785
+ - id: refine
143786
+ needs: [first_draft]
143787
+ label: Refine loop
143788
+ loop:
143789
+ # condition is the EXIT/GOAL condition \u2014 describe the goal that ENDS the
143790
+ # loop. Met (then) \u2192 stop and continue to the next step; not met (else) \u2192
143791
+ # run the body again. A body step error breaks the loop to the next step.
143792
+ body: [improve]
143793
+ condition: |
143794
+ Is the latest draft in {{ vars.draft }} good enough \u2014 clear, accurate, and well-structured?
143795
+ If yes, the goal is reached and the loop stops; if not, keep refining.
143796
+ maxIterations: 5
143797
+ - id: improve
143798
+ needs: [refine]
143799
+ label: Improve draft
143800
+ bridge: |
143801
+ Improve the current draft (start from {{ steps.first_draft.output }} or {{ vars.draft }}).
143802
+ Return JSON with vars.draft set to the improved text.
143803
+ \`\`\`
143804
+
143805
+ Example shape for a mid-run question (awaitInput pause \u2192 operator reply \u2192 continue):
143806
+ \`\`\`yaml
143807
+ name: draft-with-approval
143808
+ description: Draft an announcement, ask the operator to approve or tweak it, then publish.
143809
+ enabled: true
143810
+ steps:
143811
+ - id: draft
143812
+ label: Draft announcement
143813
+ prompt: |
143814
+ Write a short product announcement.
143815
+ - id: approve
143816
+ needs: [draft]
143817
+ label: Approve or tweak
143818
+ awaitInput: true
143819
+ prompt: |
143820
+ Here is the draft announcement:
143821
+ {{ steps.draft.output }}
143822
+ Reply with "ship it" to approve, or describe any changes you want.
143823
+ - id: publish
143824
+ needs: [approve]
143825
+ label: Publish
143826
+ prompt: |
143827
+ Apply the operator's decision ({{ steps.approve.output }}) and produce the final announcement.
143828
+ \`\`\`
143829
+ (Replace skill/tool names with ones from the catalog when drafting.)`;
143830
+ }
143831
+ function formatCatalog(entries, emptyLabel) {
143832
+ if (!entries?.length)
143833
+ return emptyLabel;
143834
+ return entries.map((entry) => `- ${entry.name}${entry.description ? ` \u2014 ${entry.description}` : ""}`).join("\n");
142566
143835
  }
142567
143836
  async function draftWorkflow(provider, model, intent, signal, opts = {}) {
142568
143837
  let accumulated = "";
@@ -142570,7 +143839,7 @@ async function draftWorkflow(provider, model, intent, signal, opts = {}) {
142570
143839
  model,
142571
143840
  system: buildSystemPrompt(opts),
142572
143841
  messages: [{ role: "user", content: [{ type: "text", text: `Build a workflow for: ${intent}` }] }],
142573
- maxTokens: opts.maxTokens ?? 1500,
143842
+ maxTokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,
142574
143843
  signal
142575
143844
  })) {
142576
143845
  if (event.type === "text_delta")
@@ -142652,7 +143921,8 @@ function createTool(deps) {
142652
143921
  const model = deps.draftModel ?? provider.models[0]?.id ?? "claude-sonnet-4-6";
142653
143922
  const drafted = await draftWorkflow(provider, model, intent, ctx.signal, {
142654
143923
  ...deps.listSkills ? { availableSkills: deps.listSkills() } : {},
142655
- ...deps.listTools ? { availableTools: deps.listTools() } : {}
143924
+ ...deps.listTools ? { availableTools: deps.listTools() } : {},
143925
+ maxTokens: 4096
142656
143926
  });
142657
143927
  if (!drafted.parse.ok || !drafted.parse.workflow) {
142658
143928
  throw new MoxxyError({
@@ -143148,9 +144418,17 @@ function buildWorkflowsIntegration(args) {
143148
144418
  const warnedCycles = /* @__PURE__ */ new Set();
143149
144419
  async function runNow(input) {
143150
144420
  const entry = await store.get(input.name);
143151
- if (!entry) return { ok: false, steps: [], output: "", error: `no workflow named "${input.name}"` };
144421
+ if (!entry) {
144422
+ return { ok: false, status: "failed", steps: [], output: "", error: `no workflow named "${input.name}"` };
144423
+ }
143152
144424
  if (inFlight.has(input.name)) {
143153
- return { ok: false, steps: [], output: "", error: `workflow "${input.name}" is already running` };
144425
+ return {
144426
+ ok: false,
144427
+ status: "failed",
144428
+ steps: [],
144429
+ output: "",
144430
+ error: `workflow "${input.name}" is already running`
144431
+ };
143154
144432
  }
143155
144433
  inFlight.add(input.name);
143156
144434
  try {
@@ -143189,12 +144467,62 @@ function buildWorkflowsIntegration(args) {
143189
144467
  },
143190
144468
  { executor: session.workflowExecutors.getActive() }
143191
144469
  );
144470
+ if (result.status === "paused") {
144471
+ logger?.warn?.("workflows: run paused awaiting operator input; not delivering to inbox", {
144472
+ workflow: input.name,
144473
+ runId: result.runId
144474
+ });
144475
+ return result;
144476
+ }
143192
144477
  await deliverToInbox(entry.workflow, result, logger);
143193
144478
  return result;
143194
144479
  } finally {
143195
144480
  inFlight.delete(input.name);
143196
144481
  }
143197
144482
  }
144483
+ async function resumeNow(runId, reply2) {
144484
+ const checkpoint = await defaultWorkflowRunStore.load(runId);
144485
+ const turnId = session.startTurn().turnId;
144486
+ const spawner = createSubagentSpawner({
144487
+ parentSession: session,
144488
+ parentTurnId: turnId,
144489
+ parentSignal: session.signal,
144490
+ parentModel: activeModel(session)
144491
+ });
144492
+ const result = await resumeWorkflowRun(
144493
+ runId,
144494
+ reply2,
144495
+ {
144496
+ spawner,
144497
+ tools: session.tools,
144498
+ lookup: {
144499
+ skill: (n2) => session.skills.byName(n2),
144500
+ workflow: (n2) => store.lookup(n2)
144501
+ },
144502
+ signal: session.signal,
144503
+ now: () => Date.now(),
144504
+ emit: (subtype, payload) => void session.log.append({
144505
+ type: "plugin_event",
144506
+ sessionId: session.id,
144507
+ turnId,
144508
+ source: "plugin",
144509
+ pluginId: PLUGIN_ID3,
144510
+ subtype,
144511
+ payload
144512
+ }),
144513
+ ...logger ? { logger } : {}
144514
+ },
144515
+ defaultWorkflowRunStore
144516
+ );
144517
+ if (result.status === "paused") {
144518
+ logger?.warn?.("workflows: run paused again awaiting operator input; not delivering to inbox", {
144519
+ runId: result.runId
144520
+ });
144521
+ return result;
144522
+ }
144523
+ if (checkpoint?.workflow) await deliverToInbox(checkpoint.workflow, result, logger);
144524
+ return result;
144525
+ }
143198
144526
  const view = {
143199
144527
  list: async () => (await store.list()).map((w4) => ({
143200
144528
  name: w4.workflow.name,
@@ -143214,7 +144542,47 @@ function buildWorkflowsIntegration(args) {
143214
144542
  ok: r2.ok,
143215
144543
  output: r2.output,
143216
144544
  ...r2.error ? { error: r2.error } : {},
143217
- steps: r2.steps.map((s2) => ({ id: s2.id, status: s2.status, ...s2.error ? { error: s2.error } : {} }))
144545
+ steps: r2.steps.map((s2) => ({ id: s2.id, status: s2.status, ...s2.error ? { error: s2.error } : {} })),
144546
+ status: r2.status,
144547
+ ...r2.runId ? { runId: r2.runId } : {}
144548
+ };
144549
+ },
144550
+ // Builder-facing additions (phase 2 GUI): validate a draft YAML, persist a
144551
+ // workflow, and fetch one as canonical YAML. Keep them on the same store so
144552
+ // the modal and the builder share one source of truth.
144553
+ validateDraft: async (yaml) => {
144554
+ const r2 = parseWorkflowYaml(yaml);
144555
+ return { ok: r2.ok, errors: r2.errors };
144556
+ },
144557
+ save: async (yaml, previousName) => {
144558
+ const parsed = parseWorkflowYaml(yaml);
144559
+ if (!parsed.ok || !parsed.workflow) {
144560
+ throw new Error(`invalid workflow YAML \u2014 ${parsed.errors.join("; ")}`);
144561
+ }
144562
+ const saved = await store.save(parsed.workflow, previousName);
144563
+ await syncSchedules();
144564
+ return { name: saved.workflow.name, scope: saved.scope, path: saved.path };
144565
+ },
144566
+ getRun: async (name) => {
144567
+ const entry = await store.get(name);
144568
+ if (!entry) return null;
144569
+ return {
144570
+ name: entry.workflow.name,
144571
+ scope: entry.scope,
144572
+ path: entry.path,
144573
+ yaml: serializeWorkflow(entry.workflow)
144574
+ };
144575
+ },
144576
+ // Human-in-the-loop: answer a paused run's awaitInput question and resume.
144577
+ resume: async (runId, reply2) => {
144578
+ const r2 = await resumeNow(runId, reply2);
144579
+ return {
144580
+ ok: r2.ok,
144581
+ output: r2.output,
144582
+ ...r2.error ? { error: r2.error } : {},
144583
+ steps: r2.steps.map((s2) => ({ id: s2.id, status: s2.status, ...s2.error ? { error: s2.error } : {} })),
144584
+ status: r2.status,
144585
+ ...r2.runId ? { runId: r2.runId } : {}
143218
144586
  };
143219
144587
  }
143220
144588
  };
@@ -143312,8 +144680,11 @@ function buildWorkflowsIntegration(args) {
143312
144680
  appendEvent: (e3) => session.log.append(e3),
143313
144681
  ...logger ? { logger } : {},
143314
144682
  provider: () => safeActiveProvider(session),
143315
- listSkills: () => session.skills.list().map((s2) => s2.frontmatter.name),
143316
- listTools: () => session.tools.list().map((t2) => t2.name),
144683
+ listSkills: () => session.skills.list().map((s2) => ({
144684
+ name: s2.frontmatter.name,
144685
+ description: s2.frontmatter.description ?? ""
144686
+ })),
144687
+ listTools: () => session.tools.list().map((t2) => ({ name: t2.name, description: t2.description ?? "" })),
143317
144688
  onChanged: syncSchedules,
143318
144689
  runNow,
143319
144690
  userDir: defaultUserWorkflowsDir(),
@@ -143321,6 +144692,13 @@ function buildWorkflowsIntegration(args) {
143321
144692
  session.workflows = view;
143322
144693
  await syncSchedules();
143323
144694
  await startFileWatchers();
144695
+ void defaultWorkflowRunStore.sweepStale().then((n2) => {
144696
+ if (n2 > 0) logger?.info?.("workflows: swept stale paused-run checkpoints", { count: n2 });
144697
+ }).catch(
144698
+ (err) => logger?.warn?.("workflows: checkpoint sweep failed", {
144699
+ err: err instanceof Error ? err.message : String(err)
144700
+ })
144701
+ );
143324
144702
  }
143325
144703
  });
143326
144704
  return {
@@ -148864,9 +150242,9 @@ ${HELP12}`);
148864
150242
  function formatCapabilities(caps) {
148865
150243
  if (!caps) return "";
148866
150244
  const bits = [];
148867
- const fs43 = caps.fs;
148868
- if (fs43?.read?.length) bits.push(`fs:read(${fs43.read.length})`);
148869
- if (fs43?.write?.length) bits.push(`fs:write(${fs43.write.length})`);
150245
+ const fs44 = caps.fs;
150246
+ if (fs44?.read?.length) bits.push(`fs:read(${fs44.read.length})`);
150247
+ if (fs44?.write?.length) bits.push(`fs:write(${fs44.write.length})`);
148870
150248
  const net3 = caps.net;
148871
150249
  if (net3?.mode) bits.push(`net:${net3.mode}`);
148872
150250
  const env3 = caps.env;