@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,453 +0,0 @@
1
- /**
2
- * Streaming artifact/action protocol.
3
- *
4
- * Faithful re-expression of an open-source web-app-builder's streamed-plan
5
- * DSL: the model emits text containing `<artifact>` blocks that wrap an
6
- * ordered list of `<action>` blocks. This module provides:
7
- *
8
- * 1. A neutral `PlanAction` discriminated union (file / shell / start /
9
- * build / data).
10
- * 2. An incremental, chunk-boundary-safe `ArtifactStreamParser`.
11
- * 3. A delegating `ActionRunner` state machine (no in-process exec, no
12
- * host FS — all side effects go through injected ports).
13
- *
14
- * Multi-tenant, fail-closed: a `tenantId` is mandatory at every entry point;
15
- * an empty tenant throws, and per-tenant runner state is isolated.
16
- */
17
-
18
- import { z } from "zod";
19
-
20
- // ── Action union ─────────────────────────────────────────────────────────────
21
-
22
- export interface FileAction {
23
- readonly type: "file";
24
- readonly filePath: string;
25
- readonly content: string;
26
- }
27
-
28
- export interface ShellAction {
29
- readonly type: "shell";
30
- readonly content: string;
31
- }
32
-
33
- export interface StartAction {
34
- readonly type: "start";
35
- readonly content: string;
36
- }
37
-
38
- export interface BuildAction {
39
- readonly type: "build";
40
- readonly content: string;
41
- }
42
-
43
- export interface DataAction {
44
- readonly type: "data";
45
- readonly operation: "migration" | "query";
46
- readonly filePath?: string | undefined;
47
- readonly projectId?: string | undefined;
48
- readonly content: string;
49
- }
50
-
51
- export type PlanAction = FileAction | ShellAction | StartAction | BuildAction | DataAction;
52
-
53
- const DataActionSchema = z.object({
54
- type: z.literal("data"),
55
- operation: z.union([z.literal("migration"), z.literal("query")]),
56
- filePath: z.string().optional(),
57
- projectId: z.string().optional(),
58
- content: z.string(),
59
- });
60
-
61
- // ── Tenant context ───────────────────────────────────────────────────────────
62
-
63
- export interface TenantContext {
64
- readonly tenantId: string;
65
- }
66
-
67
- function assertTenant(ctx: TenantContext): string {
68
- const tenantId = ctx?.tenantId;
69
- if (typeof tenantId !== "string" || tenantId.trim().length === 0) {
70
- throw new Error("artifact-stream: tenantId is mandatory and must be non-empty (fail-closed)");
71
- }
72
- return tenantId;
73
- }
74
-
75
- // ── Pure helpers ─────────────────────────────────────────────────────────────
76
-
77
- /**
78
- * Strip a single ```lang ... ``` fenced wrapper if (and only if) the trimmed
79
- * content opens with a fence on its own line and ends with a closing fence.
80
- * Inner fences are preserved (single-wrapper semantics).
81
- */
82
- export function stripFencedWrapper(input: string): string {
83
- const trimmed = input.trim();
84
- const opener = /^```[^\n]*\n/;
85
- const match = opener.exec(trimmed);
86
- if (match === null || !trimmed.endsWith("```")) {
87
- return input;
88
- }
89
- const body = trimmed.slice(match[0].length, trimmed.length - 3);
90
- return body.replace(/\n$/, "");
91
- }
92
-
93
- /** Unescape the `&lt;` / `&gt;` entities the model emits inside action bodies. */
94
- export function unescapeEntities(input: string): string {
95
- return input.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
96
- }
97
-
98
- function normalizeActionContent(raw: string): string {
99
- return unescapeEntities(stripFencedWrapper(raw));
100
- }
101
-
102
- // ── Parser ───────────────────────────────────────────────────────────────────
103
-
104
- export interface ArtifactEvent {
105
- readonly tenantId: string;
106
- readonly messageId: string;
107
- readonly artifactId: string;
108
- readonly title: string;
109
- }
110
-
111
- export interface ActionEvent {
112
- readonly tenantId: string;
113
- readonly messageId: string;
114
- readonly artifactId: string;
115
- readonly actionId: string;
116
- readonly action: PlanAction;
117
- }
118
-
119
- export interface ArtifactStreamCallbacks {
120
- readonly onArtifactOpen?: ((e: ArtifactEvent) => void) | undefined;
121
- readonly onArtifactClose?: ((e: ArtifactEvent) => void) | undefined;
122
- readonly onActionOpen?: ((e: ActionEvent) => void) | undefined;
123
- readonly onActionStream?: ((e: ActionEvent) => void) | undefined;
124
- readonly onActionClose?: ((e: ActionEvent) => void) | undefined;
125
- }
126
-
127
- interface ParseState {
128
- buffer: string;
129
- position: number;
130
- insideArtifact: boolean;
131
- insideAction: boolean;
132
- artifactCounter: number;
133
- currentArtifact?: { id: string; title: string } | undefined;
134
- currentAction?: { actionId: string; attrs: Record<string, string> } | undefined;
135
- streamedLength: number;
136
- opened: boolean;
137
- }
138
-
139
- function createState(): ParseState {
140
- return {
141
- buffer: "",
142
- position: 0,
143
- insideArtifact: false,
144
- insideAction: false,
145
- artifactCounter: 0,
146
- streamedLength: 0,
147
- opened: false,
148
- };
149
- }
150
-
151
- function parseAttrs(tag: string): Record<string, string> {
152
- const attrs: Record<string, string> = {};
153
- const re = /(\w+)="([^"]*)"/g;
154
- let m: RegExpExecArray | null = re.exec(tag);
155
- while (m !== null) {
156
- attrs[m[1] as string] = m[2] as string;
157
- m = re.exec(tag);
158
- }
159
- return attrs;
160
- }
161
-
162
- function buildAction(attrs: Record<string, string>, content: string): PlanAction {
163
- const type = attrs.type;
164
- const normalized = normalizeActionContent(content);
165
- switch (type) {
166
- case "file":
167
- return { type: "file", filePath: attrs.filePath ?? "", content: normalized };
168
- case "shell":
169
- return { type: "shell", content: normalized };
170
- case "start":
171
- return { type: "start", content: normalized };
172
- case "build":
173
- return { type: "build", content: normalized };
174
- case "data": {
175
- const candidate: Record<string, unknown> = {
176
- type: "data",
177
- operation: attrs.operation === "query" ? "query" : "migration",
178
- content: normalized,
179
- };
180
- if (attrs.filePath !== undefined) candidate.filePath = attrs.filePath;
181
- if (attrs.projectId !== undefined) candidate.projectId = attrs.projectId;
182
- return DataActionSchema.parse(candidate) as DataAction;
183
- }
184
- default:
185
- throw new Error(`artifact-stream: unknown action type "${String(type)}"`);
186
- }
187
- }
188
-
189
- /**
190
- * Incremental parser. State is keyed by `messageId` so independent streams
191
- * never bleed into each other; a tag split across a chunk boundary is
192
- * resumed from `position` and never re-emitted.
193
- */
194
- export class ArtifactStreamParser {
195
- private readonly cb: ArtifactStreamCallbacks;
196
- private readonly states = new Map<string, ParseState>();
197
-
198
- constructor(callbacks: ArtifactStreamCallbacks) {
199
- this.cb = callbacks;
200
- }
201
-
202
- parse(messageId: string, ctx: TenantContext, chunk: string): void {
203
- const tenantId = assertTenant(ctx);
204
- const state = this.states.get(messageId) ?? createState();
205
- this.states.set(messageId, state);
206
- state.buffer += chunk;
207
- this.drain(messageId, tenantId, state);
208
- }
209
-
210
- private drain(messageId: string, tenantId: string, s: ParseState): void {
211
- // Loop while we can make forward progress; stop when the next needed
212
- // token is incomplete (split across the next chunk boundary).
213
- for (;;) {
214
- const rest = s.buffer.slice(s.position);
215
-
216
- if (!s.insideArtifact) {
217
- const open = rest.indexOf("<artifact");
218
- if (open === -1) return;
219
- const end = rest.indexOf(">", open);
220
- if (end === -1) return; // tag continues in a later chunk
221
- const tag = rest.slice(open, end + 1);
222
- const attrs = parseAttrs(tag);
223
- s.position += end + 1;
224
- s.insideArtifact = true;
225
- s.currentArtifact = { id: attrs.id ?? "", title: attrs.title ?? "" };
226
- this.cb.onArtifactOpen?.({
227
- tenantId,
228
- messageId,
229
- artifactId: s.currentArtifact.id,
230
- title: s.currentArtifact.title,
231
- });
232
- continue;
233
- }
234
-
235
- if (!s.insideAction) {
236
- const close = rest.indexOf("</artifact>");
237
- const open = rest.indexOf("<action");
238
- if (open === -1 && close === -1) return;
239
- if (close !== -1 && (open === -1 || close < open)) {
240
- s.position += close + "</artifact>".length;
241
- const art = s.currentArtifact;
242
- s.insideArtifact = false;
243
- s.currentArtifact = undefined;
244
- if (art !== undefined) {
245
- this.cb.onArtifactClose?.({
246
- tenantId,
247
- messageId,
248
- artifactId: art.id,
249
- title: art.title,
250
- });
251
- }
252
- continue;
253
- }
254
- const end = rest.indexOf(">", open);
255
- if (end === -1) return; // open tag split across chunks
256
- const tag = rest.slice(open, end + 1);
257
- const attrs = parseAttrs(tag);
258
- const actionId = String(s.artifactCounter);
259
- s.artifactCounter += 1;
260
- s.position += end + 1;
261
- s.insideAction = true;
262
- s.streamedLength = 0;
263
- s.currentAction = { actionId, attrs };
264
- const art = s.currentArtifact;
265
- if (
266
- art !== undefined &&
267
- (attrs.type === "file" ||
268
- attrs.type === "shell" ||
269
- attrs.type === "start" ||
270
- attrs.type === "build" ||
271
- attrs.type === "data")
272
- ) {
273
- this.cb.onActionOpen?.({
274
- tenantId,
275
- messageId,
276
- artifactId: art.id,
277
- actionId,
278
- action: buildAction(attrs, ""),
279
- });
280
- }
281
- continue;
282
- }
283
-
284
- // Inside an action: look for its close tag.
285
- const closeIdx = rest.indexOf("</action>");
286
- const act = s.currentAction;
287
- const art = s.currentArtifact;
288
- if (closeIdx === -1) {
289
- // Partial content — stream the newly-seen portion (raw, un-normalized
290
- // since the closing fence may not have arrived yet).
291
- const partial = rest;
292
- if (partial.length > s.streamedLength && act !== undefined && art !== undefined) {
293
- s.streamedLength = partial.length;
294
- this.cb.onActionStream?.({
295
- tenantId,
296
- messageId,
297
- artifactId: art.id,
298
- actionId: act.actionId,
299
- action: buildAction(act.attrs, partial),
300
- });
301
- }
302
- return;
303
- }
304
-
305
- const content = rest.slice(0, closeIdx);
306
- s.position += closeIdx + "</action>".length;
307
- s.insideAction = false;
308
- if (act !== undefined && art !== undefined) {
309
- this.cb.onActionClose?.({
310
- tenantId,
311
- messageId,
312
- artifactId: art.id,
313
- actionId: act.actionId,
314
- action: buildAction(act.attrs, content),
315
- });
316
- }
317
- s.currentAction = undefined;
318
- }
319
- }
320
- }
321
-
322
- // ── Runner ───────────────────────────────────────────────────────────────────
323
-
324
- export type ActionStatus = "pending" | "running" | "complete" | "aborted" | "failed";
325
-
326
- export interface ActionState {
327
- readonly action: PlanAction;
328
- readonly status: ActionStatus;
329
- readonly executed: boolean;
330
- readonly error?: string | undefined;
331
- }
332
-
333
- export interface ActionPorts {
334
- readonly writeFile: (action: FileAction, ctx: TenantContext) => Promise<void>;
335
- readonly runShell: (
336
- action: ShellAction | StartAction | BuildAction,
337
- ctx: TenantContext,
338
- ) => Promise<{ exitCode: number; output: string }>;
339
- readonly dataOp: (action: DataAction, ctx: TenantContext) => Promise<void>;
340
- }
341
-
342
- export interface RunQueueOptions {
343
- readonly continueOnError?: boolean | undefined;
344
- }
345
-
346
- function setStatus(
347
- state: ActionState,
348
- status: ActionStatus,
349
- patch: { executed?: boolean; error?: string } = {},
350
- ): ActionState {
351
- return {
352
- action: state.action,
353
- status,
354
- executed: patch.executed ?? state.executed,
355
- error: patch.error ?? state.error,
356
- };
357
- }
358
-
359
- const ABORTABLE: ReadonlySet<ActionStatus> = new Set(["pending", "running"]);
360
-
361
- export class ActionRunner {
362
- private readonly ports: ActionPorts;
363
- private readonly ctx: TenantContext;
364
- private readonly tenantId: string;
365
- private readonly map = new Map<string, ActionState>();
366
- private readonly order: string[] = [];
367
-
368
- constructor(ports: ActionPorts, ctx: TenantContext) {
369
- this.tenantId = assertTenant(ctx);
370
- this.ports = ports;
371
- this.ctx = { tenantId: this.tenantId };
372
- }
373
-
374
- register(actionId: string, action: PlanAction): void {
375
- if (this.map.has(actionId)) {
376
- throw new Error(`artifact-stream: action "${actionId}" already registered`);
377
- }
378
- this.map.set(actionId, { action, status: "pending", executed: false });
379
- this.order.push(actionId);
380
- }
381
-
382
- get(actionId: string): ActionState {
383
- const state = this.map.get(actionId);
384
- if (state === undefined) {
385
- throw new Error(
386
- `artifact-stream: action "${actionId}" not found for tenant "${this.tenantId}"`,
387
- );
388
- }
389
- return state;
390
- }
391
-
392
- abort(actionId: string): void {
393
- const state = this.get(actionId);
394
- if (!ABORTABLE.has(state.status)) {
395
- throw new Error(
396
- `artifact-stream: cannot abort action "${actionId}" from status "${state.status}"`,
397
- );
398
- }
399
- this.map.set(actionId, setStatus(state, "aborted"));
400
- }
401
-
402
- async runAction(actionId: string): Promise<void> {
403
- const state = this.get(actionId);
404
- if (state.status !== "pending") {
405
- throw new Error(
406
- `artifact-stream: illegal transition for "${actionId}": ${state.status} -> running`,
407
- );
408
- }
409
- this.map.set(actionId, setStatus(state, "running"));
410
- try {
411
- await this.dispatch(state.action);
412
- this.map.set(actionId, setStatus(this.get(actionId), "complete", { executed: true }));
413
- } catch (error) {
414
- const message = error instanceof Error ? error.message : String(error);
415
- this.map.set(
416
- actionId,
417
- setStatus(this.get(actionId), "failed", { executed: true, error: message }),
418
- );
419
- }
420
- }
421
-
422
- async runQueue(options: RunQueueOptions = {}): Promise<void> {
423
- const continueOnError = options.continueOnError ?? false;
424
- for (const actionId of this.order) {
425
- const state = this.get(actionId);
426
- if (state.status !== "pending") continue;
427
- await this.runAction(actionId);
428
- if (this.get(actionId).status === "failed" && !continueOnError) {
429
- return;
430
- }
431
- }
432
- }
433
-
434
- private async dispatch(action: PlanAction): Promise<void> {
435
- switch (action.type) {
436
- case "file":
437
- await this.ports.writeFile(action, this.ctx);
438
- return;
439
- case "shell":
440
- case "start":
441
- case "build":
442
- await this.ports.runShell(action, this.ctx);
443
- return;
444
- case "data":
445
- await this.ports.dataOp(action, this.ctx);
446
- return;
447
- default: {
448
- const exhaustive: never = action;
449
- throw new Error(`artifact-stream: unhandled action ${JSON.stringify(exhaustive)}`);
450
- }
451
- }
452
- }
453
- }