salt-agent-sdk 0.1.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +941 -30
  3. package/dist/actions.d.ts +21 -5
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +673 -54
  6. package/dist/actions.js.map +1 -1
  7. package/dist/ask.d.ts +83 -0
  8. package/dist/ask.d.ts.map +1 -0
  9. package/dist/ask.js +215 -0
  10. package/dist/ask.js.map +1 -0
  11. package/dist/chatPrivacy.d.ts +34 -0
  12. package/dist/chatPrivacy.d.ts.map +1 -0
  13. package/dist/chatPrivacy.js +60 -0
  14. package/dist/chatPrivacy.js.map +1 -0
  15. package/dist/client.d.ts +3123 -35
  16. package/dist/client.d.ts.map +1 -1
  17. package/dist/client.js +1267 -143
  18. package/dist/client.js.map +1 -1
  19. package/dist/config.d.ts +16 -6
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +16 -5
  22. package/dist/config.js.map +1 -1
  23. package/dist/crypto.d.ts +62 -0
  24. package/dist/crypto.d.ts.map +1 -1
  25. package/dist/crypto.js +121 -0
  26. package/dist/crypto.js.map +1 -1
  27. package/dist/delegations.d.ts +25 -8
  28. package/dist/delegations.d.ts.map +1 -1
  29. package/dist/delegations.js +67 -11
  30. package/dist/delegations.js.map +1 -1
  31. package/dist/device/client.d.ts +161 -0
  32. package/dist/device/client.d.ts.map +1 -0
  33. package/dist/device/client.js +601 -0
  34. package/dist/device/client.js.map +1 -0
  35. package/dist/device/host.d.ts +222 -0
  36. package/dist/device/host.d.ts.map +1 -0
  37. package/dist/device/host.js +891 -0
  38. package/dist/device/host.js.map +1 -0
  39. package/dist/device/http.d.ts +12 -0
  40. package/dist/device/http.d.ts.map +1 -0
  41. package/dist/device/http.js +124 -0
  42. package/dist/device/http.js.map +1 -0
  43. package/dist/device/index.d.ts +5 -0
  44. package/dist/device/index.d.ts.map +1 -0
  45. package/dist/device/index.js +27 -0
  46. package/dist/device/index.js.map +1 -0
  47. package/dist/device/protocol.d.ts +312 -0
  48. package/dist/device/protocol.d.ts.map +1 -0
  49. package/dist/device/protocol.js +435 -0
  50. package/dist/device/protocol.js.map +1 -0
  51. package/dist/identities.d.ts +9 -2
  52. package/dist/identities.d.ts.map +1 -1
  53. package/dist/identities.js +33 -3
  54. package/dist/identities.js.map +1 -1
  55. package/dist/identity.d.ts +155 -0
  56. package/dist/identity.d.ts.map +1 -0
  57. package/dist/identity.js +196 -0
  58. package/dist/identity.js.map +1 -0
  59. package/dist/identityShare.d.ts +129 -0
  60. package/dist/identityShare.d.ts.map +1 -0
  61. package/dist/identityShare.js +308 -0
  62. package/dist/identityShare.js.map +1 -0
  63. package/dist/ids.d.ts +11 -0
  64. package/dist/ids.d.ts.map +1 -0
  65. package/dist/ids.js +17 -0
  66. package/dist/ids.js.map +1 -0
  67. package/dist/index.d.ts +13 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +13 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/reconcile.d.ts +25 -0
  72. package/dist/reconcile.d.ts.map +1 -0
  73. package/dist/reconcile.js +60 -0
  74. package/dist/reconcile.js.map +1 -0
  75. package/dist/recovery.d.ts +51 -0
  76. package/dist/recovery.d.ts.map +1 -0
  77. package/dist/recovery.js +59 -0
  78. package/dist/recovery.js.map +1 -0
  79. package/dist/register.d.ts +32 -0
  80. package/dist/register.d.ts.map +1 -0
  81. package/dist/register.js +65 -0
  82. package/dist/register.js.map +1 -0
  83. package/dist/scriptJob.d.ts +92 -0
  84. package/dist/scriptJob.d.ts.map +1 -0
  85. package/dist/scriptJob.js +270 -0
  86. package/dist/scriptJob.js.map +1 -0
  87. package/dist/sessions.d.ts +131 -0
  88. package/dist/sessions.d.ts.map +1 -0
  89. package/dist/sessions.js +240 -0
  90. package/dist/sessions.js.map +1 -0
  91. package/dist/socket.d.ts +115 -0
  92. package/dist/socket.d.ts.map +1 -0
  93. package/dist/socket.js +1038 -0
  94. package/dist/socket.js.map +1 -0
  95. package/dist/webhook.d.ts +627 -28
  96. package/dist/webhook.d.ts.map +1 -1
  97. package/dist/webhook.js +1388 -84
  98. package/dist/webhook.js.map +1 -1
  99. package/dist/work.d.ts +57 -0
  100. package/dist/work.d.ts.map +1 -0
  101. package/dist/work.js +213 -0
  102. package/dist/work.js.map +1 -0
  103. package/package.json +26 -4
@@ -18,7 +18,7 @@
18
18
  // only channel available between two separately-hosted agents is the
19
19
  // message text itself), and `register`'s timeout bounds each hop.
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.DELEGATION_TIMEOUT_MS = exports.MAX_DELEGATION_DEPTH = void 0;
21
+ exports.FLOOR_REQUEST_MARKER = exports.DELEGATION_TIMEOUT_MS = exports.MAX_DELEGATION_DEPTH = void 0;
22
22
  exports.wrap = wrap;
23
23
  exports.parseIncoming = parseIncoming;
24
24
  exports.hasPending = hasPending;
@@ -27,6 +27,11 @@ exports.cancel = cancel;
27
27
  exports.resolveIfPending = resolveIfPending;
28
28
  exports.recordTrail = recordTrail;
29
29
  exports.drainTrail = drainTrail;
30
+ exports.wrapConsult = wrapConsult;
31
+ exports.stripConsultMarker = stripConsultMarker;
32
+ exports.registerConsultAsker = registerConsultAsker;
33
+ exports.consultAskerFor = consultAskerFor;
34
+ const ids_js_1 = require("./ids.js");
30
35
  /** depth counts hops already taken; a call at depth >= this is refused, so a
31
36
  * chain reaches at most MAX_DELEGATION_DEPTH+1 agents before the last one
32
37
  * is forced to answer directly instead of delegating further. */
@@ -62,9 +67,12 @@ function parseIncoming(plaintext) {
62
67
  // concurrent delegation call into the same chat is a clean error (see
63
68
  // register below) rather than an ambiguous race over which reply belongs
64
69
  // to which caller.
70
+ // Keyed by the lowercased chat id -- see identities.ts for why ids are
71
+ // normalised before they are used as map keys.
65
72
  const pending = new Map();
73
+ const mapKey = (id) => String(id).toLowerCase();
66
74
  function hasPending(chatId) {
67
- return pending.has(chatId);
75
+ return pending.has(mapKey(chatId));
68
76
  }
69
77
  /**
70
78
  * Registers a wait for the next reply in `chatId` from `targetId`, and
@@ -73,15 +81,15 @@ function hasPending(chatId) {
73
81
  * if this chat already has a pending wait.
74
82
  */
75
83
  function register(chatId, targetId, timeoutMs) {
76
- if (pending.has(chatId)) {
84
+ if (pending.has(mapKey(chatId))) {
77
85
  throw new Error("Already waiting on a reply from this agent in this chat -- wait for that to finish first.");
78
86
  }
79
87
  return new Promise((resolve, reject) => {
80
88
  const timer = setTimeout(() => {
81
- pending.delete(chatId);
89
+ pending.delete(mapKey(chatId));
82
90
  reject(new Error(`No reply within ${Math.round(timeoutMs / 1000)}s -- the agent may be offline or slow.`));
83
91
  }, timeoutMs);
84
- pending.set(chatId, { targetId, resolve, reject, timer });
92
+ pending.set(mapKey(chatId), { targetId, resolve, reject, timer });
85
93
  });
86
94
  }
87
95
  /**
@@ -90,11 +98,11 @@ function register(chatId, targetId, timeoutMs) {
90
98
  * registry entry doesn't leak (the caller throws its own error instead).
91
99
  */
92
100
  function cancel(chatId) {
93
- const entry = pending.get(chatId);
101
+ const entry = pending.get(mapKey(chatId));
94
102
  if (!entry)
95
103
  return;
96
104
  clearTimeout(entry.timer);
97
- pending.delete(chatId);
105
+ pending.delete(mapKey(chatId));
98
106
  }
99
107
  /**
100
108
  * Call right after decrypting an inbound message, before deciding whether
@@ -106,11 +114,16 @@ function cancel(chatId) {
106
114
  * entry on its own server.
107
115
  */
108
116
  function resolveIfPending(chatId, senderId, plaintext) {
109
- const entry = pending.get(chatId);
110
- if (!entry || entry.targetId !== senderId)
117
+ const entry = pending.get(mapKey(chatId));
118
+ // sameId, not `!==`: this decides whether an inbound message is THE reply
119
+ // being waited on. Comparing a uuid that came from a webhook body against one
120
+ // that came from a REST response is not guaranteed to match on casing, and a
121
+ // false negative here restarts the whole prompt cycle instead of completing
122
+ // the delegation.
123
+ if (!entry || !(0, ids_js_1.sameId)(entry.targetId, senderId))
111
124
  return false;
112
125
  clearTimeout(entry.timer);
113
- pending.delete(chatId);
126
+ pending.delete(mapKey(chatId));
114
127
  entry.resolve(plaintext);
115
128
  return true;
116
129
  }
@@ -123,7 +136,7 @@ function resolveIfPending(chatId, senderId, plaintext) {
123
136
  // identities) never mix their trails.
124
137
  const trails = new Map();
125
138
  function trailKey(identityId, mainChatId) {
126
- return `${identityId}:${mainChatId}`;
139
+ return `${mapKey(identityId)}:${mapKey(mainChatId)}`;
127
140
  }
128
141
  function recordTrail(identityId, mainChatId, entry) {
129
142
  const key = trailKey(identityId, mainChatId);
@@ -137,4 +150,47 @@ function drainTrail(identityId, mainChatId) {
137
150
  trails.delete(key);
138
151
  return entries;
139
152
  }
153
+ // --- Consult lanes ---------------------------------------------------------
154
+ //
155
+ // A consult is a delegation that happens INLINE, in a lane off a chat both
156
+ // parties are already in (actions.ts's consult_agent), rather than in a
157
+ // separate 1:1 (delegate_to_agent). The reply-matching above (register/
158
+ // resolveIfPending) is reused as-is -- consult_agent registers a pending
159
+ // wait on the lane's own chat id exactly like delegate_to_agent does on a
160
+ // delegation chat's id. What's new is the wire marker the CONSULT PROMPT
161
+ // itself carries (stripped before the consulted agent's onMessage ever
162
+ // sees it, same convention as the depth marker above), and a small
163
+ // registry so the asking agent's OWN webhook.ts recognizes a request_floor
164
+ // message arriving back in a lane it opened.
165
+ const CONSULT_MARKER_RE = /^\[\[SALT-CONSULT room=[^\]\n]+\]\]\n/;
166
+ /** Prefixes a consult prompt so the receiving end can strip it before the task text reaches that identity's agent logic. `roomId` is informational only -- the receiving side gets the authoritative room id from the lane's own `coaching_for_chat_id`, already server-verified. */
167
+ function wrapConsult(roomId, text) {
168
+ return `[[SALT-CONSULT room=${roomId}]]\n${text}`;
169
+ }
170
+ /** Strips a leading SALT-CONSULT marker, if present. A no-op on anything else (an ordinary message, or an agent's plain-text reply). */
171
+ function stripConsultMarker(text) {
172
+ return text.replace(CONSULT_MARKER_RE, "");
173
+ }
174
+ /** The exact marker request_floor (actions.ts) posts into a consult lane to ask the asker to hand off rather than keep relaying through the lane. webhook.ts treats a message starting with this as wire protocol -- never an onMessage prompt -- for whichever identity opened that lane. */
175
+ exports.FLOOR_REQUEST_MARKER = "[[SALT-FLOOR-REQUEST]]";
176
+ // laneChatId (lowercased) -> who opened it and from where. Bounded the same
177
+ // way webhook.ts bounds its own dedup sets: a long-lived process consults a
178
+ // great many lanes over time, and none of them need to be remembered once
179
+ // they're old enough that a floor request on them is no longer plausible.
180
+ const MAX_CONSULT_ASKERS = 2000;
181
+ const consultAskers = new Map();
182
+ /** Records that `askerId` opened `laneChatId` as a consult lane off `roomId`. Called by consult_agent right after the lane is opened. */
183
+ function registerConsultAsker(laneChatId, askerId, roomId) {
184
+ const key = mapKey(laneChatId);
185
+ consultAskers.set(key, { askerId, roomId });
186
+ if (consultAskers.size > MAX_CONSULT_ASKERS) {
187
+ const oldest = consultAskers.keys().next().value;
188
+ if (oldest !== undefined)
189
+ consultAskers.delete(oldest);
190
+ }
191
+ }
192
+ /** Who opened `laneChatId` as a consult lane, and from where -- or undefined if this process didn't (or has forgotten). */
193
+ function consultAskerFor(laneChatId) {
194
+ return consultAskers.get(mapKey(laneChatId));
195
+ }
140
196
  //# sourceMappingURL=delegations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"delegations.js","sourceRoot":"","sources":["../src/delegations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"delegations.js","sourceRoot":"","sources":["../src/delegations.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,4DAA4D;AAC5D,8EAA8E;AAC9E,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,0EAA0E;AAC1E,6EAA6E;AAC7E,uEAAuE;AACvE,iFAAiF;AACjF,4EAA4E;AAC5E,wEAAwE;AACxE,cAAc;AACd,6EAA6E;AAC7E,yEAAyE;AACzE,2EAA2E;AAC3E,wEAAwE;AACxE,qEAAqE;;;AAsBrE,oBAEC;AASD,sCAIC;AAkBD,gCAEC;AAQD,4BAWC;AAOD,wBAKC;AAWD,4CAYC;AAuBD,kCAIC;AAED,gCAKC;AAkBD,kCAEC;AAGD,gDAEC;AAmBD,oDAOC;AAGD,0CAEC;AAvMD,qCAA+C;AAE/C;;kEAEkE;AACrD,QAAA,oBAAoB,GAAG,CAAC,CAAC;AAEtC;;gDAEgD;AACnC,QAAA,qBAAqB,GAAG,MAAM,CAAC;AAE5C,MAAM,SAAS,GAAG,wCAAwC,CAAC;AAE3D;;;;;GAKG;AACH,SAAgB,IAAI,CAAC,KAAa,EAAE,IAAY;IAC9C,OAAO,2BAA2B,KAAK,OAAO,IAAI,EAAE,CAAC;AACvD,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,SAAiB;IAC7C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACjD,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;AACnF,CAAC;AASD,0EAA0E;AAC1E,sEAAsE;AACtE,yEAAyE;AACzE,mBAAmB;AACnB,uEAAuE;AACvE,+CAA+C;AAC/C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;AAChD,MAAM,MAAM,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AAEhE,SAAgB,UAAU,CAAC,MAAc;IACvC,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,QAAQ,CAAC,MAAc,EAAE,QAAgB,EAAE,SAAiB;IAC1E,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAC;IAC/G,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,wCAAwC,CAAC,CAAC,CAAC;QAC7G,CAAC,EAAE,SAAS,CAAC,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAgB,MAAM,CAAC,MAAc;IACnC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,gBAAgB,CAAC,MAAc,EAAE,QAAgB,EAAE,SAAiB;IAClF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,kBAAkB;IAClB,IAAI,CAAC,KAAK,IAAI,CAAC,IAAA,eAAM,EAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9D,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/B,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AACd,CAAC;AAUD,uEAAuE;AACvE,uEAAuE;AACvE,kEAAkE;AAClE,sEAAsE;AACtE,uEAAuE;AACvE,mEAAmE;AACnE,sCAAsC;AACtC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkC,CAAC;AAEzD,SAAS,QAAQ,CAAC,UAAkB,EAAE,UAAkB;IACtD,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;AACvD,CAAC;AAED,SAAgB,WAAW,CAAC,UAAkB,EAAE,UAAkB,EAAE,KAA2B;IAC7F,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,SAAgB,UAAU,CAAC,UAAkB,EAAE,UAAkB;IAC/D,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IACtC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8EAA8E;AAC9E,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,yEAAyE;AACzE,uEAAuE;AACvE,mEAAmE;AACnE,2EAA2E;AAC3E,6CAA6C;AAE7C,MAAM,iBAAiB,GAAG,uCAAuC,CAAC;AAElE,qRAAqR;AACrR,SAAgB,WAAW,CAAC,MAAc,EAAE,IAAY;IACtD,OAAO,uBAAuB,MAAM,OAAO,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,wIAAwI;AACxI,SAAgB,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,8RAA8R;AACjR,QAAA,oBAAoB,GAAG,wBAAwB,CAAC;AAQ7D,4EAA4E;AAC5E,4EAA4E;AAC5E,0EAA0E;AAC1E,0EAA0E;AAC1E,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEtD,yIAAyI;AACzI,SAAgB,oBAAoB,CAAC,UAAkB,EAAE,OAAe,EAAE,MAAc;IACtF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAC/B,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5C,IAAI,aAAa,CAAC,IAAI,GAAG,kBAAkB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QACjD,IAAI,MAAM,KAAK,SAAS;YAAE,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,2HAA2H;AAC3H,SAAgB,eAAe,CAAC,UAAkB;IAChD,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,161 @@
1
+ import { DeviceProtocolError, type ClickArgs, type DevicePauseReason, type DeviceResultOp, type FileResult, type ObservationResult, type ObserveArgs, type ScrollArgs, type ElementRef, type AppsResult, type Progress, type TaskResult } from "./protocol.js";
2
+ import { type SaltId } from "../ids.js";
3
+ /** Who the lane messages must be encrypted for: every reader's armored public
4
+ * key. Carried by the `device_session_active` delivery. */
5
+ export interface LaneReaders {
6
+ chatId: SaltId;
7
+ publicKeys: string[];
8
+ }
9
+ export type DeviceSessionStatus = "queued" | "requested" | "active" | "paused" | "ended";
10
+ export interface DeviceSessionMeta {
11
+ id: SaltId;
12
+ device_id: SaltId;
13
+ agent_id: SaltId;
14
+ mandate_id: SaltId;
15
+ status: DeviceSessionStatus;
16
+ /** `queued` only: 1-based place in the device's queue. */
17
+ position?: number;
18
+ /** `paused` only. */
19
+ pause_reason?: DevicePauseReason;
20
+ chat?: {
21
+ id: SaltId;
22
+ readers: string[];
23
+ };
24
+ /** What the agent said it is doing when it opened (its own words, untrusted; null/absent when it gave none). */
25
+ intent?: string | null;
26
+ }
27
+ /** Thrown by a transport's openSession on `409 {code:"session_live", session_id}`:
28
+ * this agent already has a live session on the device. open() reattaches. */
29
+ export declare class DeviceSessionLiveError extends Error {
30
+ readonly sessionId: SaltId;
31
+ constructor(sessionId: SaltId);
32
+ }
33
+ /** A command was refused (or not sent) because the session is paused: the
34
+ * person is using the machine, or a secure field needs them. Wait for the
35
+ * `resumed` state (onState) and try again. */
36
+ export declare class DevicePausedError extends DeviceProtocolError {
37
+ readonly reason: DevicePauseReason;
38
+ constructor(reason: DevicePauseReason);
39
+ }
40
+ /** What onState emits. */
41
+ export type DeviceState = {
42
+ state: "queued";
43
+ position: number;
44
+ } | {
45
+ state: "requested";
46
+ } | {
47
+ state: "active";
48
+ } | {
49
+ state: "paused";
50
+ reason: DevicePauseReason;
51
+ } | {
52
+ state: "resumed";
53
+ } | {
54
+ state: "ended";
55
+ reason: string;
56
+ };
57
+ /** The injected transport. httpDeviceTransport provides a real one. */
58
+ export interface DeviceClientTransport {
59
+ /** POST /api/v1/devices/:id/sessions -> the created session: status
60
+ * `requested` (device must approve locally), `active`, or `queued` (202,
61
+ * another agent controls the device). Throws DeviceSessionLiveError on a 409
62
+ * `session_live`. */
63
+ openSession(deviceId: SaltId, opts?: {
64
+ intent?: string;
65
+ }): Promise<DeviceSessionMeta>;
66
+ /** POST /api/v1/device_sessions/:sid/reattach -> {session, last_seq}. */
67
+ reattachSession?(sessionId: SaltId): Promise<{
68
+ session: DeviceSessionMeta;
69
+ last_seq: number;
70
+ }>;
71
+ /** GET /api/v1/device_sessions/:sid; used only to learn the lane readers when
72
+ * a promotion delivery did not carry them. */
73
+ fetchSession?(sessionId: SaltId): Promise<DeviceSessionMeta>;
74
+ /** POST /api/v1/device_sessions/:sid/stop (subtract-only, idempotent). */
75
+ stopSession(sessionId: SaltId): Promise<void>;
76
+ /** Encrypt `plaintext` for the current lane readers and POST it as one lane
77
+ * message. The transport owns the crypto and the readers; this module only
78
+ * decides the plaintext. */
79
+ postCommand(plaintext: string, readers: LaneReaders): Promise<void>;
80
+ }
81
+ export interface DeviceClientOptions {
82
+ /** How long a session may sit in `requested` (waiting for the device's local
83
+ * approval) before open() gives up. Does not run while `queued`. The server
84
+ * ends an unanswered request after 2 minutes with `request_expired`;
85
+ * default here is a touch longer so the server's own end reaches us first. */
86
+ approvalTimeoutMs?: number;
87
+ /** Per-command timeout waiting for a result. Default 30s. On timeout the
88
+ * client sends `cancel {id}`. */
89
+ commandTimeoutMs?: number;
90
+ /** How long a command stays valid (header `exp`). Default COMMAND_TTL_MS. */
91
+ commandTtlMs?: number;
92
+ }
93
+ export interface DeviceResult {
94
+ op: DeviceResultOp;
95
+ body: unknown;
96
+ }
97
+ type Listener = (...args: unknown[]) => void;
98
+ export interface DeviceClient {
99
+ /** Open a session. By default resolves when it is `active`, waiting through
100
+ * `queued` (another agent controls the device) and `requested` (the device's
101
+ * local approval) on `device_session` deliveries, never polling. Rejects if
102
+ * the device declines, the request expires, or the mandate is gone. With
103
+ * `{wait: false}` it returns at once with the state the server answered
104
+ * (e.g. `queued` with `position`); watch onState for the rest. If this
105
+ * agent already has a live session on the device (409 session_live) it
106
+ * reattaches and continues at last_seq + 1. */
107
+ open(opts?: {
108
+ wait?: boolean;
109
+ intent?: string;
110
+ }): Promise<DeviceSessionMeta>;
111
+ observe(args?: ObserveArgs): Promise<ObservationResult>;
112
+ click(args: ClickArgs): Promise<void>;
113
+ type(text: string): Promise<void>;
114
+ key(keys: string[]): Promise<void>;
115
+ scroll(args: ScrollArgs): Promise<void>;
116
+ /** Confirm a named field/form through the accessibility layer (never a keystroke). */
117
+ submit(element: ElementRef): Promise<void>;
118
+ focusApp(app: string): Promise<void>;
119
+ listApps(): Promise<AppsResult>;
120
+ readFile(path: string): Promise<FileResult>;
121
+ writeFile(path: string, contentB64: string): Promise<void>;
122
+ /** Hand the computer ONE whole task (op `task`, grant `device.task`) and wait for its checked
123
+ * result. `goal` is what the person asked for, in plain words (1..2000 characters); `doneWhen`
124
+ * says how the device should check it worked (<= 500). The device does the work itself: command
125
+ * output and screenshots never come back, only a `TaskResult` of words and numbers. Rejects with
126
+ * `bad_args` before sending when the arguments are out of range, `unsupported` when the computer
127
+ * cannot run tasks, `out_of_scope` without the grant or when the budget is spent. `onProgress`
128
+ * gets each non-final line in order (at most one every 2 s, newest wins). Waits
129
+ * `timeoutS + 30` s (default 180 + 30); on that timeout, or when `signal` aborts, it sends
130
+ * `cancel` with the task's id as `re`. After an abort it waits up to 10 s for the device's
131
+ * `stopped` result and resolves with it, else rejects. */
132
+ task(goal: string, opts?: {
133
+ doneWhen?: string;
134
+ maxSteps?: number;
135
+ timeoutS?: number;
136
+ onProgress?: (p: Progress) => void;
137
+ signal?: AbortSignal;
138
+ }): Promise<TaskResult>;
139
+ /** End the session (subtract-only). Safe to call more than once. */
140
+ stop(): Promise<void>;
141
+ /** The live session metadata, or null before open() / after it ended. */
142
+ session(): DeviceSessionMeta | null;
143
+ /** The pause reason while paused, else null. */
144
+ pausedReason(): DevicePauseReason | null;
145
+ /** Feed a `device_session` socket delivery in (promoted / queue_moved /
146
+ * paused / resumed / ended, and the legacy active / ended types). The
147
+ * consumer calls this from its dispatcher. */
148
+ handleDelivery(event: string, body: unknown): void;
149
+ /** Feed a decrypted lane message in (a result the device posted). Returns
150
+ * true if it was a device result this client consumed. */
151
+ handleLaneMessage(plaintext: string): boolean;
152
+ on(event: "active" | "ended" | "ask", cb: Listener): void;
153
+ /** Session state changes: queued(position) / requested / active /
154
+ * paused(reason) / resumed / ended(reason). Returns an unsubscribe. */
155
+ onState(cb: (s: DeviceState) => void): () => void;
156
+ }
157
+ export declare function createDeviceClient(transport: DeviceClientTransport, params: {
158
+ deviceId: SaltId;
159
+ }, options?: DeviceClientOptions): DeviceClient;
160
+ export {};
161
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/device/client.ts"],"names":[],"mappings":"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"}