@hasna/workflows 0.2.1 → 0.2.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/README.md CHANGED
@@ -141,6 +141,31 @@ Use durable storage for the data directory and one deployed executor; ephemeral
141
141
  container storage is unsuitable. Model lanes require the selected harness's
142
142
  native subscription on the executor. They do not inherit paid model API keys.
143
143
 
144
+ Set `HASNA_WORKFLOWS_ENABLED_LANES` to a comma-separated subset of
145
+ `claude,codex,cursor,grok` to restrict model execution. An explicitly empty value
146
+ permits command steps only; leaving the variable unset preserves all four lanes.
147
+ Lane inspection reports disabled lanes as `wired: false` with a configuration
148
+ reason. Trigger rejects a graph containing a disabled prompt lane before any
149
+ step runs, and resume leaves such a stored run unchanged. Existing run history
150
+ and event reads remain available. This admission setting does not grant model
151
+ authentication or authorize API billing.
152
+
153
+ For a deployment with a restored data directory, set
154
+ `HASNA_WORKFLOWS_DATA_IDENTITY` to its non-secret storage identity. After verifying
155
+ the restore, the operator provisions `.workflows-data-identity` containing that
156
+ identity followed by a newline. The directory must already contain readable and
157
+ writable `workflows.db` and `sessions/session.wal`. The server refuses missing,
158
+ mismatched or inaccessible state without creating a replacement. Readiness and
159
+ storage operations repeat the check. The server also holds a process-lifetime
160
+ SQLite mutex in `.server-lease.sqlite`, containing no tables, so an overlapping
161
+ replacement cannot open a second server over the same local journal. Keep this
162
+ file in place; never delete it to take a live process's lease. This mode requires
163
+ a local filesystem with reliable SQLite locking, not a network filesystem.
164
+
165
+ Journal append acknowledgements follow file and parent-directory flushes. The
166
+ journal and PostgreSQL are separate stores, not one atomic transaction; capture
167
+ and restore them together while execution is quiescent.
168
+
144
169
  When the server sits behind an authenticated gateway, set
145
170
  `HASNA_WORKFLOWS_ORIGIN_AUTH` to the gateway's scoped origin identity. It must be
146
171
  32 to 256 ASCII letters, digits, underscores or hyphens. All API routes, including
@@ -599,513 +599,8 @@ var init_graph = __esm(() => {
599
599
  NODE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
600
600
  });
601
601
 
602
- // src/lanes/types.ts
603
- import { statSync } from "fs";
604
- function binaryOnPath(binary) {
605
- const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
606
- for (const entry of pathEntries) {
607
- try {
608
- const stat = statSync(`${entry}/${binary}`);
609
- if (stat.isFile())
610
- return true;
611
- } catch {}
612
- }
613
- return false;
614
- }
615
- function subscriptionEnvironment(overrides = {}) {
616
- const paid = /^(OPENAI_API_KEY|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|CODEX_API_KEY|CURSOR_API_KEY)$/;
617
- const sensitive = /(?:API[_-]?KEY|SECRET|PASSWORD|TOKEN|DATABASE_URL|CREDENTIAL)/i;
618
- const env = {};
619
- for (const [name, value] of Object.entries(process.env)) {
620
- if (value !== undefined && !paid.test(name) && (!sensitive.test(name) || name === "CLAUDE_CODE_OAUTH_TOKEN"))
621
- env[name] = value;
622
- }
623
- for (const [name, value] of Object.entries(overrides)) {
624
- if (paid.test(name) || sensitive.test(name))
625
- throw new Error(`lane environment field ${name} requires a supported scoped credential consumer`);
626
- env[name] = value;
627
- }
628
- return env;
629
- }
630
- function subscriptionSdkEnvironment(overrides = {}) {
631
- const safe = subscriptionEnvironment(overrides);
632
- return { ...Object.fromEntries(Object.keys(process.env).map((name) => [name, undefined])), ...safe };
633
- }
634
- function withTimeout(promise, timeoutMs, what, cancel) {
635
- return new Promise((resolve, reject) => {
636
- const timer = setTimeout(() => {
637
- cancel?.();
638
- reject(new LaneTimeoutError(what, timeoutMs));
639
- }, timeoutMs);
640
- promise.then((value) => {
641
- clearTimeout(timer);
642
- resolve(value);
643
- }, (err) => {
644
- clearTimeout(timer);
645
- reject(err);
646
- });
647
- });
648
- }
649
- async function spawnCli(cmd, opts) {
650
- const started = Date.now();
651
- const timeoutMs = opts.timeoutMs ?? DEFAULT_LANE_TIMEOUT_MS;
652
- let proc;
653
- try {
654
- proc = Bun.spawn({
655
- cmd,
656
- cwd: opts.cwd,
657
- env: subscriptionEnvironment(opts.env),
658
- stdin: "ignore",
659
- stdout: "pipe",
660
- stderr: "pipe",
661
- timeout: timeoutMs
662
- });
663
- } catch (err) {
664
- const message = String(err instanceof Error ? err.message : err);
665
- if (message.includes("ENOENT") || /not found/i.test(message)) {
666
- throw new LaneDependencyMissingError(`lane binary ${JSON.stringify(opts.binaryName)} not found on PATH; install it or provide ${opts.packageHint}`);
667
- }
668
- return { ok: false, exitCode: 1, output: "", durationMs: Date.now() - started, error: message };
669
- }
670
- const [stdout, stderr, exitCode] = await Promise.all([
671
- new Response(proc.stdout).text(),
672
- new Response(proc.stderr).text(),
673
- proc.exited
674
- ]);
675
- const output = opts.stdoutOnly ? stdout.trim() : stderr ? `${stdout}
676
- ${stderr}`.trim() : stdout.trim();
677
- return {
678
- ok: exitCode === 0,
679
- exitCode,
680
- output,
681
- durationMs: Date.now() - started,
682
- ...opts.stdoutOnly && exitCode !== 0 && stderr.trim() ? { error: stderr.trim() } : {}
683
- };
684
- }
685
- var LANE_KINDS2, LaneDependencyMissingError, LaneAdapterShapeError, DEFAULT_LANE_TIMEOUT_MS = 120000, LaneTimeoutError;
686
- var init_types = __esm(() => {
687
- LANE_KINDS2 = ["claude", "codex", "cursor", "grok"];
688
- LaneDependencyMissingError = class LaneDependencyMissingError extends Error {
689
- constructor(message) {
690
- super(message);
691
- this.name = "LaneDependencyMissingError";
692
- }
693
- };
694
- LaneAdapterShapeError = class LaneAdapterShapeError extends Error {
695
- constructor(message) {
696
- super(message);
697
- this.name = "LaneAdapterShapeError";
698
- }
699
- };
700
- LaneTimeoutError = class LaneTimeoutError extends Error {
701
- constructor(what, timeoutMs) {
702
- super(`${what} exceeded the ${timeoutMs}ms budget`);
703
- this.name = "LaneTimeoutError";
704
- }
705
- };
706
- });
707
-
708
- // src/lanes/claude.ts
709
- function createClaudeAdapter(deps = {}) {
710
- return {
711
- kind: "claude",
712
- async probe() {
713
- const loader = deps.sdkLoader ?? (async () => await import("@anthropic-ai/claude-agent-sdk"));
714
- try {
715
- await loader();
716
- return { kind: "claude", sdk: "@anthropic-ai/claude-agent-sdk", wired: true, via: "sdk" };
717
- } catch {
718
- if (binaryOnPath(deps.cliPath ?? "claude")) {
719
- return { kind: "claude", sdk: "@anthropic-ai/claude-agent-sdk", wired: true, via: "cli" };
720
- }
721
- return {
722
- kind: "claude",
723
- sdk: "@anthropic-ai/claude-agent-sdk",
724
- wired: false,
725
- reason: "neither @anthropic-ai/claude-agent-sdk nor the claude CLI is available on this machine"
726
- };
727
- }
728
- },
729
- async run(job) {
730
- if (!job.prompt) {
731
- return { ok: false, exitCode: 2, output: "", durationMs: 0, error: "claude lane requires a prompt" };
732
- }
733
- const started = Date.now();
734
- const timeoutMs = job.timeoutMs ?? DEFAULT_LANE_TIMEOUT_MS;
735
- const loader = deps.sdkLoader ?? (async () => await import("@anthropic-ai/claude-agent-sdk"));
736
- let sdk;
737
- try {
738
- sdk = await loader();
739
- } catch (err) {
740
- return runClaudeCli(job, deps.cliPath ?? "claude", started, timeoutMs);
741
- }
742
- const query = sdk.query;
743
- if (typeof query !== "function") {
744
- throw new LaneAdapterShapeError("@anthropic-ai/claude-agent-sdk exports no query() function");
745
- }
746
- const prompt = job.prompt;
747
- const abortController = new AbortController;
748
- try {
749
- const { output, isError } = await withTimeout((async () => {
750
- const generator = await query({
751
- prompt,
752
- options: { model: job.model, cwd: job.cwd, maxTurns: 32, abortController, env: subscriptionSdkEnvironment(job.env) }
753
- });
754
- let output2 = "";
755
- let isError2 = false;
756
- for await (const message of generator) {
757
- if (message.type === "assistant") {
758
- const content = message.message?.content;
759
- if (Array.isArray(content)) {
760
- for (const block of content) {
761
- if (block && typeof block === "object" && "text" in block && typeof block.text === "string") {
762
- output2 += block.text;
763
- }
764
- }
765
- }
766
- }
767
- if (message.type === "error")
768
- isError2 = true;
769
- }
770
- return { output: output2, isError: isError2 };
771
- })(), timeoutMs, "claude lane", () => abortController.abort());
772
- const durationMs = Date.now() - started;
773
- return { ok: !isError, exitCode: isError ? 1 : 0, output: output.trim(), durationMs };
774
- } catch (err) {
775
- const durationMs = Date.now() - started;
776
- return { ok: false, exitCode: 1, output: "", durationMs, error: String(err instanceof Error ? err.message : err), retryable: !(err instanceof LaneTimeoutError) };
777
- }
778
- }
779
- };
780
- }
781
- async function runClaudeCli(job, cliPath, started, timeoutMs) {
782
- try {
783
- const result = await spawnCli([cliPath, "-p", job.prompt], {
784
- binaryName: cliPath,
785
- packageHint: "@anthropic-ai/claude-agent-sdk or the claude CLI",
786
- timeoutMs,
787
- env: job.env,
788
- cwd: job.cwd
789
- });
790
- return { ...result, durationMs: Date.now() - started };
791
- } catch (err) {
792
- if (err instanceof LaneDependencyMissingError) {
793
- return { ok: false, exitCode: 127, output: "", durationMs: Date.now() - started, error: err.message };
794
- }
795
- throw err;
796
- }
797
- }
798
- var init_claude = __esm(() => {
799
- init_types();
800
- });
801
-
802
- // src/lanes/codex.ts
803
- function createCodexAdapter(deps = {}) {
804
- return {
805
- kind: "codex",
806
- async probe() {
807
- const loader = deps.sdkLoader ?? (async () => await import("@openai/codex-sdk"));
808
- try {
809
- await loader();
810
- return { kind: "codex", sdk: "@openai/codex-sdk", wired: true, via: "sdk" };
811
- } catch {
812
- if (binaryOnPath(deps.cliPath ?? "codex")) {
813
- return { kind: "codex", sdk: "@openai/codex-sdk", wired: true, via: "cli" };
814
- }
815
- return {
816
- kind: "codex",
817
- sdk: "@openai/codex-sdk",
818
- wired: false,
819
- reason: "neither @openai/codex-sdk nor the codex CLI is available on this machine"
820
- };
821
- }
822
- },
823
- async run(job) {
824
- if (!job.prompt) {
825
- return { ok: false, exitCode: 2, output: "", durationMs: 0, error: "codex lane requires a prompt" };
826
- }
827
- if (job.codexMode !== undefined && (!CODEX_EXECUTION_MODES.includes(job.codexMode) || job.lane !== "codex" || job.command !== undefined)) {
828
- return { ok: false, exitCode: 2, output: "", durationMs: 0, error: "codexMode must be repository or read-only on a codex prompt without command", retryable: false };
829
- }
830
- const started = Date.now();
831
- const timeoutMs = job.timeoutMs ?? DEFAULT_LANE_TIMEOUT_MS;
832
- const loader = deps.sdkLoader ?? (async () => await import("@openai/codex-sdk"));
833
- let sdk;
834
- try {
835
- sdk = await loader();
836
- } catch {
837
- return runCodexCli(job, deps.cliPath ?? "codex", started, timeoutMs);
838
- }
839
- const CodexClass = sdk.Codex;
840
- if (typeof CodexClass !== "function") {
841
- throw new LaneAdapterShapeError("@openai/codex-sdk exports no Codex class");
842
- }
843
- const abortController = new AbortController;
844
- try {
845
- const client = await withTimeout(Promise.resolve(new CodexClass({
846
- config: { model: job.model },
847
- env: subscriptionEnvironment(job.env)
848
- })), timeoutMs, "codex lane");
849
- const thread = client.startThread?.({
850
- model: job.model,
851
- workingDirectory: job.cwd,
852
- ...job.codexMode === "read-only" ? { sandboxMode: "read-only", skipGitRepoCheck: true, approvalPolicy: "never" } : {}
853
- });
854
- if (!thread || typeof thread.run !== "function") {
855
- throw new LaneAdapterShapeError("@openai/codex-sdk Codex.startThread() returned no run()");
856
- }
857
- const turn = await withTimeout(thread.run(job.prompt, { signal: abortController.signal }), timeoutMs, "codex lane", () => abortController.abort());
858
- const durationMs = Date.now() - started;
859
- const output = turn?.finalResponse;
860
- if (typeof output !== "string" || !output.trim()) {
861
- return missingResponse(durationMs);
862
- }
863
- return { ok: true, exitCode: 0, output, durationMs };
864
- } catch (err) {
865
- const durationMs = Date.now() - started;
866
- return { ok: false, exitCode: 1, output: "", durationMs, error: String(err instanceof Error ? err.message : err), retryable: !(err instanceof LaneTimeoutError) };
867
- }
868
- }
869
- };
870
- }
871
- async function runCodexCli(job, cliPath, started, timeoutMs) {
872
- try {
873
- const controls = job.codexMode === "read-only" ? ["--sandbox", "read-only", "--skip-git-repo-check", "--config", 'approval_policy="never"'] : [];
874
- const result = await spawnCli([cliPath, "exec", ...controls, "--", job.prompt], {
875
- binaryName: cliPath,
876
- packageHint: "@openai/codex-sdk or the codex CLI",
877
- timeoutMs,
878
- env: job.env,
879
- cwd: job.cwd,
880
- stdoutOnly: true
881
- });
882
- const durationMs = Date.now() - started;
883
- return result.ok && !result.output.trim() ? missingResponse(durationMs) : { ...result, durationMs };
884
- } catch (err) {
885
- if (err instanceof LaneDependencyMissingError) {
886
- return { ok: false, exitCode: 127, output: "", durationMs: Date.now() - started, error: err.message };
887
- }
888
- throw err;
889
- }
890
- }
891
- function missingResponse(durationMs) {
892
- return { ok: false, exitCode: 1, output: "", durationMs, error: "Codex completed without an agent response; verify the native subscription and model route", retryable: false };
893
- }
894
- var init_codex = __esm(() => {
895
- init_graph();
896
- init_types();
897
- });
898
-
899
- // src/lanes/cursor.ts
900
- function createCursorAdapter(deps = {}) {
901
- return {
902
- kind: "cursor",
903
- async probe() {
904
- const loader = deps.sdkLoader ?? (async () => await import("@cursor/sdk"));
905
- try {
906
- await loader();
907
- return { kind: "cursor", sdk: "@cursor/sdk", wired: true, via: "sdk (local mode)" };
908
- } catch {
909
- if (binaryOnPath(deps.cliPath ?? "cursor-agent")) {
910
- return { kind: "cursor", sdk: "@cursor/sdk", wired: true, via: "cli" };
911
- }
912
- return {
913
- kind: "cursor",
914
- sdk: "@cursor/sdk",
915
- wired: false,
916
- reason: "neither @cursor/sdk nor the cursor-agent CLI is available on this machine"
917
- };
918
- }
919
- },
920
- async run(job) {
921
- if (!job.prompt) {
922
- return { ok: false, exitCode: 2, output: "", durationMs: 0, error: "cursor lane requires a prompt" };
923
- }
924
- if (!deps.sdkLoader && binaryOnPath(deps.cliPath ?? "cursor-agent"))
925
- return runCursorCli(job, deps.cliPath ?? "cursor-agent", Date.now(), job.timeoutMs ?? DEFAULT_LANE_TIMEOUT_MS);
926
- const started = Date.now();
927
- const timeoutMs = job.timeoutMs ?? DEFAULT_LANE_TIMEOUT_MS;
928
- const loader = deps.sdkLoader ?? (async () => await import("@cursor/sdk"));
929
- let sdk;
930
- try {
931
- sdk = await loader();
932
- } catch {
933
- return runCursorCli(job, deps.cliPath ?? "cursor-agent", started, timeoutMs);
934
- }
935
- let cancel;
936
- try {
937
- subscriptionEnvironment(job.env);
938
- if (!deps.sdkLoader && process.env.CURSOR_API_KEY)
939
- throw new Error("cursor lane refuses ambient API billing; use the native subscription CLI");
940
- const run = await withTimeout(runLocalAgent(sdk, job, (callback) => {
941
- cancel = callback;
942
- }), timeoutMs, "cursor lane", () => cancel?.());
943
- const durationMs = Date.now() - started;
944
- const status = run.status;
945
- const ok = status === "finished";
946
- return { ok, exitCode: ok ? 0 : 1, output: run.result, durationMs };
947
- } catch (err) {
948
- if (err instanceof LaneAdapterShapeError)
949
- throw err;
950
- const durationMs = Date.now() - started;
951
- return { ok: false, exitCode: 1, output: "", durationMs, error: String(err instanceof Error ? err.message : err), retryable: !(err instanceof LaneTimeoutError) };
952
- }
953
- }
954
- };
955
- }
956
- async function runLocalAgent(sdk, job, onCancel) {
957
- const AgentClass = sdk.Agent;
958
- const CursorClass = sdk.Cursor;
959
- if (typeof AgentClass !== "function" && typeof CursorClass !== "function") {
960
- throw new LaneAdapterShapeError("@cursor/sdk exports no Agent/Cursor class");
961
- }
962
- const create = AgentClass.create;
963
- const agent = typeof create === "function" ? await create.call(AgentClass, { local: { cwd: job.cwd, enableAgentRetries: false } }) : typeof AgentClass === "function" ? new AgentClass : undefined;
964
- if (!agent || typeof agent.send !== "function") {
965
- throw new LaneAdapterShapeError("@cursor/sdk Agent has no send()");
966
- }
967
- let run = await agent.send(job.prompt, { model: job.model });
968
- if (run && typeof run === "object") {
969
- const handle = run;
970
- if (typeof handle.cancel === "function")
971
- onCancel(() => {
972
- handle.cancel().catch(() => {});
973
- });
974
- if (typeof handle.wait === "function")
975
- run = await handle.wait();
976
- }
977
- if (!run || typeof run !== "object") {
978
- throw new LaneAdapterShapeError("@cursor/sdk Agent.send() returned no Run");
979
- }
980
- const candidate = run;
981
- if (typeof candidate.status !== "string" || !["finished", "error", "cancelled"].includes(candidate.status)) {
982
- throw new LaneAdapterShapeError(`@cursor/sdk Agent.send() returned a Run without a terminal status (got ${JSON.stringify(candidate.status)}; expected finished|error|cancelled)`);
983
- }
984
- if (typeof candidate.result !== "string") {
985
- throw new LaneAdapterShapeError("@cursor/sdk Agent.send() returned a Run without a string result");
986
- }
987
- return { status: candidate.status, result: candidate.result };
988
- }
989
- async function runCursorCli(job, cliPath, started, timeoutMs) {
990
- try {
991
- const result = await spawnCli([cliPath, "--print", job.prompt, ...job.model ? ["--model", job.model] : []], {
992
- binaryName: cliPath,
993
- packageHint: "@cursor/sdk (local mode) or the cursor-agent CLI",
994
- timeoutMs,
995
- env: job.env,
996
- cwd: job.cwd
997
- });
998
- return { ...result, durationMs: Date.now() - started };
999
- } catch (err) {
1000
- if (err instanceof LaneDependencyMissingError) {
1001
- return { ok: false, exitCode: 127, output: "", durationMs: Date.now() - started, error: err.message };
1002
- }
1003
- throw err;
1004
- }
1005
- }
1006
- var init_cursor = __esm(() => {
1007
- init_types();
1008
- });
1009
-
1010
- // src/lanes/grok.ts
1011
- function createGrokAdapter(deps = {}) {
1012
- return {
1013
- kind: "grok",
1014
- async probe() {
1015
- const cliPath = deps.cliPath ?? "grok";
1016
- if (binaryOnPath(cliPath)) {
1017
- return { kind: "grok", sdk: "no npm SDK (measured 2026-08-25)", wired: true, via: "cli" };
1018
- }
1019
- return {
1020
- kind: "grok",
1021
- sdk: "no npm SDK (measured 2026-08-25)",
1022
- wired: false,
1023
- reason: `no xAI Grok SDK exists on npm (measured 2026-08-25) and the grok CLI is not on PATH`
1024
- };
1025
- },
1026
- async run(job) {
1027
- if (!job.prompt) {
1028
- return { ok: false, exitCode: 2, output: "", durationMs: 0, error: "grok lane requires a prompt" };
1029
- }
1030
- const started = Date.now();
1031
- const timeoutMs = job.timeoutMs ?? DEFAULT_LANE_TIMEOUT_MS;
1032
- const cliPath = deps.cliPath ?? "grok";
1033
- try {
1034
- const result = await spawnCli([cliPath, job.prompt], {
1035
- binaryName: cliPath,
1036
- packageHint: "the grok CLI (no xAI Grok SDK exists on npm \u2014 measured 2026-08-25)",
1037
- timeoutMs,
1038
- env: job.env,
1039
- cwd: job.cwd
1040
- });
1041
- return { ...result, durationMs: Date.now() - started };
1042
- } catch (err) {
1043
- if (err instanceof LaneDependencyMissingError) {
1044
- return { ok: false, exitCode: 127, output: "", durationMs: Date.now() - started, error: err.message };
1045
- }
1046
- throw err;
1047
- }
1048
- }
1049
- };
1050
- }
1051
- var init_grok = __esm(() => {
1052
- init_types();
1053
- });
1054
-
1055
- // src/lanes/index.ts
1056
- var exports_lanes = {};
1057
- __export(exports_lanes, {
1058
- runLaneJob: () => runLaneJob,
1059
- resolveLane: () => resolveLane,
1060
- probeLane: () => probeLane,
1061
- laneInventory: () => laneInventory,
1062
- createGrokAdapter: () => createGrokAdapter,
1063
- createCursorAdapter: () => createCursorAdapter,
1064
- createCodexAdapter: () => createCodexAdapter,
1065
- createClaudeAdapter: () => createClaudeAdapter,
1066
- binaryOnPath: () => binaryOnPath,
1067
- LaneDependencyMissingError: () => LaneDependencyMissingError,
1068
- LaneAdapterShapeError: () => LaneAdapterShapeError,
1069
- LANE_KINDS: () => LANE_KINDS2
1070
- });
1071
- function resolveLane(kind) {
1072
- const adapter = adapters.get(kind);
1073
- if (!adapter)
1074
- throw new Error(`unknown lane ${kind} (expected one of ${LANE_KINDS2.join(", ")})`);
1075
- return adapter;
1076
- }
1077
- async function runLaneJob(job) {
1078
- return resolveLane(job.lane).run(job);
1079
- }
1080
- async function probeLane(kind) {
1081
- return resolveLane(kind).probe();
1082
- }
1083
- async function laneInventory() {
1084
- return Promise.all(LANE_KINDS2.map((kind) => probeLane(kind)));
1085
- }
1086
- var adapters;
1087
- var init_lanes = __esm(() => {
1088
- init_claude();
1089
- init_codex();
1090
- init_cursor();
1091
- init_grok();
1092
- init_types();
1093
- init_types();
1094
- init_claude();
1095
- init_codex();
1096
- init_cursor();
1097
- init_grok();
1098
- adapters = new Map([
1099
- ["claude", createClaudeAdapter()],
1100
- ["codex", createCodexAdapter()],
1101
- ["cursor", createCursorAdapter()],
1102
- ["grok", createGrokAdapter()]
1103
- ]);
1104
- });
1105
-
1106
602
  // src/agent-tools.ts
1107
603
  init_graph();
1108
- init_lanes();
1109
604
 
1110
605
  // src/tool-definitions.ts
1111
606
  function textResult(payload) {
@@ -1199,7 +694,7 @@ async function callWorkflowTool(service, name, args) {
1199
694
  limit: typeof args.limit === "number" ? args.limit : 100
1200
695
  }));
1201
696
  case "workflows_lanes_list":
1202
- return textResult(await laneInventory());
697
+ return textResult(await service.laneInventory());
1203
698
  default:
1204
699
  throw new Error("Unknown workflows tool");
1205
700
  }
package/dist/cli/index.js CHANGED
@@ -33,7 +33,7 @@ var __toESM = (mod, isNodeMode, target) => {
33
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
34
  var __require = import.meta.require;
35
35
 
36
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/lib/error.js
36
+ // ../../app-test-sandbox-1/source/node_modules/commander/lib/error.js
37
37
  var require_error = __commonJS((exports) => {
38
38
  class CommanderError extends Error {
39
39
  constructor(exitCode, code, message) {
@@ -57,7 +57,7 @@ var require_error = __commonJS((exports) => {
57
57
  exports.InvalidArgumentError = InvalidArgumentError;
58
58
  });
59
59
 
60
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/lib/argument.js
60
+ // ../../app-test-sandbox-1/source/node_modules/commander/lib/argument.js
61
61
  var require_argument = __commonJS((exports) => {
62
62
  var { InvalidArgumentError } = require_error();
63
63
 
@@ -136,7 +136,7 @@ var require_argument = __commonJS((exports) => {
136
136
  exports.humanReadableArgName = humanReadableArgName;
137
137
  });
138
138
 
139
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/lib/help.js
139
+ // ../../app-test-sandbox-1/source/node_modules/commander/lib/help.js
140
140
  var require_help = __commonJS((exports) => {
141
141
  var { humanReadableArgName } = require_argument();
142
142
 
@@ -486,7 +486,7 @@ ${itemIndentStr}`);
486
486
  exports.stripColor = stripColor;
487
487
  });
488
488
 
489
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/lib/option.js
489
+ // ../../app-test-sandbox-1/source/node_modules/commander/lib/option.js
490
490
  var require_option = __commonJS((exports) => {
491
491
  var { InvalidArgumentError } = require_error();
492
492
 
@@ -664,7 +664,7 @@ var require_option = __commonJS((exports) => {
664
664
  exports.DualOptions = DualOptions;
665
665
  });
666
666
 
667
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/lib/suggestSimilar.js
667
+ // ../../app-test-sandbox-1/source/node_modules/commander/lib/suggestSimilar.js
668
668
  var require_suggestSimilar = __commonJS((exports) => {
669
669
  var maxDistance = 3;
670
670
  function editDistance(a, b) {
@@ -737,7 +737,7 @@ var require_suggestSimilar = __commonJS((exports) => {
737
737
  exports.suggestSimilar = suggestSimilar;
738
738
  });
739
739
 
740
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/lib/command.js
740
+ // ../../app-test-sandbox-1/source/node_modules/commander/lib/command.js
741
741
  var require_command = __commonJS((exports) => {
742
742
  var EventEmitter = __require("events").EventEmitter;
743
743
  var childProcess = __require("child_process");
@@ -2047,7 +2047,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2047
2047
  exports.useColor = useColor;
2048
2048
  });
2049
2049
 
2050
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/index.js
2050
+ // ../../app-test-sandbox-1/source/node_modules/commander/index.js
2051
2051
  var require_commander = __commonJS((exports) => {
2052
2052
  var { Argument } = require_argument();
2053
2053
  var { Command } = require_command();
@@ -2067,7 +2067,7 @@ var require_commander = __commonJS((exports) => {
2067
2067
  exports.InvalidOptionArgumentError = InvalidArgumentError;
2068
2068
  });
2069
2069
 
2070
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/commander/esm.mjs
2070
+ // ../../app-test-sandbox-1/source/node_modules/commander/esm.mjs
2071
2071
  var import__ = __toESM(require_commander(), 1);
2072
2072
  var {
2073
2073
  program,
@@ -2086,7 +2086,7 @@ var {
2086
2086
  // src/cli/index.ts
2087
2087
  import { readFileSync as readFileSync5, readSync, writeFileSync as writeFileSync2 } from "fs";
2088
2088
 
2089
- // ../../../../workflows-codex-execution-repair-1/dependency-update-1/standalone/workflows/node_modules/@hasna/contracts/dist/client/transport.js
2089
+ // ../../app-test-sandbox-1/source/node_modules/@hasna/contracts/dist/client/transport.js
2090
2090
  import { isIP } from "net";
2091
2091
  import { spawnSync } from "child_process";
2092
2092
  import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync2 } from "fs";
@@ -3575,7 +3575,7 @@ function workflowAgent(env = process.env, explicit) {
3575
3575
  // package.json
3576
3576
  var package_default = {
3577
3577
  name: "@hasna/workflows",
3578
- version: "0.2.1",
3578
+ version: "0.2.2",
3579
3579
  description: "Universal graph workflow app \u2014 graph language (start/step/decision/while/end), three-table store, session WAL, daemon with claims/leases, four lane adapters (claude/codex/cursor/grok), and a full CLI on four surfaces",
3580
3580
  type: "module",
3581
3581
  main: "./dist/index.js",
@@ -3651,7 +3651,7 @@ var package_default = {
3651
3651
  dependencies: {
3652
3652
  "@anthropic-ai/claude-agent-sdk": "0.3.234",
3653
3653
  "@cursor/sdk": "^1.0.28",
3654
- "@hasna/contracts": "1.3.3",
3654
+ "@hasna/contracts": "1.3.5",
3655
3655
  "@openai/codex-sdk": "0.160.0",
3656
3656
  commander: "^13.1.0",
3657
3657
  pg: "^8.16.3"