@ego-z/contracts 0.15.7 → 0.15.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ego-z/contracts",
3
- "version": "0.15.7",
3
+ "version": "0.15.9",
4
4
  "description": "Wire-format type contracts shared between EgoZ backend, SDK, MCP and console. Type-only — no runtime artifacts.",
5
5
  "types": "./src/index.d.ts",
6
6
  "exports": {
package/src/ask.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { Intent, JsonSchemaDefinition, ResponseFormat, TokenUsage } from './envelope';
9
- import type { InitContextUi } from './draft-conv';
9
+ import type { InitContextItem, InitContextUi } from './draft-conv';
10
10
 
11
11
  // ============================================================================
12
12
  // /ask — request
@@ -189,6 +189,27 @@ export interface AskRequestBody {
189
189
  * with the stored `initContext` used for the whole conversation.
190
190
  */
191
191
  ui?: InitContextUi;
192
+
193
+ /**
194
+ * What the USER explicitly attached to this turn's context.
195
+ *
196
+ * ── A sibling of `ui`, never a part of it ────────────────────────────
197
+ *
198
+ * `ui` is a FACT about the screen; `contextItems` is an INTENTION. The
199
+ * user never edits `ui` — express "don't look at this" or "also consider
200
+ * this" here instead. Editing the fact to carry the intention is how a
201
+ * window the user merely hid ends up reported as one they closed.
202
+ *
203
+ * It is also strictly wider: the user can attach something that was never
204
+ * a window at all, which no amount of flagging inside `ui` could express.
205
+ *
206
+ * Send the full current list every turn, not a delta — a client-computed
207
+ * delta needs a client-held baseline, and that baseline is wrong after a
208
+ * reload, a retry or a reconnect.
209
+ *
210
+ * Optional and additive: omit it and nothing changes.
211
+ */
212
+ contextItems?: InitContextItem[];
192
213
  }
193
214
 
194
215
  /**
@@ -58,6 +58,48 @@ export type InitContextViewport = 'mobile' | 'desktop';
58
58
  */
59
59
  export type InitContextAudience = 'operator' | 'customer';
60
60
 
61
+ /**
62
+ * One thing the user explicitly put into the chat's context.
63
+ *
64
+ * ── The caller resolves it; EgoZ never fetches ───────────────────────────
65
+ *
66
+ * You look the item up and send what it IS. EgoZ never resolves `kind + id`
67
+ * against your data model — doing so would put one integration's vocabulary
68
+ * into a shared contract, and "you can attach anything" would stop being
69
+ * true the day you invented a type we hadn't shipped. **A new `kind` costs
70
+ * zero EgoZ changes**, which is the test of whether this is genuinely
71
+ * open-ended rather than a list we happen to support.
72
+ */
73
+ export interface InitContextItem {
74
+ /**
75
+ * Your own type name — `"product"`, `"invoice"`, anything you like.
76
+ *
77
+ * ⚠️ Free-form, and **never rendered into the prompt**. Both halves go
78
+ * together: free-form so a new kind needs no change at either end, and
79
+ * unrendered because free-form text reaching the instruction block is an
80
+ * injection surface — a `kind` of "ignore previous instructions" would
81
+ * otherwise be printed into a system prompt. Used only to group items.
82
+ */
83
+ kind?: string;
84
+
85
+ /** Your own id for the item. Structural; also never rendered. */
86
+ id?: string;
87
+
88
+ /**
89
+ * Human-readable name — `"Official Chelsea Home Shirt"`. **Required in
90
+ * practice**: it is the only part the model reads, so an item without one
91
+ * is dropped rather than rendered as an id.
92
+ */
93
+ label?: string;
94
+
95
+ /**
96
+ * A short description of the item. Optional, and truncated rather than
97
+ * rejected if long — but keep it to what the thing IS, not its full
98
+ * record. Everything here displaces conversation from the context window.
99
+ */
100
+ excerpt?: string;
101
+ }
102
+
61
103
  /** One open window in the caller's UI. */
62
104
  export interface InitContextOpenWindow {
63
105
  /**
@@ -93,8 +135,43 @@ export interface InitContextOpenWindow {
93
135
  * Human-readable window label, e.g. `"Blue Hoodie"`. Safe to render
94
136
  * verbatim — unlike the window KEY, which embeds entity ids and is
95
137
  * rendered only for the focused window.
138
+ *
139
+ * OMIT IT when `withheld` is true — see below.
96
140
  */
97
141
  title?: string;
142
+
143
+ /**
144
+ * The user removed this window from the chat's context chips: it is on
145
+ * their screen, but they have asked that it NOT be sent to the agent.
146
+ *
147
+ * ── Send the id and the flag. Never the title. ───────────────────────
148
+ *
149
+ * The agent needs the ID so it can stay quiet: EgoZ derives "what changed
150
+ * since your last message" by diffing window ids, and a window that
151
+ * simply vanishes from the payload is indistinguishable from one the user
152
+ * CLOSED — so the agent would tell them a window is gone while they are
153
+ * looking at it.
154
+ *
155
+ * The TITLE is the part the user was removing. It is the field that
156
+ * carries `"Acme Corp — Overdue Invoice #4471"` into a prompt and into
157
+ * storage. Send it anyway and the control is theatre: the gesture would
158
+ * change nothing about what leaves the browser. Ids are structural
159
+ * (`ecommerce:productDetail:prod-4471`) and leak a record id, not a
160
+ * person's name.
161
+ *
162
+ * ── What it does and does not do ─────────────────────────────────────
163
+ *
164
+ * A withheld window is not rendered anywhere: not in the open-window
165
+ * list, not as the focused window, not in the change summary, and it
166
+ * never anchors a greeting.
167
+ *
168
+ * It stops FUTURE transmission only. A title sent on an earlier turn is
169
+ * already stored, and anything the agent has already SAID about that
170
+ * window remains in the transcript. Withdrawing a window does not
171
+ * withdraw the conversation about it — so describe this to users as
172
+ * "not shared from now on", never as though it were erased.
173
+ */
174
+ withheld?: boolean;
98
175
  isFocused?: boolean;
99
176
  }
100
177