@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,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
- }