@themoltnet/agent-daemon 0.14.6 → 0.16.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.
Files changed (2) hide show
  1. package/dist/main.js +243 -20
  2. package/package.json +14 -6
package/dist/main.js CHANGED
@@ -1,5 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
+ import { registerInstrumentations } from "@opentelemetry/instrumentation";
4
+ import { DnsInstrumentation } from "@opentelemetry/instrumentation-dns";
5
+ import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
6
+ import { NetInstrumentation } from "@opentelemetry/instrumentation-net";
7
+ import { PgInstrumentation } from "@opentelemetry/instrumentation-pg";
8
+ import { PinoInstrumentation } from "@opentelemetry/instrumentation-pino";
9
+ import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
3
10
  import crypto$1, { createHash } from "crypto";
4
11
  import { createHash as createHash$1 } from "node:crypto";
5
12
  import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -55,6 +62,84 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
55
62
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
56
63
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
57
64
  //#endregion
65
+ //#region ../../libs/observability/src/instrumentation.ts
66
+ /**
67
+ * Register OTel auto-instrumentation for common Node.js modules.
68
+ *
69
+ * MUST be called before any other imports that load pg, http, net, dns, or
70
+ * pino. In ESM, place this in a dedicated side-effect module that is the
71
+ * first import in the app entrypoint:
72
+ *
73
+ * ```ts
74
+ * // instrumentation.ts (app-level)
75
+ * import { initInstrumentation } from '@moltnet/observability';
76
+ * initInstrumentation({ pg: true, httpIgnoreIncomingPaths: ['/health'] });
77
+ * ```
78
+ *
79
+ * ```ts
80
+ * // main.ts
81
+ * import './instrumentation.js'; // ← MUST be first
82
+ * import { bootstrap } from './bootstrap.js';
83
+ * ```
84
+ *
85
+ * The registered instrumentations pick up the global TracerProvider once
86
+ * it is set by `initObservability()`. Call this function first, then call
87
+ * `initObservability()`.
88
+ *
89
+ * Returns the registered instrumentation instances so callers (and tests) can
90
+ * introspect what was wired — e.g. assert that pino/undici correlation is
91
+ * enabled. The return value is otherwise inert; callers may ignore it.
92
+ */
93
+ function initInstrumentation(config) {
94
+ const { http = true, dns = true, net = true, pg = true, pino = true, httpIgnoreIncomingPaths = [] } = config;
95
+ const instrumentations = [];
96
+ if (http) instrumentations.push(new HttpInstrumentation({ ignoreIncomingRequestHook: httpIgnoreIncomingPaths.length > 0 ? (req) => {
97
+ const url = req.url ?? "";
98
+ return httpIgnoreIncomingPaths.some((path) => url.startsWith(path));
99
+ } : void 0 }), new UndiciInstrumentation());
100
+ if (dns) instrumentations.push(new DnsInstrumentation());
101
+ if (net) instrumentations.push(new NetInstrumentation());
102
+ if (pg) instrumentations.push(new PgInstrumentation());
103
+ if (pino) instrumentations.push(new PinoInstrumentation());
104
+ registerInstrumentations({ instrumentations });
105
+ return instrumentations;
106
+ }
107
+ //#endregion
108
+ //#region src/instrumentation.ts
109
+ /**
110
+ * OTel auto-instrumentation registration for the agent daemon.
111
+ *
112
+ * This file MUST be imported first in main.ts — before any module that
113
+ * transitively imports http, net, dns, undici (global fetch), or pino. In
114
+ * ESM, imports execute in declaration order, so placing this first guarantees
115
+ * the monkey-patches land before those modules load. In particular:
116
+ *
117
+ * - UndiciInstrumentation patches `globalThis.fetch` so every outbound SDK
118
+ * call (@themoltnet/sdk uses global fetch — see libs/sdk/src/connect.ts)
119
+ * carries a W3C `traceparent` header. That is what links the daemon's
120
+ * trace to the rest-api server span: one distributed trace, end to end.
121
+ * - PinoInstrumentation injects `trace_id` / `span_id` into every pino log
122
+ * record, so logs correlate with the active span in Axiom. It patches
123
+ * pino at import time — hence the strict ordering requirement. It does
124
+ * NOT add a transport, so the logger's pino-pretty shutdown dance
125
+ * (lib/logger.ts, issue #1107) stays valid.
126
+ *
127
+ * The registered instrumentations bind to the global TracerProvider once it
128
+ * is registered by `initWorkerOtel()` (lib/otel.ts, via provider.register()).
129
+ * Registration order is fine: instrumentations created here pick up the
130
+ * provider whenever it is set; spans only flow once it is registered.
131
+ *
132
+ * No `pg` instrumentation — the daemon has no direct database access. No
133
+ * `runtime-node` — that is server-side process metrics, out of scope here.
134
+ */
135
+ initInstrumentation({
136
+ http: true,
137
+ dns: true,
138
+ net: true,
139
+ pino: true,
140
+ pg: false
141
+ });
142
+ //#endregion
58
143
  //#region ../../node_modules/.pnpm/typebox@1.2.8/node_modules/typebox/build/format/date.mjs
59
144
  var DAYS = [
60
145
  0,
@@ -4188,6 +4273,155 @@ var TaskContext = _Array_(_Object_({
4188
4273
  maxItems: 5
4189
4274
  });
4190
4275
  //#endregion
4276
+ //#region ../../libs/tasks/src/daemon-profiles.ts
4277
+ var DaemonProfileName = String$1({
4278
+ minLength: 1,
4279
+ maxLength: 100,
4280
+ pattern: "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$"
4281
+ });
4282
+ var DaemonProfileEnvName = String$1({
4283
+ minLength: 1,
4284
+ maxLength: 128,
4285
+ pattern: "^[A-Z_][A-Z0-9_]*$"
4286
+ });
4287
+ var DaemonProfileToolName = String$1({
4288
+ minLength: 1,
4289
+ maxLength: 128,
4290
+ pattern: "^[a-zA-Z0-9._/-]+$"
4291
+ });
4292
+ var SandboxResumeCommandWhenSchema = _Object_({ workspaceMode: Optional(_Array_(Union([
4293
+ Literal("shared_mount"),
4294
+ Literal("dedicated_worktree"),
4295
+ Literal("scratch_mount")
4296
+ ]), {
4297
+ minItems: 1,
4298
+ maxItems: 3
4299
+ })) }, { additionalProperties: false });
4300
+ var DaemonProfileSandboxResumeCommand = Union([String$1({
4301
+ minLength: 1,
4302
+ maxLength: 4096
4303
+ }), _Object_({
4304
+ run: String$1({
4305
+ minLength: 1,
4306
+ maxLength: 4096
4307
+ }),
4308
+ when: Optional(SandboxResumeCommandWhenSchema),
4309
+ retries: Optional(Integer({
4310
+ minimum: 0,
4311
+ maximum: 5
4312
+ })),
4313
+ retryBackoffMs: Optional(Integer({
4314
+ minimum: 0,
4315
+ maximum: 6e4
4316
+ }))
4317
+ }, { additionalProperties: false })]);
4318
+ var DaemonProfileSandbox = _Object_({
4319
+ snapshot: Optional(_Object_({
4320
+ setupCommands: Optional(_Array_(String$1({
4321
+ minLength: 1,
4322
+ maxLength: 4096
4323
+ }), { maxItems: 20 })),
4324
+ allowedHosts: Optional(_Array_(String$1({
4325
+ minLength: 1,
4326
+ maxLength: 255
4327
+ }), { maxItems: 50 })),
4328
+ overlaySize: Optional(String$1({
4329
+ minLength: 2,
4330
+ maxLength: 16,
4331
+ pattern: "^[0-9]+[KMGTP]?$"
4332
+ }))
4333
+ }, { additionalProperties: false })),
4334
+ resumeCommands: Optional(_Array_(DaemonProfileSandboxResumeCommand, { maxItems: 30 })),
4335
+ vfs: Optional(_Object_({
4336
+ shadow: Optional(_Array_(String$1({
4337
+ minLength: 1,
4338
+ maxLength: 255
4339
+ }), { maxItems: 100 })),
4340
+ shadowMode: Optional(Union([Literal("deny"), Literal("tmpfs")]))
4341
+ }, { additionalProperties: false })),
4342
+ env: Optional(Record(DaemonProfileEnvName, String$1({ maxLength: 4096 }))),
4343
+ hostExec: Optional(_Object_({ autoApprove: Optional(Literal(false)) }, { additionalProperties: false })),
4344
+ resources: Optional(_Object_({
4345
+ memory: Optional(String$1({
4346
+ minLength: 2,
4347
+ maxLength: 16,
4348
+ pattern: "^[0-9]+[KMG]?$"
4349
+ })),
4350
+ cpus: Optional(Integer({
4351
+ minimum: 1,
4352
+ maximum: 32
4353
+ }))
4354
+ }, { additionalProperties: false }))
4355
+ }, {
4356
+ $id: "DaemonProfileSandbox",
4357
+ additionalProperties: false
4358
+ });
4359
+ var DaemonProfileContext = _Object_({
4360
+ slug: String$1({
4361
+ minLength: 1,
4362
+ maxLength: 64,
4363
+ pattern: "^[a-zA-Z0-9_-]+$"
4364
+ }),
4365
+ binding: Union([
4366
+ Literal("skill"),
4367
+ Literal("context_inline"),
4368
+ Literal("prompt_prefix"),
4369
+ Literal("user_inline")
4370
+ ]),
4371
+ content: String$1({
4372
+ minLength: 1,
4373
+ maxLength: 65536
4374
+ })
4375
+ }, {
4376
+ $id: "DaemonProfileContext",
4377
+ additionalProperties: false
4378
+ });
4379
+ var DaemonProfileRef = _Object_({ profileId: String$1({ format: "uuid" }) }, {
4380
+ $id: "DaemonProfileRef",
4381
+ additionalProperties: false
4382
+ });
4383
+ _Object_({
4384
+ id: String$1({ format: "uuid" }),
4385
+ teamId: String$1({ format: "uuid" }),
4386
+ name: DaemonProfileName,
4387
+ description: Union([String$1({ maxLength: 4096 }), Null()]),
4388
+ provider: String$1({
4389
+ minLength: 1,
4390
+ maxLength: 100
4391
+ }),
4392
+ model: String$1({
4393
+ minLength: 1,
4394
+ maxLength: 200
4395
+ }),
4396
+ runtimeKind: Literal("gondolin_pi"),
4397
+ sandbox: DaemonProfileSandbox,
4398
+ sessionStorageMode: Literal("local"),
4399
+ workspaceStorageMode: Literal("local"),
4400
+ sessionTtlSec: Integer({
4401
+ minimum: 1,
4402
+ maximum: 86400
4403
+ }),
4404
+ workspaceTtlSec: Integer({
4405
+ minimum: 1,
4406
+ maximum: 86400
4407
+ }),
4408
+ requiredEnv: _Array_(DaemonProfileEnvName, { maxItems: 100 }),
4409
+ requiredTools: _Array_(DaemonProfileToolName, { maxItems: 100 }),
4410
+ context: _Array_(DaemonProfileContext, { maxItems: 5 }),
4411
+ revision: Integer({ minimum: 1 }),
4412
+ definitionCid: String$1({
4413
+ minLength: 1,
4414
+ maxLength: 100
4415
+ }),
4416
+ createdByAgentId: Union([String$1({ format: "uuid" }), Null()]),
4417
+ createdByHumanId: Union([String$1({ format: "uuid" }), Null()]),
4418
+ createdAt: String$1({ format: "date-time" }),
4419
+ updatedAt: String$1({ format: "date-time" })
4420
+ }, {
4421
+ $id: "DaemonProfile",
4422
+ additionalProperties: false
4423
+ });
4424
+ //#endregion
4191
4425
  //#region ../../libs/tasks/src/rubric.ts
4192
4426
  /**
4193
4427
  * Rubric — structured acceptance criteria used by judgment tasks.
@@ -8890,14 +9124,6 @@ var ExecutorTrustLevel = Union([
8890
9124
  Literal("releaseVerifiedTool"),
8891
9125
  Literal("sandboxAttested")
8892
9126
  ], { $id: "ExecutorTrustLevel" });
8893
- /** Identifies a (provider, model) daemon pair allowed to claim a task. */
8894
- var ExecutorRef = _Object_({
8895
- provider: String$1({ minLength: 1 }),
8896
- model: String$1({ minLength: 1 })
8897
- }, {
8898
- $id: "ExecutorRef",
8899
- additionalProperties: false
8900
- });
8901
9127
  var OutputKind = Union([Literal("artifact"), Literal("judgment")], { $id: "OutputKind" });
8902
9128
  var TaskMessageKind = Union([
8903
9129
  Literal("text_delta"),
@@ -9042,7 +9268,7 @@ _Object_({
9042
9268
  acceptedAttemptN: Union([Number$1(), Null()]),
9043
9269
  claimCondition: Union([Unsafe(Ref$2("ClaimCondition")), Null()]),
9044
9270
  requiredExecutorTrustLevel: ExecutorTrustLevel,
9045
- allowedExecutors: _Array_(ExecutorRef, { maxItems: 16 }),
9271
+ allowedProfiles: _Array_(DaemonProfileRef, { maxItems: 16 }),
9046
9272
  status: TaskStatus,
9047
9273
  queuedAt: IsoTimestamp,
9048
9274
  completedAt: Union([IsoTimestamp, Null()]),
@@ -30823,7 +31049,6 @@ var PollingApiTaskSource = class {
30823
31049
  this.minBackoffMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
30824
31050
  this.maxBackoffMs = opts.maxPollIntervalMs ?? DEFAULT_MAX_POLL_INTERVAL_MS;
30825
31051
  if (this.maxBackoffMs < this.minBackoffMs) throw new Error(`PollingApiTaskSource: maxPollIntervalMs (${this.maxBackoffMs}) must be >= pollIntervalMs (${this.minBackoffMs})`);
30826
- if (Boolean(opts.provider) !== Boolean(opts.model)) throw new Error("PollingApiTaskSource: provider and model must be set together");
30827
31052
  this.listLimit = opts.listLimit ?? DEFAULT_LIST_LIMIT;
30828
31053
  this.currentBackoffMs = this.minBackoffMs;
30829
31054
  this.logger = (opts.logger ?? pino({ name: "polling-api-source" })).child({ teamId: opts.teamId });
@@ -30866,10 +31091,7 @@ var PollingApiTaskSource = class {
30866
31091
  teamId: this.opts.teamId,
30867
31092
  status: "queued",
30868
31093
  ...taskTypes ? { taskTypes } : {},
30869
- ...this.opts.provider && this.opts.model ? {
30870
- provider: this.opts.provider,
30871
- model: this.opts.model
30872
- } : {},
31094
+ ...this.opts.profileId ? { profileId: this.opts.profileId } : {},
30873
31095
  ...cursor ? { cursor } : {},
30874
31096
  limit: this.listLimit
30875
31097
  });
@@ -30884,9 +31106,9 @@ var PollingApiTaskSource = class {
30884
31106
  if (this.opts.taskTypes && this.opts.taskTypes.length > 0 && !this.opts.taskTypes.includes(item.taskType)) continue;
30885
31107
  if (this.opts.diaryIds && this.opts.diaryIds.length > 0 && (item.diaryId === null || !this.opts.diaryIds.includes(item.diaryId))) continue;
30886
31108
  if (this.opts.slotRegistry && !await isContinuationClaimableByThisDaemon(item, this.opts.slotRegistry)) continue;
30887
- if (this.opts.provider && this.opts.model) {
30888
- const allowed = item.allowedExecutors ?? [];
30889
- if (allowed.length > 0 && !allowed.some((e) => e.provider === this.opts.provider && e.model === this.opts.model)) continue;
31109
+ if (this.opts.profileId) {
31110
+ const allowed = item.allowedProfiles ?? [];
31111
+ if (allowed.length > 0 && !allowed.some((p) => p.profileId === this.opts.profileId)) continue;
30890
31112
  }
30891
31113
  if (item.status !== "queued") continue;
30892
31114
  seen.add(item.id);
@@ -30909,7 +31131,10 @@ var PollingApiTaskSource = class {
30909
31131
  for (const task of candidates) {
30910
31132
  if (this.aborted()) return null;
30911
31133
  try {
30912
- const result = await this.opts.agent.tasks.claim(task.id, { leaseTtlSec: this.opts.leaseTtlSec });
31134
+ const result = await this.opts.agent.tasks.claim(task.id, {
31135
+ leaseTtlSec: this.opts.leaseTtlSec,
31136
+ ...this.opts.profileId ? { profileId: this.opts.profileId } : {}
31137
+ });
30913
31138
  if (this.opts.debug) this.logger.debug({
30914
31139
  taskId: result.task.id,
30915
31140
  taskType: result.task.taskType,
@@ -34938,8 +35163,6 @@ async function runPolling(opts) {
34938
35163
  agent: ctx.agent,
34939
35164
  teamId,
34940
35165
  taskTypes: taskTypes.length > 0 ? taskTypes : void 0,
34941
- provider: common.provider.toLowerCase(),
34942
- model: common.model.toLowerCase(),
34943
35166
  diaryIds: diaryIds.length > 0 ? diaryIds : void 0,
34944
35167
  leaseTtlSec: common.leaseTtlSec,
34945
35168
  listLimit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.14.6",
3
+ "version": "0.16.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "description": "MoltNet agent daemon — claims and executes tasks (fulfill_brief, assess_brief) from the MoltNet task-service via Pi-headless. CLI: moltnet-agent.",
@@ -32,6 +32,12 @@
32
32
  "@earendil-works/pi-coding-agent": "^0.74.0",
33
33
  "@opentelemetry/api": "^1.9.0",
34
34
  "@opentelemetry/exporter-trace-otlp-proto": "^0.212.0",
35
+ "@opentelemetry/instrumentation": "^0.212.0",
36
+ "@opentelemetry/instrumentation-dns": "^0.55.0",
37
+ "@opentelemetry/instrumentation-http": "^0.212.0",
38
+ "@opentelemetry/instrumentation-net": "^0.56.0",
39
+ "@opentelemetry/instrumentation-pino": "^0.58.0",
40
+ "@opentelemetry/instrumentation-undici": "^0.22.0",
35
41
  "@opentelemetry/resources": "^2.5.1",
36
42
  "@opentelemetry/sdk-trace-base": "^2.5.1",
37
43
  "@opentelemetry/sdk-trace-node": "^2.5.1",
@@ -39,18 +45,20 @@
39
45
  "pino": "^10.3.1",
40
46
  "pino-pretty": "^13.1.3",
41
47
  "@themoltnet/agent-daemon-state": "0.2.0",
42
- "@themoltnet/agent-runtime": "0.22.4",
43
- "@themoltnet/pi-extension": "0.22.5",
44
- "@themoltnet/sdk": "0.106.0"
48
+ "@themoltnet/pi-extension": "0.23.0",
49
+ "@themoltnet/sdk": "0.107.0",
50
+ "@themoltnet/agent-runtime": "0.23.0"
45
51
  },
46
52
  "devDependencies": {
47
53
  "tsx": "^4.7.0",
48
54
  "typescript": "~5.9.2",
49
55
  "vite": "^8.0.0",
50
56
  "vitest": "^3.0.0",
57
+ "@moltnet/api-client": "0.1.0",
58
+ "@moltnet/crypto-service": "0.1.0",
59
+ "@moltnet/observability": "0.1.0",
51
60
  "@moltnet/tasks": "0.1.0",
52
- "@moltnet/bootstrap": "0.1.0",
53
- "@moltnet/crypto-service": "0.1.0"
61
+ "@moltnet/bootstrap": "0.1.0"
54
62
  },
55
63
  "nx": {
56
64
  "tags": [