@themoltnet/agent-daemon 0.17.0 → 0.18.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 (3) hide show
  1. package/README.md +19 -8
  2. package/dist/main.js +59 -8
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -50,8 +50,17 @@ The agent's `moltnet.json` and gitconfig live next to each other in
50
50
 
51
51
  ### Pi provider auth
52
52
 
53
- Pi resolves provider credentials in this order: `~/.pi/agent/auth.json` (if
54
- present) wins, else environment variables. For CI prefer env vars:
53
+ The daemon resolves Pi config from the repository-local `.pi` directory by
54
+ default. On startup, if `PI_CODING_AGENT_DIR` is not already set, the daemon
55
+ sets it to `<repo-root>/.pi` before creating Pi sessions. This keeps daemon
56
+ runs deterministic and avoids inheriting user-level `~/.pi/agent` state.
57
+
58
+ Repo-local `.pi/settings.json` and `.pi/models.json` are intended to be
59
+ committed. `models.json` should reference provider keys by environment-variable
60
+ name, for example `"apiKey": "OLLAMA_API_KEY"`, not contain secret values.
61
+ Repo-local `.pi/auth.json` may exist for local subscription auth, but is
62
+ gitignored. Without `.pi/auth.json`, Pi falls back to environment-variable
63
+ provider keys:
55
64
 
56
65
  ```bash
57
66
  export ANTHROPIC_API_KEY=sk-ant-...
@@ -59,7 +68,8 @@ export ANTHROPIC_API_KEY=sk-ant-...
59
68
  # https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/env-api-keys.ts
60
69
  ```
61
70
 
62
- To use a non-default auth directory: `PI_CODING_AGENT_DIR=/abs/path/to/.pi/agent`.
71
+ To force a non-repo Pi directory, set
72
+ `PI_CODING_AGENT_DIR=/abs/path/to/.pi-or-agent-dir` before starting the daemon.
63
73
 
64
74
  ### Observability
65
75
 
@@ -126,11 +136,12 @@ registered task type; unknown task-type names remain invalid.
126
136
  ### Prerequisites
127
137
 
128
138
  - Docker running.
129
- - pi authenticated for the model provider you'll drive the daemon with
130
- (`~/.pi/agent/auth.json` should already contain entries for `anthropic`
131
- and/or `openai-codex` — set up via the normal pi/legreffier onboarding). The
132
- daemon does **not** read `ANTHROPIC_API_KEY` from env at the smoke-test path
133
- (CI is the exception — see [Pi provider auth](#pi-provider-auth) above).
139
+ - Pi config for the model provider you'll drive the daemon with. Local daemon
140
+ runs default `PI_CODING_AGENT_DIR` to repo-local `.pi`, so committed
141
+ `.pi/settings.json` and `.pi/models.json` must list the provider/model. For
142
+ subscription auth, put your local token blob in `.pi/auth.json`; it is
143
+ gitignored. For API-key auth, keep `.pi/auth.json` absent and export the
144
+ provider key referenced by `.pi/models.json`, for example `OLLAMA_API_KEY`.
134
145
  - `ssh-keygen` on `PATH`.
135
146
  - A `sandbox.json` at the repo root, or an explicit `--sandbox <path>` when
136
147
  starting the daemon. The daemon searches up for this file and uses its
package/dist/main.js CHANGED
@@ -31258,12 +31258,21 @@ if (!etc.sha512Sync) etc.sha512Sync = (...m) => {
31258
31258
  */
31259
31259
  async function isContinuationClaimableByThisDaemon(task, slotRegistry) {
31260
31260
  const cf = task.input?.continueFrom;
31261
- if (!cf) return true;
31261
+ if (!cf) return { claimable: true };
31262
31262
  const slot = await slotRegistry.findLatestProducerSlotByTaskAttempt(cf.taskId, cf.attemptN);
31263
- if (!slot) return false;
31263
+ if (!slot) return {
31264
+ claimable: false,
31265
+ reason: "missing_producer_slot",
31266
+ continueFrom: cf
31267
+ };
31264
31268
  const sessionDir = slot.session?.sessionDir;
31265
- if (!sessionDir || !existsSync(sessionDir)) return false;
31266
- return true;
31269
+ if (!sessionDir || !existsSync(sessionDir)) return {
31270
+ claimable: false,
31271
+ reason: "missing_session_dir",
31272
+ continueFrom: cf,
31273
+ sessionDir
31274
+ };
31275
+ return { claimable: true };
31267
31276
  }
31268
31277
  var DEFAULT_LIST_LIMIT = 10;
31269
31278
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
@@ -31351,7 +31360,19 @@ var PollingApiTaskSource = class {
31351
31360
  if (seen.has(item.id)) continue;
31352
31361
  if (this.opts.taskTypes && this.opts.taskTypes.length > 0 && !this.opts.taskTypes.includes(item.taskType)) continue;
31353
31362
  if (this.opts.diaryIds && this.opts.diaryIds.length > 0 && (item.diaryId === null || !this.opts.diaryIds.includes(item.diaryId))) continue;
31354
- if (this.opts.slotRegistry && !await isContinuationClaimableByThisDaemon(item, this.opts.slotRegistry)) continue;
31363
+ if (this.opts.slotRegistry) {
31364
+ const affinity = await isContinuationClaimableByThisDaemon(item, this.opts.slotRegistry);
31365
+ if (!affinity.claimable) {
31366
+ this.logger.debug({
31367
+ taskId: item.id,
31368
+ taskType: item.taskType,
31369
+ reason: affinity.reason,
31370
+ continueFrom: affinity.continueFrom,
31371
+ sessionDir: affinity.sessionDir
31372
+ }, "polling-api.continuation_skipped");
31373
+ continue;
31374
+ }
31375
+ }
31355
31376
  if (this.opts.profileId) {
31356
31377
  const allowed = item.allowedProfiles ?? [];
31357
31378
  if (allowed.length > 0 && !allowed.some((p) => p.profileId === this.opts.profileId)) continue;
@@ -34785,9 +34806,13 @@ function loadConfig() {
34785
34806
  otelEndpoint: process.env["MOLTNET_OTEL_ENDPOINT"] ?? "",
34786
34807
  logLevel: process.env["LOG_LEVEL"] ?? "",
34787
34808
  profilePrerequisiteEnv: process.env,
34788
- profilePrerequisitePath: process.env.PATH ?? ""
34809
+ profilePrerequisitePath: process.env.PATH ?? "",
34810
+ piCodingAgentDir: process.env["PI_CODING_AGENT_DIR"] ?? ""
34789
34811
  };
34790
34812
  }
34813
+ function activatePiCodingAgentDir(path) {
34814
+ process.env["PI_CODING_AGENT_DIR"] = path;
34815
+ }
34791
34816
  //#endregion
34792
34817
  //#region src/lib/agent-context.ts
34793
34818
  /**
@@ -35052,6 +35077,7 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
35052
35077
  return {
35053
35078
  ...basePlan,
35054
35079
  workspaceMode: "dedicated_worktree",
35080
+ workspaceId: resolution.producerSlot.workspace?.workspaceId ?? null,
35055
35081
  worktreeBranch: resolution.producerSlot.workspace?.worktreeBranch ?? null,
35056
35082
  sessionPersistence: {
35057
35083
  sessionDir: `${stateDirs.piSessionsDir}/continue-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`,
@@ -35374,6 +35400,23 @@ async function initWorkerOtel(options) {
35374
35400
  };
35375
35401
  }
35376
35402
  //#endregion
35403
+ //#region src/lib/pi-agent-dir.ts
35404
+ function ensurePiAgentDir(repoRoot, explicitPath) {
35405
+ if (explicitPath) {
35406
+ mkdirSync(explicitPath, { recursive: true });
35407
+ return {
35408
+ path: explicitPath,
35409
+ source: "env"
35410
+ };
35411
+ }
35412
+ const path = join(repoRoot, ".pi");
35413
+ mkdirSync(path, { recursive: true });
35414
+ return {
35415
+ path,
35416
+ source: "repo"
35417
+ };
35418
+ }
35419
+ //#endregion
35377
35420
  //#region src/lib/runtime-profile.ts
35378
35421
  var RuntimeProfilePrerequisiteError = class extends Error {
35379
35422
  constructor(profileName, missingEnv, missingTools) {
@@ -35614,6 +35657,8 @@ async function runPolling(opts) {
35614
35657
  rootDir: profile.mountPath,
35615
35658
  path: profile.source
35616
35659
  } : resolveSandbox(process.cwd(), values.sandbox);
35660
+ const piAgentDir = ensurePiAgentDir(sandbox.rootDir, cfg.piCodingAgentDir);
35661
+ activatePiCodingAgentDir(piAgentDir.path);
35617
35662
  const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
35618
35663
  const slotRegistry = new DaemonSlotRegistry(resolveDaemonStateStorageConfig(stateDirs.registryDbPath, cfg.agentDaemonStateDatabaseUrl));
35619
35664
  const slotIdentity = {
@@ -35691,7 +35736,9 @@ async function runPolling(opts) {
35691
35736
  profileId: profile.id,
35692
35737
  profileSessionTtlSec: profile.sessionTtlSec,
35693
35738
  profileWorkspaceTtlSec: profile.workspaceTtlSec
35694
- } : {}
35739
+ } : {},
35740
+ piAgentDir: piAgentDir.path,
35741
+ piAgentDirSource: piAgentDir.source
35695
35742
  }, "agent-daemon.starting");
35696
35743
  const outputs = [];
35697
35744
  try {
@@ -35967,6 +36014,8 @@ async function runOnce(argv) {
35967
36014
  rootDir: profile.mountPath,
35968
36015
  path: profile.source
35969
36016
  } : resolveSandbox(process.cwd(), values.sandbox);
36017
+ const piAgentDir = ensurePiAgentDir(sandbox.rootDir, cfg.piCodingAgentDir);
36018
+ activatePiCodingAgentDir(piAgentDir.path);
35970
36019
  const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
35971
36020
  const slotRegistry = new DaemonSlotRegistry(resolveDaemonStateStorageConfig(stateDirs.registryDbPath, cfg.agentDaemonStateDatabaseUrl));
35972
36021
  const slotIdentity = {
@@ -36017,7 +36066,9 @@ async function runOnce(argv) {
36017
36066
  profileId: profile.id,
36018
36067
  profileSessionTtlSec: profile.sessionTtlSec,
36019
36068
  profileWorkspaceTtlSec: profile.workspaceTtlSec
36020
- } : {}
36069
+ } : {},
36070
+ piAgentDir: piAgentDir.path,
36071
+ piAgentDirSource: piAgentDir.source
36021
36072
  }, "agent-daemon.starting");
36022
36073
  let runtime = null;
36023
36074
  let activeAttemptN = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.17.0",
3
+ "version": "0.18.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.",
@@ -46,9 +46,9 @@
46
46
  "pino": "^10.3.1",
47
47
  "pino-pretty": "^13.1.3",
48
48
  "@themoltnet/agent-daemon-state": "0.2.0",
49
- "@themoltnet/sdk": "0.108.0",
50
- "@themoltnet/agent-runtime": "0.24.0",
51
- "@themoltnet/pi-extension": "0.23.1"
49
+ "@themoltnet/agent-runtime": "0.25.0",
50
+ "@themoltnet/pi-extension": "0.24.0",
51
+ "@themoltnet/sdk": "0.108.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "tsx": "^4.7.0",
@@ -57,8 +57,8 @@
57
57
  "vitest": "^3.0.0",
58
58
  "@moltnet/bootstrap": "0.1.0",
59
59
  "@moltnet/crypto-service": "0.1.0",
60
- "@moltnet/observability": "0.1.0",
61
- "@moltnet/tasks": "0.1.0"
60
+ "@moltnet/tasks": "0.1.0",
61
+ "@moltnet/observability": "0.1.0"
62
62
  },
63
63
  "nx": {
64
64
  "tags": [