opencode-courier 0.13.8 → 0.14.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 +7 -9
- package/dist/index.d.ts +0 -8
- package/dist/index.js +6 -53
- package/dist/kernel/protocol.d.ts +0 -14
- package/dist/kernel/protocol.js +1 -15
- package/dist/kernel/tool.d.ts +0 -2
- package/dist/kernel/tool.js +0 -1
- package/dist/memory/domain.d.ts +6 -85
- package/dist/memory/domain.js +7 -63
- package/dist/memory/events.d.ts +0 -11
- package/dist/memory/events.js +1 -12
- package/dist/memory/forget.d.ts +0 -5
- package/dist/memory/forget.js +0 -4
- package/dist/memory/index.js +0 -2
- package/dist/memory/lessons.d.ts +0 -8
- package/dist/memory/lessons.js +0 -9
- package/dist/memory/note.d.ts +0 -9
- package/dist/memory/note.js +0 -5
- package/dist/memory/pin.d.ts +0 -5
- package/dist/memory/pin.js +0 -4
- package/dist/memory/ports.d.ts +2 -22
- package/dist/memory/recall.d.ts +0 -5
- package/dist/memory/recall.js +0 -12
- package/dist/memory/record.d.ts +0 -4
- package/dist/memory/record.js +8 -31
- package/dist/memory/rpc-handlers.d.ts +3 -6
- package/dist/memory/rpc-handlers.js +0 -6
- package/dist/memory/rpc.d.ts +6 -19
- package/dist/memory/rpc.js +1 -16
- package/dist/memory/store.d.ts +0 -5
- package/dist/memory/store.js +1 -16
- package/dist/memory/sweep.d.ts +0 -4
- package/dist/memory/sweep.js +0 -4
- package/dist/memory/text.d.ts +0 -25
- package/dist/memory/text.js +6 -36
- package/dist/memory/tools.d.ts +0 -2
- package/dist/memory/tools.js +0 -2
- package/dist/memory/tui.d.ts +1 -35
- package/dist/person/events.d.ts +0 -16
- package/dist/person/events.js +3 -30
- package/dist/person/form.d.ts +0 -9
- package/dist/person/form.js +0 -10
- package/dist/person/index.d.ts +0 -2
- package/dist/person/permission.d.ts +0 -34
- package/dist/person/permission.js +2 -28
- package/dist/person/ports.d.ts +0 -16
- package/dist/person/ports.js +0 -1
- package/dist/person/question/answer.d.ts +0 -28
- package/dist/person/question/answer.js +0 -49
- package/dist/person/question/index.js +0 -2
- package/dist/person/question/lifecycle.d.ts +0 -26
- package/dist/person/question/lifecycle.js +1 -32
- package/dist/person/question/pure.d.ts +0 -11
- package/dist/person/question/pure.js +0 -11
- package/dist/person/question/relay.d.ts +0 -1
- package/dist/person/question/relay.js +5 -67
- package/dist/person/question/runtime.d.ts +0 -14
- package/dist/person/question/runtime.js +1 -26
- package/dist/person/question/shared.d.ts +0 -17
- package/dist/person/question/shared.js +0 -20
- package/dist/person/question/types.d.ts +0 -44
- package/dist/person/rpc-handlers.d.ts +0 -7
- package/dist/person/rpc-handlers.js +0 -6
- package/dist/person/rpc.d.ts +0 -20
- package/dist/person/rpc.js +0 -16
- package/dist/person/text.d.ts +0 -45
- package/dist/person/text.js +2 -54
- package/dist/person/tools.d.ts +0 -3
- package/dist/person/tools.js +1 -3
- package/dist/person/tui.d.ts +0 -21
- package/dist/platform/bounded.d.ts +0 -8
- package/dist/platform/bounded.js +0 -8
- package/dist/platform/events.d.ts +0 -18
- package/dist/platform/events.js +3 -22
- package/dist/platform/hub.d.ts +0 -113
- package/dist/platform/hub.js +1 -76
- package/dist/platform/json.d.ts +0 -3
- package/dist/platform/json.js +1 -3
- package/dist/platform/storage.d.ts +0 -7
- package/dist/platform/storage.js +0 -6
- package/dist/platform/version.d.ts +0 -9
- package/dist/platform/version.js +0 -8
- package/dist/rpc.js +0 -1
- package/dist/scheduling/index.d.ts +0 -1
- package/dist/scheduling/index.js +0 -1
- package/dist/scheduling/later.d.ts +0 -11
- package/dist/scheduling/later.js +0 -14
- package/dist/scheduling/text.d.ts +0 -1
- package/dist/scheduling/text.js +0 -1
- package/dist/scheduling/tools.d.ts +0 -1
- package/dist/scheduling/tools.js +1 -5
- package/dist/skill.d.ts +1 -9
- package/dist/skill.js +4 -12
- package/dist/tree/cleanup.d.ts +0 -20
- package/dist/tree/cleanup.js +0 -26
- package/dist/tree/context.d.ts +0 -5
- package/dist/tree/context.js +0 -8
- package/dist/tree/domain.d.ts +0 -64
- package/dist/tree/domain.js +7 -45
- package/dist/tree/events.d.ts +0 -36
- package/dist/tree/events.js +4 -49
- package/dist/tree/forget.d.ts +0 -15
- package/dist/tree/forget.js +0 -18
- package/dist/tree/group.d.ts +0 -22
- package/dist/tree/group.js +1 -37
- package/dist/tree/index.d.ts +2 -2
- package/dist/tree/index.js +1 -3
- package/dist/tree/limits.d.ts +0 -4
- package/dist/tree/limits.js +1 -12
- package/dist/tree/mascot.d.ts +0 -25
- package/dist/tree/mascot.js +1 -32
- package/dist/tree/model.d.ts +0 -5
- package/dist/tree/model.js +2 -15
- package/dist/tree/nudge.d.ts +0 -11
- package/dist/tree/nudge.js +3 -16
- package/dist/tree/panel.d.ts +0 -8
- package/dist/tree/panel.js +3 -16
- package/dist/tree/ports.d.ts +2 -138
- package/dist/tree/report.d.ts +0 -10
- package/dist/tree/report.js +0 -11
- package/dist/tree/roster.d.ts +0 -21
- package/dist/tree/roster.js +0 -21
- package/dist/tree/rpc-handlers.d.ts +6 -1
- package/dist/tree/rpc-handlers.js +0 -5
- package/dist/tree/rpc.d.ts +9 -109
- package/dist/tree/rpc.js +2 -38
- package/dist/tree/running.d.ts +0 -6
- package/dist/tree/running.js +2 -10
- package/dist/tree/send.d.ts +0 -4
- package/dist/tree/send.js +4 -31
- package/dist/tree/spawn.d.ts +0 -7
- package/dist/tree/spawn.js +6 -47
- package/dist/tree/standing.d.ts +0 -8
- package/dist/tree/standing.js +1 -11
- package/dist/tree/status.d.ts +0 -8
- package/dist/tree/status.js +1 -9
- package/dist/tree/stop.d.ts +2 -9
- package/dist/tree/stop.js +58 -25
- package/dist/tree/store.d.ts +0 -8
- package/dist/tree/store.js +4 -38
- package/dist/tree/subtree.d.ts +8 -26
- package/dist/tree/subtree.js +37 -35
- package/dist/tree/summary.d.ts +0 -4
- package/dist/tree/summary.js +0 -8
- package/dist/tree/text.d.ts +1 -44
- package/dist/tree/text.js +5 -65
- package/dist/tree/tools.d.ts +0 -6
- package/dist/tree/tools.js +4 -8
- package/dist/tree/tui.d.ts +0 -81
- package/dist/tree/wake.d.ts +0 -30
- package/dist/tree/wake.js +5 -39
- package/dist/tui.js +22 -8
- package/package.json +8 -9
- package/dist/webhook/index.d.ts +0 -5
- package/dist/webhook/index.js +0 -3
- package/dist/webhook/receiver.d.ts +0 -80
- package/dist/webhook/receiver.js +0 -339
- package/dist/webhook/subscriptions.d.ts +0 -29
- package/dist/webhook/subscriptions.js +0 -52
- package/dist/webhook/text.d.ts +0 -23
- package/dist/webhook/text.js +0 -82
- package/dist/webhook/tools.d.ts +0 -19
- package/dist/webhook/tools.js +0 -46
|
@@ -6,10 +6,6 @@ import { closingSoon } from "./lifecycle.js";
|
|
|
6
6
|
import { choices, isDismissal, normalize, promptsOf, settledIn, withdrawnResult, wording, } from "./pure.js";
|
|
7
7
|
import { background, run, within } from "./runtime.js";
|
|
8
8
|
import { anyLoaded, isPassing, keyOf, QUESTION_TOOL, shared, storedOf } from "./shared.js";
|
|
9
|
-
/**
|
|
10
|
-
* Tells the top session about a question its closing location cut off, after the closing grace of
|
|
11
|
-
* `ports`, through any instance still loaded then; without one, the next load tells it.
|
|
12
|
-
*/
|
|
13
9
|
function tellLater(ports, question) {
|
|
14
10
|
const tellNow = Effect.suspend(() => {
|
|
15
11
|
const later = anyLoaded();
|
|
@@ -19,7 +15,6 @@ function tellLater(ports, question) {
|
|
|
19
15
|
});
|
|
20
16
|
void background(Effect.andThen(Effect.sleep(ports.timing.closingGraceMs), tellNow), (error) => ports.log(`courier question: could not tell ${question.top} about ${question.requestID}: ${String(error)}`));
|
|
21
17
|
}
|
|
22
|
-
/** Registers a question, stores it and tells the top session; false when storing or telling failed. */
|
|
23
18
|
async function relay(ports, question) {
|
|
24
19
|
shared.questions.set(question.requestID, question);
|
|
25
20
|
try {
|
|
@@ -31,55 +26,38 @@ async function relay(ports, question) {
|
|
|
31
26
|
return true;
|
|
32
27
|
}
|
|
33
28
|
catch (error) {
|
|
34
|
-
// The question stays registered and the call waits on: courier_children lists it, and an answer
|
|
35
|
-
// that comes anyway, through a link or courier_answer, still reaches the call.
|
|
36
29
|
ports.log(`courier question: could not tell ${question.top} about ${question.requestID}: ${String(error)}`);
|
|
37
30
|
return false;
|
|
38
31
|
}
|
|
39
32
|
}
|
|
40
|
-
/**
|
|
41
|
-
* After a relayed call ended: tidies up, and tells the top session if it was settled without it.
|
|
42
|
-
* `loaded` says whether the plugin instance that ran the call is still loaded.
|
|
43
|
-
*/
|
|
44
33
|
const settle = (ports, question, exit, loaded) => Effect.gen(function* () {
|
|
45
34
|
const unloaded = !loaded();
|
|
46
|
-
// A dismissal is held from now, alongside the wait below, so the grace is not spent waiting.
|
|
47
35
|
const dismissed = Exit.isFailure(exit) && isDismissal(exit.cause);
|
|
48
36
|
const held = dismissed && !unloaded ? yield* Effect.forkChild(closingSoon(ports, loaded, question.directory), { startImmediately: true }) : undefined;
|
|
49
|
-
// The top session is told first, for a while only, so what follows neither overtakes the notice
|
|
50
|
-
// nor waits forever on one that never returns; `told` is false when it was not told, or not yet.
|
|
51
37
|
const told = (yield* within(question.relaying, ports.timing.relayWaitMs)) === true;
|
|
52
|
-
// Answered by the top session.
|
|
53
38
|
if (Exit.isSuccess(exit) && exit.value.by === "top") {
|
|
54
39
|
if (shared.questions.get(question.requestID) === question)
|
|
55
40
|
shared.questions.delete(question.requestID);
|
|
56
41
|
return yield* Effect.promise(() => forget(ports, question));
|
|
57
42
|
}
|
|
58
|
-
// Never relayed, or dropped meanwhile.
|
|
59
43
|
if (shared.questions.get(question.requestID) !== question) {
|
|
60
44
|
return yield* Effect.promise(() => forget(ports, question));
|
|
61
45
|
}
|
|
62
|
-
//
|
|
63
|
-
// dismissal is held for a following shutdown or unload; the question stays, answered by message.
|
|
46
|
+
// a closing location withdraws its forms as if dismissed (api-notes)
|
|
64
47
|
const closing = unloaded || !loaded() || (held !== undefined && (yield* Fiber.join(held)));
|
|
65
48
|
if (Exit.isFailure(exit) && (closing || (!dismissed && Exit.hasInterrupts(exit))))
|
|
66
49
|
return closing ? tellLater(ports, question) : yield* Effect.promise(() => stopped(ports, question));
|
|
67
50
|
yield* Effect.promise(() => settledElsewhere(ports, question, settledIn(exit), told));
|
|
68
51
|
});
|
|
69
|
-
/** A relayed call cut off by its stopped turn: the top session is told now, unless it asks the person. */
|
|
70
52
|
async function stopped(ports, question) {
|
|
71
53
|
if (!question.link)
|
|
72
54
|
await tellCutOff(ports, question, "stopped");
|
|
73
55
|
}
|
|
74
|
-
/** A relayed call settled in the asking session: `told` says whether the top session was told about it. */
|
|
75
56
|
async function settledElsewhere(ports, question, how, told) {
|
|
76
|
-
// Its record goes before its registration, so an answer that finds the question gone does not
|
|
77
|
-
// find the record still there and send it on.
|
|
78
57
|
await forget(ports, question);
|
|
79
58
|
shared.questions.delete(question.requestID);
|
|
80
59
|
if (question.link)
|
|
81
60
|
return question.link(how);
|
|
82
|
-
// A top session never told about the question is not told that it is settled either.
|
|
83
61
|
if (!told)
|
|
84
62
|
return;
|
|
85
63
|
await tell(ports, question, questionSettledNotice(question, how), {
|
|
@@ -87,19 +65,11 @@ async function settledElsewhere(ports, question, how, told) {
|
|
|
87
65
|
request: question.requestID,
|
|
88
66
|
});
|
|
89
67
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Starts settling a relayed call that ended, kept on the question until it is through: an answer
|
|
92
|
-
* the call did not take waits for it.
|
|
93
|
-
*/
|
|
94
68
|
function startSettling(ports, question, exit, loaded) {
|
|
95
69
|
question.settling = background(settle(ports, question, exit, loaded), (error) => ports.log(`courier question: could not settle ${question.requestID}: ${String(error)}`)).finally(() => {
|
|
96
70
|
question.settling = undefined;
|
|
97
71
|
});
|
|
98
72
|
}
|
|
99
|
-
/**
|
|
100
|
-
* A spawned session's question call: once its form is shown the top session is told, and whichever
|
|
101
|
-
* answer comes first ends the call, the other side's question withdrawn. Other calls run unchanged.
|
|
102
|
-
*/
|
|
103
73
|
function asking(ports, directory, original) {
|
|
104
74
|
return (input, context) => Effect.gen(function* () {
|
|
105
75
|
const current = ports();
|
|
@@ -108,10 +78,8 @@ function asking(ports, directory, original) {
|
|
|
108
78
|
return yield* original(input, context);
|
|
109
79
|
const byTop = Deferred.makeUnsafe();
|
|
110
80
|
const accepted = Deferred.makeUnsafe();
|
|
111
|
-
// For a copy of the plugin that answers through `call`, which takes promises.
|
|
112
81
|
let acceptancePromise;
|
|
113
82
|
const acceptance = () => (acceptancePromise ??= Effect.runPromise(Deferred.await(accepted)));
|
|
114
|
-
// Handed one outcome only: a second, while the first still ends the call, is not taken.
|
|
115
83
|
let handed = false;
|
|
116
84
|
const top = chain.at(-1).parentID;
|
|
117
85
|
const question = {
|
|
@@ -122,7 +90,7 @@ function asking(ports, directory, original) {
|
|
|
122
90
|
...(chain.length > 1 ? { startedBy: chain[0].parentID } : {}),
|
|
123
91
|
questions: promptsOf(input),
|
|
124
92
|
askedAt: current.now(),
|
|
125
|
-
//
|
|
93
|
+
// a native child's form shows in its native root's view (api-notes)
|
|
126
94
|
...(nativeRootOf(chain) === top ? { native: true } : {}),
|
|
127
95
|
directory,
|
|
128
96
|
call: (outcome) => {
|
|
@@ -133,30 +101,22 @@ function asking(ports, directory, original) {
|
|
|
133
101
|
return acceptance();
|
|
134
102
|
},
|
|
135
103
|
};
|
|
136
|
-
//
|
|
137
|
-
// refused there never reaches the top session.
|
|
104
|
+
// form.created comes only once the call's permission check passed
|
|
138
105
|
const key = `${context.sessionID} ${context.id}`;
|
|
139
106
|
const loaded = () => ports() === current;
|
|
140
107
|
const shown = Deferred.makeUnsafe();
|
|
141
|
-
// Called as a plain function, by this copy's watcher or another copy's.
|
|
142
108
|
shared.shown.set(key, () => Deferred.doneUnsafe(shown, Exit.void));
|
|
143
109
|
const fromTop = Effect.gen(function* () {
|
|
144
110
|
yield* Deferred.await(shown);
|
|
145
|
-
// Started, not waited for: a store or a notice that hangs must not keep the top session's
|
|
146
|
-
// answer from the call. Kept on the question, so settle can wait for it.
|
|
147
111
|
question.relaying = relay(current, question);
|
|
148
112
|
return { by: "top", outcome: yield* Deferred.await(byTop) };
|
|
149
113
|
});
|
|
150
|
-
const ended = yield* Effect.raceFirst(original(input, context).pipe(Effect.map((result) => ({ by: "child", result }))), fromTop).pipe(
|
|
151
|
-
// Not waited for, so storing or a notice that hangs cannot hold the call up.
|
|
152
|
-
Effect.onExit((exit) => Effect.sync(() => {
|
|
114
|
+
const ended = yield* Effect.raceFirst(original(input, context).pipe(Effect.map((result) => ({ by: "child", result }))), fromTop).pipe(Effect.onExit((exit) => Effect.sync(() => {
|
|
153
115
|
shared.shown.delete(key);
|
|
154
|
-
// The call no longer waits, whatever settle makes of it: an answer from now on waits for settle.
|
|
155
116
|
const byTopSession = Exit.isSuccess(exit) && exit.value.by === "top";
|
|
156
117
|
if (!byTopSession)
|
|
157
118
|
question.call = undefined;
|
|
158
119
|
startSettling(current, question, exit, loaded);
|
|
159
|
-
// After settling is set, which an answer the call did not take then waits for.
|
|
160
120
|
Deferred.doneUnsafe(accepted, Exit.succeed(byTopSession));
|
|
161
121
|
})));
|
|
162
122
|
if (ended.by === "child")
|
|
@@ -175,12 +135,7 @@ function asking(ports, directory, original) {
|
|
|
175
135
|
};
|
|
176
136
|
});
|
|
177
137
|
}
|
|
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
138
|
const linkable = (question, sessionID) => question.top === sessionID && !question.link && !isPassing(question.requestID) && !(question.native && question.call);
|
|
180
|
-
/**
|
|
181
|
-
* The waiting question a top session's call asks again, the oldest if several: the same question with
|
|
182
|
-
* the same choices, in any order. Choices alone would let an unrelated yes-or-no answer a child's.
|
|
183
|
-
*/
|
|
184
139
|
function linkFor(sessionID, questions) {
|
|
185
140
|
const asked = { choices: choices(questions), wording: wording(questions) };
|
|
186
141
|
return [...shared.questions.values()]
|
|
@@ -189,10 +144,6 @@ function linkFor(sessionID, questions) {
|
|
|
189
144
|
wording(question.questions) === asked.wording)
|
|
190
145
|
.sort((a, b) => a.askedAt - b.askedAt)[0];
|
|
191
146
|
}
|
|
192
|
-
/**
|
|
193
|
-
* The result of a top session's question call that no waiting question was linked to: when one waits
|
|
194
|
-
* with the same choices, reworded or asked earlier, it says how to pass the answers on.
|
|
195
|
-
*/
|
|
196
147
|
function unlinkedResult(result, sessionID, questions) {
|
|
197
148
|
if (typeof result.content !== "string")
|
|
198
149
|
return result;
|
|
@@ -202,10 +153,6 @@ function unlinkedResult(result, sessionID, questions) {
|
|
|
202
153
|
return result;
|
|
203
154
|
return { ...result, content: `${result.content}\n${unlinkedNote(similar)}` };
|
|
204
155
|
}
|
|
205
|
-
/**
|
|
206
|
-
* After a linked call failed with its location still loaded: a dismissal by the person dismisses the
|
|
207
|
-
* child's question too, or is logged if answered meanwhile; a stop while cut off tells the top.
|
|
208
|
-
*/
|
|
209
156
|
const linkedCallFailed = (ports, linked, cause, loaded, directory) => Effect.gen(function* () {
|
|
210
157
|
if (isDismissal(cause)) {
|
|
211
158
|
if (yield* closingSoon(ports, loaded, directory))
|
|
@@ -216,12 +163,7 @@ const linkedCallFailed = (ports, linked, cause, loaded, directory) => Effect.gen
|
|
|
216
163
|
else if (!linked.call && shared.questions.get(linked.requestID) === linked)
|
|
217
164
|
yield* Effect.promise(() => tellCutOff(ports, linked, "stopped"));
|
|
218
165
|
});
|
|
219
|
-
/** Logs that what happened to a linked call could not be passed on. */
|
|
220
166
|
const notPassedOn = (ports, linked) => (error) => ports.log(`courier question: could not pass on what happened to ${linked.requestID}: ${String(error)}`);
|
|
221
|
-
/**
|
|
222
|
-
* The question call of a session that a waiting question was relayed to, asking the person the
|
|
223
|
-
* same: what they choose is passed on, and the call is withdrawn if the question is settled first.
|
|
224
|
-
*/
|
|
225
167
|
function linking(ports, directory, ask) {
|
|
226
168
|
return (input, context) => Effect.gen(function* () {
|
|
227
169
|
const current = ports();
|
|
@@ -233,16 +175,14 @@ function linking(ports, directory, ask) {
|
|
|
233
175
|
}
|
|
234
176
|
const loaded = () => ports() === current;
|
|
235
177
|
const elsewhere = Deferred.makeUnsafe();
|
|
236
|
-
// Called as a plain function, by this copy or another one.
|
|
237
178
|
const settled = (how) => void Deferred.doneUnsafe(elsewhere, Exit.succeed(how));
|
|
238
179
|
linked.link = settled;
|
|
239
180
|
const ended = yield* Effect.raceFirst(ask(input, context).pipe(Effect.map((result) => ({ by: "person", result }))), Deferred.await(elsewhere).pipe(Effect.map((how) => ({ by: "elsewhere", how })))).pipe(Effect.onExit((exit) => Effect.sync(() => {
|
|
240
181
|
if (linked.link === settled)
|
|
241
182
|
linked.link = undefined;
|
|
242
|
-
//
|
|
183
|
+
// unloaded: OpenCode withdrew the form as it closed the location
|
|
243
184
|
if (Exit.isSuccess(exit) || !loaded())
|
|
244
185
|
return;
|
|
245
|
-
// Not waited for, as in asking.
|
|
246
186
|
void background(linkedCallFailed(current, linked, exit.cause, loaded, directory), notPassedOn(current, linked));
|
|
247
187
|
})));
|
|
248
188
|
if (ended.by === "elsewhere")
|
|
@@ -263,9 +203,7 @@ function linking(ports, directory, ask) {
|
|
|
263
203
|
};
|
|
264
204
|
});
|
|
265
205
|
}
|
|
266
|
-
/** Wraps OpenCode's question tool for the relay. A tool by that id that is missing is left alone. */
|
|
267
206
|
export function relayQuestions(host, ports) {
|
|
268
|
-
// The wrapped tool runs the calls of the host's location: its shutdown is what withdraws their forms.
|
|
269
207
|
const { directory } = host.location;
|
|
270
208
|
return host.tool.transform((editor) => {
|
|
271
209
|
editor.update(QUESTION_TOOL, (tool) => {
|
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { Clock, Effect } from "effect";
|
|
2
|
-
/**
|
|
3
|
-
* Runs a relay effect in its own fiber by the relay's clock (`ports.now`): the promise settles as the
|
|
4
|
-
* effect ends, rejecting on failure or interruption; interrupting the waiter does not stop the fiber.
|
|
5
|
-
*/
|
|
6
2
|
export declare function run<A, E>(effect: Effect.Effect<A, E>): Promise<A>;
|
|
7
|
-
/**
|
|
8
|
-
* Runs an effect of the relay in the background, as `run` does; the promise resolves once it ended,
|
|
9
|
-
* however it ended. A failure is logged with `failed`; an interruption, by a test ending, is not.
|
|
10
|
-
*/
|
|
11
3
|
export declare function background(effect: Effect.Effect<unknown, unknown>, failed: (error: unknown) => void): Promise<void>;
|
|
12
|
-
/** The promise's value, or undefined once `ms` have passed or it failed. */
|
|
13
4
|
export declare const within: <T>(promise: Promise<T> | undefined, ms: number) => Effect.Effect<T | undefined>;
|
|
14
|
-
/**
|
|
15
|
-
* For tests: the clock the relay runs by from now on, a `TestClock` whose time `ports.now` reads,
|
|
16
|
-
* or the system clock again with none.
|
|
17
|
-
*/
|
|
18
5
|
export declare function useClock(next?: Clock.Clock): void;
|
|
19
|
-
/** For tests: interrupts every fiber the relay started, and goes back to the system clock. */
|
|
20
6
|
export declare function stopRelay(): Promise<void>;
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { Cause, Clock, Duration, Effect, Exit, Fiber, Option, Scope } from "effect";
|
|
2
|
-
// What the relay's own fibers run with: this copy's state, not the hub's. A copy of the plugin
|
|
3
|
-
// loaded next to this one has its own, and they meet only in the shared fields of the hub.
|
|
4
2
|
const system = Effect.runSync(Clock.clockWith(Effect.succeed));
|
|
5
|
-
/** Largest delay a timer takes; a longer one would fire at once, so it is slept in parts. */
|
|
6
3
|
const MAX_TIMER_MS = 2_147_483_647;
|
|
7
|
-
/**
|
|
8
|
-
* The system clock, whose sleeps do not keep the process running: OpenCode exits without waiting
|
|
9
|
-
* for a dismissal held, a notice told later or an answer waiting its turn.
|
|
10
|
-
*/
|
|
11
4
|
const live = {
|
|
12
5
|
currentTimeMillisUnsafe: () => system.currentTimeMillisUnsafe(),
|
|
13
6
|
currentTimeMillis: system.currentTimeMillis,
|
|
@@ -25,47 +18,29 @@ function sleep(ms) {
|
|
|
25
18
|
const rest = ms > MAX_TIMER_MS ? Effect.suspend(() => sleep(ms - MAX_TIMER_MS)) : Effect.void;
|
|
26
19
|
return Effect.callback((resume) => {
|
|
27
20
|
const timer = setTimeout(() => resume(rest), Math.min(ms, MAX_TIMER_MS));
|
|
21
|
+
// unref'd: OpenCode exits without waiting for the relay's timers
|
|
28
22
|
timer.unref?.();
|
|
29
23
|
return Effect.sync(() => clearTimeout(timer));
|
|
30
24
|
});
|
|
31
25
|
}
|
|
32
26
|
let clock = live;
|
|
33
|
-
/** The fibers the relay starts, which run as long as the process unless a test ends them. */
|
|
34
27
|
let scope = Scope.makeUnsafe();
|
|
35
|
-
/**
|
|
36
|
-
* Starts an effect of the relay in a fiber of its own, by the relay's clock, in the relay's scope:
|
|
37
|
-
* at once, up to its first wait, as an async function would.
|
|
38
|
-
*/
|
|
39
28
|
const start = (effect) => Effect.runFork(Effect.provideService(effect, Clock.Clock, clock)).pipe(Fiber.runIn(scope));
|
|
40
|
-
/**
|
|
41
|
-
* Runs a relay effect in its own fiber by the relay's clock (`ports.now`): the promise settles as the
|
|
42
|
-
* effect ends, rejecting on failure or interruption; interrupting the waiter does not stop the fiber.
|
|
43
|
-
*/
|
|
44
29
|
export function run(effect) {
|
|
45
30
|
return Effect.runPromise(Fiber.join(start(effect)));
|
|
46
31
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Runs an effect of the relay in the background, as `run` does; the promise resolves once it ended,
|
|
49
|
-
* however it ended. A failure is logged with `failed`; an interruption, by a test ending, is not.
|
|
50
|
-
*/
|
|
51
32
|
export function background(effect, failed) {
|
|
52
33
|
return Effect.runPromise(Fiber.await(start(effect))).then((exit) => {
|
|
53
34
|
if (Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause))
|
|
54
35
|
failed(Cause.squash(exit.cause));
|
|
55
36
|
});
|
|
56
37
|
}
|
|
57
|
-
/** The promise's value, or undefined once `ms` have passed or it failed. */
|
|
58
38
|
export const within = (promise, ms) => promise
|
|
59
39
|
? Effect.promise(() => promise.then((value) => value, () => undefined)).pipe(Effect.timeoutOption(ms), Effect.map(Option.getOrUndefined))
|
|
60
40
|
: Effect.succeed(undefined);
|
|
61
|
-
/**
|
|
62
|
-
* For tests: the clock the relay runs by from now on, a `TestClock` whose time `ports.now` reads,
|
|
63
|
-
* or the system clock again with none.
|
|
64
|
-
*/
|
|
65
41
|
export function useClock(next) {
|
|
66
42
|
clock = next ?? live;
|
|
67
43
|
}
|
|
68
|
-
/** For tests: interrupts every fiber the relay started, and goes back to the system clock. */
|
|
69
44
|
export async function stopRelay() {
|
|
70
45
|
const ended = scope;
|
|
71
46
|
scope = Scope.makeUnsafe();
|
|
@@ -1,34 +1,17 @@
|
|
|
1
1
|
import type { Registry } from "../../platform/storage.js";
|
|
2
2
|
import type { Asked, Question, QuestionPorts, QuestionState } from "./types.js";
|
|
3
|
-
/** OpenCode's own question tool, which the relay wraps. */
|
|
4
3
|
export declare const QUESTION_TOOL = "question";
|
|
5
4
|
export declare const PREFIX = "question/";
|
|
6
|
-
/** At most this many questions are kept in storage; the oldest go first. */
|
|
7
5
|
export declare const MAX_STORED = 100;
|
|
8
|
-
/** A question as stored; `answered` marks one whose answer went out but that could not be dropped. */
|
|
9
6
|
export type Stored = Asked & {
|
|
10
7
|
readonly answered?: true;
|
|
11
8
|
};
|
|
12
9
|
export declare const storedOf: ({ directory: _directory, call: _call, link: _link, relaying: _relaying, settling: _settling, ...asked }: Question) => Asked;
|
|
13
10
|
export declare const isPassing: (requestID: string) => boolean;
|
|
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
11
|
export declare function questionState(registry?: Registry): QuestionState;
|
|
19
12
|
export declare const shared: QuestionState;
|
|
20
|
-
/**
|
|
21
|
-
* The ports of an instance still loaded, if any: a member of this copy's hub, or else one that a
|
|
22
|
-
* copy of another hub version, or a release before the hub, keeps in `loaded`.
|
|
23
|
-
*/
|
|
24
13
|
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
14
|
export declare function portsAt(location: object): () => QuestionPorts | undefined;
|
|
30
15
|
export declare const keyOf: (requestID: string) => string;
|
|
31
|
-
/** Whether a request id is a question's rather than a permission request's. */
|
|
32
16
|
export declare const isQuestion: (requestID: string) => boolean;
|
|
33
|
-
/** Adds to one of the shared bounded sets. */
|
|
34
17
|
export declare const claim: (set: Set<string>, value: string) => boolean;
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { addBounded } from "../../platform/bounded.js";
|
|
2
2
|
import { hub, memberAt, sharedState } from "../../platform/hub.js";
|
|
3
|
-
/** OpenCode's own question tool, which the relay wraps. */
|
|
4
3
|
export const QUESTION_TOOL = "question";
|
|
5
4
|
export const PREFIX = "question/";
|
|
6
|
-
/** At most this many questions are kept in storage; the oldest go first. */
|
|
7
5
|
export const MAX_STORED = 100;
|
|
8
6
|
export const storedOf = ({ directory: _directory, call: _call, link: _link, relaying: _relaying, settling: _settling, ...asked }) => asked;
|
|
9
7
|
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
8
|
export function questionState(registry) {
|
|
15
9
|
const state = sharedState("questions", () => ({}), registry);
|
|
16
10
|
state.questions ??= new Map();
|
|
@@ -25,28 +19,14 @@ export function questionState(registry) {
|
|
|
25
19
|
state.closingWaiters ??= new Set();
|
|
26
20
|
return state;
|
|
27
21
|
}
|
|
28
|
-
// Shared by every plugin instance in the process, since a child and the session it asks can be in
|
|
29
|
-
// different locations.
|
|
30
22
|
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
23
|
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
24
|
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
25
|
export function portsAt(location) {
|
|
43
26
|
const own = memberAt(location);
|
|
44
27
|
return () => (own && hub.members.has(own) ? portsOf(own) : undefined);
|
|
45
28
|
}
|
|
46
29
|
export const keyOf = (requestID) => `${PREFIX}${requestID}`;
|
|
47
|
-
/** Whether a request id is a question's rather than a permission request's. */
|
|
48
30
|
export const isQuestion = (requestID) => requestID.startsWith("question_");
|
|
49
|
-
/** How many noticed and answered questions are remembered. */
|
|
50
31
|
const REMEMBERED = 1_000;
|
|
51
|
-
/** Adds to one of the shared bounded sets. */
|
|
52
32
|
export const claim = (set, value) => addBounded(set, value, REMEMBERED);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode/plugin";
|
|
2
2
|
import type { Storage } from "../../platform/storage.js";
|
|
3
3
|
type Context = Plugin.Context;
|
|
4
|
-
/** One question of OpenCode's question tool, as the asking model wrote it. */
|
|
5
4
|
export interface Prompt {
|
|
6
5
|
readonly question: string;
|
|
7
6
|
readonly header: string;
|
|
@@ -11,26 +10,17 @@ export interface Prompt {
|
|
|
11
10
|
}>;
|
|
12
11
|
readonly multiple?: boolean;
|
|
13
12
|
}
|
|
14
|
-
/** The answers to a question call, as the question tool returns them: one list per question. */
|
|
15
13
|
export type Answers = string[][];
|
|
16
|
-
/** A question a spawned session asked with its question tool, stored until it is answered. */
|
|
17
14
|
export interface Asked {
|
|
18
15
|
readonly requestID: string;
|
|
19
16
|
readonly sessionID: string;
|
|
20
|
-
/** The session told, at the top of the sessions started with courier_spawn; it alone answers. */
|
|
21
17
|
readonly top: string;
|
|
22
18
|
readonly title: string;
|
|
23
|
-
/** The session that started the asking one, when that is not the top session itself. */
|
|
24
19
|
readonly startedBy?: string;
|
|
25
20
|
readonly questions: ReadonlyArray<Prompt>;
|
|
26
21
|
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
22
|
readonly native?: true;
|
|
29
23
|
}
|
|
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
24
|
export type Elsewhere = {
|
|
35
25
|
readonly by: "child";
|
|
36
26
|
readonly answers: Answers;
|
|
@@ -41,39 +31,22 @@ export type Elsewhere = {
|
|
|
41
31
|
readonly by: "failed";
|
|
42
32
|
readonly error: string;
|
|
43
33
|
};
|
|
44
|
-
/** What the top session's answer does to a call that waits: answers it, or dismisses it. */
|
|
45
34
|
export type Outcome = {
|
|
46
35
|
readonly answers: Answers;
|
|
47
36
|
} | {
|
|
48
37
|
readonly dismissed: true;
|
|
49
38
|
};
|
|
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
39
|
export type Withdrawal = Elsewhere | {
|
|
55
40
|
readonly by: "top";
|
|
56
41
|
readonly outcome: Outcome;
|
|
57
42
|
};
|
|
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
43
|
export interface Question extends Asked {
|
|
63
|
-
/** The directory of the location the call runs in, whose shutdown withdraws its form. */
|
|
64
44
|
directory?: string;
|
|
65
|
-
/** Hands the waiting call the top session's outcome; true once the call has ended with it. */
|
|
66
45
|
call?: (outcome: Outcome) => Promise<boolean>;
|
|
67
46
|
link?: (how: Withdrawal) => void;
|
|
68
|
-
/** Storing the question and telling the top session, while that is under way. */
|
|
69
47
|
relaying?: Promise<boolean>;
|
|
70
|
-
/** Working out what became of it once its call ended without the top session's answer. */
|
|
71
48
|
settling?: Promise<void>;
|
|
72
49
|
}
|
|
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
50
|
export interface QuestionTiming {
|
|
78
51
|
readonly closingGraceMs: number;
|
|
79
52
|
readonly passingWaitMs: number;
|
|
@@ -83,39 +56,22 @@ export interface QuestionTiming {
|
|
|
83
56
|
export interface QuestionPorts {
|
|
84
57
|
readonly storage: Storage;
|
|
85
58
|
readonly session: Pick<Context["session"], "synthetic">;
|
|
86
|
-
/** The directory of the instance's location. */
|
|
87
59
|
readonly directory: string;
|
|
88
|
-
/** The clock, in epoch milliseconds, as OpenCode's location shutdowns are recorded. */
|
|
89
60
|
readonly now: () => number;
|
|
90
61
|
readonly timing: QuestionTiming;
|
|
91
62
|
readonly newID: () => string;
|
|
92
63
|
readonly log: (message: string) => void;
|
|
93
64
|
}
|
|
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
65
|
export interface QuestionState {
|
|
99
66
|
readonly questions: Map<string, Question>;
|
|
100
67
|
readonly noticed: Set<string>;
|
|
101
68
|
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
69
|
readonly passing: Map<string, Promise<unknown>>;
|
|
104
70
|
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
71
|
readonly loaded: Set<QuestionPorts>;
|
|
110
|
-
/** How many instances follow OpenCode's events now, and whether any has since the process started. */
|
|
111
72
|
following: number;
|
|
112
73
|
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
74
|
readonly shutdowns: Map<string, number>;
|
|
118
|
-
/** Woken when a location shuts down or an instance unloads: dismissals held to see whether one follows. */
|
|
119
75
|
readonly closingWaiters: Set<() => void>;
|
|
120
76
|
}
|
|
121
77
|
export {};
|
|
@@ -2,23 +2,16 @@ import type { Plugin } from "@opencode/plugin";
|
|
|
2
2
|
import { type Permissions } from "./permission.js";
|
|
3
3
|
import { type QuestionPorts } from "./question/index.js";
|
|
4
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
5
|
export interface PersonRpcPorts {
|
|
7
6
|
readonly questions: QuestionPorts;
|
|
8
7
|
readonly permissions: () => Iterable<Permissions>;
|
|
9
8
|
}
|
|
10
|
-
/**
|
|
11
|
-
* The permission requests and questions waiting in the courier tree a session is in, the top's own requests first, read from the
|
|
12
|
-
* roster from the top of its lineage down, so each session of the tree sees the same inbox.
|
|
13
|
-
*/
|
|
14
9
|
export declare function asks(ports: PersonRpcPorts, sessionID: string): Promise<Asks>;
|
|
15
|
-
/** 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`. */
|
|
16
10
|
export declare function answer(ports: PersonRpcPorts, input: {
|
|
17
11
|
sessionID: string;
|
|
18
12
|
requestID: string;
|
|
19
13
|
answers: ReadonlyArray<ReadonlyArray<string>>;
|
|
20
14
|
}): Promise<Answered>;
|
|
21
|
-
/** Serves the person's RPC at the plugin's location; disposed with the plugin. */
|
|
22
15
|
export declare function registerPersonRpc(rpc: Pick<Plugin.Context["rpc"], "register">, ports: PersonRpcPorts): Promise<import("@opencode/plugin/promise/rpc").RpcRegistration<{
|
|
23
16
|
readonly id: "courier-person";
|
|
24
17
|
readonly methods: {
|
|
@@ -2,10 +2,6 @@ import { lineage, subtree } from "../tree/index.js";
|
|
|
2
2
|
import { ownRequests, pendingOf } from "./permission.js";
|
|
3
3
|
import { answerForPerson, pendingQuestions } from "./question/index.js";
|
|
4
4
|
import { personRpc } from "./rpc.js";
|
|
5
|
-
/**
|
|
6
|
-
* The permission requests and questions waiting in the courier tree a session is in, the top's own requests first, read from the
|
|
7
|
-
* roster from the top of its lineage down, so each session of the tree sees the same inbox.
|
|
8
|
-
*/
|
|
9
5
|
export async function asks(ports, sessionID) {
|
|
10
6
|
const { storage } = ports.questions;
|
|
11
7
|
const top = (await lineage(storage, sessionID)).at(-1)?.parentID ?? sessionID;
|
|
@@ -53,7 +49,6 @@ export async function asks(ports, sessionID) {
|
|
|
53
49
|
}))),
|
|
54
50
|
};
|
|
55
51
|
}
|
|
56
|
-
/** 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`. */
|
|
57
52
|
export async function answer(ports, input) {
|
|
58
53
|
try {
|
|
59
54
|
const result = await answerForPerson(ports.questions, input);
|
|
@@ -63,7 +58,6 @@ export async function answer(ports, input) {
|
|
|
63
58
|
return { answered: false, problem: error instanceof Error ? error.message : String(error) };
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
|
-
/** Serves the person's RPC at the plugin's location; disposed with the plugin. */
|
|
67
61
|
export function registerPersonRpc(rpc, ports) {
|
|
68
62
|
return rpc.register(personRpc, {
|
|
69
63
|
asks: ({ sessionID }) => asks(ports, sessionID),
|
package/dist/person/rpc.d.ts
CHANGED
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
/** A session of the tree, as the inbox names where an ask comes from. */
|
|
3
2
|
declare const Session: Schema.Struct<{
|
|
4
3
|
readonly sessionID: Schema.String;
|
|
5
4
|
readonly title: Schema.String;
|
|
6
5
|
readonly parentID: Schema.String;
|
|
7
|
-
/** A child of the tree's top session is at depth 1. */
|
|
8
6
|
readonly depth: Schema.Int;
|
|
9
7
|
readonly group: Schema.optionalKey<Schema.String>;
|
|
10
8
|
}>;
|
|
11
|
-
/** A permission request a session of the tree, the top among them, waits on. */
|
|
12
9
|
declare const Permission: Schema.Struct<{
|
|
13
10
|
readonly sessionID: Schema.String;
|
|
14
11
|
readonly requestID: Schema.String;
|
|
15
12
|
readonly action: Schema.String;
|
|
16
13
|
readonly resources: Schema.$Array<Schema.String>;
|
|
17
|
-
/** What "always" would allow from now on; absent when it is not offered. */
|
|
18
14
|
readonly save: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
19
|
-
/** The request below that allowing this one passes on: the top's courier_answer prompt. */
|
|
20
15
|
readonly passesOn: Schema.optionalKey<Schema.Struct<{
|
|
21
16
|
readonly sessionID: Schema.String;
|
|
22
17
|
readonly requestID: Schema.String;
|
|
23
18
|
}>>;
|
|
24
19
|
}>;
|
|
25
|
-
/** A question a session of the tree waits on; `stopped` when its call was cut off and the answer goes as a message. */
|
|
26
20
|
declare const Question: Schema.Struct<{
|
|
27
21
|
readonly sessionID: Schema.String;
|
|
28
22
|
readonly requestID: Schema.String;
|
|
@@ -37,28 +31,21 @@ declare const Question: Schema.Struct<{
|
|
|
37
31
|
}>>;
|
|
38
32
|
readonly stopped: Schema.optionalKey<Schema.Boolean>;
|
|
39
33
|
}>;
|
|
40
|
-
/** What the sessions of a courier tree wait on the person for, but forms, which the TUI lists itself. */
|
|
41
34
|
declare const Asks: Schema.Struct<{
|
|
42
|
-
/** The session at the top of the tree, which the sessions below were started from. */
|
|
43
35
|
readonly top: Schema.String;
|
|
44
|
-
/** The sessions below the top, in the order a tree is drawn. */
|
|
45
36
|
readonly sessions: Schema.$Array<Schema.Struct<{
|
|
46
37
|
readonly sessionID: Schema.String;
|
|
47
38
|
readonly title: Schema.String;
|
|
48
39
|
readonly parentID: Schema.String;
|
|
49
|
-
/** A child of the tree's top session is at depth 1. */
|
|
50
40
|
readonly depth: Schema.Int;
|
|
51
41
|
readonly group: Schema.optionalKey<Schema.String>;
|
|
52
42
|
}>>;
|
|
53
|
-
/** The top session's own requests first, then those of the sessions below. */
|
|
54
43
|
readonly permissions: Schema.$Array<Schema.Struct<{
|
|
55
44
|
readonly sessionID: Schema.String;
|
|
56
45
|
readonly requestID: Schema.String;
|
|
57
46
|
readonly action: Schema.String;
|
|
58
47
|
readonly resources: Schema.$Array<Schema.String>;
|
|
59
|
-
/** What "always" would allow from now on; absent when it is not offered. */
|
|
60
48
|
readonly save: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
61
|
-
/** The request below that allowing this one passes on: the top's courier_answer prompt. */
|
|
62
49
|
readonly passesOn: Schema.optionalKey<Schema.Struct<{
|
|
63
50
|
readonly sessionID: Schema.String;
|
|
64
51
|
readonly requestID: Schema.String;
|
|
@@ -83,7 +70,6 @@ export type Asks = typeof Asks.Type;
|
|
|
83
70
|
export type AskedQuestion = typeof Question.Type;
|
|
84
71
|
export type AskedPermission = typeof Permission.Type;
|
|
85
72
|
export type TreeSession = typeof Session.Type;
|
|
86
|
-
/** What answering a question did; `problem` says why nothing was passed on, when it was refused. */
|
|
87
73
|
declare const Answered: Schema.Struct<{
|
|
88
74
|
readonly answered: Schema.Boolean;
|
|
89
75
|
readonly by: Schema.optionalKey<Schema.Literals<readonly ["result", "message"]>>;
|
|
@@ -170,26 +156,20 @@ export declare const personRpc: {
|
|
|
170
156
|
readonly stopped?: boolean | undefined;
|
|
171
157
|
}[];
|
|
172
158
|
}> & Schema.Struct<{
|
|
173
|
-
/** The session at the top of the tree, which the sessions below were started from. */
|
|
174
159
|
readonly top: Schema.String;
|
|
175
|
-
/** The sessions below the top, in the order a tree is drawn. */
|
|
176
160
|
readonly sessions: Schema.$Array<Schema.Struct<{
|
|
177
161
|
readonly sessionID: Schema.String;
|
|
178
162
|
readonly title: Schema.String;
|
|
179
163
|
readonly parentID: Schema.String;
|
|
180
|
-
/** A child of the tree's top session is at depth 1. */
|
|
181
164
|
readonly depth: Schema.Int;
|
|
182
165
|
readonly group: Schema.optionalKey<Schema.String>;
|
|
183
166
|
}>>;
|
|
184
|
-
/** The top session's own requests first, then those of the sessions below. */
|
|
185
167
|
readonly permissions: Schema.$Array<Schema.Struct<{
|
|
186
168
|
readonly sessionID: Schema.String;
|
|
187
169
|
readonly requestID: Schema.String;
|
|
188
170
|
readonly action: Schema.String;
|
|
189
171
|
readonly resources: Schema.$Array<Schema.String>;
|
|
190
|
-
/** What "always" would allow from now on; absent when it is not offered. */
|
|
191
172
|
readonly save: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
192
|
-
/** The request below that allowing this one passes on: the top's courier_answer prompt. */
|
|
193
173
|
readonly passesOn: Schema.optionalKey<Schema.Struct<{
|
|
194
174
|
readonly sessionID: Schema.String;
|
|
195
175
|
readonly requestID: Schema.String;
|