@clawos-dev/clawd 0.2.666 → 0.2.668

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
@@ -62157,6 +62193,7 @@ function renderDeployWorkflow(config) {
62157
62193
  " - name: Select pinned source identities",
62158
62194
  " env:",
62159
62195
  " RELEASE_JSON: ${{ inputs.release_json }}",
62196
+ " CONFIG_JSON: ${{ inputs.config_json }}",
62160
62197
  " run: |",
62161
62198
  " node --input-type=module <<'NODE'",
62162
62199
  ' import fs from "node:fs"',
@@ -62164,7 +62201,9 @@ function renderDeployWorkflow(config) {
62164
62201
  ' const r = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, "utf8")) : process.env.RELEASE_JSON ? JSON.parse(process.env.RELEASE_JSON) : undefined',
62165
62202
  ' 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
62203
  ' 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")',
62204
+ " // \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",
62205
+ ' const reconfigure = process.env.DEPLOY_ENVIRONMENT === "integration" && process.env.CONFIG_JSON && !fs.existsSync(file)',
62206
+ ' 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
62207
  " NODE",
62169
62208
  ""
62170
62209
  ];
@@ -62191,6 +62230,8 @@ function renderDeployWorkflow(config) {
62191
62230
  "",
62192
62231
  " - name: \u8BFB\u90E8\u7F72\u6587\u4EF6",
62193
62232
  " id: plan",
62233
+ " env:",
62234
+ " CLAWD_CONFIG_JSON: ${{ inputs.config_json }}",
62194
62235
  " run: |",
62195
62236
  " node clawos/cloud-host/pipeline/deploy-file.mjs \\",
62196
62237
  " --persona-dir persona \\",
@@ -62255,6 +62296,7 @@ function renderDeployWorkflow(config) {
62255
62296
  " --effort '${{ steps.plan.outputs.effort }}' \\",
62256
62297
  " --size '${{ steps.plan.outputs.size }}' \\",
62257
62298
  " --cpu '${{ steps.plan.outputs.cpu }}' \\",
62299
+ " --config-source '${{ steps.plan.outputs.config_source }}' \\",
62258
62300
  " --task-concurrency '${{ steps.plan.outputs.task_concurrency }}' \\",
62259
62301
  " --engine '${{ steps.plan.outputs.engine }}' \\",
62260
62302
  " --connectors '${{ steps.plan.outputs.connectors }}' \\",
@@ -62270,7 +62312,7 @@ function renderDeployWorkflow(config) {
62270
62312
  ];
62271
62313
  return [
62272
62314
  "name: clawd deploy",
62273
- "run-name: ${{ inputs.environment == 'production' && format('persona-production-{0}', inputs.promotion_id) || 'persona-integration' }}",
62315
+ "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
62316
  "# \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
62317
  "on:",
62276
62318
  " push:",
@@ -62285,7 +62327,15 @@ function renderDeployWorkflow(config) {
62285
62327
  " type: string",
62286
62328
  " required: false",
62287
62329
  " release_json:",
62288
- " description: Verified Persona release JSON for production promotion",
62330
+ " description: Verified Persona release JSON for production promotion or config re-test",
62331
+ " type: string",
62332
+ " required: false",
62333
+ " config_json:",
62334
+ " description: Engine/model/resource config (canonical JSON); replaces .clawd/persona.json for this run",
62335
+ " type: string",
62336
+ " required: false",
62337
+ " config_id:",
62338
+ " description: Identifies an integration config re-test run (run-name)",
62289
62339
  " type: string",
62290
62340
  " required: false",
62291
62341
  "",
@@ -62338,6 +62388,7 @@ function renderDeployWorkflow(config) {
62338
62388
  " ALIBABA_CLOUD_ACCESS_KEY_ID: ${{ secrets.ALI_ACS_ACCESS_KEY_ID }}",
62339
62389
  " ALIBABA_CLOUD_ACCESS_KEY_SECRET: ${{ secrets.ALI_ACS_ACCESS_KEY_SECRET }}",
62340
62390
  " RELEASE_JSON: ${{ inputs.release_json }}",
62391
+ " CLAWD_CONFIG_JSON: ${{ steps.plan.outputs.config_json }}",
62341
62392
  " CLAWD_PUBLISHER_TEST: ${{ steps.plan.outputs.test }}",
62342
62393
  " run: node clawos/cloud-host/pipeline/persona-candidate.mjs",
62343
62394
  "",
@@ -62799,6 +62850,10 @@ async function fileInCommit(exec, dir, sha, rel) {
62799
62850
  const r2 = await git(exec, dir, ["cat-file", "-e", `${sha}:${rel}`]);
62800
62851
  return !r2.spawnError && r2.code === 0;
62801
62852
  }
62853
+ async function readFileAtCommit(exec, dir, sha, rel) {
62854
+ const r2 = await git(exec, dir, ["show", `${sha}:${rel}`]);
62855
+ return !r2.spawnError && r2.code === 0 ? r2.stdout : void 0;
62856
+ }
62802
62857
  async function commitAll(exec, args) {
62803
62858
  await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
62804
62859
  const staged = await git(exec, args.dir, ["diff", "--cached", "--quiet"]);
@@ -62849,7 +62904,9 @@ function validatePersonaRelease(raw, expected) {
62849
62904
  baseImage: record4.baseImage,
62850
62905
  pipelineCommit: record4.pipelineCommit,
62851
62906
  repository: record4.repository,
62852
- ...record4.test ? { test: record4.test } : {}
62907
+ ...record4.test ? { test: record4.test } : {},
62908
+ // 与 cloud-host/pipeline/release.mjs 的 fingerprint 同序;老记录没有 config,哈希不变
62909
+ ...record4.config ? { config: Object.fromEntries(CONFIG_KEYS.map((k3) => [k3, record4.config[k3]])) } : {}
62853
62910
  })).digest("hex");
62854
62911
  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
62912
  return record4;
@@ -63137,6 +63194,7 @@ var SCAN_BASE_LABEL = {
63137
63194
 
63138
63195
  // src/deploy/run-status.ts
63139
63196
  init_protocol();
63197
+ var INTEGRATION_CONFIG_RUN_PREFIX = "persona-integration-config-";
63140
63198
  var runsOf = (slug, file, query) => `/repos/${slug}/actions/workflows/${encodeURIComponent(file)}/runs?${query}`;
63141
63199
  var pipelineJobKey = (name) => DEPLOY_STAGE_KEYS.find((k3) => name === k3 || name.startsWith(`${k3} `));
63142
63200
  function stageStatusOf(status, conclusion) {
@@ -63190,7 +63248,7 @@ async function findReleaseRun(api, slug, target, jobsCache) {
63190
63248
  `${target.environment === "integration" ? `head_sha=${encodeURIComponent(target.head)}&` : ""}branch=${branch}&per_page=100&page=${page}`
63191
63249
  ));
63192
63250
  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")));
63251
+ 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
63252
  if (raw) return readPinnedRun(api, slug, raw, target.environment, target.promotionId, jobsCache);
63195
63253
  if (listed.workflow_runs.length < 100 && (listed.total_count === void 0 || page * 100 >= listed.total_count)) return null;
63196
63254
  }
@@ -63513,6 +63571,12 @@ function buildDeployHandlers(deps) {
63513
63571
  ...historyError ? { historyError } : {}
63514
63572
  };
63515
63573
  }
63574
+ async function withLegacyConfig(dir, releases) {
63575
+ const latestIntegration = [...releases].reverse().find((r2) => r2.environment === "integration" && !r2.legacy);
63576
+ if (!latestIntegration || latestIntegration.config || latestIntegration.verified?.config) return releases;
63577
+ const config = await testedConfigOf(dir, latestIntegration);
63578
+ return config ? releases.map((r2) => r2 === latestIntegration ? { ...r2, config } : r2) : releases;
63579
+ }
63516
63580
  async function stateOf(personaId2, selection = {}, measure = directRead) {
63517
63581
  const { releaseHead, releaseId, environment } = selection;
63518
63582
  const historical = releaseHead !== void 0 || releaseId !== void 0;
@@ -63571,7 +63635,7 @@ function buildDeployHandlers(deps) {
63571
63635
  const integration = historical ? selected?.environment === "integration" ? selected : void 0 : [...file.releases].reverse().find((r2) => r2.environment === "integration");
63572
63636
  const intRunRead = (async () => {
63573
63637
  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" });
63638
+ 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
63639
  if (intRun2 && intRun2.headSha === integration.head) {
63576
63640
  let updated = { ...integration, runId: intRun2.runId, runAttempt: intRun2.runAttempt };
63577
63641
  if (integration.runAttempt !== intRun2.runAttempt) updated = { ...updated, verified: void 0, artifactId: void 0, confirmation: void 0 };
@@ -63660,7 +63724,7 @@ function buildDeployHandlers(deps) {
63660
63724
  ...file.engineChannels ? { engineChannels: file.engineChannels } : {},
63661
63725
  ...repo2 ? { repo: repo2 } : {},
63662
63726
  scans,
63663
- releases: file.releases,
63727
+ releases: await withLegacyConfig(dir, file.releases),
63664
63728
  ...secrets ? { secrets } : {},
63665
63729
  ...run ? { run } : {},
63666
63730
  ...online !== void 0 ? { online } : {},
@@ -63970,6 +64034,23 @@ function buildDeployHandlers(deps) {
63970
64034
  await requireIntegrationPublishingConfig(api, slug);
63971
64035
  await api.putSecret(slug, "CLAWD_INT_TEST_LOGIN_PASS", await deps.testLoginPass(), "integration");
63972
64036
  };
64037
+ const configIdOf = (r2) => r2.reconfigOf && r2.id ? { configId: r2.id } : {};
64038
+ const testedConfigOf = async (dir, record4) => {
64039
+ if (record4.verified?.config) return record4.verified.config;
64040
+ if (record4.config) return record4.config;
64041
+ const raw = await readFileAtCommit(deps.exec, dir, record4.head, PERSONA_FILE_REL);
64042
+ if (raw === void 0) return void 0;
64043
+ try {
64044
+ return configOfPersona(JSON.parse(raw));
64045
+ } catch {
64046
+ return void 0;
64047
+ }
64048
+ };
64049
+ 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" };
64050
+ const assertConfigTested = async (personaId2, record4) => {
64051
+ const changed = changedConfigKeys(configOfPersona(deps.personaEngine(personaId2) ?? {}), await testedConfigOf(mustPersona(personaId2), record4));
64052
+ 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`);
64053
+ };
63973
64054
  const releaseHandler = async (frame, _client, ctx) => {
63974
64055
  ensureOwner(ctx);
63975
64056
  const { personaId: personaId2, scanId } = parseArgs2(DeployReleaseArgsSchema, frame, "deploy:release");
@@ -64043,7 +64124,7 @@ function buildDeployHandlers(deps) {
64043
64124
  try {
64044
64125
  const result = await release(deps.exec, api, { dir, now: deps.now, auth, manifest, onPrepared: async (head) => {
64045
64126
  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 }] });
64127
+ 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
64128
  } });
64048
64129
  runCache.delete(runCacheKey(personaId2));
64049
64130
  const latest = await readHistory(dir);
@@ -64119,7 +64200,7 @@ function buildDeployHandlers(deps) {
64119
64200
  const repo2 = await repoInfo(deps.exec, dir);
64120
64201
  if (!repo2) throw invalid("\u4ED3\u5E93\u4E0D\u5B58\u5728");
64121
64202
  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 });
64203
+ 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
64204
  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
64205
  const { artifactId, release: verified } = await readVerifiedRelease(api, repoSlug(repo2.url), run);
64125
64206
  if (requireDialogue) {
@@ -64146,7 +64227,7 @@ function buildDeployHandlers(deps) {
64146
64227
  verified,
64147
64228
  advanceMain: (head) => advanceMain(deps.exec, dir, auth, head),
64148
64229
  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) }),
64230
+ 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
64231
  actualImage: observedImage ?? (async () => (await deps.cloudState(cloudName, "production")).image),
64151
64232
  now: deps.now,
64152
64233
  persist: async (next) => {
@@ -64162,6 +64243,7 @@ function buildDeployHandlers(deps) {
64162
64243
  ensureOwner(ctx);
64163
64244
  const { personaId: personaId2, releaseId } = releaseArgs(frame, DeployConfirmTestArgsSchema);
64164
64245
  const record4 = await testedRelease(personaId2, releaseId, true, true);
64246
+ await assertConfigTested(personaId2, record4);
64165
64247
  const confirmation = record4.confirmation ?? { fingerprint: record4.verified.verification.fingerprint, runId: record4.runId, runAttempt: record4.runAttempt, at: deps.now() };
64166
64248
  const dir = mustPersona(personaId2), file = await readHistory(dir);
64167
64249
  await writeScanFile(dir, { ...file, releases: file.releases.map((r2) => r2.id === releaseId ? { ...r2, confirmation } : r2) });
@@ -64178,6 +64260,7 @@ function buildDeployHandlers(deps) {
64178
64260
  const snapshot = old?.verified ?? (stored?.confirmation?.fingerprint === stored?.verified?.verification.fingerprint ? stored?.verified : void 0);
64179
64261
  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
64262
  const record4 = recovering ? { ...stored, verified: snapshot } : await testedRelease(personaId2, releaseId, true, true);
64263
+ if (!recovering) await assertConfigTested(personaId2, record4);
64181
64264
  const repo2 = await repoInfo(deps.exec, mustPersona(personaId2));
64182
64265
  if (!repo2) throw invalid("\u4ED3\u5E93\u4E0D\u5B58\u5728");
64183
64266
  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 +64279,47 @@ function buildDeployHandlers(deps) {
64196
64279
  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
64280
  return { response: { type: "deploy:promote", promotion } };
64198
64281
  };
64282
+ const applyConfig = async (frame, _client, ctx) => {
64283
+ ensureOwner(ctx);
64284
+ const { personaId: personaId2, releaseId } = releaseArgs(frame, DeployApplyConfigArgsSchema);
64285
+ const dir = mustPersona(personaId2);
64286
+ const wrong = wrongEngine(deps.personaEngine(personaId2)?.tool);
64287
+ if (wrong) throw invalid(wrong);
64288
+ const file = await readHistory(dir);
64289
+ const latest = [...file.releases].reverse().find((r2) => r2.environment === "integration");
64290
+ 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");
64291
+ 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");
64292
+ const repo2 = await repoInfo(deps.exec, dir);
64293
+ if (!repo2) throw invalid("\u4ED3\u5E93\u4E0D\u5B58\u5728");
64294
+ const { api } = await requireGithub();
64295
+ const slug = repoSlug(repo2.url);
64296
+ let base = latest;
64297
+ let retryAfterFailure = false;
64298
+ if (latest.reconfigOf) {
64299
+ const prior = await findReleaseRun(api, slug, { environment: "integration", head: latest.head, configId: latest.id });
64300
+ 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");
64301
+ if (prior && prior.status !== "completed") throw invalid("\u4E0A\u4E00\u6B21\u5E94\u7528\u914D\u7F6E\u8FD8\u5728\u8DD1\uFF0C\u7B49\u5B83\u7ED3\u675F\u518D\u6539");
64302
+ if (prior && prior.conclusion === "failure") {
64303
+ base = file.releases.find((r2) => r2.id === latest.reconfigOf) ?? latest;
64304
+ retryAfterFailure = true;
64305
+ }
64306
+ }
64307
+ const source = await testedRelease(personaId2, base.id, false);
64308
+ const config = configOfPersona(deps.personaEngine(personaId2) ?? {});
64309
+ 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");
64310
+ 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");
64311
+ const id = (0, import_node_crypto14.randomUUID)();
64312
+ try {
64313
+ await api.dispatchWorkflow(slug, DEPLOY_WORKFLOW_FILE, "integration", { environment: "integration", release_json: JSON.stringify(source.verified), config_json: JSON.stringify(config), config_id: id });
64314
+ } catch (err) {
64315
+ throw new ClawdError(ERROR_CODES.INTERNAL, err instanceof Error ? err.message : String(err));
64316
+ }
64317
+ const { verified: _v, artifactId: _a2, runId: _r2, runAttempt: _ra, confirmation: _c, ...kept } = source;
64318
+ const fresh = await readHistory(dir);
64319
+ await writeScanFile(dir, { ...fresh, releases: [...fresh.releases, { ...kept, id, at: deps.now(), pushed: true, config, reconfigOf: source.id }] });
64320
+ runCache.delete(runCacheKey(personaId2));
64321
+ return { response: { type: "deploy:applyConfig", sha: source.head, actionsUrl: "https://github.com/" + slug + "/actions", releaseId: id } };
64322
+ };
64199
64323
  const openTest = async (frame, _client, ctx) => {
64200
64324
  ensureOwner(ctx);
64201
64325
  const { personaId: personaId2, releaseId } = releaseArgs(frame, DeployOpenTestArgsSchema);
@@ -64282,6 +64406,7 @@ function buildDeployHandlers(deps) {
64282
64406
  "deploy:confirmTest": serial(confirmTest),
64283
64407
  "deploy:promote": serial(promote),
64284
64408
  "deploy:openTest": serial(openTest),
64409
+ "deploy:applyConfig": serial(applyConfig),
64285
64410
  "deploy:scan": serial(scanHandler),
64286
64411
  "deploy:patch": serial(patch),
64287
64412
  "deploy:putSecret": serial(putSecret),
@@ -74595,6 +74720,7 @@ var METHOD_GRANT_MAP = {
74595
74720
  "deploy:finishScan": ADMIN_ANY,
74596
74721
  "deploy:release": ADMIN_ANY,
74597
74722
  "deploy:confirmTest": ADMIN_ANY,
74723
+ "deploy:applyConfig": ADMIN_ANY,
74598
74724
  "deploy:promote": ADMIN_ANY,
74599
74725
  "deploy:openTest": ADMIN_ANY,
74600
74726
  "deploy:rerunJob": ADMIN_ANY,
@@ -74767,6 +74893,7 @@ var HUMAN_ONLY_METHODS = [
74767
74893
  "deploy:scan",
74768
74894
  "deploy:release",
74769
74895
  "deploy:confirmTest",
74896
+ "deploy:applyConfig",
74770
74897
  "deploy:promote",
74771
74898
  "deploy:openTest",
74772
74899
  "deploy:putSecret",
@@ -74817,7 +74944,7 @@ function computeMethodAccess(args) {
74817
74944
  }
74818
74945
 
74819
74946
  // src/version.ts
74820
- var version = "0.2.666".length > 0 ? "0.2.666" : "dev";
74947
+ var version = "0.2.668".length > 0 ? "0.2.668" : "dev";
74821
74948
 
74822
74949
  // src/cli-probe/probe.ts
74823
74950
  var fs86 = __toESM(require("fs"), 1);
@@ -78444,7 +78571,7 @@ async function startDaemon(config) {
78444
78571
  personaDir: (personaId2) => personaStore.personaDirPath(personaId2),
78445
78572
  personaEngine: (personaId2) => {
78446
78573
  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;
78574
+ 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
78575
  },
78449
78576
  exec: runCommand,
78450
78577
  // 探本机前刷一次登录 shell 的 PATH:daemon 继承的是 app 启动时的快照,之后装的 CLI(~/.local/bin)看不见。