@chatpanel/events 0.101.0 → 0.103.1

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.
Files changed (138) hide show
  1. package/adapters.js +1 -83
  2. package/adaptive-tool-policy.js +1 -45
  3. package/agent.js +2 -255
  4. package/attribution.js +1 -132
  5. package/backup-envelope.js +1 -221
  6. package/board-tool.js +2 -165
  7. package/budget.js +1 -116
  8. package/capability.js +1 -126
  9. package/citations.js +5 -127
  10. package/client-prefs.js +1 -148
  11. package/context-attachments.js +8 -144
  12. package/cowriter-router.js +1 -83
  13. package/cowriter-writer.js +19 -193
  14. package/cowriter.js +1 -190
  15. package/curate.js +3 -509
  16. package/distance.js +1 -124
  17. package/engine.js +1 -131
  18. package/entitlement.js +1 -332
  19. package/entity.js +1 -354
  20. package/event.js +1 -175
  21. package/extraction.js +4 -293
  22. package/failover.js +1 -93
  23. package/find-tool.js +1 -66
  24. package/flowchart.js +2 -644
  25. package/gate.js +1 -74
  26. package/harness.js +4 -101
  27. package/index.js +1 -345
  28. package/invariants.js +1 -174
  29. package/job.js +3 -147
  30. package/kernel.js +1 -255
  31. package/knowledge-derive.js +1 -267
  32. package/knowledge.js +3 -230
  33. package/library.js +11 -280
  34. package/loop.js +1 -132
  35. package/manifest.js +1 -107
  36. package/markdown-authoring.js +11 -288
  37. package/markdown-render.js +0 -0
  38. package/mcp-client.js +8 -358
  39. package/mcp-dispatch.js +1 -52
  40. package/mcp-errors.js +1 -106
  41. package/mcp-manager.js +1 -127
  42. package/media-transcript.js +8 -531
  43. package/meeting-analyzers.js +1 -83
  44. package/meeting-insights.js +7 -164
  45. package/meeting-shape.js +1 -282
  46. package/meeting-text.js +5 -94
  47. package/memory.js +2 -638
  48. package/model-candidates.js +1 -358
  49. package/model-health.js +1 -182
  50. package/model-ledger.js +1 -228
  51. package/model-picker.js +1 -188
  52. package/note-actions.js +15 -246
  53. package/note-graph.js +1 -158
  54. package/note-links.js +1 -68
  55. package/note-mentions.js +8 -114
  56. package/note-plan.js +9 -107
  57. package/note-research.js +1 -135
  58. package/observability.js +1 -119
  59. package/omni.js +1 -125
  60. package/order.js +1 -78
  61. package/package.json +1 -4
  62. package/pdf-layout.js +7 -253
  63. package/project-run.js +20 -444
  64. package/project.js +1 -170
  65. package/promotion.js +1 -171
  66. package/queue.js +1 -98
  67. package/reach.js +1 -23
  68. package/recipe-tool.js +2 -161
  69. package/recipe.js +1 -264
  70. package/record-list.js +2 -110
  71. package/recruit.js +3 -456
  72. package/redaction-tokens.js +1 -61
  73. package/ref.js +1 -55
  74. package/registry.js +1 -240
  75. package/route-graph.js +1 -115
  76. package/route-strategies.js +2 -232
  77. package/router.js +2 -880
  78. package/rrf.js +1 -89
  79. package/rules.js +1 -142
  80. package/schedule.js +1 -663
  81. package/scm-connection.js +1 -180
  82. package/scopes.js +1 -12
  83. package/scorecard.js +1 -335
  84. package/search-engines.js +1 -81
  85. package/skill-manifest.js +1 -330
  86. package/skill-scan.js +3 -209
  87. package/skill-sources.js +1 -162
  88. package/skill-vars.js +2 -252
  89. package/slash-commands.js +1 -158
  90. package/source-gate.js +1 -92
  91. package/sources-retrieval.js +5 -186
  92. package/sources.js +1 -273
  93. package/store.js +1 -171
  94. package/structured.js +4 -901
  95. package/subject-kinds.js +1 -5
  96. package/subject-name.js +1 -96
  97. package/sync-plan.js +1 -213
  98. package/synthesis.js +3 -123
  99. package/tags.js +1 -203
  100. package/team-board.js +6 -337
  101. package/team-cache.js +2 -63
  102. package/team-observe.js +1 -118
  103. package/team-org.js +1 -348
  104. package/team-plan.js +4 -131
  105. package/team-record.js +1 -225
  106. package/team-run.js +9 -652
  107. package/team-subtask.js +2 -191
  108. package/team-task.js +2 -124
  109. package/team-tool.js +2 -196
  110. package/team-trail.js +2 -73
  111. package/team-worklog.js +2 -149
  112. package/team.js +1 -302
  113. package/text-search.js +1 -155
  114. package/theme.js +17 -154
  115. package/titles.js +4 -285
  116. package/tool-discovery.js +1 -87
  117. package/tool-dispatch.js +5 -230
  118. package/tool-groups.js +1 -81
  119. package/tool-hints.js +10 -185
  120. package/tool-loop-guard.js +4 -182
  121. package/tool-need.js +2 -124
  122. package/tool-result.js +2 -438
  123. package/tool-round.js +0 -0
  124. package/tool-schema.js +1 -155
  125. package/tool-traits.js +1 -182
  126. package/toolset.js +2 -88
  127. package/trajectory.js +1 -530
  128. package/turn-loop.js +4 -440
  129. package/upcast.js +1 -37
  130. package/vault.js +2 -250
  131. package/view.js +1 -88
  132. package/voice-intents.js +2 -1488
  133. package/voice-speaker.js +1 -98
  134. package/weather-tool.js +1 -65
  135. package/weather.js +2 -211
  136. package/web-search-tool.js +14 -102
  137. package/web-search.js +1 -160
  138. package/widget.js +1 -153
package/pdf-layout.js CHANGED
@@ -1,258 +1,12 @@
1
- // A PDF's text items something a person (or a model) can read.
2
- //
3
- // A PDF does not contain paragraphs. It contains positioned glyph runs: "Introduction" at
4
- // (72, 690), "and this is" at (72, 674), "why" at (140, 674). Every PDF engine — pdf.js in
5
- // a browser, a native renderer on mobile, a CLI extractor on the bridge — hands back that
6
- // same list of runs with the same transform matrix, and every one of them leaves the job of
7
- // turning runs into READING ORDER to the caller. Done naively (join the items with spaces)
8
- // a two-column paper becomes an interleaved ransom note, and every hyphenated line break
9
- // becomes a broken word the model then cannot match against a query.
10
- //
11
- // So the reconstruction lives here — pure, testable, engine-agnostic — and the client
12
- // contributes only the engine that produced the items.
13
- //
14
- // THE ITEM SHAPE (pdf.js `getTextContent().items`, and what other engines are adapted to):
15
- // { str, transform: [a, b, c, d, x, y], width, height, hasEOL }
16
- // x/y are in PDF user space: y grows UPWARD from the bottom of the page.
1
+ function d(e){const l=Array.isArray(e?.transform)?e.transform:[1,0,0,1,0,0];return{text:String(e?.str??""),x:Number(l[4])||0,y:Number(l[5])||0,width:Number(e?.width)||0,size:Math.abs(Number(l[3]))||Number(e?.height)||0,eol:!!e?.hasEOL}}function m(e,{tolerance:l=.5}={}){const s=(Array.isArray(e)?e:[]).map(d).filter(t=>t.text!=="");if(!s.length)return[];const i=[];for(const t of s){const n=Math.max(1,(t.size||10)*l),o=i[i.length-1];o&&Math.abs(o.y-t.y)<=n?(o.items.push(t),t.size>o.size&&(o.size=t.size)):i.push({y:t.y,size:t.size||10,items:[t]})}return i.map(t=>{const n=t.items.slice().sort((o,c)=>o.x-c.x);return{y:t.y,size:t.size,x:n[0].x,right:Math.max(...n.map(o=>o.x+o.width)),text:z(n,t.size)}})}function z(e,l){let s="",i=null;for(const t of e)i!==null&&t.x-i>(l||10)*.2&&!/\s$/.test(s)&&!/^\s/.test(t.text)&&(s+=" "),s+=t.text,i=t.x+t.width;return s.replace(/\s+/g," ").trim()}function b(e,{pageWidth:l=0}={}){const s=(Array.isArray(e)?e:[]).slice();if(s.length<6)return s.sort((r,a)=>a.y-r.y);const t=(l||Math.max(...s.map(r=>r.right)))/2,n=s.filter(r=>r.right<=t*1.05),o=s.filter(r=>r.x>=t*.95),c=s.filter(r=>!n.includes(r)&&!o.includes(r));if(!(n.length>=3&&o.length>=3&&c.length<=s.length*.2))return s.sort((r,a)=>a.y-r.y);const h=(r,a)=>a.y-r.y;return[...c.filter(r=>r.y>Math.max(...n.map(a=>a.y),0)).sort(h),...n.sort(h),...o.sort(h),...c.filter(r=>r.y<=Math.max(...n.map(a=>a.y),0)).sort(h)]}function M(e){const l=(Array.isArray(e)?e:[]).filter(t=>t&&t.text);if(!l.length)return[];const s=[];for(const t of l){const n=s[s.length-1],o=n?.lines[n.lines.length-1],c=n&&Math.abs(n.lines[0].x-t.x)<=(t.size||10)*1.5,f=o&&Math.abs((o.size||10)-(t.size||10))<=(o.size||10)*.15;c&&f?n.lines.push(t):s.push({lines:[t]})}const i=[];for(const t of s){const n=t.lines,o=[];for(let a=1;a<n.length;a++)o.push(Math.abs(n[a-1].y-n[a].y));const c=A(o,.3)||(n[0].size||10)*1.2,f=Math.max(...n.map(a=>a.right)),h=Math.min(...n.map(a=>a.x));let r="";for(let a=0;a<n.length;a++){const u=n[a],p=n[a-1],g=p?Math.abs(p.y-u.y):0,x=u.x>h+(u.size||10)*.8,y=p?p.right<f-(u.size||10)*3:!1;r&&(g>c*1.5||x||y)?(i.push(r),r=u.text):r?/(\w)-$/.test(r)?r=r.replace(/-$/,"")+u.text.replace(/^\s+/,""):r+=" "+u.text:r=u.text}r&&i.push(r)}return i.map(t=>t.replace(/\s+/g," ").trim()).filter(Boolean)}function A(e,l){const s=e.filter(i=>Number.isFinite(i)&&i>0).sort((i,t)=>i-t);return s.length?s[Math.min(s.length-1,Math.floor(l*(s.length-1)))]:0}function S(e,{pageWidth:l=0,...s}={}){const i=m(e,s);return M(b(i,{pageWidth:l})).join(`
17
2
 
18
- /** One text item the numbers this file reasons about. */
19
- function place(item) {
20
- const t = Array.isArray(item?.transform) ? item.transform : [1, 0, 0, 1, 0, 0];
21
- return {
22
- text: String(item?.str ?? ''),
23
- x: Number(t[4]) || 0,
24
- y: Number(t[5]) || 0,
25
- width: Number(item?.width) || 0,
26
- // The transform's vertical scale is the font size; `height` is often 0 on the items
27
- // real documents produce, and a zero line height makes every line one paragraph.
28
- size: Math.abs(Number(t[3])) || Number(item?.height) || 0,
29
- eol: !!item?.hasEOL,
30
- };
31
- }
3
+ `)}const $=2e5;function w(e){const l=Array.isArray(e)?e:[];return l.length?l.reduce((i,t)=>i+String(t?.text||"").length,0)<l.length*40:!1}function F({meta:e={},pages:l=[],maxChars:s=$}={}){const i=Array.isArray(l)?l:[],t=[];e.title&&t.push(`# ${e.title}`);const n=[];e.author&&n.push(`Author: ${e.author}`),i.length&&n.push(`Pages: ${e.pageCount||i.length}`),e.url&&n.push(`URL: ${e.url}`),n.length&&t.push(n.join(" · "));const o=i.map(h=>{const r=String(h?.text||"").trim();return r?`[page ${h.page}]
4
+ ${r}`:""}).filter(Boolean).join(`
32
5
 
33
- /**
34
- * Items → lines, by shared baseline.
35
- *
36
- * Tolerance is a FRACTION OF THE FONT SIZE, not a fixed number of points: a footnote at 7pt
37
- * and a heading at 24pt do not agree on what "the same line" means, and a constant that
38
- * works for body text either splits headings or merges footnotes.
39
- */
40
- export function linesFromItems(items, { tolerance = 0.5 } = {}) {
41
- const placed = (Array.isArray(items) ? items : []).map(place).filter((p) => p.text !== '');
42
- if (!placed.length) return [];
43
- const lines = [];
44
- for (const p of placed) {
45
- const tol = Math.max(1, (p.size || 10) * tolerance);
46
- // Compare against the most recent line only: items arrive in content-stream order, so a
47
- // matching baseline further back belongs to an earlier column, not to this line.
48
- const last = lines[lines.length - 1];
49
- if (last && Math.abs(last.y - p.y) <= tol) {
50
- last.items.push(p);
51
- if (p.size > last.size) last.size = p.size;
52
- } else {
53
- lines.push({ y: p.y, size: p.size || 10, items: [p] });
54
- }
55
- }
56
- return lines.map((line) => {
57
- const sorted = line.items.slice().sort((a, b) => a.x - b.x);
58
- return {
59
- y: line.y,
60
- size: line.size,
61
- x: sorted[0].x,
62
- right: Math.max(...sorted.map((i) => i.x + i.width)),
63
- text: joinRun(sorted, line.size),
64
- };
65
- });
66
- }
6
+ `);let c=t.length?`${t.join(`
67
7
 
68
- /**
69
- * Glyph runs on one line → a string.
70
- *
71
- * PDFs encode a space either as a space character or as a horizontal gap with nothing in
72
- * it, and which one you get depends on the producer. Joining on the character alone loses
73
- * every word break in documents from the second kind; joining every item with a space puts
74
- * one inside every kerned pair in documents from the first.
75
- */
76
- function joinRun(sorted, size) {
77
- let out = '';
78
- let prevRight = null;
79
- for (const item of sorted) {
80
- if (prevRight !== null) {
81
- const gap = item.x - prevRight;
82
- const needsSpace = gap > (size || 10) * 0.2;
83
- if (needsSpace && !/\s$/.test(out) && !/^\s/.test(item.text)) out += ' ';
84
- }
85
- out += item.text;
86
- prevRight = item.x + item.width;
87
- }
88
- return out.replace(/\s+/g, ' ').trim();
89
- }
8
+ `)}
90
9
 
91
- /**
92
- * Lines → columns, in reading order.
93
- *
94
- * Two-column layouts are the case that makes naive extraction useless, and they are most of
95
- * academic and technical PDF reading. A column is detected as a vertical band that lines
96
- * cluster into; if the page is one column (or the split is not clean) this returns the lines
97
- * unchanged rather than inventing a split, because a wrong split is worse than none.
98
- */
99
- export function orderLines(lines, { pageWidth = 0 } = {}) {
100
- const list = (Array.isArray(lines) ? lines : []).slice();
101
- if (list.length < 6) return list.sort((a, b) => b.y - a.y);
102
- const width = pageWidth || Math.max(...list.map((l) => l.right));
103
- const mid = width / 2;
104
- const left = list.filter((l) => l.right <= mid * 1.05);
105
- const right = list.filter((l) => l.x >= mid * 0.95);
106
- const spanning = list.filter((l) => !left.includes(l) && !right.includes(l));
107
- // A real two-column page has substantial text on BOTH sides and few lines crossing the
108
- // gutter. Anything else is a single column with a stray indent or a wide table.
109
- const twoColumn = left.length >= 3 && right.length >= 3
110
- && spanning.length <= list.length * 0.2;
111
- if (!twoColumn) return list.sort((a, b) => b.y - a.y);
112
- const byY = (a, b) => b.y - a.y;
113
- // Headers and footers that span the gutter keep their vertical position relative to the
114
- // column they sit above; sorting them into the left column first is the conventional and
115
- // least-surprising reading order.
116
- return [...spanning.filter((l) => l.y > Math.max(...left.map((x) => x.y), 0)).sort(byY),
117
- ...left.sort(byY), ...right.sort(byY),
118
- ...spanning.filter((l) => l.y <= Math.max(...left.map((x) => x.y), 0)).sort(byY)];
119
- }
10
+ ${o}`:o,f=!1;return c.length>s&&(c=`${c.slice(0,s)}
120
11
 
121
- /**
122
- * Lines → paragraphs.
123
- *
124
- * A new paragraph is a bigger-than-usual vertical gap, a first-line indent, or a line that
125
- * ended well short of the right margin. A word broken across lines with a hyphen is put
126
- * back together — otherwise "distri-\nbuted" never matches a search for "distributed", and
127
- * the model reads a word that does not exist.
128
- *
129
- * MARGINS ARE MEASURED PER BLOCK, NOT PER PAGE. On a two-column page every line in the left
130
- * column ends far short of the page's right edge, so a page-wide margin makes every single
131
- * line "short" and therefore its own paragraph — which is what a first attempt at this did.
132
- * Lines that share a left edge are one block; a change of block is itself a paragraph break,
133
- * because it is a change of column or of indentation level.
134
- */
135
- export function paragraphsFromLines(lines) {
136
- const list = (Array.isArray(lines) ? lines : []).filter((l) => l && l.text);
137
- if (!list.length) return [];
138
-
139
- // A block is a run of lines that share a left edge AND a font size. The size half is not
140
- // cosmetic: a 14pt heading is wider than the 10pt lines under it, so a block containing
141
- // both takes its right margin from the heading — and then every body line is "short" and
142
- // every one of them becomes its own paragraph.
143
- const blocks = [];
144
- for (const line of list) {
145
- const cur = blocks[blocks.length - 1];
146
- const prev = cur?.lines[cur.lines.length - 1];
147
- const sameEdge = cur && Math.abs(cur.lines[0].x - line.x) <= (line.size || 10) * 1.5;
148
- const sameSize = prev && Math.abs((prev.size || 10) - (line.size || 10)) <= (prev.size || 10) * 0.15;
149
- if (sameEdge && sameSize) cur.lines.push(line);
150
- else blocks.push({ lines: [line] });
151
- }
152
-
153
- const paras = [];
154
- for (const block of blocks) {
155
- const rows = block.lines;
156
- const gaps = [];
157
- for (let i = 1; i < rows.length; i++) gaps.push(Math.abs(rows[i - 1].y - rows[i].y));
158
- // A LOW percentile, not the median: in a three-line block whose second gap IS the
159
- // paragraph break, the median sits between the line gap and the break and neither is
160
- // then unusual. The common gap is a line gap, and it lives at the bottom of the range.
161
- const typical = percentile(gaps, 0.3) || (rows[0].size || 10) * 1.2;
162
- const rightEdge = Math.max(...rows.map((l) => l.right));
163
- const leftEdge = Math.min(...rows.map((l) => l.x));
164
-
165
- let cur = '';
166
- for (let i = 0; i < rows.length; i++) {
167
- const line = rows[i];
168
- const prev = rows[i - 1];
169
- const gap = prev ? Math.abs(prev.y - line.y) : 0;
170
- const indented = line.x > leftEdge + (line.size || 10) * 0.8;
171
- const prevShort = prev ? prev.right < rightEdge - (line.size || 10) * 3 : false;
172
- if (cur && (gap > typical * 1.5 || indented || prevShort)) {
173
- paras.push(cur);
174
- cur = line.text;
175
- } else if (!cur) {
176
- cur = line.text;
177
- } else if (/(\w)-$/.test(cur)) {
178
- cur = cur.replace(/-$/, '') + line.text.replace(/^\s+/, '');
179
- } else {
180
- cur += ' ' + line.text;
181
- }
182
- }
183
- if (cur) paras.push(cur);
184
- }
185
- return paras.map((p) => p.replace(/\s+/g, ' ').trim()).filter(Boolean);
186
- }
187
-
188
- /** Nearest-rank percentile of the positive values, 0 when there are none. */
189
- function percentile(nums, q) {
190
- const list = nums.filter((n) => Number.isFinite(n) && n > 0).sort((a, b) => a - b);
191
- if (!list.length) return 0;
192
- return list[Math.min(list.length - 1, Math.floor(q * (list.length - 1)))];
193
- }
194
-
195
- /** One page's items → its text. */
196
- export function pageTextFromItems(items, { pageWidth = 0, ...opts } = {}) {
197
- const lines = linesFromItems(items, opts);
198
- return paragraphsFromLines(orderLines(lines, { pageWidth })).join('\n\n');
199
- }
200
-
201
- export const PDF_MAX_CHARS = 200_000;
202
-
203
- /**
204
- * Is this PDF a picture of a document rather than a document?
205
- *
206
- * A scanned page has a text layer of nothing, and every extractor answers with an empty
207
- * string. Returning that silently is the worst outcome: the user sees a summary of nothing
208
- * and no explanation. Detecting it lets the caller say "this needs OCR" — which is an
209
- * answer, where an empty attachment is a mystery.
210
- */
211
- export function looksScanned(pages) {
212
- const list = Array.isArray(pages) ? pages : [];
213
- if (!list.length) return false;
214
- const chars = list.reduce((n, p) => n + String(p?.text || '').length, 0);
215
- return chars < list.length * 40;
216
- }
217
-
218
- /**
219
- * Pages → the document a caller attaches.
220
- *
221
- * Page markers are kept because a citation into a 90-page PDF is worth nothing without one,
222
- * and because they are the only thing that tells a model the document has an order at all.
223
- */
224
- export function buildPdfDocument({ meta = {}, pages = [], maxChars = PDF_MAX_CHARS } = {}) {
225
- const list = Array.isArray(pages) ? pages : [];
226
- const head = [];
227
- if (meta.title) head.push(`# ${meta.title}`);
228
- const facts = [];
229
- if (meta.author) facts.push(`Author: ${meta.author}`);
230
- if (list.length) facts.push(`Pages: ${meta.pageCount || list.length}`);
231
- if (meta.url) facts.push(`URL: ${meta.url}`);
232
- if (facts.length) head.push(facts.join(' · '));
233
-
234
- const body = list
235
- .map((p) => {
236
- const text = String(p?.text || '').trim();
237
- return text ? `[page ${p.page}]\n${text}` : '';
238
- })
239
- .filter(Boolean)
240
- .join('\n\n');
241
-
242
- let text = head.length ? `${head.join('\n\n')}\n\n${body}` : body;
243
- let truncated = false;
244
- if (text.length > maxChars) {
245
- text = `${text.slice(0, maxChars)}\n\n…[PDF truncated at ${maxChars} characters]`;
246
- truncated = true;
247
- }
248
- return {
249
- title: meta.title || 'PDF',
250
- url: meta.url || '',
251
- pageCount: meta.pageCount || list.length,
252
- pagesRead: list.length,
253
- scanned: looksScanned(list),
254
- truncated,
255
- text,
256
- chars: text.length,
257
- };
258
- }
12
+ …[PDF truncated at ${s} characters]`,f=!0),{title:e.title||"PDF",url:e.url||"",pageCount:e.pageCount||i.length,pagesRead:i.length,scanned:w(i),truncated:f,text:c,chars:c.length}}export{$ as PDF_MAX_CHARS,F as buildPdfDocument,m as linesFromItems,w as looksScanned,b as orderLines,S as pageTextFromItems,M as paragraphsFromLines};