@cnwenf/occ 2.1.314 → 2.1.315

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 (2) hide show
  1. package/dist/cli.js +338 -133
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- globalThis.MACRO={"VERSION":"2.1.314","BINARY_NAME":"occ","BUILD_TIME":"2026-08-28T00:45:59.205Z","FEEDBACK_CHANNEL":"","ISSUES_EXPLAINER":"","NATIVE_PACKAGE_URL":"","PACKAGE_URL":"@cnwenf/occ","VERSION_CHANGELOG":""};
2
+ globalThis.MACRO={"VERSION":"2.1.315","BINARY_NAME":"occ","BUILD_TIME":"2026-08-28T20:42:08.546Z","FEEDBACK_CHANNEL":"","ISSUES_EXPLAINER":"","NATIVE_PACKAGE_URL":"","PACKAGE_URL":"@cnwenf/occ","VERSION_CHANGELOG":""};
3
3
  // @bun
4
4
  var __create = Object.create;
5
5
  var __getProtoOf = Object.getPrototypeOf;
@@ -231222,6 +231222,7 @@ async function loadAgentFromFile(filePath, pluginName, namespace, sourceName, pl
231222
231222
  }
231223
231223
  const isolationRaw = frontmatter.isolation;
231224
231224
  const isolation = isolationRaw === "worktree" ? "worktree" : undefined;
231225
+ const cacheTtl = extractAgentCacheTtl(frontmatter);
231225
231226
  const effortRaw = frontmatter.effort;
231226
231227
  const effort = effortRaw !== undefined ? parseEffortValue(effortRaw) : undefined;
231227
231228
  if (effortRaw !== undefined && effort === undefined) {
@@ -231274,7 +231275,8 @@ async function loadAgentFromFile(filePath, pluginName, namespace, sourceName, pl
231274
231275
  ...memory ? { memory } : {},
231275
231276
  ...isolation ? { isolation } : {},
231276
231277
  ...effort !== undefined ? { effort } : {},
231277
- ...maxTurns !== undefined ? { maxTurns } : {}
231278
+ ...maxTurns !== undefined ? { maxTurns } : {},
231279
+ ...cacheTtl !== undefined ? { cacheTtl } : {}
231278
231280
  };
231279
231281
  } catch (error52) {
231280
231282
  logForDebugging(`Failed to load agent from ${filePath}: ${error52}`, {
@@ -231291,6 +231293,7 @@ var init_loadPluginAgents = __esm(() => {
231291
231293
  init_memoize();
231292
231294
  init_paths();
231293
231295
  init_agentMemory();
231296
+ init_loadAgentsDir();
231294
231297
  init_prompt3();
231295
231298
  init_prompt4();
231296
231299
  init_debug();
@@ -232235,6 +232238,7 @@ __export(exports_loadAgentsDir, {
232235
232238
  getAgentDefinitionsWithOverrides: () => getAgentDefinitionsWithOverrides,
232236
232239
  getActiveAgentsFromList: () => getActiveAgentsFromList,
232237
232240
  filterAgentsByMcpRequirements: () => filterAgentsByMcpRequirements,
232241
+ extractAgentCacheTtl: () => extractAgentCacheTtl,
232238
232242
  clearAgentDefinitionsCache: () => clearAgentDefinitionsCache
232239
232243
  });
232240
232244
  import { basename as basename12, dirname as dirname26, resolve as resolve23, join as join51 } from "path";
@@ -232438,6 +232442,14 @@ function parseAgentsFromJson(agentsJson, source = "flagSettings") {
232438
232442
  return [];
232439
232443
  }
232440
232444
  }
232445
+ function extractAgentCacheTtl(frontmatter) {
232446
+ const experimental = frontmatter.experimental;
232447
+ if (typeof experimental !== "object" || experimental === null) {
232448
+ return;
232449
+ }
232450
+ const raw = Object.entries(experimental).find(([key2]) => key2.toLowerCase() === "cachettl")?.[1];
232451
+ return raw === "5m" || raw === "1h" ? raw : undefined;
232452
+ }
232441
232453
  function parseAgentFromMarkdown(filePath, baseDir, frontmatter, content, source) {
232442
232454
  try {
232443
232455
  const agentType = frontmatter["name"];
@@ -232487,6 +232499,7 @@ function parseAgentFromMarkdown(filePath, baseDir, frontmatter, content, source)
232487
232499
  logForDebugging(`Agent file ${filePath} has invalid isolation value '${isolationRaw}'. Valid options: ${VALID_ISOLATION_MODES.join(", ")}`);
232488
232500
  }
232489
232501
  }
232502
+ const cacheTtl = extractAgentCacheTtl(frontmatter);
232490
232503
  const effortRaw = frontmatter["effort"];
232491
232504
  const parsedEffort = effortRaw !== undefined ? parseEffortValue(effortRaw) : undefined;
232492
232505
  if (effortRaw !== undefined && parsedEffort === undefined) {
@@ -232564,7 +232577,8 @@ function parseAgentFromMarkdown(filePath, baseDir, frontmatter, content, source)
232564
232577
  ...maxTurns !== undefined ? { maxTurns } : {},
232565
232578
  ...background ? { background } : {},
232566
232579
  ...memory ? { memory } : {},
232567
- ...isolation ? { isolation } : {}
232580
+ ...isolation ? { isolation } : {},
232581
+ ...cacheTtl !== undefined ? { cacheTtl } : {}
232568
232582
  };
232569
232583
  return agentDef;
232570
232584
  } catch (error52) {
@@ -472395,7 +472409,8 @@ async function* runAgent({
472395
472409
  userContext: resolvedUserContext,
472396
472410
  systemContext: resolvedSystemContext,
472397
472411
  toolUseContext: agentToolUseContext,
472398
- forkContextMessages: initialMessages
472412
+ forkContextMessages: initialMessages,
472413
+ ...agentDefinition.cacheTtl !== undefined ? { agentCacheTtlOverride: agentDefinition.cacheTtl } : {}
472399
472414
  });
472400
472415
  }
472401
472416
  recordSidechainTranscript(initialMessages, agentId).catch((_err) => logForDebugging(`Failed to record sidechain transcript: ${_err}`));
@@ -472422,7 +472437,8 @@ async function* runAgent({
472422
472437
  canUseTool,
472423
472438
  toolUseContext: agentToolUseContext,
472424
472439
  querySource,
472425
- maxTurns: maxTurns ?? agentDefinition.maxTurns
472440
+ maxTurns: maxTurns ?? agentDefinition.maxTurns,
472441
+ agentCacheTtlOverride: agentDefinition.cacheTtl
472426
472442
  })) {
472427
472443
  onQueryProgress?.();
472428
472444
  if (message.type === "stream_event" && message.event.type === "message_start" && message.ttftMs != null) {
@@ -597567,7 +597583,8 @@ async function* queryLoop(params, consumedCommandUuids) {
597567
597583
  fallbackModel,
597568
597584
  querySource,
597569
597585
  maxTurns,
597570
- skipCacheWrite
597586
+ skipCacheWrite,
597587
+ agentCacheTtlOverride
597571
597588
  } = params;
597572
597589
  const deps = params.deps ?? productionDeps();
597573
597590
  let state3 = {
@@ -597780,6 +597797,7 @@ async function* queryLoop(params, consumedCommandUuids) {
597780
597797
  effortValue: appState.effortValue,
597781
597798
  advisorModel: appState.advisorModel,
597782
597799
  skipCacheWrite,
597800
+ agentCacheTtlOverride,
597783
597801
  agentId: toolUseContext.agentId,
597784
597802
  addNotification: toolUseContext.addNotification,
597785
597803
  ...params.taskBudget && {
@@ -599250,7 +599268,8 @@ async function runForkedAgent({
599250
599268
  userContext,
599251
599269
  systemContext,
599252
599270
  toolUseContext,
599253
- forkContextMessages
599271
+ forkContextMessages,
599272
+ agentCacheTtlOverride
599254
599273
  } = cacheSafeParams;
599255
599274
  const isolatedToolUseContext = createSubagentContext(toolUseContext, overrides);
599256
599275
  const initialMessages = [...forkContextMessages, ...promptMessages];
@@ -599271,7 +599290,8 @@ async function runForkedAgent({
599271
599290
  querySource,
599272
599291
  maxOutputTokensOverride: maxOutputTokens,
599273
599292
  maxTurns,
599274
- skipCacheWrite
599293
+ skipCacheWrite,
599294
+ agentCacheTtlOverride
599275
599295
  })) {
599276
599296
  if (message.type === "stream_event") {
599277
599297
  if ("event" in message && message.event?.type === "message_delta" && message.event.usage) {
@@ -631305,11 +631325,12 @@ function getPromptCachingEnabled(model) {
631305
631325
  }
631306
631326
  function getCacheControl({
631307
631327
  scope,
631308
- querySource
631328
+ querySource,
631329
+ agentCacheTtlOverride
631309
631330
  } = {}) {
631310
631331
  return {
631311
631332
  type: "ephemeral",
631312
- ...should1hCacheTTL(querySource) && { ttl: "1h" },
631333
+ ...should1hCacheTTL(querySource, agentCacheTtlOverride) && { ttl: "1h" },
631313
631334
  ...scope === "global" && { scope }
631314
631335
  };
631315
631336
  }
@@ -631322,7 +631343,7 @@ function isMainThreadQuerySource(querySource) {
631322
631343
  function parsePromptCacheTtlEnv(value) {
631323
631344
  return value === "5m" || value === "1h" ? value : undefined;
631324
631345
  }
631325
- function resolvePromptCacheTtlOverride(querySource) {
631346
+ function resolvePromptCacheTtlOverride(querySource, agentCacheTtlOverride, isUsingOverage = false) {
631326
631347
  if (isEnvTruthy(process.env.FORCE_PROMPT_CACHING_5M)) {
631327
631348
  return { ttl: "5m", reason: "force_5m_env" };
631328
631349
  }
@@ -631334,16 +631355,21 @@ function resolvePromptCacheTtlOverride(querySource) {
631334
631355
  const settingTtl = isMainThread ? settings.promptCacheTtl : settings.subagentPromptCacheTtl;
631335
631356
  if (settingTtl !== undefined)
631336
631357
  return { ttl: settingTtl, reason: "setting" };
631358
+ if (agentCacheTtlOverride !== undefined && !(agentCacheTtlOverride === "1h" && isUsingOverage)) {
631359
+ return { ttl: agentCacheTtlOverride, reason: "agent_frontmatter" };
631360
+ }
631337
631361
  if (isEnvTruthy(process.env.ENABLE_PROMPT_CACHING_1H) || getAPIProvider() === "bedrock" && isEnvTruthy(process.env.ENABLE_PROMPT_CACHING_1H_BEDROCK)) {
631338
631362
  return { ttl: "1h", reason: "enable_1h_env" };
631339
631363
  }
631340
631364
  return;
631341
631365
  }
631342
- function resolvePromptCacheTtl(querySource) {
631343
- const override = resolvePromptCacheTtlOverride(querySource);
631366
+ function resolvePromptCacheTtl(querySource, options) {
631367
+ const isSubscriber2 = isClaudeAISubscriber();
631368
+ const isOverage = isSubscriber2 && options?.ignoreOverage !== true && currentLimits.isUsingOverage === true;
631369
+ const override = resolvePromptCacheTtlOverride(querySource, options?.agentCacheTtlOverride, isOverage);
631344
631370
  if (override !== undefined)
631345
631371
  return override;
631346
- if (!isClaudeAISubscriber() || currentLimits.isUsingOverage) {
631372
+ if (!isSubscriber2 || isOverage) {
631347
631373
  return { ttl: "5m", reason: "default" };
631348
631374
  }
631349
631375
  let allowlist = getPromptCache1hAllowlist();
@@ -631356,8 +631382,8 @@ function resolvePromptCacheTtl(querySource) {
631356
631382
  }
631357
631383
  return querySourceMatchesPatterns(querySource, allowlist) ? { ttl: "1h", reason: "subscriber" } : { ttl: "5m", reason: "default" };
631358
631384
  }
631359
- function should1hCacheTTL(querySource) {
631360
- return resolvePromptCacheTtl(querySource).ttl === "1h";
631385
+ function should1hCacheTTL(querySource, agentCacheTtlOverride) {
631386
+ return resolvePromptCacheTtl(querySource, { agentCacheTtlOverride }).ttl === "1h";
631361
631387
  }
631362
631388
  function configureEffortParams(effortValue, outputConfig, extraBodyParams, betas, model) {
631363
631389
  if (!modelSupportsEffort(model) || "effort" in outputConfig) {
@@ -631448,7 +631474,7 @@ async function verifyApiKey(apiKey, isNonInteractiveSession) {
631448
631474
  throw error52;
631449
631475
  }
631450
631476
  }
631451
- function userMessageToMessageParam(message, addCache = false, enablePromptCaching, querySource) {
631477
+ function userMessageToMessageParam(message, addCache = false, enablePromptCaching, querySource, agentCacheTtlOverride) {
631452
631478
  if (addCache) {
631453
631479
  if (typeof message.message.content === "string") {
631454
631480
  return {
@@ -631458,7 +631484,10 @@ function userMessageToMessageParam(message, addCache = false, enablePromptCachin
631458
631484
  type: "text",
631459
631485
  text: message.message.content,
631460
631486
  ...enablePromptCaching && {
631461
- cache_control: getCacheControl({ querySource })
631487
+ cache_control: getCacheControl({
631488
+ querySource,
631489
+ agentCacheTtlOverride
631490
+ })
631462
631491
  }
631463
631492
  }
631464
631493
  ]
@@ -631468,7 +631497,12 @@ function userMessageToMessageParam(message, addCache = false, enablePromptCachin
631468
631497
  role: "user",
631469
631498
  content: message.message.content.map((_4, i6) => ({
631470
631499
  ..._4,
631471
- ...i6 === message.message.content.length - 1 ? enablePromptCaching ? { cache_control: getCacheControl({ querySource }) } : {} : {}
631500
+ ...i6 === message.message.content.length - 1 ? enablePromptCaching ? {
631501
+ cache_control: getCacheControl({
631502
+ querySource,
631503
+ agentCacheTtlOverride
631504
+ })
631505
+ } : {} : {}
631472
631506
  }))
631473
631507
  };
631474
631508
  }
@@ -631478,7 +631512,7 @@ function userMessageToMessageParam(message, addCache = false, enablePromptCachin
631478
631512
  content: Array.isArray(message.message.content) ? [...message.message.content] : message.message.content
631479
631513
  };
631480
631514
  }
631481
- function assistantMessageToMessageParam(message, addCache = false, enablePromptCaching, querySource) {
631515
+ function assistantMessageToMessageParam(message, addCache = false, enablePromptCaching, querySource, agentCacheTtlOverride) {
631482
631516
  if (addCache) {
631483
631517
  if (typeof message.message.content === "string") {
631484
631518
  return {
@@ -631488,7 +631522,10 @@ function assistantMessageToMessageParam(message, addCache = false, enablePromptC
631488
631522
  type: "text",
631489
631523
  text: message.message.content,
631490
631524
  ...enablePromptCaching && {
631491
- cache_control: getCacheControl({ querySource })
631525
+ cache_control: getCacheControl({
631526
+ querySource,
631527
+ agentCacheTtlOverride
631528
+ })
631492
631529
  }
631493
631530
  }
631494
631531
  ]
@@ -631498,7 +631535,12 @@ function assistantMessageToMessageParam(message, addCache = false, enablePromptC
631498
631535
  role: "assistant",
631499
631536
  content: message.message.content.map((_4, i6) => ({
631500
631537
  ..._4,
631501
- ...i6 === message.message.content.length - 1 && _4.type !== "thinking" && _4.type !== "redacted_thinking" && (feature("CONNECTOR_TEXT") ? !isConnectorTextBlock(_4) : true) ? enablePromptCaching ? { cache_control: getCacheControl({ querySource }) } : {} : {}
631538
+ ...i6 === message.message.content.length - 1 && _4.type !== "thinking" && _4.type !== "redacted_thinking" && (feature("CONNECTOR_TEXT") ? !isConnectorTextBlock(_4) : true) ? enablePromptCaching ? {
631539
+ cache_control: getCacheControl({
631540
+ querySource,
631541
+ agentCacheTtlOverride
631542
+ })
631543
+ } : {} : {}
631502
631544
  }))
631503
631545
  };
631504
631546
  }
@@ -631720,7 +631762,7 @@ async function* queryModel(messages, systemPrompt, thinkingConfig, tools, signal
631720
631762
  queryCheckpoint("query_tool_schema_build_start");
631721
631763
  const isAgenticQuery = options.querySource.startsWith("repl_main_thread") || options.querySource.startsWith("agent:") || options.querySource === "sdk" || options.querySource === "hook_agent" || options.querySource === "verification_agent";
631722
631764
  const betas = getMergedBetas(options.model, { isAgenticQuery });
631723
- if (should1hCacheTTL(options.querySource) && shouldSendExtendedCacheTtlBeta() && !betas.includes(EXTENDED_CACHE_TTL_BETA_HEADER)) {
631765
+ if (should1hCacheTTL(options.querySource, options.agentCacheTtlOverride) && shouldSendExtendedCacheTtlBeta() && !betas.includes(EXTENDED_CACHE_TTL_BETA_HEADER)) {
631724
631766
  betas.push(EXTENDED_CACHE_TTL_BETA_HEADER);
631725
631767
  }
631726
631768
  if (isAdvisorEnabled()) {
@@ -631872,7 +631914,8 @@ ${deferredToolList}
631872
631914
  const enablePromptCaching = options.enablePromptCaching ?? getPromptCachingEnabled(options.model);
631873
631915
  const system = buildSystemPromptBlocks(systemPrompt, enablePromptCaching, {
631874
631916
  skipGlobalCacheForSystemPrompt: needsToolBasedCacheMarker,
631875
- querySource: options.querySource
631917
+ querySource: options.querySource,
631918
+ agentCacheTtlOverride: options.agentCacheTtlOverride
631876
631919
  });
631877
631920
  const useBetas = betas.length > 0;
631878
631921
  const extraToolSchemas = [...options.extraToolSchemas ?? []];
@@ -632027,7 +632070,7 @@ ${deferredToolList}
632027
632070
  lastRequestBetas = betasParams;
632028
632071
  return {
632029
632072
  model: normalizeModelStringForAPI(options.model),
632030
- messages: addCacheBreakpoints(messagesForAPI, enablePromptCaching2, options.querySource, useCachedMC, consumedCacheEdits, consumedPinnedEdits, options.skipCacheWrite),
632073
+ messages: addCacheBreakpoints(messagesForAPI, enablePromptCaching2, options.querySource, useCachedMC, consumedCacheEdits, consumedPinnedEdits, options.skipCacheWrite, options.agentCacheTtlOverride),
632031
632074
  system,
632032
632075
  tools: allTools,
632033
632076
  tool_choice: options.toolChoice,
@@ -632881,7 +632924,7 @@ function accumulateUsage(totalUsage, messageUsage) {
632881
632924
  function isToolResultBlock2(block) {
632882
632925
  return block !== null && typeof block === "object" && "type" in block && block.type === "tool_result" && "tool_use_id" in block;
632883
632926
  }
632884
- function addCacheBreakpoints(messages, enablePromptCaching, querySource, useCachedMC = false, newCacheEdits, pinnedEdits, skipCacheWrite = false) {
632927
+ function addCacheBreakpoints(messages, enablePromptCaching, querySource, useCachedMC = false, newCacheEdits, pinnedEdits, skipCacheWrite = false, agentCacheTtlOverride) {
632885
632928
  logEvent2("tengu_api_cache_breakpoints", {
632886
632929
  totalMessageCount: messages.length,
632887
632930
  cachingEnabled: enablePromptCaching,
@@ -632891,9 +632934,9 @@ function addCacheBreakpoints(messages, enablePromptCaching, querySource, useCach
632891
632934
  const result = messages.map((msg, index2) => {
632892
632935
  const addCache = index2 === markerIndex;
632893
632936
  if (msg.type === "user") {
632894
- return userMessageToMessageParam(msg, addCache, enablePromptCaching, querySource);
632937
+ return userMessageToMessageParam(msg, addCache, enablePromptCaching, querySource, agentCacheTtlOverride);
632895
632938
  }
632896
- return assistantMessageToMessageParam(msg, addCache, enablePromptCaching, querySource);
632939
+ return assistantMessageToMessageParam(msg, addCache, enablePromptCaching, querySource, agentCacheTtlOverride);
632897
632940
  });
632898
632941
  if (!useCachedMC) {
632899
632942
  return result;
@@ -632984,7 +633027,8 @@ function buildSystemPromptBlocks(systemPrompt, enablePromptCaching, options) {
632984
633027
  ...enablePromptCaching && block.cacheScope !== null && {
632985
633028
  cache_control: getCacheControl({
632986
633029
  scope: block.cacheScope,
632987
- querySource: options?.querySource
633030
+ querySource: options?.querySource,
633031
+ agentCacheTtlOverride: options?.agentCacheTtlOverride
632988
633032
  })
632989
633033
  }
632990
633034
  };
@@ -727155,21 +727199,6 @@ var init_execPromptHook = __esm(() => {
727155
727199
  init_hookHelpers();
727156
727200
  });
727157
727201
 
727158
- // src/utils/hooks/hookExit2Block.ts
727159
- function exit2BlockReason(params) {
727160
- const { status: status2, validationError, hasJson, stderr, command: command11 } = params;
727161
- if (status2 !== 2) {
727162
- return null;
727163
- }
727164
- if (hasJson && !validationError) {
727165
- return null;
727166
- }
727167
- return {
727168
- blockingError: `[${command11}]: ${stderr || "No stderr output"}`,
727169
- command: command11
727170
- };
727171
- }
727172
-
727173
727202
  // src/utils/hooks/execAgentHook.ts
727174
727203
  import { randomUUID as randomUUID40 } from "crypto";
727175
727204
  async function execAgentHook(hook, hookName, hookEvent, jsonInput, signal, toolUseContext, toolUseID, _messages, agentName) {
@@ -727774,10 +727803,16 @@ var init_execMcpToolHook = __esm(() => {
727774
727803
  // src/utils/hooks.ts
727775
727804
  var exports_hooks2 = {};
727776
727805
  __export(exports_hooks2, {
727806
+ wrapHookErrorWithStderr: () => wrapHookErrorWithStderr,
727777
727807
  skipFrontmatterHooksForUntrustedOrigin: () => skipFrontmatterHooksForUntrustedOrigin,
727778
727808
  shouldSkipHookDueToTrust: () => shouldSkipHookDueToTrust,
727779
727809
  processHookJSONOutput: () => processHookJSONOutput,
727810
+ parseHookOutput: () => parseHookOutput,
727811
+ looksLikeMissingHookScript: () => looksLikeMissingHookScript,
727780
727812
  isPerHookCallbackTimeout: () => isPerHookCallbackTimeout,
727813
+ isMultipleJsonDocuments: () => isMultipleJsonDocuments,
727814
+ isAsyncHookAnnouncement: () => isAsyncHookAnnouncement,
727815
+ hookOutputSchemaHint: () => hookOutputSchemaHint,
727781
727816
  hookCallbackTimeoutMessage: () => hookCallbackTimeoutMessage,
727782
727817
  hasWorktreeCreateHook: () => hasWorktreeCreateHook,
727783
727818
  hasInstructionsLoadedHook: () => hasInstructionsLoadedHook,
@@ -727790,6 +727825,7 @@ __export(exports_hooks2, {
727790
727825
  getSessionEndHookTimeoutMs: () => getSessionEndHookTimeoutMs,
727791
727826
  getPreToolHookBlockingMessage: () => getPreToolHookBlockingMessage,
727792
727827
  getMatchingHooks: () => getMatchingHooks,
727828
+ formatHookJsonValidationError: () => formatHookJsonValidationError,
727793
727829
  executeWorktreeRemoveHook: () => executeWorktreeRemoveHook,
727794
727830
  executeWorktreeCreateHook: () => executeWorktreeCreateHook,
727795
727831
  executeUserPromptSubmitHooks: () => executeUserPromptSubmitHooks,
@@ -727993,6 +728029,109 @@ function getSessionCronsForHookInput() {
727993
728029
  prompt: capHookString(task.prompt ?? "", HOOK_STRING_CAP)
727994
728030
  }));
727995
728031
  }
728032
+ function isHookJsonPlainObject(value) {
728033
+ return typeof value === "object" && value !== null && !Array.isArray(value);
728034
+ }
728035
+ function isHookJsonDiscriminatorIssue(issue2) {
728036
+ const last3 = issue2.path.at(-1);
728037
+ return issue2.code === "invalid_value" && typeof last3 === "string" && HOOK_JSON_DISCRIMINATOR_KEYS.has(last3);
728038
+ }
728039
+ function formatHookJsonIssue(issue2, parentPath) {
728040
+ const path39 = [...parentPath, ...issue2.path];
728041
+ if (issue2.code !== "invalid_union" || (issue2.errors?.length ?? 0) === 0) {
728042
+ return { path: path39, message: issue2.message.replace(/^Invalid input: /, "") };
728043
+ }
728044
+ const errors8 = issue2.errors;
728045
+ const relevant = errors8.find((branch2) => branch2.length > 0 && !branch2.some(isHookJsonDiscriminatorIssue))?.[0];
728046
+ if (relevant) {
728047
+ return formatHookJsonIssue(relevant, path39);
728048
+ }
728049
+ const discriminatorIssues = errors8.flat().filter(isHookJsonDiscriminatorIssue);
728050
+ const first2 = discriminatorIssues[0];
728051
+ if (first2?.code !== "invalid_value") {
728052
+ return { path: path39, message: issue2.message };
728053
+ }
728054
+ const values3 = [
728055
+ ...new Set(discriminatorIssues.flatMap((discriminator) => discriminator.code === "invalid_value" ? (discriminator.values ?? []).map((value) => jsonStringify(value)) : []))
728056
+ ];
728057
+ const preview = values3.slice(0, 6).join(" | ");
728058
+ return {
728059
+ path: [...path39, ...first2.path],
728060
+ message: `expected one of ${preview}${values3.length > 6 ? " | \u2026" : ""}`
728061
+ };
728062
+ }
728063
+ function formatHookJsonPath(path39) {
728064
+ return path39.map(String).join(".") || "(root)";
728065
+ }
728066
+ function formatHookJsonValidationError(parsed, issues) {
728067
+ const hookSpecificOutput = isHookJsonPlainObject(parsed) ? parsed.hookSpecificOutput : undefined;
728068
+ const formatted = issues.map((issue2) => formatHookJsonIssue(issue2, []));
728069
+ const first2 = formatted[0];
728070
+ let primary = first2 ? `${formatHookJsonPath(first2.path)}: ${first2.message}` : "unknown error";
728071
+ if (isHookJsonPlainObject(hookSpecificOutput) && !("hookEventName" in hookSpecificOutput)) {
728072
+ primary = 'hookSpecificOutput is missing required field "hookEventName"';
728073
+ } else if (isHookJsonPlainObject(hookSpecificOutput) && hookSpecificOutput.hookEventName === "PermissionRequest" && !isHookJsonPlainObject(hookSpecificOutput.decision) && first2?.path[0] === "hookSpecificOutput" && first2.path[1] === "decision") {
728074
+ primary += ' (PermissionRequest decision must be {"behavior": "allow"} or {"behavior": "deny", "message": "..."})';
728075
+ } else if (isHookJsonPlainObject(parsed) && first2?.path.length === 1 && first2.path[0] === "decision" && (parsed.decision === "allow" || parsed.decision === "deny" || parsed.decision === "ask")) {
728076
+ primary += parsed.decision === "ask" ? ' (top-level decision is the legacy approve|block field; for "ask" use hookSpecificOutput.permissionDecision in a PreToolUse hook)' : ` (top-level decision is the legacy approve|block field; for "${parsed.decision}" use hookSpecificOutput.permissionDecision in a PreToolUse hook, or hookSpecificOutput.decision: {"behavior": "${parsed.decision}"} in a PermissionRequest hook)`;
728077
+ }
728078
+ const rest = formatted.slice(1).map((entry) => ` - ${formatHookJsonPath(entry.path)}: ${entry.message}`).join(`
728079
+ `);
728080
+ return `${HOOK_JSON_VALIDATION_ERROR_PREFIX}${primary}${rest ? `
728081
+ ${rest}` : ""}
728082
+
728083
+ The hook's output was: ${jsonStringify(parsed, null, 2)}`;
728084
+ }
728085
+ function hookOutputSchemaHint() {
728086
+ return jsonStringify({
728087
+ continue: "boolean (optional)",
728088
+ suppressOutput: "boolean (optional)",
728089
+ stopReason: "string (optional)",
728090
+ decision: '"approve" | "block" (optional)',
728091
+ reason: "string (optional)",
728092
+ systemMessage: "string (optional)",
728093
+ terminalSequence: "string (optional)",
728094
+ hookSpecificOutput: {
728095
+ "for PreToolUse": {
728096
+ hookEventName: '"PreToolUse"',
728097
+ permissionDecision: '"allow" | "deny" | "ask" | "defer" (optional)',
728098
+ permissionDecisionReason: "string (optional)",
728099
+ updatedInput: "object (optional) - Modified tool input to use"
728100
+ },
728101
+ "for PermissionRequest": {
728102
+ hookEventName: '"PermissionRequest"',
728103
+ decision: {
728104
+ "to allow": {
728105
+ behavior: '"allow"',
728106
+ updatedInput: "object (optional) - Modified tool input to use",
728107
+ updatedPermissions: "array (optional) - Permission updates"
728108
+ },
728109
+ "to deny": {
728110
+ behavior: '"deny"',
728111
+ message: "string (optional)",
728112
+ interrupt: "boolean (optional)"
728113
+ }
728114
+ }
728115
+ },
728116
+ "for UserPromptSubmit": {
728117
+ hookEventName: '"UserPromptSubmit"',
728118
+ additionalContext: "string (optional)"
728119
+ },
728120
+ "for PostToolUse": {
728121
+ hookEventName: '"PostToolUse"',
728122
+ additionalContext: "string (optional)"
728123
+ },
728124
+ "for PostToolBatch": {
728125
+ hookEventName: '"PostToolBatch"',
728126
+ additionalContext: "string (optional)"
728127
+ },
728128
+ "for Stop / SubagentStop": {
728129
+ hookEventName: '"Stop" | "SubagentStop"',
728130
+ additionalContext: "string (optional) - Feedback for the model; the conversation continues so the model can act on it"
728131
+ }
728132
+ }
728133
+ }, null, 2);
728134
+ }
727996
728135
  function validateHookJson(jsonString) {
727997
728136
  const parsed = jsonParse(jsonString);
727998
728137
  const validation2 = hookJSONOutputSchema().safeParse(parsed);
@@ -728000,21 +728139,57 @@ function validateHookJson(jsonString) {
728000
728139
  logForDebugging("Successfully parsed and validated hook JSON output");
728001
728140
  return { json: validation2.data };
728002
728141
  }
728003
- const errors8 = validation2.error.issues.map((err2) => ` - ${err2.path.join(".")}: ${err2.message}`).join(`
728004
- `);
728005
728142
  return {
728006
- validationError: `Hook JSON output validation failed:
728007
- ${errors8}
728008
-
728009
- The hook's output was: ${jsonStringify(parsed, null, 2)}`
728143
+ validationError: formatHookJsonValidationError(parsed, validation2.error.issues)
728010
728144
  };
728011
728145
  }
728146
+ function isAsyncHookAnnouncement(stdout) {
728147
+ const firstLine = firstLineOf(stdout).trim();
728148
+ if (!firstLine.startsWith("{")) {
728149
+ return false;
728150
+ }
728151
+ try {
728152
+ const parsed = jsonParse(firstLine);
728153
+ return typeof parsed === "object" && parsed !== null && "async" in parsed && parsed.async === true;
728154
+ } catch {
728155
+ return false;
728156
+ }
728157
+ }
728158
+ function isMultipleJsonDocuments(output2) {
728159
+ const lines2 = output2.split(`
728160
+ `).filter((line) => line.trim() !== "");
728161
+ if (lines2.length < 2) {
728162
+ return false;
728163
+ }
728164
+ return lines2.every((line) => {
728165
+ try {
728166
+ const validation2 = hookJSONOutputSchema().safeParse(jsonParse(line.trim()));
728167
+ return !validation2.success || Object.keys(validation2.data).length === 0;
728168
+ } catch {
728169
+ return false;
728170
+ }
728171
+ });
728172
+ }
728173
+ function wrapHookErrorWithStderr(validationError, exitCode, stderr) {
728174
+ const trimmedStderr = stderr.trim();
728175
+ return exitCode !== 0 && trimmedStderr ? `${validationError}
728176
+
728177
+ Hook exited ${exitCode} with stderr:
728178
+ ${trimmedStderr}` : validationError;
728179
+ }
728180
+ function looksLikeMissingHookScript(params) {
728181
+ const { hookEvent, stdout, stderr, pluginId } = params;
728182
+ return (MISSING_SCRIPT_HOOK_EVENTS.has(hookEvent) || Boolean(pluginId) && hookEvent === "UserPromptSubmit") && !stdout.trim() && /no such file|can't open/i.test(stderr);
728183
+ }
728012
728184
  function parseHookOutput(stdout) {
728013
728185
  const trimmed = stdout.trim();
728014
728186
  if (!trimmed.startsWith("{")) {
728015
728187
  logForDebugging("Hook output does not start with {, treating as plain text");
728016
728188
  return { plainText: stdout };
728017
728189
  }
728190
+ if (isAsyncHookAnnouncement(stdout)) {
728191
+ return { json: { async: true } };
728192
+ }
728018
728193
  try {
728019
728194
  const result = validateHookJson(trimmed);
728020
728195
  if ("json" in result) {
@@ -728023,37 +728198,22 @@ function parseHookOutput(stdout) {
728023
728198
  const errorMessage3 = `${result.validationError}
728024
728199
 
728025
728200
  Expected schema:
728026
- ${jsonStringify({
728027
- continue: "boolean (optional)",
728028
- suppressOutput: "boolean (optional)",
728029
- stopReason: "string (optional)",
728030
- decision: '"approve" | "block" (optional)',
728031
- reason: "string (optional)",
728032
- systemMessage: "string (optional)",
728033
- permissionDecision: '"allow" | "deny" | "ask" (optional)',
728034
- hookSpecificOutput: {
728035
- "for PreToolUse": {
728036
- hookEventName: '"PreToolUse"',
728037
- permissionDecision: '"allow" | "deny" | "ask" (optional)',
728038
- permissionDecisionReason: "string (optional)",
728039
- updatedInput: "object (optional) - Modified tool input to use"
728040
- },
728041
- "for UserPromptSubmit": {
728042
- hookEventName: '"UserPromptSubmit"',
728043
- additionalContext: "string (required)",
728044
- sessionTitle: "string (optional) - Set the session title (same effect as /rename)"
728045
- },
728046
- "for PostToolUse": {
728047
- hookEventName: '"PostToolUse"',
728048
- additionalContext: "string (optional)"
728049
- }
728050
- }
728051
- }, null, 2)}`;
728201
+ ${hookOutputSchemaHint()}`;
728052
728202
  logForDebugging(errorMessage3);
728053
728203
  return { plainText: stdout, validationError: errorMessage3 };
728054
728204
  } catch (e4) {
728055
- logForDebugging(`Failed to parse hook output as JSON: ${e4}`);
728056
- return { plainText: stdout };
728205
+ const parseError = e4 instanceof Error ? e4.message : String(e4);
728206
+ if (!trimmed.endsWith("}")) {
728207
+ logForDebugging(`Hook output starts with { but is not a JSON object, treating as plain text: ${parseError}`);
728208
+ return { plainText: stdout };
728209
+ }
728210
+ if (isMultipleJsonDocuments(trimmed)) {
728211
+ logForDebugging("Hook output is several JSON documents, treating as plain text");
728212
+ return { plainText: stdout };
728213
+ }
728214
+ const errorMessage3 = `Hook output looks like a JSON object but is not valid JSON \u2014 ${parseError}. Emit the payload with a JSON encoder (jq, ConvertTo-Json, json.dumps) rather than string concatenation so backslashes and quotes inside strings are escaped.`;
728215
+ logForDebugging(errorMessage3);
728216
+ return { plainText: stdout, validationError: errorMessage3 };
728057
728217
  }
728058
728218
  }
728059
728219
  function parseHttpHookOutput(body) {
@@ -729212,7 +729372,7 @@ async function* executeHooks({
729212
729372
  hookEvent,
729213
729373
  output: httpResult.body,
729214
729374
  stdout: httpResult.body,
729215
- stderr: `JSON validation failed: ${httpValidationError}`,
729375
+ stderr: httpValidationError,
729216
729376
  exitCode: httpResult.statusCode,
729217
729377
  outcome: "error"
729218
729378
  });
@@ -729222,7 +729382,7 @@ async function* executeHooks({
729222
729382
  hookName,
729223
729383
  toolUseID,
729224
729384
  hookEvent,
729225
- stderr: `JSON validation failed: ${httpValidationError}`,
729385
+ stderr: httpValidationError,
729226
729386
  stdout: httpResult.body,
729227
729387
  exitCode: httpResult.statusCode ?? 0,
729228
729388
  command: hook.url,
@@ -729353,43 +729513,16 @@ async function* executeHooks({
729353
729513
  return;
729354
729514
  }
729355
729515
  const { json: json2, plainText, validationError } = parseHookOutput(result.stdout);
729356
- if (validationError) {
729357
- const exit2Block = exit2BlockReason({
729358
- status: result.status,
729359
- validationError,
729360
- hasJson: !!json2,
729361
- stderr: result.stderr,
729362
- command: hookCommand
729363
- });
729364
- if (exit2Block) {
729365
- emitHookResponse({
729366
- hookId,
729367
- hookName,
729368
- hookEvent,
729369
- output: result.output,
729370
- stdout: result.stdout,
729371
- stderr: result.stderr,
729372
- exitCode: result.status,
729373
- outcome: "error"
729374
- });
729375
- yield {
729376
- blockingError: {
729377
- blockingError: exit2Block.blockingError,
729378
- command: exit2Block.command
729379
- },
729380
- outcome: "blocking",
729381
- hook
729382
- };
729383
- return;
729384
- }
729516
+ if (validationError && result.status !== 2) {
729517
+ const stderr = wrapHookErrorWithStderr(validationError, result.status, result.stderr);
729385
729518
  emitHookResponse({
729386
729519
  hookId,
729387
729520
  hookName,
729388
729521
  hookEvent,
729389
729522
  output: result.output,
729390
729523
  stdout: result.stdout,
729391
- stderr: `JSON validation failed: ${validationError}`,
729392
- exitCode: 1,
729524
+ stderr,
729525
+ exitCode: result.status,
729393
729526
  outcome: "error"
729394
729527
  });
729395
729528
  yield {
@@ -729398,9 +729531,9 @@ async function* executeHooks({
729398
729531
  hookName,
729399
729532
  toolUseID,
729400
729533
  hookEvent,
729401
- stderr: `JSON validation failed: ${validationError}`,
729534
+ stderr,
729402
729535
  stdout: result.stdout,
729403
- exitCode: 1,
729536
+ exitCode: result.status,
729404
729537
  command: hookCommand,
729405
729538
  durationMs
729406
729539
  }),
@@ -729411,6 +729544,55 @@ async function* executeHooks({
729411
729544
  }
729412
729545
  if (json2) {
729413
729546
  if (isAsyncHookJSONOutput(json2)) {
729547
+ if (result.status === 2) {
729548
+ emitHookResponse({
729549
+ hookId,
729550
+ hookName,
729551
+ hookEvent,
729552
+ output: result.output,
729553
+ stdout: result.stdout,
729554
+ stderr: result.stderr,
729555
+ exitCode: result.status,
729556
+ outcome: "error"
729557
+ });
729558
+ yield {
729559
+ blockingError: {
729560
+ blockingError: `[${hook.command}]: ${result.stderr || "No stderr output"}`,
729561
+ command: hook.command
729562
+ },
729563
+ outcome: "blocking",
729564
+ hook
729565
+ };
729566
+ return;
729567
+ }
729568
+ if (result.status !== 0) {
729569
+ emitHookResponse({
729570
+ hookId,
729571
+ hookName,
729572
+ hookEvent,
729573
+ output: result.output,
729574
+ stdout: result.stdout,
729575
+ stderr: result.stderr,
729576
+ exitCode: result.status,
729577
+ outcome: "error"
729578
+ });
729579
+ yield {
729580
+ message: createAttachmentMessage({
729581
+ type: "hook_non_blocking_error",
729582
+ hookName,
729583
+ toolUseID,
729584
+ hookEvent,
729585
+ stderr: `Announced async, then failed with status code ${result.status}: ${result.stderr.trim() || "No stderr output"}`,
729586
+ stdout: result.stdout,
729587
+ exitCode: result.status,
729588
+ command: hookCommand,
729589
+ durationMs
729590
+ }),
729591
+ outcome: "non_blocking_error",
729592
+ hook
729593
+ };
729594
+ return;
729595
+ }
729414
729596
  yield {
729415
729597
  outcome: "success",
729416
729598
  hook
@@ -729513,6 +729695,39 @@ async function* executeHooks({
729513
729695
  };
729514
729696
  return;
729515
729697
  }
729698
+ if (result.status === 2 && looksLikeMissingHookScript({
729699
+ hookEvent,
729700
+ stdout: result.stdout,
729701
+ stderr: result.stderr,
729702
+ pluginId
729703
+ })) {
729704
+ emitHookResponse({
729705
+ hookId,
729706
+ hookName,
729707
+ hookEvent,
729708
+ output: result.output,
729709
+ stdout: result.stdout,
729710
+ stderr: result.stderr,
729711
+ exitCode: result.status,
729712
+ outcome: "error"
729713
+ });
729714
+ yield {
729715
+ message: createAttachmentMessage({
729716
+ type: "hook_non_blocking_error",
729717
+ hookName,
729718
+ toolUseID,
729719
+ hookEvent,
729720
+ stderr: `Hook script appears to be missing \u2014 "${hookCommand}" exited 2 with: ${result.stderr.trim()}. Treating as non-blocking. ${pluginId ? `Run \`/plugin\` to reinstall '${pluginId}' or remove it from settings.` : "If this is a plugin hook, check the plugin install (run /plugin)."}`,
729721
+ stdout: result.stdout,
729722
+ exitCode: result.status,
729723
+ command: hookCommand,
729724
+ durationMs
729725
+ }),
729726
+ outcome: "non_blocking_error",
729727
+ hook
729728
+ };
729729
+ return;
729730
+ }
729516
729731
  if (result.status === 2) {
729517
729732
  emitHookResponse({
729518
729733
  hookId,
@@ -729960,25 +730175,8 @@ async function executeHooksOutsideREPL({
729960
730175
  }
729961
730176
  logForDebugging(`${hookName} [${hook.command}] completed with status ${result.status}`);
729962
730177
  const { json: json2, validationError } = parseHookOutput(result.stdout);
729963
- if (validationError) {
729964
- const exit2Block = exit2BlockReason({
729965
- status: result.status,
729966
- validationError,
729967
- hasJson: !!json2,
729968
- stderr: result.stderr,
729969
- command: hook.command
729970
- });
729971
- if (exit2Block) {
729972
- return {
729973
- command: hook.command,
729974
- succeeded: false,
729975
- output: result.stderr || "",
729976
- blocked: true,
729977
- watchPaths: undefined,
729978
- systemMessage: undefined
729979
- };
729980
- }
729981
- throw new Error(validationError);
730178
+ if (validationError && result.status !== 2) {
730179
+ throw new Error(wrapHookErrorWithStderr(validationError, result.status, result.stderr));
729982
730180
  }
729983
730181
  if (json2 && !isAsyncHookJSONOutput(json2)) {
729984
730182
  logForDebugging(`Parsed JSON output from hook: ${jsonStringify(json2)}`, { level: "verbose" });
@@ -731008,7 +731206,7 @@ async function* executeMessageDisplayHooks(display, getAppState, agentId, signal
731008
731206
  timeoutMs
731009
731207
  });
731010
731208
  }
731011
- var TOOL_HOOK_EXECUTION_TIMEOUT_MS, PRE_TOOL_USE_HOOK_TIMEOUT_MESSAGE = "PreToolUse hook did not respond before its timeout (host client may be unreachable). The tool call was not executed; other configured hooks may not have completed.", SESSION_END_HOOK_TIMEOUT_MS_DEFAULT = 1500, BACKGROUND_TASK_TYPE_LABELS, HOOK_STRING_CAP = 1000, MATCHER_COMMA_HYPHEN_EVENTS;
731209
+ var TOOL_HOOK_EXECUTION_TIMEOUT_MS, PRE_TOOL_USE_HOOK_TIMEOUT_MESSAGE = "PreToolUse hook did not respond before its timeout (host client may be unreachable). The tool call was not executed; other configured hooks may not have completed.", SESSION_END_HOOK_TIMEOUT_MS_DEFAULT = 1500, BACKGROUND_TASK_TYPE_LABELS, HOOK_STRING_CAP = 1000, HOOK_JSON_VALIDATION_ERROR_PREFIX = "Hook JSON output validation failed \u2014 ", HOOK_JSON_DISCRIMINATOR_KEYS, MISSING_SCRIPT_HOOK_EVENTS, MATCHER_COMMA_HYPHEN_EVENTS;
731012
731210
  var init_hooks5 = __esm(() => {
731013
731211
  init_file();
731014
731212
  init_envValidation();
@@ -731071,6 +731269,13 @@ var init_hooks5 = __esm(() => {
731071
731269
  dream: "dream",
731072
731270
  remote_agent: "cloud session"
731073
731271
  };
731272
+ HOOK_JSON_DISCRIMINATOR_KEYS = new Set(["async", "hookEventName", "behavior"]);
731273
+ MISSING_SCRIPT_HOOK_EVENTS = new Set([
731274
+ "Stop",
731275
+ "SubagentStop",
731276
+ "TaskCompleted",
731277
+ "TeammateIdle"
731278
+ ]);
731074
731279
  MATCHER_COMMA_HYPHEN_EVENTS = new Set([
731075
731280
  "PreToolUse",
731076
731281
  "PostToolUse",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cnwenf/occ",
3
- "version": "2.1.314",
3
+ "version": "2.1.315",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {