@themoltnet/pi-extension 0.24.0 → 0.25.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/dist/index.d.ts CHANGED
@@ -505,6 +505,12 @@ export declare interface PiTaskExecutionPlan {
505
505
  * worktree is required.
506
506
  */
507
507
  worktreeBranch: string | null;
508
+ /**
509
+ * Base ref a NEW `worktreeBranch` is cut from. Used by `fork` continuations
510
+ * to branch from the parent's tip instead of the default (main/HEAD). Ignored
511
+ * when `worktreeBranch` already exists.
512
+ */
513
+ worktreeBaseRef?: string | null;
508
514
  /**
509
515
  * Lifetime of the task workspace from the daemon's point of view.
510
516
  * `attempt` = disposable; `session` = keep stable for the reuse key.
package/dist/index.js CHANGED
@@ -8824,8 +8824,7 @@ async function resumeVm(config) {
8824
8824
  signal: config.signal
8825
8825
  });
8826
8826
  if (creds.gitconfig) {
8827
- const vmSigningKey = `${vmSshDir}/id_ed25519`;
8828
- const vmGitconfig = creds.gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSigningKey}`);
8827
+ const vmGitconfig = rewriteGitconfigPaths(creds.gitconfig, vmSshDir, vmAgentDir);
8829
8828
  await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
8830
8829
  mode: 420,
8831
8830
  signal: config.signal
@@ -8848,17 +8847,6 @@ async function resumeVm(config) {
8848
8847
  signal: config.signal
8849
8848
  });
8850
8849
  await vm.exec("chown -R agent:agent /home/agent/.pi /home/agent/.moltnet", { signal: config.signal });
8851
- const gitCredHelperPath = `${vmSshDir}/git-credential-moltnet`;
8852
- const credHelperScript = `#!/bin/sh
8853
- echo "username=x-access-token"
8854
- echo "password=$(moltnet github token --credentials ${vmSshDir}/moltnet.json)"
8855
- `;
8856
- await vm.fs.writeFile(gitCredHelperPath, credHelperScript, {
8857
- mode: 493,
8858
- signal: config.signal
8859
- });
8860
- await vmRun(vm, "git credential helper", `git config --global credential.helper ${gitCredHelperPath} && \
8861
- git config --global url."https://github.com/".insteadOf "git@github.com:"`, config.signal);
8862
8850
  return {
8863
8851
  vm,
8864
8852
  credentials: creds,
@@ -8877,6 +8865,30 @@ echo "password=$(moltnet github token --credentials ${vmSshDir}/moltnet.json)"
8877
8865
  }
8878
8866
  }
8879
8867
  /**
8868
+ * Rewrite host-absolute paths inside an agent gitconfig to VM-local
8869
+ * equivalents before injecting it into the guest.
8870
+ *
8871
+ * Two rewrites:
8872
+ * - `signingKey = <host path>` → `<vmSshDir>/id_ed25519`
8873
+ * - `... credential-helper --credentials <host moltnet.json>`
8874
+ * → `<vmAgentDir>/moltnet.json`
8875
+ *
8876
+ * The credential-helper line is generated host-side by `moltnet github setup`
8877
+ * with a host-absolute `--credentials` path; inside the guest that path is
8878
+ * invalid, so it must point at the VM-side moltnet.json. The `insteadOf`
8879
+ * rewrite rule and every other line are workspace-independent and pass through
8880
+ * unchanged. A gitconfig without a credential helper is rewritten only for
8881
+ * `signingKey`.
8882
+ *
8883
+ * This is the single source of truth for git push auth in the guest: the
8884
+ * injected gitconfig carries the tokenless mint-on-demand helper, so the VM
8885
+ * no longer hand-rolls a credential-helper script or runs an imperative
8886
+ * `git config --global ... insteadOf` against the guest $HOME.
8887
+ */
8888
+ function rewriteGitconfigPaths(gitconfig, vmSshDir, vmAgentDir) {
8889
+ return gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSshDir}/id_ed25519`).replace(/(moltnet github credential-helper --credentials )\S+/g, `$1${vmAgentDir}/moltnet.json`);
8890
+ }
8891
+ /**
8880
8892
  * Rewrite host-absolute paths inside moltnet.json to VM-local equivalents.
8881
8893
  *
8882
8894
  * Fields rewritten:
@@ -13470,6 +13482,54 @@ function validateRubricWeights(rubric) {
13470
13482
  return null;
13471
13483
  }
13472
13484
  //#endregion
13485
+ //#region ../tasks/src/runtime-models.ts
13486
+ /**
13487
+ * Runtime model catalog: a list of supported provider/model couples that
13488
+ * MoltNet daemons can target. Backed by the `runtime_models` table.
13489
+ *
13490
+ * Scope is intrinsic to the row:
13491
+ * - `teamId == null` => global entry (MoltNet-seeded, read-only to most callers)
13492
+ * - `teamId != null` => team-owned custom entry
13493
+ *
13494
+ * The REST API exposes a single shape regardless of scope; the team header
13495
+ * gates which rows are returned.
13496
+ */
13497
+ var RuntimeModelProvider = String$1({
13498
+ minLength: 1,
13499
+ maxLength: 100,
13500
+ pattern: "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$"
13501
+ });
13502
+ var RuntimeModelName = String$1({
13503
+ minLength: 1,
13504
+ maxLength: 200,
13505
+ pattern: "^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$"
13506
+ });
13507
+ var RuntimeModelCapabilities = Record(String$1({
13508
+ minLength: 1,
13509
+ maxLength: 64
13510
+ }), Union([
13511
+ Boolean$1(),
13512
+ Number$1(),
13513
+ String$1({ maxLength: 256 })
13514
+ ]));
13515
+ _Object_({
13516
+ id: String$1({ format: "uuid" }),
13517
+ teamId: Union([String$1({ format: "uuid" }), Null()]),
13518
+ provider: RuntimeModelProvider,
13519
+ model: RuntimeModelName,
13520
+ displayName: Union([String$1({ maxLength: 200 }), Null()]),
13521
+ description: Union([String$1({ maxLength: 4096 }), Null()]),
13522
+ capabilities: RuntimeModelCapabilities,
13523
+ isActive: Boolean$1(),
13524
+ createdByAgentId: Union([String$1({ format: "uuid" }), Null()]),
13525
+ createdByHumanId: Union([String$1({ format: "uuid" }), Null()]),
13526
+ createdAt: String$1({ format: "date-time" }),
13527
+ updatedAt: String$1({ format: "date-time" })
13528
+ }, {
13529
+ $id: "RuntimeModel",
13530
+ additionalProperties: false
13531
+ });
13532
+ //#endregion
13473
13533
  //#region ../tasks/src/runtime-profiles.ts
13474
13534
  var RuntimeProfileName = String$1({
13475
13535
  minLength: 1,
@@ -14010,20 +14070,17 @@ var FreeformOutput = _Object_({
14010
14070
  * 3. `freeform.sourceAttemptNotCompleted` — named attempt is missing
14011
14071
  * or not in `completed` state; warm continuation only makes sense
14012
14072
  * once the parent has produced a terminal output.
14013
- * 4. `freeform.forkModeNotImplemented` — `mode: 'fork'` is the wire
14014
- * surface for copy-on-write continuation tracked in #1293; v1
14015
- * rejects it server-side so daemons never have to branch.
14016
- * 5. `freeform.executionWorkspaceNotInheritable` — caller set
14073
+ * 4. `freeform.executionWorkspaceNotInheritable` — caller set
14017
14074
  * `execution.workspace` together with `continueFrom`. Workspace
14018
14075
  * mode for a continuation is inherited from the parent slot
14019
14076
  * (`maybeAttachWarmSlotContext` forces `dedicated_worktree` +
14020
14077
  * the parent's worktreeBranch), so any caller-supplied override
14021
14078
  * is silently dropped at the daemon plan stage. Reject explicitly
14022
14079
  * so misconfiguration surfaces at create time.
14023
- * 6. `freeform.sourceNotResumeEligible` — `daemonState` is null or
14080
+ * 5. `freeform.sourceNotResumeEligible` — `daemonState` is null or
14024
14081
  * `slotResumableUntil` is null. Older completions (pre-#1287) and
14025
14082
  * daemons that opt out fall here.
14026
- * 7. `freeform.sourceResumeExpired` — `slotResumableUntil` is in the
14083
+ * 6. `freeform.sourceResumeExpired` — `slotResumableUntil` is in the
14027
14084
  * past; the warm slot's TTL has elapsed and no daemon is
14028
14085
  * guaranteed to still hold it.
14029
14086
  *
@@ -14044,11 +14101,6 @@ async function validateFreeformInputAsync(input, ctx) {
14044
14101
  message: `Source task type '${source.taskType}' is not continuable; only freeform → freeform is supported in v1`,
14045
14102
  code: "freeform.sourceTaskTypeNotSupported"
14046
14103
  }];
14047
- if (cf.mode === "fork") return [{
14048
- field: "input/continueFrom/mode",
14049
- message: "fork mode not yet implemented; see https://github.com/getlarge/themoltnet/issues/1293",
14050
- code: "freeform.forkModeNotImplemented"
14051
- }];
14052
14104
  if (input.execution?.workspace) return [{
14053
14105
  field: "input/execution/workspace",
14054
14106
  message: "execution.workspace is inherited from the parent slot when continueFrom is set; omit it",
@@ -23203,10 +23255,11 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
23203
23255
  const relMount = relative(mainRepo, requestedMountPath);
23204
23256
  const cwdPath = relMount === "" || relMount.startsWith("..") ? worktreeDir : join(worktreeDir, relMount);
23205
23257
  const keepWorkspace = executionPlan?.workspaceScope === "session" && executionPlan.sessionKey !== null;
23206
- if (keepWorkspace) ensureReusableTaskWorktree(mainRepo, worktreeDir, branch);
23258
+ const baseRefOverride = executionPlan?.worktreeBaseRef ?? null;
23259
+ if (keepWorkspace) ensureReusableTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride);
23207
23260
  else {
23208
23261
  removeExistingTaskWorktree(mainRepo, worktreeDir);
23209
- addTaskWorktree(mainRepo, worktreeDir, branch);
23262
+ addTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride);
23210
23263
  }
23211
23264
  return {
23212
23265
  mountPath: mainRepo,
@@ -23231,14 +23284,15 @@ function resolveTaskWorktreePath(mainRepo, workspaceId) {
23231
23284
  function resolveTaskScratchPath(mainRepo, workspaceId) {
23232
23285
  return join(mainRepo, ".moltnet", "d", "task-workspaces", workspaceId);
23233
23286
  }
23234
- function ensureReusableTaskWorktree(mainRepo, worktreeDir, branch) {
23287
+ function ensureReusableTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride = null) {
23235
23288
  if (isRegisteredWorktree(mainRepo, worktreeDir)) return;
23236
23289
  if (existsSync(worktreeDir)) throw new Error(`Expected reusable worktree ${worktreeDir} to be git-managed, but it exists outside git worktree metadata.`);
23237
- addTaskWorktree(mainRepo, worktreeDir, branch);
23290
+ addTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride);
23238
23291
  }
23239
- function addTaskWorktree(mainRepo, worktreeDir, branch) {
23240
- const baseRef = resolveWorktreeBaseRef(mainRepo);
23241
- execFileSync("git", gitRefExists(mainRepo, `refs/heads/${branch}`) ? [
23292
+ function addTaskWorktree(mainRepo, worktreeDir, branch, baseRefOverride = null) {
23293
+ const branchExists = gitRefExists(mainRepo, `refs/heads/${branch}`);
23294
+ const baseRef = baseRefOverride ?? resolveWorktreeBaseRef(mainRepo);
23295
+ execFileSync("git", branchExists ? [
23242
23296
  "-C",
23243
23297
  mainRepo,
23244
23298
  "worktree",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/pi-extension",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "type": "module",
5
5
  "description": "MoltNet pi extension — sandboxed tool execution in Gondolin VMs with MoltNet identity and persistent memory",
6
6
  "keywords": [
@@ -36,8 +36,8 @@
36
36
  "@earendil-works/gondolin": "^0.9.1",
37
37
  "@opentelemetry/api": "^1.9.0",
38
38
  "typebox": "^1.2.8",
39
- "@themoltnet/agent-runtime": "0.25.0",
40
- "@themoltnet/sdk": "0.108.0"
39
+ "@themoltnet/sdk": "0.108.0",
40
+ "@themoltnet/agent-runtime": "0.25.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@earendil-works/pi-coding-agent": ">=0.74.0",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@earendil-works/pi-ai": "^0.74.0",
56
- "@earendil-works/pi-coding-agent": "^0.74.0",
56
+ "@earendil-works/pi-coding-agent": "^0.79.4",
57
57
  "@opentelemetry/sdk-metrics": "^2.5.1",
58
58
  "@opentelemetry/sdk-trace-base": "^2.5.1",
59
59
  "@types/node": "^22.19.0",
@@ -61,8 +61,8 @@
61
61
  "vite": "^8.0.0",
62
62
  "vite-plugin-dts": "^4.5.4",
63
63
  "vitest": "^3.0.0",
64
- "@moltnet/crypto-service": "0.1.0",
65
- "@moltnet/tasks": "0.1.0"
64
+ "@moltnet/tasks": "0.1.0",
65
+ "@moltnet/crypto-service": "0.1.0"
66
66
  },
67
67
  "engines": {
68
68
  "node": ">=22"