@jini-ai/agentic 0.4.0 → 0.4.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/core/a2ui/index.d.ts +1 -0
  3. package/dist/core/a2ui/index.js +1 -0
  4. package/dist/core/a2ui/surface-properties.d.ts +34 -0
  5. package/dist/core/a2ui/surface-properties.js +49 -0
  6. package/dist/core/annotated-actions-script.d.ts +13 -0
  7. package/dist/core/annotated-actions-script.js +137 -0
  8. package/dist/core/index.d.ts +1 -0
  9. package/dist/core/index.js +1 -0
  10. package/package.json +2 -2
  11. package/dist/a2ui/agent-to-renderer.d.ts +0 -613
  12. package/dist/a2ui/agent-to-renderer.js +0 -191
  13. package/dist/a2ui/catalog.d.ts +0 -73
  14. package/dist/a2ui/catalog.js +0 -304
  15. package/dist/a2ui/common-types.d.ts +0 -401
  16. package/dist/a2ui/common-types.js +0 -130
  17. package/dist/a2ui/index.d.ts +0 -16
  18. package/dist/a2ui/index.js +0 -16
  19. package/dist/a2ui/interpreter.d.ts +0 -71
  20. package/dist/a2ui/interpreter.js +0 -337
  21. package/dist/a2ui/json-pointer.d.ts +0 -48
  22. package/dist/a2ui/json-pointer.js +0 -174
  23. package/dist/a2ui/renderer-to-agent.d.ts +0 -342
  24. package/dist/a2ui/renderer-to-agent.js +0 -111
  25. package/dist/a2ui/resolve.d.ts +0 -28
  26. package/dist/a2ui/resolve.js +0 -144
  27. package/dist/a2ui/tree.d.ts +0 -66
  28. package/dist/a2ui/tree.js +0 -74
  29. package/dist/ag-ui.d.ts +0 -82
  30. package/dist/ag-ui.js +0 -85
  31. package/dist/agui/encoder.d.ts +0 -50
  32. package/dist/agui/encoder.js +0 -97
  33. package/dist/agui/events.d.ts +0 -59
  34. package/dist/agui/events.js +0 -15
  35. package/dist/capability.d.ts +0 -81
  36. package/dist/capability.js +0 -79
  37. package/dist/dom/dom-page-driver.d.ts +0 -36
  38. package/dist/dom/dom-page-driver.js +0 -601
  39. package/dist/dom/index.d.ts +0 -17
  40. package/dist/dom/index.js +0 -17
  41. package/dist/dom/model-context.d.ts +0 -69
  42. package/dist/dom/model-context.js +0 -44
  43. package/dist/element-handles.d.ts +0 -120
  44. package/dist/element-handles.js +0 -39
  45. package/dist/gen-ui/encoder.d.ts +0 -50
  46. package/dist/gen-ui/encoder.js +0 -97
  47. package/dist/gen-ui/events.d.ts +0 -59
  48. package/dist/gen-ui/events.js +0 -15
  49. package/dist/gen-ui/index.d.ts +0 -18
  50. package/dist/gen-ui/index.js +0 -17
  51. package/dist/guards.d.ts +0 -96
  52. package/dist/guards.js +0 -186
  53. package/dist/handle.d.ts +0 -41
  54. package/dist/handle.js +0 -41
  55. package/dist/mcp-ui-apps.d.ts +0 -223
  56. package/dist/mcp-ui-apps.js +0 -252
  57. package/dist/mcp-ui.d.ts +0 -133
  58. package/dist/mcp-ui.js +0 -134
  59. package/dist/page-capabilities.d.ts +0 -14
  60. package/dist/page-capabilities.js +0 -129
  61. package/dist/page-driver.d.ts +0 -120
  62. package/dist/page-driver.js +0 -2
  63. package/dist/page-executor.d.ts +0 -94
  64. package/dist/page-executor.js +0 -320
  65. package/dist/webmcp.d.ts +0 -202
  66. package/dist/webmcp.js +0 -173
@@ -1,337 +0,0 @@
1
- /**
2
- * @module interpreter
3
- *
4
- * The minimal A2UI client-side interpreter: client state (a component map + a JSON-Pointer-
5
- * addressed data model, per surface) that processes agent→renderer messages
6
- * (`createSurface -> updateComponents -> updateDataModel -> ...`) and produces renderer→agent
7
- * messages (`action`/`functionResponse`/`error`) in return. This is the piece GenUI/AG-UI
8
- * (`packages/agentic/src/gen-ui/`, this repo's existing, much smaller GenUI surface-request shim)
9
- * has no equivalent of — AG-UI streams
10
- * opaque `ui.surface_requested`/`ui.surface_responded` events with no component tree, no catalog,
11
- * no data binding; A2UI's interpreter has to actually hold and mutate structured state.
12
- *
13
- * **Design decisions made where the spec text (fetched from
14
- * https://github.com/a2ui-project/a2ui, `main` branch, this session) is silent, documented here
15
- * rather than left implicit:**
16
- *
17
- * 1. **Per-component, not per-message, validation granularity.** `updateComponents`'s own spec
18
- * text says forward-referenced children/bindings should be handled gracefully — the spirit of
19
- * that is "don't be brittle about partial/out-of-order information." This interpreter extends
20
- * that spirit to catalog violations: one bad component in a 5-component `updateComponents`
21
- * message produces one `VALIDATION_FAILED` error for that component and is skipped, while the
22
- * other 4 valid components are still applied. The alternative (reject the whole message
23
- * atomically) was considered and rejected as more brittle than the spec's own examples call for,
24
- * not as spec-mandated either way.
25
- * 2. **`callFunction`/`actionResponse` are not surface-scoped on the wire** (verified directly
26
- * against the fetched JSON Schemas — neither message type has a `surfaceId` field). Functions
27
- * are catalog-global (one active catalog per interpreter instance); `actionResponse` is
28
- * correlated by `actionId` searched across every open surface's own pending-action table.
29
- * 3. **`callFunction`'s `args` may contain `DataBinding` paths** (`FunctionCall.args` is typed
30
- * against `DynamicValue` in `common_types.json`, which includes `DataBinding`) **but
31
- * `callFunction` has no data model to bind against** (see point 2) — a real spec ambiguity, not
32
- * an oversight in this port. Resolved pragmatically: such args resolve against an empty/absent
33
- * data model, so any `DataBinding`-typed arg simply fails to resolve (`PATH_NOT_FOUND`,
34
- * degrading sanely per `resolve.ts`'s contract) rather than crashing or guessing which surface's
35
- * data model was "meant."
36
- * 4. **An envelope malformed enough to have no attributable `surfaceId`/`functionCallId` at all**
37
- * (missing `version`, no/ambiguous message-type key) cannot be turned into a spec-shaped
38
- * `error` message — `renderer_to_agent.json`'s own generic-error schema *requires* one of those
39
- * two identifiers. Rather than fabricate one, `applyAgentMessage` surfaces this case out-of-band
40
- * via `unattributedViolation` (a string, not sent over the wire) instead of forcing a fake
41
- * identifier into a wire-conformant shape.
42
- */
43
- import { AGENT_TO_RENDERER_MESSAGE_KEYS, parseAgentToRendererMessage } from './agent-to-renderer.js';
44
- import { WireComponentSchema } from './agent-to-renderer.js';
45
- import { buildActionMessage, buildFunctionResponseMessage, buildGenericErrorMessage, buildValidationFailedMessage, } from './renderer-to-agent.js';
46
- import { setAtPointer } from './json-pointer.js';
47
- import { isComponentAllowed } from './catalog.js';
48
- import { resolveDynamicValue } from './resolve.js';
49
- import { isLocalFunctionAction, } from './common-types.js';
50
- let actionIdCounter = 0;
51
- function nextActionId() {
52
- actionIdCounter += 1;
53
- return `a2ui-action-${actionIdCounter}-${Date.now().toString(36)}`;
54
- }
55
- /**
56
- * Best-effort `surfaceId` extraction from a raw envelope's message-type-keyed body, without
57
- * assuming the envelope is well-formed. Used only to attribute a parse failure to a surface when
58
- * possible (see `buildParseFailureResult`); never trusted for anything else.
59
- */
60
- function bestEffortSurfaceId(raw, key) {
61
- if (typeof raw !== 'object' || raw === null)
62
- return undefined;
63
- const body = raw[key];
64
- if (typeof body !== 'object' || body === null)
65
- return undefined;
66
- const surfaceId = body.surfaceId;
67
- return typeof surfaceId === 'string' ? surfaceId : undefined;
68
- }
69
- /**
70
- * Turns a `ParseFailure` (from `parseAgentToRendererMessage`) into what `applyAgentMessage`
71
- * returns for it. See this module's doc, decision 4: an envelope malformed enough to carry no
72
- * attributable `surfaceId`/`functionCallId` cannot be turned into a spec-shaped `error` message —
73
- * `renderer_to_agent.json`'s own generic-error schema *requires* one of those two identifiers —
74
- * so it surfaces out-of-band via `unattributedViolation` (a string, not sent over the wire)
75
- * instead of forcing a fake identifier into a wire-conformant shape.
76
- *
77
- * Exported and pure — it only reads `parsed`/`raw`, so the surfaceId-attribution rule can be
78
- * tested directly, without a live interpreter instance.
79
- */
80
- export function buildParseFailureResult(parsed, raw) {
81
- if (parsed.code === 'MISSING_VERSION'
82
- || parsed.code === 'UNSUPPORTED_VERSION'
83
- || parsed.code === 'NO_MESSAGE_KEY'
84
- || parsed.code === 'AMBIGUOUS_MESSAGE') {
85
- return { rendererMessages: [], unattributedViolation: parsed.message };
86
- }
87
- // VALIDATION_FAILED from the dispatcher: either `raw` wasn't even an object (`bestEffortSurfaceId`
88
- // safely returns `undefined` for that, same as every other shape it doesn't recognize — no
89
- // separate guard needed here), or exactly one of the six known message-type keys was present
90
- // and its body failed schema validation. Checked against the *known* keys specifically
91
- // (`AGENT_TO_RENDERER_MESSAGE_KEYS`), not "whatever the first non-version object key happens to
92
- // be" — an envelope could carry unrelated extra top-level junk ahead of the real message key in
93
- // insertion order, and grabbing that first would attribute the error to the wrong place. Best-
94
- // effort surfaceId extraction so this can still be a spec-shaped error where possible
95
- // (createSurface/updateComponents/updateDataModel/deleteSurface all carry surfaceId at the top
96
- // of their body even when some other field is what actually failed validation); callFunction/
97
- // actionResponse have no surfaceId at all, so those fall back to the out-of-band channel too.
98
- const surfaceId = AGENT_TO_RENDERER_MESSAGE_KEYS.map((key) => bestEffortSurfaceId(raw, key)).find((id) => id !== undefined);
99
- if (surfaceId) {
100
- // `parsed.path` is guaranteed set here: `ParseFailure.path` is only ever omitted for the
101
- // "raw input wasn't even an object" case (`agent-to-renderer.ts`'s very first guard), and
102
- // `surfaceId` above could only have resolved to a truthy value if `raw` *was* an object
103
- // (`bestEffortSurfaceId` requires that) — so this specific combination (object-shaped raw,
104
- // yet no `path`) cannot occur. Asserted, not defensively branched, per this package's
105
- // testing discipline (see the zod-issues-array comments elsewhere in this file).
106
- return { rendererMessages: [buildValidationFailedMessage(surfaceId, parsed.path, parsed.message)] };
107
- }
108
- return { rendererMessages: [], unattributedViolation: parsed.message };
109
- }
110
- /**
111
- * Runs a component's local `functionCall` action synchronously against `dataModel` — no network
112
- * round trip, no pending-action bookkeeping (that only applies to the agent-event branch; see
113
- * `interpreter`'s `buildAction`/`buildAgentEventAction`).
114
- *
115
- * Exported and pure given its inputs, so "a local action resolves against the surface's actual
116
- * data model, and a resolution failure becomes a typed refusal rather than a thrown error" can be
117
- * tested without a live interpreter instance.
118
- */
119
- export function runLocalFunctionAction(dataModel, catalog, action) {
120
- const result = resolveDynamicValue(action.functionCall, { dataModel, catalog, side: 'renderer' });
121
- return result.ok ? { ok: true, kind: 'local', result: result.value } : { ok: false, reason: result.detail };
122
- }
123
- export function createA2uiInterpreter(catalog) {
124
- const surfaces = new Map();
125
- const listeners = new Set();
126
- function notify() {
127
- for (const listener of listeners)
128
- listener();
129
- }
130
- function applyComponentsList(surface, wireComponents) {
131
- const errors = [];
132
- wireComponents.forEach((wireComponent, index) => {
133
- const { id, component: type, ...rest } = wireComponent;
134
- if (!isComponentAllowed(catalog, type)) {
135
- errors.push(buildValidationFailedMessage(surface.surfaceId, `/components/${index}/component`, `Component type "${type}" is not in catalog "${catalog.catalogId}" — refused, not rendered.`));
136
- return;
137
- }
138
- const spec = catalog.components.get(type);
139
- const parsed = spec.propsSchema.safeParse(rest);
140
- if (!parsed.success) {
141
- // zod guarantees a non-empty `issues` array on failure — see agent-to-renderer.ts's
142
- // parseAgentToRendererMessage for the same asserted (not defensively branched) invariant.
143
- const issue = parsed.error.issues[0];
144
- errors.push(buildValidationFailedMessage(surface.surfaceId, `/components/${index}/${issue.path.join('/')}`, `Component "${id}" (${type}) failed catalog validation: ${issue.message}`));
145
- return;
146
- }
147
- surface.components.set(id, { id, component: type, props: parsed.data });
148
- });
149
- return errors;
150
- }
151
- function handleCreateSurface(message) {
152
- const { surfaceId, catalogId, components, dataModel } = message.createSurface;
153
- if (catalogId !== catalog.catalogId) {
154
- return [buildValidationFailedMessage(surfaceId, '/createSurface/catalogId', `Unknown catalogId "${catalogId}" — this renderer only has "${catalog.catalogId}" loaded.`)];
155
- }
156
- if (surfaces.has(surfaceId)) {
157
- return [buildValidationFailedMessage(surfaceId, '/createSurface/surfaceId', `Surface "${surfaceId}" already exists — delete it before recreating (per spec).`)];
158
- }
159
- const surface = {
160
- surfaceId,
161
- catalogId,
162
- components: new Map(),
163
- dataModel: dataModel ?? {},
164
- sendDataModel: message.createSurface.sendDataModel ?? false,
165
- pendingActions: new Map(),
166
- };
167
- surfaces.set(surfaceId, surface);
168
- return components ? applyComponentsList(surface, components) : [];
169
- }
170
- function handleUpdateComponents(message) {
171
- const { surfaceId, components } = message.updateComponents;
172
- const surface = surfaces.get(surfaceId);
173
- if (!surface) {
174
- return [buildValidationFailedMessage(surfaceId, '/updateComponents/surfaceId', `Unknown surfaceId "${surfaceId}" — createSurface must be sent first.`)];
175
- }
176
- return applyComponentsList(surface, components);
177
- }
178
- function handleUpdateDataModel(message) {
179
- const { surfaceId, path, value } = message.updateDataModel;
180
- const surface = surfaces.get(surfaceId);
181
- if (!surface) {
182
- return [buildValidationFailedMessage(surfaceId, '/updateDataModel/surfaceId', `Unknown surfaceId "${surfaceId}" — createSurface must be sent first.`)];
183
- }
184
- surface.dataModel = setAtPointer(surface.dataModel, path ?? '/', value);
185
- return [];
186
- }
187
- function handleDeleteSurface(message) {
188
- const { surfaceId } = message.deleteSurface;
189
- if (!surfaces.has(surfaceId)) {
190
- return [buildValidationFailedMessage(surfaceId, '/deleteSurface/surfaceId', `Unknown surfaceId "${surfaceId}" — nothing to delete.`)];
191
- }
192
- // Dropping the whole SurfaceState also drops its pendingActions map — any actionResponse that
193
- // arrives afterward for an actionId that lived here simply won't be found (handled gracefully
194
- // in handleActionResponse below), which is exactly the "deleteSurface mid-flight" adversarial
195
- // case this is designed to answer.
196
- surfaces.delete(surfaceId);
197
- return [];
198
- }
199
- function handleCallFunction(message) {
200
- const { functionCallId, callFunction, wantResponse } = message;
201
- const result = resolveDynamicValue(callFunction, { dataModel: undefined, catalog, side: 'agent' });
202
- if (!result.ok) {
203
- const code = result.reason === 'FUNCTION_NOT_CALLABLE_FROM_SIDE' || result.reason === 'FUNCTION_NOT_REGISTERED' ? 'INVALID_FUNCTION_CALL' : result.reason;
204
- return [buildGenericErrorMessage(code, result.detail, { functionCallId })];
205
- }
206
- if (!wantResponse)
207
- return [];
208
- // `?? null` because `functionResponse.value` is required on the wire and JSON has no
209
- // `undefined`: a `returnType: 'void'` function (the catalog has three) resolves to
210
- // `undefined`, and passing that straight through built a message whose `value` vanished on
211
- // serialization — one this package's own `parseRendererToAgentMessage` correctly refuses.
212
- return [buildFunctionResponseMessage({ functionCallId, call: callFunction.call, value: result.value ?? null })];
213
- }
214
- function handleActionResponse(message) {
215
- const { actionId, actionResponse } = message;
216
- for (const surface of surfaces.values()) {
217
- const pending = surface.pendingActions.get(actionId);
218
- if (!pending)
219
- continue;
220
- surface.pendingActions.delete(actionId);
221
- if ('value' in actionResponse && pending.responsePath) {
222
- surface.dataModel = setAtPointer(surface.dataModel, pending.responsePath, actionResponse.value);
223
- }
224
- return [];
225
- }
226
- // No surface has a pending action under this id — already resolved, never existed, or its
227
- // surface was deleted while the action was in flight. Graceful no-op, not an error: the agent
228
- // sent a well-formed message, it just arrived too late (or was mistaken) to mean anything now.
229
- return [];
230
- }
231
- /** Routes one successfully-parsed envelope to its message-type handler. */
232
- function dispatchAgentMessage(message) {
233
- if ('createSurface' in message)
234
- return handleCreateSurface(message);
235
- if ('updateComponents' in message)
236
- return handleUpdateComponents(message);
237
- if ('updateDataModel' in message)
238
- return handleUpdateDataModel(message);
239
- if ('deleteSurface' in message)
240
- return handleDeleteSurface(message);
241
- if ('callFunction' in message)
242
- return handleCallFunction(message);
243
- return handleActionResponse(message);
244
- }
245
- function applyAgentMessage(raw) {
246
- const parsed = parseAgentToRendererMessage(raw);
247
- if (!parsed.ok)
248
- return buildParseFailureResult(parsed, raw);
249
- const rendererMessages = dispatchAgentMessage(parsed.message);
250
- notify();
251
- return { rendererMessages };
252
- }
253
- function getSurface(surfaceId) {
254
- const surface = surfaces.get(surfaceId);
255
- if (!surface)
256
- return undefined;
257
- return { surfaceId: surface.surfaceId, catalogId: surface.catalogId, components: surface.components, dataModel: surface.dataModel };
258
- }
259
- function getRoot(surfaceId) {
260
- return surfaces.get(surfaceId)?.components.get('root');
261
- }
262
- function resolve(surfaceId, value, itemBasePath, itemIndex) {
263
- const surface = surfaces.get(surfaceId);
264
- const itemScope = itemBasePath !== undefined && itemIndex !== undefined ? { basePath: itemBasePath, index: itemIndex } : undefined;
265
- return resolveDynamicValue(value, { dataModel: surface?.dataModel, catalog, side: 'renderer', ...(itemScope ? { itemScope } : {}) });
266
- }
267
- function resolveActionContext(context, surfaceId) {
268
- const resolved = {};
269
- for (const [key, value] of Object.entries(context ?? {})) {
270
- const result = resolve(surfaceId, value);
271
- resolved[key] = result.ok ? result.value : null;
272
- }
273
- return resolved;
274
- }
275
- /** Looks up the surface, component, and `action` prop `buildAction` needs, or the specific reason it can't. */
276
- function resolveActionTarget(surfaceId, componentId) {
277
- const surface = surfaces.get(surfaceId);
278
- if (!surface)
279
- return { ok: false, reason: `unknown surfaceId "${surfaceId}"` };
280
- const component = surface.components.get(componentId);
281
- if (!component)
282
- return { ok: false, reason: `unknown componentId "${componentId}" on surface "${surfaceId}"` };
283
- const action = component.props.action;
284
- if (!action)
285
- return { ok: false, reason: `component "${componentId}" has no action` };
286
- return { ok: true, surface, action };
287
- }
288
- /** Builds the `action` wire envelope for an agent-event action, registering a pending response if `wantResponse` was set. */
289
- function buildAgentEventAction(surface, surfaceId, componentId, event, now) {
290
- const { name, context, wantResponse, responsePath } = event;
291
- const actionId = wantResponse ? nextActionId() : undefined;
292
- if (actionId) {
293
- surface.pendingActions.set(actionId, { name, sourceComponentId: componentId, responsePath });
294
- }
295
- const payload = {
296
- name,
297
- surfaceId,
298
- sourceComponentId: componentId,
299
- timestamp: new Date(now()).toISOString(),
300
- context: resolveActionContext(context, surfaceId),
301
- ...(wantResponse !== undefined ? { wantResponse } : {}),
302
- ...(actionId !== undefined ? { actionId } : {}),
303
- };
304
- return { ok: true, kind: 'agent', message: buildActionMessage(payload) };
305
- }
306
- function buildAction(surfaceId, componentId, now = Date.now) {
307
- const target = resolveActionTarget(surfaceId, componentId);
308
- if (!target.ok)
309
- return target;
310
- const { surface, action } = target;
311
- if (isLocalFunctionAction(action)) {
312
- return runLocalFunctionAction(surface.dataModel, catalog, action);
313
- }
314
- // `ActionSchema` (validated when this component's props were ingested — see
315
- // `applyComponentsList`) is a closed 2-branch union: `{event}` | `{functionCall}`. Having
316
- // already eliminated the `functionCall` branch above, TypeScript itself narrows `action` to
317
- // the `event` branch here — no runtime `isAgentEventAction` re-check is reachable to fail; a
318
- // malformed third shape could never have made it into `component.props.action` in the first
319
- // place. (Not defensively branched-and-left-untested, per this package's own testing
320
- // discipline — see the zod-issues-array comments above for the same principle.)
321
- return buildAgentEventAction(surface, surfaceId, componentId, action.event, now);
322
- }
323
- return {
324
- applyAgentMessage,
325
- getSurface,
326
- listSurfaceIds: () => [...surfaces.keys()],
327
- getRoot,
328
- buildAction,
329
- resolve,
330
- subscribe: (listener) => {
331
- listeners.add(listener);
332
- return () => listeners.delete(listener);
333
- },
334
- };
335
- }
336
- export { WireComponentSchema };
337
- //# sourceMappingURL=interpreter.js.map
@@ -1,48 +0,0 @@
1
- /**
2
- * @module json-pointer
3
- *
4
- * A minimal RFC 6901 (JSON Pointer) implementation for A2UI's data-model bindings
5
- * (`common_types.json#/$defs/DataBinding`: `{"path": "/user/name"}`). Deliberately small — no
6
- * external dependency pulled in for this; the operations A2UI actually needs (root-scope read,
7
- * collection-scope relative read via string concatenation, write-with-null-deletes) are a thin
8
- * slice of the full RFC.
9
- *
10
- * One documented, deliberate deviation from strict RFC 6901: `updateDataModel`'s own spec text
11
- * (`agent_to_renderer.json`, `UpdateDataModelMessage.path`'s description, fetched verbatim) says
12
- * "An optional path to a location within the data model (e.g., '/user/name'). If omitted, or set
13
- * to '/', refers to the entire data model." Under strict RFC 6901, `""` (empty string) is the
14
- * pointer to the whole document and `"/"` is the pointer to the property keyed by the empty
15
- * string on the root object — two different things. A2UI's own spec text explicitly overloads
16
- * `"/"` to mean "the whole document," not "the empty-string-keyed property." This module follows
17
- * A2UI's own wording exactly (not raw RFC 6901) since that is what a conformant renderer must do;
18
- * flagged here rather than silently treated as an oversight.
19
- */
20
- export interface PointerGetResult {
21
- readonly found: boolean;
22
- readonly value: unknown;
23
- }
24
- /** Splits a non-root pointer into its unescaped reference tokens. `pointer` must start with `/`. */
25
- export declare function parsePointerTokens(pointer: string): string[];
26
- export declare function joinPointer(base: string, ...tokens: string[]): string;
27
- /** Reads the value at `pointer` in `doc`. Never throws on a non-resolving path — `found: false` degrades sanely, matching this port's "a bad binding must not crash the renderer" adversarial requirement (see `resolve.ts`). */
28
- export declare function getAtPointer(doc: unknown, pointer: string): PointerGetResult;
29
- /**
30
- * Writes `value` at `pointer` in `doc`, returning a **new** document (the original is never
31
- * mutated — every intermediate object on the path is shallow-copied). Missing intermediate
32
- * segments are created as plain objects (this port does not attempt array-vs-object inference for
33
- * not-yet-existing intermediate segments — a documented simplification, not a spec requirement).
34
- * `value === null` at a non-root pointer deletes the key/value at that location, per
35
- * `updateDataModel`'s own spec text ("To delete the key/value at path, set value explicitly to
36
- * null."). At the root pointer, `value === null` replaces the entire data model with `null` (there
37
- * is no "key" to delete at the root; documented as this port's own consistent interpretation).
38
- *
39
- * Never throws on a malformed pointer (e.g. missing leading `/`) — degrades to a no-op (`doc`
40
- * returned unchanged), matching `getAtPointer`'s "a bad binding must not crash the renderer"
41
- * contract. `updateDataModel`'s `path` is agent-authored wire data; an uncaught throw here had no
42
- * error boundary above it anywhere in `packages/chat-react` or its hosts to stop it from
43
- * unmounting the whole chat React root (see `ExtEventErrorBoundary`, added as a systemic backstop
44
- * for the same class of bug — this is the specific root cause it would otherwise have had to
45
- * catch).
46
- */
47
- export declare function setAtPointer(doc: unknown, pointer: string, value: unknown): unknown;
48
- //# sourceMappingURL=json-pointer.d.ts.map
@@ -1,174 +0,0 @@
1
- /**
2
- * @module json-pointer
3
- *
4
- * A minimal RFC 6901 (JSON Pointer) implementation for A2UI's data-model bindings
5
- * (`common_types.json#/$defs/DataBinding`: `{"path": "/user/name"}`). Deliberately small — no
6
- * external dependency pulled in for this; the operations A2UI actually needs (root-scope read,
7
- * collection-scope relative read via string concatenation, write-with-null-deletes) are a thin
8
- * slice of the full RFC.
9
- *
10
- * One documented, deliberate deviation from strict RFC 6901: `updateDataModel`'s own spec text
11
- * (`agent_to_renderer.json`, `UpdateDataModelMessage.path`'s description, fetched verbatim) says
12
- * "An optional path to a location within the data model (e.g., '/user/name'). If omitted, or set
13
- * to '/', refers to the entire data model." Under strict RFC 6901, `""` (empty string) is the
14
- * pointer to the whole document and `"/"` is the pointer to the property keyed by the empty
15
- * string on the root object — two different things. A2UI's own spec text explicitly overloads
16
- * `"/"` to mean "the whole document," not "the empty-string-keyed property." This module follows
17
- * A2UI's own wording exactly (not raw RFC 6901) since that is what a conformant renderer must do;
18
- * flagged here rather than silently treated as an oversight.
19
- */
20
- /**
21
- * Whether `record` itself carries `token` — never whether it *inherits* it.
22
- *
23
- * A JSON Pointer addresses a member of a JSON document, and a JSON document has no inherited
24
- * members. The `token in record` form this replaced answered `true` for everything
25
- * `Object.prototype` contributes, so `getAtPointer({}, "/constructor")` resolved the `Object`
26
- * constructor as though the agent's data model contained it.
27
- */
28
- function hasOwnToken(record, token) {
29
- return Object.prototype.hasOwnProperty.call(record, token);
30
- }
31
- /**
32
- * Writes one own, plain data property — never through an inherited accessor.
33
- *
34
- * `record[token] = value` looks like it defines a key, and does for every token but one:
35
- * `__proto__` is an inherited accessor on `Object.prototype`, so assigning to it *sets the
36
- * object's prototype* instead. An agent sending `{"path": "/__proto__", "value": {...}}` therefore
37
- * reshaped the object this function was building rather than putting a key in it. (Narrower than
38
- * classic prototype pollution — the shallow-copy discipline here means only the one returned
39
- * object was affected, never the global `Object.prototype` — but a pointer must address the
40
- * document, so this defines the key RFC 6901 actually named.)
41
- */
42
- function defineOwn(record, token, value) {
43
- Object.defineProperty(record, token, { value, writable: true, enumerable: true, configurable: true });
44
- }
45
- /** `true` for the two spellings A2UI's own spec treats as "the whole document" (see module doc). Anything else is parsed as a normal RFC 6901 pointer. */
46
- function isRootPointer(pointer) {
47
- return pointer === '' || pointer === '/';
48
- }
49
- function unescapeToken(token) {
50
- // RFC 6901 §4: ~1 must be decoded *before* ~0 is checked (a literal '~' that was itself part of
51
- // an escaped '/' must not be re-decoded as '~0').
52
- return token.replaceAll('~1', '/').replaceAll('~0', '~');
53
- }
54
- function escapeToken(token) {
55
- return token.replaceAll('~', '~0').replaceAll('/', '~1');
56
- }
57
- /** Splits a non-root pointer into its unescaped reference tokens. `pointer` must start with `/`. */
58
- export function parsePointerTokens(pointer) {
59
- if (pointer === '')
60
- return [];
61
- if (!pointer.startsWith('/')) {
62
- throw new Error(`invalid JSON Pointer (must start with "/"): ${JSON.stringify(pointer)}`);
63
- }
64
- return pointer.slice(1).split('/').map(unescapeToken);
65
- }
66
- export function joinPointer(base, ...tokens) {
67
- const baseTokens = base === '' || base === '/' ? [] : parsePointerTokens(base);
68
- return '/' + [...baseTokens, ...tokens].map(escapeToken).join('/');
69
- }
70
- /** Reads the value at `pointer` in `doc`. Never throws on a non-resolving path — `found: false` degrades sanely, matching this port's "a bad binding must not crash the renderer" adversarial requirement (see `resolve.ts`). */
71
- export function getAtPointer(doc, pointer) {
72
- if (isRootPointer(pointer))
73
- return { found: true, value: doc };
74
- let tokens;
75
- try {
76
- tokens = parsePointerTokens(pointer);
77
- }
78
- catch {
79
- return { found: false, value: undefined };
80
- }
81
- let current = doc;
82
- for (const token of tokens) {
83
- if (Array.isArray(current)) {
84
- if (!/^(0|[1-9]\d*)$/.test(token))
85
- return { found: false, value: undefined };
86
- const index = Number(token);
87
- if (index >= current.length)
88
- return { found: false, value: undefined };
89
- current = current[index];
90
- continue;
91
- }
92
- if (current !== null && typeof current === 'object') {
93
- const record = current;
94
- if (!hasOwnToken(record, token))
95
- return { found: false, value: undefined };
96
- current = record[token];
97
- continue;
98
- }
99
- return { found: false, value: undefined };
100
- }
101
- return { found: true, value: current };
102
- }
103
- /**
104
- * Writes `value` at `pointer` in `doc`, returning a **new** document (the original is never
105
- * mutated — every intermediate object on the path is shallow-copied). Missing intermediate
106
- * segments are created as plain objects (this port does not attempt array-vs-object inference for
107
- * not-yet-existing intermediate segments — a documented simplification, not a spec requirement).
108
- * `value === null` at a non-root pointer deletes the key/value at that location, per
109
- * `updateDataModel`'s own spec text ("To delete the key/value at path, set value explicitly to
110
- * null."). At the root pointer, `value === null` replaces the entire data model with `null` (there
111
- * is no "key" to delete at the root; documented as this port's own consistent interpretation).
112
- *
113
- * Never throws on a malformed pointer (e.g. missing leading `/`) — degrades to a no-op (`doc`
114
- * returned unchanged), matching `getAtPointer`'s "a bad binding must not crash the renderer"
115
- * contract. `updateDataModel`'s `path` is agent-authored wire data; an uncaught throw here had no
116
- * error boundary above it anywhere in `packages/chat-react` or its hosts to stop it from
117
- * unmounting the whole chat React root (see `ExtEventErrorBoundary`, added as a systemic backstop
118
- * for the same class of bug — this is the specific root cause it would otherwise have had to
119
- * catch).
120
- */
121
- export function setAtPointer(doc, pointer, value) {
122
- if (isRootPointer(pointer))
123
- return value;
124
- let tokens;
125
- try {
126
- tokens = parsePointerTokens(pointer);
127
- }
128
- catch {
129
- return doc;
130
- }
131
- const parentTokens = tokens.slice(0, -1);
132
- const lastToken = tokens[tokens.length - 1];
133
- function recurse(current, remaining) {
134
- if (remaining.length === 0) {
135
- // `current` here is the parent container; apply the final write/delete.
136
- if (Array.isArray(current)) {
137
- if (!/^(0|[1-9]\d*)$/.test(lastToken))
138
- return current;
139
- const index = Number(lastToken);
140
- const next = current.slice();
141
- if (value === null) {
142
- if (index < next.length)
143
- next.splice(index, 1);
144
- return next;
145
- }
146
- next[index] = value;
147
- return next;
148
- }
149
- const record = current !== null && typeof current === 'object' ? { ...current } : {};
150
- if (value === null) {
151
- delete record[lastToken];
152
- return record;
153
- }
154
- defineOwn(record, lastToken, value);
155
- return record;
156
- }
157
- const [head, ...rest] = remaining;
158
- if (Array.isArray(current)) {
159
- if (!head || !/^(0|[1-9]\d*)$/.test(head))
160
- return current;
161
- const index = Number(head);
162
- const next = current.slice();
163
- next[index] = recurse(index < next.length ? next[index] : {}, rest);
164
- return next;
165
- }
166
- const record = current !== null && typeof current === 'object' ? { ...current } : {};
167
- if (!head)
168
- return record;
169
- defineOwn(record, head, recurse(hasOwnToken(record, head) ? record[head] : {}, rest));
170
- return record;
171
- }
172
- return recurse(doc, parentTokens);
173
- }
174
- //# sourceMappingURL=json-pointer.js.map