@ours.network/fleet 0.19.0-nightly.13 → 0.19.0-nightly.14

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,49 @@
1
+ import type { RoomOrchestrationState, TaskState } from './types.js';
2
+ export declare const MARKDOWN_MAX_CODE_POINTS = 3500;
3
+ export declare const MARKDOWN_MAX_BYTES = 12000;
4
+ export interface StatusPresentation {
5
+ icon: string;
6
+ word: string;
7
+ }
8
+ export type MarkdownField = {
9
+ label: string;
10
+ value: unknown;
11
+ kind?: 'prose' | 'code' | 'markdown';
12
+ multiline?: boolean;
13
+ };
14
+ export type MarkdownSection = {
15
+ heading?: string;
16
+ items?: unknown[];
17
+ markdownItems?: string[];
18
+ prose?: unknown;
19
+ };
20
+ export declare function withinMarkdownBounds(value: string): boolean;
21
+ /** Escaped single-line prose suitable for a field or list item. */
22
+ export declare function markdownProse(value: unknown): string;
23
+ /** Escaped heading text. Newlines and heading/list syntax cannot escape the heading. */
24
+ export declare function markdownHeading(value: unknown): string;
25
+ /** Escaped multiline prose. Intended line breaks remain line breaks. */
26
+ export declare function markdownMultiline(value: unknown): string;
27
+ /** CommonMark code span with a delimiter longer than every run in the value. */
28
+ export declare function markdownCode(value: unknown): string;
29
+ export declare function taskStatus(value: TaskState | string): string;
30
+ export declare function roomStatus(value: RoomOrchestrationState | string): string;
31
+ export declare function renderMarkdownResult(input: {
32
+ icon: string;
33
+ title: string;
34
+ fields?: MarkdownField[];
35
+ sections?: MarkdownSection[];
36
+ }): string;
37
+ export declare function renderMarkdownList(input: {
38
+ icon: string;
39
+ title: string;
40
+ empty: string;
41
+ records: string[];
42
+ }): string;
43
+ export type FailureKind = 'usage' | 'not_found' | 'conflict' | 'state' | 'pending' | 'unexpected';
44
+ export declare function renderMarkdownFailure(input: {
45
+ kind: FailureKind;
46
+ subject: string;
47
+ detail?: unknown;
48
+ action: string;
49
+ }): string;
@@ -0,0 +1,206 @@
1
+ import { Buffer } from 'node:buffer';
2
+ export const MARKDOWN_MAX_CODE_POINTS = 3_500;
3
+ export const MARKDOWN_MAX_BYTES = 12_000;
4
+ const FIELD_MAX_CODE_POINTS = 900;
5
+ const FIELD_MAX_BYTES = 3_000;
6
+ const ID_MAX_CODE_POINTS = 160;
7
+ const ID_MAX_BYTES = 640;
8
+ const TRUNCATED = '_… output truncated._';
9
+ const SPOOFING_CONTROLS = /[\u200b-\u200f\u202a-\u202e\u2060-\u206f\ufeff]/g;
10
+ const SINGLE_LINE_CONTROLS = /[\u0000-\u001f\u007f-\u009f]/g;
11
+ const MULTILINE_CONTROLS = /[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/g;
12
+ const PROSE_PUNCTUATION = /([\\`*_[\]<>])/g;
13
+ const HEADING_PUNCTUATION = /([\\`*_[\]<>#])/g;
14
+ const TASK_STATUS = {
15
+ backlog: { icon: '⏸️', word: 'Backlog' },
16
+ provisioning: { icon: '⏳', word: 'Provisioning' },
17
+ active: { icon: '🟢', word: 'Active' },
18
+ review: { icon: '🟡', word: 'Review' },
19
+ done: { icon: '✅', word: 'Done' },
20
+ cancelled: { icon: '🚫', word: 'Cancelled' },
21
+ failed: { icon: '❌', word: 'Failed' },
22
+ };
23
+ const ROOM_STATUS = {
24
+ provisioning: { icon: '⏳', word: 'Provisioning' },
25
+ active: { icon: '🟢', word: 'Active' },
26
+ closing: { icon: '⏳', word: 'Closing' },
27
+ closed: { icon: '🔒', word: 'Closed' },
28
+ };
29
+ const codePoints = (value) => Array.from(value).length;
30
+ const bytes = (value) => Buffer.byteLength(value, 'utf8');
31
+ export function withinMarkdownBounds(value) {
32
+ return codePoints(value) <= MARKDOWN_MAX_CODE_POINTS && bytes(value) <= MARKDOWN_MAX_BYTES;
33
+ }
34
+ function normalize(value, multiline) {
35
+ let text = String(value ?? '').replace(/\r\n?/g, '\n').replace(SPOOFING_CONTROLS, '�');
36
+ if (multiline)
37
+ text = text.replace(MULTILINE_CONTROLS, '�');
38
+ else
39
+ text = text.replace(SINGLE_LINE_CONTROLS, ' ').replace(/\n+/g, ' ');
40
+ return text;
41
+ }
42
+ function boundPlain(value, maxPoints, maxBytes) {
43
+ if (codePoints(value) <= maxPoints && bytes(value) <= maxBytes)
44
+ return value;
45
+ const suffix = '…';
46
+ let out = '';
47
+ for (const point of value) {
48
+ const next = out + point + suffix;
49
+ if (codePoints(next) > maxPoints || bytes(next) > maxBytes)
50
+ break;
51
+ out += point;
52
+ }
53
+ return out.replace(/\s+$/u, '') + suffix;
54
+ }
55
+ /** Escaped single-line prose suitable for a field or list item. */
56
+ export function markdownProse(value) {
57
+ return boundPlain(normalize(value, false).trim(), FIELD_MAX_CODE_POINTS, FIELD_MAX_BYTES)
58
+ .replace(PROSE_PUNCTUATION, '\\$1');
59
+ }
60
+ /** Escaped heading text. Newlines and heading/list syntax cannot escape the heading. */
61
+ export function markdownHeading(value) {
62
+ return boundPlain(normalize(value, false).trim(), 160, 640)
63
+ .replace(HEADING_PUNCTUATION, '\\$1');
64
+ }
65
+ /** Escaped multiline prose. Intended line breaks remain line breaks. */
66
+ export function markdownMultiline(value) {
67
+ const bounded = boundPlain(normalize(value, true).trim(), FIELD_MAX_CODE_POINTS, FIELD_MAX_BYTES);
68
+ return bounded.split('\n').map(line => {
69
+ const escaped = line.replace(PROSE_PUNCTUATION, '\\$1');
70
+ // A multiline value is commonly nested inside a block quote. Escape every
71
+ // still-active line-leading block marker so user text cannot create nested
72
+ // headings, lists, quotes, thematic breaks, or tilde fences.
73
+ return escaped
74
+ .replace(/^([ \t]*)(#{1,6}|[-+>])(?=[ \t]|$)/u, '$1\\$2')
75
+ .replace(/^([ \t]*)(\d{1,9})([.)])(?=[ \t]|$)/u, '$1$2\\$3')
76
+ .replace(/^([ \t]*)(-{3,})(?=[ \t]*$)/u, '$1\\$2')
77
+ .replace(/^([ \t]*)(~{3,})/u, '$1\\$2');
78
+ }).join('\n');
79
+ }
80
+ /** CommonMark code span with a delimiter longer than every run in the value. */
81
+ export function markdownCode(value) {
82
+ const normalized = normalize(value, false);
83
+ const text = boundPlain(normalized || '—', ID_MAX_CODE_POINTS, ID_MAX_BYTES);
84
+ const longest = Math.max(0, ...[...text.matchAll(/`+/g)].map(match => match[0].length));
85
+ const fence = '`'.repeat(longest + 1);
86
+ const pad = /^`|`$/.test(text) || (/^ | $/.test(text) && !/^ +$/.test(text));
87
+ return `${fence}${pad ? ' ' : ''}${text}${pad ? ' ' : ''}${fence}`;
88
+ }
89
+ export function taskStatus(value) {
90
+ const status = TASK_STATUS[value];
91
+ return status ? `${status.icon} ${status.word}` : `⚪ Unknown (${markdownProse(value)})`;
92
+ }
93
+ export function roomStatus(value) {
94
+ const status = ROOM_STATUS[value];
95
+ return status ? `${status.icon} ${status.word}` : `⚪ Unknown (${markdownProse(value)})`;
96
+ }
97
+ function addBlock(blocks, block) {
98
+ const candidate = [...blocks, block, TRUNCATED].join('\n\n');
99
+ if (!withinMarkdownBounds(candidate))
100
+ return false;
101
+ blocks.push(block);
102
+ return true;
103
+ }
104
+ function finalize(blocks, truncated) {
105
+ if (truncated)
106
+ blocks.push(TRUNCATED);
107
+ const output = blocks.join('\n\n');
108
+ if (!withinMarkdownBounds(output))
109
+ throw new Error('Markdown result exceeded its transport bounds');
110
+ return output;
111
+ }
112
+ export function renderMarkdownResult(input) {
113
+ const blocks = [`## ${input.icon} ${markdownHeading(input.title)}`];
114
+ let truncated = false;
115
+ for (const field of input.fields ?? []) {
116
+ const label = markdownHeading(field.label);
117
+ const value = field.kind === 'code' ? markdownCode(field.value)
118
+ : field.kind === 'markdown' ? String(field.value)
119
+ : field.multiline ? markdownMultiline(field.value) : markdownProse(field.value);
120
+ const rendered = field.multiline
121
+ ? `**${label}:**\n\n${value.split('\n').map(line => `> ${line}`).join('\n')}`
122
+ : `- **${label}:** ${value || '—'}`;
123
+ if (!addBlock(blocks, rendered)) {
124
+ truncated = true;
125
+ break;
126
+ }
127
+ }
128
+ if (!truncated)
129
+ for (const section of input.sections ?? []) {
130
+ const lines = [];
131
+ if (section.heading)
132
+ lines.push(`### ${markdownHeading(section.heading)}`);
133
+ if (section.prose !== undefined)
134
+ lines.push(markdownMultiline(section.prose));
135
+ const items = [
136
+ ...(section.items ?? []).map(markdownProse),
137
+ ...(section.markdownItems ?? []),
138
+ ];
139
+ let admitted = 0;
140
+ for (const item of items) {
141
+ const next = [...lines, `- ${item}`];
142
+ const remaining = items.length - admitted - 1;
143
+ const omission = remaining
144
+ ? `- _${remaining} more ${remaining === 1 ? 'result' : 'results'} omitted\\._` : undefined;
145
+ const candidate = [...blocks, [...next, ...(omission ? [omission] : [])].join('\n'), TRUNCATED]
146
+ .join('\n\n');
147
+ if (!withinMarkdownBounds(candidate))
148
+ break;
149
+ lines.push(`- ${item}`);
150
+ admitted++;
151
+ }
152
+ if (admitted < items.length) {
153
+ const omitted = items.length - admitted;
154
+ lines.push(`- _${omitted} more ${omitted === 1 ? 'result' : 'results'} omitted\\._`);
155
+ }
156
+ if (!addBlock(blocks, lines.join('\n'))) {
157
+ truncated = true;
158
+ break;
159
+ }
160
+ }
161
+ return finalize(blocks, truncated);
162
+ }
163
+ export function renderMarkdownList(input) {
164
+ const blocks = [`## ${input.icon} ${markdownHeading(input.title)}`];
165
+ if (!input.records.length)
166
+ return finalize([...blocks, markdownProse(input.empty)], false);
167
+ let admitted = 0;
168
+ for (const record of input.records) {
169
+ const remaining = input.records.length - admitted - 1;
170
+ const rendered = `- ${record}`;
171
+ const omission = remaining
172
+ ? `- _${remaining} more ${remaining === 1 ? 'result' : 'results'} omitted\\._` : '';
173
+ const candidate = [...blocks, rendered, ...(omission ? [omission] : [])].join('\n\n');
174
+ if (!withinMarkdownBounds(candidate))
175
+ break;
176
+ blocks.push(rendered);
177
+ admitted++;
178
+ }
179
+ if (admitted < input.records.length) {
180
+ const omitted = input.records.length - admitted;
181
+ const notice = `- _${omitted} more ${omitted === 1 ? 'result' : 'results'} omitted\\._`;
182
+ if (!addBlock(blocks, notice))
183
+ return finalize(blocks, true);
184
+ }
185
+ return finalize(blocks, false);
186
+ }
187
+ export function renderMarkdownFailure(input) {
188
+ const titles = {
189
+ usage: 'Invalid command',
190
+ not_found: 'Not found',
191
+ conflict: 'Conflict',
192
+ state: 'Action not allowed',
193
+ pending: 'Action still pending',
194
+ unexpected: 'Command failed',
195
+ };
196
+ return renderMarkdownResult({
197
+ icon: input.kind === 'pending' ? '⏳' : '⚠️',
198
+ title: titles[input.kind],
199
+ fields: [
200
+ { label: 'Command', value: input.subject, kind: 'code' },
201
+ ...(input.detail !== undefined && input.kind !== 'unexpected'
202
+ ? [{ label: 'Reason', value: input.detail }] : []),
203
+ ],
204
+ sections: [{ heading: 'Next step', items: [input.action] }],
205
+ });
206
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/fleet",
3
- "version": "0.19.0-nightly.13",
3
+ "version": "0.19.0-nightly.14",
4
4
  "description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, tmux or ACP sessions, supervision, and ours.network messaging.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",