larkway 0.3.20 → 0.3.21
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 +1 -1
- package/README.zh.md +1 -1
- package/dist/cli/index.js +20 -6
- package/dist/main.js +257 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
You @ the bot in a Feishu thread. It runs on your machine — reading your real codebase, executing commands, opening MRs — and posts the result back. You define what the agent knows and what it can do. Larkway just carries the messages.
|
|
10
10
|
|
|
11
|
-
**Current release: v0.3.
|
|
11
|
+
**Current release: v0.3.21**
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
package/README.zh.md
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -111269,6 +111269,20 @@ var init_channelPostClient = __esm({
|
|
|
111269
111269
|
}
|
|
111270
111270
|
return { messageId };
|
|
111271
111271
|
}
|
|
111272
|
+
async updatePost(messageId, content) {
|
|
111273
|
+
const res = await withPostRetry(
|
|
111274
|
+
"updatePost",
|
|
111275
|
+
() => this.channel().rawClient.im.v1.message.update({
|
|
111276
|
+
path: { message_id: messageId },
|
|
111277
|
+
data: {
|
|
111278
|
+
content,
|
|
111279
|
+
msg_type: "post"
|
|
111280
|
+
}
|
|
111281
|
+
}),
|
|
111282
|
+
{ maxAttempts: this.maxAttempts, baseDelayMs: this.baseDelayMs }
|
|
111283
|
+
);
|
|
111284
|
+
return { messageId: res.data?.message_id ?? messageId };
|
|
111285
|
+
}
|
|
111272
111286
|
};
|
|
111273
111287
|
}
|
|
111274
111288
|
});
|
|
@@ -123680,7 +123694,7 @@ function defaultResponseSurfacePrototypeConfig() {
|
|
|
123680
123694
|
enabled: true,
|
|
123681
123695
|
allowed_chats: [],
|
|
123682
123696
|
allowed_threads: [],
|
|
123683
|
-
lazy_card_creation:
|
|
123697
|
+
lazy_card_creation: true,
|
|
123684
123698
|
kill_switch: false,
|
|
123685
123699
|
post_outbound_enabled: true,
|
|
123686
123700
|
allow_agent_mentions: true,
|
|
@@ -123697,7 +123711,7 @@ var responseSurfacePrototypeConfigDefaults = () => ({
|
|
|
123697
123711
|
enabled: true,
|
|
123698
123712
|
allowed_chats: [],
|
|
123699
123713
|
allowed_threads: [],
|
|
123700
|
-
lazy_card_creation:
|
|
123714
|
+
lazy_card_creation: true,
|
|
123701
123715
|
kill_switch: false,
|
|
123702
123716
|
post_outbound_enabled: true,
|
|
123703
123717
|
allow_agent_mentions: true,
|
|
@@ -123764,11 +123778,11 @@ var ResponseSurfacePrototypeConfigSchema = external_exports.object({
|
|
|
123764
123778
|
*/
|
|
123765
123779
|
allowed_threads: external_exports.array(external_exports.string().min(1)).default([]),
|
|
123766
123780
|
/**
|
|
123767
|
-
*
|
|
123768
|
-
* the
|
|
123769
|
-
*
|
|
123781
|
+
* Post-first gate. Default true means the bridge starts a lightweight post
|
|
123782
|
+
* as the live main surface when post outbound is available, and creates a
|
|
123783
|
+
* card only for fallback or card-only capabilities.
|
|
123770
123784
|
*/
|
|
123771
|
-
lazy_card_creation: external_exports.boolean().default(
|
|
123785
|
+
lazy_card_creation: external_exports.boolean().default(true),
|
|
123772
123786
|
/**
|
|
123773
123787
|
* Runtime kill switch for emergency rollback. When true, every response
|
|
123774
123788
|
* surface post path is treated as disabled even if enabled/allowlists are
|
package/dist/main.js
CHANGED
|
@@ -115497,6 +115497,20 @@ var ChannelPostClient = class {
|
|
|
115497
115497
|
}
|
|
115498
115498
|
return { messageId };
|
|
115499
115499
|
}
|
|
115500
|
+
async updatePost(messageId, content) {
|
|
115501
|
+
const res = await withPostRetry(
|
|
115502
|
+
"updatePost",
|
|
115503
|
+
() => this.channel().rawClient.im.v1.message.update({
|
|
115504
|
+
path: { message_id: messageId },
|
|
115505
|
+
data: {
|
|
115506
|
+
content,
|
|
115507
|
+
msg_type: "post"
|
|
115508
|
+
}
|
|
115509
|
+
}),
|
|
115510
|
+
{ maxAttempts: this.maxAttempts, baseDelayMs: this.baseDelayMs }
|
|
115511
|
+
);
|
|
115512
|
+
return { messageId: res.data?.message_id ?? messageId };
|
|
115513
|
+
}
|
|
115500
115514
|
};
|
|
115501
115515
|
|
|
115502
115516
|
// src/lark/channelClient.ts
|
|
@@ -117434,10 +117448,12 @@ function renderStateContract(stateFilePath) {
|
|
|
117434
117448
|
const stateTarget = stateFilePath ? `\u6307\u5B9A\u8DEF\u5F84 \`${stateFilePath}\`` : "\u5DE5\u4F5C\u76EE\u5F55\u91CC\u7684 `.larkway/state.json`";
|
|
117435
117449
|
return [
|
|
117436
117450
|
"<state-contract>",
|
|
117437
|
-
"\u4F60\u548C\u8FD0\u8425\u4E4B\u95F4\u7684\u754C\u9762\u662F\u98DE\u4E66\u8BDD\u9898\u91CC\u7684
|
|
117438
|
-
"-
|
|
117439
|
-
"- \
|
|
117440
|
-
|
|
117451
|
+
"\u4F60\u548C\u8FD0\u8425\u4E4B\u95F4\u7684\u754C\u9762\u662F\u98DE\u4E66\u8BDD\u9898\u91CC\u7684 response surface,\u8FD9\u662F\u4E00\u4E2A thin-channel \u5916\u58F3:",
|
|
117452
|
+
"- \u9ED8\u8BA4\u4E3B\u56DE\u590D\u9762\u662F post/RichText:bridge \u8D77\u624B\u53D1\u4E00\u6761\u8F7B\u91CF\u201C\u6B63\u5728\u5904\u7406\u2026\u201Dpost,\u6267\u884C\u4E2D\u7528\u5206\u5757\u7EA7\u539F\u5730\u7F16\u8F91\u6A21\u62DF\u6D41\u5F0F\u8F93\u51FA,\u7ED3\u675F\u65F6\u7F16\u8F91\u6210\u5E72\u51C0\u7EC8\u7A3F\u3002",
|
|
117453
|
+
"- \u5361\u7247\u662F\u4F8B\u5916\u8865\u5145\u9762,\u53EA\u5728\u9700\u8981\u79BB\u6563\u9009\u62E9/\u6309\u94AE(`choices`)\u6216 post \u8868\u8FBE\u4E0D\u4E86\u7684\u7ED3\u6784\u5316\u5185\u5BB9(`content_blocks`/`image_blocks`,\u4F8B\u5982\u56FE\u7247\u7EC4\u3001\u9A8C\u6536\u6E05\u5355\u3001dev \u9884\u89C8)\u65F6\u624D\u8865\u4E00\u5F20\u3002",
|
|
117454
|
+
"- bridge \u8D1F\u8D23\u521B\u5EFA/\u7F16\u8F91 post\u3001\u5FC5\u8981\u65F6\u521B\u5EFA/\u66F4\u65B0\u98DE\u4E66\u5361\u7247\u3001\u8282\u6D41\u3001\u6E32\u67D3\u6B63\u6587\u3001\u628A choices \u8F6C\u6210\u6309\u94AE\u5E76\u628A\u70B9\u51FB\u503C\u56DE\u4F20\u7ED9\u4F60\u3002",
|
|
117455
|
+
"- \u4F60\u8D1F\u8D23\u628A\u6700\u7EC8\u7ED9\u8FD0\u8425\u770B\u7684\u6B63\u6587\u3001\u72B6\u6001\u3001\u4E0B\u4E00\u6B65\u95EE\u9898\u3001\u662F\u5426\u9700\u8981 choices/\u7ED3\u6784\u5316\u5361\u7247\u5199\u8FDB state.json\u3002",
|
|
117456
|
+
`\u4F60\u4E0D\u76F4\u63A5\u53D1/\u7F16\u8F91 bridge \u7BA1\u7406\u7684 post \u6216\u5361\u7247;\u4F60\u53EA\u5199 ${stateTarget},bridge \u8BFB\u5B83\u6765\u505A\u5B89\u5168\u6E32\u67D3\u3002`,
|
|
117441
117457
|
"**\u5B8C\u6210\u672C\u6B21\u54CD\u5E94\u524D\u5FC5\u987B**\u6839\u636E\u5F53\u524D\u5B9E\u9645\u72B6\u6001\u66F4\u65B0\u8FD9\u4E2A\u6587\u4EF6(\u539F\u5B50\u5199:\u5199 .tmp \u518D mv)\u3002",
|
|
117442
117458
|
"",
|
|
117443
117459
|
"\u4F60\u80FD\u5199\u7684\u5B57\u6BB5:",
|
|
@@ -117448,18 +117464,19 @@ function renderStateContract(stateFilePath) {
|
|
|
117448
117464
|
"- card_color: \u5361\u7247\u914D\u8272(\u53EF\u9009,success/failure/neutral,\u4E5F\u53EF\u76F4\u63A5\u5199 green/red/grey)",
|
|
117449
117465
|
"- image_blocks: \u53EF\u9009\u56FE\u7247\u9884\u89C8\u5757\u6570\u7EC4,\u6700\u591A 4 \u4E2A\u3002\u6BCF\u9879 `{img_key, alt?, title?, mode?, preview?}`; `img_key` \u5FC5\u987B\u662F\u5DF2\u4E0A\u4F20/\u53EF\u7528\u4E8E\u5361\u7247\u7684 Feishu \u56FE\u7247 key,`alt` \u7701\u7565\u65F6 bridge \u9ED8\u8BA4\u201C\u56FE\u7247\u9884\u89C8\u201D,`mode` \u53EA\u5141\u8BB8 `crop_center`/`fit_horizontal` \u5E76\u6620\u5C04\u5230 Card JSON 2.0 `scale_type`,`preview` \u9ED8\u8BA4 true\u3002bridge \u4E0D\u8D1F\u8D23\u4E0B\u8F7D/\u4E0A\u4F20/\u9009\u62E9\u56FE\u7247;\u8FD9\u4E9B\u7531\u4F60\u7528 lark-cli \u7B49\u5DE5\u5177\u5148\u5B8C\u6210\u3002",
|
|
117450
117466
|
'- content_blocks: \u53EF\u9009\u6709\u5E8F\u6B63\u6587\u5757\u6570\u7EC4,\u6700\u591A 12 \u4E2A block\u3001\u6700\u591A 4 \u4E2A image block\u3002\u53EA\u652F\u6301\u7A84 union:`{type:"markdown", content}` \u548C `{type:"image", img_key, alt?, title?, mode?, preview?}`;\u4E0D\u652F\u6301 raw card JSON\u3002\u7528\u4E8E\u6B63\u6587\u4E0E\u56FE\u7247\u4EA4\u9519\u6392\u7248,\u4F8B\u5982 markdown -> image -> markdown -> image\u3002\u82E5 `content_blocks` \u975E\u7A7A,bridge \u4EE5\u5B83\u4F5C\u4E3A\u4E3B\u6B63\u6587\u5E76\u5FFD\u7565 `last_message` + `image_blocks` \u7684\u6B63\u6587\u6E32\u67D3,\u907F\u514D\u91CD\u590D;\u82E5\u7701\u7565\u5219\u4FDD\u6301\u65E7 `last_message` + `image_blocks` \u884C\u4E3A\u3002',
|
|
117451
|
-
'- response_surface: \u53EF\u9009\
|
|
117467
|
+
'- response_surface: \u53EF\u9009\u8986\u76D6\u5B57\u6BB5,\u5F62\u5982 `{mode:"card"|"post"|"hybrid", primary?:"card"|"post", post?:{mentions:[{user_id,label?}]}, card?:{compact?:boolean, capabilities?:[...]}}`\u3002\u9ED8\u8BA4\u53EF\u4E0D\u5199;\u65E0\u663E\u5F0F card \u610F\u56FE\u65F6 bridge \u6309 post-first \u5904\u7406\u3002\u9700\u8981\u771F\u5B9E @ \u65F6\u628A\u76EE\u6807\u5199\u5165 `post.mentions`;\u4E0D\u8981\u5199 raw Feishu post/card JSON\u3002',
|
|
117452
117468
|
"- scheduled reply / daily social ops review card \u7B49\u9700\u8981\u201C\u5E73\u53F0\u6B63\u6587 + \u5339\u914D\u56FE\u7247\u201D\u540C\u6BB5\u76F8\u90BB\u5C55\u793A\u7684\u573A\u666F,\u5E94\u5148\u53D6\u5F97\u5404\u56FE\u7247 `img_key`,\u518D\u5199 `content_blocks` \u4E3A `\u5E73\u53F0 markdown -> \u5BF9\u5E94 image -> \u4E0B\u4E2A\u5E73\u53F0 markdown -> \u5BF9\u5E94 image`;\u4E0D\u8981\u7528\u5355\u72EC\u8BDD\u9898\u56FE\u7247\u6D88\u606F\u6216\u5C3E\u90E8 `image_blocks` \u4EE3\u66FF\u9A8C\u6536\u9762\u3002",
|
|
117453
117469
|
"- dev_url / mr_url / \u5176\u4F59\u4E1A\u52A1\u5B57\u6BB5:\u81EA\u7531\u5199\u5165,bridge \u4E0D\u611F\u77E5\u5176\u4E1A\u52A1\u542B\u4E49;\u8981\u8BA9\u8FD0\u8425\u770B\u5230,\u8BF7\u5199\u8FDB last_message",
|
|
117454
117470
|
"- updated_at: ISO 8601 timestamp",
|
|
117455
117471
|
"",
|
|
117456
|
-
"**\u7EDD\u4E0D\u81EA\u5DF1 `lark-cli api PATCH .../im/v1/messages/...` \u6539\
|
|
117457
|
-
"\u81EA\u5DF1
|
|
117458
|
-
"\u5199\u5B8C state.json \u5C31**\u5E72\u51C0\u7ED3\u675F\u672C\u8F6E**(\u4F60\u7684\u8FDB\u7A0B\u9000\u51FA \u2192 bridge \u624D
|
|
117459
|
-
"\u6302\u7740\u4E0D\u9000\u51FA = \
|
|
117472
|
+
"**\u7EDD\u4E0D\u81EA\u5DF1 `lark-cli api PATCH/PUT .../im/v1/messages/...` \u6539 bridge \u7BA1\u7406\u7684 post/card** \u2014\u2014 \u90A3\u662F bridge \u7684\u6D3B;",
|
|
117473
|
+
"\u81EA\u5DF1\u53D1/\u6539\u4E3B\u56DE\u590D\u9762\u4F1A\u548C bridge \u7684 post \u7F16\u8F91\u3001\u5361\u7247 finalize\u3001\u6309\u94AE\u56DE\u4F20\u3001\u5D29\u6E83\u6062\u590D\u51B2\u7A81\u3002\u4F60\u53EA\u5199 state.json,\u7F51\u7EDC\u66F4\u65B0\u4EA4\u7ED9 bridge\u3002",
|
|
117474
|
+
"\u5199\u5B8C state.json \u5C31**\u5E72\u51C0\u7ED3\u675F\u672C\u8F6E**(\u4F60\u7684\u8FDB\u7A0B\u9000\u51FA \u2192 bridge \u624D\u628A post/card \u6536\u655B\u6210\u7EC8\u6001;",
|
|
117475
|
+
"\u6302\u7740\u4E0D\u9000\u51FA = \u8BDD\u9898\u6C38\u8FDC\u5361\u5728\u300C\u6B63\u5728\u5904\u7406\u2026\u300D)\u3002",
|
|
117460
117476
|
"",
|
|
117461
117477
|
"\u5361\u7247\u5C55\u793A\u539F\u5219:",
|
|
117462
|
-
"-
|
|
117478
|
+
"- \u8FD0\u884C\u4E2D\u4E3B\u9762\u662F post,bridge \u901A\u8FC7\u6574\u6761\u5BCC\u6587\u672C\u66FF\u6362\u505A\u5206\u5757\u5237\u65B0;\u5B83\u4E0D\u662F\u9010\u5B57 token streaming,\u4E0D\u8981\u4F9D\u8D56\u5DE5\u5177\u6D41/\u65E5\u5FD7\u8868\u8FBE\u4E1A\u52A1\u9636\u6BB5\u3002",
|
|
117479
|
+
"- \u6700\u7EC8 post \u4EE5\u4F60\u7684 `last_message` \u4E3A\u4E3B;\u82E5\u5199\u4E86 `choices`\u3001`content_blocks` \u6216 `image_blocks`,bridge \u4F1A\u5728 post \u4E4B\u540E\u8865\u5361\u7247\u627F\u8F7D\u8FD9\u4E9B\u80FD\u529B\u3002",
|
|
117463
117480
|
"- \u4E0D\u8981\u4F9D\u8D56 bridge \u4ECE\u8F93\u51FA\u91CC\u89E3\u6790\u4E1A\u52A1\u9636\u6BB5\u3001MR\u3001\u9884\u89C8\u5730\u5740\u6216\u4E0B\u4E00\u6B65\u52A8\u4F5C;\u9700\u8981\u5C55\u793A\u7684\u5185\u5BB9\u4F60\u81EA\u5DF1\u5199\u8FDB last_message\u3002",
|
|
117464
117481
|
"- \u4E0D\u8981\u6C42\u56FA\u5B9A\u683C\u5F0F\u3002\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u6E05\u695A\u7684\u8868\u8FBE:\u77ED\u7ED3\u8BBA\u3001\u5206\u70B9\u3001\u8868\u683C\u3001\u94FE\u63A5\u3001\u4E0B\u4E00\u6B65\u95EE\u9898\u90FD\u53EF\u4EE5\u3002",
|
|
117465
117482
|
"- \u5982\u679C\u672C\u8F6E\u6D89\u53CA repo / \u4EE3\u7801 / \u6587\u6863\u4FEE\u6539,last_message \u5E94\u5305\u542B\u8DB3\u591F\u8BA9\u8FD0\u8425\u9A8C\u6536\u7684\u8BC1\u636E,\u4F8B\u5982\u4F60\u5B9E\u9645\u4F7F\u7528\u7684 workspace/repo\u3001\u5173\u952E diff \u6216\u94FE\u63A5\u3001\u8FD0\u884C\u8FC7\u7684\u6D4B\u8BD5/\u68C0\u67E5\u547D\u4EE4\u548C\u7ED3\u679C\u3002\u5177\u4F53\u8BC1\u636E\u7531\u4EFB\u52A1\u51B3\u5B9A;dogfood E2E \u7684\u4E25\u683C\u6E05\u5355\u53EA\u5728 dogfood guide \u4E2D\u8981\u6C42\u3002",
|
|
@@ -118259,7 +118276,7 @@ function defaultResponseSurfacePrototypeConfig() {
|
|
|
118259
118276
|
enabled: true,
|
|
118260
118277
|
allowed_chats: [],
|
|
118261
118278
|
allowed_threads: [],
|
|
118262
|
-
lazy_card_creation:
|
|
118279
|
+
lazy_card_creation: true,
|
|
118263
118280
|
kill_switch: false,
|
|
118264
118281
|
post_outbound_enabled: true,
|
|
118265
118282
|
allow_agent_mentions: true,
|
|
@@ -118276,7 +118293,7 @@ var responseSurfacePrototypeConfigDefaults = () => ({
|
|
|
118276
118293
|
enabled: true,
|
|
118277
118294
|
allowed_chats: [],
|
|
118278
118295
|
allowed_threads: [],
|
|
118279
|
-
lazy_card_creation:
|
|
118296
|
+
lazy_card_creation: true,
|
|
118280
118297
|
kill_switch: false,
|
|
118281
118298
|
post_outbound_enabled: true,
|
|
118282
118299
|
allow_agent_mentions: true,
|
|
@@ -118343,11 +118360,11 @@ var ResponseSurfacePrototypeConfigSchema = external_exports.object({
|
|
|
118343
118360
|
*/
|
|
118344
118361
|
allowed_threads: external_exports.array(external_exports.string().min(1)).default([]),
|
|
118345
118362
|
/**
|
|
118346
|
-
*
|
|
118347
|
-
* the
|
|
118348
|
-
*
|
|
118363
|
+
* Post-first gate. Default true means the bridge starts a lightweight post
|
|
118364
|
+
* as the live main surface when post outbound is available, and creates a
|
|
118365
|
+
* card only for fallback or card-only capabilities.
|
|
118349
118366
|
*/
|
|
118350
|
-
lazy_card_creation: external_exports.boolean().default(
|
|
118367
|
+
lazy_card_creation: external_exports.boolean().default(true),
|
|
118351
118368
|
/**
|
|
118352
118369
|
* Runtime kill switch for emergency rollback. When true, every response
|
|
118353
118370
|
* surface post path is treated as disabled even if enabled/allowlists are
|
|
@@ -119219,6 +119236,9 @@ function fullCard(input, reason) {
|
|
|
119219
119236
|
function hasCardOnlyPayload(state) {
|
|
119220
119237
|
return !!(state?.choices?.length || state?.image_blocks?.length || state?.content_blocks?.length);
|
|
119221
119238
|
}
|
|
119239
|
+
function hasCardOnlyPayloadIn(input) {
|
|
119240
|
+
return !!(hasCardOnlyPayload(input.state) || input.baseCard.choices?.length || input.baseCard.imageBlocks?.length || input.baseCard.contentBlocks?.length);
|
|
119241
|
+
}
|
|
119222
119242
|
function compactAuditCard(input, post) {
|
|
119223
119243
|
const status = input.state?.status ?? (input.baseCard.success ? "ready" : "failed");
|
|
119224
119244
|
const title = input.baseCard.titleOverride ?? "Post \u5DF2\u53D1\u9001";
|
|
@@ -119288,10 +119308,18 @@ async function existingLedgerEntry(input, idempotencyKey) {
|
|
|
119288
119308
|
return ledger?.posts.find((post) => post.idempotencyKey === idempotencyKey) ?? null;
|
|
119289
119309
|
}
|
|
119290
119310
|
function sentResult(input, surface, post, reason) {
|
|
119311
|
+
if (hasCardOnlyPayloadIn(input)) {
|
|
119312
|
+
return {
|
|
119313
|
+
card: input.baseCard,
|
|
119314
|
+
reason: "post-sent-card-capability-required",
|
|
119315
|
+
visible: true,
|
|
119316
|
+
post
|
|
119317
|
+
};
|
|
119318
|
+
}
|
|
119291
119319
|
if (surface.mode === "hybrid" || input.cardStarted) {
|
|
119292
119320
|
return {
|
|
119293
119321
|
card: compactAuditCard(input, post),
|
|
119294
|
-
reason: reason === "post-ledger-already-sent" ? "post-ledger-already-sent" : surface.mode === "hybrid" ? "hybrid-post-sent-compact-card" :
|
|
119322
|
+
reason: reason === "post-ledger-already-sent" ? "post-ledger-already-sent" : surface.mode === "hybrid" ? "hybrid-post-sent-compact-card" : reason,
|
|
119295
119323
|
visible: true,
|
|
119296
119324
|
post
|
|
119297
119325
|
};
|
|
@@ -119334,10 +119362,11 @@ function emitSurfaceObservation(input) {
|
|
|
119334
119362
|
);
|
|
119335
119363
|
}
|
|
119336
119364
|
async function dispatchResponseSurfaceInner(input) {
|
|
119337
|
-
const
|
|
119338
|
-
if (
|
|
119365
|
+
const declaredSurface = input.state?.response_surface;
|
|
119366
|
+
if (declaredSurface?.mode === "card" || declaredSurface?.primary === "card") {
|
|
119339
119367
|
return fullCard(input, "legacy-card-mode");
|
|
119340
119368
|
}
|
|
119369
|
+
const surface = declaredSurface ?? { mode: "post", primary: "post" };
|
|
119341
119370
|
const cfg = input.prototypeConfig;
|
|
119342
119371
|
if (!cfg?.enabled) return fullCard(input, "prototype-disabled");
|
|
119343
119372
|
if (cfg.kill_switch) return fullCard(input, "kill-switch-active");
|
|
@@ -119356,9 +119385,6 @@ async function dispatchResponseSurfaceInner(input) {
|
|
|
119356
119385
|
if (!input.visibleFallbackAvailable) {
|
|
119357
119386
|
return fullCard(input, "visible-fallback-unavailable");
|
|
119358
119387
|
}
|
|
119359
|
-
if (hasCardOnlyPayload(input.state)) {
|
|
119360
|
-
return fullCard(input, "card-capability-required");
|
|
119361
|
-
}
|
|
119362
119388
|
if (!input.postLedgerAvailable || !input.worktreePath) {
|
|
119363
119389
|
return fullCard(input, "post-ledger-unavailable");
|
|
119364
119390
|
}
|
|
@@ -119381,6 +119407,16 @@ async function dispatchResponseSurfaceInner(input) {
|
|
|
119381
119407
|
const now = input.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
119382
119408
|
if (blockedMention) {
|
|
119383
119409
|
const policyError = `mention target is not allowed by response surface policy: ${blockedMention.user_id}`;
|
|
119410
|
+
if (input.livePost) {
|
|
119411
|
+
try {
|
|
119412
|
+
await input.postClient.updatePost(input.livePost.messageId, buildPostContent({ text }));
|
|
119413
|
+
} catch (err) {
|
|
119414
|
+
console.warn(
|
|
119415
|
+
"[surface_dispatch] live post policy-blocked cleanup update failed:",
|
|
119416
|
+
err
|
|
119417
|
+
);
|
|
119418
|
+
}
|
|
119419
|
+
}
|
|
119384
119420
|
await writeLedger(
|
|
119385
119421
|
input,
|
|
119386
119422
|
newLedgerEntry({
|
|
@@ -119415,7 +119451,7 @@ async function dispatchResponseSurfaceInner(input) {
|
|
|
119415
119451
|
}))
|
|
119416
119452
|
});
|
|
119417
119453
|
const contentDigest = digestPostContent(content);
|
|
119418
|
-
const idempotencyKey = derivePostIdempotencyKey({
|
|
119454
|
+
const idempotencyKey = input.livePost?.idempotencyKey ?? derivePostIdempotencyKey({
|
|
119419
119455
|
botId: input.facts.botId,
|
|
119420
119456
|
threadId: input.facts.threadId,
|
|
119421
119457
|
triggerMessageId: input.facts.triggerMessageId,
|
|
@@ -119468,7 +119504,7 @@ async function dispatchResponseSurfaceInner(input) {
|
|
|
119468
119504
|
}
|
|
119469
119505
|
};
|
|
119470
119506
|
}
|
|
119471
|
-
const budget = input.postBudget?.reserve();
|
|
119507
|
+
const budget = input.livePost ? void 0 : input.postBudget?.reserve();
|
|
119472
119508
|
if (budget && !budget.allowed) {
|
|
119473
119509
|
return {
|
|
119474
119510
|
...fullCard(input, "post-rate-limit-exhausted"),
|
|
@@ -119502,7 +119538,7 @@ async function dispatchResponseSurfaceInner(input) {
|
|
|
119502
119538
|
})
|
|
119503
119539
|
);
|
|
119504
119540
|
try {
|
|
119505
|
-
const sent = await input.postClient.createPostReply(input.facts.replyToMessageId, content, {
|
|
119541
|
+
const sent = input.livePost ? await input.postClient.updatePost(input.livePost.messageId, content) : await input.postClient.createPostReply(input.facts.replyToMessageId, content, {
|
|
119506
119542
|
replyInThread: input.facts.replyInThread,
|
|
119507
119543
|
idempotencyKey
|
|
119508
119544
|
});
|
|
@@ -119528,7 +119564,10 @@ async function dispatchResponseSurfaceInner(input) {
|
|
|
119528
119564
|
})
|
|
119529
119565
|
);
|
|
119530
119566
|
const post = { idempotencyKey, messageId: sent.messageId, role };
|
|
119531
|
-
return {
|
|
119567
|
+
return {
|
|
119568
|
+
...sentResult(input, surface, post, input.livePost ? "post-updated" : "post-sent"),
|
|
119569
|
+
budget
|
|
119570
|
+
};
|
|
119532
119571
|
} catch (err) {
|
|
119533
119572
|
const failedAt = input.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
119534
119573
|
const error = err instanceof Error ? err.message : String(err);
|
|
@@ -119581,6 +119620,122 @@ async function dispatchResponseSurface(input) {
|
|
|
119581
119620
|
return result;
|
|
119582
119621
|
}
|
|
119583
119622
|
|
|
119623
|
+
// src/bridge/postProgress.ts
|
|
119624
|
+
var DEFAULT_PLACEHOLDER_TEXT = "\u6B63\u5728\u5904\u7406\u2026";
|
|
119625
|
+
var DEFAULT_PATCH_INTERVAL_MS = 1500;
|
|
119626
|
+
var DEFAULT_MAX_PROGRESS_EDITS = 16;
|
|
119627
|
+
function normalizeText2(text) {
|
|
119628
|
+
return text.replace(/\r\n/g, "\n").trim();
|
|
119629
|
+
}
|
|
119630
|
+
function progressText(raw, fallback) {
|
|
119631
|
+
const text = normalizeText2(raw);
|
|
119632
|
+
if (!text) return fallback;
|
|
119633
|
+
return text.length > 3500 ? `${text.slice(0, 3500)}
|
|
119634
|
+
|
|
119635
|
+
_\u5904\u7406\u4E2D\u5185\u5BB9\u8F83\u957F\uFF0C\u7EC8\u7A3F\u4F1A\u6536\u655B\u3002_` : text;
|
|
119636
|
+
}
|
|
119637
|
+
var LivePostProgressHandle = class {
|
|
119638
|
+
messageId;
|
|
119639
|
+
idempotencyKey;
|
|
119640
|
+
role = "primary";
|
|
119641
|
+
postClient;
|
|
119642
|
+
patchIntervalMs;
|
|
119643
|
+
maxProgressEdits;
|
|
119644
|
+
initialText;
|
|
119645
|
+
textBuffer = "";
|
|
119646
|
+
lastPatchedText = "";
|
|
119647
|
+
progressEdits = 0;
|
|
119648
|
+
pendingPatch = null;
|
|
119649
|
+
inFlight = Promise.resolve();
|
|
119650
|
+
closed = false;
|
|
119651
|
+
constructor(opts) {
|
|
119652
|
+
this.postClient = opts.postClient;
|
|
119653
|
+
this.messageId = opts.messageId;
|
|
119654
|
+
this.idempotencyKey = opts.idempotencyKey;
|
|
119655
|
+
this.initialText = opts.initialText;
|
|
119656
|
+
this.lastPatchedText = opts.initialText;
|
|
119657
|
+
this.patchIntervalMs = opts.patchIntervalMs;
|
|
119658
|
+
this.maxProgressEdits = opts.maxProgressEdits;
|
|
119659
|
+
}
|
|
119660
|
+
handle(event) {
|
|
119661
|
+
if (this.closed) return;
|
|
119662
|
+
if (event.type !== "text_delta") return;
|
|
119663
|
+
this.textBuffer += event.text;
|
|
119664
|
+
this.schedulePatch();
|
|
119665
|
+
}
|
|
119666
|
+
async finalize(opts) {
|
|
119667
|
+
await this.drain();
|
|
119668
|
+
const finalText = normalizeText2(opts.text) || this.initialText;
|
|
119669
|
+
await this.update(finalText, opts.mentions);
|
|
119670
|
+
}
|
|
119671
|
+
async drain() {
|
|
119672
|
+
this.closed = true;
|
|
119673
|
+
if (this.pendingPatch) {
|
|
119674
|
+
clearTimeout(this.pendingPatch);
|
|
119675
|
+
this.pendingPatch = null;
|
|
119676
|
+
}
|
|
119677
|
+
await this.inFlight;
|
|
119678
|
+
}
|
|
119679
|
+
close() {
|
|
119680
|
+
this.closed = true;
|
|
119681
|
+
if (this.pendingPatch) {
|
|
119682
|
+
clearTimeout(this.pendingPatch);
|
|
119683
|
+
this.pendingPatch = null;
|
|
119684
|
+
}
|
|
119685
|
+
}
|
|
119686
|
+
schedulePatch() {
|
|
119687
|
+
if (this.pendingPatch || this.progressEdits >= this.maxProgressEdits) return;
|
|
119688
|
+
this.pendingPatch = setTimeout(() => {
|
|
119689
|
+
this.pendingPatch = null;
|
|
119690
|
+
void this.patchProgress();
|
|
119691
|
+
}, this.patchIntervalMs);
|
|
119692
|
+
this.pendingPatch.unref?.();
|
|
119693
|
+
}
|
|
119694
|
+
async patchProgress() {
|
|
119695
|
+
if (this.closed || this.progressEdits >= this.maxProgressEdits) return;
|
|
119696
|
+
const nextText = progressText(this.textBuffer, this.initialText);
|
|
119697
|
+
if (nextText === this.lastPatchedText) return;
|
|
119698
|
+
this.progressEdits += 1;
|
|
119699
|
+
this.inFlight = this.inFlight.then(() => this.update(nextText)).catch((err) => {
|
|
119700
|
+
console.warn("[post_progress] progress update failed (continuing):", err);
|
|
119701
|
+
});
|
|
119702
|
+
await this.inFlight;
|
|
119703
|
+
}
|
|
119704
|
+
async update(text, mentions = []) {
|
|
119705
|
+
const content = buildPostContent({ text, mentions });
|
|
119706
|
+
await this.postClient.updatePost(this.messageId, content);
|
|
119707
|
+
this.lastPatchedText = text;
|
|
119708
|
+
}
|
|
119709
|
+
};
|
|
119710
|
+
async function createPostProgressHandle(opts) {
|
|
119711
|
+
const role = "primary";
|
|
119712
|
+
const initialText = normalizeText2(opts.initialText ?? DEFAULT_PLACEHOLDER_TEXT);
|
|
119713
|
+
const idempotencyKey = derivePostIdempotencyKey({
|
|
119714
|
+
botId: opts.facts.botId,
|
|
119715
|
+
threadId: opts.facts.threadId,
|
|
119716
|
+
triggerMessageId: opts.facts.triggerMessageId,
|
|
119717
|
+
role,
|
|
119718
|
+
logicalIndex: 0,
|
|
119719
|
+
contentDigest: digestPostContent("live-progress-placeholder")
|
|
119720
|
+
});
|
|
119721
|
+
const sent = await opts.postClient.createPostReply(
|
|
119722
|
+
opts.replyToMessageId,
|
|
119723
|
+
buildPostContent({ text: initialText }),
|
|
119724
|
+
{
|
|
119725
|
+
replyInThread: opts.replyInThread,
|
|
119726
|
+
idempotencyKey
|
|
119727
|
+
}
|
|
119728
|
+
);
|
|
119729
|
+
return new LivePostProgressHandle({
|
|
119730
|
+
postClient: opts.postClient,
|
|
119731
|
+
messageId: sent.messageId,
|
|
119732
|
+
idempotencyKey,
|
|
119733
|
+
initialText,
|
|
119734
|
+
patchIntervalMs: opts.patchIntervalMs ?? DEFAULT_PATCH_INTERVAL_MS,
|
|
119735
|
+
maxProgressEdits: opts.maxProgressEdits ?? DEFAULT_MAX_PROGRESS_EDITS
|
|
119736
|
+
});
|
|
119737
|
+
}
|
|
119738
|
+
|
|
119584
119739
|
// src/bridge/postBudget.ts
|
|
119585
119740
|
function scopeKey(scope) {
|
|
119586
119741
|
return `${scope.botId}\0${scope.chatId}\0${scope.threadId}`;
|
|
@@ -119962,6 +120117,8 @@ var BridgeHandler = class {
|
|
|
119962
120117
|
visibleFallbackAvailable: true
|
|
119963
120118
|
});
|
|
119964
120119
|
let card;
|
|
120120
|
+
let progressPost;
|
|
120121
|
+
let progressPostStartFailed = false;
|
|
119965
120122
|
if (surfaceController.shouldStartCardImmediately()) {
|
|
119966
120123
|
try {
|
|
119967
120124
|
card = await this.deps.cardRenderer.start(messageId, { replyInThread, threadId });
|
|
@@ -120125,6 +120282,55 @@ var BridgeHandler = class {
|
|
|
120125
120282
|
} catch (err) {
|
|
120126
120283
|
console.warn("[bridge.handler] ensureStateFile failed (continuing):", err);
|
|
120127
120284
|
}
|
|
120285
|
+
if (!card && postOutboundAvailable && this.deps.postClient) {
|
|
120286
|
+
const budget = prototypeConfig ? this.responseSurfacePostBudget.reserve({
|
|
120287
|
+
scope: {
|
|
120288
|
+
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
120289
|
+
chatId: parsed.chatId,
|
|
120290
|
+
threadId
|
|
120291
|
+
},
|
|
120292
|
+
maxPosts: prototypeConfig.max_posts_per_window,
|
|
120293
|
+
windowMs: prototypeConfig.post_window_ms
|
|
120294
|
+
}) : void 0;
|
|
120295
|
+
if (budget?.allowed === false) {
|
|
120296
|
+
console.warn("[bridge.handler] post progress budget exhausted; using card fallback");
|
|
120297
|
+
progressPostStartFailed = true;
|
|
120298
|
+
} else {
|
|
120299
|
+
try {
|
|
120300
|
+
progressPost = await createPostProgressHandle({
|
|
120301
|
+
postClient: this.deps.postClient,
|
|
120302
|
+
replyToMessageId: messageId,
|
|
120303
|
+
replyInThread,
|
|
120304
|
+
facts: {
|
|
120305
|
+
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
120306
|
+
threadId,
|
|
120307
|
+
triggerMessageId: messageId
|
|
120308
|
+
},
|
|
120309
|
+
initialText: "\u6B63\u5728\u5904\u7406\u2026"
|
|
120310
|
+
});
|
|
120311
|
+
await this.deps.client.removeProcessingReaction?.(messageId);
|
|
120312
|
+
await recordEvent({
|
|
120313
|
+
status: "running",
|
|
120314
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
120315
|
+
appendPath: "\u5DF2\u521B\u5EFA post",
|
|
120316
|
+
reason: "response surface \u4F7F\u7528 post \u4F5C\u4E3A\u672C\u8F6E\u4E3B\u56DE\u590D\u9762\u3002"
|
|
120317
|
+
});
|
|
120318
|
+
} catch (err) {
|
|
120319
|
+
progressPostStartFailed = true;
|
|
120320
|
+
console.warn("[bridge.handler] create progress post failed; using card fallback:", err);
|
|
120321
|
+
}
|
|
120322
|
+
}
|
|
120323
|
+
}
|
|
120324
|
+
if (!card && progressPostStartFailed) {
|
|
120325
|
+
card = await this.deps.cardRenderer.start(messageId, { replyInThread, threadId });
|
|
120326
|
+
await this.deps.client.removeProcessingReaction?.(messageId);
|
|
120327
|
+
await recordEvent({
|
|
120328
|
+
status: "running",
|
|
120329
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
120330
|
+
appendPath: "post \u5931\u8D25\uFF0C\u5DF2\u521B\u5EFA\u5361\u7247",
|
|
120331
|
+
reason: "post \u4E3B\u9762\u521B\u5EFA\u5931\u8D25\uFF0Cbridge \u4F7F\u7528\u53EF\u89C1\u5361\u7247\u515C\u5E95\u3002"
|
|
120332
|
+
});
|
|
120333
|
+
}
|
|
120128
120334
|
if (card) {
|
|
120129
120335
|
try {
|
|
120130
120336
|
await writeCardFile(worktreePath, {
|
|
@@ -120199,6 +120405,7 @@ var BridgeHandler = class {
|
|
|
120199
120405
|
try {
|
|
120200
120406
|
for await (const ev of handle.events) {
|
|
120201
120407
|
if (card) card.handle(ev);
|
|
120408
|
+
else progressPost?.handle(ev);
|
|
120202
120409
|
if (ev.type === "system_init") {
|
|
120203
120410
|
sessionId = ev.sessionId;
|
|
120204
120411
|
}
|
|
@@ -120266,6 +120473,7 @@ var BridgeHandler = class {
|
|
|
120266
120473
|
imageBlocks: reportedState?.image_blocks,
|
|
120267
120474
|
contentBlocks: reportedState?.content_blocks
|
|
120268
120475
|
};
|
|
120476
|
+
await progressPost?.drain();
|
|
120269
120477
|
const surfaceDispatch = await dispatchResponseSurface({
|
|
120270
120478
|
state: reportedState,
|
|
120271
120479
|
prototypeConfig,
|
|
@@ -120280,10 +120488,15 @@ var BridgeHandler = class {
|
|
|
120280
120488
|
worktreePath,
|
|
120281
120489
|
baseCard: baseCardPayload,
|
|
120282
120490
|
cardStarted: !!card,
|
|
120283
|
-
postOutboundAvailable,
|
|
120491
|
+
postOutboundAvailable: postOutboundAvailable && !progressPostStartFailed,
|
|
120284
120492
|
postLedgerAvailable: true,
|
|
120285
120493
|
visibleFallbackAvailable: true,
|
|
120286
120494
|
postClient: this.deps.postClient,
|
|
120495
|
+
livePost: progressPost ? {
|
|
120496
|
+
messageId: progressPost.messageId,
|
|
120497
|
+
idempotencyKey: progressPost.idempotencyKey,
|
|
120498
|
+
role: progressPost.role
|
|
120499
|
+
} : void 0,
|
|
120287
120500
|
postBudget: prototypeConfig ? {
|
|
120288
120501
|
reserve: () => this.responseSurfacePostBudget.reserve({
|
|
120289
120502
|
scope: {
|
|
@@ -120387,6 +120600,23 @@ var BridgeHandler = class {
|
|
|
120387
120600
|
reason: String(err)
|
|
120388
120601
|
});
|
|
120389
120602
|
settle(false);
|
|
120603
|
+
if (!card && progressPost) {
|
|
120604
|
+
try {
|
|
120605
|
+
await progressPost.finalize({
|
|
120606
|
+
text: `\u6267\u884C\u5931\u8D25: ${String(err)}`
|
|
120607
|
+
});
|
|
120608
|
+
} catch (postFinalizeErr) {
|
|
120609
|
+
console.error(
|
|
120610
|
+
"[bridge.handler] progress post failure update failed; creating card fallback:",
|
|
120611
|
+
postFinalizeErr
|
|
120612
|
+
);
|
|
120613
|
+
try {
|
|
120614
|
+
card = await this.deps.cardRenderer.start(messageId, { replyInThread, threadId });
|
|
120615
|
+
} catch (cardStartErr) {
|
|
120616
|
+
console.error("[bridge.handler] failure card start also failed:", cardStartErr);
|
|
120617
|
+
}
|
|
120618
|
+
}
|
|
120619
|
+
}
|
|
120390
120620
|
if (card) {
|
|
120391
120621
|
try {
|
|
120392
120622
|
await card.finalize({
|