@lensmcp/protocol-types 1.25.33 → 1.25.34
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/index.d.ts +6 -0
- package/index.js +1 -1
- package/lib/ai-call-groups.d.ts +56 -0
- package/lib/ai-call-groups.js +2 -0
- package/lib/ai-call-row.d.ts +80 -0
- package/lib/ai-call-row.js +3 -3
- package/lib/ai-call.d.ts +2 -0
- package/lib/ai-call.js +8 -8
- package/lib/ai-context.d.ts +113 -0
- package/lib/ai-context.js +6 -0
- package/lib/ai-origins.d.ts +72 -0
- package/lib/ai-origins.js +10 -0
- package/lib/dashboard.d.ts +10 -3
- package/lib/event-loop.d.ts +46 -0
- package/lib/event-loop.js +1 -1
- package/lib/team-design.d.ts +228 -0
- package/lib/team-design.js +1 -0
- package/lib/team-library.d.ts +283 -0
- package/lib/team-library.js +1 -0
- package/lib/team.d.ts +209 -0
- package/lib/team.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AiCall } from './ai-call.js';
|
|
2
|
+
import type { AiCallRow } from './ai-call-row.js';
|
|
3
|
+
import { type AiPart } from './ai-context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Where each part of what a model was given came from (the AI call's Context tab): found by comparing the call with
|
|
6
|
+
* the earlier calls the lens kept of its run, thread or flow. A part — or a section of one, a `--- THE CASE MEMORY ---`
|
|
7
|
+
* block of a developer message — is an earlier call's answer handed on whole (`answer`), what a tool an earlier call
|
|
8
|
+
* asked for returned (`tool-output`), or holds passages of an earlier answer (`quoted`); else it is set beside the
|
|
9
|
+
* previous call of the same agent: a turn of the conversation it already had (`carried`), and the system prompt, the
|
|
10
|
+
* tools, the schema and the app's context `unchanged`, `changed` or `new` since — what the provider's prompt cache
|
|
11
|
+
* reads, and what each turn the app changed.
|
|
12
|
+
*
|
|
13
|
+
* Texts are compared normalized — trimmed, whitespace collapsed, JSON escapes read (`\n`, `\"`), a JSON text by its
|
|
14
|
+
* parsed value. Passages are found with a rolling hash over each text, once: a call of 350 KB against 30 earlier ones
|
|
15
|
+
* is a few passes over its characters, not one per passage.
|
|
16
|
+
*
|
|
17
|
+
* Pure: the server runs it on the call and the earlier calls it read.
|
|
18
|
+
*/
|
|
19
|
+
export type AiOriginHow = 'answer' | 'tool-output' | 'quoted' | 'carried' | 'unchanged' | 'changed' | 'new';
|
|
20
|
+
export interface AiOrigin {
|
|
21
|
+
/** The part's index in aiContextOf(call).parts. */
|
|
22
|
+
part: number;
|
|
23
|
+
/** A section of that part, by its title — absent when the origin is the whole part's. */
|
|
24
|
+
section?: string;
|
|
25
|
+
how: AiOriginHow;
|
|
26
|
+
/** The earlier call it came from, or was compared with. */
|
|
27
|
+
from?: {
|
|
28
|
+
id: string;
|
|
29
|
+
agent: string;
|
|
30
|
+
step?: number;
|
|
31
|
+
at: number;
|
|
32
|
+
service: string;
|
|
33
|
+
};
|
|
34
|
+
/** In words, for a person: «the answer of Document digest, 2 calls earlier», «unchanged since the previous call of this thread», «changed since the previous call — 1,240 → 1,610 chars», «returned by tool read_message (called in step 3)», «quotes 3 passages of Critical Limits Analyst's answer». */
|
|
35
|
+
said: string;
|
|
36
|
+
/** Beside an answer, a tool's output or a quote: how the part (or section) compares with `previous` — what changed is still marked. */
|
|
37
|
+
since?: 'unchanged' | 'changed' | 'new';
|
|
38
|
+
/** `quoted`: how many passages of that answer it holds. */
|
|
39
|
+
passages?: number;
|
|
40
|
+
/** It changed (`changed`, or `since: 'changed'`): its size in characters then and now. */
|
|
41
|
+
chars?: {
|
|
42
|
+
before: number;
|
|
43
|
+
after: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export interface AiOrigins {
|
|
47
|
+
/** How many earlier calls it was compared with. */
|
|
48
|
+
compared: number;
|
|
49
|
+
/** The call it was diffed against: the previous call of the same agent in the same thread (else run, else flow). */
|
|
50
|
+
previous?: {
|
|
51
|
+
id: string;
|
|
52
|
+
at: number;
|
|
53
|
+
by?: AiPreviousScope;
|
|
54
|
+
};
|
|
55
|
+
items: AiOrigin[];
|
|
56
|
+
}
|
|
57
|
+
/** What the previous call shares with this one: its thread, else its run, else its flow, else its request. */
|
|
58
|
+
export type AiPreviousScope = 'thread' | 'run' | 'flow' | 'request';
|
|
59
|
+
/** A text as compared: JSON escapes read, whitespace collapsed, trimmed. */
|
|
60
|
+
export declare function normText(s: string): string;
|
|
61
|
+
/** A part's text, as {@link aiContextOf} measured it — what `ai_call_context { texts }` shows of it. */
|
|
62
|
+
export declare const aiPartText: (call: AiCall, part: AiPart) => string;
|
|
63
|
+
/** Each of a part's sections' texts, in its order (a tool's definition for the tools' part). */
|
|
64
|
+
export declare const aiSectionTexts: (call: AiCall, part: AiPart) => string[];
|
|
65
|
+
/**
|
|
66
|
+
* Where each part of `call` came from — see the file's comment. `earlier`: the calls of its run, thread, flow or
|
|
67
|
+
* request made before it (any order; a later one, or itself, is left out).
|
|
68
|
+
*/
|
|
69
|
+
export declare function aiOriginsOf(call: AiCall, row: AiCallRow, earlier: Array<{
|
|
70
|
+
row: AiCallRow;
|
|
71
|
+
call: AiCall;
|
|
72
|
+
}>): AiOrigins;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";var It=Object.defineProperty;var v=(t,e)=>It(t,"name",{value:e,configurable:!0});var Mt=Object.defineProperty,c=v((t,e)=>Mt(t,"name",{value:e,configurable:!0}),"a");import{aiContextOf as at}from"./ai-context.js";const ct=8,lt=24,ft=32,Nt=80,ut=400,Tt=200,Ct=1e6,Jt=12;export function normText(t){return(t.includes("\\")?t.replace(/\\[nrt]/g," ").replace(/\\(["\\/])/g,"$1"):t).replace(/\s\s+|[^\S ]/g," ").trim()}v(normText,"normText"),c(normText,"normText");function S(t){const e=t.trim();if(e.startsWith("{")&&e.endsWith("}")||e.startsWith("[")&&e.endsWith("]"))try{return JSON.stringify(JSON.parse(e))}catch{}return normText(e)}v(S,"I"),c(S,"canonical");const W=ft,G=16777619,qt=(()=>{let t=1;for(let e=1;e<W;e++)t=Math.imul(t,G);return t})(),dt=1<<18,ht=dt-1;function Q(t){let e=0;for(let s=0;s<W;s++)e=Math.imul(e,G)+t.charCodeAt(s)|0;return e}v(Q,"ue"),c(Q,"headHash");class Wt{static{v(this,"Ne")}static{c(this,"PassageIndex")}constructor(){this.by=new Map,this.bits=new Uint32Array(dt>>>5),this.n=0}add(e){const s=Q(e.text),r=this.by.get(s);r?r.push(e):this.by.set(s,[e]),this.bits[(s&ht)>>>5]|=1<<(s&31),this.n++}get size(){return this.n}find(e,s){if(!this.n||e.length<W)return;const r=new Set,n=this.bits;let a=Q(e);for(let u=0;;u++){const h=(n[(a&ht)>>>5]&1<<(a&31))!==0?this.by.get(a):void 0;if(h)for(const w of h)!r.has(w)&&e.startsWith(w.text,u)&&(r.add(w),s(w));if(u+W>=e.length)break;a=Math.imul(a-Math.imul(e.charCodeAt(u),qt)|0,G)+e.charCodeAt(u+W)|0}}}const pt=c(t=>t.replace(/^(?:mcp__)?[A-Za-z0-9-]+(?:_[A-Za-z0-9-]+)*__/,""),"toolName");function C(t,e,s,r=0){if(!(r>Jt)){if(typeof t=="string")e(t,s);else if(Array.isArray(t))for(const n of t)C(n,e,s,r+1);else if(t&&typeof t=="object")for(const[n,a]of Object.entries(t))C(a,e,n,r+1)}}v(C,"T"),c(C,"strings");const V=c(t=>{const e=t.trim();if(!(!e.startsWith("{")&&!e.startsWith("[")))try{return JSON.parse(e)}catch{return}},"parseJson");function gt(t){const e=t.answer,s=[],r=c((n,a)=>{const u=S(n);u.length>=ct&&s.push({key:u,text:normText(n),...a?{what:a}:{}})},"add");e.text!==void 0&&r(e.text),e.json!==void 0&&(e.text===void 0&&r(JSON.stringify(e.json)),C(e.json,(n,a)=>{n.trim().length>=lt&&r(n,a?`its \xAB${a}\xBB`:void 0)}));for(const n of e.toolCalls){const a=n.tool||pt(n.name);r(n.arguments,`its call of ${a}`),C(V(n.arguments),(u,h)=>{u.trim().length>=lt&&r(u,h?`${a}\u2019s \xAB${h}\xBB`:`its call of ${a}`)})}return s}v(gt,"_e"),c(gt,"answerKeys");function wt(t){const e=[],s=new Set,r=c((h,w,x)=>{if(e.length>=Tt||s.has(h))return;s.add(h);const O={text:h,e:t,...w?{what:w}:{},...x?{parent:x}:{}};return e.push(O),O},"push"),n=c((h,w,x,O)=>{const[j,...K]=O;if(j)for(const J of h.split(j)){const N=normText(J);if(N.length<Nt||N===x?.text)continue;const R=r(N,w,x)??x;N.length>ut&&n(J,w,R,K)}},"pieces"),a=c((h,w)=>{const x=normText(h);if(x.length<ft)return;const O=r(x,w);x.length>ut&&n(h,w,O,[/\n\s*\n/,/\n/,/(?<=[.!?:;])\s+/])},"text"),u=t.call.answer;u.json!==void 0?C(u.json,(h,w)=>a(h,w?`its \xAB${w}\xBB`:void 0)):u.text!==void 0&&a(u.text);for(const h of u.toolCalls){const w=h.tool||pt(h.name),x=V(h.arguments);x!==void 0?C(x,(O,j)=>a(O,j?`${w}\u2019s \xAB${j}\xBB`:`its call of ${w}`)):a(h.arguments,`its call of ${w}`)}return e}v(wt,"je"),c(wt,"passagesOf");function mt(t){const e=[];let s=Ct;const r=c(n=>{if(s<=0||!n)return;const a=n.length>s?n.slice(0,s):n;s-=a.length,e.push(a)},"take");for(const n of t.system)r(n.text);for(const n of t.turns)n.kind==="tool"?(r(n.arguments),r(n.output??"")):r(n.text??"");return e}v(mt,"Te"),c(mt,"inputTexts");function _(t,e){const s=e.ref;if("system"in s)return t.system[s.system]?.text??"";if("tools"in s)return t.tools.map(r=>JSON.stringify({name:r.name,description:r.description,parameters:r.parameters})).join(`
|
|
2
|
+
`);if("schema"in s)return t.answer.format?.schema!==void 0?JSON.stringify(t.answer.format.schema):"";if("turn"in s){const r=t.turns[s.turn];return r?r.kind==="tool"?`${r.name}(${r.arguments})${r.output!==void 0?`
|
|
3
|
+
${r.output}`:""}`:r.text??"":""}return""}v(_,"K"),c(_,"partText");export const aiPartText=c((t,e)=>_(t,e),"aiPartText"),aiSectionTexts=c((t,e)=>z(t,e,_(t,e)),"aiSectionTexts");function $t(t){const e=V(t);if(e===void 0)return[];const s=e&&typeof e=="object"&&!Array.isArray(e)?e.properties:void 0,r=s&&typeof s=="object"&&!Array.isArray(s)?s:e;return(Array.isArray(r)?r:r&&typeof r=="object"?Object.values(r):[]).map(n=>typeof n=="string"?n:JSON.stringify(n,null,2)??"")}v($t,"Ee"),c($t,"jsonMemberTexts");function z(t,e,s){if(e.kind==="tools")return t.tools.map(n=>JSON.stringify({d:n.description,p:n.parameters}));let r;return e.sections.map((n,a)=>n.at!==void 0&&n.end!==void 0?s.slice(n.at,n.end):(r??=$t(s),r[a]??""))}v(z,"B"),c(z,"sectionTexts");function E(t){const e=new Map;return t.sections.map(s=>{const r=e.get(s.title)??0;return e.set(s.title,r+1),`${s.title}#${r}`})}v(E,"F"),c(E,"titled");function X(t){return t?t.kind==="tool"?`tool
|
|
4
|
+
${t.name}
|
|
5
|
+
${S(t.arguments)}
|
|
6
|
+
${S(t.output??"")}`:t.kind==="reasoning"?`reasoning
|
|
7
|
+
${S(t.text??"")}`:`${t.role}
|
|
8
|
+
${S(t.text)}`:""}v(X,"le"),c(X,"turnKey");const _t=new Set(["system","tools","schema","context"]),vt=new Set(["history","tool","turn"]);function Y(t){const e=new Map,s=new Map;for(const r of t){if(!_t.has(r.kind))continue;const n=`${r.kind}:${r.role??""}`,a=e.get(n)??0;e.set(n,a+1),s.set(r,`${n}#${a}`)}return s}v(Y,"pe"),c(Y,"structureKeys");const xt=c(t=>t.toLocaleString("en-US"),"fmt"),D=c(t=>t.k===1?"the call before":`${t.k} calls earlier`,"ago"),P=c(t=>({id:t.row.id,agent:t.row.agent.name,...t.row.step!==void 0?{step:t.row.step}:{},at:t.row.at,service:t.row.service}),"fromOf"),yt={answer:6,"tool-output":6,quoted:5,carried:4,changed:3,unchanged:2,new:1};function kt(t,e){const s=e.filter(n=>n.row.agent.key===t.agent.key),r=[["thread",t.thread?n=>n.thread?.key===t.thread.key:void 0],["run",n=>n.run.key===t.run.key],["flow",t.flowId?n=>n.flowId===t.flowId:void 0],["request",t.requestId?n=>n.requestId===t.requestId:void 0]];for(const[n,a]of r)if(a){for(let u=s.length-1;u>=0;u--)if(a(s[u].row))return{e:s[u],by:n}}}v(kt,"We"),c(kt,"previousOf");export function aiOriginsOf(t,e,s){const r=s.filter(o=>o.row.id!==e.id&&o.row.at<=e.at).sort((o,i)=>o.row.at-i.row.at||o.row.id.localeCompare(i.row.id)),n=r.map((o,i)=>({row:o.row,call:o.call,k:r.length-i})),a=kt(e,n),u={compared:n.length,...a?{previous:{id:a.e.row.id,at:a.e.row.at,by:a.by}}:{},items:[]};if(!n.length)return u;const h=new Map,w=new Wt;for(const o of n){for(const i of gt(o.call)){const d=h.get(i.key);d?d.push({...i,e:o}):h.set(i.key,[{...i,e:o}])}for(const i of wt(o))w.add(i)}const x=new Map,O=new Map,j=c((o,i)=>{const d=`${o.k}
|
|
9
|
+
${i}`;let m=O.get(d);if(m===void 0){let l=x.get(o);l||x.set(o,l=mt(o.call).map(normText)),m=l.some(f=>f.includes(i)),O.set(d,m)}return m},"given"),K=c((o,...i)=>{for(const d of i){if(d===void 0)continue;const m=S(d),l=m.length>=ct?h.get(m):void 0;for(let f=(l?.length??0)-1;f>=0;f--){const y=l[f];if(o||!j(y.e,y.text))return{e:y.e,...y.what?{what:y.what}:{}}}}},"answerOf"),J=c((o,i=!1)=>{if(!w.size)return;const d=new Set;if(w.find(normText(o),f=>d.add(f)),!i)for(const f of d)j(f.e,f.text)&&d.delete(f);if(!d.size)return;const m=new Map;for(const f of d){let y=f.parent;for(;y&&!d.has(y);)y=y.parent;if(y)continue;const I=m.get(f.e)??{e:f.e,n:0,what:new Set};I.n++,f.what&&I.what.add(f.what),m.set(f.e,I)}let l;for(const f of m.values())(!l||f.n>l.n||f.n===l.n&&f.e.k<l.e.k)&&(l=f);return l},"quoteIn"),N=c(o=>({how:"answer",from:P(o.e),said:`the answer of ${o.e.row.agent.name}${o.what?` (${o.what})`:""}, ${D(o.e)}`}),"answered"),R=c(o=>({how:"quoted",from:P(o.e),passages:o.n,said:`quotes ${o.n===1?"a passage":`${o.n} passages`} of ${o.e.row.agent.name}\u2019s answer${o.what.size===1?` (${[...o.what][0]})`:""}, ${D(o.e)}`}),"quoted"),tt=at(t);let U,et=[];if(a){const o=at(a.e.call);U=new Map;for(const[i,d]of Y(o.parts))U.set(d,{part:i,text:_(a.e.call,i)});et=o.parts.filter(i=>vt.has(i.kind)&&"turn"in i.ref).map(i=>X(a.e.call.turns[i.ref.turn]))}const bt=c(o=>{const i=U?.get(o);if(i){if(!i.sections){const d=z(a.e.call,i.part,i.text);i.sections=new Map(E(i.part).map((m,l)=>[m,{text:d[l]??"",canon:S(d[l]??"")}]))}return i.sections}},"prevSections"),St=a?`the previous call of this ${a.by}`:"",B=a?P(a.e):void 0,nt=c((o,i)=>`changed since the previous call \u2014 ${xt(o)} \u2192 ${xt(i)} chars`,"changedSaid"),Ot=Y(tt.parts),ot=[...n].reverse();let rt=0;return tt.parts.forEach((o,i)=>{if(o.kind==="carried"){const p=e.previousResponseId??t.settings.find(g=>g.name==="continues response")?.value,$=p?ot.find(g=>(g.row.responseId??g.call.responseId)===p):void 0;$&&u.items.push({part:i,how:"carried",from:P($),said:`the conversation of ${$.row.agent.name}\u2019s response it continues, ${D($)} \u2014 held by the provider, not sent again`});return}const d=_(t,o),m="turn"in o.ref?t.turns[o.ref.turn]:void 0;let l,f;const y=Ot.get(o);if(a&&y){const p=U.get(y);p?S(p.text)===S(d)?l={part:i,how:"unchanged",from:B,said:`unchanged since ${St}`}:(l={part:i,how:"changed",from:B,said:nt(p.text.length,d.length),chars:{before:p.text.length,after:d.length}},f=bt(y)):l={part:i,how:"new",from:B,said:"new since the previous call"}}else if(a&&vt.has(o.kind)){const p=X(m),$=et.indexOf(p,rt);$>=0?(rt=$+1,l={part:i,how:"carried",from:B,said:"carried from the previous call"}):l={part:i,how:"new",from:B,said:"new since the previous call"}}const I=l&&l.how!=="carried"?l.how:l?"unchanged":void 0;let k;if(m?.kind==="tool"&&m.callId){const p=m.callId,$=ot.find(g=>g.row.callIds?.includes(p)||g.call.answer.toolCalls.some(b=>b.callId===p));if($){const g=$.row.step!==void 0&&$.row.run.key===e.run.key?`called in step ${$.row.step}`:`called by ${$.row.agent.name}, ${D($)}`;k={part:i,how:"tool-output",from:P($),said:`returned by tool ${m.tool} (${g})`}}}const Z=m?.kind==="message"&&m.role==="assistant";if(!k){const p=K(Z,m?.kind==="tool"?m.output:d);p&&(k={part:i,...N(p)})}const F=k?void 0:J(d,Z);F&&(k={part:i,...R(F)}),k&&I&&(k.since=I),k&&l?.chars&&(k.chars=l.chars);const T=k&&(!l||yt[k.how]>=yt[l.how])?k:l;if(T&&f&&T.how==="changed"){const p=new Set(E(o)),$=[...f.keys()].filter(A=>!p.has(A)).map(A=>A.replace(/#\d+$/,"")),g={changed:0,new:0},b=z(t,o,d);E(o).forEach((A,H)=>{const M=f.get(A);M?M.canon!==S(b[H]??"")&&g.changed++:g.new++});const q=[g.changed?`${g.changed} of ${o.sections.length} sections changed`:"",g.new?`${g.new} new`:"",$.length?`${$.length} gone (${$.slice(0,3).map(A=>`\xAB${A}\xBB`).join(", ")}${$.length>3?", \u2026":""})`:""].filter(Boolean);q.length&&(T.said+=`: ${q.join(", ")}`)}if(T&&u.items.push(T),!o.sections.length||T?.how==="answer"||T?.how==="tool-output")return;const At=z(t,o,d),jt=E(o),Bt=!!F;o.sections.forEach((p,$)=>{const g=At[$]??"";if(!g)return;let b;if(f){const L=f.get(jt[$]);L?L.canon===S(g)?b={part:i,section:p.title,how:"unchanged",from:B,said:"unchanged since the previous call"}:b={part:i,section:p.title,how:"changed",from:B,said:nt(L.text.length,g.length),chars:{before:L.text.length,after:g.length}}:b={part:i,section:p.title,how:"new",from:B,said:"new since the previous call"}}const q=p.at!==void 0&&p.title!=="(opening)"?g.indexOf(`
|
|
10
|
+
`):-1,A=K(Z,g,q>0?g.slice(q+1):void 0),H=A?void 0:Bt?J(g,Z):void 0,M=A?{part:i,section:p.title,...N(A)}:H?{part:i,section:p.title,...R(H)}:void 0,st=b?b.how:I!=="changed"?I:void 0;M&&st&&(M.since=st),M&&b?.chars&&(M.chars=b.chars);const it=M??b;it&&u.items.push(it)})}),u}v(aiOriginsOf,"aiOriginsOf"),c(aiOriginsOf,"aiOriginsOf");
|
package/lib/dashboard.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* on-demand GETs.
|
|
12
12
|
*/
|
|
13
13
|
import type { HttpExchange } from './http-exchange.js';
|
|
14
|
+
import type { DashTeamDesignRun, TeamDesign } from './team-design.js';
|
|
14
15
|
import type { PageVisitNavType, PageVisitRaw, PageVisitReadyBy, PageVisitRequest, PageVisitVitals } from './page-visit.js';
|
|
15
16
|
/** Bumped only on a breaking change to the stream framing itself. */
|
|
16
17
|
export declare const DASHBOARD_PROTOCOL = 1;
|
|
@@ -2617,10 +2618,12 @@ export interface DashAiRun {
|
|
|
2617
2618
|
/**
|
|
2618
2619
|
* `db`: one database (`<store>/<database>`, the health check); `dbtable`: one table (`<store>/<database>/<schema>.<table>`);
|
|
2619
2620
|
* `kvstore` / `queuestore`: one Redis store's keys / BullMQ queues (`<store>`); `files`: the services' file access
|
|
2620
|
-
* (`files`); `external`: the external APIs the services call (`external`); `log`: one log line (its `seq`)
|
|
2621
|
+
* (`files`); `external`: the external APIs the services call (`external`); `log`: one log line (its `seq`);
|
|
2622
|
+
* `aiagent`: one AI agent of the app, by its key on the AI calls page (`agent:onboarding-chat-conductor`);
|
|
2623
|
+
* `team`: the project's agent team, designed (`team.design`) — by its repository (`project` when it has no id).
|
|
2621
2624
|
*/
|
|
2622
2625
|
target: {
|
|
2623
|
-
kind: 'sql' | 'route' | 'error' | 'flow' | 'ui' | 'kv' | 'queue' | 'db' | 'dbtable' | 'kvstore' | 'queuestore' | 'files' | 'external' | 'log' | 'page';
|
|
2626
|
+
kind: 'sql' | 'route' | 'error' | 'flow' | 'ui' | 'kv' | 'queue' | 'db' | 'dbtable' | 'kvstore' | 'queuestore' | 'files' | 'external' | 'log' | 'page' | 'aiagent' | 'team';
|
|
2624
2627
|
id: string;
|
|
2625
2628
|
label: string;
|
|
2626
2629
|
name?: string;
|
|
@@ -2706,6 +2709,10 @@ export interface DashAiRun {
|
|
|
2706
2709
|
/** The reviews it had run before it (a route's statements' SQL reviews), as each went. */
|
|
2707
2710
|
ranFirst?: DashAiRanFirst[];
|
|
2708
2711
|
suggestion?: DashAiSuggestion;
|
|
2712
|
+
/** A team design run's answer, as LensMCP checked it — a design is not a review: it has no suggestion. */
|
|
2713
|
+
design?: TeamDesign;
|
|
2714
|
+
/** The design was accepted on the Team page: when, the files it wrote, the goals it created. */
|
|
2715
|
+
accepted?: DashTeamDesignRun['accepted'];
|
|
2709
2716
|
impact?: DashAiImpact;
|
|
2710
2717
|
/** The instructions it sent: which guide, the workspace's rules and memories, the indicator set. */
|
|
2711
2718
|
prompt?: DashAiPrompt;
|
|
@@ -2898,7 +2905,7 @@ export interface DashSuggestion {
|
|
|
2898
2905
|
}
|
|
2899
2906
|
/** One thing a suggestion is about, or rests on — and how to read it. */
|
|
2900
2907
|
export interface DashSuggestionRef {
|
|
2901
|
-
kind: 'sql' | 'route' | 'flow' | 'page' | 'error' | 'queue' | 'kv' | 'db' | 'dbtable' | 'table' | 'external' | 'code' | 'run';
|
|
2908
|
+
kind: 'sql' | 'route' | 'flow' | 'page' | 'error' | 'queue' | 'kv' | 'db' | 'dbtable' | 'table' | 'external' | 'aiagent' | 'code' | 'run';
|
|
2902
2909
|
id: string;
|
|
2903
2910
|
label: string;
|
|
2904
2911
|
/** `target`: what was reviewed; `reviewed-with`: a review its review was shown; `named`: the finding names it; `related`: the catalogs join it to the target. */
|
package/lib/event-loop.d.ts
CHANGED
|
@@ -82,6 +82,48 @@ export interface EventLoopSeries {
|
|
|
82
82
|
lastAt: number;
|
|
83
83
|
points: EventLoopPoint[];
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* A Node process's first minute — loading modules, wiring its container, opening its first connections. Stalls there are
|
|
87
|
+
* the price of a start, not of the work it serves: on FoodGuard (2026-10-28) 83% of the services' stalls and 800 of the
|
|
88
|
+
* 822 longer than a second came in it, so the Event loops charts read the two apart.
|
|
89
|
+
*/
|
|
90
|
+
export declare const LOOP_STARTUP_MS = 60000;
|
|
91
|
+
/**
|
|
92
|
+
* When a Node loop's process started: its id ends with the process's `performance.timeOrigin`
|
|
93
|
+
* (`node:<pid>:<thread>:<timeOrigin>`). A browser document's load is not a start in this sense — its stalls are what a
|
|
94
|
+
* user sees — so a browser loop, or an id without the time, has none.
|
|
95
|
+
*/
|
|
96
|
+
export declare function loopStartedAt(loopId: string): number | undefined;
|
|
97
|
+
/** Where in its process's life a moment of a Node loop fell: starting up (its first minute) or running, and how far in. */
|
|
98
|
+
export declare function loopPhaseAt(loopId: string, at: number): {
|
|
99
|
+
phase: 'startup' | 'running';
|
|
100
|
+
sinceStartMs: number;
|
|
101
|
+
} | undefined;
|
|
102
|
+
/** One part of a process's life over a window: its stalls, the longest, and the services that had the most. */
|
|
103
|
+
export interface EventLoopPhaseTotals {
|
|
104
|
+
stalls: number;
|
|
105
|
+
longestMs: number;
|
|
106
|
+
/** The services with the most stalls in it, at most five. */
|
|
107
|
+
services: Array<{
|
|
108
|
+
project: string;
|
|
109
|
+
stalls: number;
|
|
110
|
+
longestMs: number;
|
|
111
|
+
}>;
|
|
112
|
+
}
|
|
113
|
+
/** A window's stalls read apart: a Node process's first minute ({@link LOOP_STARTUP_MS}) and the work after it. */
|
|
114
|
+
export interface EventLoopPhaseWindow {
|
|
115
|
+
window: string;
|
|
116
|
+
from: number;
|
|
117
|
+
to: number;
|
|
118
|
+
running: EventLoopPhaseTotals;
|
|
119
|
+
startup: EventLoopPhaseTotals & {
|
|
120
|
+
/** Node processes that started in the window, and those of them with a stall in their first minute. */
|
|
121
|
+
starts: number;
|
|
122
|
+
stalledStarts: number;
|
|
123
|
+
};
|
|
124
|
+
/** The share of the window's stalls that were a start's, in percent; absent when it had none. */
|
|
125
|
+
startupSharePct?: number;
|
|
126
|
+
}
|
|
85
127
|
/** The stall incidents of one loop in one bucket, for a marker: what a click opens is read from `/api/event-loops`. */
|
|
86
128
|
export interface EventLoopStallMark {
|
|
87
129
|
loopId: string;
|
|
@@ -94,6 +136,8 @@ export interface EventLoopStallMark {
|
|
|
94
136
|
longestMs: number;
|
|
95
137
|
/** One of them was not responding when last observed. */
|
|
96
138
|
unresponsive: boolean;
|
|
139
|
+
/** These stalls began in the process's first {@link LOOP_STARTUP_MS}: a loop's bucket may hold one mark of each. */
|
|
140
|
+
startup?: true;
|
|
97
141
|
}
|
|
98
142
|
/** `/api/event-loops/history`: every loop's delay over a window, bucketed, with its stalls. */
|
|
99
143
|
export interface EventLoopHistory {
|
|
@@ -107,6 +151,8 @@ export interface EventLoopHistory {
|
|
|
107
151
|
/** More loops had samples in the window than are listed: the most recently seen are. */
|
|
108
152
|
seriesTruncated?: boolean;
|
|
109
153
|
stalls: EventLoopStallMark[];
|
|
154
|
+
/** The start the stall marks were split at ({@link LOOP_STARTUP_MS}); absent from an older lens, whose marks are not. */
|
|
155
|
+
startupMs?: number;
|
|
110
156
|
persistence: 'disk' | 'memory';
|
|
111
157
|
samplesHours: number;
|
|
112
158
|
retentionDays: number;
|
package/lib/event-loop.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export{};
|
|
1
|
+
"use strict";var a=Object.defineProperty;var r=(t,e)=>a(t,"name",{value:e,configurable:!0});var i=Object.defineProperty,s=r((t,e)=>i(t,"name",{value:e,configurable:!0}),"e");export const LOOP_STARTUP_MS=6e4;export function loopStartedAt(t){if(!t.startsWith("node:"))return;const e=Number(t.slice(t.lastIndexOf(":")+1));return Number.isFinite(e)&&e>0?e:void 0}r(loopStartedAt,"loopStartedAt"),s(loopStartedAt,"loopStartedAt");export function loopPhaseAt(t,e){const n=loopStartedAt(t);if(n===void 0)return;const o=Math.max(0,Math.round(e-n));return{phase:o<6e4?"startup":"running",sinceStartMs:o}}r(loopPhaseAt,"loopPhaseAt"),s(loopPhaseAt,"loopPhaseAt");
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { DashTeamKind } from './team.js';
|
|
2
|
+
/**
|
|
3
|
+
* Team design (planning/team/01-team-design.md): LensMCP reads a project — a dossier it builds itself, then Claude's
|
|
4
|
+
* own research with the code tools — and proposes the team that should work on it: the project as understood, its
|
|
5
|
+
* goals with evidence, whether it splits at all, the roles cut along its seams with their rules, the defaults it does
|
|
6
|
+
* not need, the flow, and the first goals. The person accepts on the Team page; only then are `.lensmcp/team/` files
|
|
7
|
+
* written. These are the shapes the run, the dashboard and MCP share; the model's answer is checked into them.
|
|
8
|
+
*/
|
|
9
|
+
export declare const TEAM_DESIGN_VERSION = 1;
|
|
10
|
+
/** A part of the project that can move on its own — or cannot: `coupling` says how much it depends on the others. */
|
|
11
|
+
export interface TeamDesignDomain {
|
|
12
|
+
/** Stable, kebab-case. */
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
/** What it is and does, in a sentence or two. */
|
|
16
|
+
what: string;
|
|
17
|
+
/** Repository paths it lives in. */
|
|
18
|
+
paths: string[];
|
|
19
|
+
/** Ids of the domains it depends on. */
|
|
20
|
+
dependsOn: string[];
|
|
21
|
+
coupling: 'low' | 'medium' | 'high';
|
|
22
|
+
}
|
|
23
|
+
/** A check the project already has — a command a role runs before it says done. */
|
|
24
|
+
export interface TeamDesignGate {
|
|
25
|
+
command: string;
|
|
26
|
+
what: string;
|
|
27
|
+
/** When it applies: "every change", "a change under apps/web", "before a release"… */
|
|
28
|
+
when: string;
|
|
29
|
+
/** LensMCP found the script or target it names (package.json scripts, a project's targets, CI). */
|
|
30
|
+
checked?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** What the project is working towards, from what it shows: docs, plans, recent commits, open issues, the lens. */
|
|
33
|
+
export interface TeamDesignGoal {
|
|
34
|
+
id: string;
|
|
35
|
+
title: string;
|
|
36
|
+
why: string;
|
|
37
|
+
/** `file:<path>[:line]`, `commit:<sha or subject>`, `lens:<what>` — where it was read. */
|
|
38
|
+
evidence: string[];
|
|
39
|
+
horizon: 'now' | 'next' | 'later';
|
|
40
|
+
}
|
|
41
|
+
/** One proposed role — a brief an agent can work from — in the shape of a `.lensmcp/team/roles/<id>.md` file. */
|
|
42
|
+
export interface TeamDesignRole {
|
|
43
|
+
id: string;
|
|
44
|
+
title: string;
|
|
45
|
+
summary: string;
|
|
46
|
+
/** Why this project needs it. */
|
|
47
|
+
why: string;
|
|
48
|
+
/** The default role it refines (builder, reviewer, …), when it does. */
|
|
49
|
+
basedOn?: string;
|
|
50
|
+
/** Paths it owns — where it works and others do not. */
|
|
51
|
+
owns: string[];
|
|
52
|
+
takes: DashTeamKind[];
|
|
53
|
+
creates: DashTeamKind[] | 'any';
|
|
54
|
+
wip: number;
|
|
55
|
+
worktree: 'own' | 'shared' | 'any';
|
|
56
|
+
/** How many agents to run in it, and whether to start them now or once the work is there. */
|
|
57
|
+
agents: number;
|
|
58
|
+
start: 'now' | 'later';
|
|
59
|
+
/** Its rules, as tagged sections (`<rules id note>…</rules>`). */
|
|
60
|
+
rules: string;
|
|
61
|
+
/** The LensMCP tools and resources it should reach for first. */
|
|
62
|
+
uses: string[];
|
|
63
|
+
/** Against the repository's current team: a new role, one that changes a role it has, or one it already has as is. */
|
|
64
|
+
status?: 'new' | 'changes' | 'same';
|
|
65
|
+
}
|
|
66
|
+
/** A default role this project does not need — accepted, it is written with `off: true`. */
|
|
67
|
+
export interface TeamDesignOff {
|
|
68
|
+
id: string;
|
|
69
|
+
why: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A hand-off, in the shape of `.lensmcp/team/flow.json`'s entries (libs/storage coordination/flow.ts). The work a chain
|
|
73
|
+
* is about is its nearest build or bug: `after` holds the hand-off for one of them, `paths` for work on those paths
|
|
74
|
+
* (prefixes or globs); `to: owner` sends it back to whoever did the work (`builder`: its older name); `join` makes it
|
|
75
|
+
* once, when every task of the completed one's kind under the same parent completed with this outcome.
|
|
76
|
+
*/
|
|
77
|
+
export interface TeamDesignHandOff {
|
|
78
|
+
on: {
|
|
79
|
+
kind: DashTeamKind;
|
|
80
|
+
outcome: string;
|
|
81
|
+
after?: 'build' | 'bug';
|
|
82
|
+
paths?: string[];
|
|
83
|
+
};
|
|
84
|
+
create: {
|
|
85
|
+
kind: DashTeamKind;
|
|
86
|
+
role?: string;
|
|
87
|
+
to?: 'owner' | 'builder';
|
|
88
|
+
join?: boolean;
|
|
89
|
+
title: string;
|
|
90
|
+
description: string;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Keep the flow the repository has, or replace it. */
|
|
94
|
+
export type TeamDesignFlow = {
|
|
95
|
+
keep: true;
|
|
96
|
+
why?: string;
|
|
97
|
+
} | {
|
|
98
|
+
keep: false;
|
|
99
|
+
handOffs: TeamDesignHandOff[];
|
|
100
|
+
why: string;
|
|
101
|
+
};
|
|
102
|
+
/** A goal to put on the board when the design is accepted, with the tasks it breaks into. */
|
|
103
|
+
export interface TeamDesignFirstGoal {
|
|
104
|
+
title: string;
|
|
105
|
+
description: string;
|
|
106
|
+
priority: number;
|
|
107
|
+
tasks: Array<{
|
|
108
|
+
kind: DashTeamKind;
|
|
109
|
+
role?: string;
|
|
110
|
+
title: string;
|
|
111
|
+
description: string;
|
|
112
|
+
priority: number;
|
|
113
|
+
paths: string[];
|
|
114
|
+
}>;
|
|
115
|
+
}
|
|
116
|
+
/** What LensMCP's checks did to the model's answer before anyone saw it — never silent. */
|
|
117
|
+
export interface TeamDesignCheck {
|
|
118
|
+
level: 'dropped' | 'changed' | 'warning';
|
|
119
|
+
what: string;
|
|
120
|
+
why: string;
|
|
121
|
+
}
|
|
122
|
+
/** The project as the design read it: when the project has moved on far enough, the page offers to design again. */
|
|
123
|
+
export interface TeamDesignBasis {
|
|
124
|
+
head: string | null;
|
|
125
|
+
branch: string | null;
|
|
126
|
+
/** The projects (Nx / workspace packages) it saw, by root. */
|
|
127
|
+
projects: string[];
|
|
128
|
+
trackedFiles: number;
|
|
129
|
+
at: number;
|
|
130
|
+
}
|
|
131
|
+
export interface TeamDesign {
|
|
132
|
+
version: typeof TEAM_DESIGN_VERSION;
|
|
133
|
+
project: {
|
|
134
|
+
name: string;
|
|
135
|
+
summary: string;
|
|
136
|
+
/** Who it serves. */
|
|
137
|
+
users: string;
|
|
138
|
+
stack: string[];
|
|
139
|
+
domains: TeamDesignDomain[];
|
|
140
|
+
qualityGates: TeamDesignGate[];
|
|
141
|
+
risks: string[];
|
|
142
|
+
};
|
|
143
|
+
goals: TeamDesignGoal[];
|
|
144
|
+
/** Does the project split into parallel work at all: a team, a small team (one builder and a checker), or one agent. */
|
|
145
|
+
fit: {
|
|
146
|
+
verdict: 'team' | 'small-team' | 'single-agent';
|
|
147
|
+
why: string;
|
|
148
|
+
parallel: string[];
|
|
149
|
+
};
|
|
150
|
+
roles: TeamDesignRole[];
|
|
151
|
+
off: TeamDesignOff[];
|
|
152
|
+
flow: TeamDesignFlow;
|
|
153
|
+
firstGoals: TeamDesignFirstGoal[];
|
|
154
|
+
/** What it could not assess, and why. */
|
|
155
|
+
limits: string[];
|
|
156
|
+
/** 0–1. */
|
|
157
|
+
confidence: number;
|
|
158
|
+
checks: TeamDesignCheck[];
|
|
159
|
+
basis: TeamDesignBasis;
|
|
160
|
+
}
|
|
161
|
+
/** A design run as the Team page shows it: the run (status, steps, cost — a DashAiRun's) and, once done, its design. */
|
|
162
|
+
export interface DashTeamDesignRun {
|
|
163
|
+
id: string;
|
|
164
|
+
status: 'queued' | 'running' | 'done' | 'failed';
|
|
165
|
+
startedAt: number;
|
|
166
|
+
finishedAt?: number;
|
|
167
|
+
costUsd?: number;
|
|
168
|
+
model?: string;
|
|
169
|
+
steps: string[];
|
|
170
|
+
error?: string;
|
|
171
|
+
refused?: 'no-key' | 'over-daily-cap' | 'no-room';
|
|
172
|
+
design?: TeamDesign;
|
|
173
|
+
/** The design was accepted (when, and what it wrote). */
|
|
174
|
+
accepted?: {
|
|
175
|
+
at: number;
|
|
176
|
+
files: string[];
|
|
177
|
+
goals: string[];
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** What the person keeps of a design. Roles they edited carry their own rules. */
|
|
181
|
+
export interface DashTeamDesignAccept {
|
|
182
|
+
runId: string;
|
|
183
|
+
roles: Array<{
|
|
184
|
+
id: string;
|
|
185
|
+
rules?: string;
|
|
186
|
+
}>;
|
|
187
|
+
/** Default roles to switch off. */
|
|
188
|
+
off: string[];
|
|
189
|
+
flow: boolean;
|
|
190
|
+
/** Write `.lensmcp/team/project.md`. */
|
|
191
|
+
brief: boolean;
|
|
192
|
+
/** Indexes into `firstGoals` to create on the board. */
|
|
193
|
+
goals: number[];
|
|
194
|
+
/** Overwrite files the repository already has (they are listed to the person first). */
|
|
195
|
+
force: boolean;
|
|
196
|
+
}
|
|
197
|
+
/** What accepting wrote, and what it kept because a file was already there and `force` was not given. */
|
|
198
|
+
export interface DashTeamDesignAccepted {
|
|
199
|
+
written: string[];
|
|
200
|
+
kept: string[];
|
|
201
|
+
goals: string[];
|
|
202
|
+
problems: string[];
|
|
203
|
+
/** Whether the agents on the team were told to agent_join again (a team file that changed). */
|
|
204
|
+
notified?: boolean;
|
|
205
|
+
}
|
|
206
|
+
/** Whether a design still fits the project: what changed since it was made, and whether that is enough to design again. */
|
|
207
|
+
export interface DashTeamDesignFit {
|
|
208
|
+
fits: boolean;
|
|
209
|
+
/** What moved since, in words: commits since its HEAD, projects added or gone, tracked files. */
|
|
210
|
+
changed: string[];
|
|
211
|
+
/** The project as it is now. */
|
|
212
|
+
now: TeamDesignBasis;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* `GET /api/team/design` — and `team_design` over MCP: the newest run whole (its design once done), the newest design
|
|
216
|
+
* when that is another run, every run (without their designs), whether a key can pay for one, and — when asked
|
|
217
|
+
* (`?fits=1`) — whether the newest design still fits the project.
|
|
218
|
+
*/
|
|
219
|
+
export interface DashTeamDesignState {
|
|
220
|
+
/** A working key can pay for a design: an Anthropic key, or one of the provider of the model Settings chose for it. */
|
|
221
|
+
ready: boolean;
|
|
222
|
+
latest?: DashTeamDesignRun;
|
|
223
|
+
/** The newest finished design, when the newest run is another (one running now, one that failed). */
|
|
224
|
+
done?: DashTeamDesignRun;
|
|
225
|
+
/** Newest first, at most 20, without their designs. */
|
|
226
|
+
runs: DashTeamDesignRun[];
|
|
227
|
+
fits?: DashTeamDesignFit;
|
|
228
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";export const TEAM_DESIGN_VERSION=1;
|