@farmslot/agent-runtime 0.8.0 → 0.9.0
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/CHANGELOG.md +14 -0
- package/README.md +2 -2
- package/bin/farmslot-agent.mjs +3 -43
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/native/affinity.test.d.ts +2 -0
- package/dist/native/affinity.test.d.ts.map +1 -0
- package/dist/native/affinity.test.js +30 -0
- package/dist/native/affinity.test.js.map +1 -0
- package/dist/native/claude.d.ts +11 -0
- package/dist/native/claude.d.ts.map +1 -0
- package/dist/native/claude.js +490 -0
- package/dist/native/claude.js.map +1 -0
- package/dist/native/client.d.ts +21 -0
- package/dist/native/client.d.ts.map +1 -0
- package/dist/native/client.js +106 -0
- package/dist/native/client.js.map +1 -0
- package/dist/native/codex.d.ts +3 -0
- package/dist/native/codex.d.ts.map +1 -0
- package/dist/native/codex.js +266 -0
- package/dist/native/codex.js.map +1 -0
- package/dist/native/durability.test.d.ts +2 -0
- package/dist/native/durability.test.d.ts.map +1 -0
- package/dist/native/durability.test.js +516 -0
- package/dist/native/durability.test.js.map +1 -0
- package/dist/native/host.d.ts +2 -0
- package/dist/native/host.d.ts.map +1 -0
- package/dist/native/host.js +90 -0
- package/dist/native/host.js.map +1 -0
- package/dist/native/index.d.ts +4 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +3 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/ipc.d.ts +49 -0
- package/dist/native/ipc.d.ts.map +1 -0
- package/dist/native/ipc.js +109 -0
- package/dist/native/ipc.js.map +1 -0
- package/dist/native/manager.d.ts +43 -0
- package/dist/native/manager.d.ts.map +1 -0
- package/dist/native/manager.js +510 -0
- package/dist/native/manager.js.map +1 -0
- package/dist/native/process-tree.d.ts +15 -0
- package/dist/native/process-tree.d.ts.map +1 -0
- package/dist/native/process-tree.js +128 -0
- package/dist/native/process-tree.js.map +1 -0
- package/dist/native/process-tree.test.d.ts +2 -0
- package/dist/native/process-tree.test.d.ts.map +1 -0
- package/dist/native/process-tree.test.js +49 -0
- package/dist/native/process-tree.test.js.map +1 -0
- package/dist/native/process.d.ts +34 -0
- package/dist/native/process.d.ts.map +1 -0
- package/dist/native/process.js +223 -0
- package/dist/native/process.js.map +1 -0
- package/dist/native/service.d.ts +7 -0
- package/dist/native/service.d.ts.map +1 -0
- package/dist/native/service.js +117 -0
- package/dist/native/service.js.map +1 -0
- package/dist/native/storage.d.ts +11 -0
- package/dist/native/storage.d.ts.map +1 -0
- package/dist/native/storage.js +102 -0
- package/dist/native/storage.js.map +1 -0
- package/dist/native/supervisor.d.ts +2 -0
- package/dist/native/supervisor.d.ts.map +1 -0
- package/dist/native/supervisor.js +127 -0
- package/dist/native/supervisor.js.map +1 -0
- package/dist/native/types.d.ts +26 -0
- package/dist/native/types.d.ts.map +1 -0
- package/dist/native/types.js +2 -0
- package/dist/native/types.js.map +1 -0
- package/dist/native/workspace-files.d.ts +6 -0
- package/dist/native/workspace-files.d.ts.map +1 -0
- package/dist/native/workspace-files.js +62 -0
- package/dist/native/workspace-files.js.map +1 -0
- package/dist/native/workspace.d.ts +5 -0
- package/dist/native/workspace.d.ts.map +1 -0
- package/dist/native/workspace.js +120 -0
- package/dist/native/workspace.js.map +1 -0
- package/dist/task-init/index.d.ts +66 -0
- package/dist/task-init/index.d.ts.map +1 -0
- package/dist/task-init/index.js +120 -0
- package/dist/task-init/index.js.map +1 -0
- package/dist/task-init/task-document.d.ts +98 -0
- package/dist/task-init/task-document.d.ts.map +1 -0
- package/dist/task-init/task-document.js +184 -0
- package/dist/task-init/task-document.js.map +1 -0
- package/dist/task-init/write.d.ts +48 -0
- package/dist/task-init/write.d.ts.map +1 -0
- package/dist/task-init/write.js +70 -0
- package/dist/task-init/write.js.map +1 -0
- package/package.json +13 -4
- package/scripts/checklist-target.cjs +5 -3
- package/scripts/execution-template-cli.mjs +9 -7
- package/scripts/mark-checklist-step.cjs +4 -2
- package/scripts/task-init-cli.mjs +166 -0
- package/scripts/worker-terminal-contract.cjs +15 -3
|
@@ -32,7 +32,7 @@ function takeValue(args, i, flag) {
|
|
|
32
32
|
return value;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async function loadRuntime() {
|
|
35
|
+
export async function loadRuntime() {
|
|
36
36
|
if (!existsSync(distEntry)) {
|
|
37
37
|
throw new Error(
|
|
38
38
|
`compiled package missing at ${distEntry}; run yarn workspace @farmslot/agent-runtime build`,
|
|
@@ -58,7 +58,7 @@ function parseDomainDir(value) {
|
|
|
58
58
|
return { domain, root: value.slice(separator + 1) };
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function parseCatalogArgs(args, { materialize = false } = {}) {
|
|
61
|
+
export function parseCatalogArgs(args, { materialize = false } = {}) {
|
|
62
62
|
const opts = {
|
|
63
63
|
dirs: [],
|
|
64
64
|
domainDirs: [],
|
|
@@ -101,7 +101,7 @@ function parseCatalogArgs(args, { materialize = false } = {}) {
|
|
|
101
101
|
return opts;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function buildSources(opts, runtime) {
|
|
104
|
+
export function buildSources(opts, runtime) {
|
|
105
105
|
const sources = [];
|
|
106
106
|
const domainSourceIds = new Set();
|
|
107
107
|
for (const [index, dir] of opts.dirs.entries()) {
|
|
@@ -328,7 +328,9 @@ async function main() {
|
|
|
328
328
|
else usage(2);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
|
|
332
|
+
main().catch((error) => {
|
|
333
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
334
|
+
process.exit(1);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const fs = require('node:fs');
|
|
3
3
|
const path = require('node:path');
|
|
4
|
+
const { randomUUID } = require('node:crypto');
|
|
4
5
|
const { spawnSync } = require('node:child_process');
|
|
5
6
|
const {
|
|
6
7
|
expandedArtifactsForCommand,
|
|
@@ -43,7 +44,7 @@ function printHelp() {
|
|
|
43
44
|
[
|
|
44
45
|
usageLine,
|
|
45
46
|
'',
|
|
46
|
-
'Task-dir mode: farmslot-agent mark <task-dir> <step> —
|
|
47
|
+
'Task-dir mode: farmslot-agent mark <task-dir> <step> — marks CHECKLIST.md (or TASK.md) unless checklist-target.json, written by a role switch, points elsewhere.',
|
|
47
48
|
'Override: farmslot-agent mark <task-dir> --checklist SELF-REVIEW.md <step> — optional --signal; signal defaults from checklist name.',
|
|
48
49
|
'Bootstrap: ./mark start — role-owned signal with status running (no checklist box).',
|
|
49
50
|
'Progress: ./mark 1, ./mark 2, ... — checks the box and appends checklistTiming.',
|
|
@@ -137,7 +138,7 @@ if (terminalCommand === 'no-change' || terminalCommand === 'blocked') {
|
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
const SIGNAL_PASSTHROUGH_KEYS = ['role', 'contextId', 'prNumber'];
|
|
141
|
+
const SIGNAL_PASSTHROUGH_KEYS = ['role', 'contextId', 'attemptId', 'prNumber'];
|
|
141
142
|
|
|
142
143
|
function pickSignalPassthrough(signal) {
|
|
143
144
|
const out = {};
|
|
@@ -361,6 +362,7 @@ function assertTerminalPackagedEvidence(taskPath, taskDir, terminalCommand) {
|
|
|
361
362
|
function buildSignalUpdate(signal, terminal, target, timing, events, now, taskPath, _taskDir) {
|
|
362
363
|
const base = {
|
|
363
364
|
...pickSignalPassthrough(signal),
|
|
365
|
+
...(isStartCommand ? { attemptId: randomUUID() } : {}),
|
|
364
366
|
step: target?.label ?? signal.step ?? 'complete',
|
|
365
367
|
checklistTiming: {
|
|
366
368
|
schemaVersion: 1,
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `farmslot-agent task init`: write a task directory from a selected execution
|
|
4
|
+
* template. Thin wrappers (mm-harness) add their own defaults and forward here.
|
|
5
|
+
*/
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
|
|
11
|
+
import { buildSources, loadRuntime, parseCatalogArgs } from './execution-template-cli.mjs';
|
|
12
|
+
|
|
13
|
+
function usage(exitCode = 0) {
|
|
14
|
+
const text = [
|
|
15
|
+
'Usage: task init <task-dir> --flow f --run-mode m --platform p --template id --title t [options]',
|
|
16
|
+
'',
|
|
17
|
+
' Template sources: --dir <path> --domain-dir <domain=path> --project-worker <path>',
|
|
18
|
+
' --package-templates <path> [--package-id id] [--project-name name] [--domain d]',
|
|
19
|
+
' Task: --title t [--task-text s | --task-file path] [--ticket key] [--source-ref url]',
|
|
20
|
+
' Identity: [--surface s] [--project name] [--repo owner/name] [--attempt-id id]',
|
|
21
|
+
' Rendering: [--var KEY=VALUE]... [--task-dir-label label] [--mode-preamble text]',
|
|
22
|
+
' [--addendum-file path] [--mark-command "cmd"] [--json]',
|
|
23
|
+
'',
|
|
24
|
+
'Writes TASK.md, CHECKLIST.md, mark, inputs/handoff.json, inputs/worker-terminal-contract.json.',
|
|
25
|
+
].join('\n');
|
|
26
|
+
(exitCode === 0 ? console.log : console.error)(text);
|
|
27
|
+
process.exit(exitCode);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function takeValue(args, i, flag) {
|
|
31
|
+
const value = args[i + 1];
|
|
32
|
+
if (value === undefined) throw new Error(`${flag} requires a value`);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseArgs(args) {
|
|
37
|
+
const opts = {
|
|
38
|
+
taskDir: null,
|
|
39
|
+
template: null,
|
|
40
|
+
title: null,
|
|
41
|
+
taskText: null,
|
|
42
|
+
taskFile: null,
|
|
43
|
+
ticket: null,
|
|
44
|
+
sourceRef: null,
|
|
45
|
+
surface: 'cli',
|
|
46
|
+
project: null,
|
|
47
|
+
repo: null,
|
|
48
|
+
attemptId: null,
|
|
49
|
+
vars: {},
|
|
50
|
+
taskDirLabel: null,
|
|
51
|
+
modePreamble: null,
|
|
52
|
+
addendumFile: null,
|
|
53
|
+
markCommand: null,
|
|
54
|
+
json: false,
|
|
55
|
+
};
|
|
56
|
+
const catalogArgs = [];
|
|
57
|
+
// The task dir is the first positional, before any flag, as the usage line says.
|
|
58
|
+
if (args[0] && !args[0].startsWith('-')) opts.taskDir = args.shift();
|
|
59
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
60
|
+
const arg = args[i];
|
|
61
|
+
if (arg === '--template') opts.template = takeValue(args, i++, arg);
|
|
62
|
+
else if (arg === '--title') opts.title = takeValue(args, i++, arg);
|
|
63
|
+
else if (arg === '--task-text') opts.taskText = takeValue(args, i++, arg);
|
|
64
|
+
else if (arg === '--task-file') opts.taskFile = takeValue(args, i++, arg);
|
|
65
|
+
else if (arg === '--ticket') opts.ticket = takeValue(args, i++, arg);
|
|
66
|
+
else if (arg === '--source-ref') opts.sourceRef = takeValue(args, i++, arg);
|
|
67
|
+
else if (arg === '--surface') opts.surface = takeValue(args, i++, arg);
|
|
68
|
+
else if (arg === '--project') opts.project = takeValue(args, i++, arg);
|
|
69
|
+
else if (arg === '--repo') opts.repo = takeValue(args, i++, arg);
|
|
70
|
+
else if (arg === '--attempt-id') opts.attemptId = takeValue(args, i++, arg);
|
|
71
|
+
else if (arg === '--task-dir-label') opts.taskDirLabel = takeValue(args, i++, arg);
|
|
72
|
+
else if (arg === '--mode-preamble') opts.modePreamble = takeValue(args, i++, arg);
|
|
73
|
+
else if (arg === '--addendum-file') opts.addendumFile = takeValue(args, i++, arg);
|
|
74
|
+
else if (arg === '--mark-command') opts.markCommand = takeValue(args, i++, arg);
|
|
75
|
+
else if (arg === '--var') {
|
|
76
|
+
const pair = takeValue(args, i++, arg);
|
|
77
|
+
const eq = pair.indexOf('=');
|
|
78
|
+
if (eq <= 0) throw new Error('--var must use KEY=VALUE');
|
|
79
|
+
opts.vars[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
80
|
+
} else if (arg === '--json') {
|
|
81
|
+
opts.json = true;
|
|
82
|
+
catalogArgs.push(arg);
|
|
83
|
+
} else if (arg === '-h' || arg === '--help') usage(0);
|
|
84
|
+
else catalogArgs.push(arg);
|
|
85
|
+
}
|
|
86
|
+
if (!opts.taskDir) throw new Error('task init requires <task-dir>');
|
|
87
|
+
if (!opts.template) throw new Error('task init requires --template <id>');
|
|
88
|
+
if (!opts.title) throw new Error('task init requires --title');
|
|
89
|
+
const catalog = parseCatalogArgs([...catalogArgs, '--id', opts.template]);
|
|
90
|
+
if (!catalog.flow) throw new Error('task init requires --flow');
|
|
91
|
+
if (!catalog.runMode) throw new Error('task init requires --run-mode');
|
|
92
|
+
if (!catalog.platform) throw new Error('task init requires --platform');
|
|
93
|
+
return { opts, catalog };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function gitOutput(args) {
|
|
97
|
+
try {
|
|
98
|
+
return execFileSync('git', args, {
|
|
99
|
+
encoding: 'utf8',
|
|
100
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
101
|
+
}).trim();
|
|
102
|
+
} catch {
|
|
103
|
+
// Not a git checkout, or no remote: identity falls back to the directory name.
|
|
104
|
+
return '';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function main() {
|
|
109
|
+
const args = process.argv.slice(2);
|
|
110
|
+
if (args.length === 0 || args[0] === '-h' || args[0] === '--help') usage(0);
|
|
111
|
+
const { opts, catalog } = parseArgs(args);
|
|
112
|
+
const runtime = await loadRuntime();
|
|
113
|
+
|
|
114
|
+
const description = opts.taskFile
|
|
115
|
+
? readFileSync(path.resolve(opts.taskFile), 'utf8').trim()
|
|
116
|
+
: (opts.taskText ?? '').trim();
|
|
117
|
+
const toplevel = gitOutput(['rev-parse', '--show-toplevel']);
|
|
118
|
+
const originUrl = gitOutput(['remote', 'get-url', 'origin']);
|
|
119
|
+
const repo = opts.repo ?? (originUrl ? runtime.portableRepoIdentity(originUrl) : undefined);
|
|
120
|
+
const project =
|
|
121
|
+
opts.project ??
|
|
122
|
+
(repo ? repo.split('/').pop() : path.basename(toplevel || process.cwd())).toLowerCase();
|
|
123
|
+
// Rendered inside taskInit with the same vars as the checklist, so an addendum
|
|
124
|
+
// that works on the farm works here.
|
|
125
|
+
const addendum = opts.addendumFile ? readFileSync(path.resolve(opts.addendumFile), 'utf8') : null;
|
|
126
|
+
|
|
127
|
+
const result = await runtime.taskInit({
|
|
128
|
+
taskDir: path.resolve(opts.taskDir),
|
|
129
|
+
taskDirLabel: opts.taskDirLabel ?? opts.taskDir,
|
|
130
|
+
flow: catalog.flow,
|
|
131
|
+
runMode: catalog.runMode,
|
|
132
|
+
platform: catalog.platform,
|
|
133
|
+
...(catalog.domain ? { domain: catalog.domain } : {}),
|
|
134
|
+
template: { sources: buildSources(catalog, runtime), explicitId: opts.template },
|
|
135
|
+
task: {
|
|
136
|
+
title: opts.title,
|
|
137
|
+
description,
|
|
138
|
+
sourceKind: opts.taskFile ? 'file' : 'text',
|
|
139
|
+
...(opts.ticket ? { ticket: opts.ticket } : {}),
|
|
140
|
+
...(opts.sourceRef ? { sourceRef: opts.sourceRef } : {}),
|
|
141
|
+
},
|
|
142
|
+
handoff: {
|
|
143
|
+
surface: opts.surface,
|
|
144
|
+
project,
|
|
145
|
+
...(repo ? { repo } : {}),
|
|
146
|
+
...(opts.attemptId ? { attemptId: opts.attemptId } : {}),
|
|
147
|
+
},
|
|
148
|
+
vars: opts.vars,
|
|
149
|
+
...(opts.modePreamble ? { modePreamble: opts.modePreamble } : {}),
|
|
150
|
+
addendum,
|
|
151
|
+
...(opts.markCommand ? { markCommand: opts.markCommand } : {}),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (opts.json) {
|
|
155
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
156
|
+
} else {
|
|
157
|
+
process.stdout.write(`Task document: ${result.taskDocument}\nChecklist: ${result.checklist}\n`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
|
|
162
|
+
main().catch((error) => {
|
|
163
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
@@ -286,8 +286,11 @@ function templateBodyScope(content) {
|
|
|
286
286
|
/**
|
|
287
287
|
* Deterministic structure checks beyond terminal artifact contract.
|
|
288
288
|
* @param {string} templateContent
|
|
289
|
+
* @param {{ roleChecklist?: boolean }} [options] roleChecklist: a nested-loop role
|
|
290
|
+
* checklist (self-review, self-review-fix, ci-fix) rendered beside an existing
|
|
291
|
+
* task dir; it may keep its own `## Task` block.
|
|
289
292
|
*/
|
|
290
|
-
function lintWorkerTemplateStructure(templateContent) {
|
|
293
|
+
function lintWorkerTemplateStructure(templateContent, options = {}) {
|
|
291
294
|
/** @type {string[]} */
|
|
292
295
|
const issues = [];
|
|
293
296
|
if (/(?<!\{)\{TASK_DIR\}(?!\})/.test(templateContent)) {
|
|
@@ -302,8 +305,17 @@ function lintWorkerTemplateStructure(templateContent) {
|
|
|
302
305
|
issues.push('terminal template missing `## Checklist` or `## Completion signal` section');
|
|
303
306
|
}
|
|
304
307
|
|
|
305
|
-
|
|
306
|
-
|
|
308
|
+
// The task writer generates TASK.md (ticket block, description, acceptance
|
|
309
|
+
// criteria) and renders the flow template into CHECKLIST.md. A flow template
|
|
310
|
+
// that still carries the old `## Task` header duplicates that block inside
|
|
311
|
+
// the checklist.
|
|
312
|
+
if (
|
|
313
|
+
!options.roleChecklist &&
|
|
314
|
+
(/^## Task\s*$/m.test(templateContent) || /^TASK_DIR:\s/m.test(templateContent))
|
|
315
|
+
) {
|
|
316
|
+
issues.push(
|
|
317
|
+
'carries a `## Task` block or `TASK_DIR:` line — the task writer generates TASK.md; keep only the checklist',
|
|
318
|
+
);
|
|
307
319
|
}
|
|
308
320
|
|
|
309
321
|
const scope = templateBodyScope(templateContent);
|