@jigs.design/server 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
@@ -218,6 +218,7 @@ function createReplyParser(deps, history) {
218
218
  };
219
219
  if (error !== void 0) block.error = error;
220
220
  if (compiled.ref !== null) block.ref = compiled.ref;
221
+ if (compiled.toolKeys !== null && compiled.toolKeys !== void 0) block.toolKeys = compiled.toolKeys;
221
222
  return block;
222
223
  };
223
224
  const emit = (block) => {
@@ -615,15 +616,18 @@ async function* repairBlock(deps, block) {
615
616
  else if (event.type === "done") {
616
617
  const body = splitPragma(event.source).body;
617
618
  deps.parser.remember(block.name, body);
619
+ const { toolKeys: _before, ...rest } = block;
618
620
  const repaired = {
619
- ...block,
621
+ ...rest,
620
622
  diagnostics: event.diagnostics,
621
623
  program: program ?? block.program,
622
624
  source: event.source,
623
625
  status: "done"
624
626
  };
625
- const { ref } = deps.session.checker.compile(body);
627
+ const compiled = deps.session.checker.compile(body);
628
+ const { ref } = compiled;
626
629
  if (ref !== null) repaired.ref = ref;
630
+ if (compiled.toolKeys !== null) repaired.toolKeys = compiled.toolKeys;
627
631
  if (result !== void 0) {
628
632
  result.status = "done";
629
633
  if (ref !== null) result.ref = ref;
package/dist/compile.js CHANGED
@@ -4,6 +4,7 @@ import { inputDiagnostics } from "./inputs.js";
4
4
  import { outputDiagnostics } from "./outputs.js";
5
5
  import { splitPragma, versionDiagnostics } from "./pragma.js";
6
6
  import { compileJigs, compileLibrary, spanIn } from "@jigs.design/compiler";
7
+ import { TOOL_RESULTS } from "@jigs.design/prompt";
7
8
  import { componentSchema } from "@jigs.design/host";
8
9
  import { PROGRAM_FORMAT } from "@jigs.design/protocol";
9
10
  //#region src/compile.ts
@@ -57,6 +58,17 @@ function compileCatalog(catalog) {
57
58
  return out;
58
59
  }
59
60
  const TAG = /<([a-z][A-Za-z0-9]*)/g;
61
+ /** Each procedure kind as a diagnostic names it. */
62
+ const KIND_NAMES = {
63
+ action: "an action",
64
+ data: "provided data",
65
+ mutation: "a mutation",
66
+ query: "a query"
67
+ };
68
+ /** The hook that reads or calls a procedure of `kind`: a provided slot is read like a query. */
69
+ function hookFor(kind) {
70
+ return kind === "query" || kind === "data" ? "useQuery" : "useMutation";
71
+ }
60
72
  function procedureDiagnostics(host, source, uses) {
61
73
  const diagnostics = [];
62
74
  const report = (use, code, message) => {
@@ -83,11 +95,11 @@ function procedureDiagnostics(host, source, uses) {
83
95
  }
84
96
  const procedure = Object.hasOwn(host.procedures, use.name) ? host.procedures[use.name] : void 0;
85
97
  if (procedure === void 0) {
86
- report(use, "unknown_procedure", `"${use.name}" is not a query, mutation or action on this host; its handle reports UNKNOWN_PROCEDURE`);
98
+ report(use, "unknown_procedure", `"${use.name}" is not a query, mutation, action or provided data on this host; its handle reports UNKNOWN_PROCEDURE`);
87
99
  continue;
88
100
  }
89
- const expected = procedure.kind === "query" ? "useQuery" : "useMutation";
90
- if (expected !== use.hook) report(use, "procedure_kind", `\`${use.hook}("${use.name}")\` names ${procedure.kind === "action" ? "an" : "a"} ${procedure.kind}. Write \`${expected}("${use.name}")\`${procedure.kind === "query" ? "" : " and call `mutate` from a handler"}`);
101
+ const expected = hookFor(procedure.kind);
102
+ if (expected !== use.hook) report(use, "procedure_kind", `\`${use.hook}("${use.name}")\` names ${KIND_NAMES[procedure.kind]}. Write \`${expected}("${use.name}")\`${expected === "useQuery" ? "" : " and call `mutate` from a handler"}`);
91
103
  }
92
104
  return diagnostics;
93
105
  }
@@ -96,8 +108,7 @@ function procedureTable(host, uses) {
96
108
  for (const use of uses) {
97
109
  if (use.name === null || use.tanstack) continue;
98
110
  const procedure = Object.hasOwn(host.procedures, use.name) ? host.procedures[use.name] : void 0;
99
- const expected = procedure?.kind === "query" ? "useQuery" : "useMutation";
100
- if (procedure === void 0 || expected !== use.hook) continue;
111
+ if (procedure === void 0 || hookFor(procedure.kind) !== use.hook) continue;
101
112
  table.set(use.name, {
102
113
  invalidates: procedure.kind === "mutation" ? procedure.invalidates : [],
103
114
  kind: procedure.kind
@@ -260,6 +271,7 @@ function createChecker(host) {
260
271
  let ref = diagnostics.some((diagnostic) => diagnostic.severity === "error") ? null : refOf(result.program.tree, pages);
261
272
  const define = ref === null ? void 0 : fullLibrary.get(ref.name);
262
273
  if (ref !== null && (define === void 0 || pagePropsProblem(define, ref.props) !== null)) ref = null;
274
+ const procedures = procedureTable(host, [...own, ...reached.uses]);
263
275
  return {
264
276
  advise: (placement) => designChecks({
265
277
  catalog: host.components,
@@ -272,10 +284,11 @@ function createChecker(host) {
272
284
  components: Object.fromEntries(host.components.components.filter((component) => reached.tags.has(component.name)).map((component) => [component.name, componentSchema(component)])),
273
285
  format: PROGRAM_FORMAT,
274
286
  imageHosts: host.imageHosts,
275
- procedures: procedureTable(host, [...own, ...reached.uses]),
287
+ procedures,
276
288
  prose: result.output.constants
277
289
  },
278
- ref
290
+ ref,
291
+ toolKeys: !Object.hasOwn(procedures, TOOL_RESULTS) ? null : reached.uses.some((use) => use.name === TOOL_RESULTS) ? "all" : result.fields[TOOL_RESULTS] ?? []
279
292
  };
280
293
  };
281
294
  const compile = (body) => compileWith(body, tagLibrary, tagFacts);
package/dist/core.js CHANGED
@@ -7,6 +7,7 @@ import { confirmMessage, createConfirmer, memoryConfirmations } from "./confirm.
7
7
  import { processCompileCache, sha256 } from "./compile-cache.js";
8
8
  import { eventStream } from "./events.js";
9
9
  import { invalidationsOf } from "./invalidation.js";
10
+ import { withToolResults } from "./tool-results.js";
10
11
  import { createModelServer } from "./model-server.js";
11
12
  import { systemPromptParts } from "@jigs.design/prompt";
12
13
  import { isCallPolicy } from "@jigs.design/host";
@@ -36,6 +37,10 @@ function strictly(subject, compiled) {
36
37
  function noProps(name, props) {
37
38
  if (Object.keys(props).length > 0) throw new JigsError("BAD_INPUT", `"${name}" is a document page, which takes no props`);
38
39
  }
40
+ /** An `AbortError`, as `fetch` and other APIs reject with on an aborted signal. */
41
+ function isAbortError(cause) {
42
+ return cause instanceof Error && cause.name === "AbortError";
43
+ }
39
44
  /** A call a policy refused. The procedure stays in the prompt. */
40
45
  const denial = (name) => ({ error: {
41
46
  code: "FORBIDDEN",
@@ -91,7 +96,7 @@ function hostFingerprint(host) {
91
96
  kind: procedure.kind,
92
97
  name,
93
98
  output: procedure.output === void 0 ? null : z.toJSONSchema(procedure.output),
94
- projection: procedure.kind === "action" || procedure.projection === void 0 ? null : z.toJSONSchema(procedure.projection.schema)
99
+ projection: procedure.kind === "action" || procedure.kind === "data" || procedure.projection === void 0 ? null : z.toJSONSchema(procedure.projection.schema)
95
100
  })),
96
101
  jigs: host.jigs.map((entry) => [
97
102
  entry.name,
@@ -111,17 +116,26 @@ function defineRun(procedures, handlers) {
111
116
  return handlers;
112
117
  }
113
118
  function createJigsServer(options) {
114
- const { host } = options;
119
+ const tools = options.toolResults === true;
120
+ const host = tools ? withToolResults(options.host) : options.host;
115
121
  checkMarkdownElements(host.components);
116
- for (const [name, procedure] of Object.entries(host.procedures)) if (procedure.kind !== "action" && !Object.hasOwn(options.run, name)) throw new Error(`createJigsServer has no run function for "${name}"`);
122
+ for (const [name, procedure] of Object.entries(host.procedures)) if (procedure.kind !== "action" && procedure.kind !== "data" && !Object.hasOwn(options.run, name)) throw new Error(`createJigsServer has no run function for "${name}"`);
117
123
  const confirmer = confirmerFor(options);
118
124
  const checker = createChecker(host);
119
- const promptOptions = (mode) => options.instructions === void 0 ? { mode } : {
125
+ const promptOptions = (mode) => options.instructions === void 0 ? {
126
+ mode,
127
+ toolResults: tools
128
+ } : {
120
129
  instructions: options.instructions,
121
- mode
130
+ mode,
131
+ toolResults: tools
122
132
  };
123
- const documentParts = systemPromptParts(host, promptOptions("document"));
133
+ const documentParts = systemPromptParts(options.host, {
134
+ ...promptOptions("document"),
135
+ toolResults: false
136
+ });
124
137
  const chatParts = systemPromptParts(host, promptOptions("chat"));
138
+ const toolParts = tools ? systemPromptParts(host, promptOptions("document")) : null;
125
139
  const system = `${documentParts.stable}\n\n${documentParts.variable}`;
126
140
  const chatSystem = `${chatParts.stable}\n\n${chatParts.variable}`;
127
141
  const allowedOrigins = new Set((options.allowedOrigins ?? []).map((entry) => originOf(entry)));
@@ -220,7 +234,11 @@ function createJigsServer(options) {
220
234
  };
221
235
  const dispatch = async (request, context, trace) => {
222
236
  const procedure = Object.hasOwn(host.procedures, request.name) ? host.procedures[request.name] : void 0;
223
- const run = procedure?.kind !== "action" && Object.hasOwn(runs, request.name) ? runs[request.name] : void 0;
237
+ const run = procedure?.kind !== "action" && procedure?.kind !== "data" && Object.hasOwn(runs, request.name) ? runs[request.name] : void 0;
238
+ if (procedure?.kind === "data") return { error: {
239
+ code: "BAD_INPUT",
240
+ message: `"${request.name}" is provided data: it is supplied by the page, not fetched`
241
+ } };
224
242
  if (procedure === void 0 || procedure.kind !== "action" && run === void 0) return { error: {
225
243
  code: "UNKNOWN_PROCEDURE",
226
244
  message: `"${request.name}" is not a query, mutation or action on this host`
@@ -256,6 +274,10 @@ function createJigsServer(options) {
256
274
  try {
257
275
  result = await run(input, context);
258
276
  } catch (error) {
277
+ if (context.signal.aborted || isAbortError(error)) {
278
+ trace.aborted = true;
279
+ return { error: errorData(null) };
280
+ }
259
281
  if (isJigsError(error)) return { error: errorData(error) };
260
282
  onError(error, {
261
283
  name: request.name,
@@ -327,6 +349,7 @@ function createJigsServer(options) {
327
349
  const call = async (request, context) => {
328
350
  const started = performance.now();
329
351
  const trace = {
352
+ aborted: false,
330
353
  gate: null,
331
354
  input: request.input
332
355
  };
@@ -339,7 +362,8 @@ function createJigsServer(options) {
339
362
  name: request.name,
340
363
  outcome: trace.gate ?? "success"
341
364
  };
342
- if ("error" in outcome) {
365
+ if (trace.aborted) report.outcome = "aborted";
366
+ else if ("error" in outcome) {
343
367
  report.error = outcome.error;
344
368
  report.outcome = trace.gate === "denied" ? "denied" : "error";
345
369
  }
@@ -351,11 +375,13 @@ function createJigsServer(options) {
351
375
  const stored = splitPragma(source);
352
376
  const compiled = checker.compile(stored.body);
353
377
  const prefix = source.slice(0, source.length - stored.body.length);
354
- return {
378
+ const result = {
355
379
  advice: belowPrefix(compiled.advise(placement), prefix),
356
380
  diagnostics: [...versionDiagnostics(stored.versions, host.components.version), ...belowPrefix(compiled.diagnostics, prefix)],
357
381
  program: compiled.program
358
382
  };
383
+ if (compiled.toolKeys !== null) result.toolKeys = compiled.toolKeys;
384
+ return result;
359
385
  };
360
386
  /**
361
387
  * `compile` through the process's cache, keyed by what this server
@@ -671,9 +697,17 @@ function createJigsServer(options) {
671
697
  onError,
672
698
  page: pageRequest
673
699
  };
700
+ const data = (name, value) => {
701
+ const slot = Object.hasOwn(host.procedures, name) ? host.procedures[name] : void 0;
702
+ if (slot?.kind !== "data") throw new JigsError("BAD_INPUT", `"${name}" is not provided data on this host`);
703
+ const parsed = slot.output.safeParse(value);
704
+ if (!parsed.success) throw new JigsError("BAD_INPUT", `"${name}": ${issuesText(parsed.error)}`);
705
+ return parsed.data;
706
+ };
674
707
  const core = {
675
708
  call,
676
709
  compile,
710
+ data,
677
711
  events,
678
712
  frame,
679
713
  handler: createHandler({
@@ -691,7 +725,11 @@ function createJigsServer(options) {
691
725
  handlerDeps,
692
726
  host,
693
727
  onError,
694
- prepareSession
728
+ prepareSession,
729
+ toolPrompt: toolParts === null ? null : {
730
+ system: `${toolParts.stable}\n\n${toolParts.variable}`,
731
+ systemPrefix: toolParts.stable
732
+ }
695
733
  }, core, modelOptions)
696
734
  };
697
735
  return core;
package/dist/inputs.js CHANGED
@@ -3,6 +3,11 @@ import { z } from "zod";
3
3
  //#region src/inputs.ts
4
4
  /** A handle declared as `const name = useMutation("procedure")`. */
5
5
  const MUTATION_HANDLE = /(?<![\w$.])(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*useMutation\(\s*(["'`])([A-Za-z_$][\w$]*)\2\s*\)/g;
6
+ /**
7
+ * What may follow a provided slot's name in `useQuery`: the call's end, or
8
+ * an empty input (`undefined` or `null`) before or without options.
9
+ */
10
+ const NO_INPUT = /^(?:,\s*)?\)|^,\s*(?:undefined|null)(?![\w$])/;
6
11
  const QUERY_CALL = /(?<![\w$.])useQuery\(\s*(["'`])([A-Za-z_$][\w$]*)\1\s*/g;
7
12
  /** At most this many problems per call, so one bad literal does not flood the repair. */
8
13
  const MAX_PROBLEMS = 3;
@@ -192,8 +197,18 @@ function inputDiagnostics(host, source) {
192
197
  for (const match of source.matchAll(QUERY_CALL)) {
193
198
  const name = match[2] ?? "";
194
199
  const procedure = procedureOf(name);
195
- if (procedure?.kind !== "query" || procedure.input === void 0) continue;
196
200
  const rest = match.index + match[0].length;
201
+ if (procedure?.kind === "data") {
202
+ if (NO_INPUT.test(source.slice(rest))) continue;
203
+ diagnostics.push({
204
+ code: "data_takes_no_input",
205
+ message: `useQuery("${name}") reads provided data, which takes no input. Drop the input; the app provides this data`,
206
+ severity: "error",
207
+ span: { line: lineAt(source, match.index) }
208
+ });
209
+ continue;
210
+ }
211
+ if (procedure?.kind !== "query" || procedure.input === void 0) continue;
197
212
  const char = source.charAt(rest);
198
213
  if (char !== "," && char !== ")") continue;
199
214
  const [input] = char === ")" ? [] : readArguments(source, rest + 1);
@@ -1,7 +1,9 @@
1
1
  import { INTERNAL_MESSAGE, JigsError, isJigsError } from "./errors.js";
2
+ import { splitPragma } from "./pragma.js";
2
3
  import { createHandler, issuesText, parseBounded } from "./handler.js";
3
4
  import { MAX_CONTENT, runSession } from "./session.js";
4
5
  import { createReplyParser, repairBlock, streamChat } from "./chat.js";
6
+ import { TOOL_RESULTS } from "@jigs.design/prompt";
5
7
  import { z } from "zod";
6
8
  //#region src/model-server.ts
7
9
  /**
@@ -54,6 +56,14 @@ function createModelServer(internals, core, options) {
54
56
  system,
55
57
  systemPrefix: core.promptPrefix("document")
56
58
  };
59
+ const toolPrompt = internals.toolPrompt;
60
+ const toolSessionDeps = toolPrompt === null ? sessionDeps : {
61
+ ...sessionDeps,
62
+ system: toolPrompt.system,
63
+ systemPrefix: toolPrompt.systemPrefix
64
+ };
65
+ /** A session's deps: the slot's prompt when its document reads `toolResults`. */
66
+ const depsFor = (turn) => toolPrompt !== null && turn.document !== null && Object.hasOwn(internals.checker.compile(splitPragma(turn.document).body).program.procedures, TOOL_RESULTS) ? toolSessionDeps : sessionDeps;
57
67
  const chat = async (request, chatOptions) => {
58
68
  const refused = internals.handlerDeps.http.refusal(request);
59
69
  if (refused !== null) throw new JigsError(refused.code, refused.message);
@@ -98,7 +108,7 @@ function createModelServer(internals, core, options) {
98
108
  parser,
99
109
  prompt: input.data.prompt,
100
110
  session: {
101
- ...sessionDeps,
111
+ ...toolSessionDeps,
102
112
  kind: "chat-repair",
103
113
  maxRepairs: repairRounds
104
114
  }
@@ -122,6 +132,7 @@ function createModelServer(internals, core, options) {
122
132
  call: core.call,
123
133
  chat,
124
134
  compile: core.compile,
135
+ data: core.data,
125
136
  events: core.events,
126
137
  frame: core.frame,
127
138
  handler: createHandler({
@@ -129,7 +140,7 @@ function createModelServer(internals, core, options) {
129
140
  session: async (body, call) => {
130
141
  const prepared = await prepareSession(body, call);
131
142
  if ("error" in prepared) return prepared;
132
- return { turn: (turnCall) => runSession(sessionDeps, prepared.turn, prepared.options, turnCall) };
143
+ return { turn: (turnCall) => runSession(depsFor(prepared.turn), prepared.turn, prepared.options, turnCall) };
133
144
  }
134
145
  }),
135
146
  maxBodyBytes: core.maxBodyBytes,
@@ -145,7 +156,7 @@ function createModelServer(internals, core, options) {
145
156
  };
146
157
  return;
147
158
  }
148
- yield* runSession(sessionDeps, prepared.turn, prepared.options, context);
159
+ yield* runSession(depsFor(prepared.turn), prepared.turn, prepared.options, context);
149
160
  },
150
161
  version: core.version
151
162
  };
@@ -0,0 +1,20 @@
1
+ import { TOOL_RESULTS, toolResultsSlot } from "@jigs.design/prompt";
2
+ //#region src/tool-results.ts
3
+ /**
4
+ * `host` with the `toolResults` slot added, for a server with
5
+ * `toolResults: true`: the checker, the calls and the prompts that list it
6
+ * see one more provided slot. Throws when the host already has a procedure
7
+ * of that name.
8
+ */
9
+ function withToolResults(host) {
10
+ if (Object.hasOwn(host.procedures, TOOL_RESULTS)) throw new Error(`The host has a procedure named "${TOOL_RESULTS}", which toolResults: true adds; rename it`);
11
+ return {
12
+ ...host,
13
+ procedures: {
14
+ ...host.procedures,
15
+ [TOOL_RESULTS]: toolResultsSlot
16
+ }
17
+ };
18
+ }
19
+ //#endregion
20
+ export { withToolResults };
package/dist/types.d.ts CHANGED
@@ -2,8 +2,8 @@ import { Advice, DesignCheckCode, Placement } from "./design.js";
2
2
  import { ConfirmationStore } from "./confirm.js";
3
3
  import { EditFailureReason } from "@jigs.design/compiler";
4
4
  import { PromptMode } from "@jigs.design/prompt";
5
- import { ActionDefinition, CallPolicy, Catalog, Host, InputOf, JigDefinition, MutationDefinition, OutputOf, PageName, ProcedureDefinition, Procedures, QueryDefinition } from "@jigs.design/host";
6
- import { ChatBlock, ChatBlockStatus, Diagnostic, Invalidation, JigsErrorData, Message, PageInvalidation, PageProps, PageRef, Program, SessionEvent } from "@jigs.design/protocol";
5
+ import { ActionDefinition, CallPolicy, Catalog, DataDefinition, Host, InputOf, JigDefinition, MutationDefinition, OutputOf, PageName, ProcedureDefinition, Procedures, QueryDefinition } from "@jigs.design/host";
6
+ import { ChatBlock, ChatBlockStatus, Diagnostic, Invalidation, JigsErrorData, JsonValue, Message, PageInvalidation, PageProps, PageRef, Program, SessionEvent } from "@jigs.design/protocol";
7
7
  import { z } from "zod";
8
8
  //#region src/types.d.ts
9
9
  /**
@@ -159,9 +159,10 @@ type DeepReadonly<T> = T extends object ? { readonly [K in keyof T]: DeepReadonl
159
159
  type RunOutput<P extends ProcedureDefinition> = DeepReadonly<OutputOf<P>>;
160
160
  /**
161
161
  * One function per query and mutation. Actions have none: the page handles
162
- * them, and the server only checks their input.
162
+ * them, and the server only checks their input. Provided data has none
163
+ * either: the page supplies it.
163
164
  */
164
- export type RunFunctions<TProcedures extends Procedures, TContext> = { readonly [K in keyof TProcedures as TProcedures[K] extends ActionDefinition ? never : K]: (input: InputOf<TProcedures[K]>, call: CallContext<TContext>) => RunOutput<TProcedures[K]> | Promise<RunOutput<TProcedures[K]>>; };
165
+ export type RunFunctions<TProcedures extends Procedures, TContext> = { readonly [K in keyof TProcedures as TProcedures[K] extends ActionDefinition | DataDefinition ? never : K]: (input: InputOf<TProcedures[K]>, call: CallContext<TContext>) => RunOutput<TProcedures[K]> | Promise<RunOutput<TProcedures[K]>>; };
165
166
  export type ContextFunction<TContext> = (request: Request) => TContext | Promise<TContext>;
166
167
  /** The names of a host's mutations. */
167
168
  type MutationName<TProcedures extends Procedures> = { [K in keyof TProcedures & string]: TProcedures[K] extends MutationDefinition ? K : never; }[keyof TProcedures & string];
@@ -180,7 +181,7 @@ export type PolicyCall<TProcedures extends Procedures, TContext> = { [K in Mutat
180
181
  * as "requires-action" when the server asks the page, and once as
181
182
  * "confirmed" when it ran with the user's approval.
182
183
  */
183
- export type CallReportOutcome = "confirmed" | "denied" | "error" | "requires-action" | "success";
184
+ export type CallReportOutcome = "aborted" | "confirmed" | "denied" | "error" | "requires-action" | "success";
184
185
  /**
185
186
  * One call to a query, mutation or action, or one page request, as
186
187
  * `onCall` receives it.
@@ -189,7 +190,7 @@ export interface CallReport<TContext> {
189
190
  ctx: TContext;
190
191
  /** From the call's arrival to its outcome, in milliseconds. */
191
192
  durationMs: number;
192
- /** What the browser received, when `outcome` is not "success". */
193
+ /** What the browser received, when `outcome` is "denied" or "error". */
193
194
  error?: JigsErrorData;
194
195
  /**
195
196
  * The parsed input, or the input as the browser sent it when the call
@@ -205,7 +206,9 @@ export interface CallReport<TContext> {
205
206
  /**
206
207
  * "denied" when a policy or a bad confirmation refused the call, or
207
208
  * `pagePolicy` a page; "error" for any other failure, including one
208
- * `run` threw and a page no list has.
209
+ * `run` threw and a page no list has; "aborted" when `run` rejected
210
+ * after the browser abandoned the call (its `signal` aborted), which
211
+ * does not reach `onError`.
209
212
  */
210
213
  outcome: CallReportOutcome;
211
214
  }
@@ -388,6 +391,18 @@ export interface JigsServerOptions<TProcedures extends Procedures, TCatalog exte
388
391
  * "confirm": the server refuses to start without it.
389
392
  */
390
393
  secret?: string;
394
+ /**
395
+ * Adds a provided slot, `toolResults`, for a chat whose agent calls
396
+ * tools: the page passes each block the results of the tools called
397
+ * before it, by tool name (`toolData` in `@jigs.design/ai-sdk/client`),
398
+ * and the block reads them with `useQuery("toolResults")` instead of the
399
+ * model copying them into its source. The chat prompt lists the slot and
400
+ * says how to read it, as do chat repairs and a session on a block's
401
+ * source; a plain session's prompt is unchanged. Fixed for the server's
402
+ * life, so the prompt's cached start never changes per turn. A host
403
+ * procedure of that name throws. Default false.
404
+ */
405
+ toolResults?: boolean;
391
406
  }
392
407
  /**
393
408
  * What `withModel` takes: everything that exists only because a model does.
@@ -641,7 +656,20 @@ export interface JigsServer<TContext, TPageName extends string = never> {
641
656
  advice: ReadonlyArray<Advice>;
642
657
  diagnostics: ReadonlyArray<Diagnostic>;
643
658
  program: Program;
659
+ /**
660
+ * With `toolResults: true`, for a source that reads the slot: the tool
661
+ * names it reads, or `"all"` (`ChatBlock.toolKeys`).
662
+ */
663
+ toolKeys?: ReadonlyArray<string> | "all";
644
664
  }>;
665
+ /**
666
+ * `value` parsed with the schema of provided slot `name`, for a page
667
+ * that wants it checked before it passes it to `JigsView` as `data`.
668
+ * Throws `JigsError("BAD_INPUT")` when it does not match, or when `name`
669
+ * is not provided data on this host. The view does not check it: a value
670
+ * that does not match renders as the program reads it.
671
+ */
672
+ data: (name: string, value: JsonValue) => JsonValue;
645
673
  /**
646
674
  * The push stream for `request`, whose `ctx` the caller already has: a
647
675
  * foreign `Origin` is `403`, a server without `events` answers `204`, and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jigs.design/server",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "createJigsServer: checks and streams programs, runs procedures, and serves the sandbox frame.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,11 +29,11 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@jigs.design/compiler": "0.3.0",
33
- "@jigs.design/prompt": "0.3.0",
34
- "@jigs.design/protocol": "0.3.0",
35
- "@jigs.design/runtime": "0.3.0",
36
- "@jigs.design/host": "0.3.0"
32
+ "@jigs.design/compiler": "0.3.2",
33
+ "@jigs.design/prompt": "0.3.2",
34
+ "@jigs.design/host": "0.3.2",
35
+ "@jigs.design/protocol": "0.3.2",
36
+ "@jigs.design/runtime": "0.3.2"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "zod": "^4.0.0"