@nebutra/agent-runtime 0.2.0 → 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 (137) hide show
  1. package/LICENSE +21 -676
  2. package/README.md +2 -0
  3. package/dist/adapters/dispatcher-sse.js +1 -0
  4. package/dist/adapters/index.d.ts +24 -5
  5. package/dist/adapters/index.js +28 -0
  6. package/dist/adapters/index.js.map +1 -1
  7. package/dist/adapters/mcp-catalog.js +1 -0
  8. package/dist/adapters/prisma-rollout.js +1 -0
  9. package/dist/chunk-424PT5DM.js +23 -0
  10. package/dist/chunk-424PT5DM.js.map +1 -0
  11. package/dist/{chunk-NN7DATXA.js → chunk-4Y25ZTKI.js} +3 -3
  12. package/dist/chunk-4Y25ZTKI.js.map +1 -0
  13. package/dist/{chunk-BJBBR3QA.js → chunk-D4YAPLOW.js} +4 -4
  14. package/dist/chunk-D4YAPLOW.js.map +1 -0
  15. package/dist/{chunk-ZMYX5VBU.js → chunk-GQZKYWFT.js} +23 -7
  16. package/dist/chunk-GQZKYWFT.js.map +1 -0
  17. package/dist/chunk-KCNN4QUQ.js +255 -0
  18. package/dist/chunk-KCNN4QUQ.js.map +1 -0
  19. package/dist/{chunk-PGGWSUTM.js → chunk-NI4EDT4T.js} +2 -2
  20. package/dist/chunk-NI4EDT4T.js.map +1 -0
  21. package/dist/chunk-Q62VKHIT.js +178 -0
  22. package/dist/chunk-Q62VKHIT.js.map +1 -0
  23. package/dist/{chunk-MUF7ZZTO.js → chunk-R5HOSQUW.js} +2 -1
  24. package/dist/{chunk-MUF7ZZTO.js.map → chunk-R5HOSQUW.js.map} +1 -1
  25. package/dist/{chunk-5N4644PB.js → chunk-SD2ZJ7XG.js} +4 -4
  26. package/dist/cli.d.ts +2 -0
  27. package/dist/cli.js +52 -0
  28. package/dist/cli.js.map +1 -0
  29. package/dist/commands.js +1 -0
  30. package/dist/definitions.js +1 -0
  31. package/dist/dispatcher.js +1 -0
  32. package/dist/durable-turn.js +3 -2
  33. package/dist/hook-pipeline.js +1 -0
  34. package/dist/index.d.ts +8 -85
  35. package/dist/index.js +249 -134
  36. package/dist/index.js.map +1 -1
  37. package/dist/loop.d.ts +2 -2
  38. package/dist/loop.js +3 -2
  39. package/dist/mcp-bridge.d.ts +3 -3
  40. package/dist/mcp-bridge.js +3 -2
  41. package/dist/model.js +1 -0
  42. package/dist/orchestration.d.ts +84 -0
  43. package/dist/orchestration.js +16 -0
  44. package/dist/orchestration.js.map +1 -0
  45. package/dist/policy.js +1 -0
  46. package/dist/protocol.js +1 -0
  47. package/dist/pulsar.d.ts +78 -0
  48. package/dist/pulsar.js +17 -0
  49. package/dist/pulsar.js.map +1 -0
  50. package/dist/rollout-store-persistent.js +1 -0
  51. package/dist/rollout.js +1 -0
  52. package/dist/sandbox.js +2 -1
  53. package/dist/skills.js +2 -1
  54. package/dist/subagents.js +1 -0
  55. package/dist/tools.d.ts +4 -3
  56. package/dist/tools.js +5 -3
  57. package/package.json +84 -27
  58. package/.turbo/turbo-build.log +0 -115
  59. package/.turbo/turbo-test.log +0 -44
  60. package/.turbo/turbo-typecheck.log +0 -4
  61. package/CHANGELOG.md +0 -253
  62. package/dist/chunk-BJBBR3QA.js.map +0 -1
  63. package/dist/chunk-NN7DATXA.js.map +0 -1
  64. package/dist/chunk-PGGWSUTM.js.map +0 -1
  65. package/dist/chunk-ZMYX5VBU.js.map +0 -1
  66. package/src/adapters/dispatcher-sse.test.ts +0 -218
  67. package/src/adapters/dispatcher-sse.ts +0 -222
  68. package/src/adapters/index.ts +0 -18
  69. package/src/adapters/mcp-catalog.test.ts +0 -213
  70. package/src/adapters/mcp-catalog.ts +0 -188
  71. package/src/adapters/prisma-rollout.test.ts +0 -153
  72. package/src/adapters/prisma-rollout.ts +0 -104
  73. package/src/agent-runtime.test.ts +0 -176
  74. package/src/artifact-stream.test.ts +0 -330
  75. package/src/artifact-stream.ts +0 -453
  76. package/src/channel-gateway.test.ts +0 -432
  77. package/src/channel-gateway.ts +0 -357
  78. package/src/code-review.test.ts +0 -501
  79. package/src/code-review.ts +0 -495
  80. package/src/command-suggestions.test.ts +0 -251
  81. package/src/command-suggestions.ts +0 -338
  82. package/src/commands.test.ts +0 -184
  83. package/src/commands.ts +0 -140
  84. package/src/commit-message.test.ts +0 -249
  85. package/src/commit-message.ts +0 -180
  86. package/src/context-compaction.test.ts +0 -522
  87. package/src/context-compaction.ts +0 -434
  88. package/src/definitions.test.ts +0 -78
  89. package/src/definitions.ts +0 -190
  90. package/src/deployment-status.test.ts +0 -215
  91. package/src/deployment-status.ts +0 -227
  92. package/src/design-context.test.ts +0 -195
  93. package/src/design-context.ts +0 -198
  94. package/src/dispatcher.test.ts +0 -234
  95. package/src/dispatcher.ts +0 -189
  96. package/src/durable-turn.test.ts +0 -209
  97. package/src/durable-turn.ts +0 -135
  98. package/src/edit-planner.test.ts +0 -204
  99. package/src/edit-planner.ts +0 -325
  100. package/src/fuzzy-match.test.ts +0 -311
  101. package/src/fuzzy-match.ts +0 -444
  102. package/src/hook-pipeline.test.ts +0 -279
  103. package/src/hook-pipeline.ts +0 -373
  104. package/src/inbound-admission.test.ts +0 -394
  105. package/src/inbound-admission.ts +0 -246
  106. package/src/index.ts +0 -47
  107. package/src/loop.test.ts +0 -161
  108. package/src/loop.ts +0 -211
  109. package/src/mcp-bridge.test.ts +0 -165
  110. package/src/mcp-bridge.ts +0 -76
  111. package/src/memory-provider.test.ts +0 -232
  112. package/src/memory-provider.ts +0 -257
  113. package/src/model.ts +0 -168
  114. package/src/permission-ruleset.test.ts +0 -301
  115. package/src/permission-ruleset.ts +0 -200
  116. package/src/policy.ts +0 -151
  117. package/src/project-repo.test.ts +0 -232
  118. package/src/project-repo.ts +0 -311
  119. package/src/protocol.ts +0 -159
  120. package/src/rollout-store-persistent.test.ts +0 -217
  121. package/src/rollout-store-persistent.ts +0 -166
  122. package/src/rollout.ts +0 -150
  123. package/src/sandbox.ts +0 -113
  124. package/src/session-share.test.ts +0 -360
  125. package/src/session-share.ts +0 -310
  126. package/src/skill-distillation.test.ts +0 -177
  127. package/src/skill-distillation.ts +0 -369
  128. package/src/skills.test.ts +0 -277
  129. package/src/skills.ts +0 -255
  130. package/src/subagents.test.ts +0 -290
  131. package/src/subagents.ts +0 -332
  132. package/src/tools.ts +0 -126
  133. package/src/workbench.test.ts +0 -0
  134. package/src/workbench.ts +0 -0
  135. package/tsconfig.json +0 -12
  136. package/tsup.config.ts +0 -33
  137. /package/dist/{chunk-5N4644PB.js.map → chunk-SD2ZJ7XG.js.map} +0 -0
@@ -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
- }