@nebutra/agent-runtime 0.2.2 → 3.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.
package/README.md CHANGED
@@ -1,13 +1,19 @@
1
1
  # @nebutra/agent-runtime
2
2
 
3
- Status: **WIP**
3
+ Status: **WIP** — Track A live (grammar + gateway demo); Track B Phase 1 docked
4
4
 
5
- > Status: WIP — not yet integrated into a production app. Track-B kernel
6
- > transport and durable-turn queue binding are interface-only.
5
+ | Layer | State |
6
+ |-------|--------|
7
+ | Track A grammar (`runTurn`, policy, rollout, tools) | Shipped; gateway demo route behind `agent-runtime-demo` |
8
+ | Track B adapter (`createCarinaSandbox` → Carina JSON-RPC) | **Phase 1 done** ([#382](https://github.com/Nebutra/Nebutra-Sailor/pull/382)) |
9
+ | Track B host wire (env, ensureSession, `command_exec`, gateway bundle) | **Phase 2a done** (#386) |
10
+ | Track B ship (workspace map, approval resolve, auto-approve, status) | **Phase 2b done** |
11
+ | Product HITL UI (beyond API) | Optional |
12
+ | Default without Carina endpoint | **Fail-closed** (`REFUSING_SANDBOX`) |
7
13
 
8
14
  Multi-tenant **agent-runtime grammar**. A faithful re-expression of a terminal
9
15
  coding-agent's runtime *design* into Sailor's grammar — TypeScript,
10
- multi-tenant, zero infra changes, **no in-process untrusted-code execution**.
16
+ multi-tenant, zero in-process untrusted-code execution.
11
17
 
12
18
  ## Why this exists
13
19
 
@@ -24,9 +30,26 @@ them.
24
30
 
25
31
  - **Track A (this package)** — policy, protocol contract, model, rollout. All
26
32
  tenant-scoped. Runs inside Sailor's TS web runtime.
27
- - **Track B (decoupled, not this repo)** — an optional self-hosted isolator /
28
- kernel sidecar that implements `ExternalSandbox` over the `./protocol`
29
- contract. The protocol is the only coupling seam.
33
+ - **Track B (Carina upstream)** — the **Nebutra/carina** kernel is the sole
34
+ product isolator. Self-deployed, local-first. Sailor docks via
35
+ `createCarinaSandbox` / `ExternalSandbox` — **kernel protocol is maintained
36
+ in Carina**, not reimplemented here.
37
+
38
+ ```ts
39
+ import {
40
+ createCarinaSandbox,
41
+ REFUSING_SANDBOX,
42
+ type ExternalSandbox,
43
+ } from "@nebutra/agent-runtime";
44
+
45
+ // Production: only when a reachable Carina JSON-RPC base URL is configured.
46
+ // Same-host co-deploy (default): unix socket /var/carina/run/daemon.sock
47
+ import { resolveCarinaSandboxFromEnv } from "@nebutra/agent-runtime";
48
+ const sandbox = resolveCarinaSandboxFromEnv();
49
+ // Opt out: CARINA_CODEPLOY=0 | optional HTTP: CARINA_JSONRPC_URL=…
50
+ ```
51
+
52
+ See `docs/architecture/2026-08-03-carina-track-b-upstream.md`.
30
53
 
31
54
  ## Modules
32
55
 
@@ -37,16 +60,16 @@ them.
37
60
  | `./protocol` | JSON-RPC contract | method registry, **tenant+thread serialization scope**, server-initiated approval requests, notifications |
38
61
  | `./tools` | uniform tool/MCP | `ToolRegistry` (registry→router→hooks), `adaptMcpTool` (MCP-as-adapter) |
39
62
  | `./rollout` | event-sourced trace | append-only typed log, replay-to-state, compaction marker, per-item persistence policy |
40
- | `./sandbox` | external-sandbox seam | `ExternalSandbox` delegation interface; fail-closed `REFUSING_SANDBOX` default |
63
+ | `./sandbox` | external-sandbox seam | `ExternalSandbox`; `createCarinaSandbox` (Track B); `REFUSING_SANDBOX` default; `createHttpSandbox` generic helper |
41
64
  | `./adapters/*` | reusable concrete bindings | MCP catalog, Web-standard SSE transport, and Prisma rollout persistence without a separate package |
42
65
 
43
66
  ## Non-negotiables enforced here
44
67
 
45
68
  - **Multi-tenant**: every scope, store key, and dispatch carries `tenantId`;
46
69
  cross-tenant requests can never share a serialization scope.
47
- - **No infra change**: no sandbox runtime, no new datastore — only interfaces.
48
70
  - **No in-process untrusted exec**: the default executor refuses; real
49
- execution is delegated to a decoupled isolator.
71
+ execution is delegated to self-deployed Carina (or another `ExternalSandbox`).
72
+ - **No second kernel**: OS sandbox / capability enforcement stay in Carina.
50
73
 
51
74
  See `docs/capabilities/agent-runtime/` for the capability map and replication
52
75
  guide.
@@ -4,15 +4,15 @@ import {
4
4
  import {
5
5
  replay
6
6
  } from "./chunk-37BBB2P2.js";
7
- import {
8
- RuntimeToolRegistry
9
- } from "./chunk-4Y25ZTKI.js";
10
7
  import {
11
8
  isTurnTerminal
12
9
  } from "./chunk-6EGG2OZC.js";
13
10
  import {
14
11
  DEFAULT_APPROVAL_POLICY
15
12
  } from "./chunk-CGRCUKGT.js";
13
+ import {
14
+ RuntimeToolRegistry
15
+ } from "./chunk-4Y25ZTKI.js";
16
16
 
17
17
  // src/pulsar.ts
18
18
  function newId(prefix) {
@@ -252,4 +252,4 @@ export {
252
252
  PulsarBuilder,
253
253
  Pulsar
254
254
  };
255
- //# sourceMappingURL=chunk-KCNN4QUQ.js.map
255
+ //# sourceMappingURL=chunk-5UGIUYJR.js.map
@@ -0,0 +1,73 @@
1
+ import {
2
+ isCarinaSandbox
3
+ } from "./chunk-VXILZAXK.js";
4
+ import {
5
+ DEFAULT_CAPABILITY_POLICY
6
+ } from "./chunk-CGRCUKGT.js";
7
+ import {
8
+ RuntimeToolRegistry
9
+ } from "./chunk-4Y25ZTKI.js";
10
+
11
+ // src/command-exec.ts
12
+ import { z } from "zod";
13
+ var COMMAND_EXEC_TOOL_NAME = "command_exec";
14
+ var commandExecInputSchema = z.object({
15
+ command: z.string().min(1)
16
+ });
17
+ function registerCommandExecTool(registry, options) {
18
+ const {
19
+ sandbox,
20
+ workspaceRoot,
21
+ capabilityPolicy = DEFAULT_CAPABILITY_POLICY,
22
+ profile,
23
+ approvalMode
24
+ } = options;
25
+ registry.register(
26
+ {
27
+ name: COMMAND_EXEC_TOOL_NAME,
28
+ description: "Run a shell command in the external sandbox (Carina Track B). Never executes in the Sailor web process.",
29
+ inputSchema: commandExecInputSchema
30
+ },
31
+ async (input, ctx) => {
32
+ if (isCarinaSandbox(sandbox)) {
33
+ const root = workspaceRoot?.trim();
34
+ if (!root) {
35
+ throw new Error(
36
+ "command_exec: CARINA_WORKSPACE_ROOT / workspaceRoot required to create a Carina session before command.exec"
37
+ );
38
+ }
39
+ await sandbox.ensureSession({
40
+ threadId: ctx.threadId,
41
+ workspaceRoot: root,
42
+ tenantId: ctx.tenantId,
43
+ ...profile ? { profile } : {},
44
+ ...approvalMode ? { approvalMode } : {}
45
+ });
46
+ }
47
+ const result = await sandbox.exec({
48
+ tenantId: ctx.tenantId,
49
+ threadId: ctx.threadId,
50
+ command: input.command,
51
+ capabilityPolicy
52
+ });
53
+ return {
54
+ exitCode: result.exitCode,
55
+ aggregatedOutput: result.aggregatedOutput,
56
+ executedOn: result.executedOn,
57
+ ...result.decisionId !== void 0 ? { decisionId: result.decisionId } : {}
58
+ };
59
+ }
60
+ );
61
+ }
62
+ function buildSandboxToolRegistry(sandbox, options = {}) {
63
+ const registry = new RuntimeToolRegistry();
64
+ registerCommandExecTool(registry, { sandbox, ...options });
65
+ return registry;
66
+ }
67
+
68
+ export {
69
+ COMMAND_EXEC_TOOL_NAME,
70
+ registerCommandExecTool,
71
+ buildSandboxToolRegistry
72
+ };
73
+ //# sourceMappingURL=chunk-7ELA4SWE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/command-exec.ts"],"sourcesContent":["/**\n * Native command-exec tool that delegates to {@link ExternalSandbox}.\n * Product Track B: pair with {@link createCarinaSandbox} + ensureSession.\n */\n\nimport { z } from \"zod\";\n\nimport { type CapabilityPolicy, DEFAULT_CAPABILITY_POLICY } from \"./policy\";\nimport { type ExternalSandbox, isCarinaSandbox, type SandboxExecResult } from \"./sandbox\";\nimport { RuntimeToolRegistry } from \"./tools\";\n\nexport const COMMAND_EXEC_TOOL_NAME = \"command_exec\" as const;\n\nconst commandExecInputSchema = z.object({\n command: z.string().min(1),\n});\n\nexport type CommandExecToolInput = z.infer<typeof commandExecInputSchema>;\n\nexport type CommandExecToolOutput = {\n readonly exitCode: number;\n readonly aggregatedOutput: string;\n readonly executedOn: string;\n readonly decisionId?: string;\n};\n\nexport type RegisterCommandExecToolOptions = {\n readonly sandbox: ExternalSandbox;\n /**\n * Absolute workspace on the Carina host. Required when sandbox is Carina\n * (session.create). Gateway reads `CARINA_WORKSPACE_ROOT`.\n */\n readonly workspaceRoot?: string;\n readonly capabilityPolicy?: CapabilityPolicy;\n readonly profile?: string;\n readonly approvalMode?: string;\n};\n\n/**\n * Register `command_exec` on a tool registry. For Carina sandboxes, ensures a\n * session for the turn thread before `command.exec`.\n */\nexport function registerCommandExecTool(\n registry: RuntimeToolRegistry,\n options: RegisterCommandExecToolOptions,\n): void {\n const {\n sandbox,\n workspaceRoot,\n capabilityPolicy = DEFAULT_CAPABILITY_POLICY,\n profile,\n approvalMode,\n } = options;\n\n registry.register(\n {\n name: COMMAND_EXEC_TOOL_NAME,\n description:\n \"Run a shell command in the external sandbox (Carina Track B). \" +\n \"Never executes in the Sailor web process.\",\n inputSchema: commandExecInputSchema,\n },\n async (input, ctx): Promise<CommandExecToolOutput> => {\n if (isCarinaSandbox(sandbox)) {\n const root = workspaceRoot?.trim();\n if (!root) {\n throw new Error(\n \"command_exec: CARINA_WORKSPACE_ROOT / workspaceRoot required \" +\n \"to create a Carina session before command.exec\",\n );\n }\n await sandbox.ensureSession({\n threadId: ctx.threadId,\n workspaceRoot: root,\n tenantId: ctx.tenantId,\n ...(profile ? { profile } : {}),\n ...(approvalMode ? { approvalMode } : {}),\n });\n }\n\n const result: SandboxExecResult = await sandbox.exec({\n tenantId: ctx.tenantId,\n threadId: ctx.threadId,\n command: input.command,\n capabilityPolicy,\n });\n\n return {\n exitCode: result.exitCode,\n aggregatedOutput: result.aggregatedOutput,\n executedOn: result.executedOn,\n ...(result.decisionId !== undefined ? { decisionId: result.decisionId } : {}),\n };\n },\n );\n}\n\n/**\n * Build a registry that includes `command_exec` delegated to the given sandbox.\n * Prefer this only when a real isolator is configured; with\n * {@link REFUSING_SANDBOX} the tool will throw NoExecutorConfiguredError on use.\n */\nexport function buildSandboxToolRegistry(\n sandbox: ExternalSandbox,\n options: Omit<RegisterCommandExecToolOptions, \"sandbox\"> = {},\n): RuntimeToolRegistry {\n const registry = new RuntimeToolRegistry();\n registerCommandExecTool(registry, { sandbox, ...options });\n return registry;\n}\n"],"mappings":";;;;;;;;;;;AAKA,SAAS,SAAS;AAMX,IAAM,yBAAyB;AAEtC,IAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAC3B,CAAC;AA2BM,SAAS,wBACd,UACA,SACM;AACN,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,WAAS;AAAA,IACP;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MAEF,aAAa;AAAA,IACf;AAAA,IACA,OAAO,OAAO,QAAwC;AACpD,UAAI,gBAAgB,OAAO,GAAG;AAC5B,cAAM,OAAO,eAAe,KAAK;AACjC,YAAI,CAAC,MAAM;AACT,gBAAM,IAAI;AAAA,YACR;AAAA,UAEF;AAAA,QACF;AACA,cAAM,QAAQ,cAAc;AAAA,UAC1B,UAAU,IAAI;AAAA,UACd,eAAe;AAAA,UACf,UAAU,IAAI;AAAA,UACd,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,UAC7B,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,QACzC,CAAC;AAAA,MACH;AAEA,YAAM,SAA4B,MAAM,QAAQ,KAAK;AAAA,QACnD,UAAU,IAAI;AAAA,QACd,UAAU,IAAI;AAAA,QACd,SAAS,MAAM;AAAA,QACf;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,UAAU,OAAO;AAAA,QACjB,kBAAkB,OAAO;AAAA,QACzB,YAAY,OAAO;AAAA,QACnB,GAAI,OAAO,eAAe,SAAY,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AACF;AAOO,SAAS,yBACd,SACA,UAA2D,CAAC,GACvC;AACrB,QAAM,WAAW,IAAI,oBAAoB;AACzC,0BAAwB,UAAU,EAAE,SAAS,GAAG,QAAQ,CAAC;AACzD,SAAO;AACT;","names":[]}