@tomradex/agent 0.0.0-stage → 0.15.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.
@@ -0,0 +1,984 @@
1
+ import type { z } from "zod";
2
+ import type { Author } from "@tomradex/domain";
3
+ import type { LlmService, Message, ToolSpec } from "@tomradex/llm";
4
+ import type { RunHandle, ToolDescriptor } from "@tomradex/tools";
5
+ /**
6
+ * What the user was looking at when they sent a message. Kinds are open on
7
+ * purpose — a plugin can put its own surface on screen (a future "doc").
8
+ * Purely a hint for resolving "this/current"; tools still take explicit ids.
9
+ */
10
+ export interface FocusRef {
11
+ kind: string;
12
+ id: string;
13
+ /** Human-readable key where the entity has one (items do, boards don't). */
14
+ key?: string;
15
+ title: string;
16
+ }
17
+ /** Everything the panel knows how to draw. Engines emit these; the UI has no engine-specific code. */
18
+ export type Block = {
19
+ type: "text";
20
+ text: string;
21
+ focus?: FocusRef[];
22
+ attachments?: import("./media.js").MediaRef[]; /** Sent into a running turn; the id the panel gave it. */
23
+ steerId?: string;
24
+ } | {
25
+ type: "media";
26
+ media: import("./media.js").MediaRef;
27
+ caption?: string;
28
+ } | {
29
+ type: "trace";
30
+ tool: string;
31
+ args: unknown;
32
+ ok: boolean;
33
+ summary: string;
34
+ ms: number;
35
+ commandResult?: {
36
+ status: "success" | "no-match" | "condition-false" | "failed" | "declined" | "timed-out" | "interrupted";
37
+ exitCode: number | null;
38
+ output: string;
39
+ };
40
+ } | {
41
+ type: "question";
42
+ id: string;
43
+ text: string;
44
+ options?: string[];
45
+ multiple?: boolean;
46
+ freeText?: boolean;
47
+ } | {
48
+ type: "card";
49
+ id: string;
50
+ schema: string;
51
+ title: string;
52
+ data: unknown;
53
+ } | {
54
+ type: "status";
55
+ text: string;
56
+ } | {
57
+ type: "changes";
58
+ runId: string;
59
+ count: number;
60
+ items?: {
61
+ key: string;
62
+ label: string;
63
+ op: "created" | "updated" | "deleted";
64
+ }[];
65
+ } | {
66
+ type: "note";
67
+ text: string;
68
+ }
69
+ /** One stretch of failed model requests, stored once it ends: recovered, or the run gave up. */
70
+ | {
71
+ type: "retry";
72
+ attempts: number;
73
+ max?: number;
74
+ reason: string;
75
+ ms: number;
76
+ recovered: boolean;
77
+ }
78
+ /** `details`: what the engine knew beyond the one line, folded under it so it can be read and copied. */
79
+ | {
80
+ type: "error";
81
+ text: string;
82
+ details?: string;
83
+ };
84
+ export interface ThreadMessage {
85
+ id: string;
86
+ threadId: string;
87
+ seq: number;
88
+ role: "user" | "agent" | "system";
89
+ block: Block;
90
+ runId: string | null;
91
+ at: number;
92
+ }
93
+ /**
94
+ * Who owns a thread. `doctor`: a troubleshooting conversation, listed in the
95
+ * panel beside the assistant's. `probe`: a one-turn test the troubleshooter
96
+ * runs on an engine and deletes, never listed.
97
+ */
98
+ export type ThreadSurface = "assistant" | "code" | "doctor" | "probe";
99
+ /** The threads the assistant panel lists. */
100
+ export declare const PANEL_SURFACES: ThreadSurface[];
101
+ /** A `checklist` card: what was looked at, each row's state as it goes. */
102
+ export interface ChecklistCard {
103
+ rows: {
104
+ id: string;
105
+ title: string;
106
+ state: "pending" | "running" | "ok" | "warn" | "fail" | "skip";
107
+ line?: string;
108
+ }[];
109
+ }
110
+ /**
111
+ * What a person can do about a finding, drawn as a button. The backend words
112
+ * the label; the panel knows the verbs, so no plugin ships UI code for them.
113
+ */
114
+ export type CardAction =
115
+ /** Runs the engine's declared install or sign-in command in a terminal. */
116
+ {
117
+ kind: "install" | "sign-in";
118
+ engineId: string;
119
+ label: string;
120
+ }
121
+ /** Opens settings: the engine's own row, or model settings without one; `pane` opens that pane instead. */
122
+ | {
123
+ kind: "settings";
124
+ engineId?: string;
125
+ pane?: string;
126
+ label: string;
127
+ }
128
+ /** Moves that thread to another ready agent and sends its last message again. */
129
+ | {
130
+ kind: "retry-with";
131
+ threadId: string;
132
+ exclude?: string;
133
+ label: string;
134
+ }
135
+ /** Says this in the card's own thread. */
136
+ | {
137
+ kind: "say";
138
+ text: string;
139
+ label: string;
140
+ }
141
+ /** Installs one of Tomrade's own plugins from the official catalog; `done` is what the person is told once it is on. */
142
+ | {
143
+ kind: "install-plugin";
144
+ pluginId: string;
145
+ done: string;
146
+ label: string;
147
+ }
148
+ /** Opens the report about this thread, to read and send to Tomrade's developer. */
149
+ | {
150
+ kind: "report";
151
+ label: string;
152
+ }
153
+ /** Lets one of these agents look into it; `note` says what it will read and where that goes. */
154
+ | {
155
+ kind: "investigate";
156
+ engineIds: string[];
157
+ note: string;
158
+ label: string;
159
+ };
160
+ /** A `finding` card: one thing found, how sure, and what to do about it. */
161
+ export interface FindingCard {
162
+ tone: "ok" | "warn" | "fail";
163
+ headline: string;
164
+ body?: string;
165
+ confidence?: "high" | "medium" | "low";
166
+ actions: CardAction[];
167
+ }
168
+ export interface ThreadSummary {
169
+ execution?: GeneralExecution | null;
170
+ /** In a listing for the panel: a run of it is going right now. */
171
+ running?: boolean;
172
+ /** In a listing for the panel: it stopped on a usage limit that lifts then. */
173
+ parkedUntil?: number | null;
174
+ id: string;
175
+ projectId: string;
176
+ /** The workstream the thread stands on, and so the repository it runs in; null for a thread about the whole project. */
177
+ boardId: string | null;
178
+ /** The ticket the thread implements, when it was started from one. Such a thread runs a code engine in its own branch. */
179
+ itemId: string | null;
180
+ /** The branch the thread's worktree is on; null for a free-form thread, which sits detached at the default branch. */
181
+ branch: string | null;
182
+ title: string;
183
+ engine: string;
184
+ /** Which product surface owns the thread. Code sessions use Agent's worktree machinery without becoming Assistant conversations. */
185
+ surface: ThreadSurface;
186
+ permissionMode?: string | null;
187
+ /** Engine option overrides chosen for this thread (model, effort…); merged over the engine's settings. */
188
+ options?: Record<string, unknown> | null;
189
+ archived: boolean;
190
+ /** A person brought it back from the archive: it stays open until they put it away again. */
191
+ restored?: boolean;
192
+ /** What opened it, in the person's words — each named by the plugin that did; chips beside the title. */
193
+ origins?: string[];
194
+ createdAt: number;
195
+ updatedAt: number;
196
+ }
197
+ export type RunStatus = "running" | "done" | "asked" | "cancelled" | "error";
198
+ export interface RunView {
199
+ playbooks?: GeneralExecution["playbook"][];
200
+ execution?: GeneralExecution | null;
201
+ id: string;
202
+ threadId: string;
203
+ status: RunStatus;
204
+ engine: string;
205
+ stop: string | null;
206
+ error: string | null;
207
+ usage: {
208
+ inputTokens: number;
209
+ outputTokens: number;
210
+ } | null;
211
+ /** `usage` split into new input, cache read, cache write and output — what every surface shows. */
212
+ tokens?: import("./tokens.js").TokenBreakdown | null;
213
+ costUsd: number | null;
214
+ changes: number;
215
+ reverted: boolean;
216
+ keptOnRevert: number;
217
+ startedAt: number;
218
+ endedAt: number | null;
219
+ metrics?: import("./evaluation.js").TurnMetrics | null;
220
+ }
221
+ export interface CardView {
222
+ threadId: string;
223
+ id: string;
224
+ schema: string;
225
+ title: string;
226
+ data: unknown;
227
+ version: number;
228
+ updatedBy: Author | "human";
229
+ updatedAt: number;
230
+ }
231
+ export type UserInput = {
232
+ kind: "text";
233
+ text: string;
234
+ focus?: FocusRef[];
235
+ attachments?: import("./media.js").MediaRef[];
236
+ } | {
237
+ kind: "answer";
238
+ questionId: string;
239
+ value: string | string[];
240
+ };
241
+ export interface PendingView {
242
+ threadId: string;
243
+ kind: "ask" | "workflow" | "permission";
244
+ workflow: string | null;
245
+ question: {
246
+ id: string;
247
+ text: string;
248
+ options?: string[];
249
+ multiple?: boolean;
250
+ freeText?: boolean;
251
+ };
252
+ accepts: "options" | "text";
253
+ }
254
+ /** Pushed to the renderer over the `agent.event` broadcast. `progress` is transient — never persisted. */
255
+ export type AgentEvent = {
256
+ type: "permission-mode";
257
+ mode: string;
258
+ } | {
259
+ type: "run";
260
+ run: RunView;
261
+ } | {
262
+ type: "message";
263
+ message: ThreadMessage;
264
+ } | {
265
+ type: "delta";
266
+ runId: string;
267
+ text: string;
268
+ } | {
269
+ type: "progress";
270
+ runId: string;
271
+ activity?: string | null;
272
+ retry?: (RetryState & {
273
+ since: number;
274
+ }) | null;
275
+ usage?: {
276
+ inputTokens: number;
277
+ outputTokens: number;
278
+ };
279
+ elapsedMs?: number;
280
+ metrics?: RunView["metrics"];
281
+ } | {
282
+ type: "card";
283
+ card: CardView;
284
+ } | {
285
+ type: "pending";
286
+ pending: PendingView | null;
287
+ } | {
288
+ type: "done";
289
+ run: RunView;
290
+ };
291
+ export interface WorkflowInfo {
292
+ id: string;
293
+ title: string;
294
+ description: string;
295
+ /** Whether the workflow can start with no arguments — what a menu can offer directly. */
296
+ canStartEmpty: boolean;
297
+ }
298
+ /** Where a playbook came from. Same id from several sources is a conflict the user can settle in settings. */
299
+ export type PlaybookSource = "plugin" | "user" | "project";
300
+ /** Recommendations and prerequisites, never permission grants. */
301
+ export interface PlaybookRequirements {
302
+ workTypes?: ("coding" | "general")[];
303
+ requires?: string[];
304
+ optionalCapabilities?: string[];
305
+ outputs?: string[];
306
+ }
307
+ export interface GeneralExecution {
308
+ deliveryId: string;
309
+ playbook: {
310
+ id: string;
311
+ source: PlaybookSource;
312
+ digest: string;
313
+ body: string;
314
+ requires: string[];
315
+ };
316
+ }
317
+ /** A methodology document: the `when` line lives in every prompt, the body loads on demand. */
318
+ export interface Playbook extends PlaybookRequirements {
319
+ id: string;
320
+ title: string;
321
+ when: string;
322
+ tools: string[];
323
+ body: string;
324
+ }
325
+ export interface PlaybookIndexEntry extends PlaybookRequirements {
326
+ id: string;
327
+ title: string;
328
+ when: string;
329
+ tools: string[];
330
+ /** The source that won for this id. */
331
+ source: PlaybookSource;
332
+ /** Sources that also carry this id and lost — non-empty means a conflict. */
333
+ shadows: PlaybookSource[];
334
+ }
335
+ /** What a turn is for. A `code` turn edits a repository and gets the engine's fuller powers; a `backlog` turn talks to the app through tools. */
336
+ export type TurnPurpose = "backlog" | "code" | "general";
337
+ /** A ticket the current one waits on that is neither done nor cancelled: context for the agent, never a lock on it. */
338
+ export interface OpenDependency {
339
+ key: string;
340
+ title: string;
341
+ status: string;
342
+ }
343
+ export interface TurnInput {
344
+ threadId: string;
345
+ projectId: string;
346
+ /** The project the conversation belongs to, named so the model knows where it is. */
347
+ project?: {
348
+ name: string;
349
+ keyPrefix: string;
350
+ };
351
+ purpose: TurnPurpose;
352
+ permissionMode?: string;
353
+ /** The ticket a code thread implements — the branch it must stay on, the key its commits must name. */
354
+ ticket?: {
355
+ id: string;
356
+ key: string;
357
+ title: string;
358
+ branch: string | null;
359
+ workType?: "coding" | "general";
360
+ body?: string | null;
361
+ completionPolicy?: "merge" | "human";
362
+ waitsOn?: OpenDependency[];
363
+ };
364
+ execution?: GeneralExecution;
365
+ input: UserInput;
366
+ /** Full persisted history, the triggering user message included. */
367
+ messages: ThreadMessage[];
368
+ cards: CardView[];
369
+ brief: string | null;
370
+ /** A question still waiting that the platform did NOT route — off-script input lands here. */
371
+ pending: PendingView | null;
372
+ /** What was on the user's screen when they sent the triggering message; deixis resolves here. */
373
+ focus?: FocusRef[];
374
+ /** Parsed engine options from settings (validated against Engine.options). */
375
+ options: unknown;
376
+ /** Where a code-running engine works: a worktree of the workstream's repository. Absent for a thread without one. */
377
+ cwd?: string;
378
+ /**
379
+ * What this project's Files hold, as the Files plugin renders it for an
380
+ * agent. Discovery only: it tells the engine what is there to look up, and
381
+ * says nothing about what may be read — every path reads either way.
382
+ */
383
+ filesIndex?: {
384
+ markdown: string;
385
+ digest: string;
386
+ };
387
+ /** The project's rules, as the person wrote them: every engine follows them. */
388
+ rules?: string;
389
+ /** Ephemeral subprocess credentials supplied by plugins; never persist or include in prompts. */
390
+ env?: Record<string, string>;
391
+ workdir?: {
392
+ remote: string;
393
+ branch: string;
394
+ worktree: string | null;
395
+ };
396
+ /** The project's workstream repositories and where they are on this machine (null: no checkout here), for a main conversation to read. */
397
+ repositories?: {
398
+ workstream: string;
399
+ name: string;
400
+ path: string | null;
401
+ defaultBranch: string;
402
+ }[];
403
+ }
404
+ /** A model request failed and the engine is trying again; `attempt` counts the retries so far. */
405
+ export interface RetryState {
406
+ attempt: number;
407
+ max?: number;
408
+ reason: string;
409
+ /** How long until the next try, when the engine says. */
410
+ delayMs?: number;
411
+ }
412
+ export interface TurnIO {
413
+ media?: {
414
+ read(id: string): import("./media.js").MediaContent;
415
+ path(id: string): string;
416
+ publish(upload: import("./media.js").MediaUpload, caption?: string): import("./media.js").MediaRef;
417
+ };
418
+ permissionMode?(mode: string): void;
419
+ /** Pause this live run for a human response. Null means cancelled, never approval. */
420
+ requestPermission?(text: string, options: string[], signal?: AbortSignal): Promise<string | null>;
421
+ /** Persist + broadcast one finished block. Text streams through delta() first. */
422
+ emit(block: Block): void;
423
+ delta(text: string): void;
424
+ /** Live token totals for the working line; call whenever the count grows. */
425
+ progress(usage: {
426
+ inputTokens?: number;
427
+ outputTokens?: number;
428
+ }): void;
429
+ /** A tool the engine runs itself is under way (Tomrade's own tools report through `tools`); null when it ends. */
430
+ activity?(label: string | null): void;
431
+ /** A model request failed and will be tried again; null once one goes through. */
432
+ retry?(state: RetryState | null): void;
433
+ tools: {
434
+ describe(): ToolDescriptor[];
435
+ /** Run-bound and ledgered; throws on refusal or failure so the engine can feed the error back. */
436
+ execute(name: string, args: unknown): Promise<unknown>;
437
+ /** What each kit is for, by kit id (see ToolDefinition.kit). */
438
+ kits?(): Record<string, string>;
439
+ };
440
+ playbooks: {
441
+ index(): PlaybookIndexEntry[];
442
+ open(id: string): string | null;
443
+ };
444
+ workflows: {
445
+ list(): WorkflowInfo[];
446
+ };
447
+ llm: LlmService | null;
448
+ run: RunHandle;
449
+ signal: AbortSignal;
450
+ /** This engine's external session id from the previous turn, if it stored one. */
451
+ resume?: string;
452
+ /**
453
+ * Messages the person sent into this running turn, for an engine that `steers`. Call it where
454
+ * the model reads next (between tool calls); what it returns enters the transcript there.
455
+ */
456
+ takeSteers?(): string[];
457
+ /**
458
+ * For an engine whose agent keeps its own steering queue (pi, Codex, Claude Code): `forward`
459
+ * gets each message sent into this turn and resolves whether the agent took it. Call
460
+ * `steered(id)` when the agent reads it; that is where it enters the transcript.
461
+ */
462
+ steerTo?(forward: (steer: {
463
+ id: string;
464
+ text: string;
465
+ }) => Promise<boolean>): void;
466
+ steered?(id: string): void;
467
+ /**
468
+ * For an engine that keeps its own session: the conversation so far, to lead the first prompt of a
469
+ * session it starts without `resume` (empty when there is nothing before this message).
470
+ */
471
+ recap?(): string;
472
+ }
473
+ export interface TurnResult {
474
+ stop: "end" | "asked" | "budget" | "cancelled" | "error";
475
+ usage?: {
476
+ inputTokens?: number;
477
+ outputTokens?: number;
478
+ };
479
+ costUsd?: number;
480
+ error?: string;
481
+ /** Everything else known about the failure (see describeFailure); shown folded under `error`. */
482
+ details?: string;
483
+ /** External session id to store on the thread for the next turn. */
484
+ resume?: string;
485
+ /** The session in `resume` no longer exists and nothing ran: Tomrade forgets it and runs the turn again in a new one. */
486
+ lostSession?: boolean;
487
+ metrics?: import("./evaluation.js").TurnMetrics;
488
+ /** The engine stopped on a usage or rate limit; `until` is when it lifts, when the engine said. */
489
+ limit?: {
490
+ until: number | null;
491
+ };
492
+ }
493
+ /** A thread that stopped on a usage limit, waiting for it to lift. Kept on this machine only: it is this machine's clock that continues it. */
494
+ export interface ParkView {
495
+ threadId: string;
496
+ /** The run that hit the limit; null when a terminal session reported it. */
497
+ runId: string | null;
498
+ until: number;
499
+ /** Whether `until` came from the engine's own words, not a guess. */
500
+ known: boolean;
501
+ /** Tomrade continues the thread itself at `until`; otherwise the person is told it can go on. */
502
+ resume: boolean;
503
+ /** How many limited runs in a row this thread has had. */
504
+ attempt: number;
505
+ }
506
+ /**
507
+ * What an engine can be trusted with. `backlog` talks to the app through its
508
+ * tools; `code` also edits files and runs commands in a repository — only a
509
+ * local coding agent (Claude Code, Codex…) declares it, and only such an engine
510
+ * is offered when a ticket is started.
511
+ */
512
+ export type EngineCapability = "backlog" | "code" | "general";
513
+ /**
514
+ * What stands between an engine and its first turn, so the app can say how to fix it in the person's language.
515
+ * `repair`: the CLI is there but does not run.
516
+ */
517
+ export type EngineNeed = "install" | "repair" | "sign-in" | "model" | "service";
518
+ /** Where a local CLI candidate came from, in the order they are tried. */
519
+ export type CliSource = "manual" | "path" | "login-path" | "known";
520
+ export interface CliAttempt {
521
+ path: string;
522
+ source: CliSource;
523
+ ok: boolean;
524
+ /** The version it printed, or why it did not run. */
525
+ detail: string;
526
+ }
527
+ /** How a local agent's CLI was looked for and what was found — enough to show its state and to report it. */
528
+ export interface LocalCliStatus {
529
+ name: string;
530
+ state: "ready" | "missing" | "broken";
531
+ path?: string;
532
+ source?: CliSource;
533
+ version?: string;
534
+ /** Why the best candidate did not run, when none did. */
535
+ error?: string;
536
+ /** A path the person chose that no longer runs; detection carried on without it. */
537
+ manual?: {
538
+ path: string;
539
+ error: string;
540
+ };
541
+ /** Found only inside WSL, where a Windows app cannot run it. */
542
+ wsl?: string;
543
+ /** Not found, but this agent's desktop app is installed — which does not include the CLI. */
544
+ desktopApp?: string;
545
+ /** Not found, and this login shell did not answer in time: the folders its startup files add to PATH went unsearched. */
546
+ shellTimedOut?: string;
547
+ attempts: CliAttempt[];
548
+ /** Directories searched, in order. */
549
+ searched: string[];
550
+ platform: string;
551
+ arch: string;
552
+ /** The PATH the app itself runs with. */
553
+ pathEnv: string;
554
+ }
555
+ export interface EngineAvailability {
556
+ ok: boolean;
557
+ reason?: string;
558
+ need?: EngineNeed;
559
+ /** Present for engines that drive a CLI installed on this machine. */
560
+ cli?: LocalCliStatus;
561
+ /** What it can take on right now, when only part of it is ready; absent means all its capabilities. */
562
+ capabilities?: EngineCapability[];
563
+ /** How it installs on this machine, when that differs from the engine's declared setup — an installer that is not here is left out. */
564
+ setup?: EngineSetup;
565
+ /** The folder a local CLI reads its sign-in from, when it is not signed in (CODEX_HOME, CLAUDE_CONFIG_DIR). */
566
+ configDir?: string;
567
+ }
568
+ /**
569
+ * How a person makes a local agent ready on this machine, as commands: what
570
+ * setup screens show, copy and run in a terminal. Only what an engine declares
571
+ * here can be started in a terminal from the app.
572
+ */
573
+ export interface EngineSetup {
574
+ /** Install commands, the recommended one first. */
575
+ install: string[];
576
+ signIn?: string;
577
+ /** Signing in starts the agent itself, which then asks (`/login`); otherwise the command signs in and returns. */
578
+ signInRuns?: boolean;
579
+ docs?: string;
580
+ /** Offered on setup screens before it is installed. */
581
+ suggested?: boolean;
582
+ }
583
+ /**
584
+ * Tomrade's MCP server in the agent's own user-level configuration, so a
585
+ * session the person opens anywhere outside Tomrade can reach it. Written only
586
+ * when the person asks.
587
+ */
588
+ export interface EngineMcpLink {
589
+ status(): Promise<McpLinkView>;
590
+ /** `profile` names one of `status().profiles`; without it, the agent's own (the first). Answers as `status()` does. */
591
+ connect(profile?: string): Promise<McpLinkView>;
592
+ disconnect(profile?: string): Promise<McpLinkView>;
593
+ /** Whether the agent will load a `tomrade` server a repository's own config declares; some ask once per folder first. */
594
+ projectApproval?(dir: string): "approved" | "rejected" | "pending";
595
+ /** The same, for each of its profiles. */
596
+ projectApprovals?(dir: string): McpProjectApproval[];
597
+ /** A session of the agent started somewhere — its transcript names the profile it ran under. */
598
+ sawTranscript?(file: string): void;
599
+ }
600
+ export interface McpProjectApproval {
601
+ profile: string;
602
+ state: "approved" | "rejected" | "pending";
603
+ /** How to start the agent under that profile, for the person to run in the checkout. */
604
+ command: string;
605
+ }
606
+ export interface McpLinkView {
607
+ /** `stale`: a `tomrade` server is configured but at another address (an old port, the other Tomrade). */
608
+ state: "connected" | "missing" | "stale" | "unavailable";
609
+ /** The address this Tomrade serves; null while its MCP server is not running. */
610
+ url: string | null;
611
+ /** The address the agent's configuration names, when it names one. */
612
+ configuredUrl: string | null;
613
+ /** Where the agent keeps it, for the person to look at. */
614
+ file: string | null;
615
+ /** Commands that do the same by hand, for when the CLI cannot be run from here. */
616
+ manual: {
617
+ connect: string;
618
+ disconnect: string;
619
+ };
620
+ /** Why the last step could not be done or the state could not be read. */
621
+ error?: string;
622
+ /** The configuration folder this describes, for an agent that has several (CLAUDE_CONFIG_DIR per account). */
623
+ profile?: string;
624
+ /** Every profile, this one first, when there is more than one. */
625
+ profiles?: McpLinkView[];
626
+ }
627
+ /** A path the person typed or picked, run once to see whether it is the CLI. */
628
+ export interface CliCheck {
629
+ ok: boolean;
630
+ path: string;
631
+ version?: string;
632
+ error?: string;
633
+ }
634
+ /** Anything that can take one turn on a thread. The conductor seam. */
635
+ export interface Engine {
636
+ /** Actual formats supported by the selected transport/model; absent means text only. */
637
+ media?(options: unknown): {
638
+ input: string[];
639
+ output: string[];
640
+ };
641
+ id: string;
642
+ title: string;
643
+ author: Author;
644
+ /** Absent means `["backlog"]`. */
645
+ capabilities?: EngineCapability[];
646
+ /**
647
+ * Engines this one runs in their place: pickers list it alone, and a thread
648
+ * or setting naming one of them is answered by this engine.
649
+ */
650
+ includes?: string[];
651
+ /**
652
+ * No model judges what the user types: free text while a workflow waits is
653
+ * taken as that workflow's answer. A model engine instead reads the reply with
654
+ * the waiting question in its prompt and resumes or cancels the workflow itself.
655
+ */
656
+ literal?: boolean;
657
+ /**
658
+ * `fresh` looks again instead of answering from a recent probe — after the person installs or points at a CLI.
659
+ * `options`: whether it can run a turn with these engine options, such as the model a thread picked.
660
+ */
661
+ available(opts?: {
662
+ fresh?: boolean;
663
+ options?: unknown;
664
+ }): Promise<EngineAvailability>;
665
+ /** For an engine that runs a local CLI: whether this file is one it can use. */
666
+ checkCli?(file: string): Promise<CliCheck>;
667
+ setup?: EngineSetup;
668
+ /** Engine settings, drawn by the settings UI from the schema. */
669
+ options?: z.ZodType<unknown>;
670
+ /** Quick-pick knobs the panel offers; each writes options[key]. Called per view, so lists can be live. */
671
+ picks?(): EnginePick[];
672
+ /** `default` is what a session starts in until the person picks another mode. */
673
+ permissionModes?: {
674
+ default: string;
675
+ choices: {
676
+ value: string;
677
+ label: string;
678
+ }[];
679
+ };
680
+ /** False: it runs only inside Tomrade, with no CLI session a person can be handed in a terminal. */
681
+ terminal?: boolean;
682
+ /** True: a CLI Tomrade starts only in a terminal the person sees — handed the ticket, heard over MCP, never driven turn by turn. */
683
+ terminalOnly?: boolean;
684
+ /** True: in a terminal it reaches Tomrade only through its own user-level connection (`mcpLink`), so that comes first. */
685
+ terminalViaLink?: boolean;
686
+ /** True: that connection carries no session token, so the session's first act is claim_ticket, which joins it to the session Start made. */
687
+ terminalClaims?: boolean;
688
+ /** True: it runs on the model profiles set up in Tomrade, so a key or model error is fixed there — not in its own sign-in. */
689
+ usesModels?: boolean;
690
+ /** True: it takes messages sent while a turn runs (io.takeSteers); otherwise the panel queues them. */
691
+ steers?: boolean;
692
+ /** What the troubleshooter may read about this engine beyond `available()`. */
693
+ diagnostics?: EngineDiagnostics;
694
+ /** What the engine's own configuration lets its agent run; absent when Tomrade cannot read it. */
695
+ commands?: EngineCommands;
696
+ mcpLink?: EngineMcpLink;
697
+ turn(input: TurnInput, io: TurnIO): Promise<TurnResult>;
698
+ }
699
+ /**
700
+ * An engine's own evidence, for the troubleshooter and a report. Only what is
701
+ * named here is read: an agent investigating a failure gets no other files.
702
+ */
703
+ export interface EngineDiagnostics {
704
+ /** The agent's own log files, newest first; their tails go into a report. */
705
+ logs?(): string[] | Promise<string[]>;
706
+ /** Short facts that tell one setup from another — edition, endpoint, models offered. */
707
+ facts?(): Record<string, string> | Promise<Record<string, string>>;
708
+ }
709
+ /**
710
+ * Whether the engine's own permission settings let one shell command through,
711
+ * read from its config files — not a promise: a safety check or the remote can
712
+ * still refuse it at run time.
713
+ */
714
+ export interface EngineCommands {
715
+ check(command: string, opts: CommandCheckOptions): Promise<CommandCheck>;
716
+ /** Adds `check(...).fix.rule` to `fix.file`, then checks again. */
717
+ allow?(command: string, opts: CommandCheckOptions): Promise<CommandCheck>;
718
+ }
719
+ export interface CommandCheckOptions {
720
+ /** Where the agent will run, for project-level settings; null reads only the user's. */
721
+ cwd: string | null;
722
+ /** The run gets a fresh worktree of `cwd`: only checked-in project files reach it, not untracked local ones. */
723
+ worktree?: boolean;
724
+ /** The permission mode it will run under; absent, the one its settings default to. */
725
+ permissionMode?: string;
726
+ }
727
+ export interface CommandCheck {
728
+ /** `asks`: the person approves it when it comes up; `blocked`: refused, or likely to be, without a change. */
729
+ state: "allowed" | "asks" | "blocked";
730
+ /** Why, in a word a screen can name in its own language. */
731
+ why: "bypass" | "allow-rule" | "ask-rule" | "deny-rule" | "prompts" | "auto-mode" | "plan" | "dont-ask";
732
+ mode: string;
733
+ /** The rule that decided it, and the file it is in. */
734
+ rule?: {
735
+ rule: string;
736
+ file: string;
737
+ };
738
+ /** The one rule `allow` writes; absent when no added rule would help (a deny rule wins over it). */
739
+ fix?: {
740
+ rule: string;
741
+ file: string;
742
+ };
743
+ }
744
+ /** One quick-pick knob: "model" and "effort" keys get localized group labels; other keys show `title`. */
745
+ export interface EnginePick {
746
+ key: string;
747
+ title: string;
748
+ /** `searchesWeb`: the model searches the web on its own, so needs no web search key. */
749
+ choices: {
750
+ value: string;
751
+ label?: string;
752
+ searchesWeb?: boolean;
753
+ }[];
754
+ /** Choices are suggestions only — settings may hold any string (e.g. a model id the CLI knows). */
755
+ freeText?: boolean;
756
+ /** The engine is still asking its agent for the choices; ask again shortly. */
757
+ pending?: boolean;
758
+ }
759
+ /** One real turn on an engine, asked for by the person: does it answer? */
760
+ export interface EngineTry {
761
+ ok: boolean;
762
+ ms: number;
763
+ at: number;
764
+ /** What it said, cut short. */
765
+ reply?: string;
766
+ error?: string;
767
+ details?: string;
768
+ }
769
+ export interface EngineView {
770
+ id: string;
771
+ title: string;
772
+ capabilities: EngineCapability[];
773
+ ok: boolean;
774
+ reason?: string;
775
+ need?: EngineNeed;
776
+ cli?: LocalCliStatus;
777
+ configDir?: string;
778
+ /** The last try with the settings it has now; a change to its settings clears it. */
779
+ tried?: EngineTry;
780
+ picks?: EnginePick[];
781
+ permissionModes?: Engine["permissionModes"];
782
+ terminal?: boolean;
783
+ terminalOnly?: boolean;
784
+ usesModels?: boolean;
785
+ steers?: boolean;
786
+ setup?: EngineSetup;
787
+ /** Engines it answers for: a thread, run or setting may still name one of them. */
788
+ includes?: string[];
789
+ /** It can put Tomrade's MCP server in its own user-level configuration (`agent.mcpLink`). */
790
+ mcpLink?: boolean;
791
+ }
792
+ export interface WorkflowQuestion {
793
+ text: string;
794
+ options?: string[];
795
+ multiple?: boolean;
796
+ freeText?: boolean;
797
+ }
798
+ export type WorkflowTransition<S> = {
799
+ next: string;
800
+ state: S;
801
+ } | {
802
+ ask: WorkflowQuestion;
803
+ state: S;
804
+ } | {
805
+ done: unknown;
806
+ };
807
+ export interface WorkflowIO {
808
+ emit(block: Block): void;
809
+ tools: TurnIO["tools"];
810
+ llm: LlmService | null;
811
+ run: RunHandle;
812
+ /** Aborts when the run is stopped or runs out of time; a step's model calls end with it. */
813
+ signal: AbortSignal;
814
+ /** What a step is doing while no tool runs, for the run's live line; null clears it. */
815
+ activity(label: string | null): void;
816
+ }
817
+ /**
818
+ * A resumable, typed step machine, exposed to every engine as a tool. Control
819
+ * flow is code; steps may still call the worker model for content. A step that
820
+ * returns `ask` suspends the workflow — the platform persists the state and
821
+ * routes the user's next on-script input straight back into the same step.
822
+ */
823
+ export interface WorkflowSpec<I = unknown, S = unknown> {
824
+ /** Also the tool name — snake_case, globally unique. */
825
+ id: string;
826
+ title: string;
827
+ description: string;
828
+ input: z.ZodType<I>;
829
+ state: z.ZodType<S>;
830
+ entry: string;
831
+ start(input: I, io: WorkflowIO): S | Promise<S>;
832
+ steps: Record<string, (state: S, io: WorkflowIO, answer?: string | string[]) => WorkflowTransition<S> | Promise<WorkflowTransition<S>>>;
833
+ /** What counts as on-script input while suspended: one of the offered options, or any text. */
834
+ accepts?: "options" | "text";
835
+ /** Offered only to engines without a model (Engine.literal); a model holds this conversation itself, in the user's language. */
836
+ literal?: boolean;
837
+ }
838
+ /**
839
+ * Consulted when a turn ends, while its run is still open, so whatever it starts
840
+ * writes into the same run and asks like any other workflow. `canAsk` is false
841
+ * when the run already ends on a question, failed or was cancelled; a follow-up
842
+ * then only notes what it still owes the thread.
843
+ */
844
+ export interface TurnFollowUp {
845
+ id: string;
846
+ after(turn: {
847
+ projectId: string;
848
+ threadId: string;
849
+ runId: string;
850
+ canAsk: boolean;
851
+ }): FollowUpStart | null | Promise<FollowUpStart | null>;
852
+ }
853
+ export interface FollowUpStart {
854
+ workflow: string;
855
+ args?: unknown;
856
+ }
857
+ /** What a suspending tool call returns; engines end their turn when they see one. */
858
+ export interface Suspended {
859
+ __suspended: true;
860
+ question: {
861
+ id: string;
862
+ text: string;
863
+ options?: string[];
864
+ };
865
+ note: string;
866
+ }
867
+ export declare const isSuspended: (value: unknown) => value is Suspended;
868
+ declare module "cordis" {
869
+ interface Events {
870
+ "agent/prepare-turn"(input: TurnInput): Promise<void> | void;
871
+ /** The brief a CLI agent starts a delivery from, before it is handed over. A plugin may add context an in-app turn would get from `agent/prepare-turn`. */
872
+ "agent/prepare-brief"(brief: {
873
+ projectId: string;
874
+ itemId: string;
875
+ filesIndex?: string;
876
+ }): Promise<void> | void;
877
+ /** A run began driving a thread. */
878
+ "agent/run-started"(run: RunView, thread: ThreadSummary): void;
879
+ /** A run settled, however it stopped; `run.stop` says how. */
880
+ "agent/run-ended"(run: RunView, thread: ThreadSummary): void;
881
+ /** A thread stopped on a usage limit and waits for it to lift. */
882
+ "agent/parked"(park: ParkView, thread: ThreadSummary): void;
883
+ /** A parked thread's limit lifted and nobody continues it on its own: the person may. */
884
+ "agent/limit-lifted"(park: ParkView, thread: ThreadSummary): void;
885
+ /** A project's rules file was written through Tomrade. */
886
+ "agent/rules-changed"(projectId: string): void;
887
+ /**
888
+ * Each model request an in-app engine (`engine-loop`) is about to send: a listener may change its
889
+ * system prompt, tools and messages, before or after `next()`; what comes back is sent. CLI agents
890
+ * run their own loop and send nothing through here.
891
+ */
892
+ "agent/request"(request: AgentRequest, next: () => Promise<AgentRequest>): Promise<AgentRequest>;
893
+ /** A model request an in-app engine sent failed. The first listener to answer decides what to try instead. */
894
+ "agent/request-error"(failure: AgentRequestFailure): Promise<AgentRetry | void> | AgentRetry | void;
895
+ /** An in-app turn is about to end. The first listener to answer keeps it going, with a message to the model. */
896
+ "agent/turn-stopping"(ending: TurnEnding): Promise<{
897
+ continue: string;
898
+ } | void> | {
899
+ continue: string;
900
+ } | void;
901
+ /**
902
+ * A CLI agent's hook reached Tomrade — Claude Code, Codex, dsh and the rest. The first listener to
903
+ * answer decides; the answer reaches the agent only when `answerable` (a session Tomrade started).
904
+ */
905
+ "agent/cli-hook"(call: CliHookCall): Promise<CliHookAnswer | void> | CliHookAnswer | void;
906
+ }
907
+ }
908
+ /** What a CLI agent's hook says happened, in one set of names whichever agent sent it. */
909
+ export type CliHookEvent = "session-start" | "prompt" | "pre-tool" | "post-tool" | "stop" | "session-end";
910
+ export interface CliHookCall {
911
+ event: CliHookEvent;
912
+ /** The agent that sent it: `claude-code`, `codex`, `dsh`, `pi`, `workbuddy`, `cursor`, `opencode`, `hermes`… */
913
+ agent: string;
914
+ /** Whether an answer reaches the agent: true in a session Tomrade started, false for a connected repository's hooks. */
915
+ answerable: boolean;
916
+ projectId: string;
917
+ /** Tomrade's code session the hook belongs to. */
918
+ sessionId: string;
919
+ /** The ticket the session works, when it has one. */
920
+ ticket: string | null;
921
+ cwd: string | null;
922
+ tool?: {
923
+ name: string;
924
+ input: unknown;
925
+ output?: string;
926
+ error?: string;
927
+ };
928
+ /** What the model said last, at `stop`. */
929
+ text?: string;
930
+ /** The hook's own payload, as the agent sent it. */
931
+ raw: Record<string, unknown>;
932
+ }
933
+ /** Refuse the tool (`pre-tool`), add context for the model, or keep the turn going (`stop`). */
934
+ export interface CliHookAnswer {
935
+ deny?: string;
936
+ context?: string;
937
+ continue?: string;
938
+ }
939
+ /** Which turn, step and model profile a request belongs to: read, never changed. */
940
+ export interface AgentTurnInfo {
941
+ runId: string;
942
+ threadId: string;
943
+ projectId: string;
944
+ purpose: TurnPurpose;
945
+ profile: string;
946
+ step: number;
947
+ }
948
+ /** One model request as an in-app engine is about to send it. */
949
+ export interface AgentRequest {
950
+ system: string;
951
+ tools: ToolSpec[];
952
+ messages: Message[];
953
+ turn: AgentTurnInfo;
954
+ }
955
+ export interface AgentRequestFailure {
956
+ error: unknown;
957
+ request: AgentRequest;
958
+ /** How many times this step was already tried again on a listener's answer. */
959
+ attempt: number;
960
+ }
961
+ /** Send the request again: changed, on another model profile, or both. */
962
+ export interface AgentRetry {
963
+ request?: Partial<Pick<AgentRequest, "system" | "tools" | "messages">>;
964
+ profile?: string;
965
+ }
966
+ export interface TurnEnding {
967
+ /** What the model said last. */
968
+ text: string;
969
+ turn: AgentTurnInfo;
970
+ /** How many times this turn was already kept going. */
971
+ continued: number;
972
+ }
973
+ export interface AgentPolicy {
974
+ id: string;
975
+ /** Runs on tools/pre-execute for calls that belong to an agent run. Throw to refuse; the message reaches the model. */
976
+ before(call: {
977
+ name: string;
978
+ args: unknown;
979
+ by: Author;
980
+ projectId?: string;
981
+ run: RunHandle;
982
+ }, next: () => Promise<unknown>): Promise<unknown>;
983
+ }
984
+ //# sourceMappingURL=types.d.ts.map