@xmoon76/dsh-pi-tui 0.1.8 → 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/config/agent-presets/code/agent.cordis.yml +6 -5
- package/config/agent-presets/cordis/agent.cordis.yml +6 -5
- package/config/agent-presets/cordis/skills/editing-cordis-compositions/SKILL.md +3 -3
- package/config/agent-presets/minimal/agent.cordis.yml +27 -1
- package/config/agent-presets/standard/agent.cordis.yml +6 -5
- package/cordis.patch.yml +14 -1
- package/dist/builtins.d.mts +18 -0
- package/dist/builtins.mjs +75 -0
- package/dist/extension/advanced.d.mts +372 -0
- package/dist/extension/advanced.mjs +46 -0
- package/dist/extension/unstable.d.mts +206 -0
- package/dist/extension/unstable.mjs +30 -0
- package/dist/extensions.d.mts +132 -0
- package/dist/extensions.mjs +3 -0
- package/dist/index.d.mts +63 -3
- package/dist/index.mjs +3 -22739
- package/dist/public-types-2omeXG8d.d.mts +740 -0
- package/dist/src-AgdnRnGA.mjs +30851 -0
- package/dist/theme-registry-CjbtRmhd.d.mts +1 -0
- package/package.json +81 -55
- package/scripts/advanced-plugin-smoke.mjs +314 -0
- package/scripts/bench.mts +40 -5
- package/scripts/examples-plugin-smoke.mjs +320 -0
- package/scripts/extension-fixture-smoke.mjs +219 -0
- package/scripts/phase4-plugin-smoke.mjs +318 -0
- package/scripts/tarball-smoke.mjs +69 -7
- package/scripts/unstable-plugin-smoke.mjs +300 -0
- package/scripts/vim-plugin-smoke.mjs +315 -0
|
@@ -198,16 +198,17 @@
|
|
|
198
198
|
toolName: subagent_fork
|
|
199
199
|
backgroundMode: continuable
|
|
200
200
|
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
201
|
+
# Production dsh does not install these optional providers. Install the
|
|
202
|
+
# matching Bundle in this Profile and restart the Host, then copy this
|
|
203
|
+
# preset and remove `disabled` from the matching tool row. Host availability
|
|
204
|
+
# alone grants no tool.
|
|
204
205
|
- id: tool-subagent-codex
|
|
205
206
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
206
207
|
disabled: true
|
|
207
208
|
config:
|
|
208
209
|
provider: codex
|
|
209
210
|
toolName: subagent_codex
|
|
210
|
-
|
|
211
|
+
backgroundMode: one-shot
|
|
211
212
|
maxDepth: provider-managed
|
|
212
213
|
|
|
213
214
|
- id: tool-subagent-claude-code
|
|
@@ -216,7 +217,7 @@
|
|
|
216
217
|
config:
|
|
217
218
|
provider: claude-code
|
|
218
219
|
toolName: subagent_claude_code
|
|
219
|
-
|
|
220
|
+
backgroundMode: one-shot
|
|
220
221
|
maxDepth: provider-managed
|
|
221
222
|
|
|
222
223
|
- id: workflow-worker-thread
|
|
@@ -185,16 +185,17 @@
|
|
|
185
185
|
toolName: subagent_fork
|
|
186
186
|
backgroundMode: continuable
|
|
187
187
|
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
188
|
+
# Production dsh does not install these optional providers. Install the
|
|
189
|
+
# matching Bundle in this Profile and restart the Host, then copy this
|
|
190
|
+
# preset and remove `disabled` from the matching tool row. Host availability
|
|
191
|
+
# alone grants no tool.
|
|
191
192
|
- id: tool-subagent-codex
|
|
192
193
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
193
194
|
disabled: true
|
|
194
195
|
config:
|
|
195
196
|
provider: codex
|
|
196
197
|
toolName: subagent_codex
|
|
197
|
-
|
|
198
|
+
backgroundMode: one-shot
|
|
198
199
|
maxDepth: provider-managed
|
|
199
200
|
|
|
200
201
|
- id: tool-subagent-claude-code
|
|
@@ -203,7 +204,7 @@
|
|
|
203
204
|
config:
|
|
204
205
|
provider: claude-code
|
|
205
206
|
toolName: subagent_claude_code
|
|
206
|
-
|
|
207
|
+
backgroundMode: one-shot
|
|
207
208
|
maxDepth: provider-managed
|
|
208
209
|
|
|
209
210
|
- id: workflow-worker-thread
|
|
@@ -134,7 +134,7 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
|
|
|
134
134
|
config:
|
|
135
135
|
provider: codex
|
|
136
136
|
toolName: subagent_codex
|
|
137
|
-
|
|
137
|
+
backgroundMode: one-shot
|
|
138
138
|
maxDepth: provider-managed
|
|
139
139
|
|
|
140
140
|
- id: tool-subagent-claude-code
|
|
@@ -143,11 +143,11 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
|
|
|
143
143
|
config:
|
|
144
144
|
provider: claude-code
|
|
145
145
|
toolName: subagent_claude_code
|
|
146
|
-
|
|
146
|
+
backgroundMode: one-shot
|
|
147
147
|
maxDepth: provider-managed
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both.
|
|
150
|
+
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both. Production `dsh` does not install either optional provider: before enabling a row, install the matching `@deepseek-ai/dsh-subagent-codex` or `@deepseek-ai/dsh-subagent-claude-code` Bundle in the Profile and restart it. Each Bundle registers its dormant default provider and exclusively uses its pinned package-local platform CLI; additional named instances use extra host-plane rows from the same installed package. A preset cannot provide that host dependency. `backgroundMode: one-shot` keeps omitted or `false` calls in the foreground and lets explicit `run_in_background: true` return a generic Job id. Full presets already carry `tool-jobs`, while the base host carries the job registry; retain both so `job_output`, `job_list`, `job_kill`, cancellation, and completion notices stay available. Installing a Bundle or composing a preset row does not start a product, authenticate an account, select a model, probe credentials, or manage native product settings.
|
|
151
151
|
|
|
152
152
|
## What not to move into a preset
|
|
153
153
|
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# The persona is the complete system prompt, so global identity, Web orientation,
|
|
4
4
|
# tool guidance, and later assembly listeners cannot add prompt text. Runtime
|
|
5
5
|
# context snapshots are suppressed for this preset, and the model composes only
|
|
6
|
-
# persistent `bash`
|
|
6
|
+
# the persistent shell (`bash` on POSIX, `pwsh` on win32) and
|
|
7
|
+
# `str_replace_editor`. Context compaction is absent.
|
|
7
8
|
|
|
8
9
|
- id: persona
|
|
9
10
|
name: '@deepseek-ai/dsh-persona'
|
|
@@ -15,6 +16,8 @@
|
|
|
15
16
|
# The PTY registry is an agent-owned service, so it lives in an entry-local
|
|
16
17
|
# realm. The backend still consumes the host sandbox policy and subprocess
|
|
17
18
|
# implementation, while the tool registers into this agent's scoped catalog.
|
|
19
|
+
# Exactly one shell stack mounts per host: the bash stack gates off win32 and
|
|
20
|
+
# its pwsh twin gates off POSIX, mirroring the one-shot shell rows.
|
|
18
21
|
- id: persistent-shell
|
|
19
22
|
name: cordis:group
|
|
20
23
|
group: true
|
|
@@ -26,11 +29,13 @@
|
|
|
26
29
|
|
|
27
30
|
- id: terminal-bash
|
|
28
31
|
name: '@deepseek-ai/dsh-terminal-bash'
|
|
32
|
+
disabled: !!js process.platform === 'win32'
|
|
29
33
|
config:
|
|
30
34
|
timeoutMs: 300000
|
|
31
35
|
|
|
32
36
|
- id: persistent-bash
|
|
33
37
|
name: '@deepseek-ai/dsh-tool-bash-persistent'
|
|
38
|
+
disabled: !!js process.platform === 'win32'
|
|
34
39
|
config:
|
|
35
40
|
timeoutMs: 300000
|
|
36
41
|
description: |-
|
|
@@ -43,6 +48,27 @@
|
|
|
43
48
|
* Please avoid commands that may produce a very large amount of output.
|
|
44
49
|
* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.
|
|
45
50
|
|
|
51
|
+
- id: terminal-pwsh
|
|
52
|
+
name: '@deepseek-ai/dsh-terminal-bash'
|
|
53
|
+
disabled: !!js process.platform !== 'win32'
|
|
54
|
+
config:
|
|
55
|
+
shellDialect: pwsh
|
|
56
|
+
timeoutMs: 300000
|
|
57
|
+
|
|
58
|
+
- id: persistent-pwsh
|
|
59
|
+
name: '@deepseek-ai/dsh-tool-pwsh-persistent'
|
|
60
|
+
disabled: !!js process.platform !== 'win32'
|
|
61
|
+
config:
|
|
62
|
+
timeoutMs: 300000
|
|
63
|
+
description: |-
|
|
64
|
+
Run commands in a PowerShell shell
|
|
65
|
+
* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
|
|
66
|
+
* You don't have access to the internet via this tool.
|
|
67
|
+
* State is persistent across command calls and discussions with the user.
|
|
68
|
+
* Use native Windows paths (C:\...) and $env:NAME variables; this is PowerShell, not bash.
|
|
69
|
+
* Please avoid commands that may produce a very large amount of output.
|
|
70
|
+
* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.
|
|
71
|
+
|
|
46
72
|
# The bare local filesystem shadows the host's sandboxed provider only for this
|
|
47
73
|
# preset. The editor shares that realm and requires absolute paths.
|
|
48
74
|
- id: filesystem
|
|
@@ -197,16 +197,17 @@
|
|
|
197
197
|
toolName: subagent_fork
|
|
198
198
|
backgroundMode: continuable
|
|
199
199
|
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
#
|
|
200
|
+
# Production dsh does not install these optional providers. Install the
|
|
201
|
+
# matching Bundle in this Profile and restart the Host, then copy this
|
|
202
|
+
# preset and remove `disabled` from the matching tool row. Host availability
|
|
203
|
+
# alone grants no tool.
|
|
203
204
|
- id: tool-subagent-codex
|
|
204
205
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
205
206
|
disabled: true
|
|
206
207
|
config:
|
|
207
208
|
provider: codex
|
|
208
209
|
toolName: subagent_codex
|
|
209
|
-
|
|
210
|
+
backgroundMode: one-shot
|
|
210
211
|
maxDepth: provider-managed
|
|
211
212
|
|
|
212
213
|
- id: tool-subagent-claude-code
|
|
@@ -215,7 +216,7 @@
|
|
|
215
216
|
config:
|
|
216
217
|
provider: claude-code
|
|
217
218
|
toolName: subagent_claude_code
|
|
218
|
-
|
|
219
|
+
backgroundMode: one-shot
|
|
219
220
|
maxDepth: provider-managed
|
|
220
221
|
|
|
221
222
|
- id: workflow-worker-thread
|
package/cordis.patch.yml
CHANGED
|
@@ -11,9 +11,22 @@
|
|
|
11
11
|
- id: tui-startup
|
|
12
12
|
name: '@xmoon76/dsh-pi-tui/startup'
|
|
13
13
|
|
|
14
|
+
# The extension platform (M3/E): the host provides the piTuiExtensions
|
|
15
|
+
# service (third-party plugins inject it), the builtins row registers
|
|
16
|
+
# the first-party chrome contributions through the SAME public API, and
|
|
17
|
+
# the runner row attaches the surface host over the service's ledger.
|
|
18
|
+
# All three gate on tuiStartup, so `--help` mounts none of them.
|
|
19
|
+
- id: pi-tui-extension-host
|
|
20
|
+
name: '@xmoon76/dsh-pi-tui/extensions'
|
|
21
|
+
inject: [tuiStartup]
|
|
22
|
+
|
|
23
|
+
- id: pi-tui-builtins
|
|
24
|
+
name: '@xmoon76/dsh-pi-tui/builtins'
|
|
25
|
+
inject: [tuiStartup, piTuiExtensions]
|
|
26
|
+
|
|
14
27
|
- id: tui-app
|
|
15
28
|
name: '@xmoon76/dsh-pi-tui'
|
|
16
|
-
inject: [tuiStartup]
|
|
29
|
+
inject: [tuiStartup, piTuiExtensions]
|
|
17
30
|
config:
|
|
18
31
|
sessionId: ''
|
|
19
32
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
|
|
3
|
+
//#region src/builtins.d.ts
|
|
4
|
+
/** Stable Cordis plugin name for the builtins row. */
|
|
5
|
+
declare const name = "pi-tui-builtins";
|
|
6
|
+
/** The builtins mount only when the TUI startup flags were parsed. */
|
|
7
|
+
declare const inject: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Register the first-party chrome contributions. The turn/step footer
|
|
10
|
+
* segment subscribes to the surface state and re-bakes via handle.replace
|
|
11
|
+
* on every state change (the async-producer → cache → invalidate pattern
|
|
12
|
+
* from the plan §8.4, with replace() as the cache commit).
|
|
13
|
+
* @param ctx - the builtins plugin context (the extension service resolves
|
|
14
|
+
* through it; the registration is owned by THIS fiber).
|
|
15
|
+
*/
|
|
16
|
+
declare function apply(ctx: Context): void;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { TUI_STARTUP_SERVICE } from "./startup.mjs";
|
|
2
|
+
import { M as PI_TUI_EXTENSIONS_SERVICE } from "./src-AgdnRnGA.mjs";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region src/builtins.ts
|
|
6
|
+
/** Stable Cordis plugin name for the builtins row. */
|
|
7
|
+
const name = "pi-tui-builtins";
|
|
8
|
+
/** The builtins mount only when the TUI startup flags were parsed. */
|
|
9
|
+
const inject = [TUI_STARTUP_SERVICE, PI_TUI_EXTENSIONS_SERVICE];
|
|
10
|
+
/** The `@xmoon76/dsh-pi-tui` package version (dist/extensions.mjs → ../package.json). */
|
|
11
|
+
function packageVersion() {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(join(import.meta.dirname, "..", "package.json"), "utf8")).version ?? "0.0.0";
|
|
14
|
+
} catch {
|
|
15
|
+
return "0.0.0";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Register the first-party chrome contributions. The turn/step footer
|
|
20
|
+
* segment subscribes to the surface state and re-bakes via handle.replace
|
|
21
|
+
* on every state change (the async-producer → cache → invalidate pattern
|
|
22
|
+
* from the plan §8.4, with replace() as the cache commit).
|
|
23
|
+
* @param ctx - the builtins plugin context (the extension service resolves
|
|
24
|
+
* through it; the registration is owned by THIS fiber).
|
|
25
|
+
*/
|
|
26
|
+
function apply(ctx) {
|
|
27
|
+
if (ctx.get("tuiStartup") === void 0) return;
|
|
28
|
+
const service = ctx.get(PI_TUI_EXTENSIONS_SERVICE);
|
|
29
|
+
if (service === void 0) return;
|
|
30
|
+
service.register("chrome.header.badge", {
|
|
31
|
+
id: "builtin-version",
|
|
32
|
+
order: 1e3,
|
|
33
|
+
description: "The bundle version (first-party builtin)."
|
|
34
|
+
}, {
|
|
35
|
+
text: `v${packageVersion()}`,
|
|
36
|
+
tone: "info"
|
|
37
|
+
});
|
|
38
|
+
const counters = service.register("chrome.footer.status", {
|
|
39
|
+
id: "builtin-turns-steps",
|
|
40
|
+
order: 1e3,
|
|
41
|
+
description: "Completed turns/steps counters (first-party builtin)."
|
|
42
|
+
}, { spans: [] });
|
|
43
|
+
const renderCounters = (state) => {
|
|
44
|
+
const spans = [{ text: `t${state.session.turns}/s${state.session.steps}` }];
|
|
45
|
+
counters.replace({ spans });
|
|
46
|
+
};
|
|
47
|
+
service.subscribeState((state) => {
|
|
48
|
+
renderCounters(state);
|
|
49
|
+
});
|
|
50
|
+
const todoDock = service.register("input.dock.item", {
|
|
51
|
+
id: "builtin-todo-summary",
|
|
52
|
+
order: 1e3,
|
|
53
|
+
description: "The todo summary line (first-party builtin)."
|
|
54
|
+
}, { label: [] });
|
|
55
|
+
const renderTodoDock = (state) => {
|
|
56
|
+
const summary = state.activity.todoSummary;
|
|
57
|
+
if (summary === void 0 || summary === "") {
|
|
58
|
+
todoDock.replace({ label: [] });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const label = [{
|
|
62
|
+
text: "☑ ",
|
|
63
|
+
tone: "textDim"
|
|
64
|
+
}, {
|
|
65
|
+
text: summary,
|
|
66
|
+
tone: "textDim"
|
|
67
|
+
}];
|
|
68
|
+
todoDock.replace({ label });
|
|
69
|
+
};
|
|
70
|
+
service.subscribeState((state) => {
|
|
71
|
+
renderTodoDock(state);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { E as PiTuiApiInfo, T as NormalizedKey, _ as ExtensionTier, at as TuiOverlayOptions, h as EditorSnapshot, v as ExtensionView } from "../public-types-2omeXG8d.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/extension/advanced-types.d.ts
|
|
4
|
+
/** API level of the advanced tier. Bumped only on advanced breaking changes. */
|
|
5
|
+
declare const ADVANCED_API_LEVEL: 1;
|
|
6
|
+
/**
|
|
7
|
+
* A normalized input event — the ONLY input shape an advanced plugin ever
|
|
8
|
+
* sees. The Host has already decoded the terminal protocol (legacy + Kitty
|
|
9
|
+
* CSI-u + modifyOtherKeys encodings, bracketed paste, key release/repeat
|
|
10
|
+
* filtering), so an advanced plugin behaves identically on every terminal.
|
|
11
|
+
*/
|
|
12
|
+
type AdvancedInputEvent = /** One key press, normalized to the semantic identity. */{
|
|
13
|
+
readonly kind: 'key';
|
|
14
|
+
readonly key: NormalizedKey;
|
|
15
|
+
} /** One plain printable character run (ordinary typing). */ | {
|
|
16
|
+
readonly kind: 'text';
|
|
17
|
+
readonly text: string;
|
|
18
|
+
} /** A paste burst (bracketed-paste or the host's paste heuristic). */ | {
|
|
19
|
+
readonly kind: 'paste';
|
|
20
|
+
readonly text: string;
|
|
21
|
+
};
|
|
22
|
+
/** The capture modes of a normalized input capture (plan §5). */
|
|
23
|
+
type AdvancedInputCaptureMode = 'observe' | 'capture' | 'exclusive';
|
|
24
|
+
/**
|
|
25
|
+
* One normalized input capture registration (plan §5). The capture is
|
|
26
|
+
* consulted by the Host AFTER its own capturing flows (questions, approvals,
|
|
27
|
+
* overlays) and reserved lifecycle keys, and BEFORE the editor and the
|
|
28
|
+
* Stable keybindings — so an advanced plugin can preempt ordinary editor and
|
|
29
|
+
* panel input, but never a Host question/approval, a Host overlay, or a
|
|
30
|
+
* Host fatal-recovery shortcut (session safety stays Host-owned).
|
|
31
|
+
*
|
|
32
|
+
* Modes:
|
|
33
|
+
* - `observe` — never consumes; the handler is a pure observer.
|
|
34
|
+
* - `capture` — may consume the event (return true).
|
|
35
|
+
* - `exclusive` — the SOLE capture consumer while live: capture-mode
|
|
36
|
+
* captures are not consulted (observers still run). A second exclusive
|
|
37
|
+
* registration is an explicit error, never a load-order winner.
|
|
38
|
+
*
|
|
39
|
+
* Ordering is deterministic: `priority` ASC, then `id` ASC. A throwing
|
|
40
|
+
* handler is isolated (health ledger) and FAILS OPEN — the event continues
|
|
41
|
+
* down the Host ladder, so a broken capture can never stall the TUI.
|
|
42
|
+
*/
|
|
43
|
+
interface AdvancedInputCaptureSpec {
|
|
44
|
+
/** Stable diagnostic identity, unique per owner. */
|
|
45
|
+
readonly id: string;
|
|
46
|
+
/** Deterministic ordering: priority ASC, then id ASC. */
|
|
47
|
+
readonly priority?: number;
|
|
48
|
+
/** observe | capture | exclusive (default: capture). */
|
|
49
|
+
readonly mode?: AdvancedInputCaptureMode;
|
|
50
|
+
/** Optional gate: the capture is consulted only while this returns true.
|
|
51
|
+
* A throwing gate is treated as false (the capture is skipped). */
|
|
52
|
+
readonly when?: () => boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The event handler. Return true to CONSUME the event (the Host does
|
|
55
|
+
* nothing further with it); return false/undefined to pass it on.
|
|
56
|
+
* A throwing handler is isolated and fails open.
|
|
57
|
+
*/
|
|
58
|
+
handle(event: AdvancedInputEvent): boolean | void;
|
|
59
|
+
}
|
|
60
|
+
/** A live handle on one normalized input capture. */
|
|
61
|
+
interface AdvancedInputCaptureHandle {
|
|
62
|
+
readonly id: string;
|
|
63
|
+
/** Remove the capture. Idempotent; owner unload also disposes it. */
|
|
64
|
+
dispose(): void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The render context handed to an interactive component's render() (plan
|
|
68
|
+
* §6). The plugin owns its state; the Host compiles the returned
|
|
69
|
+
* ExtensionView and owns layout, ANSI, width/wrapping and error isolation.
|
|
70
|
+
*/
|
|
71
|
+
interface AdvancedRenderContext {
|
|
72
|
+
readonly surfaceId: string;
|
|
73
|
+
readonly generation: number;
|
|
74
|
+
/** The current terminal width in cells (re-read at every compile). */
|
|
75
|
+
readonly width: number;
|
|
76
|
+
/** The current terminal height in rows. */
|
|
77
|
+
readonly height: number;
|
|
78
|
+
/** Whether this component currently owns keyboard focus. */
|
|
79
|
+
readonly focused: boolean;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A focused interactive component (plan §6): plugin-owned interactive
|
|
83
|
+
* state rendered through the Host's component kit. The Host compiles
|
|
84
|
+
* render() output, normalizes input, leases focus and isolates every
|
|
85
|
+
* callback — the plugin never touches a private component, screen or
|
|
86
|
+
* terminal.
|
|
87
|
+
*/
|
|
88
|
+
interface AdvancedInteractiveComponent {
|
|
89
|
+
/** Render the current state as a structured view (Host-compiled). */
|
|
90
|
+
render(ctx: AdvancedRenderContext): ExtensionView;
|
|
91
|
+
/**
|
|
92
|
+
* Receive a normalized input event while this component owns focus.
|
|
93
|
+
* Return true to consume; false/undefined passes the event on (the Host
|
|
94
|
+
* drops it — the focused component owns the seat). A throwing handler
|
|
95
|
+
* is isolated.
|
|
96
|
+
*/
|
|
97
|
+
handleInput?(event: AdvancedInputEvent): boolean | void;
|
|
98
|
+
/** The component gained keyboard focus. */
|
|
99
|
+
onFocus?(): void;
|
|
100
|
+
/** The component lost keyboard focus. */
|
|
101
|
+
onBlur?(): void;
|
|
102
|
+
/** The component was disposed (overlay closed / surface died). */
|
|
103
|
+
dispose?(): void;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A Host-owned focus lease (plan §7). In Phase 2 the lease is delivered
|
|
107
|
+
* through the interactive overlay lease (the overlay IS the focused
|
|
108
|
+
* interactive surface); a stale lease (surface disposed, owner unloaded)
|
|
109
|
+
* is inert.
|
|
110
|
+
*/
|
|
111
|
+
interface AdvancedFocusHandle {
|
|
112
|
+
/** Whether this lease currently holds focus. */
|
|
113
|
+
readonly active: boolean;
|
|
114
|
+
/** Request focus for the leased surface. */
|
|
115
|
+
focus(): void;
|
|
116
|
+
/** Release focus. */
|
|
117
|
+
blur(): void;
|
|
118
|
+
/** Release the lease (idempotent; owner unload also releases it). */
|
|
119
|
+
dispose(): void;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* A live lease on one interactive managed overlay (plan §8). The plugin
|
|
123
|
+
* owns the component's state; the Host owns the physical screen mount,
|
|
124
|
+
* the overlay stack, focus, fullscreen migration and final teardown. The
|
|
125
|
+
* lease is caller-fiber-owned: owner unload closes the overlay, and a
|
|
126
|
+
* stale lease (surface disposed) is inert.
|
|
127
|
+
*/
|
|
128
|
+
interface AdvancedOverlayLease {
|
|
129
|
+
/** The lease identity (stable diagnostic id). */
|
|
130
|
+
readonly id: string;
|
|
131
|
+
/** Whether the overlay is still mounted (false after close/dispose). */
|
|
132
|
+
readonly active: boolean;
|
|
133
|
+
/** Whether the overlay currently owns keyboard focus. */
|
|
134
|
+
readonly focused: boolean;
|
|
135
|
+
/** Request focus for the overlay (also shows it if hidden). */
|
|
136
|
+
focus(): void;
|
|
137
|
+
/** Release focus. */
|
|
138
|
+
blur(): void;
|
|
139
|
+
/** Recompile the component's render() output and repaint. */
|
|
140
|
+
invalidate(): void;
|
|
141
|
+
/** Close the overlay (idempotent; the surface's dispose also closes it). */
|
|
142
|
+
close(): void;
|
|
143
|
+
/** Temporarily hide (the lease stays live). */
|
|
144
|
+
hide(): void;
|
|
145
|
+
/** Show again after hide(). */
|
|
146
|
+
show(): void;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The advanced editor controls (plan §9): direct semantic editor actions
|
|
150
|
+
* through the Host's editor seat. The Host owns submission/session safety
|
|
151
|
+
* (dispatch('submit') stays the explicit action path); these controls only
|
|
152
|
+
* carry text/cursor/focus. A stale controls object (surface disposed) is
|
|
153
|
+
* inert.
|
|
154
|
+
*/
|
|
155
|
+
interface AdvancedEditorControls {
|
|
156
|
+
/** The current immutable editor snapshot (text/cursor/focus). */
|
|
157
|
+
getEditorState(): EditorSnapshot;
|
|
158
|
+
/** Replace the draft wholesale. */
|
|
159
|
+
setEditorText(text: string): void;
|
|
160
|
+
/** Move the cursor to a flat offset within the draft. */
|
|
161
|
+
setEditorCursor(offset: number): void;
|
|
162
|
+
/** Insert text at the cursor (or at an explicit offset). */
|
|
163
|
+
insertEditorText(text: string, at?: number): void;
|
|
164
|
+
/** Paste text at the cursor (same insertion semantics as
|
|
165
|
+
* insertEditorText at the cursor — the Host owns paste protection). */
|
|
166
|
+
pasteToEditor(text: string): void;
|
|
167
|
+
/** Request keyboard focus for the editor seat. */
|
|
168
|
+
requestEditorFocus(): void;
|
|
169
|
+
}
|
|
170
|
+
/** The advanced input facade (plan §5). */
|
|
171
|
+
interface AdvancedInputFacade {
|
|
172
|
+
/**
|
|
173
|
+
* Register a normalized input capture. Caller-fiber-owned: owner unload
|
|
174
|
+
* removes it. A duplicate id or a second exclusive capture is an explicit
|
|
175
|
+
* error.
|
|
176
|
+
*/
|
|
177
|
+
capture(spec: AdvancedInputCaptureSpec): AdvancedInputCaptureHandle;
|
|
178
|
+
}
|
|
179
|
+
/** One selectable row in the imperative select broker. */
|
|
180
|
+
interface AdvancedSelectItem {
|
|
181
|
+
readonly value: string;
|
|
182
|
+
readonly label: string;
|
|
183
|
+
readonly description?: string;
|
|
184
|
+
readonly group?: string;
|
|
185
|
+
}
|
|
186
|
+
/** Options for {@link AdvancedUiFacade.select}. */
|
|
187
|
+
interface AdvancedSelectOptions {
|
|
188
|
+
readonly items: readonly AdvancedSelectItem[];
|
|
189
|
+
readonly header?: string;
|
|
190
|
+
readonly enableSearch?: boolean;
|
|
191
|
+
readonly width?: number;
|
|
192
|
+
readonly maxHeight?: number;
|
|
193
|
+
/** Abort the prompt (the promise resolves undefined). */
|
|
194
|
+
readonly signal?: AbortSignal;
|
|
195
|
+
}
|
|
196
|
+
/** Options for {@link AdvancedUiFacade.confirm}. */
|
|
197
|
+
interface AdvancedConfirmOptions {
|
|
198
|
+
readonly question: string;
|
|
199
|
+
readonly detail?: string;
|
|
200
|
+
/** The approve option label (default 'Yes'). */
|
|
201
|
+
readonly approveLabel?: string;
|
|
202
|
+
/** The reject option label (default 'No'). */
|
|
203
|
+
readonly rejectLabel?: string;
|
|
204
|
+
/** Abort the prompt (the promise resolves false). */
|
|
205
|
+
readonly signal?: AbortSignal;
|
|
206
|
+
}
|
|
207
|
+
/** Options for {@link AdvancedUiFacade.input}. */
|
|
208
|
+
interface AdvancedInputOptions {
|
|
209
|
+
readonly question: string;
|
|
210
|
+
readonly detail?: string;
|
|
211
|
+
/** Abort the prompt (the promise resolves undefined). */
|
|
212
|
+
readonly signal?: AbortSignal;
|
|
213
|
+
}
|
|
214
|
+
/** Options for {@link AdvancedUiFacade.notify}. */
|
|
215
|
+
interface AdvancedNotifyOptions {
|
|
216
|
+
readonly type?: 'info' | 'error';
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The public host facade handed to a {@link AdvancedUiFacade.custom}
|
|
220
|
+
* factory (plan §4B). The plugin builds an interactive component against
|
|
221
|
+
* this facade — never a private TUI object — and completes the custom()
|
|
222
|
+
* promise through `done`/`close`.
|
|
223
|
+
*/
|
|
224
|
+
interface AdvancedCustomHost {
|
|
225
|
+
readonly surfaceId: string;
|
|
226
|
+
readonly generation: number;
|
|
227
|
+
readonly width: number;
|
|
228
|
+
readonly height: number;
|
|
229
|
+
/** Complete the custom() promise with a result (closes the surface). */
|
|
230
|
+
done(result?: unknown): void;
|
|
231
|
+
/** Cancel the custom() promise (resolves undefined; closes the surface). */
|
|
232
|
+
close(): void;
|
|
233
|
+
}
|
|
234
|
+
/** The advanced UI facade (plan §6/§8 + Phase 4 §4A/§4B). */
|
|
235
|
+
interface AdvancedUiFacade {
|
|
236
|
+
/**
|
|
237
|
+
* Open an interactive managed overlay hosting a focused interactive
|
|
238
|
+
* component. The Host mounts it through its overlay broker (modal
|
|
239
|
+
* stacking, focus, fullscreen migration, teardown) and returns a
|
|
240
|
+
* caller-fiber-owned lease. A throwing render/input/focus callback is
|
|
241
|
+
* isolated (health ledger).
|
|
242
|
+
*/
|
|
243
|
+
showInteractiveOverlay(component: AdvancedInteractiveComponent, options?: TuiOverlayOptions): AdvancedOverlayLease;
|
|
244
|
+
/**
|
|
245
|
+
* Imperative selection (plan §4A): show a picker and resolve with the
|
|
246
|
+
* selected value, or undefined on cancel/abort. Caller-fiber-owned:
|
|
247
|
+
* owner unload aborts the prompt. Reuses the Host's picker overlay —
|
|
248
|
+
* no second modal manager.
|
|
249
|
+
*/
|
|
250
|
+
select(options: AdvancedSelectOptions): Promise<string | undefined>;
|
|
251
|
+
/**
|
|
252
|
+
* Imperative confirmation (plan §4A): show a yes/no question and
|
|
253
|
+
* resolve with the choice; cancel/abort resolves false. Caller-fiber-
|
|
254
|
+
* owned: owner unload aborts the prompt. Reuses the Host's question
|
|
255
|
+
* flow.
|
|
256
|
+
*/
|
|
257
|
+
confirm(options: AdvancedConfirmOptions): Promise<boolean>;
|
|
258
|
+
/**
|
|
259
|
+
* Imperative free-text input (plan §4A): show a question with a
|
|
260
|
+
* free-text row and resolve with the text, or undefined on
|
|
261
|
+
* cancel/abort. Caller-fiber-owned: owner unload aborts the prompt.
|
|
262
|
+
* Reuses the Host's question flow.
|
|
263
|
+
*/
|
|
264
|
+
input(options: AdvancedInputOptions): Promise<string | undefined>;
|
|
265
|
+
/**
|
|
266
|
+
* Imperative notification (plan §4A): show a transient notice. Bounded,
|
|
267
|
+
* no raw ANSI, surface-detach safe, plugin-unload safe.
|
|
268
|
+
*/
|
|
269
|
+
notify(message: string, options?: AdvancedNotifyOptions): void;
|
|
270
|
+
/**
|
|
271
|
+
* Custom interactive UI (plan §4B): mount a factory-built interactive
|
|
272
|
+
* component (fullscreen/panel/overlay) and resolve with the result the
|
|
273
|
+
* component reports through {@link AdvancedCustomHost.done}, or
|
|
274
|
+
* undefined on close/cancel. The factory receives ONLY the public host
|
|
275
|
+
* facade — never a private TUI object. Caller-fiber-owned: owner
|
|
276
|
+
* unload closes the surface and settles the promise.
|
|
277
|
+
*/
|
|
278
|
+
custom(factory: (host: AdvancedCustomHost) => AdvancedInteractiveComponent, options?: TuiOverlayOptions): Promise<unknown>;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* The advanced host-state facade (plan §4D): semantic Host UI state
|
|
282
|
+
* control — theme query/select, title override, working-indicator
|
|
283
|
+
* override and tool-expansion preference. The Host owns persistence and
|
|
284
|
+
* repaint; these are live overrides (a stale facade is inert).
|
|
285
|
+
*/
|
|
286
|
+
interface AdvancedHostState {
|
|
287
|
+
/** The current theme id ('dark' | 'light' | 'custom'). */
|
|
288
|
+
getTheme(): string;
|
|
289
|
+
/** Apply a theme by id ('dark'/'light' or a registered plugin theme
|
|
290
|
+
* name). Unknown names are a no-op. */
|
|
291
|
+
setTheme(name: string): void;
|
|
292
|
+
/** Override the session title shown in the header (undefined clears). */
|
|
293
|
+
setTitle(title: string | undefined): void;
|
|
294
|
+
/** Override the working-indicator message (undefined clears). */
|
|
295
|
+
setWorkingMessage(message: string | undefined): void;
|
|
296
|
+
/** Set the tool-output expansion master switch. */
|
|
297
|
+
setToolsExpanded(expanded: boolean): void;
|
|
298
|
+
}
|
|
299
|
+
/** The full advanced facade (plan §4: `advanced(service)`). */
|
|
300
|
+
interface AdvancedFacade {
|
|
301
|
+
/** Normalized input capture. */
|
|
302
|
+
readonly input: AdvancedInputFacade;
|
|
303
|
+
/** Focused interactive surfaces (interactive overlays) + the Phase-4
|
|
304
|
+
* imperative UI broker (select/confirm/input/notify/custom). */
|
|
305
|
+
readonly ui: AdvancedUiFacade;
|
|
306
|
+
/** The CURRENT surface's advanced editor controls (a getter — the
|
|
307
|
+
* controls follow the live surface attachment). */
|
|
308
|
+
readonly editor: AdvancedEditorControls;
|
|
309
|
+
/** The Phase-4 host-state facade (theme/title/working/tools-expanded). */
|
|
310
|
+
readonly host: AdvancedHostState;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* The minimal service surface the advanced facade consumes (plan §4:
|
|
314
|
+
* `advanced(service)`). STRUCTURAL and defined here so the packed
|
|
315
|
+
* advanced declarations never reference the internal service module —
|
|
316
|
+
* the concrete `piTuiExtensions` service implements these members, but
|
|
317
|
+
* the public `PiTuiExtensionService` interface does not declare them.
|
|
318
|
+
*
|
|
319
|
+
* `api` is the structural anchor: a plain `PiTuiExtensionService` value
|
|
320
|
+
* satisfies the facade's parameter (the seam members are optional in the
|
|
321
|
+
* type), and the facade throws loudly when the host does not implement
|
|
322
|
+
* the seam (a host/plugin version mismatch) instead of failing later
|
|
323
|
+
* with an obscure "not a function".
|
|
324
|
+
*/
|
|
325
|
+
interface AdvancedServiceHost {
|
|
326
|
+
/** The public api() surface (the structural anchor — the facade does
|
|
327
|
+
* not consume it; it makes a plain `PiTuiExtensionService` value
|
|
328
|
+
* assignable to the parameter). */
|
|
329
|
+
readonly api: () => PiTuiApiInfo;
|
|
330
|
+
/** The service's internal advanced seam: register a normalized input
|
|
331
|
+
* capture (caller-fiber-owned). */
|
|
332
|
+
_advancedCaptureInput?(spec: AdvancedInputCaptureSpec): AdvancedInputCaptureHandle;
|
|
333
|
+
/** The service's internal advanced seam: open an interactive managed
|
|
334
|
+
* overlay (caller-fiber-owned lease). */
|
|
335
|
+
_advancedShowInteractiveOverlay?(component: AdvancedInteractiveComponent, options?: TuiOverlayOptions): AdvancedOverlayLease;
|
|
336
|
+
/** The service's internal advanced seam: the current surface's advanced
|
|
337
|
+
* editor controls (inert when stale). */
|
|
338
|
+
_advancedEditorControls?(): AdvancedEditorControls;
|
|
339
|
+
/** The service's internal advanced seam (Phase 4): the imperative UI
|
|
340
|
+
* broker (select/confirm/input/notify/custom). */
|
|
341
|
+
_advancedUiSelect?(options: AdvancedSelectOptions): Promise<string | undefined>;
|
|
342
|
+
_advancedUiConfirm?(options: AdvancedConfirmOptions): Promise<boolean>;
|
|
343
|
+
_advancedUiInput?(options: AdvancedInputOptions): Promise<string | undefined>;
|
|
344
|
+
_advancedUiNotify?(message: string, options?: AdvancedNotifyOptions): void;
|
|
345
|
+
_advancedUiCustom?(factory: (host: AdvancedCustomHost) => AdvancedInteractiveComponent, options?: TuiOverlayOptions): Promise<unknown>;
|
|
346
|
+
/** The service's internal advanced seam (Phase 4): the host-state
|
|
347
|
+
* facade (theme/title/working/tools-expanded). */
|
|
348
|
+
_advancedHostState?(): AdvancedHostState;
|
|
349
|
+
}
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/extension/advanced.d.ts
|
|
352
|
+
/**
|
|
353
|
+
* Build the ADVANCED facade over the shared `piTuiExtensions` service
|
|
354
|
+
* (plan §4). The facade is a thin capability view: every resource it
|
|
355
|
+
* creates is caller-fiber-owned and surface-generation-scoped through the
|
|
356
|
+
* service's shared Extension Runtime — never a second plugin system.
|
|
357
|
+
*
|
|
358
|
+
* The facade is safe to call before any surface exists: registrations
|
|
359
|
+
* (input captures) are service-lifetime and attach later; leases
|
|
360
|
+
* (interactive overlays) and the editor controls are inert until a
|
|
361
|
+
* surface is live.
|
|
362
|
+
*
|
|
363
|
+
* The parameter is the structural {@link AdvancedServiceHost}: a plain
|
|
364
|
+
* `PiTuiExtensionService` value is accepted (the seam members are optional
|
|
365
|
+
* in the type), and the facade throws loudly when the host does not
|
|
366
|
+
* implement the seam (a host/plugin version mismatch).
|
|
367
|
+
* @param service - the `piTuiExtensions` service (from `ctx.get(...)`).
|
|
368
|
+
* @returns the Advanced facade.
|
|
369
|
+
*/
|
|
370
|
+
declare function advanced(service: AdvancedServiceHost): AdvancedFacade;
|
|
371
|
+
//#endregion
|
|
372
|
+
export { ADVANCED_API_LEVEL, type AdvancedConfirmOptions, type AdvancedCustomHost, type AdvancedEditorControls, type AdvancedFacade, type AdvancedFocusHandle, type AdvancedHostState, type AdvancedInputCaptureHandle, type AdvancedInputCaptureMode, type AdvancedInputCaptureSpec, type AdvancedInputEvent, type AdvancedInputFacade, type AdvancedInputOptions, type AdvancedInteractiveComponent, type AdvancedNotifyOptions, type AdvancedOverlayLease, type AdvancedRenderContext, type AdvancedSelectItem, type AdvancedSelectOptions, type AdvancedServiceHost, type AdvancedUiFacade, type ExtensionTier, advanced };
|