larkin 0.5.18 → 0.5.19

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.19
4
+
5
+ The Feishu processing-eye OnIt reaction is no longer a config switch. After a successful deliver, Larkin reacts OnIt only for private chats (DM / p2p), when the inbound event already marked the bot as mentioned, or when the user is talking inside a topic the bot started (a Feishu topic whose root is a bot-owned message). Ordinary group messages without @ and outside a bot-started topic never get the reaction, including free-mention chats that still wake the Agent.
6
+
7
+ The 0.5.18 `processingEye.enabled` key, `larkin config processing-eye`, and Dashboard 处理中表情 toggle are removed. Leftover keys in existing configs are ignored on load and dropped on the next config write. There is no on/off flag and no migration prompt.
8
+
3
9
  ## 0.5.18
4
10
 
5
11
  The Feishu processing-eye OnIt reaction is now off by default. New installs and existing configs that omit `processingEye` no longer auto-react on inbound messages after upgrade; there is no migration prompt. Opt back in with `larkin config processing-eye global on`, a per-Agent `on`/`off`/`inherit` override, or the Dashboard 处理中表情 toggle. Explicit `enabled: false` and a missing key both skip the reactions API.
package/README.md CHANGED
@@ -114,16 +114,9 @@ larkin config inbox-audit global off
114
114
 
115
115
  When enabled, audit only revisits originally wake-eligible human group/topic work and does not wake a model for an empty work list. Reading an audit list does not complete it: the managed Agent follows the returned inspection instructions and explicitly confirms the receipt after checking. A failed check remains retryable; newer messages cannot be completed by an older receipt. Old audit-index records without proven eligibility or observation identity are ignored; ordinary Inbox messages and conversation history are preserved.
116
116
 
117
- ### Optional processing eye (OnIt)
117
+ ### Processing eye (OnIt)
118
118
 
119
- The Feishu "processing eye" (OnIt reaction on inbound messages while the Agent works) is **off by default**. A missing `processingEye` key is treated as off, so upgrading to this version stops the auto-reaction without a migration prompt. In the Dashboard, use **Global settings → 处理中表情** or the selected Agent's **Configuration → 处理中表情**. The same settings are available through the CLI:
120
-
121
- ```bash
122
- larkin config processing-eye global on
123
- larkin config processing-eye agent off --agent <App-ID>
124
- larkin config processing-eye agent inherit --agent <App-ID>
125
- larkin config processing-eye global off
126
- ```
119
+ The Feishu "processing eye" is an OnIt reaction on the inbound message while the Agent works. It is automatic and not configurable: Larkin adds it after a successful deliver only for private chats (DM / p2p), when the inbound event marked the bot as mentioned, or when the user is talking inside a topic the bot started. Ordinary group messages without @ and outside a bot-started topic never get the reaction. The 0.5.18 `processingEye` config key, CLI, and Dashboard toggle are removed; leftover keys are ignored.
127
120
 
128
121
  <details>
129
122
  <summary>Windows support and optional autostart</summary>
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CONFIG_CLI_VALUES = exports.CONFIG_CLI_USAGE = exports.CONFIG_CLI_OPERATIONS = void 0;
4
- exports.CONFIG_CLI_OPERATIONS = Object.freeze(["show", "runtime", "model", "effort", "mention", "inbox-audit", "processing-eye", "apply"]);
4
+ exports.CONFIG_CLI_OPERATIONS = Object.freeze(["show", "runtime", "model", "effort", "mention", "inbox-audit", "apply"]);
5
5
  exports.CONFIG_CLI_USAGE = Object.freeze([
6
6
  "larkin config show [--agent <App ID>] [--chat <oc_id>] [--json]",
7
7
  "larkin config runtime <runtime> [--model <model>] [--agent <App ID>]",
@@ -12,8 +12,6 @@ exports.CONFIG_CLI_USAGE = Object.freeze([
12
12
  "larkin config mention chat <oc_id> <inherit|require|free> [--agent <App ID>]",
13
13
  "larkin config inbox-audit global <on|off> [--interval <15m|1h>]",
14
14
  "larkin config inbox-audit agent <inherit|on|off> [--agent <App ID>] [--interval <15m|inherit>]",
15
- "larkin config processing-eye global <on|off>",
16
- "larkin config processing-eye agent <inherit|on|off> [--agent <App ID>]",
17
15
  "larkin config apply [--agent <App ID>]",
18
16
  ]);
19
17
  exports.CONFIG_CLI_VALUES = Object.freeze({
@@ -1,4 +1,4 @@
1
- export const CONFIG_CLI_OPERATIONS = Object.freeze(["show", "runtime", "model", "effort", "mention", "inbox-audit", "processing-eye", "apply"]);
1
+ export const CONFIG_CLI_OPERATIONS = Object.freeze(["show", "runtime", "model", "effort", "mention", "inbox-audit", "apply"]);
2
2
  export const CONFIG_CLI_USAGE = Object.freeze([
3
3
  "larkin config show [--agent <App ID>] [--chat <oc_id>] [--json]",
4
4
  "larkin config runtime <runtime> [--model <model>] [--agent <App ID>]",
@@ -9,8 +9,6 @@ export const CONFIG_CLI_USAGE = Object.freeze([
9
9
  "larkin config mention chat <oc_id> <inherit|require|free> [--agent <App ID>]",
10
10
  "larkin config inbox-audit global <on|off> [--interval <15m|1h>]",
11
11
  "larkin config inbox-audit agent <inherit|on|off> [--agent <App ID>] [--interval <15m|inherit>]",
12
- "larkin config processing-eye global <on|off>",
13
- "larkin config processing-eye agent <inherit|on|off> [--agent <App ID>]",
14
12
  "larkin config apply [--agent <App ID>]",
15
13
  ]);
16
14
  export const CONFIG_CLI_VALUES = Object.freeze({
@@ -49,6 +49,7 @@ const reminder_routes_js_1 = require("./reminder-routes.cjs");
49
49
  const reminder_delivery_audit_js_1 = require("./reminder-delivery-audit.cjs");
50
50
  const transport_shell_js_1 = require("./transport-shell.cjs");
51
51
  const agent_lark_cli_workspace_js_1 = require("../app/agent-lark-cli-workspace.cjs");
52
+ const host_processing_eye_js_1 = require("../feishu/host-processing-eye.cjs");
52
53
  function formatFeishuError(error, grantLink = null) {
53
54
  if (!error)
54
55
  return null;
@@ -139,6 +140,8 @@ function createTransportBusinessContext(env = process.env) {
139
140
  const appendConversation = (item) => {
140
141
  try {
141
142
  fs.appendFileSync(paths.conversation, `${JSON.stringify({ ...item, text: (0, transport_shell_js_1.safeConversationExcerpt)(item.text) })}\n`);
143
+ if (item.direction === "out")
144
+ (0, host_processing_eye_js_1.persistBotTopicRoot)(stateStore, typeof item.messageId === "string" ? item.messageId : null);
142
145
  }
143
146
  catch (error) {
144
147
  log(`conversation 写失败: ${error.message}`);
@@ -12,6 +12,7 @@ import { createReminderRoutes } from "./reminder-routes.mjs";
12
12
  import { auditReminderDelivery } from "./reminder-delivery-audit.mjs";
13
13
  import { isImageMime, looksLikeMarkdown, resolveMappedChatId, safeConversationExcerpt } from "./transport-shell.mjs";
14
14
  import { managedOfficialLarkCli } from "../app/agent-lark-cli-workspace.mjs";
15
+ import { persistBotTopicRoot } from "../feishu/host-processing-eye.mjs";
15
16
  export function formatFeishuError(error, grantLink = null) {
16
17
  if (!error)
17
18
  return null;
@@ -102,6 +103,8 @@ export function createTransportBusinessContext(env = process.env) {
102
103
  const appendConversation = (item) => {
103
104
  try {
104
105
  fs.appendFileSync(paths.conversation, `${JSON.stringify({ ...item, text: safeConversationExcerpt(item.text) })}\n`);
106
+ if (item.direction === "out")
107
+ persistBotTopicRoot(stateStore, typeof item.messageId === "string" ? item.messageId : null);
105
108
  }
106
109
  catch (error) {
107
110
  log(`conversation 写失败: ${error.message}`);
@@ -256,23 +256,6 @@ function agentConfigRequest(argv, env, config, agent, dependencies) {
256
256
  agentId: targetId,
257
257
  });
258
258
  }
259
- else if (operation === "processing-eye") {
260
- const [scope, first] = options.positionals;
261
- if (scope === "global")
262
- assertOnlyFlags([]);
263
- else if (scope === "agent")
264
- assertOnlyFlags(["--agent"]);
265
- else
266
- assertOnlyFlags([]);
267
- if (options.positionals.length !== 2) {
268
- throw new Error("用法: larkin config processing-eye global <on|off> | processing-eye agent <inherit|on|off> [--agent <App ID>]");
269
- }
270
- mutation = larkinConfig.processingEyeMutationFromCli({
271
- scope: scope || "",
272
- enabled: first,
273
- agentId: targetId,
274
- });
275
- }
276
259
  else if (operation === "apply") {
277
260
  assertOnlyFlags(["--agent"]);
278
261
  if (options.positionals.length)
@@ -286,7 +269,7 @@ function agentConfigRequest(argv, env, config, agent, dependencies) {
286
269
  }).catch((error) => { throw new Error(`配置已保存但未应用:${error instanceof Error ? error.message : String(error)}`); });
287
270
  }
288
271
  else
289
- throw new Error("config 只支持 show/runtime/model/effort/mention/inbox-audit/processing-eye/apply;运行 larkin config --help");
272
+ throw new Error("config 只支持 show/runtime/model/effort/mention/inbox-audit/apply;运行 larkin config --help");
290
273
  const result = larkinConfig.mutateConfig(env, mutation, authority);
291
274
  return { ok: true, revision: result.revision, persisted: true, applyState: result.applyState, changedScope: result.changedScope };
292
275
  }
@@ -52,7 +52,7 @@ const die = (message) => {
52
52
  process.exit(1);
53
53
  };
54
54
  if (!kind || !["agents", "model", "runtime", "effort", "chats", "config"].includes(kind)) {
55
- die("用法: larkin agents | larkin config <show|mention|inbox-audit|processing-eye|apply> | larkin model [<id>] | larkin runtime [<id>] [--model <id>] | larkin effort [<level>|clear] | larkin chats [free|strict <oc_id>]");
55
+ die("用法: larkin agents | larkin config <show|mention|inbox-audit|apply> | larkin model [<id>] | larkin runtime [<id>] [--model <id>] | larkin effort [<level>|clear] | larkin chats [free|strict <oc_id>]");
56
56
  }
57
57
  const allowedValueFlags = {
58
58
  agents: new Set(), model: new Set(["--agent"]), runtime: new Set(["--agent", "--model"]),
@@ -162,23 +162,13 @@ else if (kind === "config") {
162
162
  if (positionals.length !== 3)
163
163
  die("用法: larkin config inbox-audit agent <inherit|on|off> [--agent <App ID>] [--interval <15m|inherit>]");
164
164
  }
165
- else if (operation === "processing-eye" && scope === "global") {
166
- assertOnlyFlags([]);
167
- if (positionals.length !== 3)
168
- die("用法: larkin config processing-eye global <on|off>");
169
- }
170
- else if (operation === "processing-eye" && scope === "agent") {
171
- assertOnlyFlags(["--agent"]);
172
- if (positionals.length !== 3)
173
- die("用法: larkin config processing-eye agent <inherit|on|off> [--agent <App ID>]");
174
- }
175
165
  else if (operation === "apply") {
176
166
  assertOnlyFlags(["--agent"]);
177
167
  if (positionals.length !== 1)
178
168
  die("用法: larkin config apply [--agent <App ID>]");
179
169
  }
180
170
  else {
181
- die("config 只支持 show/mention/inbox-audit/processing-eye/apply;运行 larkin config --help");
171
+ die("config 只支持 show/mention/inbox-audit/apply;运行 larkin config --help");
182
172
  }
183
173
  }
184
174
  const loaded = (() => {
@@ -362,20 +352,15 @@ if (kind === "config") {
362
352
  say(`全局群消息策略=${String(view.mentionPolicy)}(free 会唤醒所有继承它的 Agent)`);
363
353
  const inboxAudit = view.inboxAudit;
364
354
  say(`全局 Inbox Audit=${inboxAudit?.enabled ? "on" : "off"} interval=${larkinConfig.formatInboxAuditInterval(Number(inboxAudit?.intervalMs || larkinConfig.DEFAULT_INBOX_AUDIT_INTERVAL_MS))}(默认关闭;未 @ 的 require 群消息不会进入审计)`);
365
- const processingEye = view.processingEye;
366
- say(`全局处理中表情=${processingEye?.enabled ? "on" : "off"}(默认关闭;缺 key 视为关闭,升级后不再自动点 OnIt)`);
367
355
  for (const item of view.agents) {
368
356
  const mention = item.mention;
369
357
  const agentAudit = item.inboxAudit;
370
- const agentEye = item.processingEye;
371
358
  const apply = item.apply;
372
- say(`agent=${item.agentId} runtime=${item.runtime} model=${item.model} effort=${item.effort || "default"} mention=${mention.override} inbox-audit=${agentAudit?.override?.enabled || "inherit"} processing-eye=${agentEye?.override?.enabled || "inherit"} apply=${apply.applyState || "unknown"}`);
359
+ say(`agent=${item.agentId} runtime=${item.runtime} model=${item.model} effort=${item.effort || "default"} mention=${mention.override} inbox-audit=${agentAudit?.override?.enabled || "inherit"} apply=${apply.applyState || "unknown"}`);
373
360
  if (mention.chat)
374
361
  say(` chat=${mention.chat.chatId} override=${mention.chat.override} effective=${mention.chat.effective} source=${mention.chat.source}`);
375
362
  if (agentAudit?.effective)
376
363
  say(` inbox-audit effective=${agentAudit.effective.enabled ? "on" : "off"} interval=${larkinConfig.formatInboxAuditInterval(Number(agentAudit.effective.intervalMs || larkinConfig.DEFAULT_INBOX_AUDIT_INTERVAL_MS))}`);
377
- if (agentEye?.effective)
378
- say(` processing-eye effective=${agentEye.effective.enabled ? "on" : "off"}`);
379
364
  }
380
365
  }
381
366
  process.exit(0);
@@ -420,20 +405,6 @@ if (kind === "config") {
420
405
  }
421
406
  process.exit(0);
422
407
  }
423
- if (operation === "processing-eye") {
424
- try {
425
- const result = larkinConfig.mutateConfig(process.env, larkinConfig.processingEyeMutationFromCli({
426
- scope: scope || "",
427
- enabled: first,
428
- agentId: scope === "agent" ? requireTarget() : (selected || keys[0] || ""),
429
- }), authority);
430
- say(JSON.stringify({ ok: true, revision: result.revision, persisted: result.persisted, applyState: result.applyState, changedScope: result.changedScope }, null, 2));
431
- }
432
- catch (error) {
433
- die(error instanceof Error ? error.message : String(error));
434
- }
435
- process.exit(0);
436
- }
437
408
  if (operation === "apply") {
438
409
  const agentId = requireTarget();
439
410
  const expectedSignature = larkinConfig.runtimeConfigSignature(config, agentId);
@@ -453,7 +424,7 @@ if (kind === "config") {
453
424
  }
454
425
  process.exit(0);
455
426
  }
456
- die("config 只支持 show/mention/inbox-audit/processing-eye/apply");
427
+ die("config 只支持 show/mention/inbox-audit/apply");
457
428
  }
458
429
  let key = flagAgent;
459
430
  if (!key && runtimeAgentId)
package/dist/app/cli.mjs CHANGED
@@ -87,7 +87,6 @@ Examples:
87
87
  larkin config runtime pi --agent cli_x --model default
88
88
  larkin config mention chat oc_x free --agent cli_x
89
89
  larkin config inbox-audit global on --interval 15m
90
- larkin config processing-eye global on
91
90
 
92
91
  Credentials, internal paths, serverId, activeAgent, and raw config are never exposed here.`,
93
92
  session: `Usage: larkin session reset --agent <App ID> --json [--wait-ready <seconds>]
@@ -11,6 +11,7 @@ import * as larkinConfig from "../platform/config.mjs";
11
11
  import { resolveOfficialLarkCli } from "./official-lark-cli.mjs";
12
12
  import { assertAgentWorkspaceBound, managedLarkCliEnv } from "./agent-lark-cli-workspace.mjs";
13
13
  import { parseDocumentCommentTarget } from "../feishu/document-comment.mjs";
14
+ import { persistBotTopicRoot } from "../feishu/host-processing-eye.mjs";
14
15
  import { SpanKind } from "@opentelemetry/api";
15
16
  import { loadTelemetryConfig } from "../platform/telemetry-config.mjs";
16
17
  import { telemetrySingleton } from "../platform/telemetry-tracing.mjs";
@@ -1294,6 +1295,9 @@ export function runLarkCli(argv, env = process.env, dependencies = {}) {
1294
1295
  const deliveryAnchor = currentReminder?.deliveryAnchor;
1295
1296
  const memo = !write.error && write.status === 0 && writeMessage
1296
1297
  ? recordImWriteMemo(store, intentKey, writeMessage.message_id, deliveryAnchor) : { duplicate: false };
1298
+ if (!write.error && write.status === 0 && writeMessage?.message_id) {
1299
+ persistBotTopicRoot(store, writeMessage.message_id);
1300
+ }
1297
1301
  const duplicateOfEarlierReminder = memo.duplicate && Boolean(currentReminder)
1298
1302
  && memo.priorSourceMessageId !== currentReminder?.deliveryAnchor;
1299
1303
  const committedWrite = !write.error && write.status === 0 && !duplicateOfEarlierReminder;
@@ -402,17 +402,11 @@ function dashboardMutation(value) {
402
402
  "set-chat-mention": ["operation", "agentId", "chatId", "value"], "set-agent-runtime": ["operation", "agentId", "runtime", "model"],
403
403
  "set-agent-model": ["operation", "agentId", "model"], "set-agent-effort": ["operation", "agentId", "effort"],
404
404
  "set-global-inbox-audit": ["operation", "enabled", "intervalMs"], "set-agent-inbox-audit": ["operation", "agentId", "enabled", "intervalMs"],
405
- "set-global-processing-eye": ["operation", "enabled"], "set-agent-processing-eye": ["operation", "agentId", "enabled"],
406
405
  };
407
406
  if (!allowed[operation] || Object.keys(value).some((key) => !allowed[operation].includes(key)))
408
407
  throw new Error("unsupported operation");
409
408
  if (operation === "set-global-mention")
410
409
  return { kind: operation, value: String(value.value) };
411
- if (operation === "set-global-processing-eye") {
412
- if (typeof value.enabled !== "boolean")
413
- throw new Error("invalid processing-eye enabled");
414
- return { kind: operation, enabled: value.enabled };
415
- }
416
410
  if (operation === "set-global-inbox-audit") {
417
411
  if (value.enabled !== undefined && typeof value.enabled !== "boolean")
418
412
  throw new Error("invalid inbox-audit enabled");
@@ -428,11 +422,6 @@ function dashboardMutation(value) {
428
422
  throw new Error("invalid inbox-audit interval");
429
423
  return { kind: operation, agentId, ...(value.enabled !== undefined ? { enabled: value.enabled } : {}), ...(value.intervalMs !== undefined ? { intervalMs: value.intervalMs } : {}) };
430
424
  }
431
- if (operation === "set-agent-processing-eye") {
432
- if (value.enabled !== "inherit" && typeof value.enabled !== "boolean")
433
- throw new Error("invalid processing-eye enabled");
434
- return { kind: operation, agentId, enabled: value.enabled };
435
- }
436
425
  if (operation === "set-agent-mention")
437
426
  return { kind: operation, agentId, value: String(value.value) };
438
427
  if (operation === "set-chat-mention")