@themoltnet/pi-extension 0.22.5 → 0.23.1

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
@@ -718,9 +718,8 @@ declare const Task: Type.TObject<{
718
718
  acceptedAttemptN: Type.TUnion<[Type.TNumber, Type.TNull]>;
719
719
  claimCondition: Type.TUnion<[Type.TUnsafe<ClaimCondition>, Type.TNull]>;
720
720
  requiredExecutorTrustLevel: Type.TUnion<[Type.TLiteral<"selfDeclared">, Type.TLiteral<"agentSigned">, Type.TLiteral<"releaseVerifiedTool">, Type.TLiteral<"sandboxAttested">]>;
721
- allowedExecutors: Type.TArray<Type.TObject<{
722
- provider: Type.TString;
723
- model: Type.TString;
721
+ allowedProfiles: Type.TArray<Type.TObject<{
722
+ profileId: Type.TString;
724
723
  }>>;
725
724
  status: Type.TUnion<[Type.TLiteral<"waiting">, Type.TLiteral<"queued">, Type.TLiteral<"dispatched">, Type.TLiteral<"running">, Type.TLiteral<"completed">, Type.TLiteral<"failed">, Type.TLiteral<"cancelled">, Type.TLiteral<"expired">]>;
726
725
  queuedAt: Type.TString;
@@ -844,6 +843,12 @@ declare interface TaskReporter {
844
843
  * cancellation has been observed. Null until `cancelSignal` aborts.
845
844
  */
846
845
  readonly cancelReason: string | null;
846
+ /**
847
+ * Request local cancellation of the in-flight task. Runtime shutdown uses
848
+ * this to trip the same executor-facing signal as proposer cancellation,
849
+ * without waiting for the next server heartbeat.
850
+ */
851
+ requestCancel?(reason: string): void;
847
852
  }
848
853
 
849
854
  declare const TaskStatus: Type.TUnion<[Type.TLiteral<"waiting">, Type.TLiteral<"queued">, Type.TLiteral<"dispatched">, Type.TLiteral<"running">, Type.TLiteral<"completed">, Type.TLiteral<"failed">, Type.TLiteral<"cancelled">, Type.TLiteral<"expired">]>;
@@ -902,6 +907,8 @@ export declare interface VmConfig {
902
907
  extraAllowedHosts?: string[];
903
908
  /** Full sandbox config (vfs shadows, env overrides). */
904
909
  sandboxConfig?: SandboxConfig;
910
+ /** Abort resume/setup work, closing any live VM owned by resumeVm. */
911
+ signal?: AbortSignal;
905
912
  }
906
913
 
907
914
  export declare interface VmCredentials {
package/dist/index.js CHANGED
@@ -1674,6 +1674,124 @@ var updateRenderedPack = (options) => (options.client ?? client).patch({
1674
1674
  }
1675
1675
  });
1676
1676
  /**
1677
+ * List runtime profiles for the active team context.
1678
+ */
1679
+ var listRuntimeProfiles = (options) => (options?.client ?? client).get({
1680
+ security: [
1681
+ {
1682
+ scheme: "bearer",
1683
+ type: "http"
1684
+ },
1685
+ {
1686
+ name: "X-Moltnet-Session-Token",
1687
+ type: "apiKey"
1688
+ },
1689
+ {
1690
+ in: "cookie",
1691
+ name: "ory_kratos_session",
1692
+ type: "apiKey"
1693
+ }
1694
+ ],
1695
+ url: "/runtime-profiles",
1696
+ ...options
1697
+ });
1698
+ /**
1699
+ * Create a runtime profile for the active team context.
1700
+ */
1701
+ var createRuntimeProfile = (options) => (options?.client ?? client).post({
1702
+ security: [
1703
+ {
1704
+ scheme: "bearer",
1705
+ type: "http"
1706
+ },
1707
+ {
1708
+ name: "X-Moltnet-Session-Token",
1709
+ type: "apiKey"
1710
+ },
1711
+ {
1712
+ in: "cookie",
1713
+ name: "ory_kratos_session",
1714
+ type: "apiKey"
1715
+ }
1716
+ ],
1717
+ url: "/runtime-profiles",
1718
+ ...options,
1719
+ headers: {
1720
+ "Content-Type": "application/json",
1721
+ ...options?.headers
1722
+ }
1723
+ });
1724
+ /**
1725
+ * Delete one runtime profile.
1726
+ */
1727
+ var deleteRuntimeProfile = (options) => (options.client ?? client).delete({
1728
+ security: [
1729
+ {
1730
+ scheme: "bearer",
1731
+ type: "http"
1732
+ },
1733
+ {
1734
+ name: "X-Moltnet-Session-Token",
1735
+ type: "apiKey"
1736
+ },
1737
+ {
1738
+ in: "cookie",
1739
+ name: "ory_kratos_session",
1740
+ type: "apiKey"
1741
+ }
1742
+ ],
1743
+ url: "/runtime-profiles/{profileId}",
1744
+ ...options
1745
+ });
1746
+ /**
1747
+ * Get one runtime profile.
1748
+ */
1749
+ var getRuntimeProfile = (options) => (options.client ?? client).get({
1750
+ security: [
1751
+ {
1752
+ scheme: "bearer",
1753
+ type: "http"
1754
+ },
1755
+ {
1756
+ name: "X-Moltnet-Session-Token",
1757
+ type: "apiKey"
1758
+ },
1759
+ {
1760
+ in: "cookie",
1761
+ name: "ory_kratos_session",
1762
+ type: "apiKey"
1763
+ }
1764
+ ],
1765
+ url: "/runtime-profiles/{profileId}",
1766
+ ...options
1767
+ });
1768
+ /**
1769
+ * Update one runtime profile.
1770
+ */
1771
+ var updateRuntimeProfile = (options) => (options.client ?? client).patch({
1772
+ security: [
1773
+ {
1774
+ scheme: "bearer",
1775
+ type: "http"
1776
+ },
1777
+ {
1778
+ name: "X-Moltnet-Session-Token",
1779
+ type: "apiKey"
1780
+ },
1781
+ {
1782
+ in: "cookie",
1783
+ name: "ory_kratos_session",
1784
+ type: "apiKey"
1785
+ }
1786
+ ],
1787
+ url: "/runtime-profiles/{profileId}",
1788
+ ...options,
1789
+ headers: {
1790
+ "Content-Type": "application/json",
1791
+ ...options.headers
1792
+ }
1793
+ });
1794
+ /**
1677
1795
  * List tasks for a team with optional filters.
1678
1796
  */
1679
1797
  var listTasks = (options) => (options.client ?? client).get({
@@ -1788,6 +1906,32 @@ var listTaskAttempts = (options) => (options.client ?? client).get({
1788
1906
  ...options
1789
1907
  });
1790
1908
  /**
1909
+ * 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.
1910
+ */
1911
+ var abortTaskAttempt = (options) => (options.client ?? client).post({
1912
+ security: [
1913
+ {
1914
+ scheme: "bearer",
1915
+ type: "http"
1916
+ },
1917
+ {
1918
+ name: "X-Moltnet-Session-Token",
1919
+ type: "apiKey"
1920
+ },
1921
+ {
1922
+ in: "cookie",
1923
+ name: "ory_kratos_session",
1924
+ type: "apiKey"
1925
+ }
1926
+ ],
1927
+ url: "/tasks/{id}/attempts/{n}/abort",
1928
+ ...options,
1929
+ headers: {
1930
+ "Content-Type": "application/json",
1931
+ ...options.headers
1932
+ }
1933
+ });
1934
+ /**
1791
1935
  * Mark an attempt as completed with output.
1792
1936
  */
1793
1937
  var completeTask = (options) => (options.client ?? client).post({
@@ -4696,6 +4840,54 @@ function createRecoveryNamespace(context) {
4696
4840
  };
4697
4841
  }
4698
4842
  //#endregion
4843
+ //#region ../sdk/src/namespaces/runtime-profiles.ts
4844
+ function createRuntimeProfilesNamespace(context) {
4845
+ const { client, auth } = context;
4846
+ return {
4847
+ async list(options) {
4848
+ return unwrapResult(await listRuntimeProfiles({
4849
+ client,
4850
+ auth,
4851
+ headers: teamHeaders(options)
4852
+ }));
4853
+ },
4854
+ async create(body, options) {
4855
+ return unwrapResult(await createRuntimeProfile({
4856
+ client,
4857
+ auth,
4858
+ headers: teamHeaders(options),
4859
+ body
4860
+ }));
4861
+ },
4862
+ async get(profileId) {
4863
+ return unwrapResult(await getRuntimeProfile({
4864
+ client,
4865
+ auth,
4866
+ path: { profileId }
4867
+ }));
4868
+ },
4869
+ async update(profileId, body) {
4870
+ return unwrapResult(await updateRuntimeProfile({
4871
+ client,
4872
+ auth,
4873
+ path: { profileId },
4874
+ body
4875
+ }));
4876
+ },
4877
+ async delete(profileId) {
4878
+ const result = await deleteRuntimeProfile({
4879
+ client,
4880
+ auth,
4881
+ path: { profileId }
4882
+ });
4883
+ if (result.error) unwrapResult(result);
4884
+ }
4885
+ };
4886
+ }
4887
+ function teamHeaders(options) {
4888
+ return options?.teamId ? { "x-moltnet-team-id": options.teamId } : void 0;
4889
+ }
4890
+ //#endregion
4699
4891
  //#region ../sdk/src/namespaces/signing-requests.ts
4700
4892
  function createSigningRequestsNamespace(context) {
4701
4893
  const { client, auth } = context;
@@ -4816,6 +5008,17 @@ function createTasksNamespace(context) {
4816
5008
  body
4817
5009
  }));
4818
5010
  },
5011
+ async abortAttempt(id, n, body) {
5012
+ return unwrapResult(await abortTaskAttempt({
5013
+ client,
5014
+ auth,
5015
+ path: {
5016
+ id,
5017
+ n
5018
+ },
5019
+ body
5020
+ }));
5021
+ },
4819
5022
  async cancel(id, body) {
4820
5023
  return unwrapResult(await cancelTask({
4821
5024
  client,
@@ -4999,6 +5202,7 @@ function createAgent(options) {
4999
5202
  legreffier: createLegreffierNamespace(context),
5000
5203
  problems: createProblemsNamespace(context),
5001
5204
  teams: createTeamsNamespace(context),
5205
+ runtimeProfiles: createRuntimeProfilesNamespace(context),
5002
5206
  tasks: createTasksNamespace(context),
5003
5207
  client,
5004
5208
  getToken: () => tokenManager.getToken()
@@ -8310,6 +8514,63 @@ function pruneOldSnapshots(maxCached, currentDir) {
8310
8514
  });
8311
8515
  }
8312
8516
  //#endregion
8517
+ //#region src/abort-utils.ts
8518
+ function throwIfAborted(signal, label) {
8519
+ if (!signal?.aborted) return;
8520
+ throw abortError(label, signal);
8521
+ }
8522
+ function abortError(label, signal) {
8523
+ const reason = signal.reason;
8524
+ const suffix = reason instanceof Error ? reason.message : reason === void 0 ? "aborted" : String(reason);
8525
+ const err = /* @__PURE__ */ new Error(`${label} aborted: ${suffix}`);
8526
+ err.name = "AbortError";
8527
+ return err;
8528
+ }
8529
+ function cleanupLateResource(resourcePromise, opts) {
8530
+ resourcePromise.then(async (resource) => {
8531
+ try {
8532
+ await opts.cleanup(resource);
8533
+ } catch (err) {
8534
+ opts.onCleanupError?.(err);
8535
+ }
8536
+ }, () => {});
8537
+ }
8538
+ async function abortableResource(opts) {
8539
+ const { signal } = opts;
8540
+ if (!signal) return opts.promise;
8541
+ throwIfAborted(signal, opts.label);
8542
+ const resourcePromise = Promise.resolve(opts.promise);
8543
+ const abortPromise = new Promise((_, reject) => {
8544
+ const abort = () => {
8545
+ cleanupLateResource(resourcePromise, opts);
8546
+ reject(abortError(opts.label, signal));
8547
+ };
8548
+ signal.addEventListener("abort", abort, { once: true });
8549
+ resourcePromise.then(() => signal.removeEventListener("abort", abort), () => signal.removeEventListener("abort", abort));
8550
+ });
8551
+ return Promise.race([resourcePromise, abortPromise]);
8552
+ }
8553
+ async function delay(ms, signal, label) {
8554
+ if (!signal) {
8555
+ await new Promise((resolve) => {
8556
+ setTimeout(resolve, ms);
8557
+ });
8558
+ return;
8559
+ }
8560
+ throwIfAborted(signal, label);
8561
+ await new Promise((resolve, reject) => {
8562
+ const listener = () => {
8563
+ clearTimeout(timeout);
8564
+ reject(abortError(label, signal));
8565
+ };
8566
+ const timeout = setTimeout(() => {
8567
+ signal.removeEventListener("abort", listener);
8568
+ resolve();
8569
+ }, ms);
8570
+ signal.addEventListener("abort", listener, { once: true });
8571
+ });
8572
+ }
8573
+ //#endregion
8313
8574
  //#region src/vm-manager.ts
8314
8575
  /**
8315
8576
  * Memory-backed VFS mount used by the daemon to inject task-context
@@ -8426,23 +8687,33 @@ var BASE_ALLOWED_HOSTS = [
8426
8687
  * surface immediately rather than fall through to cryptic agent
8427
8688
  * errors later.
8428
8689
  */
8429
- async function vmRun(vm, label, command) {
8690
+ async function vmRun(vm, label, command, signal) {
8430
8691
  const wrapped = `set -eu\nset -o pipefail\n${command}`;
8692
+ throwIfAborted(signal, `resume step "${label}"`);
8431
8693
  const r = await vm.exec([
8432
8694
  "sh",
8433
8695
  "-c",
8434
8696
  wrapped
8435
- ]);
8697
+ ], { signal });
8436
8698
  if (r.exitCode !== 0) {
8437
8699
  const tail = [r.stderr, r.stdout].filter(Boolean).join("\n").slice(-800);
8438
8700
  throw new Error(`resume step "${label}" failed (exit ${r.exitCode}):\n${tail}`);
8439
8701
  }
8440
8702
  }
8703
+ function nonErrorMessage(err) {
8704
+ if (typeof err === "string") return err;
8705
+ try {
8706
+ return JSON.stringify(err) ?? "unknown error";
8707
+ } catch {
8708
+ return "unknown error";
8709
+ }
8710
+ }
8441
8711
  /**
8442
8712
  * Resume a VM from a checkpoint, inject credentials, configure egress +
8443
8713
  * TLS. Returns the managed VM handle.
8444
8714
  */
8445
8715
  async function resumeVm(config) {
8716
+ throwIfAborted(config.signal, "VM resume");
8446
8717
  const mainRepo = findMainWorktree();
8447
8718
  const agentDir = path.join(mainRepo, ".moltnet", config.agentName);
8448
8719
  const guestWorkspace = path.resolve(config.mountPath);
@@ -8486,24 +8757,33 @@ async function resumeVm(config) {
8486
8757
  };
8487
8758
  const resources = config.sandboxConfig?.resources;
8488
8759
  const workspaceMode = config.workspaceMode ?? "shared_mount";
8489
- const vm = await VmCheckpoint.load(config.checkpointPath).resume({
8490
- httpHooks,
8491
- env: vmEnv,
8492
- ...resources?.memory && { memory: resources.memory },
8493
- ...resources?.cpus && { cpus: resources.cpus },
8494
- vfs: { mounts: {
8495
- [guestWorkspace]: workspaceProvider,
8496
- [GUEST_TASK_SKILLS_MOUNT]: new MemoryProvider()
8497
- } }
8760
+ const vm = await abortableResource({
8761
+ promise: VmCheckpoint.load(config.checkpointPath).resume({
8762
+ httpHooks,
8763
+ env: vmEnv,
8764
+ ...resources?.memory && { memory: resources.memory },
8765
+ ...resources?.cpus && { cpus: resources.cpus },
8766
+ vfs: { mounts: {
8767
+ [guestWorkspace]: workspaceProvider,
8768
+ [GUEST_TASK_SKILLS_MOUNT]: new MemoryProvider()
8769
+ } }
8770
+ }),
8771
+ signal: config.signal,
8772
+ label: "VM resume",
8773
+ cleanup: (resumedVm) => resumedVm.close(),
8774
+ onCleanupError: (err) => {
8775
+ const message = err instanceof Error ? err.message : String(err);
8776
+ process.stderr.write(`[vm] aborted resume late vm.close() failed: ${message}\n`);
8777
+ }
8498
8778
  });
8499
8779
  try {
8500
- await vm.exec(`sh -c '
8780
+ await vmRun(vm, "TLS certificates", `
8501
8781
  cp /etc/gondolin/mitm/ca.crt /usr/local/share/ca-certificates/gondolin-mitm.crt
8502
8782
  update-ca-certificates 2>/dev/null
8503
8783
  cat /etc/gondolin/mitm/ca.crt >> /etc/ssl/certs/ca-certificates.crt
8504
- '`);
8505
- await vmRun(vm, "DNS resolvers", `printf 'nameserver 8.8.8.8\\nnameserver 1.1.1.1\\n' > /etc/resolv.conf`);
8506
- await vmRun(vm, "git safe.directory", `git config --system --add safe.directory '*'`);
8784
+ `, config.signal);
8785
+ await vmRun(vm, "DNS resolvers", `printf 'nameserver 8.8.8.8\\nnameserver 1.1.1.1\\n' > /etc/resolv.conf`, config.signal);
8786
+ await vmRun(vm, "git safe.directory", `git config --system --add safe.directory '*'`, config.signal);
8507
8787
  for (const [i, entry] of (config.sandboxConfig?.resumeCommands ?? []).entries()) {
8508
8788
  if (!shouldRunResumeCommand(entry, { workspaceMode })) continue;
8509
8789
  const { run, retries, backoffMs } = typeof entry === "string" ? {
@@ -8518,34 +8798,67 @@ async function resumeVm(config) {
8518
8798
  const label = `resumeCommands[${i}]`;
8519
8799
  let lastErr;
8520
8800
  for (let attempt = 0; attempt <= retries; attempt++) try {
8521
- await vmRun(vm, label, run);
8801
+ await vmRun(vm, label, run, config.signal);
8522
8802
  lastErr = void 0;
8523
8803
  break;
8524
8804
  } catch (err) {
8525
8805
  lastErr = err;
8526
8806
  if (attempt === retries) break;
8527
- await new Promise((resolve) => {
8528
- setTimeout(resolve, (attempt + 1) * backoffMs);
8529
- });
8807
+ await delay((attempt + 1) * backoffMs, config.signal, label);
8530
8808
  }
8531
- if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
8809
+ if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(nonErrorMessage(lastErr));
8532
8810
  }
8533
8811
  const vmSshDir = `${vmAgentDir}/ssh`;
8534
- await vm.exec(`mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent`);
8535
- if (creds.piAuthJson !== null) await vm.fs.writeFile("/home/agent/.pi/agent/auth.json", creds.piAuthJson, { mode: 384 });
8812
+ await vm.exec(`mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent`, { signal: config.signal });
8813
+ if (creds.piAuthJson !== null) await vm.fs.writeFile("/home/agent/.pi/agent/auth.json", creds.piAuthJson, {
8814
+ mode: 384,
8815
+ signal: config.signal
8816
+ });
8536
8817
  const vmMoltnetJson = rewriteMoltnetJsonPaths(creds.moltnetJson, vmAgentDir, vmSshDir, creds.githubAppPemFilename);
8537
- await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, { mode: 384 });
8538
- await vm.fs.writeFile(`${vmAgentDir}/env`, creds.agentEnvRaw, { mode: 384 });
8818
+ await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, {
8819
+ mode: 384,
8820
+ signal: config.signal
8821
+ });
8822
+ await vm.fs.writeFile(`${vmAgentDir}/env`, creds.agentEnvRaw, {
8823
+ mode: 384,
8824
+ signal: config.signal
8825
+ });
8539
8826
  if (creds.gitconfig) {
8540
8827
  const vmSigningKey = `${vmSshDir}/id_ed25519`;
8541
8828
  const vmGitconfig = creds.gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSigningKey}`);
8542
- await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, { mode: 420 });
8829
+ await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
8830
+ mode: 420,
8831
+ signal: config.signal
8832
+ });
8543
8833
  }
8544
- if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, { mode: 384 });
8545
- if (creds.sshPublicKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519.pub`, creds.sshPublicKey, { mode: 420 });
8546
- if (creds.allowedSigners) await vm.fs.writeFile(`${vmSshDir}/allowed_signers`, creds.allowedSigners, { mode: 420 });
8547
- if (creds.githubAppPem && creds.githubAppPemFilename) await vm.fs.writeFile(`${vmAgentDir}/${creds.githubAppPemFilename}`, creds.githubAppPem, { mode: 384 });
8548
- await vm.exec("chown -R agent:agent /home/agent/.pi /home/agent/.moltnet");
8834
+ if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, {
8835
+ mode: 384,
8836
+ signal: config.signal
8837
+ });
8838
+ if (creds.sshPublicKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519.pub`, creds.sshPublicKey, {
8839
+ mode: 420,
8840
+ signal: config.signal
8841
+ });
8842
+ if (creds.allowedSigners) await vm.fs.writeFile(`${vmSshDir}/allowed_signers`, creds.allowedSigners, {
8843
+ mode: 420,
8844
+ signal: config.signal
8845
+ });
8846
+ if (creds.githubAppPem && creds.githubAppPemFilename) await vm.fs.writeFile(`${vmAgentDir}/${creds.githubAppPemFilename}`, creds.githubAppPem, {
8847
+ mode: 384,
8848
+ signal: config.signal
8849
+ });
8850
+ 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);
8549
8862
  return {
8550
8863
  vm,
8551
8864
  credentials: creds,
@@ -13157,6 +13470,170 @@ function validateRubricWeights(rubric) {
13157
13470
  return null;
13158
13471
  }
13159
13472
  //#endregion
13473
+ //#region ../tasks/src/runtime-profiles.ts
13474
+ var RuntimeProfileName = String$1({
13475
+ minLength: 1,
13476
+ maxLength: 100,
13477
+ pattern: "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$"
13478
+ });
13479
+ var RuntimeProfileEnvName = String$1({
13480
+ minLength: 1,
13481
+ maxLength: 128,
13482
+ pattern: "^[A-Z_][A-Z0-9_]*$"
13483
+ });
13484
+ var RuntimeProfileToolName = String$1({
13485
+ minLength: 1,
13486
+ maxLength: 128,
13487
+ pattern: "^[a-zA-Z0-9._/-]+$"
13488
+ });
13489
+ var SandboxResumeCommandWhenSchema = _Object_({ workspaceMode: Optional(_Array_(Union([
13490
+ Literal("shared_mount"),
13491
+ Literal("dedicated_worktree"),
13492
+ Literal("scratch_mount")
13493
+ ]), {
13494
+ minItems: 1,
13495
+ maxItems: 3
13496
+ })) }, { additionalProperties: false });
13497
+ var RuntimeProfileSandboxResumeCommand = Union([String$1({
13498
+ minLength: 1,
13499
+ maxLength: 4096
13500
+ }), _Object_({
13501
+ run: String$1({
13502
+ minLength: 1,
13503
+ maxLength: 4096
13504
+ }),
13505
+ when: Optional(SandboxResumeCommandWhenSchema),
13506
+ retries: Optional(Integer({
13507
+ minimum: 0,
13508
+ maximum: 5
13509
+ })),
13510
+ retryBackoffMs: Optional(Integer({
13511
+ minimum: 0,
13512
+ maximum: 6e4
13513
+ }))
13514
+ }, { additionalProperties: false })]);
13515
+ var RuntimeProfileSandbox = _Object_({
13516
+ snapshot: Optional(_Object_({
13517
+ setupCommands: Optional(_Array_(String$1({
13518
+ minLength: 1,
13519
+ maxLength: 4096
13520
+ }), { maxItems: 20 })),
13521
+ allowedHosts: Optional(_Array_(String$1({
13522
+ minLength: 1,
13523
+ maxLength: 255
13524
+ }), { maxItems: 50 })),
13525
+ overlaySize: Optional(String$1({
13526
+ minLength: 2,
13527
+ maxLength: 16,
13528
+ pattern: "^[0-9]+[KMGTP]?$"
13529
+ }))
13530
+ }, { additionalProperties: false })),
13531
+ resumeCommands: Optional(_Array_(RuntimeProfileSandboxResumeCommand, { maxItems: 30 })),
13532
+ vfs: Optional(_Object_({
13533
+ shadow: Optional(_Array_(String$1({
13534
+ minLength: 1,
13535
+ maxLength: 255
13536
+ }), { maxItems: 100 })),
13537
+ shadowMode: Optional(Union([Literal("deny"), Literal("tmpfs")]))
13538
+ }, { additionalProperties: false })),
13539
+ env: Optional(Record(RuntimeProfileEnvName, String$1({ maxLength: 4096 }))),
13540
+ hostExec: Optional(_Object_({ autoApprove: Optional(Literal(false)) }, { additionalProperties: false })),
13541
+ resources: Optional(_Object_({
13542
+ memory: Optional(String$1({
13543
+ minLength: 2,
13544
+ maxLength: 16,
13545
+ pattern: "^[0-9]+[KMG]?$"
13546
+ })),
13547
+ cpus: Optional(Integer({
13548
+ minimum: 1,
13549
+ maximum: 32
13550
+ }))
13551
+ }, { additionalProperties: false }))
13552
+ }, {
13553
+ $id: "RuntimeProfileSandbox",
13554
+ additionalProperties: false
13555
+ });
13556
+ var RuntimeProfileContext = _Object_({
13557
+ slug: String$1({
13558
+ minLength: 1,
13559
+ maxLength: 64,
13560
+ pattern: "^[a-zA-Z0-9_-]+$"
13561
+ }),
13562
+ binding: Union([
13563
+ Literal("skill"),
13564
+ Literal("context_inline"),
13565
+ Literal("prompt_prefix"),
13566
+ Literal("user_inline")
13567
+ ]),
13568
+ content: String$1({
13569
+ minLength: 1,
13570
+ maxLength: 65536
13571
+ })
13572
+ }, {
13573
+ $id: "RuntimeProfileContext",
13574
+ additionalProperties: false
13575
+ });
13576
+ var RuntimeProfileRef = _Object_({ profileId: String$1({ format: "uuid" }) }, {
13577
+ $id: "RuntimeProfileRef",
13578
+ additionalProperties: false
13579
+ });
13580
+ var RuntimeProfileLeaseTtlSec = Integer({
13581
+ minimum: 1,
13582
+ maximum: 86400
13583
+ });
13584
+ var RuntimeProfileHeartbeatIntervalMs = Integer({
13585
+ minimum: 0,
13586
+ maximum: 36e5
13587
+ });
13588
+ var RuntimeProfileMaxBatchSize = Integer({
13589
+ minimum: 1,
13590
+ maximum: 1e3
13591
+ });
13592
+ _Object_({
13593
+ id: String$1({ format: "uuid" }),
13594
+ teamId: String$1({ format: "uuid" }),
13595
+ name: RuntimeProfileName,
13596
+ description: Union([String$1({ maxLength: 4096 }), Null()]),
13597
+ provider: String$1({
13598
+ minLength: 1,
13599
+ maxLength: 100
13600
+ }),
13601
+ model: String$1({
13602
+ minLength: 1,
13603
+ maxLength: 200
13604
+ }),
13605
+ runtimeKind: Literal("gondolin_pi"),
13606
+ sandbox: RuntimeProfileSandbox,
13607
+ sessionStorageMode: Literal("local"),
13608
+ workspaceStorageMode: Literal("local"),
13609
+ sessionTtlSec: Integer({
13610
+ minimum: 1,
13611
+ maximum: 86400
13612
+ }),
13613
+ workspaceTtlSec: Integer({
13614
+ minimum: 1,
13615
+ maximum: 86400
13616
+ }),
13617
+ leaseTtlSec: RuntimeProfileLeaseTtlSec,
13618
+ heartbeatIntervalMs: RuntimeProfileHeartbeatIntervalMs,
13619
+ maxBatchSize: RuntimeProfileMaxBatchSize,
13620
+ requiredEnv: _Array_(RuntimeProfileEnvName, { maxItems: 100 }),
13621
+ requiredTools: _Array_(RuntimeProfileToolName, { maxItems: 100 }),
13622
+ context: _Array_(RuntimeProfileContext, { maxItems: 5 }),
13623
+ revision: Integer({ minimum: 1 }),
13624
+ definitionCid: String$1({
13625
+ minLength: 1,
13626
+ maxLength: 100
13627
+ }),
13628
+ createdByAgentId: Union([String$1({ format: "uuid" }), Null()]),
13629
+ createdByHumanId: Union([String$1({ format: "uuid" }), Null()]),
13630
+ createdAt: String$1({ format: "date-time" }),
13631
+ updatedAt: String$1({ format: "date-time" })
13632
+ }, {
13633
+ $id: "RuntimeProfile",
13634
+ additionalProperties: false
13635
+ });
13636
+ //#endregion
13160
13637
  //#region ../tasks/src/success-criteria.ts
13161
13638
  /**
13162
13639
  * SuccessCriteria — proposer-stated acceptance criteria, evaluated in two
@@ -16848,6 +17325,7 @@ var TaskAttemptStatus = Union([
16848
17325
  Literal("completed"),
16849
17326
  Literal("failed"),
16850
17327
  Literal("cancelled"),
17328
+ Literal("aborted"),
16851
17329
  Literal("timed_out")
16852
17330
  ], { $id: "TaskAttemptStatus" });
16853
17331
  var ExecutorTrustLevel = Union([
@@ -16856,14 +17334,6 @@ var ExecutorTrustLevel = Union([
16856
17334
  Literal("releaseVerifiedTool"),
16857
17335
  Literal("sandboxAttested")
16858
17336
  ], { $id: "ExecutorTrustLevel" });
16859
- /** Identifies a (provider, model) daemon pair allowed to claim a task. */
16860
- var ExecutorRef = _Object_({
16861
- provider: String$1({ minLength: 1 }),
16862
- model: String$1({ minLength: 1 })
16863
- }, {
16864
- $id: "ExecutorRef",
16865
- additionalProperties: false
16866
- });
16867
17337
  var OutputKind = Union([Literal("artifact"), Literal("judgment")], { $id: "OutputKind" });
16868
17338
  var TaskMessageKind = Union([
16869
17339
  Literal("text_delta"),
@@ -17008,7 +17478,7 @@ _Object_({
17008
17478
  acceptedAttemptN: Union([Number$1(), Null()]),
17009
17479
  claimCondition: Union([Unsafe(Ref$1("ClaimCondition")), Null()]),
17010
17480
  requiredExecutorTrustLevel: ExecutorTrustLevel,
17011
- allowedExecutors: _Array_(ExecutorRef, { maxItems: 16 }),
17481
+ allowedProfiles: _Array_(RuntimeProfileRef, { maxItems: 16 }),
17012
17482
  status: TaskStatus,
17013
17483
  queuedAt: IsoTimestamp,
17014
17484
  completedAt: Union([IsoTimestamp, Null()]),
@@ -22988,6 +23458,20 @@ async function executePiTask(claimedTask, reporter, opts) {
22988
23458
  retryable: false
22989
23459
  }
22990
23460
  });
23461
+ const makeCancelledOutput = (message) => ({
23462
+ taskId: task.id,
23463
+ attemptN,
23464
+ status: "cancelled",
23465
+ output: null,
23466
+ outputCid: null,
23467
+ usage: finalUsage,
23468
+ durationMs: Date.now() - startTime,
23469
+ error: {
23470
+ code: "task_cancelled",
23471
+ message,
23472
+ retryable: false
23473
+ }
23474
+ });
22991
23475
  let onTurnEvent;
22992
23476
  if (opts.makeOnTurnEvent) try {
22993
23477
  onTurnEvent = opts.makeOnTurnEvent(claimedTask);
@@ -23050,10 +23534,15 @@ async function executePiTask(claimedTask, reporter, opts) {
23050
23534
  mountPath,
23051
23535
  workspaceMode: workspace.mode,
23052
23536
  extraAllowedHosts: opts.extraAllowedHosts,
23053
- sandboxConfig
23537
+ sandboxConfig,
23538
+ signal: reporter.cancelSignal
23054
23539
  });
23055
23540
  } catch (err) {
23056
23541
  const message = err instanceof Error ? err.message : String(err);
23542
+ if (reporter.cancelSignal.aborted) {
23543
+ await emitError("vm_resume", message, { cancelled: true });
23544
+ return makeCancelledOutput(reporter.cancelReason ?? "Task cancelled during VM resume.");
23545
+ }
23057
23546
  await emitError("vm_resume", message);
23058
23547
  return makeFailedOutput("vm_resume_failed", message);
23059
23548
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/pi-extension",
3
- "version": "0.22.5",
3
+ "version": "0.23.1",
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.22.4",
40
- "@themoltnet/sdk": "0.106.0"
39
+ "@themoltnet/sdk": "0.108.0",
40
+ "@themoltnet/agent-runtime": "0.24.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@earendil-works/pi-coding-agent": ">=0.74.0",