@anhlp/alp-protocol 0.0.0-stage → 0.0.1-beta.4

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.
Files changed (47) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +7 -0
  3. package/README.md +16 -2
  4. package/conformance/cases.json +951 -0
  5. package/conformance/run.d.mts +32 -0
  6. package/conformance/run.mjs +403 -0
  7. package/dist/connection.d.ts +13 -0
  8. package/dist/connection.js +0 -0
  9. package/dist/contract-v1.d.ts +8783 -0
  10. package/dist/contract-v1.js +5 -0
  11. package/dist/contract.d.ts +25 -0
  12. package/dist/contract.js +28 -0
  13. package/dist/dto.d.ts +104 -0
  14. package/dist/dto.js +0 -0
  15. package/dist/events.d.ts +354 -0
  16. package/dist/events.js +0 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.js +2 -0
  19. package/dist/public.d.ts +1 -0
  20. package/dist/public.js +2 -0
  21. package/dist/rpc.d.ts +1210 -0
  22. package/dist/rpc.js +0 -0
  23. package/dist/schedule.d.ts +43 -0
  24. package/dist/schedule.js +0 -0
  25. package/dist/session.d.ts +285 -0
  26. package/dist/session.js +0 -0
  27. package/dist/validate.d.ts +12 -0
  28. package/dist/validate.js +151 -0
  29. package/dist/wire.d.ts +192 -0
  30. package/dist/wire.js +137 -0
  31. package/fixtures/ipc-v1.jsonl +4 -0
  32. package/fixtures/v1/cases.json +7166 -0
  33. package/package.json +127 -4
  34. package/schema/ipc-v1.json +3271 -0
  35. package/schema/methods-v1.json +8678 -0
  36. package/src/connection.ts +13 -0
  37. package/src/contract-v1.js +2 -0
  38. package/src/contract.js +44 -0
  39. package/src/dto.ts +78 -0
  40. package/src/events.ts +207 -0
  41. package/src/index.ts +7 -0
  42. package/src/public.js +2 -0
  43. package/src/rpc.ts +272 -0
  44. package/src/schedule.ts +43 -0
  45. package/src/session.ts +213 -0
  46. package/src/validate.js +115 -0
  47. package/src/wire.js +213 -0
package/dist/rpc.d.ts ADDED
@@ -0,0 +1,1210 @@
1
+ /** Portable JSON-RPC structures; implementations and provider SDKs stay outside protocol. */
2
+ import type { RuntimeKind, Pin } from './dto.js';
3
+ import type { AlpError, Envelope, SessionSnapshot, TreeStatus, TurnAttention, UserQuestion } from './events.js';
4
+ import type { ConfigureOutcome, ConfigurePreview, ConversationBranch, ForkProvenance, PauseState, PromptContent, RecallAnswer, ResumeCommand, SessionPreview, SessionSpec, SessionSummary } from './session.js';
5
+ import type { EntryName, LanguageSetting, LibraryEntryRow, LibraryKind, LibraryScope, LibrarySource, TaskRow, TeamRow } from './dto.js';
6
+ import type { ScheduleJob, ScheduleStatus } from './schedule.js';
7
+ export type RpcId = string | number | null;
8
+ export type RpcErrorData = {
9
+ code: number;
10
+ message: string;
11
+ data?: unknown;
12
+ };
13
+ export type RpcRequest = {
14
+ jsonrpc: '2.0';
15
+ id?: RpcId;
16
+ method: string;
17
+ params?: Record<string, unknown>;
18
+ };
19
+ export type RpcResponse = {
20
+ jsonrpc: '2.0';
21
+ id: RpcId;
22
+ } & ({
23
+ result: unknown;
24
+ error?: never;
25
+ } | {
26
+ error: RpcErrorData;
27
+ result?: never;
28
+ });
29
+ export type EventNotification = {
30
+ jsonrpc: '2.0';
31
+ method: 'event';
32
+ params: Envelope;
33
+ };
34
+ /** Why a terminal ended (§12.10). */
35
+ export type TerminalEndReason = 'exited' | 'closed' | 'idle' | 'lifetime' | 'detached' | 'revoked' | 'disabled' | 'shutdown';
36
+ /** A terminal as clients see it (capability `terminal`, §12.10): never a process id, shell path or host path. */
37
+ export type TerminalInfo = {
38
+ terminalId: string;
39
+ workspaceId: string;
40
+ title: string;
41
+ cols: number;
42
+ rows: number;
43
+ state: 'running' | 'exited';
44
+ attached: boolean;
45
+ createdAt: string;
46
+ lastActivityAt: string;
47
+ expiresAt: string;
48
+ exitCode?: number | null;
49
+ signal?: number | null;
50
+ endReason?: TerminalEndReason;
51
+ };
52
+ /** Effective terminal limits a daemon.hello advertises with capability `terminal`. */
53
+ export type TerminalLimits = {
54
+ maxPerPrincipal: number;
55
+ maxPerDaemon: number;
56
+ maxInputBytes: number;
57
+ maxOutputFrameBytes: number;
58
+ replayBytes: number;
59
+ idleMs: number;
60
+ maxLifetimeMs: number;
61
+ reconnectMs: number;
62
+ };
63
+ /** params of a terminal.event notification, sent only to the connection attached to the terminal. */
64
+ export type TerminalEvent = {
65
+ terminalId: string;
66
+ type: 'output';
67
+ seq: number;
68
+ data: string;
69
+ } | {
70
+ terminalId: string;
71
+ type: 'exit';
72
+ seq: number;
73
+ exitCode: number | null;
74
+ signal: number | null;
75
+ reason: TerminalEndReason;
76
+ } | {
77
+ terminalId: string;
78
+ type: 'detached';
79
+ reason: 'replaced' | 'slow_consumer';
80
+ };
81
+ export type TerminalNotification = {
82
+ jsonrpc: '2.0';
83
+ method: 'terminal.event';
84
+ params: TerminalEvent;
85
+ };
86
+ /** Names a daemon may list in its daemon.hello result (schema/methods-v1.json `capabilities`). */
87
+ export type HistoryItem = Envelope | {
88
+ cursor: number;
89
+ oversized: {
90
+ bytes: number;
91
+ };
92
+ };
93
+ export type HistoryPage = {
94
+ generation: string;
95
+ items: HistoryItem[];
96
+ olderCursor?: string;
97
+ hasOlder: boolean;
98
+ tipCursor: number;
99
+ };
100
+ export type Capability = 'session.interrupt.turn' | 'session.fork' | 'session.rewind' | 'session.reload' | 'session.resumeCommand' | 'session.configure.live' | 'terminal' | 'attachments' | 'workspace.git' | 'workspace.worktree' | 'workspace.registry' | 'session.history' | 'attach.after' | 'prompt.receipts' | 'extension.methods' | 'team' | 'task' | 'library' | 'library.project.write' | 'settings.language' | 'session.list.page' | 'question.watch' | 'question.watch.turns' | 'session.attention' | 'workspace.files' | 'workspace.files.read' | 'prompt.images' | 'schedule';
101
+ /** `data.reason` values alpd sets on errors (docs/protocol/alp-json-rpc-v1.md §5.3). */
102
+ export type ErrorReason = 'hello_required' | 'protocol_version' | 'invalid_params' | 'invalid_id' | 'batch_unsupported' | 'frame_too_large' | 'invalid_cursor' | 'stale_cursor' | 'history_gap' | 'stale_revision' | 'request_conflict' | 'cwd_mismatch' | 'workspace_mismatch' | 'corrupt_registry' | 'identity_changed' | 'response_too_large' | 'persistence_failed' | 'outcome_unknown' | 'key_conflict' | 'invalid_path' | 'invalid_scope' | 'invalid_entry' | 'secret_marker' | 'not_alp_project' | 'unknown_task' | 'not_found' | 'library_policy' | 'unsafe_path' | 'revision_conflict' | 'task_state' | 'in_use' | 'busy' | 'unreadable' | 'not_directory' | 'blocked_path' | 'ignored' | 'linked' | 'binary' | 'too_large' | 'not_repository' | 'unknown_ref' | 'invalid_compare' | 'stale_snapshot' | 'git_unavailable' | 'git_failed' | 'invalid_name' | 'invalid_data' | 'attachment_too_large' | 'offset_mismatch' | 'chunk_conflict' | 'upload_incomplete' | 'hash_mismatch' | 'upload_expired' | 'quota_exceeded' | 'attachment_referenced' | 'attachment_limit' | 'attachment_unavailable' | 'attachment_unsupported' | 'session_busy' | 'stale_generation' | 'turn_not_found' | 'turn_in_progress' | 'empty_prefix' | 'boundary_unmapped' | 'runtime_unsupported' | 'native_refused' | 'branch_not_found' | 'attachment_forbidden' | 'session_closed' | 'no_history' | 'runtime_mismatch' | 'agent_mismatch' | 'permission_escalation' | 'team_not_found' | 'terminal_disabled' | 'terminal_unsupported' | 'terminal_limit' | 'not_attached' | 'terminal_exited' | 'input_too_large';
103
+ type Empty = Record<string, never>;
104
+ /**
105
+ * Params and result of every v1 method, as schema/methods-v1.json defines them; the protocol tests
106
+ * type-check the golden fixtures against this map, so the two cannot drift. Results may carry
107
+ * properties this map does not name: clients ignore them.
108
+ */
109
+ export type RegistryProject = {
110
+ id: string;
111
+ root: string;
112
+ canonical?: string;
113
+ customName?: string | null;
114
+ addedAt: string;
115
+ removedAt?: string | null;
116
+ revision?: number;
117
+ };
118
+ export type RegistryWorkspace = {
119
+ branch?: string | null;
120
+ managedWorktree?: boolean;
121
+ id: string;
122
+ projectId: string;
123
+ directory: string;
124
+ canonical?: string;
125
+ title?: string | null;
126
+ pinnedAt?: string | null;
127
+ archivedAt?: string | null;
128
+ labels: string[];
129
+ createdAt: string;
130
+ revision?: number;
131
+ idempotencyKey?: string;
132
+ };
133
+ /** workspace.git.status and workspace.git.diff (capability `workspace.git`, §12.5): paths are relative to the workspace, never host paths. */
134
+ export type GitChange = 'modified' | 'added' | 'deleted' | 'renamed' | 'copied' | 'typechange' | 'untracked' | 'conflicted';
135
+ export type GitCompare = {
136
+ mode: 'uncommitted' | 'staged' | 'unstaged' | 'base';
137
+ baseRef?: string;
138
+ ignoreWhitespace?: boolean;
139
+ };
140
+ export type GitStatusFile = {
141
+ path: string;
142
+ oldPath?: string;
143
+ change: GitChange;
144
+ staged: boolean;
145
+ unstaged: boolean;
146
+ };
147
+ export type GitStatus = {
148
+ state: 'repository' | 'not_repository' | 'unsupported';
149
+ reason?: string;
150
+ branch: string | null;
151
+ detached: boolean;
152
+ head: string | null;
153
+ upstream: {
154
+ ref: string;
155
+ ahead: number;
156
+ behind: number;
157
+ } | null;
158
+ baseRef: string | null;
159
+ aheadBehind: {
160
+ ahead: number;
161
+ behind: number;
162
+ } | null;
163
+ hasRemote: boolean;
164
+ remoteUrl: string | null;
165
+ dirty: boolean;
166
+ files: GitStatusFile[];
167
+ hiddenCount: number;
168
+ truncated: boolean;
169
+ snapshot: string;
170
+ };
171
+ export type GitDiffLine = {
172
+ type: 'add' | 'remove' | 'context';
173
+ content: string;
174
+ noNewline?: true;
175
+ };
176
+ export type GitDiffHunk = {
177
+ oldStart: number;
178
+ oldCount: number;
179
+ newStart: number;
180
+ newCount: number;
181
+ context?: string;
182
+ lines: GitDiffLine[];
183
+ };
184
+ export type GitDiffFile = {
185
+ path: string;
186
+ oldPath?: string;
187
+ change: GitChange;
188
+ kind: 'text' | 'binary' | 'too_large' | 'withheld' | 'symlink' | 'submodule';
189
+ additions: number;
190
+ deletions: number;
191
+ hunks: GitDiffHunk[];
192
+ };
193
+ export type GitDiffPage = {
194
+ compare: {
195
+ mode: GitCompare['mode'];
196
+ baseRef?: string;
197
+ ignoreWhitespace: boolean;
198
+ };
199
+ snapshot: string;
200
+ files: GitDiffFile[];
201
+ hiddenCount: number;
202
+ truncated: boolean;
203
+ nextCursor?: string;
204
+ };
205
+ /**
206
+ * Capability `attachments` (§12.6). Principal context is set only by a trusted gateway on its own
207
+ * connection; a local client leaves it out and acts as the owner.
208
+ */
209
+ export type PrincipalContext = {
210
+ principalId?: string;
211
+ principalEpoch?: string;
212
+ authorizedRoots?: string[];
213
+ };
214
+ /** A committed attachment as clients see it: ids and display metadata, never a host path or URL. */
215
+ export type AttachmentInfo = {
216
+ attachmentId: string;
217
+ sessionId: string;
218
+ name: string;
219
+ mimeType: string;
220
+ detectedMimeType: string;
221
+ size: number;
222
+ sha256: string;
223
+ createdAt: string;
224
+ committedAt: string;
225
+ referenced: boolean;
226
+ };
227
+ /** Effective attachment limits a daemon.hello advertises with capability `attachments`. */
228
+ export type AttachmentLimits = {
229
+ maxObjectBytes: number;
230
+ maxPerPrompt: number;
231
+ maxPromptBytes: number;
232
+ maxChunkBytes: number;
233
+ maxActiveUploads: number;
234
+ maxPrincipalBytes: number;
235
+ maxDaemonBytes: number;
236
+ uploadTimeoutMs: number;
237
+ unreferencedRetentionMs: number;
238
+ releasedGraceMs: number;
239
+ };
240
+ /** Where a fork cuts the source conversation (§12.7): after a completed turn, or before a turn. */
241
+ export type ForkBoundary = {
242
+ throughTurnId: string;
243
+ } | {
244
+ beforeTurnId: string;
245
+ };
246
+ export type RegistryMutation = {
247
+ requestId: string;
248
+ ifRevision: number;
249
+ };
250
+ export type RegistryPage = {
251
+ revision: number;
252
+ total: number;
253
+ nextCursor: string | null;
254
+ };
255
+ export type RpcMethodMap = {
256
+ 'terminal.create': {
257
+ params: PrincipalContext & {
258
+ workspaceId: string;
259
+ cols: number;
260
+ rows: number;
261
+ requestId: string;
262
+ };
263
+ result: {
264
+ terminal: TerminalInfo;
265
+ };
266
+ };
267
+ 'terminal.list': {
268
+ params: PrincipalContext & {
269
+ workspaceId?: string;
270
+ };
271
+ result: {
272
+ terminals: TerminalInfo[];
273
+ };
274
+ };
275
+ 'terminal.attach': {
276
+ params: PrincipalContext & {
277
+ terminalId: string;
278
+ after?: number;
279
+ };
280
+ result: {
281
+ terminal: TerminalInfo;
282
+ nextSeq: number;
283
+ gap?: {
284
+ fromSeq: number;
285
+ toSeq: number;
286
+ };
287
+ };
288
+ };
289
+ 'terminal.input': {
290
+ params: PrincipalContext & {
291
+ terminalId: string;
292
+ data: string;
293
+ };
294
+ result: {
295
+ bytes: number;
296
+ };
297
+ };
298
+ 'terminal.resize': {
299
+ params: PrincipalContext & {
300
+ terminalId: string;
301
+ cols: number;
302
+ rows: number;
303
+ };
304
+ result: {
305
+ cols: number;
306
+ rows: number;
307
+ };
308
+ };
309
+ 'terminal.close': {
310
+ params: PrincipalContext & {
311
+ terminalId: string;
312
+ };
313
+ result: {
314
+ closed: true;
315
+ };
316
+ };
317
+ 'terminal.revoke': {
318
+ params: {
319
+ principalId: string;
320
+ };
321
+ result: {
322
+ closed: number;
323
+ };
324
+ };
325
+ 'attachment.begin': {
326
+ params: PrincipalContext & {
327
+ sessionId: string;
328
+ name: string;
329
+ size: number;
330
+ mimeType: string;
331
+ sha256?: string;
332
+ requestId: string;
333
+ };
334
+ result: {
335
+ uploadId: string;
336
+ state: 'uploading' | 'committed';
337
+ received: number;
338
+ size: number;
339
+ maxChunkBytes: number;
340
+ expiresAt?: string;
341
+ attachmentId?: string;
342
+ };
343
+ };
344
+ 'attachment.chunk': {
345
+ params: PrincipalContext & {
346
+ uploadId: string;
347
+ offset: number;
348
+ data: string;
349
+ };
350
+ result: {
351
+ received: number;
352
+ size: number;
353
+ };
354
+ };
355
+ 'attachment.commit': {
356
+ params: PrincipalContext & {
357
+ uploadId: string;
358
+ sha256: string;
359
+ };
360
+ result: {
361
+ attachment: AttachmentInfo;
362
+ };
363
+ };
364
+ 'attachment.abort': {
365
+ params: PrincipalContext & {
366
+ uploadId: string;
367
+ };
368
+ result: {
369
+ aborted: true;
370
+ };
371
+ };
372
+ 'attachment.get': {
373
+ params: PrincipalContext & {
374
+ attachmentId: string;
375
+ };
376
+ result: {
377
+ attachment: AttachmentInfo;
378
+ };
379
+ };
380
+ 'attachment.delete': {
381
+ params: PrincipalContext & {
382
+ attachmentId: string;
383
+ };
384
+ result: {
385
+ deleted: true;
386
+ };
387
+ };
388
+ 'workspace.git.status': {
389
+ params: {
390
+ workspaceId: string;
391
+ };
392
+ result: GitStatus;
393
+ };
394
+ 'workspace.git.diff': {
395
+ params: {
396
+ workspaceId: string;
397
+ compare: GitCompare;
398
+ path?: string;
399
+ cursor?: string;
400
+ };
401
+ result: GitDiffPage;
402
+ };
403
+ 'workspace.worktree.options': {
404
+ params: {
405
+ workspaceId: string;
406
+ };
407
+ result: {
408
+ mappingIds: string[];
409
+ };
410
+ };
411
+ 'workspace.worktree.create': {
412
+ params: RegistryMutation & {
413
+ workspaceId: string;
414
+ mappingId: string;
415
+ };
416
+ result: {
417
+ workspace: RegistryWorkspace;
418
+ revision: number;
419
+ };
420
+ };
421
+ 'workspace.worktree.remove': {
422
+ params: RegistryMutation & {
423
+ workspaceId: string;
424
+ };
425
+ result: {
426
+ workspace: RegistryWorkspace;
427
+ revision: number;
428
+ filesRemoved: true;
429
+ branchRetained: true;
430
+ };
431
+ };
432
+ 'registry.worktree': {
433
+ params: {
434
+ principalId?: string;
435
+ principalEpoch?: string;
436
+ mappingIds?: string[];
437
+ authorizedRoots?: string[];
438
+ };
439
+ result: {
440
+ mappings: Array<{
441
+ id: string;
442
+ source: string;
443
+ }>;
444
+ workspaces: unknown[];
445
+ };
446
+ };
447
+ 'project.list': {
448
+ params: {
449
+ cursor?: string;
450
+ limit?: number;
451
+ projectRoots?: string[];
452
+ };
453
+ result: RegistryPage & {
454
+ projects: RegistryProject[];
455
+ };
456
+ };
457
+ 'workspace.list': {
458
+ params: {
459
+ cursor?: string;
460
+ limit?: number;
461
+ projectRoots?: string[];
462
+ };
463
+ result: RegistryPage & {
464
+ workspaces: RegistryWorkspace[];
465
+ };
466
+ };
467
+ 'workspace.get': {
468
+ params: {
469
+ workspaceId: string;
470
+ };
471
+ result: {
472
+ workspace: RegistryWorkspace;
473
+ revision: number;
474
+ };
475
+ };
476
+ 'project.register': {
477
+ params: RegistryMutation & {
478
+ directory: string;
479
+ };
480
+ result: {
481
+ project: RegistryProject;
482
+ revision: number;
483
+ };
484
+ };
485
+ 'project.remove': {
486
+ params: RegistryMutation & {
487
+ projectId: string;
488
+ };
489
+ result: {
490
+ project: RegistryProject;
491
+ revision: number;
492
+ };
493
+ };
494
+ 'workspace.update': {
495
+ params: RegistryMutation & {
496
+ workspaceId: string;
497
+ title?: string | null;
498
+ pinned?: boolean;
499
+ archived?: boolean;
500
+ };
501
+ result: {
502
+ workspace: RegistryWorkspace;
503
+ revision: number;
504
+ };
505
+ };
506
+ 'registry.owner': {
507
+ params: {
508
+ operation?: string;
509
+ args?: Record<string, unknown>;
510
+ requestId?: string;
511
+ ifRevision?: number;
512
+ };
513
+ result: {
514
+ registry: Record<string, unknown>;
515
+ };
516
+ };
517
+ 'daemon.hello': {
518
+ params: {
519
+ protocolVersion: 1;
520
+ client?: {
521
+ name: string;
522
+ version: string;
523
+ };
524
+ capabilities?: string[];
525
+ };
526
+ result: {
527
+ protocolVersion: 1;
528
+ daemonVersion?: string;
529
+ pid?: number; /** The daemon host's machine name (os.hostname()), a label for clients. */
530
+ hostname?: string;
531
+ promptImageLimits?: {
532
+ maxCount: number;
533
+ maxBytes: number;
534
+ maxTotalBytes: number;
535
+ };
536
+ attachmentLimits?: AttachmentLimits;
537
+ terminalLimits?: TerminalLimits;
538
+ capabilities?: string[];
539
+ };
540
+ };
541
+ 'daemon.status': {
542
+ params: Empty;
543
+ result: {
544
+ pid: number;
545
+ startedAt: string;
546
+ version: string;
547
+ protocolVersion: number;
548
+ sessions: number;
549
+ questions: number;
550
+ previousExit?: {
551
+ kind: 'clean' | 'crash';
552
+ at?: string;
553
+ };
554
+ persistence?: Record<string, unknown>;
555
+ health: {
556
+ live: boolean;
557
+ ready: boolean;
558
+ checks: Array<{
559
+ id: string;
560
+ ok: boolean;
561
+ code?: string;
562
+ message?: string;
563
+ }>;
564
+ };
565
+ };
566
+ };
567
+ 'daemon.shutdown': {
568
+ params: Empty;
569
+ result: Empty;
570
+ };
571
+ 'daemon.pause': {
572
+ params: {
573
+ runtime?: RuntimeKind;
574
+ now?: boolean;
575
+ reason?: string;
576
+ };
577
+ result: PauseState;
578
+ };
579
+ 'daemon.resume': {
580
+ params: {
581
+ runtime?: RuntimeKind;
582
+ };
583
+ result: PauseState;
584
+ };
585
+ 'daemon.pauses': {
586
+ params: Empty;
587
+ result: PauseState;
588
+ };
589
+ 'daemon.usage': {
590
+ params: {
591
+ refresh?: boolean;
592
+ };
593
+ result: {
594
+ providers: Array<Record<string, unknown>>;
595
+ };
596
+ };
597
+ 'catalog.get': {
598
+ params: Empty;
599
+ result: {
600
+ models: Array<Record<string, unknown>>;
601
+ modes: Array<Record<string, unknown>>;
602
+ thinkingOptions?: unknown;
603
+ defaultModel: string;
604
+ };
605
+ };
606
+ 'assignment.recall': {
607
+ params: {
608
+ assignmentId?: string;
609
+ taskId?: string;
610
+ projectRoot?: string;
611
+ question: string;
612
+ };
613
+ result: RecallAnswer;
614
+ };
615
+ 'board.list': {
616
+ params: {
617
+ projectRoot: string;
618
+ };
619
+ result: {
620
+ pins: Pin[];
621
+ };
622
+ };
623
+ 'question.list': {
624
+ params: {
625
+ projectRoot?: string;
626
+ };
627
+ result: {
628
+ questions: UserQuestion[];
629
+ };
630
+ };
631
+ /** Capability `question.watch`: the waiting questions now; then this connection is told of every tree's `question` and `question.resolved` (of `projectRoot` when given). `turns` (capability `question.watch.turns`): also every root's `turn.ended`. `attention` (capability `session.attention`): also each root's attention, now and as it changes. */
632
+ 'question.watch': {
633
+ params: {
634
+ projectRoot?: string;
635
+ turns?: boolean;
636
+ attention?: boolean;
637
+ };
638
+ result: {
639
+ questions: UserQuestion[];
640
+ attention?: Array<{
641
+ sessionId: string;
642
+ attention: TurnAttention;
643
+ }>;
644
+ };
645
+ };
646
+ /** Capability `session.attention`: acknowledges a root's attention for every client; with `at`, only that one (a later turn's stays). */
647
+ 'session.attention.clear': {
648
+ params: {
649
+ sessionId: string;
650
+ at?: string;
651
+ };
652
+ result: {
653
+ cleared: boolean;
654
+ };
655
+ };
656
+ /** Capability `workspace.files`: files under `projectRoot` whose relative path matches `query`, best first; read-only, links not followed. */
657
+ 'workspace.files.search': {
658
+ params: {
659
+ projectRoot: string;
660
+ query?: string;
661
+ limit?: number;
662
+ };
663
+ result: {
664
+ files: Array<{
665
+ path: string;
666
+ }>;
667
+ truncated?: boolean;
668
+ };
669
+ };
670
+ /** Capability `workspace.files.read`: the entries of a directory under `projectRoot` (its root when `path` is left out); links, blocked and (unless `includeIgnored`) .gitignore'd entries left out. */
671
+ 'workspace.files.list': {
672
+ params: {
673
+ projectRoot: string;
674
+ path?: string;
675
+ includeIgnored?: boolean;
676
+ };
677
+ result: {
678
+ entries: Array<{
679
+ name: string;
680
+ path: string;
681
+ kind: 'file' | 'directory';
682
+ size?: number;
683
+ modifiedAt?: string;
684
+ }>;
685
+ truncated?: boolean;
686
+ };
687
+ };
688
+ /** Capability `workspace.files.read`: a UTF-8 text file under `projectRoot`, at most 1 MiB; read-only, links not followed. */
689
+ 'workspace.files.read': {
690
+ params: {
691
+ projectRoot: string;
692
+ path: string;
693
+ includeIgnored?: boolean;
694
+ };
695
+ result: {
696
+ path: string;
697
+ content: string;
698
+ size: number;
699
+ modifiedAt?: string;
700
+ };
701
+ };
702
+ 'question.answer': {
703
+ params: {
704
+ questionId: string;
705
+ text?: string;
706
+ dismiss?: boolean;
707
+ reason?: string;
708
+ };
709
+ result: {
710
+ questionId: string;
711
+ };
712
+ };
713
+ 'session.create': {
714
+ params: {
715
+ sessionId?: string;
716
+ history?: 'skip' | 'replay';
717
+ delegation?: boolean;
718
+ resume?: boolean;
719
+ } & ({
720
+ workspaceId: string;
721
+ spec: Omit<SessionSpec, 'cwd'> & {
722
+ cwd?: string;
723
+ };
724
+ } | {
725
+ workspaceId?: undefined;
726
+ spec: SessionSpec;
727
+ });
728
+ result: {
729
+ session: SessionSnapshot;
730
+ attached: boolean;
731
+ resumed?: true;
732
+ durable?: boolean;
733
+ };
734
+ };
735
+ 'session.preview': {
736
+ params: {
737
+ spec: SessionSpec;
738
+ };
739
+ result: {
740
+ preview: SessionPreview;
741
+ };
742
+ };
743
+ 'session.history': {
744
+ params: {
745
+ sessionId: string;
746
+ before?: string;
747
+ limit?: number;
748
+ maxBytes?: number;
749
+ };
750
+ result: HistoryPage;
751
+ };
752
+ 'session.attach': {
753
+ params: {
754
+ sessionId: string;
755
+ replay?: boolean;
756
+ after?: number;
757
+ generation?: string;
758
+ };
759
+ result: {
760
+ session: SessionSummary;
761
+ cursor: number;
762
+ reset?: true;
763
+ };
764
+ };
765
+ 'session.release': {
766
+ params: {
767
+ sessionId: string;
768
+ };
769
+ result: {
770
+ closed: boolean;
771
+ };
772
+ };
773
+ 'session.prompt': {
774
+ params: PrincipalContext & {
775
+ sessionId: string;
776
+ clientMessageId: string;
777
+ delivery?: 'auto' | 'steer';
778
+ content: PromptContent;
779
+ };
780
+ /** `{}` from a daemon without prompt.receipts. */
781
+ result: {
782
+ outcome?: 'accepted' | 'failed';
783
+ error?: AlpError;
784
+ duplicate?: boolean;
785
+ durable?: boolean;
786
+ };
787
+ };
788
+ 'session.interrupt': {
789
+ params: {
790
+ sessionId: string; /** With session.interrupt.turn, cancel only this turn. */
791
+ turnId?: string;
792
+ };
793
+ result: {
794
+ interrupted?: boolean;
795
+ };
796
+ };
797
+ /**
798
+ * Changes a session's permissions mode while idle; with capability `session.configure.live`, also a live
799
+ * root's model, effort or team (§12.9): those need requestId and ifGeneration, apply at the idle barrier
800
+ * through a restart of its native process on the same conversation, and answer `configured`. Answered
801
+ * once per (sessionId, requestId): a repeat answers the first result with `duplicate: true`.
802
+ */
803
+ 'session.configure': {
804
+ params: {
805
+ sessionId: string;
806
+ mode?: string;
807
+ model?: string;
808
+ thinking?: string;
809
+ team?: string;
810
+ requestId?: string;
811
+ ifGeneration?: string;
812
+ };
813
+ result: {
814
+ session: SessionSnapshot | SessionSummary;
815
+ configured?: ConfigureOutcome;
816
+ duplicate?: true;
817
+ };
818
+ };
819
+ /** What session.configure would run a live root with, and whether it would take it now (capability `session.configure.live`, §12.9). */
820
+ 'session.configure.preview': {
821
+ params: {
822
+ sessionId: string;
823
+ mode?: string;
824
+ model?: string;
825
+ thinking?: string;
826
+ team?: string;
827
+ };
828
+ result: {
829
+ preview: ConfigurePreview;
830
+ };
831
+ };
832
+ 'session.close': {
833
+ params: {
834
+ sessionId: string;
835
+ };
836
+ result: Empty;
837
+ };
838
+ 'session.rename': {
839
+ params: {
840
+ sessionId: string;
841
+ title: string;
842
+ };
843
+ result: {
844
+ session: SessionSummary;
845
+ };
846
+ };
847
+ /**
848
+ * A new root whose conversation is the source's up to a completed turn (capability `session.fork`, §12.7).
849
+ * Answered once per (sessionId, requestId): a repeat answers the first result with `duplicate: true`.
850
+ */
851
+ 'session.fork': {
852
+ params: PrincipalContext & {
853
+ sessionId: string;
854
+ requestId: string;
855
+ ifGeneration: string;
856
+ boundary?: ForkBoundary;
857
+ branchId?: string;
858
+ workspaceId?: string;
859
+ newSessionId?: string;
860
+ title?: string;
861
+ };
862
+ result: {
863
+ session: SessionSummary;
864
+ forkedFrom: ForkProvenance;
865
+ duplicate?: true;
866
+ };
867
+ };
868
+ /**
869
+ * The root's conversation from before a turn on, under the same session id; the later turns stay as a
870
+ * recoverable branch and no file is changed (capability `session.rewind`, §12.7).
871
+ */
872
+ 'session.rewind': {
873
+ params: {
874
+ sessionId: string;
875
+ requestId: string;
876
+ ifGeneration: string;
877
+ beforeTurnId: string;
878
+ };
879
+ result: {
880
+ session: SessionSummary;
881
+ generation: string;
882
+ branch: ConversationBranch;
883
+ duplicate?: true;
884
+ };
885
+ };
886
+ /**
887
+ * Restarts a live root's native process on its own conversation and configuration at the idle
888
+ * barrier, keeping its session id, history and receipts (capability `session.reload`, §12.8).
889
+ * Answered once per (sessionId, requestId): a repeat answers the first result with `duplicate: true`.
890
+ */
891
+ 'session.reload': {
892
+ params: {
893
+ sessionId: string;
894
+ requestId: string;
895
+ ifGeneration: string;
896
+ };
897
+ result: {
898
+ session: SessionSummary;
899
+ duplicate?: true;
900
+ };
901
+ };
902
+ /**
903
+ * The ALP CLI command that continues a session in a terminal, built by alpd (capability
904
+ * `session.resumeCommand`, §12.8): argv and its POSIX and Windows command lines, for the clipboard only.
905
+ */
906
+ 'session.resumeCommand': {
907
+ params: {
908
+ sessionId: string;
909
+ };
910
+ result: ResumeCommand;
911
+ };
912
+ 'session.archive': {
913
+ params: {
914
+ sessionId: string;
915
+ archived?: boolean;
916
+ };
917
+ result: {
918
+ session: SessionSummary;
919
+ };
920
+ };
921
+ 'session.get': {
922
+ params: {
923
+ sessionId: string;
924
+ };
925
+ result: {
926
+ session: SessionSummary;
927
+ };
928
+ };
929
+ /** `limit`, `cursor`, `query` and `projectRoots` with capability `session.list.page`: a page, and `nextCursor` while more follow. */
930
+ 'session.list': {
931
+ params: {
932
+ projectRoot?: string;
933
+ projectRoots?: string[];
934
+ rootsOnly?: boolean;
935
+ includeClosed?: boolean;
936
+ includeArchived?: boolean;
937
+ limit?: number;
938
+ cursor?: string;
939
+ query?: string;
940
+ };
941
+ result: {
942
+ sessions: SessionSummary[];
943
+ nextCursor?: string;
944
+ };
945
+ };
946
+ 'session.status': {
947
+ params: {
948
+ sessionId: string;
949
+ };
950
+ result: {
951
+ status: TreeStatus;
952
+ };
953
+ };
954
+ 'session.message': {
955
+ params: {
956
+ sessionId: string;
957
+ text: string;
958
+ };
959
+ result: Empty;
960
+ };
961
+ 'session.log': {
962
+ params: {
963
+ sessionId: string;
964
+ };
965
+ result: {
966
+ rootId: string;
967
+ entries: Array<Record<string, unknown>>;
968
+ };
969
+ };
970
+ 'extension.register': {
971
+ params: {
972
+ method: string;
973
+ };
974
+ result: {
975
+ registered: string;
976
+ };
977
+ };
978
+ 'extension.unregister': {
979
+ params: {
980
+ method: string;
981
+ };
982
+ result: {
983
+ unregistered: string;
984
+ };
985
+ };
986
+ 'team.list': {
987
+ params: {
988
+ projectRoot?: string;
989
+ };
990
+ result: {
991
+ projectRoot: string | null;
992
+ teams: TeamRow[];
993
+ };
994
+ };
995
+ 'task.list': {
996
+ params: {
997
+ projectRoot: string;
998
+ };
999
+ result: {
1000
+ projectRoot: string;
1001
+ tasks: TaskRow[];
1002
+ unreadable: Array<{
1003
+ file: string;
1004
+ error: string;
1005
+ }>;
1006
+ revision: string;
1007
+ };
1008
+ };
1009
+ 'task.create': {
1010
+ params: {
1011
+ projectRoot: string;
1012
+ title: string;
1013
+ priority?: number;
1014
+ requestId?: string;
1015
+ };
1016
+ result: {
1017
+ id: string;
1018
+ duplicate?: true;
1019
+ };
1020
+ };
1021
+ 'task.update': {
1022
+ params: {
1023
+ projectRoot: string;
1024
+ id: string;
1025
+ action: 'close' | 'reopen';
1026
+ note?: string;
1027
+ ifRev?: number;
1028
+ };
1029
+ result: {
1030
+ id: string;
1031
+ status: string;
1032
+ rev: number;
1033
+ landed?: string;
1034
+ };
1035
+ };
1036
+ 'task.gate.resolve': {
1037
+ params: {
1038
+ projectRoot: string;
1039
+ id: string;
1040
+ gate: string;
1041
+ note?: string;
1042
+ ifRev?: number;
1043
+ };
1044
+ result: {
1045
+ id: string;
1046
+ status: string;
1047
+ rev: number;
1048
+ };
1049
+ };
1050
+ 'library.list': {
1051
+ params: {
1052
+ kind: LibraryKind;
1053
+ projectRoot?: string;
1054
+ };
1055
+ result: {
1056
+ projectRoot: string | null;
1057
+ entries: LibraryEntryRow[];
1058
+ };
1059
+ };
1060
+ 'library.get': {
1061
+ params: {
1062
+ kind: LibraryKind;
1063
+ name: EntryName;
1064
+ projectRoot?: string;
1065
+ scope?: LibraryScope;
1066
+ };
1067
+ /** `content` carries `{ $secret: 'keep' }` in place of each secret value; `secrets` lists their JSON Pointers. */
1068
+ result: {
1069
+ kind: LibraryKind;
1070
+ name: string;
1071
+ source: LibrarySource;
1072
+ overrides?: LibrarySource;
1073
+ content: Record<string, unknown>;
1074
+ revision: string | null;
1075
+ usedBy: string[];
1076
+ librarySkills?: string[];
1077
+ secrets?: string[];
1078
+ };
1079
+ };
1080
+ 'library.save': {
1081
+ params: {
1082
+ kind: LibraryKind;
1083
+ name: EntryName;
1084
+ scope: LibraryScope;
1085
+ projectRoot?: string;
1086
+ policy?: 'project-data';
1087
+ content: Record<string, unknown>;
1088
+ revision?: string | null;
1089
+ sync?: 'none' | 'known-projects';
1090
+ };
1091
+ result: {
1092
+ revision: string | null;
1093
+ syncedProjects?: number;
1094
+ syncErrors?: string[];
1095
+ };
1096
+ };
1097
+ 'library.delete': {
1098
+ params: {
1099
+ kind: LibraryKind;
1100
+ name: EntryName;
1101
+ scope: LibraryScope;
1102
+ projectRoot?: string;
1103
+ policy?: 'project-data';
1104
+ revision?: string;
1105
+ };
1106
+ result: {
1107
+ removed: true;
1108
+ now?: 'builtin' | 'library';
1109
+ };
1110
+ };
1111
+ 'library.duplicate': {
1112
+ params: {
1113
+ kind: LibraryKind;
1114
+ from: EntryName;
1115
+ to: EntryName;
1116
+ scope: LibraryScope;
1117
+ projectRoot?: string;
1118
+ policy?: 'project-data';
1119
+ };
1120
+ result: {
1121
+ revision: string | null;
1122
+ };
1123
+ };
1124
+ 'library.rename': {
1125
+ params: {
1126
+ kind: LibraryKind;
1127
+ from: EntryName;
1128
+ to: EntryName;
1129
+ scope: LibraryScope;
1130
+ projectRoot?: string;
1131
+ policy?: 'project-data';
1132
+ };
1133
+ result: {
1134
+ revision: string | null;
1135
+ };
1136
+ };
1137
+ 'library.skills.set': {
1138
+ params: {
1139
+ agent: EntryName;
1140
+ skills: EntryName[];
1141
+ };
1142
+ result: {
1143
+ skills: string[];
1144
+ };
1145
+ };
1146
+ 'library.models': {
1147
+ params: Empty;
1148
+ result: {
1149
+ models: Array<{
1150
+ id: string;
1151
+ label: string;
1152
+ efforts: string[];
1153
+ effort?: string;
1154
+ }>;
1155
+ };
1156
+ };
1157
+ 'settings.language.get': {
1158
+ params: Empty;
1159
+ result: LanguageSetting;
1160
+ };
1161
+ 'settings.language.set': {
1162
+ params: {
1163
+ language: string | null;
1164
+ };
1165
+ result: LanguageSetting;
1166
+ };
1167
+ /** Capability `schedule`: a one-shot continuation of a root session, due at `dueAt` (UTC, ISO 8601). */
1168
+ 'schedule.create': {
1169
+ params: {
1170
+ projectRoot: string;
1171
+ sessionId: string;
1172
+ dueAt: string;
1173
+ prompt: string;
1174
+ };
1175
+ result: {
1176
+ job: ScheduleJob;
1177
+ };
1178
+ };
1179
+ 'schedule.list': {
1180
+ params: {
1181
+ projectRoot: string;
1182
+ status?: ScheduleStatus;
1183
+ };
1184
+ result: {
1185
+ jobs: ScheduleJob[];
1186
+ };
1187
+ };
1188
+ 'schedule.get': {
1189
+ params: {
1190
+ id: string;
1191
+ projectRoot: string;
1192
+ };
1193
+ result: {
1194
+ job: ScheduleJob;
1195
+ };
1196
+ };
1197
+ 'schedule.cancel': {
1198
+ params: {
1199
+ id: string;
1200
+ projectRoot: string;
1201
+ };
1202
+ result: {
1203
+ job: ScheduleJob;
1204
+ };
1205
+ };
1206
+ };
1207
+ export type RpcMethod = keyof RpcMethodMap;
1208
+ export type RpcParams<M extends RpcMethod> = RpcMethodMap[M]['params'];
1209
+ export type RpcResult<M extends RpcMethod> = RpcMethodMap[M]['result'];
1210
+ export {};