@nebutra/agent-runtime 0.2.1 → 2.0.0

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