@popcomputer/structured-chat 0.1.0 → 0.2.0
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/LICENSE +0 -1
- package/README.md +175 -108
- package/dist/adapters/openai-compatible-model.d.ts +4 -4
- package/dist/adapters/openai-compatible-model.js +33 -31
- package/dist/core/answer.d.ts +13 -13
- package/dist/core/answer.js +21 -12
- package/dist/core/chat.d.ts +12 -15
- package/dist/core/chat.js +36 -27
- package/dist/core/collect-stage.d.ts +28 -15
- package/dist/core/collect-stage.js +58 -37
- package/dist/core/command.d.ts +1 -1
- package/dist/core/command.js +1 -1
- package/dist/core/debug-protocol.d.ts +126 -0
- package/dist/core/debug-protocol.js +19 -0
- package/dist/core/debug.d.ts +103 -0
- package/dist/core/debug.js +276 -0
- package/dist/core/json-value.d.ts +1 -1
- package/dist/core/json-value.js +8 -1
- package/dist/core/model-guard.d.ts +2 -3
- package/dist/core/model-guard.js +4 -4
- package/dist/core/model.d.ts +15 -19
- package/dist/core/model.js +22 -10
- package/dist/core/protocol.d.ts +84 -79
- package/dist/core/protocol.js +18 -12
- package/dist/core/question.js +17 -15
- package/dist/core/repair.js +1 -1
- package/dist/core/session.d.ts +22 -28
- package/dist/core/session.js +16 -7
- package/dist/core/stage-name.d.ts +1 -1
- package/dist/core/stage-name.js +1 -1
- package/dist/core/stage.d.ts +4 -4
- package/dist/core/stage.js +11 -8
- package/dist/core/tool-set.js +6 -6
- package/dist/core/tool.d.ts +36 -39
- package/dist/core/tool.js +58 -31
- package/dist/core/view.d.ts +64 -39
- package/dist/core/view.js +12 -15
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/integrations/assistant-ui-debug.d.ts +25 -0
- package/dist/integrations/assistant-ui-debug.js +1162 -0
- package/dist/integrations/assistant-ui.d.ts +10 -3
- package/dist/integrations/assistant-ui.js +48 -18
- package/dist/testing/in-memory-session-store.js +1 -1
- package/dist/testing/scenario.js +6 -6
- package/examples/answer-modes.ts +60 -49
- package/examples/prompt-injection-policy.ts +20 -20
- package/examples/resource-search.ts +104 -0
- package/package.json +15 -3
- package/examples/agency-search.ts +0 -101
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { Data, Effect,
|
|
1
|
+
import { cast, Data, Effect, Result, Schema, Struct } from "effect";
|
|
2
2
|
import { StageNameSchema } from "./stage-name.js";
|
|
3
3
|
import { ChatModelUnavailable, Instruction, runToolStep, StructuredChatModel, } from "./model.js";
|
|
4
4
|
import { defineTool, InvalidToolCall, } from "./tool.js";
|
|
5
5
|
import { defineToolSet } from "./tool-set.js";
|
|
6
6
|
import { structuredDefinition, } from "./definition.js";
|
|
7
7
|
/** Safe reason that a collect-stage model proposal was rejected. */
|
|
8
|
-
export const InvalidCollectStageResponseReasonSchema = Schema.
|
|
8
|
+
export const InvalidCollectStageResponseReasonSchema = Schema.Literals([
|
|
9
|
+
"invalid_evidence",
|
|
10
|
+
"invalid_repair",
|
|
11
|
+
]);
|
|
9
12
|
/** A collect-stage proposal was not grounded in a user message. */
|
|
10
13
|
export class InvalidCollectStageResponse extends Schema.TaggedError()("InvalidCollectStageResponse", {
|
|
11
14
|
stage: StageNameSchema,
|
|
@@ -16,14 +19,17 @@ export class InvalidCollectStageResponse extends Schema.TaggedError()("InvalidCo
|
|
|
16
19
|
export class AnswerValidationRejected extends Data.TaggedError("AnswerValidationRejected") {
|
|
17
20
|
}
|
|
18
21
|
const collectStageRuntime = Symbol("@popcomputer/structured-chat/CollectStageRuntime");
|
|
22
|
+
const collectStageInspection = Symbol("@popcomputer/structured-chat/CollectStageInspection");
|
|
19
23
|
/** @internal Read the erased runtime from an authentic collect stage. */
|
|
20
24
|
export const readCollectStageRuntime = (stage) => stage[collectStageRuntime];
|
|
25
|
+
/** @internal Read trusted definition metadata from an authentic collect stage. */
|
|
26
|
+
export const readCollectStageInspection = (stage) => stage[collectStageInspection];
|
|
21
27
|
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
22
28
|
/** Define one deterministic schema-derived fact collection stage. */
|
|
23
29
|
export const defineCollectStage = (definition) => {
|
|
24
30
|
Schema.decodeSync(StageNameSchema)(definition.name);
|
|
25
|
-
const questionGuidanceSchema = Schema.
|
|
26
|
-
const questionEscapeSchema = Schema.
|
|
31
|
+
const questionGuidanceSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(2_000));
|
|
32
|
+
const questionEscapeSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100));
|
|
27
33
|
const questionPolicyBuilder = {};
|
|
28
34
|
if (definition.questions?.guidance !== undefined) {
|
|
29
35
|
questionPolicyBuilder.guidance = Schema.decodeSync(questionGuidanceSchema)(definition.questions.guidance);
|
|
@@ -34,7 +40,7 @@ export const defineCollectStage = (definition) => {
|
|
|
34
40
|
const questions = questionPolicyBuilder;
|
|
35
41
|
// SAFETY: definition.fields is the exact Fields mapping; Object.keys returns
|
|
36
42
|
// only its enumerable string keys.
|
|
37
|
-
const fieldNames =
|
|
43
|
+
const fieldNames = cast(Object.keys(definition.fields));
|
|
38
44
|
if (fieldNames.length === 0) {
|
|
39
45
|
throw new Error("Collect stages require at least one answer field");
|
|
40
46
|
}
|
|
@@ -53,8 +59,8 @@ export const defineCollectStage = (definition) => {
|
|
|
53
59
|
if (firstField === undefined) {
|
|
54
60
|
throw new Error("Collect stages require at least one answer field");
|
|
55
61
|
}
|
|
56
|
-
const fieldSchema = Schema.
|
|
57
|
-
const messageIndexSchema = Schema.Number.
|
|
62
|
+
const fieldSchema = Schema.Literals([firstField, ...remainingFields]);
|
|
63
|
+
const messageIndexSchema = Schema.Number.check(Schema.isInt(), Schema.isBetween({ minimum: 0, maximum: 1_000_000 }));
|
|
58
64
|
const getAnswer = (field) => {
|
|
59
65
|
const answer = definition.fields[field];
|
|
60
66
|
if (answer === undefined) {
|
|
@@ -91,10 +97,10 @@ export const defineCollectStage = (definition) => {
|
|
|
91
97
|
}
|
|
92
98
|
const answerSchemaEntries = fieldNames.map((field) => [field, getAnswer(field).schema]);
|
|
93
99
|
// SAFETY: every entry uses one exact Fields key and its corresponding schema.
|
|
94
|
-
const answerSchemas =
|
|
100
|
+
const answerSchemas = cast(Object.fromEntries(answerSchemaEntries));
|
|
95
101
|
const rawAnswersSchema = Schema.Struct(answerSchemas);
|
|
96
|
-
const evidenceQuoteSchema = Schema.
|
|
97
|
-
const questionTextSchema = Schema.
|
|
102
|
+
const evidenceQuoteSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(2_000));
|
|
103
|
+
const questionTextSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(500));
|
|
98
104
|
const acceptedEvidenceSchema = Schema.Struct({
|
|
99
105
|
messageIndex: messageIndexSchema,
|
|
100
106
|
quote: evidenceQuoteSchema,
|
|
@@ -126,10 +132,10 @@ export const defineCollectStage = (definition) => {
|
|
|
126
132
|
}
|
|
127
133
|
const rawRepairSchema = remainingRepairSchemas.length === 0
|
|
128
134
|
? firstRepairSchema
|
|
129
|
-
: Schema.Union(firstRepairSchema, ...remainingRepairSchemas);
|
|
135
|
+
: Schema.Union([firstRepairSchema, ...remainingRepairSchemas]);
|
|
130
136
|
// SAFETY: every dynamically generated member uses only AnyNoContext field
|
|
131
137
|
// schemas and exact stage, field, and transition literals.
|
|
132
|
-
const repairSchema = rawRepairSchema;
|
|
138
|
+
const repairSchema = cast(rawRepairSchema);
|
|
133
139
|
const acceptedFields = Object.fromEntries(fieldNames.map((field) => [
|
|
134
140
|
field,
|
|
135
141
|
Schema.Struct({
|
|
@@ -145,8 +151,8 @@ export const defineCollectStage = (definition) => {
|
|
|
145
151
|
}),
|
|
146
152
|
]));
|
|
147
153
|
const rawStateSchema = Schema.Struct({
|
|
148
|
-
accepted: Schema.
|
|
149
|
-
asked: Schema.
|
|
154
|
+
accepted: Schema.Struct(acceptedFields).mapFields(Struct.map(Schema.optional)),
|
|
155
|
+
asked: Schema.Struct(askedFields).mapFields(Struct.map(Schema.optional)),
|
|
150
156
|
});
|
|
151
157
|
const isValidState = (state) => {
|
|
152
158
|
return fieldNames.every((field) => {
|
|
@@ -156,49 +162,61 @@ export const defineCollectStage = (definition) => {
|
|
|
156
162
|
hasOwn(state.asked, field));
|
|
157
163
|
});
|
|
158
164
|
};
|
|
159
|
-
const refinedStateSchema = rawStateSchema.
|
|
165
|
+
const refinedStateSchema = rawStateSchema.check(Schema.makeFilter(isValidState, {
|
|
160
166
|
description: "semantically valid collect-stage state",
|
|
161
167
|
}));
|
|
162
168
|
// SAFETY: rawAnswersSchema is created from every field's exact schema.
|
|
163
|
-
const answersSchema =
|
|
169
|
+
const answersSchema = cast(rawAnswersSchema);
|
|
164
170
|
// SAFETY: partial preserves the mapped accepted-answer types, while asked is
|
|
165
171
|
// a record whose keys are restricted to the exact field literal union.
|
|
166
|
-
const stateSchema =
|
|
167
|
-
const initialState = Schema.
|
|
172
|
+
const stateSchema = cast(refinedStateSchema);
|
|
173
|
+
const initialState = Schema.decodeSync(Schema.toType(stateSchema))({
|
|
168
174
|
accepted: {},
|
|
169
175
|
asked: {},
|
|
170
176
|
});
|
|
177
|
+
const inspectionFields = fieldNames.map((field) => {
|
|
178
|
+
const answer = getAnswer(field);
|
|
179
|
+
return {
|
|
180
|
+
field,
|
|
181
|
+
mode: answer.mode,
|
|
182
|
+
description: answer.description,
|
|
183
|
+
question: answer.question,
|
|
184
|
+
encodeValue: (value) => Schema.encodeUnknownEffect(answer.schema)(value, {
|
|
185
|
+
onExcessProperty: "error",
|
|
186
|
+
}),
|
|
187
|
+
};
|
|
188
|
+
});
|
|
171
189
|
// SAFETY: when guards are omitted, Guards uses its readonly [] default; an
|
|
172
190
|
// explicitly supplied tuple is returned unchanged.
|
|
173
|
-
const guards = definition.guards ??
|
|
191
|
+
const guards = definition.guards ?? cast([]);
|
|
174
192
|
// SAFETY: every entry is built from one registered AnyNoContext answer
|
|
175
193
|
// schema and adds only the model-wire null representation for absence.
|
|
176
194
|
const proposalAnswerSchemaEntries = fieldNames.map((field) => {
|
|
177
195
|
const answer = getAnswer(field);
|
|
178
196
|
return [
|
|
179
197
|
field,
|
|
180
|
-
Schema.NullOr(answer.schema).
|
|
198
|
+
Schema.NullOr(answer.schema).annotate({
|
|
181
199
|
description: `${answer.mode}: ${answer.description}`,
|
|
182
200
|
}),
|
|
183
201
|
];
|
|
184
202
|
});
|
|
185
203
|
// SAFETY: each entry contains one registered field and its no-context schema.
|
|
186
|
-
const proposalAnswerSchemas =
|
|
204
|
+
const proposalAnswerSchemas = cast(Object.fromEntries(proposalAnswerSchemaEntries));
|
|
187
205
|
const rawProposalSchema = Schema.Struct({
|
|
188
206
|
answers: Schema.Struct(proposalAnswerSchemas),
|
|
189
207
|
evidence: Schema.Array(Schema.Struct({
|
|
190
208
|
field: fieldSchema,
|
|
191
209
|
quote: evidenceQuoteSchema,
|
|
192
|
-
})).
|
|
210
|
+
})).check(Schema.isMaxLength(fieldNames.length)),
|
|
193
211
|
nextQuestion: Schema.NullOr(Schema.Struct({
|
|
194
212
|
field: fieldSchema,
|
|
195
213
|
text: questionTextSchema,
|
|
196
|
-
options: Schema.Array(Schema.
|
|
214
|
+
options: Schema.Array(Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100))).check(Schema.isMaxLength(20)),
|
|
197
215
|
})),
|
|
198
216
|
});
|
|
199
217
|
// SAFETY: every answer field schema is constrained to AnyNoContext; the
|
|
200
218
|
// generic mapped Struct cannot prove that fact after Object.fromEntries.
|
|
201
|
-
const ProposalSchema = rawProposalSchema;
|
|
219
|
+
const ProposalSchema = cast(rawProposalSchema);
|
|
202
220
|
const submitAnswers = defineTool({
|
|
203
221
|
name: "submit_answers",
|
|
204
222
|
description: "Submit grounded answers from the conversation and optionally phrase the next adaptive question.",
|
|
@@ -314,11 +332,11 @@ export const defineCollectStage = (definition) => {
|
|
|
314
332
|
// A selected label is later submitted as this answer's wire value,
|
|
315
333
|
// so model-authored labels that cannot decode would dead-end the
|
|
316
334
|
// user; fall back to the application-authored options instead.
|
|
317
|
-
const decodeLabel = Schema.
|
|
335
|
+
const decodeLabel = Schema.decodeUnknownResult(getAnswer(pending.field).schema);
|
|
318
336
|
const validOptions = supplied.length < question.minimumOptions ||
|
|
319
337
|
supplied.length > question.maximumOptions ||
|
|
320
338
|
new Set(normalized).size !== normalized.length ||
|
|
321
|
-
supplied.some(({ label }) =>
|
|
339
|
+
supplied.some(({ label }) => Result.isFailure(decodeLabel(label)))
|
|
322
340
|
? undefined
|
|
323
341
|
: supplied;
|
|
324
342
|
const selectedOptions = validOptions ??
|
|
@@ -339,8 +357,8 @@ export const defineCollectStage = (definition) => {
|
|
|
339
357
|
options,
|
|
340
358
|
};
|
|
341
359
|
return questions.escape === undefined
|
|
342
|
-
?
|
|
343
|
-
:
|
|
360
|
+
? cast(prompt)
|
|
361
|
+
: cast({ ...prompt, escape: { label: questions.escape } });
|
|
344
362
|
};
|
|
345
363
|
const askPendingQuestion = (state, messages, adaptive) => {
|
|
346
364
|
const pending = nextQuestion(state);
|
|
@@ -385,8 +403,8 @@ export const defineCollectStage = (definition) => {
|
|
|
385
403
|
options: question._tag === "ChoiceQuestion" ? question.options : [],
|
|
386
404
|
};
|
|
387
405
|
return questions.escape === undefined
|
|
388
|
-
?
|
|
389
|
-
:
|
|
406
|
+
? cast(prompt)
|
|
407
|
+
: cast({ ...prompt, escape: { label: questions.escape } });
|
|
390
408
|
};
|
|
391
409
|
const validateAnswer = (field, value) => {
|
|
392
410
|
const answer = getAnswer(field);
|
|
@@ -395,7 +413,7 @@ export const defineCollectStage = (definition) => {
|
|
|
395
413
|
}
|
|
396
414
|
// SAFETY: field selects the same answer definition whose schema parsed
|
|
397
415
|
// value before validation, preserving that field's validator input.
|
|
398
|
-
const validation =
|
|
416
|
+
const validation = cast(answer.validate);
|
|
399
417
|
return validation(value).pipe(Effect.mapError((error) => new AnswerValidationRejected({
|
|
400
418
|
stage: definition.name,
|
|
401
419
|
field,
|
|
@@ -413,7 +431,7 @@ export const defineCollectStage = (definition) => {
|
|
|
413
431
|
for (const repair of repairs) {
|
|
414
432
|
// SAFETY: the field lookup below rejects names outside Fields before
|
|
415
433
|
// any field-indexed operation runs.
|
|
416
|
-
const field =
|
|
434
|
+
const field = cast(repair.field);
|
|
417
435
|
const answer = definition.fields[field];
|
|
418
436
|
if (answer === undefined ||
|
|
419
437
|
seen.has(field) ||
|
|
@@ -547,7 +565,7 @@ export const defineCollectStage = (definition) => {
|
|
|
547
565
|
};
|
|
548
566
|
// SAFETY: accepted keys come only from fieldNames and every value was
|
|
549
567
|
// decoded by that field's schema before insertion.
|
|
550
|
-
const merged =
|
|
568
|
+
const merged = cast(runtimeMerged);
|
|
551
569
|
if (isComplete(merged)) {
|
|
552
570
|
return {
|
|
553
571
|
state: merged,
|
|
@@ -577,7 +595,7 @@ export const defineCollectStage = (definition) => {
|
|
|
577
595
|
// at the first failure. This keeps application Effects and the selected
|
|
578
596
|
// retry question deterministic. Each validator came from the same
|
|
579
597
|
// concrete Fields mapping used by the public conditional unions.
|
|
580
|
-
return execution;
|
|
598
|
+
return cast(execution);
|
|
581
599
|
};
|
|
582
600
|
const run = ({ state, messages, }) => {
|
|
583
601
|
if (!isValidState(state) || !isGroundedInMessages(state, messages)) {
|
|
@@ -594,12 +612,12 @@ export const defineCollectStage = (definition) => {
|
|
|
594
612
|
error.reason === "invalid_response"), (error) => Effect.logWarning("Falling back to the trusted pending question").pipe(Effect.annotateLogs({
|
|
595
613
|
stage: definition.name,
|
|
596
614
|
errorTag: error._tag,
|
|
597
|
-
}), Effect.as(askPendingQuestion(state, messages, null)))));
|
|
615
|
+
}), Effect.as(askPendingQuestion(state, messages, null))), (error) => Effect.fail(error)));
|
|
598
616
|
};
|
|
599
617
|
// SAFETY: The chat runtime calls these erased operations only after the
|
|
600
618
|
// generated state schema has parsed this exact collect-stage state. The
|
|
601
619
|
// public lower-level run method already requires CollectStageState<Fields>.
|
|
602
|
-
const assumeParsedState = (state) =>
|
|
620
|
+
const assumeParsedState = (state) => cast(state);
|
|
603
621
|
return structuredDefinition("collect_stage")({
|
|
604
622
|
_tag: "CollectStage",
|
|
605
623
|
name: definition.name,
|
|
@@ -609,7 +627,7 @@ export const defineCollectStage = (definition) => {
|
|
|
609
627
|
stateSchema,
|
|
610
628
|
initialState,
|
|
611
629
|
guards,
|
|
612
|
-
parseState: (input) => Schema.
|
|
630
|
+
parseState: (input) => Schema.decodeUnknownEffect(stateSchema)(input, {
|
|
613
631
|
onExcessProperty: "error",
|
|
614
632
|
}),
|
|
615
633
|
isComplete,
|
|
@@ -627,6 +645,9 @@ export const defineCollectStage = (definition) => {
|
|
|
627
645
|
},
|
|
628
646
|
}),
|
|
629
647
|
run,
|
|
648
|
+
[collectStageInspection]: {
|
|
649
|
+
fields: inspectionFields,
|
|
650
|
+
},
|
|
630
651
|
[collectStageRuntime]: {
|
|
631
652
|
initialState,
|
|
632
653
|
stateSchema,
|
package/dist/core/command.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
/** Opaque deterministic identity supplied to one command execution. */
|
|
3
|
-
export declare const CommandIdSchema: Schema.brand<Schema.
|
|
3
|
+
export declare const CommandIdSchema: Schema.brand<Schema.String, "CommandId">;
|
|
4
4
|
/** Opaque deterministic identity supplied to one command execution. */
|
|
5
5
|
export type CommandId = Schema.Schema.Type<typeof CommandIdSchema>;
|
|
6
6
|
/** Inputs whose exact tuple identity defines one command attempt. */
|
package/dist/core/command.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
/** Opaque deterministic identity supplied to one command execution. */
|
|
3
|
-
export const CommandIdSchema = Schema.String.pipe(Schema.
|
|
3
|
+
export const CommandIdSchema = Schema.String.pipe(Schema.check(Schema.isPattern(/^cmd_[0-9a-f]{64}$/)), Schema.brand("CommandId"));
|
|
4
4
|
const toHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
5
5
|
/** Derive the stable idempotency key for one persisted command turn. */
|
|
6
6
|
export const deriveCommandId = (input) => Effect.promise(async () => {
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { ChatDefinition, ChatReply, ChatStageTuple } from "./chat.js";
|
|
3
|
+
import { type InspectChatStateOptions, type InvalidChatDebugProjection } from "./debug.js";
|
|
4
|
+
import { presentChatReply, type InvalidChatPresentation, type PresentChatReplyOptions } from "./protocol.js";
|
|
5
|
+
type BrowserPresentableTurn = Parameters<typeof presentChatReply>[0]["turn"];
|
|
6
|
+
type DebugChatTurn<Name extends string, Version extends number, Stages extends ChatStageTuple> = ChatReply<Name, Version, Stages>["turn"] & BrowserPresentableTurn;
|
|
7
|
+
type DebugChatReply<Name extends string, Version extends number, Stages extends ChatStageTuple> = Omit<ChatReply<Name, Version, Stages>, "turn"> & {
|
|
8
|
+
readonly sessionId: string;
|
|
9
|
+
readonly turn: DebugChatTurn<Name, Version, Stages>;
|
|
10
|
+
};
|
|
11
|
+
/** Explicit opt-in browser response carrying one debug state projection. */
|
|
12
|
+
export declare const StructuredChatDebugTurnResponseSchema: Schema.Struct<{
|
|
13
|
+
readonly debug: Schema.Struct<{
|
|
14
|
+
readonly schemaVersion: Schema.Literal<1>;
|
|
15
|
+
readonly chat: Schema.Struct<{
|
|
16
|
+
readonly name: Schema.Trimmed;
|
|
17
|
+
readonly version: Schema.Number;
|
|
18
|
+
}>;
|
|
19
|
+
readonly status: Schema.Literals<readonly ["active", "complete"]>;
|
|
20
|
+
readonly currentStage: Schema.Struct<{
|
|
21
|
+
readonly index: Schema.Natural;
|
|
22
|
+
readonly name: Schema.Trimmed;
|
|
23
|
+
readonly kind: Schema.Literals<readonly ["collect", "tool", "command"]>;
|
|
24
|
+
}>;
|
|
25
|
+
readonly stages: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
26
|
+
readonly satisfiedFields: Schema.Natural;
|
|
27
|
+
readonly totalFields: Schema.Natural;
|
|
28
|
+
readonly fields: Schema.$Array<Schema.Struct<{
|
|
29
|
+
readonly field: Schema.String;
|
|
30
|
+
readonly mode: Schema.Literals<readonly ["semantic", "explicit", "confirmed"]>;
|
|
31
|
+
readonly description: Schema.String;
|
|
32
|
+
readonly question: Schema.Union<readonly [Schema.Struct<{
|
|
33
|
+
readonly _tag: Schema.Literal<"FixedQuestion">;
|
|
34
|
+
readonly text: Schema.String;
|
|
35
|
+
}>, Schema.Struct<{
|
|
36
|
+
readonly _tag: Schema.Literal<"AdaptiveQuestion">;
|
|
37
|
+
readonly goal: Schema.String;
|
|
38
|
+
readonly fallback: Schema.String;
|
|
39
|
+
}>, Schema.Struct<{
|
|
40
|
+
readonly _tag: Schema.Literal<"AdaptiveChoiceQuestion">;
|
|
41
|
+
readonly prompt: Schema.String;
|
|
42
|
+
readonly minimumOptions: Schema.Natural;
|
|
43
|
+
readonly maximumOptions: Schema.Natural;
|
|
44
|
+
readonly fallbackOptions: Schema.$Array<Schema.String>;
|
|
45
|
+
}>, Schema.Struct<{
|
|
46
|
+
readonly _tag: Schema.Literal<"ChoiceQuestion">;
|
|
47
|
+
readonly text: Schema.String;
|
|
48
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
49
|
+
readonly label: Schema.String;
|
|
50
|
+
}>>;
|
|
51
|
+
}>]>;
|
|
52
|
+
readonly state: Schema.Union<readonly [Schema.Struct<{
|
|
53
|
+
readonly _tag: Schema.Literal<"Missing">;
|
|
54
|
+
}>, Schema.Struct<{
|
|
55
|
+
readonly _tag: Schema.Literal<"Asked">;
|
|
56
|
+
readonly issuedQuestion: Schema.Struct<{
|
|
57
|
+
readonly messageIndex: Schema.Natural;
|
|
58
|
+
readonly text: Schema.String;
|
|
59
|
+
}>;
|
|
60
|
+
}>, Schema.Struct<{
|
|
61
|
+
readonly _tag: Schema.Literal<"Accepted">;
|
|
62
|
+
readonly value: Schema.Codec<import("./json-value.js").JsonValue, import("./json-value.js").JsonValue, never, never>;
|
|
63
|
+
readonly evidence: Schema.NullOr<Schema.Struct<{
|
|
64
|
+
readonly messageIndex: Schema.Natural;
|
|
65
|
+
readonly quote: Schema.String;
|
|
66
|
+
}>>;
|
|
67
|
+
readonly issuedQuestion: Schema.NullOr<Schema.Struct<{
|
|
68
|
+
readonly messageIndex: Schema.Natural;
|
|
69
|
+
readonly text: Schema.String;
|
|
70
|
+
}>>;
|
|
71
|
+
}>]>;
|
|
72
|
+
}>>;
|
|
73
|
+
readonly index: Schema.Natural;
|
|
74
|
+
readonly name: Schema.Trimmed;
|
|
75
|
+
readonly status: Schema.Literals<readonly ["complete", "current", "upcoming"]>;
|
|
76
|
+
readonly repairPending: Schema.Boolean;
|
|
77
|
+
readonly _tag: Schema.Literal<"CollectStage">;
|
|
78
|
+
}>, Schema.Struct<{
|
|
79
|
+
readonly tools: Schema.$Array<Schema.Trimmed>;
|
|
80
|
+
readonly afterExecution: Schema.Literals<readonly ["stay", "complete"]>;
|
|
81
|
+
readonly index: Schema.Natural;
|
|
82
|
+
readonly name: Schema.Trimmed;
|
|
83
|
+
readonly status: Schema.Literals<readonly ["complete", "current", "upcoming"]>;
|
|
84
|
+
readonly repairPending: Schema.Boolean;
|
|
85
|
+
readonly _tag: Schema.Literal<"ToolStage">;
|
|
86
|
+
}>, Schema.Struct<{
|
|
87
|
+
readonly command: Schema.Trimmed;
|
|
88
|
+
readonly index: Schema.Natural;
|
|
89
|
+
readonly name: Schema.Trimmed;
|
|
90
|
+
readonly status: Schema.Literals<readonly ["complete", "current", "upcoming"]>;
|
|
91
|
+
readonly repairPending: Schema.Boolean;
|
|
92
|
+
readonly _tag: Schema.Literal<"CommandStage">;
|
|
93
|
+
}>]>>;
|
|
94
|
+
}>;
|
|
95
|
+
readonly schemaVersion: Schema.Literal<1>;
|
|
96
|
+
readonly session: Schema.optional<Schema.Struct<{
|
|
97
|
+
readonly id: Schema.Trimmed;
|
|
98
|
+
readonly revision: Schema.Trimmed;
|
|
99
|
+
}>>;
|
|
100
|
+
readonly message: Schema.Struct<{
|
|
101
|
+
readonly role: Schema.Literal<"assistant">;
|
|
102
|
+
readonly content: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
103
|
+
readonly type: Schema.Literal<"text">;
|
|
104
|
+
readonly text: Schema.Trimmed;
|
|
105
|
+
}>, Schema.Struct<{
|
|
106
|
+
readonly type: Schema.Literal<"data">;
|
|
107
|
+
readonly name: Schema.Trimmed;
|
|
108
|
+
readonly data: Schema.Unknown;
|
|
109
|
+
}>]>>;
|
|
110
|
+
}>;
|
|
111
|
+
}>;
|
|
112
|
+
/** Explicit opt-in browser response carrying one debug state projection. */
|
|
113
|
+
export type StructuredChatDebugTurnResponse = Schema.Schema.Type<typeof StructuredChatDebugTurnResponseSchema>;
|
|
114
|
+
/** Presentation and state-inspection policies for one debug chat reply. */
|
|
115
|
+
export interface PresentChatDebugReplyOptions<Name extends string, Version extends number, Stages extends ChatStageTuple> {
|
|
116
|
+
readonly presentation?: PresentChatReplyOptions<DebugChatTurn<Name, Version, Stages>>;
|
|
117
|
+
readonly inspection?: InspectChatStateOptions;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Project one persisted reply into the explicit debug browser protocol.
|
|
121
|
+
*
|
|
122
|
+
* Applications must select this presenter deliberately and should authorize
|
|
123
|
+
* its endpoint independently from whether a debug panel is visually mounted.
|
|
124
|
+
*/
|
|
125
|
+
export declare const presentChatDebugReply: <const Name extends string, const Version extends number, const Stages extends ChatStageTuple>(chat: ChatDefinition<Name, Version, Stages>, reply: DebugChatReply<Name, Version, Stages>, options?: PresentChatDebugReplyOptions<Name, Version, Stages>) => Effect.Effect<StructuredChatDebugTurnResponse, InvalidChatPresentation | InvalidChatDebugProjection>;
|
|
126
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { inspectChatState, StructuredChatDebugSnapshotSchema, } from "./debug.js";
|
|
3
|
+
import { presentChatReply, StructuredChatTurnResponseSchema, } from "./protocol.js";
|
|
4
|
+
/** Explicit opt-in browser response carrying one debug state projection. */
|
|
5
|
+
export const StructuredChatDebugTurnResponseSchema = Schema.Struct({
|
|
6
|
+
...StructuredChatTurnResponseSchema.fields,
|
|
7
|
+
debug: StructuredChatDebugSnapshotSchema,
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* Project one persisted reply into the explicit debug browser protocol.
|
|
11
|
+
*
|
|
12
|
+
* Applications must select this presenter deliberately and should authorize
|
|
13
|
+
* its endpoint independently from whether a debug panel is visually mounted.
|
|
14
|
+
*/
|
|
15
|
+
export const presentChatDebugReply = (chat, reply, options = {}) => Effect.gen(function* () {
|
|
16
|
+
const response = yield* presentChatReply(reply, options.presentation);
|
|
17
|
+
const debug = yield* inspectChatState(chat, reply.turn.state, options.inspection);
|
|
18
|
+
return { ...response, debug };
|
|
19
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { type ChatDefinition, type ChatStageTuple, type ChatState } from "./chat.js";
|
|
3
|
+
/** Runtime schema for one JSON-safe structured-chat debug snapshot. */
|
|
4
|
+
export declare const StructuredChatDebugSnapshotSchema: Schema.Struct<{
|
|
5
|
+
readonly schemaVersion: Schema.Literal<1>;
|
|
6
|
+
readonly chat: Schema.Struct<{
|
|
7
|
+
readonly name: Schema.Trimmed;
|
|
8
|
+
readonly version: Schema.Number;
|
|
9
|
+
}>;
|
|
10
|
+
readonly status: Schema.Literals<readonly ["active", "complete"]>;
|
|
11
|
+
readonly currentStage: Schema.Struct<{
|
|
12
|
+
readonly index: Schema.Natural;
|
|
13
|
+
readonly name: Schema.Trimmed;
|
|
14
|
+
readonly kind: Schema.Literals<readonly ["collect", "tool", "command"]>;
|
|
15
|
+
}>;
|
|
16
|
+
readonly stages: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
17
|
+
readonly satisfiedFields: Schema.Natural;
|
|
18
|
+
readonly totalFields: Schema.Natural;
|
|
19
|
+
readonly fields: Schema.$Array<Schema.Struct<{
|
|
20
|
+
readonly field: Schema.String;
|
|
21
|
+
readonly mode: Schema.Literals<readonly ["semantic", "explicit", "confirmed"]>;
|
|
22
|
+
readonly description: Schema.String;
|
|
23
|
+
readonly question: Schema.Union<readonly [Schema.Struct<{
|
|
24
|
+
readonly _tag: Schema.Literal<"FixedQuestion">;
|
|
25
|
+
readonly text: Schema.String;
|
|
26
|
+
}>, Schema.Struct<{
|
|
27
|
+
readonly _tag: Schema.Literal<"AdaptiveQuestion">;
|
|
28
|
+
readonly goal: Schema.String;
|
|
29
|
+
readonly fallback: Schema.String;
|
|
30
|
+
}>, Schema.Struct<{
|
|
31
|
+
readonly _tag: Schema.Literal<"AdaptiveChoiceQuestion">;
|
|
32
|
+
readonly prompt: Schema.String;
|
|
33
|
+
readonly minimumOptions: Schema.Natural;
|
|
34
|
+
readonly maximumOptions: Schema.Natural;
|
|
35
|
+
readonly fallbackOptions: Schema.$Array<Schema.String>;
|
|
36
|
+
}>, Schema.Struct<{
|
|
37
|
+
readonly _tag: Schema.Literal<"ChoiceQuestion">;
|
|
38
|
+
readonly text: Schema.String;
|
|
39
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
40
|
+
readonly label: Schema.String;
|
|
41
|
+
}>>;
|
|
42
|
+
}>]>;
|
|
43
|
+
readonly state: Schema.Union<readonly [Schema.Struct<{
|
|
44
|
+
readonly _tag: Schema.Literal<"Missing">;
|
|
45
|
+
}>, Schema.Struct<{
|
|
46
|
+
readonly _tag: Schema.Literal<"Asked">;
|
|
47
|
+
readonly issuedQuestion: Schema.Struct<{
|
|
48
|
+
readonly messageIndex: Schema.Natural;
|
|
49
|
+
readonly text: Schema.String;
|
|
50
|
+
}>;
|
|
51
|
+
}>, Schema.Struct<{
|
|
52
|
+
readonly _tag: Schema.Literal<"Accepted">;
|
|
53
|
+
readonly value: Schema.Codec<import("./json-value.js").JsonValue, import("./json-value.js").JsonValue, never, never>;
|
|
54
|
+
readonly evidence: Schema.NullOr<Schema.Struct<{
|
|
55
|
+
readonly messageIndex: Schema.Natural;
|
|
56
|
+
readonly quote: Schema.String;
|
|
57
|
+
}>>;
|
|
58
|
+
readonly issuedQuestion: Schema.NullOr<Schema.Struct<{
|
|
59
|
+
readonly messageIndex: Schema.Natural;
|
|
60
|
+
readonly text: Schema.String;
|
|
61
|
+
}>>;
|
|
62
|
+
}>]>;
|
|
63
|
+
}>>;
|
|
64
|
+
readonly index: Schema.Natural;
|
|
65
|
+
readonly name: Schema.Trimmed;
|
|
66
|
+
readonly status: Schema.Literals<readonly ["complete", "current", "upcoming"]>;
|
|
67
|
+
readonly repairPending: Schema.Boolean;
|
|
68
|
+
readonly _tag: Schema.Literal<"CollectStage">;
|
|
69
|
+
}>, Schema.Struct<{
|
|
70
|
+
readonly tools: Schema.$Array<Schema.Trimmed>;
|
|
71
|
+
readonly afterExecution: Schema.Literals<readonly ["stay", "complete"]>;
|
|
72
|
+
readonly index: Schema.Natural;
|
|
73
|
+
readonly name: Schema.Trimmed;
|
|
74
|
+
readonly status: Schema.Literals<readonly ["complete", "current", "upcoming"]>;
|
|
75
|
+
readonly repairPending: Schema.Boolean;
|
|
76
|
+
readonly _tag: Schema.Literal<"ToolStage">;
|
|
77
|
+
}>, Schema.Struct<{
|
|
78
|
+
readonly command: Schema.Trimmed;
|
|
79
|
+
readonly index: Schema.Natural;
|
|
80
|
+
readonly name: Schema.Trimmed;
|
|
81
|
+
readonly status: Schema.Literals<readonly ["complete", "current", "upcoming"]>;
|
|
82
|
+
readonly repairPending: Schema.Boolean;
|
|
83
|
+
readonly _tag: Schema.Literal<"CommandStage">;
|
|
84
|
+
}>]>>;
|
|
85
|
+
}>;
|
|
86
|
+
/** JSON-safe read model rendered by a structured-chat debug inspector. */
|
|
87
|
+
export type StructuredChatDebugSnapshot = Schema.Schema.Type<typeof StructuredChatDebugSnapshotSchema>;
|
|
88
|
+
/** Controls sensitive provenance included in a structured-chat debug snapshot. */
|
|
89
|
+
export interface InspectChatStateOptions {
|
|
90
|
+
readonly evidence?: "include" | "omit";
|
|
91
|
+
}
|
|
92
|
+
declare const InvalidChatDebugProjection_base: Schema.Class<InvalidChatDebugProjection, Schema.TaggedStruct<"InvalidChatDebugProjection", {
|
|
93
|
+
readonly reason: Schema.Literals<readonly ["invalid_options", "invalid_state", "invalid_answer_value", "invalid_snapshot"]>;
|
|
94
|
+
}>, import("effect/Cause").YieldableError>;
|
|
95
|
+
/** A chat state or answer could not be projected into safe debug JSON. */
|
|
96
|
+
export declare class InvalidChatDebugProjection extends InvalidChatDebugProjection_base {
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Project one trusted chat definition and Type-side state into browser-safe
|
|
100
|
+
* inspector data without exposing choice values or raw Effect schemas.
|
|
101
|
+
*/
|
|
102
|
+
export declare const inspectChatState: <const Name extends string, const Version extends number, const Stages extends ChatStageTuple>(chat: ChatDefinition<Name, Version, Stages>, state: ChatState<Name, Version, Stages>, options?: InspectChatStateOptions) => Effect.Effect<StructuredChatDebugSnapshot, InvalidChatDebugProjection>;
|
|
103
|
+
export {};
|