dsh-lark-agent 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +15 -11
- package/dist/bot-menu.d.ts +1 -0
- package/dist/bot-menu.js +2 -0
- package/dist/bot-service.js +4 -2
- package/dist/bridge.d.ts +42 -1
- package/dist/bridge.js +340 -18
- package/dist/card-actions.js +1 -1
- package/dist/client.cjs +3 -3
- package/dist/config.d.ts +8 -0
- package/dist/config.js +8 -2
- package/dist/dsh-runtime.d.ts +75 -2
- package/dist/dsh-runtime.js +346 -23
- package/dist/interaction-cards.d.ts +23 -0
- package/dist/interaction-cards.js +119 -0
- package/dist/lark-cli.d.ts +24 -0
- package/dist/lark-cli.js +92 -4
- package/dist/live-reply.d.ts +87 -0
- package/dist/live-reply.js +389 -0
- package/dist/onboarding/controller.js +9 -7
- package/dist/onboarding/lark-platform.js +5 -5
- package/dist/permission-controls.d.ts +13 -0
- package/dist/permission-controls.js +26 -0
- package/dist/permission-preferences.d.ts +26 -0
- package/dist/permission-preferences.js +169 -0
- package/dist/presentation.d.ts +7 -0
- package/dist/replies.d.ts +5 -0
- package/dist/replies.js +9 -1
- package/dist/rich-scopes.d.ts +4 -0
- package/dist/rich-scopes.js +6 -0
- package/package.json +4 -4
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 dsh-
|
|
3
|
+
Copyright (c) 2026 dsh-lark-agent contributors
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
<img src="https://raw.githubusercontent.com/Nirvana-Jie/dsh-
|
|
1
|
+
<img src="https://raw.githubusercontent.com/Nirvana-Jie/dsh-lark-agent/main/assets/plugin-icon.png" width="80" align="right" alt="dsh-lark-agent icon">
|
|
2
2
|
|
|
3
|
-
# dsh-
|
|
3
|
+
# dsh-lark-agent
|
|
4
4
|
|
|
5
|
-
[中文](https://github.com/Nirvana-Jie/dsh-
|
|
5
|
+
[中文](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/README_zh.md)
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/dsh-lark-agent)
|
|
8
8
|
|
|
9
9
|
**Reach your [DeepSeek Harness](https://www.deepseek.com/harness/) assistant from Feishu / Lark when you're away from your computer.**
|
|
10
10
|
|
|
11
|
-
dsh-
|
|
11
|
+
dsh-lark-agent adds a Feishu / Lark private chat to a running DeepSeek Harness: hand off tasks, talk through plans, and pick up earlier topics from your phone. Feishu gets its own sessions and never takes over chats already open in the desktop app or on the web.
|
|
12
12
|
|
|
13
|
-
[](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/assets/readme/intro.mp4)
|
|
14
14
|
|
|
15
15
|
## Install / Update
|
|
16
16
|
|
|
@@ -30,7 +30,7 @@ Before you start:
|
|
|
30
30
|
|
|
31
31
|
In the sidebar, open **插件 → 添加插件** (Plugins → Add plugin), enter `dsh-lark-agent`, click **安装** (Install), then **立即启用** (Enable now). Restart if the app asks.
|
|
32
32
|
|
|
33
|
-
<img src="https://raw.githubusercontent.com/Nirvana-Jie/dsh-
|
|
33
|
+
<img src="https://raw.githubusercontent.com/Nirvana-Jie/dsh-lark-agent/main/assets/readme/desktop-install-02.jpg" width="720" alt="The Add plugin dialog in the DeepSeek Harness desktop app">
|
|
34
34
|
|
|
35
35
|
### Install with the `dsh` CLI
|
|
36
36
|
|
|
@@ -53,7 +53,7 @@ Then restart `dsh web`.
|
|
|
53
53
|
|
|
54
54
|
### Update
|
|
55
55
|
|
|
56
|
-
1. On the Feishu settings page, click **暂停连接** (pause) and back up as described in [Upgrade](https://github.com/Nirvana-Jie/dsh-
|
|
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`.
|
|
58
58
|
3. Click **恢复连接** (resume) and check that the bot replies.
|
|
59
59
|
|
|
@@ -72,7 +72,11 @@ The Feishu entry works only while the host is online: not when the app is closed
|
|
|
72
72
|
|
|
73
73
|
| In Feishu, you… | The assistant… |
|
|
74
74
|
| --- | --- |
|
|
75
|
-
| Send a request |
|
|
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, with its thinking and tool steps folded inside |
|
|
76
|
+
| 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
|
+
| Get asked to choose by Harness | Sends a choice card: tap an option, or fill it in and submit |
|
|
78
|
+
| Use `/permission`, or 权限模式 in the model settings card | Switches between read-only, workspace edits and full access, like the app's permission selector; full access stops asking |
|
|
79
|
+
| Ask for the result file | Sends files from its workspace straight to the chat |
|
|
76
80
|
| Say "lead with the conclusion from now on" | Remembers the preference |
|
|
77
81
|
| Keep chatting, or use `/sessions`, `/new` | Continues the topic, returns to an earlier one, or starts fresh |
|
|
78
82
|
| Ask for a bar chart of some numbers | Replies with a native Feishu chart card |
|
|
@@ -80,14 +84,14 @@ The Feishu entry works only while the host is online: not when the app is closed
|
|
|
80
84
|
| Ask for an architecture diagram or a report | Creates a Feishu doc (with a whiteboard for diagrams) shared read-only with you alone; in App `0.2.1-rc.2`, opening that Feishu session in the app also shows a local preview |
|
|
81
85
|
| Use `/model`, `/thinking` | Switches between the host's configured models and thinking depth |
|
|
82
86
|
|
|
83
|
-
By default it answers only your own private chat. Sessions and memory stay on the computer running Harness; messages pass through Feishu and your model provider.
|
|
87
|
+
By default it answers only your own private chat. In that chat the assistant is your Harness itself: the tools it can use and the actions that need your confirmation follow the permission settings on your computer. Sessions and memory stay on the computer running Harness; messages pass through Feishu and your model provider.
|
|
84
88
|
|
|
85
89
|
## Good to know
|
|
86
90
|
|
|
87
|
-
- **More docs**: commands, privacy, upgrades, the chat bottom menu, team deployment and troubleshooting are in [DEVELOPMENT.md](https://github.com/Nirvana-Jie/dsh-
|
|
91
|
+
- **More docs**: commands, privacy, upgrades, the chat bottom menu, team deployment and troubleshooting are in [DEVELOPMENT.md](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md) (Chinese).
|
|
88
92
|
- **Compatibility**: verified with PC App `0.2.1-rc.2` and `dsh` CLI / Web `0.1.1-rc.2`; other versions, platforms and tenants are not yet verified.
|
|
89
93
|
- **Language**: the settings page, the bot's built-in messages (`/help`, status text, card buttons, bottom menu) and the intro video are in Chinese.
|
|
90
94
|
|
|
91
95
|
## License
|
|
92
96
|
|
|
93
|
-
[MIT](https://github.com/Nirvana-Jie/dsh-
|
|
97
|
+
[MIT](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/LICENSE). DeepSeek and Feishu / Lark brand assets belong to their owners and are not covered by the MIT license; this is an independent open-source plugin, not an official product.
|
package/dist/bot-menu.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare const BOT_MENU_COMMANDS: Readonly<{
|
|
|
6
6
|
readonly 'dsh.sessions': "/sessions";
|
|
7
7
|
readonly 'dsh.model': "/model";
|
|
8
8
|
readonly 'dsh.thinking': "/thinking";
|
|
9
|
+
readonly 'dsh.permission': "/permission";
|
|
9
10
|
}>;
|
|
10
11
|
export type BotMenuCommand = (typeof BOT_MENU_COMMANDS)[keyof typeof BOT_MENU_COMMANDS];
|
|
11
12
|
export declare function botMenuCommand(eventKey: unknown): BotMenuCommand | undefined;
|
package/dist/bot-menu.js
CHANGED
|
@@ -6,6 +6,8 @@ export const BOT_MENU_COMMANDS = Object.freeze({
|
|
|
6
6
|
'dsh.sessions': '/sessions',
|
|
7
7
|
'dsh.model': '/model',
|
|
8
8
|
'dsh.thinking': '/thinking',
|
|
9
|
+
// Optional: not in the default five-node menu; an owner may add it under 设置.
|
|
10
|
+
'dsh.permission': '/permission',
|
|
9
11
|
});
|
|
10
12
|
export function botMenuCommand(eventKey) {
|
|
11
13
|
if (typeof eventKey !== 'string' || !Object.hasOwn(BOT_MENU_COMMANDS, eventKey))
|
package/dist/bot-service.js
CHANGED
|
@@ -13,6 +13,7 @@ import { DirectChatsStore } from './direct-chats.js';
|
|
|
13
13
|
import { ModelPreferencesStore } from './model-preferences.js';
|
|
14
14
|
import { ArtifactStore } from './artifacts.js';
|
|
15
15
|
import { LarkArtifactPublisher } from './artifact-publisher.js';
|
|
16
|
+
import { PermissionPreferencesStore } from './permission-preferences.js';
|
|
16
17
|
/** A failed close is still an owned service, never a disposable startup error. */
|
|
17
18
|
export class BotServiceStartError extends Error {
|
|
18
19
|
service;
|
|
@@ -36,6 +37,7 @@ export async function createBotService(ctx, config, onHealth = () => undefined,
|
|
|
36
37
|
SessionStore.open(config.stateDir), MemoryStore.open(config.stateDir),
|
|
37
38
|
config.cardInteractions ? CardActions.open(config.stateDir, JSON.stringify([config.profile, config.botOpenId])) : undefined,
|
|
38
39
|
DirectChatsStore.open(config.stateDir), ModelPreferencesStore.open(config.stateDir), ArtifactStore.open(config.stateDir),
|
|
40
|
+
PermissionPreferencesStore.open(config.stateDir),
|
|
39
41
|
];
|
|
40
42
|
// An opener may create signing state: finish all openers before releasing
|
|
41
43
|
// ownership after a sibling fails, so no old writer outlives the lock.
|
|
@@ -43,7 +45,7 @@ export async function createBotService(ctx, config, onHealth = () => undefined,
|
|
|
43
45
|
const failed = openedStores.find(result => result.status === 'rejected');
|
|
44
46
|
if (failed?.status === 'rejected')
|
|
45
47
|
throw failed.reason;
|
|
46
|
-
const [sessions, memories, actions, directChats, modelPreferences, artifacts] = await Promise.all(openingStores);
|
|
48
|
+
const [sessions, memories, actions, directChats, modelPreferences, artifacts, permissionPreferences] = await Promise.all(openingStores);
|
|
47
49
|
if (startup?.directChat) {
|
|
48
50
|
const { openId, chatId } = startup.directChat;
|
|
49
51
|
if (!config.users.some(user => user.openId === openId && user.direct))
|
|
@@ -59,7 +61,7 @@ export async function createBotService(ctx, config, onHealth = () => undefined,
|
|
|
59
61
|
? new LarkCli({ profile: config.profile, executable: config.executable, eventKey: 'application.bot.menu_v6' }) : undefined;
|
|
60
62
|
const progressTransport = config.loadingReaction
|
|
61
63
|
? new LarkCli({ profile: config.profile, executable: config.executable, commandTimeoutMs: 5_000 }) : undefined;
|
|
62
|
-
const bridge = new BotBridge(config, new DshRuntime(ctx), transport, ledger, { sessions, memories, actions, directChats, modelPreferences, artifacts }, event => console.info(`[im-larkbot] ${JSON.stringify(event)}`), progressTransport, new LarkArtifactPublisher(config.profile, config.botOpenId, { executable: config.executable }));
|
|
64
|
+
const bridge = new BotBridge(config, new DshRuntime(ctx), transport, ledger, { sessions, memories, actions, directChats, modelPreferences, artifacts, permissionPreferences }, event => console.info(`[im-larkbot] ${JSON.stringify(event)}`), progressTransport, new LarkArtifactPublisher(config.profile, config.botOpenId, { executable: config.executable }));
|
|
63
65
|
let stopped = false, started = false, healthy = false;
|
|
64
66
|
const readiness = { messages: false, cards: !callbacks, menu: !menu };
|
|
65
67
|
let stopping;
|
package/dist/bridge.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BotConfig } from './config.js';
|
|
2
|
-
import type { AgentPolicy, RuntimeSession, ModelSelection, ModelCatalog } from './dsh-runtime.js';
|
|
2
|
+
import type { AgentPolicy, RuntimeSession, ModelSelection, ModelCatalog, PermissionOption } from './dsh-runtime.js';
|
|
3
3
|
import type { MessageLedger } from './ledger.js';
|
|
4
4
|
import { type MemoryStore } from './memory-store.js';
|
|
5
5
|
import { type SessionStore } from './session-store.js';
|
|
@@ -7,6 +7,7 @@ import type { CardActions } from './card-actions.js';
|
|
|
7
7
|
import { type ReplyPayload } from './replies.js';
|
|
8
8
|
import { type ProgressTransport } from './progress.js';
|
|
9
9
|
import { type ModelPreferencesStore } from './model-preferences.js';
|
|
10
|
+
import type { PermissionPreferencesStore } from './permission-preferences.js';
|
|
10
11
|
import type { ArtifactStore } from './artifacts.js';
|
|
11
12
|
import type { ArtifactPublisher } from './artifact-publisher.js';
|
|
12
13
|
export interface Runtime {
|
|
@@ -14,6 +15,7 @@ export interface Runtime {
|
|
|
14
15
|
resume(policy: AgentPolicy, id: string): Promise<RuntimeSession>;
|
|
15
16
|
catalog?(): Promise<ModelCatalog>;
|
|
16
17
|
resolveModelSelection?(selection?: ModelSelection): Promise<ModelSelection>;
|
|
18
|
+
permissionOptions?(): PermissionOption[];
|
|
17
19
|
}
|
|
18
20
|
export interface BotState {
|
|
19
21
|
sessions: Pick<SessionStore, 'current' | 'list' | 'add' | 'activate' | 'archive' | 'rename' | 'touch'> & Partial<Pick<SessionStore, 'rebindNative'>>;
|
|
@@ -25,6 +27,7 @@ export interface BotState {
|
|
|
25
27
|
record(openId: string, chatId: string): Promise<void>;
|
|
26
28
|
};
|
|
27
29
|
artifacts?: Pick<ArtifactStore, 'add' | 'get' | 'list' | 'claimPublish' | 'publication' | 'submit'>;
|
|
30
|
+
permissionPreferences?: Pick<PermissionPreferencesStore, 'get' | 'set'>;
|
|
28
31
|
}
|
|
29
32
|
export interface Transport {
|
|
30
33
|
reply(messageId: string, text: string, options: {
|
|
@@ -50,6 +53,18 @@ export interface Transport {
|
|
|
50
53
|
idempotencyKey: string;
|
|
51
54
|
}): Promise<string>;
|
|
52
55
|
updateFeedback?(messageId: string, payload: ReplyPayload): Promise<void>;
|
|
56
|
+
createCardEntity?(card: Record<string, unknown>): Promise<string>;
|
|
57
|
+
replyCardEntity?(messageId: string, cardId: string, options: {
|
|
58
|
+
idempotencyKey: string;
|
|
59
|
+
inThread: boolean;
|
|
60
|
+
}): Promise<void>;
|
|
61
|
+
streamCardText?(cardId: string, elementId: string, content: string, sequence: number): Promise<void>;
|
|
62
|
+
updateCardEntity?(cardId: string, card: Record<string, unknown>, sequence: number): Promise<void>;
|
|
63
|
+
replyFile?(messageId: string, path: string, options: {
|
|
64
|
+
idempotencyKey: string;
|
|
65
|
+
inThread: boolean;
|
|
66
|
+
image: boolean;
|
|
67
|
+
}): Promise<void>;
|
|
53
68
|
}
|
|
54
69
|
export type Audit = (event: {
|
|
55
70
|
stage: string;
|
|
@@ -76,12 +91,29 @@ export declare class BotBridge {
|
|
|
76
91
|
private readonly failedDisposals;
|
|
77
92
|
private cardActionsReady;
|
|
78
93
|
private readonly progress;
|
|
94
|
+
private readonly interactions;
|
|
95
|
+
private liveUnavailableUntil;
|
|
79
96
|
constructor(config: BotConfig, runtime: Runtime, transport: Transport, ledger: MessageLedger, state: BotState, audit?: Audit, progressTransport?: ProgressTransport, artifactPublisher?: ArtifactPublisher | undefined);
|
|
80
97
|
setCardActionsReady(ready: boolean): void;
|
|
81
98
|
private parseIncoming;
|
|
82
99
|
accept(raw: unknown): Promise<void>;
|
|
83
100
|
private acceptMessage;
|
|
84
101
|
private process;
|
|
102
|
+
/** Whether this message's reply streams into a live card, which makes the typing reaction redundant. */
|
|
103
|
+
private streams;
|
|
104
|
+
private liveTransport;
|
|
105
|
+
private canInteract;
|
|
106
|
+
private issueAction;
|
|
107
|
+
/** Ask the owner about one Harness approval. Anything but an explicit allow denies the call. */
|
|
108
|
+
private approve;
|
|
109
|
+
/** Ask the owner a Harness question. Without an answer the host gets an error and continues. */
|
|
110
|
+
private ask;
|
|
111
|
+
private waitForAnswer;
|
|
112
|
+
private openInteraction;
|
|
113
|
+
private closeInteraction;
|
|
114
|
+
private answerInteraction;
|
|
115
|
+
/** Send files the host's delivery tool presented, from this user's workspace only. */
|
|
116
|
+
private deliverFiles;
|
|
85
117
|
private commandFeedback;
|
|
86
118
|
private context;
|
|
87
119
|
private sessionContext;
|
|
@@ -90,6 +122,15 @@ export declare class BotBridge {
|
|
|
90
122
|
private bindRestoredSession;
|
|
91
123
|
private unload;
|
|
92
124
|
private command;
|
|
125
|
+
private harness;
|
|
126
|
+
/** The host's presets, from the live session when there is one. Undefined when the host offers none. */
|
|
127
|
+
private permissionView;
|
|
128
|
+
/** What this chat runs: the live session's preset, else the chat's saved choice. */
|
|
129
|
+
private permissionChoice;
|
|
130
|
+
/** Owner-only: switch this private chat's Harness permission preset, like the App's selector. */
|
|
131
|
+
private permissionCommand;
|
|
132
|
+
/** Bring a newly opened harness session to the chat's saved permission preset. */
|
|
133
|
+
private applyPermission;
|
|
93
134
|
private modelState;
|
|
94
135
|
private modelCommand;
|
|
95
136
|
private commandPanel;
|