@humain/humain-code 0.3.7 → 0.4.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 +45 -0
- package/dist/bundle/agent-monitor.js +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +18 -17
- package/dist/bundle/chunks/{chunk-I2VOYPDI.js → chunk-2KKLZK2N.js} +1 -1
- package/dist/bundle/chunks/{chunk-UDTXD6EF.js → chunk-2Q2O3GZ4.js} +721 -717
- package/dist/bundle/chunks/{chunk-IYCS4ZUX.js → chunk-3ZI2TEVB.js} +1 -1
- package/dist/bundle/chunks/{chunk-ANSL5PYD.js → chunk-DUZY4PDA.js} +18 -18
- package/dist/bundle/chunks/{chunk-ZWHFLRTK.js → chunk-E22S6D55.js} +1 -1
- package/dist/bundle/chunks/{chunk-6PGWVVGK.js → chunk-E7WGCXWK.js} +1 -1
- package/dist/bundle/chunks/{chunk-MNB52OYZ.js → chunk-ERT2BD7V.js} +36 -36
- package/dist/bundle/chunks/{chunk-B44UVTZL.js → chunk-H5CZL7M5.js} +1 -1
- package/dist/bundle/chunks/{chunk-IPWO7ULX.js → chunk-HA5AW36V.js} +397 -356
- package/dist/bundle/chunks/{chunk-7VMWPYCR.js → chunk-IAI7MTU4.js} +3 -3
- package/dist/bundle/chunks/{chunk-6FTYU7I7.js → chunk-ICLZNV4V.js} +2 -2
- package/dist/bundle/chunks/{chunk-DMAZLWJY.js → chunk-JQZ7BIAI.js} +2 -13
- package/dist/bundle/chunks/{chunk-E6ZGTXQP.js → chunk-N23FFKJU.js} +40 -25
- package/dist/bundle/chunks/{chunk-RCJ2GKUP.js → chunk-OXJXKCLB.js} +1 -1
- package/dist/bundle/chunks/{chunk-7CYWKPFS.js → chunk-UBQSONVR.js} +9 -8
- package/dist/bundle/chunks/{chunk-LTQ2YA2G.js → chunk-VZV7GLZT.js} +2 -2
- package/dist/bundle/chunks/{chunk-FF26YENK.js → chunk-W2QRCQBX.js} +4 -3
- package/dist/bundle/chunks/{cli-JPMYGPAI.js → cli-5YRA5PFB.js} +2 -2
- package/dist/bundle/chunks/{compat-VHCHB5WQ.js → compat-TCOK5LFD.js} +1 -1
- package/dist/bundle/chunks/dist-33CBPO23.js +1928 -0
- package/dist/bundle/chunks/{easter-egg-3d-XXETTDXE.js → easter-egg-3d-ARXMB2RN.js} +1 -1
- package/dist/bundle/chunks/{esm-763X2IA7.js → esm-NCUV2DF7.js} +1 -1
- package/dist/bundle/chunks/{event-streams-4Q22QRY2.js → event-streams-CGCQQL5M.js} +1 -1
- package/dist/bundle/chunks/{execute-G3YZLU7S.js → execute-KEUC5IWA.js} +1 -1
- package/dist/bundle/chunks/{extract-SJF4DY5M.js → extract-WUAT5T4V.js} +4 -4
- package/dist/bundle/chunks/{google-generative-ai-ICWD4YIE.js → google-generative-ai-MDUVUFFQ.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-XIACVW6H.js → google-vertex-UQGG6LEW.js} +1 -1
- package/dist/bundle/chunks/{host-M54XD7HV.js → host-SPBJ4YAA.js} +1 -1
- package/dist/bundle/chunks/{node-W47VUEGE.js → node-XGNY74WT.js} +1 -1
- package/dist/bundle/chunks/{runtime-POHTHFKR.js → runtime-HBWXEJT3.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-ORGE66B4.js → virtual-modules-I5YD7JGC.js} +2 -2
- package/dist/bundle/cli-runtime.js +2 -2
- package/dist/bundle/flow/agents/.gitkeep +0 -0
- package/dist/bundle/flow/agents/orch-architect.md +33 -0
- package/dist/bundle/flow/agents/orch-implementation-fast.md +13 -0
- package/dist/bundle/flow/agents/orch-implementation-strong.md +28 -0
- package/dist/bundle/flow/agents/orch-qa-agent.md +29 -0
- package/dist/bundle/flow/agents/orch-scout.md +35 -0
- package/dist/bundle/flow/agents/orch-security-review.md +28 -0
- package/dist/bundle/flow/agents/orch-technical-lead.md +37 -0
- package/dist/bundle/flow/agents/orch-technical-review.md +28 -0
- package/dist/bundle/flow/agents/orch-worker.md +13 -0
- package/dist/bundle/flow/agents/orchestrator-lead.md +55 -0
- package/dist/bundle/flow/assets/.gitkeep +0 -0
- package/dist/bundle/flow/assets/contract.json +84 -0
- package/dist/bundle/flow/assets/forge-protocol-schema.sha256 +1 -0
- package/dist/bundle/flow/assets/method.json +193 -0
- package/dist/bundle/flow/assets/orchestrator-profiles.json +131 -0
- package/dist/bundle/flow/assets/profiles/anthropic.json +189 -0
- package/dist/bundle/flow/assets/profiles/openai.json +177 -0
- package/dist/bundle/flow/assets/profiles/oss-first.json +236 -0
- package/dist/bundle/flow/assets/profiles/premium.json +203 -0
- package/dist/bundle/flow/package.json +48 -0
- package/dist/bundle/index.js +5 -5
- package/dist/bundle/rpc-entry.js +2 -2
- package/dist/config.d.ts +17 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -2
- package/dist/config.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +20 -0
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +124 -12
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/humain/flow-loader.d.ts +62 -0
- package/dist/humain/flow-loader.d.ts.map +1 -0
- package/dist/humain/flow-loader.js +291 -0
- package/dist/humain/flow-loader.js.map +1 -0
- package/dist/humain/index.d.ts.map +1 -1
- package/dist/humain/index.js +2 -0
- package/dist/humain/index.js.map +1 -1
- package/dist/humain/trace-capture/child-launch.d.ts +1 -0
- package/dist/humain/trace-capture/child-launch.d.ts.map +1 -1
- package/dist/humain/trace-capture/child-launch.js +2 -1
- package/dist/humain/trace-capture/child-launch.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +136 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +3 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/docs.json +8 -0
- package/docs/environment-variables.md +1 -0
- package/docs/flow-profiles.md +173 -0
- package/docs/flow.md +112 -0
- package/docs/humain-code.md +1 -1
- package/docs/index.md +2 -0
- package/npm-shrinkwrap.json +6 -6
- package/package.json +6 -5
|
@@ -122,20 +122,44 @@ function findNearestProjectAgentsDir(cwd) {
|
|
|
122
122
|
currentDir = parentDir;
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Absolute directory of trusted packaged agents, set by a parent process that ships personas (Flow). Loaded at
|
|
127
|
+
* user trust: below the user agents dir, above project agents, which can never shadow a name defined here.
|
|
128
|
+
*/
|
|
129
|
+
export const TRUSTED_AGENTS_DIR_ENV = "HUMAIN_TERMINAL_TRUSTED_AGENTS_DIR";
|
|
130
|
+
function loadTrustedAgents() {
|
|
131
|
+
const dir = process.env[TRUSTED_AGENTS_DIR_ENV];
|
|
132
|
+
if (!dir)
|
|
133
|
+
return { agents: [], diagnostics: [] };
|
|
134
|
+
if (!path.isAbsolute(dir)) {
|
|
135
|
+
return {
|
|
136
|
+
agents: [],
|
|
137
|
+
diagnostics: [{ type: "warning", message: `${TRUSTED_AGENTS_DIR_ENV} must be an absolute path`, path: dir }],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return loadAgentsFromDir(dir, "user");
|
|
141
|
+
}
|
|
125
142
|
export function discoverAgents(cwd, scope) {
|
|
126
143
|
const userDir = path.join(getAgentDir(), "agents");
|
|
127
144
|
const projectAgentsDir = findNearestProjectAgentsDir(cwd);
|
|
128
145
|
const emptyResult = { agents: [], diagnostics: [] };
|
|
146
|
+
const trustedResult = scope === "project" ? emptyResult : loadTrustedAgents();
|
|
129
147
|
const userResult = scope === "project" ? emptyResult : loadAgentsFromDir(userDir, "user");
|
|
130
148
|
const projectResult = scope === "user" || !projectAgentsDir ? emptyResult : loadAgentsFromDir(projectAgentsDir, "project");
|
|
131
149
|
const agentMap = new Map();
|
|
132
150
|
if (scope === "both") {
|
|
133
|
-
for (const agent of
|
|
151
|
+
for (const agent of trustedResult.agents)
|
|
134
152
|
agentMap.set(agent.name, agent);
|
|
135
|
-
for (const agent of
|
|
153
|
+
for (const agent of userResult.agents)
|
|
136
154
|
agentMap.set(agent.name, agent);
|
|
155
|
+
for (const agent of projectResult.agents) {
|
|
156
|
+
if (!trustedResult.agents.some((trusted) => trusted.name === agent.name))
|
|
157
|
+
agentMap.set(agent.name, agent);
|
|
158
|
+
}
|
|
137
159
|
}
|
|
138
160
|
else if (scope === "user") {
|
|
161
|
+
for (const agent of trustedResult.agents)
|
|
162
|
+
agentMap.set(agent.name, agent);
|
|
139
163
|
for (const agent of userResult.agents)
|
|
140
164
|
agentMap.set(agent.name, agent);
|
|
141
165
|
}
|
|
@@ -146,7 +170,7 @@ export function discoverAgents(cwd, scope) {
|
|
|
146
170
|
return {
|
|
147
171
|
agents: Array.from(agentMap.values()),
|
|
148
172
|
projectAgentsDir,
|
|
149
|
-
diagnostics: [...userResult.diagnostics, ...projectResult.diagnostics],
|
|
173
|
+
diagnostics: [...trustedResult.diagnostics, ...userResult.diagnostics, ...projectResult.diagnostics],
|
|
150
174
|
};
|
|
151
175
|
}
|
|
152
176
|
// --- Formatting helpers ---
|
|
@@ -266,6 +290,7 @@ export function toProgressDetails(results) {
|
|
|
266
290
|
attempt: result.attempt,
|
|
267
291
|
exitCode: result.exitCode,
|
|
268
292
|
usage: result.usage,
|
|
293
|
+
...(result.usageIncomplete ? { usageIncomplete: true } : {}),
|
|
269
294
|
model: result.model,
|
|
270
295
|
stopReason: result.stopReason,
|
|
271
296
|
errorMessage: result.errorMessage,
|
|
@@ -343,6 +368,59 @@ function getResultDisplayItems(result) {
|
|
|
343
368
|
function getResultFinalOutput(result) {
|
|
344
369
|
return isProgressResult(result) ? result.latestText : getFinalOutput(result.messages);
|
|
345
370
|
}
|
|
371
|
+
/** Env var Flow sets to the verified approved-inputs file; inherited by every nested child. */
|
|
372
|
+
export const FLOW_APPROVED_INPUTS_ENV = "HUMAIN_FLOW_APPROVED_INPUTS_FILE";
|
|
373
|
+
/** Mirrors MAX_APPROVED_INPUT_BYTES in packages/flow/src/run-admission.ts; coding-agent must not import @humain/flow. */
|
|
374
|
+
export const MAX_FLOW_APPROVED_INPUT_BYTES = 256 * 1024;
|
|
375
|
+
/** Mirrors APPROVED_INPUTS_HEADER in packages/flow/src/run-admission.ts (first line of approvedInputsBlock). */
|
|
376
|
+
export const FLOW_APPROVED_INPUTS_HEADER = "## Approved inputs (mandatory)";
|
|
377
|
+
/**
|
|
378
|
+
* Snapshot the Flow approved-inputs file named by the env var. Unset -> undefined. Set but anything fails
|
|
379
|
+
* (absolute path, basename, no symlink, regular file, owner, mode 0600, size, header) -> throws, so a child
|
|
380
|
+
* never launches without its mandatory inputs. Bytes are read once from the opened fd.
|
|
381
|
+
*/
|
|
382
|
+
export function readFlowApprovedInputs(env) {
|
|
383
|
+
const file = env[FLOW_APPROVED_INPUTS_ENV];
|
|
384
|
+
if (file === undefined)
|
|
385
|
+
return undefined;
|
|
386
|
+
const refuse = (why) => {
|
|
387
|
+
throw new Error(`Refusing to launch subagent: Flow approved inputs (${FLOW_APPROVED_INPUTS_ENV}) ${why}.`);
|
|
388
|
+
};
|
|
389
|
+
if (!path.isAbsolute(file) || path.basename(file) !== "approved-inputs.md")
|
|
390
|
+
return refuse("must be an absolute path to approved-inputs.md");
|
|
391
|
+
let fd;
|
|
392
|
+
try {
|
|
393
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
394
|
+
const stat = fs.fstatSync(fd);
|
|
395
|
+
if (!stat.isFile())
|
|
396
|
+
return refuse("is not a regular file");
|
|
397
|
+
if (process.getuid && (stat.uid !== process.getuid() || (stat.mode & 0o777) !== 0o600))
|
|
398
|
+
return refuse("must be owned by the current user with mode 0600");
|
|
399
|
+
if (stat.size > MAX_FLOW_APPROVED_INPUT_BYTES)
|
|
400
|
+
return refuse("is too large");
|
|
401
|
+
const buffer = Buffer.alloc(MAX_FLOW_APPROVED_INPUT_BYTES + 1);
|
|
402
|
+
let size = 0;
|
|
403
|
+
while (size <= MAX_FLOW_APPROVED_INPUT_BYTES) {
|
|
404
|
+
const n = fs.readSync(fd, buffer, size, MAX_FLOW_APPROVED_INPUT_BYTES + 1 - size, null);
|
|
405
|
+
if (n === 0)
|
|
406
|
+
break;
|
|
407
|
+
size += n;
|
|
408
|
+
}
|
|
409
|
+
if (size > MAX_FLOW_APPROVED_INPUT_BYTES)
|
|
410
|
+
return refuse("is too large");
|
|
411
|
+
const text = buffer.subarray(0, size).toString("utf-8");
|
|
412
|
+
return text.startsWith(FLOW_APPROVED_INPUTS_HEADER) ? text : refuse("lacks the approved-inputs header");
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
if (error instanceof Error && error.message.startsWith("Refusing to launch subagent"))
|
|
416
|
+
throw error;
|
|
417
|
+
return refuse(`cannot be read (${error.code ?? "error"})`);
|
|
418
|
+
}
|
|
419
|
+
finally {
|
|
420
|
+
if (fd !== undefined)
|
|
421
|
+
fs.closeSync(fd);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
346
424
|
async function writePromptToTempFile(agentName, prompt) {
|
|
347
425
|
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "humain-subagent-"));
|
|
348
426
|
const safeName = agentName.replace(/[^\w.-]+/g, "_");
|
|
@@ -437,6 +515,8 @@ async function runSingleAgent(defaultCwd, agents, agentDiagnostics, agentName, t
|
|
|
437
515
|
args.push("--thinking", options.thinking);
|
|
438
516
|
let tmpPromptDir = null;
|
|
439
517
|
let tmpPromptPath = null;
|
|
518
|
+
let tmpInputsDir = null;
|
|
519
|
+
let tmpInputsPath = null;
|
|
440
520
|
const currentResult = {
|
|
441
521
|
agent: agentName,
|
|
442
522
|
agentSource: agent.source,
|
|
@@ -481,6 +561,14 @@ async function runSingleAgent(defaultCwd, agents, agentDiagnostics, agentName, t
|
|
|
481
561
|
currentResult.isolationDegradedReason = isolationDegradedReason;
|
|
482
562
|
if (signal?.aborted)
|
|
483
563
|
throw new Error("Subagent was aborted");
|
|
564
|
+
// Snapshot into a subagent-owned temp file; the env path itself is never passed on the command line.
|
|
565
|
+
const approvedInputs = readFlowApprovedInputs(process.env);
|
|
566
|
+
if (approvedInputs !== undefined) {
|
|
567
|
+
const tmp = await writePromptToTempFile("flow-approved-inputs", approvedInputs);
|
|
568
|
+
tmpInputsDir = tmp.dir;
|
|
569
|
+
tmpInputsPath = tmp.filePath;
|
|
570
|
+
args.push("--append-system-prompt", tmpInputsPath);
|
|
571
|
+
}
|
|
484
572
|
if (agent.systemPrompt.trim()) {
|
|
485
573
|
const tmp = await writePromptToTempFile(agent.name, agent.systemPrompt);
|
|
486
574
|
tmpPromptDir = tmp.dir;
|
|
@@ -548,6 +636,16 @@ async function runSingleAgent(defaultCwd, agents, agentDiagnostics, agentName, t
|
|
|
548
636
|
/* observation never affects execution */
|
|
549
637
|
}
|
|
550
638
|
};
|
|
639
|
+
const addUsage = (usage) => {
|
|
640
|
+
for (const key of ["input", "output", "cacheRead", "cacheWrite"]) {
|
|
641
|
+
const count = usage?.[key];
|
|
642
|
+
if (typeof count === "number" && Number.isSafeInteger(count) && count >= 0)
|
|
643
|
+
currentResult.usage[key] += count;
|
|
644
|
+
else
|
|
645
|
+
currentResult.usageIncomplete = true;
|
|
646
|
+
}
|
|
647
|
+
currentResult.usage.cost += usage?.cost?.total || 0;
|
|
648
|
+
};
|
|
551
649
|
const processLine = (line) => {
|
|
552
650
|
if (!line.trim())
|
|
553
651
|
return;
|
|
@@ -558,20 +656,20 @@ async function runSingleAgent(defaultCwd, agents, agentDiagnostics, agentName, t
|
|
|
558
656
|
catch {
|
|
559
657
|
return;
|
|
560
658
|
}
|
|
659
|
+
if (!event || typeof event !== "object")
|
|
660
|
+
return;
|
|
661
|
+
if (event.type === "compaction_end" && event.result?.usage) {
|
|
662
|
+
addUsage(event.result.usage);
|
|
663
|
+
emitUpdate();
|
|
664
|
+
}
|
|
561
665
|
if (event.type === "message_end" && event.message) {
|
|
562
666
|
const msg = event.message;
|
|
563
667
|
currentResult.messages.push(msg);
|
|
564
668
|
if (msg.role === "assistant") {
|
|
565
669
|
currentResult.usage.turns++;
|
|
566
|
-
|
|
567
|
-
if (usage)
|
|
568
|
-
currentResult.usage.
|
|
569
|
-
currentResult.usage.output += usage.output || 0;
|
|
570
|
-
currentResult.usage.cacheRead += usage.cacheRead || 0;
|
|
571
|
-
currentResult.usage.cacheWrite += usage.cacheWrite || 0;
|
|
572
|
-
currentResult.usage.cost += usage.cost?.total || 0;
|
|
573
|
-
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
574
|
-
}
|
|
670
|
+
addUsage(msg.usage);
|
|
671
|
+
if (msg.usage)
|
|
672
|
+
currentResult.usage.contextTokens = msg.usage.totalTokens || 0;
|
|
575
673
|
if (msg.model)
|
|
576
674
|
currentResult.model = msg.responseModel ?? msg.model;
|
|
577
675
|
if (msg.stopReason)
|
|
@@ -785,6 +883,20 @@ async function runSingleAgent(defaultCwd, agents, agentDiagnostics, agentName, t
|
|
|
785
883
|
catch {
|
|
786
884
|
/* ignore */
|
|
787
885
|
}
|
|
886
|
+
if (tmpInputsPath)
|
|
887
|
+
try {
|
|
888
|
+
fs.unlinkSync(tmpInputsPath);
|
|
889
|
+
}
|
|
890
|
+
catch {
|
|
891
|
+
/* ignore */
|
|
892
|
+
}
|
|
893
|
+
if (tmpInputsDir)
|
|
894
|
+
try {
|
|
895
|
+
fs.rmdirSync(tmpInputsDir);
|
|
896
|
+
}
|
|
897
|
+
catch {
|
|
898
|
+
/* ignore */
|
|
899
|
+
}
|
|
788
900
|
}
|
|
789
901
|
options.traceChild?.finishAttempt(currentResult, isFailedResult(currentResult));
|
|
790
902
|
return currentResult;
|