@nodii/dev 0.6.38 → 0.6.40
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/dist/core.js +1 -1
- package/dist/freeze.js +3 -1
- package/dist/lanes.js +38 -5
- package/dist/runtime-launcher.js +7 -2
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -4,7 +4,7 @@ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { homedir, platform } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
|
|
7
|
-
export const VERSION = "0.6.
|
|
7
|
+
export const VERSION = "0.6.40";
|
|
8
8
|
export const configPath = () =>
|
|
9
9
|
process.env.NODII_DEV_CONFIG_PATH ??
|
|
10
10
|
join(homedir(), ".config/nodii-dev/config.json");
|
package/dist/freeze.js
CHANGED
|
@@ -109,6 +109,7 @@ export const GATHER_LOOKUP_IDS = ["figma-guidelines"];
|
|
|
109
109
|
/** Build the stored contract; Delta Gather keeps lane-authored sections. */
|
|
110
110
|
export function freezeFromGather({
|
|
111
111
|
gather,
|
|
112
|
+
ask,
|
|
112
113
|
boundaries,
|
|
113
114
|
issue,
|
|
114
115
|
laneNotes,
|
|
@@ -131,7 +132,8 @@ export function freezeFromGather({
|
|
|
131
132
|
);
|
|
132
133
|
return {
|
|
133
134
|
objective: {
|
|
134
|
-
ask
|
|
135
|
+
// Dev's Lane contract names the ask; without one, Gather's objective.
|
|
136
|
+
ask: ask ?? gather.objective,
|
|
135
137
|
issue: issue === undefined ? (current?.objective?.issue ?? null) : issue,
|
|
136
138
|
laneNotes:
|
|
137
139
|
laneNotes === undefined
|
package/dist/lanes.js
CHANGED
|
@@ -98,7 +98,23 @@ const agentFollowUp = (ref, shortlistId, flags) => [
|
|
|
98
98
|
.filter((flag) => typeof flags[flag] === "string")
|
|
99
99
|
.map((flag) => `--${flag} ${shellQuote(flags[flag])}`),
|
|
100
100
|
].join(" ");
|
|
101
|
+
// Dev's Lane contract (hub-58): objective, issue body, the coordinator's lane
|
|
102
|
+
// notes, done check and boundaries, printed before anything else.
|
|
103
|
+
const contractLines = (data) => (data.laneContract ? ["Lane contract", data.laneContract, ""] : []);
|
|
104
|
+
async function laneContract(id, request, options) {
|
|
105
|
+
try {
|
|
106
|
+
return (await request("GET", `/api/v1/lanes/${encode(id)}/contract`, undefined, options)).data?.text ?? null;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
// A Dev from before the Lane contract route: say so, never hide it.
|
|
109
|
+
if (error?.code === "HTTP_404" || error?.code === "NOT_FOUND") return "(this Dev does not serve the Lane contract yet; read the lane with `nodii-dev lane show`)";
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Gather never ends on a bare "finished" (hub-58): the next step is the task.
|
|
114
|
+
const nextStepText = (ref, doneCheck) =>
|
|
115
|
+
`Gather is finished. Next: Task. Work the objective until ${doneCheck} is met, then run \`nodii-dev lane land ${ref}\` (or the lane's finish command). Don't stop before that.`;
|
|
101
116
|
const agentShortlistText = (ref, data, flags) => [
|
|
117
|
+
...contractLines(data),
|
|
102
118
|
`Trigger shortlist ${data.shortlistId} · ${JSON.stringify(data.objective)}`,
|
|
103
119
|
"You are Gather's picker (picker: agent). Nothing is recorded yet.",
|
|
104
120
|
...data.triggers.map((trigger) => ` ${trigger.code}\t${trigger.title ?? ""}: ${trigger.sentence ?? ""}${trigger.forcedBy?.length ? ` [forced by ${trigger.forcedBy.join(", ")}]` : ""}`),
|
|
@@ -387,8 +403,10 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
|
|
|
387
403
|
: await request("POST", "/api/v1/rules/shortlist", shortlistBody, { signal });
|
|
388
404
|
const shortlistId = shortlist.data?.shortlistId;
|
|
389
405
|
const triggers = shortlist.data?.triggers ?? [];
|
|
390
|
-
if (byAgent && !byAgent.shortlist)
|
|
391
|
-
|
|
406
|
+
if (byAgent && !byAgent.shortlist) {
|
|
407
|
+
const contract = await laneContract(id, request, { signal });
|
|
408
|
+
return { payload: { data: { recorded: false, picker: "agent", shortlistId, objective, triggers, laneContract: contract } }, human: (p) => agentShortlistText(ref, p.data, flags) };
|
|
409
|
+
}
|
|
392
410
|
// Only the local model pick takes the per-lane pick lock; an agent pick
|
|
393
411
|
// records a choice already made (AGENT-HARNESS-19 + GATHER-NA).
|
|
394
412
|
const pickInput = {
|
|
@@ -409,7 +427,8 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
|
|
|
409
427
|
// No picker on this Mac: the agent picks from the same shortlist (hub-58).
|
|
410
428
|
const fallback = agentFallbackReason(error);
|
|
411
429
|
if (!fallback) throw error;
|
|
412
|
-
|
|
430
|
+
const contract = await laneContract(id, request, { signal });
|
|
431
|
+
return { payload: { data: { recorded: false, picker: "agent", fallback, shortlistId, objective, triggers, laneContract: contract } }, human: (p) => `picker: agent (${p.data.fallback})\n${agentShortlistText(ref, p.data, flags)}` };
|
|
413
432
|
}
|
|
414
433
|
}
|
|
415
434
|
const payload = await request("POST", `/api/v1/lanes/${encode(id)}/gather`, {
|
|
@@ -421,6 +440,7 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
|
|
|
421
440
|
...(sectionLimit !== undefined ? { sectionLimitPerDocument: sectionLimit } : {}),
|
|
422
441
|
shortlistId,
|
|
423
442
|
picker: byAgent ? "agent" : picked.model ?? PICKER_MODEL,
|
|
443
|
+
doneCheck,
|
|
424
444
|
}, { signal });
|
|
425
445
|
const figma = figmaScope.map((file) => ({
|
|
426
446
|
fileKey: file.fileKey,
|
|
@@ -434,7 +454,19 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
|
|
|
434
454
|
lane.launch?.narrowingRemainder && `Narrowing remainder: ${lane.launch.narrowingRemainder}`,
|
|
435
455
|
].filter(Boolean);
|
|
436
456
|
const freezeRequest = (method, path, body, options = {}) => request(method, path, body, { ...options, signal });
|
|
437
|
-
|
|
457
|
+
// Dev's Lane contract is what Gather printed; the Freeze stores it word for word.
|
|
458
|
+
const contract = payload.data?.contract;
|
|
459
|
+
const contractRepos = contract?.boundaries?.repos;
|
|
460
|
+
const fromContract = Boolean(contractRepos?.length && contractRepos.every((repository) => repository.repo && repository.branch && repository.worktree));
|
|
461
|
+
await storeFreeze(id, fromContract ? {
|
|
462
|
+
gather: payload.data,
|
|
463
|
+
ask: contract.objective.ask,
|
|
464
|
+
boundaries: contract.boundaries,
|
|
465
|
+
issue: contract.objective.issue,
|
|
466
|
+
laneNotes: contract.objective.laneNotes,
|
|
467
|
+
doneCheck: contract.objective.doneCheck ?? doneCheck,
|
|
468
|
+
lookups: lookup ? [lookup] : [],
|
|
469
|
+
} : {
|
|
438
470
|
gather: payload.data,
|
|
439
471
|
boundaries: { repos: repositories, ...(declaredFigma ? { figma } : {}) },
|
|
440
472
|
issue,
|
|
@@ -453,7 +485,8 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
|
|
|
453
485
|
// The pick's model and latency ride along in --json (morning check records them).
|
|
454
486
|
const result = { ...payload, picker: { model: picked.model ?? null, latency: picked.latency ?? null, ...(picked.timing ? { timing: picked.timing, stats: picked.stats } : {}) } };
|
|
455
487
|
const timingLine = picked.timing ? `\n${pickTimingLine(picked.timing, picked.stats)}` : "";
|
|
456
|
-
|
|
488
|
+
const doneText = contract?.objective?.doneCheck ?? doneCheck;
|
|
489
|
+
return { payload: result, human: (p) => `${gatherText(p, objective, reasons)}${scopeNotice}${timingLine}\n\n${nextStepText(ref, doneText)}` };
|
|
457
490
|
} catch (error) {
|
|
458
491
|
if (signal.aborted) throw new CliError("GATHER_TIMEOUT", `Gather's Dev requests exceeded their ${Math.round(deadline.ms / 1000)}-second deadline (the local pick runs on its own budget); nothing was retried.`, 1);
|
|
459
492
|
throw error;
|
package/dist/runtime-launcher.js
CHANGED
|
@@ -106,10 +106,15 @@ export function buildRecoveryInvocation(data, { env = process.env, home = homedi
|
|
|
106
106
|
|
|
107
107
|
function buildInvocation(options, { env = process.env, home = homedir() } = {}) {
|
|
108
108
|
const { runId, laneKey, runtime, model, cwd, addDirs, profileHome, devConfigPath } = options;
|
|
109
|
+
// A headless lane has nobody to approve a tool call, so it runs in the
|
|
110
|
+
// operator-approved headless lane mode: no permission prompts, under the
|
|
111
|
+
// lane's own profile, with the harness safety hooks as the guard (they
|
|
112
|
+
// still run in this mode). Without it every Bash call, `nodii-dev lane
|
|
113
|
+
// gather` included, is refused "requires approval" (AGENT-HARNESS-43).
|
|
109
114
|
const args = runtime === "codex"
|
|
110
|
-
? ["exec", "--json", "--cd", cwd, "--model", model, ...addDirs.flatMap((path) => ["--add-dir", path]), "-"]
|
|
115
|
+
? ["exec", "--json", "--dangerously-bypass-approvals-and-sandbox", "--cd", cwd, "--model", model, ...addDirs.flatMap((path) => ["--add-dir", path]), "-"]
|
|
111
116
|
// Claude Code refuses --print with stream-json unless --verbose is set.
|
|
112
|
-
: ["--print", "--output-format", "stream-json", "--verbose", "--model", model, ...addDirs.flatMap((path) => ["--add-dir", path])];
|
|
117
|
+
: ["--print", "--output-format", "stream-json", "--verbose", "--dangerously-skip-permissions", "--model", model, ...addDirs.flatMap((path) => ["--add-dir", path])];
|
|
113
118
|
return {
|
|
114
119
|
command: runtime,
|
|
115
120
|
args,
|