dsh-lark-agent 0.4.0 → 0.5.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 (39) hide show
  1. package/README.md +6 -3
  2. package/dist/bridge.d.ts +2 -1
  3. package/dist/bridge.js +23 -13
  4. package/dist/client/AgentSettings.d.ts +12 -0
  5. package/dist/client/ConnectionCard.d.ts +5 -1
  6. package/dist/client/ManagerPanel.d.ts +3 -0
  7. package/dist/client/api.d.ts +14 -2
  8. package/dist/client/index.d.ts +1 -1
  9. package/dist/client/manager-styles.d.ts +2 -0
  10. package/dist/client/use-connection.d.ts +2 -2
  11. package/dist/client.cjs +727 -258
  12. package/dist/config.d.ts +4 -0
  13. package/dist/config.js +8 -0
  14. package/dist/dsh-runtime.d.ts +3 -0
  15. package/dist/dsh-runtime.js +44 -4
  16. package/dist/lark-cli.d.ts +7 -0
  17. package/dist/lark-cli.js +105 -1
  18. package/dist/media-input.d.ts +28 -0
  19. package/dist/media-input.js +127 -0
  20. package/dist/media-scopes.d.ts +5 -0
  21. package/dist/media-scopes.js +6 -0
  22. package/dist/message.d.ts +7 -0
  23. package/dist/message.js +27 -3
  24. package/dist/onboarding/agents.d.ts +9 -0
  25. package/dist/onboarding/agents.js +100 -0
  26. package/dist/onboarding/controller.d.ts +15 -2
  27. package/dist/onboarding/controller.js +155 -14
  28. package/dist/onboarding/host.d.ts +14 -2
  29. package/dist/onboarding/host.js +45 -16
  30. package/dist/onboarding/lark-platform.d.ts +6 -0
  31. package/dist/onboarding/lark-platform.js +15 -4
  32. package/dist/onboarding/manager.d.ts +32 -0
  33. package/dist/onboarding/manager.js +129 -0
  34. package/dist/onboarding/protocol.d.ts +51 -1
  35. package/dist/onboarding/settings-store.d.ts +11 -0
  36. package/dist/onboarding/settings-store.js +67 -0
  37. package/dist/session-store.d.ts +5 -1
  38. package/dist/session-store.js +32 -4
  39. package/package.json +1 -1
package/README.md CHANGED
@@ -35,7 +35,7 @@ In the sidebar, open **插件 → 添加插件** (Plugins → Add plugin), enter
35
35
  [`dsh plugin`](https://www.npmjs.com/package/@deepseek-ai/dsh) runs pnpm, so pnpm must be on your PATH:
36
36
 
37
37
  ```sh
38
- dsh plugin --profile web add dsh-lark-agent --allow-build=protobufjs
38
+ dsh plugin --profile web add dsh-lark-agent@0.5.0 --allow-build=protobufjs
39
39
  ```
40
40
 
41
41
  Then restart `dsh web`.
@@ -51,7 +51,7 @@ Then restart `dsh web`.
51
51
 
52
52
  ### Update
53
53
 
54
- When a new version has been out for a day, the Feishu settings page shows a notice with these steps (in the desktop app it also opens the plugin list for you). Until then the desktop app and `dsh plugin` still install the previous version, because pnpm 11 skips releases less than 24 hours old. If an install picks an older version that your DeepSeek Harness rejects as incompatible, see [Troubleshooting](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md#排障).
54
+ When a new version has been out for a day, the Feishu settings page shows a notice with these steps (in the desktop app it also opens the plugin list for you). Until then, installs without an exact version, including `@latest`, still select the previous version because pnpm 11 skips releases less than 24 hours old. Use the exact version in the installation example above to install this release immediately after publication. If an install picks an older version that your DeepSeek Harness rejects as incompatible, see [Troubleshooting](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md#排障).
55
55
 
56
56
  1. On the Feishu settings page, click **暂停连接** (pause) and back up as described in [Upgrade](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md#升级).
57
57
  2. Desktop app: the Add plugin dialog says third-party plugins must be uninstalled before a new version is installed, so uninstall `dsh-lark-agent` on the Plugins page and add it again (this in-app upgrade hasn't been tested end to end yet; an earlier source build is listed as `dsh-larkbot`). CLI: run `dsh plugin --profile web update dsh-lark-agent@latest`, then restart `dsh web`.
@@ -61,7 +61,7 @@ When a new version has been out for a day, the Feishu settings page shows a noti
61
61
 
62
62
  Button labels are quoted in Chinese, as they appear, with an English gloss.
63
63
 
64
- 1. Open the Feishu settings page — desktop app: **设置 → 内置插件 → 飞书** (Settings → Built-in plugins → Feishu); web: **设置 → 插件 → 飞书** (Settings → Plugins → Feishu) — and click **扫码创建并连接** (scan to create and connect).
64
+ 1. Open **设置 → 飞书 agent** (Settings → Feishu agent) in the desktop app, or **设置 → 插件 → 飞书 agent** on the web. Choose a robot and click **扫码创建并连接** (scan to create and connect). Use **添加机器人** (add robot) for another Agent; each robot has its own preset and workspace settings.
65
65
  2. Scan the QR code with your own Feishu account and approve. If your organization requires admin approval, click **重新检查** (check again) once it is approved.
66
66
  3. Click **打开飞书私聊** (open private chat) and, from the account that scanned, send the pairing word (`DSH-…`, valid for 10 minutes) as a plain-text message on its own, exactly as shown. If asked, confirm with **这是我,完成连接** (this is me).
67
67
  4. When the page shows **已连接** (connected), send the bot a message.
@@ -73,6 +73,9 @@ The connection's model check does not create a “飞书连接测试” conversa
73
73
  | In Feishu, you… | The assistant… |
74
74
  | --- | --- |
75
75
  | Send a request | Works on it with Harness's own tools (web search, reading pages, workspace files and more), streaming the reply live into one card; its thinking and tool steps are folded inside, step by step as in the app, and the card ends with the model, thinking depth and permission mode it used |
76
+ | Use several assistants | Manages up to 10 Feishu robots from one page, each with its own Agent preset, workspace, connection and history; pause a robot to edit its settings. Preset selection is available when the host provides it |
77
+ | Send images in your private Harness chat | Reads PNG, JPEG, WebP or GIF through the host's durable attachments, when the selected model supports images; up to 4 images, 10 MB each |
78
+ | Send a voice message | Use **开通语音权限** (authorize voice) for that robot, then pause, enable and save voice input. Transcribes up to 60 seconds of Chinese / English speech and replies in text; requires FFmpeg and a tenant plan that supports ASR |
76
79
  | Ask for something that needs your OK | Sends a confirmation card: tap **允许一次** (allow once) or **拒绝** (deny); no answer within 5 minutes means it won't run |
77
80
  | Get asked to choose by Harness | Sends a choice card: tap an option, or fill it in and submit |
78
81
  | Use `/permission`, or **设置 → 权限模式** (Settings → Permission mode) in the bottom menu | Switches between read-only, workspace edits and full access, like the app's permission selector; full access stops asking. The card you tap updates in place and turns green when done |
package/dist/bridge.d.ts CHANGED
@@ -11,6 +11,7 @@ import type { PermissionPreferencesStore } from './permission-preferences.js';
11
11
  import type { ArtifactStore } from './artifacts.js';
12
12
  import type { ArtifactPublisher } from './artifact-publisher.js';
13
13
  import { type TopicStore, type TopicReceipt } from './topic-store.js';
14
+ import { type MediaTransport } from './media-input.js';
14
15
  export interface Runtime {
15
16
  create(policy: AgentPolicy): Promise<RuntimeSession>;
16
17
  resume(policy: AgentPolicy, id: string): Promise<RuntimeSession>;
@@ -35,7 +36,7 @@ export interface BotState {
35
36
  permissionPreferences?: Pick<PermissionPreferencesStore, 'get' | 'set'>;
36
37
  topics?: Pick<TopicStore, 'get' | 'list' | 'record' | 'occupied'>;
37
38
  }
38
- export interface Transport {
39
+ export interface Transport extends MediaTransport {
39
40
  reply(messageId: string, text: string, options: {
40
41
  idempotencyKey: string;
41
42
  inThread: boolean;
package/dist/bridge.js CHANGED
@@ -17,6 +17,7 @@ import { answeredText, approvalCard, questionAnswers, questionCard, simpleQuesti
17
17
  import { permissionPanel, permissionStateText } from './permission-controls.js';
18
18
  import { TopicStoreError } from './topic-store.js';
19
19
  import { sessionControlPanel, sessionPage, sessionQuery, sessionViewCommandParse } from './session-controls.js';
20
+ import { MediaInputError, prepareMedia } from './media-input.js';
20
21
  class ArtifactDeliveryError extends Error {
21
22
  }
22
23
  /** A creation may have reached Lark. Never send another message as a fallback. */
@@ -79,11 +80,13 @@ const HELP = `直接把想做的事告诉我就好。
79
80
  比如:帮我理清一个问题、整理一段文字,或者接着刚才的话题继续。你也可以在私聊告诉我,以后希望我怎样配合你。
80
81
 
81
82
  私聊底部菜单可以新建话题、搜索和选择历史对话,或调整模型和思考深度。
83
+ 多个机器人与 Agent 可在电脑的「飞书 agent」设置中分别管理。
82
84
  私聊和群聊的新问题都会开一个飞书话题;新话题卡片下方点「回复话题」继续,群聊中仍需 @我。
83
85
  也可以发 /new 换话题、/sessions 找对话、/model 看模型、/thinking 调深度、/stop 停下来。`;
84
86
  const HARNESS_HELP = `
85
87
 
86
- 这里连着你电脑上的 Harness:可以让我查资料、读网页、处理工作区里的文件,回复会实时显示,思考过程折叠在卡片里;需要你确认的操作,我会先发卡片问你。发 /permission(或点底部菜单 设置 → 权限模式)可以切换权限,比如改成不再询问的「完全权限」。模型、思考深度和权限模式与电脑上的 Harness 同步。`;
88
+ 这里连着你电脑上的 Harness:可以让我查资料、读网页、处理工作区里的文件,回复会实时显示,思考过程折叠在卡片里;需要你确认的操作,我会先发卡片问你。发 /permission(或点底部菜单 设置 → 权限模式)可以切换权限,比如改成不再询问的「完全权限」。模型、思考深度和权限模式与电脑上的 Harness 同步。
89
+ 可直接发图片(需要宿主与模型支持图片输入);在电脑设置中启用语音后,可发送 60 秒以内语音,识别成文字继续对话。`;
87
90
  const ADVANCED_HELP = `高级命令(按需使用):
88
91
  /new 或 /reset:创建新的飞书话题,在新话题里开始;旧话题和长期记忆保留
89
92
  /sessions:搜索、翻页并选择以前的对话;/sessions detail 查看 ID,/sessions page <页码> 查看其他页
@@ -324,9 +327,9 @@ export class BotBridge {
324
327
  message.foreignTopic = !message.topic && await this.state.topics.occupied(this.topicScope(message).chatKey, message.threadId);
325
328
  }
326
329
  // Recognized settings questions use the entry lane before any provisional topic context is allocated.
327
- if (this.state.modelPreferences && this.runtime.catalog && isModelQuestion(message.text))
330
+ if (!message.resources?.length && this.state.modelPreferences && this.runtime.catalog && isModelQuestion(message.text))
328
331
  message.text = '/model current';
329
- const startsTopic = this.topicsEnabled() && !message.threadId && !message.text.startsWith('/');
332
+ const startsTopic = this.topicsEnabled() && !message.threadId && (!message.text.startsWith('/') || Boolean(message.resources?.length));
330
333
  const key = message.topic?.laneKey ?? (startsTopic ? this.newTopicKey(message) : sessionKey(message, this.config));
331
334
  if (startsTopic)
332
335
  message.conversationKey = key;
@@ -423,6 +426,9 @@ export class BotBridge {
423
426
  message.conversationKey = this.entryKey(message);
424
427
  }
425
428
  let text = message.text;
429
+ if (message.resources?.length && (message.chatType !== 'p2p' || lane.user.directMode !== 'harness')) {
430
+ return this.reply(message, '图片和语音输入仅用于已验证本人的 Harness 私聊,请改发文字。');
431
+ }
426
432
  const inPlace = this.inPlace(message);
427
433
  // A stale card is redrawn with the current state instead of answered with another message.
428
434
  if (message.expectedSessionId !== undefined) {
@@ -483,7 +489,7 @@ export class BotBridge {
483
489
  return this.reply(message, error instanceof PresentationError ? error.message : '作品暂时无法读取,已有内容会保留。');
484
490
  }
485
491
  }
486
- if (text.startsWith('/')) {
492
+ if (text.startsWith('/') && !message.resources?.length) {
487
493
  if (inPlace)
488
494
  return this.panelInPlace(message, lane);
489
495
  const feedback = this.topicsEnabled() && /^\/(?:new|reset|clear|resume)(?:\s|$)/i.test(text)
@@ -550,6 +556,8 @@ export class BotBridge {
550
556
  this.audit({ stage: 'live-card-unconfirmed', messageId: message.messageId });
551
557
  };
552
558
  try {
559
+ const input = await prepareMedia({ ...message, text }, this.transport, controller.signal, { images: this.config.imageInput !== false, voice: this.config.voiceInput === true, maxText: this.config.maxInputChars });
560
+ text = input.text;
553
561
  await this.ensureSession(message, lane);
554
562
  if (controller.signal.aborted) {
555
563
  if (timedOut)
@@ -572,7 +580,7 @@ export class BotBridge {
572
580
  else
573
581
  this.liveUnavailableUntil = Date.now() + LIVE_RETRY_MS;
574
582
  }
575
- const turnContext = { memoryText, ...(artifactsText ? { artifactsText } : {}) };
583
+ const turnContext = { memoryText, ...(artifactsText ? { artifactsText } : {}), ...(input.images ? { images: input.images } : {}) };
576
584
  // Only the owner's harness-mode chat can be asked anything; a restricted session gets no hooks at all.
577
585
  const observer = live || harness ? {
578
586
  ...(live ? { stream: (event) => live?.push(event) } : {}),
@@ -612,7 +620,7 @@ export class BotBridge {
612
620
  clearTimeout(timer);
613
621
  if (error instanceof ArtifactDeliveryError)
614
622
  throw error;
615
- const known = error instanceof DshRuntimeError || error instanceof MemoryStoreError || error instanceof SessionStoreError || error instanceof ModelPreferencesError || error instanceof PresentationError;
623
+ const known = error instanceof DshRuntimeError || error instanceof MemoryStoreError || error instanceof SessionStoreError || error instanceof ModelPreferencesError || error instanceof PresentationError || error instanceof MediaInputError;
616
624
  if (live) {
617
625
  const stopped = timedOut || controller.signal.aborted || this.stopped;
618
626
  await conclude({ phase: stopped ? 'stopped' : 'failed', note: timedOut ? TIMEOUT_TEXT : this.stopped ? STOPPING_TEXT
@@ -781,7 +789,7 @@ export class BotBridge {
781
789
  async deliverFiles(message, lane, files, signal) {
782
790
  if (!files.length || signal.aborted || this.stopped)
783
791
  return;
784
- const workspace = await realpath(lane.user.cwd).catch(() => undefined);
792
+ const workspace = await realpath(lane.saved?.cwd ?? lane.user.cwd).catch(() => undefined);
785
793
  const notes = [];
786
794
  for (const [index, file] of files.entries()) {
787
795
  if (signal.aborted || this.stopped)
@@ -1046,11 +1054,13 @@ export class BotBridge {
1046
1054
  return binding.link ? `已准备好「${title}」。[打开话题继续](${binding.link})。`
1047
1055
  : `已准备好「${title}」。请在聊天记录中打开对应话题继续。`;
1048
1056
  }
1049
- policy(message, lane, selection) {
1057
+ policy(message, lane, selection, cwd) {
1050
1058
  const context = this.context(message);
1059
+ const harness = message.chatType === 'p2p' && lane.user.directMode === 'harness';
1051
1060
  return {
1052
- cwd: lane.user.cwd, allowedTools: lane.user.allowedTools,
1053
- mode: message.chatType === 'p2p' && lane.user.directMode === 'harness' ? 'harness' : 'restricted',
1061
+ cwd: harness && cwd ? cwd : lane.user.cwd, allowedTools: lane.user.allowedTools,
1062
+ mode: harness ? 'harness' : 'restricted',
1063
+ ...(harness && lane.user.agentPreset ? { agentPreset: lane.user.agentPreset } : {}),
1054
1064
  provider: this.config.provider, model: this.config.model,
1055
1065
  presentation: Boolean(this.state.artifacts),
1056
1066
  ...selection,
@@ -1096,7 +1106,7 @@ export class BotBridge {
1096
1106
  // Like the App: a shared session starts on the host default and resumes on its own record.
1097
1107
  const selection = this.sharedModel(message, lane) ? undefined : modelPreference?.selection;
1098
1108
  const handle = saved
1099
- ? await this.runtime.resume(this.policy(message, lane, selection), saved.nativeId)
1109
+ ? await this.runtime.resume(this.policy(message, lane, selection, saved.cwd), saved.nativeId)
1100
1110
  : await this.runtime.create(this.policy(message, lane, selection));
1101
1111
  try {
1102
1112
  await handle.checkpoint();
@@ -1105,7 +1115,7 @@ export class BotBridge {
1105
1115
  // Publish a route only after even the empty native log is durable, and
1106
1116
  // before submitting the first prompt. An abandoned creation has no work.
1107
1117
  lane.saved = saved ? await this.bindRestoredSession(context, saved, handle)
1108
- : await this.state.sessions.add(context, handle.id, preview(message.text, 40) || '新会话');
1118
+ : await this.state.sessions.add(context, handle.id, preview(message.text, 40) || '新会话', lane.user.cwd);
1109
1119
  if (message.topic && !message.topic.sessionId) {
1110
1120
  message.topic = await this.state.topics.record({ ...message.topic, sessionId: lane.saved.id });
1111
1121
  }
@@ -1233,7 +1243,7 @@ export class BotBridge {
1233
1243
  return `已经在会话「${target.title}」中。`;
1234
1244
  await this.unload(lane);
1235
1245
  const preference = await this.state.modelPreferences?.get(context);
1236
- const handle = await this.runtime.resume(this.policy(message, lane, this.sharedModel(message, lane) ? undefined : preference?.selection), target.nativeId);
1246
+ const handle = await this.runtime.resume(this.policy(message, lane, this.sharedModel(message, lane) ? undefined : preference?.selection, target.cwd), target.nativeId);
1237
1247
  try {
1238
1248
  await handle.checkpoint();
1239
1249
  await this.applyPermission(message, lane, handle, false);
@@ -0,0 +1,12 @@
1
+ import type { AgentCatalog, AgentSettings as Settings, ConnectionSnapshot } from '../onboarding/protocol.js';
2
+ import type { MediaInputKind } from '../media-scopes.js';
3
+ export declare function AgentSettings({ snapshot, catalog, pending, save, authorize, cancelAuthorization, recheck, now }: {
4
+ snapshot: ConnectionSnapshot;
5
+ catalog?: AgentCatalog;
6
+ pending: boolean;
7
+ authorize(kind: MediaInputKind): void;
8
+ cancelAuthorization(): void;
9
+ recheck(): void;
10
+ now: number;
11
+ save(settings: Settings): Promise<ConnectionSnapshot | undefined>;
12
+ }): import("react/jsx-runtime").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import type { AgentCatalog, ConnectionSnapshot } from '../onboarding/protocol.js';
1
2
  import type { ConnectionApi } from './api.js';
2
3
  /** `app` is the desktop App; `openPlugins` switches to its plugin list and reports whether it could. */
3
4
  export interface ConnectionCardProps {
@@ -6,6 +7,9 @@ export interface ConnectionCardProps {
6
7
  app: boolean;
7
8
  openPlugins?: () => boolean;
8
9
  };
10
+ embedded?: boolean;
11
+ catalog?: AgentCatalog;
12
+ onSnapshot?: (snapshot: ConnectionSnapshot) => void;
9
13
  }
10
14
  /** The host is authoritative for every transition, including readiness and identity. */
11
- export declare function ConnectionCard({ api, host }: ConnectionCardProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function ConnectionCard({ api, host, embedded, catalog, onSnapshot }: ConnectionCardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import { type ConnectionCardProps } from './ConnectionCard.js';
2
+ /** A dedicated management page, backed by the same owner-only connection API on both hosts. */
3
+ export declare function ManagerPanel({ api, host }: ConnectionCardProps): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,4 @@
1
- import type { ConnectionEndpoint, ConnectionProfile, ConnectionSnapshot } from '../onboarding/protocol.js';
1
+ import type { AgentCatalog, ConnectionEndpoint, ConnectionList, ConnectionProfile, ConnectionSnapshot } from '../onboarding/protocol.js';
2
2
  /** Browser transport supplied by either generation of the DSH host. */
3
3
  export interface ConnectionRpc {
4
4
  call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<unknown>;
@@ -7,6 +7,8 @@ export interface ConnectionRpc {
7
7
  export declare function officialUrl(value: unknown): string;
8
8
  /** Validate host data before rendering it or making its URLs actionable. */
9
9
  export declare function parseSnapshot(value: unknown): ConnectionSnapshot;
10
+ export declare function parseConnectionList(value: unknown): ConnectionList;
11
+ export declare function parseAgentCatalog(value: unknown): AgentCatalog;
10
12
  /** A vetted, user-facing service error; raw transport errors are never displayed. */
11
13
  export declare class ConnectionUiError extends Error {
12
14
  }
@@ -14,8 +16,18 @@ export declare class ConnectionUiError extends Error {
14
16
  export declare class ConnectionApi {
15
17
  private readonly rpc;
16
18
  private readonly modern;
17
- constructor(rpc: ConnectionRpc, modern?: boolean);
19
+ private readonly id?;
20
+ constructor(rpc: ConnectionRpc, modern?: boolean, id?: string | undefined);
21
+ forConnection(id: string): ConnectionApi;
18
22
  private request;
19
23
  snapshot(endpoint: Exclude<ConnectionEndpoint, 'profiles'>, payload: unknown, signal: AbortSignal): Promise<ConnectionSnapshot>;
20
24
  profiles(signal: AbortSignal): Promise<ConnectionProfile[]>;
25
+ connections(signal: AbortSignal): Promise<ConnectionList>;
26
+ addConnection(signal: AbortSignal): Promise<ConnectionList & {
27
+ selectedId: string;
28
+ }>;
29
+ removeConnection(id: string, signal: AbortSignal): Promise<ConnectionList & {
30
+ selectedId: string;
31
+ }>;
32
+ agentCatalog(signal: AbortSignal): Promise<AgentCatalog>;
21
33
  }
@@ -1,6 +1,6 @@
1
1
  import type { ComponentType } from 'react';
2
2
  import { type ConnectionRpc } from './api.js';
3
- import { type ConnectionCardProps } from './ConnectionCard.js';
3
+ import type { ConnectionCardProps } from './ConnectionCard.js';
4
4
  /** Minimal public services shared by the Web and official desktop hosts. */
5
5
  export interface ClientContext {
6
6
  effect(factory: () => void | (() => void), label?: string): unknown;
@@ -0,0 +1,2 @@
1
+ /** Geometry and controls belong only to the plugin's management body. */
2
+ export declare const managerCss = "\n.dsh-lark-settings.lark-manager{container:lark-manager / inline-size;max-width:1160px;width:100%;min-width:0;padding:8px 0 24px;--lark-ink:#16263f;--lark-muted:#68788f;--lark-blue:#2965e8;--lark-soft:#f6f8fc}\n.lark-manager-header{display:flex;justify-content:space-between;align-items:center;gap:24px;margin-bottom:26px}.lark-eyebrow{color:var(--lark-blue);font:600 10px/1.5 ui-monospace,SFMono-Regular,monospace;letter-spacing:.16em;margin-bottom:5px}.lark-manager-header h2{font:650 26px/1.4 \"Avenir Next\",\"PingFang SC\",sans-serif;letter-spacing:-.7px;margin:0}.lark-manager-header p{font-size:13px;color:var(--lark-muted);margin:5px 0 0}.lark-manager-tools{display:flex;gap:9px;align-items:center;flex-shrink:0}\n.lark-manager-layout{display:grid;grid-template-columns:252px minmax(0,1fr);gap:28px;align-items:start}.lark-robot-sidebar{min-width:0;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:14px;padding:17px 12px}.lark-robot-count{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:0 5px 15px}.lark-robot-count strong{font-size:13px;font-weight:600}.lark-robot-count>span{font-size:11px;color:var(--lark-muted);font-variant-numeric:tabular-nums}\n.lark-manager input,.lark-manager select{font:inherit;color:var(--lark-ink);background:var(--lark-surface);border:1px solid var(--lark-line);border-radius:8px;min-height:39px;padding:8px 11px;max-width:100%;width:100%}.lark-manager input:disabled,.lark-manager select:disabled{opacity:.75;cursor:not-allowed}.lark-manager select:focus-visible{outline:3px solid #a1b8ff;outline-offset:3px}.lark-manager .lark-robot-search{font-size:12px;min-height:35px;margin-bottom:13px}.lark-robot-list{display:flex;flex-direction:column;gap:8px}.lark-robot-item{width:100%;display:flex;align-items:flex-start;gap:10px;border:1px solid transparent;border-radius:10px;background:transparent;color:var(--lark-ink);text-align:left;padding:13px 10px;position:relative;transition:background .15s}.lark-robot-item:hover{background:var(--lark-surface)}.lark-robot-item.selected{background:var(--lark-surface);border-color:#c9d9fc;box-shadow:0 2px 6px #16377906}.lark-robot-item.selected:before{content:\"\";position:absolute;left:-13px;top:18px;bottom:18px;width:3px;border-radius:2px;background:var(--lark-blue)}.lark-robot-icon{width:34px;height:34px;display:grid;place-items:center;flex-shrink:0;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:9px}.lark-robot-icon img{width:25px;height:25px;object-fit:contain}.lark-robot-description{min-width:0;display:flex;flex-direction:column;gap:5px;flex:1}.lark-robot-description>strong{font-size:13px;font-weight:600;overflow-wrap:anywhere;line-height:1.5}.lark-robot-description>small{font-size:10px;color:var(--lark-muted);overflow-wrap:anywhere}.lark-robot-chevron{color:var(--lark-muted);align-self:center;font-size:20px}.lark-robot-item.selected .lark-robot-chevron{color:var(--lark-blue)}.lark-robot-phase{display:flex;align-items:center;gap:5px;color:var(--lark-muted);font-size:10px;line-height:1.5}.lark-robot-phase>i{width:5px;height:5px;border-radius:50%;background:currentColor}.lark-robot-phase.connected{color:#13886f}.lark-robot-phase.error,.lark-robot-phase.action_required{color:#a26714}.lark-robot-note{margin:22px 5px 0;border-top:1px solid var(--lark-line);padding-top:16px;color:var(--lark-muted)}.lark-robot-note strong{font-size:11px;font-weight:600}.lark-robot-note p{font-size:11px;line-height:1.8;margin:6px 0 0}.lark-list-empty{font-size:12px;padding:14px 5px;color:var(--lark-muted)}\n.lark-robot-detail{min-width:0}.dsh-lark-settings.lark-detail-card{max-width:none;padding:0;margin:0;--lark-blue:#2965e8;--lark-soft:#f6f8fc}.lark-detail-card .lark-card-header{padding:20px 25px}.lark-detail-card .lark-card-header h2{font-size:17px}.lark-detail-card .lark-body{padding:24px 26px}.lark-detail-card .lark-connected-row{margin-top:20px;padding:14px 0}.lark-detail-card .lark-connected-row+.lark-connected-row{border-top:0;margin-top:0}.lark-detail-card .lark-connected-row p{line-height:1.8;max-width:340px}.lark-connection-summary{display:flex;align-items:center;gap:12px;border-radius:9px;background:#f0faf6;border:1px solid #d5eee3;padding:13px 15px}.lark-connection-dot{width:8px;height:8px;background:#13886f;border-radius:50%;flex-shrink:0;box-shadow:0 0 0 4px #13886f10}.lark-connection-summary strong{font-size:13px;font-weight:600}.lark-connection-summary p{font-size:12px;color:var(--lark-muted);margin:3px 0 0}.lark-connection-summary.paused{background:var(--lark-soft);border-color:var(--lark-line)}.lark-connection-summary.paused .lark-connection-dot{background:var(--lark-muted);box-shadow:none}\n.lark-agent-settings{border-top:1px solid var(--lark-line);margin-top:25px;padding-top:22px}.lark-section-heading{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:19px}.lark-body .lark-section-heading h3{font-size:14px;font-weight:650;margin:0;letter-spacing:0}.lark-section-heading>span{font-size:11px;color:var(--lark-muted)}.lark-field{margin-bottom:19px}.lark-field>label{display:block;font-size:12px;font-weight:550;margin-bottom:7px}.lark-field>p,.lark-model-summary>p,.lark-field-help{font-size:11px;color:var(--lark-muted);line-height:1.8;margin:6px 0 0;overflow-wrap:anywhere}.lark-manager input.lark-path-input{font-family:ui-monospace,SFMono-Regular,\"PingFang SC\",monospace;font-size:12px}.lark-field>p.lark-workspace-path{font-family:ui-monospace,SFMono-Regular,\"PingFang SC\",monospace;font-size:10px}.lark-model-summary{padding:13px 15px;background:var(--lark-soft);border-radius:9px}.lark-model-summary>span{font-size:12px}.lark-model-summary>strong{float:right;max-width:65%;font-size:12px;font-weight:550;overflow-wrap:anywhere}.lark-model-summary>p{clear:both;padding-top:4px}.lark-input-heading{margin-top:24px;margin-bottom:5px}.lark-input-option{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:15px 0;border-bottom:1px solid var(--lark-line);cursor:pointer}.lark-input-option>span{min-width:0}.lark-input-option strong{display:block;font-size:12px;font-weight:550}.lark-input-option small{display:block;font-size:11px;color:var(--lark-muted);line-height:1.8;margin-top:4px}.lark-manager .lark-input-option input{width:17px;height:17px;min-height:0;padding:0;flex-shrink:0;accent-color:var(--lark-blue)}.lark-settings-save{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-top:22px}.lark-settings-save>span{font-size:11px;color:var(--lark-muted)}\n.lark-connection-details{font-size:12px;color:var(--lark-muted);margin-top:14px}.lark-connection-details>summary{padding:8px 0}.lark-detail-card .lark-connection-details .lark-connected-row{margin-top:10px}.lark-detail-card .lark-recheck{margin-top:10px}.lark-manager>.lark-disconnect{margin:0 0 20px}\n.lark-manager .lark-text-button{color:var(--lark-blue)}.lark-manager input::placeholder{color:var(--lark-muted);opacity:1}.lark-manager{color-scheme:light}\n.lark-input-capability{padding:14px 0;border-bottom:1px solid var(--lark-line)}.lark-input-capability .lark-input-option{padding:0;border:0;align-items:flex-start}.lark-input-option strong{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.lark-input-option em{font-style:normal;font-size:10px;font-weight:450;color:var(--lark-muted);background:var(--lark-soft);padding:2px 6px;border-radius:4px}.lark-input-option input{margin-top:3px}.lark-input-requirements{display:grid;gap:4px;font-size:11px;line-height:1.8;color:var(--lark-muted);margin-top:10px;overflow-wrap:anywhere}.lark-input-footer{margin-top:14px}.lark-input-authorization{border:1px solid var(--lark-line);background:var(--lark-soft);border-radius:9px;padding:15px;margin-top:16px}.lark-input-authorization>strong{font-size:13px}.lark-input-authorization .lark-qr{margin-top:14px}.lark-input-authorization .lark-actions{margin-top:14px}\n@media(prefers-color-scheme:dark){.lark-manager,.lark-detail-card{color-scheme:dark}}\n@media(prefers-color-scheme:dark){.dsh-lark-settings.lark-manager,.dsh-lark-settings.lark-detail-card{--lark-ink:#e0e7f1;--lark-muted:#96a6bd;--lark-soft:#283448;--lark-blue:#7ba0ff}.lark-robot-item.selected{border-color:#465e8d}.lark-connection-summary{background:#213b35;border-color:#36574d}.lark-connection-summary.paused{background:var(--lark-soft);border-color:var(--lark-line)}.lark-robot-phase.connected{color:#78c5b0}.lark-robot-phase.error,.lark-robot-phase.action_required{color:#e4bd7c}.lark-manager .lark-primary{background:#4268df}.lark-connection-dot{background:#78c5b0}}\n.lark-manager-header>div:first-child,.lark-card-header>div{min-width:0}.lark-manager-tools{flex-wrap:wrap}.lark-detail-card{container:lark-detail / inline-size}.lark-detail-card .lark-card-header>div{flex:1}.lark-detail-card .lark-card-header h2{overflow-wrap:anywhere}.lark-detail-card .lark-card-header p{display:block}.lark-detail-card .lark-status{flex-shrink:0}.lark-detail-card .lark-steps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.lark-detail-card .lark-steps li{min-width:0;white-space:normal}.lark-detail-card .lark-steps li+li:before{display:none}.lark-detail-card .lark-steps li>span{flex-shrink:0}.lark-detail-card .lark-connected-row{flex-wrap:wrap}.lark-detail-card .lark-qr-layout{flex-wrap:wrap}.lark-detail-card .lark-qr-layout>div{min-width:0}.lark-detail-card .lark-loading-row h3{overflow-wrap:anywhere}\n@container lark-manager (max-width:900px){.lark-manager-layout{grid-template-columns:205px minmax(0,1fr);gap:18px}.lark-manager-header{gap:15px}.lark-manager-header h2{font-size:23px}.lark-detail-card .lark-body{padding:21px}.lark-section-heading{flex-wrap:wrap;gap:4px}.lark-robot-count{flex-wrap:wrap;gap:4px}.lark-robot-item{padding:11px 6px;gap:8px}}\n@container lark-manager (max-width:720px){.lark-manager-header{align-items:flex-start;flex-direction:column;gap:16px;margin-bottom:20px}.lark-manager-tools{width:100%;justify-content:flex-start}.lark-manager-layout{display:flex;flex-direction:column;gap:18px}.lark-robot-sidebar,.lark-robot-detail{width:100%}.lark-robot-sidebar{padding:12px}.lark-robot-count{padding-bottom:10px}.lark-robot-list{flex-direction:row;overflow-x:auto;gap:8px;padding-bottom:3px}.lark-robot-item{min-width:180px;max-width:230px;flex:0 0 auto;padding:11px}.lark-robot-item.selected:before{display:none}.lark-robot-note{display:none}.lark-manager .lark-robot-search{margin-bottom:10px}.lark-detail-card .lark-card-header{padding:17px}.lark-detail-card .lark-body{padding:20px 17px}.lark-model-summary>strong{max-width:60%}}\n@container lark-detail (max-width:420px){.lark-detail-card .lark-card-header{display:grid;grid-template-columns:37px minmax(0,1fr);gap:4px 10px}.lark-detail-card .lark-plugin-icon{width:37px;height:37px;grid-row:1 / 3}.lark-detail-card .lark-status{grid-column:2;margin-left:0}.lark-detail-card .lark-card-header h2{font-size:16px}.lark-detail-card .lark-card-header p{font-size:11px}.lark-detail-card .lark-steps li{flex-direction:column;text-align:center;gap:5px;font-size:11px}.lark-detail-card .lark-body h3{font-size:18px}.lark-detail-card .lark-section-heading h3{font-size:14px}.lark-detail-card .lark-section-heading{flex-wrap:wrap;gap:4px}.lark-detail-card .lark-pairing{flex-direction:column;align-items:flex-start}.lark-detail-card .lark-qr-layout{flex-direction:column;align-items:center}.lark-detail-card .lark-model-summary>strong{float:none;display:block;max-width:none;margin-top:4px}.lark-detail-card .lark-settings-save{flex-wrap:wrap}}\n";
@@ -8,7 +8,7 @@ export declare function useConnection(api: ConnectionApi): {
8
8
  pending: ConnectionEndpoint | null;
9
9
  profiles: ConnectionProfile[] | null;
10
10
  refresh: () => void;
11
- mutate: (endpoint: Mutation | "profiles", payload?: unknown) => Promise<void>;
12
- loadProfiles: () => Promise<void>;
11
+ mutate: (endpoint: Mutation | "profiles", payload?: unknown) => Promise<ConnectionSnapshot | undefined>;
12
+ loadProfiles: () => Promise<ConnectionSnapshot | undefined>;
13
13
  };
14
14
  export {};