@crouter/api 0.3.377

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 (112) hide show
  1. package/README.md +67 -0
  2. package/dist/api/__tests__/error-codes.test.d.ts +1 -0
  3. package/dist/api/__tests__/error-codes.test.js +78 -0
  4. package/dist/api/__tests__/integration/client.test.d.ts +1 -0
  5. package/dist/api/__tests__/integration/client.test.js +179 -0
  6. package/dist/api/client.d.ts +467 -0
  7. package/dist/api/client.js +1179 -0
  8. package/dist/api/command-manifest/index.d.ts +3 -0
  9. package/dist/api/command-manifest/index.js +3 -0
  10. package/dist/api/command-manifest/manifest.d.ts +51 -0
  11. package/dist/api/command-manifest/manifest.js +332 -0
  12. package/dist/api/command-manifest/result.d.ts +25 -0
  13. package/dist/api/command-manifest/result.js +97 -0
  14. package/dist/api/command-manifest/schema.d.ts +28 -0
  15. package/dist/api/command-manifest/schema.js +856 -0
  16. package/dist/api/dto/analytics.d.ts +184 -0
  17. package/dist/api/dto/analytics.js +3 -0
  18. package/dist/api/dto/attach.d.ts +22 -0
  19. package/dist/api/dto/attach.js +13 -0
  20. package/dist/api/dto/bash-jobs.d.ts +24 -0
  21. package/dist/api/dto/bash-jobs.js +9 -0
  22. package/dist/api/dto/bash.d.ts +17 -0
  23. package/dist/api/dto/bash.js +1 -0
  24. package/dist/api/dto/broker-ops.d.ts +187 -0
  25. package/dist/api/dto/broker-ops.js +6 -0
  26. package/dist/api/dto/broker-signals.d.ts +25 -0
  27. package/dist/api/dto/broker-signals.js +1 -0
  28. package/dist/api/dto/broker.d.ts +86 -0
  29. package/dist/api/dto/broker.js +20 -0
  30. package/dist/api/dto/canvas.d.ts +359 -0
  31. package/dist/api/dto/canvas.js +2 -0
  32. package/dist/api/dto/chat-inventory.d.ts +56 -0
  33. package/dist/api/dto/chat-inventory.js +11 -0
  34. package/dist/api/dto/common.d.ts +29 -0
  35. package/dist/api/dto/common.js +15 -0
  36. package/dist/api/dto/config.d.ts +36 -0
  37. package/dist/api/dto/config.js +3 -0
  38. package/dist/api/dto/crons.d.ts +150 -0
  39. package/dist/api/dto/crons.js +10 -0
  40. package/dist/api/dto/custom-objects.d.ts +66 -0
  41. package/dist/api/dto/custom-objects.js +1 -0
  42. package/dist/api/dto/delivery.d.ts +71 -0
  43. package/dist/api/dto/delivery.js +7 -0
  44. package/dist/api/dto/docs.d.ts +135 -0
  45. package/dist/api/dto/docs.js +8 -0
  46. package/dist/api/dto/files.d.ts +21 -0
  47. package/dist/api/dto/files.js +1 -0
  48. package/dist/api/dto/focus.d.ts +24 -0
  49. package/dist/api/dto/focus.js +10 -0
  50. package/dist/api/dto/grants.d.ts +14 -0
  51. package/dist/api/dto/grants.js +1 -0
  52. package/dist/api/dto/health.d.ts +106 -0
  53. package/dist/api/dto/health.js +2 -0
  54. package/dist/api/dto/human-requests.d.ts +113 -0
  55. package/dist/api/dto/human-requests.js +4 -0
  56. package/dist/api/dto/human.d.ts +28 -0
  57. package/dist/api/dto/human.js +4 -0
  58. package/dist/api/dto/inbox.d.ts +273 -0
  59. package/dist/api/dto/inbox.js +4 -0
  60. package/dist/api/dto/lifecycle.d.ts +88 -0
  61. package/dist/api/dto/lifecycle.js +3 -0
  62. package/dist/api/dto/mail.d.ts +44 -0
  63. package/dist/api/dto/mail.js +1 -0
  64. package/dist/api/dto/messages.d.ts +88 -0
  65. package/dist/api/dto/messages.js +2 -0
  66. package/dist/api/dto/model-config.d.ts +25 -0
  67. package/dist/api/dto/model-config.js +1 -0
  68. package/dist/api/dto/modelauth.d.ts +132 -0
  69. package/dist/api/dto/modelauth.js +4 -0
  70. package/dist/api/dto/node-events.d.ts +65 -0
  71. package/dist/api/dto/node-events.js +4 -0
  72. package/dist/api/dto/node-outcomes.d.ts +88 -0
  73. package/dist/api/dto/node-outcomes.js +2 -0
  74. package/dist/api/dto/node-records.d.ts +35 -0
  75. package/dist/api/dto/node-records.js +5 -0
  76. package/dist/api/dto/nodes.d.ts +368 -0
  77. package/dist/api/dto/nodes.js +3 -0
  78. package/dist/api/dto/objects.d.ts +172 -0
  79. package/dist/api/dto/objects.js +5 -0
  80. package/dist/api/dto/profiles.d.ts +117 -0
  81. package/dist/api/dto/profiles.js +4 -0
  82. package/dist/api/dto/recovery.d.ts +104 -0
  83. package/dist/api/dto/recovery.js +1 -0
  84. package/dist/api/dto/reports.d.ts +93 -0
  85. package/dist/api/dto/reports.js +2 -0
  86. package/dist/api/dto/review-comments.d.ts +146 -0
  87. package/dist/api/dto/review-comments.js +5 -0
  88. package/dist/api/dto/reviews.d.ts +113 -0
  89. package/dist/api/dto/reviews.js +5 -0
  90. package/dist/api/dto/run-events.d.ts +293 -0
  91. package/dist/api/dto/run-events.js +6 -0
  92. package/dist/api/dto/subscriptions.d.ts +14 -0
  93. package/dist/api/dto/subscriptions.js +2 -0
  94. package/dist/api/dto/worktree.d.ts +55 -0
  95. package/dist/api/dto/worktree.js +6 -0
  96. package/dist/api/error-codes.d.ts +254 -0
  97. package/dist/api/error-codes.js +54 -0
  98. package/dist/api/errors.d.ts +47 -0
  99. package/dist/api/errors.js +66 -0
  100. package/dist/api/index.d.ts +42 -0
  101. package/dist/api/index.js +41 -0
  102. package/dist/api/node-transport.d.ts +18 -0
  103. package/dist/api/node-transport.js +105 -0
  104. package/dist/api/plugin-manifest-schema.d.ts +233 -0
  105. package/dist/api/plugin-manifest-schema.js +23 -0
  106. package/dist/api/routes.d.ts +160 -0
  107. package/dist/api/routes.js +193 -0
  108. package/dist/shared/generated-context.d.ts +79 -0
  109. package/dist/shared/generated-context.js +232 -0
  110. package/dist/shared/predicates.d.ts +2 -0
  111. package/dist/shared/predicates.js +4 -0
  112. package/package.json +49 -0
@@ -0,0 +1,79 @@
1
+ /** Custom message carrying the node's session-start bearings. */
2
+ export declare const CONTEXT_INTRO_CUSTOM_TYPE = "crtr-context";
3
+ /** Custom message carrying an ambient situational-context update. */
4
+ export declare const SITUATIONAL_CONTEXT_CUSTOM_TYPE = "crtr-situational-context";
5
+ /** Custom message used when a context-size nudge waits for the next turn. */
6
+ export declare const CONTEXT_NUDGE_CUSTOM_TYPE = "crtr-context-nudge";
7
+ /** Custom message that opens a review companion's visible transcript. */
8
+ export declare const REVIEW_BOUNDARY_CUSTOM_TYPE = "crtr-review-boundary";
9
+ /** Generic completion mandate issued by the terminal-node stop guard. */
10
+ export declare const STALL_REPROMPT: string;
11
+ /** The daemon's parking mandate for an isolated turn. It leaves current truth,
12
+ * supporting material when needed, lasting lessons, and one deferred update;
13
+ * the main conversation remains untouched and resumes without a fresh cycle. */
14
+ export declare const PARK_SUMMARY_PROMPT: string;
15
+ /** Static recovery prompts shared by the broker producer and display classifier. */
16
+ export declare const AUTH_FAULT_RECOVERY_BODY = "Provider credentials were just updated (a new login landed). Your previous turn stopped on a provider authentication failure. Continue from where you left off and retry the work that failed.";
17
+ export declare const CONNECTION_FAULT_RECOVERY_BODY = "The network connection is back online. Your previous turn stopped on a connection error (the network was down). Continue from where you left off and retry the work that failed.";
18
+ export declare const PROVIDER_FAULT_RECOVERY_BODY = "Your previous turn stopped on a provider fault. Continue from where you left off and retry the work that failed.";
19
+ export type ModelFallbackRecoveryReason = 'credential' | 'not-found';
20
+ /** Format the stop guard's dynamic structured-output mandate. */
21
+ export declare function formatStructuredOutputReprompt(schema: string): string;
22
+ /** Keep model-fallback guidance editable without making it a reader contract. */
23
+ export declare function formatModelFallbackRecovery(previousModel: string, nextModel: string, reason: ModelFallbackRecoveryReason): string;
24
+ export interface GeneratedContextMessageLike {
25
+ role?: string;
26
+ customType?: string;
27
+ content?: unknown;
28
+ }
29
+ export type CardEntryDisposition = 'report' | 'human-answer' | 'human-canceled';
30
+ export interface CardEntry {
31
+ disposition: CardEntryDisposition;
32
+ kind: string;
33
+ body: string;
34
+ ref?: string;
35
+ }
36
+ export interface CardSender {
37
+ id: string;
38
+ updates: number;
39
+ finished: boolean;
40
+ entries: CardEntry[];
41
+ }
42
+ /** An already-resolved inbox entry supplied to the pure digest formatter. */
43
+ export interface InboxCardEntry {
44
+ kind: string;
45
+ body: string;
46
+ ref?: string;
47
+ disposition?: CardEntryDisposition;
48
+ }
49
+ /** An already-resolved sender section supplied to the pure digest formatter. */
50
+ export interface InboxCardSection {
51
+ id: string;
52
+ entries: readonly InboxCardEntry[];
53
+ }
54
+ export interface GeneratedCard {
55
+ kind: string;
56
+ facts: Readonly<Record<string, string>>;
57
+ body: string;
58
+ senders: CardSender[];
59
+ }
60
+ /** Plain text from a generated message's string or text-block content. */
61
+ export declare function generatedContextText(message: GeneratedContextMessageLike): string;
62
+ /** Wrap a user-role runtime message in its whole-message card envelope. */
63
+ export declare function formatCard(kind: string, facts: Record<string, string | number | boolean | undefined>, body: string): string;
64
+ /** Wrap a card whose body is DATA, not markup — the only escape on the API
65
+ * path, so a caller can neither emit a malformed card nor smuggle markup into
66
+ * one. `formatCard` keeps its raw body for the trusted crouter producers that
67
+ * deliberately nest markup (bearings blocks, the inbox `<from>`/`<entry>`
68
+ * grammar `parseInboxBody` depends on). */
69
+ export declare function formatDataCard(kind: string, facts: Record<string, string | number | boolean | undefined>, body: string): string;
70
+ /** Parse a runtime envelope, with pre-envelope customType compatibility. */
71
+ export declare function parseCard(message: GeneratedContextMessageLike): GeneratedCard | null;
72
+ /** Whether an inbox entry is the person's own words. A `human-answer` entry
73
+ * whose body is itself a runtime card is a runtime notice — review approvals
74
+ * were delivered that way before they became plain prose — never speech. */
75
+ export declare function isHumanWordsEntry(entry: CardEntry): boolean;
76
+ /** Explain how an inbox entry's ref is read without guessing its filesystem location. */
77
+ export declare function formatInboxRefInstruction(ref: string): string;
78
+ /** Format a complete inbox card from report bodies resolved by the caller. */
79
+ export declare function formatInboxCard(sections: readonly InboxCardSection[]): string;
@@ -0,0 +1,232 @@
1
+ // Runtime-card grammar for crouter-authored context messages.
2
+ //
3
+ // Every runtime message carries one whole-message envelope. customType only
4
+ // controls delivery and visibility; parsing stays independent of runtime and clients.
5
+ /** Custom message carrying the node's session-start bearings. */
6
+ export const CONTEXT_INTRO_CUSTOM_TYPE = 'crtr-context';
7
+ /** Custom message carrying an ambient situational-context update. */
8
+ export const SITUATIONAL_CONTEXT_CUSTOM_TYPE = 'crtr-situational-context';
9
+ /** Custom message used when a context-size nudge waits for the next turn. */
10
+ export const CONTEXT_NUDGE_CUSTOM_TYPE = 'crtr-context-nudge';
11
+ /** Custom message that opens a review companion's visible transcript. */
12
+ export const REVIEW_BOUNDARY_CUSTOM_TYPE = 'crtr-review-boundary';
13
+ /** Generic completion mandate issued by the terminal-node stop guard. */
14
+ export const STALL_REPROMPT = "You've stopped but you're not waiting on anyone and haven't finished. " +
15
+ "Pipe the result to `crtr push final` through a single-quoted heredoc if the work is done, or use `crtr human send` if you are blocked or need the user.";
16
+ /** The daemon's parking mandate for an isolated turn. It leaves current truth,
17
+ * supporting material when needed, lasting lessons, and one deferred update;
18
+ * the main conversation remains untouched and resumes without a fresh cycle. */
19
+ export const PARK_SUMMARY_PROMPT = 'This conversation has been idle with nothing left to wake it, so it is being concluded. This is your last turn. Use it to leave a trustworthy inheritance, not to restart or broaden the work. Do these four things now, then stop.\n\n'
20
+ + '1. Establish current truth. Check only state that may have changed outside the transcript and matters to resuming—such as the working tree, a remote run, or an external decision. Do not start new work; perform only a quick check needed to avoid recording an unverified claim. If no mandate or work ever began, record that plainly in the inheritance and keep every artifact minimal.\n\n'
21
+ + '2. Put supporting material in documents you own (`crtr doc write`) only when it is needed to keep the inheritance concise. Rewrite existing living documents rather than leave superseded versions. Task state, identifiers, and recovery detail belong here, under this node, not under a longer-lived owner.\n\n'
22
+ + '3. Give a longer-lived owner (`--owner repo|profile|user`) only to a non-obvious, reusable lesson that should survive this task and is not already recorded. Read `crtr doc write -h`, search before writing (`crtr canvas search`), and choose the narrowest owner that will reach the next agent who needs it. Do not put a conversation recap, task status, recovery handles, or facts already captured in code or docs into such a document.\n\n'
23
+ + '4. Push exactly one regular update with `crtr push update --tier deferred`, never `crtr push final`. Write for someone who has not seen this conversation: name the work in plain terms, say where it stands, and say what remains or is blocked. Put the current outcome in the first line; include only needed decisions and recovery handles, then stop.';
24
+ /** Static recovery prompts shared by the broker producer and display classifier. */
25
+ export const AUTH_FAULT_RECOVERY_BODY = 'Provider credentials were just updated (a new login landed). Your previous turn stopped on a provider authentication failure. Continue from where you left off and retry the work that failed.';
26
+ export const CONNECTION_FAULT_RECOVERY_BODY = 'The network connection is back online. Your previous turn stopped on a connection error (the network was down). Continue from where you left off and retry the work that failed.';
27
+ export const PROVIDER_FAULT_RECOVERY_BODY = 'Your previous turn stopped on a provider fault. Continue from where you left off and retry the work that failed.';
28
+ const REVIEW_APPROVAL_OPEN = '<crtr-review-approval>';
29
+ const REVIEW_APPROVAL_CLOSE = '</crtr-review-approval>';
30
+ const MODEL_FALLBACK_RECOVERY_OPEN = '<model-fallback-recovery>';
31
+ const MODEL_FALLBACK_RECOVERY_CLOSE = '</model-fallback-recovery>';
32
+ const STRUCTURED_OUTPUT_REPROMPT_PREFIX = 'You must submit a result matching the required schema with `crtr push result` before you can stop, or decline it with `crtr push result --decline "<reason>" --code <token>` when the schema cannot be honestly satisfied. You cannot finish or go dormant any other way while this request is pending.\n\nRequired schema:\n\n```json\n';
33
+ const STRUCTURED_OUTPUT_REPROMPT_SUFFIX = '\n```';
34
+ /** Format the stop guard's dynamic structured-output mandate. */
35
+ export function formatStructuredOutputReprompt(schema) {
36
+ return `${STRUCTURED_OUTPUT_REPROMPT_PREFIX}${schema}${STRUCTURED_OUTPUT_REPROMPT_SUFFIX}`;
37
+ }
38
+ /** Keep model-fallback guidance editable without making it a reader contract. */
39
+ export function formatModelFallbackRecovery(previousModel, nextModel, reason) {
40
+ return reason === 'credential'
41
+ ? `The previous model (${previousModel}) had no usable provider credential, so you were automatically switched to ${nextModel}. Continue the task from where the failed turn left off.`
42
+ : `The previous model (${previousModel}) was unavailable (provider 404 not_found), so you were automatically switched to ${nextModel}. Continue the task from where the failed turn left off.`;
43
+ }
44
+ function legacyCustomTypeKind(customType) {
45
+ switch (customType) {
46
+ case CONTEXT_NUDGE_CUSTOM_TYPE: return 'context-nudge';
47
+ case CONTEXT_INTRO_CUSTOM_TYPE: return 'bearings';
48
+ case REVIEW_BOUNDARY_CUSTOM_TYPE: return 'review-boundary';
49
+ case SITUATIONAL_CONTEXT_CUSTOM_TYPE: return 'situational';
50
+ default: return undefined;
51
+ }
52
+ }
53
+ const XML_ATTRIBUTE_NAME = /^[A-Za-z_:][A-Za-z0-9_:.-]*$/;
54
+ const ATTRIBUTE_RE = /\s+([A-Za-z_:][A-Za-z0-9_:.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/gy;
55
+ function escapeXmlAttribute(value) {
56
+ return value
57
+ .replaceAll('&', '&amp;')
58
+ .replaceAll('<', '&lt;')
59
+ .replaceAll('>', '&gt;')
60
+ .replaceAll('"', '&quot;')
61
+ .replaceAll("'", '&apos;');
62
+ }
63
+ function escapeXmlText(value) {
64
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
65
+ }
66
+ function unescapeXmlAttribute(value) {
67
+ return value.replace(/&(?:amp|lt|gt|quot|apos);/g, (entity) => ({
68
+ '&amp;': '&',
69
+ '&lt;': '<',
70
+ '&gt;': '>',
71
+ '&quot;': '"',
72
+ '&apos;': "'",
73
+ })[entity]);
74
+ }
75
+ function parseAttributes(source) {
76
+ const attributes = {};
77
+ ATTRIBUTE_RE.lastIndex = 0;
78
+ let index = 0;
79
+ while (index < source.length) {
80
+ if (/^\s*$/.test(source.slice(index)))
81
+ break;
82
+ ATTRIBUTE_RE.lastIndex = index;
83
+ const match = ATTRIBUTE_RE.exec(source);
84
+ if (match === null)
85
+ return null;
86
+ const [, key, doubleQuoted, singleQuoted] = match;
87
+ if (key === undefined || Object.hasOwn(attributes, key))
88
+ return null;
89
+ attributes[key] = unescapeXmlAttribute(doubleQuoted ?? singleQuoted ?? '');
90
+ index = ATTRIBUTE_RE.lastIndex;
91
+ }
92
+ return attributes;
93
+ }
94
+ function formatAttributes(attributes) {
95
+ return Object.entries(attributes)
96
+ .filter(([, value]) => value !== undefined)
97
+ .map(([key, value]) => {
98
+ if (!XML_ATTRIBUTE_NAME.test(key))
99
+ throw new Error(`Invalid XML attribute name: ${key}`);
100
+ return ` ${key}="${escapeXmlAttribute(String(value))}"`;
101
+ })
102
+ .join('');
103
+ }
104
+ /** Plain text from a generated message's string or text-block content. */
105
+ export function generatedContextText(message) {
106
+ if (typeof message.content === 'string')
107
+ return message.content;
108
+ if (!Array.isArray(message.content))
109
+ return '';
110
+ return message.content
111
+ .filter((block) => typeof block === 'object'
112
+ && block !== null
113
+ && block.type === 'text'
114
+ && typeof block.text === 'string')
115
+ .map((block) => block.text)
116
+ .join('');
117
+ }
118
+ /** Wrap a user-role runtime message in its whole-message card envelope. */
119
+ export function formatCard(kind, facts, body) {
120
+ if (kind === '')
121
+ throw new Error('Runtime card kind is required');
122
+ if (Object.hasOwn(facts, 'kind'))
123
+ throw new Error('Runtime card facts cannot replace kind');
124
+ return `<runtime${formatAttributes({ kind, ...facts })}>${body}</runtime>`;
125
+ }
126
+ /** Wrap a card whose body is DATA, not markup — the only escape on the API
127
+ * path, so a caller can neither emit a malformed card nor smuggle markup into
128
+ * one. `formatCard` keeps its raw body for the trusted crouter producers that
129
+ * deliberately nest markup (bearings blocks, the inbox `<from>`/`<entry>`
130
+ * grammar `parseInboxBody` depends on). */
131
+ export function formatDataCard(kind, facts, body) {
132
+ return formatCard(kind, facts, escapeXmlText(body));
133
+ }
134
+ function parseInboxBody(body) {
135
+ const senders = [];
136
+ const fromRe = /<from\b([^>]*)>([\s\S]*?)<\/from>/g;
137
+ for (const fromMatch of body.matchAll(fromRe)) {
138
+ const attributes = parseAttributes(fromMatch[1] ?? '');
139
+ if (attributes?.id === undefined)
140
+ continue;
141
+ const entries = [];
142
+ const entryBody = fromMatch[2] ?? '';
143
+ const entryRe = /<entry\b([^>]*)>([\s\S]*?)<\/entry>/g;
144
+ for (const entryMatch of entryBody.matchAll(entryRe)) {
145
+ const entryAttributes = parseAttributes(entryMatch[1] ?? '');
146
+ if (entryAttributes?.kind === undefined)
147
+ continue;
148
+ const disposition = entryAttributes.disposition === 'human-answer' || entryAttributes.disposition === 'human-canceled'
149
+ ? entryAttributes.disposition
150
+ : 'report';
151
+ entries.push({
152
+ kind: entryAttributes.kind,
153
+ disposition,
154
+ body: unescapeXmlAttribute(entryMatch[2] ?? ''),
155
+ ...(entryAttributes.ref === undefined ? {} : { ref: entryAttributes.ref }),
156
+ });
157
+ }
158
+ // `updates` and `finished` are derived from the entry list; pre-cut
159
+ // scrollback still carries them as attributes, honored when present.
160
+ const legacyUpdates = attributes.updates === undefined ? Number.NaN : Number(attributes.updates);
161
+ senders.push({
162
+ id: attributes.id,
163
+ updates: Number.isFinite(legacyUpdates) ? legacyUpdates : entries.length,
164
+ finished: attributes.finished === 'true' || entries.some((entry) => entry.kind === 'final'),
165
+ entries,
166
+ });
167
+ }
168
+ return senders;
169
+ }
170
+ function cardFor(kind, facts, body, senders) {
171
+ return { kind, facts, body, senders };
172
+ }
173
+ /** Parse a whole-message runtime envelope, regardless of delivery role. */
174
+ function parseEnvelope(text) {
175
+ const opening = /^<runtime\b([^>]*)>/.exec(text);
176
+ if (opening === null || !text.endsWith('</runtime>'))
177
+ return null;
178
+ const attributes = parseAttributes(opening[1] ?? '');
179
+ if (attributes?.kind === undefined)
180
+ return null;
181
+ const raw = text.slice(opening[0].length, -'</runtime>'.length);
182
+ const { kind, ...factValues } = attributes;
183
+ const facts = Object.freeze(factValues);
184
+ // A namespaced kind can only have been produced through the API path, whose
185
+ // renderer is `formatDataCard`, so its body is escaped by construction and
186
+ // decodes here. A bare kind's body is raw markup written by a core producer.
187
+ const body = kind.includes(':') ? unescapeXmlAttribute(raw) : raw;
188
+ return cardFor(kind, facts, body, kind === 'inbox' ? parseInboxBody(body) : []);
189
+ }
190
+ /** Parse a runtime envelope, with pre-envelope customType compatibility. */
191
+ export function parseCard(message) {
192
+ const text = generatedContextText(message);
193
+ const envelope = parseEnvelope(text);
194
+ if (envelope !== null)
195
+ return envelope;
196
+ if (message.role !== 'custom')
197
+ return null;
198
+ // Pre-envelope custom-message fallback for old sessions. New custom messages
199
+ // classify through their envelope; customType is delivery metadata only.
200
+ const kind = legacyCustomTypeKind(message.customType);
201
+ return kind === undefined ? null : {
202
+ kind,
203
+ facts: Object.freeze({}),
204
+ body: text,
205
+ senders: [],
206
+ };
207
+ }
208
+ /** Whether an inbox entry is the person's own words. A `human-answer` entry
209
+ * whose body is itself a runtime card is a runtime notice — review approvals
210
+ * were delivered that way before they became plain prose — never speech. */
211
+ export function isHumanWordsEntry(entry) {
212
+ return entry.disposition === 'human-answer' && parseEnvelope(entry.body.trim()) === null;
213
+ }
214
+ /** Explain how an inbox entry's ref is read without guessing its filesystem location. */
215
+ export function formatInboxRefInstruction(ref) {
216
+ return ref.startsWith('/')
217
+ ? `Preview only. Read the full body from file \`${ref}\`.`
218
+ : `Read this report with \`crtr canvas read ${ref}\`.`;
219
+ }
220
+ /** Format a complete inbox card from report bodies resolved by the caller. */
221
+ export function formatInboxCard(sections) {
222
+ const body = sections.map((section) => {
223
+ const entries = section.entries.map((entry) => {
224
+ const disposition = entry.disposition === 'human-answer' || entry.disposition === 'human-canceled'
225
+ ? entry.disposition
226
+ : undefined;
227
+ return `<entry${formatAttributes({ kind: entry.kind, ref: entry.ref, disposition })}>${escapeXmlText(entry.body)}</entry>`;
228
+ }).join('\n');
229
+ return `<from${formatAttributes({ id: section.id })}>${entries}</from>`;
230
+ }).join('\n');
231
+ return formatCard('inbox', {}, body);
232
+ }
@@ -0,0 +1,2 @@
1
+ /** A plain object with named properties: null and arrays are not records. */
2
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
@@ -0,0 +1,4 @@
1
+ /** A plain object with named properties: null and arrays are not records. */
2
+ export function isRecord(value) {
3
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
4
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@crouter/api",
3
+ "version": "0.3.377",
4
+ "description": "Typed crtrd /v1 API contract — DTOs, route builders, the error contract, the CrtrClient, and the command-plugin manifest format. Zero runtime dependencies.",
5
+ "type": "module",
6
+ "main": "./dist/api/index.js",
7
+ "types": "./dist/api/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/api/index.d.ts",
11
+ "import": "./dist/api/index.js"
12
+ },
13
+ "./node": {
14
+ "types": "./dist/api/node-transport.d.ts",
15
+ "import": "./dist/api/node-transport.js",
16
+ "require": "./dist/api/node-transport.js",
17
+ "default": "./dist/api/node-transport.js"
18
+ },
19
+ "./plugin-manifest": {
20
+ "types": "./dist/api/plugin-manifest-schema.d.ts",
21
+ "import": "./dist/api/plugin-manifest-schema.js"
22
+ },
23
+ "./cards": {
24
+ "types": "./dist/shared/generated-context.d.ts",
25
+ "import": "./dist/shared/generated-context.js"
26
+ },
27
+ "./command-manifest": {
28
+ "types": "./dist/api/command-manifest/index.d.ts",
29
+ "import": "./dist/api/command-manifest/index.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md"
35
+ ],
36
+ "sideEffects": false,
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/crouton-labs/crouter.git",
46
+ "directory": "packages/crouter-api"
47
+ },
48
+ "license": "GPL-3.0-only"
49
+ }