@steerable/agent-shell 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +91 -0
- package/contracts/tool-contract.json +326 -0
- package/dist/attachments.d.ts +41 -0
- package/dist/attachments.js +147 -0
- package/dist/brand.d.ts +24 -0
- package/dist/brand.js +92 -0
- package/dist/host/http-routes.d.ts +21 -0
- package/dist/host/http-routes.js +55 -0
- package/dist/host/ipc.d.ts +11 -0
- package/dist/host/ipc.js +20 -0
- package/dist/host/pack-assembly.d.ts +86 -0
- package/dist/host/pack-assembly.js +32 -0
- package/dist/host/runtime.d.ts +69 -0
- package/dist/host/runtime.js +207 -0
- package/dist/host/visible-terminal-exec.d.ts +21 -0
- package/dist/host/visible-terminal-exec.js +151 -0
- package/dist/hosted-web-search.d.ts +13 -0
- package/dist/hosted-web-search.js +82 -0
- package/dist/image-attachment.d.ts +39 -0
- package/dist/image-attachment.js +133 -0
- package/dist/insights/flush.d.ts +10 -0
- package/dist/insights/flush.js +139 -0
- package/dist/insights/record.d.ts +17 -0
- package/dist/insights/record.js +44 -0
- package/dist/json-store.d.ts +9 -0
- package/dist/json-store.js +22 -0
- package/dist/llm/index.d.ts +23 -0
- package/dist/llm/index.js +105 -0
- package/dist/llm/ollama.d.ts +23 -0
- package/dist/llm/ollama.js +242 -0
- package/dist/llm/openai-compat.d.ts +20 -0
- package/dist/llm/openai-compat.js +199 -0
- package/dist/llm/sidecar-provider.d.ts +37 -0
- package/dist/llm/sidecar-provider.js +163 -0
- package/dist/llm/tool-choice.d.ts +35 -0
- package/dist/llm/tool-choice.js +85 -0
- package/dist/llm/types.d.ts +122 -0
- package/dist/llm/types.js +1 -0
- package/dist/local-backend/agent-capability.d.ts +101 -0
- package/dist/local-backend/agent-capability.js +174 -0
- package/dist/local-backend/ai-title.d.ts +43 -0
- package/dist/local-backend/ai-title.js +173 -0
- package/dist/local-backend/auto-continue-helper.d.ts +80 -0
- package/dist/local-backend/auto-continue-helper.js +83 -0
- package/dist/local-backend/branch-helper.d.ts +24 -0
- package/dist/local-backend/branch-helper.js +27 -0
- package/dist/local-backend/context-compactor.d.ts +81 -0
- package/dist/local-backend/context-compactor.js +213 -0
- package/dist/local-backend/coreloop-stream.d.ts +245 -0
- package/dist/local-backend/coreloop-stream.js +277 -0
- package/dist/local-backend/deferred-detector.d.ts +15 -0
- package/dist/local-backend/deferred-detector.js +124 -0
- package/dist/local-backend/history-helper.d.ts +30 -0
- package/dist/local-backend/history-helper.js +34 -0
- package/dist/local-backend/interrupted-helper.d.ts +29 -0
- package/dist/local-backend/interrupted-helper.js +25 -0
- package/dist/local-backend/live-stream.d.ts +36 -0
- package/dist/local-backend/live-stream.js +23 -0
- package/dist/local-backend/llm-diagnose.d.ts +37 -0
- package/dist/local-backend/llm-diagnose.js +284 -0
- package/dist/local-backend/message-triggers.d.ts +27 -0
- package/dist/local-backend/message-triggers.js +67 -0
- package/dist/local-backend/pack-backend-routes.d.ts +31 -0
- package/dist/local-backend/pack-backend-routes.js +62 -0
- package/dist/local-backend/pack-turn-hooks.d.ts +37 -0
- package/dist/local-backend/pack-turn-hooks.js +67 -0
- package/dist/local-backend/prompt-builder.d.ts +101 -0
- package/dist/local-backend/prompt-builder.js +246 -0
- package/dist/local-backend/regenerate-helper.d.ts +62 -0
- package/dist/local-backend/regenerate-helper.js +75 -0
- package/dist/local-backend/router.d.ts +175 -0
- package/dist/local-backend/router.js +3139 -0
- package/dist/local-backend/skill-install.d.ts +19 -0
- package/dist/local-backend/skill-install.js +71 -0
- package/dist/local-backend/skill-loader.d.ts +92 -0
- package/dist/local-backend/skill-loader.js +146 -0
- package/dist/local-backend/skills/00-identity/SKILL.md +32 -0
- package/dist/local-backend/skills/10-goal/SKILL.md +59 -0
- package/dist/local-backend/skills/11-loop/SKILL.md +71 -0
- package/dist/local-backend/skills/12-create-skill/SKILL.md +88 -0
- package/dist/local-backend/skills/70-plan-mode/SKILL.md +58 -0
- package/dist/local-backend/skills/80-tool-usage/SKILL.md +70 -0
- package/dist/local-backend/skills/81-anti-deferred/SKILL.md +53 -0
- package/dist/local-backend/skills/82-data-grounding/SKILL.md +56 -0
- package/dist/local-backend/skills/85-local-exec/SKILL.md +86 -0
- package/dist/local-backend/skills/86-proactive-coding/SKILL.md +51 -0
- package/dist/local-backend/subagent-profiles.d.ts +30 -0
- package/dist/local-backend/subagent-profiles.js +74 -0
- package/dist/local-backend/task-process.d.ts +12 -0
- package/dist/local-backend/task-process.js +176 -0
- package/dist/local-backend/task-service.d.ts +135 -0
- package/dist/local-backend/task-service.js +565 -0
- package/dist/local-backend/turn-duration.d.ts +2 -0
- package/dist/local-backend/turn-duration.js +9 -0
- package/dist/local-backend/turn-timeline.d.ts +16 -0
- package/dist/local-backend/turn-timeline.js +42 -0
- package/dist/local-backend/worktree-service.d.ts +84 -0
- package/dist/local-backend/worktree-service.js +243 -0
- package/dist/local-edit.d.ts +48 -0
- package/dist/local-edit.js +44 -0
- package/dist/local-executor.d.ts +255 -0
- package/dist/local-executor.js +881 -0
- package/dist/local-script-registry.d.ts +28 -0
- package/dist/local-script-registry.js +63 -0
- package/dist/log.d.ts +13 -0
- package/dist/log.js +12 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +855 -0
- package/dist/mcp-executor.d.ts +45 -0
- package/dist/mcp-executor.js +241 -0
- package/dist/mcp-server-registry.d.ts +104 -0
- package/dist/mcp-server-registry.js +234 -0
- package/dist/preload-default.d.ts +1 -0
- package/dist/preload-default.js +9 -0
- package/dist/preload.cjs +395 -0
- package/dist/preload.d.ts +20 -0
- package/dist/preload.js +411 -0
- package/dist/product-config.d.ts +43 -0
- package/dist/product-config.js +26 -0
- package/dist/project-registry.d.ts +55 -0
- package/dist/project-registry.js +106 -0
- package/dist/project-rules.d.ts +15 -0
- package/dist/project-rules.js +102 -0
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.js +217 -0
- package/dist/scenario/pack.d.ts +8 -0
- package/dist/scenario/pack.js +1 -0
- package/dist/scenario/registry.d.ts +24 -0
- package/dist/scenario/registry.js +31 -0
- package/dist/server/http-server.d.ts +39 -0
- package/dist/server/http-server.js +361 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +107 -0
- package/dist/server/sse-bus.d.ts +14 -0
- package/dist/server/sse-bus.js +31 -0
- package/dist/shell-adapt.d.ts +21 -0
- package/dist/shell-adapt.js +104 -0
- package/dist/sidecar/boot.d.ts +36 -0
- package/dist/sidecar/boot.js +343 -0
- package/dist/sidecar/egress-hint.d.ts +15 -0
- package/dist/sidecar/egress-hint.js +46 -0
- package/dist/sidecar/egress-proxy.d.ts +183 -0
- package/dist/sidecar/egress-proxy.js +419 -0
- package/dist/sidecar/errors.d.ts +22 -0
- package/dist/sidecar/errors.js +38 -0
- package/dist/sidecar/exec-sandbox.d.ts +48 -0
- package/dist/sidecar/exec-sandbox.js +94 -0
- package/dist/sidecar/handle.d.ts +32 -0
- package/dist/sidecar/handle.js +53 -0
- package/dist/sidecar/index.d.ts +14 -0
- package/dist/sidecar/index.js +13 -0
- package/dist/sidecar/proxy-detect.d.ts +50 -0
- package/dist/sidecar/proxy-detect.js +182 -0
- package/dist/sidecar/reverse-approval.d.ts +55 -0
- package/dist/sidecar/reverse-approval.js +86 -0
- package/dist/sidecar/reverse-ask-user.d.ts +34 -0
- package/dist/sidecar/reverse-ask-user.js +59 -0
- package/dist/sidecar/reverse-spawn.d.ts +19 -0
- package/dist/sidecar/reverse-spawn.js +161 -0
- package/dist/sidecar/reverse-tools.d.ts +29 -0
- package/dist/sidecar/reverse-tools.js +106 -0
- package/dist/sidecar/safety-patterns.d.ts +41 -0
- package/dist/sidecar/safety-patterns.js +157 -0
- package/dist/sidecar/storage-path.d.ts +14 -0
- package/dist/sidecar/storage-path.js +35 -0
- package/dist/sidecar/supervisor.d.ts +218 -0
- package/dist/sidecar/supervisor.js +932 -0
- package/dist/sidecar/types.d.ts +601 -0
- package/dist/sidecar/types.js +1 -0
- package/dist/single-instance.d.ts +11 -0
- package/dist/single-instance.js +21 -0
- package/dist/storage/empty-chats.d.ts +9 -0
- package/dist/storage/empty-chats.js +16 -0
- package/dist/storage/index.d.ts +373 -0
- package/dist/storage/index.js +1158 -0
- package/dist/storage/insights-redact.d.ts +2 -0
- package/dist/storage/insights-redact.js +30 -0
- package/dist/storage/insights-settings.d.ts +53 -0
- package/dist/storage/insights-settings.js +92 -0
- package/dist/storage/llm-settings.d.ts +120 -0
- package/dist/storage/llm-settings.js +233 -0
- package/dist/storage/local-store-singleton.d.ts +28 -0
- package/dist/storage/local-store-singleton.js +38 -0
- package/dist/storage/message-order.d.ts +25 -0
- package/dist/storage/message-order.js +27 -0
- package/dist/storage/pack-migrations.d.ts +22 -0
- package/dist/storage/pack-migrations.js +24 -0
- package/dist/storage/pack-seeds.d.ts +36 -0
- package/dist/storage/pack-seeds.js +42 -0
- package/dist/storage/telemetry-settings.d.ts +38 -0
- package/dist/storage/telemetry-settings.js +59 -0
- package/dist/storage/usage-summary.d.ts +55 -0
- package/dist/storage/usage-summary.js +38 -0
- package/dist/storage/web-search-settings.d.ts +38 -0
- package/dist/storage/web-search-settings.js +74 -0
- package/dist/storage/write-lease.d.ts +26 -0
- package/dist/storage/write-lease.js +74 -0
- package/dist/terminal-manager.d.ts +83 -0
- package/dist/terminal-manager.js +506 -0
- package/dist/tool-router.d.ts +228 -0
- package/dist/tool-router.js +930 -0
- package/dist/tool-search-rank.d.ts +42 -0
- package/dist/tool-search-rank.js +96 -0
- package/package.json +67 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
import type { SidecarHealth, ToolResult } from '@steerable/agent-protocol';
|
|
2
|
+
export type SidecarHealthSnapshot = SidecarHealth;
|
|
3
|
+
export type SidecarToolResult = ToolResult;
|
|
4
|
+
/**
|
|
5
|
+
* W4-3: layer-1 (sidecar process sandbox) posture, recorded by the
|
|
6
|
+
* supervisor at spawn-plan time. `enforcement` mirrors the layer-3
|
|
7
|
+
* `_sandbox.enforcement` vocabulary (`full | partial | none`) so both
|
|
8
|
+
* layers read consistently; layer 1 never reports `full` because Seatbelt
|
|
9
|
+
* egress on remote hosts is port-only (docs/spec/safety.md). `reason`
|
|
10
|
+
* distinguishes an explicit opt-out (`disabled_by_option` /
|
|
11
|
+
* `disabled_by_env`) from a refused start
|
|
12
|
+
* (`platform_unsupported` / `seatbelt_missing` / `profile_failed` /
|
|
13
|
+
* `wrap_failed` / `helper_missing`) — confinement requested means the
|
|
14
|
+
* process does not run unsandboxed. The settings UI warns on the latter.
|
|
15
|
+
*/
|
|
16
|
+
export interface SidecarSandboxPosture {
|
|
17
|
+
backend: 'seatbelt' | 'bwrap' | 'landlock' | 'windows-restricted-token' | 'none';
|
|
18
|
+
enforcement: 'partial' | 'none';
|
|
19
|
+
reason: 'active' | 'disabled_by_option' | 'disabled_by_env' | 'platform_unsupported' | 'seatbelt_missing' | 'profile_failed' | 'wrap_failed' | 'helper_missing';
|
|
20
|
+
}
|
|
21
|
+
export interface SidecarStartOptions {
|
|
22
|
+
/** Override the python binary; defaults to the bundled portable runtime. */
|
|
23
|
+
pythonExecutable?: string;
|
|
24
|
+
/** Override the entrypoint module; defaults to ``steerable_sidecar``. */
|
|
25
|
+
entryModule?: string;
|
|
26
|
+
/** Extra arguments appended after ``-m <entryModule>``. */
|
|
27
|
+
args?: string[];
|
|
28
|
+
/** Cwd for the spawned process. */
|
|
29
|
+
cwd?: string;
|
|
30
|
+
/** Environment variables to inject. */
|
|
31
|
+
env?: NodeJS.ProcessEnv;
|
|
32
|
+
/** Max ms to wait for the ready handshake before failing. Default 15000. */
|
|
33
|
+
bootTimeoutMs?: number;
|
|
34
|
+
/** ms between health pings. Default 5000. Set <=0 to disable. */
|
|
35
|
+
healthIntervalMs?: number;
|
|
36
|
+
/** consecutive ping failures that trigger an automatic restart. Default 3. */
|
|
37
|
+
restartAfterFailedPings?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Confine the sidecar in an OS sandbox. macOS: Seatbelt. Linux: bwrap
|
|
40
|
+
* then Landlock wrapping the python process. Windows: win-spawn-helper
|
|
41
|
+
* `--passthrough` (restricted token + Job Object) with inherited stdio.
|
|
42
|
+
* If confinement cannot be applied, start() refuses rather than spawning
|
|
43
|
+
* unsandboxed. `false` or `STEERABLE_SIDECAR_SANDBOX=0` is the only
|
|
44
|
+
* unsandboxed path. Default ON.
|
|
45
|
+
*/
|
|
46
|
+
sandbox?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Egress allow-list for the sandboxed sidecar (entries `host` or
|
|
49
|
+
* `host:port`; bare hosts allow 443+80). Once any entry is given the
|
|
50
|
+
* profile fails closed: outbound is denied except to the declared
|
|
51
|
+
* endpoints. Unset/empty keeps outbound fully open (the default).
|
|
52
|
+
* Seatbelt cannot match hostnames — localhost entries pin
|
|
53
|
+
* `localhost:PORT` exactly, remote entries degrade to their port.
|
|
54
|
+
* Only consulted when `sandbox` is on. Defaults to the
|
|
55
|
+
* `STEERABLE_SIDECAR_SANDBOX_ALLOWED_HOSTS` env (comma-separated).
|
|
56
|
+
*/
|
|
57
|
+
sandboxAllowedHosts?: string[];
|
|
58
|
+
/**
|
|
59
|
+
* Also allow what the network-read tools (`web_fetch`/`web_search`) need on
|
|
60
|
+
* top of the allow-list: the system resolver plus outbound http(s) to any
|
|
61
|
+
* host. Their targets are whatever the model asks for, so no host list can
|
|
62
|
+
* name them ahead of time — without this every fetch fails name resolution
|
|
63
|
+
* inside the SSRF pre-check. Set it only where those tools are offered; it
|
|
64
|
+
* grants reach to any host on ports 80 and 443. No effect when the
|
|
65
|
+
* allow-list is unset (outbound already open) or `sandbox` is off.
|
|
66
|
+
*/
|
|
67
|
+
sandboxWebEgress?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Allow name resolution (the system resolver socket — no IP reach) on top
|
|
70
|
+
* of a fail-closed egress allow-list. Set when egress is pinned to the
|
|
71
|
+
* per-host proxy and the web tools stay offered: `web_fetch`'s SSRF
|
|
72
|
+
* pre-check resolves locally even though the fetch itself tunnels through
|
|
73
|
+
* the proxy. No effect when the allow-list is unset (outbound already
|
|
74
|
+
* open) or `sandboxWebEgress` is on (its profile already grants the
|
|
75
|
+
* resolver). macOS Seatbelt only.
|
|
76
|
+
*/
|
|
77
|
+
sandboxAllowResolver?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Extra directories the confined sidecar may write to, beyond the default
|
|
80
|
+
* `~/.steerable` root. Each entry is forwarded to the platform sandbox as
|
|
81
|
+
* an additional writable root (macOS Seatbelt `--writable-root`, Linux
|
|
82
|
+
* bwrap/Landlock `--writable-root`, Windows win-spawn-helper
|
|
83
|
+
* `--writable-root`). Needed whenever `--storage-path` (or any other
|
|
84
|
+
* sidecar-written file) lives outside `~/.steerable` — e.g. tests keeping
|
|
85
|
+
* sessions.db in a temp dir, or BS mode with `DEEPPATH_USER_DATA_DIR`.
|
|
86
|
+
*/
|
|
87
|
+
sandboxWritableRoots?: string[];
|
|
88
|
+
/** Optional hook invoked whenever the sidecar pushes a stream notification. */
|
|
89
|
+
onStreamChunk?: (params: unknown) => void;
|
|
90
|
+
/** Optional hook for log lines emitted on stderr. */
|
|
91
|
+
onLogLine?: (line: string) => void;
|
|
92
|
+
}
|
|
93
|
+
export interface SidecarMethodOptions {
|
|
94
|
+
/** Per-call timeout in ms. Default 60_000. */
|
|
95
|
+
timeoutMs?: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A request the sidecar sends *to* the host over the reverse channel
|
|
99
|
+
* (see spec/sidecar/README.md "Reverse channel"). Distinguished from a
|
|
100
|
+
* response by the presence of both `id` and `method`.
|
|
101
|
+
*/
|
|
102
|
+
export interface SidecarReverseRequest {
|
|
103
|
+
/** Reverse-call id; sidecar uses `srv_`-prefixed strings. */
|
|
104
|
+
id: string;
|
|
105
|
+
method: string;
|
|
106
|
+
params?: unknown;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Host-side handler for a reverse request. Receives the params and returns
|
|
110
|
+
* the `result` payload to send back, or throws to return an error.
|
|
111
|
+
*/
|
|
112
|
+
export type SidecarReverseHandler = (params: unknown) => Promise<unknown> | unknown;
|
|
113
|
+
/** W5-2: `agent.session.fork` result — the created branch point. */
|
|
114
|
+
export interface SidecarSessionForkResult {
|
|
115
|
+
recordId: string;
|
|
116
|
+
sourceRecordId: string | null;
|
|
117
|
+
sourceUntilSeq: number | null;
|
|
118
|
+
label: string;
|
|
119
|
+
seedMessages: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Result of an `agent.session.fork` attempt.
|
|
123
|
+
*
|
|
124
|
+
* `declined` means the sidecar answered and rejected the address — no record, or
|
|
125
|
+
* a fork point it will not split, such as an ordinal inside a branch seed, where
|
|
126
|
+
* the protocol documents host fallback as the expected path. Anything else is a
|
|
127
|
+
* fork that was supposed to happen and did not (transport, timeout, fault), and
|
|
128
|
+
* the caller must not treat the two alike: only the first is a path the
|
|
129
|
+
* framework sanctions.
|
|
130
|
+
*/
|
|
131
|
+
export type SidecarSessionForkOutcome = {
|
|
132
|
+
ok: true;
|
|
133
|
+
fork: SidecarSessionForkResult;
|
|
134
|
+
} | {
|
|
135
|
+
ok: false;
|
|
136
|
+
declined: boolean;
|
|
137
|
+
reason: string;
|
|
138
|
+
};
|
|
139
|
+
/** W1.2.1: one node in an `agent.session.branches` lineage/children list. */
|
|
140
|
+
export interface SidecarBranchPoint {
|
|
141
|
+
recordId: string;
|
|
142
|
+
sourceRecordId: string | null;
|
|
143
|
+
sourceUntilSeq: number | null;
|
|
144
|
+
label: string;
|
|
145
|
+
depth?: number;
|
|
146
|
+
}
|
|
147
|
+
/** W1.2.1: `agent.session.branches` result. */
|
|
148
|
+
export interface SidecarSessionBranches {
|
|
149
|
+
lineage: SidecarBranchPoint[];
|
|
150
|
+
children: SidecarBranchPoint[];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Session tree: one node in an `agent.session.tree` family tree
|
|
154
|
+
* (recursive). Same fields as {@link SidecarBranchPoint} plus nested
|
|
155
|
+
* `children`; `depth` is 0 on the family root and matches lineage
|
|
156
|
+
* numbering along the chain.
|
|
157
|
+
*/
|
|
158
|
+
export interface SidecarSessionTreeNode {
|
|
159
|
+
recordId: string;
|
|
160
|
+
sourceRecordId: string | null;
|
|
161
|
+
sourceUntilSeq: number | null;
|
|
162
|
+
label: string;
|
|
163
|
+
depth: number;
|
|
164
|
+
children: SidecarSessionTreeNode[];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Session tree: `agent.session.tree` result — the full branch family
|
|
168
|
+
* containing the queried record, expanded from the family root.
|
|
169
|
+
* `truncated` means the sidecar's safety bounds (depth 32 / 500 nodes)
|
|
170
|
+
* cut the expansion; the tree is still valid below the cut.
|
|
171
|
+
*/
|
|
172
|
+
export interface SidecarSessionTree {
|
|
173
|
+
recordId: string;
|
|
174
|
+
tree: SidecarSessionTreeNode;
|
|
175
|
+
nodeCount: number;
|
|
176
|
+
truncated: boolean;
|
|
177
|
+
}
|
|
178
|
+
/** W1.2.1: `agent.session.messages` result — the projected visible span. */
|
|
179
|
+
export interface SidecarSessionMessages {
|
|
180
|
+
recordId: string;
|
|
181
|
+
messages: Array<{
|
|
182
|
+
seq: number;
|
|
183
|
+
role: string;
|
|
184
|
+
content: string;
|
|
185
|
+
}>;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* W6-1: `workspace.apply_edits` result — the pure structured-edit algorithm
|
|
189
|
+
* run in the sidecar on host-supplied content. The host owns all file I/O.
|
|
190
|
+
*/
|
|
191
|
+
export interface SidecarApplyEditsResult {
|
|
192
|
+
content: string;
|
|
193
|
+
diff: string;
|
|
194
|
+
applied: number;
|
|
195
|
+
matches: Array<{
|
|
196
|
+
level: 'exact' | 'trim' | 'unicode';
|
|
197
|
+
startLine: number;
|
|
198
|
+
oldLineCount: number;
|
|
199
|
+
}>;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* W6-7/skills: `skills.list` result item — a parsed SKILL.md module, mirroring
|
|
203
|
+
* the desktop's `SkillModule`. Parsing is single-sourced in the framework's
|
|
204
|
+
* `skills.py`; the desktop no longer re-parses frontmatter.
|
|
205
|
+
*/
|
|
206
|
+
export interface SidecarSkillModule {
|
|
207
|
+
name: string;
|
|
208
|
+
displayName: string;
|
|
209
|
+
description: string;
|
|
210
|
+
priority: number;
|
|
211
|
+
tags: string[];
|
|
212
|
+
conditions: string[];
|
|
213
|
+
match: 'any' | 'all';
|
|
214
|
+
layer: 'eager' | 'catalog';
|
|
215
|
+
modelInvocable: boolean;
|
|
216
|
+
content: string;
|
|
217
|
+
dirName: string;
|
|
218
|
+
skillsDir: string;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Wire shape for ``agent.chat.stream`` requests, mirrored from
|
|
222
|
+
* ``packages/sidecar/py/src/steerable_sidecar/sidecar.py``.
|
|
223
|
+
*/
|
|
224
|
+
export interface SidecarChatStreamRequest {
|
|
225
|
+
provider: string;
|
|
226
|
+
model: string;
|
|
227
|
+
messages: Array<{
|
|
228
|
+
role: string;
|
|
229
|
+
content: string;
|
|
230
|
+
name?: string;
|
|
231
|
+
toolCallId?: string;
|
|
232
|
+
/**
|
|
233
|
+
* Assistant 消息的工具调用回传:缺失时下一轮里的 `role:'tool'` 消息会
|
|
234
|
+
* 被 OpenAI 严格协议判为孤儿(400 "must be a response to a preceding
|
|
235
|
+
* message with 'tool_calls'")。与 in-process openai-compat.ts 的
|
|
236
|
+
* mapMessage 同一约束。
|
|
237
|
+
*/
|
|
238
|
+
toolCalls?: Array<{
|
|
239
|
+
id: string;
|
|
240
|
+
name: string;
|
|
241
|
+
arguments: Record<string, unknown>;
|
|
242
|
+
}>;
|
|
243
|
+
/**
|
|
244
|
+
* Thinking 模型的推理回传(DeepSeek thinking 模式强制要求,缺了第二轮
|
|
245
|
+
* 400)。sidecar 侧按 compat.reasoningEchoField 落到正确字段名。
|
|
246
|
+
*/
|
|
247
|
+
reasoningContent?: string;
|
|
248
|
+
/**
|
|
249
|
+
* W6-3: structured content parts (multimodal). When present the sidecar
|
|
250
|
+
* treats it as authoritative over `content` (which then is just the text
|
|
251
|
+
* projection). Mirrors `spec/chat/ContentPart.schema.json`.
|
|
252
|
+
*/
|
|
253
|
+
parts?: Array<{
|
|
254
|
+
type: 'text';
|
|
255
|
+
text: string;
|
|
256
|
+
} | {
|
|
257
|
+
type: 'image';
|
|
258
|
+
data?: string;
|
|
259
|
+
url?: string;
|
|
260
|
+
mediaType?: string;
|
|
261
|
+
}>;
|
|
262
|
+
}>;
|
|
263
|
+
baseUrl?: string;
|
|
264
|
+
apiKey?: string;
|
|
265
|
+
temperature?: number;
|
|
266
|
+
/** W2.8.2: the host-assembled system prompt as a typed fragment (the
|
|
267
|
+
* sidecar enforces its token cap at the seed boundary). Mutually
|
|
268
|
+
* exclusive with a leading `system` message in `messages` — the sidecar
|
|
269
|
+
* rejects both-present as a host bug. */
|
|
270
|
+
systemPrompt?: string;
|
|
271
|
+
/** W1.3.2 explicit OpenAI-compat flag overrides (camelCase wire keys owned
|
|
272
|
+
* by the framework's `OpenAICompatFlags.from_dict`; unknown keys fail loud
|
|
273
|
+
* there). Omitted → the sidecar auto-detects from the base-URL host. */
|
|
274
|
+
compat?: Record<string, unknown>;
|
|
275
|
+
/** Provider-preset choice (framework `llm.presets`): omitted/{"enabled":
|
|
276
|
+
* true} → auto-match on base-URL+model; {"enabled": false} → layer off;
|
|
277
|
+
* {"override": {...}} → pinned preset (camelCase keys owned by the
|
|
278
|
+
* framework's `ProviderPreset.from_dict`; unknown keys fail loud there). */
|
|
279
|
+
presets?: Record<string, unknown>;
|
|
280
|
+
maxTokens?: number;
|
|
281
|
+
/**
|
|
282
|
+
* Per-turn reasoning effort from the host's model picker. The sidecar
|
|
283
|
+
* validates it strict against the resolved catalog entry (a level the
|
|
284
|
+
* model cannot honor is an `invalid_params` RPC error at stream start,
|
|
285
|
+
* never a silently dropped field); omitted → env/preset defaults apply.
|
|
286
|
+
*/
|
|
287
|
+
reasoningEffort?: string;
|
|
288
|
+
tools?: unknown[];
|
|
289
|
+
streamId?: string;
|
|
290
|
+
providerOptions?: Record<string, unknown>;
|
|
291
|
+
/** Per-start RPC timeout (NOT per-chunk). Default 30_000ms. */
|
|
292
|
+
startTimeoutMs?: number;
|
|
293
|
+
/** Route the stream through the sidecar-hosted Python CoreLoop (A4). */
|
|
294
|
+
useCoreLoop?: boolean;
|
|
295
|
+
/**
|
|
296
|
+
* W7-1: continue the durable record's interrupted turn instead of opening
|
|
297
|
+
* a new one. The sidecar substitutes the record's projected transcript
|
|
298
|
+
* (dangling tool_calls closed) as the loop seed; `messages` must be empty
|
|
299
|
+
* and `recordId` (or `chatId`) must name a non-empty record. CoreLoop-only.
|
|
300
|
+
*/
|
|
301
|
+
resume?: boolean;
|
|
302
|
+
/**
|
|
303
|
+
* Select which CoreLoop assistant text reaches the host. `all` streams
|
|
304
|
+
* every tool-round narration; `final` emits only the terminal tool-free
|
|
305
|
+
* response while preserving intermediate rounds in the record and trace.
|
|
306
|
+
*/
|
|
307
|
+
contentMode?: 'all' | 'final';
|
|
308
|
+
/**
|
|
309
|
+
* Forward every pre-digestion provider chunk (the loop's `on_stream_chunk`
|
|
310
|
+
* hook, before UI-tag stripping) as `stream.chunk` notifications carrying
|
|
311
|
+
* `rawChunk` — for hosts running incremental renderers. Default off: it
|
|
312
|
+
* costs one notification per chunk. CoreLoop-only.
|
|
313
|
+
*/
|
|
314
|
+
streamRawChunks?: boolean;
|
|
315
|
+
/** Execute every tool call on the host via the reverse channel. */
|
|
316
|
+
toolsViaHost?: boolean;
|
|
317
|
+
/**
|
|
318
|
+
* W8: advertise the sidecar-hosted `ask_user` tool (structured user
|
|
319
|
+
* questions). The host answers `ask_user.request` reverse calls with the
|
|
320
|
+
* renderer's question card; under `toolsViaHost` the sidecar intercepts
|
|
321
|
+
* `ask_user` locally so it never reaches the host's tool.invoke.
|
|
322
|
+
*/
|
|
323
|
+
askUser?: boolean;
|
|
324
|
+
/** Embedder context forwarded on every reverse tool.invoke (e.g. {mode}). */
|
|
325
|
+
toolContext?: Record<string, unknown>;
|
|
326
|
+
/** Enable the anti-hallucination hook layer (routing / deferred-claimed
|
|
327
|
+
* retry / grounding judge / narration). `true` or an options object. */
|
|
328
|
+
antiHallucination?: boolean | {
|
|
329
|
+
maxRetries?: number;
|
|
330
|
+
};
|
|
331
|
+
/** P3.1 multi-agent orchestration: OPT-IN advanced mode (off by default
|
|
332
|
+
* since the delegate-on-pool unification). Only `enabled: true` wraps the
|
|
333
|
+
* executor with the six-tool orchestration family (agent_spawn /
|
|
334
|
+
* agent_send / agent_wait / agent_close / agent_list / agent_interrupt).
|
|
335
|
+
* When delegation is also on (it is by default), both surfaces share one
|
|
336
|
+
* agent pool — one maxParallel budget, one lineage space. */
|
|
337
|
+
orchestration?: {
|
|
338
|
+
enabled?: boolean;
|
|
339
|
+
maxDepth?: number;
|
|
340
|
+
maxParallel?: number;
|
|
341
|
+
childMaxRounds?: number;
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Sub-agent delegation (delegate-on-pool). ON BY DEFAULT on the sidecar —
|
|
345
|
+
* pass `false` to disable, or an options object to configure. The model
|
|
346
|
+
* gets the single `delegate_subagent` tool; children run as pooled
|
|
347
|
+
* AgentPool runs emitting `agent.child` lifecycle notifications (the
|
|
348
|
+
* spawn payload carries the resolved `profile` name). `profiles` adds
|
|
349
|
+
* named subagent_type profiles (CC parity) with per-profile tool domains,
|
|
350
|
+
* models, round bounds, and concurrency.
|
|
351
|
+
*/
|
|
352
|
+
subagent?: boolean | {
|
|
353
|
+
toolFilter?: string[];
|
|
354
|
+
maxParallel?: number;
|
|
355
|
+
profiles?: Record<string, {
|
|
356
|
+
toolFilter?: string[];
|
|
357
|
+
model?: string;
|
|
358
|
+
maxRounds?: number;
|
|
359
|
+
concurrent?: boolean;
|
|
360
|
+
description?: string;
|
|
361
|
+
/** Profile system prompt, seeded as the child loop's first message
|
|
362
|
+
* (CC `.claude/agents` body parity). */
|
|
363
|
+
systemPrompt?: string;
|
|
364
|
+
}>;
|
|
365
|
+
};
|
|
366
|
+
/** A6 layered skill disclosure: the sidecar injects the catalog layer
|
|
367
|
+
* (first-round pre_step, recorded as a hook_action event) and answers
|
|
368
|
+
* `skill` tool calls with full bodies; the eager layer stays in the
|
|
369
|
+
* host-built system prompt. `mode: 'eager'` keeps everything host-side. */
|
|
370
|
+
skills?: {
|
|
371
|
+
roots: string[];
|
|
372
|
+
conditions?: string[];
|
|
373
|
+
exclude?: string[];
|
|
374
|
+
ignoreConditions?: boolean;
|
|
375
|
+
mode?: 'layered' | 'eager';
|
|
376
|
+
};
|
|
377
|
+
chatId?: string;
|
|
378
|
+
/** W5-2: the durable record this turn appends to. Defaults to `chatId`
|
|
379
|
+
* on the sidecar; after a regenerate-fork the chat's active record is the
|
|
380
|
+
* branch id, so the host must pass it explicitly. */
|
|
381
|
+
recordId?: string;
|
|
382
|
+
/** Wave 2 world-state sections: slow-changing host context (current
|
|
383
|
+
* time, timezone, …) as plain per-section JSON data. The sidecar injects
|
|
384
|
+
* it once as a `<world-state>` fragment; later turns diff against the
|
|
385
|
+
* snapshot embedded in the record — unchanged state costs zero tokens, a
|
|
386
|
+
* change costs one small RFC 7386 tail patch. */
|
|
387
|
+
worldState?: Record<string, unknown>;
|
|
388
|
+
/** CoreLoop tunables, mapped to LoopConfig on the sidecar. */
|
|
389
|
+
maxRounds?: number;
|
|
390
|
+
maxToolErrors?: number;
|
|
391
|
+
budgetTokens?: number;
|
|
392
|
+
softTimeoutMs?: number;
|
|
393
|
+
/** Per-tool-execution timeout (ms). On expiry the call returns a failed
|
|
394
|
+
* ToolResult (error `tool_timeout`) instead of hanging the turn. */
|
|
395
|
+
toolTimeoutMs?: number;
|
|
396
|
+
/**
|
|
397
|
+
* Wave 4 (W4-2): per-exec OS sandbox for shell/subprocess tool calls
|
|
398
|
+
* (`SandboxedToolExecutor` on the sidecar). The command is rewritten into
|
|
399
|
+
* a Seatbelt invocation before it crosses the reverse channel, so the
|
|
400
|
+
* host's shell spawns the confined command without learning sandbox
|
|
401
|
+
* mechanics. Every sandboxed result carries `data._sandbox =
|
|
402
|
+
* {backend, enforcement: full|partial|none}`. `requireFull: true`
|
|
403
|
+
* denies anything weaker than `full` (including honest `partial`).
|
|
404
|
+
* `requireBackend: true` denies only `none` (no OS backend). Absent →
|
|
405
|
+
* unconfined (legacy behavior).
|
|
406
|
+
*/
|
|
407
|
+
execSandbox?: {
|
|
408
|
+
enabled: boolean;
|
|
409
|
+
/** Directories the confined command may write into (e.g. project root). */
|
|
410
|
+
writableRoots?: string[];
|
|
411
|
+
/** Allow outbound network from the confined command. Default false. */
|
|
412
|
+
network?: boolean;
|
|
413
|
+
/** Egress allow-list (host[:port]); only consulted when network is on. */
|
|
414
|
+
allowedHosts?: string[];
|
|
415
|
+
/** Deny the call unless enforcement is `full`. Default false (marked). */
|
|
416
|
+
requireFull?: boolean;
|
|
417
|
+
/**
|
|
418
|
+
* Deny the call when there is no sandbox backend (`enforcement: none`).
|
|
419
|
+
* Partial backends (Seatbelt with `network: true`) still run. Default false.
|
|
420
|
+
*/
|
|
421
|
+
requireBackend?: boolean;
|
|
422
|
+
/**
|
|
423
|
+
* W4.1.1: when no local rewriter backend exists (Windows), delegate
|
|
424
|
+
* confined spawn to the host over the `host.process.spawn` reverse
|
|
425
|
+
* channel instead of running unconfined. The host reports the
|
|
426
|
+
* enforcement it actually applied; a host without the capability fails
|
|
427
|
+
* closed (tool error, the command never runs).
|
|
428
|
+
*/
|
|
429
|
+
hostSpawn?: boolean;
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Wave 4 (W4-1): approval algebra in front of every tool call.
|
|
433
|
+
* `mode: 'host'` asks the host UI over the reverse channel
|
|
434
|
+
* (`approval.request`); `storePath` enables the durable scope
|
|
435
|
+
* (`allow_always` / `deny_always` persisted per category);
|
|
436
|
+
* `timeoutMs` fails closed as `timed_out` (a denial) when the UI does
|
|
437
|
+
* not answer in time. Absent → no approval layer (legacy behavior).
|
|
438
|
+
*/
|
|
439
|
+
approval?: {
|
|
440
|
+
mode: 'host' | 'auto';
|
|
441
|
+
timeoutMs?: number;
|
|
442
|
+
storePath?: string;
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Pre-digestion raw provider chunk, forwarded by the sidecar when the
|
|
447
|
+
* request sets `streamRawChunks: true` (default off). The CoreLoop's
|
|
448
|
+
* `on_stream_chunk` hook observes every `LLMStreamChunk` *before* UI-tag
|
|
449
|
+
* stripping and surrogate splitting turn it into display text — this is the
|
|
450
|
+
* input for incremental renderers (e.g. a streaming UI-tag parser). The
|
|
451
|
+
* digested `delta` / `reasoningDelta` fields on the same notification stay
|
|
452
|
+
* post-stripping display text. Unset fields are omitted; the provider's
|
|
453
|
+
* original wire chunk (`raw`) and per-chunk `usage` are never forwarded.
|
|
454
|
+
*
|
|
455
|
+
* Note: the OpenAI-compat provider buffers tool-call argument fragments
|
|
456
|
+
* into one complete ToolCall, so `toolCallDelta` arrives whole — only
|
|
457
|
+
* content/reasoning are incremental.
|
|
458
|
+
*/
|
|
459
|
+
export interface SidecarRawChunk {
|
|
460
|
+
contentDelta?: string;
|
|
461
|
+
reasoningDelta?: string;
|
|
462
|
+
toolCallDelta?: {
|
|
463
|
+
id: string;
|
|
464
|
+
name: string;
|
|
465
|
+
arguments: Record<string, unknown>;
|
|
466
|
+
};
|
|
467
|
+
finishReason?: string;
|
|
468
|
+
}
|
|
469
|
+
export interface SidecarStreamChunk {
|
|
470
|
+
streamId: string;
|
|
471
|
+
delta?: string;
|
|
472
|
+
reasoningDelta?: string;
|
|
473
|
+
toolCall?: {
|
|
474
|
+
id: string;
|
|
475
|
+
name: string;
|
|
476
|
+
arguments: Record<string, unknown>;
|
|
477
|
+
};
|
|
478
|
+
/**
|
|
479
|
+
* Pre-digestion chunk (opt-in via `streamRawChunks`). Fire-and-forget
|
|
480
|
+
* emission on the sidecar: chunk order is preserved, ordering against the
|
|
481
|
+
* digested fields on sibling notifications is not.
|
|
482
|
+
*/
|
|
483
|
+
rawChunk?: SidecarRawChunk;
|
|
484
|
+
/** CoreLoop tool progress (A4 path). */
|
|
485
|
+
toolResult?: {
|
|
486
|
+
id: string;
|
|
487
|
+
name: string;
|
|
488
|
+
success: boolean;
|
|
489
|
+
durationMs?: number;
|
|
490
|
+
error?: string;
|
|
491
|
+
resultPreview?: string;
|
|
492
|
+
/** W4-2: `data._sandbox` marker lifted out of the result for the card. */
|
|
493
|
+
sandbox?: {
|
|
494
|
+
backend?: string;
|
|
495
|
+
enforcement: string;
|
|
496
|
+
};
|
|
497
|
+
};
|
|
498
|
+
/** CoreLoop notices: soft_timeout / budget_exhausted. */
|
|
499
|
+
notice?: {
|
|
500
|
+
kind: string;
|
|
501
|
+
[key: string]: unknown;
|
|
502
|
+
};
|
|
503
|
+
finishReason?: string;
|
|
504
|
+
usage?: {
|
|
505
|
+
promptTokens: number;
|
|
506
|
+
completionTokens: number;
|
|
507
|
+
totalTokens: number;
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
export interface SidecarStreamDone {
|
|
511
|
+
streamId: string;
|
|
512
|
+
ok: boolean;
|
|
513
|
+
cancelled?: boolean;
|
|
514
|
+
/** CoreLoop terminal status (completed | failed | budget_exhausted). */
|
|
515
|
+
status?: string;
|
|
516
|
+
reason?: string;
|
|
517
|
+
/** Sidecar TraceRecorder id — fetch the persisted run via `trace.fetch`. */
|
|
518
|
+
traceId?: string;
|
|
519
|
+
/**
|
|
520
|
+
* W6-9: the run's accumulated billable usage (summed over every provider
|
|
521
|
+
* request this turn), plus `costUsd` when the model is priced. Absent on
|
|
522
|
+
* paths that don't report usage.
|
|
523
|
+
*/
|
|
524
|
+
usage?: {
|
|
525
|
+
promptTokens: number;
|
|
526
|
+
completionTokens: number;
|
|
527
|
+
totalTokens: number;
|
|
528
|
+
cachedPromptTokens?: number;
|
|
529
|
+
costUsd?: number;
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
export interface SidecarStreamError {
|
|
533
|
+
streamId: string;
|
|
534
|
+
/** Present on CoreLoop failures — the sidecar recorded the partial trace. */
|
|
535
|
+
traceId?: string;
|
|
536
|
+
kind: string;
|
|
537
|
+
message: string;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* `agent.child` notification payload (child-agent lifecycle), demuxed by
|
|
541
|
+
* streamId. Emitted by the orchestration pool and by `delegate_subagent`
|
|
542
|
+
* delegations (delegate-on-pool). `kind` is one of child_spawned /
|
|
543
|
+
* child_completed / child_failed / child_cancelled / child_interrupted /
|
|
544
|
+
* child_resumed; the rest of the fields depend on the kind (childId always
|
|
545
|
+
* present; child_spawned adds `task` and `depth`; delegations add
|
|
546
|
+
* `profile` — the resolved subagent_type, `general-purpose` when untyped).
|
|
547
|
+
*/
|
|
548
|
+
export interface SidecarChildEvent {
|
|
549
|
+
kind: string;
|
|
550
|
+
childId: string;
|
|
551
|
+
task?: string;
|
|
552
|
+
depth?: number;
|
|
553
|
+
status?: string;
|
|
554
|
+
error?: string;
|
|
555
|
+
profile?: string;
|
|
556
|
+
}
|
|
557
|
+
export interface SidecarChatStreamHandlers {
|
|
558
|
+
onChunk?: (chunk: SidecarStreamChunk) => void;
|
|
559
|
+
onDone?: (done: SidecarStreamDone) => void;
|
|
560
|
+
onError?: (err: SidecarStreamError) => void;
|
|
561
|
+
onChildEvent?: (event: SidecarChildEvent) => void;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* `models.list` result row: one model id the configured gateway accepts,
|
|
565
|
+
* joined with the bundled models.dev capability catalog by the same
|
|
566
|
+
* resolution the request path's reasoning-effort clamp uses. `joinedFrom`
|
|
567
|
+
* keeps the leaf-join provenance; `capabilities: 'unknown'` means no
|
|
568
|
+
* catalog tier matched — reasoning levels are then unavailable rather
|
|
569
|
+
* than empty.
|
|
570
|
+
*/
|
|
571
|
+
export interface SidecarModelEntry {
|
|
572
|
+
id: string;
|
|
573
|
+
name: string | null;
|
|
574
|
+
window: number | null;
|
|
575
|
+
modalities: string[];
|
|
576
|
+
reasoningLevels: string[];
|
|
577
|
+
pricing: {
|
|
578
|
+
promptPerMtok: number | null;
|
|
579
|
+
completionPerMtok: number | null;
|
|
580
|
+
} | null;
|
|
581
|
+
joinedFrom: string | null;
|
|
582
|
+
capabilities: 'known' | 'unknown';
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* `models.list` result. `catalogStatus` is `live` when just fetched from
|
|
586
|
+
* the gateway, `stale` when served from cache after a refresh failure,
|
|
587
|
+
* `offline` when the gateway is unreachable with no cache (then `models`
|
|
588
|
+
* is empty and `error` carries the cause). Discovery only — the catalog
|
|
589
|
+
* is not a routing whitelist; unlisted ids may still be sent.
|
|
590
|
+
*/
|
|
591
|
+
export interface SidecarModelCatalog {
|
|
592
|
+
models: SidecarModelEntry[];
|
|
593
|
+
catalogStatus: 'live' | 'stale' | 'offline';
|
|
594
|
+
error?: string;
|
|
595
|
+
fetchedAt?: number;
|
|
596
|
+
/** Absent on the offline path (no gateway/env context to report). */
|
|
597
|
+
current?: {
|
|
598
|
+
model: string | null;
|
|
599
|
+
reasoningEffort: string | null;
|
|
600
|
+
};
|
|
601
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Electron single-instance lock. Imported first from main.ts so a second
|
|
3
|
+
* OS process exits before LocalStore opens the product DB.
|
|
4
|
+
*
|
|
5
|
+
* `requestSingleInstanceLock` must run before `app.whenReady`. `process.exit`
|
|
6
|
+
* is required because `app.quit()` is async and later imports would still
|
|
7
|
+
* construct LocalStore against the same userData.
|
|
8
|
+
*/
|
|
9
|
+
type SecondInstanceHandler = () => void;
|
|
10
|
+
export declare function setSecondInstanceHandler(handler: SecondInstanceHandler): void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Electron single-instance lock. Imported first from main.ts so a second
|
|
3
|
+
* OS process exits before LocalStore opens the product DB.
|
|
4
|
+
*
|
|
5
|
+
* `requestSingleInstanceLock` must run before `app.whenReady`. `process.exit`
|
|
6
|
+
* is required because `app.quit()` is async and later imports would still
|
|
7
|
+
* construct LocalStore against the same userData.
|
|
8
|
+
*/
|
|
9
|
+
import { app } from 'electron';
|
|
10
|
+
const gotTheLock = app.requestSingleInstanceLock();
|
|
11
|
+
if (!gotTheLock) {
|
|
12
|
+
app.quit();
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
let secondInstanceHandler = null;
|
|
16
|
+
export function setSecondInstanceHandler(handler) {
|
|
17
|
+
secondInstanceHandler = handler;
|
|
18
|
+
}
|
|
19
|
+
app.on('second-instance', () => {
|
|
20
|
+
secondInstanceHandler?.();
|
|
21
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL that lists chat sessions with no rows in `chat_messages`.
|
|
3
|
+
*
|
|
4
|
+
* Bind `exceptChatId` twice. Pass `null` for both placeholders to include
|
|
5
|
+
* every empty session; a concrete id keeps that session (the one currently
|
|
6
|
+
* open in the composer) so a first-send race cannot delete it before the
|
|
7
|
+
* user message lands.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LIST_EMPTY_CHAT_IDS_SQL = "\n SELECT id\n FROM chat_sessions\n WHERE (? IS NULL OR id != ?)\n AND NOT EXISTS (\n SELECT 1 FROM chat_messages WHERE chat_id = chat_sessions.id\n )\n";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL that lists chat sessions with no rows in `chat_messages`.
|
|
3
|
+
*
|
|
4
|
+
* Bind `exceptChatId` twice. Pass `null` for both placeholders to include
|
|
5
|
+
* every empty session; a concrete id keeps that session (the one currently
|
|
6
|
+
* open in the composer) so a first-send race cannot delete it before the
|
|
7
|
+
* user message lands.
|
|
8
|
+
*/
|
|
9
|
+
export const LIST_EMPTY_CHAT_IDS_SQL = `
|
|
10
|
+
SELECT id
|
|
11
|
+
FROM chat_sessions
|
|
12
|
+
WHERE (? IS NULL OR id != ?)
|
|
13
|
+
AND NOT EXISTS (
|
|
14
|
+
SELECT 1 FROM chat_messages WHERE chat_id = chat_sessions.id
|
|
15
|
+
)
|
|
16
|
+
`;
|