@camerontaylor/paseo-protocol 0.8.0-fork.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +12 -0
  2. package/dist/agent-attention-notification.d.ts +43 -0
  3. package/dist/agent-attention-notification.js +141 -0
  4. package/dist/agent-deep-link.d.ts +8 -0
  5. package/dist/agent-deep-link.js +49 -0
  6. package/dist/agent-labels.d.ts +10 -0
  7. package/dist/agent-labels.js +21 -0
  8. package/dist/agent-lifecycle.d.ts +3 -0
  9. package/dist/agent-lifecycle.js +8 -0
  10. package/dist/agent-state-bucket.d.ts +14 -0
  11. package/dist/agent-state-bucket.js +41 -0
  12. package/dist/agent-title-limits.d.ts +2 -0
  13. package/dist/agent-title-limits.js +2 -0
  14. package/dist/agent-types.d.ts +478 -0
  15. package/dist/agent-types.js +22 -0
  16. package/dist/binary-frames/demux.d.ts +11 -0
  17. package/dist/binary-frames/demux.js +23 -0
  18. package/dist/binary-frames/file-transfer.d.ts +51 -0
  19. package/dist/binary-frames/file-transfer.js +108 -0
  20. package/dist/binary-frames/index.d.ts +4 -0
  21. package/dist/binary-frames/index.js +4 -0
  22. package/dist/binary-frames/terminal.d.ts +35 -0
  23. package/dist/binary-frames/terminal.js +100 -0
  24. package/dist/branch-slug.d.ts +14 -0
  25. package/dist/branch-slug.js +49 -0
  26. package/dist/browser-automation/capabilities.d.ts +7 -0
  27. package/dist/browser-automation/capabilities.js +31 -0
  28. package/dist/browser-automation/rpc-schemas.d.ts +1112 -0
  29. package/dist/browser-automation/rpc-schemas.js +462 -0
  30. package/dist/chat/rpc-schemas.d.ts +158 -0
  31. package/dist/chat/rpc-schemas.js +104 -0
  32. package/dist/chat/types.d.ts +30 -0
  33. package/dist/chat/types.js +23 -0
  34. package/dist/check-traits.d.ts +11 -0
  35. package/dist/check-traits.js +10 -0
  36. package/dist/client-capabilities.d.ts +20 -0
  37. package/dist/client-capabilities.js +47 -0
  38. package/dist/connection-offer.d.ts +36 -0
  39. package/dist/connection-offer.js +53 -0
  40. package/dist/daemon-endpoints.d.ts +47 -0
  41. package/dist/daemon-endpoints.js +201 -0
  42. package/dist/error-utils.d.ts +11 -0
  43. package/dist/error-utils.js +27 -0
  44. package/dist/forge-manifest.d.ts +66 -0
  45. package/dist/forge-manifest.js +92 -0
  46. package/dist/generated/validation/ws-outbound.aot.d.ts +2 -0
  47. package/dist/generated/validation/ws-outbound.aot.js +98097 -0
  48. package/dist/git-remote.d.ts +33 -0
  49. package/dist/git-remote.js +94 -0
  50. package/dist/gitea-status.d.ts +10 -0
  51. package/dist/gitea-status.js +23 -0
  52. package/dist/gitlab-pipeline.d.ts +10 -0
  53. package/dist/gitlab-pipeline.js +17 -0
  54. package/dist/host-connection-schema.d.ts +11 -0
  55. package/dist/host-connection-schema.js +9 -0
  56. package/dist/literal-union.d.ts +2 -0
  57. package/dist/literal-union.js +2 -0
  58. package/dist/loop/rpc-schemas.d.ts +636 -0
  59. package/dist/loop/rpc-schemas.js +164 -0
  60. package/dist/messages.d.ts +60742 -0
  61. package/dist/messages.js +6152 -0
  62. package/dist/paseo-config-schema.d.ts +138 -0
  63. package/dist/paseo-config-schema.js +98 -0
  64. package/dist/paseo-tool-call-detail.d.ts +15 -0
  65. package/dist/paseo-tool-call-detail.js +305 -0
  66. package/dist/path-utils.d.ts +2 -0
  67. package/dist/path-utils.js +16 -0
  68. package/dist/plugin-requirements.d.ts +11 -0
  69. package/dist/plugin-requirements.js +26 -0
  70. package/dist/plugin-source-reference.d.ts +7 -0
  71. package/dist/plugin-source-reference.js +41 -0
  72. package/dist/provider-config.d.ts +139 -0
  73. package/dist/provider-config.js +115 -0
  74. package/dist/provider-icon-names.d.ts +5 -0
  75. package/dist/provider-icon-names.js +54 -0
  76. package/dist/provider-manifest.d.ts +37 -0
  77. package/dist/provider-manifest.js +261 -0
  78. package/dist/provider-snapshot-codec.d.ts +18 -0
  79. package/dist/provider-snapshot-codec.js +71 -0
  80. package/dist/schedule/cadence.d.ts +6 -0
  81. package/dist/schedule/cadence.js +28 -0
  82. package/dist/schedule/cron-expression.d.ts +13 -0
  83. package/dist/schedule/cron-expression.js +97 -0
  84. package/dist/schedule/rpc-schemas.d.ts +568 -0
  85. package/dist/schedule/rpc-schemas.js +155 -0
  86. package/dist/schedule/types.d.ts +201 -0
  87. package/dist/schedule/types.js +68 -0
  88. package/dist/search/text-match.d.ts +71 -0
  89. package/dist/search/text-match.js +291 -0
  90. package/dist/ssh-transport.d.ts +11 -0
  91. package/dist/ssh-transport.js +72 -0
  92. package/dist/terminal-activity.d.ts +21 -0
  93. package/dist/terminal-activity.js +25 -0
  94. package/dist/terminal-input-mode.d.ts +30 -0
  95. package/dist/terminal-input-mode.js +180 -0
  96. package/dist/terminal-key-input.d.ts +13 -0
  97. package/dist/terminal-key-input.js +210 -0
  98. package/dist/terminal-profiles.d.ts +41 -0
  99. package/dist/terminal-profiles.js +270 -0
  100. package/dist/terminal-snapshot.d.ts +3 -0
  101. package/dist/terminal-snapshot.js +184 -0
  102. package/dist/terminal-stream-protocol.d.ts +2 -0
  103. package/dist/terminal-stream-protocol.js +2 -0
  104. package/dist/terminal-subscription-key.d.ts +2 -0
  105. package/dist/terminal-subscription-key.js +9 -0
  106. package/dist/timeline-identity.d.ts +3 -0
  107. package/dist/timeline-identity.js +8 -0
  108. package/dist/tool-call-display.d.ts +11 -0
  109. package/dist/tool-call-display.js +136 -0
  110. package/dist/tool-name-normalization.d.ts +9 -0
  111. package/dist/tool-name-normalization.js +82 -0
  112. package/dist/validation/ws-outbound-schema-metadata.d.ts +11418 -0
  113. package/dist/validation/ws-outbound-schema-metadata.js +3 -0
  114. package/dist/validation/ws-outbound.d.ts +12 -0
  115. package/dist/validation/ws-outbound.js +8 -0
  116. package/dist/workspace-labels.d.ts +9 -0
  117. package/dist/workspace-labels.js +19 -0
  118. package/package.json +44 -0
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Ranked text matching shared by the app's pickers and the daemon's history
3
+ * search. A match is a tier plus the offset it was found at; lower is better on
4
+ * both, so callers sort ascending and never have to invent a scale.
5
+ *
6
+ * Typo tolerance is opt-in via `fuzzy`. The pickers leave it off — a combobox
7
+ * over a known list wants exact narrowing — while history search turns it on
8
+ * because the user is recalling a title from memory.
9
+ */
10
+ /** Exact tiers, best to worst. The fuzzy tier always sorts after all of them. */
11
+ const TIER_EXACT = 0;
12
+ const TIER_WHOLE_WORD = 1;
13
+ const TIER_PREFIX = 2;
14
+ const TIER_WORD_START = 3;
15
+ const TIER_SUBSTRING = 4;
16
+ const TIER_SUBSEQUENCE = 5;
17
+ const TIER_FUZZY = 6;
18
+ function isWordBoundaryChar(ch) {
19
+ if (ch === undefined)
20
+ return true;
21
+ return !/[a-z0-9]/.test(ch);
22
+ }
23
+ function scoreSubstringMatch(query, text) {
24
+ let best = null;
25
+ let pos = 0;
26
+ while (pos <= text.length - query.length) {
27
+ const found = text.indexOf(query, pos);
28
+ if (found === -1)
29
+ break;
30
+ const before = found > 0 ? text[found - 1] : undefined;
31
+ const after = text[found + query.length];
32
+ const startsAtBoundary = found === 0 || isWordBoundaryChar(before);
33
+ const endsAtBoundary = after === undefined || isWordBoundaryChar(after);
34
+ let tier;
35
+ if (startsAtBoundary && endsAtBoundary) {
36
+ tier = TIER_WHOLE_WORD;
37
+ }
38
+ else if (found === 0) {
39
+ tier = TIER_PREFIX;
40
+ }
41
+ else if (startsAtBoundary) {
42
+ tier = TIER_WORD_START;
43
+ }
44
+ else {
45
+ tier = TIER_SUBSTRING;
46
+ }
47
+ if (!best || tier < best.tier || (tier === best.tier && found < best.offset)) {
48
+ best = { tier, offset: found };
49
+ }
50
+ pos = found + 1;
51
+ }
52
+ return best;
53
+ }
54
+ function scoreSubsequenceMatch(query, text) {
55
+ let queryIndex = 0;
56
+ let firstIndex = -1;
57
+ let lastIndex = -1;
58
+ for (let textIndex = 0; textIndex < text.length && queryIndex < query.length; textIndex += 1) {
59
+ if (text[textIndex] !== query[queryIndex])
60
+ continue;
61
+ if (firstIndex === -1)
62
+ firstIndex = textIndex;
63
+ lastIndex = textIndex;
64
+ queryIndex += 1;
65
+ }
66
+ if (queryIndex !== query.length || firstIndex === -1)
67
+ return null;
68
+ return { tier: TIER_SUBSEQUENCE, offset: firstIndex, spread: lastIndex - firstIndex + 1 };
69
+ }
70
+ /**
71
+ * Damerau-Levenshtein distance, abandoned as soon as every cell in a row is
72
+ * over budget. Bounding it is what keeps the fuzzy tier affordable to run
73
+ * against every word of every candidate.
74
+ */
75
+ function boundedEditDistance(query, word, budget) {
76
+ if (Math.abs(query.length - word.length) > budget)
77
+ return null;
78
+ let twoRowsBack = [];
79
+ let previousRow = Array.from({ length: word.length + 1 }, (_, index) => index);
80
+ for (let queryIndex = 1; queryIndex <= query.length; queryIndex += 1) {
81
+ const currentRow = [queryIndex];
82
+ let rowBest = queryIndex;
83
+ for (let wordIndex = 1; wordIndex <= word.length; wordIndex += 1) {
84
+ const substitutionCost = query[queryIndex - 1] === word[wordIndex - 1] ? 0 : 1;
85
+ let cost = Math.min(currentRow[wordIndex - 1] + 1, previousRow[wordIndex] + 1, previousRow[wordIndex - 1] + substitutionCost);
86
+ const isTransposition = queryIndex > 1 &&
87
+ wordIndex > 1 &&
88
+ query[queryIndex - 1] === word[wordIndex - 2] &&
89
+ query[queryIndex - 2] === word[wordIndex - 1];
90
+ if (isTransposition) {
91
+ cost = Math.min(cost, twoRowsBack[wordIndex - 2] + 1);
92
+ }
93
+ currentRow.push(cost);
94
+ rowBest = Math.min(rowBest, cost);
95
+ }
96
+ if (rowBest > budget)
97
+ return null;
98
+ twoRowsBack = previousRow;
99
+ previousRow = currentRow;
100
+ }
101
+ const distance = previousRow[word.length];
102
+ return distance <= budget ? distance : null;
103
+ }
104
+ function transpositionDistance(query, word) {
105
+ return isAdjacentTransposition(query, word) ? 1 : null;
106
+ }
107
+ /** True when the two differ only by one swap of neighbouring characters. */
108
+ function isAdjacentTransposition(query, word) {
109
+ if (query.length !== word.length)
110
+ return false;
111
+ let index = 0;
112
+ while (index < query.length && query[index] === word[index])
113
+ index += 1;
114
+ if (index >= query.length - 1)
115
+ return false;
116
+ if (query[index] !== word[index + 1] || query[index + 1] !== word[index])
117
+ return false;
118
+ return query.slice(index + 2) === word.slice(index + 2);
119
+ }
120
+ export function fuzzyPolicyForToken(token) {
121
+ if (token.length <= 3)
122
+ return null;
123
+ if (token.length === 4)
124
+ return { maxEdits: 1, transpositionsOnly: true };
125
+ if (token.length <= 7)
126
+ return { maxEdits: 1, transpositionsOnly: false };
127
+ return { maxEdits: 2, transpositionsOnly: false };
128
+ }
129
+ /**
130
+ * Words are what people mistype, so the fuzzy tier compares the query against
131
+ * each word rather than against the whole string — otherwise a long title's
132
+ * length difference alone would blow the budget.
133
+ */
134
+ function scoreFuzzyMatch(query, text, policy) {
135
+ if (policy.maxEdits <= 0 || query.length <= policy.maxEdits)
136
+ return null;
137
+ let best = null;
138
+ const wordPattern = /[a-z0-9]+/g;
139
+ let word = wordPattern.exec(text);
140
+ while (word !== null) {
141
+ // Compare against the whole word and against its leading slices, so a typo
142
+ // in a prefix ("confug" for "configuration") still lands — the length gap
143
+ // to the full word would otherwise blow the budget on its own.
144
+ const candidates = new Set([
145
+ word[0],
146
+ word[0].slice(0, query.length),
147
+ word[0].slice(0, query.length + policy.maxEdits),
148
+ ]);
149
+ for (const candidate of candidates) {
150
+ const distance = policy.transpositionsOnly
151
+ ? transpositionDistance(query, candidate)
152
+ : boundedEditDistance(query, candidate, policy.maxEdits);
153
+ if (distance === null)
154
+ continue;
155
+ const score = { tier: TIER_FUZZY, offset: word.index, spread: distance };
156
+ if (!best || compareMatchScores(score, best) < 0) {
157
+ best = score;
158
+ }
159
+ }
160
+ word = wordPattern.exec(text);
161
+ }
162
+ return best;
163
+ }
164
+ export function scoreMatch(query, text, options = {}) {
165
+ if (!query)
166
+ return { tier: TIER_EXACT, offset: 0 };
167
+ const q = query.toLowerCase();
168
+ const t = text.toLowerCase();
169
+ if (t === q)
170
+ return { tier: TIER_EXACT, offset: 0 };
171
+ const substring = scoreSubstringMatch(q, t);
172
+ const exact = substring ?? (options.subsequence === false ? null : scoreSubsequenceMatch(q, t));
173
+ if (exact)
174
+ return exact;
175
+ const fuzzy = options.fuzzy;
176
+ return fuzzy ? scoreFuzzyMatch(q, t, fuzzy) : null;
177
+ }
178
+ function compactText(value) {
179
+ let compact = "";
180
+ const offsets = [];
181
+ for (let index = 0; index < value.length; index += 1) {
182
+ if (!/[a-z0-9]/i.test(value[index]))
183
+ continue;
184
+ compact += value[index].toLowerCase();
185
+ offsets.push(index);
186
+ }
187
+ return { value: compact, offsets };
188
+ }
189
+ /** Match a query against a complete displayed path, including its separators. */
190
+ export function scorePathMatch(query, path) {
191
+ const direct = scoreMatch(query, path);
192
+ if (direct)
193
+ return direct;
194
+ const compactQuery = compactText(query);
195
+ const compactPath = compactText(path);
196
+ if (!compactQuery.value || !compactPath.value)
197
+ return null;
198
+ const compactScore = scoreMatch(compactQuery.value, compactPath.value);
199
+ if (!compactScore)
200
+ return null;
201
+ return {
202
+ ...compactScore,
203
+ offset: compactPath.offsets[compactScore.offset] ?? compactScore.offset,
204
+ };
205
+ }
206
+ function mergeAdjacentRanges(indices) {
207
+ const ranges = [];
208
+ for (const index of indices) {
209
+ const last = ranges.at(-1);
210
+ if (last && last.start + last.length === index) {
211
+ last.length += 1;
212
+ continue;
213
+ }
214
+ ranges.push({ start: index, length: 1 });
215
+ }
216
+ return ranges;
217
+ }
218
+ function wordRangeAt(text, offset) {
219
+ let end = offset;
220
+ while (end < text.length && /[a-z0-9]/.test(text[end]))
221
+ end += 1;
222
+ return { start: offset, length: Math.max(end - offset, 1) };
223
+ }
224
+ /**
225
+ * Where a score's match actually landed, so a caller can mark it. Derived from
226
+ * a score rather than produced alongside one: ranking touches every candidate
227
+ * and needs no ranges, while only the handful of rows that get rendered do.
228
+ *
229
+ * The tier decides the shape. A substring hit is one span; a subsequence hit is
230
+ * the scattered characters it walked; a typo hit marks the whole word, because
231
+ * the characters the user got wrong are not in the text to point at.
232
+ */
233
+ export function matchRanges(query, text, score) {
234
+ if (!query)
235
+ return [];
236
+ const q = query.toLowerCase();
237
+ const t = text.toLowerCase();
238
+ if (score.tier === TIER_EXACT)
239
+ return [{ start: 0, length: text.length }];
240
+ if (score.tier === TIER_FUZZY)
241
+ return [wordRangeAt(t, score.offset)];
242
+ if (score.tier === TIER_SUBSEQUENCE) {
243
+ const indices = [];
244
+ let queryIndex = 0;
245
+ for (let textIndex = 0; textIndex < t.length && queryIndex < q.length; textIndex += 1) {
246
+ if (t[textIndex] !== q[queryIndex])
247
+ continue;
248
+ indices.push(textIndex);
249
+ queryIndex += 1;
250
+ }
251
+ return mergeAdjacentRanges(indices);
252
+ }
253
+ return [{ start: score.offset, length: q.length }];
254
+ }
255
+ export function compareMatchScores(a, b) {
256
+ if (a.tier !== b.tier)
257
+ return a.tier - b.tier;
258
+ if (a.offset !== b.offset)
259
+ return a.offset - b.offset;
260
+ return (a.spread ?? 0) - (b.spread ?? 0);
261
+ }
262
+ export function tokenizeQuery(query) {
263
+ return query
264
+ .trim()
265
+ .toLowerCase()
266
+ .split(/\s+/)
267
+ .filter((token) => token.length > 0);
268
+ }
269
+ export function scoreTextFields(query, fields, options = {}) {
270
+ const tokens = tokenizeQuery(query);
271
+ if (tokens.length === 0)
272
+ return { tier: TIER_EXACT, offset: 0, spread: 0 };
273
+ const aggregate = { tier: TIER_EXACT, offset: 0, spread: 0 };
274
+ for (const token of tokens) {
275
+ const fuzzy = options.typoTolerant ? fuzzyPolicyForToken(token) : null;
276
+ let best = null;
277
+ for (const field of fields) {
278
+ const score = scoreMatch(token, field, { fuzzy, subsequence: options.subsequence });
279
+ if (score && (!best || compareMatchScores(score, best) < 0)) {
280
+ best = score;
281
+ }
282
+ }
283
+ if (!best)
284
+ return null;
285
+ aggregate.tier += best.tier;
286
+ aggregate.offset += best.offset;
287
+ aggregate.spread = (aggregate.spread ?? 0) + (best.spread ?? token.length);
288
+ }
289
+ return aggregate;
290
+ }
291
+ //# sourceMappingURL=text-match.js.map
@@ -0,0 +1,11 @@
1
+ export declare const DEFAULT_SSH_DAEMON_PORT = 6767;
2
+ export interface SshTransportTarget {
3
+ host: string;
4
+ sshPort?: number;
5
+ daemonPort: number;
6
+ }
7
+ export declare function validatePort(value: string | number, label: string): number;
8
+ export declare function validateSshHost(host: string): string;
9
+ export declare function parseSshTransportUri(value: string): SshTransportTarget;
10
+ export declare function buildSshTunnelArgs(target: SshTransportTarget): string[];
11
+ //# sourceMappingURL=ssh-transport.d.ts.map
@@ -0,0 +1,72 @@
1
+ export const DEFAULT_SSH_DAEMON_PORT = 6767;
2
+ export function validatePort(value, label) {
3
+ const port = Number(value);
4
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
5
+ throw new Error(`${label} must be between 1 and 65535`);
6
+ }
7
+ return port;
8
+ }
9
+ export function validateSshHost(host) {
10
+ const normalized = host.trim();
11
+ if (!normalized)
12
+ throw new Error("SSH host is required");
13
+ if (/\s/u.test(normalized) || normalized.startsWith("-")) {
14
+ throw new Error("SSH host is invalid");
15
+ }
16
+ return normalized;
17
+ }
18
+ export function parseSshTransportUri(value) {
19
+ let url;
20
+ try {
21
+ url = new URL(value);
22
+ }
23
+ catch (error) {
24
+ throw new Error("Invalid SSH host URI", { cause: error });
25
+ }
26
+ if (url.protocol !== "ssh:" || url.password || (url.pathname !== "" && url.pathname !== "/")) {
27
+ throw new Error("Invalid SSH host URI");
28
+ }
29
+ if (url.hash)
30
+ throw new Error("SSH host URI does not support fragments");
31
+ for (const key of url.searchParams.keys()) {
32
+ if (key !== "daemonPort")
33
+ throw new Error(`Unsupported SSH host option: ${key}`);
34
+ }
35
+ const daemonPorts = url.searchParams.getAll("daemonPort");
36
+ if (daemonPorts.length > 1)
37
+ throw new Error("daemonPort may only be specified once");
38
+ const urlHostname = url.hostname;
39
+ const hostname = validateSshHost(urlHostname.startsWith("[") && urlHostname.endsWith("]")
40
+ ? urlHostname.slice(1, -1)
41
+ : urlHostname);
42
+ const username = decodeURIComponent(url.username);
43
+ const host = validateSshHost(username ? `${username}@${hostname}` : hostname);
44
+ return {
45
+ host,
46
+ ...(url.port ? { sshPort: validatePort(url.port, "SSH port") } : {}),
47
+ daemonPort: daemonPorts[0] === undefined
48
+ ? DEFAULT_SSH_DAEMON_PORT
49
+ : validatePort(daemonPorts[0], "Daemon port"),
50
+ };
51
+ }
52
+ export function buildSshTunnelArgs(target) {
53
+ const host = validateSshHost(target.host);
54
+ const daemonPort = validatePort(target.daemonPort, "Daemon port");
55
+ const args = [
56
+ "-T",
57
+ "-o",
58
+ "BatchMode=yes",
59
+ "-o",
60
+ "ConnectTimeout=10",
61
+ "-o",
62
+ "ClearAllForwardings=yes",
63
+ "-o",
64
+ "ExitOnForwardFailure=yes",
65
+ ];
66
+ if (target.sshPort !== undefined) {
67
+ args.push("-p", String(validatePort(target.sshPort, "SSH port")));
68
+ }
69
+ args.push("-W", `127.0.0.1:${daemonPort}`, host);
70
+ return args;
71
+ }
72
+ //# sourceMappingURL=ssh-transport.js.map
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ export declare const TERMINAL_ACTIVITY_STATES: readonly ["idle", "working", "attention"];
3
+ export declare const TERMINAL_ACTIVITY_ATTENTION_REASONS: readonly ["finished", "needs_input"];
4
+ export type TerminalActivityState = (typeof TERMINAL_ACTIVITY_STATES)[number];
5
+ export type TerminalActivityAttentionReason = (typeof TERMINAL_ACTIVITY_ATTENTION_REASONS)[number];
6
+ export declare const TerminalActivitySchema: z.ZodObject<{
7
+ state: z.ZodCatch<z.ZodEnum<{
8
+ idle: "idle";
9
+ working: "working";
10
+ attention: "attention";
11
+ }>>;
12
+ attentionReason: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
13
+ finished: "finished";
14
+ needs_input: "needs_input";
15
+ }>>>>;
16
+ changedAt: z.ZodNumber;
17
+ }, z.core.$strip>;
18
+ export type TerminalActivity = z.infer<typeof TerminalActivitySchema>;
19
+ export type TerminalActivityStatusBucket = "running" | "needs_input" | "attention";
20
+ export declare function deriveTerminalActivityStatusBucket(activity: TerminalActivity | null | undefined): TerminalActivityStatusBucket | null;
21
+ //# sourceMappingURL=terminal-activity.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ export const TERMINAL_ACTIVITY_STATES = ["idle", "working", "attention"];
3
+ export const TERMINAL_ACTIVITY_ATTENTION_REASONS = ["finished", "needs_input"];
4
+ export const TerminalActivitySchema = z.object({
5
+ // Forward-compat: a newer daemon may send a state this client doesn't know.
6
+ // Degrade unknown states to "idle" (no indicator, no notification) so the
7
+ // message still parses, instead of a strict enum rejecting the whole payload.
8
+ state: z.enum(TERMINAL_ACTIVITY_STATES).catch("idle"),
9
+ attentionReason: z.enum(TERMINAL_ACTIVITY_ATTENTION_REASONS).nullable().optional().catch(null),
10
+ changedAt: z.number(),
11
+ });
12
+ export function deriveTerminalActivityStatusBucket(activity) {
13
+ if (!activity)
14
+ return null;
15
+ if (activity.attentionReason === "needs_input")
16
+ return "needs_input";
17
+ if (activity.attentionReason === "finished")
18
+ return "attention";
19
+ if (activity.state === "working")
20
+ return "running";
21
+ if (activity.state === "attention")
22
+ return "needs_input";
23
+ return null;
24
+ }
25
+ //# sourceMappingURL=terminal-activity.js.map
@@ -0,0 +1,30 @@
1
+ export interface TerminalInputModeFeedResult {
2
+ changed: boolean;
3
+ responses: string[];
4
+ }
5
+ export interface TerminalInputModeState {
6
+ kittyKeyboardFlags: number;
7
+ win32InputMode: boolean;
8
+ applicationCursorKeys?: boolean;
9
+ bracketedPaste?: boolean;
10
+ }
11
+ export declare const DEFAULT_TERMINAL_INPUT_MODE_STATE: TerminalInputModeState;
12
+ export declare function terminalInputModeSupportsModifiedEnter(state: TerminalInputModeState): boolean;
13
+ export declare function terminalInputModeStatesEqual(left: TerminalInputModeState, right: TerminalInputModeState): boolean;
14
+ export declare class TerminalInputModeTracker {
15
+ private kittyKeyboardFlags;
16
+ private win32InputMode;
17
+ private applicationCursorKeys;
18
+ private bracketedPaste;
19
+ private readonly kittyKeyboardStack;
20
+ private pending;
21
+ feed(data: string): TerminalInputModeFeedResult;
22
+ reset(): void;
23
+ getState(): TerminalInputModeState;
24
+ getKittyKeyboardFlags(): number;
25
+ supportsModifiedEnter(): boolean;
26
+ getPreamble(): string;
27
+ private applyKittyKeyboardSequence;
28
+ private applyPrivateModeSequence;
29
+ }
30
+ //# sourceMappingURL=terminal-input-mode.d.ts.map
@@ -0,0 +1,180 @@
1
+ export const DEFAULT_TERMINAL_INPUT_MODE_STATE = {
2
+ kittyKeyboardFlags: 0,
3
+ win32InputMode: false,
4
+ applicationCursorKeys: false,
5
+ bracketedPaste: false,
6
+ };
7
+ const ESC = String.fromCharCode(0x1b);
8
+ const APPLICATION_CURSOR_KEYS_MODE = 1;
9
+ const WIN32_INPUT_MODE = 9001;
10
+ const BRACKETED_PASTE_MODE = 2004;
11
+ const CSI_INPUT_MODE_SEQUENCE = new RegExp(`${ESC}\\[(?:([<>=?]?)([0-9;]*)u|\\?([0-9;]*)([hl]))`, "g");
12
+ const INCOMPLETE_CSI_INPUT_MODE_SEQUENCE = new RegExp(`${ESC}\\[[<>=?]?[0-9;]*$`);
13
+ function parseFirstParam(params) {
14
+ const first = params.split(";")[0];
15
+ if (!first || !/^\d+$/.test(first)) {
16
+ return null;
17
+ }
18
+ return Number(first);
19
+ }
20
+ function parseSecondParam(params) {
21
+ const second = params.split(";")[1];
22
+ if (!second || !/^\d+$/.test(second)) {
23
+ return null;
24
+ }
25
+ return Number(second);
26
+ }
27
+ function parsePrivateModeParams(params) {
28
+ const modes = new Set();
29
+ for (const param of params.split(";")) {
30
+ if (/^\d+$/.test(param)) {
31
+ modes.add(Number(param));
32
+ }
33
+ }
34
+ return modes;
35
+ }
36
+ export function terminalInputModeSupportsModifiedEnter(state) {
37
+ return state.kittyKeyboardFlags > 0 || state.win32InputMode;
38
+ }
39
+ export function terminalInputModeStatesEqual(left, right) {
40
+ return (left.kittyKeyboardFlags === right.kittyKeyboardFlags &&
41
+ left.win32InputMode === right.win32InputMode &&
42
+ Boolean(left.applicationCursorKeys) === Boolean(right.applicationCursorKeys) &&
43
+ Boolean(left.bracketedPaste) === Boolean(right.bracketedPaste));
44
+ }
45
+ export class TerminalInputModeTracker {
46
+ constructor() {
47
+ this.kittyKeyboardFlags = 0;
48
+ this.win32InputMode = false;
49
+ this.applicationCursorKeys = false;
50
+ this.bracketedPaste = false;
51
+ this.kittyKeyboardStack = [];
52
+ this.pending = "";
53
+ }
54
+ feed(data) {
55
+ if (data.length === 0) {
56
+ return { changed: false, responses: [] };
57
+ }
58
+ const text = `${this.pending}${data}`;
59
+ this.pending = "";
60
+ let changed = false;
61
+ const responses = [];
62
+ let consumedUntil = 0;
63
+ CSI_INPUT_MODE_SEQUENCE.lastIndex = 0;
64
+ for (;;) {
65
+ const match = CSI_INPUT_MODE_SEQUENCE.exec(text);
66
+ if (!match) {
67
+ break;
68
+ }
69
+ consumedUntil = CSI_INPUT_MODE_SEQUENCE.lastIndex;
70
+ if (match[4]) {
71
+ changed = this.applyPrivateModeSequence(match[3] ?? "", match[4]) || changed;
72
+ continue;
73
+ }
74
+ const result = this.applyKittyKeyboardSequence(match[1] ?? "", match[2] ?? "");
75
+ changed = changed || result.changed;
76
+ responses.push(...result.responses);
77
+ }
78
+ const tail = text.slice(consumedUntil);
79
+ const pendingStart = tail.lastIndexOf(`${ESC}[`);
80
+ if (pendingStart >= 0) {
81
+ const pending = tail.slice(pendingStart);
82
+ if (INCOMPLETE_CSI_INPUT_MODE_SEQUENCE.test(pending)) {
83
+ this.pending = pending;
84
+ }
85
+ }
86
+ return { changed, responses };
87
+ }
88
+ reset() {
89
+ this.kittyKeyboardFlags = 0;
90
+ this.win32InputMode = false;
91
+ this.applicationCursorKeys = false;
92
+ this.bracketedPaste = false;
93
+ this.kittyKeyboardStack.length = 0;
94
+ this.pending = "";
95
+ }
96
+ getState() {
97
+ return {
98
+ kittyKeyboardFlags: this.kittyKeyboardFlags,
99
+ win32InputMode: this.win32InputMode,
100
+ applicationCursorKeys: this.applicationCursorKeys,
101
+ bracketedPaste: this.bracketedPaste,
102
+ };
103
+ }
104
+ getKittyKeyboardFlags() {
105
+ return this.kittyKeyboardFlags;
106
+ }
107
+ supportsModifiedEnter() {
108
+ return terminalInputModeSupportsModifiedEnter(this.getState());
109
+ }
110
+ getPreamble() {
111
+ const parts = [];
112
+ if (this.kittyKeyboardFlags > 0) {
113
+ parts.push(`\x1b[=${this.kittyKeyboardFlags};1u`);
114
+ }
115
+ if (this.win32InputMode) {
116
+ parts.push("\x1b[?9001h");
117
+ }
118
+ if (this.applicationCursorKeys) {
119
+ parts.push("\x1b[?1h");
120
+ }
121
+ if (this.bracketedPaste) {
122
+ parts.push("\x1b[?2004h");
123
+ }
124
+ return parts.join("");
125
+ }
126
+ applyKittyKeyboardSequence(prefix, params) {
127
+ const previousFlags = this.kittyKeyboardFlags;
128
+ switch (prefix) {
129
+ case ">": {
130
+ this.kittyKeyboardStack.push(this.kittyKeyboardFlags);
131
+ this.kittyKeyboardFlags = parseFirstParam(params) ?? 1;
132
+ break;
133
+ }
134
+ case "=": {
135
+ const mode = parseSecondParam(params) ?? 1;
136
+ this.kittyKeyboardFlags = mode === 0 ? 0 : (parseFirstParam(params) ?? 0);
137
+ break;
138
+ }
139
+ case "<": {
140
+ const count = Math.max(1, parseFirstParam(params) ?? 1);
141
+ for (let index = 0; index < count; index += 1) {
142
+ this.kittyKeyboardFlags = this.kittyKeyboardStack.pop() ?? 0;
143
+ }
144
+ break;
145
+ }
146
+ case "?":
147
+ return {
148
+ changed: false,
149
+ responses: [`\x1b[?${this.kittyKeyboardFlags}u`],
150
+ };
151
+ default:
152
+ return { changed: false, responses: [] };
153
+ }
154
+ return {
155
+ changed: this.kittyKeyboardFlags !== previousFlags,
156
+ responses: [],
157
+ };
158
+ }
159
+ applyPrivateModeSequence(params, final) {
160
+ const modes = parsePrivateModeParams(params);
161
+ let changed = false;
162
+ if (modes.has(WIN32_INPUT_MODE)) {
163
+ const previous = this.win32InputMode;
164
+ this.win32InputMode = final === "h";
165
+ changed = this.win32InputMode !== previous || changed;
166
+ }
167
+ if (modes.has(APPLICATION_CURSOR_KEYS_MODE)) {
168
+ const previous = this.applicationCursorKeys;
169
+ this.applicationCursorKeys = final === "h";
170
+ changed = this.applicationCursorKeys !== previous || changed;
171
+ }
172
+ if (modes.has(BRACKETED_PASTE_MODE)) {
173
+ const previous = this.bracketedPaste;
174
+ this.bracketedPaste = final === "h";
175
+ changed = this.bracketedPaste !== previous || changed;
176
+ }
177
+ return changed;
178
+ }
179
+ }
180
+ //# sourceMappingURL=terminal-input-mode.js.map
@@ -0,0 +1,13 @@
1
+ import type { TerminalInputModeState } from "./terminal-input-mode.js";
2
+ export interface TerminalKeyInput {
3
+ key: string;
4
+ ctrl?: boolean;
5
+ alt?: boolean;
6
+ shift?: boolean;
7
+ meta?: boolean;
8
+ }
9
+ export interface TerminalKeyInputEncodingOptions {
10
+ inputMode?: TerminalInputModeState;
11
+ }
12
+ export declare function encodeTerminalKeyInput(input: TerminalKeyInput, options?: TerminalKeyInputEncodingOptions): string;
13
+ //# sourceMappingURL=terminal-key-input.d.ts.map