@jigs.design/server 0.3.1 → 0.3.3

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
@@ -270,6 +271,7 @@ function createChecker(host) {
270
271
  let ref = diagnostics.some((diagnostic) => diagnostic.severity === "error") ? null : refOf(result.program.tree, pages);
271
272
  const define = ref === null ? void 0 : fullLibrary.get(ref.name);
272
273
  if (ref !== null && (define === void 0 || pagePropsProblem(define, ref.props) !== null)) ref = null;
274
+ const procedures = procedureTable(host, [...own, ...reached.uses]);
273
275
  return {
274
276
  advise: (placement) => designChecks({
275
277
  catalog: host.components,
@@ -282,10 +284,11 @@ function createChecker(host) {
282
284
  components: Object.fromEntries(host.components.components.filter((component) => reached.tags.has(component.name)).map((component) => [component.name, componentSchema(component)])),
283
285
  format: PROGRAM_FORMAT,
284
286
  imageHosts: host.imageHosts,
285
- procedures: procedureTable(host, [...own, ...reached.uses]),
287
+ procedures,
286
288
  prose: result.output.constants
287
289
  },
288
- ref
290
+ ref,
291
+ toolKeys: !Object.hasOwn(procedures, TOOL_RESULTS) ? null : reached.uses.some((use) => use.name === TOOL_RESULTS) ? "all" : result.fields[TOOL_RESULTS] ?? []
289
292
  };
290
293
  };
291
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";
@@ -25,7 +26,17 @@ const COMPILER_PROBE = `{@body const [count, setCount] = useState(0)}
25
26
  **done**
26
27
  </if>`;
27
28
  /** What `resolvePage` must return, checked, since a plain-JavaScript one may not. */
28
- const savedPageSchema = z.object({ source: z.string() });
29
+ const savedPageSchema = z.object({
30
+ data: z.record(z.string(), z.json()).optional(),
31
+ source: z.string()
32
+ });
33
+ /**
34
+ * The most JSON a saved page's `data` may be, in characters, the order of
35
+ * `MAX_CONTENT`: the app stored it, so past it the page is `INTERNAL`.
36
+ */
37
+ const MAX_PAGE_DATA = 256e3;
38
+ /** What a page without provided data carries: one object, shared and frozen. */
39
+ const NO_DATA = Object.freeze({});
29
40
  /** The strict rule: an error or warning diagnostic throws. */
30
41
  function strictly(subject, compiled) {
31
42
  const fatal = fatalDiagnostics(compiled.diagnostics);
@@ -115,17 +126,26 @@ function defineRun(procedures, handlers) {
115
126
  return handlers;
116
127
  }
117
128
  function createJigsServer(options) {
118
- const { host } = options;
129
+ const tools = options.toolResults === true;
130
+ const host = tools ? withToolResults(options.host) : options.host;
119
131
  checkMarkdownElements(host.components);
120
132
  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}"`);
121
133
  const confirmer = confirmerFor(options);
122
134
  const checker = createChecker(host);
123
- const promptOptions = (mode) => options.instructions === void 0 ? { mode } : {
135
+ const promptOptions = (mode) => options.instructions === void 0 ? {
136
+ mode,
137
+ toolResults: tools
138
+ } : {
124
139
  instructions: options.instructions,
125
- mode
140
+ mode,
141
+ toolResults: tools
126
142
  };
127
- const documentParts = systemPromptParts(host, promptOptions("document"));
143
+ const documentParts = systemPromptParts(options.host, {
144
+ ...promptOptions("document"),
145
+ toolResults: false
146
+ });
128
147
  const chatParts = systemPromptParts(host, promptOptions("chat"));
148
+ const toolParts = tools ? systemPromptParts(host, promptOptions("document")) : null;
129
149
  const system = `${documentParts.stable}\n\n${documentParts.variable}`;
130
150
  const chatSystem = `${chatParts.stable}\n\n${chatParts.variable}`;
131
151
  const allowedOrigins = new Set((options.allowedOrigins ?? []).map((entry) => originOf(entry)));
@@ -365,11 +385,13 @@ function createJigsServer(options) {
365
385
  const stored = splitPragma(source);
366
386
  const compiled = checker.compile(stored.body);
367
387
  const prefix = source.slice(0, source.length - stored.body.length);
368
- return {
388
+ const result = {
369
389
  advice: belowPrefix(compiled.advise(placement), prefix),
370
390
  diagnostics: [...versionDiagnostics(stored.versions, host.components.version), ...belowPrefix(compiled.diagnostics, prefix)],
371
391
  program: compiled.program
372
392
  };
393
+ if (compiled.toolKeys !== null) result.toolKeys = compiled.toolKeys;
394
+ return result;
373
395
  };
374
396
  /**
375
397
  * `compile` through the process's cache, keyed by what this server
@@ -402,11 +424,13 @@ function createJigsServer(options) {
402
424
  const subject = `Jig "${entry.name}"`;
403
425
  if (entry.kind === "document") startupPages.set(entry.name, {
404
426
  ...strictly(subject, compileStored(entry.source)),
427
+ data: NO_DATA,
405
428
  origin: "shipped",
406
429
  source: entry.source
407
430
  });
408
431
  else if (!checker.needsProps(entry.name)) startupPages.set(entry.name, {
409
432
  ...strictly(subject, compileDefine(entry.name, {})),
433
+ data: NO_DATA,
410
434
  origin: "shipped",
411
435
  source: checker.pageBody(entry.name, {})
412
436
  });
@@ -420,6 +444,7 @@ function createJigsServer(options) {
420
444
  const compiled = await cached("page", body, () => compileDefine(entry.name, props));
421
445
  return {
422
446
  ...strictly(`Page "${entry.name}"`, compiled),
447
+ data: NO_DATA,
423
448
  origin: "shipped",
424
449
  source: body
425
450
  };
@@ -431,7 +456,10 @@ function createJigsServer(options) {
431
456
  });
432
457
  return new JigsError("INTERNAL", INTERNAL_MESSAGE);
433
458
  };
434
- /** The source `resolvePage` finds for `call.ctx`, checked; null when it finds none. */
459
+ /**
460
+ * The page `resolvePage` finds for `call.ctx`, checked: its source and
461
+ * the data it keeps (empty without). Null when it finds none.
462
+ */
435
463
  const savedSource = async (name, props, lookup) => {
436
464
  if (options.resolvePage === void 0) return null;
437
465
  let found;
@@ -446,21 +474,26 @@ function createJigsServer(options) {
446
474
  }
447
475
  if (found === null) return null;
448
476
  const checked = savedPageSchema.safeParse(found);
449
- if (!checked.success) throw internal(/* @__PURE__ */ new Error(`resolvePage returned ${String(found)} for "${name}"; return { source } or null`), name);
450
- const source = checked.data.source;
451
- if (source.length > 2e5) throw internal(/* @__PURE__ */ new Error(`The source resolvePage returned for "${name}" is longer than ${MAX_CONTENT} characters`), name);
477
+ if (!checked.success) throw internal(/* @__PURE__ */ new Error(`resolvePage returned ${String(found)} for "${name}"; return { source, data? } or null`), name);
478
+ const { data } = checked.data;
479
+ if (checked.data.source.length > 2e5) throw internal(/* @__PURE__ */ new Error(`The source resolvePage returned for "${name}" is longer than ${MAX_CONTENT} characters`), name);
480
+ if (data !== void 0 && JSON.stringify(data).length > MAX_PAGE_DATA) throw internal(/* @__PURE__ */ new Error(`The data resolvePage returned for "${name}" is longer than ${MAX_PAGE_DATA} characters of JSON`), name);
452
481
  noProps(name, props);
453
- return source;
482
+ return {
483
+ data: data ?? NO_DATA,
484
+ source: checked.data.source
485
+ };
454
486
  };
455
487
  /** A page `resolvePage` finds for `call.ctx`, compiled leniently unless `strict`. */
456
488
  const savedPage = async (name, props, lookup) => {
457
- const source = await savedSource(name, props, lookup);
458
- if (source === null) return null;
459
- const compiled = await compileCached(source);
489
+ const saved = await savedSource(name, props, lookup);
490
+ if (saved === null) return null;
491
+ const compiled = await compileCached(saved.source);
460
492
  return {
461
493
  ...lookup.strict === true ? strictly(`Page "${name}"`, compiled) : compiled,
494
+ data: saved.data,
462
495
  origin: "saved",
463
- source
496
+ source: saved.source
464
497
  };
465
498
  };
466
499
  const resolve = async (name, props = {}, lookup) => {
@@ -514,6 +547,7 @@ function createJigsServer(options) {
514
547
  } else try {
515
548
  const resolved = await page(name, props, context);
516
549
  outcome = resolved === null ? { error: notFound } : {
550
+ data: resolved.data,
517
551
  diagnostics: resolved.diagnostics,
518
552
  program: resolved.program,
519
553
  version
@@ -548,7 +582,10 @@ function createJigsServer(options) {
548
582
  */
549
583
  const pageSource = async (name, props, context) => {
550
584
  const entry = jigsByName.get(name);
551
- if (entry === void 0) return savedSource(name, props, context);
585
+ if (entry === void 0) {
586
+ const saved = await savedSource(name, props, context);
587
+ return saved === null ? null : saved.source;
588
+ }
552
589
  const shippedName = name;
553
590
  if (!entry.page || !pageAllowed(context.ctx, shippedName, props)) return null;
554
591
  if (entry.kind === "document") {
@@ -713,7 +750,11 @@ function createJigsServer(options) {
713
750
  handlerDeps,
714
751
  host,
715
752
  onError,
716
- prepareSession
753
+ prepareSession,
754
+ toolPrompt: toolParts === null ? null : {
755
+ system: `${toolParts.stable}\n\n${toolParts.variable}`,
756
+ systemPrefix: toolParts.stable
757
+ }
717
758
  }, core, modelOptions)
718
759
  };
719
760
  return core;
@@ -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
  }
@@ -130,7 +140,7 @@ function createModelServer(internals, core, options) {
130
140
  session: async (body, call) => {
131
141
  const prepared = await prepareSession(body, call);
132
142
  if ("error" in prepared) return prepared;
133
- return { turn: (turnCall) => runSession(sessionDeps, prepared.turn, prepared.options, turnCall) };
143
+ return { turn: (turnCall) => runSession(depsFor(prepared.turn), prepared.turn, prepared.options, turnCall) };
134
144
  }
135
145
  }),
136
146
  maxBodyBytes: core.maxBodyBytes,
@@ -146,7 +156,7 @@ function createModelServer(internals, core, options) {
146
156
  };
147
157
  return;
148
158
  }
149
- yield* runSession(sessionDeps, prepared.turn, prepared.options, context);
159
+ yield* runSession(depsFor(prepared.turn), prepared.turn, prepared.options, context);
150
160
  },
151
161
  version: core.version
152
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
@@ -289,8 +289,15 @@ export interface ErrorInfo {
289
289
  */
290
290
  type: "call" | "chat" | "events" | "page" | "session";
291
291
  }
292
- /** A page `resolvePage` found: the source as the app stored it. */
292
+ /** A page `resolvePage` found: the source as the app stored it, and what it reads. */
293
293
  export interface SavedPage {
294
+ /**
295
+ * Provided data the page is drawn with, by slot: what the app kept with
296
+ * it, such as the tool results a chat block had. Fixed to the page: it
297
+ * travels beside the program and is cached with it. Not parsed against
298
+ * the slots' schemas; `jigs.data` checks a value before it is stored.
299
+ */
300
+ data?: Readonly<Record<string, JsonValue>>;
294
301
  /** The source, with its pragma line when the app kept it. */
295
302
  source: string;
296
303
  }
@@ -298,6 +305,7 @@ export interface SavedPage {
298
305
  * Finds a page the host does not ship, by name, for the caller `ctx` names.
299
306
  * `null` is "no such page for this caller". Runs on every request for the
300
307
  * name; cache in it if the store is slow. Throw a `JigsError` to refuse.
308
+ * `data` is optional: the provided slots the page keeps, as JSON.
301
309
  */
302
310
  export type PageResolver<TContext> = (name: string, call: {
303
311
  ctx: TContext;
@@ -391,6 +399,18 @@ export interface JigsServerOptions<TProcedures extends Procedures, TCatalog exte
391
399
  * "confirm": the server refuses to start without it.
392
400
  */
393
401
  secret?: string;
402
+ /**
403
+ * Adds a provided slot, `toolResults`, for a chat whose agent calls
404
+ * tools: the page passes each block the results of the tools called
405
+ * before it, by tool name (`toolData` in `@jigs.design/ai-sdk/client`),
406
+ * and the block reads them with `useQuery("toolResults")` instead of the
407
+ * model copying them into its source. The chat prompt lists the slot and
408
+ * says how to read it, as do chat repairs and a session on a block's
409
+ * source; a plain session's prompt is unchanged. Fixed for the server's
410
+ * life, so the prompt's cached start never changes per turn. A host
411
+ * procedure of that name throws. Default false.
412
+ */
413
+ toolResults?: boolean;
394
414
  }
395
415
  /**
396
416
  * What `withModel` takes: everything that exists only because a model does.
@@ -590,6 +610,13 @@ export interface ChatTurn {
590
610
  export interface ResolvedPage {
591
611
  /** The design checks' advice, numbered like `diagnostics`. */
592
612
  advice: ReadonlyArray<Advice>;
613
+ /**
614
+ * Provided data the page is drawn with, by slot: what `resolvePage`
615
+ * returned beside the source. Empty for a shipped page, whose route
616
+ * supplies its slots. Pass it to the view with the app's own slots:
617
+ * `data={{ ...page.data, ...own }}`.
618
+ */
619
+ data: Readonly<Record<string, JsonValue>>;
593
620
  /**
594
621
  * Never an error or warning for a shipped page, or a saved one compiled
595
622
  * with `strict`. A saved page is lenient by default: these say what
@@ -644,6 +671,11 @@ export interface JigsServer<TContext, TPageName extends string = never> {
644
671
  advice: ReadonlyArray<Advice>;
645
672
  diagnostics: ReadonlyArray<Diagnostic>;
646
673
  program: Program;
674
+ /**
675
+ * With `toolResults: true`, for a source that reads the slot: the tool
676
+ * names it reads, or `"all"` (`ChatBlock.toolKeys`).
677
+ */
678
+ toolKeys?: ReadonlyArray<string> | "all";
647
679
  }>;
648
680
  /**
649
681
  * `value` parsed with the schema of provided slot `name`, for a page
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jigs.design/server",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
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/host": "0.3.1",
33
- "@jigs.design/compiler": "0.3.1",
34
- "@jigs.design/protocol": "0.3.1",
35
- "@jigs.design/runtime": "0.3.1",
36
- "@jigs.design/prompt": "0.3.1"
32
+ "@jigs.design/prompt": "0.3.3",
33
+ "@jigs.design/host": "0.3.3",
34
+ "@jigs.design/compiler": "0.3.3",
35
+ "@jigs.design/protocol": "0.3.3",
36
+ "@jigs.design/runtime": "0.3.3"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "zod": "^4.0.0"