@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.
- package/LICENSE +21 -0
- package/LICENSE-CODEX +203 -0
- package/LICENSE-GROK-BUILD +204 -0
- package/LICENSE-HAPPY +21 -0
- package/README.md +60 -0
- package/THIRD-PARTY-NOTICES.md +34 -0
- package/dist/HappyTerminalUserError.d.ts +12 -0
- package/dist/app/AppTranscriptEntry.d.ts +33 -0
- package/dist/app/AppendOnlyStreamingRender.d.ts +12 -0
- package/dist/app/CodexFileDiff.d.ts +20 -0
- package/dist/app/CodexMcpToolCall.d.ts +25 -0
- package/dist/app/CodingAssistantAgentBackend.d.ts +81 -0
- package/dist/app/CodingAssistantApp.d.ts +128 -0
- package/dist/app/CompletedTurn.d.ts +11 -0
- package/dist/app/FileMentionAutocomplete.d.ts +30 -0
- package/dist/app/HappyTerminalProcessTerminal.d.ts +7 -0
- package/dist/app/NoticeChild.d.ts +4 -0
- package/dist/app/SecretMenuController.d.ts +31 -0
- package/dist/app/SelectionList.d.ts +24 -0
- package/dist/app/SessionDraftSync.d.ts +51 -0
- package/dist/app/StartupStatusApp.d.ts +34 -0
- package/dist/app/StartupStatusCardModel.d.ts +22 -0
- package/dist/app/TemporaryFullscreenController.d.ts +9 -0
- package/dist/app/TerminalColorLevel.d.ts +1 -0
- package/dist/app/TerminalOutputTrace.d.ts +12 -0
- package/dist/app/TerminalTheme.d.ts +11 -0
- package/dist/app/TranscriptEntryRenderCache.d.ts +10 -0
- package/dist/app/applyWorkflowRunUpdate.d.ts +2 -0
- package/dist/app/blendComposerBackground.d.ts +2 -0
- package/dist/app/boundedJsonStringify.d.ts +1 -0
- package/dist/app/calculateCacheHitPercent.d.ts +2 -0
- package/dist/app/codexDiffAnsi.d.ts +17 -0
- package/dist/app/compactCompletedTurnEntries.d.ts +2 -0
- package/dist/app/containsMarkdownTable.d.ts +1 -0
- package/dist/app/createAgentMarkdownTheme.d.ts +3 -0
- package/dist/app/createBackgroundTerminalViewer.d.ts +16 -0
- package/dist/app/createCompletedTurn.d.ts +6 -0
- package/dist/app/createEditorTheme.d.ts +3 -0
- package/dist/app/createFileMentionAutocompleteItems.d.ts +3 -0
- package/dist/app/createSecretInputPanel.d.ts +12 -0
- package/dist/app/createSelectionPanel.d.ts +14 -0
- package/dist/app/createSerialTaskQueue.d.ts +1 -0
- package/dist/app/createSessionPicker.d.ts +17 -0
- package/dist/app/createSlashCommands.d.ts +7 -0
- package/dist/app/createStopOnceHandler.d.ts +1 -0
- package/dist/app/createSubagentMonitor.d.ts +17 -0
- package/dist/app/createTerminalInputBurstHandler.d.ts +5 -0
- package/dist/app/createWorkflowMonitor.d.ts +15 -0
- package/dist/app/defaultTerminalTheme.d.ts +1 -0
- package/dist/app/describeModelChoice.d.ts +4 -0
- package/dist/app/describeProviderSignIn.d.ts +2 -0
- package/dist/app/describeReasoningLevel.d.ts +4 -0
- package/dist/app/detectCodexDiffLanguage.d.ts +1 -0
- package/dist/app/encodeModelChoice.d.ts +1 -0
- package/dist/app/endsAfterOpeningCodeFence.d.ts +1 -0
- package/dist/app/findFileMentionContext.d.ts +9 -0
- package/dist/app/findFileMentionPrefix.d.ts +5 -0
- package/dist/app/formatActivityElapsedTime.d.ts +1 -0
- package/dist/app/formatCodexMcpToolResult.d.ts +3 -0
- package/dist/app/formatCompactTokens.d.ts +1 -0
- package/dist/app/formatFileMention.d.ts +1 -0
- package/dist/app/formatProviderError.d.ts +6 -0
- package/dist/app/formatRelativeTime.d.ts +2 -0
- package/dist/app/formatResetDuration.d.ts +1 -0
- package/dist/app/formatSessionPickerEntry.d.ts +14 -0
- package/dist/app/formatSessionTokenStatus.d.ts +6 -0
- package/dist/app/formatSessionUsageSummary.d.ts +3 -0
- package/dist/app/formatStartupStatusUsageRemaining.d.ts +2 -0
- package/dist/app/formatStartupStatusUsageRows.d.ts +2 -0
- package/dist/app/formatSubagentToolCall.d.ts +10 -0
- package/dist/app/formatToolPermissionNotice.d.ts +3 -0
- package/dist/app/formatToolResultForDisplay.d.ts +2 -0
- package/dist/app/formatTurnUsageSummary.d.ts +2 -0
- package/dist/app/formatWorkUsageSummary.d.ts +14 -0
- package/dist/app/highlightAgentCode.d.ts +1 -0
- package/dist/app/highlightCodexDiffLine.d.ts +2 -0
- package/dist/app/highlightShellCommand.d.ts +1 -0
- package/dist/app/humanizeGoalStatus.d.ts +2 -0
- package/dist/app/humanizeMcpName.d.ts +1 -0
- package/dist/app/humanizePermissionMode.d.ts +2 -0
- package/dist/app/humanizePermissionReviewLevel.d.ts +2 -0
- package/dist/app/humanizeProviderId.d.ts +1 -0
- package/dist/app/humanizeReasoningLevel.d.ts +1 -0
- package/dist/app/humanizeSubagentStatus.d.ts +2 -0
- package/dist/app/humanizeToolName.d.ts +1 -0
- package/dist/app/humanizeWorkflowStatus.d.ts +2 -0
- package/dist/app/installResumeInstructions.d.ts +26 -0
- package/dist/app/installTerminalCrashCleanup.d.ts +26 -0
- package/dist/app/isLightTerminalBackground.d.ts +2 -0
- package/dist/app/layoutCodexFileDiff.d.ts +13 -0
- package/dist/app/looksLikeAuthenticationFailure.d.ts +5 -0
- package/dist/app/nearestXtermColorIndex.d.ts +2 -0
- package/dist/app/parseCodexMcpToolInvocation.d.ts +2 -0
- package/dist/app/parseSessionCommand.d.ts +6 -0
- package/dist/app/providerErrorResetAt.d.ts +2 -0
- package/dist/app/providerQuotaToStartupStatusUsage.d.ts +3 -0
- package/dist/app/readClipboardImage.d.ts +9 -0
- package/dist/app/renderActivityWave.d.ts +3 -0
- package/dist/app/renderAgentMarkdown.d.ts +8 -0
- package/dist/app/renderBackgroundTerminalCompletion.d.ts +1 -0
- package/dist/app/renderBackgroundTerminalInteraction.d.ts +2 -0
- package/dist/app/renderBackgroundTerminalSummary.d.ts +1 -0
- package/dist/app/renderChildRows.d.ts +13 -0
- package/dist/app/renderCodexDiffLine.d.ts +3 -0
- package/dist/app/renderCodexFileDiff.d.ts +7 -0
- package/dist/app/renderCodexMcpToolCall.d.ts +2 -0
- package/dist/app/renderCompletedTurnStats.d.ts +2 -0
- package/dist/app/renderExecCommand.d.ts +11 -0
- package/dist/app/renderExploration.d.ts +9 -0
- package/dist/app/renderFullscreenComponent.d.ts +8 -0
- package/dist/app/renderHappyTerminalBanner.d.ts +6 -0
- package/dist/app/renderHappyTerminalVersion.d.ts +1 -0
- package/dist/app/renderNoticeWithChildren.d.ts +7 -0
- package/dist/app/renderPendingSteeringMessages.d.ts +1 -0
- package/dist/app/renderStartupStatusCard.d.ts +7 -0
- package/dist/app/renderSubagentSummary.d.ts +6 -0
- package/dist/app/renderTurnCompletionSeparator.d.ts +1 -0
- package/dist/app/renderWorkflowSummary.d.ts +1 -0
- package/dist/app/resolveInputBackground.d.ts +3 -0
- package/dist/app/resolveStartupProviderQuota.d.ts +3 -0
- package/dist/app/resolveStartupSessionId.d.ts +18 -0
- package/dist/app/resolveTerminalColorLevel.d.ts +2 -0
- package/dist/app/resolveTerminalStyle.d.ts +1 -0
- package/dist/app/resolveTerminalTheme.d.ts +5 -0
- package/dist/app/runApp.d.ts +27 -0
- package/dist/app/runClipboardCommand.d.ts +8 -0
- package/dist/app/sanitizeTerminalText.d.ts +1 -0
- package/dist/app/shortenHomePath.d.ts +2 -0
- package/dist/app/sortSubagentsForDisplay.d.ts +2 -0
- package/dist/app/subagentElapsedMs.d.ts +2 -0
- package/dist/app/subagentLogMessageText.d.ts +2 -0
- package/dist/app/surfaceThemedLine.d.ts +3 -0
- package/dist/app/truncatePathToWidth.d.ts +1 -0
- package/dist/app/truncateTextForDisplay.d.ts +5 -0
- package/dist/app/truncateUtf8BytesForDisplay.d.ts +1 -0
- package/dist/app/upsertSubagentSummary.d.ts +2 -0
- package/dist/app/workflowDisplay.d.ts +2 -0
- package/dist/client/HappyAgentEventHub.d.ts +25 -0
- package/dist/client/RemoteAgent.d.ts +74 -0
- package/dist/client/RemoteAgentRunError.d.ts +4 -0
- package/dist/client/RemoteTerminalAttachment.d.ts +19 -0
- package/dist/client/RemoteTerminalClientReplica.d.ts +14 -0
- package/dist/client/ensureLocalProtocolServer.d.ts +10 -0
- package/dist/client/fetchProviderQuotas.d.ts +10 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/loadAgentCatalog.d.ts +32 -0
- package/dist/config/createProjectConfigSecurityNotice.d.ts +10 -0
- package/dist/config/defaultConfig.d.ts +2 -0
- package/dist/config/getDefaultGlobalConfigPath.d.ts +1 -0
- package/dist/config/getDefaultLocalConfigPath.d.ts +1 -0
- package/dist/config/getDefaultRuntimeConfigPath.d.ts +1 -0
- package/dist/config/getHappyConfigDirectory.d.ts +1 -0
- package/dist/config/getHappyTerminalHome.d.ts +1 -0
- package/dist/config/index.d.ts +15 -0
- package/dist/config/loadConfig.d.ts +2 -0
- package/dist/config/mergeConfigValues.d.ts +2 -0
- package/dist/config/parseConfigToml.d.ts +7 -0
- package/dist/config/readConfigFile.d.ts +2 -0
- package/dist/config/readProjectConfigFile.d.ts +2 -0
- package/dist/config/resolveConfigPaths.d.ts +2 -0
- package/dist/config/runtimeConfigLock.d.ts +1 -0
- package/dist/config/types.d.ts +74 -0
- package/dist/config/updateRuntimeConfig.d.ts +2 -0
- package/dist/config/updateRuntimePreferences.d.ts +2 -0
- package/dist/config/withoutProjectMachineSettings.d.ts +7 -0
- package/dist/config/writeRuntimeConfig.d.ts +3 -0
- package/dist/config/writeRuntimeConfigDefaults.d.ts +2 -0
- package/dist/daemon/createUnixSocketFetch.d.ts +2 -0
- package/dist/daemon/daemonPid.d.ts +11 -0
- package/dist/daemon/ensureHappyAgentBinary.d.ts +15 -0
- package/dist/daemon/ensureLocalProtocolServer.d.ts +24 -0
- package/dist/daemon/getHappyDaemonPaths.d.ts +16 -0
- package/dist/daemon/happyAgentBinaryConfig.d.ts +15 -0
- package/dist/daemon/index.d.ts +5 -0
- package/dist/daemon/runDaemonCommand.d.ts +4 -0
- package/dist/debug/DebugLog.d.ts +11 -0
- package/dist/debug/closeNodeInspector.d.ts +1 -0
- package/dist/debug/createDebugJsonReplacer.d.ts +1 -0
- package/dist/debug/createRequestDebugDirectory.d.ts +1 -0
- package/dist/debug/getDebugRootDirectory.d.ts +1 -0
- package/dist/debug/getNodeInspectorUrl.d.ts +1 -0
- package/dist/debug/index.d.ts +8 -0
- package/dist/debug/openNodeInspector.d.ts +1 -0
- package/dist/debug/registerHappyTerminalDebugRoot.d.ts +5 -0
- package/dist/docs/DESIGN.md +2191 -0
- package/dist/docs/README.md +93 -0
- package/dist/docs/agents-and-collaboration.md +365 -0
- package/dist/docs/architecture.md +468 -0
- package/dist/docs/extending.md +561 -0
- package/dist/docs/happy.md +386 -0
- package/dist/docs/permissions-and-sandbox.md +342 -0
- package/dist/docs/workspaces.md +281 -0
- package/dist/errorToMessage.d.ts +1 -0
- package/dist/formatCliFailure.d.ts +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +17151 -0
- package/dist/main.js +18946 -0
- package/dist/observability/daemonContext.d.ts +16 -0
- package/dist/observability/index.d.ts +1 -0
- package/dist/processes/BoundedOutputBuffer.d.ts +20 -0
- package/dist/processes/NativeProcessManager.d.ts +66 -0
- package/dist/processes/ProcessGroupReaper.d.ts +32 -0
- package/dist/processes/index.d.ts +7 -0
- package/dist/processes/isProcessRunning.d.ts +1 -0
- package/dist/processes/isTargetProcessAlive.d.ts +1 -0
- package/dist/processes/killProcessTree.d.ts +2 -0
- package/dist/processes/resolveSystemShell.d.ts +1 -0
- package/dist/processes/startProcessTransport.d.ts +33 -0
- package/dist/processes/types.d.ts +41 -0
- package/dist/processes/waitForProcessExit.d.ts +2 -0
- package/dist/protocol/AppletProtocol.d.ts +101 -0
- package/dist/protocol/Attachment.d.ts +96 -0
- package/dist/protocol/ClientProtocolTypes.d.ts +481 -0
- package/dist/protocol/DocumentProtocol.d.ts +109 -0
- package/dist/protocol/EventId.d.ts +1 -0
- package/dist/protocol/FolderProtocol.d.ts +236 -0
- package/dist/protocol/FolderSharingProtocol.d.ts +97 -0
- package/dist/protocol/GitHubPluginProtocol.d.ts +43 -0
- package/dist/protocol/GlobalSecurityProtocol.d.ts +7 -0
- package/dist/protocol/HappyCloudProtocol.d.ts +178 -0
- package/dist/protocol/InstallationProtocol.d.ts +80 -0
- package/dist/protocol/OnboardingProtocol.d.ts +47 -0
- package/dist/protocol/P2pCredentialProtocol.d.ts +273 -0
- package/dist/protocol/P2pIdentityProtocol.d.ts +8 -0
- package/dist/protocol/P2pPairingProtocol.d.ts +68 -0
- package/dist/protocol/P2pProtocol.d.ts +185 -0
- package/dist/protocol/PluginWireTypes.d.ts +81 -0
- package/dist/protocol/ProfileProtocol.d.ts +127 -0
- package/dist/protocol/ProjectFileProtocol.d.ts +63 -0
- package/dist/protocol/ProjectProtocol.d.ts +646 -0
- package/dist/protocol/ProtocolVersion.d.ts +8 -0
- package/dist/protocol/ServiceNotice.d.ts +175 -0
- package/dist/protocol/SessionProtocol.d.ts +1156 -0
- package/dist/protocol/SharingProtocol.d.ts +159 -0
- package/dist/protocol/SlotProtocol.d.ts +270 -0
- package/dist/protocol/TimelineProtocol.d.ts +88 -0
- package/dist/protocol/WorkletProtocol.d.ts +159 -0
- package/dist/protocol/createEventIdFactory.d.ts +7 -0
- package/dist/protocol/index.d.ts +34 -0
- package/dist/readPackageVersion.d.ts +1 -0
- package/dist/readPackageVersion.js +16 -0
- package/dist/reportCliFailure.d.ts +2 -0
- package/dist/runHappyTerminal.d.ts +6 -0
- package/dist/terminal/index.d.ts +2 -0
- package/dist/terminal/types.d.ts +39 -0
- package/dist/types.d.ts +6 -0
- package/dist/writeStderrSync.d.ts +5 -0
- package/package.json +97 -0
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
# Extending Happy Agent
|
|
2
|
+
|
|
3
|
+
This guide is written for a coding agent running inside Happy Agent that has been asked
|
|
4
|
+
to extend Happy Agent. Everything below describes behavior that actually ships; where
|
|
5
|
+
something is planned rather than implemented, it says so explicitly.
|
|
6
|
+
|
|
7
|
+
There are five extension surfaces, ordered by how much they let you change:
|
|
8
|
+
|
|
9
|
+
| Surface | What it adds | Who writes it |
|
|
10
|
+
| -------------------------------------- | --------------------------------------------------------------------------------------- | ----------------------- |
|
|
11
|
+
| **Plugins** | A JavaScript or TypeScript process that contributes MCP tools and local UI applications | You, inside Happy Agent |
|
|
12
|
+
| **Skills** | Instructions a model loads on demand from a `SKILL.md` file | You or the user |
|
|
13
|
+
| **MCP servers** | Tools, resources, and prompts from an external process or HTTP service | The user, in config |
|
|
14
|
+
| **Happy Agent Connect / integrations** | External apps that read Happy Agent's live state and drive it | An application author |
|
|
15
|
+
| **Subagents and workflows** | Extra agents and deterministic multi-agent scripts, at runtime | You, per task |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Plugins
|
|
20
|
+
|
|
21
|
+
A local plugin is the general-purpose extension mechanism. It is ready-to-run
|
|
22
|
+
JavaScript or TypeScript that Happy Agent runs as its own sandboxed process, connected
|
|
23
|
+
back to the daemon over a private Unix socket. From there it can create
|
|
24
|
+
workspaces, send messages to agents, read provider usage, contribute MCP tools,
|
|
25
|
+
and contribute a small local UI application.
|
|
26
|
+
|
|
27
|
+
### What a plugin folder contains
|
|
28
|
+
|
|
29
|
+
Three files are enough. Happy Agent does not require a `package.json`.
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
project-counter/
|
|
33
|
+
├── happy.plugin.json manifest — required
|
|
34
|
+
├── icon.png PNG icon — required
|
|
35
|
+
└── index.ts main entry point — required
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### The manifest
|
|
39
|
+
|
|
40
|
+
`happy.plugin.json` is validated against a strict schema and **extra fields are
|
|
41
|
+
rejected**:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"name": "Project Counter",
|
|
46
|
+
"author": "Acme Tools",
|
|
47
|
+
"category": "developer-tools",
|
|
48
|
+
"description": "Reports how many projects Happy Agent knows about.",
|
|
49
|
+
"main": "index.ts",
|
|
50
|
+
"icon": "icon.png"
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Field | Rule |
|
|
55
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
56
|
+
| `name` | Non-empty string. Human-readable; also used to derive the agent-facing MCP tool name. |
|
|
57
|
+
| `author` | Required 1–80 character publisher label without leading/trailing whitespace or control/direction characters. |
|
|
58
|
+
| `category` | Required catalog category; see the canonical values below. |
|
|
59
|
+
| `description` | Required 1–512 character explanation of what the plugin does. |
|
|
60
|
+
| `main` | Process entry path; optional only when skills or a system prompt provide the plugin's behavior. |
|
|
61
|
+
| `icon` | Relative path ending in `.png` (any capitalization of the extension). |
|
|
62
|
+
| `version` | Optional Semantic Versioning string; an omission becomes `0.0.0`. |
|
|
63
|
+
| `apps` | Optional list of bounded static MCP App manifests. |
|
|
64
|
+
|
|
65
|
+
`category` is exactly one of `automation`, `collaboration`, `data`,
|
|
66
|
+
`developer-tools`, `media`, `productivity`, `utilities`, or `other`.
|
|
67
|
+
|
|
68
|
+
Additional rules Happy Agent enforces when it reads the manifest:
|
|
69
|
+
|
|
70
|
+
- `main` and `icon` must be relative and must resolve **inside** the plugin
|
|
71
|
+
folder.
|
|
72
|
+
- Both must resolve to ordinary files inside the plugin's real directory tree;
|
|
73
|
+
final or intermediate symbolic-link escapes are rejected.
|
|
74
|
+
- The icon must be a fully decodable square PNG, between 1×1 and 2048×2048
|
|
75
|
+
pixels and no larger than 4 MiB. A renamed JPEG, truncated PNG, SVG,
|
|
76
|
+
placeholder string, or URL is rejected and the plugin does not register.
|
|
77
|
+
|
|
78
|
+
Do not invent manifest fields such as `permissions` or `contributes`; adding one
|
|
79
|
+
makes the manifest invalid.
|
|
80
|
+
|
|
81
|
+
### The icon
|
|
82
|
+
|
|
83
|
+
Every registered plugin must ship an original PNG icon. Happy Agent bundles a skill for
|
|
84
|
+
producing one, `local-plugin-icon`.
|
|
85
|
+
It triggers automatically when you create or edit a plugin, a `happy.plugin.json`,
|
|
86
|
+
or its icon. Follow it: it defines the shared visual family (Jobs-era iPhone icon
|
|
87
|
+
craft, one metaphor, no text or third-party marks) and the verification steps —
|
|
88
|
+
generate a square image, prefer 1024×1024, inspect it, save a real PNG in the
|
|
89
|
+
plugin folder, keep it at or below 2048×2048 and 4 MiB, and point `icon` at that
|
|
90
|
+
relative path.
|
|
91
|
+
|
|
92
|
+
### The entry file
|
|
93
|
+
|
|
94
|
+
Happy Agent starts `main` with the same Node executable that runs Happy Agent. Node strips
|
|
95
|
+
erasable TypeScript syntax without a compile step or extra flag, so TypeScript
|
|
96
|
+
may use top-level `await` and relative `.ts` imports. Constructs that require
|
|
97
|
+
JavaScript generation are not supported. Use `.mjs` or a local
|
|
98
|
+
`"type": "module"` package declaration for JavaScript ESM.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { happy } from "happy-plugins";
|
|
102
|
+
|
|
103
|
+
const projects = await happy.projects.list();
|
|
104
|
+
console.log(`Happy Agent has ${projects.length} projects.`);
|
|
105
|
+
|
|
106
|
+
await happy.ready("Ready.");
|
|
107
|
+
|
|
108
|
+
// A service-style plugin stays alive until Happy Agent shuts it down.
|
|
109
|
+
await new Promise<void>((resolve) => {
|
|
110
|
+
process.once("SIGTERM", resolve);
|
|
111
|
+
process.once("SIGINT", resolve);
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Plugin code never opens a connection, finds credentials, or speaks Happy Agent's
|
|
116
|
+
protocol. The `happy` singleton reads the socket path and token that the daemon
|
|
117
|
+
injects and connects for you. Happy Agent registers one ESM loader hook with `--import`
|
|
118
|
+
to map `happy-plugins` and `happy-plugins/internal` to the SDK shipped with Happy Agent;
|
|
119
|
+
the plugin does not vendor a runtime SDK.
|
|
120
|
+
|
|
121
|
+
Happy Agent provides only `happy-plugins` at runtime. Bundle every other third-party
|
|
122
|
+
dependency into the plugin's own files; Happy Agent does not copy `node_modules` when
|
|
123
|
+
it installs a plugin.
|
|
124
|
+
|
|
125
|
+
### The SDK surface
|
|
126
|
+
|
|
127
|
+
All SDK methods return promises; inputs and daemon responses are validated with
|
|
128
|
+
TypeBox at runtime. The current surface is:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
happy.projects.list();
|
|
132
|
+
|
|
133
|
+
happy.workspaces.list({ projectId? });
|
|
134
|
+
happy.workspaces.create({ projectId, name, baseRef? });
|
|
135
|
+
happy.workspaces.rename({ projectId, workspaceId, name, version });
|
|
136
|
+
happy.workspaces.archive({ projectId, workspaceId, version });
|
|
137
|
+
|
|
138
|
+
happy.sessions.list();
|
|
139
|
+
happy.sessions.create({ cwd, providerId?, modelId?, effort?, appendSystemPrompt?, workspaceId? });
|
|
140
|
+
|
|
141
|
+
happy.agents.sendMessage({ agentId, message });
|
|
142
|
+
|
|
143
|
+
happy.providers.usage();
|
|
144
|
+
|
|
145
|
+
happy.mcp.startServer({ name, tools });
|
|
146
|
+
happy.ready("Ready."); // Call once, after every startup contribution is registered.
|
|
147
|
+
happy.ui.startApplication({ id, title, entry, navigation?, resources, actions });
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Workspace mutations are optimistic: pass the `version` from the most recently
|
|
151
|
+
returned workspace. Failed requests throw `HappyPluginApiError` carrying the HTTP
|
|
152
|
+
status.
|
|
153
|
+
|
|
154
|
+
#### Contributing MCP tools
|
|
155
|
+
|
|
156
|
+
This is the first and most useful plugin point. No MCP server package is needed —
|
|
157
|
+
reuse the `Type` and `defineMcpTool` exports:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { defineMcpTool, happy, Type } from "happy-plugins";
|
|
161
|
+
|
|
162
|
+
await happy.mcp.startServer({
|
|
163
|
+
name: "Catalog",
|
|
164
|
+
tools: [
|
|
165
|
+
defineMcpTool({
|
|
166
|
+
name: "list_projects",
|
|
167
|
+
description: "List every local Happy Agent project.",
|
|
168
|
+
inputSchema: Type.Object({}, { additionalProperties: false }),
|
|
169
|
+
async execute(_input, { signal }) {
|
|
170
|
+
signal.throwIfAborted();
|
|
171
|
+
const projects = await happy.projects.list();
|
|
172
|
+
return { content: [{ type: "text", text: JSON.stringify(projects) }] };
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
],
|
|
176
|
+
});
|
|
177
|
+
await happy.ready("Ready.");
|
|
178
|
+
|
|
179
|
+
// Keep the process alive so the server stays registered.
|
|
180
|
+
await new Promise<void>(() => {});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Happy Agent offers the tool in ordinary sessions everywhere. The agent-facing name is
|
|
184
|
+
stable and derived from the plugin name, server name, and tool name by the SDK's
|
|
185
|
+
`createHappyMcpToolName`:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
mcp__<plugin name>_·_<server name>__<tool name> with every character outside
|
|
189
|
+
[A-Za-z0-9_-] replaced by "_"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
For the example above with a plugin named `Project Tools`, that is
|
|
193
|
+
`mcp__Project_Tools___Catalog__list_projects`. Call
|
|
194
|
+
`createHappyMcpToolName(pluginName, serverName, toolName)` rather than
|
|
195
|
+
hand-writing it in a test.
|
|
196
|
+
|
|
197
|
+
Plugin tool calls use the same permission path as configured MCP servers: they
|
|
198
|
+
require Auto or Full access, and every Auto call is reviewed, because a plugin
|
|
199
|
+
may act outside Happy Agent's filesystem sandbox. Cancellation reaches the handler's
|
|
200
|
+
`AbortSignal`, and disconnected, replaced, restarted, or uninstalled generations
|
|
201
|
+
are retired immediately.
|
|
202
|
+
|
|
203
|
+
#### Contributing a local application
|
|
204
|
+
|
|
205
|
+
A plugin may register one or more static bundles plus typed actions through
|
|
206
|
+
`happy.ui.startApplication`. Happy Agent serves them to hosts (currently the Happy2
|
|
207
|
+
Electron shell) which mount them instantly. Limits enforced by the daemon: 8
|
|
208
|
+
applications per plugin, 32 actions and 64 resources per application, 256 KiB per
|
|
209
|
+
resource, 1 MiB per decoded bundle, 64 concurrent actions, a 30-second action
|
|
210
|
+
timeout, and 1 MiB action bodies. Supported media types are JSON, WOFF2, JPEG,
|
|
211
|
+
PNG, SVG, WebP, CSS, HTML, and JavaScript.
|
|
212
|
+
|
|
213
|
+
### Where things live
|
|
214
|
+
|
|
215
|
+
Plugin code and Happy Agent's bounded log stay in Happy Agent's managed home; everything the
|
|
216
|
+
plugin writes at runtime goes to a folder a person can open.
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
~/.happy/agent/plugins/<folder>/ installed code, managed by Happy Agent
|
|
220
|
+
├── happy.plugin.json
|
|
221
|
+
├── icon.png
|
|
222
|
+
├── index.ts
|
|
223
|
+
└── plugin.log bounded current-run output
|
|
224
|
+
|
|
225
|
+
~/Happy/Plugins/<folder>/ the plugin's writable folder (macOS)
|
|
226
|
+
~/happy/plugins/<folder>/ the same on Linux
|
|
227
|
+
└── .runtime/plugin.sock per-plugin API socket
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`<folder>` is derived from the source directory's base name, lowercased with
|
|
231
|
+
runs of unsupported characters replaced by `-`.
|
|
232
|
+
|
|
233
|
+
Overrides, all requiring absolute paths:
|
|
234
|
+
|
|
235
|
+
| Variable | Effect |
|
|
236
|
+
| ----------------------------- | ------------------------------------------ |
|
|
237
|
+
| `HAPPY_HOME_DIR` | Moves Happy Agent's private `.happy` root. |
|
|
238
|
+
| `HAPPY_PLUGINS_DIRECTORY` | Moves the installed-plugin root. |
|
|
239
|
+
| `HAPPY_PLUGIN_DATA_DIRECTORY` | Moves the writable plugin-data root. |
|
|
240
|
+
|
|
241
|
+
The plugin process runs with its writable folder as the working directory, under
|
|
242
|
+
Happy Agent's existing command sandbox confined to that folder. Happy Agent injects:
|
|
243
|
+
|
|
244
|
+
| Variable | Meaning |
|
|
245
|
+
| -------------------------- | ---------------------------------------------- |
|
|
246
|
+
| `HAPPY_PLUGIN_DIRECTORY` | Absolute path to the plugin's writable folder. |
|
|
247
|
+
| `HAPPY_PLUGIN_SOCKET_PATH` | Private Unix socket used by the SDK. |
|
|
248
|
+
| `HAPPY_PLUGIN_TOKEN` | Per-process bearer token used by the SDK. |
|
|
249
|
+
|
|
250
|
+
Write state in `HAPPY_PLUGIN_DIRECTORY` and nowhere else.
|
|
251
|
+
|
|
252
|
+
### Installing, listing, logging, uninstalling
|
|
253
|
+
|
|
254
|
+
Four agent tools drive the lifecycle:
|
|
255
|
+
|
|
256
|
+
| Tool | Arguments | What it does |
|
|
257
|
+
| ------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
258
|
+
| `plugin_install` | `path` — folder containing `happy.plugin.json` | Copies and validates the ready-to-run folder, then starts the plugin before returning. |
|
|
259
|
+
| `plugin_list` | none | Returns every installed plugin with `status`, `directory`, `dataDirectory`, `logAvailable`, plus registration `failures`. |
|
|
260
|
+
| `plugin_logs` | `name` — plugin name or folder name | Returns the newest bounded log or startup diagnostic with `status`, `source` (`current_run` or `error`), and `truncated`. |
|
|
261
|
+
| `plugin_uninstall` | `name` | Stops the plugin, removes its installed code, and keeps its writable folder. |
|
|
262
|
+
|
|
263
|
+
Plugins live outside the workspace, so all four are reviewed in Auto mode;
|
|
264
|
+
`plugin_install` and `plugin_uninstall` additionally run with a temporary Full
|
|
265
|
+
access override because they must write outside the sandbox. A denial is a real
|
|
266
|
+
answer: do not retry the same action by another route.
|
|
267
|
+
|
|
268
|
+
Installation is staged. The plugin is copied into a hidden folder and its
|
|
269
|
+
manifest, icon, and main entry point are validated there. An invalid plugin is
|
|
270
|
+
never installed and never replaces a working one. `.git`, `.runtime`,
|
|
271
|
+
`node_modules`, and `plugin.log` are excluded from the copy, and the copy is
|
|
272
|
+
bounded to 2,000 files and 32 MiB. Happy Agent provides `happy-plugins` at runtime; all
|
|
273
|
+
other third-party dependencies must be bundled into the plugin's own files.
|
|
274
|
+
|
|
275
|
+
Every change publishes a live `plugins_changed` event carrying the whole current
|
|
276
|
+
set, so clients never poll and never wait for a daemon restart. The daemon also loads every installed plugin at startup. A
|
|
277
|
+
plugin's authoritative state is one of `running`, `stopped`, or `failed`.
|
|
278
|
+
|
|
279
|
+
For the user, `/plugins` shows the installed set and `/plugins <name>` prints
|
|
280
|
+
that plugin's current log.
|
|
281
|
+
|
|
282
|
+
### Minimal walkthrough
|
|
283
|
+
|
|
284
|
+
Building a plugin from inside Happy Agent, end to end:
|
|
285
|
+
|
|
286
|
+
1. Create the folder — `.context/project-counter/` is a good scratch location, or
|
|
287
|
+
somewhere the user names.
|
|
288
|
+
2. Write `happy.plugin.json` with the six fields this process plugin needs:
|
|
289
|
+
`name`, `author`, `category`, `description`, `main`, and `icon`.
|
|
290
|
+
3. Write `index.ts` against the `happy` singleton.
|
|
291
|
+
4. Generate `icon.png` using the bundled `local-plugin-icon` skill; verify it is
|
|
292
|
+
a fully decodable square PNG no larger than 2048×2048 pixels or 4 MiB.
|
|
293
|
+
5. Type-check and test the plugin, then call `plugin_install` with the absolute
|
|
294
|
+
path to the folder. Happy Agent validates, copies, and starts it without compiling.
|
|
295
|
+
6. Call `plugin_list` to confirm `status: "running"`, and `plugin_logs` if it is
|
|
296
|
+
`failed` or `stopped` — startup diagnostics come back through the same tool.
|
|
297
|
+
7. If it contributes MCP tools, they become available to sessions under the
|
|
298
|
+
`mcp__…` name above.
|
|
299
|
+
|
|
300
|
+
To iterate without installing, the SDK ships a runner that starts a source plugin
|
|
301
|
+
against an in-memory fake host, prints every request and registration, lists MCP
|
|
302
|
+
tools, and can call one. It needs Node 22.6 or newer and no Docker:
|
|
303
|
+
|
|
304
|
+
```sh
|
|
305
|
+
pnpm happy-plugin dev ./index.ts \
|
|
306
|
+
--seed ./happy.plugin.dev.json \
|
|
307
|
+
--list-tools \
|
|
308
|
+
--call "Project tools/list_projects" \
|
|
309
|
+
--arguments '{}'
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
`createHappyPluginTestHost()` exposes the same host programmatically for tests,
|
|
313
|
+
including `host.mcp.*`, `host.ui.*`, and a real `HAPPY_PLUGIN_DIRECTORY`.
|
|
314
|
+
|
|
315
|
+
For end-to-end coverage, write a gym test instead of mocking the daemon: drive a
|
|
316
|
+
real plugin through its sandbox socket, and assert that a source plugin's MCP
|
|
317
|
+
tool reaches an active session.
|
|
318
|
+
|
|
319
|
+
### Trust
|
|
320
|
+
|
|
321
|
+
Happy Agent does not implement a permission model for plugins. Plugin code is relatively
|
|
322
|
+
trusted and is not restricted by per-capability checks. What _is_ enforced is the
|
|
323
|
+
process sandbox, the writable-folder confinement, the authenticated socket, and
|
|
324
|
+
the ordinary MCP review path for tools a plugin contributes to a session.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Skills
|
|
329
|
+
|
|
330
|
+
A skill is a set of instructions delivered through a `SKILL.md` file. It changes
|
|
331
|
+
what a model knows how to do without changing any code. Happy Agent follows Codex
|
|
332
|
+
behavior and scope here deliberately; it does not implement Claude Code's
|
|
333
|
+
expanded skill runtime, and it does not interpret Claude or Pi skill trees.
|
|
334
|
+
|
|
335
|
+
### Where skills are discovered
|
|
336
|
+
|
|
337
|
+
Happy Agent searches these roots, in this order:
|
|
338
|
+
|
|
339
|
+
1. **Builtin** — skills shipped inside Happy Agent (currently `local-plugin-icon`).
|
|
340
|
+
Read-only; never write here.
|
|
341
|
+
2. **User** — `~/.codex/skills` and `~/.agents/skills`.
|
|
342
|
+
3. **Project** — `.agents/skills` in every directory from the project root down
|
|
343
|
+
to the working directory.
|
|
344
|
+
|
|
345
|
+
Within each root, discovery walks directories recursively looking for a
|
|
346
|
+
`SKILL.md`; finding one stops descent into that subtree. Entries starting with
|
|
347
|
+
`.` and `node_modules` are skipped. Later roots win on name collision, so a
|
|
348
|
+
project skill overrides a user skill of the same name, which overrides a builtin.
|
|
349
|
+
|
|
350
|
+
### `SKILL.md` format
|
|
351
|
+
|
|
352
|
+
YAML frontmatter followed by markdown instructions:
|
|
353
|
+
|
|
354
|
+
```markdown
|
|
355
|
+
---
|
|
356
|
+
name: release-notes
|
|
357
|
+
description: Use when the user asks to draft release notes from merged pull requests.
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
# Release notes
|
|
361
|
+
|
|
362
|
+
1. Collect merged PRs since the last tag.
|
|
363
|
+
2. Group them by area.
|
|
364
|
+
...
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Only three frontmatter keys are read:
|
|
368
|
+
`name` (string), `description` (string), and `disable-model-invocation`
|
|
369
|
+
(boolean). The first two are what matter — `disable-model-invocation` is parsed
|
|
370
|
+
but nothing currently consumes it. Any other key is ignored.
|
|
371
|
+
|
|
372
|
+
Validation, when a skill file is loaded:
|
|
373
|
+
|
|
374
|
+
- `name` defaults to the containing folder's name when frontmatter omits it, and
|
|
375
|
+
must match `^[a-z0-9-]+$`, be at most 64 characters, and not start or end with
|
|
376
|
+
`-` or contain `--`.
|
|
377
|
+
- `description` is required (there is no fallback), is trimmed, and must be
|
|
378
|
+
1–1024 characters.
|
|
379
|
+
- A file failing either check is silently skipped, so a skill that does not
|
|
380
|
+
appear almost always has an invalid name or a missing description.
|
|
381
|
+
|
|
382
|
+
### When a skill triggers
|
|
383
|
+
|
|
384
|
+
Happy Agent injects the catalog — name, description, and location — into the system
|
|
385
|
+
prompt. The model uses a skill when the user names it or the task clearly matches
|
|
386
|
+
its description, reads the complete file before acting, and resolves relative
|
|
387
|
+
paths in the skill against the directory containing that `SKILL.md`.
|
|
388
|
+
|
|
389
|
+
Skill files are **instruction resources only**. Frontmatter that requests hooks,
|
|
390
|
+
shell execution, model switching, or permission changes is ignored by design; do
|
|
391
|
+
not add such fields expecting them to work.
|
|
392
|
+
|
|
393
|
+
## MCP servers
|
|
394
|
+
|
|
395
|
+
MCP is how Happy Agent consumes tools it did not write. Servers are configured in TOML,
|
|
396
|
+
not installed.
|
|
397
|
+
|
|
398
|
+
### Configuration
|
|
399
|
+
|
|
400
|
+
Config layers, resolved in this order:
|
|
401
|
+
|
|
402
|
+
| Source | File |
|
|
403
|
+
| --------- | ------------------------------------------------------------------------------------------------------- |
|
|
404
|
+
| `global` | `happy.toml` in Happy Agent's config directory — `~/Happy/Config` on macOS or `~/happy/config` on Linux |
|
|
405
|
+
| `runtime` | `~/.happy/agent/runtime.toml` |
|
|
406
|
+
| `project` | `happy.toml` in the project |
|
|
407
|
+
|
|
408
|
+
Global and runtime are trusted layers. Project entries are separate: a project
|
|
409
|
+
server with the same name as a trusted one does not override it — the trusted one
|
|
410
|
+
wins and is flagged `projectShadowed`.
|
|
411
|
+
|
|
412
|
+
A local stdio server:
|
|
413
|
+
|
|
414
|
+
```toml
|
|
415
|
+
[mcp_servers.docs]
|
|
416
|
+
command = "docs-mcp-server"
|
|
417
|
+
args = ["--stdio"]
|
|
418
|
+
env = { API_TOKEN = "token" }
|
|
419
|
+
cwd = "/absolute/working/directory"
|
|
420
|
+
enabled = true
|
|
421
|
+
startup_timeout_sec = 10
|
|
422
|
+
tool_timeout_sec = 30
|
|
423
|
+
enabled_tools = ["search"]
|
|
424
|
+
disabled_tools = []
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
A streamable HTTP server:
|
|
428
|
+
|
|
429
|
+
```toml
|
|
430
|
+
[mcp_servers.issues]
|
|
431
|
+
url = "https://example.com/mcp"
|
|
432
|
+
transport = "http"
|
|
433
|
+
http_headers = { "X-Client" = "Happy Agent" }
|
|
434
|
+
bearer_token_env_var = "ISSUES_MCP_TOKEN"
|
|
435
|
+
oauth_client_id_env_var = "MCP_CLIENT_ID"
|
|
436
|
+
oauth_client_secret_env_var = "MCP_CLIENT_SECRET"
|
|
437
|
+
oauth_scopes = ["tools:read"]
|
|
438
|
+
enabled = true
|
|
439
|
+
startup_timeout_sec = 10
|
|
440
|
+
tool_timeout_sec = 30
|
|
441
|
+
enabled_tools = ["search"]
|
|
442
|
+
disabled_tools = []
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Exactly one of `command` and `url` must be present. `transport` is only accepted
|
|
446
|
+
as `"http"`; a `command` entry is stdio implicitly. Unknown keys in an
|
|
447
|
+
`[mcp_servers.*]` table are a configuration error, so do not guess names.
|
|
448
|
+
|
|
449
|
+
MCP tools, resources, resource templates, prompts, pagination, form elicitation,
|
|
450
|
+
bearer tokens, and OAuth client credentials are supported, and live tool
|
|
451
|
+
discovery lets a session use tools added after startup.
|
|
452
|
+
|
|
453
|
+
### Boundary rules
|
|
454
|
+
|
|
455
|
+
These are product rules, enforced on the tool definitions themselves:
|
|
456
|
+
|
|
457
|
+
- Every MCP tool sets `requiresAutoOrFullAccess: true`, because the server can
|
|
458
|
+
act outside Happy Agent's local sandbox. MCP is unavailable in Read only and Workspace
|
|
459
|
+
write.
|
|
460
|
+
- Every direct and dynamic MCP tool invocation is reviewed in Auto
|
|
461
|
+
(`shouldReviewInAutoMode: () => true`), and the approval text discloses the
|
|
462
|
+
external boundary.
|
|
463
|
+
- Happy Agent-owned protocol operations that are intrinsically read-only skip review:
|
|
464
|
+
`list_mcp_tools`, `list_mcp_resources`, `list_mcp_resource_templates`,
|
|
465
|
+
`read_mcp_resource`, and `list_mcp_prompts`. The operations that reach the
|
|
466
|
+
server to do something — `call_mcp_tool` and `get_mcp_prompt` — are reviewed.
|
|
467
|
+
- Server-supplied annotations such as `readOnlyHint` are untrusted metadata. They
|
|
468
|
+
are never authorization evidence and never a reason to skip review.
|
|
469
|
+
- MCP settings coming from a project require a one-time trust decision before the
|
|
470
|
+
server starts. The decision is fingerprinted and stored, and Happy Agent asks again if
|
|
471
|
+
the server configuration changes.
|
|
472
|
+
- Stdio servers run as local processes with the daemon environment and are **not**
|
|
473
|
+
restricted by the session filesystem sandbox. Only configure servers you trust.
|
|
474
|
+
|
|
475
|
+
Plugin-contributed MCP servers travel the same composite provider path as
|
|
476
|
+
configured ones, so tool assembly, `AgentContext`, and `PermissionContext`
|
|
477
|
+
behavior stay shared.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Happy Agent client and integrations
|
|
482
|
+
|
|
483
|
+
External applications drive the daemon through `@slopus/happy-agent-client`.
|
|
484
|
+
The host supplies a Fetch implementation and bearer token; the client exposes
|
|
485
|
+
the typed `/v0` request and SSE contracts without reading credentials or daemon
|
|
486
|
+
state directly. See `packages/happy-agent/API.md` for the complete public
|
|
487
|
+
surface.
|
|
488
|
+
|
|
489
|
+
### Other integration surfaces
|
|
490
|
+
|
|
491
|
+
- **Project and workspace files** — `GET`/`PUT` `/projects/{id}/file` and
|
|
492
|
+
`/projects/{id}/workspaces/{id}/file`, with SHA-256 optimistic concurrency, a
|
|
493
|
+
32 MB limit, and Happy Agent's workspace boundary applied.
|
|
494
|
+
- **HTTP proxy** — `CONNECT /projects/{id}/proxy` (and the workspace-scoped form)
|
|
495
|
+
tunnels ordinary HTTP through the authenticated daemon connection.
|
|
496
|
+
- **Happy mobile synchronization** — a first-class daemon feature, gated by both
|
|
497
|
+
the embedder's `happyIntegration` option and the user-wide
|
|
498
|
+
`[settings] happy_integration` config value, both fail-closed.
|
|
499
|
+
- **Remote terminals** — Happy Agent's own libghostty-based terminal protocol. It is
|
|
500
|
+
deliberately unspecified for outside consumers right now: it exists to work
|
|
501
|
+
inside Happy and Happy Agent.
|
|
502
|
+
|
|
503
|
+
### Planned, not implemented
|
|
504
|
+
|
|
505
|
+
Do not write code against these yet; they are directions rather than features:
|
|
506
|
+
|
|
507
|
+
- **Electron-isolated plugin UI.** Instant mounting under a proper Electron
|
|
508
|
+
isolation mechanism is the goal; the current implementation serves bounded
|
|
509
|
+
static bundles and typed actions and leaves mounting to the host.
|
|
510
|
+
- **A published terminal protocol specification.** The protocol may be improved
|
|
511
|
+
and specified later; today it is proprietary.
|
|
512
|
+
- **A wider plugin API.** The SDK surface grows as plugins ask for it. Extend it
|
|
513
|
+
deliberately rather than reaching around it.
|
|
514
|
+
|
|
515
|
+
Happy Agent deliberately has **no** plugin marketplace, no plugin identifier scheme, no
|
|
516
|
+
plugin permission model, and no separate Happy Agent login flow. These are settled
|
|
517
|
+
non-goals, not gaps to fill.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Subagents and workflows
|
|
522
|
+
|
|
523
|
+
The lightest extension mechanism is runtime, not installed: spawn a subagent with
|
|
524
|
+
its own model and effort, or run a deterministic multi-agent workflow. Nothing is
|
|
525
|
+
compiled and nothing persists — it is how you extend a single task rather than
|
|
526
|
+
the product.
|
|
527
|
+
|
|
528
|
+
Use it when work is bounded and parallelizable (research, review, verification),
|
|
529
|
+
or when a job is genuinely a pipeline over many items. Every subagent needs an
|
|
530
|
+
explicit model and effort; nothing is inherited.
|
|
531
|
+
|
|
532
|
+
Full guidance — when to delegate, choosing models and effort, background agents,
|
|
533
|
+
agent-to-agent messaging, and workflow scripting — is in
|
|
534
|
+
[`agents-and-collaboration.md`](agents-and-collaboration.md).
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Rules that apply to every extension
|
|
539
|
+
|
|
540
|
+
- **One permission model.** Codex, Claude, Pi, Grok, MCP, and plugin tools all
|
|
541
|
+
execute through the same `AgentContext`, filesystem boundary, shell sandbox,
|
|
542
|
+
and `PermissionContext`. Provider differences belong in tool names, argument
|
|
543
|
+
schemas, and result formatting — never in a separate security path.
|
|
544
|
+
- **Each tool owns its Auto behavior.** `shouldReviewInAutoMode` is required.
|
|
545
|
+
Define `shouldRunInFullAccessInAutoMode` only when a reviewed action must cross
|
|
546
|
+
the sandbox; review alone must not imply elevation. Never dispatch permission
|
|
547
|
+
behavior from a tool-name list, prefix, or provider key.
|
|
548
|
+
- **Common tools are assembled once.** A capability that belongs to Happy Agent rather
|
|
549
|
+
than a vendor goes through the shared common-tool entry point so any future
|
|
550
|
+
model picks it up without per-provider work.
|
|
551
|
+
- **TypeBox for all runtime validation.** Derive TypeScript types with `Static`;
|
|
552
|
+
do not hand-write parallel interfaces or ad hoc type predicates.
|
|
553
|
+
- **Early-stage compatibility.** Change schemas and behavior directly instead of
|
|
554
|
+
adding aliases or migration branches — but never edit an existing database
|
|
555
|
+
migration.
|
|
556
|
+
- **Use `pnpm`.** Never `npm`, `npx`, or `yarn`.
|
|
557
|
+
- **Test at the boundary.** For anything spanning terminal input or rendering,
|
|
558
|
+
inference, tools, processes, filesystem effects, or permissions, write a gym
|
|
559
|
+
test, and reproduce a bug in the gym before changing production code.
|
|
560
|
+
- **User-facing text is human-readable English.** Convert identifiers, enum
|
|
561
|
+
values, and file names into clear display text before rendering them.
|