@rivet-dev/agentos-runtime-core 0.0.0-chdir-fix.3d9feab

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 (282) hide show
  1. package/README.md +7 -0
  2. package/commands/[ +0 -0
  3. package/commands/_stubs +0 -0
  4. package/commands/arch +0 -0
  5. package/commands/awk +0 -0
  6. package/commands/b2sum +0 -0
  7. package/commands/base32 +0 -0
  8. package/commands/base64 +0 -0
  9. package/commands/basename +0 -0
  10. package/commands/basenc +0 -0
  11. package/commands/bash +0 -0
  12. package/commands/cat +0 -0
  13. package/commands/chcon +0 -0
  14. package/commands/chgrp +0 -0
  15. package/commands/chmod +0 -0
  16. package/commands/chown +0 -0
  17. package/commands/chroot +0 -0
  18. package/commands/cksum +0 -0
  19. package/commands/codex +0 -0
  20. package/commands/codex-exec +0 -0
  21. package/commands/column +0 -0
  22. package/commands/comm +0 -0
  23. package/commands/cp +0 -0
  24. package/commands/curl +0 -0
  25. package/commands/cut +0 -0
  26. package/commands/date +0 -0
  27. package/commands/dd +0 -0
  28. package/commands/df +0 -0
  29. package/commands/diff +0 -0
  30. package/commands/dir +0 -0
  31. package/commands/dircolors +0 -0
  32. package/commands/dirname +0 -0
  33. package/commands/du +0 -0
  34. package/commands/echo +0 -0
  35. package/commands/egrep +0 -0
  36. package/commands/env +0 -0
  37. package/commands/envsubst +0 -0
  38. package/commands/expand +0 -0
  39. package/commands/expr +0 -0
  40. package/commands/factor +0 -0
  41. package/commands/false +0 -0
  42. package/commands/fd +0 -0
  43. package/commands/fgrep +0 -0
  44. package/commands/file +0 -0
  45. package/commands/find +0 -0
  46. package/commands/fmt +0 -0
  47. package/commands/fold +0 -0
  48. package/commands/git +0 -0
  49. package/commands/git-remote-http +0 -0
  50. package/commands/git-remote-https +0 -0
  51. package/commands/grep +0 -0
  52. package/commands/groups +0 -0
  53. package/commands/gunzip +0 -0
  54. package/commands/gzip +0 -0
  55. package/commands/head +0 -0
  56. package/commands/hostid +0 -0
  57. package/commands/hostname +0 -0
  58. package/commands/http-test +0 -0
  59. package/commands/http_get +0 -0
  60. package/commands/id +0 -0
  61. package/commands/install +0 -0
  62. package/commands/join +0 -0
  63. package/commands/jq +0 -0
  64. package/commands/kill +0 -0
  65. package/commands/link +0 -0
  66. package/commands/ln +0 -0
  67. package/commands/logname +0 -0
  68. package/commands/ls +0 -0
  69. package/commands/md5sum +0 -0
  70. package/commands/mkdir +0 -0
  71. package/commands/mkfifo +0 -0
  72. package/commands/mknod +0 -0
  73. package/commands/mktemp +0 -0
  74. package/commands/more +0 -0
  75. package/commands/mv +0 -0
  76. package/commands/nice +0 -0
  77. package/commands/nl +0 -0
  78. package/commands/nohup +0 -0
  79. package/commands/nproc +0 -0
  80. package/commands/numfmt +0 -0
  81. package/commands/od +0 -0
  82. package/commands/paste +0 -0
  83. package/commands/pathchk +0 -0
  84. package/commands/pinky +0 -0
  85. package/commands/printenv +0 -0
  86. package/commands/printf +0 -0
  87. package/commands/ptx +0 -0
  88. package/commands/pwd +0 -0
  89. package/commands/readlink +0 -0
  90. package/commands/realpath +0 -0
  91. package/commands/rev +0 -0
  92. package/commands/rg +0 -0
  93. package/commands/rm +0 -0
  94. package/commands/rmdir +0 -0
  95. package/commands/runcon +0 -0
  96. package/commands/sed +0 -0
  97. package/commands/seq +0 -0
  98. package/commands/sh +0 -0
  99. package/commands/sha1sum +0 -0
  100. package/commands/sha224sum +0 -0
  101. package/commands/sha256sum +0 -0
  102. package/commands/sha384sum +0 -0
  103. package/commands/sha512sum +0 -0
  104. package/commands/shred +0 -0
  105. package/commands/shuf +0 -0
  106. package/commands/sleep +0 -0
  107. package/commands/sort +0 -0
  108. package/commands/spawn-test-host +0 -0
  109. package/commands/split +0 -0
  110. package/commands/sqlite3 +0 -0
  111. package/commands/stat +0 -0
  112. package/commands/stdbuf +0 -0
  113. package/commands/strings +0 -0
  114. package/commands/stty +0 -0
  115. package/commands/sum +0 -0
  116. package/commands/sync +0 -0
  117. package/commands/tac +0 -0
  118. package/commands/tail +0 -0
  119. package/commands/tar +0 -0
  120. package/commands/tee +0 -0
  121. package/commands/test +0 -0
  122. package/commands/timeout +0 -0
  123. package/commands/touch +0 -0
  124. package/commands/tr +0 -0
  125. package/commands/tree +0 -0
  126. package/commands/true +0 -0
  127. package/commands/truncate +0 -0
  128. package/commands/tsort +0 -0
  129. package/commands/tty +0 -0
  130. package/commands/uname +0 -0
  131. package/commands/unexpand +0 -0
  132. package/commands/uniq +0 -0
  133. package/commands/unlink +0 -0
  134. package/commands/unzip +0 -0
  135. package/commands/uptime +0 -0
  136. package/commands/users +0 -0
  137. package/commands/vdir +0 -0
  138. package/commands/wc +0 -0
  139. package/commands/which +0 -0
  140. package/commands/who +0 -0
  141. package/commands/whoami +0 -0
  142. package/commands/xargs +0 -0
  143. package/commands/xu +0 -0
  144. package/commands/yes +0 -0
  145. package/commands/yq +0 -0
  146. package/commands/zcat +0 -0
  147. package/commands/zip +0 -0
  148. package/dist/binary.d.ts +4 -0
  149. package/dist/binary.js +25 -0
  150. package/dist/bytes.d.ts +2 -0
  151. package/dist/bytes.js +6 -0
  152. package/dist/callbacks.d.ts +41 -0
  153. package/dist/callbacks.js +94 -0
  154. package/dist/cargo.d.ts +2 -0
  155. package/dist/cargo.js +142 -0
  156. package/dist/correlation.d.ts +7 -0
  157. package/dist/correlation.js +46 -0
  158. package/dist/descriptors.d.ts +84 -0
  159. package/dist/descriptors.js +60 -0
  160. package/dist/event-buffer.d.ts +90 -0
  161. package/dist/event-buffer.js +313 -0
  162. package/dist/ext.d.ts +7 -0
  163. package/dist/ext.js +13 -0
  164. package/dist/filesystem.d.ts +41 -0
  165. package/dist/filesystem.js +70 -0
  166. package/dist/frame-payload-codec.d.ts +8 -0
  167. package/dist/frame-payload-codec.js +14 -0
  168. package/dist/frame-rpc.d.ts +45 -0
  169. package/dist/frame-rpc.js +98 -0
  170. package/dist/frame-stream.d.ts +34 -0
  171. package/dist/frame-stream.js +109 -0
  172. package/dist/framing.d.ts +8 -0
  173. package/dist/framing.js +23 -0
  174. package/dist/generated/AcpLimitsConfig.d.ts +4 -0
  175. package/dist/generated/AcpLimitsConfig.js +2 -0
  176. package/dist/generated/CreateVmConfig.d.ts +26 -0
  177. package/dist/generated/CreateVmConfig.js +1 -0
  178. package/dist/generated/FsPermissionRule.d.ts +6 -0
  179. package/dist/generated/FsPermissionRule.js +1 -0
  180. package/dist/generated/FsPermissionRuleSet.d.ts +6 -0
  181. package/dist/generated/FsPermissionRuleSet.js +1 -0
  182. package/dist/generated/FsPermissionScope.d.ts +3 -0
  183. package/dist/generated/FsPermissionScope.js +1 -0
  184. package/dist/generated/HttpLimitsConfig.d.ts +3 -0
  185. package/dist/generated/HttpLimitsConfig.js +2 -0
  186. package/dist/generated/JsModuleResolution.d.ts +1 -0
  187. package/dist/generated/JsModuleResolution.js +2 -0
  188. package/dist/generated/JsRuntimeConfig.d.ts +31 -0
  189. package/dist/generated/JsRuntimeConfig.js +1 -0
  190. package/dist/generated/JsRuntimeLimitsConfig.d.ts +11 -0
  191. package/dist/generated/JsRuntimeLimitsConfig.js +2 -0
  192. package/dist/generated/JsRuntimePlatform.d.ts +1 -0
  193. package/dist/generated/JsRuntimePlatform.js +2 -0
  194. package/dist/generated/MountPluginDescriptor.d.ts +4 -0
  195. package/dist/generated/MountPluginDescriptor.js +2 -0
  196. package/dist/generated/NativeRootFilesystemConfig.d.ts +5 -0
  197. package/dist/generated/NativeRootFilesystemConfig.js +1 -0
  198. package/dist/generated/PatternPermissionRule.d.ts +6 -0
  199. package/dist/generated/PatternPermissionRule.js +1 -0
  200. package/dist/generated/PatternPermissionRuleSet.d.ts +6 -0
  201. package/dist/generated/PatternPermissionRuleSet.js +1 -0
  202. package/dist/generated/PatternPermissionScope.d.ts +3 -0
  203. package/dist/generated/PatternPermissionScope.js +1 -0
  204. package/dist/generated/PermissionMode.d.ts +1 -0
  205. package/dist/generated/PermissionMode.js +2 -0
  206. package/dist/generated/PermissionsPolicy.d.ts +10 -0
  207. package/dist/generated/PermissionsPolicy.js +1 -0
  208. package/dist/generated/PluginLimitsConfig.d.ts +4 -0
  209. package/dist/generated/PluginLimitsConfig.js +2 -0
  210. package/dist/generated/PythonLimitsConfig.d.ts +6 -0
  211. package/dist/generated/PythonLimitsConfig.js +2 -0
  212. package/dist/generated/ResourceLimitsConfig.d.ts +24 -0
  213. package/dist/generated/ResourceLimitsConfig.js +2 -0
  214. package/dist/generated/RootFilesystemConfig.d.ts +9 -0
  215. package/dist/generated/RootFilesystemConfig.js +1 -0
  216. package/dist/generated/RootFilesystemEntry.d.ts +13 -0
  217. package/dist/generated/RootFilesystemEntry.js +1 -0
  218. package/dist/generated/RootFilesystemEntryEncoding.d.ts +1 -0
  219. package/dist/generated/RootFilesystemEntryEncoding.js +2 -0
  220. package/dist/generated/RootFilesystemEntryKind.d.ts +1 -0
  221. package/dist/generated/RootFilesystemEntryKind.js +2 -0
  222. package/dist/generated/RootFilesystemLowerDescriptor.d.ts +7 -0
  223. package/dist/generated/RootFilesystemLowerDescriptor.js +1 -0
  224. package/dist/generated/RootFilesystemMode.d.ts +1 -0
  225. package/dist/generated/RootFilesystemMode.js +2 -0
  226. package/dist/generated/ToolLimitsConfig.d.ts +10 -0
  227. package/dist/generated/ToolLimitsConfig.js +2 -0
  228. package/dist/generated/VmDnsConfig.d.ts +6 -0
  229. package/dist/generated/VmDnsConfig.js +2 -0
  230. package/dist/generated/VmLimitsConfig.d.ts +18 -0
  231. package/dist/generated/VmLimitsConfig.js +1 -0
  232. package/dist/generated/VmListenPolicyConfig.d.ts +5 -0
  233. package/dist/generated/VmListenPolicyConfig.js +2 -0
  234. package/dist/generated/WasmLimitsConfig.d.ts +7 -0
  235. package/dist/generated/WasmLimitsConfig.js +2 -0
  236. package/dist/generated-protocol.d.ts +1196 -0
  237. package/dist/generated-protocol.js +3286 -0
  238. package/dist/index.d.ts +29 -0
  239. package/dist/index.js +28 -0
  240. package/dist/json.d.ts +2 -0
  241. package/dist/json.js +20 -0
  242. package/dist/kernel-proxy.d.ts +166 -0
  243. package/dist/kernel-proxy.js +1787 -0
  244. package/dist/native-client.d.ts +46 -0
  245. package/dist/native-client.js +136 -0
  246. package/dist/node-runtime-options-schema.d.ts +117 -0
  247. package/dist/node-runtime-options-schema.js +121 -0
  248. package/dist/node-runtime.d.ts +593 -0
  249. package/dist/node-runtime.js +914 -0
  250. package/dist/numbers.d.ts +1 -0
  251. package/dist/numbers.js +8 -0
  252. package/dist/ownership.d.ts +18 -0
  253. package/dist/ownership.js +77 -0
  254. package/dist/permissions.d.ts +29 -0
  255. package/dist/permissions.js +68 -0
  256. package/dist/process.d.ts +22 -0
  257. package/dist/process.js +97 -0
  258. package/dist/protocol-client.d.ts +65 -0
  259. package/dist/protocol-client.js +242 -0
  260. package/dist/protocol-frames.d.ts +68 -0
  261. package/dist/protocol-frames.js +139 -0
  262. package/dist/protocol-maps.d.ts +31 -0
  263. package/dist/protocol-maps.js +273 -0
  264. package/dist/protocol-schema.d.ts +10 -0
  265. package/dist/protocol-schema.js +11 -0
  266. package/dist/request-payloads.d.ts +171 -0
  267. package/dist/request-payloads.js +269 -0
  268. package/dist/response-payloads.d.ts +186 -0
  269. package/dist/response-payloads.js +250 -0
  270. package/dist/sidecar-client.d.ts +17 -0
  271. package/dist/sidecar-client.js +1 -0
  272. package/dist/sidecar-errors.d.ts +30 -0
  273. package/dist/sidecar-errors.js +47 -0
  274. package/dist/sidecar-process.d.ts +360 -0
  275. package/dist/sidecar-process.js +1007 -0
  276. package/dist/state.d.ts +40 -0
  277. package/dist/state.js +43 -0
  278. package/dist/test-runtime.d.ts +566 -0
  279. package/dist/test-runtime.js +2170 -0
  280. package/dist/vm-config.d.ts +31 -0
  281. package/dist/vm-config.js +1 -0
  282. package/package.json +204 -0
@@ -0,0 +1 @@
1
+ export declare function bigIntToSafeNumber(value: bigint, context: string): number;
@@ -0,0 +1,8 @@
1
+ export function bigIntToSafeNumber(value, context) {
2
+ const max = BigInt(Number.MAX_SAFE_INTEGER);
3
+ const min = BigInt(Number.MIN_SAFE_INTEGER);
4
+ if (value > max || value < min) {
5
+ throw new Error(`${context} exceeds JavaScript safe integer range`);
6
+ }
7
+ return Number(value);
8
+ }
@@ -0,0 +1,18 @@
1
+ import * as protocol from "./generated-protocol.js";
2
+ export type LiveOwnershipScope = {
3
+ scope: "connection";
4
+ connection_id: string;
5
+ } | {
6
+ scope: "session";
7
+ connection_id: string;
8
+ session_id: string;
9
+ } | {
10
+ scope: "vm";
11
+ connection_id: string;
12
+ session_id: string;
13
+ vm_id: string;
14
+ };
15
+ export declare function ownershipSelectorKey(ownership: LiveOwnershipScope): string;
16
+ export declare function ownershipMatchesSelector(selector: LiveOwnershipScope | undefined, ownership: LiveOwnershipScope): boolean;
17
+ export declare function toGeneratedOwnershipScope(ownership: LiveOwnershipScope): protocol.OwnershipScope;
18
+ export declare function fromGeneratedOwnershipScope(ownership: protocol.OwnershipScope): LiveOwnershipScope;
@@ -0,0 +1,77 @@
1
+ export function ownershipSelectorKey(ownership) {
2
+ switch (ownership.scope) {
3
+ case "connection":
4
+ return `connection:${ownership.connection_id}`;
5
+ case "session":
6
+ return `session:${ownership.connection_id}:${ownership.session_id}`;
7
+ case "vm":
8
+ return `vm:${ownership.connection_id}:${ownership.session_id}:${ownership.vm_id}`;
9
+ }
10
+ }
11
+ export function ownershipMatchesSelector(selector, ownership) {
12
+ if (!selector) {
13
+ return true;
14
+ }
15
+ switch (selector.scope) {
16
+ case "connection":
17
+ return (ownership.scope === "connection" &&
18
+ selector.connection_id === ownership.connection_id);
19
+ case "session":
20
+ return (ownership.scope === "session" &&
21
+ selector.connection_id === ownership.connection_id &&
22
+ selector.session_id === ownership.session_id);
23
+ case "vm":
24
+ return (ownership.scope === "vm" &&
25
+ selector.connection_id === ownership.connection_id &&
26
+ selector.session_id === ownership.session_id &&
27
+ selector.vm_id === ownership.vm_id);
28
+ }
29
+ }
30
+ export function toGeneratedOwnershipScope(ownership) {
31
+ switch (ownership.scope) {
32
+ case "connection":
33
+ return {
34
+ tag: "ConnectionOwnership",
35
+ val: { connectionId: ownership.connection_id },
36
+ };
37
+ case "session":
38
+ return {
39
+ tag: "SessionOwnership",
40
+ val: {
41
+ connectionId: ownership.connection_id,
42
+ sessionId: ownership.session_id,
43
+ },
44
+ };
45
+ case "vm":
46
+ return {
47
+ tag: "VmOwnership",
48
+ val: {
49
+ connectionId: ownership.connection_id,
50
+ sessionId: ownership.session_id,
51
+ vmId: ownership.vm_id,
52
+ },
53
+ };
54
+ }
55
+ }
56
+ export function fromGeneratedOwnershipScope(ownership) {
57
+ switch (ownership.tag) {
58
+ case "ConnectionOwnership":
59
+ return {
60
+ scope: "connection",
61
+ connection_id: ownership.val.connectionId,
62
+ };
63
+ case "SessionOwnership":
64
+ return {
65
+ scope: "session",
66
+ connection_id: ownership.val.connectionId,
67
+ session_id: ownership.val.sessionId,
68
+ };
69
+ case "VmOwnership":
70
+ return {
71
+ scope: "vm",
72
+ connection_id: ownership.val.connectionId,
73
+ session_id: ownership.val.sessionId,
74
+ vm_id: ownership.val.vmId,
75
+ };
76
+ }
77
+ }
@@ -0,0 +1,29 @@
1
+ import * as protocol from "./generated-protocol.js";
2
+ import { type LivePermissionMode } from "./protocol-maps.js";
3
+ export type { LivePermissionMode } from "./protocol-maps.js";
4
+ export interface LiveFsPermissionRule {
5
+ mode: LivePermissionMode;
6
+ operations?: string[];
7
+ paths?: string[];
8
+ }
9
+ export interface LivePatternPermissionRule {
10
+ mode: LivePermissionMode;
11
+ operations?: string[];
12
+ patterns?: string[];
13
+ }
14
+ export interface LiveRulePermissions<TRule> {
15
+ default?: LivePermissionMode;
16
+ rules: TRule[];
17
+ }
18
+ export type LivePermissionScope<TRule> = LivePermissionMode | LiveRulePermissions<TRule>;
19
+ export interface LivePermissionsPolicy {
20
+ fs?: LivePermissionScope<LiveFsPermissionRule>;
21
+ network?: LivePermissionScope<LivePatternPermissionRule>;
22
+ child_process?: LivePermissionScope<LivePatternPermissionRule>;
23
+ process?: LivePermissionScope<LivePatternPermissionRule>;
24
+ env?: LivePermissionScope<LivePatternPermissionRule>;
25
+ binding?: LivePermissionScope<LivePatternPermissionRule>;
26
+ }
27
+ export declare function toGeneratedPermissionsPolicy(policy: LivePermissionsPolicy | undefined): protocol.PermissionsPolicy | null;
28
+ export declare function toGeneratedFilesystemPermissionScope(scope: LivePermissionScope<LiveFsPermissionRule>): protocol.FsPermissionScope;
29
+ export declare function toGeneratedPatternPermissionScope(scope: LivePermissionScope<LivePatternPermissionRule>): protocol.PatternPermissionScope;
@@ -0,0 +1,68 @@
1
+ import { toGeneratedPermissionMode, } from "./protocol-maps.js";
2
+ export function toGeneratedPermissionsPolicy(policy) {
3
+ if (policy === undefined) {
4
+ return null;
5
+ }
6
+ return {
7
+ fs: policy.fs === undefined
8
+ ? null
9
+ : toGeneratedFilesystemPermissionScope(policy.fs),
10
+ network: policy.network === undefined
11
+ ? null
12
+ : toGeneratedPatternPermissionScope(policy.network),
13
+ childProcess: policy.child_process === undefined
14
+ ? null
15
+ : toGeneratedPatternPermissionScope(policy.child_process),
16
+ process: policy.process === undefined
17
+ ? null
18
+ : toGeneratedPatternPermissionScope(policy.process),
19
+ env: policy.env === undefined
20
+ ? null
21
+ : toGeneratedPatternPermissionScope(policy.env),
22
+ binding: policy.binding === undefined
23
+ ? null
24
+ : toGeneratedPatternPermissionScope(policy.binding),
25
+ };
26
+ }
27
+ export function toGeneratedFilesystemPermissionScope(scope) {
28
+ if (typeof scope === "string") {
29
+ return {
30
+ tag: "PermissionMode",
31
+ val: toGeneratedPermissionMode(scope),
32
+ };
33
+ }
34
+ return {
35
+ tag: "FsPermissionRuleSet",
36
+ val: {
37
+ default: scope.default === undefined
38
+ ? null
39
+ : toGeneratedPermissionMode(scope.default),
40
+ rules: scope.rules.map((rule) => ({
41
+ mode: toGeneratedPermissionMode(rule.mode),
42
+ operations: rule.operations ?? [],
43
+ paths: rule.paths ?? [],
44
+ })),
45
+ },
46
+ };
47
+ }
48
+ export function toGeneratedPatternPermissionScope(scope) {
49
+ if (typeof scope === "string") {
50
+ return {
51
+ tag: "PermissionMode",
52
+ val: toGeneratedPermissionMode(scope),
53
+ };
54
+ }
55
+ return {
56
+ tag: "PatternPermissionRuleSet",
57
+ val: {
58
+ default: scope.default === undefined
59
+ ? null
60
+ : toGeneratedPermissionMode(scope.default),
61
+ rules: scope.rules.map((rule) => ({
62
+ mode: toGeneratedPermissionMode(rule.mode),
63
+ operations: rule.operations ?? [],
64
+ patterns: rule.patterns ?? [],
65
+ })),
66
+ },
67
+ };
68
+ }
@@ -0,0 +1,22 @@
1
+ import { type ChildProcessWithoutNullStreams } from "node:child_process";
2
+ export { SidecarProcessError, SidecarProcessExited, } from "./sidecar-errors.js";
3
+ import { SidecarProcessError, SidecarProcessExited } from "./sidecar-errors.js";
4
+ export interface StdioSidecarProcessSpawnOptions {
5
+ command: string;
6
+ args?: string[];
7
+ cwd?: string;
8
+ }
9
+ export declare class StdioSidecarProcess {
10
+ readonly child: ChildProcessWithoutNullStreams;
11
+ private readonly stderrChunks;
12
+ private readonly exitListeners;
13
+ private readonly errorListeners;
14
+ private constructor();
15
+ static spawn(options: StdioSidecarProcessSpawnOptions): StdioSidecarProcess;
16
+ static fromChild(child: ChildProcessWithoutNullStreams): StdioSidecarProcess;
17
+ onExit(handler: (error: SidecarProcessExited) => void): () => void;
18
+ onError(handler: (error: SidecarProcessError) => void): () => void;
19
+ stderrText(): string;
20
+ currentExitError(): SidecarProcessExited | null;
21
+ waitForExit(timeoutMs: number): Promise<number | null>;
22
+ }
@@ -0,0 +1,97 @@
1
+ import { spawn, } from "node:child_process";
2
+ export { SidecarProcessError, SidecarProcessExited, } from "./sidecar-errors.js";
3
+ import { SidecarProcessError, SidecarProcessExited, } from "./sidecar-errors.js";
4
+ export class StdioSidecarProcess {
5
+ child;
6
+ stderrChunks = [];
7
+ exitListeners = new Set();
8
+ errorListeners = new Set();
9
+ constructor(child) {
10
+ this.child = child;
11
+ this.child.stderr.on("data", (chunk) => {
12
+ this.stderrChunks.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk));
13
+ });
14
+ this.child.on("exit", (code, signal) => {
15
+ const error = new SidecarProcessExited({
16
+ exitCode: code,
17
+ signal,
18
+ stderr: this.stderrText(),
19
+ });
20
+ for (const listener of this.exitListeners) {
21
+ listener(error);
22
+ }
23
+ });
24
+ this.child.on("error", (error) => {
25
+ const normalized = error instanceof Error ? error : new Error(String(error));
26
+ const sidecarError = new SidecarProcessError(normalized, this.stderrText());
27
+ for (const listener of this.errorListeners) {
28
+ listener(sidecarError);
29
+ }
30
+ });
31
+ }
32
+ static spawn(options) {
33
+ return new StdioSidecarProcess(spawn(options.command, options.args ?? [], {
34
+ cwd: options.cwd,
35
+ stdio: ["pipe", "pipe", "pipe"],
36
+ }));
37
+ }
38
+ static fromChild(child) {
39
+ return new StdioSidecarProcess(child);
40
+ }
41
+ onExit(handler) {
42
+ this.exitListeners.add(handler);
43
+ return () => {
44
+ this.exitListeners.delete(handler);
45
+ };
46
+ }
47
+ onError(handler) {
48
+ this.errorListeners.add(handler);
49
+ return () => {
50
+ this.errorListeners.delete(handler);
51
+ };
52
+ }
53
+ stderrText() {
54
+ return Buffer.concat(this.stderrChunks).toString("utf8").trim();
55
+ }
56
+ currentExitError() {
57
+ if (this.child.exitCode === null && this.child.signalCode === null) {
58
+ return null;
59
+ }
60
+ return new SidecarProcessExited({
61
+ exitCode: this.child.exitCode,
62
+ signal: this.child.signalCode,
63
+ stderr: this.stderrText(),
64
+ });
65
+ }
66
+ waitForExit(timeoutMs) {
67
+ return new Promise((resolve) => {
68
+ let timer = null;
69
+ const cleanup = () => {
70
+ this.child.off("exit", onExit);
71
+ this.child.off("close", onClose);
72
+ if (timer !== null) {
73
+ clearTimeout(timer);
74
+ timer = null;
75
+ }
76
+ };
77
+ const onExit = (code) => {
78
+ cleanup();
79
+ resolve(code);
80
+ };
81
+ const onClose = (code) => {
82
+ cleanup();
83
+ resolve(code);
84
+ };
85
+ if (this.child.exitCode !== null || this.child.signalCode !== null) {
86
+ resolve(this.child.exitCode);
87
+ return;
88
+ }
89
+ this.child.on("exit", onExit);
90
+ this.child.on("close", onClose);
91
+ timer = setTimeout(() => {
92
+ cleanup();
93
+ resolve(null);
94
+ }, timeoutMs);
95
+ });
96
+ }
97
+ }
@@ -0,0 +1,65 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ import { type LiveSidecarEventSelector } from "./event-buffer.js";
3
+ import type { FrameTransport } from "./frame-stream.js";
4
+ import { type LiveEventFrame, type LiveProtocolFrame, type LiveResponseFrame, type LiveSidecarRequestFrame, type LiveSidecarRequestHandler, type ProtocolFramePayloadCodec } from "./protocol-frames.js";
5
+ import type { LiveOwnershipScope } from "./ownership.js";
6
+ import type { LiveRequestPayload } from "./request-payloads.js";
7
+ export interface SidecarProtocolClientOptions {
8
+ frameTransport?: FrameTransport<LiveResponseFrame | LiveEventFrame | LiveSidecarRequestFrame, LiveProtocolFrame>;
9
+ stdin?: Writable;
10
+ stdout?: Readable;
11
+ eventBufferCapacity: number;
12
+ payloadCodec?: ProtocolFramePayloadCodec;
13
+ stderrText?: () => string;
14
+ frameError?: (error: Error) => Error;
15
+ streamEndedError?: () => Error;
16
+ /** Override the silence watchdog window. Tests only; production uses the default. */
17
+ silenceTimeoutMs?: number;
18
+ /**
19
+ * Runs when the silence watchdog fires, before pending work is rejected.
20
+ * The stdio layer uses it to SIGKILL the sidecar child.
21
+ */
22
+ onSilenceExpired?: () => void;
23
+ }
24
+ export declare class SidecarProtocolClient {
25
+ private readonly eventBuffer;
26
+ private readonly eventListeners;
27
+ private readonly silenceTimeoutMs;
28
+ private silenceTimer;
29
+ private lastInboundAtMs;
30
+ private readonly payloadCodec;
31
+ private readonly stderrText;
32
+ private readonly hostFrameFactory;
33
+ private readonly frameTransport;
34
+ private closedError;
35
+ private readonly eventWaiters;
36
+ private sidecarRequestHandler;
37
+ constructor(options: SidecarProtocolClientOptions);
38
+ /**
39
+ * Arm the silence watchdog: ANY inbound frame resets the clock (see the
40
+ * `onFrameActivity` tap above), and the sidecar heartbeats every 10s even
41
+ * while busy, so sustained silence for the full window means the process
42
+ * is dead or wedged — not slow. The check interval is unref'd so an idle
43
+ * host process can still exit naturally.
44
+ */
45
+ private startSilenceWatchdog;
46
+ private stopSilenceWatchdog;
47
+ setSidecarRequestHandler(handler: LiveSidecarRequestHandler | null): void;
48
+ onEvent(handler: (event: LiveEventFrame) => void): () => void;
49
+ sendRequest(input: {
50
+ ownership: LiveOwnershipScope;
51
+ payload: LiveRequestPayload;
52
+ }): Promise<LiveResponseFrame>;
53
+ waitForEvent(matcher: LiveSidecarEventSelector | ((event: LiveEventFrame) => boolean), timeoutMs?: number, options?: {
54
+ signal?: AbortSignal;
55
+ }): Promise<LiveEventFrame>;
56
+ failPermanently(error: Error, options?: {
57
+ replaceExisting?: (current: Error, next: Error) => boolean;
58
+ }): void;
59
+ dispose(): void;
60
+ private writeFrame;
61
+ private dispatchSidecarRequest;
62
+ private dispatchEvent;
63
+ private bufferEvent;
64
+ private rejectPending;
65
+ }
@@ -0,0 +1,242 @@
1
+ import { SidecarEventBuffer, SidecarEventBufferOverflow, normalizeSidecarEventMatcher, sidecarEventWaitAbortError, } from "./event-buffer.js";
2
+ import { FrameRpcTransport } from "./frame-rpc.js";
3
+ import { HostProtocolFrameFactory, classifySidecarWrittenProtocolFrame, decodeProtocolFramePayload, encodeProtocolFramePayload, resolveSidecarRequestFramePayload, } from "./protocol-frames.js";
4
+ import { SidecarSilenceTimeout } from "./sidecar-errors.js";
5
+ /**
6
+ * How long the host tolerates TOTAL inbound silence (no responses, events,
7
+ * sidecar requests, or heartbeats) before declaring the sidecar dead. The
8
+ * sidecar heartbeats every 10s from a dedicated thread, so this allows two
9
+ * missed beats plus margin; it bounds "sidecar is dead or wedged", never "this
10
+ * request is slow" — individual requests have no deadline of their own.
11
+ */
12
+ const DEFAULT_SIDECAR_SILENCE_TIMEOUT_MS = 30_000;
13
+ export class SidecarProtocolClient {
14
+ eventBuffer;
15
+ eventListeners = new Set();
16
+ silenceTimeoutMs;
17
+ silenceTimer = null;
18
+ lastInboundAtMs = 0;
19
+ payloadCodec;
20
+ stderrText;
21
+ hostFrameFactory = new HostProtocolFrameFactory();
22
+ frameTransport;
23
+ closedError = null;
24
+ eventWaiters = new Set();
25
+ sidecarRequestHandler = null;
26
+ constructor(options) {
27
+ this.silenceTimeoutMs =
28
+ options.silenceTimeoutMs ?? DEFAULT_SIDECAR_SILENCE_TIMEOUT_MS;
29
+ this.eventBuffer = new SidecarEventBuffer(options.eventBufferCapacity);
30
+ this.payloadCodec = options.payloadCodec ?? "bare";
31
+ this.stderrText = options.stderrText ?? (() => "");
32
+ this.frameTransport = new FrameRpcTransport({
33
+ frameTransport: options.frameTransport,
34
+ stdin: options.stdin,
35
+ stdout: options.stdout,
36
+ encodeFrame: (frame) => encodeProtocolFramePayload(frame, this.payloadCodec),
37
+ decodeFrame: (payload) => decodeProtocolFramePayload(payload, this.payloadCodec),
38
+ classifyFrame: classifySidecarWrittenProtocolFrame,
39
+ });
40
+ this.frameTransport.onEvent((event) => {
41
+ this.dispatchEvent(event);
42
+ });
43
+ this.frameTransport.onSidecarRequest((request) => {
44
+ void this.dispatchSidecarRequest(request);
45
+ });
46
+ this.frameTransport.onError((error) => {
47
+ this.failPermanently(options.frameError?.(error) ?? error);
48
+ });
49
+ this.frameTransport.onEnd(() => {
50
+ this.failPermanently(options.streamEndedError?.() ??
51
+ new Error("sidecar protocol stream ended"));
52
+ });
53
+ this.frameTransport.onFrameActivity(() => {
54
+ this.lastInboundAtMs = performance.now();
55
+ });
56
+ this.startSilenceWatchdog(options.onSilenceExpired);
57
+ }
58
+ /**
59
+ * Arm the silence watchdog: ANY inbound frame resets the clock (see the
60
+ * `onFrameActivity` tap above), and the sidecar heartbeats every 10s even
61
+ * while busy, so sustained silence for the full window means the process
62
+ * is dead or wedged — not slow. The check interval is unref'd so an idle
63
+ * host process can still exit naturally.
64
+ */
65
+ startSilenceWatchdog(onExpired) {
66
+ this.lastInboundAtMs = performance.now();
67
+ const checkIntervalMs = Math.max(Math.min(this.silenceTimeoutMs / 4, 1_000), 10);
68
+ this.silenceTimer = setInterval(() => {
69
+ const silenceMs = performance.now() - this.lastInboundAtMs;
70
+ if (silenceMs < this.silenceTimeoutMs) {
71
+ return;
72
+ }
73
+ this.stopSilenceWatchdog();
74
+ const error = new SidecarSilenceTimeout({
75
+ silenceMs,
76
+ stderr: this.stderrText(),
77
+ });
78
+ try {
79
+ onExpired?.();
80
+ }
81
+ finally {
82
+ this.failPermanently(error);
83
+ }
84
+ }, checkIntervalMs);
85
+ this.silenceTimer.unref?.();
86
+ }
87
+ stopSilenceWatchdog() {
88
+ if (this.silenceTimer !== null) {
89
+ clearInterval(this.silenceTimer);
90
+ this.silenceTimer = null;
91
+ }
92
+ }
93
+ setSidecarRequestHandler(handler) {
94
+ this.sidecarRequestHandler = handler;
95
+ }
96
+ onEvent(handler) {
97
+ this.eventListeners.add(handler);
98
+ return () => {
99
+ this.eventListeners.delete(handler);
100
+ };
101
+ }
102
+ async sendRequest(input) {
103
+ if (this.closedError) {
104
+ throw this.closedError;
105
+ }
106
+ const request = this.hostFrameFactory.createRequestFrame(input);
107
+ // No per-request deadline: only the caller knows whether an operation is
108
+ // legitimately long (a whole agent turn is one request). A dead or
109
+ // wedged sidecar rejects this via the silence watchdog instead.
110
+ const response = await this.frameTransport.sendFrame(request.request_id, request);
111
+ if (response.payload.type === "rejected") {
112
+ throw new Error(`sidecar rejected request ${request.request_id}: ${response.payload.code}: ${response.payload.message}`);
113
+ }
114
+ return response;
115
+ }
116
+ async waitForEvent(matcher, timeoutMs, options) {
117
+ if (this.closedError instanceof SidecarEventBufferOverflow) {
118
+ throw this.closedError;
119
+ }
120
+ const normalizedMatcher = normalizeSidecarEventMatcher(matcher);
121
+ const bufferedEvent = this.eventBuffer.take(normalizedMatcher);
122
+ if (bufferedEvent) {
123
+ return bufferedEvent;
124
+ }
125
+ if (this.closedError) {
126
+ throw this.closedError;
127
+ }
128
+ if (options?.signal?.aborted) {
129
+ throw sidecarEventWaitAbortError(options.signal.reason);
130
+ }
131
+ return await new Promise((resolve, reject) => {
132
+ let abortListener = null;
133
+ const waiter = {
134
+ matches: normalizedMatcher.matches,
135
+ resolve: (event) => {
136
+ if (waiter.timer !== null) {
137
+ clearTimeout(waiter.timer);
138
+ }
139
+ if (abortListener) {
140
+ options?.signal?.removeEventListener("abort", abortListener);
141
+ abortListener = null;
142
+ }
143
+ this.eventWaiters.delete(waiter);
144
+ resolve(event);
145
+ },
146
+ reject: (error) => {
147
+ if (waiter.timer !== null) {
148
+ clearTimeout(waiter.timer);
149
+ }
150
+ if (abortListener) {
151
+ options?.signal?.removeEventListener("abort", abortListener);
152
+ abortListener = null;
153
+ }
154
+ this.eventWaiters.delete(waiter);
155
+ reject(error);
156
+ },
157
+ timer: timeoutMs === undefined
158
+ ? null
159
+ : setTimeout(() => {
160
+ this.eventWaiters.delete(waiter);
161
+ reject(new Error(`timed out waiting for sidecar event\nstderr:\n${this.stderrText()}`));
162
+ }, timeoutMs),
163
+ };
164
+ if (options?.signal) {
165
+ abortListener = () => {
166
+ waiter.reject(sidecarEventWaitAbortError(options.signal?.reason));
167
+ };
168
+ options.signal.addEventListener("abort", abortListener, { once: true });
169
+ }
170
+ this.eventWaiters.add(waiter);
171
+ });
172
+ }
173
+ failPermanently(error, options) {
174
+ if (this.closedError) {
175
+ if (!options?.replaceExisting?.(this.closedError, error)) {
176
+ return;
177
+ }
178
+ }
179
+ this.closedError = error;
180
+ this.stopSilenceWatchdog();
181
+ this.rejectPending(error);
182
+ }
183
+ dispose() {
184
+ this.stopSilenceWatchdog();
185
+ this.frameTransport.dispose();
186
+ }
187
+ async writeFrame(frame) {
188
+ await this.frameTransport.writeFrame(frame);
189
+ }
190
+ async dispatchSidecarRequest(request) {
191
+ const payload = await resolveSidecarRequestFramePayload(request, this.sidecarRequestHandler);
192
+ try {
193
+ await this.writeFrame(this.hostFrameFactory.createSidecarResponseFrame({
194
+ request,
195
+ payload,
196
+ }));
197
+ }
198
+ catch (error) {
199
+ const normalized = error instanceof Error ? error : new Error(String(error));
200
+ this.failPermanently(normalized);
201
+ }
202
+ }
203
+ dispatchEvent(event) {
204
+ // Transport-level liveness beats from the sidecar. Their arrival already
205
+ // reset the silence watchdog at the frame layer; they carry no meaning
206
+ // for consumers and must never reach the bounded event buffer, where a
207
+ // long-idle VM would accumulate one every 10s until overflow.
208
+ if (event.payload.type === "structured" &&
209
+ event.payload.name === "heartbeat") {
210
+ return;
211
+ }
212
+ for (const listener of this.eventListeners) {
213
+ try {
214
+ listener(event);
215
+ }
216
+ catch {
217
+ // Event listeners are best-effort observers and must not break framing.
218
+ }
219
+ }
220
+ for (const waiter of this.eventWaiters) {
221
+ if (!waiter.matches(event)) {
222
+ continue;
223
+ }
224
+ waiter.resolve(event);
225
+ return;
226
+ }
227
+ this.bufferEvent(event);
228
+ }
229
+ bufferEvent(event) {
230
+ const overflow = this.eventBuffer.buffer(event);
231
+ if (overflow) {
232
+ this.failPermanently(overflow);
233
+ }
234
+ }
235
+ rejectPending(error) {
236
+ this.frameTransport.rejectAll(error);
237
+ for (const waiter of this.eventWaiters) {
238
+ waiter.reject(error);
239
+ }
240
+ this.eventWaiters.clear();
241
+ }
242
+ }