@slopus/happy-terminal 0.3.0-beta.13

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 (248) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE-CODEX +203 -0
  3. package/LICENSE-GROK-BUILD +204 -0
  4. package/LICENSE-HAPPY +21 -0
  5. package/README.md +60 -0
  6. package/THIRD-PARTY-NOTICES.md +34 -0
  7. package/dist/HappyTerminalUserError.d.ts +12 -0
  8. package/dist/app/AppTranscriptEntry.d.ts +33 -0
  9. package/dist/app/AppendOnlyStreamingRender.d.ts +12 -0
  10. package/dist/app/CodexFileDiff.d.ts +20 -0
  11. package/dist/app/CodexMcpToolCall.d.ts +25 -0
  12. package/dist/app/CodingAssistantAgentBackend.d.ts +81 -0
  13. package/dist/app/CodingAssistantApp.d.ts +128 -0
  14. package/dist/app/CompletedTurn.d.ts +11 -0
  15. package/dist/app/FileMentionAutocomplete.d.ts +30 -0
  16. package/dist/app/HappyTerminalProcessTerminal.d.ts +7 -0
  17. package/dist/app/NoticeChild.d.ts +4 -0
  18. package/dist/app/SecretMenuController.d.ts +31 -0
  19. package/dist/app/SelectionList.d.ts +24 -0
  20. package/dist/app/SessionDraftSync.d.ts +51 -0
  21. package/dist/app/StartupStatusApp.d.ts +34 -0
  22. package/dist/app/StartupStatusCardModel.d.ts +22 -0
  23. package/dist/app/TemporaryFullscreenController.d.ts +9 -0
  24. package/dist/app/TerminalColorLevel.d.ts +1 -0
  25. package/dist/app/TerminalOutputTrace.d.ts +12 -0
  26. package/dist/app/TerminalTheme.d.ts +11 -0
  27. package/dist/app/TranscriptEntryRenderCache.d.ts +10 -0
  28. package/dist/app/applyWorkflowRunUpdate.d.ts +2 -0
  29. package/dist/app/blendComposerBackground.d.ts +2 -0
  30. package/dist/app/boundedJsonStringify.d.ts +1 -0
  31. package/dist/app/calculateCacheHitPercent.d.ts +2 -0
  32. package/dist/app/codexDiffAnsi.d.ts +17 -0
  33. package/dist/app/compactCompletedTurnEntries.d.ts +2 -0
  34. package/dist/app/containsMarkdownTable.d.ts +1 -0
  35. package/dist/app/createAgentMarkdownTheme.d.ts +3 -0
  36. package/dist/app/createBackgroundTerminalViewer.d.ts +16 -0
  37. package/dist/app/createCompletedTurn.d.ts +6 -0
  38. package/dist/app/createEditorTheme.d.ts +3 -0
  39. package/dist/app/createFileMentionAutocompleteItems.d.ts +3 -0
  40. package/dist/app/createSecretInputPanel.d.ts +12 -0
  41. package/dist/app/createSelectionPanel.d.ts +14 -0
  42. package/dist/app/createSerialTaskQueue.d.ts +1 -0
  43. package/dist/app/createSessionPicker.d.ts +17 -0
  44. package/dist/app/createSlashCommands.d.ts +7 -0
  45. package/dist/app/createStopOnceHandler.d.ts +1 -0
  46. package/dist/app/createSubagentMonitor.d.ts +17 -0
  47. package/dist/app/createTerminalInputBurstHandler.d.ts +5 -0
  48. package/dist/app/createWorkflowMonitor.d.ts +15 -0
  49. package/dist/app/defaultTerminalTheme.d.ts +1 -0
  50. package/dist/app/describeModelChoice.d.ts +4 -0
  51. package/dist/app/describeProviderSignIn.d.ts +2 -0
  52. package/dist/app/describeReasoningLevel.d.ts +4 -0
  53. package/dist/app/detectCodexDiffLanguage.d.ts +1 -0
  54. package/dist/app/encodeModelChoice.d.ts +1 -0
  55. package/dist/app/endsAfterOpeningCodeFence.d.ts +1 -0
  56. package/dist/app/findFileMentionContext.d.ts +9 -0
  57. package/dist/app/findFileMentionPrefix.d.ts +5 -0
  58. package/dist/app/formatActivityElapsedTime.d.ts +1 -0
  59. package/dist/app/formatCodexMcpToolResult.d.ts +3 -0
  60. package/dist/app/formatCompactTokens.d.ts +1 -0
  61. package/dist/app/formatFileMention.d.ts +1 -0
  62. package/dist/app/formatProviderError.d.ts +6 -0
  63. package/dist/app/formatRelativeTime.d.ts +2 -0
  64. package/dist/app/formatResetDuration.d.ts +1 -0
  65. package/dist/app/formatSessionPickerEntry.d.ts +14 -0
  66. package/dist/app/formatSessionTokenStatus.d.ts +6 -0
  67. package/dist/app/formatSessionUsageSummary.d.ts +3 -0
  68. package/dist/app/formatStartupStatusUsageRemaining.d.ts +2 -0
  69. package/dist/app/formatStartupStatusUsageRows.d.ts +2 -0
  70. package/dist/app/formatSubagentToolCall.d.ts +10 -0
  71. package/dist/app/formatToolPermissionNotice.d.ts +3 -0
  72. package/dist/app/formatToolResultForDisplay.d.ts +2 -0
  73. package/dist/app/formatTurnUsageSummary.d.ts +2 -0
  74. package/dist/app/formatWorkUsageSummary.d.ts +14 -0
  75. package/dist/app/highlightAgentCode.d.ts +1 -0
  76. package/dist/app/highlightCodexDiffLine.d.ts +2 -0
  77. package/dist/app/highlightShellCommand.d.ts +1 -0
  78. package/dist/app/humanizeGoalStatus.d.ts +2 -0
  79. package/dist/app/humanizeMcpName.d.ts +1 -0
  80. package/dist/app/humanizePermissionMode.d.ts +2 -0
  81. package/dist/app/humanizePermissionReviewLevel.d.ts +2 -0
  82. package/dist/app/humanizeProviderId.d.ts +1 -0
  83. package/dist/app/humanizeReasoningLevel.d.ts +1 -0
  84. package/dist/app/humanizeSubagentStatus.d.ts +2 -0
  85. package/dist/app/humanizeToolName.d.ts +1 -0
  86. package/dist/app/humanizeWorkflowStatus.d.ts +2 -0
  87. package/dist/app/installResumeInstructions.d.ts +26 -0
  88. package/dist/app/installTerminalCrashCleanup.d.ts +26 -0
  89. package/dist/app/isLightTerminalBackground.d.ts +2 -0
  90. package/dist/app/layoutCodexFileDiff.d.ts +13 -0
  91. package/dist/app/looksLikeAuthenticationFailure.d.ts +5 -0
  92. package/dist/app/nearestXtermColorIndex.d.ts +2 -0
  93. package/dist/app/parseCodexMcpToolInvocation.d.ts +2 -0
  94. package/dist/app/parseSessionCommand.d.ts +6 -0
  95. package/dist/app/providerErrorResetAt.d.ts +2 -0
  96. package/dist/app/providerQuotaToStartupStatusUsage.d.ts +3 -0
  97. package/dist/app/readClipboardImage.d.ts +9 -0
  98. package/dist/app/renderActivityWave.d.ts +3 -0
  99. package/dist/app/renderAgentMarkdown.d.ts +8 -0
  100. package/dist/app/renderBackgroundTerminalCompletion.d.ts +1 -0
  101. package/dist/app/renderBackgroundTerminalInteraction.d.ts +2 -0
  102. package/dist/app/renderBackgroundTerminalSummary.d.ts +1 -0
  103. package/dist/app/renderChildRows.d.ts +13 -0
  104. package/dist/app/renderCodexDiffLine.d.ts +3 -0
  105. package/dist/app/renderCodexFileDiff.d.ts +7 -0
  106. package/dist/app/renderCodexMcpToolCall.d.ts +2 -0
  107. package/dist/app/renderCompletedTurnStats.d.ts +2 -0
  108. package/dist/app/renderExecCommand.d.ts +11 -0
  109. package/dist/app/renderExploration.d.ts +9 -0
  110. package/dist/app/renderFullscreenComponent.d.ts +8 -0
  111. package/dist/app/renderHappyTerminalBanner.d.ts +6 -0
  112. package/dist/app/renderHappyTerminalVersion.d.ts +1 -0
  113. package/dist/app/renderNoticeWithChildren.d.ts +7 -0
  114. package/dist/app/renderPendingSteeringMessages.d.ts +1 -0
  115. package/dist/app/renderStartupStatusCard.d.ts +7 -0
  116. package/dist/app/renderSubagentSummary.d.ts +6 -0
  117. package/dist/app/renderTurnCompletionSeparator.d.ts +1 -0
  118. package/dist/app/renderWorkflowSummary.d.ts +1 -0
  119. package/dist/app/resolveInputBackground.d.ts +3 -0
  120. package/dist/app/resolveStartupProviderQuota.d.ts +3 -0
  121. package/dist/app/resolveStartupSessionId.d.ts +18 -0
  122. package/dist/app/resolveTerminalColorLevel.d.ts +2 -0
  123. package/dist/app/resolveTerminalStyle.d.ts +1 -0
  124. package/dist/app/resolveTerminalTheme.d.ts +5 -0
  125. package/dist/app/runApp.d.ts +27 -0
  126. package/dist/app/runClipboardCommand.d.ts +8 -0
  127. package/dist/app/sanitizeTerminalText.d.ts +1 -0
  128. package/dist/app/shortenHomePath.d.ts +2 -0
  129. package/dist/app/sortSubagentsForDisplay.d.ts +2 -0
  130. package/dist/app/subagentElapsedMs.d.ts +2 -0
  131. package/dist/app/subagentLogMessageText.d.ts +2 -0
  132. package/dist/app/surfaceThemedLine.d.ts +3 -0
  133. package/dist/app/truncatePathToWidth.d.ts +1 -0
  134. package/dist/app/truncateTextForDisplay.d.ts +5 -0
  135. package/dist/app/truncateUtf8BytesForDisplay.d.ts +1 -0
  136. package/dist/app/upsertSubagentSummary.d.ts +2 -0
  137. package/dist/app/workflowDisplay.d.ts +2 -0
  138. package/dist/client/HappyAgentEventHub.d.ts +25 -0
  139. package/dist/client/RemoteAgent.d.ts +74 -0
  140. package/dist/client/RemoteAgentRunError.d.ts +4 -0
  141. package/dist/client/RemoteTerminalAttachment.d.ts +19 -0
  142. package/dist/client/RemoteTerminalClientReplica.d.ts +14 -0
  143. package/dist/client/ensureLocalProtocolServer.d.ts +10 -0
  144. package/dist/client/fetchProviderQuotas.d.ts +10 -0
  145. package/dist/client/index.d.ts +8 -0
  146. package/dist/client/loadAgentCatalog.d.ts +32 -0
  147. package/dist/config/createProjectConfigSecurityNotice.d.ts +10 -0
  148. package/dist/config/defaultConfig.d.ts +2 -0
  149. package/dist/config/getDefaultGlobalConfigPath.d.ts +1 -0
  150. package/dist/config/getDefaultLocalConfigPath.d.ts +1 -0
  151. package/dist/config/getDefaultRuntimeConfigPath.d.ts +1 -0
  152. package/dist/config/getHappyConfigDirectory.d.ts +1 -0
  153. package/dist/config/getHappyTerminalHome.d.ts +1 -0
  154. package/dist/config/index.d.ts +15 -0
  155. package/dist/config/loadConfig.d.ts +2 -0
  156. package/dist/config/mergeConfigValues.d.ts +2 -0
  157. package/dist/config/parseConfigToml.d.ts +7 -0
  158. package/dist/config/readConfigFile.d.ts +2 -0
  159. package/dist/config/readProjectConfigFile.d.ts +2 -0
  160. package/dist/config/resolveConfigPaths.d.ts +2 -0
  161. package/dist/config/runtimeConfigLock.d.ts +1 -0
  162. package/dist/config/types.d.ts +74 -0
  163. package/dist/config/updateRuntimeConfig.d.ts +2 -0
  164. package/dist/config/updateRuntimePreferences.d.ts +2 -0
  165. package/dist/config/withoutProjectMachineSettings.d.ts +7 -0
  166. package/dist/config/writeRuntimeConfig.d.ts +3 -0
  167. package/dist/config/writeRuntimeConfigDefaults.d.ts +2 -0
  168. package/dist/daemon/createUnixSocketFetch.d.ts +2 -0
  169. package/dist/daemon/daemonPid.d.ts +11 -0
  170. package/dist/daemon/ensureHappyAgentBinary.d.ts +15 -0
  171. package/dist/daemon/ensureLocalProtocolServer.d.ts +24 -0
  172. package/dist/daemon/getHappyDaemonPaths.d.ts +16 -0
  173. package/dist/daemon/happyAgentBinaryConfig.d.ts +15 -0
  174. package/dist/daemon/index.d.ts +5 -0
  175. package/dist/daemon/runDaemonCommand.d.ts +4 -0
  176. package/dist/debug/DebugLog.d.ts +11 -0
  177. package/dist/debug/closeNodeInspector.d.ts +1 -0
  178. package/dist/debug/createDebugJsonReplacer.d.ts +1 -0
  179. package/dist/debug/createRequestDebugDirectory.d.ts +1 -0
  180. package/dist/debug/getDebugRootDirectory.d.ts +1 -0
  181. package/dist/debug/getNodeInspectorUrl.d.ts +1 -0
  182. package/dist/debug/index.d.ts +8 -0
  183. package/dist/debug/openNodeInspector.d.ts +1 -0
  184. package/dist/debug/registerHappyTerminalDebugRoot.d.ts +5 -0
  185. package/dist/docs/DESIGN.md +2191 -0
  186. package/dist/docs/README.md +93 -0
  187. package/dist/docs/agents-and-collaboration.md +365 -0
  188. package/dist/docs/architecture.md +468 -0
  189. package/dist/docs/extending.md +561 -0
  190. package/dist/docs/happy.md +386 -0
  191. package/dist/docs/permissions-and-sandbox.md +342 -0
  192. package/dist/docs/workspaces.md +281 -0
  193. package/dist/errorToMessage.d.ts +1 -0
  194. package/dist/formatCliFailure.d.ts +10 -0
  195. package/dist/index.d.ts +3 -0
  196. package/dist/index.js +17151 -0
  197. package/dist/main.js +18946 -0
  198. package/dist/observability/daemonContext.d.ts +16 -0
  199. package/dist/observability/index.d.ts +1 -0
  200. package/dist/processes/BoundedOutputBuffer.d.ts +20 -0
  201. package/dist/processes/NativeProcessManager.d.ts +66 -0
  202. package/dist/processes/ProcessGroupReaper.d.ts +32 -0
  203. package/dist/processes/index.d.ts +7 -0
  204. package/dist/processes/isProcessRunning.d.ts +1 -0
  205. package/dist/processes/isTargetProcessAlive.d.ts +1 -0
  206. package/dist/processes/killProcessTree.d.ts +2 -0
  207. package/dist/processes/resolveSystemShell.d.ts +1 -0
  208. package/dist/processes/startProcessTransport.d.ts +33 -0
  209. package/dist/processes/types.d.ts +41 -0
  210. package/dist/processes/waitForProcessExit.d.ts +2 -0
  211. package/dist/protocol/AppletProtocol.d.ts +101 -0
  212. package/dist/protocol/Attachment.d.ts +96 -0
  213. package/dist/protocol/ClientProtocolTypes.d.ts +481 -0
  214. package/dist/protocol/DocumentProtocol.d.ts +109 -0
  215. package/dist/protocol/EventId.d.ts +1 -0
  216. package/dist/protocol/FolderProtocol.d.ts +236 -0
  217. package/dist/protocol/FolderSharingProtocol.d.ts +97 -0
  218. package/dist/protocol/GitHubPluginProtocol.d.ts +43 -0
  219. package/dist/protocol/GlobalSecurityProtocol.d.ts +7 -0
  220. package/dist/protocol/HappyCloudProtocol.d.ts +178 -0
  221. package/dist/protocol/InstallationProtocol.d.ts +80 -0
  222. package/dist/protocol/OnboardingProtocol.d.ts +47 -0
  223. package/dist/protocol/P2pCredentialProtocol.d.ts +273 -0
  224. package/dist/protocol/P2pIdentityProtocol.d.ts +8 -0
  225. package/dist/protocol/P2pPairingProtocol.d.ts +68 -0
  226. package/dist/protocol/P2pProtocol.d.ts +185 -0
  227. package/dist/protocol/PluginWireTypes.d.ts +81 -0
  228. package/dist/protocol/ProfileProtocol.d.ts +127 -0
  229. package/dist/protocol/ProjectFileProtocol.d.ts +63 -0
  230. package/dist/protocol/ProjectProtocol.d.ts +646 -0
  231. package/dist/protocol/ProtocolVersion.d.ts +8 -0
  232. package/dist/protocol/ServiceNotice.d.ts +175 -0
  233. package/dist/protocol/SessionProtocol.d.ts +1156 -0
  234. package/dist/protocol/SharingProtocol.d.ts +159 -0
  235. package/dist/protocol/SlotProtocol.d.ts +270 -0
  236. package/dist/protocol/TimelineProtocol.d.ts +88 -0
  237. package/dist/protocol/WorkletProtocol.d.ts +159 -0
  238. package/dist/protocol/createEventIdFactory.d.ts +7 -0
  239. package/dist/protocol/index.d.ts +34 -0
  240. package/dist/readPackageVersion.d.ts +1 -0
  241. package/dist/readPackageVersion.js +16 -0
  242. package/dist/reportCliFailure.d.ts +2 -0
  243. package/dist/runHappyTerminal.d.ts +6 -0
  244. package/dist/terminal/index.d.ts +2 -0
  245. package/dist/terminal/types.d.ts +39 -0
  246. package/dist/types.d.ts +6 -0
  247. package/dist/writeStderrSync.d.ts +5 -0
  248. package/package.json +97 -0
@@ -0,0 +1,342 @@
1
+ # Permissions, sandboxing, and shell behavior
2
+
3
+ This document describes what an agent running inside Happy Agent is allowed to do, how a
4
+ restricted action can be escalated, and why a refused action must not be
5
+ retried by another route. It is written for coding agents, and it describes the
6
+ behavior that is actually implemented.
7
+
8
+ ## One permission model for every provider
9
+
10
+ Happy Agent has a single permission model. Codex, Claude, Grok, and MCP tools all
11
+ execute through the same `AgentContext`, the same filesystem boundary, the same
12
+ shell sandbox, and the same `PermissionContext`. Provider differences exist only
13
+ in tool names, argument schemas, result formatting, and model-facing guidance.
14
+ There is no provider-specific security path in the agent loop.
15
+
16
+ Two consequences matter in practice:
17
+
18
+ - Running the "other provider's" tool never widens what you may do. `Bash`,
19
+ `exec_command`, and `run_terminal_command` are the same sandboxed execution
20
+ with different argument names.
21
+ - Every tool declares its own permission behavior on its definition. The loop
22
+ never decides anything from a tool name, a name prefix, a provider key, or a
23
+ guess about command contents.
24
+
25
+ The relevant fields on a tool definition are:
26
+
27
+ | Field | Meaning |
28
+ | --------------------------------- | ----------------------------------------------------------------------------------------------------------- |
29
+ | `shouldReviewInAutoMode` | Required. Whether this exact invocation must be reviewed in Auto. |
30
+ | `shouldRunInFullAccessInAutoMode` | Whether an allowed review also grants this one execution Full access. Review alone never implies elevation. |
31
+ | `requiresAutoOrFullAccess` | The tool acts outside Happy Agent's local sandbox and cannot run at all in Read only or Workspace write. |
32
+ | `describeAutoPermissionAction` | Human-readable description of the exact reviewed boundary. Required whenever a review can happen. |
33
+ | `autoPermissionInstructions` | Provider-specific Auto guidance injected into the system prompt only while the tool is active. |
34
+ | `availableToPermissionReviewer` | Whether the read-only reviewer agent may call the tool while investigating. |
35
+
36
+ ## The four permission modes
37
+
38
+ The mode is `"read_only" | "workspace_write" | "auto" | "full_access"`, and Auto
39
+ is the default.
40
+ The mode lives in a `PermissionContext`, which exposes the current `mode`, a
41
+ `revision` counter, `setMode`, and `runWithMode` for a scoped temporary
42
+ override.
43
+
44
+ ### Read only
45
+
46
+ - File tools may inspect files; any write fails with "File changes are disabled
47
+ in read-only mode."
48
+ - Shell commands run, but nothing on the host is writable except temporary
49
+ directories. The workspace itself is not writable.
50
+ - Shell network access is blocked; no managed proxy is created for this mode.
51
+ - On macOS and Linux, restricted reads follow Codex and may inspect the host
52
+ filesystem. `assertCanReadPath` only enforces its sensitive-path denial on
53
+ other platforms, where reading a private path outside the workspace fails with
54
+ a message pointing at Full access.
55
+ - On macOS, no local unix socket may be created in this mode.
56
+
57
+ ### Workspace write
58
+
59
+ - File changes are allowed inside the working directory.
60
+ - Writes outside the working directory are refused: "Workspace write mode cannot
61
+ modify files outside the working directory."
62
+ - The project config file (`happy.toml`) is refused.
63
+ - Protected Git control paths are refused: "Workspace write mode cannot modify
64
+ Git control files without Full access."
65
+ - Shell writes are confined to the working directory, its Git control paths, and
66
+ temporary directories. Shell network access is blocked except for destinations
67
+ the user allowed in configuration, reached through Happy Agent's managed proxy.
68
+
69
+ ### Auto
70
+
71
+ Auto is Workspace write plus automatic review.
72
+
73
+ - Routine reads and workspace edits run with no review at all.
74
+ - Every shell tool uses the Workspace write sandbox **by default**.
75
+ - A tool may request review for one exact action. Review is automatic and never
76
+ becomes a question to the user.
77
+ - Only a tool whose `shouldRunInFullAccessInAutoMode` returns true receives a
78
+ temporary Full access override, and only for that single execution. The loop
79
+ re-checks the mode immediately before starting the override and restores Auto
80
+ as soon as the call settles.
81
+ - Tools with `requiresAutoOrFullAccess` (all MCP operations) are available.
82
+
83
+ ### Full access
84
+
85
+ Happy Agent's filesystem, shell, and network restrictions are removed.
86
+ `createSandboxedCommand` returns the raw command with no sandbox wrapper, and
87
+ `assertCanReadPath` / `assertCanWritePath` return immediately. Full access does
88
+ not claim to provide the restricted sandbox boundary — it is the absence of it.
89
+ Custom shells (`shell:` on `exec_command`) are available only in Full access.
90
+
91
+ ## How Auto review works
92
+
93
+ The agent loop prepares a tool's permission before executing it:
94
+
95
+ 1. If the mode is not Auto, nothing is reviewed.
96
+ 2. The tool's `shouldReviewInAutoMode` is called with the real arguments. If it
97
+ returns false, the call executes normally inside the sandbox.
98
+ 3. Otherwise `describeAutoPermissionAction` produces the exact action text. A
99
+ tool that requests review without defining it fails with an error instead of
100
+ executing.
101
+ 4. A read-only side agent — Codex's Guardian policy, plus the user's global
102
+ `SECURITY.md` if present — reviews the action against the durable
103
+ conversation transcript. It may make read-only tool calls while deciding.
104
+ Its own permission mode is never Auto, so a review cannot recurse.
105
+ 5. The verdict is `allow` or `deny`. There is no third outcome and no prompt to
106
+ the user.
107
+
108
+ ### What counts as authorization
109
+
110
+ The reviewer sees a role-aware transcript built by
111
+ `createAutoPermissionTranscript`, not a compacted model-context suffix. Real
112
+ user messages and trusted answers to interactive questions are authorization
113
+ evidence and are preserved preferentially inside the review budget. Assistant
114
+ text, tool arguments, tool output, repository content, generated summaries, and
115
+ anything injected into a file are **not** authorization.
116
+
117
+ The verdict is re-derived from the reviewer's own classification
118
+ (`shouldAllowAutoPermissionReview`): a `critical` risk is never allowed
119
+ automatically, and a `high` risk is allowed only with medium or high user
120
+ authorization. If user evidence had to be dropped to fit the budget and the risk
121
+ is above `low`, the action is denied — the review fails closed.
122
+
123
+ ### Denial semantics
124
+
125
+ `describeAutoPermissionDenial` produces the agent-facing result. There are three
126
+ distinct outcomes and they do not mean the same thing:
127
+
128
+ - **Rejected.** A real judgement was made. You must not pursue the same outcome
129
+ by another route, by splitting it into smaller steps, or by working around the
130
+ restriction. Continue only with a materially safer alternative; otherwise stop
131
+ and tell the user what you wanted to do and why it was refused, so they can
132
+ decide. If the user then approves it in their own words, that reply is real
133
+ authorization and the next review can allow it.
134
+ - **Timed out.** The review did not finish within its 90-second budget. The
135
+ action is unproven, not unsafe. You may try once more, or ask the user.
136
+ - **Unavailable.** No reviewer could run, or no reviewer exists for the session.
137
+ No judgement was made about the action. Continue with work that does not need
138
+ the permission, or ask the user. Auto without a reviewer never falls back to
139
+ silent execution.
140
+
141
+ ### Refusal loops must end the turn
142
+
143
+ Because Auto never interrupts the user, nothing outside the agent can break a
144
+ loop of refusals. `AutoPermissionDenialCircuitBreaker` therefore stops the turn
145
+ itself: 3 refusals in a row, or 10 refusals within the last 50 decisions. A
146
+ successful action clears the consecutive counter but not the windowed one. When
147
+ it trips, the turn ends with an instruction to tell the user what you were
148
+ trying to do and why it kept being refused.
149
+
150
+ Treat that as the floor, not the target. An agent that reacts to a refusal by
151
+ trying a variation of the same action is doing the one thing the denial text
152
+ explicitly forbids.
153
+
154
+ ## Escalation syntax per provider
155
+
156
+ The escalation field is provider-shaped, but every one of them requests the same
157
+ runtime behavior: in Auto, review the action first; if allowed, scope only that
158
+ one tool execution to `full_access`, then restore Auto immediately.
159
+
160
+ | Provider tool | Field | Where the reason goes |
161
+ | --------------------------- | ------------------------------------------ | --------------------- |
162
+ | Codex `exec_command` | `sandbox_permissions: "require_escalated"` | `justification` |
163
+ | Claude `Bash` | `dangerouslyDisableSandbox: true` | `description` |
164
+ | Grok `run_terminal_command` | `sandbox_permissions: "require_escalated"` | `description` |
165
+
166
+ A Pi `bash` tool with `sandbox_permissions: "require_escalated"` and a
167
+ `justification` is sometimes described alongside these. Happy Agent currently ships the
168
+ Codex, Claude, and Grok shell surfaces only; the Pi surface is part of the stated
169
+ model, not of what runs today.
170
+
171
+ Rules that apply to all of them:
172
+
173
+ - Omitting the field, or setting it to `use_default` / `false`, keeps the
174
+ command sandboxed and skips review entirely.
175
+ - In Read only or Workspace write the field does not bypass the selected mode.
176
+ - Reviewed actions that do not need host access stay in the current sandbox.
177
+ Sending input to an existing shell session (`write_stdin`, `TaskInput`) is
178
+ reviewed whenever the input is non-empty, but it is never elevated.
179
+
180
+ File tools follow the same rule from the other direction. `Read`, `Write`,
181
+ `Edit`, and `apply_patch` extract their real path argument and call the shared
182
+ boundary helpers. A path outside the workspace, a symlink escape, a protected
183
+ Git control path, or the project config file triggers review and, when allowed,
184
+ the temporary elevation for that one call. Paths under the user's skill roots
185
+ are readable without review.
186
+
187
+ ## Sandbox limits
188
+
189
+ These limits apply to shell commands in Read only, Workspace write, and Auto.
190
+ They are enforced by the platform sandbox, not by inspecting your command text,
191
+ so rewriting the command does not change the outcome.
192
+
193
+ - **Writes** are confined to the working directory, its Git control paths, and
194
+ temporary directories. Everything else on the host is readable but not
195
+ writable. In Read only, only temporary directories are writable.
196
+ - **Unix sockets** must live inside the working directory. On macOS a socket
197
+ anywhere else is refused, including in a temporary directory, and the home
198
+ directory (or any ancestor of it) is never granted socket scope — that is
199
+ exactly where `~/.docker`, `~/.gnupg`, and agent sockets live. The host's own
200
+ sockets — the Docker daemon, the SSH agent, Happy Agent's control socket — are
201
+ unreachable by design.
202
+ - **Local port binding**: on macOS, binding a local TCP or UDP port is refused
203
+ unless the user enabled `network.allowLocalBinding` in configuration. On Linux
204
+ and inside Docker the command gets its own network namespace (`--unshare-net`),
205
+ so a listener it starts is reachable only from inside that command.
206
+ - **Outbound network** is blocked except for domains and ports the user allowed,
207
+ which are reached through Happy Agent's managed HTTP/SOCKS proxy. The allowlist comes
208
+ from `network.allowedDomains` with `network.allowedPorts` (default `[443]`);
209
+ `network.deniedDomains` overrides it. A blocked request explains itself: not in
210
+ the allowlist, in the denylist, DNS could not be resolved safely within two
211
+ seconds, or the destination resolves to a local or private address. Happy Agent owns
212
+ the proxy environment variables; unsetting them cannot grant direct access.
213
+ Only the user can change the policy, in the repository's `happy.toml` or the
214
+ global config.
215
+ - **Keychain**: on macOS the keychain is unavailable. `security`, and anything
216
+ backed by it, fails or reports nothing rather than returning a secret. Treat
217
+ every other system credential store the same way. Secrets reach a command only
218
+ through the `secrets` argument, which injects an attached session bundle.
219
+ - **Protected paths** are read-only even inside the workspace: `.agents`,
220
+ `.codex`, the project config files, and Happy Agent's own server directory, socket,
221
+ and token paths.
222
+
223
+ When a limit blocks necessary work: in Auto, request reviewed full-access
224
+ execution for that one command and explain why. In Read only or Workspace write,
225
+ stop and tell the user which limit it was. A sandbox refusal is not a bug in your
226
+ command and is not something to route around.
227
+
228
+ ### Platform implementations
229
+
230
+ - **macOS** uses the system Seatbelt sandbox at `/usr/bin/sandbox-exec` with a
231
+ closed-by-default policy adapted from Codex: all reads allowed, writes only to
232
+ the computed writable roots, explicit denials for protected paths, PTY support,
233
+ and no network unless local binding or loopback ports were configured.
234
+ - **Linux** uses Bubblewrap: `/` bound read-only, `--dev /dev`, a private
235
+ `/tmp`, explicit `--bind` for writable roots, `--ro-bind` for protected paths,
236
+ and `--unshare-user --unshare-pid --unshare-net`. When nested procfs mounting
237
+ is unavailable, an empty private `/proc` is the fallback. `socat` bridges the
238
+ managed proxy into the namespace over unix sockets with a command-scoped
239
+ authentication token.
240
+ - **Other platforms** fall back to `@anthropic-ai/sandbox-runtime` with a
241
+ generated settings file.
242
+ - Restricted execution fails closed with a readable explanation when a sandbox
243
+ dependency is missing.
244
+
245
+ The repository's root `happy.toml` is part of the sandbox boundary because it
246
+ can grant managed network access to later commands. Restricted commands see an
247
+ existing file read-only. When it does not exist, it remains absent before,
248
+ during, and after the command; Happy Agent never creates a placeholder or other
249
+ synthetic file at that path.
250
+
251
+ ## Shell and background processes
252
+
253
+ A shell command starts with a wait, not a life expectancy.
254
+
255
+ - The wait is a timeout on _you_, not on the command. When it expires the
256
+ command is not killed: it moves to the background and you get its output so
257
+ far plus a session ID to come back to.
258
+ - Claude `Bash`: `timeout` in milliseconds, default 120000, max 600000.
259
+ `run_in_background: true` starts it in the background immediately and waits
260
+ only about 3 seconds to confirm it did not fall over.
261
+ - Codex `exec_command`: `yield_time_ms`, default 10000 ms, effective range
262
+ 250–30000 ms.
263
+ - Every provider can run in the background, read only what accumulated since the
264
+ last read, write to stdin, and stop a process.
265
+ - Stopping is graceful first and forceful about 2 seconds later, and it takes
266
+ the whole process tree.
267
+ - A background process that exits without having been read to the end produces a
268
+ developer message saying only that it ended. No output is attached; read it
269
+ with the normal read tool, which keeps answering for a while after the exit.
270
+ Do not poll for exit.
271
+ - Lifetime: a background process belongs to the session that started it and
272
+ lives as long as that session's runtime inside the daemon. Cancelling a turn
273
+ does not kill it. Archiving the session or exiting the daemon kills everything
274
+ it started.
275
+ - At most 64 active sessions per session runtime. Passing the cap evicts and
276
+ kills the oldest instead of failing your command; the evicted session stays
277
+ readable.
278
+
279
+ ### TTY behavior
280
+
281
+ `tty` defaults to false, which uses pipes. Ask for a TTY only when a program
282
+ behaves differently without one. A PTY-backed command gets an 80×24 terminal and
283
+ an environment that discourages terminal output: `TERM=dumb`, `NO_COLOR=1`,
284
+ `COLORTERM=""`, and `PAGER`, `GIT_PAGER`, `GH_PAGER` forced to `cat`. Nothing is
285
+ done about full-screen applications beyond that. Interactive flags such as
286
+ `git rebase -i` are not supported.
287
+
288
+ ## MCP
289
+
290
+ An MCP server executes outside Happy Agent's local filesystem sandbox, so Happy Agent cannot
291
+ enforce its boundary locally.
292
+
293
+ - Every MCP tool sets `requiresAutoOrFullAccess: true`. In Read only or
294
+ Workspace write the call fails with "This action requires Auto or Full access
295
+ because it can operate outside Happy Agent's local sandbox."
296
+ - Every direct and dynamic MCP tool invocation is reviewed in Auto:
297
+ `shouldReviewInAutoMode: () => true`, including `call_mcp_tool` and
298
+ `get_mcp_prompt`.
299
+ - Server-supplied annotations such as `readOnlyHint` are untrusted metadata.
300
+ They are never authorization evidence and never a reason to skip review.
301
+ - Happy Agent-owned protocol operations that are intrinsically read-only skip review:
302
+ `list_mcp_tools`, `list_mcp_resources`, `list_mcp_resource_templates`,
303
+ `read_mcp_resource`, `list_mcp_prompts`.
304
+ - The approval text discloses the external boundary explicitly: the server can
305
+ perform actions outside Happy Agent's filesystem sandbox.
306
+
307
+ ## Docker sandbox environments
308
+
309
+ Happy Agent can run a session's commands inside a Docker container. The same permission
310
+ model applies; Docker is the outer isolation, not a replacement for the inner
311
+ sandbox.
312
+
313
+ An image or existing container used as a Happy Agent execution environment must contain
314
+ Bubblewrap and `socat`, and must allow the nested namespaces Bubblewrap needs.
315
+ `prepareDockerSandbox` probes for exactly that and fails with an actionable
316
+ message: install `bubblewrap` and `socat` in the image, and when connecting to an
317
+ existing container start it with `--security-opt seccomp=unconfined`.
318
+
319
+ Inside the container, a restricted command runs under Bubblewrap with `/` bound
320
+ read-only, a private `/tmp`, `--unshare-net`, the workspace bound writable (or
321
+ read-only in Read only mode), and `.agents`, `.codex`, and the project config
322
+ files bound read-only. Managed proxy access reaches the container through
323
+ temporary unix sockets shared by the working-directory bind mount, beneath a
324
+ root every restricted command sees read-only, with unguessable command-scoped
325
+ authentication. A restricted Docker command does not inherit the container's
326
+ parent process table or another command's temporary process-control state.
327
+
328
+ Proxy processes, socket bridges, and temporary directories are removed when a
329
+ command finishes or fails.
330
+
331
+ ## Quick reference for agents
332
+
333
+ - Assume Auto. Read and edit inside the workspace freely; that path has no
334
+ review at all.
335
+ - Escalate only when the sandbox actually blocks necessary work, with one
336
+ command and a concrete reason. Do not escalate speculatively — an escalated
337
+ command is reviewed and may be refused.
338
+ - A refusal is a decision about the proposed action only. It is not a durable
339
+ rule, and an allow is not authorization for anything later.
340
+ - Never work around a denial. Take a materially safer route, or stop and explain.
341
+ - Never assume a background process needs polling, and never assume a timeout
342
+ killed a command.
@@ -0,0 +1,281 @@
1
+ # Workspaces
2
+
3
+ A workspace is a folder an agent works in. In a Git project a workspace is a
4
+ worktree: its own checkout, on its own branch, cut from the project.
5
+
6
+ Every conversation already runs inside a workspace — the working directory. Most
7
+ work needs no workspace management at all. This document explains what a
8
+ workspace is, how one is created and archived, where it lives on disk, how to
9
+ start work inside another workspace, and when creating one is actually the right
10
+ call.
11
+
12
+ ## What a workspace is
13
+
14
+ Happy Agent knows about _projects_ and _workspaces_:
15
+
16
+ - A **project** is a folder Happy Agent has been pointed at. It has an ID, a name, and a
17
+ storage key derived from the name.
18
+ - A **workspace** belongs to one project. It has an ID, a name, a path, a status,
19
+ and — when this session created it — an `owned` flag.
20
+
21
+ A workspace's status moves through `initializing` → `ready`, or ends at `failed`.
22
+ Archiving moves it to `archiving` and then `archived`. Only a `ready` workspace
23
+ can be worked in: starting an agent in one that is still initializing is
24
+ refused with a message saying so.
25
+
26
+ Managed workspaces require a Git repository project. `create_workspace` fails
27
+ with "Managed workspaces require a Git repository project." when the project
28
+ folder is not the top level of a repository. Copying the folder for non-Git
29
+ projects is planned, not implemented.
30
+
31
+ ## Where workspaces live
32
+
33
+ Workspaces are created under a managed root, one directory per project:
34
+
35
+ ```text
36
+ <workspaces root>/<project storage key>/<workspace storage key>
37
+ ```
38
+
39
+ The root depends on the platform:
40
+
41
+ | Platform | Root |
42
+ | -------- | ----------------------------------------------------------------- |
43
+ | macOS | `~/Happy/Workspaces` |
44
+ | Linux | `~/happy/workspaces` |
45
+ | Any | `HAPPY_AGENT_WORKSPACES_DIRECTORY` (absolute path) overrides both |
46
+
47
+ Storage keys are slugs of the display name (lowercase, ASCII, dashes, at most 48
48
+ characters). A collision gets a numeric suffix: `workspace`, `workspace-2`,
49
+ `workspace-3`. The absolute path is persisted when the workspace is reserved and
50
+ stays authoritative afterwards, so changing the root only affects new
51
+ workspaces.
52
+
53
+ Never guess a workspace path. Use the `path` that `create_workspace`,
54
+ `list_workspaces`, or `delegate_to_workspace` returns.
55
+
56
+ ## Naming
57
+
58
+ A workspace is created with a placeholder name, and takes the name of its first
59
+ chat once that chat is named. Its branch follows: renaming a workspace, whether
60
+ the first chat does it or a person does, renames the Git branch to match. The
61
+ folder never moves, because a chat is already working inside it.
62
+
63
+ A workspace a person has named is never renamed again by a chat.
64
+
65
+ ## How a workspace is created
66
+
67
+ `create_workspace` takes a human-readable `name` and an optional `base_ref`:
68
+
69
+ ```json
70
+ { "name": "Retry policy rewrite" }
71
+ ```
72
+
73
+ What happens, in order:
74
+
75
+ 1. **The base commit is resolved.**
76
+ With no `base_ref`, Happy Agent fetches `origin` and forks `origin/<trunk>` — the
77
+ trunk as the remote has it, not whatever the project folder happens to be
78
+ checked out on. It falls back to the local branch when there is no reachable
79
+ `origin`. An explicit `base_ref` is used exactly as given, and an unknown ref
80
+ is an error. Fetching only writes remote-tracking refs and objects; the
81
+ project's own working tree, `HEAD`, and local branches are never touched.
82
+ 2. **The workspace row is reserved** with a unique name, storage key, and path,
83
+ and the workspace is published as `initializing`. The tool returns at this
84
+ point.
85
+ 3. **The worktree is materialized in the background**: `git worktree add -b
86
+ worktree/<branch key> <path> <commit>`, then Git's answer is verified — the
87
+ worktree must be at exactly the requested path and belong to the expected
88
+ repository.
89
+ 4. **Configured sync files are replicated** — every path in `workspace.sync`
90
+ and `workspace.protected_sync` that exists in the project root is copied
91
+ into the workspace. This shares files Git cannot provide, such as gitignored
92
+ `.env` files. While the daemon runs, the project root's copies are watched
93
+ and re-copied to every ready workspace whenever they change. Sync is
94
+ one-way and best-effort: the root copy always wins, deletions in the root
95
+ are not replicated, and a missed event catches up on the next change. Paths
96
+ in `protected_sync` are additionally write-protected in workspaces, exactly
97
+ like `permissions.protected_paths`, so sessions cannot modify their copy
98
+ without Full access.
99
+ 5. **Setup commands run** — `workspace.setup_commands` from the configuration
100
+ loaded inside the new workspace (for example `pnpm install --frozen-lockfile`).
101
+ 6. The workspace is marked `ready`. A failure at any step marks it `failed`.
102
+
103
+ Two consequences worth remembering:
104
+
105
+ - A worktree is **always** a branch, created with the worktree, named
106
+ `worktree/<branch key>` — the workspace name in kebab-case, with a numeric
107
+ suffix when Git or another workspace already holds it. Other tooling keys off
108
+ branch names, so do not rename branches casually.
109
+ - Creation is not instant: the checkout and its setup commands take real time,
110
+ and a fresh workspace has no warm build cache or context.
111
+
112
+ Creation is idempotent by identity: repeating a create with the same requested
113
+ ID answers with the same workspace rather than making a second one. One create
114
+ never produces two entries.
115
+
116
+ ## Ownership
117
+
118
+ A workspace records the session that created it. Only that session may:
119
+
120
+ - archive it with `archive_workspace`;
121
+ - start a hidden workspace agent in it with `spawn_workspace_agent`.
122
+
123
+ `list_workspaces` marks these with `owned: true`. A workspace created by another
124
+ session is not yours to move into. Reuse an existing workspace only to continue
125
+ the work already living in it, or when the user explicitly points you at it. If
126
+ coordinating across tasks seems to genuinely require somebody else's workspace,
127
+ ask the user first.
128
+
129
+ ## Archiving
130
+
131
+ Archiving is an immediate, irreversible logical action. `archive_workspace` marks
132
+ the workspace `archiving`, stops its setup work, and removes it from the active
133
+ list; removing the worktree and the folder is background cleanup afterwards. If
134
+ cleanup fails the workspace still ends up archived and the failure is logged —
135
+ archival is never rolled back because a folder could not be deleted.
136
+
137
+ Archive an owned workspace when its work is finished or abandoned. Do not keep
138
+ workspaces around "for later": a later task gets a fresh one.
139
+
140
+ `archive_workspace` is reviewed in Auto mode, and the review text names the
141
+ workspace and says its managed worktree will be removed.
142
+
143
+ ## Working inside a workspace
144
+
145
+ Work in a workspace runs _from inside_ it. There are two ways to start it, and
146
+ they differ in whether the user sees a conversation.
147
+
148
+ ### `spawn_workspace_agent` — hidden subagent
149
+
150
+ Starts a managed subagent whose working directory is the workspace. It does not
151
+ appear in the user's session list; it appears under your session as a subagent
152
+ and reports its result back to you.
153
+
154
+ ```json
155
+ {
156
+ "workspace_id": "ws_...",
157
+ "description": "Port the retry policy",
158
+ "prompt": "Full instructions...",
159
+ "provider": "codex",
160
+ "model": "openai/gpt-5.6-sol",
161
+ "reasoning_effort": "medium",
162
+ "background": true
163
+ }
164
+ ```
165
+
166
+ It returns `{ sessionId, taskName, path, status, output }`. Background agents are
167
+ the default; read their output with `TaskOutput` (or your provider's equivalent),
168
+ stop them with `TaskStop`, and send follow-up work with `SendMessage`. `read_only:
169
+ true` restricts the child to Read only instead of inheriting your permission mode.
170
+ Model and reasoning effort are required. `provider` is optional and selects a
171
+ specific visible account when the user's request calls for one; otherwise Happy Agent
172
+ resolves and routes the provider. `context: "parent"` includes the delegator's
173
+ conversation; the default, `"task"`, starts with only the prompt.
174
+ `service_tier: "priority"` requests priority service when supported.
175
+
176
+ Use this when the workspace's work is _your_ work, delegated for isolation, and
177
+ the user only needs your final answer.
178
+
179
+ ### `delegate_to_workspace` — visible session
180
+
181
+ Starts a full, user-visible conversation in the workspace. It gets its own place
182
+ in the session list, keeps your session as its parent, and can be reached
183
+ afterwards through `agent_info` + `agent_send` with the returned `agentId`.
184
+
185
+ ```json
186
+ {
187
+ "workspace_id": "ws_...",
188
+ "title": "Retry policy rewrite",
189
+ "prompt": "Full instructions...",
190
+ "provider": "codex",
191
+ "model": "openai/gpt-5.6-sol",
192
+ "reasoning_effort": "medium"
193
+ }
194
+ ```
195
+
196
+ It returns `{ agentId, sessionId, projectId, title, workspaceId, workspacePath }`.
197
+ Model and reasoning effort are required here too. `provider`, `read_only`, and
198
+ `service_tier` are optional and have the same meaning as for a hidden workspace
199
+ agent.
200
+
201
+ When the delegated run finishes, you receive a notification with its status and
202
+ result. Messages the user writes in that conversation stay there.
203
+
204
+ `delegate_to_workspace` is reviewed in Auto mode, because it starts a user-visible
205
+ agent working outside your own workspace. Only a primary session can delegate —
206
+ a subagent cannot — and you cannot delegate into the workspace you are already
207
+ working in.
208
+
209
+ ### The rule that matters
210
+
211
+ Never start an agent in your own directory and have it reach into another
212
+ workspace's folder by path. An agent whose working directory is one workspace
213
+ must not edit files in another. Start it _in_ the workspace with
214
+ `spawn_workspace_agent` or `delegate_to_workspace`.
215
+
216
+ ## Inspecting what exists
217
+
218
+ - `list_workspaces` — workspaces of your project, or of another project when you
219
+ pass `project_id` (that needs cross-workspace access). Listing is for
220
+ inspecting and following up on existing work, **not** for shopping for a
221
+ workspace to reuse.
222
+ - `list_workspace_sessions` — conversations of a project or of one workspace,
223
+ most recently active first. Each entry carries `id`, `agentId`, `title`,
224
+ `status`, `updatedAt`, and `delegatedBy` when an agent started it. The
225
+ `agentId` is what you pass to `agent_info` / `agent_send`.
226
+ - `list_projects` — every project on the machine. Requires cross-workspace access.
227
+
228
+ ## When to create a workspace
229
+
230
+ A separate workspace exists to **isolate** work, not to organize it.
231
+
232
+ Create one when a piece of work will run alongside other work and their changes
233
+ could collide. That is the only criterion — not the number of tasks, not their
234
+ size.
235
+
236
+ **Create a workspace when:**
237
+
238
+ - you are starting two or more tasks that will edit the same repository at the
239
+ same time; each gets its own fresh workspace;
240
+ - a long-running piece of work must proceed without disturbing the branch the
241
+ user is looking at.
242
+
243
+ **Do not create a workspace when:**
244
+
245
+ - the work is a subtask of what you are already doing — that belongs in the
246
+ current workspace, however many subagents help with it;
247
+ - you just want tidier organization; a workspace is a full checkout with its own
248
+ dependencies and its own cold context, and that cost has to be bought by real
249
+ isolation;
250
+ - two parallel tasks could be squeezed into one workspace — they must not be.
251
+
252
+ One task, however many hands, is one workspace.
253
+
254
+ ## Availability and configuration
255
+
256
+ The workspace tools appear only in a **primary session** (never in a subagent)
257
+ and only when `features.workspaces` is enabled — it is on by default.
258
+ `list_projects` additionally requires `features.cross_workspace`; when that is
259
+ off, the tool is not offered at all rather than failing when it is called.
260
+
261
+ Relevant configuration keys:
262
+
263
+ ```toml
264
+ [features]
265
+ workspaces = true
266
+ cross_workspace = false
267
+
268
+ [workspace]
269
+ setup_commands = ["pnpm install --frozen-lockfile"]
270
+ # Project files copied into every workspace and re-copied whenever the project root
271
+ # copy changes, such as gitignored .env files. Sync is one-way: the root copy wins.
272
+ sync = [".env"]
273
+ # Synced like sync, and additionally protected from writing without Full access.
274
+ protected_sync = [".env.production"]
275
+ ```
276
+
277
+ ## Tracking changes
278
+
279
+ For a workspace branch, the baseline for "what changed" is the branch's merge
280
+ base with `origin/main`; local `main` is never used. Happy Agent tracks changes
281
+ line by line, detects binary files, and handles large files separately.
@@ -0,0 +1 @@
1
+ export declare function errorToMessage(error: unknown): string;
@@ -0,0 +1,10 @@
1
+ export interface FormatCliFailureOptions {
2
+ /** Prints the untouched stack instead of the trimmed frame list. */
3
+ debug?: boolean;
4
+ color?: boolean;
5
+ }
6
+ /**
7
+ * Renders a failure the way the rest of Happy Terminal looks: a marked summary line, an actionable hint,
8
+ * and — only for unexpected crashes — a few trimmed frames instead of a raw Node stack.
9
+ */
10
+ export declare function formatCliFailure(error: unknown, options?: FormatCliFailureOptions): string;
@@ -0,0 +1,3 @@
1
+ export { runHappyTerminal } from "./runHappyTerminal.js";
2
+ export type { RunHappyTerminalOptions } from "./runHappyTerminal.js";
3
+ export { readPackageVersion } from "./readPackageVersion.js";