@theoremjs/web 0.3.4-next.717.gd16ffc6 → 0.3.4-next.721.g7ff150a
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 +1 -1
- package/package.json +2 -2
- package/server/handler.js +53 -3
package/README.md
CHANGED
|
@@ -456,7 +456,7 @@ To retain execution claims across connections, supply a durable coordinator as `
|
|
|
456
456
|
Browser dispatch uses a host-owned `PageToolExecution` service beside the execution coordinator.
|
|
457
457
|
`createTheoremHandler` supplies both services. Live relays put the session boundary's `pageExecution` and `toolExecution` on `SessionRequest`.
|
|
458
458
|
|
|
459
|
-
A tool with a `background` setting runs inline until you pass the `background` option to `createTheoremHandler`. The option takes `{ request, sessionId }` and returns a `ToolBackgroundService`, which receives each detached call and its late events. The session keeps the call as `background` until it settles, and a late settlement does not invalidate a checkpoint. Keep the service's values valid after the request ends. A live relay passes its service to `runSession`.
|
|
459
|
+
A tool with a `background` setting runs inline until you pass the `background` option to `createTheoremHandler`. The option takes `{ request, sessionId }` and returns a `ToolBackgroundService`, which receives each detached call and its late events. The session keeps the call as `background` until it settles, and a late settlement does not invalidate a checkpoint. A conversation read shows the call as it stands: running, then its result, or an error for a marked call. An open page reads it on its next turn or reload. When the session has dropped a settled operation, the block reads an error with the code `outcome_not_retained`. On a durable store, a worker that dies leaves the operation as `background`, so your sweep marks it `uncertain` with `worker_lost` through `compareAndSwap`. Keep the service's values valid after the request ends. A live relay passes its service to `runSession`.
|
|
460
460
|
Forward live page results to `answerPageResult`. Preserve checkpoint ownership through `beforeEvent` when using the Studio bridge.
|
|
461
461
|
The default `PageToolBroker` keeps active result waiters in process memory. Several workers need a shared broker or request affinity.
|
|
462
462
|
Use `pageToolBroker` to supply that broker. It carries results; the atomic session store retains execution authority.
|
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.721.g7ff150a",
|
|
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.721.g7ff150a"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/server/handler.js
CHANGED
|
@@ -42,7 +42,7 @@ import { answerGatedCall, resolveGateTtlMs, } from '@theoremjs/agents/kernel';
|
|
|
42
42
|
import { hostInterface, theoremHostCallRequestSchema, } from "../client/host-transport.js";
|
|
43
43
|
import { theoremConversationRequestSchema, theoremInvokeRequestSchema, theoremRewindRequestSchema, theoremSteerRequestSchema, theoremStopRequestSchema, theoremTurnRequestSchema, } from "../client/transport.js";
|
|
44
44
|
import { answerReply, conversationView, messageReply, openConversation, postedMessage, readerConversationHost, requiredOperationId, rewindConversation, statelessRefusal, waitingCalls, } from "./conversation.js";
|
|
45
|
-
import { unknownConversation } from "./conversation-store.js";
|
|
45
|
+
import { unknownConversation, } from "./conversation-store.js";
|
|
46
46
|
import { createMemoryCredentialStore, } from "./credential-store.js";
|
|
47
47
|
import { sessionStateStore, } from "./session-authority.js";
|
|
48
48
|
import { theoremSession } from "./session-identity.js";
|
|
@@ -519,7 +519,57 @@ async function conversation(ctx, request, session) {
|
|
|
519
519
|
if (!host)
|
|
520
520
|
throw statelessRefusal('read');
|
|
521
521
|
const opened = await openConversation(host, body.conversation, { kind: 'read' });
|
|
522
|
-
return jsonResponse(200,
|
|
522
|
+
return jsonResponse(200, await currentView(ctx, session, host, opened.conversation));
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* why: The transcript keeps a detached call as the turn left it. The session holds what became of
|
|
526
|
+
* it, so a call that has ended does not read as running.
|
|
527
|
+
*/
|
|
528
|
+
async function currentView(ctx, session, host, conversation) {
|
|
529
|
+
const view = conversationView(host, conversation);
|
|
530
|
+
if (!view.transcript.some(isDetachedBlock))
|
|
531
|
+
return view;
|
|
532
|
+
const { operations } = await ctx.sessions.read(session.id);
|
|
533
|
+
return {
|
|
534
|
+
...view,
|
|
535
|
+
transcript: view.transcript.map((block) => isDetachedBlock(block) ? lateBlock(block, operations, host, ctx.profile) : block),
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
function isDetachedBlock(block) {
|
|
539
|
+
return block.kind === 'tool' && block.tool.state?.phase === 'background';
|
|
540
|
+
}
|
|
541
|
+
/** A detached call's block as its operation stands. One the session no longer holds has no known end. */
|
|
542
|
+
function lateBlock(block, operations, host, profile) {
|
|
543
|
+
const { tool } = block;
|
|
544
|
+
const id = tool.operationId;
|
|
545
|
+
const operation = id && Object.hasOwn(operations, id) ? operations[id] : undefined;
|
|
546
|
+
if (operation?.status === 'running' || operation?.status === 'background')
|
|
547
|
+
return block;
|
|
548
|
+
if (operation?.status === 'settled') {
|
|
549
|
+
const settled = host.forReader(operation.outcome.event);
|
|
550
|
+
if (settled.type !== 'tool')
|
|
551
|
+
return block;
|
|
552
|
+
const { tool: state } = settled;
|
|
553
|
+
if (!('phase' in state) || (state.phase !== 'complete' && state.phase !== 'error'))
|
|
554
|
+
return block;
|
|
555
|
+
return { ...block, tool: { ...tool, state, endedAt: operation.settledAt } };
|
|
556
|
+
}
|
|
557
|
+
const message = labelText('tool.execution_uncertain', {}, profile.labels);
|
|
558
|
+
const failure = {
|
|
559
|
+
code: operation?.status === 'uncertain' ? operation.reason : 'outcome_not_retained',
|
|
560
|
+
kind: 'internal',
|
|
561
|
+
message,
|
|
562
|
+
};
|
|
563
|
+
const { name, callId } = tool;
|
|
564
|
+
const at = operation?.status === 'uncertain' ? operation.detectedAt : (tool.state?.at ?? Date.now());
|
|
565
|
+
return {
|
|
566
|
+
...block,
|
|
567
|
+
tool: {
|
|
568
|
+
...tool,
|
|
569
|
+
state: { name, callId, at, phase: 'error', failure, readBack: message },
|
|
570
|
+
endedAt: at,
|
|
571
|
+
},
|
|
572
|
+
};
|
|
523
573
|
}
|
|
524
574
|
/** The conversation cut after one block; a reply that waits on gates is walked away from first. */
|
|
525
575
|
async function rewind(ctx, request, session) {
|
|
@@ -542,7 +592,7 @@ async function rewind(ctx, request, session) {
|
|
|
542
592
|
throw new TheoremError('internal', labelText('error.internal'));
|
|
543
593
|
const rewound = {
|
|
544
594
|
conversation: next.ref,
|
|
545
|
-
view:
|
|
595
|
+
view: await currentView(ctx, session, host, next.conversation),
|
|
546
596
|
};
|
|
547
597
|
return jsonResponse(200, rewound);
|
|
548
598
|
}
|