@chatpanel/events 0.190.0 → 0.192.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.
@@ -1,3 +1,3 @@
1
- import{callFirstProvider as h}from"./capability-providers.js";import{DECIDE_QUESTION_TYPES as k,validateDecideRequest as A,validateRerankRequest as x}from"./capability-endpoints.js";const m="decide",g="rerank",v=6e4,E=200,O=2e3,f=160,q="A small DECISION MODEL and/or a RERANKER run on this device. For a sentiment, tone, urgency, spam/complaint, yes/no or pick-one judgement over text you already hold, call `decide` instead of judging it yourself — it answers in milliseconds with a probability per option. To order many candidates (search hits, comments, sections, options) by relevance to a question, call `rerank` instead of reading them all. Report its answer as the model's, with the probability, and say when it is marked uncalibrated.",_=Object.freeze({name:m,annotations:{readOnlyHint:!0,idempotentHint:!0},description:"Ask a small local decision model one or more typed questions about a text: a choice among options (sentiment, category, urgency), a score on an ordered rubric, or a yes/no. Returns the answer per question with its probability and the runner-up. Use it for any classification of text you already have — the whole thread, a comment, an email — rather than judging it yourself. Not a language model: it cannot explain, only decide.",parameters:{type:"object",properties:{text:{type:"string",description:"The text to judge, as-is. Up to ~60k characters."},questions:{type:"array",minItems:1,description:"One or more questions, each answered independently over the same text.",items:{type:"object",properties:{key:{type:"string",description:'A short identifier for the answer (e.g. "sentiment"). Optional.'},type:{type:"string",enum:[...k],description:'"choice" picks one option; "score" places the text on options read as an ordered rubric (worst first); "noul" is yes/no and takes no options.'},instructions:{type:"string",description:'What to decide, in one sentence — e.g. "The overall sentiment of the comments toward the company."'},options:{type:"array",items:{type:"string"},description:"For choice and score: 2–255 distinct options. Omit for noul."}},required:["type","instructions"],additionalProperties:!1}}},required:["text","questions"],additionalProperties:!1}}),S=Object.freeze({name:g,annotations:{readOnlyHint:!0,idempotentHint:!0},description:"Order candidate texts by relevance to a query with a local reranker (a cross-encoder). Give it the query and the candidates — search results, comments, paragraphs, options — and it returns them best first with a relevance score each. Use it to pick what to read or cite from many candidates instead of reading them all.",parameters:{type:"object",properties:{query:{type:"string",description:"What relevance is measured against — the question or the need."},documents:{type:"array",items:{type:"string"},minItems:2,description:"The candidates, 2–200 strings. Each is read up to ~2000 characters."},top_n:{type:"integer",minimum:1,description:"Return only the best N. Default: all, ordered."}},required:["query","documents"],additionalProperties:!1}}),N=/^[A-Za-z_][A-Za-z0-9_]*$/;function R(e){const o={};return(Array.isArray(e)?e:[]).forEach((t,s)=>{if(!t||typeof t!="object")return;let n=String(t.key||"").trim();(!N.test(n)||o[n])&&(n=`q${s+1}`);const r={type:String(t.type||""),instructions:String(t.instructions||"")};r.type!=="noul"&&Array.isArray(t.options)&&(r.options=t.options.map(p=>String(p))),o[n]=r}),o}const b=e=>`${Math.round(Number(e)*100)}%`,y=e=>String(e).replace(/\s+/g," ").trim();function w(e,o,t){const s=o.where?` on ${o.where}`:"",n=Number.isFinite(e.ms)?` in ${Math.round(e.ms)} ms`:"";return`Answered by ${e.model||o.id}${s}${n} — ${t}, not a language model.`}function T(e,o,t){const s=[];for(const[n,r]of Object.entries(e.answers||{})){const p=t[n],i=typeof r.value=="boolean"?r.value?"yes":"no":String(r.value),a=(r.options||[]).filter(d=>d.value!==r.value).sort((d,l)=>l.p-d.p),u=a.length?`; then ${typeof a[0].value=="boolean"?a[0].value?"yes":"no":a[0].value} ${b(a[0].p)}`:"";s.push(`${n}${p?.instructions?` (${y(p.instructions)})`:""}: ${i} — ${b(r.p)}${u}`)}return s.push("",w(e,o,"a decision model")),e.calibrated===!1&&s.push("Uncalibrated: the probabilities rank the options but are not confidence — say so if you quote them."),s.join(`
2
- `)}function j(e,o,t,{query:s="",topN:n=null}={}){const r=e.results.map((i,a)=>`${a+1}. [doc ${i.index+1}] score ${Number(i.relevance_score).toFixed(3)} — ${y(t[i.index]).slice(0,f)}${t[i.index].length>f?"…":""}`);return[`${e.results.length} of ${t.length} candidate(s) for "${y(s).slice(0,120)}", best first${n?` (top ${n})`:""}. [doc N] is the 1-based position in the list you sent.`,...r,"",w(e,o,"a reranker")].join(`
3
- `)}function I({providers:e={},fetch:o,budgetMs:t=8e3}={}){if(typeof o!="function")throw new Error("capabilityToolsProvider: fetch required");const s=Array.isArray(e.decide)?e.decide:[],n=Array.isArray(e.rerank)?e.rerank:[];if(!s.length&&!n.length)return null;const r=[];return s.length&&r.push(_),n.length&&r.push(S),{specs:r,system:q,async execute(p,i){if(p===m&&s.length){const a=String(i?.text||"").trim().slice(0,v),u=R(i?.questions);if(!a)return"decide needs `text` — the passage to judge.";if(!Object.keys(u).length)return"decide needs at least one question: { type, instructions, options? }.";let d;try{d=A({state:a,questions:u,budgetMs:t})}catch(l){return`decide could not take the questions (${l.message}). Fix them and call again: a choice or score needs 2–255 distinct options, a noul none.`}try{const{doc:l,provider:c}=await h(s,"decide",d,{fetch:o,timeoutMs:2e4,validateAgainst:d});return T(l,c,d.questions)}catch(l){return`No decision model answered (${l?.message||l}). Judge the text yourself now and say the answer is your own reading, not a model's probability.`}}if(p===g&&n.length){const a=String(i?.query||"").trim(),u=(Array.isArray(i?.documents)?i.documents:[]).map(c=>String(c??"")).filter(c=>c.trim()).slice(0,E);if(!a)return"rerank needs `query` — what relevance is measured against.";if(u.length<2)return u.length===1?"One candidate needs no ordering — it is the answer.":"rerank needs `documents` — two or more candidates.";const d=Number.isInteger(i?.top_n)&&i.top_n>=1?Math.min(i.top_n,u.length):null;let l;try{l=x({query:a,documents:u.map(c=>y(c).slice(0,O)),budgetMs:t,...d?{top_n:d}:{}})}catch(c){return`rerank could not take the candidates (${c.message}).`}try{const{doc:c,provider:$}=await h(n,"rerank",l,{fetch:o,validateAgainst:l});return j(c,$,u,{query:a,topN:d})}catch(c){return`No reranker answered (${c?.message||c}). Order the candidates yourself by reading them, and say the ordering is your own.`}}return JSON.stringify({error:`Unknown tool: ${p}`})}}}export{q as CAPABILITY_TOOLS_SYSTEM,m as DECIDE_TOOL_NAME,g as RERANK_TOOL_NAME,I as capabilityToolsProvider,R as questionsToWire,T as renderDecideResult,j as renderRerankResult};
1
+ import{callFirstProvider as m}from"./capability-providers.js";import{DECIDE_QUESTION_TYPES as A,validateDecideRequest as E,validateRerankRequest as x}from"./capability-endpoints.js";const g="decide",f="rerank",O=6e4,v=200,q=2e3,b=160,_="A small DECISION MODEL and/or a RERANKER run on this device. For a sentiment, tone, urgency, spam/complaint, yes/no or pick-one judgement over text you already hold, call `decide` instead of judging it yourself — it answers in milliseconds with a probability per option. To order many candidates (search hits, comments, sections, options) by relevance to a question, call `rerank` instead of reading them all. Report its answer as the model's, with the probability, and say when it is marked uncalibrated.",N="No decision model is running on this device. Judge the text yourself now and say the answer is your own reading, not a model's probability.",R="No reranker is running on this device. Order the candidates yourself by reading them, and say the ordering is your own.",S=Object.freeze({name:g,annotations:{readOnlyHint:!0,idempotentHint:!0},description:"Ask a small local decision model one or more typed questions about a text: a choice among options (sentiment, category, urgency), a score on an ordered rubric, or a yes/no. Returns the answer per question with its probability and the runner-up. Use it for any classification of text you already have — the whole thread, a comment, an email — rather than judging it yourself. Not a language model: it cannot explain, only decide.",parameters:{type:"object",properties:{text:{type:"string",description:"The text to judge, as-is. Up to ~60k characters."},questions:{type:"array",minItems:1,description:"One or more questions, each answered independently over the same text.",items:{type:"object",properties:{key:{type:"string",description:'A short identifier for the answer (e.g. "sentiment"). Optional.'},type:{type:"string",enum:[...A],description:'"choice" picks one option; "score" places the text on options read as an ordered rubric (worst first); "noul" is yes/no and takes no options.'},instructions:{type:"string",description:'What to decide, in one sentence — e.g. "The overall sentiment of the comments toward the company."'},options:{type:"array",items:{type:"string"},description:"For choice and score: 2–255 distinct options. Omit for noul."}},required:["type","instructions"],additionalProperties:!1}}},required:["text","questions"],additionalProperties:!1}}),T=Object.freeze({name:f,annotations:{readOnlyHint:!0,idempotentHint:!0},description:"Order candidate texts by relevance to a query with a local reranker (a cross-encoder). Give it the query and the candidates — search results, comments, paragraphs, options — and it returns them best first with a relevance score each. Use it to pick what to read or cite from many candidates instead of reading them all.",parameters:{type:"object",properties:{query:{type:"string",description:"What relevance is measured against — the question or the need."},documents:{type:"array",items:{type:"string"},minItems:2,description:"The candidates, 2–200 strings. Each is read up to ~2000 characters."},top_n:{type:"integer",minimum:1,description:"Return only the best N. Default: all, ordered."}},required:["query","documents"],additionalProperties:!1}}),D=/^[A-Za-z_][A-Za-z0-9_]*$/;function j(e){const i={};return(Array.isArray(e)?e:[]).forEach((t,s)=>{if(!t||typeof t!="object")return;let n=String(t.key||"").trim();(!D.test(n)||i[n])&&(n=`q${s+1}`);const r={type:String(t.type||""),instructions:String(t.instructions||"")};r.type!=="noul"&&Array.isArray(t.options)&&(r.options=t.options.map(p=>String(p))),i[n]=r}),i}const w=e=>`${Math.round(Number(e)*100)}%`,h=e=>String(e).replace(/\s+/g," ").trim();function $(e,i,t){const s=i.where?` on ${i.where}`:"",n=Number.isFinite(e.ms)?` in ${Math.round(e.ms)} ms`:"";return`Answered by ${e.model||i.id}${s}${n} — ${t}, not a language model.`}function C(e,i,t){const s=[];for(const[n,r]of Object.entries(e.answers||{})){const p=t[n],d=typeof r.value=="boolean"?r.value?"yes":"no":String(r.value),o=(r.options||[]).filter(c=>c.value!==r.value).sort((c,l)=>l.p-c.p),y=o.length?`; then ${typeof o[0].value=="boolean"?o[0].value?"yes":"no":o[0].value} ${w(o[0].p)}`:"";s.push(`${n}${p?.instructions?` (${h(p.instructions)})`:""}: ${d} — ${w(r.p)}${y}`)}return s.push("",$(e,i,"a decision model")),e.calibrated===!1&&s.push("Uncalibrated: the probabilities rank the options but are not confidence — say so if you quote them."),s.join(`
2
+ `)}function I(e,i,t,{query:s="",topN:n=null}={}){const r=e.results.map((d,o)=>`${o+1}. [doc ${d.index+1}] score ${Number(d.relevance_score).toFixed(3)} — ${h(t[d.index]).slice(0,b)}${t[d.index].length>b?"…":""}`);return[`${e.results.length} of ${t.length} candidate(s) for "${h(s).slice(0,120)}", best first${n?` (top ${n})`:""}. [doc N] is the 1-based position in the list you sent.`,...r,"",$(e,i,"a reranker")].join(`
3
+ `)}function H({providers:e={},fetch:i,budgetMs:t=8e3,always:s=!1}={}){if(typeof i!="function")throw new Error("capabilityToolsProvider: fetch required");const n=Array.isArray(e.decide)?e.decide:[],r=Array.isArray(e.rerank)?e.rerank:[];if(!s&&!n.length&&!r.length)return null;const p=[];return(s||n.length)&&p.push(S),(s||r.length)&&p.push(T),{specs:p,system:_,async execute(d,o){if(d===g){if(!n.length)return N;const y=String(o?.text||"").trim().slice(0,O),c=j(o?.questions);if(!y)return"decide needs `text` — the passage to judge.";if(!Object.keys(c).length)return"decide needs at least one question: { type, instructions, options? }.";let l;try{l=E({state:y,questions:c,budgetMs:t})}catch(u){return`decide could not take the questions (${u.message}). Fix them and call again: a choice or score needs 2–255 distinct options, a noul none.`}try{const{doc:u,provider:a}=await m(n,"decide",l,{fetch:i,timeoutMs:2e4,validateAgainst:l});return C(u,a,l.questions)}catch(u){return`No decision model answered (${u?.message||u}). Judge the text yourself now and say the answer is your own reading, not a model's probability.`}}if(d===f){if(!r.length)return R;const y=String(o?.query||"").trim(),c=(Array.isArray(o?.documents)?o.documents:[]).map(a=>String(a??"")).filter(a=>a.trim()).slice(0,v);if(!y)return"rerank needs `query` — what relevance is measured against.";if(c.length<2)return c.length===1?"One candidate needs no ordering — it is the answer.":"rerank needs `documents` — two or more candidates.";const l=Number.isInteger(o?.top_n)&&o.top_n>=1?Math.min(o.top_n,c.length):null;let u;try{u=x({query:y,documents:c.map(a=>h(a).slice(0,q)),budgetMs:t,...l?{top_n:l}:{}})}catch(a){return`rerank could not take the candidates (${a.message}).`}try{const{doc:a,provider:k}=await m(r,"rerank",u,{fetch:i,validateAgainst:u});return I(a,k,c,{query:y,topN:l})}catch(a){return`No reranker answered (${a?.message||a}). Order the candidates yourself by reading them, and say the ordering is your own.`}}return JSON.stringify({error:`Unknown tool: ${d}`})}}}export{_ as CAPABILITY_TOOLS_SYSTEM,g as DECIDE_TOOL_NAME,f as RERANK_TOOL_NAME,H as capabilityToolsProvider,j as questionsToWire,C as renderDecideResult,I as renderRerankResult};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatpanel/events",
3
- "version": "0.190.0",
3
+ "version": "0.192.0",
4
4
  "description": "The canonical ChatPanel event-log and capability contracts — typed durable facts, clock-free deterministic linearization, schema upcasting, and the invariants the replay harness asserts. Pure, dependency-free ESM shared by the ChatPanel extension, gateway and bridge.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -171,6 +171,8 @@
171
171
  "./tool-discovery.js": "./tool-discovery.js",
172
172
  "./tool-dispatch.js": "./tool-dispatch.js",
173
173
  "./tool-groups.js": "./tool-groups.js",
174
+ "./tool-group-capabilities.js": "./tool-group-capabilities.js",
175
+ "./tool-group-data.js": "./tool-group-data.js",
174
176
  "./tool-hints.js": "./tool-hints.js",
175
177
  "./tool-need.js": "./tool-need.js",
176
178
  "./tool-result.js": "./tool-result.js",
@@ -389,6 +391,8 @@
389
391
  "tool-discovery.js",
390
392
  "tool-dispatch.js",
391
393
  "tool-groups.js",
394
+ "tool-group-capabilities.js",
395
+ "tool-group-data.js",
392
396
  "tool-hints.js",
393
397
  "tool-need.js",
394
398
  "tool-result.js",
@@ -0,0 +1 @@
1
+ import{defineToolGroup as o}from"./tool-groups.js";import{capabilityToolsProvider as t}from"./capability-tools.js";function p({providers:i,fetch:r}={}){if(typeof i!="function")throw new Error("makeCapabilitiesGroup: providers(ctx) required");if(typeof r!="function")throw new Error("makeCapabilitiesGroup: fetch required");return o({id:"capabilities",label:"The decision model and the reranker",priority:60,applies:e=>!!e.resolvedAgent&&e.includeCapabilities!==!1,async build(e){const a=await i(e).catch(()=>null);return t({providers:{decide:a?.decide||[],rerank:a?.rerank||[]},fetch:r,always:!!e.stable})}})}export{p as makeCapabilitiesGroup};
@@ -0,0 +1 @@
1
+ import{defineToolGroup as d}from"./tool-groups.js";import{buildToolset as i}from"./toolset.js";import{findDispatchProvider as p}from"./find-tool.js";function b({history:o=null,weather:s=null,web:u=null,dispatch:a=null}={}){const l=async(e,r)=>{if(typeof e!="function")return null;try{return await e(r)}catch{return null}};return d({id:"data",label:"The user's own data and the web",priority:50,applies:e=>!!e.resolvedAgent&&(e.includeHistory!==!1||e.includeWebSearch!==!1||!!e.noteWriter||!!e.memoryWriter),async build(e){const r=[];e.includeHistory!==!1&&r.push(await l(o,e)),e.includeWebSearch!==!1&&(r.push(await l(s,e)),r.push(await l(u,e)));const n=i(r.filter(Boolean)),f=typeof a=="function"?a(e)===!1:e.dataDispatch===!1,t=[n?f?{specs:n.specs,system:n.system,execute:n.execute}:p(n):null,e.noteWriter,e.memoryWriter].filter(Boolean);return t.length?t.length===1?t[0]:i(t):null}})}export{b as makeDataGroup};