oh-my-opencode 5.0.0-beta.51 → 5.0.0-beta.52

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 (92) hide show
  1. package/dist/cli/doctor/checks/index.d.ts +2 -0
  2. package/dist/cli/doctor/checks/latest-version.d.ts +11 -0
  3. package/dist/cli/doctor/framework/constants.d.ts +5 -0
  4. package/dist/cli/doctor/framework/types.d.ts +2 -0
  5. package/dist/cli/index.js +88 -37
  6. package/dist/cli-node/index.js +88 -37
  7. package/dist/features/background-agent/manager.d.ts +7 -0
  8. package/dist/features/background-agent/types.d.ts +4 -0
  9. package/dist/features/claude-tasks/storage.d.ts +7 -2
  10. package/dist/features/hook-message-injector/index.d.ts +1 -0
  11. package/dist/features/hook-message-injector/sdk-message-lookup.d.ts +1 -0
  12. package/dist/hooks/keyword-detector/hook.d.ts +2 -0
  13. package/dist/hooks/keyword-detector/ultrawork/index.d.ts +2 -0
  14. package/dist/hooks/unstable-agent-babysitter/task-message-analyzer.d.ts +1 -0
  15. package/dist/index.js +286 -109
  16. package/dist/plugin/chat-message/types.d.ts +3 -1
  17. package/dist/plugin/stop-continuation.d.ts +3 -0
  18. package/dist/plugin/system-transform.d.ts +5 -1
  19. package/dist/skills/frontend/ATTRIBUTION.md +10 -0
  20. package/dist/skills/frontend/SKILL.md +3 -0
  21. package/dist/skills/frontend/references/design/README.md +10 -2
  22. package/dist/skills/frontend/references/design/_INDEX.md +1 -0
  23. package/dist/skills/frontend/references/design/ambience-skill.md +145 -0
  24. package/dist/skills/frontend/references/design/interaction-skill.md +2 -0
  25. package/dist/tui.js +16 -7
  26. package/package.json +14 -14
  27. package/packages/lsp-daemon/dist/cli.js +1 -2
  28. package/packages/lsp-daemon/dist/ownership.js +2 -2
  29. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  30. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  33. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  34. package/packages/omo-codex/plugin/components/git-bash/dist/cli.js +1 -1
  35. package/packages/omo-codex/plugin/components/git-bash/dist/codex-hook.js +2 -1
  36. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  37. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  38. package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +2 -1
  39. package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +6 -7
  40. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  43. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  44. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  46. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  48. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  52. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  53. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  54. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  56. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  78. package/packages/omo-codex/plugin/package-lock.json +12 -12
  79. package/packages/omo-codex/plugin/package.json +1 -1
  80. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +10 -0
  81. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +3 -0
  82. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +10 -2
  83. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -0
  84. package/packages/omo-codex/plugin/skills/frontend/references/design/ambience-skill.md +145 -0
  85. package/packages/omo-codex/plugin/skills/frontend/references/design/interaction-skill.md +2 -0
  86. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  87. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +10 -0
  88. package/packages/shared-skills/skills/frontend/SKILL.md +3 -0
  89. package/packages/shared-skills/skills/frontend/references/design/README.md +10 -2
  90. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -0
  91. package/packages/shared-skills/skills/frontend/references/design/ambience-skill.md +145 -0
  92. package/packages/shared-skills/skills/frontend/references/design/interaction-skill.md +2 -0
package/dist/index.js CHANGED
@@ -23340,10 +23340,8 @@ function validationDiagnostic(path, issues) {
23340
23340
  function unrecognizedKeyIssues(issues) {
23341
23341
  return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
23342
23342
  }
23343
- function hasUnsafeUnrecognizedKey(parsed, issues) {
23344
- if (issues.some((issue) => issue.keys.some((key) => isUnsafeObjectKey2(key))))
23345
- return true;
23346
- return hasTamperedPrototype(parsed);
23343
+ function hasUnsafeUnrecognizedKey(issues) {
23344
+ return issues.some((issue) => issue.keys.some((key) => isUnsafeObjectKey2(key)));
23347
23345
  }
23348
23346
  function hasTamperedPrototype(value) {
23349
23347
  if (Array.isArray(value))
@@ -23416,11 +23414,17 @@ function readConfigSource(path, scope, fileSystem) {
23416
23414
  source: { exists: true, loaded: false, path, scope }
23417
23415
  };
23418
23416
  }
23417
+ if (hasTamperedPrototype(parsed.data)) {
23418
+ return {
23419
+ diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: "__proto__" member is not allowed`, path },
23420
+ source: { exists: true, loaded: false, path, scope }
23421
+ };
23422
+ }
23419
23423
  const parsedRecord = toRecord2(parsed.data);
23420
23424
  const validation = OmoConfigLayerSchema.safeParse(parsed.data);
23421
23425
  if (!validation.success) {
23422
23426
  const unrecognized = unrecognizedKeyIssues(validation.error.issues);
23423
- if (unrecognized.length > 0 && hasUnsafeUnrecognizedKey(parsed.data, unrecognized)) {
23427
+ if (hasUnsafeUnrecognizedKey(unrecognized)) {
23424
23428
  return {
23425
23429
  diagnostic: validationDiagnostic(path, validation.error.issues),
23426
23430
  source: { exists: true, loaded: false, path, scope }
@@ -82598,7 +82602,7 @@ function convertSDKMessageToStoredMessage(msg) {
82598
82602
  return null;
82599
82603
  const providerID = info.model?.providerID ?? info.providerID;
82600
82604
  const modelID = info.model?.modelID ?? info.modelID;
82601
- const variant = info.model?.variant;
82605
+ const variant = info.model?.variant ?? info.variant;
82602
82606
  if (!info.agent && !providerID && !modelID) {
82603
82607
  return null;
82604
82608
  }
@@ -82695,6 +82699,10 @@ async function resolveMessageContext(sessionID, client, messageDir) {
82695
82699
  // packages/omo-opencode/src/features/hook-message-injector/id-generation.ts
82696
82700
  import { randomBytes } from "crypto";
82697
82701
  var processPrefix = randomBytes(4).toString("hex");
82702
+ var partCounter = 0;
82703
+ function generatePartId() {
82704
+ return `prt_${processPrefix}_${String(++partCounter).padStart(6, "0")}`;
82705
+ }
82698
82706
  // packages/omo-opencode/src/shared/internal-initiator-marker.ts
82699
82707
  init_internal_initiator_marker();
82700
82708
 
@@ -86537,6 +86545,10 @@ var CHECK_NAMES = {
86537
86545
  [CHECK_IDS.TEAM_MODE]: "Team Mode",
86538
86546
  [CHECK_IDS.CODEX]: "Codex"
86539
86547
  };
86548
+ var UPDATE_COMMANDS = {
86549
+ opencode: `bunx ${PUBLISHED_PACKAGE_NAME} install`,
86550
+ codex: "npx lazycodex-ai install --no-tui --codex-autonomous"
86551
+ };
86540
86552
 
86541
86553
  // packages/omo-opencode/src/cli/doctor/checks/tui-plugin-config.ts
86542
86554
  var TUI_SUBPATH = "tui";
@@ -96387,7 +96399,7 @@ function getCachedVersion(options = {}) {
96387
96399
  // package.json
96388
96400
  var package_default = {
96389
96401
  name: "oh-my-opencode",
96390
- version: "5.0.0-beta.51",
96402
+ version: "5.0.0-beta.52",
96391
96403
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
96392
96404
  main: "./dist/index.js",
96393
96405
  types: "dist/index.d.ts",
@@ -96571,7 +96583,7 @@ var package_default = {
96571
96583
  zod: "^4.4.3"
96572
96584
  },
96573
96585
  devDependencies: {
96574
- "@code-yeongyu/senpi": "2026.9.9-2",
96586
+ "@code-yeongyu/senpi": "2026.9.10",
96575
96587
  "@oh-my-opencode/agents-md-core": "workspace:*",
96576
96588
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
96577
96589
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -96612,18 +96624,18 @@ var package_default = {
96612
96624
  typescript: "^7.0.2"
96613
96625
  },
96614
96626
  optionalDependencies: {
96615
- "oh-my-opencode-darwin-arm64": "5.0.0-beta.51",
96616
- "oh-my-opencode-darwin-x64": "5.0.0-beta.51",
96617
- "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.51",
96618
- "oh-my-opencode-linux-arm64": "5.0.0-beta.51",
96619
- "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.51",
96620
- "oh-my-opencode-linux-x64": "5.0.0-beta.51",
96621
- "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.51",
96622
- "oh-my-opencode-linux-x64-musl": "5.0.0-beta.51",
96623
- "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.51",
96624
- "oh-my-opencode-windows-arm64": "5.0.0-beta.51",
96625
- "oh-my-opencode-windows-x64": "5.0.0-beta.51",
96626
- "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.51"
96627
+ "oh-my-opencode-darwin-arm64": "5.0.0-beta.52",
96628
+ "oh-my-opencode-darwin-x64": "5.0.0-beta.52",
96629
+ "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.52",
96630
+ "oh-my-opencode-linux-arm64": "5.0.0-beta.52",
96631
+ "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.52",
96632
+ "oh-my-opencode-linux-x64": "5.0.0-beta.52",
96633
+ "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.52",
96634
+ "oh-my-opencode-linux-x64-musl": "5.0.0-beta.52",
96635
+ "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.52",
96636
+ "oh-my-opencode-windows-arm64": "5.0.0-beta.52",
96637
+ "oh-my-opencode-windows-x64": "5.0.0-beta.52",
96638
+ "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.52"
96627
96639
  },
96628
96640
  overrides: {
96629
96641
  "@earendil-works/pi-agent-core": "0.84.2",
@@ -103400,8 +103412,7 @@ function getDefaultUltraworkMessage() {
103400
103412
  return ULTRAWORK_DEFAULT_MESSAGE;
103401
103413
  }
103402
103414
  // packages/omo-opencode/src/hooks/keyword-detector/ultrawork/index.ts
103403
- function getUltraworkMessage(agentName, modelID) {
103404
- const source = getUltraworkSource(agentName, modelID);
103415
+ function getUltraworkMessageForSource(source) {
103405
103416
  switch (source) {
103406
103417
  case "planner":
103407
103418
  return getPlannerUltraworkMessage();
@@ -103416,6 +103427,9 @@ function getUltraworkMessage(agentName, modelID) {
103416
103427
  return getDefaultUltraworkMessage();
103417
103428
  }
103418
103429
  }
103430
+ function getUltraworkMessage(agentName, modelID) {
103431
+ return getUltraworkMessageForSource(getUltraworkSource(agentName, modelID));
103432
+ }
103419
103433
 
103420
103434
  // packages/omo-opencode/src/hooks/keyword-detector/team/default.ts
103421
103435
  var TEAM_PATTERN = /\bteam[\s_-]?mode\b/i;
@@ -103498,6 +103512,7 @@ function extractPromptText2(parts) {
103498
103512
  // packages/omo-opencode/src/hooks/keyword-detector/hook.ts
103499
103513
  var defaultModeUltraworkInjectedSessions = new Set;
103500
103514
  var DEFAULT_MODE_ULTRAWORK_SESSION_CAP = 256;
103515
+ var ULTRAWORK_CONTINUATION_MARKER = "<ultrawork-mode>active</ultrawork-mode>";
103501
103516
  function rememberDefaultModeUltraworkInjectedSession(sessionID) {
103502
103517
  if (defaultModeUltraworkInjectedSessions.has(sessionID))
103503
103518
  return;
@@ -103526,6 +103541,7 @@ function filterAlreadyInjectedKeywords(detected, text) {
103526
103541
  function createKeywordDetectorHook(ctx, _collector, _ralphLoop, config, defaultMode) {
103527
103542
  const disabledKeywords = config?.disabled_keywords;
103528
103543
  const enabledExpansions = config?.enabled_expansions;
103544
+ const explicitUltraworkSessions = new Map;
103529
103545
  function getRuntimeVariant(input, message) {
103530
103546
  if (typeof message.variant === "string") {
103531
103547
  return message.variant;
@@ -103538,12 +103554,15 @@ function createKeywordDetectorHook(ctx, _collector, _ralphLoop, config, defaultM
103538
103554
  log2(`[keyword-detector] Skipping synthetic/internal text message`, { sessionID: input.sessionID });
103539
103555
  return;
103540
103556
  }
103541
- const promptText = extractPromptText2(output.parts);
103557
+ const promptText = extractPromptText2(output.parts.filter(isRealUserTextPart));
103542
103558
  if (isSystemDirective(promptText)) {
103543
103559
  log2(`[keyword-detector] Skipping system directive message`, { sessionID: input.sessionID });
103544
103560
  return;
103545
103561
  }
103546
103562
  if (looksLikeSlashCommand(promptText)) {
103563
+ if (/^\s*\/stop-continuation(?:\s|$)/i.test(promptText)) {
103564
+ explicitUltraworkSessions.delete(input.sessionID);
103565
+ }
103547
103566
  log2(`[keyword-detector] Skipping slash command invocation`, { sessionID: input.sessionID });
103548
103567
  return;
103549
103568
  }
@@ -103553,8 +103572,17 @@ function createKeywordDetectorHook(ctx, _collector, _ralphLoop, config, defaultM
103553
103572
  return;
103554
103573
  }
103555
103574
  const cleanText = removeSystemReminders(promptText);
103556
- const modelID = input.model?.modelID;
103575
+ const selectedModel = output.message.model;
103576
+ const modelID = selectedModel !== null && typeof selectedModel === "object" && "modelID" in selectedModel && typeof selectedModel.modelID === "string" ? selectedModel.modelID : input.model?.modelID;
103577
+ const promptSource = getUltraworkSource(currentAgent, modelID);
103557
103578
  let detectedKeywords = detectKeywordsWithType(cleanText, currentAgent, modelID, disabledKeywords, enabledExpansions);
103579
+ const explicitUltrawork = detectedKeywords.some((k) => k.type === "ultrawork" || k.type === "hyperplan-ultrawork");
103580
+ const activeUltrawork = explicitUltraworkSessions.get(input.sessionID);
103581
+ const replayUltrawork = !explicitUltrawork && activeUltrawork !== undefined;
103582
+ const compactUltrawork = activeUltrawork !== undefined && !activeUltrawork.needsRestoration && activeUltrawork.source === promptSource;
103583
+ if (replayUltrawork) {
103584
+ detectedKeywords.push({ type: "ultrawork", message: getUltraworkMessageForSource(promptSource) });
103585
+ }
103558
103586
  detectedKeywords = suppressComboStandalones(detectedKeywords);
103559
103587
  if (isPlannerAgent(currentAgent)) {
103560
103588
  const preFilterCount = detectedKeywords.length;
@@ -103606,7 +103634,7 @@ function createKeywordDetectorHook(ctx, _collector, _ralphLoop, config, defaultM
103606
103634
  return;
103607
103635
  }
103608
103636
  const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork");
103609
- if (hasUltrawork) {
103637
+ if (hasUltrawork && explicitUltrawork) {
103610
103638
  const runtimeVariant = getRuntimeVariant(input, output.message);
103611
103639
  const isRuntimeMax = runtimeVariant === "max";
103612
103640
  log2(`[keyword-detector] Ultrawork mode activated`, {
@@ -103654,34 +103682,83 @@ function createKeywordDetectorHook(ctx, _collector, _ralphLoop, config, defaultM
103654
103682
  }
103655
103683
  }).catch((err) => log2(`[keyword-detector] Failed to show toast`, { error: err, sessionID: input.sessionID }));
103656
103684
  }
103657
- const textPartIndex = output.parts.findIndex(isRealUserTextPart);
103658
- if (textPartIndex === -1) {
103659
- log2(`[keyword-detector] No text part found, skipping injection`, { sessionID: input.sessionID });
103660
- return;
103661
- }
103662
- const allMessages = detectedKeywords.map((k) => k.message).join(`
103685
+ const allMessages = detectedKeywords.filter((k) => !(compactUltrawork && k.type === "ultrawork")).map((k) => k.message).join(`
103663
103686
 
103664
103687
  `);
103665
- const originalText = output.parts[textPartIndex].text ?? "";
103666
- output.parts[textPartIndex].text = `${originalText}
103688
+ const textPart = output.parts.find(isRealUserTextPart);
103689
+ const requiresFullGuidance = (hasUltrawork || hasHyperplanUltrawork) && !compactUltrawork && allMessages.length > 0;
103690
+ const messageID = input.messageID ?? (typeof output.message.id === "string" ? output.message.id : undefined);
103691
+ let fullGuidanceDurablyAdded = textPart !== undefined && requiresFullGuidance;
103692
+ if (textPart && allMessages) {
103693
+ textPart.text = `${textPart.text}
103667
103694
 
103668
103695
  ---
103669
103696
 
103670
103697
  ${allMessages}`;
103698
+ }
103699
+ if (!textPart && requiresFullGuidance && messageID) {
103700
+ output.parts.push({
103701
+ id: generatePartId(),
103702
+ sessionID: input.sessionID,
103703
+ messageID,
103704
+ type: "text",
103705
+ text: allMessages,
103706
+ synthetic: true
103707
+ });
103708
+ fullGuidanceDurablyAdded = true;
103709
+ }
103710
+ if (hasUltrawork && compactUltrawork && !output.parts.some((part) => part.synthetic === true && part.text === ULTRAWORK_CONTINUATION_MARKER) && messageID) {
103711
+ output.parts.push({
103712
+ id: generatePartId(),
103713
+ sessionID: input.sessionID,
103714
+ messageID,
103715
+ type: "text",
103716
+ text: ULTRAWORK_CONTINUATION_MARKER,
103717
+ synthetic: true
103718
+ });
103719
+ }
103720
+ if ((explicitUltrawork || replayUltrawork) && (hasUltrawork || hasHyperplanUltrawork) && (!requiresFullGuidance || fullGuidanceDurablyAdded)) {
103721
+ if (!explicitUltraworkSessions.has(input.sessionID) && explicitUltraworkSessions.size >= DEFAULT_MODE_ULTRAWORK_SESSION_CAP) {
103722
+ const oldest = explicitUltraworkSessions.keys().next().value;
103723
+ if (oldest !== undefined)
103724
+ explicitUltraworkSessions.delete(oldest);
103725
+ }
103726
+ explicitUltraworkSessions.set(input.sessionID, { source: promptSource, needsRestoration: false });
103727
+ }
103671
103728
  log2(`[keyword-detector] Detected ${detectedKeywords.length} keywords`, {
103672
103729
  sessionID: input.sessionID,
103673
103730
  types: detectedKeywords.map((k) => k.type)
103674
103731
  });
103675
103732
  },
103733
+ clearSession: (sessionID) => {
103734
+ explicitUltraworkSessions.delete(sessionID);
103735
+ },
103736
+ getSystemTransformGuidance: (sessionID, modelID) => {
103737
+ const activeUltrawork = explicitUltraworkSessions.get(sessionID);
103738
+ const agent = getSessionAgent(sessionID);
103739
+ if (!activeUltrawork?.needsRestoration || isPlannerAgent(agent) || isNonOmoAgent(agent) || subagentSessions.has(sessionID))
103740
+ return;
103741
+ return getUltraworkMessageForSource(modelID === undefined ? activeUltrawork.source : getUltraworkSource(agent, modelID));
103742
+ },
103676
103743
  event: ({ event }) => {
103677
- if (event.type !== "session.deleted")
103744
+ if (event.type !== "session.deleted" && event.type !== "session.compacted")
103678
103745
  return;
103679
103746
  const sessionID = resolveSessionEventID(event.properties);
103680
- if (sessionID)
103747
+ if (sessionID) {
103748
+ if (event.type === "session.compacted") {
103749
+ const activeUltrawork = explicitUltraworkSessions.get(sessionID);
103750
+ if (activeUltrawork) {
103751
+ explicitUltraworkSessions.set(sessionID, { ...activeUltrawork, needsRestoration: true });
103752
+ }
103753
+ return;
103754
+ }
103681
103755
  clearDefaultModeUltraworkInjectedSession(sessionID);
103756
+ explicitUltraworkSessions.delete(sessionID);
103757
+ }
103682
103758
  },
103683
103759
  dispose: () => {
103684
103760
  clearAllDefaultModeUltraworkInjectedSessions();
103761
+ explicitUltraworkSessions.clear();
103685
103762
  }
103686
103763
  };
103687
103764
  }
@@ -113354,7 +113431,14 @@ async function resolveRecentPromptContextForSession(ctx, sessionID, deps = defau
113354
113431
  };
113355
113432
  }
113356
113433
  if (info?.providerID && info?.modelID) {
113357
- return { model: { providerID: info.providerID, modelID: info.modelID }, tools };
113434
+ return {
113435
+ model: {
113436
+ providerID: info.providerID,
113437
+ modelID: info.modelID,
113438
+ ...info.variant ? { variant: info.variant } : {}
113439
+ },
113440
+ tools
113441
+ };
113358
113442
  }
113359
113443
  }
113360
113444
  } catch (error) {
@@ -116577,6 +116661,7 @@ function getMessageInfo(value) {
116577
116661
  model,
116578
116662
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
116579
116663
  modelID: typeof info.modelID === "string" ? info.modelID : undefined,
116664
+ variant: typeof info.variant === "string" ? info.variant : undefined,
116580
116665
  tools: isRecord(info.tools) ? Object.entries(info.tools).reduce((acc, [key, value]) => {
116581
116666
  if (value === true || value === false || value === "allow" || value === "deny" || value === "ask") {
116582
116667
  acc[key] = value;
@@ -116672,7 +116757,11 @@ async function resolveMainSessionTarget(ctx, sessionID) {
116672
116757
  const info = getMessageInfo(messages[i]);
116673
116758
  if (info?.agent || info?.model || info?.providerID && info?.modelID) {
116674
116759
  agent = agent ?? info?.agent;
116675
- model = info?.model ?? (info?.providerID && info?.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined);
116760
+ model = info?.model ?? (info?.providerID && info?.modelID ? {
116761
+ providerID: info.providerID,
116762
+ modelID: info.modelID,
116763
+ ...info.variant ? { variant: info.variant } : {}
116764
+ } : undefined);
116676
116765
  tools = resolveInheritedPromptTools(sessionID, info?.tools) ?? tools;
116677
116766
  break;
116678
116767
  }
@@ -129573,6 +129662,7 @@ async function executeSyncContinuation(args, ctx, executorCtx, parentContext, de
129573
129662
  throw new Error("task_id is required to continue a sync task");
129574
129663
  }
129575
129664
  cancelSyncSessionDeletion(continuationID);
129665
+ const detachFromManager = executorCtx.manager?.attachSyncContinuation?.(continuationID);
129576
129666
  const taskId = `resume_sync_${continuationID.slice(0, 8)}`;
129577
129667
  const startTime = new Date;
129578
129668
  if (toastManager) {
@@ -129644,6 +129734,7 @@ async function executeSyncContinuation(args, ctx, executorCtx, parentContext, de
129644
129734
  toastManager.removeTask(taskId);
129645
129735
  }
129646
129736
  const errorMessage = promptError instanceof Error ? promptError.message : String(promptError);
129737
+ detachFromManager?.();
129647
129738
  scheduleSyncSessionDeletion(client, continuationID);
129648
129739
  return `Failed to send continuation prompt: ${errorMessage}
129649
129740
 
@@ -129718,6 +129809,7 @@ ${buildTaskMetadataBlock({
129718
129809
  });
129719
129810
  }
129720
129811
  }
129812
+ detachFromManager?.();
129721
129813
  scheduleSyncSessionDeletion(client, continuationID);
129722
129814
  }
129723
129815
  }
@@ -131857,21 +131949,39 @@ function writeJsonAtomic(filePath, data) {
131857
131949
  }
131858
131950
  }
131859
131951
  var STALE_LOCK_THRESHOLD_MS = 30000;
131952
+ var DEFAULT_LOCK_WAIT_TIMEOUT_MS = 5000;
131953
+ var DEFAULT_LOCK_RETRY_DELAY_MS = 10;
131954
+ var MAX_LOCK_RETRY_DELAY_MS = 50;
131955
+ function resolveLockWaitTimeoutMs() {
131956
+ const raw = process.env.OMO_TASK_LOCK_WAIT_TIMEOUT_MS?.trim();
131957
+ if (!raw)
131958
+ return DEFAULT_LOCK_WAIT_TIMEOUT_MS;
131959
+ const parsed = Number(raw);
131960
+ if (!Number.isFinite(parsed) || parsed < 0)
131961
+ return DEFAULT_LOCK_WAIT_TIMEOUT_MS;
131962
+ return parsed;
131963
+ }
131964
+ function delay5(ms) {
131965
+ return new Promise((resolve) => {
131966
+ setTimeout(resolve, ms);
131967
+ });
131968
+ }
131860
131969
  function generateTaskId() {
131861
131970
  return `T-${randomUUID9()}`;
131862
131971
  }
131863
- function acquireLock2(dirPath) {
131972
+ async function acquireLock2(dirPath, options = {}) {
131864
131973
  const lockPath = join113(dirPath, ".lock");
131865
131974
  const lockId = randomUUID9();
131975
+ const waitTimeoutMs = options.waitTimeoutMs ?? resolveLockWaitTimeoutMs();
131976
+ const baseRetryDelayMs = options.retryDelayMs ?? DEFAULT_LOCK_RETRY_DELAY_MS;
131866
131977
  const createLock = (timestamp) => {
131867
131978
  writeFileSync22(lockPath, JSON.stringify({ id: lockId, timestamp }), {
131868
131979
  encoding: "utf-8",
131869
131980
  flag: "wx"
131870
131981
  });
131871
131982
  };
131872
- const isStale = () => {
131983
+ const isStale = (lockContent) => {
131873
131984
  try {
131874
- const lockContent = readFileSync62(lockPath, "utf-8");
131875
131985
  const lockData = JSON.parse(lockContent);
131876
131986
  const lockAge = Date.now() - lockData.timestamp;
131877
131987
  return lockAge > STALE_LOCK_THRESHOLD_MS;
@@ -131880,6 +131990,25 @@ function acquireLock2(dirPath) {
131880
131990
  return true;
131881
131991
  }
131882
131992
  };
131993
+ const reclaimStaleLock = () => {
131994
+ let snapshot;
131995
+ try {
131996
+ snapshot = readFileSync62(lockPath, "utf-8");
131997
+ } catch (error) {
131998
+ ignoreClaudeTaskStorageError(error);
131999
+ return true;
132000
+ }
132001
+ if (!isStale(snapshot))
132002
+ return false;
132003
+ try {
132004
+ if (readFileSync62(lockPath, "utf-8") !== snapshot)
132005
+ return false;
132006
+ unlinkSync16(lockPath);
132007
+ } catch (error) {
132008
+ ignoreClaudeTaskStorageError(error);
132009
+ }
132010
+ return true;
132011
+ };
131883
132012
  const tryAcquire = () => {
131884
132013
  const now = Date.now();
131885
132014
  try {
@@ -131892,15 +132021,27 @@ function acquireLock2(dirPath) {
131892
132021
  throw error;
131893
132022
  }
131894
132023
  };
132024
+ const nextDelayMs = (attempt, deadline) => {
132025
+ const cap = Math.max(MAX_LOCK_RETRY_DELAY_MS, baseRetryDelayMs);
132026
+ const step = Math.min(baseRetryDelayMs * 2 ** attempt, cap);
132027
+ const jittered = step / 2 + Math.random() * (step / 2);
132028
+ return Math.max(1, Math.min(jittered, deadline - Date.now()));
132029
+ };
131895
132030
  ensureDir(dirPath);
131896
- let acquired = tryAcquire();
131897
- if (!acquired && isStale()) {
131898
- try {
131899
- unlinkSync16(lockPath);
131900
- } catch (error) {
131901
- ignoreClaudeTaskStorageError(error);
132031
+ const deadline = Date.now() + waitTimeoutMs;
132032
+ let acquired = false;
132033
+ for (let attempt = 0;; attempt++) {
132034
+ if (tryAcquire()) {
132035
+ acquired = true;
132036
+ break;
132037
+ }
132038
+ if (reclaimStaleLock() && tryAcquire()) {
132039
+ acquired = true;
132040
+ break;
131902
132041
  }
131903
- acquired = tryAcquire();
132042
+ if (Date.now() >= deadline)
132043
+ break;
132044
+ await delay5(nextDelayMs(attempt, deadline));
131904
132045
  }
131905
132046
  if (!acquired) {
131906
132047
  return {
@@ -132059,10 +132200,11 @@ async function handleCreate(args, config, ctx, context) {
132059
132200
  try {
132060
132201
  const validatedArgs = TaskCreateInputSchema.parse(args);
132061
132202
  const taskDir = getTaskDir(config);
132062
- const lock = acquireLock2(taskDir);
132203
+ const lock = await acquireLock2(taskDir);
132063
132204
  if (!lock.acquired) {
132064
- return JSON.stringify({ error: "task_lock_unavailable" });
132205
+ return JSON.stringify({ error: "task_lock_unavailable", retryable: true });
132065
132206
  }
132207
+ let validatedTask;
132066
132208
  try {
132067
132209
  const taskId = generateTaskId();
132068
132210
  const task = {
@@ -132078,18 +132220,18 @@ async function handleCreate(args, config, ctx, context) {
132078
132220
  parentID: validatedArgs.parentID,
132079
132221
  threadID: context.sessionID
132080
132222
  };
132081
- const validatedTask = TaskObjectSchema.parse(task);
132223
+ validatedTask = TaskObjectSchema.parse(task);
132082
132224
  writeJsonAtomic(join114(taskDir, `${taskId}.json`), validatedTask);
132083
- await syncTaskTodoUpdate(ctx, validatedTask, context.sessionID);
132084
- return JSON.stringify({
132085
- task: {
132086
- id: validatedTask.id,
132087
- subject: validatedTask.subject
132088
- }
132089
- });
132090
132225
  } finally {
132091
132226
  lock.release();
132092
132227
  }
132228
+ await syncTaskTodoUpdate(ctx, validatedTask, context.sessionID);
132229
+ return JSON.stringify({
132230
+ task: {
132231
+ id: validatedTask.id,
132232
+ subject: validatedTask.subject
132233
+ }
132234
+ });
132093
132235
  } catch (error) {
132094
132236
  if (error instanceof Error && error.message.includes("Required")) {
132095
132237
  return JSON.stringify({
@@ -132223,6 +132365,44 @@ Properly managed dependencies enable maximum parallel execution.`,
132223
132365
  }
132224
132366
  });
132225
132367
  }
132368
+ function applyTaskUpdate(taskPath, input) {
132369
+ const task = readJsonSafe(taskPath, TaskObjectSchema);
132370
+ if (!task) {
132371
+ return null;
132372
+ }
132373
+ if (input.subject !== undefined) {
132374
+ task.subject = input.subject;
132375
+ }
132376
+ if (input.description !== undefined) {
132377
+ task.description = input.description;
132378
+ }
132379
+ if (input.status !== undefined) {
132380
+ task.status = input.status;
132381
+ }
132382
+ if (input.activeForm !== undefined) {
132383
+ task.activeForm = input.activeForm;
132384
+ }
132385
+ if (input.owner !== undefined) {
132386
+ task.owner = input.owner;
132387
+ }
132388
+ if (input.addBlocks) {
132389
+ task.blocks = [...new Set([...task.blocks, ...input.addBlocks])];
132390
+ }
132391
+ if (input.addBlockedBy) {
132392
+ task.blockedBy = [...new Set([...task.blockedBy, ...input.addBlockedBy])];
132393
+ }
132394
+ if (input.metadata !== undefined) {
132395
+ task.metadata = { ...task.metadata, ...input.metadata };
132396
+ Object.keys(task.metadata).forEach((key) => {
132397
+ if (task.metadata?.[key] === null) {
132398
+ delete task.metadata[key];
132399
+ }
132400
+ });
132401
+ }
132402
+ const updatedTask = TaskObjectSchema.parse(task);
132403
+ writeJsonAtomic(taskPath, updatedTask);
132404
+ return updatedTask;
132405
+ }
132226
132406
  async function handleUpdate(args, config, ctx, context) {
132227
132407
  try {
132228
132408
  const validatedArgs = TaskUpdateInputSchema.parse(args);
@@ -132231,54 +132411,21 @@ async function handleUpdate(args, config, ctx, context) {
132231
132411
  return JSON.stringify({ error: "invalid_task_id" });
132232
132412
  }
132233
132413
  const taskDir = getTaskDir(config);
132234
- const lock = acquireLock2(taskDir);
132414
+ const lock = await acquireLock2(taskDir);
132235
132415
  if (!lock.acquired) {
132236
- return JSON.stringify({ error: "task_lock_unavailable" });
132416
+ return JSON.stringify({ error: "task_lock_unavailable", retryable: true });
132237
132417
  }
132418
+ let updatedTask = null;
132238
132419
  try {
132239
- const taskPath = join117(taskDir, `${taskId}.json`);
132240
- const task = readJsonSafe(taskPath, TaskObjectSchema);
132241
- if (!task) {
132242
- return JSON.stringify({ error: "task_not_found" });
132243
- }
132244
- if (validatedArgs.subject !== undefined) {
132245
- task.subject = validatedArgs.subject;
132246
- }
132247
- if (validatedArgs.description !== undefined) {
132248
- task.description = validatedArgs.description;
132249
- }
132250
- if (validatedArgs.status !== undefined) {
132251
- task.status = validatedArgs.status;
132252
- }
132253
- if (validatedArgs.activeForm !== undefined) {
132254
- task.activeForm = validatedArgs.activeForm;
132255
- }
132256
- if (validatedArgs.owner !== undefined) {
132257
- task.owner = validatedArgs.owner;
132258
- }
132259
- const addBlocks = args.addBlocks;
132260
- if (addBlocks) {
132261
- task.blocks = [...new Set([...task.blocks, ...addBlocks])];
132262
- }
132263
- const addBlockedBy = args.addBlockedBy;
132264
- if (addBlockedBy) {
132265
- task.blockedBy = [...new Set([...task.blockedBy, ...addBlockedBy])];
132266
- }
132267
- if (validatedArgs.metadata !== undefined) {
132268
- task.metadata = { ...task.metadata, ...validatedArgs.metadata };
132269
- Object.keys(task.metadata).forEach((key) => {
132270
- if (task.metadata?.[key] === null) {
132271
- delete task.metadata[key];
132272
- }
132273
- });
132274
- }
132275
- const validatedTask = TaskObjectSchema.parse(task);
132276
- writeJsonAtomic(taskPath, validatedTask);
132277
- await syncTaskTodoUpdate(ctx, validatedTask, context.sessionID);
132278
- return JSON.stringify({ task: validatedTask });
132420
+ updatedTask = applyTaskUpdate(join117(taskDir, `${taskId}.json`), validatedArgs);
132279
132421
  } finally {
132280
132422
  lock.release();
132281
132423
  }
132424
+ if (!updatedTask) {
132425
+ return JSON.stringify({ error: "task_not_found" });
132426
+ }
132427
+ await syncTaskTodoUpdate(ctx, updatedTask, context.sessionID);
132428
+ return JSON.stringify({ task: updatedTask });
132282
132429
  } catch (error) {
132283
132430
  if (error instanceof Error && error.message.includes("Required")) {
132284
132431
  return JSON.stringify({
@@ -134858,6 +135005,7 @@ async function tryFallbackRetry(args) {
134858
135005
  sessionPermission: task.sessionPermission,
134859
135006
  category: task.category,
134860
135007
  isUnstableAgent: task.isUnstableAgent,
135008
+ cwd: task.cwd,
134861
135009
  onSessionCreated: task.onSessionCreated
134862
135010
  };
134863
135011
  if (previousSessionID) {
@@ -137330,6 +137478,7 @@ class BackgroundManager {
137330
137478
  queuesByKey = new Map;
137331
137479
  processingKeys = new Set;
137332
137480
  completionTimers = new Map;
137481
+ syncAttachedSessions = new Set;
137333
137482
  completedTaskArchive = new Map;
137334
137483
  completedTaskSummaries = new Map;
137335
137484
  idleDeferralTimers = new Map;
@@ -137636,6 +137785,7 @@ class BackgroundManager {
137636
137785
  sessionPermission: input.sessionPermission,
137637
137786
  attemptCount: 0,
137638
137787
  category: input.category,
137788
+ cwd: input.cwd,
137639
137789
  onSessionCreated: input.onSessionCreated
137640
137790
  };
137641
137791
  const firstAttempt = startAttempt(task, input.model);
@@ -137750,7 +137900,8 @@ class BackgroundManager {
137750
137900
  return null;
137751
137901
  });
137752
137902
  const parentDirectory = parentSession?.data?.directory ?? this.directory;
137753
- log2(`[background-agent] Parent dir: ${parentSession?.data?.directory}, using: ${parentDirectory}`);
137903
+ const childDirectory = input.cwd ?? parentDirectory;
137904
+ log2(`[background-agent] Parent dir: ${parentSession?.data?.directory}, using: ${childDirectory}`);
137754
137905
  const createResult = await this.client.session.create({
137755
137906
  body: {
137756
137907
  parentID: input.parentSessionId,
@@ -137765,7 +137916,7 @@ class BackgroundManager {
137765
137916
  } : {}
137766
137917
  },
137767
137918
  query: {
137768
- directory: parentDirectory
137919
+ directory: childDirectory
137769
137920
  }
137770
137921
  });
137771
137922
  if (createResult.error) {
@@ -137816,7 +137967,7 @@ class BackgroundManager {
137816
137967
  task.concurrencyGroup = concurrencyKey;
137817
137968
  if (task.retryNotification) {
137818
137969
  const attemptNumber = boundAttempt.attemptNumber;
137819
- const retrySessionUrl = buildLocalSessionUrl(parentDirectory, sessionID);
137970
+ const retrySessionUrl = buildLocalSessionUrl(childDirectory, sessionID);
137820
137971
  const previousAttempt = getPreviousAttempt(task, boundAttempt.attemptId);
137821
137972
  const failedSessionID = previousAttempt?.sessionId ?? task.retryNotification.previousSessionID;
137822
137973
  const failedSessionLine = failedSessionID ? `
@@ -137901,7 +138052,7 @@ The fallback retry session is now created and can be inspected directly.
137901
138052
  promptWithRetryInDirectory(this.client, {
137902
138053
  path: { id: sessionID },
137903
138054
  body: promptBody
137904
- }, parentDirectory).catch(async (error) => {
138055
+ }, childDirectory).catch(async (error) => {
137905
138056
  if (isAgentNotFoundError(error) && input.agent !== FALLBACK_AGENT) {
137906
138057
  log2("[background-agent] Agent not found, retrying with fallback agent", {
137907
138058
  original: input.agent,
@@ -137927,7 +138078,7 @@ The fallback retry session is now created and can be inspected directly.
137927
138078
  await promptWithRetryInDirectory(this.client, {
137928
138079
  path: { id: sessionID },
137929
138080
  body: fallbackBody
137930
- }, parentDirectory);
138081
+ }, childDirectory);
137931
138082
  task.agent = FALLBACK_AGENT;
137932
138083
  return;
137933
138084
  } catch (retryError) {
@@ -138057,6 +138208,19 @@ The fallback retry session is now created and can be inspected directly.
138057
138208
  }
138058
138209
  return;
138059
138210
  }
138211
+ attachSyncContinuation(sessionID) {
138212
+ this.syncAttachedSessions.add(sessionID);
138213
+ return () => {
138214
+ if (!this.syncAttachedSessions.delete(sessionID))
138215
+ return;
138216
+ const task = this.findBySession(sessionID);
138217
+ if (!task || !TERMINAL_BACKGROUND_TASK_STATUSES.has(task.status))
138218
+ return;
138219
+ if (this.completionTimers.has(task.id))
138220
+ return;
138221
+ this.scheduleTaskRemoval(task.id);
138222
+ };
138223
+ }
138060
138224
  resolveTaskAttemptBySession(sessionID) {
138061
138225
  const task = this.findBySession(sessionID);
138062
138226
  if (!task) {
@@ -138247,7 +138411,7 @@ The fallback retry session is now created and can be inspected directly.
138247
138411
  })(),
138248
138412
  parts: [createInternalAgentTextPart(input.prompt)]
138249
138413
  },
138250
- query: { directory: this.directory }
138414
+ query: { directory: existingTask.cwd ?? this.directory }
138251
138415
  }
138252
138416
  }).then((promptResult) => {
138253
138417
  if (promptResult.status === "failed") {
@@ -138984,6 +139148,8 @@ The task was re-queued on a fallback model after a retryable failure.
138984
139148
  const task = this.tasks.get(taskId);
138985
139149
  if (!task)
138986
139150
  return;
139151
+ if (task.sessionId && this.syncAttachedSessions.has(task.sessionId))
139152
+ return;
138987
139153
  if (task.parentSessionId) {
138988
139154
  const siblings = this.getTasksByParentSession(task.parentSessionId);
138989
139155
  const runningOrPendingSiblings = siblings.filter((sibling) => sibling.id !== taskId && (sibling.status === "running" || sibling.status === "pending"));
@@ -139622,6 +139788,7 @@ The task was re-queued on a fallback model after a retryable failure.
139622
139788
  clearTimeout(timer);
139623
139789
  }
139624
139790
  this.completionTimers.clear();
139791
+ this.syncAttachedSessions.clear();
139625
139792
  for (const timer of this.idleDeferralTimers.values()) {
139626
139793
  clearTimeout(timer);
139627
139794
  }
@@ -169753,6 +169920,7 @@ async function createTeamRun(spec, leadSessionId, ctx, config, bgMgr, tmuxMgr, o
169753
169920
  skillContent: resolvedMember.systemContent,
169754
169921
  category: member.kind === "category" ? member.category : undefined,
169755
169922
  sessionPermission: QUESTION_DENIED_SESSION_PERMISSION,
169923
+ ...resource.worktreePath ? { cwd: resource.worktreePath } : {},
169756
169924
  onSessionCreated: async (sessionId) => {
169757
169925
  registerTeamSession(sessionId, {
169758
169926
  teamRunId: runtimeState.teamRunId,
@@ -171589,6 +171757,7 @@ init_logger2();
171589
171757
  // packages/omo-opencode/src/plugin/stop-continuation.ts
171590
171758
  function stopContinuation(args) {
171591
171759
  const { directory, hooks, sessionID } = args;
171760
+ hooks.keywordDetector?.clearSession?.(sessionID);
171592
171761
  hooks.stopContinuationGuard?.stop?.(sessionID);
171593
171762
  hooks.todoContinuationEnforcer?.cancelAllCountdowns();
171594
171763
  hooks.goal?.clearGoal(sessionID);
@@ -171928,9 +172097,16 @@ function toCanonicalModel(model) {
171928
172097
  return model.id;
171929
172098
  return `${model.providerID}/${model.id}`;
171930
172099
  }
171931
- function createSystemTransformHandler(defaultMode, getUltraworkMessage) {
172100
+ function createSystemTransformHandler(defaultMode, getUltraworkMessage, ultraworkRestoration) {
171932
172101
  return async (input, output) => {
171933
172102
  reconcileSisyphusRuntimePrompt(output.system, toCanonicalModel(input.model));
172103
+ const restoredGuidance = input.sessionID ? ultraworkRestoration?.getSystemTransformGuidance?.(input.sessionID, input.model?.id) : undefined;
172104
+ if (restoredGuidance) {
172105
+ if (!output.system.some((part) => part.includes(ULTRAWORK_MODE_TAG))) {
172106
+ output.system.push(restoredGuidance);
172107
+ }
172108
+ return;
172109
+ }
171934
172110
  if (!defaultMode?.ultrawork || !getUltraworkMessage)
171935
172111
  return;
171936
172112
  if (output.system.some((part) => part.includes(ULTRAWORK_MODE_TAG)))
@@ -172103,6 +172279,7 @@ function createEventHookDispatcher(hooks, runEventHookSafely) {
172103
172279
  await runEventHookSafely("directoryAgentsInjector", hooks.directoryAgentsInjector?.event, input);
172104
172280
  await runEventHookSafely("directoryReadmeInjector", hooks.directoryReadmeInjector?.event, input);
172105
172281
  await runEventHookSafely("rulesInjector", hooks.rulesInjector?.event, input);
172282
+ await runEventHookSafely("keywordDetector", hooks.keywordDetector?.event, input);
172106
172283
  await runEventHookSafely("hephaestusAgentsMdInjector", hooks.hephaestusAgentsMdInjector?.event, input);
172107
172284
  await runEventHookSafely("thinkMode", hooks.thinkMode?.event, input);
172108
172285
  await runEventHookSafely("anthropicContextWindowLimitRecovery", hooks.anthropicContextWindowLimitRecovery?.event, input);
@@ -173663,7 +173840,7 @@ function createPluginInterface(args) {
173663
173840
  "experimental.chat.messages.transform": createMessagesTransformHandler({
173664
173841
  hooks
173665
173842
  }),
173666
- "experimental.chat.system.transform": createSystemTransformHandler(pluginConfig.default_mode, getUltraworkMessage),
173843
+ "experimental.chat.system.transform": createSystemTransformHandler(pluginConfig.default_mode, getUltraworkMessage, hooks.keywordDetector),
173667
173844
  config: managers.configHandler,
173668
173845
  event: createEventHandler2({
173669
173846
  ctx,
@@ -174410,7 +174587,7 @@ async function killTargets(targets, options, config) {
174410
174587
  const terminated = await safelyTerminate(target.pid, context);
174411
174588
  if (!terminated)
174412
174589
  continue;
174413
- await delay5(options.graceMs ?? DEFAULT_GRACE_MS);
174590
+ await delay6(options.graceMs ?? DEFAULT_GRACE_MS);
174414
174591
  if (!await context.killer.isAlive(target.pid)) {
174415
174592
  killed.push(target);
174416
174593
  continue;
@@ -174469,7 +174646,7 @@ function writeSweepStamp(stampFile, nowMs) {
174469
174646
  const stampDate = new Date(nowMs);
174470
174647
  utimesSync(stampFile, stampDate, stampDate);
174471
174648
  }
174472
- function delay5(ms) {
174649
+ function delay6(ms) {
174473
174650
  if (ms <= 0)
174474
174651
  return Promise.resolve();
174475
174652
  return new Promise((resolvePromise) => {