@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,166 +0,0 @@
1
- /**
2
- * Production-grade, tenant-scoped {@link RolloutStore} backed by an injectable
3
- * persistence port. This package depends on NO datastore: a Postgres /
4
- * ClickHouse / `@nebutra/db` / `@nebutra/audit` adapter satisfies
5
- * {@link RolloutPersistencePort} from the outside, so the "no infra change"
6
- * promise stays honest — swap the adapter, keep the interface.
7
- *
8
- * Tenancy is structural: `tenantId` is part of the storage key AND Zod-validated
9
- * on every read and write. Cross-tenant access is impossible by construction.
10
- * The store fails closed — empty identifiers and malformed stored payloads
11
- * raise typed errors; records are never silently dropped.
12
- */
13
-
14
- import { z } from "zod";
15
- import type { RolloutLine, RolloutStore } from "./rollout";
16
-
17
- /**
18
- * Minimal append-only persistence seam. Implementations MUST persist records
19
- * durably and return them ascending by `seq` for a given (tenant, thread).
20
- */
21
- export interface RolloutPersistencePort {
22
- put(record: {
23
- tenantId: string;
24
- threadId: string;
25
- seq: number;
26
- at: string;
27
- payload: string;
28
- }): Promise<void>;
29
- /** Records for one (tenant, thread), ascending by seq. */
30
- list(tenantId: string, threadId: string): Promise<{ seq: number; payload: string }[]>;
31
- }
32
-
33
- /** Raised when a stored payload cannot be faithfully reconstructed. */
34
- export class RoundTripError extends Error {
35
- constructor(message: string, options?: { cause?: unknown }) {
36
- super(message, options);
37
- this.name = "RoundTripError";
38
- }
39
- }
40
-
41
- const tenantId = z.string().min(1, "tenantId is required");
42
- const threadId = z.string().min(1, "threadId is required");
43
- const at = z.string().min(1);
44
-
45
- const turnConfigSchema = z.object({
46
- model: z.string(),
47
- provider: z.string(),
48
- approvalPolicy: z.string(),
49
- capabilityPolicy: z.string(),
50
- reasoningEffort: z.enum(["low", "medium", "high"]).optional(),
51
- });
52
-
53
- const threadEventSchema = z.custom<RolloutLine extends { event: infer E } ? E : never>(
54
- (v) => typeof v === "object" && v !== null && typeof (v as { type?: unknown }).type === "string",
55
- { message: "invalid ThreadEvent" },
56
- );
57
-
58
- const rolloutLineSchema: z.ZodType<RolloutLine> = z.discriminatedUnion("type", [
59
- z.object({
60
- tenantId,
61
- threadId,
62
- type: z.literal("session_meta"),
63
- config: turnConfigSchema,
64
- at,
65
- }),
66
- z.object({
67
- tenantId,
68
- threadId,
69
- type: z.literal("event"),
70
- event: threadEventSchema,
71
- at,
72
- }),
73
- z.object({
74
- tenantId,
75
- threadId,
76
- type: z.literal("turn_context"),
77
- config: turnConfigSchema,
78
- at,
79
- }),
80
- z.object({
81
- tenantId,
82
- threadId,
83
- type: z.literal("compacted"),
84
- summary: z.string(),
85
- droppedThrough: z.string(),
86
- at,
87
- }),
88
- ]) as z.ZodType<RolloutLine>;
89
-
90
- const tenantThreadSchema = z.object({ tenantId, threadId });
91
-
92
- export class PersistentRolloutStore implements RolloutStore {
93
- readonly #port: RolloutPersistencePort;
94
- /**
95
- * Per-(tenant,thread) seq allocation chained through a promise so concurrent
96
- * `append` calls to the same thread serialize and never collide on seq —
97
- * without blocking appends to other threads.
98
- */
99
- readonly #tails = new Map<string, Promise<number>>();
100
-
101
- constructor(port: RolloutPersistencePort) {
102
- this.#port = port;
103
- }
104
-
105
- #key(t: string, th: string): string {
106
- return `${t}::${th}`;
107
- }
108
-
109
- async append(line: RolloutLine): Promise<void> {
110
- const validated = rolloutLineSchema.parse(line);
111
- const key = this.#key(validated.tenantId, validated.threadId);
112
-
113
- const prev = this.#tails.get(key) ?? Promise.resolve(-1);
114
- const next = prev.then(async (last) => {
115
- const seq = last + 1;
116
- await this.#port.put({
117
- tenantId: validated.tenantId,
118
- threadId: validated.threadId,
119
- seq,
120
- at: validated.at,
121
- payload: JSON.stringify(validated),
122
- });
123
- return seq;
124
- });
125
- // Keep the chain advancing even if a put fails (fail-closed for the caller,
126
- // but the next append must not deadlock on a rejected tail).
127
- this.#tails.set(
128
- key,
129
- next.catch(() => (this.#tails.get(key) === next ? -1 : next)),
130
- );
131
- await next;
132
- }
133
-
134
- async read(tenantIdArg: string, threadIdArg: string): Promise<readonly RolloutLine[]> {
135
- const { tenantId: t, threadId: th } = tenantThreadSchema.parse({
136
- tenantId: tenantIdArg,
137
- threadId: threadIdArg,
138
- });
139
-
140
- const records = await this.#port.list(t, th);
141
- const ordered = [...records].sort((a, b) => a.seq - b.seq);
142
-
143
- return ordered.map((r) => {
144
- let parsed: unknown;
145
- try {
146
- parsed = JSON.parse(r.payload);
147
- } catch (cause) {
148
- throw new RoundTripError(`malformed stored payload at seq ${r.seq} for ${t}::${th}`, {
149
- cause,
150
- });
151
- }
152
- const result = rolloutLineSchema.safeParse(parsed);
153
- if (!result.success) {
154
- throw new RoundTripError(
155
- `structurally-invalid stored payload at seq ${r.seq} for ${t}::${th}`,
156
- { cause: result.error },
157
- );
158
- }
159
- const reconstructed = result.data;
160
- if (reconstructed.tenantId !== t || reconstructed.threadId !== th) {
161
- throw new RoundTripError(`tenant/thread mismatch at seq ${r.seq} for ${t}::${th}`);
162
- }
163
- return reconstructed;
164
- });
165
- }
166
- }
package/src/rollout.ts DELETED
@@ -1,150 +0,0 @@
1
- /**
2
- * Event-sourced rollout / session trace (WRAP — capability #7).
3
- *
4
- * Faithful re-expression of the upstream rollout model: an append-only typed
5
- * event log, state derived by replay, a first-class context-compaction marker
6
- * (so replay stays bounded), and a per-item persistence policy that sanitizes
7
- * and size-caps outputs before they are durable.
8
- *
9
- * Multi-tenant adaptation: every rollout line carries `tenantId`; the store
10
- * interface is tenant-scoped so a Postgres/ClickHouse-backed implementation
11
- * (via `@nebutra/audit` providers / `@nebutra/db`) replaces the upstream
12
- * single-host jsonl + embedded-SQLite assumptions. Only an in-memory
13
- * reference store ships here.
14
- */
15
-
16
- import type { ThreadEvent, ThreadItem, TurnConfig } from "./model";
17
-
18
- /** Cap mirrors the upstream persisted-output ceiling. */
19
- export const PERSISTED_OUTPUT_MAX_BYTES = 10_000;
20
-
21
- export type EventPersistenceMode = "limited" | "extended";
22
-
23
- /** One durable line in a thread's append-only log. */
24
- export type RolloutLine =
25
- | {
26
- readonly tenantId: string;
27
- readonly threadId: string;
28
- readonly type: "session_meta";
29
- readonly config: TurnConfig;
30
- readonly at: string;
31
- }
32
- | {
33
- readonly tenantId: string;
34
- readonly threadId: string;
35
- readonly type: "event";
36
- readonly event: ThreadEvent;
37
- readonly at: string;
38
- }
39
- | {
40
- readonly tenantId: string;
41
- readonly threadId: string;
42
- readonly type: "turn_context";
43
- readonly config: TurnConfig;
44
- readonly at: string;
45
- }
46
- | {
47
- readonly tenantId: string;
48
- readonly threadId: string;
49
- readonly type: "compacted";
50
- readonly summary: string;
51
- readonly droppedThrough: string;
52
- readonly at: string;
53
- };
54
-
55
- /**
56
- * Persistence policy: decide per item whether it is durable, and sanitize +
57
- * size-cap its output first. Executive markers are always persisted.
58
- */
59
- export function isPersisted(item: ThreadItem, mode: EventPersistenceMode): boolean {
60
- if (item.type === "reasoning") return mode === "extended";
61
- return true;
62
- }
63
-
64
- /** Truncate aggregated command output to the persisted ceiling. */
65
- export function sanitizeForPersist(item: ThreadItem): ThreadItem {
66
- if (item.type !== "command_execution") return item;
67
- const bytes = Buffer.byteLength(item.aggregatedOutput, "utf8");
68
- if (bytes <= PERSISTED_OUTPUT_MAX_BYTES) return item;
69
- const truncated = Buffer.from(item.aggregatedOutput, "utf8")
70
- .subarray(0, PERSISTED_OUTPUT_MAX_BYTES)
71
- .toString("utf8");
72
- return { ...item, aggregatedOutput: `${truncated}\n…[truncated]` };
73
- }
74
-
75
- /** Tenant-scoped append-only store. Replace the impl, keep the interface. */
76
- export interface RolloutStore {
77
- append(line: RolloutLine): Promise<void>;
78
- /** Lines for a thread, in append order. */
79
- read(tenantId: string, threadId: string): Promise<readonly RolloutLine[]>;
80
- }
81
-
82
- /** In-memory reference store (dev/test only — never multi-instance safe). */
83
- export class InMemoryRolloutStore implements RolloutStore {
84
- readonly #lines = new Map<string, RolloutLine[]>();
85
-
86
- #key(tenantId: string, threadId: string): string {
87
- return `${tenantId}::${threadId}`;
88
- }
89
-
90
- async append(line: RolloutLine): Promise<void> {
91
- const key = this.#key(line.tenantId, line.threadId);
92
- const list = this.#lines.get(key) ?? [];
93
- list.push(line);
94
- this.#lines.set(key, list);
95
- }
96
-
97
- async read(tenantId: string, threadId: string): Promise<readonly RolloutLine[]> {
98
- return [...(this.#lines.get(this.#key(tenantId, threadId)) ?? [])];
99
- }
100
- }
101
-
102
- /** Derived thread state — the projection rebuilt by replaying the log. */
103
- export interface ThreadProjection {
104
- readonly tenantId: string;
105
- readonly threadId: string;
106
- config?: TurnConfig;
107
- readonly items: ThreadItem[];
108
- /** Marks where a compaction summary replaced earlier history. */
109
- compactionSummary?: string;
110
- }
111
-
112
- /**
113
- * Replay an append-only log into derived state. A `compacted` line bounds
114
- * replay: items prior to it are represented by the summary, not re-applied.
115
- */
116
- export function replay(lines: readonly RolloutLine[]): ThreadProjection | null {
117
- const first = lines[0];
118
- if (!first) return null;
119
- const projection: ThreadProjection = {
120
- tenantId: first.tenantId,
121
- threadId: first.threadId,
122
- items: [],
123
- };
124
- for (const line of lines) {
125
- switch (line.type) {
126
- case "session_meta":
127
- case "turn_context":
128
- projection.config = line.config;
129
- break;
130
- case "compacted":
131
- projection.compactionSummary = line.summary;
132
- projection.items.length = 0;
133
- break;
134
- case "event": {
135
- const e = line.event;
136
- if (e.type === "item.completed" || e.type === "item.started") {
137
- const idx = projection.items.findIndex((i) => i.id === e.item.id);
138
- if (idx >= 0) projection.items[idx] = e.item;
139
- else projection.items.push(e.item);
140
- } else if (e.type === "item.updated") {
141
- const idx = projection.items.findIndex((i) => i.id === e.item.id);
142
- if (idx >= 0) projection.items[idx] = e.item;
143
- else projection.items.push(e.item);
144
- }
145
- break;
146
- }
147
- }
148
- }
149
- return projection;
150
- }
package/src/sandbox.ts DELETED
@@ -1,113 +0,0 @@
1
- /**
2
- * External-sandbox delegation seam (PORT — capability #11).
3
- *
4
- * Governance decision (dual-track, ExternalSandbox posture): this multi-tenant
5
- * web runtime NEVER executes untrusted code itself and introduces no isolation
6
- * infrastructure. It only:
7
- * 1. carries the capability policy (see ./policy),
8
- * 2. delegates execution to an external isolator behind this interface
9
- * (a self-hosted kernel sidecar — Track B — or any other executor),
10
- * 3. records the outcome as a `command_execution` item (see ./model).
11
- *
12
- * The upstream OS enforcers (Seatbelt / Landlock / bubblewrap / Windows
13
- * restricted token) are deliberately NOT ported — they are single-host and
14
- * out of scope for a multi-tenant web product.
15
- */
16
-
17
- import type { CapabilityPolicy } from "./policy";
18
-
19
- export interface SandboxExecRequest {
20
- /** Mandatory tenant scope — every delegated exec is tenant-bound. */
21
- readonly tenantId: string;
22
- readonly threadId: string;
23
- readonly command: string;
24
- /** The capabilities the external isolator must honor. */
25
- readonly capabilityPolicy: CapabilityPolicy;
26
- /** Optional inputs the executor should materialize before running. */
27
- readonly inputs?: Readonly<Record<string, string>>;
28
- }
29
-
30
- export interface SandboxExecResult {
31
- readonly exitCode: number;
32
- readonly aggregatedOutput: string;
33
- /** Identifier of the isolator that actually ran the command. */
34
- readonly executedOn: string;
35
- }
36
-
37
- /**
38
- * The only thing Track A depends on for execution. Implemented by a decoupled
39
- * isolator over the ./protocol contract; never implemented in-process here.
40
- */
41
- export interface ExternalSandbox {
42
- exec(request: SandboxExecRequest): Promise<SandboxExecResult>;
43
- }
44
-
45
- /**
46
- * Fail-closed default. Wiring this in production requires a real
47
- * {@link ExternalSandbox}; absent one, untrusted code never runs — by design.
48
- */
49
- export class NoExecutorConfiguredError extends Error {
50
- constructor() {
51
- super(
52
- "No ExternalSandbox configured. This runtime never executes untrusted " +
53
- "code in-process; delegate to a decoupled isolator (Track B).",
54
- );
55
- this.name = "NoExecutorConfiguredError";
56
- }
57
- }
58
-
59
- export const REFUSING_SANDBOX: ExternalSandbox = {
60
- async exec(): Promise<SandboxExecResult> {
61
- throw new NoExecutorConfiguredError();
62
- },
63
- };
64
-
65
- export class SandboxDelegationError extends Error {
66
- constructor(
67
- message: string,
68
- readonly status: number,
69
- ) {
70
- super(message);
71
- this.name = "SandboxDelegationError";
72
- }
73
- }
74
-
75
- /**
76
- * The concrete Track-B coupling: delegate execution over HTTP to the decoupled
77
- * Rust isolator (`backends/rust/sandbox`, `POST /api/v1/sandbox/exec`). The
78
- * isolator is fail-closed; a non-2xx (e.g. 403 refusal) is surfaced as an
79
- * error and never coerced into a fabricated result.
80
- */
81
- export function createHttpSandbox(
82
- baseUrl: string,
83
- fetchImpl: typeof fetch = fetch,
84
- ): ExternalSandbox {
85
- const endpoint = `${baseUrl.replace(/\/$/, "")}/api/v1/sandbox/exec`;
86
- return {
87
- async exec(request: SandboxExecRequest): Promise<SandboxExecResult> {
88
- const response = await fetchImpl(endpoint, {
89
- method: "POST",
90
- headers: { "content-type": "application/json" },
91
- body: JSON.stringify(request),
92
- });
93
- if (!response.ok) {
94
- const detail = await response.text().catch(() => "");
95
- throw new SandboxDelegationError(
96
- `Isolator refused execution (${response.status}): ${detail}`,
97
- response.status,
98
- );
99
- }
100
- return (await response.json()) as SandboxExecResult;
101
- },
102
- };
103
- }
104
-
105
- /** Guard: reject the most dangerous posture unless explicitly opted in. */
106
- export function assertSafePosture(policy: CapabilityPolicy, allowDanger = false): void {
107
- if (policy.kind === "danger_full_access" && !allowDanger) {
108
- throw new Error(
109
- "danger_full_access capability policy refused: not permitted for " +
110
- "multi-tenant delegation without explicit opt-in.",
111
- );
112
- }
113
- }