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