@ego-z/contracts 0.15.6 → 0.15.8

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.6",
3
+ "version": "0.15.8",
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,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { Intent, JsonSchemaDefinition, ResponseFormat, TokenUsage } from './envelope';
9
+ import type { InitContextUi } from './draft-conv';
9
10
 
10
11
  // ============================================================================
11
12
  // /ask — request
@@ -158,6 +159,36 @@ export interface AskRequestBody {
158
159
  * 4. `Authorization` injected by `authType: 'FORWARD'`
159
160
  */
160
161
  toolHeaders?: Record<string, string>;
162
+
163
+ /**
164
+ * What the user has on screen RIGHT NOW — sent on every turn, not just
165
+ * when the conversation started.
166
+ *
167
+ * ── Why per-turn ─────────────────────────────────────────────────────
168
+ *
169
+ * `POST /conversations/draft` captures an `initContext` once, when the
170
+ * chat panel opens. That snapshot is frozen for the life of the
171
+ * conversation, so a window the user opens mid-chat is invisible to the
172
+ * agent by construction — "do that one as well" has no referent to
173
+ * resolve, and it reads as the model being obtuse when in fact it was
174
+ * never told.
175
+ *
176
+ * Send the CURRENT snapshot here and EgoZ works out what changed since
177
+ * the user's previous message. Deixis — "as well", "this one too", "the
178
+ * other one", "go back to the first" — then resolves from ordering and
179
+ * recency, with no phrase anyone has to enumerate.
180
+ *
181
+ * ── Send the full snapshot, never a delta ────────────────────────────
182
+ *
183
+ * Do not compute "what's new" client-side. A client delta needs a
184
+ * client-held baseline, and that baseline is wrong after a reload, a
185
+ * retry, or a reconnect. EgoZ holds the previous turn, so it can diff
186
+ * reliably; the client's job is only to report what it sees.
187
+ *
188
+ * Optional and additive: omit it and behaviour is exactly as before,
189
+ * with the stored `initContext` used for the whole conversation.
190
+ */
191
+ ui?: InitContextUi;
161
192
  }
162
193
 
163
194
  /**
@@ -60,14 +60,76 @@ export type InitContextAudience = 'operator' | 'customer';
60
60
 
61
61
  /** One open window in the caller's UI. */
62
62
  export interface InitContextOpenWindow {
63
- /** Epoch millis the window was opened. */
63
+ /**
64
+ * Epoch millis this window was FIRST SEEN by the client, on the CLIENT'S
65
+ * clock. Not when it was opened — most window systems have no true open
66
+ * time, and this is the honest name for what is actually knowable.
67
+ *
68
+ * ⚠️ Valid ONLY for ordering windows against each other **within a single
69
+ * snapshot**, where the values share one clock and one instant.
70
+ *
71
+ * It is NOT valid across snapshots and NOT valid against server time:
72
+ * - It is a browser clock. Comparing it to a server timestamp compares
73
+ * two clocks, so a client a few minutes off gets a silently wrong
74
+ * answer that differs per end user.
75
+ * - It is memory-only and RESETS ON PAGE RELOAD. After a refresh every
76
+ * open window carries the moment of boot, so a "what's new" rule
77
+ * built on it doesn't weaken — it inverts, and the window the user
78
+ * has been discussing since turn one becomes the newest.
79
+ *
80
+ * To detect what changed between turns, diff window IDS instead. That
81
+ * asks only "was this seen before", which no clock can corrupt.
82
+ */
83
+ firstSeenAt?: number;
84
+
85
+ /**
86
+ * @deprecated Use `firstSeenAt`. Same value, honest name — this one
87
+ * asserts an open time that the client cannot know, and that reading
88
+ * caused a real design error. Emitted alongside `firstSeenAt` for one
89
+ * minor; readers should prefer `firstSeenAt` and fall back to this.
90
+ */
64
91
  openedAt?: number;
65
92
  /**
66
93
  * Human-readable window label, e.g. `"Blue Hoodie"`. Safe to render
67
94
  * verbatim — unlike the window KEY, which embeds entity ids and is
68
95
  * rendered only for the focused window.
96
+ *
97
+ * OMIT IT when `withheld` is true — see below.
69
98
  */
70
99
  title?: string;
100
+
101
+ /**
102
+ * The user removed this window from the chat's context chips: it is on
103
+ * their screen, but they have asked that it NOT be sent to the agent.
104
+ *
105
+ * ── Send the id and the flag. Never the title. ───────────────────────
106
+ *
107
+ * The agent needs the ID so it can stay quiet: EgoZ derives "what changed
108
+ * since your last message" by diffing window ids, and a window that
109
+ * simply vanishes from the payload is indistinguishable from one the user
110
+ * CLOSED — so the agent would tell them a window is gone while they are
111
+ * looking at it.
112
+ *
113
+ * The TITLE is the part the user was removing. It is the field that
114
+ * carries `"Acme Corp — Overdue Invoice #4471"` into a prompt and into
115
+ * storage. Send it anyway and the control is theatre: the gesture would
116
+ * change nothing about what leaves the browser. Ids are structural
117
+ * (`ecommerce:productDetail:prod-4471`) and leak a record id, not a
118
+ * person's name.
119
+ *
120
+ * ── What it does and does not do ─────────────────────────────────────
121
+ *
122
+ * A withheld window is not rendered anywhere: not in the open-window
123
+ * list, not as the focused window, not in the change summary, and it
124
+ * never anchors a greeting.
125
+ *
126
+ * It stops FUTURE transmission only. A title sent on an earlier turn is
127
+ * already stored, and anything the agent has already SAID about that
128
+ * window remains in the transcript. Withdrawing a window does not
129
+ * withdraw the conversation about it — so describe this to users as
130
+ * "not shared from now on", never as though it were erased.
131
+ */
132
+ withheld?: boolean;
71
133
  isFocused?: boolean;
72
134
  }
73
135