@mydatavalue/polter 0.1.0 → 0.1.2

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/index.js CHANGED
@@ -437,6 +437,20 @@ async function executeAction(action, params, config) {
437
437
  return executeGuided(action, params, config);
438
438
  }
439
439
  var AgentActionContext = react.createContext(null);
440
+ function definitionToRegisteredAction(def) {
441
+ return {
442
+ name: def.name,
443
+ description: def.description,
444
+ parameters: def.parameters,
445
+ onExecute: def.onExecute,
446
+ disabled: false,
447
+ disabledReason: void 0,
448
+ getExecutionTargets: () => [],
449
+ route: def.route,
450
+ navigateVia: def.navigateVia,
451
+ mountTimeout: def.mountTimeout
452
+ };
453
+ }
440
454
  function AgentActionProvider({
441
455
  mode = "guided",
442
456
  stepDelay = 600,
@@ -446,24 +460,61 @@ function AgentActionProvider({
446
460
  cursorEnabled = true,
447
461
  children,
448
462
  onExecutionStart,
449
- onExecutionComplete
463
+ onExecutionComplete,
464
+ registry,
465
+ navigate
450
466
  }) {
451
467
  const actionsRef = react.useRef(/* @__PURE__ */ new Map());
452
468
  const targetsRef = react.useRef(/* @__PURE__ */ new Map());
469
+ const registryRef = react.useRef(/* @__PURE__ */ new Map());
470
+ const navigateRef = react.useRef(navigate);
471
+ navigateRef.current = navigate;
453
472
  const [version, setVersion] = react.useState(0);
454
473
  const [isExecuting, setIsExecuting] = react.useState(false);
455
474
  const currentExecutionRef = react.useRef(null);
475
+ react.useEffect(() => {
476
+ const newNames = /* @__PURE__ */ new Set();
477
+ for (const def of registry ?? []) {
478
+ newNames.add(def.name);
479
+ const registryAction = definitionToRegisteredAction(def);
480
+ registryRef.current.set(def.name, registryAction);
481
+ const existing = actionsRef.current.get(def.name);
482
+ if (!existing || existing.getExecutionTargets().length === 0) {
483
+ actionsRef.current.set(def.name, registryAction);
484
+ }
485
+ }
486
+ for (const name of registryRef.current.keys()) {
487
+ if (!newNames.has(name)) {
488
+ registryRef.current.delete(name);
489
+ const current = actionsRef.current.get(name);
490
+ if (current && current.getExecutionTargets().length === 0) {
491
+ actionsRef.current.delete(name);
492
+ }
493
+ }
494
+ }
495
+ setVersion((v) => v + 1);
496
+ }, [registry]);
456
497
  const registerAction = react.useCallback((action) => {
457
498
  const existing = actionsRef.current.get(action.name);
499
+ const registryAction = registryRef.current.get(action.name);
500
+ if (registryAction) {
501
+ if (!action.route) action.route = registryAction.route;
502
+ if (!action.navigateVia) action.navigateVia = registryAction.navigateVia;
503
+ if (action.mountTimeout == null) action.mountTimeout = registryAction.mountTimeout;
504
+ }
458
505
  actionsRef.current.set(action.name, action);
459
506
  if (!existing || existing.description !== action.description || existing.disabled !== action.disabled || existing.disabledReason !== action.disabledReason) {
460
507
  setVersion((v) => v + 1);
461
508
  }
462
509
  }, []);
463
510
  const unregisterAction = react.useCallback((name) => {
464
- if (actionsRef.current.delete(name)) {
465
- setVersion((v) => v + 1);
511
+ const registryAction = registryRef.current.get(name);
512
+ if (registryAction) {
513
+ actionsRef.current.set(name, registryAction);
514
+ } else {
515
+ actionsRef.current.delete(name);
466
516
  }
517
+ setVersion((v) => v + 1);
467
518
  }, []);
468
519
  const registerTarget = react.useCallback((id, entry) => {
469
520
  targetsRef.current.set(id, entry);
@@ -508,12 +559,29 @@ function AgentActionProvider({
508
559
  },
509
560
  []
510
561
  );
562
+ const waitForActionMount = react.useCallback(
563
+ async (name, signal, timeout = 5e3) => {
564
+ const maxWait = timeout;
565
+ const pollInterval = 50;
566
+ const start = Date.now();
567
+ while (Date.now() - start < maxWait) {
568
+ if (signal?.aborted) return null;
569
+ const current = actionsRef.current.get(name);
570
+ if (current && (current.componentBacked || current.getExecutionTargets().length > 0)) {
571
+ return current;
572
+ }
573
+ await new Promise((r) => setTimeout(r, pollInterval));
574
+ }
575
+ return actionsRef.current.get(name) ?? null;
576
+ },
577
+ []
578
+ );
511
579
  const execute = react.useCallback(
512
580
  async (actionName, params) => {
513
581
  currentExecutionRef.current?.abort();
514
582
  const controller = new AbortController();
515
583
  currentExecutionRef.current = controller;
516
- const action = actionsRef.current.get(actionName);
584
+ let action = actionsRef.current.get(actionName);
517
585
  if (!action) {
518
586
  return { success: false, actionName, error: `Action "${actionName}" not found` };
519
587
  }
@@ -527,7 +595,7 @@ function AgentActionProvider({
527
595
  setIsExecuting(true);
528
596
  onExecutionStart?.(actionName);
529
597
  try {
530
- const result = await executeAction(action, params ?? {}, {
598
+ const executorConfig = {
531
599
  mode,
532
600
  stepDelay,
533
601
  overlayOpacity,
@@ -537,7 +605,68 @@ function AgentActionProvider({
537
605
  signal: controller.signal,
538
606
  resolveTarget,
539
607
  resolveNamedTarget
540
- });
608
+ };
609
+ const schema = action.parameters;
610
+ if (schema?.safeParse) {
611
+ const validation = schema.safeParse(params ?? {});
612
+ if (!validation.success) {
613
+ const missing = validation.error.issues.map((i) => i.path.join(".")).filter(Boolean);
614
+ const error = missing.length > 0 ? `Required parameters missing: ${missing.join(", ")}` : validation.error.issues.map((i) => i.message).join("; ");
615
+ return { success: false, actionName, error };
616
+ }
617
+ }
618
+ if (action.navigateVia && action.navigateVia.length > 0) {
619
+ for (const viaName of action.navigateVia) {
620
+ if (controller.signal.aborted) break;
621
+ const viaRegistered = actionsRef.current.get(viaName);
622
+ const viaTimeout = viaRegistered?.mountTimeout ?? 1e4;
623
+ const viaAction = await waitForActionMount(viaName, controller.signal, viaTimeout);
624
+ if (!viaAction || viaAction.getExecutionTargets().length === 0) {
625
+ return {
626
+ success: false,
627
+ actionName,
628
+ error: `Navigation chain action "${viaName}" not found or has no targets`
629
+ };
630
+ }
631
+ const viaResult = await executeAction(viaAction, {}, executorConfig);
632
+ if (!viaResult.success) {
633
+ return {
634
+ success: false,
635
+ actionName,
636
+ error: `Navigation chain failed at "${viaName}": ${viaResult.error}`
637
+ };
638
+ }
639
+ }
640
+ const mounted = await waitForActionMount(actionName, controller.signal, action.mountTimeout ?? 1e4);
641
+ if (!mounted || !mounted.componentBacked) {
642
+ return {
643
+ success: false,
644
+ actionName,
645
+ error: `Action "${actionName}" did not mount after navigation chain \u2014 the page may require authentication or failed to load`
646
+ };
647
+ }
648
+ action = mounted;
649
+ } else {
650
+ const targets = action.getExecutionTargets();
651
+ if (targets.length === 0 && action.route && navigateRef.current) {
652
+ const path = action.route(params ?? {});
653
+ await navigateRef.current(path);
654
+ const mounted = await waitForActionMount(actionName, controller.signal, action.mountTimeout);
655
+ if (mounted) {
656
+ action = mounted;
657
+ }
658
+ }
659
+ }
660
+ if (action.disabled) {
661
+ const result2 = {
662
+ success: false,
663
+ actionName,
664
+ error: action.disabledReason || "Action is disabled"
665
+ };
666
+ onExecutionComplete?.(result2);
667
+ return result2;
668
+ }
669
+ const result = await executeAction(action, params ?? {}, executorConfig);
541
670
  onExecutionComplete?.(result);
542
671
  return result;
543
672
  } catch (err) {
@@ -555,7 +684,7 @@ function AgentActionProvider({
555
684
  }
556
685
  }
557
686
  },
558
- [mode, stepDelay, overlayOpacity, spotlightPadding, tooltipEnabled, cursorEnabled, onExecutionStart, onExecutionComplete, resolveTarget, resolveNamedTarget]
687
+ [mode, stepDelay, overlayOpacity, spotlightPadding, tooltipEnabled, cursorEnabled, onExecutionStart, onExecutionComplete, resolveTarget, resolveNamedTarget, waitForActionMount]
559
688
  );
560
689
  const availableActions = react.useMemo(
561
690
  () => Array.from(actionsRef.current.values()).map((a) => ({
@@ -600,19 +729,24 @@ function AgentActionProvider({
600
729
  return /* @__PURE__ */ jsxRuntime.jsx(AgentActionContext.Provider, { value: contextValue, children });
601
730
  }
602
731
  var AgentStepContext = react.createContext(null);
603
- function AgentAction({
604
- name,
605
- description,
606
- parameters,
607
- onExecute,
608
- disabled = false,
609
- disabledReason,
610
- children
611
- }) {
732
+ function AgentAction(props) {
733
+ const {
734
+ action,
735
+ onExecute,
736
+ disabled = false,
737
+ disabledReason,
738
+ children
739
+ } = props;
740
+ const name = props.name ?? action?.name;
741
+ const description = props.description ?? action?.description ?? "";
742
+ const parameters = props.parameters ?? action?.parameters;
612
743
  const context = react.useContext(AgentActionContext);
613
744
  if (!context) {
614
745
  throw new Error("AgentAction must be used within an AgentActionProvider");
615
746
  }
747
+ if (!name) {
748
+ throw new Error('AgentAction requires either a "name" prop or an "action" prop');
749
+ }
616
750
  const wrapperRef = react.useRef(null);
617
751
  const stepsRef = react.useRef(/* @__PURE__ */ new Map());
618
752
  const onExecuteRef = react.useRef(onExecute);
@@ -656,7 +790,8 @@ function AgentAction({
656
790
  onExecute: onExecuteRef.current ? stableOnExecute : void 0,
657
791
  disabled,
658
792
  disabledReason,
659
- getExecutionTargets
793
+ getExecutionTargets,
794
+ componentBacked: true
660
795
  });
661
796
  return () => unregisterAction(name);
662
797
  }, [name, description, disabled, disabledReason, stableOnExecute, getExecutionTargets, registerAction, unregisterAction]);
@@ -1251,20 +1386,34 @@ function useAgentCommandRouter(fallback, getActionName) {
1251
1386
  const actionName = getActionName(command);
1252
1387
  const isRegistered = availableActions.some((a) => a.name === actionName && !a.disabled);
1253
1388
  if (isRegistered) {
1254
- await execute(actionName, command);
1255
- return;
1389
+ return await execute(actionName, command);
1256
1390
  }
1257
1391
  await fallback?.(command);
1392
+ return void 0;
1258
1393
  },
1259
1394
  [execute, availableActions, fallback, getActionName]
1260
1395
  );
1261
1396
  }
1262
1397
 
1398
+ // src/core/defineAction.ts
1399
+ function defineAction(config) {
1400
+ return {
1401
+ name: config.name,
1402
+ description: config.description,
1403
+ parameters: config.parameters,
1404
+ route: config.route,
1405
+ onExecute: config.onExecute,
1406
+ navigateVia: config.navigateVia,
1407
+ mountTimeout: config.mountTimeout
1408
+ };
1409
+ }
1410
+
1263
1411
  exports.AgentAction = AgentAction;
1264
1412
  exports.AgentActionProvider = AgentActionProvider;
1265
1413
  exports.AgentDevTools = AgentDevTools;
1266
1414
  exports.AgentStep = AgentStep;
1267
1415
  exports.AgentTarget = AgentTarget;
1416
+ exports.defineAction = defineAction;
1268
1417
  exports.generateToolSchemas = generateToolSchemas;
1269
1418
  exports.useAgentAction = useAgentAction;
1270
1419
  exports.useAgentActions = useAgentActions;