maka-agent 0.2.0-dev.52.20260926 → 0.2.0-dev.53.20260927

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/activation-command.js +11 -3
  2. package/dist/pi-tui-mcp-status.js +73 -5
  3. package/dist/pi-tui-runner.js +1 -1
  4. package/dist/runtime-host-run-command.js +39 -6
  5. package/dist/runtime-host-run-mcp.js +161 -0
  6. package/dist/runtime-host-session-driver.js +22 -2
  7. package/dist/tui-copy-catalog.js +6 -0
  8. package/dist/tui-mcp-control.js +11 -2
  9. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  10. package/node_modules/@maka/core/dist/attachments.js +7 -0
  11. package/node_modules/@maka/core/dist/events.js +11 -0
  12. package/node_modules/@maka/core/dist/provider-registry.js +11 -2
  13. package/node_modules/@maka/core/dist/turn-origin.js +6 -0
  14. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +1 -5
  15. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +80 -24
  16. package/node_modules/@maka/runtime/dist/bots/proxied-fetch.js +5 -4
  17. package/node_modules/@maka/runtime/dist/computer-use-tools.js +136 -100
  18. package/node_modules/@maka/runtime/dist/context-budget-helpers.js +4 -0
  19. package/node_modules/@maka/runtime/dist/conversation-copy.js +51 -205
  20. package/node_modules/@maka/runtime/dist/edit-replace.js +7 -7
  21. package/node_modules/@maka/runtime/dist/filesystem-executor.js +4 -20
  22. package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +17 -28
  23. package/node_modules/@maka/runtime/dist/format-json.js +39 -0
  24. package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +1 -6
  25. package/node_modules/@maka/runtime/dist/image-file.js +22 -14
  26. package/node_modules/@maka/runtime/dist/model-adapter.js +18 -2
  27. package/node_modules/@maka/runtime/dist/model-history.js +201 -3
  28. package/node_modules/@maka/runtime/dist/network/abortable-connector.js +55 -0
  29. package/node_modules/@maka/runtime/dist/network/proxy-dispatcher.js +72 -37
  30. package/node_modules/@maka/runtime/dist/network/proxy-test.js +1 -1
  31. package/node_modules/@maka/runtime/dist/network/scoped-fetch-transport.js +7 -2
  32. package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +1 -3
  33. package/node_modules/@maka/runtime/dist/provider-error-classification.js +36 -8
  34. package/node_modules/@maka/runtime/dist/recovery-resolver.js +1 -91
  35. package/node_modules/@maka/runtime/dist/runtime-kernel.js +44 -0
  36. package/node_modules/@maka/runtime/dist/session-manager.js +54 -58
  37. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +84 -2
  38. package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +63 -51
  39. package/node_modules/@maka/runtime/dist/workspace-executor.js +5 -6
  40. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +27 -6
  41. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +4 -1
  42. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +9 -9
  43. package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +9 -1
  44. package/node_modules/@maka/runtime-host/dist/protocol/index.js +12 -1
  45. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +28 -6
  46. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +12 -2
  47. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +10 -7
  48. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +10 -2
  49. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +2 -0
  50. package/node_modules/@maka/runtime-host/dist/server/interactive-turn-coordinator.js +4 -1
  51. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +4 -2
  52. package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +4 -0
  53. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +6 -5
  54. package/node_modules/@maka/runtime-host/dist/server/safe-boundary-resume-policy.js +27 -0
  55. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +5 -3
  56. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +7 -2
  57. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +11 -141
  58. package/node_modules/@maka/runtime-host/dist/server/session-revision-graph-references.js +46 -75
  59. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +18 -2
  60. package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +33 -3
  61. package/node_modules/@maka/runtime-host/dist/transport/framed-byte-stream-transport.js +39 -5
  62. package/node_modules/@maka/storage/dist/agent-run-store-contract.js +7 -1
  63. package/node_modules/@maka/storage/dist/artifact-store.js +1 -5
  64. package/node_modules/@maka/storage/dist/artifact-stores.js +0 -3
  65. package/node_modules/@maka/storage/dist/atomic-file-write.js +1 -0
  66. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +107 -57
  67. package/node_modules/@maka/storage/dist/codex-session-adapter.js +227 -19
  68. package/node_modules/@maka/storage/dist/external-session-importer.js +1 -1
  69. package/node_modules/@maka/storage/dist/mcp-config-store.js +22 -3
  70. package/node_modules/@maka/storage/dist/settings-store.js +281 -13
  71. package/package.json +1 -1
@@ -635,7 +635,9 @@ export function buildComputerUseTools(deps) {
635
635
  target,
636
636
  };
637
637
  }
638
- function registerObservation(record, observation) {
638
+ function acceptObservation(state, record, lease, observation) {
639
+ if (!state.validateObservationLease(lease).ok)
640
+ return undefined;
639
641
  const normalized = {
640
642
  ...observation,
641
643
  elements: observation.elements.map((element) => ({
@@ -660,6 +662,7 @@ export function buildComputerUseTools(deps) {
660
662
  record.windowId = observation.windowId;
661
663
  record.obscuringRects = observation.obscuringRects;
662
664
  record.elements = new Map(normalized.elements.map((element) => [element.elementId, element]));
665
+ state.freshObservationSucceeded();
663
666
  return { ...normalized, observationId: frame.frameId };
664
667
  }
665
668
  function bindingFailure(reason, action) {
@@ -920,12 +923,7 @@ export function buildComputerUseTools(deps) {
920
923
  const fresh = captured && result.screenshot && !captured.screenshot
921
924
  ? { ...captured, screenshot: result.screenshot }
922
925
  : captured;
923
- if (!fresh || !state.validateObservationLease(observationLease.lease).ok) {
924
- return undefined;
925
- }
926
- const registered = registerObservation(record, fresh);
927
- const snapshot = state.freshObservationSucceeded();
928
- return snapshot.status === 'active' ? registered : undefined;
926
+ return fresh ? acceptObservation(state, record, observationLease.lease, fresh) : undefined;
929
927
  }
930
928
  async function withInvocationQueue(sessionId, signal, operation) {
931
929
  const previous = invocationQueues.get(sessionId) ?? Promise.resolve();
@@ -1102,6 +1100,51 @@ export function buildComputerUseTools(deps) {
1102
1100
  throw error;
1103
1101
  }
1104
1102
  }
1103
+ async function executeBoundAction(input) {
1104
+ const { state, lease, record, binding, action, signal } = input;
1105
+ let result;
1106
+ let consumeFailure;
1107
+ let presentation;
1108
+ try {
1109
+ const blocked = validateActionLease(state, lease);
1110
+ if (blocked)
1111
+ return { blocked };
1112
+ const context = { ...input.context, boundAction: binding };
1113
+ presentation = await runWithPresentation(action, context, signal, () => input.dispatch(context), () => validateActionLease(state, lease), input.generation);
1114
+ if (presentation.blocked)
1115
+ return { blocked: presentation.blocked };
1116
+ if (!presentation.result) {
1117
+ presentation.finish();
1118
+ return { blocked: bindingFailure('capture_failed', action.type) };
1119
+ }
1120
+ result = preservePartialDelivery(presentation.result);
1121
+ applyTypedOutcomeState(state, result.outcome);
1122
+ if (result.outcome.ok) {
1123
+ const blocked = validateActionLease(state, lease);
1124
+ if (blocked) {
1125
+ presentation.finish();
1126
+ return { blocked };
1127
+ }
1128
+ }
1129
+ }
1130
+ finally {
1131
+ // A path:none refusal retains its frame; a delivered or unknown attempt
1132
+ // consumes it even if the backend throws.
1133
+ if (dispatchedNothing(result)) {
1134
+ consumeFailure = retireBoundAction(record, binding);
1135
+ }
1136
+ else {
1137
+ consumeFailure = consumeBoundAction(record, binding);
1138
+ if (state.validateLease(lease).ok)
1139
+ state.reobserveRequired();
1140
+ }
1141
+ }
1142
+ if (consumeFailure && !hasUncertainDeliveredOutcome(result)) {
1143
+ presentation.finish();
1144
+ return { blocked: refusalAfterDispatch(consumeFailure, result, action.type) };
1145
+ }
1146
+ return { result, finish: presentation.finish };
1147
+ }
1105
1148
  const tool = {
1106
1149
  name: 'maka_computer',
1107
1150
  displayName: 'Maka Computer',
@@ -1242,6 +1285,9 @@ export function buildComputerUseTools(deps) {
1242
1285
  state.screenLocked();
1243
1286
  return sessionFailure('screen_locked');
1244
1287
  }
1288
+ // A new Turn retires the previous frame and advances the session
1289
+ // generation. Do that before leasing an observe, not after capture.
1290
+ const observingRecord = input.action === 'observe' ? sessionObservation(sessionId, turnId) : undefined;
1245
1291
  // Both halves of the wire enum are partitioned in `@maka/core`, so a
1246
1292
  // new action cannot be added without landing on one side or the
1247
1293
  // other — and offline consumers read the same partition.
@@ -1355,12 +1401,12 @@ export function buildComputerUseTools(deps) {
1355
1401
  stopped = 'capture_failed';
1356
1402
  break;
1357
1403
  }
1358
- current = registerObservation(record, recaptured);
1359
- // A frame the host just captured is a live frame. Without this
1360
- // the session stays in `reobserve_required` from the previous
1361
- // step and the next action is refused — the sequence would take
1362
- // exactly one step and stop.
1363
- state.freshObservationSucceeded();
1404
+ current = acceptObservation(state, record, lease.lease, recaptured);
1405
+ if (!current) {
1406
+ const valid = state.validateObservationLease(lease.lease);
1407
+ stopped = valid.ok ? 'reobserve_required' : valid.reason;
1408
+ break;
1409
+ }
1364
1410
  }
1365
1411
  const wanted = step.label.trim().toLowerCase();
1366
1412
  const matches = (current?.elements ?? []).filter((element) => (element.label ?? '').trim().toLowerCase() === wanted &&
@@ -1417,27 +1463,28 @@ export function buildComputerUseTools(deps) {
1417
1463
  stopped = 'stale_frame';
1418
1464
  break;
1419
1465
  }
1420
- const operationContext = { ...runCtx, boundAction: binding };
1421
- let stepResult;
1422
- let presentation;
1423
- try {
1424
- presentation = await runWithPresentation(summarySemanticAction(semantic), operationContext, abortSignal, () => deps.backend.runSemantic({ ...semantic, observationId: record.backendObservationId }, abortSignal, operationContext), undefined, invocationGeneration);
1425
- if (presentation.blocked)
1426
- return presentation.blocked;
1427
- stepResult = presentation.result;
1428
- }
1429
- finally {
1430
- consumeBoundAction(record, binding);
1431
- state.reobserveRequired();
1466
+ const backendAction = { ...semantic, observationId: record.backendObservationId };
1467
+ const execution = await executeBoundAction({
1468
+ state,
1469
+ lease: actionLeaseResult.lease,
1470
+ record,
1471
+ binding,
1472
+ action: summarySemanticAction(semantic),
1473
+ context: runCtx,
1474
+ signal: abortSignal,
1475
+ generation: invocationGeneration,
1476
+ dispatch: (context) => deps.backend.runSemantic(backendAction, abortSignal, context),
1477
+ });
1478
+ if (execution.blocked) {
1479
+ stopped = execution.blocked.error ?? 'outcome_unknown';
1480
+ done.push({ step: index + 1, label: step.label, ok: false });
1481
+ emitProgress?.(done.length, input.steps.length);
1482
+ break;
1432
1483
  }
1433
- presentation?.finish(stepResult);
1434
- if (!stepResult || !stepResult.outcome.ok) {
1435
- if (stepResult)
1436
- applyTypedOutcomeState(state, stepResult.outcome);
1437
- stopped =
1438
- stepResult && !stepResult.outcome.ok
1439
- ? stepResult.outcome.error
1440
- : 'capture_failed';
1484
+ const stepResult = execution.result;
1485
+ execution.finish(stepResult);
1486
+ if (!stepResult.outcome.ok) {
1487
+ stopped = stepResult.outcome.error;
1441
1488
  done.push({ step: index + 1, label: step.label, ok: false });
1442
1489
  emitProgress?.(done.length, input.steps.length);
1443
1490
  break;
@@ -1448,6 +1495,7 @@ export function buildComputerUseTools(deps) {
1448
1495
  // One observation at the end, whatever happened: the model needs a
1449
1496
  // current frame either to carry on or to work out what went wrong.
1450
1497
  let final;
1498
+ let closingBlock;
1451
1499
  try {
1452
1500
  const lease = state.beforeObservation();
1453
1501
  if (lease.ok) {
@@ -1463,7 +1511,14 @@ export function buildComputerUseTools(deps) {
1463
1511
  windowId: record.windowId,
1464
1512
  includeScreenshot: withPicture,
1465
1513
  }, abortSignal, runCtx);
1466
- final = registerObservation(record, await capture(true).catch(() => capture(false)));
1514
+ final = acceptObservation(state, record, lease.lease, await capture(true).catch(() => capture(false)));
1515
+ if (!final) {
1516
+ const valid = state.validateObservationLease(lease.lease);
1517
+ closingBlock = valid.ok ? 'reobserve_required' : valid.reason;
1518
+ }
1519
+ }
1520
+ else {
1521
+ closingBlock = lease.reason;
1467
1522
  }
1468
1523
  }
1469
1524
  catch {
@@ -1471,7 +1526,9 @@ export function buildComputerUseTools(deps) {
1471
1526
  }
1472
1527
  const headline = stopped
1473
1528
  ? `maka_computer.element_sequence stopped at step ${done.length} of ${input.steps.length}: ${stopped}`
1474
- : `maka_computer.element_sequence ok (${done.length} of ${input.steps.length} steps)`;
1529
+ : closingBlock
1530
+ ? `maka_computer.element_sequence failed after ${done.length} of ${input.steps.length} steps: ${closingBlock} — ${SESSION_BLOCK_RECOVERY[closingBlock]}`
1531
+ : `maka_computer.element_sequence ok (${done.length} of ${input.steps.length} steps)`;
1475
1532
  const persistedTail = final
1476
1533
  ? `\nFresh observation: ${persistedObservationText(final)}`
1477
1534
  : '';
@@ -1482,7 +1539,11 @@ export function buildComputerUseTools(deps) {
1482
1539
  return {
1483
1540
  text: `${headline}${persistedTail}`,
1484
1541
  modelText: `${headline}\n${stepLines}${modelTail}`,
1485
- ...(stopped && isComputerUseErrorCode(stopped) ? { error: stopped } : {}),
1542
+ ...(stopped && isComputerUseErrorCode(stopped)
1543
+ ? { error: stopped }
1544
+ : closingBlock
1545
+ ? { error: closingBlock }
1546
+ : {}),
1486
1547
  ...(final?.screenshot
1487
1548
  ? {
1488
1549
  screenshot: {
@@ -1647,8 +1708,13 @@ export function buildComputerUseTools(deps) {
1647
1708
  .filter((part) => typeof part === 'string')
1648
1709
  .some((part) => part.toLowerCase().includes(needle)));
1649
1710
  if (found === wantPresent) {
1650
- const observation = registerObservation(record, last);
1651
- state.freshObservationSucceeded();
1711
+ const observation = observationLease?.ok
1712
+ ? acceptObservation(state, record, observationLease.lease, last)
1713
+ : undefined;
1714
+ if (!observation) {
1715
+ const valid = state.beforeAction();
1716
+ return sessionFailure(valid.ok ? 'reobserve_required' : valid.reason, 'wait');
1717
+ }
1652
1718
  const waited = ((Date.now() - (deadline - Math.round((input.duration ?? 5) * 1000))) /
1653
1719
  1000).toFixed(1);
1654
1720
  const text = `maka_computer.wait ok — ${wantPresent ? 'appeared' : 'gone'} after ${waited}s`;
@@ -1662,8 +1728,13 @@ export function buildComputerUseTools(deps) {
1662
1728
  // holds instead is the whole question a model asks next, and
1663
1729
  // making it spend another call on that is the round trip this
1664
1730
  // action exists to remove.
1665
- const observation = registerObservation(record, last);
1666
- state.freshObservationSucceeded();
1731
+ const observation = observationLease?.ok
1732
+ ? acceptObservation(state, record, observationLease.lease, last)
1733
+ : undefined;
1734
+ if (!observation) {
1735
+ const valid = state.beforeAction();
1736
+ return sessionFailure(valid.ok ? 'reobserve_required' : valid.reason, 'wait');
1737
+ }
1667
1738
  const text = `maka_computer.wait failed: timeout — ${JSON.stringify(input.wait_for_text ?? input.wait_for_text_gone)} was still ${wantPresent ? 'absent' : 'present'} after ${(input.duration ?? 5).toFixed(1)}s and ${polls} looks. This is the window as it stands.`;
1668
1739
  return {
1669
1740
  text: `${text}\n${persistedObservationText(observation)}`,
@@ -1814,13 +1885,13 @@ export function buildComputerUseTools(deps) {
1814
1885
  error: code,
1815
1886
  };
1816
1887
  }
1817
- if (!observationLease?.ok ||
1818
- !state.validateObservationLease(observationLease.lease).ok) {
1888
+ // The observe record was created before taking the lease, so both
1889
+ // must be present before accepting this capture.
1890
+ if (!observingRecord || !observationLease?.ok) {
1819
1891
  const blocked = state.beforeAction();
1820
1892
  return sessionFailure(blocked.ok ? 'reobserve_required' : blocked.reason, 'observe');
1821
1893
  }
1822
- const record = sessionObservation(sessionId, turnId);
1823
- const observation = registerObservation(record, {
1894
+ const observation = acceptObservation(state, observingRecord, observationLease.lease, {
1824
1895
  ...withRequestedView(backendObservation, {
1825
1896
  ...(input.query ? { query: input.query } : {}),
1826
1897
  ...(input.menu ? { menu: input.menu } : {}),
@@ -1836,10 +1907,9 @@ export function buildComputerUseTools(deps) {
1836
1907
  ? { appAlias: input.app }
1837
1908
  : {}),
1838
1909
  });
1839
- const activated = state.freshObservationSucceeded();
1840
- if (activated.status !== 'active') {
1841
- invalidateObservation(sessionId);
1842
- return sessionFailure(activated.status === 'blocked_url' ? 'blocked_url' : 'user_stopped');
1910
+ if (!observation) {
1911
+ const valid = state.beforeAction();
1912
+ return sessionFailure(valid.ok ? 'reobserve_required' : valid.reason, 'observe');
1843
1913
  }
1844
1914
  const screenshot = observation.screenshot;
1845
1915
  return screenshot
@@ -2008,57 +2078,23 @@ export function buildComputerUseTools(deps) {
2008
2078
  observationId: record.backendObservationId,
2009
2079
  };
2010
2080
  const summaryAction = summarySemanticAction(semanticAction);
2011
- let result;
2012
- let consumeFailure;
2013
- let presentation;
2014
- try {
2015
- if (!actionLease)
2016
- return sessionFailure('no_active_frame', input.action);
2017
- const leaseFailure = validateActionLease(state, actionLease);
2018
- if (leaseFailure)
2019
- return leaseFailure;
2020
- const operationContext = { ...runCtx, boundAction: binding };
2021
- presentation = await runWithPresentation(summaryAction, operationContext, abortSignal, () => deps.backend.runSemantic(semanticAction, abortSignal, operationContext), () => validateActionLease(state, actionLease), invocationGeneration);
2022
- if (presentation.blocked)
2023
- return presentation.blocked;
2024
- if (!presentation.result)
2025
- return bindingFailure('capture_failed', input.action);
2026
- result = preservePartialDelivery(presentation.result);
2027
- applyTypedOutcomeState(state, result.outcome);
2028
- if (result.outcome.ok) {
2029
- const postDispatchFailure = validateActionLease(state, actionLease);
2030
- if (postDispatchFailure) {
2031
- presentation.finish();
2032
- return postDispatchFailure;
2033
- }
2034
- }
2035
- }
2036
- finally {
2037
- // A refusal that never reached the window leaves the frame it was
2038
- // quoted against exactly as it was, so it keeps its frame and its
2039
- // lease. Consuming both is what turned one refusal into three
2040
- // calls: the action failed, the frame was thrown away, and the
2041
- // code was not one that hands back a fresh one — so the model's
2042
- // next call was `reobserve_required` and the one after it was the
2043
- // `observe` it should never have had to spend.
2044
- if (dispatchedNothing(result)) {
2045
- consumeFailure = retireBoundAction(record, binding);
2046
- }
2047
- else {
2048
- consumeFailure = consumeBoundAction(record, binding);
2049
- if (actionLease && state.validateLease(actionLease).ok) {
2050
- state.reobserveRequired();
2051
- }
2052
- }
2053
- }
2054
- if (consumeFailure && !hasUncertainDeliveredOutcome(result)) {
2055
- presentation?.finish();
2056
- return refusalAfterDispatch(consumeFailure, result, input.action);
2057
- }
2058
- if (!result) {
2059
- presentation?.finish();
2060
- return bindingFailure('capture_failed', input.action);
2061
- }
2081
+ if (!actionLease)
2082
+ return sessionFailure('no_active_frame', input.action);
2083
+ const execution = await executeBoundAction({
2084
+ state,
2085
+ lease: actionLease,
2086
+ record,
2087
+ binding,
2088
+ action: summaryAction,
2089
+ context: runCtx,
2090
+ signal: abortSignal,
2091
+ generation: invocationGeneration,
2092
+ dispatch: (context) => deps.backend.runSemantic(semanticAction, abortSignal, context),
2093
+ });
2094
+ if (execution.blocked)
2095
+ return execution.blocked;
2096
+ const { result } = execution;
2097
+ const presentation = execution;
2062
2098
  // One action removes its own target on purpose, and the machinery
2063
2099
  // below reads a missing target as an uncertain outcome.
2064
2100
  //
@@ -2119,7 +2155,7 @@ export function buildComputerUseTools(deps) {
2119
2155
  // `REOBSERVABLE_FAILURES` — `target_missing`, `target_changed`,
2120
2156
  // `ambiguous_target`, `duplicate_action`, `stale_frame`,
2121
2157
  // `invalid_coordinate` — takes the fresh observation above, and
2122
- // `registerObservation` makes that the current frame. The sentence
2158
+ // `acceptObservation` makes that the current frame. The sentence
2123
2159
  // then named a frame the same reply had just superseded: the model
2124
2160
  // read "observation X is still current, use it rather than
2125
2161
  // observing again", did exactly that, and collected `stale_frame`
@@ -49,6 +49,10 @@ export function turnKey(event) {
49
49
  export function sha256(text) {
50
50
  return createHash('sha256').update(text).digest('hex');
51
51
  }
52
+ /** Accepts whitespace-only strings; trimming would change persisted checkpoint acceptance. */
53
+ export function nonEmpty(value) {
54
+ return typeof value === 'string' && value.length > 0;
55
+ }
52
56
  export function finitePositive(value) {
53
57
  return typeof value === 'number' && Number.isFinite(value) && value > 0
54
58
  ? Math.floor(value)