@xdxer/dingtalk-agent 0.1.5-beta.10 → 0.1.5-beta.12

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.
@@ -1 +1 @@
1
- {"version":3,"file":"schedule-plan.js","sourceRoot":"","sources":["../../src/schedule-plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Schedule reconcile plan — dta's half of the driving contract.\n *\n * dta owns: read the declared schedules (dingtalk-agent.json#schedules), validate\n * them, and map them to a provider-specific reconcile plan. dta does NOT fire or\n * live-write triggers — that stays with the platform ops path (see ARCHITECTURE\n * §3.1, \"触发器不属于 dingtalk-agent 核心执行\"). The plan is the authoritative,\n * provider-mapped desired state the ops skill reconciles against live.\n *\n * Pure: no network, no side effects — safe to run in CI and fully testable.\n */\nimport {\n PREDICATE_ID, inspectAgentProject, multicaAgentIdSource,\n type AgentProjectInfo, type DeclaredPredicate, type DeclaredSchedule,\n} from './development-workspace.js'\n\n/**\n * completion 的 predicate 引用形态(issue #24):`predicate:<id>` 指向 manifest\n * `predicates` 注册表里唯一的判据。声明侧与运行路径(autopilot description 里的\n * `[dta-completion-predicate] <id>` 标记、Receipt)引用同一 ID——两个求值方无法\n * 静默分叉(真事故:两套手写判据分叉 30 小时无报警)。\n */\nexport const COMPLETION_PREDICATE_PREFIX = 'predicate:'\n\n/**\n * completion 的三态解析(issue #39):自由文本 / 合法引用 / malformed 引用。\n * 只要出现 `predicate:` 前缀就已经表达了「我在引用注册表」的意图——id 为空或\n * 格式非法时绝不能降级成 legacy 自由文本(那是 fail-open),必须单独成态去阻断。\n */\nexport function parseCompletionRef(completion?: string):\n | { kind: 'free-text' }\n | { kind: 'ref'; id: string }\n | { kind: 'malformed'; raw: string } {\n const text = (completion || '').trim()\n if (!text.startsWith(COMPLETION_PREDICATE_PREFIX)) return { kind: 'free-text' }\n const id = text.slice(COMPLETION_PREDICATE_PREFIX.length).trim()\n return PREDICATE_ID.test(id) ? { kind: 'ref', id } : { kind: 'malformed', raw: text }\n}\n\nexport function completionPredicateId(completion?: string): string {\n const parsed = parseCompletionRef(completion)\n return parsed.kind === 'ref' ? parsed.id : ''\n}\n\n/**\n * autopilot description 里 predicate 标记的【唯一】parser(issue #40)。\n *\n * 合同:标记是独立一行 `[dta-completion-predicate] <kebab-id>`(写入侧见\n * multicaApplyCommand)。所有回读方——TS 的对账、Python 的运行时解析\n * (multica_ext.py 的 parse_predicate_marker 是同一正则的镜像,改这里必须同步改\n * 那边)——都必须走这个定义,不许各自手写正则:两套 parser 静默分叉正是\n * 注册表要消灭的那类事故。\n *\n * 多枚标记不一致返回 conflict 而不是挑一个:挑选规则(首个/末个)就是第二份\n * 隐式合同,回读方猜错方向时漂移会被静默放过。\n */\nexport const COMPLETION_PREDICATE_MARKER =\n /^\\[dta-completion-predicate\\] ([a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?)$/gm\n\nexport function parseCompletionPredicateMarker(description?: string):\n | { kind: 'none' }\n | { kind: 'id'; id: string }\n | { kind: 'conflict'; ids: string[] }\n | { kind: 'malformed'; raws: string[] } {\n // malformed 独立成态(issue #45,与 #39 同一课):行首出现 marker 前缀就已表达\n // 「我在标注判据」的意图,id 空/非法时折成 none 会静默跳过整个回读校验——fail-open。\n const ids: string[] = []\n const raws: string[] = []\n for (const line of (description || '').split(/\\r?\\n/)) {\n if (!line.startsWith('[dta-completion-predicate]')) continue\n const m = line.match(/^\\[dta-completion-predicate\\] ([a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?)$/)\n if (m) ids.push(m[1])\n else raws.push(line)\n }\n if (raws.length) return { kind: 'malformed', raws }\n if (!ids.length) return { kind: 'none' }\n const unique = [...new Set(ids)]\n return unique.length === 1 ? { kind: 'id', id: unique[0] } : { kind: 'conflict', ids: unique }\n}\n\nexport interface SchedulePlanEntry extends DeclaredSchedule {\n applyCommand: string\n // 平台是否为这一拍记账。work-item 每次 fire 建 issue → 有 token 用量可查;\n // ephemeral(run_only) 不建 issue → 平台不记账,这一拍的成本【看不见】。\n // 改 mode 就是在改计量口径,这里显式标出来,别让它变成隐形盲区。\n metered: boolean\n /** completion 的注册表 predicate id(引用形态且已注册时);运行路径引用同一 ID。 */\n completionPredicate?: string\n}\n\nexport interface SchedulePlan {\n $schema: 'dingtalk-agent/schedule-plan@2'\n workspace: string\n provider: string\n supported: boolean\n reason?: string\n count: number\n /**\n * Agent 绑定来源:manifest 声明的 env 引用(declared-env),或首次 deploy 写入\n * workspace state 的真实 ID(deploy-state)。黄金路径(scaffold 故意省略\n * agentIdFrom、由 deploy 创建 Agent 存 ID)走的是后者。\n */\n agentIdSource?: 'declared-env' | 'deploy-state'\n schedules: SchedulePlanEntry[]\n // 声明了 cron 却没写 completion(数据派生的完成判据)的节律名。\n // 没有完成判据 = 只能靠\"那一拍跑了没有\"来判完成,而平台会漏拍/跳过迟到的 fire,\n // 一次抖动就永久丢一期。这些拍记成缺口,不静默放过。\n missingCompletion: string[]\n // 会同一时刻并发 fire 的节律对 / 无法静态确认是否同刻的对。\n // ⚠️ plan 是 operator 真正照着部署的路径,所以撞车必须在这里也拦——\n // 否则 show 报了撞车、plan 照样吐出 apply 命令,两条都建成 autopilot → 重复外发。\n collidingPairs: string[]\n unverifiedPairs: string[]\n // predicate 引用问题(阻断)与未注册自由文本(提示迁移)——与 show 同源,见 analyzeSchedules。\n completionRefIssues: string[]\n unregisteredCompletion: string[]\n reconcile: string\n sideEffect: false\n dingtalkSideEffect: false\n}\n\n// manifest 级的静态体检,show 和 plan 共用一份,避免两处逻辑漂移。\nexport function analyzeSchedules(\n declared: DeclaredSchedule[],\n predicates?: Record<string, DeclaredPredicate>,\n): {\n collidingPairs: string[]; unverifiedPairs: string[]; missingCompletion: string[]\n completionRefIssues: string[]; unregisteredCompletion: string[]\n} {\n const collidingPairs: string[] = []\n const unverifiedPairs: string[] = []\n for (let i = 0; i < declared.length; i++) {\n for (let j = i + 1; j < declared.length; j++) {\n const pair = `${declared[i].name}×${declared[j].name}`\n // 跨时区:本地无 tz 换算 → 判不准是否同刻 → unverified(不静默当 distinct)。\n if (declared[i].timezone !== declared[j].timezone) { unverifiedPairs.push(pair); continue }\n const r = firesCollide(declared[i].cron, declared[j].cron)\n if (r === 'collide') collidingPairs.push(pair)\n else if (r === 'unverified') unverifiedPairs.push(pair)\n }\n }\n const missingCompletion = declared.filter((s) => !s.completion?.trim()).map((s) => s.name)\n // predicate 引用体检(issue #24):引用必须解析到注册表;一旦项目声明了注册表,\n // 自由文本判据整体阻断——「写了一句话」不是判据,注入一句「跑过了」就能过。\n // 未声明注册表的存量项目维持现状,只列 unregistered 清单提示迁移(渐进门禁)。\n // 「已声明」看的是 manifest 里 predicates 键存不存在,不是注册表空不空:\n // 显式空注册表 = 项目已进入引用模式,自由文本照样阻断(issue #39)。\n const registryDeclared = predicates !== undefined\n const registry = predicates || {}\n const completionRefIssues: string[] = []\n const unregisteredCompletion: string[] = []\n for (const schedule of declared) {\n const completion = (schedule.completion || '').trim()\n if (!completion) continue // missingCompletion 已覆盖\n const parsed = parseCompletionRef(completion)\n if (parsed.kind === 'malformed') {\n // 有 predicate: 前缀但 id 空/非法:引用意图已表达,降级成自由文本就是 fail-open。\n completionRefIssues.push(\n `${schedule.name}: completion 的 predicate 引用格式非法(\"${parsed.raw}\")——` +\n '需要 predicate:<kebab-case-id>')\n } else if (parsed.kind === 'ref') {\n if (!registry[parsed.id]) {\n completionRefIssues.push(`${schedule.name}: completion 引用未注册的 predicate:${parsed.id}`)\n }\n } else if (registryDeclared) {\n completionRefIssues.push(\n `${schedule.name}: 已声明 predicates 注册表,completion 必须是 predicate:<id> 引用` +\n '(自由文本判据可被一句话注入绕过)')\n } else {\n unregisteredCompletion.push(schedule.name)\n }\n }\n return {\n collidingPairs, unverifiedPairs, missingCompletion,\n completionRefIssues, unregisteredCompletion,\n }\n}\n\n/**\n * 阻断部署的问题清单(空 = 可部署)。三类都算阻断,**包括 unverified**。\n *\n * unverified 曾经只告警,理由是\"本地证明不了 ≠ 有问题\"。这条理由错在赌的方向:\n * `0 9 * * *`@Asia/Shanghai 与 `0 1 * * *`@UTC 是**同一时刻**,静态比对看不出来,\n * 于是两条各建一个 autopilot、每天把同一条消息重复发给第三方——正是本守卫要防的那个伤害。\n * 证不出不撞车就当撞车(fail closed):挡住是一次响亮且可恢复的配置改动,放过是一次静默且发出去收不回的重复外发。\n *\n * 出路有两条,都不需要改这里:把两条节律写成同一个 timezone(对齐后走真实 occurrence 比对,\n * 判得出不撞就自动放行),或换掉本地读不懂的 cron 扩展语法。\n *\n * 判据只此一份:CLI 的 show 和 plan 都调它。此前两处各自手写 exit 条件,结果都漏了 unverified——\n * 抽出 analyzeSchedules 是为了防判据分叉,退出判据自己却分叉了。\n */\nexport function scheduleBlockers(a: {\n collidingPairs: string[]; unverifiedPairs: string[]; missingCompletion: string[]\n completionRefIssues?: string[]\n}): string[] {\n return [\n ...a.collidingPairs, ...a.unverifiedPairs, ...a.missingCompletion,\n ...(a.completionRefIssues || []),\n ]\n}\n\n// ephemeral = run-only (no work item); work-item = one work item per fire.\nconst MULTICA_MODE: Record<DeclaredSchedule['mode'], string> = {\n ephemeral: 'run_only',\n 'work-item': 'create_issue',\n}\n\n// 只有 work-item 每次 fire 建 issue、平台才有 token 用量可查。\nconst isMetered = (mode: DeclaredSchedule['mode']): boolean => mode === 'work-item'\n\n// 五个字段的上界(分 时 日 月 周)。\nconst CRON_FIELD_MAX = [59, 23, 31, 12, 7]\n// 名字字面量 → 数字(robfig/cron v3 支持,Multica 实测用 MON-FRI;不支持就会漏掉真双发)。\nconst DOW_NAMES: Record<string, number> = { SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6 }\nconst MON_NAMES: Record<string, number> = {\n JAN: 1, FEB: 2, MAR: 3, APR: 4, MAY: 5, JUN: 6, JUL: 7, AUG: 8, SEP: 9, OCT: 10, NOV: 11, DEC: 12,\n}\n\n/**\n * 把一个 cron 字段展开成它命中的整数集合。支持 `*`、列表、范围、步长,以及 dow/月的名字字面量。\n * 解析不出(`L`/`W`/`#`/`@` 等扩展语法)时返回 null —— 调用方据此判 unverified,不假装读懂了。\n */\nfunction expandCronField(field: string, max: number, names?: Record<string, number>): Set<number> | null {\n const out = new Set<number>()\n const num = (t: string): number => {\n const up = t.toUpperCase()\n if (names && up in names) return names[up]\n return /^\\d+$/.test(t) ? parseInt(t, 10) : NaN\n }\n for (const part of field.split(',')) {\n const [range, stepRaw] = part.split('/')\n const step = stepRaw ? parseInt(stepRaw, 10) : 1\n let lo = 0\n let hi = max\n if (range !== '*') {\n const [a, b] = range.split('-')\n lo = num(a)\n // robfig getRange 的特判:**单个数字 + 步长时上界拉到 max**——`5/15` 等价 `5-59/15` = {5,20,35,50},\n // 不是只有 {5}。当成 {5} 会漏掉 20/35/50,把真撞车判成 distinct → 正是本守卫要防的重复外发。\n hi = b === undefined ? (stepRaw ? max : lo) : num(b)\n }\n if (Number.isNaN(lo) || Number.isNaN(hi) || Number.isNaN(step) || step < 1) return null\n for (let v = lo; v <= hi && v <= max; v += step) out.add(v === 7 && max === 7 ? 0 : v)\n }\n // 空集合 = 越界(`70`)或倒序区间(`5-2`),是【没读懂】不是【永不 fire】。\n // 返回空集会让 setsIntersect 判 false → distinct(fail-open);返回 null → unverified,交给人核。\n return out.size === 0 ? null : out\n}\n\nfunction setsIntersect(a: Set<number>, b: Set<number>): boolean {\n for (const v of a) if (b.has(v)) return true\n return false\n}\n\ninterface ParsedCron { min: Set<number>; hour: Set<number>; dom: Set<number>; mon: Set<number>; dow: Set<number>; domStar: boolean; dowStar: boolean }\n\nfunction parseCron(cron: string): ParsedCron | null {\n const f = cron.trim().split(/\\s+/)\n if (f.length !== 5) return null\n const min = expandCronField(f[0], 59)\n const hour = expandCronField(f[1], 23)\n const dom = expandCronField(f[2], 31)\n const mon = expandCronField(f[3], 12, MON_NAMES)\n const dow = expandCronField(f[4], 7, DOW_NAMES)\n if (!min || !hour || !dom || !mon || !dow) return null\n return { min, hour, dom, mon, dow, domStar: f[2] === '*', dowStar: f[4] === '*' }\n}\n\n// robfig/cron v3 的日匹配:dom 和 dow 都受限时用 OR,否则各自 AND(* 视为全通过)。\nfunction dayMatch(p: ParsedCron, dom: number, dowV: number): boolean {\n if (!p.domStar && !p.dowStar) return p.dom.has(dom) || p.dow.has(dowV)\n return (p.domStar || p.dom.has(dom)) && (p.dowStar || p.dow.has(dowV))\n}\n\nconst isLeap = (y: number): boolean => (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0\nconst DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\nfunction daysInMonth(y: number, m: number): number {\n return m === 2 && isLeap(y) ? 29 : DAYS_IN_MONTH[m - 1]\n}\n\n/**\n * 两条节律会不会在【同一时刻】fire。返回 collide / distinct / unverified。\n * 判据【按真实 occurrence】算,不做简化字段交集——robfig 的 dom/dow 是 OR 语义、支持名字字面量,\n * 简化交集会误判(Multica 用 robfig/cron v3,`0 9 * * MON-FRI` 必须能识别)。\n * - 分/时/月任一集合不相交 → 一定不同刻 → distinct(便宜的早退)。\n * - 否则枚举【完整的 400 年格里高利周期】找同一天两条都 active。为什么必须整 400 年而不是几年窗口:\n * 像 `0 9 29 2 1` 与 `0 9 30 2 3` 的首个共同 fire 落在 2040-02-29,短窗口会漏判成 distinct(假阴性→漏真双发)。\n * 公历 (月,日,星期) 组合每 400 年完整重复,所以枚举一整轮 = 完备,\"整轮无碰撞\"才敢判 distinct。\n * - 任一字段解析不出(扩展语法)→ unverified:**绝不当 distinct**,交给人核。\n * export:给 scripts/check-schedule.mjs 做回归断言。\n */\nexport function firesCollide(cronA: string, cronB: string): 'collide' | 'distinct' | 'unverified' {\n const a = parseCron(cronA)\n const b = parseCron(cronB)\n if (!a || !b) return 'unverified'\n if (!setsIntersect(a.min, b.min) || !setsIntersect(a.hour, b.hour) || !setsIntersect(a.mon, b.mon)) {\n return 'distinct'\n }\n // 整 400 年周期手工走(不 new Date,避免 14 万次分配)。2000-01-01 是周六(dow=6)。\n let dow = 6\n for (let y = 2000; y < 2400; y++) {\n for (let m = 1; m <= 12; m++) {\n const monA = a.mon.has(m)\n const monB = b.mon.has(m)\n const dim = daysInMonth(y, m)\n if (!(monA && monB)) { dow = (dow + dim) % 7; continue } // 该月两条不同时活跃,跳过但推进 dow\n for (let dom = 1; dom <= dim; dom++) {\n if (dayMatch(a, dom, dow) && dayMatch(b, dom, dow)) return 'collide'\n dow = (dow + 1) % 7\n }\n }\n }\n return 'distinct'\n}\n\nfunction shellQuote(value: string): string {\n return `\"${value.replace(/([\"\\\\$`])/g, '\\\\$1')}\"`\n}\n\nfunction envVar(ref: string): string {\n // \"env:MULTICA_FDE_AGENT_ID\" -> \"$MULTICA_FDE_AGENT_ID\"\n return ref.startsWith('env:') ? `$${ref.slice(4)}` : ref\n}\n\nfunction multicaApplyCommand(\n schedule: DeclaredSchedule, profile: string, agentRef: string, completionPredicate = '',\n): string {\n // 声明与运行路径引用同一 predicate ID:description(唤醒提示词)尾部携带机器可读\n // 标记,Agent/运维在 autopilot 侧看到的判据 ID 与 manifest 注册表一字不差。\n const description = (schedule.wake || schedule.name) + (completionPredicate\n ? `\\n[dta-completion-predicate] ${completionPredicate}` : '')\n const parts = [\n 'python3 \"$MULTICA_EXT\" autopilot-create',\n `--profile ${profile}`,\n `--agent ${agentRef}`,\n `--title ${shellQuote(schedule.name)}`,\n `--mode ${MULTICA_MODE[schedule.mode]}`,\n `--cron ${shellQuote(schedule.cron)}`,\n `--timezone ${schedule.timezone}`,\n `--description ${shellQuote(description)}`,\n ]\n return parts.join(' ')\n}\n\n/**\n * Build the reconcile plan for one workspace's provider. Returns supported:false\n * (with a reason) when the target platform does not offer schedules yet — the\n * declared rhythms are then a recorded gap, never a silent downgrade.\n */\nexport function buildSchedulePlan(info: AgentProjectInfo, workspaceName: string): SchedulePlan {\n const workspace = info.workspaces.find((item) => item.name === workspaceName)\n if (!workspace) throw new Error(`Project 未声明 Workspace: ${workspaceName}`)\n const declared = info.project.schedules\n const provider = workspace.provider.kind\n // 撞车/未验证/缺判据/predicate 引用——manifest 级,与 provider 无关\n const analysis = analyzeSchedules(declared, info.project.predicates)\n\n if (provider === 'multica') {\n const profile = workspace.provider.profile || ''\n if (!profile) {\n throw new Error(`Workspace ${workspaceName} 的 multica provider 缺 profile`)\n }\n // Agent 绑定来源与 deploy 同一份回退链(multicaAgentIdSource):黄金路径下\n // scaffold 故意省略 agentIdFrom、首次 deploy 把真实 ID 写进 state——schedule\n // 此前不读 state,导致部署完的 Agent 声明节律时还被要求手工接线(E-03)。\n const source = multicaAgentIdSource(workspace)\n if (source.kind === 'none') {\n throw new Error(\n `Workspace ${workspaceName} 缺 Agent 绑定:manifest 未声明 provider.agentIdFrom,` +\n `且尚无部署 state(.dingtalk-agent/state/workspaces/${workspaceName}.json)。` +\n `先 dta deploy --workspace ${workspaceName} 创建 Agent(ID 会写入 state 自动复用),` +\n '或在 manifest 显式声明 provider.agentIdFrom')\n }\n const agentRef = source.kind === 'declared' ? envVar(source.ref) : source.agentId\n const agentIdSource = source.kind === 'declared' ? 'declared-env' as const : 'deploy-state' as const\n const schedules = declared.map((schedule) => {\n const predicateId = completionPredicateId(schedule.completion)\n const registered = predicateId && info.project.predicates?.[predicateId] ? predicateId : ''\n return {\n ...schedule,\n applyCommand: multicaApplyCommand(schedule, profile, agentRef, registered),\n metered: isMetered(schedule.mode),\n ...(registered ? { completionPredicate: registered } : {}),\n }\n })\n return {\n $schema: 'dingtalk-agent/schedule-plan@2',\n workspace: workspaceName, provider, supported: true, count: schedules.length,\n agentIdSource, schedules,\n ...analysis,\n reconcile: '由 dta-ops-multica 技能执行:先 autopilot-list 读线上,按 title 匹配,' +\n '只建缺的、改漂移的、删多余的;$MULTICA_EXT 指向 multica_ext.py' +\n (agentIdSource === 'declared-env'\n ? ',$…_AGENT_ID 由受信环境注入。'\n : ';Agent ID 来自本项目部署 state(首次 deploy 写入)。') +\n 'autopilot-create 非幂等,务必先 list 再建,别盲建(会造重复→重复外发)。',\n sideEffect: false, dingtalkSideEffect: false,\n }\n }\n\n // opencode / local dev / not-yet-supported platforms: no schedule capability.\n const reason = provider === 'opencode'\n ? 'opencode 是本地开发运行时,不承载常驻节律;节律需部署到托管平台'\n : `provider ${provider} 尚未提供 schedule 能力`\n return {\n $schema: 'dingtalk-agent/schedule-plan@2',\n workspace: workspaceName, provider, supported: false, reason,\n count: declared.length, schedules: [],\n // provider 不支持,也照样报 manifest 自身的问题(撞车/未验证/缺判据),和平台支不支持无关。\n ...analysis,\n reconcile: `记为缺口:声明了 ${declared.length} 条节律,但目标 provider 不支持——` +\n '别静默降级成\"那就不定时了\"。',\n sideEffect: false, dingtalkSideEffect: false,\n }\n}\n\nexport interface ScheduleShow {\n $schema: 'dingtalk-agent/schedule-show@2'\n count: number\n schedules: DeclaredSchedule[]\n // 会同一时刻并发 fire 的节律对,如 \"轻拍×补拍\"。外发拍的送达回执是发完才写、没有锁,\n // 同刻并发会重复送第三方,所以这是必须静态拦下的一条。\n firesDistinct: boolean\n collidingPairs: string[]\n // 无法静态确认是否同刻的对(跨时区、或 cron 有本解析器读不懂的扩展语法)。\n // 【绝不当成 distinct】——只是本地静态检查证明不了,要人核或走 provider occurrence 预览。\n unverifiedPairs: string[]\n // 声明了 cron 却没写数据派生完成判据(completion)的节律名——见 SchedulePlan.missingCompletion。\n missingCompletion: string[]\n // predicate 引用问题(阻断):引用未注册 / 注册表模式下仍用自由文本。\n completionRefIssues: string[]\n // 未声明注册表时仍用自由文本判据的节律名(提示迁移,不阻断)。\n unregisteredCompletion: string[]\n sideEffect: false\n dingtalkSideEffect: false\n}\n\n/**\n * Declared schedules + 两条与平台无关、能静态检的不变量:\n * 1) 两拍不得在【同一时刻】并发 fire。按【真实 occurrence】判(见 firesCollide:robfig dom/dow OR、\n * 名字字面量、闰年窗口枚举),不是简化字段交集。判不准的(跨时区/扩展语法)进 unverifiedPairs,不当 distinct。\n * 2) 每条节律都要有数据派生的完成判据(completion),否则只能靠\"那一拍跑没跑\"判完成,漏拍就永久丢一期。\n * ⚠️ 这两条都只看【本地声明的 manifest】,不查平台实际生效状态——线上真相要走 provider 回读。\n */\nexport function buildScheduleShow(start: string, cliVersion = ''): ScheduleShow {\n const info = inspectAgentProject(start, cliVersion)\n const declared = info.project.schedules\n const {\n collidingPairs, unverifiedPairs, missingCompletion,\n completionRefIssues, unregisteredCompletion,\n } = analyzeSchedules(declared, info.project.predicates)\n return {\n $schema: 'dingtalk-agent/schedule-show@2',\n count: declared.length,\n schedules: declared,\n firesDistinct: collidingPairs.length === 0 && unverifiedPairs.length === 0,\n collidingPairs,\n unverifiedPairs,\n missingCompletion,\n completionRefIssues,\n unregisteredCompletion,\n sideEffect: false, dingtalkSideEffect: false,\n }\n}\n"]}
1
+ {"version":3,"file":"schedule-plan.js","sourceRoot":"","sources":["../../src/schedule-plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Schedule reconcile plan — dta's half of the driving contract.\n *\n * dta owns: read the declared schedules (dingtalk-agent.json#schedules), validate\n * them, and map them to a provider-specific reconcile plan. dta does NOT fire or\n * live-write triggers — that stays with the platform ops path (see ARCHITECTURE\n * §3.1, \"触发器不属于 dingtalk-agent 核心执行\"). The plan is the authoritative,\n * provider-mapped desired state the ops skill reconciles against live.\n *\n * Pure: no network, no side effects — safe to run in CI and fully testable.\n */\nimport {\n PREDICATE_ID, inspectAgentProject, multicaAgentIdSource,\n type AgentProjectInfo, type DeclaredPredicate, type DeclaredSchedule,\n} from './development-workspace.js'\nimport { PREDICATE_REGISTRY_PATH, predicateRegistryOwner } from './predicate-registry.js'\n\n/**\n * completion 的 predicate 引用形态(issue #24):`predicate:<id>` 指向 manifest\n * `predicates` 注册表里唯一的判据。声明侧与运行路径(autopilot description 里的\n * `[dta-completion-predicate] <id>` 标记、Receipt)引用同一 ID——两个求值方无法\n * 静默分叉(真事故:两套手写判据分叉 30 小时无报警)。\n */\nexport const COMPLETION_PREDICATE_PREFIX = 'predicate:'\n\n/**\n * completion 的三态解析(issue #39):自由文本 / 合法引用 / malformed 引用。\n * 只要出现 `predicate:` 前缀就已经表达了「我在引用注册表」的意图——id 为空或\n * 格式非法时绝不能降级成 legacy 自由文本(那是 fail-open),必须单独成态去阻断。\n */\nexport function parseCompletionRef(completion?: string):\n | { kind: 'free-text' }\n | { kind: 'ref'; id: string }\n | { kind: 'malformed'; raw: string } {\n const text = (completion || '').trim()\n if (!text.startsWith(COMPLETION_PREDICATE_PREFIX)) return { kind: 'free-text' }\n const id = text.slice(COMPLETION_PREDICATE_PREFIX.length).trim()\n return PREDICATE_ID.test(id) ? { kind: 'ref', id } : { kind: 'malformed', raw: text }\n}\n\nexport function completionPredicateId(completion?: string): string {\n const parsed = parseCompletionRef(completion)\n return parsed.kind === 'ref' ? parsed.id : ''\n}\n\n/**\n * autopilot description 里 predicate 标记的【唯一】parser(issue #40)。\n *\n * 合同:标记是独立一行 `[dta-completion-predicate] <kebab-id>`(写入侧见\n * multicaApplyCommand)。所有回读方——TS 的对账、Python 的运行时解析\n * (multica_ext.py 的 parse_predicate_marker 是同一正则的镜像,改这里必须同步改\n * 那边)——都必须走这个定义,不许各自手写正则:两套 parser 静默分叉正是\n * 注册表要消灭的那类事故。\n *\n * 多枚标记不一致返回 conflict 而不是挑一个:挑选规则(首个/末个)就是第二份\n * 隐式合同,回读方猜错方向时漂移会被静默放过。\n */\nexport const COMPLETION_PREDICATE_MARKER =\n /^\\[dta-completion-predicate\\] ([a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?)$/gm\n\nexport function parseCompletionPredicateMarker(description?: string):\n | { kind: 'none' }\n | { kind: 'id'; id: string }\n | { kind: 'conflict'; ids: string[] }\n | { kind: 'malformed'; raws: string[] } {\n // malformed 独立成态(issue #45,与 #39 同一课):行首出现 marker 前缀就已表达\n // 「我在标注判据」的意图,id 空/非法时折成 none 会静默跳过整个回读校验——fail-open。\n const ids: string[] = []\n const raws: string[] = []\n for (const line of (description || '').split(/\\r?\\n/)) {\n if (!line.startsWith('[dta-completion-predicate]')) continue\n const m = line.match(/^\\[dta-completion-predicate\\] ([a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?)$/)\n if (m) ids.push(m[1])\n else raws.push(line)\n }\n if (raws.length) return { kind: 'malformed', raws }\n if (!ids.length) return { kind: 'none' }\n const unique = [...new Set(ids)]\n return unique.length === 1 ? { kind: 'id', id: unique[0] } : { kind: 'conflict', ids: unique }\n}\n\nexport interface SchedulePlanEntry extends DeclaredSchedule {\n applyCommand: string\n // 平台是否为这一拍记账。work-item 每次 fire 建 issue → 有 token 用量可查;\n // ephemeral(run_only) 不建 issue → 平台不记账,这一拍的成本【看不见】。\n // 改 mode 就是在改计量口径,这里显式标出来,别让它变成隐形盲区。\n metered: boolean\n /** completion 的注册表 predicate id(引用形态且已注册时);运行路径引用同一 ID。 */\n completionPredicate?: string\n /** 运行拍读判据正文的位置:deploy 交付的受管投影(`<skill>/<path>`,issue #46)。 */\n predicateRegistryRef?: string\n}\n\nexport interface SchedulePlan {\n $schema: 'dingtalk-agent/schedule-plan@2'\n workspace: string\n provider: string\n supported: boolean\n reason?: string\n count: number\n /**\n * Agent 绑定来源:manifest 声明的 env 引用(declared-env),或首次 deploy 写入\n * workspace state 的真实 ID(deploy-state)。黄金路径(scaffold 故意省略\n * agentIdFrom、由 deploy 创建 Agent 存 ID)走的是后者。\n */\n agentIdSource?: 'declared-env' | 'deploy-state'\n schedules: SchedulePlanEntry[]\n // 声明了 cron 却没写 completion(数据派生的完成判据)的节律名。\n // 没有完成判据 = 只能靠\"那一拍跑了没有\"来判完成,而平台会漏拍/跳过迟到的 fire,\n // 一次抖动就永久丢一期。这些拍记成缺口,不静默放过。\n missingCompletion: string[]\n // 会同一时刻并发 fire 的节律对 / 无法静态确认是否同刻的对。\n // ⚠️ plan 是 operator 真正照着部署的路径,所以撞车必须在这里也拦——\n // 否则 show 报了撞车、plan 照样吐出 apply 命令,两条都建成 autopilot → 重复外发。\n collidingPairs: string[]\n unverifiedPairs: string[]\n // predicate 引用问题(阻断)与未注册自由文本(提示迁移)——与 show 同源,见 analyzeSchedules。\n completionRefIssues: string[]\n unregisteredCompletion: string[]\n reconcile: string\n sideEffect: false\n dingtalkSideEffect: false\n}\n\n// manifest 级的静态体检,show 和 plan 共用一份,避免两处逻辑漂移。\nexport function analyzeSchedules(\n declared: DeclaredSchedule[],\n predicates?: Record<string, DeclaredPredicate>,\n): {\n collidingPairs: string[]; unverifiedPairs: string[]; missingCompletion: string[]\n completionRefIssues: string[]; unregisteredCompletion: string[]\n} {\n const collidingPairs: string[] = []\n const unverifiedPairs: string[] = []\n for (let i = 0; i < declared.length; i++) {\n for (let j = i + 1; j < declared.length; j++) {\n const pair = `${declared[i].name}×${declared[j].name}`\n // 跨时区:本地无 tz 换算 → 判不准是否同刻 → unverified(不静默当 distinct)。\n if (declared[i].timezone !== declared[j].timezone) { unverifiedPairs.push(pair); continue }\n const r = firesCollide(declared[i].cron, declared[j].cron)\n if (r === 'collide') collidingPairs.push(pair)\n else if (r === 'unverified') unverifiedPairs.push(pair)\n }\n }\n const missingCompletion = declared.filter((s) => !s.completion?.trim()).map((s) => s.name)\n // predicate 引用体检(issue #24):引用必须解析到注册表;一旦项目声明了注册表,\n // 自由文本判据整体阻断——「写了一句话」不是判据,注入一句「跑过了」就能过。\n // 未声明注册表的存量项目维持现状,只列 unregistered 清单提示迁移(渐进门禁)。\n // 「已声明」看的是 manifest 里 predicates 键存不存在,不是注册表空不空:\n // 显式空注册表 = 项目已进入引用模式,自由文本照样阻断(issue #39)。\n const registryDeclared = predicates !== undefined\n const registry = predicates || {}\n const completionRefIssues: string[] = []\n const unregisteredCompletion: string[] = []\n for (const schedule of declared) {\n const completion = (schedule.completion || '').trim()\n if (!completion) continue // missingCompletion 已覆盖\n const parsed = parseCompletionRef(completion)\n if (parsed.kind === 'malformed') {\n // 有 predicate: 前缀但 id 空/非法:引用意图已表达,降级成自由文本就是 fail-open。\n completionRefIssues.push(\n `${schedule.name}: completion 的 predicate 引用格式非法(\"${parsed.raw}\")——` +\n '需要 predicate:<kebab-case-id>')\n } else if (parsed.kind === 'ref') {\n if (!registry[parsed.id]) {\n completionRefIssues.push(`${schedule.name}: completion 引用未注册的 predicate:${parsed.id}`)\n }\n } else if (registryDeclared) {\n completionRefIssues.push(\n `${schedule.name}: 已声明 predicates 注册表,completion 必须是 predicate:<id> 引用` +\n '(自由文本判据可被一句话注入绕过)')\n } else {\n unregisteredCompletion.push(schedule.name)\n }\n }\n return {\n collidingPairs, unverifiedPairs, missingCompletion,\n completionRefIssues, unregisteredCompletion,\n }\n}\n\n/**\n * 阻断部署的问题清单(空 = 可部署)。三类都算阻断,**包括 unverified**。\n *\n * unverified 曾经只告警,理由是\"本地证明不了 ≠ 有问题\"。这条理由错在赌的方向:\n * `0 9 * * *`@Asia/Shanghai 与 `0 1 * * *`@UTC 是**同一时刻**,静态比对看不出来,\n * 于是两条各建一个 autopilot、每天把同一条消息重复发给第三方——正是本守卫要防的那个伤害。\n * 证不出不撞车就当撞车(fail closed):挡住是一次响亮且可恢复的配置改动,放过是一次静默且发出去收不回的重复外发。\n *\n * 出路有两条,都不需要改这里:把两条节律写成同一个 timezone(对齐后走真实 occurrence 比对,\n * 判得出不撞就自动放行),或换掉本地读不懂的 cron 扩展语法。\n *\n * 判据只此一份:CLI 的 show 和 plan 都调它。此前两处各自手写 exit 条件,结果都漏了 unverified——\n * 抽出 analyzeSchedules 是为了防判据分叉,退出判据自己却分叉了。\n */\nexport function scheduleBlockers(a: {\n collidingPairs: string[]; unverifiedPairs: string[]; missingCompletion: string[]\n completionRefIssues?: string[]\n}): string[] {\n return [\n ...a.collidingPairs, ...a.unverifiedPairs, ...a.missingCompletion,\n ...(a.completionRefIssues || []),\n ]\n}\n\n// ephemeral = run-only (no work item); work-item = one work item per fire.\nconst MULTICA_MODE: Record<DeclaredSchedule['mode'], string> = {\n ephemeral: 'run_only',\n 'work-item': 'create_issue',\n}\n\n// 只有 work-item 每次 fire 建 issue、平台才有 token 用量可查。\nconst isMetered = (mode: DeclaredSchedule['mode']): boolean => mode === 'work-item'\n\n// 五个字段的上界(分 时 日 月 周)。\nconst CRON_FIELD_MAX = [59, 23, 31, 12, 7]\n// 名字字面量 → 数字(robfig/cron v3 支持,Multica 实测用 MON-FRI;不支持就会漏掉真双发)。\nconst DOW_NAMES: Record<string, number> = { SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6 }\nconst MON_NAMES: Record<string, number> = {\n JAN: 1, FEB: 2, MAR: 3, APR: 4, MAY: 5, JUN: 6, JUL: 7, AUG: 8, SEP: 9, OCT: 10, NOV: 11, DEC: 12,\n}\n\n/**\n * 把一个 cron 字段展开成它命中的整数集合。支持 `*`、列表、范围、步长,以及 dow/月的名字字面量。\n * 解析不出(`L`/`W`/`#`/`@` 等扩展语法)时返回 null —— 调用方据此判 unverified,不假装读懂了。\n */\nfunction expandCronField(field: string, max: number, names?: Record<string, number>): Set<number> | null {\n const out = new Set<number>()\n const num = (t: string): number => {\n const up = t.toUpperCase()\n if (names && up in names) return names[up]\n return /^\\d+$/.test(t) ? parseInt(t, 10) : NaN\n }\n for (const part of field.split(',')) {\n const [range, stepRaw] = part.split('/')\n const step = stepRaw ? parseInt(stepRaw, 10) : 1\n let lo = 0\n let hi = max\n if (range !== '*') {\n const [a, b] = range.split('-')\n lo = num(a)\n // robfig getRange 的特判:**单个数字 + 步长时上界拉到 max**——`5/15` 等价 `5-59/15` = {5,20,35,50},\n // 不是只有 {5}。当成 {5} 会漏掉 20/35/50,把真撞车判成 distinct → 正是本守卫要防的重复外发。\n hi = b === undefined ? (stepRaw ? max : lo) : num(b)\n }\n if (Number.isNaN(lo) || Number.isNaN(hi) || Number.isNaN(step) || step < 1) return null\n for (let v = lo; v <= hi && v <= max; v += step) out.add(v === 7 && max === 7 ? 0 : v)\n }\n // 空集合 = 越界(`70`)或倒序区间(`5-2`),是【没读懂】不是【永不 fire】。\n // 返回空集会让 setsIntersect 判 false → distinct(fail-open);返回 null → unverified,交给人核。\n return out.size === 0 ? null : out\n}\n\nfunction setsIntersect(a: Set<number>, b: Set<number>): boolean {\n for (const v of a) if (b.has(v)) return true\n return false\n}\n\ninterface ParsedCron { min: Set<number>; hour: Set<number>; dom: Set<number>; mon: Set<number>; dow: Set<number>; domStar: boolean; dowStar: boolean }\n\nfunction parseCron(cron: string): ParsedCron | null {\n const f = cron.trim().split(/\\s+/)\n if (f.length !== 5) return null\n const min = expandCronField(f[0], 59)\n const hour = expandCronField(f[1], 23)\n const dom = expandCronField(f[2], 31)\n const mon = expandCronField(f[3], 12, MON_NAMES)\n const dow = expandCronField(f[4], 7, DOW_NAMES)\n if (!min || !hour || !dom || !mon || !dow) return null\n return { min, hour, dom, mon, dow, domStar: f[2] === '*', dowStar: f[4] === '*' }\n}\n\n// robfig/cron v3 的日匹配:dom 和 dow 都受限时用 OR,否则各自 AND(* 视为全通过)。\nfunction dayMatch(p: ParsedCron, dom: number, dowV: number): boolean {\n if (!p.domStar && !p.dowStar) return p.dom.has(dom) || p.dow.has(dowV)\n return (p.domStar || p.dom.has(dom)) && (p.dowStar || p.dow.has(dowV))\n}\n\nconst isLeap = (y: number): boolean => (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0\nconst DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\nfunction daysInMonth(y: number, m: number): number {\n return m === 2 && isLeap(y) ? 29 : DAYS_IN_MONTH[m - 1]\n}\n\n/**\n * 两条节律会不会在【同一时刻】fire。返回 collide / distinct / unverified。\n * 判据【按真实 occurrence】算,不做简化字段交集——robfig 的 dom/dow 是 OR 语义、支持名字字面量,\n * 简化交集会误判(Multica 用 robfig/cron v3,`0 9 * * MON-FRI` 必须能识别)。\n * - 分/时/月任一集合不相交 → 一定不同刻 → distinct(便宜的早退)。\n * - 否则枚举【完整的 400 年格里高利周期】找同一天两条都 active。为什么必须整 400 年而不是几年窗口:\n * 像 `0 9 29 2 1` 与 `0 9 30 2 3` 的首个共同 fire 落在 2040-02-29,短窗口会漏判成 distinct(假阴性→漏真双发)。\n * 公历 (月,日,星期) 组合每 400 年完整重复,所以枚举一整轮 = 完备,\"整轮无碰撞\"才敢判 distinct。\n * - 任一字段解析不出(扩展语法)→ unverified:**绝不当 distinct**,交给人核。\n * export:给 scripts/check-schedule.mjs 做回归断言。\n */\nexport function firesCollide(cronA: string, cronB: string): 'collide' | 'distinct' | 'unverified' {\n const a = parseCron(cronA)\n const b = parseCron(cronB)\n if (!a || !b) return 'unverified'\n if (!setsIntersect(a.min, b.min) || !setsIntersect(a.hour, b.hour) || !setsIntersect(a.mon, b.mon)) {\n return 'distinct'\n }\n // 整 400 年周期手工走(不 new Date,避免 14 万次分配)。2000-01-01 是周六(dow=6)。\n let dow = 6\n for (let y = 2000; y < 2400; y++) {\n for (let m = 1; m <= 12; m++) {\n const monA = a.mon.has(m)\n const monB = b.mon.has(m)\n const dim = daysInMonth(y, m)\n if (!(monA && monB)) { dow = (dow + dim) % 7; continue } // 该月两条不同时活跃,跳过但推进 dow\n for (let dom = 1; dom <= dim; dom++) {\n if (dayMatch(a, dom, dow) && dayMatch(b, dom, dow)) return 'collide'\n dow = (dow + 1) % 7\n }\n }\n }\n return 'distinct'\n}\n\nfunction shellQuote(value: string): string {\n return `\"${value.replace(/([\"\\\\$`])/g, '\\\\$1')}\"`\n}\n\nfunction envVar(ref: string): string {\n // \"env:MULTICA_FDE_AGENT_ID\" -> \"$MULTICA_FDE_AGENT_ID\"\n return ref.startsWith('env:') ? `$${ref.slice(4)}` : ref\n}\n\n/**\n * 运行拍读注册表投影的指令(issue #46)。\n *\n * 光有 id marker 不构成「运行时读同一份注册表」:那一拍看到的只是一串 id,判据正文不在\n * 它手里。deploy 已把规范化投影作为受管 supporting file 随技能交付,所以唤醒提示词要\n * 明确指出**读哪个文件、解析不出来怎么办**——解析不到判据就没有判据,那一拍不得使用\n * 完成措辞(Basic Behavior 的完成闸门执行这条)。marker 行是机器可读的,好让回读对账\n * 「运行侧到底有没有被告知去哪读」。\n */\nexport function predicateRegistryInstruction(registryRef: string): string {\n return `[dta-predicate-registry] ${registryRef}\\n` +\n '完成判定前先读上面这份随技能交付的注册表投影,按标记的 id 取出判据正文再逐条核对;' +\n '文件缺失、hash 与内容不一致、或该 id 不在投影里,一律不得使用完成措辞——' +\n '判据解析不到就是没有判据,请改报阻塞并说明缺口。'\n}\n\nfunction multicaApplyCommand(\n schedule: DeclaredSchedule, profile: string, agentRef: string, completionPredicate = '',\n registryRef = '',\n): string {\n // 声明与运行路径引用同一 predicate ID:description(唤醒提示词)尾部携带机器可读\n // 标记,Agent/运维在 autopilot 侧看到的判据 ID 与 manifest 注册表一字不差。\n const description = (schedule.wake || schedule.name) + (completionPredicate\n ? `\\n[dta-completion-predicate] ${completionPredicate}` +\n (registryRef ? `\\n${predicateRegistryInstruction(registryRef)}` : '')\n : '')\n const parts = [\n 'python3 \"$MULTICA_EXT\" autopilot-create',\n `--profile ${profile}`,\n `--agent ${agentRef}`,\n `--title ${shellQuote(schedule.name)}`,\n `--mode ${MULTICA_MODE[schedule.mode]}`,\n `--cron ${shellQuote(schedule.cron)}`,\n `--timezone ${schedule.timezone}`,\n `--description ${shellQuote(description)}`,\n ]\n return parts.join(' ')\n}\n\n/**\n * Build the reconcile plan for one workspace's provider. Returns supported:false\n * (with a reason) when the target platform does not offer schedules yet — the\n * declared rhythms are then a recorded gap, never a silent downgrade.\n */\nexport function buildSchedulePlan(info: AgentProjectInfo, workspaceName: string): SchedulePlan {\n const workspace = info.workspaces.find((item) => item.name === workspaceName)\n if (!workspace) throw new Error(`Project 未声明 Workspace: ${workspaceName}`)\n const declared = info.project.schedules\n const provider = workspace.provider.kind\n // 撞车/未验证/缺判据/predicate 引用——manifest 级,与 provider 无关\n const analysis = analyzeSchedules(declared, info.project.predicates)\n\n if (provider === 'multica') {\n const profile = workspace.provider.profile || ''\n if (!profile) {\n throw new Error(`Workspace ${workspaceName} 的 multica provider 缺 profile`)\n }\n // Agent 绑定来源与 deploy 同一份回退链(multicaAgentIdSource):黄金路径下\n // scaffold 故意省略 agentIdFrom、首次 deploy 把真实 ID 写进 state——schedule\n // 此前不读 state,导致部署完的 Agent 声明节律时还被要求手工接线(E-03)。\n const source = multicaAgentIdSource(workspace)\n if (source.kind === 'none') {\n throw new Error(\n `Workspace ${workspaceName} 缺 Agent 绑定:manifest 未声明 provider.agentIdFrom,` +\n `且尚无部署 state(.dingtalk-agent/state/workspaces/${workspaceName}.json)。` +\n `先 dta deploy --workspace ${workspaceName} 创建 Agent(ID 会写入 state 自动复用),` +\n '或在 manifest 显式声明 provider.agentIdFrom')\n }\n const agentRef = source.kind === 'declared' ? envVar(source.ref) : source.agentId\n const agentIdSource = source.kind === 'declared' ? 'declared-env' as const : 'deploy-state' as const\n // 投影的交付位置:deploy 把注册表注入这个 Skill 的 supporting file(同一条归属规则),\n // 唤醒提示词据此告诉运行拍去哪读。注册表没声明 = 没有投影可读,不写这一行。\n const registryOwner = info.project.predicates === undefined\n ? '' : predicateRegistryOwner(info.project.skills)\n const registryRef = registryOwner ? `${registryOwner}/${PREDICATE_REGISTRY_PATH}` : ''\n const schedules = declared.map((schedule) => {\n const predicateId = completionPredicateId(schedule.completion)\n const registered = predicateId && info.project.predicates?.[predicateId] ? predicateId : ''\n return {\n ...schedule,\n applyCommand: multicaApplyCommand(schedule, profile, agentRef, registered, registryRef),\n metered: isMetered(schedule.mode),\n ...(registered ? { completionPredicate: registered } : {}),\n ...(registered && registryRef ? { predicateRegistryRef: registryRef } : {}),\n }\n })\n return {\n $schema: 'dingtalk-agent/schedule-plan@2',\n workspace: workspaceName, provider, supported: true, count: schedules.length,\n agentIdSource, schedules,\n ...analysis,\n reconcile: '由 dta-ops-multica 技能执行:先 autopilot-list 读线上,按 title 匹配,' +\n '只建缺的、改漂移的、删多余的;$MULTICA_EXT 指向 multica_ext.py' +\n (agentIdSource === 'declared-env'\n ? ',$…_AGENT_ID 由受信环境注入。'\n : ';Agent ID 来自本项目部署 state(首次 deploy 写入)。') +\n 'autopilot-create 非幂等,务必先 list 再建,别盲建(会造重复→重复外发)。',\n sideEffect: false, dingtalkSideEffect: false,\n }\n }\n\n // opencode / local dev / not-yet-supported platforms: no schedule capability.\n const reason = provider === 'opencode'\n ? 'opencode 是本地开发运行时,不承载常驻节律;节律需部署到托管平台'\n : `provider ${provider} 尚未提供 schedule 能力`\n return {\n $schema: 'dingtalk-agent/schedule-plan@2',\n workspace: workspaceName, provider, supported: false, reason,\n count: declared.length, schedules: [],\n // provider 不支持,也照样报 manifest 自身的问题(撞车/未验证/缺判据),和平台支不支持无关。\n ...analysis,\n reconcile: `记为缺口:声明了 ${declared.length} 条节律,但目标 provider 不支持——` +\n '别静默降级成\"那就不定时了\"。',\n sideEffect: false, dingtalkSideEffect: false,\n }\n}\n\nexport interface ScheduleShow {\n $schema: 'dingtalk-agent/schedule-show@2'\n count: number\n schedules: DeclaredSchedule[]\n // 会同一时刻并发 fire 的节律对,如 \"轻拍×补拍\"。外发拍的送达回执是发完才写、没有锁,\n // 同刻并发会重复送第三方,所以这是必须静态拦下的一条。\n firesDistinct: boolean\n collidingPairs: string[]\n // 无法静态确认是否同刻的对(跨时区、或 cron 有本解析器读不懂的扩展语法)。\n // 【绝不当成 distinct】——只是本地静态检查证明不了,要人核或走 provider occurrence 预览。\n unverifiedPairs: string[]\n // 声明了 cron 却没写数据派生完成判据(completion)的节律名——见 SchedulePlan.missingCompletion。\n missingCompletion: string[]\n // predicate 引用问题(阻断):引用未注册 / 注册表模式下仍用自由文本。\n completionRefIssues: string[]\n // 未声明注册表时仍用自由文本判据的节律名(提示迁移,不阻断)。\n unregisteredCompletion: string[]\n sideEffect: false\n dingtalkSideEffect: false\n}\n\n/**\n * Declared schedules + 两条与平台无关、能静态检的不变量:\n * 1) 两拍不得在【同一时刻】并发 fire。按【真实 occurrence】判(见 firesCollide:robfig dom/dow OR、\n * 名字字面量、闰年窗口枚举),不是简化字段交集。判不准的(跨时区/扩展语法)进 unverifiedPairs,不当 distinct。\n * 2) 每条节律都要有数据派生的完成判据(completion),否则只能靠\"那一拍跑没跑\"判完成,漏拍就永久丢一期。\n * ⚠️ 这两条都只看【本地声明的 manifest】,不查平台实际生效状态——线上真相要走 provider 回读。\n */\nexport function buildScheduleShow(start: string, cliVersion = ''): ScheduleShow {\n const info = inspectAgentProject(start, cliVersion)\n const declared = info.project.schedules\n const {\n collidingPairs, unverifiedPairs, missingCompletion,\n completionRefIssues, unregisteredCompletion,\n } = analyzeSchedules(declared, info.project.predicates)\n return {\n $schema: 'dingtalk-agent/schedule-show@2',\n count: declared.length,\n schedules: declared,\n firesDistinct: collidingPairs.length === 0 && unverifiedPairs.length === 0,\n collidingPairs,\n unverifiedPairs,\n missingCompletion,\n completionRefIssues,\n unregisteredCompletion,\n sideEffect: false, dingtalkSideEffect: false,\n }\n}\n"]}
@@ -0,0 +1,120 @@
1
+ import { existsSync, lstatSync, realpathSync } from 'node:fs';
2
+ import { dirname, join, relative, resolve } from 'node:path';
3
+ /**
4
+ * Agent 私有状态目录(stateDir)的**唯一**合同(issue #47 / #52)。
5
+ *
6
+ * 宿主状态目录是 no-op Receipt、Candidate、checkpoint 这些「真实性靠部署事实、
7
+ * 不靠密码学」的痕迹的落点:#42 把真实性边界定义成「state 目录归宿主所有、
8
+ * Prepared Run 的沙箱写不进来」。词法 containment 证明不了这件事——
9
+ * `resolve()` 只比较字符串,Agent root 内一个指向 root 外的目录 symlink 能让检查
10
+ * 通过,随后 `mkdirSync` / 原子写沿 symlink 把痕迹写到 root 之外(issue #52)。
11
+ *
12
+ * 所以合同有三条,词法与 realpath 都要过:
13
+ *
14
+ * 1. `resolve(root, stateDir)` 必须落在 Agent root 内(`../outside` 拒绝);
15
+ * 2. 不能等于 root 本身——stateDir 是 root 内的**独立**私有目录;
16
+ * 3. 已存在的部分(含中间目录、含 stateDir 自身)的 realpath 仍必须落在 root 内;
17
+ * 尚不存在的路径按「最近存在的祖先」判定,之后由调用方创建成真实目录,
18
+ * 并在创建后回读复验(`assertRealpathInsideRoot`)。
19
+ *
20
+ * 校验点只放在这里:agent-bindings 的 bindings 加载、memory 的 Prepared 写入路径
21
+ * 共用同一份判据,避免「一处放宽、一处收紧」再次分叉。
22
+ */
23
+ /** 词法 + realpath 双过的 stateDir 解析;返回可安全创建/写入的绝对路径。 */
24
+ export function resolvePrivateStateDir(root, stateDir, label = 'stateDir') {
25
+ const rootReal = canonicalRoot(root);
26
+ const target = resolve(rootReal, stateDir);
27
+ if (!isInside(rootReal, target)) {
28
+ throw new Error(`${label} 必须位于 Agent 根目录内: ${stateDir}`);
29
+ }
30
+ if (target === rootReal) {
31
+ throw new Error(`${label} 必须是 Agent 根目录内的独立私有目录,不能等于根目录: ${stateDir}`);
32
+ }
33
+ assertRealpathInsideRoot(rootReal, target, label);
34
+ return target;
35
+ }
36
+ /**
37
+ * 已存在部分的 realpath 必须留在 root 内。目录尚不存在时判定它最近存在的祖先——
38
+ * 剩余段将由 `mkdirSync` 创建成真实目录,祖先在内则整条路径在内。
39
+ * 创建之后应再调用一次做回读复验(覆盖 stateDir 之下的嵌套 symlink 与竞态)。
40
+ */
41
+ export function assertRealpathInsideRoot(root, target, label) {
42
+ const declaredRoot = resolve(root);
43
+ const rootReal = canonicalRoot(root);
44
+ const absolute = resolve(target);
45
+ const anchor = nearestExistingEntry(absolute);
46
+ // anchor 词法上严格位于 root 之上 = root 自身还没建:那么 anchor 之下的每一段(含 root)
47
+ // 都将由 mkdirSync 创建成真实目录,没有可跟随的 symlink,也就无从逃逸。
48
+ // 判据必须看 anchor 的**词法**位置,不能看 realpath 方向——否则 root 内一个指向 root
49
+ // 父目录的 symlink 会被当成「祖先方向的 containment」放过去。
50
+ if (anchor !== declaredRoot && isInside(anchor, declaredRoot))
51
+ return;
52
+ let real;
53
+ try {
54
+ real = realpathSync(anchor);
55
+ }
56
+ catch {
57
+ // 悬挂 symlink:lstat 有条目但 realpath 解析不出来,归宿无法证明 → fail-closed
58
+ throw new Error(`${label} 真实路径无法解析(悬挂符号链接?): ${target}`);
59
+ }
60
+ if (!isInside(rootReal, real)) {
61
+ throw new Error(`${label} 真实路径必须位于 Agent 根目录内(symlink 越界): ${target}`);
62
+ }
63
+ }
64
+ /**
65
+ * 「声称在 root 内」的路径必须真的在 root 内。词法在内、realpath 出界就是 symlink 逃逸,
66
+ * 一律拒绝;明确指向 root 之外的配置(例如绝对路径的 DTA_STATE_DIR)不在本合同内,
67
+ * 交给各自的 prepared 门禁判——读路径因此只收紧「被 symlink 骗」这一种情况:
68
+ * 逃逸后的 trace 会从调用方自己能写的目录读出 ran-empty,那正是运维要区分的判定。
69
+ */
70
+ export function assertNoSymlinkEscape(root, target, label) {
71
+ // 词法比较用调用方给的 root 原样(target 由同一个字符串拼出):拿 realpath 化的 root 去比,
72
+ // 会把 /var → /private/var 这类 root 自身的前缀 symlink 误判成「明确指向 root 外」。
73
+ if (!isInside(resolve(root), resolve(target)))
74
+ return;
75
+ assertRealpathInsideRoot(root, target, label);
76
+ }
77
+ /**
78
+ * Agent root 的 canonical 形式。root 尚不存在时(首次写状态就顺带建 root 的调用方)
79
+ * 不能让 realpathSync 抛 ENOENT——那会把「目录还没建」伪装成边界错误。做法是把最近存在
80
+ * 祖先的 realpath 与剩余词法段拼回去:存在的部分解析 symlink,不存在的部分保持词法。
81
+ */
82
+ function canonicalRoot(root) {
83
+ const absolute = resolve(root);
84
+ const anchor = nearestExistingEntry(absolute);
85
+ try {
86
+ const real = realpathSync(anchor);
87
+ return anchor === absolute ? real : join(real, relative(anchor, absolute));
88
+ }
89
+ catch {
90
+ return absolute;
91
+ }
92
+ }
93
+ /** lstat 语义的存在判定:悬挂 symlink 也算「有条目」,不能被 existsSync 漏成不存在。 */
94
+ function nearestExistingEntry(target) {
95
+ let current = target;
96
+ while (!hasEntry(current)) {
97
+ const parent = dirname(current);
98
+ if (parent === current)
99
+ return current;
100
+ current = parent;
101
+ }
102
+ return current;
103
+ }
104
+ function hasEntry(path) {
105
+ if (existsSync(path))
106
+ return true;
107
+ try {
108
+ lstatSync(path);
109
+ return true;
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ }
115
+ function isInside(root, candidate) {
116
+ const rel = relative(root, candidate);
117
+ return rel === '' ||
118
+ (rel !== '..' && !rel.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`));
119
+ }
120
+ //# sourceMappingURL=state-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-dir.js","sourceRoot":"","sources":["../../src/state-dir.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, lstatSync, realpathSync } from 'node:fs'\nimport { dirname, join, relative, resolve } from 'node:path'\n\n/**\n * Agent 私有状态目录(stateDir)的**唯一**合同(issue #47 / #52)。\n *\n * 宿主状态目录是 no-op Receipt、Candidate、checkpoint 这些「真实性靠部署事实、\n * 不靠密码学」的痕迹的落点:#42 把真实性边界定义成「state 目录归宿主所有、\n * Prepared Run 的沙箱写不进来」。词法 containment 证明不了这件事——\n * `resolve()` 只比较字符串,Agent root 内一个指向 root 外的目录 symlink 能让检查\n * 通过,随后 `mkdirSync` / 原子写沿 symlink 把痕迹写到 root 之外(issue #52)。\n *\n * 所以合同有三条,词法与 realpath 都要过:\n *\n * 1. `resolve(root, stateDir)` 必须落在 Agent root 内(`../outside` 拒绝);\n * 2. 不能等于 root 本身——stateDir 是 root 内的**独立**私有目录;\n * 3. 已存在的部分(含中间目录、含 stateDir 自身)的 realpath 仍必须落在 root 内;\n * 尚不存在的路径按「最近存在的祖先」判定,之后由调用方创建成真实目录,\n * 并在创建后回读复验(`assertRealpathInsideRoot`)。\n *\n * 校验点只放在这里:agent-bindings 的 bindings 加载、memory 的 Prepared 写入路径\n * 共用同一份判据,避免「一处放宽、一处收紧」再次分叉。\n */\n\n/** 词法 + realpath 双过的 stateDir 解析;返回可安全创建/写入的绝对路径。 */\nexport function resolvePrivateStateDir(root: string, stateDir: string, label = 'stateDir'): string {\n const rootReal = canonicalRoot(root)\n const target = resolve(rootReal, stateDir)\n if (!isInside(rootReal, target)) {\n throw new Error(`${label} 必须位于 Agent 根目录内: ${stateDir}`)\n }\n if (target === rootReal) {\n throw new Error(`${label} 必须是 Agent 根目录内的独立私有目录,不能等于根目录: ${stateDir}`)\n }\n assertRealpathInsideRoot(rootReal, target, label)\n return target\n}\n\n/**\n * 已存在部分的 realpath 必须留在 root 内。目录尚不存在时判定它最近存在的祖先——\n * 剩余段将由 `mkdirSync` 创建成真实目录,祖先在内则整条路径在内。\n * 创建之后应再调用一次做回读复验(覆盖 stateDir 之下的嵌套 symlink 与竞态)。\n */\nexport function assertRealpathInsideRoot(root: string, target: string, label: string): void {\n const declaredRoot = resolve(root)\n const rootReal = canonicalRoot(root)\n const absolute = resolve(target)\n const anchor = nearestExistingEntry(absolute)\n // anchor 词法上严格位于 root 之上 = root 自身还没建:那么 anchor 之下的每一段(含 root)\n // 都将由 mkdirSync 创建成真实目录,没有可跟随的 symlink,也就无从逃逸。\n // 判据必须看 anchor 的**词法**位置,不能看 realpath 方向——否则 root 内一个指向 root\n // 父目录的 symlink 会被当成「祖先方向的 containment」放过去。\n if (anchor !== declaredRoot && isInside(anchor, declaredRoot)) return\n let real: string\n try {\n real = realpathSync(anchor)\n } catch {\n // 悬挂 symlink:lstat 有条目但 realpath 解析不出来,归宿无法证明 → fail-closed\n throw new Error(`${label} 真实路径无法解析(悬挂符号链接?): ${target}`)\n }\n if (!isInside(rootReal, real)) {\n throw new Error(`${label} 真实路径必须位于 Agent 根目录内(symlink 越界): ${target}`)\n }\n}\n\n/**\n * 「声称在 root 内」的路径必须真的在 root 内。词法在内、realpath 出界就是 symlink 逃逸,\n * 一律拒绝;明确指向 root 之外的配置(例如绝对路径的 DTA_STATE_DIR)不在本合同内,\n * 交给各自的 prepared 门禁判——读路径因此只收紧「被 symlink 骗」这一种情况:\n * 逃逸后的 trace 会从调用方自己能写的目录读出 ran-empty,那正是运维要区分的判定。\n */\nexport function assertNoSymlinkEscape(root: string, target: string, label: string): void {\n // 词法比较用调用方给的 root 原样(target 由同一个字符串拼出):拿 realpath 化的 root 去比,\n // 会把 /var → /private/var 这类 root 自身的前缀 symlink 误判成「明确指向 root 外」。\n if (!isInside(resolve(root), resolve(target))) return\n assertRealpathInsideRoot(root, target, label)\n}\n\n/**\n * Agent root 的 canonical 形式。root 尚不存在时(首次写状态就顺带建 root 的调用方)\n * 不能让 realpathSync 抛 ENOENT——那会把「目录还没建」伪装成边界错误。做法是把最近存在\n * 祖先的 realpath 与剩余词法段拼回去:存在的部分解析 symlink,不存在的部分保持词法。\n */\nfunction canonicalRoot(root: string): string {\n const absolute = resolve(root)\n const anchor = nearestExistingEntry(absolute)\n try {\n const real = realpathSync(anchor)\n return anchor === absolute ? real : join(real, relative(anchor, absolute))\n } catch {\n return absolute\n }\n}\n\n/** lstat 语义的存在判定:悬挂 symlink 也算「有条目」,不能被 existsSync 漏成不存在。 */\nfunction nearestExistingEntry(target: string): string {\n let current = target\n while (!hasEntry(current)) {\n const parent = dirname(current)\n if (parent === current) return current\n current = parent\n }\n return current\n}\n\nfunction hasEntry(path: string): boolean {\n if (existsSync(path)) return true\n try {\n lstatSync(path)\n return true\n } catch {\n return false\n }\n}\n\nfunction isInside(root: string, candidate: string): boolean {\n const rel = relative(root, candidate)\n return rel === '' ||\n (rel !== '..' && !rel.startsWith(`..${process.platform === 'win32' ? '\\\\' : '/'}`))\n}\n"]}
@@ -4,7 +4,7 @@
4
4
  "title": "dingtalk-agent Multica Deployment Plan",
5
5
  "type": "object",
6
6
  "additionalProperties": false,
7
- "required": ["$schema", "planId", "action", "workspace", "provider", "profile", "inspectionId", "desiredHash", "deploymentHash", "expected", "operations", "writeBudget", "blocking", "requiresPlanId", "requiresExplicitYes", "remoteRead", "remoteWrite", "triggerWrite", "sideEffect", "dingtalkSideEffect"],
7
+ "required": ["$schema", "planId", "action", "workspace", "provider", "profile", "inspectionId", "desiredHash", "deploymentHash", "predicateRegistry", "expected", "operations", "writeBudget", "blocking", "requiresPlanId", "requiresExplicitYes", "remoteRead", "remoteWrite", "triggerWrite", "sideEffect", "dingtalkSideEffect"],
8
8
  "properties": {
9
9
  "$schema": { "const": "dingtalk-agent/multica-deployment-plan@1" },
10
10
  "planId": { "$ref": "#/$defs/id" },
@@ -15,6 +15,19 @@
15
15
  "inspectionId": { "type": "string" },
16
16
  "desiredHash": { "$ref": "#/$defs/hash" },
17
17
  "deploymentHash": { "$ref": "#/$defs/hash" },
18
+ "predicateRegistry": {
19
+ "type": ["object", "null"],
20
+ "description": "Managed predicate-registry projection delivered with the skill tree (issue #46); null when the project declares no registry.",
21
+ "additionalProperties": false,
22
+ "required": ["skill", "path", "hash", "ids", "schedules"],
23
+ "properties": {
24
+ "skill": { "$ref": "#/$defs/name" },
25
+ "path": { "const": "references/predicate-registry.json" },
26
+ "hash": { "$ref": "#/$defs/hash" },
27
+ "ids": { "type": "array", "items": { "type": "string" } },
28
+ "schedules": { "type": "array", "items": { "type": "string" } }
29
+ }
30
+ },
18
31
  "expected": {
19
32
  "type": "object",
20
33
  "additionalProperties": false,
@@ -4,7 +4,7 @@
4
4
  "title": "dingtalk-agent Multica Deployment Receipt",
5
5
  "type": "object",
6
6
  "additionalProperties": false,
7
- "required": ["$schema", "receiptId", "operationId", "planId", "action", "workspace", "provider", "profile", "status", "passed", "providerReady", "idempotent", "inspectionId", "inspectionObservedHash", "inspectionEvidencePath", "desiredHash", "deploymentHash", "observedHash", "agent", "skills", "assignmentHash", "calls", "smoke", "rollback", "failures", "evidencePath", "remoteRead", "remoteWrite", "triggerWrite", "sideEffect", "dingtalkSideEffect"],
7
+ "required": ["$schema", "receiptId", "operationId", "planId", "action", "workspace", "provider", "profile", "status", "passed", "providerReady", "idempotent", "inspectionId", "inspectionObservedHash", "inspectionEvidencePath", "desiredHash", "deploymentHash", "predicateRegistry", "observedHash", "agent", "skills", "assignmentHash", "calls", "smoke", "rollback", "failures", "evidencePath", "remoteRead", "remoteWrite", "triggerWrite", "sideEffect", "dingtalkSideEffect"],
8
8
  "properties": {
9
9
  "$schema": { "const": "dingtalk-agent/multica-deployment-receipt@1" },
10
10
  "receiptId": { "$ref": "#/$defs/id" },
@@ -23,6 +23,19 @@
23
23
  "inspectionEvidencePath": { "type": "string" },
24
24
  "desiredHash": { "$ref": "#/$defs/hash" },
25
25
  "deploymentHash": { "$ref": "#/$defs/hash" },
26
+ "predicateRegistry": {
27
+ "type": ["object", "null"],
28
+ "description": "Managed predicate-registry projection delivered with the skill tree (issue #46); null when the project declares no registry.",
29
+ "additionalProperties": false,
30
+ "required": ["skill", "path", "hash", "ids", "schedules"],
31
+ "properties": {
32
+ "skill": { "$ref": "#/$defs/name" },
33
+ "path": { "const": "references/predicate-registry.json" },
34
+ "hash": { "$ref": "#/$defs/hash" },
35
+ "ids": { "type": "array", "items": { "type": "string" } },
36
+ "schedules": { "type": "array", "items": { "type": "string" } }
37
+ }
38
+ },
26
39
  "observedHash": { "type": "string" },
27
40
  "agent": {
28
41
  "type": "object", "additionalProperties": false,
@@ -12,7 +12,7 @@
12
12
  "capability": "basic-skill",
13
13
  "prompt": "这是基础行为加载探针。只依据已经进入当前上下文的正文,严格输出 skill=<name>@<metadata.version>;如果正文不可见,只输出 skill=not-loaded。禁止猜测,不要补充解释。",
14
14
  "expectations": [
15
- { "id": "exact-basic-version", "type": "equals", "value": "skill=dta-basic-behavior@0.14.1" }
15
+ { "id": "exact-basic-version", "type": "equals", "value": "skill=dta-basic-behavior@0.15.0" }
16
16
  ],
17
17
  "manualChecks": []
18
18
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdxer/dingtalk-agent",
3
- "version": "0.1.5-beta.10",
3
+ "version": "0.1.5-beta.12",
4
4
  "description": "把 Agent 装配成钉钉数字员工:Skill 决策,CLI 固定事务边界,Agent 本体运行在哪个 Managed Agent Platform 上是可替换的。",
5
5
  "keywords": [
6
6
  "dingtalk",
@@ -3,7 +3,7 @@ name: dta-agent-compose
3
3
  description: 当用户要创建、新建、装配一个 Agent 或钉钉数字员工——包括把 GitHub 仓库、本地文件夹或钉钉文档定义成 Agent,或要审计、补齐、优化 Agent 的 AGENTS.md、本体职责、岗位 Skills、记忆/知识/产物存储、DWS 权限绑定与「谁叫醒它」的驱动节律时使用。即使尚未 init Workspace,也按 dingtalk-agent 的 AgentDefinition 范式给出可运行的最小装配方案;声明驱动节律但不实现触发器本身。
4
4
  compatibility: Requires dingtalk-agent on PATH; remote DingTalk documents require authenticated dws.
5
5
  metadata:
6
- version: "0.16.3"
6
+ version: "0.16.5"
7
7
  ---
8
8
 
9
9
  # 装配一个可工作的钉钉数字员工 Agent
@@ -108,7 +108,9 @@ Skill 里仍然写死通用红线(如"默认只发主人单聊;发群必须
108
108
 
109
109
  **回读校验(fail-closed)**:`multica_ext.py autopilot-create` 在创建后独立 GET 回读、比对远端 description 的标记与声明——create 响应可能只是请求回显,只有回读算数;标记丢失/被改写则非零退出并输出 `predicateRef.verified:false`,不把部署当成功。`autopilot-list/get` 会附上解析出的 `predicateRef`,对账时漂移可见。
110
110
 
111
- **跨语言合同**:注册表就在 `dingtalk-agent.json` 本体里,`multica_ext.py predicate-resolve --manifest dingtalk-agent.json(--id | --description)` 在**同一份 JSON** 里取判据正文,不另建第二份判据源;未注册 id / malformed / 冲突 marker 非零退出。⚠️ **运行时可达性尚未接线**:skill-push 不携带项目 manifest、目标 runtime 也不保证有 resolver,`predicate-resolve` 目前是 **operator 侧**对本地 checkout 使用的工具——受管的 runtime 注册表交付(绑定部署 hash)由 issue #46 跟踪,是本仓待办;run 级结构化 predicateRef 字段与平台回执通道则是上游缺口(MUL-011),两者不混。
111
+ **跨语言合同**:注册表就在 `dingtalk-agent.json` 本体里,`multica_ext.py predicate-resolve --manifest dingtalk-agent.json(--id | --description)` 在**同一份 JSON** 里取判据正文,不另建第二份判据源;未注册 id / malformed / 冲突 marker 非零退出。
112
+
113
+ **运行时可达性(issue #46 已接线)**:manifest 不随 skill-push 进入 runtime,所以判据正文靠**受管 runtime artifact** 到达运行拍——`dta deploy` 把注册表投影成 `predicate-registry@1`(`src/predicate-registry.ts` 是唯一生成与解析入口),作为归属 Skill 的 supporting file 写在 `<owner-skill>/references/predicate-registry.json`(owner 默认 `dta-basic-behavior`,否则受管集合里字典序第一),随既有 skill-push 链路交付:投影 hash 进 Skill tree hash → deployment hash → 部署 Receipt 的 `predicateRegistry`,漂移用现有 skill hash 回读即可发现。唤醒提示词除 id marker 外再带一行 `[dta-predicate-registry] <skill>/references/predicate-registry.json` 与「解析不到判据不得使用完成措辞」的指令,运行拍据此读同一份投影,不需要 manifest、resolver 脚本或平台凭据。投影被改一个字(hash 与内容不符)、id 未交付、文件缺失,在任何读方都 fail-closed。run 级结构化 predicateRef 字段与平台回执通道仍是上游缺口(MUL-011),与本条不混。
112
114
 
113
115
  **渐进门禁**(`dta schedule show` / `plan` 判定):引用未注册的 id → 阻断;一旦声明了 `predicates` 注册表(**包括显式空对象 `{}`——那是「已进入引用模式」,不是未声明**),自由文本 `completion` 也阻断(必须全量引用);`predicate:` 前缀后 id 为空或非法按 malformed 引用阻断,绝不降级成自由文本;没声明注册表的存量项目自由文本不阻断,只在 `unregisteredCompletion` 里提示迁移。
114
116
 
@@ -3,7 +3,7 @@ name: dta-basic-behavior
3
3
  description: 当 Agent 作为钉钉里的真实员工处理消息、@、单聊、群聊、任务、确认、记忆或协作事项时使用;即使当前目录没有初始化 Workspace、没有 CONTEXT.md,也先用本 Skill 判断响应资格、意图、作用域、风险、授权、隐私与完成状态。Prepared Run 中只通过 dingtalk-agent 原子动作,普通会话的钉钉产品操作按需使用 dws。
4
4
  compatibility: Requires dingtalk-agent on PATH; DingTalk side effects require dws.
5
5
  metadata:
6
- version: "0.14.1"
6
+ version: "0.15.0"
7
7
  ---
8
8
 
9
9
  # 钉钉数字员工基础行为
@@ -90,7 +90,12 @@ goal / deliverable / scope / done_when / constraints / authority / timing
90
90
  2. 状态已经进入 `verifying`,不能从 `working` 直接跳到 `completed`;
91
91
  3. 每条 `doneWhen` 都有可定位证据,证据必须指向产物、回读、Receipt 或确认,而不是“模型说已完成”;
92
92
  4. `waitingFor = null`、`nextAction = ""`,没有被总分掩盖的硬失败;
93
- 5. 最终 `checkpoint` 说明实际完成到哪里,随后才写 `completed` 并回复 origin。
93
+ 5. 唤醒提示词带 `[dta-completion-predicate] <id>` 时,判据正文只从同一条提示词指出的
94
+ `[dta-predicate-registry] <skill>/references/predicate-registry.json` 里读——那是随技能
95
+ 交付的受管投影,声明侧与运行侧同一份。读不到文件、投影 hash 与内容不符、或该 id 不在
96
+ 投影里,都**不得使用完成措辞**:判据解析不到就是没有判据,按 `blocked` 报缺口,
97
+ 不要凭记忆或正文里的一句话替代它;
98
+ 6. 最终 `checkpoint` 说明实际完成到哪里,随后才写 `completed` 并回复 origin。
94
99
 
95
100
  闸门失败时,保留 `working/verifying/waiting/blocked` 中准确的一种状态,并告诉同事真实进度。准确的“待核验”比虚假的“已完成”更有用。
96
101
 
@@ -2,7 +2,7 @@
2
2
  name: dta-ops-multica
3
3
  description: External Python wrapper defining the full developer chain for delivering an agent on the Multica (DingTalk-FDE fork) platform — workspace bootstrap, runtime/agent provisioning, skill assembly (push/pull), DingTalk robot & account binding, execution-trace observability, cron scheduling (autopilot), and health checks. Use when provisioning, operating, debugging, or observing a Multica-managed agent from the command line with only an endpoint and a token.
4
4
  metadata:
5
- version: "0.4.1"
5
+ version: "0.5.0"
6
6
  ---
7
7
 
8
8
  # Multica FDE External Skill
@@ -173,26 +173,34 @@ the declared one — the create response may merely echo the request, so only th
173
173
  readback counts. Missing or rewritten marker → non-zero exit with
174
174
  `predicateRef: {declared, remote, verified:false, reason}`; do not treat that
175
175
  deploy as done (pause the autopilot if in doubt). `autopilot-list`/`autopilot-get`
176
- annotate each object with its parsed `predicateRef` so reconcile sees drift. At
177
- runtime, resolve the predicate from the SAME manifest the declaration lives in —
178
- never from a second hand-written copy:
176
+ annotate each object with its parsed `predicateRef` so reconcile sees drift.
177
+
178
+ The predicate body reaches a run through a **managed runtime artifact** (issue
179
+ #46): `dta deploy` projects the manifest registry into
180
+ `<owner-skill>/references/predicate-registry.json` (owner defaults to
181
+ `dta-basic-behavior`) and pushes it with the skill tree, so the projection hash
182
+ enters the skill tree hash → deployment hash → Receipt (`predicateRegistry`).
183
+ The run reads that file — no manifest, no `multica_ext.py`, no credentials — and
184
+ the wake description carries both the id marker and a
185
+ `[dta-predicate-registry] <skill>/references/predicate-registry.json` line
186
+ instructing it to resolve before using completion wording.
187
+
188
+ Resolve either side of the same registry — the delivered projection (what a run
189
+ actually holds) or the declaring manifest (operator-side, local checkout):
179
190
 
180
191
  ```bash
181
- python3 $PY predicate-resolve --manifest dingtalk-agent.json \
182
- --description "<the run's autopilot description>" # parses the marker, resolves the registry
183
- # or --id <predicate-id> to skip marker parsing. Unregistered id / conflicting
184
- # markers exit non-zero: if the predicate can't be resolved, this run cannot
185
- # claim it knows how completion is judged.
192
+ python3 $PY predicate-resolve --registry <skill-dir>/references/predicate-registry.json \
193
+ --description "<the run's autopilot description>" # runtime side: schema + hash verified
194
+ python3 $PY predicate-resolve --manifest dingtalk-agent.json --id <predicate-id>
195
+ # Unregistered id / conflicting markers / a projection whose hash does not match
196
+ # its content all exit non-zero: if the predicate can't be resolved, this run
197
+ # cannot claim it knows how completion is judged.
186
198
  ```
187
199
 
188
- ⚠️ Runtime reachability is NOT wired yet (issue #46): `skill-push` uploads only
189
- the skill directory the project manifest does NOT travel with it, the target
190
- runtime is not guaranteed to have `multica_ext.py`, and nothing invokes
191
- `predicate-resolve` per schedule run. Until a managed registry artifact (bound
192
- to the deploy hash) lands, `predicate-resolve` is an OPERATOR-side tool run
193
- against the local project checkout. Separately, platform-side structured
194
- predicateRef fields and per-run receipts do not exist — that upstream gap is
195
- ledgered as MUL-011 in `docs/UPSTREAM-REQUIREMENTS.md`.
200
+ Drift is visible through the normal skill hash readback: edit the delivered
201
+ projection on the platform and the next deploy readback mismatches. Separately,
202
+ platform-side structured predicateRef fields and per-run receipts do not exist —
203
+ that upstream gap is ledgered as MUL-011 in `docs/UPSTREAM-REQUIREMENTS.md`.
196
204
 
197
205
  Manual/ad-hoc autopilots use the same commands directly:
198
206