@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.
Files changed (91) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/bundle/agent-monitor.js +1 -1
  3. package/dist/bundle/chunks/bedrock-converse-stream.js +18 -17
  4. package/dist/bundle/chunks/{chunk-I2VOYPDI.js → chunk-2KKLZK2N.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-UDTXD6EF.js → chunk-2Q2O3GZ4.js} +721 -717
  6. package/dist/bundle/chunks/{chunk-IYCS4ZUX.js → chunk-3ZI2TEVB.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-ANSL5PYD.js → chunk-DUZY4PDA.js} +18 -18
  8. package/dist/bundle/chunks/{chunk-ZWHFLRTK.js → chunk-E22S6D55.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-6PGWVVGK.js → chunk-E7WGCXWK.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-MNB52OYZ.js → chunk-ERT2BD7V.js} +36 -36
  11. package/dist/bundle/chunks/{chunk-B44UVTZL.js → chunk-H5CZL7M5.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-IPWO7ULX.js → chunk-HA5AW36V.js} +397 -356
  13. package/dist/bundle/chunks/{chunk-7VMWPYCR.js → chunk-IAI7MTU4.js} +3 -3
  14. package/dist/bundle/chunks/{chunk-6FTYU7I7.js → chunk-ICLZNV4V.js} +2 -2
  15. package/dist/bundle/chunks/{chunk-DMAZLWJY.js → chunk-JQZ7BIAI.js} +2 -13
  16. package/dist/bundle/chunks/{chunk-E6ZGTXQP.js → chunk-N23FFKJU.js} +40 -25
  17. package/dist/bundle/chunks/{chunk-RCJ2GKUP.js → chunk-OXJXKCLB.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-7CYWKPFS.js → chunk-UBQSONVR.js} +9 -8
  19. package/dist/bundle/chunks/{chunk-LTQ2YA2G.js → chunk-VZV7GLZT.js} +2 -2
  20. package/dist/bundle/chunks/{chunk-FF26YENK.js → chunk-W2QRCQBX.js} +4 -3
  21. package/dist/bundle/chunks/{cli-JPMYGPAI.js → cli-5YRA5PFB.js} +2 -2
  22. package/dist/bundle/chunks/{compat-VHCHB5WQ.js → compat-TCOK5LFD.js} +1 -1
  23. package/dist/bundle/chunks/dist-33CBPO23.js +1928 -0
  24. package/dist/bundle/chunks/{easter-egg-3d-XXETTDXE.js → easter-egg-3d-ARXMB2RN.js} +1 -1
  25. package/dist/bundle/chunks/{esm-763X2IA7.js → esm-NCUV2DF7.js} +1 -1
  26. package/dist/bundle/chunks/{event-streams-4Q22QRY2.js → event-streams-CGCQQL5M.js} +1 -1
  27. package/dist/bundle/chunks/{execute-G3YZLU7S.js → execute-KEUC5IWA.js} +1 -1
  28. package/dist/bundle/chunks/{extract-SJF4DY5M.js → extract-WUAT5T4V.js} +4 -4
  29. package/dist/bundle/chunks/{google-generative-ai-ICWD4YIE.js → google-generative-ai-MDUVUFFQ.js} +1 -1
  30. package/dist/bundle/chunks/{google-vertex-XIACVW6H.js → google-vertex-UQGG6LEW.js} +1 -1
  31. package/dist/bundle/chunks/{host-M54XD7HV.js → host-SPBJ4YAA.js} +1 -1
  32. package/dist/bundle/chunks/{node-W47VUEGE.js → node-XGNY74WT.js} +1 -1
  33. package/dist/bundle/chunks/{runtime-POHTHFKR.js → runtime-HBWXEJT3.js} +1 -1
  34. package/dist/bundle/chunks/{virtual-modules-ORGE66B4.js → virtual-modules-I5YD7JGC.js} +2 -2
  35. package/dist/bundle/cli-runtime.js +2 -2
  36. package/dist/bundle/flow/agents/.gitkeep +0 -0
  37. package/dist/bundle/flow/agents/orch-architect.md +33 -0
  38. package/dist/bundle/flow/agents/orch-implementation-fast.md +13 -0
  39. package/dist/bundle/flow/agents/orch-implementation-strong.md +28 -0
  40. package/dist/bundle/flow/agents/orch-qa-agent.md +29 -0
  41. package/dist/bundle/flow/agents/orch-scout.md +35 -0
  42. package/dist/bundle/flow/agents/orch-security-review.md +28 -0
  43. package/dist/bundle/flow/agents/orch-technical-lead.md +37 -0
  44. package/dist/bundle/flow/agents/orch-technical-review.md +28 -0
  45. package/dist/bundle/flow/agents/orch-worker.md +13 -0
  46. package/dist/bundle/flow/agents/orchestrator-lead.md +55 -0
  47. package/dist/bundle/flow/assets/.gitkeep +0 -0
  48. package/dist/bundle/flow/assets/contract.json +84 -0
  49. package/dist/bundle/flow/assets/forge-protocol-schema.sha256 +1 -0
  50. package/dist/bundle/flow/assets/method.json +193 -0
  51. package/dist/bundle/flow/assets/orchestrator-profiles.json +131 -0
  52. package/dist/bundle/flow/assets/profiles/anthropic.json +189 -0
  53. package/dist/bundle/flow/assets/profiles/openai.json +177 -0
  54. package/dist/bundle/flow/assets/profiles/oss-first.json +236 -0
  55. package/dist/bundle/flow/assets/profiles/premium.json +203 -0
  56. package/dist/bundle/flow/package.json +48 -0
  57. package/dist/bundle/index.js +5 -5
  58. package/dist/bundle/rpc-entry.js +2 -2
  59. package/dist/config.d.ts +17 -5
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +25 -2
  62. package/dist/config.js.map +1 -1
  63. package/dist/core/tools/subagent.d.ts +20 -0
  64. package/dist/core/tools/subagent.d.ts.map +1 -1
  65. package/dist/core/tools/subagent.js +124 -12
  66. package/dist/core/tools/subagent.js.map +1 -1
  67. package/dist/humain/flow-loader.d.ts +62 -0
  68. package/dist/humain/flow-loader.d.ts.map +1 -0
  69. package/dist/humain/flow-loader.js +291 -0
  70. package/dist/humain/flow-loader.js.map +1 -0
  71. package/dist/humain/index.d.ts.map +1 -1
  72. package/dist/humain/index.js +2 -0
  73. package/dist/humain/index.js.map +1 -1
  74. package/dist/humain/trace-capture/child-launch.d.ts +1 -0
  75. package/dist/humain/trace-capture/child-launch.d.ts.map +1 -1
  76. package/dist/humain/trace-capture/child-launch.js +2 -1
  77. package/dist/humain/trace-capture/child-launch.js.map +1 -1
  78. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  79. package/dist/modes/rpc/rpc-mode.js +136 -1
  80. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  81. package/dist/utils/version-check.d.ts.map +1 -1
  82. package/dist/utils/version-check.js +3 -1
  83. package/dist/utils/version-check.js.map +1 -1
  84. package/docs/docs.json +8 -0
  85. package/docs/environment-variables.md +1 -0
  86. package/docs/flow-profiles.md +173 -0
  87. package/docs/flow.md +112 -0
  88. package/docs/humain-code.md +1 -1
  89. package/docs/index.md +2 -0
  90. package/npm-shrinkwrap.json +6 -6
  91. 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 userResult.agents)
151
+ for (const agent of trustedResult.agents)
134
152
  agentMap.set(agent.name, agent);
135
- for (const agent of projectResult.agents)
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
- const usage = msg.usage;
567
- if (usage) {
568
- currentResult.usage.input += usage.input || 0;
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;