@hasna/loops 0.3.59 → 0.3.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli/index.js +351 -37
- package/dist/daemon/index.js +330 -16
- package/dist/index.js +323 -9
- package/dist/lib/store.js +26 -1
- package/dist/mcp/index.js +323 -9
- package/dist/sdk/index.js +322 -8
- package/docs/USAGE.md +4 -4
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -358,6 +358,20 @@ function optionalStringArray(value, label) {
|
|
|
358
358
|
}).filter(Boolean);
|
|
359
359
|
return values.length ? values : undefined;
|
|
360
360
|
}
|
|
361
|
+
var UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS = new Set([
|
|
362
|
+
"e",
|
|
363
|
+
"exec",
|
|
364
|
+
"agent",
|
|
365
|
+
"start",
|
|
366
|
+
"--ephemeral",
|
|
367
|
+
"--ignore-rules",
|
|
368
|
+
"--skip-git-repo-check",
|
|
369
|
+
"--json",
|
|
370
|
+
"--output-last-message",
|
|
371
|
+
"-o",
|
|
372
|
+
"--output-schema",
|
|
373
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
374
|
+
]);
|
|
361
375
|
function optionalAccountRef(value, label) {
|
|
362
376
|
if (value === undefined)
|
|
363
377
|
return;
|
|
@@ -472,6 +486,15 @@ function validateTarget(value, label, opts) {
|
|
|
472
486
|
throw new Error(`${label}.variant is not supported for provider cursor`);
|
|
473
487
|
if (value.provider === "codex" && value.agent !== undefined)
|
|
474
488
|
throw new Error(`${label}.agent is not supported for provider codex`);
|
|
489
|
+
if (value.provider === "codewith" && value.agent !== undefined) {
|
|
490
|
+
throw new Error(`${label}.agent is not supported for provider codewith durable background-agent execution`);
|
|
491
|
+
}
|
|
492
|
+
const extraArgs = optionalStringArray(value.extraArgs, `${label}.extraArgs`);
|
|
493
|
+
if (value.provider === "codewith") {
|
|
494
|
+
const unsafe = extraArgs?.find((arg) => UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS.has(arg));
|
|
495
|
+
if (unsafe)
|
|
496
|
+
throw new Error(`${label}.extraArgs cannot include ${unsafe}; codewith agent steps use durable agent start, not exec/ephemeral flags`);
|
|
497
|
+
}
|
|
475
498
|
optionalStringArray(value.addDirs, `${label}.addDirs`);
|
|
476
499
|
if (Array.isArray(value.addDirs) && value.addDirs.length > 0 && !["codewith", "codex"].includes(value.provider)) {
|
|
477
500
|
throw new Error(`${label}.addDirs is currently supported only for provider codewith or codex`);
|
|
@@ -547,7 +570,9 @@ function validateTarget(value, label, opts) {
|
|
|
547
570
|
if (value.routing.eventSource !== undefined)
|
|
548
571
|
assertString(value.routing.eventSource, `${label}.routing.eventSource`);
|
|
549
572
|
}
|
|
550
|
-
const target = { ...value };
|
|
573
|
+
const target = { ...value, extraArgs };
|
|
574
|
+
if (!extraArgs)
|
|
575
|
+
delete target.extraArgs;
|
|
551
576
|
delete target.promptFile;
|
|
552
577
|
delete target.promptSource;
|
|
553
578
|
return { ...target, ...promptFields };
|
|
@@ -3615,6 +3640,9 @@ function appendBounded(current, chunk, maxBytes) {
|
|
|
3615
3640
|
return `[truncated ${overflow} bytes]
|
|
3616
3641
|
${next.slice(-maxBytes)}`;
|
|
3617
3642
|
}
|
|
3643
|
+
function appendBoundedText(current, chunk, maxBytes) {
|
|
3644
|
+
return appendBounded(current, Buffer.from(chunk, "utf8"), maxBytes);
|
|
3645
|
+
}
|
|
3618
3646
|
function killProcessGroup(pid) {
|
|
3619
3647
|
try {
|
|
3620
3648
|
process.kill(-pid, "SIGTERM");
|
|
@@ -3662,6 +3690,21 @@ function metadataEnv(metadata) {
|
|
|
3662
3690
|
env.LOOPS_GOAL_NODE_KEY = metadata.goalNodeKey;
|
|
3663
3691
|
return env;
|
|
3664
3692
|
}
|
|
3693
|
+
function codewithAgentIdempotencyKey(metadata) {
|
|
3694
|
+
const parts = [
|
|
3695
|
+
"openloops",
|
|
3696
|
+
metadata.workflowRunId ? `workflow-run:${metadata.workflowRunId}` : undefined,
|
|
3697
|
+
metadata.workflowStepId ? `step:${metadata.workflowStepId}` : undefined,
|
|
3698
|
+
metadata.runId ? `loop-run:${metadata.runId}` : undefined,
|
|
3699
|
+
metadata.loopId ? `loop:${metadata.loopId}` : undefined,
|
|
3700
|
+
metadata.scheduledFor ? `scheduled:${metadata.scheduledFor}` : undefined,
|
|
3701
|
+
metadata.goalId ? `goal:${metadata.goalId}` : undefined,
|
|
3702
|
+
metadata.goalNodeKey ? `node:${metadata.goalNodeKey}` : undefined
|
|
3703
|
+
].filter(Boolean);
|
|
3704
|
+
if (parts.length > 1)
|
|
3705
|
+
return parts.join(":");
|
|
3706
|
+
return `openloops:adhoc:${randomBytes2(16).toString("hex")}`;
|
|
3707
|
+
}
|
|
3665
3708
|
function allowlistEnv(allowlist) {
|
|
3666
3709
|
const env = {};
|
|
3667
3710
|
if (allowlist?.tools?.length)
|
|
@@ -3698,6 +3741,20 @@ function codewithLikeSandbox(target) {
|
|
|
3698
3741
|
function configStringValue(value) {
|
|
3699
3742
|
return JSON.stringify(value);
|
|
3700
3743
|
}
|
|
3744
|
+
var UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS2 = new Set([
|
|
3745
|
+
"e",
|
|
3746
|
+
"exec",
|
|
3747
|
+
"agent",
|
|
3748
|
+
"start",
|
|
3749
|
+
"--ephemeral",
|
|
3750
|
+
"--ignore-rules",
|
|
3751
|
+
"--skip-git-repo-check",
|
|
3752
|
+
"--json",
|
|
3753
|
+
"--output-last-message",
|
|
3754
|
+
"-o",
|
|
3755
|
+
"--output-schema",
|
|
3756
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
3757
|
+
]);
|
|
3701
3758
|
function assertStringOption(value, label) {
|
|
3702
3759
|
if (value !== undefined && typeof value !== "string")
|
|
3703
3760
|
throw new Error(`${label} must be a string`);
|
|
@@ -3728,6 +3785,14 @@ function assertSupportedAgentOptions(target) {
|
|
|
3728
3785
|
}
|
|
3729
3786
|
if (target.provider === "codex" && target.agent !== undefined)
|
|
3730
3787
|
throw new Error("codex.agent is not supported");
|
|
3788
|
+
if (target.provider === "codewith" && target.agent !== undefined) {
|
|
3789
|
+
throw new Error("codewith.agent is not supported by the durable background-agent adapter");
|
|
3790
|
+
}
|
|
3791
|
+
if (target.provider === "codewith") {
|
|
3792
|
+
const unsafe = target.extraArgs?.find((arg) => UNSAFE_CODEWITH_DURABLE_EXTRA_ARGS2.has(arg));
|
|
3793
|
+
if (unsafe)
|
|
3794
|
+
throw new Error(`codewith.extraArgs cannot include ${unsafe}; durable agent steps use codewith agent start, not exec/ephemeral flags`);
|
|
3795
|
+
}
|
|
3731
3796
|
if (["codewith", "codex"].includes(target.provider)) {
|
|
3732
3797
|
if (target.permissionMode && !["default", "bypass"].includes(target.permissionMode)) {
|
|
3733
3798
|
throw new Error(`${target.provider}.permissionMode supports only default or bypass`);
|
|
@@ -3811,18 +3876,18 @@ function agentArgs(target) {
|
|
|
3811
3876
|
args.push(...target.authProfile ? ["--auth-profile", target.authProfile] : []);
|
|
3812
3877
|
if (target.variant)
|
|
3813
3878
|
args.push("-c", `model_reasoning_effort=${configStringValue(target.variant)}`);
|
|
3814
|
-
args.push("--ask-for-approval", "never", "
|
|
3815
|
-
if (isolation === "safe")
|
|
3816
|
-
args.push("--ignore-rules");
|
|
3879
|
+
args.push("--ask-for-approval", "never", "--sandbox", codewithLikeSandbox(target));
|
|
3817
3880
|
if (target.cwd)
|
|
3818
3881
|
args.push("--cd", target.cwd);
|
|
3819
3882
|
for (const dir of target.addDirs ?? [])
|
|
3820
3883
|
args.push("--add-dir", dir);
|
|
3821
3884
|
if (target.model)
|
|
3822
3885
|
args.push("--model", target.model);
|
|
3823
|
-
if (target.agent)
|
|
3824
|
-
args.push("--agent", target.agent);
|
|
3825
3886
|
args.push(...target.extraArgs ?? []);
|
|
3887
|
+
args.push("agent", "start");
|
|
3888
|
+
if (target.cwd)
|
|
3889
|
+
args.push("--cwd", target.cwd);
|
|
3890
|
+
args.push(target.prompt);
|
|
3826
3891
|
return args;
|
|
3827
3892
|
case "codex":
|
|
3828
3893
|
if (target.variant)
|
|
@@ -3898,8 +3963,9 @@ function commandSpec(target) {
|
|
|
3898
3963
|
accountTool: agentTarget.account?.tool ?? accountToolForProvider(agentTarget.provider),
|
|
3899
3964
|
nativeAuthProfile: agentTarget.authProfile ? { provider: agentTarget.provider, profile: agentTarget.authProfile } : undefined,
|
|
3900
3965
|
preflightAnyOf: agentTarget.provider === "cursor" ? ["agent"] : undefined,
|
|
3901
|
-
stdin: agentTarget.prompt,
|
|
3902
|
-
allowlist: agentTarget.allowlist
|
|
3966
|
+
stdin: agentTarget.provider === "codewith" ? undefined : agentTarget.prompt,
|
|
3967
|
+
allowlist: agentTarget.allowlist,
|
|
3968
|
+
codewithDurableAgent: agentTarget.provider === "codewith" ? { target: agentTarget } : undefined
|
|
3903
3969
|
};
|
|
3904
3970
|
}
|
|
3905
3971
|
function executionEnv(spec, metadata, opts) {
|
|
@@ -4019,6 +4085,238 @@ function preflightNativeAuthProfile(spec, env) {
|
|
|
4019
4085
|
throw new Error(`codewith auth profile not found: ${spec.nativeAuthProfile.profile}`);
|
|
4020
4086
|
}
|
|
4021
4087
|
}
|
|
4088
|
+
function codewithAgentStartArgs(target, idempotencyKey) {
|
|
4089
|
+
const args = agentArgs(target);
|
|
4090
|
+
const startIndex = args.findIndex((arg, index) => arg === "start" && args[index - 1] === "agent");
|
|
4091
|
+
if (startIndex === -1)
|
|
4092
|
+
throw new Error("internal error: codewith durable agent args missing agent start");
|
|
4093
|
+
args.splice(startIndex + 1, 0, "--idempotency-key", idempotencyKey);
|
|
4094
|
+
return args;
|
|
4095
|
+
}
|
|
4096
|
+
function codewithAgentControlArgs(target, command, agentId) {
|
|
4097
|
+
return [
|
|
4098
|
+
...target.authProfile ? ["--auth-profile", target.authProfile] : [],
|
|
4099
|
+
"agent",
|
|
4100
|
+
command,
|
|
4101
|
+
...command === "logs" ? ["--limit", "20"] : [],
|
|
4102
|
+
agentId
|
|
4103
|
+
];
|
|
4104
|
+
}
|
|
4105
|
+
function parseJsonOutput(stdout, label) {
|
|
4106
|
+
try {
|
|
4107
|
+
const value = JSON.parse(stdout || "{}");
|
|
4108
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
4109
|
+
throw new Error("not an object");
|
|
4110
|
+
return value;
|
|
4111
|
+
} catch (error) {
|
|
4112
|
+
throw new Error(`${label} did not return JSON${error instanceof Error ? `: ${error.message}` : ""}`);
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
function recordField(value, key) {
|
|
4116
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
4117
|
+
return;
|
|
4118
|
+
const field = value[key];
|
|
4119
|
+
return field && typeof field === "object" && !Array.isArray(field) ? field : undefined;
|
|
4120
|
+
}
|
|
4121
|
+
function stringField(value, key) {
|
|
4122
|
+
const field = value?.[key];
|
|
4123
|
+
return typeof field === "string" ? field : undefined;
|
|
4124
|
+
}
|
|
4125
|
+
function numberField(value, key) {
|
|
4126
|
+
const field = value?.[key];
|
|
4127
|
+
return typeof field === "number" && Number.isFinite(field) ? field : undefined;
|
|
4128
|
+
}
|
|
4129
|
+
function codewithAgentStatus(readJson) {
|
|
4130
|
+
return stringField(recordField(readJson, "agent"), "status") ?? stringField(recordField(readJson, "statusSnapshot"), "status");
|
|
4131
|
+
}
|
|
4132
|
+
function codewithAgentEvidence(startJson, readJson, logsJson) {
|
|
4133
|
+
const agent = recordField(readJson, "agent") ?? recordField(startJson, "agent");
|
|
4134
|
+
const statusSnapshot = recordField(readJson, "statusSnapshot");
|
|
4135
|
+
const events = Array.isArray(logsJson?.data) ? logsJson.data.filter((event) => Boolean(event) && typeof event === "object" && !Array.isArray(event)).map((event) => ({
|
|
4136
|
+
seq: numberField(event, "seq"),
|
|
4137
|
+
eventType: stringField(event, "eventType"),
|
|
4138
|
+
createdAt: numberField(event, "createdAt")
|
|
4139
|
+
})) : undefined;
|
|
4140
|
+
return JSON.stringify({
|
|
4141
|
+
codewithAgent: {
|
|
4142
|
+
agentId: stringField(agent, "agentId"),
|
|
4143
|
+
status: stringField(agent, "status"),
|
|
4144
|
+
desiredState: stringField(agent, "desiredState"),
|
|
4145
|
+
statusReason: stringField(agent, "statusReason"),
|
|
4146
|
+
threadId: stringField(agent, "threadId"),
|
|
4147
|
+
rolloutPath: stringField(agent, "rolloutPath"),
|
|
4148
|
+
pid: numberField(agent, "pid"),
|
|
4149
|
+
exitCode: numberField(agent, "exitCode"),
|
|
4150
|
+
created: typeof startJson.created === "boolean" ? startJson.created : undefined
|
|
4151
|
+
},
|
|
4152
|
+
statusSnapshot: statusSnapshot ? {
|
|
4153
|
+
seq: numberField(statusSnapshot, "seq"),
|
|
4154
|
+
status: stringField(statusSnapshot, "status"),
|
|
4155
|
+
summary: stringField(statusSnapshot, "summary"),
|
|
4156
|
+
pendingInteractionCount: numberField(statusSnapshot, "pendingInteractionCount"),
|
|
4157
|
+
lastEventSeq: numberField(statusSnapshot, "lastEventSeq")
|
|
4158
|
+
} : undefined,
|
|
4159
|
+
events
|
|
4160
|
+
}, null, 2);
|
|
4161
|
+
}
|
|
4162
|
+
function sleep(ms) {
|
|
4163
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
4164
|
+
}
|
|
4165
|
+
async function executeCodewithDurableAgent(spec, metadata, opts, env, startedAt) {
|
|
4166
|
+
const target = spec.codewithDurableAgent?.target;
|
|
4167
|
+
if (!target)
|
|
4168
|
+
throw new Error("internal error: missing codewith durable target");
|
|
4169
|
+
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
4170
|
+
const idempotencyKey = codewithAgentIdempotencyKey(metadata);
|
|
4171
|
+
const start = spawnSync2(spec.command, codewithAgentStartArgs(target, idempotencyKey), {
|
|
4172
|
+
cwd: spec.cwd,
|
|
4173
|
+
env,
|
|
4174
|
+
encoding: "utf8",
|
|
4175
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4176
|
+
timeout: 30000
|
|
4177
|
+
});
|
|
4178
|
+
let stderr = start.stderr || "";
|
|
4179
|
+
if (start.error || (start.status ?? 1) !== 0) {
|
|
4180
|
+
const finishedAt = nowIso();
|
|
4181
|
+
return {
|
|
4182
|
+
status: "failed",
|
|
4183
|
+
exitCode: start.status ?? undefined,
|
|
4184
|
+
stdout: start.stdout || "",
|
|
4185
|
+
stderr,
|
|
4186
|
+
error: start.error?.message ?? `codewith agent start exited with code ${start.status ?? "unknown"}`,
|
|
4187
|
+
startedAt,
|
|
4188
|
+
finishedAt,
|
|
4189
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
let startJson;
|
|
4193
|
+
try {
|
|
4194
|
+
startJson = parseJsonOutput(start.stdout || "{}", "codewith agent start");
|
|
4195
|
+
} catch (error) {
|
|
4196
|
+
const finishedAt = nowIso();
|
|
4197
|
+
return {
|
|
4198
|
+
status: "failed",
|
|
4199
|
+
stdout: start.stdout || "",
|
|
4200
|
+
stderr,
|
|
4201
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4202
|
+
startedAt,
|
|
4203
|
+
finishedAt,
|
|
4204
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4205
|
+
};
|
|
4206
|
+
}
|
|
4207
|
+
const agentId = stringField(recordField(startJson, "agent"), "agentId");
|
|
4208
|
+
if (!agentId) {
|
|
4209
|
+
const finishedAt = nowIso();
|
|
4210
|
+
return {
|
|
4211
|
+
status: "failed",
|
|
4212
|
+
stdout: start.stdout || "",
|
|
4213
|
+
stderr,
|
|
4214
|
+
error: "codewith agent start did not return agent.agentId",
|
|
4215
|
+
startedAt,
|
|
4216
|
+
finishedAt,
|
|
4217
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
const pollMs = Math.max(100, Number(opts.env?.LOOPS_CODEWITH_AGENT_POLL_MS ?? process.env.LOOPS_CODEWITH_AGENT_POLL_MS ?? 2000) || 2000);
|
|
4221
|
+
let lastReadJson = startJson;
|
|
4222
|
+
let lastLogsJson;
|
|
4223
|
+
let stdout = "";
|
|
4224
|
+
while (true) {
|
|
4225
|
+
if (opts.signal?.aborted) {
|
|
4226
|
+
spawnSync2(spec.command, codewithAgentControlArgs(target, "stop", agentId), { cwd: spec.cwd, env, stdio: "ignore", timeout: 15000 });
|
|
4227
|
+
const finishedAt = nowIso();
|
|
4228
|
+
return {
|
|
4229
|
+
status: "failed",
|
|
4230
|
+
stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
|
|
4231
|
+
stderr,
|
|
4232
|
+
error: "cancelled",
|
|
4233
|
+
startedAt,
|
|
4234
|
+
finishedAt,
|
|
4235
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4236
|
+
};
|
|
4237
|
+
}
|
|
4238
|
+
const read = spawnSync2(spec.command, codewithAgentControlArgs(target, "read", agentId), {
|
|
4239
|
+
cwd: spec.cwd,
|
|
4240
|
+
env,
|
|
4241
|
+
encoding: "utf8",
|
|
4242
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4243
|
+
timeout: 30000
|
|
4244
|
+
});
|
|
4245
|
+
stderr = appendBoundedText(stderr, read.stderr || "", maxOutputBytes);
|
|
4246
|
+
if (read.error || (read.status ?? 1) !== 0) {
|
|
4247
|
+
const finishedAt = nowIso();
|
|
4248
|
+
return {
|
|
4249
|
+
status: "failed",
|
|
4250
|
+
exitCode: read.status ?? undefined,
|
|
4251
|
+
stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
|
|
4252
|
+
stderr,
|
|
4253
|
+
error: read.error?.message ?? `codewith agent read exited with code ${read.status ?? "unknown"}`,
|
|
4254
|
+
startedAt,
|
|
4255
|
+
finishedAt,
|
|
4256
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4257
|
+
};
|
|
4258
|
+
}
|
|
4259
|
+
try {
|
|
4260
|
+
lastReadJson = parseJsonOutput(read.stdout || "{}", "codewith agent read");
|
|
4261
|
+
} catch (error) {
|
|
4262
|
+
const finishedAt = nowIso();
|
|
4263
|
+
return {
|
|
4264
|
+
status: "failed",
|
|
4265
|
+
stdout: appendBoundedText(stdout, read.stdout || "", maxOutputBytes),
|
|
4266
|
+
stderr,
|
|
4267
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4268
|
+
startedAt,
|
|
4269
|
+
finishedAt,
|
|
4270
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4271
|
+
};
|
|
4272
|
+
}
|
|
4273
|
+
const status = codewithAgentStatus(lastReadJson);
|
|
4274
|
+
if (status === "completed" || status === "failed" || status === "cancelled") {
|
|
4275
|
+
const logs = spawnSync2(spec.command, codewithAgentControlArgs(target, "logs", agentId), {
|
|
4276
|
+
cwd: spec.cwd,
|
|
4277
|
+
env,
|
|
4278
|
+
encoding: "utf8",
|
|
4279
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4280
|
+
timeout: 30000
|
|
4281
|
+
});
|
|
4282
|
+
if (!logs.error && (logs.status ?? 1) === 0) {
|
|
4283
|
+
try {
|
|
4284
|
+
lastLogsJson = parseJsonOutput(logs.stdout || "{}", "codewith agent logs");
|
|
4285
|
+
} catch {
|
|
4286
|
+
lastLogsJson = undefined;
|
|
4287
|
+
}
|
|
4288
|
+
} else {
|
|
4289
|
+
stderr = appendBoundedText(stderr, logs.stderr || logs.error?.message || "", maxOutputBytes);
|
|
4290
|
+
}
|
|
4291
|
+
const finishedAt = nowIso();
|
|
4292
|
+
const resultStatus = status === "completed" ? "succeeded" : "failed";
|
|
4293
|
+
return {
|
|
4294
|
+
status: resultStatus,
|
|
4295
|
+
exitCode: resultStatus === "succeeded" ? 0 : 1,
|
|
4296
|
+
stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
|
|
4297
|
+
stderr,
|
|
4298
|
+
error: resultStatus === "succeeded" ? undefined : stringField(recordField(lastReadJson, "agent"), "statusReason") ?? `codewith agent ${status}`,
|
|
4299
|
+
startedAt,
|
|
4300
|
+
finishedAt,
|
|
4301
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4302
|
+
};
|
|
4303
|
+
}
|
|
4304
|
+
if (typeof spec.timeoutMs === "number" && new Date(nowIso()).getTime() - new Date(startedAt).getTime() >= spec.timeoutMs) {
|
|
4305
|
+
spawnSync2(spec.command, codewithAgentControlArgs(target, "stop", agentId), { cwd: spec.cwd, env, stdio: "ignore", timeout: 15000 });
|
|
4306
|
+
const finishedAt = nowIso();
|
|
4307
|
+
return {
|
|
4308
|
+
status: "timed_out",
|
|
4309
|
+
stdout: appendBoundedText(stdout, codewithAgentEvidence(startJson, lastReadJson, lastLogsJson), maxOutputBytes),
|
|
4310
|
+
stderr,
|
|
4311
|
+
error: `timed out after ${spec.timeoutMs}ms`,
|
|
4312
|
+
startedAt,
|
|
4313
|
+
finishedAt,
|
|
4314
|
+
durationMs: new Date(finishedAt).getTime() - new Date(startedAt).getTime()
|
|
4315
|
+
};
|
|
4316
|
+
}
|
|
4317
|
+
await sleep(pollMs);
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4022
4320
|
function preflightRemoteSpec(spec, machine, metadata, opts) {
|
|
4023
4321
|
const plan = (opts.machineCommandResolver ?? resolveMachineCommand)(machine.id, "bash -s");
|
|
4024
4322
|
const result = spawnSync2(plan.command, plan.args, {
|
|
@@ -4190,6 +4488,19 @@ function preflightTarget(target, metadata = {}, opts = {}) {
|
|
|
4190
4488
|
async function executeTarget(target, metadata = {}, opts = {}) {
|
|
4191
4489
|
const spec = commandSpec(target);
|
|
4192
4490
|
const machine = resolvedMachine(opts);
|
|
4491
|
+
if (machine && !machine.local && spec.codewithDurableAgent) {
|
|
4492
|
+
const startedAt2 = nowIso();
|
|
4493
|
+
const finishedAt2 = nowIso();
|
|
4494
|
+
return {
|
|
4495
|
+
status: "failed",
|
|
4496
|
+
stdout: "",
|
|
4497
|
+
stderr: "",
|
|
4498
|
+
error: "remote Codewith durable background-agent steps require remote status polling support; run this Codewith step locally or add remote durable readback before dispatch",
|
|
4499
|
+
startedAt: startedAt2,
|
|
4500
|
+
finishedAt: finishedAt2,
|
|
4501
|
+
durationMs: new Date(finishedAt2).getTime() - new Date(startedAt2).getTime()
|
|
4502
|
+
};
|
|
4503
|
+
}
|
|
4193
4504
|
if (machine && !machine.local)
|
|
4194
4505
|
return executeRemoteSpec(spec, machine, metadata, opts);
|
|
4195
4506
|
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
@@ -4236,6 +4547,9 @@ async function executeTarget(target, metadata = {}, opts = {}) {
|
|
|
4236
4547
|
durationMs: 0
|
|
4237
4548
|
};
|
|
4238
4549
|
}
|
|
4550
|
+
if (spec.codewithDurableAgent) {
|
|
4551
|
+
return executeCodewithDurableAgent(spec, metadata, opts, env, startedAt);
|
|
4552
|
+
}
|
|
4239
4553
|
const child = spawn(spec.command, spec.args, {
|
|
4240
4554
|
cwd: spec.cwd,
|
|
4241
4555
|
env,
|
|
@@ -4587,7 +4901,7 @@ function publicGoalRun(run) {
|
|
|
4587
4901
|
// package.json
|
|
4588
4902
|
var package_default = {
|
|
4589
4903
|
name: "@hasna/loops",
|
|
4590
|
-
version: "0.3.
|
|
4904
|
+
version: "0.3.60",
|
|
4591
4905
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4592
4906
|
type: "module",
|
|
4593
4907
|
main: "dist/index.js",
|