@lucid-agents/taskmarket 1.17.0 → 1.17.2

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.
@@ -1,22 +1,67 @@
1
1
  import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
2
  import {
3
+ SANDBOX_TOOL_USER,
4
+ SandboxUnavailableError,
5
+ buyComputeChunk,
6
+ computeAllocationId,
7
+ computeCreditObserver,
8
+ computeCreditOffer,
9
+ openSandbox,
10
+ readComputeView
11
+ } from "./chunk-LDCFKFF6.js";
12
+ import {
13
+ applyComputeCreditSetup,
14
+ atomicToUsd,
15
+ buyComputeCredit,
16
+ fetchComputePricing,
17
+ loadComputeCreditConfig,
18
+ planComputeCreditSetup,
19
+ readComputeCreditBalance,
20
+ usdToAtomic
21
+ } from "./chunk-52Q2DCZN.js";
22
+ import {
23
+ MAX_RETIRED_RAILS,
24
+ WORKSPACE_CONSENT_VERSION,
25
+ XGATE_ROUTES,
26
+ XgateError,
27
+ XgatePurchaseError,
28
+ allocationViews,
29
+ assessAgentPolicy,
30
+ channelView,
31
+ createAgentTransport,
32
+ getAgentPolicyPath,
33
+ openAgentChannels,
34
+ readAgentPolicy,
35
+ reconcileLeftoverOperations,
36
+ routeOfCatalogRow,
37
+ saveAgentPolicy,
38
+ selectModels,
39
+ settledRetiredRails
40
+ } from "./chunk-GJQUALXN.js";
41
+ import {
42
+ BASE_CHANNEL_RAIL_ID,
3
43
  apiUrl,
4
44
  createBaseChannelPayer,
5
45
  createBaseChannelRpcReader,
6
- createWalletAccountFromKeystore,
7
46
  createX402Allocations,
8
47
  getTaskmarketHome,
9
48
  getX402JournalPath,
10
49
  getX402PolicyPath,
11
50
  keystoreExists,
51
+ listX402Payments,
12
52
  loadKeystore,
13
53
  loadX402Policy,
14
- onComputeBalanceObserved,
15
- rpcUrlForNetwork
16
- } from "./chunk-3XPF2O6L.js";
54
+ parseX402Policy,
55
+ saveX402Policy,
56
+ x402SpendWindowAvailability
57
+ } from "./chunk-FRV3ZHUR.js";
17
58
  import {
18
59
  AgentLaunchError
19
- } from "./chunk-O27J7ZPH.js";
60
+ } from "./chunk-YM4S6IE4.js";
61
+ import {
62
+ AGENT_VIEW_VERSION,
63
+ formatUsdc
64
+ } from "./chunk-73RXOYME.js";
20
65
  import {
21
66
  __commonJS,
22
67
  __require,
@@ -8080,11 +8125,11 @@ var require_agent = __commonJS({
8080
8125
  };
8081
8126
  })();
8082
8127
  var WINDOWS_PIPE_REGEX = /^[/\\][/\\]\.[/\\]pipe[/\\].+/;
8083
- function createAgent(path7) {
8084
- if (process.platform === "win32" && !WINDOWS_PIPE_REGEX.test(path7)) {
8085
- return path7 === "pageant" ? new PageantAgent() : new CygwinAgent(path7);
8128
+ function createAgent(path6) {
8129
+ if (process.platform === "win32" && !WINDOWS_PIPE_REGEX.test(path6)) {
8130
+ return path6 === "pageant" ? new PageantAgent() : new CygwinAgent(path6);
8086
8131
  }
8087
- return new OpenSSHAgent(path7);
8132
+ return new OpenSSHAgent(path6);
8088
8133
  }
8089
8134
  var AgentProtocol = (() => {
8090
8135
  const SSH_AGENTC_REQUEST_IDENTITIES = 11;
@@ -8882,7 +8927,7 @@ var require_handlers_misc = __commonJS({
8882
8927
  },
8883
8928
  [MESSAGE.DEBUG]: (self2, payload) => {
8884
8929
  bufferParser.init(payload, 1);
8885
- const display2 = bufferParser.readBool();
8930
+ const display = bufferParser.readBool();
8886
8931
  const msg = bufferParser.readString(true);
8887
8932
  const lang = bufferParser.readString();
8888
8933
  bufferParser.clear();
@@ -8894,7 +8939,7 @@ var require_handlers_misc = __commonJS({
8894
8939
  }
8895
8940
  self2._debug && self2._debug("Inbound: Received DEBUG");
8896
8941
  const handler = self2._handlers.DEBUG;
8897
- handler && handler(self2, display2, msg);
8942
+ handler && handler(self2, display, msg);
8898
8943
  },
8899
8944
  [MESSAGE.SERVICE_REQUEST]: (self2, payload) => {
8900
8945
  bufferParser.init(payload, 1);
@@ -13328,17 +13373,17 @@ var require_SFTP = __commonJS({
13328
13373
  // ===========================================================================
13329
13374
  // Client-specific ===========================================================
13330
13375
  // ===========================================================================
13331
- createReadStream(path7, options) {
13376
+ createReadStream(path6, options) {
13332
13377
  if (this.server)
13333
13378
  throw new Error("Client-only method called in server mode");
13334
- return new ReadStream(this, path7, options);
13379
+ return new ReadStream(this, path6, options);
13335
13380
  }
13336
- createWriteStream(path7, options) {
13381
+ createWriteStream(path6, options) {
13337
13382
  if (this.server)
13338
13383
  throw new Error("Client-only method called in server mode");
13339
- return new WriteStream(this, path7, options);
13384
+ return new WriteStream(this, path6, options);
13340
13385
  }
13341
- open(path7, flags_, attrs, cb) {
13386
+ open(path6, flags_, attrs, cb) {
13342
13387
  if (this.server)
13343
13388
  throw new Error("Client-only method called in server mode");
13344
13389
  if (typeof attrs === "function") {
@@ -13357,7 +13402,7 @@ var require_SFTP = __commonJS({
13357
13402
  attrsFlags = attrs.flags;
13358
13403
  attrsLen = attrs.nb;
13359
13404
  }
13360
- const pathLen = Buffer.byteLength(path7);
13405
+ const pathLen = Buffer.byteLength(path6);
13361
13406
  let p = 9;
13362
13407
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + 4 + attrsLen);
13363
13408
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -13365,7 +13410,7 @@ var require_SFTP = __commonJS({
13365
13410
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
13366
13411
  writeUInt32BE(buf, reqid, 5);
13367
13412
  writeUInt32BE(buf, pathLen, p);
13368
- buf.utf8Write(path7, p += 4, pathLen);
13413
+ buf.utf8Write(path6, p += 4, pathLen);
13369
13414
  writeUInt32BE(buf, flags, p += pathLen);
13370
13415
  writeUInt32BE(buf, attrsFlags, p += 4);
13371
13416
  if (attrsLen) {
@@ -13496,7 +13541,7 @@ var require_SFTP = __commonJS({
13496
13541
  throw new Error("Client-only method called in server mode");
13497
13542
  fastXfer(fs3, this, localPath, remotePath, opts, cb);
13498
13543
  }
13499
- readFile(path7, options, callback_) {
13544
+ readFile(path6, options, callback_) {
13500
13545
  if (this.server)
13501
13546
  throw new Error("Client-only method called in server mode");
13502
13547
  let callback;
@@ -13569,13 +13614,13 @@ var require_SFTP = __commonJS({
13569
13614
  return callback && callback(er, buffer);
13570
13615
  });
13571
13616
  };
13572
- this.open(path7, flag, 438, (er, handle_) => {
13617
+ this.open(path6, flag, 438, (er, handle_) => {
13573
13618
  if (er)
13574
13619
  return callback && callback(er);
13575
13620
  handle = handle_;
13576
13621
  const tryStat = (er2, st) => {
13577
13622
  if (er2) {
13578
- this.stat(path7, (er_, st_) => {
13623
+ this.stat(path6, (er_, st_) => {
13579
13624
  if (er_) {
13580
13625
  return this.close(handle, () => {
13581
13626
  callback && callback(er2);
@@ -13596,7 +13641,7 @@ var require_SFTP = __commonJS({
13596
13641
  this.fstat(handle, tryStat);
13597
13642
  });
13598
13643
  }
13599
- writeFile(path7, data, options, callback_) {
13644
+ writeFile(path6, data, options, callback_) {
13600
13645
  if (this.server)
13601
13646
  throw new Error("Client-only method called in server mode");
13602
13647
  let callback;
@@ -13615,7 +13660,7 @@ var require_SFTP = __commonJS({
13615
13660
  if (options.encoding && !Buffer.isEncoding(options.encoding))
13616
13661
  throw new Error(`Unknown encoding: ${options.encoding}`);
13617
13662
  const flag = options.flag || "w";
13618
- this.open(path7, flag, options.mode, (openErr, handle) => {
13663
+ this.open(path6, flag, options.mode, (openErr, handle) => {
13619
13664
  if (openErr) {
13620
13665
  callback && callback(openErr);
13621
13666
  } else {
@@ -13624,7 +13669,7 @@ var require_SFTP = __commonJS({
13624
13669
  if (position === null) {
13625
13670
  const tryStat = (er, st) => {
13626
13671
  if (er) {
13627
- this.stat(path7, (er_, st_) => {
13672
+ this.stat(path6, (er_, st_) => {
13628
13673
  if (er_) {
13629
13674
  return this.close(handle, () => {
13630
13675
  callback && callback(er);
@@ -13643,7 +13688,7 @@ var require_SFTP = __commonJS({
13643
13688
  }
13644
13689
  });
13645
13690
  }
13646
- appendFile(path7, data, options, callback_) {
13691
+ appendFile(path6, data, options, callback_) {
13647
13692
  if (this.server)
13648
13693
  throw new Error("Client-only method called in server mode");
13649
13694
  let callback;
@@ -13661,12 +13706,12 @@ var require_SFTP = __commonJS({
13661
13706
  throw new TypeError("Bad arguments");
13662
13707
  if (!options.flag)
13663
13708
  options = Object.assign({ flag: "a" }, options);
13664
- this.writeFile(path7, data, options, callback);
13709
+ this.writeFile(path6, data, options, callback);
13665
13710
  }
13666
- exists(path7, cb) {
13711
+ exists(path6, cb) {
13667
13712
  if (this.server)
13668
13713
  throw new Error("Client-only method called in server mode");
13669
- this.stat(path7, (err) => {
13714
+ this.stat(path6, (err) => {
13670
13715
  cb && cb(err ? false : true);
13671
13716
  });
13672
13717
  }
@@ -13709,7 +13754,7 @@ var require_SFTP = __commonJS({
13709
13754
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} RENAME`
13710
13755
  );
13711
13756
  }
13712
- mkdir(path7, attrs, cb) {
13757
+ mkdir(path6, attrs, cb) {
13713
13758
  if (this.server)
13714
13759
  throw new Error("Client-only method called in server mode");
13715
13760
  let flags = 0;
@@ -13723,7 +13768,7 @@ var require_SFTP = __commonJS({
13723
13768
  flags = attrs.flags;
13724
13769
  attrsLen = attrs.nb;
13725
13770
  }
13726
- const pathLen = Buffer.byteLength(path7);
13771
+ const pathLen = Buffer.byteLength(path6);
13727
13772
  let p = 9;
13728
13773
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + attrsLen);
13729
13774
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -13731,7 +13776,7 @@ var require_SFTP = __commonJS({
13731
13776
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
13732
13777
  writeUInt32BE(buf, reqid, 5);
13733
13778
  writeUInt32BE(buf, pathLen, p);
13734
- buf.utf8Write(path7, p += 4, pathLen);
13779
+ buf.utf8Write(path6, p += 4, pathLen);
13735
13780
  writeUInt32BE(buf, flags, p += pathLen);
13736
13781
  if (attrsLen) {
13737
13782
  p += 4;
@@ -13747,10 +13792,10 @@ var require_SFTP = __commonJS({
13747
13792
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} MKDIR`
13748
13793
  );
13749
13794
  }
13750
- rmdir(path7, cb) {
13795
+ rmdir(path6, cb) {
13751
13796
  if (this.server)
13752
13797
  throw new Error("Client-only method called in server mode");
13753
- const pathLen = Buffer.byteLength(path7);
13798
+ const pathLen = Buffer.byteLength(path6);
13754
13799
  let p = 9;
13755
13800
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
13756
13801
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -13758,7 +13803,7 @@ var require_SFTP = __commonJS({
13758
13803
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
13759
13804
  writeUInt32BE(buf, reqid, 5);
13760
13805
  writeUInt32BE(buf, pathLen, p);
13761
- buf.utf8Write(path7, p += 4, pathLen);
13806
+ buf.utf8Write(path6, p += 4, pathLen);
13762
13807
  this._requests[reqid] = { cb };
13763
13808
  const isBuffered = sendOrBuffer(this, buf);
13764
13809
  this._debug && this._debug(
@@ -13848,10 +13893,10 @@ var require_SFTP = __commonJS({
13848
13893
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} FSTAT`
13849
13894
  );
13850
13895
  }
13851
- stat(path7, cb) {
13896
+ stat(path6, cb) {
13852
13897
  if (this.server)
13853
13898
  throw new Error("Client-only method called in server mode");
13854
- const pathLen = Buffer.byteLength(path7);
13899
+ const pathLen = Buffer.byteLength(path6);
13855
13900
  let p = 9;
13856
13901
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
13857
13902
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -13859,17 +13904,17 @@ var require_SFTP = __commonJS({
13859
13904
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
13860
13905
  writeUInt32BE(buf, reqid, 5);
13861
13906
  writeUInt32BE(buf, pathLen, p);
13862
- buf.utf8Write(path7, p += 4, pathLen);
13907
+ buf.utf8Write(path6, p += 4, pathLen);
13863
13908
  this._requests[reqid] = { cb };
13864
13909
  const isBuffered = sendOrBuffer(this, buf);
13865
13910
  this._debug && this._debug(
13866
13911
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} STAT`
13867
13912
  );
13868
13913
  }
13869
- lstat(path7, cb) {
13914
+ lstat(path6, cb) {
13870
13915
  if (this.server)
13871
13916
  throw new Error("Client-only method called in server mode");
13872
- const pathLen = Buffer.byteLength(path7);
13917
+ const pathLen = Buffer.byteLength(path6);
13873
13918
  let p = 9;
13874
13919
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
13875
13920
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -13877,17 +13922,17 @@ var require_SFTP = __commonJS({
13877
13922
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
13878
13923
  writeUInt32BE(buf, reqid, 5);
13879
13924
  writeUInt32BE(buf, pathLen, p);
13880
- buf.utf8Write(path7, p += 4, pathLen);
13925
+ buf.utf8Write(path6, p += 4, pathLen);
13881
13926
  this._requests[reqid] = { cb };
13882
13927
  const isBuffered = sendOrBuffer(this, buf);
13883
13928
  this._debug && this._debug(
13884
13929
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} LSTAT`
13885
13930
  );
13886
13931
  }
13887
- opendir(path7, cb) {
13932
+ opendir(path6, cb) {
13888
13933
  if (this.server)
13889
13934
  throw new Error("Client-only method called in server mode");
13890
- const pathLen = Buffer.byteLength(path7);
13935
+ const pathLen = Buffer.byteLength(path6);
13891
13936
  let p = 9;
13892
13937
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
13893
13938
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -13895,14 +13940,14 @@ var require_SFTP = __commonJS({
13895
13940
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
13896
13941
  writeUInt32BE(buf, reqid, 5);
13897
13942
  writeUInt32BE(buf, pathLen, p);
13898
- buf.utf8Write(path7, p += 4, pathLen);
13943
+ buf.utf8Write(path6, p += 4, pathLen);
13899
13944
  this._requests[reqid] = { cb };
13900
13945
  const isBuffered = sendOrBuffer(this, buf);
13901
13946
  this._debug && this._debug(
13902
13947
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} OPENDIR`
13903
13948
  );
13904
13949
  }
13905
- setstat(path7, attrs, cb) {
13950
+ setstat(path6, attrs, cb) {
13906
13951
  if (this.server)
13907
13952
  throw new Error("Client-only method called in server mode");
13908
13953
  let flags = 0;
@@ -13914,7 +13959,7 @@ var require_SFTP = __commonJS({
13914
13959
  } else if (typeof attrs === "function") {
13915
13960
  cb = attrs;
13916
13961
  }
13917
- const pathLen = Buffer.byteLength(path7);
13962
+ const pathLen = Buffer.byteLength(path6);
13918
13963
  let p = 9;
13919
13964
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + attrsLen);
13920
13965
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -13922,7 +13967,7 @@ var require_SFTP = __commonJS({
13922
13967
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
13923
13968
  writeUInt32BE(buf, reqid, 5);
13924
13969
  writeUInt32BE(buf, pathLen, p);
13925
- buf.utf8Write(path7, p += 4, pathLen);
13970
+ buf.utf8Write(path6, p += 4, pathLen);
13926
13971
  writeUInt32BE(buf, flags, p += pathLen);
13927
13972
  if (attrsLen) {
13928
13973
  p += 4;
@@ -13982,8 +14027,8 @@ var require_SFTP = __commonJS({
13982
14027
  mtime: toUnixTimestamp(mtime)
13983
14028
  }, cb);
13984
14029
  }
13985
- utimes(path7, atime, mtime, cb) {
13986
- return this.setstat(path7, {
14030
+ utimes(path6, atime, mtime, cb) {
14031
+ return this.setstat(path6, {
13987
14032
  atime: toUnixTimestamp(atime),
13988
14033
  mtime: toUnixTimestamp(mtime)
13989
14034
  }, cb);
@@ -13994,8 +14039,8 @@ var require_SFTP = __commonJS({
13994
14039
  gid
13995
14040
  }, cb);
13996
14041
  }
13997
- chown(path7, uid, gid, cb) {
13998
- return this.setstat(path7, {
14042
+ chown(path6, uid, gid, cb) {
14043
+ return this.setstat(path6, {
13999
14044
  uid,
14000
14045
  gid
14001
14046
  }, cb);
@@ -14005,15 +14050,15 @@ var require_SFTP = __commonJS({
14005
14050
  mode
14006
14051
  }, cb);
14007
14052
  }
14008
- chmod(path7, mode, cb) {
14009
- return this.setstat(path7, {
14053
+ chmod(path6, mode, cb) {
14054
+ return this.setstat(path6, {
14010
14055
  mode
14011
14056
  }, cb);
14012
14057
  }
14013
- readlink(path7, cb) {
14058
+ readlink(path6, cb) {
14014
14059
  if (this.server)
14015
14060
  throw new Error("Client-only method called in server mode");
14016
- const pathLen = Buffer.byteLength(path7);
14061
+ const pathLen = Buffer.byteLength(path6);
14017
14062
  let p = 9;
14018
14063
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
14019
14064
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -14021,7 +14066,7 @@ var require_SFTP = __commonJS({
14021
14066
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
14022
14067
  writeUInt32BE(buf, reqid, 5);
14023
14068
  writeUInt32BE(buf, pathLen, p);
14024
- buf.utf8Write(path7, p += 4, pathLen);
14069
+ buf.utf8Write(path6, p += 4, pathLen);
14025
14070
  this._requests[reqid] = {
14026
14071
  cb: (err, names) => {
14027
14072
  if (typeof cb !== "function")
@@ -14066,10 +14111,10 @@ var require_SFTP = __commonJS({
14066
14111
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} SYMLINK`
14067
14112
  );
14068
14113
  }
14069
- realpath(path7, cb) {
14114
+ realpath(path6, cb) {
14070
14115
  if (this.server)
14071
14116
  throw new Error("Client-only method called in server mode");
14072
- const pathLen = Buffer.byteLength(path7);
14117
+ const pathLen = Buffer.byteLength(path6);
14073
14118
  let p = 9;
14074
14119
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
14075
14120
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -14077,7 +14122,7 @@ var require_SFTP = __commonJS({
14077
14122
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
14078
14123
  writeUInt32BE(buf, reqid, 5);
14079
14124
  writeUInt32BE(buf, pathLen, p);
14080
- buf.utf8Write(path7, p += 4, pathLen);
14125
+ buf.utf8Write(path6, p += 4, pathLen);
14081
14126
  this._requests[reqid] = {
14082
14127
  cb: (err, names) => {
14083
14128
  if (typeof cb !== "function")
@@ -14122,13 +14167,13 @@ var require_SFTP = __commonJS({
14122
14167
  this._debug(`SFTP: Outbound: ${which} posix-rename@openssh.com`);
14123
14168
  }
14124
14169
  }
14125
- ext_openssh_statvfs(path7, cb) {
14170
+ ext_openssh_statvfs(path6, cb) {
14126
14171
  if (this.server)
14127
14172
  throw new Error("Client-only method called in server mode");
14128
14173
  const ext = this._extensions["statvfs@openssh.com"];
14129
14174
  if (!ext || ext !== "2")
14130
14175
  throw new Error("Server does not support this extended request");
14131
- const pathLen = Buffer.byteLength(path7);
14176
+ const pathLen = Buffer.byteLength(path6);
14132
14177
  let p = 9;
14133
14178
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 19 + 4 + pathLen);
14134
14179
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -14138,7 +14183,7 @@ var require_SFTP = __commonJS({
14138
14183
  writeUInt32BE(buf, 19, p);
14139
14184
  buf.utf8Write("statvfs@openssh.com", p += 4, 19);
14140
14185
  writeUInt32BE(buf, pathLen, p += 19);
14141
- buf.utf8Write(path7, p += 4, pathLen);
14186
+ buf.utf8Write(path6, p += 4, pathLen);
14142
14187
  this._requests[reqid] = { extended: "statvfs@openssh.com", cb };
14143
14188
  const isBuffered = sendOrBuffer(this, buf);
14144
14189
  if (this._debug) {
@@ -14224,7 +14269,7 @@ var require_SFTP = __commonJS({
14224
14269
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} fsync@openssh.com`
14225
14270
  );
14226
14271
  }
14227
- ext_openssh_lsetstat(path7, attrs, cb) {
14272
+ ext_openssh_lsetstat(path6, attrs, cb) {
14228
14273
  if (this.server)
14229
14274
  throw new Error("Client-only method called in server mode");
14230
14275
  const ext = this._extensions["lsetstat@openssh.com"];
@@ -14239,7 +14284,7 @@ var require_SFTP = __commonJS({
14239
14284
  } else if (typeof attrs === "function") {
14240
14285
  cb = attrs;
14241
14286
  }
14242
- const pathLen = Buffer.byteLength(path7);
14287
+ const pathLen = Buffer.byteLength(path6);
14243
14288
  let p = 9;
14244
14289
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 20 + 4 + pathLen + 4 + attrsLen);
14245
14290
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -14249,7 +14294,7 @@ var require_SFTP = __commonJS({
14249
14294
  writeUInt32BE(buf, 20, p);
14250
14295
  buf.utf8Write("lsetstat@openssh.com", p += 4, 20);
14251
14296
  writeUInt32BE(buf, pathLen, p += 20);
14252
- buf.utf8Write(path7, p += 4, pathLen);
14297
+ buf.utf8Write(path6, p += 4, pathLen);
14253
14298
  writeUInt32BE(buf, flags, p += pathLen);
14254
14299
  if (attrsLen) {
14255
14300
  p += 4;
@@ -14266,13 +14311,13 @@ var require_SFTP = __commonJS({
14266
14311
  this._debug(`SFTP: Outbound: ${status} lsetstat@openssh.com`);
14267
14312
  }
14268
14313
  }
14269
- ext_openssh_expandPath(path7, cb) {
14314
+ ext_openssh_expandPath(path6, cb) {
14270
14315
  if (this.server)
14271
14316
  throw new Error("Client-only method called in server mode");
14272
14317
  const ext = this._extensions["expand-path@openssh.com"];
14273
14318
  if (ext !== "1")
14274
14319
  throw new Error("Server does not support this extended request");
14275
- const pathLen = Buffer.byteLength(path7);
14320
+ const pathLen = Buffer.byteLength(path6);
14276
14321
  let p = 9;
14277
14322
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 23 + 4 + pathLen);
14278
14323
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -14282,7 +14327,7 @@ var require_SFTP = __commonJS({
14282
14327
  writeUInt32BE(buf, 23, p);
14283
14328
  buf.utf8Write("expand-path@openssh.com", p += 4, 23);
14284
14329
  writeUInt32BE(buf, pathLen, p += 20);
14285
- buf.utf8Write(path7, p += 4, pathLen);
14330
+ buf.utf8Write(path6, p += 4, pathLen);
14286
14331
  this._requests[reqid] = {
14287
14332
  cb: (err, names) => {
14288
14333
  if (typeof cb !== "function")
@@ -15560,12 +15605,12 @@ var require_SFTP = __commonJS({
15560
15605
  [REQUEST.LSTAT]: (sftp, payload) => {
15561
15606
  bufferParser.init(payload, 1);
15562
15607
  const reqID = bufferParser.readUInt32BE();
15563
- const path7 = bufferParser.readString(true);
15608
+ const path6 = bufferParser.readString(true);
15564
15609
  bufferParser.clear();
15565
- if (path7 === void 0)
15610
+ if (path6 === void 0)
15566
15611
  return doFatalSFTPError(sftp, "Malformed LSTAT packet");
15567
15612
  sftp._debug && sftp._debug(`SFTP: Inbound: Received LSTAT (id:${reqID})`);
15568
- if (!sftp.emit("LSTAT", reqID, path7)) {
15613
+ if (!sftp.emit("LSTAT", reqID, path6)) {
15569
15614
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15570
15615
  }
15571
15616
  },
@@ -15584,13 +15629,13 @@ var require_SFTP = __commonJS({
15584
15629
  [REQUEST.SETSTAT]: (sftp, payload) => {
15585
15630
  bufferParser.init(payload, 1);
15586
15631
  const reqID = bufferParser.readUInt32BE();
15587
- const path7 = bufferParser.readString(true);
15632
+ const path6 = bufferParser.readString(true);
15588
15633
  const attrs = readAttrs(sftp._biOpt);
15589
15634
  bufferParser.clear();
15590
15635
  if (attrs === void 0)
15591
15636
  return doFatalSFTPError(sftp, "Malformed SETSTAT packet");
15592
15637
  sftp._debug && sftp._debug(`SFTP: Inbound: Received SETSTAT (id:${reqID})`);
15593
- if (!sftp.emit("SETSTAT", reqID, path7, attrs)) {
15638
+ if (!sftp.emit("SETSTAT", reqID, path6, attrs)) {
15594
15639
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15595
15640
  }
15596
15641
  },
@@ -15612,12 +15657,12 @@ var require_SFTP = __commonJS({
15612
15657
  [REQUEST.OPENDIR]: (sftp, payload) => {
15613
15658
  bufferParser.init(payload, 1);
15614
15659
  const reqID = bufferParser.readUInt32BE();
15615
- const path7 = bufferParser.readString(true);
15660
+ const path6 = bufferParser.readString(true);
15616
15661
  bufferParser.clear();
15617
- if (path7 === void 0)
15662
+ if (path6 === void 0)
15618
15663
  return doFatalSFTPError(sftp, "Malformed OPENDIR packet");
15619
15664
  sftp._debug && sftp._debug(`SFTP: Inbound: Received OPENDIR (id:${reqID})`);
15620
- if (!sftp.emit("OPENDIR", reqID, path7)) {
15665
+ if (!sftp.emit("OPENDIR", reqID, path6)) {
15621
15666
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15622
15667
  }
15623
15668
  },
@@ -15636,63 +15681,63 @@ var require_SFTP = __commonJS({
15636
15681
  [REQUEST.REMOVE]: (sftp, payload) => {
15637
15682
  bufferParser.init(payload, 1);
15638
15683
  const reqID = bufferParser.readUInt32BE();
15639
- const path7 = bufferParser.readString(true);
15684
+ const path6 = bufferParser.readString(true);
15640
15685
  bufferParser.clear();
15641
- if (path7 === void 0)
15686
+ if (path6 === void 0)
15642
15687
  return doFatalSFTPError(sftp, "Malformed REMOVE packet");
15643
15688
  sftp._debug && sftp._debug(`SFTP: Inbound: Received REMOVE (id:${reqID})`);
15644
- if (!sftp.emit("REMOVE", reqID, path7)) {
15689
+ if (!sftp.emit("REMOVE", reqID, path6)) {
15645
15690
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15646
15691
  }
15647
15692
  },
15648
15693
  [REQUEST.MKDIR]: (sftp, payload) => {
15649
15694
  bufferParser.init(payload, 1);
15650
15695
  const reqID = bufferParser.readUInt32BE();
15651
- const path7 = bufferParser.readString(true);
15696
+ const path6 = bufferParser.readString(true);
15652
15697
  const attrs = readAttrs(sftp._biOpt);
15653
15698
  bufferParser.clear();
15654
15699
  if (attrs === void 0)
15655
15700
  return doFatalSFTPError(sftp, "Malformed MKDIR packet");
15656
15701
  sftp._debug && sftp._debug(`SFTP: Inbound: Received MKDIR (id:${reqID})`);
15657
- if (!sftp.emit("MKDIR", reqID, path7, attrs)) {
15702
+ if (!sftp.emit("MKDIR", reqID, path6, attrs)) {
15658
15703
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15659
15704
  }
15660
15705
  },
15661
15706
  [REQUEST.RMDIR]: (sftp, payload) => {
15662
15707
  bufferParser.init(payload, 1);
15663
15708
  const reqID = bufferParser.readUInt32BE();
15664
- const path7 = bufferParser.readString(true);
15709
+ const path6 = bufferParser.readString(true);
15665
15710
  bufferParser.clear();
15666
- if (path7 === void 0)
15711
+ if (path6 === void 0)
15667
15712
  return doFatalSFTPError(sftp, "Malformed RMDIR packet");
15668
15713
  sftp._debug && sftp._debug(`SFTP: Inbound: Received RMDIR (id:${reqID})`);
15669
- if (!sftp.emit("RMDIR", reqID, path7)) {
15714
+ if (!sftp.emit("RMDIR", reqID, path6)) {
15670
15715
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15671
15716
  }
15672
15717
  },
15673
15718
  [REQUEST.REALPATH]: (sftp, payload) => {
15674
15719
  bufferParser.init(payload, 1);
15675
15720
  const reqID = bufferParser.readUInt32BE();
15676
- const path7 = bufferParser.readString(true);
15721
+ const path6 = bufferParser.readString(true);
15677
15722
  bufferParser.clear();
15678
- if (path7 === void 0)
15723
+ if (path6 === void 0)
15679
15724
  return doFatalSFTPError(sftp, "Malformed REALPATH packet");
15680
15725
  sftp._debug && sftp._debug(
15681
15726
  `SFTP: Inbound: Received REALPATH (id:${reqID})`
15682
15727
  );
15683
- if (!sftp.emit("REALPATH", reqID, path7)) {
15728
+ if (!sftp.emit("REALPATH", reqID, path6)) {
15684
15729
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15685
15730
  }
15686
15731
  },
15687
15732
  [REQUEST.STAT]: (sftp, payload) => {
15688
15733
  bufferParser.init(payload, 1);
15689
15734
  const reqID = bufferParser.readUInt32BE();
15690
- const path7 = bufferParser.readString(true);
15735
+ const path6 = bufferParser.readString(true);
15691
15736
  bufferParser.clear();
15692
- if (path7 === void 0)
15737
+ if (path6 === void 0)
15693
15738
  return doFatalSFTPError(sftp, "Malformed STAT packet");
15694
15739
  sftp._debug && sftp._debug(`SFTP: Inbound: Received STAT (id:${reqID})`);
15695
- if (!sftp.emit("STAT", reqID, path7)) {
15740
+ if (!sftp.emit("STAT", reqID, path6)) {
15696
15741
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15697
15742
  }
15698
15743
  },
@@ -15712,14 +15757,14 @@ var require_SFTP = __commonJS({
15712
15757
  [REQUEST.READLINK]: (sftp, payload) => {
15713
15758
  bufferParser.init(payload, 1);
15714
15759
  const reqID = bufferParser.readUInt32BE();
15715
- const path7 = bufferParser.readString(true);
15760
+ const path6 = bufferParser.readString(true);
15716
15761
  bufferParser.clear();
15717
- if (path7 === void 0)
15762
+ if (path6 === void 0)
15718
15763
  return doFatalSFTPError(sftp, "Malformed READLINK packet");
15719
15764
  sftp._debug && sftp._debug(
15720
15765
  `SFTP: Inbound: Received READLINK (id:${reqID})`
15721
15766
  );
15722
- if (!sftp.emit("READLINK", reqID, path7)) {
15767
+ if (!sftp.emit("READLINK", reqID, path6)) {
15723
15768
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
15724
15769
  }
15725
15770
  },
@@ -15790,7 +15835,7 @@ var require_SFTP = __commonJS({
15790
15835
  function roundUpToMultipleOf8(n) {
15791
15836
  return n + 7 & ~7;
15792
15837
  }
15793
- function ReadStream(sftp, path7, options) {
15838
+ function ReadStream(sftp, path6, options) {
15794
15839
  if (options === void 0)
15795
15840
  options = {};
15796
15841
  else if (typeof options === "string")
@@ -15804,7 +15849,7 @@ var require_SFTP = __commonJS({
15804
15849
  options.emitClose = false;
15805
15850
  options.autoDestroy = false;
15806
15851
  ReadableStream.call(this, options);
15807
- this.path = path7;
15852
+ this.path = path6;
15808
15853
  this.flags = options.flags === void 0 ? "r" : options.flags;
15809
15854
  this.mode = options.mode === void 0 ? 438 : options.mode;
15810
15855
  this.start = options.start;
@@ -15935,7 +15980,7 @@ var require_SFTP = __commonJS({
15935
15980
  },
15936
15981
  configurable: true
15937
15982
  });
15938
- function WriteStream(sftp, path7, options) {
15983
+ function WriteStream(sftp, path6, options) {
15939
15984
  if (options === void 0)
15940
15985
  options = {};
15941
15986
  else if (typeof options === "string")
@@ -15947,7 +15992,7 @@ var require_SFTP = __commonJS({
15947
15992
  options.emitClose = false;
15948
15993
  options.autoDestroy = false;
15949
15994
  WritableStream.call(this, options);
15950
- this.path = path7;
15995
+ this.path = path6;
15951
15996
  this.flags = options.flags === void 0 ? "w" : options.flags;
15952
15997
  this.mode = options.mode === void 0 ? 438 : options.mode;
15953
15998
  this.start = options.start;
@@ -16862,7 +16907,7 @@ var require_client = __commonJS({
16862
16907
  let sawHeader = false;
16863
16908
  if (this._protocol)
16864
16909
  this._protocol.cleanup();
16865
- const DEBUG_HANDLER = !debug ? void 0 : (p, display2, msg) => {
16910
+ const DEBUG_HANDLER = !debug ? void 0 : (p, display, msg) => {
16866
16911
  debug(`Debug output from server: ${JSON.stringify(msg)}`);
16867
16912
  };
16868
16913
  let serverSigAlgs;
@@ -18854,7 +18899,7 @@ var require_server = __commonJS({
18854
18899
  function onClientPreHeaderError(err) {
18855
18900
  }
18856
18901
  this.on("error", onClientPreHeaderError);
18857
- const DEBUG_HANDLER = !debug ? void 0 : (p, display2, msg) => {
18902
+ const DEBUG_HANDLER = !debug ? void 0 : (p, display, msg) => {
18858
18903
  debug(`Debug output from client: ${JSON.stringify(msg)}`);
18859
18904
  };
18860
18905
  const kaIntvl = typeof srvCfg.keepaliveInterval === "number" && isFinite(srvCfg.keepaliveInterval) && srvCfg.keepaliveInterval > 0 ? srvCfg.keepaliveInterval : typeof Server.KEEPALIVE_CLIENT_INTERVAL === "number" && isFinite(Server.KEEPALIVE_CLIENT_INTERVAL) && Server.KEEPALIVE_CLIENT_INTERVAL > 0 ? Server.KEEPALIVE_CLIENT_INTERVAL : -1;
@@ -19690,7 +19735,7 @@ var require_keygen = __commonJS({
19690
19735
  generateKeyPair: generateKeyPair_,
19691
19736
  generateKeyPairSync: generateKeyPairSync_,
19692
19737
  getCurves,
19693
- randomBytes
19738
+ randomBytes: randomBytes2
19694
19739
  } = __require("crypto");
19695
19740
  var { Ber } = require_lib();
19696
19741
  var bcrypt_pbkdf = require_bcrypt_pbkdf().pbkdf;
@@ -19954,7 +19999,7 @@ var require_keygen = __commonJS({
19954
19999
  rounds = opts.rounds;
19955
20000
  }
19956
20001
  const gen = Buffer.allocUnsafe(cipher.keyLen + cipher.ivLen);
19957
- const salt = randomBytes(SALT_LEN);
20002
+ const salt = randomBytes2(SALT_LEN);
19958
20003
  const r = bcrypt_pbkdf(
19959
20004
  passphrase,
19960
20005
  passphrase.length,
@@ -19993,7 +20038,7 @@ var require_keygen = __commonJS({
19993
20038
  const kdfOptions = encrypted ? encrypted.kdfOptions : Buffer.alloc(0);
19994
20039
  const blockLen = encrypted ? encrypted.cipher.blockLen : 8;
19995
20040
  const parsed = parseDERs(keyType, pub, priv);
19996
- const checkInt = randomBytes(4);
20041
+ const checkInt = randomBytes2(4);
19997
20042
  const commentBin = Buffer.from(comment);
19998
20043
  const privBlobLen = 4 + 4 + parsed.priv.length + 4 + commentBin.length;
19999
20044
  let padding = [];
@@ -20153,9 +20198,9 @@ var require_lib2 = __commonJS({
20153
20198
 
20154
20199
  // src/lib/agent/launch.ts
20155
20200
  import { spawn } from "child_process";
20156
- import { randomUUID as randomUUID2 } from "crypto";
20201
+ import { randomUUID as randomUUID3 } from "crypto";
20157
20202
  import { appendFileSync, mkdirSync } from "fs";
20158
- import path6 from "path";
20203
+ import path5 from "path";
20159
20204
 
20160
20205
  // ../../packages/agent-host/dist/env.js
20161
20206
  var PASSTHROUGH_ENV_NAMES = [
@@ -20476,17 +20521,6 @@ function buildPiLaunch(options) {
20476
20521
  };
20477
20522
  }
20478
20523
 
20479
- // ../../packages/remote-tools/dist/backend.js
20480
- var SANDBOX_TOOL_USER = "agent";
20481
- var SandboxUnavailableError = class extends Error {
20482
- reason;
20483
- constructor(reason, options) {
20484
- super(`Sandbox unavailable, tools are disabled (no local fallback): ${reason}`, options);
20485
- this.name = "SandboxUnavailableError";
20486
- this.reason = reason;
20487
- }
20488
- };
20489
-
20490
20524
  // ../../packages/remote-tools/dist/broker.js
20491
20525
  var import_ssh2 = __toESM(require_lib2());
20492
20526
  var { utils } = import_ssh2.default;
@@ -20518,12 +20552,12 @@ var BrokerConnector = class {
20518
20552
  get sandboxId() {
20519
20553
  return this.sandboxIdValue;
20520
20554
  }
20521
- async post(path7, body, signal) {
20555
+ async post(path6, body, signal) {
20522
20556
  const doFetch = this.options.fetch ?? fetch;
20523
20557
  const timeout = AbortSignal.timeout(this.options.timeoutMs ?? 12e4);
20524
20558
  let response;
20525
20559
  try {
20526
- response = await doFetch(`${this.options.apiUrl.replace(/\/+$/, "")}/api/sandboxes/${path7}`, {
20560
+ response = await doFetch(`${this.options.apiUrl.replace(/\/+$/, "")}/api/sandboxes/${path6}`, {
20527
20561
  method: "POST",
20528
20562
  redirect: "error",
20529
20563
  headers: { "content-type": "application/json" },
@@ -20646,47 +20680,6 @@ function serveSandboxChannel(channel, connector, options = {}) {
20646
20680
  return { close: () => replies.end() };
20647
20681
  }
20648
20682
 
20649
- // src/lib/agent/compute.ts
20650
- function computeCreditObserver(log2) {
20651
- return async (observation) => {
20652
- try {
20653
- const { topUp } = await onComputeBalanceObserved(observation);
20654
- if (topUp.action === "bought") log2("Compute credit auto-top-up bought one chunk");
20655
- else if (topUp.action === "skipped")
20656
- log2(`Compute credit auto-top-up skipped: ${topUp.reason}`);
20657
- return topUp.action === "bought";
20658
- } catch (error) {
20659
- log2(
20660
- `Compute credit auto-top-up failed: ${error.code ?? error.message}`
20661
- );
20662
- return false;
20663
- }
20664
- };
20665
- }
20666
- function insufficientCredit(error) {
20667
- return error instanceof SandboxUnavailableError && /\binsufficient_credit\b/.test(error.reason);
20668
- }
20669
- async function openSandbox(createBroker, observe) {
20670
- const first = createBroker();
20671
- try {
20672
- await first.connect("start");
20673
- return first;
20674
- } catch (error) {
20675
- if (!insufficientCredit(error)) throw error;
20676
- if (!await observe({ creditWarning: "credit_exhausted" })) {
20677
- throw new AgentLaunchError("insufficient_credit", error.reason);
20678
- }
20679
- }
20680
- const retry = createBroker();
20681
- try {
20682
- await retry.connect("start");
20683
- return retry;
20684
- } catch (error) {
20685
- if (insufficientCredit(error)) throw new AgentLaunchError("insufficient_credit", error.reason);
20686
- throw error;
20687
- }
20688
- }
20689
-
20690
20683
  // src/lib/agent/extensions.ts
20691
20684
  import { existsSync as existsSync2 } from "fs";
20692
20685
  import path3 from "path";
@@ -20709,277 +20702,11 @@ function piExtensionPath(entry) {
20709
20702
  return found;
20710
20703
  }
20711
20704
 
20712
- // ../../node_modules/.pnpm/@lucid-agents+xgate-client@0.1.6_@solana+sysvars@5.5.1_typescript@5.7.3_zod@3.25.76/node_modules/@lucid-agents/xgate-client/dist/types.js
20713
- var XgateError = class extends Error {
20714
- code;
20715
- constructor(code) {
20716
- super(`Xgate: ${code}. Inspect /xgate status before another purchase.`);
20717
- this.name = "XgateError";
20718
- this.code = code;
20719
- }
20720
- };
20721
- function requireValue(condition, code) {
20722
- if (!condition)
20723
- throw new XgateError(code);
20724
- }
20725
- function record(value) {
20726
- return value !== null && typeof value === "object" && !Array.isArray(value);
20727
- }
20728
- function atomic(value) {
20729
- requireValue(typeof value === "string" && /^(0|[1-9][0-9]{0,12})$/.test(value), "invalid_amount");
20730
- const n = BigInt(value);
20731
- requireValue(n <= 1000000000000n, "amount_bound");
20732
- return n;
20733
- }
20734
- function origin(value, loopback = false) {
20735
- const u = new URL(value);
20736
- requireValue((u.protocol === "https:" || loopback && u.protocol === "http:" && ["127.0.0.1", "[::1]"].includes(u.hostname)) && !u.username && !u.password && u.pathname === "/" && !u.search && !u.hash, "invalid_origin");
20737
- return u.origin;
20738
- }
20739
- function route(value) {
20740
- requireValue([
20741
- "/v1/chat/completions",
20742
- "/providers/openai/v1/responses",
20743
- "/providers/anthropic/v1/messages"
20744
- ].includes(String(value)), "invalid_route");
20745
- }
20746
-
20747
- // ../../node_modules/.pnpm/@lucid-agents+pi-xgate@0.1.6_@earendil-works+pi-ai@0.85.1_@earendil-works+pi-coding-agent@0.8_344x6vs3jrzh5eh3xewwgv3ik4/node_modules/@lucid-agents/pi-xgate/dist/transport.js
20748
- var XgatePurchaseError = class extends XgateError {
20749
- invocationId;
20750
- pending;
20751
- payment;
20752
- constructor(code, invocationId, pending, payment) {
20753
- super(code);
20754
- this.name = "XgatePurchaseError";
20755
- this.message = pending ? `Xgate: ${code}. The payment outcome is unknown; do not retry. Reconcile it before another purchase.` : `Xgate: ${code}. Inspect the payment status before another purchase.`;
20756
- this.invocationId = invocationId;
20757
- this.pending = pending;
20758
- if (payment)
20759
- this.payment = payment;
20760
- }
20761
- };
20762
-
20763
- // ../../node_modules/.pnpm/@lucid-agents+pi-xgate@0.1.6_@earendil-works+pi-ai@0.85.1_@earendil-works+pi-coding-agent@0.8_344x6vs3jrzh5eh3xewwgv3ik4/node_modules/@lucid-agents/pi-xgate/dist/taskmarket.js
20764
- var RESOLVED_CHARGES = ["none", "released", "settled", "manually_resolved"];
20765
- var RESOLVED_STATES = ["completed", "failed"];
20766
- var CATALOG_BYTES = 2 * 1024 * 1024;
20767
- var REQUEST_BYTES = 8 * 1024 * 1024;
20768
- var CATALOG_DEADLINE_MS = 15e3;
20769
- function createTaskMarketTransport(options) {
20770
- const { payer } = options, base = origin(options.origin), fetcher = options.fetch ?? fetch;
20771
- if (payer.apiVersion !== 1)
20772
- throw new XgateError("unsupported_payer_api");
20773
- const channel = async (payment) => {
20774
- if (!options.balances || payment.rail.kind !== "session")
20775
- return;
20776
- try {
20777
- const matches = (await options.balances()).filter((b2) => b2.railId === payment.rail.id && b2.network === payment.network && b2.asset === payment.asset);
20778
- if (matches.length !== 1)
20779
- return;
20780
- const b = matches[0];
20781
- return {
20782
- depositingAtomic: b.depositing,
20783
- lockedAtomic: b.locked,
20784
- chargedAtomic: b.charged,
20785
- pendingAtomic: b.pending,
20786
- refundableAtomic: b.refundable
20787
- };
20788
- } catch {
20789
- return;
20790
- }
20791
- };
20792
- return {
20793
- async models(signal) {
20794
- const deadline = AbortSignal.timeout(CATALOG_DEADLINE_MS);
20795
- let response;
20796
- try {
20797
- response = await fetcher(base + "/v1/models", {
20798
- signal: signal ? AbortSignal.any([signal, deadline]) : deadline,
20799
- redirect: "error",
20800
- cache: "no-store",
20801
- credentials: "omit",
20802
- referrerPolicy: "no-referrer",
20803
- headers: { accept: "application/json" }
20804
- });
20805
- } catch {
20806
- throw new XgateError("catalog_unavailable");
20807
- }
20808
- if (!response.ok) {
20809
- await response.body?.cancel().catch(() => {
20810
- });
20811
- throw new XgateError("catalog_unavailable");
20812
- }
20813
- const v = parse(await bounded(response, CATALOG_BYTES));
20814
- if (!record(v) || !Array.isArray(v.data) || v.data.length > 128)
20815
- throw new XgateError("invalid_catalog");
20816
- return v.data.filter((m) => record(m) && (options.models === void 0 || options.models.includes(m.id))).map((m) => ({ ...m, xgate_origin: base }));
20817
- },
20818
- async purchase(invocation, signal, onResponse) {
20819
- route(invocation.route);
20820
- const body = new TextEncoder().encode(invocation.body);
20821
- if (body.byteLength === 0 || body.byteLength > REQUEST_BYTES)
20822
- throw new XgateError("invalid_invocation");
20823
- const id = invocation.id;
20824
- let result;
20825
- try {
20826
- result = await payer.request({
20827
- apiVersion: 1,
20828
- invocationId: id,
20829
- url: base + invocation.route,
20830
- method: "POST",
20831
- // Application headers only: the payer owns every payment header.
20832
- headers: {
20833
- "content-type": "application/json",
20834
- accept: "application/json"
20835
- },
20836
- body,
20837
- limits: { maxRequestBodyBytes: REQUEST_BYTES },
20838
- ...options.allocationId !== void 0 ? { allocationId: options.allocationId } : {},
20839
- ...options.policyRuleId !== void 0 ? { policyRuleId: options.policyRuleId } : {},
20840
- ...options.nonInteractive !== void 0 ? { nonInteractive: options.nonInteractive } : {}
20841
- }, {
20842
- ...signal ? { signal } : {},
20843
- ...options.confirmPayment ? { confirmPayment: options.confirmPayment } : {}
20844
- });
20845
- } catch (error) {
20846
- throw failure(error, id);
20847
- }
20848
- const payment = result.payment;
20849
- if (result.invocationId !== id || result.apiVersion !== 1)
20850
- throw new XgatePurchaseError("payer_result_mismatch", id, true, payment && display(payment));
20851
- if (!payment)
20852
- throw new XgatePurchaseError(result.response.status === 200 ? "payment_missing" : "request_rejected", id, false);
20853
- const shown = display(payment);
20854
- if (payment.charge !== "settled" || typeof payment.settledAmount !== "string")
20855
- throw new XgatePurchaseError("payment_unverified", id, true, shown);
20856
- shown.chargedAtomic = payment.settledAmount;
20857
- try {
20858
- if (atomic(payment.settledAmount) > atomic(payment.authorizedAmount))
20859
- throw new XgateError("charge_over_authorization");
20860
- } catch {
20861
- throw new XgatePurchaseError("invalid_settlement", id, true, shown);
20862
- }
20863
- const r = result.response, h = r.headers;
20864
- if (r.status !== 200 || !/^application\/json\b/i.test(r.contentType) || h["x-inference-privacy"] !== "anonymous" || h["x-result-retention"] !== "none" || !h["cache-control"]?.includes("no-store"))
20865
- throw new XgatePurchaseError("response_rejected", id, false, shown);
20866
- let value;
20867
- try {
20868
- value = parse(r.body);
20869
- } catch {
20870
- throw new XgatePurchaseError("invalid_response", id, false, shown);
20871
- }
20872
- if (!record(value) || typeof value.id !== "string" || value.result_replay_available !== void 0)
20873
- throw new XgatePurchaseError("invalid_response", id, false, shown);
20874
- const head = {
20875
- status: r.status,
20876
- headers: { "content-type": r.contentType, "cache-control": "no-store" }
20877
- };
20878
- try {
20879
- await onResponse?.(head);
20880
- } catch {
20881
- throw new XgatePurchaseError("response_hook_failed", id, false, shown);
20882
- }
20883
- const balance = await channel(payment);
20884
- if (balance)
20885
- shown.channel = balance;
20886
- return {
20887
- body: value,
20888
- settlement: {
20889
- chargedAtomic: payment.settledAmount,
20890
- reference: payment.transaction ?? payment.commitment ?? payment.paymentId
20891
- },
20892
- purchase: payment.commitment ?? payment.paymentId,
20893
- payment: shown
20894
- };
20895
- },
20896
- async status() {
20897
- return {
20898
- transport: "taskmarket",
20899
- origin: base,
20900
- ...options.balances ? {
20901
- balances: (await options.balances()).map((b) => ({
20902
- rail: b.railId,
20903
- network: b.network,
20904
- asset: b.asset,
20905
- depositingAtomic: b.depositing,
20906
- lockedAtomic: b.locked,
20907
- chargedAtomic: b.charged,
20908
- pendingAtomic: b.pending,
20909
- refundableAtomic: b.refundable
20910
- }))
20911
- } : {}
20912
- };
20913
- },
20914
- async reconcile(invocationId) {
20915
- const s = await payer.reconcile(invocationId);
20916
- return {
20917
- invocationId: s.invocationId,
20918
- state: s.state,
20919
- charge: s.charge,
20920
- uncertain: s.uncertain,
20921
- ...s.errorCode ? { errorCode: s.errorCode } : {},
20922
- ...s.payment ? { payment: display(s.payment) } : {}
20923
- };
20924
- }
20925
- };
20926
- }
20927
- function display(p) {
20928
- return {
20929
- network: p.network,
20930
- scheme: p.scheme,
20931
- asset: p.asset,
20932
- payTo: p.payTo,
20933
- rail: p.rail.kind,
20934
- authorizedAtomic: p.authorizedAmount,
20935
- ...p.charge === "settled" && p.settledAmount !== void 0 ? { chargedAtomic: p.settledAmount } : {},
20936
- status: p.charge,
20937
- ...p.transaction ?? p.commitment ? { reference: p.transaction ?? p.commitment } : {}
20938
- };
20939
- }
20940
- function failure(error, id) {
20941
- const e = error;
20942
- const code = typeof e?.code === "string" && /^[a-z0-9_]{1,64}$/.test(e.code) ? e.code : "payer_failed";
20943
- const recorded = e?.invocation;
20944
- const pending = e?.pending !== false || recorded !== void 0 && !(recorded.uncertain === false && RESOLVED_CHARGES.includes(recorded.charge) && RESOLVED_STATES.includes(recorded.state));
20945
- const summary = e?.invocation?.payment;
20946
- return new XgatePurchaseError(code, id, pending, record(summary) ? display(summary) : void 0);
20947
- }
20948
- async function bounded(response, limit) {
20949
- if (!response.body)
20950
- throw new XgateError("invalid_catalog");
20951
- const reader = response.body.getReader(), chunks = [];
20952
- let length = 0;
20953
- try {
20954
- for (; ; ) {
20955
- const part = await reader.read();
20956
- if (part.done)
20957
- break;
20958
- length += part.value.byteLength;
20959
- if (length > limit)
20960
- throw new XgateError("catalog_bound");
20961
- chunks.push(part.value);
20962
- }
20963
- } catch (error) {
20964
- await reader.cancel().catch(() => {
20965
- });
20966
- throw error instanceof XgateError ? error : new XgateError("catalog_unavailable");
20967
- } finally {
20968
- reader.releaseLock();
20969
- }
20970
- return Buffer.concat(chunks, length);
20971
- }
20972
- function parse(bytes) {
20973
- try {
20974
- return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
20975
- } catch {
20976
- throw new XgateError("invalid_response");
20977
- }
20978
- }
20979
-
20980
20705
  // src/lib/agent/inference-channel.ts
20981
20706
  var MAX_MESSAGE_BYTES2 = 48 * 1024 * 1024;
20982
20707
  var CATALOG_TIMEOUT_MS = 3e4;
20708
+ var BUY_TIMEOUT_MS = 5 * 6e4;
20709
+ var CODE = /^[a-z0-9_]{1,64}$/;
20983
20710
  function lines2(stream, onLine, onOverflow, maxBytes) {
20984
20711
  let chunks = [];
20985
20712
  let size = 0;
@@ -21113,6 +20840,31 @@ function serveInferenceChannel(channel, transport, options = {}) {
21113
20840
  (typeof request.invocationId === "string" && transport.reconcile ? transport.reconcile(request.invocationId) : Promise.reject(new XgateError("purchase_id_required"))).then((value) => ({ value }))
21114
20841
  );
21115
20842
  return;
20843
+ case "budget":
20844
+ void answer(
20845
+ request.id,
20846
+ (options.views?.budget ? options.views.budget({ unresolved: [...unresolved] }) : Promise.reject(new XgateError("unsupported_by_transport"))).then((value) => ({ value }))
20847
+ );
20848
+ return;
20849
+ case "credit_offer":
20850
+ void answer(
20851
+ request.id,
20852
+ (options.views?.creditOffer ? options.views.creditOffer() : Promise.reject(new XgateError("unsupported_by_transport"))).then((value) => ({ value }))
20853
+ );
20854
+ return;
20855
+ case "buy_credits": {
20856
+ const amount = request.amountUsd;
20857
+ void answer(
20858
+ request.id,
20859
+ (amount !== void 0 && !(Number.isSafeInteger(amount) && amount > 0) ? Promise.reject(new XgateError("invalid_amount")) : options.views?.buyCredits ? options.views.buyCredits(amount).catch((error) => {
20860
+ const code = error.code;
20861
+ throw new XgateError(
20862
+ typeof code === "string" && CODE.test(code) ? code : "purchase_failed"
20863
+ );
20864
+ }) : Promise.reject(new XgateError("unsupported_by_transport"))).then((value) => ({ value }))
20865
+ );
20866
+ return;
20867
+ }
21116
20868
  case "abort":
21117
20869
  running.get(request.target)?.abort.abort();
21118
20870
  return;
@@ -21158,170 +20910,6 @@ function serveInferenceChannel(channel, transport, options = {}) {
21158
20910
  };
21159
20911
  }
21160
20912
 
21161
- // src/lib/agent/payer.ts
21162
- function routeOfCatalogRow(row) {
21163
- return row?.owned_by === "anthropic" ? "/providers/anthropic/v1/messages" : "/providers/openai/v1/responses";
21164
- }
21165
- async function createAgentRails(options) {
21166
- const journalPath = getX402JournalPath();
21167
- const policyPath = getX402PolicyPath();
21168
- const loadPolicy = (loadOptions) => loadX402Policy(policyPath, loadOptions);
21169
- const x402Policy = await loadX402Policy(policyPath);
21170
- const allocations = createX402Allocations({ journalPath, loadPolicy });
21171
- return options.policy.rails.map((rail) => {
21172
- if (rail.wallet.toLowerCase() !== options.walletAddress.toLowerCase()) {
21173
- throw new AgentLaunchError(
21174
- "invalid_agent_policy",
21175
- `Rail ${rail.id} pays from ${rail.wallet}, but the wallet is ${options.walletAddress}`
21176
- );
21177
- }
21178
- let rpcUrl;
21179
- try {
21180
- rpcUrl = rpcUrlForNetwork(x402Policy, rail.network);
21181
- } catch (error) {
21182
- throw new AgentLaunchError("invalid_agent_policy", error.message);
21183
- }
21184
- const channel = {
21185
- network: rail.network,
21186
- payer: rail.wallet,
21187
- receiver: rail.receiver,
21188
- receiverAuthorizer: rail.receiverAuthorizer,
21189
- salt: rail.salt
21190
- };
21191
- const payer = createBaseChannelPayer({
21192
- channel,
21193
- reader: createBaseChannelRpcReader({ channel, rpcUrl, fromBlock: BigInt(rail.fromBlock) }),
21194
- resourceUrl: `${rail.origin}${rail.route}`,
21195
- journalPath,
21196
- loadPolicy,
21197
- getAccount: options.getAccount,
21198
- allocationId: rail.allocationId,
21199
- budgetAmount: rail.budgetAmount,
21200
- ...rail.depositAmount !== void 0 ? { depositAmount: rail.depositAmount } : {},
21201
- ...rail.policyRuleId !== void 0 ? { policyRuleId: rail.policyRuleId } : {},
21202
- ...options.fetch ? { fetch: options.fetch } : {}
21203
- });
21204
- const transport = createTaskMarketTransport({
21205
- payer,
21206
- origin: rail.origin,
21207
- allocationId: rail.allocationId,
21208
- ...rail.policyRuleId !== void 0 ? { policyRuleId: rail.policyRuleId } : {},
21209
- nonInteractive: true,
21210
- models: options.models,
21211
- balances: () => allocations.balances(),
21212
- ...options.fetch ? { fetch: options.fetch } : {}
21213
- });
21214
- return { rail, payer, transport };
21215
- });
21216
- }
21217
- function bodyModel(body) {
21218
- try {
21219
- const model = JSON.parse(body)?.model;
21220
- return typeof model === "string" ? model : void 0;
21221
- } catch {
21222
- return void 0;
21223
- }
21224
- }
21225
- async function neverRecorded(payer, invocationId) {
21226
- try {
21227
- await payer.status(invocationId);
21228
- return false;
21229
- } catch (error) {
21230
- return error.code === "invocation_not_found";
21231
- }
21232
- }
21233
- function createAgentTransport(rails) {
21234
- const byRoute = new Map(rails.map((r) => [r.rail.route, r]));
21235
- let offered = /* @__PURE__ */ new Map();
21236
- return {
21237
- async models(signal) {
21238
- const seen = /* @__PURE__ */ new Map();
21239
- const rows = [];
21240
- for (const { rail, transport } of rails) {
21241
- for (const row of await transport.models(signal)) {
21242
- const id = row.id;
21243
- if (routeOfCatalogRow(row) !== rail.route || typeof id !== "string" || seen.has(id)) {
21244
- continue;
21245
- }
21246
- seen.set(id, rail.route);
21247
- rows.push(row);
21248
- }
21249
- }
21250
- offered = seen;
21251
- return rows;
21252
- },
21253
- async purchase(invocation, signal, onResponse) {
21254
- const rail = byRoute.get(invocation.route);
21255
- if (!rail) {
21256
- throw new XgatePurchaseError("no_rail_for_route", invocation.id, false);
21257
- }
21258
- const model = typeof invocation.body === "string" ? bodyModel(invocation.body) : void 0;
21259
- if (model === void 0 || offered.get(model) !== invocation.route) {
21260
- throw new XgatePurchaseError("model_not_offered", invocation.id, false);
21261
- }
21262
- const leftover = (await rail.payer.channelStatus()).pendingOperation;
21263
- if (leftover && leftover.id !== invocation.id) {
21264
- throw new XgatePurchaseError("channel_operation_pending", invocation.id, false);
21265
- }
21266
- try {
21267
- return await rail.transport.purchase(invocation, signal, onResponse);
21268
- } catch (error) {
21269
- if (error instanceof XgatePurchaseError && error.pending && await neverRecorded(rail.payer, invocation.id)) {
21270
- throw new XgatePurchaseError(
21271
- error.code === "outcome_uncertain" ? "channel_operation_pending" : error.code,
21272
- invocation.id,
21273
- false
21274
- );
21275
- }
21276
- throw error;
21277
- }
21278
- },
21279
- async status() {
21280
- return {
21281
- transport: "taskmarket",
21282
- rails: await Promise.all(
21283
- rails.map(async ({ rail, transport }) => ({
21284
- rail: rail.id,
21285
- ...await transport.status()
21286
- }))
21287
- )
21288
- };
21289
- },
21290
- async reconcile(invocationId) {
21291
- for (const { transport } of rails) {
21292
- try {
21293
- return await transport.reconcile(invocationId);
21294
- } catch (error) {
21295
- if (error.code !== "invocation_not_found") throw error;
21296
- }
21297
- }
21298
- throw new XgateError("invocation_not_found");
21299
- }
21300
- };
21301
- }
21302
- async function reconcileLeftoverOperations(rails, log2 = () => void 0) {
21303
- for (const { rail, payer } of rails) {
21304
- let pending = (await payer.channelStatus()).pendingOperation;
21305
- if (!pending) continue;
21306
- if (pending.kind === "payment") {
21307
- log2(`Reconciling payment ${pending.id} left on rail ${rail.id}`);
21308
- try {
21309
- await payer.reconcile(pending.id);
21310
- } catch (error) {
21311
- log2(
21312
- `Payment ${pending.id} could not be reconciled: ${error.code ?? error.message}`
21313
- );
21314
- }
21315
- pending = (await payer.channelStatus()).pendingOperation;
21316
- if (!pending) continue;
21317
- }
21318
- throw new AgentLaunchError(
21319
- "channel_operation_pending",
21320
- `The ${rail.id} channel has an unresolved ${pending.kind} operation ${pending.id} from an earlier session; it must be reconciled before anything else is bought on that channel. Try again later.`
21321
- );
21322
- }
21323
- }
21324
-
21325
20913
  // src/lib/agent/pi-settings.ts
21326
20914
  import { randomUUID } from "crypto";
21327
20915
  import { promises as fs } from "fs";
@@ -21384,110 +20972,1232 @@ async function writeManagedPiSettings(agentDir, managed) {
21384
20972
  return settings;
21385
20973
  }
21386
20974
 
21387
- // src/lib/agent/policy.ts
20975
+ // src/lib/agent/session-budget.ts
20976
+ var COMPUTE_TTL_MS = 6e4;
20977
+ function aggregateBudget(rails, allocations) {
20978
+ const id = rails[0]?.rail.budgetAllocationId;
20979
+ const found = allocations.find((allocation) => allocation.allocationId === id);
20980
+ return found ? { allocationId: found.allocationId, cap: found.caps.amount, used: found.used.total } : null;
20981
+ }
20982
+ function createSessionBudget(options) {
20983
+ const now = options.now ?? Date.now;
20984
+ const readCompute = options.readCompute ?? readComputeView;
20985
+ const buyCompute = options.buyCompute ?? buyComputeChunk;
20986
+ let turns = 0;
20987
+ let charged = 0n;
20988
+ let lastTurn = null;
20989
+ let warning = null;
20990
+ let compute;
20991
+ const running = /* @__PURE__ */ new Set();
20992
+ const track = (work) => {
20993
+ running.add(work);
20994
+ void work.then(
20995
+ () => running.delete(work),
20996
+ () => running.delete(work)
20997
+ );
20998
+ return work;
20999
+ };
21000
+ const inner = options.transport;
21001
+ const transport = {
21002
+ ...inner,
21003
+ async purchase(invocation, signal, onResponse) {
21004
+ const result = await inner.purchase(invocation, signal, onResponse);
21005
+ const amount = result.settlement?.chargedAtomic;
21006
+ if (typeof amount === "string" && /^\d{1,40}$/.test(amount)) {
21007
+ turns += 1;
21008
+ charged += BigInt(amount);
21009
+ lastTurn = { invocationId: invocation.id, chargedAtomic: amount };
21010
+ }
21011
+ return result;
21012
+ }
21013
+ };
21014
+ const computeView = () => {
21015
+ if (!compute || now() - compute.at >= COMPUTE_TTL_MS) {
21016
+ compute = {
21017
+ at: now(),
21018
+ value: track(
21019
+ readCompute({
21020
+ warning,
21021
+ ...options.observeCompute ? { observe: options.observeCompute } : {}
21022
+ })
21023
+ )
21024
+ };
21025
+ }
21026
+ return compute.value;
21027
+ };
21028
+ return {
21029
+ transport,
21030
+ async view({ unresolved }) {
21031
+ const [channels, allocations, computeValue] = await Promise.all([
21032
+ Promise.all(options.rails.map((rail) => channelView(rail))),
21033
+ computeAllocationId().then((id) => allocationViews(options.rails, [id])),
21034
+ computeView()
21035
+ ]);
21036
+ return {
21037
+ version: AGENT_VIEW_VERSION,
21038
+ lastTurn,
21039
+ session: { turns, chargedAtomic: charged.toString() },
21040
+ channels,
21041
+ allocations,
21042
+ unresolved,
21043
+ compute: computeValue,
21044
+ budget: aggregateBudget(options.rails, allocations)
21045
+ };
21046
+ },
21047
+ creditOffer: () => (options.offerCompute ?? computeCreditOffer)(),
21048
+ async buyCredits(amountUsd) {
21049
+ try {
21050
+ return await track(buyCompute(amountUsd));
21051
+ } finally {
21052
+ compute = void 0;
21053
+ }
21054
+ },
21055
+ async settled() {
21056
+ await Promise.allSettled([...running]);
21057
+ },
21058
+ noteCreditWarning(next) {
21059
+ if (next !== warning) compute = void 0;
21060
+ warning = next;
21061
+ }
21062
+ };
21063
+ }
21064
+
21065
+ // src/lib/agent/deposit-window.ts
21066
+ var DEPOSIT_WINDOW_SECONDS = 86400;
21067
+ function depositWindows(total, routes) {
21068
+ const count = BigInt(routes.length);
21069
+ const share = BigInt(total) / count;
21070
+ const remainder = BigInt(total) - share * count;
21071
+ return new Map(
21072
+ routes.map((route, index) => [route, String(index === 0 ? share + remainder : share)])
21073
+ );
21074
+ }
21075
+ var CENT = 10000n;
21076
+ var roundUp = (value, unit) => (value + unit - 1n) / unit * unit;
21077
+ function roundUpToSecond(date) {
21078
+ return new Date(Math.ceil(date.getTime() / 1e3) * 1e3).toISOString().replace(".000Z", "Z");
21079
+ }
21080
+ async function checkDepositWindows(input) {
21081
+ const records = await listX402Payments({}, input.journalPath);
21082
+ const deposit = BigInt(input.limits.deposit) < BigInt(input.limits.total) ? BigInt(input.limits.deposit) : BigInt(input.limits.total);
21083
+ const windows = depositWindows(input.limits.total, input.routes);
21084
+ const availability = (rail, maximum) => x402SpendWindowAvailability({
21085
+ records,
21086
+ ruleId: rail.ruleId,
21087
+ network: rail.network,
21088
+ asset: rail.asset,
21089
+ windowSeconds: DEPOSIT_WINDOW_SECONDS,
21090
+ maximum,
21091
+ requested: deposit,
21092
+ now: input.now
21093
+ });
21094
+ const blocks = [];
21095
+ for (const rail of input.rails) {
21096
+ if (!rail.newChannel) continue;
21097
+ const window2 = windows.get(rail.route);
21098
+ if (window2 === void 0) continue;
21099
+ const result = availability(rail, window2);
21100
+ if (result.fits) continue;
21101
+ blocks.push({
21102
+ railId: rail.railId,
21103
+ route: rail.route,
21104
+ window: window2,
21105
+ used: String(result.used),
21106
+ deposit: String(deposit),
21107
+ ...result.fitsAt ? { resetsAt: roundUpToSecond(result.fitsAt) } : {}
21108
+ });
21109
+ }
21110
+ if (blocks.length === 0) return void 0;
21111
+ const needs = /* @__PURE__ */ new Map();
21112
+ for (const rail of input.rails) {
21113
+ if (!input.routes.includes(rail.route)) continue;
21114
+ const used = availability(rail, "0").used;
21115
+ needs.set(rail.route, (needs.get(rail.route) ?? 0n) + used + deposit);
21116
+ }
21117
+ const fitsAll = (total2) => {
21118
+ const shares = depositWindows(String(total2), input.routes);
21119
+ return [...needs].every(([route, need]) => BigInt(shares.get(route)) >= need);
21120
+ };
21121
+ const n = BigInt(input.routes.length);
21122
+ const first = needs.get(input.routes[0]) ?? 0n;
21123
+ const others = input.routes.slice(1).reduce((max, route) => (needs.get(route) ?? 0n) > max ? needs.get(route) : max, 0n);
21124
+ const q = others > first - (n - 1n) ? others : first - (n - 1n);
21125
+ let total = n * q + (first > q ? first - q : 0n);
21126
+ if (input.floor !== void 0 && input.floor > total) total = input.floor;
21127
+ total = roundUp(total, CENT);
21128
+ if (!fitsAll(total)) {
21129
+ const largest = first > others ? first : others;
21130
+ const even = (total + n - 1n) / n;
21131
+ total = n * roundUp(even > largest ? even : largest, CENT);
21132
+ }
21133
+ const resets = blocks.map((block) => block.resetsAt);
21134
+ const resetsAt = resets.every((value) => value !== void 0) ? resets.reduce((latest, value) => value > latest ? value : latest) : void 0;
21135
+ return { blocks, minimumTotal: String(total), ...resetsAt ? { resetsAt } : {} };
21136
+ }
21137
+
21138
+ // src/lib/agent/onboarding.ts
21139
+ import { randomBytes, randomUUID as randomUUID2 } from "crypto";
21140
+ import { createPublicClient, erc20Abi, http } from "viem";
21141
+
21142
+ // src/lib/agent/rail-profile.ts
21388
21143
  import { promises as fs2 } from "fs";
21389
- import path5 from "path";
21390
21144
  import { z } from "zod";
21391
- var XGATE_ROUTES = [
21392
- "/providers/anthropic/v1/messages",
21393
- "/providers/openai/v1/responses"
21394
- ];
21145
+ var AGENT_RAIL_PROFILE_ENV = "TASKMARKET_AGENT_RAIL_PROFILE";
21146
+ var AGENT_RAIL_PROFILE_FILE_ENV = "TASKMARKET_AGENT_RAIL_PROFILE_FILE";
21147
+ var AGENT_DEVELOPER_ENV = "TASKMARKET_AGENT_DEVELOPER";
21148
+ var BASE_MAINNET = "eip155:8453";
21395
21149
  var address = z.string().regex(/^0x[0-9a-fA-F]{40}$/, "expected a 0x-prefixed 20-byte address");
21396
- var atomic2 = z.string().regex(/^(0|[1-9][0-9]{0,30})$/, "expected an integer amount string");
21397
- var ruleId = z.string().regex(/^[a-z0-9][a-z0-9._-]{0,63}$/, "invalid id");
21398
- var origin2 = z.string().url().refine((value) => {
21399
- const url = new URL(value);
21400
- return url.protocol === "https:" && !url.username && !url.password && url.pathname === "/" && !url.search && !url.hash && !value.endsWith("/");
21401
- }, "expected an HTTPS origin such as https://api.xgate.run");
21402
- var BaseBatchChannelRailSchema = z.object({
21403
- id: ruleId,
21150
+ var InferenceRailProfileSchema = z.object({
21151
+ /** Shown to the user, e.g. `base-mainnet`. */
21152
+ name: z.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/),
21404
21153
  kind: z.literal("base-batch-channel"),
21154
+ origin: z.string().url(),
21405
21155
  network: z.enum(["eip155:8453", "eip155:84532"]),
21406
- /** The wallet that funds deposits and signs vouchers; must be the keystore's wallet. */
21407
- wallet: address,
21408
- origin: origin2,
21409
- route: z.enum(XGATE_ROUTES),
21156
+ scheme: z.literal("batch-settlement"),
21157
+ asset: address,
21158
+ assetSymbol: z.literal("USDC"),
21159
+ assetDecimals: z.literal(6),
21410
21160
  receiver: address,
21411
21161
  receiverAuthorizer: address,
21412
- salt: z.string().regex(/^0x[0-9a-fA-F]{64}$/, "expected a 32-byte hex salt"),
21413
- /** A block at or before the channel's first deposit; return scans start here. */
21414
- fromBlock: z.string().regex(/^(0|[1-9][0-9]{0,19})$/),
21415
- allocationId: z.string().min(1).max(128),
21416
- budgetAmount: atomic2,
21417
- depositAmount: atomic2.optional(),
21418
- policyRuleId: ruleId.optional()
21162
+ withdrawDelaySeconds: z.number().int().positive().max(31536e3),
21163
+ /** The environment variable the x402 policy names for this network's RPC URL. */
21164
+ rpcUrlEnv: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/),
21165
+ /** Local devnet only: let the payer reach a loopback origin. */
21166
+ allowPrivateNetwork: z.boolean().default(false)
21419
21167
  }).strict();
21420
- var AgentPolicySchema = z.object({
21421
- version: z.literal(1),
21422
- /** xgate model ids the user approved. Only those the router also qualifies are offered. */
21423
- models: z.array(z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/, "invalid model id")).min(1).max(64),
21424
- rails: z.array(BaseBatchChannelRailSchema).min(1).max(XGATE_ROUTES.length)
21425
- }).strict().superRefine((policy, ctx) => {
21426
- const ids = /* @__PURE__ */ new Set();
21427
- const routes = /* @__PURE__ */ new Set();
21428
- policy.rails.forEach((rail, index) => {
21429
- if (ids.has(rail.id)) {
21430
- ctx.addIssue({
21431
- code: "custom",
21432
- message: `duplicate rail id ${rail.id}`,
21433
- path: ["rails", index]
21434
- });
21435
- }
21436
- if (routes.has(rail.route)) {
21437
- ctx.addIssue({
21438
- code: "custom",
21439
- message: `more than one rail pays ${rail.route}`,
21440
- path: ["rails", index]
21441
- });
21442
- }
21443
- ids.add(rail.id);
21444
- routes.add(rail.route);
21445
- });
21446
- if (new Set(policy.models).size !== policy.models.length) {
21447
- ctx.addIssue({ code: "custom", message: "duplicate model id", path: ["models"] });
21168
+ var BASE_MAINNET_PROFILE = {
21169
+ name: "base-mainnet",
21170
+ kind: "base-batch-channel",
21171
+ origin: "https://api.xgate.run",
21172
+ network: BASE_MAINNET,
21173
+ scheme: "batch-settlement",
21174
+ asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
21175
+ assetSymbol: "USDC",
21176
+ assetDecimals: 6,
21177
+ withdrawDelaySeconds: 900,
21178
+ rpcUrlEnv: "BASE_RPC_URL",
21179
+ allowPrivateNetwork: false
21180
+ };
21181
+ var STAGING_PROFILE = {
21182
+ name: "staging",
21183
+ kind: "base-batch-channel",
21184
+ origin: "https://devnet-api.xgate.run",
21185
+ network: "eip155:84532",
21186
+ scheme: "batch-settlement",
21187
+ asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
21188
+ assetSymbol: "USDC",
21189
+ assetDecimals: 6,
21190
+ receiver: "0x11E5BA3a2593c44d6d180054A4c8aa839202B8dd",
21191
+ receiverAuthorizer: "0x3721824a31197dcDD2984cF43b92B6cc8A87c0Fb",
21192
+ withdrawDelaySeconds: 3600,
21193
+ rpcUrlEnv: "BASE_SEPOLIA_RPC_URL",
21194
+ allowPrivateNetwork: false
21195
+ };
21196
+ var RAIL_PROFILES = {
21197
+ [BASE_MAINNET_PROFILE.name]: BASE_MAINNET_PROFILE,
21198
+ [STAGING_PROFILE.name]: STAGING_PROFILE
21199
+ };
21200
+ var NETWORK_NAMES = {
21201
+ "eip155:8453": "Base",
21202
+ "eip155:84532": "Base Sepolia (testnet)"
21203
+ };
21204
+ function unavailable(message) {
21205
+ return new AgentLaunchError("inference_rail_unavailable", `${message}. Nothing was written.`);
21206
+ }
21207
+ function isLoopbackOrigin(origin) {
21208
+ const host = new URL(origin).hostname;
21209
+ return host === "localhost" || host === "[::1]" || /^127(\.\d{1,3}){3}$/.test(host);
21210
+ }
21211
+ function pinnedProfile(name) {
21212
+ const pinned = Object.hasOwn(RAIL_PROFILES, name) ? RAIL_PROFILES[name] : void 0;
21213
+ if (!pinned) {
21214
+ throw unavailable(
21215
+ `${AGENT_RAIL_PROFILE_ENV} names ${JSON.stringify(name)}, which is not a rail profile this CLI ships (${Object.keys(RAIL_PROFILES).join(", ")})`
21216
+ );
21448
21217
  }
21449
- });
21450
- function getAgentPolicyPath() {
21451
- return path5.join(getTaskmarketHome(), "agent", "policy.json");
21218
+ const parsed = InferenceRailProfileSchema.safeParse(pinned);
21219
+ if (!parsed.success) {
21220
+ throw unavailable(
21221
+ `The ${name} inference channel for taskmarket agent is not published yet, so there is nothing to approve`
21222
+ );
21223
+ }
21224
+ return parsed.data;
21452
21225
  }
21453
- async function loadAgentPolicy(policyPath = getAgentPolicyPath()) {
21226
+ async function customProfile(file) {
21454
21227
  let raw;
21455
21228
  try {
21456
- raw = await fs2.readFile(policyPath, "utf8");
21229
+ raw = JSON.parse(await fs2.readFile(file, "utf8"));
21457
21230
  } catch (error) {
21458
- if (error.code === "ENOENT") {
21231
+ throw unavailable(
21232
+ `${AGENT_RAIL_PROFILE_FILE_ENV} names ${file}, which could not be read as JSON: ${error.message}`
21233
+ );
21234
+ }
21235
+ const parsed = InferenceRailProfileSchema.safeParse(raw);
21236
+ if (!parsed.success) {
21237
+ const issue = parsed.error.issues[0];
21238
+ throw unavailable(`${file}: ${issue.path.join(".") || "(root)"}: ${issue.message}`);
21239
+ }
21240
+ const profile = parsed.data;
21241
+ if (profile.network === BASE_MAINNET) {
21242
+ throw unavailable(
21243
+ `${file}: a custom rail profile cannot use Base mainnet (${BASE_MAINNET}); mainnet terms are pinned in the CLI`
21244
+ );
21245
+ }
21246
+ if (Object.hasOwn(RAIL_PROFILES, profile.name)) {
21247
+ throw unavailable(`${file}: the name ${profile.name} belongs to a pinned rail profile`);
21248
+ }
21249
+ const url = new URL(profile.origin);
21250
+ if (url.protocol !== "https:" || url.origin !== profile.origin) {
21251
+ throw unavailable(`${file}: origin must be an HTTPS origin with nothing after it`);
21252
+ }
21253
+ if (profile.allowPrivateNetwork && !isLoopbackOrigin(profile.origin)) {
21254
+ throw unavailable(
21255
+ `${file}: allowPrivateNetwork is only for a local devnet, whose origin is a loopback address`
21256
+ );
21257
+ }
21258
+ return profile;
21259
+ }
21260
+ async function loadInferenceRailProfile(env = process.env) {
21261
+ const name = env[AGENT_RAIL_PROFILE_ENV]?.trim();
21262
+ const file = env[AGENT_RAIL_PROFILE_FILE_ENV]?.trim();
21263
+ if (!file) return pinnedProfile(name || BASE_MAINNET_PROFILE.name);
21264
+ if (name) {
21265
+ throw unavailable(
21266
+ `Set either ${AGENT_RAIL_PROFILE_ENV} (a pinned profile) or ${AGENT_RAIL_PROFILE_FILE_ENV} (a development profile), not both`
21267
+ );
21268
+ }
21269
+ if (env[AGENT_DEVELOPER_ENV]?.trim() !== "1") {
21270
+ throw unavailable(
21271
+ `${AGENT_RAIL_PROFILE_FILE_ENV} is a development setting and is read only with ${AGENT_DEVELOPER_ENV}=1; set ${AGENT_RAIL_PROFILE_ENV} to a pinned profile (${Object.keys(RAIL_PROFILES).join(", ")}) instead`
21272
+ );
21273
+ }
21274
+ return customProfile(file);
21275
+ }
21276
+
21277
+ // src/lib/agent/onboarding.ts
21278
+ var ONBOARDING_DEFAULTS = {
21279
+ depositUsd: "5",
21280
+ perRequestUsd: "1",
21281
+ /** The total defaults to this many deposits. */
21282
+ totalDeposits: 2n,
21283
+ expiryDays: 30,
21284
+ computePerDayUsd: "10",
21285
+ computePerMonthUsd: "20",
21286
+ autoTopUpChunksPerDay: 1
21287
+ };
21288
+ var MAX_EXPIRY_DAYS = 365;
21289
+ var MAX_ATTEMPTS = 5;
21290
+ var DAY_MS = 864e5;
21291
+ function rate(value) {
21292
+ const r = value;
21293
+ if (typeof r?.numerator !== "string" || !/^\d{1,13}$/.test(r.numerator)) return void 0;
21294
+ if (!Number.isSafeInteger(r.denominator) || r.denominator <= 0) return void 0;
21295
+ return Number(r.numerator) / r.denominator;
21296
+ }
21297
+ async function readXgateCatalog(origin, fetchImpl = fetch) {
21298
+ const response = await fetchImpl(`${origin}/v1/models`, {
21299
+ signal: AbortSignal.timeout(15e3),
21300
+ redirect: "error",
21301
+ headers: { accept: "application/json" }
21302
+ });
21303
+ if (!response.ok) throw new Error(`the model catalog answered ${response.status}`);
21304
+ const body = await response.json();
21305
+ if (!Array.isArray(body.data)) throw new Error("the model catalog is malformed");
21306
+ const models = [];
21307
+ for (const row of body.data.slice(0, 128)) {
21308
+ const id = row?.id;
21309
+ if (typeof id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(id)) continue;
21310
+ const rates = row.execution_profile?.rates;
21311
+ if (!rates) continue;
21312
+ models.push({
21313
+ id,
21314
+ route: routeOfCatalogRow(row),
21315
+ inputUsdPerMTok: rate(rates["input_uncached"]),
21316
+ outputUsdPerMTok: rate(rates["output"])
21317
+ });
21318
+ }
21319
+ return models;
21320
+ }
21321
+ var CHAIN_IDS = {
21322
+ "eip155:8453": 8453,
21323
+ "eip155:84532": 84532
21324
+ };
21325
+ async function readBaseChain(profile, rpcUrl, wallet) {
21326
+ const client = createPublicClient({
21327
+ transport: http(rpcUrl, { timeout: 15e3, retryCount: 1 })
21328
+ });
21329
+ const chainId = await client.getChainId();
21330
+ if (chainId !== CHAIN_IDS[profile.network]) {
21331
+ throw new Error(`the RPC serves chain ${chainId}, not ${profile.network}`);
21332
+ }
21333
+ const blockNumber = await client.getBlockNumber();
21334
+ let usdcBalance;
21335
+ try {
21336
+ usdcBalance = await client.readContract({
21337
+ address: profile.asset,
21338
+ abi: erc20Abi,
21339
+ functionName: "balanceOf",
21340
+ args: [wallet]
21341
+ });
21342
+ } catch {
21343
+ }
21344
+ return { blockNumber, usdcBalance };
21345
+ }
21346
+ function defaultDeps() {
21347
+ return {
21348
+ now: () => /* @__PURE__ */ new Date(),
21349
+ loadProfile: () => loadInferenceRailProfile(),
21350
+ readCatalog: (origin) => readXgateCatalog(origin),
21351
+ readChain: readBaseChain,
21352
+ computePricing: fetchComputePricing,
21353
+ computeBalance: readComputeCreditBalance,
21354
+ computeConfig: loadComputeCreditConfig,
21355
+ async computeSetUp(input, pricing) {
21356
+ const plan = await planComputeCreditSetup(input, pricing);
21357
+ return applyComputeCreditSetup(
21358
+ plan,
21359
+ `Approved at the terminal by the device owner during taskmarket agent setup: compute credit caps $${plan.caps.perDayUsd}/day, $${plan.caps.perMonthUsd}/month, $${plan.caps.totalUsd} total${plan.autoTopUp ? `; auto-top-up $${plan.autoTopUp.amountUsd} below ${plan.autoTopUp.thresholdCredits} credits, at most ${plan.autoTopUp.maxChunksPerDay}/day` : ""}`
21360
+ );
21361
+ },
21362
+ async computeBuy(amountUsd) {
21363
+ const bought = await buyComputeCredit({ amountUsd });
21364
+ return { credits: bought.purchase.credits };
21365
+ },
21366
+ randomSalt: () => `0x${randomBytes(32).toString("hex")}`,
21367
+ env: process.env
21368
+ };
21369
+ }
21370
+ var usd = (atomic) => `$${atomicToUsd(String(atomic))}`;
21371
+ function tooManyAnswers() {
21372
+ return new AgentLaunchError(
21373
+ "setup_cancelled",
21374
+ "Setup was cancelled after too many answers that could not be used; nothing more was written"
21375
+ );
21376
+ }
21377
+ async function askUsd(prompter, question, fallbackAtomic, check = () => void 0) {
21378
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
21379
+ const answer = (await prompter.ask(question, usd(fallbackAtomic))).replace(/^\$/, "");
21380
+ let atomic;
21381
+ try {
21382
+ atomic = usdToAtomic(answer);
21383
+ } catch {
21384
+ prompter.say([` Not a dollar amount above zero: ${answer}`]);
21385
+ continue;
21386
+ }
21387
+ const problem = check(BigInt(atomic));
21388
+ if (problem) {
21389
+ prompter.say([` ${problem}`]);
21390
+ continue;
21391
+ }
21392
+ return atomic;
21393
+ }
21394
+ throw tooManyAnswers();
21395
+ }
21396
+ async function askWhole(prompter, question, fallback, min, max) {
21397
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
21398
+ const answer = await prompter.ask(question, String(fallback));
21399
+ if (/^\d{1,6}$/.test(answer) && Number(answer) >= min && Number(answer) <= max) {
21400
+ return Number(answer);
21401
+ }
21402
+ prompter.say([` Enter a whole number from ${min} to ${max}`]);
21403
+ }
21404
+ throw tooManyAnswers();
21405
+ }
21406
+ async function askYesNo(prompter, question, fallback) {
21407
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
21408
+ const answer = (await prompter.ask(question, fallback ? "yes" : "no")).toLowerCase();
21409
+ if (answer === "yes" || answer === "y") return true;
21410
+ if (answer === "no" || answer === "n") return false;
21411
+ prompter.say([" Answer yes or no"]);
21412
+ }
21413
+ throw tooManyAnswers();
21414
+ }
21415
+ function duration(seconds) {
21416
+ if (seconds % 3600 === 0) return `${seconds / 3600} hour${seconds === 3600 ? "" : "s"}`;
21417
+ if (seconds % 60 === 0) return `${seconds / 60} minutes`;
21418
+ return `${seconds} seconds`;
21419
+ }
21420
+ function price(value) {
21421
+ return value === void 0 ? "unlisted" : `$${Number(value.toPrecision(6))}`;
21422
+ }
21423
+ function termsLines(profile, wallet, models, chain) {
21424
+ return [
21425
+ "",
21426
+ "Inference terms",
21427
+ ` Wallet: ${wallet} (its key stays in the keystore and is never asked for)`,
21428
+ ...chain.usdcBalance !== void 0 ? [` Balance: ${usd(chain.usdcBalance)} ${profile.assetSymbol}`] : [],
21429
+ ` Origin: ${profile.origin}`,
21430
+ ` Network: ${NETWORK_NAMES[profile.network]} (${profile.network})`,
21431
+ ` Asset: ${profile.assetSymbol} ${profile.asset}`,
21432
+ ` Receiver: ${profile.receiver} (authorizer ${profile.receiverAuthorizer})`,
21433
+ ` Scheme: ${profile.scheme}, profile ${profile.name}`,
21434
+ " Models and prices, per million tokens. Each turn is charged its actual usage, at most",
21435
+ " the per-request limit:",
21436
+ ...models.map(
21437
+ (model) => ` ${model.id}: input ${price(model.inputUsdPerMTok)}, output ${price(model.outputUsdPerMTok)}`
21438
+ )
21439
+ ];
21440
+ }
21441
+ function channelLines(profile, routes, limits, usedSoFar, blocked) {
21442
+ const windows = depositWindows(limits.total, routes);
21443
+ return [
21444
+ "",
21445
+ `Inference channel (x402 ${profile.scheme} on ${NETWORK_NAMES[profile.network]})`,
21446
+ " - Your deposit is the session budget. It is locked in escrow in the channel contract, not",
21447
+ " paid to anyone; each turn signs an off-chain voucher for what the turn actually used.",
21448
+ " - The deposit is gasless: it travels with your first paid turn and the facilitator pays its",
21449
+ " gas. Setup itself deposits nothing and buys nothing.",
21450
+ ` - Unused funds are refundable: a cooperative refund returns them at once, or a withdrawal`,
21451
+ ` returns them after the channel's withdraw delay of ${duration(profile.withdrawDelaySeconds)}.`,
21452
+ " - When the deposit runs low it is topped up to the deposit amount, never past the total.",
21453
+ ` - Channels: ${routes.length} (${routes.join(", ")}); each has its own deposit, and all of`,
21454
+ " them together spend at most the total.",
21455
+ ` Deposit (session budget): ${usd(limits.deposit)} a channel`,
21456
+ ` Per-request limit: ${usd(limits.perRequest)}`,
21457
+ ` Total limit: ${usd(limits.total)} for all channels together, for the life of the budget${usedSoFar > 0n ? `, including ${usd(usedSoFar)} already used (history is kept)` : ""}`,
21458
+ ...routes.length > 1 ? [
21459
+ ` Deposits a day: at most ${routes.map((route) => `${usd(windows.get(route))} into ${slug(route)}`).join(", ")}, the total split between the channels`
21460
+ ] : [` Deposits a day: at most ${usd(limits.total)}`],
21461
+ ` Expires: ${limits.expiresAt}`,
21462
+ ...blocked ? [
21463
+ ` Note: today's deposit limit is used, so the first turn${blocked.resetsAt ? ` cannot deposit until ${utc(blocked.resetsAt)}` : " cannot deposit yet"} (a total of at least ${usd(blocked.minimumTotal)} would fit today).`
21464
+ ] : []
21465
+ ];
21466
+ }
21467
+ var utc = (iso) => iso.replace("T", " ").replace(/Z$/, " UTC");
21468
+ function depositWindowLines(check, limits) {
21469
+ return [
21470
+ "",
21471
+ "Today's deposit limit is used",
21472
+ ...check.blocks.map(
21473
+ (block) => ` - ${slug(block.route)}: ${usd(block.used)} of its ${usd(block.window)} daily deposit limit is already used, so the new channel's first ${usd(block.deposit)} deposit does not fit.`
21474
+ ),
21475
+ " Deposits count against the limit for 24 hours, even once refunded, so closing a channel",
21476
+ " does not free it.",
21477
+ check.resetsAt ? ` The limit resets at ${utc(check.resetsAt)}. Until then the first turn is refused (policy_denied).` : " A deposit or purchase whose outcome is unknown counts in full until it is reconciled",
21478
+ ...check.resetsAt ? [] : [
21479
+ " (see taskmarket agent status), so waiting alone does not free the limit. Until then the",
21480
+ " first turn is refused (policy_denied)."
21481
+ ],
21482
+ " Either:",
21483
+ ` - raise the total to at least ${usd(check.minimumTotal)} (from ${usd(limits.total)}): the daily limit is a share of the total; or`,
21484
+ check.resetsAt ? ` - keep ${usd(limits.total)} and start the first turn after ${utc(check.resetsAt)}.` : ` - keep ${usd(limits.total)} and reconcile the unknown outcome first.`
21485
+ ];
21486
+ }
21487
+ var WORKSPACE_LINES = [
21488
+ "",
21489
+ "Remote workspace",
21490
+ " - Every tool the agent uses (read, write, edit, bash, grep, find, ls and your ! commands)",
21491
+ " runs in a Boat cloud VM in the EU, never on this machine. The VM holds no wallet key or",
21492
+ " credential, and it has open internet egress.",
21493
+ " - Uploaded: your project's git-tracked files and untracked files you select, respecting",
21494
+ " .gitignore. Secret-like files (.env*, private keys, credentials) are excluded unless you",
21495
+ " include them yourself. Changes come back only as a diff you review. Until workspace sync",
21496
+ " ships, the sandbox starts empty and nothing is uploaded.",
21497
+ " - Retention: Boat snapshots the workspace every minute while the VM runs and keeps the",
21498
+ " snapshots for the life of the sandbox; deleting the sandbox removes the snapshots only it",
21499
+ " uses. Boat keeps machine-assignment, IP and billing records."
21500
+ ];
21501
+ function computeRateLines(pricing, availableCredits) {
21502
+ return [
21503
+ "",
21504
+ "Compute credit (pays for the sandbox VM, separately from inference)",
21505
+ ...pricing.machineTypes.map(
21506
+ (machine) => ` ${machine.machineType} sandbox: $${machine.usdPerHour} an hour while it runs (${machine.creditsPerHour} credits), nothing while stopped`
21507
+ ),
21508
+ ` The rate is the provider's cost times ${pricing.markup}; $${pricing.usdPerCredit} a credit (rate card ${pricing.rateCardVersion}${pricing.salesApproval === "approved" ? "" : ", not yet approved for live sales"})`,
21509
+ ` Available: ${availableCredits} credits`
21510
+ ];
21511
+ }
21512
+ function slug(route) {
21513
+ return route.split("/")[2];
21514
+ }
21515
+ function shortId() {
21516
+ return randomUUID2().replace(/-/g, "").slice(0, 12);
21517
+ }
21518
+ async function preflight(deps, profile, wallet, prompter) {
21519
+ prompter.say(["", "Checking the inference terms, the chain and compute (read-only)..."]);
21520
+ const step = async (name, read) => {
21521
+ try {
21522
+ return await read();
21523
+ } catch (error) {
21524
+ if (error instanceof AgentLaunchError) throw error;
21459
21525
  throw new AgentLaunchError(
21460
- "setup_required",
21461
- `taskmarket agent has no approved spending policy at ${policyPath}`
21526
+ "preflight_failed",
21527
+ `Setup could not read ${name}: ${error.message}. Nothing was written.`,
21528
+ { check: name }
21462
21529
  );
21463
21530
  }
21531
+ };
21532
+ const x402Policy = await step(
21533
+ "the x402 policy",
21534
+ () => loadX402Policy(getX402PolicyPath(), { allowMissing: true })
21535
+ );
21536
+ const rpcUrlEnv = x402Policy.networks[profile.network]?.rpcUrlEnv ?? profile.rpcUrlEnv;
21537
+ const rpcUrl = deps.env[rpcUrlEnv]?.trim();
21538
+ if (!rpcUrl) {
21539
+ throw new AgentLaunchError(
21540
+ "preflight_failed",
21541
+ `Set ${rpcUrlEnv} to a ${NETWORK_NAMES[profile.network]} RPC URL; the channel's deposits and refunds are checked on chain. Nothing was written.`,
21542
+ { check: "rpc" }
21543
+ );
21544
+ }
21545
+ const models = await step("the xgate model catalog", () => deps.readCatalog(profile.origin));
21546
+ if (models.length === 0) {
21547
+ throw new AgentLaunchError(
21548
+ "no_qualified_models",
21549
+ `xgate at ${profile.origin} offers no model a channel can pay for`
21550
+ );
21551
+ }
21552
+ const chain = await step("the chain", () => deps.readChain(profile, rpcUrl, wallet));
21553
+ const pricing = await step("the compute pricing", () => deps.computePricing());
21554
+ const availableCredits = await step("the compute credit balance", () => deps.computeBalance());
21555
+ return { x402Policy, rpcUrlEnv, rpcUrl, models, chain, pricing, availableCredits };
21556
+ }
21557
+ function totalRules(input) {
21558
+ const { deposit, perRequest, previousTotal, usedSoFar, channels } = input;
21559
+ const sharedFloor = BigInt(deposit) * BigInt(channels);
21560
+ const floor = [sharedFloor, previousTotal, usedSoFar + BigInt(perRequest)].reduce(
21561
+ (a, b) => a > b ? a : b
21562
+ );
21563
+ const problem = (value) => {
21564
+ if (value < BigInt(deposit)) return "The total must be at least the deposit";
21565
+ if (value < sharedFloor) {
21566
+ return `The ${channels} channels share the total, and each needs room for one deposit a day, so it must be at least ${usd(sharedFloor)}`;
21567
+ }
21568
+ if (value < previousTotal) return `The total can only go up from ${usd(previousTotal)}`;
21569
+ if (value < usedSoFar + BigInt(perRequest)) {
21570
+ return `The total must leave room for one request after the ${usd(usedSoFar)} already used`;
21571
+ }
21572
+ return void 0;
21573
+ };
21574
+ return { floor, problem };
21575
+ }
21576
+ async function askLimits(prompter, previous, usedSoFar, exhausted, now, channels) {
21577
+ prompter.say([
21578
+ "",
21579
+ previous ? "Renew the inference limits (amounts in USD; Enter keeps the value shown)" : "Inference limits (amounts in USD; Enter keeps the value shown)"
21580
+ ]);
21581
+ const deposit = await askUsd(
21582
+ prompter,
21583
+ "Deposit, the session budget locked in escrow",
21584
+ previous?.limits.deposit ?? usdToAtomic(ONBOARDING_DEFAULTS.depositUsd)
21585
+ );
21586
+ const perRequestFallback = previous?.limits.perRequest ?? usdToAtomic(ONBOARDING_DEFAULTS.perRequestUsd);
21587
+ const perRequest = await askUsd(
21588
+ prompter,
21589
+ "Per-request limit, the most one turn may cost",
21590
+ BigInt(perRequestFallback) > BigInt(deposit) ? deposit : perRequestFallback,
21591
+ (value) => value > BigInt(deposit) ? "The per-request limit must fit in the deposit" : void 0
21592
+ );
21593
+ const previousTotal = previous ? BigInt(previous.limits.total) : 0n;
21594
+ const { floor, problem } = totalRules({
21595
+ deposit,
21596
+ perRequest,
21597
+ previousTotal,
21598
+ usedSoFar,
21599
+ channels
21600
+ });
21601
+ let totalFallback = previous ? previousTotal + (exhausted ? BigInt(deposit) : 0n) : BigInt(deposit) * ONBOARDING_DEFAULTS.totalDeposits;
21602
+ if (totalFallback < floor) totalFallback = floor;
21603
+ const total = await askUsd(
21604
+ prompter,
21605
+ previous ? `Total limit, the most this budget may ever spend (${usd(usedSoFar)} used so far)` : "Total limit, the most this budget may ever spend",
21606
+ String(totalFallback),
21607
+ problem
21608
+ );
21609
+ const days = await askWhole(
21610
+ prompter,
21611
+ "Expires after how many days",
21612
+ ONBOARDING_DEFAULTS.expiryDays,
21613
+ 1,
21614
+ MAX_EXPIRY_DAYS
21615
+ );
21616
+ return {
21617
+ perRequest,
21618
+ deposit,
21619
+ total,
21620
+ expiresAt: new Date(now.getTime() + days * DAY_MS).toISOString()
21621
+ };
21622
+ }
21623
+ function sameTerms(rail, profile, wallet) {
21624
+ const same = (a, b) => a.toLowerCase() === b.toLowerCase();
21625
+ return rail.origin === profile.origin && rail.network === profile.network && same(rail.asset, profile.asset) && same(rail.receiver, profile.receiver) && same(rail.receiverAuthorizer, profile.receiverAuthorizer) && same(rail.wallet, wallet);
21626
+ }
21627
+ function channelSessionKey(rail, rpcUrl, journalPath) {
21628
+ const channel = {
21629
+ network: rail.network,
21630
+ payer: rail.wallet,
21631
+ receiver: rail.receiver,
21632
+ receiverAuthorizer: rail.receiverAuthorizer,
21633
+ salt: rail.salt
21634
+ };
21635
+ return createBaseChannelPayer({
21636
+ channel,
21637
+ reader: createBaseChannelRpcReader({ channel, rpcUrl, fromBlock: BigInt(rail.fromBlock) }),
21638
+ resourceUrl: `${rail.origin}${rail.route}`,
21639
+ journalPath,
21640
+ loadPolicy: (options) => loadX402Policy(getX402PolicyPath(), options),
21641
+ getAccount: async () => {
21642
+ throw new Error("onboarding never signs");
21643
+ },
21644
+ allocationId: rail.allocationId,
21645
+ budgetAmount: rail.limits.total,
21646
+ depositAmount: rail.limits.deposit,
21647
+ policyRuleId: rail.policyRuleId
21648
+ }).sessionKey;
21649
+ }
21650
+ function ruleFor(rail, profile, depositWindow) {
21651
+ return {
21652
+ id: rail.policyRuleId,
21653
+ enabled: true,
21654
+ priority: 100,
21655
+ origin: rail.origin,
21656
+ pathPrefix: rail.route,
21657
+ methods: ["POST"],
21658
+ unattended: true,
21659
+ allowPrivateNetwork: profile.allowPrivateNetwork,
21660
+ maxAuthorizationSeconds: 300,
21661
+ payments: [
21662
+ {
21663
+ scheme: rail.scheme,
21664
+ network: rail.network,
21665
+ asset: rail.asset,
21666
+ payTo: rail.receiver,
21667
+ displaySymbol: profile.assetSymbol,
21668
+ displayDecimals: profile.assetDecimals,
21669
+ maxPerPayment: rail.limits.perRequest,
21670
+ // Each deposit and top-up counts against this window once (ADR-0236). The rails' windows
21671
+ // split the total, so together they admit at most the total in a day.
21672
+ spendWindow: { seconds: 86400, max: depositWindow }
21673
+ }
21674
+ ]
21675
+ };
21676
+ }
21677
+ async function openBudgetCap(store, id) {
21678
+ try {
21679
+ const budget = await store.get(id);
21680
+ return budget.allocation.closedAt ? void 0 : budget.allocation.caps.amount;
21681
+ } catch (error) {
21682
+ if (error.code === "allocation_not_found") return void 0;
21464
21683
  throw error;
21465
21684
  }
21466
- let parsed;
21685
+ }
21686
+ async function openBudget(store, rail, note) {
21687
+ const cap = await openBudgetCap(store, rail.budgetAllocationId);
21688
+ if (cap !== void 0 && BigInt(cap) < BigInt(rail.limits.total)) {
21689
+ await store.raiseCap({
21690
+ allocationId: rail.budgetAllocationId,
21691
+ caps: { amount: rail.limits.total },
21692
+ note
21693
+ });
21694
+ }
21695
+ if (cap === void 0) {
21696
+ await store.open(
21697
+ {
21698
+ id: rail.budgetAllocationId,
21699
+ kind: "agent_session",
21700
+ exposure: "inference",
21701
+ network: rail.network,
21702
+ asset: rail.asset,
21703
+ caps: { amount: rail.limits.total }
21704
+ },
21705
+ { note }
21706
+ );
21707
+ }
21708
+ }
21709
+ async function openSession(store, rail, sessionKey) {
21710
+ await store.open({
21711
+ id: rail.allocationId,
21712
+ kind: "agent_session",
21713
+ exposure: "inference",
21714
+ parentId: rail.budgetAllocationId,
21715
+ network: rail.network,
21716
+ asset: rail.asset,
21717
+ caps: { amount: rail.limits.total },
21718
+ funding: { railId: BASE_CHANNEL_RAIL_ID, sessionKey }
21719
+ });
21720
+ }
21721
+ async function computeStep(deps, prompter, pricing, availableCredits, renewing) {
21722
+ if (renewing && Number(availableCredits) > 0) {
21723
+ return { action: "not_offered", availableCredits };
21724
+ }
21725
+ prompter.say(computeRateLines(pricing, availableCredits));
21726
+ const sizes = pricing.denominations.map((entry) => entry.amountUsd);
21727
+ const fallback = (pricing.denominations.find((entry) => entry.default) ?? pricing.denominations[0]).amountUsd;
21728
+ let amountUsd = -1;
21729
+ for (let attempt = 0; attempt < MAX_ATTEMPTS && amountUsd < 0; attempt += 1) {
21730
+ const answer = (await prompter.ask(
21731
+ `Buy a first chunk of compute credit in USD: ${sizes.join(", ")}, or 0 for none`,
21732
+ String(fallback)
21733
+ )).replace(/^\$/, "");
21734
+ if (answer === "0" || sizes.map(String).includes(answer)) amountUsd = Number(answer);
21735
+ else prompter.say([` Choose one of ${sizes.join(", ")}, or 0`]);
21736
+ }
21737
+ if (amountUsd < 0) throw tooManyAnswers();
21738
+ let config = await deps.computeConfig();
21739
+ let setup;
21740
+ if (!config) {
21741
+ const autoTopUp = await askYesNo(
21742
+ prompter,
21743
+ "Turn on auto-top-up, buying one chunk when credit runs low",
21744
+ false
21745
+ );
21746
+ if (amountUsd === 0 && !autoTopUp) return { action: "skipped" };
21747
+ const chunk = amountUsd || fallback;
21748
+ const minimumDay = usdToAtomic(String(chunk));
21749
+ const perDay = await askUsd(
21750
+ prompter,
21751
+ "Compute cap: most bought in one UTC day",
21752
+ BigInt(usdToAtomic(ONBOARDING_DEFAULTS.computePerDayUsd)) < BigInt(minimumDay) ? minimumDay : usdToAtomic(ONBOARDING_DEFAULTS.computePerDayUsd),
21753
+ (value) => value < BigInt(minimumDay) ? `The daily cap must cover a $${chunk} chunk` : void 0
21754
+ );
21755
+ const perMonth = await askUsd(
21756
+ prompter,
21757
+ "Compute cap: most bought in one UTC month",
21758
+ BigInt(usdToAtomic(ONBOARDING_DEFAULTS.computePerMonthUsd)) < BigInt(perDay) ? perDay : usdToAtomic(ONBOARDING_DEFAULTS.computePerMonthUsd),
21759
+ (value) => value < BigInt(perDay) ? "The monthly cap must be at least the daily cap" : void 0
21760
+ );
21761
+ setup = { perDayUsd: atomicToUsd(perDay), perMonthUsd: atomicToUsd(perMonth) };
21762
+ if (autoTopUp) {
21763
+ const chunkCredits = pricing.denominations.find((entry) => entry.amountUsd === chunk)?.credits ?? "1";
21764
+ const threshold = await prompter.ask(
21765
+ "Auto-top-up: buy when available credit falls below (credits)",
21766
+ String(Math.max(1, Math.floor(Number(chunkCredits) / 4)))
21767
+ );
21768
+ const maxChunks = await askWhole(
21769
+ prompter,
21770
+ `Auto-top-up: most $${chunk} chunks in one UTC day`,
21771
+ ONBOARDING_DEFAULTS.autoTopUpChunksPerDay,
21772
+ 1,
21773
+ Math.max(1, Math.floor(Number(atomicToUsd(perDay)) / chunk))
21774
+ );
21775
+ setup.autoTopUp = {
21776
+ thresholdCredits: threshold,
21777
+ amountUsd: chunk,
21778
+ maxChunksPerDay: maxChunks
21779
+ };
21780
+ }
21781
+ } else if (amountUsd === 0) {
21782
+ return { action: "skipped" };
21783
+ }
21784
+ const credits = pricing.denominations.find((entry) => entry.amountUsd === amountUsd)?.credits;
21785
+ const approved = await prompter.confirm(
21786
+ [
21787
+ "",
21788
+ ...setup ? [
21789
+ `Approve a compute allocation capped at $${setup.perDayUsd} a day and $${setup.perMonthUsd} a month${setup.autoTopUp ? `, with auto-top-up of $${setup.autoTopUp.amountUsd} below ${setup.autoTopUp.thresholdCredits} credits, at most ${setup.autoTopUp.maxChunksPerDay} a day` : ""}`
21790
+ ] : [],
21791
+ ...amountUsd > 0 ? [
21792
+ `Buy $${amountUsd} of compute credit (${credits} credits) now, from your wallet over x402`
21793
+ ] : [],
21794
+ ""
21795
+ ].join("\n")
21796
+ );
21797
+ if (!approved) {
21798
+ prompter.say(["No compute credit was approved or bought."]);
21799
+ return { action: "declined" };
21800
+ }
21467
21801
  try {
21468
- parsed = JSON.parse(raw);
21469
- } catch {
21470
- throw new AgentLaunchError("invalid_agent_policy", `${policyPath} is not valid JSON`);
21802
+ if (setup) {
21803
+ await deps.computeSetUp(setup, pricing);
21804
+ config = await deps.computeConfig();
21805
+ }
21806
+ if (amountUsd === 0) return { action: "skipped" };
21807
+ const bought = await deps.computeBuy(amountUsd);
21808
+ prompter.say([`Bought $${amountUsd} of compute credit (${bought.credits} credits).`]);
21809
+ return { action: "bought", amountUsd, credits: bought.credits };
21810
+ } catch (error) {
21811
+ const code = String(error.code ?? "unknown");
21812
+ const message = error.message;
21813
+ prompter.say([
21814
+ `The compute credit purchase did not complete (${code}): ${message}`,
21815
+ "Your inference policy is saved. Buy credit later with taskmarket agent credits buy."
21816
+ ]);
21817
+ return { action: "failed", code, message };
21471
21818
  }
21472
- const result = AgentPolicySchema.safeParse(parsed);
21473
- if (!result.success) {
21474
- const issue = result.error.issues[0];
21819
+ }
21820
+ async function runOnboarding(input) {
21821
+ const deps = { ...defaultDeps(), ...input.deps };
21822
+ const { prompter, walletAddress: wallet } = input;
21823
+ const previousPolicy = input.renew?.policy;
21824
+ const reasons = input.renew?.assessment.reasons ?? [];
21825
+ prompter.say(
21826
+ previousPolicy ? [
21827
+ "taskmarket agent: your spending policy needs renewal",
21828
+ ...reasons.map(
21829
+ ({ reason, railId }) => ` - ${railId ? `${railId}: ` : ""}${{
21830
+ expired: "the policy expired",
21831
+ exhausted: "the budget has less left than one request may cost",
21832
+ allocation_unavailable: "its budget allocation is closed or missing",
21833
+ consent_outdated: "the remote workspace terms changed"
21834
+ }[reason]}`
21835
+ ),
21836
+ " Renewing keeps the spending history: nothing already used is forgotten."
21837
+ ] : [
21838
+ "taskmarket agent: first-run setup",
21839
+ "Nothing is spent until you approve it. The agent pays for inference from a channel you",
21840
+ "fund, and for its cloud sandbox from prepaid compute credit."
21841
+ ]
21842
+ );
21843
+ const profile = await deps.loadProfile();
21844
+ const checks = await preflight(deps, profile, wallet, prompter);
21845
+ const models = checks.models.filter((model) => XGATE_ROUTES.includes(model.route));
21846
+ const routes = XGATE_ROUTES.filter((route) => models.some((model) => model.route === route));
21847
+ if (routes.length === 0) {
21475
21848
  throw new AgentLaunchError(
21476
- "invalid_agent_policy",
21477
- `${policyPath}: ${issue.path.join(".") || "(root)"}: ${issue.message}`
21849
+ "no_qualified_models",
21850
+ `xgate at ${profile.origin} offers no model a channel can pay for`
21478
21851
  );
21479
21852
  }
21480
- return result.data;
21853
+ prompter.say(termsLines(profile, wallet, models, checks.chain));
21854
+ const now = deps.now();
21855
+ const previousRails = new Map(
21856
+ (previousPolicy?.rails ?? []).filter((rail) => sameTerms(rail, profile, wallet)).map((rail) => [rail.route, rail])
21857
+ );
21858
+ const used = new Map(
21859
+ (input.renew?.assessment.usage ?? []).map((u) => [u.railId, BigInt(u.used)])
21860
+ );
21861
+ const usedSoFar = [...previousRails.values()].reduce((max, rail) => {
21862
+ const value = used.get(rail.id) ?? 0n;
21863
+ return value > max ? value : max;
21864
+ }, 0n);
21865
+ const exhausted = reasons.some((r) => r.reason === "exhausted");
21866
+ const previousRail = previousRails.values().next().value;
21867
+ let limits = await askLimits(prompter, previousRail, usedSoFar, exhausted, now, routes.length);
21868
+ const journalPath = getX402JournalPath();
21869
+ const store = createX402Allocations({
21870
+ journalPath,
21871
+ loadPolicy: (options) => loadX402Policy(getX402PolicyPath(), options)
21872
+ });
21873
+ let budgetAllocationId;
21874
+ for (const rail of previousRails.values()) {
21875
+ if (await openBudgetCap(store, rail.budgetAllocationId) !== void 0) {
21876
+ budgetAllocationId = rail.budgetAllocationId;
21877
+ break;
21878
+ }
21879
+ }
21880
+ budgetAllocationId ??= `agent-inference-${shortId()}`;
21881
+ const openSessions = /* @__PURE__ */ new Set();
21882
+ for (const rail of previousRails.values()) {
21883
+ if (rail.budgetAllocationId === budgetAllocationId && await openBudgetCap(store, rail.allocationId) !== void 0) {
21884
+ openSessions.add(rail.allocationId);
21885
+ }
21886
+ }
21887
+ const keepsChannel = (previous, next) => previous !== void 0 && openSessions.has(previous.allocationId) && previous.limits.deposit === next.deposit && previous.limits.total === next.total;
21888
+ const { floor: totalFloor, problem: totalProblem } = totalRules({
21889
+ deposit: limits.deposit,
21890
+ perRequest: limits.perRequest,
21891
+ previousTotal: previousRail ? BigInt(previousRail.limits.total) : 0n,
21892
+ usedSoFar,
21893
+ channels: routes.length
21894
+ });
21895
+ const windowCheck = (next) => checkDepositWindows({
21896
+ journalPath,
21897
+ now,
21898
+ routes,
21899
+ rails: routes.map((route) => {
21900
+ const previous = previousRails.get(route);
21901
+ const id = previous?.id ?? `xgate-${slug(route)}`;
21902
+ return {
21903
+ railId: id,
21904
+ route,
21905
+ ruleId: previous?.policyRuleId ?? `taskmarket-agent-${id}`,
21906
+ network: profile.network,
21907
+ asset: profile.asset,
21908
+ newChannel: !keepsChannel(previous, next)
21909
+ };
21910
+ }),
21911
+ limits: next,
21912
+ floor: totalFloor
21913
+ });
21914
+ let blocked = await windowCheck(limits);
21915
+ for (let attempt = 0; blocked && attempt < MAX_ATTEMPTS; attempt += 1) {
21916
+ prompter.say(depositWindowLines(blocked, limits));
21917
+ const total = await askUsd(
21918
+ prompter,
21919
+ `Total limit (Enter keeps ${usd(limits.total)}; at least ${usd(blocked.minimumTotal)} fits today)`,
21920
+ limits.total,
21921
+ totalProblem
21922
+ );
21923
+ if (total === limits.total) break;
21924
+ limits = { ...limits, total };
21925
+ blocked = await windowCheck(limits);
21926
+ }
21927
+ prompter.say(channelLines(profile, routes, limits, usedSoFar, blocked));
21928
+ if (!await prompter.confirm("Open the inference channel with these limits?")) {
21929
+ throw new AgentLaunchError(
21930
+ "setup_declined",
21931
+ "The inference channel was not approved. Nothing was written and nothing was bought.",
21932
+ { step: "channel" }
21933
+ );
21934
+ }
21935
+ const consentCurrent = previousPolicy?.workspace.consentVersion === WORKSPACE_CONSENT_VERSION;
21936
+ if (!consentCurrent) {
21937
+ prompter.say(WORKSPACE_LINES);
21938
+ if (!await prompter.confirm("Use the remote workspace on these terms?")) {
21939
+ throw new AgentLaunchError(
21940
+ "setup_declined",
21941
+ "The remote workspace terms were not accepted, and the agent runs its tools only there. Nothing was written and nothing was bought.",
21942
+ { step: "workspace" }
21943
+ );
21944
+ }
21945
+ }
21946
+ const rails = routes.map((route) => {
21947
+ const previous = previousRails.get(route);
21948
+ if (previous && keepsChannel(previous, limits)) {
21949
+ return { ...previous, withdrawDelaySeconds: profile.withdrawDelaySeconds, limits };
21950
+ }
21951
+ const id = previous?.id ?? `xgate-${slug(route)}`;
21952
+ return {
21953
+ id,
21954
+ kind: "base-batch-channel",
21955
+ wallet,
21956
+ network: profile.network,
21957
+ scheme: profile.scheme,
21958
+ asset: profile.asset,
21959
+ origin: profile.origin,
21960
+ route,
21961
+ receiver: profile.receiver,
21962
+ receiverAuthorizer: profile.receiverAuthorizer,
21963
+ salt: deps.randomSalt(),
21964
+ fromBlock: checks.chain.blockNumber.toString(),
21965
+ withdrawDelaySeconds: profile.withdrawDelaySeconds,
21966
+ budgetAllocationId,
21967
+ allocationId: `agent-inference-${slug(route)}-${shortId()}`,
21968
+ policyRuleId: previous?.policyRuleId ?? `taskmarket-agent-${id}`,
21969
+ limits
21970
+ };
21971
+ });
21972
+ const retired = (previousPolicy?.rails ?? []).filter(
21973
+ (rail) => !rails.some((next) => next.salt === rail.salt)
21974
+ );
21975
+ const settled = await settledRetiredRails(store, previousPolicy?.retiredRails ?? []);
21976
+ const retiredRails = [
21977
+ ...retired,
21978
+ ...(previousPolicy?.retiredRails ?? []).filter((rail) => !settled.has(rail.salt))
21979
+ ];
21980
+ if (retiredRails.length > MAX_RETIRED_RAILS) {
21981
+ throw new AgentLaunchError(
21982
+ "retired_rails_full",
21983
+ `The policy already lists ${retiredRails.length - retired.length} retired channels that may still hold refundable deposits, and this change would retire ${retired.length} more, past the limit of ${MAX_RETIRED_RAILS}. Close and refund old channels first; a retired channel is never forgotten while it may hold funds. Nothing was written.`,
21984
+ { retired: retiredRails.length, limit: MAX_RETIRED_RAILS }
21985
+ );
21986
+ }
21987
+ const windows = depositWindows(limits.total, routes);
21988
+ const rules = rails.map((rail) => ruleFor(rail, profile, windows.get(rail.route)));
21989
+ const retiredRules = new Set(
21990
+ retired.map((rail) => rail.policyRuleId).filter((id) => !rules.some((rule) => rule.id === id))
21991
+ );
21992
+ const x402Policy = parseX402Policy({
21993
+ ...checks.x402Policy,
21994
+ networks: {
21995
+ ...checks.x402Policy.networks,
21996
+ [profile.network]: checks.x402Policy.networks[profile.network] ?? {
21997
+ rpcUrlEnv: checks.rpcUrlEnv
21998
+ }
21999
+ },
22000
+ rules: [
22001
+ ...checks.x402Policy.rules.filter((rule) => !rules.some((next) => next.id === rule.id)).map((rule) => retiredRules.has(rule.id) ? { ...rule, enabled: false } : rule),
22002
+ ...rules
22003
+ ]
22004
+ });
22005
+ await saveX402Policy(x402Policy, getX402PolicyPath());
22006
+ const newRails = rails.filter((rail) => previousRails.get(rail.route)?.salt !== rail.salt);
22007
+ if (newRails.length > 0) {
22008
+ const note = `Approved at the terminal by the device owner during taskmarket agent ${previousPolicy ? "renewal" : "setup"}: rails ${rails.map((rail) => rail.id).join(", ")} on ${profile.network}, deposit ${usd(
22009
+ limits.deposit
22010
+ )} a channel, per request ${usd(limits.perRequest)}, total ${usd(
22011
+ limits.total
22012
+ )} for all channels together, expires ${limits.expiresAt}`;
22013
+ await openBudget(store, newRails[0], note);
22014
+ for (const rail of newRails) {
22015
+ await openSession(store, rail, channelSessionKey(rail, checks.rpcUrl, journalPath));
22016
+ }
22017
+ }
22018
+ const policy = {
22019
+ version: 1,
22020
+ approvedAt: now.toISOString(),
22021
+ models: models.map((model) => model.id),
22022
+ rails,
22023
+ retiredRails,
22024
+ workspace: consentCurrent ? previousPolicy.workspace : { consentVersion: WORKSPACE_CONSENT_VERSION, approvedAt: now.toISOString() }
22025
+ };
22026
+ await saveAgentPolicy(policy);
22027
+ for (const rail of retired) {
22028
+ await store.close(rail.allocationId).catch(() => void 0);
22029
+ }
22030
+ const replaced = retired.filter((rail) => routes.includes(rail.route));
22031
+ const unused = retired.filter((rail) => !routes.includes(rail.route));
22032
+ if (replaced.length > 0) {
22033
+ prompter.say([
22034
+ "",
22035
+ `A new channel replaces ${replaced.map((rail) => rail.id).join(", ")}: a channel's deposit and total are fixed for its life.`,
22036
+ "The replaced channel buys nothing more. Its unused deposit stays in escrow and refundable,",
22037
+ "and it stays listed in the policy so it can be refunded."
22038
+ ]);
22039
+ }
22040
+ if (unused.length > 0) {
22041
+ prompter.say([
22042
+ "",
22043
+ `No approved model uses ${unused.map((rail) => `${rail.id} (${rail.route})`).join(", ")} now, so it buys nothing more.`,
22044
+ "Its unused deposit stays in escrow and refundable, and it stays listed in the policy so it",
22045
+ "can be refunded."
22046
+ ]);
22047
+ }
22048
+ prompter.say([
22049
+ "",
22050
+ "Spending policy saved. Later launches reuse it until it expires or runs out."
22051
+ ]);
22052
+ const compute = await computeStep(
22053
+ deps,
22054
+ prompter,
22055
+ checks.pricing,
22056
+ checks.availableCredits,
22057
+ previousPolicy !== void 0
22058
+ );
22059
+ return { policy, compute };
22060
+ }
22061
+
22062
+ // src/lib/agent/prompter.ts
22063
+ import { createInterface } from "readline";
22064
+ function cancelled() {
22065
+ return new AgentLaunchError(
22066
+ "setup_cancelled",
22067
+ "Setup was cancelled; nothing more was written or bought"
22068
+ );
21481
22069
  }
21482
- function selectModels(policy, requested) {
21483
- if (requested === void 0) return [...policy.models];
21484
- if (!policy.models.includes(requested)) {
22070
+ function terminalPrompter(input = process.stdin, output = process.stderr) {
22071
+ let readline;
22072
+ let closed = false;
22073
+ let reject;
22074
+ const open = () => {
22075
+ if (readline) return readline;
22076
+ readline = createInterface({ input, output, terminal: true });
22077
+ readline.on("SIGINT", () => {
22078
+ output.write("\n");
22079
+ reject?.(cancelled());
22080
+ });
22081
+ readline.on("close", () => {
22082
+ closed = true;
22083
+ reject?.(cancelled());
22084
+ });
22085
+ return readline;
22086
+ };
22087
+ const question = (text) => new Promise((resolve, fail) => {
22088
+ if (closed) {
22089
+ fail(cancelled());
22090
+ return;
22091
+ }
22092
+ reject = fail;
22093
+ open().question(text, (answer) => {
22094
+ reject = void 0;
22095
+ resolve(answer);
22096
+ });
22097
+ });
22098
+ return {
22099
+ say(lines3) {
22100
+ output.write(`${lines3.join("\n")}
22101
+ `);
22102
+ },
22103
+ async ask(text, fallback) {
22104
+ const answer = (await question(`${text} [${fallback}]: `)).trim();
22105
+ return answer === "" ? fallback : answer;
22106
+ },
22107
+ async confirm(text) {
22108
+ const answer = await question(`${text} Type yes to approve: `);
22109
+ return answer.trim().toLowerCase() === "yes";
22110
+ },
22111
+ close() {
22112
+ reject = void 0;
22113
+ readline?.close();
22114
+ readline = void 0;
22115
+ }
22116
+ };
22117
+ }
22118
+
22119
+ // src/lib/agent/setup.ts
22120
+ async function ensureAgentPolicy(options) {
22121
+ const policy = await readAgentPolicy();
22122
+ let assessment;
22123
+ if (policy) {
22124
+ const allocations = createX402Allocations({ journalPath: getX402JournalPath() });
22125
+ assessment = await assessAgentPolicy(policy, {
22126
+ now: options.now,
22127
+ allocation: (id) => allocations.get(id)
22128
+ });
22129
+ if (assessment.reasons.length === 0) return void 0;
22130
+ }
22131
+ if (!options.interactive) {
22132
+ const reasons = assessment ? [...new Set(assessment.reasons.map(({ reason }) => reason))] : ["no_policy"];
22133
+ const window2 = policy && assessment ? await renewalDepositWindow(policy, assessment, options.now ?? /* @__PURE__ */ new Date()) : void 0;
21485
22134
  throw new AgentLaunchError(
21486
- "model_not_allowed",
21487
- `Model ${requested} is not in the approved spending policy (${policy.models.join(", ")})`
22135
+ "setup_required",
22136
+ policy ? `The agent spending policy at ${getAgentPolicyPath()} needs renewal (${reasons.join(", ")}); run taskmarket agent at a terminal to renew it. Its spending history is kept.${window2 ? ` ${depositWindowMessage(window2, policy)}` : ""}` : `taskmarket agent has no approved spending policy at ${getAgentPolicyPath()}; run taskmarket agent at a terminal to set it up`,
22137
+ {
22138
+ reasons,
22139
+ ...window2 ? {
22140
+ depositWindow: {
22141
+ reason: "deposit_window_used",
22142
+ total: policy.rails[0].limits.total,
22143
+ minimumTotal: window2.minimumTotal,
22144
+ ...window2.resetsAt ? { resetsAt: window2.resetsAt } : {},
22145
+ rails: window2.blocks
22146
+ }
22147
+ } : {}
22148
+ }
21488
22149
  );
21489
22150
  }
21490
- return [requested];
22151
+ const prompter = (options.prompter ?? terminalPrompter)();
22152
+ try {
22153
+ return await (options.onboard ?? runOnboarding)({
22154
+ prompter,
22155
+ walletAddress: options.walletAddress,
22156
+ ...policy && assessment ? { renew: { policy, assessment } } : {}
22157
+ });
22158
+ } finally {
22159
+ prompter.close();
22160
+ }
22161
+ }
22162
+ async function renewalDepositWindow(policy, assessment, now) {
22163
+ const limits = policy.rails[0].limits;
22164
+ const reopened = new Set(
22165
+ assessment.reasons.filter(({ reason }) => reason === "allocation_unavailable").map(({ railId }) => railId)
22166
+ );
22167
+ const used = assessment.usage.reduce(
22168
+ (max, usage) => BigInt(usage.used) > max ? BigInt(usage.used) : max,
22169
+ 0n
22170
+ );
22171
+ const floors = [
22172
+ BigInt(limits.total),
22173
+ BigInt(limits.deposit) * BigInt(policy.rails.length),
22174
+ used + BigInt(limits.perRequest)
22175
+ ];
22176
+ try {
22177
+ return await checkDepositWindows({
22178
+ journalPath: getX402JournalPath(),
22179
+ now,
22180
+ routes: policy.rails.map((rail) => rail.route),
22181
+ rails: policy.rails.map((rail) => ({
22182
+ railId: rail.id,
22183
+ route: rail.route,
22184
+ ruleId: rail.policyRuleId,
22185
+ network: rail.network,
22186
+ asset: rail.asset,
22187
+ newChannel: reopened.has(rail.id)
22188
+ })),
22189
+ limits,
22190
+ floor: floors.reduce((a, b) => a > b ? a : b)
22191
+ });
22192
+ } catch {
22193
+ return void 0;
22194
+ }
22195
+ }
22196
+ function depositWindowMessage(window2, policy) {
22197
+ const usd2 = formatUsdc;
22198
+ const total = policy.rails[0].limits.total;
22199
+ const railIds = window2.blocks.map((block) => block.railId).join(", ");
22200
+ return window2.resetsAt ? `Today's deposit limit is used: renewing with the same total (${usd2(total)}) leaves ${railIds} no room for its first deposit until it resets at ${window2.resetsAt}, and its first turn would be refused (policy_denied). Raise the total to at least ${usd2(window2.minimumTotal)} at renewal, or wait until ${window2.resetsAt}.` : `Today's deposit limit is used: renewing with the same total (${usd2(total)}) leaves ${railIds} no room for its first deposit while a deposit or purchase of unknown outcome counts in full, and its first turn would be refused (policy_denied). Raise the total to at least ${usd2(window2.minimumTotal)} at renewal, or reconcile the unknown outcome first.`;
21491
22201
  }
21492
22202
 
21493
22203
  // src/lib/agent/signals.ts
@@ -21544,10 +22254,10 @@ var STOP_TIMEOUT_MS = 15e3;
21544
22254
  var log = (message) => process.stderr.write(`[taskmarket] ${message}
21545
22255
  `);
21546
22256
  function sessionLog() {
21547
- const file = path6.join(getTaskmarketHome(), "agent", "session.log");
22257
+ const file = path5.join(getTaskmarketHome(), "agent", "session.log");
21548
22258
  return (message) => {
21549
22259
  try {
21550
- mkdirSync(path6.dirname(file), { recursive: true, mode: 448 });
22260
+ mkdirSync(path5.dirname(file), { recursive: true, mode: 448 });
21551
22261
  appendFileSync(file, `${(/* @__PURE__ */ new Date()).toISOString()} ${message}
21552
22262
  `, { mode: 384 });
21553
22263
  } catch {
@@ -21564,7 +22274,16 @@ function withTimeout(promise, ms) {
21564
22274
  ]).finally(() => clearTimeout(timer));
21565
22275
  }
21566
22276
  async function runAgent(options) {
21567
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
22277
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
22278
+ if (!await keystoreExists()) {
22279
+ throw new AgentLaunchError(
22280
+ "wallet_required",
22281
+ "No wallet is set up. Import one with `taskmarket wallet import` (or create one with `taskmarket init`), then run taskmarket agent again; this command never asks for a key"
22282
+ );
22283
+ }
22284
+ const keystore = await loadKeystore();
22285
+ await ensureAgentPolicy({ interactive, walletAddress: keystore.walletAddress });
22286
+ if (!interactive) {
21568
22287
  throw new AgentLaunchError(
21569
22288
  "interactive_terminal_required",
21570
22289
  "taskmarket agent opens an interactive session and needs a terminal"
@@ -21574,7 +22293,7 @@ async function runAgent(options) {
21574
22293
  onCleanupSignal: () => log("Finishing: settling purchases and stopping the sandbox")
21575
22294
  });
21576
22295
  try {
21577
- return await runSession(options, signals);
22296
+ return await runSession(options, signals, keystore);
21578
22297
  } finally {
21579
22298
  signals.dispose();
21580
22299
  }
@@ -21585,37 +22304,25 @@ function interruptedLaunch() {
21585
22304
  "The launch was interrupted before Pi started"
21586
22305
  );
21587
22306
  }
21588
- async function runSession(options, signals) {
22307
+ async function runSession(options, signals, keystore) {
21589
22308
  const checkInterrupted = () => {
21590
22309
  if (signals.interrupted) throw interruptedLaunch();
21591
22310
  };
21592
- if (!await keystoreExists()) {
21593
- throw new AgentLaunchError(
21594
- "wallet_required",
21595
- "No wallet is set up; run `taskmarket wallet import` or `taskmarket init` first"
21596
- );
21597
- }
21598
- const keystore = await loadKeystore();
21599
- const policy = await loadAgentPolicy();
21600
- const models = selectModels(policy, options.model);
21601
- let account;
21602
- const getAccount = () => {
21603
- account ??= createWalletAccountFromKeystore(keystore).catch((error) => {
21604
- account = void 0;
21605
- throw error;
21606
- });
21607
- return account;
21608
- };
21609
- const rails = await createAgentRails({
21610
- policy,
21611
- models,
21612
- walletAddress: keystore.walletAddress,
21613
- getAccount
22311
+ let models = [];
22312
+ const { rails } = await openAgentChannels({
22313
+ keystore,
22314
+ models: (policy) => models = selectModels(policy, options.model)
21614
22315
  });
21615
22316
  checkInterrupted();
21616
22317
  await reconcileLeftoverOperations(rails, log);
21617
22318
  checkInterrupted();
21618
- const transport = createAgentTransport(rails);
22319
+ const observeCompute = computeCreditObserver(sessionLog());
22320
+ const budget = createSessionBudget({
22321
+ rails,
22322
+ transport: createAgentTransport(rails),
22323
+ observeCompute
22324
+ });
22325
+ const transport = budget.transport;
21619
22326
  let offered;
21620
22327
  try {
21621
22328
  offered = (await transport.models(AbortSignal.any([AbortSignal.timeout(3e4), signals.setupSignal]))).map((row) => row.id);
@@ -21633,15 +22340,15 @@ async function runSession(options, signals) {
21633
22340
  `None of the approved models (${models.join(", ")}) is qualified by xgate on a route the policy pays for`
21634
22341
  );
21635
22342
  }
21636
- const observeCompute = computeCreditObserver(sessionLog());
21637
22343
  const observations = /* @__PURE__ */ new Set();
21638
22344
  const createBroker = () => new BrokerConnector({
21639
22345
  apiUrl: apiUrl(),
21640
22346
  deviceId: keystore.deviceId,
21641
22347
  apiToken: keystore.apiToken,
21642
22348
  purpose: "interactive",
21643
- idempotencyKey: randomUUID2(),
22349
+ idempotencyKey: randomUUID3(),
21644
22350
  onCreditWarning: (warning) => {
22351
+ budget.noteCreditWarning(warning);
21645
22352
  if (warning !== "low_credit" && warning !== "credit_exhausted") return;
21646
22353
  const observed = observeCompute({ creditWarning: warning });
21647
22354
  observations.add(observed);
@@ -21681,7 +22388,7 @@ async function runSession(options, signals) {
21681
22388
  return await runPiSession(launch, {
21682
22389
  secrets: [broker.toolKey.privateKey, keystore.apiToken, keystore.encryptedKey],
21683
22390
  broker,
21684
- transport,
22391
+ budget,
21685
22392
  signals
21686
22393
  });
21687
22394
  } finally {
@@ -21693,7 +22400,7 @@ async function runSession(options, signals) {
21693
22400
  STOP_TIMEOUT_MS
21694
22401
  );
21695
22402
  }
21696
- await withTimeout(Promise.allSettled([...observations]), STOP_TIMEOUT_MS);
22403
+ await withTimeout(Promise.allSettled([...observations, budget.settled()]), STOP_TIMEOUT_MS);
21697
22404
  }
21698
22405
  }
21699
22406
  async function runPiSession(launch, context) {
@@ -21720,8 +22427,15 @@ async function runPiSession(launch, context) {
21720
22427
  const inferenceFds = launch.inferenceFds;
21721
22428
  const inference = serveInferenceChannel(
21722
22429
  { requests: stdio[inferenceFds.request], replies: stdio[inferenceFds.reply] },
21723
- context.transport,
21724
- { log: diagnostics }
22430
+ context.budget.transport,
22431
+ {
22432
+ log: diagnostics,
22433
+ views: {
22434
+ budget: (view) => context.budget.view(view),
22435
+ creditOffer: () => context.budget.creditOffer(),
22436
+ buyCredits: (amountUsd) => context.budget.buyCredits(amountUsd)
22437
+ }
22438
+ }
21725
22439
  );
21726
22440
  let killTimer;
21727
22441
  context.signals.piStarted(() => {