@evomap/evolver-core 2.0.0-beta.2 → 2.0.0-beta.22

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.
Files changed (240) hide show
  1. package/assets/gep/genes.jsonl +5 -5
  2. package/dist/algo/bans.d.ts +10 -1
  3. package/dist/algo/bans.js +56 -6
  4. package/dist/algo/candidateAssembly.d.ts +25 -2
  5. package/dist/algo/candidateAssembly.js +171 -26
  6. package/dist/algo/capabilityCandidates.js +10 -0
  7. package/dist/algo/conversationSniffer.js +25 -8
  8. package/dist/algo/cycleEngine.d.ts +63 -5
  9. package/dist/algo/cycleEngine.js +320 -42
  10. package/dist/algo/cycleFailureClassifier.d.ts +1 -1
  11. package/dist/algo/evolutionEvent.d.ts +26 -0
  12. package/dist/algo/evolutionEvent.js +32 -0
  13. package/dist/algo/exploration.d.ts +7 -0
  14. package/dist/algo/exploration.js +16 -3
  15. package/dist/algo/geneHealth.d.ts +36 -3
  16. package/dist/algo/geneHealth.js +47 -4
  17. package/dist/algo/geneIntake.d.ts +46 -1
  18. package/dist/algo/geneIntake.js +121 -10
  19. package/dist/algo/geneSelection.d.ts +126 -6
  20. package/dist/algo/geneSelection.js +479 -36
  21. package/dist/algo/index.d.ts +6 -1
  22. package/dist/algo/index.js +6 -1
  23. package/dist/algo/kautoProjection.d.ts +41 -0
  24. package/dist/algo/kautoProjection.js +95 -0
  25. package/dist/algo/kautoValidator.d.ts +68 -0
  26. package/dist/algo/kautoValidator.js +256 -0
  27. package/dist/algo/memoryGraph.d.ts +62 -0
  28. package/dist/algo/memoryGraph.js +86 -0
  29. package/dist/algo/orchestrator.d.ts +17 -1
  30. package/dist/algo/orchestrator.js +30 -4
  31. package/dist/algo/publishEligibility.d.ts +34 -0
  32. package/dist/algo/publishEligibility.js +52 -0
  33. package/dist/algo/solidify.d.ts +11 -2
  34. package/dist/algo/solidify.js +37 -7
  35. package/dist/algo/ucb1.d.ts +53 -0
  36. package/dist/algo/ucb1.js +156 -0
  37. package/dist/assetrepair/hubRejection.d.ts +12 -0
  38. package/dist/assetrepair/hubRejection.js +109 -0
  39. package/dist/assetrepair/index.d.ts +2 -0
  40. package/dist/assetrepair/index.js +2 -0
  41. package/dist/assetrepair/repair.d.ts +33 -0
  42. package/dist/assetrepair/repair.js +155 -0
  43. package/dist/assetstore/assetSidecarRecords.d.ts +28 -0
  44. package/dist/assetstore/assetSidecarRecords.js +384 -0
  45. package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
  46. package/dist/assetstore/assetSidecarRecovery.js +288 -0
  47. package/dist/assetstore/assetStoreHealth.d.ts +81 -0
  48. package/dist/assetstore/assetStoreHealth.js +319 -0
  49. package/dist/assetstore/assetStoreLayout.d.ts +2 -0
  50. package/dist/assetstore/assetStoreLayout.js +6 -0
  51. package/dist/assetstore/assetStoreStorage.d.ts +42 -0
  52. package/dist/assetstore/assetStoreStorage.js +336 -0
  53. package/dist/assetstore/assetSyncLedger.d.ts +91 -1
  54. package/dist/assetstore/assetSyncLedger.js +718 -59
  55. package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
  56. package/dist/assetstore/foreignJsonlSource.js +150 -0
  57. package/dist/assetstore/index.d.ts +5 -0
  58. package/dist/assetstore/index.js +5 -0
  59. package/dist/assetstore/learningHistory.js +3 -3
  60. package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
  61. package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
  62. package/dist/assetstore/localJsonl.d.ts +12 -1
  63. package/dist/assetstore/localJsonl.js +251 -42
  64. package/dist/assetstore/provenance.d.ts +93 -4
  65. package/dist/assetstore/provenance.js +368 -84
  66. package/dist/assetstore/provider.d.ts +63 -0
  67. package/dist/assetstore/provider.js +97 -6
  68. package/dist/assetstore/reviewFilter.d.ts +19 -1
  69. package/dist/assetstore/reviewFilter.js +39 -1
  70. package/dist/assetstore/reviewLedger.d.ts +8 -2
  71. package/dist/assetstore/reviewLedger.js +71 -45
  72. package/dist/assetstore/unionReadStore.d.ts +25 -0
  73. package/dist/assetstore/unionReadStore.js +119 -0
  74. package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
  75. package/dist/benchmark/antiGeneBenchmark.js +4 -3
  76. package/dist/benchmark/antiGeneRollout.d.ts +2 -0
  77. package/dist/benchmark/antiGeneRollout.js +4 -3
  78. package/dist/benchmark/index.d.ts +3 -1
  79. package/dist/benchmark/index.js +3 -1
  80. package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
  81. package/dist/benchmark/selectionFlatAbstention.js +481 -0
  82. package/dist/benchmark/triggerShift.d.ts +62 -0
  83. package/dist/benchmark/triggerShift.js +106 -0
  84. package/dist/bootstrap/envFingerprint.d.ts +9 -0
  85. package/dist/bootstrap/envFingerprint.js +5 -0
  86. package/dist/bootstrap/index.d.ts +3 -1
  87. package/dist/bootstrap/index.js +3 -1
  88. package/dist/bootstrap/lifecycleBootstrap.d.ts +111 -0
  89. package/dist/bootstrap/lifecycleBootstrap.js +433 -0
  90. package/dist/bootstrap/v1EnvCompat.d.ts +113 -0
  91. package/dist/bootstrap/v1EnvCompat.js +303 -0
  92. package/dist/events/eventArchive.d.ts +2 -0
  93. package/dist/events/eventArchive.js +13 -3
  94. package/dist/events/eventSchema.d.ts +7 -7
  95. package/dist/events/eventStore.d.ts +2 -0
  96. package/dist/events/eventStore.js +5 -1
  97. package/dist/events/ingest.d.ts +2 -1
  98. package/dist/events/ingest.js +15 -0
  99. package/dist/events/paths.d.ts +12 -10
  100. package/dist/events/paths.js +24 -20
  101. package/dist/events/public.d.ts +2 -2
  102. package/dist/events/public.js +2 -2
  103. package/dist/events/reports.d.ts +2 -0
  104. package/dist/events/reports.js +4 -0
  105. package/dist/exec/autoExec.d.ts +68 -4
  106. package/dist/exec/autoExec.js +392 -33
  107. package/dist/exec/autonomousCycle.d.ts +31 -4
  108. package/dist/exec/autonomousCycle.js +71 -13
  109. package/dist/exec/claudeBridge.d.ts +80 -15
  110. package/dist/exec/claudeBridge.js +845 -76
  111. package/dist/exec/executionBinding.d.ts +414 -0
  112. package/dist/exec/executionBinding.js +588 -0
  113. package/dist/exec/index.d.ts +1 -0
  114. package/dist/exec/index.js +1 -0
  115. package/dist/exec/openPrRegistry.d.ts +8 -2
  116. package/dist/exec/openPrRegistry.js +32 -22
  117. package/dist/exec/prompt.js +14 -1
  118. package/dist/exec/proofOfWork.d.ts +1 -1
  119. package/dist/exec/proofOfWork.js +2 -2
  120. package/dist/exec/runnerRegistry.d.ts +153 -36
  121. package/dist/exec/runnerRegistry.js +848 -65
  122. package/dist/exec/selfPr.js +1 -7
  123. package/dist/feedback/envelope.d.ts +61 -0
  124. package/dist/feedback/envelope.js +168 -0
  125. package/dist/feedback/index.d.ts +1 -0
  126. package/dist/feedback/index.js +1 -0
  127. package/dist/hooks/hooks.js +1 -0
  128. package/dist/hub/assetCallLog.d.ts +35 -1
  129. package/dist/hub/assetCallLog.js +124 -1
  130. package/dist/hub/bindings.d.ts +8 -1
  131. package/dist/hub/bindings.js +29 -8
  132. package/dist/hub/capability.d.ts +130 -4
  133. package/dist/hub/conversationDistiller.d.ts +19 -0
  134. package/dist/hub/conversationDistiller.js +115 -37
  135. package/dist/hub/fake.d.ts +3 -2
  136. package/dist/hub/fake.js +2 -1
  137. package/dist/hub/index.d.ts +1 -0
  138. package/dist/hub/index.js +1 -0
  139. package/dist/hub/questionGenerator.d.ts +5 -1
  140. package/dist/hub/questionGenerator.js +8 -6
  141. package/dist/hub/recipeCompose.d.ts +27 -0
  142. package/dist/hub/recipeCompose.js +90 -0
  143. package/dist/hub/sanitize.js +122 -7
  144. package/dist/index.d.ts +6 -1
  145. package/dist/index.js +7 -1
  146. package/dist/issueReporter/index.d.ts +156 -0
  147. package/dist/issueReporter/index.js +1688 -0
  148. package/dist/mailbox/dispatch.d.ts +1 -1
  149. package/dist/mailbox/dispatch.js +22 -6
  150. package/dist/mailbox/envelope.d.ts +7 -1
  151. package/dist/mailbox/envelope.js +9 -2
  152. package/dist/mailbox/ipcServer.d.ts +12 -2
  153. package/dist/mailbox/ipcServer.js +183 -13
  154. package/dist/mailbox/store.d.ts +89 -3
  155. package/dist/mailbox/store.js +895 -41
  156. package/dist/modelCompatibility.d.ts +164 -0
  157. package/dist/modelCompatibility.js +309 -0
  158. package/dist/observers/valueDigestObserver.d.ts +9 -0
  159. package/dist/observers/valueDigestObserver.js +35 -2
  160. package/dist/ops/cleanup.js +1 -1
  161. package/dist/ops/evolutionGraphProjection.d.ts +20 -0
  162. package/dist/ops/evolutionGraphProjection.js +315 -0
  163. package/dist/ops/index.d.ts +2 -1
  164. package/dist/ops/index.js +2 -1
  165. package/dist/ops/savingsCore.js +1 -2
  166. package/dist/ops/selfUpdate.d.ts +18 -1
  167. package/dist/ops/selfUpdate.js +88 -23
  168. package/dist/ops/valueOutreach.d.ts +3 -1
  169. package/dist/ops/valueOutreach.js +5 -1
  170. package/dist/personality/schema.d.ts +24 -24
  171. package/dist/schema/evolutionGraph.d.ts +784 -0
  172. package/dist/schema/evolutionGraph.js +187 -0
  173. package/dist/schema/index.d.ts +1 -0
  174. package/dist/schema/index.js +1 -0
  175. package/dist/schema/proofOfWork.d.ts +125 -6
  176. package/dist/schema/proofOfWork.js +102 -4
  177. package/dist/schema/signal.d.ts +3 -3
  178. package/dist/schema/signal.js +1 -1
  179. package/dist/shadow/shadowHub.js +1 -0
  180. package/dist/signals/curriculum.d.ts +55 -0
  181. package/dist/signals/curriculum.js +202 -0
  182. package/dist/signals/cycleHistoryFromEvents.js +17 -8
  183. package/dist/signals/expand.d.ts +15 -1
  184. package/dist/signals/expand.js +169 -1
  185. package/dist/signals/extractor.d.ts +2 -2
  186. package/dist/signals/extractor.js +31 -6
  187. package/dist/signals/index.d.ts +4 -1
  188. package/dist/signals/index.js +4 -1
  189. package/dist/signals/metaSignals.d.ts +4 -0
  190. package/dist/signals/metaSignals.js +42 -0
  191. package/dist/signals/scopeVocabulary.d.ts +75 -0
  192. package/dist/signals/scopeVocabulary.js +91 -0
  193. package/dist/signals/signalGate.js +1 -1
  194. package/dist/signals/taskDomain.d.ts +22 -0
  195. package/dist/signals/taskDomain.js +43 -0
  196. package/dist/strategy/constraintAblation.d.ts +64 -0
  197. package/dist/strategy/constraintAblation.js +2820 -0
  198. package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
  199. package/dist/strategy/constraintAblationPredicates.js +339 -0
  200. package/dist/strategy/index.d.ts +2 -1
  201. package/dist/strategy/index.js +2 -1
  202. package/dist/trace/index.d.ts +3 -1
  203. package/dist/trace/index.js +3 -1
  204. package/dist/trace/learningTrace.d.ts +216 -0
  205. package/dist/trace/learningTrace.js +298 -0
  206. package/dist/trace/proxyTurns.d.ts +31 -0
  207. package/dist/trace/proxyTurns.js +137 -0
  208. package/dist/trace/trajectory.d.ts +8 -0
  209. package/dist/trace/trajectory.js +14 -2
  210. package/dist/util/fetchPort.d.ts +1 -0
  211. package/dist/util/fetchPort.js +11 -0
  212. package/dist/util/fileLock.d.ts +113 -7
  213. package/dist/util/fileLock.js +1035 -80
  214. package/dist/util/index.d.ts +3 -1
  215. package/dist/util/index.js +2 -1
  216. package/dist/verify/index.d.ts +2 -1
  217. package/dist/verify/index.js +1 -1
  218. package/dist/verify/sandboxRunner.d.ts +30 -0
  219. package/dist/verify/sandboxRunner.js +370 -27
  220. package/dist/verify/sandboxedValidation.d.ts +16 -2
  221. package/dist/verify/sandboxedValidation.js +200 -22
  222. package/dist/verify/validation.d.ts +35 -4
  223. package/dist/verify/validation.js +164 -16
  224. package/dist/wire/geneHints.d.ts +88 -16
  225. package/dist/wire/geneHints.js +124 -15
  226. package/dist/wire/index.d.ts +8 -3
  227. package/dist/wire/index.js +2 -2
  228. package/dist/wire/schemaGate.d.ts +21 -0
  229. package/dist/wire/schemaGate.js +119 -10
  230. package/dist/workflow/dsl.d.ts +24 -3
  231. package/dist/workflow/dsl.js +4 -0
  232. package/dist/workflow/engine.d.ts +5 -1
  233. package/dist/workflow/engine.js +3 -0
  234. package/dist/workflow/index.d.ts +3 -1
  235. package/dist/workflow/index.js +3 -1
  236. package/dist/workflow/runtime.d.ts +110 -0
  237. package/dist/workflow/runtime.js +1298 -0
  238. package/dist/workflow/stateStore.d.ts +172 -0
  239. package/dist/workflow/stateStore.js +1044 -0
  240. package/package.json +12 -4
@@ -16,6 +16,11 @@ export interface PublishReceipt {
16
16
  assetIds?: string[];
17
17
  /** 终态标记: true=不可重试(reject/quarantine/402), SyncEngine 不得 incrementRetry(money-safety risk). */
18
18
  terminal?: boolean;
19
+ /** Adapter-owned structured rejection metadata; callers must not infer policy from free-form `reason`. */
20
+ rejection?: {
21
+ code: 'invalid_request' | 'credit_shortage' | 'node_unauthorized' | 'not_found' | 'duplicate' | 'invalid_payload' | 'cooldown' | 'hub_rejected';
22
+ retryAfterMs?: number;
23
+ };
19
24
  /** M8-1: 公版经济侧只读回执(core 不解释经济语义, 仅透传供观测/UI). private hub 多为空. */
20
25
  economic?: {
21
26
  creditShortage?: boolean;
@@ -25,6 +30,10 @@ export interface PublishReceipt {
25
30
  gdiScore?: number;
26
31
  };
27
32
  }
33
+ export interface PublishOptions {
34
+ /** Stable, non-blank per logical publish so adapters can make transport retries idempotent. */
35
+ idempotencyKey?: string;
36
+ }
28
37
  /** 资产查询. 复用 assetstore SearchQuery, 不发明第二套查询 DSL. */
29
38
  export type HubQuery = SearchQuery;
30
39
  /** task 子系统事件(订阅流元素). 公版含经济上下文(bounty/credit), 私有仅 workflow. */
@@ -111,6 +120,75 @@ export interface ReuseResultReceipt {
111
120
  reason?: string;
112
121
  id?: string;
113
122
  }
123
+ export type LearningAssetType = 'memory' | 'skill' | 'playbook' | 'eval';
124
+ export type LearningAssetStatus = 'pending_review' | 'active' | 'disabled' | 'stale' | 'archived';
125
+ export type LearningAssetEffectiveStatus = LearningAssetStatus | 'expired';
126
+ export type LearningAssetUsageOutcome = ReuseResultOutcome;
127
+ export interface LearningAssetOwner {
128
+ node_id?: string | null;
129
+ user_id?: string | null;
130
+ org_id?: string | null;
131
+ workspace_id?: string | null;
132
+ }
133
+ /** Hub-owned learning asset record. Core keeps it opaque; adapters own wire shape and auth. */
134
+ export interface LearningAssetRecord {
135
+ asset_id: string;
136
+ type: LearningAssetType;
137
+ source_packet_id?: string;
138
+ scope?: string[];
139
+ owner?: LearningAssetOwner;
140
+ title?: string;
141
+ summary?: string;
142
+ confidence?: number;
143
+ last_used_at?: string | null;
144
+ usage_count?: number;
145
+ success_count?: number;
146
+ success_rate?: number;
147
+ expires_at?: string | null;
148
+ conflicts_with?: string[];
149
+ status?: LearningAssetStatus;
150
+ effective_status?: LearningAssetEffectiveStatus;
151
+ status_reason?: string;
152
+ provenance?: Record<string, unknown>;
153
+ payload?: Record<string, unknown>;
154
+ created_at?: string | null;
155
+ updated_at?: string | null;
156
+ [k: string]: unknown;
157
+ }
158
+ export interface LearningAssetListOptions {
159
+ type?: LearningAssetType;
160
+ scope?: readonly string[];
161
+ status?: LearningAssetStatus | readonly LearningAssetStatus[];
162
+ includeExpired?: boolean;
163
+ includePayload?: boolean;
164
+ limit?: number;
165
+ }
166
+ export interface LearningAssetListResult {
167
+ assets: LearningAssetRecord[];
168
+ limit: number;
169
+ reason?: string;
170
+ }
171
+ export interface LearningAssetUsageReport {
172
+ assetId?: string;
173
+ usedAssetIds?: readonly string[];
174
+ outcome: LearningAssetUsageOutcome;
175
+ score?: number;
176
+ sourceEventId: string;
177
+ reason?: string;
178
+ }
179
+ export interface LearningAssetUsageResultRow {
180
+ asset_id: string;
181
+ recorded: boolean;
182
+ duplicated?: boolean;
183
+ error?: string;
184
+ [k: string]: unknown;
185
+ }
186
+ /** Learning-asset usage reporting is best-effort; failures must not break runtime solve paths. */
187
+ export interface LearningAssetUsageReceipt {
188
+ recorded: boolean;
189
+ reason?: string;
190
+ results: LearningAssetUsageResultRow[];
191
+ }
114
192
  /**
115
193
  * Pre-publish dry-run result. The hub runs the same hub-side quality + content-safety
116
194
  * gate as publish but stores nothing and charges no credits, so an agent can check a
@@ -141,6 +219,10 @@ export interface RecipeCreateRequest {
141
219
  pricePerExecution?: number;
142
220
  currency?: string;
143
221
  maxConcurrent?: number;
222
+ idempotencyKey?: string;
223
+ }
224
+ export interface RecipePublishOptions {
225
+ idempotencyKey?: string;
144
226
  }
145
227
  export interface RecipeReceipt {
146
228
  recipeId?: string;
@@ -156,14 +238,29 @@ export interface RecipeExpressRequest {
156
238
  export interface RecipeExpressionReceipt extends RecipeReceipt {
157
239
  organismId?: string;
158
240
  }
241
+ /** Search/list published recipes. Empty `q` is valid; adapters may fall through to list. */
242
+ export interface RecipeSearchRequest {
243
+ q?: string;
244
+ limit?: number;
245
+ cursor?: string;
246
+ sort?: string;
247
+ }
248
+ export interface RecipeSearchReceipt {
249
+ recipes: unknown[];
250
+ nextCursor?: string;
251
+ hasMore?: boolean;
252
+ raw: unknown;
253
+ }
159
254
  /** Optional Hub recipe/DNA capability. Core stays wire-agnostic; adapters own REST shape. */
160
255
  export interface RecipeCapability {
161
256
  create(request: RecipeCreateRequest): Promise<RecipeReceipt>;
162
- publish(recipeId: string): Promise<RecipeReceipt>;
257
+ publish(recipeId: string, options?: RecipePublishOptions): Promise<RecipeReceipt>;
163
258
  get(recipeId: string): Promise<RecipeFetchReceipt>;
164
259
  express(recipeId: string, request?: RecipeExpressRequest): Promise<RecipeExpressionReceipt>;
260
+ search(request?: RecipeSearchRequest): Promise<RecipeSearchReceipt>;
261
+ list(request?: RecipeSearchRequest): Promise<RecipeSearchReceipt>;
165
262
  }
166
- export type HubCapabilityName = 'publish' | 'fetch' | 'search' | 'task' | 'mailbox' | 'auth' | 'audit' | 'air_gap' | 'tenant_isolation' | 'marketplace' | 'economy' | 'questions' | 'recipes' | 'agent_directory';
263
+ export type HubCapabilityName = 'publish' | 'fetch' | 'search' | 'task' | 'mailbox' | 'auth' | 'audit' | 'air_gap' | 'tenant_isolation' | 'marketplace' | 'economy' | 'questions' | 'recipes' | 'agent_directory' | 'learning_assets';
167
264
  /** hub 能力清单(可选). core 据此动态适配 UI/行为, "按需耦合". */
168
265
  export interface HubManifest {
169
266
  capabilities: HubCapabilityName[] | string[];
@@ -201,6 +298,7 @@ export interface HttpRequestLike {
201
298
  method: string;
202
299
  path: string;
203
300
  body?: string;
301
+ signal?: AbortSignal;
204
302
  }
205
303
  /**
206
304
  * Pluggable auth. Example implementations: public OAuth device token / Ed25519 keypair / enterprise SSO (OIDC/LDAP).
@@ -217,20 +315,48 @@ export interface AuthProvider {
217
315
  /** 撤销凭证(机器丢失/泄露). */
218
316
  revoke(credentialId: string): Promise<void>;
219
317
  }
318
+ /**
319
+ * By-id fetch is content-addressed by default. Integrity-sensitive callers may
320
+ * explicitly opt in to receiving a schema-valid delivery whose declared
321
+ * asset_id matches the request while its current content hash does not.
322
+ */
323
+ export interface FetchAssetByIdOptions {
324
+ allowUnverifiedExactIdentity?: boolean;
325
+ }
326
+ /** Why a by-id fetch produced no asset. `absent` is the ONLY one that means the hub does not have it. */
327
+ export type AssetDeliveryRejection = 'identity_mismatch' | 'revoked' | 'ambiguous' | 'unverified_not_allowed';
328
+ /**
329
+ * The outcome of a by-id fetch. `verified:false` means the delivery is bound to the requested id but its body
330
+ * hashes to something else — usable only under quarantine, never as canonical content.
331
+ */
332
+ export type AssetDeliveryOutcome = {
333
+ status: 'delivered';
334
+ asset: AssetRecord;
335
+ verified: boolean;
336
+ } | {
337
+ status: 'absent';
338
+ } | {
339
+ status: 'rejected';
340
+ reason: AssetDeliveryRejection;
341
+ };
220
342
  export interface HubCapability {
221
343
  /**
222
344
  * 异步发布. 公版 /a2a/publish 收 **bundle**[Gene,Capsule,(EvolutionEvent)](一个 cycle 的产物一起发, +GDI);
223
345
  * 传单资产=[asset]。core 已 normalizeForPut(算/校验 asset_id), hub 做经济/治理/质量 gate。
224
346
  */
225
- publish(bundle: AssetRecord[]): Promise<PublishReceipt>;
347
+ publish(bundle: AssetRecord[], options?: PublishOptions): Promise<PublishReceipt>;
226
348
  /** 拉取资产. hub 侧排序/scope(公版 GDI, 私有 RBAC); core 不重排. */
227
349
  fetch(query: HubQuery): Promise<AssetRecord[]>;
228
350
  /** 搜索资产. 与 fetch 同 shape, 差异在 hub 侧召回策略. */
229
351
  search(query: HubQuery): Promise<AssetRecord[]>;
230
352
  /** 可选: 按 content-addressed asset_id 拉单个 full asset. private adapter/proxy 用它保证只 fetch 一个 winner. */
231
- fetchAssetById?(assetId: string): Promise<AssetRecord | null>;
353
+ fetchAssetById?(assetId: string, options?: FetchAssetByIdOptions): Promise<AssetRecord | null>;
232
354
  /** 可选: 报告某个资产被复用后的结果. PHub/enterprise adapter 实现; 不支持时 runtime 应明确降级. */
233
355
  recordReuseResult?(report: ReuseResultReport): Promise<ReuseResultReceipt>;
356
+ /** 可选: 列出 hub 端 runtime learning assets. Missing means the adapter does not support this MVP surface. */
357
+ listLearningAssets?(options?: LearningAssetListOptions): Promise<LearningAssetListResult>;
358
+ /** 可选: 上报 learning asset 使用结果. Best-effort; failures must never break solve paths. */
359
+ recordLearningAssetUsage?(report: LearningAssetUsageReport): Promise<LearningAssetUsageReceipt>;
234
360
  /**
235
361
  * 可选: 发布前 dry-run 校验(公版 POST /a2a/validate). 跑与 publish 相同的 hub 端质量门禁 + 内容安全扫描,
236
362
  * 但不落库、不计费; 经由 Proxy 暴露时调用方需先执行与 publish 相同的客户端脱敏/泄漏拦截.
@@ -43,6 +43,7 @@ interface NormalizedExecution {
43
43
  files: number;
44
44
  lines: number;
45
45
  };
46
+ untrustedStatusClaim: boolean;
46
47
  }
47
48
  interface NormalizedConversation {
48
49
  text: string;
@@ -65,6 +66,22 @@ export interface QualityGate {
65
66
  export interface ConversationDistillOptions {
66
67
  persist?: boolean;
67
68
  store?: AssetStoreProvider;
69
+ /**
70
+ * 仅由受信任的宿主验证器注入。HTTP/MCP 请求体中的 execution 不能替代此证据。
71
+ */
72
+ verifiedExecution?: ConversationDistillVerifiedExecution;
73
+ }
74
+ export interface ConversationDistillVerifiedExecution {
75
+ trace: ReadonlyArray<{
76
+ command: string;
77
+ exit: number;
78
+ summary?: string;
79
+ }>;
80
+ validation?: readonly string[];
81
+ blast_radius?: {
82
+ files?: number;
83
+ lines?: number;
84
+ };
68
85
  }
69
86
  export type ConversationDistillResult = {
70
87
  ok: false;
@@ -80,6 +97,8 @@ export type ConversationDistillResult = {
80
97
  signals: string[];
81
98
  gene: AssetRecord;
82
99
  capsule: AssetRecord;
100
+ /** 仅当质量闸门通过且草稿可以进入发布流程时为 true。 */
101
+ publishable: boolean;
83
102
  };
84
103
  export declare function inferSignals(text: string, providedSignals?: unknown): string[];
85
104
  export declare function evaluateGate(input: ConversationDistillInput, normalized: NormalizedConversation): QualityGate;
@@ -1,6 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { redactString } from './sanitize.js';
3
- import { SCHEMA_VERSION, computeAssetId } from '../wire/index.js';
2
+ import { intakeGene } from '../algo/geneIntake.js';
3
+ import { redactDeep, redactString } from './sanitize.js';
4
+ import { SCHEMA_VERSION, computeAssetId, stripGeneHints, validateWireDeep } from '../wire/index.js';
4
5
  const DEFAULT_SIGNALS = [
5
6
  'conversation_distillation',
6
7
  'reusable_capability',
@@ -16,6 +17,7 @@ const SIGNAL_RULES = [
16
17
  { signal: 'plugin_integration', re: /\b(plugin|codex|claude|cursor|antigravity|workbuddy|hook|notify)\b|插件|钩子/i },
17
18
  { signal: 'test_verified', re: /\b(test|build|verify|passed|green)\b|测试|验证|通过/i },
18
19
  ];
20
+ const BUNDLE_PERSISTENCE_UNSUPPORTED = 'bundle_persistence_unsupported';
19
21
  function asRecord(v) {
20
22
  return v && typeof v === 'object' && !Array.isArray(v) ? v : {};
21
23
  }
@@ -74,37 +76,63 @@ function normalizePublishBlastRadius(value, artifactCount) {
74
76
  };
75
77
  }
76
78
  function capsuleContent(normalized) {
77
- return redactString(JSON.stringify({
79
+ return redactDeep({
78
80
  platform: normalized.platform,
79
81
  source_thread: normalized.source_thread || null,
80
82
  artifacts: normalized.artifacts,
81
83
  excerpt: normalized.text.slice(0, 1200),
82
- }));
84
+ });
83
85
  }
84
86
  function normalizeExecution(input) {
85
87
  const execution = asRecord(input.execution);
86
88
  const validation = normalizeList(input.validation ?? input.verification ?? execution['validation'], 8, 180);
89
+ const untrustedStatusClaim = execution['status'] !== undefined || execution['ok'] !== undefined;
90
+ let malformedTrace = false;
87
91
  const trace = asArray(execution['trace']).map((item) => {
88
- if (typeof item === 'string')
89
- return { command: trimText(item, 180), exit: 0 };
92
+ if (typeof item === 'string') {
93
+ malformedTrace = true;
94
+ return null;
95
+ }
90
96
  const row = asRecord(item);
91
- if (Object.keys(row).length === 0)
97
+ if (Object.keys(row).length === 0) {
98
+ malformedTrace = true;
99
+ return null;
100
+ }
101
+ const command = trimText(row['command'] ?? row['cmd'] ?? row['name'] ?? '', 180);
102
+ if (!command) {
103
+ malformedTrace = true;
92
104
  return null;
105
+ }
106
+ const rawExit = row['exit'];
107
+ const exit = Number.isInteger(rawExit)
108
+ ? Number(rawExit)
109
+ : (typeof row['ok'] === 'boolean' ? (row['ok'] ? 0 : 1) : undefined);
110
+ if (exit === undefined) {
111
+ malformedTrace = true;
112
+ return null;
113
+ }
93
114
  return {
94
- command: trimText(row['command'] ?? row['cmd'] ?? row['name'] ?? 'validation', 180),
95
- exit: Number.isInteger(row['exit']) ? Number(row['exit']) : (row['ok'] === false ? 1 : 0),
115
+ command,
116
+ exit,
96
117
  summary: trimText(row['summary'] ?? row['output'] ?? '', 240),
97
118
  };
98
119
  }).filter((row) => row !== null);
99
- for (const cmd of validation)
100
- if (!trace.some((t) => t.command === cmd))
101
- trace.push({ command: cmd, exit: 0 });
102
- const ok = execution['status'] === 'success' || execution['ok'] === true || validation.length > 0 || trace.some((t) => t.exit === 0);
120
+ // 声明的验证命令只是计划,并不能证明命令已经执行。
121
+ // 保留它作为质量信号,但不能为未执行的声明伪造 exit-0 轨迹或成功结果。
122
+ // 只有显式结构化的退出码才可作为证据。调用方声明的 status/ok 字段只是元数据,
123
+ // 不能据此把资产晋级到发布流程。
124
+ const normalizedValidation = new Set(validation);
125
+ const covered = [...normalizedValidation].every((command) => trace.some((row) => row.command === command));
126
+ const ok = trace.length > 0
127
+ && !malformedTrace
128
+ && trace.every((t) => t.exit === 0)
129
+ && covered;
103
130
  return {
104
131
  status: ok ? 'success' : 'failed',
105
132
  trace,
106
133
  validation,
107
134
  blast_radius: normalizeBlastRadius(execution['blast_radius'] ?? input.blast_radius),
135
+ untrustedStatusClaim,
108
136
  };
109
137
  }
110
138
  function buildStrategy(input) {
@@ -145,7 +173,9 @@ export function evaluateGate(input, normalized) {
145
173
  reasons.push('explicit_distill_signal');
146
174
  }
147
175
  const rawThreshold = Number(input.min_score ?? input.minScore ?? 5);
148
- const threshold = Number.isFinite(rawThreshold) ? rawThreshold : 5;
176
+ // 调用方可以要求更严格的阈值,但不能把准入策略降为零,
177
+ // 也不能把未经确认的草稿变成可发布资产。
178
+ const threshold = Number.isFinite(rawThreshold) ? Math.max(5, rawThreshold) : 5;
149
179
  if (score < threshold)
150
180
  return { ok: false, score, threshold, reasons, reason: 'insufficient_reusable_signal' };
151
181
  return { ok: true, score, threshold, reasons };
@@ -178,12 +208,17 @@ export function normalizeConversationInput(input) {
178
208
  export async function distillConversation(input, opts = {}) {
179
209
  if (!input || typeof input !== 'object' || Array.isArray(input))
180
210
  return { ok: false, status: 'skipped', reason: 'input_object_required' };
181
- const normalized = normalizeConversationInput(input);
211
+ const effectiveInput = opts.verifiedExecution
212
+ ? {
213
+ ...input,
214
+ execution: opts.verifiedExecution,
215
+ ...(opts.verifiedExecution.validation ? { validation: opts.verifiedExecution.validation } : {}),
216
+ }
217
+ : input;
218
+ const normalized = normalizeConversationInput(effectiveInput);
182
219
  if (!normalized.summary || normalized.summary.length < 20)
183
220
  return { ok: false, status: 'skipped', reason: 'summary_required' };
184
221
  const gate = evaluateGate(input, normalized);
185
- if (!gate.ok)
186
- return { ok: false, status: 'skipped', reason: gate.reason ?? 'insufficient_reusable_signal', quality: gate, signals: normalized.signals };
187
222
  const slug = slugify(input.name ?? input.title ?? normalized.summary);
188
223
  const fingerprint = hashInput({
189
224
  summary: normalized.summary,
@@ -192,9 +227,13 @@ export async function distillConversation(input, opts = {}) {
192
227
  artifacts: normalized.artifacts,
193
228
  });
194
229
  const category = inferCategory(normalized.signals, normalized.text);
195
- const gene = {
196
- type: 'Gene',
197
- schema_version: SCHEMA_VERSION,
230
+ // 只有质量门禁与受信任宿主证据同时通过时,Capsule 才允许广播。
231
+ const publishable = gate.ok && normalized.execution.status === 'success' && opts.verifiedExecution !== undefined;
232
+ const declaredValidation = normalized.execution.validation;
233
+ // GEP Gene schema 要求非空 validation。调用方没有计划时保留旧版 Node 身份检查,
234
+ // 但它只用于形成可审阅草稿,绝不能产生 verifier 坐标或发布资格。
235
+ const validation = declaredValidation.length > 0 ? declaredValidation : ['node --version'];
236
+ const intake = intakeGene({
198
237
  id: `gene_conversation_${slug}_${fingerprint}`,
199
238
  summary: normalized.summary,
200
239
  category,
@@ -204,18 +243,28 @@ export async function distillConversation(input, opts = {}) {
204
243
  'The conversation includes enough evidence to reconstruct when and how to use it.',
205
244
  ],
206
245
  strategy: normalized.strategy,
207
- validation: normalized.execution.validation.length > 0 ? normalized.execution.validation : ['node --version'],
246
+ validation,
208
247
  constraints: { max_files: 20, forbidden_paths: ['.git', 'node_modules', '.env'] },
209
- _source: {
210
- kind: 'conversation_distillation',
211
- platform: normalized.platform,
212
- source_thread: normalized.source_thread || null,
248
+ generation_meta: { source: 'distilled' },
249
+ model_name: normalized.model,
250
+ });
251
+ if (!intake.ok || !intake.gene) {
252
+ return {
253
+ ok: false,
254
+ status: 'skipped',
255
+ reason: 'gene_intake_failed',
213
256
  quality: gate,
214
- distilled_at: new Date().toISOString(),
215
- },
216
- asset_id: '',
217
- };
218
- gene.asset_id = computeAssetId(gene) ?? '';
257
+ signals: normalized.signals,
258
+ };
259
+ }
260
+ const gene = { ...intake.gene };
261
+ // intakeGene 会从任何非空 validation 计划推导 verifier_profile。旧版身份检查并非
262
+ // 行为验证证据,因此必须移除该坐标并重新绑定内容寻址 ID,保持严格 K_auto fail-closed。
263
+ if (declaredValidation.length === 0) {
264
+ delete gene.verifier_profile;
265
+ gene.asset_id = '';
266
+ gene.asset_id = computeAssetId(gene) ?? '';
267
+ }
219
268
  const capsule = {
220
269
  type: 'Capsule',
221
270
  schema_version: SCHEMA_VERSION,
@@ -226,13 +275,13 @@ export async function distillConversation(input, opts = {}) {
226
275
  confidence: Math.min(0.95, 0.5 + gate.score / 20),
227
276
  blast_radius: normalizePublishBlastRadius(normalized.execution.blast_radius, normalized.artifacts.length),
228
277
  outcome: { status: normalized.execution.status, score: normalized.execution.status === 'success' ? 0.82 : 0.35 },
229
- source_type: 'conversation_distillation',
278
+ source_type: 'generated',
230
279
  strategy: normalized.strategy,
231
280
  execution_trace: normalized.execution.trace,
232
- a2a: { eligible_to_broadcast: true },
281
+ a2a: { eligible_to_broadcast: publishable },
233
282
  content: capsuleContent(normalized),
234
- diff: '',
235
- reused_asset_id: '',
283
+ diff: null,
284
+ reused_asset_id: null,
236
285
  env_fingerprint: {
237
286
  platform: normalized.platform,
238
287
  source_thread: normalized.source_thread || null,
@@ -247,14 +296,43 @@ export async function distillConversation(input, opts = {}) {
247
296
  asset_id: '',
248
297
  };
249
298
  capsule.asset_id = computeAssetId(capsule) ?? '';
250
- const persist = (opts.persist ?? input.persist) === true;
299
+ // 质量准入可以保持宽松,但草稿仍必须是有效的 wire 记录。
300
+ // 在持久化或排队前捕获生产者与 schema 的漂移。
301
+ if (!validateWireDeep(stripGeneHints(gene)).ok || !validateWireDeep(capsule).ok) {
302
+ return {
303
+ ok: false,
304
+ status: 'skipped',
305
+ reason: 'wire_schema_invalid',
306
+ quality: gate,
307
+ signals: normalized.signals,
308
+ };
309
+ }
310
+ // 只有受信任宿主验证器提供的成功 execution 才能解锁发布。
311
+ // 本地调用方显式要求 persist 时,可以保留没有成功证据的失败草稿供人工复核,
312
+ // 但请求体伪造的 exit-0 轨迹不能借此写入看似成功的资产。
313
+ const persistRequested = (opts.persist ?? input.persist) === true;
314
+ const failedDraftPersistence = !opts.verifiedExecution
315
+ && normalized.execution.status === 'failed'
316
+ && !normalized.execution.untrustedStatusClaim;
317
+ const persist = persistRequested && (publishable || (gate.ok && failedDraftPersistence));
251
318
  if (persist && opts.store) {
252
- await opts.store.put(gene);
253
- await opts.store.put(capsule);
319
+ // Gene/Capsule 是一个可发布单元。只提供单条写入的 provider 无法恢复
320
+ // 两次写入之间的崩溃窗口;不要用兼容回退制造孤儿 Gene。
321
+ if (typeof opts.store.putBundle !== 'function') {
322
+ return {
323
+ ok: false,
324
+ status: 'skipped',
325
+ reason: BUNDLE_PERSISTENCE_UNSUPPORTED,
326
+ quality: gate,
327
+ signals: normalized.signals,
328
+ };
329
+ }
330
+ await opts.store.putBundle([gene, capsule]);
254
331
  }
255
332
  return {
256
333
  ok: true,
257
334
  status: persist && opts.store ? 'stored' : 'draft',
335
+ publishable,
258
336
  distill_id: fingerprint,
259
337
  quality: gate,
260
338
  signals: normalized.signals,
@@ -1,4 +1,4 @@
1
- import type { HubCapability, PublishReceipt, AgentEvent, TaskEvent, AuthProvider, HubQuery, AssetRecord, TaskCompleteContext } from './capability.js';
1
+ import type { HubCapability, PublishReceipt, AgentEvent, TaskEvent, AuthProvider, HubQuery, AssetRecord, TaskCompleteContext, PublishOptions } from './capability.js';
2
2
  export interface FakeHubOptions {
3
3
  /** 脚本化 publish gate: 返回 reject/quarantine 以测 PublishReceipt 终态路径. */
4
4
  publishGate?: (asset: AssetRecord) => Pick<PublishReceipt, 'status' | 'reason' | 'terminal'>;
@@ -24,11 +24,12 @@ export declare class FakeHubCapability implements HubCapability {
24
24
  }>;
25
25
  readonly auth: AuthProvider;
26
26
  agentDirectory?: import('./agentDirectory.js').AgentDirectoryCapability;
27
+ recipes?: import('./capability.js').RecipeCapability;
27
28
  nextPollAfterMs: number | undefined;
28
29
  constructor(opts?: FakeHubOptions);
29
30
  /** 测试注入: 排一条 inbound 事件供 poll 拉. */
30
31
  seedInbound(e: AgentEvent): void;
31
- publish(bundle: AssetRecord[]): Promise<PublishReceipt>;
32
+ publish(bundle: AssetRecord[], _options?: PublishOptions): Promise<PublishReceipt>;
32
33
  fetch(query: HubQuery): Promise<AssetRecord[]>;
33
34
  search(query: HubQuery): Promise<AssetRecord[]>;
34
35
  task: {
package/dist/hub/fake.js CHANGED
@@ -20,13 +20,14 @@ export class FakeHubCapability {
20
20
  completed = [];
21
21
  auth = fakeAuth;
22
22
  agentDirectory;
23
+ recipes;
23
24
  nextPollAfterMs;
24
25
  constructor(opts = {}) {
25
26
  this.opts = opts;
26
27
  }
27
28
  /** 测试注入: 排一条 inbound 事件供 poll 拉. */
28
29
  seedInbound(e) { this.inbox.push(e); }
29
- async publish(bundle) {
30
+ async publish(bundle, _options) {
30
31
  const assetIds = bundle.map((a) => computeAssetId(a));
31
32
  const gate = this.opts.publishGate?.(bundle[0] ?? {}) ?? { status: 'accepted' };
32
33
  if (gate.status === 'accepted')
@@ -8,4 +8,5 @@ export * from './assetCallLog.js';
8
8
  export * from './hubReview.js';
9
9
  export * from './reuseDecision.js';
10
10
  export * from './conversationDistiller.js';
11
+ export * from './recipeCompose.js';
11
12
  export * from './questionGenerator.js';
package/dist/hub/index.js CHANGED
@@ -8,4 +8,5 @@ export * from './assetCallLog.js';
8
8
  export * from './hubReview.js';
9
9
  export * from './reuseDecision.js';
10
10
  export * from './conversationDistiller.js';
11
+ export * from './recipeCompose.js';
11
12
  export * from './questionGenerator.js';
@@ -54,6 +54,10 @@ export interface QuestionGenerationResult {
54
54
  state: QuestionGeneratorState;
55
55
  changed: boolean;
56
56
  }
57
- export declare function extractTopicKeywords(transcript: string | undefined, memory: string | undefined, max?: number): string[];
57
+ export interface TopicKeywordOptions {
58
+ minOccurrences?: number;
59
+ preserveSourceOrder?: boolean;
60
+ }
61
+ export declare function extractTopicKeywords(transcript: string | undefined, memory: string | undefined, max?: number, opts?: TopicKeywordOptions): string[];
58
62
  export declare function generateQuestions(input?: GenerateQuestionsInput): QuestionGenerationResult;
59
63
  export declare function generateUrgentQuestions(input?: GenerateUrgentQuestionsInput): QuestionGenerationResult;
@@ -21,7 +21,7 @@ const EXPLORE_STOPWORDS = new Set([
21
21
  'agent', 'about', 'into', 'then', 'than', 'they', 'them', 'their', 'there',
22
22
  'here', 'will', 'would', 'could', 'should', 'been', 'were', 'using', 'used',
23
23
  'cycle', 'evolution', 'error', 'errors', 'failed', 'failure', 'null', 'undefined',
24
- 'true', 'false', 'console', 'return', 'function', 'const', 'value', 'result',
24
+ 'true', 'false', 'console', 'return', 'function', 'const', 'value', 'result', 'redacted',
25
25
  ]);
26
26
  const SENSITIVE_TOPIC_RE = /secret|token|api[_-]?key|password|passwd|credential|bearer|authorization|cookie|session[_-]?id|private[_-]?key|oauth|refresh[_-]?token/i;
27
27
  const LONG_OPAQUE_RE = /^[a-z0-9_-]{32,}$/i;
@@ -106,8 +106,8 @@ function safeTopicToken(token) {
106
106
  return false;
107
107
  return true;
108
108
  }
109
- export function extractTopicKeywords(transcript, memory, max = 5) {
110
- const text = `${String(transcript ?? '')} ${String(memory ?? '')}`.toLowerCase();
109
+ export function extractTopicKeywords(transcript, memory, max = 5, opts = {}) {
110
+ const text = redactString(`${String(transcript ?? '')} ${String(memory ?? '')}`).toLowerCase();
111
111
  const words = text.match(/[a-z][a-z0-9_-]{4,}/g) ?? [];
112
112
  const freq = new Map();
113
113
  for (const word of words) {
@@ -115,9 +115,11 @@ export function extractTopicKeywords(transcript, memory, max = 5) {
115
115
  continue;
116
116
  freq.set(word, (freq.get(word) ?? 0) + 1);
117
117
  }
118
- return [...freq.entries()]
119
- .filter(([, count]) => count >= 2)
120
- .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
118
+ const entries = [...freq.entries()]
119
+ .filter(([, count]) => count >= Math.max(1, Math.trunc(opts.minOccurrences ?? 2)));
120
+ if (!opts.preserveSourceOrder)
121
+ entries.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
122
+ return entries
121
123
  .map(([word]) => word)
122
124
  .slice(0, max);
123
125
  }
@@ -0,0 +1,27 @@
1
+ import type { AssetRecord, HubCapability } from './capability.js';
2
+ export declare const RECIPE_COMPOSE_FLAG = "compose_recipe";
3
+ export interface RecipeComposeInput {
4
+ title?: string;
5
+ description?: string;
6
+ assets?: readonly unknown[];
7
+ source?: unknown;
8
+ [k: string]: unknown;
9
+ }
10
+ export interface RecipeComposeResult {
11
+ attempted: boolean;
12
+ ok: boolean;
13
+ reason?: string;
14
+ recipeId?: string;
15
+ status?: string;
16
+ }
17
+ export declare function recipeComposeRequested(payload: RecipeComposeInput): boolean;
18
+ export declare function recipeAssetsFromPayload(payload: RecipeComposeInput): {
19
+ gene?: AssetRecord;
20
+ capsule?: AssetRecord;
21
+ };
22
+ /**
23
+ * After a Gene/Capsule bundle is accepted by Hub, compose a Recipe as the
24
+ * preferred public artifact. Skill Store publish is not the default outbound.
25
+ * Missing recipe capability or missing Gene is a no-op, never a publish failure.
26
+ */
27
+ export declare function composeRecipeAfterAssetPublish(hub: Pick<HubCapability, 'recipes'>, payload: RecipeComposeInput): Promise<RecipeComposeResult>;