@jigs.design/ai-sdk 0.3.0 → 0.3.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/dist/chat.js CHANGED
@@ -204,7 +204,7 @@ async function reviveJigs(jigs, messages) {
204
204
  ...message,
205
205
  parts: await Promise.all(message.parts.map(async (part) => {
206
206
  if (part.type !== "data-jigs" || !("data" in part)) return part;
207
- const stored = storedBlock.parse(part.data);
207
+ const { toolKeys: _stored, ...stored } = storedBlock.parse(part.data);
208
208
  const compiled = await jigs.compile(stored.source, { strict: false });
209
209
  const data = {
210
210
  ...stored,
@@ -212,6 +212,7 @@ async function reviveJigs(jigs, messages) {
212
212
  program: compiled.program,
213
213
  status: "done"
214
214
  };
215
+ if (compiled.toolKeys !== void 0) data.toolKeys = compiled.toolKeys;
215
216
  return {
216
217
  ...part,
217
218
  data
package/dist/client.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import { JigsBlockData, JigsDataTypes, JigsUIMessage, StoredJigsBlock, StoredJigsUIMessage, recentMessages, withoutPrograms } from "./stored.js";
2
- export { type JigsBlockData, type JigsDataTypes, type JigsUIMessage, type StoredJigsBlock, type StoredJigsUIMessage, recentMessages, withoutPrograms };
2
+ import { ToolDataOptions, ToolPart, toolData, toolValue } from "./tool-data.js";
3
+ export { type JigsBlockData, type JigsDataTypes, type JigsUIMessage, type StoredJigsBlock, type StoredJigsUIMessage, type ToolDataOptions, type ToolPart, recentMessages, toolData, toolValue, withoutPrograms };
package/dist/client.js CHANGED
@@ -1 +1,3 @@
1
- export { recentMessages, withoutPrograms } from "./stored.js";
1
+ import { recentMessages, withoutPrograms } from "./stored.js";
2
+ import { toolData, toolValue } from "./tool-data.js";
3
+ export { recentMessages, toolData, toolValue, withoutPrograms };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { JigsBlockData, JigsDataTypes, JigsUIMessage, StoredJigsBlock, StoredJigsUIMessage, recentMessages, withoutPrograms } from "./stored.js";
2
+ import { ToolDataOptions, ToolPart, toolData, toolValue } from "./tool-data.js";
2
3
  import { JigsChat, jigsChat, reviveJigs } from "./chat.js";
3
4
  import { ModelSettings, aiSdkModel, cachedInstructions } from "./model.js";
4
5
  import { jigsDataSchemas } from "./schemas.js";
5
- export { type JigsBlockData, type JigsChat, type JigsDataTypes, type JigsUIMessage, type ModelSettings, type StoredJigsBlock, type StoredJigsUIMessage, aiSdkModel, cachedInstructions, jigsChat, jigsDataSchemas, recentMessages, reviveJigs, withoutPrograms };
6
+ export { type JigsBlockData, type JigsChat, type JigsDataTypes, type JigsUIMessage, type ModelSettings, type StoredJigsBlock, type StoredJigsUIMessage, type ToolDataOptions, type ToolPart, aiSdkModel, cachedInstructions, jigsChat, jigsDataSchemas, recentMessages, reviveJigs, toolData, toolValue, withoutPrograms };
package/dist/index.js CHANGED
@@ -2,4 +2,5 @@ import { jigsDataSchemas } from "./schemas.js";
2
2
  import { jigsChat, reviveJigs } from "./chat.js";
3
3
  import { aiSdkModel, cachedInstructions } from "./model.js";
4
4
  import { recentMessages, withoutPrograms } from "./stored.js";
5
- export { aiSdkModel, cachedInstructions, jigsChat, jigsDataSchemas, recentMessages, reviveJigs, withoutPrograms };
5
+ import { toolData, toolValue } from "./tool-data.js";
6
+ export { aiSdkModel, cachedInstructions, jigsChat, jigsDataSchemas, recentMessages, reviveJigs, toolData, toolValue, withoutPrograms };
package/dist/schemas.d.ts CHANGED
@@ -63,6 +63,7 @@ export declare const jigsDataSchemas: {
63
63
  repairing: "repairing";
64
64
  streaming: "streaming";
65
65
  }>;
66
+ toolKeys: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodLiteral<"all">]>>;
66
67
  version: z.ZodNumber;
67
68
  }, z.core.$strip>;
68
69
  };
package/dist/schemas.js CHANGED
@@ -41,6 +41,7 @@ const storedBlock = z.object({
41
41
  "repairing",
42
42
  "done"
43
43
  ]),
44
+ toolKeys: z.union([z.array(z.string()), z.literal("all")]).exactOptional(),
44
45
  version: z.number()
45
46
  });
46
47
  /**
@@ -0,0 +1,48 @@
1
+ import { JsonValue } from "@jigs.design/protocol";
2
+ import { DynamicToolUIPart, ToolUIPart, UIMessage } from "ai";
3
+ //#region src/tool-data.d.ts
4
+ /** A tool call in a message: a `tool-<name>` part, or a `dynamic-tool` one. */
5
+ export type ToolPart = DynamicToolUIPart | ToolUIPart;
6
+ type JsonObject = {
7
+ readonly [key: string]: JsonValue;
8
+ };
9
+ /**
10
+ * A tool part's output as a block reads it: what the model was shown, as
11
+ * JSON. Undefined for no value: a call not finished (a preliminary output
12
+ * too, which the model never sees), one that failed or was refused, an MCP
13
+ * result with `isError`, or an output over 32 KB of JSON. An output is read
14
+ * as JSON, as it crossed the wire. A tool from an MCP client
15
+ * (`toolMetadata.clientName`, which `@ai-sdk/mcp` sets) is read as
16
+ * `mcpToModelOutput` shows it: its structured content, else its text parts,
17
+ * parsed when there is one and it is JSON.
18
+ */
19
+ export declare function toolValue(part: ToolPart): JsonValue | undefined;
20
+ export interface ToolDataOptions {
21
+ /**
22
+ * A tool's value for a block instead of `toolValue`'s, for an app tool
23
+ * whose `toModelOutput` shows the model a different shape from its
24
+ * output; undefined for no value. Keep the same function for a page:
25
+ * results are reused by it.
26
+ */
27
+ value?: (toolName: string, part: ToolPart) => JsonValue | undefined;
28
+ }
29
+ /**
30
+ * Each jig's provided data from the tool calls of the conversation, by
31
+ * block key, for `JigsView`'s `data` on a server with `toolResults: true`:
32
+ * `<JigsView data={{ ...hostData, ...tools.get(block.key) }} … />`. Only a
33
+ * block that reads the slot (`ChatBlock.toolKeys`) has an entry, cut to the
34
+ * tools it reads.
35
+ *
36
+ * A block version reads, for each tool: the latest value before it in the
37
+ * reply that wrote it; else its previous version's value; else the latest
38
+ * value earlier in the conversation. A failed or refused call in that reply
39
+ * removes the tool's entry, so the block shows its empty state rather than
40
+ * an older result. A tool called twice in one step leaves the last result.
41
+ * While a block's results are the same, it gets the same object, so the view
42
+ * does not compare them again.
43
+ */
44
+ export declare function toolData<TMessage extends UIMessage>(messages: ReadonlyArray<TMessage>, options?: ToolDataOptions): ReadonlyMap<string, {
45
+ toolResults: JsonObject;
46
+ }>;
47
+ //#endregion
48
+ export {};
@@ -0,0 +1,172 @@
1
+ //#region src/tool-data.ts
2
+ /** The most JSON one tool's value may take before a block gets none of it. */
3
+ const MAX_VALUE_CHARS = 32768;
4
+ function isList(value) {
5
+ return Array.isArray(value);
6
+ }
7
+ function isObject(value) {
8
+ return typeof value === "object" && value !== null && !isList(value);
9
+ }
10
+ function isText(value) {
11
+ return typeof value === "string";
12
+ }
13
+ /** JSON text read back; the caller made it, so it is JSON. */
14
+ function parsed(text) {
15
+ return JSON.parse(text);
16
+ }
17
+ /** The tool a part called, or null for a part that is not a tool call. */
18
+ function toolNameOf(part) {
19
+ if (part.type === "dynamic-tool") return part.toolName;
20
+ return part.type.startsWith("tool-") ? part.type.slice(5) : null;
21
+ }
22
+ function isToolPart(part) {
23
+ return toolNameOf(part) !== null && "state" in part;
24
+ }
25
+ /** A block's `data-jigs` part, with its data as `jigsChat` sent it. */
26
+ function isBlockPart(part) {
27
+ return part.type === "data-jigs" && "data" in part && typeof part.data === "object" && part.data !== null && "key" in part.data && "name" in part.data;
28
+ }
29
+ /** What `mcpToModelOutput` (`@ai-sdk/mcp`) shows the model of one content part. */
30
+ function contentText(part) {
31
+ if (!isObject(part)) return null;
32
+ if (part.type === "text" && isText(part.text)) return part.text;
33
+ return part.type === "image" ? null : JSON.stringify(part);
34
+ }
35
+ /** An MCP tool's `CallToolResult` as the model was shown it. */
36
+ function mcpValue(output) {
37
+ if (!isObject(output) || output.isError === true) return;
38
+ const structured = output.structuredContent;
39
+ if (structured !== void 0) return structured;
40
+ const content = output.content;
41
+ if (content === void 0 || !isList(content)) return output;
42
+ const texts = content.flatMap((part) => {
43
+ const text = contentText(part);
44
+ return text === null ? [] : [text];
45
+ });
46
+ const [only] = texts;
47
+ if (texts.length === 1 && only !== void 0) try {
48
+ return parsed(only);
49
+ } catch {
50
+ return only;
51
+ }
52
+ return texts.length === 0 ? void 0 : texts.join("\n");
53
+ }
54
+ /** Values already worked out, by part; null for none. */
55
+ const values = /* @__PURE__ */ new WeakMap();
56
+ /**
57
+ * A tool part's output as a block reads it: what the model was shown, as
58
+ * JSON. Undefined for no value: a call not finished (a preliminary output
59
+ * too, which the model never sees), one that failed or was refused, an MCP
60
+ * result with `isError`, or an output over 32 KB of JSON. An output is read
61
+ * as JSON, as it crossed the wire. A tool from an MCP client
62
+ * (`toolMetadata.clientName`, which `@ai-sdk/mcp` sets) is read as
63
+ * `mcpToModelOutput` shows it: its structured content, else its text parts,
64
+ * parsed when there is one and it is JSON.
65
+ */
66
+ function toolValue(part) {
67
+ if (part.state !== "output-available" || part.preliminary === true) return;
68
+ const known = values.get(part);
69
+ if (known !== void 0) return known ?? void 0;
70
+ const text = JSON.stringify(part.output);
71
+ let value;
72
+ if (text !== void 0 && text.length <= MAX_VALUE_CHARS) {
73
+ const output = parsed(text);
74
+ value = part.toolMetadata?.clientName === void 0 ? output : mcpValue(output);
75
+ }
76
+ values.set(part, value ?? null);
77
+ return value;
78
+ }
79
+ /** What a finished tool part does to a snapshot: sets its tool's value, or removes it. */
80
+ function apply(snapshot, part, value) {
81
+ const name = toolNameOf(part) ?? "";
82
+ if (value === void 0) snapshot.delete(name);
83
+ else snapshot.set(name, {
84
+ id: `${part.toolCallId}:${part.state}`,
85
+ value
86
+ });
87
+ }
88
+ /** Whether a part settles its tool's value: a final output, an error or a refusal. */
89
+ const settles = (part) => part.state === "output-available" && part.preliminary !== true || part.state === "output-error" || part.state === "output-denied";
90
+ /** `snapshot` cut to `keys`, as the record a block reads. */
91
+ function project(snapshot, keys) {
92
+ const names = (keys === "all" ? [...snapshot.keys()] : keys).toSorted();
93
+ const record = {};
94
+ const ids = [];
95
+ for (const name of names) {
96
+ const entry = snapshot.get(name);
97
+ if (entry !== void 0) {
98
+ record[name] = entry.value;
99
+ ids.push(`${name}=${entry.id}`);
100
+ }
101
+ }
102
+ return {
103
+ fingerprint: ids.join("\n"),
104
+ record
105
+ };
106
+ }
107
+ /** A block's results as last passed, by its block data, for each `value` function. */
108
+ const remembered = /* @__PURE__ */ new WeakMap();
109
+ const DEFAULT_VALUE = {};
110
+ /**
111
+ * Each jig's provided data from the tool calls of the conversation, by
112
+ * block key, for `JigsView`'s `data` on a server with `toolResults: true`:
113
+ * `<JigsView data={{ ...hostData, ...tools.get(block.key) }} … />`. Only a
114
+ * block that reads the slot (`ChatBlock.toolKeys`) has an entry, cut to the
115
+ * tools it reads.
116
+ *
117
+ * A block version reads, for each tool: the latest value before it in the
118
+ * reply that wrote it; else its previous version's value; else the latest
119
+ * value earlier in the conversation. A failed or refused call in that reply
120
+ * removes the tool's entry, so the block shows its empty state rather than
121
+ * an older result. A tool called twice in one step leaves the last result.
122
+ * While a block's results are the same, it gets the same object, so the view
123
+ * does not compare them again.
124
+ */
125
+ function toolData(messages, options = {}) {
126
+ const valueOf = options.value;
127
+ const bucket = valueOf ?? DEFAULT_VALUE;
128
+ const cache = remembered.get(bucket) ?? /* @__PURE__ */ new WeakMap();
129
+ remembered.set(bucket, cache);
130
+ const result = /* @__PURE__ */ new Map();
131
+ const conversation = /* @__PURE__ */ new Map();
132
+ const versions = /* @__PURE__ */ new Map();
133
+ for (const message of messages) {
134
+ const before = new Map(conversation);
135
+ /** The settled tool calls of this message so far, in order. */
136
+ const settled = [];
137
+ for (const part of message.parts) {
138
+ if (isToolPart(part)) {
139
+ if (settles(part)) {
140
+ const value = valueOf === void 0 ? toolValue(part) : valueOf(toolNameOf(part) ?? "", part);
141
+ settled.push({
142
+ part,
143
+ value
144
+ });
145
+ }
146
+ continue;
147
+ }
148
+ if (!isBlockPart(part)) continue;
149
+ const block = part.data;
150
+ const snapshot = new Map(versions.get(block.name) ?? before);
151
+ for (const entry of settled) apply(snapshot, entry.part, entry.value);
152
+ versions.set(block.name, snapshot);
153
+ if (block.toolKeys === void 0) continue;
154
+ const projected = project(snapshot, block.toolKeys);
155
+ const known = cache.get(block);
156
+ if (known !== void 0 && known.fingerprint === projected.fingerprint) {
157
+ result.set(block.key, known.data);
158
+ continue;
159
+ }
160
+ const data = { toolResults: projected.record };
161
+ cache.set(block, {
162
+ ...projected,
163
+ data
164
+ });
165
+ result.set(block.key, data);
166
+ }
167
+ for (const entry of settled) apply(conversation, entry.part, entry.value);
168
+ }
169
+ return result;
170
+ }
171
+ //#endregion
172
+ export { toolData, toolValue };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jigs.design/ai-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Jigs on the AI SDK: a model function for createJigsServer, and chat mode for useChat.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,8 +29,8 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@jigs.design/protocol": "0.3.0",
33
- "@jigs.design/server": "0.3.0"
32
+ "@jigs.design/protocol": "0.3.2",
33
+ "@jigs.design/server": "0.3.2"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "ai": "^7.0.0",
@@ -40,7 +40,7 @@
40
40
  "@ai-sdk/provider": "4.0.27",
41
41
  "ai": "7.0.140",
42
42
  "zod": "4.6.7",
43
- "@jigs.design/catalog": "0.3.0",
44
- "@jigs.design/host": "0.3.0"
43
+ "@jigs.design/catalog": "0.3.2",
44
+ "@jigs.design/host": "0.3.2"
45
45
  }
46
46
  }