dsh-loop-engine-allinone 0.0.0-stage → 0.4.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/CHANGELOG.md +119 -0
- package/CONTRIBUTING.md +78 -0
- package/LICENSE +23 -0
- package/NOTICE +45 -0
- package/README.md +223 -2
- package/README.zh.md +179 -0
- package/SECURITY.md +39 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +2166 -0
- package/lib/index.js +11621 -0
- package/lib/invariant.js +453 -0
- package/lib/types/agent-preset-ids.d.ts +276 -0
- package/lib/types/client/LoopEngineBadge.d.ts +71 -0
- package/lib/types/client/LoopEngineComposerSelect.d.ts +126 -0
- package/lib/types/client/LoopEngineSection.d.ts +35 -0
- package/lib/types/client/engine-marks.d.ts +87 -0
- package/lib/types/client/index.d.ts +39 -0
- package/lib/types/client/locales.d.ts +182 -0
- package/lib/types/client/reload.d.ts +135 -0
- package/lib/types/client/session-engine.d.ts +508 -0
- package/lib/types/client/store.d.ts +103 -0
- package/lib/types/client/turn-status.d.ts +234 -0
- package/lib/types/client/use-session-engine.d.ts +74 -0
- package/lib/types/commands.d.ts +77 -0
- package/lib/types/compat.d.ts +25 -0
- package/lib/types/driver-acp/agent.d.ts +289 -0
- package/lib/types/driver-acp/approval.d.ts +27 -0
- package/lib/types/driver-acp/capabilities.d.ts +200 -0
- package/lib/types/driver-acp/client.d.ts +297 -0
- package/lib/types/driver-acp/executable.d.ts +53 -0
- package/lib/types/driver-acp/mapping.d.ts +76 -0
- package/lib/types/driver-acp/profile.d.ts +126 -0
- package/lib/types/driver-acp/runtime.d.ts +45 -0
- package/lib/types/driver-acp/subprocess.d.ts +19 -0
- package/lib/types/driver-acp/terminal.d.ts +98 -0
- package/lib/types/driver-acp/transport.d.ts +43 -0
- package/lib/types/driver-acp/types.d.ts +152 -0
- package/lib/types/driver-cli/capability.d.ts +42 -0
- package/lib/types/driver-cli/cli-agent.d.ts +258 -0
- package/lib/types/driver-cli/cli-loop.d.ts +46 -0
- package/lib/types/driver-cli/flatten.d.ts +35 -0
- package/lib/types/driver-cli/frames.d.ts +128 -0
- package/lib/types/driver-cli/ndjson.d.ts +106 -0
- package/lib/types/driver-cli/session-sink.d.ts +333 -0
- package/lib/types/driver-cli/stream-json.d.ts +61 -0
- package/lib/types/driver-cli/text.d.ts +66 -0
- package/lib/types/driver-cli/transport.d.ts +84 -0
- package/lib/types/driver-core/agents-md-skill-provider.d.ts +72 -0
- package/lib/types/driver-core/assistant-stream.d.ts +68 -0
- package/lib/types/driver-core/context-files.d.ts +62 -0
- package/lib/types/driver-core/host-servers.d.ts +119 -0
- package/lib/types/driver-core/hosted-engine-runtime.d.ts +190 -0
- package/lib/types/driver-core/hosted-tool-vocabulary.d.ts +78 -0
- package/lib/types/driver-core/idle-child.d.ts +57 -0
- package/lib/types/driver-core/image-access.d.ts +25 -0
- package/lib/types/driver-core/inbox.d.ts +104 -0
- package/lib/types/driver-core/model-handover.d.ts +134 -0
- package/lib/types/driver-core/ownership.d.ts +41 -0
- package/lib/types/driver-core/permission-knobs.d.ts +26 -0
- package/lib/types/driver-core/prompt.d.ts +64 -0
- package/lib/types/driver-core/session-lifetime.d.ts +62 -0
- package/lib/types/driver-core/session-model.d.ts +128 -0
- package/lib/types/driver-core/skill-inject.d.ts +59 -0
- package/lib/types/driver-core/step-close.d.ts +49 -0
- package/lib/types/driver-core/step-images.d.ts +62 -0
- package/lib/types/driver-core/step-tools.d.ts +43 -0
- package/lib/types/driver-core/system-head.d.ts +43 -0
- package/lib/types/engine-agy/agent.d.ts +67 -0
- package/lib/types/engine-agy/frames.d.ts +90 -0
- package/lib/types/engine-agy/loop.d.ts +64 -0
- package/lib/types/engine-agy/process.d.ts +173 -0
- package/lib/types/engine-claude/agent.d.ts +186 -0
- package/lib/types/engine-claude/loop.d.ts +73 -0
- package/lib/types/engine-claude/mapping.d.ts +108 -0
- package/lib/types/engine-claude/permission.d.ts +41 -0
- package/lib/types/engine-claude/process.d.ts +59 -0
- package/lib/types/engine-claude/sdk.d.ts +67 -0
- package/lib/types/engine-claude/types.d.ts +28 -0
- package/lib/types/engine-codex/agent.d.ts +216 -0
- package/lib/types/engine-codex/appserver/client.d.ts +80 -0
- package/lib/types/engine-codex/appserver/mapping.d.ts +67 -0
- package/lib/types/engine-codex/appserver/thread.d.ts +66 -0
- package/lib/types/engine-codex/appserver/types.d.ts +225 -0
- package/lib/types/engine-codex/loop.d.ts +75 -0
- package/lib/types/engine-codex/model-handover.d.ts +56 -0
- package/lib/types/engine-codex/permission.d.ts +126 -0
- package/lib/types/engine-codex/skills.d.ts +28 -0
- package/lib/types/engine-codex/types.d.ts +36 -0
- package/lib/types/engine-drivers.d.ts +132 -0
- package/lib/types/engine-grok/agent.d.ts +24 -0
- package/lib/types/engine-grok/loop.d.ts +61 -0
- package/lib/types/engine-grok/process.d.ts +46 -0
- package/lib/types/engine-grok/profile.d.ts +82 -0
- package/lib/types/engine-hermes/agent.d.ts +47 -0
- package/lib/types/engine-hermes/loop.d.ts +56 -0
- package/lib/types/engine-hermes/process.d.ts +92 -0
- package/lib/types/engine-kimi/acp/client.d.ts +123 -0
- package/lib/types/engine-kimi/acp/mapping.d.ts +73 -0
- package/lib/types/engine-kimi/acp/types.d.ts +143 -0
- package/lib/types/engine-kimi/agent.d.ts +24 -0
- package/lib/types/engine-kimi/commands.d.ts +44 -0
- package/lib/types/engine-kimi/loop.d.ts +62 -0
- package/lib/types/engine-kimi/model-handover.d.ts +32 -0
- package/lib/types/engine-kimi/permission.d.ts +28 -0
- package/lib/types/engine-kimi/process.d.ts +47 -0
- package/lib/types/engine-kimi/profile.d.ts +19 -0
- package/lib/types/engine-kimi/skills.d.ts +52 -0
- package/lib/types/engine-kimi/types.d.ts +25 -0
- package/lib/types/engine-minimaxcode/agent.d.ts +24 -0
- package/lib/types/engine-minimaxcode/loop.d.ts +83 -0
- package/lib/types/engine-minimaxcode/process.d.ts +53 -0
- package/lib/types/engine-minimaxcode/profile.d.ts +72 -0
- package/lib/types/engine-of-session.d.ts +97 -0
- package/lib/types/engine-pi/agent.d.ts +230 -0
- package/lib/types/engine-pi/loop.d.ts +72 -0
- package/lib/types/engine-pi/model-handover.d.ts +84 -0
- package/lib/types/engine-pi/permission.d.ts +47 -0
- package/lib/types/engine-pi/rpc/client.d.ts +107 -0
- package/lib/types/engine-pi/rpc/mapping.d.ts +37 -0
- package/lib/types/engine-pi/rpc/types.d.ts +249 -0
- package/lib/types/engine-pi/skills.d.ts +47 -0
- package/lib/types/engine-pi/types.d.ts +36 -0
- package/lib/types/engine-qoderclicn/agent.d.ts +59 -0
- package/lib/types/engine-qoderclicn/loop.d.ts +51 -0
- package/lib/types/engine-qoderclicn/process.d.ts +113 -0
- package/lib/types/engine-registry.d.ts +584 -0
- package/lib/types/engine-remote.d.ts +192 -0
- package/lib/types/engine-surface.d.ts +36 -0
- package/lib/types/engine-zcode/agent.d.ts +50 -0
- package/lib/types/engine-zcode/loop.d.ts +47 -0
- package/lib/types/engine-zcode/process.d.ts +111 -0
- package/lib/types/index.d.ts +205 -0
- package/lib/types/invariant.d.ts +26 -0
- package/lib/types/model-selection-reset.d.ts +271 -0
- package/lib/types/namespace.d.ts +25 -0
- package/lib/types/patch-manager.d.ts +104 -0
- package/lib/types/preset.d.ts +254 -0
- package/lib/types/provider-route.d.ts +245 -0
- package/lib/types/router-loop.d.ts +458 -0
- package/lib/types/session-engine-store.d.ts +138 -0
- package/lib/types/settings.d.ts +60 -0
- package/lib/types/skills.d.ts +85 -0
- package/package.json +152 -6
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiniMax Code's ACP dialect: the shared ACP body with mcode's own handshake.
|
|
3
|
+
*
|
|
4
|
+
* Measured against MiniMax Code 0.6.12 on this fork's reference host (raw frames
|
|
5
|
+
* with `scripts/probe/acp-handshake.mjs`), and the reason each hook below exists:
|
|
6
|
+
*
|
|
7
|
+
* 1. `initialize` answers `protocolVersion: 1`, advertises NO auth method (the
|
|
8
|
+
* CLI reads its own `~/.minimax/auth/prod/<region>/…` state and its
|
|
9
|
+
* subscription is chosen with `mcode provider use token-plan`), and reports
|
|
10
|
+
* `promptCapabilities.image: true` — so this engine does receive image blocks;
|
|
11
|
+
* 2. `session/new` succeeds WITHOUT a prior `authenticate` and answers a
|
|
12
|
+
* `sessionId`, ACP `modes` (`default`/`plan`), and `configOptions` for
|
|
13
|
+
* `permissionMode`, `model` and `thinkingEffort`. The model ids are the
|
|
14
|
+
* engine's own composite form (`m:minimax:MiniMax-M3:v:thinking`);
|
|
15
|
+
* 3. a model or permission is changed with `session/set_config_option` and a BARE
|
|
16
|
+
* value (`value: 'm:…'`) — the `{ value }` wrapper that Grok's documentation
|
|
17
|
+
* shows is REJECTED here with `-32602 Invalid params`. `session/set_model`
|
|
18
|
+
* does not exist on this engine at all (`-32601 Method not found`), which is
|
|
19
|
+
* why the shared driver's model selection is a profile hook;
|
|
20
|
+
* 4. `session/load` is accepted, so engine-side resume exists even though this
|
|
21
|
+
* driver replays the durable transcript and opens a fresh session per step.
|
|
22
|
+
*
|
|
23
|
+
* ## The permission fold
|
|
24
|
+
*
|
|
25
|
+
* The engine's `permissionMode` option is its own gate (`default` = Ask,
|
|
26
|
+
* `auto` = Auto, `bypassPermissions` = Full access). The driver maps the session's
|
|
27
|
+
* dsh approval knob onto it only where the knob is decisive — `never` becomes
|
|
28
|
+
* `bypassPermissions` and `ask` becomes `default` — and otherwise LEAVES the
|
|
29
|
+
* engine's own default alone (`auto`), rather than inventing a policy the user
|
|
30
|
+
* never chose. Whatever the engine still asks for arrives as the reverse-RPC
|
|
31
|
+
* `session/request_permission`, answered by the shared fold.
|
|
32
|
+
*
|
|
33
|
+
* @module dsh-loop-engine-allinone/engine-minimaxcode/profile
|
|
34
|
+
*/
|
|
35
|
+
import type { AcpEngineProfile } from '../driver-acp/profile.ts';
|
|
36
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
37
|
+
/** The engine's model config option id, as `session/new` advertises it. */
|
|
38
|
+
export declare const MCODE_MODEL_CONFIG_ID = "model";
|
|
39
|
+
/** The engine's tool-permission config option id (category `_permission`). */
|
|
40
|
+
export declare const MCODE_PERMISSION_CONFIG_ID = "permissionMode";
|
|
41
|
+
/**
|
|
42
|
+
* The engine's `permissionMode` value for one session's dsh approval knob.
|
|
43
|
+
*
|
|
44
|
+
* ALWAYS decisive, never "leave the engine's default alone". That default is not
|
|
45
|
+
* a safe resting state: `session/new` on this engine reports the option's
|
|
46
|
+
* `currentValue` from whatever the CLI last persisted in `~/.minimax/config.yaml`,
|
|
47
|
+
* and a previous run — including a probe, measured on this fork's reference
|
|
48
|
+
* host — can leave `bypassPermissions` there. A driver that only spoke when the
|
|
49
|
+
* knob was decisive would then inherit Full access for a session that asked for
|
|
50
|
+
* nothing of the sort.
|
|
51
|
+
*
|
|
52
|
+
* The mapping itself follows the shared fold's semantics:
|
|
53
|
+
* - `never` (auto-approve) means the human is not in the loop at all, so the
|
|
54
|
+
* engine is told its most permissive mode rather than asking a gate that
|
|
55
|
+
* would always answer yes;
|
|
56
|
+
* - `ask` means there IS no human in the unattended runtime, so the engine's Ask
|
|
57
|
+
* mode is selected and the reverse-RPC fold denies — fail-closed, exactly like
|
|
58
|
+
* the kimi and codex bridges;
|
|
59
|
+
* - an unset knob is not a choice, so the engine's own middle mode is selected:
|
|
60
|
+
* it may still ask, and the fold (which approves under this policy) answers.
|
|
61
|
+
*
|
|
62
|
+
* KNOWN SIDE EFFECT, documented rather than hidden: this option is
|
|
63
|
+
* process-scoped, and the engine persists it, so setting it here rewrites the
|
|
64
|
+
* user's `~/.minimax/config.yaml`. Stating the mode per session is the price of
|
|
65
|
+
* never inheriting a stale Full-access default.
|
|
66
|
+
* @param session - the session whose durable knobs are read.
|
|
67
|
+
* @returns the `permissionMode` value to set.
|
|
68
|
+
*/
|
|
69
|
+
export declare function mcodePermissionMode(session: Session): string;
|
|
70
|
+
/** MiniMax Code's ACP dialect, as the shared driver reads it. */
|
|
71
|
+
export declare function minimaxcodeProfile(bin: string): AcpEngineProfile;
|
|
72
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine one session runs: the single authoritative read, shared by the
|
|
3
|
+
* router's routing decision and the plugin's own Remote.
|
|
4
|
+
*
|
|
5
|
+
* Two facts can answer it, in this order:
|
|
6
|
+
*
|
|
7
|
+
* 1. THE PLUGIN'S OWN RECORD — `$DSH_HOME/.loop-engine/engines.json` (see
|
|
8
|
+
* `./session-engine-store.ts`). It exists because a user may move a session
|
|
9
|
+
* to another engine at any time, including long after the session started,
|
|
10
|
+
* and the harness's preset channel refuses exactly that
|
|
11
|
+
* (`agent-preset/locked`), so the plugin keeps the choice itself. When the
|
|
12
|
+
* record names an engine, that IS the session's engine and nothing else is
|
|
13
|
+
* consulted — not even the durable log, which is why a session with a record
|
|
14
|
+
* still reads correctly when its persistence is unavailable.
|
|
15
|
+
* 2. THE RECORDED AGENT PRESET, for every session the plugin has no record
|
|
16
|
+
* for: a session created before per-session switching existed, one created
|
|
17
|
+
* on a preset this plugin does not own, or one in a deployment whose record
|
|
18
|
+
* file is gone. That answer comes from the durable log — the harness's
|
|
19
|
+
* `agentPreset` projection folds the session header together with every
|
|
20
|
+
* committed `agent-preset/selected`, so reading it IS reading the log — and
|
|
21
|
+
* it never comes from the session header alone (a creation fact, deep-frozen)
|
|
22
|
+
* or from a client-side listing hint (a partial cache).
|
|
23
|
+
*
|
|
24
|
+
* This module is the ONLY read: the router routes on what it returns and the
|
|
25
|
+
* plugin's own Remote reports the same value, so the engine a session is driven
|
|
26
|
+
* by and the engine it is shown as running cannot disagree.
|
|
27
|
+
*
|
|
28
|
+
* There is one refinement on top of it, and it is the whole reason
|
|
29
|
+
* {@link engineReportOfSession} exists: a session with a LIVE agent has a better
|
|
30
|
+
* answer than its record — the agent in front of it — and the two can differ
|
|
31
|
+
* (only when a release of that agent did not take, since every other switch
|
|
32
|
+
* either swaps the agent in place or leaves the session cold). The report
|
|
33
|
+
* carries both, never blending them (see {@link SessionEngineReport}).
|
|
34
|
+
*
|
|
35
|
+
* The secondary read goes through the same seam the host itself uses before
|
|
36
|
+
* choosing the composition to mount
|
|
37
|
+
* (`ctx.sessionQuery.observeSession(id, { projectionMode: 'all' })`, see
|
|
38
|
+
* `packages/api/session-controller/src/agent.ts` in the harness). The
|
|
39
|
+
* observation is a read-only lease: it is released immediately through `using`,
|
|
40
|
+
* takes no write handle, and holds no write lock.
|
|
41
|
+
*
|
|
42
|
+
* @module dsh-loop-engine-allinone/engine-of-session
|
|
43
|
+
*/
|
|
44
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
45
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
46
|
+
import { type LoopEngineId, type SessionEngine, type SessionEngineReport } from './agent-preset-ids.ts';
|
|
47
|
+
import type { EngineRecordSource } from './session-engine-store.ts';
|
|
48
|
+
/**
|
|
49
|
+
* The engine one session runs, read from the plugin's own record first and from
|
|
50
|
+
* the durable `agentPreset` projection otherwise.
|
|
51
|
+
*
|
|
52
|
+
* A deployment that composes no `sessionQuery` service has no durable log to
|
|
53
|
+
* ask, so a session with no record reads as {@link SessionEngine} `unset`: no
|
|
54
|
+
* engine is claimed for it, and the router falls back exactly as it does for a
|
|
55
|
+
* session that recorded nothing.
|
|
56
|
+
* @param ctx - the context carrying the session-query seam.
|
|
57
|
+
* @param sessionId - the persisted session to inspect.
|
|
58
|
+
* @param records - the plugin's own per-session engine record.
|
|
59
|
+
* @returns what the plugin's record, or else the durable log, says about that
|
|
60
|
+
* session's engine.
|
|
61
|
+
*/
|
|
62
|
+
export declare function engineOfSession(ctx: Context, sessionId: SessionId, records: EngineRecordSource): Promise<SessionEngine>;
|
|
63
|
+
/**
|
|
64
|
+
* Reports the engine of one session's LIVE agent, when this process has one:
|
|
65
|
+
* the loop router's own bookkeeping (`RouterLoop.live`), which is the only
|
|
66
|
+
* record of what it actually built for a session.
|
|
67
|
+
*
|
|
68
|
+
* A reader rather than the router itself, so this fold can be exercised without
|
|
69
|
+
* one — and so the router stays the only thing that owns its bookkeeping.
|
|
70
|
+
*/
|
|
71
|
+
export type LiveEngineReader = (sessionId: SessionId) => LoopEngineId | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* What one session actually runs, and — when they differ — the engine its own
|
|
74
|
+
* record names.
|
|
75
|
+
*
|
|
76
|
+
* The live agent outranks the record, because it is the answer to the question
|
|
77
|
+
* this report exists to answer honestly: what is driving this session NOW. The
|
|
78
|
+
* record is still consulted, but as the OTHER fact — the engine this session
|
|
79
|
+
* takes when it is next built. A record that equals the live agent is no news
|
|
80
|
+
* and travels as absent; a live agent with no record at all has nothing to
|
|
81
|
+
* report beside it, because a session with no record was never switched.
|
|
82
|
+
*
|
|
83
|
+
* A session with NO live agent answers exactly as {@link engineOfSession} does:
|
|
84
|
+
* nothing is running, so the record (or, with no record, the recorded preset) IS
|
|
85
|
+
* the engine that session runs — the one its next build will use, which is the
|
|
86
|
+
* state a switch that released the session's agent leaves it in. There is no
|
|
87
|
+
* second fact to report and never a pending one.
|
|
88
|
+
* @param ctx - the context carrying the session-query seam.
|
|
89
|
+
* @param sessionId - the session to report on.
|
|
90
|
+
* @param records - the plugin's own per-session engine record.
|
|
91
|
+
* @param liveEngineOf - the engine of this session's live agent, when this
|
|
92
|
+
* process has one (the router's bookkeeping).
|
|
93
|
+
* @returns the engine driving the session, plus the recorded engine it is not
|
|
94
|
+
* running when the two differ.
|
|
95
|
+
*/
|
|
96
|
+
export declare function engineReportOfSession(ctx: Context, sessionId: SessionId, records: EngineRecordSource, liveEngineOf: LiveEngineReader): Promise<SessionEngineReport>;
|
|
97
|
+
//# sourceMappingURL=engine-of-session.d.ts.map
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi loop Agent: drives one session through turn and step boundaries by
|
|
3
|
+
* spawning a `pi --mode rpc` child process and speaking strict-LF JSONL over
|
|
4
|
+
* stdio. The dsh session log is the sole source of truth and each step runs one
|
|
5
|
+
* stateless Pi session (a fresh `new_session` + a single `prompt`), so the
|
|
6
|
+
* prompt is a pure serialization of the durable history plus the assembled dsh
|
|
7
|
+
* system prompt. Pi owns its tools natively but has no permission system, so
|
|
8
|
+
* the whole child is sandboxed by the dsh subprocess seam and its `--tools`
|
|
9
|
+
* are pruned to the resolved stance.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-loop-engine-allinone/engine-pi/agent
|
|
12
|
+
*/
|
|
13
|
+
import type { Agent, AgentCancelCause, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from '@deepseek-ai/dsh-agent';
|
|
14
|
+
import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
15
|
+
import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
16
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
+
import type { ResolvedConfig } from './types.ts';
|
|
18
|
+
import { DriverInbox } from '../driver-core/inbox.ts';
|
|
19
|
+
import { type PiSpawnCapability } from './rpc/client.ts';
|
|
20
|
+
/**
|
|
21
|
+
* Provider route label this driver logs into request/header snapshots and
|
|
22
|
+
* message provenance — the ONE route every hosted engine shares
|
|
23
|
+
* ({@link HOSTED_ROUTE_LABEL}), so all four engines select `external/default`
|
|
24
|
+
* and the model menu carries a single group instead of one per engine.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PROVIDER = "external";
|
|
27
|
+
/** Drives one session through turn and step boundaries on Pi. */
|
|
28
|
+
export declare class PiAgent implements Agent {
|
|
29
|
+
private loopCtx;
|
|
30
|
+
readonly id: SessionId;
|
|
31
|
+
readonly options: AgentOptions;
|
|
32
|
+
readonly session: Session;
|
|
33
|
+
private readonly config;
|
|
34
|
+
private readonly spawn;
|
|
35
|
+
private readonly bin;
|
|
36
|
+
readonly inbox: DriverInbox;
|
|
37
|
+
private phase;
|
|
38
|
+
private activityDone;
|
|
39
|
+
/** The agent-scoped registration boundary; the lifecycle owner unwinds it after the driver exits. */
|
|
40
|
+
readonly scope: Scope;
|
|
41
|
+
readonly ctx: Context;
|
|
42
|
+
/** Fused dispatcher, built once in the constructor so hot-path dispatches never allocate. */
|
|
43
|
+
private readonly dispatch;
|
|
44
|
+
/** Whether this loop instance has appended its initial/resume request anchor. */
|
|
45
|
+
private requestHeaderLogged;
|
|
46
|
+
/** Agent-lifecycle-local counter naming each streamed attempt. */
|
|
47
|
+
private streamAttempts;
|
|
48
|
+
/** This step's RPC child; released by the step teardown and the scope teardown. */
|
|
49
|
+
private rpc;
|
|
50
|
+
/**
|
|
51
|
+
* Resolves the read-only path behind each image block of the prompt. The
|
|
52
|
+
* attachment service is read lazily on every call, so a host that composes it
|
|
53
|
+
* after this agent still reaches the transcript.
|
|
54
|
+
*/
|
|
55
|
+
private readonly imageAccess;
|
|
56
|
+
/**
|
|
57
|
+
* Tool results logged into the currently open step. A result means the
|
|
58
|
+
* segment that requested the call is finished, so the next assistant content
|
|
59
|
+
* opens the next step (see {@link beginSegment}).
|
|
60
|
+
*/
|
|
61
|
+
private stepSettledTools;
|
|
62
|
+
/**
|
|
63
|
+
* The current step's calls, and which of them already carry a result.
|
|
64
|
+
*
|
|
65
|
+
* Pi announces a call from its streamed assistant message and reports the
|
|
66
|
+
* outcome from a separate execution event, so "which calls of THIS step are
|
|
67
|
+
* still open" is state the driver has to keep. Both halves of the Session V4
|
|
68
|
+
* rule read it: a call may not be given a second result in one step, and a
|
|
69
|
+
* step may not end while a call it announced has no result at all — the
|
|
70
|
+
* closing pass writes the missing outcome instead ({@link closeStep}).
|
|
71
|
+
*
|
|
72
|
+
* Reset per step (see {@link step}), never on an internal segment rotation:
|
|
73
|
+
* call ids are unique for the life of the session, so retaining the marks only
|
|
74
|
+
* widens duplicate suppression and can never hide a legitimate result.
|
|
75
|
+
*/
|
|
76
|
+
private readonly stepTools;
|
|
77
|
+
/**
|
|
78
|
+
* Rotate to the next step when the segment that ran a tool has finished, so
|
|
79
|
+
* each assistant segment lands in its own step.
|
|
80
|
+
*
|
|
81
|
+
* Called as new assistant content begins. A step holding a settled tool
|
|
82
|
+
* result means the previous segment is complete, and the content about to be
|
|
83
|
+
* written belongs to the next one. Rotating at a message boundary keeps calls
|
|
84
|
+
* announced together — one model turn — in one step.
|
|
85
|
+
*
|
|
86
|
+
* A step that still OWES a result does not rotate: Pi announces several calls
|
|
87
|
+
* in one turn and reports their outcomes one at a time, and rotating on the
|
|
88
|
+
* first one would strand the rest in a step that can never resolve them
|
|
89
|
+
* (`step/end leaves unresolved tool call …`). Those calls run one model turn
|
|
90
|
+
* later instead, which is what they belong to, and a call the engine never
|
|
91
|
+
* answers is closed by {@link closeStep} rather than by moving on.
|
|
92
|
+
* @param phase - the running phase carrying the open step's position.
|
|
93
|
+
*/
|
|
94
|
+
private beginSegment;
|
|
95
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig, spawn: PiSpawnCapability, bin: string);
|
|
96
|
+
/**
|
|
97
|
+
* Open this step's RPC child. The Pi RPC process is single-session, so a step
|
|
98
|
+
* never reuses one: the step teardown disposes it and the next step respawns
|
|
99
|
+
* a fresh child.
|
|
100
|
+
*/
|
|
101
|
+
private rpcClient;
|
|
102
|
+
get status(): AgentStatus;
|
|
103
|
+
/** Commit a phase and publish its externally visible status transition. */
|
|
104
|
+
private setPhase;
|
|
105
|
+
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
|
|
106
|
+
/**
|
|
107
|
+
* Queue a message for the next turn and wake the driver.
|
|
108
|
+
* @param input - the user message to deliver.
|
|
109
|
+
*/
|
|
110
|
+
followup(input: UserMessage): void;
|
|
111
|
+
/**
|
|
112
|
+
* Queue a message for the running step and wake the driver.
|
|
113
|
+
* @param input - the user message to deliver.
|
|
114
|
+
*/
|
|
115
|
+
steer(input: UserMessage): void;
|
|
116
|
+
/**
|
|
117
|
+
* Queue a message for the running step without waking the driver.
|
|
118
|
+
* @param input - the user message to deliver.
|
|
119
|
+
*/
|
|
120
|
+
inject(input: UserMessage): void;
|
|
121
|
+
cancel(cause: AgentCancelCause, options?: CancelOptions): void;
|
|
122
|
+
/**
|
|
123
|
+
* Run a maintenance job while the agent is idle.
|
|
124
|
+
* @param job - the maintenance operation, receiving the phase abort signal.
|
|
125
|
+
* @returns the maintenance result.
|
|
126
|
+
*/
|
|
127
|
+
runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
128
|
+
/**
|
|
129
|
+
* Start one driver, or latch its wake behind maintenance or an aborted
|
|
130
|
+
* activity. A wake sent while idle always opens its turn boundary, even
|
|
131
|
+
* when its message was cleared; only a latched replay is suppressed when
|
|
132
|
+
* the queue no longer holds the wake.
|
|
133
|
+
* @param wakeAfterAbort - the {@link send} classification, captured before
|
|
134
|
+
* the inbox insertion so a reentrant cancel cannot reclassify it.
|
|
135
|
+
*/
|
|
136
|
+
private wakeDriver;
|
|
137
|
+
whenIdle(): Promise<void>;
|
|
138
|
+
/** Report one failure at its live boundary, then preserve it for driver containment. */
|
|
139
|
+
private throwError;
|
|
140
|
+
private kick;
|
|
141
|
+
private preStep;
|
|
142
|
+
/**
|
|
143
|
+
* Scan the step's user messages for `/name` skill gestures, load each
|
|
144
|
+
* matching skill, and inject the rendered skill content into the message
|
|
145
|
+
* batch. This mirrors what dsh-tool-skill does for the in-process engine.
|
|
146
|
+
* @param messages - the current step's message batch.
|
|
147
|
+
* @param signal - cancellation signal (aborted loads are silently dropped).
|
|
148
|
+
* @returns the original batch when no skill was invoked, or an extended
|
|
149
|
+
* batch with injected skill-content messages appended.
|
|
150
|
+
*/
|
|
151
|
+
private injectSkills;
|
|
152
|
+
/**
|
|
153
|
+
* Resolve the runtime permission stance for one query. Deployment-pinned
|
|
154
|
+
* fields win; anything unpinned follows the session's durable dsh permission
|
|
155
|
+
* knobs, re-folded per query so mid-session preset switches take effect on the
|
|
156
|
+
* next step.
|
|
157
|
+
* @returns the permission fields of the query spec.
|
|
158
|
+
*/
|
|
159
|
+
private queryPermission;
|
|
160
|
+
/** Open one turn before claiming its first proposed step. */
|
|
161
|
+
private turn;
|
|
162
|
+
/** Model label recorded in the request header for one lifecycle. */
|
|
163
|
+
private modelLabel;
|
|
164
|
+
/** Append the request header snapshot once per loop instance. */
|
|
165
|
+
private assertRequestHeader;
|
|
166
|
+
/**
|
|
167
|
+
* Build the `pi --mode rpc` argv/cwd/env for one step's child process.
|
|
168
|
+
*
|
|
169
|
+
* The model comes from the session's own selection when it names a real dsh
|
|
170
|
+
* model, else from the deployment's pinned configuration
|
|
171
|
+
* ({@link sessionModelOverrideOf}). Pi's `--model` flag is
|
|
172
|
+
* `"provider/id"`-qualified, so a session pick travels as the composite —
|
|
173
|
+
* which also carries the provider, leaving the deployment's `--provider` out
|
|
174
|
+
* of the argv for that step. A step with neither a selection nor a pin sends
|
|
175
|
+
* no `--model` at all and lets Pi's own configuration decide. Read every step,
|
|
176
|
+
* so a model picked mid-conversation lands on the next child.
|
|
177
|
+
*
|
|
178
|
+
* When dsh also discloses that model's ENDPOINT, the child is pointed at it:
|
|
179
|
+
* `--provider` names the provider pi's `models.json` declares, `--api-key`
|
|
180
|
+
* carries the credential, and `PI_CODING_AGENT_DIR` redirects pi's agent
|
|
181
|
+
* directory to the plugin-owned one holding that `models.json`
|
|
182
|
+
* ({@link piAgentDir}) — so the child runs on dsh's endpoint instead of its
|
|
183
|
+
* own `~/.pi` configuration, which is never touched. The handover reaching here
|
|
184
|
+
* is already judged: {@link supportedHandover} dropped one that named no
|
|
185
|
+
* protocol and refused one pi cannot speak.
|
|
186
|
+
*/
|
|
187
|
+
private spawnSpec;
|
|
188
|
+
/**
|
|
189
|
+
* The endpoint handover this step may point the child at, or `undefined` to
|
|
190
|
+
* leave pi on its own configuration.
|
|
191
|
+
*
|
|
192
|
+
* Pi is the one engine that cannot run a provider handed over without a wire
|
|
193
|
+
* protocol: its `models.json` rejects a provider without `api` (verified
|
|
194
|
+
* against pi 0.84.3 — `{"status":"invalid","reason":"invalid_state"}`), and the
|
|
195
|
+
* plugin will not write a document it knows pi refuses. So a handover that
|
|
196
|
+
* names no protocol is dropped here rather than materialized into a broken
|
|
197
|
+
* agent directory. It is unreachable for every route `SHIPPED_ROUTE_APIS`
|
|
198
|
+
* names, which today is the only way `api` goes missing.
|
|
199
|
+
*
|
|
200
|
+
* A protocol pi does not SPEAK is a different answer, and a refusal rather than
|
|
201
|
+
* a drop: dropping it would leave the user's dsh model selection in place while
|
|
202
|
+
* pi quietly ran its own default model — the exact silent degradation this
|
|
203
|
+
* driver exists not to produce, and the shape of GitHub issue #2 (a third-party
|
|
204
|
+
* provider plugin declaring `api: "anthropic"`, which pi has no implementation
|
|
205
|
+
* for). The step fails instead, naming the provider, the model, and the
|
|
206
|
+
* offending protocol — so the user can pick `external/default` or fix it.
|
|
207
|
+
*
|
|
208
|
+
* The returned handover carries the PI spelling of its protocol
|
|
209
|
+
* ({@link resolvePiApi}), so everything downstream — `models.json` above all —
|
|
210
|
+
* sees the one protocol pi will actually be configured with.
|
|
211
|
+
* @param resolved - the endpoint dsh disclosed for this step's model selection.
|
|
212
|
+
* @returns the handover to point the child at, or `undefined` for none.
|
|
213
|
+
*/
|
|
214
|
+
private supportedHandover;
|
|
215
|
+
/**
|
|
216
|
+
* Run one Pi RPC query for the current step and map its event stream into the
|
|
217
|
+
* session log. The step opens a fresh Pi session (`new_session`) and sends the
|
|
218
|
+
* serialized session history as one prompt, then consumes events until the
|
|
219
|
+
* agent settles. Like the Codex/Claude drivers, Pi owns its own system prompt
|
|
220
|
+
* natively, so the dsh system-prompt assembly (which pulls dsh tool schemas
|
|
221
|
+
* and `agent.ctx.tools`) is deliberately not run — the durable session log is
|
|
222
|
+
* the sole source of model context.
|
|
223
|
+
* @param messages - the messages this step delivers; only their images ride
|
|
224
|
+
* with the prompt as bytes, and only they.
|
|
225
|
+
*/
|
|
226
|
+
private step;
|
|
227
|
+
/** Append one Pi tool result to the durable log as a `tool/result` message. */
|
|
228
|
+
private appendToolResult;
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi loop engine module: drives every session it is handed through the Pi CLI
|
|
3
|
+
* (`@earendil-works/pi-coding-agent`) over its JSONL RPC mode, one stateless
|
|
4
|
+
* session per dsh step, with the durable session log as the sole source of
|
|
5
|
+
* model context. The router routes a session here on the plugin's own engine
|
|
6
|
+
* record, else its agent preset; this module is a library, not a Cordis plugin entry. Pi has no permission system, so the entire `pi --mode rpc` child is
|
|
7
|
+
* spawned through the dsh subprocess seam — the only available privilege
|
|
8
|
+
* boundary — and its `--tools` are pruned to the resolved sandbox stance.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-loop-engine-allinone/engine-pi
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import z from '@deepseek-ai/schemastery';
|
|
14
|
+
import type { AgentOptions } from '@deepseek-ai/dsh-agent';
|
|
15
|
+
import type { Session, SessionId } from '@deepseek-ai/dsh-session';
|
|
16
|
+
import { PiAgent } from './agent.ts';
|
|
17
|
+
import type { PiProcess, PiSpawnSpec } from './rpc/client.ts';
|
|
18
|
+
import type { PiSandboxMode, ResolvedConfig } from './types.ts';
|
|
19
|
+
import { HostedEngineRuntime } from '../driver-core/hosted-engine-runtime.ts';
|
|
20
|
+
/** Pi CLI sandbox modes a deployment may pin. */
|
|
21
|
+
export declare const PI_SANDBOX_MODES: readonly PiSandboxMode[];
|
|
22
|
+
/** Grace in milliseconds for Pi process-tree termination. */
|
|
23
|
+
export declare const PI_DISPOSE_GRACE_MS = 3000;
|
|
24
|
+
/** Deployment-owned configuration for the Pi loop plugin. */
|
|
25
|
+
export interface Config {
|
|
26
|
+
/**
|
|
27
|
+
* Pinned sandbox stance for every RPC child. When omitted, each query follows
|
|
28
|
+
* the session's dsh permission knobs (`sandbox/mode` and `approval/policy`):
|
|
29
|
+
* full access runs native, `workspace-write` wraps the child in the dsh
|
|
30
|
+
* sandbox with a write-capable tool set, an `ask` policy degrades to a
|
|
31
|
+
* read-only denial, and anything else fails closed with `read-only`.
|
|
32
|
+
*/
|
|
33
|
+
sandboxMode?: PiSandboxMode;
|
|
34
|
+
/** LLM provider for the `pi` child (`--provider`), when the deployment pins one. */
|
|
35
|
+
provider?: string;
|
|
36
|
+
/** Fallback model for the `pi` child (`--model`), used when the session selects none; Pi native settings own the model when omitted. */
|
|
37
|
+
model?: string;
|
|
38
|
+
/** Thinking/reasoning level, appended to the `--model` pattern when pinned. */
|
|
39
|
+
thinkingLevel?: string;
|
|
40
|
+
/** Explicit environment entries passed to the `pi` child. */
|
|
41
|
+
env?: Record<string, string>;
|
|
42
|
+
/**
|
|
43
|
+
* An EXTERNAL Pi CLI entrypoint, replacing this package's bundled
|
|
44
|
+
* `@earendil-works/pi-coding-agent`. Omitted keeps the bundled copy, which is
|
|
45
|
+
* what every earlier release ran.
|
|
46
|
+
*/
|
|
47
|
+
bin?: string;
|
|
48
|
+
}
|
|
49
|
+
/** Schema of the Pi loop plugin configuration. */
|
|
50
|
+
export declare const Config: z<Config>;
|
|
51
|
+
/** Host-face ctx key this engine's runtime is registered under. */
|
|
52
|
+
export declare const PI_ENGINE_LABEL = "agentLoopPi";
|
|
53
|
+
declare module '@deepseek-ai/cordis' {
|
|
54
|
+
interface Context {
|
|
55
|
+
agentLoopPi: PiLoop;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creation/resume machinery for the Pi engine. The process-wide AgentFactory
|
|
60
|
+
* slot belongs to the router, which delegates each session to the engine its
|
|
61
|
+
* preset names; this class is that engine's driver, not a plugin.
|
|
62
|
+
*/
|
|
63
|
+
export declare class PiLoop extends HostedEngineRuntime<ResolvedConfig, PiAgent> {
|
|
64
|
+
/** Process-tree spawn capability handed to every agent, sandboxed by the subprocess seam. */
|
|
65
|
+
readonly spawn: (spec: PiSpawnSpec) => PiProcess;
|
|
66
|
+
/** Resolved Pi CLI entrypoint; `argv[0]` of every Pi RPC child. */
|
|
67
|
+
readonly bin: string;
|
|
68
|
+
constructor(ctx: Context, config: Config);
|
|
69
|
+
/** Construct the Pi RPC driver for one prepared session. */
|
|
70
|
+
protected buildAgent(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session): PiAgent;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi's dialect for a dsh endpoint: a self-built agent directory plus the argv
|
|
3
|
+
* flags that point the `pi --mode rpc` child at dsh's model.
|
|
4
|
+
*
|
|
5
|
+
* Pi resolves a model through its own agent directory — `models.json` under
|
|
6
|
+
* `PI_CODING_AGENT_DIR` (or `~/.pi` when unset) — and its custom-provider schema
|
|
7
|
+
* has no `apiKeyEnv`: a provider's credential is either a literal `apiKey` in
|
|
8
|
+
* that file or the CLI's own `--api-key`. There is no `PI_*` variable for a base
|
|
9
|
+
* URL either. So the only automatic way to hand pi a dsh endpoint is to give it
|
|
10
|
+
* an agent directory THIS PLUGIN owns, containing a `models.json` that declares
|
|
11
|
+
* the dsh provider, and to point `PI_CODING_AGENT_DIR` at it.
|
|
12
|
+
*
|
|
13
|
+
* The user's `~/.pi` is deliberately never written or read: the plugin owns a
|
|
14
|
+
* separate directory under the OS temp dir, one per distinct endpoint, created
|
|
15
|
+
* 0700 with a 0600 file because it carries the credential. The cost is real and
|
|
16
|
+
* documented: redirecting the agent directory means the child no longer sees the
|
|
17
|
+
* user's own `~/.pi` skills/auth/theme. That is the trade the deployment makes
|
|
18
|
+
* by selecting a real dsh model on pi.
|
|
19
|
+
*
|
|
20
|
+
* pi's provider schema requires a wire protocol (`api`) AND requires that pi
|
|
21
|
+
* itself has an implementation for the one it is given, so this is the one
|
|
22
|
+
* engine whose dialect has to judge the protocol rather than pass it along:
|
|
23
|
+
* {@link resolvePiApi} answers "is this a protocol pi can speak", and the driver
|
|
24
|
+
* drops a protocol-less handover or refuses an unspeakable one before either
|
|
25
|
+
* reaches here. What does reach here is written in pi's own spelling
|
|
26
|
+
* ({@link PI_API_ALIASES}) instead of dsh's raw string.
|
|
27
|
+
*
|
|
28
|
+
* @module dsh-loop-engine-allinone/engine-pi/model-handover
|
|
29
|
+
*/
|
|
30
|
+
import type { DshModelHandover } from '../driver-core/model-handover.ts';
|
|
31
|
+
/**
|
|
32
|
+
* What pi can be told about one handover's wire protocol.
|
|
33
|
+
*
|
|
34
|
+
* A discriminated answer rather than an `api: string | undefined`, because the
|
|
35
|
+
* driver treats the two negative cases differently: no protocol at all is
|
|
36
|
+
* dropped, while a protocol pi does not speak is REFUSED — dropping the second
|
|
37
|
+
* one would leave the user's dsh model selection standing while pi quietly ran
|
|
38
|
+
* its OWN default model, the misleading failure this driver exists not to
|
|
39
|
+
* produce (the driver's gate is `PiAgent.supportedHandover`, `agent.ts`).
|
|
40
|
+
*/
|
|
41
|
+
export type PiApiResolution = {
|
|
42
|
+
readonly kind: 'absent';
|
|
43
|
+
} | {
|
|
44
|
+
readonly kind: 'supported';
|
|
45
|
+
readonly api: string;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: 'unsupported';
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Judge one handover's wire protocol against pi's own vocabulary.
|
|
51
|
+
* @param api - dsh's protocol name for the endpoint, or `undefined` when neither
|
|
52
|
+
* the provider's settings section nor a shipped-route entry declares one.
|
|
53
|
+
* @returns `absent` for no protocol, `supported` (carrying the pi spelling to
|
|
54
|
+
* use) for one pi speaks, `unsupported` for anything else.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolvePiApi(api: string | undefined): PiApiResolution;
|
|
57
|
+
/**
|
|
58
|
+
* A handover whose wire protocol pi can speak — i.e. one that came through
|
|
59
|
+
* {@link resolvePiApi}.
|
|
60
|
+
*
|
|
61
|
+
* The type is the point: {@link piAgentDir} materializes an endpoint pi will be
|
|
62
|
+
* pointed at, and pi rejects a provider whose `api` is missing or unknown, so a
|
|
63
|
+
* protocol-less handover has no business reaching this module's write path.
|
|
64
|
+
* Narrowing `api` to a required string makes that a compile-time obligation at
|
|
65
|
+
* every call site instead of a runtime check the writer would have to fail on.
|
|
66
|
+
*/
|
|
67
|
+
export interface PiSupportedHandover extends DshModelHandover {
|
|
68
|
+
/** The endpoint's wire protocol, in pi's own vocabulary. */
|
|
69
|
+
readonly api: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The agent directory for one handover, materialized on first use.
|
|
73
|
+
*
|
|
74
|
+
* The directory name is a hash of the endpoint facts, so two sessions on the
|
|
75
|
+
* same provider/model share one directory and a changed endpoint gets its own;
|
|
76
|
+
* `models.json` declares exactly the provider pi is told to use (`--provider`)
|
|
77
|
+
* and the one model it is told to run (`--model`), keyed the way pi's own
|
|
78
|
+
* schema reads it (`baseUrl`/`api`/`apiKey`/`models`).
|
|
79
|
+
* @param handover - the resolved dsh endpoint for the session's selection, whose
|
|
80
|
+
* protocol {@link resolvePiApi} already accepted.
|
|
81
|
+
* @returns the absolute path to hand the child as `PI_CODING_AGENT_DIR`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function piAgentDir(handover: PiSupportedHandover): string;
|
|
84
|
+
//# sourceMappingURL=model-handover.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping from the dsh session's durable permission knobs to one Pi RPC
|
|
3
|
+
* process's runtime stance. Pi carries no native permission system — "runs
|
|
4
|
+
* with the permissions of the user" — so the driver cannot ask it to sandbox or
|
|
5
|
+
* approve. What is left is the `--tools` allowlist: the child runs under the
|
|
6
|
+
* dsh user either way (the subprocess seam carries no confinement of its own),
|
|
7
|
+
* so withholding a tool IS the stance. The fold mirrors the codex bridge,
|
|
8
|
+
* mapping the session's `sandbox/mode` and `approval/policy` events directly:
|
|
9
|
+
* - full access → `danger-full-access`, no pruning (Pi's native tools);
|
|
10
|
+
* - `workspace-write` → a write-capable set, still without a shell;
|
|
11
|
+
* - an `ask` policy → degraded to a read-only denial (Pi has no request
|
|
12
|
+
* callback, so interactive approval can only become a rejection);
|
|
13
|
+
* - anything else fails closed → `read-only`.
|
|
14
|
+
*
|
|
15
|
+
* @module dsh-loop-engine-allinone/engine-pi/permission
|
|
16
|
+
*/
|
|
17
|
+
import type { PermissionEvent } from '../driver-core/permission-knobs.ts';
|
|
18
|
+
import type { PiSandboxMode } from './types.ts';
|
|
19
|
+
/** The runtime stance one Pi RPC process should run under. */
|
|
20
|
+
export interface PiPermission {
|
|
21
|
+
/** The resolved sandbox stance; selects the tool set, not a process sandbox. */
|
|
22
|
+
readonly sandboxMode: PiSandboxMode;
|
|
23
|
+
/** The `--tools` allowlist — the stance's only enforcement; empty means no pruning. */
|
|
24
|
+
readonly tools: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Conservative unattended default: read-only sandbox, no write/exec tools. The
|
|
28
|
+
* allowlist names pi's built-ins (`read`, `bash`, `edit`, `write`) and nothing
|
|
29
|
+
* else — a name pi does not know matches no tool at all, silently, so a list
|
|
30
|
+
* carrying invented entries would enforce less than it claims to.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEFAULT_PI_PERMISSION: PiPermission;
|
|
33
|
+
/**
|
|
34
|
+
* Derive the `--tools` allowlist for a given sandbox stance. Full access prunes
|
|
35
|
+
* nothing; `workspace-write` allows a write-capable set; `read-only` allows
|
|
36
|
+
* reading and nothing else. Only pi's own built-in tool names appear here.
|
|
37
|
+
* @param mode - the resolved sandbox stance.
|
|
38
|
+
* @returns the tool set to pass as `--tools`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function toolsForSandbox(mode: PiSandboxMode): readonly string[];
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the session's effective Pi runtime stance.
|
|
43
|
+
* @param events - the durable session log.
|
|
44
|
+
* @returns the stance one Pi RPC process should run under.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveSessionPermission(events: readonly PermissionEvent[]): PiPermission;
|
|
47
|
+
//# sourceMappingURL=permission.d.ts.map
|