@x-otto/interchange 0.1.0-alpha.0 → 0.1.0-alpha.2

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/README.md CHANGED
@@ -1,13 +1,13 @@
1
- # @otto/interchange
1
+ # @x-otto/interchange
2
2
 
3
3
  > Provider-neutral protocol type single source of truth. Message, StreamEvent, Model, Tool, Session Event types — zero dependencies.
4
4
 
5
- `@otto/interchange` (formerly `@otto/protocol`, renamed/merged) is the zero-dependency pure type leaf package that defines the canonical protocol types shared across the entire monorepo: `Message`, `StreamEvent`, `Model`, `Usage`, `ToolCall`, `AgentSessionEvent`, `GrillQuestion`, sigil input types, and more. Every package that deals with LLM provider communication, session events, or user input contracts imports from here.
5
+ `@x-otto/interchange` (formerly `@x-otto/protocol`, renamed/merged) is the zero-dependency pure type leaf package that defines the canonical protocol types shared across the entire monorepo: `Message`, `StreamEvent`, `Model`, `Usage`, `ToolCall`, `AgentSessionEvent`, `GrillQuestion`, sigil input types, and more. Every package that deals with LLM provider communication, session events, or user input contracts imports from here.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- pnpm add @otto/interchange
10
+ pnpm add @x-otto/interchange
11
11
  ```
12
12
 
13
13
  ## Usage
@@ -26,8 +26,8 @@ import type {
26
26
  NormalizedImage,
27
27
  SigilEntry,
28
28
  SigilKind,
29
- } from '@otto/interchange'
30
- import { hasToolCalls, normalizeImageData, sigilOf } from '@otto/interchange'
29
+ } from '@x-otto/interchange'
30
+ import { hasToolCalls, normalizeImageData, sigilOf } from '@x-otto/interchange'
31
31
 
32
32
  // Inspect a message for tool calls
33
33
  const msg: AssistantMessage = { role: 'assistant', content: [...] }
@@ -83,7 +83,7 @@ const entry: SigilEntry = { label: 'File', value: '/path/to/file', kind: 'file',
83
83
  ### Input Types (input.ts)
84
84
  - `SigilKind` / `SigilPrefix` — `'#'` and `'@'` trigger types
85
85
  - `SigilEntry` / `SigilSource` / `SigilProvider` — input enrichment entries
86
- - `SigilResolver` — resolve function for runtime chip expansion (RFC-210 D2)
86
+ - `SigilResolver` — resolve function for runtime chip expansion
87
87
  - `SigilTrigger` / `DismissedSigil` — trigger detection state machine
88
88
  - `evaluateSigilTrigger` / `extractSigilPrefix` / `applySigilCompletion` — pure sigil logic functions
89
89
 
package/dist/index.d.ts CHANGED
@@ -164,7 +164,7 @@ interface ToolResultMessage<T extends unknown = unknown> {
164
164
  }
165
165
  /**
166
166
  * RFC-144 M1:mid-conversation system 通知消息——**真正进 provider 请求**的协议消息类型
167
- * (区别于 `@otto/hook-contracts` 的 `SystemAgentMessage`,后者是 hooks/UI 域、
167
+ * (区别于 `@x-otto/hook-contracts` 的 `SystemAgentMessage`,后者是 hooks/UI 域、
168
168
  * convertToLLM 类型边界显式过滤、绝不进 provider 请求)。
169
169
  *
170
170
  * 用途:lifecycleAsync 工具(RFC-144)真正完成时,追加一条独立消息告知 LLM"运营方观察到
@@ -198,14 +198,14 @@ interface SystemNotificationMessage {
198
198
  type Message = UserMessage | AssistantMessage | ToolResultMessage | SystemNotificationMessage;
199
199
  /**
200
200
  * 历史压缩摘要消息的标记前缀——单一真相源(跨包协议常量)。
201
- * 生产者 @otto/memory 注入摘要消息时用、压缩去重(isSummaryMessage)时识别;消费者
202
- * @otto/agent 的 ContextPipeline 从会话树恢复摘要时用同一前缀重建摘要消息。放在协议叶子,
201
+ * 生产者 @x-otto/memory 注入摘要消息时用、压缩去重(isSummaryMessage)时识别;消费者
202
+ * @x-otto/agent 的 ContextPipeline 从会话树恢复摘要时用同一前缀重建摘要消息。放在协议叶子,
203
203
  * 避免双份字面量漂移导致 isSummaryMessage 失配。
204
204
  */
205
205
  declare const CONVERSATION_SUMMARY_PREFIX = "[Conversation Summary]";
206
206
  /**
207
207
  * 从 assistant 消息的内容块中提取全部 tool_call 项(终局架构 review 2026-07-10 P0-3:
208
- * 从 @otto/ai 物理迁移——此前是 @otto/ai 唯一自己实现(非 re-export)的两个函数,只依赖
208
+ * 从 @x-otto/ai 物理迁移——此前是 @x-otto/ai 唯一自己实现(非 re-export)的两个函数,只依赖
209
209
  * AssistantMessage/ToolCall,真源本就该在协议叶子而非模型注册中枢)。
210
210
  */
211
211
  declare function getToolCallsByAssistantMessage(message: AssistantMessage): ToolCall[];
@@ -423,7 +423,29 @@ type ModelSpec = string | {
423
423
  //#endregion
424
424
  //#region src/model-capability.d.ts
425
425
  /**
426
- * 该模型是否支持图像输入。
426
+ * 能力事实三态(RFC-374 M2)。`supported` 携带证据值;`unsupported` = 声明了但不具备;
427
+ * `unknown` = 未声明/无数据——消费方必须显式选择 unknown 的处置策略
428
+ * (保守降级 or 乐观放行),禁止把 unknown 静默当 unsupported。
429
+ */
430
+ type CapabilityFact<T> = {
431
+ state: 'supported';
432
+ value: T;
433
+ } | {
434
+ state: 'unsupported';
435
+ } | {
436
+ state: 'unknown';
437
+ };
438
+ /**
439
+ * 模型图像输入能力的三态事实。
440
+ * - `input` 明确包含 `'image'` → supported;
441
+ * - `input` 非空且不含 `'image'` → unsupported(manifest 声明了文本-only);
442
+ * - `input` 为空数组(manifest 未声明 input)→ unknown——既有的 fail-safe 投影
443
+ * 把它当「不支持」(保守降级,见 modelSupportsImages),但事实层保留三态供
444
+ * 选型收据/诊断区分「这个模型确实没有视觉」与「声明缺失」。
445
+ */
446
+ declare function modelImageCapability(model: Pick<Model, 'input'>): CapabilityFact<true>;
447
+ /**
448
+ * 该模型是否支持图像输入(fail-safe 投影,RFC-181 语义保留)。
427
449
  *
428
450
  * 保守默认:仅当 `input` 数组**明确**包含 `'image'` 时返回 `true`;`input`
429
451
  * 缺失/为空数组时返回 `false`(RFC-181 §3 D1/§4 规则 2——第三方插件 manifest
@@ -432,6 +454,49 @@ type ModelSpec = string | {
432
454
  * 可通过补全 manifest 声明恢复)。
433
455
  */
434
456
  declare function modelSupportsImages(model: Pick<Model, 'input'>): boolean;
457
+ /**
458
+ * 模型能力事实快照(RFC-374 M2 选型收据用)。字段按能力域扩展——
459
+ * 新增能力域(工具/推理/上下文)时在此加字段,消费方(选型收据/诊断面板)
460
+ * 共享同一快照形状,杜绝每处各自拼一份「这个模型能干什么」。
461
+ */
462
+ interface ModelCapabilityFacts {
463
+ vision: CapabilityFact<true>;
464
+ }
465
+ /** 收集某模型的能力事实快照(纯函数,无 I/O)。 */
466
+ declare function collectModelCapabilityFacts(model: Pick<Model, 'input'>): ModelCapabilityFacts;
467
+ //#endregion
468
+ //#region src/context-fragment.d.ts
469
+ /**
470
+ * context-fragment.ts —— 有界上下文片段类型层(RFC-374 M3)。
471
+ *
472
+ * 借鉴 DeepSeek-TUI `fragments`(crates/core/src/fragments.rs:1-40):每一类注入
473
+ * system prompt 的上下文片段都有 typed id + 字节硬顶 + 截断语义,杜绝各注入点
474
+ * 局部常量的漂移。otto 侧的排序权威在 `@x-otto/runtime` 的 SYSTEM_PROMPT_SECTIONS
475
+ * (priority + stable/volatile lane + 本类型的 budgetBytes),本文件只提供
476
+ * 「片段形状 + 截断原语」的纯类型/纯函数叶。
477
+ *
478
+ * 截断语义(安全红线,与 RFC-142 互补):截断产物是**纯文本**(截断点 + 简短后缀),
479
+ * 绝不发明结构化 JSON 元数据键(模型会误学模仿引发上下文自我污染,见 docs/
480
+ * CODE-STYLE.md §6b)。片段内容被截断只损失尾部信息,不改变已注入部分的可读性。
481
+ */
482
+ /** system prompt 注入片段的类型化 id(与 SYSTEM_PROMPT_SECTIONS 的字段一一对应)。 */
483
+ type ContextFragmentId = 'toolGuidance' | 'skillLoopGuidance' | 'environment' | 'memoryIndex' | 'skillCatalog' | 'lesson' | 'memoryDelta' | 'todoReminder' | 'docSyncReminder';
484
+ /** 有界上下文片段:id + 内容。预算声明在 SYSTEM_PROMPT_SECTIONS(runtime 包)。 */
485
+ interface ContextFragment {
486
+ id: ContextFragmentId;
487
+ content: string;
488
+ }
489
+ /** UTF-8 字节长度(预算口径——字节硬顶,与 DeepSeek-TUI MAX_FRAGMENT_BYTES 同语义)。 */
490
+ declare function contextFragmentBytes(content: string): number;
491
+ /**
492
+ * 把片段内容裁剪到字节预算内。超预算时保留前缀(已注入部分完整可读)并追加纯文本
493
+ * 截断后缀;未超预算原样返回。`budgetBytes <= 0` 时返回仅含后缀的片段(fail-closed
494
+ * 而非静默放行)。返回新对象,不修改入参。
495
+ *
496
+ * 实现:Buffer 按字节截断后回退到完整 UTF-8 字符边界(continuation byte 0b10xxxxxx
497
+ * 不回切),避免多字节字符(CJK 等)被劈成乱码。
498
+ */
499
+ declare function boundFragment(fragment: ContextFragment, budgetBytes: number): ContextFragment;
435
500
  //#endregion
436
501
  //#region src/tool.d.ts
437
502
  /**
@@ -465,6 +530,12 @@ interface ToolCallContext<Params = unknown> {
465
530
  agentName?: string;
466
531
  /** 当前会话委托深度(root=0);委托工具据此回传 caller 深度给护栏。 */
467
532
  depth?: number;
533
+ /**
534
+ * RFC-371 S3:发起此工具调用的父任务 id(用于 per-parent 活跃扇出计数)。
535
+ * 缺省 undefined = root 层(无 per-parent 限制)。由 agent 的 ToolExecutor
536
+ * 从 turn 上下文注入;委托工具(task_delegate/agent_call)透传给 orchestrator。
537
+ */
538
+ parentTaskId?: string;
468
539
  onUpdate?: (update: string) => void;
469
540
  }
470
541
  interface ToolCallEvent {
@@ -806,7 +877,9 @@ type AgentSessionEvent = {
806
877
  * 修复(历史裁剪可观测性,2026-07-12;语义澄清 RFC-159 T6,2026-07-13):会话内存态
807
878
  * 消息数超上限(`capStoredHistory`,纯条数防御上限,与 memory.pruned 的 token 维度裁剪
808
879
  * 是完全不同的机制,勿混)裁剪最早的消息条目时广播——此前该操作零 UI 反馈,长会话(内存
809
- * 消息数超上限,如默认 2000 条)resume 后用户发现前段历史消失却毫无线索。RFC-159 起持久
880
+ * 消息数超上限,见 `SESSION_MAX_HISTORY_MESSAGES`;RFC-321 D7 起为 50000 结构兜底闸,
881
+ * 早期注释写的"默认 2000 条"已过时,2026-08-12 复审订正)resume 后用户发现前段历史
882
+ * 消失却毫无线索。RFC-159 起持久
810
883
  * 层为 append-forever,被裁剪的条目仍完整保留在 DB(默认 SQLite 后端),本事件语义是
811
884
  * "内存视图收窄"而非数据删除。`removedCount` 为本次从内存视图移除的条目数。
812
885
  */
@@ -971,7 +1044,7 @@ type AgentSessionEventMap = { [E in AgentSessionEvent as E['type']]: (event: E)
971
1044
  /**
972
1045
  * engine-command.ts — RFC-090:renderer→engine 命令协议
973
1046
  *
974
- * `EngineCommand` 是可序列化的联合类型,engine/renderer 共享于 @otto/interchange。
1047
+ * `EngineCommand` 是可序列化的联合类型,engine/renderer 共享于 @x-otto/interchange。
975
1048
  * 硬上限 15 种 command kind。同进程 transport 无需序列化(直接函数调用);
976
1049
  * 进程分离(RFC-091)后通过 stdin/stdout/WebSocket 传输。
977
1050
  */
@@ -1007,7 +1080,7 @@ type EngineCommand = {
1007
1080
  }
1008
1081
  /**
1009
1082
  * RFC-303 D8 修正:`replacement` 运行时语义是 `AgentMessage[]`(会话压缩后的替换消息数组),
1010
- * 但 `AgentMessage` 定义在 `@otto/hook-contracts`(依赖本包 type-only),本包不能反向引用其
1083
+ * 但 `AgentMessage` 定义在 `@x-otto/hook-contracts`(依赖本包 type-only),本包不能反向引用其
1011
1084
  * 类型——故用 `unknown[]`(准确反映"数组"而非之前声明的 `string`)。此前声明为 `string` 是
1012
1085
  * 未被检查出的类型错误:发送侧靠 `as unknown as string` 双重断言掩盖真实值仍是数组;接收侧
1013
1086
  * `engine-main.ts` 曾用 `String(cmd.replacement)` 把数组转成 `"[object Object]"`,破坏数据
@@ -1037,9 +1110,9 @@ type EngineCommand = {
1037
1110
  //#region src/todo.d.ts
1038
1111
  /**
1039
1112
  * Todo 状态与条目 — 单一真源,供 write_todos 等工具跨层消费。
1040
- * 原在 @otto/orchestration-contracts 与本包各有一份逐字相同的镜像定义
1113
+ * 原在 @x-otto/orchestration-contracts 与本包各有一份逐字相同的镜像定义
1041
1114
  * (M-C 沉入 protocol 时未删旧副本,post-RFC-092 review F4 发现并归一)。
1042
- * @otto/orchestration-contracts/src/primitives.ts 现从本文件 re-export,不再自行定义。
1115
+ * @x-otto/orchestration-contracts/src/primitives.ts 现从本文件 re-export,不再自行定义。
1043
1116
  */
1044
1117
  type TodoStatus = 'pending' | 'in_progress' | 'done' | 'skipped' | 'failed';
1045
1118
  interface TodoItem {
@@ -1146,8 +1219,10 @@ interface PluginPerfMetricResult {
1146
1219
  //#region src/input.d.ts
1147
1220
  /** Input: mentions, sigils, content providers — user input enrichment types. */
1148
1221
  /** sigil 子类型:`hashtag` 属 `#`,其余属 `@`。`resource` = 插件自定义资源(RFC-210 D2);
1149
- * `shortcut` = 用户在本仓库自定义的文本快捷方式(RFC-233)。 */
1150
- type SigilKind = 'hashtag' | 'file' | 'person' | 'agent' | 'skill' | 'mcp' | 'plugin' | 'model' | 'resource' | 'shortcut';
1222
+ * `shortcut` = 用户在本仓库自定义的文本快捷方式(RFC-233);`effort` = 推理投入档
1223
+ * (low/medium/high/xhigh/max,RFC-360 D3——不复用 `model`,两者在 `@` 面板需独立
1224
+ * 分类展示,复用会导致 effort 档位被误标"模型"分类)。 */
1225
+ type SigilKind = 'hashtag' | 'file' | 'person' | 'agent' | 'skill' | 'mcp' | 'plugin' | 'model' | 'resource' | 'shortcut' | 'effort';
1151
1226
  /** 触发前缀。 */
1152
1227
  type SigilPrefix = '#' | '@';
1153
1228
  /** 条目来源标识(unregisterBySource 据此批量注销,防僵尸条目)。 */
@@ -1202,7 +1277,7 @@ interface SigilResolveContext {
1202
1277
  maxOutput: number;
1203
1278
  }
1204
1279
  /**
1205
- * 插件输入注册表(UI 无关,由 createApp() 创建单例,实现留 @otto/coding)。
1280
+ * 插件输入注册表(UI 无关,由 createApp() 创建单例,实现留 @x-otto/coding)。
1206
1281
  *
1207
1282
  * 静态 push 供给(register)+ 动态 pull 供给(registerProvider,服务 @file
1208
1283
  * 大候选集懒查,RFC §1.3.1 F-domain / RFC-044 M044-07)。
@@ -1369,5 +1444,5 @@ declare function evaluateSigilTrigger(input: {
1369
1444
  sigilFilter?: (sigil: SigilPrefix) => boolean;
1370
1445
  }): SigilTriggerDecision;
1371
1446
  //#endregion
1372
- export { type A2uiComponent, type A2uiContent, type AgentSessionEvent, type AgentSessionEventMap, type AgentSessionEventType, type AgentSessionSubscriber, type AgentTool, type AnthropicImageMime, type Api, type AskUserQuestion, type AssistantMessage, CONVERSATION_SUMMARY_PREFIX, type ChildProcessConfig, type CommonImageMime, type ContentPart, type Cost, type DismissedSigil, type EngineCommand, type GrillAnswer, type GrillOption, type GrillQuestion, type GrillRequest, type ImageContent, type KnownApi, type KnownProvider, type Message, type Model, type ModelSpec, type ModelStrength, type NormalizedImage, type PluginInputRegistry, type PluginPerfMetricResult, type ProcessTracker, type PromptStopReason, type Provider, type ProviderUsageSnapshot, type SigilEntry, type SigilKind, type SigilPrefix, type SigilProvider, type SigilResolveContext, type SigilResolver, type SigilSource, type SigilTrigger, type SigilTriggerDecision, type StopReason, type StreamEvent, type SystemNotificationMessage, type TextChangeSource, type TextContent, type ThinkingContent, type ThinkingLevel, type TodoItem, type TodoStatus, type ToolCall, type ToolCallContext, type ToolCallEvent, type ToolDescriptionContext, type ToolErrorKind, type ToolResult, type ToolResultMessage, type Usage, type UserMessage, type ZodType, adjustDismissedSigil, applySigilCompletion, applySigilReplacement, evaluateSigilTrigger, extractSigilPrefix, extractToolResultText, getToolCallsByAssistantMessage, hasToolCalls, isSigilDismissed, modelSupportsImages, normalizeImageData, sigilOf, toAnthropicImageMime };
1447
+ export { type A2uiComponent, type A2uiContent, type AgentSessionEvent, type AgentSessionEventMap, type AgentSessionEventType, type AgentSessionSubscriber, type AgentTool, type AnthropicImageMime, type Api, type AskUserQuestion, type AssistantMessage, CONVERSATION_SUMMARY_PREFIX, type CapabilityFact, type ChildProcessConfig, type CommonImageMime, type ContentPart, type ContextFragment, type ContextFragmentId, type Cost, type DismissedSigil, type EngineCommand, type GrillAnswer, type GrillOption, type GrillQuestion, type GrillRequest, type ImageContent, type KnownApi, type KnownProvider, type Message, type Model, type ModelCapabilityFacts, type ModelSpec, type ModelStrength, type NormalizedImage, type PluginInputRegistry, type PluginPerfMetricResult, type ProcessTracker, type PromptStopReason, type Provider, type ProviderUsageSnapshot, type SigilEntry, type SigilKind, type SigilPrefix, type SigilProvider, type SigilResolveContext, type SigilResolver, type SigilSource, type SigilTrigger, type SigilTriggerDecision, type StopReason, type StreamEvent, type SystemNotificationMessage, type TextChangeSource, type TextContent, type ThinkingContent, type ThinkingLevel, type TodoItem, type TodoStatus, type ToolCall, type ToolCallContext, type ToolCallEvent, type ToolDescriptionContext, type ToolErrorKind, type ToolResult, type ToolResultMessage, type Usage, type UserMessage, type ZodType, adjustDismissedSigil, applySigilCompletion, applySigilReplacement, boundFragment, collectModelCapabilityFacts, contextFragmentBytes, evaluateSigilTrigger, extractSigilPrefix, extractToolResultText, getToolCallsByAssistantMessage, hasToolCalls, isSigilDismissed, modelImageCapability, modelSupportsImages, normalizeImageData, sigilOf, toAnthropicImageMime };
1373
1448
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- const e=`[Conversation Summary]`;function t(e){return e.content.filter(e=>e.type===`tool_call`)}function n(e){return e.content.some(e=>e.type===`tool_call`)}function r(e){return Array.isArray(e.input)&&e.input.includes(`image`)}function i(e){return e.content.filter(e=>e.type===`text`).map(e=>e.text).join(`
2
- `)}const a=/^data:([^;,]*)(?:;[^,]*)?,(.*)$/s;function o(e,t){let n=a.exec(e),r=(n?n[2]??``:e).replace(/\s+/g,``);return{base64:r,mediaType:((n?n[1]:``)||t||``).toLowerCase(),valid:s(r)}}function s(e){return e.length===0||e===`undefined`||e.length%4==1?!1:/^[A-Za-z0-9+/]*={0,2}$/.test(e)}function c(e,t){switch(e.toLowerCase()){case`image/jpeg`:case`image/jpg`:return`image/jpeg`;case`image/png`:return`image/png`;case`image/gif`:return`image/gif`;case`image/webp`:return`image/webp`;default:return l(t)}}function l(e){return e.startsWith(`/9j/`)?`image/jpeg`:e.startsWith(`iVBOR`)?`image/png`:e.startsWith(`R0lGOD`)?`image/gif`:e.startsWith(`UklGR`)?`image/webp`:`image/png`}function u(e){return e===`hashtag`?`#`:`@`}const d=/(?:^|\s)([#@])(\S*)$/;function f(e,t){let n=e.slice(0,t).match(d);if(!n)return null;let r=n[1],i=n[2]??``;return{sigil:r,query:i,anchor:t-i.length-1}}function p(e,t){return!!t&&t.sigil===e.sigil&&t.anchor===e.anchor}function m(e,t){let n=Math.min(e.length,t.length),r=0;for(;r<n&&e[r]===t[r];)r++;let i=n-r,a=0;for(;a<i&&e[e.length-1-a]===t[t.length-1-a];)a++;return{start:r,end:e.length-a,delta:t.length-e.length}}function h(e,t,n){if(!e||t===n)return e;let{start:r,end:i,delta:a}=m(t,n),o=e.anchor+e.tokenLength;return i<=e.anchor?{...e,anchor:e.anchor+a}:r>=o||r>e.anchor&&r<o?e:null}function g(e,t,n,r){if(n<0||n>t)return{text:e,offset:t};let i=t>=e.length?` `:``;return{text:e.slice(0,n)+r.value+i+e.slice(t),offset:n+r.value.length+(i?1:0)}}function _(e,t,n,r){return t<0||t>n||n>e.length?{text:e,offset:n}:{text:e.slice(0,t)+r.value+e.slice(n),offset:t+r.value.length}}function v(e){let{source:t,oldText:n,newText:r,cursorOffset:i}=e,a=e.sigilFilter??(e=>e===`@`),o=h(e.dismissed,n,r);if(t!==`typing`||r.length<=n.length)return{action:`suppress`,dismissed:o};let s=f(r,i),c=s&&a(s.sigil)?s:null;return c?p(c,o)?{action:`suppress`,dismissed:o}:{action:`open`,trigger:c,dismissed:null}:{action:`close`,dismissed:o}}export{e as CONVERSATION_SUMMARY_PREFIX,h as adjustDismissedSigil,g as applySigilCompletion,_ as applySigilReplacement,v as evaluateSigilTrigger,f as extractSigilPrefix,i as extractToolResultText,t as getToolCallsByAssistantMessage,n as hasToolCalls,p as isSigilDismissed,r as modelSupportsImages,o as normalizeImageData,u as sigilOf,c as toAnthropicImageMime};
1
+ const e=`[Conversation Summary]`;function t(e){return e.content.filter(e=>e.type===`tool_call`)}function n(e){return e.content.some(e=>e.type===`tool_call`)}function r(e){return!Array.isArray(e.input)||e.input.length===0?{state:`unknown`}:e.input.includes(`image`)?{state:`supported`,value:!0}:{state:`unsupported`}}function i(e){return r(e).state===`supported`}function a(e){return{vision:r(e)}}function o(e){return Buffer.byteLength(e,`utf8`)}function s(e,t){let n=Buffer.from(e.content,`utf8`);if(n.length<=t)return e;let r=Math.max(0,t);for(;r>0&&r<n.length&&(n[r]&192)==128;)r--;return{id:e.id,content:n.subarray(0,r).toString(`utf8`).trimEnd()+`
2
+ [context truncated: budget exceeded]`}}function c(e){return e.content.filter(e=>e.type===`text`).map(e=>e.text).join(`
3
+ `)}const l=/^data:([^;,]*)(?:;[^,]*)?,(.*)$/s;function u(e,t){let n=l.exec(e),r=(n?n[2]??``:e).replace(/\s+/g,``);return{base64:r,mediaType:((n?n[1]:``)||t||``).toLowerCase(),valid:d(r)}}function d(e){return e.length===0||e===`undefined`||e.length%4==1?!1:/^[A-Za-z0-9+/]*={0,2}$/.test(e)}function f(e,t){switch(e.toLowerCase()){case`image/jpeg`:case`image/jpg`:return`image/jpeg`;case`image/png`:return`image/png`;case`image/gif`:return`image/gif`;case`image/webp`:return`image/webp`;default:return p(t)}}function p(e){return e.startsWith(`/9j/`)?`image/jpeg`:e.startsWith(`iVBOR`)?`image/png`:e.startsWith(`R0lGOD`)?`image/gif`:e.startsWith(`UklGR`)?`image/webp`:`image/png`}function m(e){return e===`hashtag`?`#`:`@`}const h=/(?:^|\s)([#@])(\S*)$/;function g(e,t){let n=e.slice(0,t).match(h);if(!n)return null;let r=n[1],i=n[2]??``;return{sigil:r,query:i,anchor:t-i.length-1}}function _(e,t){return!!t&&t.sigil===e.sigil&&t.anchor===e.anchor}function v(e,t){let n=Math.min(e.length,t.length),r=0;for(;r<n&&e[r]===t[r];)r++;let i=n-r,a=0;for(;a<i&&e[e.length-1-a]===t[t.length-1-a];)a++;return{start:r,end:e.length-a,delta:t.length-e.length}}function y(e,t,n){if(!e||t===n)return e;let{start:r,end:i,delta:a}=v(t,n),o=e.anchor+e.tokenLength;return i<=e.anchor?{...e,anchor:e.anchor+a}:r>=o||r>e.anchor&&r<o?e:null}function b(e,t,n,r){if(n<0||n>t)return{text:e,offset:t};let i=t>=e.length?` `:``;return{text:e.slice(0,n)+r.value+i+e.slice(t),offset:n+r.value.length+(i?1:0)}}function x(e,t,n,r){return t<0||t>n||n>e.length?{text:e,offset:n}:{text:e.slice(0,t)+r.value+e.slice(n),offset:t+r.value.length}}function S(e){let{source:t,oldText:n,newText:r,cursorOffset:i}=e,a=e.sigilFilter??(e=>e===`@`),o=y(e.dismissed,n,r);if(t!==`typing`||r.length<=n.length)return{action:`suppress`,dismissed:o};let s=g(r,i),c=s&&a(s.sigil)?s:null;return c?_(c,o)?{action:`suppress`,dismissed:o}:{action:`open`,trigger:c,dismissed:null}:{action:`close`,dismissed:o}}export{e as CONVERSATION_SUMMARY_PREFIX,y as adjustDismissedSigil,b as applySigilCompletion,x as applySigilReplacement,s as boundFragment,a as collectModelCapabilityFacts,o as contextFragmentBytes,S as evaluateSigilTrigger,g as extractSigilPrefix,c as extractToolResultText,t as getToolCallsByAssistantMessage,n as hasToolCalls,_ as isSigilDismissed,r as modelImageCapability,i as modelSupportsImages,u as normalizeImageData,m as sigilOf,f as toAnthropicImageMime};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-otto/interchange",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -25,4 +25,4 @@
25
25
  "test": "vitest run",
26
26
  "clean": "rm -rf dist"
27
27
  }
28
- }
28
+ }