@roll-agent/core 0.12.0 → 0.13.0
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/dist/cli/chat/ink/app.js +1 -1
- package/dist/cli/chat/ink/history-item.js +1 -1
- package/dist/cli/chat/ink/live-region.js +1 -1
- package/dist/cli/chat/ink/markdown.d.ts +1 -0
- package/dist/cli/chat/ink/markdown.js +1 -1
- package/dist/cli/chat/ink/state.d.ts +8 -0
- package/dist/cli/chat/ink/state.js +1 -1
- package/dist/cli/chat/ink/status-line.d.ts +11 -0
- package/dist/cli/chat/ink/status-line.js +1 -1
- package/dist/cli/chat/ink/thinking-text.d.ts +1 -0
- package/dist/cli/chat/ink/thinking-text.js +1 -1
- package/dist/cli/utils/token-format.d.ts +5 -0
- package/dist/cli/utils/token-format.js +1 -1
- package/dist/llm/engine.d.ts +1 -1
- package/dist/llm/providers.d.ts +5 -5
- package/dist/llm/providers.js +1 -1
- package/dist/mcp/client-manager.d.ts +2 -2
- package/dist/mcp/sampling-handler.d.ts +2 -2
- package/dist/router/llm-router.d.ts +2 -2
- package/dist/tool-runtime/argument-extractor.d.ts +2 -2
- package/package.json +8 -8
package/dist/cli/chat/ink/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as i}from"node:crypto";import{createElement as t,useState as n}from"react";import{Box as o,useInput as e}from"ink";import{useSession as m}from"./use-session.js";import{HistoryItemView as s}from"./history-item.js";import{LiveRegion as r}from"./live-region.js";import{StatusLine as a}from"./status-line.js";import{TextPrompt as l}from"./text-prompt.js";import{ConfirmSelect as f}from"./confirm-select.js";import{SlashPopup as h}from"./slash-popup.js";import{filterCommands as p,SLASH_COMMANDS as d}from"./commands.js";import{cycleThinking as c}from"./thinking.js";function u(){return d.map(i=>`${i.name} — ${i.description}`).join("\n")}export function ChatApp(g){const{session:k,model:v,contextWindow:x,onUserSubmit:j,onExit:C}=g,{state:y,submit:L,compact:T,resolveConfirm:M,setDraft:w,setThinking:S,commitHistory:b}=m(k,{model:v,contextWindow:x,...g.initialHistory?{initialHistory:g.initialHistory}:{},...g.initialThinkingLevel?{initialThinkingLevel:g.initialThinkingLevel}:{},...g.onThinkingChange?{onThinkingChange:g.onThinkingChange}:{}}),[D,H]=n(0),$="idle"===y.phase&&y.draft.startsWith("/"),W=$?p(y.draft):[],A=Math.max(W.length-1,0),E=Math.min(D,A);e((i,t)=>{t.meta&&"."===i?S(c(y.status.thinkingLevel,1)):t.meta&&","===i&&S(c(y.status.thinkingLevel,-1))});const R="confirm"===y.phase&&void 0!==y.pendingConfirm?t(f,{prompt:y.pendingConfirm.prompt,onDecide:M}):t(l,{value:y.draft,disabled:"idle"!==y.phase,slashActive:$,onChange:w,onSubmit:i=>{const t=i.trim();0!==t.length?(j(t),L(t)):w("")},onSlashMove:i=>{H(t=>Math.min(Math.max(Math.min(t,A)+i,0),A))},onSlashComplete:()=>{const i=W[E];i&&(w(`${i.name} `),H(0))},onSlashRun:()=>{const t=y.draft.trim().split(/\s+/,1)[0]??"";(t=>{w("");const n=t.trim().split(/\s+/),o=n[0]??"",e=(n[1]??"").toLowerCase(),m=y.status.thinkingLevel;"/compact"!==o?"/think"!==o?"/effort"!==o?"/help"!==o?"/exit"!==o?b({kind:"notice",id:i(),text:`未知命令 ${o}`}):C():b({kind:"notice",id:i(),text:u()}):"low"===e||"medium"===e||"high"===e?S(e):b({kind:"notice",id:i(),text:"用法: /effort low | medium | high"}):S("off"===e?"off":"on"===e?"off"===m?"medium":m:"off"===m?"medium":"off"):T()})(d.some(i=>i.name===t)?y.draft:W[E]?.name??y.draft)}});return t(o,{flexDirection:"column"},t(o,{flexDirection:"column"},...y.history.map(i=>{const n="user"===i.kind||"assistant"===i.kind,e="tool"===i.kind;return t(o,{key:i.id,marginTop:n?1:0,marginLeft:e?2:0},t(s,{item:i}))})),t(r,{live:y.live}),t(a,{status:y.status}),$?t(h,{matches:W,selected:E}):null,R)}
|
|
1
|
+
import{randomUUID as i}from"node:crypto";import{createElement as t,useState as n}from"react";import{Box as o,useInput as e}from"ink";import{useSession as m}from"./use-session.js";import{HistoryItemView as s}from"./history-item.js";import{LiveRegion as r}from"./live-region.js";import{StatusLine as a}from"./status-line.js";import{TextPrompt as l}from"./text-prompt.js";import{ConfirmSelect as f}from"./confirm-select.js";import{SlashPopup as h}from"./slash-popup.js";import{filterCommands as p,SLASH_COMMANDS as d}from"./commands.js";import{cycleThinking as c}from"./thinking.js";function u(){return d.map(i=>`${i.name} — ${i.description}`).join("\n")}export function ChatApp(g){const{session:k,model:v,contextWindow:x,onUserSubmit:j,onExit:C}=g,{state:y,submit:L,compact:T,resolveConfirm:M,setDraft:w,setThinking:S,commitHistory:b}=m(k,{model:v,contextWindow:x,...g.initialHistory?{initialHistory:g.initialHistory}:{},...g.initialThinkingLevel?{initialThinkingLevel:g.initialThinkingLevel}:{},...g.onThinkingChange?{onThinkingChange:g.onThinkingChange}:{}}),[D,H]=n(0),$="idle"===y.phase&&y.draft.startsWith("/"),W=$?p(y.draft):[],A=Math.max(W.length-1,0),E=Math.min(D,A);e((i,t)=>{t.meta&&"."===i?S(c(y.status.thinkingLevel,1)):t.meta&&","===i&&S(c(y.status.thinkingLevel,-1))});const R="confirm"===y.phase&&void 0!==y.pendingConfirm?t(f,{prompt:y.pendingConfirm.prompt,onDecide:M}):t(l,{value:y.draft,disabled:"idle"!==y.phase,slashActive:$,onChange:w,onSubmit:i=>{const t=i.trim();0!==t.length?(j(t),L(t)):w("")},onSlashMove:i=>{H(t=>Math.min(Math.max(Math.min(t,A)+i,0),A))},onSlashComplete:()=>{const i=W[E];i&&(w(`${i.name} `),H(0))},onSlashRun:()=>{const t=y.draft.trim().split(/\s+/,1)[0]??"";(t=>{w("");const n=t.trim().split(/\s+/),o=n[0]??"",e=(n[1]??"").toLowerCase(),m=y.status.thinkingLevel;"/compact"!==o?"/think"!==o?"/effort"!==o?"/help"!==o?"/exit"!==o?b({kind:"notice",id:i(),text:`未知命令 ${o}`}):C():b({kind:"notice",id:i(),text:u()}):"low"===e||"medium"===e||"high"===e?S(e):b({kind:"notice",id:i(),text:"用法: /effort low | medium | high"}):S("off"===e?"off":"on"===e?"off"===m?"medium":m:"off"===m?"medium":"off"):T()})(d.some(i=>i.name===t)?y.draft:W[E]?.name??y.draft)}});return t(o,{flexDirection:"column"},t(o,{flexDirection:"column"},...y.history.map(i=>{const n="user"===i.kind||"assistant"===i.kind,e="tool"===i.kind||"denied"===i.kind;return t(o,{key:i.id,marginTop:n?1:0,marginLeft:e?2:0},t(s,{item:i}))})),t(r,{live:y.live}),t(a,{status:y.status}),$?t(h,{matches:W,selected:E}):null,R)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createElement as
|
|
1
|
+
import{createElement as t}from"react";import{Box as r,Text as o}from"ink";import{parseThinking as e}from"./thinking-text.js";import{Markdown as n}from"./markdown.js";import{ToolLabel as l}from"./tool-label.js";const i=["已取消执行","策略拒绝执行"];function c(t){const r=t.trimStart();return i.some(t=>r.startsWith(t))}export function HistoryItemView({item:i}){switch(i.kind){case"user":return t(r,null,t(o,{color:"cyan",bold:!0},"▌ "),t(o,{color:"cyan"},i.text));case"assistant":return c(i.text)?t(r,null,t(o,{dimColor:!0},"⊘ "),t(o,{dimColor:!0},i.text.trim())):t(r,{flexDirection:"column"},...e(i.text).map((r,e)=>r.thinking?t(o,{key:String(e),dimColor:!0},r.text):t(n,{key:String(e),text:r.text})));case"tool":{const r=i.args.length>0&&"{}"!==i.args?` ${i.args}`:"";return t(o,null,t(o,i.ok?{color:"green"}:{color:"red"},i.ok?"✓ ":"✗ "),t(l,{name:i.name}),r.length>0?t(o,{dimColor:!0},r):null)}case"denied":return t(o,{dimColor:!0},`⊘ ${i.name} ${i.label}`);case"compaction":return t(o,{dimColor:!0},i.notice);case"notice":return t(r,null,t(o,{color:"yellow"},"⚠ "),t(o,{color:"yellow"},i.text));case"error":return t(r,null,t(o,{color:"red"},"✗ "),t(o,{color:"red"},i.message));default:return t(o,null,"")}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createElement as
|
|
1
|
+
import{createElement as n}from"react";import{Box as l,Text as t}from"ink";import{Spinner as e}from"./spinner.js";import{ThinkingText as o}from"./thinking-text.js";import{ToolLabel as i}from"./tool-label.js";export function LiveRegion({live:r}){return n(l,{flexDirection:"column"},r.streamingText.length>0?n(o,{text:r.thinkTagOpen?`<think>${r.streamingText}`:r.streamingText}):null,r.thinking?n(l,{marginTop:1},n(e,null),n(t,{dimColor:!0}," 思考中…")):null,...r.activeTools.map(o=>n(l,{key:o.toolCallId},n(e,null),n(t,null," "),n(i,{name:o.name}),o.args.length>0?n(t,{dimColor:!0},` ${o.args}`):null)),r.compacting?n(l,null,n(e,null),n(t,{dimColor:!0}," 压缩上下文中…")):null)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createElement as e}from"react";import{Box as t,Text as r}from"ink";import{marked as n}from"marked";const o={"&":"&","<":"<",">":">",""":'"',"'":"'","'":"'"};function c(e){return e.replace(/&(?:amp|lt|gt|quot|#39|#x27);/g,e=>o[e]??e)}function a(t,n){return void 0===t?[]:t.map((t,o)=>{const
|
|
1
|
+
import{createElement as e}from"react";import{Box as t,Text as r}from"ink";import{marked as n}from"marked";const o={"&":"&","<":"<",">":">",""":'"',"'":"'","'":"'"};function c(e){return e.replace(/&(?:amp|lt|gt|quot|#39|#x27);/g,e=>o[e]??e)}function a(t,n){return void 0===t?[]:t.map((t,o)=>{const s=`${n}-${String(o)}`;switch(t.type){case"strong":return e(r,{key:s,bold:!0},...a(t.tokens,s));case"em":return e(r,{key:s,italic:!0},...a(t.tokens,s));case"codespan":return e(r,{key:s,color:"yellow"},c(t.text));case"del":return e(r,{key:s,strikethrough:!0},...a(t.tokens,s));case"link":return e(r,{key:s},e(r,{color:"cyan",underline:!0},t.text),e(r,{dimColor:!0},` (${t.href})`));case"br":return"\n";case"text":return void 0!==t.tokens?e(r,{key:s},...a(t.tokens,s)):c(t.text);default:return c(t.raw)}})}const s=[[4352,4447],[9001,9002],[9989,9989],[10060,10062],[11035,11093],[11904,12350],[12353,13311],[13312,19903],[19968,40959],[40960,42191],[44032,55203],[63744,64255],[65040,65049],[65072,65135],[65280,65376],[65504,65510],[126976,129791],[131072,196605],[196608,262141]],i=[[768,879],[8203,8207],[65024,65039]];function u(e,t){return t.some(([t,r])=>e>=t&&e<=r)}export function displayWidth(e){let t=0;for(const r of e){const e=r.codePointAt(0)??0;u(e,i)||(t+=u(e,s)?2:1)}return t}function l(e){return void 0===e?"":e.map(e=>{switch(e.type){case"strong":case"em":case"del":return l(e.tokens);case"codespan":return c(e.text);case"link":return`${e.text} (${e.href})`;case"br":return"\n";case"text":return void 0!==e.tokens?l(e.tokens):c(e.text);default:return c(e.raw)}}).join("")}function k(e){return l(e.tokens).split("\n").reduce((e,t)=>Math.max(e,displayWidth(t)),0)}function d(n,o,c,s){return e(t,{key:c},...n.map((n,i)=>e(t,{key:`${c}-${String(i)}`,width:(o[i]??0)+2},e(r,s?{bold:!0}:{},...a(n.tokens,`${c}-${String(i)}`)))))}function m(n,o){const c=n.header.map((e,t)=>{let r=k(e);for(const e of n.rows){const n=e[t];void 0!==n&&(r=Math.max(r,k(n)))}return r}),a=e(t,{key:`${o}-sep`},...c.map((n,c)=>e(t,{key:`${o}-sep-${String(c)}`,width:n+2},e(r,{dimColor:!0},"─".repeat(n)))));return e(t,{key:o,flexDirection:"column"},d(n.header,c,`${o}-h`,!0),a,...n.rows.map((e,t)=>d(e,c,`${o}-r${String(t)}`,!1)))}function p(n,o){switch(n.type){case"space":return null;case"heading":return e(r,{key:o,bold:!0,color:"cyan"},...a(n.tokens,o));case"paragraph":return e(r,{key:o},...a(n.tokens,o));case"blockquote":return e(t,{key:o},e(r,{color:"gray"},"│ "),e(t,{flexDirection:"column"},...y(n.tokens??[],o)));case"code":return e(t,{key:o,paddingLeft:2},e(r,{dimColor:!0},n.text));case"list":return e(t,{key:o,flexDirection:"column"},...n.items.map((c,a)=>{const s=`${o}-${String(a)}`,i=n.ordered?`${String(Number(n.start||1)+a)}. `:"• ";return e(t,{key:s},e(r,{color:"cyan"},i),e(t,{flexDirection:"column"},...y(c.tokens,s)))}));case"hr":return e(r,{key:o,dimColor:!0},"────────");case"table":return m(n,o);case"text":return void 0!==n.tokens?e(r,{key:o},...a(n.tokens,o)):e(r,{key:o},c(n.text));default:return e(r,{key:o},n.raw)}}function y(r,n){return r.map((e,t)=>p(e,`${n}-${String(t)}`)).filter(e=>null!==e).map((r,o)=>0===o?r:e(t,{key:`${n}-gap-${String(o)}`,marginTop:1},r))}export function Markdown({text:o}){let c;try{c=n.lexer(o)}catch{return e(r,null,o)}return e(t,{flexDirection:"column"},...y(c,"md"))}
|
|
@@ -19,6 +19,11 @@ export type HistoryItem = {
|
|
|
19
19
|
readonly name: string;
|
|
20
20
|
readonly args: string;
|
|
21
21
|
readonly ok: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: "denied";
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly label: string;
|
|
22
27
|
} | {
|
|
23
28
|
readonly kind: "compaction";
|
|
24
29
|
readonly id: string;
|
|
@@ -35,6 +40,7 @@ export type HistoryItem = {
|
|
|
35
40
|
export interface LiveState {
|
|
36
41
|
readonly streamingText: string;
|
|
37
42
|
readonly thinking: boolean;
|
|
43
|
+
readonly thinkTagOpen: boolean;
|
|
38
44
|
readonly activeTools: readonly ToolRowState[];
|
|
39
45
|
readonly compacting: boolean;
|
|
40
46
|
readonly producedOutput: boolean;
|
|
@@ -45,6 +51,7 @@ export interface StatusState {
|
|
|
45
51
|
readonly turnUsage: SessionTokenUsage | undefined;
|
|
46
52
|
readonly sessionUsage: SessionTokenUsage | undefined;
|
|
47
53
|
readonly contextInputTokens: number | undefined;
|
|
54
|
+
readonly outputTokensPerSecond: number | undefined;
|
|
48
55
|
readonly thinkingLevel: ThinkingLevel;
|
|
49
56
|
}
|
|
50
57
|
export type ChatPhase = "idle" | "busy" | "confirm";
|
|
@@ -92,4 +99,5 @@ export declare function createInitialState(model: string, contextWindow: number
|
|
|
92
99
|
export declare function buildConfirmPrompt(event: Extract<SessionEvent, {
|
|
93
100
|
type: "confirmation-required";
|
|
94
101
|
}>): string;
|
|
102
|
+
export declare function denialLabel(output: unknown): string | undefined;
|
|
95
103
|
export declare function chatReducer(state: ChatUiState, action: ChatUiAction): ChatUiState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{formatToolInput as t}from"../../utils/tool-format.js";const
|
|
1
|
+
import{formatToolInput as t}from"../../utils/tool-format.js";import{endsInsideThink as e}from"./thinking-text.js";const i={streamingText:"",thinking:!1,thinkTagOpen:!1,activeTools:[],compacting:!1,producedOutput:!1};function n(t,e){return e?`<think>${t}`:t}export function createInitialState(t,e,n){return{history:n?.history??[],draft:"",live:i,status:{model:t,contextWindow:e,turnUsage:void 0,sessionUsage:void 0,contextInputTokens:void 0,outputTokensPerSecond:void 0,thinkingLevel:n?.thinkingLevel??"medium"},phase:"idle",pendingConfirm:void 0}}export function buildConfirmPrompt(t){const e=t.reason?`(${t.reason})`:"";return`执行 ${t.agentName}.${t.toolName}${e}?`}function o(t){const e="auto"===t.reason?"自动压缩":"手动压缩",i=t.truncatedTools?`,精简 ${String(t.truncatedTools)} 个工具结果`:"";return 0!==t.removed||t.truncatedTools?`🗜 ${e}(${t.strategy}):移除 ${String(t.removed)} 条 → 保留 ${String(t.kept)} 条${i}`:`🗜 ${e}:无需压缩`}const r=[["已取消执行","已取消"],["策略拒绝执行","策略拒绝"]];function s(t){return"string"==typeof t?t:"object"==typeof t&&null!==t&&"output"in t&&"string"==typeof t.output?t.output:void 0}export function denialLabel(t){const e=s(t);if(void 0!==e)return r.find(([t])=>e.startsWith(t))?.[1]}function a(t,e,i){const n=t.live.activeTools.find(t=>t.toolCallId===i.toolCallId),o=n?.name??`${i.agentName}.${i.toolName}`,r=n?.args??"",s=i.isError?denialLabel(i.output):void 0,a=void 0!==s?{kind:"denied",id:e,name:o,label:s}:{kind:"tool",id:e,name:o,args:r,ok:!i.isError};return{...t,history:[...t.history,a],live:{...t.live,activeTools:t.live.activeTools.filter(t=>t.toolCallId!==i.toolCallId)}}}function l(r,s,l){switch(l.type){case"message-start":return{...r,live:{...r.live,thinking:!0}};case"text-delta":return{...r,live:{...r.live,thinking:!1,producedOutput:!0,streamingText:r.live.streamingText+l.delta}};case"tool-call":{const i=r.live.streamingText.length>0?[{kind:"assistant",id:s,text:n(r.live.streamingText,r.live.thinkTagOpen)}]:[];return{...r,history:[...r.history,...i],live:{...r.live,thinking:!1,producedOutput:!0,streamingText:"",thinkTagOpen:e(r.live.streamingText,r.live.thinkTagOpen),activeTools:[...r.live.activeTools,{toolCallId:l.toolCallId,name:`${l.agentName}.${l.toolName}`,args:t(l.input)}]}}}case"tool-result":return a(r,s,l);case"confirmation-required":return{...r,phase:"confirm",pendingConfirm:{approvalId:l.approvalId,prompt:buildConfirmPrompt(l)}};case"compaction-start":return{...r,live:{...r.live,compacting:!0}};case"context-compacted":return{...r,live:{...r.live,compacting:!1},history:[...r.history,{kind:"compaction",id:s,notice:o(l)}]};case"message-finish":{const t=[];return r.live.streamingText.length>0?t.push({kind:"assistant",id:s,text:n(r.live.streamingText,r.live.thinkTagOpen)}):!r.live.producedOutput&&(l.totalUsage?.outputTokens??0)>0&&t.push({kind:"notice",id:s,text:"模型本轮只返回了 thinking/reasoning,没有生成可见回复"}),l.stoppedAtStepLimit&&t.push({kind:"notice",id:`${s}-step-limit`,text:"已达单轮最大工具步数,任务可能未完成 — 继续追问即可接着做,或调高 runtime.max-steps"}),{...r,history:[...r.history,...t],live:{...i},status:{...r.status,turnUsage:l.totalUsage,sessionUsage:l.sessionUsage,contextInputTokens:l.contextInputTokens,outputTokensPerSecond:l.outputTokensPerSecond}}}case"error":return{...r,history:[...r.history,{kind:"error",id:s,message:l.message}],live:{...i}};default:return r}}export function chatReducer(t,e){switch(e.type){case"submit-user":return{...t,history:[...t.history,{kind:"user",id:e.id,text:e.text}],draft:"",live:{...i},phase:"busy",pendingConfirm:void 0};case"set-draft":return{...t,draft:e.value};case"set-thinking":return{...t,status:{...t.status,thinkingLevel:e.level}};case"commit-history":return{...t,history:[...t.history,e.item],draft:""};case"start-compaction":return{...t,live:{...i},phase:"busy",pendingConfirm:void 0};case"session-event":return l(t,e.id,e.event);case"confirm-resolved":return{...t,phase:"busy",pendingConfirm:void 0};case"turn-end":return{...t,phase:"idle",live:{...i}};default:return t}}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
2
|
import type { StatusState } from "./state.ts";
|
|
3
|
+
interface SegmentProps {
|
|
4
|
+
readonly color?: string;
|
|
5
|
+
readonly dimColor?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface StatusSegmentView {
|
|
8
|
+
readonly key: string;
|
|
9
|
+
readonly text: string;
|
|
10
|
+
readonly props: SegmentProps;
|
|
11
|
+
}
|
|
12
|
+
export declare function composeStatusSegments(status: StatusState, width: number): StatusSegmentView[];
|
|
3
13
|
export declare function StatusLine({ status }: {
|
|
4
14
|
status: StatusState;
|
|
5
15
|
}): ReactElement;
|
|
16
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createElement as o}from"react";import{Box as
|
|
1
|
+
import{createElement as o}from"react";import{Box as t,Text as e,useStdout as s}from"ink";import{computeUsageParts as n,contextPressure as i,formatContextUsage as p,formatThroughput as r,formatTokens as c,formatTurnUsage as l}from"../../utils/token-format.js";import{displayWidth as m}from"./markdown.js";import{thinkingLabel as u}from"./thinking.js";const k={ok:{dimColor:!0},warn:{color:"yellow"},critical:{color:"red"}},a={off:"off",low:"low",medium:"med",high:"high"},d=["tps","session","turn","think"],f=" · ";export function composeStatusSegments(o,t){const e=n(o.turnUsage,o.sessionUsage,o.contextWindow,o.contextInputTokens),s=[{key:"model",full:o.model,compact:o.model,props:{color:"magenta"}},{key:"think",full:u(o.thinkingLevel),compact:`🧠 ${a[o.thinkingLevel]}`,props:"off"===o.thinkingLevel?{color:"yellow"}:{dimColor:!0}}],h=p(e);void 0!==h&&void 0!==e.usedTokens&&void 0!==e.contextWindow&&s.push({key:"ctx",full:h,compact:`ctx ${c(e.usedTokens)}/${c(e.contextWindow)}`,props:k[i(e.percentLeft)]});const y=l(e);if(void 0!==y){const o=[];void 0!==e.inputTokens&&o.push(`↑${c(e.inputTokens)}`),void 0!==e.outputTokens&&o.push(`↓${c(e.outputTokens)}`),s.push({key:"turn",full:y,compact:o.join(" "),props:{dimColor:!0}})}const g=r(o.outputTokensPerSecond);void 0!==g&&s.push({key:"tps",full:g,compact:g.replace(" tok/s","t/s"),props:{dimColor:!0}}),void 0!==e.sessionTokens&&s.push({key:"session",full:`session ${c(e.sessionTokens)}`,compact:`Σ${c(e.sessionTokens)}`,props:{dimColor:!0}});const v=(o,t)=>o.map(o=>({key:o.key,text:o[t],props:o.props})),w=o=>m(o.map(o=>o.text).join(f))<=t,x=v(s,"full");if(w(x))return x;let T=s,$=v(T,"compact");for(const o of d){if(w($))break;T=T.filter(t=>t.key!==o),$=v(T,"compact")}return $}export function StatusLine({status:n}){const{stdout:i}=s(),p=i.columns??80,r=composeStatusSegments(n,p);return o(t,{width:p},o(e,{wrap:"truncate-end"},...r.flatMap((t,s)=>[...s>0?[o(e,{key:`${t.key}-sep`,dimColor:!0},f)]:[],o(e,{key:t.key,...t.props},t.text)])))}
|
|
@@ -4,6 +4,7 @@ export interface TextSegment {
|
|
|
4
4
|
readonly thinking: boolean;
|
|
5
5
|
}
|
|
6
6
|
export declare function parseThinking(text: string): TextSegment[];
|
|
7
|
+
export declare function endsInsideThink(text: string, startInside: boolean): boolean;
|
|
7
8
|
export declare function ThinkingText({ text }: {
|
|
8
9
|
text: string;
|
|
9
10
|
}): ReactElement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createElement as t}from"react";import{Text as n}from"ink";export function parseThinking(t){const n=[],e=/<think>([\s\S]*?)(?:<\/think>|$)/gi;let
|
|
1
|
+
import{createElement as t}from"react";import{Text as n}from"ink";export function parseThinking(t){const n=[],e=t.toLowerCase(),i=e.indexOf("<think>"),r=e.indexOf("</think>");let l=t;r>=0&&(i<0||r<i)&&(n.push({text:t.slice(0,r),thinking:!0}),l=t.slice(r+8));const h=/<think>([\s\S]*?)(?:<\/think>|$)/gi;let s=0,o=h.exec(l);for(;null!==o;)o.index>s&&n.push({text:l.slice(s,o.index),thinking:!1}),n.push({text:o[1]??"",thinking:!0}),s=h.lastIndex,o=h.exec(l);return s<l.length&&n.push({text:l.slice(s),thinking:!1}),n.filter(t=>t.text.length>0)}export function endsInsideThink(t,n){let e=n;const i=/<\/?think>/gi;let r=i.exec(t);for(;null!==r;)e=!r[0].startsWith("</"),r=i.exec(t);return e}export function ThinkingText({text:e}){const i=parseThinking(e);return 0===i.length?t(n,null,""):t(n,null,...i.map((e,i)=>t(n,{key:String(i),...e.thinking?{dimColor:!0}:{}},e.text)))}
|
|
@@ -4,6 +4,7 @@ export interface UsageLineParts {
|
|
|
4
4
|
readonly outputTokens?: number;
|
|
5
5
|
readonly sessionTokens?: number;
|
|
6
6
|
readonly cachedInputTokens?: number;
|
|
7
|
+
readonly cacheWriteTokens?: number;
|
|
7
8
|
readonly reasoningTokens?: number;
|
|
8
9
|
readonly percentLeft?: number;
|
|
9
10
|
readonly usedTokens?: number;
|
|
@@ -15,4 +16,8 @@ export interface UsagePartsOptions {
|
|
|
15
16
|
export declare function formatTokens(value: number): string;
|
|
16
17
|
export declare function sessionTotal(usage: SessionTokenUsage): number;
|
|
17
18
|
export declare function computeUsageParts(turn: SessionTokenUsage | undefined, session: SessionTokenUsage | undefined, contextWindow: number | undefined, contextInputTokens: number | undefined, options?: UsagePartsOptions): UsageLineParts;
|
|
19
|
+
export declare function formatTurnUsage(parts: UsageLineParts): string | undefined;
|
|
20
|
+
export declare function formatContextUsage(parts: UsageLineParts): string | undefined;
|
|
21
|
+
export declare function formatThroughput(tokensPerSecond: number | undefined): string | undefined;
|
|
22
|
+
export declare function contextPressure(percentLeft: number | undefined): "ok" | "warn" | "critical";
|
|
18
23
|
export declare function formatUsageLine(parts: UsageLineParts): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const o=12e3,n=.2;export function formatTokens(o){return o>=1e3?`${(o/1e3).toFixed(1)}k`:String(o)}export function sessionTotal(o){return void 0!==o.totalTokens&&o.totalTokens>0?o.totalTokens:(o.inputTokens??0)+(o.outputTokens??0)}function e(o,e
|
|
1
|
+
const o=12e3,n=.2;function t(o){return o.endsWith(".0")?o.slice(0,-2):o}export function formatTokens(o){return o>=1e6?`${t((o/1e6).toFixed(1))}M`:o>=1e3?`${t((o/1e3).toFixed(1))}k`:String(o)}export function sessionTotal(o){return void 0!==o.totalTokens&&o.totalTokens>0?o.totalTokens:(o.inputTokens??0)+(o.outputTokens??0)}function e(o,t,e){const s=Math.min(e,Math.floor(t*n)),i=Math.max(t-s,1),r=Math.max(o-s,0),u=Math.max(i-r,0);return Math.min(Math.max(Math.round(u/i*100),0),100)}export function computeUsageParts(n,t,s,i,r){const u=i??n?.inputTokens,a=r?.baselineHeadroom??o,c=n?.cachedInputTokens,T=n?.cacheWriteTokens,k=n?.reasoningTokens;return{...void 0!==n?.inputTokens?{inputTokens:n.inputTokens}:{},...void 0!==n?.outputTokens?{outputTokens:n.outputTokens}:{},...t?{sessionTokens:sessionTotal(t)}:{},...void 0!==c&&c>0?{cachedInputTokens:c}:{},...void 0!==T&&T>0?{cacheWriteTokens:T}:{},...void 0!==k&&k>0?{reasoningTokens:k}:{},...void 0!==s&&void 0!==u?{percentLeft:e(u,s,a),usedTokens:u,contextWindow:s}:{}}}export function formatTurnUsage(o){const n=[];if(void 0!==o.inputTokens){const t=[];void 0!==o.cachedInputTokens&&t.push(`+${formatTokens(o.cachedInputTokens)} cached`),void 0!==o.cacheWriteTokens&&t.push(`+${formatTokens(o.cacheWriteTokens)} cache-write`);const e=t.length>0?` (${t.join(", ")})`:"";n.push(`in ${formatTokens(o.inputTokens)}${e}`)}if(void 0!==o.outputTokens){const t=void 0!==o.reasoningTokens?` (+${formatTokens(o.reasoningTokens)} reasoning)`:"";n.push(`out ${formatTokens(o.outputTokens)}${t}`)}return n.length>0?`turn ${n.join(" ")}`:void 0}export function formatContextUsage(o){if(void 0!==o.percentLeft&&void 0!==o.usedTokens&&void 0!==o.contextWindow)return`ctx ${formatTokens(o.usedTokens)}/${formatTokens(o.contextWindow)} (${String(o.percentLeft)}% left)`}export function formatThroughput(o){if(void 0===o||!Number.isFinite(o)||o<=0)return;const n=o>=10?Math.round(o):o.toFixed(1);return`${String(n)} tok/s`}export function contextPressure(o){return void 0===o||o>25?"ok":o>10?"warn":"critical"}export function formatUsageLine(o){const n=[],t=formatTurnUsage(o);void 0!==t&&n.push(t),void 0!==o.sessionTokens&&n.push(`session ${formatTokens(o.sessionTokens)}`);const e=formatContextUsage(o);return void 0!==e&&n.push(e),n.length>0?`↳ ${n.join(" · ")}`:void 0}
|
package/dist/llm/engine.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare class LLMEngine {
|
|
|
13
13
|
/**
|
|
14
14
|
* 生成文本(非流式)。
|
|
15
15
|
*
|
|
16
|
-
* 使用 AI SDK
|
|
16
|
+
* 使用 AI SDK v7 的 generateText,支持 Anthropic provider。
|
|
17
17
|
*/
|
|
18
18
|
generateText(prompt: string, options?: LLMEngineOptions): Promise<string>;
|
|
19
19
|
/** 解析 provider + model,创建 AI SDK LanguageModel 实例 */
|
package/dist/llm/providers.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LanguageModelV4, SharedV4ProviderOptions } from "@ai-sdk/provider";
|
|
2
2
|
import { runtimeThinkingLevels } from "../config/schema.ts";
|
|
3
3
|
export type ThinkingLevel = (typeof runtimeThinkingLevels)[number];
|
|
4
|
-
export declare function thinkingProviderOptions(providerName: string, modelName: string, level: ThinkingLevel):
|
|
4
|
+
export declare function thinkingProviderOptions(providerName: string, modelName: string, level: ThinkingLevel): SharedV4ProviderOptions | undefined;
|
|
5
5
|
/**
|
|
6
6
|
* 根据 provider 名称创建 AI SDK LanguageModel 实例。
|
|
7
7
|
*
|
|
8
8
|
* 支持: anthropic, openai, deepseek, qwen
|
|
9
9
|
*/
|
|
10
|
-
export declare function createProviderModel(providerName: string, modelName: string, apiKey: string, baseURL?: string):
|
|
10
|
+
export declare function createProviderModel(providerName: string, modelName: string, apiKey: string, baseURL?: string): LanguageModelV4;
|
|
11
11
|
/** generateText 调用目的 */
|
|
12
12
|
export type LLMCallPurpose = "structured-output" | "text" | "sampling" | "chat";
|
|
13
13
|
/** resolveLLMCall 的返回值 */
|
|
14
14
|
export interface ResolvedLLMCall {
|
|
15
|
-
readonly model:
|
|
16
|
-
readonly providerOptions?:
|
|
15
|
+
readonly model: LanguageModelV4;
|
|
16
|
+
readonly providerOptions?: SharedV4ProviderOptions;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* 按 provider + 调用目的解析 generateText 的完整调用上下文。
|
package/dist/llm/providers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createAlibaba as e}from"@ai-sdk/alibaba";import{createAnthropic as i}from"@ai-sdk/anthropic";import{createOpenAI as n}from"@ai-sdk/openai";import{createDeepSeek as o}from"@ai-sdk/deepseek";import{runtimeThinkingLevels as t}from"../config/schema.js";const
|
|
1
|
+
import{createAlibaba as e}from"@ai-sdk/alibaba";import{createAnthropic as i}from"@ai-sdk/anthropic";import{createOpenAI as n}from"@ai-sdk/openai";import{createDeepSeek as o}from"@ai-sdk/deepseek";import{runtimeThinkingLevels as t}from"../config/schema.js";const a={low:2048,medium:8192,high:16384},r=["gpt-5.1","gpt-5.2","gpt-5.3","gpt-5.4"];function p(e){return r.some(i=>e.startsWith(i))}function s(e){const i=/^claude-[a-z]+-(\d+)(?:-(\d+))?(?:\b|-)/.exec(e),n=i?.[1];if(!n)return!1;const o=Number(n),t=i?.[2],a=void 0!==t&&t.length<=2?Number(t):0;return o>4||4===o&&a>=6}export function thinkingProviderOptions(e,i,n){return"openai"===e?"off"===n?p(i)?{openai:{reasoningEffort:"none"}}:void 0:{openai:{reasoningEffort:n}}:"anthropic"===e?"off"===n?{anthropic:{thinking:{type:"disabled"}}}:s(i)?{anthropic:{thinking:{type:"adaptive"},effort:n}}:{anthropic:{thinking:{type:"enabled",budgetTokens:a[n]}}}:"qwen"===e?"off"===n?{alibaba:{enableThinking:!1}}:{alibaba:{enableThinking:!0,thinkingBudget:a[n]}}:"deepseek"===e?{deepseek:{thinking:{type:"off"===n?"disabled":"enabled"}}}:void 0}const d="https://dashscope.aliyuncs.com/compatible-mode/v1",c={anthropic:(e,{apiKey:n,baseURL:o})=>i({apiKey:n,...o?{baseURL:o}:{}})(e),openai:(e,{apiKey:i,baseURL:o})=>n({apiKey:i,...o?{baseURL:o}:{}})(e),deepseek:(e,{apiKey:i,baseURL:n})=>o({apiKey:i,...n?{baseURL:n}:{}})(e),qwen:(i,{apiKey:n,baseURL:o})=>e({apiKey:n,baseURL:o??d})(i)};export function createProviderModel(e,i,n,o){const t=c[e];if(!t){const i=Object.keys(c).join(", ");throw new Error(`Unknown LLM provider "${e}". Supported: ${i}`)}return t(i,{apiKey:n,baseURL:o})}export function resolveLLMCall(e,i,n,o,t,a="medium"){const r=createProviderModel(e,i,n,t);if("structured-output"===o&&"qwen"===e)return{model:r,providerOptions:{alibaba:{enableThinking:!1}}};if("chat"===o){const n=thinkingProviderOptions(e,i,a);return n?{model:r,providerOptions:n}:{model:r}}return{model:r}}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { LanguageModelV4 } from "@ai-sdk/provider";
|
|
3
3
|
import type { AgentTransport } from "../types/agent.ts";
|
|
4
4
|
export interface ConnectOptions {
|
|
5
5
|
/** 连接超时(毫秒),默认 30s */
|
|
6
6
|
readonly timeoutMs?: number;
|
|
7
7
|
/** 为子 Agent 提供的 LLM model(启用 Sampling 支持) */
|
|
8
|
-
readonly samplingModel?:
|
|
8
|
+
readonly samplingModel?: LanguageModelV4;
|
|
9
9
|
/** 注入到 stdio 子进程的环境变量(与 process.env 合并) */
|
|
10
10
|
readonly env?: Readonly<Record<string, string>>;
|
|
11
11
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { LanguageModelV4 } from "@ai-sdk/provider";
|
|
3
3
|
/**
|
|
4
4
|
* 在 MCP Client 上注册 Sampling Handler。
|
|
5
5
|
*
|
|
@@ -8,4 +8,4 @@ import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
|
8
8
|
*
|
|
9
9
|
* @see https://spec.modelcontextprotocol.io/specification/client/sampling/
|
|
10
10
|
*/
|
|
11
|
-
export declare function registerSamplingHandler(client: Client, model:
|
|
11
|
+
export declare function registerSamplingHandler(client: Client, model: LanguageModelV4): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LanguageModelV4, SharedV4ProviderOptions } from "@ai-sdk/provider";
|
|
2
2
|
import type { RegisteredAgent } from "../types/agent.ts";
|
|
3
3
|
import type { RouteSelection } from "../types/router.ts";
|
|
4
4
|
/**
|
|
@@ -8,4 +8,4 @@ import type { RouteSelection } from "../types/router.ts";
|
|
|
8
8
|
* 1. 优先尝试 structured output
|
|
9
9
|
* 2. 仅当模型未遵循 schema 时,降级为纯文本 JSON fallback
|
|
10
10
|
*/
|
|
11
|
-
export declare function routeWithLLM(message: string, agents: ReadonlyArray<RegisteredAgent>, model:
|
|
11
|
+
export declare function routeWithLLM(message: string, agents: ReadonlyArray<RegisteredAgent>, model: LanguageModelV4, structuredOutputProviderOptions?: SharedV4ProviderOptions): Promise<RouteSelection>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LanguageModelV4, SharedV4ProviderOptions } from "@ai-sdk/provider";
|
|
2
2
|
import type { AgentTool } from "../types/agent.ts";
|
|
3
|
-
export declare function extractToolInput(message: string, tool: Pick<AgentTool, "name" | "description" | "inputSchema">, model:
|
|
3
|
+
export declare function extractToolInput(message: string, tool: Pick<AgentTool, "name" | "description" | "inputSchema">, model: LanguageModelV4, structuredOutputProviderOptions?: SharedV4ProviderOptions): Promise<Readonly<Record<string, unknown>>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roll-agent/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
"node": ">=22.6.0"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@ai-sdk/alibaba": "^
|
|
62
|
-
"@ai-sdk/anthropic": "^
|
|
63
|
-
"@ai-sdk/deepseek": "^
|
|
64
|
-
"@ai-sdk/openai": "^
|
|
65
|
-
"@ai-sdk/provider": "^
|
|
61
|
+
"@ai-sdk/alibaba": "^2.0.3",
|
|
62
|
+
"@ai-sdk/anthropic": "^4.0.4",
|
|
63
|
+
"@ai-sdk/deepseek": "^3.0.2",
|
|
64
|
+
"@ai-sdk/openai": "^4.0.4",
|
|
65
|
+
"@ai-sdk/provider": "^4.0.1",
|
|
66
66
|
"@clack/prompts": "1.4.0",
|
|
67
67
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
68
|
-
"ai": "^
|
|
68
|
+
"ai": "^7.0.9",
|
|
69
69
|
"chalk": "^5.4.0",
|
|
70
70
|
"citty": "^0.1.6",
|
|
71
71
|
"cli-table3": "^0.6.5",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"react": "19.2.7",
|
|
77
77
|
"yaml": "^2.7.0",
|
|
78
78
|
"zod": "^3.25.76",
|
|
79
|
-
"@roll-agent/runtime": "0.
|
|
79
|
+
"@roll-agent/runtime": "0.3.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@types/node": "^22.0.0",
|