@shendeguize/dsh-agent-sidecar 0.1.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/LICENSE +21 -0
- package/README.md +167 -0
- package/cordis.patch.yml +10 -0
- package/lib/client.js +8062 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +396 -0
- package/lib/index.js +4166 -0
- package/package.json +101 -0
- package/src/analysis.ts +782 -0
- package/src/bridge.ts +841 -0
- package/src/client/analysis/AnalysisPanel.tsx +191 -0
- package/src/client/analysis/analysis.module.css +183 -0
- package/src/client/analysis-glue.ts +331 -0
- package/src/client/api.ts +380 -0
- package/src/client/board/Board.tsx +214 -0
- package/src/client/board/board.module.css +302 -0
- package/src/client/board/logic.ts +556 -0
- package/src/client/board/project-view-logic.ts +361 -0
- package/src/client/board/project-view.module.css +307 -0
- package/src/client/board/project-view.tsx +189 -0
- package/src/client/board/strings.ts +112 -0
- package/src/client/commands.ts +484 -0
- package/src/client/controller.ts +360 -0
- package/src/client/css-modules.d.ts +11 -0
- package/src/client/detail/SessionDetail.tsx +270 -0
- package/src/client/detail/detail.module.css +433 -0
- package/src/client/detail/logic.ts +779 -0
- package/src/client/detail/strings.ts +98 -0
- package/src/client/detail/transport.ts +175 -0
- package/src/client/detail-glue.ts +397 -0
- package/src/client/detail-view.module.css +79 -0
- package/src/client/detail-view.tsx +233 -0
- package/src/client/dsh-tools/LineageTree.tsx +210 -0
- package/src/client/dsh-tools/SearchPanel.tsx +169 -0
- package/src/client/dsh-tools/dsh-tools.module.css +374 -0
- package/src/client/dsh-tools/logic.ts +596 -0
- package/src/client/dsh-tools/strings.ts +90 -0
- package/src/client/index.ts +315 -0
- package/src/client/inject/InjectPanel.tsx +482 -0
- package/src/client/inject/inject.module.css +446 -0
- package/src/client/inject/logic.ts +516 -0
- package/src/client/inject/overlay.module.css +22 -0
- package/src/client/inject-glue.ts +171 -0
- package/src/client/locales/command.ts +48 -0
- package/src/client/locales/en.ts +385 -0
- package/src/client/locales/index.ts +123 -0
- package/src/client/locales/zh.ts +402 -0
- package/src/client/m3-transport.ts +151 -0
- package/src/client/mount.tsx +307 -0
- package/src/client/project-glue.ts +134 -0
- package/src/client/search-glue.ts +143 -0
- package/src/client/settings-card.module.css +359 -0
- package/src/client/settings-card.tsx +565 -0
- package/src/client/settings-glue.ts +130 -0
- package/src/client/sidebar-tab.tsx +494 -0
- package/src/client/sse.ts +366 -0
- package/src/client/widget.tsx +80 -0
- package/src/config.ts +193 -0
- package/src/dsh-inject.ts +240 -0
- package/src/fusion.ts +988 -0
- package/src/guard.ts +274 -0
- package/src/index.ts +950 -0
- package/src/inject-gateway.ts +574 -0
- package/src/routes.ts +1133 -0
- package/src/send-cli.ts +340 -0
- package/src/session-store.ts +184 -0
- package/src/skills-provider.ts +293 -0
- package/src/supervisor.ts +463 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inject integration glue (S5 wiring, T4.9): everything that stands between
|
|
3
|
+
* the presentational {@link InjectPanel} (T4.5, src/client/inject/) and the
|
|
4
|
+
* rest of the client half, kept pure/injectable so it unit-tests under
|
|
5
|
+
* plain node (same posture as controller.ts / settings-glue.ts).
|
|
6
|
+
*
|
|
7
|
+
* - {@link prepareEnvelope} / {@link executeEnvelope}: build the M2 action
|
|
8
|
+
* wire bodies of the host dispatcher (src/routes.ts `handleAction`:
|
|
9
|
+
* `{type:'inject.prepare'|'inject.execute', ...}` with the phase fields
|
|
10
|
+
* at the top level). The body types are api.ts's `ActionEnvelope` union
|
|
11
|
+
* members — the data layer owns the wire mirror, so `postAction` is
|
|
12
|
+
* typed end-to-end and this module casts nothing on the way out.
|
|
13
|
+
* - {@link createInjectActions}: the panel's `onPrepare`/`onExecute` props.
|
|
14
|
+
* Execute posts with {@link EXECUTE_TIMEOUT_MS} (path two's server-side
|
|
15
|
+
* budget outlives the 15s data-layer default; prepare keeps the default,
|
|
16
|
+
* it has no delivery side effect).
|
|
17
|
+
* Per the panel contract, transport failures resolve AS VALUES: the data
|
|
18
|
+
* layer's normalized ApiError satisfies the panel's structural
|
|
19
|
+
* {@link ApiErrorLike} and is returned, never thrown (anything that is
|
|
20
|
+
* not an ApiError is re-thrown and lands in the panel's defensive catch:
|
|
21
|
+
* retryable notice for prepare, terminal unknown for execute — S6).
|
|
22
|
+
* A delivered execute fires the optional `onDelivered` hook so the owner
|
|
23
|
+
* can pull one fresh snapshot; failed/unknown never do.
|
|
24
|
+
* - {@link findInjectTarget}: board card selection → panel target (the
|
|
25
|
+
* design §5.1 view-3 target summary comes from the selected SessionView
|
|
26
|
+
* as mapped into the controller's card VMs). A session that has left the
|
|
27
|
+
* snapshot resolves to null — the panel then shows its no-target hint
|
|
28
|
+
* instead of injecting into a stale target.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { isApiError, postAction } from './api.ts'
|
|
34
|
+
import type { ExecuteActionBody, PrepareActionBody, RequestOptions } from './api.ts'
|
|
35
|
+
import type { SessionCardVM } from './board/logic.ts'
|
|
36
|
+
import type { InjectPanelTarget } from './inject/InjectPanel.tsx'
|
|
37
|
+
import type {
|
|
38
|
+
ApiErrorLike,
|
|
39
|
+
InjectResultView,
|
|
40
|
+
PanelExecuteRequest,
|
|
41
|
+
PanelPrepareRequest,
|
|
42
|
+
PrepareSuccess,
|
|
43
|
+
} from './inject/logic.ts'
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Wire bodies (canonical mirror lives in api.ts; re-exported for consumers).
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
export type { ExecuteActionBody, PrepareActionBody } from './api.ts'
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Execute-path HTTP deadline. Path two's server-side budget is the 30s CLI
|
|
53
|
+
* timeout + 5s hard-kill buffer (host: send-cli.ts `DEFAULT_SEND_TIMEOUT_MS`
|
|
54
|
+
* + `HARD_TIMEOUT_BUFFER_MS` = 35s worst case); a client deadline below
|
|
55
|
+
* that fabricates a terminal 'unknown' out of every slow-but-honest
|
|
56
|
+
* delivery receipt (M2 review F-3). 45s = server worst case + 10s margin;
|
|
57
|
+
* the mirror relation is pinned by test against the host constants.
|
|
58
|
+
*/
|
|
59
|
+
export const EXECUTE_TIMEOUT_MS = 45_000
|
|
60
|
+
|
|
61
|
+
/** Panel prepare request → the host prepare wire body. */
|
|
62
|
+
export function prepareEnvelope(req: PanelPrepareRequest): PrepareActionBody {
|
|
63
|
+
return {
|
|
64
|
+
type: 'inject.prepare',
|
|
65
|
+
target: { agent: req.target.agent, sessionId: req.target.sessionId },
|
|
66
|
+
mode: req.mode,
|
|
67
|
+
message: req.message,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Panel execute request → the host execute wire body. */
|
|
72
|
+
export function executeEnvelope(req: PanelExecuteRequest): ExecuteActionBody {
|
|
73
|
+
return {
|
|
74
|
+
type: 'inject.execute',
|
|
75
|
+
requestId: req.requestId,
|
|
76
|
+
confirmToken: req.confirmToken,
|
|
77
|
+
message: req.message,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Action callbacks.
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
/** Transport seam: POST one action body, resolve the parsed JSON reply. */
|
|
86
|
+
export type PostActionFn = (
|
|
87
|
+
body: PrepareActionBody | ExecuteActionBody,
|
|
88
|
+
opts?: RequestOptions,
|
|
89
|
+
) => Promise<unknown>
|
|
90
|
+
|
|
91
|
+
/** Default transport: api.ts postAction (typed directly; the wire bodies
|
|
92
|
+
* are ActionEnvelope union members, so no cast stands between the panel
|
|
93
|
+
* and the dispatcher). */
|
|
94
|
+
const defaultPost: PostActionFn = (body, opts) => postAction(body, opts)
|
|
95
|
+
|
|
96
|
+
/** The two integration callbacks the InjectPanel consumes. */
|
|
97
|
+
export interface InjectActions {
|
|
98
|
+
onPrepare(req: PanelPrepareRequest): Promise<PrepareSuccess | ApiErrorLike>
|
|
99
|
+
onExecute(req: PanelExecuteRequest): Promise<InjectResultView | ApiErrorLike>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface InjectActionDeps {
|
|
103
|
+
/** Transport override (tests); defaults to api.ts postAction. */
|
|
104
|
+
post?: PostActionFn
|
|
105
|
+
/** Fired once per delivered execute (e.g. a controller snapshot refresh). */
|
|
106
|
+
onDelivered?: () => void
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Build the panel's `onPrepare`/`onExecute` callbacks over the action
|
|
111
|
+
* transport. Error posture per the panel contract: ApiError resolves as a
|
|
112
|
+
* value (the panel classifies http-kind as a server vocabulary verdict and
|
|
113
|
+
* everything else as a transport failure); non-ApiError throws propagate.
|
|
114
|
+
* @param deps - transport and delivery-hook seams.
|
|
115
|
+
* @returns the callbacks, ready to spread onto the panel props.
|
|
116
|
+
*/
|
|
117
|
+
export function createInjectActions(deps: InjectActionDeps = {}): InjectActions {
|
|
118
|
+
const post = deps.post ?? defaultPost
|
|
119
|
+
return {
|
|
120
|
+
async onPrepare(req) {
|
|
121
|
+
try {
|
|
122
|
+
return (await post(prepareEnvelope(req))) as PrepareSuccess
|
|
123
|
+
} catch (err) {
|
|
124
|
+
if (isApiError(err)) return err
|
|
125
|
+
throw err
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
async onExecute(req) {
|
|
129
|
+
let result: InjectResultView
|
|
130
|
+
try {
|
|
131
|
+
// Longer deadline than prepare: the send-cli path legitimately runs
|
|
132
|
+
// up to 35s server-side before an honest receipt arrives (F-3).
|
|
133
|
+
result = (await post(executeEnvelope(req), {
|
|
134
|
+
timeoutMs: EXECUTE_TIMEOUT_MS,
|
|
135
|
+
})) as InjectResultView
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (isApiError(err)) return err
|
|
138
|
+
throw err
|
|
139
|
+
}
|
|
140
|
+
// Only a positive delivery refreshes; 'unknown' stays terminal and
|
|
141
|
+
// untouched (S6), 'failed' changes nothing worth re-pulling.
|
|
142
|
+
if (result.outcome === 'delivered') deps.onDelivered?.()
|
|
143
|
+
return result
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Target mapping.
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Resolve the selected board card into the panel's injection target.
|
|
154
|
+
* @param sessions - current card VMs from the controller view state.
|
|
155
|
+
* @param sessionId - the selected session, or null when nothing is selected.
|
|
156
|
+
* @returns the target, or null when unselected or gone from the snapshot.
|
|
157
|
+
*/
|
|
158
|
+
export function findInjectTarget(
|
|
159
|
+
sessions: readonly SessionCardVM[],
|
|
160
|
+
sessionId: string | null,
|
|
161
|
+
): InjectPanelTarget | null {
|
|
162
|
+
if (sessionId === null) return null
|
|
163
|
+
const card = sessions.find((session) => session.sessionId === sessionId)
|
|
164
|
+
if (card === undefined) return null
|
|
165
|
+
const title = card.title.trim()
|
|
166
|
+
return {
|
|
167
|
+
agent: card.agent,
|
|
168
|
+
sessionId: card.sessionId,
|
|
169
|
+
...(title !== '' ? { title } : {}),
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `command.*` locale segment for the `/sidecar` slash command (T4.6).
|
|
3
|
+
*
|
|
4
|
+
* T5.10b unification: the `command.*` copy now LIVES in the main
|
|
5
|
+
* dictionaries (./zh.ts + ./en.ts, domain `command`) and this module is a
|
|
6
|
+
* thin derived view kept for its consumers ({@link tCommand} for
|
|
7
|
+
* commands.ts, {@link commandZh}/{@link commandEn} for tests and for the
|
|
8
|
+
* `ctx.locale.register(ns, locale, dict)` bridge). Translation goes
|
|
9
|
+
* through the shared engine and the shared active-locale switch, so
|
|
10
|
+
* `setLocale` drives every surface consistently and the main-table parity
|
|
11
|
+
* test covers these keys too.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { t, zh, en } from './index.ts'
|
|
17
|
+
import type { KeysOfDomain } from './index.ts'
|
|
18
|
+
|
|
19
|
+
/** Key union of the command locale segment (main zh table is the source). */
|
|
20
|
+
export type CommandLocaleKey = KeysOfDomain<'command'>
|
|
21
|
+
|
|
22
|
+
function commandSlice(dict: Readonly<Record<string, string>>): Record<CommandLocaleKey, string> {
|
|
23
|
+
const out: Record<string, string> = {}
|
|
24
|
+
for (const [key, value] of Object.entries(dict)) {
|
|
25
|
+
if (key.startsWith('command.')) out[key] = value
|
|
26
|
+
}
|
|
27
|
+
return out as Record<CommandLocaleKey, string>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The `command.*` slice of the main zh dictionary. */
|
|
31
|
+
export const commandZh: Readonly<Record<CommandLocaleKey, string>> = commandSlice(zh)
|
|
32
|
+
|
|
33
|
+
/** The `command.*` slice of the main en dictionary. */
|
|
34
|
+
export const commandEn: Readonly<Record<CommandLocaleKey, string>> = commandSlice(en)
|
|
35
|
+
|
|
36
|
+
/** Derived dictionaries of this segment, keyed by locale id. */
|
|
37
|
+
export const commandDictionaries = { zh: commandZh, en: commandEn } as const
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Translate a command-segment key in the shared active locale (delegates
|
|
41
|
+
* to the main-table {@link t}; lookup chain: active locale → zh → key).
|
|
42
|
+
* @param key - a key of the command segment.
|
|
43
|
+
* @param params - optional `{name}` template params.
|
|
44
|
+
* @returns the translated string.
|
|
45
|
+
*/
|
|
46
|
+
export function tCommand(key: CommandLocaleKey, params?: Record<string, unknown>): string {
|
|
47
|
+
return t(key, params)
|
|
48
|
+
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* English dictionary, compile-checked complete against the zh key set (a
|
|
3
|
+
* missing or extra key is a type error; see ./zh.ts for the key-space
|
|
4
|
+
* convention).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { SidecarLocaleKey } from './zh.ts'
|
|
8
|
+
|
|
9
|
+
export const en = {
|
|
10
|
+
// ── card chrome ────────────────────────────────────────────────────────
|
|
11
|
+
'settings.cardTitle': 'Agent Sidecar',
|
|
12
|
+
'settings.cardDescription':
|
|
13
|
+
'Runtime settings for cross-agent session monitoring, injection and bypass analysis.',
|
|
14
|
+
'settings.docsLink': 'Documentation',
|
|
15
|
+
'settings.readOnly': 'The settings document is read-only; edits cannot be saved.',
|
|
16
|
+
'settings.unsaved': 'Unsaved',
|
|
17
|
+
'settings.save': 'Save',
|
|
18
|
+
'settings.saving': 'Saving…',
|
|
19
|
+
'settings.discard': 'Discard',
|
|
20
|
+
'settings.saveFailed': 'Save failed; please retry.',
|
|
21
|
+
'settings.expand': 'Expand',
|
|
22
|
+
'settings.collapse': 'Collapse',
|
|
23
|
+
'settings.invalidNumber': 'Enter an integer no less than {min}',
|
|
24
|
+
|
|
25
|
+
// ── daemon lifecycle ───────────────────────────────────────────────────
|
|
26
|
+
'settings.sectionDaemon': 'Daemon lifecycle',
|
|
27
|
+
'settings.daemonPolicyLabel': 'Management policy',
|
|
28
|
+
'settings.daemonPolicyHint':
|
|
29
|
+
'adopt-or-host probes and adopts an existing daemon, else spawns one; adopt-only never spawns; off leaves the lifecycle alone (read-only reconcile still runs).',
|
|
30
|
+
'settings.daemonPolicyAdoptOrHost': 'adopt-or-host (adopt, else spawn)',
|
|
31
|
+
'settings.daemonPolicyAdoptOnly': 'adopt-only (never spawn)',
|
|
32
|
+
'settings.daemonPolicyOff': 'off (unmanaged)',
|
|
33
|
+
'settings.daemonBackoffLimitLabel': 'Backoff limit',
|
|
34
|
+
'settings.daemonBackoffLimitHint':
|
|
35
|
+
'After this many consecutive hosting failures the supervisor stops restarting and trips failed.',
|
|
36
|
+
'settings.daemonStatusLabel': 'Daemon status',
|
|
37
|
+
'settings.daemonStateProbe': 'Probing',
|
|
38
|
+
'settings.daemonStateAdopted': 'Adopted an existing daemon',
|
|
39
|
+
'settings.daemonStateDefer': 'Waiting for the system service',
|
|
40
|
+
'settings.daemonStateReprobe': 'Re-probing',
|
|
41
|
+
'settings.daemonStateHosting': 'Spawning',
|
|
42
|
+
'settings.daemonStateHosted': 'Hosted by the plugin',
|
|
43
|
+
'settings.daemonStateBackoff': 'Backing off before retry',
|
|
44
|
+
'settings.daemonStateFailed': 'Tripped (circuit open)',
|
|
45
|
+
'settings.daemonPidVersion': 'pid {pid} · v{version}',
|
|
46
|
+
'settings.daemonDeferNote':
|
|
47
|
+
'The daemon is managed by a system service (LaunchAgent); the plugin only probes and waits — it never spawns a duplicate or terminates it.',
|
|
48
|
+
'settings.daemonFailedNote':
|
|
49
|
+
'Consecutive hosting failures reached the backoff limit; the board degraded to the last snapshot. Check the sidecar command, then retry.',
|
|
50
|
+
'settings.daemonRetry': 'Retry',
|
|
51
|
+
|
|
52
|
+
// ── sidecar invocation ─────────────────────────────────────────────────
|
|
53
|
+
'settings.sectionSidecar': 'Sidecar invocation',
|
|
54
|
+
'settings.sidecarCommandLabel': 'Executable command',
|
|
55
|
+
'settings.sidecarCommandHint':
|
|
56
|
+
'A PATH name, an absolute path, or a space-separated multi-part command (e.g. python3 /path/agent-sidecar.pyz); the plugin never installs the sidecar for you.',
|
|
57
|
+
'settings.sidecarRuntimeDirLabel': 'Runtime directory',
|
|
58
|
+
'settings.sidecarRuntimeDirHint':
|
|
59
|
+
'Empty uses the default ~/.agent_sidecar (honoring AGENT_SIDECAR_RUNTIME_DIR); a non-empty value is passed to spawned daemons via the environment.',
|
|
60
|
+
|
|
61
|
+
// ── stream reconciliation ──────────────────────────────────────────────
|
|
62
|
+
'settings.sectionStream': 'Stream reconciliation',
|
|
63
|
+
'settings.streamActiveMsLabel': 'Active cadence (ms)',
|
|
64
|
+
'settings.streamActiveMsHint': 'Status snapshot cadence while any session is working.',
|
|
65
|
+
'settings.streamIdleMsLabel': 'Idle cadence (ms)',
|
|
66
|
+
'settings.streamIdleMsHint': 'Status snapshot cadence while no session is working.',
|
|
67
|
+
|
|
68
|
+
// ── injection ──────────────────────────────────────────────────────────
|
|
69
|
+
'settings.sectionInject': 'Message injection',
|
|
70
|
+
'settings.injectEnabledLabel': 'Enable injection',
|
|
71
|
+
'settings.injectEnabledHint':
|
|
72
|
+
'When off, the board hides every inject affordance and the server rejects write actions.',
|
|
73
|
+
'settings.injectDefaultModeLabel': 'Default injection mode',
|
|
74
|
+
'settings.injectDefaultModeHint': 'The mode preselected when the inject panel opens.',
|
|
75
|
+
'settings.injectModeQueue': 'queue (next turn)',
|
|
76
|
+
'settings.injectModeSteer': 'steer (mid-turn)',
|
|
77
|
+
'settings.injectSafetyNote':
|
|
78
|
+
'Safety: injection is off by default; even when enabled, every injection still passes a per-request confirmation dialog — there is no batch or scheduled injection. Not recommended on multi-user hosts.',
|
|
79
|
+
|
|
80
|
+
// ── bypass analysis ────────────────────────────────────────────────────
|
|
81
|
+
'settings.sectionAnalysis': 'Bypass analysis',
|
|
82
|
+
'settings.analysisEnabledLabel': 'Enable AI bypass analysis',
|
|
83
|
+
'settings.analysisEnabledHint':
|
|
84
|
+
'Spins up a dsh analysis session over an observed session on demand (consumes model tokens; off by default).',
|
|
85
|
+
|
|
86
|
+
// ── board UI ───────────────────────────────────────────────────────────
|
|
87
|
+
'settings.sectionUi': 'Board UI',
|
|
88
|
+
'settings.uiTimeWindowHoursLabel': 'Session time window (hours)',
|
|
89
|
+
'settings.uiTimeWindowHoursHint': 'The board lists only sessions active within this window.',
|
|
90
|
+
'settings.uiShowDeadLabel': 'Show dead sessions',
|
|
91
|
+
'settings.uiShowDeadHint': 'Also list finished (dead) sessions on the board.',
|
|
92
|
+
|
|
93
|
+
// ── skill mode ─────────────────────────────────────────────────────────
|
|
94
|
+
'settings.sectionSkill': 'Skill mode',
|
|
95
|
+
'settings.skillProvideLabel': 'Provide the skill in-process',
|
|
96
|
+
'settings.skillProvideHint':
|
|
97
|
+
'Provide the agent-sidecar skill to dsh via registerProvider (enabled in M4; applies after restart).',
|
|
98
|
+
|
|
99
|
+
// ── inject panel: chrome & editor (T4.5, design §5.1 view 3) ───────────
|
|
100
|
+
'inject.title': 'Inject message',
|
|
101
|
+
'inject.confirmTitle': 'Confirm injection',
|
|
102
|
+
'inject.close': 'Close',
|
|
103
|
+
'inject.done': 'Done',
|
|
104
|
+
'inject.capabilityOff': 'Injection is not enabled; turn it on in Settings first.',
|
|
105
|
+
'inject.noTarget':
|
|
106
|
+
'No injection target selected; start from a board card or a session detail page.',
|
|
107
|
+
'inject.targetLabel': 'Target',
|
|
108
|
+
'inject.messageLabel': 'Message',
|
|
109
|
+
'inject.messagePlaceholder': 'Message to inject (16 KiB max; never paste secrets)',
|
|
110
|
+
'inject.byteCount': '{bytes} / {limit} bytes',
|
|
111
|
+
'inject.msgEmpty': 'The message must not be empty.',
|
|
112
|
+
'inject.msgNul': 'The message contains an illegal NUL character.',
|
|
113
|
+
'inject.msgTooLarge': 'The message is {bytes} bytes, over the {limit}-byte limit.',
|
|
114
|
+
'inject.modeLabel': 'Injection mode',
|
|
115
|
+
'inject.modeQueue': 'queue (next turn)',
|
|
116
|
+
'inject.modeQueueHint':
|
|
117
|
+
'The message queues and is handled when the target session starts its next turn.',
|
|
118
|
+
'inject.modeSteer': 'steer (mid-turn)',
|
|
119
|
+
'inject.modeSteerHint':
|
|
120
|
+
'The message is injected mid-turn and steers the target session immediately.',
|
|
121
|
+
'inject.argvWarning':
|
|
122
|
+
"cursor-cli target: injection runs through its native subprocess, and the message is visible in this machine's process list while that process lives; never include secrets.",
|
|
123
|
+
'inject.auditNote':
|
|
124
|
+
'This injection is recorded in the sidecar audit log (byte size and content fingerprint; never the message plaintext).',
|
|
125
|
+
'inject.prepare': 'Prepare injection',
|
|
126
|
+
'inject.preparing': 'Validating…',
|
|
127
|
+
|
|
128
|
+
// ── inject panel: confirm phase ──────────────────────────────────────
|
|
129
|
+
'inject.planTargetLabel': 'Target snapshot',
|
|
130
|
+
'inject.planStatus': 'Current status: {status}',
|
|
131
|
+
'inject.statusObservedNote':
|
|
132
|
+
'Status is an observed value inferred from persisted data and may lag.',
|
|
133
|
+
'inject.planModeLabel': 'Mode',
|
|
134
|
+
'inject.planPreviewLabel': 'Message digest ({bytes} bytes)',
|
|
135
|
+
'inject.countdown': 'The confirm token expires in {seconds}s',
|
|
136
|
+
'inject.confirmExecute': 'Confirm injection',
|
|
137
|
+
'inject.executing': 'Injecting…',
|
|
138
|
+
'inject.cancel': 'Cancel',
|
|
139
|
+
'inject.tokenExpired':
|
|
140
|
+
'Confirmation timed out and the token is void; prepare the injection again.',
|
|
141
|
+
|
|
142
|
+
// ── inject panel: result phase ───────────────────────────────────────
|
|
143
|
+
'inject.resultDelivered': 'Delivered: the message was injected into the target session.',
|
|
144
|
+
'inject.resultFailed': 'Injection failed.',
|
|
145
|
+
'inject.resultUnknown':
|
|
146
|
+
'Outcome unknown: the message may have been delivered. Do NOT retry; check the target session before deciding anything.',
|
|
147
|
+
'inject.resultReplayed':
|
|
148
|
+
'Idempotent replay: this is the earlier result of the same request — no second injection happened.',
|
|
149
|
+
'inject.reprepare': 'Prepare again',
|
|
150
|
+
|
|
151
|
+
// ── inject panel: error vocabulary (gateway + transport) ─────────────
|
|
152
|
+
'inject.errInjectDisabled': 'Injection is disabled on the server; enable it in Settings.',
|
|
153
|
+
'inject.errInvalidMessage': 'The message failed server-side validation.',
|
|
154
|
+
'inject.errTargetNotFound':
|
|
155
|
+
'The target session does not exist or left the observation window.',
|
|
156
|
+
'inject.errTargetDead': 'The target session has ended (dead); it cannot be injected.',
|
|
157
|
+
'inject.errTooManyPending':
|
|
158
|
+
'Too many injections are pending confirmation; try again later.',
|
|
159
|
+
'inject.errTokenMissing': 'The confirm token is missing or was never issued; prepare again.',
|
|
160
|
+
'inject.errTokenExpired': 'The confirm token expired; prepare again.',
|
|
161
|
+
'inject.errTokenReused': 'The confirm token was already consumed; prepare again.',
|
|
162
|
+
'inject.errTokenMismatch':
|
|
163
|
+
'The confirmation no longer matches what was prepared; prepare again.',
|
|
164
|
+
'inject.errUnsupportedAgent': 'This agent has no injection path.',
|
|
165
|
+
'inject.errExecutorError': 'The injection path failed while executing.',
|
|
166
|
+
'inject.errTimeout': 'The request timed out without a server receipt.',
|
|
167
|
+
'inject.errAborted': 'The request was cancelled.',
|
|
168
|
+
'inject.errNetwork': 'Network error; the request could not be sent.',
|
|
169
|
+
'inject.errParse': 'The server response could not be parsed.',
|
|
170
|
+
'inject.errGeneric': 'Request failed ({code}).',
|
|
171
|
+
|
|
172
|
+
// ── board tab chrome: main-view switcher ───────────────────────────────
|
|
173
|
+
'board.viewBoard': 'Session board',
|
|
174
|
+
'board.viewProjects': 'Projects',
|
|
175
|
+
|
|
176
|
+
// ── session detail ───────────────────────────────────────────────────
|
|
177
|
+
'detail.header.close': 'Back to board',
|
|
178
|
+
'detail.header.listenOn': 'Listening',
|
|
179
|
+
'detail.header.listenOff': 'Listen',
|
|
180
|
+
'detail.header.listenHint': 'New events append live and get highlighted while on',
|
|
181
|
+
'detail.header.untitled': '(untitled)',
|
|
182
|
+
'detail.header.unknownProject': 'Unknown project',
|
|
183
|
+
'detail.header.observedDisclaimer':
|
|
184
|
+
'Status is an observed value inferred from persisted data and may lag',
|
|
185
|
+
'detail.status.working': 'Working',
|
|
186
|
+
'detail.status.waiting': 'Waiting',
|
|
187
|
+
'detail.status.idle': 'Idle',
|
|
188
|
+
'detail.status.dead': 'Finished',
|
|
189
|
+
'detail.status.unknown': 'Unknown',
|
|
190
|
+
'detail.sources.title': 'Data sources',
|
|
191
|
+
'detail.sources.dshLive': 'dsh live',
|
|
192
|
+
'detail.sources.dshCold': 'dsh cold read',
|
|
193
|
+
'detail.sources.sidecarReplay': 'sidecar replay',
|
|
194
|
+
'detail.sources.sidecarBuffer': 'sidecar buffer',
|
|
195
|
+
'detail.sources.none': 'Unknown source',
|
|
196
|
+
'detail.kind.user': 'User message',
|
|
197
|
+
'detail.kind.assistant': 'Assistant reply',
|
|
198
|
+
'detail.kind.thinking': 'Thinking',
|
|
199
|
+
'detail.kind.toolCall': 'Tool call',
|
|
200
|
+
'detail.kind.toolResult': 'Tool result',
|
|
201
|
+
'detail.kind.turn': 'Turn',
|
|
202
|
+
'detail.kind.step': 'Step',
|
|
203
|
+
'detail.kind.error': 'Error',
|
|
204
|
+
'detail.kind.other': 'Event',
|
|
205
|
+
'detail.gap.label':
|
|
206
|
+
'Gap: about {n} events may be uncaptured (256-slot queue cap or not persisted)',
|
|
207
|
+
'detail.timeline.loadMore': 'Load older history',
|
|
208
|
+
'detail.timeline.loadingMore': 'Loading…',
|
|
209
|
+
'detail.timeline.noMore': 'Start of the timeline',
|
|
210
|
+
'detail.timeline.expand': 'Expand',
|
|
211
|
+
'detail.timeline.collapse': 'Collapse',
|
|
212
|
+
'detail.timeline.newBadge': 'New',
|
|
213
|
+
'detail.timeline.seq': 'seq {n}',
|
|
214
|
+
'detail.timeline.hiddenNotice': '{n} earlier entries collapsed to stay smooth',
|
|
215
|
+
'detail.timeline.showAll': 'Show all',
|
|
216
|
+
'detail.states.loadingTitle': 'Loading the timeline…',
|
|
217
|
+
'detail.states.emptyTitle': 'No events yet',
|
|
218
|
+
'detail.states.emptyHint': 'This session has no normalized events to show yet.',
|
|
219
|
+
'detail.states.errorTitle': 'Timeline failed to load',
|
|
220
|
+
'detail.states.errorFallback': 'Error code: {reason}',
|
|
221
|
+
'detail.states.errors.session_not_found': 'The session does not exist or is no longer visible',
|
|
222
|
+
'detail.states.errors.invalid_cursor': 'The paging cursor is invalid; reopen the detail view',
|
|
223
|
+
'detail.states.errors.fusion_not_wired': 'This host has no timeline capability enabled',
|
|
224
|
+
'detail.states.errors.network_error': 'Network error; the dsh host is unreachable',
|
|
225
|
+
'detail.states.errors.request_timeout': 'The request timed out',
|
|
226
|
+
'detail.time.justNow': 'just now',
|
|
227
|
+
'detail.time.minutesAgo': '{n} min ago',
|
|
228
|
+
'detail.time.hoursAgo': '{n} h ago',
|
|
229
|
+
'detail.time.daysAgo': '{n} d ago',
|
|
230
|
+
|
|
231
|
+
// ── session detail: integration chrome ─────────────────────────────────
|
|
232
|
+
'detail.actions.inject': 'Inject',
|
|
233
|
+
'detail.actions.analyze': 'AI analysis',
|
|
234
|
+
'detail.actions.analyzeDisabledHint':
|
|
235
|
+
'Enable "AI bypass analysis" in Settings to use this',
|
|
236
|
+
|
|
237
|
+
// ── dsh deep-query tools ────────────────────────────────────────────────
|
|
238
|
+
'dshtools.lineage.title': 'Session lineage',
|
|
239
|
+
'dshtools.lineage.loading': 'Loading lineage…',
|
|
240
|
+
'dshtools.lineage.error': 'Lineage failed to load',
|
|
241
|
+
'dshtools.lineage.empty': 'No lineage data',
|
|
242
|
+
'dshtools.lineage.currentBadge': 'Current session',
|
|
243
|
+
'dshtools.lineage.liveBadge': 'Live',
|
|
244
|
+
'dshtools.lineage.notPersistedBadge': 'Not persisted',
|
|
245
|
+
'dshtools.lineage.role.ancestor': 'Ancestor',
|
|
246
|
+
'dshtools.lineage.role.target': 'Target',
|
|
247
|
+
'dshtools.lineage.role.descendant': 'Child session',
|
|
248
|
+
'dshtools.lineage.jumpTitle': 'Jump to this session',
|
|
249
|
+
'dshtools.lineage.currentTitle': 'Currently viewing this session',
|
|
250
|
+
'dshtools.lineage.expand': 'Expand',
|
|
251
|
+
'dshtools.lineage.collapse': 'Collapse',
|
|
252
|
+
'dshtools.lineage.nodeCount': '{n} sessions',
|
|
253
|
+
'dshtools.lineage.incompleteWithId':
|
|
254
|
+
'Lineage incomplete: parent session {id} could not be resolved',
|
|
255
|
+
'dshtools.lineage.incomplete': 'Lineage incomplete: part of the parent chain is unresolved',
|
|
256
|
+
'dshtools.lineage.degrade.notDshTitle': 'Lineage/provenance is dsh-session only',
|
|
257
|
+
'dshtools.lineage.degrade.notDshBody':
|
|
258
|
+
"This session comes from an external agent; dsh's lineage and provenance do not apply.",
|
|
259
|
+
'dshtools.lineage.degrade.queryUnavailableTitle': 'dsh lineage service unavailable',
|
|
260
|
+
'dshtools.lineage.degrade.queryUnavailableBody':
|
|
261
|
+
'This dsh composition has no sessionQuery mounted; lineage and provenance are unavailable.',
|
|
262
|
+
'dshtools.lineage.degrade.traceFailedTitle': 'Lineage trace failed',
|
|
263
|
+
'dshtools.lineage.degrade.traceFailedBody': "dsh could not resolve this session's lineage.",
|
|
264
|
+
'dshtools.lineage.degrade.unknownTitle': 'Lineage unavailable',
|
|
265
|
+
'dshtools.lineage.degrade.unknownBody':
|
|
266
|
+
'The backend reports lineage unavailable (reason: {reason}).',
|
|
267
|
+
'dshtools.search.title': 'Session search',
|
|
268
|
+
'dshtools.search.placeholder': 'Search sessions (title / project / full text)',
|
|
269
|
+
'dshtools.search.submit': 'Search',
|
|
270
|
+
'dshtools.search.loading': 'Searching…',
|
|
271
|
+
'dshtools.search.error': 'Search failed',
|
|
272
|
+
'dshtools.search.empty': 'No matching sessions',
|
|
273
|
+
'dshtools.search.filterOnlyNotice':
|
|
274
|
+
'dsh full-text search is unavailable; degraded to title/project filtering',
|
|
275
|
+
'dshtools.search.projectFilter': 'Project filter: {project}',
|
|
276
|
+
'dshtools.search.matchedBy.full-text': 'Full text',
|
|
277
|
+
'dshtools.search.matchedBy.title': 'Title',
|
|
278
|
+
'dshtools.search.matchedBy.project': 'Project',
|
|
279
|
+
'dshtools.search.matchedBy.other': 'Other',
|
|
280
|
+
'dshtools.search.untitled': '(untitled)',
|
|
281
|
+
|
|
282
|
+
// ── project correlation view ────────────────────────────────────────────
|
|
283
|
+
'project.title': 'Project correlation',
|
|
284
|
+
'project.summary': '{projects} projects · {sessions} sessions',
|
|
285
|
+
'project.crossAgent': '{n} agent kinds',
|
|
286
|
+
'project.sessionCount': '{n} sessions',
|
|
287
|
+
'project.lastActive': 'Last active {time}',
|
|
288
|
+
'project.liveChip': 'Live',
|
|
289
|
+
'project.untitled': '(untitled)',
|
|
290
|
+
'project.empty.title': 'No project correlation yet',
|
|
291
|
+
'project.empty.hint':
|
|
292
|
+
'No cross-agent project activity within the time window; projects appear here once an agent works inside a project directory.',
|
|
293
|
+
'project.loading': 'Loading project correlation…',
|
|
294
|
+
'project.errorTitle': 'Project correlation failed to load',
|
|
295
|
+
|
|
296
|
+
// ── AI bypass analysis panel ────────────────────────────────────────────
|
|
297
|
+
'analysis.title': 'AI analysis',
|
|
298
|
+
'analysis.close': 'Close',
|
|
299
|
+
'analysis.disabledNote':
|
|
300
|
+
'AI bypass analysis is off; enable "AI bypass analysis" in Settings first.',
|
|
301
|
+
'analysis.idleHint':
|
|
302
|
+
'Spin up one dsh bypass-analysis pass over this session on demand (consumes model tokens).',
|
|
303
|
+
'analysis.start': 'Start analysis',
|
|
304
|
+
'analysis.requesting': 'Analyzing… (up to ~60 s)',
|
|
305
|
+
'analysis.exchangeInitial': 'Analysis summary',
|
|
306
|
+
'analysis.followupLabel': 'Follow-up',
|
|
307
|
+
'analysis.truncatedNotice':
|
|
308
|
+
'The input exceeded the budget and was truncated; the analysis covers partial context.',
|
|
309
|
+
'analysis.emptySummary': '(the analysis session returned no summary)',
|
|
310
|
+
'analysis.disclaimerFallback':
|
|
311
|
+
'AI analysis is for reference only; trust the actual session over its conclusions.',
|
|
312
|
+
'analysis.followupPlaceholder': 'Ask a follow-up about this analysis…',
|
|
313
|
+
'analysis.followupSubmit': 'Ask',
|
|
314
|
+
'analysis.answering': 'Answering…',
|
|
315
|
+
'analysis.stop': 'Stop analysis',
|
|
316
|
+
'analysis.stopped': 'Analysis stopped; the analysis session was released.',
|
|
317
|
+
'analysis.restart': 'Analyze again',
|
|
318
|
+
'analysis.noticeTimeout':
|
|
319
|
+
'This follow-up timed out; retry later — the analysis session is kept.',
|
|
320
|
+
'analysis.noticeNetwork':
|
|
321
|
+
'The request could not be sent; retry — the analysis session is kept.',
|
|
322
|
+
'analysis.noticeCancelFailed': 'The stop request did not go through; try again.',
|
|
323
|
+
'analysis.errDisabled': 'AI analysis is disabled on the server; enable it in Settings and retry.',
|
|
324
|
+
'analysis.errUnavailable':
|
|
325
|
+
'This host has no AI analysis capability (agents service unavailable).',
|
|
326
|
+
'analysis.errTargetNotFound': 'The analysis target does not exist or left the observation window.',
|
|
327
|
+
'analysis.errTooManyActive': 'Too many concurrent analysis sessions; try again later.',
|
|
328
|
+
'analysis.errTimeout': 'The analysis timed out and its session was released; start again.',
|
|
329
|
+
'analysis.errCreateFailed': 'Failed to create the analysis session.',
|
|
330
|
+
'analysis.errCancelled': 'The analysis was cancelled.',
|
|
331
|
+
'analysis.errNetwork': 'Network error; the analysis request could not complete.',
|
|
332
|
+
'analysis.errGeneric': 'Analysis failed ({code}).',
|
|
333
|
+
|
|
334
|
+
// ── /sidecar slash command (folded from locales/command.ts) ────────────
|
|
335
|
+
'command.description': 'Sidecar status at a glance (daemon, connection, sessions)',
|
|
336
|
+
'command.daemon.probe': 'Probing',
|
|
337
|
+
'command.daemon.adopted': 'Connected · adopted',
|
|
338
|
+
'command.daemon.defer': 'Waiting for the system service',
|
|
339
|
+
'command.daemon.reprobe': 'Re-probing',
|
|
340
|
+
'command.daemon.hosting': 'Starting',
|
|
341
|
+
'command.daemon.hosted': 'Connected · hosted',
|
|
342
|
+
'command.daemon.backoff': 'Restart backoff',
|
|
343
|
+
'command.daemon.failed': 'Offline',
|
|
344
|
+
'command.daemon.unknown': 'State unknown',
|
|
345
|
+
'command.connection.ok': 'Connected',
|
|
346
|
+
'command.connection.degraded': 'Connection unstable',
|
|
347
|
+
'command.connection.off': 'Offline',
|
|
348
|
+
'command.status.working': 'Working',
|
|
349
|
+
'command.status.waiting': 'Waiting',
|
|
350
|
+
'command.status.idle': 'Idle',
|
|
351
|
+
'command.status.dead': 'Finished',
|
|
352
|
+
'command.status.unknown': 'Unknown',
|
|
353
|
+
'command.daemonRow': 'Sidecar · {state}',
|
|
354
|
+
'command.countsRow': '{working} working · {waiting} waiting',
|
|
355
|
+
'command.countsDetail': '{total} sessions total',
|
|
356
|
+
'command.sessionDetail': '{project} · {status} · {time}',
|
|
357
|
+
'command.noSessions': 'No observed sessions yet',
|
|
358
|
+
'command.unknownProject': 'Unknown project',
|
|
359
|
+
'command.untitled': '(untitled)',
|
|
360
|
+
'command.truncated': '{n} more active sessions not listed',
|
|
361
|
+
'command.boardHint': 'Open the "Sidecar" tab in the conversation view for the full board',
|
|
362
|
+
'command.unreachable': 'sidecar is not connected',
|
|
363
|
+
'command.unreachableHint':
|
|
364
|
+
'The state snapshot could not be fetched; check that the agent-sidecar plugin is enabled and the daemon is available, then retry.',
|
|
365
|
+
'command.offlineFailed': 'sidecar is offline (daemon start failures tripped the breaker)',
|
|
366
|
+
'command.offlineFailedHint':
|
|
367
|
+
'Showing the last snapshot. Retry from the settings card, or run agent-sidecar daemon start manually and wait for adoption.',
|
|
368
|
+
'command.offlineDefer': 'Waiting for the system service to start the daemon',
|
|
369
|
+
'command.offlineDeferHint':
|
|
370
|
+
'A LaunchAgent manages the daemon; the plugin only probes and waits. The overview recovers automatically once the service brings it up.',
|
|
371
|
+
'command.time.justNow': 'just now',
|
|
372
|
+
'command.time.minutesAgo': '{n} min ago',
|
|
373
|
+
'command.time.hoursAgo': '{n} h ago',
|
|
374
|
+
'command.time.daysAgo': '{n} d ago',
|
|
375
|
+
|
|
376
|
+
// ── better-sidebar mini tab (T6.3) ─────────────────────────────────────
|
|
377
|
+
'sidebar.tabTitle': 'Sidecar',
|
|
378
|
+
'sidebar.countsRow': '{working} working · {waiting} waiting',
|
|
379
|
+
'sidebar.recentTitle': 'Recently active',
|
|
380
|
+
'sidebar.connecting': 'Waiting for the sidecar snapshot…',
|
|
381
|
+
'sidebar.noSessions': 'No active sessions',
|
|
382
|
+
'sidebar.noEvent': 'No events recorded yet',
|
|
383
|
+
'sidebar.untitled': '(untitled)',
|
|
384
|
+
'sidebar.boardHint': 'Full board: the "Sidecar" tab in the conversation view',
|
|
385
|
+
} satisfies Record<SidecarLocaleKey, string>
|