@kmmao/happy-agent 0.5.5 → 0.7.0

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/dist/index.cjs CHANGED
@@ -39,7 +39,7 @@ function _interopNamespaceDefault(e) {
39
39
 
40
40
  var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
41
41
 
42
- var version = "0.5.5";
42
+ var version = "0.7.0";
43
43
 
44
44
  function loadConfig() {
45
45
  const serverUrl = (process.env.HAPPY_SERVER_URL ?? "https://s.sangreal.code.xycloud.info:2443").replace(/\/+$/, "");
@@ -2261,6 +2261,8 @@ function handleTaskTrigger(data, serverUrl, _authToken, scheduler) {
2261
2261
  approvedNewDirectoryCreation: true,
2262
2262
  automationContext: { kind: "task", trigger: "task-dispatch", projectId: data.projectId },
2263
2263
  environmentVariables: {
2264
+ // Profile-resolved env applied first so task-specific overrides win.
2265
+ ...data.runtimeProfile?.environmentVariables ?? {},
2264
2266
  HAPPY_INITIAL_PROMPT_FILE: promptFile,
2265
2267
  HAPPY_TASK_ID: data.taskId,
2266
2268
  HAPPY_TASK_PRIORITY: data.priority,
@@ -4885,6 +4887,182 @@ z__namespace.discriminatedUnion("allowed", [
4885
4887
  VoiceTokenDeniedSchema
4886
4888
  ]);
4887
4889
 
4890
+ const CODEX_APP_SERVER_BACKEND = "codex-app-server";
4891
+ const CODEX_MCP_LEGACY_BACKEND = "codex-mcp-legacy";
4892
+ const CodexBackendModeSchema = z__namespace.enum([
4893
+ "auto",
4894
+ CODEX_APP_SERVER_BACKEND,
4895
+ CODEX_MCP_LEGACY_BACKEND
4896
+ ]);
4897
+ const CodexRequestedBackendSchema = CodexBackendModeSchema;
4898
+ const CodexResolvedBackendSchema = z__namespace.enum([
4899
+ CODEX_APP_SERVER_BACKEND,
4900
+ CODEX_MCP_LEGACY_BACKEND
4901
+ ]);
4902
+ const CodexConfigModeSchema = z__namespace.enum([
4903
+ "inherit",
4904
+ "managed-profile",
4905
+ "managed-overrides"
4906
+ ]);
4907
+ const CODEX_REQUESTED_BACKEND_ALIASES = {
4908
+ auto: ["", "auto"],
4909
+ [CODEX_APP_SERVER_BACKEND]: ["app-server", "appserver", CODEX_APP_SERVER_BACKEND],
4910
+ [CODEX_MCP_LEGACY_BACKEND]: [
4911
+ "legacy",
4912
+ "mcp",
4913
+ "mcp-legacy",
4914
+ CODEX_MCP_LEGACY_BACKEND
4915
+ ]
4916
+ };
4917
+ new Map(
4918
+ Object.entries(CODEX_REQUESTED_BACKEND_ALIASES).flatMap(
4919
+ ([backend, aliases]) => aliases.map((alias) => [alias, backend])
4920
+ )
4921
+ );
4922
+
4923
+ function isTemplateAwareUrl(value) {
4924
+ if (!value) return true;
4925
+ if (/^\$\{[A-Z_][A-Z0-9_]*(:-[^}]*)?\}$/.test(value)) return true;
4926
+ try {
4927
+ new URL(value);
4928
+ return true;
4929
+ } catch {
4930
+ return false;
4931
+ }
4932
+ }
4933
+ const BuiltInAIBackendProfileIdSchema = z.z.enum([
4934
+ "anthropic",
4935
+ "deepseek",
4936
+ "zai",
4937
+ "openai",
4938
+ "azure-openai",
4939
+ "minimax",
4940
+ "kimi"
4941
+ ]);
4942
+ new Set(
4943
+ BuiltInAIBackendProfileIdSchema.options
4944
+ );
4945
+ const EnvironmentVariableSchema = z.z.object({
4946
+ name: z.z.string().regex(/^[A-Z_][A-Z0-9_]*$/, "Invalid environment variable name"),
4947
+ value: z.z.string()
4948
+ });
4949
+ const ProfileCompatibilitySchema = z.z.object({
4950
+ claude: z.z.boolean().default(true),
4951
+ codex: z.z.boolean().default(true),
4952
+ gemini: z.z.boolean().default(true)
4953
+ });
4954
+ const AnthropicConfigSchema = z.z.object({
4955
+ baseUrl: z.z.string().refine(isTemplateAwareUrl, {
4956
+ message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
4957
+ }).optional(),
4958
+ authToken: z.z.string().optional(),
4959
+ model: z.z.string().optional()
4960
+ });
4961
+ const OpenAIConfigSchema = z.z.object({
4962
+ apiKey: z.z.string().optional(),
4963
+ baseUrl: z.z.string().refine(isTemplateAwareUrl, {
4964
+ message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
4965
+ }).optional(),
4966
+ model: z.z.string().optional()
4967
+ });
4968
+ const AzureOpenAIConfigSchema = z.z.object({
4969
+ apiKey: z.z.string().optional(),
4970
+ endpoint: z.z.string().refine(isTemplateAwareUrl, {
4971
+ message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
4972
+ }).optional(),
4973
+ apiVersion: z.z.string().optional(),
4974
+ deploymentName: z.z.string().optional()
4975
+ });
4976
+ const TogetherAIConfigSchema = z.z.object({
4977
+ apiKey: z.z.string().optional(),
4978
+ model: z.z.string().optional()
4979
+ });
4980
+ const CodexConfigSchema = z.z.object({
4981
+ backendMode: CodexBackendModeSchema.optional(),
4982
+ configMode: CodexConfigModeSchema.optional(),
4983
+ codexProfileName: z.z.string().optional(),
4984
+ model: z.z.string().optional(),
4985
+ reasoningEffort: z.z.string().optional(),
4986
+ reasoningSummary: z.z.string().optional(),
4987
+ verbosity: z.z.string().optional(),
4988
+ personality: z.z.string().optional(),
4989
+ serviceTier: z.z.string().optional(),
4990
+ webSearchEnabled: z.z.boolean().optional(),
4991
+ approvalPolicy: z.z.string().optional(),
4992
+ sandboxMode: z.z.string().optional()
4993
+ });
4994
+ const TmuxConfigSchema = z.z.object({
4995
+ sessionName: z.z.string().optional(),
4996
+ tmpDir: z.z.string().optional(),
4997
+ updateEnvironment: z.z.boolean().optional()
4998
+ });
4999
+ const CustomModelSchema = z.z.object({
5000
+ id: z.z.string().min(1),
5001
+ name: z.z.string().min(1).max(100),
5002
+ description: z.z.string().nullish()
5003
+ });
5004
+ const DefaultPermissionModeSchema = z.z.enum([
5005
+ "default",
5006
+ "acceptEdits",
5007
+ "auto",
5008
+ "bypassPermissions",
5009
+ "plan",
5010
+ "read-only",
5011
+ "safe-yolo",
5012
+ "yolo"
5013
+ ]);
5014
+ z.z.object({
5015
+ id: z.z.string().min(1),
5016
+ name: z.z.string().min(1).max(100),
5017
+ description: z.z.string().max(500).optional(),
5018
+ anthropicConfig: AnthropicConfigSchema.optional(),
5019
+ openaiConfig: OpenAIConfigSchema.optional(),
5020
+ azureOpenAIConfig: AzureOpenAIConfigSchema.optional(),
5021
+ togetherAIConfig: TogetherAIConfigSchema.optional(),
5022
+ codexConfig: CodexConfigSchema.optional(),
5023
+ tmuxConfig: TmuxConfigSchema.optional(),
5024
+ startupBashScript: z.z.string().optional(),
5025
+ environmentVariables: z.z.array(EnvironmentVariableSchema).default([]),
5026
+ customModels: z.z.array(CustomModelSchema).optional(),
5027
+ modelMappings: z.z.record(z.z.string(), z.z.string()).optional(),
5028
+ defaultSessionType: z.z.enum(["simple", "worktree"]).optional(),
5029
+ defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5030
+ defaultModelMode: z.z.string().optional(),
5031
+ compatibility: ProfileCompatibilitySchema.default({
5032
+ claude: true,
5033
+ codex: true,
5034
+ gemini: true
5035
+ }),
5036
+ isBuiltIn: z.z.boolean().default(false),
5037
+ createdAt: z.z.number().default(() => Date.now()),
5038
+ updatedAt: z.z.number().default(() => Date.now()),
5039
+ version: z.z.string().default("1.0.0")
5040
+ });
5041
+ const RuntimeProfileSourceSchema = z.z.enum([
5042
+ "built-in-profile",
5043
+ "account-profile",
5044
+ "local-profile",
5045
+ "ad-hoc"
5046
+ ]);
5047
+ const RuntimeProfileTrustSchema = z.z.enum(["trusted", "untrusted"]);
5048
+ const RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION = 1;
5049
+ const ResolvedRuntimeProfileSchema = z.z.object({
5050
+ schemaVersion: z.z.literal(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION).default(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION),
5051
+ profileId: z.z.string().optional(),
5052
+ profileName: z.z.string().optional(),
5053
+ source: RuntimeProfileSourceSchema,
5054
+ trust: RuntimeProfileTrustSchema,
5055
+ isBuiltIn: z.z.boolean().optional(),
5056
+ compatibility: ProfileCompatibilitySchema.optional(),
5057
+ environmentVariables: z.z.record(z.z.string(), z.z.string()).default({}),
5058
+ startupBashScript: z.z.string().optional(),
5059
+ customModels: z.z.array(CustomModelSchema).optional(),
5060
+ modelMappings: z.z.record(z.z.string(), z.z.string()).optional(),
5061
+ defaultSessionType: z.z.enum(["simple", "worktree"]).optional(),
5062
+ defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5063
+ defaultModelMode: z.z.string().optional()
5064
+ });
5065
+
4888
5066
  const TaskPrioritySchema = z__namespace.enum([
4889
5067
  "urgent",
4890
5068
  // User-initiated, needs immediate execution
@@ -4943,7 +5121,12 @@ z__namespace.object({
4943
5121
  // Encrypted by App
4944
5122
  priority: TaskPrioritySchema.default("user"),
4945
5123
  maxAttempts: z__namespace.number().int().min(1).max(10).default(3),
4946
- skillIds: z__namespace.array(z__namespace.string()).max(10).default([])
5124
+ skillIds: z__namespace.array(z__namespace.string()).max(10).default([]),
5125
+ // Profile binding (wire 0.15.0). Business key — built-in id like
5126
+ // "anthropic" or AiBackendProfile.profileKey for the account. Optional:
5127
+ // when omitted the server falls back to Project.supervisorConfig.defaultProfileId
5128
+ // via the unified runtimeProfileResolver (feature-flagged).
5129
+ profileId: z__namespace.string().optional()
4947
5130
  });
4948
5131
  z__namespace.object({
4949
5132
  type: z__namespace.literal("task-trigger"),
@@ -4961,8 +5144,16 @@ z__namespace.object({
4961
5144
  })).optional(),
4962
5145
  agentType: z__namespace.string().nullable().optional(),
4963
5146
  // "claude" | "codex" | "gemini" — null = inherit CLI default
4964
- modelOverride: z__namespace.string().nullable().optional()
5147
+ modelOverride: z__namespace.string().nullable().optional(),
4965
5148
  // e.g. "claude-sonnet-4-20250514" — null = agent default
5149
+ // Profile binding for this task. `profileId` references the AIBackendProfile
5150
+ // selected when the task was created (Task.profileId column). `runtimeProfile`
5151
+ // is the resolved snapshot (env vars, startup script, permission mode, etc.)
5152
+ // that the CLI should honor when spawning the session. Both optional for
5153
+ // backward compatibility; starting from wire 0.14.0 + server unified resolver
5154
+ // these are always populated for scheduled/webhook/manual tasks.
5155
+ profileId: z__namespace.string().optional(),
5156
+ runtimeProfile: ResolvedRuntimeProfileSchema.optional()
4966
5157
  });
4967
5158
  const TaskOutcomeSchema = z__namespace.enum([
4968
5159
  "completed",
@@ -5140,182 +5331,6 @@ z__namespace.object({
5140
5331
  exitCode: z__namespace.number()
5141
5332
  });
5142
5333
 
5143
- const CODEX_APP_SERVER_BACKEND = "codex-app-server";
5144
- const CODEX_MCP_LEGACY_BACKEND = "codex-mcp-legacy";
5145
- const CodexBackendModeSchema = z__namespace.enum([
5146
- "auto",
5147
- CODEX_APP_SERVER_BACKEND,
5148
- CODEX_MCP_LEGACY_BACKEND
5149
- ]);
5150
- const CodexRequestedBackendSchema = CodexBackendModeSchema;
5151
- const CodexResolvedBackendSchema = z__namespace.enum([
5152
- CODEX_APP_SERVER_BACKEND,
5153
- CODEX_MCP_LEGACY_BACKEND
5154
- ]);
5155
- const CodexConfigModeSchema = z__namespace.enum([
5156
- "inherit",
5157
- "managed-profile",
5158
- "managed-overrides"
5159
- ]);
5160
- const CODEX_REQUESTED_BACKEND_ALIASES = {
5161
- auto: ["", "auto"],
5162
- [CODEX_APP_SERVER_BACKEND]: ["app-server", "appserver", CODEX_APP_SERVER_BACKEND],
5163
- [CODEX_MCP_LEGACY_BACKEND]: [
5164
- "legacy",
5165
- "mcp",
5166
- "mcp-legacy",
5167
- CODEX_MCP_LEGACY_BACKEND
5168
- ]
5169
- };
5170
- new Map(
5171
- Object.entries(CODEX_REQUESTED_BACKEND_ALIASES).flatMap(
5172
- ([backend, aliases]) => aliases.map((alias) => [alias, backend])
5173
- )
5174
- );
5175
-
5176
- function isTemplateAwareUrl(value) {
5177
- if (!value) return true;
5178
- if (/^\$\{[A-Z_][A-Z0-9_]*(:-[^}]*)?\}$/.test(value)) return true;
5179
- try {
5180
- new URL(value);
5181
- return true;
5182
- } catch {
5183
- return false;
5184
- }
5185
- }
5186
- const BuiltInAIBackendProfileIdSchema = z.z.enum([
5187
- "anthropic",
5188
- "deepseek",
5189
- "zai",
5190
- "openai",
5191
- "azure-openai",
5192
- "minimax",
5193
- "kimi"
5194
- ]);
5195
- new Set(
5196
- BuiltInAIBackendProfileIdSchema.options
5197
- );
5198
- const EnvironmentVariableSchema = z.z.object({
5199
- name: z.z.string().regex(/^[A-Z_][A-Z0-9_]*$/, "Invalid environment variable name"),
5200
- value: z.z.string()
5201
- });
5202
- const ProfileCompatibilitySchema = z.z.object({
5203
- claude: z.z.boolean().default(true),
5204
- codex: z.z.boolean().default(true),
5205
- gemini: z.z.boolean().default(true)
5206
- });
5207
- const AnthropicConfigSchema = z.z.object({
5208
- baseUrl: z.z.string().refine(isTemplateAwareUrl, {
5209
- message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
5210
- }).optional(),
5211
- authToken: z.z.string().optional(),
5212
- model: z.z.string().optional()
5213
- });
5214
- const OpenAIConfigSchema = z.z.object({
5215
- apiKey: z.z.string().optional(),
5216
- baseUrl: z.z.string().refine(isTemplateAwareUrl, {
5217
- message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
5218
- }).optional(),
5219
- model: z.z.string().optional()
5220
- });
5221
- const AzureOpenAIConfigSchema = z.z.object({
5222
- apiKey: z.z.string().optional(),
5223
- endpoint: z.z.string().refine(isTemplateAwareUrl, {
5224
- message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
5225
- }).optional(),
5226
- apiVersion: z.z.string().optional(),
5227
- deploymentName: z.z.string().optional()
5228
- });
5229
- const TogetherAIConfigSchema = z.z.object({
5230
- apiKey: z.z.string().optional(),
5231
- model: z.z.string().optional()
5232
- });
5233
- const CodexConfigSchema = z.z.object({
5234
- backendMode: CodexBackendModeSchema.optional(),
5235
- configMode: CodexConfigModeSchema.optional(),
5236
- codexProfileName: z.z.string().optional(),
5237
- model: z.z.string().optional(),
5238
- reasoningEffort: z.z.string().optional(),
5239
- reasoningSummary: z.z.string().optional(),
5240
- verbosity: z.z.string().optional(),
5241
- personality: z.z.string().optional(),
5242
- serviceTier: z.z.string().optional(),
5243
- webSearchEnabled: z.z.boolean().optional(),
5244
- approvalPolicy: z.z.string().optional(),
5245
- sandboxMode: z.z.string().optional()
5246
- });
5247
- const TmuxConfigSchema = z.z.object({
5248
- sessionName: z.z.string().optional(),
5249
- tmpDir: z.z.string().optional(),
5250
- updateEnvironment: z.z.boolean().optional()
5251
- });
5252
- const CustomModelSchema = z.z.object({
5253
- id: z.z.string().min(1),
5254
- name: z.z.string().min(1).max(100),
5255
- description: z.z.string().nullish()
5256
- });
5257
- const DefaultPermissionModeSchema = z.z.enum([
5258
- "default",
5259
- "acceptEdits",
5260
- "auto",
5261
- "bypassPermissions",
5262
- "plan",
5263
- "read-only",
5264
- "safe-yolo",
5265
- "yolo"
5266
- ]);
5267
- z.z.object({
5268
- id: z.z.string().min(1),
5269
- name: z.z.string().min(1).max(100),
5270
- description: z.z.string().max(500).optional(),
5271
- anthropicConfig: AnthropicConfigSchema.optional(),
5272
- openaiConfig: OpenAIConfigSchema.optional(),
5273
- azureOpenAIConfig: AzureOpenAIConfigSchema.optional(),
5274
- togetherAIConfig: TogetherAIConfigSchema.optional(),
5275
- codexConfig: CodexConfigSchema.optional(),
5276
- tmuxConfig: TmuxConfigSchema.optional(),
5277
- startupBashScript: z.z.string().optional(),
5278
- environmentVariables: z.z.array(EnvironmentVariableSchema).default([]),
5279
- customModels: z.z.array(CustomModelSchema).optional(),
5280
- modelMappings: z.z.record(z.z.string(), z.z.string()).optional(),
5281
- defaultSessionType: z.z.enum(["simple", "worktree"]).optional(),
5282
- defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5283
- defaultModelMode: z.z.string().optional(),
5284
- compatibility: ProfileCompatibilitySchema.default({
5285
- claude: true,
5286
- codex: true,
5287
- gemini: true
5288
- }),
5289
- isBuiltIn: z.z.boolean().default(false),
5290
- createdAt: z.z.number().default(() => Date.now()),
5291
- updatedAt: z.z.number().default(() => Date.now()),
5292
- version: z.z.string().default("1.0.0")
5293
- });
5294
- const RuntimeProfileSourceSchema = z.z.enum([
5295
- "built-in-profile",
5296
- "account-profile",
5297
- "local-profile",
5298
- "ad-hoc"
5299
- ]);
5300
- const RuntimeProfileTrustSchema = z.z.enum(["trusted", "untrusted"]);
5301
- const RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION = 1;
5302
- z.z.object({
5303
- schemaVersion: z.z.literal(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION).default(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION),
5304
- profileId: z.z.string().optional(),
5305
- profileName: z.z.string().optional(),
5306
- source: RuntimeProfileSourceSchema,
5307
- trust: RuntimeProfileTrustSchema,
5308
- isBuiltIn: z.z.boolean().optional(),
5309
- compatibility: ProfileCompatibilitySchema.optional(),
5310
- environmentVariables: z.z.record(z.z.string(), z.z.string()).default({}),
5311
- startupBashScript: z.z.string().optional(),
5312
- customModels: z.z.array(CustomModelSchema).optional(),
5313
- modelMappings: z.z.record(z.z.string(), z.z.string()).optional(),
5314
- defaultSessionType: z.z.enum(["simple", "worktree"]).optional(),
5315
- defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5316
- defaultModelMode: z.z.string().optional()
5317
- });
5318
-
5319
5334
  const sessionProgressTodoStatusSchema = z__namespace.enum([
5320
5335
  "pending",
5321
5336
  "in_progress",
@@ -5567,6 +5582,90 @@ z__namespace.object({
5567
5582
  mcpServers: z__namespace.array(CodexMcpServerSummarySchema).optional()
5568
5583
  });
5569
5584
 
5585
+ z.z.object({}).strict();
5586
+ z.z.object({
5587
+ /** Pre-formatted single-line summary (same text `/usage` prints in non-interactive mode). */
5588
+ formatted: z.z.string(),
5589
+ /** Total USD spent on this session so far. */
5590
+ totalUsd: z.z.number().nonnegative(),
5591
+ /** Optional breakdown by model. */
5592
+ byModel: z.z.record(z.z.string(), z.z.object({
5593
+ inputTokens: z.z.number().int().nonnegative(),
5594
+ outputTokens: z.z.number().int().nonnegative(),
5595
+ cacheCreationInputTokens: z.z.number().int().nonnegative().optional(),
5596
+ cacheReadInputTokens: z.z.number().int().nonnegative().optional(),
5597
+ costUsd: z.z.number().nonnegative()
5598
+ })).optional()
5599
+ }).strict();
5600
+ z.z.object({}).strict();
5601
+ z.z.object({
5602
+ /** Remote Claude Code binary version string, e.g. "2.1.119". */
5603
+ version: z.z.string(),
5604
+ /** Path to the binary if resolvable, informational only. */
5605
+ binaryPath: z.z.string().optional(),
5606
+ /** Happy-cli package version for context. */
5607
+ happyCliVersion: z.z.string().optional()
5608
+ }).strict();
5609
+ z.z.object({
5610
+ /** Agent color name or the literal "default" to reset. */
5611
+ color: z.z.string().min(1).max(64)
5612
+ }).strict();
5613
+ z.z.object({
5614
+ success: z.z.literal(true),
5615
+ color: z.z.string()
5616
+ }).strict();
5617
+ z.z.object({
5618
+ /** Path relative to cwd or absolute. CLI applies path blacklist + Read-tool permission gating. */
5619
+ path: z.z.string().min(1).max(4096),
5620
+ /** Optional max bytes to return; server-side hard cap enforces <= 1 MiB. */
5621
+ maxBytes: z.z.number().int().positive().max(1024 * 1024).optional()
5622
+ }).strict();
5623
+ z.z.object({
5624
+ /** Null when permission denied / missing / blocked by CLI path blacklist. */
5625
+ result: z.z.object({
5626
+ contents: z.z.string(),
5627
+ absPath: z.z.string(),
5628
+ truncated: z.z.boolean().optional()
5629
+ }).nullable(),
5630
+ /** Machine-readable reason when result is null. */
5631
+ deniedReason: z.z.enum(["not_found", "permission_denied", "blacklisted_path", "too_large", "error"]).optional()
5632
+ }).strict();
5633
+ z.z.object({
5634
+ /** Fully-qualified MCP tool name, e.g. `mcp__fs__read`. Must pass CLI whitelist. */
5635
+ tool: z.z.string().regex(/^mcp__[a-z0-9_-]+__[a-z0-9_.-]+$/i, "must be of form mcp__<server>__<tool>"),
5636
+ /** Tool arguments; schema-free, CLI passes through to MCP server. */
5637
+ arguments: z.z.record(z.z.string(), z.z.unknown()).optional(),
5638
+ /**
5639
+ * Client-nonce that App must have displayed to the user in a 2-step
5640
+ * confirm dialog. CLI uses this only for audit logging — the actual
5641
+ * confirmation happens on App side and is logged for security review.
5642
+ */
5643
+ clientConfirmToken: z.z.string().min(8).max(128)
5644
+ }).strict();
5645
+ z.z.object({
5646
+ success: z.z.boolean(),
5647
+ /** Tool response payload, shape defined by each MCP tool. */
5648
+ result: z.z.unknown().optional(),
5649
+ /** Error code for UI to localize; see CLI handler for enum values. */
5650
+ errorCode: z.z.enum([
5651
+ "not_whitelisted",
5652
+ "server_unavailable",
5653
+ "tool_not_found",
5654
+ "invalid_arguments",
5655
+ "permission_denied",
5656
+ /**
5657
+ * SDK 0.2.119 defines the `mcp_call` control protocol type but does
5658
+ * not expose a public runtime method on the `Query` interface. Until
5659
+ * upstream lands a `callMcpTool()` / equivalent, the CLI handler
5660
+ * returns this code so the App can surface an honest "waiting on
5661
+ * SDK" state instead of masking the gap as a server error.
5662
+ */
5663
+ "sdk_not_implemented",
5664
+ "unknown"
5665
+ ]).optional(),
5666
+ errorMessage: z.z.string().optional()
5667
+ }).strict();
5668
+
5570
5669
  function buildSummaryRefreshPrompt(requestId) {
5571
5670
  return [
5572
5671
  "Please call mcp__happy__update_session_summary to record the current session summary with: goal, currentFocus, keyDecisions (if any), openQuestions (if any), impactScope (if any). Keep it accurate and concise.",
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import * as z from 'zod';
3
+ import { z as z$1 } from 'zod';
3
4
  import { EventEmitter } from 'node:events';
4
5
  import { Socket } from 'socket.io-client';
5
6
 
@@ -354,6 +355,52 @@ declare const DaemonStateSchema: z.ZodObject<{
354
355
  }, z.core.$strip>;
355
356
  type DaemonState = z.infer<typeof DaemonStateSchema>;
356
357
 
358
+ declare const ResolvedRuntimeProfileSchema: z$1.ZodObject<{
359
+ schemaVersion: z$1.ZodDefault<z$1.ZodLiteral<1>>;
360
+ profileId: z$1.ZodOptional<z$1.ZodString>;
361
+ profileName: z$1.ZodOptional<z$1.ZodString>;
362
+ source: z$1.ZodEnum<{
363
+ "built-in-profile": "built-in-profile";
364
+ "account-profile": "account-profile";
365
+ "local-profile": "local-profile";
366
+ "ad-hoc": "ad-hoc";
367
+ }>;
368
+ trust: z$1.ZodEnum<{
369
+ trusted: "trusted";
370
+ untrusted: "untrusted";
371
+ }>;
372
+ isBuiltIn: z$1.ZodOptional<z$1.ZodBoolean>;
373
+ compatibility: z$1.ZodOptional<z$1.ZodObject<{
374
+ claude: z$1.ZodDefault<z$1.ZodBoolean>;
375
+ codex: z$1.ZodDefault<z$1.ZodBoolean>;
376
+ gemini: z$1.ZodDefault<z$1.ZodBoolean>;
377
+ }, z$1.core.$strip>>;
378
+ environmentVariables: z$1.ZodDefault<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>;
379
+ startupBashScript: z$1.ZodOptional<z$1.ZodString>;
380
+ customModels: z$1.ZodOptional<z$1.ZodArray<z$1.ZodObject<{
381
+ id: z$1.ZodString;
382
+ name: z$1.ZodString;
383
+ description: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodString>>;
384
+ }, z$1.core.$strip>>>;
385
+ modelMappings: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>;
386
+ defaultSessionType: z$1.ZodOptional<z$1.ZodEnum<{
387
+ simple: "simple";
388
+ worktree: "worktree";
389
+ }>>;
390
+ defaultPermissionMode: z$1.ZodOptional<z$1.ZodEnum<{
391
+ default: "default";
392
+ acceptEdits: "acceptEdits";
393
+ bypassPermissions: "bypassPermissions";
394
+ plan: "plan";
395
+ auto: "auto";
396
+ "read-only": "read-only";
397
+ "safe-yolo": "safe-yolo";
398
+ yolo: "yolo";
399
+ }>>;
400
+ defaultModelMode: z$1.ZodOptional<z$1.ZodString>;
401
+ }, z$1.core.$strip>;
402
+ type ResolvedRuntimeProfile = z$1.infer<typeof ResolvedRuntimeProfileSchema>;
403
+
357
404
  type EncryptionVariant = "legacy" | "dataKey";
358
405
  type SessionEncryption = {
359
406
  readonly key: Uint8Array;
@@ -943,22 +990,6 @@ declare class AutomationAuditStore {
943
990
  get size(): number;
944
991
  }
945
992
 
946
- /**
947
- * Machine WebSocket client — trimmed from CLI's ApiMachineClient.
948
- *
949
- * Core capabilities retained:
950
- * - Machine-scoped Socket.IO connection
951
- * - RPC handler registration (via RpcHandlerManager)
952
- * - keepAlive heartbeat
953
- * - updateMetadata / updateDaemonState with OCC backoff
954
- * - Ephemeral event handling (webhook/supervisor triggers)
955
- *
956
- * Removed (CLI-only):
957
- * - Webhook/supervisor status emit + pending queues
958
- * - Fix kill handler
959
- * - CLI-specific logging (debugLargeJson)
960
- */
961
-
962
993
  /** Strongly-typed ephemeral event from server. */
963
994
  type EphemeralEvent = {
964
995
  type: "activity";
@@ -1001,6 +1032,10 @@ type EphemeralEvent = {
1001
1032
  name: string;
1002
1033
  content: string;
1003
1034
  }>;
1035
+ agentType?: string | null;
1036
+ modelOverride?: string | null;
1037
+ profileId?: string;
1038
+ runtimeProfile?: ResolvedRuntimeProfile;
1004
1039
  };
1005
1040
  type MachineClientOptions = {
1006
1041
  readonly token: string;
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import * as z from 'zod';
3
+ import { z as z$1 } from 'zod';
3
4
  import { EventEmitter } from 'node:events';
4
5
  import { Socket } from 'socket.io-client';
5
6
 
@@ -354,6 +355,52 @@ declare const DaemonStateSchema: z.ZodObject<{
354
355
  }, z.core.$strip>;
355
356
  type DaemonState = z.infer<typeof DaemonStateSchema>;
356
357
 
358
+ declare const ResolvedRuntimeProfileSchema: z$1.ZodObject<{
359
+ schemaVersion: z$1.ZodDefault<z$1.ZodLiteral<1>>;
360
+ profileId: z$1.ZodOptional<z$1.ZodString>;
361
+ profileName: z$1.ZodOptional<z$1.ZodString>;
362
+ source: z$1.ZodEnum<{
363
+ "built-in-profile": "built-in-profile";
364
+ "account-profile": "account-profile";
365
+ "local-profile": "local-profile";
366
+ "ad-hoc": "ad-hoc";
367
+ }>;
368
+ trust: z$1.ZodEnum<{
369
+ trusted: "trusted";
370
+ untrusted: "untrusted";
371
+ }>;
372
+ isBuiltIn: z$1.ZodOptional<z$1.ZodBoolean>;
373
+ compatibility: z$1.ZodOptional<z$1.ZodObject<{
374
+ claude: z$1.ZodDefault<z$1.ZodBoolean>;
375
+ codex: z$1.ZodDefault<z$1.ZodBoolean>;
376
+ gemini: z$1.ZodDefault<z$1.ZodBoolean>;
377
+ }, z$1.core.$strip>>;
378
+ environmentVariables: z$1.ZodDefault<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>;
379
+ startupBashScript: z$1.ZodOptional<z$1.ZodString>;
380
+ customModels: z$1.ZodOptional<z$1.ZodArray<z$1.ZodObject<{
381
+ id: z$1.ZodString;
382
+ name: z$1.ZodString;
383
+ description: z$1.ZodOptional<z$1.ZodNullable<z$1.ZodString>>;
384
+ }, z$1.core.$strip>>>;
385
+ modelMappings: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>;
386
+ defaultSessionType: z$1.ZodOptional<z$1.ZodEnum<{
387
+ simple: "simple";
388
+ worktree: "worktree";
389
+ }>>;
390
+ defaultPermissionMode: z$1.ZodOptional<z$1.ZodEnum<{
391
+ default: "default";
392
+ acceptEdits: "acceptEdits";
393
+ bypassPermissions: "bypassPermissions";
394
+ plan: "plan";
395
+ auto: "auto";
396
+ "read-only": "read-only";
397
+ "safe-yolo": "safe-yolo";
398
+ yolo: "yolo";
399
+ }>>;
400
+ defaultModelMode: z$1.ZodOptional<z$1.ZodString>;
401
+ }, z$1.core.$strip>;
402
+ type ResolvedRuntimeProfile = z$1.infer<typeof ResolvedRuntimeProfileSchema>;
403
+
357
404
  type EncryptionVariant = "legacy" | "dataKey";
358
405
  type SessionEncryption = {
359
406
  readonly key: Uint8Array;
@@ -943,22 +990,6 @@ declare class AutomationAuditStore {
943
990
  get size(): number;
944
991
  }
945
992
 
946
- /**
947
- * Machine WebSocket client — trimmed from CLI's ApiMachineClient.
948
- *
949
- * Core capabilities retained:
950
- * - Machine-scoped Socket.IO connection
951
- * - RPC handler registration (via RpcHandlerManager)
952
- * - keepAlive heartbeat
953
- * - updateMetadata / updateDaemonState with OCC backoff
954
- * - Ephemeral event handling (webhook/supervisor triggers)
955
- *
956
- * Removed (CLI-only):
957
- * - Webhook/supervisor status emit + pending queues
958
- * - Fix kill handler
959
- * - CLI-specific logging (debugLargeJson)
960
- */
961
-
962
993
  /** Strongly-typed ephemeral event from server. */
963
994
  type EphemeralEvent = {
964
995
  type: "activity";
@@ -1001,6 +1032,10 @@ type EphemeralEvent = {
1001
1032
  name: string;
1002
1033
  content: string;
1003
1034
  }>;
1035
+ agentType?: string | null;
1036
+ modelOverride?: string | null;
1037
+ profileId?: string;
1038
+ runtimeProfile?: ResolvedRuntimeProfile;
1004
1039
  };
1005
1040
  type MachineClientOptions = {
1006
1041
  readonly token: string;
package/dist/index.mjs CHANGED
@@ -19,7 +19,7 @@ import { createServer } from 'http';
19
19
  import * as z from 'zod';
20
20
  import { z as z$1 } from 'zod';
21
21
 
22
- var version = "0.5.5";
22
+ var version = "0.7.0";
23
23
 
24
24
  function loadConfig() {
25
25
  const serverUrl = (process.env.HAPPY_SERVER_URL ?? "https://s.sangreal.code.xycloud.info:2443").replace(/\/+$/, "");
@@ -2241,6 +2241,8 @@ function handleTaskTrigger(data, serverUrl, _authToken, scheduler) {
2241
2241
  approvedNewDirectoryCreation: true,
2242
2242
  automationContext: { kind: "task", trigger: "task-dispatch", projectId: data.projectId },
2243
2243
  environmentVariables: {
2244
+ // Profile-resolved env applied first so task-specific overrides win.
2245
+ ...data.runtimeProfile?.environmentVariables ?? {},
2244
2246
  HAPPY_INITIAL_PROMPT_FILE: promptFile,
2245
2247
  HAPPY_TASK_ID: data.taskId,
2246
2248
  HAPPY_TASK_PRIORITY: data.priority,
@@ -4865,6 +4867,182 @@ z.discriminatedUnion("allowed", [
4865
4867
  VoiceTokenDeniedSchema
4866
4868
  ]);
4867
4869
 
4870
+ const CODEX_APP_SERVER_BACKEND = "codex-app-server";
4871
+ const CODEX_MCP_LEGACY_BACKEND = "codex-mcp-legacy";
4872
+ const CodexBackendModeSchema = z.enum([
4873
+ "auto",
4874
+ CODEX_APP_SERVER_BACKEND,
4875
+ CODEX_MCP_LEGACY_BACKEND
4876
+ ]);
4877
+ const CodexRequestedBackendSchema = CodexBackendModeSchema;
4878
+ const CodexResolvedBackendSchema = z.enum([
4879
+ CODEX_APP_SERVER_BACKEND,
4880
+ CODEX_MCP_LEGACY_BACKEND
4881
+ ]);
4882
+ const CodexConfigModeSchema = z.enum([
4883
+ "inherit",
4884
+ "managed-profile",
4885
+ "managed-overrides"
4886
+ ]);
4887
+ const CODEX_REQUESTED_BACKEND_ALIASES = {
4888
+ auto: ["", "auto"],
4889
+ [CODEX_APP_SERVER_BACKEND]: ["app-server", "appserver", CODEX_APP_SERVER_BACKEND],
4890
+ [CODEX_MCP_LEGACY_BACKEND]: [
4891
+ "legacy",
4892
+ "mcp",
4893
+ "mcp-legacy",
4894
+ CODEX_MCP_LEGACY_BACKEND
4895
+ ]
4896
+ };
4897
+ new Map(
4898
+ Object.entries(CODEX_REQUESTED_BACKEND_ALIASES).flatMap(
4899
+ ([backend, aliases]) => aliases.map((alias) => [alias, backend])
4900
+ )
4901
+ );
4902
+
4903
+ function isTemplateAwareUrl(value) {
4904
+ if (!value) return true;
4905
+ if (/^\$\{[A-Z_][A-Z0-9_]*(:-[^}]*)?\}$/.test(value)) return true;
4906
+ try {
4907
+ new URL(value);
4908
+ return true;
4909
+ } catch {
4910
+ return false;
4911
+ }
4912
+ }
4913
+ const BuiltInAIBackendProfileIdSchema = z$1.enum([
4914
+ "anthropic",
4915
+ "deepseek",
4916
+ "zai",
4917
+ "openai",
4918
+ "azure-openai",
4919
+ "minimax",
4920
+ "kimi"
4921
+ ]);
4922
+ new Set(
4923
+ BuiltInAIBackendProfileIdSchema.options
4924
+ );
4925
+ const EnvironmentVariableSchema = z$1.object({
4926
+ name: z$1.string().regex(/^[A-Z_][A-Z0-9_]*$/, "Invalid environment variable name"),
4927
+ value: z$1.string()
4928
+ });
4929
+ const ProfileCompatibilitySchema = z$1.object({
4930
+ claude: z$1.boolean().default(true),
4931
+ codex: z$1.boolean().default(true),
4932
+ gemini: z$1.boolean().default(true)
4933
+ });
4934
+ const AnthropicConfigSchema = z$1.object({
4935
+ baseUrl: z$1.string().refine(isTemplateAwareUrl, {
4936
+ message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
4937
+ }).optional(),
4938
+ authToken: z$1.string().optional(),
4939
+ model: z$1.string().optional()
4940
+ });
4941
+ const OpenAIConfigSchema = z$1.object({
4942
+ apiKey: z$1.string().optional(),
4943
+ baseUrl: z$1.string().refine(isTemplateAwareUrl, {
4944
+ message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
4945
+ }).optional(),
4946
+ model: z$1.string().optional()
4947
+ });
4948
+ const AzureOpenAIConfigSchema = z$1.object({
4949
+ apiKey: z$1.string().optional(),
4950
+ endpoint: z$1.string().refine(isTemplateAwareUrl, {
4951
+ message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
4952
+ }).optional(),
4953
+ apiVersion: z$1.string().optional(),
4954
+ deploymentName: z$1.string().optional()
4955
+ });
4956
+ const TogetherAIConfigSchema = z$1.object({
4957
+ apiKey: z$1.string().optional(),
4958
+ model: z$1.string().optional()
4959
+ });
4960
+ const CodexConfigSchema = z$1.object({
4961
+ backendMode: CodexBackendModeSchema.optional(),
4962
+ configMode: CodexConfigModeSchema.optional(),
4963
+ codexProfileName: z$1.string().optional(),
4964
+ model: z$1.string().optional(),
4965
+ reasoningEffort: z$1.string().optional(),
4966
+ reasoningSummary: z$1.string().optional(),
4967
+ verbosity: z$1.string().optional(),
4968
+ personality: z$1.string().optional(),
4969
+ serviceTier: z$1.string().optional(),
4970
+ webSearchEnabled: z$1.boolean().optional(),
4971
+ approvalPolicy: z$1.string().optional(),
4972
+ sandboxMode: z$1.string().optional()
4973
+ });
4974
+ const TmuxConfigSchema = z$1.object({
4975
+ sessionName: z$1.string().optional(),
4976
+ tmpDir: z$1.string().optional(),
4977
+ updateEnvironment: z$1.boolean().optional()
4978
+ });
4979
+ const CustomModelSchema = z$1.object({
4980
+ id: z$1.string().min(1),
4981
+ name: z$1.string().min(1).max(100),
4982
+ description: z$1.string().nullish()
4983
+ });
4984
+ const DefaultPermissionModeSchema = z$1.enum([
4985
+ "default",
4986
+ "acceptEdits",
4987
+ "auto",
4988
+ "bypassPermissions",
4989
+ "plan",
4990
+ "read-only",
4991
+ "safe-yolo",
4992
+ "yolo"
4993
+ ]);
4994
+ z$1.object({
4995
+ id: z$1.string().min(1),
4996
+ name: z$1.string().min(1).max(100),
4997
+ description: z$1.string().max(500).optional(),
4998
+ anthropicConfig: AnthropicConfigSchema.optional(),
4999
+ openaiConfig: OpenAIConfigSchema.optional(),
5000
+ azureOpenAIConfig: AzureOpenAIConfigSchema.optional(),
5001
+ togetherAIConfig: TogetherAIConfigSchema.optional(),
5002
+ codexConfig: CodexConfigSchema.optional(),
5003
+ tmuxConfig: TmuxConfigSchema.optional(),
5004
+ startupBashScript: z$1.string().optional(),
5005
+ environmentVariables: z$1.array(EnvironmentVariableSchema).default([]),
5006
+ customModels: z$1.array(CustomModelSchema).optional(),
5007
+ modelMappings: z$1.record(z$1.string(), z$1.string()).optional(),
5008
+ defaultSessionType: z$1.enum(["simple", "worktree"]).optional(),
5009
+ defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5010
+ defaultModelMode: z$1.string().optional(),
5011
+ compatibility: ProfileCompatibilitySchema.default({
5012
+ claude: true,
5013
+ codex: true,
5014
+ gemini: true
5015
+ }),
5016
+ isBuiltIn: z$1.boolean().default(false),
5017
+ createdAt: z$1.number().default(() => Date.now()),
5018
+ updatedAt: z$1.number().default(() => Date.now()),
5019
+ version: z$1.string().default("1.0.0")
5020
+ });
5021
+ const RuntimeProfileSourceSchema = z$1.enum([
5022
+ "built-in-profile",
5023
+ "account-profile",
5024
+ "local-profile",
5025
+ "ad-hoc"
5026
+ ]);
5027
+ const RuntimeProfileTrustSchema = z$1.enum(["trusted", "untrusted"]);
5028
+ const RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION = 1;
5029
+ const ResolvedRuntimeProfileSchema = z$1.object({
5030
+ schemaVersion: z$1.literal(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION).default(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION),
5031
+ profileId: z$1.string().optional(),
5032
+ profileName: z$1.string().optional(),
5033
+ source: RuntimeProfileSourceSchema,
5034
+ trust: RuntimeProfileTrustSchema,
5035
+ isBuiltIn: z$1.boolean().optional(),
5036
+ compatibility: ProfileCompatibilitySchema.optional(),
5037
+ environmentVariables: z$1.record(z$1.string(), z$1.string()).default({}),
5038
+ startupBashScript: z$1.string().optional(),
5039
+ customModels: z$1.array(CustomModelSchema).optional(),
5040
+ modelMappings: z$1.record(z$1.string(), z$1.string()).optional(),
5041
+ defaultSessionType: z$1.enum(["simple", "worktree"]).optional(),
5042
+ defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5043
+ defaultModelMode: z$1.string().optional()
5044
+ });
5045
+
4868
5046
  const TaskPrioritySchema = z.enum([
4869
5047
  "urgent",
4870
5048
  // User-initiated, needs immediate execution
@@ -4923,7 +5101,12 @@ z.object({
4923
5101
  // Encrypted by App
4924
5102
  priority: TaskPrioritySchema.default("user"),
4925
5103
  maxAttempts: z.number().int().min(1).max(10).default(3),
4926
- skillIds: z.array(z.string()).max(10).default([])
5104
+ skillIds: z.array(z.string()).max(10).default([]),
5105
+ // Profile binding (wire 0.15.0). Business key — built-in id like
5106
+ // "anthropic" or AiBackendProfile.profileKey for the account. Optional:
5107
+ // when omitted the server falls back to Project.supervisorConfig.defaultProfileId
5108
+ // via the unified runtimeProfileResolver (feature-flagged).
5109
+ profileId: z.string().optional()
4927
5110
  });
4928
5111
  z.object({
4929
5112
  type: z.literal("task-trigger"),
@@ -4941,8 +5124,16 @@ z.object({
4941
5124
  })).optional(),
4942
5125
  agentType: z.string().nullable().optional(),
4943
5126
  // "claude" | "codex" | "gemini" — null = inherit CLI default
4944
- modelOverride: z.string().nullable().optional()
5127
+ modelOverride: z.string().nullable().optional(),
4945
5128
  // e.g. "claude-sonnet-4-20250514" — null = agent default
5129
+ // Profile binding for this task. `profileId` references the AIBackendProfile
5130
+ // selected when the task was created (Task.profileId column). `runtimeProfile`
5131
+ // is the resolved snapshot (env vars, startup script, permission mode, etc.)
5132
+ // that the CLI should honor when spawning the session. Both optional for
5133
+ // backward compatibility; starting from wire 0.14.0 + server unified resolver
5134
+ // these are always populated for scheduled/webhook/manual tasks.
5135
+ profileId: z.string().optional(),
5136
+ runtimeProfile: ResolvedRuntimeProfileSchema.optional()
4946
5137
  });
4947
5138
  const TaskOutcomeSchema = z.enum([
4948
5139
  "completed",
@@ -5120,182 +5311,6 @@ z.object({
5120
5311
  exitCode: z.number()
5121
5312
  });
5122
5313
 
5123
- const CODEX_APP_SERVER_BACKEND = "codex-app-server";
5124
- const CODEX_MCP_LEGACY_BACKEND = "codex-mcp-legacy";
5125
- const CodexBackendModeSchema = z.enum([
5126
- "auto",
5127
- CODEX_APP_SERVER_BACKEND,
5128
- CODEX_MCP_LEGACY_BACKEND
5129
- ]);
5130
- const CodexRequestedBackendSchema = CodexBackendModeSchema;
5131
- const CodexResolvedBackendSchema = z.enum([
5132
- CODEX_APP_SERVER_BACKEND,
5133
- CODEX_MCP_LEGACY_BACKEND
5134
- ]);
5135
- const CodexConfigModeSchema = z.enum([
5136
- "inherit",
5137
- "managed-profile",
5138
- "managed-overrides"
5139
- ]);
5140
- const CODEX_REQUESTED_BACKEND_ALIASES = {
5141
- auto: ["", "auto"],
5142
- [CODEX_APP_SERVER_BACKEND]: ["app-server", "appserver", CODEX_APP_SERVER_BACKEND],
5143
- [CODEX_MCP_LEGACY_BACKEND]: [
5144
- "legacy",
5145
- "mcp",
5146
- "mcp-legacy",
5147
- CODEX_MCP_LEGACY_BACKEND
5148
- ]
5149
- };
5150
- new Map(
5151
- Object.entries(CODEX_REQUESTED_BACKEND_ALIASES).flatMap(
5152
- ([backend, aliases]) => aliases.map((alias) => [alias, backend])
5153
- )
5154
- );
5155
-
5156
- function isTemplateAwareUrl(value) {
5157
- if (!value) return true;
5158
- if (/^\$\{[A-Z_][A-Z0-9_]*(:-[^}]*)?\}$/.test(value)) return true;
5159
- try {
5160
- new URL(value);
5161
- return true;
5162
- } catch {
5163
- return false;
5164
- }
5165
- }
5166
- const BuiltInAIBackendProfileIdSchema = z$1.enum([
5167
- "anthropic",
5168
- "deepseek",
5169
- "zai",
5170
- "openai",
5171
- "azure-openai",
5172
- "minimax",
5173
- "kimi"
5174
- ]);
5175
- new Set(
5176
- BuiltInAIBackendProfileIdSchema.options
5177
- );
5178
- const EnvironmentVariableSchema = z$1.object({
5179
- name: z$1.string().regex(/^[A-Z_][A-Z0-9_]*$/, "Invalid environment variable name"),
5180
- value: z$1.string()
5181
- });
5182
- const ProfileCompatibilitySchema = z$1.object({
5183
- claude: z$1.boolean().default(true),
5184
- codex: z$1.boolean().default(true),
5185
- gemini: z$1.boolean().default(true)
5186
- });
5187
- const AnthropicConfigSchema = z$1.object({
5188
- baseUrl: z$1.string().refine(isTemplateAwareUrl, {
5189
- message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
5190
- }).optional(),
5191
- authToken: z$1.string().optional(),
5192
- model: z$1.string().optional()
5193
- });
5194
- const OpenAIConfigSchema = z$1.object({
5195
- apiKey: z$1.string().optional(),
5196
- baseUrl: z$1.string().refine(isTemplateAwareUrl, {
5197
- message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
5198
- }).optional(),
5199
- model: z$1.string().optional()
5200
- });
5201
- const AzureOpenAIConfigSchema = z$1.object({
5202
- apiKey: z$1.string().optional(),
5203
- endpoint: z$1.string().refine(isTemplateAwareUrl, {
5204
- message: "Must be a valid URL or ${VAR} or ${VAR:-default} template string"
5205
- }).optional(),
5206
- apiVersion: z$1.string().optional(),
5207
- deploymentName: z$1.string().optional()
5208
- });
5209
- const TogetherAIConfigSchema = z$1.object({
5210
- apiKey: z$1.string().optional(),
5211
- model: z$1.string().optional()
5212
- });
5213
- const CodexConfigSchema = z$1.object({
5214
- backendMode: CodexBackendModeSchema.optional(),
5215
- configMode: CodexConfigModeSchema.optional(),
5216
- codexProfileName: z$1.string().optional(),
5217
- model: z$1.string().optional(),
5218
- reasoningEffort: z$1.string().optional(),
5219
- reasoningSummary: z$1.string().optional(),
5220
- verbosity: z$1.string().optional(),
5221
- personality: z$1.string().optional(),
5222
- serviceTier: z$1.string().optional(),
5223
- webSearchEnabled: z$1.boolean().optional(),
5224
- approvalPolicy: z$1.string().optional(),
5225
- sandboxMode: z$1.string().optional()
5226
- });
5227
- const TmuxConfigSchema = z$1.object({
5228
- sessionName: z$1.string().optional(),
5229
- tmpDir: z$1.string().optional(),
5230
- updateEnvironment: z$1.boolean().optional()
5231
- });
5232
- const CustomModelSchema = z$1.object({
5233
- id: z$1.string().min(1),
5234
- name: z$1.string().min(1).max(100),
5235
- description: z$1.string().nullish()
5236
- });
5237
- const DefaultPermissionModeSchema = z$1.enum([
5238
- "default",
5239
- "acceptEdits",
5240
- "auto",
5241
- "bypassPermissions",
5242
- "plan",
5243
- "read-only",
5244
- "safe-yolo",
5245
- "yolo"
5246
- ]);
5247
- z$1.object({
5248
- id: z$1.string().min(1),
5249
- name: z$1.string().min(1).max(100),
5250
- description: z$1.string().max(500).optional(),
5251
- anthropicConfig: AnthropicConfigSchema.optional(),
5252
- openaiConfig: OpenAIConfigSchema.optional(),
5253
- azureOpenAIConfig: AzureOpenAIConfigSchema.optional(),
5254
- togetherAIConfig: TogetherAIConfigSchema.optional(),
5255
- codexConfig: CodexConfigSchema.optional(),
5256
- tmuxConfig: TmuxConfigSchema.optional(),
5257
- startupBashScript: z$1.string().optional(),
5258
- environmentVariables: z$1.array(EnvironmentVariableSchema).default([]),
5259
- customModels: z$1.array(CustomModelSchema).optional(),
5260
- modelMappings: z$1.record(z$1.string(), z$1.string()).optional(),
5261
- defaultSessionType: z$1.enum(["simple", "worktree"]).optional(),
5262
- defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5263
- defaultModelMode: z$1.string().optional(),
5264
- compatibility: ProfileCompatibilitySchema.default({
5265
- claude: true,
5266
- codex: true,
5267
- gemini: true
5268
- }),
5269
- isBuiltIn: z$1.boolean().default(false),
5270
- createdAt: z$1.number().default(() => Date.now()),
5271
- updatedAt: z$1.number().default(() => Date.now()),
5272
- version: z$1.string().default("1.0.0")
5273
- });
5274
- const RuntimeProfileSourceSchema = z$1.enum([
5275
- "built-in-profile",
5276
- "account-profile",
5277
- "local-profile",
5278
- "ad-hoc"
5279
- ]);
5280
- const RuntimeProfileTrustSchema = z$1.enum(["trusted", "untrusted"]);
5281
- const RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION = 1;
5282
- z$1.object({
5283
- schemaVersion: z$1.literal(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION).default(RESOLVED_RUNTIME_PROFILE_SCHEMA_VERSION),
5284
- profileId: z$1.string().optional(),
5285
- profileName: z$1.string().optional(),
5286
- source: RuntimeProfileSourceSchema,
5287
- trust: RuntimeProfileTrustSchema,
5288
- isBuiltIn: z$1.boolean().optional(),
5289
- compatibility: ProfileCompatibilitySchema.optional(),
5290
- environmentVariables: z$1.record(z$1.string(), z$1.string()).default({}),
5291
- startupBashScript: z$1.string().optional(),
5292
- customModels: z$1.array(CustomModelSchema).optional(),
5293
- modelMappings: z$1.record(z$1.string(), z$1.string()).optional(),
5294
- defaultSessionType: z$1.enum(["simple", "worktree"]).optional(),
5295
- defaultPermissionMode: DefaultPermissionModeSchema.optional(),
5296
- defaultModelMode: z$1.string().optional()
5297
- });
5298
-
5299
5314
  const sessionProgressTodoStatusSchema = z.enum([
5300
5315
  "pending",
5301
5316
  "in_progress",
@@ -5547,6 +5562,90 @@ z.object({
5547
5562
  mcpServers: z.array(CodexMcpServerSummarySchema).optional()
5548
5563
  });
5549
5564
 
5565
+ z$1.object({}).strict();
5566
+ z$1.object({
5567
+ /** Pre-formatted single-line summary (same text `/usage` prints in non-interactive mode). */
5568
+ formatted: z$1.string(),
5569
+ /** Total USD spent on this session so far. */
5570
+ totalUsd: z$1.number().nonnegative(),
5571
+ /** Optional breakdown by model. */
5572
+ byModel: z$1.record(z$1.string(), z$1.object({
5573
+ inputTokens: z$1.number().int().nonnegative(),
5574
+ outputTokens: z$1.number().int().nonnegative(),
5575
+ cacheCreationInputTokens: z$1.number().int().nonnegative().optional(),
5576
+ cacheReadInputTokens: z$1.number().int().nonnegative().optional(),
5577
+ costUsd: z$1.number().nonnegative()
5578
+ })).optional()
5579
+ }).strict();
5580
+ z$1.object({}).strict();
5581
+ z$1.object({
5582
+ /** Remote Claude Code binary version string, e.g. "2.1.119". */
5583
+ version: z$1.string(),
5584
+ /** Path to the binary if resolvable, informational only. */
5585
+ binaryPath: z$1.string().optional(),
5586
+ /** Happy-cli package version for context. */
5587
+ happyCliVersion: z$1.string().optional()
5588
+ }).strict();
5589
+ z$1.object({
5590
+ /** Agent color name or the literal "default" to reset. */
5591
+ color: z$1.string().min(1).max(64)
5592
+ }).strict();
5593
+ z$1.object({
5594
+ success: z$1.literal(true),
5595
+ color: z$1.string()
5596
+ }).strict();
5597
+ z$1.object({
5598
+ /** Path relative to cwd or absolute. CLI applies path blacklist + Read-tool permission gating. */
5599
+ path: z$1.string().min(1).max(4096),
5600
+ /** Optional max bytes to return; server-side hard cap enforces <= 1 MiB. */
5601
+ maxBytes: z$1.number().int().positive().max(1024 * 1024).optional()
5602
+ }).strict();
5603
+ z$1.object({
5604
+ /** Null when permission denied / missing / blocked by CLI path blacklist. */
5605
+ result: z$1.object({
5606
+ contents: z$1.string(),
5607
+ absPath: z$1.string(),
5608
+ truncated: z$1.boolean().optional()
5609
+ }).nullable(),
5610
+ /** Machine-readable reason when result is null. */
5611
+ deniedReason: z$1.enum(["not_found", "permission_denied", "blacklisted_path", "too_large", "error"]).optional()
5612
+ }).strict();
5613
+ z$1.object({
5614
+ /** Fully-qualified MCP tool name, e.g. `mcp__fs__read`. Must pass CLI whitelist. */
5615
+ tool: z$1.string().regex(/^mcp__[a-z0-9_-]+__[a-z0-9_.-]+$/i, "must be of form mcp__<server>__<tool>"),
5616
+ /** Tool arguments; schema-free, CLI passes through to MCP server. */
5617
+ arguments: z$1.record(z$1.string(), z$1.unknown()).optional(),
5618
+ /**
5619
+ * Client-nonce that App must have displayed to the user in a 2-step
5620
+ * confirm dialog. CLI uses this only for audit logging — the actual
5621
+ * confirmation happens on App side and is logged for security review.
5622
+ */
5623
+ clientConfirmToken: z$1.string().min(8).max(128)
5624
+ }).strict();
5625
+ z$1.object({
5626
+ success: z$1.boolean(),
5627
+ /** Tool response payload, shape defined by each MCP tool. */
5628
+ result: z$1.unknown().optional(),
5629
+ /** Error code for UI to localize; see CLI handler for enum values. */
5630
+ errorCode: z$1.enum([
5631
+ "not_whitelisted",
5632
+ "server_unavailable",
5633
+ "tool_not_found",
5634
+ "invalid_arguments",
5635
+ "permission_denied",
5636
+ /**
5637
+ * SDK 0.2.119 defines the `mcp_call` control protocol type but does
5638
+ * not expose a public runtime method on the `Query` interface. Until
5639
+ * upstream lands a `callMcpTool()` / equivalent, the CLI handler
5640
+ * returns this code so the App can surface an honest "waiting on
5641
+ * SDK" state instead of masking the gap as a server error.
5642
+ */
5643
+ "sdk_not_implemented",
5644
+ "unknown"
5645
+ ]).optional(),
5646
+ errorMessage: z$1.string().optional()
5647
+ }).strict();
5648
+
5550
5649
  function buildSummaryRefreshPrompt(requestId) {
5551
5650
  return [
5552
5651
  "Please call mcp__happy__update_session_summary to record the current session summary with: goal, currentFocus, keyDecisions (if any), openQuestions (if any), impactScope (if any). Keep it accurate and concise.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kmmao/happy-agent",
3
- "version": "0.5.5",
3
+ "version": "0.7.0",
4
4
  "description": "CLI client for controlling Happy Coder agents remotely",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",
@@ -43,7 +43,7 @@
43
43
  "release": "npx --no-install release-it"
44
44
  },
45
45
  "dependencies": {
46
- "@kmmao/happy-wire": "^0.13.0",
46
+ "@kmmao/happy-wire": "^0.16.0",
47
47
  "axios": "^1.15.2",
48
48
  "chalk": "^5.6.2",
49
49
  "commander": "^13.1.0",