@farmslot/agent-runtime 0.8.1 → 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.
Files changed (97) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +2 -2
  3. package/bin/farmslot-agent.mjs +3 -43
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/native/affinity.test.d.ts +2 -0
  9. package/dist/native/affinity.test.d.ts.map +1 -0
  10. package/dist/native/affinity.test.js +30 -0
  11. package/dist/native/affinity.test.js.map +1 -0
  12. package/dist/native/claude.d.ts +11 -0
  13. package/dist/native/claude.d.ts.map +1 -0
  14. package/dist/native/claude.js +490 -0
  15. package/dist/native/claude.js.map +1 -0
  16. package/dist/native/client.d.ts +21 -0
  17. package/dist/native/client.d.ts.map +1 -0
  18. package/dist/native/client.js +106 -0
  19. package/dist/native/client.js.map +1 -0
  20. package/dist/native/codex.d.ts +3 -0
  21. package/dist/native/codex.d.ts.map +1 -0
  22. package/dist/native/codex.js +266 -0
  23. package/dist/native/codex.js.map +1 -0
  24. package/dist/native/durability.test.d.ts +2 -0
  25. package/dist/native/durability.test.d.ts.map +1 -0
  26. package/dist/native/durability.test.js +516 -0
  27. package/dist/native/durability.test.js.map +1 -0
  28. package/dist/native/host.d.ts +2 -0
  29. package/dist/native/host.d.ts.map +1 -0
  30. package/dist/native/host.js +90 -0
  31. package/dist/native/host.js.map +1 -0
  32. package/dist/native/index.d.ts +4 -0
  33. package/dist/native/index.d.ts.map +1 -0
  34. package/dist/native/index.js +3 -0
  35. package/dist/native/index.js.map +1 -0
  36. package/dist/native/ipc.d.ts +49 -0
  37. package/dist/native/ipc.d.ts.map +1 -0
  38. package/dist/native/ipc.js +109 -0
  39. package/dist/native/ipc.js.map +1 -0
  40. package/dist/native/manager.d.ts +43 -0
  41. package/dist/native/manager.d.ts.map +1 -0
  42. package/dist/native/manager.js +510 -0
  43. package/dist/native/manager.js.map +1 -0
  44. package/dist/native/process-tree.d.ts +15 -0
  45. package/dist/native/process-tree.d.ts.map +1 -0
  46. package/dist/native/process-tree.js +128 -0
  47. package/dist/native/process-tree.js.map +1 -0
  48. package/dist/native/process-tree.test.d.ts +2 -0
  49. package/dist/native/process-tree.test.d.ts.map +1 -0
  50. package/dist/native/process-tree.test.js +49 -0
  51. package/dist/native/process-tree.test.js.map +1 -0
  52. package/dist/native/process.d.ts +34 -0
  53. package/dist/native/process.d.ts.map +1 -0
  54. package/dist/native/process.js +223 -0
  55. package/dist/native/process.js.map +1 -0
  56. package/dist/native/service.d.ts +7 -0
  57. package/dist/native/service.d.ts.map +1 -0
  58. package/dist/native/service.js +117 -0
  59. package/dist/native/service.js.map +1 -0
  60. package/dist/native/storage.d.ts +11 -0
  61. package/dist/native/storage.d.ts.map +1 -0
  62. package/dist/native/storage.js +102 -0
  63. package/dist/native/storage.js.map +1 -0
  64. package/dist/native/supervisor.d.ts +2 -0
  65. package/dist/native/supervisor.d.ts.map +1 -0
  66. package/dist/native/supervisor.js +127 -0
  67. package/dist/native/supervisor.js.map +1 -0
  68. package/dist/native/types.d.ts +26 -0
  69. package/dist/native/types.d.ts.map +1 -0
  70. package/dist/native/types.js +2 -0
  71. package/dist/native/types.js.map +1 -0
  72. package/dist/native/workspace-files.d.ts +6 -0
  73. package/dist/native/workspace-files.d.ts.map +1 -0
  74. package/dist/native/workspace-files.js +62 -0
  75. package/dist/native/workspace-files.js.map +1 -0
  76. package/dist/native/workspace.d.ts +5 -0
  77. package/dist/native/workspace.d.ts.map +1 -0
  78. package/dist/native/workspace.js +120 -0
  79. package/dist/native/workspace.js.map +1 -0
  80. package/dist/task-init/index.d.ts +66 -0
  81. package/dist/task-init/index.d.ts.map +1 -0
  82. package/dist/task-init/index.js +120 -0
  83. package/dist/task-init/index.js.map +1 -0
  84. package/dist/task-init/task-document.d.ts +98 -0
  85. package/dist/task-init/task-document.d.ts.map +1 -0
  86. package/dist/task-init/task-document.js +184 -0
  87. package/dist/task-init/task-document.js.map +1 -0
  88. package/dist/task-init/write.d.ts +48 -0
  89. package/dist/task-init/write.d.ts.map +1 -0
  90. package/dist/task-init/write.js +70 -0
  91. package/dist/task-init/write.js.map +1 -0
  92. package/package.json +13 -4
  93. package/scripts/checklist-target.cjs +5 -3
  94. package/scripts/execution-template-cli.mjs +9 -7
  95. package/scripts/mark-checklist-step.cjs +1 -1
  96. package/scripts/task-init-cli.mjs +166 -0
  97. 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
- main().catch((error) => {
332
- console.error(error instanceof Error ? error.message : String(error));
333
- process.exit(1);
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
+ }
@@ -44,7 +44,7 @@ function printHelp() {
44
44
  [
45
45
  usageLine,
46
46
  '',
47
- 'Task-dir mode: farmslot-agent mark <task-dir> <step> — requires checklist-target.json written by the gateway at task creation or role switch.',
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.',
48
48
  'Override: farmslot-agent mark <task-dir> --checklist SELF-REVIEW.md <step> — optional --signal; signal defaults from checklist name.',
49
49
  'Bootstrap: ./mark start — role-owned signal with status running (no checklist box).',
50
50
  'Progress: ./mark 1, ./mark 2, ... — checks the box and appends checklistTiming.',
@@ -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
- if (!templateContent.includes('## Task') && !templateContent.includes('TASK_DIR:')) {
306
- issues.push('missing `## Task` block or `TASK_DIR:` metadata');
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);