@north-light/crouter-api 0.3.156

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 (50) hide show
  1. package/README.md +51 -0
  2. package/dist/__tests__/client.test.d.ts +1 -0
  3. package/dist/__tests__/client.test.js +274 -0
  4. package/dist/client.d.ts +246 -0
  5. package/dist/client.js +611 -0
  6. package/dist/dto/attach.d.ts +16 -0
  7. package/dist/dto/attach.js +13 -0
  8. package/dist/dto/broker.d.ts +45 -0
  9. package/dist/dto/broker.js +20 -0
  10. package/dist/dto/canvas.d.ts +253 -0
  11. package/dist/dto/canvas.js +2 -0
  12. package/dist/dto/common.d.ts +27 -0
  13. package/dist/dto/common.js +15 -0
  14. package/dist/dto/config.d.ts +19 -0
  15. package/dist/dto/config.js +3 -0
  16. package/dist/dto/crons.d.ts +124 -0
  17. package/dist/dto/crons.js +10 -0
  18. package/dist/dto/files.d.ts +11 -0
  19. package/dist/dto/files.js +7 -0
  20. package/dist/dto/focus.d.ts +24 -0
  21. package/dist/dto/focus.js +10 -0
  22. package/dist/dto/health.d.ts +41 -0
  23. package/dist/dto/health.js +2 -0
  24. package/dist/dto/human.d.ts +57 -0
  25. package/dist/dto/human.js +4 -0
  26. package/dist/dto/inbox.d.ts +105 -0
  27. package/dist/dto/inbox.js +10 -0
  28. package/dist/dto/lifecycle.d.ts +79 -0
  29. package/dist/dto/lifecycle.js +3 -0
  30. package/dist/dto/messages.d.ts +55 -0
  31. package/dist/dto/messages.js +2 -0
  32. package/dist/dto/modelauth.d.ts +41 -0
  33. package/dist/dto/modelauth.js +3 -0
  34. package/dist/dto/nodes.d.ts +194 -0
  35. package/dist/dto/nodes.js +3 -0
  36. package/dist/dto/profiles.d.ts +14 -0
  37. package/dist/dto/profiles.js +3 -0
  38. package/dist/dto/reports.d.ts +41 -0
  39. package/dist/dto/reports.js +2 -0
  40. package/dist/dto/subscriptions.d.ts +14 -0
  41. package/dist/dto/subscriptions.js +2 -0
  42. package/dist/dto/worktree.d.ts +19 -0
  43. package/dist/dto/worktree.js +6 -0
  44. package/dist/errors.d.ts +19 -0
  45. package/dist/errors.js +30 -0
  46. package/dist/index.d.ts +24 -0
  47. package/dist/index.js +25 -0
  48. package/dist/routes.d.ts +63 -0
  49. package/dist/routes.js +91 -0
  50. package/package.json +33 -0
@@ -0,0 +1,57 @@
1
+ import type { IsoTime, NodeIdDTO } from './common.js';
2
+ export type ConsultKindDTO = 'follow_up' | 'visual';
3
+ /** A consult-outbox entry (the canvas half of a human consult/visual request). */
4
+ export interface ConsultOutboxEntryDTO {
5
+ request_id: string;
6
+ node_id: NodeIdDTO;
7
+ kind: ConsultKindDTO;
8
+ dir: string;
9
+ created: IsoTime;
10
+ }
11
+ /** `GET /v1/canvas/consults` result. */
12
+ export interface ConsultOutboxDTO {
13
+ entries: ConsultOutboxEntryDTO[];
14
+ }
15
+ /** `POST /v1/canvas/consults` body. */
16
+ export interface CreateConsultRequest {
17
+ node_id: NodeIdDTO;
18
+ kind: ConsultKindDTO;
19
+ request_id: string;
20
+ dir: string;
21
+ root: string;
22
+ }
23
+ /** Result of enqueuing a consult. */
24
+ export interface ConsultResultDTO {
25
+ request_id: string;
26
+ created: boolean;
27
+ }
28
+ /** `POST /v1/human/bridge` body — create a terminal `kind:'human'` bridge node
29
+ * via `spawnNode` (NO broker engine). Distinct from `POST /v1/nodes`, whose
30
+ * `spawnChild` always launches a broker the bridge must never have. */
31
+ export interface CreateHumanBridgeRequest {
32
+ kind: 'human';
33
+ parent: NodeIdDTO | null;
34
+ cwd: string;
35
+ name: string;
36
+ }
37
+ /** `POST /v1/human/bridge` result — the minted bridge node id + its name. */
38
+ export interface HumanBridgeResultDTO {
39
+ node_id: NodeIdDTO;
40
+ name: string;
41
+ }
42
+ /** `POST /v1/human/deliver` result — the registered humanloop completion
43
+ * handler run server-side. */
44
+ export interface HumanDeliverResultDTO {
45
+ delivered: boolean;
46
+ }
47
+ /** `POST /v1/human/consult` result — the registered follow-up handler run
48
+ * server-side. `handled` communicates whether an action was taken (never a
49
+ * throw for a stale/superseded event). */
50
+ export interface HumanConsultResultDTO {
51
+ handled: boolean;
52
+ }
53
+ /** `POST /v1/human/visual` result — the registered visual handler run
54
+ * server-side. */
55
+ export interface HumanVisualResultDTO {
56
+ handled: boolean;
57
+ }
@@ -0,0 +1,4 @@
1
+ // Human-bridge DTOs — the CANVAS HALF ONLY (spec §6.5). The humanloop
2
+ // ticket/deck store is an external package and stays local; these cover the
3
+ // `consult_outbox` rows that route through crtrd.
4
+ export {};
@@ -0,0 +1,105 @@
1
+ import type { IsoTime } from './common.js';
2
+ /** Opaque, stable, URL-safe ticket id: lowercase SHA-256 hex of
3
+ * `canonicalRoot + "\0" + ticketBasename`. Clients must treat it as opaque —
4
+ * it discloses no home filesystem path. */
5
+ export type InboxTicketIdDTO = string;
6
+ export type InteractionKindDTO = 'notify' | 'decision' | 'context' | 'error' | 'review';
7
+ export interface DeckSourceDTO {
8
+ sessionName?: string;
9
+ askedBy?: string;
10
+ blockedSince?: IsoTime;
11
+ nodeId?: string;
12
+ visual?: 'humanloop.visual/v1';
13
+ }
14
+ export interface DeckTicketSummaryDTO {
15
+ ticket_id: InboxTicketIdDTO;
16
+ kind: 'deck';
17
+ title: string;
18
+ subtitle: string;
19
+ blocked_since: IsoTime;
20
+ source: DeckSourceDTO;
21
+ interaction_kind?: InteractionKindDTO;
22
+ }
23
+ export interface ReviewTicketSummaryDTO {
24
+ ticket_id: InboxTicketIdDTO;
25
+ kind: 'review';
26
+ title: string;
27
+ subtitle: string;
28
+ blocked_since: IsoTime;
29
+ source: DeckSourceDTO;
30
+ }
31
+ export type InboxTicketSummaryDTO = DeckTicketSummaryDTO | ReviewTicketSummaryDTO;
32
+ /** `GET /v1/human/inbox` result. `tickets` is sorted newest `blocked_since`
33
+ * first, then `ticket_id` ascending. */
34
+ export interface InboxListDTO {
35
+ tickets: InboxTicketSummaryDTO[];
36
+ }
37
+ export interface InteractionOptionDTO {
38
+ id: string;
39
+ label: string;
40
+ description?: string;
41
+ }
42
+ export interface InteractionPreAnswerDTO {
43
+ selectedOptionId?: string;
44
+ selectedOptionIds?: string[];
45
+ freetext?: string;
46
+ label?: string;
47
+ }
48
+ export interface InteractionDTO {
49
+ id: string;
50
+ title: string;
51
+ subtitle: string;
52
+ /** Resolved source Markdown — `bodyPath` is deliberately impossible here. */
53
+ body?: string;
54
+ options: InteractionOptionDTO[];
55
+ multiSelect?: boolean;
56
+ allowFreetext?: boolean;
57
+ freetextLabel?: string;
58
+ kind?: InteractionKindDTO;
59
+ preAnswered?: InteractionPreAnswerDTO;
60
+ }
61
+ export interface DeckDTO {
62
+ title: string;
63
+ source?: DeckSourceDTO;
64
+ interactions: InteractionDTO[];
65
+ }
66
+ /** `GET /v1/human/inbox/:ticket_id` result for a pending deck. */
67
+ export interface InboxDeckDTO {
68
+ ticket_id: InboxTicketIdDTO;
69
+ kind: 'deck';
70
+ deck: DeckDTO;
71
+ }
72
+ export interface InteractionResponseDTO {
73
+ id: string;
74
+ selectedOptionId?: string;
75
+ selectedOptionIds?: string[];
76
+ freetext?: string;
77
+ optionComments?: Record<string, string>;
78
+ }
79
+ /** `POST /v1/human/inbox/:ticket_id/respond` body. */
80
+ export interface RespondInboxDeckRequest {
81
+ responses: InteractionResponseDTO[];
82
+ }
83
+ /** `POST /v1/human/inbox/:ticket_id/respond` result — the canonical humanloop
84
+ * `humanloop.response/v2` result, unchanged. */
85
+ export interface DeckTicketResultDTO {
86
+ schema: 'humanloop.response/v2';
87
+ kind: 'deck';
88
+ responses: InteractionResponseDTO[];
89
+ summary: string;
90
+ completedAt: IsoTime;
91
+ }
92
+ /** `POST /v1/human/inbox/:ticket_id/cancel` body. `reason`, when present, must
93
+ * be nonempty after trim and at most 1000 characters. */
94
+ export interface CancelInboxTicketRequest {
95
+ reason?: string;
96
+ }
97
+ /** `POST /v1/human/inbox/:ticket_id/cancel` result — the canonical humanloop
98
+ * `humanloop.cancel/v1` result, unchanged. `actor` is always `"human"`. */
99
+ export interface CanceledTicketResultDTO {
100
+ schema: 'humanloop.cancel/v1';
101
+ kind: 'canceled';
102
+ canceledAt: IsoTime;
103
+ reason?: string;
104
+ actor?: string;
105
+ }
@@ -0,0 +1,10 @@
1
+ // Humanloop inbox DTOs — crtrd `/v1/human/inbox` (Northlight crouter-inbox v1,
2
+ // inbox-contract.md §A). Crouter API envelope fields use the existing
3
+ // snake_case convention; nested humanloop protocol objects retain their
4
+ // canonical camelCase field names so they cross the wire without translation
5
+ // or loss. Optional fields are omitted when absent, never serialized as
6
+ // `null`. `bodyPath` is deliberately impossible on this wire — crtrd resolves
7
+ // it server-side via humanloop's `parseDeck` and returns inline `body`.
8
+ //
9
+ // PURITY (spec §3.1): Node built-ins + `src/api/*` only.
10
+ export {};
@@ -0,0 +1,79 @@
1
+ import type { NodeIdDTO, NodeStatusDTO } from './common.js';
2
+ /** `POST /v1/nodes/{id}/revive` body. */
3
+ export interface ReviveRequest {
4
+ /** Resume the saved conversation (true) vs a fresh launch (false). */
5
+ resume?: boolean;
6
+ /** Clear the finalization latch before reviving (#339 `--reopen` gate). The
7
+ * latch clear is a canvas write, so the gate is enforced server-side: with
8
+ * `reopen:false` (default) a finalized node is REJECTED (`node_finalized`);
9
+ * with `reopen:true` a node that is NOT finalized is rejected
10
+ * (`not_finalized`). */
11
+ reopen?: boolean;
12
+ /** Wake provenance: the cron whose run drove this revive (`CRTR_CRON_ID`,
13
+ * set by the daemon in every cron run's environment and forwarded by the
14
+ * CLI). With `resume:false` and a still-live cron row, crtrd injects the
15
+ * `<crtr-wake>` block so the revived node learns a CLOCK woke it, not a
16
+ * message. Prose only — an unknown id is ignored, never an error. */
17
+ cron_id?: string;
18
+ }
19
+ /** Result of a revive. `revived` is false on the idempotent double-revive no-op. */
20
+ export interface ReviveResultDTO {
21
+ node_id: NodeIdDTO;
22
+ revived: boolean;
23
+ resumed: boolean;
24
+ status: NodeStatusDTO;
25
+ }
26
+ /** `POST /v1/nodes/{id}/relaunch-root` result. A null id means the target
27
+ * was not a relaunchable live root, or its replacement failed to launch. */
28
+ export interface RelaunchRootResultDTO {
29
+ newNodeId: NodeIdDTO | null;
30
+ }
31
+ /** `POST /v1/nodes/revive-all` result. Node ids relaunched, plus any that
32
+ * failed to revive with the reason (the reader reports both). */
33
+ export interface ReviveAllResultDTO {
34
+ revived: NodeIdDTO[];
35
+ failed: {
36
+ node_id: NodeIdDTO;
37
+ error: string;
38
+ }[];
39
+ }
40
+ /** `POST /v1/nodes/{id}/close` body. The cascade set is computed server-side
41
+ * from edges; the flag only opts out of cascading descendants. */
42
+ export interface CloseRequest {
43
+ cascade?: boolean;
44
+ /** Root close disposition: `true` finalizes the root to `done` (the browse `x`
45
+ * "finish" semantics); default/`false` cancels it. The cascade set (computed
46
+ * server-side) is torn down either way. */
47
+ finish?: boolean;
48
+ }
49
+ /** Result of a close. */
50
+ export interface CloseResultDTO {
51
+ /** The closed node — the cascade root. */
52
+ root: NodeIdDTO;
53
+ /** Every node torn down (root + cascaded descendants), leaves-first. */
54
+ closed: NodeIdDTO[];
55
+ /** Descendants left alive because an out-of-subtree manager still subscribes. */
56
+ spared: NodeIdDTO[];
57
+ }
58
+ /** `POST /v1/nodes/{id}/promote` body. */
59
+ export interface PromoteRequest {
60
+ /** Specialize as this kind of orchestrator; defaults to the node's current kind. */
61
+ kind?: string;
62
+ /** Also flip lifecycle→resident (interactable). */
63
+ resident?: boolean;
64
+ /** Durably change the model tier (ultra|strong). */
65
+ model?: string;
66
+ }
67
+ /** `POST /v1/nodes/{id}/yield` body. */
68
+ export interface YieldRequest {
69
+ note?: string;
70
+ promote?: boolean;
71
+ /** Respecialize the kind as the node refreshes. */
72
+ kind?: string;
73
+ /** Durably raise the model tier (ultra|strong) for the fresh revive. */
74
+ model?: string;
75
+ }
76
+ /** `POST /v1/nodes/{id}/wait` body. */
77
+ export interface WaitRequest {
78
+ controller: NodeIdDTO;
79
+ }
@@ -0,0 +1,3 @@
1
+ // Lifecycle action DTOs (spec §6.2). Recycle/demote take no body and return a
2
+ // `NodeDetailDTO` (nodes.ts); revive/close/promote/yield/wait are here.
3
+ export {};
@@ -0,0 +1,55 @@
1
+ import type { InboxTierDTO, IsoTime, NodeIdDTO } from './common.js';
2
+ /** `POST /v1/nodes/{id}/messages` body. */
3
+ export interface SendMessageRequest {
4
+ body: string;
5
+ tier?: InboxTierDTO;
6
+ /** Sender node id for feed attribution (the CLI resolves `CRTR_NODE_ID`;
7
+ * the daemon has no ambient caller identity). Absent → an external/human
8
+ * caller (`from: null`). */
9
+ from?: NodeIdDTO | null;
10
+ /** Revive with no inbox entry (`--fresh` → `reviveNode({ resume: false })`). */
11
+ fresh?: boolean;
12
+ /** Clear a latched target's finalization latch before an immediate delivery
13
+ * or `--fresh` revive (`--reopen`). Immediate only. */
14
+ reopen?: boolean;
15
+ /** Hidden ambient context upserted onto the target's sidecar and delivered as
16
+ * a `<situational-context>` block, never visible chat (`--situational-context`).
17
+ * Immediate only; valid alone (no body). */
18
+ situational_context?: string;
19
+ /** Raw JSON-schema string granting a one-off `submit` tool before delivery
20
+ * (`--output-schema`). Immediate only. */
21
+ output_schema?: string;
22
+ /** `'interactive'`: deliver via the target's LIVE broker engine (prompt/steer
23
+ * on its one serialized frame loop — the same ordering a tmux viewer gets)
24
+ * instead of the durable inbox; a dormant or mid-revive target falls back to
25
+ * the durable inbox + revive (watcher delivers post-boot). Plain immediate
26
+ * body only — rejected with fresh/reopen/situational_context/
27
+ * output_schema or tier 'deferred'. Absent → durable inbox (unchanged). */
28
+ delivery?: 'interactive';
29
+ }
30
+ /** Result of an immediate message send. */
31
+ export interface MessageResultDTO {
32
+ node_id: NodeIdDTO;
33
+ /** Whether an inbox entry was appended now. */
34
+ delivered: boolean;
35
+ /** Whether a dormant target was revived to receive the message. */
36
+ revived: boolean;
37
+ delivered_at?: IsoTime;
38
+ /** Which channel a `delivery:'interactive'` send actually used: `'engine'`
39
+ * (live broker frame loop, no inbox entry) or `'inbox'` (durable fallback).
40
+ * Absent for non-interactive sends. */
41
+ delivered_via?: 'engine' | 'inbox';
42
+ }
43
+ /** `POST /v1/nodes/{id}/interrupt` result — the human Esc, first-class. Cancels
44
+ * pending undelivered human-send inbox entries FIRST, then aborts a live
45
+ * in-flight turn; a dormant target is NEVER revived. */
46
+ export interface InterruptResultDTO {
47
+ node_id: NodeIdDTO;
48
+ /** Primary outcome: `aborted_turn` wins over `canceled_pending` over `idle`
49
+ * (abort + cancel can co-occur; the flags below carry the full picture). */
50
+ outcome: 'aborted_turn' | 'canceled_pending' | 'idle';
51
+ /** True when a live in-flight turn (or `!` bash run) was actually aborted. */
52
+ aborted_turn: boolean;
53
+ /** How many pending undelivered human-send inbox entries were canceled. */
54
+ canceled_pending: number;
55
+ }
@@ -0,0 +1,2 @@
1
+ // Message DTOs (spec §6.2).
2
+ export {};
@@ -0,0 +1,41 @@
1
+ /** `PUT /v1/model-auth/{provider}` body — a discriminated union over credential
2
+ * kind. Idempotent upsert/rotate keyed by provider (and `account_id` for a
3
+ * managed account). */
4
+ export type InstallCredentialRequest = {
5
+ kind: 'api_key';
6
+ api_key: string;
7
+ } | {
8
+ kind: 'oauth';
9
+ access_token: string;
10
+ /** Required — pi's `OAuthCredentials` always carries both; the handler is
11
+ * fail-loud (400) when either is absent, so the type matches (no lenient
12
+ * fallback). */
13
+ refresh_token: string;
14
+ expires_at: string;
15
+ account_id?: string;
16
+ /** Provider-specific credential keys beyond the canonical tokens (e.g.
17
+ * github-copilot's `enterpriseUrl`), retained for enterprise routing and
18
+ * token refresh. */
19
+ extra?: Record<string, unknown>;
20
+ } | {
21
+ kind: 'managed_account';
22
+ /** Optional requested label. A bare provider login automatically labels a newly seen
23
+ * account from its identity profile. */
24
+ label?: string;
25
+ auto_label?: boolean;
26
+ account_id?: string;
27
+ access_token: string;
28
+ /** Required — the managed login always returns both; the handler is
29
+ * fail-loud (400) when either is absent. */
30
+ refresh_token: string;
31
+ expires_at: string;
32
+ /** Causal-cooldown floor snapshotted before the interactive login flow. Epoch ms. */
33
+ last_rate_limited_at?: number;
34
+ };
35
+ /** Result of a credential install/rotate. */
36
+ export interface CredentialResultDTO {
37
+ provider: string;
38
+ installed: true;
39
+ managed: boolean;
40
+ account_id?: string;
41
+ }
@@ -0,0 +1,3 @@
1
+ // Model-auth DTOs (spec §7.6). `PUT /v1/model-auth/{provider}` installs/rotates
2
+ // already-obtained credential material; interactive collection stays client-side.
3
+ export {};
@@ -0,0 +1,194 @@
1
+ import type { Cursor, ExitIntentDTO, IsoTime, LifecycleDTO, ModeDTO, NodeIdDTO, NodeStatusDTO } from './common.js';
2
+ /** `POST /v1/nodes` body. Carries the full immediate spawn recipe. */
3
+ export interface CreateNodeRequest {
4
+ kind: string;
5
+ prompt?: string;
6
+ profile?: string;
7
+ mode?: ModeDTO;
8
+ cwd?: string;
9
+ /** Display name (tmux window + resume picker). Defaults to the kind. */
10
+ name?: string;
11
+ parent?: NodeIdDTO | null;
12
+ root?: boolean;
13
+ /** Worktree branch name, or true for an auto-named managed worktree. */
14
+ worktree?: string | boolean;
15
+ fork_from?: string;
16
+ model?: string;
17
+ situational_context?: string;
18
+ no_kickoff?: boolean;
19
+ output_schema?: string;
20
+ /** Spawn AT this exact node id instead of a runtime-minted one — format-
21
+ * validated and duplicate-rejected server-side (`NodeIdConflictError` →
22
+ * HTTP 409 `node_id_exists`). See `crtr node new --node-id`. */
23
+ node_id?: string;
24
+ /** Serve this create from the warm pool when a pre-booted spare matches the
25
+ * request's frozen launch tuple (kind, mode, cwd, profile, model,
26
+ * situational_context) — answering in milliseconds instead of waiting out a
27
+ * full engine boot. Only a bare root with no kickoff qualifies; anything the
28
+ * pool cannot honor (or an empty pool) falls back to an ordinary cold spawn,
29
+ * so the flag never fails a create, it only ever makes it faster. */
30
+ prefer_warm?: boolean;
31
+ }
32
+ /** The list/queryable projection of a node — the indexed row columns. */
33
+ export interface NodeSummaryDTO {
34
+ node_id: NodeIdDTO;
35
+ name: string;
36
+ kind: string;
37
+ mode: ModeDTO;
38
+ lifecycle: LifecycleDTO;
39
+ status: NodeStatusDTO;
40
+ cwd: string;
41
+ host_kind: 'tmux' | 'broker' | null;
42
+ profile_id: string | null;
43
+ parent: NodeIdDTO | null;
44
+ created: IsoTime;
45
+ intent: ExitIntentDTO;
46
+ waiting_for: NodeIdDTO | null;
47
+ pi_pid: number | null;
48
+ final_report: string | null;
49
+ finalized_at: IsoTime | null;
50
+ }
51
+ /** The spine + subscription edges of a node (absorbs `managers`/`paths` reads). */
52
+ export interface NodeEdgesDTO {
53
+ /** Spine parent (my manager); null for a root. */
54
+ parent: NodeIdDTO | null;
55
+ /** Provenance — who spawned me. */
56
+ spawned_by: NodeIdDTO | null;
57
+ /** Publishers I subscribe to. */
58
+ subscribes_to: NodeIdDTO[];
59
+ /** Subscribers to my output (my managers). */
60
+ subscribers: NodeIdDTO[];
61
+ /** Children I spawned. */
62
+ children: NodeIdDTO[];
63
+ }
64
+ /** Absolute filesystem paths for a node (absorbs the `paths` read). */
65
+ export interface NodePathsDTO {
66
+ node_dir: string;
67
+ context_dir: string;
68
+ reports_dir: string;
69
+ meta_path: string;
70
+ inbox_path: string;
71
+ transcript_path: string;
72
+ view_socket: string;
73
+ }
74
+ /** A node's managed git worktree, if any. */
75
+ export interface NodeWorktreeDTO {
76
+ state: 'open' | 'closed';
77
+ path: string;
78
+ branch: string;
79
+ repo_root: string;
80
+ base_ref: string;
81
+ base_sha: string;
82
+ created: IsoTime;
83
+ closed?: IsoTime;
84
+ }
85
+ /** The full node view — summary ∪ identity extras ∪ edges ∪ paths. Returned by
86
+ * `GET /v1/nodes/{id}` and by the create/lifecycle actions that yield a node. */
87
+ export interface NodeDetailDTO extends NodeSummaryDTO {
88
+ description?: string;
89
+ cycles?: number;
90
+ /** Approximate context-window token load of the node's live/last session,
91
+ * used by the orchestrator yield-nudge (`childFollowUp`). Null when unknown
92
+ * (never launched, or no token accounting yet). */
93
+ context_tokens?: number | null;
94
+ pi_session_id?: string | null;
95
+ /** Launch-time process-identity fingerprint captured alongside `pi_pid`
96
+ * (`NodeMeta.pi_pid_identity`), or null. Surfaced so `revive --now`'s
97
+ * client-side SIGTERM can pass the identity baseline to `recordedPidLiveness`
98
+ * and refuse to signal a stranger process that reused a dead broker's pid.
99
+ * Null when the node predates the field or its launch-time capture failed
100
+ * (fail-open — no baseline means no guard, not a false mismatch). */
101
+ pi_pid_identity?: string | null;
102
+ /** The node's durable model override (`NodeMeta.model_override`), or null when
103
+ * it runs on the kind/profile default. Surfaced so `node config --model`
104
+ * can report the resolved model after a patch. */
105
+ model_override?: string | null;
106
+ /** Absolute path to pi's session `.jsonl`, captured at session_start
107
+ * (`NodeMeta.pi_session_file`). Distinct from `paths.transcript_path` (the
108
+ * crtr-owned transcript mirror). Consumed by `memory origin` to deref a doc
109
+ * back to the conversation that authored it. */
110
+ pi_session_file?: string | null;
111
+ edges: NodeEdgesDTO;
112
+ paths: NodePathsDTO;
113
+ worktree?: NodeWorktreeDTO | null;
114
+ /** Present only on a `POST /promote` response — the roadmap/goal facts the
115
+ * promote primitive returns beyond the node meta (spec §6.2). A plain detail
116
+ * read omits them. */
117
+ roadmap_written?: boolean;
118
+ roadmap_path?: string;
119
+ goal_path?: string;
120
+ }
121
+ /** `GET /v1/nodes` query filters. */
122
+ export interface ListNodesQuery {
123
+ status?: NodeStatusDTO;
124
+ kind?: string;
125
+ mode?: ModeDTO;
126
+ /** Restrict to the subtree under this node. */
127
+ under?: NodeIdDTO;
128
+ /** Only nodes with a dangling/hanging manager edge. */
129
+ hanging?: boolean;
130
+ }
131
+ /** `GET /v1/nodes/{id}/snapshot` — the node's reconstructed broker snapshot
132
+ * (`readNodeSnapshot`): the message log, aggregate stats, and current engine
133
+ * state, plus the node's registered command set. */
134
+ export interface NodeSnapshotDTO {
135
+ node_id: NodeIdDTO;
136
+ snapshot: {
137
+ messages: unknown[];
138
+ stats: unknown;
139
+ state: Record<string, unknown>;
140
+ };
141
+ commands: {
142
+ name: string;
143
+ description: string;
144
+ source: string;
145
+ }[];
146
+ captured_at: IsoTime;
147
+ }
148
+ /** `GET /v1/nodes/{id}/transcript` query. */
149
+ export interface TranscriptQuery {
150
+ limit?: number;
151
+ cursor?: Cursor;
152
+ }
153
+ /** `GET /v1/nodes/{id}/transcript` result. The reader (`transcriptMarkdown`)
154
+ * renders the whole conversation as a single markdown document. */
155
+ export interface TranscriptDTO {
156
+ node_id: NodeIdDTO;
157
+ markdown: string;
158
+ }
159
+ /** A canvas artifact under a node (`nodeArtifacts` → `HistoryArtifact`): a
160
+ * pushed report, a context doc, or the node roadmap. */
161
+ export interface ArtifactDTO {
162
+ /** Stable `<node-id>:<relpath>` handle. */
163
+ ref: string;
164
+ /** Artifact source — `report:<kind>` | `doc` | `roadmap` | `meta`. */
165
+ source: string;
166
+ ts: IsoTime;
167
+ title: string;
168
+ }
169
+ /** `GET /v1/nodes/{id}/artifacts` query. Narrow to one corpus; absent is the
170
+ * default report/doc/roadmap set (`inbox` is opt-in only). */
171
+ export interface ArtifactsQuery {
172
+ type?: 'report' | 'doc' | 'roadmap' | 'inbox';
173
+ }
174
+ /** `GET /v1/nodes/{id}/artifacts` result. */
175
+ export interface ArtifactListDTO {
176
+ node_id: NodeIdDTO;
177
+ artifacts: ArtifactDTO[];
178
+ }
179
+ /** One context root visible to a node — its own dir plus each publisher it
180
+ * subscribes to (the shared-document roster). */
181
+ export interface ContextRootDTO {
182
+ node_id: NodeIdDTO;
183
+ label: string;
184
+ dir: string;
185
+ /** True for the node's own context root. */
186
+ self: boolean;
187
+ /** Count of context + report files under the root. */
188
+ files: number;
189
+ }
190
+ /** `GET /v1/nodes/{id}/context` result (the listing; the nvim popup stays local). */
191
+ export interface ContextListDTO {
192
+ node_id: NodeIdDTO;
193
+ roots: ContextRootDTO[];
194
+ }
@@ -0,0 +1,3 @@
1
+ // Node resource DTOs (spec §6.2): create request, summary/detail projections,
2
+ // list query, and the node read-endpoint shapes.
3
+ export {};
@@ -0,0 +1,14 @@
1
+ /** `PUT /v1/profiles/{name}` body — idempotent ensure. */
2
+ export interface EnsureProfileRequest {
3
+ /** Absolute project directories in the profile's purview. */
4
+ projects?: string[];
5
+ }
6
+ /** A profile projection. */
7
+ export interface ProfileDTO {
8
+ /** Stable profile-directory id (`<slug>-<id>`). */
9
+ id: string;
10
+ name: string;
11
+ projects: string[];
12
+ /** The directory this profile is pinned as default for, if any. */
13
+ default_dir?: string | null;
14
+ }
@@ -0,0 +1,3 @@
1
+ // Profile DTOs (spec §6.6). Server-side for remote/Core (P2) consumers; local
2
+ // `crtr profile *` verbs stay fs-local.
3
+ export {};
@@ -0,0 +1,41 @@
1
+ import type { IsoTime, NodeIdDTO } from './common.js';
2
+ /** Report tier — `crtr push {update,urgent,final}`. */
3
+ export type ReportTierDTO = 'update' | 'urgent' | 'final';
4
+ /** `POST /v1/nodes/{id}/reports` body ({id} = the reporting node). */
5
+ export interface PushReportRequest {
6
+ tier: ReportTierDTO;
7
+ body: string;
8
+ }
9
+ /** Result of a push. `transitioned` is present only for `final`, which drives a
10
+ * server-side lifecycle transition. */
11
+ export interface PushReportResultDTO {
12
+ /** Absolute path of the written report file. */
13
+ report_path: string;
14
+ /** Subscriber node ids that received an inbox entry. */
15
+ notified: NodeIdDTO[];
16
+ transitioned?: {
17
+ from: string;
18
+ to: string;
19
+ };
20
+ /** Present (and true) only for a `final` push whose reporting node owned a
21
+ * managed worktree with zero commits ahead of its base and no uncommitted
22
+ * changes: the server auto-dropped it instead of blocking on `node worktree
23
+ * close` (#327/#333). A worktree with real unlanded work instead blocks with
24
+ * an `open_managed_worktree` error. */
25
+ worktree_auto_dropped?: boolean;
26
+ /** Present only alongside `worktree_auto_dropped`: the checkout path dropped. */
27
+ worktree_auto_dropped_path?: string;
28
+ }
29
+ /** `GET /v1/nodes/{id}/reports` query filters. */
30
+ export interface ReportsQuery {
31
+ tier?: ReportTierDTO;
32
+ limit?: number;
33
+ }
34
+ /** A single stored report entry. */
35
+ export interface ReportDTO {
36
+ /** Absolute path of the report file. */
37
+ path: string;
38
+ tier: ReportTierDTO;
39
+ body: string;
40
+ created: IsoTime;
41
+ }
@@ -0,0 +1,2 @@
1
+ // Push / report DTOs (spec §7.4).
2
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { IsoTime, NodeIdDTO } from './common.js';
2
+ /** `POST /v1/nodes/{id}/subscriptions` body — {id} subscribes to `target`. */
3
+ export interface SubscribeRequest {
4
+ target: NodeIdDTO;
5
+ /** active = wake the subscriber on emit; passive = accumulate, no wake. */
6
+ active?: boolean;
7
+ }
8
+ /** A subscription as seen from one endpoint. */
9
+ export interface SubscriptionDTO {
10
+ /** The node id at the other end of the edge. */
11
+ node_id: NodeIdDTO;
12
+ active: boolean;
13
+ created: IsoTime;
14
+ }
@@ -0,0 +1,2 @@
1
+ // Subscription DTOs (spec §6.2).
2
+ export {};