@humain/humain-code 0.4.1 → 0.4.3

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 (30) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/bundle/agent-monitor.js +1 -1
  3. package/dist/bundle/chunks/bedrock-converse-stream.js +23 -28
  4. package/dist/bundle/chunks/{chunk-7LDXR4AN.js → chunk-FHLF33QE.js} +180 -177
  5. package/dist/bundle/chunks/{chunk-OYDXJK4L.js → chunk-H6YXYISM.js} +316 -315
  6. package/dist/bundle/chunks/{chunk-KMMH6Z2Y.js → chunk-IUZLRXBD.js} +2 -2
  7. package/dist/bundle/chunks/{chunk-UBQSONVR.js → chunk-Q5TQLAOM.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-N23FFKJU.js → chunk-T45HXZW7.js} +4 -4
  9. package/dist/bundle/chunks/{compat-GXD2ZGMZ.js → compat-HHX6L5CV.js} +1 -1
  10. package/dist/bundle/chunks/{dist-IKSCGVMG.js → dist-PHLMPB47.js} +566 -506
  11. package/dist/bundle/chunks/{event-streams-CGCQQL5M.js → event-streams-PKTMLNE5.js} +1 -1
  12. package/dist/bundle/chunks/{virtual-modules-BYOLP3HY.js → virtual-modules-3UEIUCY5.js} +2 -2
  13. package/dist/bundle/cli-runtime.js +2 -2
  14. package/dist/bundle/flow/assets/orchestrator-profiles.json +32 -32
  15. package/dist/bundle/flow/assets/profiles/oss-first.json +37 -72
  16. package/dist/bundle/flow/assets/profiles/premium.json +5 -5
  17. package/dist/bundle/flow/package.json +1 -1
  18. package/dist/bundle/index.js +4 -4
  19. package/dist/bundle/rpc-entry.js +2 -2
  20. package/dist/core/tools/subagent.d.ts +2 -0
  21. package/dist/core/tools/subagent.d.ts.map +1 -1
  22. package/dist/core/tools/subagent.js +72 -3
  23. package/dist/core/tools/subagent.js.map +1 -1
  24. package/dist/humain/flow-loader.d.ts.map +1 -1
  25. package/dist/humain/flow-loader.js +89 -4
  26. package/dist/humain/flow-loader.js.map +1 -1
  27. package/docs/flow-profiles.md +4 -2
  28. package/docs/flow.md +11 -3
  29. package/npm-shrinkwrap.json +6 -6
  30. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"flow-loader.d.ts","sourceRoot":"","sources":["../../src/humain/flow-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAEhH,OAAO,KAAK,EAEX,uBAAuB,EACvB,gBAAgB,EAEhB,MAAM,6BAA6B,CAAC;AAErC;;;;;GAKG;AAEH,eAAO,MAAM,wBAAwB,kCAAkC,CAAC;AACxE,eAAO,MAAM,2BAA2B,qCAAqC,CAAC;AAE9E,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC9B,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAC;QAAC,MAAM,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACnF,WAAW,IAAI,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IAC1B,aAAa,EAAE,gBAAgB,CAAC;IAChC,kHAAkH;IAClH,oBAAoB,CAAC,CAAC,OAAO,EAAE;QAC9B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/D,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;QAC3B,wFAAwF;QACxF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC/C,GAAG,cAAc,CAAC;IACnB,sHAAsH;IACtH,qBAAqB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/G,kBAAkB,CAAC,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;CAChF;AAED,MAAM,WAAW,uBAAuB;IACvC,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,uGAAuG;IACvG,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sHAAsH;IACtH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,qGAAqG;AACrG,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAI9E;AAED,sHAAsH;AACtH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAE9G;AAED,mHAAmH;AACnH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAE7F;AAgKD,wBAAgB,sCAAsC,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB,CAmH9G"}
1
+ {"version":3,"file":"flow-loader.d.ts","sourceRoot":"","sources":["../../src/humain/flow-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAEhH,OAAO,KAAK,EAEX,uBAAuB,EACvB,gBAAgB,EAEhB,MAAM,6BAA6B,CAAC;AAIrC;;;;;GAKG;AAEH,eAAO,MAAM,wBAAwB,kCAAkC,CAAC;AACxE,eAAO,MAAM,2BAA2B,qCAAqC,CAAC;AAE9E,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC9B,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAC;QAAC,MAAM,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACnF,WAAW,IAAI,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IAC1B,aAAa,EAAE,gBAAgB,CAAC;IAChC,kHAAkH;IAClH,oBAAoB,CAAC,CAAC,OAAO,EAAE;QAC9B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/D,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;QAC3B,wFAAwF;QACxF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC/C,GAAG,cAAc,CAAC;IACnB,sHAAsH;IACtH,qBAAqB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/G,kBAAkB,CAAC,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;CAChF;AAED,MAAM,WAAW,uBAAuB;IACvC,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,uGAAuG;IACvG,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sHAAsH;IACtH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,qGAAqG;AACrG,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAI9E;AAED,sHAAsH;AACtH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAE9G;AAED,mHAAmH;AACnH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAE7F;AAsOD,wBAAgB,sCAAsC,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB,CAgI9G"}
@@ -1,5 +1,8 @@
1
+ import { createHash, randomBytes } from "node:crypto";
1
2
  import { join } from "node:path";
2
3
  import { getAgentDir, getBundledFlowPackageDir, getBundledFlowProfilesDir } from "../config.js";
4
+ import { createTraceRedactor } from "../core/trace/redaction.js";
5
+ import { sanitizeForTerminal } from "../core/trace-client/sanitize.js";
3
6
  /**
4
7
  * Lazy loader for the private @humain/flow package.
5
8
  *
@@ -95,6 +98,72 @@ const AVAILABLE_WHEN_DISABLED = new Set(["flow-cancel", "flow-status", "flow-pau
95
98
  function isRecord(value) {
96
99
  return typeof value === "object" && value !== null;
97
100
  }
101
+ /**
102
+ * Print/json mode has no UI: core makes ctx.ui.notify a no-op, so headless notices go to stderr as bare text.
103
+ * Notice text can echo user args or env values, so it is redacted, stripped of terminal escapes, then redacted again
104
+ * with the same redactor: the first pass catches secrets that stripping could otherwise mangle, the second catches
105
+ * secrets that were split by escapes and are rejoined by stripping. Fails closed to a fixed line on any failure.
106
+ * Replacing `ui` snapshots ui (descriptors preserve other ctx getters, so lazy getters on ctx stay live; same approach
107
+ * as the runner's createCommandContext).
108
+ */
109
+ const NOTICE_WITHHELD = "[notice withheld: redaction unavailable]";
110
+ function safeNotice(message) {
111
+ try {
112
+ const redactor = createTraceRedactor({ env: process.env });
113
+ return redactor.redactText(sanitizeForTerminal(redactor.redactText(message).text)).text;
114
+ }
115
+ catch {
116
+ return NOTICE_WITHHELD;
117
+ }
118
+ }
119
+ function withHeadlessNotices(ctx) {
120
+ if (ctx.mode !== "print" && ctx.mode !== "json")
121
+ return ctx;
122
+ const ui = Object.defineProperties({}, Object.getOwnPropertyDescriptors(ctx.ui));
123
+ ui.notify = (message) => {
124
+ process.stderr.write(`${safeNotice(message)}\n`);
125
+ };
126
+ const wrapped = Object.defineProperties({}, Object.getOwnPropertyDescriptors(ctx));
127
+ Object.defineProperty(wrapped, "ui", { value: ui, enumerable: true, configurable: true });
128
+ return wrapped;
129
+ }
130
+ /**
131
+ * Mirrors packages/flow emitRefusalSettled: an rpc /flow start that dispatches nothing still emits one terminal
132
+ * `run_settled` frame so the host stops waiting. Built here because the entry graph must not reach @humain/flow.
133
+ * Print/json refusals exit non-zero (mirrors flow's markHeadlessFailure). Never throws; no-op otherwise.
134
+ */
135
+ async function settleRefusal(name, ctx, failureClass) {
136
+ if ((name === "flow" || name === "flow-continue") && (ctx.mode === "print" || ctx.mode === "json"))
137
+ process.exitCode = 1;
138
+ if (ctx.mode !== "rpc" || !rpcOutput || (name !== "flow" && name !== "flow-continue"))
139
+ return;
140
+ try {
141
+ const runId = `flow-refused-${randomBytes(8).toString("hex")}`;
142
+ const frame = {
143
+ type: "flow_event",
144
+ event: {
145
+ schema: "humain-flow-event",
146
+ schemaVersion: 1,
147
+ eventId: `${runId}.refused`,
148
+ runId,
149
+ seq: 1,
150
+ ts: new Date().toISOString(),
151
+ profile: {
152
+ id: "unknown",
153
+ version: 1,
154
+ digest: createHash("sha256").update("unknown", "utf8").digest("hex"),
155
+ },
156
+ causes: [],
157
+ kind: "run_settled",
158
+ payload: { outcome: "failed", failureClass, settledCleanly: true, retryOffer: ["direct"], totals: {} },
159
+ },
160
+ };
161
+ await rpcOutput(frame);
162
+ }
163
+ catch {
164
+ // Refusal must still notify; a dead transport is not the command's problem.
165
+ }
166
+ }
98
167
  function disabledReason(pi, env) {
99
168
  const raw = env[HUMAIN_FLOW_DISABLED_ENV]?.trim().toLowerCase();
100
169
  if (raw === "1" || raw === "true")
@@ -241,10 +310,16 @@ export function createHumainFlowLoaderExtensionFactory(options = {}) {
241
310
  for (const [name, description] of Object.entries(STUB_COMMANDS)) {
242
311
  pi.registerCommand(name, {
243
312
  description,
244
- handler: async (args, ctx) => {
313
+ handler: async (args, rawCtx) => {
314
+ const ctx = withHeadlessNotices(rawCtx);
245
315
  const reason = AVAILABLE_WHEN_DISABLED.has(name) ? undefined : disabledReason(pi, env);
246
316
  if (reason) {
247
- ctx.ui.notify(`Flow disabled (${reason}). /${name} is unavailable.`, "warning");
317
+ try {
318
+ ctx.ui.notify(`Flow disabled (${reason}). /${name} is unavailable.`, "warning");
319
+ }
320
+ finally {
321
+ await settleRefusal(name, ctx, "config_invalid");
322
+ }
248
323
  return;
249
324
  }
250
325
  if (name === "flow-profile") {
@@ -274,12 +349,22 @@ export function createHumainFlowLoaderExtensionFactory(options = {}) {
274
349
  commands = await load();
275
350
  }
276
351
  catch (error) {
277
- ctx.ui.notify(`Failed to load Flow: ${error instanceof Error ? error.message : String(error)}`, "error");
352
+ try {
353
+ ctx.ui.notify(`Failed to load Flow: ${error instanceof Error ? error.message : String(error)}`, "error");
354
+ }
355
+ finally {
356
+ await settleRefusal(name, ctx, "internal");
357
+ }
278
358
  return;
279
359
  }
280
360
  const real = commands.get(name);
281
361
  if (!real) {
282
- ctx.ui.notify(`/${name} is not available yet`, "error");
362
+ try {
363
+ ctx.ui.notify(`/${name} is not available yet`, "error");
364
+ }
365
+ finally {
366
+ await settleRefusal(name, ctx, "internal");
367
+ }
283
368
  return;
284
369
  }
285
370
  await real.handler(args, ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"flow-loader.js","sourceRoot":"","sources":["../../src/humain/flow-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport type { FlowEventFrameV1, FlowReceiptV1, FlowStatusV1 } from \"@humain/humain-code-contracts/flow-control\";\nimport { getAgentDir, getBundledFlowPackageDir, getBundledFlowProfilesDir } from \"../config.ts\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionFactory,\n\tRegisteredCommand,\n} from \"../core/extensions/types.ts\";\n\n/**\n * Lazy loader for the private @humain/flow package.\n *\n * The CLI entry graph must not statically reach Flow: this module registers cheap stub commands and a flag, and\n * only loads Flow (one memoized dynamic import) the first time a stub runs. Flow is bundled as its own chunk.\n */\n\nexport const HUMAIN_FLOW_DISABLED_ENV = \"HUMAIN_TERMINAL_FLOW_DISABLED\";\nexport const HUMAIN_FLOW_PACKAGE_DIR_ENV = \"HUMAIN_TERMINAL_FLOW_PACKAGE_DIR\";\n\n/** Run-control surface Flow exposes to rpc mode (flow_status/pause/resume/cancel/message). */\nexport interface FlowRpcControl {\n\thandle(input: unknown): Promise<{ receipt: FlowReceiptV1; status?: FlowStatusV1 }>;\n\tactiveRunId(): string | undefined;\n}\n\nexport interface FlowModule {\n\tflowExtension: ExtensionFactory;\n\t/** Documented rpc hook: called once when Flow loads; `writeFrame` carries `flow_event` frames to the rpc host. */\n\tcreateFlowRpcControl?(options: {\n\t\twriteFrame?: (frame: FlowEventFrameV1) => void | Promise<void>;\n\t\tisDisabled?: () => boolean;\n\t\t/** Queues text on the host session as a follow-up (flow_message with no active run). */\n\t\tsendFollowUp?: (text: string) => Promise<void>;\n\t}): FlowRpcControl;\n\t/** B1 profile command; optional so test fakes need not provide it. Methods stay bivariant for the real signatures. */\n\trunFlowProfileCommand?(args: string, ctx: ExtensionCommandContext, deps: { store: unknown }): Promise<unknown>;\n\tcreateProfileStore?(options: { flowDir: string; builtInDir?: string }): unknown;\n}\n\nexport interface HumainFlowLoaderOptions {\n\t/** Test seam. Defaults to the single sanctioned dynamic import of @humain/flow. */\n\timportFlow?: () => Promise<FlowModule>;\n\tenv?: NodeJS.ProcessEnv;\n\t/** Directory holding Flow's agents/assets/package.json. Defaults to the bundled layout's flow/ dir. */\n\tflowPackageDir?: string;\n\t/** Built-in Flow profiles dir. Defaults to the bundled layout's assets/profiles; undefined lets Flow find its own. */\n\tbuiltInProfilesDir?: string;\n\t/** Flow profile store directory. Defaults to `<agentDir>/flow`. */\n\tflowDir?: string;\n}\n\nlet rpcControl: FlowRpcControl | undefined;\nlet disabledProbe: (() => boolean) | undefined;\nlet rpcOutput: ((frame: FlowEventFrameV1) => void | Promise<void>) | undefined;\nlet rpcLoader: (() => Promise<FlowRpcControl | undefined>) | undefined;\nlet rpcFollowUp: ((text: string) => Promise<void>) | undefined;\n\n/** The documented rpc hook: rpc mode reads Flow's run control here and never imports Flow itself. */\nexport function getFlowRpcControl(): FlowRpcControl | undefined {\n\treturn rpcControl;\n}\n\n/**\n * The documented rpc hook: loads Flow through the same lazy-load path the stub commands use and returns its run control.\n * Undefined when Flow is disabled (never loaded then), not wired, or fails to load.\n */\nexport async function loadFlowRpcControl(): Promise<FlowRpcControl | undefined> {\n\tif (rpcControl) return rpcControl;\n\tif (isFlowDisabled()) return undefined;\n\treturn rpcLoader?.();\n}\n\n/** True while Flow is switched off (flow.enabled=false or the env kill switch); rpc mode refuses flow_resume then. */\nexport function isFlowDisabled(): boolean {\n\treturn disabledProbe?.() ?? false;\n}\n\n/** The documented rpc hook: rpc mode registers where Flow's `flow_event` frames are written. */\nexport function setFlowRpcOutput(writer: ((frame: FlowEventFrameV1) => void | Promise<void>) | undefined): void {\n\trpcOutput = writer;\n}\n\n/** The documented rpc hook: rpc mode registers how direct-mode flow_message text is queued as a host follow-up. */\nexport function setFlowRpcFollowUp(queue: ((text: string) => Promise<void>) | undefined): void {\n\trpcFollowUp = queue;\n}\n\ntype CommandOptions = Omit<RegisteredCommand, \"name\" | \"sourceInfo\">;\ntype EventHandler = (...args: unknown[]) => unknown;\n\n/**\n * ExtensionAPI members that only record registrations (see core/extensions/loader.ts). They are staged while Flow's\n * factory runs and applied to the real pi only after the factory and the session_start replay succeed. Reads and\n * actions (getFlag, getSettings, exec, sendMessage, ...) and `events` stay live: they have no registration to undo\n * or are needed by the factory itself. Flow uses none of the live ones that subscribe (`events.on`).\n */\nconst STAGED_MEMBERS = new Set([\n\t\"on\",\n\t\"registerTool\",\n\t\"registerCommand\",\n\t\"registerShortcut\",\n\t\"registerFlag\",\n\t\"registerMessageRenderer\",\n\t\"registerMarkdownTransformer\",\n\t\"registerEntryRenderer\",\n\t\"registerProvider\",\n\t\"unregisterProvider\",\n\t\"registerMcpServer\",\n\t\"unregisterMcpServer\",\n\t\"registerVirtualModel\",\n\t\"unregisterVirtualModel\",\n]);\n\ninterface StagedCall {\n\tmember: string;\n\targs: unknown[];\n\tcancelled: boolean;\n\tunsubscribe?: () => void;\n}\n\n// Descriptions match the real Flow commands (packages/flow/src). flow-profile is served here through Flow's\n// runFlowProfileCommand.\nconst STUB_COMMANDS: Record<string, string> = {\n\tflow:\n\t\t\"Plan and dispatch a hierarchical agent run. \" +\n\t\t\"Args: <goal> [--task-class T] [--complexity N] [--risk low|medium|high|critical] \" +\n\t\t\"[--profile NAME] [--lead-size small|standard|large] [--cheap ALIAS] [--mid ALIAS] [--premium ALIAS] [--frontier ALIAS] [--model <capability>=ALIAS] [--effort LEVEL] \" +\n\t\t\"[--quality-floor F] [--cost-aggressiveness C] [--max-retries R] [--flow-admission FILE] \" +\n\t\t\"[--context FILE ...] [--with-last-reply] [--force]\\n\\n\" +\n\t\t\"With no triage flags, an LLM triage call (cheapest configured model) \" +\n\t\t\"auto-fills task_class, complexity, and risk from the goal text. \" +\n\t\t\"Models: flags > profile (orchestrator-profiles.json) > cost-tier resolver. See /flow-models.\",\n\t\"flow-continue\":\n\t\t\"Preview and explicitly confirm recovery in the original checkout. Args: [run-id] (no --yes or --force).\",\n\t\"flow-status\": \"Open the full live orchestration board (↑↓ PgUp/PgDn scroll, Esc closes without cancelling).\",\n\t\"flow-cancel\": \"Cancel the currently running /flow run, if any.\",\n\t\"flow-pause\": \"Pause the currently running /flow run, if any.\",\n\t\"flow-resume\": \"Resume a paused /flow run.\",\n\t\"flow-title\": \"Rename the active flow and display its title on the widget border.\",\n\t\"flow-models\":\n\t\t\"Manage which models /flow uses. Subcommands: show|list|validate [--live]|check|set|effort|use|new|pick|workflow. \" +\n\t\t\"Aliases like fable-5-1, opus-5-5, sonnet-5, gpt-6-sol, gpt-6-luna, astra resolve against your configured models.\",\n\t\"flow-profile\": \"List, show, select, import or edit Flow profiles. Args: [list|show|use|export|import|validate ...]\",\n\tomsg:\n\t\t\"Send a message to the running orchestration (queued, delivered to the next dispatched task). \" +\n\t\t\"Usage: /omsg <text> — the lead will see and respond to it. Use '\\\\n' for newlines if needed.\",\n};\n\n/**\n * Act on an already loaded run only; with Flow not loaded there is no run, so Flow is never imported for them. They\n * stay reachable when Flow is disabled so a live run can still be stopped. Every other stub is refused when disabled.\n */\nconst LOADED_RUN_COMMANDS = new Set([\"flow-cancel\", \"flow-status\", \"flow-pause\", \"flow-resume\"]);\n/** Kill switch: resume would restart dispatch, so it is refused while Flow is disabled (cancel, status and pause are not). */\nconst AVAILABLE_WHEN_DISABLED = new Set([\"flow-cancel\", \"flow-status\", \"flow-pause\"]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction disabledReason(pi: ExtensionAPI, env: NodeJS.ProcessEnv): string | undefined {\n\tconst raw = env[HUMAIN_FLOW_DISABLED_ENV]?.trim().toLowerCase();\n\tif (raw === \"1\" || raw === \"true\") return `${HUMAIN_FLOW_DISABLED_ENV}=${raw}`;\n\ttry {\n\t\t// `flow` is not a typed Settings key: settings.json keeps unknown keys, so read it raw.\n\t\tconst flow = (pi.getSettings() as Record<string, unknown>).flow;\n\t\tif (isRecord(flow) && flow.enabled === false) return \"setting flow.enabled=false\";\n\t} catch {\n\t\t// Runtime not bound yet: treat as enabled.\n\t}\n\treturn undefined;\n}\n\n/**\n * Run flowExtension against a staging proxy. Stub commands are captured (so names never double-register); every other\n * registration is recorded and applied to the real pi only after the factory and the session_start replay succeed, so\n * a failed attempt leaves nothing behind and a retry starts from scratch. Late registration is supported by the\n * extension loader (handlers, tools, commands and flags are written to the live extension object), but\n * `session_start` has already fired by the time Flow loads, so the last session_start event is replayed to Flow's\n * staged session_start handlers.\n *\n * Commit is not transactional for non-handler members: if one throws midway, committed handlers are unsubscribed, and\n * tools/commands/flags/shortcuts/renderers (keyed maps in the extension loader) are simply overwritten on retry.\n */\nasync function runFlowExtension(\n\tpi: ExtensionAPI,\n\tflowExtension: ExtensionFactory,\n\tlastSessionStart: () => unknown[] | undefined,\n): Promise<Map<string, CommandOptions>> {\n\tconst captured = new Map<string, CommandOptions>();\n\tconst staged: StagedCall[] = [];\n\t// After a successful commit the proxy forwards live, so registrations made later (from handlers) reach the real pi.\n\tlet live = false;\n\tconst proxy = new Proxy(pi, {\n\t\tget(target, prop) {\n\t\t\tif (!live && typeof prop === \"string\" && STAGED_MEMBERS.has(prop)) {\n\t\t\t\treturn (...args: unknown[]) => {\n\t\t\t\t\tif (prop === \"registerCommand\" && typeof args[0] === \"string\" && args[0] in STUB_COMMANDS) {\n\t\t\t\t\t\tcaptured.set(args[0], args[1] as CommandOptions);\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\tconst call: StagedCall = { member: prop, args, cancelled: false };\n\t\t\t\t\tstaged.push(call);\n\t\t\t\t\tif (prop !== \"on\") return undefined;\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tcall.cancelled = true;\n\t\t\t\t\t\tcall.unsubscribe?.();\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst value: unknown = Reflect.get(target, prop, target);\n\t\t\treturn typeof value === \"function\" ? value.bind(target) : value;\n\t\t},\n\t});\n\tawait flowExtension(proxy);\n\n\tlet replayed: unknown[] | undefined;\n\tfor (let start = lastSessionStart(); start && start !== replayed; start = lastSessionStart()) {\n\t\treplayed = start;\n\t\tfor (const call of staged) {\n\t\t\tif (call.member === \"on\" && call.args[0] === \"session_start\" && !call.cancelled) {\n\t\t\t\tawait (call.args[1] as EventHandler)(...start);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Synchronous from the last replay check: no session_start can slip between replay and commit.\n\tconst committed: Array<() => void> = [];\n\ttry {\n\t\tfor (const call of staged) {\n\t\t\tif (call.cancelled) continue;\n\t\t\tconst result = (Reflect.get(pi, call.member) as (...args: unknown[]) => unknown).apply(pi, call.args);\n\t\t\tif (call.member === \"on\") {\n\t\t\t\tcall.unsubscribe = result as () => void;\n\t\t\t\tcommitted.push(call.unsubscribe);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tfor (const unsubscribe of committed) unsubscribe();\n\t\tthrow error;\n\t}\n\tlive = true;\n\treturn captured;\n}\n\nexport function createHumainFlowLoaderExtensionFactory(options: HumainFlowLoaderOptions = {}): ExtensionFactory {\n\tconst env = options.env ?? process.env;\n\t// This is the single sanctioned lazy-load boundary for @humain/flow (see AGENTS.md \"No inline imports\" exception).\n\tconst importFlow = options.importFlow ?? (() => import(\"@humain/flow\"));\n\tconst flowPackageDir = options.flowPackageDir ?? getBundledFlowPackageDir();\n\tconst builtInProfilesDir = options.builtInProfilesDir ?? getBundledFlowProfilesDir();\n\tlet flowModule: Promise<FlowModule> | undefined;\n\n\treturn (pi) => {\n\t\tdisabledProbe = () => disabledReason(pi, env) !== undefined;\n\t\tlet lastStart: unknown[] | undefined;\n\t\tlet loaded: Promise<Map<string, CommandOptions>> | undefined;\n\n\t\tpi.on(\"session_start\", (event, ctx) => {\n\t\t\tlastStart = [event, ctx];\n\t\t});\n\t\tpi.registerFlag(\"profile\", { description: \"Flow profile id\", type: \"string\" });\n\t\tpi.registerFlag(\"flow-admission\", { description: \"Flow run admission file (JSON)\", type: \"string\" });\n\n\t\tconst loadModule = async (): Promise<FlowModule> => {\n\t\t\tif (flowPackageDir && !env[HUMAIN_FLOW_PACKAGE_DIR_ENV]) env[HUMAIN_FLOW_PACKAGE_DIR_ENV] = flowPackageDir;\n\t\t\tflowModule ??= importFlow().then((module) => {\n\t\t\t\trpcControl = module.createFlowRpcControl?.({\n\t\t\t\t\twriteFrame: (frame) => rpcOutput?.(frame),\n\t\t\t\t\tisDisabled: isFlowDisabled,\n\t\t\t\t\tsendFollowUp: (text) =>\n\t\t\t\t\t\trpcFollowUp ? rpcFollowUp(text) : Promise.reject(new Error(\"host unavailable\")),\n\t\t\t\t});\n\t\t\t\treturn module;\n\t\t\t});\n\t\t\ttry {\n\t\t\t\treturn await flowModule;\n\t\t\t} catch (error) {\n\t\t\t\tflowModule = undefined;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t};\n\n\t\trpcLoader = async () => {\n\t\t\ttry {\n\t\t\t\tawait loadModule();\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn rpcControl;\n\t\t};\n\n\t\tconst load = (): Promise<Map<string, CommandOptions>> => {\n\t\t\tif (!loaded) {\n\t\t\t\tconst attempt = (async () => {\n\t\t\t\t\tconst module = await loadModule();\n\t\t\t\t\treturn runFlowExtension(pi, module.flowExtension, () => lastStart);\n\t\t\t\t})();\n\t\t\t\tloaded = attempt;\n\t\t\t\t// A failed load must be retryable on the next invocation.\n\t\t\t\tattempt.catch(() => {\n\t\t\t\t\tif (loaded === attempt) loaded = undefined;\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn loaded;\n\t\t};\n\n\t\tfor (const [name, description] of Object.entries(STUB_COMMANDS)) {\n\t\t\tpi.registerCommand(name, {\n\t\t\t\tdescription,\n\t\t\t\thandler: async (args: string, ctx: ExtensionCommandContext) => {\n\t\t\t\t\tconst reason = AVAILABLE_WHEN_DISABLED.has(name) ? undefined : disabledReason(pi, env);\n\t\t\t\t\tif (reason) {\n\t\t\t\t\t\tctx.ui.notify(`Flow disabled (${reason}). /${name} is unavailable.`, \"warning\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (name === \"flow-profile\") {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst module = await loadModule();\n\t\t\t\t\t\t\tif (!module.runFlowProfileCommand || !module.createProfileStore) {\n\t\t\t\t\t\t\t\tctx.ui.notify(\"/flow-profile is not available in this Flow build\", \"error\");\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst store = module.createProfileStore({\n\t\t\t\t\t\t\t\tflowDir: options.flowDir ?? join(getAgentDir(), \"flow\"),\n\t\t\t\t\t\t\t\t...(builtInProfilesDir !== undefined ? { builtInDir: builtInProfilesDir } : {}),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tawait module.runFlowProfileCommand(args, ctx, { store });\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t\t`/flow-profile failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (LOADED_RUN_COMMANDS.has(name) && !loaded) {\n\t\t\t\t\t\tctx.ui.notify(\"no active run\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet commands: Map<string, CommandOptions>;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcommands = await load();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t`Failed to load Flow: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst real = commands.get(name);\n\t\t\t\t\tif (!real) {\n\t\t\t\t\t\tctx.ui.notify(`/${name} is not available yet`, \"error\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait real.handler(args, ctx);\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t};\n}\n"]}
1
+ {"version":3,"file":"flow-loader.js","sourceRoot":"","sources":["../../src/humain/flow-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport { join } from \"node:path\";\nimport type { FlowFailureClass } from \"@humain/humain-code-contracts/flow-common\";\nimport type { FlowEventFrameV1, FlowReceiptV1, FlowStatusV1 } from \"@humain/humain-code-contracts/flow-control\";\nimport { getAgentDir, getBundledFlowPackageDir, getBundledFlowProfilesDir } from \"../config.ts\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionFactory,\n\tRegisteredCommand,\n} from \"../core/extensions/types.ts\";\nimport { createTraceRedactor } from \"../core/trace/redaction.ts\";\nimport { sanitizeForTerminal } from \"../core/trace-client/sanitize.ts\";\n\n/**\n * Lazy loader for the private @humain/flow package.\n *\n * The CLI entry graph must not statically reach Flow: this module registers cheap stub commands and a flag, and\n * only loads Flow (one memoized dynamic import) the first time a stub runs. Flow is bundled as its own chunk.\n */\n\nexport const HUMAIN_FLOW_DISABLED_ENV = \"HUMAIN_TERMINAL_FLOW_DISABLED\";\nexport const HUMAIN_FLOW_PACKAGE_DIR_ENV = \"HUMAIN_TERMINAL_FLOW_PACKAGE_DIR\";\n\n/** Run-control surface Flow exposes to rpc mode (flow_status/pause/resume/cancel/message). */\nexport interface FlowRpcControl {\n\thandle(input: unknown): Promise<{ receipt: FlowReceiptV1; status?: FlowStatusV1 }>;\n\tactiveRunId(): string | undefined;\n}\n\nexport interface FlowModule {\n\tflowExtension: ExtensionFactory;\n\t/** Documented rpc hook: called once when Flow loads; `writeFrame` carries `flow_event` frames to the rpc host. */\n\tcreateFlowRpcControl?(options: {\n\t\twriteFrame?: (frame: FlowEventFrameV1) => void | Promise<void>;\n\t\tisDisabled?: () => boolean;\n\t\t/** Queues text on the host session as a follow-up (flow_message with no active run). */\n\t\tsendFollowUp?: (text: string) => Promise<void>;\n\t}): FlowRpcControl;\n\t/** B1 profile command; optional so test fakes need not provide it. Methods stay bivariant for the real signatures. */\n\trunFlowProfileCommand?(args: string, ctx: ExtensionCommandContext, deps: { store: unknown }): Promise<unknown>;\n\tcreateProfileStore?(options: { flowDir: string; builtInDir?: string }): unknown;\n}\n\nexport interface HumainFlowLoaderOptions {\n\t/** Test seam. Defaults to the single sanctioned dynamic import of @humain/flow. */\n\timportFlow?: () => Promise<FlowModule>;\n\tenv?: NodeJS.ProcessEnv;\n\t/** Directory holding Flow's agents/assets/package.json. Defaults to the bundled layout's flow/ dir. */\n\tflowPackageDir?: string;\n\t/** Built-in Flow profiles dir. Defaults to the bundled layout's assets/profiles; undefined lets Flow find its own. */\n\tbuiltInProfilesDir?: string;\n\t/** Flow profile store directory. Defaults to `<agentDir>/flow`. */\n\tflowDir?: string;\n}\n\nlet rpcControl: FlowRpcControl | undefined;\nlet disabledProbe: (() => boolean) | undefined;\nlet rpcOutput: ((frame: FlowEventFrameV1) => void | Promise<void>) | undefined;\nlet rpcLoader: (() => Promise<FlowRpcControl | undefined>) | undefined;\nlet rpcFollowUp: ((text: string) => Promise<void>) | undefined;\n\n/** The documented rpc hook: rpc mode reads Flow's run control here and never imports Flow itself. */\nexport function getFlowRpcControl(): FlowRpcControl | undefined {\n\treturn rpcControl;\n}\n\n/**\n * The documented rpc hook: loads Flow through the same lazy-load path the stub commands use and returns its run control.\n * Undefined when Flow is disabled (never loaded then), not wired, or fails to load.\n */\nexport async function loadFlowRpcControl(): Promise<FlowRpcControl | undefined> {\n\tif (rpcControl) return rpcControl;\n\tif (isFlowDisabled()) return undefined;\n\treturn rpcLoader?.();\n}\n\n/** True while Flow is switched off (flow.enabled=false or the env kill switch); rpc mode refuses flow_resume then. */\nexport function isFlowDisabled(): boolean {\n\treturn disabledProbe?.() ?? false;\n}\n\n/** The documented rpc hook: rpc mode registers where Flow's `flow_event` frames are written. */\nexport function setFlowRpcOutput(writer: ((frame: FlowEventFrameV1) => void | Promise<void>) | undefined): void {\n\trpcOutput = writer;\n}\n\n/** The documented rpc hook: rpc mode registers how direct-mode flow_message text is queued as a host follow-up. */\nexport function setFlowRpcFollowUp(queue: ((text: string) => Promise<void>) | undefined): void {\n\trpcFollowUp = queue;\n}\n\ntype CommandOptions = Omit<RegisteredCommand, \"name\" | \"sourceInfo\">;\ntype EventHandler = (...args: unknown[]) => unknown;\n\n/**\n * ExtensionAPI members that only record registrations (see core/extensions/loader.ts). They are staged while Flow's\n * factory runs and applied to the real pi only after the factory and the session_start replay succeed. Reads and\n * actions (getFlag, getSettings, exec, sendMessage, ...) and `events` stay live: they have no registration to undo\n * or are needed by the factory itself. Flow uses none of the live ones that subscribe (`events.on`).\n */\nconst STAGED_MEMBERS = new Set([\n\t\"on\",\n\t\"registerTool\",\n\t\"registerCommand\",\n\t\"registerShortcut\",\n\t\"registerFlag\",\n\t\"registerMessageRenderer\",\n\t\"registerMarkdownTransformer\",\n\t\"registerEntryRenderer\",\n\t\"registerProvider\",\n\t\"unregisterProvider\",\n\t\"registerMcpServer\",\n\t\"unregisterMcpServer\",\n\t\"registerVirtualModel\",\n\t\"unregisterVirtualModel\",\n]);\n\ninterface StagedCall {\n\tmember: string;\n\targs: unknown[];\n\tcancelled: boolean;\n\tunsubscribe?: () => void;\n}\n\n// Descriptions match the real Flow commands (packages/flow/src). flow-profile is served here through Flow's\n// runFlowProfileCommand.\nconst STUB_COMMANDS: Record<string, string> = {\n\tflow:\n\t\t\"Plan and dispatch a hierarchical agent run. \" +\n\t\t\"Args: <goal> [--task-class T] [--complexity N] [--risk low|medium|high|critical] \" +\n\t\t\"[--profile NAME] [--lead-size small|standard|large] [--cheap ALIAS] [--mid ALIAS] [--premium ALIAS] [--frontier ALIAS] [--model <capability>=ALIAS] [--effort LEVEL] \" +\n\t\t\"[--quality-floor F] [--cost-aggressiveness C] [--max-retries R] [--flow-admission FILE] \" +\n\t\t\"[--context FILE ...] [--with-last-reply] [--force]\\n\\n\" +\n\t\t\"With no triage flags, an LLM triage call (cheapest configured model) \" +\n\t\t\"auto-fills task_class, complexity, and risk from the goal text. \" +\n\t\t\"Models: flags > profile (orchestrator-profiles.json) > cost-tier resolver. See /flow-models.\",\n\t\"flow-continue\":\n\t\t\"Preview and explicitly confirm recovery in the original checkout. Args: [run-id] (no --yes or --force).\",\n\t\"flow-status\": \"Open the full live orchestration board (↑↓ PgUp/PgDn scroll, Esc closes without cancelling).\",\n\t\"flow-cancel\": \"Cancel the currently running /flow run, if any.\",\n\t\"flow-pause\": \"Pause the currently running /flow run, if any.\",\n\t\"flow-resume\": \"Resume a paused /flow run.\",\n\t\"flow-title\": \"Rename the active flow and display its title on the widget border.\",\n\t\"flow-models\":\n\t\t\"Manage which models /flow uses. Subcommands: show|list|validate [--live]|check|set|effort|use|new|pick|workflow. \" +\n\t\t\"Aliases like fable-5-1, opus-5-5, sonnet-5, gpt-6-sol, gpt-6-luna, astra resolve against your configured models.\",\n\t\"flow-profile\": \"List, show, select, import or edit Flow profiles. Args: [list|show|use|export|import|validate ...]\",\n\tomsg:\n\t\t\"Send a message to the running orchestration (queued, delivered to the next dispatched task). \" +\n\t\t\"Usage: /omsg <text> — the lead will see and respond to it. Use '\\\\n' for newlines if needed.\",\n};\n\n/**\n * Act on an already loaded run only; with Flow not loaded there is no run, so Flow is never imported for them. They\n * stay reachable when Flow is disabled so a live run can still be stopped. Every other stub is refused when disabled.\n */\nconst LOADED_RUN_COMMANDS = new Set([\"flow-cancel\", \"flow-status\", \"flow-pause\", \"flow-resume\"]);\n/** Kill switch: resume would restart dispatch, so it is refused while Flow is disabled (cancel, status and pause are not). */\nconst AVAILABLE_WHEN_DISABLED = new Set([\"flow-cancel\", \"flow-status\", \"flow-pause\"]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\n/**\n * Print/json mode has no UI: core makes ctx.ui.notify a no-op, so headless notices go to stderr as bare text.\n * Notice text can echo user args or env values, so it is redacted, stripped of terminal escapes, then redacted again\n * with the same redactor: the first pass catches secrets that stripping could otherwise mangle, the second catches\n * secrets that were split by escapes and are rejoined by stripping. Fails closed to a fixed line on any failure.\n * Replacing `ui` snapshots ui (descriptors preserve other ctx getters, so lazy getters on ctx stay live; same approach\n * as the runner's createCommandContext).\n */\nconst NOTICE_WITHHELD = \"[notice withheld: redaction unavailable]\";\n\nfunction safeNotice(message: string): string {\n\ttry {\n\t\tconst redactor = createTraceRedactor({ env: process.env });\n\t\treturn redactor.redactText(sanitizeForTerminal(redactor.redactText(message).text)).text;\n\t} catch {\n\t\treturn NOTICE_WITHHELD;\n\t}\n}\n\nfunction withHeadlessNotices(ctx: ExtensionCommandContext): ExtensionCommandContext {\n\tif (ctx.mode !== \"print\" && ctx.mode !== \"json\") return ctx;\n\tconst ui = Object.defineProperties({}, Object.getOwnPropertyDescriptors(ctx.ui)) as ExtensionCommandContext[\"ui\"];\n\tui.notify = (message: string) => {\n\t\tprocess.stderr.write(`${safeNotice(message)}\\n`);\n\t};\n\tconst wrapped = Object.defineProperties({}, Object.getOwnPropertyDescriptors(ctx)) as ExtensionCommandContext;\n\tObject.defineProperty(wrapped, \"ui\", { value: ui, enumerable: true, configurable: true });\n\treturn wrapped;\n}\n\n/**\n * Mirrors packages/flow emitRefusalSettled: an rpc /flow start that dispatches nothing still emits one terminal\n * `run_settled` frame so the host stops waiting. Built here because the entry graph must not reach @humain/flow.\n * Print/json refusals exit non-zero (mirrors flow's markHeadlessFailure). Never throws; no-op otherwise.\n */\nasync function settleRefusal(\n\tname: string,\n\tctx: ExtensionCommandContext,\n\tfailureClass: FlowFailureClass,\n): Promise<void> {\n\tif ((name === \"flow\" || name === \"flow-continue\") && (ctx.mode === \"print\" || ctx.mode === \"json\"))\n\t\tprocess.exitCode = 1;\n\tif (ctx.mode !== \"rpc\" || !rpcOutput || (name !== \"flow\" && name !== \"flow-continue\")) return;\n\ttry {\n\t\tconst runId = `flow-refused-${randomBytes(8).toString(\"hex\")}`;\n\t\tconst frame: FlowEventFrameV1 = {\n\t\t\ttype: \"flow_event\",\n\t\t\tevent: {\n\t\t\t\tschema: \"humain-flow-event\",\n\t\t\t\tschemaVersion: 1,\n\t\t\t\teventId: `${runId}.refused`,\n\t\t\t\trunId,\n\t\t\t\tseq: 1,\n\t\t\t\tts: new Date().toISOString(),\n\t\t\t\tprofile: {\n\t\t\t\t\tid: \"unknown\",\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tdigest: createHash(\"sha256\").update(\"unknown\", \"utf8\").digest(\"hex\"),\n\t\t\t\t},\n\t\t\t\tcauses: [],\n\t\t\t\tkind: \"run_settled\",\n\t\t\t\tpayload: { outcome: \"failed\", failureClass, settledCleanly: true, retryOffer: [\"direct\"], totals: {} },\n\t\t\t},\n\t\t};\n\t\tawait rpcOutput(frame);\n\t} catch {\n\t\t// Refusal must still notify; a dead transport is not the command's problem.\n\t}\n}\n\nfunction disabledReason(pi: ExtensionAPI, env: NodeJS.ProcessEnv): string | undefined {\n\tconst raw = env[HUMAIN_FLOW_DISABLED_ENV]?.trim().toLowerCase();\n\tif (raw === \"1\" || raw === \"true\") return `${HUMAIN_FLOW_DISABLED_ENV}=${raw}`;\n\ttry {\n\t\t// `flow` is not a typed Settings key: settings.json keeps unknown keys, so read it raw.\n\t\tconst flow = (pi.getSettings() as Record<string, unknown>).flow;\n\t\tif (isRecord(flow) && flow.enabled === false) return \"setting flow.enabled=false\";\n\t} catch {\n\t\t// Runtime not bound yet: treat as enabled.\n\t}\n\treturn undefined;\n}\n\n/**\n * Run flowExtension against a staging proxy. Stub commands are captured (so names never double-register); every other\n * registration is recorded and applied to the real pi only after the factory and the session_start replay succeed, so\n * a failed attempt leaves nothing behind and a retry starts from scratch. Late registration is supported by the\n * extension loader (handlers, tools, commands and flags are written to the live extension object), but\n * `session_start` has already fired by the time Flow loads, so the last session_start event is replayed to Flow's\n * staged session_start handlers.\n *\n * Commit is not transactional for non-handler members: if one throws midway, committed handlers are unsubscribed, and\n * tools/commands/flags/shortcuts/renderers (keyed maps in the extension loader) are simply overwritten on retry.\n */\nasync function runFlowExtension(\n\tpi: ExtensionAPI,\n\tflowExtension: ExtensionFactory,\n\tlastSessionStart: () => unknown[] | undefined,\n): Promise<Map<string, CommandOptions>> {\n\tconst captured = new Map<string, CommandOptions>();\n\tconst staged: StagedCall[] = [];\n\t// After a successful commit the proxy forwards live, so registrations made later (from handlers) reach the real pi.\n\tlet live = false;\n\tconst proxy = new Proxy(pi, {\n\t\tget(target, prop) {\n\t\t\tif (!live && typeof prop === \"string\" && STAGED_MEMBERS.has(prop)) {\n\t\t\t\treturn (...args: unknown[]) => {\n\t\t\t\t\tif (prop === \"registerCommand\" && typeof args[0] === \"string\" && args[0] in STUB_COMMANDS) {\n\t\t\t\t\t\tcaptured.set(args[0], args[1] as CommandOptions);\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\tconst call: StagedCall = { member: prop, args, cancelled: false };\n\t\t\t\t\tstaged.push(call);\n\t\t\t\t\tif (prop !== \"on\") return undefined;\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tcall.cancelled = true;\n\t\t\t\t\t\tcall.unsubscribe?.();\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst value: unknown = Reflect.get(target, prop, target);\n\t\t\treturn typeof value === \"function\" ? value.bind(target) : value;\n\t\t},\n\t});\n\tawait flowExtension(proxy);\n\n\tlet replayed: unknown[] | undefined;\n\tfor (let start = lastSessionStart(); start && start !== replayed; start = lastSessionStart()) {\n\t\treplayed = start;\n\t\tfor (const call of staged) {\n\t\t\tif (call.member === \"on\" && call.args[0] === \"session_start\" && !call.cancelled) {\n\t\t\t\tawait (call.args[1] as EventHandler)(...start);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Synchronous from the last replay check: no session_start can slip between replay and commit.\n\tconst committed: Array<() => void> = [];\n\ttry {\n\t\tfor (const call of staged) {\n\t\t\tif (call.cancelled) continue;\n\t\t\tconst result = (Reflect.get(pi, call.member) as (...args: unknown[]) => unknown).apply(pi, call.args);\n\t\t\tif (call.member === \"on\") {\n\t\t\t\tcall.unsubscribe = result as () => void;\n\t\t\t\tcommitted.push(call.unsubscribe);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tfor (const unsubscribe of committed) unsubscribe();\n\t\tthrow error;\n\t}\n\tlive = true;\n\treturn captured;\n}\n\nexport function createHumainFlowLoaderExtensionFactory(options: HumainFlowLoaderOptions = {}): ExtensionFactory {\n\tconst env = options.env ?? process.env;\n\t// This is the single sanctioned lazy-load boundary for @humain/flow (see AGENTS.md \"No inline imports\" exception).\n\tconst importFlow = options.importFlow ?? (() => import(\"@humain/flow\"));\n\tconst flowPackageDir = options.flowPackageDir ?? getBundledFlowPackageDir();\n\tconst builtInProfilesDir = options.builtInProfilesDir ?? getBundledFlowProfilesDir();\n\tlet flowModule: Promise<FlowModule> | undefined;\n\n\treturn (pi) => {\n\t\tdisabledProbe = () => disabledReason(pi, env) !== undefined;\n\t\tlet lastStart: unknown[] | undefined;\n\t\tlet loaded: Promise<Map<string, CommandOptions>> | undefined;\n\n\t\tpi.on(\"session_start\", (event, ctx) => {\n\t\t\tlastStart = [event, ctx];\n\t\t});\n\t\tpi.registerFlag(\"profile\", { description: \"Flow profile id\", type: \"string\" });\n\t\tpi.registerFlag(\"flow-admission\", { description: \"Flow run admission file (JSON)\", type: \"string\" });\n\n\t\tconst loadModule = async (): Promise<FlowModule> => {\n\t\t\tif (flowPackageDir && !env[HUMAIN_FLOW_PACKAGE_DIR_ENV]) env[HUMAIN_FLOW_PACKAGE_DIR_ENV] = flowPackageDir;\n\t\t\tflowModule ??= importFlow().then((module) => {\n\t\t\t\trpcControl = module.createFlowRpcControl?.({\n\t\t\t\t\twriteFrame: (frame) => rpcOutput?.(frame),\n\t\t\t\t\tisDisabled: isFlowDisabled,\n\t\t\t\t\tsendFollowUp: (text) =>\n\t\t\t\t\t\trpcFollowUp ? rpcFollowUp(text) : Promise.reject(new Error(\"host unavailable\")),\n\t\t\t\t});\n\t\t\t\treturn module;\n\t\t\t});\n\t\t\ttry {\n\t\t\t\treturn await flowModule;\n\t\t\t} catch (error) {\n\t\t\t\tflowModule = undefined;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t};\n\n\t\trpcLoader = async () => {\n\t\t\ttry {\n\t\t\t\tawait loadModule();\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn rpcControl;\n\t\t};\n\n\t\tconst load = (): Promise<Map<string, CommandOptions>> => {\n\t\t\tif (!loaded) {\n\t\t\t\tconst attempt = (async () => {\n\t\t\t\t\tconst module = await loadModule();\n\t\t\t\t\treturn runFlowExtension(pi, module.flowExtension, () => lastStart);\n\t\t\t\t})();\n\t\t\t\tloaded = attempt;\n\t\t\t\t// A failed load must be retryable on the next invocation.\n\t\t\t\tattempt.catch(() => {\n\t\t\t\t\tif (loaded === attempt) loaded = undefined;\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn loaded;\n\t\t};\n\n\t\tfor (const [name, description] of Object.entries(STUB_COMMANDS)) {\n\t\t\tpi.registerCommand(name, {\n\t\t\t\tdescription,\n\t\t\t\thandler: async (args: string, rawCtx: ExtensionCommandContext) => {\n\t\t\t\t\tconst ctx = withHeadlessNotices(rawCtx);\n\t\t\t\t\tconst reason = AVAILABLE_WHEN_DISABLED.has(name) ? undefined : disabledReason(pi, env);\n\t\t\t\t\tif (reason) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tctx.ui.notify(`Flow disabled (${reason}). /${name} is unavailable.`, \"warning\");\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tawait settleRefusal(name, ctx, \"config_invalid\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (name === \"flow-profile\") {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst module = await loadModule();\n\t\t\t\t\t\t\tif (!module.runFlowProfileCommand || !module.createProfileStore) {\n\t\t\t\t\t\t\t\tctx.ui.notify(\"/flow-profile is not available in this Flow build\", \"error\");\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst store = module.createProfileStore({\n\t\t\t\t\t\t\t\tflowDir: options.flowDir ?? join(getAgentDir(), \"flow\"),\n\t\t\t\t\t\t\t\t...(builtInProfilesDir !== undefined ? { builtInDir: builtInProfilesDir } : {}),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tawait module.runFlowProfileCommand(args, ctx, { store });\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t\t`/flow-profile failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (LOADED_RUN_COMMANDS.has(name) && !loaded) {\n\t\t\t\t\t\tctx.ui.notify(\"no active run\", \"info\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet commands: Map<string, CommandOptions>;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcommands = await load();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t\t`Failed to load Flow: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tawait settleRefusal(name, ctx, \"internal\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst real = commands.get(name);\n\t\t\t\t\tif (!real) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tctx.ui.notify(`/${name} is not available yet`, \"error\");\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tawait settleRefusal(name, ctx, \"internal\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait real.handler(args, ctx);\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t};\n}\n"]}
@@ -40,7 +40,7 @@ Four read-only profiles ship in `packages/flow/assets/profiles/`. All are `kind:
40
40
  | `premium` | `openai-codex/gpt-6.1-sol` | `openai-codex/gpt-6-luna` | `openai-codex/gpt-6.1-sol` | `anthropic/claude-sonnet-5-5` |
41
41
  | `anthropic` | `anthropic/claude-opus-5-5` | `anthropic/claude-sonnet-5` | `anthropic/claude-sonnet-5` | `anthropic/claude-sonnet-5` |
42
42
  | `openai` | `openai-codex/gpt-6.1-sol` | `openai-codex/gpt-6-luna` | `openai-codex/gpt-6.1-sol` | `openai-codex/gpt-6.1-sol` |
43
- | `oss-first` | `humain-node/glm-5.2` | `humain-node/glm-5.2` | `humain-node/glm-5.2` | `humain-node/glm-5.2` |
43
+ | `oss-first` v2 | `humain-node/glm-5.3-flash` | `humain-node/glm-5.3-flash` | `humain-node/glm-5.3-flash` | `humain-node/glm-5.3-flash` |
44
44
 
45
45
  Built-ins cannot be saved over or deleted (`profile_builtin_readonly`). Clone one to customize it. The ids are reserved: a file with a built-in id in the user directory is reported as `profile_id_reserved` and not loaded.
46
46
 
@@ -139,7 +139,7 @@ The editor only uses the standard `select`, `confirm`, and `input` dialogs. It a
139
139
 
140
140
  ## Selecting a profile for a run
141
141
 
142
- A run selects a profile with `--profile <id>`. If the flag is omitted, the active profile, then `premium`, is used (see Resolution order). The `--profile <id>` flag and the writing of `runs/<id>/admission.json` are NOT part of this change. They are provided by the Flow loader integration (stream A2, pending). B1 provides only `resolveProfile` and `buildCliAdmission`.
142
+ A run selects a profile with `--profile <id>`. If the flag is omitted, the active profile, then `premium`, is used (see Resolution order). The Flow loader registers `--profile <id>` and `--flow-admission <file>`. Flow resolves the selected profile, freezes it in `runs/<id>/admission.json` before dispatch, and uses that frozen profile for the run. A profile confines dispatch to its listed role primaries and backups; escalation and mandatory review cannot select models outside it. An unavailable role primary routes to the role's first resolvable listed backup that qualifies. Nested `subagent` launches by Flow children are restricted to the profile's models (`HUMAIN_ORCHESTRATOR_RESIDENT_MODELS`); a child running a shell command can still start the CLI directly (known limit).
143
143
 
144
144
  `buildCliAdmission` freezes the resolved profile into the admission document for the run:
145
145
 
@@ -149,6 +149,8 @@ A run selects a profile with `--profile <id>`. If the flag is omitted, the activ
149
149
  - `mode` is `direct` for `kind: direct` profiles and `auto` for `kind: flow`.
150
150
  - The document gets its own `admissionDigest` and is validated before it is returned.
151
151
 
152
+ `qwen3.8-flash-next` has no sourced maximum-output value in `packages/ai/src/providers/data/*.json`; the separate `qwen3.8-flash` entry lists 131072. The live HUMAIN Node catalog reports `max_output_tokens: 0` (unknown). Flow keeps strict model qualification, so the `oss-first` profile's `scout` and `triage` roles use their listed `glm-5.3-flash` backup.
153
+
152
154
  ## Error codes
153
155
 
154
156
  | Code | Meaning |
package/docs/flow.md CHANGED
@@ -20,7 +20,7 @@ Stub commands: `/flow`, `/flow-continue`, `/flow-status`, `/flow-cancel`, `/flow
20
20
  [--context FILE ...] [--with-last-reply] [--force]
21
21
  ```
22
22
 
23
- Flags are honored only before or after the goal text. A `--flag` between goal words is part of the goal. Unrecognized flags are reported, not silently dropped. Values are not all validated by the parser: `--complexity` is rounded and clamped to 1-10, and a non-numeric value falls back to 5. `--risk` and `--task-class` are stored as given, and the parser does not check them against a list of allowed values. The parser also accepts other flags (`--fan-out`, `--candidate`, `--route-only`, `--live-qa`, `--no-live-qa`, `--live-qa-adapter`, `--live-qa-scope`, `--live-qa-acceptance`, `--check`); this guide covers only the flags above.
23
+ Flags are honored only before or after the goal text. A `--flag` between goal words is part of the goal. An unrecognized `--flag` that ends up inside the goal text (for example, a trailing `--size small`, where the value word makes the flag part of the goal) stays in the goal and is reported once as a warning: `Unknown flag(s) kept in the /flow goal text: ...`; the run still starts. Unrecognized flags in the leading or trailing flag block refuse the run. Values are not all validated by the parser: `--complexity` is rounded and clamped to 1-10, and a non-numeric value falls back to 5. `--risk` and `--task-class` are stored as given, and the parser does not check them against a list of allowed values. The parser also accepts other flags (`--fan-out`, `--candidate`, `--route-only`, `--live-qa`, `--no-live-qa`, `--live-qa-adapter`, `--live-qa-scope`, `--live-qa-acceptance`, `--check`); this guide covers only the flags above.
24
24
 
25
25
  Auto mode:
26
26
 
@@ -38,6 +38,14 @@ Flow has four workflow levels: `direct`, `checked`, `led`, `full`.
38
38
  | medium | `led` | lead plus workers, review, QA |
39
39
  | large | `full` | architect, recon, several leads, review, QA |
40
40
 
41
+ A `checked` run in a repository with no discovered deterministic checks passes when the independent review passes. It reports `evidence_status: unverified_checks_unavailable` as a warning in the run summary, `run.log`, and the ledger, and starts no repair. A review FAIL still fails. `direct` runs and continuations or recovery runs still require deterministic checks.
42
+
43
+ A `led` or `full` run (medium or large) with no discoverable deterministic checks settles the same way when the mandatory independent review passes and QA passes: `succeeded` with `evidence_status: unverified_checks_unavailable` as a warning, one QA dispatch, and no repair. A QA FAIL or review FAIL still fails the run. If checks appear during the run (at the run root, git root, verify root, or a changed file's package directories), the exception no longer applies. When no round was attested, the report's verification target shows the last observed candidate identity with `, not attested`, and prints `HEAD unborn` for a tree without commits.
44
+
45
+ The candidate identity normally comes from git. When git is missing (`ENOENT`) or refuses the repository for dubious ownership (`safe.directory`) and the root has a `.git` entry, it falls back to a git-free filesystem walk (basis `filesystem`). Unborn repositories (no commits) keep the git-based identity.
46
+
47
+ Each run writes its diagnostics under `<STATE_ROOT>/runs/<runId>/`. A repeated dispatch with the same task id (for example a QA re-dispatch) writes its stderr log as `<taskId>.attempt-N.stderr.log` (N = 2, 3, ...); the first attempt keeps `<taskId>.stderr.log`. `<taskId>.events.jsonl` and `<taskId>.prompt.md` are shared across attempts.
48
+
41
49
  The mapping is implemented as a pure function (`sizeToWorkflow`), and a rules-then-triage size decision exists (`decideSize`: rule classifier first, triage only when rules say medium or large). **Neither is called by the `/flow` run path yet.** Wiring size-based auto mode into `/flow` is `(planned)` (plan section 3 A3, deliverable 3; A2).
42
50
 
43
51
  What `/flow` does today to pick a level:
@@ -49,7 +57,7 @@ What `/flow` does today to pick a level:
49
57
 
50
58
  ### Size override
51
59
 
52
- A user-facing `--size small|medium|large` flag does not exist. The engine-level equivalent today is `--workflow`. A `--size` override is `(planned)` (plan section 3 A3 deliverable 3; section 3 D2 deliverable 1).
60
+ There is still no user-facing `--size` flag on `/flow` (`--size` in the goal is not applied; see warning above). A size override comes only from a `FlowRunAdmissionV1` `sizeOverride` (`hc --flow-admission <file>`). With an admission `sizeOverride: "small"`, the run uses `checked` unless a higher safety floor (risk, protected path, or cross-package interface change) applies, and skips the workflow scope scout (logged as `workflow: scope scout skipped (admission size small)`); the checked reviewer still runs. A user-facing CLI `--size` flag is `(planned)` (plan section 3 A3 deliverable 3; section 3 D2 deliverable 1).
53
61
 
54
62
  ### Escalation
55
63
 
@@ -64,7 +72,7 @@ A user-facing `--size small|medium|large` flag does not exist. The engine-level
64
72
 
65
73
  Today the id selects a profile in `orchestrator-profiles.json` (model bindings per tier and capability). Precedence for models is: flags, then profile, then the cost-tier resolver. An id not defined in that file produces a warning. Manage these with `/flow-models`.
66
74
 
67
- The versioned Flow profile store and the frozen run admission (`resolveProfile`, `buildCliAdmission`) exist in the Flow package but are not called by the `/flow` run path. Selecting them with `--profile`, and writing `admission.json` for each run, is `(planned)` (plan section 3 A2 deliverables 6 and 9; section 3 B1 deliverable 5). See [flow-profiles.md](flow-profiles.md). `hc --flow-admission <file>` is a string startup flag; it is authoritative for every `/flow` in the session: `/flow --profile` and `/flow --flow-admission <other file>` are refused before dispatch, and the same path is allowed.
75
+ The versioned Flow profile store and the frozen run admission (`resolveProfile`, `buildCliAdmission`) exist in the Flow package but are not called by the `/flow` run path. Selecting them with `--profile`, and writing `admission.json` for each run, is `(planned)` (plan section 3 A2 deliverables 6 and 9; section 3 B1 deliverable 5). See [flow-profiles.md](flow-profiles.md). `hc --flow-admission <file>` is a string startup flag and is authoritative for every `/flow` in the session. With it, `/flow` parses no flags: the whole argument text is the goal, and flag-like tokens (`--force`, `--candidate`, `--context`, `--profile`, `--model`, `--route-only`, `--flow-admission`, and any other `--x` or `-y`) are left in the goal, not applied, and reported once as a warning (`Ignored flag(s) in the /flow goal: ...`). Combining startup `--profile` with startup `--flow-admission` is refused before dispatch.
68
76
 
69
77
  ## Run control and inspection
70
78
 
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@humain/humain-code",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@humain/humain-code",
9
- "version": "0.4.1",
9
+ "version": "0.4.3",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@humain/humain-code-contracts": "0.4.1",
12
+ "@humain/humain-code-contracts": "0.4.3",
13
13
  "@aws-sdk/client-bedrock-runtime": "3.1146.0",
14
14
  "@earendil-works/pi-agent-core": "1.1.0",
15
15
  "@earendil-works/pi-ai": "1.1.0",
@@ -1024,9 +1024,9 @@
1024
1024
  "hasInstallScript": true
1025
1025
  },
1026
1026
  "node_modules/@humain/humain-code-contracts": {
1027
- "version": "0.4.1",
1028
- "resolved": "https://registry.npmjs.org/@humain/humain-code-contracts/-/humain-code-contracts-0.4.1.tgz",
1029
- "integrity": "sha512-a1t8577LgoOeHnU/uND3VMfzVG7f+ClYLqNKYIT44NpDKFirMKEQ7UolKyDPmF6bfHHvrl6dZXDOHcEsZbNYJA==",
1027
+ "version": "0.4.3",
1028
+ "resolved": "https://registry.npmjs.org/@humain/humain-code-contracts/-/humain-code-contracts-0.4.3.tgz",
1029
+ "integrity": "sha512-VJSnuajBJOaHQR8BZbzXZhCKXMxCOz1/+JWtrWuX4o0FEN5HH+LqWb0uV8nsuHyOZepL+yKRvhAGyy/h7i8mAQ==",
1030
1030
  "license": "MIT",
1031
1031
  "dependencies": {
1032
1032
  "typebox": "1.3.35"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humain/humain-code",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "humainTerminalConfig": {
@@ -86,7 +86,7 @@
86
86
  "prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
87
87
  },
88
88
  "dependencies": {
89
- "@humain/humain-code-contracts": "0.4.1",
89
+ "@humain/humain-code-contracts": "0.4.3",
90
90
  "@aws-sdk/client-bedrock-runtime": "3.1146.0",
91
91
  "@earendil-works/pi-agent-core": "1.1.0",
92
92
  "@earendil-works/pi-ai": "1.1.0",