@onepointfour-packs/flaredeck-agent 0.1.0 → 0.2.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 (73) hide show
  1. package/dist/agent.d.ts +16 -0
  2. package/dist/agent.js +38 -1
  3. package/dist/agent.js.map +1 -1
  4. package/dist/approval.js +39 -1
  5. package/dist/approval.js.map +1 -1
  6. package/dist/browser.d.ts +19 -0
  7. package/dist/browser.js +261 -0
  8. package/dist/browser.js.map +1 -0
  9. package/dist/config.d.ts +18 -0
  10. package/dist/config.js +2 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/desperation.d.ts +24 -0
  13. package/dist/desperation.js +64 -0
  14. package/dist/desperation.js.map +1 -0
  15. package/dist/engine-antigravity.d.ts +7 -0
  16. package/dist/engine-antigravity.js +280 -32
  17. package/dist/engine-antigravity.js.map +1 -1
  18. package/dist/engine.d.ts +28 -0
  19. package/dist/engine.js.map +1 -1
  20. package/dist/env.d.ts +5 -1
  21. package/dist/env.js +19 -1
  22. package/dist/env.js.map +1 -1
  23. package/dist/environment.d.ts +87 -2
  24. package/dist/environment.js +213 -11
  25. package/dist/environment.js.map +1 -1
  26. package/dist/flaredeck-proxy.d.ts +47 -1
  27. package/dist/flaredeck-proxy.js +120 -3
  28. package/dist/flaredeck-proxy.js.map +1 -1
  29. package/dist/gitguard.d.ts +6 -0
  30. package/dist/gitguard.js +30 -1
  31. package/dist/gitguard.js.map +1 -1
  32. package/dist/github.d.ts +23 -1
  33. package/dist/github.js +105 -28
  34. package/dist/github.js.map +1 -1
  35. package/dist/journal.d.ts +23 -0
  36. package/dist/journal.js +142 -0
  37. package/dist/journal.js.map +1 -0
  38. package/dist/localmcp.d.ts +50 -0
  39. package/dist/localmcp.js +229 -0
  40. package/dist/localmcp.js.map +1 -0
  41. package/dist/main.js +4 -1
  42. package/dist/main.js.map +1 -1
  43. package/dist/permissions.d.ts +17 -0
  44. package/dist/permissions.js +38 -1
  45. package/dist/permissions.js.map +1 -1
  46. package/dist/runs.js +3 -2
  47. package/dist/runs.js.map +1 -1
  48. package/dist/sandbox.d.ts +34 -0
  49. package/dist/sandbox.js +122 -2
  50. package/dist/sandbox.js.map +1 -1
  51. package/dist/schedules.d.ts +1 -1
  52. package/dist/schedules.js +3 -2
  53. package/dist/schedules.js.map +1 -1
  54. package/dist/serve.js +461 -39
  55. package/dist/serve.js.map +1 -1
  56. package/dist/stream.d.ts +1 -0
  57. package/dist/team.d.ts +1 -1
  58. package/dist/team.js +3 -2
  59. package/dist/team.js.map +1 -1
  60. package/dist/ui.d.ts +2 -0
  61. package/dist/ui.js +34 -39
  62. package/dist/ui.js.map +1 -1
  63. package/dist/updater-python.d.ts +19 -0
  64. package/dist/updater-python.js +149 -0
  65. package/dist/updater-python.js.map +1 -0
  66. package/dist/workspace.d.ts +2 -0
  67. package/dist/workspace.js +1 -1
  68. package/dist/workspace.js.map +1 -1
  69. package/dist/worktree.d.ts +39 -1
  70. package/dist/worktree.js +147 -4
  71. package/dist/worktree.js.map +1 -1
  72. package/package.json +3 -2
  73. package/python/sidecar.py +209 -7
package/dist/engine.d.ts CHANGED
@@ -70,9 +70,37 @@ export type RunContext = {
70
70
  runs?: ToolSet;
71
71
  schedules?: ToolSet;
72
72
  flaredeck?: ToolSet;
73
+ /** A real browser, when this card has an environment to point it at. */
74
+ browser?: ToolSet;
73
75
  };
74
76
  /** Hooks the harness insists on, whatever the engine: the guards. */
75
77
  guard?: unknown;
78
+ /**
79
+ * Publish this card's branch. An out-of-process engine cannot reach the in-process publishing
80
+ * tool, which left a Gemini agent with no way to publish at all — and reporting that it had.
81
+ */
82
+ publish?: () => Promise<{
83
+ ok: boolean;
84
+ text: string;
85
+ }>;
86
+ /** Why this card may not be called finished yet, or null when it may. */
87
+ finish?: () => Promise<string | null>;
88
+ /** The card a tool call names, so the pane can say what is being worked, not what woke it. */
89
+ noteCard?: (code: string) => void;
90
+ /** Which FlareDeck tools this agent is offered. Absent means all of them. */
91
+ keep?: (name: string) => boolean;
92
+ /**
93
+ * Who this agent is and how it works — the standing brief, as opposed to this turn's. The
94
+ * Claude engine appends it to its system prompt; an out-of-process engine needs it passed.
95
+ */
96
+ standing?: string;
97
+ /** What is in this card's branch, appended to anything the agent says. */
98
+ saved?: () => Promise<{
99
+ branch: string;
100
+ commits: number;
101
+ pushed: boolean;
102
+ known?: boolean;
103
+ } | null>;
76
104
  };
77
105
  export type ToolSet = {
78
106
  server: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAsGA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAA6C;IAC3E,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjG,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC5D,OAAO,IAAI,YAAY,EAAE,CAAC;IAC5B,CAAC;IACD,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;QAC5B,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QACtE,OAAO,IAAI,iBAAiB,EAAE,CAAC;IACjC,CAAC;IACD,6FAA6F;IAC7F,4EAA4E;IAC5E,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,oCAAoC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;CAAG"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AA0HA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAA6C;IAC3E,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjG,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC5D,OAAO,IAAI,YAAY,EAAE,CAAC;IAC5B,CAAC;IACD,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;QAC5B,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QACtE,OAAO,IAAI,iBAAiB,EAAE,CAAC;IACjC,CAAC;IACD,6FAA6F;IAC7F,4EAA4E;IAC5E,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,oCAAoC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;CAAG"}
package/dist/env.d.ts CHANGED
@@ -1 +1,5 @@
1
- export {};
1
+ /**
2
+ * The environment to give a child process: ours, minus the NODE_ENV we invented for Ink. An
3
+ * explicit NODE_ENV from the person's own shell is theirs and is passed through untouched.
4
+ */
5
+ export declare function childEnv(extra?: Record<string, string>): NodeJS.ProcessEnv;
package/dist/env.js CHANGED
@@ -5,6 +5,24 @@
5
5
  // them, so a daemon that repaints a status line all day accumulates a million entries
6
6
  // ("MaxPerformanceEntryBufferExceededWarning") and grows without bound. Nothing else in the
7
7
  // harness reads NODE_ENV, so defaulting it costs nothing and an explicit value still wins.
8
+ //
9
+ // It is NOT free for the agent, though, and that took a while to notice. Every child process
10
+ // inherits it, so `npm ci` in a fresh worktree skipped devDependencies and left no tsc, no tsx
11
+ // and no drizzle-kit — on cards whose own instructions say to typecheck. An agent wrote the
12
+ // workaround into its notes as if it were a property of the project: "shell has
13
+ // NODE_ENV=production ... must run npm ci --include=dev".
14
+ //
15
+ // So the value is remembered, and `childEnv()` hands children the environment as it was.
16
+ const INHERITED = process.env['NODE_ENV'];
8
17
  process.env['NODE_ENV'] ??= 'production';
9
- export {};
18
+ /**
19
+ * The environment to give a child process: ours, minus the NODE_ENV we invented for Ink. An
20
+ * explicit NODE_ENV from the person's own shell is theirs and is passed through untouched.
21
+ */
22
+ export function childEnv(extra = {}) {
23
+ const env = { ...process.env, ...extra };
24
+ if (INHERITED === undefined)
25
+ delete env['NODE_ENV'];
26
+ return env;
27
+ }
10
28
  //# sourceMappingURL=env.js.map
package/dist/env.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,EAAE;AACF,4FAA4F;AAC5F,yFAAyF;AACzF,sFAAsF;AACtF,4FAA4F;AAC5F,2FAA2F;AAC3F,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,CAAC"}
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,EAAE;AACF,4FAA4F;AAC5F,yFAAyF;AACzF,sFAAsF;AACtF,4FAA4F;AAC5F,2FAA2F;AAC3F,EAAE;AACF,6FAA6F;AAC7F,+FAA+F;AAC/F,4FAA4F;AAC5F,gFAAgF;AAChF,0DAA0D;AAC1D,EAAE;AACF,yFAAyF;AACzF,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC1C,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,QAAgC,EAAE;IACzD,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IACzC,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -15,15 +15,59 @@
15
15
  * says, and the agent is told.
16
16
  */
17
17
  export declare const START_SCRIPT: string;
18
+ /**
19
+ * What a project may tell the harness about its own environment.
20
+ *
21
+ * The old contract was `name=url` lines, which says where things are and nothing else — so an
22
+ * agent that got the app running still had to work out how to log into it, how to get data into
23
+ * it, and whether any of that was safe. It guessed, or it asked, or it stopped.
24
+ *
25
+ * A project that wants more says so in JSON, printed on one line or fenced between
26
+ * FLAREDECK_ENV_START and FLAREDECK_ENV_END. Everything in it is optional; a project that only
27
+ * prints `api=http://localhost:3000` keeps working exactly as before.
28
+ */
29
+ export type EnvContract = {
30
+ /** Where things ended up. The old `name=url` lines land here too. */
31
+ parts?: Record<string, string>;
32
+ /** How to get in. Test credentials for a local environment, never production ones. */
33
+ credentials?: Record<string, {
34
+ username?: string;
35
+ password?: string;
36
+ note?: string;
37
+ } | string>;
38
+ /** Things an agent may run, and what each one does. */
39
+ scripts?: Record<string, {
40
+ path?: string;
41
+ does?: string;
42
+ } | string>;
43
+ /** Anything else the next agent needs: "run seed before testing onboarding". */
44
+ notes?: string;
45
+ };
18
46
  export type Started = {
19
47
  ran: boolean;
20
48
  ok: boolean;
21
49
  /** Whatever the script announced: api, ui, db… */
22
50
  endpoints: Record<string, string>;
23
51
  detail: string;
52
+ /** The structured contract, when the project printed one. */
53
+ contract?: EnvContract;
24
54
  };
25
- export declare function hasStartScript(repo: string): boolean;
26
- export declare function startEnvironment(repo: string, onStep: (text: string) => void): Promise<Started>;
55
+ /**
56
+ * Which script brings this project up, and where it is.
57
+ *
58
+ * The path is relative to the REPOSITORY root, which is the only arrangement that works: it is
59
+ * committed, so it is present in every worktree automatically, and it runs with the worktree as
60
+ * its working directory — against this card's code rather than whatever the shared checkout is
61
+ * sitting on. A board declaring `tools/dev.sh` means `<worktree>/tools/dev.sh`.
62
+ */
63
+ export declare function startScriptPath(repo: string, declared?: string): string | null;
64
+ export declare function hasStartScript(repo: string, declared?: string): boolean;
65
+ export declare function startEnvironment(repo: string, onStep: (text: string) => void, declared?: string): Promise<Started>;
66
+ /**
67
+ * The JSON contract, if the script printed one. Fenced output is read first because a script that
68
+ * logs freely cannot be trusted to keep its JSON on one tidy line.
69
+ */
70
+ export declare function readContract(out: string): EnvContract | undefined;
27
71
  /** What the agent is told about where things are, or why it cannot start. */
28
72
  /**
29
73
  * `needsRunning` is Jev's answer to whether this card can be done without the project running —
@@ -37,3 +81,44 @@ export declare function startEnvironment(repo: string, onStep: (text: string) =>
37
81
  * about it.
38
82
  */
39
83
  export declare function environmentBrief(started: Started, repo: string, needsRunning?: boolean): string;
84
+ /**
85
+ * Told to an agent whose card did not look like it needed the project running.
86
+ *
87
+ * Starting it is minutes — an install, a database, servers — and most cards never touch a running
88
+ * thing: a stylesheet nudge, a copy change, a guard clause. The judgement can be wrong, so this
89
+ * says plainly that it was a judgement and how to overturn it, rather than leaving the agent to
90
+ * conclude the project cannot be run here.
91
+ */
92
+ export declare function notStartedBrief(skipped: boolean, repo: string): string;
93
+ /**
94
+ * The rest of what the project said about its environment: how to log in, what may be run, and
95
+ * whatever the last person wished they had been told.
96
+ *
97
+ * Addresses alone leave an agent with a running app it cannot get into. Everything here is
98
+ * optional, and a project that says nothing produces nothing.
99
+ */
100
+ export declare function contractBrief(c: EnvContract | undefined): string;
101
+ /**
102
+ * Whether the environment this card is testing is actually this card's.
103
+ *
104
+ * Several worktrees run side by side, and a front end built against a hardcoded port will, in
105
+ * the second one, reach the FIRST worktree's API. Nothing errors — it answers perfectly, running
106
+ * somebody else's code, so the agent tests a change that is not there or cannot reproduce a bug
107
+ * that is. Guidance alone does not catch a script that gets this wrong; this does.
108
+ *
109
+ * For each address the script printed, find the process listening on that port and ask which
110
+ * directory it is running from. A listener whose working directory is a DIFFERENT worktree is
111
+ * conclusive: that part belongs to another card.
112
+ *
113
+ * Deliberately quiet about everything else. A process started from the repository root, a docker
114
+ * container, anything it cannot resolve — none of those are evidence of a problem, and a check
115
+ * that cries wolf gets ignored exactly when it matters.
116
+ */
117
+ export declare function crossWired(endpoints: Record<string, string>, worktree: string): Promise<string[]>;
118
+ /**
119
+ * What to say about the browser, which is only worth saying when there is something to look at.
120
+ *
121
+ * An agent with a running environment and no way to see it writes "verified by reading the code",
122
+ * which is not verification. An agent that HAS a browser and is not told so writes the same thing.
123
+ */
124
+ export declare function browserBrief(hasBrowser: boolean, endpoints: Record<string, string>): string;
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { existsSync } from 'node:fs';
2
+ import { existsSync, realpathSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  /**
5
5
  * Get the project running, if it has told us how.
@@ -19,14 +19,35 @@ import { join } from 'node:path';
19
19
  */
20
20
  export const START_SCRIPT = join('.flaredeck', 'start.sh');
21
21
  const TIMEOUT_MS = 5 * 60 * 1000;
22
- export function hasStartScript(repo) {
23
- return existsSync(join(repo, START_SCRIPT));
22
+ /**
23
+ * Which script brings this project up, and where it is.
24
+ *
25
+ * The path is relative to the REPOSITORY root, which is the only arrangement that works: it is
26
+ * committed, so it is present in every worktree automatically, and it runs with the worktree as
27
+ * its working directory — against this card's code rather than whatever the shared checkout is
28
+ * sitting on. A board declaring `tools/dev.sh` means `<worktree>/tools/dev.sh`.
29
+ */
30
+ export function startScriptPath(repo, declared) {
31
+ const wanted = (declared ?? '').trim().replace(/^\.\//, '');
32
+ // A declared path that is not there is worth saying out loud rather than silently falling back
33
+ // to a default the board did not ask for.
34
+ if (wanted)
35
+ return existsSync(join(repo, wanted)) ? wanted : null;
36
+ return existsSync(join(repo, START_SCRIPT)) ? START_SCRIPT : null;
24
37
  }
25
- export async function startEnvironment(repo, onStep) {
26
- if (!hasStartScript(repo))
38
+ export function hasStartScript(repo, declared) {
39
+ return startScriptPath(repo, declared) !== null;
40
+ }
41
+ export async function startEnvironment(repo, onStep, declared) {
42
+ const script = startScriptPath(repo, declared);
43
+ if (!script) {
44
+ if ((declared ?? '').trim()) {
45
+ onStep(`this board declares ${declared} as its start script, and it is not in the checkout`);
46
+ }
27
47
  return { ran: false, ok: true, endpoints: {}, detail: '' };
28
- onStep('starting the environment');
29
- const { code, out } = await run(`bash ${START_SCRIPT}`, repo);
48
+ }
49
+ onStep(`starting the environment (${script})`);
50
+ const { code, out } = await run(`bash ${script}`, repo);
30
51
  const endpoints = {};
31
52
  for (const line of out.split('\n')) {
32
53
  // `name=value`, one per line, value running to the end. Tolerant of surrounding noise, since
@@ -36,6 +57,10 @@ export async function startEnvironment(repo, onStep) {
36
57
  if (m?.[1] && m[2])
37
58
  endpoints[m[1]] = m[2];
38
59
  }
60
+ const contract = readContract(out);
61
+ // A contract's own parts are the authority; the loose lines fill anything it did not name.
62
+ for (const [k, v] of Object.entries(contract?.parts ?? {}))
63
+ endpoints[k] = v;
39
64
  const names = Object.keys(endpoints);
40
65
  onStep(code === 0
41
66
  ? `environment ready${names.length ? ` — ${names.join(', ')}` : ''}`
@@ -45,8 +70,29 @@ export async function startEnvironment(repo, onStep) {
45
70
  ok: code === 0,
46
71
  endpoints,
47
72
  detail: out.trim().split('\n').slice(-5).join('\n').slice(0, 600),
73
+ ...(contract ? { contract } : {}),
48
74
  };
49
75
  }
76
+ /**
77
+ * The JSON contract, if the script printed one. Fenced output is read first because a script that
78
+ * logs freely cannot be trusted to keep its JSON on one tidy line.
79
+ */
80
+ export function readContract(out) {
81
+ const fenced = /FLAREDECK_ENV_START\s*([\s\S]*?)\s*FLAREDECK_ENV_END/.exec(out);
82
+ const candidates = fenced?.[1]
83
+ ? [fenced[1]]
84
+ : out.split('\n').filter((l) => l.trim().startsWith('{') && l.trim().endsWith('}'));
85
+ for (const text of candidates.reverse()) {
86
+ try {
87
+ const doc = JSON.parse(text);
88
+ if (doc && typeof doc === 'object' && (doc.parts || doc.credentials || doc.scripts || doc.notes)) {
89
+ return doc;
90
+ }
91
+ }
92
+ catch { /* not this line */ }
93
+ }
94
+ return undefined;
95
+ }
50
96
  /** What the agent is told about where things are, or why it cannot start. */
51
97
  /**
52
98
  * `needsRunning` is Jev's answer to whether this card can be done without the project running —
@@ -75,14 +121,18 @@ export function environmentBrief(started, repo, needsRunning = false) {
75
121
  }
76
122
  if (!started.ok) {
77
123
  return (`\n\nSTOP — ${START_SCRIPT} failed, so the environment is not up:\n${started.detail}\n\n` +
78
- `Do not diagnose the code: nothing is running, so anything you conclude would be wrong. If ` +
79
- `the fix is obviously in the start script itself, fix that. Otherwise say on the card what ` +
80
- `failed, call set-needs-human, and stop.`);
124
+ `This is not your card's problem to solve. A board that issues cards needing the project ` +
125
+ `running owes its agents a start script that works in a fresh worktree, and keeping it ` +
126
+ `working is the lead's job, not yours. Do not diagnose the code — nothing is running, so ` +
127
+ `anything you conclude would be wrong — and do not start rebuilding the environment by ` +
128
+ `hand. Tell your lead what failed, with the output above, say the same on the card, and ` +
129
+ `stop. If the fix is obviously IN the start script and small, you may fix that one file.`);
81
130
  }
82
131
  const listed = Object.entries(started.endpoints).map(([k, v]) => ` · ${k}: ${v}`).join('\n');
83
- return listed
132
+ const head = listed
84
133
  ? `\n\nThe environment is up and these are its addresses — use them rather than assuming ports:\n${listed}`
85
134
  : `\n\nThe environment started cleanly (${repo}/${START_SCRIPT}).`;
135
+ return head + contractBrief(started.contract);
86
136
  }
87
137
  function run(command, cwd) {
88
138
  return new Promise((resolve) => {
@@ -97,4 +147,156 @@ function run(command, cwd) {
97
147
  child.on('error', (e) => { clearTimeout(timer); resolve({ code: 1, out: String(e) }); });
98
148
  });
99
149
  }
150
+ /**
151
+ * Told to an agent whose card did not look like it needed the project running.
152
+ *
153
+ * Starting it is minutes — an install, a database, servers — and most cards never touch a running
154
+ * thing: a stylesheet nudge, a copy change, a guard clause. The judgement can be wrong, so this
155
+ * says plainly that it was a judgement and how to overturn it, rather than leaving the agent to
156
+ * conclude the project cannot be run here.
157
+ */
158
+ export function notStartedBrief(skipped, repo) {
159
+ if (!skipped)
160
+ return '';
161
+ return (`\n\nThe project is NOT running. This card did not look like one that needs it, so nothing ` +
162
+ `was started and you have your checkout and the code. Do not start it out of habit: for a ` +
163
+ `small change, reading the code and the project's own checks (a typecheck, a lint, a verify ` +
164
+ `script) is both faster and better evidence than a screenshot.\n\n` +
165
+ `If you DO need it — you are changing behaviour rather than appearance, or you want to see ` +
166
+ `it in a browser — start it DETACHED, never in the foreground:\n\n` +
167
+ ` ( bash ${START_SCRIPT} > /tmp/env.log 2>&1 & ) ; sleep 25 ; tail -40 /tmp/env.log\n\n` +
168
+ `${START_SCRIPT} brings up servers and does not return, so running it and waiting is a turn ` +
169
+ `that never ends — the harness runs it with a timeout for exactly that reason, and your ` +
170
+ `shell has none. Read the \`name=url\` lines it prints for where things ended up, and poll ` +
171
+ `the log again if it is not ready yet.\n\n` +
172
+ `**A check that fails because something is not RUNNING or not MIGRATED is a reason to start ` +
173
+ `the environment, not a reason to report that you could not run it.** A missing table or ` +
174
+ `column, a refused connection, an empty database — the start script makes a fresh one of its ` +
175
+ `own, already migrated. Start it, run the check again, and report what it then said. Only say ` +
176
+ `a check could not be run once you have tried it with the environment up.`);
177
+ }
178
+ /**
179
+ * The rest of what the project said about its environment: how to log in, what may be run, and
180
+ * whatever the last person wished they had been told.
181
+ *
182
+ * Addresses alone leave an agent with a running app it cannot get into. Everything here is
183
+ * optional, and a project that says nothing produces nothing.
184
+ */
185
+ export function contractBrief(c) {
186
+ if (!c)
187
+ return '';
188
+ const out = [];
189
+ const creds = Object.entries(c.credentials ?? {});
190
+ if (creds.length) {
191
+ out.push('\nHow to sign in (these are this environment\'s own test accounts):');
192
+ for (const [who, v] of creds) {
193
+ if (typeof v === 'string') {
194
+ out.push(` · ${who}: ${v}`);
195
+ continue;
196
+ }
197
+ const bits = [v.username && `username ${v.username}`, v.password && `password ${v.password}`, v.note]
198
+ .filter(Boolean).join(' · ');
199
+ out.push(` · ${who}: ${bits}`);
200
+ }
201
+ }
202
+ const scripts = Object.entries(c.scripts ?? {});
203
+ if (scripts.length) {
204
+ out.push('\nScripts this project says you may run:');
205
+ for (const [name, v] of scripts) {
206
+ if (typeof v === 'string') {
207
+ out.push(` · ${name}: ${v}`);
208
+ continue;
209
+ }
210
+ out.push(` · ${name}${v.path ? ` (${v.path})` : ''}${v.does ? ` — ${v.does}` : ''}`);
211
+ }
212
+ }
213
+ if (c.notes?.trim())
214
+ out.push(`\nFrom the project: ${c.notes.trim()}`);
215
+ return out.length ? `\n${out.join('\n')}` : '';
216
+ }
217
+ /**
218
+ * Whether the environment this card is testing is actually this card's.
219
+ *
220
+ * Several worktrees run side by side, and a front end built against a hardcoded port will, in
221
+ * the second one, reach the FIRST worktree's API. Nothing errors — it answers perfectly, running
222
+ * somebody else's code, so the agent tests a change that is not there or cannot reproduce a bug
223
+ * that is. Guidance alone does not catch a script that gets this wrong; this does.
224
+ *
225
+ * For each address the script printed, find the process listening on that port and ask which
226
+ * directory it is running from. A listener whose working directory is a DIFFERENT worktree is
227
+ * conclusive: that part belongs to another card.
228
+ *
229
+ * Deliberately quiet about everything else. A process started from the repository root, a docker
230
+ * container, anything it cannot resolve — none of those are evidence of a problem, and a check
231
+ * that cries wolf gets ignored exactly when it matters.
232
+ */
233
+ export async function crossWired(endpoints, worktree) {
234
+ // Resolved, both sides. macOS reports /private/var/... where the harness holds /var/..., and a
235
+ // path comparison that misses that accuses an agent of testing someone else's code when it is
236
+ // testing its own — which is how a check earns a reputation for crying wolf and gets ignored.
237
+ const mine = realpathSafe(worktree);
238
+ const found = [];
239
+ for (const [name, value] of Object.entries(endpoints)) {
240
+ const port = /(?::|\blocalhost:|\b127\.0\.0\.1:)(\d{2,5})\b/.exec(String(value))?.[1];
241
+ if (!port)
242
+ continue;
243
+ const owner = await listenerCwd(port);
244
+ if (!owner)
245
+ continue;
246
+ // Only a sibling worktree is proof. Anything else is a legitimate way to run a service.
247
+ const sibling = /\/\.worktrees-[^/]+\/[^/]+/.exec(owner)?.[0];
248
+ if (!sibling)
249
+ continue;
250
+ if (owner === mine || owner.startsWith(`${mine}/`))
251
+ continue;
252
+ found.push(`${name} (${value}) is served by a process running in ${owner} — that is ANOTHER card's ` +
253
+ `checkout, not yours (${mine}). You would be testing its code and believing the result.`);
254
+ }
255
+ return found;
256
+ }
257
+ /** Both sides of every path comparison go through this. See the note in crossWired. */
258
+ function realpathSafe(path) {
259
+ try {
260
+ return realpathSync(path).replace(/\/+$/, '');
261
+ }
262
+ catch {
263
+ return path.replace(/\/+$/, '');
264
+ }
265
+ }
266
+ /** The working directory of whatever is listening on a port, or null. */
267
+ async function listenerCwd(port) {
268
+ const { execFile } = await import('node:child_process');
269
+ const run = (cmd, args) => new Promise((resolve) => {
270
+ execFile(cmd, args, { timeout: 5000 }, (err, out) => resolve(err ? '' : String(out)));
271
+ });
272
+ const pids = (await run('lsof', ['-nP', '-t', `-i:${port}`, '-sTCP:LISTEN'])).trim().split('\n').filter(Boolean);
273
+ for (const pid of pids.slice(0, 3)) {
274
+ const cwd = (await run('lsof', ['-a', '-p', pid, '-d', 'cwd', '-Fn']))
275
+ .split('\n').find((l) => l.startsWith('n'))?.slice(1).trim();
276
+ if (cwd)
277
+ return realpathSafe(cwd);
278
+ }
279
+ return null;
280
+ }
281
+ /**
282
+ * What to say about the browser, which is only worth saying when there is something to look at.
283
+ *
284
+ * An agent with a running environment and no way to see it writes "verified by reading the code",
285
+ * which is not verification. An agent that HAS a browser and is not told so writes the same thing.
286
+ */
287
+ export function browserBrief(hasBrowser, endpoints) {
288
+ if (!hasBrowser)
289
+ return '';
290
+ const part = endpoints['ui'] ? 'ui' : Object.keys(endpoints)[0] ?? 'ui';
291
+ return (`\n\nYou can look at this with a real browser. \`open_page\` takes the NAME of a part — ` +
292
+ `\`${part}\` — not a port, because every worktree runs on different ones. It gives you the page ` +
293
+ `as a list of what is on it, by role and name; click and fill take those same names, so you ` +
294
+ `never write a selector.\n\n` +
295
+ `Use it when a card is about something a person SEES — layout, wording, whether a control ` +
296
+ `works. For anything else the project's own checks are faster and better evidence. If the ` +
297
+ `card asks for a picture, \`screenshot\` saves one in your checkout to attach.\n\n` +
298
+ `When you work something out that the next session would have to work out again — how to ` +
299
+ `sign in, where a screen lives — \`remember\` it. That is kept against the project rather ` +
300
+ `than this card, so it is still there on the next one, and signing in is saved with it.`);
301
+ }
100
302
  //# sourceMappingURL=environment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"environment.js","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"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"}
@@ -52,8 +52,54 @@ export declare function discoverFlaredeck(mcpUrl: string, token: string): Promis
52
52
  * works; only the argument check is looser.
53
53
  */
54
54
  export declare function zodFor(schema: JsonSchema | undefined, notes: string[], path?: string): ZodTypeAny;
55
+ /**
56
+ * What this agent has actually saved, appended to anything it SAYS.
57
+ *
58
+ * The finish gate guards the board: it refuses a card being moved or marked done on an empty
59
+ * branch. It cannot see prose. HAL told its lead a fix was "fully completed" in a message, never
60
+ * touched a status tool, and the gate was never consulted — the card sat in Doing with a branch
61
+ * holding nothing but lockfile churn from an install.
62
+ *
63
+ * So every comment and message from an agent working a card carries one line of ground truth.
64
+ * Not a judgement about what it wrote, which would need to guess at meaning and would guess
65
+ * wrong: just what is in the branch, every time, so a claim and its evidence arrive together.
66
+ */
67
+ export declare function workFooter(state: {
68
+ branch: string;
69
+ commits: number;
70
+ pushed: boolean;
71
+ known?: boolean;
72
+ } | null): string;
73
+ /**
74
+ * Tools a worker is not given.
75
+ *
76
+ * Every tool costs its description and schema in the prompt on EVERY turn — about 3,200
77
+ * characters of description alone across the full set, before schemas — and a longer menu is also
78
+ * a worse menu: more ways to pick the wrong thing. None of these belong to an agent whose job is
79
+ * cards and code.
80
+ *
81
+ * A lead keeps the lot: the portfolio and conversation tools are its surface, not noise.
82
+ */
83
+ export declare const NOT_FOR_WORKERS: readonly string[];
84
+ export declare function cardIn(args: Record<string, unknown>): string;
85
+ export declare function CLAIMS_DONE(name: string, args: Record<string, unknown>): boolean;
55
86
  /** The in-process server: FlareDeck's tools, forwarded with a fresh token each time. */
56
- export declare function flaredeckProxy(serverName: string, mcpUrl: string, token: () => Promise<string>, discovered: Discovered, warn?: (s: string) => void): {
87
+ export declare function flaredeckProxy(serverName: string, mcpUrl: string, token: () => Promise<string>, discovered: Discovered, warn?: (s: string) => void,
88
+ /**
89
+ * Why this card may not be called finished yet, or null when it may. Consulted at the moment a
90
+ * tool would report it done, because that is the only moment a wrong claim can still be caught.
91
+ */
92
+ tooEarly?: () => Promise<string | null>,
93
+ /** The card a call names, so the pane can say what is being worked rather than what woke it. */
94
+ onCard?: (code: string) => void,
95
+ /** Which tools this agent gets. Everything, for a lead. */
96
+ keep?: (name: string) => boolean,
97
+ /** What is in this card's branch right now, for the footer on anything it says. */
98
+ saved?: () => Promise<{
99
+ branch: string;
100
+ commits: number;
101
+ pushed: boolean;
102
+ } | null>): {
57
103
  server: unknown;
58
104
  toolNames: string[];
59
105
  };