@hasna/loops 0.3.59 → 0.3.60

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.
@@ -358,6 +358,20 @@ function optionalStringArray(value, label) {
358
358
  }).filter(Boolean);
359
359
  return values.length ? values : undefined;
360
360
  }
361
+ var UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS = new Set([
362
+ "e",
363
+ "exec",
364
+ "agent",
365
+ "start",
366
+ "--ephemeral",
367
+ "--ignore-rules",
368
+ "--skip-git-repo-check",
369
+ "--json",
370
+ "--output-last-message",
371
+ "-o",
372
+ "--output-schema",
373
+ "--dangerously-bypass-approvals-and-sandbox"
374
+ ]);
361
375
  function optionalAccountRef(value, label) {
362
376
  if (value === undefined)
363
377
  return;
@@ -472,6 +486,15 @@ function validateTarget(value, label, opts) {
472
486
  throw new Error(`${label}.variant is not supported for provider cursor`);
473
487
  if (value.provider === "codex" && value.agent !== undefined)
474
488
  throw new Error(`${label}.agent is not supported for provider codex`);
489
+ if (value.provider === "codewith" && value.agent !== undefined) {
490
+ throw new Error(`${label}.agent is not supported for provider codewith durable background-agent execution`);
491
+ }
492
+ const extraArgs = optionalStringArray(value.extraArgs, `${label}.extraArgs`);
493
+ if (value.provider === "codewith") {
494
+ const unsafe = extraArgs?.find((arg) => UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS.has(arg));
495
+ if (unsafe)
496
+ throw new Error(`${label}.extraArgs cannot include ${unsafe}; codewith agent steps use durable agent start, not exec/ephemeral flags`);
497
+ }
475
498
  optionalStringArray(value.addDirs, `${label}.addDirs`);
476
499
  if (Array.isArray(value.addDirs) && value.addDirs.length > 0 && !["codewith", "codex"].includes(value.provider)) {
477
500
  throw new Error(`${label}.addDirs is currently supported only for provider codewith or codex`);
@@ -547,7 +570,9 @@ function validateTarget(value, label, opts) {
547
570
  if (value.routing.eventSource !== undefined)
548
571
  assertString(value.routing.eventSource, `${label}.routing.eventSource`);
549
572
  }
550
- const target = { ...value };
573
+ const target = { ...value, extraArgs };
574
+ if (!extraArgs)
575
+ delete target.extraArgs;
551
576
  delete target.promptFile;
552
577
  delete target.promptSource;
553
578
  return { ...target, ...promptFields };
@@ -3488,6 +3513,9 @@ function appendBounded(current, chunk, maxBytes) {
3488
3513
  return `[truncated ${overflow} bytes]
3489
3514
  ${next.slice(-maxBytes)}`;
3490
3515
  }
3516
+ function appendBoundedText(current, chunk, maxBytes) {
3517
+ return appendBounded(current, Buffer.from(chunk, "utf8"), maxBytes);
3518
+ }
3491
3519
  function killProcessGroup(pid) {
3492
3520
  try {
3493
3521
  process.kill(-pid, "SIGTERM");
@@ -3535,6 +3563,21 @@ function metadataEnv(metadata) {
3535
3563
  env.LOOPS_GOAL_NODE_KEY = metadata.goalNodeKey;
3536
3564
  return env;
3537
3565
  }
3566
+ function codewithAgentIdempotencyKey(metadata) {
3567
+ const parts = [
3568
+ "openloops",
3569
+ metadata.workflowRunId ? `workflow-run:${metadata.workflowRunId}` : undefined,
3570
+ metadata.workflowStepId ? `step:${metadata.workflowStepId}` : undefined,
3571
+ metadata.runId ? `loop-run:${metadata.runId}` : undefined,
3572
+ metadata.loopId ? `loop:${metadata.loopId}` : undefined,
3573
+ metadata.scheduledFor ? `scheduled:${metadata.scheduledFor}` : undefined,
3574
+ metadata.goalId ? `goal:${metadata.goalId}` : undefined,
3575
+ metadata.goalNodeKey ? `node:${metadata.goalNodeKey}` : undefined
3576
+ ].filter(Boolean);
3577
+ if (parts.length > 1)
3578
+ return parts.join(":");
3579
+ return `openloops:adhoc:${randomBytes2(16).toString("hex")}`;
3580
+ }
3538
3581
  function allowlistEnv(allowlist) {
3539
3582
  const env = {};
3540
3583
  if (allowlist?.tools?.length)
@@ -3571,6 +3614,20 @@ function codewithLikeSandbox(target) {
3571
3614
  function configStringValue(value) {
3572
3615
  return JSON.stringify(value);
3573
3616
  }
3617
+ var UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS2 = new Set([
3618
+ "e",
3619
+ "exec",
3620
+ "agent",
3621
+ "start",
3622
+ "--ephemeral",
3623
+ "--ignore-rules",
3624
+ "--skip-git-repo-check",
3625
+ "--json",
3626
+ "--output-last-message",
3627
+ "-o",
3628
+ "--output-schema",
3629
+ "--dangerously-bypass-approvals-and-sandbox"
3630
+ ]);
3574
3631
  function assertStringOption(value, label) {
3575
3632
  if (value !== undefined && typeof value !== "string")
3576
3633
  throw new Error(`${label} must be a string`);
@@ -3601,6 +3658,14 @@ function assertSupportedAgentOptions(target) {
3601
3658
  }
3602
3659
  if (target.provider === "codex" && target.agent !== undefined)
3603
3660
  throw new Error("codex.agent is not supported");
3661
+ if (target.provider === "codewith" && target.agent !== undefined) {
3662
+ throw new Error("codewith.agent is not supported by the durable background-agent adapter");
3663
+ }
3664
+ if (target.provider === "codewith") {
3665
+ const unsafe = target.extraArgs?.find((arg) => UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS2.has(arg));
3666
+ if (unsafe)
3667
+ throw new Error(`codewith.extraArgs cannot include ${unsafe}; durable agent steps use codewith agent start, not exec/ephemeral flags`);
3668
+ }
3604
3669
  if (["codewith", "codex"].includes(target.provider)) {
3605
3670
  if (target.permissionMode && !["default", "bypass"].includes(target.permissionMode)) {
3606
3671
  throw new Error(`${target.provider}.permissionMode supports only default or bypass`);
@@ -3684,18 +3749,18 @@ function agentArgs(target) {
3684
3749
  args.push(...target.authProfile ? ["--auth-profile", target.authProfile] : []);
3685
3750
  if (target.variant)
3686
3751
  args.push("-c", `model_reasoning_effort=${configStringValue(target.variant)}`);
3687
- args.push("--ask-for-approval", "never", "exec", "--json", "--ephemeral", "--sandbox", codewithLikeSandbox(target), "--skip-git-repo-check");
3688
- if (isolation === "safe")
3689
- args.push("--ignore-rules");
3752
+ args.push("--ask-for-approval", "never", "--sandbox", codewithLikeSandbox(target));
3690
3753
  if (target.cwd)
3691
3754
  args.push("--cd", target.cwd);
3692
3755
  for (const dir of target.addDirs ?? [])
3693
3756
  args.push("--add-dir", dir);
3694
3757
  if (target.model)
3695
3758
  args.push("--model", target.model);
3696
- if (target.agent)
3697
- args.push("--agent", target.agent);
3698
3759
  args.push(...target.extraArgs ?? []);
3760
+ args.push("agent", "start");
3761
+ if (target.cwd)
3762
+ args.push("--cwd", target.cwd);
3763
+ args.push(target.prompt);
3699
3764
  return args;
3700
3765
  case "codex":
3701
3766
  if (target.variant)
@@ -3771,8 +3836,9 @@ function commandSpec(target) {
3771
3836
  accountTool: agentTarget.account?.tool ?? accountToolForProvider(agentTarget.provider),
3772
3837
  nativeAuthProfile: agentTarget.authProfile ? { provider: agentTarget.provider, profile: agentTarget.authProfile } : undefined,
3773
3838
  preflightAnyOf: agentTarget.provider === "cursor" ? ["agent"] : undefined,
3774
- stdin: agentTarget.prompt,
3775
- allowlist: agentTarget.allowlist
3839
+ stdin: agentTarget.provider === "codewith" ? undefined : agentTarget.prompt,
3840
+ allowlist: agentTarget.allowlist,
3841
+ codewithDurableAgent: agentTarget.provider === "codewith" ? { target: agentTarget } : undefined
3776
3842
  };
3777
3843
  }
3778
3844
  function executionEnv(spec, metadata, opts) {
@@ -3892,6 +3958,238 @@ function preflightNativeAuthProfile(spec, env) {
3892
3958
  throw new Error(`codewith auth profile not found: ${spec.nativeAuthProfile.profile}`);
3893
3959
  }
3894
3960
  }
3961
+ function codewithAgentStartArgs(target, idempotencyKey) {
3962
+ const args = agentArgs(target);
3963
+ const startIndex = args.findIndex((arg, index) => arg === "start" && args[index - 1] === "agent");
3964
+ if (startIndex === -1)
3965
+ throw new Error("internal error: codewith durable agent args missing agent start");
3966
+ args.splice(startIndex + 1, 0, "--idempotency-key", idempotencyKey);
3967
+ return args;
3968
+ }
3969
+ function codewithAgentControlArgs(target, command, agentId) {
3970
+ return [
3971
+ ...target.authProfile ? ["--auth-profile", target.authProfile] : [],
3972
+ "agent",
3973
+ command,
3974
+ ...command === "logs" ? ["--limit", "20"] : [],
3975
+ agentId
3976
+ ];
3977
+ }
3978
+ function parseJsonOutput(stdout, label) {
3979
+ try {
3980
+ const value = JSON.parse(stdout || "{}");
3981
+ if (!value || typeof value !== "object" || Array.isArray(value))
3982
+ throw new Error("not an object");
3983
+ return value;
3984
+ } catch (error) {
3985
+ throw new Error(`${label} did not return JSON${error instanceof Error ? `: ${error.message}` : ""}`);
3986
+ }
3987
+ }
3988
+ function recordField(value, key) {
3989
+ if (!value || typeof value !== "object" || Array.isArray(value))
3990
+ return;
3991
+ const field = value[key];
3992
+ return field && typeof field === "object" && !Array.isArray(field) ? field : undefined;
3993
+ }
3994
+ function stringField(value, key) {
3995
+ const field = value?.[key];
3996
+ return typeof field === "string" ? field : undefined;
3997
+ }
3998
+ function numberField(value, key) {
3999
+ const field = value?.[key];
4000
+ return typeof field === "number" && Number.isFinite(field) ? field : undefined;
4001
+ }
4002
+ function codewithAgentStatus(readJson) {
4003
+ return stringField(recordField(readJson, "agent"), "status") ?? stringField(recordField(readJson, "statusSnapshot"), "status");
4004
+ }
4005
+ function codewithAgentEvidence(startJson, readJson, logsJson) {
4006
+ const agent = recordField(readJson, "agent") ?? recordField(startJson, "agent");
4007
+ const statusSnapshot = recordField(readJson, "statusSnapshot");
4008
+ const events = Array.isArray(logsJson?.data) ? logsJson.data.filter((event) => Boolean(event) && typeof event === "object" && !Array.isArray(event)).map((event) => ({
4009
+ seq: numberField(event, "seq"),
4010
+ eventType: stringField(event, "eventType"),
4011
+ createdAt: numberField(event, "createdAt")
4012
+ })) : undefined;
4013
+ return JSON.stringify({
4014
+ codewithAgent: {
4015
+ agentId: stringField(agent, "agentId"),
4016
+ status: stringField(agent, "status"),
4017
+ desiredState: stringField(agent, "desiredState"),
4018
+ statusReason: stringField(agent, "statusReason"),
4019
+ threadId: stringField(agent, "threadId"),
4020
+ rolloutPath: stringField(agent, "rolloutPath"),
4021
+ pid: numberField(agent, "pid"),
4022
+ exitCode: numberField(agent, "exitCode"),
4023
+ created: typeof startJson.created === "boolean" ? startJson.created : undefined
4024
+ },
4025
+ statusSnapshot: statusSnapshot ? {
4026
+ seq: numberField(statusSnapshot, "seq"),
4027
+ status: stringField(statusSnapshot, "status"),
4028
+ summary: stringField(statusSnapshot, "summary"),
4029
+ pendingInteractionCount: numberField(statusSnapshot, "pendingInteractionCount"),
4030
+ lastEventSeq: numberField(statusSnapshot, "lastEventSeq")
4031
+ } : undefined,
4032
+ events
4033
+ }, null, 2);
4034
+ }
4035
+ function sleep(ms) {
4036
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
4037
+ }
4038
+ async function executeCodewithDurableAgent(spec, metadata, opts, env, startedAt) {
4039
+ const target = spec.codewithDurableAgent?.target;
4040
+ if (!target)
4041
+ throw new Error("internal error: missing codewith durable target");
4042
+ const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
4043
+ const idempotencyKey = codewithAgentIdempotencyKey(metadata);
4044
+ const start = spawnSync2(spec.command, codewithAgentStartArgs(target, idempotencyKey), {
4045
+ cwd: spec.cwd,
4046
+ env,
4047
+ encoding: "utf8",
4048
+ stdio: ["ignore", "pipe", "pipe"],
4049
+ timeout: 30000
4050
+ });
4051
+ let stderr = start.stderr || "";
4052
+ if (start.error || (start.status ?? 1) !== 0) {
4053
+ const finishedAt = nowIso();
4054
+ return {
4055
+ status: "failed",
4056
+ exitCode: start.status ?? undefined,
4057
+ stdout: start.stdout || "",
4058
+ stderr,
4059
+ error: start.error?.message ?? `codewith agent start exited with code ${start.status ?? "unknown"}`,
4060
+ startedAt,
4061
+ finishedAt,
4062
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4063
+ };
4064
+ }
4065
+ let startJson;
4066
+ try {
4067
+ startJson = parseJsonOutput(start.stdout || "{}", "codewith agent start");
4068
+ } catch (error) {
4069
+ const finishedAt = nowIso();
4070
+ return {
4071
+ status: "failed",
4072
+ stdout: start.stdout || "",
4073
+ stderr,
4074
+ error: error instanceof Error ? error.message : String(error),
4075
+ startedAt,
4076
+ finishedAt,
4077
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4078
+ };
4079
+ }
4080
+ const agentId = stringField(recordField(startJson, "agent"), "agentId");
4081
+ if (!agentId) {
4082
+ const finishedAt = nowIso();
4083
+ return {
4084
+ status: "failed",
4085
+ stdout: start.stdout || "",
4086
+ stderr,
4087
+ error: "codewith agent start did not return agent.agentId",
4088
+ startedAt,
4089
+ finishedAt,
4090
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4091
+ };
4092
+ }
4093
+ const pollMs = Math.max(100, Number(opts.env?.LOOPS_CODEWITH_AGENT_POLL_MS ?? process.env.LOOPS_CODEWITH_AGENT_POLL_MS ?? 2000) || 2000);
4094
+ let lastReadJson = startJson;
4095
+ let lastLogsJson;
4096
+ let stdout = "";
4097
+ while (true) {
4098
+ if (opts.signal?.aborted) {
4099
+ spawnSync2(spec.command, codewithAgentControlArgs(target, "stop", agentId), { cwd: spec.cwd, env, stdio: "ignore", timeout: 15000 });
4100
+ const finishedAt = nowIso();
4101
+ return {
4102
+ status: "failed",
4103
+ stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
4104
+ stderr,
4105
+ error: "cancelled",
4106
+ startedAt,
4107
+ finishedAt,
4108
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4109
+ };
4110
+ }
4111
+ const read = spawnSync2(spec.command, codewithAgentControlArgs(target, "read", agentId), {
4112
+ cwd: spec.cwd,
4113
+ env,
4114
+ encoding: "utf8",
4115
+ stdio: ["ignore", "pipe", "pipe"],
4116
+ timeout: 30000
4117
+ });
4118
+ stderr = appendBoundedText(stderr, read.stderr || "", maxOutputBytes);
4119
+ if (read.error || (read.status ?? 1) !== 0) {
4120
+ const finishedAt = nowIso();
4121
+ return {
4122
+ status: "failed",
4123
+ exitCode: read.status ?? undefined,
4124
+ stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
4125
+ stderr,
4126
+ error: read.error?.message ?? `codewith agent read exited with code ${read.status ?? "unknown"}`,
4127
+ startedAt,
4128
+ finishedAt,
4129
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4130
+ };
4131
+ }
4132
+ try {
4133
+ lastReadJson = parseJsonOutput(read.stdout || "{}", "codewith agent read");
4134
+ } catch (error) {
4135
+ const finishedAt = nowIso();
4136
+ return {
4137
+ status: "failed",
4138
+ stdout: appendBoundedText(stdout, read.stdout || "", maxOutputBytes),
4139
+ stderr,
4140
+ error: error instanceof Error ? error.message : String(error),
4141
+ startedAt,
4142
+ finishedAt,
4143
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4144
+ };
4145
+ }
4146
+ const status = codewithAgentStatus(lastReadJson);
4147
+ if (status === "completed" || status === "failed" || status === "cancelled") {
4148
+ const logs = spawnSync2(spec.command, codewithAgentControlArgs(target, "logs", agentId), {
4149
+ cwd: spec.cwd,
4150
+ env,
4151
+ encoding: "utf8",
4152
+ stdio: ["ignore", "pipe", "pipe"],
4153
+ timeout: 30000
4154
+ });
4155
+ if (!logs.error && (logs.status ?? 1) === 0) {
4156
+ try {
4157
+ lastLogsJson = parseJsonOutput(logs.stdout || "{}", "codewith agent logs");
4158
+ } catch {
4159
+ lastLogsJson = undefined;
4160
+ }
4161
+ } else {
4162
+ stderr = appendBoundedText(stderr, logs.stderr || logs.error?.message || "", maxOutputBytes);
4163
+ }
4164
+ const finishedAt = nowIso();
4165
+ const resultStatus = status === "completed" ? "succeeded" : "failed";
4166
+ return {
4167
+ status: resultStatus,
4168
+ exitCode: resultStatus === "succeeded" ? 0 : 1,
4169
+ stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
4170
+ stderr,
4171
+ error: resultStatus === "succeeded" ? undefined : stringField(recordField(lastReadJson, "agent"), "statusReason") ?? `codewith agent ${status}`,
4172
+ startedAt,
4173
+ finishedAt,
4174
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4175
+ };
4176
+ }
4177
+ if (typeof spec.timeoutMs === "number" && new Date(nowIso()).getTime() - new Date(startedAt).getTime() >= spec.timeoutMs) {
4178
+ spawnSync2(spec.command, codewithAgentControlArgs(target, "stop", agentId), { cwd: spec.cwd, env, stdio: "ignore", timeout: 15000 });
4179
+ const finishedAt = nowIso();
4180
+ return {
4181
+ status: "timed_out",
4182
+ stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
4183
+ stderr,
4184
+ error: `timed out after ${spec.timeoutMs}ms`,
4185
+ startedAt,
4186
+ finishedAt,
4187
+ durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
4188
+ };
4189
+ }
4190
+ await sleep(pollMs);
4191
+ }
4192
+ }
3895
4193
  function preflightRemoteSpec(spec, machine, metadata, opts) {
3896
4194
  const plan = (opts.machineCommandResolver ?? resolveMachineCommand)(machine.id, "bash -s");
3897
4195
  const result = spawnSync2(plan.command, plan.args, {
@@ -4063,6 +4361,19 @@ function preflightTarget(target, metadata = {}, opts = {}) {
4063
4361
  async function executeTarget(target, metadata = {}, opts = {}) {
4064
4362
  const spec = commandSpec(target);
4065
4363
  const machine = resolvedMachine(opts);
4364
+ if (machine && !machine.local && spec.codewithDurableAgent) {
4365
+ const startedAt2 = nowIso();
4366
+ const finishedAt2 = nowIso();
4367
+ return {
4368
+ status: "failed",
4369
+ stdout: "",
4370
+ stderr: "",
4371
+ error: "remote Codewith durable background-agent steps require remote status polling support; run this Codewith step locally or add remote durable readback before dispatch",
4372
+ startedAt: startedAt2,
4373
+ finishedAt: finishedAt2,
4374
+ durationMs: new Date(finishedAt2).getTime() - new Date(startedAt2).getTime()
4375
+ };
4376
+ }
4066
4377
  if (machine && !machine.local)
4067
4378
  return executeRemoteSpec(spec, machine, metadata, opts);
4068
4379
  const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
@@ -4109,6 +4420,9 @@ async function executeTarget(target, metadata = {}, opts = {}) {
4109
4420
  durationMs: 0
4110
4421
  };
4111
4422
  }
4423
+ if (spec.codewithDurableAgent) {
4424
+ return executeCodewithDurableAgent(spec, metadata, opts, env, startedAt);
4425
+ }
4112
4426
  const child = spawn(spec.command, spec.args, {
4113
4427
  cwd: spec.cwd,
4114
4428
  env,
@@ -5320,7 +5634,7 @@ function realSleep(ms) {
5320
5634
  return new Promise((resolve2) => setTimeout(resolve2, ms));
5321
5635
  }
5322
5636
  async function runLoop(opts) {
5323
- const sleep = opts.sleep ?? realSleep;
5637
+ const sleep2 = opts.sleep ?? realSleep;
5324
5638
  const sliceMs = opts.sliceMs ?? 200;
5325
5639
  while (!opts.shouldStop()) {
5326
5640
  try {
@@ -5331,7 +5645,7 @@ async function runLoop(opts) {
5331
5645
  let waited = 0;
5332
5646
  while (waited < opts.intervalMs && !opts.shouldStop()) {
5333
5647
  const chunk = Math.min(sliceMs, opts.intervalMs - waited);
5334
- await sleep(chunk);
5648
+ await sleep2(chunk);
5335
5649
  waited += chunk;
5336
5650
  }
5337
5651
  }
@@ -5413,7 +5727,7 @@ async function stopDaemon(opts = {}) {
5413
5727
  } finally {
5414
5728
  store.close();
5415
5729
  }
5416
- const sleep = opts.sleep ?? realSleep;
5730
+ const sleep2 = opts.sleep ?? realSleep;
5417
5731
  try {
5418
5732
  process.kill(state.pid, "SIGTERM");
5419
5733
  } catch {
@@ -5422,7 +5736,7 @@ async function stopDaemon(opts = {}) {
5422
5736
  }
5423
5737
  const steps = Math.max(1, Math.ceil((opts.timeoutMs ?? 6000) / 100));
5424
5738
  for (let i = 0;i < steps; i++) {
5425
- await sleep(100);
5739
+ await sleep2(100);
5426
5740
  if (!isAlive(state.pid)) {
5427
5741
  removePid(path);
5428
5742
  return { wasRunning: true, stopped: true, forced: false, pid: state.pid };
@@ -5431,7 +5745,7 @@ async function stopDaemon(opts = {}) {
5431
5745
  try {
5432
5746
  process.kill(state.pid, "SIGKILL");
5433
5747
  } catch {}
5434
- await sleep(150);
5748
+ await sleep2(150);
5435
5749
  removePid(path);
5436
5750
  return { wasRunning: true, stopped: !isAlive(state.pid), forced: true, pid: state.pid };
5437
5751
  }
@@ -5597,10 +5911,10 @@ async function startDaemon(opts) {
5597
5911
  stdio: ["ignore", out, out]
5598
5912
  });
5599
5913
  child.unref();
5600
- const sleep = opts.sleep ?? realSleep;
5914
+ const sleep2 = opts.sleep ?? realSleep;
5601
5915
  const deadline = Math.max(1, Math.ceil((opts.waitMs ?? 4000) / 100));
5602
5916
  for (let i = 0;i < deadline; i++) {
5603
- await sleep(100);
5917
+ await sleep2(100);
5604
5918
  const current = isDaemonRunning();
5605
5919
  if (current.running)
5606
5920
  return { started: true, alreadyRunning: false, pid: current.pid };
@@ -5695,7 +6009,7 @@ function enableStartup(result) {
5695
6009
  // package.json
5696
6010
  var package_default = {
5697
6011
  name: "@hasna/loops",
5698
- version: "0.3.59",
6012
+ version: "0.3.60",
5699
6013
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5700
6014
  type: "module",
5701
6015
  main: "dist/index.js",