@theoremjs/web 0.3.4-next.590.g0246066 → 0.3.4-next.594.g145ceaf

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/README.md CHANGED
@@ -237,6 +237,7 @@ const transport = withPageToolDispatch(
237
237
  | `session` | Returns the session id of the caller, for example `${userId}:${conversationId}`. Return `undefined` to refuse the request with a 401. Default: a random id in an HttpOnly, SameSite=Lax cookie that lasts 30 days. |
238
238
  | `host` | App data for the tool handlers (`ctx.host`), such as the signed-in user. |
239
239
  | `prepare` | Your app's own turn, from the one the handler builds (`TurnWiring.prepare` from `@theoremjs/agents`). Set what your app sets per turn, such as `system`, `credentials`, `onStage` and `onAgentCall`; read your app data as `turn.host`. The handler keeps its profile, its tool execution and its abort. Your `onStage` runs before the handler's steering, and your `credentials` are read before the session's. |
240
+ | `onStage` | The server's own stage handler, on every call the handler runs, a host profile's included: after your app's (`prepare`), before steering. For a rule the server adds to whatever the app says, such as a question before each tool that writes. |
240
241
  | `pageToolBroker` | Delivers results to active page dispatches. Default: bounded process memory. |
241
242
  | `conversationStore` | Where the conversation is kept. Required, except for a stateless profile, which must not have one. See [Keep the conversation](#keep-the-conversation). |
242
243
  | `sessionStore`, `steerInbox` | Where open gates are kept, and where steers and stops reach a running turn. Default: process memory, which reaches only turns this process runs. On more than one instance, give a shared `SteerInbox`. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theoremjs/web",
3
- "version": "0.3.4-next.590.g0246066",
3
+ "version": "0.3.4-next.594.g145ceaf",
4
4
  "description": "Theorem on the web platform, with no UI framework: Web-standard server handlers that serve a profile, and the browser client that talks to them.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,6 +25,6 @@
25
25
  },
26
26
  "peerDependencies": {
27
27
  "zod": "^4.6.5",
28
- "@theoremjs/agents": "^0.3.4-next.590.g0246066"
28
+ "@theoremjs/agents": "^0.3.4-next.594.g145ceaf"
29
29
  }
30
30
  }
@@ -32,7 +32,7 @@ import { type PageToolBroker } from './page-dispatch.ts';
32
32
  *
33
33
  * @module
34
34
  */
35
- import { type Profile, type ProfileDefinition, type ProviderHostOptions, type ToolGate, type TurnWiring, type z } from '@theoremjs/agents';
35
+ import { type Profile, type ProfileDefinition, type ProviderHostOptions, type StageHandler, type ToolGate, type TurnWiring, type z } from '@theoremjs/agents';
36
36
  import { type ClientTurnOptions } from '@theoremjs/agents/host';
37
37
  import { type TheoremConversationStore } from './conversation-store.ts';
38
38
  import { type TheoremCredentialStore } from './credential-store.ts';
@@ -71,6 +71,12 @@ export type TheoremHandlerOptions = {
71
71
  * answered, with the `onStage` and `credentials` it gives for that call's turn.
72
72
  */
73
73
  prepare?: TurnWiring['prepare'];
74
+ /**
75
+ * The server's own stage handler, on every call this handler runs, a host's included: after
76
+ * the application's, before steering. For a rule the server adds to whatever the application
77
+ * says, such as a question before each tool that writes.
78
+ */
79
+ onStage?: StageHandler;
74
80
  /**
75
81
  * Resolve the caller's session id — e.g. `${userId}:${conversationId}` from your
76
82
  * auth. Return `undefined` to refuse the request (401). Default: an opaque id
package/server/handler.js CHANGED
@@ -51,7 +51,7 @@ import { checkRequest } from "./request-check.js";
51
51
  import { createMemorySessionStore, DEFAULT_OPERATION_RETENTION_MS, } from "./session-store.js";
52
52
  import { createMemorySteerInbox, steerStage, steerUnitOf, stopOnRequest, } from "./steer-inbox.js";
53
53
  import { sameAcceptedAnswer, sessionToolCoordinator } from "./tool-operations.js";
54
- import { preparedTurn } from "./turn-wiring.js";
54
+ import { preparedTurn, stagesInOrder } from "./turn-wiring.js";
55
55
  const NDJSON_HEADERS = {
56
56
  'content-type': 'application/x-ndjson; charset=utf-8',
57
57
  'cache-control': 'no-store',
@@ -361,7 +361,10 @@ async function* settleAnswered(ctx, request, session, answered) {
361
361
  async function* invokeAnswered(ctx, request, sessionId, { callId, pending, answer }) {
362
362
  const host = await ctx.options.host?.(request);
363
363
  // why: The held call goes on with what the application set for its turn: its stage handler and credentials.
364
- const { credentials, onStage } = await preparedTurn(ctx.options.prepare, { profile: ctx.profile.id, input: pending.turnInput ?? {}, host, signal: request.signal }, { credentials: sessionCredentials(ctx, sessionId) });
364
+ const { credentials, onStage } = await preparedTurn(ctx.options.prepare, { profile: ctx.profile.id, input: pending.turnInput ?? {}, host, signal: request.signal }, {
365
+ credentials: sessionCredentials(ctx, sessionId),
366
+ ...(ctx.options.onStage ? { onStage: ctx.options.onStage } : {}),
367
+ });
365
368
  yield* invokeTool({
366
369
  profile: ctx.profile.id,
367
370
  execution: sessionToolCoordinator(ctx.sessions, sessionId),
@@ -400,6 +403,7 @@ async function* chatTurn(ctx, request, session, turn, key, stop) {
400
403
  stopOnRequest(ctx.inbox, key, stop);
401
404
  }
402
405
  try {
406
+ const onStage = stagesInOrder(ctx.options.onStage, key ? steerStage(ctx.inbox, key) : undefined);
403
407
  const prepared = await preparedTurn(ctx.options.prepare, {
404
408
  profile: ctx.profile.id,
405
409
  execution: sessionToolCoordinator(ctx.sessions, session.id),
@@ -413,7 +417,7 @@ async function* chatTurn(ctx, request, session, turn, key, stop) {
413
417
  ...(turn.effort ? { effort: turn.effort } : {}),
414
418
  }, {
415
419
  credentials: sessionCredentials(ctx, session.id),
416
- ...(key ? { onStage: steerStage(ctx.inbox, key) } : {}),
420
+ ...(onStage ? { onStage } : {}),
417
421
  });
418
422
  const events = runTurn(prepared, provider);
419
423
  yield* recorded(ctx.sessions, session.id, withAuthorizationUrls(ctx, request, session.id, events), {
@@ -575,6 +579,7 @@ async function* callEvents(ctx, request, session, body) {
575
579
  input: body.input,
576
580
  sessionPermissions: state.permissions,
577
581
  credentials: sessionCredentials(ctx, session.id),
582
+ ...(ctx.options.onStage ? { onStage: ctx.options.onStage } : {}),
578
583
  signal: request.signal,
579
584
  host,
580
585
  });