@versatly/workgraph 1.3.1 → 1.3.3

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.
@@ -26,7 +26,7 @@ import {
26
26
  saveRegistry,
27
27
  stop,
28
28
  update
29
- } from "./chunk-BJVBE7F4.js";
29
+ } from "./chunk-P6ZRS6BF.js";
30
30
 
31
31
  // src/workspace.ts
32
32
  var workspace_exports = {};
@@ -2345,7 +2345,10 @@ function runTriggerEngineCycle(workspacePath, options = {}) {
2345
2345
  const intervalSeconds = normalizeInt(options.intervalSeconds, DEFAULT_ENGINE_INTERVAL_SECONDS, 1);
2346
2346
  const state = loadTriggerState(workspacePath);
2347
2347
  const triggers = listNormalizedTriggers(workspacePath);
2348
- const ledgerEntries = readAll(workspacePath);
2348
+ const requiresLedgerRead = triggers.some(
2349
+ (trigger) => trigger.status === "active" && (trigger.condition?.type === "event" || trigger.condition?.type === "thread-complete")
2350
+ );
2351
+ const ledgerEntries = requiresLedgerRead ? readAll(workspacePath) : [];
2349
2352
  let fired = 0;
2350
2353
  let errors = 0;
2351
2354
  const results = [];
@@ -2894,24 +2897,30 @@ function evaluateSynthesisCondition(input) {
2894
2897
  }
2895
2898
  function evaluateEventCondition(input, eventTypeRaw) {
2896
2899
  const eventType = eventTypeRaw.toLowerCase();
2897
- const latestTs = input.ledgerEntries[input.ledgerEntries.length - 1]?.ts;
2898
- if (!input.runtime.lastEventCursorTs) {
2899
- input.runtime.lastEventCursorTs = latestTs ?? input.now.toISOString();
2900
+ const totalEntries = input.ledgerEntries.length;
2901
+ const latestEntry = input.ledgerEntries[totalEntries - 1];
2902
+ if (input.runtime.lastEventCursorOffset === void 0) {
2903
+ input.runtime.lastEventCursorOffset = deriveEventCursorOffset(input.ledgerEntries, input.runtime);
2904
+ input.runtime.lastEventCursorTs = latestEntry?.ts ?? input.now.toISOString();
2905
+ input.runtime.lastEventCursorHash = latestEntry?.hash;
2900
2906
  return {
2901
2907
  matched: false,
2902
- reason: `Initialized event cursor for ${eventType}.`
2908
+ reason: `Initialized event cursor for ${eventType} at offset ${input.runtime.lastEventCursorOffset}.`
2903
2909
  };
2904
2910
  }
2905
- const cursor = input.runtime.lastEventCursorTs;
2906
- const newEntries = input.ledgerEntries.filter((entry) => entry.ts > cursor);
2911
+ const cursorOffset = clampEventCursorOffset(input.runtime.lastEventCursorOffset, totalEntries);
2912
+ const newEntries = input.ledgerEntries.slice(cursorOffset);
2907
2913
  if (newEntries.length === 0) {
2908
2914
  return {
2909
2915
  matched: false,
2910
- reason: `No new events for ${eventType} since ${cursor}.`
2916
+ reason: `No new events for ${eventType} since ledger offset ${cursorOffset}.`
2911
2917
  };
2912
2918
  }
2913
2919
  const matching = newEntries.filter((entry) => ledgerEntryMatchesEventType(entry, eventType));
2914
- input.runtime.lastEventCursorTs = newEntries[newEntries.length - 1].ts;
2920
+ const latestProcessed = newEntries[newEntries.length - 1];
2921
+ input.runtime.lastEventCursorOffset = totalEntries;
2922
+ input.runtime.lastEventCursorTs = latestProcessed.ts;
2923
+ input.runtime.lastEventCursorHash = latestProcessed.hash;
2915
2924
  if (matching.length === 0) {
2916
2925
  return {
2917
2926
  matched: false,
@@ -2931,6 +2940,37 @@ function evaluateEventCondition(input, eventTypeRaw) {
2931
2940
  }
2932
2941
  };
2933
2942
  }
2943
+ function deriveEventCursorOffset(entries, runtime) {
2944
+ if (entries.length === 0) return 0;
2945
+ if (runtime.lastEventCursorOffset !== void 0) {
2946
+ return clampEventCursorOffset(runtime.lastEventCursorOffset, entries.length);
2947
+ }
2948
+ const cursorTs = typeof runtime.lastEventCursorTs === "string" ? runtime.lastEventCursorTs.trim() : "";
2949
+ if (!cursorTs) return entries.length;
2950
+ const cursorHash = typeof runtime.lastEventCursorHash === "string" ? runtime.lastEventCursorHash.trim() : "";
2951
+ if (cursorHash) {
2952
+ const hashIdx = findLastEntryIndex(
2953
+ entries,
2954
+ (entry) => entry.ts === cursorTs && String(entry.hash ?? "") === cursorHash
2955
+ );
2956
+ if (hashIdx !== -1) return hashIdx + 1;
2957
+ }
2958
+ const sameTsIdx = findLastEntryIndex(entries, (entry) => entry.ts === cursorTs);
2959
+ if (sameTsIdx !== -1) return sameTsIdx + 1;
2960
+ const firstNewerIdx = entries.findIndex((entry) => entry.ts > cursorTs);
2961
+ if (firstNewerIdx !== -1) return firstNewerIdx;
2962
+ return entries.length;
2963
+ }
2964
+ function clampEventCursorOffset(offset, totalEntries) {
2965
+ if (!Number.isFinite(offset)) return totalEntries;
2966
+ return Math.min(totalEntries, Math.max(0, Math.trunc(offset)));
2967
+ }
2968
+ function findLastEntryIndex(entries, predicate) {
2969
+ for (let idx = entries.length - 1; idx >= 0; idx -= 1) {
2970
+ if (predicate(entries[idx])) return idx;
2971
+ }
2972
+ return -1;
2973
+ }
2934
2974
  function evaluateFileWatchCondition(input, glob) {
2935
2975
  const nowIso = input.now.toISOString();
2936
2976
  if (!input.runtime.lastFileScanTs) {
package/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  swarm_exports,
15
15
  trigger_exports,
16
16
  workspace_exports
17
- } from "./chunk-NGRQ6T4O.js";
17
+ } from "./chunk-GJCMMGGX.js";
18
18
  import {
19
19
  autonomy_exports,
20
20
  dispatch_exports,
@@ -30,7 +30,7 @@ import {
30
30
  thread_audit_exports,
31
31
  thread_exports,
32
32
  trigger_engine_exports
33
- } from "./chunk-BJVBE7F4.js";
33
+ } from "./chunk-P6ZRS6BF.js";
34
34
 
35
35
  // src/cli.ts
36
36
  import fs from "fs";
package/dist/index.d.ts CHANGED
@@ -1213,6 +1213,8 @@ interface TriggerRuntimeState {
1213
1213
  state?: TriggerRuntimeStatus;
1214
1214
  lastResult?: Record<string, unknown>;
1215
1215
  lastEventCursorTs?: string;
1216
+ lastEventCursorHash?: string;
1217
+ lastEventCursorOffset?: number;
1216
1218
  lastFileScanTs?: string;
1217
1219
  lastCronBucket?: string;
1218
1220
  synthesisCursorTs?: string;
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  swarm_exports,
17
17
  trigger_exports,
18
18
  workspace_exports
19
- } from "./chunk-NGRQ6T4O.js";
19
+ } from "./chunk-GJCMMGGX.js";
20
20
  import {
21
21
  CursorCloudAdapter,
22
22
  THREAD_STATUS_TRANSITIONS,
@@ -38,7 +38,7 @@ import {
38
38
  thread_audit_exports,
39
39
  thread_exports,
40
40
  trigger_engine_exports
41
- } from "./chunk-BJVBE7F4.js";
41
+ } from "./chunk-P6ZRS6BF.js";
42
42
  export {
43
43
  CursorCloudAdapter,
44
44
  THREAD_STATUS_TRANSITIONS,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createWorkgraphMcpServer,
3
3
  startWorkgraphMcpServer
4
- } from "./chunk-BJVBE7F4.js";
4
+ } from "./chunk-P6ZRS6BF.js";
5
5
  export {
6
6
  createWorkgraphMcpServer,
7
7
  startWorkgraphMcpServer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versatly/workgraph",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Agent-first workgraph workspace for multi-agent coordination with dynamic primitives, append-only ledger, and markdown-native storage.",
5
5
  "workspaces": [
6
6
  "packages/*"