@hasna/loops 0.3.44 → 0.3.46
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 +17 -4
- package/dist/cli/index.js +43 -12
- package/dist/daemon/index.js +15 -4
- package/dist/index.js +24 -4
- package/dist/lib/store.js +4 -0
- package/dist/lib/templates.d.ts +4 -0
- package/dist/sdk/index.js +14 -3
- package/dist/types.d.ts +1 -0
- package/docs/USAGE.md +21 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,7 +224,9 @@ loops templates render todos-task-worker-verifier \
|
|
|
224
224
|
--var projectPath=/path/to/repo \
|
|
225
225
|
--var provider=codewith \
|
|
226
226
|
--var authProfilePool=account004,account005,account006 \
|
|
227
|
-
--var sandbox=workspace-write
|
|
227
|
+
--var sandbox=workspace-write \
|
|
228
|
+
--var todosProjectPath=$HOME/.hasna/loops \
|
|
229
|
+
--var addDirs=$HOME/.hasna/todos,$HOME/.hasna/loops
|
|
228
230
|
loops templates create-workflow todos-task-worker-verifier \
|
|
229
231
|
--var taskId=<task-id> \
|
|
230
232
|
--var projectPath=/path/to/repo
|
|
@@ -260,6 +262,8 @@ cat task-created-event.json | loops events handle todos-task \
|
|
|
260
262
|
--auth-profile-pool account004,account005,account006 \
|
|
261
263
|
--permission-mode bypass \
|
|
262
264
|
--sandbox workspace-write \
|
|
265
|
+
--todos-project "$HOME/.hasna/loops" \
|
|
266
|
+
--add-dir "$HOME/.hasna/todos,$HOME/.hasna/loops" \
|
|
263
267
|
--worktree-mode required
|
|
264
268
|
```
|
|
265
269
|
|
|
@@ -290,6 +294,15 @@ is requested without `manualBreakGlass=true`. Use `workspace-write` for
|
|
|
290
294
|
unattended task/event routes. Full access is an explicit manual emergency path,
|
|
291
295
|
not a default automation mode.
|
|
292
296
|
|
|
297
|
+
When a sandboxed Codewith/Codex worker must update app stores outside the repo
|
|
298
|
+
worktree, pass those stores explicitly with `--add-dir` or template `addDirs`.
|
|
299
|
+
For task-created routes, pass `--todos-project` so worker/verifier prompts use
|
|
300
|
+
the actual todos storage project while repo routing and worktree isolation still
|
|
301
|
+
use the repository path. This avoids `danger-full-access` for normal todos
|
|
302
|
+
comments, completion state, and loop evidence writes. `addDirs` is intentionally
|
|
303
|
+
accepted only for Codewith/Codex until other providers expose equivalent
|
|
304
|
+
directory-scoped write controls.
|
|
305
|
+
|
|
293
306
|
Inspect route state with:
|
|
294
307
|
|
|
295
308
|
```bash
|
|
@@ -484,10 +497,10 @@ On Linux this writes a user systemd service. On macOS it writes a LaunchAgent pl
|
|
|
484
497
|
The adapters intentionally use provider command surfaces instead of pretending every agent has one SDK:
|
|
485
498
|
|
|
486
499
|
- Claude uses `claude -p --output-format json` and safe-mode/local setting sources by default.
|
|
487
|
-
- Codewith uses `codewith --ask-for-approval never exec --json --ephemeral --skip-git-repo-check
|
|
500
|
+
- Codewith uses `codewith --ask-for-approval never exec --json --ephemeral --skip-git-repo-check`, with `--add-dir` for explicit extra writable directories.
|
|
488
501
|
- AI Copilot and OpenCode use `run --format json --pure`.
|
|
489
|
-
- Cursor is CLI-first for now via `
|
|
490
|
-
- Codex uses `codex exec --json --ephemeral --
|
|
502
|
+
- Cursor is CLI-first for now via standalone `agent -p` when available, with `cursor agent -p` as a compatibility fallback; treat output as less stable until a stronger public SDK contract is selected.
|
|
503
|
+
- Codex uses `codex exec --json --ephemeral --skip-git-repo-check`, with `--add-dir` for explicit extra writable directories where supported.
|
|
491
504
|
- Agent prompts are sent through child stdin instead of argv so prompt bodies do not appear in process listings.
|
|
492
505
|
- 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.
|
|
493
506
|
- `--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 `exec`; they do not call OpenAccounts.
|
package/dist/cli/index.js
CHANGED
|
@@ -388,6 +388,10 @@ function validateTarget(value, label) {
|
|
|
388
388
|
}
|
|
389
389
|
if (value.variant !== undefined)
|
|
390
390
|
assertString(value.variant, `${label}.variant`);
|
|
391
|
+
optionalStringArray(value.addDirs, `${label}.addDirs`);
|
|
392
|
+
if (Array.isArray(value.addDirs) && value.addDirs.length > 0 && !["codewith", "codex"].includes(value.provider)) {
|
|
393
|
+
throw new Error(`${label}.addDirs is currently supported only for provider codewith or codex`);
|
|
394
|
+
}
|
|
391
395
|
if (value.permissionMode !== undefined) {
|
|
392
396
|
assertString(value.permissionMode, `${label}.permissionMode`);
|
|
393
397
|
const permissionModes = ["default", "plan", "auto", "bypass"];
|
|
@@ -3153,6 +3157,9 @@ function assertSupportedAgentOptions(target) {
|
|
|
3153
3157
|
if (target.authProfile !== undefined && target.provider !== "codewith") {
|
|
3154
3158
|
throw new Error(`${target.provider}.authProfile is supported only for codewith`);
|
|
3155
3159
|
}
|
|
3160
|
+
if (target.addDirs?.length && !["codewith", "codex"].includes(target.provider)) {
|
|
3161
|
+
throw new Error(`${target.provider}.addDirs is currently supported only for codewith or codex`);
|
|
3162
|
+
}
|
|
3156
3163
|
if (target.permissionMode && !["default", "plan", "auto", "bypass"].includes(target.permissionMode)) {
|
|
3157
3164
|
throw new Error(`${target.provider}.permissionMode must be default, plan, auto, or bypass`);
|
|
3158
3165
|
}
|
|
@@ -3212,10 +3219,10 @@ function agentArgs(target) {
|
|
|
3212
3219
|
case "cursor":
|
|
3213
3220
|
args.push("-c", [
|
|
3214
3221
|
"set -eu",
|
|
3215
|
-
"if command -v
|
|
3216
|
-
' exec cursor agent "$@"',
|
|
3217
|
-
"elif command -v agent >/dev/null 2>&1; then",
|
|
3222
|
+
"if command -v agent >/dev/null 2>&1; then",
|
|
3218
3223
|
' exec agent "$@"',
|
|
3224
|
+
"elif command -v cursor >/dev/null 2>&1; then",
|
|
3225
|
+
' exec cursor agent "$@"',
|
|
3219
3226
|
"else",
|
|
3220
3227
|
" echo 'Executable not found in PATH: cursor agent or agent' >&2",
|
|
3221
3228
|
" exit 127",
|
|
@@ -3247,6 +3254,8 @@ function agentArgs(target) {
|
|
|
3247
3254
|
args.push("--ignore-rules");
|
|
3248
3255
|
if (target.cwd)
|
|
3249
3256
|
args.push("--cd", target.cwd);
|
|
3257
|
+
for (const dir of target.addDirs ?? [])
|
|
3258
|
+
args.push("--add-dir", dir);
|
|
3250
3259
|
if (target.model)
|
|
3251
3260
|
args.push("--model", target.model);
|
|
3252
3261
|
if (target.agent)
|
|
@@ -3261,6 +3270,8 @@ function agentArgs(target) {
|
|
|
3261
3270
|
args.push("--ignore-rules");
|
|
3262
3271
|
if (target.cwd)
|
|
3263
3272
|
args.push("--cd", target.cwd);
|
|
3273
|
+
for (const dir of target.addDirs ?? [])
|
|
3274
|
+
args.push("--add-dir", dir);
|
|
3264
3275
|
if (target.model)
|
|
3265
3276
|
args.push("--model", target.model);
|
|
3266
3277
|
args.push(...target.extraArgs ?? []);
|
|
@@ -5732,7 +5743,7 @@ function buildScriptInventoryReport(store, opts = {}) {
|
|
|
5732
5743
|
// package.json
|
|
5733
5744
|
var package_default = {
|
|
5734
5745
|
name: "@hasna/loops",
|
|
5735
|
-
version: "0.3.
|
|
5746
|
+
version: "0.3.46",
|
|
5736
5747
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
5737
5748
|
type: "module",
|
|
5738
5749
|
main: "dist/index.js",
|
|
@@ -5845,6 +5856,7 @@ var TEMPLATE_SUMMARIES = [
|
|
|
5845
5856
|
{ name: "taskId", required: true, description: "Todos task id to execute." },
|
|
5846
5857
|
{ name: "taskTitle", description: "Human-readable task title." },
|
|
5847
5858
|
{ name: "projectPath", required: true, description: "Repository or project working directory." },
|
|
5859
|
+
{ name: "todosProjectPath", description: "Todos storage project path used in worker/verifier commands." },
|
|
5848
5860
|
{ name: "routeProjectPath", description: "Canonical project path used for scheduler concurrency limits." },
|
|
5849
5861
|
{ name: "projectGroup", description: "Optional project group used for scheduler concurrency limits." },
|
|
5850
5862
|
{ name: "provider", default: "codewith", description: "Agent provider: codewith, claude, cursor, opencode, aicopilot, or codex." },
|
|
@@ -5855,6 +5867,7 @@ var TEMPLATE_SUMMARIES = [
|
|
|
5855
5867
|
{ name: "accountPool", description: "Comma-separated OpenAccounts profiles; worker/verifier are selected deterministically." },
|
|
5856
5868
|
{ name: "model", description: "Provider model." },
|
|
5857
5869
|
{ name: "variant", description: "Provider reasoning/model effort variant." },
|
|
5870
|
+
{ name: "addDirs", description: "Comma-separated additional writable directories for provider sandboxes." },
|
|
5858
5871
|
{ name: "permissionMode", default: "bypass", description: "Provider permission mode: default, plan, auto, or bypass." },
|
|
5859
5872
|
{ name: "sandbox", default: "workspace-write", description: "Provider sandbox mode." },
|
|
5860
5873
|
{ name: "manualBreakGlass", default: "false", description: "Allow explicit danger-full-access in a generated workflow. Intended for manual emergency use only." },
|
|
@@ -5884,6 +5897,7 @@ var TEMPLATE_SUMMARIES = [
|
|
|
5884
5897
|
{ name: "accountPool", description: "Comma-separated OpenAccounts profiles; worker/verifier are selected deterministically." },
|
|
5885
5898
|
{ name: "model", description: "Provider model." },
|
|
5886
5899
|
{ name: "variant", description: "Provider reasoning/model effort variant." },
|
|
5900
|
+
{ name: "addDirs", description: "Comma-separated additional writable directories for provider sandboxes." },
|
|
5887
5901
|
{ name: "permissionMode", default: "bypass", description: "Provider permission mode: default, plan, auto, or bypass." },
|
|
5888
5902
|
{ name: "sandbox", default: "workspace-write", description: "Provider sandbox mode." },
|
|
5889
5903
|
{ name: "manualBreakGlass", default: "false", description: "Allow explicit danger-full-access in a generated workflow. Intended for manual emergency use only." },
|
|
@@ -6264,6 +6278,7 @@ function agentTarget(input, prompt, role, seed, plan) {
|
|
|
6264
6278
|
model: input.model,
|
|
6265
6279
|
variant: input.variant,
|
|
6266
6280
|
agent: input.agent,
|
|
6281
|
+
addDirs: input.addDirs,
|
|
6267
6282
|
authProfile: provider === "codewith" ? authProfileForRole(input, role, seed) : undefined,
|
|
6268
6283
|
configIsolation: "safe",
|
|
6269
6284
|
permissionMode: input.permissionMode ?? "bypass",
|
|
@@ -6307,7 +6322,7 @@ function renderTodosTaskWorkerVerifierWorkflow(input) {
|
|
|
6307
6322
|
throw new Error("taskId is required");
|
|
6308
6323
|
if (!input.projectPath?.trim())
|
|
6309
6324
|
throw new Error("projectPath is required");
|
|
6310
|
-
const todosProjectPath = input.routeProjectPath ?? input.projectPath;
|
|
6325
|
+
const todosProjectPath = input.todosProjectPath ?? input.routeProjectPath ?? input.projectPath;
|
|
6311
6326
|
const plan = worktreePlan(input, input.taskId);
|
|
6312
6327
|
const taskContext = {
|
|
6313
6328
|
taskId: input.taskId,
|
|
@@ -6541,6 +6556,7 @@ function renderLifecycleBoundedTemplate(id, values) {
|
|
|
6541
6556
|
model: values.model,
|
|
6542
6557
|
variant: values.variant,
|
|
6543
6558
|
agent: values.agent,
|
|
6559
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
6544
6560
|
permissionMode: values.permissionMode,
|
|
6545
6561
|
sandbox: values.sandbox ?? (id === REPORT_ONLY_TEMPLATE_ID ? "read-only" : undefined),
|
|
6546
6562
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
|
@@ -6657,6 +6673,7 @@ function renderLoopTemplate(id, values) {
|
|
|
6657
6673
|
taskTitle: values.taskTitle,
|
|
6658
6674
|
taskDescription: values.taskDescription,
|
|
6659
6675
|
projectPath: values.projectPath ?? values.cwd ?? process.cwd(),
|
|
6676
|
+
todosProjectPath: values.todosProjectPath ?? values.todosProject,
|
|
6660
6677
|
routeProjectPath: values.routeProjectPath,
|
|
6661
6678
|
projectGroup: values.projectGroup,
|
|
6662
6679
|
provider: values.provider,
|
|
@@ -6669,6 +6686,7 @@ function renderLoopTemplate(id, values) {
|
|
|
6669
6686
|
model: values.model,
|
|
6670
6687
|
variant: values.variant,
|
|
6671
6688
|
agent: values.agent,
|
|
6689
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
6672
6690
|
permissionMode: values.permissionMode,
|
|
6673
6691
|
sandbox: values.sandbox,
|
|
6674
6692
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
|
@@ -6700,6 +6718,7 @@ function renderLoopTemplate(id, values) {
|
|
|
6700
6718
|
model: values.model,
|
|
6701
6719
|
variant: values.variant,
|
|
6702
6720
|
agent: values.agent,
|
|
6721
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
6703
6722
|
permissionMode: values.permissionMode,
|
|
6704
6723
|
sandbox: values.sandbox,
|
|
6705
6724
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
|
@@ -6726,6 +6745,7 @@ function renderLoopTemplate(id, values) {
|
|
|
6726
6745
|
model: values.model,
|
|
6727
6746
|
variant: values.variant,
|
|
6728
6747
|
agent: values.agent,
|
|
6748
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
6729
6749
|
permissionMode: values.permissionMode,
|
|
6730
6750
|
sandbox: values.sandbox,
|
|
6731
6751
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
|
@@ -6938,6 +6958,10 @@ function allowlistFromOpts(opts) {
|
|
|
6938
6958
|
enforcement: "metadata_only"
|
|
6939
6959
|
};
|
|
6940
6960
|
}
|
|
6961
|
+
function listFromRepeatedOpts(value) {
|
|
6962
|
+
const values = (value ?? []).flatMap((entry) => splitList(entry) ?? []);
|
|
6963
|
+
return values.length ? values : undefined;
|
|
6964
|
+
}
|
|
6941
6965
|
function accountPoolFromOpts(opts) {
|
|
6942
6966
|
return splitList(opts.accountPool)?.map((profile) => ({ profile, tool: opts.accountTool }));
|
|
6943
6967
|
}
|
|
@@ -7541,6 +7565,7 @@ function routeTodosTaskEvent(event, opts) {
|
|
|
7541
7565
|
model: opts.model,
|
|
7542
7566
|
variant: opts.variant,
|
|
7543
7567
|
agent: opts.agent,
|
|
7568
|
+
addDirs: listFromRepeatedOpts(opts.addDir),
|
|
7544
7569
|
permissionMode,
|
|
7545
7570
|
sandbox,
|
|
7546
7571
|
manualBreakGlass: Boolean(opts.manualBreakGlass),
|
|
@@ -7548,7 +7573,8 @@ function routeTodosTaskEvent(event, opts) {
|
|
|
7548
7573
|
worktreeRoot: opts.worktreeRoot,
|
|
7549
7574
|
worktreeBranchPrefix: opts.worktreeBranchPrefix ?? "openloops",
|
|
7550
7575
|
eventId: event.id,
|
|
7551
|
-
eventType: event.type
|
|
7576
|
+
eventType: event.type,
|
|
7577
|
+
todosProjectPath: opts.todosProject
|
|
7552
7578
|
});
|
|
7553
7579
|
workflowBody.name = workflowName;
|
|
7554
7580
|
workflowBody.description = `Task-triggered worker/verifier workflow for ${taskTitle ?? taskId} from ${event.source}/${event.type}; ` + `idempotency=${idempotencyKey}; event=${event.id}; project=${projectPath}; projectGroup=${projectGroup ?? "-"}`;
|
|
@@ -7753,6 +7779,7 @@ function routeGenericEvent(event, opts) {
|
|
|
7753
7779
|
model: opts.model,
|
|
7754
7780
|
variant: opts.variant,
|
|
7755
7781
|
agent: opts.agent,
|
|
7782
|
+
addDirs: listFromRepeatedOpts(opts.addDir),
|
|
7756
7783
|
permissionMode,
|
|
7757
7784
|
sandbox,
|
|
7758
7785
|
manualBreakGlass: Boolean(opts.manualBreakGlass),
|
|
@@ -8115,7 +8142,7 @@ addGoalOptions(addAccountOptions(addMachineOptions(addScheduleOptions(create.com
|
|
|
8115
8142
|
store.close();
|
|
8116
8143
|
}
|
|
8117
8144
|
});
|
|
8118
|
-
addGoalOptions(addAccountOptions(addMachineOptions(addScheduleOptions(create.command("agent <name>").description("create a headless coding-agent loop").requiredOption("--provider <provider>", "claude, cursor, codewith, aicopilot, opencode, or codex").requiredOption("--prompt <prompt>", "agent prompt").option("--cwd <dir>", "working directory").option("--model <model>", "model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--timeout <duration>", "run timeout").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass").option("--sandbox <mode>", "provider sandbox: codewith/codex use read-only/workspace-write/danger-full-access; cursor uses enabled/disabled").option("--allow-tool <name>", "advisory per-session tool allowlist metadata; may be repeated or comma-separated", collectValues, []).option("--allow-command <name>", "advisory per-session command allowlist metadata; may be repeated or comma-separated", collectValues, []).option("--config-isolation <mode>", "safe or none", "safe").option("--preflight-each-run", "check provider/account readiness before every scheduled run").option("--preflight", "check target executables/accounts before storing the loop"))))).action((name, opts) => {
|
|
8145
|
+
addGoalOptions(addAccountOptions(addMachineOptions(addScheduleOptions(create.command("agent <name>").description("create a headless coding-agent loop").requiredOption("--provider <provider>", "claude, cursor, codewith, aicopilot, opencode, or codex").requiredOption("--prompt <prompt>", "agent prompt").option("--cwd <dir>", "working directory").option("--model <model>", "model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--add-dir <dir>", "additional writable directory for provider sandboxes; may be repeated or comma-separated", collectValues, []).option("--timeout <duration>", "run timeout").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass").option("--sandbox <mode>", "provider sandbox: codewith/codex use read-only/workspace-write/danger-full-access; cursor uses enabled/disabled").option("--allow-tool <name>", "advisory per-session tool allowlist metadata; may be repeated or comma-separated", collectValues, []).option("--allow-command <name>", "advisory per-session command allowlist metadata; may be repeated or comma-separated", collectValues, []).option("--config-isolation <mode>", "safe or none", "safe").option("--preflight-each-run", "check provider/account readiness before every scheduled run").option("--preflight", "check target executables/accounts before storing the loop"))))).action((name, opts) => {
|
|
8119
8146
|
const provider = opts.provider;
|
|
8120
8147
|
if (!["claude", "cursor", "codewith", "aicopilot", "opencode", "codex"].includes(provider)) {
|
|
8121
8148
|
throw new Error("unsupported provider");
|
|
@@ -8134,6 +8161,7 @@ addGoalOptions(addAccountOptions(addMachineOptions(addScheduleOptions(create.com
|
|
|
8134
8161
|
variant: opts.variant,
|
|
8135
8162
|
agent: opts.agent,
|
|
8136
8163
|
authProfile: providerAuthProfileFromOpts(opts, provider),
|
|
8164
|
+
addDirs: listFromRepeatedOpts(opts.addDir),
|
|
8137
8165
|
timeoutMs: opts.timeout ? parseDuration(opts.timeout) : undefined,
|
|
8138
8166
|
configIsolation: opts.configIsolation,
|
|
8139
8167
|
permissionMode: permissionModeFromOpts(opts, provider),
|
|
@@ -8174,10 +8202,10 @@ var events = program.command("events").description("handle Hasna event envelopes
|
|
|
8174
8202
|
var machines = program.command("machines").description("inspect OpenMachines topology for loop assignment");
|
|
8175
8203
|
var goal = program.command("goal").description("inspect goal runs");
|
|
8176
8204
|
function addRouteEventOptions(command) {
|
|
8177
|
-
return command.option("--event-file <file>", "read event envelope JSON from a file instead of stdin/HASNA_EVENT_JSON").option("--event-json <json>", "read event envelope JSON from this string instead of stdin/HASNA_EVENT_JSON").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix").option("--preflight", "check generated workflow steps before storing the workflow loop");
|
|
8205
|
+
return command.option("--event-file <file>", "read event envelope JSON from a file instead of stdin/HASNA_EVENT_JSON").option("--event-json <json>", "read event envelope JSON from this string instead of stdin/HASNA_EVENT_JSON").option("--todos-project <path>", "todos storage project path for generated task commands", defaultLoopsProject()).option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--add-dir <dir>", "additional writable directory for provider sandboxes; may be repeated or comma-separated", collectValues, []).option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix").option("--preflight", "check generated workflow steps before storing the workflow loop");
|
|
8178
8206
|
}
|
|
8179
8207
|
function addTodosDrainOptions(command) {
|
|
8180
|
-
return command.option("--todos-project <path>", "todos storage project path", defaultLoopsProject()).option("--todos-project-id <id>", "filter todos ready output to one todos project id").option("--task-list <id-or-slug>", "filter ready tasks to one task-list id, slug, or name").option("--project-path-prefix <path>", "filter ready tasks to a project/repo path prefix").option("--tags <tags>", "require all comma-separated tags before routing").option("--tag <tags>", "alias for --tags").option("--limit <n>", "maximum filtered ready-task candidates to consider", "50").option("--scan-limit <n>", "maximum raw todos ready rows to fetch before filters; defaults to 500 when filters are used").option("--max-dispatch <n>", "maximum new workflow loops to create in this drain run", "1").option("--evidence-dir <path>", "write a JSON drain report to this directory").option("--compact", "print compact JSON to stdout while preserving the full evidence file").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing workflow loops").option("--dry-run", "preview selected tasks and generated workflow loops without storing anything");
|
|
8208
|
+
return command.option("--todos-project <path>", "todos storage project path", defaultLoopsProject()).option("--todos-project-id <id>", "filter todos ready output to one todos project id").option("--task-list <id-or-slug>", "filter ready tasks to one task-list id, slug, or name").option("--project-path-prefix <path>", "filter ready tasks to a project/repo path prefix").option("--tags <tags>", "require all comma-separated tags before routing").option("--tag <tags>", "alias for --tags").option("--limit <n>", "maximum filtered ready-task candidates to consider", "50").option("--scan-limit <n>", "maximum raw todos ready rows to fetch before filters; defaults to 500 when filters are used").option("--max-dispatch <n>", "maximum new workflow loops to create in this drain run", "1").option("--evidence-dir <path>", "write a JSON drain report to this directory").option("--compact", "print compact JSON to stdout while preserving the full evidence file").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--add-dir <dir>", "additional writable directory for provider sandboxes; may be repeated or comma-separated", collectValues, []).option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing workflow loops").option("--dry-run", "preview selected tasks and generated workflow loops without storing anything");
|
|
8181
8209
|
}
|
|
8182
8210
|
function routeEventByKind(kind, event, opts) {
|
|
8183
8211
|
if (kind === "todos-task")
|
|
@@ -8219,6 +8247,8 @@ function routeDrainArgs(opts) {
|
|
|
8219
8247
|
add("--model", opts.model);
|
|
8220
8248
|
add("--variant", opts.variant);
|
|
8221
8249
|
add("--agent", opts.agent);
|
|
8250
|
+
for (const dir of listFromRepeatedOpts(opts.addDir) ?? [])
|
|
8251
|
+
add("--add-dir", dir);
|
|
8222
8252
|
add("--permission-mode", opts.permissionMode);
|
|
8223
8253
|
add("--sandbox", opts.sandbox);
|
|
8224
8254
|
addBool("--manual-break-glass", opts.manualBreakGlass);
|
|
@@ -8437,13 +8467,13 @@ addScheduleOptions(addTodosDrainOptions(routes.command("schedule <kind> <name>")
|
|
|
8437
8467
|
}
|
|
8438
8468
|
});
|
|
8439
8469
|
var eventsHandle = events.command("handle").description("handle a Hasna event envelope");
|
|
8440
|
-
eventsHandle.command("todos-task").description("create a one-shot worker/verifier workflow loop for a todos task event").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing the workflow loop").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
8470
|
+
eventsHandle.command("todos-task").description("create a one-shot worker/verifier workflow loop for a todos task event").option("--todos-project <path>", "todos storage project path for generated task commands", defaultLoopsProject()).option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--add-dir <dir>", "additional writable directory for provider sandboxes; may be repeated or comma-separated", collectValues, []).option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing the workflow loop").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
8441
8471
|
const event = await readEventEnvelopeFromStdin();
|
|
8442
8472
|
const result = routeTodosTaskEvent(event, opts);
|
|
8443
8473
|
print(result.value, result.human);
|
|
8444
8474
|
});
|
|
8445
8475
|
var eventsDrain = events.command("drain").description("drain durable source queues into bounded OpenLoops workflows");
|
|
8446
|
-
eventsDrain.command("todos-task").description("drain ready todos tasks into bounded worker/verifier workflow loops").option("--todos-project <path>", "todos storage project path", defaultLoopsProject()).option("--todos-project-id <id>", "filter todos ready output to one todos project id").option("--task-list <id-or-slug>", "filter ready tasks to one task-list id, slug, or name").option("--project-path-prefix <path>", "filter ready tasks to a project/repo path prefix").option("--tags <tags>", "require all comma-separated tags before routing").option("--tag <tags>", "alias for --tags").option("--limit <n>", "maximum filtered ready-task candidates to consider", "50").option("--scan-limit <n>", "maximum raw todos ready rows to fetch before filters; defaults to 500 when filters are used").option("--max-dispatch <n>", "maximum new workflow loops to create in this drain run", "1").option("--evidence-dir <path>", "write a JSON drain report to this directory").option("--compact", "print compact JSON to stdout while preserving the full evidence file").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing workflow loops").option("--dry-run", "preview selected tasks and generated workflow loops without storing anything").action((opts) => {
|
|
8476
|
+
eventsDrain.command("todos-task").description("drain ready todos tasks into bounded worker/verifier workflow loops").option("--todos-project <path>", "todos storage project path", defaultLoopsProject()).option("--todos-project-id <id>", "filter todos ready output to one todos project id").option("--task-list <id-or-slug>", "filter ready tasks to one task-list id, slug, or name").option("--project-path-prefix <path>", "filter ready tasks to a project/repo path prefix").option("--tags <tags>", "require all comma-separated tags before routing").option("--tag <tags>", "alias for --tags").option("--limit <n>", "maximum filtered ready-task candidates to consider", "50").option("--scan-limit <n>", "maximum raw todos ready rows to fetch before filters; defaults to 500 when filters are used").option("--max-dispatch <n>", "maximum new workflow loops to create in this drain run", "1").option("--evidence-dir <path>", "write a JSON drain report to this directory").option("--compact", "print compact JSON to stdout while preserving the full evidence file").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--add-dir <dir>", "additional writable directory for provider sandboxes; may be repeated or comma-separated", collectValues, []).option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing workflow loops").option("--dry-run", "preview selected tasks and generated workflow loops without storing anything").action((opts) => {
|
|
8447
8477
|
const maxDispatch = positiveInteger(opts.maxDispatch ?? "1", "--max-dispatch") ?? 1;
|
|
8448
8478
|
const todosProject = opts.todosProject ?? defaultLoopsProject();
|
|
8449
8479
|
const requiredTags = splitList(opts.tags ?? opts.tag) ?? [];
|
|
@@ -8527,7 +8557,7 @@ eventsDrain.command("todos-task").description("drain ready todos tasks into boun
|
|
|
8527
8557
|
} : { ...report, evidencePath };
|
|
8528
8558
|
print(output, `drained todos ready queue: considered=${report.considered} created=${report.created} deduped=${report.deduped} throttled=${report.throttled} skipped=${report.skipped}`);
|
|
8529
8559
|
});
|
|
8530
|
-
eventsHandle.command("generic").description("create a one-shot worker/verifier workflow loop for any Hasna event").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated event worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:generic").option("--preflight", "check generated workflow steps before storing the workflow loop").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
8560
|
+
eventsHandle.command("generic").description("create a one-shot worker/verifier workflow loop for any Hasna event").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--add-dir <dir>", "additional writable directory for provider sandboxes; may be repeated or comma-separated", collectValues, []).option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--manual-break-glass", "allow danger-full-access in generated worker/verifier workflow metadata; for explicit operator emergency use only").option("--project-path <path>", "fallback project/repo working directory").option("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated event worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:generic").option("--preflight", "check generated workflow steps before storing the workflow loop").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
8531
8561
|
const event = await readEventEnvelopeFromStdin();
|
|
8532
8562
|
const data = eventData(event);
|
|
8533
8563
|
const metadata = eventMetadata(event);
|
|
@@ -8569,6 +8599,7 @@ eventsHandle.command("generic").description("create a one-shot worker/verifier w
|
|
|
8569
8599
|
model: opts.model,
|
|
8570
8600
|
variant: opts.variant,
|
|
8571
8601
|
agent: opts.agent,
|
|
8602
|
+
addDirs: listFromRepeatedOpts(opts.addDir),
|
|
8572
8603
|
permissionMode,
|
|
8573
8604
|
sandbox,
|
|
8574
8605
|
manualBreakGlass: Boolean(opts.manualBreakGlass),
|
package/dist/daemon/index.js
CHANGED
|
@@ -388,6 +388,10 @@ function validateTarget(value, label) {
|
|
|
388
388
|
}
|
|
389
389
|
if (value.variant !== undefined)
|
|
390
390
|
assertString(value.variant, `${label}.variant`);
|
|
391
|
+
optionalStringArray(value.addDirs, `${label}.addDirs`);
|
|
392
|
+
if (Array.isArray(value.addDirs) && value.addDirs.length > 0 && !["codewith", "codex"].includes(value.provider)) {
|
|
393
|
+
throw new Error(`${label}.addDirs is currently supported only for provider codewith or codex`);
|
|
394
|
+
}
|
|
391
395
|
if (value.permissionMode !== undefined) {
|
|
392
396
|
assertString(value.permissionMode, `${label}.permissionMode`);
|
|
393
397
|
const permissionModes = ["default", "plan", "auto", "bypass"];
|
|
@@ -3037,6 +3041,9 @@ function assertSupportedAgentOptions(target) {
|
|
|
3037
3041
|
if (target.authProfile !== undefined && target.provider !== "codewith") {
|
|
3038
3042
|
throw new Error(`${target.provider}.authProfile is supported only for codewith`);
|
|
3039
3043
|
}
|
|
3044
|
+
if (target.addDirs?.length && !["codewith", "codex"].includes(target.provider)) {
|
|
3045
|
+
throw new Error(`${target.provider}.addDirs is currently supported only for codewith or codex`);
|
|
3046
|
+
}
|
|
3040
3047
|
if (target.permissionMode && !["default", "plan", "auto", "bypass"].includes(target.permissionMode)) {
|
|
3041
3048
|
throw new Error(`${target.provider}.permissionMode must be default, plan, auto, or bypass`);
|
|
3042
3049
|
}
|
|
@@ -3096,10 +3103,10 @@ function agentArgs(target) {
|
|
|
3096
3103
|
case "cursor":
|
|
3097
3104
|
args.push("-c", [
|
|
3098
3105
|
"set -eu",
|
|
3099
|
-
"if command -v
|
|
3100
|
-
' exec cursor agent "$@"',
|
|
3101
|
-
"elif command -v agent >/dev/null 2>&1; then",
|
|
3106
|
+
"if command -v agent >/dev/null 2>&1; then",
|
|
3102
3107
|
' exec agent "$@"',
|
|
3108
|
+
"elif command -v cursor >/dev/null 2>&1; then",
|
|
3109
|
+
' exec cursor agent "$@"',
|
|
3103
3110
|
"else",
|
|
3104
3111
|
" echo 'Executable not found in PATH: cursor agent or agent' >&2",
|
|
3105
3112
|
" exit 127",
|
|
@@ -3131,6 +3138,8 @@ function agentArgs(target) {
|
|
|
3131
3138
|
args.push("--ignore-rules");
|
|
3132
3139
|
if (target.cwd)
|
|
3133
3140
|
args.push("--cd", target.cwd);
|
|
3141
|
+
for (const dir of target.addDirs ?? [])
|
|
3142
|
+
args.push("--add-dir", dir);
|
|
3134
3143
|
if (target.model)
|
|
3135
3144
|
args.push("--model", target.model);
|
|
3136
3145
|
if (target.agent)
|
|
@@ -3145,6 +3154,8 @@ function agentArgs(target) {
|
|
|
3145
3154
|
args.push("--ignore-rules");
|
|
3146
3155
|
if (target.cwd)
|
|
3147
3156
|
args.push("--cd", target.cwd);
|
|
3157
|
+
for (const dir of target.addDirs ?? [])
|
|
3158
|
+
args.push("--add-dir", dir);
|
|
3148
3159
|
if (target.model)
|
|
3149
3160
|
args.push("--model", target.model);
|
|
3150
3161
|
args.push(...target.extraArgs ?? []);
|
|
@@ -5046,7 +5057,7 @@ function enableStartup(result) {
|
|
|
5046
5057
|
// package.json
|
|
5047
5058
|
var package_default = {
|
|
5048
5059
|
name: "@hasna/loops",
|
|
5049
|
-
version: "0.3.
|
|
5060
|
+
version: "0.3.46",
|
|
5050
5061
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
5051
5062
|
type: "module",
|
|
5052
5063
|
main: "dist/index.js",
|
package/dist/index.js
CHANGED
|
@@ -386,6 +386,10 @@ function validateTarget(value, label) {
|
|
|
386
386
|
}
|
|
387
387
|
if (value.variant !== undefined)
|
|
388
388
|
assertString(value.variant, `${label}.variant`);
|
|
389
|
+
optionalStringArray(value.addDirs, `${label}.addDirs`);
|
|
390
|
+
if (Array.isArray(value.addDirs) && value.addDirs.length > 0 && !["codewith", "codex"].includes(value.provider)) {
|
|
391
|
+
throw new Error(`${label}.addDirs is currently supported only for provider codewith or codex`);
|
|
392
|
+
}
|
|
389
393
|
if (value.permissionMode !== undefined) {
|
|
390
394
|
assertString(value.permissionMode, `${label}.permissionMode`);
|
|
391
395
|
const permissionModes = ["default", "plan", "auto", "bypass"];
|
|
@@ -3027,6 +3031,9 @@ function assertSupportedAgentOptions(target) {
|
|
|
3027
3031
|
if (target.authProfile !== undefined && target.provider !== "codewith") {
|
|
3028
3032
|
throw new Error(`${target.provider}.authProfile is supported only for codewith`);
|
|
3029
3033
|
}
|
|
3034
|
+
if (target.addDirs?.length && !["codewith", "codex"].includes(target.provider)) {
|
|
3035
|
+
throw new Error(`${target.provider}.addDirs is currently supported only for codewith or codex`);
|
|
3036
|
+
}
|
|
3030
3037
|
if (target.permissionMode && !["default", "plan", "auto", "bypass"].includes(target.permissionMode)) {
|
|
3031
3038
|
throw new Error(`${target.provider}.permissionMode must be default, plan, auto, or bypass`);
|
|
3032
3039
|
}
|
|
@@ -3086,10 +3093,10 @@ function agentArgs(target) {
|
|
|
3086
3093
|
case "cursor":
|
|
3087
3094
|
args.push("-c", [
|
|
3088
3095
|
"set -eu",
|
|
3089
|
-
"if command -v
|
|
3090
|
-
' exec cursor agent "$@"',
|
|
3091
|
-
"elif command -v agent >/dev/null 2>&1; then",
|
|
3096
|
+
"if command -v agent >/dev/null 2>&1; then",
|
|
3092
3097
|
' exec agent "$@"',
|
|
3098
|
+
"elif command -v cursor >/dev/null 2>&1; then",
|
|
3099
|
+
' exec cursor agent "$@"',
|
|
3093
3100
|
"else",
|
|
3094
3101
|
" echo 'Executable not found in PATH: cursor agent or agent' >&2",
|
|
3095
3102
|
" exit 127",
|
|
@@ -3121,6 +3128,8 @@ function agentArgs(target) {
|
|
|
3121
3128
|
args.push("--ignore-rules");
|
|
3122
3129
|
if (target.cwd)
|
|
3123
3130
|
args.push("--cd", target.cwd);
|
|
3131
|
+
for (const dir of target.addDirs ?? [])
|
|
3132
|
+
args.push("--add-dir", dir);
|
|
3124
3133
|
if (target.model)
|
|
3125
3134
|
args.push("--model", target.model);
|
|
3126
3135
|
if (target.agent)
|
|
@@ -3135,6 +3144,8 @@ function agentArgs(target) {
|
|
|
3135
3144
|
args.push("--ignore-rules");
|
|
3136
3145
|
if (target.cwd)
|
|
3137
3146
|
args.push("--cd", target.cwd);
|
|
3147
|
+
for (const dir of target.addDirs ?? [])
|
|
3148
|
+
args.push("--add-dir", dir);
|
|
3138
3149
|
if (target.model)
|
|
3139
3150
|
args.push("--model", target.model);
|
|
3140
3151
|
args.push(...target.extraArgs ?? []);
|
|
@@ -4806,6 +4817,7 @@ var TEMPLATE_SUMMARIES = [
|
|
|
4806
4817
|
{ name: "taskId", required: true, description: "Todos task id to execute." },
|
|
4807
4818
|
{ name: "taskTitle", description: "Human-readable task title." },
|
|
4808
4819
|
{ name: "projectPath", required: true, description: "Repository or project working directory." },
|
|
4820
|
+
{ name: "todosProjectPath", description: "Todos storage project path used in worker/verifier commands." },
|
|
4809
4821
|
{ name: "routeProjectPath", description: "Canonical project path used for scheduler concurrency limits." },
|
|
4810
4822
|
{ name: "projectGroup", description: "Optional project group used for scheduler concurrency limits." },
|
|
4811
4823
|
{ name: "provider", default: "codewith", description: "Agent provider: codewith, claude, cursor, opencode, aicopilot, or codex." },
|
|
@@ -4816,6 +4828,7 @@ var TEMPLATE_SUMMARIES = [
|
|
|
4816
4828
|
{ name: "accountPool", description: "Comma-separated OpenAccounts profiles; worker/verifier are selected deterministically." },
|
|
4817
4829
|
{ name: "model", description: "Provider model." },
|
|
4818
4830
|
{ name: "variant", description: "Provider reasoning/model effort variant." },
|
|
4831
|
+
{ name: "addDirs", description: "Comma-separated additional writable directories for provider sandboxes." },
|
|
4819
4832
|
{ name: "permissionMode", default: "bypass", description: "Provider permission mode: default, plan, auto, or bypass." },
|
|
4820
4833
|
{ name: "sandbox", default: "workspace-write", description: "Provider sandbox mode." },
|
|
4821
4834
|
{ name: "manualBreakGlass", default: "false", description: "Allow explicit danger-full-access in a generated workflow. Intended for manual emergency use only." },
|
|
@@ -4845,6 +4858,7 @@ var TEMPLATE_SUMMARIES = [
|
|
|
4845
4858
|
{ name: "accountPool", description: "Comma-separated OpenAccounts profiles; worker/verifier are selected deterministically." },
|
|
4846
4859
|
{ name: "model", description: "Provider model." },
|
|
4847
4860
|
{ name: "variant", description: "Provider reasoning/model effort variant." },
|
|
4861
|
+
{ name: "addDirs", description: "Comma-separated additional writable directories for provider sandboxes." },
|
|
4848
4862
|
{ name: "permissionMode", default: "bypass", description: "Provider permission mode: default, plan, auto, or bypass." },
|
|
4849
4863
|
{ name: "sandbox", default: "workspace-write", description: "Provider sandbox mode." },
|
|
4850
4864
|
{ name: "manualBreakGlass", default: "false", description: "Allow explicit danger-full-access in a generated workflow. Intended for manual emergency use only." },
|
|
@@ -5225,6 +5239,7 @@ function agentTarget(input, prompt, role, seed, plan) {
|
|
|
5225
5239
|
model: input.model,
|
|
5226
5240
|
variant: input.variant,
|
|
5227
5241
|
agent: input.agent,
|
|
5242
|
+
addDirs: input.addDirs,
|
|
5228
5243
|
authProfile: provider === "codewith" ? authProfileForRole(input, role, seed) : undefined,
|
|
5229
5244
|
configIsolation: "safe",
|
|
5230
5245
|
permissionMode: input.permissionMode ?? "bypass",
|
|
@@ -5268,7 +5283,7 @@ function renderTodosTaskWorkerVerifierWorkflow(input) {
|
|
|
5268
5283
|
throw new Error("taskId is required");
|
|
5269
5284
|
if (!input.projectPath?.trim())
|
|
5270
5285
|
throw new Error("projectPath is required");
|
|
5271
|
-
const todosProjectPath = input.routeProjectPath ?? input.projectPath;
|
|
5286
|
+
const todosProjectPath = input.todosProjectPath ?? input.routeProjectPath ?? input.projectPath;
|
|
5272
5287
|
const plan = worktreePlan(input, input.taskId);
|
|
5273
5288
|
const taskContext = {
|
|
5274
5289
|
taskId: input.taskId,
|
|
@@ -5502,6 +5517,7 @@ function renderLifecycleBoundedTemplate(id, values) {
|
|
|
5502
5517
|
model: values.model,
|
|
5503
5518
|
variant: values.variant,
|
|
5504
5519
|
agent: values.agent,
|
|
5520
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
5505
5521
|
permissionMode: values.permissionMode,
|
|
5506
5522
|
sandbox: values.sandbox ?? (id === REPORT_ONLY_TEMPLATE_ID ? "read-only" : undefined),
|
|
5507
5523
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
|
@@ -5618,6 +5634,7 @@ function renderLoopTemplate(id, values) {
|
|
|
5618
5634
|
taskTitle: values.taskTitle,
|
|
5619
5635
|
taskDescription: values.taskDescription,
|
|
5620
5636
|
projectPath: values.projectPath ?? values.cwd ?? process.cwd(),
|
|
5637
|
+
todosProjectPath: values.todosProjectPath ?? values.todosProject,
|
|
5621
5638
|
routeProjectPath: values.routeProjectPath,
|
|
5622
5639
|
projectGroup: values.projectGroup,
|
|
5623
5640
|
provider: values.provider,
|
|
@@ -5630,6 +5647,7 @@ function renderLoopTemplate(id, values) {
|
|
|
5630
5647
|
model: values.model,
|
|
5631
5648
|
variant: values.variant,
|
|
5632
5649
|
agent: values.agent,
|
|
5650
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
5633
5651
|
permissionMode: values.permissionMode,
|
|
5634
5652
|
sandbox: values.sandbox,
|
|
5635
5653
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
|
@@ -5661,6 +5679,7 @@ function renderLoopTemplate(id, values) {
|
|
|
5661
5679
|
model: values.model,
|
|
5662
5680
|
variant: values.variant,
|
|
5663
5681
|
agent: values.agent,
|
|
5682
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
5664
5683
|
permissionMode: values.permissionMode,
|
|
5665
5684
|
sandbox: values.sandbox,
|
|
5666
5685
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
|
@@ -5687,6 +5706,7 @@ function renderLoopTemplate(id, values) {
|
|
|
5687
5706
|
model: values.model,
|
|
5688
5707
|
variant: values.variant,
|
|
5689
5708
|
agent: values.agent,
|
|
5709
|
+
addDirs: listVar(values.addDirs ?? values.addDir),
|
|
5690
5710
|
permissionMode: values.permissionMode,
|
|
5691
5711
|
sandbox: values.sandbox,
|
|
5692
5712
|
manualBreakGlass: booleanVar(values.manualBreakGlass),
|
package/dist/lib/store.js
CHANGED
|
@@ -386,6 +386,10 @@ function validateTarget(value, label) {
|
|
|
386
386
|
}
|
|
387
387
|
if (value.variant !== undefined)
|
|
388
388
|
assertString(value.variant, `${label}.variant`);
|
|
389
|
+
optionalStringArray(value.addDirs, `${label}.addDirs`);
|
|
390
|
+
if (Array.isArray(value.addDirs) && value.addDirs.length > 0 && !["codewith", "codex"].includes(value.provider)) {
|
|
391
|
+
throw new Error(`${label}.addDirs is currently supported only for provider codewith or codex`);
|
|
392
|
+
}
|
|
389
393
|
if (value.permissionMode !== undefined) {
|
|
390
394
|
assertString(value.permissionMode, `${label}.permissionMode`);
|
|
391
395
|
const permissionModes = ["default", "plan", "auto", "bypass"];
|
package/dist/lib/templates.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface TodosTaskWorkflowTemplateInput {
|
|
|
14
14
|
taskTitle?: string;
|
|
15
15
|
taskDescription?: string;
|
|
16
16
|
projectPath: string;
|
|
17
|
+
todosProjectPath?: string;
|
|
17
18
|
routeProjectPath?: string;
|
|
18
19
|
projectGroup?: string;
|
|
19
20
|
provider?: AgentProvider;
|
|
@@ -28,6 +29,7 @@ export interface TodosTaskWorkflowTemplateInput {
|
|
|
28
29
|
model?: string;
|
|
29
30
|
variant?: string;
|
|
30
31
|
agent?: string;
|
|
32
|
+
addDirs?: string[];
|
|
31
33
|
permissionMode?: AgentPermissionMode;
|
|
32
34
|
sandbox?: AgentSandbox;
|
|
33
35
|
manualBreakGlass?: boolean;
|
|
@@ -59,6 +61,7 @@ export interface EventWorkflowTemplateInput {
|
|
|
59
61
|
model?: string;
|
|
60
62
|
variant?: string;
|
|
61
63
|
agent?: string;
|
|
64
|
+
addDirs?: string[];
|
|
62
65
|
permissionMode?: AgentPermissionMode;
|
|
63
66
|
sandbox?: AgentSandbox;
|
|
64
67
|
manualBreakGlass?: boolean;
|
|
@@ -85,6 +88,7 @@ export interface BoundedAgentWorkflowTemplateInput {
|
|
|
85
88
|
model?: string;
|
|
86
89
|
variant?: string;
|
|
87
90
|
agent?: string;
|
|
91
|
+
addDirs?: string[];
|
|
88
92
|
permissionMode?: AgentPermissionMode;
|
|
89
93
|
sandbox?: AgentSandbox;
|
|
90
94
|
manualBreakGlass?: boolean;
|
package/dist/sdk/index.js
CHANGED
|
@@ -386,6 +386,10 @@ function validateTarget(value, label) {
|
|
|
386
386
|
}
|
|
387
387
|
if (value.variant !== undefined)
|
|
388
388
|
assertString(value.variant, `${label}.variant`);
|
|
389
|
+
optionalStringArray(value.addDirs, `${label}.addDirs`);
|
|
390
|
+
if (Array.isArray(value.addDirs) && value.addDirs.length > 0 && !["codewith", "codex"].includes(value.provider)) {
|
|
391
|
+
throw new Error(`${label}.addDirs is currently supported only for provider codewith or codex`);
|
|
392
|
+
}
|
|
389
393
|
if (value.permissionMode !== undefined) {
|
|
390
394
|
assertString(value.permissionMode, `${label}.permissionMode`);
|
|
391
395
|
const permissionModes = ["default", "plan", "auto", "bypass"];
|
|
@@ -3027,6 +3031,9 @@ function assertSupportedAgentOptions(target) {
|
|
|
3027
3031
|
if (target.authProfile !== undefined && target.provider !== "codewith") {
|
|
3028
3032
|
throw new Error(`${target.provider}.authProfile is supported only for codewith`);
|
|
3029
3033
|
}
|
|
3034
|
+
if (target.addDirs?.length && !["codewith", "codex"].includes(target.provider)) {
|
|
3035
|
+
throw new Error(`${target.provider}.addDirs is currently supported only for codewith or codex`);
|
|
3036
|
+
}
|
|
3030
3037
|
if (target.permissionMode && !["default", "plan", "auto", "bypass"].includes(target.permissionMode)) {
|
|
3031
3038
|
throw new Error(`${target.provider}.permissionMode must be default, plan, auto, or bypass`);
|
|
3032
3039
|
}
|
|
@@ -3086,10 +3093,10 @@ function agentArgs(target) {
|
|
|
3086
3093
|
case "cursor":
|
|
3087
3094
|
args.push("-c", [
|
|
3088
3095
|
"set -eu",
|
|
3089
|
-
"if command -v
|
|
3090
|
-
' exec cursor agent "$@"',
|
|
3091
|
-
"elif command -v agent >/dev/null 2>&1; then",
|
|
3096
|
+
"if command -v agent >/dev/null 2>&1; then",
|
|
3092
3097
|
' exec agent "$@"',
|
|
3098
|
+
"elif command -v cursor >/dev/null 2>&1; then",
|
|
3099
|
+
' exec cursor agent "$@"',
|
|
3093
3100
|
"else",
|
|
3094
3101
|
" echo 'Executable not found in PATH: cursor agent or agent' >&2",
|
|
3095
3102
|
" exit 127",
|
|
@@ -3121,6 +3128,8 @@ function agentArgs(target) {
|
|
|
3121
3128
|
args.push("--ignore-rules");
|
|
3122
3129
|
if (target.cwd)
|
|
3123
3130
|
args.push("--cd", target.cwd);
|
|
3131
|
+
for (const dir of target.addDirs ?? [])
|
|
3132
|
+
args.push("--add-dir", dir);
|
|
3124
3133
|
if (target.model)
|
|
3125
3134
|
args.push("--model", target.model);
|
|
3126
3135
|
if (target.agent)
|
|
@@ -3135,6 +3144,8 @@ function agentArgs(target) {
|
|
|
3135
3144
|
args.push("--ignore-rules");
|
|
3136
3145
|
if (target.cwd)
|
|
3137
3146
|
args.push("--cd", target.cwd);
|
|
3147
|
+
for (const dir of target.addDirs ?? [])
|
|
3148
|
+
args.push("--add-dir", dir);
|
|
3138
3149
|
if (target.model)
|
|
3139
3150
|
args.push("--model", target.model);
|
|
3140
3151
|
args.push(...target.extraArgs ?? []);
|
package/dist/types.d.ts
CHANGED
package/docs/USAGE.md
CHANGED
|
@@ -228,7 +228,9 @@ loops templates render todos-task-worker-verifier \
|
|
|
228
228
|
--var projectPath=/path/to/repo \
|
|
229
229
|
--var provider=codewith \
|
|
230
230
|
--var authProfilePool=account004,account005,account006 \
|
|
231
|
-
--var sandbox=workspace-write
|
|
231
|
+
--var sandbox=workspace-write \
|
|
232
|
+
--var todosProjectPath=$HOME/.hasna/loops \
|
|
233
|
+
--var addDirs=$HOME/.hasna/todos,$HOME/.hasna/loops
|
|
232
234
|
loops templates create-workflow todos-task-worker-verifier \
|
|
233
235
|
--var taskId=<task-id> \
|
|
234
236
|
--var projectPath=/path/to/repo
|
|
@@ -286,6 +288,8 @@ cat task-created-event.json | loops events handle todos-task \
|
|
|
286
288
|
--auth-profile-pool account004,account005,account006 \
|
|
287
289
|
--permission-mode bypass \
|
|
288
290
|
--sandbox workspace-write \
|
|
291
|
+
--todos-project "$HOME/.hasna/loops" \
|
|
292
|
+
--add-dir "$HOME/.hasna/todos,$HOME/.hasna/loops" \
|
|
289
293
|
--worktree-mode required
|
|
290
294
|
```
|
|
291
295
|
|
|
@@ -322,6 +326,15 @@ idempotency key before rendering worktree plans or checking route limits. In
|
|
|
322
326
|
dry-run mode, throttle counts are not evaluated because opening the live loop
|
|
323
327
|
store can create or migrate the local database.
|
|
324
328
|
|
|
329
|
+
When a sandboxed Codewith/Codex worker must update app stores outside the repo
|
|
330
|
+
worktree, pass those stores explicitly with `--add-dir` or template `addDirs`.
|
|
331
|
+
For task-created routes, pass `--todos-project` so worker/verifier prompts use
|
|
332
|
+
the actual todos storage project while route concurrency and worktree isolation
|
|
333
|
+
still use the repository path. This avoids `danger-full-access` for normal
|
|
334
|
+
todos comments, completion state, and loop evidence writes. `addDirs` is
|
|
335
|
+
intentionally accepted only for Codewith/Codex until other providers expose
|
|
336
|
+
equivalent directory-scoped write controls.
|
|
337
|
+
|
|
325
338
|
Inspect route state with:
|
|
326
339
|
|
|
327
340
|
```bash
|
|
@@ -351,12 +364,13 @@ locks, or non-pending states stay queued in todos and are not routed:
|
|
|
351
364
|
|
|
352
365
|
```bash
|
|
353
366
|
loops events drain todos-task \
|
|
354
|
-
--todos-project
|
|
367
|
+
--todos-project "$HOME/.hasna/loops" \
|
|
355
368
|
--task-list repoops-pr-queue \
|
|
356
369
|
--tags auto:route \
|
|
357
|
-
--project-path-prefix /
|
|
370
|
+
--project-path-prefix "$HOME/workspace/hasna/opensource" \
|
|
358
371
|
--provider codewith \
|
|
359
372
|
--auth-profile-pool account004,account005,account006 \
|
|
373
|
+
--add-dir "$HOME/.hasna/todos,$HOME/.hasna/loops" \
|
|
360
374
|
--project-group oss \
|
|
361
375
|
--max-dispatch 2 \
|
|
362
376
|
--scan-limit 500 \
|
|
@@ -364,7 +378,7 @@ loops events drain todos-task \
|
|
|
364
378
|
--max-active-per-project-group 4 \
|
|
365
379
|
--max-active 12 \
|
|
366
380
|
--worktree-mode required \
|
|
367
|
-
--evidence-dir
|
|
381
|
+
--evidence-dir "$HOME/.hasna/loops/reports/task-drain"
|
|
368
382
|
```
|
|
369
383
|
|
|
370
384
|
`--max-dispatch` caps new workflow-loop creation per drain run. `--limit` caps
|
|
@@ -562,10 +576,10 @@ On Linux this writes a user systemd service. On macOS it writes a LaunchAgent pl
|
|
|
562
576
|
The adapters intentionally use provider command surfaces instead of pretending every agent has one SDK:
|
|
563
577
|
|
|
564
578
|
- Claude uses `claude -p --output-format json` and safe-mode/local setting sources by default.
|
|
565
|
-
- Codewith uses `codewith --ask-for-approval never exec --json --ephemeral --skip-git-repo-check
|
|
579
|
+
- Codewith uses `codewith --ask-for-approval never exec --json --ephemeral --skip-git-repo-check`, with `--add-dir` for explicit extra writable directories.
|
|
566
580
|
- AI Copilot and OpenCode use `run --format json --pure`.
|
|
567
|
-
- Cursor is CLI-first for now via `
|
|
568
|
-
- Codex uses `codex exec --json --ephemeral --
|
|
581
|
+
- Cursor is CLI-first for now via standalone `agent -p` when available, with `cursor agent -p` as a compatibility fallback; treat output as less stable until a stronger public SDK contract is selected.
|
|
582
|
+
- Codex uses `codex exec --json --ephemeral --skip-git-repo-check`, with `--add-dir` for explicit extra writable directories where supported.
|
|
569
583
|
- Agent prompts are sent through child stdin instead of argv so prompt bodies do not appear in process listings.
|
|
570
584
|
- 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.
|
|
571
585
|
- `--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 `exec`; they do not call OpenAccounts.
|
package/package.json
CHANGED