@hasna/loops 0.4.8 → 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/CHANGELOG.md +24 -0
- package/README.md +4 -4
- package/dist/api/index.js +1 -1
- package/dist/cli/index.js +83 -345
- package/dist/daemon/index.js +25 -313
- package/dist/index.js +12 -300
- package/dist/lib/agent-adapter.d.ts +1 -1
- package/dist/lib/mode.js +1 -1
- package/dist/lib/route/pr-review.d.ts +11 -1
- package/dist/lib/storage/index.js +10 -11
- package/dist/lib/storage/sqlite.js +10 -11
- package/dist/lib/store.js +10 -11
- package/dist/mcp/index.js +12 -300
- package/dist/runner/index.js +12 -300
- package/dist/sdk/index.js +12 -300
- package/docs/USAGE.md +4 -4
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -581,7 +581,7 @@ import { isAbsolute, resolve } from "path";
|
|
|
581
581
|
|
|
582
582
|
// src/lib/agent-adapter.ts
|
|
583
583
|
import { spawn } from "child_process";
|
|
584
|
-
var
|
|
584
|
+
var UNSAFE_CODEWITH_EXEC_EXTRA_ARGS = new Set([
|
|
585
585
|
"e",
|
|
586
586
|
"exec",
|
|
587
587
|
"agent",
|
|
@@ -630,12 +630,12 @@ function validateAgentOptions(target, label, capabilities) {
|
|
|
630
630
|
throw new Error(`${label}.agent is not supported for provider codex`);
|
|
631
631
|
}
|
|
632
632
|
if (provider === "codewith" && target.agent !== undefined) {
|
|
633
|
-
throw new Error(`${label}.agent is not supported for provider codewith
|
|
633
|
+
throw new Error(`${label}.agent is not supported for provider codewith`);
|
|
634
634
|
}
|
|
635
635
|
if (provider === "codewith") {
|
|
636
|
-
const unsafe = target.extraArgs?.find((arg) =>
|
|
636
|
+
const unsafe = target.extraArgs?.find((arg) => UNSAFE_CODEWITH_EXEC_EXTRA_ARGS.has(arg));
|
|
637
637
|
if (unsafe) {
|
|
638
|
-
throw new Error(`${label}.extraArgs cannot include ${unsafe}; codewith
|
|
638
|
+
throw new Error(`${label}.extraArgs cannot include ${unsafe}; codewith exec launch flags are managed by the adapter`);
|
|
639
639
|
}
|
|
640
640
|
}
|
|
641
641
|
if (target.addDirs?.length && !["codewith", "codex"].includes(provider)) {
|
|
@@ -718,7 +718,10 @@ function buildAgentInvocation(target) {
|
|
|
718
718
|
args.push(...target.authProfile ? ["--auth-profile", target.authProfile] : []);
|
|
719
719
|
if (target.variant)
|
|
720
720
|
args.push("-c", `model_reasoning_effort=${configStringValue(target.variant)}`);
|
|
721
|
-
|
|
721
|
+
const sandbox = codewithLikeSandbox(target);
|
|
722
|
+
if (sandbox === "workspace-write")
|
|
723
|
+
args.push("-c", "sandbox_workspace_write.network_access=true");
|
|
724
|
+
args.push("--ask-for-approval", "never", "exec", "--json", "--ephemeral", "--sandbox", sandbox, "--skip-git-repo-check");
|
|
722
725
|
if (target.cwd)
|
|
723
726
|
args.push("--cd", target.cwd);
|
|
724
727
|
for (const dir of target.addDirs ?? [])
|
|
@@ -726,11 +729,7 @@ function buildAgentInvocation(target) {
|
|
|
726
729
|
if (target.model)
|
|
727
730
|
args.push("--model", target.model);
|
|
728
731
|
args.push(...target.extraArgs ?? []);
|
|
729
|
-
|
|
730
|
-
if (target.cwd)
|
|
731
|
-
args.push("--cwd", target.cwd);
|
|
732
|
-
args.push(target.prompt);
|
|
733
|
-
return { command: "codewith", args };
|
|
732
|
+
return { command: "codewith", args, stdin: target.prompt };
|
|
734
733
|
}
|
|
735
734
|
case "codex": {
|
|
736
735
|
if (target.variant)
|
|
@@ -783,7 +782,7 @@ function adapterFor(provider, capabilities) {
|
|
|
783
782
|
var PROVIDER_ADAPTERS = {
|
|
784
783
|
claude: adapterFor("claude", { sandbox: [], durable: false, remote: true, promptChannel: "stdin" }),
|
|
785
784
|
cursor: adapterFor("cursor", { sandbox: CURSOR_SANDBOXES, durable: false, remote: true, promptChannel: "stdin" }),
|
|
786
|
-
codewith: adapterFor("codewith", { sandbox: CODEX_LIKE_SANDBOXES, durable:
|
|
785
|
+
codewith: adapterFor("codewith", { sandbox: CODEX_LIKE_SANDBOXES, durable: false, remote: true, promptChannel: "stdin" }),
|
|
787
786
|
codex: adapterFor("codex", { sandbox: CODEX_LIKE_SANDBOXES, durable: false, remote: true, promptChannel: "stdin" }),
|
|
788
787
|
aicopilot: adapterFor("aicopilot", { sandbox: [], durable: false, remote: true, promptChannel: "stdin" }),
|
|
789
788
|
opencode: adapterFor("opencode", { sandbox: [], durable: false, remote: true, promptChannel: "stdin" })
|
|
@@ -4744,11 +4743,6 @@ var TRANSPORT_ENV_KEYS = new Set([
|
|
|
4744
4743
|
"USER",
|
|
4745
4744
|
"XDG_RUNTIME_DIR"
|
|
4746
4745
|
]);
|
|
4747
|
-
function boundedText(text, maxBytes) {
|
|
4748
|
-
const buffer = new BoundedOutputBuffer(maxBytes);
|
|
4749
|
-
buffer.append(text);
|
|
4750
|
-
return buffer.value();
|
|
4751
|
-
}
|
|
4752
4746
|
function buildResult(status, startedAt, fields = {}) {
|
|
4753
4747
|
const finishedAt = fields.finishedAt ?? nowIso();
|
|
4754
4748
|
return {
|
|
@@ -4812,21 +4806,6 @@ function metadataEnv(metadata) {
|
|
|
4812
4806
|
env.LOOPS_GOAL_NODE_KEY = metadata.goalNodeKey;
|
|
4813
4807
|
return env;
|
|
4814
4808
|
}
|
|
4815
|
-
function codewithAgentIdempotencyKey(metadata) {
|
|
4816
|
-
const parts = [
|
|
4817
|
-
"openloops",
|
|
4818
|
-
metadata.workflowRunId ? `workflow-run:${metadata.workflowRunId}` : undefined,
|
|
4819
|
-
metadata.workflowStepId ? `step:${metadata.workflowStepId}` : undefined,
|
|
4820
|
-
metadata.runId ? `loop-run:${metadata.runId}` : undefined,
|
|
4821
|
-
metadata.loopId ? `loop:${metadata.loopId}` : undefined,
|
|
4822
|
-
metadata.scheduledFor ? `scheduled:${metadata.scheduledFor}` : undefined,
|
|
4823
|
-
metadata.goalId ? `goal:${metadata.goalId}` : undefined,
|
|
4824
|
-
metadata.goalNodeKey ? `node:${metadata.goalNodeKey}` : undefined
|
|
4825
|
-
].filter(Boolean);
|
|
4826
|
-
if (parts.length > 1)
|
|
4827
|
-
return parts.join(":");
|
|
4828
|
-
return `openloops:adhoc:${randomBytes2(16).toString("hex")}`;
|
|
4829
|
-
}
|
|
4830
4809
|
function allowlistEnv(allowlist) {
|
|
4831
4810
|
const env = {};
|
|
4832
4811
|
if (allowlist?.tools?.length)
|
|
@@ -4881,8 +4860,7 @@ function commandSpec(target, opts) {
|
|
|
4881
4860
|
preflightAnyOf: invocation.preflightAnyOf,
|
|
4882
4861
|
stdin: invocation.stdin,
|
|
4883
4862
|
allowlist: agentTarget.allowlist,
|
|
4884
|
-
worktree: agentTarget.worktree
|
|
4885
|
-
codewithDurableAgent: adapter.capabilities.durable ? { target: agentTarget } : undefined
|
|
4863
|
+
worktree: agentTarget.worktree
|
|
4886
4864
|
};
|
|
4887
4865
|
}
|
|
4888
4866
|
function composeExecutionEnv(spec, metadata, opts, accountEnv) {
|
|
@@ -5092,266 +5070,6 @@ async function preflightNativeAuthProfile(spec, env) {
|
|
|
5092
5070
|
const result = await spawnCapture(spec.command, ["profile", "list"], { env, timeoutMs: 15000 });
|
|
5093
5071
|
assertCodewithProfileListed(spec.nativeAuthProfile.profile, result);
|
|
5094
5072
|
}
|
|
5095
|
-
function codewithAgentStartArgs(target, idempotencyKey) {
|
|
5096
|
-
const args = providerAdapter(target.provider).buildInvocation(target).args;
|
|
5097
|
-
const startIndex = args.findIndex((arg, index) => arg === "start" && args[index - 1] === "agent");
|
|
5098
|
-
if (startIndex === -1)
|
|
5099
|
-
throw new Error("internal error: codewith durable agent args missing agent start");
|
|
5100
|
-
args.splice(startIndex + 1, 0, "--idempotency-key", idempotencyKey);
|
|
5101
|
-
return args;
|
|
5102
|
-
}
|
|
5103
|
-
function codewithAgentControlArgs(target, command, agentId) {
|
|
5104
|
-
return [
|
|
5105
|
-
...target.authProfile ? ["--auth-profile", target.authProfile] : [],
|
|
5106
|
-
"agent",
|
|
5107
|
-
command,
|
|
5108
|
-
...command === "logs" ? ["--limit", "20"] : [],
|
|
5109
|
-
"--json",
|
|
5110
|
-
agentId
|
|
5111
|
-
];
|
|
5112
|
-
}
|
|
5113
|
-
function extractFirstJsonObject(text) {
|
|
5114
|
-
let depth = 0;
|
|
5115
|
-
let start = -1;
|
|
5116
|
-
let inString = false;
|
|
5117
|
-
let escaped = false;
|
|
5118
|
-
for (let i = 0;i < text.length; i += 1) {
|
|
5119
|
-
const ch = text[i];
|
|
5120
|
-
if (inString) {
|
|
5121
|
-
if (escaped)
|
|
5122
|
-
escaped = false;
|
|
5123
|
-
else if (ch === "\\")
|
|
5124
|
-
escaped = true;
|
|
5125
|
-
else if (ch === '"')
|
|
5126
|
-
inString = false;
|
|
5127
|
-
continue;
|
|
5128
|
-
}
|
|
5129
|
-
if (ch === '"') {
|
|
5130
|
-
inString = true;
|
|
5131
|
-
} else if (ch === "{") {
|
|
5132
|
-
if (depth === 0)
|
|
5133
|
-
start = i;
|
|
5134
|
-
depth += 1;
|
|
5135
|
-
} else if (ch === "}") {
|
|
5136
|
-
if (depth > 0) {
|
|
5137
|
-
depth -= 1;
|
|
5138
|
-
if (depth === 0 && start !== -1)
|
|
5139
|
-
return text.slice(start, i + 1);
|
|
5140
|
-
}
|
|
5141
|
-
}
|
|
5142
|
-
}
|
|
5143
|
-
return;
|
|
5144
|
-
}
|
|
5145
|
-
function parseJsonOutput(stdout, label) {
|
|
5146
|
-
const raw = stdout || "{}";
|
|
5147
|
-
const candidates = [raw.trim()];
|
|
5148
|
-
const scanned = extractFirstJsonObject(raw);
|
|
5149
|
-
if (scanned && scanned !== raw.trim())
|
|
5150
|
-
candidates.push(scanned);
|
|
5151
|
-
let lastError;
|
|
5152
|
-
for (const candidate of candidates) {
|
|
5153
|
-
try {
|
|
5154
|
-
const value = JSON.parse(candidate);
|
|
5155
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
5156
|
-
throw new Error("not an object");
|
|
5157
|
-
return value;
|
|
5158
|
-
} catch (error) {
|
|
5159
|
-
lastError = error;
|
|
5160
|
-
}
|
|
5161
|
-
}
|
|
5162
|
-
throw new Error(`${label} did not return JSON${lastError instanceof Error ? `: ${lastError.message}` : ""}`);
|
|
5163
|
-
}
|
|
5164
|
-
function recordField(value, key) {
|
|
5165
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
5166
|
-
return;
|
|
5167
|
-
const field = value[key];
|
|
5168
|
-
return field && typeof field === "object" && !Array.isArray(field) ? field : undefined;
|
|
5169
|
-
}
|
|
5170
|
-
function stringField(value, key) {
|
|
5171
|
-
const field = value?.[key];
|
|
5172
|
-
return typeof field === "string" ? field : undefined;
|
|
5173
|
-
}
|
|
5174
|
-
function numberField(value, key) {
|
|
5175
|
-
const field = value?.[key];
|
|
5176
|
-
return typeof field === "number" && Number.isFinite(field) ? field : undefined;
|
|
5177
|
-
}
|
|
5178
|
-
function codewithAgentStatus(readJson) {
|
|
5179
|
-
return stringField(recordField(readJson, "agent"), "status") ?? stringField(recordField(readJson, "statusSnapshot"), "status");
|
|
5180
|
-
}
|
|
5181
|
-
function codewithAgentLastEventSeq(readJson) {
|
|
5182
|
-
const agentSeq = numberField(recordField(readJson, "agent"), "lastEventSeq");
|
|
5183
|
-
const snapshotSeq = numberField(recordField(readJson, "statusSnapshot"), "lastEventSeq");
|
|
5184
|
-
if (agentSeq !== undefined && snapshotSeq !== undefined)
|
|
5185
|
-
return Math.max(agentSeq, snapshotSeq);
|
|
5186
|
-
return agentSeq ?? snapshotSeq;
|
|
5187
|
-
}
|
|
5188
|
-
function codewithAgentEvidence(startJson, readJson, logsJson) {
|
|
5189
|
-
const agent = recordField(readJson, "agent") ?? recordField(startJson, "agent");
|
|
5190
|
-
const statusSnapshot = recordField(readJson, "statusSnapshot");
|
|
5191
|
-
const events = Array.isArray(logsJson?.data) ? logsJson.data.filter((event) => Boolean(event) && typeof event === "object" && !Array.isArray(event)).map((event) => ({
|
|
5192
|
-
seq: numberField(event, "seq"),
|
|
5193
|
-
eventType: stringField(event, "eventType"),
|
|
5194
|
-
createdAt: numberField(event, "createdAt")
|
|
5195
|
-
})) : undefined;
|
|
5196
|
-
return JSON.stringify({
|
|
5197
|
-
codewithAgent: {
|
|
5198
|
-
agentId: stringField(agent, "agentId"),
|
|
5199
|
-
status: stringField(agent, "status"),
|
|
5200
|
-
desiredState: stringField(agent, "desiredState"),
|
|
5201
|
-
statusReason: stringField(agent, "statusReason"),
|
|
5202
|
-
threadId: stringField(agent, "threadId"),
|
|
5203
|
-
rolloutPath: stringField(agent, "rolloutPath"),
|
|
5204
|
-
pid: numberField(agent, "pid"),
|
|
5205
|
-
exitCode: numberField(agent, "exitCode"),
|
|
5206
|
-
created: typeof startJson.created === "boolean" ? startJson.created : undefined
|
|
5207
|
-
},
|
|
5208
|
-
statusSnapshot: statusSnapshot ? {
|
|
5209
|
-
seq: numberField(statusSnapshot, "seq"),
|
|
5210
|
-
status: stringField(statusSnapshot, "status"),
|
|
5211
|
-
summary: stringField(statusSnapshot, "summary"),
|
|
5212
|
-
pendingInteractionCount: numberField(statusSnapshot, "pendingInteractionCount"),
|
|
5213
|
-
lastEventSeq: numberField(statusSnapshot, "lastEventSeq")
|
|
5214
|
-
} : undefined,
|
|
5215
|
-
events
|
|
5216
|
-
}, null, 2);
|
|
5217
|
-
}
|
|
5218
|
-
function codewithAgentProgress(readJson) {
|
|
5219
|
-
const agent = recordField(readJson, "agent");
|
|
5220
|
-
const statusSnapshot = recordField(readJson, "statusSnapshot");
|
|
5221
|
-
return {
|
|
5222
|
-
provider: "codewith",
|
|
5223
|
-
agentId: stringField(agent, "agentId"),
|
|
5224
|
-
status: stringField(agent, "status") ?? stringField(statusSnapshot, "status"),
|
|
5225
|
-
summary: stringField(statusSnapshot, "summary"),
|
|
5226
|
-
statusReason: stringField(agent, "statusReason"),
|
|
5227
|
-
threadId: stringField(agent, "threadId"),
|
|
5228
|
-
rolloutPath: stringField(agent, "rolloutPath"),
|
|
5229
|
-
pid: numberField(agent, "pid"),
|
|
5230
|
-
lastEventSeq: codewithAgentLastEventSeq(readJson)
|
|
5231
|
-
};
|
|
5232
|
-
}
|
|
5233
|
-
function sleep(ms) {
|
|
5234
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5235
|
-
}
|
|
5236
|
-
async function executeCodewithDurableAgent(spec, metadata, opts, env, startedAt) {
|
|
5237
|
-
const target = spec.codewithDurableAgent?.target;
|
|
5238
|
-
if (!target)
|
|
5239
|
-
throw new Error("internal error: missing codewith durable target");
|
|
5240
|
-
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
5241
|
-
const idempotencyKey = codewithAgentIdempotencyKey(metadata);
|
|
5242
|
-
const start = await spawnCapture(spec.command, codewithAgentStartArgs(target, idempotencyKey), {
|
|
5243
|
-
cwd: spec.cwd,
|
|
5244
|
-
env,
|
|
5245
|
-
timeoutMs: 30000,
|
|
5246
|
-
maxOutputBytes
|
|
5247
|
-
});
|
|
5248
|
-
const stderr = new BoundedOutputBuffer(maxOutputBytes);
|
|
5249
|
-
stderr.append(start.stderr);
|
|
5250
|
-
if (start.error || (start.status ?? 1) !== 0) {
|
|
5251
|
-
return failureResult(startedAt, start.error ?? `codewith agent start exited with code ${start.status ?? "unknown"}`, {
|
|
5252
|
-
exitCode: start.status ?? undefined,
|
|
5253
|
-
stdout: start.stdout,
|
|
5254
|
-
stderr: stderr.value()
|
|
5255
|
-
});
|
|
5256
|
-
}
|
|
5257
|
-
let startJson;
|
|
5258
|
-
try {
|
|
5259
|
-
startJson = parseJsonOutput(start.stdout || "{}", "codewith agent start");
|
|
5260
|
-
} catch (error) {
|
|
5261
|
-
return failureResult(startedAt, error instanceof Error ? error.message : String(error), { stdout: start.stdout, stderr: stderr.value() });
|
|
5262
|
-
}
|
|
5263
|
-
const agentId = stringField(recordField(startJson, "agent"), "agentId");
|
|
5264
|
-
if (!agentId) {
|
|
5265
|
-
return failureResult(startedAt, "codewith agent start did not return agent.agentId", { stdout: start.stdout, stderr: stderr.value() });
|
|
5266
|
-
}
|
|
5267
|
-
opts.onAgentProgress?.(codewithAgentProgress(startJson));
|
|
5268
|
-
const stopAgent = async () => {
|
|
5269
|
-
await spawnCapture(spec.command, codewithAgentControlArgs(target, "stop", agentId), {
|
|
5270
|
-
cwd: spec.cwd,
|
|
5271
|
-
env,
|
|
5272
|
-
timeoutMs: 15000,
|
|
5273
|
-
maxOutputBytes
|
|
5274
|
-
});
|
|
5275
|
-
};
|
|
5276
|
-
const pollMs = Math.max(100, Number(opts.env?.LOOPS_CODEWITH_AGENT_POLL_MS ?? process.env.LOOPS_CODEWITH_AGENT_POLL_MS ?? 2000) || 2000);
|
|
5277
|
-
let lastReadJson = startJson;
|
|
5278
|
-
let lastLogsJson;
|
|
5279
|
-
let lastFingerprint;
|
|
5280
|
-
let lastProgressAt = Date.now();
|
|
5281
|
-
const evidence = () => boundedText(codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes);
|
|
5282
|
-
while (true) {
|
|
5283
|
-
if (opts.signal?.aborted) {
|
|
5284
|
-
await stopAgent();
|
|
5285
|
-
return failureResult(startedAt, "cancelled", { stdout: evidence(), stderr: stderr.value() });
|
|
5286
|
-
}
|
|
5287
|
-
const read = await spawnCapture(spec.command, codewithAgentControlArgs(target, "read", agentId), {
|
|
5288
|
-
cwd: spec.cwd,
|
|
5289
|
-
env,
|
|
5290
|
-
timeoutMs: 30000,
|
|
5291
|
-
maxOutputBytes
|
|
5292
|
-
});
|
|
5293
|
-
stderr.append(read.stderr);
|
|
5294
|
-
if (read.error || (read.status ?? 1) !== 0) {
|
|
5295
|
-
return failureResult(startedAt, read.error ?? `codewith agent read exited with code ${read.status ?? "unknown"}`, {
|
|
5296
|
-
exitCode: read.status ?? undefined,
|
|
5297
|
-
stdout: evidence(),
|
|
5298
|
-
stderr: stderr.value()
|
|
5299
|
-
});
|
|
5300
|
-
}
|
|
5301
|
-
try {
|
|
5302
|
-
lastReadJson = parseJsonOutput(read.stdout || "{}", "codewith agent read");
|
|
5303
|
-
} catch (error) {
|
|
5304
|
-
return failureResult(startedAt, error instanceof Error ? error.message : String(error), {
|
|
5305
|
-
stdout: boundedText(read.stdout, maxOutputBytes),
|
|
5306
|
-
stderr: stderr.value()
|
|
5307
|
-
});
|
|
5308
|
-
}
|
|
5309
|
-
const status = codewithAgentStatus(lastReadJson);
|
|
5310
|
-
const fingerprint = JSON.stringify({
|
|
5311
|
-
status,
|
|
5312
|
-
agentLastEventSeq: numberField(recordField(lastReadJson, "agent"), "lastEventSeq"),
|
|
5313
|
-
snapshot: recordField(lastReadJson, "statusSnapshot") ?? null
|
|
5314
|
-
});
|
|
5315
|
-
if (fingerprint !== lastFingerprint) {
|
|
5316
|
-
lastFingerprint = fingerprint;
|
|
5317
|
-
lastProgressAt = Date.now();
|
|
5318
|
-
opts.onAgentProgress?.(codewithAgentProgress(lastReadJson));
|
|
5319
|
-
}
|
|
5320
|
-
if (status === "completed" || status === "failed" || status === "cancelled") {
|
|
5321
|
-
const logs = await spawnCapture(spec.command, codewithAgentControlArgs(target, "logs", agentId), {
|
|
5322
|
-
cwd: spec.cwd,
|
|
5323
|
-
env,
|
|
5324
|
-
timeoutMs: 30000,
|
|
5325
|
-
maxOutputBytes
|
|
5326
|
-
});
|
|
5327
|
-
if (!logs.error && (logs.status ?? 1) === 0) {
|
|
5328
|
-
try {
|
|
5329
|
-
lastLogsJson = parseJsonOutput(logs.stdout || "{}", "codewith agent logs");
|
|
5330
|
-
} catch {
|
|
5331
|
-
lastLogsJson = undefined;
|
|
5332
|
-
}
|
|
5333
|
-
} else {
|
|
5334
|
-
stderr.append(logs.stderr || logs.error || "");
|
|
5335
|
-
}
|
|
5336
|
-
if (status === "completed") {
|
|
5337
|
-
return successResult(startedAt, { exitCode: 0, stdout: evidence(), stderr: stderr.value() });
|
|
5338
|
-
}
|
|
5339
|
-
return failureResult(startedAt, stringField(recordField(lastReadJson, "agent"), "statusReason") ?? `codewith agent ${status}`, { exitCode: 1, stdout: evidence(), stderr: stderr.value() });
|
|
5340
|
-
}
|
|
5341
|
-
if (typeof spec.timeoutMs === "number" && new Date(nowIso()).getTime() - new Date(startedAt).getTime() >= spec.timeoutMs) {
|
|
5342
|
-
await stopAgent();
|
|
5343
|
-
return timeoutResult(startedAt, `timed out after ${spec.timeoutMs}ms`, { stdout: evidence(), stderr: stderr.value() });
|
|
5344
|
-
}
|
|
5345
|
-
if (typeof spec.idleTimeoutMs === "number" && Date.now() - lastProgressAt >= spec.idleTimeoutMs) {
|
|
5346
|
-
await stopAgent();
|
|
5347
|
-
return timeoutResult(startedAt, `idle timed out after ${spec.idleTimeoutMs}ms without agent progress`, {
|
|
5348
|
-
stdout: evidence(),
|
|
5349
|
-
stderr: stderr.value()
|
|
5350
|
-
});
|
|
5351
|
-
}
|
|
5352
|
-
await sleep(pollMs);
|
|
5353
|
-
}
|
|
5354
|
-
}
|
|
5355
5073
|
function resolvedDirEquals(left, right) {
|
|
5356
5074
|
try {
|
|
5357
5075
|
return realpathSync(left) === realpathSync(right);
|
|
@@ -5585,9 +5303,6 @@ function preflightTarget(target, metadata = {}, opts = {}) {
|
|
|
5585
5303
|
async function executeTarget(target, metadata = {}, opts = {}) {
|
|
5586
5304
|
let spec = commandSpec(target, opts);
|
|
5587
5305
|
const machine = resolvedMachine(opts);
|
|
5588
|
-
if (machine && !machine.local && spec.codewithDurableAgent) {
|
|
5589
|
-
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");
|
|
5590
|
-
}
|
|
5591
5306
|
if (machine && !machine.local) {
|
|
5592
5307
|
const remoteFallbackSpec = spec.worktree?.enabled && spec.worktree.mode === "auto" ? worktreeFallbackSpec(target, opts, spec.worktree.originalCwd) : undefined;
|
|
5593
5308
|
return executeRemoteSpec(spec, machine, metadata, opts, remoteFallbackSpec);
|
|
@@ -5618,9 +5333,6 @@ async function executeTarget(target, metadata = {}, opts = {}) {
|
|
|
5618
5333
|
if (worktreeEntry?.fallbackCwd) {
|
|
5619
5334
|
spec = worktreeFallbackSpec(target, opts, worktreeEntry.fallbackCwd) ?? spec;
|
|
5620
5335
|
}
|
|
5621
|
-
if (spec.codewithDurableAgent) {
|
|
5622
|
-
return executeCodewithDurableAgent(spec, metadata, opts, env, startedAt);
|
|
5623
|
-
}
|
|
5624
5336
|
const child = spawn2(spec.command, spec.args, {
|
|
5625
5337
|
cwd: spec.cwd,
|
|
5626
5338
|
env,
|
|
@@ -7719,7 +7431,7 @@ async function tick(deps) {
|
|
|
7719
7431
|
// package.json
|
|
7720
7432
|
var package_default = {
|
|
7721
7433
|
name: "@hasna/loops",
|
|
7722
|
-
version: "0.4.
|
|
7434
|
+
version: "0.4.9",
|
|
7723
7435
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
7724
7436
|
type: "module",
|
|
7725
7437
|
main: "dist/index.js",
|