opencode-courier 0.3.0 → 0.4.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/README.md +175 -274
- package/dist/index.js +47 -73
- package/dist/kernel/protocol.d.ts +20 -0
- package/dist/kernel/protocol.js +36 -0
- package/dist/kernel/tool.d.ts +5 -0
- package/dist/kernel/tool.js +5 -0
- package/dist/person/events.d.ts +23 -0
- package/dist/person/events.js +113 -0
- package/dist/person/form.d.ts +26 -0
- package/dist/person/form.js +64 -0
- package/dist/person/index.d.ts +17 -0
- package/dist/person/index.js +5 -0
- package/dist/{relay.d.ts → person/permission.d.ts} +27 -21
- package/dist/{relay.js → person/permission.js} +9 -5
- package/dist/person/ports.d.ts +58 -0
- package/dist/person/ports.js +2 -0
- package/dist/{question → person/question}/answer.d.ts +33 -4
- package/dist/{question → person/question}/answer.js +43 -8
- package/dist/person/question/index.d.ts +7 -0
- package/dist/{question → person/question}/index.js +3 -3
- package/dist/{question → person/question}/lifecycle.d.ts +1 -1
- package/dist/{question → person/question}/pure.d.ts +1 -2
- package/dist/{question → person/question}/pure.js +1 -1
- package/dist/{question → person/question}/relay.d.ts +1 -1
- package/dist/{question → person/question}/relay.js +14 -8
- package/dist/{question → person/question}/shared.d.ts +13 -3
- package/dist/person/question/shared.js +52 -0
- package/dist/person/question/types.d.ts +121 -0
- package/dist/person/question/types.js +1 -0
- package/dist/person/rpc-handlers.d.ts +160 -0
- package/dist/person/rpc-handlers.js +62 -0
- package/dist/person/rpc.d.ts +227 -0
- package/dist/person/rpc.js +70 -0
- package/dist/person/text.d.ts +68 -0
- package/dist/person/text.js +262 -0
- package/dist/person/tools.d.ts +20 -0
- package/dist/person/tools.js +55 -0
- package/dist/person/tui.d.ts +46 -0
- package/dist/{bounded.d.ts → platform/bounded.d.ts} +4 -0
- package/dist/{bounded.js → platform/bounded.js} +4 -0
- package/dist/platform/events.d.ts +34 -0
- package/dist/platform/events.js +121 -0
- package/dist/platform/hub.d.ts +195 -0
- package/dist/{hub.js → platform/hub.js} +27 -71
- package/dist/{version.js → platform/version.js} +1 -1
- package/dist/rpc.d.ts +2 -0
- package/dist/rpc.js +3 -0
- package/dist/scheduling/index.d.ts +6 -0
- package/dist/scheduling/index.js +5 -0
- package/dist/{later.d.ts → scheduling/later.d.ts} +7 -3
- package/dist/{later.js → scheduling/later.js} +12 -4
- package/dist/scheduling/text.d.ts +6 -0
- package/dist/scheduling/text.js +10 -0
- package/dist/scheduling/tools.d.ts +14 -0
- package/dist/scheduling/tools.js +48 -0
- package/dist/skill.d.ts +4 -0
- package/dist/skill.js +54 -1
- package/dist/{cleanup.d.ts → tree/cleanup.d.ts} +1 -27
- package/dist/{cleanup.js → tree/cleanup.js} +9 -16
- package/dist/tree/context.d.ts +15 -0
- package/dist/tree/context.js +34 -0
- package/dist/tree/domain.d.ts +101 -0
- package/dist/tree/domain.js +68 -0
- package/dist/{watch.d.ts → tree/events.d.ts} +10 -37
- package/dist/tree/events.js +204 -0
- package/dist/tree/forget.d.ts +24 -0
- package/dist/tree/forget.js +59 -0
- package/dist/tree/group.d.ts +82 -0
- package/dist/tree/group.js +203 -0
- package/dist/tree/index.d.ts +10 -0
- package/dist/tree/index.js +11 -0
- package/dist/tree/limits.d.ts +10 -0
- package/dist/{limits.js → tree/limits.js} +6 -55
- package/dist/tree/panel.d.ts +13 -0
- package/dist/tree/panel.js +75 -0
- package/dist/tree/ports.d.ts +276 -0
- package/dist/tree/ports.js +1 -0
- package/dist/tree/report.d.ts +23 -0
- package/dist/tree/report.js +33 -0
- package/dist/tree/roster.d.ts +27 -0
- package/dist/tree/roster.js +62 -0
- package/dist/tree/rpc-handlers.d.ts +201 -0
- package/dist/tree/rpc-handlers.js +20 -0
- package/dist/tree/rpc.d.ts +344 -0
- package/dist/tree/rpc.js +83 -0
- package/dist/tree/send.d.ts +14 -0
- package/dist/tree/send.js +99 -0
- package/dist/tree/spawn.d.ts +21 -0
- package/dist/tree/spawn.js +173 -0
- package/dist/tree/status.d.ts +128 -0
- package/dist/tree/status.js +62 -0
- package/dist/{stop.d.ts → tree/stop.d.ts} +1 -9
- package/dist/{stop.js → tree/stop.js} +9 -13
- package/dist/tree/store.d.ts +24 -0
- package/dist/tree/store.js +264 -0
- package/dist/{tree.d.ts → tree/subtree.d.ts} +5 -6
- package/dist/{tree.js → tree/subtree.js} +7 -5
- package/dist/tree/summary.d.ts +7 -0
- package/dist/tree/summary.js +35 -0
- package/dist/tree/text.d.ts +77 -0
- package/dist/tree/text.js +287 -0
- package/dist/tree/tools.d.ts +54 -0
- package/dist/{tools.js → tree/tools.js} +12 -142
- package/dist/tree/tui.d.ts +53 -0
- package/dist/tui.d.ts +3 -0
- package/dist/tui.js +1219 -0
- package/dist/webhook/index.d.ts +5 -0
- package/dist/webhook/index.js +3 -0
- package/dist/{webhook.d.ts → webhook/receiver.d.ts} +17 -23
- package/dist/{webhook.js → webhook/receiver.js} +58 -50
- package/dist/webhook/subscriptions.d.ts +29 -0
- package/dist/webhook/subscriptions.js +52 -0
- package/dist/webhook/text.d.ts +23 -0
- package/dist/webhook/text.js +82 -0
- package/dist/webhook/tools.d.ts +19 -0
- package/dist/webhook/tools.js +46 -0
- package/package.json +18 -7
- package/dist/courier.d.ts +0 -200
- package/dist/courier.js +0 -315
- package/dist/group.d.ts +0 -86
- package/dist/group.js +0 -247
- package/dist/hub.d.ts +0 -300
- package/dist/limits.d.ts +0 -65
- package/dist/notices.d.ts +0 -356
- package/dist/notices.js +0 -678
- package/dist/question/index.d.ts +0 -6
- package/dist/question/shared.js +0 -24
- package/dist/report.d.ts +0 -48
- package/dist/report.js +0 -88
- package/dist/roster.d.ts +0 -93
- package/dist/roster.js +0 -206
- package/dist/tools.d.ts +0 -94
- package/dist/watch.js +0 -474
- /package/dist/{question → person/question}/lifecycle.js +0 -0
- /package/dist/{question → person/question}/runtime.d.ts +0 -0
- /package/dist/{question → person/question}/runtime.js +0 -0
- /package/dist/{json.d.ts → platform/json.d.ts} +0 -0
- /package/dist/{json.js → platform/json.js} +0 -0
- /package/dist/{storage.d.ts → platform/storage.d.ts} +0 -0
- /package/dist/{storage.js → platform/storage.js} +0 -0
- /package/dist/{version.d.ts → platform/version.d.ts} +0 -0
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import type { QuestionPorts } from "
|
|
3
|
-
import { type
|
|
4
|
-
import { type RosterStorage } from "
|
|
2
|
+
import type { Answers, Asked, Outcome, Prompt, QuestionPorts, Withdrawal } from "./types.js";
|
|
3
|
+
import { type CutOff } from "../text.js";
|
|
4
|
+
import { type RosterStorage } from "../../tree/index.js";
|
|
5
5
|
import { type QuestionAnswerInput } from "./pure.js";
|
|
6
|
+
/** What answering a question did; `answered` is false when it no longer waited. */
|
|
7
|
+
export type QuestionAnswered = {
|
|
8
|
+
readonly sessionID: string;
|
|
9
|
+
readonly requestID: string;
|
|
10
|
+
readonly answered: false;
|
|
11
|
+
} | {
|
|
12
|
+
readonly sessionID: string;
|
|
13
|
+
readonly requestID: string;
|
|
14
|
+
readonly answered: true;
|
|
15
|
+
readonly by: "result" | "message";
|
|
16
|
+
readonly answers: Answers;
|
|
17
|
+
};
|
|
6
18
|
export declare function tell(ports: QuestionPorts, asked: Asked, text: string, attributes: Record<string, string>): Promise<void>;
|
|
7
19
|
/** Tells the top session that a question's call was cut off, once per question and process. */
|
|
8
20
|
export declare function tellCutOff(ports: QuestionPorts, asked: Asked, cutOff: CutOff): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* What a linked call of the top session is withdrawn with once an answer is passed on, `withdrawal`, and
|
|
23
|
+
* whether one was: `linked`, set once it is withdrawn.
|
|
24
|
+
*/
|
|
25
|
+
export interface Linked {
|
|
26
|
+
readonly withdrawal: Withdrawal;
|
|
27
|
+
linked: boolean;
|
|
28
|
+
}
|
|
9
29
|
/**
|
|
10
30
|
* Passes the top session's answer, or the person's dismissal, to a question: as the result of the
|
|
11
31
|
* call that waits on it, or as a message when that call was cut off. Undefined when it was settled.
|
|
12
32
|
*/
|
|
13
|
-
export declare const deliver: (ports: QuestionPorts, asked: Asked, outcome: Outcome) => Effect.Effect<"result" | "message" | undefined, unknown>;
|
|
33
|
+
export declare const deliver: (ports: QuestionPorts, asked: Asked, outcome: Outcome, given?: Linked) => Effect.Effect<"result" | "message" | undefined, unknown>;
|
|
14
34
|
/**
|
|
15
35
|
* Drops a stored question whose answer went out. If that fails, it is marked answered instead, so
|
|
16
36
|
* it is neither listed nor answered again, nor told about after a restart.
|
|
@@ -21,6 +41,15 @@ export declare function forget(ports: QuestionPorts, asked: Asked): Promise<void
|
|
|
21
41
|
* be the session at the top, as for permission requests. `answered` is false when it no longer waits.
|
|
22
42
|
*/
|
|
23
43
|
export declare function answerQuestion(ports: QuestionPorts, callerID: string, input: QuestionAnswerInput): Promise<QuestionAnswered>;
|
|
44
|
+
/**
|
|
45
|
+
* Answers a question for the person, from the TUI's inbox, as courier_answer does; the top session hears it once, as
|
|
46
|
+
* answered by the person: its linked call is withdrawn, or else, if it was told of it, it is told settled.
|
|
47
|
+
*/
|
|
48
|
+
export declare function answerForPerson(ports: QuestionPorts, input: QuestionAnswerInput): Promise<QuestionAnswered>;
|
|
49
|
+
/** Whether a session waits on a question of its own, open or cut off and not yet answered. */
|
|
50
|
+
export declare function asksQuestion(ports: {
|
|
51
|
+
readonly storage: RosterStorage;
|
|
52
|
+
}, sessionID: string): Promise<boolean>;
|
|
24
53
|
/** The questions a session waits on, for courier_status; `stopped` when its call was cut off. */
|
|
25
54
|
export declare function pendingQuestions(storage: RosterStorage, sessionID: string): Promise<{
|
|
26
55
|
type: "question";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Deferred, Effect, Semaphore } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { envelope } from "../../kernel/protocol.js";
|
|
3
|
+
import { cutOffAnswer, questionNotice, questionSettledNotice } from "../text.js";
|
|
4
|
+
import { allEntries, answeringTop, lineageIn, RETENTION_MS } from "../../tree/index.js";
|
|
5
|
+
import { scanAll } from "../../platform/storage.js";
|
|
5
6
|
import { within, run } from "./runtime.js";
|
|
6
7
|
import { normalize } from "./pure.js";
|
|
7
8
|
import { claim, isPassing, keyOf, MAX_STORED, PREFIX, shared, storedOf } from "./shared.js";
|
|
@@ -54,7 +55,8 @@ const holding = (id, ms, effect) => Effect.acquireUseRelease(Effect.sync(() => {
|
|
|
54
55
|
* Passes the top session's answer, or the person's dismissal, to a question: as the result of the
|
|
55
56
|
* call that waits on it, or as a message when that call was cut off. Undefined when it was settled.
|
|
56
57
|
*/
|
|
57
|
-
export const deliver = (ports, asked, outcome) => Effect.suspend(() => {
|
|
58
|
+
export const deliver = (ports, asked, outcome, given) => Effect.suspend(() => {
|
|
59
|
+
const linked = given ?? { withdrawal: { by: "top", outcome }, linked: false };
|
|
58
60
|
const id = asked.requestID;
|
|
59
61
|
const wait = ports.timing.passingWaitMs;
|
|
60
62
|
// One answer at a time, and once, per question in this process, since storage cannot be claimed
|
|
@@ -70,7 +72,7 @@ export const deliver = (ports, asked, outcome) => Effect.suspend(() => {
|
|
|
70
72
|
}
|
|
71
73
|
if (shared.answered.has(id))
|
|
72
74
|
return undefined;
|
|
73
|
-
const passing = passOn(ports, asked, outcome);
|
|
75
|
+
const passing = passOn(ports, asked, outcome, linked);
|
|
74
76
|
shared.passing.set(id, passing);
|
|
75
77
|
// Held while it is passed on; one that hangs on a notice that never returns gives way after a
|
|
76
78
|
// while, so the question can be answered again, and the child may then be told twice.
|
|
@@ -82,7 +84,14 @@ export const deliver = (ports, asked, outcome) => Effect.suspend(() => {
|
|
|
82
84
|
}));
|
|
83
85
|
return Effect.flatMap(turn, (passing) => passing ? Effect.tryPromise({ try: () => passing, catch: (error) => error }) : Effect.succeed(undefined));
|
|
84
86
|
});
|
|
85
|
-
|
|
87
|
+
/** Withdraws the top session's call linked to a question, if any, now that it is settled. */
|
|
88
|
+
function withdraw(known, linked) {
|
|
89
|
+
if (!known.link)
|
|
90
|
+
return;
|
|
91
|
+
linked.linked = true;
|
|
92
|
+
known.link(linked.withdrawal);
|
|
93
|
+
}
|
|
94
|
+
async function passOn(ports, asked, outcome, linked) {
|
|
86
95
|
const id = asked.requestID;
|
|
87
96
|
let known = shared.questions.get(id);
|
|
88
97
|
// Taken only if the call ends with it: one already ending some other way, its turn being
|
|
@@ -91,7 +100,7 @@ async function passOn(ports, asked, outcome) {
|
|
|
91
100
|
claim(shared.answered, id);
|
|
92
101
|
shared.questions.delete(id);
|
|
93
102
|
// A call of the top session linked to it, asking the person, is withdrawn: it is settled.
|
|
94
|
-
known
|
|
103
|
+
withdraw(known, linked);
|
|
95
104
|
return "result";
|
|
96
105
|
}
|
|
97
106
|
// Its call just ended: whether it was cut off and stays or was settled and is dropped is known once
|
|
@@ -114,7 +123,7 @@ async function passOn(ports, asked, outcome) {
|
|
|
114
123
|
// linked call, if any, goes then too, not before the answer is out.
|
|
115
124
|
claim(shared.answered, id);
|
|
116
125
|
shared.questions.delete(id);
|
|
117
|
-
known
|
|
126
|
+
withdraw(known, linked);
|
|
118
127
|
await forget(ports, asked);
|
|
119
128
|
return "message";
|
|
120
129
|
}
|
|
@@ -155,6 +164,32 @@ export async function answerQuestion(ports, callerID, input) {
|
|
|
155
164
|
const by = await run(deliver(ports, asked, { answers }));
|
|
156
165
|
return by ? { sessionID, requestID, answered: true, by, answers } : { sessionID, requestID, answered: false };
|
|
157
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Answers a question for the person, from the TUI's inbox, as courier_answer does; the top session hears it once, as
|
|
169
|
+
* answered by the person: its linked call is withdrawn, or else, if it was told of it, it is told settled.
|
|
170
|
+
*/
|
|
171
|
+
export async function answerForPerson(ports, input) {
|
|
172
|
+
const { sessionID, requestID } = input;
|
|
173
|
+
const known = shared.questions.get(requestID);
|
|
174
|
+
const asked = known ?? (await ports.storage.get(keyOf(requestID)));
|
|
175
|
+
if (asked?.sessionID !== sessionID || asked.answered)
|
|
176
|
+
return { sessionID, requestID, answered: false };
|
|
177
|
+
const answers = normalize(asked, input.answers);
|
|
178
|
+
const settled = { by: "child", answers, via: "inbox" };
|
|
179
|
+
const linked = { withdrawal: settled, linked: false };
|
|
180
|
+
const by = await run(deliver(ports, asked, { answers }, linked));
|
|
181
|
+
if (!by)
|
|
182
|
+
return { sessionID, requestID, answered: false };
|
|
183
|
+
// A question whose notice did not go out is not settled with one either; a stored one was told when cut off.
|
|
184
|
+
const told = !known?.relaying || (await run(within(known.relaying, ports.timing.relayWaitMs))) === true;
|
|
185
|
+
if (!linked.linked && told)
|
|
186
|
+
await tell(ports, asked, questionSettledNotice(asked, settled), { answered: "elsewhere", request: requestID }).catch((error) => ports.log(`courier question: could not tell ${asked.top} that ${requestID} is settled: ${String(error)}`));
|
|
187
|
+
return { sessionID, requestID, answered: true, by, answers };
|
|
188
|
+
}
|
|
189
|
+
/** Whether a session waits on a question of its own, open or cut off and not yet answered. */
|
|
190
|
+
export async function asksQuestion(ports, sessionID) {
|
|
191
|
+
return (await pendingQuestions(ports.storage, sessionID)).length > 0;
|
|
192
|
+
}
|
|
158
193
|
/** The questions a session waits on, for courier_status; `stopped` when its call was cut off. */
|
|
159
194
|
export async function pendingQuestions(storage, sessionID) {
|
|
160
195
|
const found = new Map();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { answerForPerson, answerQuestion, asksQuestion, noticeCutOff, pendingQuestions } from "./answer.js";
|
|
2
|
+
export { eventsFollowed, eventsLeft, formShown, formsMayHaveBeenMissed, joinRelay, locationClosing, shutdownReportedAt } from "./lifecycle.js";
|
|
3
|
+
export { byCodeUnit, type QuestionAnswerInput } from "./pure.js";
|
|
4
|
+
export { relayQuestions } from "./relay.js";
|
|
5
|
+
export { isQuestion, MAX_STORED, portsAt, QUESTION_TOOL } from "./shared.js";
|
|
6
|
+
export type { Answers, Asked, Elsewhere, Outcome, Prompt, QuestionPorts, QuestionTiming, Withdrawal } from "./types.js";
|
|
7
|
+
export type { QuestionAnswered } from "./answer.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The question relay: what the rest of the plugin uses of it. The relay's state is kept in the hub
|
|
2
|
-
// (
|
|
3
|
-
export { answerQuestion, noticeCutOff, pendingQuestions } from "./answer.js";
|
|
2
|
+
// (`../../platform/hub.ts`), under a process-wide key of its own; `types.ts` types it.
|
|
3
|
+
export { answerForPerson, answerQuestion, asksQuestion, noticeCutOff, pendingQuestions } from "./answer.js";
|
|
4
4
|
export { eventsFollowed, eventsLeft, formShown, formsMayHaveBeenMissed, joinRelay, locationClosing, shutdownReportedAt } from "./lifecycle.js";
|
|
5
5
|
export { byCodeUnit } from "./pure.js";
|
|
6
6
|
export { relayQuestions } from "./relay.js";
|
|
7
|
-
export { isQuestion, MAX_STORED, QUESTION_TOOL } from "./shared.js";
|
|
7
|
+
export { isQuestion, MAX_STORED, portsAt, QUESTION_TOOL } from "./shared.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import type { QuestionPorts } from "
|
|
2
|
+
import type { QuestionPorts } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* For tests: when the location at `directory` was last reported shutting down, if at all; a shutdown
|
|
5
5
|
* reported without a location counts for every directory, and with none given, any location's counts.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Plugin as EffectPlugin } from "@opencode/plugin/effect";
|
|
2
2
|
import { Cause, Exit, type Effect } from "effect";
|
|
3
|
-
import type { Question } from "
|
|
4
|
-
import { type Answers, type Asked, type Elsewhere, type Prompt, type Withdrawal } from "../notices.js";
|
|
3
|
+
import type { Answers, Asked, Elsewhere, Prompt, Question, Withdrawal } from "./types.js";
|
|
5
4
|
type ToolEditor = Parameters<Parameters<EffectPlugin.Context["tool"]["transform"]>[0]>[0];
|
|
6
5
|
type HostTool = Parameters<Parameters<ToolEditor["update"]>[1]>[0];
|
|
7
6
|
export type Execute = HostTool["execute"];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Cause, Exit, Result } from "effect";
|
|
2
|
-
import { withdrawnText } from "../
|
|
2
|
+
import { withdrawnText } from "../text.js";
|
|
3
3
|
/** The questions of a call, without anything the tool does not define. */
|
|
4
4
|
export function promptsOf(input) {
|
|
5
5
|
const questions = input?.questions ?? [];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Plugin as EffectPlugin } from "@opencode/plugin/effect";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import type { QuestionPorts } from "
|
|
3
|
+
import type { QuestionPorts } from "./types.js";
|
|
4
4
|
/** Wraps OpenCode's question tool for the relay. A tool by that id that is missing is left alone. */
|
|
5
5
|
export declare function relayQuestions(host: Pick<EffectPlugin.Context, "tool" | "location">, ports: () => QuestionPorts | undefined): Effect.Effect<import("@opencode/plugin/effect/registration").Registration, never, import("effect/Scope").Scope>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Deferred, Effect, Exit, Fiber } from "effect";
|
|
2
|
-
import { answeredText, DISMISSED, passedNote, questionNotice, questionSettledNotice, unlinkedNote, } from "../
|
|
3
|
-
import { lineage } from "
|
|
2
|
+
import { answeredText, DISMISSED, nativeQuestionNotice, passedNote, questionNotice, questionSettledNotice, unlinkedNote, } from "../text.js";
|
|
3
|
+
import { lineage, nativeRootOf } from "../../tree/index.js";
|
|
4
4
|
import { deliver, forget, tell, tellCutOff } from "./answer.js";
|
|
5
5
|
import { closingSoon } from "./lifecycle.js";
|
|
6
6
|
import { choices, isDismissal, normalize, promptsOf, settledIn, withdrawnResult, wording, } from "./pure.js";
|
|
@@ -24,7 +24,10 @@ async function relay(ports, question) {
|
|
|
24
24
|
shared.questions.set(question.requestID, question);
|
|
25
25
|
try {
|
|
26
26
|
await ports.storage.set(keyOf(question.requestID), storedOf(question));
|
|
27
|
-
|
|
27
|
+
if (question.native)
|
|
28
|
+
await tell(ports, question, nativeQuestionNotice(question), { asks: "question", request: question.requestID, shown: "native" });
|
|
29
|
+
else
|
|
30
|
+
await tell(ports, question, questionNotice(question), { asks: "question", request: question.requestID });
|
|
28
31
|
return true;
|
|
29
32
|
}
|
|
30
33
|
catch (error) {
|
|
@@ -110,14 +113,17 @@ function asking(ports, directory, original) {
|
|
|
110
113
|
const acceptance = () => (acceptancePromise ??= Effect.runPromise(Deferred.await(accepted)));
|
|
111
114
|
// Handed one outcome only: a second, while the first still ends the call, is not taken.
|
|
112
115
|
let handed = false;
|
|
116
|
+
const top = chain.at(-1).parentID;
|
|
113
117
|
const question = {
|
|
114
118
|
requestID: current.newID(),
|
|
115
119
|
sessionID: context.sessionID,
|
|
116
|
-
top
|
|
120
|
+
top,
|
|
117
121
|
title: chain[0].title,
|
|
118
122
|
...(chain.length > 1 ? { startedBy: chain[0].parentID } : {}),
|
|
119
123
|
questions: promptsOf(input),
|
|
120
124
|
askedAt: current.now(),
|
|
125
|
+
// OpenCode shows a native child's form in its native root's view: when that is the top's, the person answers there.
|
|
126
|
+
...(nativeRootOf(chain) === top ? { native: true } : {}),
|
|
121
127
|
directory,
|
|
122
128
|
call: (outcome) => {
|
|
123
129
|
if (handed)
|
|
@@ -169,6 +175,8 @@ function asking(ports, directory, original) {
|
|
|
169
175
|
};
|
|
170
176
|
});
|
|
171
177
|
}
|
|
178
|
+
/** Whether the top session's question call may be linked to a question: not a native child's while its own form is shown. */
|
|
179
|
+
const linkable = (question, sessionID) => question.top === sessionID && !question.link && !isPassing(question.requestID) && !(question.native && question.call);
|
|
172
180
|
/**
|
|
173
181
|
* The waiting question a top session's call asks again, the oldest if several: the same question with
|
|
174
182
|
* the same choices, in any order. Choices alone would let an unrelated yes-or-no answer a child's.
|
|
@@ -176,9 +184,7 @@ function asking(ports, directory, original) {
|
|
|
176
184
|
function linkFor(sessionID, questions) {
|
|
177
185
|
const asked = { choices: choices(questions), wording: wording(questions) };
|
|
178
186
|
return [...shared.questions.values()]
|
|
179
|
-
.filter((question) => question
|
|
180
|
-
!question.link &&
|
|
181
|
-
!isPassing(question.requestID) &&
|
|
187
|
+
.filter((question) => linkable(question, sessionID) &&
|
|
182
188
|
choices(question.questions) === asked.choices &&
|
|
183
189
|
wording(question.questions) === asked.wording)
|
|
184
190
|
.sort((a, b) => a.askedAt - b.askedAt)[0];
|
|
@@ -191,7 +197,7 @@ function unlinkedResult(result, sessionID, questions) {
|
|
|
191
197
|
if (typeof result.content !== "string")
|
|
192
198
|
return result;
|
|
193
199
|
const asked = choices(questions);
|
|
194
|
-
const similar = [...shared.questions.values()].filter((question) => question
|
|
200
|
+
const similar = [...shared.questions.values()].filter((question) => linkable(question, sessionID) && choices(question.questions) === asked);
|
|
195
201
|
if (!similar.length)
|
|
196
202
|
return result;
|
|
197
203
|
return { ...result, content: `${result.content}\n${unlinkedNote(similar)}` };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { Asked } from "
|
|
1
|
+
import type { Registry } from "../../platform/storage.js";
|
|
2
|
+
import type { Asked, Question, QuestionPorts, QuestionState } from "./types.js";
|
|
3
3
|
/** OpenCode's own question tool, which the relay wraps. */
|
|
4
4
|
export declare const QUESTION_TOOL = "question";
|
|
5
5
|
export declare const PREFIX = "question/";
|
|
@@ -11,12 +11,22 @@ export type Stored = Asked & {
|
|
|
11
11
|
};
|
|
12
12
|
export declare const storedOf: ({ directory: _directory, call: _call, link: _link, relaying: _relaying, settling: _settling, ...asked }: Question) => Asked;
|
|
13
13
|
export declare const isPassing: (requestID: string) => boolean;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The relay's state in `registry`, the hub's `questions`, with the fields it lacks made: field by
|
|
16
|
+
* field, since a copy before the hub may have made it without the fields added later.
|
|
17
|
+
*/
|
|
18
|
+
export declare function questionState(registry?: Registry): QuestionState;
|
|
19
|
+
export declare const shared: QuestionState;
|
|
15
20
|
/**
|
|
16
21
|
* The ports of an instance still loaded, if any: a member of this copy's hub, or else one that a
|
|
17
22
|
* copy of another hub version, or a release before the hub, keeps in `loaded`.
|
|
18
23
|
*/
|
|
19
24
|
export declare const anyLoaded: () => QuestionPorts | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The question ports of the member that just joined at `location`, while it is loaded: resolved once,
|
|
27
|
+
* so a later instance at the same location does not take its place while this one is still loaded.
|
|
28
|
+
*/
|
|
29
|
+
export declare function portsAt(location: object): () => QuestionPorts | undefined;
|
|
20
30
|
export declare const keyOf: (requestID: string) => string;
|
|
21
31
|
/** Whether a request id is a question's rather than a permission request's. */
|
|
22
32
|
export declare const isQuestion: (requestID: string) => boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { addBounded } from "../../platform/bounded.js";
|
|
2
|
+
import { hub, memberAt, sharedState } from "../../platform/hub.js";
|
|
3
|
+
/** OpenCode's own question tool, which the relay wraps. */
|
|
4
|
+
export const QUESTION_TOOL = "question";
|
|
5
|
+
export const PREFIX = "question/";
|
|
6
|
+
/** At most this many questions are kept in storage; the oldest go first. */
|
|
7
|
+
export const MAX_STORED = 100;
|
|
8
|
+
export const storedOf = ({ directory: _directory, call: _call, link: _link, relaying: _relaying, settling: _settling, ...asked }) => asked;
|
|
9
|
+
export const isPassing = (requestID) => shared.passing.has(requestID) || shared.answered.has(requestID);
|
|
10
|
+
/**
|
|
11
|
+
* The relay's state in `registry`, the hub's `questions`, with the fields it lacks made: field by
|
|
12
|
+
* field, since a copy before the hub may have made it without the fields added later.
|
|
13
|
+
*/
|
|
14
|
+
export function questionState(registry) {
|
|
15
|
+
const state = sharedState("questions", () => ({}), registry);
|
|
16
|
+
state.questions ??= new Map();
|
|
17
|
+
state.noticed ??= new Set();
|
|
18
|
+
state.shown ??= new Map();
|
|
19
|
+
state.passing ??= new Map();
|
|
20
|
+
state.answered ??= new Set();
|
|
21
|
+
state.loaded ??= new Set();
|
|
22
|
+
state.following ??= 0;
|
|
23
|
+
state.followed ??= false;
|
|
24
|
+
state.shutdowns ??= new Map();
|
|
25
|
+
state.closingWaiters ??= new Set();
|
|
26
|
+
return state;
|
|
27
|
+
}
|
|
28
|
+
// Shared by every plugin instance in the process, since a child and the session it asks can be in
|
|
29
|
+
// different locations.
|
|
30
|
+
export const shared = questionState();
|
|
31
|
+
/** A member's question ports: every hub-v2 member's `questions` are its instance's, whichever copy joined it. */
|
|
32
|
+
const portsOf = (member) => member?.questions;
|
|
33
|
+
/**
|
|
34
|
+
* The ports of an instance still loaded, if any: a member of this copy's hub, or else one that a
|
|
35
|
+
* copy of another hub version, or a release before the hub, keeps in `loaded`.
|
|
36
|
+
*/
|
|
37
|
+
export const anyLoaded = () => portsOf(hub.members.values().next().value) ?? shared.loaded.values().next().value;
|
|
38
|
+
/**
|
|
39
|
+
* The question ports of the member that just joined at `location`, while it is loaded: resolved once,
|
|
40
|
+
* so a later instance at the same location does not take its place while this one is still loaded.
|
|
41
|
+
*/
|
|
42
|
+
export function portsAt(location) {
|
|
43
|
+
const own = memberAt(location);
|
|
44
|
+
return () => (own && hub.members.has(own) ? portsOf(own) : undefined);
|
|
45
|
+
}
|
|
46
|
+
export const keyOf = (requestID) => `${PREFIX}${requestID}`;
|
|
47
|
+
/** Whether a request id is a question's rather than a permission request's. */
|
|
48
|
+
export const isQuestion = (requestID) => requestID.startsWith("question_");
|
|
49
|
+
/** How many noticed and answered questions are remembered. */
|
|
50
|
+
const REMEMBERED = 1_000;
|
|
51
|
+
/** Adds to one of the shared bounded sets. */
|
|
52
|
+
export const claim = (set, value) => addBounded(set, value, REMEMBERED);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode/plugin";
|
|
2
|
+
import type { Storage } from "../../platform/storage.js";
|
|
3
|
+
type Context = Plugin.Context;
|
|
4
|
+
/** One question of OpenCode's question tool, as the asking model wrote it. */
|
|
5
|
+
export interface Prompt {
|
|
6
|
+
readonly question: string;
|
|
7
|
+
readonly header: string;
|
|
8
|
+
readonly options: ReadonlyArray<{
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly description: string;
|
|
11
|
+
}>;
|
|
12
|
+
readonly multiple?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** The answers to a question call, as the question tool returns them: one list per question. */
|
|
15
|
+
export type Answers = string[][];
|
|
16
|
+
/** A question a spawned session asked with its question tool, stored until it is answered. */
|
|
17
|
+
export interface Asked {
|
|
18
|
+
readonly requestID: string;
|
|
19
|
+
readonly sessionID: string;
|
|
20
|
+
/** The session told, at the top of the sessions started with courier_spawn; it alone answers. */
|
|
21
|
+
readonly top: string;
|
|
22
|
+
readonly title: string;
|
|
23
|
+
/** The session that started the asking one, when that is not the top session itself. */
|
|
24
|
+
readonly startedBy?: string;
|
|
25
|
+
readonly questions: ReadonlyArray<Prompt>;
|
|
26
|
+
readonly askedAt: number;
|
|
27
|
+
/** A native child's, whose form OpenCode shows the person in the top session's own view; not linked while shown. */
|
|
28
|
+
readonly native?: true;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* How a question was settled without the top session: in the asking session (or, `via` "inbox", by the
|
|
32
|
+
* person in the TUI's asks inbox), or not at all.
|
|
33
|
+
*/
|
|
34
|
+
export type Elsewhere = {
|
|
35
|
+
readonly by: "child";
|
|
36
|
+
readonly answers: Answers;
|
|
37
|
+
readonly via?: "inbox";
|
|
38
|
+
} | {
|
|
39
|
+
readonly by: "dismissed";
|
|
40
|
+
} | {
|
|
41
|
+
readonly by: "failed";
|
|
42
|
+
readonly error: string;
|
|
43
|
+
};
|
|
44
|
+
/** What the top session's answer does to a call that waits: answers it, or dismisses it. */
|
|
45
|
+
export type Outcome = {
|
|
46
|
+
readonly answers: Answers;
|
|
47
|
+
} | {
|
|
48
|
+
readonly dismissed: true;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Why a linked call is withdrawn: its question was settled elsewhere, or the top session's outcome
|
|
52
|
+
* reached it another way (courier_answer, or a dismissal through an earlier call linked to it).
|
|
53
|
+
*/
|
|
54
|
+
export type Withdrawal = Elsewhere | {
|
|
55
|
+
readonly by: "top";
|
|
56
|
+
readonly outcome: Outcome;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* A question this process knows: its call waits (`call`), or was cut off and is answered by message.
|
|
60
|
+
* `link` withdraws the top session's call passing the answer on, once settled without that call.
|
|
61
|
+
*/
|
|
62
|
+
export interface Question extends Asked {
|
|
63
|
+
/** The directory of the location the call runs in, whose shutdown withdraws its form. */
|
|
64
|
+
directory?: string;
|
|
65
|
+
/** Hands the waiting call the top session's outcome; true once the call has ended with it. */
|
|
66
|
+
call?: (outcome: Outcome) => Promise<boolean>;
|
|
67
|
+
link?: (how: Withdrawal) => void;
|
|
68
|
+
/** Storing the question and telling the top session, while that is under way. */
|
|
69
|
+
relaying?: Promise<boolean>;
|
|
70
|
+
/** Working out what became of it once its call ended without the top session's answer. */
|
|
71
|
+
settling?: Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The relay's timings, carried by its ports: the closing grace before a cut-off question is told, the
|
|
75
|
+
* wait for an answer still being passed on, the wait for a notice to go out, and the dismissal hold.
|
|
76
|
+
*/
|
|
77
|
+
export interface QuestionTiming {
|
|
78
|
+
readonly closingGraceMs: number;
|
|
79
|
+
readonly passingWaitMs: number;
|
|
80
|
+
readonly relayWaitMs: number;
|
|
81
|
+
readonly dismissalGraceMs: number;
|
|
82
|
+
}
|
|
83
|
+
export interface QuestionPorts {
|
|
84
|
+
readonly storage: Storage;
|
|
85
|
+
readonly session: Pick<Context["session"], "synthetic">;
|
|
86
|
+
/** The directory of the instance's location. */
|
|
87
|
+
readonly directory: string;
|
|
88
|
+
/** The clock, in epoch milliseconds, as OpenCode's location shutdowns are recorded. */
|
|
89
|
+
readonly now: () => number;
|
|
90
|
+
readonly timing: QuestionTiming;
|
|
91
|
+
readonly newID: () => string;
|
|
92
|
+
readonly log: (message: string) => void;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The question relay's state, shared by every instance, as a child and the session it asks can be in
|
|
96
|
+
* different locations: open questions, cut-off ones already told, and calls waiting for their form.
|
|
97
|
+
*/
|
|
98
|
+
export interface QuestionState {
|
|
99
|
+
readonly questions: Map<string, Question>;
|
|
100
|
+
readonly noticed: Set<string>;
|
|
101
|
+
readonly shown: Map<string, () => void>;
|
|
102
|
+
/** The questions whose answer is being passed on, each until that is done, and those whose answer went out. */
|
|
103
|
+
readonly passing: Map<string, Promise<unknown>>;
|
|
104
|
+
readonly answered: Set<string>;
|
|
105
|
+
/**
|
|
106
|
+
* The ports of every loaded instance of every copy. A release before the hub picks one here to tell
|
|
107
|
+
* a cut-off question through, so every copy fills it; this copy reads it only for another copy.
|
|
108
|
+
*/
|
|
109
|
+
readonly loaded: Set<QuestionPorts>;
|
|
110
|
+
/** How many instances follow OpenCode's events now, and whether any has since the process started. */
|
|
111
|
+
following: number;
|
|
112
|
+
followed: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* When OpenCode last reported each location shutting down (`location.shutdown`), by directory
|
|
115
|
+
* in epoch milliseconds by the clock of the watcher that saw it; under `""` when the event named no location.
|
|
116
|
+
*/
|
|
117
|
+
readonly shutdowns: Map<string, number>;
|
|
118
|
+
/** Woken when a location shuts down or an instance unloads: dismissals held to see whether one follows. */
|
|
119
|
+
readonly closingWaiters: Set<() => void>;
|
|
120
|
+
}
|
|
121
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode/plugin";
|
|
2
|
+
import { type Permissions } from "./permission.js";
|
|
3
|
+
import { type QuestionPorts } from "./question/index.js";
|
|
4
|
+
import { type Answered, type Asks } from "./rpc.js";
|
|
5
|
+
/** What the person's RPC reads and answers through: the relay's ports, which carry the storage, and every location's permissions. */
|
|
6
|
+
export interface PersonRpcPorts {
|
|
7
|
+
readonly questions: QuestionPorts;
|
|
8
|
+
readonly permissions: () => Iterable<Permissions>;
|
|
9
|
+
/** At most this many sessions of a tree are read, as courier_tree reads them. */
|
|
10
|
+
readonly maxTotal: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The permission requests and questions waiting in the courier tree a session is in, from the roster: read from the
|
|
14
|
+
* top of its lineage down, so each session of the tree sees the same inbox.
|
|
15
|
+
*/
|
|
16
|
+
export declare function asks(ports: PersonRpcPorts, sessionID: string): Promise<Asks>;
|
|
17
|
+
/** Passes the person's answers to a question on, as courier_answer does; a refusal, such as answers that do not fit, comes back as `problem`. */
|
|
18
|
+
export declare function answer(ports: PersonRpcPorts, input: {
|
|
19
|
+
sessionID: string;
|
|
20
|
+
requestID: string;
|
|
21
|
+
answers: ReadonlyArray<ReadonlyArray<string>>;
|
|
22
|
+
}): Promise<Answered>;
|
|
23
|
+
/** Serves the person's RPC at the plugin's location; disposed with the plugin. */
|
|
24
|
+
export declare function registerPersonRpc(rpc: Pick<Plugin.Context["rpc"], "register">, ports: PersonRpcPorts): Promise<import("@opencode/plugin/promise/rpc").RpcRegistration<{
|
|
25
|
+
readonly id: "courier-person";
|
|
26
|
+
readonly methods: {
|
|
27
|
+
readonly asks: {
|
|
28
|
+
readonly input: import("effect/StandardSchema").StandardSchemaV1<{
|
|
29
|
+
readonly sessionID: string;
|
|
30
|
+
}, {
|
|
31
|
+
readonly sessionID: string;
|
|
32
|
+
}> & import("effect/Schema").Struct<{
|
|
33
|
+
readonly sessionID: import("effect/Schema").String;
|
|
34
|
+
}>;
|
|
35
|
+
readonly output: import("effect/StandardSchema").StandardSchemaV1<{
|
|
36
|
+
readonly questions: readonly {
|
|
37
|
+
readonly questions: readonly {
|
|
38
|
+
readonly options: readonly {
|
|
39
|
+
readonly label: string;
|
|
40
|
+
readonly description: string;
|
|
41
|
+
}[];
|
|
42
|
+
readonly question: string;
|
|
43
|
+
readonly header: string;
|
|
44
|
+
readonly multiple?: boolean | undefined;
|
|
45
|
+
}[];
|
|
46
|
+
readonly sessionID: string;
|
|
47
|
+
readonly requestID: string;
|
|
48
|
+
readonly stopped?: boolean | undefined;
|
|
49
|
+
}[];
|
|
50
|
+
readonly permissions: readonly {
|
|
51
|
+
readonly sessionID: string;
|
|
52
|
+
readonly resources: readonly string[];
|
|
53
|
+
readonly action: string;
|
|
54
|
+
readonly requestID: string;
|
|
55
|
+
readonly save?: readonly string[] | undefined;
|
|
56
|
+
}[];
|
|
57
|
+
readonly top: string;
|
|
58
|
+
readonly sessions: readonly {
|
|
59
|
+
readonly sessionID: string;
|
|
60
|
+
readonly title: string;
|
|
61
|
+
readonly parentID: string;
|
|
62
|
+
readonly depth: number;
|
|
63
|
+
readonly group?: string | undefined;
|
|
64
|
+
}[];
|
|
65
|
+
readonly truncated?: number | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
readonly questions: readonly {
|
|
68
|
+
readonly questions: readonly {
|
|
69
|
+
readonly options: readonly {
|
|
70
|
+
readonly label: string;
|
|
71
|
+
readonly description: string;
|
|
72
|
+
}[];
|
|
73
|
+
readonly question: string;
|
|
74
|
+
readonly header: string;
|
|
75
|
+
readonly multiple?: boolean | undefined;
|
|
76
|
+
}[];
|
|
77
|
+
readonly sessionID: string;
|
|
78
|
+
readonly requestID: string;
|
|
79
|
+
readonly stopped?: boolean | undefined;
|
|
80
|
+
}[];
|
|
81
|
+
readonly permissions: readonly {
|
|
82
|
+
readonly sessionID: string;
|
|
83
|
+
readonly resources: readonly string[];
|
|
84
|
+
readonly action: string;
|
|
85
|
+
readonly requestID: string;
|
|
86
|
+
readonly save?: readonly string[] | undefined;
|
|
87
|
+
}[];
|
|
88
|
+
readonly top: string;
|
|
89
|
+
readonly sessions: readonly {
|
|
90
|
+
readonly sessionID: string;
|
|
91
|
+
readonly title: string;
|
|
92
|
+
readonly parentID: string;
|
|
93
|
+
readonly depth: number;
|
|
94
|
+
readonly group?: string | undefined;
|
|
95
|
+
}[];
|
|
96
|
+
readonly truncated?: number | undefined;
|
|
97
|
+
}> & import("effect/Schema").Struct<{
|
|
98
|
+
readonly top: import("effect/Schema").String;
|
|
99
|
+
readonly sessions: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
100
|
+
readonly sessionID: import("effect/Schema").String;
|
|
101
|
+
readonly title: import("effect/Schema").String;
|
|
102
|
+
readonly parentID: import("effect/Schema").String;
|
|
103
|
+
readonly depth: import("effect/Schema").Int;
|
|
104
|
+
readonly group: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
105
|
+
}>>;
|
|
106
|
+
readonly permissions: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
107
|
+
readonly sessionID: import("effect/Schema").String;
|
|
108
|
+
readonly requestID: import("effect/Schema").String;
|
|
109
|
+
readonly action: import("effect/Schema").String;
|
|
110
|
+
readonly resources: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
111
|
+
readonly save: import("effect/Schema").optionalKey<import("effect/Schema").$Array<import("effect/Schema").String>>;
|
|
112
|
+
}>>;
|
|
113
|
+
readonly questions: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
114
|
+
readonly sessionID: import("effect/Schema").String;
|
|
115
|
+
readonly requestID: import("effect/Schema").String;
|
|
116
|
+
readonly questions: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
117
|
+
readonly question: import("effect/Schema").String;
|
|
118
|
+
readonly header: import("effect/Schema").String;
|
|
119
|
+
readonly options: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
120
|
+
readonly label: import("effect/Schema").String;
|
|
121
|
+
readonly description: import("effect/Schema").String;
|
|
122
|
+
}>>;
|
|
123
|
+
readonly multiple: import("effect/Schema").optionalKey<import("effect/Schema").Boolean>;
|
|
124
|
+
}>>;
|
|
125
|
+
readonly stopped: import("effect/Schema").optionalKey<import("effect/Schema").Boolean>;
|
|
126
|
+
}>>;
|
|
127
|
+
readonly truncated: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
|
|
128
|
+
}>;
|
|
129
|
+
};
|
|
130
|
+
readonly answer: {
|
|
131
|
+
readonly input: import("effect/StandardSchema").StandardSchemaV1<{
|
|
132
|
+
readonly sessionID: string;
|
|
133
|
+
readonly requestID: string;
|
|
134
|
+
readonly answers: readonly (readonly string[])[];
|
|
135
|
+
}, {
|
|
136
|
+
readonly sessionID: string;
|
|
137
|
+
readonly requestID: string;
|
|
138
|
+
readonly answers: readonly (readonly string[])[];
|
|
139
|
+
}> & import("effect/Schema").Struct<{
|
|
140
|
+
readonly sessionID: import("effect/Schema").String;
|
|
141
|
+
readonly requestID: import("effect/Schema").String;
|
|
142
|
+
readonly answers: import("effect/Schema").$Array<import("effect/Schema").$Array<import("effect/Schema").String>>;
|
|
143
|
+
}>;
|
|
144
|
+
readonly output: import("effect/StandardSchema").StandardSchemaV1<{
|
|
145
|
+
readonly answered: boolean;
|
|
146
|
+
readonly by?: "message" | "result" | undefined;
|
|
147
|
+
readonly problem?: string | undefined;
|
|
148
|
+
}, {
|
|
149
|
+
readonly answered: boolean;
|
|
150
|
+
readonly by?: "message" | "result" | undefined;
|
|
151
|
+
readonly problem?: string | undefined;
|
|
152
|
+
}> & import("effect/Schema").Struct<{
|
|
153
|
+
readonly answered: import("effect/Schema").Boolean;
|
|
154
|
+
readonly by: import("effect/Schema").optionalKey<import("effect/Schema").Literals<readonly ["result", "message"]>>;
|
|
155
|
+
readonly problem: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
156
|
+
}>;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
readonly events: {};
|
|
160
|
+
}>>;
|