@nebutra/agent-runtime 0.2.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/.turbo/turbo-build.log +115 -0
- package/.turbo/turbo-test.log +44 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +253 -0
- package/LICENSE +676 -0
- package/README.md +50 -0
- package/dist/adapters/dispatcher-sse.d.ts +68 -0
- package/dist/adapters/dispatcher-sse.js +11 -0
- package/dist/adapters/dispatcher-sse.js.map +1 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.js +21 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mcp-catalog.d.ts +58 -0
- package/dist/adapters/mcp-catalog.js +9 -0
- package/dist/adapters/mcp-catalog.js.map +1 -0
- package/dist/adapters/prisma-rollout.d.ts +60 -0
- package/dist/adapters/prisma-rollout.js +7 -0
- package/dist/adapters/prisma-rollout.js.map +1 -0
- package/dist/chunk-24ZXP7FI.js +93 -0
- package/dist/chunk-24ZXP7FI.js.map +1 -0
- package/dist/chunk-2DA6Q6TN.js +126 -0
- package/dist/chunk-2DA6Q6TN.js.map +1 -0
- package/dist/chunk-37BBB2P2.js +73 -0
- package/dist/chunk-37BBB2P2.js.map +1 -0
- package/dist/chunk-57W3AR43.js +52 -0
- package/dist/chunk-57W3AR43.js.map +1 -0
- package/dist/chunk-5N4644PB.js +67 -0
- package/dist/chunk-5N4644PB.js.map +1 -0
- package/dist/chunk-5YS7WAPS.js +177 -0
- package/dist/chunk-5YS7WAPS.js.map +1 -0
- package/dist/chunk-6EGG2OZC.js +13 -0
- package/dist/chunk-6EGG2OZC.js.map +1 -0
- package/dist/chunk-7BUOF367.js +126 -0
- package/dist/chunk-7BUOF367.js.map +1 -0
- package/dist/chunk-BJBBR3QA.js +121 -0
- package/dist/chunk-BJBBR3QA.js.map +1 -0
- package/dist/chunk-CGRCUKGT.js +73 -0
- package/dist/chunk-CGRCUKGT.js.map +1 -0
- package/dist/chunk-FUG5DT2C.js +75 -0
- package/dist/chunk-FUG5DT2C.js.map +1 -0
- package/dist/chunk-LO24VOA3.js +199 -0
- package/dist/chunk-LO24VOA3.js.map +1 -0
- package/dist/chunk-MUF7ZZTO.js +57 -0
- package/dist/chunk-MUF7ZZTO.js.map +1 -0
- package/dist/chunk-NN7DATXA.js +46 -0
- package/dist/chunk-NN7DATXA.js.map +1 -0
- package/dist/chunk-PGGWSUTM.js +33 -0
- package/dist/chunk-PGGWSUTM.js.map +1 -0
- package/dist/chunk-RDKYDMXT.js +135 -0
- package/dist/chunk-RDKYDMXT.js.map +1 -0
- package/dist/chunk-YYFPDBJG.js +63 -0
- package/dist/chunk-YYFPDBJG.js.map +1 -0
- package/dist/chunk-ZMYX5VBU.js +135 -0
- package/dist/chunk-ZMYX5VBU.js.map +1 -0
- package/dist/chunk-ZTSKS42I.js +131 -0
- package/dist/chunk-ZTSKS42I.js.map +1 -0
- package/dist/commands.d.ts +74 -0
- package/dist/commands.js +10 -0
- package/dist/commands.js.map +1 -0
- package/dist/definitions.d.ts +94 -0
- package/dist/definitions.js +15 -0
- package/dist/definitions.js.map +1 -0
- package/dist/dispatcher.d.ts +50 -0
- package/dist/dispatcher.js +8 -0
- package/dist/dispatcher.js.map +1 -0
- package/dist/durable-turn.d.ts +58 -0
- package/dist/durable-turn.js +9 -0
- package/dist/durable-turn.js.map +1 -0
- package/dist/hook-pipeline.d.ts +114 -0
- package/dist/hook-pipeline.js +13 -0
- package/dist/hook-pipeline.js.map +1 -0
- package/dist/index.d.ts +1874 -0
- package/dist/index.js +3117 -0
- package/dist/index.js.map +1 -0
- package/dist/loop.d.ts +78 -0
- package/dist/loop.js +9 -0
- package/dist/loop.js.map +1 -0
- package/dist/mcp-bridge.d.ts +48 -0
- package/dist/mcp-bridge.js +8 -0
- package/dist/mcp-bridge.js.map +1 -0
- package/dist/model.d.ts +154 -0
- package/dist/model.js +9 -0
- package/dist/model.js.map +1 -0
- package/dist/policy.d.ts +130 -0
- package/dist/policy.js +23 -0
- package/dist/policy.js.map +1 -0
- package/dist/protocol.d.ts +170 -0
- package/dist/protocol.js +15 -0
- package/dist/protocol.js.map +1 -0
- package/dist/rollout-store-persistent.d.ts +48 -0
- package/dist/rollout-store-persistent.js +9 -0
- package/dist/rollout-store-persistent.js.map +1 -0
- package/dist/rollout.d.ts +82 -0
- package/dist/rollout.js +15 -0
- package/dist/rollout.js.map +1 -0
- package/dist/sandbox.d.ts +65 -0
- package/dist/sandbox.js +15 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/skills.d.ts +93 -0
- package/dist/skills.js +10 -0
- package/dist/skills.js.map +1 -0
- package/dist/subagents.d.ts +129 -0
- package/dist/subagents.js +21 -0
- package/dist/subagents.js.map +1 -0
- package/dist/tools.d.ts +77 -0
- package/dist/tools.js +9 -0
- package/dist/tools.js.map +1 -0
- package/package.json +74 -0
- package/src/adapters/dispatcher-sse.test.ts +218 -0
- package/src/adapters/dispatcher-sse.ts +222 -0
- package/src/adapters/index.ts +18 -0
- package/src/adapters/mcp-catalog.test.ts +213 -0
- package/src/adapters/mcp-catalog.ts +188 -0
- package/src/adapters/prisma-rollout.test.ts +153 -0
- package/src/adapters/prisma-rollout.ts +104 -0
- package/src/agent-runtime.test.ts +176 -0
- package/src/artifact-stream.test.ts +330 -0
- package/src/artifact-stream.ts +453 -0
- package/src/channel-gateway.test.ts +432 -0
- package/src/channel-gateway.ts +357 -0
- package/src/code-review.test.ts +501 -0
- package/src/code-review.ts +495 -0
- package/src/command-suggestions.test.ts +251 -0
- package/src/command-suggestions.ts +338 -0
- package/src/commands.test.ts +184 -0
- package/src/commands.ts +140 -0
- package/src/commit-message.test.ts +249 -0
- package/src/commit-message.ts +180 -0
- package/src/context-compaction.test.ts +522 -0
- package/src/context-compaction.ts +434 -0
- package/src/definitions.test.ts +78 -0
- package/src/definitions.ts +190 -0
- package/src/deployment-status.test.ts +215 -0
- package/src/deployment-status.ts +227 -0
- package/src/design-context.test.ts +195 -0
- package/src/design-context.ts +198 -0
- package/src/dispatcher.test.ts +234 -0
- package/src/dispatcher.ts +189 -0
- package/src/durable-turn.test.ts +209 -0
- package/src/durable-turn.ts +135 -0
- package/src/edit-planner.test.ts +204 -0
- package/src/edit-planner.ts +325 -0
- package/src/fuzzy-match.test.ts +311 -0
- package/src/fuzzy-match.ts +444 -0
- package/src/hook-pipeline.test.ts +279 -0
- package/src/hook-pipeline.ts +373 -0
- package/src/inbound-admission.test.ts +394 -0
- package/src/inbound-admission.ts +246 -0
- package/src/index.ts +47 -0
- package/src/loop.test.ts +161 -0
- package/src/loop.ts +211 -0
- package/src/mcp-bridge.test.ts +165 -0
- package/src/mcp-bridge.ts +76 -0
- package/src/memory-provider.test.ts +232 -0
- package/src/memory-provider.ts +257 -0
- package/src/model.ts +168 -0
- package/src/permission-ruleset.test.ts +301 -0
- package/src/permission-ruleset.ts +200 -0
- package/src/policy.ts +151 -0
- package/src/project-repo.test.ts +232 -0
- package/src/project-repo.ts +311 -0
- package/src/protocol.ts +159 -0
- package/src/rollout-store-persistent.test.ts +217 -0
- package/src/rollout-store-persistent.ts +166 -0
- package/src/rollout.ts +150 -0
- package/src/sandbox.ts +113 -0
- package/src/session-share.test.ts +360 -0
- package/src/session-share.ts +310 -0
- package/src/skill-distillation.test.ts +177 -0
- package/src/skill-distillation.ts +369 -0
- package/src/skills.test.ts +277 -0
- package/src/skills.ts +255 -0
- package/src/subagents.test.ts +290 -0
- package/src/subagents.ts +332 -0
- package/src/tools.ts +126 -0
- package/src/workbench.test.ts +0 -0
- package/src/workbench.ts +0 -0
- package/tsconfig.json +12 -0
- package/tsup.config.ts +33 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RolloutStore, RolloutLine } from './rollout.js';
|
|
2
|
+
import './model.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Production-grade, tenant-scoped {@link RolloutStore} backed by an injectable
|
|
6
|
+
* persistence port. This package depends on NO datastore: a Postgres /
|
|
7
|
+
* ClickHouse / `@nebutra/db` / `@nebutra/audit` adapter satisfies
|
|
8
|
+
* {@link RolloutPersistencePort} from the outside, so the "no infra change"
|
|
9
|
+
* promise stays honest — swap the adapter, keep the interface.
|
|
10
|
+
*
|
|
11
|
+
* Tenancy is structural: `tenantId` is part of the storage key AND Zod-validated
|
|
12
|
+
* on every read and write. Cross-tenant access is impossible by construction.
|
|
13
|
+
* The store fails closed — empty identifiers and malformed stored payloads
|
|
14
|
+
* raise typed errors; records are never silently dropped.
|
|
15
|
+
*/
|
|
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
|
+
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<{
|
|
31
|
+
seq: number;
|
|
32
|
+
payload: string;
|
|
33
|
+
}[]>;
|
|
34
|
+
}
|
|
35
|
+
/** Raised when a stored payload cannot be faithfully reconstructed. */
|
|
36
|
+
declare class RoundTripError extends Error {
|
|
37
|
+
constructor(message: string, options?: {
|
|
38
|
+
cause?: unknown;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
declare class PersistentRolloutStore implements RolloutStore {
|
|
42
|
+
#private;
|
|
43
|
+
constructor(port: RolloutPersistencePort);
|
|
44
|
+
append(line: RolloutLine): Promise<void>;
|
|
45
|
+
read(tenantIdArg: string, threadIdArg: string): Promise<readonly RolloutLine[]>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { PersistentRolloutStore, type RolloutPersistencePort, RoundTripError };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { TurnConfig, ThreadEvent, ThreadItem } from './model.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Event-sourced rollout / session trace (WRAP — capability #7).
|
|
5
|
+
*
|
|
6
|
+
* Faithful re-expression of the upstream rollout model: an append-only typed
|
|
7
|
+
* event log, state derived by replay, a first-class context-compaction marker
|
|
8
|
+
* (so replay stays bounded), and a per-item persistence policy that sanitizes
|
|
9
|
+
* and size-caps outputs before they are durable.
|
|
10
|
+
*
|
|
11
|
+
* Multi-tenant adaptation: every rollout line carries `tenantId`; the store
|
|
12
|
+
* interface is tenant-scoped so a Postgres/ClickHouse-backed implementation
|
|
13
|
+
* (via `@nebutra/audit` providers / `@nebutra/db`) replaces the upstream
|
|
14
|
+
* single-host jsonl + embedded-SQLite assumptions. Only an in-memory
|
|
15
|
+
* reference store ships here.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** Cap mirrors the upstream persisted-output ceiling. */
|
|
19
|
+
declare const PERSISTED_OUTPUT_MAX_BYTES = 10000;
|
|
20
|
+
type EventPersistenceMode = "limited" | "extended";
|
|
21
|
+
/** One durable line in a thread's append-only log. */
|
|
22
|
+
type RolloutLine = {
|
|
23
|
+
readonly tenantId: string;
|
|
24
|
+
readonly threadId: string;
|
|
25
|
+
readonly type: "session_meta";
|
|
26
|
+
readonly config: TurnConfig;
|
|
27
|
+
readonly at: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly tenantId: string;
|
|
30
|
+
readonly threadId: string;
|
|
31
|
+
readonly type: "event";
|
|
32
|
+
readonly event: ThreadEvent;
|
|
33
|
+
readonly at: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly tenantId: string;
|
|
36
|
+
readonly threadId: string;
|
|
37
|
+
readonly type: "turn_context";
|
|
38
|
+
readonly config: TurnConfig;
|
|
39
|
+
readonly at: string;
|
|
40
|
+
} | {
|
|
41
|
+
readonly tenantId: string;
|
|
42
|
+
readonly threadId: string;
|
|
43
|
+
readonly type: "compacted";
|
|
44
|
+
readonly summary: string;
|
|
45
|
+
readonly droppedThrough: string;
|
|
46
|
+
readonly at: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Persistence policy: decide per item whether it is durable, and sanitize +
|
|
50
|
+
* size-cap its output first. Executive markers are always persisted.
|
|
51
|
+
*/
|
|
52
|
+
declare function isPersisted(item: ThreadItem, mode: EventPersistenceMode): boolean;
|
|
53
|
+
/** Truncate aggregated command output to the persisted ceiling. */
|
|
54
|
+
declare function sanitizeForPersist(item: ThreadItem): ThreadItem;
|
|
55
|
+
/** Tenant-scoped append-only store. Replace the impl, keep the interface. */
|
|
56
|
+
interface RolloutStore {
|
|
57
|
+
append(line: RolloutLine): Promise<void>;
|
|
58
|
+
/** Lines for a thread, in append order. */
|
|
59
|
+
read(tenantId: string, threadId: string): Promise<readonly RolloutLine[]>;
|
|
60
|
+
}
|
|
61
|
+
/** In-memory reference store (dev/test only — never multi-instance safe). */
|
|
62
|
+
declare class InMemoryRolloutStore implements RolloutStore {
|
|
63
|
+
#private;
|
|
64
|
+
append(line: RolloutLine): Promise<void>;
|
|
65
|
+
read(tenantId: string, threadId: string): Promise<readonly RolloutLine[]>;
|
|
66
|
+
}
|
|
67
|
+
/** Derived thread state — the projection rebuilt by replaying the log. */
|
|
68
|
+
interface ThreadProjection {
|
|
69
|
+
readonly tenantId: string;
|
|
70
|
+
readonly threadId: string;
|
|
71
|
+
config?: TurnConfig;
|
|
72
|
+
readonly items: ThreadItem[];
|
|
73
|
+
/** Marks where a compaction summary replaced earlier history. */
|
|
74
|
+
compactionSummary?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Replay an append-only log into derived state. A `compacted` line bounds
|
|
78
|
+
* replay: items prior to it are represented by the summary, not re-applied.
|
|
79
|
+
*/
|
|
80
|
+
declare function replay(lines: readonly RolloutLine[]): ThreadProjection | null;
|
|
81
|
+
|
|
82
|
+
export { type EventPersistenceMode, InMemoryRolloutStore, PERSISTED_OUTPUT_MAX_BYTES, type RolloutLine, type RolloutStore, type ThreadProjection, isPersisted, replay, sanitizeForPersist };
|
package/dist/rollout.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InMemoryRolloutStore,
|
|
3
|
+
PERSISTED_OUTPUT_MAX_BYTES,
|
|
4
|
+
isPersisted,
|
|
5
|
+
replay,
|
|
6
|
+
sanitizeForPersist
|
|
7
|
+
} from "./chunk-37BBB2P2.js";
|
|
8
|
+
export {
|
|
9
|
+
InMemoryRolloutStore,
|
|
10
|
+
PERSISTED_OUTPUT_MAX_BYTES,
|
|
11
|
+
isPersisted,
|
|
12
|
+
replay,
|
|
13
|
+
sanitizeForPersist
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=rollout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CapabilityPolicy } from './policy.js';
|
|
2
|
+
import 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* External-sandbox delegation seam (PORT — capability #11).
|
|
6
|
+
*
|
|
7
|
+
* Governance decision (dual-track, ExternalSandbox posture): this multi-tenant
|
|
8
|
+
* web runtime NEVER executes untrusted code itself and introduces no isolation
|
|
9
|
+
* infrastructure. It only:
|
|
10
|
+
* 1. carries the capability policy (see ./policy),
|
|
11
|
+
* 2. delegates execution to an external isolator behind this interface
|
|
12
|
+
* (a self-hosted kernel sidecar — Track B — or any other executor),
|
|
13
|
+
* 3. records the outcome as a `command_execution` item (see ./model).
|
|
14
|
+
*
|
|
15
|
+
* The upstream OS enforcers (Seatbelt / Landlock / bubblewrap / Windows
|
|
16
|
+
* restricted token) are deliberately NOT ported — they are single-host and
|
|
17
|
+
* out of scope for a multi-tenant web product.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface SandboxExecRequest {
|
|
21
|
+
/** Mandatory tenant scope — every delegated exec is tenant-bound. */
|
|
22
|
+
readonly tenantId: string;
|
|
23
|
+
readonly threadId: string;
|
|
24
|
+
readonly command: string;
|
|
25
|
+
/** The capabilities the external isolator must honor. */
|
|
26
|
+
readonly capabilityPolicy: CapabilityPolicy;
|
|
27
|
+
/** Optional inputs the executor should materialize before running. */
|
|
28
|
+
readonly inputs?: Readonly<Record<string, string>>;
|
|
29
|
+
}
|
|
30
|
+
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
|
+
* The only thing Track A depends on for execution. Implemented by a decoupled
|
|
38
|
+
* isolator over the ./protocol contract; never implemented in-process here.
|
|
39
|
+
*/
|
|
40
|
+
interface ExternalSandbox {
|
|
41
|
+
exec(request: SandboxExecRequest): Promise<SandboxExecResult>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Fail-closed default. Wiring this in production requires a real
|
|
45
|
+
* {@link ExternalSandbox}; absent one, untrusted code never runs — by design.
|
|
46
|
+
*/
|
|
47
|
+
declare class NoExecutorConfiguredError extends Error {
|
|
48
|
+
constructor();
|
|
49
|
+
}
|
|
50
|
+
declare const REFUSING_SANDBOX: ExternalSandbox;
|
|
51
|
+
declare class SandboxDelegationError extends Error {
|
|
52
|
+
readonly status: number;
|
|
53
|
+
constructor(message: string, status: number);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The concrete Track-B coupling: delegate execution over HTTP to the decoupled
|
|
57
|
+
* Rust isolator (`backends/rust/sandbox`, `POST /api/v1/sandbox/exec`). The
|
|
58
|
+
* isolator is fail-closed; a non-2xx (e.g. 403 refusal) is surfaced as an
|
|
59
|
+
* error and never coerced into a fabricated result.
|
|
60
|
+
*/
|
|
61
|
+
declare function createHttpSandbox(baseUrl: string, fetchImpl?: typeof fetch): ExternalSandbox;
|
|
62
|
+
/** Guard: reject the most dangerous posture unless explicitly opted in. */
|
|
63
|
+
declare function assertSafePosture(policy: CapabilityPolicy, allowDanger?: boolean): void;
|
|
64
|
+
|
|
65
|
+
export { type ExternalSandbox, NoExecutorConfiguredError, REFUSING_SANDBOX, SandboxDelegationError, type SandboxExecRequest, type SandboxExecResult, assertSafePosture, createHttpSandbox };
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NoExecutorConfiguredError,
|
|
3
|
+
REFUSING_SANDBOX,
|
|
4
|
+
SandboxDelegationError,
|
|
5
|
+
assertSafePosture,
|
|
6
|
+
createHttpSandbox
|
|
7
|
+
} from "./chunk-MUF7ZZTO.js";
|
|
8
|
+
export {
|
|
9
|
+
NoExecutorConfiguredError,
|
|
10
|
+
REFUSING_SANDBOX,
|
|
11
|
+
SandboxDelegationError,
|
|
12
|
+
assertSafePosture,
|
|
13
|
+
createHttpSandbox
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/skills.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Definition, DefinitionResolver, ResolveContext } from './definitions.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Skills system (WRAP — progressive-disclosure capability).
|
|
6
|
+
*
|
|
7
|
+
* Faithful re-expression of the source harness's two-phase skill model:
|
|
8
|
+
*
|
|
9
|
+
* Phase 1 — Listing (cheap, always in context): a budget-bounded catalog
|
|
10
|
+
* exposing only {name, description, whenToUse} per skill. First-party
|
|
11
|
+
* skills (bundled/builtin tier) are never truncated and never dropped;
|
|
12
|
+
* lower-tier skills degrade to names-only, then drop, as the context
|
|
13
|
+
* budget is exhausted. Path-activated skills stay hidden until a touched
|
|
14
|
+
* file matches one of their globs.
|
|
15
|
+
*
|
|
16
|
+
* Phase 2 — Invocation (lazy, on demand): expand the chosen skill's body
|
|
17
|
+
* into turn messages, with arg/var substitution and a merged tool
|
|
18
|
+
* allowlist contributed to the permission context.
|
|
19
|
+
*
|
|
20
|
+
* A skill IS a `Definition` — the same tenant-scoped layered resolver backs
|
|
21
|
+
* the command registry and the subagent registry (see ./definitions).
|
|
22
|
+
*
|
|
23
|
+
* SECURITY: this re-expression intentionally drops the source's body-side
|
|
24
|
+
* command-substitution (the upstream "!`…`" inline-shell facility). Skill
|
|
25
|
+
* bodies are inert text; they NEVER trigger host shell execution here. All
|
|
26
|
+
* entry points require a non-empty `tenantId` and fail closed; cross-tenant
|
|
27
|
+
* inputs are rejected by the underlying `DefinitionResolver`. Pure data/logic
|
|
28
|
+
* — no host filesystem access (resource base is an injected indirection).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** A skill is just a resolvable `Definition`. */
|
|
32
|
+
type SkillRecord = Definition;
|
|
33
|
+
/** One catalog entry — only the cheap, always-in-context surface. */
|
|
34
|
+
interface SkillListingEntry {
|
|
35
|
+
readonly name: string;
|
|
36
|
+
readonly description: string;
|
|
37
|
+
readonly whenToUse?: string | undefined;
|
|
38
|
+
/** True when this entry was degraded to names-only by the budget. */
|
|
39
|
+
readonly degraded: boolean;
|
|
40
|
+
readonly firstParty: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface SkillListing {
|
|
43
|
+
readonly entries: readonly SkillListingEntry[];
|
|
44
|
+
readonly text: string;
|
|
45
|
+
}
|
|
46
|
+
declare const listingOptionsSchema: z.ZodObject<{
|
|
47
|
+
maxListingDescChars: z.ZodDefault<z.ZodNumber>;
|
|
48
|
+
contextWindowTokens: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
budgetPercent: z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
touchedPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
51
|
+
}, z.core.$strip>;
|
|
52
|
+
type SkillListingOptions = z.input<typeof listingOptionsSchema>;
|
|
53
|
+
/**
|
|
54
|
+
* Phase 1 — build the always-in-context skill catalog. Pure & deterministic.
|
|
55
|
+
*
|
|
56
|
+
* Budget = `contextWindowTokens * budgetPercent`. First-party skills are
|
|
57
|
+
* emitted in full and do NOT consume budget against being dropped. Each
|
|
58
|
+
* remaining skill is tried at full description; if it would overflow it is
|
|
59
|
+
* degraded to names-only; if even that overflows it is dropped. Skills in
|
|
60
|
+
* `ctx.loadedSlugs` and unactivated path-gated skills are suppressed.
|
|
61
|
+
*/
|
|
62
|
+
declare function buildSkillListing(resolver: DefinitionResolver<SkillRecord>, ctx: ResolveContext, options?: SkillListingOptions): SkillListing;
|
|
63
|
+
interface SkillMessage {
|
|
64
|
+
readonly role: "system" | "user";
|
|
65
|
+
readonly content: string;
|
|
66
|
+
}
|
|
67
|
+
interface ExpandedSkill {
|
|
68
|
+
readonly messages: readonly SkillMessage[];
|
|
69
|
+
readonly allowedTools: readonly string[];
|
|
70
|
+
readonly modelOverride?: string | undefined;
|
|
71
|
+
readonly effort?: "low" | "medium" | "high" | undefined;
|
|
72
|
+
readonly executionMode: "inline" | "fork";
|
|
73
|
+
}
|
|
74
|
+
interface ExpandContext {
|
|
75
|
+
readonly tenantId: string;
|
|
76
|
+
readonly args: Record<string, string>;
|
|
77
|
+
readonly vars: Record<string, string>;
|
|
78
|
+
/** Lazy body fetch — host supplies; called only here, only once. */
|
|
79
|
+
readonly bodyLoader: (bodyRef: string) => Promise<string>;
|
|
80
|
+
/** Host-injected resource-base indirection (NO host FS access here). */
|
|
81
|
+
readonly skillResourceBase: (slug: string) => string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Phase 2 — expand a skill into turn messages. Body is loaded lazily via the
|
|
85
|
+
* injected `bodyLoader` (never during listing). A `Base directory…` line is
|
|
86
|
+
* prepended via the injected `skillResourceBase` indirection. Args + vars are
|
|
87
|
+
* substituted with `substituteArguments`. The skill's `allowedTools` are
|
|
88
|
+
* returned for the caller to merge into the permission context — this never
|
|
89
|
+
* mutates global state and never executes anything from the body.
|
|
90
|
+
*/
|
|
91
|
+
declare function expandSkill(skill: SkillRecord, ctx: ExpandContext): Promise<ExpandedSkill>;
|
|
92
|
+
|
|
93
|
+
export { type ExpandContext, type ExpandedSkill, type SkillListing, type SkillListingEntry, type SkillListingOptions, type SkillMessage, type SkillRecord, buildSkillListing, expandSkill };
|
package/dist/skills.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Definition, Frontmatter } from './definitions.js';
|
|
2
|
+
import { ThreadItem, TurnUsage } from './model.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Subagent / task dispatch (WRAP — isolated-child-agent model, the delta).
|
|
7
|
+
*
|
|
8
|
+
* Faithful re-expression of the source harness's child-agent dispatch grammar:
|
|
9
|
+
* a registry decides WHICH definition runs, a context-boundary contract decides
|
|
10
|
+
* WITH WHAT inherited context it starts, and a typed terminal envelope decides
|
|
11
|
+
* HOW its result re-enters the parent. The child loop, child policy, and
|
|
12
|
+
* rollout store live elsewhere in agent-runtime and are intentionally NOT
|
|
13
|
+
* reimplemented here — this layer is pure decision + data + state machine.
|
|
14
|
+
*
|
|
15
|
+
* Multi-tenant: every task operation is keyed by `tenantId` and fails closed.
|
|
16
|
+
* Pure data/logic — no FS scan, no shell, no network.
|
|
17
|
+
*
|
|
18
|
+
* NOTE on dropped task kinds: the source harness models several local execution
|
|
19
|
+
* task kinds (local_bash / monitor_mcp / worktree). Those are deliberately NOT
|
|
20
|
+
* represented here: this package never executes locally — execution is delegated
|
|
21
|
+
* via the external-sandbox seam — so only the agent-facing `subagent` kind (and
|
|
22
|
+
* its `deferred` sibling) is modelled.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** A subagent definition is just a `Definition` (same loader as skills/commands). */
|
|
26
|
+
type SubagentRecord = Definition;
|
|
27
|
+
/**
|
|
28
|
+
* Effective tool set for a subagent: `allowedTools` minus `disallowedTools`,
|
|
29
|
+
* intersected with the runtime tool universe so a definition can never widen
|
|
30
|
+
* its scope past what the host exposes. When `allowedTools` is empty the
|
|
31
|
+
* contract is "all tools except the denied ones" (all-except-deny).
|
|
32
|
+
*
|
|
33
|
+
* Pure — order follows `allTools` for determinism.
|
|
34
|
+
*/
|
|
35
|
+
declare function resolveSubagentTools(fm: Frontmatter, allTools: readonly string[]): string[];
|
|
36
|
+
type DispatchMode = "spawn" | "fork";
|
|
37
|
+
interface DispatchCtx {
|
|
38
|
+
readonly tenantId: string;
|
|
39
|
+
}
|
|
40
|
+
interface PrepareDispatchInput {
|
|
41
|
+
readonly definition: SubagentRecord;
|
|
42
|
+
readonly mode: DispatchMode;
|
|
43
|
+
readonly parentItems?: readonly ThreadItem[];
|
|
44
|
+
/** Caller's directive. Mandatory & non-empty in `spawn` mode. */
|
|
45
|
+
readonly brief: string;
|
|
46
|
+
readonly ctx: DispatchCtx;
|
|
47
|
+
/** Runtime tool universe; defaults to the definition's own allow list. */
|
|
48
|
+
readonly allTools?: readonly string[];
|
|
49
|
+
}
|
|
50
|
+
interface PreparedDispatch {
|
|
51
|
+
readonly initialContext: readonly ThreadItem[];
|
|
52
|
+
readonly toolScope: readonly string[];
|
|
53
|
+
/** Opaque reference to the system prompt body (resolved by the child loop). */
|
|
54
|
+
readonly systemPromptRef: string;
|
|
55
|
+
readonly modelOverride?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Decide the child's starting context boundary.
|
|
59
|
+
*
|
|
60
|
+
* - `spawn`: zero inherited context. The caller MUST fully brief the child;
|
|
61
|
+
* an empty brief is a programming error and throws.
|
|
62
|
+
* - `fork`: inherit a defensive copy of `parentItems` with incomplete tool
|
|
63
|
+
* calls filtered out, then append the brief as a directive.
|
|
64
|
+
*
|
|
65
|
+
* `parentItems` is never mutated. Cross-tenant dispatch fails closed.
|
|
66
|
+
*/
|
|
67
|
+
declare function prepareDispatch(input: PrepareDispatchInput): PreparedDispatch;
|
|
68
|
+
declare const TASK_KINDS: readonly ["subagent", "deferred"];
|
|
69
|
+
type TaskKind = (typeof TASK_KINDS)[number];
|
|
70
|
+
declare const TASK_STATUSES: readonly ["pending", "running", "completed", "failed", "killed"];
|
|
71
|
+
type TaskStatus = (typeof TASK_STATUSES)[number];
|
|
72
|
+
interface TaskRecord {
|
|
73
|
+
readonly taskId: string;
|
|
74
|
+
readonly tenantId: string;
|
|
75
|
+
readonly status: TaskStatus;
|
|
76
|
+
readonly notified: boolean;
|
|
77
|
+
readonly kind: TaskKind;
|
|
78
|
+
readonly createdAtMs: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Tenant-scoped task registry. Every mutating op verifies the caller's
|
|
82
|
+
* tenant against the stored record; a mismatch is treated as "not found"
|
|
83
|
+
* for reads and throws for mutations — cross-tenant access is impossible.
|
|
84
|
+
*/
|
|
85
|
+
declare class TaskRegistry {
|
|
86
|
+
#private;
|
|
87
|
+
create(tenantId: string, kind: TaskKind): string;
|
|
88
|
+
/** Tenant-checked read. Returns undefined for unknown or cross-tenant ids. */
|
|
89
|
+
get(taskId: string, tenantId: string): TaskRecord | undefined;
|
|
90
|
+
transition(taskId: string, to: TaskStatus, tenantId: string): TaskRecord;
|
|
91
|
+
/** Unified kill — drives any non-terminal task to `killed`. */
|
|
92
|
+
stop(taskId: string, tenantId: string): TaskRecord;
|
|
93
|
+
markNotified(taskId: string, tenantId: string): TaskRecord;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* No-peek / no-race guard. Throws if a parent tries to read an in-flight
|
|
97
|
+
* deferred task's transcript before it has settled.
|
|
98
|
+
*/
|
|
99
|
+
declare function assertNotPeeking(task: TaskRecord): void;
|
|
100
|
+
interface SyncEnvelope {
|
|
101
|
+
readonly kind: "sync";
|
|
102
|
+
readonly content: readonly ThreadItem[];
|
|
103
|
+
readonly usage: TurnUsage;
|
|
104
|
+
readonly agentId: string;
|
|
105
|
+
readonly toolUses: number;
|
|
106
|
+
readonly durationMs: number;
|
|
107
|
+
}
|
|
108
|
+
interface DeferredEnvelope {
|
|
109
|
+
readonly kind: "deferred";
|
|
110
|
+
readonly taskId: string;
|
|
111
|
+
readonly channel: string;
|
|
112
|
+
}
|
|
113
|
+
type DispatchEnvelope = SyncEnvelope | DeferredEnvelope;
|
|
114
|
+
/** Build a fire-and-forget deferred envelope for a registered task. */
|
|
115
|
+
declare function makeDeferred(taskId: string, channel: string): DeferredEnvelope;
|
|
116
|
+
interface SettleDeferredTarget {
|
|
117
|
+
readonly taskId: string;
|
|
118
|
+
readonly channel: string;
|
|
119
|
+
readonly tenantId: string;
|
|
120
|
+
readonly registry: TaskRegistry;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Settle a previously-deferred task: drive it to `completed` (tenant-checked)
|
|
124
|
+
* and produce the eventual sync-shaped result. Empty content is normalized to
|
|
125
|
+
* the explicit no-output marker.
|
|
126
|
+
*/
|
|
127
|
+
declare function settleDeferred(target: SettleDeferredTarget, content: readonly ThreadItem[], usage: TurnUsage): SyncEnvelope;
|
|
128
|
+
|
|
129
|
+
export { type DeferredEnvelope, type DispatchCtx, type DispatchEnvelope, type DispatchMode, type PrepareDispatchInput, type PreparedDispatch, type SettleDeferredTarget, type SubagentRecord, type SyncEnvelope, TASK_KINDS, TASK_STATUSES, type TaskKind, type TaskRecord, TaskRegistry, type TaskStatus, assertNotPeeking, makeDeferred, prepareDispatch, resolveSubagentTools, settleDeferred };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TASK_KINDS,
|
|
3
|
+
TASK_STATUSES,
|
|
4
|
+
TaskRegistry,
|
|
5
|
+
assertNotPeeking,
|
|
6
|
+
makeDeferred,
|
|
7
|
+
prepareDispatch,
|
|
8
|
+
resolveSubagentTools,
|
|
9
|
+
settleDeferred
|
|
10
|
+
} from "./chunk-5YS7WAPS.js";
|
|
11
|
+
export {
|
|
12
|
+
TASK_KINDS,
|
|
13
|
+
TASK_STATUSES,
|
|
14
|
+
TaskRegistry,
|
|
15
|
+
assertNotPeeking,
|
|
16
|
+
makeDeferred,
|
|
17
|
+
prepareDispatch,
|
|
18
|
+
resolveSubagentTools,
|
|
19
|
+
settleDeferred
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=subagents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tool / MCP abstraction (WRAP — capability #9).
|
|
5
|
+
*
|
|
6
|
+
* Faithful re-expression of the upstream uniform tool model: one
|
|
7
|
+
* `ToolDefinition` interface, JSON-schema in/out, a deferred-load flag, and
|
|
8
|
+
* MCP-as-adapter behind the same interface (MCP tools and native tools are
|
|
9
|
+
* indistinguishable at dispatch). Activates `@nebutra/mcp` primitives rather
|
|
10
|
+
* than re-porting them.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** A tool definition — pure data; transport-agnostic. */
|
|
14
|
+
interface ToolDefinition<I = unknown, O = unknown> {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly description: string;
|
|
17
|
+
readonly inputSchema: z.ZodType<I>;
|
|
18
|
+
readonly outputSchema?: z.ZodType<O>;
|
|
19
|
+
/**
|
|
20
|
+
* When true, the full schema is withheld until a `tool_search` discovery
|
|
21
|
+
* step lazily loads it (parity with the upstream deferred-tool pattern).
|
|
22
|
+
*/
|
|
23
|
+
readonly deferLoading?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Source provenance — native vs. adapted from an MCP server. */
|
|
26
|
+
type ToolOrigin = {
|
|
27
|
+
readonly kind: "native";
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: "mcp";
|
|
30
|
+
readonly server: string;
|
|
31
|
+
};
|
|
32
|
+
interface RegisteredTool {
|
|
33
|
+
readonly definition: ToolDefinition;
|
|
34
|
+
readonly origin: ToolOrigin;
|
|
35
|
+
}
|
|
36
|
+
/** A tool invocation routed through the registry. */
|
|
37
|
+
interface ToolDispatchContext {
|
|
38
|
+
/** Mandatory tenant scope — every dispatch is tenant-bound. */
|
|
39
|
+
readonly tenantId: string;
|
|
40
|
+
readonly threadId: string;
|
|
41
|
+
}
|
|
42
|
+
type ToolHandler<I = unknown, O = unknown> = (input: I, ctx: ToolDispatchContext) => Promise<O>;
|
|
43
|
+
/** Pre/post dispatch hooks (parity with upstream orchestrator hooks). */
|
|
44
|
+
interface ToolHooks {
|
|
45
|
+
readonly preToolUse?: (name: string, input: unknown, ctx: ToolDispatchContext) => Promise<void>;
|
|
46
|
+
readonly postToolUse?: (name: string, output: unknown, ctx: ToolDispatchContext) => Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* registry -> router -> orchestrator pipeline. MCP tools register via
|
|
50
|
+
* {@link adaptMcpTool} and are dispatched identically to native tools.
|
|
51
|
+
*/
|
|
52
|
+
declare class ToolRegistry {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(hooks?: ToolHooks);
|
|
55
|
+
register<I, O>(definition: ToolDefinition<I, O>, handler: ToolHandler<I, O>, origin?: ToolOrigin): void;
|
|
56
|
+
list(): readonly RegisteredTool[];
|
|
57
|
+
dispatch(name: string, rawInput: unknown, ctx: ToolDispatchContext): Promise<unknown>;
|
|
58
|
+
}
|
|
59
|
+
/** Minimal shape of an MCP client (satisfied by `@nebutra/mcp`'s `mcpClient`). */
|
|
60
|
+
interface McpClientLike {
|
|
61
|
+
executeTool(name: string, args: unknown, ctx: {
|
|
62
|
+
requestId: string;
|
|
63
|
+
tenantId: string;
|
|
64
|
+
}): Promise<unknown>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Adapt an external MCP tool into the uniform tool model. The resulting
|
|
68
|
+
* handler delegates to the MCP client; callers cannot tell it apart from a
|
|
69
|
+
* native tool.
|
|
70
|
+
*/
|
|
71
|
+
declare function adaptMcpTool(server: string, definition: ToolDefinition, client: McpClientLike): {
|
|
72
|
+
definition: ToolDefinition;
|
|
73
|
+
handler: ToolHandler;
|
|
74
|
+
origin: ToolOrigin;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { type McpClientLike, type RegisteredTool, type ToolDefinition, type ToolDispatchContext, type ToolHandler, type ToolHooks, type ToolOrigin, ToolRegistry, adaptMcpTool };
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|