@evandu/super-pi 0.1.132 → 0.1.135

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 (69) hide show
  1. package/dist/.super-pi-check-inputs.json +4 -0
  2. package/dist/client/.super-pi-client-sources.json +8 -8
  3. package/dist/client/assets/{CodeMirrorEditor-ktyL2PcX.js → CodeMirrorEditor-D6awRUZO.js} +2 -2
  4. package/dist/client/assets/{dist--igDAzRS.js → dist-1jSyapdW.js} +1 -1
  5. package/dist/client/assets/{dist-CPLCQDqy.js → dist-BGgidsaF.js} +1 -1
  6. package/dist/client/assets/{dist-DNZMXYNq.js → dist-BYMYC_2I.js} +1 -1
  7. package/dist/client/assets/{dist-vsMBLoxV.js → dist-BkVaPA_q.js} +1 -1
  8. package/dist/client/assets/{dist-B8OZ-n_C.js → dist-BoeM5xW7.js} +1 -1
  9. package/dist/client/assets/{dist-D_Zdnhm8.js → dist-C46474ys.js} +1 -1
  10. package/dist/client/assets/{dist-Cu75NyQ0.js → dist-CCkdCH6a.js} +1 -1
  11. package/dist/client/assets/{dist-BJi5HmH4.js → dist-CNuKauOq.js} +1 -1
  12. package/dist/client/assets/{dist-BAJCrbnX.js → dist-CubYGjAk.js} +1 -1
  13. package/dist/client/assets/{dist-8pdzh6h1.js → dist-Cwb-Lb5-.js} +1 -1
  14. package/dist/client/assets/{dist-BRjGckmQ.js → dist-D0Iw5XQA.js} +1 -1
  15. package/dist/client/assets/{dist-BjZUYxLd.js → dist-DCiuVvGU.js} +1 -1
  16. package/dist/client/assets/{dist-Hj_dWgPr.js → dist-DHBWUQhM.js} +1 -1
  17. package/dist/client/assets/{dist-CoszOk5k.js → dist-DOwrSiuY.js} +1 -1
  18. package/dist/client/assets/{dist-Bg9zLfex.js → dist-DeZsNbFx.js} +1 -1
  19. package/dist/client/assets/{dist-C6iAHGvb.js → dist-Dk3e6RfY.js} +1 -1
  20. package/dist/client/assets/{dist-Bv6P2Rcp.js → dist-DoDmoWTn.js} +1 -1
  21. package/dist/client/assets/{dist-CgmISAMr.js → dist-WkVUFX28.js} +1 -1
  22. package/dist/client/assets/{dist-DvHghuGd.js → dist-lZUWPQbT.js} +1 -1
  23. package/dist/client/assets/{dist-Zhdkfvng.js → dist-mPR8uuzk.js} +1 -1
  24. package/dist/client/assets/{dist-60usx__o.js → dist-spB2Q2CG.js} +1 -1
  25. package/dist/client/assets/{dist-CMscy2Nn.js → dist-yTTCht0J.js} +1 -1
  26. package/dist/client/assets/index-Dyta-HkQ.js +100 -0
  27. package/dist/client/assets/{index-D3MEG8uH.css → index-V93Kzott.css} +1 -1
  28. package/dist/client/index.html +2 -2
  29. package/dist/server/active-memory.d.ts +1 -0
  30. package/dist/server/active-memory.js +6 -2
  31. package/dist/server/agents.js +21 -1
  32. package/dist/server/application/feishu-service.d.ts +57 -10
  33. package/dist/server/application/feishu-service.js +118 -24
  34. package/dist/server/application/run-service.d.ts +4 -0
  35. package/dist/server/application/run-service.js +123 -88
  36. package/dist/server/application/workspace-service.d.ts +8 -4
  37. package/dist/server/application/workspace-service.js +11 -10
  38. package/dist/server/failure-text.d.ts +18 -0
  39. package/dist/server/failure-text.js +44 -0
  40. package/dist/server/feishu-cli.d.ts +18 -2
  41. package/dist/server/feishu-cli.js +46 -12
  42. package/dist/server/feishu-config.d.ts +68 -0
  43. package/dist/server/feishu-config.js +84 -0
  44. package/dist/server/feishu.d.ts +187 -0
  45. package/dist/server/feishu.js +695 -0
  46. package/dist/server/http-server.js +36 -5
  47. package/dist/server/index.js +5 -2
  48. package/dist/server/knowledge-embedding-worker.d.ts +1 -0
  49. package/dist/server/knowledge-embedding-worker.js +27 -0
  50. package/dist/server/knowledge-vectors.d.ts +5 -3
  51. package/dist/server/knowledge-vectors.js +49 -47
  52. package/dist/server/knowledge.d.ts +1 -0
  53. package/dist/server/knowledge.js +6 -2
  54. package/dist/server/persistence/sqlite-state-repository.d.ts +0 -1
  55. package/dist/server/persistence/sqlite-state-repository.js +55 -1
  56. package/dist/server/pi-session-events.js +10 -2
  57. package/dist/server/pi-session-pool.d.ts +33 -1
  58. package/dist/server/pi-session-pool.js +20 -2
  59. package/dist/server/providers.d.ts +13 -0
  60. package/dist/server/providers.js +27 -2
  61. package/dist/server/runtime.d.ts +26 -1
  62. package/dist/server/runtime.js +98 -9
  63. package/dist/server/store.d.ts +61 -0
  64. package/dist/server/store.js +112 -14
  65. package/dist/server/types.d.ts +10 -0
  66. package/dist/server/ws-server.d.ts +3 -0
  67. package/dist/server/ws-server.js +35 -6
  68. package/package.json +25 -2
  69. package/dist/client/assets/index-ChyemF2I.js +0 -100
@@ -8,8 +8,8 @@
8
8
  <link rel="alternate icon" href="/favicon.ico" />
9
9
  <link rel="apple-touch-icon" href="/favicon.svg" />
10
10
  <title>Super Pi</title>
11
- <script type="module" crossorigin src="/assets/index-ChyemF2I.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-D3MEG8uH.css">
11
+ <script type="module" crossorigin src="/assets/index-Dyta-HkQ.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-V93Kzott.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -61,6 +61,7 @@ export declare class ActiveMemory {
61
61
  }): string;
62
62
  release(token: string): void;
63
63
  private identity;
64
+ private hasWorkspace;
64
65
  search(workspaceId: string, query: string, sessionId?: string): {
65
66
  conversationId: string;
66
67
  title: string;
@@ -109,10 +109,14 @@ export class ActiveMemory {
109
109
  release(token) { this.capabilities.delete(token); }
110
110
  identity(token) {
111
111
  const identity = this.capabilities.get(token);
112
- if (!identity || !this.store.snapshot().workspaces.some(w => w.id === identity.workspaceId))
112
+ if (!identity || !this.hasWorkspace(identity.workspaceId))
113
113
  throw new Error("记忆授权已失效");
114
114
  return identity;
115
115
  }
116
+ hasWorkspace(workspaceId) {
117
+ const store = this.store;
118
+ return store.hasWorkspace ? store.hasWorkspace(workspaceId) : store.snapshot().workspaces.some(w => w.id === workspaceId);
119
+ }
116
120
  search(workspaceId, query, sessionId) {
117
121
  const words = query.toLocaleLowerCase().match(/[\p{L}\p{N}_-]{2,}/gu)?.slice(0, 12) ?? [];
118
122
  if (!words.length)
@@ -232,7 +236,7 @@ export class ActiveMemory {
232
236
  return alreadyLocked ? work() : this.locked(work);
233
237
  }
234
238
  async manage(workspaceId, body) {
235
- if (!this.store.snapshot().workspaces.some(w => w.id === workspaceId))
239
+ if (!this.hasWorkspace(workspaceId))
236
240
  throw new Error("工作区不存在");
237
241
  if (body.action === "configure") {
238
242
  await this.configure(body.settings);
@@ -447,7 +447,27 @@ export async function readAgents(directory, options = {}) {
447
447
  throw error;
448
448
  }
449
449
  for (const file of entries.filter((name) => name.toLowerCase().endsWith(".md")).sort()) {
450
- const source = await readFile(join(dir, file), "utf8");
450
+ // A FILE THAT CANNOT BE READ IS THE SAME KIND OF PROBLEM AS ONE THAT CANNOT BE PARSED.
451
+ //
452
+ // The function's own reason for existing says it plainly: "one typo in one agent must not take
453
+ // down the picker and hide every other agent with it". A file that cannot be READ is at least as
454
+ // ordinary as that - on Windows "another program has this file open" is a Tuesday, not an
455
+ // emergency - and it used to be the one unguarded step in this loop: the parse failure below is
456
+ // collected as a problem, while the read failure was thrown, so a single locked file emptied the
457
+ // whole directory. Four callers ate that: the main agent's own definition (the turn could not
458
+ // start), the roster handed to sub-agents on every turn, the roster used when dispatching one,
459
+ // and the picker (which came back with an empty agent list).
460
+ let source;
461
+ try {
462
+ source = await readFile(join(dir, file), "utf8");
463
+ }
464
+ catch (error) {
465
+ const fileId = file.replace(/\.md$/i, "");
466
+ if (!seenIds.has(fileId)) {
467
+ problems.push({ file, message: `读取失败:${error instanceof Error ? error.message : String(error)}` });
468
+ }
469
+ continue;
470
+ }
451
471
  const parsed = parseDefinition(file, source);
452
472
  if ("message" in parsed) {
453
473
  const fileId = file.replace(/\.md$/i, "");
@@ -23,8 +23,31 @@ export type FeishuStatus = {
23
23
  identity?: "user" | "bot";
24
24
  /** Whether the agent can read and write documents right now. */
25
25
  connected: boolean;
26
+ /**
27
+ * WHICH STEP OF THE GUIDE THE PERSON IS ON.
28
+ *
29
+ * 1 = install the CLI, 2 = connect the account, 3 = ready. The panel draws the three steps and
30
+ * marks this one, so "what do I do now" is answered by where the cursor is rather than by a
31
+ * sentence the person has to interpret.
32
+ */
33
+ step: 1 | 2 | 3;
26
34
  /** What to do next, in one sentence. */
27
35
  message: string;
36
+ /** The CLI's own words, when it had something to say about the situation. */
37
+ detail?: string;
38
+ /** The CLI's own hint, which usually names the exact command to run. */
39
+ hint?: string;
40
+ /** Whether the check actually reached Feishu. */
41
+ verified?: boolean;
42
+ /**
43
+ * WHETHER THE APPLICATION ITSELF HAS TO BE REBUILT.
44
+ *
45
+ * Distinct from "step 2": a person whose application is fine but who has not logged in still
46
+ * needs step 2, while a person whose credential was refused needs a NEW application. Telling
47
+ * those two apart is what keeps a transient network failure from being answered by "let me
48
+ * create you another application".
49
+ */
50
+ rebuild?: boolean;
28
51
  };
29
52
  /** The outcome of starting the connect flow. */
30
53
  export type FeishuConnectResult = {
@@ -50,35 +73,59 @@ export declare class FeishuService {
50
73
  private readonly cli;
51
74
  /** The device code of a login that is waiting for the person to authorize it. */
52
75
  private pendingDeviceCode?;
76
+ /** Whether an application registration is running and waiting for the browser confirmation. */
77
+ private pendingAppCreation;
53
78
  constructor(cli?: FeishuCli);
54
79
  /**
55
80
  * WHAT THE CLI CAN DO RIGHT NOW.
56
81
  *
57
- * The three states a person can be in, and what each one means for the agent:
82
+ * The states a person can be in, and what each one means for the agent:
58
83
  *
59
84
  * - The CLI is not installed: nothing works, and the fix is one command.
60
85
  * - The CLI has no application: `config init --new` creates one through a browser confirmation,
61
86
  * so the developer console is never visited.
62
- * - The CLI has an application but no person logged in: the agent can still act as the
63
- * application, but it cannot reach that person's own documents until they authorize it.
87
+ * - The application exists but its credential is no longer accepted: the CLI says
88
+ * `verify_failed` and names the reason, and the fix is a new application - which is the same
89
+ * browser confirmation, so it stays one button.
90
+ * - The application works but no person is logged in: the agent can act as the application, but
91
+ * it cannot reach that person's own documents until they authorize it.
64
92
  */
65
93
  status(): Promise<FeishuStatus>;
94
+ /**
95
+ * CHECKS THE CONNECTION, and says whether it actually works.
96
+ *
97
+ * The status already verifies; this is the same answer shaped for a button, so that a person can
98
+ * ask "it still doesn't work, why?" and get the CLI's own verdict rather than a guess.
99
+ */
100
+ verify(): Promise<{
101
+ ok: boolean;
102
+ message: string;
103
+ detail?: string;
104
+ hint?: string;
105
+ }>;
66
106
  /**
67
107
  * STARTS THE CONNECT FLOW.
68
108
  *
69
109
  * Two steps, and which one runs is decided by what the CLI already has:
70
110
  *
71
- * - No application: `config init --new` registers one through a browser confirmation. It BLOCKS
72
- * until the person confirms, so it runs in the background and the URL is taken from its output.
73
- * - An application but no person: `auth login --no-wait --json` asks for a device code and hands
74
- * back the URL to open. The code is kept here until the person says they are done.
111
+ * - No application, or one whose credential no longer works: `config init --new` registers a
112
+ * fresh application through a browser confirmation. It BLOCKS until the person confirms, so it
113
+ * runs in the background and the URL is taken from its output. The broken case goes here too,
114
+ * because a new application is the one repair that needs no secret to be typed into a form.
115
+ * - A working application but no person: `auth login --no-wait --json` asks for a device code
116
+ * and hands back the URL to open. The code is kept here until the person says they are done.
75
117
  */
76
118
  connect(): Promise<FeishuConnectResult>;
77
119
  /**
78
- * FINISHES THE LOGIN, once the person says they authorized it.
120
+ * FINISHES THE FLOW, once the person says they are done.
121
+ *
122
+ * TWO KINDS OF "DONE", and the difference is what the CLI is waiting for:
79
123
  *
80
- * The device code is handed back to the CLI, which completes the exchange and stores the token
81
- * in the keychain. Super Pi never sees the token itself.
124
+ * - An application is being registered: `config init --new` finishes by itself once the person
125
+ * confirms in the browser, so there is nothing to hand back. The status is re-read and the
126
+ * answer is whether the application showed up.
127
+ * - A login is being authorized: the device code is handed back to the CLI, which completes the
128
+ * exchange and stores the token in the keychain. Super Pi never sees the token itself.
82
129
  */
83
130
  completeConnect(): Promise<FeishuConnectResult>;
84
131
  /**
@@ -17,19 +17,24 @@ export class FeishuService {
17
17
  cli;
18
18
  /** The device code of a login that is waiting for the person to authorize it. */
19
19
  pendingDeviceCode;
20
+ /** Whether an application registration is running and waiting for the browser confirmation. */
21
+ pendingAppCreation = false;
20
22
  constructor(cli = new FeishuCli()) {
21
23
  this.cli = cli;
22
24
  }
23
25
  /**
24
26
  * WHAT THE CLI CAN DO RIGHT NOW.
25
27
  *
26
- * The three states a person can be in, and what each one means for the agent:
28
+ * The states a person can be in, and what each one means for the agent:
27
29
  *
28
30
  * - The CLI is not installed: nothing works, and the fix is one command.
29
31
  * - The CLI has no application: `config init --new` creates one through a browser confirmation,
30
32
  * so the developer console is never visited.
31
- * - The CLI has an application but no person logged in: the agent can still act as the
32
- * application, but it cannot reach that person's own documents until they authorize it.
33
+ * - The application exists but its credential is no longer accepted: the CLI says
34
+ * `verify_failed` and names the reason, and the fix is a new application - which is the same
35
+ * browser confirmation, so it stays one button.
36
+ * - The application works but no person is logged in: the agent can act as the application, but
37
+ * it cannot reach that person's own documents until they authorize it.
33
38
  */
34
39
  async status() {
35
40
  const status = await this.cli.status();
@@ -39,59 +44,121 @@ export class FeishuService {
39
44
  userReady: false,
40
45
  botReady: false,
41
46
  connected: false,
42
- message: "没有检测到官方飞书 CLI(lark-cli)。请先在终端运行 npm i -g @larksuite/cli 装上它,再回到这里点「连接飞书账号」。如果已经装了但 Super Pi 找不到(两边的 PATH 不一样),用环境变量 SUPER_PI_FEISHU_CLI 指向它的完整路径再重启 Super Pi。",
47
+ step: 1,
48
+ message: "还没有安装官方飞书 CLI。在终端里跑一次 npm i -g @larksuite/cli 就行,装完回到这里点「连接飞书账号」。",
49
+ ...(status.hint ? { hint: status.hint } : {}),
43
50
  };
44
51
  }
52
+ const common = {
53
+ installed: true,
54
+ ...(status.version ? { version: status.version } : {}),
55
+ ...(status.appId ? { appId: status.appId } : {}),
56
+ ...(status.verified !== undefined ? { verified: status.verified } : {}),
57
+ ...(status.note ? { detail: status.note } : {}),
58
+ };
45
59
  if (!status.appId) {
46
60
  return {
47
- installed: true,
48
- ...(status.version ? { version: status.version } : {}),
61
+ ...common,
62
+ userReady: false,
63
+ botReady: false,
64
+ connected: false,
65
+ step: 2,
66
+ message: "CLI 已就绪,还差一个应用。点「连接飞书账号」,浏览器里确认一下就有了——不用去开发者后台注册。",
67
+ };
68
+ }
69
+ // A CREDENTIAL THE CLI KNOWS IS BROKEN is the case a plain status would have hidden: the local
70
+ // file still holds an app, so the panel would have said "已配置", and every document call
71
+ // would have failed. The CLI's own verdict is the one that can be acted on.
72
+ const broken = status.botStatus === "verify_failed" || (status.verified === true && !status.botReady && !status.userReady);
73
+ if (broken) {
74
+ return {
75
+ ...common,
49
76
  userReady: false,
50
77
  botReady: false,
51
78
  connected: false,
52
- message: "飞书 CLI 已就绪,但还没有应用。点「连接飞书账号」,它会给你一个浏览器链接,确认之后应用就建好了——不用去开发者后台注册。",
79
+ step: 2,
80
+ rebuild: true,
81
+ message: `应用的凭据已经失效:${trimSentence(status.botMessage ?? "飞书拒绝了这次验证")}。点「连接飞书账号」会重新走一遍确认流程、建一个可用的应用。`,
82
+ ...(status.hint ? { hint: status.hint } : {}),
83
+ };
84
+ }
85
+ if (status.verified === false) {
86
+ return {
87
+ ...common,
88
+ userReady: false,
89
+ botReady: false,
90
+ connected: false,
91
+ step: 2,
92
+ message: "没能连上飞书(本地配置读到了,但验证请求没出去)。检查一下网络,然后点「测试连接」重试。",
93
+ ...(status.hint ? { hint: status.hint } : {}),
53
94
  };
54
95
  }
55
96
  if (!status.userReady) {
56
97
  return {
57
- installed: true,
58
- ...(status.version ? { version: status.version } : {}),
59
- appId: status.appId,
98
+ ...common,
60
99
  userReady: false,
61
100
  botReady: status.botReady,
62
101
  connected: false,
102
+ step: 2,
63
103
  ...(status.identity ? { identity: status.identity } : {}),
64
- message: "应用已配置,但还没有登录你的账号。点「连接飞书账号」用浏览器授权,授权之后 Agent 就能以你的身份读写你自己的文档,不用逐篇加协作者。",
104
+ message: "应用已经能用,但还没登录你的账号。点「连接飞书账号」在浏览器里授权一下,授权之后 Agent 就能以你的身份读写你自己的文档,不用逐篇加协作者。",
105
+ ...(status.hint ? { hint: status.hint } : {}),
65
106
  };
66
107
  }
67
108
  return {
68
- installed: true,
69
- ...(status.version ? { version: status.version } : {}),
70
- appId: status.appId,
109
+ ...common,
71
110
  userReady: true,
72
111
  botReady: status.botReady,
73
112
  connected: true,
74
113
  identity: status.identity ?? "user",
114
+ step: 3,
75
115
  message: "已连接:Agent 会以你的身份读写飞书云文档。",
76
116
  };
77
117
  }
118
+ /**
119
+ * CHECKS THE CONNECTION, and says whether it actually works.
120
+ *
121
+ * The status already verifies; this is the same answer shaped for a button, so that a person can
122
+ * ask "it still doesn't work, why?" and get the CLI's own verdict rather than a guess.
123
+ */
124
+ async verify() {
125
+ const status = await this.status();
126
+ if (status.connected) {
127
+ return {
128
+ ok: true,
129
+ message: status.message,
130
+ ...(status.detail ? { detail: status.detail } : {}),
131
+ };
132
+ }
133
+ return {
134
+ ok: false,
135
+ message: status.message,
136
+ ...(status.detail ? { detail: status.detail } : {}),
137
+ ...(status.hint ? { hint: status.hint } : {}),
138
+ };
139
+ }
78
140
  /**
79
141
  * STARTS THE CONNECT FLOW.
80
142
  *
81
143
  * Two steps, and which one runs is decided by what the CLI already has:
82
144
  *
83
- * - No application: `config init --new` registers one through a browser confirmation. It BLOCKS
84
- * until the person confirms, so it runs in the background and the URL is taken from its output.
85
- * - An application but no person: `auth login --no-wait --json` asks for a device code and hands
86
- * back the URL to open. The code is kept here until the person says they are done.
145
+ * - No application, or one whose credential no longer works: `config init --new` registers a
146
+ * fresh application through a browser confirmation. It BLOCKS until the person confirms, so it
147
+ * runs in the background and the URL is taken from its output. The broken case goes here too,
148
+ * because a new application is the one repair that needs no secret to be typed into a form.
149
+ * - A working application but no person: `auth login --no-wait --json` asks for a device code
150
+ * and hands back the URL to open. The code is kept here until the person says they are done.
87
151
  */
88
152
  async connect() {
89
153
  const status = await this.status();
90
154
  if (!status.installed) {
91
155
  return { ok: false, message: status.message };
92
156
  }
93
- if (!status.appId) {
157
+ // Step 2 is "connect the account"; the application part of it is missing or broken when the
158
+ // person has no application OR when the one they have was refused by Feishu.
159
+ if (!status.appId || status.rebuild) {
94
160
  const verificationUri = await this.cli.startAppCreation();
161
+ this.pendingAppCreation = true;
95
162
  return {
96
163
  ok: true,
97
164
  step: "app",
@@ -119,12 +186,31 @@ export class FeishuService {
119
186
  }
120
187
  }
121
188
  /**
122
- * FINISHES THE LOGIN, once the person says they authorized it.
189
+ * FINISHES THE FLOW, once the person says they are done.
123
190
  *
124
- * The device code is handed back to the CLI, which completes the exchange and stores the token
125
- * in the keychain. Super Pi never sees the token itself.
191
+ * TWO KINDS OF "DONE", and the difference is what the CLI is waiting for:
192
+ *
193
+ * - An application is being registered: `config init --new` finishes by itself once the person
194
+ * confirms in the browser, so there is nothing to hand back. The status is re-read and the
195
+ * answer is whether the application showed up.
196
+ * - A login is being authorized: the device code is handed back to the CLI, which completes the
197
+ * exchange and stores the token in the keychain. Super Pi never sees the token itself.
126
198
  */
127
199
  async completeConnect() {
200
+ if (this.pendingAppCreation) {
201
+ this.pendingAppCreation = false;
202
+ const status = await this.status();
203
+ if (status.appId) {
204
+ return {
205
+ ok: true,
206
+ message: "应用建好了。接下来点「连接飞书账号」授权你的账号,Agent 就能以你的身份读写文档了。",
207
+ };
208
+ }
209
+ return {
210
+ ok: false,
211
+ message: "还没看到应用建好。请确认浏览器里的确认已经提交(有时要等 CLI 写完钥匙串),然后再点一次「我已完成」。",
212
+ };
213
+ }
128
214
  if (!this.pendingDeviceCode) {
129
215
  return { ok: false, message: "没有待完成的登录。点「连接飞书账号」重新开始。" };
130
216
  }
@@ -235,8 +321,16 @@ export class FeishuService {
235
321
  throw new Error(`未知的飞书工具操作:${action}`);
236
322
  }
237
323
  }
238
- /** The message of a failure, whatever shape the failure has. */
239
- function messageOf(error) {
324
+ /**
325
+ * A SENTENCE WITHOUT ITS OWN FULL STOP.
326
+ *
327
+ * The CLI writes English sentences that end in a period, and this service writes Chinese ones that
328
+ * end in 。 — so a message pasted in verbatim reads "invalid.。点「重新连接」".
329
+ */
330
+ function trimSentence(text) {
331
+ return text.trim().replace(/[.\s]+$/, "");
332
+ }
333
+ /** The message of a failure, whatever shape the failure has. */ function messageOf(error) {
240
334
  if (error instanceof FeishuCliError)
241
335
  return error.message;
242
336
  if (error instanceof Error)
@@ -61,6 +61,7 @@ export declare class RunService {
61
61
  private readonly extensionUiService;
62
62
  private agentsDir;
63
63
  private readonly workflows;
64
+ private readonly questionWaiters;
64
65
  private memory?;
65
66
  setMemory(memory: import("../active-memory.js").ActiveMemory): void;
66
67
  /**
@@ -178,6 +179,9 @@ export declare class RunService {
178
179
  answers?: QuestionAnswer[];
179
180
  cancelled?: boolean;
180
181
  }): Promise<void>;
182
+ private questionKey;
183
+ private answerPayload;
184
+ private settleQuestion;
181
185
  /**
182
186
  * THE `question` TOOL BLOCKS HERE, ON THE CALL THAT ASKED IT.
183
187
  *