@versatly/workgraph 1.2.0 → 1.3.1

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.
@@ -622,8 +622,7 @@ var BUILT_IN_TYPES = [
622
622
  proposed_at: { type: "date" },
623
623
  promoted_at: { type: "date" },
624
624
  depends_on: { type: "list", default: [], description: "Skill dependencies by slug or path" },
625
- distribution: { type: "string", default: "tailscale-shared-vault", description: "Distribution channel for skill usage" },
626
- tailscale_path: { type: "string", description: "Shared vault path over Tailscale" },
625
+ distribution: { type: "string", default: "shared-vault", description: "Distribution channel for skill usage" },
627
626
  tags: { type: "list", default: [] },
628
627
  created: { type: "date", required: true },
629
628
  updated: { type: "date", required: true }
@@ -841,8 +840,24 @@ function ensureBuiltIns(registry) {
841
840
  for (const t of BUILT_IN_TYPES) {
842
841
  if (!registry.types[t.name]) {
843
842
  registry.types[t.name] = t;
843
+ continue;
844
+ }
845
+ const existing = registry.types[t.name];
846
+ if (existing.builtIn) {
847
+ registry.types[t.name] = {
848
+ ...existing,
849
+ description: t.description,
850
+ directory: t.directory,
851
+ fields: {
852
+ ...existing.fields,
853
+ ...t.fields
854
+ }
855
+ };
844
856
  }
845
857
  }
858
+ if (registry.types.skill?.builtIn && "tailscale_path" in registry.types.skill.fields) {
859
+ delete registry.types.skill.fields.tailscale_path;
860
+ }
846
861
  return registry;
847
862
  }
848
863
 
@@ -4163,6 +4178,9 @@ function asDate(value) {
4163
4178
  return new Date(timestamp);
4164
4179
  }
4165
4180
 
4181
+ // src/adapter-shell-worker.ts
4182
+ import { spawn } from "child_process";
4183
+
4166
4184
  // src/orientation.ts
4167
4185
  var orientation_exports = {};
4168
4186
  __export(orientation_exports, {
@@ -4564,9 +4582,523 @@ function renderSummary(data) {
4564
4582
  return lines.join("\n");
4565
4583
  }
4566
4584
 
4585
+ // src/adapter-shell-worker.ts
4586
+ var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
4587
+ var MAX_CAPTURE_CHARS = 12e3;
4588
+ var ShellWorkerAdapter = class {
4589
+ name = "shell-worker";
4590
+ fallback = new CursorCloudAdapter();
4591
+ async create(_input) {
4592
+ return { runId: "shell-worker-managed", status: "queued" };
4593
+ }
4594
+ async status(runId) {
4595
+ return { runId, status: "running" };
4596
+ }
4597
+ async followup(runId, _actor, _input) {
4598
+ return { runId, status: "running" };
4599
+ }
4600
+ async stop(runId, _actor) {
4601
+ return { runId, status: "cancelled" };
4602
+ }
4603
+ async logs(_runId) {
4604
+ return [];
4605
+ }
4606
+ async execute(input) {
4607
+ const command = readString(input.context?.shell_command);
4608
+ if (!command) {
4609
+ return this.fallback.execute(input);
4610
+ }
4611
+ const shellCwd = readString(input.context?.shell_cwd) ?? input.workspacePath;
4612
+ const timeoutMs = clampInt(readNumber(input.context?.shell_timeout_ms), DEFAULT_TIMEOUT_MS, 1e3, 60 * 60 * 1e3);
4613
+ const shellEnv = readEnv(input.context?.shell_env);
4614
+ const logs2 = [];
4615
+ const startedAt = Date.now();
4616
+ const outputParts = [];
4617
+ const errorParts = [];
4618
+ pushLog2(logs2, "info", `shell-worker starting command: ${command}`);
4619
+ pushLog2(logs2, "info", `shell-worker cwd: ${shellCwd}`);
4620
+ const result = await runShellCommand({
4621
+ command,
4622
+ cwd: shellCwd,
4623
+ timeoutMs,
4624
+ env: shellEnv,
4625
+ isCancelled: input.isCancelled,
4626
+ onStdout: (chunk) => {
4627
+ outputParts.push(chunk);
4628
+ pushLog2(logs2, "info", `[stdout] ${chunk.trimEnd()}`);
4629
+ },
4630
+ onStderr: (chunk) => {
4631
+ errorParts.push(chunk);
4632
+ pushLog2(logs2, "warn", `[stderr] ${chunk.trimEnd()}`);
4633
+ }
4634
+ });
4635
+ const elapsedMs = Date.now() - startedAt;
4636
+ const stdout = truncateText(outputParts.join(""), MAX_CAPTURE_CHARS);
4637
+ const stderr = truncateText(errorParts.join(""), MAX_CAPTURE_CHARS);
4638
+ if (result.cancelled) {
4639
+ pushLog2(logs2, "warn", `shell-worker command cancelled after ${elapsedMs}ms`);
4640
+ return {
4641
+ status: "cancelled",
4642
+ output: formatShellOutput(command, result.exitCode, stdout, stderr, elapsedMs, true),
4643
+ logs: logs2
4644
+ };
4645
+ }
4646
+ if (result.timedOut) {
4647
+ pushLog2(logs2, "error", `shell-worker command timed out after ${elapsedMs}ms`);
4648
+ return {
4649
+ status: "failed",
4650
+ error: formatShellOutput(command, result.exitCode, stdout, stderr, elapsedMs, false),
4651
+ logs: logs2
4652
+ };
4653
+ }
4654
+ if (result.exitCode !== 0) {
4655
+ pushLog2(logs2, "error", `shell-worker command failed with exit code ${result.exitCode}`);
4656
+ return {
4657
+ status: "failed",
4658
+ error: formatShellOutput(command, result.exitCode, stdout, stderr, elapsedMs, false),
4659
+ logs: logs2
4660
+ };
4661
+ }
4662
+ pushLog2(logs2, "info", `shell-worker command succeeded in ${elapsedMs}ms`);
4663
+ return {
4664
+ status: "succeeded",
4665
+ output: formatShellOutput(command, result.exitCode, stdout, stderr, elapsedMs, false),
4666
+ logs: logs2,
4667
+ metrics: {
4668
+ elapsedMs,
4669
+ exitCode: result.exitCode,
4670
+ adapter: "shell-worker"
4671
+ }
4672
+ };
4673
+ }
4674
+ };
4675
+ async function runShellCommand(options) {
4676
+ return new Promise((resolve) => {
4677
+ const child = spawn(options.command, {
4678
+ cwd: options.cwd,
4679
+ env: { ...process.env, ...options.env },
4680
+ shell: true,
4681
+ stdio: ["ignore", "pipe", "pipe"]
4682
+ });
4683
+ let resolved = false;
4684
+ let timedOut = false;
4685
+ let cancelled = false;
4686
+ const timeoutHandle = setTimeout(() => {
4687
+ timedOut = true;
4688
+ child.kill("SIGTERM");
4689
+ setTimeout(() => child.kill("SIGKILL"), 1500).unref();
4690
+ }, options.timeoutMs);
4691
+ const cancelWatcher = setInterval(() => {
4692
+ if (options.isCancelled?.()) {
4693
+ cancelled = true;
4694
+ child.kill("SIGTERM");
4695
+ }
4696
+ }, 200);
4697
+ cancelWatcher.unref();
4698
+ child.stdout.on("data", (chunk) => {
4699
+ options.onStdout(chunk.toString("utf-8"));
4700
+ });
4701
+ child.stderr.on("data", (chunk) => {
4702
+ options.onStderr(chunk.toString("utf-8"));
4703
+ });
4704
+ child.on("close", (code) => {
4705
+ if (resolved) return;
4706
+ resolved = true;
4707
+ clearTimeout(timeoutHandle);
4708
+ clearInterval(cancelWatcher);
4709
+ resolve({
4710
+ exitCode: typeof code === "number" ? code : 1,
4711
+ timedOut,
4712
+ cancelled
4713
+ });
4714
+ });
4715
+ child.on("error", () => {
4716
+ if (resolved) return;
4717
+ resolved = true;
4718
+ clearTimeout(timeoutHandle);
4719
+ clearInterval(cancelWatcher);
4720
+ resolve({
4721
+ exitCode: 1,
4722
+ timedOut,
4723
+ cancelled
4724
+ });
4725
+ });
4726
+ });
4727
+ }
4728
+ function pushLog2(target, level, message) {
4729
+ target.push({
4730
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4731
+ level,
4732
+ message
4733
+ });
4734
+ }
4735
+ function readEnv(value) {
4736
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
4737
+ const input = value;
4738
+ const result = {};
4739
+ for (const [key, raw] of Object.entries(input)) {
4740
+ if (!key) continue;
4741
+ if (raw === void 0 || raw === null) continue;
4742
+ result[key] = String(raw);
4743
+ }
4744
+ return result;
4745
+ }
4746
+ function readString(value) {
4747
+ if (typeof value !== "string") return void 0;
4748
+ const trimmed = value.trim();
4749
+ return trimmed.length > 0 ? trimmed : void 0;
4750
+ }
4751
+ function readNumber(value) {
4752
+ if (typeof value === "number" && Number.isFinite(value)) return value;
4753
+ if (typeof value === "string" && value.trim().length > 0) {
4754
+ const parsed = Number(value);
4755
+ if (Number.isFinite(parsed)) return parsed;
4756
+ }
4757
+ return void 0;
4758
+ }
4759
+ function clampInt(value, fallback, min, max) {
4760
+ const raw = typeof value === "number" ? Math.trunc(value) : fallback;
4761
+ return Math.min(max, Math.max(min, raw));
4762
+ }
4763
+ function truncateText(value, limit) {
4764
+ if (value.length <= limit) return value;
4765
+ return `${value.slice(0, limit)}
4766
+ ...[truncated]`;
4767
+ }
4768
+ function formatShellOutput(command, exitCode, stdout, stderr, elapsedMs, cancelled) {
4769
+ const lines = [
4770
+ "Shell worker execution summary",
4771
+ `Command: ${command}`,
4772
+ `Exit code: ${exitCode}`,
4773
+ `Elapsed ms: ${elapsedMs}`,
4774
+ `Cancelled: ${cancelled ? "yes" : "no"}`,
4775
+ "",
4776
+ "STDOUT:",
4777
+ stdout || "(empty)",
4778
+ "",
4779
+ "STDERR:",
4780
+ stderr || "(empty)"
4781
+ ];
4782
+ return lines.join("\n");
4783
+ }
4784
+
4785
+ // src/adapter-claude-code.ts
4786
+ var ClaudeCodeAdapter = class {
4787
+ name = "claude-code";
4788
+ shellWorker = new ShellWorkerAdapter();
4789
+ async create(input) {
4790
+ return this.shellWorker.create(input);
4791
+ }
4792
+ async status(runId) {
4793
+ return this.shellWorker.status(runId);
4794
+ }
4795
+ async followup(runId, actor, input) {
4796
+ return this.shellWorker.followup(runId, actor, input);
4797
+ }
4798
+ async stop(runId, actor) {
4799
+ return this.shellWorker.stop(runId, actor);
4800
+ }
4801
+ async logs(runId) {
4802
+ return this.shellWorker.logs(runId);
4803
+ }
4804
+ async execute(input) {
4805
+ const template = readString2(input.context?.claude_command_template) ?? process.env.WORKGRAPH_CLAUDE_COMMAND_TEMPLATE;
4806
+ if (!template) {
4807
+ return {
4808
+ status: "failed",
4809
+ error: [
4810
+ "claude-code adapter requires a command template.",
4811
+ "Set context.claude_command_template or WORKGRAPH_CLAUDE_COMMAND_TEMPLATE.",
4812
+ "Template tokens: {workspace}, {run_id}, {actor}, {objective}, {prompt}, {prompt_shell}.",
4813
+ "Example: claude -p {prompt_shell}"
4814
+ ].join(" "),
4815
+ logs: [
4816
+ {
4817
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4818
+ level: "error",
4819
+ message: "Missing Claude command template."
4820
+ }
4821
+ ]
4822
+ };
4823
+ }
4824
+ const prompt = buildPrompt(input);
4825
+ const command = applyTemplate(template, {
4826
+ workspace: input.workspacePath,
4827
+ run_id: input.runId,
4828
+ actor: input.actor,
4829
+ objective: input.objective,
4830
+ prompt,
4831
+ prompt_shell: quoteForShell(prompt)
4832
+ });
4833
+ const context = {
4834
+ ...input.context,
4835
+ shell_command: command,
4836
+ shell_cwd: readString2(input.context?.shell_cwd) ?? input.workspacePath,
4837
+ shell_timeout_ms: input.context?.shell_timeout_ms ?? process.env.WORKGRAPH_CLAUDE_TIMEOUT_MS
4838
+ };
4839
+ const result = await this.shellWorker.execute({
4840
+ ...input,
4841
+ context
4842
+ });
4843
+ const logs2 = [
4844
+ {
4845
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4846
+ level: "info",
4847
+ message: "claude-code adapter dispatched shell execution from command template."
4848
+ },
4849
+ ...result.logs ?? []
4850
+ ];
4851
+ return {
4852
+ ...result,
4853
+ logs: logs2,
4854
+ metrics: {
4855
+ ...result.metrics ?? {},
4856
+ adapter: "claude-code"
4857
+ }
4858
+ };
4859
+ }
4860
+ };
4861
+ function buildPrompt(input) {
4862
+ const extraInstructions = readString2(input.context?.claude_instructions);
4863
+ const sections = [
4864
+ `Workgraph run id: ${input.runId}`,
4865
+ `Actor: ${input.actor}`,
4866
+ `Objective: ${input.objective}`,
4867
+ `Workspace: ${input.workspacePath}`
4868
+ ];
4869
+ if (extraInstructions) {
4870
+ sections.push(`Instructions: ${extraInstructions}`);
4871
+ }
4872
+ return sections.join("\n");
4873
+ }
4874
+ function applyTemplate(template, values) {
4875
+ let rendered = template;
4876
+ for (const [key, value] of Object.entries(values)) {
4877
+ rendered = rendered.replaceAll(`{${key}}`, value);
4878
+ }
4879
+ return rendered;
4880
+ }
4881
+ function quoteForShell(value) {
4882
+ return `'${value.replace(/'/g, `'\\''`)}'`;
4883
+ }
4884
+ function readString2(value) {
4885
+ if (typeof value !== "string") return void 0;
4886
+ const trimmed = value.trim();
4887
+ return trimmed.length > 0 ? trimmed : void 0;
4888
+ }
4889
+
4890
+ // src/adapter-http-webhook.ts
4891
+ var DEFAULT_POLL_MS = 1e3;
4892
+ var DEFAULT_MAX_WAIT_MS = 9e4;
4893
+ var HttpWebhookAdapter = class {
4894
+ name = "http-webhook";
4895
+ async create(_input) {
4896
+ return { runId: "http-webhook-managed", status: "queued" };
4897
+ }
4898
+ async status(runId) {
4899
+ return { runId, status: "running" };
4900
+ }
4901
+ async followup(runId, _actor, _input) {
4902
+ return { runId, status: "running" };
4903
+ }
4904
+ async stop(runId, _actor) {
4905
+ return { runId, status: "cancelled" };
4906
+ }
4907
+ async logs(_runId) {
4908
+ return [];
4909
+ }
4910
+ async execute(input) {
4911
+ const logs2 = [];
4912
+ const webhookUrl = resolveUrl(input.context?.webhook_url, process.env.WORKGRAPH_DISPATCH_WEBHOOK_URL);
4913
+ if (!webhookUrl) {
4914
+ return {
4915
+ status: "failed",
4916
+ error: "http-webhook adapter requires context.webhook_url or WORKGRAPH_DISPATCH_WEBHOOK_URL.",
4917
+ logs: logs2
4918
+ };
4919
+ }
4920
+ const token = readString3(input.context?.webhook_token) ?? process.env.WORKGRAPH_DISPATCH_WEBHOOK_TOKEN;
4921
+ const headers = {
4922
+ "content-type": "application/json",
4923
+ ...extractHeaders(input.context?.webhook_headers),
4924
+ ...token ? { authorization: `Bearer ${token}` } : {}
4925
+ };
4926
+ const payload = {
4927
+ runId: input.runId,
4928
+ actor: input.actor,
4929
+ objective: input.objective,
4930
+ workspacePath: input.workspacePath,
4931
+ context: input.context ?? {},
4932
+ ts: (/* @__PURE__ */ new Date()).toISOString()
4933
+ };
4934
+ pushLog3(logs2, "info", `http-webhook posting run ${input.runId} to ${webhookUrl}`);
4935
+ const response = await fetch(webhookUrl, {
4936
+ method: "POST",
4937
+ headers,
4938
+ body: JSON.stringify(payload)
4939
+ });
4940
+ const rawText = await response.text();
4941
+ const parsed = safeParseJson(rawText);
4942
+ pushLog3(logs2, response.ok ? "info" : "error", `http-webhook response status: ${response.status}`);
4943
+ if (!response.ok) {
4944
+ return {
4945
+ status: "failed",
4946
+ error: `http-webhook request failed (${response.status}): ${rawText || response.statusText}`,
4947
+ logs: logs2
4948
+ };
4949
+ }
4950
+ const immediateStatus = normalizeRunStatus(parsed?.status);
4951
+ if (immediateStatus && isTerminalStatus(immediateStatus)) {
4952
+ return {
4953
+ status: immediateStatus,
4954
+ output: typeof parsed?.output === "string" ? parsed.output : rawText,
4955
+ error: typeof parsed?.error === "string" ? parsed.error : void 0,
4956
+ logs: logs2,
4957
+ metrics: {
4958
+ adapter: "http-webhook",
4959
+ httpStatus: response.status
4960
+ }
4961
+ };
4962
+ }
4963
+ const pollUrl = resolveUrl(parsed?.pollUrl, input.context?.webhook_status_url, process.env.WORKGRAPH_DISPATCH_WEBHOOK_STATUS_URL);
4964
+ if (!pollUrl) {
4965
+ return {
4966
+ status: "succeeded",
4967
+ output: rawText || "http-webhook acknowledged run successfully.",
4968
+ logs: logs2,
4969
+ metrics: {
4970
+ adapter: "http-webhook",
4971
+ httpStatus: response.status
4972
+ }
4973
+ };
4974
+ }
4975
+ const pollMs = clampInt2(readNumber2(input.context?.webhook_poll_ms), DEFAULT_POLL_MS, 200, 3e4);
4976
+ const maxWaitMs = clampInt2(readNumber2(input.context?.webhook_max_wait_ms), DEFAULT_MAX_WAIT_MS, 1e3, 15 * 6e4);
4977
+ const startedAt = Date.now();
4978
+ pushLog3(logs2, "info", `http-webhook polling status from ${pollUrl}`);
4979
+ while (Date.now() - startedAt < maxWaitMs) {
4980
+ if (input.isCancelled?.()) {
4981
+ pushLog3(logs2, "warn", "http-webhook run cancelled while polling");
4982
+ return {
4983
+ status: "cancelled",
4984
+ output: "http-webhook polling cancelled by dispatcher.",
4985
+ logs: logs2
4986
+ };
4987
+ }
4988
+ const pollResponse = await fetch(pollUrl, {
4989
+ method: "GET",
4990
+ headers: {
4991
+ ...headers
4992
+ }
4993
+ });
4994
+ const pollText = await pollResponse.text();
4995
+ const pollJson = safeParseJson(pollText);
4996
+ const pollStatus = normalizeRunStatus(pollJson?.status);
4997
+ pushLog3(logs2, "info", `poll status=${pollResponse.status} run_status=${pollStatus ?? "unknown"}`);
4998
+ if (pollStatus && isTerminalStatus(pollStatus)) {
4999
+ return {
5000
+ status: pollStatus,
5001
+ output: typeof pollJson?.output === "string" ? pollJson.output : pollText,
5002
+ error: typeof pollJson?.error === "string" ? pollJson.error : void 0,
5003
+ logs: logs2,
5004
+ metrics: {
5005
+ adapter: "http-webhook",
5006
+ pollUrl,
5007
+ pollHttpStatus: pollResponse.status,
5008
+ elapsedMs: Date.now() - startedAt
5009
+ }
5010
+ };
5011
+ }
5012
+ await sleep3(pollMs);
5013
+ }
5014
+ return {
5015
+ status: "failed",
5016
+ error: `http-webhook polling exceeded timeout (${maxWaitMs}ms) for run ${input.runId}.`,
5017
+ logs: logs2
5018
+ };
5019
+ }
5020
+ };
5021
+ function pushLog3(target, level, message) {
5022
+ target.push({
5023
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
5024
+ level,
5025
+ message
5026
+ });
5027
+ }
5028
+ function readString3(value) {
5029
+ if (typeof value !== "string") return void 0;
5030
+ const trimmed = value.trim();
5031
+ return trimmed.length > 0 ? trimmed : void 0;
5032
+ }
5033
+ function resolveUrl(...values) {
5034
+ for (const value of values) {
5035
+ const parsed = readString3(value);
5036
+ if (!parsed) continue;
5037
+ try {
5038
+ const url = new URL(parsed);
5039
+ if (url.protocol === "http:" || url.protocol === "https:") {
5040
+ return url.toString();
5041
+ }
5042
+ } catch {
5043
+ continue;
5044
+ }
5045
+ }
5046
+ return void 0;
5047
+ }
5048
+ function extractHeaders(input) {
5049
+ if (!input || typeof input !== "object" || Array.isArray(input)) return {};
5050
+ const record = input;
5051
+ const out = {};
5052
+ for (const [key, value] of Object.entries(record)) {
5053
+ if (!key || value === void 0 || value === null) continue;
5054
+ out[key.toLowerCase()] = String(value);
5055
+ }
5056
+ return out;
5057
+ }
5058
+ function safeParseJson(value) {
5059
+ if (!value || !value.trim()) return null;
5060
+ try {
5061
+ const parsed = JSON.parse(value);
5062
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
5063
+ return parsed;
5064
+ } catch {
5065
+ return null;
5066
+ }
5067
+ }
5068
+ function normalizeRunStatus(value) {
5069
+ const normalized = String(value ?? "").toLowerCase();
5070
+ if (normalized === "queued" || normalized === "running" || normalized === "succeeded" || normalized === "failed" || normalized === "cancelled") {
5071
+ return normalized;
5072
+ }
5073
+ return void 0;
5074
+ }
5075
+ function isTerminalStatus(status2) {
5076
+ return status2 === "succeeded" || status2 === "failed" || status2 === "cancelled";
5077
+ }
5078
+ function readNumber2(value) {
5079
+ if (typeof value === "number" && Number.isFinite(value)) return value;
5080
+ if (typeof value === "string" && value.trim().length > 0) {
5081
+ const parsed = Number(value);
5082
+ if (Number.isFinite(parsed)) return parsed;
5083
+ }
5084
+ return void 0;
5085
+ }
5086
+ function clampInt2(value, fallback, min, max) {
5087
+ const raw = typeof value === "number" ? Math.trunc(value) : fallback;
5088
+ return Math.min(max, Math.max(min, raw));
5089
+ }
5090
+ function sleep3(ms) {
5091
+ return new Promise((resolve) => {
5092
+ setTimeout(resolve, ms);
5093
+ });
5094
+ }
5095
+
4567
5096
  // src/runtime-adapter-registry.ts
4568
5097
  var adapterFactories = /* @__PURE__ */ new Map([
4569
- ["cursor-cloud", () => new CursorCloudAdapter()]
5098
+ ["claude-code", () => new ClaudeCodeAdapter()],
5099
+ ["cursor-cloud", () => new CursorCloudAdapter()],
5100
+ ["http-webhook", () => new HttpWebhookAdapter()],
5101
+ ["shell-worker", () => new ShellWorkerAdapter()]
4570
5102
  ]);
4571
5103
  function resolveDispatchAdapter(name) {
4572
5104
  const safeName = normalizeName(name);
@@ -5049,9 +5581,9 @@ function resolveThreadRef2(threadRef) {
5049
5581
  // src/autonomy.ts
5050
5582
  async function runAutonomyLoop(workspacePath, options) {
5051
5583
  const watch = options.watch === true;
5052
- const pollMs = clampInt(options.pollMs, 2e3, 100, 6e4);
5053
- const maxCycles = clampInt(options.maxCycles, watch ? Number.MAX_SAFE_INTEGER : 20, 1, Number.MAX_SAFE_INTEGER);
5054
- const maxIdleCycles = clampInt(options.maxIdleCycles, watch ? Number.MAX_SAFE_INTEGER : 2, 1, Number.MAX_SAFE_INTEGER);
5584
+ const pollMs = clampInt3(options.pollMs, 2e3, 100, 6e4);
5585
+ const maxCycles = clampInt3(options.maxCycles, watch ? Number.MAX_SAFE_INTEGER : 20, 1, Number.MAX_SAFE_INTEGER);
5586
+ const maxIdleCycles = clampInt3(options.maxIdleCycles, watch ? Number.MAX_SAFE_INTEGER : 2, 1, Number.MAX_SAFE_INTEGER);
5055
5587
  const cycles = [];
5056
5588
  let idleCycles = 0;
5057
5589
  for (let cycle = 1; cycle <= maxCycles; cycle++) {
@@ -5110,7 +5642,7 @@ async function runAutonomyLoop(workspacePath, options) {
5110
5642
  if (cycle >= maxCycles) {
5111
5643
  break;
5112
5644
  }
5113
- await sleep3(pollMs);
5645
+ await sleep4(pollMs);
5114
5646
  }
5115
5647
  const finalReadyThreads = (options.space ? listReadyThreadsInSpace(workspacePath, options.space) : listReadyThreads(workspacePath)).length;
5116
5648
  writeHeartbeat(options.heartbeatFile, {
@@ -5124,11 +5656,11 @@ async function runAutonomyLoop(workspacePath, options) {
5124
5656
  finalDriftOk: true
5125
5657
  };
5126
5658
  }
5127
- function clampInt(value, fallback, min, max) {
5659
+ function clampInt3(value, fallback, min, max) {
5128
5660
  const raw = typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : fallback;
5129
5661
  return Math.min(max, Math.max(min, raw));
5130
5662
  }
5131
- function sleep3(ms) {
5663
+ function sleep4(ms) {
5132
5664
  return new Promise((resolve) => {
5133
5665
  setTimeout(resolve, ms);
5134
5666
  });
@@ -26,7 +26,7 @@ import {
26
26
  saveRegistry,
27
27
  stop,
28
28
  update
29
- } from "./chunk-G6B47IBD.js";
29
+ } from "./chunk-BJVBE7F4.js";
30
30
 
31
31
  // src/workspace.ts
32
32
  var workspace_exports = {};
@@ -363,8 +363,7 @@ function writeSkill(workspacePath, title, body, actor, options = {}) {
363
363
  owner: options.owner ?? actor,
364
364
  version: options.version ?? "0.1.0",
365
365
  status,
366
- distribution: options.distribution ?? "tailscale-shared-vault",
367
- tailscale_path: options.tailscalePath,
366
+ distribution: options.distribution ?? "shared-vault",
368
367
  reviewers: options.reviewers ?? [],
369
368
  depends_on: options.dependsOn ?? [],
370
369
  tags: options.tags ?? []
@@ -379,8 +378,7 @@ function writeSkill(workspacePath, title, body, actor, options = {}) {
379
378
  owner: options.owner ?? existing.fields.owner ?? actor,
380
379
  version: options.version ?? existing.fields.version ?? "0.1.0",
381
380
  status,
382
- distribution: options.distribution ?? existing.fields.distribution ?? "tailscale-shared-vault",
383
- tailscale_path: options.tailscalePath ?? existing.fields.tailscale_path,
381
+ distribution: options.distribution ?? existing.fields.distribution ?? "shared-vault",
384
382
  reviewers: options.reviewers ?? existing.fields.reviewers ?? [],
385
383
  depends_on: options.dependsOn ?? existing.fields.depends_on ?? [],
386
384
  tags: options.tags ?? existing.fields.tags ?? []
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-MCHTUXG2.js";
17
+ } from "./chunk-NGRQ6T4O.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-G6B47IBD.js";
33
+ } from "./chunk-BJVBE7F4.js";
34
34
 
35
35
  // src/cli.ts
36
36
  import fs from "fs";
package/dist/index.d.ts CHANGED
@@ -614,11 +614,11 @@ interface WriteSkillOptions {
614
614
  version?: string;
615
615
  status?: 'draft' | 'proposed' | 'active' | 'deprecated' | 'archived';
616
616
  distribution?: string;
617
- tailscalePath?: string;
618
617
  reviewers?: string[];
619
618
  tags?: string[];
620
619
  dependsOn?: string[];
621
620
  expectedUpdatedAt?: string;
621
+ tailscalePath?: string;
622
622
  }
623
623
  interface ProposeSkillOptions {
624
624
  proposalThread?: 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-MCHTUXG2.js";
19
+ } from "./chunk-NGRQ6T4O.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-G6B47IBD.js";
41
+ } from "./chunk-BJVBE7F4.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-G6B47IBD.js";
4
+ } from "./chunk-BJVBE7F4.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.2.0",
3
+ "version": "1.3.1",
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/*"