@nebutra/agent-runtime 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +21 -676
  2. package/dist/adapters/index.d.ts +24 -5
  3. package/dist/adapters/index.js +27 -0
  4. package/dist/adapters/index.js.map +1 -1
  5. package/dist/chunk-Q62VKHIT.js +178 -0
  6. package/dist/chunk-Q62VKHIT.js.map +1 -0
  7. package/dist/{chunk-MUF7ZZTO.js → chunk-R5HOSQUW.js} +2 -1
  8. package/dist/{chunk-MUF7ZZTO.js.map → chunk-R5HOSQUW.js.map} +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +14 -17
  11. package/dist/index.js.map +1 -1
  12. package/dist/orchestration.d.ts +42 -1
  13. package/dist/orchestration.js +7 -3
  14. package/dist/sandbox.js +1 -1
  15. package/package.json +80 -30
  16. package/.turbo/turbo-build.log +0 -130
  17. package/.turbo/turbo-test.log +0 -47
  18. package/.turbo/turbo-typecheck.log +0 -4
  19. package/CHANGELOG.md +0 -250
  20. package/dist/chunk-MX2WL43P.js +0 -90
  21. package/dist/chunk-MX2WL43P.js.map +0 -1
  22. package/examples/pulsar-quickstart.ts +0 -35
  23. package/examples/resume-branch.ts +0 -45
  24. package/examples/subagent-fanout.ts +0 -20
  25. package/src/adapters/dispatcher-sse.test.ts +0 -218
  26. package/src/adapters/dispatcher-sse.ts +0 -222
  27. package/src/adapters/index.ts +0 -18
  28. package/src/adapters/mcp-catalog.test.ts +0 -213
  29. package/src/adapters/mcp-catalog.ts +0 -188
  30. package/src/adapters/prisma-rollout.test.ts +0 -153
  31. package/src/adapters/prisma-rollout.ts +0 -104
  32. package/src/agent-runtime.test.ts +0 -176
  33. package/src/artifact-stream.test.ts +0 -330
  34. package/src/artifact-stream.ts +0 -453
  35. package/src/channel-gateway.test.ts +0 -432
  36. package/src/channel-gateway.ts +0 -357
  37. package/src/cli.ts +0 -49
  38. package/src/code-review.test.ts +0 -501
  39. package/src/code-review.ts +0 -495
  40. package/src/command-suggestions.test.ts +0 -251
  41. package/src/command-suggestions.ts +0 -338
  42. package/src/commands.test.ts +0 -184
  43. package/src/commands.ts +0 -140
  44. package/src/commit-message.test.ts +0 -249
  45. package/src/commit-message.ts +0 -180
  46. package/src/context-compaction.test.ts +0 -522
  47. package/src/context-compaction.ts +0 -438
  48. package/src/definitions.test.ts +0 -78
  49. package/src/definitions.ts +0 -190
  50. package/src/deployment-status.test.ts +0 -215
  51. package/src/deployment-status.ts +0 -227
  52. package/src/design-context.test.ts +0 -195
  53. package/src/design-context.ts +0 -198
  54. package/src/dispatcher.test.ts +0 -234
  55. package/src/dispatcher.ts +0 -189
  56. package/src/durable-turn.test.ts +0 -209
  57. package/src/durable-turn.ts +0 -135
  58. package/src/edit-planner.test.ts +0 -204
  59. package/src/edit-planner.ts +0 -325
  60. package/src/fuzzy-match.test.ts +0 -311
  61. package/src/fuzzy-match.ts +0 -444
  62. package/src/hook-pipeline.test.ts +0 -279
  63. package/src/hook-pipeline.ts +0 -373
  64. package/src/inbound-admission.test.ts +0 -394
  65. package/src/inbound-admission.ts +0 -246
  66. package/src/index.ts +0 -49
  67. package/src/loop.test.ts +0 -161
  68. package/src/loop.ts +0 -211
  69. package/src/mcp-bridge.test.ts +0 -165
  70. package/src/mcp-bridge.ts +0 -81
  71. package/src/memory-provider.test.ts +0 -232
  72. package/src/memory-provider.ts +0 -257
  73. package/src/model.ts +0 -168
  74. package/src/orchestration.test.ts +0 -53
  75. package/src/orchestration.ts +0 -146
  76. package/src/permission-ruleset.test.ts +0 -301
  77. package/src/permission-ruleset.ts +0 -1
  78. package/src/policy.ts +0 -151
  79. package/src/project-repo.test.ts +0 -232
  80. package/src/project-repo.ts +0 -311
  81. package/src/protocol.ts +0 -159
  82. package/src/pulsar.test.ts +0 -156
  83. package/src/pulsar.ts +0 -322
  84. package/src/rollout-store-persistent.test.ts +0 -217
  85. package/src/rollout-store-persistent.ts +0 -166
  86. package/src/rollout.ts +0 -150
  87. package/src/sandbox.ts +0 -113
  88. package/src/session-share.test.ts +0 -360
  89. package/src/session-share.ts +0 -310
  90. package/src/skill-distillation.test.ts +0 -177
  91. package/src/skill-distillation.ts +0 -369
  92. package/src/skills.test.ts +0 -277
  93. package/src/skills.ts +0 -277
  94. package/src/subagents.test.ts +0 -290
  95. package/src/subagents.ts +0 -332
  96. package/src/tools.test.ts +0 -12
  97. package/src/tools.ts +0 -129
  98. package/src/workbench.test.ts +0 -0
  99. package/src/workbench.ts +0 -0
  100. package/tsconfig.json +0 -12
  101. package/tsup.config.ts +0 -36
@@ -1,444 +0,0 @@
1
- /**
2
- * fuzzy-match — pure, deterministic string-matching primitives.
3
- *
4
- * A dependency-free re-expression of the classic "smart-case fuzzy +
5
- * glob-wildcard" matcher found in modern terminals and command palettes.
6
- * Everything here is a small pure function: no I/O, no shared state, no
7
- * mutation of inputs, no `console`.
8
- *
9
- * Surface:
10
- * - matchIndices smart-case subsequence match
11
- * - matchIndicesCaseInsensitive always case-insensitive
12
- * - matchIndicesCaseInsensitiveIgnoreSpaces ci + spaces ignored
13
- * - matchWildcardPattern glob (* ? [..] \) full-string
14
- * - matchWildcardPatternCaseInsensitive glob, case-insensitive
15
- * - rankByFuzzy generic rank/filter/sort helper
16
- *
17
- * --------------------------------------------------------------------------
18
- * Subsequence scoring heuristic (deterministic; only ORDERING is contractual)
19
- * --------------------------------------------------------------------------
20
- * Every char of `query` must appear in `text` in order. Among the many
21
- * possible subsequences we use a single forward greedy scan with a fixed,
22
- * documented tie-break so the result is fully deterministic:
23
- *
24
- * For each query char, scan `text` forward from the previous match + 1 and
25
- * collect every candidate position. Pick the candidate that maximises a
26
- * per-char local score:
27
- *
28
- * + WORD_BOUNDARY_BONUS if the candidate is at text start, or the
29
- * preceding char is a separator (`/ _ - . space`)
30
- * or a lower→upper camelCase hump.
31
- * + CONTIGUOUS_BONUS if the candidate immediately follows the
32
- * previous matched index (a run).
33
- * - GAP_PENALTY * gap distance skipped since the previous match.
34
- * - LEADING_PENALTY (first query char only) * its absolute index,
35
- * so earlier overall placement is preferred.
36
- *
37
- * Ties on local score break toward the EARLIEST candidate index — this is
38
- * what makes the scan deterministic and is exercised by the tests.
39
- *
40
- * The aggregate `score` is the sum of per-char local scores. Higher is a
41
- * better match. Magnitudes are intentionally unspecified; only the relative
42
- * ordering (boundary/contiguous/at-start outranks scattered/buried/late) is
43
- * part of the contract.
44
- *
45
- * Empty query: defined to match everything with `score === 0` and
46
- * `indices === []`. Empty text with a non-empty query: `null`.
47
- */
48
-
49
- const SEPARATORS = new Set(["/", "_", "-", ".", " "]);
50
-
51
- const WORD_BOUNDARY_BONUS = 16;
52
- const CONTIGUOUS_BONUS = 12;
53
- const STRING_START_BONUS = 8;
54
- const GAP_PENALTY = 1;
55
- const LEADING_PENALTY = 2;
56
-
57
- /** A successful match: the chosen text positions and an aggregate score. */
58
- export interface FuzzyMatch {
59
- readonly score: number;
60
- readonly indices: readonly number[];
61
- }
62
-
63
- interface MatchOptions {
64
- /** Force case-insensitive matching regardless of query casing. */
65
- readonly forceCaseInsensitive?: boolean | undefined;
66
- }
67
-
68
- function hasUpperCase(value: string): boolean {
69
- return value !== value.toLowerCase();
70
- }
71
-
72
- function isSeparator(ch: string | undefined): boolean {
73
- return ch !== undefined && SEPARATORS.has(ch);
74
- }
75
-
76
- /**
77
- * True when `text[index]` sits at a "word boundary": string start, right
78
- * after a separator, or a lower→upper camelCase hump.
79
- */
80
- function isBoundary(text: string, index: number): boolean {
81
- if (index === 0) {
82
- return true;
83
- }
84
- const prev = text[index - 1];
85
- if (isSeparator(prev)) {
86
- return true;
87
- }
88
- const cur = text[index];
89
- if (prev !== undefined && cur !== undefined) {
90
- const prevLower = prev === prev.toLowerCase() && prev !== prev.toUpperCase();
91
- const curUpper = cur === cur.toUpperCase() && cur !== cur.toLowerCase();
92
- if (prevLower && curUpper) {
93
- return true;
94
- }
95
- }
96
- return false;
97
- }
98
-
99
- /**
100
- * Core greedy subsequence scan.
101
- *
102
- * @param text the haystack
103
- * @param query the needle
104
- * @param caseInsensitive compare lower-cased chars
105
- * @param skipChar optional predicate: text chars for which this returns true
106
- * are skipped for *matching* but their original index is never
107
- * emitted (used by the ignore-spaces variant). Query chars for
108
- * which it returns true are dropped before scanning.
109
- */
110
- function scan(
111
- text: string,
112
- query: string,
113
- caseInsensitive: boolean,
114
- skipChar?: (ch: string) => boolean,
115
- ): FuzzyMatch | null {
116
- // Drop skipped chars from the query (e.g. spaces) up front.
117
- const effectiveQuery = skipChar
118
- ? Array.from(query)
119
- .filter((c) => !skipChar(c))
120
- .join("")
121
- : query;
122
-
123
- if (effectiveQuery.length === 0) {
124
- return { score: 0, indices: [] };
125
- }
126
- if (text.length === 0) {
127
- return null;
128
- }
129
-
130
- const fold = (c: string): string => (caseInsensitive ? c.toLowerCase() : c);
131
-
132
- const indices: number[] = [];
133
- let score = 0;
134
- let searchFrom = 0;
135
- let prevMatched = -1;
136
-
137
- for (let q = 0; q < effectiveQuery.length; q++) {
138
- const target = fold(effectiveQuery[q]!);
139
-
140
- let bestIndex = -1;
141
- let bestLocal = Number.NEGATIVE_INFINITY;
142
-
143
- for (let t = searchFrom; t < text.length; t++) {
144
- const tch = text[t]!;
145
- if (skipChar && skipChar(tch)) {
146
- continue;
147
- }
148
- if (fold(tch) !== target) {
149
- continue;
150
- }
151
-
152
- let local = 0;
153
- if (isBoundary(text, t)) {
154
- local += WORD_BOUNDARY_BONUS;
155
- }
156
- if (t === 0) {
157
- local += STRING_START_BONUS;
158
- }
159
- if (prevMatched >= 0 && t === prevMatched + 1) {
160
- local += CONTIGUOUS_BONUS;
161
- }
162
- const gap = prevMatched >= 0 ? t - prevMatched - 1 : 0;
163
- local -= GAP_PENALTY * gap;
164
- if (q === 0) {
165
- local -= LEADING_PENALTY * t;
166
- }
167
-
168
- // Deterministic tie-break: strictly-greater wins, so the EARLIEST
169
- // candidate is retained on ties.
170
- if (local > bestLocal) {
171
- bestLocal = local;
172
- bestIndex = t;
173
- }
174
- }
175
-
176
- if (bestIndex === -1) {
177
- return null;
178
- }
179
-
180
- indices.push(bestIndex);
181
- score += bestLocal;
182
- prevMatched = bestIndex;
183
- searchFrom = bestIndex + 1;
184
- }
185
-
186
- return { score, indices };
187
- }
188
-
189
- /**
190
- * Smart-case subsequence fuzzy match.
191
- *
192
- * If `query` is all-lowercase the match is case-insensitive; if it contains
193
- * any uppercase char the *whole* query matches case-sensitively. Returns the
194
- * chosen 0-based `indices` in `text` plus an aggregate `score`, or `null` if
195
- * `query` is not a subsequence of `text`.
196
- */
197
- export function matchIndices(
198
- text: string,
199
- query: string,
200
- options: MatchOptions = {},
201
- ): FuzzyMatch | null {
202
- const caseInsensitive = options.forceCaseInsensitive === true || !hasUpperCase(query);
203
- return scan(text, query, caseInsensitive);
204
- }
205
-
206
- /** Like {@link matchIndices} but always case-insensitive. */
207
- export function matchIndicesCaseInsensitive(text: string, query: string): FuzzyMatch | null {
208
- return scan(text, query, true);
209
- }
210
-
211
- /**
212
- * Case-insensitive subsequence match where spaces in BOTH `query` and `text`
213
- * are ignored for matching. Returned `indices` always point at original
214
- * `text` positions and never land on a skipped space.
215
- */
216
- export function matchIndicesCaseInsensitiveIgnoreSpaces(
217
- text: string,
218
- query: string,
219
- ): FuzzyMatch | null {
220
- return scan(text, query, true, (c) => c === " ");
221
- }
222
-
223
- // ---------------------------------------------------------------------------
224
- // Wildcard / glob matcher
225
- // ---------------------------------------------------------------------------
226
-
227
- type GlobToken =
228
- | { readonly kind: "literal"; readonly ch: string }
229
- | { readonly kind: "any" } // *
230
- | { readonly kind: "single" } // ?
231
- | {
232
- readonly kind: "class";
233
- readonly negated: boolean;
234
- readonly ranges: ReadonlyArray<readonly [number, number]>;
235
- };
236
-
237
- /**
238
- * Compile a glob pattern into a token stream. Supports:
239
- * `*` any run (including empty)
240
- * `?` exactly one char
241
- * `[..]` / `[a-z]` / `[!..]` char class (negation via leading `!` or `^`)
242
- * `\x` escape — next char is literal
243
- * `/` is an ordinary literal (path-friendly). Unterminated `[` is treated as
244
- * a literal `[`.
245
- */
246
- function compileGlob(pattern: string): readonly GlobToken[] {
247
- const tokens: GlobToken[] = [];
248
- let i = 0;
249
- while (i < pattern.length) {
250
- const ch = pattern[i]!;
251
- if (ch === "\\") {
252
- const next = pattern[i + 1];
253
- if (next !== undefined) {
254
- tokens.push({ kind: "literal", ch: next });
255
- i += 2;
256
- continue;
257
- }
258
- tokens.push({ kind: "literal", ch: "\\" });
259
- i += 1;
260
- continue;
261
- }
262
- if (ch === "*") {
263
- tokens.push({ kind: "any" });
264
- i += 1;
265
- continue;
266
- }
267
- if (ch === "?") {
268
- tokens.push({ kind: "single" });
269
- i += 1;
270
- continue;
271
- }
272
- if (ch === "[") {
273
- const close = pattern.indexOf("]", i + 1);
274
- if (close === -1) {
275
- tokens.push({ kind: "literal", ch: "[" });
276
- i += 1;
277
- continue;
278
- }
279
- let body = pattern.slice(i + 1, close);
280
- let negated = false;
281
- if (body.startsWith("!") || body.startsWith("^")) {
282
- negated = true;
283
- body = body.slice(1);
284
- }
285
- const ranges: Array<readonly [number, number]> = [];
286
- let b = 0;
287
- while (b < body.length) {
288
- const c0 = body[b]!;
289
- if (body[b + 1] === "-" && b + 2 < body.length) {
290
- const c1 = body[b + 2]!;
291
- ranges.push([c0.charCodeAt(0), c1.charCodeAt(0)]);
292
- b += 3;
293
- } else {
294
- ranges.push([c0.charCodeAt(0), c0.charCodeAt(0)]);
295
- b += 1;
296
- }
297
- }
298
- tokens.push({ kind: "class", negated, ranges });
299
- i = close + 1;
300
- continue;
301
- }
302
- tokens.push({ kind: "literal", ch });
303
- i += 1;
304
- }
305
- return tokens;
306
- }
307
-
308
- function classMatches(token: Extract<GlobToken, { kind: "class" }>, code: number): boolean {
309
- let inRange = false;
310
- for (const [lo, hi] of token.ranges) {
311
- if (code >= lo && code <= hi) {
312
- inRange = true;
313
- break;
314
- }
315
- }
316
- return token.negated ? !inRange : inRange;
317
- }
318
-
319
- /**
320
- * Linear-time anchored glob match (no RegExp, no catastrophic backtracking).
321
- *
322
- * Classic two-pointer wildcard algorithm: on a `*` we record a backtrack
323
- * point and greedily advance; on any later mismatch we resume the last `*`
324
- * one char further. This is O(text * pattern) worst case — adversarial
325
- * `***...*b` inputs stay fast.
326
- */
327
- function globMatch(text: string, tokens: readonly GlobToken[], caseInsensitive: boolean): boolean {
328
- const fold = (c: string): string => (caseInsensitive ? c.toLowerCase() : c);
329
-
330
- let ti = 0; // text index
331
- let pi = 0; // pattern (token) index
332
- let starPi = -1; // last '*' token index
333
- let starTi = 0; // text index when last '*' was seen
334
-
335
- while (ti < text.length) {
336
- const tok = tokens[pi];
337
- if (tok !== undefined && tok.kind === "any") {
338
- starPi = pi;
339
- starTi = ti;
340
- pi += 1;
341
- continue;
342
- }
343
- if (tok !== undefined && tok.kind === "single") {
344
- pi += 1;
345
- ti += 1;
346
- continue;
347
- }
348
- if (tok !== undefined && tok.kind === "literal") {
349
- if (fold(text[ti]!) === fold(tok.ch)) {
350
- pi += 1;
351
- ti += 1;
352
- continue;
353
- }
354
- } else if (tok !== undefined && tok.kind === "class") {
355
- const ch = caseInsensitive ? text[ti]!.toLowerCase() : text[ti]!;
356
- // For case-insensitive class matching, test both casings.
357
- const matched = caseInsensitive
358
- ? classMatches(tok, ch.charCodeAt(0)) ||
359
- classMatches(tok, text[ti]!.toUpperCase().charCodeAt(0))
360
- : classMatches(tok, ch.charCodeAt(0));
361
- if (matched) {
362
- pi += 1;
363
- ti += 1;
364
- continue;
365
- }
366
- }
367
-
368
- // Mismatch: resume from the last '*' if any.
369
- if (starPi !== -1) {
370
- pi = starPi + 1;
371
- starTi += 1;
372
- ti = starTi;
373
- continue;
374
- }
375
- return false;
376
- }
377
-
378
- // Consume any trailing '*' tokens.
379
- while (pi < tokens.length && tokens[pi]!.kind === "any") {
380
- pi += 1;
381
- }
382
- return pi === tokens.length;
383
- }
384
-
385
- /**
386
- * Anchored (full-string) glob match. `*` = any run incl. empty, `?` = one
387
- * char, `[..]`/`[a-z]`/`[!..]` char classes, `\` escapes a metachar. `/` is
388
- * an ordinary char. Case-sensitive.
389
- */
390
- export function matchWildcardPattern(text: string, pattern: string): boolean {
391
- return globMatch(text, compileGlob(pattern), false);
392
- }
393
-
394
- /** Case-insensitive variant of {@link matchWildcardPattern}. */
395
- export function matchWildcardPatternCaseInsensitive(text: string, pattern: string): boolean {
396
- return globMatch(text, compileGlob(pattern), true);
397
- }
398
-
399
- // ---------------------------------------------------------------------------
400
- // Ranking helper
401
- // ---------------------------------------------------------------------------
402
-
403
- /** One ranked result: the original item plus its match score and indices. */
404
- export interface RankedFuzzyResult<T> {
405
- readonly item: T;
406
- readonly score: number;
407
- readonly indices: readonly number[];
408
- }
409
-
410
- interface RankByFuzzyOptions {
411
- /** Force case-insensitive matching regardless of query casing. */
412
- readonly caseInsensitive?: boolean | undefined;
413
- }
414
-
415
- /**
416
- * Rank `items` by fuzzy-matching `key(item)` against `query`.
417
- *
418
- * - Non-matches are filtered out.
419
- * - Sorted by `score` descending.
420
- * - Stable for equal scores (input order preserved).
421
- * - Pure: neither `items` nor its elements are mutated.
422
- * - Empty query → every item with `score 0` / empty `indices`, input order.
423
- */
424
- export function rankByFuzzy<T>(
425
- items: readonly T[],
426
- query: string,
427
- key: (item: T) => string,
428
- options: RankByFuzzyOptions = {},
429
- ): ReadonlyArray<RankedFuzzyResult<T>> {
430
- const forceCaseInsensitive = options.caseInsensitive === true;
431
-
432
- const ranked: Array<RankedFuzzyResult<T> & { readonly order: number }> = [];
433
- items.forEach((item, order) => {
434
- const match = matchIndices(key(item), query, { forceCaseInsensitive });
435
- if (match !== null) {
436
- ranked.push({ item, score: match.score, indices: match.indices, order });
437
- }
438
- });
439
-
440
- // Stable sort: score desc, original order asc for ties.
441
- ranked.sort((a, b) => b.score - a.score || a.order - b.order);
442
-
443
- return ranked.map(({ item, score, indices }) => ({ item, score, indices }));
444
- }