@reserendipity/dsh-superagent 0.0.0-stage → 0.1.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 +21 -0
- package/NOTICE +50 -0
- package/README.md +206 -2
- package/cordis.patch.yml +214 -0
- package/examples/profile.cordis.patch.yml +31 -0
- package/lib/client.js +7695 -0
- package/lib/feishu-Dvj-1eJf.js +1325 -0
- package/lib/feishu-watch.d.ts +125 -0
- package/lib/feishu-watch.js +2 -0
- package/lib/github-pull-DRiuuSs8.js +597 -0
- package/lib/github-watch.d.ts +2 -0
- package/lib/github-watch.js +836 -0
- package/lib/host-BIWKSB5j.js +124 -0
- package/lib/inbox-watch.d.ts +43 -0
- package/lib/inbox-watch.js +343 -0
- package/lib/index.d.ts +369 -0
- package/lib/index.js +13654 -0
- package/lib/instance-status-Cqaju-dw.js +87 -0
- package/lib/instances-view.d.ts +164 -0
- package/lib/instances-view.js +258 -0
- package/lib/intake-TNIkqilh.d.ts +735 -0
- package/lib/kind--8woUEex.d.ts +1019 -0
- package/lib/poll-Bm5EYA2c.js +819 -0
- package/lib/schemas-MhNERx8d.js +4484 -0
- package/lib/schemas-lx-F2nnt.d.ts +2134 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +159 -4
- package/prompts/admission.md +68 -0
- package/prompts/cli.md +17 -0
- package/prompts/dispatch.md +59 -0
- package/prompts/handoff-orchestrator.md +4 -0
- package/prompts/handoff-worker.md +7 -0
- package/prompts/handoff.md +6 -0
- package/prompts/orchestrator.md +16 -0
- package/prompts/presets/brief/communication.md +4 -0
- package/prompts/presets/brief/delivery.md +3 -0
- package/prompts/presets/brief/scope.md +4 -0
- package/prompts/presets/evidence/communication.md +6 -0
- package/prompts/presets/evidence/delivery.md +4 -0
- package/prompts/presets/evidence/scope.md +5 -0
- package/prompts/presets/result/communication.md +4 -0
- package/prompts/presets/result/delivery.md +3 -0
- package/prompts/presets/result/scope.md +5 -0
- package/prompts/replies.md +35 -0
- package/prompts/shared.md +9 -0
- package/prompts/subscriptions.md +13 -0
- package/prompts/team/communication.md +101 -0
- package/prompts/team/delivery.md +35 -0
- package/prompts/team/scope.md +26 -0
- package/prompts/work.md +49 -0
- package/prompts/worker.md +10 -0
- package/schema/declarative-settings.example.json +26 -0
- package/schema/declarative-settings.schema.json +348 -0
- package/skills/README.md +14 -0
- package/skills/dsh-superagent-feishu/SKILL.md +12 -0
- package/skills/dsh-superagent-github/SKILL.md +14 -0
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { Volatile } from "@deepseek-ai/cordis";
|
|
2
|
+
import s from "@deepseek-ai/schemastery";
|
|
3
|
+
import "@deepseek-ai/dsh-subprocess";
|
|
4
|
+
//#region src/deployment.d.ts
|
|
5
|
+
/** Plugin configuration of the dsh-superagent entry. Volatile fields apply without a restart because dsh-superagent reads them on each use; changing `stateDir` remounts the plugin. */
|
|
6
|
+
declare const Config: s<Schemastery.ObjectS<NoInfer<{
|
|
7
|
+
stateDir: s<string, string, "defined">;
|
|
8
|
+
configPath: s<string, string, "plain">;
|
|
9
|
+
promptMaxChars: s<number, number, "volatile-defined">;
|
|
10
|
+
admissionContextMaxPages: s<number, number, "volatile-defined">;
|
|
11
|
+
admissionContextTimeoutMs: s<number, number, "volatile-defined">;
|
|
12
|
+
mode: s<"local" | "github", "local" | "github", "defined">;
|
|
13
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
14
|
+
stateDir: s<string, string, "defined">;
|
|
15
|
+
configPath: s<string, string, "plain">;
|
|
16
|
+
promptMaxChars: s<number, number, "volatile-defined">;
|
|
17
|
+
admissionContextMaxPages: s<number, number, "volatile-defined">;
|
|
18
|
+
admissionContextTimeoutMs: s<number, number, "volatile-defined">;
|
|
19
|
+
mode: s<"local" | "github", "local" | "github", "defined">;
|
|
20
|
+
}>>, "plain">;
|
|
21
|
+
type DeploymentConfig = ReturnType<typeof Config>;
|
|
22
|
+
interface Machine {
|
|
23
|
+
stateDir: string;
|
|
24
|
+
/** `github` polls a repository; `local` runs without any GitHub Source. */
|
|
25
|
+
mode: 'github' | 'local';
|
|
26
|
+
/** Declarative settings file applied at startup, when the deployment names one. */
|
|
27
|
+
configPath?: string;
|
|
28
|
+
promptMaxChars: Volatile<number>;
|
|
29
|
+
admissionContextMaxPages: Volatile<number>;
|
|
30
|
+
admissionContextTimeoutMs: Volatile<number>;
|
|
31
|
+
}
|
|
32
|
+
interface Deployment extends Machine {
|
|
33
|
+
repository: string;
|
|
34
|
+
github: {
|
|
35
|
+
cli: string;
|
|
36
|
+
login: string;
|
|
37
|
+
noreply: string;
|
|
38
|
+
};
|
|
39
|
+
feishu?: FeishuBot;
|
|
40
|
+
}
|
|
41
|
+
interface FeishuBot {
|
|
42
|
+
cli: string;
|
|
43
|
+
appId: string;
|
|
44
|
+
brand: 'feishu' | 'lark';
|
|
45
|
+
botId: string;
|
|
46
|
+
name: string;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/core/reasons.d.ts
|
|
50
|
+
/**
|
|
51
|
+
* Structured reason codes and templates for decisions, filtered triggers, and ended assignments.
|
|
52
|
+
* Policy gates and source scopes may also emit their own literal reason codes.
|
|
53
|
+
*/
|
|
54
|
+
declare const REASONS: {
|
|
55
|
+
readonly said: "{text}";
|
|
56
|
+
readonly timeout: "Assessment timed out";
|
|
57
|
+
readonly policyChanged: "Policy changed during assessment";
|
|
58
|
+
readonly noAdmissible: "No admissible trigger";
|
|
59
|
+
readonly startedTicket: "Already started ticket {ticket}";
|
|
60
|
+
readonly acceptedBefore: "Trigger {trigger} was accepted before";
|
|
61
|
+
readonly routed: "Routed to ticket {ticket}";
|
|
62
|
+
readonly explicitRouted: "Explicit request from an allowed sender routed by the orchestrator";
|
|
63
|
+
readonly humanStop: "Human asked to stop";
|
|
64
|
+
readonly legacyImport: "Imported from the pre-domain-model task record";
|
|
65
|
+
readonly forwarded: "Forwarded to the worker of the Ticket already on this Thread";
|
|
66
|
+
readonly senderNotMember: "The sender is not on the Source's list";
|
|
67
|
+
readonly retried: "Retried by a person";
|
|
68
|
+
readonly hostRestarted: "dsh-superagent restarted";
|
|
69
|
+
readonly stopUnknown: "Stop target is unknown or already ended";
|
|
70
|
+
readonly stopNotExplicit: "A stop needs an explicit request from a sender on the list";
|
|
71
|
+
readonly directionUnsettled: "Implementation intent or target direction is not established, or a product/design decision is still required";
|
|
72
|
+
readonly noRuleMatched: "No enabled admission rule matched";
|
|
73
|
+
readonly ignoredFromCard: "Ignored from its card, without a rule assessment";
|
|
74
|
+
readonly observed: "Observe mode: would accept ({text})";
|
|
75
|
+
};
|
|
76
|
+
type ReasonCode = keyof typeof REASONS;
|
|
77
|
+
/** Reason code and template values; `triggerId` identifies the trigger in a batch that the reason applies to. */
|
|
78
|
+
interface Reason {
|
|
79
|
+
code: ReasonCode | (string & {});
|
|
80
|
+
values?: Record<string, string>;
|
|
81
|
+
triggerId?: string;
|
|
82
|
+
}
|
|
83
|
+
declare const said: (text: string) => Reason;
|
|
84
|
+
/** Formats reasons in English (prefixed by `triggerId` when present) for agent consumption. */
|
|
85
|
+
declare function reasonText(reasons: readonly Reason[] | Reason | undefined): string;
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/core/model.d.ts
|
|
88
|
+
/** Actor identity in the source's own vocabulary; Policy decides which actors may originate work on that Source. */
|
|
89
|
+
interface ActorRef {
|
|
90
|
+
source: string;
|
|
91
|
+
id: string;
|
|
92
|
+
}
|
|
93
|
+
/** Thread identity; the rule that derives `id` from a native event belongs to the source. */
|
|
94
|
+
interface ThreadRef {
|
|
95
|
+
source: string;
|
|
96
|
+
id: string;
|
|
97
|
+
}
|
|
98
|
+
/** Write-only address on a thread; the address format belongs to the source. */
|
|
99
|
+
interface ChannelRef {
|
|
100
|
+
source: string;
|
|
101
|
+
thread: string;
|
|
102
|
+
address: Record<string, string>;
|
|
103
|
+
}
|
|
104
|
+
/** Stable key of a thread across the whole domain. */
|
|
105
|
+
declare const threadKey: (ref: ThreadRef) => string;
|
|
106
|
+
/**
|
|
107
|
+
* Normalizes valid timestamps to `Date#toISOString` form so stored timestamps compare lexicographically in memory and SQL;
|
|
108
|
+
* values that do not parse as a timestamp are returned unchanged.
|
|
109
|
+
*/
|
|
110
|
+
declare function isoTime(value: string): string;
|
|
111
|
+
/** One immutable event produced by a source. `text` and `meta` are untrusted external data. */
|
|
112
|
+
interface Trigger {
|
|
113
|
+
id: string;
|
|
114
|
+
source: string;
|
|
115
|
+
/** Every thread the trigger belongs to; the first (`threads[0]`) is the primary thread that hosts its evaluation. */
|
|
116
|
+
threads: ThreadRef[];
|
|
117
|
+
actor: ActorRef;
|
|
118
|
+
/** Source-declared: the event was written by a person rather than a bot, CI, or this agent. */
|
|
119
|
+
human: boolean;
|
|
120
|
+
/** Source-declared: the sender addressed the agent, by mentioning it or in a direct message to it. */
|
|
121
|
+
addressed?: 'mention' | 'direct';
|
|
122
|
+
at: string;
|
|
123
|
+
text: string;
|
|
124
|
+
channel?: ChannelRef;
|
|
125
|
+
url?: string;
|
|
126
|
+
/** Source-native details for adapters; the core never interprets them. */
|
|
127
|
+
meta: Record<string, unknown>;
|
|
128
|
+
}
|
|
129
|
+
/** Where a trigger went after intake; it grants nothing, but decides who decides a person's follow-up (see `followupOwners`). */
|
|
130
|
+
interface TriggerDisposition {
|
|
131
|
+
/** Rejected by the source scope filter before entering the domain, for the Source's reason. */
|
|
132
|
+
filtered?: Reason;
|
|
133
|
+
evalId?: string;
|
|
134
|
+
decisionId?: string;
|
|
135
|
+
/**
|
|
136
|
+
* The tickets it reached, by subscription fan-out or attached by the main Session; a ticket that ended or could not resume
|
|
137
|
+
* without its worker taking it up no longer counts.
|
|
138
|
+
*/
|
|
139
|
+
fanout: string[];
|
|
140
|
+
/** The tickets of `fanout` it reached only on a Thread their Executor follows for context, which makes it none of theirs. */
|
|
141
|
+
context?: string[];
|
|
142
|
+
/** A person, or the main Session for a message no ticket decides, sent it to Eval again; a hand-back does not set it. */
|
|
143
|
+
requeued?: true;
|
|
144
|
+
/** The tickets whose Executor handed it back as beyond the ticket, each once, with why; it goes to Eval once none decides it. */
|
|
145
|
+
handbacks?: {
|
|
146
|
+
ticketId: string;
|
|
147
|
+
reason: string;
|
|
148
|
+
at: string;
|
|
149
|
+
}[];
|
|
150
|
+
/** The tickets whose Executor acknowledged it or answered it, taking it up as within the ticket. */
|
|
151
|
+
acked?: string[];
|
|
152
|
+
at: string;
|
|
153
|
+
}
|
|
154
|
+
/** What deciding a follow-up's owners reads: the stored tickets, Threads and dispositions, as the Store and the core keep them. */
|
|
155
|
+
interface FollowupRecords {
|
|
156
|
+
tickets: {
|
|
157
|
+
get(id: string): Ticket | undefined;
|
|
158
|
+
};
|
|
159
|
+
threads: {
|
|
160
|
+
get(key: string): Thread | undefined;
|
|
161
|
+
};
|
|
162
|
+
triggers: {
|
|
163
|
+
disposition(id: string): TriggerDisposition | undefined;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The tickets whose worker decides a person's follow-up: those it reached on their own Threads that can still decide it
|
|
168
|
+
* and did not hand it back. A ticket can while it is live, also once a person dropped its Subscription to the Thread, and
|
|
169
|
+
* while it is ended, not by a person, with the Trigger waiting to resume it. None once a person sent it to Eval, or while
|
|
170
|
+
* an Eval holds it, which an earlier version may have queued. While a ticket owns it, the message goes to no Eval, and the
|
|
171
|
+
* main Session may only stop the work on it.
|
|
172
|
+
*/
|
|
173
|
+
declare function followupOwners(trigger: Pick<Trigger, 'id' | 'human' | 'threads'>, records: FollowupRecords): string[];
|
|
174
|
+
/**
|
|
175
|
+
* Whether a Decision settled what a person's message asks for: a stop on it, taken or refused, decides only that ticket's
|
|
176
|
+
* work, routing it to a ticket leaves it to that ticket's worker, and an unavailable one decided nothing.
|
|
177
|
+
*/
|
|
178
|
+
declare const settled: (decision: Pick<Decision, "kind" | "reason">) => boolean;
|
|
179
|
+
type Strength = 'explicit' | 'inferred';
|
|
180
|
+
/** The cells of a receiving scope's row in the admission table, by whether the sender is listed and addresses the agent. */
|
|
181
|
+
type AdmissionCell = 'listedUnaddressed' | 'unlistedAddressed' | 'unlistedUnaddressed';
|
|
182
|
+
/**
|
|
183
|
+
* Whether proactive candidates are assessed and accepted (`auto`), still assessed so Decisions record what they would accept
|
|
184
|
+
* (`would_accept`) without accepting (`observe`), or not admitted (`off`).
|
|
185
|
+
*/
|
|
186
|
+
type AcceptMode = 'off' | 'observe' | 'auto';
|
|
187
|
+
/** Where a Mandate's Trigger was received: its Source, its receiving scope as the Source recorded it, and its admission row. */
|
|
188
|
+
interface Received {
|
|
189
|
+
source?: string;
|
|
190
|
+
scope?: Record<string, unknown>;
|
|
191
|
+
row?: string;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* How the gate admitted the Trigger a Mandate came from, which `revalidate` re-checks (see `docs/002`):
|
|
195
|
+
* - `addressed`: a sender on the Source's list addressed the agent;
|
|
196
|
+
* - `followup`: a message on a Thread that has tickets;
|
|
197
|
+
* - `admission`: the cell of the receiving scope's admission row; `observation` when it made an inferred candidate under
|
|
198
|
+
* an accept mode;
|
|
199
|
+
* - `listed`: accepted before Mandates recorded their basis, from a sender who must still be on the list; `authority` is
|
|
200
|
+
* what an earlier version recorded instead.
|
|
201
|
+
* `sender` names the allowed sender at acceptance time, and `rules` the rules an assessment matched.
|
|
202
|
+
*/
|
|
203
|
+
type MandateBasis = Received & {
|
|
204
|
+
sender?: string;
|
|
205
|
+
rules?: string[];
|
|
206
|
+
} & ({
|
|
207
|
+
via: 'addressed';
|
|
208
|
+
addressed: 'mention' | 'direct';
|
|
209
|
+
} | {
|
|
210
|
+
via: 'followup';
|
|
211
|
+
authority?: string;
|
|
212
|
+
} | {
|
|
213
|
+
via: 'admission';
|
|
214
|
+
cell: AdmissionCell;
|
|
215
|
+
addressed?: 'mention' | 'direct';
|
|
216
|
+
observation?: {
|
|
217
|
+
mode: AcceptMode;
|
|
218
|
+
};
|
|
219
|
+
} | {
|
|
220
|
+
via: 'listed';
|
|
221
|
+
addressed?: 'mention' | 'direct';
|
|
222
|
+
observation?: {
|
|
223
|
+
mode?: AcceptMode;
|
|
224
|
+
};
|
|
225
|
+
authority?: string;
|
|
226
|
+
} |
|
|
227
|
+
/** A message of a bot on its Source's bot list: an inferred candidate at most, never an explicit request or a stop. */
|
|
228
|
+
{
|
|
229
|
+
via: 'bot';
|
|
230
|
+
addressed?: 'mention' | 'direct';
|
|
231
|
+
observation: {
|
|
232
|
+
mode: AcceptMode;
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
/** Immutable authorization produced by an accept decision; execution is bounded by it and it never changes. */
|
|
236
|
+
interface Mandate {
|
|
237
|
+
strength: Strength;
|
|
238
|
+
policyVersion: string;
|
|
239
|
+
/** How the gate admitted it, for re-validation and audit. */
|
|
240
|
+
basis: MandateBasis;
|
|
241
|
+
actor: ActorRef;
|
|
242
|
+
scope: string;
|
|
243
|
+
}
|
|
244
|
+
/** Active trigger evaluation on a thread; at most one exists per thread. */
|
|
245
|
+
interface Evaluation {
|
|
246
|
+
id: string;
|
|
247
|
+
threadKey: string;
|
|
248
|
+
triggerIds: string[];
|
|
249
|
+
/** The admissible Trigger that gave the batch its strength, which a ticket the Eval starts takes as its origin. */
|
|
250
|
+
primary?: string;
|
|
251
|
+
strength: Strength;
|
|
252
|
+
/** Actor and gate basis of the strongest admissible trigger; they seed the mandate on accept. */
|
|
253
|
+
actor: ActorRef;
|
|
254
|
+
basis: MandateBasis;
|
|
255
|
+
policyVersion: string;
|
|
256
|
+
startedAt: string;
|
|
257
|
+
/** Timestamp when the candidate reached the assessor's Session; it cannot expire before, and its deadline counts from then. */
|
|
258
|
+
delivered?: string;
|
|
259
|
+
/** Expiration timestamp, counted from delivery (or from start until delivered); a processing pause shifts it forward by the pause duration. */
|
|
260
|
+
deadline: string;
|
|
261
|
+
/** Timestamp when processing pause began; resuming shifts `deadline` forward by the pause duration; cleared on resume. */
|
|
262
|
+
pausedAt?: string;
|
|
263
|
+
}
|
|
264
|
+
/** Thread aggregate: consistency boundary of eval, holder of subscriptions and decision history. */
|
|
265
|
+
interface Thread {
|
|
266
|
+
key: string;
|
|
267
|
+
ref: ThreadRef;
|
|
268
|
+
createdAt: string;
|
|
269
|
+
/** Time of the newest trigger on the thread. */
|
|
270
|
+
lastAt?: string;
|
|
271
|
+
/** Tickets subscribed to this thread; live ones receive every trigger as context through fan-out. */
|
|
272
|
+
subscriptions: string[];
|
|
273
|
+
/**
|
|
274
|
+
* The subscribed tickets whose Executor follows the thread for read-only context, such as an Issue or another pull request
|
|
275
|
+
* it found: its messages reach them, but grant nothing, and a person's message there resumes none of them that ended.
|
|
276
|
+
*/
|
|
277
|
+
context?: string[];
|
|
278
|
+
/** Keys of threads that shared a trigger with this one; context only. */
|
|
279
|
+
correlated: string[];
|
|
280
|
+
/** Triggers waiting for the next eval (debounce buffer). */
|
|
281
|
+
pending: string[];
|
|
282
|
+
evaluating?: Evaluation;
|
|
283
|
+
/** How many Evals of its queued Triggers expired in a row, each of which queued them again; a Decision clears it. */
|
|
284
|
+
expired?: number;
|
|
285
|
+
/**
|
|
286
|
+
* Source-native attributes needed by channels or readers (for example a Feishu thread id). Its Source may also describe
|
|
287
|
+
* the thread for people: `label`, a display name, and `facts`, a {@link ThreadFacts} map.
|
|
288
|
+
*/
|
|
289
|
+
meta: Record<string, unknown>;
|
|
290
|
+
}
|
|
291
|
+
/** Text in both interface languages, or the same text in both. */
|
|
292
|
+
type Text = string | {
|
|
293
|
+
zh: string;
|
|
294
|
+
en: string;
|
|
295
|
+
};
|
|
296
|
+
/** One fact a Source states about its thread, such as an issue number or a CI result; `problem` marks one that needs a person. */
|
|
297
|
+
interface ThreadFact {
|
|
298
|
+
label?: Text;
|
|
299
|
+
value: Text;
|
|
300
|
+
problem?: true;
|
|
301
|
+
}
|
|
302
|
+
/** A thread's facts by id, ordered for display; each writer owns its fact ids. */
|
|
303
|
+
type ThreadFacts = Record<string, ThreadFact>;
|
|
304
|
+
type DecisionKind = 'ignore' | 'accept' | 'reopen' | 'stop' | 'unavailable';
|
|
305
|
+
/** Outcome of one eval, recorded on the thread. */
|
|
306
|
+
interface Decision {
|
|
307
|
+
id: string;
|
|
308
|
+
evalId: string;
|
|
309
|
+
threadKey: string;
|
|
310
|
+
triggerIds: string[];
|
|
311
|
+
policyVersion: string;
|
|
312
|
+
at: string;
|
|
313
|
+
kind: DecisionKind;
|
|
314
|
+
/** Why, by code: one reason, or one for each Trigger of the batch it concerns. */
|
|
315
|
+
reason: Reason[];
|
|
316
|
+
strength: Strength;
|
|
317
|
+
ticketId?: string;
|
|
318
|
+
mandate?: Mandate;
|
|
319
|
+
/** Assessor-supplied audit data (rule assessments, direction, evidence). */
|
|
320
|
+
audit?: Record<string, unknown>;
|
|
321
|
+
/** A person dismissed the problem an unavailable Decision shows. */
|
|
322
|
+
dismissed?: true;
|
|
323
|
+
}
|
|
324
|
+
type AssignmentState = 'pending' | 'running' | 'ended';
|
|
325
|
+
/**
|
|
326
|
+
* What an Assignment reports: work is in progress (`progress`), its worker waits for a Trigger (`waiting`), a result is
|
|
327
|
+
* ready (`ready`), a person must act to unblock or recover it (`blocked` or `failed`), or nothing is left to do (`dropped`).
|
|
328
|
+
*/
|
|
329
|
+
type ReportState = 'progress' | 'waiting' | 'ready' | 'blocked' | 'failed' | 'dropped';
|
|
330
|
+
/** Binding between a ticket and one executor instance. */
|
|
331
|
+
interface Assignment {
|
|
332
|
+
id: string;
|
|
333
|
+
state: AssignmentState;
|
|
334
|
+
createdAt: string;
|
|
335
|
+
startedAt?: string;
|
|
336
|
+
endedAt?: string;
|
|
337
|
+
endedReason?: string;
|
|
338
|
+
report?: {
|
|
339
|
+
state: ReportState;
|
|
340
|
+
summary: string;
|
|
341
|
+
at: string;
|
|
342
|
+
};
|
|
343
|
+
/** When Inputs last reached the worker; one at or after `report.at` means the worker has been busy with Inputs since that report. */
|
|
344
|
+
deliveredAt?: string;
|
|
345
|
+
/**
|
|
346
|
+
* When a turn of its worker last ended with nothing left to run for it and nothing waiting for it: what its Session took
|
|
347
|
+
* into a step before, the worker read.
|
|
348
|
+
*/
|
|
349
|
+
idleAt?: string;
|
|
350
|
+
}
|
|
351
|
+
type TicketInput =
|
|
352
|
+
/** A Trigger on one of its Threads, or attached to it; `human` when a person wrote it. */
|
|
353
|
+
{
|
|
354
|
+
id: string;
|
|
355
|
+
kind: 'trigger';
|
|
356
|
+
triggerId: string;
|
|
357
|
+
at: string;
|
|
358
|
+
note?: string;
|
|
359
|
+
human?: true;
|
|
360
|
+
/** It reached the ticket only on a Thread its Executor follows for context. */
|
|
361
|
+
context?: true;
|
|
362
|
+
/**
|
|
363
|
+
* When the worker's Session took the message that carried it into a step, from which its model reads it; `null` while
|
|
364
|
+
* it waits in the Session. One delivered before reads were recorded has none, and counts as read from `deliveredAt`.
|
|
365
|
+
*/
|
|
366
|
+
readAt?: string | null;
|
|
367
|
+
/** The Assignment it was last delivered to. */
|
|
368
|
+
deliveredTo?: string;
|
|
369
|
+
/** When an earlier version handed it to the worker. */
|
|
370
|
+
deliveredAt?: string;
|
|
371
|
+
} |
|
|
372
|
+
/** The Policy changed in a way that bears on the ticket, as `effect` says; Inputs written before effects have none. */
|
|
373
|
+
{
|
|
374
|
+
id: string;
|
|
375
|
+
kind: 'policy-changed';
|
|
376
|
+
from: string;
|
|
377
|
+
to: string;
|
|
378
|
+
at: string;
|
|
379
|
+
effect?: PolicyEffect;
|
|
380
|
+
} |
|
|
381
|
+
/** A Decision reopened the ticket; written before ended tickets resumed on their own. */
|
|
382
|
+
{
|
|
383
|
+
id: string;
|
|
384
|
+
kind: 'reopen';
|
|
385
|
+
decisionId: string;
|
|
386
|
+
at: string;
|
|
387
|
+
} |
|
|
388
|
+
/** A human's message on one of its Threads resumed the ended ticket under its unchanged Mandate. */
|
|
389
|
+
{
|
|
390
|
+
id: string;
|
|
391
|
+
kind: 'resumed';
|
|
392
|
+
triggerId: string;
|
|
393
|
+
at: string;
|
|
394
|
+
} | {
|
|
395
|
+
id: string;
|
|
396
|
+
kind: 'text';
|
|
397
|
+
text: string;
|
|
398
|
+
at: string;
|
|
399
|
+
} |
|
|
400
|
+
/** A fact the main Session gave the ticket from other work: context, which asks for nothing and grants nothing. */
|
|
401
|
+
{
|
|
402
|
+
id: string;
|
|
403
|
+
kind: 'note';
|
|
404
|
+
text: string;
|
|
405
|
+
at: string;
|
|
406
|
+
};
|
|
407
|
+
/**
|
|
408
|
+
* How a Policy change bears on a ticket: the rules it was accepted under that changed, with their instruction now, or no
|
|
409
|
+
* longer apply; `proactive: false` once inferred work is no longer accepted, as when the accept mode leaves `auto`; and
|
|
410
|
+
* `requesterAllowed: false` once the sender it is for, `requester`, a person or a bot, may no longer start work there.
|
|
411
|
+
*/
|
|
412
|
+
interface PolicyEffect {
|
|
413
|
+
rules?: {
|
|
414
|
+
id: string;
|
|
415
|
+
name: string;
|
|
416
|
+
change: 'changed' | 'disabled';
|
|
417
|
+
instruction?: string;
|
|
418
|
+
}[];
|
|
419
|
+
proactive?: false;
|
|
420
|
+
requesterAllowed?: false;
|
|
421
|
+
requester?: ActorRef;
|
|
422
|
+
}
|
|
423
|
+
type TicketEndBy = 'executor' | 'human';
|
|
424
|
+
/** How a ticket ended: with its result, with its pull request merged or closed unmerged, with nothing to do, or stopped by a person. */
|
|
425
|
+
type TicketOutcome = 'completed' | 'merged' | 'closed' | 'dropped' | 'stopped';
|
|
426
|
+
/** A question for a person; `triggerId` is the human message that raised it, whose conversation it goes back to. */
|
|
427
|
+
interface Attention {
|
|
428
|
+
id: string;
|
|
429
|
+
question: string;
|
|
430
|
+
triggerId?: string;
|
|
431
|
+
}
|
|
432
|
+
/** Ticket aggregate: an authorized work item with its assignments, channels, thread bindings, and inputs. */
|
|
433
|
+
interface Ticket {
|
|
434
|
+
id: string;
|
|
435
|
+
title: string;
|
|
436
|
+
decisionId: string;
|
|
437
|
+
mandate: Mandate;
|
|
438
|
+
/** The Channels of the messages it was accepted from, which its replies go to. */
|
|
439
|
+
channels: ChannelRef[];
|
|
440
|
+
/** When, by whom, and how the ticket ended, with a person's or the host's reason beyond the outcome; an open ticket has none. */
|
|
441
|
+
ended?: {
|
|
442
|
+
at: string;
|
|
443
|
+
by: TicketEndBy;
|
|
444
|
+
outcome: TicketOutcome;
|
|
445
|
+
reason?: string;
|
|
446
|
+
};
|
|
447
|
+
/** Open question for a person; cleared by the executor or when the ticket ends. */
|
|
448
|
+
attention?: Attention;
|
|
449
|
+
assignments: Assignment[];
|
|
450
|
+
inputs: TicketInput[];
|
|
451
|
+
/** Inputs not yet handed to a running assignment. */
|
|
452
|
+
undelivered: string[];
|
|
453
|
+
/**
|
|
454
|
+
* Threads a Source made the ticket follow, such as its pull requests': a `ready` report ends the ticket only once
|
|
455
|
+
* `followsDone` holds; the close that makes it hold ends the ticket with that outcome unless its work has not finished (see
|
|
456
|
+
* `Tickets.unfinished`), and then its next `ready` or `dropped` report ends it.
|
|
457
|
+
*/
|
|
458
|
+
follows?: Follow[];
|
|
459
|
+
/** People's questions the worker answered, whose answers the main Session may send. */
|
|
460
|
+
answers?: string[];
|
|
461
|
+
createdAt: string;
|
|
462
|
+
updatedAt: string;
|
|
463
|
+
/** The Triggers it was accepted from: the one that started it, and the batch its Eval held. */
|
|
464
|
+
origin: {
|
|
465
|
+
triggerId: string;
|
|
466
|
+
triggerIds: string[];
|
|
467
|
+
};
|
|
468
|
+
/** What its Executor chose: its short name, directory and assignment text. */
|
|
469
|
+
execution?: Execution;
|
|
470
|
+
/** The latest failed delivery of an Input to its worker, until a delivery succeeds. */
|
|
471
|
+
delivery?: {
|
|
472
|
+
error: string;
|
|
473
|
+
at: string;
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* A Thread a ticket follows, by its key: `own` when the ticket opened it, such as its own pull request, and when and how it
|
|
478
|
+
* closed. `followsDone` holds once no Thread of its own is open, or, when the ticket has no `own` Thread, once every followed
|
|
479
|
+
* Thread closed; the ticket then ends unless its work has not finished (see `Tickets.unfinished`).
|
|
480
|
+
*/
|
|
481
|
+
interface Follow {
|
|
482
|
+
thread: string;
|
|
483
|
+
own: boolean;
|
|
484
|
+
closed?: {
|
|
485
|
+
at: string;
|
|
486
|
+
outcome: 'merged' | 'closed';
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
interface Execution {
|
|
490
|
+
name: string;
|
|
491
|
+
directory: string;
|
|
492
|
+
assignment: string;
|
|
493
|
+
}
|
|
494
|
+
/** Whether a ticket is open: it has not ended. */
|
|
495
|
+
declare const liveTicket: (ticket: Pick<Ticket, "ended">) => boolean;
|
|
496
|
+
/** How a ticket ended. */
|
|
497
|
+
declare const ticketOutcome: (ticket: Pick<Ticket, "ended">) => TicketOutcome | undefined;
|
|
498
|
+
declare const runningAssignment: (ticket: Ticket) => Assignment | undefined;
|
|
499
|
+
/**
|
|
500
|
+
* Whether a Trigger reached a ticket: it started the ticket, came to it as an Input, or is on a Thread the ticket subscribes
|
|
501
|
+
* to; with `context` false, not a Thread the ticket follows only for context, where its worker may read but not write.
|
|
502
|
+
*/
|
|
503
|
+
declare function reached(ticket: Ticket, trigger: Trigger, thread: (key: string) => Thread | undefined, context?: boolean): boolean;
|
|
504
|
+
declare const latestAssignment: (ticket: Ticket) => Assignment | undefined;
|
|
505
|
+
/** The latest report any of a ticket's Assignments made, which is what the ticket last said. */
|
|
506
|
+
declare const latestReport: (ticket: Pick<Ticket, "assignments">) => Assignment["report"];
|
|
507
|
+
/**
|
|
508
|
+
* Until when a ticket's worker had a turn to read what reached it: its latest report, but a progress report when `progress`
|
|
509
|
+
* is false, or the end of its latest turn, whichever is later.
|
|
510
|
+
*/
|
|
511
|
+
declare function readUntil(ticket: Pick<Ticket, 'assignments'>, progress?: boolean): string;
|
|
512
|
+
/**
|
|
513
|
+
* Whether a ticket waits for a person: its latest Assignment reported blocked or failed, and no person's message reached it
|
|
514
|
+
* since. A later report that is neither, a person's message, or a retry, which starts a new Assignment, lifts it.
|
|
515
|
+
*/
|
|
516
|
+
declare function waitsForPerson(ticket: Ticket): boolean;
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region src/core/ports.d.ts
|
|
519
|
+
/** Gate admission result: either inadmissible with a reason code, or admissible with strength and MandateBasis. */
|
|
520
|
+
type GateResult = {
|
|
521
|
+
admissible: false;
|
|
522
|
+
reason: string;
|
|
523
|
+
} | {
|
|
524
|
+
admissible: true;
|
|
525
|
+
strength: Strength;
|
|
526
|
+
basis: MandateBasis;
|
|
527
|
+
};
|
|
528
|
+
/** Whether a Source receives a Trigger; `observed` when it also receives messages not addressed to the agent, and `row` the receiving scope the Policy's admission table names, such as a chat. */
|
|
529
|
+
type ScopeResult = {
|
|
530
|
+
listened: true;
|
|
531
|
+
observed: boolean;
|
|
532
|
+
row?: string;
|
|
533
|
+
} | {
|
|
534
|
+
listened: false;
|
|
535
|
+
reason: string;
|
|
536
|
+
};
|
|
537
|
+
/** Source configuration as the domain sees it: listening scope per trigger or per recorded Mandate basis. */
|
|
538
|
+
interface SourceScope {
|
|
539
|
+
scope(input: {
|
|
540
|
+
source: string;
|
|
541
|
+
meta: Record<string, unknown>;
|
|
542
|
+
}): ScopeResult;
|
|
543
|
+
}
|
|
544
|
+
/** Versioned rules; `gate` decides who may originate work. Source scope is not part of Policy. */
|
|
545
|
+
interface Policy {
|
|
546
|
+
version: string;
|
|
547
|
+
paused: boolean;
|
|
548
|
+
gate(trigger: Trigger, thread: Thread, tickets: Ticket[]): GateResult;
|
|
549
|
+
/** Re-check a mandate's gate-layer basis under this policy, such as before an ended ticket resumes. */
|
|
550
|
+
revalidate(mandate: Mandate): boolean;
|
|
551
|
+
/** Whether an inferred candidate may be accepted on the rules its assessment matched: assessments accept, and each rule is enabled. */
|
|
552
|
+
admitsInferred(rules: readonly string[]): boolean;
|
|
553
|
+
/** How long an assessor may take before an eval is recorded as unavailable. */
|
|
554
|
+
evalTimeoutMs: number;
|
|
555
|
+
}
|
|
556
|
+
/** What the assessor receives: bounded facts, never authority. */
|
|
557
|
+
interface Candidate {
|
|
558
|
+
evalId: string;
|
|
559
|
+
thread: Thread;
|
|
560
|
+
triggers: Trigger[];
|
|
561
|
+
strength: Strength;
|
|
562
|
+
policyVersion: string;
|
|
563
|
+
/** Every ticket on the thread, including ended ones, for duplicate judgement. */
|
|
564
|
+
tickets: Ticket[];
|
|
565
|
+
}
|
|
566
|
+
type AssessmentOutcome = {
|
|
567
|
+
kind: 'ignore';
|
|
568
|
+
reason: string | Reason;
|
|
569
|
+
audit?: Record<string, unknown>;
|
|
570
|
+
} | {
|
|
571
|
+
kind: 'unavailable';
|
|
572
|
+
reason: string | Reason;
|
|
573
|
+
audit?: Record<string, unknown>;
|
|
574
|
+
} |
|
|
575
|
+
/** An accept: `rules` the assessment matched, `name` the ticket's requested short name, and `note` a routing note for its worker. */
|
|
576
|
+
{
|
|
577
|
+
kind: 'accept';
|
|
578
|
+
title: string;
|
|
579
|
+
scope: string;
|
|
580
|
+
reason: string | Reason;
|
|
581
|
+
acknowledgement?: string;
|
|
582
|
+
rules?: string[];
|
|
583
|
+
audit?: Record<string, unknown>;
|
|
584
|
+
name?: string;
|
|
585
|
+
note?: string;
|
|
586
|
+
} | {
|
|
587
|
+
kind: 'stop';
|
|
588
|
+
ticketId: string;
|
|
589
|
+
reason: string | Reason;
|
|
590
|
+
audit?: Record<string, unknown>;
|
|
591
|
+
};
|
|
592
|
+
/** Semantic judgement. `request` queues the assessment, idempotently per `evalId`; the outcome returns through `Engine.decide`. */
|
|
593
|
+
interface Assessor {
|
|
594
|
+
request(candidate: Candidate): void;
|
|
595
|
+
/** Called whenever an eval ends, decided or not, so the assessor drops what it kept for it. */
|
|
596
|
+
release?(evalId: string): void;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Executes assignments; concurrency and workspace management are its own concern. The Engine calls it inside its
|
|
600
|
+
* commands, so each method only queues work, which reports back through the Engine's callbacks.
|
|
601
|
+
*/
|
|
602
|
+
interface Executor {
|
|
603
|
+
/** Queue a pending assignment for launch; reports back through `Engine.assignmentStarted` or a `failed` report. */
|
|
604
|
+
start(ticket: Ticket, assignment: Assignment): void;
|
|
605
|
+
/**
|
|
606
|
+
* Queue delivery of the ticket's waiting Inputs to its running assignment; reports back through `Engine.inputsDelivered`
|
|
607
|
+
* or `Engine.deliveryFailed`, and through `Engine.inputsRead` once its worker's Session takes them into a step.
|
|
608
|
+
*/
|
|
609
|
+
deliver(ticket: Ticket, assignment: Assignment): void;
|
|
610
|
+
/** End an assignment's work, such as cancelling its Session's turn; called after the ticket or assignment has already ended in the domain. */
|
|
611
|
+
stop(ticket: Ticket, assignment: Assignment, reason: string): void;
|
|
612
|
+
}
|
|
613
|
+
/** A message for a Channel; `ticketId` names the Ticket it is for, when there is one. */
|
|
614
|
+
interface OutboundMessage {
|
|
615
|
+
id: string;
|
|
616
|
+
channel: ChannelRef;
|
|
617
|
+
text: string;
|
|
618
|
+
kind: 'acknowledgement' | 'reply' | 'notice';
|
|
619
|
+
ticketId?: string;
|
|
620
|
+
}
|
|
621
|
+
/** A queued message waits (`pending`), went out (`sent`), or was dropped because its conversation can never receive it (`dropped`). */
|
|
622
|
+
type ReplyStatus = 'pending' | 'sent' | 'dropped';
|
|
623
|
+
/** The messages queued for Channels; the Engine queues them inside its commands, and a sender delivers them later. */
|
|
624
|
+
interface Outbox {
|
|
625
|
+
/** Queue a message idempotently; an id queued before keeps its first message. */
|
|
626
|
+
send(message: OutboundMessage): void;
|
|
627
|
+
/** Whether a message was queued, and what became of it. */
|
|
628
|
+
status(id: string): ReplyStatus | undefined;
|
|
629
|
+
/** Withdraw a message that has not gone out. */
|
|
630
|
+
withdraw(id: string): void;
|
|
631
|
+
}
|
|
632
|
+
/** What the Engine reports after a change, by event type. */
|
|
633
|
+
interface DomainEvents {
|
|
634
|
+
'trigger.received': {
|
|
635
|
+
triggerId: string;
|
|
636
|
+
threads: string[];
|
|
637
|
+
};
|
|
638
|
+
'eval.started': {
|
|
639
|
+
evalId: string;
|
|
640
|
+
threadKey: string;
|
|
641
|
+
triggerIds: string[];
|
|
642
|
+
strength: Strength;
|
|
643
|
+
};
|
|
644
|
+
'decision.recorded': {
|
|
645
|
+
decisionId: string;
|
|
646
|
+
kind: DecisionKind;
|
|
647
|
+
threadKey: string;
|
|
648
|
+
};
|
|
649
|
+
'ticket.created': {
|
|
650
|
+
ticketId: string;
|
|
651
|
+
decisionId: string;
|
|
652
|
+
strength: Strength;
|
|
653
|
+
};
|
|
654
|
+
'ticket.resumed': {
|
|
655
|
+
ticketId: string;
|
|
656
|
+
triggerId: string;
|
|
657
|
+
};
|
|
658
|
+
'ticket.ended': {
|
|
659
|
+
ticketId: string;
|
|
660
|
+
by: TicketEndBy;
|
|
661
|
+
outcome: TicketOutcome;
|
|
662
|
+
reason?: string;
|
|
663
|
+
};
|
|
664
|
+
'assignment.started': {
|
|
665
|
+
ticketId: string;
|
|
666
|
+
assignmentId: string;
|
|
667
|
+
};
|
|
668
|
+
'assignment.ended': {
|
|
669
|
+
ticketId: string;
|
|
670
|
+
assignmentId: string;
|
|
671
|
+
reason: string;
|
|
672
|
+
};
|
|
673
|
+
'thread.subscribed': {
|
|
674
|
+
threadKey: string;
|
|
675
|
+
ticketId: string;
|
|
676
|
+
};
|
|
677
|
+
/** A person dropped every ticket's Subscription, or a ticket's Executor its own, for `reason`. */
|
|
678
|
+
'thread.unsubscribed': {
|
|
679
|
+
threadKey: string;
|
|
680
|
+
ticketId: string;
|
|
681
|
+
by: 'person' | 'executor';
|
|
682
|
+
reason?: string;
|
|
683
|
+
};
|
|
684
|
+
'trigger.requeued': {
|
|
685
|
+
triggerId: string;
|
|
686
|
+
};
|
|
687
|
+
/** A person's follow-up that reached tickets and that an earlier version also queued for Eval left the queue. */
|
|
688
|
+
'trigger.followup': {
|
|
689
|
+
triggerId: string;
|
|
690
|
+
};
|
|
691
|
+
'policy.changed': {
|
|
692
|
+
from: string;
|
|
693
|
+
to: string;
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
type DomainEvent = { [K in keyof DomainEvents]: {
|
|
697
|
+
type: K;
|
|
698
|
+
at: string;
|
|
699
|
+
data: DomainEvents[K];
|
|
700
|
+
}; }[keyof DomainEvents];
|
|
701
|
+
/** Durable state. Every method is synchronous so `atomic` can wrap a unit of work. */
|
|
702
|
+
interface Repositories {
|
|
703
|
+
atomic<T>(work: () => T): T;
|
|
704
|
+
triggers: {
|
|
705
|
+
get(id: string): Trigger | undefined;
|
|
706
|
+
save(trigger: Trigger): void;
|
|
707
|
+
disposition(id: string): TriggerDisposition | undefined;
|
|
708
|
+
saveDisposition(id: string, value: TriggerDisposition): void;
|
|
709
|
+
};
|
|
710
|
+
threads: {
|
|
711
|
+
get(key: string): Thread | undefined;
|
|
712
|
+
save(thread: Thread): void;
|
|
713
|
+
list(): Thread[];
|
|
714
|
+
/** Threads with Triggers queued for Eval or an Eval in progress. */
|
|
715
|
+
active(): Thread[];
|
|
716
|
+
};
|
|
717
|
+
tickets: {
|
|
718
|
+
get(id: string): Ticket | undefined;
|
|
719
|
+
save(ticket: Ticket): void;
|
|
720
|
+
list(): Ticket[];
|
|
721
|
+
open(): Ticket[];
|
|
722
|
+
/** Tickets with undelivered Inputs or a pending or running Assignment, which is always the latest. */
|
|
723
|
+
active(): Ticket[];
|
|
724
|
+
};
|
|
725
|
+
decisions: {
|
|
726
|
+
get(id: string): Decision | undefined;
|
|
727
|
+
save(decision: Decision): void;
|
|
728
|
+
byThread(key: string): Decision[];
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
//#endregion
|
|
732
|
+
//#region src/core/intake.d.ts
|
|
733
|
+
type ReceiveResult = 'stored' | 'duplicate' | 'filtered';
|
|
734
|
+
//#endregion
|
|
735
|
+
export { settled as $, MandateBasis as A, TicketEndBy as B, Decision as C, Follow as D, Execution as E, Thread as F, followupOwners as G, TicketOutcome as H, ThreadFact as I, latestReport as J, isoTime as K, ThreadFacts as L, ReportState as M, Strength as N, FollowupRecords as O, Text as P, runningAssignment as Q, ThreadRef as R, ChannelRef as S, Evaluation as T, Trigger as U, TicketInput as V, TriggerDisposition as W, reached as X, liveTicket as Y, readUntil as Z, ActorRef as _, DomainEvent as a, ReasonCode as at, AssignmentState as b, GateResult as c, Config as ct, Policy as d, threadKey as et, ReplyStatus as f, AcceptMode as g, SourceScope as h, Candidate as i, Reason as it, PolicyEffect as j, Mandate as k, OutboundMessage as l, Deployment as lt, ScopeResult as m, AssessmentOutcome as n, waitsForPerson as nt, DomainEvents as o, reasonText as ot, Repositories as p, latestAssignment as q, Assessor as r, REASONS as rt, Executor as s, said as st, ReceiveResult as t, ticketOutcome as tt, Outbox as u, DeploymentConfig as ut, AdmissionCell as v, DecisionKind as w, Attention as x, Assignment as y, Ticket as z };
|