@themoltnet/agent-daemon 0.15.0 → 0.17.0
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 +5 -0
- package/dist/main.js +888 -136
- package/package.json +9 -7
package/dist/main.js
CHANGED
|
@@ -9,10 +9,10 @@ import { PinoInstrumentation } from "@opentelemetry/instrumentation-pino";
|
|
|
9
9
|
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
|
|
10
10
|
import crypto$1, { createHash } from "crypto";
|
|
11
11
|
import { createHash as createHash$1 } from "node:crypto";
|
|
12
|
-
import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
12
|
+
import path, { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
13
13
|
import { parseArgs, parseEnv, promisify } from "node:util";
|
|
14
14
|
import { execFile, execFileSync } from "node:child_process";
|
|
15
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
15
|
+
import { accessSync, constants, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
16
16
|
import { DatabaseSync } from "node:sqlite";
|
|
17
17
|
import { ROOT_CONTEXT, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
|
|
18
18
|
import { pino, transport } from "pino";
|
|
@@ -836,8 +836,8 @@ Reset();
|
|
|
836
836
|
*
|
|
837
837
|
* Idempotent: registration is guarded by `Format.Has(...)`.
|
|
838
838
|
*/
|
|
839
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
840
|
-
if (!Has("uuid")) Set$1("uuid", (v) => UUID_RE.test(v));
|
|
839
|
+
var UUID_RE$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
840
|
+
if (!Has("uuid")) Set$1("uuid", (v) => UUID_RE$1.test(v));
|
|
841
841
|
if (!Has("date-time")) Set$1("date-time", (v) => !Number.isNaN(Date.parse(v)));
|
|
842
842
|
//#endregion
|
|
843
843
|
//#region ../../node_modules/.pnpm/typebox@1.2.8/node_modules/typebox/build/system/memory/metrics.mjs
|
|
@@ -4375,6 +4375,170 @@ function validateRubricWeights(rubric) {
|
|
|
4375
4375
|
return null;
|
|
4376
4376
|
}
|
|
4377
4377
|
//#endregion
|
|
4378
|
+
//#region ../../libs/tasks/src/runtime-profiles.ts
|
|
4379
|
+
var RuntimeProfileName = String$1({
|
|
4380
|
+
minLength: 1,
|
|
4381
|
+
maxLength: 100,
|
|
4382
|
+
pattern: "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$"
|
|
4383
|
+
});
|
|
4384
|
+
var RuntimeProfileEnvName = String$1({
|
|
4385
|
+
minLength: 1,
|
|
4386
|
+
maxLength: 128,
|
|
4387
|
+
pattern: "^[A-Z_][A-Z0-9_]*$"
|
|
4388
|
+
});
|
|
4389
|
+
var RuntimeProfileToolName = String$1({
|
|
4390
|
+
minLength: 1,
|
|
4391
|
+
maxLength: 128,
|
|
4392
|
+
pattern: "^[a-zA-Z0-9._/-]+$"
|
|
4393
|
+
});
|
|
4394
|
+
var SandboxResumeCommandWhenSchema = _Object_({ workspaceMode: Optional(_Array_(Union([
|
|
4395
|
+
Literal("shared_mount"),
|
|
4396
|
+
Literal("dedicated_worktree"),
|
|
4397
|
+
Literal("scratch_mount")
|
|
4398
|
+
]), {
|
|
4399
|
+
minItems: 1,
|
|
4400
|
+
maxItems: 3
|
|
4401
|
+
})) }, { additionalProperties: false });
|
|
4402
|
+
var RuntimeProfileSandboxResumeCommand = Union([String$1({
|
|
4403
|
+
minLength: 1,
|
|
4404
|
+
maxLength: 4096
|
|
4405
|
+
}), _Object_({
|
|
4406
|
+
run: String$1({
|
|
4407
|
+
minLength: 1,
|
|
4408
|
+
maxLength: 4096
|
|
4409
|
+
}),
|
|
4410
|
+
when: Optional(SandboxResumeCommandWhenSchema),
|
|
4411
|
+
retries: Optional(Integer({
|
|
4412
|
+
minimum: 0,
|
|
4413
|
+
maximum: 5
|
|
4414
|
+
})),
|
|
4415
|
+
retryBackoffMs: Optional(Integer({
|
|
4416
|
+
minimum: 0,
|
|
4417
|
+
maximum: 6e4
|
|
4418
|
+
}))
|
|
4419
|
+
}, { additionalProperties: false })]);
|
|
4420
|
+
var RuntimeProfileSandbox = _Object_({
|
|
4421
|
+
snapshot: Optional(_Object_({
|
|
4422
|
+
setupCommands: Optional(_Array_(String$1({
|
|
4423
|
+
minLength: 1,
|
|
4424
|
+
maxLength: 4096
|
|
4425
|
+
}), { maxItems: 20 })),
|
|
4426
|
+
allowedHosts: Optional(_Array_(String$1({
|
|
4427
|
+
minLength: 1,
|
|
4428
|
+
maxLength: 255
|
|
4429
|
+
}), { maxItems: 50 })),
|
|
4430
|
+
overlaySize: Optional(String$1({
|
|
4431
|
+
minLength: 2,
|
|
4432
|
+
maxLength: 16,
|
|
4433
|
+
pattern: "^[0-9]+[KMGTP]?$"
|
|
4434
|
+
}))
|
|
4435
|
+
}, { additionalProperties: false })),
|
|
4436
|
+
resumeCommands: Optional(_Array_(RuntimeProfileSandboxResumeCommand, { maxItems: 30 })),
|
|
4437
|
+
vfs: Optional(_Object_({
|
|
4438
|
+
shadow: Optional(_Array_(String$1({
|
|
4439
|
+
minLength: 1,
|
|
4440
|
+
maxLength: 255
|
|
4441
|
+
}), { maxItems: 100 })),
|
|
4442
|
+
shadowMode: Optional(Union([Literal("deny"), Literal("tmpfs")]))
|
|
4443
|
+
}, { additionalProperties: false })),
|
|
4444
|
+
env: Optional(Record(RuntimeProfileEnvName, String$1({ maxLength: 4096 }))),
|
|
4445
|
+
hostExec: Optional(_Object_({ autoApprove: Optional(Literal(false)) }, { additionalProperties: false })),
|
|
4446
|
+
resources: Optional(_Object_({
|
|
4447
|
+
memory: Optional(String$1({
|
|
4448
|
+
minLength: 2,
|
|
4449
|
+
maxLength: 16,
|
|
4450
|
+
pattern: "^[0-9]+[KMG]?$"
|
|
4451
|
+
})),
|
|
4452
|
+
cpus: Optional(Integer({
|
|
4453
|
+
minimum: 1,
|
|
4454
|
+
maximum: 32
|
|
4455
|
+
}))
|
|
4456
|
+
}, { additionalProperties: false }))
|
|
4457
|
+
}, {
|
|
4458
|
+
$id: "RuntimeProfileSandbox",
|
|
4459
|
+
additionalProperties: false
|
|
4460
|
+
});
|
|
4461
|
+
var RuntimeProfileContext = _Object_({
|
|
4462
|
+
slug: String$1({
|
|
4463
|
+
minLength: 1,
|
|
4464
|
+
maxLength: 64,
|
|
4465
|
+
pattern: "^[a-zA-Z0-9_-]+$"
|
|
4466
|
+
}),
|
|
4467
|
+
binding: Union([
|
|
4468
|
+
Literal("skill"),
|
|
4469
|
+
Literal("context_inline"),
|
|
4470
|
+
Literal("prompt_prefix"),
|
|
4471
|
+
Literal("user_inline")
|
|
4472
|
+
]),
|
|
4473
|
+
content: String$1({
|
|
4474
|
+
minLength: 1,
|
|
4475
|
+
maxLength: 65536
|
|
4476
|
+
})
|
|
4477
|
+
}, {
|
|
4478
|
+
$id: "RuntimeProfileContext",
|
|
4479
|
+
additionalProperties: false
|
|
4480
|
+
});
|
|
4481
|
+
var RuntimeProfileRef = _Object_({ profileId: String$1({ format: "uuid" }) }, {
|
|
4482
|
+
$id: "RuntimeProfileRef",
|
|
4483
|
+
additionalProperties: false
|
|
4484
|
+
});
|
|
4485
|
+
var RuntimeProfileLeaseTtlSec = Integer({
|
|
4486
|
+
minimum: 1,
|
|
4487
|
+
maximum: 86400
|
|
4488
|
+
});
|
|
4489
|
+
var RuntimeProfileHeartbeatIntervalMs = Integer({
|
|
4490
|
+
minimum: 0,
|
|
4491
|
+
maximum: 36e5
|
|
4492
|
+
});
|
|
4493
|
+
var RuntimeProfileMaxBatchSize = Integer({
|
|
4494
|
+
minimum: 1,
|
|
4495
|
+
maximum: 1e3
|
|
4496
|
+
});
|
|
4497
|
+
_Object_({
|
|
4498
|
+
id: String$1({ format: "uuid" }),
|
|
4499
|
+
teamId: String$1({ format: "uuid" }),
|
|
4500
|
+
name: RuntimeProfileName,
|
|
4501
|
+
description: Union([String$1({ maxLength: 4096 }), Null()]),
|
|
4502
|
+
provider: String$1({
|
|
4503
|
+
minLength: 1,
|
|
4504
|
+
maxLength: 100
|
|
4505
|
+
}),
|
|
4506
|
+
model: String$1({
|
|
4507
|
+
minLength: 1,
|
|
4508
|
+
maxLength: 200
|
|
4509
|
+
}),
|
|
4510
|
+
runtimeKind: Literal("gondolin_pi"),
|
|
4511
|
+
sandbox: RuntimeProfileSandbox,
|
|
4512
|
+
sessionStorageMode: Literal("local"),
|
|
4513
|
+
workspaceStorageMode: Literal("local"),
|
|
4514
|
+
sessionTtlSec: Integer({
|
|
4515
|
+
minimum: 1,
|
|
4516
|
+
maximum: 86400
|
|
4517
|
+
}),
|
|
4518
|
+
workspaceTtlSec: Integer({
|
|
4519
|
+
minimum: 1,
|
|
4520
|
+
maximum: 86400
|
|
4521
|
+
}),
|
|
4522
|
+
leaseTtlSec: RuntimeProfileLeaseTtlSec,
|
|
4523
|
+
heartbeatIntervalMs: RuntimeProfileHeartbeatIntervalMs,
|
|
4524
|
+
maxBatchSize: RuntimeProfileMaxBatchSize,
|
|
4525
|
+
requiredEnv: _Array_(RuntimeProfileEnvName, { maxItems: 100 }),
|
|
4526
|
+
requiredTools: _Array_(RuntimeProfileToolName, { maxItems: 100 }),
|
|
4527
|
+
context: _Array_(RuntimeProfileContext, { maxItems: 5 }),
|
|
4528
|
+
revision: Integer({ minimum: 1 }),
|
|
4529
|
+
definitionCid: String$1({
|
|
4530
|
+
minLength: 1,
|
|
4531
|
+
maxLength: 100
|
|
4532
|
+
}),
|
|
4533
|
+
createdByAgentId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4534
|
+
createdByHumanId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4535
|
+
createdAt: String$1({ format: "date-time" }),
|
|
4536
|
+
updatedAt: String$1({ format: "date-time" })
|
|
4537
|
+
}, {
|
|
4538
|
+
$id: "RuntimeProfile",
|
|
4539
|
+
additionalProperties: false
|
|
4540
|
+
});
|
|
4541
|
+
//#endregion
|
|
4378
4542
|
//#region ../../libs/tasks/src/success-criteria.ts
|
|
4379
4543
|
/**
|
|
4380
4544
|
* SuccessCriteria — proposer-stated acceptance criteria, evaluated in two
|
|
@@ -8967,6 +9131,7 @@ var TaskAttemptStatus = Union([
|
|
|
8967
9131
|
Literal("completed"),
|
|
8968
9132
|
Literal("failed"),
|
|
8969
9133
|
Literal("cancelled"),
|
|
9134
|
+
Literal("aborted"),
|
|
8970
9135
|
Literal("timed_out")
|
|
8971
9136
|
], { $id: "TaskAttemptStatus" });
|
|
8972
9137
|
var ExecutorTrustLevel = Union([
|
|
@@ -8975,14 +9140,6 @@ var ExecutorTrustLevel = Union([
|
|
|
8975
9140
|
Literal("releaseVerifiedTool"),
|
|
8976
9141
|
Literal("sandboxAttested")
|
|
8977
9142
|
], { $id: "ExecutorTrustLevel" });
|
|
8978
|
-
/** Identifies a (provider, model) daemon pair allowed to claim a task. */
|
|
8979
|
-
var ExecutorRef = _Object_({
|
|
8980
|
-
provider: String$1({ minLength: 1 }),
|
|
8981
|
-
model: String$1({ minLength: 1 })
|
|
8982
|
-
}, {
|
|
8983
|
-
$id: "ExecutorRef",
|
|
8984
|
-
additionalProperties: false
|
|
8985
|
-
});
|
|
8986
9143
|
var OutputKind = Union([Literal("artifact"), Literal("judgment")], { $id: "OutputKind" });
|
|
8987
9144
|
var TaskMessageKind = Union([
|
|
8988
9145
|
Literal("text_delta"),
|
|
@@ -9127,7 +9284,7 @@ _Object_({
|
|
|
9127
9284
|
acceptedAttemptN: Union([Number$1(), Null()]),
|
|
9128
9285
|
claimCondition: Union([Unsafe(Ref$2("ClaimCondition")), Null()]),
|
|
9129
9286
|
requiredExecutorTrustLevel: ExecutorTrustLevel,
|
|
9130
|
-
|
|
9287
|
+
allowedProfiles: _Array_(RuntimeProfileRef, { maxItems: 16 }),
|
|
9131
9288
|
status: TaskStatus,
|
|
9132
9289
|
queuedAt: IsoTimestamp,
|
|
9133
9290
|
completedAt: Union([IsoTimestamp, Null()]),
|
|
@@ -9223,15 +9380,23 @@ _Object_({
|
|
|
9223
9380
|
//#region src/lib/help.ts
|
|
9224
9381
|
var COMMON_REQUIRED_FLAGS = `\
|
|
9225
9382
|
-a, --agent <name> MoltNet agent identity. Reads credentials
|
|
9226
|
-
from <repo-root>/.moltnet/<name>/moltnet.json
|
|
9383
|
+
from <repo-root>/.moltnet/<name>/moltnet.json.`;
|
|
9384
|
+
var COMMON_MODEL_FLAGS = `\
|
|
9227
9385
|
-p, --provider <id> LLM provider id (e.g. anthropic, openai-codex).
|
|
9228
9386
|
-m, --model <id> LLM model id for the provider (e.g.
|
|
9229
|
-
claude-sonnet-4-5, gpt-5.3-codex)
|
|
9387
|
+
claude-sonnet-4-5, gpt-5.3-codex). Required
|
|
9388
|
+
unless --profile is set.`;
|
|
9230
9389
|
var COMMON_OPTIONAL_FLAGS = `\
|
|
9231
9390
|
--sandbox <path> Path to sandbox.json. Default: search up from
|
|
9232
9391
|
the daemon's CWD until found. The directory
|
|
9233
9392
|
containing sandbox.json is also used as the
|
|
9234
|
-
VM mountPath.
|
|
9393
|
+
VM mountPath. Cannot be used with --profile.
|
|
9394
|
+
--profile <uuid|name> Remote runtime profile. When set, provider,
|
|
9395
|
+
model, and sandbox policy come from the
|
|
9396
|
+
profile; task listing/claiming is restricted
|
|
9397
|
+
to unrestricted tasks plus tasks allowing this
|
|
9398
|
+
profile. requiredEnv/requiredTools are checked
|
|
9399
|
+
before claiming. Name lookup is team-scoped.
|
|
9235
9400
|
--lease-ttl-sec <n> Sliding liveness window. Silence longer than
|
|
9236
9401
|
this ends the attempt with lease_expired.
|
|
9237
9402
|
Default: 300.
|
|
@@ -9249,7 +9414,8 @@ var COMMON_OPTIONAL_FLAGS = `\
|
|
|
9249
9414
|
--warm-session-ttl-sec <n> Retain resumable daemon slots (Pi sessions +
|
|
9250
9415
|
reusable worktrees) in local daemon state for
|
|
9251
9416
|
this many seconds after use. 0 = disable reuse.
|
|
9252
|
-
Default: 1800
|
|
9417
|
+
Default: 1800, or min(profile session/workspace
|
|
9418
|
+
TTL) when --profile is set.
|
|
9253
9419
|
--debug Verbose logging: also log successful list/claim
|
|
9254
9420
|
outcomes (candidate counts, claim attempts).`;
|
|
9255
9421
|
var REGISTERED_TASK_TYPES = Object.keys(BUILT_IN_TASK_TYPES).sort();
|
|
@@ -9272,9 +9438,10 @@ Run \`agent-daemon <command> --help\` for command-specific flags.
|
|
|
9272
9438
|
|
|
9273
9439
|
Prerequisites (all subcommands):
|
|
9274
9440
|
- <repo-root>/.moltnet/<agent>/moltnet.json — credentials (see --agent)
|
|
9275
|
-
- sandbox.json —
|
|
9276
|
-
from CWD, or pass --sandbox <path>.
|
|
9277
|
-
|
|
9441
|
+
- sandbox.json or --profile — local sandbox config is resolved by
|
|
9442
|
+
searching up from CWD, or pass --sandbox <path>. With --profile, the
|
|
9443
|
+
remote runtime profile supplies provider/model/sandbox policy and CWD
|
|
9444
|
+
is used as the VM mountPath.
|
|
9278
9445
|
|
|
9279
9446
|
Registered task types: ${knownTaskTypesList()}`;
|
|
9280
9447
|
var POLL_HELP = `\
|
|
@@ -9287,6 +9454,7 @@ Required:
|
|
|
9287
9454
|
--team <uuid> Team whose queue to serve. The daemon must be
|
|
9288
9455
|
a member of this team (canAccessTeam permit).
|
|
9289
9456
|
${COMMON_REQUIRED_FLAGS}
|
|
9457
|
+
${COMMON_MODEL_FLAGS}
|
|
9290
9458
|
|
|
9291
9459
|
Optional:
|
|
9292
9460
|
--task-types <csv> Whitelist of task types to claim. Default:
|
|
@@ -9317,6 +9485,7 @@ Required:
|
|
|
9317
9485
|
-t, --task-id <uuid> Task to claim and execute. Must already be
|
|
9318
9486
|
in 'queued' status.
|
|
9319
9487
|
${COMMON_REQUIRED_FLAGS}
|
|
9488
|
+
${COMMON_MODEL_FLAGS}
|
|
9320
9489
|
|
|
9321
9490
|
Optional:
|
|
9322
9491
|
${COMMON_OPTIONAL_FLAGS}
|
|
@@ -9342,6 +9511,7 @@ sleeps and retries forever).
|
|
|
9342
9511
|
Required:
|
|
9343
9512
|
--team <uuid> Team whose queue to drain.
|
|
9344
9513
|
${COMMON_REQUIRED_FLAGS}
|
|
9514
|
+
${COMMON_MODEL_FLAGS}
|
|
9345
9515
|
|
|
9346
9516
|
Optional:
|
|
9347
9517
|
--task-types <csv> Whitelist. Known types: ${knownTaskTypesList()}
|
|
@@ -24318,6 +24488,9 @@ var ApiTaskReporter = class {
|
|
|
24318
24488
|
get cancelReason() {
|
|
24319
24489
|
return this.observedCancelReason;
|
|
24320
24490
|
}
|
|
24491
|
+
requestCancel(reason) {
|
|
24492
|
+
this.abortForCancel(reason);
|
|
24493
|
+
}
|
|
24321
24494
|
async open(ctx) {
|
|
24322
24495
|
if (this.heartbeatTimer) {
|
|
24323
24496
|
clearInterval(this.heartbeatTimer);
|
|
@@ -24500,13 +24673,15 @@ var ApiTaskReporter = class {
|
|
|
24500
24673
|
async sendHeartbeat() {
|
|
24501
24674
|
const body = this.opts.leaseTtlSec ? { leaseTtlSec: this.opts.leaseTtlSec } : {};
|
|
24502
24675
|
const response = await this.opts.tasks.heartbeat(this.taskId, this.attemptN, body);
|
|
24503
|
-
if (response?.cancelled
|
|
24504
|
-
|
|
24505
|
-
|
|
24506
|
-
|
|
24507
|
-
|
|
24508
|
-
|
|
24509
|
-
|
|
24676
|
+
if (response?.cancelled) this.abortForCancel(response.cancelReason ?? null);
|
|
24677
|
+
}
|
|
24678
|
+
abortForCancel(reason) {
|
|
24679
|
+
if (this.cancelController.signal.aborted) return;
|
|
24680
|
+
this.observedCancelReason = reason;
|
|
24681
|
+
this.cancelController.abort(/* @__PURE__ */ new Error(`Task cancelled${this.observedCancelReason ? `: ${this.observedCancelReason}` : ""}`));
|
|
24682
|
+
if (this.heartbeatTimer) {
|
|
24683
|
+
clearInterval(this.heartbeatTimer);
|
|
24684
|
+
this.heartbeatTimer = null;
|
|
24510
24685
|
}
|
|
24511
24686
|
}
|
|
24512
24687
|
};
|
|
@@ -24519,6 +24694,7 @@ var AgentRuntime = class {
|
|
|
24519
24694
|
currentTaskId: null
|
|
24520
24695
|
};
|
|
24521
24696
|
stopRequested = false;
|
|
24697
|
+
currentReporter = null;
|
|
24522
24698
|
logger;
|
|
24523
24699
|
constructor(opts) {
|
|
24524
24700
|
this.opts = opts;
|
|
@@ -24547,6 +24723,7 @@ var AgentRuntime = class {
|
|
|
24547
24723
|
});
|
|
24548
24724
|
taskLogger.info({}, "agent-runtime.task_claimed");
|
|
24549
24725
|
const reporter = this.opts.makeReporter(claimedTask);
|
|
24726
|
+
this.currentReporter = reporter;
|
|
24550
24727
|
const taskCtx = Object.keys(claimedTask.traceHeaders).length ? propagation.extract(ROOT_CONTEXT, claimedTask.traceHeaders) : context.active();
|
|
24551
24728
|
const taskStart = Date.now();
|
|
24552
24729
|
let output;
|
|
@@ -24604,16 +24781,19 @@ var AgentRuntime = class {
|
|
|
24604
24781
|
}
|
|
24605
24782
|
this.status.tasksProcessed += 1;
|
|
24606
24783
|
this.status.currentTaskId = null;
|
|
24784
|
+
this.currentReporter = null;
|
|
24607
24785
|
}
|
|
24608
24786
|
} finally {
|
|
24787
|
+
this.currentReporter = null;
|
|
24609
24788
|
await this.opts.source.close();
|
|
24610
24789
|
this.status.state = "stopped";
|
|
24611
24790
|
}
|
|
24612
24791
|
return outputs;
|
|
24613
24792
|
}
|
|
24614
24793
|
/** Request cooperative shutdown. Safe from signal handlers. */
|
|
24615
|
-
stop() {
|
|
24794
|
+
stop(reason) {
|
|
24616
24795
|
this.stopRequested = true;
|
|
24796
|
+
if (reason !== void 0) this.currentReporter?.requestCancel?.(reason);
|
|
24617
24797
|
}
|
|
24618
24798
|
};
|
|
24619
24799
|
//#endregion
|
|
@@ -24625,8 +24805,11 @@ var ApiTaskSource = class {
|
|
|
24625
24805
|
}
|
|
24626
24806
|
async claim() {
|
|
24627
24807
|
if (this.claimed) return null;
|
|
24628
|
-
const { agent, taskId, leaseTtlSec } = this.opts;
|
|
24629
|
-
const result = await agent.tasks.claim(taskId,
|
|
24808
|
+
const { agent, taskId, leaseTtlSec, profileId } = this.opts;
|
|
24809
|
+
const result = await agent.tasks.claim(taskId, {
|
|
24810
|
+
...leaseTtlSec ? { leaseTtlSec } : {},
|
|
24811
|
+
...profileId ? { profileId } : {}
|
|
24812
|
+
});
|
|
24630
24813
|
this.claimed = true;
|
|
24631
24814
|
return {
|
|
24632
24815
|
task: result.task,
|
|
@@ -26271,6 +26454,124 @@ var updateRenderedPack = (options) => (options.client ?? client).patch({
|
|
|
26271
26454
|
}
|
|
26272
26455
|
});
|
|
26273
26456
|
/**
|
|
26457
|
+
* List runtime profiles for the active team context.
|
|
26458
|
+
*/
|
|
26459
|
+
var listRuntimeProfiles = (options) => (options?.client ?? client).get({
|
|
26460
|
+
security: [
|
|
26461
|
+
{
|
|
26462
|
+
scheme: "bearer",
|
|
26463
|
+
type: "http"
|
|
26464
|
+
},
|
|
26465
|
+
{
|
|
26466
|
+
name: "X-Moltnet-Session-Token",
|
|
26467
|
+
type: "apiKey"
|
|
26468
|
+
},
|
|
26469
|
+
{
|
|
26470
|
+
in: "cookie",
|
|
26471
|
+
name: "ory_kratos_session",
|
|
26472
|
+
type: "apiKey"
|
|
26473
|
+
}
|
|
26474
|
+
],
|
|
26475
|
+
url: "/runtime-profiles",
|
|
26476
|
+
...options
|
|
26477
|
+
});
|
|
26478
|
+
/**
|
|
26479
|
+
* Create a runtime profile for the active team context.
|
|
26480
|
+
*/
|
|
26481
|
+
var createRuntimeProfile = (options) => (options?.client ?? client).post({
|
|
26482
|
+
security: [
|
|
26483
|
+
{
|
|
26484
|
+
scheme: "bearer",
|
|
26485
|
+
type: "http"
|
|
26486
|
+
},
|
|
26487
|
+
{
|
|
26488
|
+
name: "X-Moltnet-Session-Token",
|
|
26489
|
+
type: "apiKey"
|
|
26490
|
+
},
|
|
26491
|
+
{
|
|
26492
|
+
in: "cookie",
|
|
26493
|
+
name: "ory_kratos_session",
|
|
26494
|
+
type: "apiKey"
|
|
26495
|
+
}
|
|
26496
|
+
],
|
|
26497
|
+
url: "/runtime-profiles",
|
|
26498
|
+
...options,
|
|
26499
|
+
headers: {
|
|
26500
|
+
"Content-Type": "application/json",
|
|
26501
|
+
...options?.headers
|
|
26502
|
+
}
|
|
26503
|
+
});
|
|
26504
|
+
/**
|
|
26505
|
+
* Delete one runtime profile.
|
|
26506
|
+
*/
|
|
26507
|
+
var deleteRuntimeProfile = (options) => (options.client ?? client).delete({
|
|
26508
|
+
security: [
|
|
26509
|
+
{
|
|
26510
|
+
scheme: "bearer",
|
|
26511
|
+
type: "http"
|
|
26512
|
+
},
|
|
26513
|
+
{
|
|
26514
|
+
name: "X-Moltnet-Session-Token",
|
|
26515
|
+
type: "apiKey"
|
|
26516
|
+
},
|
|
26517
|
+
{
|
|
26518
|
+
in: "cookie",
|
|
26519
|
+
name: "ory_kratos_session",
|
|
26520
|
+
type: "apiKey"
|
|
26521
|
+
}
|
|
26522
|
+
],
|
|
26523
|
+
url: "/runtime-profiles/{profileId}",
|
|
26524
|
+
...options
|
|
26525
|
+
});
|
|
26526
|
+
/**
|
|
26527
|
+
* Get one runtime profile.
|
|
26528
|
+
*/
|
|
26529
|
+
var getRuntimeProfile = (options) => (options.client ?? client).get({
|
|
26530
|
+
security: [
|
|
26531
|
+
{
|
|
26532
|
+
scheme: "bearer",
|
|
26533
|
+
type: "http"
|
|
26534
|
+
},
|
|
26535
|
+
{
|
|
26536
|
+
name: "X-Moltnet-Session-Token",
|
|
26537
|
+
type: "apiKey"
|
|
26538
|
+
},
|
|
26539
|
+
{
|
|
26540
|
+
in: "cookie",
|
|
26541
|
+
name: "ory_kratos_session",
|
|
26542
|
+
type: "apiKey"
|
|
26543
|
+
}
|
|
26544
|
+
],
|
|
26545
|
+
url: "/runtime-profiles/{profileId}",
|
|
26546
|
+
...options
|
|
26547
|
+
});
|
|
26548
|
+
/**
|
|
26549
|
+
* Update one runtime profile.
|
|
26550
|
+
*/
|
|
26551
|
+
var updateRuntimeProfile = (options) => (options.client ?? client).patch({
|
|
26552
|
+
security: [
|
|
26553
|
+
{
|
|
26554
|
+
scheme: "bearer",
|
|
26555
|
+
type: "http"
|
|
26556
|
+
},
|
|
26557
|
+
{
|
|
26558
|
+
name: "X-Moltnet-Session-Token",
|
|
26559
|
+
type: "apiKey"
|
|
26560
|
+
},
|
|
26561
|
+
{
|
|
26562
|
+
in: "cookie",
|
|
26563
|
+
name: "ory_kratos_session",
|
|
26564
|
+
type: "apiKey"
|
|
26565
|
+
}
|
|
26566
|
+
],
|
|
26567
|
+
url: "/runtime-profiles/{profileId}",
|
|
26568
|
+
...options,
|
|
26569
|
+
headers: {
|
|
26570
|
+
"Content-Type": "application/json",
|
|
26571
|
+
...options.headers
|
|
26572
|
+
}
|
|
26573
|
+
});
|
|
26574
|
+
/**
|
|
26274
26575
|
* List tasks for a team with optional filters.
|
|
26275
26576
|
*/
|
|
26276
26577
|
var listTasks = (options) => (options.client ?? client).get({
|
|
@@ -26385,6 +26686,32 @@ var listTaskAttempts = (options) => (options.client ?? client).get({
|
|
|
26385
26686
|
...options
|
|
26386
26687
|
});
|
|
26387
26688
|
/**
|
|
26689
|
+
* Claimant intentionally abandons this attempt (e.g. daemon shutdown). The attempt becomes aborted and the task requeues for another claim (or fails when retries are exhausted). Does NOT cancel the task.
|
|
26690
|
+
*/
|
|
26691
|
+
var abortTaskAttempt = (options) => (options.client ?? client).post({
|
|
26692
|
+
security: [
|
|
26693
|
+
{
|
|
26694
|
+
scheme: "bearer",
|
|
26695
|
+
type: "http"
|
|
26696
|
+
},
|
|
26697
|
+
{
|
|
26698
|
+
name: "X-Moltnet-Session-Token",
|
|
26699
|
+
type: "apiKey"
|
|
26700
|
+
},
|
|
26701
|
+
{
|
|
26702
|
+
in: "cookie",
|
|
26703
|
+
name: "ory_kratos_session",
|
|
26704
|
+
type: "apiKey"
|
|
26705
|
+
}
|
|
26706
|
+
],
|
|
26707
|
+
url: "/tasks/{id}/attempts/{n}/abort",
|
|
26708
|
+
...options,
|
|
26709
|
+
headers: {
|
|
26710
|
+
"Content-Type": "application/json",
|
|
26711
|
+
...options.headers
|
|
26712
|
+
}
|
|
26713
|
+
});
|
|
26714
|
+
/**
|
|
26388
26715
|
* Mark an attempt as completed with output.
|
|
26389
26716
|
*/
|
|
26390
26717
|
var completeTask = (options) => (options.client ?? client).post({
|
|
@@ -28517,6 +28844,54 @@ function createRecoveryNamespace(context) {
|
|
|
28517
28844
|
};
|
|
28518
28845
|
}
|
|
28519
28846
|
//#endregion
|
|
28847
|
+
//#region ../../libs/sdk/src/namespaces/runtime-profiles.ts
|
|
28848
|
+
function createRuntimeProfilesNamespace(context) {
|
|
28849
|
+
const { client, auth } = context;
|
|
28850
|
+
return {
|
|
28851
|
+
async list(options) {
|
|
28852
|
+
return unwrapResult(await listRuntimeProfiles({
|
|
28853
|
+
client,
|
|
28854
|
+
auth,
|
|
28855
|
+
headers: teamHeaders(options)
|
|
28856
|
+
}));
|
|
28857
|
+
},
|
|
28858
|
+
async create(body, options) {
|
|
28859
|
+
return unwrapResult(await createRuntimeProfile({
|
|
28860
|
+
client,
|
|
28861
|
+
auth,
|
|
28862
|
+
headers: teamHeaders(options),
|
|
28863
|
+
body
|
|
28864
|
+
}));
|
|
28865
|
+
},
|
|
28866
|
+
async get(profileId) {
|
|
28867
|
+
return unwrapResult(await getRuntimeProfile({
|
|
28868
|
+
client,
|
|
28869
|
+
auth,
|
|
28870
|
+
path: { profileId }
|
|
28871
|
+
}));
|
|
28872
|
+
},
|
|
28873
|
+
async update(profileId, body) {
|
|
28874
|
+
return unwrapResult(await updateRuntimeProfile({
|
|
28875
|
+
client,
|
|
28876
|
+
auth,
|
|
28877
|
+
path: { profileId },
|
|
28878
|
+
body
|
|
28879
|
+
}));
|
|
28880
|
+
},
|
|
28881
|
+
async delete(profileId) {
|
|
28882
|
+
const result = await deleteRuntimeProfile({
|
|
28883
|
+
client,
|
|
28884
|
+
auth,
|
|
28885
|
+
path: { profileId }
|
|
28886
|
+
});
|
|
28887
|
+
if (result.error) unwrapResult(result);
|
|
28888
|
+
}
|
|
28889
|
+
};
|
|
28890
|
+
}
|
|
28891
|
+
function teamHeaders(options) {
|
|
28892
|
+
return options?.teamId ? { "x-moltnet-team-id": options.teamId } : void 0;
|
|
28893
|
+
}
|
|
28894
|
+
//#endregion
|
|
28520
28895
|
//#region ../../libs/sdk/src/namespaces/signing-requests.ts
|
|
28521
28896
|
function createSigningRequestsNamespace(context) {
|
|
28522
28897
|
const { client, auth } = context;
|
|
@@ -28637,6 +29012,17 @@ function createTasksNamespace(context) {
|
|
|
28637
29012
|
body
|
|
28638
29013
|
}));
|
|
28639
29014
|
},
|
|
29015
|
+
async abortAttempt(id, n, body) {
|
|
29016
|
+
return unwrapResult(await abortTaskAttempt({
|
|
29017
|
+
client,
|
|
29018
|
+
auth,
|
|
29019
|
+
path: {
|
|
29020
|
+
id,
|
|
29021
|
+
n
|
|
29022
|
+
},
|
|
29023
|
+
body
|
|
29024
|
+
}));
|
|
29025
|
+
},
|
|
28640
29026
|
async cancel(id, body) {
|
|
28641
29027
|
return unwrapResult(await cancelTask({
|
|
28642
29028
|
client,
|
|
@@ -28820,6 +29206,7 @@ function createAgent(options) {
|
|
|
28820
29206
|
legreffier: createLegreffierNamespace(context),
|
|
28821
29207
|
problems: createProblemsNamespace(context),
|
|
28822
29208
|
teams: createTeamsNamespace(context),
|
|
29209
|
+
runtimeProfiles: createRuntimeProfilesNamespace(context),
|
|
28823
29210
|
tasks: createTasksNamespace(context),
|
|
28824
29211
|
client,
|
|
28825
29212
|
getToken: () => tokenManager.getToken()
|
|
@@ -30908,7 +31295,6 @@ var PollingApiTaskSource = class {
|
|
|
30908
31295
|
this.minBackoffMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
30909
31296
|
this.maxBackoffMs = opts.maxPollIntervalMs ?? DEFAULT_MAX_POLL_INTERVAL_MS;
|
|
30910
31297
|
if (this.maxBackoffMs < this.minBackoffMs) throw new Error(`PollingApiTaskSource: maxPollIntervalMs (${this.maxBackoffMs}) must be >= pollIntervalMs (${this.minBackoffMs})`);
|
|
30911
|
-
if (Boolean(opts.provider) !== Boolean(opts.model)) throw new Error("PollingApiTaskSource: provider and model must be set together");
|
|
30912
31298
|
this.listLimit = opts.listLimit ?? DEFAULT_LIST_LIMIT;
|
|
30913
31299
|
this.currentBackoffMs = this.minBackoffMs;
|
|
30914
31300
|
this.logger = (opts.logger ?? pino({ name: "polling-api-source" })).child({ teamId: opts.teamId });
|
|
@@ -30951,10 +31337,7 @@ var PollingApiTaskSource = class {
|
|
|
30951
31337
|
teamId: this.opts.teamId,
|
|
30952
31338
|
status: "queued",
|
|
30953
31339
|
...taskTypes ? { taskTypes } : {},
|
|
30954
|
-
...this.opts.
|
|
30955
|
-
provider: this.opts.provider,
|
|
30956
|
-
model: this.opts.model
|
|
30957
|
-
} : {},
|
|
31340
|
+
...this.opts.profileId ? { profileId: this.opts.profileId } : {},
|
|
30958
31341
|
...cursor ? { cursor } : {},
|
|
30959
31342
|
limit: this.listLimit
|
|
30960
31343
|
});
|
|
@@ -30969,9 +31352,9 @@ var PollingApiTaskSource = class {
|
|
|
30969
31352
|
if (this.opts.taskTypes && this.opts.taskTypes.length > 0 && !this.opts.taskTypes.includes(item.taskType)) continue;
|
|
30970
31353
|
if (this.opts.diaryIds && this.opts.diaryIds.length > 0 && (item.diaryId === null || !this.opts.diaryIds.includes(item.diaryId))) continue;
|
|
30971
31354
|
if (this.opts.slotRegistry && !await isContinuationClaimableByThisDaemon(item, this.opts.slotRegistry)) continue;
|
|
30972
|
-
if (this.opts.
|
|
30973
|
-
const allowed = item.
|
|
30974
|
-
if (allowed.length > 0 && !allowed.some((
|
|
31355
|
+
if (this.opts.profileId) {
|
|
31356
|
+
const allowed = item.allowedProfiles ?? [];
|
|
31357
|
+
if (allowed.length > 0 && !allowed.some((p) => p.profileId === this.opts.profileId)) continue;
|
|
30975
31358
|
}
|
|
30976
31359
|
if (item.status !== "queued") continue;
|
|
30977
31360
|
seen.add(item.id);
|
|
@@ -30994,7 +31377,10 @@ var PollingApiTaskSource = class {
|
|
|
30994
31377
|
for (const task of candidates) {
|
|
30995
31378
|
if (this.aborted()) return null;
|
|
30996
31379
|
try {
|
|
30997
|
-
const result = await this.opts.agent.tasks.claim(task.id, {
|
|
31380
|
+
const result = await this.opts.agent.tasks.claim(task.id, {
|
|
31381
|
+
leaseTtlSec: this.opts.leaseTtlSec,
|
|
31382
|
+
...this.opts.profileId ? { profileId: this.opts.profileId } : {}
|
|
31383
|
+
});
|
|
30998
31384
|
if (this.opts.debug) this.logger.debug({
|
|
30999
31385
|
taskId: result.task.id,
|
|
31000
31386
|
taskType: result.task.taskType,
|
|
@@ -32128,6 +32514,63 @@ function pruneOldSnapshots(maxCached, currentDir) {
|
|
|
32128
32514
|
});
|
|
32129
32515
|
}
|
|
32130
32516
|
//#endregion
|
|
32517
|
+
//#region ../../libs/pi-extension/src/abort-utils.ts
|
|
32518
|
+
function throwIfAborted(signal, label) {
|
|
32519
|
+
if (!signal?.aborted) return;
|
|
32520
|
+
throw abortError(label, signal);
|
|
32521
|
+
}
|
|
32522
|
+
function abortError(label, signal) {
|
|
32523
|
+
const reason = signal.reason;
|
|
32524
|
+
const suffix = reason instanceof Error ? reason.message : reason === void 0 ? "aborted" : String(reason);
|
|
32525
|
+
const err = /* @__PURE__ */ new Error(`${label} aborted: ${suffix}`);
|
|
32526
|
+
err.name = "AbortError";
|
|
32527
|
+
return err;
|
|
32528
|
+
}
|
|
32529
|
+
function cleanupLateResource(resourcePromise, opts) {
|
|
32530
|
+
resourcePromise.then(async (resource) => {
|
|
32531
|
+
try {
|
|
32532
|
+
await opts.cleanup(resource);
|
|
32533
|
+
} catch (err) {
|
|
32534
|
+
opts.onCleanupError?.(err);
|
|
32535
|
+
}
|
|
32536
|
+
}, () => {});
|
|
32537
|
+
}
|
|
32538
|
+
async function abortableResource(opts) {
|
|
32539
|
+
const { signal } = opts;
|
|
32540
|
+
if (!signal) return opts.promise;
|
|
32541
|
+
throwIfAborted(signal, opts.label);
|
|
32542
|
+
const resourcePromise = Promise.resolve(opts.promise);
|
|
32543
|
+
const abortPromise = new Promise((_, reject) => {
|
|
32544
|
+
const abort = () => {
|
|
32545
|
+
cleanupLateResource(resourcePromise, opts);
|
|
32546
|
+
reject(abortError(opts.label, signal));
|
|
32547
|
+
};
|
|
32548
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
32549
|
+
resourcePromise.then(() => signal.removeEventListener("abort", abort), () => signal.removeEventListener("abort", abort));
|
|
32550
|
+
});
|
|
32551
|
+
return Promise.race([resourcePromise, abortPromise]);
|
|
32552
|
+
}
|
|
32553
|
+
async function delay(ms, signal, label) {
|
|
32554
|
+
if (!signal) {
|
|
32555
|
+
await new Promise((resolve) => {
|
|
32556
|
+
setTimeout(resolve, ms);
|
|
32557
|
+
});
|
|
32558
|
+
return;
|
|
32559
|
+
}
|
|
32560
|
+
throwIfAborted(signal, label);
|
|
32561
|
+
await new Promise((resolve, reject) => {
|
|
32562
|
+
const listener = () => {
|
|
32563
|
+
clearTimeout(timeout);
|
|
32564
|
+
reject(abortError(label, signal));
|
|
32565
|
+
};
|
|
32566
|
+
const timeout = setTimeout(() => {
|
|
32567
|
+
signal.removeEventListener("abort", listener);
|
|
32568
|
+
resolve();
|
|
32569
|
+
}, ms);
|
|
32570
|
+
signal.addEventListener("abort", listener, { once: true });
|
|
32571
|
+
});
|
|
32572
|
+
}
|
|
32573
|
+
//#endregion
|
|
32131
32574
|
//#region ../../libs/pi-extension/src/vm-manager.ts
|
|
32132
32575
|
/**
|
|
32133
32576
|
* Memory-backed VFS mount used by the daemon to inject task-context
|
|
@@ -32244,23 +32687,33 @@ var BASE_ALLOWED_HOSTS = [
|
|
|
32244
32687
|
* surface immediately rather than fall through to cryptic agent
|
|
32245
32688
|
* errors later.
|
|
32246
32689
|
*/
|
|
32247
|
-
async function vmRun(vm, label, command) {
|
|
32690
|
+
async function vmRun(vm, label, command, signal) {
|
|
32248
32691
|
const wrapped = `set -eu\nset -o pipefail\n${command}`;
|
|
32692
|
+
throwIfAborted(signal, `resume step "${label}"`);
|
|
32249
32693
|
const r = await vm.exec([
|
|
32250
32694
|
"sh",
|
|
32251
32695
|
"-c",
|
|
32252
32696
|
wrapped
|
|
32253
|
-
]);
|
|
32697
|
+
], { signal });
|
|
32254
32698
|
if (r.exitCode !== 0) {
|
|
32255
32699
|
const tail = [r.stderr, r.stdout].filter(Boolean).join("\n").slice(-800);
|
|
32256
32700
|
throw new Error(`resume step "${label}" failed (exit ${r.exitCode}):\n${tail}`);
|
|
32257
32701
|
}
|
|
32258
32702
|
}
|
|
32703
|
+
function nonErrorMessage(err) {
|
|
32704
|
+
if (typeof err === "string") return err;
|
|
32705
|
+
try {
|
|
32706
|
+
return JSON.stringify(err) ?? "unknown error";
|
|
32707
|
+
} catch {
|
|
32708
|
+
return "unknown error";
|
|
32709
|
+
}
|
|
32710
|
+
}
|
|
32259
32711
|
/**
|
|
32260
32712
|
* Resume a VM from a checkpoint, inject credentials, configure egress +
|
|
32261
32713
|
* TLS. Returns the managed VM handle.
|
|
32262
32714
|
*/
|
|
32263
32715
|
async function resumeVm(config) {
|
|
32716
|
+
throwIfAborted(config.signal, "VM resume");
|
|
32264
32717
|
const mainRepo = findMainWorktree();
|
|
32265
32718
|
const agentDir = path.join(mainRepo, ".moltnet", config.agentName);
|
|
32266
32719
|
const guestWorkspace = path.resolve(config.mountPath);
|
|
@@ -32304,24 +32757,33 @@ async function resumeVm(config) {
|
|
|
32304
32757
|
};
|
|
32305
32758
|
const resources = config.sandboxConfig?.resources;
|
|
32306
32759
|
const workspaceMode = config.workspaceMode ?? "shared_mount";
|
|
32307
|
-
const vm = await
|
|
32308
|
-
|
|
32309
|
-
|
|
32310
|
-
|
|
32311
|
-
|
|
32312
|
-
|
|
32313
|
-
|
|
32314
|
-
|
|
32315
|
-
|
|
32760
|
+
const vm = await abortableResource({
|
|
32761
|
+
promise: VmCheckpoint.load(config.checkpointPath).resume({
|
|
32762
|
+
httpHooks,
|
|
32763
|
+
env: vmEnv,
|
|
32764
|
+
...resources?.memory && { memory: resources.memory },
|
|
32765
|
+
...resources?.cpus && { cpus: resources.cpus },
|
|
32766
|
+
vfs: { mounts: {
|
|
32767
|
+
[guestWorkspace]: workspaceProvider,
|
|
32768
|
+
[GUEST_TASK_SKILLS_MOUNT]: new MemoryProvider()
|
|
32769
|
+
} }
|
|
32770
|
+
}),
|
|
32771
|
+
signal: config.signal,
|
|
32772
|
+
label: "VM resume",
|
|
32773
|
+
cleanup: (resumedVm) => resumedVm.close(),
|
|
32774
|
+
onCleanupError: (err) => {
|
|
32775
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32776
|
+
process.stderr.write(`[vm] aborted resume late vm.close() failed: ${message}\n`);
|
|
32777
|
+
}
|
|
32316
32778
|
});
|
|
32317
32779
|
try {
|
|
32318
|
-
await vm
|
|
32780
|
+
await vmRun(vm, "TLS certificates", `
|
|
32319
32781
|
cp /etc/gondolin/mitm/ca.crt /usr/local/share/ca-certificates/gondolin-mitm.crt
|
|
32320
32782
|
update-ca-certificates 2>/dev/null
|
|
32321
32783
|
cat /etc/gondolin/mitm/ca.crt >> /etc/ssl/certs/ca-certificates.crt
|
|
32322
|
-
|
|
32323
|
-
await vmRun(vm, "DNS resolvers", `printf 'nameserver 8.8.8.8\\nnameserver 1.1.1.1\\n' > /etc/resolv.conf
|
|
32324
|
-
await vmRun(vm, "git safe.directory", `git config --system --add safe.directory '*'
|
|
32784
|
+
`, config.signal);
|
|
32785
|
+
await vmRun(vm, "DNS resolvers", `printf 'nameserver 8.8.8.8\\nnameserver 1.1.1.1\\n' > /etc/resolv.conf`, config.signal);
|
|
32786
|
+
await vmRun(vm, "git safe.directory", `git config --system --add safe.directory '*'`, config.signal);
|
|
32325
32787
|
for (const [i, entry] of (config.sandboxConfig?.resumeCommands ?? []).entries()) {
|
|
32326
32788
|
if (!shouldRunResumeCommand(entry, { workspaceMode })) continue;
|
|
32327
32789
|
const { run, retries, backoffMs } = typeof entry === "string" ? {
|
|
@@ -32336,34 +32798,67 @@ async function resumeVm(config) {
|
|
|
32336
32798
|
const label = `resumeCommands[${i}]`;
|
|
32337
32799
|
let lastErr;
|
|
32338
32800
|
for (let attempt = 0; attempt <= retries; attempt++) try {
|
|
32339
|
-
await vmRun(vm, label, run);
|
|
32801
|
+
await vmRun(vm, label, run, config.signal);
|
|
32340
32802
|
lastErr = void 0;
|
|
32341
32803
|
break;
|
|
32342
32804
|
} catch (err) {
|
|
32343
32805
|
lastErr = err;
|
|
32344
32806
|
if (attempt === retries) break;
|
|
32345
|
-
await
|
|
32346
|
-
setTimeout(resolve, (attempt + 1) * backoffMs);
|
|
32347
|
-
});
|
|
32807
|
+
await delay((attempt + 1) * backoffMs, config.signal, label);
|
|
32348
32808
|
}
|
|
32349
|
-
if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(
|
|
32809
|
+
if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(nonErrorMessage(lastErr));
|
|
32350
32810
|
}
|
|
32351
32811
|
const vmSshDir = `${vmAgentDir}/ssh`;
|
|
32352
|
-
await vm.exec(`mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent
|
|
32353
|
-
if (creds.piAuthJson !== null) await vm.fs.writeFile("/home/agent/.pi/agent/auth.json", creds.piAuthJson, {
|
|
32812
|
+
await vm.exec(`mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent`, { signal: config.signal });
|
|
32813
|
+
if (creds.piAuthJson !== null) await vm.fs.writeFile("/home/agent/.pi/agent/auth.json", creds.piAuthJson, {
|
|
32814
|
+
mode: 384,
|
|
32815
|
+
signal: config.signal
|
|
32816
|
+
});
|
|
32354
32817
|
const vmMoltnetJson = rewriteMoltnetJsonPaths(creds.moltnetJson, vmAgentDir, vmSshDir, creds.githubAppPemFilename);
|
|
32355
|
-
await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, {
|
|
32356
|
-
|
|
32818
|
+
await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, {
|
|
32819
|
+
mode: 384,
|
|
32820
|
+
signal: config.signal
|
|
32821
|
+
});
|
|
32822
|
+
await vm.fs.writeFile(`${vmAgentDir}/env`, creds.agentEnvRaw, {
|
|
32823
|
+
mode: 384,
|
|
32824
|
+
signal: config.signal
|
|
32825
|
+
});
|
|
32357
32826
|
if (creds.gitconfig) {
|
|
32358
32827
|
const vmSigningKey = `${vmSshDir}/id_ed25519`;
|
|
32359
32828
|
const vmGitconfig = creds.gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSigningKey}`);
|
|
32360
|
-
await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
|
|
32829
|
+
await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
|
|
32830
|
+
mode: 420,
|
|
32831
|
+
signal: config.signal
|
|
32832
|
+
});
|
|
32361
32833
|
}
|
|
32362
|
-
if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, {
|
|
32363
|
-
|
|
32364
|
-
|
|
32365
|
-
|
|
32366
|
-
await vm.
|
|
32834
|
+
if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, {
|
|
32835
|
+
mode: 384,
|
|
32836
|
+
signal: config.signal
|
|
32837
|
+
});
|
|
32838
|
+
if (creds.sshPublicKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519.pub`, creds.sshPublicKey, {
|
|
32839
|
+
mode: 420,
|
|
32840
|
+
signal: config.signal
|
|
32841
|
+
});
|
|
32842
|
+
if (creds.allowedSigners) await vm.fs.writeFile(`${vmSshDir}/allowed_signers`, creds.allowedSigners, {
|
|
32843
|
+
mode: 420,
|
|
32844
|
+
signal: config.signal
|
|
32845
|
+
});
|
|
32846
|
+
if (creds.githubAppPem && creds.githubAppPemFilename) await vm.fs.writeFile(`${vmAgentDir}/${creds.githubAppPemFilename}`, creds.githubAppPem, {
|
|
32847
|
+
mode: 384,
|
|
32848
|
+
signal: config.signal
|
|
32849
|
+
});
|
|
32850
|
+
await vm.exec("chown -R agent:agent /home/agent/.pi /home/agent/.moltnet", { signal: config.signal });
|
|
32851
|
+
const gitCredHelperPath = `${vmSshDir}/git-credential-moltnet`;
|
|
32852
|
+
const credHelperScript = `#!/bin/sh
|
|
32853
|
+
echo "username=x-access-token"
|
|
32854
|
+
echo "password=$(moltnet github token --credentials ${vmSshDir}/moltnet.json)"
|
|
32855
|
+
`;
|
|
32856
|
+
await vm.fs.writeFile(gitCredHelperPath, credHelperScript, {
|
|
32857
|
+
mode: 493,
|
|
32858
|
+
signal: config.signal
|
|
32859
|
+
});
|
|
32860
|
+
await vmRun(vm, "git credential helper", `git config --global credential.helper ${gitCredHelperPath} && \
|
|
32861
|
+
git config --global url."https://github.com/".insteadOf "git@github.com:"`, config.signal);
|
|
32367
32862
|
return {
|
|
32368
32863
|
vm,
|
|
32369
32864
|
credentials: creds,
|
|
@@ -33616,6 +34111,20 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
33616
34111
|
retryable: false
|
|
33617
34112
|
}
|
|
33618
34113
|
});
|
|
34114
|
+
const makeCancelledOutput = (message) => ({
|
|
34115
|
+
taskId: task.id,
|
|
34116
|
+
attemptN,
|
|
34117
|
+
status: "cancelled",
|
|
34118
|
+
output: null,
|
|
34119
|
+
outputCid: null,
|
|
34120
|
+
usage: finalUsage,
|
|
34121
|
+
durationMs: Date.now() - startTime,
|
|
34122
|
+
error: {
|
|
34123
|
+
code: "task_cancelled",
|
|
34124
|
+
message,
|
|
34125
|
+
retryable: false
|
|
34126
|
+
}
|
|
34127
|
+
});
|
|
33619
34128
|
let onTurnEvent;
|
|
33620
34129
|
if (opts.makeOnTurnEvent) try {
|
|
33621
34130
|
onTurnEvent = opts.makeOnTurnEvent(claimedTask);
|
|
@@ -33678,10 +34187,15 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
33678
34187
|
mountPath,
|
|
33679
34188
|
workspaceMode: workspace.mode,
|
|
33680
34189
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
33681
|
-
sandboxConfig
|
|
34190
|
+
sandboxConfig,
|
|
34191
|
+
signal: reporter.cancelSignal
|
|
33682
34192
|
});
|
|
33683
34193
|
} catch (err) {
|
|
33684
34194
|
const message = err instanceof Error ? err.message : String(err);
|
|
34195
|
+
if (reporter.cancelSignal.aborted) {
|
|
34196
|
+
await emitError("vm_resume", message, { cancelled: true });
|
|
34197
|
+
return makeCancelledOutput(reporter.cancelReason ?? "Task cancelled during VM resume.");
|
|
34198
|
+
}
|
|
33685
34199
|
await emitError("vm_resume", message);
|
|
33686
34200
|
return makeFailedOutput("vm_resume_failed", message);
|
|
33687
34201
|
}
|
|
@@ -34269,7 +34783,9 @@ function loadConfig() {
|
|
|
34269
34783
|
return {
|
|
34270
34784
|
agentDaemonStateDatabaseUrl: process.env["MOLTNET_AGENT_DAEMON_STATE_DATABASE_URL"] ?? "",
|
|
34271
34785
|
otelEndpoint: process.env["MOLTNET_OTEL_ENDPOINT"] ?? "",
|
|
34272
|
-
logLevel: process.env["LOG_LEVEL"] ?? ""
|
|
34786
|
+
logLevel: process.env["LOG_LEVEL"] ?? "",
|
|
34787
|
+
profilePrerequisiteEnv: process.env,
|
|
34788
|
+
profilePrerequisitePath: process.env.PATH ?? ""
|
|
34273
34789
|
};
|
|
34274
34790
|
}
|
|
34275
34791
|
//#endregion
|
|
@@ -34697,13 +35213,27 @@ function createRootLogger(options) {
|
|
|
34697
35213
|
target: "pino-pretty",
|
|
34698
35214
|
options: { colorize: true }
|
|
34699
35215
|
}) : null;
|
|
35216
|
+
let transportClosed = prettyTransport === null;
|
|
35217
|
+
let shutdownStarted = false;
|
|
35218
|
+
prettyTransport?.on("close", () => {
|
|
35219
|
+
transportClosed = true;
|
|
35220
|
+
});
|
|
35221
|
+
prettyTransport?.on("error", (err) => {
|
|
35222
|
+
transportClosed = true;
|
|
35223
|
+
process.stderr.write(`[pino] transport error: ` + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
35224
|
+
});
|
|
34700
35225
|
const logger = pino(options, prettyTransport ?? void 0);
|
|
34701
35226
|
const shutdown = async () => {
|
|
34702
|
-
if (!prettyTransport) return;
|
|
34703
|
-
|
|
35227
|
+
if (!prettyTransport || shutdownStarted || transportClosed) return;
|
|
35228
|
+
shutdownStarted = true;
|
|
35229
|
+
try {
|
|
35230
|
+
logger.flush();
|
|
35231
|
+
} catch (err) {
|
|
35232
|
+
process.stderr.write(`[pino] logger flush failed: ` + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
35233
|
+
}
|
|
34704
35234
|
try {
|
|
34705
35235
|
prettyTransport.end();
|
|
34706
|
-
await once(prettyTransport, "close");
|
|
35236
|
+
if (!transportClosed) await once(prettyTransport, "close");
|
|
34707
35237
|
} catch (err) {
|
|
34708
35238
|
process.stderr.write(`[pino] transport teardown failed: ` + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
34709
35239
|
}
|
|
@@ -34731,22 +35261,29 @@ var MissingRequiredOptionError = class extends Error {
|
|
|
34731
35261
|
this.name = "MissingRequiredOptionError";
|
|
34732
35262
|
}
|
|
34733
35263
|
};
|
|
34734
|
-
function parseCommonOptions(args) {
|
|
35264
|
+
function parseCommonOptions(args, options = {}) {
|
|
35265
|
+
const requireProviderModel = options.requireProviderModel ?? true;
|
|
35266
|
+
const runtimeDefaults = {
|
|
35267
|
+
leaseTtlSec: options.runtimeDefaults?.leaseTtlSec ?? DEFAULTS.leaseTtlSec,
|
|
35268
|
+
heartbeatIntervalMs: options.runtimeDefaults?.heartbeatIntervalMs ?? DEFAULTS.heartbeatIntervalMs,
|
|
35269
|
+
maxBatchSize: options.runtimeDefaults?.maxBatchSize ?? DEFAULTS.maxBatchSize,
|
|
35270
|
+
warmSessionTtlSec: options.runtimeDefaults?.warmSessionTtlSec ?? DEFAULTS.warmSessionTtlSec
|
|
35271
|
+
};
|
|
34735
35272
|
if (!args.agent) throw new MissingRequiredOptionError("agent");
|
|
34736
|
-
if (!args.provider) throw new MissingRequiredOptionError("provider");
|
|
34737
|
-
if (!args.model) throw new MissingRequiredOptionError("model");
|
|
35273
|
+
if (requireProviderModel && !args.provider) throw new MissingRequiredOptionError("provider");
|
|
35274
|
+
if (requireProviderModel && !args.model) throw new MissingRequiredOptionError("model");
|
|
34738
35275
|
if (!/^[a-zA-Z0-9_-]+$/.test(args.agent)) throw new Error(`Invalid --agent "${args.agent}": must match /^[a-zA-Z0-9_-]+$/`);
|
|
34739
35276
|
return {
|
|
34740
35277
|
agent: args.agent,
|
|
34741
|
-
provider: args.provider,
|
|
34742
|
-
model: args.model,
|
|
34743
|
-
leaseTtlSec: parsePositiveInt(args["lease-ttl-sec"], "lease-ttl-sec",
|
|
34744
|
-
heartbeatIntervalMs: parseNonNegativeInt(args["heartbeat-interval-ms"], "heartbeat-interval-ms",
|
|
34745
|
-
maxBatchSize: parsePositiveInt(args["max-batch-size"], "max-batch-size",
|
|
35278
|
+
...args.provider ? { provider: args.provider } : {},
|
|
35279
|
+
...args.model ? { model: args.model } : {},
|
|
35280
|
+
leaseTtlSec: parsePositiveInt(args["lease-ttl-sec"], "lease-ttl-sec", runtimeDefaults.leaseTtlSec),
|
|
35281
|
+
heartbeatIntervalMs: parseNonNegativeInt(args["heartbeat-interval-ms"], "heartbeat-interval-ms", runtimeDefaults.heartbeatIntervalMs),
|
|
35282
|
+
maxBatchSize: parsePositiveInt(args["max-batch-size"], "max-batch-size", runtimeDefaults.maxBatchSize),
|
|
34746
35283
|
flushIntervalMs: parseNonNegativeInt(args["flush-interval-ms"], "flush-interval-ms", DEFAULTS.flushIntervalMs),
|
|
34747
35284
|
maxTurns: parseNonNegativeInt(args["max-turns"], "max-turns", DEFAULTS.maxTurns),
|
|
34748
35285
|
maxBashTimeouts: parseNonNegativeInt(args["max-bash-timeouts"], "max-bash-timeouts", DEFAULTS.maxBashTimeouts),
|
|
34749
|
-
warmSessionTtlSec: parseNonNegativeInt(args["warm-session-ttl-sec"], "warm-session-ttl-sec",
|
|
35286
|
+
warmSessionTtlSec: parseNonNegativeInt(args["warm-session-ttl-sec"], "warm-session-ttl-sec", runtimeDefaults.warmSessionTtlSec),
|
|
34750
35287
|
debug: args.debug === true
|
|
34751
35288
|
};
|
|
34752
35289
|
}
|
|
@@ -34837,6 +35374,71 @@ async function initWorkerOtel(options) {
|
|
|
34837
35374
|
};
|
|
34838
35375
|
}
|
|
34839
35376
|
//#endregion
|
|
35377
|
+
//#region src/lib/runtime-profile.ts
|
|
35378
|
+
var RuntimeProfilePrerequisiteError = class extends Error {
|
|
35379
|
+
constructor(profileName, missingEnv, missingTools) {
|
|
35380
|
+
const parts = [missingEnv.length > 0 ? `missing env: ${missingEnv.join(", ")}` : null, missingTools.length > 0 ? `missing tools: ${missingTools.join(", ")}` : null].filter(Boolean);
|
|
35381
|
+
super(`Runtime profile "${profileName}" prerequisites are not satisfied: ${parts.join("; ")}`);
|
|
35382
|
+
this.profileName = profileName;
|
|
35383
|
+
this.missingEnv = missingEnv;
|
|
35384
|
+
this.missingTools = missingTools;
|
|
35385
|
+
this.name = "RuntimeProfilePrerequisiteError";
|
|
35386
|
+
}
|
|
35387
|
+
};
|
|
35388
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
35389
|
+
async function resolveRuntimeProfile(options) {
|
|
35390
|
+
const profile = UUID_RE.test(options.profile) ? await options.agent.runtimeProfiles.get(options.profile) : await resolveProfileByName(options);
|
|
35391
|
+
if (options.teamId && profile.teamId !== options.teamId) throw new Error(`Runtime profile "${options.profile}" belongs to team ${profile.teamId}, not ${options.teamId}.`);
|
|
35392
|
+
return {
|
|
35393
|
+
id: profile.id,
|
|
35394
|
+
name: profile.name,
|
|
35395
|
+
teamId: profile.teamId,
|
|
35396
|
+
provider: profile.provider,
|
|
35397
|
+
model: profile.model,
|
|
35398
|
+
leaseTtlSec: profile.leaseTtlSec,
|
|
35399
|
+
heartbeatIntervalMs: profile.heartbeatIntervalMs,
|
|
35400
|
+
maxBatchSize: profile.maxBatchSize,
|
|
35401
|
+
sessionTtlSec: profile.sessionTtlSec,
|
|
35402
|
+
workspaceTtlSec: profile.workspaceTtlSec,
|
|
35403
|
+
requiredEnv: profile.requiredEnv,
|
|
35404
|
+
requiredTools: profile.requiredTools,
|
|
35405
|
+
sandboxConfig: profile.sandbox,
|
|
35406
|
+
mountPath: resolve(options.cwd),
|
|
35407
|
+
source: `runtime-profile:${profile.id}`
|
|
35408
|
+
};
|
|
35409
|
+
}
|
|
35410
|
+
function validateRuntimeProfilePrerequisites(profile, env, pathValue) {
|
|
35411
|
+
const missingEnv = profile.requiredEnv.filter((name) => !env[name]);
|
|
35412
|
+
const missingTools = profile.requiredTools.filter((tool) => !isExecutableOnPath(tool, pathValue));
|
|
35413
|
+
if (missingEnv.length > 0 || missingTools.length > 0) throw new RuntimeProfilePrerequisiteError(profile.name, missingEnv, missingTools);
|
|
35414
|
+
}
|
|
35415
|
+
function resolveProfileWarmSessionTtlSec(profile) {
|
|
35416
|
+
return Math.min(profile.sessionTtlSec, profile.workspaceTtlSec);
|
|
35417
|
+
}
|
|
35418
|
+
async function resolveProfileByName(options) {
|
|
35419
|
+
if (!options.teamId) throw new Error(`Runtime profile name "${options.profile}" requires --team. Use a profile UUID when running without a team-scoped list.`);
|
|
35420
|
+
const matches = (await options.agent.runtimeProfiles.list({ teamId: options.teamId })).items.filter((item) => item.name === options.profile);
|
|
35421
|
+
if (matches.length === 0) throw new Error(`Runtime profile "${options.profile}" was not found in team ${options.teamId}.`);
|
|
35422
|
+
if (matches.length > 1) throw new Error(`Runtime profile name "${options.profile}" is ambiguous in team ${options.teamId}. Use the profile UUID instead.`);
|
|
35423
|
+
return matches[0];
|
|
35424
|
+
}
|
|
35425
|
+
function isExecutableOnPath(tool, pathValue) {
|
|
35426
|
+
if (tool.includes("/")) return isExecutable(isAbsolute(tool) ? tool : resolve(process.cwd(), tool));
|
|
35427
|
+
for (const dir of (pathValue ?? "").split(delimiter)) {
|
|
35428
|
+
if (!dir) continue;
|
|
35429
|
+
if (isExecutable(resolve(dir, tool))) return true;
|
|
35430
|
+
}
|
|
35431
|
+
return false;
|
|
35432
|
+
}
|
|
35433
|
+
function isExecutable(path) {
|
|
35434
|
+
try {
|
|
35435
|
+
accessSync(path, constants.X_OK);
|
|
35436
|
+
return true;
|
|
35437
|
+
} catch {
|
|
35438
|
+
return false;
|
|
35439
|
+
}
|
|
35440
|
+
}
|
|
35441
|
+
//#endregion
|
|
34840
35442
|
//#region src/lib/sandbox.ts
|
|
34841
35443
|
function resolveSandbox(startDir, explicitPath) {
|
|
34842
35444
|
const path = explicitPath ? isAbsolute(explicitPath) ? explicitPath : resolve(startDir, explicitPath) : findUp(startDir, "sandbox.json");
|
|
@@ -34865,6 +35467,38 @@ function findUp(startDir, filename) {
|
|
|
34865
35467
|
}
|
|
34866
35468
|
}
|
|
34867
35469
|
//#endregion
|
|
35470
|
+
//#region src/lib/shutdown-signal.ts
|
|
35471
|
+
function installShutdownSignalHandlers(opts) {
|
|
35472
|
+
const proc = opts.proc ?? process;
|
|
35473
|
+
let drainingSignal = null;
|
|
35474
|
+
const onSignal = (signal) => {
|
|
35475
|
+
if (drainingSignal) {
|
|
35476
|
+
proc.stderr.write(`[agent-daemon] ${signal} received while already draining from ${drainingSignal}; waiting for cleanup.\n`);
|
|
35477
|
+
proc.exitCode = signalExitCode(signal);
|
|
35478
|
+
return;
|
|
35479
|
+
}
|
|
35480
|
+
drainingSignal = signal;
|
|
35481
|
+
proc.exitCode = signalExitCode(signal);
|
|
35482
|
+
try {
|
|
35483
|
+
opts.logDrain(signal);
|
|
35484
|
+
} catch (err) {
|
|
35485
|
+
proc.stderr.write(`[agent-daemon] failed to log ${signal}: ` + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
35486
|
+
}
|
|
35487
|
+
opts.drain(signal);
|
|
35488
|
+
};
|
|
35489
|
+
const handleSigint = () => onSignal("SIGINT");
|
|
35490
|
+
const handleSigterm = () => onSignal("SIGTERM");
|
|
35491
|
+
proc.on("SIGINT", handleSigint);
|
|
35492
|
+
proc.on("SIGTERM", handleSigterm);
|
|
35493
|
+
return { dispose: () => {
|
|
35494
|
+
proc.off("SIGINT", handleSigint);
|
|
35495
|
+
proc.off("SIGTERM", handleSigterm);
|
|
35496
|
+
} };
|
|
35497
|
+
}
|
|
35498
|
+
function signalExitCode(signal) {
|
|
35499
|
+
return signal === "SIGINT" ? 130 : 143;
|
|
35500
|
+
}
|
|
35501
|
+
//#endregion
|
|
34868
35502
|
//#region src/lib/state-dir.ts
|
|
34869
35503
|
function ensureDaemonStateDirs(mountPath) {
|
|
34870
35504
|
const rootDir = join(mountPath, ".moltnet", "d");
|
|
@@ -34914,7 +35548,8 @@ async function runPolling(opts) {
|
|
|
34914
35548
|
"poll-interval-ms": { type: "string" },
|
|
34915
35549
|
"max-poll-interval-ms": { type: "string" },
|
|
34916
35550
|
"list-limit": { type: "string" },
|
|
34917
|
-
sandbox: { type: "string" }
|
|
35551
|
+
sandbox: { type: "string" },
|
|
35552
|
+
profile: { type: "string" }
|
|
34918
35553
|
}
|
|
34919
35554
|
});
|
|
34920
35555
|
if (!values.team) {
|
|
@@ -34934,7 +35569,7 @@ async function runPolling(opts) {
|
|
|
34934
35569
|
const diaryIds = parseCsv(values["diary-ids"]);
|
|
34935
35570
|
let common;
|
|
34936
35571
|
try {
|
|
34937
|
-
common = parseCommonOptions(values);
|
|
35572
|
+
common = parseCommonOptions(values, { requireProviderModel: !values.profile });
|
|
34938
35573
|
} catch (err) {
|
|
34939
35574
|
if (err instanceof MissingRequiredOptionError) {
|
|
34940
35575
|
console.error(`${err.message}\n`);
|
|
@@ -34946,15 +35581,45 @@ async function runPolling(opts) {
|
|
|
34946
35581
|
const pollIntervalMs = optionalPositiveInt(values["poll-interval-ms"], "poll-interval-ms", 2e3);
|
|
34947
35582
|
const maxPollIntervalMs = optionalPositiveInt(values["max-poll-interval-ms"], "max-poll-interval-ms", 3e4);
|
|
34948
35583
|
const listLimit = optionalPositiveInt(values["list-limit"], "list-limit", 10);
|
|
35584
|
+
if (values.profile && values.sandbox) {
|
|
35585
|
+
console.error(`[${opts.modeLabel}] Cannot use --sandbox with --profile. Remote runtime profiles define sandbox policy.`);
|
|
35586
|
+
return 1;
|
|
35587
|
+
}
|
|
34949
35588
|
if (taskTypes.length === 0) console.error(`[${opts.modeLabel}] --task-types is empty — daemon will accept any registered type. Pass an explicit list to limit scope (e.g. --task-types fulfill_brief).`);
|
|
34950
|
-
const sandbox = resolveSandbox(process.cwd(), values.sandbox);
|
|
34951
35589
|
const cfg = loadConfig();
|
|
35590
|
+
const ctx = await resolveAgentContext(common.agent);
|
|
35591
|
+
const profile = values.profile ? await resolveRuntimeProfile({
|
|
35592
|
+
agent: ctx.agent,
|
|
35593
|
+
profile: values.profile,
|
|
35594
|
+
teamId,
|
|
35595
|
+
cwd: process.cwd()
|
|
35596
|
+
}) : null;
|
|
35597
|
+
if (profile) {
|
|
35598
|
+
validateRuntimeProfilePrerequisites(profile, cfg.profilePrerequisiteEnv, cfg.profilePrerequisitePath);
|
|
35599
|
+
common = parseCommonOptions(values, {
|
|
35600
|
+
requireProviderModel: false,
|
|
35601
|
+
runtimeDefaults: {
|
|
35602
|
+
leaseTtlSec: profile.leaseTtlSec,
|
|
35603
|
+
heartbeatIntervalMs: profile.heartbeatIntervalMs,
|
|
35604
|
+
maxBatchSize: profile.maxBatchSize,
|
|
35605
|
+
warmSessionTtlSec: resolveProfileWarmSessionTtlSec(profile)
|
|
35606
|
+
}
|
|
35607
|
+
});
|
|
35608
|
+
}
|
|
35609
|
+
const provider = profile?.provider ?? common.provider;
|
|
35610
|
+
const model = profile?.model ?? common.model;
|
|
35611
|
+
if (!provider || !model) throw new Error("provider/model missing after runtime profile resolution");
|
|
35612
|
+
const sandbox = profile ? {
|
|
35613
|
+
config: profile.sandboxConfig,
|
|
35614
|
+
rootDir: profile.mountPath,
|
|
35615
|
+
path: profile.source
|
|
35616
|
+
} : resolveSandbox(process.cwd(), values.sandbox);
|
|
34952
35617
|
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
34953
35618
|
const slotRegistry = new DaemonSlotRegistry(resolveDaemonStateStorageConfig(stateDirs.registryDbPath, cfg.agentDaemonStateDatabaseUrl));
|
|
34954
35619
|
const slotIdentity = {
|
|
34955
35620
|
agentName: common.agent,
|
|
34956
|
-
provider
|
|
34957
|
-
model
|
|
35621
|
+
provider,
|
|
35622
|
+
model
|
|
34958
35623
|
};
|
|
34959
35624
|
const mainRepo = findMainWorktree();
|
|
34960
35625
|
const executionPlans = createExecutionPlanCache({
|
|
@@ -34963,7 +35628,6 @@ async function runPolling(opts) {
|
|
|
34963
35628
|
warmSessionTtlSec: common.warmSessionTtlSec,
|
|
34964
35629
|
slotRegistry
|
|
34965
35630
|
});
|
|
34966
|
-
const ctx = await resolveAgentContext(common.agent);
|
|
34967
35631
|
const otelShutdown = await initWorkerOtel({
|
|
34968
35632
|
serviceName: opts.serviceName,
|
|
34969
35633
|
agentDir: ctx.agentDir,
|
|
@@ -34971,8 +35635,9 @@ async function runPolling(opts) {
|
|
|
34971
35635
|
resourceAttributes: {
|
|
34972
35636
|
"moltnet.team.id": teamId,
|
|
34973
35637
|
"moltnet.agent.name": common.agent,
|
|
34974
|
-
"moltnet.llm.provider":
|
|
34975
|
-
"moltnet.llm.model":
|
|
35638
|
+
"moltnet.llm.provider": provider,
|
|
35639
|
+
"moltnet.llm.model": model,
|
|
35640
|
+
...profile ? { "moltnet.daemon_profile.id": profile.id } : {}
|
|
34976
35641
|
}
|
|
34977
35642
|
});
|
|
34978
35643
|
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
@@ -34983,48 +35648,82 @@ async function runPolling(opts) {
|
|
|
34983
35648
|
mode: opts.modeLabel,
|
|
34984
35649
|
agent: common.agent,
|
|
34985
35650
|
teamId,
|
|
34986
|
-
provider
|
|
34987
|
-
model
|
|
35651
|
+
provider,
|
|
35652
|
+
model,
|
|
35653
|
+
...profile ? {
|
|
35654
|
+
daemonProfileId: profile.id,
|
|
35655
|
+
daemonProfileName: profile.name
|
|
35656
|
+
} : {}
|
|
34988
35657
|
});
|
|
34989
35658
|
const abort = new AbortController();
|
|
34990
35659
|
let runtime = null;
|
|
34991
|
-
|
|
34992
|
-
|
|
34993
|
-
|
|
34994
|
-
|
|
34995
|
-
|
|
34996
|
-
|
|
34997
|
-
|
|
35660
|
+
let active = null;
|
|
35661
|
+
const signalHandlers = installShutdownSignalHandlers({
|
|
35662
|
+
logDrain: (signal) => {
|
|
35663
|
+
rootLogger.warn({ signal }, "agent-daemon.draining");
|
|
35664
|
+
},
|
|
35665
|
+
drain: (signal) => {
|
|
35666
|
+
abort.abort();
|
|
35667
|
+
runtime?.stop(`agent-daemon received ${signal}`);
|
|
35668
|
+
if (active === null) return;
|
|
35669
|
+
const { taskId, attemptN } = active;
|
|
35670
|
+
ctx.agent.tasks.abortAttempt(taskId, attemptN, { reason: `runner_${signal.toLowerCase()}` }).catch((err) => {
|
|
35671
|
+
try {
|
|
35672
|
+
rootLogger.warn({
|
|
35673
|
+
err: err instanceof Error ? err.message : String(err),
|
|
35674
|
+
taskId,
|
|
35675
|
+
attemptN
|
|
35676
|
+
}, "agent-daemon.abort_on_signal_failed");
|
|
35677
|
+
} catch {}
|
|
35678
|
+
});
|
|
35679
|
+
}
|
|
35680
|
+
});
|
|
34998
35681
|
rootLogger.info({
|
|
34999
35682
|
sandbox: sandbox.path,
|
|
35000
35683
|
taskTypes: taskTypes.length > 0 ? taskTypes : ["*"],
|
|
35001
35684
|
diaryIds: diaryIds.length > 0 ? diaryIds : ["*"],
|
|
35002
35685
|
leaseTtlSec: common.leaseTtlSec,
|
|
35003
35686
|
heartbeatIntervalMs: common.heartbeatIntervalMs,
|
|
35687
|
+
warmSessionTtlSec: common.warmSessionTtlSec,
|
|
35004
35688
|
pollIntervalMs,
|
|
35005
|
-
maxPollIntervalMs
|
|
35689
|
+
maxPollIntervalMs,
|
|
35690
|
+
...profile ? {
|
|
35691
|
+
profileId: profile.id,
|
|
35692
|
+
profileSessionTtlSec: profile.sessionTtlSec,
|
|
35693
|
+
profileWorkspaceTtlSec: profile.workspaceTtlSec
|
|
35694
|
+
} : {}
|
|
35006
35695
|
}, "agent-daemon.starting");
|
|
35007
35696
|
const outputs = [];
|
|
35008
35697
|
try {
|
|
35009
|
-
const
|
|
35698
|
+
const rawExecuteTask = createPiTaskExecutor({
|
|
35010
35699
|
agentName: common.agent,
|
|
35011
35700
|
mountPath: sandbox.rootDir,
|
|
35012
|
-
provider
|
|
35013
|
-
model
|
|
35701
|
+
provider,
|
|
35702
|
+
model,
|
|
35014
35703
|
sandboxConfig: sandbox.config,
|
|
35015
35704
|
makeExecutionPlan: (claimedTask) => executionPlans.getOrCreate(claimedTask),
|
|
35016
35705
|
makeOnTurnEvent: makeTurnEventHandlerFactory(rootLogger),
|
|
35017
35706
|
maxTurns: common.maxTurns,
|
|
35018
35707
|
maxBashTimeouts: common.maxBashTimeouts
|
|
35019
35708
|
});
|
|
35709
|
+
const executeTask = async (claimedTask, reporter) => {
|
|
35710
|
+
active = {
|
|
35711
|
+
taskId: claimedTask.task.id,
|
|
35712
|
+
attemptN: claimedTask.attemptN
|
|
35713
|
+
};
|
|
35714
|
+
try {
|
|
35715
|
+
return await rawExecuteTask(claimedTask, reporter);
|
|
35716
|
+
} finally {
|
|
35717
|
+
active = null;
|
|
35718
|
+
}
|
|
35719
|
+
};
|
|
35020
35720
|
runtime = new AgentRuntime({
|
|
35021
35721
|
logger: rootLogger,
|
|
35022
35722
|
source: new PollingApiTaskSource({
|
|
35023
35723
|
agent: ctx.agent,
|
|
35024
35724
|
teamId,
|
|
35025
35725
|
taskTypes: taskTypes.length > 0 ? taskTypes : void 0,
|
|
35026
|
-
|
|
35027
|
-
model: common.model.toLowerCase(),
|
|
35726
|
+
...profile ? { profileId: profile.id } : {},
|
|
35028
35727
|
diaryIds: diaryIds.length > 0 ? diaryIds : void 0,
|
|
35029
35728
|
leaseTtlSec: common.leaseTtlSec,
|
|
35030
35729
|
listLimit,
|
|
@@ -35171,6 +35870,7 @@ async function runPolling(opts) {
|
|
|
35171
35870
|
rootLogger.info({ processed: drained.length }, "agent-daemon.drained");
|
|
35172
35871
|
return drained.some((o) => o.status !== "completed") ? 1 : 0;
|
|
35173
35872
|
} finally {
|
|
35873
|
+
signalHandlers.dispose();
|
|
35174
35874
|
await slotRegistry.close();
|
|
35175
35875
|
await otelShutdown();
|
|
35176
35876
|
await shutdownLogger();
|
|
@@ -35215,7 +35915,8 @@ async function runOnce(argv) {
|
|
|
35215
35915
|
type: "string",
|
|
35216
35916
|
short: "t"
|
|
35217
35917
|
},
|
|
35218
|
-
sandbox: { type: "string" }
|
|
35918
|
+
sandbox: { type: "string" },
|
|
35919
|
+
profile: { type: "string" }
|
|
35219
35920
|
}
|
|
35220
35921
|
});
|
|
35221
35922
|
if (!values["task-id"]) {
|
|
@@ -35226,7 +35927,7 @@ async function runOnce(argv) {
|
|
|
35226
35927
|
const taskId = values["task-id"];
|
|
35227
35928
|
let opts;
|
|
35228
35929
|
try {
|
|
35229
|
-
opts = parseCommonOptions(values);
|
|
35930
|
+
opts = parseCommonOptions(values, { requireProviderModel: !values.profile });
|
|
35230
35931
|
} catch (err) {
|
|
35231
35932
|
if (err instanceof MissingRequiredOptionError) {
|
|
35232
35933
|
console.error(`${err.message}\n`);
|
|
@@ -35235,14 +35936,43 @@ async function runOnce(argv) {
|
|
|
35235
35936
|
}
|
|
35236
35937
|
throw err;
|
|
35237
35938
|
}
|
|
35238
|
-
|
|
35939
|
+
if (values.profile && values.sandbox) {
|
|
35940
|
+
console.error("Cannot use --sandbox with --profile. Remote runtime profiles define sandbox policy.");
|
|
35941
|
+
return 1;
|
|
35942
|
+
}
|
|
35239
35943
|
const cfg = loadConfig();
|
|
35944
|
+
const ctx = await resolveAgentContext(opts.agent);
|
|
35945
|
+
const profile = values.profile ? await resolveRuntimeProfile({
|
|
35946
|
+
agent: ctx.agent,
|
|
35947
|
+
profile: values.profile,
|
|
35948
|
+
cwd: process.cwd()
|
|
35949
|
+
}) : null;
|
|
35950
|
+
if (profile) {
|
|
35951
|
+
validateRuntimeProfilePrerequisites(profile, cfg.profilePrerequisiteEnv, cfg.profilePrerequisitePath);
|
|
35952
|
+
opts = parseCommonOptions(values, {
|
|
35953
|
+
requireProviderModel: false,
|
|
35954
|
+
runtimeDefaults: {
|
|
35955
|
+
leaseTtlSec: profile.leaseTtlSec,
|
|
35956
|
+
heartbeatIntervalMs: profile.heartbeatIntervalMs,
|
|
35957
|
+
maxBatchSize: profile.maxBatchSize,
|
|
35958
|
+
warmSessionTtlSec: resolveProfileWarmSessionTtlSec(profile)
|
|
35959
|
+
}
|
|
35960
|
+
});
|
|
35961
|
+
}
|
|
35962
|
+
const provider = profile?.provider ?? opts.provider;
|
|
35963
|
+
const model = profile?.model ?? opts.model;
|
|
35964
|
+
if (!provider || !model) throw new Error("provider/model missing after runtime profile resolution");
|
|
35965
|
+
const sandbox = profile ? {
|
|
35966
|
+
config: profile.sandboxConfig,
|
|
35967
|
+
rootDir: profile.mountPath,
|
|
35968
|
+
path: profile.source
|
|
35969
|
+
} : resolveSandbox(process.cwd(), values.sandbox);
|
|
35240
35970
|
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
35241
35971
|
const slotRegistry = new DaemonSlotRegistry(resolveDaemonStateStorageConfig(stateDirs.registryDbPath, cfg.agentDaemonStateDatabaseUrl));
|
|
35242
35972
|
const slotIdentity = {
|
|
35243
35973
|
agentName: opts.agent,
|
|
35244
|
-
provider
|
|
35245
|
-
model
|
|
35974
|
+
provider,
|
|
35975
|
+
model
|
|
35246
35976
|
};
|
|
35247
35977
|
const mainRepo = findMainWorktree();
|
|
35248
35978
|
const executionPlans = createExecutionPlanCache({
|
|
@@ -35251,7 +35981,6 @@ async function runOnce(argv) {
|
|
|
35251
35981
|
warmSessionTtlSec: opts.warmSessionTtlSec,
|
|
35252
35982
|
slotRegistry
|
|
35253
35983
|
});
|
|
35254
|
-
const ctx = await resolveAgentContext(opts.agent);
|
|
35255
35984
|
const otelShutdown = await initWorkerOtel({
|
|
35256
35985
|
serviceName: "moltnet.agent-daemon.once",
|
|
35257
35986
|
agentDir: ctx.agentDir,
|
|
@@ -35259,8 +35988,9 @@ async function runOnce(argv) {
|
|
|
35259
35988
|
resourceAttributes: {
|
|
35260
35989
|
"moltnet.task.id": taskId,
|
|
35261
35990
|
"moltnet.agent.name": opts.agent,
|
|
35262
|
-
"moltnet.llm.provider":
|
|
35263
|
-
"moltnet.llm.model":
|
|
35991
|
+
"moltnet.llm.provider": provider,
|
|
35992
|
+
"moltnet.llm.model": model,
|
|
35993
|
+
...profile ? { "moltnet.daemon_profile.id": profile.id } : {}
|
|
35264
35994
|
}
|
|
35265
35995
|
});
|
|
35266
35996
|
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
@@ -35270,39 +36000,57 @@ async function runOnce(argv) {
|
|
|
35270
36000
|
const rootLogger = logger.child({
|
|
35271
36001
|
mode: "once",
|
|
35272
36002
|
agent: opts.agent,
|
|
35273
|
-
provider
|
|
35274
|
-
model
|
|
36003
|
+
provider,
|
|
36004
|
+
model,
|
|
36005
|
+
...profile ? {
|
|
36006
|
+
daemonProfileId: profile.id,
|
|
36007
|
+
daemonProfileName: profile.name
|
|
36008
|
+
} : {}
|
|
35275
36009
|
});
|
|
35276
36010
|
rootLogger.info({
|
|
35277
36011
|
sandbox: sandbox.path,
|
|
35278
|
-
taskId
|
|
36012
|
+
taskId,
|
|
36013
|
+
leaseTtlSec: opts.leaseTtlSec,
|
|
36014
|
+
heartbeatIntervalMs: opts.heartbeatIntervalMs,
|
|
36015
|
+
warmSessionTtlSec: opts.warmSessionTtlSec,
|
|
36016
|
+
...profile ? {
|
|
36017
|
+
profileId: profile.id,
|
|
36018
|
+
profileSessionTtlSec: profile.sessionTtlSec,
|
|
36019
|
+
profileWorkspaceTtlSec: profile.workspaceTtlSec
|
|
36020
|
+
} : {}
|
|
35279
36021
|
}, "agent-daemon.starting");
|
|
35280
36022
|
let runtime = null;
|
|
35281
|
-
|
|
35282
|
-
|
|
35283
|
-
|
|
35284
|
-
taskId
|
|
35285
|
-
}, "agent-daemon.draining");
|
|
35286
|
-
runtime?.stop();
|
|
35287
|
-
ctx.agent.tasks.cancel(taskId, { reason: `runner_${sig.toLowerCase()}` }).catch((err) => {
|
|
36023
|
+
let activeAttemptN = null;
|
|
36024
|
+
const signalHandlers = installShutdownSignalHandlers({
|
|
36025
|
+
logDrain: (signal) => {
|
|
35288
36026
|
rootLogger.warn({
|
|
35289
|
-
|
|
36027
|
+
signal,
|
|
35290
36028
|
taskId
|
|
35291
|
-
}, "agent-daemon.
|
|
35292
|
-
}
|
|
35293
|
-
|
|
35294
|
-
|
|
35295
|
-
|
|
35296
|
-
|
|
35297
|
-
|
|
35298
|
-
|
|
36029
|
+
}, "agent-daemon.draining");
|
|
36030
|
+
},
|
|
36031
|
+
drain: (signal) => {
|
|
36032
|
+
runtime?.stop(`agent-daemon received ${signal}`);
|
|
36033
|
+
if (activeAttemptN === null) return;
|
|
36034
|
+
const attemptN = activeAttemptN;
|
|
36035
|
+
ctx.agent.tasks.abortAttempt(taskId, attemptN, { reason: `runner_${signal.toLowerCase()}` }).catch((err) => {
|
|
36036
|
+
try {
|
|
36037
|
+
rootLogger.warn({
|
|
36038
|
+
err: err instanceof Error ? err.message : String(err),
|
|
36039
|
+
taskId,
|
|
36040
|
+
attemptN
|
|
36041
|
+
}, "agent-daemon.abort_on_signal_failed");
|
|
36042
|
+
} catch (logErr) {
|
|
36043
|
+
process.stderr.write(`[agent-daemon] failed to log abort error: ` + (logErr instanceof Error ? logErr.message : String(logErr)) + "\n");
|
|
36044
|
+
}
|
|
36045
|
+
});
|
|
36046
|
+
}
|
|
35299
36047
|
});
|
|
35300
36048
|
try {
|
|
35301
36049
|
const rawExecuteTask = createPiTaskExecutor({
|
|
35302
36050
|
agentName: opts.agent,
|
|
35303
36051
|
mountPath: sandbox.rootDir,
|
|
35304
|
-
provider
|
|
35305
|
-
model
|
|
36052
|
+
provider,
|
|
36053
|
+
model,
|
|
35306
36054
|
sandboxConfig: sandbox.config,
|
|
35307
36055
|
makeExecutionPlan: (claimedTask) => executionPlans.getOrCreate(claimedTask),
|
|
35308
36056
|
onTurnEvent: makeTurnEventHandler(rootLogger, { taskId }),
|
|
@@ -35363,9 +36111,11 @@ async function runOnce(argv) {
|
|
|
35363
36111
|
lastAttemptN: claimedTask.attemptN,
|
|
35364
36112
|
ttlSec: opts.warmSessionTtlSec
|
|
35365
36113
|
});
|
|
36114
|
+
activeAttemptN = claimedTask.attemptN;
|
|
35366
36115
|
try {
|
|
35367
36116
|
return await rawExecuteTask(claimedTask, reporter);
|
|
35368
36117
|
} finally {
|
|
36118
|
+
activeAttemptN = null;
|
|
35369
36119
|
executionPlans.delete(claimedTask);
|
|
35370
36120
|
if (executionPlan.slotKey) await slotRegistry.finishSlot(slotIdentity, executionPlan.slotKey, opts.warmSessionTtlSec, executionPlan.sessionPersistence ? resolveLatestPiSessionPath(executionPlan.sessionPersistence.sessionDir) : null);
|
|
35371
36121
|
}
|
|
@@ -35379,7 +36129,8 @@ async function runOnce(argv) {
|
|
|
35379
36129
|
source: new ApiTaskSource({
|
|
35380
36130
|
agent: ctx.agent,
|
|
35381
36131
|
taskId,
|
|
35382
|
-
leaseTtlSec: opts.leaseTtlSec
|
|
36132
|
+
leaseTtlSec: opts.leaseTtlSec,
|
|
36133
|
+
...profile ? { profileId: profile.id } : {}
|
|
35383
36134
|
}),
|
|
35384
36135
|
makeReporter: () => new ApiTaskReporter({
|
|
35385
36136
|
tasks: ctx.agent.tasks,
|
|
@@ -35408,6 +36159,7 @@ async function runOnce(argv) {
|
|
|
35408
36159
|
console.log(JSON.stringify(output, null, 2));
|
|
35409
36160
|
return output.status === "completed" ? 0 : 1;
|
|
35410
36161
|
} finally {
|
|
36162
|
+
signalHandlers.dispose();
|
|
35411
36163
|
await slotRegistry.close();
|
|
35412
36164
|
await otelShutdown();
|
|
35413
36165
|
await shutdownLogger();
|