@themoltnet/agent-daemon 0.16.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 +852 -238
- package/package.json +9 -8
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
|
|
@@ -4273,18 +4273,120 @@ var TaskContext = _Array_(_Object_({
|
|
|
4273
4273
|
maxItems: 5
|
|
4274
4274
|
});
|
|
4275
4275
|
//#endregion
|
|
4276
|
-
//#region ../../libs/tasks/src/
|
|
4277
|
-
|
|
4276
|
+
//#region ../../libs/tasks/src/rubric.ts
|
|
4277
|
+
/**
|
|
4278
|
+
* Rubric — structured acceptance criteria used by judgment tasks.
|
|
4279
|
+
*
|
|
4280
|
+
* Phase 1 (this PR): rubrics are embedded in task inputs. Their integrity
|
|
4281
|
+
* is pinned via the task's `input_cid` (which covers the whole input,
|
|
4282
|
+
* including the inline rubric). No separate storage, no CRUD.
|
|
4283
|
+
*
|
|
4284
|
+
* Phase 2 (see #881): rubrics become a first-class resource with their
|
|
4285
|
+
* own signed rows and CIDv1 lookup. The schema below is designed to
|
|
4286
|
+
* carry forward unchanged — only storage and addressing differ.
|
|
4287
|
+
*
|
|
4288
|
+
* Until Phase 2 lands, `rubricId` + `version` + `contentHash` are
|
|
4289
|
+
* informational fields the author fills in; no uniqueness is enforced.
|
|
4290
|
+
* `contentHash` is optional in Phase 1 because the *task*'s input_cid
|
|
4291
|
+
* is the authoritative commitment.
|
|
4292
|
+
*/
|
|
4293
|
+
/**
|
|
4294
|
+
* How a judge must score a single criterion.
|
|
4295
|
+
*
|
|
4296
|
+
* - `llm_score`: 0..1 continuous, `rationale` required. Smooths failures
|
|
4297
|
+
* into the gradient — use `llm_checklist` instead for properties where
|
|
4298
|
+
* a single failure is a real failure (grounding, faithfulness).
|
|
4299
|
+
* - `llm_checklist`: judge enumerates per-claim assertions with
|
|
4300
|
+
* `{passed, evidence}`. The criterion's numeric `score` is derived:
|
|
4301
|
+
* `1` iff every assertion passes, else `0`. Per-claim evidence is the
|
|
4302
|
+
* dataset for cluster-analysis of failure modes. See #999.
|
|
4303
|
+
* - `boolean`: 0 or 1, `rationale` optional.
|
|
4304
|
+
* - `deterministic_signature_check`: judge runs a signature check;
|
|
4305
|
+
* result is 0 or 1. No LLM discretion.
|
|
4306
|
+
* - `deterministic_coverage_check`: every referenced source entry
|
|
4307
|
+
* appears in the rendered output; 0 or 1.
|
|
4308
|
+
*/
|
|
4309
|
+
var RubricScoringMode = Union([
|
|
4310
|
+
Literal("llm_score"),
|
|
4311
|
+
Literal("llm_checklist"),
|
|
4312
|
+
Literal("boolean"),
|
|
4313
|
+
Literal("deterministic_signature_check"),
|
|
4314
|
+
Literal("deterministic_coverage_check")
|
|
4315
|
+
], { $id: "RubricScoringMode" });
|
|
4316
|
+
/**
|
|
4317
|
+
* One binary check produced by an `llm_checklist`-mode criterion.
|
|
4318
|
+
*
|
|
4319
|
+
* `evidence` is REQUIRED for both PASS and FAIL — agentskills.io grading
|
|
4320
|
+
* principle: \"Don't give the benefit of the doubt.\" A PASS without
|
|
4321
|
+
* concrete evidence (a quoted span, an entry id, a source location)
|
|
4322
|
+
* cannot be audited. A FAIL without evidence cannot be clustered into
|
|
4323
|
+
* structural fixes. The same shape is reused by `judge-eval-variant`
|
|
4324
|
+
* (#943) so tooling, dashboards, and analysis stay uniform.
|
|
4325
|
+
*/
|
|
4326
|
+
var AssertionResult = _Object_({
|
|
4327
|
+
id: String$1({ minLength: 1 }),
|
|
4328
|
+
text: String$1({ minLength: 1 }),
|
|
4329
|
+
passed: Boolean$1(),
|
|
4330
|
+
evidence: String$1({ minLength: 1 })
|
|
4331
|
+
}, {
|
|
4332
|
+
$id: "AssertionResult",
|
|
4333
|
+
additionalProperties: false
|
|
4334
|
+
});
|
|
4335
|
+
var RubricCriterion = _Object_({
|
|
4336
|
+
id: String$1({ minLength: 1 }),
|
|
4337
|
+
description: String$1({ minLength: 1 }),
|
|
4338
|
+
weight: Number$1({
|
|
4339
|
+
minimum: 0,
|
|
4340
|
+
maximum: 1
|
|
4341
|
+
}),
|
|
4342
|
+
scoring: RubricScoringMode
|
|
4343
|
+
}, {
|
|
4344
|
+
$id: "RubricCriterion",
|
|
4345
|
+
additionalProperties: false
|
|
4346
|
+
});
|
|
4347
|
+
/**
|
|
4348
|
+
* A complete rubric. Same shape used in Phase 1 (inline) and Phase 2
|
|
4349
|
+
* (stored row `body`); only the addressing mechanism differs.
|
|
4350
|
+
*/
|
|
4351
|
+
var Rubric = _Object_({
|
|
4352
|
+
rubricId: String$1({ minLength: 1 }),
|
|
4353
|
+
version: String$1({ minLength: 1 }),
|
|
4354
|
+
preamble: Optional(String$1()),
|
|
4355
|
+
criteria: _Array_(RubricCriterion, { minItems: 1 }),
|
|
4356
|
+
scope: Optional(String$1()),
|
|
4357
|
+
contentHash: Optional(String$1())
|
|
4358
|
+
}, {
|
|
4359
|
+
$id: "Rubric",
|
|
4360
|
+
additionalProperties: false
|
|
4361
|
+
});
|
|
4362
|
+
/**
|
|
4363
|
+
* Verify rubric criteria weights sum to 1.0 within floating-point tolerance.
|
|
4364
|
+
* The schema constrains each weight to [0,1] but can't express a cross-field
|
|
4365
|
+
* sum constraint, so this is enforced programmatically by callers that
|
|
4366
|
+
* accept rubrics (task input validators, server-side task creation).
|
|
4367
|
+
*
|
|
4368
|
+
* Returns null when valid; otherwise an error message suitable for surfacing
|
|
4369
|
+
* to the caller. Tolerance is 1e-6 to accommodate JSON round-tripping of
|
|
4370
|
+
* decimal fractions (e.g. 0.1 + 0.2 + 0.3 + 0.4 ≠ 1.0 exactly).
|
|
4371
|
+
*/
|
|
4372
|
+
function validateRubricWeights(rubric) {
|
|
4373
|
+
const sum = rubric.criteria.reduce((acc, c) => acc + c.weight, 0);
|
|
4374
|
+
if (Math.abs(sum - 1) > 1e-6) return `Rubric weights must sum to 1.0 (got ${sum.toFixed(6)})`;
|
|
4375
|
+
return null;
|
|
4376
|
+
}
|
|
4377
|
+
//#endregion
|
|
4378
|
+
//#region ../../libs/tasks/src/runtime-profiles.ts
|
|
4379
|
+
var RuntimeProfileName = String$1({
|
|
4278
4380
|
minLength: 1,
|
|
4279
4381
|
maxLength: 100,
|
|
4280
4382
|
pattern: "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$"
|
|
4281
4383
|
});
|
|
4282
|
-
var
|
|
4384
|
+
var RuntimeProfileEnvName = String$1({
|
|
4283
4385
|
minLength: 1,
|
|
4284
4386
|
maxLength: 128,
|
|
4285
4387
|
pattern: "^[A-Z_][A-Z0-9_]*$"
|
|
4286
4388
|
});
|
|
4287
|
-
var
|
|
4389
|
+
var RuntimeProfileToolName = String$1({
|
|
4288
4390
|
minLength: 1,
|
|
4289
4391
|
maxLength: 128,
|
|
4290
4392
|
pattern: "^[a-zA-Z0-9._/-]+$"
|
|
@@ -4297,7 +4399,7 @@ var SandboxResumeCommandWhenSchema = _Object_({ workspaceMode: Optional(_Array_(
|
|
|
4297
4399
|
minItems: 1,
|
|
4298
4400
|
maxItems: 3
|
|
4299
4401
|
})) }, { additionalProperties: false });
|
|
4300
|
-
var
|
|
4402
|
+
var RuntimeProfileSandboxResumeCommand = Union([String$1({
|
|
4301
4403
|
minLength: 1,
|
|
4302
4404
|
maxLength: 4096
|
|
4303
4405
|
}), _Object_({
|
|
@@ -4315,7 +4417,7 @@ var DaemonProfileSandboxResumeCommand = Union([String$1({
|
|
|
4315
4417
|
maximum: 6e4
|
|
4316
4418
|
}))
|
|
4317
4419
|
}, { additionalProperties: false })]);
|
|
4318
|
-
var
|
|
4420
|
+
var RuntimeProfileSandbox = _Object_({
|
|
4319
4421
|
snapshot: Optional(_Object_({
|
|
4320
4422
|
setupCommands: Optional(_Array_(String$1({
|
|
4321
4423
|
minLength: 1,
|
|
@@ -4331,7 +4433,7 @@ var DaemonProfileSandbox = _Object_({
|
|
|
4331
4433
|
pattern: "^[0-9]+[KMGTP]?$"
|
|
4332
4434
|
}))
|
|
4333
4435
|
}, { additionalProperties: false })),
|
|
4334
|
-
resumeCommands: Optional(_Array_(
|
|
4436
|
+
resumeCommands: Optional(_Array_(RuntimeProfileSandboxResumeCommand, { maxItems: 30 })),
|
|
4335
4437
|
vfs: Optional(_Object_({
|
|
4336
4438
|
shadow: Optional(_Array_(String$1({
|
|
4337
4439
|
minLength: 1,
|
|
@@ -4339,7 +4441,7 @@ var DaemonProfileSandbox = _Object_({
|
|
|
4339
4441
|
}), { maxItems: 100 })),
|
|
4340
4442
|
shadowMode: Optional(Union([Literal("deny"), Literal("tmpfs")]))
|
|
4341
4443
|
}, { additionalProperties: false })),
|
|
4342
|
-
env: Optional(Record(
|
|
4444
|
+
env: Optional(Record(RuntimeProfileEnvName, String$1({ maxLength: 4096 }))),
|
|
4343
4445
|
hostExec: Optional(_Object_({ autoApprove: Optional(Literal(false)) }, { additionalProperties: false })),
|
|
4344
4446
|
resources: Optional(_Object_({
|
|
4345
4447
|
memory: Optional(String$1({
|
|
@@ -4353,10 +4455,10 @@ var DaemonProfileSandbox = _Object_({
|
|
|
4353
4455
|
}))
|
|
4354
4456
|
}, { additionalProperties: false }))
|
|
4355
4457
|
}, {
|
|
4356
|
-
$id: "
|
|
4458
|
+
$id: "RuntimeProfileSandbox",
|
|
4357
4459
|
additionalProperties: false
|
|
4358
4460
|
});
|
|
4359
|
-
var
|
|
4461
|
+
var RuntimeProfileContext = _Object_({
|
|
4360
4462
|
slug: String$1({
|
|
4361
4463
|
minLength: 1,
|
|
4362
4464
|
maxLength: 64,
|
|
@@ -4373,17 +4475,29 @@ var DaemonProfileContext = _Object_({
|
|
|
4373
4475
|
maxLength: 65536
|
|
4374
4476
|
})
|
|
4375
4477
|
}, {
|
|
4376
|
-
$id: "
|
|
4478
|
+
$id: "RuntimeProfileContext",
|
|
4377
4479
|
additionalProperties: false
|
|
4378
4480
|
});
|
|
4379
|
-
var
|
|
4380
|
-
$id: "
|
|
4481
|
+
var RuntimeProfileRef = _Object_({ profileId: String$1({ format: "uuid" }) }, {
|
|
4482
|
+
$id: "RuntimeProfileRef",
|
|
4381
4483
|
additionalProperties: false
|
|
4382
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
|
+
});
|
|
4383
4497
|
_Object_({
|
|
4384
4498
|
id: String$1({ format: "uuid" }),
|
|
4385
4499
|
teamId: String$1({ format: "uuid" }),
|
|
4386
|
-
name:
|
|
4500
|
+
name: RuntimeProfileName,
|
|
4387
4501
|
description: Union([String$1({ maxLength: 4096 }), Null()]),
|
|
4388
4502
|
provider: String$1({
|
|
4389
4503
|
minLength: 1,
|
|
@@ -4394,7 +4508,7 @@ _Object_({
|
|
|
4394
4508
|
maxLength: 200
|
|
4395
4509
|
}),
|
|
4396
4510
|
runtimeKind: Literal("gondolin_pi"),
|
|
4397
|
-
sandbox:
|
|
4511
|
+
sandbox: RuntimeProfileSandbox,
|
|
4398
4512
|
sessionStorageMode: Literal("local"),
|
|
4399
4513
|
workspaceStorageMode: Literal("local"),
|
|
4400
4514
|
sessionTtlSec: Integer({
|
|
@@ -4405,9 +4519,12 @@ _Object_({
|
|
|
4405
4519
|
minimum: 1,
|
|
4406
4520
|
maximum: 86400
|
|
4407
4521
|
}),
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
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 }),
|
|
4411
4528
|
revision: Integer({ minimum: 1 }),
|
|
4412
4529
|
definitionCid: String$1({
|
|
4413
4530
|
minLength: 1,
|
|
@@ -4418,111 +4535,9 @@ _Object_({
|
|
|
4418
4535
|
createdAt: String$1({ format: "date-time" }),
|
|
4419
4536
|
updatedAt: String$1({ format: "date-time" })
|
|
4420
4537
|
}, {
|
|
4421
|
-
$id: "
|
|
4422
|
-
additionalProperties: false
|
|
4423
|
-
});
|
|
4424
|
-
//#endregion
|
|
4425
|
-
//#region ../../libs/tasks/src/rubric.ts
|
|
4426
|
-
/**
|
|
4427
|
-
* Rubric — structured acceptance criteria used by judgment tasks.
|
|
4428
|
-
*
|
|
4429
|
-
* Phase 1 (this PR): rubrics are embedded in task inputs. Their integrity
|
|
4430
|
-
* is pinned via the task's `input_cid` (which covers the whole input,
|
|
4431
|
-
* including the inline rubric). No separate storage, no CRUD.
|
|
4432
|
-
*
|
|
4433
|
-
* Phase 2 (see #881): rubrics become a first-class resource with their
|
|
4434
|
-
* own signed rows and CIDv1 lookup. The schema below is designed to
|
|
4435
|
-
* carry forward unchanged — only storage and addressing differ.
|
|
4436
|
-
*
|
|
4437
|
-
* Until Phase 2 lands, `rubricId` + `version` + `contentHash` are
|
|
4438
|
-
* informational fields the author fills in; no uniqueness is enforced.
|
|
4439
|
-
* `contentHash` is optional in Phase 1 because the *task*'s input_cid
|
|
4440
|
-
* is the authoritative commitment.
|
|
4441
|
-
*/
|
|
4442
|
-
/**
|
|
4443
|
-
* How a judge must score a single criterion.
|
|
4444
|
-
*
|
|
4445
|
-
* - `llm_score`: 0..1 continuous, `rationale` required. Smooths failures
|
|
4446
|
-
* into the gradient — use `llm_checklist` instead for properties where
|
|
4447
|
-
* a single failure is a real failure (grounding, faithfulness).
|
|
4448
|
-
* - `llm_checklist`: judge enumerates per-claim assertions with
|
|
4449
|
-
* `{passed, evidence}`. The criterion's numeric `score` is derived:
|
|
4450
|
-
* `1` iff every assertion passes, else `0`. Per-claim evidence is the
|
|
4451
|
-
* dataset for cluster-analysis of failure modes. See #999.
|
|
4452
|
-
* - `boolean`: 0 or 1, `rationale` optional.
|
|
4453
|
-
* - `deterministic_signature_check`: judge runs a signature check;
|
|
4454
|
-
* result is 0 or 1. No LLM discretion.
|
|
4455
|
-
* - `deterministic_coverage_check`: every referenced source entry
|
|
4456
|
-
* appears in the rendered output; 0 or 1.
|
|
4457
|
-
*/
|
|
4458
|
-
var RubricScoringMode = Union([
|
|
4459
|
-
Literal("llm_score"),
|
|
4460
|
-
Literal("llm_checklist"),
|
|
4461
|
-
Literal("boolean"),
|
|
4462
|
-
Literal("deterministic_signature_check"),
|
|
4463
|
-
Literal("deterministic_coverage_check")
|
|
4464
|
-
], { $id: "RubricScoringMode" });
|
|
4465
|
-
/**
|
|
4466
|
-
* One binary check produced by an `llm_checklist`-mode criterion.
|
|
4467
|
-
*
|
|
4468
|
-
* `evidence` is REQUIRED for both PASS and FAIL — agentskills.io grading
|
|
4469
|
-
* principle: \"Don't give the benefit of the doubt.\" A PASS without
|
|
4470
|
-
* concrete evidence (a quoted span, an entry id, a source location)
|
|
4471
|
-
* cannot be audited. A FAIL without evidence cannot be clustered into
|
|
4472
|
-
* structural fixes. The same shape is reused by `judge-eval-variant`
|
|
4473
|
-
* (#943) so tooling, dashboards, and analysis stay uniform.
|
|
4474
|
-
*/
|
|
4475
|
-
var AssertionResult = _Object_({
|
|
4476
|
-
id: String$1({ minLength: 1 }),
|
|
4477
|
-
text: String$1({ minLength: 1 }),
|
|
4478
|
-
passed: Boolean$1(),
|
|
4479
|
-
evidence: String$1({ minLength: 1 })
|
|
4480
|
-
}, {
|
|
4481
|
-
$id: "AssertionResult",
|
|
4482
|
-
additionalProperties: false
|
|
4483
|
-
});
|
|
4484
|
-
var RubricCriterion = _Object_({
|
|
4485
|
-
id: String$1({ minLength: 1 }),
|
|
4486
|
-
description: String$1({ minLength: 1 }),
|
|
4487
|
-
weight: Number$1({
|
|
4488
|
-
minimum: 0,
|
|
4489
|
-
maximum: 1
|
|
4490
|
-
}),
|
|
4491
|
-
scoring: RubricScoringMode
|
|
4492
|
-
}, {
|
|
4493
|
-
$id: "RubricCriterion",
|
|
4538
|
+
$id: "RuntimeProfile",
|
|
4494
4539
|
additionalProperties: false
|
|
4495
4540
|
});
|
|
4496
|
-
/**
|
|
4497
|
-
* A complete rubric. Same shape used in Phase 1 (inline) and Phase 2
|
|
4498
|
-
* (stored row `body`); only the addressing mechanism differs.
|
|
4499
|
-
*/
|
|
4500
|
-
var Rubric = _Object_({
|
|
4501
|
-
rubricId: String$1({ minLength: 1 }),
|
|
4502
|
-
version: String$1({ minLength: 1 }),
|
|
4503
|
-
preamble: Optional(String$1()),
|
|
4504
|
-
criteria: _Array_(RubricCriterion, { minItems: 1 }),
|
|
4505
|
-
scope: Optional(String$1()),
|
|
4506
|
-
contentHash: Optional(String$1())
|
|
4507
|
-
}, {
|
|
4508
|
-
$id: "Rubric",
|
|
4509
|
-
additionalProperties: false
|
|
4510
|
-
});
|
|
4511
|
-
/**
|
|
4512
|
-
* Verify rubric criteria weights sum to 1.0 within floating-point tolerance.
|
|
4513
|
-
* The schema constrains each weight to [0,1] but can't express a cross-field
|
|
4514
|
-
* sum constraint, so this is enforced programmatically by callers that
|
|
4515
|
-
* accept rubrics (task input validators, server-side task creation).
|
|
4516
|
-
*
|
|
4517
|
-
* Returns null when valid; otherwise an error message suitable for surfacing
|
|
4518
|
-
* to the caller. Tolerance is 1e-6 to accommodate JSON round-tripping of
|
|
4519
|
-
* decimal fractions (e.g. 0.1 + 0.2 + 0.3 + 0.4 ≠ 1.0 exactly).
|
|
4520
|
-
*/
|
|
4521
|
-
function validateRubricWeights(rubric) {
|
|
4522
|
-
const sum = rubric.criteria.reduce((acc, c) => acc + c.weight, 0);
|
|
4523
|
-
if (Math.abs(sum - 1) > 1e-6) return `Rubric weights must sum to 1.0 (got ${sum.toFixed(6)})`;
|
|
4524
|
-
return null;
|
|
4525
|
-
}
|
|
4526
4541
|
//#endregion
|
|
4527
4542
|
//#region ../../libs/tasks/src/success-criteria.ts
|
|
4528
4543
|
/**
|
|
@@ -9116,6 +9131,7 @@ var TaskAttemptStatus = Union([
|
|
|
9116
9131
|
Literal("completed"),
|
|
9117
9132
|
Literal("failed"),
|
|
9118
9133
|
Literal("cancelled"),
|
|
9134
|
+
Literal("aborted"),
|
|
9119
9135
|
Literal("timed_out")
|
|
9120
9136
|
], { $id: "TaskAttemptStatus" });
|
|
9121
9137
|
var ExecutorTrustLevel = Union([
|
|
@@ -9268,7 +9284,7 @@ _Object_({
|
|
|
9268
9284
|
acceptedAttemptN: Union([Number$1(), Null()]),
|
|
9269
9285
|
claimCondition: Union([Unsafe(Ref$2("ClaimCondition")), Null()]),
|
|
9270
9286
|
requiredExecutorTrustLevel: ExecutorTrustLevel,
|
|
9271
|
-
allowedProfiles: _Array_(
|
|
9287
|
+
allowedProfiles: _Array_(RuntimeProfileRef, { maxItems: 16 }),
|
|
9272
9288
|
status: TaskStatus,
|
|
9273
9289
|
queuedAt: IsoTimestamp,
|
|
9274
9290
|
completedAt: Union([IsoTimestamp, Null()]),
|
|
@@ -9364,15 +9380,23 @@ _Object_({
|
|
|
9364
9380
|
//#region src/lib/help.ts
|
|
9365
9381
|
var COMMON_REQUIRED_FLAGS = `\
|
|
9366
9382
|
-a, --agent <name> MoltNet agent identity. Reads credentials
|
|
9367
|
-
from <repo-root>/.moltnet/<name>/moltnet.json
|
|
9383
|
+
from <repo-root>/.moltnet/<name>/moltnet.json.`;
|
|
9384
|
+
var COMMON_MODEL_FLAGS = `\
|
|
9368
9385
|
-p, --provider <id> LLM provider id (e.g. anthropic, openai-codex).
|
|
9369
9386
|
-m, --model <id> LLM model id for the provider (e.g.
|
|
9370
|
-
claude-sonnet-4-5, gpt-5.3-codex)
|
|
9387
|
+
claude-sonnet-4-5, gpt-5.3-codex). Required
|
|
9388
|
+
unless --profile is set.`;
|
|
9371
9389
|
var COMMON_OPTIONAL_FLAGS = `\
|
|
9372
9390
|
--sandbox <path> Path to sandbox.json. Default: search up from
|
|
9373
9391
|
the daemon's CWD until found. The directory
|
|
9374
9392
|
containing sandbox.json is also used as the
|
|
9375
|
-
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.
|
|
9376
9400
|
--lease-ttl-sec <n> Sliding liveness window. Silence longer than
|
|
9377
9401
|
this ends the attempt with lease_expired.
|
|
9378
9402
|
Default: 300.
|
|
@@ -9390,7 +9414,8 @@ var COMMON_OPTIONAL_FLAGS = `\
|
|
|
9390
9414
|
--warm-session-ttl-sec <n> Retain resumable daemon slots (Pi sessions +
|
|
9391
9415
|
reusable worktrees) in local daemon state for
|
|
9392
9416
|
this many seconds after use. 0 = disable reuse.
|
|
9393
|
-
Default: 1800
|
|
9417
|
+
Default: 1800, or min(profile session/workspace
|
|
9418
|
+
TTL) when --profile is set.
|
|
9394
9419
|
--debug Verbose logging: also log successful list/claim
|
|
9395
9420
|
outcomes (candidate counts, claim attempts).`;
|
|
9396
9421
|
var REGISTERED_TASK_TYPES = Object.keys(BUILT_IN_TASK_TYPES).sort();
|
|
@@ -9413,9 +9438,10 @@ Run \`agent-daemon <command> --help\` for command-specific flags.
|
|
|
9413
9438
|
|
|
9414
9439
|
Prerequisites (all subcommands):
|
|
9415
9440
|
- <repo-root>/.moltnet/<agent>/moltnet.json — credentials (see --agent)
|
|
9416
|
-
- sandbox.json —
|
|
9417
|
-
from CWD, or pass --sandbox <path>.
|
|
9418
|
-
|
|
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.
|
|
9419
9445
|
|
|
9420
9446
|
Registered task types: ${knownTaskTypesList()}`;
|
|
9421
9447
|
var POLL_HELP = `\
|
|
@@ -9428,6 +9454,7 @@ Required:
|
|
|
9428
9454
|
--team <uuid> Team whose queue to serve. The daemon must be
|
|
9429
9455
|
a member of this team (canAccessTeam permit).
|
|
9430
9456
|
${COMMON_REQUIRED_FLAGS}
|
|
9457
|
+
${COMMON_MODEL_FLAGS}
|
|
9431
9458
|
|
|
9432
9459
|
Optional:
|
|
9433
9460
|
--task-types <csv> Whitelist of task types to claim. Default:
|
|
@@ -9458,6 +9485,7 @@ Required:
|
|
|
9458
9485
|
-t, --task-id <uuid> Task to claim and execute. Must already be
|
|
9459
9486
|
in 'queued' status.
|
|
9460
9487
|
${COMMON_REQUIRED_FLAGS}
|
|
9488
|
+
${COMMON_MODEL_FLAGS}
|
|
9461
9489
|
|
|
9462
9490
|
Optional:
|
|
9463
9491
|
${COMMON_OPTIONAL_FLAGS}
|
|
@@ -9483,6 +9511,7 @@ sleeps and retries forever).
|
|
|
9483
9511
|
Required:
|
|
9484
9512
|
--team <uuid> Team whose queue to drain.
|
|
9485
9513
|
${COMMON_REQUIRED_FLAGS}
|
|
9514
|
+
${COMMON_MODEL_FLAGS}
|
|
9486
9515
|
|
|
9487
9516
|
Optional:
|
|
9488
9517
|
--task-types <csv> Whitelist. Known types: ${knownTaskTypesList()}
|
|
@@ -24459,6 +24488,9 @@ var ApiTaskReporter = class {
|
|
|
24459
24488
|
get cancelReason() {
|
|
24460
24489
|
return this.observedCancelReason;
|
|
24461
24490
|
}
|
|
24491
|
+
requestCancel(reason) {
|
|
24492
|
+
this.abortForCancel(reason);
|
|
24493
|
+
}
|
|
24462
24494
|
async open(ctx) {
|
|
24463
24495
|
if (this.heartbeatTimer) {
|
|
24464
24496
|
clearInterval(this.heartbeatTimer);
|
|
@@ -24641,13 +24673,15 @@ var ApiTaskReporter = class {
|
|
|
24641
24673
|
async sendHeartbeat() {
|
|
24642
24674
|
const body = this.opts.leaseTtlSec ? { leaseTtlSec: this.opts.leaseTtlSec } : {};
|
|
24643
24675
|
const response = await this.opts.tasks.heartbeat(this.taskId, this.attemptN, body);
|
|
24644
|
-
if (response?.cancelled
|
|
24645
|
-
|
|
24646
|
-
|
|
24647
|
-
|
|
24648
|
-
|
|
24649
|
-
|
|
24650
|
-
|
|
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;
|
|
24651
24685
|
}
|
|
24652
24686
|
}
|
|
24653
24687
|
};
|
|
@@ -24660,6 +24694,7 @@ var AgentRuntime = class {
|
|
|
24660
24694
|
currentTaskId: null
|
|
24661
24695
|
};
|
|
24662
24696
|
stopRequested = false;
|
|
24697
|
+
currentReporter = null;
|
|
24663
24698
|
logger;
|
|
24664
24699
|
constructor(opts) {
|
|
24665
24700
|
this.opts = opts;
|
|
@@ -24688,6 +24723,7 @@ var AgentRuntime = class {
|
|
|
24688
24723
|
});
|
|
24689
24724
|
taskLogger.info({}, "agent-runtime.task_claimed");
|
|
24690
24725
|
const reporter = this.opts.makeReporter(claimedTask);
|
|
24726
|
+
this.currentReporter = reporter;
|
|
24691
24727
|
const taskCtx = Object.keys(claimedTask.traceHeaders).length ? propagation.extract(ROOT_CONTEXT, claimedTask.traceHeaders) : context.active();
|
|
24692
24728
|
const taskStart = Date.now();
|
|
24693
24729
|
let output;
|
|
@@ -24745,16 +24781,19 @@ var AgentRuntime = class {
|
|
|
24745
24781
|
}
|
|
24746
24782
|
this.status.tasksProcessed += 1;
|
|
24747
24783
|
this.status.currentTaskId = null;
|
|
24784
|
+
this.currentReporter = null;
|
|
24748
24785
|
}
|
|
24749
24786
|
} finally {
|
|
24787
|
+
this.currentReporter = null;
|
|
24750
24788
|
await this.opts.source.close();
|
|
24751
24789
|
this.status.state = "stopped";
|
|
24752
24790
|
}
|
|
24753
24791
|
return outputs;
|
|
24754
24792
|
}
|
|
24755
24793
|
/** Request cooperative shutdown. Safe from signal handlers. */
|
|
24756
|
-
stop() {
|
|
24794
|
+
stop(reason) {
|
|
24757
24795
|
this.stopRequested = true;
|
|
24796
|
+
if (reason !== void 0) this.currentReporter?.requestCancel?.(reason);
|
|
24758
24797
|
}
|
|
24759
24798
|
};
|
|
24760
24799
|
//#endregion
|
|
@@ -24766,8 +24805,11 @@ var ApiTaskSource = class {
|
|
|
24766
24805
|
}
|
|
24767
24806
|
async claim() {
|
|
24768
24807
|
if (this.claimed) return null;
|
|
24769
|
-
const { agent, taskId, leaseTtlSec } = this.opts;
|
|
24770
|
-
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
|
+
});
|
|
24771
24813
|
this.claimed = true;
|
|
24772
24814
|
return {
|
|
24773
24815
|
task: result.task,
|
|
@@ -26412,6 +26454,124 @@ var updateRenderedPack = (options) => (options.client ?? client).patch({
|
|
|
26412
26454
|
}
|
|
26413
26455
|
});
|
|
26414
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
|
+
/**
|
|
26415
26575
|
* List tasks for a team with optional filters.
|
|
26416
26576
|
*/
|
|
26417
26577
|
var listTasks = (options) => (options.client ?? client).get({
|
|
@@ -26526,6 +26686,32 @@ var listTaskAttempts = (options) => (options.client ?? client).get({
|
|
|
26526
26686
|
...options
|
|
26527
26687
|
});
|
|
26528
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
|
+
/**
|
|
26529
26715
|
* Mark an attempt as completed with output.
|
|
26530
26716
|
*/
|
|
26531
26717
|
var completeTask = (options) => (options.client ?? client).post({
|
|
@@ -28658,6 +28844,54 @@ function createRecoveryNamespace(context) {
|
|
|
28658
28844
|
};
|
|
28659
28845
|
}
|
|
28660
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
|
|
28661
28895
|
//#region ../../libs/sdk/src/namespaces/signing-requests.ts
|
|
28662
28896
|
function createSigningRequestsNamespace(context) {
|
|
28663
28897
|
const { client, auth } = context;
|
|
@@ -28778,6 +29012,17 @@ function createTasksNamespace(context) {
|
|
|
28778
29012
|
body
|
|
28779
29013
|
}));
|
|
28780
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
|
+
},
|
|
28781
29026
|
async cancel(id, body) {
|
|
28782
29027
|
return unwrapResult(await cancelTask({
|
|
28783
29028
|
client,
|
|
@@ -28961,6 +29206,7 @@ function createAgent(options) {
|
|
|
28961
29206
|
legreffier: createLegreffierNamespace(context),
|
|
28962
29207
|
problems: createProblemsNamespace(context),
|
|
28963
29208
|
teams: createTeamsNamespace(context),
|
|
29209
|
+
runtimeProfiles: createRuntimeProfilesNamespace(context),
|
|
28964
29210
|
tasks: createTasksNamespace(context),
|
|
28965
29211
|
client,
|
|
28966
29212
|
getToken: () => tokenManager.getToken()
|
|
@@ -32268,6 +32514,63 @@ function pruneOldSnapshots(maxCached, currentDir) {
|
|
|
32268
32514
|
});
|
|
32269
32515
|
}
|
|
32270
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
|
|
32271
32574
|
//#region ../../libs/pi-extension/src/vm-manager.ts
|
|
32272
32575
|
/**
|
|
32273
32576
|
* Memory-backed VFS mount used by the daemon to inject task-context
|
|
@@ -32384,23 +32687,33 @@ var BASE_ALLOWED_HOSTS = [
|
|
|
32384
32687
|
* surface immediately rather than fall through to cryptic agent
|
|
32385
32688
|
* errors later.
|
|
32386
32689
|
*/
|
|
32387
|
-
async function vmRun(vm, label, command) {
|
|
32690
|
+
async function vmRun(vm, label, command, signal) {
|
|
32388
32691
|
const wrapped = `set -eu\nset -o pipefail\n${command}`;
|
|
32692
|
+
throwIfAborted(signal, `resume step "${label}"`);
|
|
32389
32693
|
const r = await vm.exec([
|
|
32390
32694
|
"sh",
|
|
32391
32695
|
"-c",
|
|
32392
32696
|
wrapped
|
|
32393
|
-
]);
|
|
32697
|
+
], { signal });
|
|
32394
32698
|
if (r.exitCode !== 0) {
|
|
32395
32699
|
const tail = [r.stderr, r.stdout].filter(Boolean).join("\n").slice(-800);
|
|
32396
32700
|
throw new Error(`resume step "${label}" failed (exit ${r.exitCode}):\n${tail}`);
|
|
32397
32701
|
}
|
|
32398
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
|
+
}
|
|
32399
32711
|
/**
|
|
32400
32712
|
* Resume a VM from a checkpoint, inject credentials, configure egress +
|
|
32401
32713
|
* TLS. Returns the managed VM handle.
|
|
32402
32714
|
*/
|
|
32403
32715
|
async function resumeVm(config) {
|
|
32716
|
+
throwIfAborted(config.signal, "VM resume");
|
|
32404
32717
|
const mainRepo = findMainWorktree();
|
|
32405
32718
|
const agentDir = path.join(mainRepo, ".moltnet", config.agentName);
|
|
32406
32719
|
const guestWorkspace = path.resolve(config.mountPath);
|
|
@@ -32444,24 +32757,33 @@ async function resumeVm(config) {
|
|
|
32444
32757
|
};
|
|
32445
32758
|
const resources = config.sandboxConfig?.resources;
|
|
32446
32759
|
const workspaceMode = config.workspaceMode ?? "shared_mount";
|
|
32447
|
-
const vm = await
|
|
32448
|
-
|
|
32449
|
-
|
|
32450
|
-
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
|
|
32454
|
-
|
|
32455
|
-
|
|
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
|
+
}
|
|
32456
32778
|
});
|
|
32457
32779
|
try {
|
|
32458
|
-
await vm
|
|
32780
|
+
await vmRun(vm, "TLS certificates", `
|
|
32459
32781
|
cp /etc/gondolin/mitm/ca.crt /usr/local/share/ca-certificates/gondolin-mitm.crt
|
|
32460
32782
|
update-ca-certificates 2>/dev/null
|
|
32461
32783
|
cat /etc/gondolin/mitm/ca.crt >> /etc/ssl/certs/ca-certificates.crt
|
|
32462
|
-
|
|
32463
|
-
await vmRun(vm, "DNS resolvers", `printf 'nameserver 8.8.8.8\\nnameserver 1.1.1.1\\n' > /etc/resolv.conf
|
|
32464
|
-
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);
|
|
32465
32787
|
for (const [i, entry] of (config.sandboxConfig?.resumeCommands ?? []).entries()) {
|
|
32466
32788
|
if (!shouldRunResumeCommand(entry, { workspaceMode })) continue;
|
|
32467
32789
|
const { run, retries, backoffMs } = typeof entry === "string" ? {
|
|
@@ -32476,34 +32798,67 @@ async function resumeVm(config) {
|
|
|
32476
32798
|
const label = `resumeCommands[${i}]`;
|
|
32477
32799
|
let lastErr;
|
|
32478
32800
|
for (let attempt = 0; attempt <= retries; attempt++) try {
|
|
32479
|
-
await vmRun(vm, label, run);
|
|
32801
|
+
await vmRun(vm, label, run, config.signal);
|
|
32480
32802
|
lastErr = void 0;
|
|
32481
32803
|
break;
|
|
32482
32804
|
} catch (err) {
|
|
32483
32805
|
lastErr = err;
|
|
32484
32806
|
if (attempt === retries) break;
|
|
32485
|
-
await
|
|
32486
|
-
setTimeout(resolve, (attempt + 1) * backoffMs);
|
|
32487
|
-
});
|
|
32807
|
+
await delay((attempt + 1) * backoffMs, config.signal, label);
|
|
32488
32808
|
}
|
|
32489
|
-
if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(
|
|
32809
|
+
if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(nonErrorMessage(lastErr));
|
|
32490
32810
|
}
|
|
32491
32811
|
const vmSshDir = `${vmAgentDir}/ssh`;
|
|
32492
|
-
await vm.exec(`mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent
|
|
32493
|
-
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
|
+
});
|
|
32494
32817
|
const vmMoltnetJson = rewriteMoltnetJsonPaths(creds.moltnetJson, vmAgentDir, vmSshDir, creds.githubAppPemFilename);
|
|
32495
|
-
await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, {
|
|
32496
|
-
|
|
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
|
+
});
|
|
32497
32826
|
if (creds.gitconfig) {
|
|
32498
32827
|
const vmSigningKey = `${vmSshDir}/id_ed25519`;
|
|
32499
32828
|
const vmGitconfig = creds.gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSigningKey}`);
|
|
32500
|
-
await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
|
|
32829
|
+
await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
|
|
32830
|
+
mode: 420,
|
|
32831
|
+
signal: config.signal
|
|
32832
|
+
});
|
|
32501
32833
|
}
|
|
32502
|
-
if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, {
|
|
32503
|
-
|
|
32504
|
-
|
|
32505
|
-
|
|
32506
|
-
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);
|
|
32507
32862
|
return {
|
|
32508
32863
|
vm,
|
|
32509
32864
|
credentials: creds,
|
|
@@ -33756,6 +34111,20 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
33756
34111
|
retryable: false
|
|
33757
34112
|
}
|
|
33758
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
|
+
});
|
|
33759
34128
|
let onTurnEvent;
|
|
33760
34129
|
if (opts.makeOnTurnEvent) try {
|
|
33761
34130
|
onTurnEvent = opts.makeOnTurnEvent(claimedTask);
|
|
@@ -33818,10 +34187,15 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
33818
34187
|
mountPath,
|
|
33819
34188
|
workspaceMode: workspace.mode,
|
|
33820
34189
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
33821
|
-
sandboxConfig
|
|
34190
|
+
sandboxConfig,
|
|
34191
|
+
signal: reporter.cancelSignal
|
|
33822
34192
|
});
|
|
33823
34193
|
} catch (err) {
|
|
33824
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
|
+
}
|
|
33825
34199
|
await emitError("vm_resume", message);
|
|
33826
34200
|
return makeFailedOutput("vm_resume_failed", message);
|
|
33827
34201
|
}
|
|
@@ -34409,7 +34783,9 @@ function loadConfig() {
|
|
|
34409
34783
|
return {
|
|
34410
34784
|
agentDaemonStateDatabaseUrl: process.env["MOLTNET_AGENT_DAEMON_STATE_DATABASE_URL"] ?? "",
|
|
34411
34785
|
otelEndpoint: process.env["MOLTNET_OTEL_ENDPOINT"] ?? "",
|
|
34412
|
-
logLevel: process.env["LOG_LEVEL"] ?? ""
|
|
34786
|
+
logLevel: process.env["LOG_LEVEL"] ?? "",
|
|
34787
|
+
profilePrerequisiteEnv: process.env,
|
|
34788
|
+
profilePrerequisitePath: process.env.PATH ?? ""
|
|
34413
34789
|
};
|
|
34414
34790
|
}
|
|
34415
34791
|
//#endregion
|
|
@@ -34837,13 +35213,27 @@ function createRootLogger(options) {
|
|
|
34837
35213
|
target: "pino-pretty",
|
|
34838
35214
|
options: { colorize: true }
|
|
34839
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
|
+
});
|
|
34840
35225
|
const logger = pino(options, prettyTransport ?? void 0);
|
|
34841
35226
|
const shutdown = async () => {
|
|
34842
|
-
if (!prettyTransport) return;
|
|
34843
|
-
|
|
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
|
+
}
|
|
34844
35234
|
try {
|
|
34845
35235
|
prettyTransport.end();
|
|
34846
|
-
await once(prettyTransport, "close");
|
|
35236
|
+
if (!transportClosed) await once(prettyTransport, "close");
|
|
34847
35237
|
} catch (err) {
|
|
34848
35238
|
process.stderr.write(`[pino] transport teardown failed: ` + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
34849
35239
|
}
|
|
@@ -34871,22 +35261,29 @@ var MissingRequiredOptionError = class extends Error {
|
|
|
34871
35261
|
this.name = "MissingRequiredOptionError";
|
|
34872
35262
|
}
|
|
34873
35263
|
};
|
|
34874
|
-
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
|
+
};
|
|
34875
35272
|
if (!args.agent) throw new MissingRequiredOptionError("agent");
|
|
34876
|
-
if (!args.provider) throw new MissingRequiredOptionError("provider");
|
|
34877
|
-
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");
|
|
34878
35275
|
if (!/^[a-zA-Z0-9_-]+$/.test(args.agent)) throw new Error(`Invalid --agent "${args.agent}": must match /^[a-zA-Z0-9_-]+$/`);
|
|
34879
35276
|
return {
|
|
34880
35277
|
agent: args.agent,
|
|
34881
|
-
provider: args.provider,
|
|
34882
|
-
model: args.model,
|
|
34883
|
-
leaseTtlSec: parsePositiveInt(args["lease-ttl-sec"], "lease-ttl-sec",
|
|
34884
|
-
heartbeatIntervalMs: parseNonNegativeInt(args["heartbeat-interval-ms"], "heartbeat-interval-ms",
|
|
34885
|
-
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),
|
|
34886
35283
|
flushIntervalMs: parseNonNegativeInt(args["flush-interval-ms"], "flush-interval-ms", DEFAULTS.flushIntervalMs),
|
|
34887
35284
|
maxTurns: parseNonNegativeInt(args["max-turns"], "max-turns", DEFAULTS.maxTurns),
|
|
34888
35285
|
maxBashTimeouts: parseNonNegativeInt(args["max-bash-timeouts"], "max-bash-timeouts", DEFAULTS.maxBashTimeouts),
|
|
34889
|
-
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),
|
|
34890
35287
|
debug: args.debug === true
|
|
34891
35288
|
};
|
|
34892
35289
|
}
|
|
@@ -34977,6 +35374,71 @@ async function initWorkerOtel(options) {
|
|
|
34977
35374
|
};
|
|
34978
35375
|
}
|
|
34979
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
|
|
34980
35442
|
//#region src/lib/sandbox.ts
|
|
34981
35443
|
function resolveSandbox(startDir, explicitPath) {
|
|
34982
35444
|
const path = explicitPath ? isAbsolute(explicitPath) ? explicitPath : resolve(startDir, explicitPath) : findUp(startDir, "sandbox.json");
|
|
@@ -35005,6 +35467,38 @@ function findUp(startDir, filename) {
|
|
|
35005
35467
|
}
|
|
35006
35468
|
}
|
|
35007
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
|
|
35008
35502
|
//#region src/lib/state-dir.ts
|
|
35009
35503
|
function ensureDaemonStateDirs(mountPath) {
|
|
35010
35504
|
const rootDir = join(mountPath, ".moltnet", "d");
|
|
@@ -35054,7 +35548,8 @@ async function runPolling(opts) {
|
|
|
35054
35548
|
"poll-interval-ms": { type: "string" },
|
|
35055
35549
|
"max-poll-interval-ms": { type: "string" },
|
|
35056
35550
|
"list-limit": { type: "string" },
|
|
35057
|
-
sandbox: { type: "string" }
|
|
35551
|
+
sandbox: { type: "string" },
|
|
35552
|
+
profile: { type: "string" }
|
|
35058
35553
|
}
|
|
35059
35554
|
});
|
|
35060
35555
|
if (!values.team) {
|
|
@@ -35074,7 +35569,7 @@ async function runPolling(opts) {
|
|
|
35074
35569
|
const diaryIds = parseCsv(values["diary-ids"]);
|
|
35075
35570
|
let common;
|
|
35076
35571
|
try {
|
|
35077
|
-
common = parseCommonOptions(values);
|
|
35572
|
+
common = parseCommonOptions(values, { requireProviderModel: !values.profile });
|
|
35078
35573
|
} catch (err) {
|
|
35079
35574
|
if (err instanceof MissingRequiredOptionError) {
|
|
35080
35575
|
console.error(`${err.message}\n`);
|
|
@@ -35086,15 +35581,45 @@ async function runPolling(opts) {
|
|
|
35086
35581
|
const pollIntervalMs = optionalPositiveInt(values["poll-interval-ms"], "poll-interval-ms", 2e3);
|
|
35087
35582
|
const maxPollIntervalMs = optionalPositiveInt(values["max-poll-interval-ms"], "max-poll-interval-ms", 3e4);
|
|
35088
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
|
+
}
|
|
35089
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).`);
|
|
35090
|
-
const sandbox = resolveSandbox(process.cwd(), values.sandbox);
|
|
35091
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);
|
|
35092
35617
|
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
35093
35618
|
const slotRegistry = new DaemonSlotRegistry(resolveDaemonStateStorageConfig(stateDirs.registryDbPath, cfg.agentDaemonStateDatabaseUrl));
|
|
35094
35619
|
const slotIdentity = {
|
|
35095
35620
|
agentName: common.agent,
|
|
35096
|
-
provider
|
|
35097
|
-
model
|
|
35621
|
+
provider,
|
|
35622
|
+
model
|
|
35098
35623
|
};
|
|
35099
35624
|
const mainRepo = findMainWorktree();
|
|
35100
35625
|
const executionPlans = createExecutionPlanCache({
|
|
@@ -35103,7 +35628,6 @@ async function runPolling(opts) {
|
|
|
35103
35628
|
warmSessionTtlSec: common.warmSessionTtlSec,
|
|
35104
35629
|
slotRegistry
|
|
35105
35630
|
});
|
|
35106
|
-
const ctx = await resolveAgentContext(common.agent);
|
|
35107
35631
|
const otelShutdown = await initWorkerOtel({
|
|
35108
35632
|
serviceName: opts.serviceName,
|
|
35109
35633
|
agentDir: ctx.agentDir,
|
|
@@ -35111,8 +35635,9 @@ async function runPolling(opts) {
|
|
|
35111
35635
|
resourceAttributes: {
|
|
35112
35636
|
"moltnet.team.id": teamId,
|
|
35113
35637
|
"moltnet.agent.name": common.agent,
|
|
35114
|
-
"moltnet.llm.provider":
|
|
35115
|
-
"moltnet.llm.model":
|
|
35638
|
+
"moltnet.llm.provider": provider,
|
|
35639
|
+
"moltnet.llm.model": model,
|
|
35640
|
+
...profile ? { "moltnet.daemon_profile.id": profile.id } : {}
|
|
35116
35641
|
}
|
|
35117
35642
|
});
|
|
35118
35643
|
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
@@ -35123,46 +35648,82 @@ async function runPolling(opts) {
|
|
|
35123
35648
|
mode: opts.modeLabel,
|
|
35124
35649
|
agent: common.agent,
|
|
35125
35650
|
teamId,
|
|
35126
|
-
provider
|
|
35127
|
-
model
|
|
35651
|
+
provider,
|
|
35652
|
+
model,
|
|
35653
|
+
...profile ? {
|
|
35654
|
+
daemonProfileId: profile.id,
|
|
35655
|
+
daemonProfileName: profile.name
|
|
35656
|
+
} : {}
|
|
35128
35657
|
});
|
|
35129
35658
|
const abort = new AbortController();
|
|
35130
35659
|
let runtime = null;
|
|
35131
|
-
|
|
35132
|
-
|
|
35133
|
-
|
|
35134
|
-
|
|
35135
|
-
|
|
35136
|
-
|
|
35137
|
-
|
|
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
|
+
});
|
|
35138
35681
|
rootLogger.info({
|
|
35139
35682
|
sandbox: sandbox.path,
|
|
35140
35683
|
taskTypes: taskTypes.length > 0 ? taskTypes : ["*"],
|
|
35141
35684
|
diaryIds: diaryIds.length > 0 ? diaryIds : ["*"],
|
|
35142
35685
|
leaseTtlSec: common.leaseTtlSec,
|
|
35143
35686
|
heartbeatIntervalMs: common.heartbeatIntervalMs,
|
|
35687
|
+
warmSessionTtlSec: common.warmSessionTtlSec,
|
|
35144
35688
|
pollIntervalMs,
|
|
35145
|
-
maxPollIntervalMs
|
|
35689
|
+
maxPollIntervalMs,
|
|
35690
|
+
...profile ? {
|
|
35691
|
+
profileId: profile.id,
|
|
35692
|
+
profileSessionTtlSec: profile.sessionTtlSec,
|
|
35693
|
+
profileWorkspaceTtlSec: profile.workspaceTtlSec
|
|
35694
|
+
} : {}
|
|
35146
35695
|
}, "agent-daemon.starting");
|
|
35147
35696
|
const outputs = [];
|
|
35148
35697
|
try {
|
|
35149
|
-
const
|
|
35698
|
+
const rawExecuteTask = createPiTaskExecutor({
|
|
35150
35699
|
agentName: common.agent,
|
|
35151
35700
|
mountPath: sandbox.rootDir,
|
|
35152
|
-
provider
|
|
35153
|
-
model
|
|
35701
|
+
provider,
|
|
35702
|
+
model,
|
|
35154
35703
|
sandboxConfig: sandbox.config,
|
|
35155
35704
|
makeExecutionPlan: (claimedTask) => executionPlans.getOrCreate(claimedTask),
|
|
35156
35705
|
makeOnTurnEvent: makeTurnEventHandlerFactory(rootLogger),
|
|
35157
35706
|
maxTurns: common.maxTurns,
|
|
35158
35707
|
maxBashTimeouts: common.maxBashTimeouts
|
|
35159
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
|
+
};
|
|
35160
35720
|
runtime = new AgentRuntime({
|
|
35161
35721
|
logger: rootLogger,
|
|
35162
35722
|
source: new PollingApiTaskSource({
|
|
35163
35723
|
agent: ctx.agent,
|
|
35164
35724
|
teamId,
|
|
35165
35725
|
taskTypes: taskTypes.length > 0 ? taskTypes : void 0,
|
|
35726
|
+
...profile ? { profileId: profile.id } : {},
|
|
35166
35727
|
diaryIds: diaryIds.length > 0 ? diaryIds : void 0,
|
|
35167
35728
|
leaseTtlSec: common.leaseTtlSec,
|
|
35168
35729
|
listLimit,
|
|
@@ -35309,6 +35870,7 @@ async function runPolling(opts) {
|
|
|
35309
35870
|
rootLogger.info({ processed: drained.length }, "agent-daemon.drained");
|
|
35310
35871
|
return drained.some((o) => o.status !== "completed") ? 1 : 0;
|
|
35311
35872
|
} finally {
|
|
35873
|
+
signalHandlers.dispose();
|
|
35312
35874
|
await slotRegistry.close();
|
|
35313
35875
|
await otelShutdown();
|
|
35314
35876
|
await shutdownLogger();
|
|
@@ -35353,7 +35915,8 @@ async function runOnce(argv) {
|
|
|
35353
35915
|
type: "string",
|
|
35354
35916
|
short: "t"
|
|
35355
35917
|
},
|
|
35356
|
-
sandbox: { type: "string" }
|
|
35918
|
+
sandbox: { type: "string" },
|
|
35919
|
+
profile: { type: "string" }
|
|
35357
35920
|
}
|
|
35358
35921
|
});
|
|
35359
35922
|
if (!values["task-id"]) {
|
|
@@ -35364,7 +35927,7 @@ async function runOnce(argv) {
|
|
|
35364
35927
|
const taskId = values["task-id"];
|
|
35365
35928
|
let opts;
|
|
35366
35929
|
try {
|
|
35367
|
-
opts = parseCommonOptions(values);
|
|
35930
|
+
opts = parseCommonOptions(values, { requireProviderModel: !values.profile });
|
|
35368
35931
|
} catch (err) {
|
|
35369
35932
|
if (err instanceof MissingRequiredOptionError) {
|
|
35370
35933
|
console.error(`${err.message}\n`);
|
|
@@ -35373,14 +35936,43 @@ async function runOnce(argv) {
|
|
|
35373
35936
|
}
|
|
35374
35937
|
throw err;
|
|
35375
35938
|
}
|
|
35376
|
-
|
|
35939
|
+
if (values.profile && values.sandbox) {
|
|
35940
|
+
console.error("Cannot use --sandbox with --profile. Remote runtime profiles define sandbox policy.");
|
|
35941
|
+
return 1;
|
|
35942
|
+
}
|
|
35377
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);
|
|
35378
35970
|
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
35379
35971
|
const slotRegistry = new DaemonSlotRegistry(resolveDaemonStateStorageConfig(stateDirs.registryDbPath, cfg.agentDaemonStateDatabaseUrl));
|
|
35380
35972
|
const slotIdentity = {
|
|
35381
35973
|
agentName: opts.agent,
|
|
35382
|
-
provider
|
|
35383
|
-
model
|
|
35974
|
+
provider,
|
|
35975
|
+
model
|
|
35384
35976
|
};
|
|
35385
35977
|
const mainRepo = findMainWorktree();
|
|
35386
35978
|
const executionPlans = createExecutionPlanCache({
|
|
@@ -35389,7 +35981,6 @@ async function runOnce(argv) {
|
|
|
35389
35981
|
warmSessionTtlSec: opts.warmSessionTtlSec,
|
|
35390
35982
|
slotRegistry
|
|
35391
35983
|
});
|
|
35392
|
-
const ctx = await resolveAgentContext(opts.agent);
|
|
35393
35984
|
const otelShutdown = await initWorkerOtel({
|
|
35394
35985
|
serviceName: "moltnet.agent-daemon.once",
|
|
35395
35986
|
agentDir: ctx.agentDir,
|
|
@@ -35397,8 +35988,9 @@ async function runOnce(argv) {
|
|
|
35397
35988
|
resourceAttributes: {
|
|
35398
35989
|
"moltnet.task.id": taskId,
|
|
35399
35990
|
"moltnet.agent.name": opts.agent,
|
|
35400
|
-
"moltnet.llm.provider":
|
|
35401
|
-
"moltnet.llm.model":
|
|
35991
|
+
"moltnet.llm.provider": provider,
|
|
35992
|
+
"moltnet.llm.model": model,
|
|
35993
|
+
...profile ? { "moltnet.daemon_profile.id": profile.id } : {}
|
|
35402
35994
|
}
|
|
35403
35995
|
});
|
|
35404
35996
|
const { logger, shutdown: shutdownLogger } = createRootLogger({
|
|
@@ -35408,39 +36000,57 @@ async function runOnce(argv) {
|
|
|
35408
36000
|
const rootLogger = logger.child({
|
|
35409
36001
|
mode: "once",
|
|
35410
36002
|
agent: opts.agent,
|
|
35411
|
-
provider
|
|
35412
|
-
model
|
|
36003
|
+
provider,
|
|
36004
|
+
model,
|
|
36005
|
+
...profile ? {
|
|
36006
|
+
daemonProfileId: profile.id,
|
|
36007
|
+
daemonProfileName: profile.name
|
|
36008
|
+
} : {}
|
|
35413
36009
|
});
|
|
35414
36010
|
rootLogger.info({
|
|
35415
36011
|
sandbox: sandbox.path,
|
|
35416
|
-
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
|
+
} : {}
|
|
35417
36021
|
}, "agent-daemon.starting");
|
|
35418
36022
|
let runtime = null;
|
|
35419
|
-
|
|
35420
|
-
|
|
35421
|
-
|
|
35422
|
-
taskId
|
|
35423
|
-
}, "agent-daemon.draining");
|
|
35424
|
-
runtime?.stop();
|
|
35425
|
-
ctx.agent.tasks.cancel(taskId, { reason: `runner_${sig.toLowerCase()}` }).catch((err) => {
|
|
36023
|
+
let activeAttemptN = null;
|
|
36024
|
+
const signalHandlers = installShutdownSignalHandlers({
|
|
36025
|
+
logDrain: (signal) => {
|
|
35426
36026
|
rootLogger.warn({
|
|
35427
|
-
|
|
36027
|
+
signal,
|
|
35428
36028
|
taskId
|
|
35429
|
-
}, "agent-daemon.
|
|
35430
|
-
}
|
|
35431
|
-
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
|
|
35435
|
-
|
|
35436
|
-
|
|
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
|
+
}
|
|
35437
36047
|
});
|
|
35438
36048
|
try {
|
|
35439
36049
|
const rawExecuteTask = createPiTaskExecutor({
|
|
35440
36050
|
agentName: opts.agent,
|
|
35441
36051
|
mountPath: sandbox.rootDir,
|
|
35442
|
-
provider
|
|
35443
|
-
model
|
|
36052
|
+
provider,
|
|
36053
|
+
model,
|
|
35444
36054
|
sandboxConfig: sandbox.config,
|
|
35445
36055
|
makeExecutionPlan: (claimedTask) => executionPlans.getOrCreate(claimedTask),
|
|
35446
36056
|
onTurnEvent: makeTurnEventHandler(rootLogger, { taskId }),
|
|
@@ -35501,9 +36111,11 @@ async function runOnce(argv) {
|
|
|
35501
36111
|
lastAttemptN: claimedTask.attemptN,
|
|
35502
36112
|
ttlSec: opts.warmSessionTtlSec
|
|
35503
36113
|
});
|
|
36114
|
+
activeAttemptN = claimedTask.attemptN;
|
|
35504
36115
|
try {
|
|
35505
36116
|
return await rawExecuteTask(claimedTask, reporter);
|
|
35506
36117
|
} finally {
|
|
36118
|
+
activeAttemptN = null;
|
|
35507
36119
|
executionPlans.delete(claimedTask);
|
|
35508
36120
|
if (executionPlan.slotKey) await slotRegistry.finishSlot(slotIdentity, executionPlan.slotKey, opts.warmSessionTtlSec, executionPlan.sessionPersistence ? resolveLatestPiSessionPath(executionPlan.sessionPersistence.sessionDir) : null);
|
|
35509
36121
|
}
|
|
@@ -35517,7 +36129,8 @@ async function runOnce(argv) {
|
|
|
35517
36129
|
source: new ApiTaskSource({
|
|
35518
36130
|
agent: ctx.agent,
|
|
35519
36131
|
taskId,
|
|
35520
|
-
leaseTtlSec: opts.leaseTtlSec
|
|
36132
|
+
leaseTtlSec: opts.leaseTtlSec,
|
|
36133
|
+
...profile ? { profileId: profile.id } : {}
|
|
35521
36134
|
}),
|
|
35522
36135
|
makeReporter: () => new ApiTaskReporter({
|
|
35523
36136
|
tasks: ctx.agent.tasks,
|
|
@@ -35546,6 +36159,7 @@ async function runOnce(argv) {
|
|
|
35546
36159
|
console.log(JSON.stringify(output, null, 2));
|
|
35547
36160
|
return output.status === "completed" ? 0 : 1;
|
|
35548
36161
|
} finally {
|
|
36162
|
+
signalHandlers.dispose();
|
|
35549
36163
|
await slotRegistry.close();
|
|
35550
36164
|
await otelShutdown();
|
|
35551
36165
|
await shutdownLogger();
|