@hasna/loops 0.4.7 → 0.4.9

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/dist/sdk/index.js CHANGED
@@ -579,7 +579,7 @@ import { isAbsolute, resolve } from "path";
579
579
 
580
580
  // src/lib/agent-adapter.ts
581
581
  import { spawn } from "child_process";
582
- var UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS = new Set([
582
+ var UNSAFE_CODEWITH_EXEC_EXTRA_ARGS = new Set([
583
583
  "e",
584
584
  "exec",
585
585
  "agent",
@@ -628,12 +628,12 @@ function validateAgentOptions(target, label, capabilities) {
628
628
  throw new Error(`${label}.agent is not supported for provider codex`);
629
629
  }
630
630
  if (provider === "codewith" && target.agent !== undefined) {
631
- throw new Error(`${label}.agent is not supported for provider codewith durable background-agent execution`);
631
+ throw new Error(`${label}.agent is not supported for provider codewith`);
632
632
  }
633
633
  if (provider === "codewith") {
634
- const unsafe = target.extraArgs?.find((arg) => UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS.has(arg));
634
+ const unsafe = target.extraArgs?.find((arg) => UNSAFE_CODEWITH_EXEC_EXTRA_ARGS.has(arg));
635
635
  if (unsafe) {
636
- throw new Error(`${label}.extraArgs cannot include ${unsafe}; codewith agent steps use durable agent start, not exec/ephemeral flags`);
636
+ throw new Error(`${label}.extraArgs cannot include ${unsafe}; codewith exec launch flags are managed by the adapter`);
637
637
  }
638
638
  }
639
639
  if (target.addDirs?.length && !["codewith", "codex"].includes(provider)) {
@@ -716,7 +716,10 @@ function buildAgentInvocation(target) {
716
716
  args.push(...target.authProfile ? ["--auth-profile", target.authProfile] : []);
717
717
  if (target.variant)
718
718
  args.push("-c", `model_reasoning_effort=${configStringValue(target.variant)}`);
719
- args.push("--ask-for-approval", "never", "--sandbox", codewithLikeSandbox(target));
719
+ const sandbox = codewithLikeSandbox(target);
720
+ if (sandbox === "workspace-write")
721
+ args.push("-c", "sandbox_workspace_write.network_access=true");
722
+ args.push("--ask-for-approval", "never", "exec", "--json", "--ephemeral", "--sandbox", sandbox, "--skip-git-repo-check");
720
723
  if (target.cwd)
721
724
  args.push("--cd", target.cwd);
722
725
  for (const dir of target.addDirs ?? [])
@@ -724,11 +727,7 @@ function buildAgentInvocation(target) {
724
727
  if (target.model)
725
728
  args.push("--model", target.model);
726
729
  args.push(...target.extraArgs ?? []);
727
- args.push("agent", "start", "--json");
728
- if (target.cwd)
729
- args.push("--cwd", target.cwd);
730
- args.push(target.prompt);
731
- return { command: "codewith", args };
730
+ return { command: "codewith", args, stdin: target.prompt };
732
731
  }
733
732
  case "codex": {
734
733
  if (target.variant)
@@ -781,7 +780,7 @@ function adapterFor(provider, capabilities) {
781
780
  var PROVIDER_ADAPTERS = {
782
781
  claude: adapterFor("claude", { sandbox: [], durable: false, remote: true, promptChannel: "stdin" }),
783
782
  cursor: adapterFor("cursor", { sandbox: CURSOR_SANDBOXES, durable: false, remote: true, promptChannel: "stdin" }),
784
- codewith: adapterFor("codewith", { sandbox: CODEX_LIKE_SANDBOXES, durable: true, remote: false, promptChannel: "argv" }),
783
+ codewith: adapterFor("codewith", { sandbox: CODEX_LIKE_SANDBOXES, durable: false, remote: true, promptChannel: "stdin" }),
785
784
  codex: adapterFor("codex", { sandbox: CODEX_LIKE_SANDBOXES, durable: false, remote: true, promptChannel: "stdin" }),
786
785
  aicopilot: adapterFor("aicopilot", { sandbox: [], durable: false, remote: true, promptChannel: "stdin" }),
787
786
  opencode: adapterFor("opencode", { sandbox: [], durable: false, remote: true, promptChannel: "stdin" })
@@ -4217,7 +4216,7 @@ class Store {
4217
4216
  // package.json
4218
4217
  var package_default = {
4219
4218
  name: "@hasna/loops",
4220
- version: "0.4.7",
4219
+ version: "0.4.9",
4221
4220
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4222
4221
  type: "module",
4223
4222
  main: "dist/index.js",
@@ -5004,11 +5003,6 @@ var TRANSPORT_ENV_KEYS = new Set([
5004
5003
  "USER",
5005
5004
  "XDG_RUNTIME_DIR"
5006
5005
  ]);
5007
- function boundedText(text, maxBytes) {
5008
- const buffer = new BoundedOutputBuffer(maxBytes);
5009
- buffer.append(text);
5010
- return buffer.value();
5011
- }
5012
5006
  function buildResult(status, startedAt, fields = {}) {
5013
5007
  const finishedAt = fields.finishedAt ?? nowIso();
5014
5008
  return {
@@ -5072,21 +5066,6 @@ function metadataEnv(metadata) {
5072
5066
  env.LOOPS_GOAL_NODE_KEY = metadata.goalNodeKey;
5073
5067
  return env;
5074
5068
  }
5075
- function codewithAgentIdempotencyKey(metadata) {
5076
- const parts = [
5077
- "openloops",
5078
- metadata.workflowRunId ? `workflow-run:${metadata.workflowRunId}` : undefined,
5079
- metadata.workflowStepId ? `step:${metadata.workflowStepId}` : undefined,
5080
- metadata.runId ? `loop-run:${metadata.runId}` : undefined,
5081
- metadata.loopId ? `loop:${metadata.loopId}` : undefined,
5082
- metadata.scheduledFor ? `scheduled:${metadata.scheduledFor}` : undefined,
5083
- metadata.goalId ? `goal:${metadata.goalId}` : undefined,
5084
- metadata.goalNodeKey ? `node:${metadata.goalNodeKey}` : undefined
5085
- ].filter(Boolean);
5086
- if (parts.length > 1)
5087
- return parts.join(":");
5088
- return `openloops:adhoc:${randomBytes2(16).toString("hex")}`;
5089
- }
5090
5069
  function allowlistEnv(allowlist) {
5091
5070
  const env = {};
5092
5071
  if (allowlist?.tools?.length)
@@ -5141,8 +5120,7 @@ function commandSpec(target, opts) {
5141
5120
  preflightAnyOf: invocation.preflightAnyOf,
5142
5121
  stdin: invocation.stdin,
5143
5122
  allowlist: agentTarget.allowlist,
5144
- worktree: agentTarget.worktree,
5145
- codewithDurableAgent: adapter.capabilities.durable ? { target: agentTarget } : undefined
5123
+ worktree: agentTarget.worktree
5146
5124
  };
5147
5125
  }
5148
5126
  function composeExecutionEnv(spec, metadata, opts, accountEnv) {
@@ -5352,266 +5330,6 @@ async function preflightNativeAuthProfile(spec, env) {
5352
5330
  const result = await spawnCapture(spec.command, ["profile", "list"], { env, timeoutMs: 15000 });
5353
5331
  assertCodewithProfileListed(spec.nativeAuthProfile.profile, result);
5354
5332
  }
5355
- function codewithAgentStartArgs(target, idempotencyKey) {
5356
- const args = providerAdapter(target.provider).buildInvocation(target).args;
5357
- const startIndex = args.findIndex((arg, index) => arg === "start" && args[index - 1] === "agent");
5358
- if (startIndex === -1)
5359
- throw new Error("internal error: codewith durable agent args missing agent start");
5360
- args.splice(startIndex + 1, 0, "--idempotency-key", idempotencyKey);
5361
- return args;
5362
- }
5363
- function codewithAgentControlArgs(target, command, agentId) {
5364
- return [
5365
- ...target.authProfile ? ["--auth-profile", target.authProfile] : [],
5366
- "agent",
5367
- command,
5368
- ...command === "logs" ? ["--limit", "20"] : [],
5369
- "--json",
5370
- agentId
5371
- ];
5372
- }
5373
- function extractFirstJsonObject(text) {
5374
- let depth = 0;
5375
- let start = -1;
5376
- let inString = false;
5377
- let escaped = false;
5378
- for (let i = 0;i < text.length; i += 1) {
5379
- const ch = text[i];
5380
- if (inString) {
5381
- if (escaped)
5382
- escaped = false;
5383
- else if (ch === "\\")
5384
- escaped = true;
5385
- else if (ch === '"')
5386
- inString = false;
5387
- continue;
5388
- }
5389
- if (ch === '"') {
5390
- inString = true;
5391
- } else if (ch === "{") {
5392
- if (depth === 0)
5393
- start = i;
5394
- depth += 1;
5395
- } else if (ch === "}") {
5396
- if (depth > 0) {
5397
- depth -= 1;
5398
- if (depth === 0 && start !== -1)
5399
- return text.slice(start, i + 1);
5400
- }
5401
- }
5402
- }
5403
- return;
5404
- }
5405
- function parseJsonOutput(stdout, label) {
5406
- const raw = stdout || "{}";
5407
- const candidates = [raw.trim()];
5408
- const scanned = extractFirstJsonObject(raw);
5409
- if (scanned && scanned !== raw.trim())
5410
- candidates.push(scanned);
5411
- let lastError;
5412
- for (const candidate of candidates) {
5413
- try {
5414
- const value = JSON.parse(candidate);
5415
- if (!value || typeof value !== "object" || Array.isArray(value))
5416
- throw new Error("not an object");
5417
- return value;
5418
- } catch (error) {
5419
- lastError = error;
5420
- }
5421
- }
5422
- throw new Error(`${label} did not return JSON${lastError instanceof Error ? `: ${lastError.message}` : ""}`);
5423
- }
5424
- function recordField(value, key) {
5425
- if (!value || typeof value !== "object" || Array.isArray(value))
5426
- return;
5427
- const field = value[key];
5428
- return field && typeof field === "object" && !Array.isArray(field) ? field : undefined;
5429
- }
5430
- function stringField(value, key) {
5431
- const field = value?.[key];
5432
- return typeof field === "string" ? field : undefined;
5433
- }
5434
- function numberField(value, key) {
5435
- const field = value?.[key];
5436
- return typeof field === "number" && Number.isFinite(field) ? field : undefined;
5437
- }
5438
- function codewithAgentStatus(readJson) {
5439
- return stringField(recordField(readJson, "agent"), "status") ?? stringField(recordField(readJson, "statusSnapshot"), "status");
5440
- }
5441
- function codewithAgentLastEventSeq(readJson) {
5442
- const agentSeq = numberField(recordField(readJson, "agent"), "lastEventSeq");
5443
- const snapshotSeq = numberField(recordField(readJson, "statusSnapshot"), "lastEventSeq");
5444
- if (agentSeq !== undefined && snapshotSeq !== undefined)
5445
- return Math.max(agentSeq, snapshotSeq);
5446
- return agentSeq ?? snapshotSeq;
5447
- }
5448
- function codewithAgentEvidence(startJson, readJson, logsJson) {
5449
- const agent = recordField(readJson, "agent") ?? recordField(startJson, "agent");
5450
- const statusSnapshot = recordField(readJson, "statusSnapshot");
5451
- const events = Array.isArray(logsJson?.data) ? logsJson.data.filter((event) => Boolean(event) && typeof event === "object" && !Array.isArray(event)).map((event) => ({
5452
- seq: numberField(event, "seq"),
5453
- eventType: stringField(event, "eventType"),
5454
- createdAt: numberField(event, "createdAt")
5455
- })) : undefined;
5456
- return JSON.stringify({
5457
- codewithAgent: {
5458
- agentId: stringField(agent, "agentId"),
5459
- status: stringField(agent, "status"),
5460
- desiredState: stringField(agent, "desiredState"),
5461
- statusReason: stringField(agent, "statusReason"),
5462
- threadId: stringField(agent, "threadId"),
5463
- rolloutPath: stringField(agent, "rolloutPath"),
5464
- pid: numberField(agent, "pid"),
5465
- exitCode: numberField(agent, "exitCode"),
5466
- created: typeof startJson.created === "boolean" ? startJson.created : undefined
5467
- },
5468
- statusSnapshot: statusSnapshot ? {
5469
- seq: numberField(statusSnapshot, "seq"),
5470
- status: stringField(statusSnapshot, "status"),
5471
- summary: stringField(statusSnapshot, "summary"),
5472
- pendingInteractionCount: numberField(statusSnapshot, "pendingInteractionCount"),
5473
- lastEventSeq: numberField(statusSnapshot, "lastEventSeq")
5474
- } : undefined,
5475
- events
5476
- }, null, 2);
5477
- }
5478
- function codewithAgentProgress(readJson) {
5479
- const agent = recordField(readJson, "agent");
5480
- const statusSnapshot = recordField(readJson, "statusSnapshot");
5481
- return {
5482
- provider: "codewith",
5483
- agentId: stringField(agent, "agentId"),
5484
- status: stringField(agent, "status") ?? stringField(statusSnapshot, "status"),
5485
- summary: stringField(statusSnapshot, "summary"),
5486
- statusReason: stringField(agent, "statusReason"),
5487
- threadId: stringField(agent, "threadId"),
5488
- rolloutPath: stringField(agent, "rolloutPath"),
5489
- pid: numberField(agent, "pid"),
5490
- lastEventSeq: codewithAgentLastEventSeq(readJson)
5491
- };
5492
- }
5493
- function sleep(ms) {
5494
- return new Promise((resolve3) => setTimeout(resolve3, ms));
5495
- }
5496
- async function executeCodewithDurableAgent(spec, metadata, opts, env, startedAt) {
5497
- const target = spec.codewithDurableAgent?.target;
5498
- if (!target)
5499
- throw new Error("internal error: missing codewith durable target");
5500
- const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
5501
- const idempotencyKey = codewithAgentIdempotencyKey(metadata);
5502
- const start = await spawnCapture(spec.command, codewithAgentStartArgs(target, idempotencyKey), {
5503
- cwd: spec.cwd,
5504
- env,
5505
- timeoutMs: 30000,
5506
- maxOutputBytes
5507
- });
5508
- const stderr = new BoundedOutputBuffer(maxOutputBytes);
5509
- stderr.append(start.stderr);
5510
- if (start.error || (start.status ?? 1) !== 0) {
5511
- return failureResult(startedAt, start.error ?? `codewith agent start exited with code ${start.status ?? "unknown"}`, {
5512
- exitCode: start.status ?? undefined,
5513
- stdout: start.stdout,
5514
- stderr: stderr.value()
5515
- });
5516
- }
5517
- let startJson;
5518
- try {
5519
- startJson = parseJsonOutput(start.stdout || "{}", "codewith agent start");
5520
- } catch (error) {
5521
- return failureResult(startedAt, error instanceof Error ? error.message : String(error), { stdout: start.stdout, stderr: stderr.value() });
5522
- }
5523
- const agentId = stringField(recordField(startJson, "agent"), "agentId");
5524
- if (!agentId) {
5525
- return failureResult(startedAt, "codewith agent start did not return agent.agentId", { stdout: start.stdout, stderr: stderr.value() });
5526
- }
5527
- opts.onAgentProgress?.(codewithAgentProgress(startJson));
5528
- const stopAgent = async () => {
5529
- await spawnCapture(spec.command, codewithAgentControlArgs(target, "stop", agentId), {
5530
- cwd: spec.cwd,
5531
- env,
5532
- timeoutMs: 15000,
5533
- maxOutputBytes
5534
- });
5535
- };
5536
- const pollMs = Math.max(100, Number(opts.env?.LOOPS_CODEWITH_AGENT_POLL_MS ?? process.env.LOOPS_CODEWITH_AGENT_POLL_MS ?? 2000) || 2000);
5537
- let lastReadJson = startJson;
5538
- let lastLogsJson;
5539
- let lastFingerprint;
5540
- let lastProgressAt = Date.now();
5541
- const evidence = () => boundedText(codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes);
5542
- while (true) {
5543
- if (opts.signal?.aborted) {
5544
- await stopAgent();
5545
- return failureResult(startedAt, "cancelled", { stdout: evidence(), stderr: stderr.value() });
5546
- }
5547
- const read = await spawnCapture(spec.command, codewithAgentControlArgs(target, "read", agentId), {
5548
- cwd: spec.cwd,
5549
- env,
5550
- timeoutMs: 30000,
5551
- maxOutputBytes
5552
- });
5553
- stderr.append(read.stderr);
5554
- if (read.error || (read.status ?? 1) !== 0) {
5555
- return failureResult(startedAt, read.error ?? `codewith agent read exited with code ${read.status ?? "unknown"}`, {
5556
- exitCode: read.status ?? undefined,
5557
- stdout: evidence(),
5558
- stderr: stderr.value()
5559
- });
5560
- }
5561
- try {
5562
- lastReadJson = parseJsonOutput(read.stdout || "{}", "codewith agent read");
5563
- } catch (error) {
5564
- return failureResult(startedAt, error instanceof Error ? error.message : String(error), {
5565
- stdout: boundedText(read.stdout, maxOutputBytes),
5566
- stderr: stderr.value()
5567
- });
5568
- }
5569
- const status = codewithAgentStatus(lastReadJson);
5570
- const fingerprint = JSON.stringify({
5571
- status,
5572
- agentLastEventSeq: numberField(recordField(lastReadJson, "agent"), "lastEventSeq"),
5573
- snapshot: recordField(lastReadJson, "statusSnapshot") ?? null
5574
- });
5575
- if (fingerprint !== lastFingerprint) {
5576
- lastFingerprint = fingerprint;
5577
- lastProgressAt = Date.now();
5578
- opts.onAgentProgress?.(codewithAgentProgress(lastReadJson));
5579
- }
5580
- if (status === "completed" || status === "failed" || status === "cancelled") {
5581
- const logs = await spawnCapture(spec.command, codewithAgentControlArgs(target, "logs", agentId), {
5582
- cwd: spec.cwd,
5583
- env,
5584
- timeoutMs: 30000,
5585
- maxOutputBytes
5586
- });
5587
- if (!logs.error && (logs.status ?? 1) === 0) {
5588
- try {
5589
- lastLogsJson = parseJsonOutput(logs.stdout || "{}", "codewith agent logs");
5590
- } catch {
5591
- lastLogsJson = undefined;
5592
- }
5593
- } else {
5594
- stderr.append(logs.stderr || logs.error || "");
5595
- }
5596
- if (status === "completed") {
5597
- return successResult(startedAt, { exitCode: 0, stdout: evidence(), stderr: stderr.value() });
5598
- }
5599
- return failureResult(startedAt, stringField(recordField(lastReadJson, "agent"), "statusReason") ?? `codewith agent ${status}`, { exitCode: 1, stdout: evidence(), stderr: stderr.value() });
5600
- }
5601
- if (typeof spec.timeoutMs === "number" && new Date(nowIso()).getTime() - new Date(startedAt).getTime() >= spec.timeoutMs) {
5602
- await stopAgent();
5603
- return timeoutResult(startedAt, `timed out after ${spec.timeoutMs}ms`, { stdout: evidence(), stderr: stderr.value() });
5604
- }
5605
- if (typeof spec.idleTimeoutMs === "number" && Date.now() - lastProgressAt >= spec.idleTimeoutMs) {
5606
- await stopAgent();
5607
- return timeoutResult(startedAt, `idle timed out after ${spec.idleTimeoutMs}ms without agent progress`, {
5608
- stdout: evidence(),
5609
- stderr: stderr.value()
5610
- });
5611
- }
5612
- await sleep(pollMs);
5613
- }
5614
- }
5615
5333
  function resolvedDirEquals(left, right) {
5616
5334
  try {
5617
5335
  return realpathSync(left) === realpathSync(right);
@@ -5845,9 +5563,6 @@ function preflightTarget(target, metadata = {}, opts = {}) {
5845
5563
  async function executeTarget(target, metadata = {}, opts = {}) {
5846
5564
  let spec = commandSpec(target, opts);
5847
5565
  const machine = resolvedMachine(opts);
5848
- if (machine && !machine.local && spec.codewithDurableAgent) {
5849
- return failureResult(nowIso(), "remote Codewith durable background-agent steps require remote status polling support; run this Codewith step locally or add remote durable readback before dispatch");
5850
- }
5851
5566
  if (machine && !machine.local) {
5852
5567
  const remoteFallbackSpec = spec.worktree?.enabled && spec.worktree.mode === "auto" ? worktreeFallbackSpec(target, opts, spec.worktree.originalCwd) : undefined;
5853
5568
  return executeRemoteSpec(spec, machine, metadata, opts, remoteFallbackSpec);
@@ -5878,9 +5593,6 @@ async function executeTarget(target, metadata = {}, opts = {}) {
5878
5593
  if (worktreeEntry?.fallbackCwd) {
5879
5594
  spec = worktreeFallbackSpec(target, opts, worktreeEntry.fallbackCwd) ?? spec;
5880
5595
  }
5881
- if (spec.codewithDurableAgent) {
5882
- return executeCodewithDurableAgent(spec, metadata, opts, env, startedAt);
5883
- }
5884
5596
  const child = spawn2(spec.command, spec.args, {
5885
5597
  cwd: spec.cwd,
5886
5598
  env,
package/docs/USAGE.md CHANGED
@@ -6,7 +6,7 @@ It supports deterministic command loops, JSON-defined workflows, and guarded CLI
6
6
 
7
7
  - `claude`
8
8
  - `agent` (Cursor Agent CLI)
9
- - `codewith agent start`
9
+ - `codewith exec`
10
10
  - `aicopilot run`
11
11
  - `opencode run`
12
12
  - `codex exec`
@@ -991,13 +991,13 @@ On Linux this writes a user systemd service. On macOS it writes a LaunchAgent pl
991
991
  The adapters intentionally use provider command surfaces instead of pretending every agent has one SDK:
992
992
 
993
993
  - Claude uses `claude -p --output-format json` and safe-mode/local setting sources by default.
994
- - Codewith uses durable `codewith --ask-for-approval never agent start` background-agent runs by default, then polls `codewith agent read` until the run is terminal. While the step is running, OpenLoops records bounded agent id/status updates as `step_progress` workflow events and as the running step's compact stdout; on terminal completion it records compact status/event evidence. Remote Codewith agent steps fail closed until remote durable readback is implemented, so workflows do not advance immediately after enqueue. OpenLoops rejects Codewith `extraArgs` that try to force `exec`, `--ephemeral`, or other non-durable exec-only flags for task-lifecycle, planner, worker, verifier, reviewer, release, or other long-running agentic work.
994
+ - Codewith runs non-interactive `codewith --ask-for-approval never exec --json` sessions by default. exec starts a fresh session per invocation, avoiding the multi-megabyte rollout history that `codewith agent start` reloaded every turn (which drove `context_length_exceeded` silent no-ops), and it keeps network egress for gh/git the `workspace-write` sandbox opts back into `sandbox_workspace_write.network_access`. Codewith exec is remote-capable like codex. OpenLoops rejects Codewith `extraArgs` that try to force `exec`, `--ephemeral`, `--json`, or other exec launch flags that the adapter already manages.
995
995
  - AI Copilot and OpenCode use `run --format json --pure`. OpenCode requires an explicit provider/model id because ambient OpenCode config is machine-specific.
996
996
  - Cursor is CLI-first for now via the standalone `agent -p` binary. OpenLoops no longer falls back to `cursor agent`; install the standalone Cursor Agent CLI so preflight and scheduled runs use the same executable.
997
997
  - Codex uses `codex --ask-for-approval never exec --json --ephemeral --skip-git-repo-check`, with `--add-dir` for explicit extra writable directories where supported.
998
- - Agent prompts are sent through child stdin instead of argv where the provider supports stdin. Codewith durable background agents currently accept prompts as native `agent start` arguments, so OpenLoops stores only bounded status/event evidence and omits raw Codewith prompt text and raw event payloads from workflow stdout.
998
+ - Agent prompts are sent through child stdin instead of argv where the provider supports stdin, including Codewith `exec` (which reads instructions from stdin when no positional prompt is given), so the prompt never lands on argv.
999
999
  - When `--account` or a step `account` is set, OpenLoops resolves `accounts env <profile> --tool <tool>` before spawning the target, strips inherited tool home/API-key variables, and applies the selected profile only to that process. Missing account profiles fail before the provider binary receives the prompt.
1000
- - `--auth-profile` and step `authProfile` are provider-native auth selectors. They currently apply to Codewith and are passed to Codewith as `--auth-profile <name>` before `agent start/read/logs`; they do not call OpenAccounts.
1000
+ - `--auth-profile` and step `authProfile` are provider-native auth selectors. They currently apply to Codewith and are passed to Codewith as `--auth-profile <name>` on the `exec` invocation; they do not call OpenAccounts.
1001
1001
  - `--sandbox` maps to provider-native sandbox flags. Codewith/Codex accept `read-only`, `workspace-write`, or `danger-full-access`; Cursor accepts `enabled` or `disabled`.
1002
1002
  - `--permission-mode` maps `plan`, `auto`, and `bypass` where the provider supports it. Claude uses native permission modes, Cursor maps bypass to `--force`, and OpenCode/AICopilot map bypass to `--dangerously-skip-permissions`.
1003
1003
  - `--variant` is provider-specific reasoning/model effort. Claude maps it to `--effort`, Codewith/Codex map it to `model_reasoning_effort`, and OpenCode/AICopilot pass `--variant`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/loops",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",