larkin 0.5.17 → 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,15 @@
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
+
9
+ ## 0.5.18
10
+
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.
12
+
3
13
  ## 0.5.8
4
14
 
5
15
  Lark setup now reconciles requested tenant permissions with actual grants and links missing scopes to the same application in the developer console. Required permissions still block setup until granted; optional permissions and event/callback verification are reported separately. Existing-application authorization preserves and checks its App ID before recording any capability state.
package/README.md CHANGED
@@ -114,6 +114,10 @@ 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
+ ### Processing eye (OnIt)
118
+
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.
120
+
117
121
  <details>
118
122
  <summary>Windows support and optional autostart</summary>
119
123
 
@@ -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}`);
@@ -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;
@@ -46,4 +46,4 @@ Error generating stack: `+e.message+`
46
46
  `},ar=function(){var e=parseInt(document.body.getAttribute(`data-scroll-locked`)||`0`,10);return isFinite(e)?e:0},or=function(){C.useEffect(function(){return document.body.setAttribute(rr,(ar()+1).toString()),function(){var e=ar()-1;e<=0?document.body.removeAttribute(rr):document.body.setAttribute(rr,e.toString())}},[])},sr=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?`margin`:r;or();var a=C.useMemo(function(){return tr(i)},[i]);return C.createElement(nr,{styles:ir(a,!t,i,n?``:`!important`)})},cr=!1;if(typeof window<`u`)try{var lr=Object.defineProperty({},"passive",{get:function(){return cr=!0,!0}});window.addEventListener(`test`,lr,lr),window.removeEventListener(`test`,lr,lr)}catch{cr=!1}var ur=cr?{passive:!1}:!1,dr=function(e){return e.tagName===`TEXTAREA`},fr=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!==`hidden`&&!(n.overflowY===n.overflowX&&!dr(e)&&n[t]===`visible`)},pr=function(e){return fr(e,`overflowY`)},mr=function(e){return fr(e,`overflowX`)},hr=function(e,t){var n=t.ownerDocument,r=t;do{if(typeof ShadowRoot<`u`&&r instanceof ShadowRoot&&(r=r.host),vr(e,r)){var i=yr(e,r);if(i[1]>i[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},gr=function(e){return[e.scrollTop,e.scrollHeight,e.clientHeight]},_r=function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]},vr=function(e,t){return e===`v`?pr(t):mr(t)},yr=function(e,t){return e===`v`?gr(t):_r(t)},br=function(e,t){return e===`h`&&t===`rtl`?-1:1},xr=function(e,t,n,r,i){var a=br(e,window.getComputedStyle(t).direction),o=a*r,s=n.target,c=t.contains(s),l=!1,u=o>0,d=0,f=0;do{if(!s)break;var p=yr(e,s),m=p[0],h=p[1]-p[2]-a*m;(m||h)&&vr(e,s)&&(d+=h,f+=m);var g=s.parentNode;s=g&&g.nodeType===Node.DOCUMENT_FRAGMENT_NODE?g.host:g}while(!c&&s!==document.body||c&&(t.contains(s)||t===s));return(u&&(i&&Math.abs(d)<1||!i&&o>d)||!u&&(i&&Math.abs(f)<1||!i&&-o>f))&&(l=!0),l},Sr=function(e){return`changedTouches`in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},Cr=function(e){return[e.deltaX,e.deltaY]},wr=function(e){return e&&`current`in e?e.current:e},Tr=function(e,t){return e[0]===t[0]&&e[1]===t[1]},Er=function(e){return`
47
47
  .block-interactivity-${e} {pointer-events: none;}
48
48
  .allow-interactivity-${e} {pointer-events: all;}
49
- `},Dr=0,Or=[];function kr(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Dr++)[0],a=C.useState(Zn)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=En([e.lockRef.current],(e.shards||[]).map(wr),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Sr(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=hr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=hr(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return xr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!Or.length||Or[Or.length-1]!==a)){var r=`deltaY`in n?Cr(n):Sr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Tr(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(wr).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:Ar(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Sr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Sr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return Or.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,ur),document.addEventListener(`touchmove`,c,ur),document.addEventListener(`touchstart`,u,ur),function(){Or=Or.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,ur),document.removeEventListener(`touchmove`,c,ur),document.removeEventListener(`touchstart`,u,ur)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Er(i)}):null,p?C.createElement(sr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function Ar(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var jr=Bn(Vn,kr),Mr=C.forwardRef(function(e,t){return C.createElement(Un,wn({},e,{ref:t,sideCar:jr}))});Mr.classNames=Un.classNames;var Nr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Pr=new WeakMap,Fr=new WeakMap,Ir={},Lr=0,Rr=function(e){return e&&(e.host||Rr(e.parentNode))},zr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=Rr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},Br=function(e,t,n,r){var i=zr(t,Array.isArray(e)?e:[e]);Ir[n]||(Ir[n]=new WeakMap);var a=Ir[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(Pr.get(e)||0)+1,l=(a.get(e)||0)+1;Pr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Fr.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),Lr++,function(){o.forEach(function(e){var t=Pr.get(e)-1,i=a.get(e)-1;Pr.set(e,t),a.set(e,i),t||(Fr.has(e)||e.removeAttribute(r),Fr.delete(e)),i||e.removeAttribute(n)}),Lr--,Lr||(Pr=new WeakMap,Pr=new WeakMap,Fr=new WeakMap,Ir={})}},Vr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Nr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Br(r,i,n,`aria-hidden`)):function(){return null}},Hr=Object.defineProperty,Ur=(e,t)=>Hr(e,`name`,{value:t,configurable:!0}),Wr=`Dialog`,[Gr,Kr]=ze(Wr),[qr,Jr]=Gr(Wr),Yr=Ur(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=tt({prop:r,defaultProp:i??!1,onChange:a,caller:Wr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,j.jsx)(qr,{scope:t,triggerRef:s,contentRef:c,contentId:Ke(),titleId:Ke(),descriptionId:Ke(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Xr=`DialogPortal`,[Zr,Qr]=Gr(Xr,{forceMount:void 0}),$r=Ur(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Jr(Xr,t);return(0,j.jsx)(Zr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,j.jsx)(dn,{present:n||a.open,children:(0,j.jsx)(sn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ei=`DialogOverlay`,ti=C.forwardRef(Ur(function(e,t){let n=Qr(ei,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ei,e.__scopeDialog);return a.modal?(0,j.jsx)(dn,{present:r||a.open,children:(0,j.jsx)(ri,{...i,ref:t})}):null},`DialogOverlay`)),ni=lt(`DialogOverlay.RemoveScroll`),ri=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(ei,n),a=Pe(t,It());return(0,j.jsx)(Mr,{as:ni,allowPinchZoom:!0,shards:[i.contentRef],children:(0,j.jsx)(wt.div,{"data-state":hi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ii=`DialogContent`,ai=C.forwardRef(Ur(function(e,t){let n=Qr(ii,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ii,e.__scopeDialog);return(0,j.jsx)(dn,{present:r||a.open,children:a.modal?(0,j.jsx)(oi,{...i,ref:t}):(0,j.jsx)(si,{...i,ref:t})})},`DialogContent`)),oi=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(null),i=Pe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Vr(e)},[]),(0,j.jsx)(ci,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Te(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Te(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Te(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),si=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,j.jsx)(ci,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),ci=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Jr(ii,n);return Sn(),(0,j.jsx)(j.Fragment,{children:(0,j.jsx)(qt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,j.jsx)(Ft,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":hi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),li=`DialogTitle`,ui=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(li,n),{setTitleCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),di=`DialogDescription`,fi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(di,n),{setDescriptionCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),pi=`DialogClose`,mi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(pi,n);return(0,j.jsx)(wt.button,{type:`button`,...r,ref:t,onClick:Te(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function hi(e){return e?`open`:`closed`}Ur(hi,`getState`);function gi(...e){return e.filter(Boolean).join(` `)}function _i({className:e,...t}){return(0,j.jsx)(`button`,{className:gi(`ui-button`,e),...t})}function vi({className:e,...t}){return(0,j.jsx)(`span`,{className:gi(`ui-badge`,e),...t})}function yi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,j.jsx)(Yr,{open:e,onOpenChange:t,children:(0,j.jsxs)($r,{children:[(0,j.jsx)(ti,{className:`sheet-overlay`}),(0,j.jsxs)(ai,{className:`sheet-content`,children:[(0,j.jsxs)(`div`,{className:`sheet-head`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ui,{children:n}),r?(0,j.jsx)(fi,{children:r}):null]}),(0,j.jsx)(mi,{asChild:!0,children:(0,j.jsx)(_i,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,j.jsx)(pe,{size:18})})})]}),i]})]})})}function bi({title:e,detail:t}){return(0,j.jsxs)(`div`,{className:`empty-state`,children:[(0,j.jsx)(`strong`,{children:e}),t?(0,j.jsx)(`p`,{children:t}):null]})}var xi=[`codex`,`claude`,`pi`],Si={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},Ci=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},wi=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ti(e,t={}){let n=await fetch(e,{cache:`no-store`,...t});if(!n.ok){let e=await n.json().catch(()=>({}));throw new wi(e.error||`HTTP ${n.status}`,e)}return n.json()}function Ei(e,t){if(!(e instanceof wi)||!e.body.readiness)return e instanceof Error?e.message:t;let n=e.body.readiness;return[n.reason||e.message,n.nextAction?`下一步:${n.nextAction}`:null].filter(Boolean).join(`;`)}async function Di(e){return Ti(`/api/config`,{method:`PATCH`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":Ci.csrfCapability},body:JSON.stringify(e)})}function Oi(e,t={}){return Ti(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":Ci.csrfCapability}})}function ki(e){return e==null||!Number.isFinite(e)?`暂无`:e<60?`${e}s 前`:e<3600?`${Math.floor(e/60)}m 前`:e<86400?`${Math.floor(e/3600)}h 前`:`${Math.floor(e/86400)}d 前`}function Ai(e){let t=new Date(String(e||``));return Number.isFinite(t.getTime())?t.toLocaleString(`zh-CN`,{hour12:!1}):`未知时间`}function ji(e){return Number.isFinite(Number(e))?Number(e).toLocaleString(`en-US`):`—`}var Mi=1,Ni=1440;function Pi(e){return String(e/6e4)}function Fi(e){let t=Number(String(e).trim()),n=t*6e4,r=Math.round(n);return Number.isFinite(t)&&t>=Mi&&t<=Ni&&Number.isSafeInteger(r)&&Math.abs(n-r)<=1e-6?r:null}function Ii(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Li(){let[e,t]=(0,C.useState)(null),[n,r]=(0,C.useState)(null),[i,a]=(0,C.useState)(!0),[o,s]=(0,C.useState)(!1),c=(0,C.useRef)(xe()),l=(0,C.useRef)(null),u=(0,C.useRef)(null),d=(0,C.useCallback)(async()=>{u.current?.abort();let e=new AbortController;u.current=e;let n=c.current.issue();try{let i=await Ti(`/api/status`,{signal:e.signal});if(!c.current.accepts(n))return;if(Ci.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==Ci.buildFingerprint){window.location.reload();return}t(i),r(null),s(!1)}catch(t){if(e.signal.aborted||!c.current.accepts(n))return;r(t instanceof Error?t.message:String(t)),s(!0)}finally{c.current.accepts(n)&&a(!1)}},[]);return(0,C.useEffect)(()=>{let e=!0,t=async()=>{await d(),e&&(l.current=window.setTimeout(t,3e3))};return t(),()=>{e=!1,l.current!==null&&window.clearTimeout(l.current),u.current?.abort()}},[d]),{data:e,error:n,loading:i,stale:o,retry:d}}function Ri({agent:e}){let t=(e.displayName||e.name||e.agentId).slice(0,2).toUpperCase(),[n,r]=(0,C.useState)(!1);(0,C.useEffect)(()=>r(!1),[e.agentId,e.bot?.hasAvatar]);let i=e.bot?.hasAvatar&&!n;return(0,j.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,j.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function zi({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>ye(e,i),[e,i]),s=(0,C.useRef)([]),c=(e,t)=>{if(!o.length)return;let n=t===0?e:(e+t+o.length)%o.length;s.current[n]?.focus()};return(0,j.jsxs)(`div`,{className:gi(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,j.jsxs)(`label`,{className:`agent-search`,children:[(0,j.jsx)(de,{size:15,"aria-hidden":`true`}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,j.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,j.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,j.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:gi(`agent-list-item`,e.agentId===t&&`selected`),onClick:()=>n(e.agentId),onKeyDown:e=>{e.key===`ArrowDown`&&(e.preventDefault(),c(r,1)),e.key===`ArrowUp`&&(e.preventDefault(),c(r,-1)),e.key===`Home`&&(e.preventDefault(),c(0,0)),e.key===`End`&&(e.preventDefault(),c(o.length-1,0))},children:[(0,j.jsx)(Ri,{agent:e}),(0,j.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,j.jsx)(`strong`,{children:e.displayName||e.name}),(0,j.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,j.jsxs)(`span`,{className:`status-line`,children:[(0,j.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Ii(e)]})]}),e.issue?(0,j.jsx)(A,{size:15,className:`danger`,"aria-label":`异常`}):(0,j.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,j.jsx)(bi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function N({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[l,u]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[d,f]=(0,C.useState)(`15`),[p,m]=(0,C.useState)(!1),[h,g]=(0,C.useState)(null),_=a!==r||l.enabled!==s.enabled||d!==Pi(s.intervalMs);return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return m(!0),Oi(`/api/config`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),c(e.inboxAudit),u(e.inboxAudit),f(Pi(e.inboxAudit.intervalMs)),g(null)}).catch(e=>{t.signal.aborted||g(e.message)}).finally(()=>{t.signal.aborted||m(!1)}),()=>t.abort()},[e]),(0,j.jsx)(yi,{open:e,onOpenChange:e=>{!e&&_&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,j.jsxs)(`div`,{className:`settings-form`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`真人群消息默认策略`}),(0,j.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:p,children:[(0,j.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`global-inbox-audit-title`,children:[(0,j.jsxs)(`div`,{className:`audit-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`global-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsx)(`p`,{children:`定时检查仍待处理的入站工作;关闭时不会安排巡检。`})]}),(0,j.jsxs)(`label`,{className:`audit-switch`,children:[(0,j.jsx)(`input`,{type:`checkbox`,checked:l.enabled,disabled:p,onChange:e=>u(t=>({...t,enabled:e.target.checked}))}),(0,j.jsx)(`span`,{"aria-hidden":`true`}),(0,j.jsx)(`b`,{children:l.enabled?`已开启`:`已关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`全局巡检间隔(分钟)`,type:`number`,min:Mi,max:Ni,step:`any`,inputMode:`decimal`,value:d,disabled:p,onChange:e=>f(e.target.value)})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`可在关闭时预先调整;保存间隔本身不会开启巡检。`})]}),(0,j.jsxs)(`div`,{className:`form-actions`,children:[(0,j.jsx)(_i,{onClick:()=>{o(r),u(s),f(Pi(s.intervalMs))},disabled:!_||p,children:`放弃草稿`}),(0,j.jsx)(_i,{className:`primary`,onClick:async()=>{let e=l.enabled!==s.enabled||d!==Pi(s.intervalMs),t=e?Fi(d):null;if(e&&t===null){g(`巡检间隔必须在 1 到 1440 分钟之间。`);return}let o=t??s.intervalMs;m(!0);try{let t=null;a!==r&&(t=await Di({operation:`set-global-mention`,value:a})),e&&(t=await Di({operation:`set-global-inbox-audit`,...l.enabled===s.enabled?{}:{enabled:l.enabled},...d===Pi(s.intervalMs)?{}:{intervalMs:o}})),i(a),c({...l,intervalMs:o}),u(e=>({...e,intervalMs:o})),f(Pi(o)),g(t?`已保存 · ${t.applyState} · ${t.revision.slice(0,20)}…`:`没有需要保存的修改`),n()}catch(e){g(e instanceof Error?e.message:String(e))}finally{m(!1)}},disabled:!_||p,children:p?`保存中…`:`保存全局设置`})]}),h?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:h}):null]})})}function P({config:e,onSave:t,disabled:n}){let[r,i]=(0,C.useState)(``),[a,o]=(0,C.useState)(``),[s,c]=(0,C.useState)(`require`),l=e.knownChats.filter(e=>e.kind===`group`&&e.override!==`inherit`).filter(e=>{let t=r.trim().toLocaleLowerCase();return!t||e.chatId.toLocaleLowerCase().includes(t)||String(e.displayName||`未解析群名`).toLocaleLowerCase().includes(t)});return(0,j.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,j.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,j.jsxs)(`label`,{className:`table-search`,children:[(0,j.jsx)(de,{size:14}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,j.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,j.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,j.jsxs)(`table`,{className:`policy-table`,children:[(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{children:`群`}),(0,j.jsx)(`th`,{children:`消息策略`}),(0,j.jsx)(`th`,{children:`操作`})]})}),(0,j.jsx)(`tbody`,{children:l.map(e=>(0,j.jsxs)(`tr`,{children:[(0,j.jsxs)(`td`,{children:[(0,j.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,j.jsx)(`code`,{children:e.chatId}):(0,j.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,j.jsx)(`td`,{children:(0,j.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,j.jsx)(`td`,{children:(0,j.jsx)(_i,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,j.jsx)(bi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,j.jsxs)(`div`,{className:`chat-add`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`完整 chat_id`}),(0,j.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`消息策略`}),(0,j.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,j.jsx)(_i,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function Bi(e){return[`Readiness ${e.state}`,e.reason,e.nextAction].filter(Boolean).join(` · `)}function Vi({agentId:e,readiness:t,onDirtyChange:n,refreshKey:r}){let[i,a]=(0,C.useState)(null),[o,s]=(0,C.useState)({}),[c,l]=(0,C.useState)({}),[u,d]=(0,C.useState)(!0),[f,p]=(0,C.useState)(null),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(xe()),_=(0,C.useRef)(xe()),v=!be(o,c);(0,C.useEffect)(()=>n(v),[v,n]),(0,C.useEffect)(()=>{let e=e=>{v&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[v]);let y=(0,C.useCallback)(async(t=!1)=>{let n=g.current.issue();d(!0);try{let r=await Oi(`/api/config?agent=${encodeURIComponent(e)}`);if(!g.current.accepts(n))return;let i=r.agents[0];if(!i)throw Error(`配置投影中没有当前 Agent`);let o=i.inboxAudit.override.intervalMs===`inherit`?i.inboxAudit.effective.intervalMs:i.inboxAudit.override.intervalMs,c={runtime:i.runtimeOption||i.runtime,model:i.model,effort:i.effort||`default`,mention:i.mention.override,auditEnabled:i.inboxAudit.override.enabled,auditIntervalInherited:i.inboxAudit.override.intervalMs===`inherit`,auditGapMinutes:Pi(o)};a(r),s(c),(!t||!v)&&l(c)}catch(e){g.current.accepts(n)&&p(e instanceof Error?e.message:String(e))}finally{g.current.accepts(n)&&d(!1)}},[e,v]);(0,C.useEffect)(()=>{y(!1)},[e]),(0,C.useEffect)(()=>{r>0&&y(!0)},[r]);let b=i?.agents[0],x=(e,t)=>l(n=>({...n,[e]:t})),S=String(c.runtime||b?.runtime||``),w=String(c.model||b?.model||`default`);(0,C.useEffect)(()=>{if(h({runtime:S,status:`loading`,models:[]}),!xi.includes(S)){h({runtime:S,status:`error`,models:[]});return}let t=_.current.issue(),n=new AbortController;return Oi(`/api/models/${S}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{_.current.accepts(t)&&h({runtime:S,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&_.current.accepts(t)&&(h({runtime:S,status:`error`,models:[]}),p(`${S} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,S]);let ee=m?.runtime===S&&m.status===`ready`,te=m?.runtime===S?m.status:`loading`,T=ee?m.models:[],E=`${w} · 当前配置(模型目录${te===`error`?`不可用`:`加载中`})`,ne=ee?T.some(e=>e.id===w)?T:[{id:w,label:`${w} · 当前配置`},...T]:[{id:w,label:E}],re=ee&&w!=="default"&&T.find(e=>e.id===w)?.supportedReasoningEfforts||[],ie=c.runtime!==o.runtime||c.model!==o.model||c.effort!==o.effort,ae=c.auditEnabled===`on`||c.auditEnabled===`off`?c.auditEnabled:`inherit`,oe=c.auditIntervalInherited===!0,se=String(c.auditGapMinutes||``),ce=c.auditEnabled!==o.auditEnabled||c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes,D=()=>Ti(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":Ci.csrfCapability},body:JSON.stringify({agentId:e})}),O=async()=>{if(!b)return;let t=Fi(c.auditGapMinutes);if(ce&&!oe&&t===null){p(`巡检间隔必须在 1 到 1440 分钟之间;草稿已保留。`);return}d(!0);try{let n=[];c.runtime===o.runtime?c.model!==o.model&&n.push({operation:`set-agent-model`,agentId:e,model:c.model}):n.push({operation:`set-agent-runtime`,agentId:e,runtime:c.runtime,model:c.model}),c.effort!==o.effort&&n.push({operation:`set-agent-effort`,agentId:e,effort:c.effort}),c.mention!==o.mention&&n.push({operation:`set-agent-mention`,agentId:e,value:c.mention}),ce&&n.push({operation:`set-agent-inbox-audit`,agentId:e,...c.auditEnabled===o.auditEnabled?{}:{enabled:ae===`inherit`?`inherit`:ae===`on`},...c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes?{intervalMs:oe?`inherit`:t}:{}});let r=null;for(let e of n)r=await Di(e);if(ie)try{await D(),p(`配置已保存并应用`)}catch(e){p(`配置已保存但未应用:${Ei(e,`稍后重试`)}`)}else p(r?`配置已保存`:`没有需要保存的修改`);await y(!1)}catch(e){p(Ei(e,e instanceof Error?e.message:String(e)))}finally{d(!1)}},le=async()=>{d(!0);try{await D(),p(`已应用保存的运行配置`),await y(!0)}catch(e){p(`配置保持已保存但未应用:${Ei(e,`稍后重试`)}`)}finally{d(!1)}},de=async(t,n)=>{d(!0);try{let r=await Di({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});p(`群策略已保存 · ${r.applyState}`),await y(!0)}catch(e){p(e instanceof Error?e.message:String(e))}finally{d(!1)}};if(!b&&u)return(0,j.jsx)(bi,{title:`加载 Agent 配置…`});if(!b)return(0,j.jsx)(bi,{title:`配置不可用`,detail:f||`请重试`});let fe=b.inboxAudit.effective,k=b.inboxAudit.source,A=`${k.enabled===`agent`?`当前 Agent 已单独设置`:k.enabled===`global`?`当前使用全局设置`:`当前使用默认设置`}:${fe.enabled?`已开启`:`已关闭`},每 ${Pi(fe.intervalMs)} 分钟`,pe=b.apply.applyState===`pending`?`待应用`:b.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,j.jsxs)(`div`,{className:`configuration-page`,children:[(0,j.jsxs)(`section`,{className:`config-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`Agent 配置`}),(0,j.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,j.jsx)(vi,{className:b.apply.applyState===`pending`?`warning`:`success`,children:pe})]}),(0,j.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,j.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),t?(0,j.jsx)(`p`,{className:`field-help`,children:Bi(t)}):null,(0,j.jsxs)(`div`,{className:`config-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Runtime`}),(0,j.jsx)(`select`,{value:S,disabled:u,onChange:e=>{x(`runtime`,e.target.value),x(`model`,`default`),x(`effort`,`default`)},children:(i?.runtimeOptions||xi).map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Model`}),(0,j.jsx)(`select`,{value:w,disabled:u||!ee,onChange:e=>{x(`model`,e.target.value),e.target.value==="default"&&x(`effort`,`default`)},children:ne.map(e=>(0,j.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Effort`}),(0,j.jsxs)(`select`,{value:String(c.effort||`default`),disabled:u||!ee||w==="default"||!re.length,onChange:e=>x(`effort`,e.target.value),children:[(0,j.jsx)(`option`,{value:`default`,children:`default · 不指定`}),re.map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`群消息策略`}),(0,j.jsxs)(`select`,{value:String(c.mention||`inherit`),disabled:u,onChange:e=>x(`mention`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`agent-inbox-audit-title`,children:[(0,j.jsx)(`div`,{className:`audit-heading`,children:(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`agent-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsxs)(`p`,{children:[A,`。`]})]})}),(0,j.jsxs)(`div`,{className:`config-grid audit-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检设置`}),(0,j.jsxs)(`select`,{"aria-label":`Inbox 巡检设置`,value:ae,disabled:u,onChange:e=>x(`auditEnabled`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`on`,children:`为此 Agent 开启`}),(0,j.jsx)(`option`,{value:`off`,children:`为此 Agent 关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`Agent 巡检间隔(分钟)`,type:`number`,min:Mi,max:Ni,step:`any`,inputMode:`decimal`,value:se,disabled:u,onChange:e=>l(t=>({...t,auditGapMinutes:e.target.value,auditIntervalInherited:!1}))})]})]}),(0,j.jsxs)(`div`,{className:`audit-inline-actions`,children:[(0,j.jsx)(`span`,{children:oe?`间隔跟随全局设置;编辑数字后会仅作用于此 Agent。`:`此 Agent 使用单独的巡检间隔。`}),oe?null:(0,j.jsx)(_i,{disabled:u,onClick:()=>l(e=>({...e,auditIntervalInherited:!0,auditGapMinutes:Pi(fe.intervalMs)})),children:`间隔跟随全局设置`})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`即使关闭巡检,也可保存间隔;间隔修改不会开启巡检。`})]}),v||b.apply.applyState===`pending`?(0,j.jsxs)(`div`,{className:`form-actions`,children:[v?(0,j.jsx)(_i,{disabled:u,onClick:()=>l(o),children:`放弃草稿`}):null,v?(0,j.jsx)(_i,{className:`primary`,disabled:u,onClick:O,children:u?`处理中…`:ie?`保存并应用`:`保存 Agent 配置`}):(0,j.jsxs)(_i,{className:`primary`,disabled:u,onClick:le,children:[(0,j.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,f?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:f}):null]}),(0,j.jsx)(P,{config:b,onSave:de,disabled:u})]})}function Hi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,j.jsxs)(`div`,{className:`overview-page`,children:[(0,j.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ie,{})}),(0,j.jsx)(`span`,{children:`连接`}),(0,j.jsx)(`strong`,{children:e.connection.state}),(0,j.jsx)(`small`,{children:e.connection.reason})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`执行状态`}),(0,j.jsx)(`strong`,{children:Ii(e)}),(0,j.jsx)(`small`,{children:e.lastActivity?ki(e.lastActivity.ageSec):`尚无活动`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(le,{})}),(0,j.jsx)(`span`,{children:`最近消息`}),(0,j.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,j.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${ki(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`上下文`}),(0,j.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,j.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`累计 Token`}),(0,j.jsx)(`strong`,{children:ji(t.cumulativeTokens)}),(0,j.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`最近一轮用量`}),(0,j.jsx)(`strong`,{children:ji(t.latestTokens)}),(0,j.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ue,{})}),(0,j.jsx)(`span`,{children:`Compaction`}),(0,j.jsx)(`strong`,{children:ji(n.count)}),(0,j.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${Ai(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,j.jsxs)(`div`,{className:`overview-sections`,children:[(0,j.jsxs)(`section`,{className:`content-section wide`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`最近动态`}),(0,j.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Ui,{items:e.feed.slice(0,10)})]}),(0,j.jsxs)(`section`,{className:`content-section`,children:[(0,j.jsx)(`h3`,{children:`运行摘要`}),(0,j.jsxs)(`dl`,{className:`facts`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Runtime`}),(0,j.jsx)(`dd`,{children:e.runtime})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Readiness`}),(0,j.jsx)(`dd`,{children:e.runtimeReadiness?`${e.runtimeReadiness.state}${e.runtimeReadiness.version?` · ${e.runtimeReadiness.version}`:``}${e.runtimeReadiness.executable?` · ${e.runtimeReadiness.executable}`:``}${e.runtimeReadiness.reason?` · ${e.runtimeReadiness.reason}`:``}${e.runtimeReadiness.nextAction?` · ${e.runtimeReadiness.nextAction}`:``}`:`unknown`})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Model`}),(0,j.jsx)(`dd`,{children:e.model})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Effort`}),(0,j.jsx)(`dd`,{children:e.effort||`default`})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`入站`}),(0,j.jsx)(`dd`,{children:e.inbound.state})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`提醒`}),(0,j.jsx)(`dd`,{children:e.activeReminders})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`指示灯`}),(0,j.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function Ui({items:e}){if(!e.length)return(0,j.jsx)(bi,{title:`还没有活动记录`});let t=e=>e.kind===`error`||e.state===`error`?`state-error`:e.kind===`deliver`?`state-deliver`:[`working`,`thinking`,`tool`].includes(String(e.state))?`state-active`:[`idle`,`online`].includes(String(e.state))?`state-idle`:`state-unknown`;return(0,j.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,j.jsxs)(`li`,{children:[(0,j.jsx)(`span`,{className:gi(`timeline-dot`,t(e))}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,j.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,j.jsx)(`time`,{children:Ai(e.at)})]})]},`${e.at}-${n}`))})}function Wi({agent:e}){return e.conversation.length?(0,j.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,j.jsxs)(`article`,{className:gi(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,j.jsxs)(`header`,{children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(vi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,j.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,j.jsx)(`time`,{children:Ai(t.at)})]}),(0,j.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,j.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,j.jsx)(bi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Gi({agent:e}){if(!e.remindersList.length)return(0,j.jsx)(bi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,j.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,j.jsxs)(`article`,{className:gi(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,j.jsx)(ne,{size:16}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,j.jsxs)(`p`,{children:[Ai(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,j.jsx)(vi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function Ki({agentId:e}){let[t,n]=(0,C.useState)(null),[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)(!0),[s,c]=(0,C.useState)(null),l=(0,C.useRef)(xe()),u=(0,C.useCallback)(async t=>{let r=l.current.issue();o(!0);try{let a=await Ti(`/api/workspace?agent=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`);if(!l.current.accepts(r))return;a.kind===`directory`?(n(a),i(null)):i(a),c(null)}catch(e){l.current.accepts(r)&&c(e instanceof Error?e.message:String(e))}finally{l.current.accepts(r)&&o(!1)}},[e]);(0,C.useEffect)(()=>{u(``)},[u]);let d=t?.path.split(`/`).filter(Boolean)||[];return(0,j.jsxs)(`div`,{className:`workspace-browser`,children:[(0,j.jsxs)(`div`,{className:`workspace-side`,children:[(0,j.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,j.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,j.jsxs)(`span`,{children:[(0,j.jsx)(re,{size:12}),(0,j.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,j.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,j.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,j.jsx)(se,{size:15}):(0,j.jsx)(oe,{size:15}),(0,j.jsx)(`span`,{children:e.name}),(0,j.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,j.jsx)(bi,{title:`空目录`}):null]})]}),(0,j.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,j.jsx)(bi,{title:`读取工作区…`}):s?(0,j.jsx)(bi,{title:`工作区读取失败`,detail:s}):r?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`header`,{children:[(0,j.jsx)(`strong`,{children:r.name}),(0,j.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,j.jsx)(bi,{title:`二进制文件不可预览`}):(0,j.jsx)(`pre`,{children:r.content})]}):(0,j.jsx)(bi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function qi({agent:e}){return(0,j.jsxs)(`section`,{className:`logs-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`结构化日志`}),(0,j.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Ui,{items:e.feed}),e.recentErrors.length?(0,j.jsxs)(`section`,{className:`error-stack`,children:[(0,j.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ae,{size:14}),(0,j.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,j.jsx)(`time`,{children:Ai(e.at)})]},`${e.at}-${t}`))]}):null]})}function Ji({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{},r=t.contextPercent==null?`上下文 ${e.session?.turns||0} turns`:`上下文 ${Math.round(Number(t.contextPercent))}% · ${ji(t.latestTokens)} / ${ji(t.contextWindow)}`;return(0,j.jsxs)(`header`,{className:`agent-header`,children:[(0,j.jsxs)(`div`,{className:`agent-title`,children:[(0,j.jsx)(Ri,{agent:e}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`p`,{children:e.agentId}),(0,j.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,j.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,j.jsx)(vi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,j.jsx)(`span`,{children:e.runtime}),(0,j.jsx)(`span`,{children:e.model}),(0,j.jsx)(`span`,{children:e.effort||`default effort`}),(0,j.jsx)(`span`,{children:r}),(0,j.jsxs)(`span`,{children:[`累计 `,ji(t.cumulativeTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`最近一轮 `,ji(t.latestTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`Compact `,ji(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,j.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?ki(e.lastDeliver.ageSec):`暂无`]}),(0,j.jsx)(`span`,{children:e.lastActivity?.detail||Ii(e)})]})]})}function Yi(){let e=Li(),t=(0,C.useMemo)(()=>ge(window.location.search),[]),[n,r]=(0,C.useState)(t.agentId),[i,a]=(0,C.useState)(t.tab),[o,s]=(0,C.useState)(!1),[c,l]=(0,C.useState)(!1),[u,d]=(0,C.useState)(0),[f,p]=(0,C.useState)(!1),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(new Map),_=e.data?.agents||[],v=e.data?.packageVersion||Ci.packageVersion,y=_.find(e=>e.agentId===n)||null,b=(0,C.useCallback)((e,t)=>{let n=`${e}:${t}`,r=window.scrollY;g.current.set(n,r);try{sessionStorage.setItem(`larkin-dashboard-scroll:${n}`,String(r))}catch{}},[]),x=(0,C.useCallback)(e=>{if(g.current.has(e))return g.current.get(e)||0;try{let t=Number(sessionStorage.getItem(`larkin-dashboard-scroll:${e}`));return Number.isFinite(t)&&t>=0?t:0}catch{return 0}},[]),S=(0,C.useCallback)((e,t,o=!1)=>{n&&b(n,i);let s=o?`replaceState`:`pushState`;window.history[s](null,``,_e(e,t)),r(e),a(t)},[b,n,i]),w=()=>!f||window.confirm(`Agent 配置尚未保存,确定放弃草稿吗?`),ee=e=>{w()&&(p(!1),S(e,i),l(!1))},te=e=>{e!==`configuration`&&!w()||(e!==`configuration`&&p(!1),S(n,e))};return(0,C.useEffect)(()=>{if(!e.data)return;let t=ve(n,e.data.agents);t!==n&&(h(n?`原 Agent 已不存在,已切换到第一个可用 Agent。`:null),S(t,i,!0))},[e.data,n,i,S]),(0,C.useEffect)(()=>{let e=()=>{let e=ge(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,_e(n,i));return}n&&b(n,i),p(!1),r(e.agentId),a(e.tab)};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[f,b,n,i]),(0,C.useLayoutEffect)(()=>{if(!n)return;let e=`${n}:${i}`,t=x(e);window.scrollY!==t&&window.scrollTo({top:t,behavior:`auto`})},[x,n,i]),(0,j.jsxs)(`div`,{className:gi(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,j.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,j.jsxs)(`div`,{className:`brand`,children:[(0,j.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,j.jsx)(zi,{agents:_,selectedId:n,onSelect:ee}),(0,j.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,j.jsxs)(`main`,{className:gi(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,j.jsxs)(`div`,{className:`app-topbar`,children:[(0,j.jsx)(_i,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,j.jsx)(O,{size:18})}),(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,j.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,j.jsx)(vi,{className:`warning`,children:`状态陈旧`}):null]}),(0,j.jsxs)(_i,{onClick:()=>s(!0),children:[(0,j.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,j.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,j.jsx)(A,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,j.jsx)(_i,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,j.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,j.jsx)(_i,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,j.jsxs)(`div`,{className:`page-loading`,children:[(0,j.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(Ji,{agent:y}),(0,j.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:he.map(e=>(0,j.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,j.jsx)(ce,{}):e===`conversation`?(0,j.jsx)(le,{}):e===`configuration`?(0,j.jsx)(k,{}):e===`reminders`?(0,j.jsx)(ne,{}):e===`workspace`?(0,j.jsx)(se,{}):(0,j.jsx)(D,{}),Si[e]]},e))}),(0,j.jsxs)(`div`,{className:gi(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,j.jsx)(Hi,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,j.jsx)(Wi,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,j.jsx)(Vi,{agentId:y.agentId,readiness:y.runtimeReadiness??null,onDirtyChange:p,refreshKey:u})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,j.jsx)(Gi,{agent:y})}),(0,j.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,j.jsx)(Ki,{agentId:y.agentId})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,j.jsx)(qi,{agent:y})})]},y.agentId)]}):(0,j.jsx)(bi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,j.jsx)(N,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,j.jsx)(yi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,j.jsx)(zi,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Xi=document.getElementById(`root`);if(!Xi)throw Error(`dashboard root is missing`);(0,me.createRoot)(Xi).render((0,j.jsx)(C.StrictMode,{children:(0,j.jsx)(Yi,{})}));
49
+ `},Dr=0,Or=[];function kr(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Dr++)[0],a=C.useState(Zn)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=En([e.lockRef.current],(e.shards||[]).map(wr),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Sr(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=hr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=hr(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return xr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!Or.length||Or[Or.length-1]!==a)){var r=`deltaY`in n?Cr(n):Sr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Tr(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(wr).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:Ar(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Sr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Sr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return Or.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,ur),document.addEventListener(`touchmove`,c,ur),document.addEventListener(`touchstart`,u,ur),function(){Or=Or.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,ur),document.removeEventListener(`touchmove`,c,ur),document.removeEventListener(`touchstart`,u,ur)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Er(i)}):null,p?C.createElement(sr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function Ar(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var jr=Bn(Vn,kr),Mr=C.forwardRef(function(e,t){return C.createElement(Un,wn({},e,{ref:t,sideCar:jr}))});Mr.classNames=Un.classNames;var Nr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Pr=new WeakMap,Fr=new WeakMap,Ir={},Lr=0,Rr=function(e){return e&&(e.host||Rr(e.parentNode))},zr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=Rr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},Br=function(e,t,n,r){var i=zr(t,Array.isArray(e)?e:[e]);Ir[n]||(Ir[n]=new WeakMap);var a=Ir[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(Pr.get(e)||0)+1,l=(a.get(e)||0)+1;Pr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Fr.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),Lr++,function(){o.forEach(function(e){var t=Pr.get(e)-1,i=a.get(e)-1;Pr.set(e,t),a.set(e,i),t||(Fr.has(e)||e.removeAttribute(r),Fr.delete(e)),i||e.removeAttribute(n)}),Lr--,Lr||(Pr=new WeakMap,Pr=new WeakMap,Fr=new WeakMap,Ir={})}},Vr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Nr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Br(r,i,n,`aria-hidden`)):function(){return null}},Hr=Object.defineProperty,Ur=(e,t)=>Hr(e,`name`,{value:t,configurable:!0}),Wr=`Dialog`,[Gr,Kr]=ze(Wr),[qr,Jr]=Gr(Wr),Yr=Ur(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=tt({prop:r,defaultProp:i??!1,onChange:a,caller:Wr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,j.jsx)(qr,{scope:t,triggerRef:s,contentRef:c,contentId:Ke(),titleId:Ke(),descriptionId:Ke(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Xr=`DialogPortal`,[Zr,Qr]=Gr(Xr,{forceMount:void 0}),$r=Ur(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Jr(Xr,t);return(0,j.jsx)(Zr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,j.jsx)(dn,{present:n||a.open,children:(0,j.jsx)(sn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ei=`DialogOverlay`,ti=C.forwardRef(Ur(function(e,t){let n=Qr(ei,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ei,e.__scopeDialog);return a.modal?(0,j.jsx)(dn,{present:r||a.open,children:(0,j.jsx)(ri,{...i,ref:t})}):null},`DialogOverlay`)),ni=lt(`DialogOverlay.RemoveScroll`),ri=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(ei,n),a=Pe(t,It());return(0,j.jsx)(Mr,{as:ni,allowPinchZoom:!0,shards:[i.contentRef],children:(0,j.jsx)(wt.div,{"data-state":hi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ii=`DialogContent`,ai=C.forwardRef(Ur(function(e,t){let n=Qr(ii,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ii,e.__scopeDialog);return(0,j.jsx)(dn,{present:r||a.open,children:a.modal?(0,j.jsx)(oi,{...i,ref:t}):(0,j.jsx)(si,{...i,ref:t})})},`DialogContent`)),oi=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(null),i=Pe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Vr(e)},[]),(0,j.jsx)(ci,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Te(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Te(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Te(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),si=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,j.jsx)(ci,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),ci=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Jr(ii,n);return Sn(),(0,j.jsx)(j.Fragment,{children:(0,j.jsx)(qt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,j.jsx)(Ft,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":hi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),li=`DialogTitle`,ui=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(li,n),{setTitleCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),di=`DialogDescription`,fi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(di,n),{setDescriptionCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),pi=`DialogClose`,mi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(pi,n);return(0,j.jsx)(wt.button,{type:`button`,...r,ref:t,onClick:Te(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function hi(e){return e?`open`:`closed`}Ur(hi,`getState`);function gi(...e){return e.filter(Boolean).join(` `)}function _i({className:e,...t}){return(0,j.jsx)(`button`,{className:gi(`ui-button`,e),...t})}function vi({className:e,...t}){return(0,j.jsx)(`span`,{className:gi(`ui-badge`,e),...t})}function yi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,j.jsx)(Yr,{open:e,onOpenChange:t,children:(0,j.jsxs)($r,{children:[(0,j.jsx)(ti,{className:`sheet-overlay`}),(0,j.jsxs)(ai,{className:`sheet-content`,children:[(0,j.jsxs)(`div`,{className:`sheet-head`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ui,{children:n}),r?(0,j.jsx)(fi,{children:r}):null]}),(0,j.jsx)(mi,{asChild:!0,children:(0,j.jsx)(_i,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,j.jsx)(pe,{size:18})})})]}),i]})]})})}function bi({title:e,detail:t}){return(0,j.jsxs)(`div`,{className:`empty-state`,children:[(0,j.jsx)(`strong`,{children:e}),t?(0,j.jsx)(`p`,{children:t}):null]})}var xi=[`codex`,`claude`,`pi`],Si={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},Ci=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},wi=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ti(e,t={}){let n=await fetch(e,{cache:`no-store`,...t});if(!n.ok){let e=await n.json().catch(()=>({}));throw new wi(e.error||`HTTP ${n.status}`,e)}return n.json()}function Ei(e,t){if(!(e instanceof wi)||!e.body.readiness)return e instanceof Error?e.message:t;let n=e.body.readiness;return[n.reason||e.message,n.nextAction?`下一步:${n.nextAction}`:null].filter(Boolean).join(`;`)}async function Di(e){return Ti(`/api/config`,{method:`PATCH`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":Ci.csrfCapability},body:JSON.stringify(e)})}function Oi(e,t={}){return Ti(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":Ci.csrfCapability}})}function ki(e){return e==null||!Number.isFinite(e)?`暂无`:e<60?`${e}s 前`:e<3600?`${Math.floor(e/60)}m 前`:e<86400?`${Math.floor(e/3600)}h 前`:`${Math.floor(e/86400)}d 前`}function Ai(e){let t=new Date(String(e||``));return Number.isFinite(t.getTime())?t.toLocaleString(`zh-CN`,{hour12:!1}):`未知时间`}function ji(e){return Number.isFinite(Number(e))?Number(e).toLocaleString(`en-US`):`—`}var Mi=1,Ni=1440;function Pi(e){return String(e/6e4)}function Fi(e){let t=Number(String(e).trim()),n=t*6e4,r=Math.round(n);return Number.isFinite(t)&&t>=Mi&&t<=Ni&&Number.isSafeInteger(r)&&Math.abs(n-r)<=1e-6?r:null}function Ii(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Li(){let[e,t]=(0,C.useState)(null),[n,r]=(0,C.useState)(null),[i,a]=(0,C.useState)(!0),[o,s]=(0,C.useState)(!1),c=(0,C.useRef)(xe()),l=(0,C.useRef)(null),u=(0,C.useRef)(null),d=(0,C.useCallback)(async()=>{u.current?.abort();let e=new AbortController;u.current=e;let n=c.current.issue();try{let i=await Ti(`/api/status`,{signal:e.signal});if(!c.current.accepts(n))return;if(Ci.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==Ci.buildFingerprint){window.location.reload();return}t(i),r(null),s(!1)}catch(t){if(e.signal.aborted||!c.current.accepts(n))return;r(t instanceof Error?t.message:String(t)),s(!0)}finally{c.current.accepts(n)&&a(!1)}},[]);return(0,C.useEffect)(()=>{let e=!0,t=async()=>{await d(),e&&(l.current=window.setTimeout(t,3e3))};return t(),()=>{e=!1,l.current!==null&&window.clearTimeout(l.current),u.current?.abort()}},[d]),{data:e,error:n,loading:i,stale:o,retry:d}}function Ri({agent:e}){let t=(e.displayName||e.name||e.agentId).slice(0,2).toUpperCase(),[n,r]=(0,C.useState)(!1);(0,C.useEffect)(()=>r(!1),[e.agentId,e.bot?.hasAvatar]);let i=e.bot?.hasAvatar&&!n;return(0,j.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,j.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function zi({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>ye(e,i),[e,i]),s=(0,C.useRef)([]),c=(e,t)=>{if(!o.length)return;let n=t===0?e:(e+t+o.length)%o.length;s.current[n]?.focus()};return(0,j.jsxs)(`div`,{className:gi(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,j.jsxs)(`label`,{className:`agent-search`,children:[(0,j.jsx)(de,{size:15,"aria-hidden":`true`}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,j.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,j.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,j.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:gi(`agent-list-item`,e.agentId===t&&`selected`),onClick:()=>n(e.agentId),onKeyDown:e=>{e.key===`ArrowDown`&&(e.preventDefault(),c(r,1)),e.key===`ArrowUp`&&(e.preventDefault(),c(r,-1)),e.key===`Home`&&(e.preventDefault(),c(0,0)),e.key===`End`&&(e.preventDefault(),c(o.length-1,0))},children:[(0,j.jsx)(Ri,{agent:e}),(0,j.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,j.jsx)(`strong`,{children:e.displayName||e.name}),(0,j.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,j.jsxs)(`span`,{className:`status-line`,children:[(0,j.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Ii(e)]})]}),e.issue?(0,j.jsx)(A,{size:15,className:`danger`,"aria-label":`异常`}):(0,j.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,j.jsx)(bi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function N({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[l,u]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[d,f]=(0,C.useState)(`15`),[p,m]=(0,C.useState)(!1),[h,g]=(0,C.useState)(null),_=a!==r||l.enabled!==s.enabled||d!==Pi(s.intervalMs);return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return m(!0),Oi(`/api/config`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),c(e.inboxAudit),u(e.inboxAudit),f(Pi(e.inboxAudit.intervalMs)),g(null)}).catch(e=>{t.signal.aborted||g(e.message)}).finally(()=>{t.signal.aborted||m(!1)}),()=>t.abort()},[e]),(0,j.jsx)(yi,{open:e,onOpenChange:e=>{!e&&_&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,j.jsxs)(`div`,{className:`settings-form`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`真人群消息默认策略`}),(0,j.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:p,children:[(0,j.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`global-inbox-audit-title`,children:[(0,j.jsxs)(`div`,{className:`audit-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`global-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsx)(`p`,{children:`定时检查仍待处理的入站工作;关闭时不会安排巡检。`})]}),(0,j.jsxs)(`label`,{className:`audit-switch`,children:[(0,j.jsx)(`input`,{"aria-label":`Inbox 巡检开关`,type:`checkbox`,checked:l.enabled,disabled:p,onChange:e=>u(t=>({...t,enabled:e.target.checked}))}),(0,j.jsx)(`span`,{"aria-hidden":`true`}),(0,j.jsx)(`b`,{children:l.enabled?`已开启`:`已关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`全局巡检间隔(分钟)`,type:`number`,min:Mi,max:Ni,step:`any`,inputMode:`decimal`,value:d,disabled:p,onChange:e=>f(e.target.value)})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`可在关闭时预先调整;保存间隔本身不会开启巡检。`})]}),(0,j.jsxs)(`div`,{className:`form-actions`,children:[(0,j.jsx)(_i,{onClick:()=>{o(r),u(s),f(Pi(s.intervalMs))},disabled:!_||p,children:`放弃草稿`}),(0,j.jsx)(_i,{className:`primary`,onClick:async()=>{let e=l.enabled!==s.enabled||d!==Pi(s.intervalMs),t=e?Fi(d):null;if(e&&t===null){g(`巡检间隔必须在 1 到 1440 分钟之间。`);return}let o=t??s.intervalMs;m(!0);try{let t=null;a!==r&&(t=await Di({operation:`set-global-mention`,value:a})),e&&(t=await Di({operation:`set-global-inbox-audit`,...l.enabled===s.enabled?{}:{enabled:l.enabled},...d===Pi(s.intervalMs)?{}:{intervalMs:o}})),i(a),c({...l,intervalMs:o}),u(e=>({...e,intervalMs:o})),f(Pi(o)),g(t?`已保存 · ${t.applyState} · ${t.revision.slice(0,20)}…`:`没有需要保存的修改`),n()}catch(e){g(e instanceof Error?e.message:String(e))}finally{m(!1)}},disabled:!_||p,children:p?`保存中…`:`保存全局设置`})]}),h?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:h}):null]})})}function P({config:e,onSave:t,disabled:n}){let[r,i]=(0,C.useState)(``),[a,o]=(0,C.useState)(``),[s,c]=(0,C.useState)(`require`),l=e.knownChats.filter(e=>e.kind===`group`&&e.override!==`inherit`).filter(e=>{let t=r.trim().toLocaleLowerCase();return!t||e.chatId.toLocaleLowerCase().includes(t)||String(e.displayName||`未解析群名`).toLocaleLowerCase().includes(t)});return(0,j.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,j.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,j.jsxs)(`label`,{className:`table-search`,children:[(0,j.jsx)(de,{size:14}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,j.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,j.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,j.jsxs)(`table`,{className:`policy-table`,children:[(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{children:`群`}),(0,j.jsx)(`th`,{children:`消息策略`}),(0,j.jsx)(`th`,{children:`操作`})]})}),(0,j.jsx)(`tbody`,{children:l.map(e=>(0,j.jsxs)(`tr`,{children:[(0,j.jsxs)(`td`,{children:[(0,j.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,j.jsx)(`code`,{children:e.chatId}):(0,j.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,j.jsx)(`td`,{children:(0,j.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,j.jsx)(`td`,{children:(0,j.jsx)(_i,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,j.jsx)(bi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,j.jsxs)(`div`,{className:`chat-add`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`完整 chat_id`}),(0,j.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`消息策略`}),(0,j.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,j.jsx)(_i,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function Bi(e){return[`Readiness ${e.state}`,e.reason,e.nextAction].filter(Boolean).join(` · `)}function Vi({agentId:e,readiness:t,onDirtyChange:n,refreshKey:r}){let[i,a]=(0,C.useState)(null),[o,s]=(0,C.useState)({}),[c,l]=(0,C.useState)({}),[u,d]=(0,C.useState)(!0),[f,p]=(0,C.useState)(null),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(xe()),_=(0,C.useRef)(xe()),v=!be(o,c);(0,C.useEffect)(()=>n(v),[v,n]),(0,C.useEffect)(()=>{let e=e=>{v&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[v]);let y=(0,C.useCallback)(async(t=!1)=>{let n=g.current.issue();d(!0);try{let r=await Oi(`/api/config?agent=${encodeURIComponent(e)}`);if(!g.current.accepts(n))return;let i=r.agents[0];if(!i)throw Error(`配置投影中没有当前 Agent`);let o=i.inboxAudit.override.intervalMs===`inherit`?i.inboxAudit.effective.intervalMs:i.inboxAudit.override.intervalMs,c={runtime:i.runtimeOption||i.runtime,model:i.model,effort:i.effort||`default`,mention:i.mention.override,auditEnabled:i.inboxAudit.override.enabled,auditIntervalInherited:i.inboxAudit.override.intervalMs===`inherit`,auditGapMinutes:Pi(o)};a(r),s(c),(!t||!v)&&l(c)}catch(e){g.current.accepts(n)&&p(e instanceof Error?e.message:String(e))}finally{g.current.accepts(n)&&d(!1)}},[e,v]);(0,C.useEffect)(()=>{y(!1)},[e]),(0,C.useEffect)(()=>{r>0&&y(!0)},[r]);let b=i?.agents[0],x=(e,t)=>l(n=>({...n,[e]:t})),S=String(c.runtime||b?.runtime||``),w=String(c.model||b?.model||`default`);(0,C.useEffect)(()=>{if(h({runtime:S,status:`loading`,models:[]}),!xi.includes(S)){h({runtime:S,status:`error`,models:[]});return}let t=_.current.issue(),n=new AbortController;return Oi(`/api/models/${S}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{_.current.accepts(t)&&h({runtime:S,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&_.current.accepts(t)&&(h({runtime:S,status:`error`,models:[]}),p(`${S} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,S]);let ee=m?.runtime===S&&m.status===`ready`,te=m?.runtime===S?m.status:`loading`,T=ee?m.models:[],E=`${w} · 当前配置(模型目录${te===`error`?`不可用`:`加载中`})`,ne=ee?T.some(e=>e.id===w)?T:[{id:w,label:`${w} · 当前配置`},...T]:[{id:w,label:E}],re=ee&&w!=="default"&&T.find(e=>e.id===w)?.supportedReasoningEfforts||[],ie=c.runtime!==o.runtime||c.model!==o.model||c.effort!==o.effort,ae=c.auditEnabled===`on`||c.auditEnabled===`off`?c.auditEnabled:`inherit`,oe=c.auditIntervalInherited===!0,se=String(c.auditGapMinutes||``),ce=c.auditEnabled!==o.auditEnabled||c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes,D=()=>Ti(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":Ci.csrfCapability},body:JSON.stringify({agentId:e})}),O=async()=>{if(!b)return;let t=Fi(c.auditGapMinutes);if(ce&&!oe&&t===null){p(`巡检间隔必须在 1 到 1440 分钟之间;草稿已保留。`);return}d(!0);try{let n=[];c.runtime===o.runtime?c.model!==o.model&&n.push({operation:`set-agent-model`,agentId:e,model:c.model}):n.push({operation:`set-agent-runtime`,agentId:e,runtime:c.runtime,model:c.model}),c.effort!==o.effort&&n.push({operation:`set-agent-effort`,agentId:e,effort:c.effort}),c.mention!==o.mention&&n.push({operation:`set-agent-mention`,agentId:e,value:c.mention}),ce&&n.push({operation:`set-agent-inbox-audit`,agentId:e,...c.auditEnabled===o.auditEnabled?{}:{enabled:ae===`inherit`?`inherit`:ae===`on`},...c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes?{intervalMs:oe?`inherit`:t}:{}});let r=null;for(let e of n)r=await Di(e);if(ie)try{await D(),p(`配置已保存并应用`)}catch(e){p(`配置已保存但未应用:${Ei(e,`稍后重试`)}`)}else p(r?`配置已保存`:`没有需要保存的修改`);await y(!1)}catch(e){p(Ei(e,e instanceof Error?e.message:String(e)))}finally{d(!1)}},le=async()=>{d(!0);try{await D(),p(`已应用保存的运行配置`),await y(!0)}catch(e){p(`配置保持已保存但未应用:${Ei(e,`稍后重试`)}`)}finally{d(!1)}},de=async(t,n)=>{d(!0);try{let r=await Di({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});p(`群策略已保存 · ${r.applyState}`),await y(!0)}catch(e){p(e instanceof Error?e.message:String(e))}finally{d(!1)}};if(!b&&u)return(0,j.jsx)(bi,{title:`加载 Agent 配置…`});if(!b)return(0,j.jsx)(bi,{title:`配置不可用`,detail:f||`请重试`});let fe=b.inboxAudit.effective,k=b.inboxAudit.source,A=`${k.enabled===`agent`?`当前 Agent 已单独设置`:k.enabled===`global`?`当前使用全局设置`:`当前使用默认设置`}:${fe.enabled?`已开启`:`已关闭`},每 ${Pi(fe.intervalMs)} 分钟`,pe=b.apply.applyState===`pending`?`待应用`:b.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,j.jsxs)(`div`,{className:`configuration-page`,children:[(0,j.jsxs)(`section`,{className:`config-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`Agent 配置`}),(0,j.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,j.jsx)(vi,{className:b.apply.applyState===`pending`?`warning`:`success`,children:pe})]}),(0,j.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,j.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),t?(0,j.jsx)(`p`,{className:`field-help`,children:Bi(t)}):null,(0,j.jsxs)(`div`,{className:`config-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Runtime`}),(0,j.jsx)(`select`,{value:S,disabled:u,onChange:e=>{x(`runtime`,e.target.value),x(`model`,`default`),x(`effort`,`default`)},children:(i?.runtimeOptions||xi).map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Model`}),(0,j.jsx)(`select`,{value:w,disabled:u||!ee,onChange:e=>{x(`model`,e.target.value),e.target.value==="default"&&x(`effort`,`default`)},children:ne.map(e=>(0,j.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Effort`}),(0,j.jsxs)(`select`,{value:String(c.effort||`default`),disabled:u||!ee||w==="default"||!re.length,onChange:e=>x(`effort`,e.target.value),children:[(0,j.jsx)(`option`,{value:`default`,children:`default · 不指定`}),re.map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`群消息策略`}),(0,j.jsxs)(`select`,{value:String(c.mention||`inherit`),disabled:u,onChange:e=>x(`mention`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`agent-inbox-audit-title`,children:[(0,j.jsx)(`div`,{className:`audit-heading`,children:(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`agent-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsxs)(`p`,{children:[A,`。`]})]})}),(0,j.jsxs)(`div`,{className:`config-grid audit-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检设置`}),(0,j.jsxs)(`select`,{"aria-label":`Inbox 巡检设置`,value:ae,disabled:u,onChange:e=>x(`auditEnabled`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`on`,children:`为此 Agent 开启`}),(0,j.jsx)(`option`,{value:`off`,children:`为此 Agent 关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`Agent 巡检间隔(分钟)`,type:`number`,min:Mi,max:Ni,step:`any`,inputMode:`decimal`,value:se,disabled:u,onChange:e=>l(t=>({...t,auditGapMinutes:e.target.value,auditIntervalInherited:!1}))})]})]}),(0,j.jsxs)(`div`,{className:`audit-inline-actions`,children:[(0,j.jsx)(`span`,{children:oe?`间隔跟随全局设置;编辑数字后会仅作用于此 Agent。`:`此 Agent 使用单独的巡检间隔。`}),oe?null:(0,j.jsx)(_i,{disabled:u,onClick:()=>l(e=>({...e,auditIntervalInherited:!0,auditGapMinutes:Pi(fe.intervalMs)})),children:`间隔跟随全局设置`})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`即使关闭巡检,也可保存间隔;间隔修改不会开启巡检。`})]}),v||b.apply.applyState===`pending`?(0,j.jsxs)(`div`,{className:`form-actions`,children:[v?(0,j.jsx)(_i,{disabled:u,onClick:()=>l(o),children:`放弃草稿`}):null,v?(0,j.jsx)(_i,{className:`primary`,disabled:u,onClick:O,children:u?`处理中…`:ie?`保存并应用`:`保存 Agent 配置`}):(0,j.jsxs)(_i,{className:`primary`,disabled:u,onClick:le,children:[(0,j.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,f?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:f}):null]}),(0,j.jsx)(P,{config:b,onSave:de,disabled:u})]})}function Hi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,j.jsxs)(`div`,{className:`overview-page`,children:[(0,j.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ie,{})}),(0,j.jsx)(`span`,{children:`连接`}),(0,j.jsx)(`strong`,{children:e.connection.state}),(0,j.jsx)(`small`,{children:e.connection.reason})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`执行状态`}),(0,j.jsx)(`strong`,{children:Ii(e)}),(0,j.jsx)(`small`,{children:e.lastActivity?ki(e.lastActivity.ageSec):`尚无活动`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(le,{})}),(0,j.jsx)(`span`,{children:`最近消息`}),(0,j.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,j.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${ki(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`上下文`}),(0,j.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,j.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`累计 Token`}),(0,j.jsx)(`strong`,{children:ji(t.cumulativeTokens)}),(0,j.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`最近一轮用量`}),(0,j.jsx)(`strong`,{children:ji(t.latestTokens)}),(0,j.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ue,{})}),(0,j.jsx)(`span`,{children:`Compaction`}),(0,j.jsx)(`strong`,{children:ji(n.count)}),(0,j.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${Ai(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,j.jsxs)(`div`,{className:`overview-sections`,children:[(0,j.jsxs)(`section`,{className:`content-section wide`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`最近动态`}),(0,j.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Ui,{items:e.feed.slice(0,10)})]}),(0,j.jsxs)(`section`,{className:`content-section`,children:[(0,j.jsx)(`h3`,{children:`运行摘要`}),(0,j.jsxs)(`dl`,{className:`facts`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Runtime`}),(0,j.jsx)(`dd`,{children:e.runtime})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Readiness`}),(0,j.jsx)(`dd`,{children:e.runtimeReadiness?`${e.runtimeReadiness.state}${e.runtimeReadiness.version?` · ${e.runtimeReadiness.version}`:``}${e.runtimeReadiness.executable?` · ${e.runtimeReadiness.executable}`:``}${e.runtimeReadiness.reason?` · ${e.runtimeReadiness.reason}`:``}${e.runtimeReadiness.nextAction?` · ${e.runtimeReadiness.nextAction}`:``}`:`unknown`})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Model`}),(0,j.jsx)(`dd`,{children:e.model})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Effort`}),(0,j.jsx)(`dd`,{children:e.effort||`default`})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`入站`}),(0,j.jsx)(`dd`,{children:e.inbound.state})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`提醒`}),(0,j.jsx)(`dd`,{children:e.activeReminders})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`指示灯`}),(0,j.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function Ui({items:e}){if(!e.length)return(0,j.jsx)(bi,{title:`还没有活动记录`});let t=e=>e.kind===`error`||e.state===`error`?`state-error`:e.kind===`deliver`?`state-deliver`:[`working`,`thinking`,`tool`].includes(String(e.state))?`state-active`:[`idle`,`online`].includes(String(e.state))?`state-idle`:`state-unknown`;return(0,j.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,j.jsxs)(`li`,{children:[(0,j.jsx)(`span`,{className:gi(`timeline-dot`,t(e))}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,j.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,j.jsx)(`time`,{children:Ai(e.at)})]})]},`${e.at}-${n}`))})}function Wi({agent:e}){return e.conversation.length?(0,j.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,j.jsxs)(`article`,{className:gi(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,j.jsxs)(`header`,{children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(vi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,j.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,j.jsx)(`time`,{children:Ai(t.at)})]}),(0,j.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,j.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,j.jsx)(bi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Gi({agent:e}){if(!e.remindersList.length)return(0,j.jsx)(bi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,j.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,j.jsxs)(`article`,{className:gi(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,j.jsx)(ne,{size:16}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,j.jsxs)(`p`,{children:[Ai(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,j.jsx)(vi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function Ki({agentId:e}){let[t,n]=(0,C.useState)(null),[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)(!0),[s,c]=(0,C.useState)(null),l=(0,C.useRef)(xe()),u=(0,C.useCallback)(async t=>{let r=l.current.issue();o(!0);try{let a=await Ti(`/api/workspace?agent=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`);if(!l.current.accepts(r))return;a.kind===`directory`?(n(a),i(null)):i(a),c(null)}catch(e){l.current.accepts(r)&&c(e instanceof Error?e.message:String(e))}finally{l.current.accepts(r)&&o(!1)}},[e]);(0,C.useEffect)(()=>{u(``)},[u]);let d=t?.path.split(`/`).filter(Boolean)||[];return(0,j.jsxs)(`div`,{className:`workspace-browser`,children:[(0,j.jsxs)(`div`,{className:`workspace-side`,children:[(0,j.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,j.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,j.jsxs)(`span`,{children:[(0,j.jsx)(re,{size:12}),(0,j.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,j.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,j.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,j.jsx)(se,{size:15}):(0,j.jsx)(oe,{size:15}),(0,j.jsx)(`span`,{children:e.name}),(0,j.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,j.jsx)(bi,{title:`空目录`}):null]})]}),(0,j.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,j.jsx)(bi,{title:`读取工作区…`}):s?(0,j.jsx)(bi,{title:`工作区读取失败`,detail:s}):r?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`header`,{children:[(0,j.jsx)(`strong`,{children:r.name}),(0,j.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,j.jsx)(bi,{title:`二进制文件不可预览`}):(0,j.jsx)(`pre`,{children:r.content})]}):(0,j.jsx)(bi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function qi({agent:e}){return(0,j.jsxs)(`section`,{className:`logs-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`结构化日志`}),(0,j.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Ui,{items:e.feed}),e.recentErrors.length?(0,j.jsxs)(`section`,{className:`error-stack`,children:[(0,j.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ae,{size:14}),(0,j.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,j.jsx)(`time`,{children:Ai(e.at)})]},`${e.at}-${t}`))]}):null]})}function Ji({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{},r=t.contextPercent==null?`上下文 ${e.session?.turns||0} turns`:`上下文 ${Math.round(Number(t.contextPercent))}% · ${ji(t.latestTokens)} / ${ji(t.contextWindow)}`;return(0,j.jsxs)(`header`,{className:`agent-header`,children:[(0,j.jsxs)(`div`,{className:`agent-title`,children:[(0,j.jsx)(Ri,{agent:e}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`p`,{children:e.agentId}),(0,j.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,j.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,j.jsx)(vi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,j.jsx)(`span`,{children:e.runtime}),(0,j.jsx)(`span`,{children:e.model}),(0,j.jsx)(`span`,{children:e.effort||`default effort`}),(0,j.jsx)(`span`,{children:r}),(0,j.jsxs)(`span`,{children:[`累计 `,ji(t.cumulativeTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`最近一轮 `,ji(t.latestTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`Compact `,ji(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,j.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?ki(e.lastDeliver.ageSec):`暂无`]}),(0,j.jsx)(`span`,{children:e.lastActivity?.detail||Ii(e)})]})]})}function Yi(){let e=Li(),t=(0,C.useMemo)(()=>ge(window.location.search),[]),[n,r]=(0,C.useState)(t.agentId),[i,a]=(0,C.useState)(t.tab),[o,s]=(0,C.useState)(!1),[c,l]=(0,C.useState)(!1),[u,d]=(0,C.useState)(0),[f,p]=(0,C.useState)(!1),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(new Map),_=e.data?.agents||[],v=e.data?.packageVersion||Ci.packageVersion,y=_.find(e=>e.agentId===n)||null,b=(0,C.useCallback)((e,t)=>{let n=`${e}:${t}`,r=window.scrollY;g.current.set(n,r);try{sessionStorage.setItem(`larkin-dashboard-scroll:${n}`,String(r))}catch{}},[]),x=(0,C.useCallback)(e=>{if(g.current.has(e))return g.current.get(e)||0;try{let t=Number(sessionStorage.getItem(`larkin-dashboard-scroll:${e}`));return Number.isFinite(t)&&t>=0?t:0}catch{return 0}},[]),S=(0,C.useCallback)((e,t,o=!1)=>{n&&b(n,i);let s=o?`replaceState`:`pushState`;window.history[s](null,``,_e(e,t)),r(e),a(t)},[b,n,i]),w=()=>!f||window.confirm(`Agent 配置尚未保存,确定放弃草稿吗?`),ee=e=>{w()&&(p(!1),S(e,i),l(!1))},te=e=>{e!==`configuration`&&!w()||(e!==`configuration`&&p(!1),S(n,e))};return(0,C.useEffect)(()=>{if(!e.data)return;let t=ve(n,e.data.agents);t!==n&&(h(n?`原 Agent 已不存在,已切换到第一个可用 Agent。`:null),S(t,i,!0))},[e.data,n,i,S]),(0,C.useEffect)(()=>{let e=()=>{let e=ge(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,_e(n,i));return}n&&b(n,i),p(!1),r(e.agentId),a(e.tab)};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[f,b,n,i]),(0,C.useLayoutEffect)(()=>{if(!n)return;let e=`${n}:${i}`,t=x(e);window.scrollY!==t&&window.scrollTo({top:t,behavior:`auto`})},[x,n,i]),(0,j.jsxs)(`div`,{className:gi(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,j.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,j.jsxs)(`div`,{className:`brand`,children:[(0,j.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,j.jsx)(zi,{agents:_,selectedId:n,onSelect:ee}),(0,j.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,j.jsxs)(`main`,{className:gi(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,j.jsxs)(`div`,{className:`app-topbar`,children:[(0,j.jsx)(_i,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,j.jsx)(O,{size:18})}),(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,j.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,j.jsx)(vi,{className:`warning`,children:`状态陈旧`}):null]}),(0,j.jsxs)(_i,{onClick:()=>s(!0),children:[(0,j.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,j.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,j.jsx)(A,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,j.jsx)(_i,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,j.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,j.jsx)(_i,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,j.jsxs)(`div`,{className:`page-loading`,children:[(0,j.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(Ji,{agent:y}),(0,j.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:he.map(e=>(0,j.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,j.jsx)(ce,{}):e===`conversation`?(0,j.jsx)(le,{}):e===`configuration`?(0,j.jsx)(k,{}):e===`reminders`?(0,j.jsx)(ne,{}):e===`workspace`?(0,j.jsx)(se,{}):(0,j.jsx)(D,{}),Si[e]]},e))}),(0,j.jsxs)(`div`,{className:gi(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,j.jsx)(Hi,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,j.jsx)(Wi,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,j.jsx)(Vi,{agentId:y.agentId,readiness:y.runtimeReadiness??null,onDirtyChange:p,refreshKey:u})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,j.jsx)(Gi,{agent:y})}),(0,j.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,j.jsx)(Ki,{agentId:y.agentId})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,j.jsx)(qi,{agent:y})})]},y.agentId)]}):(0,j.jsx)(bi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,j.jsx)(N,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,j.jsx)(yi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,j.jsx)(zi,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Xi=document.getElementById(`root`);if(!Xi)throw Error(`dashboard root is missing`);(0,me.createRoot)(Xi).render((0,j.jsx)(C.StrictMode,{children:(0,j.jsx)(Yi,{})}));
@@ -1,8 +1,61 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ProcessingEyeOrchestrator = void 0;
3
+ exports.ProcessingEyeOrchestrator = exports.BOT_TOPIC_ROOT_LIMIT = void 0;
4
+ exports.shouldShowProcessingEye = shouldShowProcessingEye;
5
+ exports.listBotTopicRoots = listBotTopicRoots;
6
+ exports.rememberBotTopicRoot = rememberBotTopicRoot;
7
+ exports.persistBotTopicRoot = persistBotTopicRoot;
8
+ exports.isBotStartedTopic = isBotStartedTopic;
4
9
  const node_child_process_1 = require("node:child_process");
5
10
  const agent_lark_cli_workspace_js_1 = require("../app/agent-lark-cli-workspace.cjs");
11
+ exports.BOT_TOPIC_ROOT_LIMIT = 256;
12
+ const BOT_TOPIC_ID = /^om[t]?_[A-Za-z0-9_-]+$/;
13
+ function isPrivateChat(input) {
14
+ const chatType = String(input.chatType || "").toLowerCase();
15
+ if (chatType === "p2p" || chatType === "dm")
16
+ return true;
17
+ if (String(input.channelType || "").toLowerCase() === "dm")
18
+ return true;
19
+ return String(input.parentChannelType || "").toLowerCase() === "dm";
20
+ }
21
+ /** OnIt 给私聊、入站 @bot(含 @all),或用户在 AI 消息下发起的话题里说话。群未@且非 AI 话题不点。 */
22
+ function shouldShowProcessingEye(input) {
23
+ if (isPrivateChat(input))
24
+ return true;
25
+ if (input.mentionedBot === true)
26
+ return true;
27
+ return input.botStartedTopic === true;
28
+ }
29
+ function listBotTopicRoots(state) {
30
+ if (!Array.isArray(state?.bot_topic_roots))
31
+ return [];
32
+ return state.bot_topic_roots.filter((id) => typeof id === "string" && BOT_TOPIC_ID.test(id));
33
+ }
34
+ function rememberBotTopicRoot(ids, candidate) {
35
+ const id = String(candidate || "");
36
+ if (!BOT_TOPIC_ID.test(id) || ids.includes(id))
37
+ return [...ids];
38
+ return [...ids, id].slice(-exports.BOT_TOPIC_ROOT_LIMIT);
39
+ }
40
+ /** 机器人成功发出的 `om_`/`omt_` 记为话题根,供后续「AI 发起的话题」判定,不做飞书回查。 */
41
+ function persistBotTopicRoot(store, candidate) {
42
+ const id = String(candidate || "");
43
+ if (!BOT_TOPIC_ID.test(id))
44
+ return;
45
+ store.mutateJson("freshnessState", { version: 1, cursors: {} }, (state) => {
46
+ state.bot_topic_roots = rememberBotTopicRoot(listBotTopicRoots(state), id);
47
+ });
48
+ }
49
+ function isBotStartedTopic(input) {
50
+ const threadId = String(input.threadId || "");
51
+ if (!threadId)
52
+ return false;
53
+ const owned = new Set(input.ownedIds || []);
54
+ if (owned.has(threadId))
55
+ return true;
56
+ const rootId = String(input.rootId || "");
57
+ return Boolean(rootId) && owned.has(rootId);
58
+ }
6
59
  function errorMessage(error) {
7
60
  return error instanceof Error ? error.message : String(error || "?");
8
61
  }
@@ -1,5 +1,53 @@
1
1
  import { execFile as nodeExecFile } from "node:child_process";
2
2
  import { managedOfficialLarkCli } from "../app/agent-lark-cli-workspace.mjs";
3
+ export const BOT_TOPIC_ROOT_LIMIT = 256;
4
+ const BOT_TOPIC_ID = /^om[t]?_[A-Za-z0-9_-]+$/;
5
+ function isPrivateChat(input) {
6
+ const chatType = String(input.chatType || "").toLowerCase();
7
+ if (chatType === "p2p" || chatType === "dm")
8
+ return true;
9
+ if (String(input.channelType || "").toLowerCase() === "dm")
10
+ return true;
11
+ return String(input.parentChannelType || "").toLowerCase() === "dm";
12
+ }
13
+ /** OnIt 给私聊、入站 @bot(含 @all),或用户在 AI 消息下发起的话题里说话。群未@且非 AI 话题不点。 */
14
+ export function shouldShowProcessingEye(input) {
15
+ if (isPrivateChat(input))
16
+ return true;
17
+ if (input.mentionedBot === true)
18
+ return true;
19
+ return input.botStartedTopic === true;
20
+ }
21
+ export function listBotTopicRoots(state) {
22
+ if (!Array.isArray(state?.bot_topic_roots))
23
+ return [];
24
+ return state.bot_topic_roots.filter((id) => typeof id === "string" && BOT_TOPIC_ID.test(id));
25
+ }
26
+ export function rememberBotTopicRoot(ids, candidate) {
27
+ const id = String(candidate || "");
28
+ if (!BOT_TOPIC_ID.test(id) || ids.includes(id))
29
+ return [...ids];
30
+ return [...ids, id].slice(-BOT_TOPIC_ROOT_LIMIT);
31
+ }
32
+ /** 机器人成功发出的 `om_`/`omt_` 记为话题根,供后续「AI 发起的话题」判定,不做飞书回查。 */
33
+ export function persistBotTopicRoot(store, candidate) {
34
+ const id = String(candidate || "");
35
+ if (!BOT_TOPIC_ID.test(id))
36
+ return;
37
+ store.mutateJson("freshnessState", { version: 1, cursors: {} }, (state) => {
38
+ state.bot_topic_roots = rememberBotTopicRoot(listBotTopicRoots(state), id);
39
+ });
40
+ }
41
+ export function isBotStartedTopic(input) {
42
+ const threadId = String(input.threadId || "");
43
+ if (!threadId)
44
+ return false;
45
+ const owned = new Set(input.ownedIds || []);
46
+ if (owned.has(threadId))
47
+ return true;
48
+ const rootId = String(input.rootId || "");
49
+ return Boolean(rootId) && owned.has(rootId);
50
+ }
3
51
  function errorMessage(error) {
4
52
  return error instanceof Error ? error.message : String(error || "?");
5
53
  }
@@ -600,10 +600,29 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
600
600
  at: new Date().toISOString(),
601
601
  }, 30);
602
602
  if (inboxEnvelope.sender_type === "human" || inboxEnvelope.sender_type === "agent") {
603
- processingEyes.add(agent, String(inboxEnvelope.message_id || ""), {
604
- replyInThread: String(inboxEnvelope.target || "").startsWith("thread:"),
605
- target: String(inboxEnvelope.target || ""),
603
+ // 私聊、入站 @bot,或 inbound root/thread 落在本 bot 已发出的话题根上才点 OnIt。
604
+ const botStartedTopic = (0, host_processing_eye_js_1.isBotStartedTopic)({
605
+ threadId: event.thread_id,
606
+ rootId: event.root_id,
607
+ ownedIds: (0, host_processing_eye_js_1.listBotTopicRoots)(stateStore(agent).readJson("freshnessState", {})),
606
608
  });
609
+ if ((0, host_processing_eye_js_1.shouldShowProcessingEye)({
610
+ chatType: event.chat_type,
611
+ channelType: typeof inboxEnvelope.channel_type === "string" ? inboxEnvelope.channel_type : undefined,
612
+ parentChannelType: typeof inboxEnvelope.parent_channel_type === "string" ? inboxEnvelope.parent_channel_type : undefined,
613
+ mentionedBot: event._mentioned_bot === true,
614
+ botStartedTopic,
615
+ })) {
616
+ if (botStartedTopic)
617
+ (0, host_processing_eye_js_1.persistBotTopicRoot)(stateStore(agent), event.thread_id);
618
+ processingEyes.add(agent, String(inboxEnvelope.message_id || ""), {
619
+ replyInThread: String(inboxEnvelope.target || "").startsWith("thread:"),
620
+ target: String(inboxEnvelope.target || ""),
621
+ });
622
+ }
623
+ else {
624
+ log(`👀 跳过群未@且非 AI 话题消息 agent=${agent.name} chat=${event.chat_id}`);
625
+ }
607
626
  }
608
627
  if (receipt.status === "deferred")
609
628
  log(`Runtime 暂缓投递,消息保留在 inbox seq=${inboxEnvelope.seq}: ${receipt.reason}`);
@@ -8,7 +8,7 @@ import { currentProcessMetadata } from "../platform/process-inspect.cjs";
8
8
  import { reconcileAgentWorkspace } from "../platform/workspace-service.mjs";
9
9
  import { createAgentStateStore } from "../agent/agent-state-store.mjs";
10
10
  import { HostEnvelopeProjector, HostStateProjection, SenderIdentityCache, projectActivityStatus, projectSessionStatus, safeConversationExcerpt, shouldPreventiveReconnect, } from "./host-business-state.mjs";
11
- import { ProcessingEyeOrchestrator } from "./host-processing-eye.mjs";
11
+ import { isBotStartedTopic, listBotTopicRoots, persistBotTopicRoot, ProcessingEyeOrchestrator, shouldShowProcessingEye, } from "./host-processing-eye.mjs";
12
12
  import { feishuImTarget, serializeFeishuImTarget } from "./im-freshness-adapter.mjs";
13
13
  import { projectInboxEnvelope, targetKeyOfInboxEnvelope } from "../agent/inbox-projection.mjs";
14
14
  import { HostReminderOrchestrator } from "../agent/host-reminder-orchestrator.mjs";
@@ -559,10 +559,29 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
559
559
  at: new Date().toISOString(),
560
560
  }, 30);
561
561
  if (inboxEnvelope.sender_type === "human" || inboxEnvelope.sender_type === "agent") {
562
- processingEyes.add(agent, String(inboxEnvelope.message_id || ""), {
563
- replyInThread: String(inboxEnvelope.target || "").startsWith("thread:"),
564
- target: String(inboxEnvelope.target || ""),
562
+ // 私聊、入站 @bot,或 inbound root/thread 落在本 bot 已发出的话题根上才点 OnIt。
563
+ const botStartedTopic = isBotStartedTopic({
564
+ threadId: event.thread_id,
565
+ rootId: event.root_id,
566
+ ownedIds: listBotTopicRoots(stateStore(agent).readJson("freshnessState", {})),
565
567
  });
568
+ if (shouldShowProcessingEye({
569
+ chatType: event.chat_type,
570
+ channelType: typeof inboxEnvelope.channel_type === "string" ? inboxEnvelope.channel_type : undefined,
571
+ parentChannelType: typeof inboxEnvelope.parent_channel_type === "string" ? inboxEnvelope.parent_channel_type : undefined,
572
+ mentionedBot: event._mentioned_bot === true,
573
+ botStartedTopic,
574
+ })) {
575
+ if (botStartedTopic)
576
+ persistBotTopicRoot(stateStore(agent), event.thread_id);
577
+ processingEyes.add(agent, String(inboxEnvelope.message_id || ""), {
578
+ replyInThread: String(inboxEnvelope.target || "").startsWith("thread:"),
579
+ target: String(inboxEnvelope.target || ""),
580
+ });
581
+ }
582
+ else {
583
+ log(`👀 跳过群未@且非 AI 话题消息 agent=${agent.name} chat=${event.chat_id}`);
584
+ }
566
585
  }
567
586
  if (receipt.status === "deferred")
568
587
  log(`Runtime 暂缓投递,消息保留在 inbox seq=${inboxEnvelope.seq}: ${receipt.reason}`);
@@ -36,6 +36,7 @@ function normalizeChannelMessage(message) {
36
36
  content: String(message.content || ""),
37
37
  create_time: message.createTime,
38
38
  thread_id: message.threadId || null,
39
+ root_id: message.rootId || null,
39
40
  _mentioned_bot: Boolean(message.mentionedBot || message.mentionAll),
40
41
  _mention_all: Boolean(message.mentionAll),
41
42
  _sender_is_bot: message.senderIsBot === true,
@@ -23,6 +23,7 @@ export function normalizeChannelMessage(message) {
23
23
  content: String(message.content || ""),
24
24
  create_time: message.createTime,
25
25
  thread_id: message.threadId || null,
26
+ root_id: message.rootId || null,
26
27
  _mentioned_bot: Boolean(message.mentionedBot || message.mentionAll),
27
28
  _mention_all: Boolean(message.mentionAll),
28
29
  _sender_is_bot: message.senderIsBot === true,
@@ -89,7 +89,7 @@ const TOP_FIELDS_V4 = new Set(["version", "serverId", "mentionPolicy", "activeAg
89
89
  const OPTIONAL_TOP_FIELDS_V4 = new Set(["inboxAudit"]);
90
90
  const AGENT_FIELDS_V3 = new Set(["runtime", "model", "effort", "noMentionChats", "createdAt"]);
91
91
  const AGENT_FIELDS_V4 = new Set(["runtime", "model", "effort", "mentionPolicy", "chatMentionPolicies", "inboxAudit", "createdAt"]);
92
- const LEGACY_AGENT_FIELDS = new Set(["piDistribution", "runtimeOption"]);
92
+ const LEGACY_AGENT_FIELDS = new Set(["piDistribution", "runtimeOption", "processingEye"]);
93
93
  const INBOX_AUDIT_FIELDS = new Set(["enabled", "intervalMs"]);
94
94
  const APP_ID = /^cli_[A-Za-z0-9]+$/;
95
95
  const CHAT_ID = /^oc_[A-Za-z0-9_-]+$/;
@@ -203,6 +203,15 @@ function resolvedInboxAudit(settings) {
203
203
  intervalMs: settings?.intervalMs ?? exports.DEFAULT_INBOX_AUDIT_INTERVAL_MS,
204
204
  };
205
205
  }
206
+ function dropRetiredProcessingEye(raw) {
207
+ delete raw.processingEye;
208
+ if (!isPlainObject(raw.agents))
209
+ return;
210
+ for (const agent of Object.values(raw.agents)) {
211
+ if (isPlainObject(agent))
212
+ delete agent.processingEye;
213
+ }
214
+ }
206
215
  function inboxAuditMutationFromCli(input) {
207
216
  if (input.scope === "global") {
208
217
  if (input.enabled !== "on" && input.enabled !== "off") {
@@ -359,6 +368,7 @@ function normalizeConfig(raw, configDir, { mint } = {}) {
359
368
  };
360
369
  if (!isPlainObject(raw))
361
370
  throw new Error("config 必须是普通 object");
371
+ dropRetiredProcessingEye(raw);
362
372
  const version = raw.version;
363
373
  if (version !== 3 && version !== 4)
364
374
  throw new Error("不支持该配置格式:larkin 只接受 version=3/4");
@@ -682,6 +692,8 @@ function agentSignature(config, agentId) {
682
692
  runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
683
693
  globalMentionPolicy: config.mentionPolicy, agentMentionPolicy: agent.mentionPolicy ?? null, chatMentionPolicies: chats,
684
694
  globalInboxAudit: config.inboxAudit ?? null, agentInboxAudit: agent.inboxAudit ?? null,
695
+ // 0.5.19 起不再有 processingEye 开关;固定 null 以免未配过该 key 的 Agent 升级后无意义 pending apply。
696
+ globalProcessingEye: null, agentProcessingEye: null,
685
697
  }));
686
698
  }
687
699
  function runtimeConfigSignature(config, agentId) {
@@ -16,7 +16,7 @@ const TOP_FIELDS_V4 = new Set(["version", "serverId", "mentionPolicy", "activeAg
16
16
  const OPTIONAL_TOP_FIELDS_V4 = new Set(["inboxAudit"]);
17
17
  const AGENT_FIELDS_V3 = new Set(["runtime", "model", "effort", "noMentionChats", "createdAt"]);
18
18
  const AGENT_FIELDS_V4 = new Set(["runtime", "model", "effort", "mentionPolicy", "chatMentionPolicies", "inboxAudit", "createdAt"]);
19
- const LEGACY_AGENT_FIELDS = new Set(["piDistribution", "runtimeOption"]);
19
+ const LEGACY_AGENT_FIELDS = new Set(["piDistribution", "runtimeOption", "processingEye"]);
20
20
  const INBOX_AUDIT_FIELDS = new Set(["enabled", "intervalMs"]);
21
21
  const APP_ID = /^cli_[A-Za-z0-9]+$/;
22
22
  const CHAT_ID = /^oc_[A-Za-z0-9_-]+$/;
@@ -130,6 +130,15 @@ function resolvedInboxAudit(settings) {
130
130
  intervalMs: settings?.intervalMs ?? DEFAULT_INBOX_AUDIT_INTERVAL_MS,
131
131
  };
132
132
  }
133
+ function dropRetiredProcessingEye(raw) {
134
+ delete raw.processingEye;
135
+ if (!isPlainObject(raw.agents))
136
+ return;
137
+ for (const agent of Object.values(raw.agents)) {
138
+ if (isPlainObject(agent))
139
+ delete agent.processingEye;
140
+ }
141
+ }
133
142
  export function inboxAuditMutationFromCli(input) {
134
143
  if (input.scope === "global") {
135
144
  if (input.enabled !== "on" && input.enabled !== "off") {
@@ -286,6 +295,7 @@ export function normalizeConfig(raw, configDir, { mint } = {}) {
286
295
  };
287
296
  if (!isPlainObject(raw))
288
297
  throw new Error("config 必须是普通 object");
298
+ dropRetiredProcessingEye(raw);
289
299
  const version = raw.version;
290
300
  if (version !== 3 && version !== 4)
291
301
  throw new Error("不支持该配置格式:larkin 只接受 version=3/4");
@@ -609,6 +619,8 @@ function agentSignature(config, agentId) {
609
619
  runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
610
620
  globalMentionPolicy: config.mentionPolicy, agentMentionPolicy: agent.mentionPolicy ?? null, chatMentionPolicies: chats,
611
621
  globalInboxAudit: config.inboxAudit ?? null, agentInboxAudit: agent.inboxAudit ?? null,
622
+ // 0.5.19 起不再有 processingEye 开关;固定 null 以免未配过该 key 的 Agent 升级后无意义 pending apply。
623
+ globalProcessingEye: null, agentProcessingEye: null,
612
624
  }));
613
625
  }
614
626
  export function runtimeConfigSignature(config, agentId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "larkin",
3
- "version": "0.5.17",
3
+ "version": "0.5.19",
4
4
  "private": false,
5
5
  "larkinPackageRole": "npm-published",
6
6
  "files": [