@theoremjs/web 0.3.4-next.679.gcdf1a3a → 0.3.4-next.693.g4ab1526
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 +2 -0
- package/client/chat.d.ts +3 -1
- package/client/chat.js +3 -2
- package/client/index.d.ts +1 -1
- package/client/live/live-page-tool.d.ts +3 -0
- package/client/live-client.d.ts +6 -1
- package/client/live-client.js +12 -1
- package/client/live-messages.d.ts +3 -1
- package/client/live-messages.js +2 -1
- package/client/transport.d.ts +9 -0
- package/client/transport.js +8 -0
- package/package.json +2 -2
- package/server/conversation-store.d.ts +3 -1
- package/server/conversation-store.js +2 -0
- package/server/conversation.d.ts +3 -1
- package/server/conversation.js +8 -1
- package/server/handler.d.ts +5 -0
- package/server/handler.js +11 -1
- package/server/mod.d.ts +1 -1
- package/server/session-boundary.d.ts +3 -1
- package/server/session-boundary.js +3 -0
- package/server/turn-input.d.ts +1 -1
- package/server/turn-input.js +1 -0
package/README.md
CHANGED
|
@@ -199,6 +199,7 @@ await chat.submit();
|
|
|
199
199
|
- `getState()` returns a `ChatState`. It is a new object after each change, and the same object between changes. So you can compare it by reference, as React's `useSyncExternalStore` does.
|
|
200
200
|
- `subscribe(listener)` calls the listener once for each group of changes, after the code that made them has run. It returns the function that unsubscribes. Until the first subscribe or send, the chat asks nothing of the host. Then it reads a resumed conversation that came without its `conversationView`, and answers a restored page gate as unanswered.
|
|
201
201
|
- `update(options)` gives the controller your latest options, such as a new `context`, `slots`, or `pageTools`. Each turn reads the options as they are when it is sent. `conversation`, `conversationView`, `initialText`, `initialFiles`, `initialVoice`, and `initialPending` are read only when the controller is created.
|
|
202
|
+
- A live call takes `stated` in its options, and `setStated(stated)` changes it during the call. As with `setSlots`, the call is taken up again with the prompt lines the new value chooses.
|
|
202
203
|
- Each method is bound to the controller. Pass it as a callback as it is.
|
|
203
204
|
|
|
204
205
|
`state.phase` is `'idle'`, `'streaming'`, or `'gated'`. The methods for the composer depend on it:
|
|
@@ -261,6 +262,7 @@ const transport = withPageToolDispatch(
|
|
|
261
262
|
| `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. A call held at a gate runs on, once answered, with the `host`, `onStage` and `credentials` you give for that call's turn. |
|
|
262
263
|
| `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. |
|
|
263
264
|
| `pageToolBroker` | Delivers results to active page dispatches. Default: bounded process memory. |
|
|
265
|
+
| `pageStatesTurn` | Let the page say which prompt lines a turn gets, through `stated` on a turn or a live call. Set it only for a page you trust as yourself, such as a builder's own tool. Without it, a request with `stated` is refused. |
|
|
264
266
|
| `conversationStore` | Where the conversation is kept. Required, except for a stateless profile, which must not have one. See [Keep the conversation](#keep-the-conversation). |
|
|
265
267
|
| `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`. |
|
|
266
268
|
| `credentialStore` | Where tool credentials are kept, by session. Default: process memory. |
|
package/client/chat.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type AttachmentValidationIssue, type ComposerMenuAction, type ComposerP
|
|
|
2
2
|
import { type ClientFailure } from './failure.ts';
|
|
3
3
|
import { type PageTools } from './live/live-page-tool.ts';
|
|
4
4
|
import type { AnsweringGate, ToolDecisionAction } from './tool-resume.ts';
|
|
5
|
-
import { type ConversationView, type TheoremTransport, type TurnEventSink } from './transport.ts';
|
|
5
|
+
import { type ConversationView, type StatedTurn, type TheoremTransport, type TurnEventSink } from './transport.ts';
|
|
6
6
|
export type ChatOptions = {
|
|
7
7
|
transport: TheoremTransport;
|
|
8
8
|
/** The profile's composer interface, as `transport.describe()` returns it. `null` while it loads. */
|
|
@@ -42,6 +42,8 @@ export type ChatOptions = {
|
|
|
42
42
|
* `pickSlot`), starting at its first allowed value.
|
|
43
43
|
*/
|
|
44
44
|
slots?: Record<string, string>;
|
|
45
|
+
/** Which prompt lines each turn gets, for a host that lets its page choose (`pageStatesTurn`). */
|
|
46
|
+
stated?: StatedTurn;
|
|
45
47
|
/** Told each time the visitor picks a slot, with every slot's value. */
|
|
46
48
|
onSlotsChange?: (slots: Record<string, string>) => void;
|
|
47
49
|
/** What the page wants the agent to know: any JSON. The latest value goes with every turn. */
|
package/client/chat.js
CHANGED
|
@@ -653,12 +653,13 @@ class Chat {
|
|
|
653
653
|
};
|
|
654
654
|
}
|
|
655
655
|
#withValues(request) {
|
|
656
|
-
const { iface, slots: fixed, context } = this.#options;
|
|
656
|
+
const { iface, slots: fixed, context, stated } = this.#options;
|
|
657
657
|
const slots = slotsOf(iface, fixed, this.#picked).values;
|
|
658
|
-
if (slots === undefined && context === undefined)
|
|
658
|
+
if (slots === undefined && context === undefined && stated === undefined)
|
|
659
659
|
return request;
|
|
660
660
|
return {
|
|
661
661
|
...request,
|
|
662
|
+
...(stated === undefined ? {} : { stated }),
|
|
662
663
|
input: {
|
|
663
664
|
...request.input,
|
|
664
665
|
...(slots === undefined ? {} : { slots }),
|
package/client/index.d.ts
CHANGED
|
@@ -33,5 +33,5 @@ export { hasLead, layoutResult, type ResultChart, type ResultFigure, type Result
|
|
|
33
33
|
export type { AnsweringGate, ToolDecisionAction, ToolGateResolution, } from './tool-resume.ts';
|
|
34
34
|
export { continueGatedToolInvocation, type PersonGateResolution } from './tool-resume.ts';
|
|
35
35
|
export { createTraceFeed, recordedFeed, type TraceFeed } from './trace-feed.ts';
|
|
36
|
-
export { CHAT_LINES, type ChatLine, type ChatLineSink, type ClientTurnEvent, type ConversationLine, type ConversationView, createHttpTransport, fetchConversation, fetchRewound, fetchStarted, type HostErrorBody, type HttpOptions, type HttpTransportOptions, hostError, isTheoremStreamError, type MalformedEvent, postJson, postNdjson, readNdjsonStream, type TheoremConversationRequest, type TheoremInvokeRequest, type TheoremReplay, type TheoremRewindRequest, type TheoremRewound, type TheoremStartRequest, type TheoremSteerRequest, type TheoremStopRequest, TheoremStreamError, type TheoremTransport, type TheoremTurnInput, type TheoremTurnRequest, type TurnEventSink, type UnsupportedEvent, type WireLines, } from './transport.ts';
|
|
36
|
+
export { CHAT_LINES, type ChatLine, type ChatLineSink, type ClientTurnEvent, type ConversationLine, type ConversationView, createHttpTransport, fetchConversation, fetchRewound, fetchStarted, type HostErrorBody, type HttpOptions, type HttpTransportOptions, hostError, isTheoremStreamError, type MalformedEvent, postJson, postNdjson, readNdjsonStream, type StatedTurn, type TheoremConversationRequest, type TheoremInvokeRequest, type TheoremReplay, type TheoremRewindRequest, type TheoremRewound, type TheoremStartRequest, type TheoremSteerRequest, type TheoremStopRequest, TheoremStreamError, type TheoremTransport, type TheoremTurnInput, type TheoremTurnRequest, type TurnEventSink, type UnsupportedEvent, type WireLines, } from './transport.ts';
|
|
37
37
|
export { buildInvokeRequest, buildTurnRequest, foldAssistantTurn, issuedOperationId, type RunScope, type WalkAway, } from './turn-client.ts';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StatedTurn } from '../transport.ts';
|
|
1
2
|
/** The page's result for a call: the tool's output schema checks it, then the model reads it. */
|
|
2
3
|
export type PageToolAnswer = {
|
|
3
4
|
output: unknown;
|
|
@@ -16,6 +17,8 @@ export type PageTools = Record<string, PageTool>;
|
|
|
16
17
|
export type LiveCallOptions = {
|
|
17
18
|
/** The value chosen for each of the profile's `inputs.slots`, read as the call starts. */
|
|
18
19
|
slots?: Record<string, string>;
|
|
20
|
+
/** Which prompt lines the call gets, for a host that lets its page choose (`pageStatesTurn`). */
|
|
21
|
+
stated?: StatedTurn;
|
|
19
22
|
/**
|
|
20
23
|
* What the page tells the agent: any JSON. It goes with the call's opening, and
|
|
21
24
|
* again whenever it changes, as background the agent reads without replying.
|
package/client/live-client.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { type SessionEventOf, type TraceRecord } from '@theoremjs/agents';
|
|
|
15
15
|
import type { PageTools } from './live/live-page-tool.ts';
|
|
16
16
|
import type { LiveConnectPhase, LiveSessionStatus } from './live/live-state.ts';
|
|
17
17
|
import type { LiveGateReply, LiveToolGatePrompt } from './live/live-tool.ts';
|
|
18
|
-
import { type ClientTurnEvent } from './transport.ts';
|
|
18
|
+
import { type ClientTurnEvent, type StatedTurn } from './transport.ts';
|
|
19
19
|
export type { LiveConnection, LiveSocket } from './live-messages.ts';
|
|
20
20
|
import type { LiveSocket } from './live-messages.ts';
|
|
21
21
|
export interface LiveClientOptions {
|
|
@@ -24,6 +24,8 @@ export interface LiveClientOptions {
|
|
|
24
24
|
openMessage?: Record<string, unknown>;
|
|
25
25
|
/** The value chosen for each of the profile's `inputs.slots`, fixed for the call. */
|
|
26
26
|
slots?: Record<string, string>;
|
|
27
|
+
/** Which prompt lines the call gets, for a host that lets its page choose (`pageStatesTurn`). */
|
|
28
|
+
stated?: StatedTurn;
|
|
27
29
|
/** What the page tells the agent as the call opens; `setContext` replaces it during the call. */
|
|
28
30
|
context?: unknown;
|
|
29
31
|
relayUrl?: string;
|
|
@@ -181,6 +183,9 @@ export declare class LiveSessionClient {
|
|
|
181
183
|
* handle; with none, the next call has them.
|
|
182
184
|
*/
|
|
183
185
|
setSlots(slots: Record<string, string> | undefined): void;
|
|
186
|
+
/** The call's prompt lines, as `setSlots` takes its slots. */
|
|
187
|
+
setStated(stated: StatedTurn | undefined): void;
|
|
188
|
+
private takeUpAgain;
|
|
184
189
|
/**
|
|
185
190
|
* Replace the page's context package. In a call it reaches the agent as
|
|
186
191
|
* background, with no reply; before one, it goes with the call's opening.
|
package/client/live-client.js
CHANGED
|
@@ -228,11 +228,12 @@ export class LiveSessionClient {
|
|
|
228
228
|
}
|
|
229
229
|
/** The first message: the call's values, and the handle and time away when it takes a dropped call up again. */
|
|
230
230
|
openMessage() {
|
|
231
|
-
const { slots, openMessage: host } = this.options;
|
|
231
|
+
const { slots, stated, openMessage: host } = this.options;
|
|
232
232
|
return {
|
|
233
233
|
type: 'open',
|
|
234
234
|
...(this.history.length ? { history: structuredClone(this.history) } : {}),
|
|
235
235
|
...(slots ? { slots } : {}),
|
|
236
|
+
...(stated ? { stated } : {}),
|
|
236
237
|
...(this.context === undefined ? {} : { context: this.context }),
|
|
237
238
|
...(this.providerState
|
|
238
239
|
? {
|
|
@@ -771,6 +772,16 @@ export class LiveSessionClient {
|
|
|
771
772
|
if (JSON.stringify(slots) === JSON.stringify(this.options.slots))
|
|
772
773
|
return;
|
|
773
774
|
this.options = { ...this.options, slots };
|
|
775
|
+
this.takeUpAgain();
|
|
776
|
+
}
|
|
777
|
+
/** The call's prompt lines, as `setSlots` takes its slots. */
|
|
778
|
+
setStated(stated) {
|
|
779
|
+
if (JSON.stringify(stated) === JSON.stringify(this.options.stated))
|
|
780
|
+
return;
|
|
781
|
+
this.options = { ...this.options, stated };
|
|
782
|
+
this.takeUpAgain();
|
|
783
|
+
}
|
|
784
|
+
takeUpAgain() {
|
|
774
785
|
if (this.providerState && !this.opening && this.ws?.readyState === WebSocket.OPEN) {
|
|
775
786
|
this.handleDrop();
|
|
776
787
|
}
|
|
@@ -7,7 +7,7 @@ import { type ProviderCheckpoint } from '@theoremjs/agents';
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { type GateDecision, type ToolGate, type TraceRecord, type TurnEvent, type TurnHistoryMessage, z } from '@theoremjs/agents';
|
|
10
|
-
import { type HostErrorBody } from './transport.ts';
|
|
10
|
+
import { type HostErrorBody, type StatedTurn } from './transport.ts';
|
|
11
11
|
import { type MalformedEvent, type UnsupportedEvent } from './wire-line.ts';
|
|
12
12
|
/**
|
|
13
13
|
* The live client's first message, before the relay opens the session: the
|
|
@@ -20,6 +20,8 @@ export type LiveOpenMessage = {
|
|
|
20
20
|
slots?: Record<string, string>;
|
|
21
21
|
/** What the page tells the agent as the call opens: the client's context package. */
|
|
22
22
|
context?: unknown;
|
|
23
|
+
/** Which prompt lines the call gets, for a host that lets its page choose (`pageStatesTurn`). */
|
|
24
|
+
stated?: StatedTurn;
|
|
23
25
|
/** A call that dropped, taken up again: the provider's handle, and how long the caller was away. */
|
|
24
26
|
resume?: {
|
|
25
27
|
providerState: ProviderCheckpoint;
|
package/client/live-messages.js
CHANGED
|
@@ -7,13 +7,14 @@ import { providerCheckpointSchema } from '@theoremjs/agents';
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { GATE_DECISIONS, TURN_EVENT_SCHEMAS, toolGateSchema, traceRecordSchema, turnHistoryMessageSchema, z, } from '@theoremjs/agents';
|
|
10
|
-
import { hostErrorBodySchema } from "./transport.js";
|
|
10
|
+
import { hostErrorBodySchema, statedTurnSchema, } from "./transport.js";
|
|
11
11
|
import { readWireLine, readWireValue, } from "./wire-line.js";
|
|
12
12
|
const liveOpenMessage = z.object({
|
|
13
13
|
type: z.literal('open'),
|
|
14
14
|
history: z.array(turnHistoryMessageSchema).optional(),
|
|
15
15
|
slots: z.record(z.string(), z.string()).optional(),
|
|
16
16
|
context: z.unknown().optional(),
|
|
17
|
+
stated: statedTurnSchema.optional(),
|
|
17
18
|
resume: z
|
|
18
19
|
.object({ providerState: providerCheckpointSchema, awayMs: z.number().nonnegative() })
|
|
19
20
|
.optional(),
|
package/client/transport.d.ts
CHANGED
|
@@ -49,6 +49,13 @@ export type TheoremReplay = {
|
|
|
49
49
|
};
|
|
50
50
|
/** For a host that reads the studio's client-held state. */
|
|
51
51
|
export declare const theoremReplaySchema: z.ZodType<TheoremReplay>;
|
|
52
|
+
/** Which prompt lines a turn gets: the path it says it came by, and its `identity.conditions`. */
|
|
53
|
+
export type StatedTurn = {
|
|
54
|
+
path?: string;
|
|
55
|
+
conditions?: Record<string, string>;
|
|
56
|
+
};
|
|
57
|
+
/** A stated turn off the wire. */
|
|
58
|
+
export declare const statedTurnSchema: z.ZodType<StatedTurn>;
|
|
52
59
|
/**
|
|
53
60
|
* One message. The host resolves the profile and owns the conversation: the
|
|
54
61
|
* client sends only the message and the ref of the conversation it continues.
|
|
@@ -63,6 +70,8 @@ export type TheoremTurnRequest = {
|
|
|
63
70
|
conversation?: string;
|
|
64
71
|
model?: string;
|
|
65
72
|
effort?: string;
|
|
73
|
+
/** For a host that lets its page choose (`pageStatesTurn`); any other host refuses it. */
|
|
74
|
+
stated?: StatedTurn;
|
|
66
75
|
/** Client-generated id so mid-turn steers can find this turn's inbox. */
|
|
67
76
|
turnId?: string;
|
|
68
77
|
/** invariant: Both issued IDs identify each paused action that this message abandons. */
|
package/client/transport.js
CHANGED
|
@@ -39,11 +39,19 @@ true;
|
|
|
39
39
|
export const theoremReplaySchema = theoremReplay;
|
|
40
40
|
/** An id the client made: trimmed, never blank. */
|
|
41
41
|
const clientId = z.string().trim().min(1);
|
|
42
|
+
const statedTurn = z.object({
|
|
43
|
+
path: z.string().optional(),
|
|
44
|
+
conditions: z.record(z.string(), z.string()).optional(),
|
|
45
|
+
});
|
|
46
|
+
true;
|
|
47
|
+
/** A stated turn off the wire. */
|
|
48
|
+
export const statedTurnSchema = statedTurn;
|
|
42
49
|
const theoremTurnRequest = z.object({
|
|
43
50
|
input: theoremTurnInput,
|
|
44
51
|
conversation: z.string().min(1).optional(),
|
|
45
52
|
model: z.string().optional(),
|
|
46
53
|
effort: z.string().optional(),
|
|
54
|
+
stated: statedTurn.optional(),
|
|
47
55
|
turnId: clientId.optional(),
|
|
48
56
|
abandon: z.array(gateReferenceSchema).min(1).optional(),
|
|
49
57
|
replay: theoremReplay.pick({ sessionPermissions: true }).optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theoremjs/web",
|
|
3
|
-
"version": "0.3.4-next.
|
|
3
|
+
"version": "0.3.4-next.693.g4ab1526",
|
|
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.
|
|
28
|
+
"@theoremjs/agents": "^0.3.4-next.693.g4ab1526"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { type ProviderCheckpoint, TheoremError, type TurnEvent, type TurnHistoryMessage, z } from '@theoremjs/agents';
|
|
13
13
|
import type { TranscriptBlock } from '@theoremjs/agents/interface';
|
|
14
|
+
import { type StatedTurn } from '../client/transport.ts';
|
|
14
15
|
import { type MemorySessionStoreOptions } from './session-store.ts';
|
|
15
16
|
/** A reply paused on gates: its events so far, and the history it was given. */
|
|
16
17
|
export type PausedReply = {
|
|
@@ -23,9 +24,10 @@ export type PausedReply = {
|
|
|
23
24
|
workedMs: number;
|
|
24
25
|
/** When the reply last paused (epoch ms). */
|
|
25
26
|
endedAt: number;
|
|
26
|
-
/** The model and
|
|
27
|
+
/** The model, effort, and stated turn the reply ran with, for the run that goes on after its gates. */
|
|
27
28
|
model?: string;
|
|
28
29
|
effort?: string;
|
|
30
|
+
stated?: StatedTurn;
|
|
29
31
|
};
|
|
30
32
|
/** One conversation as the host stores it. */
|
|
31
33
|
export type StoredConversation = {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { labelText, providerCheckpointSchema, TheoremError, turnEventSchema, turnHistoryMessageSchema, z, } from '@theoremjs/agents';
|
|
13
13
|
import { openSecret, sealSecret } from '@theoremjs/agents/kernel';
|
|
14
14
|
import { transcriptBlockSchema } from "../client/transcript-schema.js";
|
|
15
|
+
import { statedTurnSchema } from "../client/transport.js";
|
|
15
16
|
import { memoryStoreLimits } from "./session-store.js";
|
|
16
17
|
const pausedReply = z.object({
|
|
17
18
|
events: z.array(turnEventSchema),
|
|
@@ -21,6 +22,7 @@ const pausedReply = z.object({
|
|
|
21
22
|
endedAt: z.number(),
|
|
22
23
|
model: z.string().optional(),
|
|
23
24
|
effort: z.string().optional(),
|
|
25
|
+
stated: statedTurnSchema.optional(),
|
|
24
26
|
});
|
|
25
27
|
true;
|
|
26
28
|
const storedConversation = z.object({
|
package/server/conversation.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { type GateReference, type LabelOverrides, type Profile, type ProviderCheckpoint, TheoremError, type TurnEvent, type TurnHistoryMessage, type TurnInput } from '@theoremjs/agents';
|
|
18
18
|
import { type ClientTurnOptions } from '@theoremjs/agents/host';
|
|
19
19
|
import { type ComposerProfileInterface, streamThoughtsEnabled, type TranscriptBlock, type UserTurnDraft, type UserTurnHistoryMedia } from '@theoremjs/agents/interface';
|
|
20
|
-
import type { ConversationLine, ConversationView, TheoremTurnInput } from '../client/transport.ts';
|
|
20
|
+
import type { ConversationLine, ConversationView, StatedTurn, TheoremTurnInput } from '../client/transport.ts';
|
|
21
21
|
import { type StoredConversation, type TheoremConversationStore } from './conversation-store.ts';
|
|
22
22
|
/** One walked-away call: its id, and the run that settles it (`invokeTool` with the abandoned resume). */
|
|
23
23
|
export type WalkedAwayCall = {
|
|
@@ -119,6 +119,7 @@ export type ReplyTurn = {
|
|
|
119
119
|
providerState?: ProviderCheckpoint;
|
|
120
120
|
model?: string;
|
|
121
121
|
effort?: string;
|
|
122
|
+
stated?: StatedTurn;
|
|
122
123
|
};
|
|
123
124
|
/** What a reply's model run takes from the host. */
|
|
124
125
|
export type ReplyRunner = {
|
|
@@ -131,6 +132,7 @@ export type ReplyRunner = {
|
|
|
131
132
|
compact(history: TurnHistoryMessage[], tokens: number, model: string | undefined): Promise<TurnHistoryMessage[] | undefined>;
|
|
132
133
|
model?: string;
|
|
133
134
|
effort?: string;
|
|
135
|
+
stated?: StatedTurn;
|
|
134
136
|
signal: AbortSignal;
|
|
135
137
|
};
|
|
136
138
|
/** A line of a reply's stream: an event, or the ref of what the store wrote. */
|
package/server/conversation.js
CHANGED
|
@@ -192,6 +192,7 @@ function pausedOn(conversation, reply, runner) {
|
|
|
192
192
|
endedAt: Date.now(),
|
|
193
193
|
...(runner.model ? { model: runner.model } : {}),
|
|
194
194
|
...(runner.effort ? { effort: runner.effort } : {}),
|
|
195
|
+
...(runner.stated ? { stated: runner.stated } : {}),
|
|
195
196
|
};
|
|
196
197
|
return { ...conversation, ...metered(conversation, reply.events), paused };
|
|
197
198
|
}
|
|
@@ -234,6 +235,7 @@ async function* runReply(host, opened, reply, runner, input, withMessage) {
|
|
|
234
235
|
providerState: conversation.providerState,
|
|
235
236
|
model: runner.model,
|
|
236
237
|
effort: runner.effort,
|
|
238
|
+
...(runner.stated ? { stated: runner.stated } : {}),
|
|
237
239
|
});
|
|
238
240
|
for await (const event of events) {
|
|
239
241
|
reply.events.push(event);
|
|
@@ -485,7 +487,12 @@ export async function* answerReply(args) {
|
|
|
485
487
|
yield error;
|
|
486
488
|
return;
|
|
487
489
|
}
|
|
488
|
-
yield* runReply(host, { ...opened, conversation: waiting }, reply, {
|
|
490
|
+
yield* runReply(host, { ...opened, conversation: waiting }, reply, {
|
|
491
|
+
...runner,
|
|
492
|
+
model: runner.model ?? paused.model,
|
|
493
|
+
effort: runner.effort ?? paused.effort,
|
|
494
|
+
...(paused.stated ? { stated: paused.stated } : {}),
|
|
495
|
+
}, {
|
|
489
496
|
history: replyHistory(reply),
|
|
490
497
|
...(waiting.inputTokens === undefined ? {} : { inputTokens: waiting.inputTokens }),
|
|
491
498
|
...(waiting.historyTokens === undefined ? {} : { historyTokens: waiting.historyTokens }),
|
package/server/handler.d.ts
CHANGED
|
@@ -73,6 +73,11 @@ export type TheoremHandlerOptions = {
|
|
|
73
73
|
* answered, with the `host`, `onStage` and `credentials` it gives for that call's turn.
|
|
74
74
|
*/
|
|
75
75
|
prepare?: TurnWiring['prepare'];
|
|
76
|
+
/**
|
|
77
|
+
* Let the page say which prompt lines a turn gets (`stated`), for a page the host trusts as it
|
|
78
|
+
* trusts itself: the studio's. Unset refuses them, so a public page never chooses its own lines.
|
|
79
|
+
*/
|
|
80
|
+
pageStatesTurn?: boolean;
|
|
76
81
|
/**
|
|
77
82
|
* The server's own stage handler, on every call this handler runs, a host's included: after
|
|
78
83
|
* the application's, before steering. For a rule the server adds to whatever the application
|
package/server/handler.js
CHANGED
|
@@ -418,6 +418,7 @@ async function* chatTurn(ctx, request, session, turn, key, stop) {
|
|
|
418
418
|
host,
|
|
419
419
|
...(turn.model ? { model: turn.model } : {}),
|
|
420
420
|
...(turn.effort ? { effort: turn.effort } : {}),
|
|
421
|
+
...turn.stated,
|
|
421
422
|
}, {
|
|
422
423
|
credentials: sessionCredentials(ctx, session.id),
|
|
423
424
|
...(onStage ? { onStage } : {}),
|
|
@@ -454,6 +455,7 @@ function chatRunner(ctx, request, session, generation) {
|
|
|
454
455
|
},
|
|
455
456
|
...(generation.model ? { model: generation.model } : {}),
|
|
456
457
|
...(generation.effort ? { effort: generation.effort } : {}),
|
|
458
|
+
...(generation.stated ? { stated: generation.stated } : {}),
|
|
457
459
|
signal,
|
|
458
460
|
};
|
|
459
461
|
}
|
|
@@ -472,6 +474,9 @@ function conversationHost(ctx, session) {
|
|
|
472
474
|
/** The person's message: a new reply, run against the conversation the host keeps. */
|
|
473
475
|
async function turn(ctx, chat, request, session) {
|
|
474
476
|
const body = await readBody(request, theoremTurnRequestSchema, ctx.options.maxRequestBytes);
|
|
477
|
+
if (body.stated && !ctx.options.pageStatesTurn) {
|
|
478
|
+
throw new TheoremError('request', labelText('error.request'));
|
|
479
|
+
}
|
|
475
480
|
const message = postedMessage(chatInterface(chat), body.input, await ctx.options.context?.(request));
|
|
476
481
|
const runner = chatRunner(ctx, request, session, body);
|
|
477
482
|
const host = conversationHost(ctx, session);
|
|
@@ -481,7 +486,12 @@ async function turn(ctx, chat, request, session) {
|
|
|
481
486
|
// labels-exempt: internal diagnostic; the user reads error.request
|
|
482
487
|
if (body.abandon)
|
|
483
488
|
throw statelessRefusal('walk away from');
|
|
484
|
-
return eventStream(ctx, request, () => runner.run({
|
|
489
|
+
return eventStream(ctx, request, () => runner.run({
|
|
490
|
+
input: message.input,
|
|
491
|
+
model: body.model,
|
|
492
|
+
effort: body.effort,
|
|
493
|
+
...(body.stated ? { stated: body.stated } : {}),
|
|
494
|
+
}));
|
|
485
495
|
}
|
|
486
496
|
const opened = await openConversation(host, body.conversation, {
|
|
487
497
|
kind: 'message',
|
package/server/mod.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { theoremHostCallRequestSchema } from '../client/host-transport.ts';
|
|
9
9
|
export type { LiveClientMessage, LiveOpenMessage } from '../client/live-messages.ts';
|
|
10
|
-
export { theoremConversationRequestSchema, theoremInvokeRequestSchema, theoremReplaySchema, theoremRewindRequestSchema, theoremStartRequestSchema, theoremSteerRequestSchema, theoremStopRequestSchema, theoremTurnRequestSchema, } from '../client/transport.ts';
|
|
10
|
+
export { type StatedTurn, theoremConversationRequestSchema, theoremInvokeRequestSchema, theoremReplaySchema, theoremRewindRequestSchema, theoremStartRequestSchema, theoremSteerRequestSchema, theoremStopRequestSchema, theoremTurnRequestSchema, } from '../client/transport.ts';
|
|
11
11
|
export { answerReply, type ConversationHost, type ConversationUse, conversationView, type IssuedWalkAway, messageReply, type OpenedConversation, openConversation, type PostedMessage, postedMessage, type ReplyLine, type ReplyRunner, type ReplyTurn, readerConversationHost, rewindConversation, startConversation, statelessRefusal, type WalkedAwayCall, waitingCalls, } from './conversation.ts';
|
|
12
12
|
export { type ConversationSnapshot, type ConversationWriteResult, createMemoryConversationStore, createSealedConversationStore, emptyConversation, type PausedReply, type SealedConversationStoreOptions, type StoredConversation, storedConversationSchema, type TheoremConversationStore, unknownConversation, } from './conversation-store.ts';
|
|
13
13
|
export { createMemoryCredentialStore, type TheoremCredentialStore, type TheoremCredentials, } from './credential-store.ts';
|
|
@@ -8,11 +8,13 @@ export interface SessionBoundaryOptions {
|
|
|
8
8
|
sessionStore: TheoremSessionStore;
|
|
9
9
|
gateTtlMs?: number;
|
|
10
10
|
operationRetentionMs?: number;
|
|
11
|
+
/** As `TheoremHandlerOptions.pageStatesTurn`: refuses a live call's `stated` unless set. */
|
|
12
|
+
pageStatesTurn?: boolean;
|
|
11
13
|
pageToolBroker?: PageToolBroker;
|
|
12
14
|
/** How long the page has to answer a tool call it runs, in milliseconds. Unset, it waits. */
|
|
13
15
|
pageToolTimeoutMs?: number;
|
|
14
16
|
}
|
|
15
|
-
export type PreparedLiveOpen = Pick<SessionRequest, 'history' | 'slots' | 'context' | 'providerState' | 'awayMs'>;
|
|
17
|
+
export type PreparedLiveOpen = Pick<SessionRequest, 'history' | 'slots' | 'context' | 'providerState' | 'awayMs' | 'path' | 'conditions'>;
|
|
16
18
|
export interface TheoremSessionBoundary {
|
|
17
19
|
sessionId: string;
|
|
18
20
|
setCookie?: string;
|
|
@@ -31,6 +31,9 @@ export async function createTheoremSessionBoundary(options) {
|
|
|
31
31
|
toolExecution: sessionToolCoordinator(store, id),
|
|
32
32
|
async prepareLiveOpen(raw, server) {
|
|
33
33
|
const open = checkRequest(liveOpenMessageSchema, raw, 'live open');
|
|
34
|
+
if (open.stated && !options.pageStatesTurn) {
|
|
35
|
+
throw new TheoremError('request', labelText('error.request'));
|
|
36
|
+
}
|
|
34
37
|
const prepared = liveSessionOpen(open, server);
|
|
35
38
|
const state = await store.read(id);
|
|
36
39
|
const providerState = await requireOwnedCheckpoint(state.providerCheckpoints, prepared.providerState, state.operations);
|
package/server/turn-input.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export declare function kernelTurnInput(input: TheoremTurnInput, server?: unknow
|
|
|
7
7
|
* What a live call's first message asks of the session, as `runSession` takes
|
|
8
8
|
* it: the page's context is the client's, and `server` is the host's own.
|
|
9
9
|
*/
|
|
10
|
-
export declare function liveSessionOpen(open: LiveOpenMessage, server?: unknown): Pick<SessionRequest, 'slots' | 'context' | 'providerState' | 'awayMs' | 'history'>;
|
|
10
|
+
export declare function liveSessionOpen(open: LiveOpenMessage, server?: unknown): Pick<SessionRequest, 'slots' | 'context' | 'providerState' | 'awayMs' | 'history' | 'path' | 'conditions'>;
|
package/server/turn-input.js
CHANGED
|
@@ -22,6 +22,7 @@ export function liveSessionOpen(open, server) {
|
|
|
22
22
|
...(open.history ? { history: open.history } : {}),
|
|
23
23
|
...(open.slots ? { slots: open.slots } : {}),
|
|
24
24
|
...(context ? { context } : {}),
|
|
25
|
+
...open.stated,
|
|
25
26
|
...(open.resume
|
|
26
27
|
? { providerState: open.resume.providerState, awayMs: open.resume.awayMs }
|
|
27
28
|
: {}),
|