@inixiative/agent-session 0.12.0 → 0.12.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/README.md +44 -10
- package/package.json +5 -4
- package/src/muse-session.ts +14 -11
- package/src/transport.ts +2 -2
package/README.md
CHANGED
|
@@ -51,7 +51,8 @@ session.kill();
|
|
|
51
51
|
blocks in the same user message, Codex `image` inputs with a `data:` URL. Anything else is
|
|
52
52
|
refused before a byte is written.
|
|
53
53
|
- **fork / resume / interrupt** and pre-send hooks (rewrite the outgoing
|
|
54
|
-
message, for example to add context for that turn).
|
|
54
|
+
message, for example to add context for that turn). Resume is configuration, not a
|
|
55
|
+
method: build the session with `externalSessionId`.
|
|
55
56
|
- **A resume whose native conversation is not found is typed.** Claude finding no transcript
|
|
56
57
|
("No conversation found with session ID": cleaned up, deleted, or under another project
|
|
57
58
|
directory or config home) and Codex finding no rollout ("no rollout found for thread id") both
|
|
@@ -96,12 +97,13 @@ Codex runs only over `codex app-server --listen stdio://` (`CodexAppServerSessio
|
|
|
96
97
|
Codex CLI 0.155 has no `mcp-server` subcommand.
|
|
97
98
|
|
|
98
99
|
Muse Code runs over `muse serve` through Meta's `@muse-code/sdk` (`MuseSession`), pinned to
|
|
99
|
-
the CLI's version (1.4.
|
|
100
|
+
the CLI's version (1.4.20): a host serving another version is refused at start. It bills the
|
|
100
101
|
saved Muse login: metered keys and endpoints never reach it (nor Muse's own tool runs, which
|
|
101
102
|
inherit its environment: a repo command needing `OPENAI_API_KEY` will not see it under Muse),
|
|
102
103
|
and a resumed session must be on provider `meta`. Per-login isolation is `env: { XDG_CONFIG_HOME, XDG_DATA_HOME }` (login and
|
|
103
|
-
sessions). `
|
|
104
|
-
the host default lets Muse's AI reviewer answer
|
|
104
|
+
sessions). The session's `permissionMode` (default `default`, gated) goes through `musePermissions(mode)`
|
|
105
|
+
to an explicit permission profile, because the host default lets Muse's AI reviewer answer
|
|
106
|
+
approvals instead of the gates. Questions arrive
|
|
105
107
|
through `onUserInput()`; unanswered ones are cancelled with a reason. An approval offering
|
|
106
108
|
no approve-once or reject choice is reported as an `error` event and left open (the turn ends
|
|
107
109
|
at its timeout). Work Muse starts itself
|
|
@@ -233,9 +235,11 @@ const result = await decisions.decide(spec, { input, standby });
|
|
|
233
235
|
main thread, which wakes the main model (Claude Code 2.1.300). Members of a
|
|
234
236
|
dispatcher that ended unasked are therefore never retired or stopped, and an
|
|
235
237
|
orphan's `answer` call is held for the session's life. Past `maxOrphans`
|
|
236
|
-
(40)
|
|
238
|
+
(40) `serving()` turns false and `setup` reports `degraded`; `decide()` does
|
|
239
|
+
not refuse on its own, so the caller checks `serving()` and falls back.
|
|
237
240
|
- **Limits.** Claude Code runs 20 subagents at once by default; the launch
|
|
238
|
-
raises `CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS` (
|
|
241
|
+
raises `CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS` to the user's own cap (20 when
|
|
242
|
+
unset) plus `maxSubagents` (76), so 96 by default. The
|
|
239
243
|
session declares `perTaskStopAffordance`, so the user's Stop spares
|
|
240
244
|
background agents.
|
|
241
245
|
|
|
@@ -251,9 +255,12 @@ const pool = new SubscriptionPool({ instances: [
|
|
|
251
255
|
const { session, lease } = await pool.open({ runtime: "claude", model: "sonnet", organizationId: "org", preferredInstanceId: "work" });
|
|
252
256
|
```
|
|
253
257
|
|
|
254
|
-
Routing is deterministic
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
Routing is deterministic over observed limits, priority, leases and health, by a
|
|
259
|
+
named strategy (`quartile-balanced` by default, `owner-first`, `priority-balanced`,
|
|
260
|
+
`priority-strict`, `least-used`) or an explicit term list; `pinned` restricts it to
|
|
261
|
+
the preferred instance. By default stale or unknown limits exclude an instance
|
|
262
|
+
(`unknownUtilization: 'rank-last'` ranks it last instead), and every exclusion has a
|
|
263
|
+
reason. `continueOn` moves a thread only between
|
|
257
264
|
instances that share native history, and says why when it cannot.
|
|
258
265
|
|
|
259
266
|
## Usage telemetry
|
|
@@ -271,6 +278,33 @@ model and tags) in `raw`; they may repeat across content blocks. Do not sum
|
|
|
271
278
|
request snapshots together with result totals. Interrupted turns retain request
|
|
272
279
|
snapshots in the artifact but do not fabricate a completed-turn total.
|
|
273
280
|
|
|
281
|
+
## Secret scratch pad
|
|
282
|
+
|
|
283
|
+
A person can hand an agent a secret as `$$word`. `SecretScratchPad(dir)` lifts it out
|
|
284
|
+
of the message before the agent sees it (`lift`), into a private pad per thread
|
|
285
|
+
(`<thread>.pad`, 0600 in a 0700 directory), and leaves a reference in its place:
|
|
286
|
+
`†<message>.<n>` inside its thread, `†<thread>/<message>.<n>` from anywhere else.
|
|
287
|
+
`lift(text, at, { patterns })` also lifts matches of the caller's global regexes, for
|
|
288
|
+
tokens the person forgot to mark, and counts them as `recognised`.
|
|
289
|
+
|
|
290
|
+
The agent uses a value without reading it. `env` sets `KEY=value` in an env file,
|
|
291
|
+
`write` puts it in a file, and `pipe` runs a command with the value on its stdin,
|
|
292
|
+
from a reference or a key already in an env file; only the exit code comes back, and
|
|
293
|
+
a timeout kills the whole process group. `consume` (or `consume: true` on a use)
|
|
294
|
+
removes a block, `list` shows what is pending without values, `scrub` swaps held
|
|
295
|
+
values in text back to references, and `clear` deletes a pad.
|
|
296
|
+
|
|
297
|
+
Files `env` and `write` touched are recorded (`heldFiles()`). `privatePathGate`
|
|
298
|
+
(or `privatePathDenial` inside your own gate) denies a tool call naming one of them,
|
|
299
|
+
a `.private` path, a `privatePaths` glob or the pad directory; `isOwnTool` exempts the
|
|
300
|
+
scratch tool's own calls. A `privatePathGate` keeps the `heldFiles` list it was built
|
|
301
|
+
with: rebuild it after `env` or `write`, or call
|
|
302
|
+
`privatePathDenial(request, { scratchDir: pad.dir, heldFiles: pad.heldFiles() })` in your
|
|
303
|
+
own gate on each request. `SECRET_SCRATCH_GUIDANCE` is the text to give the agent.
|
|
304
|
+
The gate reads names, not contents, and a pipe command can still copy a value out:
|
|
305
|
+
it keeps honest agents from reading secrets, it is not a sandbox. Design and
|
|
306
|
+
limits: [tickets/AS-009](tickets/AS-009-secret-scratch-pad.md).
|
|
307
|
+
|
|
274
308
|
## Roadmap
|
|
275
309
|
|
|
276
310
|
| Runtime | Status |
|
|
@@ -299,4 +333,4 @@ MIT
|
|
|
299
333
|
|
|
300
334
|
## Runtime
|
|
301
335
|
|
|
302
|
-
Development and new continuation captures use Bun 1.4.
|
|
336
|
+
Development and new continuation captures use Bun 1.4.4, pinned in `.bun-version` and `package.json`. Historical S0 recordings retain their original Bun 1.3.14 evidence; they are not new 1.4.4 captures.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inixiative/agent-session",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Drive coding-agent CLIs as persistent, streaming, event-captured sessions over interchangeable transports, with primed decision sessions, subscription pools, routing, limit polling and continuity.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"check": "bun run typecheck && bun run lint && bun run test",
|
|
19
19
|
"prepublishOnly": "bun run check",
|
|
20
20
|
"measure:primed": "bun scripts/measure-primed.ts",
|
|
21
|
+
"record:muse": "bun scripts/record-muse.ts",
|
|
21
22
|
"lint": "biome check .",
|
|
22
23
|
"prepare": "if git rev-parse --git-dir >/dev/null 2>&1; then lefthook install; fi"
|
|
23
24
|
},
|
|
@@ -36,12 +37,12 @@
|
|
|
36
37
|
],
|
|
37
38
|
"license": "MIT",
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@inixiative/json-rules": "^3.
|
|
40
|
-
"@muse-code/sdk": "1.4.
|
|
40
|
+
"@inixiative/json-rules": "^3.22.0",
|
|
41
|
+
"@muse-code/sdk": "1.4.20"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@biomejs/biome": "2.5.2",
|
|
44
|
-
"@inixiative/config": "^0.5.
|
|
45
|
+
"@inixiative/config": "^0.5.212",
|
|
45
46
|
"@types/bun": "1.4.4",
|
|
46
47
|
"lefthook": "2.1.9",
|
|
47
48
|
"typescript": "6.0.3"
|
package/src/muse-session.ts
CHANGED
|
@@ -40,17 +40,18 @@ import { TurnState } from './turn-state';
|
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
// MuseSession — one `muse serve` host per session, driven through Meta's
|
|
42
42
|
// `@muse-code/sdk` MuseClient (Muse Session Protocol). The facade opens the
|
|
43
|
-
// session, submits turns, answers approvals and folds the view
|
|
44
|
-
//
|
|
45
|
-
//
|
|
43
|
+
// session, submits turns, answers approvals and folds the view. turn/interrupt,
|
|
44
|
+
// turn/steer, session/fork, session/setApprovalMode, usage/read and
|
|
45
|
+
// userInput/cancel go through the same connection's `command`, written before
|
|
46
|
+
// the facade had them (1.4.14) and kept on the raw wire, whose types 1.4.20 keeps.
|
|
46
47
|
// Every view notification is classified into the shared SessionEvent taxonomy.
|
|
47
48
|
// The Muse session id is the externalSessionId used for cold resume.
|
|
48
49
|
//
|
|
49
|
-
// Wire contract: Muse Code 1.4.
|
|
50
|
+
// Wire contract: Muse Code 1.4.20 / @muse-code/sdk 1.4.20 (lockstep).
|
|
50
51
|
// ---------------------------------------------------------------------------
|
|
51
52
|
|
|
52
53
|
/** The SDK version this session is built against; the served CLI must report the same. */
|
|
53
|
-
export const MUSE_SDK_VERSION = '1.4.
|
|
54
|
+
export const MUSE_SDK_VERSION = '1.4.20';
|
|
54
55
|
|
|
55
56
|
export type MuseApprovalMode = 'allowAll' | 'promptUnmatched';
|
|
56
57
|
export type MusePermissionProfile = ':ask-me' | ':read-only' | ':unrestricted';
|
|
@@ -226,13 +227,15 @@ const CLIENT_INFO = { name: 'agent_session', title: 'agent-session', version: '1
|
|
|
226
227
|
|
|
227
228
|
/**
|
|
228
229
|
* Inherited variables that would bill, route or loosen Muse away from its saved subscription login
|
|
229
|
-
* and the gates, by the names the 1.4.
|
|
230
|
+
* and the gates, by the names the 1.4.20 binary reads: metered keys and endpoints (META_, LLM_,
|
|
230
231
|
* MODEL_, LLAMA_, OPENROUTER_, ANTHROPIC_, OPENAI_), Meta's auth and key-mint endpoints, and MUSE_
|
|
231
|
-
* switches for the model, experiments, contributor mode, the approval judge
|
|
232
|
-
*
|
|
232
|
+
* switches for the model, experiments, contributor mode, the approval judge, a fail-open profile,
|
|
233
|
+
* request routing and custom headers, the computer-use dev policy and identity skip, the launcher's
|
|
234
|
+
* channel and install switches, the credential backend, and the eval and test hooks (prompt injection,
|
|
235
|
+
* policy and grant overrides). The CA and TLS roots stay: a machine behind a proxy needs them. Muse's own tool runs inherit this environment, so they do not see these either.
|
|
233
236
|
*/
|
|
234
237
|
const COMPETING_ENV =
|
|
235
|
-
/^(META_|LLM_|MODEL_|LLAMA_|OPENROUTER_|ANTHROPIC_|OPENAI_|TBH_(AUTH|MINT)_BASE_URL$|MUSE_EXPERIMENTAL_|MUSE_(MODEL|CONTRIBUTOR_MODE|DISABLE_APPROVAL_JUDGE|PERMISSION_PROFILE_FAIL_OPEN)$)/;
|
|
238
|
+
/^(META_|LLM_|MODEL_|LLAMA_|OPENROUTER_|ANTHROPIC_|OPENAI_|TBH_(AUTH|MINT)_BASE_URL$|MUSE_EXPERIMENTAL_|TBH_(CREDENTIAL_BACKEND$|EVAL_|TEST_)|MUSE_CUA_DEV_|MUSE_(MODEL|CHANNEL|LAUNCHER_INSTALL|CONTRIBUTOR_MODE|DISABLE_APPROVAL_JUDGE|PERMISSION_PROFILE_FAIL_OPEN|WWW_ROUTING|CUSTOM_HEADERS)$)/;
|
|
236
239
|
|
|
237
240
|
export class MuseSession implements HarnessSession {
|
|
238
241
|
get transport() {
|
|
@@ -1914,10 +1917,10 @@ export class MuseSession implements HarnessSession {
|
|
|
1914
1917
|
}
|
|
1915
1918
|
|
|
1916
1919
|
// ---------------------------------------------------------------------------
|
|
1917
|
-
// Default host: `muse serve` through the SDK's owned-process handshake
|
|
1920
|
+
// Default host: `muse serve` through the SDK's owned-process handshake; exported for scripts/record-muse.ts
|
|
1918
1921
|
// ---------------------------------------------------------------------------
|
|
1919
1922
|
|
|
1920
|
-
async function spawnMuseHost(launch: MuseLaunch): Promise<MuseHost> {
|
|
1923
|
+
export async function spawnMuseHost(launch: MuseLaunch): Promise<MuseHost> {
|
|
1921
1924
|
const handshake = spawnMspConnection({
|
|
1922
1925
|
command: launch.museBin,
|
|
1923
1926
|
args: launch.args,
|
package/src/transport.ts
CHANGED
|
@@ -143,12 +143,12 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
143
143
|
},
|
|
144
144
|
notes: [
|
|
145
145
|
"`muse serve --trust-workspace` (plus `--disable-sandbox` under bypassPermissions) driven through Meta's `@muse-code/sdk` MuseClient, pinned to the installed CLI's version.",
|
|
146
|
-
'Billing is the saved Muse Code login: metered keys and endpoints (META_, LLM_, MODEL_, LLAMA_, OPENROUTER_, ANTHROPIC_, OPENAI_), Meta auth endpoints (TBH_) and the MUSE_ model, experiment, contributor, approval-judge
|
|
146
|
+
'Billing is the saved Muse Code login: metered keys and endpoints (META_, LLM_, MODEL_, LLAMA_, OPENROUTER_, ANTHROPIC_, OPENAI_), Meta auth endpoints (TBH_) and the MUSE_ model, experiment, contributor, approval-judge, fail-open, routing, custom-header, computer-use dev, channel and install switches, and the TBH_ credential backend and eval/test hooks are removed from the child environment; a resumed or forked session must report provider `meta`.',
|
|
147
147
|
'Approvals use an explicit permission profile (`:ask-me` supervised, `:read-only` plan, `:unrestricted` bypass), never the host default `:auto-review`, whose AI reviewer would decide instead of the gates; the committed profile, approval mode and (gated) a human reviewer are read back before any turn.',
|
|
148
148
|
"Work Muse starts itself (cron firings, monitor and background-terminal wakes, goals, reminder agents, workflows, another client's messages) is folded like a turn's and emitted with `unattributedReason: 'task'`, plus a `task` lifecycle typed `muse-cron`, `muse-monitor`, `muse-goal`, `muse-reminder`, `muse-workflow` or `muse-turn`; it is seen only while this host has the session loaded.",
|
|
149
149
|
"interruptNative() sends turn/interrupt; push() sends turn/steer; fork() runs session/fork on the new session's own host; readLimits() sends usage/read.",
|
|
150
150
|
'userInput/request questions go to onUserInput(); without an answer they are cancelled with a reason, so a turn never waits on them.',
|
|
151
|
-
'Live-verified (muse 1.4.
|
|
151
|
+
'Live-verified (muse 1.4.20, 2026-11-15): start, profile read-back, multi-stage shell and network approvals, text and tool events, token usage, usage/changed and usage/read (recorded in fixtures/muse); fork, resume with setApprovalMode and sessionNotFound were checked live on 1.4.14 without a model turn (not recorded). A steer (`turn/steer` into a running shell turn, answered in that turn) and an interrupt (`turn/interrupt`, terminal `cancelled`) were checked live on 1.4.20, 2026-11-15 (not recorded). Images, questions and Muse-initiated turns follow the SDK wire types without a live run.',
|
|
152
152
|
],
|
|
153
153
|
},
|
|
154
154
|
'claude-agent-sdk': {
|