dsh-lark-agent 0.2.0 → 0.3.1
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/README.md +8 -6
- package/dist/bot-menu.d.ts +2 -1
- package/dist/bot-menu.js +3 -2
- package/dist/bridge.d.ts +30 -1
- package/dist/bridge.js +269 -49
- package/dist/client/ConnectionCard.d.ts +6 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client.cjs +77 -7
- package/dist/dsh-runtime.d.ts +20 -2
- package/dist/dsh-runtime.js +113 -11
- package/dist/interaction-cards.d.ts +8 -3
- package/dist/interaction-cards.js +4 -4
- package/dist/live-reply.d.ts +23 -9
- package/dist/live-reply.js +183 -54
- package/dist/model-controls.d.ts +9 -1
- package/dist/model-controls.js +45 -20
- package/dist/onboarding/host.d.ts +5 -1
- package/dist/onboarding/host.js +30 -3
- package/dist/onboarding/protocol.d.ts +5 -0
- package/dist/onboarding/updates.d.ts +23 -0
- package/dist/onboarding/updates.js +67 -0
- package/dist/permission-controls.d.ts +8 -1
- package/dist/permission-controls.js +18 -7
- package/dist/replies.d.ts +10 -0
- package/dist/replies.js +18 -5
- package/package.json +7 -7
package/dist/client.cjs
CHANGED
|
@@ -111,6 +111,15 @@ function parseSnapshot(value) {
|
|
|
111
111
|
snapshot.documentSetup.qr = { url: officialUrl(qr.url), image, expiresAt: timestamp(qr.expiresAt) };
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
if (data.update !== void 0) {
|
|
115
|
+
const update = object(data.update);
|
|
116
|
+
const version = (value2) => {
|
|
117
|
+
const raw = text(value2, 64);
|
|
118
|
+
if (!/^\d{1,9}\.\d{1,9}\.\d{1,9}(?:-[0-9A-Za-z.-]{1,40})?$/.test(raw)) throw new Error("invalid version");
|
|
119
|
+
return raw;
|
|
120
|
+
};
|
|
121
|
+
snapshot.update = { current: version(update.current), latest: version(update.latest) };
|
|
122
|
+
}
|
|
114
123
|
return snapshot;
|
|
115
124
|
}
|
|
116
125
|
function parseProfiles(value) {
|
|
@@ -275,6 +284,8 @@ var deepseek_harness_icon_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEU
|
|
|
275
284
|
|
|
276
285
|
// src/client/ConnectionCard.tsx
|
|
277
286
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
287
|
+
var UPGRADE_COMMAND = "dsh plugin --profile web update dsh-lark-agent@latest";
|
|
288
|
+
var UPGRADE_GUIDE = "https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md#\u5347\u7EA7";
|
|
278
289
|
var phaseLabels = {
|
|
279
290
|
idle: "\u672A\u8FDE\u63A5",
|
|
280
291
|
creating: "\u6B63\u5728\u521B\u5EFA",
|
|
@@ -319,7 +330,54 @@ function Steps({ active }) {
|
|
|
319
330
|
label
|
|
320
331
|
] }, label)) });
|
|
321
332
|
}
|
|
322
|
-
function
|
|
333
|
+
function UpdateNotice({ update, host, phase }) {
|
|
334
|
+
const [open, setOpen] = (0, import_react2.useState)(false);
|
|
335
|
+
const [status, setStatus] = (0, import_react2.useState)("");
|
|
336
|
+
const app = host?.app ?? false;
|
|
337
|
+
const settled = phase === "connected" || phase === "paused";
|
|
338
|
+
const copy = async () => {
|
|
339
|
+
try {
|
|
340
|
+
await navigator.clipboard.writeText(UPGRADE_COMMAND);
|
|
341
|
+
setStatus("\u5DF2\u590D\u5236\u5347\u7EA7\u547D\u4EE4");
|
|
342
|
+
} catch {
|
|
343
|
+
setStatus("\u8BF7\u9009\u4E2D\u4E0A\u65B9\u547D\u4EE4\u624B\u52A8\u590D\u5236");
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-update", children: [
|
|
347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-update-row", children: [
|
|
348
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
349
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
|
|
350
|
+
"\u98DE\u4E66\u63D2\u4EF6\u6709\u65B0\u7248\u672C ",
|
|
351
|
+
update.latest
|
|
352
|
+
] }),
|
|
353
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
|
|
354
|
+
"\u5F53\u524D ",
|
|
355
|
+
update.current,
|
|
356
|
+
"\uFF0C\u5347\u7EA7\u540E\u4FDD\u7559\u73B0\u6709\u8FDE\u63A5\u548C\u5BF9\u8BDD\u3002"
|
|
357
|
+
] })
|
|
358
|
+
] }),
|
|
359
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-secondary", "aria-expanded": open, onClick: () => setOpen(!open), children: open ? "\u6536\u8D77" : "\u5347\u7EA7" })
|
|
360
|
+
] }),
|
|
361
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
362
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ol", { className: "lark-instructions", children: [
|
|
363
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: phase === "paused" ? "\u8FDE\u63A5\u5DF2\u6682\u505C\uFF0C\u53EF\u4EE5\u76F4\u63A5\u5347\u7EA7\u3002" : settled ? "\u5148\u70B9\u4E0B\u65B9\u300C\u6682\u505C\u8FDE\u63A5\u300D\u3002" : "\u5148\u5B8C\u6210\u6216\u53D6\u6D88\u5F53\u524D\u7684\u8FDE\u63A5\u6B65\u9AA4\u3002" }),
|
|
364
|
+
app ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u5728\u300C\u63D2\u4EF6\u5217\u8868\u300D\u5378\u8F7D dsh-lark-agent\uFF0C\u518D\u70B9\u300C\u6DFB\u52A0\u63D2\u4EF6\u300D\u8F93\u5165 dsh-lark-agent \u5B89\u88C5\u65B0\u7248\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
365
|
+
"\u5728\u7EC8\u7AEF\u8FD0\u884C\u4E0B\u9762\u7684\u547D\u4EE4\uFF0C\u7136\u540E\u91CD\u542F dsh web\uFF1A",
|
|
366
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "lark-command", children: UPGRADE_COMMAND })
|
|
367
|
+
] }),
|
|
368
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: settled ? "\u56DE\u5230\u8FD9\u91CC\u70B9\u300C\u6062\u590D\u8FDE\u63A5\u300D\uFF0C\u5728\u98DE\u4E66\u53D1\u4E00\u53E5\u8BDD\u786E\u8BA4\u80FD\u56DE\u590D\u3002" : "\u5347\u7EA7\u540E\u56DE\u5230\u8FD9\u91CC\u7EE7\u7EED\u3002" })
|
|
369
|
+
] }),
|
|
370
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-actions", children: [
|
|
371
|
+
app ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-secondary", onClick: () => {
|
|
372
|
+
if (!host?.openPlugins?.()) setStatus("\u8BF7\u5207\u5230\u4E0A\u65B9\u7684\u300C\u63D2\u4EF6\u5217\u8868\u300D\u7EE7\u7EED\u3002");
|
|
373
|
+
}, children: "\u6253\u5F00\u63D2\u4EF6\u5217\u8868" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-secondary", onClick: () => void copy(), children: "\u590D\u5236\u5347\u7EA7\u547D\u4EE4" }),
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(External, { className: "lark-text-button", url: UPGRADE_GUIDE, children: "\u5347\u7EA7\u524D\u7684\u5907\u4EFD\u8BF4\u660E" })
|
|
375
|
+
] }),
|
|
376
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lark-copy-status", role: "status", children: status })
|
|
377
|
+
] })
|
|
378
|
+
] });
|
|
379
|
+
}
|
|
380
|
+
function ConnectionCard({ api, host }) {
|
|
323
381
|
const { snapshot, error, pending, profiles, refresh, mutate, loadProfiles } = useConnection(api);
|
|
324
382
|
const [chooseExisting, setChooseExisting] = (0, import_react2.useState)(false);
|
|
325
383
|
const [selected, setSelected] = (0, import_react2.useState)("");
|
|
@@ -506,7 +564,7 @@ function ConnectionCard({ api }) {
|
|
|
506
564
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lark-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-quiet", disabled, onClick: cancel, children: "\u53D6\u6D88\u8FDE\u63A5" }) })
|
|
507
565
|
] });
|
|
508
566
|
if (phase === "connected" || phase === "paused") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
509
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Steps, { active:
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Steps, { active: 3 }),
|
|
510
568
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrandLink, { connected: phase === "connected" }),
|
|
511
569
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "lark-intro-title", children: phase === "paused" ? "\u5DF2\u6682\u505C\u98DE\u4E66\u8FDE\u63A5" : "\u53EF\u4EE5\u5728\u98DE\u4E66\u627E\u6211\u4E86" }),
|
|
512
570
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lark-copy lark-centered", children: phase === "paused" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
@@ -598,6 +656,7 @@ function ConnectionCard({ api }) {
|
|
|
598
656
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: error }),
|
|
599
657
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-text-button", disabled, onClick: refresh, children: "\u91CD\u8BD5" })
|
|
600
658
|
] }),
|
|
659
|
+
snapshot?.update && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpdateNotice, { update: snapshot.update, host, phase }),
|
|
601
660
|
body(),
|
|
602
661
|
pending && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-pending", role: "status", children: [
|
|
603
662
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "lark-spinner", "aria-hidden": "true" }),
|
|
@@ -612,10 +671,10 @@ function ConnectionCard({ api }) {
|
|
|
612
671
|
// src/client/styles.ts
|
|
613
672
|
var css = `
|
|
614
673
|
.dsh-lark-settings{--lark-ink:#263247;--lark-muted:#738096;--lark-line:#e4eaf2;--lark-blue:#4268df;--lark-surface:#fff;--lark-soft:#f5f8fd;font:14px/1.65 -apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;color:var(--lark-ink);max-width:720px;margin:0 auto;padding:8px 0 20px;box-sizing:border-box}
|
|
615
|
-
.dsh-lark-settings *{box-sizing:border-box}.dsh-lark-settings button,.dsh-lark-settings input{font:inherit}.dsh-lark-settings button,.dsh-lark-settings summary{cursor:pointer}.dsh-lark-settings button:disabled{opacity:.5;cursor:not-allowed}.dsh-lark-settings button:focus-visible,.dsh-lark-settings a:focus-visible,.dsh-lark-settings input:focus-visible,.dsh-lark-settings summary:focus-visible{outline:3px solid #a1b8ff;outline-offset:3px}.dsh-lark-settings a{overflow-wrap:anywhere}.lark-heading{margin:0 0 22px}.lark-heading h2{font-size:23px;font-weight:650;margin:0 0 5px;letter-spacing:-.4px}.lark-heading p,.lark-card-header p{color:var(--lark-muted);font-size:13px;margin:0}.lark-card{border:1px solid var(--lark-line);border-radius:15px;background:var(--lark-surface);overflow:hidden;box-shadow:0 2px 5px #27385504}.lark-card-header{display:flex;align-items:center;gap:12px;padding:19px 23px;border-bottom:1px solid var(--lark-line)}.lark-plugin-icon{width:43px;height:43px;object-fit:contain;flex-shrink:0}.lark-card-header h2{font-size:15px;font-weight:650;line-height:1.6;margin:0}.lark-card-header p{font-size:12px}.lark-status{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:11px;color:var(--lark-muted);white-space:nowrap}.lark-status i{width:6px;height:6px;border-radius:50%;background:#9da9bc}.lark-status.online{color:#248470}.lark-status.online i{background:#248470}.lark-body{padding:28px;position:relative}.lark-body h3{font-size:20px;font-weight:650;letter-spacing:-.25px;margin:0 0 9px;line-height:1.5}.lark-copy{color:var(--lark-muted);font-size:13px;margin:0 0 20px}.lark-centered{text-align:center;line-height:1.9}.lark-body .lark-intro-title{text-align:center;font-size:21px}.lark-brands{display:flex;justify-content:center;align-items:center;gap:16px;margin:5px 0 25px}.lark-brand{width:53px;height:53px;display:grid;place-items:center;border-radius:14px;background:var(--lark-soft);border:1px solid var(--lark-line)}.lark-brand img{width:37px;height:37px;object-fit:contain}.lark-brand-line{width:78px;height:1px;background:#dbe3ee;position:relative}.lark-brand-line:after{content:"";position:absolute;left:calc(50% - 3px);top:-3px;width:7px;height:7px;border-radius:50%;background:#a9bbdc;border:2px solid var(--lark-surface)}.lark-brands-connected .lark-brand-line{background:#99d1c1}.lark-brands-connected .lark-brand-line:after{background:#248470}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:38px;padding:8px 16px;border-radius:8px;text-decoration:none;font-size:13px;font-weight:550;border:1px solid transparent;line-height:1.5;transition:background .14s}.lark-primary{background:var(--lark-blue);color:#fff}.lark-primary:hover{background:#3559ca}.lark-secondary{color:#485b79;background:var(--lark-surface);border-color:#dce3ed}.lark-secondary:hover{background:var(--lark-soft)}.lark-quiet{background:transparent;color:var(--lark-muted);font-weight:450}.lark-quiet:hover{background:var(--lark-soft)}.lark-danger{color:#ad4949;border-color:#e5bcbc;background:var(--lark-surface)}.lark-text-button{display:inline-flex;align-items:center;gap:7px;background:transparent;border:0;padding:5px 0;color:#5875a5;font-size:12px}.lark-entry{display:flex;flex-direction:column;align-items:center;gap:10px}.lark-entry .lark-primary{min-width:200px}.lark-actions{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-top:23px}.lark-spread{justify-content:space-between}.lark-center{justify-content:center}.lark-footnote{color:var(--lark-muted);font-size:11px;text-align:center;margin:18px 0 0}.lark-left{text-align:left}.lark-bottom-note{text-align:center;color:var(--lark-muted);font-size:11px;margin:15px 0 0}.lark-steps{display:flex;list-style:none;gap:12px;margin:0 0 25px;padding:0;color:#8b98ad;font-size:11px}.lark-steps li{display:flex;gap:6px;align-items:center;white-space:nowrap}.lark-steps li+li:before{content:"";width:23px;height:1px;background:var(--lark-line);margin-right:5px}.lark-steps li span{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;background:var(--lark-soft);font-size:10px}.lark-steps .active{color:var(--lark-blue);font-weight:600}.lark-steps .active span{background:#edf2ff}.lark-steps .done{color:#248470}.lark-steps .done span{background:#edf8f4}.lark-qr-layout{display:flex;align-items:center;gap:25px;margin:23px 0}.lark-qr{position:relative;width:166px;height:166px;padding:10px;background:white;border:1px solid var(--lark-line);border-radius:12px;flex-shrink:0}.lark-qr img{width:100%;height:100%;object-fit:contain}.lark-qr.expired img{opacity:.12}.lark-qr>span{position:absolute;inset:0;display:grid;place-items:center;color:#64758e;font-weight:600}.lark-qr-layout strong{font-weight:600;font-size:13px}.lark-qr-layout .lark-copy{margin:7px 0 10px}.lark-time{color:var(--lark-muted);font-size:11px;font-variant-numeric:tabular-nums}.lark-link-details{border-top:1px solid var(--lark-line);padding:13px 0 0;font-size:12px;color:var(--lark-muted)}.lark-link-details summary{margin-bottom:12px}.lark-raw-url{font-size:10px;line-height:1.65;overflow-wrap:anywhere;margin-top:10px;color:var(--lark-muted);user-select:all}.lark-notice{padding:13px 15px;background:#eef3fe;border-radius:9px;color:#526c95;font-size:12px;line-height:1.8}.lark-notice.warning{background:#fff7e9;color:#8b671e}.lark-state-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#edf8f4;color:#248470;font-size:20px;margin:2px 0 18px}.lark-state-icon.warning{background:#fff7e9;color:#9b7530}.lark-checks{padding:0;list-style:none;margin:20px 0;font-size:13px;color:var(--lark-muted)}.lark-checks li{padding:10px 0;border-bottom:1px solid var(--lark-line)}.lark-checks li:before{content:"\xB7";margin-right:12px}.lark-pairing{background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:11px;padding:17px 18px;display:flex;align-items:center;justify-content:space-between;gap:13px}.lark-pair-code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:21px;letter-spacing:.02em;color:#3d5a91;margin:4px 0 2px;overflow-wrap:anywhere;user-select:all}.lark-pairing.expired .lark-pair-code{opacity:.5}.lark-caption{font-size:11px;color:var(--lark-muted)}.lark-copy-status{min-height:18px;font-size:11px;color:#248470;margin:5px 0}.lark-instructions{padding-left:20px;color:#536784;font-size:13px}.lark-instructions li{padding:3px 0}.lark-owner{padding:17px 18px;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:10px;display:grid;gap:6px}.lark-owner strong{font-size:15px;font-weight:600;overflow-wrap:anywhere}.lark-connected-row{border-top:1px solid var(--lark-line);border-bottom:1px solid var(--lark-line);display:flex;justify-content:space-between;align-items:center;gap:16px;padding:16px 0;margin:26px 0 0}.lark-connected-row strong{font-size:12px;font-weight:600}.lark-connected-row p{font-size:11px;color:var(--lark-muted);margin:3px 0 0}.lark-advanced{font-size:12px;color:var(--lark-muted);padding-top:14px}.lark-advanced p{margin:9px 0 0;line-height:1.9}.lark-disconnect{border:1px solid #eacbcb;padding:14px;border-radius:10px;margin-top:16px;font-size:12px}.lark-disconnect p{margin:0}.lark-disconnect .lark-actions{margin-top:12px}.lark-profile-list{border:0;margin:20px 0 0;padding:0;display:grid;gap:10px;min-width:0}.lark-profile{display:flex;align-items:center;gap:12px;border:1px solid var(--lark-line);border-radius:10px;padding:15px;background:var(--lark-soft);cursor:pointer;overflow-wrap:anywhere}.lark-profile input{accent-color:var(--lark-blue)}.lark-error{display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff1f1;border:1px solid #f0d5d5;color:#9b4e4e;border-radius:9px;padding:11px 13px;font-size:12px;margin-bottom:21px}.lark-error .lark-text-button{flex-shrink:0;color:#9b4e4e}.lark-loading{padding:27px 0;text-align:center}.lark-loading .lark-spinner{margin:0 auto 17px;display:block}.lark-loading p{font-size:13px;color:var(--lark-muted);margin:0}.lark-loading-row{display:flex;align-items:center;gap:12px;color:var(--lark-muted);font-size:13px;margin:4px 0 15px}.lark-loading-row h3{color:var(--lark-ink);margin:0}.lark-spinner{display:inline-block;width:18px;height:18px;border:2px solid #dfe6f4;border-top-color:var(--lark-blue);border-radius:50%;animation:lark-spin .85s linear infinite;flex-shrink:0}.lark-pending{display:flex;align-items:center;gap:9px;margin-top:19px;font-size:11px;color:var(--lark-muted)}.lark-pending .lark-spinner{width:13px;height:13px}.lark-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
674
|
+
.dsh-lark-settings *{box-sizing:border-box}.dsh-lark-settings button,.dsh-lark-settings input{font:inherit}.dsh-lark-settings button,.dsh-lark-settings summary{cursor:pointer}.dsh-lark-settings button:disabled{opacity:.5;cursor:not-allowed}.dsh-lark-settings button:focus-visible,.dsh-lark-settings a:focus-visible,.dsh-lark-settings input:focus-visible,.dsh-lark-settings summary:focus-visible{outline:3px solid #a1b8ff;outline-offset:3px}.dsh-lark-settings a{overflow-wrap:anywhere}.lark-heading{margin:0 0 22px}.lark-heading h2{font-size:23px;font-weight:650;margin:0 0 5px;letter-spacing:-.4px}.lark-heading p,.lark-card-header p{color:var(--lark-muted);font-size:13px;margin:0}.lark-card{border:1px solid var(--lark-line);border-radius:15px;background:var(--lark-surface);overflow:hidden;box-shadow:0 2px 5px #27385504}.lark-card-header{display:flex;align-items:center;gap:12px;padding:19px 23px;border-bottom:1px solid var(--lark-line)}.lark-plugin-icon{width:43px;height:43px;object-fit:contain;flex-shrink:0}.lark-card-header h2{font-size:15px;font-weight:650;line-height:1.6;margin:0}.lark-card-header p{font-size:12px}.lark-status{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:11px;color:var(--lark-muted);white-space:nowrap}.lark-status i{width:6px;height:6px;border-radius:50%;background:#9da9bc}.lark-status.online{color:#248470}.lark-status.online i{background:#248470}.lark-body{padding:28px;position:relative}.lark-body h3{font-size:20px;font-weight:650;letter-spacing:-.25px;margin:0 0 9px;line-height:1.5}.lark-copy{color:var(--lark-muted);font-size:13px;margin:0 0 20px}.lark-centered{text-align:center;line-height:1.9}.lark-body .lark-intro-title{text-align:center;font-size:21px}.lark-brands{display:flex;justify-content:center;align-items:center;gap:16px;margin:5px 0 25px}.lark-brand{width:53px;height:53px;display:grid;place-items:center;border-radius:14px;background:var(--lark-soft);border:1px solid var(--lark-line)}.lark-brand img{width:37px;height:37px;object-fit:contain}.lark-brand-line{width:78px;height:1px;background:#dbe3ee;position:relative}.lark-brand-line:after{content:"";position:absolute;left:calc(50% - 3px);top:-3px;width:7px;height:7px;border-radius:50%;background:#a9bbdc;border:2px solid var(--lark-surface)}.lark-brands-connected .lark-brand-line{background:#99d1c1}.lark-brands-connected .lark-brand-line:after{background:#248470}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:38px;padding:8px 16px;border-radius:8px;text-decoration:none;font-size:13px;font-weight:550;border:1px solid transparent;line-height:1.5;transition:background .14s}.lark-primary{background:var(--lark-blue);color:#fff}.lark-primary:hover{background:#3559ca}.lark-secondary{color:#485b79;background:var(--lark-surface);border-color:#dce3ed}.lark-secondary:hover{background:var(--lark-soft)}.lark-quiet{background:transparent;color:var(--lark-muted);font-weight:450}.lark-quiet:hover{background:var(--lark-soft)}.lark-danger{color:#ad4949;border-color:#e5bcbc;background:var(--lark-surface)}.lark-text-button{display:inline-flex;align-items:center;gap:7px;background:transparent;border:0;padding:5px 0;color:#5875a5;font-size:12px}.lark-entry{display:flex;flex-direction:column;align-items:center;gap:10px}.lark-entry .lark-primary{min-width:200px}.lark-actions{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-top:23px}.lark-spread{justify-content:space-between}.lark-center{justify-content:center}.lark-footnote{color:var(--lark-muted);font-size:11px;text-align:center;margin:18px 0 0}.lark-left{text-align:left}.lark-bottom-note{text-align:center;color:var(--lark-muted);font-size:11px;margin:15px 0 0}.lark-steps{display:flex;list-style:none;gap:12px;margin:0 0 25px;padding:0;color:#8b98ad;font-size:11px}.lark-steps li{display:flex;gap:6px;align-items:center;white-space:nowrap}.lark-steps li+li:before{content:"";width:23px;height:1px;background:var(--lark-line);margin-right:5px}.lark-steps li span{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;background:var(--lark-soft);font-size:10px}.lark-steps .active{color:var(--lark-blue);font-weight:600}.lark-steps .active span{background:#edf2ff}.lark-steps .done{color:#248470}.lark-steps .done span{background:#edf8f4}.lark-qr-layout{display:flex;align-items:center;gap:25px;margin:23px 0}.lark-qr{position:relative;width:166px;height:166px;padding:10px;background:white;border:1px solid var(--lark-line);border-radius:12px;flex-shrink:0}.lark-qr img{width:100%;height:100%;object-fit:contain}.lark-qr.expired img{opacity:.12}.lark-qr>span{position:absolute;inset:0;display:grid;place-items:center;color:#64758e;font-weight:600}.lark-qr-layout strong{font-weight:600;font-size:13px}.lark-qr-layout .lark-copy{margin:7px 0 10px}.lark-time{color:var(--lark-muted);font-size:11px;font-variant-numeric:tabular-nums}.lark-link-details{border-top:1px solid var(--lark-line);padding:13px 0 0;font-size:12px;color:var(--lark-muted)}.lark-link-details summary{margin-bottom:12px}.lark-raw-url{font-size:10px;line-height:1.65;overflow-wrap:anywhere;margin-top:10px;color:var(--lark-muted);user-select:all}.lark-notice{padding:13px 15px;background:#eef3fe;border-radius:9px;color:#526c95;font-size:12px;line-height:1.8}.lark-notice.warning{background:#fff7e9;color:#8b671e}.lark-state-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#edf8f4;color:#248470;font-size:20px;margin:2px 0 18px}.lark-state-icon.warning{background:#fff7e9;color:#9b7530}.lark-checks{padding:0;list-style:none;margin:20px 0;font-size:13px;color:var(--lark-muted)}.lark-checks li{padding:10px 0;border-bottom:1px solid var(--lark-line)}.lark-checks li:before{content:"\xB7";margin-right:12px}.lark-pairing{background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:11px;padding:17px 18px;display:flex;align-items:center;justify-content:space-between;gap:13px}.lark-pair-code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:21px;letter-spacing:.02em;color:#3d5a91;margin:4px 0 2px;overflow-wrap:anywhere;user-select:all}.lark-pairing.expired .lark-pair-code{opacity:.5}.lark-caption{font-size:11px;color:var(--lark-muted)}.lark-copy-status{min-height:18px;font-size:11px;color:#248470;margin:5px 0}.lark-instructions{padding-left:20px;color:#536784;font-size:13px}.lark-instructions li{padding:3px 0}.lark-owner{padding:17px 18px;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:10px;display:grid;gap:6px}.lark-owner strong{font-size:15px;font-weight:600;overflow-wrap:anywhere}.lark-connected-row{border-top:1px solid var(--lark-line);border-bottom:1px solid var(--lark-line);display:flex;justify-content:space-between;align-items:center;gap:16px;padding:16px 0;margin:26px 0 0}.lark-connected-row strong{font-size:12px;font-weight:600}.lark-connected-row p{font-size:11px;color:var(--lark-muted);margin:3px 0 0}.lark-advanced{font-size:12px;color:var(--lark-muted);padding-top:14px}.lark-advanced p{margin:9px 0 0;line-height:1.9}.lark-disconnect{border:1px solid #eacbcb;padding:14px;border-radius:10px;margin-top:16px;font-size:12px}.lark-disconnect p{margin:0}.lark-disconnect .lark-actions{margin-top:12px}.lark-profile-list{border:0;margin:20px 0 0;padding:0;display:grid;gap:10px;min-width:0}.lark-profile{display:flex;align-items:center;gap:12px;border:1px solid var(--lark-line);border-radius:10px;padding:15px;background:var(--lark-soft);cursor:pointer;overflow-wrap:anywhere}.lark-profile input{accent-color:var(--lark-blue)}.lark-error{display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff1f1;border:1px solid #f0d5d5;color:#9b4e4e;border-radius:9px;padding:11px 13px;font-size:12px;margin-bottom:21px}.lark-error .lark-text-button{flex-shrink:0;color:#9b4e4e}.lark-loading{padding:27px 0;text-align:center}.lark-loading .lark-spinner{margin:0 auto 17px;display:block}.lark-loading p{font-size:13px;color:var(--lark-muted);margin:0}.lark-loading-row{display:flex;align-items:center;gap:12px;color:var(--lark-muted);font-size:13px;margin:4px 0 15px}.lark-loading-row h3{color:var(--lark-ink);margin:0}.lark-spinner{display:inline-block;width:18px;height:18px;border:2px solid #dfe6f4;border-top-color:var(--lark-blue);border-radius:50%;animation:lark-spin .85s linear infinite;flex-shrink:0}.lark-pending{display:flex;align-items:center;gap:9px;margin-top:19px;font-size:11px;color:var(--lark-muted)}.lark-pending .lark-spinner{width:13px;height:13px}.lark-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.lark-update{border:1px solid #cfdcf8;background:#f4f7ff;border-radius:11px;padding:14px 16px;margin:0 0 22px}.lark-update-row{display:flex;align-items:center;justify-content:space-between;gap:14px}.lark-update strong{font-size:13px;font-weight:600}.lark-update p{font-size:12px;color:var(--lark-muted);margin:3px 0 0}.lark-update .lark-instructions{margin:12px 0 0}.lark-update .lark-actions{margin-top:12px}.lark-update .lark-copy-status{margin:6px 0 0}.lark-command{display:block;margin-top:6px;padding:8px 10px;border-radius:7px;background:var(--lark-surface);border:1px solid var(--lark-line);font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere;user-select:all}
|
|
616
675
|
@keyframes lark-spin{to{transform:rotate(360deg)}}
|
|
617
|
-
@media(prefers-color-scheme:dark){.dsh-lark-settings{--lark-ink:#e0e7f1;--lark-muted:#96a6bd;--lark-line:#364357;--lark-surface:#202a39;--lark-soft:#283448;--lark-blue:#5077ec}.lark-secondary{color:#c6d5ed;border-color:#46536b}.lark-pair-code{color:#b9ceff}.lark-instructions{color:#b0c2de}.lark-notice{background:#273d5a;color:#c2d3ec}.lark-notice.warning,.lark-state-icon.warning{background:#44391f;color:#e4c886}.lark-error{background:#492e32;border-color:#684047;color:#edbec2}.lark-error .lark-text-button{color:#edbec2}.lark-steps .active span{background:#293e6b}.lark-steps .done span,.lark-state-icon{background:#23483f}.lark-status.online,.lark-steps .done{color:#78c5b0}}
|
|
618
|
-
@media(max-width:520px){.lark-heading{margin-bottom:17px}.lark-card-header{padding:16px;gap:10px}.lark-card-header p{display:none}.lark-plugin-icon{width:37px;height:37px}.lark-body{padding:21px 18px}.lark-steps{gap:7px}.lark-steps li+li:before{width:12px;margin-right:2px}.lark-qr-layout{gap:17px;align-items:flex-start}.lark-qr{width:138px;height:138px}.lark-qr-layout strong{font-size:12px}.lark-qr-layout .lark-copy{font-size:12px}.lark-pairing{align-items:flex-start;flex-direction:column}.lark-pair-code{font-size:19px}.lark-body h3{font-size:19px}.lark-connected-row{gap:10px}.lark-actions{gap:8px}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{padding:8px 12px}.lark-copy{font-size:12px}}
|
|
676
|
+
@media(prefers-color-scheme:dark){.dsh-lark-settings{--lark-ink:#e0e7f1;--lark-muted:#96a6bd;--lark-line:#364357;--lark-surface:#202a39;--lark-soft:#283448;--lark-blue:#5077ec}.lark-secondary{color:#c6d5ed;border-color:#46536b}.lark-pair-code{color:#b9ceff}.lark-instructions{color:#b0c2de}.lark-notice{background:#273d5a;color:#c2d3ec}.lark-notice.warning,.lark-state-icon.warning{background:#44391f;color:#e4c886}.lark-error{background:#492e32;border-color:#684047;color:#edbec2}.lark-error .lark-text-button{color:#edbec2}.lark-steps .active span{background:#293e6b}.lark-steps .done span,.lark-state-icon{background:#23483f}.lark-status.online,.lark-steps .done{color:#78c5b0}.lark-update{background:#24324a;border-color:#3a4d70}}
|
|
677
|
+
@media(max-width:520px){.lark-heading{margin-bottom:17px}.lark-card-header{padding:16px;gap:10px}.lark-card-header p{display:none}.lark-plugin-icon{width:37px;height:37px}.lark-body{padding:21px 18px}.lark-steps{gap:7px}.lark-steps li+li:before{width:12px;margin-right:2px}.lark-qr-layout{gap:17px;align-items:flex-start}.lark-qr{width:138px;height:138px}.lark-qr-layout strong{font-size:12px}.lark-qr-layout .lark-copy{font-size:12px}.lark-pairing{align-items:flex-start;flex-direction:column}.lark-pair-code{font-size:19px}.lark-body h3{font-size:19px}.lark-connected-row{gap:10px}.lark-actions{gap:8px}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{padding:8px 12px}.lark-copy{font-size:12px}.lark-update-row{flex-direction:column;align-items:flex-start}}
|
|
619
678
|
@media(max-width:360px){.lark-qr-layout{flex-direction:column;align-items:center;text-align:center}.lark-steps{font-size:10px}.lark-steps li+li:before{width:8px}.lark-status{font-size:10px}}
|
|
620
679
|
@media(prefers-reduced-motion:reduce){.dsh-lark-settings *{animation:none!important;transition:none!important}}
|
|
621
680
|
`;
|
|
@@ -630,14 +689,25 @@ function installStyles() {
|
|
|
630
689
|
// src/client/index.tsx
|
|
631
690
|
var inject = ["slots", "connection"];
|
|
632
691
|
function apply(ctx) {
|
|
633
|
-
const
|
|
692
|
+
const app = ctx.connection.generation !== void 0;
|
|
693
|
+
const api = new ConnectionApi(ctx.connection.rpc, app);
|
|
694
|
+
const openPlugins = () => {
|
|
695
|
+
try {
|
|
696
|
+
const navigation = ctx.get?.("pluginNavigation");
|
|
697
|
+
if (typeof navigation?.openList !== "function") return false;
|
|
698
|
+
navigation.openList();
|
|
699
|
+
return true;
|
|
700
|
+
} catch {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
634
704
|
ctx.effect(installStyles, "dsh-larkbot: connection styles");
|
|
635
705
|
ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
|
|
636
706
|
name: "settings.plugins.tab",
|
|
637
707
|
id: "dsh-larkbot",
|
|
638
708
|
order: 30,
|
|
639
709
|
label: () => "\u98DE\u4E66",
|
|
640
|
-
inject: () => ({ api })
|
|
710
|
+
inject: () => ({ api, host: { app, openPlugins } })
|
|
641
711
|
}, ConnectionCard));
|
|
642
712
|
}
|
|
643
713
|
return module.exports; } });
|
package/dist/dsh-runtime.d.ts
CHANGED
|
@@ -47,10 +47,12 @@ export type TurnStreamEvent = {
|
|
|
47
47
|
callId: string;
|
|
48
48
|
name: string;
|
|
49
49
|
detail?: string;
|
|
50
|
+
code?: boolean;
|
|
50
51
|
} | {
|
|
51
52
|
type: 'tool-done';
|
|
52
53
|
callId: string;
|
|
53
54
|
ok: boolean;
|
|
55
|
+
exitCode?: number;
|
|
54
56
|
};
|
|
55
57
|
export interface ApprovalPrompt {
|
|
56
58
|
toolName: string;
|
|
@@ -79,9 +81,11 @@ export interface PermissionOption {
|
|
|
79
81
|
name: string;
|
|
80
82
|
description?: string;
|
|
81
83
|
}
|
|
84
|
+
/** defaultPreset is the App's choice for new sessions, when it is one of the options. */
|
|
82
85
|
export interface PermissionState {
|
|
83
86
|
current: string;
|
|
84
87
|
options: PermissionOption[];
|
|
88
|
+
defaultPreset?: string;
|
|
85
89
|
}
|
|
86
90
|
/** One turn's channel hooks. A harness-mode session asks its owner through them. */
|
|
87
91
|
export interface TurnObserver {
|
|
@@ -123,14 +127,24 @@ export interface RuntimeSession {
|
|
|
123
127
|
cancel(): void;
|
|
124
128
|
dispose(): Promise<void>;
|
|
125
129
|
modelSelection?(): ModelSelection;
|
|
126
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Call only after the bridge validates and durably saves the complete choice.
|
|
132
|
+
* In a session that shares its model with the App it is recorded in the
|
|
133
|
+
* session itself, so checkpoint before confirming it.
|
|
134
|
+
*/
|
|
127
135
|
setModelSelection?(next: ModelSelection): void;
|
|
136
|
+
/**
|
|
137
|
+
* True when this session decides its model like the App's own sessions: its
|
|
138
|
+
* latest pick (from Feishu or the App), else its last request's model, else
|
|
139
|
+
* the host default. The App then shows and changes the same choice.
|
|
140
|
+
*/
|
|
141
|
+
sharesModelSelection?: boolean;
|
|
128
142
|
/** Harness sessions on a host with permission presets; undefined otherwise. */
|
|
129
143
|
permissionState?(): PermissionState | undefined;
|
|
130
144
|
/** Switch this session's permission preset, as the App's own selector does. */
|
|
131
145
|
setPermission?(id: string): void;
|
|
132
146
|
}
|
|
133
|
-
export type RuntimeErrorCode = 'SETUP_FAILED' | 'RESTORE_FAILED' | 'SESSION_IN_USE' | 'PERSISTENCE_FAILED' | 'TURN_FAILED' | 'CANCELLED' | 'OUTPUT_LIMIT' | 'BUSY' | 'DISPOSED' | 'MODEL_UNAVAILABLE' | 'INVALID_MODEL_SELECTION' | 'PERMISSION_UNAVAILABLE';
|
|
147
|
+
export type RuntimeErrorCode = 'SETUP_FAILED' | 'RESTORE_FAILED' | 'SESSION_IN_USE' | 'PERSISTENCE_FAILED' | 'TURN_FAILED' | 'CANCELLED' | 'OUTPUT_LIMIT' | 'BUSY' | 'DISPOSED' | 'MODEL_UNAVAILABLE' | 'INVALID_MODEL_SELECTION' | 'MODEL_NOT_SAVED' | 'PERMISSION_UNAVAILABLE';
|
|
134
148
|
/** Safe to show to a chat user: never contains a provider, tool, or host error. */
|
|
135
149
|
export declare class DshRuntimeError extends Error {
|
|
136
150
|
readonly code: RuntimeErrorCode;
|
|
@@ -158,6 +172,10 @@ export declare class DshRuntime {
|
|
|
158
172
|
create(policy: AgentPolicy): Promise<RuntimeSession>;
|
|
159
173
|
/** The host's selectable permission presets, for a chat that has no live session yet. */
|
|
160
174
|
permissionOptions(): PermissionOption[];
|
|
175
|
+
/** Whether harness sessions here share their model choice with the App (see RuntimeSession.sharesModelSelection). */
|
|
176
|
+
sharesModelSelection(): boolean;
|
|
177
|
+
/** Make a choice the App's default for new sessions, as the App's own model selector does. */
|
|
178
|
+
saveDefaultModel(selection: ModelSelection): Promise<void>;
|
|
161
179
|
/** The preset a stored session runs on: its current projection, else its header. */
|
|
162
180
|
private recordedPreset;
|
|
163
181
|
resume(policy: AgentPolicy, sessionId: string): Promise<RuntimeSession>;
|
package/dist/dsh-runtime.js
CHANGED
|
@@ -27,6 +27,7 @@ export class DshRuntimeError extends Error {
|
|
|
27
27
|
DISPOSED: '当前会话已关闭,请重新开始。',
|
|
28
28
|
MODEL_UNAVAILABLE: '当前宿主暂时无法提供模型选项,请检查模型配置。',
|
|
29
29
|
INVALID_MODEL_SELECTION: '这个模型或思考深度当前不可用,请刷新选项后重试。',
|
|
30
|
+
MODEL_NOT_SAVED: '模型设置没能保存到电脑上的 Harness,请稍后重试。',
|
|
30
31
|
PERMISSION_UNAVAILABLE: '权限模式暂时无法在飞书切换,请在电脑上的 Harness 里调整。',
|
|
31
32
|
};
|
|
32
33
|
super(messages[code]);
|
|
@@ -61,6 +62,10 @@ function clip(value, limit) {
|
|
|
61
62
|
const points = Array.from(value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, '').trim());
|
|
62
63
|
return points.length > limit ? points.slice(0, limit).join('') + '…' : points.join('');
|
|
63
64
|
}
|
|
65
|
+
// The App's order: a call's own title or description first, then what it acts on.
|
|
66
|
+
const DETAIL_KEYS = ['title', 'description', 'objective', 'task', 'task_name', 'name', 'question', 'questions', 'prompt', 'message',
|
|
67
|
+
'command', 'cmd', 'queries', 'query', 'pattern', 'url', 'uri', 'file_path', 'path', 'target', 'action', 'status'];
|
|
68
|
+
const CODE_KEYS = new Set(['command', 'cmd', 'pattern', 'file_path', 'path']);
|
|
64
69
|
/** What a call is about, from well-known argument fields only; never the whole argument object. */
|
|
65
70
|
function callDetail(args, limit) {
|
|
66
71
|
let value;
|
|
@@ -72,11 +77,13 @@ function callDetail(args, limit) {
|
|
|
72
77
|
}
|
|
73
78
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
74
79
|
return;
|
|
75
|
-
for (const key of
|
|
80
|
+
for (const key of DETAIL_KEYS) {
|
|
76
81
|
const field = value[key];
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
const items = Array.isArray(field) ? field.map(item => typeof item === 'string' ? item
|
|
83
|
+
: item && typeof item === 'object' && typeof item.question === 'string' ? item.question : undefined) : [field];
|
|
84
|
+
const text = items.filter((item) => typeof item === 'string' && Boolean(item.trim())).join(', ');
|
|
85
|
+
if (text.trim())
|
|
86
|
+
return { text: clip(text.replace(/\s+/g, ' ').trim(), limit), code: CODE_KEYS.has(key) };
|
|
80
87
|
}
|
|
81
88
|
}
|
|
82
89
|
/** An approval is only informed if the owner sees the arguments being decided. */
|
|
@@ -258,6 +265,54 @@ function permissionOptions(service) {
|
|
|
258
265
|
}
|
|
259
266
|
return options;
|
|
260
267
|
}
|
|
268
|
+
/** The App's default for new sessions, when the catalog names one of its own options. */
|
|
269
|
+
function permissionDefault(service, options) {
|
|
270
|
+
let catalog;
|
|
271
|
+
try {
|
|
272
|
+
catalog = service.catalog();
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const value = catalog && typeof catalog === 'object' ? catalog.defaultPreset : undefined;
|
|
278
|
+
return typeof value === 'string' && options.some(option => option.id === value) ? value : undefined;
|
|
279
|
+
}
|
|
280
|
+
function sessionProjections(ctx) {
|
|
281
|
+
let service;
|
|
282
|
+
try {
|
|
283
|
+
service = ctx.get('sessionProjections');
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
return typeof service?.stateOf === 'function' ? service : undefined;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* The model a session's next request uses under the App's own rule: its latest
|
|
292
|
+
* pick, else its last request's model (an effort the adapter filled in stays
|
|
293
|
+
* unset), else the host default. Undefined when the host keeps no such record.
|
|
294
|
+
*/
|
|
295
|
+
function sessionChoice(projections, session, defaults) {
|
|
296
|
+
try {
|
|
297
|
+
const state = projections.stateOf(session, 'modelSelection');
|
|
298
|
+
if (!state || typeof state !== 'object')
|
|
299
|
+
return;
|
|
300
|
+
if (state.pending)
|
|
301
|
+
return nativeSelection(modelSelection(state.pending));
|
|
302
|
+
const header = session
|
|
303
|
+
.requestHeader?.();
|
|
304
|
+
if (header?.config) {
|
|
305
|
+
const { provider, model, reasoningEffort } = header.config;
|
|
306
|
+
return nativeSelection(modelSelection({ provider, model,
|
|
307
|
+
...(reasoningEffort === undefined || header.adapterDefaults?.reasoningEffort === true ? {} : { reasoningEffort: String(reasoningEffort) }) }));
|
|
308
|
+
}
|
|
309
|
+
const fallback = defaults?.currentSelection();
|
|
310
|
+
return fallback ? nativeSelection(modelSelection(fallback)) : undefined;
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
261
316
|
const MODEL_CATALOG_TTL_MS = 30_000;
|
|
262
317
|
const modelId = (value) => typeof value === 'string' && value.length > 0 && value.length <= 256
|
|
263
318
|
&& value.trim() === value && !/[\u0000-\u001f\u007f]/.test(value);
|
|
@@ -537,6 +592,22 @@ export class DshRuntime {
|
|
|
537
592
|
const service = permissionPresets(this.ctx);
|
|
538
593
|
return service ? permissionOptions(service) : [];
|
|
539
594
|
}
|
|
595
|
+
/** Whether harness sessions here share their model choice with the App (see RuntimeSession.sharesModelSelection). */
|
|
596
|
+
sharesModelSelection() {
|
|
597
|
+
return Boolean(sessionProjections(this.ctx)) && typeof this.ctx.get('agentDefaultModel')?.saveSelection === 'function';
|
|
598
|
+
}
|
|
599
|
+
/** Make a choice the App's default for new sessions, as the App's own model selector does. */
|
|
600
|
+
async saveDefaultModel(selection) {
|
|
601
|
+
const service = this.ctx.get('agentDefaultModel');
|
|
602
|
+
if (typeof service?.saveSelection !== 'function')
|
|
603
|
+
return;
|
|
604
|
+
try {
|
|
605
|
+
await service.saveSelection(nativeSelection(modelSelection(selection)));
|
|
606
|
+
}
|
|
607
|
+
catch {
|
|
608
|
+
throw new DshRuntimeError('MODEL_NOT_SAVED');
|
|
609
|
+
}
|
|
610
|
+
}
|
|
540
611
|
/** The preset a stored session runs on: its current projection, else its header. */
|
|
541
612
|
recordedPreset(session) {
|
|
542
613
|
const projections = this.ctx.get('sessionProjections');
|
|
@@ -570,7 +641,17 @@ export class DshRuntime {
|
|
|
570
641
|
let memoryText = '';
|
|
571
642
|
let artifactsText = '';
|
|
572
643
|
let active;
|
|
573
|
-
|
|
644
|
+
// A restricted session, or one on an administrator's fixed route, keeps that route.
|
|
645
|
+
// A harness session on the App host follows its own record once it exists (see sharesModelSelection).
|
|
646
|
+
const projections = harness && !(policy.provider && policy.model) ? sessionProjections(this.ctx) : undefined;
|
|
647
|
+
const defaultModel = projections ? this.ctx.get('agentDefaultModel') : undefined;
|
|
648
|
+
let fixed;
|
|
649
|
+
let shared;
|
|
650
|
+
const selected = {
|
|
651
|
+
get current() { return (shared && projections && sessionChoice(projections, shared, defaultModel)) || fixed; },
|
|
652
|
+
set current(next) { fixed = next; },
|
|
653
|
+
assembled: undefined,
|
|
654
|
+
};
|
|
574
655
|
let handle;
|
|
575
656
|
// Legacy hosts log raw chunks as session events, modern hosts publish live
|
|
576
657
|
// frames. Use whichever arrives first so a host offering both shows text once.
|
|
@@ -600,8 +681,8 @@ export class DshRuntime {
|
|
|
600
681
|
// Kept only so an approval prompt can show the exact call being decided.
|
|
601
682
|
if (turn.calls.size < 256)
|
|
602
683
|
turn.calls.set(block.id, args);
|
|
603
|
-
const detail = callDetail(args,
|
|
604
|
-
emit(turn, { type: 'tool', callId: block.id, name: clip(block.name, 64), ...(detail ? { detail } : {}) });
|
|
684
|
+
const detail = callDetail(args, 160);
|
|
685
|
+
emit(turn, { type: 'tool', callId: block.id, name: clip(block.name, 64), ...(detail ? { detail: detail.text, ...(detail.code ? { code: true } : {}) } : {}) });
|
|
605
686
|
};
|
|
606
687
|
try {
|
|
607
688
|
if (!isAbsolute(policy.cwd))
|
|
@@ -779,8 +860,12 @@ export class DshRuntime {
|
|
|
779
860
|
}
|
|
780
861
|
});
|
|
781
862
|
agentCtx.on('tools/result', (execution, result) => {
|
|
782
|
-
if (active
|
|
783
|
-
|
|
863
|
+
if (!active || typeof execution.callId !== 'string')
|
|
864
|
+
return;
|
|
865
|
+
// A command that ran but exited non-zero still succeeded as a tool call; show its code like the App.
|
|
866
|
+
const exitCode = result.value?.exitCode;
|
|
867
|
+
emit(active, { type: 'tool-done', callId: execution.callId, ok: !result.isError,
|
|
868
|
+
...(typeof exitCode === 'number' && Number.isInteger(exitCode) && exitCode !== 0 ? { exitCode } : {}) });
|
|
784
869
|
});
|
|
785
870
|
}
|
|
786
871
|
else {
|
|
@@ -854,6 +939,8 @@ export class DshRuntime {
|
|
|
854
939
|
throw new DshRuntimeError(resumeId ? 'RESTORE_FAILED' : 'SETUP_FAILED');
|
|
855
940
|
}
|
|
856
941
|
const agent = handle.agent;
|
|
942
|
+
if (projections && sessionChoice(projections, agent.session, defaultModel))
|
|
943
|
+
shared = agent.session;
|
|
857
944
|
// Keep this capability while the owner is active. Cordis unloads sibling
|
|
858
945
|
// effects concurrently, so native agent teardown may detach this session
|
|
859
946
|
// before the bridge's teardown reaches it.
|
|
@@ -992,8 +1079,22 @@ export class DshRuntime {
|
|
|
992
1079
|
throw new DshRuntimeError('DISPOSED');
|
|
993
1080
|
if (active || agent.status === 'running')
|
|
994
1081
|
throw new DshRuntimeError('BUSY');
|
|
995
|
-
|
|
1082
|
+
const choice = nativeSelection(modelSelection(next));
|
|
1083
|
+
if (!shared) {
|
|
1084
|
+
selected.current = choice;
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
// Recorded like the App selector's own pick, so the App shows it as this session's model.
|
|
1088
|
+
// Older SDK typings predate this event; only a host with the record gets here.
|
|
1089
|
+
try {
|
|
1090
|
+
agent.session.append('model/selection', { provider: choice.provider, model: choice.model,
|
|
1091
|
+
...(choice.reasoningEffort === undefined ? {} : { reasoningEffort: String(choice.reasoningEffort) }) });
|
|
1092
|
+
}
|
|
1093
|
+
catch {
|
|
1094
|
+
throw new DshRuntimeError('INVALID_MODEL_SELECTION');
|
|
1095
|
+
}
|
|
996
1096
|
},
|
|
1097
|
+
sharesModelSelection: Boolean(shared),
|
|
997
1098
|
permissionState: () => {
|
|
998
1099
|
const service = harness && !disposed ? permissionPresets(this.ctx) : undefined;
|
|
999
1100
|
const options = service ? permissionOptions(service) : [];
|
|
@@ -1006,7 +1107,8 @@ export class DshRuntime {
|
|
|
1006
1107
|
catch {
|
|
1007
1108
|
return;
|
|
1008
1109
|
}
|
|
1009
|
-
|
|
1110
|
+
const defaultPreset = permissionDefault(service, options);
|
|
1111
|
+
return { current: typeof current === 'string' ? current : 'custom', options, ...(defaultPreset ? { defaultPreset } : {}) };
|
|
1010
1112
|
},
|
|
1011
1113
|
setPermission: (id) => {
|
|
1012
1114
|
if (disposed)
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import type { ApprovalPrompt, QuestionAnswer, UserQuestion } from './dsh-runtime.js';
|
|
2
|
-
import { type ReplyPayload } from './replies.js';
|
|
2
|
+
import { type PanelTone, type ReplyPayload } from './replies.js';
|
|
3
3
|
/** A verified card click on a pending approval or question. */
|
|
4
4
|
export interface InteractionAnswer {
|
|
5
5
|
verb: 'approve' | 'deny' | 'choose' | 'reply';
|
|
6
6
|
index?: number;
|
|
7
7
|
formValues?: unknown;
|
|
8
8
|
}
|
|
9
|
+
/** How a card closed: allowed or answered (success), refused (error), or expired and stopped (muted). */
|
|
10
|
+
export interface InteractionOutcome {
|
|
11
|
+
text: string;
|
|
12
|
+
tone: PanelTone;
|
|
13
|
+
}
|
|
9
14
|
/** Buttons answer one short single-choice question in one tap; anything else uses a form. */
|
|
10
15
|
export declare function simpleQuestion(questions: readonly UserQuestion[]): boolean;
|
|
11
16
|
/** The decision card. Without tokens it shows the settled outcome and no buttons. */
|
|
12
17
|
export declare function approvalCard(prompt: ApprovalPrompt, tokens?: {
|
|
13
18
|
allow: string;
|
|
14
19
|
deny: string;
|
|
15
|
-
}, outcome?:
|
|
20
|
+
}, outcome?: InteractionOutcome): ReplyPayload;
|
|
16
21
|
/** A question card: buttons for one short choice, otherwise a form. Without tokens it shows the outcome. */
|
|
17
22
|
export declare function questionCard(questions: readonly UserQuestion[], tokens?: {
|
|
18
23
|
choose?: string[];
|
|
19
24
|
reply?: string;
|
|
20
|
-
}, outcome?:
|
|
25
|
+
}, outcome?: InteractionOutcome): ReplyPayload;
|
|
21
26
|
/** Map a click or form back to the host's answer shape, echoing the original option labels. */
|
|
22
27
|
export declare function questionAnswers(questions: readonly UserQuestion[], answer: InteractionAnswer): QuestionAnswer[] | undefined;
|
|
23
28
|
export declare function answeredText(questions: readonly UserQuestion[], answers: readonly QuestionAnswer[]): string;
|
|
@@ -14,12 +14,12 @@ export function simpleQuestion(questions) {
|
|
|
14
14
|
export function approvalCard(prompt, tokens, outcome) {
|
|
15
15
|
return renderPanel({
|
|
16
16
|
title: tokens ? '需要你确认' : '确认结果',
|
|
17
|
-
summary: `是否允许使用工具 ${prompt.toolName}`,
|
|
17
|
+
summary: outcome?.text ?? `是否允许使用工具 ${prompt.toolName}`,
|
|
18
|
+
...(outcome ? { tone: outcome.tone, status: outcome } : {}),
|
|
18
19
|
sections: [
|
|
19
20
|
{ title: '工具', text: code(prompt.toolName) },
|
|
20
21
|
...(prompt.reason ? [{ title: '原因', text: prompt.reason }] : []),
|
|
21
22
|
...(prompt.detail ? [{ title: '参数', text: fenced(prompt.detail) }] : []),
|
|
22
|
-
...(outcome ? [{ text: `**${outcome}**` }] : []),
|
|
23
23
|
],
|
|
24
24
|
...(tokens ? { actions: [{ label: '允许一次', value: { token: tokens.allow }, primary: true }, { label: '拒绝', value: { token: tokens.deny } }],
|
|
25
25
|
actionColumns: 2, note: '只允许这一次,不改变电脑上的权限设置。5 分钟内没有确认会按拒绝处理。' } : {}),
|
|
@@ -36,11 +36,11 @@ export function questionCard(questions, tokens, outcome) {
|
|
|
36
36
|
const title = questions.length === 1 && first.header ? first.header : '需要你选择';
|
|
37
37
|
const form = Boolean(tokens?.reply);
|
|
38
38
|
const base = renderPanel({
|
|
39
|
-
title, summary: `${title}:${first.question}`,
|
|
39
|
+
title, summary: outcome?.text ?? `${title}:${first.question}`,
|
|
40
|
+
...(outcome ? { tone: outcome.tone, status: outcome } : {}),
|
|
40
41
|
sections: [
|
|
41
42
|
...(form ? [{ text: questions.length > 1 ? `请回答下面 ${questions.length} 个问题,然后提交。` : '请选择或填写后提交。' }]
|
|
42
43
|
: questions.map(question => ({ text: questionText(question) }))),
|
|
43
|
-
...(outcome ? [{ text: `**${outcome}**` }] : []),
|
|
44
44
|
],
|
|
45
45
|
...(tokens?.choose ? { actions: [...first.options.map((option, index) => ({ label: option.label, value: { token: tokens.choose[index] }, primary: index === 0 })),
|
|
46
46
|
{ label: '跳过', value: { token: tokens.choose[first.options.length] } }], actionColumns: 1 } : {}),
|
package/dist/live-reply.d.ts
CHANGED
|
@@ -22,10 +22,12 @@ export interface LiveReplyFinal {
|
|
|
22
22
|
note?: string;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* One streaming reply card: the answer, with
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
25
|
+
* One streaming reply card: the answer, with the process in a panel that
|
|
26
|
+
* stays folded unless the reader opens it. Like the App, the process is a
|
|
27
|
+
* list of steps, thinking and tool calls, each on its own row. Only the
|
|
28
|
+
* initial reply is a send; every later operation updates the same card
|
|
29
|
+
* entity and is safe to supersede or repeat. A reply never continues in
|
|
30
|
+
* another message.
|
|
29
31
|
*/
|
|
30
32
|
export declare class LiveReply {
|
|
31
33
|
private readonly transport;
|
|
@@ -33,15 +35,18 @@ export declare class LiveReply {
|
|
|
33
35
|
private readonly options;
|
|
34
36
|
private phase;
|
|
35
37
|
private readonly steps;
|
|
38
|
+
private nextStep;
|
|
36
39
|
private trimmed;
|
|
37
40
|
private answer;
|
|
38
41
|
private separator;
|
|
39
42
|
private mark;
|
|
40
43
|
private tools;
|
|
41
44
|
private note?;
|
|
45
|
+
private footer?;
|
|
42
46
|
private cardId?;
|
|
43
47
|
private sequence;
|
|
44
48
|
private readonly sent;
|
|
49
|
+
private shown;
|
|
45
50
|
private dirty;
|
|
46
51
|
private layoutDirty;
|
|
47
52
|
private degraded;
|
|
@@ -60,6 +65,8 @@ export declare class LiveReply {
|
|
|
60
65
|
}, options?: LiveReplyOptions);
|
|
61
66
|
/** Send the card. False means nothing was sent, so the caller keeps its ordinary reply. */
|
|
62
67
|
open(): Promise<boolean>;
|
|
68
|
+
/** Trusted settings line under the answer, such as the model and thinking depth in use. */
|
|
69
|
+
setFooter(text: string): void;
|
|
63
70
|
push(event: TurnStreamEvent): void;
|
|
64
71
|
/** Stop streaming and write the final card. True only when Feishu confirmed it. */
|
|
65
72
|
finish(final: LiveReplyFinal): Promise<boolean>;
|
|
@@ -73,15 +80,22 @@ export declare class LiveReply {
|
|
|
73
80
|
/** Pacing between updates; finishing the reply ends it early. */
|
|
74
81
|
private pause;
|
|
75
82
|
private flush;
|
|
83
|
+
/** Streamable text elements of a card: its process rows and the answer. */
|
|
84
|
+
private textElements;
|
|
76
85
|
private markSent;
|
|
77
|
-
|
|
78
|
-
private
|
|
86
|
+
/** One step as a row, in the App's words: what was done and on what. */
|
|
87
|
+
private row;
|
|
79
88
|
private answerContent;
|
|
80
89
|
private panelTitle;
|
|
81
|
-
/** The folded process panel. Readers open it when they want the
|
|
90
|
+
/** The folded process panel. Readers open it when they want the thinking and tool steps. */
|
|
82
91
|
private panel;
|
|
83
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* The rows that fit in maxBytes, newest last. Older steps give way first and
|
|
94
|
+
* leave a note; a single oversized thought keeps its most recent part.
|
|
95
|
+
*/
|
|
96
|
+
private rows;
|
|
97
|
+
/** Live layout when final is undefined; a final layout otherwise (null leaves out the panel's rows). */
|
|
84
98
|
private card;
|
|
85
|
-
/** The answer comes first. The folded process
|
|
99
|
+
/** The answer comes first. The folded process gets all the space that is left. */
|
|
86
100
|
private finalCard;
|
|
87
101
|
}
|