@skanl/brambo-session 0.1.1
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/LICENSE +21 -0
- package/README.md +199 -0
- package/dist/executors.d.ts +146 -0
- package/dist/executors.js +342 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +73 -0
- package/dist/methods.d.ts +137 -0
- package/dist/methods.js +264 -0
- package/dist/remote-mcp.d.ts +37 -0
- package/dist/remote-mcp.js +76 -0
- package/dist/run-session.d.ts +300 -0
- package/dist/run-session.js +523 -0
- package/dist/tool-executor.d.ts +4 -0
- package/dist/tool-executor.js +159 -0
- package/dist/usage.d.ts +30 -0
- package/dist/usage.js +110 -0
- package/dist/workspaces.d.ts +63 -0
- package/dist/workspaces.js +126 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SKANL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# @skanl/brambo-session
|
|
2
|
+
|
|
3
|
+
The brambo session: everything `brambo run` does except argv, JSON and exit codes.
|
|
4
|
+
Compose through a kernel, create a workspace, run a prompt under a cancellation
|
|
5
|
+
signal through that kernel's interception waterfall, release and dispose.
|
|
6
|
+
|
|
7
|
+
The adapter and the workspace provider are **mounted as kernel plugins**, not
|
|
8
|
+
constructed: `createSessionKernel` registers both, seeds the kernel's layered
|
|
9
|
+
configuration from brambo's own documents, and `runSession` consumes the
|
|
10
|
+
`executor` and `workspace` services by name.
|
|
11
|
+
|
|
12
|
+
Installing `@skanl/brambo-cli` is not required — that is the point of this package. Nor
|
|
13
|
+
is installing `@skanl/brambo-contracts` or `@skanl/brambo-kernel`: every type and helper the
|
|
14
|
+
surface needs is re-exported from here.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { runSession } from '@skanl/brambo-session'
|
|
18
|
+
|
|
19
|
+
const envelope = await runSession({ prompt: 'list files in this workspace' })
|
|
20
|
+
console.log(envelope.status, envelope.summary)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The returned value is the `ResultEnvelope` `brambo run` prints. Environment
|
|
24
|
+
failures throw instead, carrying a `code` — `BRAMBO_CONTRACT_*` from the workspace
|
|
25
|
+
port, `BRAMBO_KERNEL_*` from a budget refusal:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
try {
|
|
29
|
+
await runSession({ prompt: 'list files' })
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error((error as { code?: string }).code)
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## What it writes, and what it owns
|
|
36
|
+
|
|
37
|
+
- **Disk.** Each call creates `<cwd>/.brambo/workspaces/<uuid>`, and the session
|
|
38
|
+
itself never removes it: `release()` ends a lease and `dispose()` leaves the
|
|
39
|
+
tree so work survives. Removal is a separate, named capability this package
|
|
40
|
+
exports — `inspectLocalWorkspaces` / `removeLocalWorkspace` and
|
|
41
|
+
`inspectWorktrees` / `removeWorktree`, which is what `brambo workspace remove`
|
|
42
|
+
calls. It is deliberately NOT on the `WorkspaceProvider` port: `dispose()` is
|
|
43
|
+
documented in five places as preserving state, so making it the removal would
|
|
44
|
+
invert a published clause.
|
|
45
|
+
- **The provider.** The session disposes whatever `createProvider` returns, on
|
|
46
|
+
every path. Return a **fresh** provider per session; a pooled one comes back
|
|
47
|
+
disposed and the next session fails with `BRAMBO_CONTRACT_PROVIDER_DISPOSED`.
|
|
48
|
+
`createProvider` is **refused** beside a supplied `kernel` — a supplied kernel
|
|
49
|
+
already carries a provider, and pooling one behind its shared pipeline made the
|
|
50
|
+
second run fail `BRAMBO_KERNEL_ACTION_INVALID` on a repeated workspace id.
|
|
51
|
+
- **The kernel.** A kernel `runSession` built itself is stopped on every path,
|
|
52
|
+
which is what runs every mounted plugin's disposer. A kernel you passed in is
|
|
53
|
+
yours to stop; the session never does, so several sessions can share one.
|
|
54
|
+
- **Interrupts.** There is no default — a library that installs
|
|
55
|
+
`process.on('SIGINT')` steals the signal from its host. Pass `onInterrupt` to
|
|
56
|
+
wire your own (`@skanl/brambo-cli` passes its SIGINT/SIGTERM registration).
|
|
57
|
+
|
|
58
|
+
## Budgets and the record stream
|
|
59
|
+
|
|
60
|
+
The executor invocation is an action on the **kernel's** waterfall, so
|
|
61
|
+
declarative caps apply to it and every invocation is recorded. `log` receives the
|
|
62
|
+
waterfall's records; the kernel's lifecycle records (manifest validation,
|
|
63
|
+
activation, disposal) stay in the kernel's own stream — build the kernel yourself
|
|
64
|
+
to read those, as the shared-kernel example below does:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { createMemoryLogSink, runSession, SESSION_ACTION_ID } from '@skanl/brambo-session'
|
|
68
|
+
|
|
69
|
+
const log = createMemoryLogSink()
|
|
70
|
+
try {
|
|
71
|
+
// A refusal happens BEFORE the executor process is started.
|
|
72
|
+
await runSession({ prompt: 'do not spawn', log, actionPolicy: { maxInvocations: 0 } })
|
|
73
|
+
} catch {
|
|
74
|
+
// `drain()` first: an async sink still has records in flight when runSession resolves.
|
|
75
|
+
await log.drain()
|
|
76
|
+
console.log(log.records.map((record) => record.event)) // ['action.refused']
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Records are subject-scoped to the workspace, so match with
|
|
81
|
+
`record.subject.startsWith(SESSION_ACTION_ID + '#')`.
|
|
82
|
+
|
|
83
|
+
## Brambo's own configuration
|
|
84
|
+
|
|
85
|
+
`runSession` reads no files. Hand it the documents instead, and they seed the
|
|
86
|
+
kernel's layered configuration — the ONE composed document both the executor
|
|
87
|
+
selection and every mounted plugin read:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { readExecutorConfigLayers, runSession } from '@skanl/brambo-session'
|
|
91
|
+
|
|
92
|
+
const configLayers = await readExecutorConfigLayers({ projectDir: process.cwd() })
|
|
93
|
+
await runSession({ prompt: 'list files', configLayers })
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`resolveExecutor()` still exists and still answers *which executor*, but a
|
|
97
|
+
selection alone carries no document: `runSession({ executorId })` seeds only
|
|
98
|
+
brambo's defaults and that one choice, so nothing a user wrote reaches a mounted
|
|
99
|
+
plugin. Pass `configLayers` whenever you want the document to configure the run.
|
|
100
|
+
|
|
101
|
+
Resolution order is `defaults → global → project → agent → invocation`. Naming a
|
|
102
|
+
`cwd` makes the workspace root this invocation's answer; omitting one lets a
|
|
103
|
+
`workspace.rootDir` in the document decide. A key brambo reads and cannot use is
|
|
104
|
+
reported through `onWarning` and never fails the run.
|
|
105
|
+
|
|
106
|
+
## One kernel, many sessions — one budget
|
|
107
|
+
|
|
108
|
+
`runSession` builds a kernel per call unless you hand it one. `createSessionKernel`
|
|
109
|
+
builds one you own, and then the pipeline, its caps and its record stream are
|
|
110
|
+
shared by every session on it:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { createMemoryLogSink, createSessionKernel, runSession } from '@skanl/brambo-session'
|
|
114
|
+
|
|
115
|
+
const log = createMemoryLogSink()
|
|
116
|
+
const kernel = createSessionKernel({
|
|
117
|
+
log, // the WHOLE kernel stream: activation and disposal, not only the waterfall
|
|
118
|
+
actionPolicy: { maxTotalCost: 1.5 },
|
|
119
|
+
executorId: 'codex',
|
|
120
|
+
onWarning: (message) => console.error(message),
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
await runSession({ prompt: 'first', kernel })
|
|
124
|
+
// Refused: BRAMBO_KERNEL_COST_CAP_EXCEEDED, before the executor is spawned.
|
|
125
|
+
await runSession({ prompt: 'second', kernel }).catch(() => {})
|
|
126
|
+
await kernel.stop() // disposes every mounted plugin, in reverse order
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`createSessionKernel` is the only composition surface this package exposes, and
|
|
130
|
+
that is deliberate: it hands back a started kernel and no plugin factory. A
|
|
131
|
+
`PluginFactory` a caller can invoke with an `ActivationContext` of its own
|
|
132
|
+
construction yields a real vendor adapter wired to the caller's own pipeline, so
|
|
133
|
+
re-exporting the factories put a bypass on the surface of a package whose reason
|
|
134
|
+
to exist is that the executor goes through the waterfall.
|
|
135
|
+
|
|
136
|
+
A kernel you supply owns its configuration, its plugins, its pipeline, its sink
|
|
137
|
+
and its provider, so `configLayers`, `cwd`, `executorId`, `adapterOptions`,
|
|
138
|
+
`createAdapter`, `createProvider`, `onSelection`, `log` and `actionPolicy` are
|
|
139
|
+
**refused** beside it rather than silently ignored.
|
|
140
|
+
|
|
141
|
+
## Sandbox and tool-composition seam
|
|
142
|
+
|
|
143
|
+
SDK hosts may supply a `sandboxProvider`, `toolExecutor`, `toolPolicy`, approval,
|
|
144
|
+
and tool/sandbox event callbacks when composing a session. `executeTool()` is the
|
|
145
|
+
explicit SDK operation that validates the invocation and context, checks the
|
|
146
|
+
provider's declared capability facts, requests host approval, executes through
|
|
147
|
+
the supplied executor, and emits the normalized result.
|
|
148
|
+
|
|
149
|
+
The boundary is explicit: bind a
|
|
150
|
+
caller-owned sandbox session to `createToolExecutor`, pass `local` or
|
|
151
|
+
`mcp-stdio` exact argv, and dispose the session at the host's lifecycle boundary.
|
|
152
|
+
This is not an arbitrary JavaScript-handler API, and `ToolProvider` discovery is
|
|
153
|
+
not execution authority. The session seam also makes no OS-isolation or concrete
|
|
154
|
+
remote-protocol claim.
|
|
155
|
+
|
|
156
|
+
**Read the caps honestly.** A run is ADMITTED at `SESSION_ACTION_COST` (a flat 1)
|
|
157
|
+
and then SETTLED against the token figure the executor itself reported, so
|
|
158
|
+
`maxTotalCost` and `maxInvocations` now refuse on **different runs**: one
|
|
159
|
+
claude-code run settles at tens of thousands of tokens, which trips a cost cap
|
|
160
|
+
while the invocation count is still 1, and a cheap run trips an invocation cap
|
|
161
|
+
with the settled cost nowhere near its own limit. The error `code` still says
|
|
162
|
+
which cap fired.
|
|
163
|
+
|
|
164
|
+
Two ceilings remain, and both are tracked in
|
|
165
|
+
`_bmad-output/implementation-artifacts/deferred-work.md`. `maxConcurrent` is
|
|
166
|
+
still collapsed with the other two for a session that registers one action and
|
|
167
|
+
awaits it — nothing a single `brambo run` does puts two operations in flight. And
|
|
168
|
+
the ESTIMATE is a flat 1 while the settlement is in the vendor's tokens, so a
|
|
169
|
+
host that budgets in tokens should pass its own `cost` through
|
|
170
|
+
`createExecutorPlugin`; brambo will not invent a pre-run token figure.
|
|
171
|
+
|
|
172
|
+
The settlement is also what the record stream carries: with a policy configured,
|
|
173
|
+
each admitted run emits an `action.estimated` and, if the vendor reported a
|
|
174
|
+
figure, an `action.settled`, so the total is reconstructable from the records
|
|
175
|
+
alone. With no policy set, the stream is exactly what it was before.
|
|
176
|
+
|
|
177
|
+
### What the session seam means today
|
|
178
|
+
|
|
179
|
+
The session package re-exports the SDK composition seam, but its current
|
|
180
|
+
executor run path does not contain a tool-call route. Supplying
|
|
181
|
+
`sandboxProvider`, `toolExecutor`, policy, approval, or callbacks therefore
|
|
182
|
+
validates/configures inputs but does not itself create a sandbox or execute a
|
|
183
|
+
tool. A host with an actual tool-call flow should create a provider-owned
|
|
184
|
+
session, call `createToolExecutor(session)`, and dispose it at the host
|
|
185
|
+
lifecycle boundary.
|
|
186
|
+
|
|
187
|
+
`ToolExecutor` preserves exact argv. A local descriptor such as
|
|
188
|
+
`{ kind: 'local', argv: ['node', 'server.mjs'] }` with `['--config', 'a b']`
|
|
189
|
+
reaches the provider as `['node', 'server.mjs', '--config', 'a b']`; no shell
|
|
190
|
+
parsing or arbitrary JavaScript handler is involved. An `mcp-stdio` descriptor
|
|
191
|
+
uses the same argv and provider-owned framed stdio channel for local MCP.
|
|
192
|
+
|
|
193
|
+
Policies requiring unproven controls or resource limits fail closed. The
|
|
194
|
+
current local provider evidence is conservative; the remote provider is an
|
|
195
|
+
injected transport adapter, not a bundled protocol. `danger-full-access`
|
|
196
|
+
requires explicit acknowledgement and may emit validated start/completion
|
|
197
|
+
audit events, but neither audit events nor current tests prove OS isolation.
|
|
198
|
+
Repository/provider tests are current-platform evidence. The optional
|
|
199
|
+
Linux/macOS/Windows host-conformance runs have not been claimed as executed.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { DEFAULT_EXECUTOR_ID, EXECUTOR_CATALOGUE, EXECUTOR_CONFIG_KEY, availableExecutorIds, createExecutorAdapter } from '@skanl/brambo-adapter-cli';
|
|
2
|
+
import type { CliExecutorAdapterOptions, ShippedExecutor } from '@skanl/brambo-adapter-cli';
|
|
3
|
+
import type { ConfigLayer, LayeredConfig } from '@skanl/brambo-kernel';
|
|
4
|
+
export { DEFAULT_EXECUTOR_ID, EXECUTOR_CATALOGUE, EXECUTOR_CONFIG_KEY, availableExecutorIds, createExecutorAdapter, type ShippedExecutor, };
|
|
5
|
+
export type { CliExecutorAdapterOptions };
|
|
6
|
+
/** Brambo's own configuration document for a scope root. */
|
|
7
|
+
export declare function executorConfigPath(scopeDir: string): string;
|
|
8
|
+
export interface ResolveExecutorOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Explicit override for this invocation, e.g. `brambo run --executor codex`.
|
|
11
|
+
* Set as the `invocation` LAYER, so it wins over both documents and is
|
|
12
|
+
* reported as having done so. Omitted, no invocation layer exists at all.
|
|
13
|
+
*/
|
|
14
|
+
readonly executorId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Root of the machine scope; `<homeDir>/.brambo/config.json` is the `global`
|
|
17
|
+
* layer. A SEAM: it defaults to the OS home directory in production and every
|
|
18
|
+
* test points it at a temp directory, because a suite whose result depends on
|
|
19
|
+
* the `~/.brambo` of whoever runs it passes and fails for reasons having
|
|
20
|
+
* nothing to do with the code.
|
|
21
|
+
*/
|
|
22
|
+
readonly homeDir?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Root of the project scope; `<projectDir>/.brambo/config.json` is the
|
|
25
|
+
* `project` layer. The same seam, defaulting to `process.cwd()`.
|
|
26
|
+
*/
|
|
27
|
+
readonly projectDir?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ExecutorSelection {
|
|
30
|
+
/** The id that won; always a key of `EXECUTOR_CATALOGUE`. */
|
|
31
|
+
readonly executorId: string;
|
|
32
|
+
/**
|
|
33
|
+
* The layer that supplied it, taken from the layered config's OWN `dump()`.
|
|
34
|
+
* Never recomputed here: provenance derived a second time is how a report
|
|
35
|
+
* starts disagreeing with the thing it reports on.
|
|
36
|
+
*/
|
|
37
|
+
readonly layer: ConfigLayer;
|
|
38
|
+
/**
|
|
39
|
+
* Every id a selection may name. Here for a host that offers a CHOICE and has
|
|
40
|
+
* to render one; `@skanl/brambo-cli` does not print it, because on the one path where
|
|
41
|
+
* a user needs the list — an id brambo has no adapter for — the coded error's
|
|
42
|
+
* own message already carries it.
|
|
43
|
+
*/
|
|
44
|
+
readonly available: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
/** One of brambo's own configuration documents, and where it came from. */
|
|
47
|
+
export interface ExecutorConfigDocument {
|
|
48
|
+
/** The path it was read from, so a layer that rejects it can name the file. */
|
|
49
|
+
readonly filePath: string;
|
|
50
|
+
readonly document: unknown;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Brambo's own documents, READ but not yet composed.
|
|
54
|
+
*
|
|
55
|
+
* This exists so the documents are read ONCE per run. Story M3.B made the
|
|
56
|
+
* kernel's layered configuration the one the mounted plugins read, and the
|
|
57
|
+
* kernel is constructed inside `runSession` — so a caller that resolved a
|
|
58
|
+
* selection first and then ran would have read `.brambo/config.json` twice, with
|
|
59
|
+
* a window between them in which the two could disagree. Handing the SNAPSHOTS
|
|
60
|
+
* forward closes that window: `seedExecutorConfig` composes them into whichever
|
|
61
|
+
* configuration is going to be used, and nothing re-reads a file.
|
|
62
|
+
*/
|
|
63
|
+
export interface ExecutorConfigLayers {
|
|
64
|
+
/**
|
|
65
|
+
* Values composed UNDER brambo's own built-in default, so any document can
|
|
66
|
+
* still override them. `@skanl/brambo-session` puts its computed workspace root here
|
|
67
|
+
* when the caller named no `cwd`, which is what lets a user's
|
|
68
|
+
* `workspace.rootDir` actually decide the directory.
|
|
69
|
+
*/
|
|
70
|
+
readonly defaults?: unknown;
|
|
71
|
+
/** `<homeDir>/.brambo/config.json`, when it exists. */
|
|
72
|
+
readonly global?: ExecutorConfigDocument;
|
|
73
|
+
/** `<projectDir>/.brambo/config.json`, when it exists and is not the machine one. */
|
|
74
|
+
readonly project?: ExecutorConfigDocument;
|
|
75
|
+
/** This invocation's explicit override, e.g. `brambo run --executor codex`. */
|
|
76
|
+
readonly invocation?: unknown;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Reads brambo's own documents into layer snapshots. The ONLY filesystem access
|
|
80
|
+
* in executor selection.
|
|
81
|
+
*
|
|
82
|
+
* A MISSING document is an absent layer. A document that exists and cannot be
|
|
83
|
+
* used is a coded error — reading a corrupt file and shrugging back to the
|
|
84
|
+
* default runs a different agent than the user configured, silently, which is
|
|
85
|
+
* the failure this selection exists to remove.
|
|
86
|
+
*/
|
|
87
|
+
export declare function readExecutorConfigLayers(options?: ResolveExecutorOptions): Promise<ExecutorConfigLayers>;
|
|
88
|
+
/**
|
|
89
|
+
* Composes brambo's defaults and the given documents into ONE layered
|
|
90
|
+
* configuration: `defaults` -> `global` -> `project` -> `invocation`.
|
|
91
|
+
*
|
|
92
|
+
* The `setLayer` calls are WRAPPED because the kernel's validation is what
|
|
93
|
+
* rejects a hostile document, and it names the offending KEY rather than the
|
|
94
|
+
* file: a `__proto__` key in the machine document and in the project document
|
|
95
|
+
* produced byte-identical stderr naming neither, and an unbounded nesting depth
|
|
96
|
+
* (~3000 levels) produced a bare `RangeError` carrying no `code` at all — an
|
|
97
|
+
* UNCODED crash on the exact input class the matrix says must be refused coded.
|
|
98
|
+
* The kernel's own error travels on as the `cause`, so its code is preserved in
|
|
99
|
+
* the chain rather than swallowed.
|
|
100
|
+
*
|
|
101
|
+
* Since Story M3.B this is what seeds the KERNEL's configuration, so the mounted
|
|
102
|
+
* plugins and the executor selection read one composed document rather than two.
|
|
103
|
+
*/
|
|
104
|
+
/**
|
|
105
|
+
* A key brambo READ off disk and refused to admit into its layer, with what it
|
|
106
|
+
* is running instead.
|
|
107
|
+
*
|
|
108
|
+
* Typed rather than logged, because AD-5 is "typed absence over silence" and its
|
|
109
|
+
* opposite of IGNORED is TYPED AND REPORTED, not FATAL. The guard this replaces
|
|
110
|
+
* read that as a binary -- "REFUSED rather than ignored, per AD-5" -- and made a
|
|
111
|
+
* cloned repository able to deny service to the machine owner's own selection.
|
|
112
|
+
* `using` is `undefined` when nothing else selects one, which is a different
|
|
113
|
+
* sentence and has to stay tellable apart.
|
|
114
|
+
*/
|
|
115
|
+
export interface DeclinedConfigKey {
|
|
116
|
+
readonly key: 'method';
|
|
117
|
+
/** What the project document recommended, verbatim. */
|
|
118
|
+
readonly specifier: string;
|
|
119
|
+
/** The document that recommended it, so the notice can name the file. */
|
|
120
|
+
readonly filePath: string;
|
|
121
|
+
/** What decides instead, taken from the COMPOSED view rather than guessed. */
|
|
122
|
+
readonly using: string | undefined;
|
|
123
|
+
}
|
|
124
|
+
export declare function seedExecutorConfig(config: LayeredConfig, layers?: ExecutorConfigLayers): DeclinedConfigKey | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* The selection an already-seeded configuration decides, with the layer that
|
|
127
|
+
* decided it. Pure: it reads the composed view and touches no file.
|
|
128
|
+
*/
|
|
129
|
+
export declare function selectExecutor(config: LayeredConfig): ExecutorSelection;
|
|
130
|
+
/**
|
|
131
|
+
* Which executor this run uses, resolved through the kernel's layered
|
|
132
|
+
* configuration: `defaults` -> `global` -> `project` -> `invocation`.
|
|
133
|
+
*
|
|
134
|
+
* This — not `runSession` — is what reads the filesystem. A session primitive
|
|
135
|
+
* whose behaviour depends on files under the running user's home is not usable
|
|
136
|
+
* from a host that already knows what it wants, and it would make every existing
|
|
137
|
+
* `brambo run` test depend on the `~/.brambo` of whoever ran the suite.
|
|
138
|
+
*
|
|
139
|
+
* Ships from `@skanl/brambo-session` beside `runSession`, so FR-29 holds: a third party
|
|
140
|
+
* imports this package and gets the selection AND the run, with no CLI involved.
|
|
141
|
+
*
|
|
142
|
+
* `brambo run` does NOT call this: it reads the layers once and hands them to
|
|
143
|
+
* `runSession`, which seeds the KERNEL's configuration and selects from that one
|
|
144
|
+
* composed document. The three steps are exactly the three this function performs.
|
|
145
|
+
*/
|
|
146
|
+
export declare function resolveExecutor(options?: ResolveExecutorOptions): Promise<ExecutorSelection>;
|