@bastani/atomic 0.8.28-alpha.2 → 0.8.28-alpha.4

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 (117) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/package.json +1 -1
  5. package/dist/builtin/web-access/package.json +1 -1
  6. package/dist/builtin/workflows/CHANGELOG.md +24 -0
  7. package/dist/builtin/workflows/README.md +1 -1
  8. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +2 -1
  9. package/dist/builtin/workflows/builtin/goal.ts +2 -1
  10. package/dist/builtin/workflows/builtin/open-claude-design.ts +2 -1
  11. package/dist/builtin/workflows/builtin/ralph.ts +4 -2
  12. package/dist/builtin/workflows/package.json +1 -1
  13. package/dist/builtin/workflows/src/authoring.d.ts +5 -2
  14. package/dist/builtin/workflows/src/extension/dispatcher.ts +2 -0
  15. package/dist/builtin/workflows/src/extension/index.ts +8 -0
  16. package/dist/builtin/workflows/src/extension/render-result.ts +5 -2
  17. package/dist/builtin/workflows/src/extension/workflow-schema.ts +18 -0
  18. package/dist/builtin/workflows/src/runs/background/status.ts +4 -0
  19. package/dist/builtin/workflows/src/runs/foreground/executor.ts +1251 -110
  20. package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +34 -10
  21. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +10 -2
  22. package/dist/builtin/workflows/src/shared/persistence-restore.ts +28 -9
  23. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +9 -3
  24. package/dist/builtin/workflows/src/shared/store-types.ts +10 -3
  25. package/dist/builtin/workflows/src/shared/store.ts +29 -7
  26. package/dist/builtin/workflows/src/shared/types.ts +12 -10
  27. package/dist/builtin/workflows/src/tui/run-detail.ts +12 -0
  28. package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
  29. package/dist/builtin/workflows/src/tui/status-list.ts +15 -1
  30. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +1 -1
  31. package/dist/builtin/workflows/src/tui/widget.ts +12 -3
  32. package/dist/builtin/workflows/src/workflows/define-workflow.ts +3 -3
  33. package/dist/core/agent-session-services.d.ts +1 -0
  34. package/dist/core/agent-session-services.d.ts.map +1 -1
  35. package/dist/core/agent-session-services.js +1 -0
  36. package/dist/core/agent-session-services.js.map +1 -1
  37. package/dist/core/agent-session.d.ts +4 -0
  38. package/dist/core/agent-session.d.ts.map +1 -1
  39. package/dist/core/agent-session.js +12 -1
  40. package/dist/core/agent-session.js.map +1 -1
  41. package/dist/core/index.d.ts +1 -0
  42. package/dist/core/index.d.ts.map +1 -1
  43. package/dist/core/index.js +1 -0
  44. package/dist/core/index.js.map +1 -1
  45. package/dist/core/sdk.d.ts +4 -2
  46. package/dist/core/sdk.d.ts.map +1 -1
  47. package/dist/core/sdk.js +1 -0
  48. package/dist/core/sdk.js.map +1 -1
  49. package/dist/core/tools/ask-user-question/state/inline-input.d.ts +28 -0
  50. package/dist/core/tools/ask-user-question/state/inline-input.d.ts.map +1 -0
  51. package/dist/core/tools/ask-user-question/state/inline-input.js +56 -0
  52. package/dist/core/tools/ask-user-question/state/inline-input.js.map +1 -0
  53. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  54. package/dist/core/tools/ask-user-question/state/key-router.js +30 -4
  55. package/dist/core/tools/ask-user-question/state/key-router.js.map +1 -1
  56. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  57. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +9 -8
  58. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  59. package/dist/core/tools/ask-user-question/state/row-intent.d.ts +3 -2
  60. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
  61. package/dist/core/tools/ask-user-question/state/row-intent.js +1 -1
  62. package/dist/core/tools/ask-user-question/state/row-intent.js.map +1 -1
  63. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +2 -0
  64. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  65. package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
  66. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  67. package/dist/core/tools/ask-user-question/state/selectors/projections.js +2 -0
  68. package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
  69. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  70. package/dist/core/tools/ask-user-question/state/state-reducer.js +36 -24
  71. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  72. package/dist/core/tools/ask-user-question/state/state.d.ts +8 -0
  73. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  74. package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
  75. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts +6 -0
  76. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  77. package/dist/core/tools/ask-user-question/tool/format-answer.js +19 -1
  78. package/dist/core/tools/ask-user-question/tool/format-answer.js.map +1 -1
  79. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts +3 -2
  80. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  81. package/dist/core/tools/ask-user-question/tool/response-envelope.js +15 -3
  82. package/dist/core/tools/ask-user-question/tool/response-envelope.js.map +1 -1
  83. package/dist/core/tools/ask-user-question/tool/types.d.ts +2 -1
  84. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
  85. package/dist/core/tools/ask-user-question/tool/types.js.map +1 -1
  86. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts +5 -2
  87. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  88. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js +2 -0
  89. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -1
  90. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +1 -0
  91. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  92. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +2 -1
  93. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  94. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts +3 -3
  95. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  96. package/dist/core/tools/ask-user-question/view/props-adapter.js +11 -4
  97. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
  98. package/dist/core/tools/bash-policy.d.ts +62 -0
  99. package/dist/core/tools/bash-policy.d.ts.map +1 -0
  100. package/dist/core/tools/bash-policy.js +1069 -0
  101. package/dist/core/tools/bash-policy.js.map +1 -0
  102. package/dist/core/tools/bash.d.ts +5 -0
  103. package/dist/core/tools/bash.d.ts.map +1 -1
  104. package/dist/core/tools/bash.js +7 -0
  105. package/dist/core/tools/bash.js.map +1 -1
  106. package/dist/core/tools/index.d.ts +1 -0
  107. package/dist/core/tools/index.d.ts.map +1 -1
  108. package/dist/core/tools/index.js +1 -0
  109. package/dist/core/tools/index.js.map +1 -1
  110. package/dist/index.d.ts +2 -2
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +1 -1
  113. package/dist/index.js.map +1 -1
  114. package/docs/sdk.md +42 -0
  115. package/docs/security.md +2 -0
  116. package/docs/workflows.md +127 -15
  117. package/package.json +1 -1
@@ -5,6 +5,7 @@ export { AgentSession, type AgentSessionConfig, type AgentSessionEvent, type Age
5
5
  export { AgentSessionRuntime, type CreateAgentSessionRuntimeFactory, type CreateAgentSessionRuntimeResult, createAgentSessionRuntime, } from "./agent-session-runtime.ts";
6
6
  export { type AgentSessionRuntimeDiagnostic, type AgentSessionServices, type CreateAgentSessionFromServicesOptions, type CreateAgentSessionServicesOptions, createAgentSessionFromServices, createAgentSessionServices, } from "./agent-session-services.ts";
7
7
  export { type BashExecutorOptions, type BashResult, executeBashWithOperations } from "./bash-executor.ts";
8
+ export { evaluateBashCommandPolicy, formatBashCommandPolicyRejection, parseBashCommandSegments, validateBashCommandPolicy, type BashCommandParseError, type BashCommandParseResult, type BashCommandPolicy, type BashCommandPolicyDecision, type BashCommandPolicyMatchMode, type BashCommandPolicyRejection, type BashCommandRule, type BashCommandSegment, type BashCommandSegmentSource, } from "./tools/bash-policy.ts";
8
9
  export type { ContextCompactionResult } from "./compaction/index.ts";
9
10
  export { createEventBus, type EventBus, type EventBusController } from "./event-bus.ts";
10
11
  export { areExperimentalFeaturesEnabled } from "./experimental.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,mBAAmB,EACnB,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,yBAAyB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACN,KAAK,6BAA6B,EAClC,KAAK,oBAAoB,EACzB,KAAK,qCAAqC,EAC1C,KAAK,iCAAiC,EACtC,8BAA8B,EAC9B,0BAA0B,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC1G,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,YAAY,EACjB,UAAU,EACV,yBAAyB,EACzB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,uBAAuB,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * Core modules shared between all run modes.\n */\n\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./agent-session.ts\";\nexport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\ttype CreateAgentSessionRuntimeResult,\n\tcreateAgentSessionRuntime,\n} from \"./agent-session-runtime.ts\";\nexport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\ttype CreateAgentSessionFromServicesOptions,\n\ttype CreateAgentSessionServicesOptions,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./agent-session-services.ts\";\nexport { type BashExecutorOptions, type BashResult, executeBashWithOperations } from \"./bash-executor.ts\";\nexport type { ContextCompactionResult } from \"./compaction/index.ts\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./event-bus.ts\";\nexport { areExperimentalFeaturesEnabled } from \"./experimental.ts\";\n// Extensions system\nexport {\n\ttype AgentEndEvent,\n\ttype AgentStartEvent,\n\ttype AgentToolResult,\n\ttype AgentToolUpdateCallback,\n\ttype BeforeAgentStartEvent,\n\ttype BeforeAgentStartEventResult,\n\ttype BuildSystemPromptOptions,\n\ttype ContextEvent,\n\tdefineTool,\n\tdiscoverAndLoadExtensions,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype Extension,\n\ttype ExtensionAPI,\n\ttype ExtensionCommandContext,\n\ttype ExtensionContext,\n\ttype ExtensionError,\n\ttype ExtensionEvent,\n\ttype ExtensionFactory,\n\ttype ExtensionFlag,\n\ttype ExtensionHandler,\n\tExtensionRunner,\n\ttype ExtensionShortcut,\n\ttype ExtensionUIContext,\n\ttype LoadExtensionsResult,\n\ttype MessageRenderer,\n\ttype RegisteredCommand,\n\ttype SessionBeforeCompactEvent,\n\ttype SessionBeforeForkEvent,\n\ttype SessionBeforeSwitchEvent,\n\ttype SessionBeforeTreeEvent,\n\ttype SessionCompactEvent,\n\ttype SessionShutdownEvent,\n\ttype SessionStartEvent,\n\ttype SessionTreeEvent,\n\ttype ToolCallEvent,\n\ttype ToolCallEventResult,\n\ttype ToolDefinition,\n\ttype ToolRenderResultOptions,\n\ttype ToolResultEvent,\n\ttype TurnEndEvent,\n\ttype TurnStartEvent,\n\ttype WorkingIndicatorOptions,\n} from \"./extensions/index.ts\";\nexport { createSyntheticSourceInfo } from \"./source-info.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,mBAAmB,EACnB,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,yBAAyB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACN,KAAK,6BAA6B,EAClC,KAAK,oBAAoB,EACzB,KAAK,qCAAqC,EAC1C,KAAK,iCAAiC,EACtC,8BAA8B,EAC9B,0BAA0B,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC1G,OAAO,EACN,yBAAyB,EACzB,gCAAgC,EAChC,wBAAwB,EACxB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,GAC7B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,YAAY,EACjB,UAAU,EACV,yBAAyB,EACzB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,uBAAuB,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * Core modules shared between all run modes.\n */\n\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./agent-session.ts\";\nexport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\ttype CreateAgentSessionRuntimeResult,\n\tcreateAgentSessionRuntime,\n} from \"./agent-session-runtime.ts\";\nexport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\ttype CreateAgentSessionFromServicesOptions,\n\ttype CreateAgentSessionServicesOptions,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./agent-session-services.ts\";\nexport { type BashExecutorOptions, type BashResult, executeBashWithOperations } from \"./bash-executor.ts\";\nexport {\n\tevaluateBashCommandPolicy,\n\tformatBashCommandPolicyRejection,\n\tparseBashCommandSegments,\n\tvalidateBashCommandPolicy,\n\ttype BashCommandParseError,\n\ttype BashCommandParseResult,\n\ttype BashCommandPolicy,\n\ttype BashCommandPolicyDecision,\n\ttype BashCommandPolicyMatchMode,\n\ttype BashCommandPolicyRejection,\n\ttype BashCommandRule,\n\ttype BashCommandSegment,\n\ttype BashCommandSegmentSource,\n} from \"./tools/bash-policy.ts\";\nexport type { ContextCompactionResult } from \"./compaction/index.ts\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./event-bus.ts\";\nexport { areExperimentalFeaturesEnabled } from \"./experimental.ts\";\n// Extensions system\nexport {\n\ttype AgentEndEvent,\n\ttype AgentStartEvent,\n\ttype AgentToolResult,\n\ttype AgentToolUpdateCallback,\n\ttype BeforeAgentStartEvent,\n\ttype BeforeAgentStartEventResult,\n\ttype BuildSystemPromptOptions,\n\ttype ContextEvent,\n\tdefineTool,\n\tdiscoverAndLoadExtensions,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype Extension,\n\ttype ExtensionAPI,\n\ttype ExtensionCommandContext,\n\ttype ExtensionContext,\n\ttype ExtensionError,\n\ttype ExtensionEvent,\n\ttype ExtensionFactory,\n\ttype ExtensionFlag,\n\ttype ExtensionHandler,\n\tExtensionRunner,\n\ttype ExtensionShortcut,\n\ttype ExtensionUIContext,\n\ttype LoadExtensionsResult,\n\ttype MessageRenderer,\n\ttype RegisteredCommand,\n\ttype SessionBeforeCompactEvent,\n\ttype SessionBeforeForkEvent,\n\ttype SessionBeforeSwitchEvent,\n\ttype SessionBeforeTreeEvent,\n\ttype SessionCompactEvent,\n\ttype SessionShutdownEvent,\n\ttype SessionStartEvent,\n\ttype SessionTreeEvent,\n\ttype ToolCallEvent,\n\ttype ToolCallEventResult,\n\ttype ToolDefinition,\n\ttype ToolRenderResultOptions,\n\ttype ToolResultEvent,\n\ttype TurnEndEvent,\n\ttype TurnStartEvent,\n\ttype WorkingIndicatorOptions,\n} from \"./extensions/index.ts\";\nexport { createSyntheticSourceInfo } from \"./source-info.ts\";\n"]}
@@ -5,6 +5,7 @@ export { AgentSession, } from "./agent-session.js";
5
5
  export { AgentSessionRuntime, createAgentSessionRuntime, } from "./agent-session-runtime.js";
6
6
  export { createAgentSessionFromServices, createAgentSessionServices, } from "./agent-session-services.js";
7
7
  export { executeBashWithOperations } from "./bash-executor.js";
8
+ export { evaluateBashCommandPolicy, formatBashCommandPolicyRejection, parseBashCommandSegments, validateBashCommandPolicy, } from "./tools/bash-policy.js";
8
9
  export { createEventBus } from "./event-bus.js";
9
10
  export { areExperimentalFeaturesEnabled } from "./experimental.js";
10
11
  // Extensions system
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,GAOZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,mBAAmB,EAGnB,yBAAyB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAKN,8BAA8B,EAC9B,0BAA0B,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAA6C,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAE1G,OAAO,EAAE,cAAc,EAA0C,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AACnE,oBAAoB;AACpB,OAAO,EASN,UAAU,EACV,yBAAyB,EAYzB,eAAe,GAsBf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * Core modules shared between all run modes.\n */\n\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./agent-session.ts\";\nexport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\ttype CreateAgentSessionRuntimeResult,\n\tcreateAgentSessionRuntime,\n} from \"./agent-session-runtime.ts\";\nexport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\ttype CreateAgentSessionFromServicesOptions,\n\ttype CreateAgentSessionServicesOptions,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./agent-session-services.ts\";\nexport { type BashExecutorOptions, type BashResult, executeBashWithOperations } from \"./bash-executor.ts\";\nexport type { ContextCompactionResult } from \"./compaction/index.ts\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./event-bus.ts\";\nexport { areExperimentalFeaturesEnabled } from \"./experimental.ts\";\n// Extensions system\nexport {\n\ttype AgentEndEvent,\n\ttype AgentStartEvent,\n\ttype AgentToolResult,\n\ttype AgentToolUpdateCallback,\n\ttype BeforeAgentStartEvent,\n\ttype BeforeAgentStartEventResult,\n\ttype BuildSystemPromptOptions,\n\ttype ContextEvent,\n\tdefineTool,\n\tdiscoverAndLoadExtensions,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype Extension,\n\ttype ExtensionAPI,\n\ttype ExtensionCommandContext,\n\ttype ExtensionContext,\n\ttype ExtensionError,\n\ttype ExtensionEvent,\n\ttype ExtensionFactory,\n\ttype ExtensionFlag,\n\ttype ExtensionHandler,\n\tExtensionRunner,\n\ttype ExtensionShortcut,\n\ttype ExtensionUIContext,\n\ttype LoadExtensionsResult,\n\ttype MessageRenderer,\n\ttype RegisteredCommand,\n\ttype SessionBeforeCompactEvent,\n\ttype SessionBeforeForkEvent,\n\ttype SessionBeforeSwitchEvent,\n\ttype SessionBeforeTreeEvent,\n\ttype SessionCompactEvent,\n\ttype SessionShutdownEvent,\n\ttype SessionStartEvent,\n\ttype SessionTreeEvent,\n\ttype ToolCallEvent,\n\ttype ToolCallEventResult,\n\ttype ToolDefinition,\n\ttype ToolRenderResultOptions,\n\ttype ToolResultEvent,\n\ttype TurnEndEvent,\n\ttype TurnStartEvent,\n\ttype WorkingIndicatorOptions,\n} from \"./extensions/index.ts\";\nexport { createSyntheticSourceInfo } from \"./source-info.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,GAOZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,mBAAmB,EAGnB,yBAAyB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAKN,8BAA8B,EAC9B,0BAA0B,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAA6C,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC1G,OAAO,EACN,yBAAyB,EACzB,gCAAgC,EAChC,wBAAwB,EACxB,yBAAyB,GAUzB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAA0C,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AACnE,oBAAoB;AACpB,OAAO,EASN,UAAU,EACV,yBAAyB,EAYzB,eAAe,GAsBf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * Core modules shared between all run modes.\n */\n\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./agent-session.ts\";\nexport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\ttype CreateAgentSessionRuntimeResult,\n\tcreateAgentSessionRuntime,\n} from \"./agent-session-runtime.ts\";\nexport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\ttype CreateAgentSessionFromServicesOptions,\n\ttype CreateAgentSessionServicesOptions,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./agent-session-services.ts\";\nexport { type BashExecutorOptions, type BashResult, executeBashWithOperations } from \"./bash-executor.ts\";\nexport {\n\tevaluateBashCommandPolicy,\n\tformatBashCommandPolicyRejection,\n\tparseBashCommandSegments,\n\tvalidateBashCommandPolicy,\n\ttype BashCommandParseError,\n\ttype BashCommandParseResult,\n\ttype BashCommandPolicy,\n\ttype BashCommandPolicyDecision,\n\ttype BashCommandPolicyMatchMode,\n\ttype BashCommandPolicyRejection,\n\ttype BashCommandRule,\n\ttype BashCommandSegment,\n\ttype BashCommandSegmentSource,\n} from \"./tools/bash-policy.ts\";\nexport type { ContextCompactionResult } from \"./compaction/index.ts\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./event-bus.ts\";\nexport { areExperimentalFeaturesEnabled } from \"./experimental.ts\";\n// Extensions system\nexport {\n\ttype AgentEndEvent,\n\ttype AgentStartEvent,\n\ttype AgentToolResult,\n\ttype AgentToolUpdateCallback,\n\ttype BeforeAgentStartEvent,\n\ttype BeforeAgentStartEventResult,\n\ttype BuildSystemPromptOptions,\n\ttype ContextEvent,\n\tdefineTool,\n\tdiscoverAndLoadExtensions,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype Extension,\n\ttype ExtensionAPI,\n\ttype ExtensionCommandContext,\n\ttype ExtensionContext,\n\ttype ExtensionError,\n\ttype ExtensionEvent,\n\ttype ExtensionFactory,\n\ttype ExtensionFlag,\n\ttype ExtensionHandler,\n\tExtensionRunner,\n\ttype ExtensionShortcut,\n\ttype ExtensionUIContext,\n\ttype LoadExtensionsResult,\n\ttype MessageRenderer,\n\ttype RegisteredCommand,\n\ttype SessionBeforeCompactEvent,\n\ttype SessionBeforeForkEvent,\n\ttype SessionBeforeSwitchEvent,\n\ttype SessionBeforeTreeEvent,\n\ttype SessionCompactEvent,\n\ttype SessionShutdownEvent,\n\ttype SessionStartEvent,\n\ttype SessionTreeEvent,\n\ttype ToolCallEvent,\n\ttype ToolCallEventResult,\n\ttype ToolDefinition,\n\ttype ToolRenderResultOptions,\n\ttype ToolResultEvent,\n\ttype TurnEndEvent,\n\ttype TurnStartEvent,\n\ttype WorkingIndicatorOptions,\n} from \"./extensions/index.ts\";\nexport { createSyntheticSourceInfo } from \"./source-info.ts\";\n"]}
@@ -7,7 +7,7 @@ import { ModelRegistry } from "./model-registry.ts";
7
7
  import type { ResourceLoader } from "./resource-loader.ts";
8
8
  import { SessionManager } from "./session-manager.ts";
9
9
  import { SettingsManager } from "./settings-manager.ts";
10
- import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue } from "./tools/index.ts";
10
+ import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue, type BashCommandPolicy } from "./tools/index.ts";
11
11
  export interface CreateAgentSessionOptions {
12
12
  /** Working directory for project-local discovery. Default: process.cwd() */
13
13
  cwd?: string;
@@ -53,6 +53,8 @@ export interface CreateAgentSessionOptions {
53
53
  excludedTools?: string[];
54
54
  /** Custom tools to register (in addition to built-in tools). */
55
55
  customTools?: ToolDefinition[];
56
+ /** Optional command-level policy for the built-in bash tool. Does not expose bash by itself. */
57
+ bashPolicy?: BashCommandPolicy;
56
58
  /** Resource loader. When omitted, DefaultResourceLoader is used. */
57
59
  resourceLoader?: ResourceLoader;
58
60
  /** Session manager. Default: SessionManager.create(cwd) */
@@ -77,7 +79,7 @@ export * from "./agent-session-runtime.ts";
77
79
  export type { ExtensionAPI, ExtensionCommandContext, ExtensionContext, ExtensionFactory, SlashCommandInfo, SlashCommandSource, ToolDefinition, } from "./extensions/index.ts";
78
80
  export type { PromptTemplate } from "./prompt-templates.ts";
79
81
  export type { Skill } from "./skills.ts";
80
- export type { Tool } from "./tools/index.ts";
82
+ export type { BashCommandParseError, BashCommandParseResult, BashCommandPolicy, BashCommandPolicyDecision, BashCommandPolicyMatchMode, BashCommandPolicyRejection, BashCommandRule, BashCommandSegment, BashCommandSegmentSource, Tool, } from "./tools/index.ts";
81
83
  export { withFileMutationQueue, createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createEditTool, createWriteTool, createGrepTool, createFindTool, createLsTool, };
82
84
  /**
83
85
  * Create an AgentSession with the specified options.
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type { Tool } from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const authStorage = options.authStorage ?? AuthStorage.create(authPath);\n const modelRegistry =\n options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = modelRegistry.find(\n existingSession.model.provider,\n existingSession.model.modelId,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n return converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n return guardedPayload;\n }\n return runner.emitBeforeProviderRequest(guardedPayload);\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n };\n}\n"]}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n type BashCommandPolicy,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n /** Optional command-level policy for the built-in bash tool. Does not expose bash by itself. */\n bashPolicy?: BashCommandPolicy;\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type {\n BashCommandParseError,\n BashCommandParseResult,\n BashCommandPolicy,\n BashCommandPolicyDecision,\n BashCommandPolicyMatchMode,\n BashCommandPolicyRejection,\n BashCommandRule,\n BashCommandSegment,\n BashCommandSegmentSource,\n Tool,\n} from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const authStorage = options.authStorage ?? AuthStorage.create(authPath);\n const modelRegistry =\n options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = modelRegistry.find(\n existingSession.model.provider,\n existingSession.model.modelId,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n return converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n return guardedPayload;\n }\n return runner.emitBeforeProviderRequest(guardedPayload);\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n bashPolicy: options.bashPolicy,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n };\n}\n"]}
package/dist/core/sdk.js CHANGED
@@ -275,6 +275,7 @@ export async function createAgentSession(options = {}) {
275
275
  scopedModels: options.scopedModels,
276
276
  resourceLoader,
277
277
  customTools: options.customTools,
278
+ bashPolicy: options.bashPolicy,
278
279
  modelRegistry,
279
280
  initialActiveToolNames,
280
281
  allowedToolNames,
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type { Tool } from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const authStorage = options.authStorage ?? AuthStorage.create(authPath);\n const modelRegistry =\n options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = modelRegistry.find(\n existingSession.model.provider,\n existingSession.model.modelId,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n return converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n return guardedPayload;\n }\n return runner.emitBeforeProviderRequest(guardedPayload);\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n };\n}\n"]}
1
+ {"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n type BashCommandPolicy,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n /** Optional command-level policy for the built-in bash tool. Does not expose bash by itself. */\n bashPolicy?: BashCommandPolicy;\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type {\n BashCommandParseError,\n BashCommandParseResult,\n BashCommandPolicy,\n BashCommandPolicyDecision,\n BashCommandPolicyMatchMode,\n BashCommandPolicyRejection,\n BashCommandRule,\n BashCommandSegment,\n BashCommandSegmentSource,\n Tool,\n} from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const authStorage = options.authStorage ?? AuthStorage.create(authPath);\n const modelRegistry =\n options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = modelRegistry.find(\n existingSession.model.provider,\n existingSession.model.modelId,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n return converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n return guardedPayload;\n }\n return runner.emitBeforeProviderRequest(guardedPayload);\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n bashPolicy: options.bashPolicy,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n };\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { InlineInputOwner, QuestionnaireState } from "./state.ts";
2
+ /**
3
+ * Shared owner-branching accessors/mutator for the two per-owner inline-input
4
+ * map pairs on `QuestionnaireState`:
5
+ * - `"chat"` → `chatDraftByTab` / `chatCaretByTab`
6
+ * - `"other"` → `customDraftByTab` / `customCaretByTab`
7
+ *
8
+ * Both the pure reducer (`state-reducer.ts`) and the runtime
9
+ * (`questionnaire-session.ts`) read and write these maps. Hoisting the helpers
10
+ * here keeps the `owner === "chat"` branch in exactly one place instead of
11
+ * copying it into each caller (previously `withInlineDraft` was duplicated
12
+ * verbatim and the accessor pairs were near-duplicates split across the two
13
+ * files).
14
+ */
15
+ /** Raw draft text persisted for `owner` at the current tab, or undefined when none is stored. */
16
+ export declare function readInlineDraft(state: QuestionnaireState, owner: InlineInputOwner): string | undefined;
17
+ /** Raw caret offset persisted for `owner` at the current tab, or undefined when none is stored. */
18
+ export declare function readInlineCaret(state: QuestionnaireState, owner: InlineInputOwner): number | undefined;
19
+ /** Immutably persist `value`/`caret` into `owner`'s draft+caret maps for the current tab. */
20
+ export declare function withInlineDraft(state: QuestionnaireState, owner: InlineInputOwner, value: string, caret: number): QuestionnaireState;
21
+ /**
22
+ * Draft text used to hydrate the inline editor when (re)focusing `owner`: the
23
+ * in-flight draft when present, otherwise the prior committed answer for that
24
+ * owner. The `"chat"` owner excludes the reserved sentinel label so a prior
25
+ * signal-only chat (`"Chat about this"`) does not re-hydrate as editable text.
26
+ */
27
+ export declare function resolveInlineDraftValue(state: QuestionnaireState, owner: InlineInputOwner): string;
28
+ //# sourceMappingURL=inline-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-input.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/inline-input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;;;;;;;;;GAYG;AAEH,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEtG;AAED,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEtG;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAC9B,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,kBAAkB,CAapB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAUlG","sourcesContent":["import { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { InlineInputOwner, QuestionnaireState } from \"./state.ts\";\n\n/**\n * Shared owner-branching accessors/mutator for the two per-owner inline-input\n * map pairs on `QuestionnaireState`:\n * - `\"chat\"` → `chatDraftByTab` / `chatCaretByTab`\n * - `\"other\"` → `customDraftByTab` / `customCaretByTab`\n *\n * Both the pure reducer (`state-reducer.ts`) and the runtime\n * (`questionnaire-session.ts`) read and write these maps. Hoisting the helpers\n * here keeps the `owner === \"chat\"` branch in exactly one place instead of\n * copying it into each caller (previously `withInlineDraft` was duplicated\n * verbatim and the accessor pairs were near-duplicates split across the two\n * files).\n */\n\n/** Raw draft text persisted for `owner` at the current tab, or undefined when none is stored. */\nexport function readInlineDraft(state: QuestionnaireState, owner: InlineInputOwner): string | undefined {\n\treturn owner === \"chat\" ? state.chatDraftByTab.get(state.currentTab) : state.customDraftByTab.get(state.currentTab);\n}\n\n/** Raw caret offset persisted for `owner` at the current tab, or undefined when none is stored. */\nexport function readInlineCaret(state: QuestionnaireState, owner: InlineInputOwner): number | undefined {\n\treturn owner === \"chat\" ? state.chatCaretByTab.get(state.currentTab) : state.customCaretByTab.get(state.currentTab);\n}\n\n/** Immutably persist `value`/`caret` into `owner`'s draft+caret maps for the current tab. */\nexport function withInlineDraft(\n\tstate: QuestionnaireState,\n\towner: InlineInputOwner,\n\tvalue: string,\n\tcaret: number,\n): QuestionnaireState {\n\tif (owner === \"chat\") {\n\t\tconst chatDraftByTab = new Map(state.chatDraftByTab);\n\t\tconst chatCaretByTab = new Map(state.chatCaretByTab);\n\t\tchatDraftByTab.set(state.currentTab, value);\n\t\tchatCaretByTab.set(state.currentTab, caret);\n\t\treturn { ...state, chatDraftByTab, chatCaretByTab };\n\t}\n\tconst customDraftByTab = new Map(state.customDraftByTab);\n\tconst customCaretByTab = new Map(state.customCaretByTab);\n\tcustomDraftByTab.set(state.currentTab, value);\n\tcustomCaretByTab.set(state.currentTab, caret);\n\treturn { ...state, customDraftByTab, customCaretByTab };\n}\n\n/**\n * Draft text used to hydrate the inline editor when (re)focusing `owner`: the\n * in-flight draft when present, otherwise the prior committed answer for that\n * owner. The `\"chat\"` owner excludes the reserved sentinel label so a prior\n * signal-only chat (`\"Chat about this\"`) does not re-hydrate as editable text.\n */\nexport function resolveInlineDraftValue(state: QuestionnaireState, owner: InlineInputOwner): string {\n\tconst draft = readInlineDraft(state, owner);\n\tif (draft !== undefined) return draft;\n\tconst prior = state.answers.get(state.currentTab);\n\tif (owner === \"other\") {\n\t\treturn prior?.kind === \"custom\" && typeof prior.answer === \"string\" ? prior.answer : \"\";\n\t}\n\treturn prior?.kind === \"chat\" && typeof prior.answer === \"string\" && prior.answer !== ROW_INTENT_META.chat.label\n\t\t? prior.answer\n\t\t: \"\";\n}\n"]}
@@ -0,0 +1,56 @@
1
+ import { ROW_INTENT_META } from "./row-intent.js";
2
+ /**
3
+ * Shared owner-branching accessors/mutator for the two per-owner inline-input
4
+ * map pairs on `QuestionnaireState`:
5
+ * - `"chat"` → `chatDraftByTab` / `chatCaretByTab`
6
+ * - `"other"` → `customDraftByTab` / `customCaretByTab`
7
+ *
8
+ * Both the pure reducer (`state-reducer.ts`) and the runtime
9
+ * (`questionnaire-session.ts`) read and write these maps. Hoisting the helpers
10
+ * here keeps the `owner === "chat"` branch in exactly one place instead of
11
+ * copying it into each caller (previously `withInlineDraft` was duplicated
12
+ * verbatim and the accessor pairs were near-duplicates split across the two
13
+ * files).
14
+ */
15
+ /** Raw draft text persisted for `owner` at the current tab, or undefined when none is stored. */
16
+ export function readInlineDraft(state, owner) {
17
+ return owner === "chat" ? state.chatDraftByTab.get(state.currentTab) : state.customDraftByTab.get(state.currentTab);
18
+ }
19
+ /** Raw caret offset persisted for `owner` at the current tab, or undefined when none is stored. */
20
+ export function readInlineCaret(state, owner) {
21
+ return owner === "chat" ? state.chatCaretByTab.get(state.currentTab) : state.customCaretByTab.get(state.currentTab);
22
+ }
23
+ /** Immutably persist `value`/`caret` into `owner`'s draft+caret maps for the current tab. */
24
+ export function withInlineDraft(state, owner, value, caret) {
25
+ if (owner === "chat") {
26
+ const chatDraftByTab = new Map(state.chatDraftByTab);
27
+ const chatCaretByTab = new Map(state.chatCaretByTab);
28
+ chatDraftByTab.set(state.currentTab, value);
29
+ chatCaretByTab.set(state.currentTab, caret);
30
+ return { ...state, chatDraftByTab, chatCaretByTab };
31
+ }
32
+ const customDraftByTab = new Map(state.customDraftByTab);
33
+ const customCaretByTab = new Map(state.customCaretByTab);
34
+ customDraftByTab.set(state.currentTab, value);
35
+ customCaretByTab.set(state.currentTab, caret);
36
+ return { ...state, customDraftByTab, customCaretByTab };
37
+ }
38
+ /**
39
+ * Draft text used to hydrate the inline editor when (re)focusing `owner`: the
40
+ * in-flight draft when present, otherwise the prior committed answer for that
41
+ * owner. The `"chat"` owner excludes the reserved sentinel label so a prior
42
+ * signal-only chat (`"Chat about this"`) does not re-hydrate as editable text.
43
+ */
44
+ export function resolveInlineDraftValue(state, owner) {
45
+ const draft = readInlineDraft(state, owner);
46
+ if (draft !== undefined)
47
+ return draft;
48
+ const prior = state.answers.get(state.currentTab);
49
+ if (owner === "other") {
50
+ return prior?.kind === "custom" && typeof prior.answer === "string" ? prior.answer : "";
51
+ }
52
+ return prior?.kind === "chat" && typeof prior.answer === "string" && prior.answer !== ROW_INTENT_META.chat.label
53
+ ? prior.answer
54
+ : "";
55
+ }
56
+ //# sourceMappingURL=inline-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-input.js","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/inline-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD;;;;;;;;;;;;GAYG;AAEH,iGAAiG;AACjG,MAAM,UAAU,eAAe,CAAC,KAAyB,EAAE,KAAuB;IACjF,OAAO,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AACrH,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,eAAe,CAAC,KAAyB,EAAE,KAAuB;IACjF,OAAO,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AACrH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,eAAe,CAC9B,KAAyB,EACzB,KAAuB,EACvB,KAAa,EACb,KAAa;IAEb,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACrD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACrD,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC5C,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC5C,OAAO,EAAE,GAAG,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;IACrD,CAAC;IACD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACzD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACzD,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC9C,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC9C,OAAO,EAAE,GAAG,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAyB,EAAE,KAAuB;IACzF,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAClD,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC;IACD,OAAO,KAAK,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,eAAe,CAAC,IAAI,CAAC,KAAK;QAC/G,CAAC,CAAC,KAAK,CAAC,MAAM;QACd,CAAC,CAAC,EAAE,CAAC;AACP,CAAC","sourcesContent":["import { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { InlineInputOwner, QuestionnaireState } from \"./state.ts\";\n\n/**\n * Shared owner-branching accessors/mutator for the two per-owner inline-input\n * map pairs on `QuestionnaireState`:\n * - `\"chat\"` → `chatDraftByTab` / `chatCaretByTab`\n * - `\"other\"` → `customDraftByTab` / `customCaretByTab`\n *\n * Both the pure reducer (`state-reducer.ts`) and the runtime\n * (`questionnaire-session.ts`) read and write these maps. Hoisting the helpers\n * here keeps the `owner === \"chat\"` branch in exactly one place instead of\n * copying it into each caller (previously `withInlineDraft` was duplicated\n * verbatim and the accessor pairs were near-duplicates split across the two\n * files).\n */\n\n/** Raw draft text persisted for `owner` at the current tab, or undefined when none is stored. */\nexport function readInlineDraft(state: QuestionnaireState, owner: InlineInputOwner): string | undefined {\n\treturn owner === \"chat\" ? state.chatDraftByTab.get(state.currentTab) : state.customDraftByTab.get(state.currentTab);\n}\n\n/** Raw caret offset persisted for `owner` at the current tab, or undefined when none is stored. */\nexport function readInlineCaret(state: QuestionnaireState, owner: InlineInputOwner): number | undefined {\n\treturn owner === \"chat\" ? state.chatCaretByTab.get(state.currentTab) : state.customCaretByTab.get(state.currentTab);\n}\n\n/** Immutably persist `value`/`caret` into `owner`'s draft+caret maps for the current tab. */\nexport function withInlineDraft(\n\tstate: QuestionnaireState,\n\towner: InlineInputOwner,\n\tvalue: string,\n\tcaret: number,\n): QuestionnaireState {\n\tif (owner === \"chat\") {\n\t\tconst chatDraftByTab = new Map(state.chatDraftByTab);\n\t\tconst chatCaretByTab = new Map(state.chatCaretByTab);\n\t\tchatDraftByTab.set(state.currentTab, value);\n\t\tchatCaretByTab.set(state.currentTab, caret);\n\t\treturn { ...state, chatDraftByTab, chatCaretByTab };\n\t}\n\tconst customDraftByTab = new Map(state.customDraftByTab);\n\tconst customCaretByTab = new Map(state.customCaretByTab);\n\tcustomDraftByTab.set(state.currentTab, value);\n\tcustomCaretByTab.set(state.currentTab, caret);\n\treturn { ...state, customDraftByTab, customCaretByTab };\n}\n\n/**\n * Draft text used to hydrate the inline editor when (re)focusing `owner`: the\n * in-flight draft when present, otherwise the prior committed answer for that\n * owner. The `\"chat\"` owner excludes the reserved sentinel label so a prior\n * signal-only chat (`\"Chat about this\"`) does not re-hydrate as editable text.\n */\nexport function resolveInlineDraftValue(state: QuestionnaireState, owner: InlineInputOwner): string {\n\tconst draft = readInlineDraft(state, owner);\n\tif (draft !== undefined) return draft;\n\tconst prior = state.answers.get(state.currentTab);\n\tif (owner === \"other\") {\n\t\treturn prior?.kind === \"custom\" && typeof prior.answer === \"string\" ? prior.answer : \"\";\n\t}\n\treturn prior?.kind === \"chat\" && typeof prior.answer === \"string\" && prior.answer !== ROW_INTENT_META.chat.label\n\t\t? prior.answer\n\t\t: \"\";\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"key-router.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/key-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAU3E,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE;AACxB;;;GAGG;GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAM7F;AAkGD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAwHpH","sourcesContent":["import { Key, matchesKey } from \"@earendil-works/pi-tui\";\nimport type { QuestionAnswer } from \"../tool/types.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { QuestionnaireRuntime, QuestionnaireState } from \"./state.ts\";\n\nconst KEYBIND_UP = \"tui.select.up\";\nconst KEYBIND_DOWN = \"tui.select.down\";\nconst KEYBIND_CONFIRM = \"tui.select.confirm\";\nconst KEYBIND_CANCEL = \"tui.select.cancel\";\n\nconst NOTES_ACTIVATE_KEY = \"n\";\nconst SPACE_KEY = \" \";\n\nexport type QuestionnaireAction =\n\t| { kind: \"nav\"; nextIndex: number }\n\t| { kind: \"tab_switch\"; nextTab: number }\n\t| { kind: \"confirm\"; answer: QuestionAnswer; autoAdvanceTab?: number }\n\t| { kind: \"toggle\"; index: number }\n\t| { kind: \"multi_confirm\"; selected: string[]; autoAdvanceTab?: number }\n\t| { kind: \"cancel\" }\n\t| { kind: \"notes_enter\" }\n\t| { kind: \"notes_exit\" }\n\t| { kind: \"submit\" }\n\t| { kind: \"submit_nav\"; nextIndex: 0 | 1 }\n\t| { kind: \"focus_chat\" }\n\t/**\n\t * Carries the target index so UP/DOWN form a continuous cycle through\n\t * `[chat, option0, …, optionLast]`.\n\t */\n\t| { kind: \"focus_options\"; optionIndex: number }\n\t| { kind: \"notes_forward\"; data: string }\n\t| { kind: \"ignore\" };\n\nexport interface QuestionnaireKeybindings {\n\tmatches(data: string, name: string): boolean;\n}\n\nexport function wrapTab(index: number, total: number): number {\n\tif (total <= 0) return 0;\n\treturn ((index % total) + total) % total;\n}\n\nexport function allAnswered(state: QuestionnaireState, runtime: QuestionnaireRuntime): boolean {\n\tif (runtime.questions.length === 0) return false;\n\tfor (let i = 0; i < runtime.questions.length; i++) {\n\t\tif (!state.answers.has(i)) return false;\n\t}\n\treturn true;\n}\n\nfunction totalTabs(runtime: QuestionnaireRuntime): number {\n\treturn runtime.isMulti ? runtime.questions.length + 1 : 1;\n}\n\nfunction computeAutoAdvanceTab(state: QuestionnaireState, runtime: QuestionnaireRuntime): number | undefined {\n\tif (!runtime.isMulti) return undefined;\n\tif (state.currentTab < runtime.questions.length - 1) return state.currentTab + 1;\n\treturn runtime.questions.length;\n}\n\nfunction buildSingleSelectAnswer(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionAnswer | null {\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return null;\n\n\t// Chat sentinel takes priority over inputMode: when chatFocused=true, the host overrides\n\t// currentItem() to return the chat sentinel even if inputMode is still true (e.g. user\n\t// navigated from \"Type something.\" and DOWN focused the chat row).\n\tconst item = runtime.currentItem;\n\tif (item?.kind === \"chat\") {\n\t\treturn {\n\t\t\tquestionIndex: state.currentTab,\n\t\t\tquestion: q.question,\n\t\t\tkind: \"chat\",\n\t\t\tanswer: item.label,\n\t\t};\n\t}\n\n\tif (state.inputMode) {\n\t\tconst label = runtime.inputBuffer;\n\t\treturn {\n\t\t\tquestionIndex: state.currentTab,\n\t\t\tquestion: q.question,\n\t\t\tkind: \"custom\",\n\t\t\tanswer: label.length > 0 ? label : null,\n\t\t};\n\t}\n\tif (!item) return null;\n\tif (item.kind === \"other\") {\n\t\treturn null;\n\t}\n\tif (item.kind === \"next\") {\n\t\treturn null;\n\t}\n\treturn {\n\t\tquestionIndex: state.currentTab,\n\t\tquestion: q.question,\n\t\tkind: \"option\",\n\t\tanswer: item.label,\n\t};\n}\n\nfunction buildMultiSelected(state: QuestionnaireState, runtime: QuestionnaireRuntime): string[] {\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return [];\n\tconst out: string[] = [];\n\tfor (let i = 0; i < q.options.length; i++) {\n\t\tif (state.multiSelectChecked.has(i)) {\n\t\t\tconst label = q.options[i]?.label;\n\t\t\tif (typeof label === \"string\") out.push(label);\n\t\t}\n\t}\n\treturn out;\n}\n\nfunction tabSwitchAction(\n\tdata: string,\n\tstate: QuestionnaireState,\n\truntime: QuestionnaireRuntime,\n): QuestionnaireAction | null {\n\tif (!runtime.isMulti) return null;\n\tconst total = totalTabs(runtime);\n\tif (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {\n\t\treturn { kind: \"tab_switch\", nextTab: wrapTab(state.currentTab + 1, total) };\n\t}\n\tif (matchesKey(data, Key.shift(\"tab\")) || matchesKey(data, Key.left)) {\n\t\treturn { kind: \"tab_switch\", nextTab: wrapTab(state.currentTab - 1, total) };\n\t}\n\treturn null;\n}\n\n// DOWN at the last item emits focus_chat so the cycle [chat, option0, …, optionLast] wraps.\nfunction nextNavOnDown(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tif (runtime.items.length > 0 && state.optionIndex === runtime.items.length - 1) {\n\t\treturn { kind: \"focus_chat\" };\n\t}\n\treturn { kind: \"nav\", nextIndex: wrapTab(state.optionIndex + 1, Math.max(1, runtime.items.length)) };\n}\n\n// UP at the top item emits focus_chat (symmetric with nextNavOnDown).\nfunction prevNavOnUp(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tif (runtime.items.length > 0 && state.optionIndex === 0) {\n\t\treturn { kind: \"focus_chat\" };\n\t}\n\treturn { kind: \"nav\", nextIndex: wrapTab(state.optionIndex - 1, Math.max(1, runtime.items.length)) };\n}\n\nexport function routeKey(data: string, state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tconst kb = runtime.keybindings;\n\n\tif (state.notesVisible) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"notes_exit\" };\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) return { kind: \"notes_exit\" };\n\t\treturn { kind: \"notes_forward\", data };\n\t}\n\n\tif (state.chatFocused) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\t\tif (!answer) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t\t}\n\t\t// Continuous cycle: UP from chat → bottom of options (last navigable row), DOWN from\n\t\t// chat → top of options (option 0). Symmetric with UP-at-top → focus_chat and\n\t\t// DOWN-at-bottom → focus_chat below; together they form one wrapping cycle through\n\t\t// `[chat, option0, …, optionLast]`.\n\t\tif (kb.matches(data, KEYBIND_UP)) {\n\t\t\tconst last = Math.max(0, runtime.items.length - 1);\n\t\t\treturn { kind: \"focus_options\", optionIndex: last };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\t\treturn { kind: \"focus_options\", optionIndex: 0 };\n\t\t}\n\t\tconst tab = tabSwitchAction(data, state, runtime);\n\t\tif (tab) return tab;\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (state.inputMode) {\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\t\tif (!answer) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tif (kb.matches(data, KEYBIND_UP)) {\n\t\t\treturn prevNavOnUp(state, runtime);\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\t\treturn nextNavOnDown(state, runtime);\n\t\t}\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (runtime.isMulti && state.currentTab === runtime.questions.length) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tconst tab = tabSwitchAction(data, state, runtime);\n\t\tif (tab) return tab;\n\t\tif (kb.matches(data, KEYBIND_UP) || kb.matches(data, KEYBIND_DOWN)) {\n\t\t\tconst delta = kb.matches(data, KEYBIND_DOWN) ? 1 : -1;\n\t\t\tconst next = wrapTab(state.submitChoiceIndex + delta, 2);\n\t\t\treturn { kind: \"submit_nav\", nextIndex: (next === 1 ? 1 : 0) as 0 | 1 };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\t// D1 (revised): Submit always submits; Cancel always cancels. The warning header\n\t\t\t// is informational only — `allAnswered(state)` no longer gates submission. Partial\n\t\t\t// answers flow through `orderedAnswers()` in the host.\n\t\t\treturn state.submitChoiceIndex === 1 ? { kind: \"cancel\" } : { kind: \"submit\" };\n\t\t}\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tconst tab = tabSwitchAction(data, state, runtime);\n\tif (tab) return tab;\n\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return { kind: \"ignore\" };\n\n\tif (data === NOTES_ACTIVATE_KEY && !q.multiSelect && state.focusedOptionHasPreview) {\n\t\treturn { kind: \"notes_enter\" };\n\t}\n\n\tif (kb.matches(data, KEYBIND_UP)) {\n\t\treturn prevNavOnUp(state, runtime);\n\t}\n\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\treturn nextNavOnDown(state, runtime);\n\t}\n\n\tif (q.multiSelect) {\n\t\tconst focusedKind = runtime.currentItem?.kind;\n\t\tconst focusedMeta = focusedKind ? ROW_INTENT_META[focusedKind] : undefined;\n\t\t// Space toggles the focused row's checkbox. Suppressed on rows whose META declares\n\t\t// `blocksMultiToggle` (the Next sentinel) — Next is not a real option and has no\n\t\t// checked/unchecked state.\n\t\tif (data === SPACE_KEY) {\n\t\t\tif (focusedMeta?.blocksMultiToggle) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"toggle\", index: state.optionIndex };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\t// Enter on a regular row toggles (matching Space) — committing the question is now\n\t\t\t// gated behind explicit focus on a row whose META declares `autoSubmitsInMulti`\n\t\t\t// (the Next sentinel), so Enter on options is a no-cost way to flip checkboxes\n\t\t\t// without leaving the keyboard home row.\n\t\t\tif (!focusedMeta?.autoSubmitsInMulti) return { kind: \"toggle\", index: state.optionIndex };\n\t\t\t// Enter on Next: carry autoAdvanceTab so the host can advance to the next tab in\n\t\t\t// multi-question mode, OR submit the dialog in single-question mode\n\t\t\t// (autoAdvanceTab === undefined when !isMulti). Without this, a single multi-select\n\t\t\t// question would have no way to commit at all.\n\t\t\treturn {\n\t\t\t\tkind: \"multi_confirm\",\n\t\t\t\tselected: buildMultiSelected(state, runtime),\n\t\t\t\tautoAdvanceTab: computeAutoAdvanceTab(state, runtime),\n\t\t\t};\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\tif (!answer) return { kind: \"ignore\" };\n\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t}\n\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\treturn { kind: \"ignore\" };\n}\n"]}
1
+ {"version":3,"file":"key-router.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/key-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAU3E,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE;AACxB;;;GAGG;GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAM7F;AAgHD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAsIpH","sourcesContent":["import { Key, matchesKey } from \"@earendil-works/pi-tui\";\nimport type { QuestionAnswer } from \"../tool/types.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { QuestionnaireRuntime, QuestionnaireState } from \"./state.ts\";\n\nconst KEYBIND_UP = \"tui.select.up\";\nconst KEYBIND_DOWN = \"tui.select.down\";\nconst KEYBIND_CONFIRM = \"tui.select.confirm\";\nconst KEYBIND_CANCEL = \"tui.select.cancel\";\n\nconst NOTES_ACTIVATE_KEY = \"n\";\nconst SPACE_KEY = \" \";\n\nexport type QuestionnaireAction =\n\t| { kind: \"nav\"; nextIndex: number }\n\t| { kind: \"tab_switch\"; nextTab: number }\n\t| { kind: \"confirm\"; answer: QuestionAnswer; autoAdvanceTab?: number }\n\t| { kind: \"toggle\"; index: number }\n\t| { kind: \"multi_confirm\"; selected: string[]; autoAdvanceTab?: number }\n\t| { kind: \"cancel\" }\n\t| { kind: \"notes_enter\" }\n\t| { kind: \"notes_exit\" }\n\t| { kind: \"submit\" }\n\t| { kind: \"submit_nav\"; nextIndex: 0 | 1 }\n\t| { kind: \"focus_chat\" }\n\t/**\n\t * Carries the target index so UP/DOWN form a continuous cycle through\n\t * `[chat, option0, …, optionLast]`.\n\t */\n\t| { kind: \"focus_options\"; optionIndex: number }\n\t| { kind: \"notes_forward\"; data: string }\n\t| { kind: \"ignore\" };\n\nexport interface QuestionnaireKeybindings {\n\tmatches(data: string, name: string): boolean;\n}\n\nexport function wrapTab(index: number, total: number): number {\n\tif (total <= 0) return 0;\n\treturn ((index % total) + total) % total;\n}\n\nexport function allAnswered(state: QuestionnaireState, runtime: QuestionnaireRuntime): boolean {\n\tif (runtime.questions.length === 0) return false;\n\tfor (let i = 0; i < runtime.questions.length; i++) {\n\t\tif (!state.answers.has(i)) return false;\n\t}\n\treturn true;\n}\n\nfunction totalTabs(runtime: QuestionnaireRuntime): number {\n\treturn runtime.isMulti ? runtime.questions.length + 1 : 1;\n}\n\nfunction computeAutoAdvanceTab(state: QuestionnaireState, runtime: QuestionnaireRuntime): number | undefined {\n\tif (!runtime.isMulti) return undefined;\n\tif (state.currentTab < runtime.questions.length - 1) return state.currentTab + 1;\n\treturn runtime.questions.length;\n}\n\n/**\n * Resolve the chat answer text at confirm time. Reads the LIVE `runtime.inputBuffer`\n * rather than a persisted draft because confirm only reaches here while chat is the\n * focused row and therefore owns the live inline editor (`inlineInputOwner === \"chat\"`),\n * so the in-flight buffer is authoritative. The blank-vs-typed decision uses `trim()`,\n * but the returned text is left UNTRIMMED to preserve the user's literal message; a\n * blank/whitespace buffer falls back to the reserved sentinel label (signal-only chat).\n */\nfunction chatAnswerValue(state: QuestionnaireState, runtime: QuestionnaireRuntime, sentinelLabel: string): string {\n\tif (state.inlineInputOwner !== \"chat\") return sentinelLabel;\n\treturn runtime.inputBuffer.trim().length > 0 ? runtime.inputBuffer : sentinelLabel;\n}\n\nfunction buildSingleSelectAnswer(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionAnswer | null {\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return null;\n\n\t// Chat sentinel takes priority over inputMode: when chatFocused=true, the host overrides\n\t// currentItem() to return the chat sentinel even though inputMode is true for the shared\n\t// inline editor. Non-blank chat drafts become the chat answer; blank drafts preserve the\n\t// legacy sentinel-only \"Chat about this\" answer.\n\tconst item = runtime.currentItem;\n\tif (item?.kind === \"chat\") {\n\t\treturn {\n\t\t\tquestionIndex: state.currentTab,\n\t\t\tquestion: q.question,\n\t\t\tkind: \"chat\",\n\t\t\tanswer: chatAnswerValue(state, runtime, item.label),\n\t\t};\n\t}\n\n\tif (state.inputMode && state.inlineInputOwner === \"other\") {\n\t\tconst label = runtime.inputBuffer;\n\t\treturn {\n\t\t\tquestionIndex: state.currentTab,\n\t\t\tquestion: q.question,\n\t\t\tkind: \"custom\",\n\t\t\tanswer: label.length > 0 ? label : null,\n\t\t};\n\t}\n\tif (!item) return null;\n\tif (item.kind === \"other\") {\n\t\treturn null;\n\t}\n\tif (item.kind === \"next\") {\n\t\treturn null;\n\t}\n\treturn {\n\t\tquestionIndex: state.currentTab,\n\t\tquestion: q.question,\n\t\tkind: \"option\",\n\t\tanswer: item.label,\n\t};\n}\n\nfunction buildMultiSelected(state: QuestionnaireState, runtime: QuestionnaireRuntime): string[] {\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return [];\n\tconst out: string[] = [];\n\tfor (let i = 0; i < q.options.length; i++) {\n\t\tif (state.multiSelectChecked.has(i)) {\n\t\t\tconst label = q.options[i]?.label;\n\t\t\tif (typeof label === \"string\") out.push(label);\n\t\t}\n\t}\n\treturn out;\n}\n\nfunction tabSwitchAction(\n\tdata: string,\n\tstate: QuestionnaireState,\n\truntime: QuestionnaireRuntime,\n): QuestionnaireAction | null {\n\tif (!runtime.isMulti) return null;\n\tconst total = totalTabs(runtime);\n\tif (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {\n\t\treturn { kind: \"tab_switch\", nextTab: wrapTab(state.currentTab + 1, total) };\n\t}\n\tif (matchesKey(data, Key.shift(\"tab\")) || matchesKey(data, Key.left)) {\n\t\treturn { kind: \"tab_switch\", nextTab: wrapTab(state.currentTab - 1, total) };\n\t}\n\treturn null;\n}\n\n// DOWN at the last item emits focus_chat so the cycle [chat, option0, …, optionLast] wraps.\nfunction nextNavOnDown(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tif (runtime.items.length > 0 && state.optionIndex === runtime.items.length - 1) {\n\t\treturn { kind: \"focus_chat\" };\n\t}\n\treturn { kind: \"nav\", nextIndex: wrapTab(state.optionIndex + 1, Math.max(1, runtime.items.length)) };\n}\n\n// UP at the top item emits focus_chat (symmetric with nextNavOnDown).\nfunction prevNavOnUp(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tif (runtime.items.length > 0 && state.optionIndex === 0) {\n\t\treturn { kind: \"focus_chat\" };\n\t}\n\treturn { kind: \"nav\", nextIndex: wrapTab(state.optionIndex - 1, Math.max(1, runtime.items.length)) };\n}\n\nexport function routeKey(data: string, state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tconst kb = runtime.keybindings;\n\n\tif (state.notesVisible) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"notes_exit\" };\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) return { kind: \"notes_exit\" };\n\t\treturn { kind: \"notes_forward\", data };\n\t}\n\n\tif (state.chatFocused) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\t\tif (!answer) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t\t}\n\t\t// Continuous cycle: UP from chat → bottom of options (last navigable row), DOWN from\n\t\t// chat → top of options (option 0). Symmetric with UP-at-top → focus_chat and\n\t\t// DOWN-at-bottom → focus_chat below; together they form one wrapping cycle through\n\t\t// `[chat, option0, …, optionLast]`.\n\t\tif (kb.matches(data, KEYBIND_UP)) {\n\t\t\tconst last = Math.max(0, runtime.items.length - 1);\n\t\t\treturn { kind: \"focus_options\", optionIndex: last };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\t\treturn { kind: \"focus_options\", optionIndex: 0 };\n\t\t}\n\t\t// Left/Right guard is intentionally asymmetric — it exists for the chat owner but\n\t\t// NOT the \"other\" inline editor. The chatFocused branch falls through to\n\t\t// `tabSwitchAction` below, which in multi-question mode consumes Left/Right for tab\n\t\t// switching; swallowing them here keeps Left/Right as in-editor caret movement for\n\t\t// the chat input. The \"other\" inline editor flows through the separate\n\t\t// `state.inputMode` branch (further down), which never calls `tabSwitchAction`, so it\n\t\t// needs no equivalent guard. Do not \"simplify\" by unifying the two paths.\n\t\tif (\n\t\t\tstate.inputMode &&\n\t\t\tstate.inlineInputOwner === \"chat\" &&\n\t\t\t(matchesKey(data, Key.left) || matchesKey(data, Key.right))\n\t\t) {\n\t\t\treturn { kind: \"ignore\" };\n\t\t}\n\t\tconst tab = tabSwitchAction(data, state, runtime);\n\t\tif (tab) return tab;\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (state.inputMode) {\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\t\tif (!answer) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tif (kb.matches(data, KEYBIND_UP)) {\n\t\t\treturn prevNavOnUp(state, runtime);\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\t\treturn nextNavOnDown(state, runtime);\n\t\t}\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (runtime.isMulti && state.currentTab === runtime.questions.length) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tconst tab = tabSwitchAction(data, state, runtime);\n\t\tif (tab) return tab;\n\t\tif (kb.matches(data, KEYBIND_UP) || kb.matches(data, KEYBIND_DOWN)) {\n\t\t\tconst delta = kb.matches(data, KEYBIND_DOWN) ? 1 : -1;\n\t\t\tconst next = wrapTab(state.submitChoiceIndex + delta, 2);\n\t\t\treturn { kind: \"submit_nav\", nextIndex: (next === 1 ? 1 : 0) as 0 | 1 };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\t// D1 (revised): Submit always submits; Cancel always cancels. The warning header\n\t\t\t// is informational only — `allAnswered(state)` no longer gates submission. Partial\n\t\t\t// answers flow through `orderedAnswers()` in the host.\n\t\t\treturn state.submitChoiceIndex === 1 ? { kind: \"cancel\" } : { kind: \"submit\" };\n\t\t}\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tconst tab = tabSwitchAction(data, state, runtime);\n\tif (tab) return tab;\n\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return { kind: \"ignore\" };\n\n\tif (data === NOTES_ACTIVATE_KEY && !q.multiSelect && state.focusedOptionHasPreview) {\n\t\treturn { kind: \"notes_enter\" };\n\t}\n\n\tif (kb.matches(data, KEYBIND_UP)) {\n\t\treturn prevNavOnUp(state, runtime);\n\t}\n\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\treturn nextNavOnDown(state, runtime);\n\t}\n\n\tif (q.multiSelect) {\n\t\tconst focusedKind = runtime.currentItem?.kind;\n\t\tconst focusedMeta = focusedKind ? ROW_INTENT_META[focusedKind] : undefined;\n\t\t// Space toggles the focused row's checkbox. Suppressed on rows whose META declares\n\t\t// `blocksMultiToggle` (the Next sentinel) — Next is not a real option and has no\n\t\t// checked/unchecked state.\n\t\tif (data === SPACE_KEY) {\n\t\t\tif (focusedMeta?.blocksMultiToggle) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"toggle\", index: state.optionIndex };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\t// Enter on a regular row toggles (matching Space) — committing the question is now\n\t\t\t// gated behind explicit focus on a row whose META declares `autoSubmitsInMulti`\n\t\t\t// (the Next sentinel), so Enter on options is a no-cost way to flip checkboxes\n\t\t\t// without leaving the keyboard home row.\n\t\t\tif (!focusedMeta?.autoSubmitsInMulti) return { kind: \"toggle\", index: state.optionIndex };\n\t\t\t// Enter on Next: carry autoAdvanceTab so the host can advance to the next tab in\n\t\t\t// multi-question mode, OR submit the dialog in single-question mode\n\t\t\t// (autoAdvanceTab === undefined when !isMulti). Without this, a single multi-select\n\t\t\t// question would have no way to commit at all.\n\t\t\treturn {\n\t\t\t\tkind: \"multi_confirm\",\n\t\t\t\tselected: buildMultiSelected(state, runtime),\n\t\t\t\tautoAdvanceTab: computeAutoAdvanceTab(state, runtime),\n\t\t\t};\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\tif (!answer) return { kind: \"ignore\" };\n\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t}\n\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\treturn { kind: \"ignore\" };\n}\n"]}
@@ -30,23 +30,37 @@ function computeAutoAdvanceTab(state, runtime) {
30
30
  return state.currentTab + 1;
31
31
  return runtime.questions.length;
32
32
  }
33
+ /**
34
+ * Resolve the chat answer text at confirm time. Reads the LIVE `runtime.inputBuffer`
35
+ * rather than a persisted draft because confirm only reaches here while chat is the
36
+ * focused row and therefore owns the live inline editor (`inlineInputOwner === "chat"`),
37
+ * so the in-flight buffer is authoritative. The blank-vs-typed decision uses `trim()`,
38
+ * but the returned text is left UNTRIMMED to preserve the user's literal message; a
39
+ * blank/whitespace buffer falls back to the reserved sentinel label (signal-only chat).
40
+ */
41
+ function chatAnswerValue(state, runtime, sentinelLabel) {
42
+ if (state.inlineInputOwner !== "chat")
43
+ return sentinelLabel;
44
+ return runtime.inputBuffer.trim().length > 0 ? runtime.inputBuffer : sentinelLabel;
45
+ }
33
46
  function buildSingleSelectAnswer(state, runtime) {
34
47
  const q = runtime.questions[state.currentTab];
35
48
  if (!q)
36
49
  return null;
37
50
  // Chat sentinel takes priority over inputMode: when chatFocused=true, the host overrides
38
- // currentItem() to return the chat sentinel even if inputMode is still true (e.g. user
39
- // navigated from "Type something." and DOWN focused the chat row).
51
+ // currentItem() to return the chat sentinel even though inputMode is true for the shared
52
+ // inline editor. Non-blank chat drafts become the chat answer; blank drafts preserve the
53
+ // legacy sentinel-only "Chat about this" answer.
40
54
  const item = runtime.currentItem;
41
55
  if (item?.kind === "chat") {
42
56
  return {
43
57
  questionIndex: state.currentTab,
44
58
  question: q.question,
45
59
  kind: "chat",
46
- answer: item.label,
60
+ answer: chatAnswerValue(state, runtime, item.label),
47
61
  };
48
62
  }
49
- if (state.inputMode) {
63
+ if (state.inputMode && state.inlineInputOwner === "other") {
50
64
  const label = runtime.inputBuffer;
51
65
  return {
52
66
  questionIndex: state.currentTab,
@@ -139,6 +153,18 @@ export function routeKey(data, state, runtime) {
139
153
  if (kb.matches(data, KEYBIND_DOWN)) {
140
154
  return { kind: "focus_options", optionIndex: 0 };
141
155
  }
156
+ // Left/Right guard is intentionally asymmetric — it exists for the chat owner but
157
+ // NOT the "other" inline editor. The chatFocused branch falls through to
158
+ // `tabSwitchAction` below, which in multi-question mode consumes Left/Right for tab
159
+ // switching; swallowing them here keeps Left/Right as in-editor caret movement for
160
+ // the chat input. The "other" inline editor flows through the separate
161
+ // `state.inputMode` branch (further down), which never calls `tabSwitchAction`, so it
162
+ // needs no equivalent guard. Do not "simplify" by unifying the two paths.
163
+ if (state.inputMode &&
164
+ state.inlineInputOwner === "chat" &&
165
+ (matchesKey(data, Key.left) || matchesKey(data, Key.right))) {
166
+ return { kind: "ignore" };
167
+ }
142
168
  const tab = tabSwitchAction(data, state, runtime);
143
169
  if (tab)
144
170
  return tab;