nawabari 0.3.0 → 0.5.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 +136 -7
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +333 -40
- package/dist/cli.js.map +1 -1
- package/dist/contract.js +50 -0
- package/dist/contract.js.map +1 -1
- package/dist/domain/errors.d.ts +1 -1
- package/dist/domain/errors.js +4 -1
- package/dist/domain/errors.js.map +1 -1
- package/dist/domain/sandbox-launcher.d.ts +35 -0
- package/dist/domain/sandbox-launcher.js +679 -0
- package/dist/domain/sandbox-launcher.js.map +1 -0
- package/dist/domain/sandbox.d.ts +190 -0
- package/dist/domain/sandbox.js +363 -0
- package/dist/domain/sandbox.js.map +1 -0
- package/dist/domain/session-backend.d.ts +2 -1
- package/dist/domain/session-backend.js +71 -1
- package/dist/domain/session-backend.js.map +1 -1
- package/dist/domain/session.d.ts +54 -0
- package/dist/domain/session.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/git.d.ts +25 -0
- package/dist/git.js +75 -0
- package/dist/git.js.map +1 -1
- package/dist/registry/atomic.d.ts +24 -0
- package/dist/registry/atomic.js +35 -2
- package/dist/registry/atomic.js.map +1 -1
- package/dist/registry/errors.d.ts +1 -1
- package/dist/registry/errors.js +1 -0
- package/dist/registry/errors.js.map +1 -1
- package/dist/session-registry.d.ts +120 -0
- package/dist/session-registry.js +456 -92
- package/dist/session-registry.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -5,10 +5,51 @@ agents. It gives each active session one exclusively owned worktree and one
|
|
|
5
5
|
mutable branch. It works without GitHub, `gh`, a network connection, Mottainai,
|
|
6
6
|
or a particular agent runtime.
|
|
7
7
|
|
|
8
|
-
Nawabari governs operations routed through Nawabari.
|
|
9
|
-
ownership boundary, not an operating-system or filesystem
|
|
10
|
-
that already has filesystem permissions can still edit
|
|
11
|
-
directly.
|
|
8
|
+
Nawabari governs operations routed through Nawabari. By default it is an
|
|
9
|
+
authorization and ownership boundary, not an operating-system or filesystem
|
|
10
|
+
sandbox: a process that already has filesystem permissions can still edit
|
|
11
|
+
another worktree directly. This default (legacy) mode is unchanged.
|
|
12
|
+
|
|
13
|
+
The opt-in protected session mode defines a Linux-only OS/filesystem/process
|
|
14
|
+
enforcement boundary underneath the existing session/worktree/resource
|
|
15
|
+
authority (`src/domain/sandbox.ts`, contract
|
|
16
|
+
`nawabari.sandbox-execution.v1`). It binds one existing Nawabari session
|
|
17
|
+
resolved through the authoritative registry/guard path to a typed sandbox
|
|
18
|
+
execution request; it does not create a second session identity. A
|
|
19
|
+
machine-readable capability/doctor report distinguishes required Linux
|
|
20
|
+
primitives (bubblewrap, user/mount/PID/IPC/UTS namespaces) from optional
|
|
21
|
+
defense-in-depth primitives (cgroups v2, Landlock, seccomp, capability
|
|
22
|
+
inspection). When protected execution is requested and a required capability
|
|
23
|
+
is unavailable or the platform is unsupported, resolution fails closed and
|
|
24
|
+
never returns a request that claims the legacy unsandboxed path is
|
|
25
|
+
protected. The lower-level contract remains responsible only for capability
|
|
26
|
+
detection and the typed request/result shape; `resolveSandboxExecutionRequest()`
|
|
27
|
+
returns a request without invoking bubblewrap. The managed `session run`
|
|
28
|
+
launcher consumes that request and establishes the protected boundary. Network
|
|
29
|
+
mode is honestly reported as `inherited` (shared with the host), not isolated.
|
|
30
|
+
|
|
31
|
+
`git nawabari session run --session <id> -- <command> [args...]` is the
|
|
32
|
+
managed protected execution entry point. It resolves the existing session
|
|
33
|
+
through the normal guard authority and compiles a fixed bubblewrap argv; the
|
|
34
|
+
command is passed after an argv terminator and is never interpreted by a
|
|
35
|
+
shell. Resolution or launch failure never falls back to the legacy ambient
|
|
36
|
+
filesystem view.
|
|
37
|
+
|
|
38
|
+
The canonical profile starts from a private root, mounts only the owned
|
|
39
|
+
worktree read-write, gives each session private `/tmp`, `/proc`, HOME and
|
|
40
|
+
cache state, and exposes no sibling worktree or control-plane path. A small
|
|
41
|
+
repository-owned `nawabari/sandbox/shared-home` subtree is the only HOME state
|
|
42
|
+
shared between sessions. Selected host user-tool directories (`~/.local/bin`
|
|
43
|
+
and pnpm's user bin when present) are read-only; credentials and the rest of
|
|
44
|
+
the host HOME are not mounted. `/dev`, system certificates/configuration, and
|
|
45
|
+
the detected runtime are explicit read-only/runtime inputs.
|
|
46
|
+
|
|
47
|
+
On standalone Linux the profile uses existing `/usr`, `/bin`, `/lib*` and
|
|
48
|
+
selected `/etc` paths only when present. On NixOS it additionally selects
|
|
49
|
+
`/nix/store`, `/run/current-system`, `/run/wrappers`, and the per-user profile
|
|
50
|
+
when present; no `/usr` layout is assumed. Missing required paths or namespace
|
|
51
|
+
support produces a stable capability/topology error. Network remains
|
|
52
|
+
inherited by design.
|
|
12
53
|
|
|
13
54
|
## Install
|
|
14
55
|
|
|
@@ -41,6 +82,13 @@ result-schema versions, identity fields, and stable `failure_codes`. The
|
|
|
41
82
|
package version is release metadata; it is not a substitute for the
|
|
42
83
|
machine-contract identifier.
|
|
43
84
|
|
|
85
|
+
The `resource-claims` capability additionally exposes a machine-readable
|
|
86
|
+
`claim_set_replacement` object (`commands`, `atomic: true`,
|
|
87
|
+
`pairing: "adjacent-resource-mode"`, `idempotent_retry: true`,
|
|
88
|
+
`unchanged_on_rejection: true`) describing the atomic multi-claim replacement
|
|
89
|
+
surface documented above, so a caller can discover this contract instead of
|
|
90
|
+
assuming it from the CLI help text.
|
|
91
|
+
|
|
44
92
|
The supported standalone sequence is:
|
|
45
93
|
|
|
46
94
|
```text
|
|
@@ -123,9 +171,15 @@ git nawabari doctor --json
|
|
|
123
171
|
```
|
|
124
172
|
|
|
125
173
|
`status --json` reports the resolved `managed_worktree_root` used when
|
|
126
|
-
`session create` omits `--worktree`. `session create
|
|
127
|
-
all
|
|
128
|
-
base (`HEAD`), and label.
|
|
174
|
+
`session create` omits `--worktree` and `--worktree-root`. `session create
|
|
175
|
+
--help --json` describes all create options as optional and reports defaults
|
|
176
|
+
for branch, worktree, worktree root, base (`HEAD`), and label.
|
|
177
|
+
|
|
178
|
+
`--worktree-root` selects only the parent directory for a new session
|
|
179
|
+
worktree; Nawabari still derives the final worktree basename from its own
|
|
180
|
+
session-naming contract. It is mutually exclusive with `--worktree`, the
|
|
181
|
+
exact-path override. Every session record's `worktree_root` field reports
|
|
182
|
+
the resolved parent of that session's worktree.
|
|
129
183
|
|
|
130
184
|
`session create` provisions a dedicated worktree and mutable branch atomically
|
|
131
185
|
under the repository-scoped mutation lock. The default/integration worktree
|
|
@@ -182,6 +236,28 @@ git nawabari session update --session "$NAWABARI_SESSION_ID" \
|
|
|
182
236
|
git nawabari session release --session "$NAWABARI_SESSION_ID" --json
|
|
183
237
|
```
|
|
184
238
|
|
|
239
|
+
`session update` (and its `resource update` alias) atomically replaces a
|
|
240
|
+
session's _complete_ claim set in one `updateClaims()` transaction, backed by
|
|
241
|
+
the same repository lock as every other mutation. `--resource`/`--mode` are
|
|
242
|
+
repeatable to submit a multi-resource desired set in a single call; each
|
|
243
|
+
`--resource` must be immediately followed by its own `--mode`, so pairing is
|
|
244
|
+
positional adjacency rather than flag order and repeated resources can never
|
|
245
|
+
be associated with the wrong mode:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
git nawabari session update --session "$NAWABARI_SESSION_ID" \
|
|
249
|
+
--resource src/a.ts --mode exclusive-write \
|
|
250
|
+
--resource src/b.ts --mode exclusive-write \
|
|
251
|
+
--json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
If any requested claim in the set is invalid or conflicts, the whole update
|
|
255
|
+
is rejected and the session's prior claim set is left unchanged; no partial
|
|
256
|
+
or empty intermediate claim state is ever observable. Submitting the same
|
|
257
|
+
complete desired set again is idempotent. A successful replacement's JSON
|
|
258
|
+
exposes the resulting `claims` together with machine-readable `added` and
|
|
259
|
+
`released` claims.
|
|
260
|
+
|
|
185
261
|
The modes have these normative meanings:
|
|
186
262
|
|
|
187
263
|
- `read`: a non-mutating access declaration. It is not a consistency lease,
|
|
@@ -251,6 +327,48 @@ Detached, corrupt, missing, or conflicting state fails closed. The guard does
|
|
|
251
327
|
not install hooks and does not prevent direct filesystem writes outside
|
|
252
328
|
Nawabari.
|
|
253
329
|
|
|
330
|
+
## Actionable lifecycle rejections
|
|
331
|
+
|
|
332
|
+
Stable codes are necessary but not sufficient for a caller to pick a safe
|
|
333
|
+
next step without a second, separate inspection. For the codes below,
|
|
334
|
+
Nawabari's `details` carry bounded, deterministic evidence and a
|
|
335
|
+
`safeActions`/`safe_actions` list of stable, kebab-case next-action
|
|
336
|
+
identifiers, in addition to the human-readable `message`. JSON and human
|
|
337
|
+
output always render the identical underlying result; only the formatting
|
|
338
|
+
differs.
|
|
339
|
+
|
|
340
|
+
- **`RESOURCE_CLAIM_CONFLICT`** (`session claim`/`session update`,
|
|
341
|
+
`authorize`, `guard --operation`) reports the blocking claim
|
|
342
|
+
(`ownerClaimId`, `ownerResource`, `ownerMode`) and the blocking session's
|
|
343
|
+
canonical identity (`ownerSessionId`, `ownerWorktree`, `ownerBranch`, and
|
|
344
|
+
`ownerLabel` when the session has one) in the same result, so a caller
|
|
345
|
+
never needs a second `session list` scan to find the owner.
|
|
346
|
+
- **`PROTECTED_WORKTREE`**/**`PROTECTED_BRANCH`** raised by a live command
|
|
347
|
+
(not by cleanup) add `phase: "execution"` and distinguish the current
|
|
348
|
+
(protected) execution context from the referenced `--session` target:
|
|
349
|
+
`requestedSessionId`, `targetWorktree`, `targetBranch`, and `targetState`
|
|
350
|
+
when that session exists. `safeActions` names the deterministic fix
|
|
351
|
+
(`run-from-managed-session-worktree`, `select-target-session-explicitly`)
|
|
352
|
+
instead of the unrelated cleanup-time hint.
|
|
353
|
+
- **`INVALID_SESSION_ID`** stays machine-ID based: an invalid `--session`
|
|
354
|
+
value is never silently reinterpreted as a label. When it exactly and
|
|
355
|
+
unambiguously matches one active session's label, the result adds
|
|
356
|
+
`session_id_hint` (the canonical session ID) and
|
|
357
|
+
`session_label_match: "unique"` as a non-authoritative hint. An ambiguous
|
|
358
|
+
or absent label match never guesses: `session_label_match` reports
|
|
359
|
+
`"ambiguous"` (with `session_label_match_count`) or `"none"` instead.
|
|
360
|
+
- **`RECOVERABLE_COMMITS`** raised by `session close` carries the same
|
|
361
|
+
`close_readiness`/`result_state` classification `session inspect` reports
|
|
362
|
+
for the identical state — `external_evidence_required` when ancestry alone
|
|
363
|
+
could not prove the branch safe and a `--integrated-revision` proof might
|
|
364
|
+
resolve it (e.g. after a squash/rebase merge), versus `blocked` when
|
|
365
|
+
supplied evidence failed to prove equivalence, versus `ambiguous` when Git
|
|
366
|
+
observation itself was inconclusive. Both surfaces reuse one authority, so
|
|
367
|
+
a raw close rejection and `session inspect` never drift apart.
|
|
368
|
+
|
|
369
|
+
None of the above weakens fail-closed behavior, changes an error code's
|
|
370
|
+
meaning, or performs any mutation while producing the rejection.
|
|
371
|
+
|
|
254
372
|
## Orchestrator integration
|
|
255
373
|
|
|
256
374
|
An external orchestrator can create a session, capture the returned
|
|
@@ -267,6 +385,17 @@ worktree=$(printf '%s' "$created" | jq -r .worktree)
|
|
|
267
385
|
git nawabari session close --session "$session_id" --json
|
|
268
386
|
```
|
|
269
387
|
|
|
388
|
+
For a worker that must receive the protected filesystem/process boundary, use
|
|
389
|
+
the managed launcher instead of starting the worker directly:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
git nawabari session run --session "$session_id" -- node worker.js
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
The launcher owns only the child sandbox topology and process attachment. The
|
|
396
|
+
session registry, worktree/branch ownership, resource claims and lifecycle
|
|
397
|
+
remain the authoritative Nawabari domain state.
|
|
398
|
+
|
|
270
399
|
The orchestrator owns scheduling, prompts, and worker lifetime; Nawabari owns
|
|
271
400
|
only local session identity, worktree/branch ownership, and safe cleanup. No
|
|
272
401
|
Mottainai, GitHub, `gh`, network, or agent-runtime dependency is required.
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { type DomainResult } from "./domain/errors.js";
|
|
1
2
|
import { type SessionBackend } from "./domain/session.js";
|
|
2
3
|
import { type CliIO } from "./presentation.js";
|
|
4
|
+
import { type SandboxCommand, type SandboxExecutionResult, type SandboxProbe, type SandboxRuntimeLayout } from "./domain/sandbox.js";
|
|
3
5
|
export type CliDependencies = {
|
|
4
6
|
backend?: SessionBackend;
|
|
5
7
|
cwd?: string;
|
|
6
8
|
io?: CliIO;
|
|
7
9
|
version?: string;
|
|
10
|
+
sandboxRunner?: (request: import("./domain/sandbox.js").SandboxExecutionRequest, command: SandboxCommand) => Promise<DomainResult<SandboxExecutionResult>>;
|
|
11
|
+
sandboxProbe?: SandboxProbe;
|
|
12
|
+
sandboxRuntimeLayout?: SandboxRuntimeLayout;
|
|
8
13
|
};
|
|
9
14
|
export declare function runCli(argv: string[], dependencies?: CliDependencies): Promise<number>;
|