@dna113p/machines 0.3.4 → 0.3.6
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.
- package/README.md +7 -2
- package/claude/statusline.mjs +2 -0
- package/dist/mcp/server.js +2 -2
- package/dist/src/cli.js +58 -4
- package/dist/src/codex.d.ts +2 -0
- package/dist/src/codex.js +2 -0
- package/dist/src/host.d.ts +6 -0
- package/dist/src/host.js +7 -2
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/launcher.d.ts +3 -0
- package/dist/src/launcher.js +3 -0
- package/dist/src/run-graph.d.ts +39 -0
- package/dist/src/run-graph.js +91 -0
- package/dist/src/run-status.d.ts +16 -2
- package/dist/src/run-status.js +56 -3
- package/dist/src/run-ui-page.d.ts +6 -0
- package/dist/src/run-ui-page.js +690 -0
- package/dist/src/run-ui.d.ts +62 -0
- package/dist/src/run-ui.js +258 -0
- package/dist/src/run-viewer.d.ts +2 -0
- package/dist/src/run-viewer.js +1 -0
- package/docs/authoring.md +13 -0
- package/docs/integrations.md +62 -4
- package/package.json +88 -21
- package/skills/machine-builder/SKILL.md +31 -0
- package/skills/machine-delegation/SKILL.md +23 -0
- package/skills/machine-project-setup/SKILL.md +23 -5
- package/skills/machine-project-setup/references/project-workflows.md +4 -0
package/dist/src/run-status.js
CHANGED
|
@@ -2,7 +2,10 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import { machinesUserHome } from "./discovery.js";
|
|
5
|
+
import { statePaths } from "./run-graph.js";
|
|
5
6
|
const staleAfterMilliseconds = 60 * 60 * 1_000;
|
|
7
|
+
/** How many entered states a record keeps: enough for a long run's repairs, small enough to rewrite at every step. */
|
|
8
|
+
export const trailLimit = 200;
|
|
6
9
|
/** How long a record goes on naming a response its run took: longer than a sender waits to hear of it. */
|
|
7
10
|
export const deliveryRetentionMilliseconds = 60_000;
|
|
8
11
|
/** How long a session's watch list is kept after its last change: sessions end without saying so. */
|
|
@@ -59,8 +62,9 @@ export function runDeliveryPath(directory, id, delivery) {
|
|
|
59
62
|
/**
|
|
60
63
|
* Publishes one run for hosts that can only read files, such as a status line,
|
|
61
64
|
* in `runStatusDirectory`. Publication is presentation: it never throws, and it
|
|
62
|
-
* records
|
|
63
|
-
* written, as in a read-only home, is not
|
|
65
|
+
* records no Machine input and only the supplied summary of its output.
|
|
66
|
+
* A run whose first record cannot be written, as in a read-only home, is not
|
|
67
|
+
* published at all.
|
|
64
68
|
*/
|
|
65
69
|
export function createRunStatusPublisher(run, environment = process.env) {
|
|
66
70
|
const directory = runStatusDirectory(environment);
|
|
@@ -79,6 +83,14 @@ export function createRunStatusPublisher(run, environment = process.env) {
|
|
|
79
83
|
let human;
|
|
80
84
|
let deliveries = [];
|
|
81
85
|
let error;
|
|
86
|
+
let note;
|
|
87
|
+
let trail = [];
|
|
88
|
+
const enter = (entered, at) => {
|
|
89
|
+
const path = entered === undefined ? "" : statePaths(entered).join(", ");
|
|
90
|
+
if (path === "" || trail.at(-1)?.state === path)
|
|
91
|
+
return;
|
|
92
|
+
trail = [...trail, { state: path, at }].slice(-trailLimit);
|
|
93
|
+
};
|
|
82
94
|
const publish = () => {
|
|
83
95
|
const record = {
|
|
84
96
|
schemaVersion: 2,
|
|
@@ -99,6 +111,9 @@ export function createRunStatusPublisher(run, environment = process.env) {
|
|
|
99
111
|
...(run.answeredBy === undefined ? {} : { answeredBy: run.answeredBy }),
|
|
100
112
|
...(deliveries.length === 0 ? {} : { deliveries: deliveries.map((delivery) => delivery.id) }),
|
|
101
113
|
...(error === undefined ? {} : { error }),
|
|
114
|
+
...(note === undefined ? {} : { note }),
|
|
115
|
+
...(run.graph === undefined ? {} : { graph: run.graph }),
|
|
116
|
+
...(trail.length === 0 ? {} : { trail }),
|
|
102
117
|
startedAt,
|
|
103
118
|
updatedAt: new Date().toISOString(),
|
|
104
119
|
};
|
|
@@ -153,8 +168,10 @@ export function createRunStatusPublisher(run, environment = process.env) {
|
|
|
153
168
|
// Repeated notifications for an unchanged run do not need another write.
|
|
154
169
|
if (JSON.stringify(next) === JSON.stringify({ status, state, agent, limit, human, deliveries }))
|
|
155
170
|
return;
|
|
156
|
-
if (next.state !== state)
|
|
171
|
+
if (next.state !== state) {
|
|
157
172
|
stateSince = new Date().toISOString();
|
|
173
|
+
enter(next.state, stateSince);
|
|
174
|
+
}
|
|
158
175
|
({ status, state, agent, limit, human, deliveries } = next);
|
|
159
176
|
publish();
|
|
160
177
|
},
|
|
@@ -168,6 +185,8 @@ export function createRunStatusPublisher(run, environment = process.env) {
|
|
|
168
185
|
else {
|
|
169
186
|
status = "completed";
|
|
170
187
|
state = formatState(outcome.state) ?? state;
|
|
188
|
+
note = formatNote(outcome.note);
|
|
189
|
+
enter(state, new Date().toISOString());
|
|
171
190
|
}
|
|
172
191
|
stateSince = undefined;
|
|
173
192
|
agent = undefined;
|
|
@@ -353,9 +372,23 @@ function isRunStatusRecord(value) {
|
|
|
353
372
|
&& (record.answeredBy === undefined || record.answeredBy === "host")
|
|
354
373
|
&& isOptionalStrings(record.deliveries)
|
|
355
374
|
&& isOptionalString(record.error)
|
|
375
|
+
&& isOptionalString(record.note)
|
|
376
|
+
&& (record.graph === undefined || isGraph(record.graph))
|
|
377
|
+
&& (record.trail === undefined || (Array.isArray(record.trail) && record.trail.every((step) => step !== null && typeof step === "object" && typeof step.state === "string" && isTimestamp(step.at))))
|
|
356
378
|
&& isTimestamp(record.startedAt)
|
|
357
379
|
&& isTimestamp(record.updatedAt);
|
|
358
380
|
}
|
|
381
|
+
function isGraph(value) {
|
|
382
|
+
if (value === null || typeof value !== "object")
|
|
383
|
+
return false;
|
|
384
|
+
const graph = value;
|
|
385
|
+
return isOptionalString(graph.initial)
|
|
386
|
+
&& Array.isArray(graph.states) && graph.states.every((state) => state !== null && typeof state === "object"
|
|
387
|
+
&& typeof state.id === "string" && typeof state.kind === "string"
|
|
388
|
+
&& isOptionalString(state.parent) && isOptionalString(state.role))
|
|
389
|
+
&& Array.isArray(graph.edges) && graph.edges.every((edge) => edge !== null && typeof edge === "object"
|
|
390
|
+
&& typeof edge.from === "string" && typeof edge.to === "string" && typeof edge.event === "string");
|
|
391
|
+
}
|
|
359
392
|
function isOptionalString(value) {
|
|
360
393
|
return value === undefined || typeof value === "string";
|
|
361
394
|
}
|
|
@@ -384,6 +417,26 @@ function formatState(state) {
|
|
|
384
417
|
return undefined;
|
|
385
418
|
return typeof state === "string" ? state : JSON.stringify(state);
|
|
386
419
|
}
|
|
420
|
+
/** Only an object's non-empty summary is published from a Machine's output. */
|
|
421
|
+
export function runOutputSummary(output) {
|
|
422
|
+
if (output === null || typeof output !== "object" || Array.isArray(output))
|
|
423
|
+
return undefined;
|
|
424
|
+
const summary = output.summary;
|
|
425
|
+
return typeof summary === "string" && summary.trim() !== "" ? summary : undefined;
|
|
426
|
+
}
|
|
427
|
+
function formatNote(note) {
|
|
428
|
+
const text = note?.trim();
|
|
429
|
+
if (!text)
|
|
430
|
+
return undefined;
|
|
431
|
+
if (text.length <= 2000)
|
|
432
|
+
return text;
|
|
433
|
+
// Reserve one character for the ellipsis, keeping the last whole word that fits.
|
|
434
|
+
let end = 1999;
|
|
435
|
+
while (end > 0 && !/\s/u.test(text[end]))
|
|
436
|
+
end -= 1;
|
|
437
|
+
// A single word longer than the limit still has to fit in the record.
|
|
438
|
+
return `${text.slice(0, end || 1999).trimEnd()}…`;
|
|
439
|
+
}
|
|
387
440
|
/** One short line: a label is a caption, not a place for task text. */
|
|
388
441
|
function formatLabel(label) {
|
|
389
442
|
const line = label?.split(/\r?\n/u).map((part) => part.trim()).find((part) => part !== "");
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page `machine ui` serves: one self-contained document, with no outside
|
|
3
|
+
* scripts, styles, or fonts, so it works offline and loads nothing but its own
|
|
4
|
+
* data. The token lets only this page answer a run.
|
|
5
|
+
*/
|
|
6
|
+
export declare function runUiPage(token: string): string;
|