@anhlp/alp-protocol 0.0.0-stage → 0.0.1-beta.4

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 (47) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +7 -0
  3. package/README.md +16 -2
  4. package/conformance/cases.json +951 -0
  5. package/conformance/run.d.mts +32 -0
  6. package/conformance/run.mjs +403 -0
  7. package/dist/connection.d.ts +13 -0
  8. package/dist/connection.js +0 -0
  9. package/dist/contract-v1.d.ts +8783 -0
  10. package/dist/contract-v1.js +5 -0
  11. package/dist/contract.d.ts +25 -0
  12. package/dist/contract.js +28 -0
  13. package/dist/dto.d.ts +104 -0
  14. package/dist/dto.js +0 -0
  15. package/dist/events.d.ts +354 -0
  16. package/dist/events.js +0 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.js +2 -0
  19. package/dist/public.d.ts +1 -0
  20. package/dist/public.js +2 -0
  21. package/dist/rpc.d.ts +1210 -0
  22. package/dist/rpc.js +0 -0
  23. package/dist/schedule.d.ts +43 -0
  24. package/dist/schedule.js +0 -0
  25. package/dist/session.d.ts +285 -0
  26. package/dist/session.js +0 -0
  27. package/dist/validate.d.ts +12 -0
  28. package/dist/validate.js +151 -0
  29. package/dist/wire.d.ts +192 -0
  30. package/dist/wire.js +137 -0
  31. package/fixtures/ipc-v1.jsonl +4 -0
  32. package/fixtures/v1/cases.json +7166 -0
  33. package/package.json +127 -4
  34. package/schema/ipc-v1.json +3271 -0
  35. package/schema/methods-v1.json +8678 -0
  36. package/src/connection.ts +13 -0
  37. package/src/contract-v1.js +2 -0
  38. package/src/contract.js +44 -0
  39. package/src/dto.ts +78 -0
  40. package/src/events.ts +207 -0
  41. package/src/index.ts +7 -0
  42. package/src/public.js +2 -0
  43. package/src/rpc.ts +272 -0
  44. package/src/schedule.ts +43 -0
  45. package/src/session.ts +213 -0
  46. package/src/validate.js +115 -0
  47. package/src/wire.js +213 -0
package/dist/wire.d.ts ADDED
@@ -0,0 +1,192 @@
1
+ /**
2
+ * The decoded bytes one attachment.chunk may carry over frames of at most `maxFrame` bytes:
3
+ * `maxChunkBytes`, lowered so its base64 and ATTACHMENT_CHUNK_OVERHEAD fit. At least 1 byte.
4
+ * @param {number} [maxFrame] @param {number} [maxChunkBytes]
5
+ */
6
+ export function attachmentChunkBytes(maxFrame?: number, maxChunkBytes?: number): number;
7
+ /**
8
+ * Whether a path relative to a workspace names something workspace.files.list and
9
+ * workspace.files.read never show, whatever the grant: a segment that is a blocked directory, or
10
+ * any segment (file or directory) named as a secret is (BLOCKED_* above). Compared without case,
11
+ * as macOS and Windows file systems name files. alpd and the gateway both check it; alpd also
12
+ * refuses a file whose content holds a private key, whatever its name.
13
+ * @param {string} relative a path relative to the workspace, '/'-separated
14
+ */
15
+ export function blockedFilePath(relative: string): boolean;
16
+ /** UTF-8 socket writers use one newline-terminated JSON value per frame. @param {unknown} value */
17
+ export function encodeFrame(value: unknown): string;
18
+ /** Parse one complete frame. Buffering and failure handling belong to the socket owner. @param {string} line @returns {unknown} */
19
+ export function decodeFrame(line: string): unknown;
20
+ /** One argument for a POSIX shell: as it is when nothing in it is special, else in single quotes. @param {string} arg */
21
+ export function quotePosix(arg: string): string;
22
+ /**
23
+ * One argument for cmd.exe: as it is when nothing in it is special; else in double quotes for the C
24
+ * runtime's split (backslashes before the closing quote doubled), with every cmd.exe metacharacter,
25
+ * the quotes too, escaped with ^ for the interactive parse; the shim's second parse then sees one
26
+ * quoted argument, inside which `&|<>()^` are literal. `"`, `%` and `!` are refused (see above).
27
+ * @param {string} arg
28
+ */
29
+ export function quoteWindows(arg: string): string;
30
+ /** argv as one command line. @param {string[]} argv @param {'posix' | 'windows'} platform */
31
+ export function commandLine(argv: string[], platform: "posix" | "windows"): string;
32
+ /**
33
+ * session.resumeCommand's answer for a session: `alp attach <sessionId>`, which follows the session in
34
+ * a terminal (a closed one says how to send it a message). ALP keeps the orchestration, so it is never
35
+ * a native runtime's own resume command.
36
+ * @param {string} sessionId @param {'posix' | 'windows'} platform the daemon host's
37
+ */
38
+ export function resumeCommandOf(sessionId: string, platform: "posix" | "windows"): {
39
+ argv: string[];
40
+ posix: string;
41
+ windows: string;
42
+ display: string;
43
+ platform: "posix" | "windows";
44
+ };
45
+ /** Wire protocol of alpd (plans/reference/ALPD.md §3). Bump only for incompatible changes. */
46
+ export const PROTOCOL_VERSION: 1;
47
+ export const MAX_FRAME: number;
48
+ export class AlpRpcError extends Error {
49
+ constructor(error: any);
50
+ code: any;
51
+ data: any;
52
+ }
53
+ export const ERROR: Readonly<{
54
+ failed: 1000;
55
+ notFound: 1001;
56
+ forbidden: 1003;
57
+ conflict: 1004;
58
+ unavailable: 1005;
59
+ protocol: 1006;
60
+ }>;
61
+ /** The JSON-RPC 2.0 codes alpd answers with (docs/protocol/alp-json-rpc-v1.md §5). */
62
+ export const RPC_ERROR: Readonly<{
63
+ parseError: -32700;
64
+ invalidRequest: -32600;
65
+ methodNotFound: -32601;
66
+ invalidParams: -32602;
67
+ }>;
68
+ /**
69
+ * Features a daemon lists in its daemon.hello result. A client uses a feature only when listed;
70
+ * a daemon that lists none (one older than this list) has none of them. Unknown names are ignored.
71
+ */
72
+ export const CAPABILITY: Readonly<{
73
+ sessionInterruptTurn: "session.interrupt.turn";
74
+ attachAfter: "attach.after";
75
+ sessionHistory: "session.history";
76
+ workspaceRegistry: "workspace.registry";
77
+ workspaceWorktree: "workspace.worktree";
78
+ workspaceGit: "workspace.git";
79
+ promptReceipts: "prompt.receipts";
80
+ extensionMethods: "extension.methods";
81
+ team: "team";
82
+ task: "task";
83
+ library: "library";
84
+ libraryProjectWrite: "library.project.write";
85
+ settingsLanguage: "settings.language";
86
+ sessionListPage: "session.list.page";
87
+ questionWatch: "question.watch";
88
+ questionWatchTurns: "question.watch.turns";
89
+ sessionAttention: "session.attention";
90
+ workspaceFiles: "workspace.files";
91
+ workspaceFilesRead: "workspace.files.read";
92
+ promptImages: "prompt.images";
93
+ schedule: "schedule";
94
+ attachments: "attachments";
95
+ sessionFork: "session.fork";
96
+ sessionRewind: "session.rewind";
97
+ sessionReload: "session.reload";
98
+ sessionResumeCommand: "session.resumeCommand";
99
+ sessionConfigureLive: "session.configure.live";
100
+ terminal: "terminal";
101
+ }>;
102
+ /**
103
+ * What alpd takes as images in one session.prompt (capability `prompt.images`): inline base64 of
104
+ * these types only, never a path or URL; at most `maxCount` of them, each at most `maxBytes`
105
+ * decoded, and `maxTotalBytes` together, so their base64 fits one MAX_FRAME. A gateway may allow
106
+ * less (docs/alp/remote-gateway.md).
107
+ */
108
+ export const PROMPT_IMAGES: Readonly<{
109
+ mimeTypes: readonly string[];
110
+ maxCount: 8;
111
+ maxBytes: number;
112
+ maxTotalBytes: number;
113
+ }>;
114
+ export function base64Bytes(data: string): number;
115
+ export const RPC_METHODS: readonly string[];
116
+ /**
117
+ * Limits of workspace.files.list and workspace.files.read (capability `workspace.files.read`,
118
+ * docs/protocol/alp-json-rpc-v1.md §12.4): a file of at most `maxReadBytes`, a listing of at most
119
+ * `maxListEntries` entries, a relative path of at most `maxPathLength` characters.
120
+ */
121
+ export const WORKSPACE_FILES: Readonly<{
122
+ maxReadBytes: number;
123
+ maxListEntries: 1000;
124
+ maxPathLength: 4096;
125
+ }>;
126
+ /**
127
+ * Limits of workspace.git.status and workspace.git.diff (capability `workspace.git`,
128
+ * docs/protocol/alp-json-rpc-v1.md §12.5): one file's patch at most `maxFileBytes`, one page's
129
+ * patches at most `maxTotalBytes` (then `nextCursor`), at most `maxUntracked` untracked files with
130
+ * content, `maxStatusFiles` status rows, a base branch name of at most `maxRefLength` characters.
131
+ * A gateway may lower them; never raise them.
132
+ */
133
+ export const WORKSPACE_GIT: Readonly<{
134
+ maxFileBytes: number;
135
+ maxTotalBytes: number;
136
+ maxUntracked: 200;
137
+ maxStatusFiles: 1000;
138
+ maxRefLength: 255;
139
+ }>;
140
+ /**
141
+ * Limits of non-image attachments (capability `attachments`, docs/protocol/alp-json-rpc-v1.md
142
+ * §12.6): an object of at most `maxObjectBytes`; a prompt of at most `maxPerPrompt` attachments and
143
+ * `maxPromptBytes` together; chunks of at most `maxChunkBytes` decoded (lowered further by
144
+ * attachmentChunkBytes to fit a frame); `maxActiveUploads` incomplete uploads and
145
+ * `maxPrincipalBytes` stored per principal, `maxDaemonBytes` per daemon, retained references
146
+ * included. An incomplete upload expires after `uploadTimeoutMs`; an unreferenced committed object
147
+ * is purged after `unreferencedRetentionMs`, one whose references were all released after
148
+ * `releasedGraceMs`. A display name is at most `maxNameBytes` UTF-8 bytes. An operator or a gateway
149
+ * may lower them, never raise them; daemon.hello advertises the effective ones (`attachmentLimits`).
150
+ */
151
+ export const ATTACHMENTS: Readonly<{
152
+ maxObjectBytes: number;
153
+ maxPerPrompt: 8;
154
+ maxPromptBytes: number;
155
+ maxChunkBytes: number;
156
+ maxActiveUploads: 2;
157
+ maxPrincipalBytes: number;
158
+ maxDaemonBytes: number;
159
+ uploadTimeoutMs: number;
160
+ unreferencedRetentionMs: number;
161
+ releasedGraceMs: number;
162
+ maxNameBytes: 255;
163
+ }>;
164
+ /**
165
+ * Limits of terminals (capability `terminal`, docs/protocol/alp-json-rpc-v1.md §12.10): at most
166
+ * `maxPerPrincipal` running terminals per principal and `maxPerDaemon` per daemon; a
167
+ * terminal.input of at most `maxInputBytes` UTF-8 bytes; output events of at most
168
+ * `maxOutputFrameBytes` bytes of data; `replayBytes` of output kept per terminal for attach; a
169
+ * terminal ends after `idleMs` without input or output, after `maxLifetimeMs` in any case, and
170
+ * `reconnectMs` after its stream detached unless attached again. Sizes are `minCols`..`maxCols`
171
+ * columns and `minRows`..`maxRows` rows. An operator or a gateway may lower the first eight, never
172
+ * raise them; daemon.hello advertises the effective ones (`terminalLimits`).
173
+ */
174
+ export const TERMINAL: Readonly<{
175
+ maxPerPrincipal: 4;
176
+ maxPerDaemon: 16;
177
+ maxInputBytes: number;
178
+ maxOutputFrameBytes: number;
179
+ replayBytes: number;
180
+ idleMs: number;
181
+ maxLifetimeMs: number;
182
+ reconnectMs: number;
183
+ minCols: 2;
184
+ maxCols: 500;
185
+ minRows: 1;
186
+ maxRows: 200;
187
+ }>;
188
+ /** The TERMINAL limits a daemon.hello `terminalLimits` carries (the size bounds are fixed). */
189
+ export const TERMINAL_LIMIT_KEYS: readonly string[];
190
+ export function isTerminalId(id: unknown): boolean;
191
+ /** Room an attachment.chunk request needs besides its base64 data. */
192
+ export const ATTACHMENT_CHUNK_OVERHEAD: number;
package/dist/wire.js ADDED
@@ -0,0 +1,137 @@
1
+ // packages/protocol/src/wire.js
2
+ var PROTOCOL_VERSION = 1;
3
+ var MAX_FRAME = 16 * 1024 * 1024;
4
+ var AlpRpcError = class extends Error {
5
+ constructor(error) {
6
+ super(error?.message ?? "ALP daemon request failed");
7
+ this.code = error?.code;
8
+ this.data = error?.data;
9
+ }
10
+ };
11
+ var ERROR = Object.freeze({ failed: 1e3, notFound: 1001, forbidden: 1003, conflict: 1004, unavailable: 1005, protocol: 1006 });
12
+ var RPC_ERROR = Object.freeze({ parseError: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602 });
13
+ var CAPABILITY = Object.freeze({ sessionInterruptTurn: "session.interrupt.turn", attachAfter: "attach.after", sessionHistory: "session.history", workspaceRegistry: "workspace.registry", workspaceWorktree: "workspace.worktree", workspaceGit: "workspace.git", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", libraryProjectWrite: "library.project.write", settingsLanguage: "settings.language", sessionListPage: "session.list.page", questionWatch: "question.watch", questionWatchTurns: "question.watch.turns", sessionAttention: "session.attention", workspaceFiles: "workspace.files", workspaceFilesRead: "workspace.files.read", promptImages: "prompt.images", schedule: "schedule", attachments: "attachments", sessionFork: "session.fork", sessionRewind: "session.rewind", sessionReload: "session.reload", sessionResumeCommand: "session.resumeCommand", sessionConfigureLive: "session.configure.live", terminal: "terminal" });
14
+ var PROMPT_IMAGES = Object.freeze({ mimeTypes: Object.freeze(["image/png", "image/jpeg", "image/gif", "image/webp"]), maxCount: 8, maxBytes: 5 * 1024 * 1024, maxTotalBytes: 11 * 1024 * 1024 });
15
+ var base64Bytes = (data) => Math.floor(data.length * 3 / 4) - (data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0);
16
+ var RPC_METHODS = Object.freeze(["assignment.recall", "attachment.abort", "attachment.begin", "attachment.chunk", "attachment.commit", "attachment.delete", "attachment.get", "board.list", "catalog.get", "daemon.hello", "daemon.pause", "daemon.pauses", "daemon.resume", "daemon.shutdown", "daemon.status", "daemon.usage", "extension.register", "extension.unregister", "library.delete", "library.duplicate", "library.get", "library.list", "library.models", "library.rename", "library.save", "library.skills.set", "project.list", "project.register", "project.remove", "question.answer", "question.list", "question.watch", "registry.owner", "registry.worktree", "schedule.cancel", "schedule.create", "schedule.get", "schedule.list", "session.archive", "session.attach", "session.attention.clear", "session.close", "session.configure", "session.configure.preview", "session.create", "session.fork", "session.get", "session.history", "session.interrupt", "session.list", "session.log", "session.message", "session.preview", "session.prompt", "session.release", "session.reload", "session.rename", "session.resumeCommand", "session.rewind", "session.status", "settings.language.get", "settings.language.set", "task.create", "task.gate.resolve", "task.list", "task.update", "team.list", "terminal.attach", "terminal.close", "terminal.create", "terminal.input", "terminal.list", "terminal.resize", "terminal.revoke", "workspace.files.list", "workspace.files.read", "workspace.files.search", "workspace.get", "workspace.git.diff", "workspace.git.status", "workspace.list", "workspace.update", "workspace.worktree.create", "workspace.worktree.options", "workspace.worktree.remove"]);
17
+ var WORKSPACE_FILES = Object.freeze({ maxReadBytes: 1024 * 1024, maxListEntries: 1e3, maxPathLength: 4096 });
18
+ var WORKSPACE_GIT = Object.freeze({ maxFileBytes: 1024 * 1024, maxTotalBytes: 2 * 1024 * 1024, maxUntracked: 200, maxStatusFiles: 1e3, maxRefLength: 255 });
19
+ var ATTACHMENTS = Object.freeze({
20
+ maxObjectBytes: 20 * 1024 * 1024,
21
+ maxPerPrompt: 8,
22
+ maxPromptBytes: 40 * 1024 * 1024,
23
+ maxChunkBytes: 256 * 1024,
24
+ maxActiveUploads: 2,
25
+ maxPrincipalBytes: 200 * 1024 * 1024,
26
+ maxDaemonBytes: 1024 * 1024 * 1024,
27
+ uploadTimeoutMs: 60 * 60 * 1e3,
28
+ unreferencedRetentionMs: 24 * 60 * 60 * 1e3,
29
+ releasedGraceMs: 7 * 24 * 60 * 60 * 1e3,
30
+ maxNameBytes: 255
31
+ });
32
+ var TERMINAL = Object.freeze({
33
+ maxPerPrincipal: 4,
34
+ maxPerDaemon: 16,
35
+ maxInputBytes: 64 * 1024,
36
+ maxOutputFrameBytes: 64 * 1024,
37
+ replayBytes: 1024 * 1024,
38
+ idleMs: 30 * 60 * 1e3,
39
+ maxLifetimeMs: 8 * 60 * 60 * 1e3,
40
+ reconnectMs: 60 * 1e3,
41
+ minCols: 2,
42
+ maxCols: 500,
43
+ minRows: 1,
44
+ maxRows: 200
45
+ });
46
+ var TERMINAL_LIMIT_KEYS = Object.freeze(["maxPerPrincipal", "maxPerDaemon", "maxInputBytes", "maxOutputFrameBytes", "replayBytes", "idleMs", "maxLifetimeMs", "reconnectMs"]);
47
+ var isTerminalId = (id) => typeof id === "string" && /^term_[0-9a-f]{32}$/.test(id);
48
+ var ATTACHMENT_CHUNK_OVERHEAD = 64 * 1024;
49
+ function attachmentChunkBytes(maxFrame = MAX_FRAME, maxChunkBytes = ATTACHMENTS.maxChunkBytes) {
50
+ const fits = Math.floor((maxFrame - ATTACHMENT_CHUNK_OVERHEAD) * 3 / 4);
51
+ return Math.max(1, Math.min(maxChunkBytes, fits));
52
+ }
53
+ var BLOCKED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".alp", ".ssh", ".gnupg", ".aws", ".azure", ".kube", ".docker", "gcloud"]);
54
+ var BLOCKED_NAMES = /* @__PURE__ */ new Set([".netrc", "_netrc", ".npmrc", ".yarnrc.yml", ".pypirc", ".pgpass", ".htpasswd", ".git-credentials", ".dockercfg", ".boto", ".s3cfg", ".mcp.json", "auth.json", "kubeconfig", "settings.local.json", ".vault-token", "application_default_credentials.json", "token.json"]);
55
+ var BLOCKED_PAIRS = /* @__PURE__ */ new Set(["gh/hosts.yml"]);
56
+ var BLOCKED_STEMS = /* @__PURE__ */ new Set(["credential", "credentials", "secret", "secrets"]);
57
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set(["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs", "py", "go", "rs", "java", "kt", "kts", "rb", "php", "cs", "swift", "c", "cc", "cpp", "h", "hpp", "m", "mm", "scala", "dart", "vue", "svelte"]);
58
+ var BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set(["pem", "key", "p12", "pfx", "p8", "jks", "keystore", "ppk", "der", "gpg", "pgp", "asc", "kdbx", "ovpn", "tfstate", "tfvars"]);
59
+ var BLOCKED_PATTERN = /^(?:\.env|id_|service[-_]?account)|_history$|\.tfstate\.backup$/;
60
+ function blockedFilePath(relative) {
61
+ let previous = "";
62
+ for (const segment of String(relative).toLowerCase().split(/[\\/]/)) {
63
+ if (!segment) continue;
64
+ if (BLOCKED_PAIRS.has(`${previous}/${segment}`)) return true;
65
+ previous = segment;
66
+ if (BLOCKED_DIRECTORIES.has(segment) || BLOCKED_NAMES.has(segment) || BLOCKED_PATTERN.test(segment)) return true;
67
+ const dot = segment.lastIndexOf(".");
68
+ const extension = dot >= 0 ? segment.slice(dot + 1) : "";
69
+ if (BLOCKED_EXTENSIONS.has(extension)) return true;
70
+ const parts = segment.replace(/^\.+/, "").split(".");
71
+ if (parts.length > 1) parts.pop();
72
+ if (parts.some((part) => BLOCKED_STEMS.has(part)) && !SOURCE_EXTENSIONS.has(extension)) return true;
73
+ }
74
+ return false;
75
+ }
76
+ function encodeFrame(value) {
77
+ return `${JSON.stringify(value)}
78
+ `;
79
+ }
80
+ function decodeFrame(line) {
81
+ return JSON.parse(line);
82
+ }
83
+ var POSIX_SAFE = /^[A-Za-z0-9_@+:,./-]+$/;
84
+ var WINDOWS_SAFE = /^[A-Za-z0-9_@+:./\\-]+$/;
85
+ var assertArgument = (arg) => {
86
+ if (typeof arg !== "string") throw new TypeError("A command argument is a string");
87
+ if (/[\u0000-\u001f\u007f]/.test(arg)) throw new TypeError("A command argument has no control characters");
88
+ return arg;
89
+ };
90
+ function quotePosix(arg) {
91
+ const text = assertArgument(arg);
92
+ return text && POSIX_SAFE.test(text) ? text : `'${text.replace(/'/g, `'\\''`)}'`;
93
+ }
94
+ function quoteWindows(arg) {
95
+ const text = assertArgument(arg);
96
+ if (text && WINDOWS_SAFE.test(text)) return text;
97
+ if (/["%!]/.test(text)) throw new TypeError('A cmd.exe command argument has no ", % or !');
98
+ const quoted = `"${text.replace(/(\\*)$/, "$1$1")}"`;
99
+ return quoted.replace(/([()\][^"`<>&|;, *?])/g, "^$1");
100
+ }
101
+ var SESSION_ID = /^[\w.:-]{1,128}$/;
102
+ function commandLine(argv, platform) {
103
+ return argv.map(platform === "windows" ? quoteWindows : quotePosix).join(" ");
104
+ }
105
+ function resumeCommandOf(sessionId, platform) {
106
+ if (typeof sessionId !== "string" || !SESSION_ID.test(sessionId)) throw new TypeError("A session id is [A-Za-z0-9_.:-], at most 128 characters");
107
+ const argv = ["alp", "attach", sessionId];
108
+ const posix = commandLine(argv, "posix");
109
+ const windows = commandLine(argv, "windows");
110
+ return { argv, posix, windows, display: platform === "windows" ? windows : posix, platform };
111
+ }
112
+ export {
113
+ ATTACHMENTS,
114
+ ATTACHMENT_CHUNK_OVERHEAD,
115
+ AlpRpcError,
116
+ CAPABILITY,
117
+ ERROR,
118
+ MAX_FRAME,
119
+ PROMPT_IMAGES,
120
+ PROTOCOL_VERSION,
121
+ RPC_ERROR,
122
+ RPC_METHODS,
123
+ TERMINAL,
124
+ TERMINAL_LIMIT_KEYS,
125
+ WORKSPACE_FILES,
126
+ WORKSPACE_GIT,
127
+ attachmentChunkBytes,
128
+ base64Bytes,
129
+ blockedFilePath,
130
+ commandLine,
131
+ decodeFrame,
132
+ encodeFrame,
133
+ isTerminalId,
134
+ quotePosix,
135
+ quoteWindows,
136
+ resumeCommandOf
137
+ };
@@ -0,0 +1,4 @@
1
+ {"jsonrpc":"2.0","id":1,"method":"daemon.hello","params":{"protocolVersion":1,"client":{"name":"fixture","version":"0.9.5"}}}
2
+ {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1}}
3
+ {"jsonrpc":"2.0","method":"event","params":{"sessionId":"ses_fixture","epoch":"fixture-epoch","seq":1,"ts":"2026-10-14T00:00:00.000Z","event":{"type":"session.ready"}}}
4
+ {"jsonrpc":"2.0","id":2,"error":{"code":1001,"message":"No session ses_fixture"}}