@clawos-dev/clawd 0.2.667 → 0.2.670

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/cli.cjs CHANGED
@@ -6209,7 +6209,7 @@ var init_environment_schemas = __esm({
6209
6209
  });
6210
6210
 
6211
6211
  // ../protocol/src/release-schemas.ts
6212
- var DEPLOY_ENVIRONMENTS, DeployEnvironmentSchema, ImageDigestSchema, SourceCommitSchema, IntegrationEndpointSchema, PublisherTestReceiptSchema, VerificationSchema, VerifiedPersonaReleaseSchema, VerifiedBaseReleaseSchema;
6212
+ var DEPLOY_ENVIRONMENTS, DeployEnvironmentSchema, ImageDigestSchema, SourceCommitSchema, IntegrationEndpointSchema, PublisherTestReceiptSchema, VerificationSchema, DeployConfigSchema, VerifiedPersonaReleaseSchema, VerifiedBaseReleaseSchema;
6213
6213
  var init_release_schemas = __esm({
6214
6214
  "../protocol/src/release-schemas.ts"() {
6215
6215
  "use strict";
@@ -6247,6 +6247,14 @@ var init_release_schemas = __esm({
6247
6247
  verifiedAt: external_exports.string().refine((s2) => Number.isFinite(Date.parse(s2))),
6248
6248
  test: PublisherTestReceiptSchema.optional()
6249
6249
  }).strict();
6250
+ DeployConfigSchema = external_exports.object({
6251
+ engine: external_exports.string().min(1),
6252
+ model: external_exports.string(),
6253
+ effort: external_exports.string(),
6254
+ size: external_exports.string().min(1),
6255
+ cpu: external_exports.number().positive(),
6256
+ taskConcurrency: external_exports.number().int().positive()
6257
+ }).strict();
6250
6258
  VerifiedPersonaReleaseSchema = external_exports.object({
6251
6259
  version: external_exports.literal(1),
6252
6260
  environment: external_exports.literal("integration"),
@@ -6256,6 +6264,7 @@ var init_release_schemas = __esm({
6256
6264
  baseImage: ImageDigestSchema,
6257
6265
  pipelineCommit: SourceCommitSchema,
6258
6266
  repository: external_exports.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/),
6267
+ config: DeployConfigSchema.optional(),
6259
6268
  verification: VerificationSchema.refine((v3) => v3.checks.includes("persona-smoke")),
6260
6269
  test: PublisherTestSchema.optional()
6261
6270
  }).strict().refine((record4) => {
@@ -6274,6 +6283,189 @@ var init_release_schemas = __esm({
6274
6283
  }
6275
6284
  });
6276
6285
 
6286
+ // ../protocol/src/tool-family.ts
6287
+ function isCodexFamily(tool) {
6288
+ return tool === "codex" || tool === CODEX_CLOUD_TOOL_ID;
6289
+ }
6290
+ function isPiFamily(tool) {
6291
+ return tool === PI_TOOL_ID || tool === PI_CLOUD_TOOL_ID;
6292
+ }
6293
+ function isCloudTool(tool) {
6294
+ return typeof tool === "string" && tool.length > CLOUD_TOOL_SUFFIX.length && tool.endsWith(CLOUD_TOOL_SUFFIX);
6295
+ }
6296
+ function isRpcAgentTool(tool) {
6297
+ return isCodexFamily(tool) || isPiFamily(tool);
6298
+ }
6299
+ var CLOUD_TOOL_SUFFIX, CODEX_CLOUD_TOOL_ID, CODEX_SERVICE_TIERS, CLAUDE_CLOUD_TOOL_ID, PI_TOOL_ID, PI_CLOUD_TOOL_ID;
6300
+ var init_tool_family = __esm({
6301
+ "../protocol/src/tool-family.ts"() {
6302
+ "use strict";
6303
+ CLOUD_TOOL_SUFFIX = "-cloud";
6304
+ CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
6305
+ CODEX_SERVICE_TIERS = { standard: "default", fast: "priority" };
6306
+ CLAUDE_CLOUD_TOOL_ID = `claude${CLOUD_TOOL_SUFFIX}`;
6307
+ PI_TOOL_ID = "pi";
6308
+ PI_CLOUD_TOOL_ID = `${PI_TOOL_ID}${CLOUD_TOOL_SUFFIX}`;
6309
+ }
6310
+ });
6311
+
6312
+ // ../protocol/src/persona-schemas.ts
6313
+ var CodexServiceTierSchema, PERSONA_SIZES, PersonaSizeSchema, DEFAULT_PERSONA_SIZE, PERSONA_CPUS, PersonaCpuSchema, PERSONA_CPU_DEFAULTS, PersonaCloudEngineSchema, PersonaFileSchema, PersonaSkillSummarySchema, PersonaPluginSummarySchema, CodexSandboxSettingsSchema, PersonaSandboxSettingsSchema, PersonaInfoResponseSchema, PersonaCreateArgsSchema, PersonaIdArgsSchema, PersonaUpdateArgsSchema;
6314
+ var init_persona_schemas = __esm({
6315
+ "../protocol/src/persona-schemas.ts"() {
6316
+ "use strict";
6317
+ init_zod();
6318
+ init_release_schemas();
6319
+ init_tool_family();
6320
+ CodexServiceTierSchema = external_exports.enum([CODEX_SERVICE_TIERS.standard, CODEX_SERVICE_TIERS.fast]);
6321
+ PERSONA_SIZES = ["tiny", "small", "medium", "large", "xlarge"];
6322
+ PersonaSizeSchema = external_exports.enum(PERSONA_SIZES);
6323
+ DEFAULT_PERSONA_SIZE = "medium";
6324
+ PERSONA_CPUS = [0.5, 1, 2];
6325
+ PersonaCpuSchema = external_exports.number().refine((cpu) => PERSONA_CPUS.includes(cpu), "Unsupported CPU");
6326
+ PERSONA_CPU_DEFAULTS = { tiny: 0.5, small: 1, medium: 2, large: 2, xlarge: 2 };
6327
+ PersonaCloudEngineSchema = external_exports.object({
6328
+ model: external_exports.string().optional(),
6329
+ effort: external_exports.string().optional()
6330
+ }).strict();
6331
+ PersonaFileSchema = external_exports.object({
6332
+ personaId: external_exports.string(),
6333
+ label: external_exports.string(),
6334
+ model: external_exports.string().optional(),
6335
+ // 与 model 平行的第二个 session 默认值:reasoning effort。值域跟着 tool 走
6336
+ // (claude 是 '' | low | medium | high | xhigh | max,codex 是 per-model 档位),
6337
+ // 协议层保持 string,由消费侧按 capabilities 收窄。
6338
+ effort: external_exports.string().optional(),
6339
+ serviceTier: CodexServiceTierSchema.optional(),
6340
+ public: external_exports.boolean(),
6341
+ // 8-key set defined UI-side in `lib/session-icons.ts`; protocol stays plain string
6342
+ // for forward compatibility, consumer fallbacks to default when key not found.
6343
+ iconKey: external_exports.string().optional(),
6344
+ // Default engine binding for new sessions; missing values are consumed as claude.
6345
+ tool: external_exports.string().optional(),
6346
+ /** Cloud resource size is string-compatible in stored files and narrowed at update/deploy boundaries; local execution ignores it. */
6347
+ size: external_exports.string().optional(),
6348
+ cpu: external_exports.number().finite().optional(),
6349
+ /** Cloud task-Pod concurrency, validated at release. Local execution ignores it; changes apply on the next publication. */
6350
+ taskConcurrency: external_exports.number().int().positive().optional(),
6351
+ cloudEngine: PersonaCloudEngineSchema.optional(),
6352
+ createdAt: external_exports.number(),
6353
+ updatedAt: external_exports.number(),
6354
+ /** Registry-only cloud entry, with no local persona directory. Local key adds -cloud; gateway paths use cloud.personaId and the registered engine determines the cloud tool id. */
6355
+ cloud: external_exports.object({
6356
+ gatewayUrl: external_exports.string().min(1),
6357
+ personaId: external_exports.string().min(1),
6358
+ publishedByMe: external_exports.boolean().optional(),
6359
+ environment: DeployEnvironmentSchema.optional(),
6360
+ expectedImage: ImageDigestSchema.optional(),
6361
+ testReleaseId: external_exports.string().optional()
6362
+ }).strict().optional()
6363
+ }).strict();
6364
+ PersonaSkillSummarySchema = external_exports.object({
6365
+ // 目录名作为 skill name(对齐 CC:frontmatter `name:` 仅作 displayName 用)
6366
+ name: external_exports.string().min(1),
6367
+ description: external_exports.string().optional()
6368
+ });
6369
+ PersonaPluginSummarySchema = external_exports.object({
6370
+ id: external_exports.string().min(1)
6371
+ });
6372
+ CodexSandboxSettingsSchema = external_exports.object({
6373
+ // 额外可写的绝对路径或 ~ 路径(pnpm 工具链等);workspace(cwd) 和 userWorkDir 由 daemon 自动加,不用列。
6374
+ writableRoots: external_exports.array(external_exports.string()).optional(),
6375
+ // 禁读 glob(相对 workspace root 或绝对),如 ".secrets/**"、"**/*.env"。
6376
+ denyRead: external_exports.array(external_exports.string()).optional(),
6377
+ // true → 网络全放(*);false / 缺省 → 断网(fail-closed)。**「新建默认联网」不靠这里的缺省**,
6378
+ // 靠 persona 创建时就把 CODEX_SANDBOX_DEFAULT 物化进文件(见 PersonaManager.create)——
6379
+ // 缺省只在文件损坏这种异常路径上出现,那时断网是更安全的一侧。
6380
+ network: external_exports.boolean().optional()
6381
+ }).strict();
6382
+ PersonaSandboxSettingsSchema = external_exports.object({
6383
+ permissions: external_exports.object({
6384
+ defaultMode: external_exports.string().optional(),
6385
+ allow: external_exports.array(external_exports.string()).optional(),
6386
+ // deny 优先级最高;强制底座(persona 私密区)由 daemon compose 强制 union,owner 删不掉。
6387
+ deny: external_exports.array(external_exports.string()).optional()
6388
+ }).optional(),
6389
+ sandbox: external_exports.object({
6390
+ enabled: external_exports.boolean().optional(),
6391
+ autoAllowBashIfSandboxed: external_exports.boolean().optional(),
6392
+ allowUnsandboxedCommands: external_exports.boolean().optional(),
6393
+ // 命令前缀白名单(CC `sandbox.excludedCommands`):匹配的命令在沙箱外运行,
6394
+ // 用于需逃出沙箱的工具(如 'docker *' / 'aliyun *' 要访问宿主 socket / 凭证)。
6395
+ excludedCommands: external_exports.array(external_exports.string()).optional(),
6396
+ filesystem: external_exports.object({
6397
+ denyRead: external_exports.array(external_exports.string()).optional(),
6398
+ allowRead: external_exports.array(external_exports.string()).optional(),
6399
+ denyWrite: external_exports.array(external_exports.string()).optional(),
6400
+ allowWrite: external_exports.array(external_exports.string()).optional()
6401
+ }).optional(),
6402
+ network: external_exports.object({
6403
+ allowedDomains: external_exports.array(external_exports.string()).optional(),
6404
+ allowLocalBinding: external_exports.boolean().optional()
6405
+ }).optional()
6406
+ }).optional()
6407
+ });
6408
+ PersonaInfoResponseSchema = PersonaFileSchema.extend({
6409
+ // 人格 builtin 层:`<personaDir>/CLAUDE.md`(= AGENTS.md 镜像)。预装 persona 的这份是
6410
+ // daemon ship 的产品规格,每次 daemon 启动被覆盖回 bundle 版(personalityManaged 标记它)。
6411
+ personality: external_exports.string().optional(),
6412
+ // Owner-managed personality overlay survives daemon resource refreshes and is injected for owner and guest sessions. Empty text clears it.
6413
+ personalityLocal: external_exports.string().optional(),
6414
+ // Managed builtin personality is refreshed by daemon and shown read-only; user edits belong to the overlay. User-created persona files remain editable.
6415
+ personalityManaged: external_exports.boolean().optional(),
6416
+ // 仅 'persona:get' 携带;null = 文件不存在 / parse 失败;undefined = 不在该响应类型上
6417
+ skills: external_exports.array(PersonaSkillSummarySchema).optional(),
6418
+ // 仅 'persona:get' 携带;空数组 = 没启用任何插件 / settings.json 不存在;undefined = 不在该响应类型上
6419
+ plugins: external_exports.array(PersonaPluginSummarySchema).optional(),
6420
+ sandboxSettings: PersonaSandboxSettingsSchema.nullable().optional(),
6421
+ // 仅 'persona:get' 携带;null = codex-sandbox.json 不存在 / parse 失败。
6422
+ // UI 的「访客联网」开关读它(见 PersonaCardSettings)。
6423
+ codexSandbox: CodexSandboxSettingsSchema.nullable().optional()
6424
+ });
6425
+ PersonaCreateArgsSchema = external_exports.object({
6426
+ // Caller provides a safe slug, daemon adds persona-. Keep identity separate from the freely editable display label.
6427
+ slug: external_exports.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/).max(32),
6428
+ label: external_exports.string().min(1),
6429
+ personality: external_exports.string(),
6430
+ model: external_exports.string().optional(),
6431
+ effort: external_exports.string().optional(),
6432
+ serviceTier: PersonaFileSchema.shape.serviceTier,
6433
+ tool: external_exports.string().optional(),
6434
+ public: external_exports.boolean().optional(),
6435
+ iconKey: external_exports.string().optional()
6436
+ }).strict();
6437
+ PersonaIdArgsSchema = external_exports.object({
6438
+ personaId: external_exports.string().min(1)
6439
+ });
6440
+ PersonaUpdateArgsSchema = external_exports.object({
6441
+ personaId: external_exports.string().min(1),
6442
+ patch: external_exports.object({
6443
+ label: external_exports.string().min(1).optional(),
6444
+ model: external_exports.string().optional(),
6445
+ effort: external_exports.string().optional(),
6446
+ serviceTier: PersonaFileSchema.shape.serviceTier,
6447
+ tool: external_exports.string().optional(),
6448
+ size: PersonaSizeSchema.optional(),
6449
+ cpu: PersonaCpuSchema.optional(),
6450
+ taskConcurrency: PersonaFileSchema.shape.taskConcurrency,
6451
+ // 整体替换(上云页每次交完整的 { model, effort });切 tool 不清它
6452
+ cloudEngine: PersonaCloudEngineSchema.optional(),
6453
+ personality: external_exports.string().optional(),
6454
+ // 人格补充层正文(见 PersonaInfoResponseSchema.personalityLocal)。与 personality 各写
6455
+ // 各层、互不影响;'' = 清空补充层。预装 persona 的编辑入口写的就是这个字段。
6456
+ personalityLocal: external_exports.string().optional(),
6457
+ public: external_exports.boolean().optional(),
6458
+ // pass `null` to clear; daemon strips the field from PersonaFile on null
6459
+ iconKey: external_exports.string().nullable().optional(),
6460
+ // codex guest 沙箱(落盘 .clawd/codex-sandbox.json,不在 PersonaFile 里)。与 personality
6461
+ // 同款分层写:handler 剥出来单独落盘,且**局部合并**——只带 network 的 patch 不能抹掉
6462
+ // owner 手配的 writableRoots / denyRead。
6463
+ codexSandbox: CodexSandboxSettingsSchema.optional()
6464
+ }).strict()
6465
+ }).strict();
6466
+ }
6467
+ });
6468
+
6277
6469
  // ../protocol/src/deploy-schemas.ts
6278
6470
  function latestProductionRelease(releases) {
6279
6471
  return [...releases].reverse().find((r2) => (r2.environment ?? "production") === "production");
@@ -6314,10 +6506,20 @@ function cloudEngineSettings(p3) {
6314
6506
  const src = tool === "claude" ? p3.cloudEngine ?? {} : p3;
6315
6507
  return { tool, model: src.model ?? "", effort: src.effort ?? "" };
6316
6508
  }
6509
+ function configOfPersona(p3) {
6510
+ const { tool, model, effort } = cloudEngineSettings(p3);
6511
+ const size = (p3.size ?? "").trim() || DEFAULT_PERSONA_SIZE;
6512
+ const cpu = p3.cpu ?? PERSONA_CPU_DEFAULTS[size] ?? PERSONA_CPU_DEFAULTS[DEFAULT_PERSONA_SIZE];
6513
+ return { engine: tool, model: model.trim(), effort: effort.trim(), size, cpu, taskConcurrency: p3.taskConcurrency ?? DEFAULT_TASK_CONCURRENCY };
6514
+ }
6515
+ function changedConfigKeys(a2, b3) {
6516
+ if (!a2 || !b3) return null;
6517
+ return CONFIG_KEYS.filter((key) => a2[key] !== b3[key]);
6518
+ }
6317
6519
  function repoSlug(url) {
6318
6520
  return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
6319
6521
  }
6320
- var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DEPLOY_PROMOTION_PHASES, DeployPromotionSchema, DeployEngineChannelSchema, DeployEngineChannelsSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, ENGINE_AUTH_SECRETS, ENGINE_AUTH_PARTIAL_BLOCKER, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_VERIFY_STAGE, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployLocalStateSchema, DeployPatchResultSchema, DeploySecretsResultSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployConfirmTestArgsSchema, DeployPromoteArgsSchema, DeployOpenTestArgsSchema, DeployEnvironmentArgsSchema, DeployConfirmTestResultSchema, DeployPromoteResultSchema, DeployOpenTestResultSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeploySetEngineAuthArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6522
+ var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DEPLOY_PROMOTION_PHASES, DeployPromotionSchema, DeployEngineChannelSchema, DeployEngineChannelsSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, ENGINE_AUTH_SECRETS, ENGINE_AUTH_PARTIAL_BLOCKER, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_VERIFY_STAGE, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployLocalStateSchema, DeployPatchResultSchema, DeploySecretsResultSchema, DEPLOY_WORKFLOW_VERSION, DEFAULT_TASK_CONCURRENCY, CONFIG_KEYS, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployConfirmTestArgsSchema, DeployPromoteArgsSchema, DeployApplyConfigArgsSchema, DeployOpenTestArgsSchema, DeployEnvironmentArgsSchema, DeployConfirmTestResultSchema, DeployPromoteResultSchema, DeployOpenTestResultSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeploySetEngineAuthArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6321
6523
  var init_deploy_schemas = __esm({
6322
6524
  "../protocol/src/deploy-schemas.ts"() {
6323
6525
  "use strict";
@@ -6325,6 +6527,7 @@ var init_deploy_schemas = __esm({
6325
6527
  init_github_auth_schemas();
6326
6528
  init_environment_schemas();
6327
6529
  init_release_schemas();
6530
+ init_persona_schemas();
6328
6531
  DeployFileSchema = external_exports.object({
6329
6532
  version: external_exports.literal(1),
6330
6533
  manifest: EnvironmentManifestSchema
@@ -6365,6 +6568,12 @@ var init_deploy_schemas = __esm({
6365
6568
  runAttempt: external_exports.number().int().positive().optional(),
6366
6569
  artifactId: external_exports.number().int().positive().optional(),
6367
6570
  verified: VerifiedPersonaReleaseSchema.optional(),
6571
+ /** 这一轮测试用的配置(design 2026-10-14-deploy-config-decouple §3.1);老记录没有,按「发布时 commit 里的 persona.json」算 */
6572
+ config: DeployConfigSchema.optional(),
6573
+ /** 「应用配置」产生的记录:复用的是哪条测试发布的镜像(它的 id)。首次构建的记录没有 */
6574
+ reconfigOf: external_exports.string().min(1).optional(),
6575
+ /** 发布时写进仓库的 workflow 模板版本;缺 = 老模板(不认 config_json 输入,要先完整重发一次) */
6576
+ workflowVersion: external_exports.number().int().positive().optional(),
6368
6577
  confirmation: external_exports.object({
6369
6578
  fingerprint: external_exports.string().regex(/^[a-f0-9]{64}$/),
6370
6579
  runId: external_exports.number().int().positive(),
@@ -6516,6 +6725,9 @@ var init_deploy_schemas = __esm({
6516
6725
  secrets: external_exports.array(DeploySecretPresenceSchema).optional(),
6517
6726
  engineChannels: DeployEngineChannelsSchema.optional()
6518
6727
  }).strict();
6728
+ DEPLOY_WORKFLOW_VERSION = 2;
6729
+ DEFAULT_TASK_CONCURRENCY = 4;
6730
+ CONFIG_KEYS = ["engine", "model", "effort", "size", "cpu", "taskConcurrency"];
6519
6731
  DeployReleaseResultSchema = external_exports.object({
6520
6732
  sha: external_exports.string().min(1),
6521
6733
  actionsUrl: external_exports.string().min(1),
@@ -6537,6 +6749,7 @@ var init_deploy_schemas = __esm({
6537
6749
  }).strict();
6538
6750
  DeployConfirmTestArgsSchema = DeployPersonaArgsSchema.extend({ releaseId: external_exports.string().min(1) }).strict();
6539
6751
  DeployPromoteArgsSchema = DeployConfirmTestArgsSchema;
6752
+ DeployApplyConfigArgsSchema = DeployConfirmTestArgsSchema;
6540
6753
  DeployOpenTestArgsSchema = DeployConfirmTestArgsSchema;
6541
6754
  DeployEnvironmentArgsSchema = DeployPersonaArgsSchema.extend({ environment: DeployEnvironmentSchema.default("production") }).strict();
6542
6755
  DeployConfirmTestResultSchema = external_exports.object({ confirmation: DeployReleasedSchema.shape.confirmation.unwrap() }).strict();
@@ -7773,187 +7986,6 @@ var init_dashboard_schemas = __esm({
7773
7986
  }
7774
7987
  });
7775
7988
 
7776
- // ../protocol/src/tool-family.ts
7777
- function isCodexFamily(tool) {
7778
- return tool === "codex" || tool === CODEX_CLOUD_TOOL_ID;
7779
- }
7780
- function isPiFamily(tool) {
7781
- return tool === PI_TOOL_ID || tool === PI_CLOUD_TOOL_ID;
7782
- }
7783
- function isCloudTool(tool) {
7784
- return typeof tool === "string" && tool.length > CLOUD_TOOL_SUFFIX.length && tool.endsWith(CLOUD_TOOL_SUFFIX);
7785
- }
7786
- function isRpcAgentTool(tool) {
7787
- return isCodexFamily(tool) || isPiFamily(tool);
7788
- }
7789
- var CLOUD_TOOL_SUFFIX, CODEX_CLOUD_TOOL_ID, CODEX_SERVICE_TIERS, CLAUDE_CLOUD_TOOL_ID, PI_TOOL_ID, PI_CLOUD_TOOL_ID;
7790
- var init_tool_family = __esm({
7791
- "../protocol/src/tool-family.ts"() {
7792
- "use strict";
7793
- CLOUD_TOOL_SUFFIX = "-cloud";
7794
- CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
7795
- CODEX_SERVICE_TIERS = { standard: "default", fast: "priority" };
7796
- CLAUDE_CLOUD_TOOL_ID = `claude${CLOUD_TOOL_SUFFIX}`;
7797
- PI_TOOL_ID = "pi";
7798
- PI_CLOUD_TOOL_ID = `${PI_TOOL_ID}${CLOUD_TOOL_SUFFIX}`;
7799
- }
7800
- });
7801
-
7802
- // ../protocol/src/persona-schemas.ts
7803
- var CodexServiceTierSchema, PERSONA_SIZES, PersonaSizeSchema, PERSONA_CPUS, PersonaCpuSchema, PersonaCloudEngineSchema, PersonaFileSchema, PersonaSkillSummarySchema, PersonaPluginSummarySchema, CodexSandboxSettingsSchema, PersonaSandboxSettingsSchema, PersonaInfoResponseSchema, PersonaCreateArgsSchema, PersonaIdArgsSchema, PersonaUpdateArgsSchema;
7804
- var init_persona_schemas = __esm({
7805
- "../protocol/src/persona-schemas.ts"() {
7806
- "use strict";
7807
- init_zod();
7808
- init_release_schemas();
7809
- init_tool_family();
7810
- CodexServiceTierSchema = external_exports.enum([CODEX_SERVICE_TIERS.standard, CODEX_SERVICE_TIERS.fast]);
7811
- PERSONA_SIZES = ["tiny", "small", "medium", "large", "xlarge"];
7812
- PersonaSizeSchema = external_exports.enum(PERSONA_SIZES);
7813
- PERSONA_CPUS = [0.5, 1, 2];
7814
- PersonaCpuSchema = external_exports.number().refine((cpu) => PERSONA_CPUS.includes(cpu), "Unsupported CPU");
7815
- PersonaCloudEngineSchema = external_exports.object({
7816
- model: external_exports.string().optional(),
7817
- effort: external_exports.string().optional()
7818
- }).strict();
7819
- PersonaFileSchema = external_exports.object({
7820
- personaId: external_exports.string(),
7821
- label: external_exports.string(),
7822
- model: external_exports.string().optional(),
7823
- // 与 model 平行的第二个 session 默认值:reasoning effort。值域跟着 tool 走
7824
- // (claude 是 '' | low | medium | high | xhigh | max,codex 是 per-model 档位),
7825
- // 协议层保持 string,由消费侧按 capabilities 收窄。
7826
- effort: external_exports.string().optional(),
7827
- serviceTier: CodexServiceTierSchema.optional(),
7828
- public: external_exports.boolean(),
7829
- // 8-key set defined UI-side in `lib/session-icons.ts`; protocol stays plain string
7830
- // for forward compatibility, consumer fallbacks to default when key not found.
7831
- iconKey: external_exports.string().optional(),
7832
- // Default engine binding for new sessions; missing values are consumed as claude.
7833
- tool: external_exports.string().optional(),
7834
- /** Cloud resource size is string-compatible in stored files and narrowed at update/deploy boundaries; local execution ignores it. */
7835
- size: external_exports.string().optional(),
7836
- cpu: external_exports.number().finite().optional(),
7837
- /** Cloud task-Pod concurrency, validated at release. Local execution ignores it; changes apply on the next publication. */
7838
- taskConcurrency: external_exports.number().int().positive().optional(),
7839
- cloudEngine: PersonaCloudEngineSchema.optional(),
7840
- createdAt: external_exports.number(),
7841
- updatedAt: external_exports.number(),
7842
- /** Registry-only cloud entry, with no local persona directory. Local key adds -cloud; gateway paths use cloud.personaId and the registered engine determines the cloud tool id. */
7843
- cloud: external_exports.object({
7844
- gatewayUrl: external_exports.string().min(1),
7845
- personaId: external_exports.string().min(1),
7846
- publishedByMe: external_exports.boolean().optional(),
7847
- environment: DeployEnvironmentSchema.optional(),
7848
- expectedImage: ImageDigestSchema.optional(),
7849
- testReleaseId: external_exports.string().optional()
7850
- }).strict().optional()
7851
- }).strict();
7852
- PersonaSkillSummarySchema = external_exports.object({
7853
- // 目录名作为 skill name(对齐 CC:frontmatter `name:` 仅作 displayName 用)
7854
- name: external_exports.string().min(1),
7855
- description: external_exports.string().optional()
7856
- });
7857
- PersonaPluginSummarySchema = external_exports.object({
7858
- id: external_exports.string().min(1)
7859
- });
7860
- CodexSandboxSettingsSchema = external_exports.object({
7861
- // 额外可写的绝对路径或 ~ 路径(pnpm 工具链等);workspace(cwd) 和 userWorkDir 由 daemon 自动加,不用列。
7862
- writableRoots: external_exports.array(external_exports.string()).optional(),
7863
- // 禁读 glob(相对 workspace root 或绝对),如 ".secrets/**"、"**/*.env"。
7864
- denyRead: external_exports.array(external_exports.string()).optional(),
7865
- // true → 网络全放(*);false / 缺省 → 断网(fail-closed)。**「新建默认联网」不靠这里的缺省**,
7866
- // 靠 persona 创建时就把 CODEX_SANDBOX_DEFAULT 物化进文件(见 PersonaManager.create)——
7867
- // 缺省只在文件损坏这种异常路径上出现,那时断网是更安全的一侧。
7868
- network: external_exports.boolean().optional()
7869
- }).strict();
7870
- PersonaSandboxSettingsSchema = external_exports.object({
7871
- permissions: external_exports.object({
7872
- defaultMode: external_exports.string().optional(),
7873
- allow: external_exports.array(external_exports.string()).optional(),
7874
- // deny 优先级最高;强制底座(persona 私密区)由 daemon compose 强制 union,owner 删不掉。
7875
- deny: external_exports.array(external_exports.string()).optional()
7876
- }).optional(),
7877
- sandbox: external_exports.object({
7878
- enabled: external_exports.boolean().optional(),
7879
- autoAllowBashIfSandboxed: external_exports.boolean().optional(),
7880
- allowUnsandboxedCommands: external_exports.boolean().optional(),
7881
- // 命令前缀白名单(CC `sandbox.excludedCommands`):匹配的命令在沙箱外运行,
7882
- // 用于需逃出沙箱的工具(如 'docker *' / 'aliyun *' 要访问宿主 socket / 凭证)。
7883
- excludedCommands: external_exports.array(external_exports.string()).optional(),
7884
- filesystem: external_exports.object({
7885
- denyRead: external_exports.array(external_exports.string()).optional(),
7886
- allowRead: external_exports.array(external_exports.string()).optional(),
7887
- denyWrite: external_exports.array(external_exports.string()).optional(),
7888
- allowWrite: external_exports.array(external_exports.string()).optional()
7889
- }).optional(),
7890
- network: external_exports.object({
7891
- allowedDomains: external_exports.array(external_exports.string()).optional(),
7892
- allowLocalBinding: external_exports.boolean().optional()
7893
- }).optional()
7894
- }).optional()
7895
- });
7896
- PersonaInfoResponseSchema = PersonaFileSchema.extend({
7897
- // 人格 builtin 层:`<personaDir>/CLAUDE.md`(= AGENTS.md 镜像)。预装 persona 的这份是
7898
- // daemon ship 的产品规格,每次 daemon 启动被覆盖回 bundle 版(personalityManaged 标记它)。
7899
- personality: external_exports.string().optional(),
7900
- // Owner-managed personality overlay survives daemon resource refreshes and is injected for owner and guest sessions. Empty text clears it.
7901
- personalityLocal: external_exports.string().optional(),
7902
- // Managed builtin personality is refreshed by daemon and shown read-only; user edits belong to the overlay. User-created persona files remain editable.
7903
- personalityManaged: external_exports.boolean().optional(),
7904
- // 仅 'persona:get' 携带;null = 文件不存在 / parse 失败;undefined = 不在该响应类型上
7905
- skills: external_exports.array(PersonaSkillSummarySchema).optional(),
7906
- // 仅 'persona:get' 携带;空数组 = 没启用任何插件 / settings.json 不存在;undefined = 不在该响应类型上
7907
- plugins: external_exports.array(PersonaPluginSummarySchema).optional(),
7908
- sandboxSettings: PersonaSandboxSettingsSchema.nullable().optional(),
7909
- // 仅 'persona:get' 携带;null = codex-sandbox.json 不存在 / parse 失败。
7910
- // UI 的「访客联网」开关读它(见 PersonaCardSettings)。
7911
- codexSandbox: CodexSandboxSettingsSchema.nullable().optional()
7912
- });
7913
- PersonaCreateArgsSchema = external_exports.object({
7914
- // Caller provides a safe slug, daemon adds persona-. Keep identity separate from the freely editable display label.
7915
- slug: external_exports.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/).max(32),
7916
- label: external_exports.string().min(1),
7917
- personality: external_exports.string(),
7918
- model: external_exports.string().optional(),
7919
- effort: external_exports.string().optional(),
7920
- serviceTier: PersonaFileSchema.shape.serviceTier,
7921
- tool: external_exports.string().optional(),
7922
- public: external_exports.boolean().optional(),
7923
- iconKey: external_exports.string().optional()
7924
- }).strict();
7925
- PersonaIdArgsSchema = external_exports.object({
7926
- personaId: external_exports.string().min(1)
7927
- });
7928
- PersonaUpdateArgsSchema = external_exports.object({
7929
- personaId: external_exports.string().min(1),
7930
- patch: external_exports.object({
7931
- label: external_exports.string().min(1).optional(),
7932
- model: external_exports.string().optional(),
7933
- effort: external_exports.string().optional(),
7934
- serviceTier: PersonaFileSchema.shape.serviceTier,
7935
- tool: external_exports.string().optional(),
7936
- size: PersonaSizeSchema.optional(),
7937
- cpu: PersonaCpuSchema.optional(),
7938
- taskConcurrency: PersonaFileSchema.shape.taskConcurrency,
7939
- // 整体替换(上云页每次交完整的 { model, effort });切 tool 不清它
7940
- cloudEngine: PersonaCloudEngineSchema.optional(),
7941
- personality: external_exports.string().optional(),
7942
- // 人格补充层正文(见 PersonaInfoResponseSchema.personalityLocal)。与 personality 各写
7943
- // 各层、互不影响;'' = 清空补充层。预装 persona 的编辑入口写的就是这个字段。
7944
- personalityLocal: external_exports.string().optional(),
7945
- public: external_exports.boolean().optional(),
7946
- // pass `null` to clear; daemon strips the field from PersonaFile on null
7947
- iconKey: external_exports.string().nullable().optional(),
7948
- // codex guest 沙箱(落盘 .clawd/codex-sandbox.json,不在 PersonaFile 里)。与 personality
7949
- // 同款分层写:handler 剥出来单独落盘,且**局部合并**——只带 network 的 patch 不能抹掉
7950
- // owner 手配的 writableRoots / denyRead。
7951
- codexSandbox: CodexSandboxSettingsSchema.optional()
7952
- }).strict()
7953
- }).strict();
7954
- }
7955
- });
7956
-
7957
7989
  // ../protocol/src/events.ts
7958
7990
  var DAEMON_MODE_VALUES, CLOUD_HISTORY_REFRESH_KIND, SESSION_STATUS_VALUES, HISTORY_USER_META_VALUES, SKILL_SOURCE_VALUES, AGENT_SOURCE_VALUES, ASK_USER_QUESTION_TOOL_NAME;
7959
7991
  var init_events = __esm({
@@ -9112,6 +9144,10 @@ var init_methods = __esm({
9112
9144
  summary: "\u786E\u8BA4\u6240\u9009\u6D4B\u8BD5\u7248\u901A\u8FC7\u5B9E\u9645\u8BD5\u7528\uFF1B\u6309\u5DF2\u4FDD\u5B58\u5236\u54C1\u548C\u6D4B\u8BD5\u767B\u8BB0\u6821\u9A8C\uFF0Cowner-only\u3001human-only",
9113
9145
  args: DeployConfirmTestArgsSchema
9114
9146
  },
9147
+ "deploy:applyConfig": {
9148
+ summary: "\u628A releaseId \u90A3\u6761\u6D4B\u8BD5\u53D1\u5E03\u7684**\u955C\u50CF**\u6362\u4E0A persona \u5F53\u524D\u7684\u5F15\u64CE / \u6A21\u578B / \u8D44\u6E90\u914D\u7F6E\uFF0C\u5728\u6D4B\u8BD5\u73AF\u5883\u91CD\u6D4B\u4E00\u904D\uFF1A\u4E0D\u626B\u63CF\u3001\u4E0D\u6784\u5EFA\uFF0C\u6D41\u6C34\u7EBF\u8D70 smoke \u2192 \u767B\u8BB0 \u2192 \u9A8C\u8BC1\u3002\u524D\u7F6E\uFF1A\u90A3\u6761\u53D1\u5E03\u5DF2\u63A8\u9001\u5E76\u901A\u8FC7\u9A8C\u8BC1\uFF0C\u4E14\u4ED3\u5E93\u91CC\u7684 workflow \u6A21\u677F\u591F\u65B0\uFF08\u7F3A config_json \u8F93\u5165\u7684\u8981\u5148\u300C\u53D1\u5E03\u5230\u6D4B\u8BD5\u300D\u5B8C\u6574\u91CD\u53D1\u4E00\u6B21\uFF09\uFF1B\u8FC7\u4E0E\u53D1\u5E03\u540C\u4E00\u5957\u95F8\uFF08\u9009\u4E86\u6A21\u578B\u3001\u8BA2\u9605\u51ED\u636E\u6210\u5BF9\u7B49\uFF09\u3002\u56DE\u65B0\u6D4B\u8BD5\u53D1\u5E03\u7684 commit \u4E0E Actions \u5730\u5740\uFF1B\u4E4B\u540E\u8D70\u5E38\u89C4\u7684\u6D4B\u8BD5\u786E\u8BA4 / \u664B\u7EA7\uFF1Bowner-only\u3001human-only",
9149
+ args: DeployApplyConfigArgsSchema
9150
+ },
9115
9151
  "deploy:promote": {
9116
9152
  summary: "\u5C06\u5DF2\u786E\u8BA4\u6D4B\u8BD5\u7248\u7684\u63D0\u4EA4\u63A8\u8FDB main \u5E76\u4E0A\u7EBF\u76F8\u540C\u955C\u50CF\uFF1B\u6062\u590D\u4E2D\u65AD\u7684\u540C\u4E00\u6B21\u664B\u7EA7\uFF0Cowner-only\u3001human-only",
9117
9153
  args: DeployPromoteArgsSchema
@@ -45606,10 +45642,14 @@ var import_node_crypto = require("crypto");
45606
45642
 
45607
45643
  // src/tools/codex-app-server-client.ts
45608
45644
  var import_node_readline = require("readline");
45609
- function stdioLineStream(proc) {
45645
+ function stdioLineStream(proc, opts = {}) {
45610
45646
  const rl = (0, import_node_readline.createInterface)({ input: proc.stdout, crlfDelay: Infinity });
45611
45647
  const lineCbs = [];
45612
45648
  const closeCbs = [];
45649
+ proc.stdin?.on(
45650
+ "error",
45651
+ (err) => opts.logger?.warn("codex stdin error (write dropped)", { code: err.code, message: err.message })
45652
+ );
45613
45653
  rl.on("line", (l2) => {
45614
45654
  for (const cb of lineCbs) cb(l2);
45615
45655
  });
@@ -54765,7 +54805,7 @@ function createCodexSession(ctx, sink, deps = {}) {
54765
54805
  ctx,
54766
54806
  sink,
54767
54807
  logger: deps.logger,
54768
- stream: stdioLineStream(proc),
54808
+ stream: stdioLineStream(proc, { logger: deps.logger }),
54769
54809
  threadParams: threadStartParams(ctx),
54770
54810
  // persona 惯例装在 <cwd>/.claude/skills/,经 extraRoots 注入 discovery
54771
54811
  // (skills:list RPC 侧 codex-skills.ts 同款注入,两处保持一致)。
@@ -62157,6 +62197,7 @@ function renderDeployWorkflow(config) {
62157
62197
  " - name: Select pinned source identities",
62158
62198
  " env:",
62159
62199
  " RELEASE_JSON: ${{ inputs.release_json }}",
62200
+ " CONFIG_JSON: ${{ inputs.config_json }}",
62160
62201
  " run: |",
62161
62202
  " node --input-type=module <<'NODE'",
62162
62203
  ' import fs from "node:fs"',
@@ -62164,7 +62205,9 @@ function renderDeployWorkflow(config) {
62164
62205
  ' const r = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, "utf8")) : process.env.RELEASE_JSON ? JSON.parse(process.env.RELEASE_JSON) : undefined',
62165
62206
  ' if (r && (!/^[a-f0-9]{40}$/.test(r.commit) || !/^[a-f0-9]{40}$/.test(r.pipelineCommit) || r.repository !== process.env.GITHUB_REPOSITORY)) throw new Error("invalid Persona release identity")',
62166
62207
  ' if (process.env.DEPLOY_ENVIRONMENT === "production" && (!r || process.env.GITHUB_REF_NAME !== "main" || process.env.GITHUB_SHA !== r.commit)) throw new Error("production main must equal tested commit")',
62167
- ' fs.appendFileSync(process.env.GITHUB_ENV, "SOURCE_COMMIT=" + (r?.commit ?? process.env.GITHUB_SHA) + "\\nPIPELINE_COMMIT=" + (r?.pipelineCommit ?? process.env.CLAWOS_REF) + "\\n")',
62208
+ " // \u5E94\u7528\u914D\u7F6E\uFF08integration + config_json + release_json\uFF0C\u4E14\u8FD8\u6CA1\u6709\u5019\u9009\u6587\u4EF6\uFF09\u8981\u6362\u4E0A\u5F53\u524D\u6D41\u6C34\u7EBF\uFF1A\u8BB0\u5F55\u91CC\u9489\u7684\u65E7\u811A\u672C\u4E0D\u8BA4\u8BC6 config_json",
62209
+ ' const reconfigure = process.env.DEPLOY_ENVIRONMENT === "integration" && process.env.CONFIG_JSON && !fs.existsSync(file)',
62210
+ ' fs.appendFileSync(process.env.GITHUB_ENV, "SOURCE_COMMIT=" + (r?.commit ?? process.env.GITHUB_SHA) + "\\nPIPELINE_COMMIT=" + (reconfigure ? process.env.CLAWOS_REF : r?.pipelineCommit ?? process.env.CLAWOS_REF) + "\\n")',
62168
62211
  " NODE",
62169
62212
  ""
62170
62213
  ];
@@ -62191,6 +62234,8 @@ function renderDeployWorkflow(config) {
62191
62234
  "",
62192
62235
  " - name: \u8BFB\u90E8\u7F72\u6587\u4EF6",
62193
62236
  " id: plan",
62237
+ " env:",
62238
+ " CLAWD_CONFIG_JSON: ${{ inputs.config_json }}",
62194
62239
  " run: |",
62195
62240
  " node clawos/cloud-host/pipeline/deploy-file.mjs \\",
62196
62241
  " --persona-dir persona \\",
@@ -62255,6 +62300,7 @@ function renderDeployWorkflow(config) {
62255
62300
  " --effort '${{ steps.plan.outputs.effort }}' \\",
62256
62301
  " --size '${{ steps.plan.outputs.size }}' \\",
62257
62302
  " --cpu '${{ steps.plan.outputs.cpu }}' \\",
62303
+ " --config-source '${{ steps.plan.outputs.config_source }}' \\",
62258
62304
  " --task-concurrency '${{ steps.plan.outputs.task_concurrency }}' \\",
62259
62305
  " --engine '${{ steps.plan.outputs.engine }}' \\",
62260
62306
  " --connectors '${{ steps.plan.outputs.connectors }}' \\",
@@ -62270,7 +62316,7 @@ function renderDeployWorkflow(config) {
62270
62316
  ];
62271
62317
  return [
62272
62318
  "name: clawd deploy",
62273
- "run-name: ${{ inputs.environment == 'production' && format('persona-production-{0}', inputs.promotion_id) || 'persona-integration' }}",
62319
+ "run-name: ${{ inputs.environment == 'production' && format('persona-production-{0}', inputs.promotion_id) || inputs.config_id && format('persona-integration-config-{0}', inputs.config_id) || 'persona-integration' }}",
62274
62320
  "# \u7531 clawd \u751F\u6210\u3001\u7531 clawd \u7EF4\u62A4\u3002push \u6784\u5EFA int \u5019\u9009\uFF1Bproduction \u624B\u52A8\u664B\u7EA7\u5DF2\u9A8C\u6536\u5236\u54C1\u3002",
62275
62321
  "on:",
62276
62322
  " push:",
@@ -62285,7 +62331,15 @@ function renderDeployWorkflow(config) {
62285
62331
  " type: string",
62286
62332
  " required: false",
62287
62333
  " release_json:",
62288
- " description: Verified Persona release JSON for production promotion",
62334
+ " description: Verified Persona release JSON for production promotion or config re-test",
62335
+ " type: string",
62336
+ " required: false",
62337
+ " config_json:",
62338
+ " description: Engine/model/resource config (canonical JSON); replaces .clawd/persona.json for this run",
62339
+ " type: string",
62340
+ " required: false",
62341
+ " config_id:",
62342
+ " description: Identifies an integration config re-test run (run-name)",
62289
62343
  " type: string",
62290
62344
  " required: false",
62291
62345
  "",
@@ -62338,6 +62392,7 @@ function renderDeployWorkflow(config) {
62338
62392
  " ALIBABA_CLOUD_ACCESS_KEY_ID: ${{ secrets.ALI_ACS_ACCESS_KEY_ID }}",
62339
62393
  " ALIBABA_CLOUD_ACCESS_KEY_SECRET: ${{ secrets.ALI_ACS_ACCESS_KEY_SECRET }}",
62340
62394
  " RELEASE_JSON: ${{ inputs.release_json }}",
62395
+ " CLAWD_CONFIG_JSON: ${{ steps.plan.outputs.config_json }}",
62341
62396
  " CLAWD_PUBLISHER_TEST: ${{ steps.plan.outputs.test }}",
62342
62397
  " run: node clawos/cloud-host/pipeline/persona-candidate.mjs",
62343
62398
  "",
@@ -62799,6 +62854,10 @@ async function fileInCommit(exec, dir, sha, rel) {
62799
62854
  const r2 = await git(exec, dir, ["cat-file", "-e", `${sha}:${rel}`]);
62800
62855
  return !r2.spawnError && r2.code === 0;
62801
62856
  }
62857
+ async function readFileAtCommit(exec, dir, sha, rel) {
62858
+ const r2 = await git(exec, dir, ["show", `${sha}:${rel}`]);
62859
+ return !r2.spawnError && r2.code === 0 ? r2.stdout : void 0;
62860
+ }
62802
62861
  async function commitAll(exec, args) {
62803
62862
  await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
62804
62863
  const staged = await git(exec, args.dir, ["diff", "--cached", "--quiet"]);
@@ -62849,7 +62908,9 @@ function validatePersonaRelease(raw, expected) {
62849
62908
  baseImage: record4.baseImage,
62850
62909
  pipelineCommit: record4.pipelineCommit,
62851
62910
  repository: record4.repository,
62852
- ...record4.test ? { test: record4.test } : {}
62911
+ ...record4.test ? { test: record4.test } : {},
62912
+ // 与 cloud-host/pipeline/release.mjs 的 fingerprint 同序;老记录没有 config,哈希不变
62913
+ ...record4.config ? { config: Object.fromEntries(CONFIG_KEYS.map((k3) => [k3, record4.config[k3]])) } : {}
62853
62914
  })).digest("hex");
62854
62915
  if (record4.verification.fingerprint !== fingerprint) throw new Error("\u5236\u54C1\u5DF2\u504F\u79BB\u6D4B\u8BD5\u65F6\u7684\u7248\u672C\uFF0C\u8BF7\u91CD\u65B0\u6D4B\u8BD5");
62855
62916
  return record4;
@@ -63137,6 +63198,7 @@ var SCAN_BASE_LABEL = {
63137
63198
 
63138
63199
  // src/deploy/run-status.ts
63139
63200
  init_protocol();
63201
+ var INTEGRATION_CONFIG_RUN_PREFIX = "persona-integration-config-";
63140
63202
  var runsOf = (slug, file, query) => `/repos/${slug}/actions/workflows/${encodeURIComponent(file)}/runs?${query}`;
63141
63203
  var pipelineJobKey = (name) => DEPLOY_STAGE_KEYS.find((k3) => name === k3 || name.startsWith(`${k3} `));
63142
63204
  function stageStatusOf(status, conclusion) {
@@ -63190,7 +63252,7 @@ async function findReleaseRun(api, slug, target, jobsCache) {
63190
63252
  `${target.environment === "integration" ? `head_sha=${encodeURIComponent(target.head)}&` : ""}branch=${branch}&per_page=100&page=${page}`
63191
63253
  ));
63192
63254
  if (!Array.isArray(listed?.workflow_runs)) return void 0;
63193
- const raw = listed.workflow_runs.find((r2) => r2.head_branch === branch && (target.environment === "production" ? r2.event === "workflow_dispatch" && r2.display_title === `persona-production-${target.promotionId}` : r2.head_sha === target.head && (r2.event === "push" || r2.event === "workflow_dispatch")));
63255
+ const raw = listed.workflow_runs.find((r2) => r2.head_branch === branch && (target.environment === "production" ? r2.event === "workflow_dispatch" && r2.display_title === `persona-production-${target.promotionId}` : target.configId ? r2.head_sha === target.head && r2.event === "workflow_dispatch" && r2.display_title === `${INTEGRATION_CONFIG_RUN_PREFIX}${target.configId}` : r2.head_sha === target.head && (r2.event === "push" || r2.event === "workflow_dispatch") && !(typeof r2.display_title === "string" && r2.display_title.startsWith(INTEGRATION_CONFIG_RUN_PREFIX))));
63194
63256
  if (raw) return readPinnedRun(api, slug, raw, target.environment, target.promotionId, jobsCache);
63195
63257
  if (listed.workflow_runs.length < 100 && (listed.total_count === void 0 || page * 100 >= listed.total_count)) return null;
63196
63258
  }
@@ -63513,6 +63575,12 @@ function buildDeployHandlers(deps) {
63513
63575
  ...historyError ? { historyError } : {}
63514
63576
  };
63515
63577
  }
63578
+ async function withLegacyConfig(dir, releases) {
63579
+ const latestIntegration = [...releases].reverse().find((r2) => r2.environment === "integration" && !r2.legacy);
63580
+ if (!latestIntegration || latestIntegration.config || latestIntegration.verified?.config) return releases;
63581
+ const config = await testedConfigOf(dir, latestIntegration);
63582
+ return config ? releases.map((r2) => r2 === latestIntegration ? { ...r2, config } : r2) : releases;
63583
+ }
63516
63584
  async function stateOf(personaId2, selection = {}, measure = directRead) {
63517
63585
  const { releaseHead, releaseId, environment } = selection;
63518
63586
  const historical = releaseHead !== void 0 || releaseId !== void 0;
@@ -63571,7 +63639,7 @@ function buildDeployHandlers(deps) {
63571
63639
  const integration = historical ? selected?.environment === "integration" ? selected : void 0 : [...file.releases].reverse().find((r2) => r2.environment === "integration");
63572
63640
  const intRunRead = (async () => {
63573
63641
  if (!integration) return void 0;
63574
- const intRun2 = await measure("run", async () => integration.runId ? runById(api, slug, integration.runId, "integration", void 0, completedJobs) : await findReleaseRun(api, slug, { environment: "integration", head: integration.head }, completedJobs) ?? void 0, { environment: "integration" });
63642
+ const intRun2 = await measure("run", async () => integration.runId ? runById(api, slug, integration.runId, "integration", void 0, completedJobs) : await findReleaseRun(api, slug, { environment: "integration", head: integration.head, ...configIdOf(integration) }, completedJobs) ?? void 0, { environment: "integration" });
63575
63643
  if (intRun2 && intRun2.headSha === integration.head) {
63576
63644
  let updated = { ...integration, runId: intRun2.runId, runAttempt: intRun2.runAttempt };
63577
63645
  if (integration.runAttempt !== intRun2.runAttempt) updated = { ...updated, verified: void 0, artifactId: void 0, confirmation: void 0 };
@@ -63660,7 +63728,7 @@ function buildDeployHandlers(deps) {
63660
63728
  ...file.engineChannels ? { engineChannels: file.engineChannels } : {},
63661
63729
  ...repo2 ? { repo: repo2 } : {},
63662
63730
  scans,
63663
- releases: file.releases,
63731
+ releases: await withLegacyConfig(dir, file.releases),
63664
63732
  ...secrets ? { secrets } : {},
63665
63733
  ...run ? { run } : {},
63666
63734
  ...online !== void 0 ? { online } : {},
@@ -63970,6 +64038,23 @@ function buildDeployHandlers(deps) {
63970
64038
  await requireIntegrationPublishingConfig(api, slug);
63971
64039
  await api.putSecret(slug, "CLAWD_INT_TEST_LOGIN_PASS", await deps.testLoginPass(), "integration");
63972
64040
  };
64041
+ const configIdOf = (r2) => r2.reconfigOf && r2.id ? { configId: r2.id } : {};
64042
+ const testedConfigOf = async (dir, record4) => {
64043
+ if (record4.verified?.config) return record4.verified.config;
64044
+ if (record4.config) return record4.config;
64045
+ const raw = await readFileAtCommit(deps.exec, dir, record4.head, PERSONA_FILE_REL);
64046
+ if (raw === void 0) return void 0;
64047
+ try {
64048
+ return configOfPersona(JSON.parse(raw));
64049
+ } catch {
64050
+ return void 0;
64051
+ }
64052
+ };
64053
+ const CONFIG_KEY_LABEL = { engine: "\u5F15\u64CE", model: "\u6A21\u578B", effort: "\u63A8\u7406\u6863\u4F4D", size: "\u89C4\u683C", cpu: "CPU", taskConcurrency: "\u4EFB\u52A1\u5E76\u53D1" };
64054
+ const assertConfigTested = async (personaId2, record4) => {
64055
+ const changed = changedConfigKeys(configOfPersona(deps.personaEngine(personaId2) ?? {}), await testedConfigOf(mustPersona(personaId2), record4));
64056
+ if (changed?.length) throw invalid(`\u914D\u7F6E\u6539\u8FC7\u4E86\uFF08${changed.map((k3) => CONFIG_KEY_LABEL[k3]).join("\u3001")}\uFF09\uFF0C\u5148\u300C\u5E94\u7528\u5230\u6D4B\u8BD5\u73AF\u5883\u300D\u5E76\u786E\u8BA4\uFF0C\u518D\u4E0A\u7EBF`);
64057
+ };
63973
64058
  const releaseHandler = async (frame, _client, ctx) => {
63974
64059
  ensureOwner(ctx);
63975
64060
  const { personaId: personaId2, scanId } = parseArgs2(DeployReleaseArgsSchema, frame, "deploy:release");
@@ -64043,7 +64128,7 @@ function buildDeployHandlers(deps) {
64043
64128
  try {
64044
64129
  const result = await release(deps.exec, api, { dir, now: deps.now, auth, manifest, onPrepared: async (head) => {
64045
64130
  const latest2 = await readHistory(dir);
64046
- await writeScanFile(dir, { ...latest2, releases: [...latest2.releases, { id: releaseId, environment: "integration", head, contentHead: entry.head, scanId: entry.id, at: deps.now(), pushed: false }] });
64131
+ await writeScanFile(dir, { ...latest2, releases: [...latest2.releases, { id: releaseId, environment: "integration", head, contentHead: entry.head, scanId: entry.id, at: deps.now(), pushed: false, config: configOfPersona(deps.personaEngine(personaId2) ?? {}), workflowVersion: DEPLOY_WORKFLOW_VERSION }] });
64047
64132
  } });
64048
64133
  runCache.delete(runCacheKey(personaId2));
64049
64134
  const latest = await readHistory(dir);
@@ -64119,7 +64204,7 @@ function buildDeployHandlers(deps) {
64119
64204
  const repo2 = await repoInfo(deps.exec, dir);
64120
64205
  if (!repo2) throw invalid("\u4ED3\u5E93\u4E0D\u5B58\u5728");
64121
64206
  const { api } = await requireGithub();
64122
- const run = record4.runId ? await runById(api, repoSlug(repo2.url), record4.runId, "integration") : await findReleaseRun(api, repoSlug(repo2.url), { environment: "integration", head: record4.head });
64207
+ const run = record4.runId ? await runById(api, repoSlug(repo2.url), record4.runId, "integration") : await findReleaseRun(api, repoSlug(repo2.url), { environment: "integration", head: record4.head, ...configIdOf(record4) });
64123
64208
  if (!run || run.headSha !== record4.head) throw invalid("\u6682\u65F6\u67E5\u4E0D\u5230\u672C\u6B21\u6D4B\u8BD5\u6D41\u6C34\u7EBF\uFF0C\u8BF7\u5237\u65B0");
64124
64209
  const { artifactId, release: verified } = await readVerifiedRelease(api, repoSlug(repo2.url), run);
64125
64210
  if (requireDialogue) {
@@ -64146,7 +64231,7 @@ function buildDeployHandlers(deps) {
64146
64231
  verified,
64147
64232
  advanceMain: (head) => advanceMain(deps.exec, dir, auth, head),
64148
64233
  findRun: (id) => findReleaseRun(observedApi ?? api, slug, { environment: "production", head: record4.head, promotionId: id }),
64149
- dispatch: (id, verified2) => api.dispatchWorkflow(slug, DEPLOY_WORKFLOW_FILE, "main", { environment: "production", promotion_id: id, release_json: JSON.stringify(verified2) }),
64234
+ dispatch: (id, verified2) => api.dispatchWorkflow(slug, DEPLOY_WORKFLOW_FILE, "main", { environment: "production", promotion_id: id, release_json: JSON.stringify(verified2), ...verified2.config ? { config_json: JSON.stringify(verified2.config) } : {} }),
64150
64235
  actualImage: observedImage ?? (async () => (await deps.cloudState(cloudName, "production")).image),
64151
64236
  now: deps.now,
64152
64237
  persist: async (next) => {
@@ -64162,6 +64247,7 @@ function buildDeployHandlers(deps) {
64162
64247
  ensureOwner(ctx);
64163
64248
  const { personaId: personaId2, releaseId } = releaseArgs(frame, DeployConfirmTestArgsSchema);
64164
64249
  const record4 = await testedRelease(personaId2, releaseId, true, true);
64250
+ await assertConfigTested(personaId2, record4);
64165
64251
  const confirmation = record4.confirmation ?? { fingerprint: record4.verified.verification.fingerprint, runId: record4.runId, runAttempt: record4.runAttempt, at: deps.now() };
64166
64252
  const dir = mustPersona(personaId2), file = await readHistory(dir);
64167
64253
  await writeScanFile(dir, { ...file, releases: file.releases.map((r2) => r2.id === releaseId ? { ...r2, confirmation } : r2) });
@@ -64178,6 +64264,7 @@ function buildDeployHandlers(deps) {
64178
64264
  const snapshot = old?.verified ?? (stored?.confirmation?.fingerprint === stored?.verified?.verification.fingerprint ? stored?.verified : void 0);
64179
64265
  if (recovering && (!snapshot || !stored)) throw invalid("\u539F\u4E0A\u7EBF\u4EFB\u52A1\u7684\u5DF2\u6D4B\u5236\u54C1\u8BB0\u5F55\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u5148\u6838\u5B9E\u539F\u6D41\u6C34\u7EBF\uFF1B\u4E0D\u4F1A\u6539\u7528\u65B0\u7684\u6D4B\u8BD5\u7248\u672C");
64180
64266
  const record4 = recovering ? { ...stored, verified: snapshot } : await testedRelease(personaId2, releaseId, true, true);
64267
+ if (!recovering) await assertConfigTested(personaId2, record4);
64181
64268
  const repo2 = await repoInfo(deps.exec, mustPersona(personaId2));
64182
64269
  if (!repo2) throw invalid("\u4ED3\u5E93\u4E0D\u5B58\u5728");
64183
64270
  if (await deps.claimPublisher(cloudPersonaIdOf(repo2.url), "production") === "taken") throw invalid("\u7EBF\u4E0A\u73AF\u5883\u91CC\u7684\u8FD9\u4E2A\u540D\u5B57\u5DF2\u7531\u522B\u4EBA\u53D1\u5E03");
@@ -64196,6 +64283,47 @@ function buildDeployHandlers(deps) {
64196
64283
  const promotion = await continuePromotion(personaId2, record4, reuse ? { ...old, verified: snapshot ?? record4.verified } : { id: (0, import_node_crypto14.randomUUID)(), releaseId, at: deps.now(), phase: "prepared", verified: record4.verified }, true);
64197
64284
  return { response: { type: "deploy:promote", promotion } };
64198
64285
  };
64286
+ const applyConfig = async (frame, _client, ctx) => {
64287
+ ensureOwner(ctx);
64288
+ const { personaId: personaId2, releaseId } = releaseArgs(frame, DeployApplyConfigArgsSchema);
64289
+ const dir = mustPersona(personaId2);
64290
+ const wrong = wrongEngine(deps.personaEngine(personaId2)?.tool);
64291
+ if (wrong) throw invalid(wrong);
64292
+ const file = await readHistory(dir);
64293
+ const latest = [...file.releases].reverse().find((r2) => r2.environment === "integration");
64294
+ if (!latest || latest.id !== releaseId) throw invalid("\u53EA\u80FD\u5BF9\u6700\u65B0\u4E00\u6B21\u6D4B\u8BD5\u53D1\u5E03\u5E94\u7528\u914D\u7F6E\uFF0C\u5237\u65B0\u9875\u9762\u540E\u518D\u8BD5");
64295
+ if (!latest.workflowVersion || latest.workflowVersion < DEPLOY_WORKFLOW_VERSION) throw invalid("\u8FD9\u4E2A\u4ED3\u5E93\u7684\u53D1\u5E03\u6D41\u6C34\u7EBF\u662F\u65E7\u7248\u672C\uFF0C\u4E0D\u8BA4\u65B0\u914D\u7F6E\u2014\u2014\u8BF7\u5148\u5B8C\u6574\u91CD\u65B0\u53D1\u5E03\u4E00\u6B21\uFF08\u4F1A\u91CD\u65B0\u6784\u5EFA\uFF09");
64296
+ const repo2 = await repoInfo(deps.exec, dir);
64297
+ if (!repo2) throw invalid("\u4ED3\u5E93\u4E0D\u5B58\u5728");
64298
+ const { api } = await requireGithub();
64299
+ const slug = repoSlug(repo2.url);
64300
+ let base = latest;
64301
+ let retryAfterFailure = false;
64302
+ if (latest.reconfigOf) {
64303
+ const prior = await findReleaseRun(api, slug, { environment: "integration", head: latest.head, configId: latest.id });
64304
+ if (prior === void 0) throw invalid("\u6682\u65F6\u67E5\u4E0D\u5230\u4E0A\u4E00\u6B21\u5E94\u7528\u914D\u7F6E\u7684\u6D41\u6C34\u7EBF\uFF0C\u8BF7\u5237\u65B0");
64305
+ if (prior && prior.status !== "completed") throw invalid("\u4E0A\u4E00\u6B21\u5E94\u7528\u914D\u7F6E\u8FD8\u5728\u8DD1\uFF0C\u7B49\u5B83\u7ED3\u675F\u518D\u6539");
64306
+ if (prior && prior.conclusion === "failure") {
64307
+ base = file.releases.find((r2) => r2.id === latest.reconfigOf) ?? latest;
64308
+ retryAfterFailure = true;
64309
+ }
64310
+ }
64311
+ const source = await testedRelease(personaId2, base.id, false);
64312
+ const config = configOfPersona(deps.personaEngine(personaId2) ?? {});
64313
+ if (!config.model) throw invalid("\u8FD8\u6CA1\u9009\u6A21\u578B\u2014\u2014\u5728\u4E0A\u4E91\u9875\u300C\u5F15\u64CE\u4E0E\u6A21\u578B\u300D\u91CC\u9009\u4E00\u4E2A");
64314
+ if (!retryAfterFailure && !(changedConfigKeys(config, await testedConfigOf(dir, source)) ?? ["engine"]).length) throw invalid("\u914D\u7F6E\u548C\u6D4B\u8BD5\u73AF\u5883\u91CC\u7684\u4E00\u81F4\uFF0C\u6CA1\u6709\u9700\u8981\u5E94\u7528\u7684");
64315
+ const id = (0, import_node_crypto14.randomUUID)();
64316
+ try {
64317
+ await api.dispatchWorkflow(slug, DEPLOY_WORKFLOW_FILE, "integration", { environment: "integration", release_json: JSON.stringify(source.verified), config_json: JSON.stringify(config), config_id: id });
64318
+ } catch (err) {
64319
+ throw new ClawdError(ERROR_CODES.INTERNAL, err instanceof Error ? err.message : String(err));
64320
+ }
64321
+ const { verified: _v, artifactId: _a2, runId: _r2, runAttempt: _ra, confirmation: _c, ...kept } = source;
64322
+ const fresh = await readHistory(dir);
64323
+ await writeScanFile(dir, { ...fresh, releases: [...fresh.releases, { ...kept, id, at: deps.now(), pushed: true, config, reconfigOf: source.id }] });
64324
+ runCache.delete(runCacheKey(personaId2));
64325
+ return { response: { type: "deploy:applyConfig", sha: source.head, actionsUrl: "https://github.com/" + slug + "/actions", releaseId: id } };
64326
+ };
64199
64327
  const openTest = async (frame, _client, ctx) => {
64200
64328
  ensureOwner(ctx);
64201
64329
  const { personaId: personaId2, releaseId } = releaseArgs(frame, DeployOpenTestArgsSchema);
@@ -64282,6 +64410,7 @@ function buildDeployHandlers(deps) {
64282
64410
  "deploy:confirmTest": serial(confirmTest),
64283
64411
  "deploy:promote": serial(promote),
64284
64412
  "deploy:openTest": serial(openTest),
64413
+ "deploy:applyConfig": serial(applyConfig),
64285
64414
  "deploy:scan": serial(scanHandler),
64286
64415
  "deploy:patch": serial(patch),
64287
64416
  "deploy:putSecret": serial(putSecret),
@@ -74595,6 +74724,7 @@ var METHOD_GRANT_MAP = {
74595
74724
  "deploy:finishScan": ADMIN_ANY,
74596
74725
  "deploy:release": ADMIN_ANY,
74597
74726
  "deploy:confirmTest": ADMIN_ANY,
74727
+ "deploy:applyConfig": ADMIN_ANY,
74598
74728
  "deploy:promote": ADMIN_ANY,
74599
74729
  "deploy:openTest": ADMIN_ANY,
74600
74730
  "deploy:rerunJob": ADMIN_ANY,
@@ -74767,6 +74897,7 @@ var HUMAN_ONLY_METHODS = [
74767
74897
  "deploy:scan",
74768
74898
  "deploy:release",
74769
74899
  "deploy:confirmTest",
74900
+ "deploy:applyConfig",
74770
74901
  "deploy:promote",
74771
74902
  "deploy:openTest",
74772
74903
  "deploy:putSecret",
@@ -74817,7 +74948,7 @@ function computeMethodAccess(args) {
74817
74948
  }
74818
74949
 
74819
74950
  // src/version.ts
74820
- var version = "0.2.667".length > 0 ? "0.2.667" : "dev";
74951
+ var version = "0.2.670".length > 0 ? "0.2.670" : "dev";
74821
74952
 
74822
74953
  // src/cli-probe/probe.ts
74823
74954
  var fs86 = __toESM(require("fs"), 1);
@@ -78444,7 +78575,7 @@ async function startDaemon(config) {
78444
78575
  personaDir: (personaId2) => personaStore.personaDirPath(personaId2),
78445
78576
  personaEngine: (personaId2) => {
78446
78577
  const p3 = personaRegistry.get(personaId2);
78447
- return p3 ? { ...p3.tool ? { tool: p3.tool } : {}, ...p3.model ? { model: p3.model } : {}, ...p3.cloudEngine ? { cloudEngine: p3.cloudEngine } : {} } : void 0;
78578
+ return p3 ? { ...p3.tool ? { tool: p3.tool } : {}, ...p3.model ? { model: p3.model } : {}, ...p3.effort ? { effort: p3.effort } : {}, ...p3.cloudEngine ? { cloudEngine: p3.cloudEngine } : {}, ...p3.size ? { size: p3.size } : {}, ...p3.cpu !== void 0 ? { cpu: p3.cpu } : {}, ...p3.taskConcurrency !== void 0 ? { taskConcurrency: p3.taskConcurrency } : {} } : void 0;
78448
78579
  },
78449
78580
  exec: runCommand,
78450
78581
  // 探本机前刷一次登录 shell 的 PATH:daemon 继承的是 app 启动时的快照,之后装的 CLI(~/.local/bin)看不见。