agentworkshop 0.7.48 → 0.7.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/0530cee1-fff5-4025-a6f4-5708b6534608.json +1 -0
- package/.output/server/chunks/_/nitro.mjs +957 -955
- package/.output/server/package.json +1 -1
- package/README-zh.md +813 -811
- package/README.md +823 -821
- package/config.yml +176 -176
- package/docs/cli.en.md +445 -445
- package/docs/cli.md +410 -410
- package/docs/full-test-plan.md +191 -191
- package/docs/plugins.en.md +567 -567
- package/docs/plugins.md +560 -560
- package/docs/sdk.en.md +922 -922
- package/docs/sdk.md +861 -861
- package/package.json +172 -172
- package/scripts/_aw0746-stage5.mjs +159 -144
- package/scripts/_aw0746-stage7-twin.mjs +301 -267
- package/scripts/_aw0748-failclosed-probe.mjs +117 -90
- package/scripts/_aw0748-fresh-channel.mjs +21 -0
- package/scripts/_aw0748-mock-closedloop.mjs +89 -0
- package/scripts/_aw0748-wake-channel.mjs +46 -0
- package/scripts/_aw0749-readme-rows.mjs +23 -0
- package/scripts/_probe-lease.cjs +13 -0
- package/scripts/_probe-task-queue.cjs +9 -0
- package/server/services/workshop/aml/twin/provider-contracts.ts +34 -0
- package/server/services/workshop/aml/twin/provider-health.ts +8 -0
- package/server/services/workshop/aml/twin/provider-loader.ts +67 -0
- package/server/services/workshop/aml/twin/provider-registry.ts +50 -0
- package/server/services/workshop/runtime/manager/state.ts +21 -4
- package/.output/public/_nuxt/builds/meta/6f524f3b-53e1-4df9-84e6-84d8058b9f16.json +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { sha256 } from './contracts'
|
|
2
|
+
import { checkProviderHealth, normalizeProviderHealth, providerHashOf, providerHealthAllowsUse, validateProviderDefinition } from './provider-health'
|
|
3
|
+
import type { PhysicsProviderManifest, ProviderHealth, ProviderLifecycleState, ProviderValidationResult, ScenePack, SolverAdapter, SolverAdapterManifest, TwinPhysicsProvider, TwinPluginContribution, TwinProviderDrainResult, TwinProviderFilter, TwinProviderLease, TwinProviderRegistrationResult, TwinProviderRetireResult, TwinProviderSummary, TwinProviderUnregistrationResult, TwinRegistrationSource } from './provider-contracts'
|
|
4
|
+
export interface ResolveProviderOptions { generation?:number, allowDraining?:boolean, includeFailed?:boolean }
|
|
5
|
+
interface ProviderRecord { provider:TwinPhysicsProvider, manifest:PhysicsProviderManifest, providerHash:string, pluginName:string, pluginVersion?:string, generation:number, state:ProviderLifecycleState, health:ProviderHealth, inFlight:number, registeredAt:string, drainingAt?:string, retiredAt?:string }
|
|
6
|
+
interface GenerationRecord { pluginName:string, pluginVersion?:string, generation:number, state:ProviderLifecycleState, registeredAt:string, drainingAt?:string, retiredAt?:string, providers:Map<string,ProviderRecord>, scenePacks:Map<string,ScenePack>, solverAdapters:Map<string,SolverAdapter> }
|
|
7
|
+
export class TwinProviderRegistryError extends Error{readonly code:string;constructor(code:string,message:string){super(message);this.name='TwinProviderRegistryError';this.code=code}}
|
|
8
|
+
function pluginOf(s: TwinRegistrationSource|string='core'){return typeof s==='string'?s:String(s.pluginName??s.plugin??s.name??'core')}
|
|
9
|
+
function verOf(s: TwinRegistrationSource|string='core'){return typeof s==='string'?undefined:s.pluginVersion??s.version}
|
|
10
|
+
function key(id:string,v:string){return `${id}@${v}`}
|
|
11
|
+
function normalizePack(raw:any):ScenePack{return{...raw,sceneKind:raw.sceneKind??raw.scenePackId??'unknown-scene',sceneSchemaVersion:String(raw.sceneSchemaVersion??raw.version??'1.0.0')}}
|
|
12
|
+
export class TwinProviderRegistry{
|
|
13
|
+
private generations=new Map<string,GenerationRecord>();private next=new Map<string,number>();private leases=0
|
|
14
|
+
private allocate(plugin:string){const n=(this.next.get(plugin)??0)+1;this.next.set(plugin,n);return n}
|
|
15
|
+
registerTwinPluginContribution(pluginName:string,c:TwinPluginContribution,generation?:number):TwinProviderRegistrationResult{const plugin=String(pluginName||'').trim();if(!plugin)throw new TwinProviderRegistryError('PLUGIN_NAME_MISSING','pluginName required');const g=generation??this.allocate(plugin);const gk=`${plugin}\0${g}`;let gen=this.generations.get(gk);if(gen?.state==='RETIRED'||gen?.state==='DRAINING')throw new TwinProviderRegistryError('GENERATION_UNAVAILABLE',`generation ${plugin}@${g} unavailable`);if(!gen){gen={pluginName:plugin,generation:g,state:'VALIDATED',registeredAt:new Date().toISOString(),providers:new Map(),scenePacks:new Map(),solverAdapters:new Map()};this.generations.set(gk,gen)}let added=0;for(const provider of [...(c.providers??[]),...(c.physicsProviders??[])]){const check=validateProviderDefinition(provider);if(!check.valid||!check.manifest||!check.providerHash)throw new TwinProviderRegistryError('PROVIDER_INVALID',check.errors.join(';')||'invalid provider');const k=key(check.manifest.providerId,check.manifest.version);const old=gen.providers.get(k);if(old){if(old.providerHash!==check.providerHash)throw new TwinProviderRegistryError('PROVIDER_CONFLICT',k);continue}for(const [otherKey,otherGen] of this.generations){if(otherKey===gk)continue;const oldOther=otherGen.providers.get(k);if(oldOther&&oldOther.state!=='RETIRED'&&oldOther.pluginName!==plugin)throw new TwinProviderRegistryError('PROVIDER_IDENTITY_CONFLICT',`${k} already registered by ${oldOther.pluginName}`)}gen.providers.set(k,{provider,manifest:check.manifest,providerHash:check.providerHash,pluginName:plugin,pluginVersion:verOf((c as any).source),generation:g,state:'READY',health:normalizeProviderHealth(undefined),inFlight:0,registeredAt:gen.registeredAt});added++}for(const raw of c.scenePacks??[]){const pack=normalizePack(raw);gen.scenePacks.set(`${pack.sceneKind}@${pack.sceneSchemaVersion}`,pack)}for(const adapter of c.solverAdapters??[]){const m=(adapter as any).manifest as SolverAdapterManifest;if(!m?.adapterId||typeof (adapter as any).solve!=='function')throw new TwinProviderRegistryError('SOLVER_ADAPTER_INVALID','solver adapter requires manifest.adapterId and solve()');gen.solverAdapters.set(`${m.adapterId}@${m.version}`,adapter)}gen.state='READY';for(const [k,old] of this.generations){if(old.pluginName===plugin&&k!==gk&&old.state!=='RETIRED'){this.markDraining(old)}}return{ok:true,pluginName:plugin,generation:g,providerIds:[...gen.providers.values()].map(r=>r.manifest.providerId),sceneKinds:[...gen.scenePacks.values()].map(p=>p.sceneKind),solverAdapterIds:[...gen.solverAdapters.values()].map(a=>(a as any).manifest.adapterId),state:gen.state,idempotent:added===0}}
|
|
16
|
+
unregisterTwinPluginContribution(pluginName:string,generation?:number):TwinProviderUnregistrationResult{const drained:number[]=[],retired:number[]=[];for(const g of this.generations.values())if(g.pluginName===pluginName&&(generation==null||g.generation===generation)){if(g.state!=='RETIRED'){this.markDraining(g);drained.push(g.generation)}if(g.state==='RETIRED')retired.push(g.generation)}return{ok:true,pluginName:pluginName,generation,drainedGenerations:drained,retiredGenerations:retired}}
|
|
17
|
+
registerPhysicsProvider(provider:TwinPhysicsProvider,source:TwinRegistrationSource|string='core',generation?:number){return this.registerTwinPluginContribution(pluginOf(source),{providers:[provider]},generation)}
|
|
18
|
+
registerScenePack(pack:ScenePack,source:TwinRegistrationSource|string='core',generation?:number){return this.registerTwinPluginContribution(pluginOf(source),{scenePacks:[pack]},generation)}
|
|
19
|
+
registerSolverAdapter(adapter:SolverAdapter,source:TwinRegistrationSource|string='core',generation?:number){return this.registerTwinPluginContribution(pluginOf(source),{solverAdapters:[adapter]},generation)}
|
|
20
|
+
resolveProvider(providerId:string,versionOrOptions?:string|ResolveProviderOptions,maybeOptions?:ResolveProviderOptions):TwinProviderLease|undefined{const version=typeof versionOrOptions==='string'?versionOrOptions:undefined;const options=(typeof versionOrOptions==='object'?versionOrOptions:maybeOptions)??{};const rows=[...this.generations.values()].flatMap(g=>[...g.providers.values()]).filter(r=>r.manifest.providerId===providerId&&(!version||r.manifest.version===version)&&(!options.generation||r.generation===options.generation)&&r.state!=='RETIRED'&&(!options.includeFailed&&r.state==='FAILED'?false:true)&&(!options.allowDraining&&r.state==='DRAINING'?false:true)).sort((a,b)=>b.generation-a.generation);const r=rows[0];if(!r)return;return this.lease(r)}
|
|
21
|
+
resolve(providerId:string,versionOrOptions?:string|ResolveProviderOptions,maybeOptions?:ResolveProviderOptions){return this.resolveProvider(providerId,versionOrOptions,maybeOptions)}
|
|
22
|
+
acquireProvider(providerId:string,versionOrOptions?:string|ResolveProviderOptions,maybeOptions?:ResolveProviderOptions){return this.resolveProvider(providerId,versionOrOptions,maybeOptions)}
|
|
23
|
+
listProviders(filter:TwinProviderFilter={}):TwinProviderSummary[]{return[...this.generations.values()].flatMap(g=>[...g.providers.values()]).filter(r=>(!filter.providerId||r.manifest.providerId===filter.providerId)&&(!filter.version||r.manifest.version===filter.version)&&(!filter.pluginName||r.pluginName===filter.pluginName)&&(!filter.generation||r.generation===filter.generation)&&(!filter.states?.length||filter.states.includes(r.state))&&(filter.includeDraining||r.state!=='DRAINING')&&(filter.includeRetired||r.state!=='RETIRED')&&(filter.includeFailed||r.state!=='FAILED')).map(r=>({...r,manifest:r.manifest}))}
|
|
24
|
+
listTwinProviders(filter:TwinProviderFilter={}){return this.listProviders(filter)}
|
|
25
|
+
listScenePacks(pluginName?:string){return[...this.generations.values()].filter(g=>(!pluginName||g.pluginName===pluginName)&&g.state!=='RETIRED').flatMap(g=>[...g.scenePacks.values()])}
|
|
26
|
+
listSolverAdapters(pluginName?:string){return[...this.generations.values()].filter(g=>(!pluginName||g.pluginName===pluginName)&&g.state!=='RETIRED').flatMap(g=>[...g.solverAdapters.values()])}
|
|
27
|
+
getProviderHealth(providerId:string,version?:string){return this.resolveProvider(providerId,version,{allowDraining:true,includeFailed:true})?.manifest?this.find(providerId,version)?.health:undefined}
|
|
28
|
+
async checkProviderHealth(providerId:string,version?:string){const r=this.find(providerId,version);if(!r)throw new TwinProviderRegistryError('PROVIDER_NOT_FOUND',providerId);r.health=await checkProviderHealth(r.provider);if(!providerHealthAllowsUse(r.health))r.state='FAILED';return r.health}
|
|
29
|
+
validateProvider(providerId:string,version?:string):ProviderValidationResult{const r=this.find(providerId,version);if(!r)return{valid:false,errors:['PROVIDER_NOT_FOUND'],warnings:[]};return validateProviderDefinition(r.provider)}
|
|
30
|
+
drainGeneration(pluginName:string,generation:number):TwinProviderDrainResult{const g=this.generations.get(`${pluginName}\0${generation}`);if(!g)throw new TwinProviderRegistryError('GENERATION_NOT_FOUND',`${pluginName}@${generation}`);this.markDraining(g);return{ok:true,pluginName,generation,state:g.state,inFlight:[...g.providers.values()].reduce((a,r)=>a+r.inFlight,0),retired:g.state==='RETIRED',providerIds:[...g.providers.values()].map(r=>r.manifest.providerId)}}
|
|
31
|
+
drain(pluginName:string,generation:number){return this.drainGeneration(pluginName,generation)}
|
|
32
|
+
retireProvider(providerId:string,version:string,generation?:number):TwinProviderRetireResult{const r=this.find(providerId,version,generation);if(!r)throw new TwinProviderRegistryError('PROVIDER_NOT_FOUND',`${providerId}@${version}`);r.state=r.inFlight?'DRAINING':'RETIRED';if(r.state==='RETIRED')r.retiredAt=new Date().toISOString();return{ok:true,providerId,version,generation:r.generation,state:r.state,inFlight:r.inFlight,retired:r.state==='RETIRED'}}
|
|
33
|
+
retire(providerId:string,version:string,generation?:number){return this.retireProvider(providerId,version,generation)}
|
|
34
|
+
reloadGeneration(pluginName:string,c:TwinPluginContribution,generation?:number){return this.registerTwinPluginContribution(pluginName,c,generation??this.allocate(pluginName))}
|
|
35
|
+
reloadProvider(pluginName:string,c:TwinPluginContribution,generation?:number){return this.reloadGeneration(pluginName,c,generation)}
|
|
36
|
+
private find(id:string,version?:string,generation?:number){return[...this.generations.values()].flatMap(g=>[...g.providers.values()]).filter(r=>r.manifest.providerId===id&&(!version||r.manifest.version===version)&&(!generation||r.generation===generation)).sort((a,b)=>b.generation-a.generation)[0]}
|
|
37
|
+
private markDraining(g:GenerationRecord){if(g.state==='RETIRED')return;g.state='DRAINING';g.drainingAt??=new Date().toISOString();for(const r of g.providers.values()){if(!r.inFlight){r.state='RETIRED';r.retiredAt??=new Date().toISOString()}else r.state='DRAINING'}if([...g.providers.values()].every(r=>r.state==='RETIRED')){g.state='RETIRED';g.retiredAt??=new Date().toISOString()}}
|
|
38
|
+
private lease(r:ProviderRecord):TwinProviderLease{r.inFlight++;const refId=`twin-provider-ref-${++this.leases}`;let released=false;return{refId,provider:r.provider,manifest:r.manifest,lineage:{providerId:r.manifest.providerId,providerVersion:r.manifest.version,providerHash:r.providerHash,pluginName:r.pluginName,generation:r.generation},generation:r.generation,get released(){return released},release:()=>{if(released)return;released=true;r.inFlight=Math.max(0,r.inFlight-1);if(!r.inFlight&&r.state==='DRAINING'){r.state='RETIRED';r.retiredAt??=new Date().toISOString()}}}}
|
|
39
|
+
}
|
|
40
|
+
const singleton=new TwinProviderRegistry();export function getTwinProviderRegistry(){return singleton}
|
|
41
|
+
export const registerTwinPluginContribution=(p:string,c:TwinPluginContribution,g?:number)=>singleton.registerTwinPluginContribution(p,c,g)
|
|
42
|
+
export const unregisterTwinPluginContribution=(p:string,g?:number)=>singleton.unregisterTwinPluginContribution(p,g)
|
|
43
|
+
export const listTwinProviders=(f:TwinProviderFilter={})=>singleton.listProviders(f)
|
|
44
|
+
export const resolveTwinProvider=(p:string,v?:string|ResolveProviderOptions,o?:ResolveProviderOptions)=>singleton.resolveProvider(p,v,o)
|
|
45
|
+
export const acquireTwinProvider=resolveTwinProvider
|
|
46
|
+
export const reloadTwinProviderGeneration=(p:string,c:TwinPluginContribution,g?:number)=>singleton.reloadGeneration(p,c,g)
|
|
47
|
+
export const drainTwinProviderGeneration=(p:string,g:number)=>singleton.drainGeneration(p,g)
|
|
48
|
+
export const retireTwinProvider=(p:string,v:string,g?:number)=>singleton.retireProvider(p,v,g)
|
|
49
|
+
export function attachTwinPluginBridge(registry:TwinProviderRegistry=singleton){const global=globalThis as any;const existing=global.__twinPluginExt;const api:any={registerPhysicsProvider:(payload:any,source:any)=>registry.registerPhysicsProvider(payload,source),registerScenePack:(payload:any,source:any)=>registry.registerScenePack({...(payload as any),sceneKind:(payload as any).sceneKind??(payload as any).scenePackId??'unknown',sceneSchemaVersion:(payload as any).sceneSchemaVersion??(payload as any).version??'1.0.0'},source),registerObjectiveProfile:()=>({ok:true}),registerTrainingAdapter:()=>({ok:true}),registerSolverAdapter:(payload:any,source:any)=>registry.registerSolverAdapter(payload,source),listProviders:(f:any)=>registry.listProviders(f),listTwinProviders:(f:any)=>registry.listProviders(f),getProviderHealth:(p:string,v?:string)=>registry.getProviderHealth(p,v),resolveProvider:(p:string,v?:string)=>registry.resolveProvider(p,v),validateProvider:(p:string,v?:string)=>registry.validateProvider(p,v),retireProvider:(p:string,v?:string)=>v?registry.retireProvider(p,v):undefined,unregisterPlugin:(s:any)=>registry.unregisterTwinPluginContribution(String(s?.plugin??s?.pluginName??s?.name??s)),removePlugin:(s:any)=>registry.unregisterTwinPluginContribution(String(s?.plugin??s?.pluginName??s?.name??s))};if(existing&&typeof existing.attach==='function'){existing.attach(api);return existing}const bridge=existing??{pending:[],register:(kind:string,payload:any,source:any)=>{const fn=api[kind];return fn?fn(payload,source):undefined},registerEntry:(e:any)=>bridge.register(e.kind,e.payload,e.source),attach:(r:any)=>{bridge.registry=r},call:(m:string,...a:any[])=>bridge.registry?.[m]?.(...a),invoke:(m:string,...a:any[])=>bridge.call(m,...a)};global.__twinPluginExt=bridge;if(Array.isArray(bridge.pending)&&bridge.pending.length){const pending=bridge.pending.splice(0);for(const e of pending){const fn=api[e.kind];if(fn)fn(e.payload,e.source)}}return bridge}
|
|
50
|
+
attachTwinPluginBridge()
|
|
@@ -221,12 +221,18 @@ export abstract class ManagerState extends ManagerContracts {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
/**
|
|
225
|
+
* 按需装配频道(lead 运行时 + SchedulerLoop,幂等)。实现位于装配层
|
|
226
|
+
* `ManagerRuntimeWiring.ensureChannelActive`;基类只声明,调用点在任务事件钩子里。
|
|
227
|
+
*/
|
|
228
|
+
protected abstract ensureChannelActive(channelId: string): void
|
|
229
|
+
|
|
224
230
|
protected getTaskEngine(): TaskEngine {
|
|
225
231
|
if (!this.taskEngine) {
|
|
226
232
|
const factory = this.deps.taskEngineFactory ?? (r => new TaskEngineImpl(r, {
|
|
227
233
|
onTaskChange: (e) => {
|
|
228
|
-
|
|
229
|
-
|
|
234
|
+
// 全字段转发(含 progress):TaskEngine 内部进度变化(applyEvent 分块折算 /
|
|
235
|
+
// complete 置 100)须经总线 → WS task.progress 实时同步,否则前端实体进度滞后
|
|
230
236
|
this.buses.get(e.channelId)?.notifyTask({
|
|
231
237
|
taskId: e.taskId,
|
|
232
238
|
state: e.state,
|
|
@@ -237,8 +243,19 @@ export abstract class ManagerState extends ManagerContracts {
|
|
|
237
243
|
reassignFrom: e.reassignFrom,
|
|
238
244
|
reason: e.reason,
|
|
239
245
|
})
|
|
240
|
-
// 事件驱动调度:任务状态变化即唤醒该频道调度循环(空闲退避即刻恢复快节奏)
|
|
241
|
-
|
|
246
|
+
// 事件驱动调度:任务状态变化即唤醒该频道调度循环(空闲退避即刻恢复快节奏)。
|
|
247
|
+
//
|
|
248
|
+
// ⚠️ 只 wake 不够:lead 运行时因空闲被清扫卸载后 `cr.scheduler` 会被置空
|
|
249
|
+
// (见 runtime-wiring.attachScheduler 注释),此后新建的根任务只落库,
|
|
250
|
+
// `scheduler?.wake()` 变成空操作 —— 任务永久停在 SUBMITTED,直到
|
|
251
|
+
// ROOT_TIMEOUT(~18 分钟)被判 FAILED(实测复现:mock Channel 第一条根任务
|
|
252
|
+
// COMPLETED,后续两条一直 SUBMITTED;omp Channel 同样如此)。
|
|
253
|
+
// `reviveScheduler` 只在"lead 正在被装配"的窗口里被调用,没有别的事件会重建
|
|
254
|
+
// 调度循环,所以这里补上**重新挂载**的分支:没有 scheduler 就按需装配 lead +
|
|
255
|
+
// 调度循环(ensureChannelActive 幂等;频道停用/无 lead 时内部直接返回)。
|
|
256
|
+
const cr = this.channels.get(e.channelId)
|
|
257
|
+
if (cr?.scheduler) cr.scheduler.wake()
|
|
258
|
+
else if (e.state === 'SUBMITTED' || e.state === 'ASSIGNED') ((this as unknown) as { ensureChannelActive: (channelId: string) => void }).ensureChannelActive(e.channelId)
|
|
242
259
|
},
|
|
243
260
|
}))
|
|
244
261
|
this.taskEngine = factory({ tasks: this.deps.repos.tasks, messages: this.deps.repos.messages })
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"6f524f3b-53e1-4df9-84e6-84d8058b9f16","timestamp":1790323631296,"prerendered":[]}
|