@achasoft/dsh-advanced-sidebar 0.1.0 → 0.3.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.
Files changed (38) hide show
  1. package/README.md +279 -128
  2. package/cordis.patch.yml +31 -3
  3. package/lib/client.js +2803 -466
  4. package/lib/client.js.map +1 -1
  5. package/lib/host.js +2071 -418
  6. package/lib/index.js +6 -2
  7. package/lib/preview-content-BVUQ5oOR.js +465 -0
  8. package/lib/remote.js +330 -25
  9. package/lib/typert.host.js +330 -25
  10. package/lib/ui-preview.js +352 -0
  11. package/package.json +8 -2
  12. package/types/client/ActionMenu.d.ts +16 -1
  13. package/types/client/LogDownloadDialog.d.ts +24 -0
  14. package/types/client/contract.d.ts +57 -1
  15. package/types/client/index.d.ts +4 -2
  16. package/types/client/locales.d.ts +100 -0
  17. package/types/client/log-download.d.ts +179 -0
  18. package/types/client/panels/PreviewPanel.d.ts +20 -15
  19. package/types/client/panels/preview-file.d.ts +61 -0
  20. package/types/client/panels/preview-mode.d.ts +67 -0
  21. package/types/client/panels/preview-scratchpad.d.ts +53 -0
  22. package/types/client/panels/preview-url.d.ts +17 -0
  23. package/types/client/panels/shared.d.ts +15 -2
  24. package/types/client/preview-driver.d.ts +121 -0
  25. package/types/client/preview-storage.d.ts +43 -0
  26. package/types/client/preview-types.d.ts +21 -0
  27. package/types/client/preview-values.d.ts +43 -0
  28. package/types/host/deletion.d.ts +32 -23
  29. package/types/host/git.d.ts +94 -8
  30. package/types/host/index.d.ts +97 -5
  31. package/types/host/preview-content.d.ts +179 -0
  32. package/types/host/preview-serve.d.ts +242 -0
  33. package/types/host/settings-section.d.ts +49 -0
  34. package/types/host/types.d.ts +341 -0
  35. package/types/host/ui-bridge.d.ts +197 -0
  36. package/types/host/ui-preview-tool.d.ts +60 -0
  37. package/types/index.d.ts +6 -2
  38. package/types/ui-preview.d.ts +11 -0
@@ -0,0 +1,121 @@
1
+ /**
2
+ * The panel's end of the agent channel: poll for work, execute it against the frame, report back.
3
+ *
4
+ * There is no host-to-browser push in an out-of-tree plugin, so the direction is inverted — the
5
+ * panel asks the Host whether the agent wants anything, and answers with what it found. This module
6
+ * owns that loop, the frame-side execution, and the console capture the agent reads through.
7
+ *
8
+ * Three rules shape everything here:
9
+ *
10
+ * 1. **Only same-origin frames can be inspected.** A page framed from another origin has no
11
+ * reachable `document`, and `contentDocument === null` is the only reliable test for it. Every
12
+ * read and every write refuses such a frame by name, so the model gets "this page is
13
+ * cross-origin" instead of a timeout or an empty DOM.
14
+ * 2. **The agent's window into the frame is bounded.** The DOM walk stops at a node count and a
15
+ * text length, the eval result is serialized with a cycle-safe replacer under a character cap,
16
+ * and the console buffer is a ring. A page that logs per animation frame cannot flood a model's
17
+ * context through this tool.
18
+ * 3. **Nothing here throws across the boundary.** A command that a page rejects — a missing
19
+ * selector, an expression that throws, a frame that navigated mid-command — comes back as a
20
+ * readable sentence, because a rejected promise at this layer would be an unexplained tool
21
+ * timeout.
22
+ * @module @achasoft/dsh-advanced-sidebar/client/preview-driver
23
+ */
24
+ import type { PreviewControlMessage } from '../host/types.ts';
25
+ import type { PreviewFace } from './preview-types.ts';
26
+ /** The frame as this driver needs to see it. */
27
+ export interface FrameView {
28
+ /** The iframe element, or null while none is mounted. */
29
+ readonly element: HTMLIFrameElement | null;
30
+ /**
31
+ * The frame's document, or null when it is cross-origin or not loaded yet.
32
+ *
33
+ * Read from the element each time rather than cached: a navigation replaces the document, and a
34
+ * cached reference to the old one would answer about the page the operator just left.
35
+ */
36
+ readonly document: Document | null;
37
+ /** The frame's window, or null when it is cross-origin or not loaded yet. */
38
+ readonly window: Window | null;
39
+ }
40
+ /**
41
+ * What the driver needs from the panel around it.
42
+ *
43
+ * Callbacks rather than a React reference: the loop runs outside the render cycle, and a panel that
44
+ * re-rendered on every tick would re-run its own effects at poll cadence.
45
+ */
46
+ export interface DriverHooks {
47
+ /** Read the frame as it is right now. */
48
+ frame: () => FrameView;
49
+ /** Apply a mode change the agent asked for. */
50
+ control: (message: PreviewControlMessage) => void;
51
+ /** Force the frame to remount and reload. */
52
+ reload: () => void;
53
+ /** Apply a viewport the agent asked for. */
54
+ resize: (width: number, height: number) => void;
55
+ }
56
+ /**
57
+ * The polling driver. One instance per mounted panel; `stop()` releases it.
58
+ */
59
+ export declare class PreviewDriver {
60
+ private readonly face;
61
+ private readonly clientId;
62
+ private readonly sessionId;
63
+ private readonly hooks;
64
+ /** Console entries captured since the last report, oldest first. */
65
+ private readonly buffer;
66
+ /** Commands currently executing, so a poll that overlaps the previous one does not double-run. */
67
+ private readonly running;
68
+ private timer;
69
+ private stopped;
70
+ /**
71
+ * @param face - the Remote face the panel was handed.
72
+ * @param clientId - this browser tab's identity, generated once per panel mount.
73
+ * @param sessionId - the session the panel serves.
74
+ * @param hooks - how to read and act on the frame.
75
+ */
76
+ constructor(face: PreviewFace, clientId: string, sessionId: string, hooks: DriverHooks);
77
+ /**
78
+ * Start polling. Idempotent, so a re-render may call it again freely.
79
+ * @param bind - the panel's current state, refreshed on every poll.
80
+ */
81
+ start(bind: () => {
82
+ mounted: boolean;
83
+ mode: 'server' | 'file' | 'url' | 'scratchpad';
84
+ filePath?: string | undefined;
85
+ workspacePath?: string | undefined;
86
+ url?: string | undefined;
87
+ inspectable: boolean;
88
+ width: number;
89
+ height: number;
90
+ }): void;
91
+ /** Stop polling. Called when the panel unmounts. */
92
+ stop(): void;
93
+ /**
94
+ * Execute one command and report what it did.
95
+ * @param command - the command.
96
+ */
97
+ private execute;
98
+ /**
99
+ * Run one command against the frame.
100
+ * @param command - the command.
101
+ * @returns the result, or the sentence explaining the refusal.
102
+ */
103
+ private run;
104
+ /**
105
+ * Take everything captured since the last drain.
106
+ * @returns the entries, oldest first.
107
+ */
108
+ private drain;
109
+ /**
110
+ * Wrap the frame's console, once per document.
111
+ *
112
+ * Called from the poll rather than from a load listener because a navigation can complete between
113
+ * two polls, and the check is one property read on the frame's own window.
114
+ */
115
+ private captureConsole;
116
+ /**
117
+ * Append one captured entry, dropping the oldest past the ring.
118
+ * @param entry - the entry.
119
+ */
120
+ private push;
121
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Where the Preview panel's scratchpad document lives between reloads.
3
+ *
4
+ * A scratchpad is a thought being worked out, not a deliverable, so it is stored in this browser
5
+ * rather than written to the workspace as a file — and per workspace, because two projects'
6
+ * experiments have nothing to do with each other.
7
+ *
8
+ * Split out of the mode component so the rules are testable without a DOM, and so every storage
9
+ * failure is handled in one place: `localStorage` may be absent (a non-browser host), disabled
10
+ * (private modes and hardened settings), or full, and none of those may take the panel down. Each is
11
+ * reported as "nothing saved" or "the write was refused", which the editor renders as a note.
12
+ * @module @achasoft/dsh-plugins/dsh-advanced-sidebar/client/preview-storage
13
+ */
14
+ /**
15
+ * Largest document this mode will store.
16
+ *
17
+ * A person typing HTML reaches a few kilobytes; a megabyte is a paste of something that belongs in a
18
+ * file. The Host's own route refuses more than its limit, so reading is capped here too rather than
19
+ * discovering the refusal after a round trip.
20
+ */
21
+ export declare const MAX_SCRATCHPAD_CHARS: number;
22
+ /**
23
+ * The storage key one workspace's scratchpad lives under.
24
+ *
25
+ * The workspace path is the whole identity: two sessions in one directory are editing the same
26
+ * experiment, and a session with no directory shares one global scratchpad rather than losing it.
27
+ * @param workspace - the absolute workspace path, or undefined.
28
+ * @returns the key.
29
+ */
30
+ export declare function scratchpadKey(workspace: string | undefined): string;
31
+ /**
32
+ * Read one workspace's saved document.
33
+ * @param workspace - the absolute workspace path.
34
+ * @returns the saved document, or undefined when there is none or storage refuses.
35
+ */
36
+ export declare function readScratchpad(workspace: string | undefined): string | undefined;
37
+ /**
38
+ * Write one workspace's document, reporting a refused write instead of throwing.
39
+ * @param workspace - the absolute workspace path.
40
+ * @param text - the document.
41
+ * @returns true when the browser accepted it.
42
+ */
43
+ export declare function writeScratchpad(workspace: string | undefined, text: string): boolean;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The Preview panel's own faces: what its modes are handed, and the narrow slice of the dock's
3
+ * injected face the command driver needs.
4
+ *
5
+ * Split out from the panel component so the driver — which is not a React component and must be
6
+ * testable and readable on its own — does not import the component that imports it.
7
+ * @module @achasoft/dsh-advanced-sidebar/client/preview-types
8
+ */
9
+ import type { PanelHostInjected } from './contract.ts';
10
+ /** The dock's injected face without the reserved `hooks` compartment, as every panel receives it. */
11
+ export type PanelFace = Omit<PanelHostInjected, 'hooks'>;
12
+ /**
13
+ * The endpoints the command driver calls, and only those.
14
+ *
15
+ * Narrowed deliberately: a driver handed the whole panel face could reach a git write, and the
16
+ * point of the split is that the agent channel's browser end can do exactly three things — ask for
17
+ * work, report a result, and say it left.
18
+ */
19
+ export type PreviewFace = Pick<PanelFace, 'previewPoll' | 'previewResult' | 'previewRelease'>;
20
+ /** Which mode the Preview panel is showing. */
21
+ export type PreviewMode = 'server' | 'file' | 'url' | 'scratchpad';
@@ -0,0 +1,43 @@
1
+ /**
2
+ * How a value that crossed the frame boundary is turned into text a model can read.
3
+ *
4
+ * Everything the agent channel reports — a console argument, an `eval` result, an element — arrived
5
+ * from a page nobody controls. A page can hold a cyclic object, a `BigInt`, a getter that throws, a
6
+ * function, a DOM node, or an `Error`, and `JSON.stringify` refuses three of those outright. Each is
7
+ * projected to something readable rather than failing the command, because "a model asked for
8
+ * something awkward" is an ordinary event and an unexplained tool failure is not.
9
+ *
10
+ * Pure and frame-free, so the whole projection is stated in tests without a document.
11
+ * @module @achasoft/dsh-advanced-sidebar/client/preview-values
12
+ */
13
+ /** Largest JSON body one `eval` result may produce. */
14
+ export declare const EVAL_CAP: number;
15
+ /**
16
+ * Render one console argument the way a browser's own console would read it.
17
+ * @param value - the argument.
18
+ * @returns a string.
19
+ */
20
+ export declare function describeValue(value: unknown): string;
21
+ /**
22
+ * Serialize a value with cycles broken and DOM nodes projected to their markup.
23
+ * @param value - the value.
24
+ * @returns the JSON text, or undefined when the value serializes to `undefined`.
25
+ */
26
+ export declare function stringify(value: unknown): string | undefined;
27
+ /**
28
+ * Build a replacer that keeps one reference per object, so a cycle serializes instead of throwing.
29
+ *
30
+ * A fresh replacer per call, because the seen-set is per serialization: sharing one across calls
31
+ * would label a value `[circular]` only because an earlier, unrelated call had already seen it.
32
+ * @returns the replacer.
33
+ */
34
+ export declare function createReplacer(): (key: string, value: unknown) => unknown;
35
+ /**
36
+ * Serialize a value the way a model can read it.
37
+ * @param value - the evaluated value.
38
+ * @returns the JSON text, and why it is not JSON when it is not.
39
+ */
40
+ export declare function safeJson(value: unknown): {
41
+ text: string;
42
+ note?: string;
43
+ };
@@ -1,29 +1,46 @@
1
1
  /**
2
2
  * Session deletion.
3
3
  *
4
- * No harness capability deletes a session: session persistence is append-only and exposes no delete
5
- * verb, and the workspace registry can only ARCHIVE hide a session while keeping its log and its
6
- * accounting slot. Delete is therefore assembled here from the two things that do exist, and it is
7
- * honest about which one it managed:
4
+ * No harness capability deletes a session. Delete is therefore the one thing that does exist — the
5
+ * workspace registry's ARCHIVE, which hides a session while keeping its log and its accounting slot —
6
+ * and it is honest that this is all it did.
8
7
  *
9
- * - `archive` hides the session. Reversible in principle (the durable slot is preserved), and the
10
- * only mode that can act on a session that is currently live.
11
- * - `purge` additionally removes the persistence backend's own per-session artifact. Nothing undoes
12
- * that, and a backend that keeps no per-session artifact (SQLite) reports the archive alone.
8
+ * `deleteMode: 'purge'` asked for more: the backend's own per-session artifact removed as well. On
9
+ * the harness this release targets (`0.1.5-rc.2`) that cannot be done correctly, and so it is not
10
+ * attempted:
13
11
  *
14
- * A live session is never purged. Its writer holds the artifact open and a running turn would keep
15
- * appending to a file that no longer exists, so the archive commits and the reason is returned.
12
+ * - The published `SessionPersistence` contract is `create`, `open`, `flush`, `stat` and `list`. It
13
+ * has no removal verb and no way to ask where a session's bytes live; the `supportsRawArtifacts`
14
+ * flag and `locate()` this module used to read were never part of it, so purging was silently
15
+ * impossible while the settings still offered it.
16
+ * - The JSONL backend keeps a session as a DIRECTORY — one immutable file per format generation plus
17
+ * a `session.lock` write lease — behind an in-process cold-log memo, and the workspace registry and
18
+ * the session projection cache index its header. Unlinking files underneath all of that bypasses the
19
+ * lease another process may hold, leaves stale caches answering for a log that is gone, and is the
20
+ * same class of out-of-band edit that produces "torn record" corruption reports.
21
+ *
22
+ * So the capability is reported as unavailable with that reason, the settings card shows the reason
23
+ * and refuses to select `purge`, and a composition that still configures it gets an archive whose
24
+ * result says, every time, why nothing was removed. When the harness grows a supported removal verb,
25
+ * this is the one module to change.
16
26
  * @module @achasoft/dsh-advanced-sidebar/host/deletion
17
27
  */
18
28
  import type { Context } from '@deepseek-ai/cordis';
19
29
  import type { AdvancedSidebarSettings, DeleteSessionRequest, DeleteSessionResult } from './types.ts';
20
30
  /** What Delete can do on this Host right now. */
21
31
  export interface DeletionCapability {
22
- /** Whether the persistence backend exposes a per-session artifact that could be removed. */
32
+ /** Whether a session's durable log can be removed. Always false on this harness; see the module. */
23
33
  readonly canPurge: boolean;
24
34
  /** Why purging is unavailable, when it is. */
25
35
  readonly reason?: string;
26
36
  }
37
+ /**
38
+ * Why `purge` is unavailable, in the words the settings card and the Delete result show.
39
+ *
40
+ * One sentence for both, so the reason a person reads before choosing a mode is the reason they read
41
+ * after pressing Delete.
42
+ */
43
+ export declare const PURGE_UNAVAILABLE_REASON: string;
27
44
  /**
28
45
  * Commits Delete for the sidebar menu. Stateless apart from the context and settings it reads.
29
46
  */
@@ -31,27 +48,19 @@ export declare class SessionDeleter {
31
48
  private readonly ctx;
32
49
  private readonly source;
33
50
  /**
34
- * @param ctx - Host context carrying the workspace registry and session persistence.
51
+ * @param ctx - Host context carrying the workspace registry.
35
52
  * @param source - reads the current settings section; called per request.
36
53
  */
37
54
  constructor(ctx: Context, source: () => AdvancedSidebarSettings);
38
55
  /**
39
56
  * Report whether the durable log can be removed at all.
40
- * @returns the capability, with a reason when purging is impossible.
57
+ * @returns the capability, with the reason purging is impossible.
41
58
  */
42
59
  describe(): DeletionCapability;
43
60
  /**
44
- * Hide one session, and remove its durable artifact when the mode and the Host allow it.
61
+ * Hide one session, and say plainly when a requested purge did not happen.
45
62
  * @param request - the session to delete.
46
- * @param signal - cancellation for the persistence listing.
47
63
  * @returns what was actually done, or a classified failure.
48
64
  */
49
- delete(request: DeleteSessionRequest, signal?: AbortSignal): Promise<DeleteSessionResult>;
50
- /**
51
- * Find the backend artifact for one session, or say why it will not be removed.
52
- * @param sessionId - the session to look up.
53
- * @param signal - cancellation for the persistence listing.
54
- * @returns the artifact path, or the reason purging is skipped.
55
- */
56
- private locateArtifact;
65
+ delete(request: DeleteSessionRequest): Promise<DeleteSessionResult>;
57
66
  }
@@ -19,6 +19,20 @@ import type { AdvancedSidebarSettings, CapabilityState, GitCommitMessageRequest,
19
19
  * @returns the message, trimmed.
20
20
  */
21
21
  export declare function stripFence(text: string): string;
22
+ /**
23
+ * The `-c` overrides that disarm the untrusted filter programs one config listing names.
24
+ *
25
+ * An empty value is git's own "no command" for a filter (`convert.c` runs a driver only when its
26
+ * command is non-empty), so the file is then hashed as its bytes, exactly as with no driver at all.
27
+ * @param listing - `git config --show-scope --name-only --get-regexp` output, one `scope<TAB>key` per
28
+ * line; a line with no scope (an older git) is treated as untrusted.
29
+ * @returns the overrides, or the key git could not be told about safely.
30
+ */
31
+ export declare function filterOverrides(listing: string): {
32
+ readonly config: readonly string[];
33
+ } | {
34
+ readonly unsafeKey: string;
35
+ };
22
36
  /**
23
37
  * Reads one workspace's git state. One instance serves every request; the resolved `git` path is
24
38
  * cached across calls and dropped whenever a lookup fails, so installing git later needs no restart.
@@ -91,6 +105,47 @@ export declare class GitReader {
91
105
  * @returns the push, the reading after it, or a classified failure.
92
106
  */
93
107
  push(request: GitPushRequest, signal?: AbortSignal): Promise<GitPushResult>;
108
+ /**
109
+ * The arguments that publish one branch to one remote and record it as the upstream.
110
+ *
111
+ * Both names come from repository state, not from the browser — and repository state is not
112
+ * trusted either. A HEAD of `refs/heads/--receive-pack=/tmp/x` is a valid ref (`check-ref-format`
113
+ * accepts it) that `git status` reports as the branch `--receive-pack=/tmp/x`; handed to
114
+ * `git push` as a bare argument, git parsed it as the option and ran `/tmp/x` as the remote's
115
+ * receive-pack. So three independent things stand in the way:
116
+ *
117
+ * 1. Both names are refused outright when they begin with `-`.
118
+ * 2. The branch must pass `git check-ref-format --branch`, which is git's own branch-name grammar
119
+ * (it rejects a leading `-`, `..`, control characters, `@{`) and must echo back unchanged, so a
120
+ * `@{-1}` shorthand cannot be expanded into some other branch. The remote must make a valid
121
+ * remote-tracking ref, which is how git itself validates a remote name.
122
+ * 3. The push names the refs after `--`, where `git push` (parse-options) stops reading options,
123
+ * and as a fully qualified `refs/heads/<b>:refs/heads/<b>` refspec, which also cannot be read as
124
+ * a shorter ref with the same name on the remote. `--set-upstream` records the same tracking
125
+ * branch it records for the short spelling.
126
+ * @param repository - the resolved repository.
127
+ * @param remote - the remote to publish to, from `git remote`.
128
+ * @param branch - the current branch, from `git status`.
129
+ * @param signal - cancellation for the validation invocations.
130
+ * @returns the push arguments, or the failure to return.
131
+ */
132
+ private publishArgv;
133
+ /**
134
+ * Whether git accepts a name as a branch name, spelled exactly as given.
135
+ * @param repository - the resolved repository.
136
+ * @param branch - the name, already known not to begin with `-`.
137
+ * @param signal - cancellation for the invocation.
138
+ * @returns true when `check-ref-format --branch` accepts it and echoes it back unchanged.
139
+ */
140
+ private isValidBranchName;
141
+ /**
142
+ * Whether git accepts a name as a remote name.
143
+ * @param repository - the resolved repository.
144
+ * @param remote - the name, already known not to begin with `-`.
145
+ * @param signal - cancellation for the invocation.
146
+ * @returns true when `refs/remotes/<remote>/HEAD` is a well-formed ref, git's own remote-name rule.
147
+ */
148
+ private isValidRemoteName;
94
149
  /**
95
150
  * Ask the deployment's own model to write a commit message for what is staged.
96
151
  *
@@ -104,7 +159,7 @@ export declare class GitReader {
104
159
  draftCommitMessage(request: GitCommitMessageRequest, signal?: AbortSignal): Promise<GitCommitMessageResult>;
105
160
  /**
106
161
  * The patch a drafted message describes, bounded so a large change cannot become a large request.
107
- * @param root - absolute repository root.
162
+ * @param repository - the resolved repository.
108
163
  * @param amend - describe the previous commit's content as well as the index.
109
164
  * @param signal - cancellation for the invocations.
110
165
  * @returns the patch and whether it was cut, or the failure to return.
@@ -112,14 +167,14 @@ export declare class GitReader {
112
167
  private stagedPatch;
113
168
  /**
114
169
  * Whether HEAD has a parent commit.
115
- * @param cwd - absolute repository root.
170
+ * @param repository - the resolved repository.
116
171
  * @param signal - cancellation for the invocation.
117
172
  * @returns true when `HEAD~1` resolves.
118
173
  */
119
174
  private hasParent;
120
175
  /**
121
176
  * The remote an unpublished branch would be published to.
122
- * @param cwd - absolute repository root.
177
+ * @param repository - the resolved repository.
123
178
  * @param signal - cancellation for the invocation.
124
179
  * @returns `origin` when it exists, else the first remote, else undefined.
125
180
  */
@@ -147,21 +202,21 @@ export declare class GitReader {
147
202
  private contain;
148
203
  /**
149
204
  * The author `git commit` would record.
150
- * @param cwd - the repository root.
205
+ * @param repository - the resolved repository.
151
206
  * @param signal - cancellation for the invocation.
152
207
  * @returns `Name <email>`, or undefined when git has no identity configured.
153
208
  */
154
209
  private author;
155
210
  /**
156
211
  * Whether the index differs from HEAD.
157
- * @param cwd - the repository root.
212
+ * @param repository - the resolved repository.
158
213
  * @param signal - cancellation for the invocation.
159
214
  * @returns true when a commit would record something.
160
215
  */
161
216
  private hasStaged;
162
217
  /**
163
218
  * Report what the panel may do to this repository.
164
- * @param cwd - the repository root.
219
+ * @param repository - the resolved repository.
165
220
  * @param signal - cancellation for the identity lookup.
166
221
  * @returns the write capability.
167
222
  */
@@ -181,10 +236,41 @@ export declare class GitReader {
181
236
  */
182
237
  private locateRepository;
183
238
  /**
184
- * Run one git invocation with this plugin's own bounds.
239
+ * The overrides that switch off the content filters a repository's own config defines.
240
+ *
241
+ * Listing config runs nothing — `git config` reads files — so this is safe to ask before any
242
+ * reading. `--show-scope` arrived in git 2.26; an older git refuses the flag, and the listing is
243
+ * then repeated without it and every filter it names is treated as untrusted, which can only make
244
+ * a reading more conservative. A listing that fails both ways fails the reading: a status that
245
+ * cannot prove its filters are disarmed is not run.
246
+ * @param root - absolute repository root.
247
+ * @param signal - cancellation for the listing.
248
+ * @returns the `-c` pairs, or the failure to return.
249
+ */
250
+ private untrustedFilterConfig;
251
+ /**
252
+ * Run one READING: an invocation the panel makes on its own, which must run no program the
253
+ * repository's config names.
254
+ *
255
+ * On top of {@link git}'s own `core.fsmonitor` override it disarms the repository's content
256
+ * filters. Writes (`add`, `restore`, `commit`, `push`) deliberately do not go through here: they are
257
+ * an operator's explicit action, a filter such as LFS is part of what staging correctly means, and a
258
+ * commit's hooks are a real answer the panel reports.
259
+ * @param repository - the resolved repository, carrying its filter overrides.
260
+ * @param args - arguments after the executable and the overrides.
261
+ * @param signal - the caller's cancellation.
262
+ * @param maxBytes - the caller's own output bound, when it has one.
263
+ * @param cwd - directory to run in; the repository root unless the reading is relative to the workspace.
264
+ * @returns the finished command.
265
+ */
266
+ private read;
267
+ /**
268
+ * Run one git invocation with this plugin's own bounds and {@link INVOCATION_CONFIG}.
185
269
  * @param cwd - directory to run in.
186
- * @param args - arguments after the executable.
270
+ * @param args - arguments after the executable and the invocation config.
187
271
  * @param signal - the caller's cancellation.
272
+ * @param maxBytes - the caller's own output bound, when it has one.
273
+ * @param timeoutMs - the caller's own wall-clock bound, when it has one.
188
274
  * @returns the finished command.
189
275
  */
190
276
  private git;
@@ -17,7 +17,8 @@
17
17
  import { Context } from '@deepseek-ai/cordis';
18
18
  import z from '@deepseek-ai/schemastery';
19
19
  import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
20
- import type { AdvancedSidebarSettings, AdvancedSidebarView, DeleteSessionRequest, DeleteSessionResult, GitCommitMessageRequest, GitCommitMessageResult, GitCommitRequest, GitCommitResult, GitDiffRequest, GitDiffResult, GitPushRequest, GitPushResult, GitStageRequest, GitStageResult, GitStatusRequest, GitStatusResult, ListEntriesRequest, ListEntriesResult, OpenInRequest, OpenInResult, PreviewListRequest, PreviewListResult, PreviewLogsRequest, PreviewLogsResult, PreviewStartRequest, PreviewStartResult, PreviewStopRequest, PreviewStopResult, ReadFileRequest, ReadFileResult, TaskKillRequest, TaskKillResult, TaskOutputRequest, TaskOutputResult, TerminalAckResult, TerminalCloseRequest, TerminalOpenRequest, TerminalOpenResult, TerminalReadRequest, TerminalReadResult, TerminalSignalRequest, TerminalWriteRequest } from './types.ts';
20
+ import type { PreviewCommandBody } from './ui-preview-tool.ts';
21
+ import type { AdvancedSidebarSettings, AdvancedSidebarView, DeleteSessionRequest, DeleteSessionResult, GitCommitMessageRequest, GitCommitMessageResult, GitCommitRequest, GitCommitResult, GitDiffRequest, GitDiffResult, GitPushRequest, GitPushResult, GitStageRequest, GitStageResult, GitStatusRequest, GitStatusResult, ListEntriesRequest, ListEntriesResult, OpenInRequest, OpenInResult, PreviewCommandResult, PreviewFileInfoRequest, PreviewFileInfoResult, PreviewFileKind, PreviewListRequest, PreviewListResult, PreviewLogsRequest, PreviewLogsResult, PreviewPollRequest, PreviewPollResult, PreviewReleaseRequest, PreviewReleaseResult, PreviewResultAck, PreviewResultRequest, PreviewStartRequest, PreviewStartResult, PreviewStopRequest, PreviewStopResult, ReadFileRequest, ReadFileResult, TaskKillRequest, TaskKillResult, TaskOutputRequest, TaskOutputResult, TerminalAckResult, TerminalCloseRequest, TerminalOpenRequest, TerminalOpenResult, TerminalReadRequest, TerminalReadResult, TerminalSignalRequest, TerminalWriteRequest } from './types.ts';
21
22
  export type * from './types.ts';
22
23
  export { REVEAL_TARGET_ID } from './open-in.ts';
23
24
  /**
@@ -27,7 +28,7 @@ export { REVEAL_TARGET_ID } from './open-in.ts';
27
28
  * Host validates, while a Remote namespace is read as `ctx.remote.advancedSidebar.…` and so must be
28
29
  * an identifier.
29
30
  */
30
- export declare const ADVANCED_SIDEBAR_SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
31
+ export declare const ADVANCED_SIDEBAR_SETTINGS_NAMESPACE: "advanced-sidebar";
31
32
  /** Deployment configuration for the advanced sidebar; the `advanced-sidebar` section's own shape. */
32
33
  export type Config = AdvancedSidebarSettings;
33
34
  declare module '@deepseek-ai/cordis' {
@@ -47,6 +48,8 @@ export declare class AdvancedSidebarService extends TypertRemoteService {
47
48
  private readonly tasks;
48
49
  private readonly deleter;
49
50
  private readonly preview;
51
+ private readonly surface;
52
+ private readonly bindings;
50
53
  /**
51
54
  * @param ctx - Host context; every capability this service uses is resolved optionally, so a
52
55
  * deployment missing one still serves a view that explains which panel is dark and why.
@@ -173,6 +176,38 @@ export declare class AdvancedSidebarService extends TypertRemoteService {
173
176
  * @returns the delta and the state, or a classified failure.
174
177
  */
175
178
  previewLogs(request: PreviewLogsRequest): Promise<PreviewLogsResult>;
179
+ /**
180
+ * Describe one workspace file for the Preview panel's Files mode.
181
+ *
182
+ * Separate from `readFile`, which returns text for the Files panel's reader: a preview needs the
183
+ * kind, the size, the same-origin URL, and a change token, and it must not pull a 200 MB video
184
+ * through the wire to find out what it is.
185
+ * @param request - the workspace and the file inside it.
186
+ * @param signal - gateway-supplied cancellation for the resolution and metadata reads.
187
+ * @returns the file's kind and frame URL, or a classified failure.
188
+ */
189
+ previewFileInfo(request: PreviewFileInfoRequest, signal: AbortSignal): Promise<PreviewFileInfoResult>;
190
+ /**
191
+ * Register one Preview panel and take whatever the agent queued for it.
192
+ *
193
+ * This is the polling half of the agent channel: the browser calls it while a preview is mounted,
194
+ * the call is the panel's liveness heartbeat, and its answer carries the commands to execute.
195
+ * @param request - which panel, where it is, and whether a preview is actually rendered.
196
+ * @returns the work to do, or a classified failure.
197
+ */
198
+ previewPoll(request: PreviewPollRequest): Promise<PreviewPollResult>;
199
+ /**
200
+ * Record what one command did.
201
+ * @param request - the panel, the command id, and the outcome.
202
+ * @returns settlement.
203
+ */
204
+ previewResult(request: PreviewResultRequest): Promise<PreviewResultAck>;
205
+ /**
206
+ * Say that one panel is gone, so its queued work is dropped and its waits fail now.
207
+ * @param request - the panel that closed.
208
+ * @returns settlement.
209
+ */
210
+ previewRelease(request: PreviewReleaseRequest): Promise<PreviewReleaseResult>;
176
211
  /**
177
212
  * Read one file for the Files panel preview.
178
213
  * @param request - the file and the workspace it must stay inside.
@@ -200,11 +235,68 @@ export declare class AdvancedSidebarService extends TypertRemoteService {
200
235
  */
201
236
  taskOutput(request: TaskOutputRequest): Promise<TaskOutputResult>;
202
237
  /**
203
- * Delete one session: archive it, and remove its durable artifact when the mode and Host allow.
238
+ * Delete one session: archive it, and report plainly that a configured purge did not happen.
204
239
  * @param request - the session to delete.
205
- * @param signal - gateway-supplied cancellation for the persistence listing.
206
240
  * @returns what was actually done, or a classified failure.
207
241
  */
208
- deleteSession(request: DeleteSessionRequest, signal: AbortSignal): Promise<DeleteSessionResult>;
242
+ deleteSession(request: DeleteSessionRequest): Promise<DeleteSessionResult>;
243
+ /**
244
+ * Queue one command against the session's Preview panel and wait for its answer.
245
+ * @param sessionId - the session whose panel should execute it.
246
+ * @param body - the command, without its id, panel, or deadline.
247
+ * @returns the result, or a sentence explaining why there is none.
248
+ */
249
+ queueCommand(sessionId: string, body: PreviewCommandBody): Promise<{
250
+ ok: true;
251
+ result: PreviewCommandResult;
252
+ } | {
253
+ ok: false;
254
+ message: string;
255
+ }>;
256
+ /**
257
+ * Point the session's panel at a URL.
258
+ *
259
+ * The panel decides for itself how to frame it — a loopback URL goes through this Host's proxy and
260
+ * becomes inspectable, anything else is framed cross-origin and is not — so the answer says which
261
+ * happened rather than the tool guessing.
262
+ * @param sessionId - the session whose panel should show it.
263
+ * @param url - the absolute `http(s)` URL, already validated by the caller.
264
+ * @param waitMs - how long the panel may take to mount and load.
265
+ * @returns the outcome and what the panel is now framing.
266
+ */
267
+ openUrl(sessionId: string, url: string, waitMs: number): Promise<{
268
+ ok: true;
269
+ message: string;
270
+ detail: Record<string, unknown>;
271
+ } | {
272
+ ok: false;
273
+ message: string;
274
+ }>;
275
+ /**
276
+ * Point the session's panel at one workspace file.
277
+ * @param request - the session, the workspace, the file, its kind, and the wait budget.
278
+ * @returns the outcome and where the panel is now framed from.
279
+ */
280
+ openFile(request: {
281
+ sessionId: string;
282
+ workspacePath: string;
283
+ filePath: string;
284
+ kind: PreviewFileKind;
285
+ waitMs: number;
286
+ }): Promise<{
287
+ ok: true;
288
+ message: string;
289
+ detail: Record<string, unknown>;
290
+ } | {
291
+ ok: false;
292
+ message: string;
293
+ }>;
294
+ /**
295
+ * Read one workspace file's preview description, for the tool's own `open` validation.
296
+ * @param request - the workspace and the file.
297
+ * @param signal - cancellation for the reads.
298
+ * @returns the description, or a classified failure.
299
+ */
300
+ describeFile(request: PreviewFileInfoRequest, signal?: AbortSignal): Promise<PreviewFileInfoResult>;
209
301
  }
210
302
  export default AdvancedSidebarService;